diff options
-rw-r--r-- | Documentation/devicetree/bindings/pci/pci-thunder-pem.txt | 43 | ||||
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | drivers/pci/host/Kconfig | 7 | ||||
-rw-r--r-- | drivers/pci/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/pci/host/pci-thunder-pem.c | 346 |
5 files changed, 405 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/pci/pci-thunder-pem.txt b/Documentation/devicetree/bindings/pci/pci-thunder-pem.txt new file mode 100644 index 000000000000..f131faea3b7c --- /dev/null +++ b/Documentation/devicetree/bindings/pci/pci-thunder-pem.txt | |||
@@ -0,0 +1,43 @@ | |||
1 | * ThunderX PEM PCIe host controller | ||
2 | |||
3 | Firmware-initialized PCI host controller found on some Cavium | ||
4 | ThunderX processors. | ||
5 | |||
6 | The properties and their meanings are identical to those described in | ||
7 | host-generic-pci.txt except as listed below. | ||
8 | |||
9 | Properties of the host controller node that differ from | ||
10 | host-generic-pci.txt: | ||
11 | |||
12 | - compatible : Must be "cavium,pci-host-thunder-pem" | ||
13 | |||
14 | - reg : Two entries: First the configuration space for down | ||
15 | stream devices base address and size, as accessed | ||
16 | from the parent bus. Second, the register bank of | ||
17 | the PEM device PCIe bridge. | ||
18 | |||
19 | Example: | ||
20 | |||
21 | pci@87e0,c2000000 { | ||
22 | compatible = "cavium,pci-host-thunder-pem"; | ||
23 | device_type = "pci"; | ||
24 | msi-parent = <&its>; | ||
25 | msi-map = <0 &its 0x10000 0x10000>; | ||
26 | bus-range = <0x8f 0xc7>; | ||
27 | #size-cells = <2>; | ||
28 | #address-cells = <3>; | ||
29 | |||
30 | reg = <0x8880 0x8f000000 0x0 0x39000000>, /* Configuration space */ | ||
31 | <0x87e0 0xc2000000 0x0 0x00010000>; /* PEM space */ | ||
32 | ranges = <0x01000000 0x00 0x00020000 0x88b0 0x00020000 0x00 0x00010000>, /* I/O */ | ||
33 | <0x03000000 0x00 0x10000000 0x8890 0x10000000 0x0f 0xf0000000>, /* mem64 */ | ||
34 | <0x43000000 0x10 0x00000000 0x88a0 0x00000000 0x10 0x00000000>, /* mem64-pref */ | ||
35 | <0x03000000 0x87e0 0xc2f00000 0x87e0 0xc2000000 0x00 0x00100000>; /* mem64 PEM BAR4 */ | ||
36 | |||
37 | #interrupt-cells = <1>; | ||
38 | interrupt-map-mask = <0 0 0 7>; | ||
39 | interrupt-map = <0 0 0 1 &gic0 0 0 0 24 4>, /* INTA */ | ||
40 | <0 0 0 2 &gic0 0 0 0 25 4>, /* INTB */ | ||
41 | <0 0 0 3 &gic0 0 0 0 26 4>, /* INTC */ | ||
42 | <0 0 0 4 &gic0 0 0 0 27 4>; /* INTD */ | ||
43 | }; | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 73c5bde6d167..1aa8f82837ea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -8419,6 +8419,14 @@ L: linux-arm-msm@vger.kernel.org | |||
8419 | S: Maintained | 8419 | S: Maintained |
8420 | F: drivers/pci/host/*qcom* | 8420 | F: drivers/pci/host/*qcom* |
8421 | 8421 | ||
8422 | PCIE DRIVER FOR CAVIUM THUNDERX | ||
8423 | M: David Daney <david.daney@cavium.com> | ||
8424 | L: linux-pci@vger.kernel.org | ||
8425 | L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) | ||
8426 | S: Supported | ||
8427 | F: Documentation/devicetree/bindings/pci/pci-thunder-* | ||
8428 | F: drivers/pci/host/pci-thunder-* | ||
8429 | |||
8422 | PCMCIA SUBSYSTEM | 8430 | PCMCIA SUBSYSTEM |
8423 | P: Linux PCMCIA Team | 8431 | P: Linux PCMCIA Team |
8424 | L: linux-pcmcia@lists.infradead.org | 8432 | L: linux-pcmcia@lists.infradead.org |
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 65709b4bb3a4..184df22e4a69 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig | |||
@@ -195,4 +195,11 @@ config PCIE_QCOM | |||
195 | PCIe controller uses the Designware core plus Qualcomm-specific | 195 | PCIe controller uses the Designware core plus Qualcomm-specific |
196 | hardware wrappers. | 196 | hardware wrappers. |
197 | 197 | ||
198 | config PCI_HOST_THUNDER_PEM | ||
199 | bool "Cavium Thunder PCIe controller to off-chip devices" | ||
200 | depends on OF && ARM64 | ||
201 | select PCI_HOST_COMMON | ||
202 | help | ||
203 | Say Y here if you want PCIe support for CN88XX Cavium Thunder SoCs. | ||
204 | |||
198 | endmenu | 205 | endmenu |
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 3b24af860284..890317234dae 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile | |||
@@ -23,3 +23,4 @@ obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o | |||
23 | obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o | 23 | obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o |
24 | obj-$(CONFIG_PCI_HISI) += pcie-hisi.o | 24 | obj-$(CONFIG_PCI_HISI) += pcie-hisi.o |
25 | obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o | 25 | obj-$(CONFIG_PCIE_QCOM) += pcie-qcom.o |
26 | obj-$(CONFIG_PCI_HOST_THUNDER_PEM) += pci-thunder-pem.o | ||
diff --git a/drivers/pci/host/pci-thunder-pem.c b/drivers/pci/host/pci-thunder-pem.c new file mode 100644 index 000000000000..cabb92a514ac --- /dev/null +++ b/drivers/pci/host/pci-thunder-pem.c | |||
@@ -0,0 +1,346 @@ | |||
1 | /* | ||
2 | * This program is free software; you can redistribute it and/or modify | ||
3 | * it under the terms of the GNU General Public License version 2 as | ||
4 | * published by the Free Software Foundation. | ||
5 | * | ||
6 | * This program is distributed in the hope that it will be useful, | ||
7 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
8 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
9 | * GNU General Public License for more details. | ||
10 | * | ||
11 | * You should have received a copy of the GNU General Public License | ||
12 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
13 | * | ||
14 | * Copyright (C) 2015 - 2016 Cavium, Inc. | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/of_address.h> | ||
20 | #include <linux/of_pci.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | |||
23 | #include "pci-host-common.h" | ||
24 | |||
25 | #define PEM_CFG_WR 0x28 | ||
26 | #define PEM_CFG_RD 0x30 | ||
27 | |||
28 | struct thunder_pem_pci { | ||
29 | struct gen_pci gen_pci; | ||
30 | u32 ea_entry[3]; | ||
31 | void __iomem *pem_reg_base; | ||
32 | }; | ||
33 | |||
34 | static void __iomem *thunder_pem_map_bus(struct pci_bus *bus, | ||
35 | unsigned int devfn, int where) | ||
36 | { | ||
37 | struct gen_pci *pci = bus->sysdata; | ||
38 | resource_size_t idx = bus->number - pci->cfg.bus_range->start; | ||
39 | |||
40 | return pci->cfg.win[idx] + ((devfn << 16) | where); | ||
41 | } | ||
42 | |||
43 | static int thunder_pem_bridge_read(struct pci_bus *bus, unsigned int devfn, | ||
44 | int where, int size, u32 *val) | ||
45 | { | ||
46 | u64 read_val; | ||
47 | struct thunder_pem_pci *pem_pci; | ||
48 | struct gen_pci *pci = bus->sysdata; | ||
49 | |||
50 | pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci); | ||
51 | |||
52 | if (devfn != 0 || where >= 2048) { | ||
53 | *val = ~0; | ||
54 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
55 | } | ||
56 | |||
57 | /* | ||
58 | * 32-bit accesses only. Write the address to the low order | ||
59 | * bits of PEM_CFG_RD, then trigger the read by reading back. | ||
60 | * The config data lands in the upper 32-bits of PEM_CFG_RD. | ||
61 | */ | ||
62 | read_val = where & ~3ull; | ||
63 | writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD); | ||
64 | read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); | ||
65 | read_val >>= 32; | ||
66 | |||
67 | /* | ||
68 | * The config space contains some garbage, fix it up. Also | ||
69 | * synthesize an EA capability for the BAR used by MSI-X. | ||
70 | */ | ||
71 | switch (where & ~3) { | ||
72 | case 0x40: | ||
73 | read_val &= 0xffff00ff; | ||
74 | read_val |= 0x00007000; /* Skip MSI CAP */ | ||
75 | break; | ||
76 | case 0x70: /* Express Cap */ | ||
77 | /* PME interrupt on vector 2*/ | ||
78 | read_val |= (2u << 25); | ||
79 | break; | ||
80 | case 0xb0: /* MSI-X Cap */ | ||
81 | /* TableSize=4, Next Cap is EA */ | ||
82 | read_val &= 0xc00000ff; | ||
83 | read_val |= 0x0003bc00; | ||
84 | break; | ||
85 | case 0xb4: | ||
86 | /* Table offset=0, BIR=0 */ | ||
87 | read_val = 0x00000000; | ||
88 | break; | ||
89 | case 0xb8: | ||
90 | /* BPA offset=0xf0000, BIR=0 */ | ||
91 | read_val = 0x000f0000; | ||
92 | break; | ||
93 | case 0xbc: | ||
94 | /* EA, 1 entry, no next Cap */ | ||
95 | read_val = 0x00010014; | ||
96 | break; | ||
97 | case 0xc0: | ||
98 | /* DW2 for type-1 */ | ||
99 | read_val = 0x00000000; | ||
100 | break; | ||
101 | case 0xc4: | ||
102 | /* Entry BEI=0, PP=0x00, SP=0xff, ES=3 */ | ||
103 | read_val = 0x80ff0003; | ||
104 | break; | ||
105 | case 0xc8: | ||
106 | read_val = pem_pci->ea_entry[0]; | ||
107 | break; | ||
108 | case 0xcc: | ||
109 | read_val = pem_pci->ea_entry[1]; | ||
110 | break; | ||
111 | case 0xd0: | ||
112 | read_val = pem_pci->ea_entry[2]; | ||
113 | break; | ||
114 | default: | ||
115 | break; | ||
116 | } | ||
117 | read_val >>= (8 * (where & 3)); | ||
118 | switch (size) { | ||
119 | case 1: | ||
120 | read_val &= 0xff; | ||
121 | break; | ||
122 | case 2: | ||
123 | read_val &= 0xffff; | ||
124 | break; | ||
125 | default: | ||
126 | break; | ||
127 | } | ||
128 | *val = read_val; | ||
129 | return PCIBIOS_SUCCESSFUL; | ||
130 | } | ||
131 | |||
132 | static int thunder_pem_config_read(struct pci_bus *bus, unsigned int devfn, | ||
133 | int where, int size, u32 *val) | ||
134 | { | ||
135 | struct gen_pci *pci = bus->sysdata; | ||
136 | |||
137 | if (bus->number < pci->cfg.bus_range->start || | ||
138 | bus->number > pci->cfg.bus_range->end) | ||
139 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
140 | |||
141 | /* | ||
142 | * The first device on the bus is the PEM PCIe bridge. | ||
143 | * Special case its config access. | ||
144 | */ | ||
145 | if (bus->number == pci->cfg.bus_range->start) | ||
146 | return thunder_pem_bridge_read(bus, devfn, where, size, val); | ||
147 | |||
148 | return pci_generic_config_read(bus, devfn, where, size, val); | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * Some of the w1c_bits below also include read-only or non-writable | ||
153 | * reserved bits, this makes the code simpler and is OK as the bits | ||
154 | * are not affected by writing zeros to them. | ||
155 | */ | ||
156 | static u32 thunder_pem_bridge_w1c_bits(int where) | ||
157 | { | ||
158 | u32 w1c_bits = 0; | ||
159 | |||
160 | switch (where & ~3) { | ||
161 | case 0x04: /* Command/Status */ | ||
162 | case 0x1c: /* Base and I/O Limit/Secondary Status */ | ||
163 | w1c_bits = 0xff000000; | ||
164 | break; | ||
165 | case 0x44: /* Power Management Control and Status */ | ||
166 | w1c_bits = 0xfffffe00; | ||
167 | break; | ||
168 | case 0x78: /* Device Control/Device Status */ | ||
169 | case 0x80: /* Link Control/Link Status */ | ||
170 | case 0x88: /* Slot Control/Slot Status */ | ||
171 | case 0x90: /* Root Status */ | ||
172 | case 0xa0: /* Link Control 2 Registers/Link Status 2 */ | ||
173 | w1c_bits = 0xffff0000; | ||
174 | break; | ||
175 | case 0x104: /* Uncorrectable Error Status */ | ||
176 | case 0x110: /* Correctable Error Status */ | ||
177 | case 0x130: /* Error Status */ | ||
178 | case 0x160: /* Link Control 4 */ | ||
179 | w1c_bits = 0xffffffff; | ||
180 | break; | ||
181 | default: | ||
182 | break; | ||
183 | } | ||
184 | return w1c_bits; | ||
185 | } | ||
186 | |||
187 | static int thunder_pem_bridge_write(struct pci_bus *bus, unsigned int devfn, | ||
188 | int where, int size, u32 val) | ||
189 | { | ||
190 | struct gen_pci *pci = bus->sysdata; | ||
191 | struct thunder_pem_pci *pem_pci; | ||
192 | u64 write_val, read_val; | ||
193 | u32 mask = 0; | ||
194 | |||
195 | pem_pci = container_of(pci, struct thunder_pem_pci, gen_pci); | ||
196 | |||
197 | if (devfn != 0 || where >= 2048) | ||
198 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
199 | |||
200 | /* | ||
201 | * 32-bit accesses only. If the write is for a size smaller | ||
202 | * than 32-bits, we must first read the 32-bit value and merge | ||
203 | * in the desired bits and then write the whole 32-bits back | ||
204 | * out. | ||
205 | */ | ||
206 | switch (size) { | ||
207 | case 1: | ||
208 | read_val = where & ~3ull; | ||
209 | writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD); | ||
210 | read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); | ||
211 | read_val >>= 32; | ||
212 | mask = ~(0xff << (8 * (where & 3))); | ||
213 | read_val &= mask; | ||
214 | val = (val & 0xff) << (8 * (where & 3)); | ||
215 | val |= (u32)read_val; | ||
216 | break; | ||
217 | case 2: | ||
218 | read_val = where & ~3ull; | ||
219 | writeq(read_val, pem_pci->pem_reg_base + PEM_CFG_RD); | ||
220 | read_val = readq(pem_pci->pem_reg_base + PEM_CFG_RD); | ||
221 | read_val >>= 32; | ||
222 | mask = ~(0xffff << (8 * (where & 3))); | ||
223 | read_val &= mask; | ||
224 | val = (val & 0xffff) << (8 * (where & 3)); | ||
225 | val |= (u32)read_val; | ||
226 | break; | ||
227 | default: | ||
228 | break; | ||
229 | } | ||
230 | |||
231 | /* | ||
232 | * By expanding the write width to 32 bits, we may | ||
233 | * inadvertently hit some W1C bits that were not intended to | ||
234 | * be written. Calculate the mask that must be applied to the | ||
235 | * data to be written to avoid these cases. | ||
236 | */ | ||
237 | if (mask) { | ||
238 | u32 w1c_bits = thunder_pem_bridge_w1c_bits(where); | ||
239 | |||
240 | if (w1c_bits) { | ||
241 | mask &= w1c_bits; | ||
242 | val &= ~mask; | ||
243 | } | ||
244 | } | ||
245 | |||
246 | /* | ||
247 | * Low order bits are the config address, the high order 32 | ||
248 | * bits are the data to be written. | ||
249 | */ | ||
250 | write_val = where & ~3ull; | ||
251 | write_val |= (((u64)val) << 32); | ||
252 | writeq(write_val, pem_pci->pem_reg_base + PEM_CFG_WR); | ||
253 | return PCIBIOS_SUCCESSFUL; | ||
254 | } | ||
255 | |||
256 | static int thunder_pem_config_write(struct pci_bus *bus, unsigned int devfn, | ||
257 | int where, int size, u32 val) | ||
258 | { | ||
259 | struct gen_pci *pci = bus->sysdata; | ||
260 | |||
261 | if (bus->number < pci->cfg.bus_range->start || | ||
262 | bus->number > pci->cfg.bus_range->end) | ||
263 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
264 | /* | ||
265 | * The first device on the bus is the PEM PCIe bridge. | ||
266 | * Special case its config access. | ||
267 | */ | ||
268 | if (bus->number == pci->cfg.bus_range->start) | ||
269 | return thunder_pem_bridge_write(bus, devfn, where, size, val); | ||
270 | |||
271 | |||
272 | return pci_generic_config_write(bus, devfn, where, size, val); | ||
273 | } | ||
274 | |||
275 | static struct gen_pci_cfg_bus_ops thunder_pem_bus_ops = { | ||
276 | .bus_shift = 24, | ||
277 | .ops = { | ||
278 | .map_bus = thunder_pem_map_bus, | ||
279 | .read = thunder_pem_config_read, | ||
280 | .write = thunder_pem_config_write, | ||
281 | } | ||
282 | }; | ||
283 | |||
284 | static const struct of_device_id thunder_pem_of_match[] = { | ||
285 | { .compatible = "cavium,pci-host-thunder-pem", | ||
286 | .data = &thunder_pem_bus_ops }, | ||
287 | |||
288 | { }, | ||
289 | }; | ||
290 | MODULE_DEVICE_TABLE(of, thunder_pem_of_match); | ||
291 | |||
292 | static int thunder_pem_probe(struct platform_device *pdev) | ||
293 | { | ||
294 | struct device *dev = &pdev->dev; | ||
295 | const struct of_device_id *of_id; | ||
296 | resource_size_t bar4_start; | ||
297 | struct resource *res_pem; | ||
298 | struct thunder_pem_pci *pem_pci; | ||
299 | |||
300 | pem_pci = devm_kzalloc(dev, sizeof(*pem_pci), GFP_KERNEL); | ||
301 | if (!pem_pci) | ||
302 | return -ENOMEM; | ||
303 | |||
304 | of_id = of_match_node(thunder_pem_of_match, dev->of_node); | ||
305 | pem_pci->gen_pci.cfg.ops = (struct gen_pci_cfg_bus_ops *)of_id->data; | ||
306 | |||
307 | /* | ||
308 | * The second register range is the PEM bridge to the PCIe | ||
309 | * bus. It has a different config access method than those | ||
310 | * devices behind the bridge. | ||
311 | */ | ||
312 | res_pem = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
313 | if (!res_pem) { | ||
314 | dev_err(dev, "missing \"reg[1]\"property\n"); | ||
315 | return -EINVAL; | ||
316 | } | ||
317 | |||
318 | pem_pci->pem_reg_base = devm_ioremap(dev, res_pem->start, 0x10000); | ||
319 | if (!pem_pci->pem_reg_base) | ||
320 | return -ENOMEM; | ||
321 | |||
322 | /* | ||
323 | * The MSI-X BAR for the PEM and AER interrupts is located at | ||
324 | * a fixed offset from the PEM register base. Generate a | ||
325 | * fragment of the synthesized Enhanced Allocation capability | ||
326 | * structure here for the BAR. | ||
327 | */ | ||
328 | bar4_start = res_pem->start + 0xf00000; | ||
329 | pem_pci->ea_entry[0] = (u32)bar4_start | 2; | ||
330 | pem_pci->ea_entry[1] = (u32)(res_pem->end - bar4_start) & ~3u; | ||
331 | pem_pci->ea_entry[2] = (u32)(bar4_start >> 32); | ||
332 | |||
333 | return pci_host_common_probe(pdev, &pem_pci->gen_pci); | ||
334 | } | ||
335 | |||
336 | static struct platform_driver thunder_pem_driver = { | ||
337 | .driver = { | ||
338 | .name = KBUILD_MODNAME, | ||
339 | .of_match_table = thunder_pem_of_match, | ||
340 | }, | ||
341 | .probe = thunder_pem_probe, | ||
342 | }; | ||
343 | module_platform_driver(thunder_pem_driver); | ||
344 | |||
345 | MODULE_DESCRIPTION("Thunder PEM PCIe host driver"); | ||
346 | MODULE_LICENSE("GPL v2"); | ||