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