diff options
Diffstat (limited to 'target/linux/bcm53xx/patches-4.1/036-0007-PCI-iproc-Add-outbound-mapping-support.patch')
-rw-r--r-- | target/linux/bcm53xx/patches-4.1/036-0007-PCI-iproc-Add-outbound-mapping-support.patch | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/target/linux/bcm53xx/patches-4.1/036-0007-PCI-iproc-Add-outbound-mapping-support.patch b/target/linux/bcm53xx/patches-4.1/036-0007-PCI-iproc-Add-outbound-mapping-support.patch new file mode 100644 index 0000000000..e335f45780 --- /dev/null +++ b/target/linux/bcm53xx/patches-4.1/036-0007-PCI-iproc-Add-outbound-mapping-support.patch @@ -0,0 +1,236 @@ +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 7/7] 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); |