| /* |
| * ARM Versatile/PB PCI host controller |
| * |
| * Copyright (c) 2006-2009 CodeSourcery. |
| * Written by Paul Brook |
| * |
| * This code is licensed under the LGPL. |
| */ |
| |
| #include "sysbus.h" |
| #include "pci/pci.h" |
| #include "pci/pci_bus.h" |
| #include "pci/pci_host.h" |
| #include "exec/address-spaces.h" |
| |
| typedef struct { |
| PCIHostState parent_obj; |
| |
| qemu_irq irq[4]; |
| MemoryRegion mem_config; |
| MemoryRegion mem_config2; |
| MemoryRegion pci_io_space; |
| PCIBus pci_bus; |
| PCIDevice pci_dev; |
| } PCIVPBState; |
| |
| #define TYPE_VERSATILE_PCI "versatile_pci" |
| #define PCI_VPB(obj) \ |
| OBJECT_CHECK(PCIVPBState, (obj), TYPE_VERSATILE_PCI) |
| |
| #define TYPE_VERSATILE_PCI_HOST "versatile_pci_host" |
| #define PCI_VPB_HOST(obj) \ |
| OBJECT_CHECK(PCIDevice), (obj), TYPE_VERSATILE_PCIHOST) |
| |
| static inline uint32_t vpb_pci_config_addr(hwaddr addr) |
| { |
| return addr & 0xffffff; |
| } |
| |
| static void pci_vpb_config_write(void *opaque, hwaddr addr, |
| uint64_t val, unsigned size) |
| { |
| pci_data_write(opaque, vpb_pci_config_addr(addr), val, size); |
| } |
| |
| static uint64_t pci_vpb_config_read(void *opaque, hwaddr addr, |
| unsigned size) |
| { |
| uint32_t val; |
| val = pci_data_read(opaque, vpb_pci_config_addr(addr), size); |
| return val; |
| } |
| |
| static const MemoryRegionOps pci_vpb_config_ops = { |
| .read = pci_vpb_config_read, |
| .write = pci_vpb_config_write, |
| .endianness = DEVICE_NATIVE_ENDIAN, |
| }; |
| |
| static int pci_vpb_map_irq(PCIDevice *d, int irq_num) |
| { |
| /* Slot to IRQ mapping for RealView Platform Baseboard 926 backplane |
| * name slot IntA IntB IntC IntD |
| * A 31 IRQ28 IRQ29 IRQ30 IRQ27 |
| * B 30 IRQ27 IRQ28 IRQ29 IRQ30 |
| * C 29 IRQ30 IRQ27 IRQ28 IRQ29 |
| * Slot C is for the host bridge; A and B the peripherals. |
| * Our output irqs 0..3 correspond to the baseboard's 27..30. |
| */ |
| return (PCI_SLOT(d->devfn) + irq_num - 2) % PCI_NUM_PINS; |
| } |
| |
| static void pci_vpb_set_irq(void *opaque, int irq_num, int level) |
| { |
| qemu_irq *pic = opaque; |
| |
| qemu_set_irq(pic[irq_num], level); |
| } |
| |
| static void pci_vpb_init(Object *obj) |
| { |
| PCIHostState *h = PCI_HOST_BRIDGE(obj); |
| PCIVPBState *s = PCI_VPB(obj); |
| |
| // XXX size of region? |
| memory_region_init(&s->pci_io_space, "pci_io", 0x100000); |
| |
| pci_bus_new_inplace(&s->pci_bus, DEVICE(obj), "pci", |
| get_system_memory(), &s->pci_io_space, |
| PCI_DEVFN(29, 0)); |
| h->bus = &s->pci_bus; |
| |
| object_initialize(&s->pci_dev, TYPE_VERSATILE_PCI_HOST); |
| qdev_set_parent_bus(DEVICE(&s->pci_dev), BUS(&s->pci_bus)); |
| object_property_set_int(OBJECT(&s->pci_dev), PCI_DEVFN(29, 0), "addr", |
| NULL); |
| } |
| |
| static void pci_vpb_realize(DeviceState *dev, Error **errp) |
| { |
| PCIVPBState *s = PCI_VPB(dev); |
| SysBusDevice *sbd = SYS_BUS_DEVICE(dev); |
| int i; |
| |
| for (i = 0; i < 4; i++) { |
| sysbus_init_irq(sbd, &s->irq[i]); |
| } |
| |
| pci_bus_irqs(&s->pci_bus, pci_vpb_set_irq, pci_vpb_map_irq, s->irq, 4); |
| |
| /* ??? Register memory space. */ |
| |
| /* Our memory regions are: |
| * 0 : PCI self config window |
| * 1 : PCI config window |
| * 2 : PCI IO window |
| */ |
| memory_region_init_io(&s->mem_config, &pci_vpb_config_ops, &s->pci_bus, |
| "pci-vpb-selfconfig", 0x1000000); |
| sysbus_init_mmio(sbd, &s->mem_config); |
| memory_region_init_io(&s->mem_config2, &pci_vpb_config_ops, &s->pci_bus, |
| "pci-vpb-config", 0x1000000); |
| sysbus_init_mmio(sbd, &s->mem_config2); |
| |
| sysbus_init_mmio(sbd, &s->pci_io_space); |
| |
| /* TODO Remove once realize propagates to child devices. */ |
| object_property_set_bool(OBJECT(&s->pci_dev), true, "realized", errp); |
| } |
| |
| static int versatile_pci_host_init(PCIDevice *d) |
| { |
| pci_set_word(d->config + PCI_STATUS, |
| PCI_STATUS_66MHZ | PCI_STATUS_DEVSEL_MEDIUM); |
| pci_set_byte(d->config + PCI_LATENCY_TIMER, 0x10); |
| return 0; |
| } |
| |
| static void versatile_pci_host_class_init(ObjectClass *klass, void *data) |
| { |
| PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
| |
| k->init = versatile_pci_host_init; |
| k->vendor_id = PCI_VENDOR_ID_XILINX; |
| k->device_id = PCI_DEVICE_ID_XILINX_XC2VP30; |
| k->class_id = PCI_CLASS_PROCESSOR_CO; |
| } |
| |
| static const TypeInfo versatile_pci_host_info = { |
| .name = TYPE_VERSATILE_PCI_HOST, |
| .parent = TYPE_PCI_DEVICE, |
| .instance_size = sizeof(PCIDevice), |
| .class_init = versatile_pci_host_class_init, |
| }; |
| |
| static void pci_vpb_class_init(ObjectClass *klass, void *data) |
| { |
| DeviceClass *dc = DEVICE_CLASS(klass); |
| |
| dc->realize = pci_vpb_realize; |
| } |
| |
| static const TypeInfo pci_vpb_info = { |
| .name = TYPE_VERSATILE_PCI, |
| .parent = TYPE_PCI_HOST_BRIDGE, |
| .instance_size = sizeof(PCIVPBState), |
| .instance_init = pci_vpb_init, |
| .class_init = pci_vpb_class_init, |
| }; |
| |
| static void versatile_pci_register_types(void) |
| { |
| type_register_static(&pci_vpb_info); |
| type_register_static(&versatile_pci_host_info); |
| } |
| |
| type_init(versatile_pci_register_types) |