11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * drivers/pci/bus.c 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * From setup-res.c, by: 51da177e4SLinus Torvalds * Dave Rusling (david.rusling@reo.mts.dec.com) 61da177e4SLinus Torvalds * David Mosberger (davidm@cs.arizona.edu) 71da177e4SLinus Torvalds * David Miller (davem@redhat.com) 81da177e4SLinus Torvalds * Ivan Kokshaysky (ink@jurassic.park.msu.ru) 91da177e4SLinus Torvalds */ 101da177e4SLinus Torvalds #include <linux/module.h> 111da177e4SLinus Torvalds #include <linux/kernel.h> 121da177e4SLinus Torvalds #include <linux/pci.h> 131da177e4SLinus Torvalds #include <linux/errno.h> 141da177e4SLinus Torvalds #include <linux/ioport.h> 151da177e4SLinus Torvalds #include <linux/proc_fs.h> 165a0e3ad6STejun Heo #include <linux/slab.h> 171da177e4SLinus Torvalds 181da177e4SLinus Torvalds #include "pci.h" 191da177e4SLinus Torvalds 200efd5aabSBjorn Helgaas void pci_add_resource_offset(struct list_head *resources, struct resource *res, 210efd5aabSBjorn Helgaas resource_size_t offset) 2245ca9e97SBjorn Helgaas { 2314d76b68SJiang Liu struct resource_entry *entry; 2445ca9e97SBjorn Helgaas 2514d76b68SJiang Liu entry = resource_list_create_entry(res, 0); 2614d76b68SJiang Liu if (!entry) { 270efd5aabSBjorn Helgaas printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res); 2845ca9e97SBjorn Helgaas return; 2945ca9e97SBjorn Helgaas } 3045ca9e97SBjorn Helgaas 3114d76b68SJiang Liu entry->offset = offset; 3214d76b68SJiang Liu resource_list_add_tail(entry, resources); 330efd5aabSBjorn Helgaas } 340efd5aabSBjorn Helgaas EXPORT_SYMBOL(pci_add_resource_offset); 350efd5aabSBjorn Helgaas 360efd5aabSBjorn Helgaas void pci_add_resource(struct list_head *resources, struct resource *res) 370efd5aabSBjorn Helgaas { 380efd5aabSBjorn Helgaas pci_add_resource_offset(resources, res, 0); 3945ca9e97SBjorn Helgaas } 4045ca9e97SBjorn Helgaas EXPORT_SYMBOL(pci_add_resource); 4145ca9e97SBjorn Helgaas 4245ca9e97SBjorn Helgaas void pci_free_resource_list(struct list_head *resources) 4345ca9e97SBjorn Helgaas { 4414d76b68SJiang Liu resource_list_free(resources); 4545ca9e97SBjorn Helgaas } 4645ca9e97SBjorn Helgaas EXPORT_SYMBOL(pci_free_resource_list); 4745ca9e97SBjorn Helgaas 482fe2abf8SBjorn Helgaas void pci_bus_add_resource(struct pci_bus *bus, struct resource *res, 492fe2abf8SBjorn Helgaas unsigned int flags) 502fe2abf8SBjorn Helgaas { 512fe2abf8SBjorn Helgaas struct pci_bus_resource *bus_res; 522fe2abf8SBjorn Helgaas 532fe2abf8SBjorn Helgaas bus_res = kzalloc(sizeof(struct pci_bus_resource), GFP_KERNEL); 542fe2abf8SBjorn Helgaas if (!bus_res) { 552fe2abf8SBjorn Helgaas dev_err(&bus->dev, "can't add %pR resource\n", res); 562fe2abf8SBjorn Helgaas return; 572fe2abf8SBjorn Helgaas } 582fe2abf8SBjorn Helgaas 592fe2abf8SBjorn Helgaas bus_res->res = res; 602fe2abf8SBjorn Helgaas bus_res->flags = flags; 612fe2abf8SBjorn Helgaas list_add_tail(&bus_res->list, &bus->resources); 622fe2abf8SBjorn Helgaas } 632fe2abf8SBjorn Helgaas 642fe2abf8SBjorn Helgaas struct resource *pci_bus_resource_n(const struct pci_bus *bus, int n) 652fe2abf8SBjorn Helgaas { 662fe2abf8SBjorn Helgaas struct pci_bus_resource *bus_res; 672fe2abf8SBjorn Helgaas 682fe2abf8SBjorn Helgaas if (n < PCI_BRIDGE_RESOURCE_NUM) 692fe2abf8SBjorn Helgaas return bus->resource[n]; 702fe2abf8SBjorn Helgaas 712fe2abf8SBjorn Helgaas n -= PCI_BRIDGE_RESOURCE_NUM; 722fe2abf8SBjorn Helgaas list_for_each_entry(bus_res, &bus->resources, list) { 732fe2abf8SBjorn Helgaas if (n-- == 0) 742fe2abf8SBjorn Helgaas return bus_res->res; 752fe2abf8SBjorn Helgaas } 762fe2abf8SBjorn Helgaas return NULL; 772fe2abf8SBjorn Helgaas } 782fe2abf8SBjorn Helgaas EXPORT_SYMBOL_GPL(pci_bus_resource_n); 792fe2abf8SBjorn Helgaas 802fe2abf8SBjorn Helgaas void pci_bus_remove_resources(struct pci_bus *bus) 812fe2abf8SBjorn Helgaas { 822fe2abf8SBjorn Helgaas int i; 83817a2685SYinghai Lu struct pci_bus_resource *bus_res, *tmp; 842fe2abf8SBjorn Helgaas 852fe2abf8SBjorn Helgaas for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) 867736a05aSStephen Hemminger bus->resource[i] = NULL; 872fe2abf8SBjorn Helgaas 88817a2685SYinghai Lu list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) { 89817a2685SYinghai Lu list_del(&bus_res->list); 90817a2685SYinghai Lu kfree(bus_res); 91817a2685SYinghai Lu } 922fe2abf8SBjorn Helgaas } 932fe2abf8SBjorn Helgaas 94*950334bcSBjorn Helgaas int devm_request_pci_bus_resources(struct device *dev, 95*950334bcSBjorn Helgaas struct list_head *resources) 96*950334bcSBjorn Helgaas { 97*950334bcSBjorn Helgaas struct resource_entry *win; 98*950334bcSBjorn Helgaas struct resource *parent, *res; 99*950334bcSBjorn Helgaas int err; 100*950334bcSBjorn Helgaas 101*950334bcSBjorn Helgaas resource_list_for_each_entry(win, resources) { 102*950334bcSBjorn Helgaas res = win->res; 103*950334bcSBjorn Helgaas switch (resource_type(res)) { 104*950334bcSBjorn Helgaas case IORESOURCE_IO: 105*950334bcSBjorn Helgaas parent = &ioport_resource; 106*950334bcSBjorn Helgaas break; 107*950334bcSBjorn Helgaas case IORESOURCE_MEM: 108*950334bcSBjorn Helgaas parent = &iomem_resource; 109*950334bcSBjorn Helgaas break; 110*950334bcSBjorn Helgaas default: 111*950334bcSBjorn Helgaas continue; 112*950334bcSBjorn Helgaas } 113*950334bcSBjorn Helgaas 114*950334bcSBjorn Helgaas err = devm_request_resource(dev, parent, res); 115*950334bcSBjorn Helgaas if (err) 116*950334bcSBjorn Helgaas return err; 117*950334bcSBjorn Helgaas } 118*950334bcSBjorn Helgaas 119*950334bcSBjorn Helgaas return 0; 120*950334bcSBjorn Helgaas } 121*950334bcSBjorn Helgaas EXPORT_SYMBOL_GPL(devm_request_pci_bus_resources); 122*950334bcSBjorn Helgaas 123f75b99d5SYinghai Lu static struct pci_bus_region pci_32_bit = {0, 0xffffffffULL}; 1243a9ad0b4SYinghai Lu #ifdef CONFIG_PCI_BUS_ADDR_T_64BIT 125f75b99d5SYinghai Lu static struct pci_bus_region pci_64_bit = {0, 1263a9ad0b4SYinghai Lu (pci_bus_addr_t) 0xffffffffffffffffULL}; 1273a9ad0b4SYinghai Lu static struct pci_bus_region pci_high = {(pci_bus_addr_t) 0x100000000ULL, 1283a9ad0b4SYinghai Lu (pci_bus_addr_t) 0xffffffffffffffffULL}; 129f75b99d5SYinghai Lu #endif 130f75b99d5SYinghai Lu 131f75b99d5SYinghai Lu /* 132f75b99d5SYinghai Lu * @res contains CPU addresses. Clip it so the corresponding bus addresses 133f75b99d5SYinghai Lu * on @bus are entirely within @region. This is used to control the bus 134f75b99d5SYinghai Lu * addresses of resources we allocate, e.g., we may need a resource that 135f75b99d5SYinghai Lu * can be mapped by a 32-bit BAR. 1361da177e4SLinus Torvalds */ 137f75b99d5SYinghai Lu static void pci_clip_resource_to_region(struct pci_bus *bus, 138f75b99d5SYinghai Lu struct resource *res, 139f75b99d5SYinghai Lu struct pci_bus_region *region) 140f75b99d5SYinghai Lu { 141f75b99d5SYinghai Lu struct pci_bus_region r; 142f75b99d5SYinghai Lu 143f75b99d5SYinghai Lu pcibios_resource_to_bus(bus, &r, res); 144f75b99d5SYinghai Lu if (r.start < region->start) 145f75b99d5SYinghai Lu r.start = region->start; 146f75b99d5SYinghai Lu if (r.end > region->end) 147f75b99d5SYinghai Lu r.end = region->end; 148f75b99d5SYinghai Lu 149f75b99d5SYinghai Lu if (r.end < r.start) 150f75b99d5SYinghai Lu res->end = res->start - 1; 151f75b99d5SYinghai Lu else 152f75b99d5SYinghai Lu pcibios_bus_to_resource(bus, res, &r); 153f75b99d5SYinghai Lu } 154f75b99d5SYinghai Lu 155f75b99d5SYinghai Lu static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, 156e31dd6e4SGreg Kroah-Hartman resource_size_t size, resource_size_t align, 157664c2848SBjorn Helgaas resource_size_t min, unsigned long type_mask, 158b26b2d49SDominik Brodowski resource_size_t (*alignf)(void *, 1593b7a17fcSDominik Brodowski const struct resource *, 160b26b2d49SDominik Brodowski resource_size_t, 161e31dd6e4SGreg Kroah-Hartman resource_size_t), 162f75b99d5SYinghai Lu void *alignf_data, 163f75b99d5SYinghai Lu struct pci_bus_region *region) 1641da177e4SLinus Torvalds { 165f75b99d5SYinghai Lu int i, ret; 166f75b99d5SYinghai Lu struct resource *r, avail; 167f75b99d5SYinghai Lu resource_size_t max; 1681da177e4SLinus Torvalds 169aa11fc58SBjorn Helgaas type_mask |= IORESOURCE_TYPE_BITS; 1701da177e4SLinus Torvalds 1716db45b76SBjorn Helgaas pci_bus_for_each_resource(bus, r, i) { 1723460baa6SChristoph Biedl resource_size_t min_used = min; 1733460baa6SChristoph Biedl 1746db45b76SBjorn Helgaas if (!r) 1756db45b76SBjorn Helgaas continue; 1766db45b76SBjorn Helgaas 1771da177e4SLinus Torvalds /* type_mask must match */ 1781da177e4SLinus Torvalds if ((res->flags ^ r->flags) & type_mask) 1791da177e4SLinus Torvalds continue; 1801da177e4SLinus Torvalds 1811da177e4SLinus Torvalds /* We cannot allocate a non-prefetching resource 1821da177e4SLinus Torvalds from a pre-fetching area */ 1831da177e4SLinus Torvalds if ((r->flags & IORESOURCE_PREFETCH) && 1841da177e4SLinus Torvalds !(res->flags & IORESOURCE_PREFETCH)) 1851da177e4SLinus Torvalds continue; 1861da177e4SLinus Torvalds 187f75b99d5SYinghai Lu avail = *r; 188f75b99d5SYinghai Lu pci_clip_resource_to_region(bus, &avail, region); 189f75b99d5SYinghai Lu 190f75b99d5SYinghai Lu /* 191f75b99d5SYinghai Lu * "min" is typically PCIBIOS_MIN_IO or PCIBIOS_MIN_MEM to 192f75b99d5SYinghai Lu * protect badly documented motherboard resources, but if 193f75b99d5SYinghai Lu * this is an already-configured bridge window, its start 194f75b99d5SYinghai Lu * overrides "min". 195f75b99d5SYinghai Lu */ 196f75b99d5SYinghai Lu if (avail.start) 1973460baa6SChristoph Biedl min_used = avail.start; 198f75b99d5SYinghai Lu 199f75b99d5SYinghai Lu max = avail.end; 200f75b99d5SYinghai Lu 2011da177e4SLinus Torvalds /* Ok, try it out.. */ 2023460baa6SChristoph Biedl ret = allocate_resource(r, res, size, min_used, max, 203f75b99d5SYinghai Lu align, alignf, alignf_data); 2041da177e4SLinus Torvalds if (ret == 0) 205f75b99d5SYinghai Lu return 0; 2061da177e4SLinus Torvalds } 207f75b99d5SYinghai Lu return -ENOMEM; 208f75b99d5SYinghai Lu } 209f75b99d5SYinghai Lu 2101da177e4SLinus Torvalds /** 2111da177e4SLinus Torvalds * pci_bus_alloc_resource - allocate a resource from a parent bus 2121da177e4SLinus Torvalds * @bus: PCI bus 2131da177e4SLinus Torvalds * @res: resource to allocate 2141da177e4SLinus Torvalds * @size: size of resource to allocate 2151da177e4SLinus Torvalds * @align: alignment of resource to allocate 2161da177e4SLinus Torvalds * @min: minimum /proc/iomem address to allocate 2171da177e4SLinus Torvalds * @type_mask: IORESOURCE_* type flags 2181da177e4SLinus Torvalds * @alignf: resource alignment function 2191da177e4SLinus Torvalds * @alignf_data: data argument for resource alignment function 2201da177e4SLinus Torvalds * 2211da177e4SLinus Torvalds * Given the PCI bus a device resides on, the size, minimum address, 2221da177e4SLinus Torvalds * alignment and type, try to find an acceptable resource allocation 2231da177e4SLinus Torvalds * for a specific device resource. 2241da177e4SLinus Torvalds */ 225d56dbf5bSYinghai Lu int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, 2261da177e4SLinus Torvalds resource_size_t size, resource_size_t align, 227664c2848SBjorn Helgaas resource_size_t min, unsigned long type_mask, 2281da177e4SLinus Torvalds resource_size_t (*alignf)(void *, 2291da177e4SLinus Torvalds const struct resource *, 2301da177e4SLinus Torvalds resource_size_t, 2311da177e4SLinus Torvalds resource_size_t), 2321da177e4SLinus Torvalds void *alignf_data) 2331da177e4SLinus Torvalds { 2343a9ad0b4SYinghai Lu #ifdef CONFIG_PCI_BUS_ADDR_T_64BIT 235d56dbf5bSYinghai Lu int rc; 236d56dbf5bSYinghai Lu 237d56dbf5bSYinghai Lu if (res->flags & IORESOURCE_MEM_64) { 238d56dbf5bSYinghai Lu rc = pci_bus_alloc_from_region(bus, res, size, align, min, 239d56dbf5bSYinghai Lu type_mask, alignf, alignf_data, 240d56dbf5bSYinghai Lu &pci_high); 241d56dbf5bSYinghai Lu if (rc == 0) 242d56dbf5bSYinghai Lu return 0; 243d56dbf5bSYinghai Lu 244f75b99d5SYinghai Lu return pci_bus_alloc_from_region(bus, res, size, align, min, 245f75b99d5SYinghai Lu type_mask, alignf, alignf_data, 246f75b99d5SYinghai Lu &pci_64_bit); 247d56dbf5bSYinghai Lu } 248f75b99d5SYinghai Lu #endif 2491da177e4SLinus Torvalds 250f75b99d5SYinghai Lu return pci_bus_alloc_from_region(bus, res, size, align, min, 251f75b99d5SYinghai Lu type_mask, alignf, alignf_data, 252f75b99d5SYinghai Lu &pci_32_bit); 2531da177e4SLinus Torvalds } 254b7fe9434SRyan Desfosses EXPORT_SYMBOL(pci_bus_alloc_resource); 2551da177e4SLinus Torvalds 2560f7e7aeeSYinghai Lu /* 2570f7e7aeeSYinghai Lu * The @idx resource of @dev should be a PCI-PCI bridge window. If this 2580f7e7aeeSYinghai Lu * resource fits inside a window of an upstream bridge, do nothing. If it 2590f7e7aeeSYinghai Lu * overlaps an upstream window but extends outside it, clip the resource so 2600f7e7aeeSYinghai Lu * it fits completely inside. 2610f7e7aeeSYinghai Lu */ 2620f7e7aeeSYinghai Lu bool pci_bus_clip_resource(struct pci_dev *dev, int idx) 2630f7e7aeeSYinghai Lu { 2640f7e7aeeSYinghai Lu struct pci_bus *bus = dev->bus; 2650f7e7aeeSYinghai Lu struct resource *res = &dev->resource[idx]; 2660f7e7aeeSYinghai Lu struct resource orig_res = *res; 2670f7e7aeeSYinghai Lu struct resource *r; 2680f7e7aeeSYinghai Lu int i; 2690f7e7aeeSYinghai Lu 2700f7e7aeeSYinghai Lu pci_bus_for_each_resource(bus, r, i) { 2710f7e7aeeSYinghai Lu resource_size_t start, end; 2720f7e7aeeSYinghai Lu 2730f7e7aeeSYinghai Lu if (!r) 2740f7e7aeeSYinghai Lu continue; 2750f7e7aeeSYinghai Lu 2760f7e7aeeSYinghai Lu if (resource_type(res) != resource_type(r)) 2770f7e7aeeSYinghai Lu continue; 2780f7e7aeeSYinghai Lu 2790f7e7aeeSYinghai Lu start = max(r->start, res->start); 2800f7e7aeeSYinghai Lu end = min(r->end, res->end); 2810f7e7aeeSYinghai Lu 2820f7e7aeeSYinghai Lu if (start > end) 2830f7e7aeeSYinghai Lu continue; /* no overlap */ 2840f7e7aeeSYinghai Lu 2850f7e7aeeSYinghai Lu if (res->start == start && res->end == end) 2860f7e7aeeSYinghai Lu return false; /* no change */ 2870f7e7aeeSYinghai Lu 2880f7e7aeeSYinghai Lu res->start = start; 2890f7e7aeeSYinghai Lu res->end = end; 290b838b39eSBjorn Helgaas res->flags &= ~IORESOURCE_UNSET; 291b838b39eSBjorn Helgaas orig_res.flags &= ~IORESOURCE_UNSET; 2920f7e7aeeSYinghai Lu dev_printk(KERN_DEBUG, &dev->dev, "%pR clipped to %pR\n", 2930f7e7aeeSYinghai Lu &orig_res, res); 2940f7e7aeeSYinghai Lu 2950f7e7aeeSYinghai Lu return true; 2960f7e7aeeSYinghai Lu } 2970f7e7aeeSYinghai Lu 2980f7e7aeeSYinghai Lu return false; 2990f7e7aeeSYinghai Lu } 3000f7e7aeeSYinghai Lu 3013c449ed0SYinghai Lu void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { } 3023c449ed0SYinghai Lu 3037b77061fSWei Yang void __weak pcibios_bus_add_device(struct pci_dev *pdev) { } 3047b77061fSWei Yang 3051da177e4SLinus Torvalds /** 3064f535093SYinghai Lu * pci_bus_add_device - start driver for a single device 3071da177e4SLinus Torvalds * @dev: device to add 3081da177e4SLinus Torvalds * 3094f535093SYinghai Lu * This adds add sysfs entries and start device drivers 3101da177e4SLinus Torvalds */ 311c893d133SYijing Wang void pci_bus_add_device(struct pci_dev *dev) 3121da177e4SLinus Torvalds { 313b19441afSGreg Kroah-Hartman int retval; 314735bff10SMyron Stowe 3154f535093SYinghai Lu /* 3164f535093SYinghai Lu * Can not put in pci_device_add yet because resources 3174f535093SYinghai Lu * are not assigned yet for some devices. 3184f535093SYinghai Lu */ 3197b77061fSWei Yang pcibios_bus_add_device(dev); 320e253aaf0SYinghai Lu pci_fixup_device(pci_fixup_final, dev); 3214f535093SYinghai Lu pci_create_sysfs_dev_files(dev); 322ef37702eSYinghai Lu pci_proc_attach_device(dev); 3231da177e4SLinus Torvalds 32458d9a38fSYinghai Lu dev->match_driver = true; 32558d9a38fSYinghai Lu retval = device_attach(&dev->dev); 3269a2a5a63SLukas Wunner if (retval < 0 && retval != -EPROBE_DEFER) { 327ab1a187bSBjorn Helgaas dev_warn(&dev->dev, "device attach failed (%d)\n", retval); 328ab1a187bSBjorn Helgaas pci_proc_detach_device(dev); 329ab1a187bSBjorn Helgaas pci_remove_sysfs_dev_files(dev); 330ab1a187bSBjorn Helgaas return; 331ab1a187bSBjorn Helgaas } 33258d9a38fSYinghai Lu 3338a1bc901SGreg Kroah-Hartman dev->is_added = 1; 3341da177e4SLinus Torvalds } 335b7fe9434SRyan Desfosses EXPORT_SYMBOL_GPL(pci_bus_add_device); 3361da177e4SLinus Torvalds 3371da177e4SLinus Torvalds /** 3384f535093SYinghai Lu * pci_bus_add_devices - start driver for PCI devices 3391da177e4SLinus Torvalds * @bus: bus to check for new devices 3401da177e4SLinus Torvalds * 3414f535093SYinghai Lu * Start driver for PCI devices and add some sysfs entries. 3421da177e4SLinus Torvalds */ 343c48f1670Sakpm@linux-foundation.org void pci_bus_add_devices(const struct pci_bus *bus) 3441da177e4SLinus Torvalds { 3451da177e4SLinus Torvalds struct pci_dev *dev; 3463fa16fdbSYu Zhao struct pci_bus *child; 3471da177e4SLinus Torvalds 3481da177e4SLinus Torvalds list_for_each_entry(dev, &bus->devices, bus_list) { 3498a1bc901SGreg Kroah-Hartman /* Skip already-added devices */ 3508a1bc901SGreg Kroah-Hartman if (dev->is_added) 3511da177e4SLinus Torvalds continue; 352c893d133SYijing Wang pci_bus_add_device(dev); 3531da177e4SLinus Torvalds } 3541da177e4SLinus Torvalds 3551da177e4SLinus Torvalds list_for_each_entry(dev, &bus->devices, bus_list) { 3561e398eaeSLukas Wunner /* Skip if device attach failed */ 3571e398eaeSLukas Wunner if (!dev->is_added) 3581e398eaeSLukas Wunner continue; 3593fa16fdbSYu Zhao child = dev->subordinate; 360981cf9eaSJiang Liu if (child) 3613fa16fdbSYu Zhao pci_bus_add_devices(child); 3621da177e4SLinus Torvalds } 3631da177e4SLinus Torvalds } 364b7fe9434SRyan Desfosses EXPORT_SYMBOL(pci_bus_add_devices); 3651da177e4SLinus Torvalds 366cecf4864SPaul Mackerras /** pci_walk_bus - walk devices on/under bus, calling callback. 367cecf4864SPaul Mackerras * @top bus whose devices should be walked 368cecf4864SPaul Mackerras * @cb callback to be called for each device found 369cecf4864SPaul Mackerras * @userdata arbitrary pointer to be passed to callback. 370cecf4864SPaul Mackerras * 371cecf4864SPaul Mackerras * Walk the given bus, including any bridged devices 372cecf4864SPaul Mackerras * on buses under this bus. Call the provided callback 373cecf4864SPaul Mackerras * on each device found. 37470298c6eSZhang, Yanmin * 37570298c6eSZhang, Yanmin * We check the return of @cb each time. If it returns anything 37670298c6eSZhang, Yanmin * other than 0, we break out. 37770298c6eSZhang, Yanmin * 378cecf4864SPaul Mackerras */ 37970298c6eSZhang, Yanmin void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), 380cecf4864SPaul Mackerras void *userdata) 381cecf4864SPaul Mackerras { 382cecf4864SPaul Mackerras struct pci_dev *dev; 383cecf4864SPaul Mackerras struct pci_bus *bus; 384cecf4864SPaul Mackerras struct list_head *next; 38570298c6eSZhang, Yanmin int retval; 386cecf4864SPaul Mackerras 387cecf4864SPaul Mackerras bus = top; 388d71374daSZhang Yanmin down_read(&pci_bus_sem); 389cecf4864SPaul Mackerras next = top->devices.next; 390cecf4864SPaul Mackerras for (;;) { 391cecf4864SPaul Mackerras if (next == &bus->devices) { 392cecf4864SPaul Mackerras /* end of this bus, go up or finish */ 393cecf4864SPaul Mackerras if (bus == top) 394cecf4864SPaul Mackerras break; 395cecf4864SPaul Mackerras next = bus->self->bus_list.next; 396cecf4864SPaul Mackerras bus = bus->self->bus; 397cecf4864SPaul Mackerras continue; 398cecf4864SPaul Mackerras } 399cecf4864SPaul Mackerras dev = list_entry(next, struct pci_dev, bus_list); 400cecf4864SPaul Mackerras if (dev->subordinate) { 401cecf4864SPaul Mackerras /* this is a pci-pci bridge, do its devices next */ 402cecf4864SPaul Mackerras next = dev->subordinate->devices.next; 403cecf4864SPaul Mackerras bus = dev->subordinate; 404cecf4864SPaul Mackerras } else 405cecf4864SPaul Mackerras next = dev->bus_list.next; 406cecf4864SPaul Mackerras 40770298c6eSZhang, Yanmin retval = cb(dev, userdata); 40870298c6eSZhang, Yanmin if (retval) 40970298c6eSZhang, Yanmin break; 410cecf4864SPaul Mackerras } 411d71374daSZhang Yanmin up_read(&pci_bus_sem); 412cecf4864SPaul Mackerras } 4137c94def8SKonrad Rzeszutek Wilk EXPORT_SYMBOL_GPL(pci_walk_bus); 414cecf4864SPaul Mackerras 415fe830ef6SJiang Liu struct pci_bus *pci_bus_get(struct pci_bus *bus) 416fe830ef6SJiang Liu { 417fe830ef6SJiang Liu if (bus) 418fe830ef6SJiang Liu get_device(&bus->dev); 419fe830ef6SJiang Liu return bus; 420fe830ef6SJiang Liu } 421fe830ef6SJiang Liu EXPORT_SYMBOL(pci_bus_get); 422fe830ef6SJiang Liu 423fe830ef6SJiang Liu void pci_bus_put(struct pci_bus *bus) 424fe830ef6SJiang Liu { 425fe830ef6SJiang Liu if (bus) 426fe830ef6SJiang Liu put_device(&bus->dev); 427fe830ef6SJiang Liu } 428fe830ef6SJiang Liu EXPORT_SYMBOL(pci_bus_put); 429