123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- 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, ®);
- @@ -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;
|