| 1 | /* |
| 2 | * ARM Versatile/PB PCI host controller |
| 3 | * |
| 4 | * Copyright (c) 2006-2009 CodeSourcery. |
| 5 | * Written by Paul Brook |
| 6 | * |
| 7 | * This code is licensed under the LGPL. |
| 8 | */ |
| 9 | |
| 10 | #include "qemu/osdep.h" |
| 11 | #include "qemu/units.h" |
| 12 | #include "hw/core/sysbus.h" |
| 13 | #include "migration/vmstate.h" |
| 14 | #include "hw/core/irq.h" |
| 15 | #include "hw/pci/pci_device.h" |
| 16 | #include "hw/pci/pci_bus.h" |
| 17 | #include "hw/pci/pci_host.h" |
| 18 | #include "hw/core/qdev-properties.h" |
| 19 | #include "qemu/log.h" |
| 20 | #include "qemu/module.h" |
| 21 | #include "qom/object.h" |
| 22 | |
| 23 | /* Old and buggy versions of QEMU used the wrong mapping from |
| 24 | * PCI IRQs to system interrupt lines. Unfortunately the Linux |
| 25 | * kernel also had the corresponding bug in setting up interrupts |
| 26 | * (so older kernels work on QEMU and not on real hardware). |
| 27 | * We automatically detect these broken kernels and flip back |
| 28 | * to the broken irq mapping by spotting guest writes to the |
| 29 | * PCI_INTERRUPT_LINE register to see where the guest thinks |
| 30 | * interrupts are going to be routed. So we start in state |
| 31 | * ASSUME_OK on reset, and transition to either BROKEN or |
| 32 | * FORCE_OK at the first write to an INTERRUPT_LINE register for |
| 33 | * a slot where broken and correct interrupt mapping would differ. |
| 34 | * Once in either BROKEN or FORCE_OK we never transition again; |
| 35 | * this allows a newer kernel to use the INTERRUPT_LINE |
| 36 | * registers arbitrarily once it has indicated that it isn't |
| 37 | * broken in its init code somewhere. |
| 38 | * |
| 39 | * Unfortunately we have to cope with multiple different |
| 40 | * variants on the broken kernel behaviour: |
| 41 | * phase I (before kernel commit 1bc39ac5d) kernels assume old |
| 42 | * QEMU behaviour, so they use IRQ 27 for all slots |
| 43 | * phase II (1bc39ac5d and later, but before e3e92a7be6) kernels |
| 44 | * swizzle IRQs between slots, but do it wrongly, so they |
| 45 | * work only for every fourth PCI card, and only if (like old |
| 46 | * QEMU) the PCI host device is at slot 0 rather than where |
| 47 | * the h/w actually puts it |
| 48 | * phase III (e3e92a7be6 and later) kernels still swizzle IRQs between |
| 49 | * slots wrongly, but add a fixed offset of 64 to everything |
| 50 | * they write to PCI_INTERRUPT_LINE. |
| 51 | * |
| 52 | * We live in hope of a mythical phase IV kernel which might |
| 53 | * actually behave in ways that work on the hardware. Such a |
| 54 | * kernel should probably start off by writing some value neither |
| 55 | * 27 nor 91 to slot zero's PCI_INTERRUPT_LINE register to |
| 56 | * disable the autodetection. After that it can do what it likes. |
| 57 | * |
| 58 | * Slot % 4 | hw | I | II | III |
| 59 | * ------------------------------- |
| 60 | * 0 | 29 | 27 | 27 | 91 |
| 61 | * 1 | 30 | 27 | 28 | 92 |
| 62 | * 2 | 27 | 27 | 29 | 93 |
| 63 | * 3 | 28 | 27 | 30 | 94 |
| 64 | * |
| 65 | * Since our autodetection is not perfect we also provide a |
| 66 | * property so the user can make us start in BROKEN or FORCE_OK |
| 67 | * on reset if they know they have a bad or good kernel. |
| 68 | */ |
| 69 | enum { |
| 70 | PCI_VPB_IRQMAP_ASSUME_OK, |
| 71 | PCI_VPB_IRQMAP_BROKEN, |
| 72 | PCI_VPB_IRQMAP_FORCE_OK, |
| 73 | }; |
| 74 | |
| 75 | struct PCIVPBState { |
| 76 | PCIHostState parent_obj; |
| 77 | |
| 78 | qemu_irq irq[4]; |
| 79 | MemoryRegion controlregs; |
| 80 | MemoryRegion mem_config; |
| 81 | MemoryRegion mem_config2; |
| 82 | /* Containers representing the PCI address spaces */ |
| 83 | MemoryRegion pci_io_space; |
| 84 | MemoryRegion pci_mem_space; |
| 85 | /* Alias regions into PCI address spaces which we expose as sysbus regions. |
| 86 | * The offsets into pci_mem_space are controlled by the imap registers. |
| 87 | */ |
| 88 | MemoryRegion pci_io_window; |
| 89 | MemoryRegion pci_mem_window[3]; |
| 90 | PCIBus pci_bus; |
| 91 | PCIDevice pci_dev; |
| 92 | |
| 93 | /* Constant for life of device: */ |
| 94 | int realview; |
| 95 | uint32_t mem_win_size[3]; |
| 96 | uint8_t irq_mapping_prop; |
| 97 | |
| 98 | /* Variable state: */ |
| 99 | uint32_t imap[3]; |
| 100 | uint32_t smap[3]; |
| 101 | uint32_t selfid; |
| 102 | uint32_t flags; |
| 103 | uint8_t irq_mapping; |
| 104 | }; |
| 105 | typedef struct PCIVPBState PCIVPBState; |
| 106 | |
| 107 | static void pci_vpb_update_window(PCIVPBState *s, int i) |
| 108 | { |
| 109 | /* Adjust the offset of the alias region we use for |
| 110 | * the memory window i to account for a change in the |
| 111 | * value of the corresponding IMAP register. |
| 112 | * Note that the semantics of the IMAP register differ |
| 113 | * for realview and versatile variants of the controller. |
| 114 | */ |
| 115 | hwaddr offset; |
| 116 | if (s->realview) { |
| 117 | /* Top bits of register (masked according to window size) provide |
| 118 | * top bits of PCI address. |
| 119 | */ |
| 120 | offset = s->imap[i] & ~(s->mem_win_size[i] - 1); |
| 121 | } else { |
| 122 | /* Bottom 4 bits of register provide top 4 bits of PCI address */ |
| 123 | offset = s->imap[i] << 28; |
| 124 | } |
| 125 | memory_region_set_alias_offset(&s->pci_mem_window[i], offset); |
| 126 | } |
| 127 | |
| 128 | static void pci_vpb_update_all_windows(PCIVPBState *s) |
| 129 | { |
| 130 | /* Update all alias windows based on the current register state */ |
| 131 | int i; |
| 132 | |
| 133 | for (i = 0; i < 3; i++) { |
| 134 | pci_vpb_update_window(s, i); |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | static int pci_vpb_post_load(void *opaque, int version_id) |
| 139 | { |
| 140 | PCIVPBState *s = opaque; |
| 141 | pci_vpb_update_all_windows(s); |
| 142 | return 0; |
| 143 | } |
| 144 | |
| 145 | static const VMStateDescription pci_vpb_vmstate = { |
| 146 | .name = "versatile-pci", |
| 147 | .version_id = 1, |
| 148 | .minimum_version_id = 1, |
| 149 | .post_load = pci_vpb_post_load, |
| 150 | .fields = (const VMStateField[]) { |
| 151 | VMSTATE_UINT32_ARRAY(imap, PCIVPBState, 3), |
| 152 | VMSTATE_UINT32_ARRAY(smap, PCIVPBState, 3), |
| 153 | VMSTATE_UINT32(selfid, PCIVPBState), |
| 154 | VMSTATE_UINT32(flags, PCIVPBState), |
| 155 | VMSTATE_UINT8(irq_mapping, PCIVPBState), |
| 156 | VMSTATE_END_OF_LIST() |
| 157 | } |
| 158 | }; |
| 159 | |
| 160 | #define TYPE_VERSATILE_PCI "versatile_pci" |
| 161 | DECLARE_INSTANCE_CHECKER(PCIVPBState, PCI_VPB, |
| 162 | TYPE_VERSATILE_PCI) |
| 163 | |
| 164 | #define TYPE_VERSATILE_PCI_HOST "versatile_pci_host" |
| 165 | DECLARE_INSTANCE_CHECKER(PCIDevice, PCI_VPB_HOST, |
| 166 | TYPE_VERSATILE_PCI_HOST) |
| 167 | |
| 168 | typedef enum { |
| 169 | PCI_IMAP0 = 0x0, |
| 170 | PCI_IMAP1 = 0x4, |
| 171 | PCI_IMAP2 = 0x8, |
| 172 | PCI_SELFID = 0xc, |
| 173 | PCI_FLAGS = 0x10, |
| 174 | PCI_SMAP0 = 0x14, |
| 175 | PCI_SMAP1 = 0x18, |
| 176 | PCI_SMAP2 = 0x1c, |
| 177 | } PCIVPBControlRegs; |
| 178 | |
| 179 | static void pci_vpb_reg_write(void *opaque, hwaddr addr, |
| 180 | uint64_t val, unsigned size) |
| 181 | { |
| 182 | PCIVPBState *s = opaque; |
| 183 | |
| 184 | switch (addr) { |
| 185 | case PCI_IMAP0: |
| 186 | case PCI_IMAP1: |
| 187 | case PCI_IMAP2: |
| 188 | { |
| 189 | int win = (addr - PCI_IMAP0) >> 2; |
| 190 | s->imap[win] = val; |
| 191 | pci_vpb_update_window(s, win); |
| 192 | break; |
| 193 | } |
| 194 | case PCI_SELFID: |
| 195 | s->selfid = val; |
| 196 | break; |
| 197 | case PCI_FLAGS: |
| 198 | s->flags = val; |
| 199 | break; |
| 200 | case PCI_SMAP0: |
| 201 | case PCI_SMAP1: |
| 202 | case PCI_SMAP2: |
| 203 | { |
| 204 | int win = (addr - PCI_SMAP0) >> 2; |
| 205 | s->smap[win] = val; |
| 206 | break; |
| 207 | } |
| 208 | default: |
| 209 | qemu_log_mask(LOG_GUEST_ERROR, |
| 210 | "pci_vpb_reg_write: Bad offset %x\n", (int)addr); |
| 211 | break; |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | static uint64_t pci_vpb_reg_read(void *opaque, hwaddr addr, |
| 216 | unsigned size) |
| 217 | { |
| 218 | PCIVPBState *s = opaque; |
| 219 | |
| 220 | switch (addr) { |
| 221 | case PCI_IMAP0: |
| 222 | case PCI_IMAP1: |
| 223 | case PCI_IMAP2: |
| 224 | { |
| 225 | int win = (addr - PCI_IMAP0) >> 2; |
| 226 | return s->imap[win]; |
| 227 | } |
| 228 | case PCI_SELFID: |
| 229 | return s->selfid; |
| 230 | case PCI_FLAGS: |
| 231 | return s->flags; |
| 232 | case PCI_SMAP0: |
| 233 | case PCI_SMAP1: |
| 234 | case PCI_SMAP2: |
| 235 | { |
| 236 | int win = (addr - PCI_SMAP0) >> 2; |
| 237 | return s->smap[win]; |
| 238 | } |
| 239 | default: |
| 240 | qemu_log_mask(LOG_GUEST_ERROR, |
| 241 | "pci_vpb_reg_read: Bad offset %x\n", (int)addr); |
| 242 | return 0; |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | static const MemoryRegionOps pci_vpb_reg_ops = { |
| 247 | .read = pci_vpb_reg_read, |
| 248 | .write = pci_vpb_reg_write, |
| 249 | .endianness = DEVICE_LITTLE_ENDIAN, |
| 250 | .valid = { |
| 251 | .min_access_size = 4, |
| 252 | .max_access_size = 4, |
| 253 | }, |
| 254 | }; |
| 255 | |
| 256 | static int pci_vpb_broken_irq(int slot, int irq) |
| 257 | { |
| 258 | /* Determine whether this IRQ value for this slot represents a |
| 259 | * known broken Linux kernel behaviour for this slot. |
| 260 | * Return one of the PCI_VPB_IRQMAP_ constants: |
| 261 | * BROKEN : if this definitely looks like a broken kernel |
| 262 | * FORCE_OK : if this definitely looks good |
| 263 | * ASSUME_OK : if we can't tell |
| 264 | */ |
| 265 | slot %= PCI_NUM_PINS; |
| 266 | |
| 267 | if (irq == 27) { |
| 268 | if (slot == 2) { |
| 269 | /* Might be a Phase I kernel, or might be a fixed kernel, |
| 270 | * since slot 2 is where we expect this IRQ. |
| 271 | */ |
| 272 | return PCI_VPB_IRQMAP_ASSUME_OK; |
| 273 | } |
| 274 | /* Phase I kernel */ |
| 275 | return PCI_VPB_IRQMAP_BROKEN; |
| 276 | } |
| 277 | if (irq == slot + 27) { |
| 278 | /* Phase II kernel */ |
| 279 | return PCI_VPB_IRQMAP_BROKEN; |
| 280 | } |
| 281 | if (irq == slot + 27 + 64) { |
| 282 | /* Phase III kernel */ |
| 283 | return PCI_VPB_IRQMAP_BROKEN; |
| 284 | } |
| 285 | /* Anything else must be a fixed kernel, possibly using an |
| 286 | * arbitrary irq map. |
| 287 | */ |
| 288 | return PCI_VPB_IRQMAP_FORCE_OK; |
| 289 | } |
| 290 | |
| 291 | static void pci_vpb_config_write(void *opaque, hwaddr addr, |
| 292 | uint64_t val, unsigned size) |
| 293 | { |
| 294 | PCIVPBState *s = opaque; |
| 295 | if (!s->realview && (addr & 0xff) == PCI_INTERRUPT_LINE |
| 296 | && s->irq_mapping == PCI_VPB_IRQMAP_ASSUME_OK) { |
| 297 | uint8_t devfn = addr >> 8; |
| 298 | s->irq_mapping = pci_vpb_broken_irq(PCI_SLOT(devfn), val); |
| 299 | } |
| 300 | pci_data_write(&s->pci_bus, addr, val, size); |
| 301 | } |
| 302 | |
| 303 | static uint64_t pci_vpb_config_read(void *opaque, hwaddr addr, |
| 304 | unsigned size) |
| 305 | { |
| 306 | PCIVPBState *s = opaque; |
| 307 | uint32_t val; |
| 308 | val = pci_data_read(&s->pci_bus, addr, size); |
| 309 | return val; |
| 310 | } |
| 311 | |
| 312 | static const MemoryRegionOps pci_vpb_config_ops = { |
| 313 | .read = pci_vpb_config_read, |
| 314 | .write = pci_vpb_config_write, |
| 315 | .endianness = DEVICE_LITTLE_ENDIAN, |
| 316 | }; |
| 317 | |
| 318 | static int pci_vpb_map_irq(PCIDevice *d, int irq_num) |
| 319 | { |
| 320 | PCIVPBState *s = container_of(pci_get_bus(d), PCIVPBState, pci_bus); |
| 321 | |
| 322 | if (s->irq_mapping == PCI_VPB_IRQMAP_BROKEN) { |
| 323 | /* Legacy broken IRQ mapping for compatibility with old and |
| 324 | * buggy Linux guests |
| 325 | */ |
| 326 | return irq_num; |
| 327 | } |
| 328 | |
| 329 | /* Slot to IRQ mapping for RealView Platform Baseboard 926 backplane |
| 330 | * name slot IntA IntB IntC IntD |
| 331 | * A 31 IRQ28 IRQ29 IRQ30 IRQ27 |
| 332 | * B 30 IRQ27 IRQ28 IRQ29 IRQ30 |
| 333 | * C 29 IRQ30 IRQ27 IRQ28 IRQ29 |
| 334 | * Slot C is for the host bridge; A and B the peripherals. |
| 335 | * Our output irqs 0..3 correspond to the baseboard's 27..30. |
| 336 | * |
| 337 | * This mapping function takes account of an oddity in the PB926 |
| 338 | * board wiring, where the FPGA's P_nINTA input is connected to |
| 339 | * the INTB connection on the board PCI edge connector, P_nINTB |
| 340 | * is connected to INTC, and so on, so everything is one number |
| 341 | * further round from where you might expect. |
| 342 | */ |
| 343 | return pci_swizzle_map_irq_fn(d, irq_num + 2); |
| 344 | } |
| 345 | |
| 346 | static int pci_vpb_rv_map_irq(PCIDevice *d, int irq_num) |
| 347 | { |
| 348 | /* Slot to IRQ mapping for RealView EB and PB1176 backplane |
| 349 | * name slot IntA IntB IntC IntD |
| 350 | * A 31 IRQ50 IRQ51 IRQ48 IRQ49 |
| 351 | * B 30 IRQ49 IRQ50 IRQ51 IRQ48 |
| 352 | * C 29 IRQ48 IRQ49 IRQ50 IRQ51 |
| 353 | * Slot C is for the host bridge; A and B the peripherals. |
| 354 | * Our output irqs 0..3 correspond to the baseboard's 48..51. |
| 355 | * |
| 356 | * The PB1176 and EB boards don't have the PB926 wiring oddity |
| 357 | * described above; P_nINTA connects to INTA, P_nINTB to INTB |
| 358 | * and so on, which is why this mapping function is different. |
| 359 | */ |
| 360 | return pci_swizzle_map_irq_fn(d, irq_num + 3); |
| 361 | } |
| 362 | |
| 363 | static void pci_vpb_set_irq(void *opaque, int irq_num, int level) |
| 364 | { |
| 365 | qemu_irq *pic = opaque; |
| 366 | |
| 367 | qemu_set_irq(pic[irq_num], level); |
| 368 | } |
| 369 | |
| 370 | static void pci_vpb_reset(DeviceState *d) |
| 371 | { |
| 372 | PCIVPBState *s = PCI_VPB(d); |
| 373 | |
| 374 | s->imap[0] = 0; |
| 375 | s->imap[1] = 0; |
| 376 | s->imap[2] = 0; |
| 377 | s->smap[0] = 0; |
| 378 | s->smap[1] = 0; |
| 379 | s->smap[2] = 0; |
| 380 | s->selfid = 0; |
| 381 | s->flags = 0; |
| 382 | s->irq_mapping = s->irq_mapping_prop; |
| 383 | |
| 384 | pci_vpb_update_all_windows(s); |
| 385 | } |
| 386 | |
| 387 | static void pci_vpb_init(Object *obj) |
| 388 | { |
| 389 | PCIVPBState *s = PCI_VPB(obj); |
| 390 | |
| 391 | /* Window sizes for VersatilePB; realview_pci's init will override */ |
| 392 | s->mem_win_size[0] = 0x0c000000; |
| 393 | s->mem_win_size[1] = 0x10000000; |
| 394 | s->mem_win_size[2] = 0x10000000; |
| 395 | } |
| 396 | |
| 397 | static void pci_vpb_realize(DeviceState *dev, Error **errp) |
| 398 | { |
| 399 | PCIVPBState *s = PCI_VPB(dev); |
| 400 | PCIHostState *h = PCI_HOST_BRIDGE(dev); |
| 401 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); |
| 402 | pci_map_irq_fn mapfn; |
| 403 | int i; |
| 404 | |
| 405 | memory_region_init(&s->pci_io_space, OBJECT(s), "pci_io", 4 * GiB); |
| 406 | memory_region_init(&s->pci_mem_space, OBJECT(s), "pci_mem", 4 * GiB); |
| 407 | |
| 408 | pci_root_bus_init(&s->pci_bus, sizeof(s->pci_bus), dev, "pci", |
| 409 | &s->pci_mem_space, &s->pci_io_space, |
| 410 | PCI_DEVFN(11, 0), TYPE_PCI_BUS); |
| 411 | h->bus = &s->pci_bus; |
| 412 | |
| 413 | object_initialize(&s->pci_dev, sizeof(s->pci_dev), TYPE_VERSATILE_PCI_HOST); |
| 414 | |
| 415 | for (i = 0; i < 4; i++) { |
| 416 | sysbus_init_irq(sbd, &s->irq[i]); |
| 417 | } |
| 418 | |
| 419 | if (s->realview) { |
| 420 | mapfn = pci_vpb_rv_map_irq; |
| 421 | } else { |
| 422 | mapfn = pci_vpb_map_irq; |
| 423 | } |
| 424 | |
| 425 | pci_bus_irqs(&s->pci_bus, pci_vpb_set_irq, s->irq, 4); |
| 426 | pci_bus_map_irqs(&s->pci_bus, mapfn); |
| 427 | |
| 428 | /* Our memory regions are: |
| 429 | * 0 : our control registers |
| 430 | * 1 : PCI self config window |
| 431 | * 2 : PCI config window |
| 432 | * 3 : PCI IO window |
| 433 | * 4..6 : PCI memory windows |
| 434 | */ |
| 435 | memory_region_init_io(&s->controlregs, OBJECT(s), &pci_vpb_reg_ops, s, |
| 436 | "pci-vpb-regs", 0x1000); |
| 437 | sysbus_init_mmio(sbd, &s->controlregs); |
| 438 | memory_region_init_io(&s->mem_config, OBJECT(s), &pci_vpb_config_ops, s, |
| 439 | "pci-vpb-selfconfig", 0x1000000); |
| 440 | sysbus_init_mmio(sbd, &s->mem_config); |
| 441 | memory_region_init_io(&s->mem_config2, OBJECT(s), &pci_vpb_config_ops, s, |
| 442 | "pci-vpb-config", 0x1000000); |
| 443 | sysbus_init_mmio(sbd, &s->mem_config2); |
| 444 | |
| 445 | /* The window into I/O space is always into a fixed base address; |
| 446 | * its size is the same for both realview and versatile. |
| 447 | */ |
| 448 | memory_region_init_alias(&s->pci_io_window, OBJECT(s), "pci-vbp-io-window", |
| 449 | &s->pci_io_space, 0, 0x100000); |
| 450 | |
| 451 | sysbus_init_mmio(sbd, &s->pci_io_space); |
| 452 | |
| 453 | /* Create the alias regions corresponding to our three windows onto |
| 454 | * PCI memory space. The sizes vary from board to board; the base |
| 455 | * offsets are guest controllable via the IMAP registers. |
| 456 | */ |
| 457 | for (i = 0; i < 3; i++) { |
| 458 | memory_region_init_alias(&s->pci_mem_window[i], OBJECT(s), "pci-vbp-window", |
| 459 | &s->pci_mem_space, 0, s->mem_win_size[i]); |
| 460 | sysbus_init_mmio(sbd, &s->pci_mem_window[i]); |
| 461 | } |
| 462 | |
| 463 | /* TODO Remove once realize propagates to child devices. */ |
| 464 | qdev_realize(DEVICE(&s->pci_dev), BUS(&s->pci_bus), errp); |
| 465 | } |
| 466 | |
| 467 | static void versatile_pci_host_realize(PCIDevice *d, Error **errp) |
| 468 | { |
| 469 | pci_set_word(d->config + PCI_STATUS, |
| 470 | PCI_STATUS_66MHZ | PCI_STATUS_DEVSEL_MEDIUM); |
| 471 | pci_set_byte(d->config + PCI_LATENCY_TIMER, 0x10); |
| 472 | } |
| 473 | |
| 474 | static void versatile_pci_host_class_init(ObjectClass *klass, const void *data) |
| 475 | { |
| 476 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
| 477 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 478 | |
| 479 | k->realize = versatile_pci_host_realize; |
| 480 | k->vendor_id = PCI_VENDOR_ID_XILINX; |
| 481 | k->device_id = PCI_DEVICE_ID_XILINX_XC2VP30; |
| 482 | k->class_id = PCI_CLASS_PROCESSOR_CO; |
| 483 | /* |
| 484 | * PCI-facing part of the host bridge, not usable without the |
| 485 | * host-facing part, which can't be device_add'ed, yet. |
| 486 | */ |
| 487 | dc->user_creatable = false; |
| 488 | } |
| 489 | |
| 490 | static const TypeInfo versatile_pci_host_info = { |
| 491 | .name = TYPE_VERSATILE_PCI_HOST, |
| 492 | .parent = TYPE_PCI_DEVICE, |
| 493 | .instance_size = sizeof(PCIDevice), |
| 494 | .class_init = versatile_pci_host_class_init, |
| 495 | .interfaces = (const InterfaceInfo[]) { |
| 496 | { INTERFACE_CONVENTIONAL_PCI_DEVICE }, |
| 497 | { }, |
| 498 | }, |
| 499 | }; |
| 500 | |
| 501 | static const Property pci_vpb_properties[] = { |
| 502 | DEFINE_PROP_UINT8("broken-irq-mapping", PCIVPBState, irq_mapping_prop, |
| 503 | PCI_VPB_IRQMAP_ASSUME_OK), |
| 504 | }; |
| 505 | |
| 506 | static void pci_vpb_class_init(ObjectClass *klass, const void *data) |
| 507 | { |
| 508 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 509 | |
| 510 | dc->realize = pci_vpb_realize; |
| 511 | device_class_set_legacy_reset(dc, pci_vpb_reset); |
| 512 | dc->vmsd = &pci_vpb_vmstate; |
| 513 | device_class_set_props(dc, pci_vpb_properties); |
| 514 | } |
| 515 | |
| 516 | static const TypeInfo pci_vpb_info = { |
| 517 | .name = TYPE_VERSATILE_PCI, |
| 518 | .parent = TYPE_PCI_HOST_BRIDGE, |
| 519 | .instance_size = sizeof(PCIVPBState), |
| 520 | .instance_init = pci_vpb_init, |
| 521 | .class_init = pci_vpb_class_init, |
| 522 | }; |
| 523 | |
| 524 | static void pci_realview_init(Object *obj) |
| 525 | { |
| 526 | PCIVPBState *s = PCI_VPB(obj); |
| 527 | |
| 528 | s->realview = 1; |
| 529 | /* The PCI window sizes are different on Realview boards */ |
| 530 | s->mem_win_size[0] = 0x01000000; |
| 531 | s->mem_win_size[1] = 0x04000000; |
| 532 | s->mem_win_size[2] = 0x08000000; |
| 533 | } |
| 534 | |
| 535 | static const TypeInfo pci_realview_info = { |
| 536 | .name = "realview_pci", |
| 537 | .parent = TYPE_VERSATILE_PCI, |
| 538 | .instance_init = pci_realview_init, |
| 539 | }; |
| 540 | |
| 541 | static void versatile_pci_register_types(void) |
| 542 | { |
| 543 | type_register_static(&pci_vpb_info); |
| 544 | type_register_static(&pci_realview_info); |
| 545 | type_register_static(&versatile_pci_host_info); |
| 546 | } |
| 547 | |
| 548 | type_init(versatile_pci_register_types) |