162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * From setup-res.c, by: 462306a36Sopenharmony_ci * Dave Rusling (david.rusling@reo.mts.dec.com) 562306a36Sopenharmony_ci * David Mosberger (davidm@cs.arizona.edu) 662306a36Sopenharmony_ci * David Miller (davem@redhat.com) 762306a36Sopenharmony_ci * Ivan Kokshaysky (ink@jurassic.park.msu.ru) 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/ioport.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/proc_fs.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "pci.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_civoid pci_add_resource_offset(struct list_head *resources, struct resource *res, 2162306a36Sopenharmony_ci resource_size_t offset) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct resource_entry *entry; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci entry = resource_list_create_entry(res, 0); 2662306a36Sopenharmony_ci if (!entry) { 2762306a36Sopenharmony_ci pr_err("PCI: can't add host bridge window %pR\n", res); 2862306a36Sopenharmony_ci return; 2962306a36Sopenharmony_ci } 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci entry->offset = offset; 3262306a36Sopenharmony_ci resource_list_add_tail(entry, resources); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ciEXPORT_SYMBOL(pci_add_resource_offset); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_civoid pci_add_resource(struct list_head *resources, struct resource *res) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci pci_add_resource_offset(resources, res, 0); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ciEXPORT_SYMBOL(pci_add_resource); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_civoid pci_free_resource_list(struct list_head *resources) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci resource_list_free(resources); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ciEXPORT_SYMBOL(pci_free_resource_list); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_civoid pci_bus_add_resource(struct pci_bus *bus, struct resource *res, 4962306a36Sopenharmony_ci unsigned int flags) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci struct pci_bus_resource *bus_res; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci bus_res = kzalloc(sizeof(struct pci_bus_resource), GFP_KERNEL); 5462306a36Sopenharmony_ci if (!bus_res) { 5562306a36Sopenharmony_ci dev_err(&bus->dev, "can't add %pR resource\n", res); 5662306a36Sopenharmony_ci return; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci bus_res->res = res; 6062306a36Sopenharmony_ci bus_res->flags = flags; 6162306a36Sopenharmony_ci list_add_tail(&bus_res->list, &bus->resources); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistruct resource *pci_bus_resource_n(const struct pci_bus *bus, int n) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct pci_bus_resource *bus_res; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (n < PCI_BRIDGE_RESOURCE_NUM) 6962306a36Sopenharmony_ci return bus->resource[n]; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci n -= PCI_BRIDGE_RESOURCE_NUM; 7262306a36Sopenharmony_ci list_for_each_entry(bus_res, &bus->resources, list) { 7362306a36Sopenharmony_ci if (n-- == 0) 7462306a36Sopenharmony_ci return bus_res->res; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci return NULL; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_bus_resource_n); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_civoid pci_bus_remove_resource(struct pci_bus *bus, struct resource *res) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct pci_bus_resource *bus_res, *tmp; 8362306a36Sopenharmony_ci int i; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) { 8662306a36Sopenharmony_ci if (bus->resource[i] == res) { 8762306a36Sopenharmony_ci bus->resource[i] = NULL; 8862306a36Sopenharmony_ci return; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) { 9362306a36Sopenharmony_ci if (bus_res->res == res) { 9462306a36Sopenharmony_ci list_del(&bus_res->list); 9562306a36Sopenharmony_ci kfree(bus_res); 9662306a36Sopenharmony_ci return; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_civoid pci_bus_remove_resources(struct pci_bus *bus) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci int i; 10462306a36Sopenharmony_ci struct pci_bus_resource *bus_res, *tmp; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) 10762306a36Sopenharmony_ci bus->resource[i] = NULL; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) { 11062306a36Sopenharmony_ci list_del(&bus_res->list); 11162306a36Sopenharmony_ci kfree(bus_res); 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ciint devm_request_pci_bus_resources(struct device *dev, 11662306a36Sopenharmony_ci struct list_head *resources) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct resource_entry *win; 11962306a36Sopenharmony_ci struct resource *parent, *res; 12062306a36Sopenharmony_ci int err; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci resource_list_for_each_entry(win, resources) { 12362306a36Sopenharmony_ci res = win->res; 12462306a36Sopenharmony_ci switch (resource_type(res)) { 12562306a36Sopenharmony_ci case IORESOURCE_IO: 12662306a36Sopenharmony_ci parent = &ioport_resource; 12762306a36Sopenharmony_ci break; 12862306a36Sopenharmony_ci case IORESOURCE_MEM: 12962306a36Sopenharmony_ci parent = &iomem_resource; 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci default: 13262306a36Sopenharmony_ci continue; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci err = devm_request_resource(dev, parent, res); 13662306a36Sopenharmony_ci if (err) 13762306a36Sopenharmony_ci return err; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_request_pci_bus_resources); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic struct pci_bus_region pci_32_bit = {0, 0xffffffffULL}; 14562306a36Sopenharmony_ci#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 14662306a36Sopenharmony_cistatic struct pci_bus_region pci_64_bit = {0, 14762306a36Sopenharmony_ci (pci_bus_addr_t) 0xffffffffffffffffULL}; 14862306a36Sopenharmony_cistatic struct pci_bus_region pci_high = {(pci_bus_addr_t) 0x100000000ULL, 14962306a36Sopenharmony_ci (pci_bus_addr_t) 0xffffffffffffffffULL}; 15062306a36Sopenharmony_ci#endif 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci/* 15362306a36Sopenharmony_ci * @res contains CPU addresses. Clip it so the corresponding bus addresses 15462306a36Sopenharmony_ci * on @bus are entirely within @region. This is used to control the bus 15562306a36Sopenharmony_ci * addresses of resources we allocate, e.g., we may need a resource that 15662306a36Sopenharmony_ci * can be mapped by a 32-bit BAR. 15762306a36Sopenharmony_ci */ 15862306a36Sopenharmony_cistatic void pci_clip_resource_to_region(struct pci_bus *bus, 15962306a36Sopenharmony_ci struct resource *res, 16062306a36Sopenharmony_ci struct pci_bus_region *region) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct pci_bus_region r; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci pcibios_resource_to_bus(bus, &r, res); 16562306a36Sopenharmony_ci if (r.start < region->start) 16662306a36Sopenharmony_ci r.start = region->start; 16762306a36Sopenharmony_ci if (r.end > region->end) 16862306a36Sopenharmony_ci r.end = region->end; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (r.end < r.start) 17162306a36Sopenharmony_ci res->end = res->start - 1; 17262306a36Sopenharmony_ci else 17362306a36Sopenharmony_ci pcibios_bus_to_resource(bus, res, &r); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, 17762306a36Sopenharmony_ci resource_size_t size, resource_size_t align, 17862306a36Sopenharmony_ci resource_size_t min, unsigned long type_mask, 17962306a36Sopenharmony_ci resource_size_t (*alignf)(void *, 18062306a36Sopenharmony_ci const struct resource *, 18162306a36Sopenharmony_ci resource_size_t, 18262306a36Sopenharmony_ci resource_size_t), 18362306a36Sopenharmony_ci void *alignf_data, 18462306a36Sopenharmony_ci struct pci_bus_region *region) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct resource *r, avail; 18762306a36Sopenharmony_ci resource_size_t max; 18862306a36Sopenharmony_ci int ret; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci type_mask |= IORESOURCE_TYPE_BITS; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci pci_bus_for_each_resource(bus, r) { 19362306a36Sopenharmony_ci resource_size_t min_used = min; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (!r) 19662306a36Sopenharmony_ci continue; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* type_mask must match */ 19962306a36Sopenharmony_ci if ((res->flags ^ r->flags) & type_mask) 20062306a36Sopenharmony_ci continue; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* We cannot allocate a non-prefetching resource 20362306a36Sopenharmony_ci from a pre-fetching area */ 20462306a36Sopenharmony_ci if ((r->flags & IORESOURCE_PREFETCH) && 20562306a36Sopenharmony_ci !(res->flags & IORESOURCE_PREFETCH)) 20662306a36Sopenharmony_ci continue; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci avail = *r; 20962306a36Sopenharmony_ci pci_clip_resource_to_region(bus, &avail, region); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* 21262306a36Sopenharmony_ci * "min" is typically PCIBIOS_MIN_IO or PCIBIOS_MIN_MEM to 21362306a36Sopenharmony_ci * protect badly documented motherboard resources, but if 21462306a36Sopenharmony_ci * this is an already-configured bridge window, its start 21562306a36Sopenharmony_ci * overrides "min". 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci if (avail.start) 21862306a36Sopenharmony_ci min_used = avail.start; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci max = avail.end; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* Don't bother if available space isn't large enough */ 22362306a36Sopenharmony_ci if (size > max - min_used + 1) 22462306a36Sopenharmony_ci continue; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* Ok, try it out.. */ 22762306a36Sopenharmony_ci ret = allocate_resource(r, res, size, min_used, max, 22862306a36Sopenharmony_ci align, alignf, alignf_data); 22962306a36Sopenharmony_ci if (ret == 0) 23062306a36Sopenharmony_ci return 0; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci return -ENOMEM; 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci/** 23662306a36Sopenharmony_ci * pci_bus_alloc_resource - allocate a resource from a parent bus 23762306a36Sopenharmony_ci * @bus: PCI bus 23862306a36Sopenharmony_ci * @res: resource to allocate 23962306a36Sopenharmony_ci * @size: size of resource to allocate 24062306a36Sopenharmony_ci * @align: alignment of resource to allocate 24162306a36Sopenharmony_ci * @min: minimum /proc/iomem address to allocate 24262306a36Sopenharmony_ci * @type_mask: IORESOURCE_* type flags 24362306a36Sopenharmony_ci * @alignf: resource alignment function 24462306a36Sopenharmony_ci * @alignf_data: data argument for resource alignment function 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci * Given the PCI bus a device resides on, the size, minimum address, 24762306a36Sopenharmony_ci * alignment and type, try to find an acceptable resource allocation 24862306a36Sopenharmony_ci * for a specific device resource. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_ciint pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, 25162306a36Sopenharmony_ci resource_size_t size, resource_size_t align, 25262306a36Sopenharmony_ci resource_size_t min, unsigned long type_mask, 25362306a36Sopenharmony_ci resource_size_t (*alignf)(void *, 25462306a36Sopenharmony_ci const struct resource *, 25562306a36Sopenharmony_ci resource_size_t, 25662306a36Sopenharmony_ci resource_size_t), 25762306a36Sopenharmony_ci void *alignf_data) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 26062306a36Sopenharmony_ci int rc; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (res->flags & IORESOURCE_MEM_64) { 26362306a36Sopenharmony_ci rc = pci_bus_alloc_from_region(bus, res, size, align, min, 26462306a36Sopenharmony_ci type_mask, alignf, alignf_data, 26562306a36Sopenharmony_ci &pci_high); 26662306a36Sopenharmony_ci if (rc == 0) 26762306a36Sopenharmony_ci return 0; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return pci_bus_alloc_from_region(bus, res, size, align, min, 27062306a36Sopenharmony_ci type_mask, alignf, alignf_data, 27162306a36Sopenharmony_ci &pci_64_bit); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci#endif 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return pci_bus_alloc_from_region(bus, res, size, align, min, 27662306a36Sopenharmony_ci type_mask, alignf, alignf_data, 27762306a36Sopenharmony_ci &pci_32_bit); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ciEXPORT_SYMBOL(pci_bus_alloc_resource); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci/* 28262306a36Sopenharmony_ci * The @idx resource of @dev should be a PCI-PCI bridge window. If this 28362306a36Sopenharmony_ci * resource fits inside a window of an upstream bridge, do nothing. If it 28462306a36Sopenharmony_ci * overlaps an upstream window but extends outside it, clip the resource so 28562306a36Sopenharmony_ci * it fits completely inside. 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_cibool pci_bus_clip_resource(struct pci_dev *dev, int idx) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct pci_bus *bus = dev->bus; 29062306a36Sopenharmony_ci struct resource *res = &dev->resource[idx]; 29162306a36Sopenharmony_ci struct resource orig_res = *res; 29262306a36Sopenharmony_ci struct resource *r; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci pci_bus_for_each_resource(bus, r) { 29562306a36Sopenharmony_ci resource_size_t start, end; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (!r) 29862306a36Sopenharmony_ci continue; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci if (resource_type(res) != resource_type(r)) 30162306a36Sopenharmony_ci continue; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci start = max(r->start, res->start); 30462306a36Sopenharmony_ci end = min(r->end, res->end); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (start > end) 30762306a36Sopenharmony_ci continue; /* no overlap */ 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci if (res->start == start && res->end == end) 31062306a36Sopenharmony_ci return false; /* no change */ 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci res->start = start; 31362306a36Sopenharmony_ci res->end = end; 31462306a36Sopenharmony_ci res->flags &= ~IORESOURCE_UNSET; 31562306a36Sopenharmony_ci orig_res.flags &= ~IORESOURCE_UNSET; 31662306a36Sopenharmony_ci pci_info(dev, "%pR clipped to %pR\n", &orig_res, res); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci return true; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return false; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_civoid __weak pcibios_resource_survey_bus(struct pci_bus *bus) { } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_civoid __weak pcibios_bus_add_device(struct pci_dev *pdev) { } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci/** 32962306a36Sopenharmony_ci * pci_bus_add_device - start driver for a single device 33062306a36Sopenharmony_ci * @dev: device to add 33162306a36Sopenharmony_ci * 33262306a36Sopenharmony_ci * This adds add sysfs entries and start device drivers 33362306a36Sopenharmony_ci */ 33462306a36Sopenharmony_civoid pci_bus_add_device(struct pci_dev *dev) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct device_node *dn = dev->dev.of_node; 33762306a36Sopenharmony_ci int retval; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* 34062306a36Sopenharmony_ci * Can not put in pci_device_add yet because resources 34162306a36Sopenharmony_ci * are not assigned yet for some devices. 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_ci pcibios_bus_add_device(dev); 34462306a36Sopenharmony_ci pci_fixup_device(pci_fixup_final, dev); 34562306a36Sopenharmony_ci if (pci_is_bridge(dev)) 34662306a36Sopenharmony_ci of_pci_make_dev_node(dev); 34762306a36Sopenharmony_ci pci_create_sysfs_dev_files(dev); 34862306a36Sopenharmony_ci pci_proc_attach_device(dev); 34962306a36Sopenharmony_ci pci_bridge_d3_update(dev); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci dev->match_driver = !dn || of_device_is_available(dn); 35262306a36Sopenharmony_ci retval = device_attach(&dev->dev); 35362306a36Sopenharmony_ci if (retval < 0 && retval != -EPROBE_DEFER) 35462306a36Sopenharmony_ci pci_warn(dev, "device attach failed (%d)\n", retval); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci pci_dev_assign_added(dev, true); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_bus_add_device); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci/** 36162306a36Sopenharmony_ci * pci_bus_add_devices - start driver for PCI devices 36262306a36Sopenharmony_ci * @bus: bus to check for new devices 36362306a36Sopenharmony_ci * 36462306a36Sopenharmony_ci * Start driver for PCI devices and add some sysfs entries. 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_civoid pci_bus_add_devices(const struct pci_bus *bus) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct pci_dev *dev; 36962306a36Sopenharmony_ci struct pci_bus *child; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci list_for_each_entry(dev, &bus->devices, bus_list) { 37262306a36Sopenharmony_ci /* Skip already-added devices */ 37362306a36Sopenharmony_ci if (pci_dev_is_added(dev)) 37462306a36Sopenharmony_ci continue; 37562306a36Sopenharmony_ci pci_bus_add_device(dev); 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci list_for_each_entry(dev, &bus->devices, bus_list) { 37962306a36Sopenharmony_ci /* Skip if device attach failed */ 38062306a36Sopenharmony_ci if (!pci_dev_is_added(dev)) 38162306a36Sopenharmony_ci continue; 38262306a36Sopenharmony_ci child = dev->subordinate; 38362306a36Sopenharmony_ci if (child) 38462306a36Sopenharmony_ci pci_bus_add_devices(child); 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ciEXPORT_SYMBOL(pci_bus_add_devices); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci/** pci_walk_bus - walk devices on/under bus, calling callback. 39062306a36Sopenharmony_ci * @top bus whose devices should be walked 39162306a36Sopenharmony_ci * @cb callback to be called for each device found 39262306a36Sopenharmony_ci * @userdata arbitrary pointer to be passed to callback. 39362306a36Sopenharmony_ci * 39462306a36Sopenharmony_ci * Walk the given bus, including any bridged devices 39562306a36Sopenharmony_ci * on buses under this bus. Call the provided callback 39662306a36Sopenharmony_ci * on each device found. 39762306a36Sopenharmony_ci * 39862306a36Sopenharmony_ci * We check the return of @cb each time. If it returns anything 39962306a36Sopenharmony_ci * other than 0, we break out. 40062306a36Sopenharmony_ci * 40162306a36Sopenharmony_ci */ 40262306a36Sopenharmony_civoid pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), 40362306a36Sopenharmony_ci void *userdata) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct pci_dev *dev; 40662306a36Sopenharmony_ci struct pci_bus *bus; 40762306a36Sopenharmony_ci struct list_head *next; 40862306a36Sopenharmony_ci int retval; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci bus = top; 41162306a36Sopenharmony_ci down_read(&pci_bus_sem); 41262306a36Sopenharmony_ci next = top->devices.next; 41362306a36Sopenharmony_ci for (;;) { 41462306a36Sopenharmony_ci if (next == &bus->devices) { 41562306a36Sopenharmony_ci /* end of this bus, go up or finish */ 41662306a36Sopenharmony_ci if (bus == top) 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci next = bus->self->bus_list.next; 41962306a36Sopenharmony_ci bus = bus->self->bus; 42062306a36Sopenharmony_ci continue; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci dev = list_entry(next, struct pci_dev, bus_list); 42362306a36Sopenharmony_ci if (dev->subordinate) { 42462306a36Sopenharmony_ci /* this is a pci-pci bridge, do its devices next */ 42562306a36Sopenharmony_ci next = dev->subordinate->devices.next; 42662306a36Sopenharmony_ci bus = dev->subordinate; 42762306a36Sopenharmony_ci } else 42862306a36Sopenharmony_ci next = dev->bus_list.next; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci retval = cb(dev, userdata); 43162306a36Sopenharmony_ci if (retval) 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci up_read(&pci_bus_sem); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_walk_bus); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistruct pci_bus *pci_bus_get(struct pci_bus *bus) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci if (bus) 44162306a36Sopenharmony_ci get_device(&bus->dev); 44262306a36Sopenharmony_ci return bus; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_civoid pci_bus_put(struct pci_bus *bus) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci if (bus) 44862306a36Sopenharmony_ci put_device(&bus->dev); 44962306a36Sopenharmony_ci} 450