Commit | Line | Data |
---|---|---|
551a3d87 IK |
1 | /* |
2 | * Support for PCI on Celleb platform. | |
3 | * | |
4 | * (C) Copyright 2006-2007 TOSHIBA CORPORATION | |
5 | * | |
6 | * This code is based on arch/powerpc/kernel/rtas_pci.c: | |
7 | * Copyright (C) 2001 Dave Engebretsen, IBM Corporation | |
8 | * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM | |
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 as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or | |
13 | * (at your option) any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License along | |
21 | * with this program; if not, write to the Free Software Foundation, Inc., | |
22 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
23 | */ | |
24 | ||
25 | #undef DEBUG | |
26 | ||
27 | #include <linux/kernel.h> | |
28 | #include <linux/threads.h> | |
29 | #include <linux/pci.h> | |
30 | #include <linux/string.h> | |
31 | #include <linux/init.h> | |
32 | #include <linux/bootmem.h> | |
33 | #include <linux/pci_regs.h> | |
283029d1 | 34 | #include <linux/of.h> |
da0bd34e | 35 | #include <linux/of_device.h> |
5a0e3ad6 | 36 | #include <linux/slab.h> |
551a3d87 IK |
37 | |
38 | #include <asm/io.h> | |
39 | #include <asm/irq.h> | |
40 | #include <asm/prom.h> | |
551a3d87 IK |
41 | #include <asm/pci-bridge.h> |
42 | #include <asm/ppc-pci.h> | |
43 | ||
116bdc42 | 44 | #include "celleb_pci.h" |
551a3d87 IK |
45 | |
46 | #define MAX_PCI_DEVICES 32 | |
47 | #define MAX_PCI_FUNCTIONS 8 | |
48 | #define MAX_PCI_BASE_ADDRS 3 /* use 64 bit address */ | |
49 | ||
50 | /* definition for fake pci configuration area for GbE, .... ,and etc. */ | |
51 | ||
52 | struct celleb_pci_resource { | |
53 | struct resource r[MAX_PCI_BASE_ADDRS]; | |
54 | }; | |
55 | ||
56 | struct celleb_pci_private { | |
57 | unsigned char *fake_config[MAX_PCI_DEVICES][MAX_PCI_FUNCTIONS]; | |
58 | struct celleb_pci_resource *res[MAX_PCI_DEVICES][MAX_PCI_FUNCTIONS]; | |
59 | }; | |
60 | ||
61 | static inline u8 celleb_fake_config_readb(void *addr) | |
62 | { | |
63 | u8 *p = addr; | |
64 | return *p; | |
65 | } | |
66 | ||
67 | static inline u16 celleb_fake_config_readw(void *addr) | |
68 | { | |
f1fda895 | 69 | __le16 *p = addr; |
551a3d87 IK |
70 | return le16_to_cpu(*p); |
71 | } | |
72 | ||
73 | static inline u32 celleb_fake_config_readl(void *addr) | |
74 | { | |
f1fda895 | 75 | __le32 *p = addr; |
551a3d87 IK |
76 | return le32_to_cpu(*p); |
77 | } | |
78 | ||
79 | static inline void celleb_fake_config_writeb(u32 val, void *addr) | |
80 | { | |
81 | u8 *p = addr; | |
82 | *p = val; | |
83 | } | |
84 | ||
85 | static inline void celleb_fake_config_writew(u32 val, void *addr) | |
86 | { | |
f1fda895 AV |
87 | __le16 val16; |
88 | __le16 *p = addr; | |
551a3d87 IK |
89 | val16 = cpu_to_le16(val); |
90 | *p = val16; | |
91 | } | |
92 | ||
93 | static inline void celleb_fake_config_writel(u32 val, void *addr) | |
94 | { | |
f1fda895 AV |
95 | __le32 val32; |
96 | __le32 *p = addr; | |
551a3d87 IK |
97 | val32 = cpu_to_le32(val); |
98 | *p = val32; | |
99 | } | |
100 | ||
101 | static unsigned char *get_fake_config_start(struct pci_controller *hose, | |
102 | int devno, int fn) | |
103 | { | |
104 | struct celleb_pci_private *private = hose->private_data; | |
105 | ||
106 | if (private == NULL) | |
107 | return NULL; | |
108 | ||
109 | return private->fake_config[devno][fn]; | |
110 | } | |
111 | ||
112 | static struct celleb_pci_resource *get_resource_start( | |
113 | struct pci_controller *hose, | |
114 | int devno, int fn) | |
115 | { | |
116 | struct celleb_pci_private *private = hose->private_data; | |
117 | ||
118 | if (private == NULL) | |
119 | return NULL; | |
120 | ||
121 | return private->res[devno][fn]; | |
122 | } | |
123 | ||
124 | ||
125 | static void celleb_config_read_fake(unsigned char *config, int where, | |
126 | int size, u32 *val) | |
127 | { | |
128 | char *p = config + where; | |
129 | ||
130 | switch (size) { | |
131 | case 1: | |
132 | *val = celleb_fake_config_readb(p); | |
133 | break; | |
134 | case 2: | |
135 | *val = celleb_fake_config_readw(p); | |
136 | break; | |
137 | case 4: | |
138 | *val = celleb_fake_config_readl(p); | |
139 | break; | |
140 | } | |
551a3d87 IK |
141 | } |
142 | ||
143 | static void celleb_config_write_fake(unsigned char *config, int where, | |
144 | int size, u32 val) | |
145 | { | |
146 | char *p = config + where; | |
147 | ||
148 | switch (size) { | |
149 | case 1: | |
150 | celleb_fake_config_writeb(val, p); | |
151 | break; | |
152 | case 2: | |
153 | celleb_fake_config_writew(val, p); | |
154 | break; | |
155 | case 4: | |
156 | celleb_fake_config_writel(val, p); | |
157 | break; | |
158 | } | |
551a3d87 IK |
159 | } |
160 | ||
161 | static int celleb_fake_pci_read_config(struct pci_bus *bus, | |
162 | unsigned int devfn, int where, int size, u32 *val) | |
163 | { | |
164 | char *config; | |
58513dc4 | 165 | struct pci_controller *hose = pci_bus_to_host(bus); |
551a3d87 IK |
166 | unsigned int devno = devfn >> 3; |
167 | unsigned int fn = devfn & 0x7; | |
168 | ||
169 | /* allignment check */ | |
170 | BUG_ON(where % size); | |
171 | ||
172 | pr_debug(" fake read: bus=0x%x, ", bus->number); | |
551a3d87 IK |
173 | config = get_fake_config_start(hose, devno, fn); |
174 | ||
175 | pr_debug("devno=0x%x, where=0x%x, size=0x%x, ", devno, where, size); | |
176 | if (!config) { | |
177 | pr_debug("failed\n"); | |
178 | return PCIBIOS_DEVICE_NOT_FOUND; | |
179 | } | |
180 | ||
181 | celleb_config_read_fake(config, where, size, val); | |
182 | pr_debug("val=0x%x\n", *val); | |
183 | ||
184 | return PCIBIOS_SUCCESSFUL; | |
185 | } | |
186 | ||
187 | ||
188 | static int celleb_fake_pci_write_config(struct pci_bus *bus, | |
116bdc42 | 189 | unsigned int devfn, int where, int size, u32 val) |
551a3d87 IK |
190 | { |
191 | char *config; | |
58513dc4 | 192 | struct pci_controller *hose = pci_bus_to_host(bus); |
551a3d87 IK |
193 | struct celleb_pci_resource *res; |
194 | unsigned int devno = devfn >> 3; | |
195 | unsigned int fn = devfn & 0x7; | |
196 | ||
197 | /* allignment check */ | |
198 | BUG_ON(where % size); | |
199 | ||
551a3d87 IK |
200 | config = get_fake_config_start(hose, devno, fn); |
201 | ||
202 | if (!config) | |
203 | return PCIBIOS_DEVICE_NOT_FOUND; | |
204 | ||
205 | if (val == ~0) { | |
206 | int i = (where - PCI_BASE_ADDRESS_0) >> 3; | |
207 | ||
208 | switch (where) { | |
209 | case PCI_BASE_ADDRESS_0: | |
210 | case PCI_BASE_ADDRESS_2: | |
211 | if (size != 4) | |
212 | return PCIBIOS_DEVICE_NOT_FOUND; | |
213 | res = get_resource_start(hose, devno, fn); | |
214 | if (!res) | |
215 | return PCIBIOS_DEVICE_NOT_FOUND; | |
216 | celleb_config_write_fake(config, where, size, | |
217 | (res->r[i].end - res->r[i].start)); | |
218 | return PCIBIOS_SUCCESSFUL; | |
219 | case PCI_BASE_ADDRESS_1: | |
220 | case PCI_BASE_ADDRESS_3: | |
221 | case PCI_BASE_ADDRESS_4: | |
222 | case PCI_BASE_ADDRESS_5: | |
223 | break; | |
224 | default: | |
225 | break; | |
226 | } | |
227 | } | |
228 | ||
229 | celleb_config_write_fake(config, where, size, val); | |
230 | pr_debug(" fake write: where=%x, size=%d, val=%x\n", | |
231 | where, size, val); | |
232 | ||
233 | return PCIBIOS_SUCCESSFUL; | |
234 | } | |
235 | ||
236 | static struct pci_ops celleb_fake_pci_ops = { | |
aec249bc NL |
237 | .read = celleb_fake_pci_read_config, |
238 | .write = celleb_fake_pci_write_config, | |
551a3d87 IK |
239 | }; |
240 | ||
241 | static inline void celleb_setup_pci_base_addrs(struct pci_controller *hose, | |
242 | unsigned int devno, unsigned int fn, | |
243 | unsigned int num_base_addr) | |
244 | { | |
245 | u32 val; | |
246 | unsigned char *config; | |
247 | struct celleb_pci_resource *res; | |
248 | ||
249 | config = get_fake_config_start(hose, devno, fn); | |
250 | res = get_resource_start(hose, devno, fn); | |
251 | ||
252 | if (!config || !res) | |
253 | return; | |
254 | ||
255 | switch (num_base_addr) { | |
256 | case 3: | |
257 | val = (res->r[2].start & 0xfffffff0) | |
258 | | PCI_BASE_ADDRESS_MEM_TYPE_64; | |
259 | celleb_config_write_fake(config, PCI_BASE_ADDRESS_4, 4, val); | |
260 | val = res->r[2].start >> 32; | |
261 | celleb_config_write_fake(config, PCI_BASE_ADDRESS_5, 4, val); | |
262 | /* FALLTHROUGH */ | |
263 | case 2: | |
264 | val = (res->r[1].start & 0xfffffff0) | |
265 | | PCI_BASE_ADDRESS_MEM_TYPE_64; | |
266 | celleb_config_write_fake(config, PCI_BASE_ADDRESS_2, 4, val); | |
267 | val = res->r[1].start >> 32; | |
268 | celleb_config_write_fake(config, PCI_BASE_ADDRESS_3, 4, val); | |
269 | /* FALLTHROUGH */ | |
270 | case 1: | |
271 | val = (res->r[0].start & 0xfffffff0) | |
272 | | PCI_BASE_ADDRESS_MEM_TYPE_64; | |
273 | celleb_config_write_fake(config, PCI_BASE_ADDRESS_0, 4, val); | |
274 | val = res->r[0].start >> 32; | |
275 | celleb_config_write_fake(config, PCI_BASE_ADDRESS_1, 4, val); | |
276 | break; | |
277 | } | |
278 | ||
279 | val = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; | |
280 | celleb_config_write_fake(config, PCI_COMMAND, 2, val); | |
281 | } | |
282 | ||
e5b9187b IK |
283 | static int __init celleb_setup_fake_pci_device(struct device_node *node, |
284 | struct pci_controller *hose) | |
551a3d87 IK |
285 | { |
286 | unsigned int rlen; | |
287 | int num_base_addr = 0; | |
288 | u32 val; | |
289 | const u32 *wi0, *wi1, *wi2, *wi3, *wi4; | |
290 | unsigned int devno, fn; | |
291 | struct celleb_pci_private *private = hose->private_data; | |
292 | unsigned char **config = NULL; | |
293 | struct celleb_pci_resource **res = NULL; | |
294 | const char *name; | |
295 | const unsigned long *li; | |
296 | int size, result; | |
297 | ||
298 | if (private == NULL) { | |
299 | printk(KERN_ERR "PCI: " | |
300 | "memory space for pci controller is not assigned\n"); | |
301 | goto error; | |
302 | } | |
303 | ||
e2eb6392 | 304 | name = of_get_property(node, "model", &rlen); |
551a3d87 IK |
305 | if (!name) { |
306 | printk(KERN_ERR "PCI: model property not found.\n"); | |
307 | goto error; | |
308 | } | |
309 | ||
e2eb6392 | 310 | wi4 = of_get_property(node, "reg", &rlen); |
551a3d87 IK |
311 | if (wi4 == NULL) |
312 | goto error; | |
313 | ||
314 | devno = ((wi4[0] >> 8) & 0xff) >> 3; | |
315 | fn = (wi4[0] >> 8) & 0x7; | |
316 | ||
317 | pr_debug("PCI: celleb_setup_fake_pci() %s devno=%x fn=%x\n", name, | |
318 | devno, fn); | |
319 | ||
320 | size = 256; | |
321 | config = &private->fake_config[devno][fn]; | |
a56555e5 | 322 | *config = zalloc_maybe_bootmem(size, GFP_KERNEL); |
551a3d87 IK |
323 | if (*config == NULL) { |
324 | printk(KERN_ERR "PCI: " | |
325 | "not enough memory for fake configuration space\n"); | |
326 | goto error; | |
327 | } | |
328 | pr_debug("PCI: fake config area assigned 0x%016lx\n", | |
329 | (unsigned long)*config); | |
330 | ||
331 | size = sizeof(struct celleb_pci_resource); | |
332 | res = &private->res[devno][fn]; | |
a56555e5 | 333 | *res = zalloc_maybe_bootmem(size, GFP_KERNEL); |
551a3d87 IK |
334 | if (*res == NULL) { |
335 | printk(KERN_ERR | |
336 | "PCI: not enough memory for resource data space\n"); | |
337 | goto error; | |
338 | } | |
339 | pr_debug("PCI: res assigned 0x%016lx\n", (unsigned long)*res); | |
340 | ||
e2eb6392 SR |
341 | wi0 = of_get_property(node, "device-id", NULL); |
342 | wi1 = of_get_property(node, "vendor-id", NULL); | |
343 | wi2 = of_get_property(node, "class-code", NULL); | |
344 | wi3 = of_get_property(node, "revision-id", NULL); | |
3a1c81f4 CG |
345 | if (!wi0 || !wi1 || !wi2 || !wi3) { |
346 | printk(KERN_ERR "PCI: Missing device tree properties.\n"); | |
347 | goto error; | |
348 | } | |
551a3d87 IK |
349 | |
350 | celleb_config_write_fake(*config, PCI_DEVICE_ID, 2, wi0[0] & 0xffff); | |
351 | celleb_config_write_fake(*config, PCI_VENDOR_ID, 2, wi1[0] & 0xffff); | |
352 | pr_debug("class-code = 0x%08x\n", wi2[0]); | |
353 | ||
354 | celleb_config_write_fake(*config, PCI_CLASS_PROG, 1, wi2[0] & 0xff); | |
355 | celleb_config_write_fake(*config, PCI_CLASS_DEVICE, 2, | |
356 | (wi2[0] >> 8) & 0xffff); | |
357 | celleb_config_write_fake(*config, PCI_REVISION_ID, 1, wi3[0]); | |
358 | ||
359 | while (num_base_addr < MAX_PCI_BASE_ADDRS) { | |
360 | result = of_address_to_resource(node, | |
361 | num_base_addr, &(*res)->r[num_base_addr]); | |
362 | if (result) | |
363 | break; | |
364 | num_base_addr++; | |
365 | } | |
366 | ||
367 | celleb_setup_pci_base_addrs(hose, devno, fn, num_base_addr); | |
368 | ||
e2eb6392 | 369 | li = of_get_property(node, "interrupts", &rlen); |
3a1c81f4 CG |
370 | if (!li) { |
371 | printk(KERN_ERR "PCI: interrupts not found.\n"); | |
372 | goto error; | |
373 | } | |
551a3d87 IK |
374 | val = li[0]; |
375 | celleb_config_write_fake(*config, PCI_INTERRUPT_PIN, 1, 1); | |
376 | celleb_config_write_fake(*config, PCI_INTERRUPT_LINE, 1, val); | |
377 | ||
378 | #ifdef DEBUG | |
379 | pr_debug("PCI: %s irq=%ld\n", name, li[0]); | |
380 | for (i = 0; i < 6; i++) { | |
381 | celleb_config_read_fake(*config, | |
382 | PCI_BASE_ADDRESS_0 + 0x4 * i, 4, | |
383 | &val); | |
384 | pr_debug("PCI: %s fn=%d base_address_%d=0x%x\n", | |
385 | name, fn, i, val); | |
386 | } | |
387 | #endif | |
388 | ||
389 | celleb_config_write_fake(*config, PCI_HEADER_TYPE, 1, | |
390 | PCI_HEADER_TYPE_NORMAL); | |
391 | ||
392 | return 0; | |
393 | ||
394 | error: | |
395 | if (mem_init_done) { | |
396 | if (config && *config) | |
397 | kfree(*config); | |
398 | if (res && *res) | |
399 | kfree(*res); | |
400 | ||
401 | } else { | |
402 | if (config && *config) { | |
403 | size = 256; | |
81df9bff | 404 | free_bootmem(__pa(*config), size); |
551a3d87 IK |
405 | } |
406 | if (res && *res) { | |
407 | size = sizeof(struct celleb_pci_resource); | |
81df9bff | 408 | free_bootmem(__pa(*res), size); |
551a3d87 IK |
409 | } |
410 | } | |
411 | ||
412 | return 1; | |
413 | } | |
414 | ||
e5b9187b IK |
415 | static int __init phb_set_bus_ranges(struct device_node *dev, |
416 | struct pci_controller *phb) | |
551a3d87 IK |
417 | { |
418 | const int *bus_range; | |
419 | unsigned int len; | |
420 | ||
e2eb6392 | 421 | bus_range = of_get_property(dev, "bus-range", &len); |
551a3d87 IK |
422 | if (bus_range == NULL || len < 2 * sizeof(int)) |
423 | return 1; | |
424 | ||
425 | phb->first_busno = bus_range[0]; | |
426 | phb->last_busno = bus_range[1]; | |
427 | ||
428 | return 0; | |
429 | } | |
430 | ||
e5b9187b | 431 | static void __init celleb_alloc_private_mem(struct pci_controller *hose) |
551a3d87 | 432 | { |
7b2c3c5b | 433 | hose->private_data = |
a56555e5 | 434 | zalloc_maybe_bootmem(sizeof(struct celleb_pci_private), |
7b2c3c5b | 435 | GFP_KERNEL); |
551a3d87 IK |
436 | } |
437 | ||
da0bd34e IK |
438 | static int __init celleb_setup_fake_pci(struct device_node *dev, |
439 | struct pci_controller *phb) | |
551a3d87 | 440 | { |
551a3d87 | 441 | struct device_node *node; |
551a3d87 | 442 | |
da0bd34e IK |
443 | phb->ops = &celleb_fake_pci_ops; |
444 | celleb_alloc_private_mem(phb); | |
551a3d87 | 445 | |
da0bd34e IK |
446 | for (node = of_get_next_child(dev, NULL); |
447 | node != NULL; node = of_get_next_child(dev, node)) | |
448 | celleb_setup_fake_pci_device(node, phb); | |
449 | ||
450 | return 0; | |
451 | } | |
452 | ||
6ec859e1 IK |
453 | static struct celleb_phb_spec celleb_fake_pci_spec __initdata = { |
454 | .setup = celleb_setup_fake_pci, | |
455 | }; | |
551a3d87 | 456 | |
ce6d73c9 | 457 | static const struct of_device_id celleb_phb_match[] __initconst = { |
da0bd34e IK |
458 | { |
459 | .name = "pci-pseudo", | |
6ec859e1 | 460 | .data = &celleb_fake_pci_spec, |
da0bd34e IK |
461 | }, { |
462 | .name = "epci", | |
6ec859e1 | 463 | .data = &celleb_epci_spec, |
884d04cd IK |
464 | }, { |
465 | .name = "pcie", | |
466 | .data = &celleb_pciex_spec, | |
da0bd34e IK |
467 | }, { |
468 | }, | |
469 | }; | |
551a3d87 | 470 | |
da0bd34e IK |
471 | int __init celleb_setup_phb(struct pci_controller *phb) |
472 | { | |
44ef3390 | 473 | struct device_node *dev = phb->dn; |
da0bd34e | 474 | const struct of_device_id *match; |
12736b14 | 475 | const struct celleb_phb_spec *phb_spec; |
6ec859e1 | 476 | int rc; |
da0bd34e IK |
477 | |
478 | match = of_match_node(celleb_phb_match, dev); | |
479 | if (!match) | |
551a3d87 IK |
480 | return 1; |
481 | ||
da0bd34e IK |
482 | phb_set_bus_ranges(dev, phb); |
483 | phb->buid = 1; | |
484 | ||
6ec859e1 IK |
485 | phb_spec = match->data; |
486 | rc = (*phb_spec->setup)(dev, phb); | |
487 | if (rc) | |
488 | return 1; | |
489 | ||
d1109b75 ME |
490 | if (phb_spec->ops) |
491 | iowa_register_bus(phb, phb_spec->ops, | |
492 | phb_spec->iowa_init, | |
493 | phb_spec->iowa_data); | |
494 | return 0; | |
551a3d87 IK |
495 | } |
496 | ||
497 | int celleb_pci_probe_mode(struct pci_bus *bus) | |
498 | { | |
499 | return PCI_PROBE_DEVTREE; | |
500 | } |