summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSinan Kaya <okaya@codeaurora.org>2017-07-12 00:04:14 -0400
committerBjorn Helgaas <bhelgaas@google.com>2017-07-31 14:31:22 -0500
commit62ce94a7a5a54aac80975f5e6731707225d4077e (patch)
treeaf6e5d8fb778bb2fe51a14dcde3b0355b1035bf5
parent16f73eb02d7e1765ccab3d2018e0bd98eb93d973 (diff)
PCI: Mark Broadcom HT2100 Root Port Extended Tags as broken
Per PCIe r3.1, sec 2.2.6.2 and 7.8.4, a Requester may not use 8-bit Tags unless its Extended Tag Field Enable is set, but all Receivers/Completers must handle 8-bit Tags correctly regardless of their Extended Tag Field Enable. Some devices do not handle 8-bit Tags as Completers, so add a quirk for them. If we find such a device, we disable Extended Tags for the entire hierarchy to make peer-to-peer DMA possible. The Broadcom HT2100 seems to have issues with handling 8-bit tags. Mark it as broken. The pci_walk_bus() in the quirk handles devices we've enumerated in the past, and pci_configure_device() handles devices we enumerate in the future. Fixes: 60db3a4d8cc9 ("PCI: Enable PCIe Extended Tags if supported") Link: https://bugzilla.redhat.com/show_bug.cgi?id=1467674 Reported-and-tested-by: Wim ten Have <wim.ten.have@oracle.com> Signed-off-by: Sinan Kaya <okaya@codeaurora.org> [bhelgaas: changelog, tweak messages, rename bit and quirk] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
-rw-r--r--drivers/pci/pci.h1
-rw-r--r--drivers/pci/probe.c43
-rw-r--r--drivers/pci/quirks.c16
-rw-r--r--include/linux/pci.h1
4 files changed, 54 insertions, 7 deletions
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 22e061738c6f..a6560c9baa52 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -235,6 +235,7 @@ enum pci_bar_type {
pci_bar_mem64, /* A 64-bit memory BAR */
};
+int pci_configure_extended_tags(struct pci_dev *dev, void *ign);
bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
int crs_timeout);
int pci_setup_device(struct pci_dev *dev);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index c31310db0404..c81c9835f4c7 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1745,21 +1745,50 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
*/
}
-static void pci_configure_extended_tags(struct pci_dev *dev)
+int pci_configure_extended_tags(struct pci_dev *dev, void *ign)
{
- u32 dev_cap;
+ struct pci_host_bridge *host;
+ u32 cap;
+ u16 ctl;
int ret;
if (!pci_is_pcie(dev))
- return;
+ return 0;
- ret = pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &dev_cap);
+ ret = pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &cap);
if (ret)
- return;
+ return 0;
+
+ if (!(cap & PCI_EXP_DEVCAP_EXT_TAG))
+ return 0;
- if (dev_cap & PCI_EXP_DEVCAP_EXT_TAG)
+ ret = pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl);
+ if (ret)
+ return 0;
+
+ host = pci_find_host_bridge(dev->bus);
+ if (!host)
+ return 0;
+
+ /*
+ * If some device in the hierarchy doesn't handle Extended Tags
+ * correctly, make sure they're disabled.
+ */
+ if (host->no_ext_tags) {
+ if (ctl & PCI_EXP_DEVCTL_EXT_TAG) {
+ dev_info(&dev->dev, "disabling Extended Tags\n");
+ pcie_capability_clear_word(dev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_EXT_TAG);
+ }
+ return 0;
+ }
+
+ if (!(ctl & PCI_EXP_DEVCTL_EXT_TAG)) {
+ dev_info(&dev->dev, "enabling Extended Tags\n");
pcie_capability_set_word(dev, PCI_EXP_DEVCTL,
PCI_EXP_DEVCTL_EXT_TAG);
+ }
+ return 0;
}
static void pci_configure_device(struct pci_dev *dev)
@@ -1768,7 +1797,7 @@ static void pci_configure_device(struct pci_dev *dev)
int ret;
pci_configure_mps(dev);
- pci_configure_extended_tags(dev);
+ pci_configure_extended_tags(dev, NULL);
memset(&hpp, 0, sizeof(hpp));
ret = pci_get_hp_params(dev, &hpp);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 6967c6b4cf6b..f135765555c9 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -4681,3 +4681,19 @@ static void quirk_intel_no_flr(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1502, quirk_intel_no_flr);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x1503, quirk_intel_no_flr);
+
+static void quirk_no_ext_tags(struct pci_dev *pdev)
+{
+ struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus);
+
+ if (!bridge)
+ return;
+
+ bridge->no_ext_tags = 1;
+ dev_info(&pdev->dev, "disabling Extended Tags (this device can't handle them)\n");
+
+ pci_walk_bus(bridge->bus, pci_configure_extended_tags, NULL);
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0140, quirk_no_ext_tags);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0142, quirk_no_ext_tags);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, 0x0144, quirk_no_ext_tags);
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 4869e66dd659..3b968d435895 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -451,6 +451,7 @@ struct pci_host_bridge {
void *release_data;
struct msi_controller *msi;
unsigned int ignore_reset_delay:1; /* for entire hierarchy */
+ unsigned int no_ext_tags:1; /* no Extended Tags */
/* Resource alignment requirements */
resource_size_t (*align_resource)(struct pci_dev *dev,
const struct resource *res,