Commit | Line | Data |
---|---|---|
e1e86ee0 | 1 | // SPDX-License-Identifier: GPL-2.0 |
7b6e7ba8 | 2 | /* |
7b6e7ba8 DD |
3 | * Copyright (C) 2015, 2016 Cavium, Inc. |
4 | */ | |
5 | ||
6 | #include <linux/kernel.h> | |
d0c6fd76 | 7 | #include <linux/init.h> |
7b6e7ba8 DD |
8 | #include <linux/ioport.h> |
9 | #include <linux/of_pci.h> | |
10 | #include <linux/of.h> | |
80955f9e | 11 | #include <linux/pci-ecam.h> |
7b6e7ba8 DD |
12 | #include <linux/platform_device.h> |
13 | ||
648d93fc TN |
14 | #if defined(CONFIG_PCI_HOST_THUNDER_ECAM) || (defined(CONFIG_ACPI) && defined(CONFIG_PCI_QUIRKS)) |
15 | ||
7b6e7ba8 DD |
16 | static void set_val(u32 v, int where, int size, u32 *val) |
17 | { | |
18 | int shift = (where & 3) * 8; | |
19 | ||
fd1ae23b | 20 | pr_debug("set_val %04x: %08x\n", (unsigned int)(where & ~3), v); |
7b6e7ba8 DD |
21 | v >>= shift; |
22 | if (size == 1) | |
23 | v &= 0xff; | |
24 | else if (size == 2) | |
25 | v &= 0xffff; | |
26 | *val = v; | |
27 | } | |
28 | ||
29 | static int handle_ea_bar(u32 e0, int bar, struct pci_bus *bus, | |
30 | unsigned int devfn, int where, int size, u32 *val) | |
31 | { | |
32 | void __iomem *addr; | |
33 | u32 v; | |
34 | ||
35 | /* Entries are 16-byte aligned; bits[2,3] select word in entry */ | |
36 | int where_a = where & 0xc; | |
37 | ||
38 | if (where_a == 0) { | |
39 | set_val(e0, where, size, val); | |
40 | return PCIBIOS_SUCCESSFUL; | |
41 | } | |
42 | if (where_a == 0x4) { | |
43 | addr = bus->ops->map_bus(bus, devfn, bar); /* BAR 0 */ | |
658f7ecd | 44 | if (!addr) |
7b6e7ba8 | 45 | return PCIBIOS_DEVICE_NOT_FOUND; |
658f7ecd | 46 | |
7b6e7ba8 DD |
47 | v = readl(addr); |
48 | v &= ~0xf; | |
49 | v |= 2; /* EA entry-1. Base-L */ | |
50 | set_val(v, where, size, val); | |
51 | return PCIBIOS_SUCCESSFUL; | |
52 | } | |
53 | if (where_a == 0x8) { | |
54 | u32 barl_orig; | |
55 | u32 barl_rb; | |
56 | ||
57 | addr = bus->ops->map_bus(bus, devfn, bar); /* BAR 0 */ | |
658f7ecd | 58 | if (!addr) |
7b6e7ba8 | 59 | return PCIBIOS_DEVICE_NOT_FOUND; |
658f7ecd | 60 | |
7b6e7ba8 DD |
61 | barl_orig = readl(addr + 0); |
62 | writel(0xffffffff, addr + 0); | |
63 | barl_rb = readl(addr + 0); | |
64 | writel(barl_orig, addr + 0); | |
65 | /* zeros in unsettable bits */ | |
66 | v = ~barl_rb & ~3; | |
67 | v |= 0xc; /* EA entry-2. Offset-L */ | |
68 | set_val(v, where, size, val); | |
69 | return PCIBIOS_SUCCESSFUL; | |
70 | } | |
71 | if (where_a == 0xc) { | |
72 | addr = bus->ops->map_bus(bus, devfn, bar + 4); /* BAR 1 */ | |
658f7ecd | 73 | if (!addr) |
7b6e7ba8 | 74 | return PCIBIOS_DEVICE_NOT_FOUND; |
658f7ecd | 75 | |
7b6e7ba8 DD |
76 | v = readl(addr); /* EA entry-3. Base-H */ |
77 | set_val(v, where, size, val); | |
78 | return PCIBIOS_SUCCESSFUL; | |
79 | } | |
80 | return PCIBIOS_DEVICE_NOT_FOUND; | |
81 | } | |
82 | ||
83 | static int thunder_ecam_p2_config_read(struct pci_bus *bus, unsigned int devfn, | |
84 | int where, int size, u32 *val) | |
85 | { | |
1958e717 | 86 | struct pci_config_window *cfg = bus->sysdata; |
7b6e7ba8 DD |
87 | int where_a = where & ~3; |
88 | void __iomem *addr; | |
89 | u32 node_bits; | |
90 | u32 v; | |
91 | ||
92 | /* EA Base[63:32] may be missing some bits ... */ | |
93 | switch (where_a) { | |
94 | case 0xa8: | |
95 | case 0xbc: | |
96 | case 0xd0: | |
97 | case 0xe4: | |
98 | break; | |
99 | default: | |
100 | return pci_generic_config_read(bus, devfn, where, size, val); | |
101 | } | |
102 | ||
103 | addr = bus->ops->map_bus(bus, devfn, where_a); | |
658f7ecd | 104 | if (!addr) |
7b6e7ba8 | 105 | return PCIBIOS_DEVICE_NOT_FOUND; |
7b6e7ba8 DD |
106 | |
107 | v = readl(addr); | |
108 | ||
109 | /* | |
110 | * Bit 44 of the 64-bit Base must match the same bit in | |
111 | * the config space access window. Since we are working with | |
112 | * the high-order 32 bits, shift everything down by 32 bits. | |
113 | */ | |
16f7ae59 | 114 | node_bits = upper_32_bits(cfg->res.start) & (1 << 12); |
7b6e7ba8 DD |
115 | |
116 | v |= node_bits; | |
117 | set_val(v, where, size, val); | |
118 | ||
119 | return PCIBIOS_SUCCESSFUL; | |
120 | } | |
121 | ||
122 | static int thunder_ecam_config_read(struct pci_bus *bus, unsigned int devfn, | |
123 | int where, int size, u32 *val) | |
124 | { | |
125 | u32 v; | |
126 | u32 vendor_device; | |
127 | u32 class_rev; | |
128 | void __iomem *addr; | |
129 | int cfg_type; | |
130 | int where_a = where & ~3; | |
131 | ||
132 | addr = bus->ops->map_bus(bus, devfn, 0xc); | |
658f7ecd | 133 | if (!addr) |
7b6e7ba8 | 134 | return PCIBIOS_DEVICE_NOT_FOUND; |
7b6e7ba8 DD |
135 | |
136 | v = readl(addr); | |
137 | ||
138 | /* Check for non type-00 header */ | |
139 | cfg_type = (v >> 16) & 0x7f; | |
140 | ||
141 | addr = bus->ops->map_bus(bus, devfn, 8); | |
658f7ecd | 142 | if (!addr) |
7b6e7ba8 | 143 | return PCIBIOS_DEVICE_NOT_FOUND; |
7b6e7ba8 DD |
144 | |
145 | class_rev = readl(addr); | |
146 | if (class_rev == 0xffffffff) | |
147 | goto no_emulation; | |
148 | ||
149 | if ((class_rev & 0xff) >= 8) { | |
150 | /* Pass-2 handling */ | |
151 | if (cfg_type) | |
152 | goto no_emulation; | |
153 | return thunder_ecam_p2_config_read(bus, devfn, where, | |
154 | size, val); | |
155 | } | |
156 | ||
157 | /* | |
158 | * All BARs have fixed addresses specified by the EA | |
159 | * capability; they must return zero on read. | |
160 | */ | |
161 | if (cfg_type == 0 && | |
162 | ((where >= 0x10 && where < 0x2c) || | |
163 | (where >= 0x1a4 && where < 0x1bc))) { | |
164 | /* BAR or SR-IOV BAR */ | |
165 | *val = 0; | |
166 | return PCIBIOS_SUCCESSFUL; | |
167 | } | |
168 | ||
169 | addr = bus->ops->map_bus(bus, devfn, 0); | |
658f7ecd | 170 | if (!addr) |
7b6e7ba8 | 171 | return PCIBIOS_DEVICE_NOT_FOUND; |
7b6e7ba8 DD |
172 | |
173 | vendor_device = readl(addr); | |
174 | if (vendor_device == 0xffffffff) | |
175 | goto no_emulation; | |
176 | ||
177 | pr_debug("%04x:%04x - Fix pass#: %08x, where: %03x, devfn: %03x\n", | |
178 | vendor_device & 0xffff, vendor_device >> 16, class_rev, | |
fd1ae23b | 179 | (unsigned int)where, devfn); |
7b6e7ba8 DD |
180 | |
181 | /* Check for non type-00 header */ | |
182 | if (cfg_type == 0) { | |
183 | bool has_msix; | |
184 | bool is_nic = (vendor_device == 0xa01e177d); | |
185 | bool is_tns = (vendor_device == 0xa01f177d); | |
186 | ||
187 | addr = bus->ops->map_bus(bus, devfn, 0x70); | |
658f7ecd | 188 | if (!addr) |
7b6e7ba8 | 189 | return PCIBIOS_DEVICE_NOT_FOUND; |
658f7ecd | 190 | |
7b6e7ba8 DD |
191 | /* E_CAP */ |
192 | v = readl(addr); | |
193 | has_msix = (v & 0xff00) != 0; | |
194 | ||
195 | if (!has_msix && where_a == 0x70) { | |
196 | v |= 0xbc00; /* next capability is EA at 0xbc */ | |
197 | set_val(v, where, size, val); | |
198 | return PCIBIOS_SUCCESSFUL; | |
199 | } | |
200 | if (where_a == 0xb0) { | |
201 | addr = bus->ops->map_bus(bus, devfn, where_a); | |
658f7ecd | 202 | if (!addr) |
7b6e7ba8 | 203 | return PCIBIOS_DEVICE_NOT_FOUND; |
658f7ecd | 204 | |
7b6e7ba8 DD |
205 | v = readl(addr); |
206 | if (v & 0xff00) | |
207 | pr_err("Bad MSIX cap header: %08x\n", v); | |
208 | v |= 0xbc00; /* next capability is EA at 0xbc */ | |
209 | set_val(v, where, size, val); | |
210 | return PCIBIOS_SUCCESSFUL; | |
211 | } | |
212 | if (where_a == 0xbc) { | |
213 | if (is_nic) | |
214 | v = 0x40014; /* EA last in chain, 4 entries */ | |
215 | else if (is_tns) | |
216 | v = 0x30014; /* EA last in chain, 3 entries */ | |
217 | else if (has_msix) | |
218 | v = 0x20014; /* EA last in chain, 2 entries */ | |
219 | else | |
220 | v = 0x10014; /* EA last in chain, 1 entry */ | |
221 | set_val(v, where, size, val); | |
222 | return PCIBIOS_SUCCESSFUL; | |
223 | } | |
224 | if (where_a >= 0xc0 && where_a < 0xd0) | |
225 | /* EA entry-0. PP=0, BAR0 Size:3 */ | |
226 | return handle_ea_bar(0x80ff0003, | |
227 | 0x10, bus, devfn, where, | |
228 | size, val); | |
229 | if (where_a >= 0xd0 && where_a < 0xe0 && has_msix) | |
230 | /* EA entry-1. PP=0, BAR4 Size:3 */ | |
231 | return handle_ea_bar(0x80ff0043, | |
232 | 0x20, bus, devfn, where, | |
233 | size, val); | |
234 | if (where_a >= 0xe0 && where_a < 0xf0 && is_tns) | |
235 | /* EA entry-2. PP=0, BAR2, Size:3 */ | |
236 | return handle_ea_bar(0x80ff0023, | |
237 | 0x18, bus, devfn, where, | |
238 | size, val); | |
239 | if (where_a >= 0xe0 && where_a < 0xf0 && is_nic) | |
240 | /* EA entry-2. PP=4, VF_BAR0 (9), Size:3 */ | |
241 | return handle_ea_bar(0x80ff0493, | |
242 | 0x1a4, bus, devfn, where, | |
243 | size, val); | |
244 | if (where_a >= 0xf0 && where_a < 0x100 && is_nic) | |
245 | /* EA entry-3. PP=4, VF_BAR4 (d), Size:3 */ | |
246 | return handle_ea_bar(0x80ff04d3, | |
247 | 0x1b4, bus, devfn, where, | |
248 | size, val); | |
249 | } else if (cfg_type == 1) { | |
250 | bool is_rsl_bridge = devfn == 0x08; | |
251 | bool is_rad_bridge = devfn == 0xa0; | |
252 | bool is_zip_bridge = devfn == 0xa8; | |
253 | bool is_dfa_bridge = devfn == 0xb0; | |
254 | bool is_nic_bridge = devfn == 0x10; | |
255 | ||
256 | if (where_a == 0x70) { | |
257 | addr = bus->ops->map_bus(bus, devfn, where_a); | |
658f7ecd | 258 | if (!addr) |
7b6e7ba8 | 259 | return PCIBIOS_DEVICE_NOT_FOUND; |
658f7ecd | 260 | |
7b6e7ba8 DD |
261 | v = readl(addr); |
262 | if (v & 0xff00) | |
263 | pr_err("Bad PCIe cap header: %08x\n", v); | |
264 | v |= 0xbc00; /* next capability is EA at 0xbc */ | |
265 | set_val(v, where, size, val); | |
266 | return PCIBIOS_SUCCESSFUL; | |
267 | } | |
268 | if (where_a == 0xbc) { | |
269 | if (is_nic_bridge) | |
270 | v = 0x10014; /* EA last in chain, 1 entry */ | |
271 | else | |
272 | v = 0x00014; /* EA last in chain, no entries */ | |
273 | set_val(v, where, size, val); | |
274 | return PCIBIOS_SUCCESSFUL; | |
275 | } | |
276 | if (where_a == 0xc0) { | |
277 | if (is_rsl_bridge || is_nic_bridge) | |
278 | v = 0x0101; /* subordinate:secondary = 1:1 */ | |
279 | else if (is_rad_bridge) | |
280 | v = 0x0202; /* subordinate:secondary = 2:2 */ | |
281 | else if (is_zip_bridge) | |
282 | v = 0x0303; /* subordinate:secondary = 3:3 */ | |
283 | else if (is_dfa_bridge) | |
284 | v = 0x0404; /* subordinate:secondary = 4:4 */ | |
285 | set_val(v, where, size, val); | |
286 | return PCIBIOS_SUCCESSFUL; | |
287 | } | |
288 | if (where_a == 0xc4 && is_nic_bridge) { | |
289 | /* Enabled, not-Write, SP=ff, PP=05, BEI=6, ES=4 */ | |
290 | v = 0x80ff0564; | |
291 | set_val(v, where, size, val); | |
292 | return PCIBIOS_SUCCESSFUL; | |
293 | } | |
294 | if (where_a == 0xc8 && is_nic_bridge) { | |
295 | v = 0x00000002; /* Base-L 64-bit */ | |
296 | set_val(v, where, size, val); | |
297 | return PCIBIOS_SUCCESSFUL; | |
298 | } | |
299 | if (where_a == 0xcc && is_nic_bridge) { | |
300 | v = 0xfffffffe; /* MaxOffset-L 64-bit */ | |
301 | set_val(v, where, size, val); | |
302 | return PCIBIOS_SUCCESSFUL; | |
303 | } | |
304 | if (where_a == 0xd0 && is_nic_bridge) { | |
305 | v = 0x00008430; /* NIC Base-H */ | |
306 | set_val(v, where, size, val); | |
307 | return PCIBIOS_SUCCESSFUL; | |
308 | } | |
309 | if (where_a == 0xd4 && is_nic_bridge) { | |
310 | v = 0x0000000f; /* MaxOffset-H */ | |
311 | set_val(v, where, size, val); | |
312 | return PCIBIOS_SUCCESSFUL; | |
313 | } | |
314 | } | |
315 | no_emulation: | |
316 | return pci_generic_config_read(bus, devfn, where, size, val); | |
317 | } | |
318 | ||
319 | static int thunder_ecam_config_write(struct pci_bus *bus, unsigned int devfn, | |
320 | int where, int size, u32 val) | |
321 | { | |
322 | /* | |
323 | * All BARs have fixed addresses; ignore BAR writes so they | |
324 | * don't get corrupted. | |
325 | */ | |
326 | if ((where >= 0x10 && where < 0x2c) || | |
327 | (where >= 0x1a4 && where < 0x1bc)) | |
328 | /* BAR or SR-IOV BAR */ | |
329 | return PCIBIOS_SUCCESSFUL; | |
330 | ||
331 | return pci_generic_config_write(bus, devfn, where, size, val); | |
332 | } | |
333 | ||
0b104773 | 334 | const struct pci_ecam_ops pci_thunder_ecam_ops = { |
1958e717 J |
335 | .pci_ops = { |
336 | .map_bus = pci_ecam_map_bus, | |
7b6e7ba8 DD |
337 | .read = thunder_ecam_config_read, |
338 | .write = thunder_ecam_config_write, | |
339 | } | |
340 | }; | |
341 | ||
648d93fc TN |
342 | #ifdef CONFIG_PCI_HOST_THUNDER_ECAM |
343 | ||
7b6e7ba8 | 344 | static const struct of_device_id thunder_ecam_of_match[] = { |
b2f75a41 RH |
345 | { |
346 | .compatible = "cavium,pci-host-thunder-ecam", | |
347 | .data = &pci_thunder_ecam_ops, | |
348 | }, | |
7b6e7ba8 DD |
349 | { }, |
350 | }; | |
7b6e7ba8 | 351 | |
7b6e7ba8 DD |
352 | static struct platform_driver thunder_ecam_driver = { |
353 | .driver = { | |
354 | .name = KBUILD_MODNAME, | |
355 | .of_match_table = thunder_ecam_of_match, | |
a5f40e80 | 356 | .suppress_bind_attrs = true, |
7b6e7ba8 | 357 | }, |
b2f75a41 | 358 | .probe = pci_host_common_probe, |
7b6e7ba8 | 359 | }; |
d0c6fd76 | 360 | builtin_platform_driver(thunder_ecam_driver); |
648d93fc TN |
361 | |
362 | #endif | |
363 | #endif |