diff options
Diffstat (limited to 'drivers/pci/probe.c')
-rw-r--r-- | drivers/pci/probe.c | 298 |
1 files changed, 216 insertions, 82 deletions
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 71eac9cd724..5e1ca3c58a7 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -15,6 +15,8 @@ #define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ #define CARDBUS_RESERVE_BUSNR 3 +static LIST_HEAD(pci_host_bridges); + /* Ugh. Need to stop exporting this to modules. */ LIST_HEAD(pci_root_buses); EXPORT_SYMBOL(pci_root_buses); @@ -42,6 +44,82 @@ int no_pci_devices(void) } EXPORT_SYMBOL(no_pci_devices); +static struct pci_host_bridge *pci_host_bridge(struct pci_dev *dev) +{ + struct pci_bus *bus; + struct pci_host_bridge *bridge; + + bus = dev->bus; + while (bus->parent) + bus = bus->parent; + + list_for_each_entry(bridge, &pci_host_bridges, list) { + if (bridge->bus == bus) + return bridge; + } + + return NULL; +} + +static bool resource_contains(struct resource *res1, struct resource *res2) +{ + return res1->start <= res2->start && res1->end >= res2->end; +} + +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + struct pci_host_bridge *bridge = pci_host_bridge(dev); + struct pci_host_bridge_window *window; + resource_size_t offset = 0; + + list_for_each_entry(window, &bridge->windows, list) { + if (resource_type(res) != resource_type(window->res)) + continue; + + if (resource_contains(window->res, res)) { + offset = window->offset; + break; + } + } + + region->start = res->start - offset; + region->end = res->end - offset; +} +EXPORT_SYMBOL(pcibios_resource_to_bus); + +static bool region_contains(struct pci_bus_region *region1, + struct pci_bus_region *region2) +{ + return region1->start <= region2->start && region1->end >= region2->end; +} + +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) +{ + struct pci_host_bridge *bridge = pci_host_bridge(dev); + struct pci_host_bridge_window *window; + struct pci_bus_region bus_region; + resource_size_t offset = 0; + + list_for_each_entry(window, &bridge->windows, list) { + if (resource_type(res) != resource_type(window->res)) + continue; + + bus_region.start = window->res->start - window->offset; + bus_region.end = window->res->end - window->offset; + + if (region_contains(&bus_region, region)) { + offset = window->offset; + break; + } + } + + res->start = region->start + offset; + res->end = region->end + offset; +} +EXPORT_SYMBOL(pcibios_bus_to_resource); + /* * PCI Bus Class */ @@ -135,6 +213,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, { u32 l, sz, mask; u16 orig_cmd; + struct pci_bus_region region; mask = type ? PCI_ROM_ADDRESS_MASK : ~0; @@ -214,11 +293,13 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, /* Address above 32-bit boundary; disable the BAR */ pci_write_config_dword(dev, pos, 0); pci_write_config_dword(dev, pos + 4, 0); - res->start = 0; - res->end = sz64; + region.start = 0; + region.end = sz64; + pcibios_bus_to_resource(dev, res, ®ion); } else { - res->start = l64; - res->end = l64 + sz64; + region.start = l64; + region.end = l64 + sz64; + pcibios_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n", pos, res); } @@ -228,8 +309,9 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, if (!sz) goto fail; - res->start = l; - res->end = l + sz; + region.start = l; + region.end = l + sz; + pcibios_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n", pos, res); } @@ -266,7 +348,8 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) struct pci_dev *dev = child->self; u8 io_base_lo, io_limit_lo; unsigned long base, limit; - struct resource *res; + struct pci_bus_region region; + struct resource *res, res2; res = child->resource[0]; pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); @@ -284,10 +367,14 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) if (base && base <= limit) { res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; + res2.flags = res->flags; + region.start = base; + region.end = limit + 0xfff; + pcibios_bus_to_resource(dev, &res2, ®ion); if (!res->start) - res->start = base; + res->start = res2.start; if (!res->end) - res->end = limit + 0xfff; + res->end = res2.end; dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } @@ -297,6 +384,7 @@ static void __devinit pci_read_bridge_mmio(struct pci_bus *child) struct pci_dev *dev = child->self; u16 mem_base_lo, mem_limit_lo; unsigned long base, limit; + struct pci_bus_region region; struct resource *res; res = child->resource[1]; @@ -306,8 +394,9 @@ static void __devinit pci_read_bridge_mmio(struct pci_bus *child) limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; if (base && base <= limit) { res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM; - res->start = base; - res->end = limit + 0xfffff; + region.start = base; + region.end = limit + 0xfffff; + pcibios_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } @@ -317,6 +406,7 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child) struct pci_dev *dev = child->self; u16 mem_base_lo, mem_limit_lo; unsigned long base, limit; + struct pci_bus_region region; struct resource *res; res = child->resource[2]; @@ -353,8 +443,9 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child) IORESOURCE_MEM | IORESOURCE_PREFETCH; if (res->flags & PCI_PREF_RANGE_TYPE_64) res->flags |= IORESOURCE_MEM_64; - res->start = base; - res->end = limit + 0xfffff; + region.start = base; + region.end = limit + 0xfffff; + pcibios_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } @@ -900,6 +991,8 @@ int pci_setup_device(struct pci_dev *dev) u8 hdr_type; struct pci_slot *slot; int pos = 0; + struct pci_bus_region region; + struct resource *res; if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type)) return -EIO; @@ -926,12 +1019,10 @@ int pci_setup_device(struct pci_dev *dev) pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); dev->revision = class & 0xff; - class >>= 8; /* upper 3 bytes */ - dev->class = class; - class >>= 8; + dev->class = class >> 8; /* upper 3 bytes */ - dev_printk(KERN_DEBUG, &dev->dev, "[%04x:%04x] type %d class %#08x\n", - dev->vendor, dev->device, dev->hdr_type, class); + dev_printk(KERN_DEBUG, &dev->dev, "[%04x:%04x] type %02x class %#08x\n", + dev->vendor, dev->device, dev->hdr_type, dev->class); /* need to have dev->class ready */ dev->cfg_size = pci_cfg_space_size(dev); @@ -963,20 +1054,28 @@ int pci_setup_device(struct pci_dev *dev) u8 progif; pci_read_config_byte(dev, PCI_CLASS_PROG, &progif); if ((progif & 1) == 0) { - dev->resource[0].start = 0x1F0; - dev->resource[0].end = 0x1F7; - dev->resource[0].flags = LEGACY_IO_RESOURCE; - dev->resource[1].start = 0x3F6; - dev->resource[1].end = 0x3F6; - dev->resource[1].flags = LEGACY_IO_RESOURCE; + region.start = 0x1F0; + region.end = 0x1F7; + res = &dev->resource[0]; + res->flags = LEGACY_IO_RESOURCE; + pcibios_bus_to_resource(dev, res, ®ion); + region.start = 0x3F6; + region.end = 0x3F6; + res = &dev->resource[1]; + res->flags = LEGACY_IO_RESOURCE; + pcibios_bus_to_resource(dev, res, ®ion); } if ((progif & 4) == 0) { - dev->resource[2].start = 0x170; - dev->resource[2].end = 0x177; - dev->resource[2].flags = LEGACY_IO_RESOURCE; - dev->resource[3].start = 0x376; - dev->resource[3].end = 0x376; - dev->resource[3].flags = LEGACY_IO_RESOURCE; + region.start = 0x170; + region.end = 0x177; + res = &dev->resource[2]; + res->flags = LEGACY_IO_RESOURCE; + pcibios_bus_to_resource(dev, res, ®ion); + region.start = 0x376; + region.end = 0x376; + res = &dev->resource[3]; + res->flags = LEGACY_IO_RESOURCE; + pcibios_bus_to_resource(dev, res, ®ion); } } break; @@ -1013,8 +1112,8 @@ int pci_setup_device(struct pci_dev *dev) return -EIO; bad: - dev_err(&dev->dev, "ignoring class %02x (doesn't match header " - "type %02x)\n", class, dev->hdr_type); + dev_err(&dev->dev, "ignoring class %#08x (doesn't match header " + "type %02x)\n", dev->class, dev->hdr_type); dev->class = PCI_CLASS_NOT_DEFINED; } @@ -1026,6 +1125,7 @@ static void pci_release_capabilities(struct pci_dev *dev) { pci_vpd_release(dev); pci_iov_release(dev); + pci_free_cap_save_buffers(dev); } /** @@ -1118,40 +1218,54 @@ struct pci_dev *alloc_pci_dev(void) } EXPORT_SYMBOL(alloc_pci_dev); -/* - * Read the config data for a PCI device, sanity-check it - * and fill in the dev structure... - */ -static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) +bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l, + int crs_timeout) { - struct pci_dev *dev; - u32 l; int delay = 1; - if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l)) - return NULL; + if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l)) + return false; /* some broken boards return 0 or ~0 if a slot is empty: */ - if (l == 0xffffffff || l == 0x00000000 || - l == 0x0000ffff || l == 0xffff0000) - return NULL; + if (*l == 0xffffffff || *l == 0x00000000 || + *l == 0x0000ffff || *l == 0xffff0000) + return false; /* Configuration request Retry Status */ - while (l == 0xffff0001) { + while (*l == 0xffff0001) { + if (!crs_timeout) + return false; + msleep(delay); delay *= 2; - if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l)) - return NULL; + if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l)) + return false; /* Card hasn't responded in 60 seconds? Must be stuck. */ - if (delay > 60 * 1000) { + if (delay > crs_timeout) { printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not " "responding\n", pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); - return NULL; + return false; } } + return true; +} +EXPORT_SYMBOL(pci_bus_read_dev_vendor_id); + +/* + * Read the config data for a PCI device, sanity-check it + * and fill in the dev structure... + */ +static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) +{ + struct pci_dev *dev; + u32 l; + + if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000)) + return NULL; + dev = alloc_pci_dev(); if (!dev) return NULL; @@ -1212,6 +1326,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) /* Fix up broken headers */ pci_fixup_device(pci_fixup_header, dev); + /* moved out from quirk header fixup code */ + pci_reassigndev_resource_alignment(dev); + /* Clear the state_saved flag. */ dev->state_saved = false; @@ -1530,21 +1647,27 @@ unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) struct pci_bus *pci_create_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { - int error, i; + int error; + struct pci_host_bridge *bridge; struct pci_bus *b, *b2; struct device *dev; - struct pci_bus_resource *bus_res, *n; + struct pci_host_bridge_window *window, *n; struct resource *res; + resource_size_t offset; + char bus_addr[64]; + char *fmt; + + bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); + if (!bridge) + return NULL; b = pci_alloc_bus(); if (!b) - return NULL; + goto err_bus; dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - kfree(b); - return NULL; - } + if (!dev) + goto err_dev; b->sysdata = sysdata; b->ops = ops; @@ -1556,10 +1679,6 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, goto err_out; } - down_write(&pci_bus_sem); - list_add_tail(&b->node, &pci_root_buses); - up_write(&pci_bus_sem); - dev->parent = parent; dev->release = pci_release_bus_bridge_dev; dev_set_name(dev, "pci%04x:%02x", pci_domain_nr(b), bus); @@ -1585,31 +1704,53 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, b->number = b->secondary = bus; - /* Add initial resources to the bus */ - list_for_each_entry_safe(bus_res, n, resources, list) - list_move_tail(&bus_res->list, &b->resources); + bridge->bus = b; + INIT_LIST_HEAD(&bridge->windows); if (parent) dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev)); else printk(KERN_INFO "PCI host bridge to bus %s\n", dev_name(&b->dev)); - pci_bus_for_each_resource(b, res, i) { - if (res) - dev_info(&b->dev, "root bus resource %pR\n", res); + /* Add initial resources to the bus */ + list_for_each_entry_safe(window, n, resources, list) { + list_move_tail(&window->list, &bridge->windows); + res = window->res; + offset = window->offset; + pci_bus_add_resource(b, res, 0); + if (offset) { + if (resource_type(res) == IORESOURCE_IO) + fmt = " (bus address [%#06llx-%#06llx])"; + else + fmt = " (bus address [%#010llx-%#010llx])"; + snprintf(bus_addr, sizeof(bus_addr), fmt, + (unsigned long long) (res->start - offset), + (unsigned long long) (res->end - offset)); + } else + bus_addr[0] = '\0'; + dev_info(&b->dev, "root bus resource %pR%s\n", res, bus_addr); } + down_write(&pci_bus_sem); + list_add_tail(&bridge->list, &pci_host_bridges); + list_add_tail(&b->node, &pci_root_buses); + up_write(&pci_bus_sem); + return b; class_dev_reg_err: device_unregister(dev); dev_reg_err: down_write(&pci_bus_sem); + list_del(&bridge->list); list_del(&b->node); up_write(&pci_bus_sem); err_out: kfree(dev); +err_dev: kfree(b); +err_bus: + kfree(bridge); return NULL; } @@ -1667,36 +1808,29 @@ EXPORT_SYMBOL(pci_scan_bus); #ifdef CONFIG_HOTPLUG /** - * pci_rescan_bus - scan a PCI bus for devices. - * @bus: PCI bus to scan + * pci_rescan_bus_bridge_resize - scan a PCI bus for devices. + * @bridge: PCI bridge for the bus to scan * - * Scan a PCI bus and child buses for new devices, adds them, - * and enables them. + * Scan a PCI bus and child buses for new devices, add them, + * and enable them, resizing bridge mmio/io resource if necessary + * and possible. The caller must ensure the child devices are already + * removed for resizing to occur. * * Returns the max number of subordinate bus discovered. */ -unsigned int __ref pci_rescan_bus(struct pci_bus *bus) +unsigned int __ref pci_rescan_bus_bridge_resize(struct pci_dev *bridge) { unsigned int max; - struct pci_dev *dev; + struct pci_bus *bus = bridge->subordinate; max = pci_scan_child_bus(bus); - down_read(&pci_bus_sem); - list_for_each_entry(dev, &bus->devices, bus_list) - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || - dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) - if (dev->subordinate) - pci_bus_size_bridges(dev->subordinate); - up_read(&pci_bus_sem); + pci_assign_unassigned_bridge_resources(bridge); - pci_bus_assign_resources(bus); - pci_enable_bridges(bus); pci_bus_add_devices(bus); return max; } -EXPORT_SYMBOL_GPL(pci_rescan_bus); EXPORT_SYMBOL(pci_add_new_bus); EXPORT_SYMBOL(pci_scan_slot); |