diff options
Diffstat (limited to 'hw/ppc/ppc440_pcix.c')
-rw-r--r-- | hw/ppc/ppc440_pcix.c | 50 |
1 files changed, 32 insertions, 18 deletions
diff --git a/hw/ppc/ppc440_pcix.c b/hw/ppc/ppc440_pcix.c index ee952314c8..91cbcd0504 100644 --- a/hw/ppc/ppc440_pcix.c +++ b/hw/ppc/ppc440_pcix.c @@ -169,7 +169,7 @@ static void ppc440_pcix_reg_write4(void *opaque, hwaddr addr, { struct PPC440PCIXState *s = opaque; - trace_ppc440_pcix_reg_read(addr, val); + trace_ppc440_pcix_reg_write(addr, val, size); switch (addr) { case PCI_VENDOR_ID ... PCI_MAX_LAT: stl_le_p(s->dev->config + addr, val); @@ -415,8 +415,15 @@ static void ppc440_pcix_reset(DeviceState *dev) s->sts = 0; } -/* All pins from each slot are tied to a single board IRQ. - * This may need further refactoring for other boards. */ +/* + * All four IRQ[ABCD] pins from all slots are tied to a single board + * IRQ, so our mapping function here maps everything to IRQ 0. + * The code in pci_change_irq_level() tracks the number of times + * the mapped IRQ is asserted and deasserted, so if multiple devices + * assert an IRQ at the same time the behaviour is correct. + * + * This may need further refactoring for boards that use multiple IRQ lines. + */ static int ppc440_pcix_map_irq(PCIDevice *pci_dev, int irq_num) { trace_ppc440_pcix_map_irq(pci_dev->devfn, irq_num, 0); @@ -442,28 +449,35 @@ static AddressSpace *ppc440_pcix_set_iommu(PCIBus *b, void *opaque, int devfn) return &s->bm_as; } -/* The default pci_host_data_{read,write} functions in pci/pci_host.c - * deny access to registers without bit 31 set but our clients want - * this to work so we have to override these here */ -static void pci_host_data_write(void *opaque, hwaddr addr, - uint64_t val, unsigned len) +/* + * Some guests on sam460ex write all kinds of garbage here such as + * missing enable bit and low bits set and still expect this to work + * (apparently it does on real hardware because these boot there) so + * we have to override these ops here and fix it up + */ +static void pci_host_config_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) { PCIHostState *s = opaque; - pci_data_write(s->bus, s->config_reg | (addr & 3), val, len); + + if (addr != 0 || len != 4) { + return; + } + s->config_reg = (val & 0xfffffffcULL) | (1UL << 31); } -static uint64_t pci_host_data_read(void *opaque, - hwaddr addr, unsigned len) +static uint64_t pci_host_config_read(void *opaque, hwaddr addr, + unsigned len) { PCIHostState *s = opaque; - uint32_t val; - val = pci_data_read(s->bus, s->config_reg | (addr & 3), len); + uint32_t val = s->config_reg; + return val; } -const MemoryRegionOps ppc440_pcix_host_data_ops = { - .read = pci_host_data_read, - .write = pci_host_data_write, +const MemoryRegionOps ppc440_pcix_host_conf_ops = { + .read = pci_host_config_read, + .write = pci_host_config_write, .endianness = DEVICE_LITTLE_ENDIAN, }; @@ -490,9 +504,9 @@ static void ppc440_pcix_realize(DeviceState *dev, Error **errp) pci_setup_iommu(h->bus, ppc440_pcix_set_iommu, s); memory_region_init(&s->container, OBJECT(s), "pci-container", PCI_ALL_SIZE); - memory_region_init_io(&h->conf_mem, OBJECT(s), &pci_host_conf_le_ops, + memory_region_init_io(&h->conf_mem, OBJECT(s), &ppc440_pcix_host_conf_ops, h, "pci-conf-idx", 4); - memory_region_init_io(&h->data_mem, OBJECT(s), &ppc440_pcix_host_data_ops, + memory_region_init_io(&h->data_mem, OBJECT(s), &pci_host_data_le_ops, h, "pci-conf-data", 4); memory_region_init_io(&s->iomem, OBJECT(s), &pci_reg_ops, s, "pci.reg", PPC440_REG_SIZE); |