Commit 662e512bb0772a70aa6acf95a37be8e927f6dd4d

Authored by Kishon Vijay Abraham I
Committed by Nitin Garg
1 parent c1edce0616

PCI: dra7xx: Add TI DRA7xx PCIe driver

Add support for PCIe controller in DRA7xx.  This driver re-uses the
designware core code that is already present in kernel.

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Jingoo Han <jg1.han@samsung.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Pawel Moll <pawel.moll@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Ian Campbell <ijc+devicetree@hellion.org.uk>
Cc: Kumar Gala <galak@codeaurora.org>
Cc: Jason Gunthorpe <jgunthorpe@obsidianresearch.com>
Cc: Mohit Kumar <mohit.kumar@st.com>
Cc: Marek Vasut <marex@denx.de>
Cc: Arnd Bergmann <arnd@arndb.de>
(cherry picked from commit 47ff3de911a728cdf9ecc6ad777131902cff62b4)

Showing 5 changed files with 535 additions and 0 deletions Side-by-side Diff

Documentation/devicetree/bindings/pci/ti-pci.txt
  1 +TI PCI Controllers
  2 +
  3 +PCIe Designware Controller
  4 + - compatible: Should be "ti,dra7-pcie""
  5 + - reg : Two register ranges as listed in the reg-names property
  6 + - reg-names : The first entry must be "ti-conf" for the TI specific registers
  7 + The second entry must be "rc-dbics" for the designware pcie
  8 + registers
  9 + The third entry must be "config" for the PCIe configuration space
  10 + - phys : list of PHY specifiers (used by generic PHY framework)
  11 + - phy-names : must be "pcie-phy0", "pcie-phy1", "pcie-phyN".. based on the
  12 + number of PHYs as specified in *phys* property.
  13 + - ti,hwmods : Name of the hwmod associated to the pcie, "pcie<X>",
  14 + where <X> is the instance number of the pcie from the HW spec.
  15 + - interrupts : Two interrupt entries must be specified. The first one is for
  16 + main interrupt line and the second for MSI interrupt line.
  17 + - #address-cells,
  18 + #size-cells,
  19 + #interrupt-cells,
  20 + device_type,
  21 + ranges,
  22 + num-lanes,
  23 + interrupt-map-mask,
  24 + interrupt-map : as specified in ../designware-pcie.txt
  25 +
  26 +Example:
  27 +axi {
  28 + compatible = "simple-bus";
  29 + #size-cells = <1>;
  30 + #address-cells = <1>;
  31 + ranges = <0x51000000 0x51000000 0x3000
  32 + 0x0 0x20000000 0x10000000>;
  33 + pcie@51000000 {
  34 + compatible = "ti,dra7-pcie";
  35 + reg = <0x51000000 0x2000>, <0x51002000 0x14c>, <0x1000 0x2000>;
  36 + reg-names = "rc_dbics", "ti_conf", "config";
  37 + interrupts = <0 232 0x4>, <0 233 0x4>;
  38 + #address-cells = <3>;
  39 + #size-cells = <2>;
  40 + device_type = "pci";
  41 + ranges = <0x81000000 0 0 0x03000 0 0x00010000
  42 + 0x82000000 0 0x20013000 0x13000 0 0xffed000>;
  43 + #interrupt-cells = <1>;
  44 + num-lanes = <1>;
  45 + ti,hwmods = "pcie1";
  46 + phys = <&pcie1_phy>;
  47 + phy-names = "pcie-phy0";
  48 + interrupt-map-mask = <0 0 0 7>;
  49 + interrupt-map = <0 0 0 1 &pcie_intc 1>,
  50 + <0 0 0 2 &pcie_intc 2>,
  51 + <0 0 0 3 &pcie_intc 3>,
  52 + <0 0 0 4 &pcie_intc 4>;
  53 + pcie_intc: interrupt-controller {
  54 + interrupt-controller;
  55 + #address-cells = <0>;
  56 + #interrupt-cells = <1>;
  57 + };
  58 + };
  59 +};
... ... @@ -6618,6 +6618,14 @@
6618 6618 F: Documentation/devicetree/bindings/pci/nvidia,tegra20-pcie.txt
6619 6619 F: drivers/pci/host/pci-tegra.c
6620 6620  
  6621 +PCI DRIVER FOR TI DRA7XX
  6622 +M: Kishon Vijay Abraham I <kishon@ti.com>
  6623 +L: linux-omap@vger.kernel.org
  6624 +L: linux-pci@vger.kernel.org
  6625 +S: Supported
  6626 +F: Documentation/devicetree/bindings/pci/ti-pci.txt
  6627 +F: drivers/pci/host/pci-dra7xx.c
  6628 +
6621 6629 PCI DRIVER FOR RENESAS R-CAR
6622 6630 M: Simon Horman <horms@verge.net.au>
6623 6631 L: linux-pci@vger.kernel.org
drivers/pci/host/Kconfig
1 1 menu "PCI host controller drivers"
2 2 depends on PCI
3 3  
  4 +config PCI_DRA7XX
  5 + bool "TI DRA7xx PCIe controller"
  6 + select PCIE_DW
  7 + depends on OF && HAS_IOMEM && TI_PIPE3
  8 + help
  9 + Enables support for the PCIe controller in the DRA7xx SoC. There
  10 + are two instances of PCIe controller in DRA7xx. This controller can
  11 + act both as EP and RC. This reuses the Designware core.
  12 +
4 13 config PCI_MVEBU
5 14 bool "Marvell EBU PCIe controller"
6 15 depends on ARCH_MVEBU || ARCH_DOVE
drivers/pci/host/Makefile
1 1 obj-$(CONFIG_PCIE_DW) += pcie-designware.o
  2 +obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
2 3 obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
3 4 obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
4 5 obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
drivers/pci/host/pci-dra7xx.c
  1 +/*
  2 + * pcie-dra7xx - PCIe controller driver for TI DRA7xx SoCs
  3 + *
  4 + * Copyright (C) 2013-2014 Texas Instruments Incorporated - http://www.ti.com
  5 + *
  6 + * Authors: Kishon Vijay Abraham I <kishon@ti.com>
  7 + *
  8 + * This program is free software; you can redistribute it and/or modify
  9 + * it under the terms of the GNU General Public License version 2 as
  10 + * published by the Free Software Foundation.
  11 + */
  12 +
  13 +#include <linux/delay.h>
  14 +#include <linux/err.h>
  15 +#include <linux/interrupt.h>
  16 +#include <linux/irq.h>
  17 +#include <linux/irqdomain.h>
  18 +#include <linux/kernel.h>
  19 +#include <linux/module.h>
  20 +#include <linux/pci.h>
  21 +#include <linux/phy/phy.h>
  22 +#include <linux/platform_device.h>
  23 +#include <linux/pm_runtime.h>
  24 +#include <linux/resource.h>
  25 +#include <linux/types.h>
  26 +
  27 +#include "pcie-designware.h"
  28 +
  29 +/* PCIe controller wrapper DRA7XX configuration registers */
  30 +
  31 +#define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN 0x0024
  32 +#define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN 0x0028
  33 +#define ERR_SYS BIT(0)
  34 +#define ERR_FATAL BIT(1)
  35 +#define ERR_NONFATAL BIT(2)
  36 +#define ERR_COR BIT(3)
  37 +#define ERR_AXI BIT(4)
  38 +#define ERR_ECRC BIT(5)
  39 +#define PME_TURN_OFF BIT(8)
  40 +#define PME_TO_ACK BIT(9)
  41 +#define PM_PME BIT(10)
  42 +#define LINK_REQ_RST BIT(11)
  43 +#define LINK_UP_EVT BIT(12)
  44 +#define CFG_BME_EVT BIT(13)
  45 +#define CFG_MSE_EVT BIT(14)
  46 +#define INTERRUPTS (ERR_SYS | ERR_FATAL | ERR_NONFATAL | ERR_COR | ERR_AXI | \
  47 + ERR_ECRC | PME_TURN_OFF | PME_TO_ACK | PM_PME | \
  48 + LINK_REQ_RST | LINK_UP_EVT | CFG_BME_EVT | CFG_MSE_EVT)
  49 +
  50 +#define PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI 0x0034
  51 +#define PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI 0x0038
  52 +#define INTA BIT(0)
  53 +#define INTB BIT(1)
  54 +#define INTC BIT(2)
  55 +#define INTD BIT(3)
  56 +#define MSI BIT(4)
  57 +#define LEG_EP_INTERRUPTS (INTA | INTB | INTC | INTD)
  58 +
  59 +#define PCIECTRL_DRA7XX_CONF_DEVICE_CMD 0x0104
  60 +#define LTSSM_EN 0x1
  61 +
  62 +#define PCIECTRL_DRA7XX_CONF_PHY_CS 0x010C
  63 +#define LINK_UP BIT(16)
  64 +
  65 +struct dra7xx_pcie {
  66 + void __iomem *base;
  67 + struct phy **phy;
  68 + int phy_count;
  69 + struct device *dev;
  70 + struct pcie_port pp;
  71 +};
  72 +
  73 +#define to_dra7xx_pcie(x) container_of((x), struct dra7xx_pcie, pp)
  74 +
  75 +static inline u32 dra7xx_pcie_readl(struct dra7xx_pcie *pcie, u32 offset)
  76 +{
  77 + return readl(pcie->base + offset);
  78 +}
  79 +
  80 +static inline void dra7xx_pcie_writel(struct dra7xx_pcie *pcie, u32 offset,
  81 + u32 value)
  82 +{
  83 + writel(value, pcie->base + offset);
  84 +}
  85 +
  86 +static int dra7xx_pcie_link_up(struct pcie_port *pp)
  87 +{
  88 + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
  89 + u32 reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS);
  90 +
  91 + return !!(reg & LINK_UP);
  92 +}
  93 +
  94 +static int dra7xx_pcie_establish_link(struct pcie_port *pp)
  95 +{
  96 + u32 reg;
  97 + unsigned int retries = 1000;
  98 + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
  99 +
  100 + if (dw_pcie_link_up(pp)) {
  101 + dev_err(pp->dev, "link is already up\n");
  102 + return 0;
  103 + }
  104 +
  105 + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD);
  106 + reg |= LTSSM_EN;
  107 + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
  108 +
  109 + while (retries--) {
  110 + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_PHY_CS);
  111 + if (reg & LINK_UP)
  112 + break;
  113 + usleep_range(10, 20);
  114 + }
  115 +
  116 + if (retries == 0) {
  117 + dev_err(pp->dev, "link is not up\n");
  118 + return -ETIMEDOUT;
  119 + }
  120 +
  121 + return 0;
  122 +}
  123 +
  124 +static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp)
  125 +{
  126 + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
  127 +
  128 + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN,
  129 + ~INTERRUPTS);
  130 + dra7xx_pcie_writel(dra7xx,
  131 + PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MAIN, INTERRUPTS);
  132 + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI,
  133 + ~LEG_EP_INTERRUPTS & ~MSI);
  134 +
  135 + if (IS_ENABLED(CONFIG_PCI_MSI))
  136 + dra7xx_pcie_writel(dra7xx,
  137 + PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI, MSI);
  138 + else
  139 + dra7xx_pcie_writel(dra7xx,
  140 + PCIECTRL_DRA7XX_CONF_IRQENABLE_SET_MSI,
  141 + LEG_EP_INTERRUPTS);
  142 +}
  143 +
  144 +static void dra7xx_pcie_host_init(struct pcie_port *pp)
  145 +{
  146 + dw_pcie_setup_rc(pp);
  147 + dra7xx_pcie_establish_link(pp);
  148 + if (IS_ENABLED(CONFIG_PCI_MSI))
  149 + dw_pcie_msi_init(pp);
  150 + dra7xx_pcie_enable_interrupts(pp);
  151 +}
  152 +
  153 +static struct pcie_host_ops dra7xx_pcie_host_ops = {
  154 + .link_up = dra7xx_pcie_link_up,
  155 + .host_init = dra7xx_pcie_host_init,
  156 +};
  157 +
  158 +static int dra7xx_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
  159 + irq_hw_number_t hwirq)
  160 +{
  161 + irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
  162 + irq_set_chip_data(irq, domain->host_data);
  163 + set_irq_flags(irq, IRQF_VALID);
  164 +
  165 + return 0;
  166 +}
  167 +
  168 +static const struct irq_domain_ops intx_domain_ops = {
  169 + .map = dra7xx_pcie_intx_map,
  170 +};
  171 +
  172 +static int dra7xx_pcie_init_irq_domain(struct pcie_port *pp)
  173 +{
  174 + struct device *dev = pp->dev;
  175 + struct device_node *node = dev->of_node;
  176 + struct device_node *pcie_intc_node = of_get_next_child(node, NULL);
  177 +
  178 + if (!pcie_intc_node) {
  179 + dev_err(dev, "No PCIe Intc node found\n");
  180 + return PTR_ERR(pcie_intc_node);
  181 + }
  182 +
  183 + pp->irq_domain = irq_domain_add_linear(pcie_intc_node, 4,
  184 + &intx_domain_ops, pp);
  185 + if (!pp->irq_domain) {
  186 + dev_err(dev, "Failed to get a INTx IRQ domain\n");
  187 + return PTR_ERR(pp->irq_domain);
  188 + }
  189 +
  190 + return 0;
  191 +}
  192 +
  193 +static irqreturn_t dra7xx_pcie_msi_irq_handler(int irq, void *arg)
  194 +{
  195 + struct pcie_port *pp = arg;
  196 + struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
  197 + u32 reg;
  198 +
  199 + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI);
  200 +
  201 + switch (reg) {
  202 + case MSI:
  203 + dw_handle_msi_irq(pp);
  204 + break;
  205 + case INTA:
  206 + case INTB:
  207 + case INTC:
  208 + case INTD:
  209 + generic_handle_irq(irq_find_mapping(pp->irq_domain, ffs(reg)));
  210 + break;
  211 + }
  212 +
  213 + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MSI, reg);
  214 +
  215 + return IRQ_HANDLED;
  216 +}
  217 +
  218 +
  219 +static irqreturn_t dra7xx_pcie_irq_handler(int irq, void *arg)
  220 +{
  221 + struct dra7xx_pcie *dra7xx = arg;
  222 + u32 reg;
  223 +
  224 + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN);
  225 +
  226 + if (reg & ERR_SYS)
  227 + dev_dbg(dra7xx->dev, "System Error\n");
  228 +
  229 + if (reg & ERR_FATAL)
  230 + dev_dbg(dra7xx->dev, "Fatal Error\n");
  231 +
  232 + if (reg & ERR_NONFATAL)
  233 + dev_dbg(dra7xx->dev, "Non Fatal Error\n");
  234 +
  235 + if (reg & ERR_COR)
  236 + dev_dbg(dra7xx->dev, "Correctable Error\n");
  237 +
  238 + if (reg & ERR_AXI)
  239 + dev_dbg(dra7xx->dev, "AXI tag lookup fatal Error\n");
  240 +
  241 + if (reg & ERR_ECRC)
  242 + dev_dbg(dra7xx->dev, "ECRC Error\n");
  243 +
  244 + if (reg & PME_TURN_OFF)
  245 + dev_dbg(dra7xx->dev,
  246 + "Power Management Event Turn-Off message received\n");
  247 +
  248 + if (reg & PME_TO_ACK)
  249 + dev_dbg(dra7xx->dev,
  250 + "Power Management Turn-Off Ack message received\n");
  251 +
  252 + if (reg & PM_PME)
  253 + dev_dbg(dra7xx->dev,
  254 + "PM Power Management Event message received\n");
  255 +
  256 + if (reg & LINK_REQ_RST)
  257 + dev_dbg(dra7xx->dev, "Link Request Reset\n");
  258 +
  259 + if (reg & LINK_UP_EVT)
  260 + dev_dbg(dra7xx->dev, "Link-up state change\n");
  261 +
  262 + if (reg & CFG_BME_EVT)
  263 + dev_dbg(dra7xx->dev, "CFG 'Bus Master Enable' change\n");
  264 +
  265 + if (reg & CFG_MSE_EVT)
  266 + dev_dbg(dra7xx->dev, "CFG 'Memory Space Enable' change\n");
  267 +
  268 + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_IRQSTATUS_MAIN, reg);
  269 +
  270 + return IRQ_HANDLED;
  271 +}
  272 +
  273 +static int add_pcie_port(struct dra7xx_pcie *dra7xx,
  274 + struct platform_device *pdev)
  275 +{
  276 + int ret;
  277 + struct pcie_port *pp;
  278 + struct resource *res;
  279 + struct device *dev = &pdev->dev;
  280 +
  281 + pp = &dra7xx->pp;
  282 + pp->dev = dev;
  283 + pp->ops = &dra7xx_pcie_host_ops;
  284 +
  285 + pp->irq = platform_get_irq(pdev, 1);
  286 + if (pp->irq < 0) {
  287 + dev_err(dev, "missing IRQ resource\n");
  288 + return -EINVAL;
  289 + }
  290 +
  291 + ret = devm_request_irq(&pdev->dev, pp->irq,
  292 + dra7xx_pcie_msi_irq_handler, IRQF_SHARED,
  293 + "dra7-pcie-msi", pp);
  294 + if (ret) {
  295 + dev_err(&pdev->dev, "failed to request irq\n");
  296 + return ret;
  297 + }
  298 +
  299 + if (!IS_ENABLED(CONFIG_PCI_MSI)) {
  300 + ret = dra7xx_pcie_init_irq_domain(pp);
  301 + if (ret < 0)
  302 + return ret;
  303 + }
  304 +
  305 + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbics");
  306 + pp->dbi_base = devm_ioremap(dev, res->start, resource_size(res));
  307 + if (!pp->dbi_base)
  308 + return -ENOMEM;
  309 +
  310 + ret = dw_pcie_host_init(pp);
  311 + if (ret) {
  312 + dev_err(dra7xx->dev, "failed to initialize host\n");
  313 + return ret;
  314 + }
  315 +
  316 + return 0;
  317 +}
  318 +
  319 +static int __init dra7xx_pcie_probe(struct platform_device *pdev)
  320 +{
  321 + u32 reg;
  322 + int ret;
  323 + int irq;
  324 + int i;
  325 + int phy_count;
  326 + struct phy **phy;
  327 + void __iomem *base;
  328 + struct resource *res;
  329 + struct dra7xx_pcie *dra7xx;
  330 + struct device *dev = &pdev->dev;
  331 + struct device_node *np = dev->of_node;
  332 + char name[10];
  333 +
  334 + dra7xx = devm_kzalloc(dev, sizeof(*dra7xx), GFP_KERNEL);
  335 + if (!dra7xx)
  336 + return -ENOMEM;
  337 +
  338 + irq = platform_get_irq(pdev, 0);
  339 + if (irq < 0) {
  340 + dev_err(dev, "missing IRQ resource\n");
  341 + return -EINVAL;
  342 + }
  343 +
  344 + ret = devm_request_irq(dev, irq, dra7xx_pcie_irq_handler,
  345 + IRQF_SHARED, "dra7xx-pcie-main", dra7xx);
  346 + if (ret) {
  347 + dev_err(dev, "failed to request irq\n");
  348 + return ret;
  349 + }
  350 +
  351 + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ti_conf");
  352 + base = devm_ioremap_nocache(dev, res->start, resource_size(res));
  353 + if (!base)
  354 + return -ENOMEM;
  355 +
  356 + phy_count = of_property_count_strings(np, "phy-names");
  357 + if (phy_count < 0) {
  358 + dev_err(dev, "unable to find the strings\n");
  359 + return phy_count;
  360 + }
  361 +
  362 + phy = devm_kzalloc(dev, sizeof(*phy) * phy_count, GFP_KERNEL);
  363 + if (!phy)
  364 + return -ENOMEM;
  365 +
  366 + for (i = 0; i < phy_count; i++) {
  367 + snprintf(name, sizeof(name), "pcie-phy%d", i);
  368 + phy[i] = devm_phy_get(dev, name);
  369 + if (IS_ERR(phy[i]))
  370 + return PTR_ERR(phy[i]);
  371 +
  372 + ret = phy_init(phy[i]);
  373 + if (ret < 0)
  374 + goto err_phy;
  375 +
  376 + ret = phy_power_on(phy[i]);
  377 + if (ret < 0) {
  378 + phy_exit(phy[i]);
  379 + goto err_phy;
  380 + }
  381 + }
  382 +
  383 + dra7xx->base = base;
  384 + dra7xx->phy = phy;
  385 + dra7xx->dev = dev;
  386 + dra7xx->phy_count = phy_count;
  387 +
  388 + pm_runtime_enable(dev);
  389 + ret = pm_runtime_get_sync(dev);
  390 + if (IS_ERR_VALUE(ret)) {
  391 + dev_err(dev, "pm_runtime_get_sync failed\n");
  392 + goto err_phy;
  393 + }
  394 +
  395 + reg = dra7xx_pcie_readl(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD);
  396 + reg &= ~LTSSM_EN;
  397 + dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
  398 +
  399 + platform_set_drvdata(pdev, dra7xx);
  400 +
  401 + ret = add_pcie_port(dra7xx, pdev);
  402 + if (ret < 0)
  403 + goto err_add_port;
  404 +
  405 + return 0;
  406 +
  407 +err_add_port:
  408 + pm_runtime_put(dev);
  409 + pm_runtime_disable(dev);
  410 +
  411 +err_phy:
  412 + while (--i >= 0) {
  413 + phy_power_off(phy[i]);
  414 + phy_exit(phy[i]);
  415 + }
  416 +
  417 + return ret;
  418 +}
  419 +
  420 +static int __exit dra7xx_pcie_remove(struct platform_device *pdev)
  421 +{
  422 + struct dra7xx_pcie *dra7xx = platform_get_drvdata(pdev);
  423 + struct pcie_port *pp = &dra7xx->pp;
  424 + struct device *dev = &pdev->dev;
  425 + int count = dra7xx->phy_count;
  426 +
  427 + if (pp->irq_domain)
  428 + irq_domain_remove(pp->irq_domain);
  429 + pm_runtime_put(dev);
  430 + pm_runtime_disable(dev);
  431 + while (count--) {
  432 + phy_power_off(dra7xx->phy[count]);
  433 + phy_exit(dra7xx->phy[count]);
  434 + }
  435 +
  436 + return 0;
  437 +}
  438 +
  439 +static const struct of_device_id of_dra7xx_pcie_match[] = {
  440 + { .compatible = "ti,dra7-pcie", },
  441 + {},
  442 +};
  443 +MODULE_DEVICE_TABLE(of, of_dra7xx_pcie_match);
  444 +
  445 +static struct platform_driver dra7xx_pcie_driver = {
  446 + .remove = __exit_p(dra7xx_pcie_remove),
  447 + .driver = {
  448 + .name = "dra7-pcie",
  449 + .owner = THIS_MODULE,
  450 + .of_match_table = of_dra7xx_pcie_match,
  451 + },
  452 +};
  453 +
  454 +module_platform_driver_probe(dra7xx_pcie_driver, dra7xx_pcie_probe);
  455 +
  456 +MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
  457 +MODULE_DESCRIPTION("TI PCIe controller driver");
  458 +MODULE_LICENSE("GPL v2");