2016-10-24 22:17:30

by Ray Jui

[permalink] [raw]
Subject: [PATCH 00/12] Additional iProc PCIe host support/fixes

This patch series contains various changes and fixes to the iProc PCIe
host driver. It also adds support for the next generation of PAXB and
PAXC based host controllers

This patch series was developed based on v4.9-rc1 and tested on both NS2
SVK and Cygnus wireless audio platform

repo: https://github.com/Broadcom/arm64-linux.git
branch: iproc-pcie-v1

Ray Jui (12):
PCI: iproc: Improve core register population
PCI: iproc: Do not reset PAXC when initializing the driver
PCI: iproc: Add BCMA type
PCI: iproc: Fix exception with multi-function devices
PCI: iproc: Update iProc PCIe DT binding
PCI: iproc: Add PAXC v2 support
PCI: iproc: Update iProc PCIe DT binding
PCI: iproc: Making outbound mapping code more generic
PCI: iproc: Update iProc PCIe DT binding
PCI: iproc: Add inbound DMA mapping support
PCI: iproc: Update iProc PCIe DT binding
PCI: iproc: Add support for the next-gen PAXB controller

.../devicetree/bindings/pci/brcm,iproc-pcie.txt | 43 +-
drivers/pci/host/pcie-iproc-bcma.c | 1 +
drivers/pci/host/pcie-iproc-msi.c | 1 +
drivers/pci/host/pcie-iproc-platform.c | 19 +-
drivers/pci/host/pcie-iproc.c | 942 ++++++++++++++++++---
drivers/pci/host/pcie-iproc.h | 45 +-
6 files changed, 916 insertions(+), 135 deletions(-)

--
2.1.4


2016-10-24 22:17:36

by Ray Jui

[permalink] [raw]
Subject: [PATCH 01/12] PCI: iproc: Improve core register population

As the number of iProc PCIe core registers starts to grow and differ
between different revisions of the iProc PCIe controllers, the
current way of populating each individual unsupported register with
value 'IPROC_PCIE_REG_INVALID' with a table entry has become a bit
messy and is difficult to scale up in the future

This patch improves the current driver by populating the invalid
entries with code instead of through individual table entries. This
helps to avoid a significant number of invalid table entries when
support for the next revision of the iProc controller is added

Signed-off-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
---
drivers/pci/host/pcie-iproc.c | 83 +++++++++++++++++++++++++++++++------------
drivers/pci/host/pcie-iproc.h | 4 ++-
2 files changed, 64 insertions(+), 23 deletions(-)

diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c
index 0b999a9..a9b9cd8 100644
--- a/drivers/pci/host/pcie-iproc.c
+++ b/drivers/pci/host/pcie-iproc.c
@@ -69,18 +69,35 @@

#define IPROC_PCIE_REG_INVALID 0xffff

+/*
+ * iProc PCIe host registers
+ */
enum iproc_pcie_reg {
+ /* clock/reset signal control */
IPROC_PCIE_CLK_CTRL = 0,
+
+ /* allow access to root complex configuration space */
IPROC_PCIE_CFG_IND_ADDR,
IPROC_PCIE_CFG_IND_DATA,
+
+ /* allow access to device configuration space */
IPROC_PCIE_CFG_ADDR,
IPROC_PCIE_CFG_DATA,
+
+ /* enable INTx */
IPROC_PCIE_INTX_EN,
+
+ /* outbound address mapping */
IPROC_PCIE_OARR_LO,
IPROC_PCIE_OARR_HI,
IPROC_PCIE_OMAP_LO,
IPROC_PCIE_OMAP_HI,
+
+ /* link status */
IPROC_PCIE_LINK_STATUS,
+
+ /* total number of core registers */
+ IPROC_PCIE_MAX_NUM_REG,
};

/* iProc PCIe PAXB registers */
@@ -105,12 +122,6 @@ static const u16 iproc_pcie_reg_paxc[] = {
[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)
@@ -204,7 +215,7 @@ static void __iomem *iproc_pcie_map_cfg_bus(struct pci_bus *bus,
* PAXC is connected to an internally emulated EP within the SoC. It
* allows only one device.
*/
- if (pcie->type == IPROC_PCIE_PAXC)
+ if (pcie->ep_is_internal)
if (slot > 0)
return NULL;

@@ -232,7 +243,7 @@ static void iproc_pcie_reset(struct iproc_pcie *pcie)
{
u32 val;

- if (pcie->type == IPROC_PCIE_PAXC) {
+ if (pcie->ep_is_internal) {
val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
val &= ~PAXC_RESET_MASK;
iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
@@ -270,7 +281,7 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie, struct pci_bus *bus)
* 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)
+ if (pcie->ep_is_internal)
return 0;

val = iproc_pcie_read_reg(pcie, IPROC_PCIE_LINK_STATUS);
@@ -454,6 +465,40 @@ static void iproc_pcie_msi_disable(struct iproc_pcie *pcie)
iproc_msi_exit(pcie);
}

+static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
+{
+ struct device *dev = pcie->dev;
+ unsigned int reg_idx;
+ const u16 *regs;
+
+ switch (pcie->type) {
+ case IPROC_PCIE_PAXB:
+ regs = iproc_pcie_reg_paxb;
+ break;
+ case IPROC_PCIE_PAXC:
+ regs = iproc_pcie_reg_paxc;
+ pcie->ep_is_internal = true;
+ break;
+ default:
+ dev_err(dev, "incompatible iProc PCIe interface\n");
+ return -EINVAL;
+ }
+
+ pcie->reg_offsets = devm_kcalloc(dev, IPROC_PCIE_MAX_NUM_REG,
+ sizeof(*pcie->reg_offsets),
+ GFP_KERNEL);
+ if (!pcie->reg_offsets)
+ return -ENOMEM;
+
+ /* go through the register table and populate all valid registers */
+ pcie->reg_offsets[0] = regs[0];
+ for (reg_idx = 1; reg_idx < IPROC_PCIE_MAX_NUM_REG; reg_idx++)
+ pcie->reg_offsets[reg_idx] = regs[reg_idx] ?
+ regs[reg_idx] : IPROC_PCIE_REG_INVALID;
+
+ return 0;
+}
+
int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
{
struct device *dev;
@@ -462,6 +507,13 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
struct pci_bus *bus;

dev = pcie->dev;
+
+ ret = iproc_pcie_rev_init(pcie);
+ if (ret) {
+ dev_err(dev, "unable to initialize controller parameters\n");
+ return ret;
+ }
+
ret = devm_request_pci_bus_resources(dev, res);
if (ret)
return ret;
@@ -478,19 +530,6 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
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(dev, "incompatible iProc PCIe interface\n");
- ret = -EINVAL;
- goto err_power_off_phy;
- }
-
iproc_pcie_reset(pcie);

if (pcie->need_ob_cfg) {
diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h
index e84d93c..83643d5 100644
--- a/drivers/pci/host/pcie-iproc.h
+++ b/drivers/pci/host/pcie-iproc.h
@@ -55,6 +55,7 @@ struct iproc_msi;
* @root_bus: pointer to root bus
* @phy: optional PHY device that controls the Serdes
* @map_irq: function callback to map interrupts
+ * @ep_is_internal: indicates an internal emulated endpoint device is connected
* @need_ob_cfg: indicates SW needs to configure the outbound mapping window
* @ob: outbound mapping parameters
* @msi: MSI data
@@ -62,7 +63,7 @@ struct iproc_msi;
struct iproc_pcie {
struct device *dev;
enum iproc_pcie_type type;
- const u16 *reg_offsets;
+ u16 *reg_offsets;
void __iomem *base;
phys_addr_t base_addr;
#ifdef CONFIG_ARM
@@ -71,6 +72,7 @@ struct iproc_pcie {
struct pci_bus *root_bus;
struct phy *phy;
int (*map_irq)(const struct pci_dev *, u8, u8);
+ bool ep_is_internal;
bool need_ob_cfg;
struct iproc_pcie_ob ob;
struct iproc_msi *msi;
--
2.1.4

2016-10-24 22:17:45

by Ray Jui

[permalink] [raw]
Subject: [PATCH 02/12] PCI: iproc: Do not reset PAXC when initializing the driver

During initialization, the current iProc PCIe host driver resets PAXC
and the downstream internal endpoint device that PAXC connects to. If
the endpoint device is already loaded with firmware and has started
running from the bootloader stage, this downstream reset causes the
endpoint device to stop working

Signed-off-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
---
drivers/pci/host/pcie-iproc.c | 15 ++++++---------
1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c
index a9b9cd8..3f4884a 100644
--- a/drivers/pci/host/pcie-iproc.c
+++ b/drivers/pci/host/pcie-iproc.c
@@ -243,16 +243,13 @@ static void iproc_pcie_reset(struct iproc_pcie *pcie)
{
u32 val;

- if (pcie->ep_is_internal) {
- 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);
+ /*
+ * PAXC and the internal emulated endpoint device downstream should not
+ * be reset. If firmware has been loaded on the endpoint device at an
+ * earlier boot stage, reset here causes issues
+ */
+ if (pcie->ep_is_internal)
return;
- }

/*
* Select perst_b signal as reset source. Put the device into reset,
--
2.1.4

2016-10-24 22:17:50

by Ray Jui

[permalink] [raw]
Subject: [PATCH 03/12] PCI: iproc: Add BCMA type

The iProc PCIe driver is currently using type IPROC_PCIE_PAXB for
the following SoCs: NS, NSP, Cygnus, NS2, and Pegasus. In fact, the BCMA
based NS uses a legacy PAXB controller that is slightly different from
the PAXB controller used in the rest of SoCs, e.g., some registers are
missing and it does not require software configuration of
outbound/inbound address mapping

A new type IPROC_PCIE_PAXB_BCMA is added in this patch to allow us to
properly support the BCMA based NS along with other iProc based SoCs
going forward

Signed-off-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
---
drivers/pci/host/pcie-iproc-bcma.c | 1 +
drivers/pci/host/pcie-iproc-msi.c | 1 +
drivers/pci/host/pcie-iproc.c | 14 ++++++++++++++
drivers/pci/host/pcie-iproc.h | 3 ++-
4 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/drivers/pci/host/pcie-iproc-bcma.c b/drivers/pci/host/pcie-iproc-bcma.c
index 8ce0890..bd4c9ec 100644
--- a/drivers/pci/host/pcie-iproc-bcma.c
+++ b/drivers/pci/host/pcie-iproc-bcma.c
@@ -54,6 +54,7 @@ static int iproc_pcie_bcma_probe(struct bcma_device *bdev)

pcie->dev = dev;

+ pcie->type = IPROC_PCIE_PAXB_BCMA;
pcie->base = bdev->io_addr;
if (!pcie->base) {
dev_err(dev, "no controller registers\n");
diff --git a/drivers/pci/host/pcie-iproc-msi.c b/drivers/pci/host/pcie-iproc-msi.c
index 9a2973b..9fad791 100644
--- a/drivers/pci/host/pcie-iproc-msi.c
+++ b/drivers/pci/host/pcie-iproc-msi.c
@@ -563,6 +563,7 @@ int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node)
}

switch (pcie->type) {
+ case IPROC_PCIE_PAXB_BCMA:
case IPROC_PCIE_PAXB:
msi->reg_offsets = iproc_msi_reg_paxb;
msi->nr_eq_region = 1;
diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c
index 3f4884a..f3b3340 100644
--- a/drivers/pci/host/pcie-iproc.c
+++ b/drivers/pci/host/pcie-iproc.c
@@ -100,6 +100,17 @@ enum iproc_pcie_reg {
IPROC_PCIE_MAX_NUM_REG,
};

+/* iProc PCIe PAXB BCMA registers */
+static const u16 iproc_pcie_reg_paxb_bcma[] = {
+ [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_LINK_STATUS] = 0xf0c,
+};
+
/* iProc PCIe PAXB registers */
static const u16 iproc_pcie_reg_paxb[] = {
[IPROC_PCIE_CLK_CTRL] = 0x000,
@@ -469,6 +480,9 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
const u16 *regs;

switch (pcie->type) {
+ case IPROC_PCIE_PAXB_BCMA:
+ regs = iproc_pcie_reg_paxb_bcma;
+ break;
case IPROC_PCIE_PAXB:
regs = iproc_pcie_reg_paxb;
break;
diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h
index 83643d5..768be05 100644
--- a/drivers/pci/host/pcie-iproc.h
+++ b/drivers/pci/host/pcie-iproc.h
@@ -24,7 +24,8 @@
* endpoint devices.
*/
enum iproc_pcie_type {
- IPROC_PCIE_PAXB = 0,
+ IPROC_PCIE_PAXB_BCMA = 0,
+ IPROC_PCIE_PAXB,
IPROC_PCIE_PAXC,
};

--
2.1.4

2016-10-24 22:17:58

by Ray Jui

[permalink] [raw]
Subject: [PATCH 05/12] PCI: iproc: Update iProc PCIe DT binding

Add new compatible string "brcm,iproc-pcie-paxc-v2" to the iProc
PCIe device tree binding document. "brcm,iproc-pcie-paxc-v2" is for the
second generation of the Broadcom iProc PCIe PAXC host controller

Also updated the binding document with more detailed description of
each compatible string and compatible SoCs

Finally, added description of optional property "msi-map", for use with
MSI controllers with sideband data

Signed-off-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
---
.../devicetree/bindings/pci/brcm,iproc-pcie.txt | 31 ++++++++++++++++------
1 file changed, 23 insertions(+), 8 deletions(-)

diff --git a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
index 01b88f4..071bbc2 100644
--- a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
@@ -1,10 +1,15 @@
* Broadcom iProc PCIe controller with the platform bus interface

Required properties:
-- compatible: Must be "brcm,iproc-pcie" for PAXB, or "brcm,iproc-pcie-paxc"
- for PAXC. PAXB-based root complex is used for external endpoint devices.
- PAXC-based root complex is connected to emulated endpoint devices
- internal to the ASIC
+- compatible:
+ "brcm,iproc-pcie" for the first generation of PAXB based controller,
+used in SoCs including NSP, Cygnus, NS2, and Pegasus
+ "brcm,iproc-pcie-paxc" for the first generation of PAXC based
+controller, used in NS2
+ "brcm,iproc-pcie-paxc-v2" for the second generation of PAXC based
+controller, used in Stingray
+ PAXB-based root complex is used for external endpoint devices. PAXC-based
+root complex is connected to emulated endpoint devices internal to the ASIC
- reg: base address and length of the PCIe controller I/O register space
- #interrupt-cells: set to <1>
- interrupt-map-mask and interrupt-map, standard PCI properties to define the
@@ -19,6 +24,7 @@ Required properties:
Optional properties:
- phys: phandle of the PCIe PHY device
- phy-names: must be "pcie-phy"
+- dma-coherent: present if DMA operations are coherent

- brcm,pcie-ob: Some iProc SoCs do not have the outbound address mapping done
by the ASIC after power on reset. In this case, SW needs to configure it
@@ -41,10 +47,19 @@ For older platforms without MSI integrated in the GIC, iProc PCIe core provides
an event queue based MSI support. The iProc MSI uses host memories to store
MSI posted writes in the event queues

-- msi-parent: Link to the device node of the MSI controller. On newer iProc
-platforms, the MSI controller may be gicv2m or gicv3-its. On older iProc
-platforms without MSI support in its interrupt controller, one may use the
-event queue based MSI support integrated within the iProc PCIe core.
+On newer iProc platforms, gicv2m or gicv3-its based MSI support should be used
+
+- msi-map: Maps a Requester ID to an MSI controller and associated MSI
+sideband data
+
+- msi-parent: Link to the device node of the MSI controller, used when no MSI
+sideband data is passed between the iProc PCIe controller and the MSI
+controller
+
+Refer to the following binding documents for more detailed description on
+the use of 'msi-map' and 'msi-parent':
+ Documentation/devicetree/bindings/pci/pci-msi.txt
+ Documentation/devicetree/bindings/interrupt-controller/msi.txt

When the iProc event queue based MSI is used, one needs to define the
following properties in the MSI device node:
--
2.1.4

2016-10-24 22:18:05

by Ray Jui

[permalink] [raw]
Subject: [PATCH 04/12] PCI: iproc: Fix exception with multi-function devices

During enumeration with multi-function EP devices, access to the
configuration space of a non-exist function results in an unsupported
request being returned as expected. By default the PAXB based iProc
PCIe controller forwards this as an APB error to the host system and
that causes an exception, which is undesired

This patch disables this undesired behavior and lets the kernel PCI
stack deals with an access to the non-exist function, in which case
a vendor ID of 0xffff is returned and handled gracefully

Reported-by: JD Zheng <[email protected]>
Signed-off-by: Ray Jui <[email protected]>
Reviewed-by: JD Zheng <[email protected]>
Reviewed-by: Oza Oza <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
---
drivers/pci/host/pcie-iproc.c | 58 +++++++++++++++++++++++++++++++++++++++++--
drivers/pci/host/pcie-iproc.h | 3 +++
2 files changed, 59 insertions(+), 2 deletions(-)

diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c
index f3b3340..07ec478 100644
--- a/drivers/pci/host/pcie-iproc.c
+++ b/drivers/pci/host/pcie-iproc.c
@@ -58,6 +58,9 @@
#define PCIE_DL_ACTIVE_SHIFT 2
#define PCIE_DL_ACTIVE BIT(PCIE_DL_ACTIVE_SHIFT)

+#define APB_ERR_EN_SHIFT 0
+#define APB_ERR_EN BIT(APB_ERR_EN_SHIFT)
+
#define OARR_VALID_SHIFT 0
#define OARR_VALID BIT(OARR_VALID_SHIFT)
#define OARR_SIZE_CFG_SHIFT 1
@@ -96,6 +99,9 @@ enum iproc_pcie_reg {
/* link status */
IPROC_PCIE_LINK_STATUS,

+ /* enable APB error for unsupported requests */
+ IPROC_PCIE_APB_ERR_EN,
+
/* total number of core registers */
IPROC_PCIE_MAX_NUM_REG,
};
@@ -124,6 +130,7 @@ static const u16 iproc_pcie_reg_paxb[] = {
[IPROC_PCIE_OMAP_LO] = 0xd40,
[IPROC_PCIE_OMAP_HI] = 0xd44,
[IPROC_PCIE_LINK_STATUS] = 0xf0c,
+ [IPROC_PCIE_APB_ERR_EN] = 0xf40,
};

/* iProc PCIe PAXC v1 registers */
@@ -181,6 +188,28 @@ static inline void iproc_pcie_write_reg(struct iproc_pcie *pcie,
writel(val, pcie->base + offset);
}

+/**
+ * APB error forwarding can be disabled during access of configuration
+ * registers of the endpoint device, to prevent unsupported requests
+ * (typically seen during enumeration with multi-function devices) from
+ * triggering a system exception
+ */
+static inline void iproc_pcie_apb_err_disable(struct pci_bus *bus,
+ bool disable)
+{
+ struct iproc_pcie *pcie = iproc_data(bus);
+ u32 val;
+
+ if (bus->number && pcie->has_apb_err_disable) {
+ val = iproc_pcie_read_reg(pcie, IPROC_PCIE_APB_ERR_EN);
+ if (disable)
+ val &= ~APB_ERR_EN;
+ else
+ val |= APB_ERR_EN;
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_APB_ERR_EN, val);
+ }
+}
+
static inline void iproc_pcie_ob_write(struct iproc_pcie *pcie,
enum iproc_pcie_reg reg,
unsigned window, u32 val)
@@ -244,10 +273,34 @@ static void __iomem *iproc_pcie_map_cfg_bus(struct pci_bus *bus,
return (pcie->base + offset);
}

+static int iproc_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 *val)
+{
+ int ret;
+
+ iproc_pcie_apb_err_disable(bus, true);
+ ret = pci_generic_config_read32(bus, devfn, where, size, val);
+ iproc_pcie_apb_err_disable(bus, false);
+
+ return ret;
+}
+
+static int iproc_pcie_config_write32(struct pci_bus *bus, unsigned int devfn,
+ int where, int size, u32 val)
+{
+ int ret;
+
+ iproc_pcie_apb_err_disable(bus, true);
+ ret = pci_generic_config_write32(bus, devfn, where, size, val);
+ iproc_pcie_apb_err_disable(bus, false);
+
+ return ret;
+}
+
static struct pci_ops iproc_pcie_ops = {
.map_bus = iproc_pcie_map_cfg_bus,
- .read = pci_generic_config_read32,
- .write = pci_generic_config_write32,
+ .read = iproc_pcie_config_read32,
+ .write = iproc_pcie_config_write32,
};

static void iproc_pcie_reset(struct iproc_pcie *pcie)
@@ -485,6 +538,7 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
break;
case IPROC_PCIE_PAXB:
regs = iproc_pcie_reg_paxb;
+ pcie->has_apb_err_disable = true;
break;
case IPROC_PCIE_PAXC:
regs = iproc_pcie_reg_paxc;
diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h
index 768be05..711dd3a 100644
--- a/drivers/pci/host/pcie-iproc.h
+++ b/drivers/pci/host/pcie-iproc.h
@@ -57,6 +57,8 @@ struct iproc_msi;
* @phy: optional PHY device that controls the Serdes
* @map_irq: function callback to map interrupts
* @ep_is_internal: indicates an internal emulated endpoint device is connected
+ * @has_apb_err_disable: indicates the controller can be configured to prevent
+ * unsupported request from being forwarded as an APB bus error
* @need_ob_cfg: indicates SW needs to configure the outbound mapping window
* @ob: outbound mapping parameters
* @msi: MSI data
@@ -74,6 +76,7 @@ struct iproc_pcie {
struct phy *phy;
int (*map_irq)(const struct pci_dev *, u8, u8);
bool ep_is_internal;
+ bool has_apb_err_disable;
bool need_ob_cfg;
struct iproc_pcie_ob ob;
struct iproc_msi *msi;
--
2.1.4

2016-10-24 22:18:27

by Ray Jui

[permalink] [raw]
Subject: [PATCH 11/12] PCI: iproc: Update iProc PCIe DT binding

Add new compatible string 'brcm,iproc-pcie-paxb-v2', for the next
generation of the iProc PAXB PCIe host controller

Signed-off-by: Oza Oza <[email protected]>
Signed-off-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
---
Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt | 2 ++
1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
index 62a0ab9..c8d8c1c 100644
--- a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
@@ -4,6 +4,8 @@ Required properties:
- compatible:
"brcm,iproc-pcie" for the first generation of PAXB based controller,
used in SoCs including NSP, Cygnus, NS2, and Pegasus
+ "brcm,iproc-pcie-paxb-v2" for the second generation of PAXB based
+controller, used in Stingray
"brcm,iproc-pcie-paxc" for the first generation of PAXC based
controller, used in NS2
"brcm,iproc-pcie-paxc-v2" for the second generation of PAXC based
--
2.1.4

2016-10-24 22:18:34

by Ray Jui

[permalink] [raw]
Subject: [PATCH 12/12] PCI: iproc: Add support for the next-gen PAXB controller

This patch adds the support for the next generation of the iProc PAXB
host controller, used in Stingray

Signed-off-by: Oza Oza <[email protected]>
Signed-off-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
---
drivers/pci/host/pcie-iproc-platform.c | 3 +
drivers/pci/host/pcie-iproc.c | 128 +++++++++++++++++++++++++++++++++
drivers/pci/host/pcie-iproc.h | 1 +
3 files changed, 132 insertions(+)

diff --git a/drivers/pci/host/pcie-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c
index 47329d3..fd3ed9b 100644
--- a/drivers/pci/host/pcie-iproc-platform.c
+++ b/drivers/pci/host/pcie-iproc-platform.c
@@ -31,6 +31,9 @@ static const struct of_device_id iproc_pcie_of_match_table[] = {
.compatible = "brcm,iproc-pcie",
.data = (int *)IPROC_PCIE_PAXB,
}, {
+ .compatible = "brcm,iproc-pcie-paxb-v2",
+ .data = (int *)IPROC_PCIE_PAXB_V2,
+ }, {
.compatible = "brcm,iproc-pcie-paxc",
.data = (int *)IPROC_PCIE_PAXC,
}, {
diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c
index c4e2211..8ffb9d4 100644
--- a/drivers/pci/host/pcie-iproc.c
+++ b/drivers/pci/host/pcie-iproc.c
@@ -118,6 +118,29 @@ static const struct iproc_pcie_ob_map paxb_ob_map[] = {
},
};

+static const struct iproc_pcie_ob_map paxb_v2_ob_map[] = {
+ {
+ /* OARR0/OMAP0 */
+ .window_sizes = { 128, 256 },
+ .nr_sizes = 2,
+ },
+ {
+ /* OARR1/OMAP1 */
+ .window_sizes = { 128, 256 },
+ .nr_sizes = 2,
+ },
+ {
+ /* OARR2/OMAP2 */
+ .window_sizes = { 128, 256, 512, 1024 },
+ .nr_sizes = 4,
+ },
+ {
+ /* OARR3/OMAP3 */
+ .window_sizes = { 128, 256, 512, 1024 },
+ .nr_sizes = 4,
+ },
+};
+
/**
* iProc PCIe inbound mapping type
*/
@@ -156,6 +179,54 @@ struct iproc_pcie_ib_map {
u16 imap_window_offset;
};

+static const struct iproc_pcie_ib_map paxb_v2_ib_map[] = {
+ {
+ /* IARR0/IMAP0 */
+ .type = IPROC_PCIE_IB_MAP_IO,
+ .size_unit = SZ_1K,
+ .region_sizes = { 32 },
+ .nr_sizes = 1,
+ .nr_windows = 8,
+ .imap_addr_offset = 0x40,
+ .imap_window_offset = 0x4,
+ },
+ {
+ /* IARR1/IMAP1 (currently unused) */
+ .type = IPROC_PCIE_IB_MAP_INVALID,
+ },
+ {
+ /* IARR2/IMAP2 */
+ .type = IPROC_PCIE_IB_MAP_MEM,
+ .size_unit = SZ_1M,
+ .region_sizes = { 64, 128, 256, 512, 1024, 2048, 4096, 8192,
+ 16384 },
+ .nr_sizes = 9,
+ .nr_windows = 1,
+ .imap_addr_offset = 0x4,
+ .imap_window_offset = 0x8,
+ },
+ {
+ /* IARR3/IMAP3 */
+ .type = IPROC_PCIE_IB_MAP_MEM,
+ .size_unit = SZ_1G,
+ .region_sizes = { 1, 2, 4, 8, 16, 32 },
+ .nr_sizes = 6,
+ .nr_windows = 8,
+ .imap_addr_offset = 0x4,
+ .imap_window_offset = 0x8,
+ },
+ {
+ /* IARR4/IMAP4 */
+ .type = IPROC_PCIE_IB_MAP_MEM,
+ .size_unit = SZ_1G,
+ .region_sizes = { 32, 64, 128, 256, 512 },
+ .nr_sizes = 5,
+ .nr_windows = 8,
+ .imap_addr_offset = 0x4,
+ .imap_window_offset = 0x8,
+ },
+};
+
/*
* iProc PCIe host registers
*/
@@ -258,6 +329,34 @@ static const u16 iproc_pcie_reg_paxb[] = {
[IPROC_PCIE_APB_ERR_EN] = 0xf40,
};

+/* iProc PCIe PAXB v2 registers */
+static const u16 iproc_pcie_reg_paxb_v2[] = {
+ [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_OARR0] = 0xd20,
+ [IPROC_PCIE_OMAP0] = 0xd40,
+ [IPROC_PCIE_OARR1] = 0xd28,
+ [IPROC_PCIE_OMAP1] = 0xd48,
+ [IPROC_PCIE_OARR2] = 0xd60,
+ [IPROC_PCIE_OMAP2] = 0xd68,
+ [IPROC_PCIE_OARR3] = 0xdf0,
+ [IPROC_PCIE_OMAP3] = 0xdf8,
+ [IPROC_PCIE_IARR0] = 0xd00,
+ [IPROC_PCIE_IMAP0] = 0xc00,
+ [IPROC_PCIE_IARR2] = 0xd10,
+ [IPROC_PCIE_IMAP2] = 0xcc0,
+ [IPROC_PCIE_IARR3] = 0xe00,
+ [IPROC_PCIE_IMAP3] = 0xe08,
+ [IPROC_PCIE_IARR4] = 0xe68,
+ [IPROC_PCIE_IMAP4] = 0xe70,
+ [IPROC_PCIE_LINK_STATUS] = 0xf0c,
+ [IPROC_PCIE_APB_ERR_EN] = 0xf40,
+};
+
/* iProc PCIe PAXC v1 registers */
static const u16 iproc_pcie_reg_paxc[] = {
[IPROC_PCIE_CLK_CTRL] = 0x000,
@@ -912,6 +1011,19 @@ static int iproce_pcie_get_msi(struct iproc_pcie *pcie,
return 0;
}

+static int iproc_pcie_paxb_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr)
+{
+ int ret;
+ struct of_pci_range range;
+
+ memset(&range, 0, sizeof(range));
+ range.size = SZ_32K;
+ range.pci_addr = range.cpu_addr = ALIGN(msi_addr, range.size);
+
+ ret = iproc_pcie_setup_ib(pcie, &range, IPROC_PCIE_IB_MAP_IO);
+ return ret;
+}
+
static void iproc_pcie_paxc_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr)
{
u32 val;
@@ -963,6 +1075,11 @@ static int iproc_pcie_msi_steer(struct iproc_pcie *pcie,
}

switch (pcie->type) {
+ case IPROC_PCIE_PAXB_V2:
+ ret = iproc_pcie_paxb_v2_msi_steer(pcie, msi_addr);
+ if (ret)
+ return ret;
+ break;
case IPROC_PCIE_PAXC_V2:
iproc_pcie_paxc_v2_msi_steer(pcie, msi_addr);
break;
@@ -1040,6 +1157,17 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
pcie->ob.nr_windows = ARRAY_SIZE(paxb_ob_map);
}
break;
+ case IPROC_PCIE_PAXB_V2:
+ regs = iproc_pcie_reg_paxb_v2;
+ pcie->has_apb_err_disable = true;
+ if (pcie->need_ob_cfg) {
+ pcie->ob_map = paxb_v2_ob_map;
+ pcie->ob.nr_windows = ARRAY_SIZE(paxb_v2_ob_map);
+ }
+ pcie->ib.nr_regions = ARRAY_SIZE(paxb_v2_ib_map);
+ pcie->ib_map = paxb_v2_ib_map;
+ pcie->need_msi_steer = true;
+ break;
case IPROC_PCIE_PAXC:
regs = iproc_pcie_reg_paxc;
pcie->ep_is_internal = true;
diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h
index 3f2709a..04fed8e 100644
--- a/drivers/pci/host/pcie-iproc.h
+++ b/drivers/pci/host/pcie-iproc.h
@@ -26,6 +26,7 @@
enum iproc_pcie_type {
IPROC_PCIE_PAXB_BCMA = 0,
IPROC_PCIE_PAXB,
+ IPROC_PCIE_PAXB_V2,
IPROC_PCIE_PAXC,
IPROC_PCIE_PAXC_V2,
};
--
2.1.4

2016-10-24 22:18:12

by Ray Jui

[permalink] [raw]
Subject: [PATCH 08/12] PCI: iproc: Making outbound mapping code more generic

Improve the iProc PCIe outbound mapping code by making it more generic
and removing redundant device tree properties
'brcm,pcie-ob-window-size' and 'brcm,pcie-ob-oarr-size'. The driver is
still backward compatible to device tree binaries with the two
properties specified

The driver now automatically confgures the correct mapping window size
and number of mapping windows based on the value of device tree
property 'ranges' and the capability of of the iProc PCIe controller

Signed-off-by: Oza Oza <[email protected]>
Signed-off-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
---
drivers/pci/host/pcie-iproc-platform.c | 13 --
drivers/pci/host/pcie-iproc.c | 247 +++++++++++++++++++++++----------
drivers/pci/host/pcie-iproc.h | 15 +-
3 files changed, 186 insertions(+), 89 deletions(-)

diff --git a/drivers/pci/host/pcie-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c
index f243150..47329d3 100644
--- a/drivers/pci/host/pcie-iproc-platform.c
+++ b/drivers/pci/host/pcie-iproc-platform.c
@@ -87,19 +87,6 @@ static int iproc_pcie_pltfm_probe(struct platform_device *pdev)
return ret;
}
pcie->ob.axi_offset = val;
-
- ret = of_property_read_u32(np, "brcm,pcie-ob-window-size",
- &val);
- if (ret) {
- dev_err(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;
}

diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c
index 02f3ed3..b466ba6 100644
--- a/drivers/pci/host/pcie-iproc.c
+++ b/drivers/pci/host/pcie-iproc.c
@@ -68,17 +68,47 @@
#define APB_ERR_EN_SHIFT 0
#define APB_ERR_EN BIT(APB_ERR_EN_SHIFT)

+/* derive the enum index of the outbound/inbound mapping registers */
+#define MAP_REG(base_reg, index) ((base_reg) + (index) * 2)
+
+/*
+ * Maximum number of outbound mapping window sizes that can be supported by any
+ * OARR/OMAP mapping pair
+ */
+#define MAX_NUM_OB_WINDOW_SIZES 4
+
#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 PCI_EXP_CAP 0xac

-#define MAX_NUM_OB_WINDOWS 2
-
#define IPROC_PCIE_REG_INVALID 0xffff

+/**
+ * iProc PCIe outbound mapping controller specific parameters
+ *
+ * @window_sizes: list of supported outbound mapping window sizes in MB
+ * @nr_sizes: number of supported outbound mapping window sizes
+ */
+struct iproc_pcie_ob_map {
+ resource_size_t window_sizes[MAX_NUM_OB_WINDOW_SIZES];
+ unsigned int nr_sizes;
+};
+
+static const struct iproc_pcie_ob_map paxb_ob_map[] = {
+ {
+ /* OARR0/OMAP0 */
+ .window_sizes = { 128, 256 },
+ .nr_sizes = 2,
+ },
+ {
+ /* OARR1/OMAP1 */
+ .window_sizes = { 128, 256 },
+ .nr_sizes = 2,
+ },
+};
+
/*
* iProc PCIe host registers
*/
@@ -123,10 +153,14 @@ enum iproc_pcie_reg {
IPROC_PCIE_INTX_EN,

/* outbound address mapping */
- IPROC_PCIE_OARR_LO,
- IPROC_PCIE_OARR_HI,
- IPROC_PCIE_OMAP_LO,
- IPROC_PCIE_OMAP_HI,
+ IPROC_PCIE_OARR0,
+ IPROC_PCIE_OMAP0,
+ IPROC_PCIE_OARR1,
+ IPROC_PCIE_OMAP1,
+ IPROC_PCIE_OARR2,
+ IPROC_PCIE_OMAP2,
+ IPROC_PCIE_OARR3,
+ IPROC_PCIE_OMAP3,

/* link status */
IPROC_PCIE_LINK_STATUS,
@@ -151,27 +185,27 @@ static const u16 iproc_pcie_reg_paxb_bcma[] = {

/* 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_APB_ERR_EN] = 0xf40,
+ [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_OARR0] = 0xd20,
+ [IPROC_PCIE_OMAP0] = 0xd40,
+ [IPROC_PCIE_OARR1] = 0xd28,
+ [IPROC_PCIE_OMAP1] = 0xd48,
+ [IPROC_PCIE_LINK_STATUS] = 0xf0c,
+ [IPROC_PCIE_APB_ERR_EN] = 0xf40,
};

/* 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_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 PAXC v2 registers */
@@ -256,18 +290,6 @@ static inline void iproc_pcie_apb_err_disable(struct pci_bus *bus,
}
}

-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));
-}
-
/**
* Note access to the configuration registers are protected at the higher layer
* by 'pci_lock' in drivers/pci/access.c
@@ -452,6 +474,58 @@ static void iproc_pcie_enable(struct iproc_pcie *pcie)
iproc_pcie_write_reg(pcie, IPROC_PCIE_INTX_EN, SYS_RC_INTX_MASK);
}

+static inline bool iproc_pcie_ob_is_valid(struct iproc_pcie *pcie,
+ int window_idx)
+{
+ u32 val;
+
+ val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_OARR0, window_idx));
+
+ return !!(val & OARR_VALID);
+}
+
+static inline int iproc_pcie_ob_write(struct iproc_pcie *pcie, int window_idx,
+ int size_idx, u64 axi_addr, u64 pci_addr)
+{
+ struct device *dev = pcie->dev;
+ u16 oarr_offset, omap_offset;
+
+ /*
+ * Derive the OARR/OMAP offset from the first pair (OARR0/OMAP0) based
+ * on window index
+ */
+ oarr_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OARR0,
+ window_idx));
+ omap_offset = iproc_pcie_reg_offset(pcie, MAP_REG(IPROC_PCIE_OMAP0,
+ window_idx));
+ if (iproc_pcie_reg_is_invalid(oarr_offset) ||
+ iproc_pcie_reg_is_invalid(omap_offset))
+ return -EINVAL;
+
+ /*
+ * Program the OARR registers. The upper 32-bit OARR register is
+ * always right after the lower 32-bit OARR register
+ */
+ writel(lower_32_bits(axi_addr) | (size_idx << OARR_SIZE_CFG_SHIFT) |
+ OARR_VALID, pcie->base + oarr_offset);
+ writel(upper_32_bits(axi_addr), pcie->base + oarr_offset + 4);
+
+ /* now program the OMAP registers */
+ writel(lower_32_bits(pci_addr), pcie->base + omap_offset);
+ writel(upper_32_bits(pci_addr), pcie->base + omap_offset + 4);
+
+ dev_info(dev, "ob window [%d]: offset 0x%x axi %pap pci %pap\n",
+ window_idx, oarr_offset, &axi_addr, &pci_addr);
+ dev_info(dev, "oarr lo 0x%x oarr hi 0x%x\n",
+ readl(pcie->base + oarr_offset),
+ readl(pcie->base + oarr_offset + 4));
+ dev_info(dev, "omap lo 0x%x omap hi 0x%x\n",
+ readl(pcie->base + omap_offset),
+ readl(pcie->base + omap_offset + 4));
+
+ return 0;
+}
+
/**
* Some iProc SoCs require the SW to configure the outbound address mapping
*
@@ -468,24 +542,7 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
{
struct iproc_pcie_ob *ob = &pcie->ob;
struct device *dev = pcie->dev;
- unsigned i;
- u64 max_size = (u64)ob->window_size * MAX_NUM_OB_WINDOWS;
- u64 remainder;
-
- if (size > max_size) {
- dev_err(dev,
- "res size %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(dev,
- "res size %pap needs to be multiple of window size %pap\n",
- &size, &ob->window_size);
- return -EINVAL;
- }
+ int ret = -EINVAL, window_idx, size_idx;

if (axi_addr < ob->axi_offset) {
dev_err(dev, "axi address %pap less than offset %pap\n",
@@ -499,26 +556,70 @@ static int iproc_pcie_setup_ob(struct iproc_pcie *pcie, u64 axi_addr,
*/
axi_addr -= ob->axi_offset;

- for (i = 0; i < MAX_NUM_OB_WINDOWS; 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)
+ /* iterate through all OARR/OMAP mapping windows */
+ for (window_idx = ob->nr_windows - 1; window_idx >= 0; window_idx--) {
+ const struct iproc_pcie_ob_map *ob_map =
+ &pcie->ob_map[window_idx];
+
+ /*
+ * If current outbound window is already in use, move on to the
+ * next one
+ */
+ if (iproc_pcie_ob_is_valid(pcie, window_idx))
+ continue;
+
+ /*
+ * Iterate through all supported window sizes within the
+ * OARR/OMAP pair to find a match. Go through the window sizes
+ * in a descending order
+ */
+ for (size_idx = ob_map->nr_sizes - 1; size_idx >= 0;
+ size_idx--) {
+ resource_size_t window_size =
+ ob_map->window_sizes[size_idx] * SZ_1M;
+
+ if (size < window_size)
+ continue;
+
+ if (!IS_ALIGNED(axi_addr, window_size) ||
+ !IS_ALIGNED(pci_addr, window_size)) {
+ dev_err(dev,
+ "axi %pap or pci %pap not aligned\n",
+ &axi_addr, &pci_addr);
+ return -EINVAL;
+ }
+
+ /*
+ * Match found! Program both OARR and OMAP and mark
+ * them as a valid entry
+ */
+ ret = iproc_pcie_ob_write(pcie, window_idx, size_idx,
+ axi_addr, pci_addr);
+ if (ret)
+ goto err_ob;
+
+ size -= window_size;
+ if (size == 0)
+ return 0;
+
+ /*
+ * If we are here, we are done with the current window,
+ * but not yet finished all mappings. Need to move on
+ * to the next window
+ */
+ axi_addr += window_size;
+ pci_addr += window_size;
break;
-
- axi_addr += ob->window_size;
- pci_addr += ob->window_size;
+ }
}

- return 0;
+err_ob:
+ dev_err(dev, "unable to configure outbound mapping\n");
+ dev_err(dev,
+ "axi %pap, axi offset %pap, pci %pap, res size %pap\n",
+ &axi_addr, &ob->axi_offset, &pci_addr, &size);
+
+ return ret;
}

static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
@@ -703,6 +804,10 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
case IPROC_PCIE_PAXB:
regs = iproc_pcie_reg_paxb;
pcie->has_apb_err_disable = true;
+ if (pcie->need_ob_cfg) {
+ pcie->ob_map = paxb_ob_map;
+ pcie->ob.nr_windows = ARRAY_SIZE(paxb_ob_map);
+ }
break;
case IPROC_PCIE_PAXC:
regs = iproc_pcie_reg_paxc;
diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h
index c2da140..861b526 100644
--- a/drivers/pci/host/pcie-iproc.h
+++ b/drivers/pci/host/pcie-iproc.h
@@ -32,17 +32,16 @@ enum iproc_pcie_type {

/**
* 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
+ * @nr_windows: total number of supported outbound mapping windows
*/
struct iproc_pcie_ob {
- bool set_oarr_size;
resource_size_t axi_offset;
- resource_size_t window_size;
+ unsigned int nr_windows;
};

+struct iproc_pcie_ob_map;
struct iproc_msi;

/**
@@ -60,8 +59,11 @@ struct iproc_msi;
* @ep_is_internal: indicates an internal emulated endpoint device is connected
* @has_apb_err_disable: indicates the controller can be configured to prevent
* unsupported request from being forwarded as an APB bus error
+ *
* @need_ob_cfg: indicates SW needs to configure the outbound mapping window
- * @ob: outbound mapping parameters
+ * @ob: outbound mapping related parameters
+ * @ob_map: outbound mapping related parameters specific to the controller
+ *
* @need_msi_steer: indicates additional configuration of the iProc PCIe
* controller is required to steer MSI writes to external interrupt controller
* @msi: MSI data
@@ -80,8 +82,11 @@ struct iproc_pcie {
int (*map_irq)(const struct pci_dev *, u8, u8);
bool ep_is_internal;
bool has_apb_err_disable;
+
bool need_ob_cfg;
struct iproc_pcie_ob ob;
+ const struct iproc_pcie_ob_map *ob_map;
+
bool need_msi_steer;
struct iproc_msi *msi;
};
--
2.1.4

2016-10-24 22:19:07

by Ray Jui

[permalink] [raw]
Subject: [PATCH 10/12] PCI: iproc: Add inbound DMA mapping support

This patch adds the support of inbound mapping to the iProc PCIe driver.
The range of the inbound mapping is configured by the optional device
tree property 'dma-ranges'.

While inbound mapping is done automatically in the ASIC on most iProc
based SoCs, newer ASIC (e.g., Stingray) requires inbound mapping to be
configured explcitly in software

Signed-off-by: Oza Oza <[email protected]>
Signed-off-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
---
drivers/pci/host/pcie-iproc.c | 235 ++++++++++++++++++++++++++++++++++++++++++
drivers/pci/host/pcie-iproc.h | 15 +++
2 files changed, 250 insertions(+)

diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c
index b466ba6..c4e2211 100644
--- a/drivers/pci/host/pcie-iproc.c
+++ b/drivers/pci/host/pcie-iproc.c
@@ -81,6 +81,15 @@
#define OARR_VALID BIT(OARR_VALID_SHIFT)
#define OARR_SIZE_CFG_SHIFT 1

+/*
+ * Maximum number of inbound mapping region sizes that can be supported by an
+ * IARR
+ */
+#define MAX_NUM_IB_REGION_SIZES 9
+
+#define IMAP_VALID_SHIFT 0
+#define IMAP_VALID BIT(IMAP_VALID_SHIFT)
+
#define PCI_EXP_CAP 0xac

#define IPROC_PCIE_REG_INVALID 0xffff
@@ -109,6 +118,44 @@ static const struct iproc_pcie_ob_map paxb_ob_map[] = {
},
};

+/**
+ * iProc PCIe inbound mapping type
+ */
+enum iproc_pcie_ib_map_type {
+ /* for DDR memory */
+ IPROC_PCIE_IB_MAP_MEM = 0,
+
+ /* for device I/O memory */
+ IPROC_PCIE_IB_MAP_IO,
+
+ /* invalid or unused */
+ IPROC_PCIE_IB_MAP_INVALID
+};
+
+/**
+ * iProc PCIe inbound mapping controller specific parameters
+ *
+ * @type: inbound mapping region type
+ * @size_unit: inbound mapping region size unit, could be SZ_1K, SZ_1M, or
+ * SZ_1G
+ * @region_sizes: list of supported inbound mapping region sizes in KB, MB, or
+ * GB, depedning on the size unit
+ * @nr_sizes: number of supported inbound mapping region sizes
+ * @nr_windows: number of supported inbound mapping windows for the region
+ * @imap_addr_offset: register offset between the upper and lower 32-bit
+ * IMAP address registers
+ * @imap_window_offset: register offset between each IMAP window
+ */
+struct iproc_pcie_ib_map {
+ enum iproc_pcie_ib_map_type type;
+ unsigned int size_unit;
+ resource_size_t region_sizes[MAX_NUM_IB_REGION_SIZES];
+ unsigned int nr_sizes;
+ unsigned int nr_windows;
+ u16 imap_addr_offset;
+ u16 imap_window_offset;
+};
+
/*
* iProc PCIe host registers
*/
@@ -162,6 +209,18 @@ enum iproc_pcie_reg {
IPROC_PCIE_OARR3,
IPROC_PCIE_OMAP3,

+ /* inbound address mapping */
+ IPROC_PCIE_IARR0,
+ IPROC_PCIE_IMAP0,
+ IPROC_PCIE_IARR1,
+ IPROC_PCIE_IMAP1,
+ IPROC_PCIE_IARR2,
+ IPROC_PCIE_IMAP2,
+ IPROC_PCIE_IARR3,
+ IPROC_PCIE_IMAP3,
+ IPROC_PCIE_IARR4,
+ IPROC_PCIE_IMAP4,
+
/* link status */
IPROC_PCIE_LINK_STATUS,

@@ -653,6 +712,178 @@ static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
return 0;
}

+static inline bool iproc_pcie_ib_is_in_use(struct iproc_pcie *pcie,
+ int region_idx)
+{
+ const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx];
+ u32 val;
+
+ val = iproc_pcie_read_reg(pcie, MAP_REG(IPROC_PCIE_IARR0, region_idx));
+
+ return !!(val & (BIT(ib_map->nr_sizes) - 1));
+}
+
+static inline bool iproc_pcie_ib_check_type(const struct iproc_pcie_ib_map *ib_map,
+ enum iproc_pcie_ib_map_type type)
+{
+ return !!(ib_map->type == type);
+}
+
+static int iproc_pcie_ib_write(struct iproc_pcie *pcie, int region_idx,
+ int size_idx, int nr_windows, u64 axi_addr,
+ u64 pci_addr, resource_size_t size)
+{
+ struct device *dev = pcie->dev;
+ const struct iproc_pcie_ib_map *ib_map = &pcie->ib_map[region_idx];
+ u16 iarr_offset, imap_offset;
+ u32 val;
+ int window_idx;
+
+ iarr_offset = iproc_pcie_reg_offset(pcie,
+ MAP_REG(IPROC_PCIE_IARR0, region_idx));
+ imap_offset = iproc_pcie_reg_offset(pcie,
+ MAP_REG(IPROC_PCIE_IMAP0, region_idx));
+ if (iproc_pcie_reg_is_invalid(iarr_offset) ||
+ iproc_pcie_reg_is_invalid(imap_offset))
+ return -EINVAL;
+
+ dev_info(dev, "ib region [%d]: offset 0x%x axi %pap pci %pap\n",
+ region_idx, iarr_offset, &axi_addr, &pci_addr);
+
+ /*
+ * Program the IARR registers. The upper 32-bit IARR register is
+ * always right after the lower 32-bit IARR register
+ */
+ writel(lower_32_bits(pci_addr) | BIT(size_idx),
+ pcie->base + iarr_offset);
+ writel(upper_32_bits(pci_addr), pcie->base + iarr_offset + 4);
+
+ dev_info(dev, "iarr lo 0x%x iarr hi 0x%x\n",
+ readl(pcie->base + iarr_offset),
+ readl(pcie->base + iarr_offset + 4));
+
+ /*
+ * Now program the IMAP registers. Each IARR region may have one or
+ * more IMAP windows
+ */
+ size /= nr_windows;
+ for (window_idx = 0; window_idx < nr_windows; window_idx++) {
+ val = readl(pcie->base + imap_offset);
+ val |= lower_32_bits(axi_addr) | IMAP_VALID;
+ writel(val, pcie->base + imap_offset);
+ writel(upper_32_bits(axi_addr),
+ pcie->base + imap_offset + ib_map->imap_addr_offset);
+
+ dev_info(dev, "imap window [%d] lo 0x%x hi 0x%x\n",
+ window_idx, readl(pcie->base + imap_offset),
+ readl(pcie->base + imap_offset +
+ ib_map->imap_addr_offset));
+
+ imap_offset += ib_map->imap_window_offset;
+ axi_addr += size;
+ }
+
+ return 0;
+}
+
+static int iproc_pcie_setup_ib(struct iproc_pcie *pcie,
+ struct of_pci_range *range,
+ enum iproc_pcie_ib_map_type type)
+{
+ struct device *dev = pcie->dev;
+ struct iproc_pcie_ib *ib = &pcie->ib;
+ int ret;
+ unsigned int region_idx, size_idx;
+ u64 axi_addr = range->cpu_addr, pci_addr = range->pci_addr;
+ resource_size_t size = range->size;
+
+ /* iterate through all IARR mapping regions */
+ for (region_idx = 0; region_idx < ib->nr_regions; region_idx++) {
+ const struct iproc_pcie_ib_map *ib_map =
+ &pcie->ib_map[region_idx];
+
+ /*
+ * If current inbound region is already in use or not a
+ * compatible type, move on to the next
+ */
+ if (iproc_pcie_ib_is_in_use(pcie, region_idx) ||
+ !iproc_pcie_ib_check_type(ib_map, type))
+ continue;
+
+ /* iterate through all supported region sizes to find a match */
+ for (size_idx = 0; size_idx < ib_map->nr_sizes; size_idx++) {
+ resource_size_t region_size =
+ ib_map->region_sizes[size_idx] * ib_map->size_unit;
+
+ if (size != region_size)
+ continue;
+
+ if (!IS_ALIGNED(axi_addr, region_size) ||
+ !IS_ALIGNED(pci_addr, region_size)) {
+ dev_err(dev,
+ "axi %pap or pci %pap not aligned\n",
+ &axi_addr, &pci_addr);
+ return -EINVAL;
+ }
+
+ /* match found! program IARR and all IMAP windows */
+ ret = iproc_pcie_ib_write(pcie, region_idx, size_idx,
+ ib_map->nr_windows, axi_addr,
+ pci_addr, size);
+ if (ret)
+ goto err_ib;
+ else
+ return 0;
+
+ }
+ }
+err_ib:
+ dev_err(dev, "unable to configure inbound mapping\n");
+ dev_err(dev, "axi %pap, pci %pap, res size %pap\n",
+ &axi_addr, &pci_addr, &size);
+
+ return ret;
+}
+
+static int pci_dma_range_parser_init(struct of_pci_range_parser *parser,
+ struct device_node *node)
+{
+ const int na = 3, ns = 2;
+ int rlen;
+
+ parser->node = node;
+ parser->pna = of_n_addr_cells(node);
+ parser->np = parser->pna + na + ns;
+
+ parser->range = of_get_property(node, "dma-ranges", &rlen);
+ if (!parser->range)
+ return -ENOENT;
+
+ parser->end = parser->range + rlen / sizeof(__be32);
+ return 0;
+}
+
+static int iproc_pcie_map_dma_ranges(struct iproc_pcie *pcie)
+{
+ struct of_pci_range range;
+ struct of_pci_range_parser parser;
+ int ret;
+
+ /* get the dma-ranges from DT */
+ ret = pci_dma_range_parser_init(&parser, pcie->dev->of_node);
+ if (ret)
+ return ret;
+
+ for_each_of_pci_range(&parser, &range) {
+ /* each range entry corresponds to an inbound mapping region */
+ ret = iproc_pcie_setup_ib(pcie, &range, IPROC_PCIE_IB_MAP_MEM);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int iproce_pcie_get_msi(struct iproc_pcie *pcie,
struct device_node *msi_node,
u64 *msi_addr)
@@ -880,6 +1111,10 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
}
}

+ ret = iproc_pcie_map_dma_ranges(pcie);
+ if (ret && ret != -ENOENT)
+ goto err_power_off_phy;
+
#ifdef CONFIG_ARM
pcie->sysdata.private_data = pcie;
sysdata = &pcie->sysdata;
diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h
index 861b526..3f2709a 100644
--- a/drivers/pci/host/pcie-iproc.h
+++ b/drivers/pci/host/pcie-iproc.h
@@ -41,7 +41,16 @@ struct iproc_pcie_ob {
unsigned int nr_windows;
};

+/**
+ * iProc PCIe inbound mapping
+ * @nr_regions: total number of supported inbound mapping regions
+ */
+struct iproc_pcie_ib {
+ unsigned int nr_regions;
+};
+
struct iproc_pcie_ob_map;
+struct iproc_pcie_ib_map;
struct iproc_msi;

/**
@@ -64,6 +73,9 @@ struct iproc_msi;
* @ob: outbound mapping related parameters
* @ob_map: outbound mapping related parameters specific to the controller
*
+ * @ib: inbound mapping related parameters
+ * @ib_map: outbound mapping region related parameters
+ *
* @need_msi_steer: indicates additional configuration of the iProc PCIe
* controller is required to steer MSI writes to external interrupt controller
* @msi: MSI data
@@ -87,6 +99,9 @@ struct iproc_pcie {
struct iproc_pcie_ob ob;
const struct iproc_pcie_ob_map *ob_map;

+ struct iproc_pcie_ib ib;
+ const struct iproc_pcie_ib_map *ib_map;
+
bool need_msi_steer;
struct iproc_msi *msi;
};
--
2.1.4

2016-10-24 22:19:23

by Ray Jui

[permalink] [raw]
Subject: [PATCH 06/12] PCI: iproc: Add PAXC v2 support

Add support for the second generation of the iProc PCIe PAXC host
controller to the iProc PCIe host driver

Signed-off-by: Ray Jui <[email protected]>
Reviewed-by: Anup Patel <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
---
drivers/pci/host/pcie-iproc-platform.c | 3 +
drivers/pci/host/pcie-iproc.c | 176 ++++++++++++++++++++++++++++++++-
drivers/pci/host/pcie-iproc.h | 4 +
3 files changed, 180 insertions(+), 3 deletions(-)

diff --git a/drivers/pci/host/pcie-iproc-platform.c b/drivers/pci/host/pcie-iproc-platform.c
index a3de087..f243150 100644
--- a/drivers/pci/host/pcie-iproc-platform.c
+++ b/drivers/pci/host/pcie-iproc-platform.c
@@ -33,6 +33,9 @@ static const struct of_device_id iproc_pcie_of_match_table[] = {
}, {
.compatible = "brcm,iproc-pcie-paxc",
.data = (int *)IPROC_PCIE_PAXC,
+ }, {
+ .compatible = "brcm,iproc-pcie-paxc-v2",
+ .data = (int *)IPROC_PCIE_PAXC_V2,
},
{ /* sentinel */ }
};
diff --git a/drivers/pci/host/pcie-iproc.c b/drivers/pci/host/pcie-iproc.c
index 07ec478..02f3ed3 100644
--- a/drivers/pci/host/pcie-iproc.c
+++ b/drivers/pci/host/pcie-iproc.c
@@ -21,6 +21,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/irqchip/arm-gic-v3.h>
#include <linux/platform_device.h>
#include <linux/of_address.h>
#include <linux/of_pci.h>
@@ -38,6 +39,12 @@
#define RC_PCIE_RST_OUTPUT BIT(RC_PCIE_RST_OUTPUT_SHIFT)
#define PAXC_RESET_MASK 0x7f

+#define GIC_V3_CFG_SHIFT 0
+#define GIC_V3_CFG BIT(GIC_V3_CFG_SHIFT)
+
+#define MSI_ENABLE_CFG_SHIFT 0
+#define MSI_ENABLE_CFG BIT(MSI_ENABLE_CFG_SHIFT)
+
#define CFG_IND_ADDR_MASK 0x00001ffc

#define CFG_ADDR_BUS_NUM_SHIFT 20
@@ -79,6 +86,31 @@ enum iproc_pcie_reg {
/* clock/reset signal control */
IPROC_PCIE_CLK_CTRL = 0,

+ /*
+ * To allow MSI to be steered to an external MSI controller (e.g., ARM
+ * GICv3 ITS)
+ */
+ IPROC_PCIE_MSI_GIC_MODE,
+
+ /*
+ * IPROC_PCIE_MSI_BASE_ADDR and IPROC_PCIE_MSI_WINDOW_SIZE define the
+ * window where the MSI posted writes are written, for the writes to be
+ * interpreted as MSI writes
+ */
+ IPROC_PCIE_MSI_BASE_ADDR,
+ IPROC_PCIE_MSI_WINDOW_SIZE,
+
+ /*
+ * To hold the address of the register where the MSI writes are
+ * programed. When ARM GICv3 ITS is used, this should be programmed
+ * with the address of the GITS_TRANSLATER register
+ */
+ IPROC_PCIE_MSI_ADDR_LO,
+ IPROC_PCIE_MSI_ADDR_HI,
+
+ /* enable MSI */
+ IPROC_PCIE_MSI_EN_CFG,
+
/* allow access to root complex configuration space */
IPROC_PCIE_CFG_IND_ADDR,
IPROC_PCIE_CFG_IND_DATA,
@@ -142,6 +174,20 @@ static const u16 iproc_pcie_reg_paxc[] = {
[IPROC_PCIE_CFG_DATA] = 0x1fc,
};

+/* iProc PCIe PAXC v2 registers */
+static const u16 iproc_pcie_reg_paxc_v2[] = {
+ [IPROC_PCIE_MSI_GIC_MODE] = 0x050,
+ [IPROC_PCIE_MSI_BASE_ADDR] = 0x074,
+ [IPROC_PCIE_MSI_WINDOW_SIZE] = 0x078,
+ [IPROC_PCIE_MSI_ADDR_LO] = 0x07c,
+ [IPROC_PCIE_MSI_ADDR_HI] = 0x080,
+ [IPROC_PCIE_MSI_EN_CFG] = 0x09c,
+ [IPROC_PCIE_CFG_IND_ADDR] = 0x1f0,
+ [IPROC_PCIE_CFG_IND_DATA] = 0x1f4,
+ [IPROC_PCIE_CFG_ADDR] = 0x1f8,
+ [IPROC_PCIE_CFG_DATA] = 0x1fc,
+};
+
static inline struct iproc_pcie *iproc_data(struct pci_bus *bus)
{
struct iproc_pcie *pcie;
@@ -506,13 +552,131 @@ static int iproc_pcie_map_ranges(struct iproc_pcie *pcie,
return 0;
}

+static int iproce_pcie_get_msi(struct iproc_pcie *pcie,
+ struct device_node *msi_node,
+ u64 *msi_addr)
+{
+ struct device *dev = pcie->dev;
+ int ret;
+ struct resource res;
+
+ /*
+ * Check if 'msi-map' points to ARM GICv3 ITS, which is the only
+ * supported external MSI controller that requires steering
+ */
+ if (!of_device_is_compatible(msi_node, "arm,gic-v3-its")) {
+ dev_err(dev, "unable to find compatible MSI controller\n");
+ return -ENODEV;
+ }
+
+ /* derive GITS_TRANSLATER address from GICv3 */
+ ret = of_address_to_resource(msi_node, 0, &res);
+ if (ret < 0) {
+ dev_err(dev, "unable to obtain MSI controller resources\n");
+ return ret;
+ }
+
+ *msi_addr = res.start + GITS_TRANSLATER;
+ return 0;
+}
+
+static void iproc_pcie_paxc_v2_msi_steer(struct iproc_pcie *pcie, u64 msi_addr)
+{
+ u32 val;
+
+ /*
+ * Program bits [43:13] of address of GITS_TRANSLATER register into
+ * bits [30:0] of the MSI base address register. In fact, in all iProc
+ * based SoCs, all I/O register bases are well below the 32-bit
+ * boundary, so we can safely assume bits [43:32] are always zeros
+ */
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_BASE_ADDR,
+ (u32)(msi_addr >> 13));
+
+ /* use a default 8K window size */
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_WINDOW_SIZE, 0);
+
+ /* steering MSI to GICv3 ITS */
+ val = iproc_pcie_read_reg(pcie, IPROC_PCIE_MSI_GIC_MODE);
+ val |= GIC_V3_CFG;
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_GIC_MODE, val);
+
+ /*
+ * Program bits [43:2] of address of GITS_TRANSLATER register into the
+ * iProc MSI address registers
+ */
+ msi_addr >>= 2;
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_ADDR_HI,
+ upper_32_bits(msi_addr));
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_ADDR_LO,
+ lower_32_bits(msi_addr));
+
+ /* enable MSI */
+ val = iproc_pcie_read_reg(pcie, IPROC_PCIE_MSI_EN_CFG);
+ val |= MSI_ENABLE_CFG;
+ iproc_pcie_write_reg(pcie, IPROC_PCIE_MSI_EN_CFG, val);
+}
+
+static int iproc_pcie_msi_steer(struct iproc_pcie *pcie,
+ struct device_node *msi_node)
+{
+ struct device *dev = pcie->dev;
+ int ret;
+ u64 msi_addr;
+
+ ret = iproce_pcie_get_msi(pcie, msi_node, &msi_addr);
+ if (ret < 0) {
+ dev_err(dev, "msi steering failed\n");
+ return ret;
+ }
+
+ switch (pcie->type) {
+ case IPROC_PCIE_PAXC_V2:
+ iproc_pcie_paxc_v2_msi_steer(pcie, msi_addr);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int iproc_pcie_msi_enable(struct iproc_pcie *pcie)
{
struct device_node *msi_node;
+ int ret;
+
+ /*
+ * Either the "msi-parent" or the "msi-map" phandle needs to exist
+ * for us to obtain the MSI node
+ */

msi_node = of_parse_phandle(pcie->dev->of_node, "msi-parent", 0);
- if (!msi_node)
- return -ENODEV;
+ if (!msi_node) {
+ const __be32 *msi_map = NULL;
+ int len;
+ u32 phandle;
+
+ msi_map = of_get_property(pcie->dev->of_node, "msi-map", &len);
+ if (!msi_map)
+ return -ENODEV;
+
+ phandle = be32_to_cpup(msi_map + 1);
+ msi_node = of_find_node_by_phandle(phandle);
+ if (!msi_node)
+ return -ENODEV;
+ }
+
+ /*
+ * Certain revisions of the iProc PCIe controller require additional
+ * configurations to steer the MSI writes towards an external MSI
+ * controller
+ */
+ if (pcie->need_msi_steer) {
+ ret = iproc_pcie_msi_steer(pcie, msi_node);
+ if (ret)
+ return ret;
+ }

/*
* If another MSI controller is being used, the call below should fail
@@ -544,6 +708,11 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
regs = iproc_pcie_reg_paxc;
pcie->ep_is_internal = true;
break;
+ case IPROC_PCIE_PAXC_V2:
+ regs = iproc_pcie_reg_paxc_v2;
+ pcie->ep_is_internal = true;
+ pcie->need_msi_steer = true;
+ break;
default:
dev_err(dev, "incompatible iProc PCIe interface\n");
return -EINVAL;
@@ -556,7 +725,8 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
return -ENOMEM;

/* go through the register table and populate all valid registers */
- pcie->reg_offsets[0] = regs[0];
+ pcie->reg_offsets[0] = (pcie->type == IPROC_PCIE_PAXC_V2) ?
+ IPROC_PCIE_REG_INVALID : regs[0];
for (reg_idx = 1; reg_idx < IPROC_PCIE_MAX_NUM_REG; reg_idx++)
pcie->reg_offsets[reg_idx] = regs[reg_idx] ?
regs[reg_idx] : IPROC_PCIE_REG_INVALID;
diff --git a/drivers/pci/host/pcie-iproc.h b/drivers/pci/host/pcie-iproc.h
index 711dd3a..c2da140 100644
--- a/drivers/pci/host/pcie-iproc.h
+++ b/drivers/pci/host/pcie-iproc.h
@@ -27,6 +27,7 @@ enum iproc_pcie_type {
IPROC_PCIE_PAXB_BCMA = 0,
IPROC_PCIE_PAXB,
IPROC_PCIE_PAXC,
+ IPROC_PCIE_PAXC_V2,
};

/**
@@ -61,6 +62,8 @@ struct iproc_msi;
* unsupported request from being forwarded as an APB bus error
* @need_ob_cfg: indicates SW needs to configure the outbound mapping window
* @ob: outbound mapping parameters
+ * @need_msi_steer: indicates additional configuration of the iProc PCIe
+ * controller is required to steer MSI writes to external interrupt controller
* @msi: MSI data
*/
struct iproc_pcie {
@@ -79,6 +82,7 @@ struct iproc_pcie {
bool has_apb_err_disable;
bool need_ob_cfg;
struct iproc_pcie_ob ob;
+ bool need_msi_steer;
struct iproc_msi *msi;
};

--
2.1.4

2016-10-24 22:19:45

by Ray Jui

[permalink] [raw]
Subject: [PATCH 09/12] PCI: iproc: Update iProc PCIe DT binding

Add description for optional device tree property 'dma-ranges' for
inbound mapping

Signed-off-by: Oza Oza <[email protected]>
Signed-off-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
---
Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt | 3 +++
1 file changed, 3 insertions(+)

diff --git a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
index f827b8d..62a0ab9 100644
--- a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
@@ -25,6 +25,9 @@ Optional properties:
- phys: phandle of the PCIe PHY device
- phy-names: must be "pcie-phy"
- dma-coherent: present if DMA operations are coherent
+- dma-ranges: Some PAXB based root complexes do not have inbound mapping done
+ by the ASIC after power on reset. In this case, SW is required to configure
+the mapping, based on inbound memory regions specified by this property

- brcm,pcie-ob: Some iProc SoCs do not have the outbound address mapping done
by the ASIC after power on reset. In this case, SW needs to configure it
--
2.1.4

2016-10-24 22:20:35

by Ray Jui

[permalink] [raw]
Subject: [PATCH 07/12] PCI: iproc: Update iProc PCIe DT binding

Remove the following outbound related device tree properties:
brcm,pcie-ob-window-size
brcm,pcie-ob-oarr-size

The above two prperties are a bit duplicated in functions. In addition,
the next generation iProc PCIe controller has outbound mapping window that
supports more than just two sizes, which cannot be properly supported by
these properties

A better solution is to make the driver outbound mapping logic slightly
more inteligent so it can figure out how to configure the outbound mapping
based on the 'ranges' specified in device tree and the number and sizes
of the outbound mapping windows that a particular revision of the iProc
PCIe controller can support

Signed-off-by: Ray Jui <[email protected]>
Reviewed-by: Scott Branden <[email protected]>
---
Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt | 7 -------
1 file changed, 7 deletions(-)

diff --git a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
index 071bbc2..f827b8d 100644
--- a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
@@ -35,11 +35,6 @@ effective:
Required:
- brcm,pcie-ob-axi-offset: The offset from the AXI address to the internal
address used by the iProc PCIe core (not the PCIe address)
-- brcm,pcie-ob-window-size: The outbound address mapping window size (in MB)
-
-Optional:
-- brcm,pcie-ob-oarr-size: Some iProc SoCs need the OARR size bit to be set to
-increase the outbound window size

MSI support (optional):

@@ -95,9 +90,7 @@ Example:
phy-names = "pcie-phy";

brcm,pcie-ob;
- brcm,pcie-ob-oarr-size;
brcm,pcie-ob-axi-offset = <0x00000000>;
- brcm,pcie-ob-window-size = <256>;

msi-parent = <&msi0>;

--
2.1.4

2016-10-24 22:24:51

by Scott Branden

[permalink] [raw]
Subject: Re: [PATCH 07/12] PCI: iproc: Update iProc PCIe DT binding

2 spelling typos

On 16-10-24 03:17 PM, Ray Jui wrote:
> Remove the following outbound related device tree properties:
> brcm,pcie-ob-window-size
> brcm,pcie-ob-oarr-size
>
> The above two prperties are a bit duplicated in functions. In addition,
properties typo
> the next generation iProc PCIe controller has outbound mapping window that
> supports more than just two sizes, which cannot be properly supported by
> these properties
>
> A better solution is to make the driver outbound mapping logic slightly
> more inteligent so it can figure out how to configure the outbound mapping
intelligent typo
> based on the 'ranges' specified in device tree and the number and sizes
> of the outbound mapping windows that a particular revision of the iProc
> PCIe controller can support
>
> Signed-off-by: Ray Jui <[email protected]>
> Reviewed-by: Scott Branden <[email protected]>
> ---
> Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt | 7 -------
> 1 file changed, 7 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
> index 071bbc2..f827b8d 100644
> --- a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
> +++ b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
> @@ -35,11 +35,6 @@ effective:
> Required:
> - brcm,pcie-ob-axi-offset: The offset from the AXI address to the internal
> address used by the iProc PCIe core (not the PCIe address)
> -- brcm,pcie-ob-window-size: The outbound address mapping window size (in MB)
> -
> -Optional:
> -- brcm,pcie-ob-oarr-size: Some iProc SoCs need the OARR size bit to be set to
> -increase the outbound window size
>
> MSI support (optional):
>
> @@ -95,9 +90,7 @@ Example:
> phy-names = "pcie-phy";
>
> brcm,pcie-ob;
> - brcm,pcie-ob-oarr-size;
> brcm,pcie-ob-axi-offset = <0x00000000>;
> - brcm,pcie-ob-window-size = <256>;
>
> msi-parent = <&msi0>;
>
>

2016-10-24 22:27:18

by Ray Jui

[permalink] [raw]
Subject: Re: [PATCH 07/12] PCI: iproc: Update iProc PCIe DT binding

Thanks! Will address both typos in the next revision. Waiting for
feedback from Bjorn before sending out the next revision.

Ray

On 10/24/2016 3:24 PM, Scott Branden wrote:
> 2 spelling typos
>
> On 16-10-24 03:17 PM, Ray Jui wrote:
>> Remove the following outbound related device tree properties:
>> brcm,pcie-ob-window-size
>> brcm,pcie-ob-oarr-size
>>
>> The above two prperties are a bit duplicated in functions. In addition,
> properties typo
>> the next generation iProc PCIe controller has outbound mapping window
>> that
>> supports more than just two sizes, which cannot be properly supported by
>> these properties
>>
>> A better solution is to make the driver outbound mapping logic slightly
>> more inteligent so it can figure out how to configure the outbound
>> mapping
> intelligent typo
>> based on the 'ranges' specified in device tree and the number and sizes
>> of the outbound mapping windows that a particular revision of the iProc
>> PCIe controller can support
>>
>> Signed-off-by: Ray Jui <[email protected]>
>> Reviewed-by: Scott Branden <[email protected]>
>> ---
>> Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt | 7 -------
>> 1 file changed, 7 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
>> b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
>> index 071bbc2..f827b8d 100644
>> --- a/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
>> +++ b/Documentation/devicetree/bindings/pci/brcm,iproc-pcie.txt
>> @@ -35,11 +35,6 @@ effective:
>> Required:
>> - brcm,pcie-ob-axi-offset: The offset from the AXI address to the
>> internal
>> address used by the iProc PCIe core (not the PCIe address)
>> -- brcm,pcie-ob-window-size: The outbound address mapping window size
>> (in MB)
>> -
>> -Optional:
>> -- brcm,pcie-ob-oarr-size: Some iProc SoCs need the OARR size bit to
>> be set to
>> -increase the outbound window size
>>
>> MSI support (optional):
>>
>> @@ -95,9 +90,7 @@ Example:
>> phy-names = "pcie-phy";
>>
>> brcm,pcie-ob;
>> - brcm,pcie-ob-oarr-size;
>> brcm,pcie-ob-axi-offset = <0x00000000>;
>> - brcm,pcie-ob-window-size = <256>;
>>
>> msi-parent = <&msi0>;
>>
>>

2016-10-24 22:30:23

by Scott Branden

[permalink] [raw]
Subject: Re: [PATCH 00/12] Additional iProc PCIe host support/fixes

Looks fine other than spelling mistakes in some commit messages.

Acked-by: Scott Branden <[email protected]>

On 16-10-24 03:17 PM, Ray Jui wrote:
> This patch series contains various changes and fixes to the iProc PCIe
> host driver. It also adds support for the next generation of PAXB and
> PAXC based host controllers
>
> This patch series was developed based on v4.9-rc1 and tested on both NS2
> SVK and Cygnus wireless audio platform
>
> repo: https://github.com/Broadcom/arm64-linux.git
> branch: iproc-pcie-v1
>
> Ray Jui (12):
> PCI: iproc: Improve core register population
> PCI: iproc: Do not reset PAXC when initializing the driver
> PCI: iproc: Add BCMA type
> PCI: iproc: Fix exception with multi-function devices
> PCI: iproc: Update iProc PCIe DT binding
> PCI: iproc: Add PAXC v2 support
> PCI: iproc: Update iProc PCIe DT binding
> PCI: iproc: Making outbound mapping code more generic
> PCI: iproc: Update iProc PCIe DT binding
> PCI: iproc: Add inbound DMA mapping support
> PCI: iproc: Update iProc PCIe DT binding
> PCI: iproc: Add support for the next-gen PAXB controller
>
> .../devicetree/bindings/pci/brcm,iproc-pcie.txt | 43 +-
> drivers/pci/host/pcie-iproc-bcma.c | 1 +
> drivers/pci/host/pcie-iproc-msi.c | 1 +
> drivers/pci/host/pcie-iproc-platform.c | 19 +-
> drivers/pci/host/pcie-iproc.c | 942 ++++++++++++++++++---
> drivers/pci/host/pcie-iproc.h | 45 +-
> 6 files changed, 916 insertions(+), 135 deletions(-)
>

2016-10-24 22:32:01

by Ray Jui

[permalink] [raw]
Subject: Re: [PATCH 00/12] Additional iProc PCIe host support/fixes



On 10/24/2016 3:30 PM, Scott Branden wrote:
> Looks fine other than spelling mistakes in some commit messages.
>
> Acked-by: Scott Branden <[email protected]>
>

Thanks, Scott. I will address those in the next revision of patchset.
But would like to get some comments from Bjorn before sending out the
next patchset.


> On 16-10-24 03:17 PM, Ray Jui wrote:
>> This patch series contains various changes and fixes to the iProc PCIe
>> host driver. It also adds support for the next generation of PAXB and
>> PAXC based host controllers
>>
>> This patch series was developed based on v4.9-rc1 and tested on both NS2
>> SVK and Cygnus wireless audio platform
>>
>> repo: https://github.com/Broadcom/arm64-linux.git
>> branch: iproc-pcie-v1
>>
>> Ray Jui (12):
>> PCI: iproc: Improve core register population
>> PCI: iproc: Do not reset PAXC when initializing the driver
>> PCI: iproc: Add BCMA type
>> PCI: iproc: Fix exception with multi-function devices
>> PCI: iproc: Update iProc PCIe DT binding
>> PCI: iproc: Add PAXC v2 support
>> PCI: iproc: Update iProc PCIe DT binding
>> PCI: iproc: Making outbound mapping code more generic
>> PCI: iproc: Update iProc PCIe DT binding
>> PCI: iproc: Add inbound DMA mapping support
>> PCI: iproc: Update iProc PCIe DT binding
>> PCI: iproc: Add support for the next-gen PAXB controller
>>
>> .../devicetree/bindings/pci/brcm,iproc-pcie.txt | 43 +-
>> drivers/pci/host/pcie-iproc-bcma.c | 1 +
>> drivers/pci/host/pcie-iproc-msi.c | 1 +
>> drivers/pci/host/pcie-iproc-platform.c | 19 +-
>> drivers/pci/host/pcie-iproc.c | 942
>> ++++++++++++++++++---
>> drivers/pci/host/pcie-iproc.h | 45 +-
>> 6 files changed, 916 insertions(+), 135 deletions(-)
>>

2016-10-24 22:52:50

by Bjorn Helgaas

[permalink] [raw]
Subject: Re: [PATCH 00/12] Additional iProc PCIe host support/fixes

Hi Ray,

On Mon, Oct 24, 2016 at 03:17:00PM -0700, Ray Jui wrote:
> This patch series contains various changes and fixes to the iProc PCIe
> host driver. It also adds support for the next generation of PAXB and
> PAXC based host controllers
>
> This patch series was developed based on v4.9-rc1 and tested on both NS2
> SVK and Cygnus wireless audio platform
>
> repo: https://github.com/Broadcom/arm64-linux.git
> branch: iproc-pcie-v1
>
> Ray Jui (12):
> PCI: iproc: Improve core register population
> PCI: iproc: Do not reset PAXC when initializing the driver
> PCI: iproc: Add BCMA type
> PCI: iproc: Fix exception with multi-function devices
> PCI: iproc: Update iProc PCIe DT binding
> PCI: iproc: Add PAXC v2 support
> PCI: iproc: Update iProc PCIe DT binding
> PCI: iproc: Making outbound mapping code more generic
> PCI: iproc: Update iProc PCIe DT binding
> PCI: iproc: Add inbound DMA mapping support
> PCI: iproc: Update iProc PCIe DT binding
> PCI: iproc: Add support for the next-gen PAXB controller

I'll look at these more soon, but there are four patches above with
identical titles ("PCI: iproc: Update iProc PCIe DT binding"). The
"iProc PCIe" part is a little redundant; can you remove that and
instead include a hint about what each patch is about, e.g.,
"add optional dma-ranges", "remove outbound window properties", etc?

> .../devicetree/bindings/pci/brcm,iproc-pcie.txt | 43 +-
> drivers/pci/host/pcie-iproc-bcma.c | 1 +
> drivers/pci/host/pcie-iproc-msi.c | 1 +
> drivers/pci/host/pcie-iproc-platform.c | 19 +-
> drivers/pci/host/pcie-iproc.c | 942 ++++++++++++++++++---
> drivers/pci/host/pcie-iproc.h | 45 +-
> 6 files changed, 916 insertions(+), 135 deletions(-)
>
> --
> 2.1.4
>

2016-10-24 22:54:08

by Ray Jui

[permalink] [raw]
Subject: Re: [PATCH 00/12] Additional iProc PCIe host support/fixes

Hi Bjorn,

On 10/24/2016 3:52 PM, Bjorn Helgaas wrote:
> Hi Ray,
>
> On Mon, Oct 24, 2016 at 03:17:00PM -0700, Ray Jui wrote:
>> This patch series contains various changes and fixes to the iProc PCIe
>> host driver. It also adds support for the next generation of PAXB and
>> PAXC based host controllers
>>
>> This patch series was developed based on v4.9-rc1 and tested on both NS2
>> SVK and Cygnus wireless audio platform
>>
>> repo: https://github.com/Broadcom/arm64-linux.git
>> branch: iproc-pcie-v1
>>
>> Ray Jui (12):
>> PCI: iproc: Improve core register population
>> PCI: iproc: Do not reset PAXC when initializing the driver
>> PCI: iproc: Add BCMA type
>> PCI: iproc: Fix exception with multi-function devices
>> PCI: iproc: Update iProc PCIe DT binding
>> PCI: iproc: Add PAXC v2 support
>> PCI: iproc: Update iProc PCIe DT binding
>> PCI: iproc: Making outbound mapping code more generic
>> PCI: iproc: Update iProc PCIe DT binding
>> PCI: iproc: Add inbound DMA mapping support
>> PCI: iproc: Update iProc PCIe DT binding
>> PCI: iproc: Add support for the next-gen PAXB controller
>
> I'll look at these more soon, but there are four patches above with
> identical titles ("PCI: iproc: Update iProc PCIe DT binding"). The
> "iProc PCIe" part is a little redundant; can you remove that and
> instead include a hint about what each patch is about, e.g.,
> "add optional dma-ranges", "remove outbound window properties", etc?
>

Yes, I'll certainly do that in the next revision of the patchset.

Thanks,

Ray