master
c 286 lines 8.37 KB
Raw
1 /*
2 * LASI Wrapper for NCR710 SCSI Controller
3 *
4 * Copyright (c) 2025 Soumyajyotii Ssarkar <soumyajyotisarkar23@gmail.com>
5 * This driver was developed during the Google Summer of Code 2025 program.
6 * Mentored by Helge Deller <deller@gmx.de>
7 *
8 * NCR710 SCSI Controller implementation
9 * Based on the NCR53C710 Technical Manual Version 3.2, December 2000
10 *
11 * SPDX-License-Identifier: GPL-2.0-or-later
12 */
13
14 #include "qemu/osdep.h"
15 #include "hw/scsi/lasi_ncr710.h"
16 #include "hw/scsi/ncr53c710.h"
17 #include "hw/core/sysbus.h"
18 #include "qemu/timer.h"
19 #include "qemu/log.h"
20 #include "trace.h"
21 #include "system/blockdev.h"
22 #include "migration/vmstate.h"
23 #include "qapi/error.h"
24 #include "system/dma.h"
25
26 #define LASI_710_SVERSION 0x00082
27 #define SCNR 0xBEEFBABE
28 #define LASI_710_HVERSION 0x3D
29 #define HPHW_FIO 5 /* Fixed I/O module */
30
31 static uint64_t lasi_ncr710_reg_read(void *opaque, hwaddr addr,
32 unsigned size)
33 {
34 LasiNCR710State *s = LASI_NCR710(opaque);
35 uint64_t val = 0;
36
37 trace_lasi_ncr710_reg_read(addr, 0, size);
38
39 if (addr == 0x00) { /* Device ID */
40 val = (HPHW_FIO << 24) | LASI_710_SVERSION;
41 trace_lasi_ncr710_reg_read_id(HPHW_FIO, LASI_710_SVERSION, val);
42 return val;
43 }
44
45 if (addr == 0x08) { /* HVersion */
46 val = LASI_710_HVERSION;
47 trace_lasi_ncr710_reg_read_hversion(val);
48 return val;
49 }
50
51 if (addr >= 0x100) {
52 hwaddr ncr_addr = addr - 0x100;
53 if (size == 1) {
54 ncr_addr ^= 3;
55 NCR710_DPRINTF("Reading value to LASI WRAPPER == 0x%lx%s, "
56 "val=0x%lx, size=%u\n",
57 addr - 0x100, size == 1 ? " (XORed)" : "",
58 val, size);
59 val = ncr710_reg_read(&s->ncr710, ncr_addr, size);
60 } else {
61 val = 0;
62 for (unsigned i = 0; i < size; i++) {
63 uint8_t byte_val = ncr710_reg_read(&s->ncr710, ncr_addr + i, 1);
64 val |= ((uint64_t)byte_val) << (i * 8);
65 NCR710_DPRINTF(" Read byte %u from NCR addr 0x%lx: "
66 "0x%02x\n", i, ncr_addr + i, byte_val);
67 }
68 NCR710_DPRINTF(" Reconstructed %u-byte value: 0x%lx\n",
69 size, val);
70 }
71
72 trace_lasi_ncr710_reg_forward_read(addr, val);
73 } else {
74 val = 0;
75 trace_lasi_ncr710_reg_read(addr, val, size);
76 }
77 return val;
78 }
79
80 static void lasi_ncr710_reg_write(void *opaque, hwaddr addr,
81 uint64_t val, unsigned size)
82 {
83 LasiNCR710State *s = LASI_NCR710(opaque);
84
85 trace_lasi_ncr710_reg_write(addr, val, size);
86
87 if (addr <= 0x0F) {
88 return;
89 }
90
91 if (addr >= 0x100) {
92 hwaddr ncr_addr = addr - 0x100;
93
94 if (size == 1) {
95 ncr_addr ^= 3;
96 NCR710_DPRINTF("Writing value to LASI WRAPPER == 0x%lx%s, "
97 "val=0x%lx, size=%u\n",
98 addr - 0x100, size == 1 ? " (XORed)" : "",
99 val, size);
100 ncr710_reg_write(&s->ncr710, ncr_addr, val, size);
101 } else {
102 for (unsigned i = 0; i < size; i++) {
103 uint8_t byte_val = (val >> (i * 8)) & 0xff;
104 NCR710_DPRINTF(" Writing byte %u to NCR addr 0x%lx: 0x%02x\n",
105 i, ncr_addr + i, byte_val);
106 ncr710_reg_write(&s->ncr710, ncr_addr + i, byte_val, 1);
107 }
108 }
109
110 trace_lasi_ncr710_reg_forward_write(addr, val);
111 } else {
112 trace_lasi_ncr710_reg_write(addr, val, size);
113 }
114 }
115
116 /*
117 * req_cancelled, command_complete, transfer_data forwards
118 * commands to its core counterparts.
119 */
120 static void lasi_ncr710_request_cancelled(SCSIRequest *req)
121 {
122 trace_lasi_ncr710_request_cancelled(req);
123 ncr710_request_cancelled(req);
124 }
125
126 static void lasi_ncr710_command_complete(SCSIRequest *req, size_t resid)
127 {
128 trace_lasi_ncr710_command_complete(req->status, resid);
129 ncr710_command_complete(req, resid);
130 }
131
132 static void lasi_ncr710_transfer_data(SCSIRequest *req, uint32_t len)
133 {
134 trace_lasi_ncr710_transfer_data(len);
135 ncr710_transfer_data(req, len);
136 }
137
138 static const struct SCSIBusInfo lasi_ncr710_scsi_info = {
139 .tcq = true,
140 .max_target = 8,
141 .max_lun = 8, /* full LUN support */
142
143 .transfer_data = lasi_ncr710_transfer_data,
144 .complete = lasi_ncr710_command_complete,
145 .cancel = lasi_ncr710_request_cancelled,
146 };
147
148 static const MemoryRegionOps lasi_ncr710_mmio_ops = {
149 .read = lasi_ncr710_reg_read,
150 .write = lasi_ncr710_reg_write,
151 .endianness = DEVICE_BIG_ENDIAN,
152 .valid = {
153 .min_access_size = 1,
154 .max_access_size = 4,
155 },
156 };
157
158 static const VMStateDescription vmstate_lasi_ncr710 = {
159 .name = "lasi-ncr710",
160 .version_id = 1,
161 .minimum_version_id = 1,
162 .fields = (const VMStateField[]) {
163 VMSTATE_UINT32(hw_type, LasiNCR710State),
164 VMSTATE_UINT32(sversion, LasiNCR710State),
165 VMSTATE_UINT32(hversion, LasiNCR710State),
166 VMSTATE_STRUCT(ncr710, LasiNCR710State, 1, vmstate_ncr710, NCR710State),
167 VMSTATE_END_OF_LIST()
168 }
169 };
170
171 static void lasi_ncr710_realize(DeviceState *dev, Error **errp)
172 {
173 LasiNCR710State *s = LASI_NCR710(dev);
174 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
175
176 trace_lasi_ncr710_device_realize();
177
178 scsi_bus_init(&s->ncr710.bus, sizeof(s->ncr710.bus), dev,
179 &lasi_ncr710_scsi_info);
180 s->ncr710.as = &address_space_memory;
181 s->ncr710.irq = s->lasi_irq;
182
183 s->ncr710.reselection_retry_timer =
184 timer_new_ns(QEMU_CLOCK_VIRTUAL,
185 ncr710_reselection_retry_callback,
186 &s->ncr710);
187
188 ncr710_soft_reset(&s->ncr710);
189
190 trace_lasi_ncr710_timers_initialized(
191 (uint64_t)s->ncr710.reselection_retry_timer);
192
193 /* Initialize memory region */
194 memory_region_init_io(&s->mmio, OBJECT(dev), &lasi_ncr710_mmio_ops, s,
195 "lasi-ncr710", 0x200);
196 sysbus_init_mmio(sbd, &s->mmio);
197 }
198
199 void lasi_ncr710_handle_legacy_cmdline(DeviceState *lasi_dev)
200 {
201 LasiNCR710State *s = LASI_NCR710(lasi_dev);
202 SCSIBus *bus = &s->ncr710.bus;
203 int found_drives = 0;
204
205 if (!bus) {
206 return;
207 }
208
209 for (int unit = 0; unit <= 7; unit++) {
210 DriveInfo *dinfo = drive_get(IF_SCSI, bus->busnr, unit);
211 if (dinfo) {
212 trace_lasi_ncr710_legacy_drive_found(bus->busnr, unit);
213 found_drives++;
214 }
215 }
216
217 trace_lasi_ncr710_handle_legacy_cmdline(bus->busnr, found_drives);
218
219 scsi_bus_legacy_handle_cmdline(bus);
220 BusChild *kid;
221 QTAILQ_FOREACH(kid, &bus->qbus.children, sibling) {
222 trace_lasi_ncr710_scsi_device_created(
223 object_get_typename(OBJECT(kid->child)));
224 }
225 }
226
227 DeviceState *lasi_ncr710_init(MemoryRegion *addr_space, hwaddr hpa,
228 qemu_irq irq)
229 {
230 DeviceState *dev;
231 LasiNCR710State *s;
232 SysBusDevice *sbd;
233
234 dev = qdev_new(TYPE_LASI_NCR710);
235 s = LASI_NCR710(dev);
236 sbd = SYS_BUS_DEVICE(dev);
237 s->lasi_irq = irq;
238 sysbus_realize_and_unref(sbd, &error_fatal);
239 memory_region_add_subregion(addr_space, hpa,
240 sysbus_mmio_get_region(sbd, 0));
241 return dev;
242 }
243
244 static void lasi_ncr710_reset(DeviceState *dev)
245 {
246 LasiNCR710State *s = LASI_NCR710(dev);
247 trace_lasi_ncr710_device_reset();
248 ncr710_soft_reset(&s->ncr710);
249 }
250
251 static void lasi_ncr710_instance_init(Object *obj)
252 {
253 LasiNCR710State *s = LASI_NCR710(obj);
254
255 s->hw_type = HPHW_FIO;
256 s->sversion = LASI_710_SVERSION;
257 s->hversion = LASI_710_HVERSION;
258 }
259
260 static void lasi_ncr710_class_init(ObjectClass *klass, const void *data)
261 {
262 DeviceClass *dc = DEVICE_CLASS(klass);
263
264 dc->realize = lasi_ncr710_realize;
265 set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
266 dc->fw_name = "scsi";
267 dc->desc = "HP-PARISC LASI NCR710 SCSI adapter";
268 device_class_set_legacy_reset(dc, lasi_ncr710_reset);
269 dc->vmsd = &vmstate_lasi_ncr710;
270 dc->user_creatable = false;
271 }
272
273 static const TypeInfo lasi_ncr710_info = {
274 .name = TYPE_LASI_NCR710,
275 .parent = TYPE_SYS_BUS_DEVICE,
276 .instance_size = sizeof(LasiNCR710State),
277 .instance_init = lasi_ncr710_instance_init,
278 .class_init = lasi_ncr710_class_init,
279 };
280
281 static void lasi_ncr710_register_types(void)
282 {
283 type_register_static(&lasi_ncr710_info);
284 }
285
286 type_init(lasi_ncr710_register_types)