]>
Commit | Line | Data |
---|---|---|
3cbee15b JM |
1 | /* |
2 | * PowerMac MacIO device emulation | |
3 | * | |
4 | * Copyright (c) 2005-2007 Fabrice Bellard | |
5 | * Copyright (c) 2007 Jocelyn Mayer | |
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 | */ | |
83c9f4ca PB |
25 | #include "hw/hw.h" |
26 | #include "hw/ppc/mac.h" | |
27 | #include "hw/pci/pci.h" | |
0d09e41a PB |
28 | #include "hw/ppc/mac_dbdma.h" |
29 | #include "hw/char/escc.h" | |
3cbee15b | 30 | |
fcf1bbab AF |
31 | #define TYPE_MACIO "macio" |
32 | #define MACIO(obj) OBJECT_CHECK(MacIOState, (obj), TYPE_MACIO) | |
33 | ||
d8c51b05 AL |
34 | typedef struct MacIOState |
35 | { | |
fcf1bbab | 36 | /*< private >*/ |
d8c51b05 | 37 | PCIDevice parent; |
fcf1bbab AF |
38 | /*< public >*/ |
39 | ||
23c5e4ca | 40 | MemoryRegion bar; |
45fa67fb | 41 | CUDAState cuda; |
07a7484e | 42 | void *dbdma; |
23c5e4ca | 43 | MemoryRegion *pic_mem; |
23c5e4ca | 44 | MemoryRegion *escc_mem; |
b981289c | 45 | uint64_t frequency; |
d8c51b05 | 46 | } MacIOState; |
3cbee15b | 47 | |
95ed3b7c AF |
48 | #define OLDWORLD_MACIO(obj) \ |
49 | OBJECT_CHECK(OldWorldMacIOState, (obj), TYPE_OLDWORLD_MACIO) | |
50 | ||
51 | typedef struct OldWorldMacIOState { | |
52 | /*< private >*/ | |
53 | MacIOState parent_obj; | |
54 | /*< public >*/ | |
55 | ||
14eefd0e | 56 | qemu_irq irqs[5]; |
07a7484e | 57 | |
95ed3b7c | 58 | MacIONVRAMState nvram; |
14eefd0e | 59 | MACIOIDEState ide[2]; |
95ed3b7c AF |
60 | } OldWorldMacIOState; |
61 | ||
07a7484e AF |
62 | #define NEWWORLD_MACIO(obj) \ |
63 | OBJECT_CHECK(NewWorldMacIOState, (obj), TYPE_NEWWORLD_MACIO) | |
64 | ||
65 | typedef struct NewWorldMacIOState { | |
66 | /*< private >*/ | |
67 | MacIOState parent_obj; | |
68 | /*< public >*/ | |
45fa67fb | 69 | qemu_irq irqs[5]; |
07a7484e AF |
70 | MACIOIDEState ide[2]; |
71 | } NewWorldMacIOState; | |
72 | ||
0d54a502 AG |
73 | /* |
74 | * The mac-io has two interfaces to the ESCC. One is called "escc-legacy", | |
75 | * while the other one is the normal, current ESCC interface. | |
76 | * | |
77 | * The magic below creates memory aliases to spawn the escc-legacy device | |
78 | * purely by rerouting the respective registers to our escc region. This | |
79 | * works because the only difference between the two memory regions is the | |
80 | * register layout, not their semantics. | |
81 | * | |
82 | * Reference: ftp://ftp.software.ibm.com/rs6000/technology/spec/chrp/inwork/CHRP_IORef_1.0.pdf | |
83 | */ | |
84 | static void macio_escc_legacy_setup(MacIOState *macio_state) | |
85 | { | |
86 | MemoryRegion *escc_legacy = g_new(MemoryRegion, 1); | |
87 | MemoryRegion *bar = &macio_state->bar; | |
88 | int i; | |
89 | static const int maps[] = { | |
90 | 0x00, 0x00, | |
91 | 0x02, 0x20, | |
92 | 0x04, 0x10, | |
93 | 0x06, 0x30, | |
94 | 0x08, 0x40, | |
95 | 0x0A, 0x50, | |
96 | 0x60, 0x60, | |
97 | 0x70, 0x70, | |
98 | 0x80, 0x70, | |
99 | 0x90, 0x80, | |
100 | 0xA0, 0x90, | |
101 | 0xB0, 0xA0, | |
102 | 0xC0, 0xB0, | |
103 | 0xD0, 0xC0, | |
104 | 0xE0, 0xD0, | |
105 | 0xF0, 0xE0, | |
106 | }; | |
107 | ||
2c9b15ca | 108 | memory_region_init(escc_legacy, NULL, "escc-legacy", 256); |
0d54a502 AG |
109 | for (i = 0; i < ARRAY_SIZE(maps); i += 2) { |
110 | MemoryRegion *port = g_new(MemoryRegion, 1); | |
2c9b15ca PB |
111 | memory_region_init_alias(port, NULL, "escc-legacy-port", |
112 | macio_state->escc_mem, maps[i+1], 0x2); | |
0d54a502 AG |
113 | memory_region_add_subregion(escc_legacy, maps[i], port); |
114 | } | |
115 | ||
116 | memory_region_add_subregion(bar, 0x12000, escc_legacy); | |
117 | } | |
118 | ||
d8c51b05 | 119 | static void macio_bar_setup(MacIOState *macio_state) |
3cbee15b | 120 | { |
23c5e4ca | 121 | MemoryRegion *bar = &macio_state->bar; |
3cbee15b | 122 | |
23c5e4ca AK |
123 | if (macio_state->escc_mem) { |
124 | memory_region_add_subregion(bar, 0x13000, macio_state->escc_mem); | |
0d54a502 | 125 | macio_escc_legacy_setup(macio_state); |
7fa9ae1a | 126 | } |
3cbee15b JM |
127 | } |
128 | ||
d037834a | 129 | static int macio_common_initfn(PCIDevice *d) |
d8c51b05 | 130 | { |
7b925079 | 131 | MacIOState *s = MACIO(d); |
45fa67fb AF |
132 | SysBusDevice *sysbus_dev; |
133 | int ret; | |
7b925079 | 134 | |
d8c51b05 | 135 | d->config[0x3d] = 0x01; // interrupt on pin 1 |
7b925079 | 136 | |
45fa67fb AF |
137 | ret = qdev_init(DEVICE(&s->cuda)); |
138 | if (ret < 0) { | |
139 | return ret; | |
140 | } | |
141 | sysbus_dev = SYS_BUS_DEVICE(&s->cuda); | |
142 | memory_region_add_subregion(&s->bar, 0x16000, | |
143 | sysbus_mmio_get_region(sysbus_dev, 0)); | |
144 | ||
7b925079 AF |
145 | macio_bar_setup(s); |
146 | pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar); | |
147 | ||
d8c51b05 AL |
148 | return 0; |
149 | } | |
150 | ||
14eefd0e AG |
151 | static int macio_initfn_ide(MacIOState *s, MACIOIDEState *ide, qemu_irq irq0, |
152 | qemu_irq irq1, int dmaid) | |
153 | { | |
154 | SysBusDevice *sysbus_dev; | |
155 | ||
156 | sysbus_dev = SYS_BUS_DEVICE(ide); | |
157 | sysbus_connect_irq(sysbus_dev, 0, irq0); | |
158 | sysbus_connect_irq(sysbus_dev, 1, irq1); | |
159 | macio_ide_register_dma(ide, s->dbdma, dmaid); | |
160 | return qdev_init(DEVICE(ide)); | |
161 | } | |
162 | ||
d037834a AF |
163 | static int macio_oldworld_initfn(PCIDevice *d) |
164 | { | |
165 | MacIOState *s = MACIO(d); | |
95ed3b7c AF |
166 | OldWorldMacIOState *os = OLDWORLD_MACIO(d); |
167 | SysBusDevice *sysbus_dev; | |
14eefd0e AG |
168 | int i; |
169 | int cur_irq = 0; | |
d037834a AF |
170 | int ret = macio_common_initfn(d); |
171 | if (ret < 0) { | |
172 | return ret; | |
173 | } | |
174 | ||
45fa67fb | 175 | sysbus_dev = SYS_BUS_DEVICE(&s->cuda); |
14eefd0e | 176 | sysbus_connect_irq(sysbus_dev, 0, os->irqs[cur_irq++]); |
45fa67fb | 177 | |
95ed3b7c AF |
178 | ret = qdev_init(DEVICE(&os->nvram)); |
179 | if (ret < 0) { | |
180 | return ret; | |
181 | } | |
182 | sysbus_dev = SYS_BUS_DEVICE(&os->nvram); | |
183 | memory_region_add_subregion(&s->bar, 0x60000, | |
184 | sysbus_mmio_get_region(sysbus_dev, 0)); | |
185 | pmac_format_nvram_partition(&os->nvram, os->nvram.size); | |
186 | ||
d037834a AF |
187 | if (s->pic_mem) { |
188 | /* Heathrow PIC */ | |
189 | memory_region_add_subregion(&s->bar, 0x00000, s->pic_mem); | |
190 | } | |
191 | ||
14eefd0e AG |
192 | /* IDE buses */ |
193 | for (i = 0; i < ARRAY_SIZE(os->ide); i++) { | |
194 | qemu_irq irq0 = os->irqs[cur_irq++]; | |
195 | qemu_irq irq1 = os->irqs[cur_irq++]; | |
196 | ||
197 | ret = macio_initfn_ide(s, &os->ide[i], irq0, irq1, 0x16 + (i * 4)); | |
198 | if (ret < 0) { | |
199 | return ret; | |
200 | } | |
07a7484e AF |
201 | } |
202 | ||
d037834a AF |
203 | return 0; |
204 | } | |
205 | ||
213f0c4f AF |
206 | static void macio_init_ide(MacIOState *s, MACIOIDEState *ide, size_t ide_size, |
207 | int index) | |
14eefd0e AG |
208 | { |
209 | gchar *name; | |
210 | ||
213f0c4f | 211 | object_initialize(ide, ide_size, TYPE_MACIO_IDE); |
14eefd0e AG |
212 | qdev_set_parent_bus(DEVICE(ide), sysbus_get_default()); |
213 | memory_region_add_subregion(&s->bar, 0x1f000 + ((index + 1) * 0x1000), | |
214 | &ide->mem); | |
215 | name = g_strdup_printf("ide[%i]", index); | |
216 | object_property_add_child(OBJECT(s), name, OBJECT(ide), NULL); | |
217 | g_free(name); | |
218 | } | |
219 | ||
95ed3b7c AF |
220 | static void macio_oldworld_init(Object *obj) |
221 | { | |
07a7484e | 222 | MacIOState *s = MACIO(obj); |
95ed3b7c AF |
223 | OldWorldMacIOState *os = OLDWORLD_MACIO(obj); |
224 | DeviceState *dev; | |
14eefd0e | 225 | int i; |
95ed3b7c | 226 | |
07a7484e AF |
227 | qdev_init_gpio_out(DEVICE(obj), os->irqs, ARRAY_SIZE(os->irqs)); |
228 | ||
213f0c4f | 229 | object_initialize(&os->nvram, sizeof(os->nvram), TYPE_MACIO_NVRAM); |
95ed3b7c AF |
230 | dev = DEVICE(&os->nvram); |
231 | qdev_prop_set_uint32(dev, "size", 0x2000); | |
232 | qdev_prop_set_uint32(dev, "it_shift", 4); | |
07a7484e | 233 | |
14eefd0e | 234 | for (i = 0; i < 2; i++) { |
213f0c4f | 235 | macio_init_ide(s, &os->ide[i], sizeof(os->ide[i]), i); |
14eefd0e | 236 | } |
95ed3b7c AF |
237 | } |
238 | ||
a0f9fdfd AG |
239 | static void timer_write(void *opaque, hwaddr addr, uint64_t value, |
240 | unsigned size) | |
241 | { | |
242 | } | |
243 | ||
244 | static uint64_t timer_read(void *opaque, hwaddr addr, unsigned size) | |
245 | { | |
246 | uint32_t value = 0; | |
d696760b AG |
247 | uint64_t systime = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
248 | uint64_t kltime; | |
249 | ||
250 | kltime = muldiv64(systime, 4194300, get_ticks_per_sec() * 4); | |
251 | kltime = muldiv64(kltime, 18432000, 1048575); | |
a0f9fdfd AG |
252 | |
253 | switch (addr) { | |
254 | case 0x38: | |
d696760b | 255 | value = kltime; |
a0f9fdfd AG |
256 | break; |
257 | case 0x3c: | |
d696760b | 258 | value = kltime >> 32; |
a0f9fdfd AG |
259 | break; |
260 | } | |
261 | ||
262 | return value; | |
263 | } | |
264 | ||
265 | static const MemoryRegionOps timer_ops = { | |
266 | .read = timer_read, | |
267 | .write = timer_write, | |
9397a7c8 | 268 | .endianness = DEVICE_LITTLE_ENDIAN, |
a0f9fdfd AG |
269 | }; |
270 | ||
d037834a AF |
271 | static int macio_newworld_initfn(PCIDevice *d) |
272 | { | |
273 | MacIOState *s = MACIO(d); | |
07a7484e AF |
274 | NewWorldMacIOState *ns = NEWWORLD_MACIO(d); |
275 | SysBusDevice *sysbus_dev; | |
6c5819c4 | 276 | MemoryRegion *timer_memory = NULL; |
14eefd0e AG |
277 | int i; |
278 | int cur_irq = 0; | |
d037834a AF |
279 | int ret = macio_common_initfn(d); |
280 | if (ret < 0) { | |
281 | return ret; | |
282 | } | |
283 | ||
45fa67fb | 284 | sysbus_dev = SYS_BUS_DEVICE(&s->cuda); |
14eefd0e | 285 | sysbus_connect_irq(sysbus_dev, 0, ns->irqs[cur_irq++]); |
45fa67fb | 286 | |
d037834a AF |
287 | if (s->pic_mem) { |
288 | /* OpenPIC */ | |
289 | memory_region_add_subregion(&s->bar, 0x40000, s->pic_mem); | |
290 | } | |
291 | ||
14eefd0e AG |
292 | /* IDE buses */ |
293 | for (i = 0; i < ARRAY_SIZE(ns->ide); i++) { | |
294 | qemu_irq irq0 = ns->irqs[cur_irq++]; | |
295 | qemu_irq irq1 = ns->irqs[cur_irq++]; | |
07a7484e | 296 | |
14eefd0e AG |
297 | ret = macio_initfn_ide(s, &ns->ide[i], irq0, irq1, 0x16 + (i * 4)); |
298 | if (ret < 0) { | |
299 | return ret; | |
300 | } | |
07a7484e AF |
301 | } |
302 | ||
a0f9fdfd | 303 | /* Timer */ |
6c5819c4 | 304 | timer_memory = g_new(MemoryRegion, 1); |
a0f9fdfd AG |
305 | memory_region_init_io(timer_memory, OBJECT(s), &timer_ops, NULL, "timer", |
306 | 0x1000); | |
307 | memory_region_add_subregion(&s->bar, 0x15000, timer_memory); | |
308 | ||
d037834a AF |
309 | return 0; |
310 | } | |
311 | ||
07a7484e AF |
312 | static void macio_newworld_init(Object *obj) |
313 | { | |
314 | MacIOState *s = MACIO(obj); | |
315 | NewWorldMacIOState *ns = NEWWORLD_MACIO(obj); | |
316 | int i; | |
07a7484e AF |
317 | |
318 | qdev_init_gpio_out(DEVICE(obj), ns->irqs, ARRAY_SIZE(ns->irqs)); | |
319 | ||
320 | for (i = 0; i < 2; i++) { | |
213f0c4f | 321 | macio_init_ide(s, &ns->ide[i], sizeof(ns->ide[i]), i); |
07a7484e AF |
322 | } |
323 | } | |
324 | ||
fcf1bbab AF |
325 | static void macio_instance_init(Object *obj) |
326 | { | |
327 | MacIOState *s = MACIO(obj); | |
07a7484e | 328 | MemoryRegion *dbdma_mem; |
fcf1bbab | 329 | |
2c9b15ca | 330 | memory_region_init(&s->bar, NULL, "macio", 0x80000); |
07a7484e | 331 | |
213f0c4f | 332 | object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA); |
45fa67fb AF |
333 | qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); |
334 | object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL); | |
335 | ||
07a7484e AF |
336 | s->dbdma = DBDMA_init(&dbdma_mem); |
337 | memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem); | |
fcf1bbab AF |
338 | } |
339 | ||
02635923 MCA |
340 | static const VMStateDescription vmstate_macio_oldworld = { |
341 | .name = "macio-oldworld", | |
342 | .version_id = 0, | |
343 | .minimum_version_id = 0, | |
344 | .fields = (VMStateField[]) { | |
345 | VMSTATE_PCI_DEVICE(parent_obj.parent, OldWorldMacIOState), | |
346 | VMSTATE_END_OF_LIST() | |
347 | } | |
348 | }; | |
349 | ||
d037834a AF |
350 | static void macio_oldworld_class_init(ObjectClass *oc, void *data) |
351 | { | |
352 | PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); | |
02635923 | 353 | DeviceClass *dc = DEVICE_CLASS(oc); |
d037834a AF |
354 | |
355 | pdc->init = macio_oldworld_initfn; | |
356 | pdc->device_id = PCI_DEVICE_ID_APPLE_343S1201; | |
02635923 | 357 | dc->vmsd = &vmstate_macio_oldworld; |
d037834a AF |
358 | } |
359 | ||
02635923 MCA |
360 | static const VMStateDescription vmstate_macio_newworld = { |
361 | .name = "macio-newworld", | |
362 | .version_id = 0, | |
363 | .minimum_version_id = 0, | |
364 | .fields = (VMStateField[]) { | |
365 | VMSTATE_PCI_DEVICE(parent_obj.parent, NewWorldMacIOState), | |
366 | VMSTATE_END_OF_LIST() | |
367 | } | |
368 | }; | |
369 | ||
d037834a AF |
370 | static void macio_newworld_class_init(ObjectClass *oc, void *data) |
371 | { | |
372 | PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); | |
02635923 | 373 | DeviceClass *dc = DEVICE_CLASS(oc); |
d037834a AF |
374 | |
375 | pdc->init = macio_newworld_initfn; | |
376 | pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL; | |
02635923 | 377 | dc->vmsd = &vmstate_macio_newworld; |
d037834a AF |
378 | } |
379 | ||
b981289c AG |
380 | static Property macio_properties[] = { |
381 | DEFINE_PROP_UINT64("frequency", MacIOState, frequency, 0), | |
382 | DEFINE_PROP_END_OF_LIST() | |
383 | }; | |
384 | ||
40021f08 AL |
385 | static void macio_class_init(ObjectClass *klass, void *data) |
386 | { | |
387 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); | |
b981289c | 388 | DeviceClass *dc = DEVICE_CLASS(klass); |
40021f08 | 389 | |
40021f08 AL |
390 | k->vendor_id = PCI_VENDOR_ID_APPLE; |
391 | k->class_id = PCI_CLASS_OTHERS << 8; | |
b981289c | 392 | dc->props = macio_properties; |
40021f08 AL |
393 | } |
394 | ||
d037834a AF |
395 | static const TypeInfo macio_oldworld_type_info = { |
396 | .name = TYPE_OLDWORLD_MACIO, | |
397 | .parent = TYPE_MACIO, | |
95ed3b7c AF |
398 | .instance_size = sizeof(OldWorldMacIOState), |
399 | .instance_init = macio_oldworld_init, | |
d037834a AF |
400 | .class_init = macio_oldworld_class_init, |
401 | }; | |
402 | ||
403 | static const TypeInfo macio_newworld_type_info = { | |
404 | .name = TYPE_NEWWORLD_MACIO, | |
405 | .parent = TYPE_MACIO, | |
07a7484e AF |
406 | .instance_size = sizeof(NewWorldMacIOState), |
407 | .instance_init = macio_newworld_init, | |
d037834a AF |
408 | .class_init = macio_newworld_class_init, |
409 | }; | |
410 | ||
fcf1bbab AF |
411 | static const TypeInfo macio_type_info = { |
412 | .name = TYPE_MACIO, | |
39bffca2 AL |
413 | .parent = TYPE_PCI_DEVICE, |
414 | .instance_size = sizeof(MacIOState), | |
fcf1bbab | 415 | .instance_init = macio_instance_init, |
d037834a | 416 | .abstract = true, |
39bffca2 | 417 | .class_init = macio_class_init, |
d8c51b05 AL |
418 | }; |
419 | ||
83f7d43a | 420 | static void macio_register_types(void) |
d8c51b05 | 421 | { |
fcf1bbab | 422 | type_register_static(&macio_type_info); |
d037834a AF |
423 | type_register_static(&macio_oldworld_type_info); |
424 | type_register_static(&macio_newworld_type_info); | |
d8c51b05 AL |
425 | } |
426 | ||
83f7d43a | 427 | type_init(macio_register_types) |
d8c51b05 | 428 | |
d037834a | 429 | void macio_init(PCIDevice *d, |
07a7484e | 430 | MemoryRegion *pic_mem, |
d037834a | 431 | MemoryRegion *escc_mem) |
3cbee15b | 432 | { |
d037834a | 433 | MacIOState *macio_state = MACIO(d); |
3cbee15b | 434 | |
23c5e4ca | 435 | macio_state->pic_mem = pic_mem; |
23c5e4ca | 436 | macio_state->escc_mem = escc_mem; |
3cbee15b JM |
437 | /* Note: this code is strongly inspirated from the corresponding code |
438 | in PearPC */ | |
b981289c AG |
439 | qdev_prop_set_uint64(DEVICE(&macio_state->cuda), "frequency", |
440 | macio_state->frequency); | |
deb54399 | 441 | |
7b925079 | 442 | qdev_init_nofail(DEVICE(d)); |
3cbee15b | 443 | } |