]>
Commit | Line | Data |
---|---|---|
4c3df0ec JQ |
1 | /* |
2 | * QEMU IDE Emulation: PCI cmd646 support. | |
3 | * | |
4 | * Copyright (c) 2003 Fabrice Bellard | |
5 | * Copyright (c) 2006 Openedhand Ltd. | |
6 | * | |
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 | * of this software and associated documentation files (the "Software"), to deal | |
9 | * in the Software without restriction, including without limitation the rights | |
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 | * copies of the Software, and to permit persons to whom the Software is | |
12 | * furnished to do so, subject to the following conditions: | |
13 | * | |
14 | * The above copyright notice and this permission notice shall be included in | |
15 | * all copies or substantial portions of the Software. | |
16 | * | |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
23 | * THE SOFTWARE. | |
24 | */ | |
25 | #include <hw/hw.h> | |
26 | #include <hw/pc.h> | |
27 | #include <hw/pci.h> | |
28 | #include <hw/isa.h> | |
29 | #include "block.h" | |
4c3df0ec JQ |
30 | #include "sysemu.h" |
31 | #include "dma.h" | |
32 | ||
33 | #include <hw/ide/pci.h> | |
34 | ||
35 | /* CMD646 specific */ | |
36 | #define MRDMODE 0x71 | |
37 | #define MRDMODE_INTR_CH0 0x04 | |
38 | #define MRDMODE_INTR_CH1 0x08 | |
39 | #define MRDMODE_BLK_CH0 0x10 | |
40 | #define MRDMODE_BLK_CH1 0x20 | |
41 | #define UDIDETCR0 0x73 | |
42 | #define UDIDETCR1 0x7B | |
43 | ||
44 | static void cmd646_update_irq(PCIIDEState *d); | |
45 | ||
a9deb8c6 AK |
46 | static uint64_t cmd646_cmd_read(void *opaque, target_phys_addr_t addr, |
47 | unsigned size) | |
4c3df0ec | 48 | { |
a9deb8c6 AK |
49 | CMD646BAR *cmd646bar = opaque; |
50 | ||
51 | if (addr != 2 || size != 1) { | |
52 | return ((uint64_t)1 << (size * 8)) - 1; | |
53 | } | |
54 | return ide_status_read(cmd646bar->bus, addr + 2); | |
55 | } | |
56 | ||
57 | static void cmd646_cmd_write(void *opaque, target_phys_addr_t addr, | |
58 | uint64_t data, unsigned size) | |
59 | { | |
60 | CMD646BAR *cmd646bar = opaque; | |
61 | ||
62 | if (addr != 2 || size != 1) { | |
63 | return; | |
64 | } | |
65 | ide_cmd_write(cmd646bar->bus, addr + 2, data); | |
66 | } | |
67 | ||
68 | static MemoryRegionOps cmd646_cmd_ops = { | |
69 | .read = cmd646_cmd_read, | |
70 | .write = cmd646_cmd_write, | |
71 | .endianness = DEVICE_LITTLE_ENDIAN, | |
72 | }; | |
73 | ||
74 | static uint64_t cmd646_data_read(void *opaque, target_phys_addr_t addr, | |
75 | unsigned size) | |
76 | { | |
77 | CMD646BAR *cmd646bar = opaque; | |
78 | ||
79 | if (size == 1) { | |
80 | return ide_ioport_read(cmd646bar->bus, addr); | |
81 | } else if (addr == 0) { | |
82 | if (size == 2) { | |
83 | return ide_data_readw(cmd646bar->bus, addr); | |
4c3df0ec | 84 | } else { |
a9deb8c6 | 85 | return ide_data_readl(cmd646bar->bus, addr); |
4c3df0ec JQ |
86 | } |
87 | } | |
a9deb8c6 | 88 | return ((uint64_t)1 << (size * 8)) - 1; |
4c3df0ec JQ |
89 | } |
90 | ||
a9deb8c6 AK |
91 | static void cmd646_data_write(void *opaque, target_phys_addr_t addr, |
92 | uint64_t data, unsigned size) | |
61f58e59 | 93 | { |
a9deb8c6 AK |
94 | CMD646BAR *cmd646bar = opaque; |
95 | ||
96 | if (size == 1) { | |
97 | return ide_ioport_write(cmd646bar->bus, addr, data); | |
98 | } else if (addr == 0) { | |
99 | if (size == 2) { | |
100 | return ide_data_writew(cmd646bar->bus, addr, data); | |
101 | } else { | |
102 | return ide_data_writel(cmd646bar->bus, addr, data); | |
103 | } | |
104 | } | |
105 | } | |
106 | ||
107 | static MemoryRegionOps cmd646_data_ops = { | |
108 | .read = cmd646_data_read, | |
109 | .write = cmd646_data_write, | |
110 | .endianness = DEVICE_LITTLE_ENDIAN, | |
111 | }; | |
112 | ||
113 | static void setup_cmd646_bar(PCIIDEState *d, int bus_num) | |
114 | { | |
115 | IDEBus *bus = &d->bus[bus_num]; | |
116 | CMD646BAR *bar = &d->cmd646_bar[bus_num]; | |
117 | ||
118 | bar->bus = bus; | |
119 | bar->pci_dev = d; | |
120 | memory_region_init_io(&bar->cmd, &cmd646_cmd_ops, bar, "cmd646-cmd", 4); | |
121 | memory_region_init_io(&bar->data, &cmd646_data_ops, bar, "cmd646-data", 8); | |
122 | } | |
123 | ||
124 | static uint64_t bmdma_read(void *opaque, target_phys_addr_t addr, | |
125 | unsigned size) | |
126 | { | |
127 | BMDMAState *bm = opaque; | |
128 | PCIIDEState *pci_dev = bm->pci_dev; | |
4c3df0ec JQ |
129 | uint32_t val; |
130 | ||
a9deb8c6 AK |
131 | if (size != 1) { |
132 | return ((uint64_t)1 << (size * 8)) - 1; | |
133 | } | |
134 | ||
4c3df0ec JQ |
135 | switch(addr & 3) { |
136 | case 0: | |
137 | val = bm->cmd; | |
138 | break; | |
139 | case 1: | |
58c0e732 | 140 | val = pci_dev->dev.config[MRDMODE]; |
4c3df0ec JQ |
141 | break; |
142 | case 2: | |
143 | val = bm->status; | |
144 | break; | |
145 | case 3: | |
70ae65f5 | 146 | if (bm == &pci_dev->bmdma[0]) { |
58c0e732 | 147 | val = pci_dev->dev.config[UDIDETCR0]; |
4c3df0ec | 148 | } else { |
58c0e732 | 149 | val = pci_dev->dev.config[UDIDETCR1]; |
4c3df0ec JQ |
150 | } |
151 | break; | |
152 | default: | |
153 | val = 0xff; | |
154 | break; | |
155 | } | |
156 | #ifdef DEBUG_IDE | |
157 | printf("bmdma: readb 0x%02x : 0x%02x\n", addr, val); | |
158 | #endif | |
159 | return val; | |
160 | } | |
161 | ||
a9deb8c6 AK |
162 | static void bmdma_write(void *opaque, target_phys_addr_t addr, |
163 | uint64_t val, unsigned size) | |
70ae65f5 | 164 | { |
a9deb8c6 AK |
165 | BMDMAState *bm = opaque; |
166 | PCIIDEState *pci_dev = bm->pci_dev; | |
70ae65f5 | 167 | |
a9deb8c6 AK |
168 | if (size != 1) { |
169 | return; | |
170 | } | |
70ae65f5 | 171 | |
4c3df0ec JQ |
172 | #ifdef DEBUG_IDE |
173 | printf("bmdma: writeb 0x%02x : 0x%02x\n", addr, val); | |
174 | #endif | |
175 | switch(addr & 3) { | |
50a48094 | 176 | case 0: |
a9deb8c6 | 177 | bmdma_cmd_writeb(bm, val); |
50a48094 | 178 | break; |
4c3df0ec | 179 | case 1: |
58c0e732 JQ |
180 | pci_dev->dev.config[MRDMODE] = |
181 | (pci_dev->dev.config[MRDMODE] & ~0x30) | (val & 0x30); | |
182 | cmd646_update_irq(pci_dev); | |
4c3df0ec JQ |
183 | break; |
184 | case 2: | |
185 | bm->status = (val & 0x60) | (bm->status & 1) | (bm->status & ~val & 0x06); | |
186 | break; | |
187 | case 3: | |
70ae65f5 | 188 | if (bm == &pci_dev->bmdma[0]) |
58c0e732 JQ |
189 | pci_dev->dev.config[UDIDETCR0] = val; |
190 | else | |
191 | pci_dev->dev.config[UDIDETCR1] = val; | |
4c3df0ec JQ |
192 | break; |
193 | } | |
194 | } | |
195 | ||
a9deb8c6 AK |
196 | static MemoryRegionOps cmd646_bmdma_ops = { |
197 | .read = bmdma_read, | |
198 | .write = bmdma_write, | |
199 | }; | |
70ae65f5 | 200 | |
a9deb8c6 | 201 | static void bmdma_setup_bar(PCIIDEState *d) |
4c3df0ec | 202 | { |
a9deb8c6 | 203 | BMDMAState *bm; |
4c3df0ec JQ |
204 | int i; |
205 | ||
a9deb8c6 | 206 | memory_region_init(&d->bmdma_bar, "cmd646-bmdma", 16); |
4c3df0ec | 207 | for(i = 0;i < 2; i++) { |
a9deb8c6 AK |
208 | bm = &d->bmdma[i]; |
209 | memory_region_init_io(&bm->extra_io, &cmd646_bmdma_ops, bm, | |
210 | "cmd646-bmdma-bus", 4); | |
211 | memory_region_add_subregion(&d->bmdma_bar, i * 8, &bm->extra_io); | |
212 | memory_region_init_io(&bm->addr_ioport, &bmdma_addr_ioport_ops, bm, | |
213 | "cmd646-bmdma-ioport", 4); | |
214 | memory_region_add_subregion(&d->bmdma_bar, i * 8 + 4, &bm->addr_ioport); | |
4c3df0ec JQ |
215 | } |
216 | } | |
217 | ||
218 | /* XXX: call it also when the MRDMODE is changed from the PCI config | |
219 | registers */ | |
220 | static void cmd646_update_irq(PCIIDEState *d) | |
221 | { | |
222 | int pci_level; | |
223 | pci_level = ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH0) && | |
224 | !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH0)) || | |
225 | ((d->dev.config[MRDMODE] & MRDMODE_INTR_CH1) && | |
226 | !(d->dev.config[MRDMODE] & MRDMODE_BLK_CH1)); | |
227 | qemu_set_irq(d->dev.irq[0], pci_level); | |
228 | } | |
229 | ||
230 | /* the PCI irq level is the logical OR of the two channels */ | |
231 | static void cmd646_set_irq(void *opaque, int channel, int level) | |
232 | { | |
233 | PCIIDEState *d = opaque; | |
234 | int irq_mask; | |
235 | ||
236 | irq_mask = MRDMODE_INTR_CH0 << channel; | |
237 | if (level) | |
238 | d->dev.config[MRDMODE] |= irq_mask; | |
239 | else | |
240 | d->dev.config[MRDMODE] &= ~irq_mask; | |
241 | cmd646_update_irq(d); | |
242 | } | |
243 | ||
244 | static void cmd646_reset(void *opaque) | |
245 | { | |
246 | PCIIDEState *d = opaque; | |
247 | unsigned int i; | |
248 | ||
4a643563 BS |
249 | for (i = 0; i < 2; i++) { |
250 | ide_bus_reset(&d->bus[i]); | |
4a643563 | 251 | } |
4c3df0ec JQ |
252 | } |
253 | ||
254 | /* CMD646 PCI IDE controller */ | |
255 | static int pci_cmd646_ide_initfn(PCIDevice *dev) | |
256 | { | |
257 | PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev); | |
258 | uint8_t *pci_conf = d->dev.config; | |
259 | qemu_irq *irq; | |
61d9d6b0 | 260 | int i; |
4c3df0ec | 261 | |
409570a7 | 262 | pci_conf[PCI_CLASS_PROG] = 0x8f; |
4c3df0ec | 263 | |
4c3df0ec JQ |
264 | pci_conf[0x51] = 0x04; // enable IDE0 |
265 | if (d->secondary) { | |
266 | /* XXX: if not enabled, really disable the seconday IDE controller */ | |
267 | pci_conf[0x51] |= 0x08; /* enable IDE1 */ | |
268 | } | |
269 | ||
a9deb8c6 AK |
270 | setup_cmd646_bar(d, 0); |
271 | setup_cmd646_bar(d, 1); | |
e824b2cc AK |
272 | pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[0].data); |
273 | pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[0].cmd); | |
274 | pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[1].data); | |
275 | pci_register_bar(dev, 3, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[1].cmd); | |
a9deb8c6 | 276 | bmdma_setup_bar(d); |
e824b2cc | 277 | pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar); |
4c3df0ec | 278 | |
409570a7 MT |
279 | /* TODO: RST# value should be 0 */ |
280 | pci_conf[PCI_INTERRUPT_PIN] = 0x01; // interrupt on pin 1 | |
4c3df0ec JQ |
281 | |
282 | irq = qemu_allocate_irqs(cmd646_set_irq, d, 2); | |
61d9d6b0 SH |
283 | for (i = 0; i < 2; i++) { |
284 | ide_bus_new(&d->bus[i], &d->dev.qdev, i); | |
285 | ide_init2(&d->bus[i], irq[i]); | |
286 | ||
a9deb8c6 | 287 | bmdma_init(&d->bus[i], &d->bmdma[i], d); |
f56b18c0 | 288 | d->bmdma[i].bus = &d->bus[i]; |
61d9d6b0 | 289 | qemu_add_vm_change_state_handler(d->bus[i].dma->ops->restart_cb, |
f56b18c0 | 290 | &d->bmdma[i].dma); |
61d9d6b0 | 291 | } |
4c3df0ec | 292 | |
0be71e32 | 293 | vmstate_register(&dev->qdev, 0, &vmstate_ide_pci, d); |
4c3df0ec | 294 | qemu_register_reset(cmd646_reset, d); |
4c3df0ec JQ |
295 | return 0; |
296 | } | |
297 | ||
a9deb8c6 AK |
298 | static int pci_cmd646_ide_exitfn(PCIDevice *dev) |
299 | { | |
300 | PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev); | |
301 | unsigned i; | |
302 | ||
303 | for (i = 0; i < 2; ++i) { | |
304 | memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].extra_io); | |
305 | memory_region_destroy(&d->bmdma[i].extra_io); | |
306 | memory_region_del_subregion(&d->bmdma_bar, &d->bmdma[i].addr_ioport); | |
307 | memory_region_destroy(&d->bmdma[i].addr_ioport); | |
308 | memory_region_destroy(&d->cmd646_bar[i].cmd); | |
309 | memory_region_destroy(&d->cmd646_bar[i].data); | |
310 | } | |
311 | memory_region_destroy(&d->bmdma_bar); | |
312 | ||
313 | return 0; | |
314 | } | |
315 | ||
4c3df0ec JQ |
316 | void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table, |
317 | int secondary_ide_enabled) | |
318 | { | |
319 | PCIDevice *dev; | |
320 | ||
556cd098 | 321 | dev = pci_create(bus, -1, "cmd646-ide"); |
4c3df0ec JQ |
322 | qdev_prop_set_uint32(&dev->qdev, "secondary", secondary_ide_enabled); |
323 | qdev_init_nofail(&dev->qdev); | |
324 | ||
325 | pci_ide_create_devs(dev, hd_table); | |
326 | } | |
327 | ||
328 | static PCIDeviceInfo cmd646_ide_info[] = { | |
329 | { | |
556cd098 | 330 | .qdev.name = "cmd646-ide", |
4c3df0ec JQ |
331 | .qdev.size = sizeof(PCIIDEState), |
332 | .init = pci_cmd646_ide_initfn, | |
a9deb8c6 | 333 | .exit = pci_cmd646_ide_exitfn, |
c04ca075 IY |
334 | .vendor_id = PCI_VENDOR_ID_CMD, |
335 | .device_id = PCI_DEVICE_ID_CMD_646, | |
336 | .revision = 0x07, // IDE controller revision | |
337 | .class_id = PCI_CLASS_STORAGE_IDE, | |
4c3df0ec JQ |
338 | .qdev.props = (Property[]) { |
339 | DEFINE_PROP_UINT32("secondary", PCIIDEState, secondary, 0), | |
340 | DEFINE_PROP_END_OF_LIST(), | |
341 | }, | |
342 | },{ | |
343 | /* end of list */ | |
344 | } | |
345 | }; | |
346 | ||
347 | static void cmd646_ide_register(void) | |
348 | { | |
349 | pci_qdev_register_many(cmd646_ide_info); | |
350 | } | |
351 | device_init(cmd646_ide_register); |