1 | /* |
2 | * QEMU SiI3112A PCI to Serial ATA Controller Emulation |
3 | * |
4 | * Copyright (C) 2017 BALATON Zoltan <balaton@eik.bme.hu> |
5 | * |
6 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
7 | * See the COPYING file in the top-level directory. |
8 | * |
9 | */ |
10 | |
11 | /* For documentation on this and similar cards see: |
12 | * http://wiki.osdev.org/User:Quok/Silicon_Image_Datasheets |
13 | */ |
14 | |
15 | #include "qemu/osdep.h" |
16 | #include "hw/ide/pci.h" |
17 | #include "qemu/module.h" |
18 | #include "sysemu/reset.h" |
19 | #include "trace.h" |
20 | |
21 | #define TYPE_SII3112_PCI "sii3112" |
22 | #define SII3112_PCI(obj) OBJECT_CHECK(SiI3112PCIState, (obj), \ |
23 | TYPE_SII3112_PCI) |
24 | |
25 | typedef struct SiI3112Regs { |
26 | uint32_t confstat; |
27 | uint32_t scontrol; |
28 | uint16_t sien; |
29 | uint8_t swdata; |
30 | } SiI3112Regs; |
31 | |
32 | typedef struct SiI3112PCIState { |
33 | PCIIDEState i; |
34 | MemoryRegion mmio; |
35 | SiI3112Regs regs[2]; |
36 | } SiI3112PCIState; |
37 | |
38 | /* The sii3112_reg_read and sii3112_reg_write functions implement the |
39 | * Internal Register Space - BAR5 (section 6.7 of the data sheet). |
40 | */ |
41 | |
42 | static uint64_t sii3112_reg_read(void *opaque, hwaddr addr, |
43 | unsigned int size) |
44 | { |
45 | SiI3112PCIState *d = opaque; |
46 | uint64_t val = 0; |
47 | |
48 | switch (addr) { |
49 | case 0x00: |
50 | val = d->i.bmdma[0].cmd; |
51 | break; |
52 | case 0x01: |
53 | val = d->regs[0].swdata; |
54 | break; |
55 | case 0x02: |
56 | val = d->i.bmdma[0].status; |
57 | break; |
58 | case 0x03: |
59 | val = 0; |
60 | break; |
61 | case 0x04 ... 0x07: |
62 | val = bmdma_addr_ioport_ops.read(&d->i.bmdma[0], addr - 4, size); |
63 | break; |
64 | case 0x08: |
65 | val = d->i.bmdma[1].cmd; |
66 | break; |
67 | case 0x09: |
68 | val = d->regs[1].swdata; |
69 | break; |
70 | case 0x0a: |
71 | val = d->i.bmdma[1].status; |
72 | break; |
73 | case 0x0b: |
74 | val = 0; |
75 | break; |
76 | case 0x0c ... 0x0f: |
77 | val = bmdma_addr_ioport_ops.read(&d->i.bmdma[1], addr - 12, size); |
78 | break; |
79 | case 0x10: |
80 | val = d->i.bmdma[0].cmd; |
81 | val |= (d->regs[0].confstat & (1UL << 11) ? (1 << 4) : 0); /*SATAINT0*/ |
82 | val |= (d->regs[1].confstat & (1UL << 11) ? (1 << 6) : 0); /*SATAINT1*/ |
83 | val |= (d->i.bmdma[1].status & BM_STATUS_INT ? (1 << 14) : 0); |
84 | val |= (uint32_t)d->i.bmdma[0].status << 16; |
85 | val |= (uint32_t)d->i.bmdma[1].status << 24; |
86 | break; |
87 | case 0x18: |
88 | val = d->i.bmdma[1].cmd; |
89 | val |= (d->regs[1].confstat & (1UL << 11) ? (1 << 4) : 0); |
90 | val |= (uint32_t)d->i.bmdma[1].status << 16; |
91 | break; |
92 | case 0x80 ... 0x87: |
93 | val = pci_ide_data_le_ops.read(&d->i.bus[0], addr - 0x80, size); |
94 | break; |
95 | case 0x8a: |
96 | val = pci_ide_cmd_le_ops.read(&d->i.bus[0], 2, size); |
97 | break; |
98 | case 0xa0: |
99 | val = d->regs[0].confstat; |
100 | break; |
101 | case 0xc0 ... 0xc7: |
102 | val = pci_ide_data_le_ops.read(&d->i.bus[1], addr - 0xc0, size); |
103 | break; |
104 | case 0xca: |
105 | val = pci_ide_cmd_le_ops.read(&d->i.bus[1], 2, size); |
106 | break; |
107 | case 0xe0: |
108 | val = d->regs[1].confstat; |
109 | break; |
110 | case 0x100: |
111 | val = d->regs[0].scontrol; |
112 | break; |
113 | case 0x104: |
114 | val = (d->i.bus[0].ifs[0].blk) ? 0x113 : 0; |
115 | break; |
116 | case 0x148: |
117 | val = (uint32_t)d->regs[0].sien << 16; |
118 | break; |
119 | case 0x180: |
120 | val = d->regs[1].scontrol; |
121 | break; |
122 | case 0x184: |
123 | val = (d->i.bus[1].ifs[0].blk) ? 0x113 : 0; |
124 | break; |
125 | case 0x1c8: |
126 | val = (uint32_t)d->regs[1].sien << 16; |
127 | break; |
128 | default: |
129 | val = 0; |
130 | } |
131 | trace_sii3112_read(size, addr, val); |
132 | return val; |
133 | } |
134 | |
135 | static void sii3112_reg_write(void *opaque, hwaddr addr, |
136 | uint64_t val, unsigned int size) |
137 | { |
138 | SiI3112PCIState *d = opaque; |
139 | |
140 | trace_sii3112_write(size, addr, val); |
141 | switch (addr) { |
142 | case 0x00: |
143 | case 0x10: |
144 | bmdma_cmd_writeb(&d->i.bmdma[0], val); |
145 | break; |
146 | case 0x01: |
147 | case 0x11: |
148 | d->regs[0].swdata = val & 0x3f; |
149 | break; |
150 | case 0x02: |
151 | case 0x12: |
152 | d->i.bmdma[0].status = (val & 0x60) | (d->i.bmdma[0].status & 1) | |
153 | (d->i.bmdma[0].status & ~val & 6); |
154 | break; |
155 | case 0x04 ... 0x07: |
156 | bmdma_addr_ioport_ops.write(&d->i.bmdma[0], addr - 4, val, size); |
157 | break; |
158 | case 0x08: |
159 | case 0x18: |
160 | bmdma_cmd_writeb(&d->i.bmdma[1], val); |
161 | break; |
162 | case 0x09: |
163 | case 0x19: |
164 | d->regs[1].swdata = val & 0x3f; |
165 | break; |
166 | case 0x0a: |
167 | case 0x1a: |
168 | d->i.bmdma[1].status = (val & 0x60) | (d->i.bmdma[1].status & 1) | |
169 | (d->i.bmdma[1].status & ~val & 6); |
170 | break; |
171 | case 0x0c ... 0x0f: |
172 | bmdma_addr_ioport_ops.write(&d->i.bmdma[1], addr - 12, val, size); |
173 | break; |
174 | case 0x80 ... 0x87: |
175 | pci_ide_data_le_ops.write(&d->i.bus[0], addr - 0x80, val, size); |
176 | break; |
177 | case 0x8a: |
178 | pci_ide_cmd_le_ops.write(&d->i.bus[0], 2, val, size); |
179 | break; |
180 | case 0xc0 ... 0xc7: |
181 | pci_ide_data_le_ops.write(&d->i.bus[1], addr - 0xc0, val, size); |
182 | break; |
183 | case 0xca: |
184 | pci_ide_cmd_le_ops.write(&d->i.bus[1], 2, val, size); |
185 | break; |
186 | case 0x100: |
187 | d->regs[0].scontrol = val & 0xfff; |
188 | if (val & 1) { |
189 | ide_bus_reset(&d->i.bus[0]); |
190 | } |
191 | break; |
192 | case 0x148: |
193 | d->regs[0].sien = (val >> 16) & 0x3eed; |
194 | break; |
195 | case 0x180: |
196 | d->regs[1].scontrol = val & 0xfff; |
197 | if (val & 1) { |
198 | ide_bus_reset(&d->i.bus[1]); |
199 | } |
200 | break; |
201 | case 0x1c8: |
202 | d->regs[1].sien = (val >> 16) & 0x3eed; |
203 | break; |
204 | default: |
205 | val = 0; |
206 | } |
207 | } |
208 | |
209 | static const MemoryRegionOps sii3112_reg_ops = { |
210 | .read = sii3112_reg_read, |
211 | .write = sii3112_reg_write, |
212 | .endianness = DEVICE_LITTLE_ENDIAN, |
213 | }; |
214 | |
215 | /* the PCI irq level is the logical OR of the two channels */ |
216 | static void sii3112_update_irq(SiI3112PCIState *s) |
217 | { |
218 | int i, set = 0; |
219 | |
220 | for (i = 0; i < 2; i++) { |
221 | set |= s->regs[i].confstat & (1UL << 11); |
222 | } |
223 | pci_set_irq(PCI_DEVICE(s), (set ? 1 : 0)); |
224 | } |
225 | |
226 | static void sii3112_set_irq(void *opaque, int channel, int level) |
227 | { |
228 | SiI3112PCIState *s = opaque; |
229 | |
230 | trace_sii3112_set_irq(channel, level); |
231 | if (level) { |
232 | s->regs[channel].confstat |= (1UL << 11); |
233 | } else { |
234 | s->regs[channel].confstat &= ~(1UL << 11); |
235 | } |
236 | |
237 | sii3112_update_irq(s); |
238 | } |
239 | |
240 | static void sii3112_reset(void *opaque) |
241 | { |
242 | SiI3112PCIState *s = opaque; |
243 | int i; |
244 | |
245 | for (i = 0; i < 2; i++) { |
246 | s->regs[i].confstat = 0x6515 << 16; |
247 | ide_bus_reset(&s->i.bus[i]); |
248 | } |
249 | } |
250 | |
251 | static void sii3112_pci_realize(PCIDevice *dev, Error **errp) |
252 | { |
253 | SiI3112PCIState *d = SII3112_PCI(dev); |
254 | PCIIDEState *s = PCI_IDE(dev); |
255 | MemoryRegion *mr; |
256 | qemu_irq *irq; |
257 | int i; |
258 | |
259 | pci_config_set_interrupt_pin(dev->config, 1); |
260 | pci_set_byte(dev->config + PCI_CACHE_LINE_SIZE, 8); |
261 | |
262 | /* BAR5 is in PCI memory space */ |
263 | memory_region_init_io(&d->mmio, OBJECT(d), &sii3112_reg_ops, d, |
264 | "sii3112.bar5" , 0x200); |
265 | pci_register_bar(dev, 5, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); |
266 | |
267 | /* BAR0-BAR4 are PCI I/O space aliases into BAR5 */ |
268 | mr = g_new(MemoryRegion, 1); |
269 | memory_region_init_alias(mr, OBJECT(d), "sii3112.bar0" , &d->mmio, 0x80, 8); |
270 | pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, mr); |
271 | mr = g_new(MemoryRegion, 1); |
272 | memory_region_init_alias(mr, OBJECT(d), "sii3112.bar1" , &d->mmio, 0x88, 4); |
273 | pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_IO, mr); |
274 | mr = g_new(MemoryRegion, 1); |
275 | memory_region_init_alias(mr, OBJECT(d), "sii3112.bar2" , &d->mmio, 0xc0, 8); |
276 | pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_IO, mr); |
277 | mr = g_new(MemoryRegion, 1); |
278 | memory_region_init_alias(mr, OBJECT(d), "sii3112.bar3" , &d->mmio, 0xc8, 4); |
279 | pci_register_bar(dev, 3, PCI_BASE_ADDRESS_SPACE_IO, mr); |
280 | mr = g_new(MemoryRegion, 1); |
281 | memory_region_init_alias(mr, OBJECT(d), "sii3112.bar4" , &d->mmio, 0, 16); |
282 | pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, mr); |
283 | |
284 | irq = qemu_allocate_irqs(sii3112_set_irq, d, 2); |
285 | for (i = 0; i < 2; i++) { |
286 | ide_bus_new(&s->bus[i], sizeof(s->bus[i]), DEVICE(dev), i, 1); |
287 | ide_init2(&s->bus[i], irq[i]); |
288 | |
289 | bmdma_init(&s->bus[i], &s->bmdma[i], s); |
290 | s->bmdma[i].bus = &s->bus[i]; |
291 | ide_register_restart_cb(&s->bus[i]); |
292 | } |
293 | qemu_register_reset(sii3112_reset, s); |
294 | } |
295 | |
296 | static void sii3112_pci_class_init(ObjectClass *klass, void *data) |
297 | { |
298 | DeviceClass *dc = DEVICE_CLASS(klass); |
299 | PCIDeviceClass *pd = PCI_DEVICE_CLASS(klass); |
300 | |
301 | pd->vendor_id = 0x1095; |
302 | pd->device_id = 0x3112; |
303 | pd->class_id = PCI_CLASS_STORAGE_RAID; |
304 | pd->revision = 1; |
305 | pd->realize = sii3112_pci_realize; |
306 | dc->desc = "SiI3112A SATA controller" ; |
307 | set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); |
308 | } |
309 | |
310 | static const TypeInfo sii3112_pci_info = { |
311 | .name = TYPE_SII3112_PCI, |
312 | .parent = TYPE_PCI_IDE, |
313 | .instance_size = sizeof(SiI3112PCIState), |
314 | .class_init = sii3112_pci_class_init, |
315 | }; |
316 | |
317 | static void sii3112_register_types(void) |
318 | { |
319 | type_register_static(&sii3112_pci_info); |
320 | } |
321 | |
322 | type_init(sii3112_register_types) |
323 | |