You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
237 lines
6.6 KiB
237 lines
6.6 KiB
9 years ago
|
From e99a187b5c5f60fe55ca586f82ac1a3557fb166a Mon Sep 17 00:00:00 2001
|
||
|
From: Ray Jui <rjui@broadcom.com>
|
||
|
Date: Fri, 16 Oct 2015 08:18:24 -0500
|
||
|
Subject: [PATCH 146/147] PCI: iproc: Add outbound mapping support
|
||
|
|
||
|
Certain SoCs require the PCIe outbound mapping to be configured in
|
||
|
software. Add support for those chips.
|
||
|
|
||
|
[jonmason: Use %pap format when printing size_t to avoid warnings in 32-bit
|
||
|
build.]
|
||
|
[arnd: Use div64_u64() instead of "%" to avoid __aeabi_uldivmod link error
|
||
|
in 32-bit build.]
|
||
|
Signed-off-by: Ray Jui <rjui@broadcom.com>
|
||
|
Signed-off-by: Jon Mason <jonmason@broadcom.com>
|
||
|
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
|
||
|
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
|
||
|
---
|
||
|
drivers/pci/host/pcie-iproc-platform.c | 27 ++++++++
|
||
|
drivers/pci/host/pcie-iproc.c | 115 +++++++++++++++++++++++++++++++++
|
||
|
drivers/pci/host/pcie-iproc.h | 17 +++++
|
||
|
3 files changed, 159 insertions(+)
|
||
|
|
||
|
--- a/drivers/pci/host/pcie-iproc-platform.c
|
||
|
+++ b/drivers/pci/host/pcie-iproc-platform.c
|
||
|
@@ -54,6 +54,33 @@ static int iproc_pcie_pltfm_probe(struct
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
+ if (of_property_read_bool(np, "brcm,pcie-ob")) {
|
||
|
+ u32 val;
|
||
|
+
|
||
|
+ ret = of_property_read_u32(np, "brcm,pcie-ob-axi-offset",
|
||
|
+ &val);
|
||
|
+ if (ret) {
|
||
|
+ dev_err(pcie->dev,
|
||
|
+ "missing brcm,pcie-ob-axi-offset property\n");
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+ pcie->ob.axi_offset = val;
|
||
|
+
|
||
|
+ ret = of_property_read_u32(np, "brcm,pcie-ob-window-size",
|
||
|
+ &val);
|
||
|
+ if (ret) {
|
||
|
+ dev_err(pcie->dev,
|
||
|
+ "missing brcm,pcie-ob-window-size property\n");
|
||
|
+ return ret;
|
||
|
+ }
|
||
|
+ pcie->ob.window_size = (resource_size_t)val * SZ_1M;
|
||
|
+
|
||
|
+ if (of_property_read_bool(np, "brcm,pcie-ob-oarr-size"))
|
||
|
+ pcie->ob.set_oarr_size = true;
|
||
|
+
|
||
|
+ pcie->need_ob_cfg = true;
|
||
|
+ }
|
||
|
+
|
||
|
/* PHY use is optional */
|
||
|
pcie->phy = devm_phy_get(&pdev->dev, "pcie-phy");
|
||
|
if (IS_ERR(pcie->phy)) {
|
||
|
--- a/drivers/pci/host/pcie-iproc.c
|
||
|
+++ b/drivers/pci/host/pcie-iproc.c
|
||
|
@@ -66,6 +66,18 @@
|
||
|
#define PCIE_DL_ACTIVE_SHIFT 2
|
||
|
#define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT)
|
||
|
|
||
|
+#define OARR_VALID_SHIFT 0
|
||
|
+#define OARR_VALID BIT(OARR_VALID_SHIFT)
|
||
|
+#define OARR_SIZE_CFG_SHIFT 1
|
||
|
+#define OARR_SIZE_CFG BIT(OARR_SIZE_CFG_SHIFT)
|
||
|
+
|
||
|
+#define OARR_LO(window) (0xd20 + (window) * 8)
|
||
|
+#define OARR_HI(window) (0xd24 + (window) * 8)
|
||
|
+#define OMAP_LO(window) (0xd40 + (window) * 8)
|
||
|
+#define OMAP_HI(window) (0xd44 + (window) * 8)
|
||
|
+
|
||
|
+#define MAX_NUM_OB_WINDOWS 2
|
||
|
+
|
||
|
static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
|
||
|
{
|
||
|
struct iproc_pcie *pcie;
|
||
|
@@ -212,6 +224,101 @@ static void iproc_pcie_enable(struct ipr
|
||
|
writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
|
||
|
}
|
||
|
|
||
|
+/**
|
||
|
+ * Some iProc SoCs require the SW to configure the outbound address mapping
|
||
|
+ *
|
||
|
+ * Outbound address translation:
|
||
|
+ *
|
||
|
+ * iproc_pcie_address = axi_address - axi_offset
|
||
|
+ * OARR = iproc_pcie_address
|
||
|
+ * OMAP = pci_addr
|
||
|
+ *
|
||
|
+ * axi_addr -> iproc_pcie_address -> OARR -> OMAP -> pci_address
|
||
|
+ */
|
||
|
+static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
|
||
|
+ u64 pci_addr, resource_size_t size)
|
||
|
+{
|
||
|
+ struct iproc_pcie_ob *ob = &pcie->ob;
|
||
|
+ unsigned i;
|
||
|
+ u64 max_size = (u64)ob->window_size * MAX_NUM_OB_WINDOWS;
|
||
|
+ u64 remainder;
|
||
|
+
|
||
|
+ if (size > max_size) {
|
||
|
+ dev_err(pcie->dev,
|
||
|
+ "res size 0x%pap exceeds max supported size 0x%llx\n",
|
||
|
+ &size, max_size);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ div64_u64_rem(size, ob->window_size, &remainder);
|
||
|
+ if (remainder) {
|
||
|
+ dev_err(pcie->dev,
|
||
|
+ "res size %pap needs to be multiple of window size %pap\n",
|
||
|
+ &size, &ob->window_size);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (axi_addr < ob->axi_offset) {
|
||
|
+ dev_err(pcie->dev,
|
||
|
+ "axi address %pap less than offset %pap\n",
|
||
|
+ &axi_addr, &ob->axi_offset);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Translate the AXI address to the internal address used by the iProc
|
||
|
+ * PCIe core before programming the OARR
|
||
|
+ */
|
||
|
+ axi_addr -= ob->axi_offset;
|
||
|
+
|
||
|
+ for (i = 0; i < MAX_NUM_OB_WINDOWS; i++) {
|
||
|
+ writel(lower_32_bits(axi_addr) | OARR_VALID |
|
||
|
+ (ob->set_oarr_size ? 1 : 0), pcie->base + OARR_LO(i));
|
||
|
+ writel(upper_32_bits(axi_addr), pcie->base + OARR_HI(i));
|
||
|
+ writel(lower_32_bits(pci_addr), pcie->base + OMAP_LO(i));
|
||
|
+ writel(upper_32_bits(pci_addr), pcie->base + OMAP_HI(i));
|
||
|
+
|
||
|
+ size -= ob->window_size;
|
||
|
+ if (size == 0)
|
||
|
+ break;
|
||
|
+
|
||
|
+ axi_addr += ob->window_size;
|
||
|
+ pci_addr += ob->window_size;
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
|
||
|
+ struct list_head *resources)
|
||
|
+{
|
||
|
+ struct resource_entry *window;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ resource_list_for_each_entry(window, resources) {
|
||
|
+ struct resource *res = window->res;
|
||
|
+ u64 res_type = resource_type(res);
|
||
|
+
|
||
|
+ switch (res_type) {
|
||
|
+ case IORESOURCE_IO:
|
||
|
+ case IORESOURCE_BUS:
|
||
|
+ break;
|
||
|
+ case IORESOURCE_MEM:
|
||
|
+ ret = iproc_pcie_setup_ob(pcie, res->start,
|
||
|
+ res->start - window->offset,
|
||
|
+ resource_size(res));
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ dev_err(pcie->dev, "invalid resource %pR\n", res);
|
||
|
+ return -EINVAL;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
|
||
|
{
|
||
|
int ret;
|
||
|
@@ -235,6 +342,14 @@ int iproc_pcie_setup(struct iproc_pcie *
|
||
|
|
||
|
iproc_pcie_reset(pcie);
|
||
|
|
||
|
+ if (pcie->need_ob_cfg) {
|
||
|
+ ret = iproc_pcie_map_ranges(pcie, res);
|
||
|
+ if (ret) {
|
||
|
+ dev_err(pcie->dev, "map failed\n");
|
||
|
+ goto err_power_off_phy;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
#ifdef CONFIG_ARM
|
||
|
pcie->sysdata.private_data = pcie;
|
||
|
sysdata = &pcie->sysdata;
|
||
|
--- a/drivers/pci/host/pcie-iproc.h
|
||
|
+++ b/drivers/pci/host/pcie-iproc.h
|
||
|
@@ -15,6 +15,19 @@
|
||
|
#define _PCIE_IPROC_H
|
||
|
|
||
|
/**
|
||
|
+ * iProc PCIe outbound mapping
|
||
|
+ * @set_oarr_size: indicates the OARR size bit needs to be set
|
||
|
+ * @axi_offset: offset from the AXI address to the internal address used by
|
||
|
+ * the iProc PCIe core
|
||
|
+ * @window_size: outbound window size
|
||
|
+ */
|
||
|
+struct iproc_pcie_ob {
|
||
|
+ bool set_oarr_size;
|
||
|
+ resource_size_t axi_offset;
|
||
|
+ resource_size_t window_size;
|
||
|
+};
|
||
|
+
|
||
|
+/**
|
||
|
* iProc PCIe device
|
||
|
* @dev: pointer to device data structure
|
||
|
* @base: PCIe host controller I/O register base
|
||
|
@@ -23,6 +36,8 @@
|
||
|
* @phy: optional PHY device that controls the Serdes
|
||
|
* @irqs: interrupt IDs
|
||
|
* @map_irq: function callback to map interrupts
|
||
|
+ * @need_ob_cfg: indidates SW needs to configure the outbound mapping window
|
||
|
+ * @ob: outbound mapping parameters
|
||
|
*/
|
||
|
struct iproc_pcie {
|
||
|
struct device *dev;
|
||
|
@@ -33,6 +48,8 @@ struct iproc_pcie {
|
||
|
struct pci_bus *root_bus;
|
||
|
struct phy *phy;
|
||
|
int (*map_irq)(const struct pci_dev *, u8, u8);
|
||
|
+ bool need_ob_cfg;
|
||
|
+ struct iproc_pcie_ob ob;
|
||
|
};
|
||
|
|
||
|
int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res);
|