diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2018-06-06 16:10:08 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2018-06-06 16:10:08 -0500 |
commit | 5e3165d1a813cba0e7fad3880f9d704241e31245 (patch) | |
tree | ce0f3f3f6c3d7c702fef408279869b95055f8b47 /drivers/pci | |
parent | 8e069da28dfb09b78938395ebfb45a279507faf3 (diff) | |
parent | e5b1db0186bfb3bede41e412b27c9bcf2b336622 (diff) |
Merge branch 'pci/enumeration'
- neaten pci=earlydump output (Andy Shevchenko)
- avoid errors when extended config space inaccessible (Gilles Buloz)
- prevent sysfs disable of device while driver attached (Christoph
Hellwig)
- use core interface to report PCIe link properties in bnx2x, bnxt_en,
cxgb4, ixgbe (Bjorn Helgaas)
- remove unused pcie_get_minimum_link() (Bjorn Helgaas)
* pci/enumeration:
PCI: Remove unused pcie_get_minimum_link()
ixgbe: Report PCIe link properties with pcie_print_link_status()
cxgb4: Report PCIe link properties with pcie_print_link_status()
bnxt_en: Report PCIe link properties with pcie_print_link_status()
bnx2x: Report PCIe link properties with pcie_print_link_status()
PCI: Prevent sysfs disable of device while driver is attached
PCI: Check whether bridges allow access to extended config space
x86/PCI: Make pci=earlydump output neat
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/pci-sysfs.c | 15 | ||||
-rw-r--r-- | drivers/pci/pci.c | 43 | ||||
-rw-r--r-- | drivers/pci/probe.c | 52 |
3 files changed, 61 insertions, 49 deletions
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 366d93af051d..788a200fb2dc 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -288,13 +288,16 @@ static ssize_t enable_store(struct device *dev, struct device_attribute *attr, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (!val) { - if (pci_is_enabled(pdev)) - pci_disable_device(pdev); - else - result = -EIO; - } else + device_lock(dev); + if (dev->driver) + result = -EBUSY; + else if (val) result = pci_enable_device(pdev); + else if (pci_is_enabled(pdev)) + pci_disable_device(pdev); + else + result = -EIO; + device_unlock(dev); return result < 0 ? result : count; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 764bf64a097d..2aaab81341ac 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -5099,49 +5099,6 @@ int pcie_set_mps(struct pci_dev *dev, int mps) EXPORT_SYMBOL(pcie_set_mps); /** - * pcie_get_minimum_link - determine minimum link settings of a PCI device - * @dev: PCI device to query - * @speed: storage for minimum speed - * @width: storage for minimum width - * - * This function will walk up the PCI device chain and determine the minimum - * link width and speed of the device. - */ -int pcie_get_minimum_link(struct pci_dev *dev, enum pci_bus_speed *speed, - enum pcie_link_width *width) -{ - int ret; - - *speed = PCI_SPEED_UNKNOWN; - *width = PCIE_LNK_WIDTH_UNKNOWN; - - while (dev) { - u16 lnksta; - enum pci_bus_speed next_speed; - enum pcie_link_width next_width; - - ret = pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnksta); - if (ret) - return ret; - - next_speed = pcie_link_speed[lnksta & PCI_EXP_LNKSTA_CLS]; - next_width = (lnksta & PCI_EXP_LNKSTA_NLW) >> - PCI_EXP_LNKSTA_NLW_SHIFT; - - if (next_speed < *speed) - *speed = next_speed; - - if (next_width < *width) - *width = next_width; - - dev = dev->bus->self; - } - - return 0; -} -EXPORT_SYMBOL(pcie_get_minimum_link); - -/** * pcie_bandwidth_available - determine minimum link settings of a PCIe * device and its bandwidth limitation * @dev: PCI device to query diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index cc1688d75664..4fd402946b43 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -883,6 +883,45 @@ free: return err; } +static bool pci_bridge_child_ext_cfg_accessible(struct pci_dev *bridge) +{ + int pos; + u32 status; + + /* + * If extended config space isn't accessible on a bridge's primary + * bus, we certainly can't access it on the secondary bus. + */ + if (bridge->bus->bus_flags & PCI_BUS_FLAGS_NO_EXTCFG) + return false; + + /* + * PCIe Root Ports and switch ports are PCIe on both sides, so if + * extended config space is accessible on the primary, it's also + * accessible on the secondary. + */ + if (pci_is_pcie(bridge) && + (pci_pcie_type(bridge) == PCI_EXP_TYPE_ROOT_PORT || + pci_pcie_type(bridge) == PCI_EXP_TYPE_UPSTREAM || + pci_pcie_type(bridge) == PCI_EXP_TYPE_DOWNSTREAM)) + return true; + + /* + * For the other bridge types: + * - PCI-to-PCI bridges + * - PCIe-to-PCI/PCI-X forward bridges + * - PCI/PCI-X-to-PCIe reverse bridges + * extended config space on the secondary side is only accessible + * if the bridge supports PCI-X Mode 2. + */ + pos = pci_find_capability(bridge, PCI_CAP_ID_PCIX); + if (!pos) + return false; + + pci_read_config_dword(bridge, pos + PCI_X_STATUS, &status); + return status & (PCI_X_STATUS_266MHZ | PCI_X_STATUS_533MHZ); +} + static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr) { @@ -924,6 +963,16 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, pci_set_bus_of_node(child); pci_set_bus_speed(child); + /* + * Check whether extended config space is accessible on the child + * bus. Note that we currently assume it is always accessible on + * the root bus. + */ + if (!pci_bridge_child_ext_cfg_accessible(bridge)) { + child->bus_flags |= PCI_BUS_FLAGS_NO_EXTCFG; + pci_info(child, "extended config space not accessible\n"); + } + /* Set up default resource pointers and names */ for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) { child->resource[i] = &bridge->resource[PCI_BRIDGE_RESOURCES+i]; @@ -1394,6 +1443,9 @@ int pci_cfg_space_size(struct pci_dev *dev) u32 status; u16 class; + if (dev->bus->bus_flags & PCI_BUS_FLAGS_NO_EXTCFG) + return PCI_CFG_SPACE_SIZE; + class = dev->class >> 8; if (class == PCI_CLASS_BRIDGE_HOST) return pci_cfg_space_size_ext(dev); |