diff options
Diffstat (limited to 'drivers/pci/probe.c')
-rw-r--r-- | drivers/pci/probe.c | 140 |
1 files changed, 111 insertions, 29 deletions
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 19c8950c6c38..c31310db0404 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -510,16 +510,18 @@ static struct pci_bus *pci_alloc_bus(struct pci_bus *parent) return b; } -static void pci_release_host_bridge_dev(struct device *dev) +static void devm_pci_release_host_bridge_dev(struct device *dev) { struct pci_host_bridge *bridge = to_pci_host_bridge(dev); if (bridge->release_fn) bridge->release_fn(bridge); +} - pci_free_resource_list(&bridge->windows); - - kfree(bridge); +static void pci_release_host_bridge_dev(struct device *dev) +{ + devm_pci_release_host_bridge_dev(dev); + pci_free_host_bridge(to_pci_host_bridge(dev)); } struct pci_host_bridge *pci_alloc_host_bridge(size_t priv) @@ -531,11 +533,36 @@ struct pci_host_bridge *pci_alloc_host_bridge(size_t priv) return NULL; INIT_LIST_HEAD(&bridge->windows); + bridge->dev.release = pci_release_host_bridge_dev; return bridge; } EXPORT_SYMBOL(pci_alloc_host_bridge); +struct pci_host_bridge *devm_pci_alloc_host_bridge(struct device *dev, + size_t priv) +{ + struct pci_host_bridge *bridge; + + bridge = devm_kzalloc(dev, sizeof(*bridge) + priv, GFP_KERNEL); + if (!bridge) + return NULL; + + INIT_LIST_HEAD(&bridge->windows); + bridge->dev.release = devm_pci_release_host_bridge_dev; + + return bridge; +} +EXPORT_SYMBOL(devm_pci_alloc_host_bridge); + +void pci_free_host_bridge(struct pci_host_bridge *bridge) +{ + pci_free_resource_list(&bridge->windows); + + kfree(bridge); +} +EXPORT_SYMBOL(pci_free_host_bridge); + static const unsigned char pcix_bus_speed[] = { PCI_SPEED_UNKNOWN, /* 0 */ PCI_SPEED_66MHz_PCIX, /* 1 */ @@ -719,7 +746,7 @@ static void pci_set_bus_msi_domain(struct pci_bus *bus) dev_set_msi_domain(&bus->dev, d); } -int pci_register_host_bridge(struct pci_host_bridge *bridge) +static int pci_register_host_bridge(struct pci_host_bridge *bridge) { struct device *parent = bridge->dev.parent; struct resource_entry *window, *n; @@ -834,7 +861,6 @@ free: kfree(bus); return err; } -EXPORT_SYMBOL(pci_register_host_bridge); static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr) @@ -1330,6 +1356,34 @@ static void pci_msi_setup_pci_dev(struct pci_dev *dev) } /** + * pci_intx_mask_broken - test PCI_COMMAND_INTX_DISABLE writability + * @dev: PCI device + * + * Test whether PCI_COMMAND_INTX_DISABLE is writable for @dev. Check this + * at enumeration-time to avoid modifying PCI_COMMAND at run-time. + */ +static int pci_intx_mask_broken(struct pci_dev *dev) +{ + u16 orig, toggle, new; + + pci_read_config_word(dev, PCI_COMMAND, &orig); + toggle = orig ^ PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(dev, PCI_COMMAND, toggle); + pci_read_config_word(dev, PCI_COMMAND, &new); + + pci_write_config_word(dev, PCI_COMMAND, orig); + + /* + * PCI_COMMAND_INTX_DISABLE was reserved and read-only prior to PCI + * r2.3, so strictly speaking, a device is not *broken* if it's not + * writable. But we'll live with the misnomer for now. + */ + if (new != toggle) + return 1; + return 0; +} + +/** * pci_setup_device - fill in class and map information of a device * @dev: the device structure to fill * @@ -1399,6 +1453,8 @@ int pci_setup_device(struct pci_dev *dev) } } + dev->broken_intx_masking = pci_intx_mask_broken(dev); + switch (dev->hdr_type) { /* header type */ case PCI_HEADER_TYPE_NORMAL: /* standard header */ if (class == PCI_CLASS_BRIDGE_PCI) @@ -1674,6 +1730,11 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) /* Initialize Advanced Error Capabilities and Control Register */ pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); reg32 = (reg32 & hpp->adv_err_cap_and) | hpp->adv_err_cap_or; + /* Don't enable ECRC generation or checking if unsupported */ + if (!(reg32 & PCI_ERR_CAP_ECRC_GENC)) + reg32 &= ~PCI_ERR_CAP_ECRC_GENE; + if (!(reg32 & PCI_ERR_CAP_ECRC_CHKC)) + reg32 &= ~PCI_ERR_CAP_ECRC_CHKE; pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); /* @@ -2298,9 +2359,8 @@ void __weak pcibios_remove_bus(struct pci_bus *bus) { } -static struct pci_bus *pci_create_root_bus_msi(struct device *parent, - int bus, struct pci_ops *ops, void *sysdata, - struct list_head *resources, struct msi_controller *msi) +struct pci_bus *pci_create_root_bus(struct device *parent, int bus, + struct pci_ops *ops, void *sysdata, struct list_head *resources) { int error; struct pci_host_bridge *bridge; @@ -2310,13 +2370,11 @@ static struct pci_bus *pci_create_root_bus_msi(struct device *parent, return NULL; bridge->dev.parent = parent; - bridge->dev.release = pci_release_host_bridge_dev; list_splice_init(resources, &bridge->windows); bridge->sysdata = sysdata; bridge->busnr = bus; bridge->ops = ops; - bridge->msi = msi; error = pci_register_host_bridge(bridge); if (error < 0) @@ -2328,13 +2386,6 @@ err_out: kfree(bridge); return NULL; } - -struct pci_bus *pci_create_root_bus(struct device *parent, int bus, - struct pci_ops *ops, void *sysdata, struct list_head *resources) -{ - return pci_create_root_bus_msi(parent, bus, ops, sysdata, resources, - NULL); -} EXPORT_SYMBOL_GPL(pci_create_root_bus); int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max) @@ -2400,24 +2451,28 @@ void pci_bus_release_busn_res(struct pci_bus *b) res, ret ? "can not be" : "is"); } -struct pci_bus *pci_scan_root_bus_msi(struct device *parent, int bus, - struct pci_ops *ops, void *sysdata, - struct list_head *resources, struct msi_controller *msi) +int pci_scan_root_bus_bridge(struct pci_host_bridge *bridge) { struct resource_entry *window; bool found = false; struct pci_bus *b; - int max; + int max, bus, ret; - resource_list_for_each_entry(window, resources) + if (!bridge) + return -EINVAL; + + resource_list_for_each_entry(window, &bridge->windows) if (window->res->flags & IORESOURCE_BUS) { found = true; break; } - b = pci_create_root_bus_msi(parent, bus, ops, sysdata, resources, msi); - if (!b) - return NULL; + ret = pci_register_host_bridge(bridge); + if (ret < 0) + return ret; + + b = bridge->bus; + bus = bridge->busnr; if (!found) { dev_info(&b->dev, @@ -2431,14 +2486,41 @@ struct pci_bus *pci_scan_root_bus_msi(struct device *parent, int bus, if (!found) pci_bus_update_busn_res_end(b, max); - return b; + return 0; } +EXPORT_SYMBOL(pci_scan_root_bus_bridge); struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { - return pci_scan_root_bus_msi(parent, bus, ops, sysdata, resources, - NULL); + struct resource_entry *window; + bool found = false; + struct pci_bus *b; + int max; + + resource_list_for_each_entry(window, resources) + if (window->res->flags & IORESOURCE_BUS) { + found = true; + break; + } + + b = pci_create_root_bus(parent, bus, ops, sysdata, resources); + if (!b) + return NULL; + + if (!found) { + dev_info(&b->dev, + "No busn resource found for root bus, will use [bus %02x-ff]\n", + bus); + pci_bus_insert_busn_res(b, bus, 255); + } + + max = pci_scan_child_bus(b); + + if (!found) + pci_bus_update_busn_res_end(b, max); + + return b; } EXPORT_SYMBOL(pci_scan_root_bus); |