Commit | Line | Data |
---|---|---|
5a3aa2a8 JP |
1 | /* |
2 | * PCIe RC driver for Synopsys DesignWare Core | |
3 | * | |
4 | * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com) | |
5 | * | |
9f46107b | 6 | * Authors: Joao Pinto <Joao.Pinto@synopsys.com> |
5a3aa2a8 JP |
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 | #include <linux/clk.h> | |
13 | #include <linux/delay.h> | |
14 | #include <linux/gpio.h> | |
15 | #include <linux/interrupt.h> | |
16 | #include <linux/kernel.h> | |
ca8d3346 | 17 | #include <linux/init.h> |
5a3aa2a8 JP |
18 | #include <linux/of_gpio.h> |
19 | #include <linux/pci.h> | |
20 | #include <linux/platform_device.h> | |
21 | #include <linux/resource.h> | |
22 | #include <linux/signal.h> | |
23 | #include <linux/types.h> | |
24 | ||
25 | #include "pcie-designware.h" | |
26 | ||
27 | struct dw_plat_pcie { | |
442ec4c0 | 28 | struct dw_pcie *pci; |
5a3aa2a8 JP |
29 | }; |
30 | ||
31 | static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg) | |
32 | { | |
33 | struct pcie_port *pp = arg; | |
34 | ||
35 | return dw_handle_msi_irq(pp); | |
36 | } | |
37 | ||
38 | static void dw_plat_pcie_host_init(struct pcie_port *pp) | |
39 | { | |
442ec4c0 KVA |
40 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); |
41 | ||
5a3aa2a8 | 42 | dw_pcie_setup_rc(pp); |
442ec4c0 | 43 | dw_pcie_wait_for_link(pci); |
5a3aa2a8 JP |
44 | |
45 | if (IS_ENABLED(CONFIG_PCI_MSI)) | |
46 | dw_pcie_msi_init(pp); | |
47 | } | |
48 | ||
442ec4c0 | 49 | static struct dw_pcie_host_ops dw_plat_pcie_host_ops = { |
5a3aa2a8 JP |
50 | .host_init = dw_plat_pcie_host_init, |
51 | }; | |
52 | ||
53 | static int dw_plat_add_pcie_port(struct pcie_port *pp, | |
54 | struct platform_device *pdev) | |
55 | { | |
442ec4c0 | 56 | struct device *dev = &pdev->dev; |
5a3aa2a8 JP |
57 | int ret; |
58 | ||
59 | pp->irq = platform_get_irq(pdev, 1); | |
60 | if (pp->irq < 0) | |
61 | return pp->irq; | |
62 | ||
63 | if (IS_ENABLED(CONFIG_PCI_MSI)) { | |
64 | pp->msi_irq = platform_get_irq(pdev, 0); | |
65 | if (pp->msi_irq < 0) | |
66 | return pp->msi_irq; | |
67 | ||
2d6054b9 | 68 | ret = devm_request_irq(dev, pp->msi_irq, |
5a3aa2a8 JP |
69 | dw_plat_pcie_msi_irq_handler, |
70 | IRQF_SHARED, "dw-plat-pcie-msi", pp); | |
71 | if (ret) { | |
2d6054b9 | 72 | dev_err(dev, "failed to request MSI IRQ\n"); |
5a3aa2a8 JP |
73 | return ret; |
74 | } | |
75 | } | |
76 | ||
77 | pp->root_bus_nr = -1; | |
78 | pp->ops = &dw_plat_pcie_host_ops; | |
79 | ||
80 | ret = dw_pcie_host_init(pp); | |
81 | if (ret) { | |
2d6054b9 | 82 | dev_err(dev, "failed to initialize host\n"); |
5a3aa2a8 JP |
83 | return ret; |
84 | } | |
85 | ||
86 | return 0; | |
87 | } | |
88 | ||
794a8604 NC |
89 | static const struct dw_pcie_ops dw_pcie_ops = { |
90 | }; | |
91 | ||
5a3aa2a8 JP |
92 | static int dw_plat_pcie_probe(struct platform_device *pdev) |
93 | { | |
2d6054b9 | 94 | struct device *dev = &pdev->dev; |
5a3aa2a8 | 95 | struct dw_plat_pcie *dw_plat_pcie; |
442ec4c0 | 96 | struct dw_pcie *pci; |
5a3aa2a8 JP |
97 | struct resource *res; /* Resource from DT */ |
98 | int ret; | |
99 | ||
2d6054b9 | 100 | dw_plat_pcie = devm_kzalloc(dev, sizeof(*dw_plat_pcie), GFP_KERNEL); |
5a3aa2a8 JP |
101 | if (!dw_plat_pcie) |
102 | return -ENOMEM; | |
103 | ||
442ec4c0 KVA |
104 | pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); |
105 | if (!pci) | |
106 | return -ENOMEM; | |
107 | ||
108 | pci->dev = dev; | |
794a8604 | 109 | pci->ops = &dw_pcie_ops; |
5a3aa2a8 | 110 | |
c0464062 GR |
111 | dw_plat_pcie->pci = pci; |
112 | ||
5a3aa2a8 | 113 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
442ec4c0 KVA |
114 | pci->dbi_base = devm_ioremap_resource(dev, res); |
115 | if (IS_ERR(pci->dbi_base)) | |
116 | return PTR_ERR(pci->dbi_base); | |
5a3aa2a8 | 117 | |
9bcf0a6f KVA |
118 | platform_set_drvdata(pdev, dw_plat_pcie); |
119 | ||
442ec4c0 | 120 | ret = dw_plat_add_pcie_port(&pci->pp, pdev); |
5a3aa2a8 JP |
121 | if (ret < 0) |
122 | return ret; | |
123 | ||
5a3aa2a8 JP |
124 | return 0; |
125 | } | |
126 | ||
127 | static const struct of_device_id dw_plat_pcie_of_match[] = { | |
128 | { .compatible = "snps,dw-pcie", }, | |
129 | {}, | |
130 | }; | |
5a3aa2a8 JP |
131 | |
132 | static struct platform_driver dw_plat_pcie_driver = { | |
133 | .driver = { | |
134 | .name = "dw-pcie", | |
135 | .of_match_table = dw_plat_pcie_of_match, | |
136 | }, | |
137 | .probe = dw_plat_pcie_probe, | |
138 | }; | |
ca8d3346 | 139 | builtin_platform_driver(dw_plat_pcie_driver); |