summaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorDuc Dang <dhdang@apm.com>2015-06-13 02:35:57 +0200
committerBjorn Helgaas <bhelgaas@google.com>2015-06-18 19:09:03 +0200
commitf09f8735fb9ca113239fead45d96a48660cc2ae3 (patch)
treefe12e44c989b3bcd9498f43ee7cc71ed40cdf70f /drivers/pci
parentarm64: dts: Add APM X-Gene PCIe MSI nodes (diff)
downloadlinux-f09f8735fb9ca113239fead45d96a48660cc2ae3.tar.xz
linux-f09f8735fb9ca113239fead45d96a48660cc2ae3.zip
PCI: xgene: Disable Configuration Request Retry Status for v1 silicon
When a CPU reads the Vendor and Device ID of a non-existent device, the controller should fabricate return data of 0xFFFFFFFF. Configuration Request Retry Status (CRS) is not applicable in this case because the device doesn't exist at all. The X-Gene v1 PCIe controller has a bug in the CRS logic such that when CRS is enabled, it fabricates return data of 0xFFFF0001 for this case, which means "the device exists but is not ready." That causes the PCI core to retry the read until it times out after 60 seconds. Disable CRS capability advertisement by clearing the CRS Software Visibility bit in the Root Capabilities Register. [bhelgaas: changelog and comment] Tested-by: Ian Campbell <ian.campbell@citrix.com> Tested-by: Marcin Juszkiewicz <mjuszkiewicz@redhat.com> Signed-off-by: Duc Dang <dhdang@apm.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Acked-by: Tanmay Inamdar <tinamdar@apm.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/host/pci-xgene.c41
1 files changed, 40 insertions, 1 deletions
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c
index 3e5a636c9a9a..70af714a2d43 100644
--- a/drivers/pci/host/pci-xgene.c
+++ b/drivers/pci/host/pci-xgene.c
@@ -59,6 +59,12 @@
#define SZ_1T (SZ_1G*1024ULL)
#define PIPE_PHY_RATE_RD(src) ((0xc000 & (u32)(src)) >> 0xe)
+#define ROOT_CAP_AND_CTRL 0x5C
+
+/* PCIe IP version */
+#define XGENE_PCIE_IP_VER_UNKN 0
+#define XGENE_PCIE_IP_VER_1 1
+
struct xgene_pcie_port {
struct device_node *node;
struct device *dev;
@@ -67,6 +73,7 @@ struct xgene_pcie_port {
void __iomem *cfg_base;
unsigned long cfg_addr;
bool link_up;
+ u32 version;
};
static inline u32 pcie_bar_low_val(u32 addr, u32 flags)
@@ -140,9 +147,37 @@ static void __iomem *xgene_pcie_map_bus(struct pci_bus *bus, unsigned int devfn,
return xgene_pcie_get_cfg_base(bus) + offset;
}
+static int xgene_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ struct xgene_pcie_port *port = bus->sysdata;
+
+ if (pci_generic_config_read32(bus, devfn, where & ~0x3, 4, val) !=
+ PCIBIOS_SUCCESSFUL)
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ /*
+ * The v1 controller has a bug in its Configuration Request
+ * Retry Status (CRS) logic: when CRS is enabled and we read the
+ * Vendor and Device ID of a non-existent device, the controller
+ * fabricates return data of 0xFFFF0001 ("device exists but is not
+ * ready") instead of 0xFFFFFFFF ("device does not exist"). This
+ * causes the PCI core to retry the read until it times out.
+ * Avoid this by not claiming to support CRS.
+ */
+ if (pci_is_root_bus(bus) && (port->version == XGENE_PCIE_IP_VER_1) &&
+ ((where & ~0x3) == ROOT_CAP_AND_CTRL))
+ *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
+
+ if (size <= 2)
+ *val = (*val >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
static struct pci_ops xgene_pcie_ops = {
.map_bus = xgene_pcie_map_bus,
- .read = pci_generic_config_read32,
+ .read = xgene_pcie_config_read32,
.write = pci_generic_config_write32,
};
@@ -500,6 +535,10 @@ static int xgene_pcie_probe_bridge(struct platform_device *pdev)
port->node = of_node_get(pdev->dev.of_node);
port->dev = &pdev->dev;
+ port->version = XGENE_PCIE_IP_VER_UNKN;
+ if (of_device_is_compatible(port->node, "apm,xgene-pcie"))
+ port->version = XGENE_PCIE_IP_VER_1;
+
ret = xgene_pcie_map_reg(port, pdev);
if (ret)
return ret;