diff options
Diffstat (limited to 'arch/powerpc/kernel/prom_parse.c')
-rw-r--r-- | arch/powerpc/kernel/prom_parse.c | 165 |
1 files changed, 146 insertions, 19 deletions
diff --git a/arch/powerpc/kernel/prom_parse.c b/arch/powerpc/kernel/prom_parse.c index 23c85af53d47..5b764277f470 100644 --- a/arch/powerpc/kernel/prom_parse.c +++ b/arch/powerpc/kernel/prom_parse.c | |||
@@ -4,7 +4,9 @@ | |||
4 | #include <linux/string.h> | 4 | #include <linux/string.h> |
5 | #include <linux/pci_regs.h> | 5 | #include <linux/pci_regs.h> |
6 | #include <linux/module.h> | 6 | #include <linux/module.h> |
7 | #include <linux/ioport.h> | ||
7 | #include <asm/prom.h> | 8 | #include <asm/prom.h> |
9 | #include <asm/pci-bridge.h> | ||
8 | 10 | ||
9 | #ifdef DEBUG | 11 | #ifdef DEBUG |
10 | #define DBG(fmt...) do { printk(fmt); } while(0) | 12 | #define DBG(fmt...) do { printk(fmt); } while(0) |
@@ -54,6 +56,7 @@ struct of_bus { | |||
54 | int *addrc, int *sizec); | 56 | int *addrc, int *sizec); |
55 | u64 (*map)(u32 *addr, u32 *range, int na, int ns, int pna); | 57 | u64 (*map)(u32 *addr, u32 *range, int na, int ns, int pna); |
56 | int (*translate)(u32 *addr, u64 offset, int na); | 58 | int (*translate)(u32 *addr, u64 offset, int na); |
59 | unsigned int (*get_flags)(u32 *addr); | ||
57 | }; | 60 | }; |
58 | 61 | ||
59 | 62 | ||
@@ -61,8 +64,8 @@ struct of_bus { | |||
61 | * Default translator (generic bus) | 64 | * Default translator (generic bus) |
62 | */ | 65 | */ |
63 | 66 | ||
64 | static void of_default_count_cells(struct device_node *dev, | 67 | static void of_bus_default_count_cells(struct device_node *dev, |
65 | int *addrc, int *sizec) | 68 | int *addrc, int *sizec) |
66 | { | 69 | { |
67 | if (addrc) | 70 | if (addrc) |
68 | *addrc = prom_n_addr_cells(dev); | 71 | *addrc = prom_n_addr_cells(dev); |
@@ -70,7 +73,7 @@ static void of_default_count_cells(struct device_node *dev, | |||
70 | *sizec = prom_n_size_cells(dev); | 73 | *sizec = prom_n_size_cells(dev); |
71 | } | 74 | } |
72 | 75 | ||
73 | static u64 of_default_map(u32 *addr, u32 *range, int na, int ns, int pna) | 76 | static u64 of_bus_default_map(u32 *addr, u32 *range, int na, int ns, int pna) |
74 | { | 77 | { |
75 | u64 cp, s, da; | 78 | u64 cp, s, da; |
76 | 79 | ||
@@ -86,7 +89,7 @@ static u64 of_default_map(u32 *addr, u32 *range, int na, int ns, int pna) | |||
86 | return da - cp; | 89 | return da - cp; |
87 | } | 90 | } |
88 | 91 | ||
89 | static int of_default_translate(u32 *addr, u64 offset, int na) | 92 | static int of_bus_default_translate(u32 *addr, u64 offset, int na) |
90 | { | 93 | { |
91 | u64 a = of_read_addr(addr, na); | 94 | u64 a = of_read_addr(addr, na); |
92 | memset(addr, 0, na * 4); | 95 | memset(addr, 0, na * 4); |
@@ -98,6 +101,11 @@ static int of_default_translate(u32 *addr, u64 offset, int na) | |||
98 | return 0; | 101 | return 0; |
99 | } | 102 | } |
100 | 103 | ||
104 | static unsigned int of_bus_default_get_flags(u32 *addr) | ||
105 | { | ||
106 | return IORESOURCE_MEM; | ||
107 | } | ||
108 | |||
101 | 109 | ||
102 | /* | 110 | /* |
103 | * PCI bus specific translator | 111 | * PCI bus specific translator |
@@ -139,7 +147,24 @@ static u64 of_bus_pci_map(u32 *addr, u32 *range, int na, int ns, int pna) | |||
139 | 147 | ||
140 | static int of_bus_pci_translate(u32 *addr, u64 offset, int na) | 148 | static int of_bus_pci_translate(u32 *addr, u64 offset, int na) |
141 | { | 149 | { |
142 | return of_default_translate(addr + 1, offset, na - 1); | 150 | return of_bus_default_translate(addr + 1, offset, na - 1); |
151 | } | ||
152 | |||
153 | static unsigned int of_bus_pci_get_flags(u32 *addr) | ||
154 | { | ||
155 | unsigned int flags = 0; | ||
156 | u32 w = addr[0]; | ||
157 | |||
158 | switch((w >> 24) & 0x03) { | ||
159 | case 0x01: | ||
160 | flags |= IORESOURCE_IO; | ||
161 | case 0x02: /* 32 bits */ | ||
162 | case 0x03: /* 64 bits */ | ||
163 | flags |= IORESOURCE_MEM; | ||
164 | } | ||
165 | if (w & 0x40000000) | ||
166 | flags |= IORESOURCE_PREFETCH; | ||
167 | return flags; | ||
143 | } | 168 | } |
144 | 169 | ||
145 | /* | 170 | /* |
@@ -182,9 +207,22 @@ static u64 of_bus_isa_map(u32 *addr, u32 *range, int na, int ns, int pna) | |||
182 | 207 | ||
183 | static int of_bus_isa_translate(u32 *addr, u64 offset, int na) | 208 | static int of_bus_isa_translate(u32 *addr, u64 offset, int na) |
184 | { | 209 | { |
185 | return of_default_translate(addr + 1, offset, na - 1); | 210 | return of_bus_default_translate(addr + 1, offset, na - 1); |
211 | } | ||
212 | |||
213 | static unsigned int of_bus_isa_get_flags(u32 *addr) | ||
214 | { | ||
215 | unsigned int flags = 0; | ||
216 | u32 w = addr[0]; | ||
217 | |||
218 | if (w & 1) | ||
219 | flags |= IORESOURCE_IO; | ||
220 | else | ||
221 | flags |= IORESOURCE_MEM; | ||
222 | return flags; | ||
186 | } | 223 | } |
187 | 224 | ||
225 | |||
188 | /* | 226 | /* |
189 | * Array of bus specific translators | 227 | * Array of bus specific translators |
190 | */ | 228 | */ |
@@ -198,6 +236,7 @@ static struct of_bus of_busses[] = { | |||
198 | .count_cells = of_bus_pci_count_cells, | 236 | .count_cells = of_bus_pci_count_cells, |
199 | .map = of_bus_pci_map, | 237 | .map = of_bus_pci_map, |
200 | .translate = of_bus_pci_translate, | 238 | .translate = of_bus_pci_translate, |
239 | .get_flags = of_bus_pci_get_flags, | ||
201 | }, | 240 | }, |
202 | /* ISA */ | 241 | /* ISA */ |
203 | { | 242 | { |
@@ -207,15 +246,17 @@ static struct of_bus of_busses[] = { | |||
207 | .count_cells = of_bus_isa_count_cells, | 246 | .count_cells = of_bus_isa_count_cells, |
208 | .map = of_bus_isa_map, | 247 | .map = of_bus_isa_map, |
209 | .translate = of_bus_isa_translate, | 248 | .translate = of_bus_isa_translate, |
249 | .get_flags = of_bus_isa_get_flags, | ||
210 | }, | 250 | }, |
211 | /* Default */ | 251 | /* Default */ |
212 | { | 252 | { |
213 | .name = "default", | 253 | .name = "default", |
214 | .addresses = "reg", | 254 | .addresses = "reg", |
215 | .match = NULL, | 255 | .match = NULL, |
216 | .count_cells = of_default_count_cells, | 256 | .count_cells = of_bus_default_count_cells, |
217 | .map = of_default_map, | 257 | .map = of_bus_default_map, |
218 | .translate = of_default_translate, | 258 | .translate = of_bus_default_translate, |
259 | .get_flags = of_bus_default_get_flags, | ||
219 | }, | 260 | }, |
220 | }; | 261 | }; |
221 | 262 | ||
@@ -254,7 +295,8 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus, | |||
254 | ranges = (u32 *)get_property(parent, "ranges", &rlen); | 295 | ranges = (u32 *)get_property(parent, "ranges", &rlen); |
255 | if (ranges == NULL || rlen == 0) { | 296 | if (ranges == NULL || rlen == 0) { |
256 | offset = of_read_addr(addr, na); | 297 | offset = of_read_addr(addr, na); |
257 | memset(addr, 0, pna); | 298 | memset(addr, 0, pna * 4); |
299 | DBG("OF: no ranges, 1:1 translation\n"); | ||
258 | goto finish; | 300 | goto finish; |
259 | } | 301 | } |
260 | 302 | ||
@@ -370,7 +412,8 @@ u64 of_translate_address(struct device_node *dev, u32 *in_addr) | |||
370 | } | 412 | } |
371 | EXPORT_SYMBOL(of_translate_address); | 413 | EXPORT_SYMBOL(of_translate_address); |
372 | 414 | ||
373 | u32 *of_get_address(struct device_node *dev, int index, u64 *size) | 415 | u32 *of_get_address(struct device_node *dev, int index, u64 *size, |
416 | unsigned int *flags) | ||
374 | { | 417 | { |
375 | u32 *prop; | 418 | u32 *prop; |
376 | unsigned int psize; | 419 | unsigned int psize; |
@@ -399,22 +442,106 @@ u32 *of_get_address(struct device_node *dev, int index, u64 *size) | |||
399 | if (i == index) { | 442 | if (i == index) { |
400 | if (size) | 443 | if (size) |
401 | *size = of_read_addr(prop + na, ns); | 444 | *size = of_read_addr(prop + na, ns); |
445 | if (flags) | ||
446 | *flags = bus->get_flags(prop); | ||
402 | return prop; | 447 | return prop; |
403 | } | 448 | } |
404 | return NULL; | 449 | return NULL; |
405 | } | 450 | } |
406 | EXPORT_SYMBOL(of_get_address); | 451 | EXPORT_SYMBOL(of_get_address); |
407 | 452 | ||
408 | u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size) | 453 | u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size, |
454 | unsigned int *flags) | ||
409 | { | 455 | { |
410 | u32 *addr; | 456 | u32 *prop; |
411 | int index; | 457 | unsigned int psize; |
458 | struct device_node *parent; | ||
459 | struct of_bus *bus; | ||
460 | int onesize, i, na, ns; | ||
412 | 461 | ||
413 | for (index = 0; (addr = of_get_address(dev, index, size)) != NULL; | 462 | /* Get parent & match bus type */ |
414 | index++) { | 463 | parent = of_get_parent(dev); |
415 | if ((addr[0] & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0)) | 464 | if (parent == NULL) |
416 | return addr; | 465 | return NULL; |
417 | } | 466 | bus = of_match_bus(parent); |
467 | if (strcmp(bus->name, "pci")) | ||
468 | return NULL; | ||
469 | bus->count_cells(dev, &na, &ns); | ||
470 | of_node_put(parent); | ||
471 | if (!OF_CHECK_COUNTS(na, ns)) | ||
472 | return NULL; | ||
473 | |||
474 | /* Get "reg" or "assigned-addresses" property */ | ||
475 | prop = (u32 *)get_property(dev, bus->addresses, &psize); | ||
476 | if (prop == NULL) | ||
477 | return NULL; | ||
478 | psize /= 4; | ||
479 | |||
480 | onesize = na + ns; | ||
481 | for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) | ||
482 | if ((prop[0] & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0)) { | ||
483 | if (size) | ||
484 | *size = of_read_addr(prop + na, ns); | ||
485 | if (flags) | ||
486 | *flags = bus->get_flags(prop); | ||
487 | return prop; | ||
488 | } | ||
418 | return NULL; | 489 | return NULL; |
419 | } | 490 | } |
420 | EXPORT_SYMBOL(of_get_pci_address); | 491 | EXPORT_SYMBOL(of_get_pci_address); |
492 | |||
493 | static int __of_address_to_resource(struct device_node *dev, u32 *addrp, | ||
494 | u64 size, unsigned int flags, | ||
495 | struct resource *r) | ||
496 | { | ||
497 | u64 taddr; | ||
498 | |||
499 | if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0) | ||
500 | return -EINVAL; | ||
501 | taddr = of_translate_address(dev, addrp); | ||
502 | if (taddr == OF_BAD_ADDR) | ||
503 | return -EINVAL; | ||
504 | memset(r, 0, sizeof(struct resource)); | ||
505 | if (flags & IORESOURCE_IO) { | ||
506 | unsigned int port; | ||
507 | port = pci_address_to_pio(taddr); | ||
508 | if (port == (unsigned int)-1) | ||
509 | return -EINVAL; | ||
510 | r->start = port; | ||
511 | r->end = port + size - 1; | ||
512 | } else { | ||
513 | r->start = taddr; | ||
514 | r->end = taddr + size - 1; | ||
515 | } | ||
516 | r->flags = flags; | ||
517 | r->name = dev->name; | ||
518 | return 0; | ||
519 | } | ||
520 | |||
521 | int of_address_to_resource(struct device_node *dev, int index, | ||
522 | struct resource *r) | ||
523 | { | ||
524 | u32 *addrp; | ||
525 | u64 size; | ||
526 | unsigned int flags; | ||
527 | |||
528 | addrp = of_get_address(dev, index, &size, &flags); | ||
529 | if (addrp == NULL) | ||
530 | return -EINVAL; | ||
531 | return __of_address_to_resource(dev, addrp, size, flags, r); | ||
532 | } | ||
533 | EXPORT_SYMBOL_GPL(of_address_to_resource); | ||
534 | |||
535 | int of_pci_address_to_resource(struct device_node *dev, int bar, | ||
536 | struct resource *r) | ||
537 | { | ||
538 | u32 *addrp; | ||
539 | u64 size; | ||
540 | unsigned int flags; | ||
541 | |||
542 | addrp = of_get_pci_address(dev, bar, &size, &flags); | ||
543 | if (addrp == NULL) | ||
544 | return -EINVAL; | ||
545 | return __of_address_to_resource(dev, addrp, size, flags, r); | ||
546 | } | ||
547 | EXPORT_SYMBOL_GPL(of_pci_address_to_resource); | ||