aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/controller/dwc
diff options
context:
space:
mode:
authorKunihiko Hayashi <hayashi.kunihiko@socionext.com>2018-12-06 19:53:12 -0500
committerLorenzo Pieralisi <lorenzo.pieralisi@arm.com>2018-12-19 05:25:44 -0500
commit7e6d5cd88a6f4961ed2968799ef98528c13ac5f8 (patch)
tree679161fb638261ec917e6c686671e511d2509965 /drivers/pci/controller/dwc
parentdb67cdb04234899d0c1a734beef4bfea4c38efef (diff)
PCI: uniphier: Add UniPhier PCIe host controller support
This introduces specific glue layer for UniPhier platform to support PCIe host controller that is based on the DesignWare PCIe core, and this driver supports Root Complex (host) mode. Signed-off-by: Kunihiko Hayashi <hayashi.kunihiko@socionext.com> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Diffstat (limited to 'drivers/pci/controller/dwc')
-rw-r--r--drivers/pci/controller/dwc/Kconfig10
-rw-r--r--drivers/pci/controller/dwc/Makefile1
-rw-r--r--drivers/pci/controller/dwc/pcie-uniphier.c471
3 files changed, 482 insertions, 0 deletions
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
index 91b0194240a5..955068867c35 100644
--- a/drivers/pci/controller/dwc/Kconfig
+++ b/drivers/pci/controller/dwc/Kconfig
@@ -193,4 +193,14 @@ config PCIE_HISI_STB
193 help 193 help
194 Say Y here if you want PCIe controller support on HiSilicon STB SoCs 194 Say Y here if you want PCIe controller support on HiSilicon STB SoCs
195 195
196config PCIE_UNIPHIER
197 bool "Socionext UniPhier PCIe controllers"
198 depends on ARCH_UNIPHIER || COMPILE_TEST
199 depends on OF && HAS_IOMEM
200 depends on PCI_MSI_IRQ_DOMAIN
201 select PCIE_DW_HOST
202 help
203 Say Y here if you want PCIe controller support on UniPhier SoCs.
204 This driver supports LD20 and PXs3 SoCs.
205
196endmenu 206endmenu
diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile
index fcf91eacfc63..688a0ec13121 100644
--- a/drivers/pci/controller/dwc/Makefile
+++ b/drivers/pci/controller/dwc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_PCIE_ARMADA_8K) += pcie-armada8k.o
14obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o 14obj-$(CONFIG_PCIE_ARTPEC6) += pcie-artpec6.o
15obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o 15obj-$(CONFIG_PCIE_KIRIN) += pcie-kirin.o
16obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o 16obj-$(CONFIG_PCIE_HISI_STB) += pcie-histb.o
17obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o
17 18
18# The following drivers are for devices that use the generic ACPI 19# The following drivers are for devices that use the generic ACPI
19# pci_root.c driver but don't support standard ECAM config access. 20# 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
60struct 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
71static 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
84static 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
111static 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
128static 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
139static 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
151static 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
158static 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
164static 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
170static 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
183static 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
196static 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
209static 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
216static 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
226static const struct irq_domain_ops uniphier_intx_domain_ops = {
227 .map = uniphier_pcie_intx_map,
228};
229
230static 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, &reg, 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
267static 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
299static 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
322static const struct dw_pcie_host_ops uniphier_pcie_host_ops = {
323 .host_init = uniphier_pcie_host_init,
324};
325
326static 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
351static 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
375out_phy_exit:
376 phy_exit(priv->phy);
377out_rst_assert:
378 reset_control_assert(priv->rst);
379out_clk_disable:
380 clk_disable_unprepare(priv->clk);
381
382 return ret;
383}
384
385static 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
393static 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
399static 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
444static 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
453static const struct of_device_id uniphier_pcie_match[] = {
454 { .compatible = "socionext,uniphier-pcie", },
455 { /* sentinel */ },
456};
457MODULE_DEVICE_TABLE(of, uniphier_pcie_match);
458
459static 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};
467builtin_platform_driver(uniphier_pcie_driver);
468
469MODULE_AUTHOR("Kunihiko Hayashi <hayashi.kunihiko@socionext.com>");
470MODULE_DESCRIPTION("UniPhier PCIe host controller driver");
471MODULE_LICENSE("GPL v2");