18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * From setup-res.c, by: 48c2ecf20Sopenharmony_ci * Dave Rusling (david.rusling@reo.mts.dec.com) 58c2ecf20Sopenharmony_ci * David Mosberger (davidm@cs.arizona.edu) 68c2ecf20Sopenharmony_ci * David Miller (davem@redhat.com) 78c2ecf20Sopenharmony_ci * Ivan Kokshaysky (ink@jurassic.park.msu.ru) 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/pci.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/ioport.h> 148c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "pci.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_civoid pci_add_resource_offset(struct list_head *resources, struct resource *res, 208c2ecf20Sopenharmony_ci resource_size_t offset) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct resource_entry *entry; 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci entry = resource_list_create_entry(res, 0); 258c2ecf20Sopenharmony_ci if (!entry) { 268c2ecf20Sopenharmony_ci pr_err("PCI: can't add host bridge window %pR\n", res); 278c2ecf20Sopenharmony_ci return; 288c2ecf20Sopenharmony_ci } 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci entry->offset = offset; 318c2ecf20Sopenharmony_ci resource_list_add_tail(entry, resources); 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_add_resource_offset); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_civoid pci_add_resource(struct list_head *resources, struct resource *res) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci pci_add_resource_offset(resources, res, 0); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_add_resource); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_civoid pci_free_resource_list(struct list_head *resources) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci resource_list_free(resources); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_free_resource_list); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_civoid pci_bus_add_resource(struct pci_bus *bus, struct resource *res, 488c2ecf20Sopenharmony_ci unsigned int flags) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct pci_bus_resource *bus_res; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci bus_res = kzalloc(sizeof(struct pci_bus_resource), GFP_KERNEL); 538c2ecf20Sopenharmony_ci if (!bus_res) { 548c2ecf20Sopenharmony_ci dev_err(&bus->dev, "can't add %pR resource\n", res); 558c2ecf20Sopenharmony_ci return; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci bus_res->res = res; 598c2ecf20Sopenharmony_ci bus_res->flags = flags; 608c2ecf20Sopenharmony_ci list_add_tail(&bus_res->list, &bus->resources); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistruct resource *pci_bus_resource_n(const struct pci_bus *bus, int n) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct pci_bus_resource *bus_res; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (n < PCI_BRIDGE_RESOURCE_NUM) 688c2ecf20Sopenharmony_ci return bus->resource[n]; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci n -= PCI_BRIDGE_RESOURCE_NUM; 718c2ecf20Sopenharmony_ci list_for_each_entry(bus_res, &bus->resources, list) { 728c2ecf20Sopenharmony_ci if (n-- == 0) 738c2ecf20Sopenharmony_ci return bus_res->res; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci return NULL; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_bus_resource_n); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_civoid pci_bus_remove_resources(struct pci_bus *bus) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci int i; 828c2ecf20Sopenharmony_ci struct pci_bus_resource *bus_res, *tmp; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) 858c2ecf20Sopenharmony_ci bus->resource[i] = NULL; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) { 888c2ecf20Sopenharmony_ci list_del(&bus_res->list); 898c2ecf20Sopenharmony_ci kfree(bus_res); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ciint devm_request_pci_bus_resources(struct device *dev, 948c2ecf20Sopenharmony_ci struct list_head *resources) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct resource_entry *win; 978c2ecf20Sopenharmony_ci struct resource *parent, *res; 988c2ecf20Sopenharmony_ci int err; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci resource_list_for_each_entry(win, resources) { 1018c2ecf20Sopenharmony_ci res = win->res; 1028c2ecf20Sopenharmony_ci switch (resource_type(res)) { 1038c2ecf20Sopenharmony_ci case IORESOURCE_IO: 1048c2ecf20Sopenharmony_ci parent = &ioport_resource; 1058c2ecf20Sopenharmony_ci break; 1068c2ecf20Sopenharmony_ci case IORESOURCE_MEM: 1078c2ecf20Sopenharmony_ci parent = &iomem_resource; 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci default: 1108c2ecf20Sopenharmony_ci continue; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci err = devm_request_resource(dev, parent, res); 1148c2ecf20Sopenharmony_ci if (err) 1158c2ecf20Sopenharmony_ci return err; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return 0; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_request_pci_bus_resources); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic struct pci_bus_region pci_32_bit = {0, 0xffffffffULL}; 1238c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 1248c2ecf20Sopenharmony_cistatic struct pci_bus_region pci_64_bit = {0, 1258c2ecf20Sopenharmony_ci (pci_bus_addr_t) 0xffffffffffffffffULL}; 1268c2ecf20Sopenharmony_cistatic struct pci_bus_region pci_high = {(pci_bus_addr_t) 0x100000000ULL, 1278c2ecf20Sopenharmony_ci (pci_bus_addr_t) 0xffffffffffffffffULL}; 1288c2ecf20Sopenharmony_ci#endif 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* 1318c2ecf20Sopenharmony_ci * @res contains CPU addresses. Clip it so the corresponding bus addresses 1328c2ecf20Sopenharmony_ci * on @bus are entirely within @region. This is used to control the bus 1338c2ecf20Sopenharmony_ci * addresses of resources we allocate, e.g., we may need a resource that 1348c2ecf20Sopenharmony_ci * can be mapped by a 32-bit BAR. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_cistatic void pci_clip_resource_to_region(struct pci_bus *bus, 1378c2ecf20Sopenharmony_ci struct resource *res, 1388c2ecf20Sopenharmony_ci struct pci_bus_region *region) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct pci_bus_region r; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci pcibios_resource_to_bus(bus, &r, res); 1438c2ecf20Sopenharmony_ci if (r.start < region->start) 1448c2ecf20Sopenharmony_ci r.start = region->start; 1458c2ecf20Sopenharmony_ci if (r.end > region->end) 1468c2ecf20Sopenharmony_ci r.end = region->end; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (r.end < r.start) 1498c2ecf20Sopenharmony_ci res->end = res->start - 1; 1508c2ecf20Sopenharmony_ci else 1518c2ecf20Sopenharmony_ci pcibios_bus_to_resource(bus, res, &r); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, 1558c2ecf20Sopenharmony_ci resource_size_t size, resource_size_t align, 1568c2ecf20Sopenharmony_ci resource_size_t min, unsigned long type_mask, 1578c2ecf20Sopenharmony_ci resource_size_t (*alignf)(void *, 1588c2ecf20Sopenharmony_ci const struct resource *, 1598c2ecf20Sopenharmony_ci resource_size_t, 1608c2ecf20Sopenharmony_ci resource_size_t), 1618c2ecf20Sopenharmony_ci void *alignf_data, 1628c2ecf20Sopenharmony_ci struct pci_bus_region *region) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci int i, ret; 1658c2ecf20Sopenharmony_ci struct resource *r, avail; 1668c2ecf20Sopenharmony_ci resource_size_t max; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci type_mask |= IORESOURCE_TYPE_BITS; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci pci_bus_for_each_resource(bus, r, i) { 1718c2ecf20Sopenharmony_ci resource_size_t min_used = min; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (!r) 1748c2ecf20Sopenharmony_ci continue; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* type_mask must match */ 1778c2ecf20Sopenharmony_ci if ((res->flags ^ r->flags) & type_mask) 1788c2ecf20Sopenharmony_ci continue; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* We cannot allocate a non-prefetching resource 1818c2ecf20Sopenharmony_ci from a pre-fetching area */ 1828c2ecf20Sopenharmony_ci if ((r->flags & IORESOURCE_PREFETCH) && 1838c2ecf20Sopenharmony_ci !(res->flags & IORESOURCE_PREFETCH)) 1848c2ecf20Sopenharmony_ci continue; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci avail = *r; 1878c2ecf20Sopenharmony_ci pci_clip_resource_to_region(bus, &avail, region); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * "min" is typically PCIBIOS_MIN_IO or PCIBIOS_MIN_MEM to 1918c2ecf20Sopenharmony_ci * protect badly documented motherboard resources, but if 1928c2ecf20Sopenharmony_ci * this is an already-configured bridge window, its start 1938c2ecf20Sopenharmony_ci * overrides "min". 1948c2ecf20Sopenharmony_ci */ 1958c2ecf20Sopenharmony_ci if (avail.start) 1968c2ecf20Sopenharmony_ci min_used = avail.start; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci max = avail.end; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* Ok, try it out.. */ 2018c2ecf20Sopenharmony_ci ret = allocate_resource(r, res, size, min_used, max, 2028c2ecf20Sopenharmony_ci align, alignf, alignf_data); 2038c2ecf20Sopenharmony_ci if (ret == 0) 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci return -ENOMEM; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/** 2108c2ecf20Sopenharmony_ci * pci_bus_alloc_resource - allocate a resource from a parent bus 2118c2ecf20Sopenharmony_ci * @bus: PCI bus 2128c2ecf20Sopenharmony_ci * @res: resource to allocate 2138c2ecf20Sopenharmony_ci * @size: size of resource to allocate 2148c2ecf20Sopenharmony_ci * @align: alignment of resource to allocate 2158c2ecf20Sopenharmony_ci * @min: minimum /proc/iomem address to allocate 2168c2ecf20Sopenharmony_ci * @type_mask: IORESOURCE_* type flags 2178c2ecf20Sopenharmony_ci * @alignf: resource alignment function 2188c2ecf20Sopenharmony_ci * @alignf_data: data argument for resource alignment function 2198c2ecf20Sopenharmony_ci * 2208c2ecf20Sopenharmony_ci * Given the PCI bus a device resides on, the size, minimum address, 2218c2ecf20Sopenharmony_ci * alignment and type, try to find an acceptable resource allocation 2228c2ecf20Sopenharmony_ci * for a specific device resource. 2238c2ecf20Sopenharmony_ci */ 2248c2ecf20Sopenharmony_ciint pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, 2258c2ecf20Sopenharmony_ci resource_size_t size, resource_size_t align, 2268c2ecf20Sopenharmony_ci resource_size_t min, unsigned long type_mask, 2278c2ecf20Sopenharmony_ci resource_size_t (*alignf)(void *, 2288c2ecf20Sopenharmony_ci const struct resource *, 2298c2ecf20Sopenharmony_ci resource_size_t, 2308c2ecf20Sopenharmony_ci resource_size_t), 2318c2ecf20Sopenharmony_ci void *alignf_data) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 2348c2ecf20Sopenharmony_ci int rc; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_MEM_64) { 2378c2ecf20Sopenharmony_ci rc = pci_bus_alloc_from_region(bus, res, size, align, min, 2388c2ecf20Sopenharmony_ci type_mask, alignf, alignf_data, 2398c2ecf20Sopenharmony_ci &pci_high); 2408c2ecf20Sopenharmony_ci if (rc == 0) 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci return pci_bus_alloc_from_region(bus, res, size, align, min, 2448c2ecf20Sopenharmony_ci type_mask, alignf, alignf_data, 2458c2ecf20Sopenharmony_ci &pci_64_bit); 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci#endif 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return pci_bus_alloc_from_region(bus, res, size, align, min, 2508c2ecf20Sopenharmony_ci type_mask, alignf, alignf_data, 2518c2ecf20Sopenharmony_ci &pci_32_bit); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_bus_alloc_resource); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci/* 2568c2ecf20Sopenharmony_ci * The @idx resource of @dev should be a PCI-PCI bridge window. If this 2578c2ecf20Sopenharmony_ci * resource fits inside a window of an upstream bridge, do nothing. If it 2588c2ecf20Sopenharmony_ci * overlaps an upstream window but extends outside it, clip the resource so 2598c2ecf20Sopenharmony_ci * it fits completely inside. 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_cibool pci_bus_clip_resource(struct pci_dev *dev, int idx) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct pci_bus *bus = dev->bus; 2648c2ecf20Sopenharmony_ci struct resource *res = &dev->resource[idx]; 2658c2ecf20Sopenharmony_ci struct resource orig_res = *res; 2668c2ecf20Sopenharmony_ci struct resource *r; 2678c2ecf20Sopenharmony_ci int i; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci pci_bus_for_each_resource(bus, r, i) { 2708c2ecf20Sopenharmony_ci resource_size_t start, end; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (!r) 2738c2ecf20Sopenharmony_ci continue; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (resource_type(res) != resource_type(r)) 2768c2ecf20Sopenharmony_ci continue; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci start = max(r->start, res->start); 2798c2ecf20Sopenharmony_ci end = min(r->end, res->end); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (start > end) 2828c2ecf20Sopenharmony_ci continue; /* no overlap */ 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (res->start == start && res->end == end) 2858c2ecf20Sopenharmony_ci return false; /* no change */ 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci res->start = start; 2888c2ecf20Sopenharmony_ci res->end = end; 2898c2ecf20Sopenharmony_ci res->flags &= ~IORESOURCE_UNSET; 2908c2ecf20Sopenharmony_ci orig_res.flags &= ~IORESOURCE_UNSET; 2918c2ecf20Sopenharmony_ci pci_info(dev, "%pR clipped to %pR\n", &orig_res, res); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return true; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return false; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_civoid __weak pcibios_resource_survey_bus(struct pci_bus *bus) { } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_civoid __weak pcibios_bus_add_device(struct pci_dev *pdev) { } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci/** 3048c2ecf20Sopenharmony_ci * pci_bus_add_device - start driver for a single device 3058c2ecf20Sopenharmony_ci * @dev: device to add 3068c2ecf20Sopenharmony_ci * 3078c2ecf20Sopenharmony_ci * This adds add sysfs entries and start device drivers 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_civoid pci_bus_add_device(struct pci_dev *dev) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci int retval; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* 3148c2ecf20Sopenharmony_ci * Can not put in pci_device_add yet because resources 3158c2ecf20Sopenharmony_ci * are not assigned yet for some devices. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ci pcibios_bus_add_device(dev); 3188c2ecf20Sopenharmony_ci pci_fixup_device(pci_fixup_final, dev); 3198c2ecf20Sopenharmony_ci pci_create_sysfs_dev_files(dev); 3208c2ecf20Sopenharmony_ci pci_proc_attach_device(dev); 3218c2ecf20Sopenharmony_ci pci_bridge_d3_update(dev); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci dev->match_driver = true; 3248c2ecf20Sopenharmony_ci retval = device_attach(&dev->dev); 3258c2ecf20Sopenharmony_ci if (retval < 0 && retval != -EPROBE_DEFER) 3268c2ecf20Sopenharmony_ci pci_warn(dev, "device attach failed (%d)\n", retval); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci pci_dev_assign_added(dev, true); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_bus_add_device); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/** 3338c2ecf20Sopenharmony_ci * pci_bus_add_devices - start driver for PCI devices 3348c2ecf20Sopenharmony_ci * @bus: bus to check for new devices 3358c2ecf20Sopenharmony_ci * 3368c2ecf20Sopenharmony_ci * Start driver for PCI devices and add some sysfs entries. 3378c2ecf20Sopenharmony_ci */ 3388c2ecf20Sopenharmony_civoid pci_bus_add_devices(const struct pci_bus *bus) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct pci_dev *dev; 3418c2ecf20Sopenharmony_ci struct pci_bus *child; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci list_for_each_entry(dev, &bus->devices, bus_list) { 3448c2ecf20Sopenharmony_ci /* Skip already-added devices */ 3458c2ecf20Sopenharmony_ci if (pci_dev_is_added(dev)) 3468c2ecf20Sopenharmony_ci continue; 3478c2ecf20Sopenharmony_ci pci_bus_add_device(dev); 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci list_for_each_entry(dev, &bus->devices, bus_list) { 3518c2ecf20Sopenharmony_ci /* Skip if device attach failed */ 3528c2ecf20Sopenharmony_ci if (!pci_dev_is_added(dev)) 3538c2ecf20Sopenharmony_ci continue; 3548c2ecf20Sopenharmony_ci child = dev->subordinate; 3558c2ecf20Sopenharmony_ci if (child) 3568c2ecf20Sopenharmony_ci pci_bus_add_devices(child); 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci} 3598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_bus_add_devices); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci/** pci_walk_bus - walk devices on/under bus, calling callback. 3628c2ecf20Sopenharmony_ci * @top bus whose devices should be walked 3638c2ecf20Sopenharmony_ci * @cb callback to be called for each device found 3648c2ecf20Sopenharmony_ci * @userdata arbitrary pointer to be passed to callback. 3658c2ecf20Sopenharmony_ci * 3668c2ecf20Sopenharmony_ci * Walk the given bus, including any bridged devices 3678c2ecf20Sopenharmony_ci * on buses under this bus. Call the provided callback 3688c2ecf20Sopenharmony_ci * on each device found. 3698c2ecf20Sopenharmony_ci * 3708c2ecf20Sopenharmony_ci * We check the return of @cb each time. If it returns anything 3718c2ecf20Sopenharmony_ci * other than 0, we break out. 3728c2ecf20Sopenharmony_ci * 3738c2ecf20Sopenharmony_ci */ 3748c2ecf20Sopenharmony_civoid pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), 3758c2ecf20Sopenharmony_ci void *userdata) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct pci_dev *dev; 3788c2ecf20Sopenharmony_ci struct pci_bus *bus; 3798c2ecf20Sopenharmony_ci struct list_head *next; 3808c2ecf20Sopenharmony_ci int retval; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci bus = top; 3838c2ecf20Sopenharmony_ci down_read(&pci_bus_sem); 3848c2ecf20Sopenharmony_ci next = top->devices.next; 3858c2ecf20Sopenharmony_ci for (;;) { 3868c2ecf20Sopenharmony_ci if (next == &bus->devices) { 3878c2ecf20Sopenharmony_ci /* end of this bus, go up or finish */ 3888c2ecf20Sopenharmony_ci if (bus == top) 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci next = bus->self->bus_list.next; 3918c2ecf20Sopenharmony_ci bus = bus->self->bus; 3928c2ecf20Sopenharmony_ci continue; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci dev = list_entry(next, struct pci_dev, bus_list); 3958c2ecf20Sopenharmony_ci if (dev->subordinate) { 3968c2ecf20Sopenharmony_ci /* this is a pci-pci bridge, do its devices next */ 3978c2ecf20Sopenharmony_ci next = dev->subordinate->devices.next; 3988c2ecf20Sopenharmony_ci bus = dev->subordinate; 3998c2ecf20Sopenharmony_ci } else 4008c2ecf20Sopenharmony_ci next = dev->bus_list.next; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci retval = cb(dev, userdata); 4038c2ecf20Sopenharmony_ci if (retval) 4048c2ecf20Sopenharmony_ci break; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci up_read(&pci_bus_sem); 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(pci_walk_bus); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistruct pci_bus *pci_bus_get(struct pci_bus *bus) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci if (bus) 4138c2ecf20Sopenharmony_ci get_device(&bus->dev); 4148c2ecf20Sopenharmony_ci return bus; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_bus_get); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_civoid pci_bus_put(struct pci_bus *bus) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci if (bus) 4218c2ecf20Sopenharmony_ci put_device(&bus->dev); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pci_bus_put); 424