diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2019-01-02 16:31:15 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2019-01-02 16:31:15 -0500 |
commit | cdf4f4dc118bafd97d0148d53730e396ece11afa (patch) | |
tree | 91f12d628f1ec43fee71da3269b8fe0ad7d4b4e8 /drivers/pci/controller/dwc | |
parent | c266b026ae7a51f876eb74e15ff63f834fe35976 (diff) | |
parent | 7e6d5cd88a6f4961ed2968799ef98528c13ac5f8 (diff) |
Merge branch 'remotes/lorenzo/pci/uniphier'
- Add UniPhier PCIe controller driver and DT bindings (Kunihiko Hayashi)
* remotes/lorenzo/pci/uniphier:
PCI: uniphier: Add UniPhier PCIe host controller support
dt-bindings: PCI: Add UniPhier PCIe host controller description
# Conflicts:
# drivers/pci/controller/dwc/Kconfig
# drivers/pci/controller/dwc/Makefile
Diffstat (limited to 'drivers/pci/controller/dwc')
-rw-r--r-- | drivers/pci/controller/dwc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/pci/controller/dwc/Makefile | 1 | ||||
-rw-r--r-- | drivers/pci/controller/dwc/pcie-uniphier.c | 471 |
3 files changed, 482 insertions, 0 deletions
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index 0b1ab6b5e234..548c58223868 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig | |||
@@ -203,4 +203,14 @@ config PCI_MESON | |||
203 | and therefore the driver re-uses the DesignWare core functions to | 203 | and therefore the driver re-uses the DesignWare core functions to |
204 | implement the driver. | 204 | implement the driver. |
205 | 205 | ||
206 | config PCIE_UNIPHIER | ||
207 | bool "Socionext UniPhier PCIe controllers" | ||
208 | depends on ARCH_UNIPHIER || COMPILE_TEST | ||
209 | depends on OF && HAS_IOMEM | ||
210 | depends on PCI_MSI_IRQ_DOMAIN | ||
211 | select PCIE_DW_HOST | ||
212 | help | ||
213 | Say Y here if you want PCIe controller support on UniPhier SoCs. | ||
214 | This driver supports LD20 and PXs3 SoCs. | ||
215 | |||
206 | endmenu | 216 | endmenu |
diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index e05a0153ffc4..7bcdcdf5024e 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile | |||
@@ -15,6 +15,7 @@ obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o | |||
15 | obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o | 15 | obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o |
16 | obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o | 16 | obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o |
17 | obj-$(CONFIG_PCI_MESON) += pci-meson.o | 17 | obj-$(CONFIG_PCI_MESON) += pci-meson.o |
18 | obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o | ||
18 | 19 | ||
19 | # The following drivers are for devices that use the generic ACPI | 20 | # The following drivers are for devices that use the generic ACPI |
20 | # pci_root.c driver but don't support standard ECAM config access. | 21 | # pci_root.c driver but don't support standard ECAM config access. |
diff --git a/drivers/pci/controller/dwc/pcie-uniphier.c b/drivers/pci/controller/dwc/pcie-uniphier.c new file mode 100644 index 000000000000..d5dc40289cce --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-uniphier.c | |||
@@ -0,0 +1,471 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * PCIe host controller driver for UniPhier SoCs | ||
4 | * Copyright 2018 Socionext Inc. | ||
5 | * Author: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> | ||
6 | */ | ||
7 | |||
8 | #include <linux/bitops.h> | ||
9 | #include <linux/bitfield.h> | ||
10 | #include <linux/clk.h> | ||
11 | #include <linux/delay.h> | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/iopoll.h> | ||
14 | #include <linux/irqchip/chained_irq.h> | ||
15 | #include <linux/irqdomain.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/of_irq.h> | ||
18 | #include <linux/pci.h> | ||
19 | #include <linux/phy/phy.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/reset.h> | ||
22 | |||
23 | #include "pcie-designware.h" | ||
24 | |||
25 | #define PCL_PINCTRL0 0x002c | ||
26 | #define PCL_PERST_PLDN_REGEN BIT(12) | ||
27 | #define PCL_PERST_NOE_REGEN BIT(11) | ||
28 | #define PCL_PERST_OUT_REGEN BIT(8) | ||
29 | #define PCL_PERST_PLDN_REGVAL BIT(4) | ||
30 | #define PCL_PERST_NOE_REGVAL BIT(3) | ||
31 | #define PCL_PERST_OUT_REGVAL BIT(0) | ||
32 | |||
33 | #define PCL_PIPEMON 0x0044 | ||
34 | #define PCL_PCLK_ALIVE BIT(15) | ||
35 | |||
36 | #define PCL_APP_READY_CTRL 0x8008 | ||
37 | #define PCL_APP_LTSSM_ENABLE BIT(0) | ||
38 | |||
39 | #define PCL_APP_PM0 0x8078 | ||
40 | #define PCL_SYS_AUX_PWR_DET BIT(8) | ||
41 | |||
42 | #define PCL_RCV_INT 0x8108 | ||
43 | #define PCL_RCV_INT_ALL_ENABLE GENMASK(20, 17) | ||
44 | #define PCL_CFG_BW_MGT_STATUS BIT(4) | ||
45 | #define PCL_CFG_LINK_AUTO_BW_STATUS BIT(3) | ||
46 | #define PCL_CFG_AER_RC_ERR_MSI_STATUS BIT(2) | ||
47 | #define PCL_CFG_PME_MSI_STATUS BIT(1) | ||
48 | |||
49 | #define PCL_RCV_INTX 0x810c | ||
50 | #define PCL_RCV_INTX_ALL_ENABLE GENMASK(19, 16) | ||
51 | #define PCL_RCV_INTX_ALL_MASK GENMASK(11, 8) | ||
52 | #define PCL_RCV_INTX_MASK_SHIFT 8 | ||
53 | #define PCL_RCV_INTX_ALL_STATUS GENMASK(3, 0) | ||
54 | #define PCL_RCV_INTX_STATUS_SHIFT 0 | ||
55 | |||
56 | #define PCL_STATUS_LINK 0x8140 | ||
57 | #define PCL_RDLH_LINK_UP BIT(1) | ||
58 | #define PCL_XMLH_LINK_UP BIT(0) | ||
59 | |||
60 | struct uniphier_pcie_priv { | ||
61 | void __iomem *base; | ||
62 | struct dw_pcie pci; | ||
63 | struct clk *clk; | ||
64 | struct reset_control *rst; | ||
65 | struct phy *phy; | ||
66 | struct irq_domain *legacy_irq_domain; | ||
67 | }; | ||
68 | |||
69 | #define to_uniphier_pcie(x) dev_get_drvdata((x)->dev) | ||
70 | |||
71 | static void uniphier_pcie_ltssm_enable(struct uniphier_pcie_priv *priv, | ||
72 | bool enable) | ||
73 | { | ||
74 | u32 val; | ||
75 | |||
76 | val = readl(priv->base + PCL_APP_READY_CTRL); | ||
77 | if (enable) | ||
78 | val |= PCL_APP_LTSSM_ENABLE; | ||
79 | else | ||
80 | val &= ~PCL_APP_LTSSM_ENABLE; | ||
81 | writel(val, priv->base + PCL_APP_READY_CTRL); | ||
82 | } | ||
83 | |||
84 | static void uniphier_pcie_init_rc(struct uniphier_pcie_priv *priv) | ||
85 | { | ||
86 | u32 val; | ||
87 | |||
88 | /* use auxiliary power detection */ | ||
89 | val = readl(priv->base + PCL_APP_PM0); | ||
90 | val |= PCL_SYS_AUX_PWR_DET; | ||
91 | writel(val, priv->base + PCL_APP_PM0); | ||
92 | |||
93 | /* assert PERST# */ | ||
94 | val = readl(priv->base + PCL_PINCTRL0); | ||
95 | val &= ~(PCL_PERST_NOE_REGVAL | PCL_PERST_OUT_REGVAL | ||
96 | | PCL_PERST_PLDN_REGVAL); | ||
97 | val |= PCL_PERST_NOE_REGEN | PCL_PERST_OUT_REGEN | ||
98 | | PCL_PERST_PLDN_REGEN; | ||
99 | writel(val, priv->base + PCL_PINCTRL0); | ||
100 | |||
101 | uniphier_pcie_ltssm_enable(priv, false); | ||
102 | |||
103 | usleep_range(100000, 200000); | ||
104 | |||
105 | /* deassert PERST# */ | ||
106 | val = readl(priv->base + PCL_PINCTRL0); | ||
107 | val |= PCL_PERST_OUT_REGVAL | PCL_PERST_OUT_REGEN; | ||
108 | writel(val, priv->base + PCL_PINCTRL0); | ||
109 | } | ||
110 | |||
111 | static int uniphier_pcie_wait_rc(struct uniphier_pcie_priv *priv) | ||
112 | { | ||
113 | u32 status; | ||
114 | int ret; | ||
115 | |||
116 | /* wait PIPE clock */ | ||
117 | ret = readl_poll_timeout(priv->base + PCL_PIPEMON, status, | ||
118 | status & PCL_PCLK_ALIVE, 100000, 1000000); | ||
119 | if (ret) { | ||
120 | dev_err(priv->pci.dev, | ||
121 | "Failed to initialize controller in RC mode\n"); | ||
122 | return ret; | ||
123 | } | ||
124 | |||
125 | return 0; | ||
126 | } | ||
127 | |||
128 | static int uniphier_pcie_link_up(struct dw_pcie *pci) | ||
129 | { | ||
130 | struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); | ||
131 | u32 val, mask; | ||
132 | |||
133 | val = readl(priv->base + PCL_STATUS_LINK); | ||
134 | mask = PCL_RDLH_LINK_UP | PCL_XMLH_LINK_UP; | ||
135 | |||
136 | return (val & mask) == mask; | ||
137 | } | ||
138 | |||
139 | static int uniphier_pcie_establish_link(struct dw_pcie *pci) | ||
140 | { | ||
141 | struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); | ||
142 | |||
143 | if (dw_pcie_link_up(pci)) | ||
144 | return 0; | ||
145 | |||
146 | uniphier_pcie_ltssm_enable(priv, true); | ||
147 | |||
148 | return dw_pcie_wait_for_link(pci); | ||
149 | } | ||
150 | |||
151 | static void uniphier_pcie_stop_link(struct dw_pcie *pci) | ||
152 | { | ||
153 | struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); | ||
154 | |||
155 | uniphier_pcie_ltssm_enable(priv, false); | ||
156 | } | ||
157 | |||
158 | static void uniphier_pcie_irq_enable(struct uniphier_pcie_priv *priv) | ||
159 | { | ||
160 | writel(PCL_RCV_INT_ALL_ENABLE, priv->base + PCL_RCV_INT); | ||
161 | writel(PCL_RCV_INTX_ALL_ENABLE, priv->base + PCL_RCV_INTX); | ||
162 | } | ||
163 | |||
164 | static void uniphier_pcie_irq_disable(struct uniphier_pcie_priv *priv) | ||
165 | { | ||
166 | writel(0, priv->base + PCL_RCV_INT); | ||
167 | writel(0, priv->base + PCL_RCV_INTX); | ||
168 | } | ||
169 | |||
170 | static void uniphier_pcie_irq_ack(struct irq_data *d) | ||
171 | { | ||
172 | struct pcie_port *pp = irq_data_get_irq_chip_data(d); | ||
173 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); | ||
174 | struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); | ||
175 | u32 val; | ||
176 | |||
177 | val = readl(priv->base + PCL_RCV_INTX); | ||
178 | val &= ~PCL_RCV_INTX_ALL_STATUS; | ||
179 | val |= BIT(irqd_to_hwirq(d) + PCL_RCV_INTX_STATUS_SHIFT); | ||
180 | writel(val, priv->base + PCL_RCV_INTX); | ||
181 | } | ||
182 | |||
183 | static void uniphier_pcie_irq_mask(struct irq_data *d) | ||
184 | { | ||
185 | struct pcie_port *pp = irq_data_get_irq_chip_data(d); | ||
186 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); | ||
187 | struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); | ||
188 | u32 val; | ||
189 | |||
190 | val = readl(priv->base + PCL_RCV_INTX); | ||
191 | val &= ~PCL_RCV_INTX_ALL_MASK; | ||
192 | val |= BIT(irqd_to_hwirq(d) + PCL_RCV_INTX_MASK_SHIFT); | ||
193 | writel(val, priv->base + PCL_RCV_INTX); | ||
194 | } | ||
195 | |||
196 | static void uniphier_pcie_irq_unmask(struct irq_data *d) | ||
197 | { | ||
198 | struct pcie_port *pp = irq_data_get_irq_chip_data(d); | ||
199 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); | ||
200 | struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); | ||
201 | u32 val; | ||
202 | |||
203 | val = readl(priv->base + PCL_RCV_INTX); | ||
204 | val &= ~PCL_RCV_INTX_ALL_MASK; | ||
205 | val &= ~BIT(irqd_to_hwirq(d) + PCL_RCV_INTX_MASK_SHIFT); | ||
206 | writel(val, priv->base + PCL_RCV_INTX); | ||
207 | } | ||
208 | |||
209 | static struct irq_chip uniphier_pcie_irq_chip = { | ||
210 | .name = "PCI", | ||
211 | .irq_ack = uniphier_pcie_irq_ack, | ||
212 | .irq_mask = uniphier_pcie_irq_mask, | ||
213 | .irq_unmask = uniphier_pcie_irq_unmask, | ||
214 | }; | ||
215 | |||
216 | static int uniphier_pcie_intx_map(struct irq_domain *domain, unsigned int irq, | ||
217 | irq_hw_number_t hwirq) | ||
218 | { | ||
219 | irq_set_chip_and_handler(irq, &uniphier_pcie_irq_chip, | ||
220 | handle_level_irq); | ||
221 | irq_set_chip_data(irq, domain->host_data); | ||
222 | |||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | static const struct irq_domain_ops uniphier_intx_domain_ops = { | ||
227 | .map = uniphier_pcie_intx_map, | ||
228 | }; | ||
229 | |||
230 | static void uniphier_pcie_irq_handler(struct irq_desc *desc) | ||
231 | { | ||
232 | struct pcie_port *pp = irq_desc_get_handler_data(desc); | ||
233 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); | ||
234 | struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); | ||
235 | struct irq_chip *chip = irq_desc_get_chip(desc); | ||
236 | unsigned long reg; | ||
237 | u32 val, bit, virq; | ||
238 | |||
239 | /* INT for debug */ | ||
240 | val = readl(priv->base + PCL_RCV_INT); | ||
241 | |||
242 | if (val & PCL_CFG_BW_MGT_STATUS) | ||
243 | dev_dbg(pci->dev, "Link Bandwidth Management Event\n"); | ||
244 | if (val & PCL_CFG_LINK_AUTO_BW_STATUS) | ||
245 | dev_dbg(pci->dev, "Link Autonomous Bandwidth Event\n"); | ||
246 | if (val & PCL_CFG_AER_RC_ERR_MSI_STATUS) | ||
247 | dev_dbg(pci->dev, "Root Error\n"); | ||
248 | if (val & PCL_CFG_PME_MSI_STATUS) | ||
249 | dev_dbg(pci->dev, "PME Interrupt\n"); | ||
250 | |||
251 | writel(val, priv->base + PCL_RCV_INT); | ||
252 | |||
253 | /* INTx */ | ||
254 | chained_irq_enter(chip, desc); | ||
255 | |||
256 | val = readl(priv->base + PCL_RCV_INTX); | ||
257 | reg = FIELD_GET(PCL_RCV_INTX_ALL_STATUS, val); | ||
258 | |||
259 | for_each_set_bit(bit, ®, PCI_NUM_INTX) { | ||
260 | virq = irq_linear_revmap(priv->legacy_irq_domain, bit); | ||
261 | generic_handle_irq(virq); | ||
262 | } | ||
263 | |||
264 | chained_irq_exit(chip, desc); | ||
265 | } | ||
266 | |||
267 | static int uniphier_pcie_config_legacy_irq(struct pcie_port *pp) | ||
268 | { | ||
269 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); | ||
270 | struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); | ||
271 | struct device_node *np = pci->dev->of_node; | ||
272 | struct device_node *np_intc; | ||
273 | |||
274 | np_intc = of_get_child_by_name(np, "legacy-interrupt-controller"); | ||
275 | if (!np_intc) { | ||
276 | dev_err(pci->dev, "Failed to get legacy-interrupt-controller node\n"); | ||
277 | return -EINVAL; | ||
278 | } | ||
279 | |||
280 | pp->irq = irq_of_parse_and_map(np_intc, 0); | ||
281 | if (!pp->irq) { | ||
282 | dev_err(pci->dev, "Failed to get an IRQ entry in legacy-interrupt-controller\n"); | ||
283 | return -EINVAL; | ||
284 | } | ||
285 | |||
286 | priv->legacy_irq_domain = irq_domain_add_linear(np_intc, PCI_NUM_INTX, | ||
287 | &uniphier_intx_domain_ops, pp); | ||
288 | if (!priv->legacy_irq_domain) { | ||
289 | dev_err(pci->dev, "Failed to get INTx domain\n"); | ||
290 | return -ENODEV; | ||
291 | } | ||
292 | |||
293 | irq_set_chained_handler_and_data(pp->irq, uniphier_pcie_irq_handler, | ||
294 | pp); | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static int uniphier_pcie_host_init(struct pcie_port *pp) | ||
300 | { | ||
301 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); | ||
302 | struct uniphier_pcie_priv *priv = to_uniphier_pcie(pci); | ||
303 | int ret; | ||
304 | |||
305 | ret = uniphier_pcie_config_legacy_irq(pp); | ||
306 | if (ret) | ||
307 | return ret; | ||
308 | |||
309 | uniphier_pcie_irq_enable(priv); | ||
310 | |||
311 | dw_pcie_setup_rc(pp); | ||
312 | ret = uniphier_pcie_establish_link(pci); | ||
313 | if (ret) | ||
314 | return ret; | ||
315 | |||
316 | if (IS_ENABLED(CONFIG_PCI_MSI)) | ||
317 | dw_pcie_msi_init(pp); | ||
318 | |||
319 | return 0; | ||
320 | } | ||
321 | |||
322 | static const struct dw_pcie_host_ops uniphier_pcie_host_ops = { | ||
323 | .host_init = uniphier_pcie_host_init, | ||
324 | }; | ||
325 | |||
326 | static int uniphier_add_pcie_port(struct uniphier_pcie_priv *priv, | ||
327 | struct platform_device *pdev) | ||
328 | { | ||
329 | struct dw_pcie *pci = &priv->pci; | ||
330 | struct pcie_port *pp = &pci->pp; | ||
331 | struct device *dev = &pdev->dev; | ||
332 | int ret; | ||
333 | |||
334 | pp->ops = &uniphier_pcie_host_ops; | ||
335 | |||
336 | if (IS_ENABLED(CONFIG_PCI_MSI)) { | ||
337 | pp->msi_irq = platform_get_irq_byname(pdev, "msi"); | ||
338 | if (pp->msi_irq < 0) | ||
339 | return pp->msi_irq; | ||
340 | } | ||
341 | |||
342 | ret = dw_pcie_host_init(pp); | ||
343 | if (ret) { | ||
344 | dev_err(dev, "Failed to initialize host (%d)\n", ret); | ||
345 | return ret; | ||
346 | } | ||
347 | |||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | static int uniphier_pcie_host_enable(struct uniphier_pcie_priv *priv) | ||
352 | { | ||
353 | int ret; | ||
354 | |||
355 | ret = clk_prepare_enable(priv->clk); | ||
356 | if (ret) | ||
357 | return ret; | ||
358 | |||
359 | ret = reset_control_deassert(priv->rst); | ||
360 | if (ret) | ||
361 | goto out_clk_disable; | ||
362 | |||
363 | uniphier_pcie_init_rc(priv); | ||
364 | |||
365 | ret = phy_init(priv->phy); | ||
366 | if (ret) | ||
367 | goto out_rst_assert; | ||
368 | |||
369 | ret = uniphier_pcie_wait_rc(priv); | ||
370 | if (ret) | ||
371 | goto out_phy_exit; | ||
372 | |||
373 | return 0; | ||
374 | |||
375 | out_phy_exit: | ||
376 | phy_exit(priv->phy); | ||
377 | out_rst_assert: | ||
378 | reset_control_assert(priv->rst); | ||
379 | out_clk_disable: | ||
380 | clk_disable_unprepare(priv->clk); | ||
381 | |||
382 | return ret; | ||
383 | } | ||
384 | |||
385 | static void uniphier_pcie_host_disable(struct uniphier_pcie_priv *priv) | ||
386 | { | ||
387 | uniphier_pcie_irq_disable(priv); | ||
388 | phy_exit(priv->phy); | ||
389 | reset_control_assert(priv->rst); | ||
390 | clk_disable_unprepare(priv->clk); | ||
391 | } | ||
392 | |||
393 | static const struct dw_pcie_ops dw_pcie_ops = { | ||
394 | .start_link = uniphier_pcie_establish_link, | ||
395 | .stop_link = uniphier_pcie_stop_link, | ||
396 | .link_up = uniphier_pcie_link_up, | ||
397 | }; | ||
398 | |||
399 | static int uniphier_pcie_probe(struct platform_device *pdev) | ||
400 | { | ||
401 | struct device *dev = &pdev->dev; | ||
402 | struct uniphier_pcie_priv *priv; | ||
403 | struct resource *res; | ||
404 | int ret; | ||
405 | |||
406 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
407 | if (!priv) | ||
408 | return -ENOMEM; | ||
409 | |||
410 | priv->pci.dev = dev; | ||
411 | priv->pci.ops = &dw_pcie_ops; | ||
412 | |||
413 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); | ||
414 | priv->pci.dbi_base = devm_pci_remap_cfg_resource(dev, res); | ||
415 | if (IS_ERR(priv->pci.dbi_base)) | ||
416 | return PTR_ERR(priv->pci.dbi_base); | ||
417 | |||
418 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "link"); | ||
419 | priv->base = devm_ioremap_resource(dev, res); | ||
420 | if (IS_ERR(priv->base)) | ||
421 | return PTR_ERR(priv->base); | ||
422 | |||
423 | priv->clk = devm_clk_get(dev, NULL); | ||
424 | if (IS_ERR(priv->clk)) | ||
425 | return PTR_ERR(priv->clk); | ||
426 | |||
427 | priv->rst = devm_reset_control_get_shared(dev, NULL); | ||
428 | if (IS_ERR(priv->rst)) | ||
429 | return PTR_ERR(priv->rst); | ||
430 | |||
431 | priv->phy = devm_phy_optional_get(dev, "pcie-phy"); | ||
432 | if (IS_ERR(priv->phy)) | ||
433 | return PTR_ERR(priv->phy); | ||
434 | |||
435 | platform_set_drvdata(pdev, priv); | ||
436 | |||
437 | ret = uniphier_pcie_host_enable(priv); | ||
438 | if (ret) | ||
439 | return ret; | ||
440 | |||
441 | return uniphier_add_pcie_port(priv, pdev); | ||
442 | } | ||
443 | |||
444 | static int uniphier_pcie_remove(struct platform_device *pdev) | ||
445 | { | ||
446 | struct uniphier_pcie_priv *priv = platform_get_drvdata(pdev); | ||
447 | |||
448 | uniphier_pcie_host_disable(priv); | ||
449 | |||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | static const struct of_device_id uniphier_pcie_match[] = { | ||
454 | { .compatible = "socionext,uniphier-pcie", }, | ||
455 | { /* sentinel */ }, | ||
456 | }; | ||
457 | MODULE_DEVICE_TABLE(of, uniphier_pcie_match); | ||
458 | |||
459 | static struct platform_driver uniphier_pcie_driver = { | ||
460 | .probe = uniphier_pcie_probe, | ||
461 | .remove = uniphier_pcie_remove, | ||
462 | .driver = { | ||
463 | .name = "uniphier-pcie", | ||
464 | .of_match_table = uniphier_pcie_match, | ||
465 | }, | ||
466 | }; | ||
467 | builtin_platform_driver(uniphier_pcie_driver); | ||
468 | |||
469 | MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>"); | ||
470 | MODULE_DESCRIPTION("UniPhier PCIe host controller driver"); | ||
471 | MODULE_LICENSE("GPL v2"); | ||