From: Jiang Liu <jiang.liu@xxxxxxxxxx> According to PCIe Base Specification 3.0, devices are guarenteed to return zero for unimplemented V2 PCI Express Capability registers. But there's no such guarantee for unimplemented V1 PCI Express Capability registers, so we need to explicitly check availability of V1 PCI Express Capability registers and return zero for unimplemented registers. Changeset 1b6b8ce2ac372e "PCI: only save/restore existent registers in the PCIe capability" introduces pcie_cap_has_lnkctl(), which assumes that PCIe root port, endpoint and legacy endpoint with 1.0 PCIe Capability implement Link Cap/Status/Control registers. On the other hand, section 7.8 of PCIe Base Spec V1.1 and V3.0 states that "The Link Capabilities, Link Status, and Link Control registers are required for all Root Ports, Switch Ports, Bridges, and Endpoints that are not Root Complex Integrated Endpoints." That means Link Capability/Status/Control registers are also available for PCIe Upstream Port, Downstream Port, PCIe-to-PCI bridge and PCI-to-PCIe bridge. So change pcie_cap_has_lnkctl() to follow PCIe specifications. Also refine pcie_cap_has_sltctl() and pcie_cap_has_rtctl() for readability. Signed-off-by: Jiang Liu <jiang.liu@xxxxxxxxxx> Reported-by: Yuval Mintz <yuvalmin@xxxxxxxxxxxx> --- drivers/pci/access.c | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 1cc2366..b9af3d1 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -470,6 +470,13 @@ void pci_cfg_access_unlock(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_cfg_access_unlock); +/* + * According to PCIe base spec 3.0, devices are guarenteed to return zero for + * unimplemented V2 PCI Express Capability registers. But there's no such + * guarantee for unimplemented V1 PCI Express Capability registers, so + * explicitly check availability of V1 PCI Express Capability registers + * and return zero for unimplemented registers. + */ static inline int pcie_cap_version(const struct pci_dev *dev) { return pcie_caps_reg(dev) & PCI_EXP_FLAGS_VERS; @@ -484,29 +491,39 @@ static inline bool pcie_cap_has_lnkctl(const struct pci_dev *dev) { int type = pci_pcie_type(dev); - return pcie_cap_version(dev) > 1 || - type == PCI_EXP_TYPE_ROOT_PORT || - type == PCI_EXP_TYPE_ENDPOINT || - type == PCI_EXP_TYPE_LEG_END; + if (pcie_cap_version(dev) == 1) + return type == PCI_EXP_TYPE_ENDPOINT || + type == PCI_EXP_TYPE_LEG_END || + type == PCI_EXP_TYPE_ROOT_PORT || + type == PCI_EXP_TYPE_UPSTREAM || + type == PCI_EXP_TYPE_DOWNSTREAM || + type == PCI_EXP_TYPE_PCI_BRIDGE || + type == PCI_EXP_TYPE_PCIE_BRIDGE; + + return true; } static inline bool pcie_cap_has_sltctl(const struct pci_dev *dev) { int type = pci_pcie_type(dev); - return pcie_cap_version(dev) > 1 || - type == PCI_EXP_TYPE_ROOT_PORT || - (type == PCI_EXP_TYPE_DOWNSTREAM && - pcie_caps_reg(dev) & PCI_EXP_FLAGS_SLOT); + if (pcie_cap_version(dev) == 1) + return type == PCI_EXP_TYPE_ROOT_PORT || + (type == PCI_EXP_TYPE_DOWNSTREAM && + pcie_caps_reg(dev) & PCI_EXP_FLAGS_SLOT); + + return true; } static inline bool pcie_cap_has_rtctl(const struct pci_dev *dev) { int type = pci_pcie_type(dev); - return pcie_cap_version(dev) > 1 || - type == PCI_EXP_TYPE_ROOT_PORT || - type == PCI_EXP_TYPE_RC_EC; + if (pcie_cap_version(dev) == 1) + return type == PCI_EXP_TYPE_ROOT_PORT || + type == PCI_EXP_TYPE_RC_EC; + + return true; } static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos) -- 1.8.1.2 -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html