// SPDX-License-Identifier: GPL-2.0 /* * From setup-res.c, by: * Dave Rusling (david.rusling@reo.mts.dec.com) * David Mosberger (davidm@cs.arizona.edu) * David Miller (davem@redhat.com) * Ivan Kokshaysky (ink@jurassic.park.msu.ru)
*/ #include <linux/module.h> #include <linux/kernel.h> #include <linux/pci.h> #include <linux/errno.h> #include <linux/ioport.h> #include <linux/of.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/proc_fs.h> #include <linux/slab.h>
#include"pci.h"
/* * The first PCI_BRIDGE_RESOURCE_NUM PCI bus resources (those that correspond * to P2P or CardBus bridge windows) go in a table. Additional ones (for * buses below host bridges or subtractive decode bridges) go in the list. * Use pci_bus_for_each_resource() to iterate through all the resources.
*/
/* * @res contains CPU addresses. Clip it so the corresponding bus addresses * on @bus are entirely within @region. This is used to control the bus * addresses of resources we allocate, e.g., we may need a resource that * can be mapped by a 32-bit BAR.
*/ staticvoid pci_clip_resource_to_region(struct pci_bus *bus, struct resource *res, struct pci_bus_region *region)
{ struct pci_bus_region r;
pcibios_resource_to_bus(bus, &r, res); if (r.start < region->start)
r.start = region->start; if (r.end > region->end)
r.end = region->end;
/* type_mask must match */ if ((res->flags ^ r->flags) & type_mask) continue;
/* We cannot allocate a non-prefetching resource
from a pre-fetching area */ if ((r->flags & IORESOURCE_PREFETCH) &&
!(res->flags & IORESOURCE_PREFETCH)) continue;
/* * "min" is typically PCIBIOS_MIN_IO or PCIBIOS_MIN_MEM to * protect badly documented motherboard resources, but if * this is an already-configured bridge window, its start * overrides "min".
*/ if (avail.start)
min_used = avail.start;
max = avail.end;
/* Don't bother if available space isn't large enough */ if (size > max - min_used + 1) continue;
/* Ok, try it out.. */
ret = allocate_resource(r, res, size, min_used, max,
align, alignf, alignf_data); if (ret == 0) return 0;
} return -ENOMEM;
}
/** * pci_bus_alloc_resource - allocate a resource from a parent bus * @bus: PCI bus * @res: resource to allocate * @size: size of resource to allocate * @align: alignment of resource to allocate * @min: minimum /proc/iomem address to allocate * @type_mask: IORESOURCE_* type flags * @alignf: resource alignment function * @alignf_data: data argument for resource alignment function * * Given the PCI bus a device resides on, the size, minimum address, * alignment and type, try to find an acceptable resource allocation * for a specific device resource.
*/ int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
resource_size_t size, resource_size_t align,
resource_size_t min, unsignedlong type_mask,
resource_alignf alignf, void *alignf_data)
{ #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT int rc;
/* * The @idx resource of @dev should be a PCI-PCI bridge window. If this * resource fits inside a window of an upstream bridge, do nothing. If it * overlaps an upstream window but extends outside it, clip the resource so * it fits completely inside.
*/ bool pci_bus_clip_resource(struct pci_dev *dev, int idx)
{ struct pci_bus *bus = dev->bus; struct resource *res = &dev->resource[idx]; struct resource orig_res = *res; struct resource *r;
/** * pci_bus_add_device - start driver for a single device * @dev: device to add * * This adds add sysfs entries and start device drivers
*/ void pci_bus_add_device(struct pci_dev *dev)
{ struct device_node *dn = dev->dev.of_node; struct platform_device *pdev;
/* * Can not put in pci_device_add yet because resources * are not assigned yet for some devices.
*/
pcibios_bus_add_device(dev);
pci_fixup_device(pci_fixup_final, dev); if (pci_is_bridge(dev))
of_pci_make_dev_node(dev);
pci_create_sysfs_dev_files(dev);
pci_proc_attach_device(dev);
pci_bridge_d3_update(dev);
/* * If the PCI device is associated with a pwrctrl device with a * power supply, create a device link between the PCI device and * pwrctrl device. This ensures that pwrctrl drivers are probed * before PCI client drivers.
*/
pdev = of_find_device_by_node(dn); if (pdev) { if (of_pci_supply_present(dn)) { if (!device_link_add(&dev->dev, &pdev->dev,
DL_FLAG_AUTOREMOVE_CONSUMER)) {
pci_err(dev, "failed to add device link to power control device %s\n",
pdev->name);
}
}
put_device(&pdev->dev);
}
if (!dn || of_device_is_available(dn))
pci_dev_allow_binding(dev);
list_for_each_entry(dev, &bus->devices, bus_list) { /* Skip if device attach failed */ if (!pci_dev_is_added(dev)) continue;
child = dev->subordinate; if (child)
pci_bus_add_devices(child);
}
}
EXPORT_SYMBOL(pci_bus_add_devices);
staticint __pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void *userdata)
{ struct pci_dev *dev; int ret = 0;
list_for_each_entry(dev, &top->devices, bus_list) {
ret = cb(dev, userdata); if (ret) break; if (dev->subordinate) {
ret = __pci_walk_bus(dev->subordinate, cb, userdata); if (ret) break;
}
} return ret;
}
/** * pci_walk_bus - walk devices on/under bus, calling callback. * @top: bus whose devices should be walked * @cb: callback to be called for each device found * @userdata: arbitrary pointer to be passed to callback * * Walk the given bus, including any bridged devices * on buses under this bus. Call the provided callback * on each device found. * * We check the return of @cb each time. If it returns anything * other than 0, we break out.
*/ void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void *userdata)
{
down_read(&pci_bus_sem);
__pci_walk_bus(top, cb, userdata);
up_read(&pci_bus_sem);
}
EXPORT_SYMBOL_GPL(pci_walk_bus);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.