[PATCH v9 2/2] pcie-designware platform driver

From: Joao Pinto
Date: Mon Feb 08 2016 - 12:45:38 EST


The "wait for link" routine was centralised and so all drivers using it
(dra7xx, exynos, imx6 and spear13xx) were updated to include the new
function. The keystone driver was not updated because it had some custom
opreations in the link waiting loop.

A simple module (pcie-designware-plat) was created to contain the
specific platform init code for pcie-designware.

Signed-off-by: Joao Pinto <jpinto@xxxxxxxxxxxx>
---
Change v8 -> v9 (Bjorn Helgaas and Arnd Bergmann):
- wait for link routine was moved to pcie-designware, this implicated
an update in ra7xx, exynos, imx6 and spear13xx pcie drivers
- the proposed synopsys pcie rc platform driver is now a simple module
that uses pcie-designware functions only and has no custom features
- the designware-pcie.txt was updated with a DT example
Change v7 -> v8 (Bjorn Helgaas and Arnd Bergmann):
- driver name was changed from pcie-synopsys to pcie-dw-pltfm
- mdelay() replaced for msleep() in the new driver
- Devicetree bindings for the new driver was updated (config space removed
from ranges)
- Unnecessary synopsys_pcie_irq_handler() was removed
- Driver compatibility strings updated
Change v6 -> v7 (Bjorn Helgaas):
- driver name was changed from pcie-snpsdev to pcie-synopsys
- driver internals (functions and certain variables) also changed name
accordingly
- devicetree bindings documentation also changed accordingly
- quirk function dw_pcie_link_retrain() was removed (tests were made
successfully without it)
- driver was changed to meet pci standards (link up confirmation routine,
init rc functions, etc.)
- EPROBE_DEFER were removed
Change v5 -> v6:
- Nothing changed (just to keep up with patch set version).
Change v4 -> v5:
- Nothing changed (just to keep up with patch set version).
Changes v3 -> v4 (Bjorn Helgaas):
- ARCH dependencies were added to the drivers/pci/host/kconfig for the
PCIE_SNPSDEV.
Changes v2 -> v3 (Bjorn Helgaas):
- link init stuff was moved to a snpsdev_pcie_establish_link() function in
pcie-snpsdev
- pcie-snpsdev driver declaration was changed to be more
standard (Bjorn Helgaas)
- pcie-designware' dw_pcie_link_retrain() now use standard registers from
pci-regs.h (Bjorn Helgaas)
- pcie-snpsdev.txt was complemented with more info (Mark Rutland)
Changes v1 -> v2 (Bjorn Helgaas):
- Fixups snpsdev_pcie_fixup_bridge() and snpsdev_pcie_fixup_res() were removed
from the driver (these functions were for specific tests only and not usefull
in mainline)
- Driver' comments were reviewed (fix Typos and excessive comments removal)
- Removed unnecessary definitions in the driver source (PCIE_PHY_CTRL and
PCIE_PHY_STAT)

.../devicetree/bindings/pci/designware-pcie.txt | 17 +++
drivers/pci/host/Kconfig | 11 ++
drivers/pci/host/Makefile | 1 +
drivers/pci/host/pci-dra7xx.c | 11 +-
drivers/pci/host/pci-exynos.c | 11 +-
drivers/pci/host/pci-imx6.c | 11 +-
drivers/pci/host/pcie-designware-plat.c | 143 +++++++++++++++++++++
drivers/pci/host/pcie-designware.c | 31 ++++-
drivers/pci/host/pcie-designware.h | 6 +
drivers/pci/host/pcie-spear13xx.c | 12 +-
10 files changed, 217 insertions(+), 37 deletions(-)
create mode 100644 drivers/pci/host/pcie-designware-plat.c

diff --git a/Documentation/devicetree/bindings/pci/designware-pcie.txt b/Documentation/devicetree/bindings/pci/designware-pcie.txt
index 5b0853d..64f2fff 100644
--- a/Documentation/devicetree/bindings/pci/designware-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/designware-pcie.txt
@@ -28,3 +28,20 @@ Optional properties:
- clock-names: Must include the following entries:
- "pcie"
- "pcie_bus"
+
+Example configuration:
+
+ pcie: pcie@0xdffff000 {
+ compatible = "snps,dw-pcie";
+ reg = <0xdffff000 0x1000>, /* Controller registers */
+ <0xd0000000 0x2000>; /* PCI config space */
+ reg-names = "ctrlreg", "config";
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ ranges = <0x81000000 0 0x00000000 0xde000000 0 0x00010000
+ 0x82000000 0 0xd0400000 0xd0400000 0 0x0d000000>;
+ interrupts = <25>, <24>;
+ #interrupt-cells = <1>;
+ num-lanes = <1>;
+ };
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index f131ba9..b564f8a 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -15,6 +15,17 @@ config PCI_MVEBU
depends on ARCH_MVEBU || ARCH_DOVE
depends on OF

+config PCIE_DW_PLAT
+ bool "Platform bus based DesignWare PCIe Controller"
+ select PCIE_DW
+ ---help---
+ This selects the DesignWare PCIe controller support. Select this if
+ you have an PCIe controller on Platform bus.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
config PCIE_DW
bool

diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 9d4d3c6..d979121 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -1,4 +1,5 @@
obj-$(CONFIG_PCIE_DW) += pcie-designware.o
+obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/host/pci-dra7xx.c
index 8c36880..9b394bb 100644
--- a/drivers/pci/host/pci-dra7xx.c
+++ b/drivers/pci/host/pci-dra7xx.c
@@ -10,7 +10,6 @@
* published by the Free Software Foundation.
*/

-#include <linux/delay.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
@@ -108,7 +107,6 @@ static int dra7xx_pcie_establish_link(struct pcie_port *pp)
{
struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
u32 reg;
- unsigned int retries;

if (dw_pcie_link_up(pp)) {
dev_err(pp->dev, "link is already up\n");
@@ -119,13 +117,10 @@ static int dra7xx_pcie_establish_link(struct pcie_port *pp)
reg |= LTSSM_EN;
dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);

- for (retries = 0; retries < 1000; retries++) {
- if (dw_pcie_link_up(pp))
- return 0;
- usleep_range(10, 20);
- }
+ /* check if the link is up or not */
+ if (!dw_pcie_wait_for_link(pp))
+ return 0;

- dev_err(pp->dev, "link is not up\n");
return -EINVAL;
}

diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
index 01095e1..bd26e15 100644
--- a/drivers/pci/host/pci-exynos.c
+++ b/drivers/pci/host/pci-exynos.c
@@ -318,7 +318,6 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
{
struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
u32 val;
- unsigned int retries;

if (dw_pcie_link_up(pp)) {
dev_err(pp->dev, "Link already up\n");
@@ -357,13 +356,8 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
PCIE_APP_LTSSM_ENABLE);

/* check if the link is up or not */
- for (retries = 0; retries < 10; retries++) {
- if (dw_pcie_link_up(pp)) {
- dev_info(pp->dev, "Link up\n");
- return 0;
- }
- mdelay(100);
- }
+ if (!dw_pcie_wait_for_link(pp))
+ return 0;

while (exynos_phy_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED) == 0) {
val = exynos_blk_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED);
@@ -372,7 +366,6 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
/* power off phy */
exynos_pcie_power_off_phy(pp);

- dev_err(pp->dev, "PCIe Link Fail\n");
return -EINVAL;
}

diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c
index 22e8224..42c6930 100644
--- a/drivers/pci/host/pci-imx6.c
+++ b/drivers/pci/host/pci-imx6.c
@@ -330,15 +330,10 @@ static void imx6_pcie_init_phy(struct pcie_port *pp)

static int imx6_pcie_wait_for_link(struct pcie_port *pp)
{
- unsigned int retries;
-
- for (retries = 0; retries < 200; retries++) {
- if (dw_pcie_link_up(pp))
- return 0;
- usleep_range(100, 1000);
- }
+ /* check if the link is up or not */
+ if (!dw_pcie_wait_for_link(pp))
+ return 0;

- dev_err(pp->dev, "phy link never came up\n");
dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
diff --git a/drivers/pci/host/pcie-designware-plat.c b/drivers/pci/host/pcie-designware-plat.c
new file mode 100644
index 0000000..5f75aba
--- /dev/null
+++ b/drivers/pci/host/pcie-designware-plat.c
@@ -0,0 +1,143 @@
+/*
+ * PCIe RC driver for Synopsys Designware Core
+ *
+ * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com)
+ *
+ * Authors: Joao Pinto <jpinto@xxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/signal.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+struct dw_plat_pcie {
+ void __iomem *mem_base;
+ struct pcie_port pp;
+};
+
+static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg)
+{
+ struct pcie_port *pp = arg;
+
+ return dw_handle_msi_irq(pp);
+}
+
+static void dw_plat_pcie_host_init(struct pcie_port *pp)
+{
+ dw_pcie_setup_rc(pp);
+
+ dw_pcie_wait_for_link(pp);
+
+ if (IS_ENABLED(CONFIG_PCI_MSI))
+ dw_pcie_msi_init(pp);
+}
+
+static struct pcie_host_ops dw_plat_pcie_host_ops = {
+ .host_init = dw_plat_pcie_host_init,
+};
+
+static int dw_plat_add_pcie_port(struct pcie_port *pp,
+ struct platform_device *pdev)
+{
+ int ret;
+
+ pp->irq = platform_get_irq(pdev, 1);
+
+ if (pp->irq < 0)
+ return pp->irq;
+
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ pp->msi_irq = platform_get_irq(pdev, 0);
+
+ if (pp->msi_irq < 0)
+ return pp->msi_irq;
+
+ ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+ dw_plat_pcie_msi_irq_handler,
+ IRQF_SHARED, "dw-plat-pcie-msi", pp);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request msi irq\n");
+ return ret;
+ }
+ }
+
+ pp->root_bus_nr = -1;
+ pp->ops = &dw_plat_pcie_host_ops;
+
+ ret = dw_pcie_host_init(pp);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize host\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int dw_plat_pcie_probe(struct platform_device *pdev)
+{
+ struct dw_plat_pcie *dw_plat_pcie;
+ struct pcie_port *pp;
+ struct resource *res; /* Resource from DT */
+ int ret;
+
+ dw_plat_pcie = devm_kzalloc(&pdev->dev, sizeof(*dw_plat_pcie),
+ GFP_KERNEL);
+ if (!dw_plat_pcie)
+ return -ENOMEM;
+
+ pp = &dw_plat_pcie->pp;
+ pp->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res)
+ return -ENODEV;
+
+ dw_plat_pcie->mem_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dw_plat_pcie->mem_base))
+ return PTR_ERR(dw_plat_pcie->mem_base);
+
+ pp->dbi_base = dw_plat_pcie->mem_base;
+
+ ret = dw_plat_add_pcie_port(pp, pdev);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, dw_plat_pcie);
+
+ return 0;
+}
+
+static const struct of_device_id dw_plat_pcie_of_match[] = {
+ { .compatible = "snps,dw-pcie", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dw_plat_pcie_of_match);
+
+static struct platform_driver dw_plat_pcie_driver = {
+ .driver = {
+ .name = "dw-pcie",
+ .of_match_table = dw_plat_pcie_of_match,
+ },
+ .probe = dw_plat_pcie_probe,
+};
+
+module_platform_driver(dw_plat_pcie_driver);
+
+MODULE_AUTHOR("Joao Pinto <Joao.Pinto@xxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Synopsys PCIe host controller glue platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index 540f077..68ca614 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -22,6 +22,7 @@
#include <linux/pci_regs.h>
#include <linux/platform_device.h>
#include <linux/types.h>
+#include <linux/delay.h>

#include "pcie-designware.h"

@@ -69,6 +70,11 @@
#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
#define PCIE_ATU_UPPER_TARGET 0x91C

+/* PCIe Port Logic registers */
+#define PCIE_PHY_DEBUG_R1_LINK_UP 0x00000010
+#define PLR_OFFSET 0x700
+#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c)
+
static struct pci_ops dw_pcie_ops;

int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val)
@@ -380,12 +386,33 @@ static struct msi_controller dw_pcie_msi_chip = {
.teardown_irq = dw_msi_teardown_irq,
};

+int dw_pcie_wait_for_link(struct pcie_port *pp)
+{
+ int retries;
+
+ /* check if the link is up or not */
+ for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
+ if (dw_pcie_link_up(pp)) {
+ dev_info(pp->dev, "link up\n");
+ return 0;
+ }
+ usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
+ }
+
+ dev_err(pp->dev, "phy link never came up\n");
+
+ return 1;
+}
+
int dw_pcie_link_up(struct pcie_port *pp)
{
+ u32 val;
+
if (pp->ops->link_up)
return pp->ops->link_up(pp);
- else
- return 0;
+
+ val = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1);
+ return val & PCIE_PHY_DEBUG_R1_LINK_UP;
}

static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h
index 2356d29..8e42624 100644
--- a/drivers/pci/host/pcie-designware.h
+++ b/drivers/pci/host/pcie-designware.h
@@ -22,6 +22,11 @@
#define MAX_MSI_IRQS 32
#define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32)

+/* Parameters for the Waiting for link up routine */
+#define LINK_WAIT_MAX_RETRIES 10
+#define LINK_WAIT_USLEEP_MIN 90000
+#define LINK_WAIT_USLEEP_MAX 100000
+
struct pcie_port {
struct device *dev;
u8 root_bus_nr;
@@ -76,6 +81,7 @@ int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val);
int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val);
irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
void dw_pcie_msi_init(struct pcie_port *pp);
+int dw_pcie_wait_for_link(struct pcie_port *pp);
int dw_pcie_link_up(struct pcie_port *pp);
void dw_pcie_setup_rc(struct pcie_port *pp);
int dw_pcie_host_init(struct pcie_port *pp);
diff --git a/drivers/pci/host/pcie-spear13xx.c b/drivers/pci/host/pcie-spear13xx.c
index b95b756..ffdff1d 100644
--- a/drivers/pci/host/pcie-spear13xx.c
+++ b/drivers/pci/host/pcie-spear13xx.c
@@ -13,7 +13,6 @@
*/

#include <linux/clk.h>
-#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -149,7 +148,6 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp)
struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
u32 exp_cap_off = EXP_CAP_ID_OFFSET;
- unsigned int retries;

if (dw_pcie_link_up(pp)) {
dev_err(pp->dev, "link already up\n");
@@ -201,15 +199,9 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp)
&app_reg->app_ctrl_0);

/* check if the link is up or not */
- for (retries = 0; retries < 10; retries++) {
- if (dw_pcie_link_up(pp)) {
- dev_info(pp->dev, "link up\n");
- return 0;
- }
- mdelay(100);
- }
+ if (!dw_pcie_wait_for_link(pp))
+ return 0;

- dev_err(pp->dev, "link Fail\n");
return -EINVAL;
}

--
1.8.1.5