diff options
Diffstat (limited to 'hw/pci')
-rw-r--r-- | hw/pci/pci.c | 121 | ||||
-rw-r--r-- | hw/pci/pci.h | 34 | ||||
-rw-r--r-- | hw/pci/pci_bridge.c | 51 | ||||
-rw-r--r-- | hw/pci/pci_bridge.h | 17 | ||||
-rw-r--r-- | hw/pci/pci_bus.h | 10 | ||||
-rw-r--r-- | hw/pci/pcie.c | 16 | ||||
-rw-r--r-- | hw/pci/pcie.h | 1 | ||||
-rw-r--r-- | hw/pci/pcie_port.c | 18 |
8 files changed, 220 insertions, 48 deletions
diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 2f45c8f02f..d5257ed4c5 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -75,6 +75,11 @@ static const TypeInfo pci_bus_info = { .class_init = pci_bus_class_init, }; +static const TypeInfo pcie_bus_info = { + .name = TYPE_PCIE_BUS, + .parent = TYPE_PCI_BUS, +}; + static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num); static void pci_update_mappings(PCIDevice *d); static void pci_set_irq(void *opaque, int irq_num, int level); @@ -292,13 +297,23 @@ static void pci_bus_init(PCIBus *bus, DeviceState *parent, vmstate_register(NULL, -1, &vmstate_pcibus, bus); } +bool pci_bus_is_express(PCIBus *bus) +{ + return object_dynamic_cast(OBJECT(bus), TYPE_PCIE_BUS); +} + +bool pci_bus_is_root(PCIBus *bus) +{ + return !bus->parent_dev; +} + void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, const char *name, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, - uint8_t devfn_min) + uint8_t devfn_min, const char *typename) { - qbus_create_inplace(bus, TYPE_PCI_BUS, parent, name); + qbus_create_inplace(bus, typename, parent, name); pci_bus_init(bus, parent, name, address_space_mem, address_space_io, devfn_min); } @@ -306,11 +321,11 @@ void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, PCIBus *pci_bus_new(DeviceState *parent, const char *name, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, - uint8_t devfn_min) + uint8_t devfn_min, const char *typename) { PCIBus *bus; - bus = PCI_BUS(qbus_create(TYPE_PCI_BUS, parent, name)); + bus = PCI_BUS(qbus_create(typename, parent, name)); pci_bus_init(bus, parent, name, address_space_mem, address_space_io, devfn_min); return bus; @@ -338,19 +353,19 @@ PCIBus *pci_register_bus(DeviceState *parent, const char *name, void *irq_opaque, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, - uint8_t devfn_min, int nirq) + uint8_t devfn_min, int nirq, const char *typename) { PCIBus *bus; bus = pci_bus_new(parent, name, address_space_mem, - address_space_io, devfn_min); + address_space_io, devfn_min, typename); pci_bus_irqs(bus, set_irq, map_irq, irq_opaque, nirq); return bus; } int pci_bus_num(PCIBus *s) { - if (!s->parent_dev) + if (pci_bus_is_root(s)) return 0; /* pci host bridge */ return s->parent_dev->config[PCI_SECONDARY_BUS]; } @@ -668,12 +683,10 @@ static void pci_init_mask_bridge(PCIDevice *d) pci_word_test_and_set_mask(d->config + PCI_PREF_MEMORY_LIMIT, PCI_PREF_RANGE_TYPE_64); -/* TODO: add this define to pci_regs.h in linux and then in qemu. */ -#define PCI_BRIDGE_CTL_VGA_16BIT 0x10 /* VGA 16-bit decode */ -#define PCI_BRIDGE_CTL_DISCARD 0x100 /* Primary discard timer */ -#define PCI_BRIDGE_CTL_SEC_DISCARD 0x200 /* Secondary discard timer */ -#define PCI_BRIDGE_CTL_DISCARD_STATUS 0x400 /* Discard timer status */ -#define PCI_BRIDGE_CTL_DISCARD_SERR 0x800 /* Discard timer SERR# enable */ + /* + * TODO: Bridges default to 10-bit VGA decoding but we currently only + * implement 16-bit decoding (no alias support). + */ pci_set_word(d->wmask + PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR | @@ -875,6 +888,8 @@ static void pci_unregister_io_regions(PCIDevice *pci_dev) continue; memory_region_del_subregion(r->address_space, r->memory); } + + pci_unregister_vga(pci_dev); } static int pci_unregister_device(DeviceState *dev) @@ -937,6 +952,63 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, : pci_dev->bus->address_space_mem; } +static void pci_update_vga(PCIDevice *pci_dev) +{ + uint16_t cmd; + + if (!pci_dev->has_vga) { + return; + } + + cmd = pci_get_word(pci_dev->config + PCI_COMMAND); + + memory_region_set_enabled(pci_dev->vga_regions[QEMU_PCI_VGA_MEM], + cmd & PCI_COMMAND_MEMORY); + memory_region_set_enabled(pci_dev->vga_regions[QEMU_PCI_VGA_IO_LO], + cmd & PCI_COMMAND_IO); + memory_region_set_enabled(pci_dev->vga_regions[QEMU_PCI_VGA_IO_HI], + cmd & PCI_COMMAND_IO); +} + +void pci_register_vga(PCIDevice *pci_dev, MemoryRegion *mem, + MemoryRegion *io_lo, MemoryRegion *io_hi) +{ + assert(!pci_dev->has_vga); + + assert(memory_region_size(mem) == QEMU_PCI_VGA_MEM_SIZE); + pci_dev->vga_regions[QEMU_PCI_VGA_MEM] = mem; + memory_region_add_subregion_overlap(pci_dev->bus->address_space_mem, + QEMU_PCI_VGA_MEM_BASE, mem, 1); + + assert(memory_region_size(io_lo) == QEMU_PCI_VGA_IO_LO_SIZE); + pci_dev->vga_regions[QEMU_PCI_VGA_IO_LO] = io_lo; + memory_region_add_subregion_overlap(pci_dev->bus->address_space_io, + QEMU_PCI_VGA_IO_LO_BASE, io_lo, 1); + + assert(memory_region_size(io_hi) == QEMU_PCI_VGA_IO_HI_SIZE); + pci_dev->vga_regions[QEMU_PCI_VGA_IO_HI] = io_hi; + memory_region_add_subregion_overlap(pci_dev->bus->address_space_io, + QEMU_PCI_VGA_IO_HI_BASE, io_hi, 1); + pci_dev->has_vga = true; + + pci_update_vga(pci_dev); +} + +void pci_unregister_vga(PCIDevice *pci_dev) +{ + if (!pci_dev->has_vga) { + return; + } + + memory_region_del_subregion(pci_dev->bus->address_space_mem, + pci_dev->vga_regions[QEMU_PCI_VGA_MEM]); + memory_region_del_subregion(pci_dev->bus->address_space_io, + pci_dev->vga_regions[QEMU_PCI_VGA_IO_LO]); + memory_region_del_subregion(pci_dev->bus->address_space_io, + pci_dev->vga_regions[QEMU_PCI_VGA_IO_HI]); + pci_dev->has_vga = false; +} + pcibus_t pci_get_bar_addr(PCIDevice *pci_dev, int region_num) { return pci_dev->io_regions[region_num].addr; @@ -1036,6 +1108,8 @@ static void pci_update_mappings(PCIDevice *d) r->addr, r->memory, 1); } } + + pci_update_vga(d); } static inline int pci_irq_disabled(PCIDevice *d) @@ -1117,7 +1191,7 @@ static void pci_set_irq(void *opaque, int irq_num, int level) /* Special hooks used by device assignment */ void pci_bus_set_route_irq_fn(PCIBus *bus, pci_route_irq_fn route_intx_to_irq) { - assert(!bus->parent_dev); + assert(pci_bus_is_root(bus)); bus->route_intx_to_irq = route_intx_to_irq; } @@ -1156,9 +1230,10 @@ void pci_bus_fire_intx_routing_notifier(PCIBus *bus) if (dev && dev->intx_routing_notifier) { dev->intx_routing_notifier(dev); } - QLIST_FOREACH(sec, &bus->child, sibling) { - pci_bus_fire_intx_routing_notifier(sec); - } + } + + QLIST_FOREACH(sec, &bus->child, sibling) { + pci_bus_fire_intx_routing_notifier(sec); } } @@ -1581,7 +1656,7 @@ static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num) } /* Consider all bus numbers in range for the host pci bridge. */ - if (bus->parent_dev && + if (!pci_bus_is_root(bus) && !pci_secondary_bus_in_range(bus->parent_dev, bus_num)) { return NULL; } @@ -1589,7 +1664,7 @@ static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num) /* try child bus */ for (; bus; bus = sec) { QLIST_FOREACH(sec, &bus->child, sibling) { - assert(sec->parent_dev); + assert(!pci_bus_is_root(sec)); if (sec->parent_dev->config[PCI_SECONDARY_BUS] == bus_num) { return sec; } @@ -1852,7 +1927,12 @@ static int pci_add_option_rom(PCIDevice *pdev, bool is_default_rom) size = get_image_size(path); if (size < 0) { error_report("%s: failed to find romfile \"%s\"", - __FUNCTION__, pdev->romfile); + __func__, pdev->romfile); + g_free(path); + return -1; + } else if (size == 0) { + error_report("%s: ignoring empty romfile \"%s\"", + __func__, pdev->romfile); g_free(path); return -1; } @@ -2171,6 +2251,7 @@ static const TypeInfo pci_device_type_info = { static void pci_register_types(void) { type_register_static(&pci_bus_info); + type_register_static(&pcie_bus_info); type_register_static(&pci_device_type_info); } diff --git a/hw/pci/pci.h b/hw/pci/pci.h index 3beb70bf9a..9ea67a3832 100644 --- a/hw/pci/pci.h +++ b/hw/pci/pci.h @@ -109,6 +109,20 @@ typedef struct PCIIORegion { #define PCI_ROM_SLOT 6 #define PCI_NUM_REGIONS 7 +enum { + QEMU_PCI_VGA_MEM, + QEMU_PCI_VGA_IO_LO, + QEMU_PCI_VGA_IO_HI, + QEMU_PCI_VGA_NUM_REGIONS, +}; + +#define QEMU_PCI_VGA_MEM_BASE 0xa0000 +#define QEMU_PCI_VGA_MEM_SIZE 0x20000 +#define QEMU_PCI_VGA_IO_LO_BASE 0x3b0 +#define QEMU_PCI_VGA_IO_LO_SIZE 0xc +#define QEMU_PCI_VGA_IO_HI_BASE 0x3c0 +#define QEMU_PCI_VGA_IO_HI_SIZE 0x20 + #include "hw/pci/pci_regs.h" /* PCI HEADER_TYPE */ @@ -235,6 +249,10 @@ struct PCIDevice { /* IRQ objects for the INTA-INTD pins. */ qemu_irq *irq; + /* Legacy PCI VGA regions */ + MemoryRegion *vga_regions[QEMU_PCI_VGA_NUM_REGIONS]; + bool has_vga; + /* Current IRQ levels. Used internally by the generic PCI code. */ uint8_t irq_state; @@ -288,6 +306,9 @@ struct PCIDevice { void pci_register_bar(PCIDevice *pci_dev, int region_num, uint8_t attr, MemoryRegion *memory); +void pci_register_vga(PCIDevice *pci_dev, MemoryRegion *mem, + MemoryRegion *io_lo, MemoryRegion *io_hi); +void pci_unregister_vga(PCIDevice *pci_dev); pcibus_t pci_get_bar_addr(PCIDevice *pci_dev, int region_num); int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, @@ -319,15 +340,22 @@ typedef enum { typedef int (*pci_hotplug_fn)(DeviceState *qdev, PCIDevice *pci_dev, PCIHotplugState state); + +#define TYPE_PCI_BUS "PCI" +#define PCI_BUS(obj) OBJECT_CHECK(PCIBus, (obj), TYPE_PCI_BUS) +#define TYPE_PCIE_BUS "PCIE" + +bool pci_bus_is_express(PCIBus *bus); +bool pci_bus_is_root(PCIBus *bus); void pci_bus_new_inplace(PCIBus *bus, DeviceState *parent, const char *name, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, - uint8_t devfn_min); + uint8_t devfn_min, const char *typename); PCIBus *pci_bus_new(DeviceState *parent, const char *name, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, - uint8_t devfn_min); + uint8_t devfn_min, const char *typename); void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *irq_opaque, int nirq); int pci_bus_get_irq_level(PCIBus *bus, int irq_num); @@ -339,7 +367,7 @@ PCIBus *pci_register_bus(DeviceState *parent, const char *name, void *irq_opaque, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, - uint8_t devfn_min, int nirq); + uint8_t devfn_min, int nirq, const char *typename); void pci_bus_set_route_irq_fn(PCIBus *, pci_route_irq_fn); PCIINTxRoute pci_device_route_intx_to_irq(PCIDevice *dev, int pin); bool pci_intx_route_changed(PCIINTxRoute *old, PCIINTxRoute *new); diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index 995842a72d..24be6c5067 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -151,6 +151,28 @@ static void pci_bridge_init_alias(PCIBridge *bridge, MemoryRegion *alias, memory_region_add_subregion_overlap(parent_space, base, alias, 1); } +static void pci_bridge_init_vga_aliases(PCIBridge *br, PCIBus *parent, + MemoryRegion *alias_vga) +{ + uint16_t brctl = pci_get_word(br->dev.config + PCI_BRIDGE_CONTROL); + + memory_region_init_alias(&alias_vga[QEMU_PCI_VGA_IO_LO], + "pci_bridge_vga_io_lo", &br->address_space_io, + QEMU_PCI_VGA_IO_LO_BASE, QEMU_PCI_VGA_IO_LO_SIZE); + memory_region_init_alias(&alias_vga[QEMU_PCI_VGA_IO_HI], + "pci_bridge_vga_io_hi", &br->address_space_io, + QEMU_PCI_VGA_IO_HI_BASE, QEMU_PCI_VGA_IO_HI_SIZE); + memory_region_init_alias(&alias_vga[QEMU_PCI_VGA_MEM], + "pci_bridge_vga_mem", &br->address_space_mem, + QEMU_PCI_VGA_MEM_BASE, QEMU_PCI_VGA_MEM_SIZE); + + if (brctl & PCI_BRIDGE_CTL_VGA) { + pci_register_vga(&br->dev, &alias_vga[QEMU_PCI_VGA_MEM], + &alias_vga[QEMU_PCI_VGA_IO_LO], + &alias_vga[QEMU_PCI_VGA_IO_HI]); + } +} + static PCIBridgeWindows *pci_bridge_region_init(PCIBridge *br) { PCIBus *parent = br->dev.bus; @@ -175,7 +197,8 @@ static PCIBridgeWindows *pci_bridge_region_init(PCIBridge *br) &br->address_space_io, parent->address_space_io, cmd & PCI_COMMAND_IO); - /* TODO: optinal VGA and VGA palette snooping support. */ + + pci_bridge_init_vga_aliases(br, parent, w->alias_vga); return w; } @@ -187,6 +210,7 @@ static void pci_bridge_region_del(PCIBridge *br, PCIBridgeWindows *w) memory_region_del_subregion(parent->address_space_io, &w->alias_io); memory_region_del_subregion(parent->address_space_mem, &w->alias_mem); memory_region_del_subregion(parent->address_space_mem, &w->alias_pref_mem); + pci_unregister_vga(&br->dev); } static void pci_bridge_region_cleanup(PCIBridge *br, PCIBridgeWindows *w) @@ -194,6 +218,9 @@ static void pci_bridge_region_cleanup(PCIBridge *br, PCIBridgeWindows *w) memory_region_destroy(&w->alias_io); memory_region_destroy(&w->alias_mem); memory_region_destroy(&w->alias_pref_mem); + memory_region_destroy(&w->alias_vga[QEMU_PCI_VGA_IO_LO]); + memory_region_destroy(&w->alias_vga[QEMU_PCI_VGA_IO_HI]); + memory_region_destroy(&w->alias_vga[QEMU_PCI_VGA_MEM]); g_free(w); } @@ -227,7 +254,10 @@ void pci_bridge_write_config(PCIDevice *d, /* memory base/limit, prefetchable base/limit and io base/limit upper 16 */ - ranges_overlap(address, len, PCI_MEMORY_BASE, 20)) { + ranges_overlap(address, len, PCI_MEMORY_BASE, 20) || + + /* vga enable */ + ranges_overlap(address, len, PCI_BRIDGE_CONTROL, 2)) { pci_bridge_update_mappings(s); } @@ -298,7 +328,7 @@ void pci_bridge_reset(DeviceState *qdev) } /* default qdev initialization function for PCI-to-PCI bridge */ -int pci_bridge_initfn(PCIDevice *dev) +int pci_bridge_initfn(PCIDevice *dev, const char *typename) { PCIBus *parent = dev->bus; PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); @@ -306,6 +336,16 @@ int pci_bridge_initfn(PCIDevice *dev) pci_word_test_and_set_mask(dev->config + PCI_STATUS, PCI_STATUS_66MHZ | PCI_STATUS_FAST_BACK); + + /* + * TODO: We implement VGA Enable in the Bridge Control Register + * therefore per the PCI to PCI bridge spec we must also implement + * VGA Palette Snooping. When done, set this bit writable: + * + * pci_word_test_and_set_mask(dev->wmask + PCI_COMMAND, + * PCI_COMMAND_VGA_PALETTE); + */ + pci_config_set_class(dev->config, PCI_CLASS_BRIDGE_PCI); dev->config[PCI_HEADER_TYPE] = (dev->config[PCI_HEADER_TYPE] & PCI_HEADER_TYPE_MULTI_FUNCTION) | @@ -323,10 +363,9 @@ int pci_bridge_initfn(PCIDevice *dev) br->bus_name = dev->qdev.id; } - qbus_create_inplace(&sec_bus->qbus, TYPE_PCI_BUS, &dev->qdev, - br->bus_name); + qbus_create_inplace(&sec_bus->qbus, typename, &dev->qdev, br->bus_name); sec_bus->parent_dev = dev; - sec_bus->map_irq = br->map_irq; + sec_bus->map_irq = br->map_irq ? br->map_irq : pci_swizzle_map_irq_fn; sec_bus->address_space_mem = &br->address_space_mem; memory_region_init(&br->address_space_mem, "pci_bridge_pci", INT64_MAX); sec_bus->address_space_io = &br->address_space_io; diff --git a/hw/pci/pci_bridge.h b/hw/pci/pci_bridge.h index 455cb6677a..1868f7aea8 100644 --- a/hw/pci/pci_bridge.h +++ b/hw/pci/pci_bridge.h @@ -43,7 +43,7 @@ void pci_bridge_disable_base_limit(PCIDevice *dev); void pci_bridge_reset_reg(PCIDevice *dev); void pci_bridge_reset(DeviceState *qdev); -int pci_bridge_initfn(PCIDevice *pci_dev); +int pci_bridge_initfn(PCIDevice *pci_dev, const char *typename); void pci_bridge_exitfn(PCIDevice *pci_dev); @@ -55,12 +55,11 @@ void pci_bridge_exitfn(PCIDevice *pci_dev); void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, pci_map_irq_fn map_irq); +/* TODO: add this define to pci_regs.h in linux and then in qemu. */ +#define PCI_BRIDGE_CTL_VGA_16BIT 0x10 /* VGA 16-bit decode */ +#define PCI_BRIDGE_CTL_DISCARD 0x100 /* Primary discard timer */ +#define PCI_BRIDGE_CTL_SEC_DISCARD 0x200 /* Secondary discard timer */ +#define PCI_BRIDGE_CTL_DISCARD_STATUS 0x400 /* Discard timer status */ +#define PCI_BRIDGE_CTL_DISCARD_SERR 0x800 /* Discard timer SERR# enable */ + #endif /* QEMU_PCI_BRIDGE_H */ -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 8 - * indent-tab-mode: nil - * End: - */ diff --git a/hw/pci/pci_bus.h b/hw/pci/pci_bus.h index f905b9e11e..6ee443cf88 100644 --- a/hw/pci/pci_bus.h +++ b/hw/pci/pci_bus.h @@ -8,9 +8,6 @@ * use accessor functions in pci.h, pci_bridge.h */ -#define TYPE_PCI_BUS "PCI" -#define PCI_BUS(obj) OBJECT_CHECK(PCIBus, (obj), TYPE_PCI_BUS) - struct PCIBus { BusState qbus; PCIDMAContextFunc dma_context_fn; @@ -47,6 +44,13 @@ struct PCIBridgeWindows { MemoryRegion alias_pref_mem; MemoryRegion alias_mem; MemoryRegion alias_io; + /* + * When bridge control VGA forwarding is enabled, bridges will + * provide positive decode on the PCI VGA defined I/O port and + * MMIO ranges. When enabled forwarding is only qualified on the + * I/O and memory enable bits in the bridge command register. + */ + MemoryRegion alias_vga[QEMU_PCI_VGA_NUM_REGIONS]; }; struct PCIBridge { diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 485c94c1b2..62bd0b8b3e 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -87,6 +87,22 @@ int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port) return pos; } +int pcie_endpoint_cap_init(PCIDevice *dev, uint8_t offset) +{ + uint8_t type = PCI_EXP_TYPE_ENDPOINT; + + /* + * Windows guests will report Code 10, device cannot start, if + * a regular Endpoint type is exposed on a root complex. These + * should instead be Root Complex Integrated Endpoints. + */ + if (pci_bus_is_express(dev->bus) && pci_bus_is_root(dev->bus)) { + type = PCI_EXP_TYPE_RC_END; + } + + return pcie_cap_init(dev, offset, type, 0); +} + void pcie_cap_exit(PCIDevice *dev) { pci_del_capability(dev, PCI_CAP_ID_EXP, PCI_EXP_VER2_SIZEOF); diff --git a/hw/pci/pcie.h b/hw/pci/pcie.h index 31604e2742..c010007c5e 100644 --- a/hw/pci/pcie.h +++ b/hw/pci/pcie.h @@ -95,6 +95,7 @@ struct PCIExpressDevice { /* PCI express capability helper functions */ int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port); +int pcie_endpoint_cap_init(PCIDevice *dev, uint8_t offset); void pcie_cap_exit(PCIDevice *dev); uint8_t pcie_cap_get_type(const PCIDevice *dev); void pcie_cap_flags_set_vector(PCIDevice *dev, uint8_t vector); diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c index 33a6b0a08a..91b53a0fc2 100644 --- a/hw/pci/pcie_port.c +++ b/hw/pci/pcie_port.c @@ -27,13 +27,17 @@ void pcie_port_init_reg(PCIDevice *d) pci_set_word(d->config + PCI_STATUS, 0); pci_set_word(d->config + PCI_SEC_STATUS, 0); - /* Unlike conventional pci bridge, some bits are hardwired to 0. */ - pci_set_word(d->wmask + PCI_BRIDGE_CONTROL, - PCI_BRIDGE_CTL_PARITY | - PCI_BRIDGE_CTL_ISA | - PCI_BRIDGE_CTL_VGA | - PCI_BRIDGE_CTL_SERR | - PCI_BRIDGE_CTL_BUS_RESET); + /* + * Unlike conventional pci bridge, for some bits the spec states: + * Does not apply to PCI Express and must be hardwired to 0. + */ + pci_word_test_and_clear_mask(d->wmask + PCI_BRIDGE_CONTROL, + PCI_BRIDGE_CTL_MASTER_ABORT | + PCI_BRIDGE_CTL_FAST_BACK | + PCI_BRIDGE_CTL_DISCARD | + PCI_BRIDGE_CTL_SEC_DISCARD | + PCI_BRIDGE_CTL_DISCARD_STATUS | + PCI_BRIDGE_CTL_DISCARD_SERR); } /************************************************************************** |