Discussion:
[PATCH v6] PCI: Layerscape: Add Layerscape PCIe driver
Minghuan Lian
2014-10-14 10:18:08 UTC
Permalink
Add support for Freescale Layerscape PCIe controller. This driver
re-uses the designware core code.

Signed-off-by: Minghuan Lian <***@freescale.com>
---
Change log:
v6:
1. Delete unnecessary if statement in ls_pcie_host_init()

v5:
1. Add MAINTAINERS information
2. Remove nnnecessary NULL pointer check
3. Use dev_dbg instead of dev_err in ls_pcie_msi_irq_handler()

v4:
1. Add 'depends on OF' to Kconfig

v3:
1. Add prefechable mem region and adjust mem region
2. Rename 'pcie-scfg' to 'fsl,pcie-scfg'
3. Change MSI interrupt handler
4. Use module_platform_driver_probe instead of subsys_initcall

v2:
1. Add pcie-scfg to link scfg device node and remove "fsl, ls-pcie" compatible
2. Use regmap API to access scfg.
3. remove ls1021 PCI device ID.
4. remove unused irq pme_irq and ls_pcie_list.
5. Do not set scfg bit reverse mode

.../devicetree/bindings/pci/layerscape-pci.txt | 46 ++++
MAINTAINERS | 10 +
drivers/pci/host/Kconfig | 8 +
drivers/pci/host/Makefile | 1 +
drivers/pci/host/pci-layerscape.c | 269 +++++++++++++++++++++
5 files changed, 334 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pci/layerscape-pci.txt
create mode 100644 drivers/pci/host/pci-layerscape.c

diff --git a/Documentation/devicetree/bindings/pci/layerscape-pci.txt b/Documentation/devicetree/bindings/pci/layerscape-pci.txt
new file mode 100644
index 0000000..d7b0026
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/layerscape-pci.txt
@@ -0,0 +1,46 @@
+Freescale Layerscape PCIe controller
+
+This PCIe host controller is based on the Synopsis Designware PCIe IP
+and thus inherits all the common properties defined in designware-pcie.txt.
+
+Required properties:
+- compatible: should contain the platform identifier such as "fsl,ls1021a-pcie"
+- reg: base addresses and lengths of the PCIe controller
+- interrupts: A list of interrupt outputs of the controller. Must contain an
+ entry for each entry in the interrupt-names property.
+- interrupt-names: Must include the following entries:
+ "intr": The interrupt that is asserted for controller interrupts
+ "msi": The interrupt that is asserted when an MSI is received
+ "pme": The interrupt that is asserted when PME state changes
+- fsl,pcie-scfg: Must include two entries.
+ The first entry must be a link to the SCFG device node
+ The second entry must be '0' or '1' based on physical PCIe controller index.
+ used to get SCFG PEXN registers
+
+Example:
+
+ ***@3400000 {
+ compatible = "fsl,ls1021a-pcie", "snps,dw-pcie";
+ reg = <0x00 0x03400000 0x0 0x00010000 /* controller registers */
+ 0x40 0x00000000 0x0 0x00002000>; /* configuration space */
+ reg-names = "regs", "config";
+ interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */
+ <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH>, /* MSI interrupt */
+ <GIC_SPI 181 IRQ_TYPE_LEVEL_HIGH>; /* PME interrupt */
+ interrupt-names = "intr", "msi", "pme";
+ fsl,pcie-scfg = <&scfg 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ num-lanes = <4>;
+ bus-range = <0x0 0xff>;
+ ranges = <0x81000000 0x0 0x00000000 0x40 0x00010000 0x0 0x00010000 /* downstream I/O */
+ 0xc2000000 0x0 0x20000000 0x40 0x20000000 0x0 0x20000000 /* prefetchable memory */
+ 0x82000000 0x0 0x40000000 0x40 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0000 0 0 1 &gic GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>,
+ <0000 0 0 2 &gic GIC_SPI 188 IRQ_TYPE_LEVEL_HIGH>,
+ <0000 0 0 3 &gic GIC_SPI 190 IRQ_TYPE_LEVEL_HIGH>,
+ <0000 0 0 4 &gic GIC_SPI 192 IRQ_TYPE_LEVEL_HIGH>;
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 1ff06de..b6237ab 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6936,6 +6936,16 @@ L: linux-***@vger.kernel.org
S: Maintained
F: drivers/pci/host/*spear*

+PCIe DRIVER FOR FREESCALE LAYERSCAPE
+M: Minghuan Lian <***@freescale.com>
+M: Mingkai Hu <***@freescale.com>
+M: Roy Zang <tie-***@freescale.com>
+L: linuxppc-***@lists.ozlabs.org
+L: linux-***@vger.kernel.org
+L: linux-arm-***@lists.infradead.org
+S: Maintained
+F: drivers/pci/host/pci-layerscape.c
+
PCMCIA SUBSYSTEM
P: Linux PCMCIA Team
L: linux-***@lists.infradead.org
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 8922c37..4788fb3 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -63,4 +63,12 @@ config PCIE_SPEAR13XX
help
Say Y here if you want PCIe support on SPEAr13XX SoCs.

+config PCI_LAYERSCAPE
+ bool "Freescale Layerscape PCIe controller"
+ depends on OF
+ select PCIE_DW
+ select MFD_SYSCON
+ help
+ Say Y here if you want PCIe controller support on Layerscape SoCs.
+
endmenu
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index d0e88f1..54cd9b2 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o
obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
+obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci-layerscape.c
new file mode 100644
index 0000000..a459de2
--- /dev/null
+++ b/drivers/pci/host/pci-layerscape.c
@@ -0,0 +1,269 @@
+/*
+ * PCIe host controller driver for Freescale Layerscape SoCs
+ *
+ * Copyright (C) 2014 Freescale Semiconductor.
+ *
+ * Author: Minghuan Lian <***@freescale.com>
+ *
+ * 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/kernel.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/bitrev.h>
+
+#include "pcie-designware.h"
+
+/* PEX1/2 Misc Ports Status Register */
+#define SCFG_PEXMSCPORTSR(pex_idx) (0x94 + (pex_idx) * 4)
+#define LTSSM_STATE_SHIFT 20
+#define LTSSM_STATE_MASK 0x3f
+#define LTSSM_PCIE_L0 0x11 /* L0 state */
+
+/* SCFG MSI register */
+#define SCFG_SPIMSICR 0x40
+#define SCFG_SPIMSICLRCR 0x90
+
+#define MSI_LS1021A_ADDR 0x1570040
+#define MSI_LS1021A_DATA(pex_idx) (0xb3 + pex_idx)
+
+/* Symbol Timer Register and Filter Mask Register 1 */
+#define PCIE_STRFMR1 0x71c
+
+struct ls_pcie {
+ struct list_head node;
+ struct device *dev;
+ struct pci_bus *bus;
+ void __iomem *dbi;
+ struct regmap *scfg;
+ struct pcie_port pp;
+ int index;
+ int msi_irq;
+};
+
+#define to_ls_pcie(x) container_of(x, struct ls_pcie, pp)
+
+static int ls_pcie_link_up(struct pcie_port *pp)
+{
+ u32 state;
+ struct ls_pcie *pcie = to_ls_pcie(pp);
+
+ regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state);
+ state = bitrev32(state);
+ state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK;
+
+ if (state < LTSSM_PCIE_L0)
+ return 0;
+
+ return 1;
+}
+
+static u32 ls_pcie_get_msi_addr(struct pcie_port *pp)
+{
+ return MSI_LS1021A_ADDR;
+}
+
+static u32 ls_pcie_get_msi_data(struct pcie_port *pp, int pos)
+{
+ struct ls_pcie *pcie = to_ls_pcie(pp);
+
+ return MSI_LS1021A_DATA(pcie->index);
+}
+
+static irqreturn_t ls_pcie_msi_irq_handler(int irq, void *data)
+{
+ struct pcie_port *pp = data;
+ struct ls_pcie *pcie = to_ls_pcie(pp);
+ unsigned int msi_irq;
+
+ /* clear the interrupt */
+ regmap_write(pcie->scfg, SCFG_SPIMSICLRCR,
+ MSI_LS1021A_DATA(pcie->index));
+
+ msi_irq = irq_find_mapping(pp->irq_domain, 0);
+ if (!msi_irq) {
+ /*
+ * that's weird who triggered this?
+ * just clear it
+ */
+ dev_dbg(pcie->dev, "unexpected MSI\n");
+ return IRQ_NONE;
+ }
+
+ generic_handle_irq(msi_irq);
+ return IRQ_HANDLED;
+}
+
+static void ls_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
+{
+}
+
+static void ls_pcie_msi_set_irq(struct pcie_port *pp, int irq)
+{
+}
+
+static void ls1021a_pcie_msi_fixup(struct pcie_port *pp)
+{
+ int i;
+
+ /*
+ * LS1021A has only one MSI interrupt
+ * Set all msi interrupts as used except the first one
+ */
+ for (i = 1; i < MAX_MSI_IRQS; i++)
+ set_bit(i, pp->msi_irq_in_use);
+}
+
+static void ls_pcie_host_init(struct pcie_port *pp)
+{
+ struct ls_pcie *pcie = to_ls_pcie(pp);
+ int count = 0;
+ u32 val;
+
+ dw_pcie_setup_rc(pp);
+
+ while (!ls_pcie_link_up(pp)) {
+ usleep_range(100, 1000);
+ count++;
+ if (count >= 200) {
+ dev_err(pp->dev, "phy link never came up\n");
+ return;
+ }
+ }
+
+ /*
+ * LS1021A Workaround for internal TKT228622
+ * to fix the INTx hang issue
+ */
+ val = ioread32(pcie->dbi + PCIE_STRFMR1);
+ val &= 0xffff;
+ iowrite32(val, pcie->dbi + PCIE_STRFMR1);
+
+ ls1021a_pcie_msi_fixup(pp);
+}
+
+static struct pcie_host_ops ls_pcie_host_ops = {
+ .link_up = ls_pcie_link_up,
+ .host_init = ls_pcie_host_init,
+ .msi_set_irq = ls_pcie_msi_set_irq,
+ .msi_clear_irq = ls_pcie_msi_clear_irq,
+ .get_msi_addr = ls_pcie_get_msi_addr,
+ .get_msi_data = ls_pcie_get_msi_data,
+};
+
+static int ls_add_pcie_port(struct ls_pcie *pcie)
+{
+ struct pcie_port *pp;
+ int ret;
+
+ pp = &pcie->pp;
+ pp->dev = pcie->dev;
+ pp->dbi_base = pcie->dbi;
+ pp->msi_irq = pcie->msi_irq;
+
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ ret = devm_request_irq(pp->dev, pp->msi_irq,
+ ls_pcie_msi_irq_handler,
+ IRQF_SHARED, "ls-pcie-msi", pp);
+ if (ret) {
+ dev_err(pp->dev, "failed to request msi irq\n");
+ return ret;
+ }
+ }
+
+ pp->root_bus_nr = -1;
+ pp->ops = &ls_pcie_host_ops;
+
+ ret = dw_pcie_host_init(pp);
+ if (ret) {
+ dev_err(pp->dev, "failed to initialize host\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __init ls_pcie_probe(struct platform_device *pdev)
+{
+ struct ls_pcie *pcie;
+ struct resource *dbi_base;
+ u32 index[2];
+ int ret;
+
+ pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return -ENOMEM;
+
+ pcie->dev = &pdev->dev;
+
+ dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+ if (!dbi_base) {
+ dev_err(&pdev->dev, "missing *regs* space\n");
+ return -ENODEV;
+ }
+
+ pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base);
+ if (IS_ERR(pcie->dbi))
+ return PTR_ERR(pcie->dbi);
+
+ pcie->scfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "fsl,pcie-scfg");
+ if (IS_ERR(pcie->scfg)) {
+ dev_err(&pdev->dev, "No syscfg phandle specified\n");
+ return PTR_ERR(pcie->scfg);
+ }
+
+ ret = of_property_read_u32_array(pdev->dev.of_node,
+ "fsl,pcie-scfg", index, 2);
+ if (ret)
+ return ret;
+ pcie->index = index[1];
+
+ pcie->msi_irq = platform_get_irq_byname(pdev, "msi");
+ if (pcie->msi_irq < 0) {
+ dev_err(&pdev->dev,
+ "failed to get MSI IRQ: %d\n", pcie->msi_irq);
+ return pcie->msi_irq;
+ }
+
+ ret = ls_add_pcie_port(pcie);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, pcie);
+
+ return 0;
+}
+
+static const struct of_device_id ls_pcie_of_match[] = {
+ { .compatible = "fsl,ls1021a-pcie" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ls_pcie_of_match);
+
+static struct platform_driver ls_pcie_driver = {
+ .driver = {
+ .name = "layerscape-pcie",
+ .owner = THIS_MODULE,
+ .of_match_table = ls_pcie_of_match,
+ },
+};
+
+module_platform_driver_probe(ls_pcie_driver, ls_pcie_probe);
+
+MODULE_AUTHOR("Minghuan Lian <***@freescale.com>");
+MODULE_DESCRIPTION("Freescale Layerscape PCIe host controller driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
Minghuan Lian
2014-10-14 10:18:09 UTC
Permalink
Add support for Freescale Layerscape PCIe controller. This driver
re-uses the designware core code.

Signed-off-by: Minghuan Lian <***@freescale.com>
---
Change log:
v6:
1. Delete unnecessary if statement in ls_pcie_host_init()

v5:
1. Add MAINTAINERS information
2. Remove nnnecessary NULL pointer check
3. Use dev_dbg instead of dev_err in ls_pcie_msi_irq_handler()

v4:
1. Add 'depends on OF' to Kconfig

v3:
1. Add prefechable mem region and adjust mem region
2. Rename 'pcie-scfg' to 'fsl,pcie-scfg'
3. Change MSI interrupt handler
4. Use module_platform_driver_probe instead of subsys_initcall

v2:
1. Add pcie-scfg to link scfg device node and remove "fsl, ls-pcie" compatible
2. Use regmap API to access scfg.
3. remove ls1021 PCI device ID.
4. remove unused irq pme_irq and ls_pcie_list.
5. Do not set scfg bit reverse mode

.../devicetree/bindings/pci/layerscape-pci.txt | 46 ++++
MAINTAINERS | 10 +
drivers/pci/host/Kconfig | 8 +
drivers/pci/host/Makefile | 1 +
drivers/pci/host/pci-layerscape.c | 269 +++++++++++++++++++++
5 files changed, 334 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pci/layerscape-pci.txt
create mode 100644 drivers/pci/host/pci-layerscape.c

diff --git a/Documentation/devicetree/bindings/pci/layerscape-pci.txt b/Documentation/devicetree/bindings/pci/layerscape-pci.txt
new file mode 100644
index 0000000..d7b0026
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/layerscape-pci.txt
@@ -0,0 +1,46 @@
+Freescale Layerscape PCIe controller
+
+This PCIe host controller is based on the Synopsis Designware PCIe IP
+and thus inherits all the common properties defined in designware-pcie.txt.
+
+Required properties:
+- compatible: should contain the platform identifier such as "fsl,ls1021a-pcie"
+- reg: base addresses and lengths of the PCIe controller
+- interrupts: A list of interrupt outputs of the controller. Must contain an
+ entry for each entry in the interrupt-names property.
+- interrupt-names: Must include the following entries:
+ "intr": The interrupt that is asserted for controller interrupts
+ "msi": The interrupt that is asserted when an MSI is received
+ "pme": The interrupt that is asserted when PME state changes
+- fsl,pcie-scfg: Must include two entries.
+ The first entry must be a link to the SCFG device node
+ The second entry must be '0' or '1' based on physical PCIe controller index.
+ used to get SCFG PEXN registers
+
+Example:
+
+ ***@3400000 {
+ compatible = "fsl,ls1021a-pcie", "snps,dw-pcie";
+ reg = <0x00 0x03400000 0x0 0x00010000 /* controller registers */
+ 0x40 0x00000000 0x0 0x00002000>; /* configuration space */
+ reg-names = "regs", "config";
+ interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */
+ <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH>, /* MSI interrupt */
+ <GIC_SPI 181 IRQ_TYPE_LEVEL_HIGH>; /* PME interrupt */
+ interrupt-names = "intr", "msi", "pme";
+ fsl,pcie-scfg = <&scfg 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ device_type = "pci";
+ num-lanes = <4>;
+ bus-range = <0x0 0xff>;
+ ranges = <0x81000000 0x0 0x00000000 0x40 0x00010000 0x0 0x00010000 /* downstream I/O */
+ 0xc2000000 0x0 0x20000000 0x40 0x20000000 0x0 0x20000000 /* prefetchable memory */
+ 0x82000000 0x0 0x40000000 0x40 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0000 0 0 1 &gic GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>,
+ <0000 0 0 2 &gic GIC_SPI 188 IRQ_TYPE_LEVEL_HIGH>,
+ <0000 0 0 3 &gic GIC_SPI 190 IRQ_TYPE_LEVEL_HIGH>,
+ <0000 0 0 4 &gic GIC_SPI 192 IRQ_TYPE_LEVEL_HIGH>;
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 1ff06de..b6237ab 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6936,6 +6936,16 @@ L: linux-***@vger.kernel.org
S: Maintained
F: drivers/pci/host/*spear*

+PCIe DRIVER FOR FREESCALE LAYERSCAPE
+M: Minghuan Lian <***@freescale.com>
+M: Mingkai Hu <***@freescale.com>
+M: Roy Zang <tie-***@freescale.com>
+L: linuxppc-***@lists.ozlabs.org
+L: linux-***@vger.kernel.org
+L: linux-arm-***@lists.infradead.org
+S: Maintained
+F: drivers/pci/host/pci-layerscape.c
+
PCMCIA SUBSYSTEM
P: Linux PCMCIA Team
L: linux-***@lists.infradead.org
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index 8922c37..4788fb3 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -63,4 +63,12 @@ config PCIE_SPEAR13XX
help
Say Y here if you want PCIe support on SPEAr13XX SoCs.

+config PCI_LAYERSCAPE
+ bool "Freescale Layerscape PCIe controller"
+ depends on OF
+ select PCIE_DW
+ select MFD_SYSCON
+ help
+ Say Y here if you want PCIe controller support on Layerscape SoCs.
+
endmenu
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index d0e88f1..54cd9b2 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o
obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
+obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci-layerscape.c
new file mode 100644
index 0000000..a459de2
--- /dev/null
+++ b/drivers/pci/host/pci-layerscape.c
@@ -0,0 +1,269 @@
+/*
+ * PCIe host controller driver for Freescale Layerscape SoCs
+ *
+ * Copyright (C) 2014 Freescale Semiconductor.
+ *
+ * Author: Minghuan Lian <***@freescale.com>
+ *
+ * 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/kernel.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/bitrev.h>
+
+#include "pcie-designware.h"
+
+/* PEX1/2 Misc Ports Status Register */
+#define SCFG_PEXMSCPORTSR(pex_idx) (0x94 + (pex_idx) * 4)
+#define LTSSM_STATE_SHIFT 20
+#define LTSSM_STATE_MASK 0x3f
+#define LTSSM_PCIE_L0 0x11 /* L0 state */
+
+/* SCFG MSI register */
+#define SCFG_SPIMSICR 0x40
+#define SCFG_SPIMSICLRCR 0x90
+
+#define MSI_LS1021A_ADDR 0x1570040
+#define MSI_LS1021A_DATA(pex_idx) (0xb3 + pex_idx)
+
+/* Symbol Timer Register and Filter Mask Register 1 */
+#define PCIE_STRFMR1 0x71c
+
+struct ls_pcie {
+ struct list_head node;
+ struct device *dev;
+ struct pci_bus *bus;
+ void __iomem *dbi;
+ struct regmap *scfg;
+ struct pcie_port pp;
+ int index;
+ int msi_irq;
+};
+
+#define to_ls_pcie(x) container_of(x, struct ls_pcie, pp)
+
+static int ls_pcie_link_up(struct pcie_port *pp)
+{
+ u32 state;
+ struct ls_pcie *pcie = to_ls_pcie(pp);
+
+ regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state);
+ state = bitrev32(state);
+ state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK;
+
+ if (state < LTSSM_PCIE_L0)
+ return 0;
+
+ return 1;
+}
+
+static u32 ls_pcie_get_msi_addr(struct pcie_port *pp)
+{
+ return MSI_LS1021A_ADDR;
+}
+
+static u32 ls_pcie_get_msi_data(struct pcie_port *pp, int pos)
+{
+ struct ls_pcie *pcie = to_ls_pcie(pp);
+
+ return MSI_LS1021A_DATA(pcie->index);
+}
+
+static irqreturn_t ls_pcie_msi_irq_handler(int irq, void *data)
+{
+ struct pcie_port *pp = data;
+ struct ls_pcie *pcie = to_ls_pcie(pp);
+ unsigned int msi_irq;
+
+ /* clear the interrupt */
+ regmap_write(pcie->scfg, SCFG_SPIMSICLRCR,
+ MSI_LS1021A_DATA(pcie->index));
+
+ msi_irq = irq_find_mapping(pp->irq_domain, 0);
+ if (!msi_irq) {
+ /*
+ * that's weird who triggered this?
+ * just clear it
+ */
+ dev_dbg(pcie->dev, "unexpected MSI\n");
+ return IRQ_NONE;
+ }
+
+ generic_handle_irq(msi_irq);
+ return IRQ_HANDLED;
+}
+
+static void ls_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
+{
+}
+
+static void ls_pcie_msi_set_irq(struct pcie_port *pp, int irq)
+{
+}
+
+static void ls1021a_pcie_msi_fixup(struct pcie_port *pp)
+{
+ int i;
+
+ /*
+ * LS1021A has only one MSI interrupt
+ * Set all msi interrupts as used except the first one
+ */
+ for (i = 1; i < MAX_MSI_IRQS; i++)
+ set_bit(i, pp->msi_irq_in_use);
+}
+
+static void ls_pcie_host_init(struct pcie_port *pp)
+{
+ struct ls_pcie *pcie = to_ls_pcie(pp);
+ int count = 0;
+ u32 val;
+
+ dw_pcie_setup_rc(pp);
+
+ while (!ls_pcie_link_up(pp)) {
+ usleep_range(100, 1000);
+ count++;
+ if (count >= 200) {
+ dev_err(pp->dev, "phy link never came up\n");
+ return;
+ }
+ }
+
+ /*
+ * LS1021A Workaround for internal TKT228622
+ * to fix the INTx hang issue
+ */
+ val = ioread32(pcie->dbi + PCIE_STRFMR1);
+ val &= 0xffff;
+ iowrite32(val, pcie->dbi + PCIE_STRFMR1);
+
+ ls1021a_pcie_msi_fixup(pp);
+}
+
+static struct pcie_host_ops ls_pcie_host_ops = {
+ .link_up = ls_pcie_link_up,
+ .host_init = ls_pcie_host_init,
+ .msi_set_irq = ls_pcie_msi_set_irq,
+ .msi_clear_irq = ls_pcie_msi_clear_irq,
+ .get_msi_addr = ls_pcie_get_msi_addr,
+ .get_msi_data = ls_pcie_get_msi_data,
+};
+
+static int ls_add_pcie_port(struct ls_pcie *pcie)
+{
+ struct pcie_port *pp;
+ int ret;
+
+ pp = &pcie->pp;
+ pp->dev = pcie->dev;
+ pp->dbi_base = pcie->dbi;
+ pp->msi_irq = pcie->msi_irq;
+
+ if (IS_ENABLED(CONFIG_PCI_MSI)) {
+ ret = devm_request_irq(pp->dev, pp->msi_irq,
+ ls_pcie_msi_irq_handler,
+ IRQF_SHARED, "ls-pcie-msi", pp);
+ if (ret) {
+ dev_err(pp->dev, "failed to request msi irq\n");
+ return ret;
+ }
+ }
+
+ pp->root_bus_nr = -1;
+ pp->ops = &ls_pcie_host_ops;
+
+ ret = dw_pcie_host_init(pp);
+ if (ret) {
+ dev_err(pp->dev, "failed to initialize host\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __init ls_pcie_probe(struct platform_device *pdev)
+{
+ struct ls_pcie *pcie;
+ struct resource *dbi_base;
+ u32 index[2];
+ int ret;
+
+ pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return -ENOMEM;
+
+ pcie->dev = &pdev->dev;
+
+ dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
+ if (!dbi_base) {
+ dev_err(&pdev->dev, "missing *regs* space\n");
+ return -ENODEV;
+ }
+
+ pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base);
+ if (IS_ERR(pcie->dbi))
+ return PTR_ERR(pcie->dbi);
+
+ pcie->scfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+ "fsl,pcie-scfg");
+ if (IS_ERR(pcie->scfg)) {
+ dev_err(&pdev->dev, "No syscfg phandle specified\n");
+ return PTR_ERR(pcie->scfg);
+ }
+
+ ret = of_property_read_u32_array(pdev->dev.of_node,
+ "fsl,pcie-scfg", index, 2);
+ if (ret)
+ return ret;
+ pcie->index = index[1];
+
+ pcie->msi_irq = platform_get_irq_byname(pdev, "msi");
+ if (pcie->msi_irq < 0) {
+ dev_err(&pdev->dev,
+ "failed to get MSI IRQ: %d\n", pcie->msi_irq);
+ return pcie->msi_irq;
+ }
+
+ ret = ls_add_pcie_port(pcie);
+ if (ret < 0)
+ return ret;
+
+ platform_set_drvdata(pdev, pcie);
+
+ return 0;
+}
+
+static const struct of_device_id ls_pcie_of_match[] = {
+ { .compatible = "fsl,ls1021a-pcie" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ls_pcie_of_match);
+
+static struct platform_driver ls_pcie_driver = {
+ .driver = {
+ .name = "layerscape-pcie",
+ .owner = THIS_MODULE,
+ .of_match_table = ls_pcie_of_match,
+ },
+};
+
+module_platform_driver_probe(ls_pcie_driver, ls_pcie_probe);
+
+MODULE_AUTHOR("Minghuan Lian <***@freescale.com>");
+MODULE_DESCRIPTION("Freescale Layerscape PCIe host controller driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
Kumar Gala
2014-10-16 14:41:27 UTC
Permalink
On Oct 14, 2014, at 12:18 PM, Minghuan Lian <***@freescale.co=
m> wrote:

> Add support for Freescale Layerscape PCIe controller. This driver
> re-uses the designware core code.
>=20
> Signed-off-by: Minghuan Lian <***@freescale.com>

Do you support multiple controllers or just one? I feel like the DWC P=
CIe core code needs changes to handle multiple controllers?

> ---
> Change log:
> v6:
> 1. Delete unnecessary if statement in ls_pcie_host_init()
>=20
> v5:
> 1. Add MAINTAINERS information
> 2. Remove nnnecessary NULL pointer check
> 3. Use dev_dbg instead of dev_err in ls_pcie_msi_irq_handler()=20
>=20
> v4:
> 1. Add 'depends on OF' to Kconfig=20
>=20
> v3:
> 1. Add prefechable mem region and adjust mem region
> 2. Rename 'pcie-scfg' to 'fsl,pcie-scfg'
> 3. Change MSI interrupt handler
> 4. Use module_platform_driver_probe instead of subsys_initcall
>=20
> v2:
> 1. Add pcie-scfg to link scfg device node and remove "fsl, ls-pcie" c=
ompatible
> 2. Use regmap API to access scfg.
> 3. remove ls1021 PCI device ID.
> 4. remove unused irq pme_irq and ls_pcie_list.
> 5. Do not set scfg bit reverse mode
>=20
> .../devicetree/bindings/pci/layerscape-pci.txt | 46 ++++
> MAINTAINERS | 10 +
> drivers/pci/host/Kconfig | 8 +
> drivers/pci/host/Makefile | 1 +
> drivers/pci/host/pci-layerscape.c | 269 ++++++++++++=
+++++++++
> 5 files changed, 334 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/pci/layerscape-p=
ci.txt
> create mode 100644 drivers/pci/host/pci-layerscape.c
>=20
> diff --git a/Documentation/devicetree/bindings/pci/layerscape-pci.txt=
b/Documentation/devicetree/bindings/pci/layerscape-pci.txt
> new file mode 100644
> index 0000000..d7b0026
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/layerscape-pci.txt
> @@ -0,0 +1,46 @@
> +Freescale Layerscape PCIe controller
> +
> +This PCIe host controller is based on the Synopsis Designware PCIe I=
P
> +and thus inherits all the common properties defined in designware-pc=
ie.txt.
> +
> +Required properties:
> +- compatible: should contain the platform identifier such as "fsl,ls=
1021a-pcie"
> +- reg: base addresses and lengths of the PCIe controller
> +- interrupts: A list of interrupt outputs of the controller. Must co=
ntain an
> + entry for each entry in the interrupt-names property.
> +- interrupt-names: Must include the following entries:
> + "intr": The interrupt that is asserted for controller interrupts
> + "msi": The interrupt that is asserted when an MSI is received
> + "pme": The interrupt that is asserted when PME state changes
> +- fsl,pcie-scfg: Must include two entries.
> + The first entry must be a link to the SCFG device node
> + The second entry must be '0' or '1' based on physical PCIe control=
ler index.
> + used to get SCFG PEXN registers
> +
> +Example:
> +
> + ***@3400000 {
> + compatible =3D "fsl,ls1021a-pcie", "snps,dw-pcie";
> + reg =3D <0x00 0x03400000 0x0 0x00010000 /* controller registers =
*/
> + 0x40 0x00000000 0x0 0x00002000>; /* configuration space */
> + reg-names =3D "regs", "config";
> + interrupts =3D <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>, /* controller in=
terrupt */
> + <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH>, /* MSI interrupt */
> + <GIC_SPI 181 IRQ_TYPE_LEVEL_HIGH>; /* PME interrupt */
> + interrupt-names =3D "intr", "msi", "pme";
> + fsl,pcie-scfg =3D <&scfg 0>;
> + #address-cells =3D <3>;
> + #size-cells =3D <2>;
> + device_type =3D "pci";
> + num-lanes =3D <4>;
> + bus-range =3D <0x0 0xff>;
> + ranges =3D <0x81000000 0x0 0x00000000 0x40 0x00010000 0x0 0x000100=
00 /* downstream I/O */
> + 0xc2000000 0x0 0x20000000 0x40 0x20000000 0x0 0x20000000 /* p=
refetchable memory */
> + 0x82000000 0x0 0x40000000 0x40 0x40000000 0x0 0x40000000>; /* n=
on-prefetchable memory */
> + #interrupt-cells =3D <1>;
> + interrupt-map-mask =3D <0 0 0 7>;
> + interrupt-map =3D <0000 0 0 1 &gic GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH=
>,
> + <0000 0 0 2 &gic GIC_SPI 188 IRQ_TYPE_LEVEL_HIGH>,
> + <0000 0 0 3 &gic GIC_SPI 190 IRQ_TYPE_LEVEL_HIGH>,
> + <0000 0 0 4 &gic GIC_SPI 192 IRQ_TYPE_LEVEL_HIGH>;
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 1ff06de..b6237ab 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6936,6 +6936,16 @@ L: linux-***@vger.kernel.org
> S: Maintained
> F: drivers/pci/host/*spear*
>=20
> +PCIe DRIVER FOR FREESCALE LAYERSCAPE
> +M: Minghuan Lian <***@freescale.com>
> +M: Mingkai Hu <***@freescale.com>
> +M: Roy Zang <tie-***@freescale.com>
> +L: linuxppc-***@lists.ozlabs.org
> +L: linux-***@vger.kernel.org
> +L: linux-arm-***@lists.infradead.org
> +S: Maintained
> +F: drivers/pci/host/pci-layerscape.c
> +
> PCMCIA SUBSYSTEM
> P: Linux PCMCIA Team
> L: linux-***@lists.infradead.org
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 8922c37..4788fb3 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -63,4 +63,12 @@ config PCIE_SPEAR13XX
> help
> Say Y here if you want PCIe support on SPEAr13XX SoCs.
>=20
> +config PCI_LAYERSCAPE
> + bool "Freescale Layerscape PCIe controller"
> + depends on OF
> + select PCIE_DW
> + select MFD_SYSCON
> + help
> + Say Y here if you want PCIe controller support on Layerscape SoCs=
=2E
> +
> endmenu
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index d0e88f1..54cd9b2 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -8,3 +8,4 @@ obj-$(CONFIG_PCI_RCAR_GEN2) +=3D pci-rcar-gen2.o
> obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) +=3D pcie-rcar.o
> obj-$(CONFIG_PCI_HOST_GENERIC) +=3D pci-host-generic.o
> obj-$(CONFIG_PCIE_SPEAR13XX) +=3D pcie-spear13xx.o
> +obj-$(CONFIG_PCI_LAYERSCAPE) +=3D pci-layerscape.o
> diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci=
-layerscape.c
> new file mode 100644
> index 0000000..a459de2
> --- /dev/null
> +++ b/drivers/pci/host/pci-layerscape.c
> @@ -0,0 +1,269 @@
> +/*
> + * PCIe host controller driver for Freescale Layerscape SoCs
> + *
> + * Copyright (C) 2014 Freescale Semiconductor.
> + *
> + * Author: Minghuan Lian <***@freescale.com>
> + *
> + * This program is free software; you can redistribute it and/or mod=
ify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of_pci.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_address.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/resource.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +#include <linux/bitrev.h>
> +
> +#include "pcie-designware.h"
> +
> +/* PEX1/2 Misc Ports Status Register */
> +#define SCFG_PEXMSCPORTSR(pex_idx) (0x94 + (pex_idx) * 4)
> +#define LTSSM_STATE_SHIFT 20
> +#define LTSSM_STATE_MASK 0x3f
> +#define LTSSM_PCIE_L0 0x11 /* L0 state */
> +
> +/* SCFG MSI register */
> +#define SCFG_SPIMSICR 0x40
> +#define SCFG_SPIMSICLRCR 0x90
> +
> +#define MSI_LS1021A_ADDR 0x1570040

This shouldn=92t be hard coded, should be coming from DT.

> +#define MSI_LS1021A_DATA(pex_idx) (0xb3 + pex_idx)
> +
> +/* Symbol Timer Register and Filter Mask Register 1 */
> +#define PCIE_STRFMR1 0x71c
> +
> +struct ls_pcie {
> + struct list_head node;
> + struct device *dev;
> + struct pci_bus *bus;
> + void __iomem *dbi;
> + struct regmap *scfg;
> + struct pcie_port pp;
> + int index;
> + int msi_irq;
> +};
> +
> +#define to_ls_pcie(x) container_of(x, struct ls_pcie, pp)
> +
> +static int ls_pcie_link_up(struct pcie_port *pp)
> +{
> + u32 state;
> + struct ls_pcie *pcie =3D to_ls_pcie(pp);
> +
> + regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state);
> + state =3D bitrev32(state);
> + state =3D (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK;
> +
> + if (state < LTSSM_PCIE_L0)
> + return 0;
> +
> + return 1;
> +}
> +
> +static u32 ls_pcie_get_msi_addr(struct pcie_port *pp)
> +{
> + return MSI_LS1021A_ADDR;
> +}
> +
> +static u32 ls_pcie_get_msi_data(struct pcie_port *pp, int pos)
> +{
> + struct ls_pcie *pcie =3D to_ls_pcie(pp);
> +
> + return MSI_LS1021A_DATA(pcie->index);
> +}
> +
> +static irqreturn_t ls_pcie_msi_irq_handler(int irq, void *data)
> +{
> + struct pcie_port *pp =3D data;
> + struct ls_pcie *pcie =3D to_ls_pcie(pp);
> + unsigned int msi_irq;
> +
> + /* clear the interrupt */
> + regmap_write(pcie->scfg, SCFG_SPIMSICLRCR,
> + MSI_LS1021A_DATA(pcie->index));
> +
> + msi_irq =3D irq_find_mapping(pp->irq_domain, 0);
> + if (!msi_irq) {
> + /*
> + * that's weird who triggered this?
> + * just clear it
> + */
> + dev_dbg(pcie->dev, "unexpected MSI\n");
> + return IRQ_NONE;
> + }
> +
> + generic_handle_irq(msi_irq);
> + return IRQ_HANDLED;
> +}
> +
> +static void ls_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
> +{
> +}
> +
> +static void ls_pcie_msi_set_irq(struct pcie_port *pp, int irq)
> +{
> +}
> +
> +static void ls1021a_pcie_msi_fixup(struct pcie_port *pp)
> +{
> + int i;
> +
> + /*
> + * LS1021A has only one MSI interrupt
> + * Set all msi interrupts as used except the first one
> + */
> + for (i =3D 1; i < MAX_MSI_IRQS; i++)
> + set_bit(i, pp->msi_irq_in_use);
> +}
> +
> +static void ls_pcie_host_init(struct pcie_port *pp)
> +{
> + struct ls_pcie *pcie =3D to_ls_pcie(pp);
> + int count =3D 0;
> + u32 val;
> +
> + dw_pcie_setup_rc(pp);
> +
> + while (!ls_pcie_link_up(pp)) {
> + usleep_range(100, 1000);
> + count++;
> + if (count >=3D 200) {
> + dev_err(pp->dev, "phy link never came up\n");
> + return;
> + }
> + }
> +
> + /*
> + * LS1021A Workaround for internal TKT228622
> + * to fix the INTx hang issue
> + */
> + val =3D ioread32(pcie->dbi + PCIE_STRFMR1);
> + val &=3D 0xffff;
> + iowrite32(val, pcie->dbi + PCIE_STRFMR1);

Is this something that will be fixed for production silicon? If so it =
should probably not exist for upstream code.

> +
> + ls1021a_pcie_msi_fixup(pp);
> +}
> +
> +static struct pcie_host_ops ls_pcie_host_ops =3D {
> + .link_up =3D ls_pcie_link_up,
> + .host_init =3D ls_pcie_host_init,
> + .msi_set_irq =3D ls_pcie_msi_set_irq,
> + .msi_clear_irq =3D ls_pcie_msi_clear_irq,
> + .get_msi_addr =3D ls_pcie_get_msi_addr,
> + .get_msi_data =3D ls_pcie_get_msi_data,
> +};
> +
> +static int ls_add_pcie_port(struct ls_pcie *pcie)
> +{
> + struct pcie_port *pp;
> + int ret;
> +
> + pp =3D &pcie->pp;
> + pp->dev =3D pcie->dev;
> + pp->dbi_base =3D pcie->dbi;
> + pp->msi_irq =3D pcie->msi_irq;
> +
> + if (IS_ENABLED(CONFIG_PCI_MSI)) {
> + ret =3D devm_request_irq(pp->dev, pp->msi_irq,
> + ls_pcie_msi_irq_handler,
> + IRQF_SHARED, "ls-pcie-msi", pp);
> + if (ret) {
> + dev_err(pp->dev, "failed to request msi irq\n");
> + return ret;
> + }
> + }
> +
> + pp->root_bus_nr =3D -1;
> + pp->ops =3D &ls_pcie_host_ops;
> +
> + ret =3D dw_pcie_host_init(pp);
> + if (ret) {
> + dev_err(pp->dev, "failed to initialize host\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int __init ls_pcie_probe(struct platform_device *pdev)
> +{
> + struct ls_pcie *pcie;
> + struct resource *dbi_base;
> + u32 index[2];
> + int ret;
> +
> + pcie =3D devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
> + if (!pcie)
> + return -ENOMEM;
> +
> + pcie->dev =3D &pdev->dev;
> +
> + dbi_base =3D platform_get_resource_byname(pdev, IORESOURCE_MEM, "re=
gs");
> + if (!dbi_base) {
> + dev_err(&pdev->dev, "missing *regs* space\n");
> + return -ENODEV;
> + }
> +
> + pcie->dbi =3D devm_ioremap_resource(&pdev->dev, dbi_base);
> + if (IS_ERR(pcie->dbi))
> + return PTR_ERR(pcie->dbi);
> +
> + pcie->scfg =3D syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
> + "fsl,pcie-scfg");
> + if (IS_ERR(pcie->scfg)) {
> + dev_err(&pdev->dev, "No syscfg phandle specified\n");
> + return PTR_ERR(pcie->scfg);
> + }
> +
> + ret =3D of_property_read_u32_array(pdev->dev.of_node,
> + "fsl,pcie-scfg", index, 2);
> + if (ret)
> + return ret;
> + pcie->index =3D index[1];
> +
> + pcie->msi_irq =3D platform_get_irq_byname(pdev, "msi");
> + if (pcie->msi_irq < 0) {
> + dev_err(&pdev->dev,
> + "failed to get MSI IRQ: %d\n", pcie->msi_irq);
> + return pcie->msi_irq;
> + }
> +
> + ret =3D ls_add_pcie_port(pcie);
> + if (ret < 0)
> + return ret;
> +
> + platform_set_drvdata(pdev, pcie);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id ls_pcie_of_match[] =3D {
> + { .compatible =3D "fsl,ls1021a-pcie" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, ls_pcie_of_match);
> +
> +static struct platform_driver ls_pcie_driver =3D {
> + .driver =3D {
> + .name =3D "layerscape-pcie",
> + .owner =3D THIS_MODULE,
> + .of_match_table =3D ls_pcie_of_match,
> + },
> +};
> +
> +module_platform_driver_probe(ls_pcie_driver, ls_pcie_probe);
> +
> +MODULE_AUTHOR("Minghuan Lian <***@freescale.com>");
> +MODULE_DESCRIPTION("Freescale Layerscape PCIe host controller driver=
");
> +MODULE_LICENSE("GPL v2");
> --=20
> 1.9.1
>=20
>=20
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-***@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

--=20
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora For=
um,
a Linux Foundation Collaborative Project
M***@freescale.com
2014-10-17 07:18:09 UTC
Permalink
Hi Kumar,

Currently the code supports multiple controllers.
LS1021A contains two controllers and both can work well.

Thanks,
Minghuan

________________________________________
发件人: Kumar Gala <***@codeaurora.org>
发送时间: 2014年10月16日 22:41
收件人: Lian Minghuan-B31939
抄送: linux-***@vger.kernel.org; Arnd Bergmann; Hu Mingkai-B21284; Zang Roy-R61911; Bjorn Helgaas; linux-arm-***@lists.infradead.org
主题: Re: [PATCH v6] PCI: Layerscape: Add Layerscape PCIe driver

On Oct 14, 2014, at 12:18 PM, Minghuan Lian <***@freescale.com> wrote:

> Add support for Freescale Layerscape PCIe controller. This driver
> re-uses the designware core code.
>
> Signed-off-by: Minghuan Lian <***@freescale.com>

Do you support multiple controllers or just one? I feel like the DWC PCIe core code needs changes to handle multiple controllers?

> ---
> Change log:
> v6:
> 1. Delete unnecessary if statement in ls_pcie_host_init()
>
> v5:
> 1. Add MAINTAINERS information
> 2. Remove nnnecessary NULL pointer check
> 3. Use dev_dbg instead of dev_err in ls_pcie_msi_irq_handler()
>
> v4:
> 1. Add 'depends on OF' to Kconfig
>
> v3:
> 1. Add prefechable mem region and adjust mem region
> 2. Rename 'pcie-scfg' to 'fsl,pcie-scfg'
> 3. Change MSI interrupt handler
> 4. Use module_platform_driver_probe instead of subsys_initcall
>
> v2:
> 1. Add pcie-scfg to link scfg device node and remove "fsl, ls-pcie" compatible
> 2. Use regmap API to access scfg.
> 3. remove ls1021 PCI device ID.
> 4. remove unused irq pme_irq and ls_pcie_list.
> 5. Do not set scfg bit reverse mode
>
> .../devicetree/bindings/pci/layerscape-pci.txt | 46 ++++
> MAINTAINERS | 10 +
> drivers/pci/host/Kconfig | 8 +
> drivers/pci/host/Makefile | 1 +
> drivers/pci/host/pci-layerscape.c | 269 +++++++++++++++++++++
> 5 files changed, 334 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/pci/layerscape-pci.txt
> create mode 100644 drivers/pci/host/pci-layerscape.c
>
> diff --git a/Documentation/devicetree/bindings/pci/layerscape-pci.txt b/Documentation/devicetree/bindings/pci/layerscape-pci.txt
> new file mode 100644
> index 0000000..d7b0026
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/layerscape-pci.txt
> @@ -0,0 +1,46 @@
> +Freescale Layerscape PCIe controller
> +
> +This PCIe host controller is based on the Synopsis Designware PCIe IP
> +and thus inherits all the common properties defined in designware-pcie.txt.
> +
> +Required properties:
> +- compatible: should contain the platform identifier such as "fsl,ls1021a-pcie"
> +- reg: base addresses and lengths of the PCIe controller
> +- interrupts: A list of interrupt outputs of the controller. Must contain an
> + entry for each entry in the interrupt-names property.
> +- interrupt-names: Must include the following entries:
> + "intr": The interrupt that is asserted for controller interrupts
> + "msi": The interrupt that is asserted when an MSI is received
> + "pme": The interrupt that is asserted when PME state changes
> +- fsl,pcie-scfg: Must include two entries.
> + The first entry must be a link to the SCFG device node
> + The second entry must be '0' or '1' based on physical PCIe controller index.
> + used to get SCFG PEXN registers
> +
> +Example:
> +
> + ***@3400000 {
> + compatible = "fsl,ls1021a-pcie", "snps,dw-pcie";
> + reg = <0x00 0x03400000 0x0 0x00010000 /* controller registers */
> + 0x40 0x00000000 0x0 0x00002000>; /* configuration space */
> + reg-names = "regs", "config";
> + interrupts = <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>, /* controller interrupt */
> + <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH>, /* MSI interrupt */
> + <GIC_SPI 181 IRQ_TYPE_LEVEL_HIGH>; /* PME interrupt */
> + interrupt-names = "intr", "msi", "pme";
> + fsl,pcie-scfg = <&scfg 0>;
> + #address-cells = <3>;
> + #size-cells = <2>;
> + device_type = "pci";
> + num-lanes = <4>;
> + bus-range = <0x0 0xff>;
> + ranges = <0x81000000 0x0 0x00000000 0x40 0x00010000 0x0 0x00010000 /* downstream I/O */
> + 0xc2000000 0x0 0x20000000 0x40 0x20000000 0x0 0x20000000 /* prefetchable memory */
> + 0x82000000 0x0 0x40000000 0x40 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */
> + #interrupt-cells = <1>;
> + interrupt-map-mask = <0 0 0 7>;
> + interrupt-map = <0000 0 0 1 &gic GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>,
> + <0000 0 0 2 &gic GIC_SPI 188 IRQ_TYPE_LEVEL_HIGH>,
> + <0000 0 0 3 &gic GIC_SPI 190 IRQ_TYPE_LEVEL_HIGH>,
> + <0000 0 0 4 &gic GIC_SPI 192 IRQ_TYPE_LEVEL_HIGH>;
> + };
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 1ff06de..b6237ab 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -6936,6 +6936,16 @@ L: linux-***@vger.kernel.org
> S: Maintained
> F: drivers/pci/host/*spear*
>
> +PCIe DRIVER FOR FREESCALE LAYERSCAPE
> +M: Minghuan Lian <***@freescale.com>
> +M: Mingkai Hu <***@freescale.com>
> +M: Roy Zang <tie-***@freescale.com>
> +L: linuxppc-***@lists.ozlabs.org
> +L: linux-***@vger.kernel.org
> +L: linux-arm-***@lists.infradead.org
> +S: Maintained
> +F: drivers/pci/host/pci-layerscape.c
> +
> PCMCIA SUBSYSTEM
> P: Linux PCMCIA Team
> L: linux-***@lists.infradead.org
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index 8922c37..4788fb3 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -63,4 +63,12 @@ config PCIE_SPEAR13XX
> help
> Say Y here if you want PCIe support on SPEAr13XX SoCs.
>
> +config PCI_LAYERSCAPE
> + bool "Freescale Layerscape PCIe controller"
> + depends on OF
> + select PCIE_DW
> + select MFD_SYSCON
> + help
> + Say Y here if you want PCIe controller support on Layerscape SoCs.
> +
> endmenu
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index d0e88f1..54cd9b2 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -8,3 +8,4 @@ obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
> obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) += pcie-rcar.o
> obj-$(CONFIG_PCI_HOST_GENERIC) += pci-host-generic.o
> obj-$(CONFIG_PCIE_SPEAR13XX) += pcie-spear13xx.o
> +obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
> diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pci-layerscape.c
> new file mode 100644
> index 0000000..a459de2
> --- /dev/null
> +++ b/drivers/pci/host/pci-layerscape.c
> @@ -0,0 +1,269 @@
> +/*
> + * PCIe host controller driver for Freescale Layerscape SoCs
> + *
> + * Copyright (C) 2014 Freescale Semiconductor.
> + *
> + * Author: Minghuan Lian <***@freescale.com>
> + *
> + * 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/kernel.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of_pci.h>
> +#include <linux/of_platform.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_address.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/resource.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +#include <linux/bitrev.h>
> +
> +#include "pcie-designware.h"
> +
> +/* PEX1/2 Misc Ports Status Register */
> +#define SCFG_PEXMSCPORTSR(pex_idx) (0x94 + (pex_idx) * 4)
> +#define LTSSM_STATE_SHIFT 20
> +#define LTSSM_STATE_MASK 0x3f
> +#define LTSSM_PCIE_L0 0x11 /* L0 state */
> +
> +/* SCFG MSI register */
> +#define SCFG_SPIMSICR 0x40
> +#define SCFG_SPIMSICLRCR 0x90
> +
> +#define MSI_LS1021A_ADDR 0x1570040

This shouldn’t be hard coded, should be coming from DT.

> +#define MSI_LS1021A_DATA(pex_idx) (0xb3 + pex_idx)
> +
> +/* Symbol Timer Register and Filter Mask Register 1 */
> +#define PCIE_STRFMR1 0x71c
> +
> +struct ls_pcie {
> + struct list_head node;
> + struct device *dev;
> + struct pci_bus *bus;
> + void __iomem *dbi;
> + struct regmap *scfg;
> + struct pcie_port pp;
> + int index;
> + int msi_irq;
> +};
> +
> +#define to_ls_pcie(x) container_of(x, struct ls_pcie, pp)
> +
> +static int ls_pcie_link_up(struct pcie_port *pp)
> +{
> + u32 state;
> + struct ls_pcie *pcie = to_ls_pcie(pp);
> +
> + regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state);
> + state = bitrev32(state);
> + state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK;
> +
> + if (state < LTSSM_PCIE_L0)
> + return 0;
> +
> + return 1;
> +}
> +
> +static u32 ls_pcie_get_msi_addr(struct pcie_port *pp)
> +{
> + return MSI_LS1021A_ADDR;
> +}
> +
> +static u32 ls_pcie_get_msi_data(struct pcie_port *pp, int pos)
> +{
> + struct ls_pcie *pcie = to_ls_pcie(pp);
> +
> + return MSI_LS1021A_DATA(pcie->index);
> +}
> +
> +static irqreturn_t ls_pcie_msi_irq_handler(int irq, void *data)
> +{
> + struct pcie_port *pp = data;
> + struct ls_pcie *pcie = to_ls_pcie(pp);
> + unsigned int msi_irq;
> +
> + /* clear the interrupt */
> + regmap_write(pcie->scfg, SCFG_SPIMSICLRCR,
> + MSI_LS1021A_DATA(pcie->index));
> +
> + msi_irq = irq_find_mapping(pp->irq_domain, 0);
> + if (!msi_irq) {
> + /*
> + * that's weird who triggered this?
> + * just clear it
> + */
> + dev_dbg(pcie->dev, "unexpected MSI\n");
> + return IRQ_NONE;
> + }
> +
> + generic_handle_irq(msi_irq);
> + return IRQ_HANDLED;
> +}
> +
> +static void ls_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
> +{
> +}
> +
> +static void ls_pcie_msi_set_irq(struct pcie_port *pp, int irq)
> +{
> +}
> +
> +static void ls1021a_pcie_msi_fixup(struct pcie_port *pp)
> +{
> + int i;
> +
> + /*
> + * LS1021A has only one MSI interrupt
> + * Set all msi interrupts as used except the first one
> + */
> + for (i = 1; i < MAX_MSI_IRQS; i++)
> + set_bit(i, pp->msi_irq_in_use);
> +}
> +
> +static void ls_pcie_host_init(struct pcie_port *pp)
> +{
> + struct ls_pcie *pcie = to_ls_pcie(pp);
> + int count = 0;
> + u32 val;
> +
> + dw_pcie_setup_rc(pp);
> +
> + while (!ls_pcie_link_up(pp)) {
> + usleep_range(100, 1000);
> + count++;
> + if (count >= 200) {
> + dev_err(pp->dev, "phy link never came up\n");
> + return;
> + }
> + }
> +
> + /*
> + * LS1021A Workaround for internal TKT228622
> + * to fix the INTx hang issue
> + */
> + val = ioread32(pcie->dbi + PCIE_STRFMR1);
> + val &= 0xffff;
> + iowrite32(val, pcie->dbi + PCIE_STRFMR1);

Is this something that will be fixed for production silicon? If so it should probably not exist for upstream code.

> +
> + ls1021a_pcie_msi_fixup(pp);
> +}
> +
> +static struct pcie_host_ops ls_pcie_host_ops = {
> + .link_up = ls_pcie_link_up,
> + .host_init = ls_pcie_host_init,
> + .msi_set_irq = ls_pcie_msi_set_irq,
> + .msi_clear_irq = ls_pcie_msi_clear_irq,
> + .get_msi_addr = ls_pcie_get_msi_addr,
> + .get_msi_data = ls_pcie_get_msi_data,
> +};
> +
> +static int ls_add_pcie_port(struct ls_pcie *pcie)
> +{
> + struct pcie_port *pp;
> + int ret;
> +
> + pp = &pcie->pp;
> + pp->dev = pcie->dev;
> + pp->dbi_base = pcie->dbi;
> + pp->msi_irq = pcie->msi_irq;
> +
> + if (IS_ENABLED(CONFIG_PCI_MSI)) {
> + ret = devm_request_irq(pp->dev, pp->msi_irq,
> + ls_pcie_msi_irq_handler,
> + IRQF_SHARED, "ls-pcie-msi", pp);
> + if (ret) {
> + dev_err(pp->dev, "failed to request msi irq\n");
> + return ret;
> + }
> + }
> +
> + pp->root_bus_nr = -1;
> + pp->ops = &ls_pcie_host_ops;
> +
> + ret = dw_pcie_host_init(pp);
> + if (ret) {
> + dev_err(pp->dev, "failed to initialize host\n");
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int __init ls_pcie_probe(struct platform_device *pdev)
> +{
> + struct ls_pcie *pcie;
> + struct resource *dbi_base;
> + u32 index[2];
> + int ret;
> +
> + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
> + if (!pcie)
> + return -ENOMEM;
> +
> + pcie->dev = &pdev->dev;
> +
> + dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
> + if (!dbi_base) {
> + dev_err(&pdev->dev, "missing *regs* space\n");
> + return -ENODEV;
> + }
> +
> + pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base);
> + if (IS_ERR(pcie->dbi))
> + return PTR_ERR(pcie->dbi);
> +
> + pcie->scfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
> + "fsl,pcie-scfg");
> + if (IS_ERR(pcie->scfg)) {
> + dev_err(&pdev->dev, "No syscfg phandle specified\n");
> + return PTR_ERR(pcie->scfg);
> + }
> +
> + ret = of_property_read_u32_array(pdev->dev.of_node,
> + "fsl,pcie-scfg", index, 2);
> + if (ret)
> + return ret;
> + pcie->index = index[1];
> +
> + pcie->msi_irq = platform_get_irq_byname(pdev, "msi");
> + if (pcie->msi_irq < 0) {
> + dev_err(&pdev->dev,
> + "failed to get MSI IRQ: %d\n", pcie->msi_irq);
> + return pcie->msi_irq;
> + }
> +
> + ret = ls_add_pcie_port(pcie);
> + if (ret < 0)
> + return ret;
> +
> + platform_set_drvdata(pdev, pcie);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id ls_pcie_of_match[] = {
> + { .compatible = "fsl,ls1021a-pcie" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, ls_pcie_of_match);
> +
> +static struct platform_driver ls_pcie_driver = {
> + .driver = {
> + .name = "layerscape-pcie",
> + .owner = THIS_MODULE,
> + .of_match_table = ls_pcie_of_match,
> + },
> +};
> +
> +module_platform_driver_probe(ls_pcie_driver, ls_pcie_probe);
> +
> +MODULE_AUTHOR("Minghuan Lian <***@freescale.com>");
> +MODULE_DESCRIPTION("Freescale Layerscape PCIe host controller driver");
> +MODULE_LICENSE("GPL v2");
> --
> 1.9.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-***@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

--
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
Kumar Gala
2014-10-17 07:43:18 UTC
Permalink
On Oct 17, 2014, at 9:18 AM, ***@freescale.com wrote:

> Hi Kumar,
>=20
> Currently the code supports multiple controllers.
> LS1021A contains two controllers and both can work well.

There are other comments I made for the code. Also, it seems like you =
have locking issues (or lack of locking) around what appears to be a sh=
ared MSI region for both controllers.

>=20
> Thanks,
> Minghuan
>=20
> ________________________________________
> =B7=A2=BC=FE=C8=CB: Kumar Gala <***@codeaurora.org>
> =B7=A2=CB=CD=CA=B1=BC=E4: 2014=C4=EA10=D4=C216=C8=D5 22:41
> =CA=D5=BC=FE=C8=CB: Lian Minghuan-B31939
> =B3=AD=CB=CD: linux-***@vger.kernel.org; Arnd Bergmann; Hu Mingkai-B2=
1284; Zang Roy-R61911; Bjorn Helgaas; linux-arm-***@lists.infradead.=
org
> =D6=F7=CC=E2: Re: [PATCH v6] PCI: Layerscape: Add Layerscape PCIe dri=
ver
>=20
> On Oct 14, 2014, at 12:18 PM, Minghuan Lian <***@freescale.=
com> wrote:
>=20
>> Add support for Freescale Layerscape PCIe controller. This driver
>> re-uses the designware core code.
>>=20
>> Signed-off-by: Minghuan Lian <***@freescale.com>
>=20
> Do you support multiple controllers or just one? I feel like the DWC=
PCIe core code needs changes to handle multiple controllers?
>=20
>> ---
>> Change log:
>> v6:
>> 1. Delete unnecessary if statement in ls_pcie_host_init()
>>=20
>> v5:
>> 1. Add MAINTAINERS information
>> 2. Remove nnnecessary NULL pointer check
>> 3. Use dev_dbg instead of dev_err in ls_pcie_msi_irq_handler()
>>=20
>> v4:
>> 1. Add 'depends on OF' to Kconfig
>>=20
>> v3:
>> 1. Add prefechable mem region and adjust mem region
>> 2. Rename 'pcie-scfg' to 'fsl,pcie-scfg'
>> 3. Change MSI interrupt handler
>> 4. Use module_platform_driver_probe instead of subsys_initcall
>>=20
>> v2:
>> 1. Add pcie-scfg to link scfg device node and remove "fsl, ls-pcie" =
compatible
>> 2. Use regmap API to access scfg.
>> 3. remove ls1021 PCI device ID.
>> 4. remove unused irq pme_irq and ls_pcie_list.
>> 5. Do not set scfg bit reverse mode
>>=20
>> .../devicetree/bindings/pci/layerscape-pci.txt | 46 ++++
>> MAINTAINERS | 10 +
>> drivers/pci/host/Kconfig | 8 +
>> drivers/pci/host/Makefile | 1 +
>> drivers/pci/host/pci-layerscape.c | 269 +++++++++++=
++++++++++
>> 5 files changed, 334 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/pci/layerscape-=
pci.txt
>> create mode 100644 drivers/pci/host/pci-layerscape.c
>>=20
>> diff --git a/Documentation/devicetree/bindings/pci/layerscape-pci.tx=
t b/Documentation/devicetree/bindings/pci/layerscape-pci.txt
>> new file mode 100644
>> index 0000000..d7b0026
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/pci/layerscape-pci.txt
>> @@ -0,0 +1,46 @@
>> +Freescale Layerscape PCIe controller
>> +
>> +This PCIe host controller is based on the Synopsis Designware PCIe =
IP
>> +and thus inherits all the common properties defined in designware-p=
cie.txt.
>> +
>> +Required properties:
>> +- compatible: should contain the platform identifier such as "fsl,l=
s1021a-pcie"
>> +- reg: base addresses and lengths of the PCIe controller
>> +- interrupts: A list of interrupt outputs of the controller. Must c=
ontain an
>> + entry for each entry in the interrupt-names property.
>> +- interrupt-names: Must include the following entries:
>> + "intr": The interrupt that is asserted for controller interrupts
>> + "msi": The interrupt that is asserted when an MSI is received
>> + "pme": The interrupt that is asserted when PME state changes
>> +- fsl,pcie-scfg: Must include two entries.
>> + The first entry must be a link to the SCFG device node
>> + The second entry must be '0' or '1' based on physical PCIe contro=
ller index.
>> + used to get SCFG PEXN registers
>> +
>> +Example:
>> +
>> + ***@3400000 {
>> + compatible =3D "fsl,ls1021a-pcie", "snps,dw-pcie";
>> + reg =3D <0x00 0x03400000 0x0 0x00010000 /* controlle=
r registers */
>> + 0x40 0x00000000 0x0 0x00002000>; /* configurati=
on space */
>> + reg-names =3D "regs", "config";
>> + interrupts =3D <GIC_SPI 177 IRQ_TYPE_LEVEL_HIGH>, /* c=
ontroller interrupt */
>> + <GIC_SPI 179 IRQ_TYPE_LEVEL_HIGH>, /* MSI=
interrupt */
>> + <GIC_SPI 181 IRQ_TYPE_LEVEL_HIGH>; /* PME=
interrupt */
>> + interrupt-names =3D "intr", "msi", "pme";
>> + fsl,pcie-scfg =3D <&scfg 0>;
>> + #address-cells =3D <3>;
>> + #size-cells =3D <2>;
>> + device_type =3D "pci";
>> + num-lanes =3D <4>;
>> + bus-range =3D <0x0 0xff>;
>> + ranges =3D <0x81000000 0x0 0x00000000 0x40 0x00010000 =
0x0 0x00010000 /* downstream I/O */
>> + 0xc2000000 0x0 0x20000000 0x40 0x20000000 0x=
0 0x20000000 /* prefetchable memory */
>> + 0x82000000 0x0 0x40000000 0x40 0x40000000 0x=
0 0x40000000>; /* non-prefetchable memory */
>> + #interrupt-cells =3D <1>;
>> + interrupt-map-mask =3D <0 0 0 7>;
>> + interrupt-map =3D <0000 0 0 1 &gic GIC_SPI 91 IRQ_TYP=
E_LEVEL_HIGH>,
>> + <0000 0 0 2 &gic GIC_SPI 188 IRQ_TYPE_=
LEVEL_HIGH>,
>> + <0000 0 0 3 &gic GIC_SPI 190 IRQ_TYPE_=
LEVEL_HIGH>,
>> + <0000 0 0 4 &gic GIC_SPI 192 IRQ_TYPE_=
LEVEL_HIGH>;
>> + };
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 1ff06de..b6237ab 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -6936,6 +6936,16 @@ L: linux-***@vger.kernel.org
>> S: Maintained
>> F: drivers/pci/host/*spear*
>>=20
>> +PCIe DRIVER FOR FREESCALE LAYERSCAPE
>> +M: Minghuan Lian <***@freescale.com>
>> +M: Mingkai Hu <***@freescale.com>
>> +M: Roy Zang <tie-***@freescale.com>
>> +L: linuxppc-***@lists.ozlabs.org
>> +L: linux-***@vger.kernel.org
>> +L: linux-arm-***@lists.infradead.org
>> +S: Maintained
>> +F: drivers/pci/host/pci-layerscape.c
>> +
>> PCMCIA SUBSYSTEM
>> P: Linux PCMCIA Team
>> L: linux-***@lists.infradead.org
>> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
>> index 8922c37..4788fb3 100644
>> --- a/drivers/pci/host/Kconfig
>> +++ b/drivers/pci/host/Kconfig
>> @@ -63,4 +63,12 @@ config PCIE_SPEAR13XX
>> help
>> Say Y here if you want PCIe support on SPEAr13XX SoCs.
>>=20
>> +config PCI_LAYERSCAPE
>> + bool "Freescale Layerscape PCIe controller"
>> + depends on OF
>> + select PCIE_DW
>> + select MFD_SYSCON
>> + help
>> + Say Y here if you want PCIe controller support on Layerscape=
SoCs.
>> +
>> endmenu
>> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
>> index d0e88f1..54cd9b2 100644
>> --- a/drivers/pci/host/Makefile
>> +++ b/drivers/pci/host/Makefile
>> @@ -8,3 +8,4 @@ obj-$(CONFIG_PCI_RCAR_GEN2) +=3D pci-rcar-gen2.o
>> obj-$(CONFIG_PCI_RCAR_GEN2_PCIE) +=3D pcie-rcar.o
>> obj-$(CONFIG_PCI_HOST_GENERIC) +=3D pci-host-generic.o
>> obj-$(CONFIG_PCIE_SPEAR13XX) +=3D pcie-spear13xx.o
>> +obj-$(CONFIG_PCI_LAYERSCAPE) +=3D pci-layerscape.o
>> diff --git a/drivers/pci/host/pci-layerscape.c b/drivers/pci/host/pc=
i-layerscape.c
>> new file mode 100644
>> index 0000000..a459de2
>> --- /dev/null
>> +++ b/drivers/pci/host/pci-layerscape.c
>> @@ -0,0 +1,269 @@
>> +/*
>> + * PCIe host controller driver for Freescale Layerscape SoCs
>> + *
>> + * Copyright (C) 2014 Freescale Semiconductor.
>> + *
>> + * Author: Minghuan Lian <***@freescale.com>
>> + *
>> + * This program is free software; you can redistribute it and/or mo=
dify
>> + * it under the terms of the GNU General Public License version 2 a=
s
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/delay.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/of_pci.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/of_irq.h>
>> +#include <linux/of_address.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/resource.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/regmap.h>
>> +#include <linux/bitrev.h>
>> +
>> +#include "pcie-designware.h"
>> +
>> +/* PEX1/2 Misc Ports Status Register */
>> +#define SCFG_PEXMSCPORTSR(pex_idx) (0x94 + (pex_idx) * 4)
>> +#define LTSSM_STATE_SHIFT 20
>> +#define LTSSM_STATE_MASK 0x3f
>> +#define LTSSM_PCIE_L0 0x11 /* L0 state */
>> +
>> +/* SCFG MSI register */
>> +#define SCFG_SPIMSICR 0x40
>> +#define SCFG_SPIMSICLRCR 0x90
>> +
>> +#define MSI_LS1021A_ADDR 0x1570040
>=20
> This shouldn=A1=AFt be hard coded, should be coming from DT.
>=20
>> +#define MSI_LS1021A_DATA(pex_idx) (0xb3 + pex_idx)
>> +
>> +/* Symbol Timer Register and Filter Mask Register 1 */
>> +#define PCIE_STRFMR1 0x71c
>> +
>> +struct ls_pcie {
>> + struct list_head node;
>> + struct device *dev;
>> + struct pci_bus *bus;
>> + void __iomem *dbi;
>> + struct regmap *scfg;
>> + struct pcie_port pp;
>> + int index;
>> + int msi_irq;
>> +};
>> +
>> +#define to_ls_pcie(x) container_of(x, struct ls_pcie, pp)
>> +
>> +static int ls_pcie_link_up(struct pcie_port *pp)
>> +{
>> + u32 state;
>> + struct ls_pcie *pcie =3D to_ls_pcie(pp);
>> +
>> + regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state=
);
>> + state =3D bitrev32(state);
>> + state =3D (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK;
>> +
>> + if (state < LTSSM_PCIE_L0)
>> + return 0;
>> +
>> + return 1;
>> +}
>> +
>> +static u32 ls_pcie_get_msi_addr(struct pcie_port *pp)
>> +{
>> + return MSI_LS1021A_ADDR;
>> +}
>> +
>> +static u32 ls_pcie_get_msi_data(struct pcie_port *pp, int pos)
>> +{
>> + struct ls_pcie *pcie =3D to_ls_pcie(pp);
>> +
>> + return MSI_LS1021A_DATA(pcie->index);
>> +}
>> +
>> +static irqreturn_t ls_pcie_msi_irq_handler(int irq, void *data)
>> +{
>> + struct pcie_port *pp =3D data;
>> + struct ls_pcie *pcie =3D to_ls_pcie(pp);
>> + unsigned int msi_irq;
>> +
>> + /* clear the interrupt */
>> + regmap_write(pcie->scfg, SCFG_SPIMSICLRCR,
>> + MSI_LS1021A_DATA(pcie->index));
>> +
>> + msi_irq =3D irq_find_mapping(pp->irq_domain, 0);
>> + if (!msi_irq) {
>> + /*
>> + * that's weird who triggered this?
>> + * just clear it
>> + */
>> + dev_dbg(pcie->dev, "unexpected MSI\n");
>> + return IRQ_NONE;
>> + }
>> +
>> + generic_handle_irq(msi_irq);
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static void ls_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
>> +{
>> +}
>> +
>> +static void ls_pcie_msi_set_irq(struct pcie_port *pp, int irq)
>> +{
>> +}
>> +
>> +static void ls1021a_pcie_msi_fixup(struct pcie_port *pp)
>> +{
>> + int i;
>> +
>> + /*
>> + * LS1021A has only one MSI interrupt
>> + * Set all msi interrupts as used except the first one
>> + */
>> + for (i =3D 1; i < MAX_MSI_IRQS; i++)
>> + set_bit(i, pp->msi_irq_in_use);
>> +}
>> +
>> +static void ls_pcie_host_init(struct pcie_port *pp)
>> +{
>> + struct ls_pcie *pcie =3D to_ls_pcie(pp);
>> + int count =3D 0;
>> + u32 val;
>> +
>> + dw_pcie_setup_rc(pp);
>> +
>> + while (!ls_pcie_link_up(pp)) {
>> + usleep_range(100, 1000);
>> + count++;
>> + if (count >=3D 200) {
>> + dev_err(pp->dev, "phy link never came up\n");
>> + return;
>> + }
>> + }
>> +
>> + /*
>> + * LS1021A Workaround for internal TKT228622
>> + * to fix the INTx hang issue
>> + */
>> + val =3D ioread32(pcie->dbi + PCIE_STRFMR1);
>> + val &=3D 0xffff;
>> + iowrite32(val, pcie->dbi + PCIE_STRFMR1);
>=20
> Is this something that will be fixed for production silicon? If so i=
t should probably not exist for upstream code.
>=20
>> +
>> + ls1021a_pcie_msi_fixup(pp);
>> +}
>> +
>> +static struct pcie_host_ops ls_pcie_host_ops =3D {
>> + .link_up =3D ls_pcie_link_up,
>> + .host_init =3D ls_pcie_host_init,
>> + .msi_set_irq =3D ls_pcie_msi_set_irq,
>> + .msi_clear_irq =3D ls_pcie_msi_clear_irq,
>> + .get_msi_addr =3D ls_pcie_get_msi_addr,
>> + .get_msi_data =3D ls_pcie_get_msi_data,
>> +};
>> +
>> +static int ls_add_pcie_port(struct ls_pcie *pcie)
>> +{
>> + struct pcie_port *pp;
>> + int ret;
>> +
>> + pp =3D &pcie->pp;
>> + pp->dev =3D pcie->dev;
>> + pp->dbi_base =3D pcie->dbi;
>> + pp->msi_irq =3D pcie->msi_irq;
>> +
>> + if (IS_ENABLED(CONFIG_PCI_MSI)) {
>> + ret =3D devm_request_irq(pp->dev, pp->msi_irq,
>> + ls_pcie_msi_irq_handler,
>> + IRQF_SHARED, "ls-pcie-msi", pp=
);
>> + if (ret) {
>> + dev_err(pp->dev, "failed to request msi irq\n"=
);
>> + return ret;
>> + }
>> + }
>> +
>> + pp->root_bus_nr =3D -1;
>> + pp->ops =3D &ls_pcie_host_ops;
>> +
>> + ret =3D dw_pcie_host_init(pp);
>> + if (ret) {
>> + dev_err(pp->dev, "failed to initialize host\n");
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int __init ls_pcie_probe(struct platform_device *pdev)
>> +{
>> + struct ls_pcie *pcie;
>> + struct resource *dbi_base;
>> + u32 index[2];
>> + int ret;
>> +
>> + pcie =3D devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
>> + if (!pcie)
>> + return -ENOMEM;
>> +
>> + pcie->dev =3D &pdev->dev;
>> +
>> + dbi_base =3D platform_get_resource_byname(pdev, IORESOURCE_MEM=
, "regs");
>> + if (!dbi_base) {
>> + dev_err(&pdev->dev, "missing *regs* space\n");
>> + return -ENODEV;
>> + }
>> +
>> + pcie->dbi =3D devm_ioremap_resource(&pdev->dev, dbi_base);
>> + if (IS_ERR(pcie->dbi))
>> + return PTR_ERR(pcie->dbi);
>> +
>> + pcie->scfg =3D syscon_regmap_lookup_by_phandle(pdev->dev.of_no=
de,
>> + "fsl,pcie-scfg");
>> + if (IS_ERR(pcie->scfg)) {
>> + dev_err(&pdev->dev, "No syscfg phandle specified\n");
>> + return PTR_ERR(pcie->scfg);
>> + }
>> +
>> + ret =3D of_property_read_u32_array(pdev->dev.of_node,
>> + "fsl,pcie-scfg", index, 2);
>> + if (ret)
>> + return ret;
>> + pcie->index =3D index[1];
>> +
>> + pcie->msi_irq =3D platform_get_irq_byname(pdev, "msi");
>> + if (pcie->msi_irq < 0) {
>> + dev_err(&pdev->dev,
>> + "failed to get MSI IRQ: %d\n", pcie->msi_irq);
>> + return pcie->msi_irq;
>> + }
>> +
>> + ret =3D ls_add_pcie_port(pcie);
>> + if (ret < 0)
>> + return ret;
>> +
>> + platform_set_drvdata(pdev, pcie);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct of_device_id ls_pcie_of_match[] =3D {
>> + { .compatible =3D "fsl,ls1021a-pcie" },
>> + { },
>> +};
>> +MODULE_DEVICE_TABLE(of, ls_pcie_of_match);
>> +
>> +static struct platform_driver ls_pcie_driver =3D {
>> + .driver =3D {
>> + .name =3D "layerscape-pcie",
>> + .owner =3D THIS_MODULE,
>> + .of_match_table =3D ls_pcie_of_match,
>> + },
>> +};
>> +
>> +module_platform_driver_probe(ls_pcie_driver, ls_pcie_probe);
>> +
>> +MODULE_AUTHOR("Minghuan Lian <***@freescale.com>");
>> +MODULE_DESCRIPTION("Freescale Layerscape PCIe host controller drive=
r");
>> +MODULE_LICENSE("GPL v2");
>> --
>> 1.9.1
>>=20
>>=20
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-***@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>=20
> --
> Qualcomm Innovation Center, Inc.
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora F=
orum,
> a Linux Foundation Collaborative Project
>=20
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-***@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

--=20
Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora For=
um,
a Linux Foundation Collaborative Project
Loading...