Commit 662e512bb0772a70aa6acf95a37be8e927f6dd4d
Committed by
Nitin Garg
1 parent
c1edce0616
Exists in
smarc-imx_3.14.28_1.0.0_ga
and in
1 other branch
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 | +}; |
MAINTAINERS
... | ... | @@ -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
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"); |