diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2018-01-31 19:10:30 +0300 |
---|---|---|
committer | Bjorn Helgaas <helgaas@kernel.org> | 2018-01-31 19:10:30 +0300 |
commit | 5be31686cf4752443e669f48843eed036c69f999 (patch) | |
tree | 2b13b1c012f929de0a897f1088c8c330b7e786a4 | |
parent | 6b290397afb89ac23f29be30c6637756d256f959 (diff) | |
parent | 20c3ff6114b0c32beb85d476d7331ad9ab1942e2 (diff) | |
download | linux-5be31686cf4752443e669f48843eed036c69f999.tar.xz |
Merge branch 'pci/enumeration' into next
* pci/enumeration:
RDMA/qedr: Use pci_enable_atomic_ops_to_root()
PCI: Add pci_enable_atomic_ops_to_root()
PCI: Make PCI_SCAN_ALL_PCIE_DEVS work for Root as well as Downstream Ports
-rw-r--r-- | drivers/infiniband/hw/qedr/main.c | 59 | ||||
-rw-r--r-- | drivers/pci/pci.c | 75 | ||||
-rw-r--r-- | drivers/pci/probe.c | 25 | ||||
-rw-r--r-- | include/linux/pci.h | 1 | ||||
-rw-r--r-- | include/uapi/linux/pci_regs.h | 4 |
5 files changed, 102 insertions, 62 deletions
diff --git a/drivers/infiniband/hw/qedr/main.c b/drivers/infiniband/hw/qedr/main.c index 50812b33291b..b3786474e84a 100644 --- a/drivers/infiniband/hw/qedr/main.c +++ b/drivers/infiniband/hw/qedr/main.c @@ -430,59 +430,16 @@ static void qedr_remove_sysfiles(struct qedr_dev *dev) static void qedr_pci_set_atomic(struct qedr_dev *dev, struct pci_dev *pdev) { - struct pci_dev *bridge; - u32 ctl2, cap2; - u16 flags; - int rc; - - bridge = pdev->bus->self; - if (!bridge) - goto disable; - - /* Check atomic routing support all the way to root complex */ - while (bridge->bus->parent) { - rc = pcie_capability_read_word(bridge, PCI_EXP_FLAGS, &flags); - if (rc || ((flags & PCI_EXP_FLAGS_VERS) < 2)) - goto disable; - - rc = pcie_capability_read_dword(bridge, PCI_EXP_DEVCAP2, &cap2); - if (rc) - goto disable; + int rc = pci_enable_atomic_ops_to_root(pdev, + PCI_EXP_DEVCAP2_ATOMIC_COMP64); - rc = pcie_capability_read_dword(bridge, PCI_EXP_DEVCTL2, &ctl2); - if (rc) - goto disable; - - if (!(cap2 & PCI_EXP_DEVCAP2_ATOMIC_ROUTE) || - (ctl2 & PCI_EXP_DEVCTL2_ATOMIC_EGRESS_BLOCK)) - goto disable; - bridge = bridge->bus->parent->self; + if (rc) { + dev->atomic_cap = IB_ATOMIC_NONE; + DP_DEBUG(dev, QEDR_MSG_INIT, "Atomic capability disabled\n"); + } else { + dev->atomic_cap = IB_ATOMIC_GLOB; + DP_DEBUG(dev, QEDR_MSG_INIT, "Atomic capability enabled\n"); } - - rc = pcie_capability_read_word(bridge, PCI_EXP_FLAGS, &flags); - if (rc || ((flags & PCI_EXP_FLAGS_VERS) < 2)) - goto disable; - - rc = pcie_capability_read_dword(bridge, PCI_EXP_DEVCAP2, &cap2); - if (rc || !(cap2 & PCI_EXP_DEVCAP2_ATOMIC_COMP64)) - goto disable; - - /* Set atomic operations */ - pcie_capability_set_word(pdev, PCI_EXP_DEVCTL2, - PCI_EXP_DEVCTL2_ATOMIC_REQ); - dev->atomic_cap = IB_ATOMIC_GLOB; - - DP_DEBUG(dev, QEDR_MSG_INIT, "Atomic capability enabled\n"); - - return; - -disable: - pcie_capability_clear_word(pdev, PCI_EXP_DEVCTL2, - PCI_EXP_DEVCTL2_ATOMIC_REQ); - dev->atomic_cap = IB_ATOMIC_NONE; - - DP_DEBUG(dev, QEDR_MSG_INIT, "Atomic capability disabled\n"); - } static const struct qed_rdma_ops *qed_ops; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 4a7c6864fdf4..6112dd8d68b6 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3066,6 +3066,81 @@ int pci_rebar_set_size(struct pci_dev *pdev, int bar, int size) } /** + * pci_enable_atomic_ops_to_root - enable AtomicOp requests to root port + * @dev: the PCI device + * @cap_mask: mask of desired AtomicOp sizes, including one or more of: + * PCI_EXP_DEVCAP2_ATOMIC_COMP32 + * PCI_EXP_DEVCAP2_ATOMIC_COMP64 + * PCI_EXP_DEVCAP2_ATOMIC_COMP128 + * + * Return 0 if all upstream bridges support AtomicOp routing, egress + * blocking is disabled on all upstream ports, and the root port supports + * the requested completion capabilities (32-bit, 64-bit and/or 128-bit + * AtomicOp completion), or negative otherwise. + */ +int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask) +{ + struct pci_bus *bus = dev->bus; + struct pci_dev *bridge; + u32 cap, ctl2; + + if (!pci_is_pcie(dev)) + return -EINVAL; + + /* + * Per PCIe r4.0, sec 6.15, endpoints and root ports may be + * AtomicOp requesters. For now, we only support endpoints as + * requesters and root ports as completers. No endpoints as + * completers, and no peer-to-peer. + */ + + switch (pci_pcie_type(dev)) { + case PCI_EXP_TYPE_ENDPOINT: + case PCI_EXP_TYPE_LEG_END: + case PCI_EXP_TYPE_RC_END: + break; + default: + return -EINVAL; + } + + while (bus->parent) { + bridge = bus->self; + + pcie_capability_read_dword(bridge, PCI_EXP_DEVCAP2, &cap); + + switch (pci_pcie_type(bridge)) { + /* Ensure switch ports support AtomicOp routing */ + case PCI_EXP_TYPE_UPSTREAM: + case PCI_EXP_TYPE_DOWNSTREAM: + if (!(cap & PCI_EXP_DEVCAP2_ATOMIC_ROUTE)) + return -EINVAL; + break; + + /* Ensure root port supports all the sizes we care about */ + case PCI_EXP_TYPE_ROOT_PORT: + if ((cap & cap_mask) != cap_mask) + return -EINVAL; + break; + } + + /* Ensure upstream ports don't block AtomicOps on egress */ + if (!bridge->has_secondary_link) { + pcie_capability_read_dword(bridge, PCI_EXP_DEVCTL2, + &ctl2); + if (ctl2 & PCI_EXP_DEVCTL2_ATOMIC_EGRESS_BLOCK) + return -EINVAL; + } + + bus = bus->parent; + } + + pcie_capability_set_word(dev, PCI_EXP_DEVCTL2, + PCI_EXP_DEVCTL2_ATOMIC_REQ); + return 0; +} +EXPORT_SYMBOL(pci_enable_atomic_ops_to_root); + +/** * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge * @dev: the PCI device * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTC, 4=INTD) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 3761b1303529..070476036846 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -2248,22 +2248,27 @@ static unsigned next_fn(struct pci_bus *bus, struct pci_dev *dev, unsigned fn) static int only_one_child(struct pci_bus *bus) { - struct pci_dev *parent = bus->self; + struct pci_dev *bridge = bus->self; - if (!parent || !pci_is_pcie(parent)) + /* + * Systems with unusual topologies set PCI_SCAN_ALL_PCIE_DEVS so + * we scan for all possible devices, not just Device 0. + */ + if (pci_has_flag(PCI_SCAN_ALL_PCIE_DEVS)) return 0; - if (pci_pcie_type(parent) == PCI_EXP_TYPE_ROOT_PORT) - return 1; /* - * PCIe downstream ports are bridges that normally lead to only a - * device 0, but if PCI_SCAN_ALL_PCIE_DEVS is set, scan all - * possible devices, not just device 0. See PCIe spec r3.0, - * sec 7.3.1. + * A PCIe Downstream Port normally leads to a Link with only Device + * 0 on it (PCIe spec r3.1, sec 7.3.1). As an optimization, scan + * only for Device 0 in that situation. + * + * Checking has_secondary_link is a hack to identify Downstream + * Ports because sometimes Switches are configured such that the + * PCIe Port Type labels are backwards. */ - if (parent->has_secondary_link && - !pci_has_flag(PCI_SCAN_ALL_PCIE_DEVS)) + if (bridge && pci_is_pcie(bridge) && bridge->has_secondary_link) return 1; + return 0; } diff --git a/include/linux/pci.h b/include/linux/pci.h index d75d30e55976..22dfaff8bafc 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2063,6 +2063,7 @@ void pci_request_acs(void); bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags); bool pci_acs_path_enabled(struct pci_dev *start, struct pci_dev *end, u16 acs_flags); +int pci_enable_atomic_ops_to_root(struct pci_dev *dev, u32 cap_mask); #define PCI_VPD_LRDT 0x80 /* Large Resource Data Type */ #define PCI_VPD_LRDT_ID(x) ((x) | PCI_VPD_LRDT) diff --git a/include/uapi/linux/pci_regs.h b/include/uapi/linux/pci_regs.h index 66d71461d2f0..c8586db4fb07 100644 --- a/include/uapi/linux/pci_regs.h +++ b/include/uapi/linux/pci_regs.h @@ -624,7 +624,9 @@ #define PCI_EXP_DEVCAP2 36 /* Device Capabilities 2 */ #define PCI_EXP_DEVCAP2_ARI 0x00000020 /* Alternative Routing-ID */ #define PCI_EXP_DEVCAP2_ATOMIC_ROUTE 0x00000040 /* Atomic Op routing */ -#define PCI_EXP_DEVCAP2_ATOMIC_COMP64 0x00000100 /* Atomic 64-bit compare */ +#define PCI_EXP_DEVCAP2_ATOMIC_COMP32 0x00000080 /* 32b AtomicOp completion */ +#define PCI_EXP_DEVCAP2_ATOMIC_COMP64 0x00000100 /* 64b AtomicOp completion */ +#define PCI_EXP_DEVCAP2_ATOMIC_COMP128 0x00000200 /* 128b AtomicOp completion */ #define PCI_EXP_DEVCAP2_LTR 0x00000800 /* Latency tolerance reporting */ #define PCI_EXP_DEVCAP2_OBFF_MASK 0x000c0000 /* OBFF support mechanism */ #define PCI_EXP_DEVCAP2_OBFF_MSG 0x00040000 /* New message signaling */ |