diff options
Diffstat (limited to 'arch/sparc64/kernel/pci.c')
-rw-r--r-- | arch/sparc64/kernel/pci.c | 80 |
1 files changed, 74 insertions, 6 deletions
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 425e883e7e3b..b63341c2a334 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <asm/ebus.h> | 26 | #include <asm/ebus.h> |
27 | #include <asm/isa.h> | 27 | #include <asm/isa.h> |
28 | #include <asm/prom.h> | 28 | #include <asm/prom.h> |
29 | #include <asm/apb.h> | ||
29 | 30 | ||
30 | #include "pci_impl.h" | 31 | #include "pci_impl.h" |
31 | 32 | ||
@@ -372,6 +373,7 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, | |||
372 | struct dev_archdata *sd; | 373 | struct dev_archdata *sd; |
373 | struct pci_dev *dev; | 374 | struct pci_dev *dev; |
374 | const char *type; | 375 | const char *type; |
376 | u32 class; | ||
375 | 377 | ||
376 | dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL); | 378 | dev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL); |
377 | if (!dev) | 379 | if (!dev) |
@@ -409,7 +411,15 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, | |||
409 | 411 | ||
410 | sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus), | 412 | sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus), |
411 | dev->bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); | 413 | dev->bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); |
412 | dev->class = of_getintprop_default(node, "class-code", 0); | 414 | |
415 | /* dev->class = of_getintprop_default(node, "class-code", 0); */ | ||
416 | /* We can't actually use the firmware value, we have to read what | ||
417 | * is in the register right now. One reason is that in the case | ||
418 | * of IDE interfaces the firmware can sample the value before the | ||
419 | * the IDE interface is programmed into native mode. | ||
420 | */ | ||
421 | pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); | ||
422 | dev->class = class >> 8; | ||
413 | 423 | ||
414 | printk(" class: 0x%x\n", dev->class); | 424 | printk(" class: 0x%x\n", dev->class); |
415 | 425 | ||
@@ -440,6 +450,53 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm, | |||
440 | return dev; | 450 | return dev; |
441 | } | 451 | } |
442 | 452 | ||
453 | static void __init apb_calc_first_last(u8 map, u32 *first_p, u32 *last_p) | ||
454 | { | ||
455 | u32 idx, first, last; | ||
456 | |||
457 | first = 8; | ||
458 | last = 0; | ||
459 | for (idx = 0; idx < 8; idx++) { | ||
460 | if ((map & (1 << idx)) != 0) { | ||
461 | if (first > idx) | ||
462 | first = idx; | ||
463 | if (last < idx) | ||
464 | last = idx; | ||
465 | } | ||
466 | } | ||
467 | |||
468 | *first_p = first; | ||
469 | *last_p = last; | ||
470 | } | ||
471 | |||
472 | /* Cook up fake bus resources for SUNW,simba PCI bridges which lack | ||
473 | * a proper 'ranges' property. | ||
474 | */ | ||
475 | static void __init apb_fake_ranges(struct pci_dev *dev, | ||
476 | struct pci_bus *bus, | ||
477 | struct pci_pbm_info *pbm) | ||
478 | { | ||
479 | struct resource *res; | ||
480 | u32 first, last; | ||
481 | u8 map; | ||
482 | |||
483 | pci_read_config_byte(dev, APB_IO_ADDRESS_MAP, &map); | ||
484 | apb_calc_first_last(map, &first, &last); | ||
485 | res = bus->resource[0]; | ||
486 | res->start = (first << 21); | ||
487 | res->end = (last << 21) + ((1 << 21) - 1); | ||
488 | res->flags = IORESOURCE_IO; | ||
489 | pbm->parent->resource_adjust(dev, res, &pbm->io_space); | ||
490 | |||
491 | pci_read_config_byte(dev, APB_MEM_ADDRESS_MAP, &map); | ||
492 | apb_calc_first_last(map, &first, &last); | ||
493 | res = bus->resource[1]; | ||
494 | res->start = (first << 21); | ||
495 | res->end = (last << 21) + ((1 << 21) - 1); | ||
496 | res->flags = IORESOURCE_MEM; | ||
497 | pbm->parent->resource_adjust(dev, res, &pbm->mem_space); | ||
498 | } | ||
499 | |||
443 | static void __init pci_of_scan_bus(struct pci_pbm_info *pbm, | 500 | static void __init pci_of_scan_bus(struct pci_pbm_info *pbm, |
444 | struct device_node *node, | 501 | struct device_node *node, |
445 | struct pci_bus *bus); | 502 | struct pci_bus *bus); |
@@ -452,7 +509,7 @@ void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm, | |||
452 | { | 509 | { |
453 | struct pci_bus *bus; | 510 | struct pci_bus *bus; |
454 | const u32 *busrange, *ranges; | 511 | const u32 *busrange, *ranges; |
455 | int len, i; | 512 | int len, i, simba; |
456 | struct resource *res; | 513 | struct resource *res; |
457 | unsigned int flags; | 514 | unsigned int flags; |
458 | u64 size; | 515 | u64 size; |
@@ -467,10 +524,16 @@ void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm, | |||
467 | return; | 524 | return; |
468 | } | 525 | } |
469 | ranges = of_get_property(node, "ranges", &len); | 526 | ranges = of_get_property(node, "ranges", &len); |
527 | simba = 0; | ||
470 | if (ranges == NULL) { | 528 | if (ranges == NULL) { |
471 | printk(KERN_DEBUG "Can't get ranges for PCI-PCI bridge %s\n", | 529 | char *model = of_get_property(node, "model", NULL); |
472 | node->full_name); | 530 | if (model && !strcmp(model, "SUNW,simba")) { |
473 | return; | 531 | simba = 1; |
532 | } else { | ||
533 | printk(KERN_DEBUG "Can't get ranges for PCI-PCI bridge %s\n", | ||
534 | node->full_name); | ||
535 | return; | ||
536 | } | ||
474 | } | 537 | } |
475 | 538 | ||
476 | bus = pci_add_new_bus(dev->bus, dev, busrange[0]); | 539 | bus = pci_add_new_bus(dev->bus, dev, busrange[0]); |
@@ -484,7 +547,7 @@ void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm, | |||
484 | bus->subordinate = busrange[1]; | 547 | bus->subordinate = busrange[1]; |
485 | bus->bridge_ctl = 0; | 548 | bus->bridge_ctl = 0; |
486 | 549 | ||
487 | /* parse ranges property */ | 550 | /* parse ranges property, or cook one up by hand for Simba */ |
488 | /* PCI #address-cells == 3 and #size-cells == 2 always */ | 551 | /* PCI #address-cells == 3 and #size-cells == 2 always */ |
489 | res = &dev->resource[PCI_BRIDGE_RESOURCES]; | 552 | res = &dev->resource[PCI_BRIDGE_RESOURCES]; |
490 | for (i = 0; i < PCI_NUM_RESOURCES - PCI_BRIDGE_RESOURCES; ++i) { | 553 | for (i = 0; i < PCI_NUM_RESOURCES - PCI_BRIDGE_RESOURCES; ++i) { |
@@ -492,6 +555,10 @@ void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm, | |||
492 | bus->resource[i] = res; | 555 | bus->resource[i] = res; |
493 | ++res; | 556 | ++res; |
494 | } | 557 | } |
558 | if (simba) { | ||
559 | apb_fake_ranges(dev, bus, pbm); | ||
560 | goto simba_cont; | ||
561 | } | ||
495 | i = 1; | 562 | i = 1; |
496 | for (; len >= 32; len -= 32, ranges += 8) { | 563 | for (; len >= 32; len -= 32, ranges += 8) { |
497 | struct resource *root; | 564 | struct resource *root; |
@@ -529,6 +596,7 @@ void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm, | |||
529 | */ | 596 | */ |
530 | pbm->parent->resource_adjust(dev, res, root); | 597 | pbm->parent->resource_adjust(dev, res, root); |
531 | } | 598 | } |
599 | simba_cont: | ||
532 | sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus), | 600 | sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus), |
533 | bus->number); | 601 | bus->number); |
534 | printk(" bus name: %s\n", bus->name); | 602 | printk(" bus name: %s\n", bus->name); |