diff options
Diffstat (limited to 'arch/sparc64/kernel/pci.c')
-rw-r--r-- | arch/sparc64/kernel/pci.c | 97 |
1 files changed, 89 insertions, 8 deletions
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 38a32bc95d22..81f4a5ea05f7 100644 --- a/arch/sparc64/kernel/pci.c +++ b/arch/sparc64/kernel/pci.c | |||
@@ -522,6 +522,89 @@ static void pci_resource_adjust(struct resource *res, | |||
522 | res->end += root->start; | 522 | res->end += root->start; |
523 | } | 523 | } |
524 | 524 | ||
525 | /* For PCI bus devices which lack a 'ranges' property we interrogate | ||
526 | * the config space values to set the resources, just like the generic | ||
527 | * Linux PCI probing code does. | ||
528 | */ | ||
529 | static void __devinit pci_cfg_fake_ranges(struct pci_dev *dev, | ||
530 | struct pci_bus *bus, | ||
531 | struct pci_pbm_info *pbm) | ||
532 | { | ||
533 | struct resource *res; | ||
534 | u8 io_base_lo, io_limit_lo; | ||
535 | u16 mem_base_lo, mem_limit_lo; | ||
536 | unsigned long base, limit; | ||
537 | |||
538 | pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); | ||
539 | pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo); | ||
540 | base = (io_base_lo & PCI_IO_RANGE_MASK) << 8; | ||
541 | limit = (io_limit_lo & PCI_IO_RANGE_MASK) << 8; | ||
542 | |||
543 | if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { | ||
544 | u16 io_base_hi, io_limit_hi; | ||
545 | |||
546 | pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi); | ||
547 | pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi); | ||
548 | base |= (io_base_hi << 16); | ||
549 | limit |= (io_limit_hi << 16); | ||
550 | } | ||
551 | |||
552 | res = bus->resource[0]; | ||
553 | if (base <= limit) { | ||
554 | res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; | ||
555 | if (!res->start) | ||
556 | res->start = base; | ||
557 | if (!res->end) | ||
558 | res->end = limit + 0xfff; | ||
559 | pci_resource_adjust(res, &pbm->io_space); | ||
560 | } | ||
561 | |||
562 | pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo); | ||
563 | pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo); | ||
564 | base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16; | ||
565 | limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; | ||
566 | |||
567 | res = bus->resource[1]; | ||
568 | if (base <= limit) { | ||
569 | res->flags = ((mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | | ||
570 | IORESOURCE_MEM); | ||
571 | res->start = base; | ||
572 | res->end = limit + 0xfffff; | ||
573 | pci_resource_adjust(res, &pbm->mem_space); | ||
574 | } | ||
575 | |||
576 | pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); | ||
577 | pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo); | ||
578 | base = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16; | ||
579 | limit = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16; | ||
580 | |||
581 | if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { | ||
582 | u32 mem_base_hi, mem_limit_hi; | ||
583 | |||
584 | pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi); | ||
585 | pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi); | ||
586 | |||
587 | /* | ||
588 | * Some bridges set the base > limit by default, and some | ||
589 | * (broken) BIOSes do not initialize them. If we find | ||
590 | * this, just assume they are not being used. | ||
591 | */ | ||
592 | if (mem_base_hi <= mem_limit_hi) { | ||
593 | base |= ((long) mem_base_hi) << 32; | ||
594 | limit |= ((long) mem_limit_hi) << 32; | ||
595 | } | ||
596 | } | ||
597 | |||
598 | res = bus->resource[2]; | ||
599 | if (base <= limit) { | ||
600 | res->flags = ((mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | | ||
601 | IORESOURCE_MEM | IORESOURCE_PREFETCH); | ||
602 | res->start = base; | ||
603 | res->end = limit + 0xfffff; | ||
604 | pci_resource_adjust(res, &pbm->mem_space); | ||
605 | } | ||
606 | } | ||
607 | |||
525 | /* Cook up fake bus resources for SUNW,simba PCI bridges which lack | 608 | /* Cook up fake bus resources for SUNW,simba PCI bridges which lack |
526 | * a proper 'ranges' property. | 609 | * a proper 'ranges' property. |
527 | */ | 610 | */ |
@@ -581,13 +664,8 @@ static void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm, | |||
581 | simba = 0; | 664 | simba = 0; |
582 | if (ranges == NULL) { | 665 | if (ranges == NULL) { |
583 | const char *model = of_get_property(node, "model", NULL); | 666 | const char *model = of_get_property(node, "model", NULL); |
584 | if (model && !strcmp(model, "SUNW,simba")) { | 667 | if (model && !strcmp(model, "SUNW,simba")) |
585 | simba = 1; | 668 | simba = 1; |
586 | } else { | ||
587 | printk(KERN_DEBUG "Can't get ranges for PCI-PCI bridge %s\n", | ||
588 | node->full_name); | ||
589 | return; | ||
590 | } | ||
591 | } | 669 | } |
592 | 670 | ||
593 | bus = pci_add_new_bus(dev->bus, dev, busrange[0]); | 671 | bus = pci_add_new_bus(dev->bus, dev, busrange[0]); |
@@ -611,7 +689,10 @@ static void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm, | |||
611 | } | 689 | } |
612 | if (simba) { | 690 | if (simba) { |
613 | apb_fake_ranges(dev, bus, pbm); | 691 | apb_fake_ranges(dev, bus, pbm); |
614 | goto simba_cont; | 692 | goto after_ranges; |
693 | } else if (ranges == NULL) { | ||
694 | pci_cfg_fake_ranges(dev, bus, pbm); | ||
695 | goto after_ranges; | ||
615 | } | 696 | } |
616 | i = 1; | 697 | i = 1; |
617 | for (; len >= 32; len -= 32, ranges += 8) { | 698 | for (; len >= 32; len -= 32, ranges += 8) { |
@@ -650,7 +731,7 @@ static void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm, | |||
650 | */ | 731 | */ |
651 | pci_resource_adjust(res, root); | 732 | pci_resource_adjust(res, root); |
652 | } | 733 | } |
653 | simba_cont: | 734 | after_ranges: |
654 | sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus), | 735 | sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus), |
655 | bus->number); | 736 | bus->number); |
656 | if (ofpci_verbose) | 737 | if (ofpci_verbose) |