PCI: xgene: Disable Configuration Request Retry Status for v1 silicon
authorDuc Dang <dhdang@apm.com>
Sat, 13 Jun 2015 00:35:57 +0000 (17:35 -0700)
committerBjorn Helgaas <bhelgaas@google.com>
Thu, 18 Jun 2015 17:09:03 +0000 (12:09 -0500)
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>
drivers/pci/host/pci-xgene.c

index 3e5a636c9a9a7107974a95d78a8d494d71d6fe23..70af714a2d438f2e850b0c87cea10e81691b6768 100644 (file)
 #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;