+Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala On Tuesday 08 July 2014 06:04 PM, Kishon Vijay Abraham I wrote: > Hi Arnd, Tony, other dt guys, > > On Wednesday 25 June 2014 11:26 PM, Kishon Vijay Abraham I wrote: >> Added support for pcie controller in dra7xx. This driver re-uses >> the designware core code that is already present in kernel. >> > > Are you okay with this patch? an you give your Acked-by? > > Thanks > Kishon >> Cc: Jason Gunthorpe <jgunthorpe@xxxxxxxxxxxxxxxxxxxx> >> Cc: Bjorn Helgaas <bhelgaas@xxxxxxxxxx> >> Cc: Mohit Kumar <mohit.kumar@xxxxxx> >> Cc: Jingoo Han <jg1.han@xxxxxxxxxxx> >> Cc: Marek Vasut <marex@xxxxxxx> >> Cc: Arnd Bergmann <arnd@xxxxxxxx> >> Signed-off-by: Kishon Vijay Abraham I <kishon@xxxxxx> >> --- >> Documentation/devicetree/bindings/pci/ti-pci.txt | 59 +++ >> drivers/pci/host/Kconfig | 10 + >> drivers/pci/host/Makefile | 1 + >> drivers/pci/host/pci-dra7xx.c | 458 ++++++++++++++++++++++ >> 4 files changed, 528 insertions(+) >> create mode 100644 Documentation/devicetree/bindings/pci/ti-pci.txt >> create mode 100644 drivers/pci/host/pci-dra7xx.c >> >> diff --git a/Documentation/devicetree/bindings/pci/ti-pci.txt b/Documentation/devicetree/bindings/pci/ti-pci.txt >> new file mode 100644 >> index 0000000..3d21791 >> --- /dev/null >> +++ b/Documentation/devicetree/bindings/pci/ti-pci.txt >> @@ -0,0 +1,59 @@ >> +TI PCI Controllers >> + >> +PCIe Designware Controller >> + - compatible: Should be "ti,dra7-pcie"" >> + - reg : Two register ranges as listed in the reg-names property >> + - reg-names : The first entry must be "ti-conf" for the TI specific registers >> + The second entry must be "rc-dbics" for the designware pcie >> + registers >> + The third entry must be "config" for the PCIe configuration space >> + - phys : list of PHY specifiers (used by generic PHY framework) >> + - phy-names : must be "pcie-phy0", "pcie-phy1", "pcie-phyN".. based on the >> + number of PHYs as specified in *phys* property. >> + - ti,hwmods : Name of the hwmod associated to the pcie, "pcie<X>", >> + where <X> is the instance number of the pcie from the HW spec. >> + - interrupts : Two interrupt entries must be specified. The first one is for >> + main interrupt line and the second for MSI interrupt line. >> + - #address-cells, >> + #size-cells, >> + #interrupt-cells, >> + device_type, >> + ranges, >> + num-lanes, >> + interrupt-map-mask, >> + interrupt-map : as specified in ../designware-pcie.txt >> + >> +Example: >> +axi { >> + compatible = "simple-bus"; >> + #size-cells = <1>; >> + #address-cells = <1>; >> + ranges = <0x51000000 0x51000000 0x3000 >> + 0x0 0x20000000 0x10000000>; >> + pcie@51000000 { >> + compatible = "ti,dra7-pcie"; >> + reg = <0x51000000 0x2000>, <0x51002000 0x14c>, <0x1000 0x2000>; >> + reg-names = "rc_dbics", "ti_conf", "config"; >> + interrupts = <0 232 0x4>, <0 233 0x4>; >> + #address-cells = <3>; >> + #size-cells = <2>; >> + device_type = "pci"; >> + ranges = <0x81000000 0 0 0x03000 0 0x00010000 >> + 0x82000000 0 0x20013000 0x13000 0 0xffed000>; >> + #interrupt-cells = <1>; >> + num-lanes = <1>; >> + ti,hwmods = "pcie1"; >> + phys = <&pcie1_phy>; >> + phy-names = "pcie-phy0"; >> + interrupt-map-mask = <0 0 0 7>; >> + interrupt-map = <0 0 0 1 &pcie_intc 1>, >> + <0 0 0 2 &pcie_intc 2>, >> + <0 0 0 3 &pcie_intc 3>, >> + <0 0 0 4 &pcie_intc 4>; >> + pcie_intc: interrupt-controller { >> + interrupt-controller; >> + #address-cells = <0>; >> + #interrupt-cells = <1>; >> + }; >> + }; >> +}; >> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig >> index 21df477..22117b0 100644 >> --- a/drivers/pci/host/Kconfig >> +++ b/drivers/pci/host/Kconfig >> @@ -1,6 +1,16 @@ >> menu "PCI host controller drivers" >> depends on PCI >> >> +config PCI_DRA7XX >> + bool "TI DRA7xx PCIe controller" >> + select PCIE_DW >> + depends on OF && HAS_IOMEM && TI_PIPE3 >> + help >> + Enables support for the PCIE controller present in DRA7xx SoC. There >> + are two instances of PCIE controller in DRA7xx. This controller can >> + act both as EP and RC. This reuses the same Designware core as used >> + by other SoCs. >> + >> config PCI_MVEBU >> bool "Marvell EBU PCIe controller" >> depends on ARCH_MVEBU || ARCH_DOVE || ARCH_KIRKWOOD >> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile >> index 611ba4b..c42844d 100644 >> --- a/drivers/pci/host/Makefile >> +++ b/drivers/pci/host/Makefile >> @@ -1,4 +1,5 @@ >> obj-$(CONFIG_PCIE_DW) += pcie-designware.o >> +obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o >> obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o >> obj-$(CONFIG_PCI_IMX6) += pci-imx6.o >> obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o >> diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/host/pci-dra7xx.c >> new file mode 100644 >> index 0000000..52b34fe >> --- /dev/null >> +++ b/drivers/pci/host/pci-dra7xx.c >> @@ -0,0 +1,458 @@ >> +/* >> + * pcie-dra7xx - PCIe controller driver for TI DRA7xx SoCs >> + * >> + * Copyright (C) 2013-2014 Texas Instruments Incorporated - http://www.ti.com >> + * >> + * Authors: Kishon Vijay Abraham I <kishon@xxxxxx> >> + * >> + * 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/delay.h> >> +#include <linux/err.h> >> +#include <linux/interrupt.h> >> +#include <linux/irq.h> >> +#include <linux/irqdomain.h> >> +#include <linux/kernel.h> >> +#include <linux/module.h> >> +#include <linux/pci.h> >> +#include <linux/phy/phy.h> >> +#include <linux/platform_device.h> >> +#include <linux/pm_runtime.h> >> +#include <linux/resource.h> >> +#include <linux/types.h> >> + >> +#include "pcie-designware.h" >> + >> +/* PCIe controller wrapper DRA7XX configuration registers */ >> + >> +#define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN 0x0024 >> +#define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN 0x0028 >> +#define ERR_SYS BIT(0) >> +#define ERR_FATAL BIT(1) >> +#define ERR_NONFATAL BIT(2) >> +#define ERR_COR BIT(3) >> +#define ERR_AXI BIT(4) >> +#define ERR_ECRC BIT(5) >> +#define PME_TURN_OFF BIT(8) >> +#define PME_TO_ACK BIT(9) >> +#define PM_PME BIT(10) >> +#define LINK_REQ_RST BIT(11) >> +#define LINK_UP_EVT BIT(12) >> +#define CFG_BME_EVT BIT(13) >> +#define CFG_MSE_EVT BIT(14) >> +#define INTERRUPTS (ERR_SYS | ERR_FATAL | ERR_NONFATAL | ERR_COR | ERR_AXI | \ >> + ERR_ECRC | PME_TURN_OFF | PME_TO_ACK | PM_PME | \ >> + LINK_REQ_RST | LINK_UP_EVT | CFG_BME_EVT | CFG_MSE_EVT) >> + >> +#define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI 0x0034 >> +#define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI 0x0038 >> +#define INTA BIT(0) >> +#define INTB BIT(1) >> +#define INTC BIT(2) >> +#define INTD BIT(3) >> +#define MSI BIT(4) >> +#define LEG_EP_INTERRUPTS (INTA | INTB | INTC | INTD) >> + >> +#define PCIECTRL_DRA7XX_CONF_DEVICE_CMD 0x0104 >> +#define LTSSM_EN 0x1 >> + >> +#define PCIECTRL_DRA7XX_CONF_PHY_CS 0x010C >> +#define LINK_UP BIT(16) >> + >> +struct dra7xx_pcie { >> + void __iomem *base; >> + struct phy **phy; >> + int phy_count; >> + struct device *dev; >> + struct pcie_port pp; >> +}; >> + >> +#define to_dra7xx_pcie(x) container_of((x), struct dra7xx_pcie, pp) >> + >> +static inline u32 dra7xx_pcie_readl(struct dra7xx_pcie *pcie, u32 offset) >> +{ >> + return readl(pcie->base + offset); >> +} >> + >> +static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset, >> + u32 value) >> +{ >> + writel(value, pcie->base + offset); >> +} >> + >> +static int dra7xx_pcie_link_up(struct pcie_port *pp) >> +{ >> + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); >> + u32 reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS); >> + >> + return !!(reg & LINK_UP); >> +} >> + >> +static int dra7xx_pcie_establish_link(struct pcie_port *pp) >> +{ >> + u32 reg; >> + unsigned int retries = 1000; >> + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); >> + >> + if (dw_pcie_link_up(pp)) { >> + dev_err(pp->dev, "link is already up\n"); >> + return 0; >> + } >> + >> + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); >> + reg |= LTSSM_EN; >> + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); >> + >> + while (retries--) { >> + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS); >> + if (reg & LINK_UP) >> + break; >> + usleep_range(10, 20); >> + } >> + >> + if (retries == 0) { >> + dev_err(pp->dev, "link is not up\n"); >> + return -ETIMEDOUT; >> + } >> + >> + return 0; >> +} >> + >> +static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp) >> +{ >> + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); >> + >> + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, >> + ~INTERRUPTS); >> + dra7xx_pcie_writel(dra7xx, >> + PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN, INTERRUPTS); >> + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI, >> + ~LEG_EP_INTERRUPTS & ~MSI); >> + >> + if (IS_ENABLED(CONFIG_PCI_MSI)) >> + dra7xx_pcie_writel(dra7xx, >> + PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI, MSI); >> + else >> + dra7xx_pcie_writel(dra7xx, >> + PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI, >> + LEG_EP_INTERRUPTS); >> +} >> + >> +static void dra7xx_pcie_host_init(struct pcie_port *pp) >> +{ >> + dw_pcie_setup_rc(pp); >> + dra7xx_pcie_establish_link(pp); >> + if (IS_ENABLED(CONFIG_PCI_MSI)) >> + dw_pcie_msi_init(pp); >> + dra7xx_pcie_enable_interrupts(pp); >> +} >> + >> +static struct pcie_host_ops dra7xx_pcie_host_ops = { >> + .link_up = dra7xx_pcie_link_up, >> + .host_init = dra7xx_pcie_host_init, >> +}; >> + >> +static int dra7xx_pcie_intx_map(struct irq_domain *domain, unsigned int irq, >> + irq_hw_number_t hwirq) >> +{ >> + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq); >> + irq_set_chip_data(irq, domain->host_data); >> + set_irq_flags(irq, IRQF_VALID); >> + >> + return 0; >> +} >> + >> +static const struct irq_domain_ops intx_domain_ops = { >> + .map = dra7xx_pcie_intx_map, >> +}; >> + >> +static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp) >> +{ >> + struct device *dev = pp->dev; >> + struct device_node *node = dev->of_node; >> + struct device_node *pcie_intc_node = of_get_next_child(node, NULL); >> + >> + if (!pcie_intc_node) { >> + dev_err(dev, "No PCIe Intc node found\n"); >> + return PTR_ERR(pcie_intc_node); >> + } >> + >> + pp->irq_domain = irq_domain_add_linear(pcie_intc_node, 4, >> + &intx_domain_ops, pp); >> + if (!pp->irq_domain) { >> + dev_err(dev, "Failed to get a INTx IRQ domain\n"); >> + return PTR_ERR(pp->irq_domain); >> + } >> + >> + return 0; >> +} >> + >> +static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg) >> +{ >> + struct pcie_port *pp = arg; >> + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp); >> + u32 reg; >> + >> + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI); >> + >> + switch (reg) { >> + case MSI: >> + dw_handle_msi_irq(pp); >> + break; >> + case INTA: >> + case INTB: >> + case INTC: >> + case INTD: >> + generic_handle_irq(irq_find_mapping(pp->irq_domain, ffs(reg))); >> + break; >> + } >> + >> + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI, reg); >> + >> + return IRQ_HANDLED; >> +} >> + >> + >> +static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg) >> +{ >> + struct dra7xx_pcie *dra7xx = arg; >> + u32 reg; >> + >> + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN); >> + >> + if (reg & ERR_SYS) >> + dev_dbg(dra7xx->dev, "System Error\n"); >> + >> + if (reg & ERR_FATAL) >> + dev_dbg(dra7xx->dev, "Fatal Error\n"); >> + >> + if (reg & ERR_NONFATAL) >> + dev_dbg(dra7xx->dev, "Non Fatal Error\n"); >> + >> + if (reg & ERR_COR) >> + dev_dbg(dra7xx->dev, "Correctable Error\n"); >> + >> + if (reg & ERR_AXI) >> + dev_dbg(dra7xx->dev, "AXI tag lookup fatal Error\n"); >> + >> + if (reg & ERR_ECRC) >> + dev_dbg(dra7xx->dev, "ECRC Error\n"); >> + >> + if (reg & PME_TURN_OFF) >> + dev_dbg(dra7xx->dev, >> + "Power Management Event Turn-Off message received\n"); >> + >> + if (reg & PME_TO_ACK) >> + dev_dbg(dra7xx->dev, >> + "Power Management Turn-Off Ack message received\n"); >> + >> + if (reg & PM_PME) >> + dev_dbg(dra7xx->dev, >> + "PM Power Management Event message received\n"); >> + >> + if (reg & LINK_REQ_RST) >> + dev_dbg(dra7xx->dev, "Link Request Reset\n"); >> + >> + if (reg & LINK_UP_EVT) >> + dev_dbg(dra7xx->dev, "Link-up state change\n"); >> + >> + if (reg & CFG_BME_EVT) >> + dev_dbg(dra7xx->dev, "CFG 'Bus Master Enable' change\n"); >> + >> + if (reg & CFG_MSE_EVT) >> + dev_dbg(dra7xx->dev, "CFG 'Memory Space Enable' change\n"); >> + >> + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, reg); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int add_pcie_port(struct dra7xx_pcie *dra7xx, >> + struct platform_device *pdev) >> +{ >> + int ret; >> + struct pcie_port *pp; >> + struct resource *res; >> + struct device *dev = &pdev->dev; >> + >> + pp = &dra7xx->pp; >> + pp->dev = dev; >> + pp->ops = &dra7xx_pcie_host_ops; >> + >> + pp->irq = platform_get_irq(pdev, 1); >> + if (pp->irq < 0) { >> + dev_err(dev, "missing IRQ resource\n"); >> + return -EINVAL; >> + } >> + >> + ret = devm_request_irq(&pdev->dev, pp->irq, >> + dra7xx_pcie_msi_irq_handler, IRQF_SHARED, >> + "dra7-pcie-msi", pp); >> + if (ret) { >> + dev_err(&pdev->dev, "failed to request irq\n"); >> + return ret; >> + } >> + >> + if (!IS_ENABLED(CONFIG_PCI_MSI)) { >> + ret = dra7xx_pcie_init_irq_domain(pp); >> + if (ret < 0) >> + return ret; >> + } >> + >> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbics"); >> + pp->dbi_base = devm_ioremap(dev, res->start, resource_size(res)); >> + if (!pp->dbi_base) >> + return -ENOMEM; >> + >> + ret = dw_pcie_host_init(pp); >> + if (ret) { >> + dev_err(dra7xx->dev, "failed to initialize host\n"); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> +static int __init dra7xx_pcie_probe(struct platform_device *pdev) >> +{ >> + u32 reg; >> + int ret; >> + int irq; >> + int i; >> + int phy_count; >> + struct phy **phy; >> + void __iomem *base; >> + struct resource *res; >> + struct dra7xx_pcie *dra7xx; >> + struct device *dev = &pdev->dev; >> + struct device_node *np = dev->of_node; >> + char name[10]; >> + >> + dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL); >> + if (!dra7xx) >> + return -ENOMEM; >> + >> + irq = platform_get_irq(pdev, 0); >> + if (irq < 0) { >> + dev_err(dev, "missing IRQ resource\n"); >> + return -EINVAL; >> + } >> + >> + ret = devm_request_irq(dev, irq, dra7xx_pcie_irq_handler, >> + IRQF_SHARED, "dra7xx-pcie-main", dra7xx); >> + if (ret) { >> + dev_err(dev, "failed to request irq\n"); >> + return ret; >> + } >> + >> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ti_conf"); >> + base = devm_ioremap_nocache(dev, res->start, resource_size(res)); >> + if (!base) >> + return -ENOMEM; >> + >> + phy_count = of_property_count_strings(np, "phy-names"); >> + if (phy_count < 0) { >> + dev_err(dev, "unable to find the strings\n"); >> + return phy_count; >> + } >> + >> + phy = devm_kzalloc(dev, sizeof(*phy) * phy_count, GFP_KERNEL); >> + if (!phy) >> + return -ENOMEM; >> + >> + for (i = 0; i < phy_count; i++) { >> + snprintf(name, sizeof(name), "pcie-phy%d", i); >> + phy[i] = devm_phy_get(dev, name); >> + if (IS_ERR(phy[i])) >> + return PTR_ERR(phy[i]); >> + >> + ret = phy_init(phy[i]); >> + if (ret < 0) >> + goto err_phy; >> + >> + ret = phy_power_on(phy[i]); >> + if (ret < 0) { >> + phy_exit(phy[i]); >> + goto err_phy; >> + } >> + } >> + >> + dra7xx->base = base; >> + dra7xx->phy = phy; >> + dra7xx->dev = dev; >> + dra7xx->phy_count = phy_count; >> + >> + pm_runtime_enable(dev); >> + ret = pm_runtime_get_sync(dev); >> + if (IS_ERR_VALUE(ret)) { >> + dev_err(dev, "pm_runtime_get_sync failed\n"); >> + goto err_phy; >> + } >> + >> + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD); >> + reg &= ~LTSSM_EN; >> + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg); >> + >> + platform_set_drvdata(pdev, dra7xx); >> + >> + ret = add_pcie_port(dra7xx, pdev); >> + if (ret < 0) >> + goto err_add_port; >> + >> + return 0; >> + >> +err_add_port: >> + pm_runtime_put(dev); >> + pm_runtime_disable(dev); >> + >> +err_phy: >> + while (--i >= 0) { >> + phy_power_off(phy[i]); >> + phy_exit(phy[i]); >> + } >> + >> + return ret; >> +} >> + >> +static int __exit dra7xx_pcie_remove(struct platform_device *pdev) >> +{ >> + struct dra7xx_pcie *dra7xx = platform_get_drvdata(pdev); >> + struct pcie_port *pp = &dra7xx->pp; >> + struct device *dev = &pdev->dev; >> + int count = dra7xx->phy_count; >> + >> + if (pp->irq_domain) >> + irq_domain_remove(pp->irq_domain); >> + pm_runtime_put(dev); >> + pm_runtime_disable(dev); >> + while (count--) { >> + phy_power_off(dra7xx->phy[count]); >> + phy_exit(dra7xx->phy[count]); >> + } >> + >> + return 0; >> +} >> + >> +static const struct of_device_id of_dra7xx_pcie_match[] = { >> + { .compatible = "ti,dra7-pcie", }, >> + {}, >> +}; >> +MODULE_DEVICE_TABLE(of, of_dra7xx_pcie_match); >> + >> +static struct platform_driver dra7xx_pcie_driver = { >> + .remove = __exit_p(dra7xx_pcie_remove), >> + .driver = { >> + .name = "dra7-pcie", >> + .owner = THIS_MODULE, >> + .of_match_table = of_dra7xx_pcie_match, >> + }, >> +}; >> + >> +module_platform_driver_probe(dra7xx_pcie_driver, dra7xx_pcie_probe); >> + >> +MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@xxxxxx>"); >> +MODULE_DESCRIPTION("TI PCIe controller driver"); >> +MODULE_LICENSE("GPL v2"); >> > -- > To unsubscribe from this list: send the line "unsubscribe linux-pci" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html