/*
 * 86Box    A hypervisor and IBM PC system emulator that specializes in
 *          running old operating systems and software designed for IBM
 *          PC systems and compatibles from 1981 through fairly recent
 *          system designs based on the PCI bus.
 *
 *          This file is part of the 86Box distribution.
 *
 *          Header of the code common to the AHA-154x series of SCSI
 *          Host Adapters made by Adaptec, Inc. and the BusLogic series
 *          of SCSI Host Adapters made by Mylex.
 *          These controllers were designed for various buses.
 *
 * Authors: TheCollector1995, <mariogplayer@gmail.com>
 *          Miran Grca, <mgrca8@gmail.com>
 *          Fred N. van Kempen, <decwiz@yahoo.com>
 *
 *          Copyright 2016-2018 Miran Grca.
 *          Copyright 2017-2018 Fred N. van Kempen.
 */
#ifndef SCSI_X54X_H
#define SCSI_X54X_H

#define SCSI_DELAY_TM 1 /* was 50 */

#define ROM_SIZE      16384 /* one ROM is 16K */
#define NVR_SIZE      256   /* size of NVR */

/* EEPROM map and bit definitions. */
#define EE0_HOSTID  0x07 /* EE(0) [2:0]                         */
#define EE0_ALTFLOP 0x80 /* EE(0) [7] FDC at 370h               */
#define EE1_IRQCH   0x07 /* EE(1) [3:0]                         */
#define EE1_DMACH   0x70 /* EE(1) [7:4]                         */
#define EE2_RMVOK   0x01 /* EE(2) [0] Support removable disks   */
#define EE2_HABIOS  0x02 /* EE(2) [1] HA Bios Space Reserved    */
#define EE2_INT19   0x04 /* EE(2) [2] HA Bios Controls INT19    */
#define EE2_DYNSCAN 0x08 /* EE(2) [3] Dynamically scan bus      */
#define EE2_TWODRV  0x10 /* EE(2) [4] Allow more than 2 drives  */
#define EE2_SEEKRET 0x20 /* EE(2) [5] Immediate return on seek  */
#define EE2_EXT1G   0x80 /* EE(2) [7] Extended Translation >1GB */
#define EE3_SPEED   0x00 /* EE(3) [7:0] DMA Speed               */
#define SPEED_33    0xFF
#define SPEED_50    0x00
#define SPEED_56    0x04
#define SPEED_67    0x01
#define SPEED_80    0x02
#define SPEED_10    0x03
#define EE4_FLOPTOK 0x80 /* EE(4) [7] Support Flopticals        */
#define EE6_PARITY  0x01 /* EE(6) [0] parity check enable       */
#define EE6_TERM    0x02 /* EE(6) [1] host term enable          */
#define EE6_RSTBUS  0x04 /* EE(6) [2] reset SCSI bus on boot    */
#define EEE_SYNC    0x01 /* EE(E) [0] Enable Sync Negotiation   */
#define EEE_DISCON  0x02 /* EE(E) [1] Enable Disconnection      */
#define EEE_FAST    0x04 /* EE(E) [2] Enable FAST SCSI          */
#define EEE_START   0x08 /* EE(E) [3] Enable Start Unit         */

/*
 * Host Adapter I/O ports.
 *
 * READ  Port x+0: STATUS
 * WRITE Port x+0: CONTROL
 *
 * READ  Port x+1: DATA
 * WRITE Port x+1: COMMAND
 *
 * READ  Port x+2: INTERRUPT STATUS
 * WRITE Port x+2: (undefined?)
 *
 * R/W   Port x+3: (undefined)
 */

/* WRITE CONTROL commands. */
#define CTRL_HRST  0x80 /* Hard reset */
#define CTRL_SRST  0x40 /* Soft reset */
#define CTRL_IRST  0x20 /* interrupt reset */
#define CTRL_SCRST 0x10 /* SCSI bus reset */

/* READ STATUS. */
#define STAT_STST   0x80 /* self-test in progress */
#define STAT_DFAIL  0x40 /* internal diagnostic failure */
#define STAT_INIT   0x20 /* mailbox initialization required */
#define STAT_IDLE   0x10 /* HBA is idle */
#define STAT_CDFULL 0x08 /* Command/Data output port is full */
#define STAT_DFULL  0x04 /* Data input port is full */
#define STAT_INVCMD 0x01 /* Invalid command */

/* READ/WRITE DATA. */
#define CMD_NOP         0x00 /* No operation */
#define CMD_MBINIT      0x01 /* mailbox initialization */
#define CMD_START_SCSI  0x02 /* Start SCSI command */
#define CMD_BIOSCMD     0x03 /* Execute ROM BIOS command */
#define CMD_INQUIRY     0x04 /* Adapter inquiry */
#define CMD_EMBOI       0x05 /* enable Mailbox Out Interrupt */
#define CMD_SELTIMEOUT  0x06 /* Set SEL timeout */
#define CMD_BUSON_TIME  0x07 /* set bus-On time */
#define CMD_BUSOFF_TIME 0x08 /* set bus-off time */
#define CMD_DMASPEED    0x09 /* set ISA DMA speed */
#define CMD_RETDEVS     0x0A /* return installed devices */
#define CMD_RETCONF     0x0B /* return configuration data */
#define CMD_TARGET      0x0C /* set HBA to target mode */
#define CMD_RETSETUP    0x0D /* return setup data */
#define CMD_WRITE_CH2   0x1A /* write channel 2 buffer */
#define CMD_READ_CH2    0x1B /* read channel 2 buffer */
#define CMD_ECHO        0x1F /* ECHO command data */
#define CMD_OPTIONS     0x21 /* set adapter options */

/* READ INTERRUPT STATUS. */
#define INTR_ANY       0x80 /* any interrupt */
#define INTR_SRCD      0x08 /* SCSI reset detected */
#define INTR_HACC      0x04 /* HA command complete */
#define INTR_MBOA      0x02 /* MBO empty */
#define INTR_MBIF      0x01 /* MBI full */

#define ADDR_TO_U32(x) (((x).hi << 16) | ((x).mid << 8) | ((x).lo & 0xFF))
#define U32_TO_ADDR(a, x)    \
    do {                     \
        (a).hi  = (x) >> 16; \
        (a).mid = (x) >> 8;  \
        (a).lo  = (x) &0xFF; \
    } while (0)

/*
 * Mailbox Definitions.
 *
 * Mailbox Out (MBO) command values.
 */
#define MBO_FREE  0x00
#define MBO_START 0x01
#define MBO_ABORT 0x02

/* Mailbox In (MBI) status values. */
#define MBI_FREE      0x00
#define MBI_SUCCESS   0x01
#define MBI_ABORT     0x02
#define MBI_NOT_FOUND 0x03
#define MBI_ERROR     0x04

/*
 *
 * CCB - SCSI Command Control Block
 *
 *    The CCB is a superset of the CDB (Command Descriptor Block)
 *    and specifies detailed information about a SCSI command.
 *
 */
/*    Byte 0    Command Control Block Operation Code */
#define SCSI_INITIATOR_COMMAND     0x00
#define TARGET_MODE_COMMAND        0x01
#define SCATTER_GATHER_COMMAND     0x02
#define SCSI_INITIATOR_COMMAND_RES 0x03
#define SCATTER_GATHER_COMMAND_RES 0x04
#define BUS_RESET                  0x81

/*    Byte 1    Address and Direction Control */
#define CCB_TARGET_ID_SHIFT    0x06 /* CCB Op Code = 00, 02 */
#define CCB_INITIATOR_ID_SHIFT 0x06 /* CCB Op Code = 01 */
#define CCB_DATA_XFER_IN       0x01
#define CCB_DATA_XFER_OUT      0x02
#define CCB_LUN_MASK           0x07 /* Logical Unit Number */

/*    Byte 2    SCSI_Command_Length - Length of SCSI CDB
      Byte 3    Request Sense Allocation Length */
#define FOURTEEN_BYTES        0x00 /* Request Sense Buffer size */
#define NO_AUTO_REQUEST_SENSE 0x01 /* No Request Sense Buffer */

/*    Bytes 4, 5 and 6    Data Length  - Data transfer byte count */
/*    Bytes 7, 8 and 9    Data Pointer - SGD List or Data Buffer */
/*    Bytes 10, 11 and 12 Link Pointer - Next CCB in Linked List */
/*    Byte 13   Command Link ID        - TBD (I don't know yet) */
/*    Byte 14   Host Status            - Host Adapter status */
#define CCB_COMPLETE            0x00 /* CCB completed without error */
#define CCB_LINKED_COMPLETE     0x0A /* Linked command completed */
#define CCB_LINKED_COMPLETE_INT 0x0B /* Linked complete with intr */
#define CCB_SELECTION_TIMEOUT   0x11 /* Set SCSI selection timed out */
#define CCB_DATA_OVER_UNDER_RUN 0x12
#define CCB_UNEXPECTED_BUS_FREE 0x13 /* Trg dropped SCSI BSY */
#define CCB_PHASE_SEQUENCE_FAIL 0x14 /* Trg bus phase sequence fail */
#define CCB_BAD_MBO_COMMAND     0x15 /* MBO command not 0, 1 or 2 */
#define CCB_INVALID_OP_CODE     0x16 /* CCB invalid operation code */
#define CCB_BAD_LINKED_LUN      0x17 /* Linked CCB LUN diff from 1st */
#define CCB_INVALID_DIRECTION   0x18 /* Invalid target direction */
#define CCB_DUPLICATE_CCB       0x19 /* Duplicate CCB */
#define CCB_INVALID_CCB         0x1A /* Invalid CCB - bad parameter */

#define lba32_blk(p)            ((uint32_t) (p->u.lba.lba0 << 24) | (p->u.lba.lba1 << 16) | (p->u.lba.lba2 << 8) | p->u.lba.lba3)

/*
 *
 * Scatter/Gather Segment List Definitions
 *
 * Adapter limits
 */
#define MAX_SG_DESCRIPTORS 32 /* Always make the array 32 elements long, if less are used, that's not an issue. */

#pragma pack(push, 1)
typedef struct addr24_s {
    uint8_t hi;
    uint8_t mid;
    uint8_t lo;
} addr24_t;

/* Structure for the INQUIRE_SETUP_INFORMATION reply. */
typedef struct ReplyInquireSetupInformationSynchronousValue_t {
    uint8_t uOffset         : 4;
    uint8_t uTransferPeriod : 3;
    uint8_t fSynchronous    : 1;
} ReplyInquireSetupInformationSynchronousValue;

typedef struct ReplyInquireSetupInformation_t {
    uint8_t fSynchronousInitiationEnabled     : 1;
    uint8_t fParityCheckingEnabled            : 1;
    uint8_t uReserved1                        : 6;
    uint8_t                                      uBusTransferRate;
    uint8_t                                      uPreemptTimeOnBus;
    uint8_t                                      uTimeOffBus;
    uint8_t                                      cMailbox;
    addr24_t                                     MailboxAddress;
    ReplyInquireSetupInformationSynchronousValue SynchronousValuesId0To7[8];
    uint8_t                                      uDisconnectPermittedId0To7;
    uint8_t                                      VendorSpecificData[28];
} ReplyInquireSetupInformation;

typedef struct MailboxInit_t {
    uint8_t Count;
    addr24_t Address;
} MailboxInit_t;

typedef struct Mailbox_t {
    uint8_t  CmdStatus;
    addr24_t CCBPointer;
} Mailbox_t;

typedef struct Mailbox32_t {
    uint32_t CCBPointer;
    union {
        struct out_t {
            uint8_t Reserved[3];
            uint8_t ActionCode;
        } out;
        struct in_t {
            uint8_t HostStatus;
            uint8_t TargetStatus;
            uint8_t Reserved;
            uint8_t CompletionCode;
        } in;
    } u;
} Mailbox32_t;

/*    Byte 15   Target Status

      See scsi.h files for these statuses.
      Bytes 16 and 17   Reserved (must be 0)
      Bytes 18 through 18+n-1, where n=size of CDB  Command Descriptor Block */

typedef struct CCB32_t {
    uint8_t  Opcode;
    uint8_t  Reserved1   : 3;
    uint8_t  ControlByte : 2;
    uint8_t  TagQueued   : 1;
    uint8_t  QueueTag    : 2;
    uint8_t  CdbLength;
    uint8_t  RequestSenseLength;
    uint32_t DataLength;
    uint32_t DataPointer;
    uint8_t  Reserved2[2];
    uint8_t  HostStatus;
    uint8_t  TargetStatus;
    uint8_t  Id;
    uint8_t  Lun             : 5;
    uint8_t  LegacyTagEnable : 1;
    uint8_t  LegacyQueueTag   : 2;
    uint8_t  Cdb[12];
    uint8_t  Reserved3[6];
    uint32_t SensePointer;
} CCB32;

typedef struct CCB_t {
    uint8_t  Opcode;
    uint8_t  Lun         : 3;
    uint8_t  ControlByte : 2;
    uint8_t  Id          : 3;
    uint8_t  CdbLength;
    uint8_t  RequestSenseLength;
    addr24_t DataLength;
    addr24_t DataPointer;
    addr24_t LinkPointer;
    uint8_t  LinkId;
    uint8_t  HostStatus;
    uint8_t  TargetStatus;
    uint8_t  Reserved[2];
    uint8_t  Cdb[12];
} CCB;

typedef struct CCBC_t {
    uint8_t Opcode;
    uint8_t Pad1        : 3;
    uint8_t ControlByte : 2;
    uint8_t Pad2        : 3;
    uint8_t CdbLength;
    uint8_t RequestSenseLength;
    uint8_t Pad3[9];
    uint8_t CompletionCode; /* Only used by the 1542C/CF(/CP?) BIOS mailboxes */
    uint8_t HostStatus;
    uint8_t TargetStatus;
    uint8_t Pad4[2];
    uint8_t Cdb[12];
} CCBC;

typedef union CCBU_t {
    CCB32 new;
    CCB   old;
    CCBC  common;
} CCBU;

typedef struct {
    CCBU     CmdBlock;
    uint8_t *RequestSenseBuffer;
    uint32_t CCBPointer;
    int      Is24bit;
    uint8_t  TargetID;
    uint8_t  LUN;
    uint8_t  HostStatus;
    uint8_t  TargetStatus;
    uint8_t  MailboxCompletionCode;
} Req_t;

typedef struct BIOSCMD_t {
    uint8_t command;
    uint8_t lun      : 3;
    uint8_t reserved : 2;
    uint8_t id       : 3;
    union {
        struct chs_t {
            uint16_t cyl;
            uint8_t  head;
            uint8_t  sec;
        } chs;
        struct lba_t {
            uint8_t lba0; /* MSB */
            uint8_t lba1;
            uint8_t lba2;
            uint8_t lba3; /* LSB */
        } lba;
    } u;
    uint8_t   secount;
    addr24_t  dma_address;
} BIOSCMD;

typedef struct SGE32_t {
    uint32_t Segment;
    uint32_t SegmentPointer;
} SGE32;

typedef struct SGE_t {
    addr24_t Segment;
    addr24_t SegmentPointer;
} SGE;
#pragma pack(pop)

#define X54X_CDROM_BOOT        1
#define X54X_32BIT             2
#define X54X_LBA_BIOS          4
#define X54X_INT_GEOM_WRITABLE 8
#define X54X_MBX_24BIT         16
#define X54X_ISAPNP            32
#define X54X_HAS_SIGNATURE     64

typedef struct x54x_t {
    /* 32 bytes */
    char vendor[16]; /* name of device vendor */
    char name[16];   /* name of device */

    /* 24 bytes */
    int8_t type; /* type of device */
    int8_t IrqEnabled;
    int8_t Irq;
    int8_t DmaChannel;
    int8_t HostID;

    uint8_t callback_phase     : 4;
    uint8_t callback_sub_phase : 4;
    uint8_t scsi_cmd_phase;
    uint8_t bus;
    uint8_t sync;
    uint8_t parity;
    uint8_t shram_mode;
    uint8_t Geometry;
    uint8_t Control;
    uint8_t Command;
    uint8_t CmdParam;
    uint8_t BusOnTime;
    uint8_t BusOffTime;
    uint8_t ATBusSpeed;
    uint8_t setup_info_len;
    uint8_t max_id;
    uint8_t pci_slot;
    uint8_t irq_state;
    uint8_t pad;
    uint8_t pad0;
    uint8_t pad1;
    uint8_t temp_cdb[12];

    /* for multi-threading, keep these volatile */
    volatile uint8_t Status;
    volatile uint8_t Interrupt;
    volatile uint8_t MailboxIsBIOS;
    volatile uint8_t ToRaise;
    volatile uint8_t flags;

    /* 65928 bytes */
    uint8_t pos_regs[8]; /* MCA */
    uint8_t CmdBuf[128];
    uint8_t DataBuf[65536];
    uint8_t shadow_ram[128];
    uint8_t dma_buffer[128];
    uint8_t cmd_33_buf[4096];

    /* 16 bytes */
    char *fw_rev; /* The 4 bytes of the revision command information + 2 extra bytes for BusLogic */

    uint8_t *rom1; /* main BIOS image */
    uint8_t *rom2; /* SCSI-Select image */
    uint8_t *nvr;  /* EEPROM buffer */

    /* 6 words = 12 bytes */
    uint16_t DataReply;
    uint16_t DataReplyLeft;
    uint16_t rom_ioaddr;    /* offset in BIOS of I/O addr */
    uint16_t rom_shram;     /* index to shared RAM */
    uint16_t rom_shramsz;   /* size of shared RAM */
    uint16_t rom_fwhigh;    /* offset in BIOS of ver ID */
    uint16_t pnp_len;       /* length of the PnP ROM */
    uint16_t pnp_offset;    /* offset in the microcode ROM of the PnP ROM */
    uint16_t cmd_33_len;    /* length of the SCSISelect code decompressor program */
    uint16_t cmd_33_offset; /* offset in the microcode ROM of the SCSISelect code decompressor program */

    /* 16 + 20 + 52 = 88 bytes */
    volatile int MailboxOutInterrupts;
    volatile int PendingInterrupt;
    volatile int Lock;
    volatile int target_data_len;
    volatile int pad2;

    uint32_t Base;
    uint32_t fdc_address;
    uint32_t rom_addr; /* address of BIOS ROM */
    uint32_t CmdParamLeft;
    uint32_t Outgoing;
    uint32_t transfer_size;

    volatile uint32_t MailboxInit;
    volatile uint32_t MailboxCount;
    volatile uint32_t MailboxOutAddr;
    volatile uint32_t MailboxOutPosCur;
    volatile uint32_t MailboxInAddr;
    volatile uint32_t MailboxInPosCur;
    volatile uint32_t MailboxReq;
    volatile uint32_t BIOSMailboxInit;
    volatile uint32_t BIOSMailboxCount;
    volatile uint32_t BIOSMailboxOutAddr;
    volatile uint32_t BIOSMailboxOutPosCur;
    volatile uint32_t BIOSMailboxReq;
    volatile uint32_t Residue;
    volatile uint32_t card_bus; /* Basically a copy of device flags */

    /* 8 bytes */
    uint64_t temp_period;

    /* 16 bytes */
    double media_period;
    double ha_bps; /* bytes per second */

    /* 8 bytes */
    char *bios_path;  /* path to BIOS image file */
    char *mcode_path; /* path to microcode image file, needed by the AHA-1542CP */

    char nvr_path[64]; /* path to NVR image file */

    /* 56 bytes */
    /* Pointer to a structure of vendor-specific data that only the vendor-specific code can understand */
    void *ven_data;

    /* Pointer to a function that performs vendor-specific operation during the timer callback */
    void (*ven_callback)(void *priv);
    /* Pointer to a function that executes the second parameter phase of the vendor-specific command */
    void (*ven_cmd_phase1)(void *priv);
    /* Pointer to a function that gets the host adapter ID in case it has to be read from a non-standard location */
    uint8_t (*ven_get_host_id)(void *priv);
    /* Pointer to a function that updates the IRQ in the vendor-specific space */
    uint8_t (*ven_get_irq)(void *priv);
    /* Pointer to a function that updates the DMA channel in the vendor-specific space */
    uint8_t (*ven_get_dma)(void *priv);
    /* Pointer to a function that returns whether command is fast */
    uint8_t (*ven_cmd_is_fast)(void *priv);
    /* Pointer to a function that executes vendor-specific fast path commands */
    uint8_t (*ven_fast_cmds)(void *priv, uint8_t cmd);
    /* Pointer to a function that gets the parameter length for vendor-specific commands */
    uint8_t (*get_ven_param_len)(void *priv);
    /* Pointer to a function that executes vendor-specific commands and returns whether or not to suppress the IRQ */
    uint8_t (*ven_cmds)(void *priv);
    /* Pointer to a function that fills in the vendor-specific setup data */
    void (*get_ven_data)(void *priv);
    /* Pointer to a function that determines if the mode is aggressive */
    uint8_t (*is_aggressive_mode)(void *priv);
    /* Pointer to a function that returns interrupt type (0 = edge, 1 = level) */
    uint8_t (*interrupt_type)(void *priv);
    /* Pointer to a function that resets vendor-specific data */
    void (*ven_reset)(void *priv);

    rom_t bios;     /* BIOS memory descriptor */
    rom_t uppersck; /* BIOS memory descriptor */

    mem_mapping_t mmio_mapping;

    pc_timer_t timer;
    pc_timer_t ResetCB;

    Req_t Req;

    fdc_t *fdc;
} x54x_t;

extern void    x54x_reset_ctrl(x54x_t *dev, uint8_t Reset);
extern uint8_t x54x_mbo_process(x54x_t *dev);
extern void    x54x_wait_for_poll(void);
extern void    x54x_io_set(x54x_t *dev, uint32_t base, uint8_t len);
extern void    x54x_io_remove(x54x_t *dev, uint32_t base, uint8_t len);
extern void    x54x_mem_init(x54x_t *dev, uint32_t addr);
extern void    x54x_mem_enable(x54x_t *dev);
extern void    x54x_mem_set_addr(x54x_t *dev, uint32_t base);
extern void    x54x_mem_disable(x54x_t *dev);
extern void   *x54x_init(const device_t *info);
extern void    x54x_close(void *priv);
extern void    x54x_device_reset(void *priv);

#endif /*SCSI_X54X_H*/
