Commit | Line | Data |
---|---|---|
500a1d9a | 1 | /* |
5930fe4e | 2 | * PCIe host controller driver for HiSilicon SoCs |
500a1d9a ZW |
3 | * |
4 | * Copyright (C) 2015 HiSilicon Co., Ltd. http://www.hisilicon.com | |
5 | * | |
5930fe4e GP |
6 | * Authors: Zhou Wang <wangzhou1@hisilicon.com> |
7 | * Dacai Zhu <zhudacai@hisilicon.com> | |
8 | * Gabriele Paoloni <gabriele.paoloni@huawei.com> | |
500a1d9a ZW |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | */ | |
14 | #include <linux/interrupt.h> | |
fb38118d | 15 | #include <linux/init.h> |
500a1d9a ZW |
16 | #include <linux/mfd/syscon.h> |
17 | #include <linux/of_address.h> | |
18 | #include <linux/of_pci.h> | |
19 | #include <linux/platform_device.h> | |
5930fe4e | 20 | #include <linux/of_device.h> |
5f00f1a0 DL |
21 | #include <linux/pci.h> |
22 | #include <linux/pci-acpi.h> | |
23 | #include <linux/pci-ecam.h> | |
500a1d9a | 24 | #include <linux/regmap.h> |
5f00f1a0 DL |
25 | #include "../pci.h" |
26 | ||
a2ec1996 | 27 | #if defined(CONFIG_PCI_HISI) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) |
5f00f1a0 | 28 | |
4788316f BH |
29 | static int hisi_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, |
30 | int size, u32 *val) | |
5f00f1a0 DL |
31 | { |
32 | struct pci_config_window *cfg = bus->sysdata; | |
33 | int dev = PCI_SLOT(devfn); | |
34 | ||
35 | if (bus->number == cfg->busr.start) { | |
36 | /* access only one slot on each root port */ | |
37 | if (dev > 0) | |
38 | return PCIBIOS_DEVICE_NOT_FOUND; | |
39 | else | |
40 | return pci_generic_config_read32(bus, devfn, where, | |
41 | size, val); | |
42 | } | |
43 | ||
44 | return pci_generic_config_read(bus, devfn, where, size, val); | |
45 | } | |
46 | ||
4788316f BH |
47 | static int hisi_pcie_wr_conf(struct pci_bus *bus, u32 devfn, |
48 | int where, int size, u32 val) | |
5f00f1a0 DL |
49 | { |
50 | struct pci_config_window *cfg = bus->sysdata; | |
51 | int dev = PCI_SLOT(devfn); | |
52 | ||
53 | if (bus->number == cfg->busr.start) { | |
54 | /* access only one slot on each root port */ | |
55 | if (dev > 0) | |
56 | return PCIBIOS_DEVICE_NOT_FOUND; | |
57 | else | |
58 | return pci_generic_config_write32(bus, devfn, where, | |
59 | size, val); | |
60 | } | |
61 | ||
62 | return pci_generic_config_write(bus, devfn, where, size, val); | |
63 | } | |
64 | ||
65 | static void __iomem *hisi_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, | |
66 | int where) | |
67 | { | |
68 | struct pci_config_window *cfg = bus->sysdata; | |
69 | void __iomem *reg_base = cfg->priv; | |
70 | ||
71 | if (bus->number == cfg->busr.start) | |
72 | return reg_base + where; | |
73 | else | |
74 | return pci_ecam_map_bus(bus, devfn, where); | |
75 | } | |
76 | ||
a2ec1996 DL |
77 | #if defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS) |
78 | ||
5f00f1a0 DL |
79 | static int hisi_pcie_init(struct pci_config_window *cfg) |
80 | { | |
81 | struct device *dev = cfg->parent; | |
82 | struct acpi_device *adev = to_acpi_device(dev); | |
83 | struct acpi_pci_root *root = acpi_driver_data(adev); | |
84 | struct resource *res; | |
85 | void __iomem *reg_base; | |
86 | int ret; | |
87 | ||
88 | /* | |
89 | * Retrieve RC base and size from a HISI0081 device with _UID | |
90 | * matching our segment. | |
91 | */ | |
92 | res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); | |
93 | if (!res) | |
94 | return -ENOMEM; | |
95 | ||
96 | ret = acpi_get_rc_resources(dev, "HISI0081", root->segment, res); | |
97 | if (ret) { | |
98 | dev_err(dev, "can't get rc base address\n"); | |
99 | return -ENOMEM; | |
100 | } | |
101 | ||
102 | reg_base = devm_ioremap(dev, res->start, resource_size(res)); | |
103 | if (!reg_base) | |
104 | return -ENOMEM; | |
105 | ||
106 | cfg->priv = reg_base; | |
107 | return 0; | |
108 | } | |
109 | ||
110 | struct pci_ecam_ops hisi_pcie_ops = { | |
111 | .bus_shift = 20, | |
112 | .init = hisi_pcie_init, | |
113 | .pci_ops = { | |
114 | .map_bus = hisi_pcie_map_bus, | |
4788316f BH |
115 | .read = hisi_pcie_rd_conf, |
116 | .write = hisi_pcie_wr_conf, | |
5f00f1a0 DL |
117 | } |
118 | }; | |
119 | ||
120 | #endif | |
121 | ||
122 | #ifdef CONFIG_PCI_HISI | |
500a1d9a ZW |
123 | |
124 | #include "pcie-designware.h" | |
125 | ||
a458ce33 BH |
126 | #define PCIE_SUBCTRL_SYS_STATE4_REG 0x6818 |
127 | #define PCIE_HIP06_CTRL_OFF 0x1000 | |
128 | #define PCIE_SYS_STATE4 (PCIE_HIP06_CTRL_OFF + 0x31c) | |
129 | #define PCIE_LTSSM_LINKUP_STATE 0x11 | |
130 | #define PCIE_LTSSM_STATE_MASK 0x3F | |
500a1d9a | 131 | |
442ec4c0 | 132 | #define to_hisi_pcie(x) dev_get_drvdata((x)->dev) |
500a1d9a | 133 | |
5930fe4e GP |
134 | struct hisi_pcie; |
135 | ||
136 | struct pcie_soc_ops { | |
bf4ed37c | 137 | int (*hisi_pcie_link_up)(struct hisi_pcie *hisi_pcie); |
5930fe4e GP |
138 | }; |
139 | ||
500a1d9a | 140 | struct hisi_pcie { |
442ec4c0 | 141 | struct dw_pcie *pci; |
500a1d9a | 142 | struct regmap *subctrl; |
500a1d9a | 143 | u32 port_id; |
792e0a68 | 144 | const struct pcie_soc_ops *soc_ops; |
500a1d9a ZW |
145 | }; |
146 | ||
5930fe4e | 147 | /* HipXX PCIe host only supports 32-bit config access */ |
500a1d9a ZW |
148 | static int hisi_pcie_cfg_read(struct pcie_port *pp, int where, int size, |
149 | u32 *val) | |
150 | { | |
151 | u32 reg; | |
152 | u32 reg_val; | |
500a1d9a | 153 | void *walker = ®_val; |
442ec4c0 | 154 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); |
500a1d9a ZW |
155 | |
156 | walker += (where & 0x3); | |
157 | reg = where & ~0x3; | |
442ec4c0 | 158 | reg_val = dw_pcie_readl_dbi(pci, reg); |
500a1d9a ZW |
159 | |
160 | if (size == 1) | |
161 | *val = *(u8 __force *) walker; | |
162 | else if (size == 2) | |
163 | *val = *(u16 __force *) walker; | |
1dbe162d DL |
164 | else if (size == 4) |
165 | *val = reg_val; | |
166 | else | |
500a1d9a ZW |
167 | return PCIBIOS_BAD_REGISTER_NUMBER; |
168 | ||
169 | return PCIBIOS_SUCCESSFUL; | |
170 | } | |
171 | ||
5930fe4e | 172 | /* HipXX PCIe host only supports 32-bit config access */ |
500a1d9a ZW |
173 | static int hisi_pcie_cfg_write(struct pcie_port *pp, int where, int size, |
174 | u32 val) | |
175 | { | |
176 | u32 reg_val; | |
177 | u32 reg; | |
500a1d9a | 178 | void *walker = ®_val; |
442ec4c0 | 179 | struct dw_pcie *pci = to_dw_pcie_from_pp(pp); |
500a1d9a ZW |
180 | |
181 | walker += (where & 0x3); | |
182 | reg = where & ~0x3; | |
183 | if (size == 4) | |
442ec4c0 | 184 | dw_pcie_writel_dbi(pci, reg, val); |
500a1d9a | 185 | else if (size == 2) { |
442ec4c0 | 186 | reg_val = dw_pcie_readl_dbi(pci, reg); |
500a1d9a | 187 | *(u16 __force *) walker = val; |
442ec4c0 | 188 | dw_pcie_writel_dbi(pci, reg, reg_val); |
500a1d9a | 189 | } else if (size == 1) { |
442ec4c0 | 190 | reg_val = dw_pcie_readl_dbi(pci, reg); |
500a1d9a | 191 | *(u8 __force *) walker = val; |
442ec4c0 | 192 | dw_pcie_writel_dbi(pci, reg, reg_val); |
500a1d9a ZW |
193 | } else |
194 | return PCIBIOS_BAD_REGISTER_NUMBER; | |
195 | ||
196 | return PCIBIOS_SUCCESSFUL; | |
197 | } | |
198 | ||
5930fe4e | 199 | static int hisi_pcie_link_up_hip05(struct hisi_pcie *hisi_pcie) |
500a1d9a ZW |
200 | { |
201 | u32 val; | |
500a1d9a ZW |
202 | |
203 | regmap_read(hisi_pcie->subctrl, PCIE_SUBCTRL_SYS_STATE4_REG + | |
204 | 0x100 * hisi_pcie->port_id, &val); | |
205 | ||
206 | return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); | |
207 | } | |
208 | ||
5930fe4e GP |
209 | static int hisi_pcie_link_up_hip06(struct hisi_pcie *hisi_pcie) |
210 | { | |
442ec4c0 | 211 | struct dw_pcie *pci = hisi_pcie->pci; |
5930fe4e GP |
212 | u32 val; |
213 | ||
442ec4c0 | 214 | val = dw_pcie_readl_dbi(pci, PCIE_SYS_STATE4); |
5930fe4e GP |
215 | |
216 | return ((val & PCIE_LTSSM_STATE_MASK) == PCIE_LTSSM_LINKUP_STATE); | |
217 | } | |
218 | ||
442ec4c0 | 219 | static int hisi_pcie_link_up(struct dw_pcie *pci) |
5930fe4e | 220 | { |
442ec4c0 | 221 | struct hisi_pcie *hisi_pcie = to_hisi_pcie(pci); |
5930fe4e GP |
222 | |
223 | return hisi_pcie->soc_ops->hisi_pcie_link_up(hisi_pcie); | |
224 | } | |
225 | ||
442ec4c0 | 226 | static struct dw_pcie_host_ops hisi_pcie_host_ops = { |
500a1d9a ZW |
227 | .rd_own_conf = hisi_pcie_cfg_read, |
228 | .wr_own_conf = hisi_pcie_cfg_write, | |
500a1d9a ZW |
229 | }; |
230 | ||
e9480b5a BH |
231 | static int hisi_add_pcie_port(struct hisi_pcie *hisi_pcie, |
232 | struct platform_device *pdev) | |
500a1d9a | 233 | { |
442ec4c0 KVA |
234 | struct dw_pcie *pci = hisi_pcie->pci; |
235 | struct pcie_port *pp = &pci->pp; | |
236 | struct device *dev = &pdev->dev; | |
500a1d9a ZW |
237 | int ret; |
238 | u32 port_id; | |
500a1d9a | 239 | |
88790f99 BH |
240 | if (of_property_read_u32(dev->of_node, "port-id", &port_id)) { |
241 | dev_err(dev, "failed to read port-id\n"); | |
500a1d9a ZW |
242 | return -EINVAL; |
243 | } | |
244 | if (port_id > 3) { | |
88790f99 | 245 | dev_err(dev, "Invalid port-id: %d\n", port_id); |
500a1d9a ZW |
246 | return -EINVAL; |
247 | } | |
248 | hisi_pcie->port_id = port_id; | |
249 | ||
250 | pp->ops = &hisi_pcie_host_ops; | |
251 | ||
252 | ret = dw_pcie_host_init(pp); | |
253 | if (ret) { | |
88790f99 | 254 | dev_err(dev, "failed to initialize host\n"); |
500a1d9a ZW |
255 | return ret; |
256 | } | |
257 | ||
258 | return 0; | |
259 | } | |
260 | ||
442ec4c0 KVA |
261 | static const struct dw_pcie_ops dw_pcie_ops = { |
262 | .link_up = hisi_pcie_link_up, | |
263 | }; | |
264 | ||
9f55cf56 | 265 | static int hisi_pcie_probe(struct platform_device *pdev) |
500a1d9a | 266 | { |
88790f99 | 267 | struct device *dev = &pdev->dev; |
442ec4c0 | 268 | struct dw_pcie *pci; |
500a1d9a | 269 | struct hisi_pcie *hisi_pcie; |
500a1d9a | 270 | struct resource *reg; |
5930fe4e | 271 | struct device_driver *driver; |
500a1d9a ZW |
272 | int ret; |
273 | ||
88790f99 | 274 | hisi_pcie = devm_kzalloc(dev, sizeof(*hisi_pcie), GFP_KERNEL); |
500a1d9a ZW |
275 | if (!hisi_pcie) |
276 | return -ENOMEM; | |
277 | ||
442ec4c0 KVA |
278 | pci = devm_kzalloc(dev, sizeof(*pci), GFP_KERNEL); |
279 | if (!pci) | |
280 | return -ENOMEM; | |
281 | ||
282 | pci->dev = dev; | |
283 | pci->ops = &dw_pcie_ops; | |
284 | ||
88790f99 | 285 | driver = dev->driver; |
5930fe4e | 286 | |
792e0a68 | 287 | hisi_pcie->soc_ops = of_device_get_match_data(dev); |
500a1d9a ZW |
288 | |
289 | hisi_pcie->subctrl = | |
792e0a68 | 290 | syscon_regmap_lookup_by_compatible("hisilicon,pcie-sas-subctrl"); |
500a1d9a | 291 | if (IS_ERR(hisi_pcie->subctrl)) { |
88790f99 | 292 | dev_err(dev, "cannot get subctrl base\n"); |
500a1d9a ZW |
293 | return PTR_ERR(hisi_pcie->subctrl); |
294 | } | |
295 | ||
296 | reg = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rc_dbi"); | |
442ec4c0 KVA |
297 | pci->dbi_base = devm_ioremap_resource(dev, reg); |
298 | if (IS_ERR(pci->dbi_base)) | |
299 | return PTR_ERR(pci->dbi_base); | |
500a1d9a | 300 | |
9bcf0a6f KVA |
301 | platform_set_drvdata(pdev, hisi_pcie); |
302 | ||
e9480b5a | 303 | ret = hisi_add_pcie_port(hisi_pcie, pdev); |
500a1d9a ZW |
304 | if (ret) |
305 | return ret; | |
306 | ||
500a1d9a ZW |
307 | return 0; |
308 | } | |
309 | ||
5930fe4e GP |
310 | static struct pcie_soc_ops hip05_ops = { |
311 | &hisi_pcie_link_up_hip05 | |
312 | }; | |
313 | ||
314 | static struct pcie_soc_ops hip06_ops = { | |
315 | &hisi_pcie_link_up_hip06 | |
316 | }; | |
317 | ||
500a1d9a | 318 | static const struct of_device_id hisi_pcie_of_match[] = { |
5930fe4e GP |
319 | { |
320 | .compatible = "hisilicon,hip05-pcie", | |
321 | .data = (void *) &hip05_ops, | |
322 | }, | |
323 | { | |
324 | .compatible = "hisilicon,hip06-pcie", | |
325 | .data = (void *) &hip06_ops, | |
326 | }, | |
500a1d9a ZW |
327 | {}, |
328 | }; | |
329 | ||
500a1d9a ZW |
330 | static struct platform_driver hisi_pcie_driver = { |
331 | .probe = hisi_pcie_probe, | |
332 | .driver = { | |
333 | .name = "hisi-pcie", | |
334 | .of_match_table = hisi_pcie_of_match, | |
335 | }, | |
336 | }; | |
fb38118d | 337 | builtin_platform_driver(hisi_pcie_driver); |
5f00f1a0 | 338 | |
a2ec1996 DL |
339 | static int hisi_pcie_almost_ecam_probe(struct platform_device *pdev) |
340 | { | |
341 | struct device *dev = &pdev->dev; | |
342 | struct pci_ecam_ops *ops; | |
343 | ||
344 | ops = (struct pci_ecam_ops *)of_device_get_match_data(dev); | |
345 | return pci_host_common_probe(pdev, ops); | |
346 | } | |
347 | ||
348 | static int hisi_pcie_platform_init(struct pci_config_window *cfg) | |
349 | { | |
350 | struct device *dev = cfg->parent; | |
351 | struct platform_device *pdev = to_platform_device(dev); | |
352 | struct resource *res; | |
353 | void __iomem *reg_base; | |
354 | ||
355 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | |
356 | if (!res) { | |
357 | dev_err(dev, "missing \"reg[1]\"property\n"); | |
358 | return -EINVAL; | |
359 | } | |
360 | ||
361 | reg_base = devm_ioremap(dev, res->start, resource_size(res)); | |
362 | if (!reg_base) | |
363 | return -ENOMEM; | |
364 | ||
365 | cfg->priv = reg_base; | |
366 | return 0; | |
367 | } | |
368 | ||
369 | struct pci_ecam_ops hisi_pcie_platform_ops = { | |
370 | .bus_shift = 20, | |
371 | .init = hisi_pcie_platform_init, | |
372 | .pci_ops = { | |
373 | .map_bus = hisi_pcie_map_bus, | |
4788316f BH |
374 | .read = hisi_pcie_rd_conf, |
375 | .write = hisi_pcie_wr_conf, | |
a2ec1996 DL |
376 | } |
377 | }; | |
378 | ||
379 | static const struct of_device_id hisi_pcie_almost_ecam_of_match[] = { | |
380 | { | |
381 | .compatible = "hisilicon,pcie-almost-ecam", | |
382 | .data = (void *) &hisi_pcie_platform_ops, | |
383 | }, | |
384 | {}, | |
385 | }; | |
386 | ||
387 | static struct platform_driver hisi_pcie_almost_ecam_driver = { | |
388 | .probe = hisi_pcie_almost_ecam_probe, | |
389 | .driver = { | |
390 | .name = "hisi-pcie-almost-ecam", | |
391 | .of_match_table = hisi_pcie_almost_ecam_of_match, | |
392 | }, | |
393 | }; | |
394 | builtin_platform_driver(hisi_pcie_almost_ecam_driver); | |
395 | ||
396 | #endif | |
5f00f1a0 | 397 | #endif |