|
@@ -68,6 +68,9 @@
|
|
|
#define APB_ERR_EN_SHIFT 0
|
|
|
#define APB_ERR_EN BIT(APB_ERR_EN_SHIFT)
|
|
|
|
|
|
+#define CFG_RETRY_STATUS 0xffff0001
|
|
|
+#define CFG_RETRY_STATUS_TIMEOUT_US 500000 /* 500 milliseconds */
|
|
|
+
|
|
|
/* derive the enum index of the outbound/inbound mapping registers */
|
|
|
#define MAP_REG(base_reg, index) ((base_reg) + (index) * 2)
|
|
|
|
|
@@ -473,6 +476,77 @@ static void __iomem *iproc_pcie_map_ep_cfg_reg(struct iproc_pcie *pcie,
|
|
|
return (pcie->base + offset);
|
|
|
}
|
|
|
|
|
|
+static unsigned int iproc_pcie_cfg_retry(void __iomem *cfg_data_p)
|
|
|
+{
|
|
|
+ int timeout = CFG_RETRY_STATUS_TIMEOUT_US;
|
|
|
+ unsigned int data;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * As per PCIe spec r3.1, sec 2.3.2, CRS Software Visibility only
|
|
|
+ * affects config reads of the Vendor ID. For config writes or any
|
|
|
+ * other config reads, the Root may automatically reissue the
|
|
|
+ * configuration request again as a new request.
|
|
|
+ *
|
|
|
+ * For config reads, this hardware returns CFG_RETRY_STATUS data
|
|
|
+ * when it receives a CRS completion, regardless of the address of
|
|
|
+ * the read or the CRS Software Visibility Enable bit. As a
|
|
|
+ * partial workaround for this, we retry in software any read that
|
|
|
+ * returns CFG_RETRY_STATUS.
|
|
|
+ *
|
|
|
+ * Note that a non-Vendor ID config register may have a value of
|
|
|
+ * CFG_RETRY_STATUS. If we read that, we can't distinguish it from
|
|
|
+ * a CRS completion, so we will incorrectly retry the read and
|
|
|
+ * eventually return the wrong data (0xffffffff).
|
|
|
+ */
|
|
|
+ data = readl(cfg_data_p);
|
|
|
+ while (data == CFG_RETRY_STATUS && timeout--) {
|
|
|
+ udelay(1);
|
|
|
+ data = readl(cfg_data_p);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (data == CFG_RETRY_STATUS)
|
|
|
+ data = 0xffffffff;
|
|
|
+
|
|
|
+ return data;
|
|
|
+}
|
|
|
+
|
|
|
+static int iproc_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
|
|
|
+ int where, int size, u32 *val)
|
|
|
+{
|
|
|
+ struct iproc_pcie *pcie = iproc_data(bus);
|
|
|
+ unsigned int slot = PCI_SLOT(devfn);
|
|
|
+ unsigned int fn = PCI_FUNC(devfn);
|
|
|
+ unsigned int busno = bus->number;
|
|
|
+ void __iomem *cfg_data_p;
|
|
|
+ unsigned int data;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /* root complex access */
|
|
|
+ if (busno == 0) {
|
|
|
+ ret = pci_generic_config_read32(bus, devfn, where, size, val);
|
|
|
+ if (ret != PCIBIOS_SUCCESSFUL)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ /* Don't advertise CRS SV support */
|
|
|
+ if ((where & ~0x3) == PCI_EXP_CAP + PCI_EXP_RTCTL)
|
|
|
+ *val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
|
|
|
+ return PCIBIOS_SUCCESSFUL;
|
|
|
+ }
|
|
|
+
|
|
|
+ cfg_data_p = iproc_pcie_map_ep_cfg_reg(pcie, busno, slot, fn, where);
|
|
|
+
|
|
|
+ if (!cfg_data_p)
|
|
|
+ return PCIBIOS_DEVICE_NOT_FOUND;
|
|
|
+
|
|
|
+ data = iproc_pcie_cfg_retry(cfg_data_p);
|
|
|
+
|
|
|
+ *val = data;
|
|
|
+ if (size <= 2)
|
|
|
+ *val = (data >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);
|
|
|
+
|
|
|
+ return PCIBIOS_SUCCESSFUL;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* Note access to the configuration registers are protected at the higher layer
|
|
|
* by 'pci_lock' in drivers/pci/access.c
|
|
@@ -567,9 +641,13 @@ static int iproc_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
|
|
|
int where, int size, u32 *val)
|
|
|
{
|
|
|
int ret;
|
|
|
+ struct iproc_pcie *pcie = iproc_data(bus);
|
|
|
|
|
|
iproc_pcie_apb_err_disable(bus, true);
|
|
|
- ret = pci_generic_config_read32(bus, devfn, where, size, val);
|
|
|
+ if (pcie->type == IPROC_PCIE_PAXB_V2)
|
|
|
+ ret = iproc_pcie_config_read(bus, devfn, where, size, val);
|
|
|
+ else
|
|
|
+ ret = pci_generic_config_read32(bus, devfn, where, size, val);
|
|
|
iproc_pcie_apb_err_disable(bus, false);
|
|
|
|
|
|
return ret;
|
|
@@ -1236,6 +1314,8 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
|
|
|
pcie->ib.nr_regions = ARRAY_SIZE(paxb_v2_ib_map);
|
|
|
pcie->ib_map = paxb_v2_ib_map;
|
|
|
pcie->need_msi_steer = true;
|
|
|
+ dev_warn(dev, "reads of config registers that contain %#x return incorrect data\n",
|
|
|
+ CFG_RETRY_STATUS);
|
|
|
break;
|
|
|
case IPROC_PCIE_PAXC:
|
|
|
regs = iproc_pcie_reg_paxc;
|