| 1 | /* |
| 2 | * QEMU NCR710 SCSI Controller |
| 3 | * |
| 4 | * Copyright (c) 2025 Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com> |
| 5 | * |
| 6 | * NCR710 SCSI Controller implementation |
| 7 | * Based on the NCR53C710 Technical Manual Version 3.2, December 2000 |
| 8 | * |
| 9 | * Developed from the hackish implementation of NCR53C710 by Helge Deller |
| 10 | * which was interim based on the hackish implementation by Toni Wilen for UAE |
| 11 | * |
| 12 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 13 | */ |
| 14 | |
| 15 | #ifndef HW_NCR53C710_H |
| 16 | #define HW_NCR53C710_H |
| 17 | |
| 18 | #include "hw/core/sysbus.h" |
| 19 | #include "hw/scsi/scsi.h" |
| 20 | #include "qemu/fifo8.h" |
| 21 | #include "qom/object.h" |
| 22 | #include "system/memory.h" |
| 23 | #include "hw/core/irq.h" |
| 24 | #include "qemu/timer.h" |
| 25 | |
| 26 | #define TYPE_NCR710_SCSI "ncr710-scsi" |
| 27 | #define TYPE_SYSBUS_NCR710_SCSI "sysbus-ncr710-scsi" |
| 28 | |
| 29 | #define SYSBUS_NCR710_SCSI(obj) \ |
| 30 | OBJECT_CHECK(SysBusNCR710State, (obj), TYPE_SYSBUS_NCR710_SCSI) |
| 31 | |
| 32 | #define ENABLE_DEBUG 0 |
| 33 | #if ENABLE_DEBUG |
| 34 | #define DBG(x) x |
| 35 | #define NCR710_DPRINTF(fmt, ...) \ |
| 36 | fprintf(stderr, "QEMU: " fmt, ## __VA_ARGS__) |
| 37 | #define BADF(fmt, ...) \ |
| 38 | fprintf(stderr, "QEMU: error: " fmt, ## __VA_ARGS__) |
| 39 | #else |
| 40 | #define DBG(x) do { } while (0) |
| 41 | #define NCR710_DPRINTF(fmt, ...) do { } while (0) |
| 42 | #define BADF(fmt, ...) do { } while (0) |
| 43 | #endif |
| 44 | |
| 45 | /* NCR710 - Little Endian register Ordering */ |
| 46 | #define NCR710_SCNTL0_REG 0x00 /* SCSI Control Zero */ |
| 47 | #define NCR710_SCNTL1_REG 0x01 /* SCSI Control One */ |
| 48 | #define NCR710_SDID_REG 0x02 /* SCSI Destination ID */ |
| 49 | #define NCR710_SIEN_REG 0x03 /* SCSI Interrupt Enable */ |
| 50 | #define NCR710_SCID_REG 0x04 /* SCSI Chip ID */ |
| 51 | #define NCR710_SXFER_REG 0x05 /* SCSI Transfer */ |
| 52 | #define NCR710_SODL_REG 0x06 /* SCSI Output Data Latch */ |
| 53 | #define NCR710_SOCL_REG 0x07 /* SCSI Output Control Latch */ |
| 54 | #define NCR710_SFBR_REG 0x08 /* SCSI First Byte Received */ |
| 55 | #define NCR710_SIDL_REG 0x09 /* SCSI Input Data Latch */ |
| 56 | #define NCR710_SBDL_REG 0x0A /* SCSI Bus Data Lines */ |
| 57 | #define NCR710_SBCL_REG 0x0B /* SCSI Bus Control Lines */ |
| 58 | #define NCR710_DSTAT_REG 0x0C /* DMA Status */ |
| 59 | #define NCR710_SSTAT0_REG 0x0D /* SCSI Status Zero */ |
| 60 | #define NCR710_SSTAT1_REG 0x0E /* SCSI Status One */ |
| 61 | #define NCR710_SSTAT2_REG 0x0F /* SCSI Status Two */ |
| 62 | #define NCR710_DSA_REG 0x10 /* Data Structure Address */ |
| 63 | #define NCR710_CTEST0_REG 0x14 /* Chip Test Zero */ |
| 64 | #define NCR710_CTEST1_REG 0x15 /* Chip Test One */ |
| 65 | #define NCR710_CTEST2_REG 0x16 /* Chip Test Two */ |
| 66 | #define NCR710_CTEST3_REG 0x17 /* Chip Test Three */ |
| 67 | #define NCR710_CTEST4_REG 0x18 /* Chip Test Four */ |
| 68 | #define NCR710_CTEST5_REG 0x19 /* Chip Test Five */ |
| 69 | #define NCR710_CTEST6_REG 0x1A /* Chip Test Six */ |
| 70 | #define NCR710_CTEST7_REG 0x1B /* Chip Test Seven */ |
| 71 | #define NCR710_TEMP_REG 0x1C /* Temporary Stack */ |
| 72 | #define NCR710_DFIFO_REG 0x20 /* DMA FIFO */ |
| 73 | #define NCR710_ISTAT_REG 0x21 /* Interrupt Status */ |
| 74 | #define NCR710_CTEST8_REG 0x22 /* Chip Test Eight */ |
| 75 | #define NCR710_LCRC_REG 0x23 /* Longitudinal Parity */ |
| 76 | #define NCR710_DBC_REG 0x24 /* DMA Byte Counter (24-bit, LE) */ |
| 77 | #define NCR710_DCMD_REG 0x27 /* DMA Command */ |
| 78 | #define NCR710_DNAD_REG 0x28 /* DMA Next Data Address (32-bit, LE) */ |
| 79 | #define NCR710_DSP_REG 0x2C /* DMA SCRIPTS Pointer (32-bit, LE) */ |
| 80 | #define NCR710_DSPS_REG 0x30 /* DMA SCRIPTS Pointer Save */ |
| 81 | #define NCR710_SCRATCH_REG 0x34 /* Scratch (32-bit, LE) */ |
| 82 | #define NCR710_DMODE_REG 0x38 /* DMA Mode */ |
| 83 | #define NCR710_DIEN_REG 0x39 /* DMA Interrupt Enable */ |
| 84 | #define NCR710_DWT_REG 0x3A /* DMA Watchdog Timer */ |
| 85 | #define NCR710_DCNTL_REG 0x3B /* DMA Control */ |
| 86 | #define NCR710_ADDER_REG 0x3C /* Adder Sum Output (32-bit, LE) */ |
| 87 | |
| 88 | #define NCR710_REG_SIZE 0x100 |
| 89 | |
| 90 | #define NCR710_BUF_SIZE 4096 |
| 91 | #define NCR710_HOST_ID 7 |
| 92 | #define NCR710_MAX_MSGIN_LEN 8 |
| 93 | #define NCR710_SCSI_FIFO_SIZE 8 |
| 94 | |
| 95 | typedef enum { |
| 96 | NCR710_WAIT_NONE = 0, |
| 97 | NCR710_WAIT_RESELECT = 1, |
| 98 | NCR710_WAIT_DMA = 2, |
| 99 | NCR710_WAIT_RESERVED = 3 |
| 100 | } NCR710WaitState; |
| 101 | |
| 102 | typedef enum { |
| 103 | NCR710_CMD_PENDING = 0, |
| 104 | NCR710_CMD_DATA_READY = 1, |
| 105 | NCR710_CMD_COMPLETE = 2 |
| 106 | } NCR710CommandState; |
| 107 | |
| 108 | typedef enum { |
| 109 | NCR710_MSG_ACTION_NONE = 0, |
| 110 | NCR710_MSG_ACTION_DISCONNECT = 1, |
| 111 | NCR710_MSG_ACTION_DATA_OUT = 2, |
| 112 | NCR710_MSG_ACTION_DATA_IN = 3 |
| 113 | } NCR710MessageAction; |
| 114 | |
| 115 | typedef struct NCR710State NCR710State; |
| 116 | typedef struct NCR710Request NCR710Request; |
| 117 | |
| 118 | /* |
| 119 | * SCSI FIFO structure - 8 transfers deep, 1 byte per transfer |
| 120 | * (9-bit wide with parity) |
| 121 | */ |
| 122 | typedef struct { |
| 123 | uint8_t data[NCR710_SCSI_FIFO_SIZE]; |
| 124 | uint8_t parity[NCR710_SCSI_FIFO_SIZE]; |
| 125 | int head; |
| 126 | int count; |
| 127 | } NCR710_SCSI_FIFO; |
| 128 | |
| 129 | struct NCR710Request { |
| 130 | SCSIRequest *req; |
| 131 | uint32_t tag; |
| 132 | uint32_t dma_len; |
| 133 | uint32_t pending; |
| 134 | uint8_t status; |
| 135 | bool active; |
| 136 | uint8_t *dma_buf; |
| 137 | bool out; |
| 138 | uint32_t resume_offset; |
| 139 | uint32_t saved_dnad; |
| 140 | }; |
| 141 | |
| 142 | struct NCR710State { |
| 143 | SysBusDevice parent_obj; |
| 144 | MemoryRegion mmio; |
| 145 | qemu_irq irq; |
| 146 | |
| 147 | SCSIBus bus; |
| 148 | AddressSpace *as; |
| 149 | |
| 150 | /* Registers */ |
| 151 | uint8_t scntl0; |
| 152 | uint8_t scntl1; |
| 153 | uint8_t sdid; |
| 154 | uint8_t sien0; |
| 155 | uint8_t scid; |
| 156 | uint8_t sxfer; |
| 157 | uint8_t sodl; |
| 158 | uint8_t socl; |
| 159 | uint8_t sfbr; |
| 160 | uint8_t sidl; |
| 161 | uint8_t sbdl; |
| 162 | uint8_t sbcl; |
| 163 | uint8_t dstat; |
| 164 | uint8_t sstat0; |
| 165 | uint8_t sstat1; |
| 166 | uint8_t sstat2; |
| 167 | uint32_t dsa; |
| 168 | uint8_t ctest0; |
| 169 | uint8_t ctest1; |
| 170 | uint8_t ctest2; |
| 171 | uint8_t ctest3; |
| 172 | uint8_t ctest4; |
| 173 | uint8_t ctest5; |
| 174 | uint8_t ctest6; |
| 175 | uint8_t ctest7; |
| 176 | uint8_t ctest8; |
| 177 | uint32_t temp; |
| 178 | uint8_t dfifo; |
| 179 | uint8_t istat; |
| 180 | uint8_t lcrc; |
| 181 | uint32_t dbc; |
| 182 | uint8_t dcmd; |
| 183 | uint32_t dnad; |
| 184 | uint32_t dsp; |
| 185 | uint32_t dsps; |
| 186 | uint32_t scratch; |
| 187 | uint8_t dmode; |
| 188 | uint8_t dien; |
| 189 | uint8_t dwt; |
| 190 | uint8_t dcntl; |
| 191 | uint32_t adder; |
| 192 | |
| 193 | NCR710_SCSI_FIFO scsi_fifo; |
| 194 | |
| 195 | NCR710Request *current; |
| 196 | uint8_t status; |
| 197 | uint8_t msg[NCR710_MAX_MSGIN_LEN]; |
| 198 | uint8_t msg_len; |
| 199 | uint8_t msg_action; /* NCR710MessageAction values */ |
| 200 | int carry; |
| 201 | bool script_active; |
| 202 | int32_t waiting; /* NCR710WaitState values */ |
| 203 | uint8_t command_complete; /* NCR710CommandState values */ |
| 204 | |
| 205 | QEMUTimer *reselection_retry_timer; |
| 206 | uint32_t saved_dsps; |
| 207 | |
| 208 | uint32_t select_tag; |
| 209 | uint8_t current_lun; |
| 210 | uint8_t reselection_id; |
| 211 | bool wait_reselect; |
| 212 | }; |
| 213 | |
| 214 | typedef struct SysBusNCR710State { |
| 215 | SysBusDevice parent_obj; |
| 216 | MemoryRegion mmio; |
| 217 | MemoryRegion iomem; |
| 218 | qemu_irq irq; |
| 219 | NCR710State ncr710; |
| 220 | } SysBusNCR710State; |
| 221 | |
| 222 | static inline NCR710State *ncr710_from_scsi_bus(SCSIBus *bus) |
| 223 | { |
| 224 | return container_of(bus, NCR710State, bus); |
| 225 | } |
| 226 | |
| 227 | static inline SysBusNCR710State *sysbus_from_ncr710(NCR710State *s) |
| 228 | { |
| 229 | return container_of(s, SysBusNCR710State, ncr710); |
| 230 | } |
| 231 | |
| 232 | DeviceState *ncr53c710_init(MemoryRegion *address_space, hwaddr addr, |
| 233 | qemu_irq irq); |
| 234 | DeviceState *ncr710_device_create_sysbus(hwaddr addr, qemu_irq irq); |
| 235 | void ncr710_reg_write(void *opaque, hwaddr addr, uint64_t val, unsigned size); |
| 236 | uint64_t ncr710_reg_read(void *opaque, hwaddr addr, unsigned size); |
| 237 | void ncr710_soft_reset(NCR710State *s); |
| 238 | void ncr710_request_cancelled(SCSIRequest *req); |
| 239 | void ncr710_command_complete(SCSIRequest *req, size_t resid); |
| 240 | void ncr710_transfer_data(SCSIRequest *req, uint32_t len); |
| 241 | void ncr710_execute_script(NCR710State *s); |
| 242 | void ncr710_set_phase(NCR710State *s, int phase); |
| 243 | void ncr710_reselection_retry_callback(void *opaque); |
| 244 | extern const VMStateDescription vmstate_ncr710; |
| 245 | |
| 246 | #endif /* HW_NCR53C710_H */ |