17328c8f4SBjorn Helgaas // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * From setup-res.c, by:
41da177e4SLinus Torvalds * Dave Rusling (david.rusling@reo.mts.dec.com)
51da177e4SLinus Torvalds * David Mosberger (davidm@cs.arizona.edu)
61da177e4SLinus Torvalds * David Miller (davem@redhat.com)
71da177e4SLinus Torvalds * Ivan Kokshaysky (ink@jurassic.park.msu.ru)
81da177e4SLinus Torvalds */
91da177e4SLinus Torvalds #include <linux/module.h>
101da177e4SLinus Torvalds #include <linux/kernel.h>
111da177e4SLinus Torvalds #include <linux/pci.h>
121da177e4SLinus Torvalds #include <linux/errno.h>
131da177e4SLinus Torvalds #include <linux/ioport.h>
141a8c251cSVladimir Oltean #include <linux/of.h>
151da177e4SLinus Torvalds #include <linux/proc_fs.h>
165a0e3ad6STejun Heo #include <linux/slab.h>
171da177e4SLinus Torvalds
181da177e4SLinus Torvalds #include "pci.h"
191da177e4SLinus Torvalds
pci_add_resource_offset(struct list_head * resources,struct resource * res,resource_size_t offset)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) {
2725da8dbaSMohan Kumar pr_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
pci_add_resource(struct list_head * resources,struct resource * res)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
pci_free_resource_list(struct list_head * resources)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
pci_bus_add_resource(struct pci_bus * bus,struct resource * res,unsigned int flags)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
pci_bus_resource_n(const struct pci_bus * bus,int n)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
pci_bus_remove_resource(struct pci_bus * bus,struct resource * res)80ab909509SNiklas Schnelle void pci_bus_remove_resource(struct pci_bus *bus, struct resource *res)
81ab909509SNiklas Schnelle {
82ab909509SNiklas Schnelle struct pci_bus_resource *bus_res, *tmp;
83ab909509SNiklas Schnelle int i;
84ab909509SNiklas Schnelle
85ab909509SNiklas Schnelle for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) {
86ab909509SNiklas Schnelle if (bus->resource[i] == res) {
87ab909509SNiklas Schnelle bus->resource[i] = NULL;
88ab909509SNiklas Schnelle return;
89ab909509SNiklas Schnelle }
90ab909509SNiklas Schnelle }
91ab909509SNiklas Schnelle
92ab909509SNiklas Schnelle list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) {
93ab909509SNiklas Schnelle if (bus_res->res == res) {
94ab909509SNiklas Schnelle list_del(&bus_res->list);
95ab909509SNiklas Schnelle kfree(bus_res);
96ab909509SNiklas Schnelle return;
97ab909509SNiklas Schnelle }
98ab909509SNiklas Schnelle }
99ab909509SNiklas Schnelle }
100ab909509SNiklas Schnelle
pci_bus_remove_resources(struct pci_bus * bus)1012fe2abf8SBjorn Helgaas void pci_bus_remove_resources(struct pci_bus *bus)
1022fe2abf8SBjorn Helgaas {
1032fe2abf8SBjorn Helgaas int i;
104817a2685SYinghai Lu struct pci_bus_resource *bus_res, *tmp;
1052fe2abf8SBjorn Helgaas
1062fe2abf8SBjorn Helgaas for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++)
1077736a05aSStephen Hemminger bus->resource[i] = NULL;
1082fe2abf8SBjorn Helgaas
109817a2685SYinghai Lu list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) {
110817a2685SYinghai Lu list_del(&bus_res->list);
111817a2685SYinghai Lu kfree(bus_res);
112817a2685SYinghai Lu }
1132fe2abf8SBjorn Helgaas }
1142fe2abf8SBjorn Helgaas
devm_request_pci_bus_resources(struct device * dev,struct list_head * resources)115950334bcSBjorn Helgaas int devm_request_pci_bus_resources(struct device *dev,
116950334bcSBjorn Helgaas struct list_head *resources)
117950334bcSBjorn Helgaas {
118950334bcSBjorn Helgaas struct resource_entry *win;
119950334bcSBjorn Helgaas struct resource *parent, *res;
120950334bcSBjorn Helgaas int err;
121950334bcSBjorn Helgaas
122950334bcSBjorn Helgaas resource_list_for_each_entry(win, resources) {
123950334bcSBjorn Helgaas res = win->res;
124950334bcSBjorn Helgaas switch (resource_type(res)) {
125950334bcSBjorn Helgaas case IORESOURCE_IO:
126950334bcSBjorn Helgaas parent = &ioport_resource;
127950334bcSBjorn Helgaas break;
128950334bcSBjorn Helgaas case IORESOURCE_MEM:
129950334bcSBjorn Helgaas parent = &iomem_resource;
130950334bcSBjorn Helgaas break;
131950334bcSBjorn Helgaas default:
132950334bcSBjorn Helgaas continue;
133950334bcSBjorn Helgaas }
134950334bcSBjorn Helgaas
135950334bcSBjorn Helgaas err = devm_request_resource(dev, parent, res);
136950334bcSBjorn Helgaas if (err)
137950334bcSBjorn Helgaas return err;
138950334bcSBjorn Helgaas }
139950334bcSBjorn Helgaas
140950334bcSBjorn Helgaas return 0;
141950334bcSBjorn Helgaas }
142950334bcSBjorn Helgaas EXPORT_SYMBOL_GPL(devm_request_pci_bus_resources);
143950334bcSBjorn Helgaas
144f75b99d5SYinghai Lu static struct pci_bus_region pci_32_bit = {0, 0xffffffffULL};
1458e639079SChristoph Hellwig #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
146f75b99d5SYinghai Lu static struct pci_bus_region pci_64_bit = {0,
1473a9ad0b4SYinghai Lu (pci_bus_addr_t) 0xffffffffffffffffULL};
1483a9ad0b4SYinghai Lu static struct pci_bus_region pci_high = {(pci_bus_addr_t) 0x100000000ULL,
1493a9ad0b4SYinghai Lu (pci_bus_addr_t) 0xffffffffffffffffULL};
150f75b99d5SYinghai Lu #endif
151f75b99d5SYinghai Lu
152f75b99d5SYinghai Lu /*
153f75b99d5SYinghai Lu * @res contains CPU addresses. Clip it so the corresponding bus addresses
154f75b99d5SYinghai Lu * on @bus are entirely within @region. This is used to control the bus
155f75b99d5SYinghai Lu * addresses of resources we allocate, e.g., we may need a resource that
156f75b99d5SYinghai Lu * can be mapped by a 32-bit BAR.
1571da177e4SLinus Torvalds */
pci_clip_resource_to_region(struct pci_bus * bus,struct resource * res,struct pci_bus_region * region)158f75b99d5SYinghai Lu static void pci_clip_resource_to_region(struct pci_bus *bus,
159f75b99d5SYinghai Lu struct resource *res,
160f75b99d5SYinghai Lu struct pci_bus_region *region)
161f75b99d5SYinghai Lu {
162f75b99d5SYinghai Lu struct pci_bus_region r;
163f75b99d5SYinghai Lu
164f75b99d5SYinghai Lu pcibios_resource_to_bus(bus, &r, res);
165f75b99d5SYinghai Lu if (r.start < region->start)
166f75b99d5SYinghai Lu r.start = region->start;
167f75b99d5SYinghai Lu if (r.end > region->end)
168f75b99d5SYinghai Lu r.end = region->end;
169f75b99d5SYinghai Lu
170f75b99d5SYinghai Lu if (r.end < r.start)
171f75b99d5SYinghai Lu res->end = res->start - 1;
172f75b99d5SYinghai Lu else
173f75b99d5SYinghai Lu pcibios_bus_to_resource(bus, res, &r);
174f75b99d5SYinghai Lu }
175f75b99d5SYinghai Lu
pci_bus_alloc_from_region(struct pci_bus * bus,struct resource * res,resource_size_t size,resource_size_t align,resource_size_t min,unsigned long type_mask,resource_size_t (* alignf)(void *,const struct resource *,resource_size_t,resource_size_t),void * alignf_data,struct pci_bus_region * region)176f75b99d5SYinghai Lu static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
177e31dd6e4SGreg Kroah-Hartman resource_size_t size, resource_size_t align,
178664c2848SBjorn Helgaas resource_size_t min, unsigned long type_mask,
179b26b2d49SDominik Brodowski resource_size_t (*alignf)(void *,
1803b7a17fcSDominik Brodowski const struct resource *,
181b26b2d49SDominik Brodowski resource_size_t,
182e31dd6e4SGreg Kroah-Hartman resource_size_t),
183f75b99d5SYinghai Lu void *alignf_data,
184f75b99d5SYinghai Lu struct pci_bus_region *region)
1851da177e4SLinus Torvalds {
186f75b99d5SYinghai Lu struct resource *r, avail;
187f75b99d5SYinghai Lu resource_size_t max;
18802992064SAndy Shevchenko int ret;
1891da177e4SLinus Torvalds
190aa11fc58SBjorn Helgaas type_mask |= IORESOURCE_TYPE_BITS;
1911da177e4SLinus Torvalds
19202992064SAndy Shevchenko pci_bus_for_each_resource(bus, r) {
1933460baa6SChristoph Biedl resource_size_t min_used = min;
1943460baa6SChristoph Biedl
1956db45b76SBjorn Helgaas if (!r)
1966db45b76SBjorn Helgaas continue;
1976db45b76SBjorn Helgaas
1981da177e4SLinus Torvalds /* type_mask must match */
1991da177e4SLinus Torvalds if ((res->flags ^ r->flags) & type_mask)
2001da177e4SLinus Torvalds continue;
2011da177e4SLinus Torvalds
2021da177e4SLinus Torvalds /* We cannot allocate a non-prefetching resource
2031da177e4SLinus Torvalds from a pre-fetching area */
2041da177e4SLinus Torvalds if ((r->flags & IORESOURCE_PREFETCH) &&
2051da177e4SLinus Torvalds !(res->flags & IORESOURCE_PREFETCH))
2061da177e4SLinus Torvalds continue;
2071da177e4SLinus Torvalds
208f75b99d5SYinghai Lu avail = *r;
209f75b99d5SYinghai Lu pci_clip_resource_to_region(bus, &avail, region);
210f75b99d5SYinghai Lu
211f75b99d5SYinghai Lu /*
212f75b99d5SYinghai Lu * "min" is typically PCIBIOS_MIN_IO or PCIBIOS_MIN_MEM to
213f75b99d5SYinghai Lu * protect badly documented motherboard resources, but if
214f75b99d5SYinghai Lu * this is an already-configured bridge window, its start
215f75b99d5SYinghai Lu * overrides "min".
216f75b99d5SYinghai Lu */
217f75b99d5SYinghai Lu if (avail.start)
2183460baa6SChristoph Biedl min_used = avail.start;
219f75b99d5SYinghai Lu
220f75b99d5SYinghai Lu max = avail.end;
221f75b99d5SYinghai Lu
2225c5fb3c3SBjorn Helgaas /* Don't bother if available space isn't large enough */
2235c5fb3c3SBjorn Helgaas if (size > max - min_used + 1)
2245c5fb3c3SBjorn Helgaas continue;
2255c5fb3c3SBjorn Helgaas
2261da177e4SLinus Torvalds /* Ok, try it out.. */
2273460baa6SChristoph Biedl ret = allocate_resource(r, res, size, min_used, max,
228f75b99d5SYinghai Lu align, alignf, alignf_data);
2291da177e4SLinus Torvalds if (ret == 0)
230f75b99d5SYinghai Lu return 0;
2311da177e4SLinus Torvalds }
232f75b99d5SYinghai Lu return -ENOMEM;
233f75b99d5SYinghai Lu }
234f75b99d5SYinghai Lu
2351da177e4SLinus Torvalds /**
2361da177e4SLinus Torvalds * pci_bus_alloc_resource - allocate a resource from a parent bus
2371da177e4SLinus Torvalds * @bus: PCI bus
2381da177e4SLinus Torvalds * @res: resource to allocate
2391da177e4SLinus Torvalds * @size: size of resource to allocate
2401da177e4SLinus Torvalds * @align: alignment of resource to allocate
2411da177e4SLinus Torvalds * @min: minimum /proc/iomem address to allocate
2421da177e4SLinus Torvalds * @type_mask: IORESOURCE_* type flags
2431da177e4SLinus Torvalds * @alignf: resource alignment function
2441da177e4SLinus Torvalds * @alignf_data: data argument for resource alignment function
2451da177e4SLinus Torvalds *
2461da177e4SLinus Torvalds * Given the PCI bus a device resides on, the size, minimum address,
2471da177e4SLinus Torvalds * alignment and type, try to find an acceptable resource allocation
2481da177e4SLinus Torvalds * for a specific device resource.
2491da177e4SLinus Torvalds */
pci_bus_alloc_resource(struct pci_bus * bus,struct resource * res,resource_size_t size,resource_size_t align,resource_size_t min,unsigned long type_mask,resource_size_t (* alignf)(void *,const struct resource *,resource_size_t,resource_size_t),void * alignf_data)250d56dbf5bSYinghai Lu int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
2511da177e4SLinus Torvalds resource_size_t size, resource_size_t align,
252664c2848SBjorn Helgaas resource_size_t min, unsigned long type_mask,
2531da177e4SLinus Torvalds resource_size_t (*alignf)(void *,
2541da177e4SLinus Torvalds const struct resource *,
2551da177e4SLinus Torvalds resource_size_t,
2561da177e4SLinus Torvalds resource_size_t),
2571da177e4SLinus Torvalds void *alignf_data)
2581da177e4SLinus Torvalds {
2598e639079SChristoph Hellwig #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
260d56dbf5bSYinghai Lu int rc;
261d56dbf5bSYinghai Lu
262d56dbf5bSYinghai Lu if (res->flags & IORESOURCE_MEM_64) {
263d56dbf5bSYinghai Lu rc = pci_bus_alloc_from_region(bus, res, size, align, min,
264d56dbf5bSYinghai Lu type_mask, alignf, alignf_data,
265d56dbf5bSYinghai Lu &pci_high);
266d56dbf5bSYinghai Lu if (rc == 0)
267d56dbf5bSYinghai Lu return 0;
268d56dbf5bSYinghai Lu
269f75b99d5SYinghai Lu return pci_bus_alloc_from_region(bus, res, size, align, min,
270f75b99d5SYinghai Lu type_mask, alignf, alignf_data,
271f75b99d5SYinghai Lu &pci_64_bit);
272d56dbf5bSYinghai Lu }
273f75b99d5SYinghai Lu #endif
2741da177e4SLinus Torvalds
275f75b99d5SYinghai Lu return pci_bus_alloc_from_region(bus, res, size, align, min,
276f75b99d5SYinghai Lu type_mask, alignf, alignf_data,
277f75b99d5SYinghai Lu &pci_32_bit);
2781da177e4SLinus Torvalds }
279b7fe9434SRyan Desfosses EXPORT_SYMBOL(pci_bus_alloc_resource);
2801da177e4SLinus Torvalds
2810f7e7aeeSYinghai Lu /*
2820f7e7aeeSYinghai Lu * The @idx resource of @dev should be a PCI-PCI bridge window. If this
2830f7e7aeeSYinghai Lu * resource fits inside a window of an upstream bridge, do nothing. If it
2840f7e7aeeSYinghai Lu * overlaps an upstream window but extends outside it, clip the resource so
2850f7e7aeeSYinghai Lu * it fits completely inside.
2860f7e7aeeSYinghai Lu */
pci_bus_clip_resource(struct pci_dev * dev,int idx)2870f7e7aeeSYinghai Lu bool pci_bus_clip_resource(struct pci_dev *dev, int idx)
2880f7e7aeeSYinghai Lu {
2890f7e7aeeSYinghai Lu struct pci_bus *bus = dev->bus;
2900f7e7aeeSYinghai Lu struct resource *res = &dev->resource[idx];
2910f7e7aeeSYinghai Lu struct resource orig_res = *res;
2920f7e7aeeSYinghai Lu struct resource *r;
2930f7e7aeeSYinghai Lu
29402992064SAndy Shevchenko pci_bus_for_each_resource(bus, r) {
2950f7e7aeeSYinghai Lu resource_size_t start, end;
2960f7e7aeeSYinghai Lu
2970f7e7aeeSYinghai Lu if (!r)
2980f7e7aeeSYinghai Lu continue;
2990f7e7aeeSYinghai Lu
3000f7e7aeeSYinghai Lu if (resource_type(res) != resource_type(r))
3010f7e7aeeSYinghai Lu continue;
3020f7e7aeeSYinghai Lu
3030f7e7aeeSYinghai Lu start = max(r->start, res->start);
3040f7e7aeeSYinghai Lu end = min(r->end, res->end);
3050f7e7aeeSYinghai Lu
3060f7e7aeeSYinghai Lu if (start > end)
3070f7e7aeeSYinghai Lu continue; /* no overlap */
3080f7e7aeeSYinghai Lu
3090f7e7aeeSYinghai Lu if (res->start == start && res->end == end)
3100f7e7aeeSYinghai Lu return false; /* no change */
3110f7e7aeeSYinghai Lu
3120f7e7aeeSYinghai Lu res->start = start;
3130f7e7aeeSYinghai Lu res->end = end;
314b838b39eSBjorn Helgaas res->flags &= ~IORESOURCE_UNSET;
315b838b39eSBjorn Helgaas orig_res.flags &= ~IORESOURCE_UNSET;
31634c6b710SMohan Kumar pci_info(dev, "%pR clipped to %pR\n", &orig_res, res);
3170f7e7aeeSYinghai Lu
3180f7e7aeeSYinghai Lu return true;
3190f7e7aeeSYinghai Lu }
3200f7e7aeeSYinghai Lu
3210f7e7aeeSYinghai Lu return false;
3220f7e7aeeSYinghai Lu }
3230f7e7aeeSYinghai Lu
pcibios_resource_survey_bus(struct pci_bus * bus)3243c449ed0SYinghai Lu void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { }
3253c449ed0SYinghai Lu
pcibios_bus_add_device(struct pci_dev * pdev)3267b77061fSWei Yang void __weak pcibios_bus_add_device(struct pci_dev *pdev) { }
3277b77061fSWei Yang
3281da177e4SLinus Torvalds /**
3294f535093SYinghai Lu * pci_bus_add_device - start driver for a single device
3301da177e4SLinus Torvalds * @dev: device to add
3311da177e4SLinus Torvalds *
3324f535093SYinghai Lu * This adds add sysfs entries and start device drivers
3331da177e4SLinus Torvalds */
pci_bus_add_device(struct pci_dev * dev)334c893d133SYijing Wang void pci_bus_add_device(struct pci_dev *dev)
3351da177e4SLinus Torvalds {
3361a8c251cSVladimir Oltean struct device_node *dn = dev->dev.of_node;
337b19441afSGreg Kroah-Hartman int retval;
338735bff10SMyron Stowe
3394f535093SYinghai Lu /*
3404f535093SYinghai Lu * Can not put in pci_device_add yet because resources
3414f535093SYinghai Lu * are not assigned yet for some devices.
3424f535093SYinghai Lu */
3437b77061fSWei Yang pcibios_bus_add_device(dev);
344e253aaf0SYinghai Lu pci_fixup_device(pci_fixup_final, dev);
345407d1a51SLizhi Hou if (pci_is_bridge(dev))
346407d1a51SLizhi Hou of_pci_make_dev_node(dev);
3474f535093SYinghai Lu pci_create_sysfs_dev_files(dev);
348ef37702eSYinghai Lu pci_proc_attach_device(dev);
3491ed276a7SLukas Wunner pci_bridge_d3_update(dev);
3501da177e4SLinus Torvalds
3511a8c251cSVladimir Oltean dev->match_driver = !dn || of_device_is_available(dn);
35258d9a38fSYinghai Lu retval = device_attach(&dev->dev);
3532194bc7cSRajat Jain if (retval < 0 && retval != -EPROBE_DEFER)
3547506dc79SFrederick Lawler pci_warn(dev, "device attach failed (%d)\n", retval);
35558d9a38fSYinghai Lu
35644bda4b7SHari Vyas pci_dev_assign_added(dev, true);
3571da177e4SLinus Torvalds }
358b7fe9434SRyan Desfosses EXPORT_SYMBOL_GPL(pci_bus_add_device);
3591da177e4SLinus Torvalds
3601da177e4SLinus Torvalds /**
3614f535093SYinghai Lu * pci_bus_add_devices - start driver for PCI devices
3621da177e4SLinus Torvalds * @bus: bus to check for new devices
3631da177e4SLinus Torvalds *
3644f535093SYinghai Lu * Start driver for PCI devices and add some sysfs entries.
3651da177e4SLinus Torvalds */
pci_bus_add_devices(const struct pci_bus * bus)366c48f1670Sakpm@linux-foundation.org void pci_bus_add_devices(const struct pci_bus *bus)
3671da177e4SLinus Torvalds {
3681da177e4SLinus Torvalds struct pci_dev *dev;
3693fa16fdbSYu Zhao struct pci_bus *child;
3701da177e4SLinus Torvalds
3711da177e4SLinus Torvalds list_for_each_entry(dev, &bus->devices, bus_list) {
3728a1bc901SGreg Kroah-Hartman /* Skip already-added devices */
37344bda4b7SHari Vyas if (pci_dev_is_added(dev))
3741da177e4SLinus Torvalds continue;
375c893d133SYijing Wang pci_bus_add_device(dev);
3761da177e4SLinus Torvalds }
3771da177e4SLinus Torvalds
3781da177e4SLinus Torvalds list_for_each_entry(dev, &bus->devices, bus_list) {
3791e398eaeSLukas Wunner /* Skip if device attach failed */
38044bda4b7SHari Vyas if (!pci_dev_is_added(dev))
3811e398eaeSLukas Wunner continue;
3823fa16fdbSYu Zhao child = dev->subordinate;
383981cf9eaSJiang Liu if (child)
3843fa16fdbSYu Zhao pci_bus_add_devices(child);
3851da177e4SLinus Torvalds }
3861da177e4SLinus Torvalds }
387b7fe9434SRyan Desfosses EXPORT_SYMBOL(pci_bus_add_devices);
3881da177e4SLinus Torvalds
__pci_walk_bus(struct pci_bus * top,int (* cb)(struct pci_dev *,void *),void * userdata,bool locked)389*b0f44788SJohan Hovold static void __pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),
390*b0f44788SJohan Hovold void *userdata, bool locked)
391cecf4864SPaul Mackerras {
392cecf4864SPaul Mackerras struct pci_dev *dev;
393cecf4864SPaul Mackerras struct pci_bus *bus;
394cecf4864SPaul Mackerras struct list_head *next;
39570298c6eSZhang, Yanmin int retval;
396cecf4864SPaul Mackerras
397cecf4864SPaul Mackerras bus = top;
398*b0f44788SJohan Hovold if (!locked)
399d71374daSZhang Yanmin down_read(&pci_bus_sem);
400cecf4864SPaul Mackerras next = top->devices.next;
401cecf4864SPaul Mackerras for (;;) {
402cecf4864SPaul Mackerras if (next == &bus->devices) {
403cecf4864SPaul Mackerras /* end of this bus, go up or finish */
404cecf4864SPaul Mackerras if (bus == top)
405cecf4864SPaul Mackerras break;
406cecf4864SPaul Mackerras next = bus->self->bus_list.next;
407cecf4864SPaul Mackerras bus = bus->self->bus;
408cecf4864SPaul Mackerras continue;
409cecf4864SPaul Mackerras }
410cecf4864SPaul Mackerras dev = list_entry(next, struct pci_dev, bus_list);
411cecf4864SPaul Mackerras if (dev->subordinate) {
412cecf4864SPaul Mackerras /* this is a pci-pci bridge, do its devices next */
413cecf4864SPaul Mackerras next = dev->subordinate->devices.next;
414cecf4864SPaul Mackerras bus = dev->subordinate;
415cecf4864SPaul Mackerras } else
416cecf4864SPaul Mackerras next = dev->bus_list.next;
417cecf4864SPaul Mackerras
41870298c6eSZhang, Yanmin retval = cb(dev, userdata);
41970298c6eSZhang, Yanmin if (retval)
42070298c6eSZhang, Yanmin break;
421cecf4864SPaul Mackerras }
422*b0f44788SJohan Hovold if (!locked)
423d71374daSZhang Yanmin up_read(&pci_bus_sem);
424cecf4864SPaul Mackerras }
425*b0f44788SJohan Hovold
426*b0f44788SJohan Hovold /**
427*b0f44788SJohan Hovold * pci_walk_bus - walk devices on/under bus, calling callback.
428*b0f44788SJohan Hovold * @top: bus whose devices should be walked
429*b0f44788SJohan Hovold * @cb: callback to be called for each device found
430*b0f44788SJohan Hovold * @userdata: arbitrary pointer to be passed to callback
431*b0f44788SJohan Hovold *
432*b0f44788SJohan Hovold * Walk the given bus, including any bridged devices
433*b0f44788SJohan Hovold * on buses under this bus. Call the provided callback
434*b0f44788SJohan Hovold * on each device found.
435*b0f44788SJohan Hovold *
436*b0f44788SJohan Hovold * We check the return of @cb each time. If it returns anything
437*b0f44788SJohan Hovold * other than 0, we break out.
438*b0f44788SJohan Hovold */
pci_walk_bus(struct pci_bus * top,int (* cb)(struct pci_dev *,void *),void * userdata)439*b0f44788SJohan Hovold void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void *userdata)
440*b0f44788SJohan Hovold {
441*b0f44788SJohan Hovold __pci_walk_bus(top, cb, userdata, false);
442*b0f44788SJohan Hovold }
4437c94def8SKonrad Rzeszutek Wilk EXPORT_SYMBOL_GPL(pci_walk_bus);
444cecf4864SPaul Mackerras
pci_walk_bus_locked(struct pci_bus * top,int (* cb)(struct pci_dev *,void *),void * userdata)445*b0f44788SJohan Hovold void pci_walk_bus_locked(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void *userdata)
446*b0f44788SJohan Hovold {
447*b0f44788SJohan Hovold lockdep_assert_held(&pci_bus_sem);
448*b0f44788SJohan Hovold
449*b0f44788SJohan Hovold __pci_walk_bus(top, cb, userdata, true);
450*b0f44788SJohan Hovold }
451*b0f44788SJohan Hovold EXPORT_SYMBOL_GPL(pci_walk_bus_locked);
452*b0f44788SJohan Hovold
pci_bus_get(struct pci_bus * bus)453fe830ef6SJiang Liu struct pci_bus *pci_bus_get(struct pci_bus *bus)
454fe830ef6SJiang Liu {
455fe830ef6SJiang Liu if (bus)
456fe830ef6SJiang Liu get_device(&bus->dev);
457fe830ef6SJiang Liu return bus;
458fe830ef6SJiang Liu }
459fe830ef6SJiang Liu
pci_bus_put(struct pci_bus * bus)460fe830ef6SJiang Liu void pci_bus_put(struct pci_bus *bus)
461fe830ef6SJiang Liu {
462fe830ef6SJiang Liu if (bus)
463fe830ef6SJiang Liu put_device(&bus->dev);
464fe830ef6SJiang Liu }
465