aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/controller/dwc/pci-layerscape.c
diff options
context:
space:
mode:
authorShawn Lin <shawn.lin@rock-chips.com>2018-05-30 21:12:37 -0400
committerBjorn Helgaas <bhelgaas@google.com>2018-06-08 08:50:11 -0400
commit6e0832fa432ec99c94caee733c8f5851cf85560b (patch)
treec4326f9e2d8ff1a6cb17e959fc5268c9e577ca94 /drivers/pci/controller/dwc/pci-layerscape.c
parent3a3869f1c443383ef8354ffa0e5fb8df65d8b549 (diff)
PCI: Collect all native drivers under drivers/pci/controller/
Native PCI drivers for root complex devices were originally all in drivers/pci/host/. Some of these devices can also be operated in endpoint mode. Drivers for endpoint mode didn't seem to fit in the "host" directory, so we put both the root complex and endpoint drivers in per-device directories, e.g., drivers/pci/dwc/, drivers/pci/cadence/, etc. These per-device directories contain trivial Kconfig and Makefiles and clutter drivers/pci/. Make a new drivers/pci/controllers/ directory and collect all the device-specific drivers there. No functional change intended. Link: https://lkml.kernel.org/r/1520304202-232891-1-git-send-email-shawn.lin@rock-chips.com Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com> [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'drivers/pci/controller/dwc/pci-layerscape.c')
-rw-r--r--drivers/pci/controller/dwc/pci-layerscape.c341
1 files changed, 341 insertions, 0 deletions
diff --git a/drivers/pci/controller/dwc/pci-layerscape.c b/drivers/pci/controller/dwc/pci-layerscape.c
new file mode 100644
index 000000000000..3724d3ef7008
--- /dev/null
+++ b/drivers/pci/controller/dwc/pci-layerscape.c
@@ -0,0 +1,341 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * PCIe host controller driver for Freescale Layerscape SoCs
4 *
5 * Copyright (C) 2014 Freescale Semiconductor.
6 *
7 * Author: Minghuan Lian <Minghuan.Lian@freescale.com>
8 */
9
10#include <linux/kernel.h>
11#include <linux/interrupt.h>
12#include <linux/init.h>
13#include <linux/of_pci.h>
14#include <linux/of_platform.h>
15#include <linux/of_irq.h>
16#include <linux/of_address.h>
17#include <linux/pci.h>
18#include <linux/platform_device.h>
19#include <linux/resource.h>
20#include <linux/mfd/syscon.h>
21#include <linux/regmap.h>
22
23#include "pcie-designware.h"
24
25/* PEX1/2 Misc Ports Status Register */
26#define SCFG_PEXMSCPORTSR(pex_idx) (0x94 + (pex_idx) * 4)
27#define LTSSM_STATE_SHIFT 20
28#define LTSSM_STATE_MASK 0x3f
29#define LTSSM_PCIE_L0 0x11 /* L0 state */
30
31/* PEX Internal Configuration Registers */
32#define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */
33#define PCIE_ABSERR 0x8d0 /* Bridge Slave Error Response Register */
34#define PCIE_ABSERR_SETTING 0x9401 /* Forward error of non-posted request */
35
36#define PCIE_IATU_NUM 6
37
38struct ls_pcie_drvdata {
39 u32 lut_offset;
40 u32 ltssm_shift;
41 u32 lut_dbg;
42 const struct dw_pcie_host_ops *ops;
43 const struct dw_pcie_ops *dw_pcie_ops;
44};
45
46struct ls_pcie {
47 struct dw_pcie *pci;
48 void __iomem *lut;
49 struct regmap *scfg;
50 const struct ls_pcie_drvdata *drvdata;
51 int index;
52};
53
54#define to_ls_pcie(x) dev_get_drvdata((x)->dev)
55
56static bool ls_pcie_is_bridge(struct ls_pcie *pcie)
57{
58 struct dw_pcie *pci = pcie->pci;
59 u32 header_type;
60
61 header_type = ioread8(pci->dbi_base + PCI_HEADER_TYPE);
62 header_type &= 0x7f;
63
64 return header_type == PCI_HEADER_TYPE_BRIDGE;
65}
66
67/* Clear multi-function bit */
68static void ls_pcie_clear_multifunction(struct ls_pcie *pcie)
69{
70 struct dw_pcie *pci = pcie->pci;
71
72 iowrite8(PCI_HEADER_TYPE_BRIDGE, pci->dbi_base + PCI_HEADER_TYPE);
73}
74
75/* Drop MSG TLP except for Vendor MSG */
76static void ls_pcie_drop_msg_tlp(struct ls_pcie *pcie)
77{
78 u32 val;
79 struct dw_pcie *pci = pcie->pci;
80
81 val = ioread32(pci->dbi_base + PCIE_STRFMR1);
82 val &= 0xDFFFFFFF;
83 iowrite32(val, pci->dbi_base + PCIE_STRFMR1);
84}
85
86static void ls_pcie_disable_outbound_atus(struct ls_pcie *pcie)
87{
88 int i;
89
90 for (i = 0; i < PCIE_IATU_NUM; i++)
91 dw_pcie_disable_atu(pcie->pci, DW_PCIE_REGION_OUTBOUND, i);
92}
93
94static int ls1021_pcie_link_up(struct dw_pcie *pci)
95{
96 u32 state;
97 struct ls_pcie *pcie = to_ls_pcie(pci);
98
99 if (!pcie->scfg)
100 return 0;
101
102 regmap_read(pcie->scfg, SCFG_PEXMSCPORTSR(pcie->index), &state);
103 state = (state >> LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK;
104
105 if (state < LTSSM_PCIE_L0)
106 return 0;
107
108 return 1;
109}
110
111static int ls_pcie_link_up(struct dw_pcie *pci)
112{
113 struct ls_pcie *pcie = to_ls_pcie(pci);
114 u32 state;
115
116 state = (ioread32(pcie->lut + pcie->drvdata->lut_dbg) >>
117 pcie->drvdata->ltssm_shift) &
118 LTSSM_STATE_MASK;
119
120 if (state < LTSSM_PCIE_L0)
121 return 0;
122
123 return 1;
124}
125
126/* Forward error response of outbound non-posted requests */
127static void ls_pcie_fix_error_response(struct ls_pcie *pcie)
128{
129 struct dw_pcie *pci = pcie->pci;
130
131 iowrite32(PCIE_ABSERR_SETTING, pci->dbi_base + PCIE_ABSERR);
132}
133
134static int ls_pcie_host_init(struct pcie_port *pp)
135{
136 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
137 struct ls_pcie *pcie = to_ls_pcie(pci);
138
139 /*
140 * Disable outbound windows configured by the bootloader to avoid
141 * one transaction hitting multiple outbound windows.
142 * dw_pcie_setup_rc() will reconfigure the outbound windows.
143 */
144 ls_pcie_disable_outbound_atus(pcie);
145 ls_pcie_fix_error_response(pcie);
146
147 dw_pcie_dbi_ro_wr_en(pci);
148 ls_pcie_clear_multifunction(pcie);
149 dw_pcie_dbi_ro_wr_dis(pci);
150
151 ls_pcie_drop_msg_tlp(pcie);
152
153 dw_pcie_setup_rc(pp);
154
155 return 0;
156}
157
158static int ls1021_pcie_host_init(struct pcie_port *pp)
159{
160 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
161 struct ls_pcie *pcie = to_ls_pcie(pci);
162 struct device *dev = pci->dev;
163 u32 index[2];
164 int ret;
165
166 pcie->scfg = syscon_regmap_lookup_by_phandle(dev->of_node,
167 "fsl,pcie-scfg");
168 if (IS_ERR(pcie->scfg)) {
169 ret = PTR_ERR(pcie->scfg);
170 dev_err(dev, "No syscfg phandle specified\n");
171 pcie->scfg = NULL;
172 return ret;
173 }
174
175 if (of_property_read_u32_array(dev->of_node,
176 "fsl,pcie-scfg", index, 2)) {
177 pcie->scfg = NULL;
178 return -EINVAL;
179 }
180 pcie->index = index[1];
181
182 return ls_pcie_host_init(pp);
183}
184
185static int ls_pcie_msi_host_init(struct pcie_port *pp)
186{
187 struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
188 struct device *dev = pci->dev;
189 struct device_node *np = dev->of_node;
190 struct device_node *msi_node;
191
192 /*
193 * The MSI domain is set by the generic of_msi_configure(). This
194 * .msi_host_init() function keeps us from doing the default MSI
195 * domain setup in dw_pcie_host_init() and also enforces the
196 * requirement that "msi-parent" exists.
197 */
198 msi_node = of_parse_phandle(np, "msi-parent", 0);
199 if (!msi_node) {
200 dev_err(dev, "failed to find msi-parent\n");
201 return -EINVAL;
202 }
203
204 return 0;
205}
206
207static const struct dw_pcie_host_ops ls1021_pcie_host_ops = {
208 .host_init = ls1021_pcie_host_init,
209 .msi_host_init = ls_pcie_msi_host_init,
210};
211
212static const struct dw_pcie_host_ops ls_pcie_host_ops = {
213 .host_init = ls_pcie_host_init,
214 .msi_host_init = ls_pcie_msi_host_init,
215};
216
217static const struct dw_pcie_ops dw_ls1021_pcie_ops = {
218 .link_up = ls1021_pcie_link_up,
219};
220
221static const struct dw_pcie_ops dw_ls_pcie_ops = {
222 .link_up = ls_pcie_link_up,
223};
224
225static struct ls_pcie_drvdata ls1021_drvdata = {
226 .ops = &ls1021_pcie_host_ops,
227 .dw_pcie_ops = &dw_ls1021_pcie_ops,
228};
229
230static struct ls_pcie_drvdata ls1043_drvdata = {
231 .lut_offset = 0x10000,
232 .ltssm_shift = 24,
233 .lut_dbg = 0x7fc,
234 .ops = &ls_pcie_host_ops,
235 .dw_pcie_ops = &dw_ls_pcie_ops,
236};
237
238static struct ls_pcie_drvdata ls1046_drvdata = {
239 .lut_offset = 0x80000,
240 .ltssm_shift = 24,
241 .lut_dbg = 0x407fc,
242 .ops = &ls_pcie_host_ops,
243 .dw_pcie_ops = &dw_ls_pcie_ops,
244};
245
246static struct ls_pcie_drvdata ls2080_drvdata = {
247 .lut_offset = 0x80000,
248 .ltssm_shift = 0,
249 .lut_dbg = 0x7fc,
250 .ops = &ls_pcie_host_ops,
251 .dw_pcie_ops = &dw_ls_pcie_ops,
252};
253
254static struct ls_pcie_drvdata ls2088_drvdata = {
255 .lut_offset = 0x80000,
256 .ltssm_shift = 0,
257 .lut_dbg = 0x407fc,
258 .ops = &ls_pcie_host_ops,
259 .dw_pcie_ops = &dw_ls_pcie_ops,
260};
261
262static const struct of_device_id ls_pcie_of_match[] = {
263 { .compatible = "fsl,ls1012a-pcie", .data = &ls1046_drvdata },
264 { .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata },
265 { .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata },
266 { .compatible = "fsl,ls1046a-pcie", .data = &ls1046_drvdata },
267 { .compatible = "fsl,ls2080a-pcie", .data = &ls2080_drvdata },
268 { .compatible = "fsl,ls2085a-pcie", .data = &ls2080_drvdata },
269 { .compatible = "fsl,ls2088a-pcie", .data = &ls2088_drvdata },
270 { .compatible = "fsl,ls1088a-pcie", .data = &ls2088_drvdata },
271 { },
272};
273
274static int __init ls_add_pcie_port(struct ls_pcie *pcie)
275{
276 struct dw_pcie *pci = pcie->pci;
277 struct pcie_port *pp = &pci->pp;
278 struct device *dev = pci->dev;
279 int ret;
280
281 pp->ops = pcie->drvdata->ops;
282
283 ret = dw_pcie_host_init(pp);
284 if (ret) {
285 dev_err(dev, "failed to initialize host\n");
286 return ret;
287 }
288
289 return 0;
290}
291
292static int __init ls_pcie_probe(struct platform_device *pdev)
293{
294 struct device *dev = &pdev->dev;
295 struct dw_pcie *pci;
296 struct ls_pcie *pcie;
297 struct resource *dbi_base;
298 int ret;
299
300 pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL);
301 if (!pcie)
302 return -ENOMEM;
303
304 pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL);
305 if (!pci)
306 return -ENOMEM;
307
308 pcie->drvdata = of_device_get_match_data(dev);
309
310 pci->dev = dev;
311 pci->ops = pcie->drvdata->dw_pcie_ops;
312
313 pcie->pci = pci;
314
315 dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
316 pci->dbi_base = devm_pci_remap_cfg_resource(dev, dbi_base);
317 if (IS_ERR(pci->dbi_base))
318 return PTR_ERR(pci->dbi_base);
319
320 pcie->lut = pci->dbi_base + pcie->drvdata->lut_offset;
321
322 if (!ls_pcie_is_bridge(pcie))
323 return -ENODEV;
324
325 platform_set_drvdata(pdev, pcie);
326
327 ret = ls_add_pcie_port(pcie);
328 if (ret < 0)
329 return ret;
330
331 return 0;
332}
333
334static struct platform_driver ls_pcie_driver = {
335 .driver = {
336 .name = "layerscape-pcie",
337 .of_match_table = ls_pcie_of_match,
338 .suppress_bind_attrs = true,
339 },
340};
341builtin_platform_driver_probe(ls_pcie_driver, ls_pcie_probe);