aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm53xx/patches-4.4/087-0002-PCI-iproc-Add-PAXC-interface-support.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/bcm53xx/patches-4.4/087-0002-PCI-iproc-Add-PAXC-interface-support.patch')
-rw-r--r--target/linux/bcm53xx/patches-4.4/087-0002-PCI-iproc-Add-PAXC-interface-support.patch429
1 files changed, 429 insertions, 0 deletions
diff --git a/target/linux/bcm53xx/patches-4.4/087-0002-PCI-iproc-Add-PAXC-interface-support.patch b/target/linux/bcm53xx/patches-4.4/087-0002-PCI-iproc-Add-PAXC-interface-support.patch
new file mode 100644
index 0000000000..b011328761
--- /dev/null
+++ b/target/linux/bcm53xx/patches-4.4/087-0002-PCI-iproc-Add-PAXC-interface-support.patch
@@ -0,0 +1,429 @@
+From 943ebae781f519ecfecbfa1b997f15f59116e41d Mon Sep 17 00:00:00 2001
+From: Ray Jui <rjui@broadcom.com>
+Date: Fri, 4 Dec 2015 09:34:59 -0800
+Subject: [PATCH 2/5] PCI: iproc: Add PAXC interface support
+
+Traditionally, all iProc PCIe root complexes use PAXB-based wrapper, with
+an integrated on-chip Serdes to support external endpoint devices. On
+newer iProc platforms, a PAXC-based wrapper is introduced, for connection
+with internally emulated PCIe endpoint devices in the ASIC.
+
+Add support for PAXC-based iProc PCIe root complex in the iProc PCIe core
+driver. This change factors out common logic between PAXB and PAXC, and
+uses tables to store register offsets that are different between PAXB and
+PAXC. This allows the driver to be scaled to support subsequent PAXC
+revisions in the future.
+
+Signed-off-by: Ray Jui <rjui@broadcom.com>
+Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
+Reviewed-by: Scott Branden <sbranden@broadcom.com>
+---
+ drivers/pci/host/pcie-iproc-platform.c | 24 +++-
+ drivers/pci/host/pcie-iproc.c | 202 +++++++++++++++++++++++++++------
+ drivers/pci/host/pcie-iproc.h | 19 ++++
+ 3 files changed, 205 insertions(+), 40 deletions(-)
+
+--- a/drivers/pci/host/pcie-iproc-platform.c
++++ b/drivers/pci/host/pcie-iproc-platform.c
+@@ -26,8 +26,21 @@
+
+ #include "pcie-iproc.h"
+
++static const struct of_device_id iproc_pcie_of_match_table[] = {
++ {
++ .compatible = "brcm,iproc-pcie",
++ .data = (int *)IPROC_PCIE_PAXB,
++ }, {
++ .compatible = "brcm,iproc-pcie-paxc",
++ .data = (int *)IPROC_PCIE_PAXC,
++ },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table);
++
+ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
+ {
++ const struct of_device_id *of_id;
+ struct iproc_pcie *pcie;
+ struct device_node *np = pdev->dev.of_node;
+ struct resource reg;
+@@ -35,11 +48,16 @@ static int iproc_pcie_pltfm_probe(struct
+ LIST_HEAD(res);
+ int ret;
+
++ of_id = of_match_device(iproc_pcie_of_match_table, &pdev->dev);
++ if (!of_id)
++ return -EINVAL;
++
+ pcie = devm_kzalloc(&pdev->dev, sizeof(struct iproc_pcie), GFP_KERNEL);
+ if (!pcie)
+ return -ENOMEM;
+
+ pcie->dev = &pdev->dev;
++ pcie->type = (enum iproc_pcie_type)of_id->data;
+ platform_set_drvdata(pdev, pcie);
+
+ ret = of_address_to_resource(np, 0, &reg);
+@@ -114,12 +132,6 @@ static int iproc_pcie_pltfm_remove(struc
+ return iproc_pcie_remove(pcie);
+ }
+
+-static const struct of_device_id iproc_pcie_of_match_table[] = {
+- { .compatible = "brcm,iproc-pcie", },
+- { /* sentinel */ }
+-};
+-MODULE_DEVICE_TABLE(of, iproc_pcie_of_match_table);
+-
+ static struct platform_driver iproc_pcie_pltfm_driver = {
+ .driver = {
+ .name = "iproc-pcie",
+--- a/drivers/pci/host/pcie-iproc.c
++++ b/drivers/pci/host/pcie-iproc.c
+@@ -30,20 +30,16 @@
+
+ #include "pcie-iproc.h"
+
+-#define CLK_CONTROL_OFFSET 0x000
+ #define EP_PERST_SOURCE_SELECT_SHIFT 2
+ #define EP_PERST_SOURCE_SELECT BIT(EP_PERST_SOURCE_SELECT_SHIFT)
+ #define EP_MODE_SURVIVE_PERST_SHIFT 1
+ #define EP_MODE_SURVIVE_PERST BIT(EP_MODE_SURVIVE_PERST_SHIFT)
+ #define RC_PCIE_RST_OUTPUT_SHIFT 0
+ #define RC_PCIE_RST_OUTPUT BIT(RC_PCIE_RST_OUTPUT_SHIFT)
++#define PAXC_RESET_MASK 0x7f
+
+-#define CFG_IND_ADDR_OFFSET 0x120
+ #define CFG_IND_ADDR_MASK 0x00001ffc
+
+-#define CFG_IND_DATA_OFFSET 0x124
+-
+-#define CFG_ADDR_OFFSET 0x1f8
+ #define CFG_ADDR_BUS_NUM_SHIFT 20
+ #define CFG_ADDR_BUS_NUM_MASK 0x0ff00000
+ #define CFG_ADDR_DEV_NUM_SHIFT 15
+@@ -55,12 +51,8 @@
+ #define CFG_ADDR_CFG_TYPE_SHIFT 0
+ #define CFG_ADDR_CFG_TYPE_MASK 0x00000003
+
+-#define CFG_DATA_OFFSET 0x1fc
+-
+-#define SYS_RC_INTX_EN 0x330
+ #define SYS_RC_INTX_MASK 0xf
+
+-#define PCIE_LINK_STATUS_OFFSET 0xf0c
+ #define PCIE_PHYLINKUP_SHIFT 3
+ #define PCIE_PHYLINKUP BIT(PCIE_PHYLINKUP_SHIFT)
+ #define PCIE_DL_ACTIVE_SHIFT 2
+@@ -71,12 +63,54 @@
+ #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
++#define MAX_NUM_PAXC_PF 4
++
++#define IPROC_PCIE_REG_INVALID 0xffff
++
++enum iproc_pcie_reg {
++ IPROC_PCIE_CLK_CTRL = 0,
++ IPROC_PCIE_CFG_IND_ADDR,
++ IPROC_PCIE_CFG_IND_DATA,
++ IPROC_PCIE_CFG_ADDR,
++ IPROC_PCIE_CFG_DATA,
++ IPROC_PCIE_INTX_EN,
++ IPROC_PCIE_OARR_LO,
++ IPROC_PCIE_OARR_HI,
++ IPROC_PCIE_OMAP_LO,
++ IPROC_PCIE_OMAP_HI,
++ IPROC_PCIE_LINK_STATUS,
++};
++
++/* iProc PCIe PAXB registers */
++static const u16 iproc_pcie_reg_paxb[] = {
++ [IPROC_PCIE_CLK_CTRL] = 0x000,
++ [IPROC_PCIE_CFG_IND_ADDR] = 0x120,
++ [IPROC_PCIE_CFG_IND_DATA] = 0x124,
++ [IPROC_PCIE_CFG_ADDR] = 0x1f8,
++ [IPROC_PCIE_CFG_DATA] = 0x1fc,
++ [IPROC_PCIE_INTX_EN] = 0x330,
++ [IPROC_PCIE_OARR_LO] = 0xd20,
++ [IPROC_PCIE_OARR_HI] = 0xd24,
++ [IPROC_PCIE_OMAP_LO] = 0xd40,
++ [IPROC_PCIE_OMAP_HI] = 0xd44,
++ [IPROC_PCIE_LINK_STATUS] = 0xf0c,
++};
++
++/* iProc PCIe PAXC v1 registers */
++static const u16 iproc_pcie_reg_paxc[] = {
++ [IPROC_PCIE_CLK_CTRL] = 0x000,
++ [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0,
++ [IPROC_PCIE_CFG_IND_DATA] = 0x1f4,
++ [IPROC_PCIE_CFG_ADDR] = 0x1f8,
++ [IPROC_PCIE_CFG_DATA] = 0x1fc,
++ [IPROC_PCIE_INTX_EN] = IPROC_PCIE_REG_INVALID,
++ [IPROC_PCIE_OARR_LO] = IPROC_PCIE_REG_INVALID,
++ [IPROC_PCIE_OARR_HI] = IPROC_PCIE_REG_INVALID,
++ [IPROC_PCIE_OMAP_LO] = IPROC_PCIE_REG_INVALID,
++ [IPROC_PCIE_OMAP_HI] = IPROC_PCIE_REG_INVALID,
++ [IPROC_PCIE_LINK_STATUS] = IPROC_PCIE_REG_INVALID,
++};
+
+ static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
+ {
+@@ -91,6 +125,65 @@ static inline struct iproc_pcie *iproc_d
+ return pcie;
+ }
+
++static inline bool iproc_pcie_reg_is_invalid(u16 reg_offset)
++{
++ return !!(reg_offset == IPROC_PCIE_REG_INVALID);
++}
++
++static inline u16 iproc_pcie_reg_offset(struct iproc_pcie *pcie,
++ enum iproc_pcie_reg reg)
++{
++ return pcie->reg_offsets[reg];
++}
++
++static inline u32 iproc_pcie_read_reg(struct iproc_pcie *pcie,
++ enum iproc_pcie_reg reg)
++{
++ u16 offset = iproc_pcie_reg_offset(pcie, reg);
++
++ if (iproc_pcie_reg_is_invalid(offset))
++ return 0;
++
++ return readl(pcie->base + offset);
++}
++
++static inline void iproc_pcie_write_reg(struct iproc_pcie *pcie,
++ enum iproc_pcie_reg reg, u32 val)
++{
++ u16 offset = iproc_pcie_reg_offset(pcie, reg);
++
++ if (iproc_pcie_reg_is_invalid(offset))
++ return;
++
++ writel(val, pcie->base + offset);
++}
++
++static inline void iproc_pcie_ob_write(struct iproc_pcie *pcie,
++ enum iproc_pcie_reg reg,
++ unsigned window, u32 val)
++{
++ u16 offset = iproc_pcie_reg_offset(pcie, reg);
++
++ if (iproc_pcie_reg_is_invalid(offset))
++ return;
++
++ writel(val, pcie->base + offset + (window * 8));
++}
++
++static inline bool iproc_pcie_device_is_valid(struct iproc_pcie *pcie,
++ unsigned int slot,
++ unsigned int fn)
++{
++ if (slot > 0)
++ return false;
++
++ /* PAXC can only support limited number of functions */
++ if (pcie->type == IPROC_PCIE_PAXC && fn >= MAX_NUM_PAXC_PF)
++ return false;
++
++ return true;
++}
++
+ /**
+ * Note access to the configuration registers are protected at the higher layer
+ * by 'pci_lock' in drivers/pci/access.c
+@@ -104,28 +197,34 @@ static void __iomem *iproc_pcie_map_cfg_
+ unsigned fn = PCI_FUNC(devfn);
+ unsigned busno = bus->number;
+ u32 val;
++ u16 offset;
++
++ if (!iproc_pcie_device_is_valid(pcie, slot, fn))
++ return NULL;
+
+ /* root complex access */
+ if (busno == 0) {
+- if (slot >= 1)
++ iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_IND_ADDR,
++ where & CFG_IND_ADDR_MASK);
++ offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_IND_DATA);
++ if (iproc_pcie_reg_is_invalid(offset))
+ return NULL;
+- writel(where & CFG_IND_ADDR_MASK,
+- pcie->base + CFG_IND_ADDR_OFFSET);
+- return (pcie->base + CFG_IND_DATA_OFFSET);
++ else
++ return (pcie->base + offset);
+ }
+
+- if (fn > 1)
+- return NULL;
+-
+ /* EP device access */
+ val = (busno << CFG_ADDR_BUS_NUM_SHIFT) |
+ (slot << CFG_ADDR_DEV_NUM_SHIFT) |
+ (fn << CFG_ADDR_FUNC_NUM_SHIFT) |
+ (where & CFG_ADDR_REG_NUM_MASK) |
+ (1 & CFG_ADDR_CFG_TYPE_MASK);
+- writel(val, pcie->base + CFG_ADDR_OFFSET);
+-
+- return (pcie->base + CFG_DATA_OFFSET);
++ iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_ADDR, val);
++ offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_DATA);
++ if (iproc_pcie_reg_is_invalid(offset))
++ return NULL;
++ else
++ return (pcie->base + offset);
+ }
+
+ static struct pci_ops iproc_pcie_ops = {
+@@ -138,18 +237,29 @@ static void iproc_pcie_reset(struct ipro
+ {
+ u32 val;
+
++ if (pcie->type == IPROC_PCIE_PAXC) {
++ val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
++ val &= ~PAXC_RESET_MASK;
++ iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
++ udelay(100);
++ val |= PAXC_RESET_MASK;
++ iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
++ udelay(100);
++ return;
++ }
++
+ /*
+ * Select perst_b signal as reset source. Put the device into reset,
+ * and then bring it out of reset
+ */
+- val = readl(pcie->base + CLK_CONTROL_OFFSET);
++ val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
+ val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST &
+ ~RC_PCIE_RST_OUTPUT;
+- writel(val, pcie->base + CLK_CONTROL_OFFSET);
++ iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
+ udelay(250);
+
+ val |= RC_PCIE_RST_OUTPUT;
+- writel(val, pcie->base + CLK_CONTROL_OFFSET);
++ iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
+ msleep(100);
+ }
+
+@@ -160,7 +270,14 @@ static int iproc_pcie_check_link(struct
+ u16 pos, link_status;
+ bool link_is_active = false;
+
+- val = readl(pcie->base + PCIE_LINK_STATUS_OFFSET);
++ /*
++ * PAXC connects to emulated endpoint devices directly and does not
++ * have a Serdes. Therefore skip the link detection logic here.
++ */
++ if (pcie->type == IPROC_PCIE_PAXC)
++ return 0;
++
++ val = iproc_pcie_read_reg(pcie, IPROC_PCIE_LINK_STATUS);
+ if (!(val & PCIE_PHYLINKUP) || !(val & PCIE_DL_ACTIVE)) {
+ dev_err(pcie->dev, "PHY or data link is INACTIVE!\n");
+ return -ENODEV;
+@@ -221,7 +338,7 @@ static int iproc_pcie_check_link(struct
+
+ static void iproc_pcie_enable(struct iproc_pcie *pcie)
+ {
+- writel(SYS_RC_INTX_MASK, pcie->base + SYS_RC_INTX_EN);
++ iproc_pcie_write_reg(pcie, IPROC_PCIE_INTX_EN, SYS_RC_INTX_MASK);
+ }
+
+ /**
+@@ -272,11 +389,15 @@ static int iproc_pcie_setup_ob(struct ip
+ 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));
++ iproc_pcie_ob_write(pcie, IPROC_PCIE_OARR_LO, i,
++ lower_32_bits(axi_addr) | OARR_VALID |
++ (ob->set_oarr_size ? 1 : 0));
++ iproc_pcie_ob_write(pcie, IPROC_PCIE_OARR_HI, i,
++ upper_32_bits(axi_addr));
++ iproc_pcie_ob_write(pcie, IPROC_PCIE_OMAP_LO, i,
++ lower_32_bits(pci_addr));
++ iproc_pcie_ob_write(pcie, IPROC_PCIE_OMAP_HI, i,
++ upper_32_bits(pci_addr));
+
+ size -= ob->window_size;
+ if (size == 0)
+@@ -340,6 +461,19 @@ int iproc_pcie_setup(struct iproc_pcie *
+ goto err_exit_phy;
+ }
+
++ switch (pcie->type) {
++ case IPROC_PCIE_PAXB:
++ pcie->reg_offsets = iproc_pcie_reg_paxb;
++ break;
++ case IPROC_PCIE_PAXC:
++ pcie->reg_offsets = iproc_pcie_reg_paxc;
++ break;
++ default:
++ dev_err(pcie->dev, "incompatible iProc PCIe interface\n");
++ ret = -EINVAL;
++ goto err_power_off_phy;
++ }
++
+ iproc_pcie_reset(pcie);
+
+ if (pcie->need_ob_cfg) {
+--- a/drivers/pci/host/pcie-iproc.h
++++ b/drivers/pci/host/pcie-iproc.h
+@@ -15,6 +15,20 @@
+ #define _PCIE_IPROC_H
+
+ /**
++ * iProc PCIe interface type
++ *
++ * PAXB is the wrapper used in root complex that can be connected to an
++ * external endpoint device.
++ *
++ * PAXC is the wrapper used in root complex dedicated for internal emulated
++ * endpoint devices.
++ */
++enum iproc_pcie_type {
++ IPROC_PCIE_PAXB = 0,
++ IPROC_PCIE_PAXC,
++};
++
++/**
+ * 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
+@@ -29,7 +43,10 @@ struct iproc_pcie_ob {
+
+ /**
+ * iProc PCIe device
++ *
+ * @dev: pointer to device data structure
++ * @type: iProc PCIe interface type
++ * @reg_offsets: register offsets
+ * @base: PCIe host controller I/O register base
+ * @sysdata: Per PCI controller data (ARM-specific)
+ * @root_bus: pointer to root bus
+@@ -41,6 +58,8 @@ struct iproc_pcie_ob {
+ */
+ struct iproc_pcie {
+ struct device *dev;
++ enum iproc_pcie_type type;
++ const u16 *reg_offsets;
+ void __iomem *base;
+ #ifdef CONFIG_ARM
+ struct pci_sys_data sysdata;