diff options
Diffstat (limited to 'arch/sparc64')
-rw-r--r-- | arch/sparc64/kernel/of_device.c | 19 | ||||
-rw-r--r-- | arch/sparc64/kernel/pci.c | 97 |
2 files changed, 103 insertions, 13 deletions
diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c index 16cc46a7187..6676b93219d 100644 --- a/arch/sparc64/kernel/of_device.c +++ b/arch/sparc64/kernel/of_device.c @@ -343,6 +343,15 @@ static int of_bus_simba_match(struct device_node *np) if (model && !strcmp(model, "SUNW,simba")) return 1; + + /* Treat PCI busses lacking ranges property just like + * simba. + */ + if (!strcmp(np->type, "pci") || !strcmp(np->type, "pciex")) { + if (!of_find_property(np, "ranges", NULL)) + return 1; + } + return 0; } @@ -549,8 +558,6 @@ static int __init build_one_resource(struct device_node *parent, static int __init use_1to1_mapping(struct device_node *pp) { - const char *model; - /* If this is on the PMU bus, don't try to translate it even * if a ranges property exists. */ @@ -567,9 +574,11 @@ static int __init use_1to1_mapping(struct device_node *pp) if (!strcmp(pp->name, "dma")) return 0; - /* Similarly for Simba PCI bridges. */ - model = of_get_property(pp, "model", NULL); - if (model && !strcmp(model, "SUNW,simba")) + /* Similarly for all PCI bridges, if we get this far + * it lacks a ranges property, and this will include + * cases like Simba. + */ + if (!strcmp(pp->type, "pci") || !strcmp(pp->type, "pciex")) return 0; return 1; diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c index 38a32bc95d2..81f4a5ea05f 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, res->end += root->start; } +/* For PCI bus devices which lack a 'ranges' property we interrogate + * the config space values to set the resources, just like the generic + * Linux PCI probing code does. + */ +static void __devinit pci_cfg_fake_ranges(struct pci_dev *dev, + struct pci_bus *bus, + struct pci_pbm_info *pbm) +{ + struct resource *res; + u8 io_base_lo, io_limit_lo; + u16 mem_base_lo, mem_limit_lo; + unsigned long base, limit; + + pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); + pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo); + base = (io_base_lo & PCI_IO_RANGE_MASK) << 8; + limit = (io_limit_lo & PCI_IO_RANGE_MASK) << 8; + + if ((io_base_lo & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) { + u16 io_base_hi, io_limit_hi; + + pci_read_config_word(dev, PCI_IO_BASE_UPPER16, &io_base_hi); + pci_read_config_word(dev, PCI_IO_LIMIT_UPPER16, &io_limit_hi); + base |= (io_base_hi << 16); + limit |= (io_limit_hi << 16); + } + + res = bus->resource[0]; + if (base <= limit) { + res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; + if (!res->start) + res->start = base; + if (!res->end) + res->end = limit + 0xfff; + pci_resource_adjust(res, &pbm->io_space); + } + + pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo); + pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo); + base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16; + limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; + + res = bus->resource[1]; + if (base <= limit) { + res->flags = ((mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | + IORESOURCE_MEM); + res->start = base; + res->end = limit + 0xfffff; + pci_resource_adjust(res, &pbm->mem_space); + } + + pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); + pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo); + base = (mem_base_lo & PCI_PREF_RANGE_MASK) << 16; + limit = (mem_limit_lo & PCI_PREF_RANGE_MASK) << 16; + + if ((mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) { + u32 mem_base_hi, mem_limit_hi; + + pci_read_config_dword(dev, PCI_PREF_BASE_UPPER32, &mem_base_hi); + pci_read_config_dword(dev, PCI_PREF_LIMIT_UPPER32, &mem_limit_hi); + + /* + * Some bridges set the base > limit by default, and some + * (broken) BIOSes do not initialize them. If we find + * this, just assume they are not being used. + */ + if (mem_base_hi <= mem_limit_hi) { + base |= ((long) mem_base_hi) << 32; + limit |= ((long) mem_limit_hi) << 32; + } + } + + res = bus->resource[2]; + if (base <= limit) { + res->flags = ((mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | + IORESOURCE_MEM | IORESOURCE_PREFETCH); + res->start = base; + res->end = limit + 0xfffff; + pci_resource_adjust(res, &pbm->mem_space); + } +} + /* Cook up fake bus resources for SUNW,simba PCI bridges which lack * a proper 'ranges' property. */ @@ -581,13 +664,8 @@ static void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm, simba = 0; if (ranges == NULL) { const char *model = of_get_property(node, "model", NULL); - if (model && !strcmp(model, "SUNW,simba")) { + if (model && !strcmp(model, "SUNW,simba")) simba = 1; - } else { - printk(KERN_DEBUG "Can't get ranges for PCI-PCI bridge %s\n", - node->full_name); - return; - } } 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, } if (simba) { apb_fake_ranges(dev, bus, pbm); - goto simba_cont; + goto after_ranges; + } else if (ranges == NULL) { + pci_cfg_fake_ranges(dev, bus, pbm); + goto after_ranges; } i = 1; for (; len >= 32; len -= 32, ranges += 8) { @@ -650,7 +731,7 @@ static void __devinit of_scan_pci_bridge(struct pci_pbm_info *pbm, */ pci_resource_adjust(res, root); } -simba_cont: +after_ranges: sprintf(bus->name, "PCI Bus %04x:%02x", pci_domain_nr(bus), bus->number); if (ofpci_verbose) |