bus.c 6.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
/*
 *	drivers/pci/bus.c
 *
 * 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/proc_fs.h>
#include <linux/init.h>

#include "pci.h"

/**
 * 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,
37 38
		resource_size_t size, resource_size_t align,
		resource_size_t min, unsigned int type_mask,
39
		resource_size_t (*alignf)(void *,
40
					  const struct resource *,
41 42
					  resource_size_t,
					  resource_size_t),
43
		void *alignf_data)
L
Linus Torvalds 已提交
44 45
{
	int i, ret = -ENOMEM;
46
	resource_size_t max = -1;
L
Linus Torvalds 已提交
47 48 49

	type_mask |= IORESOURCE_IO | IORESOURCE_MEM;

50 51 52 53
	/* don't allocate too high if the pref mem doesn't support 64bit*/
	if (!(res->flags & IORESOURCE_MEM_64))
		max = PCIBIOS_MAX_MEM_32;

L
Linus Torvalds 已提交
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
	for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) {
		struct resource *r = bus->resource[i];
		if (!r)
			continue;

		/* 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;

		/* Ok, try it out.. */
70 71
		ret = allocate_resource(r, res, size,
					r->start ? : min,
72
					max, align,
L
Linus Torvalds 已提交
73 74 75 76 77 78 79 80
					alignf, alignf_data);
		if (ret == 0)
			break;
	}
	return ret;
}

/**
Y
Yu Zhao 已提交
81
 * pci_bus_add_device - add a single device
L
Linus Torvalds 已提交
82 83 84 85 86
 * @dev: device to add
 *
 * This adds a single pci device to the global
 * device list and adds sysfs and procfs entries
 */
87
int pci_bus_add_device(struct pci_dev *dev)
L
Linus Torvalds 已提交
88
{
89 90 91 92
	int retval;
	retval = device_add(&dev->dev);
	if (retval)
		return retval;
L
Linus Torvalds 已提交
93

94
	dev->is_added = 1;
L
Linus Torvalds 已提交
95 96
	pci_proc_attach_device(dev);
	pci_create_sysfs_dev_files(dev);
97
	return 0;
L
Linus Torvalds 已提交
98 99
}

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
/**
 * pci_bus_add_child - add a child bus
 * @bus: bus to add
 *
 * This adds sysfs entries for a single bus
 */
int pci_bus_add_child(struct pci_bus *bus)
{
	int retval;

	if (bus->bridge)
		bus->dev.parent = bus->bridge;

	retval = device_register(&bus->dev);
	if (retval)
		return retval;

	bus->is_added = 1;

	retval = device_create_file(&bus->dev, &dev_attr_cpuaffinity);
	if (retval)
		return retval;

	retval = device_create_file(&bus->dev, &dev_attr_cpulistaffinity);

	/* Create legacy_io and legacy_mem files for this bus */
	pci_create_legacy_files(bus);

	return retval;
}

L
Linus Torvalds 已提交
131 132 133 134 135 136 137 138 139 140 141 142
/**
 * pci_bus_add_devices - insert newly discovered PCI devices
 * @bus: bus to check for new devices
 *
 * Add newly discovered PCI devices (which are on the bus->devices
 * list) to the global PCI device list, add the sysfs and procfs
 * entries.  Where a bridge is found, add the discovered bus to
 * the parents list of child buses, and recurse (breadth-first
 * to be compatible with 2.4)
 *
 * Call hotplug for each new devices.
 */
143
void pci_bus_add_devices(const struct pci_bus *bus)
L
Linus Torvalds 已提交
144 145
{
	struct pci_dev *dev;
Y
Yu Zhao 已提交
146
	struct pci_bus *child;
147
	int retval;
L
Linus Torvalds 已提交
148 149

	list_for_each_entry(dev, &bus->devices, bus_list) {
150 151
		/* Skip already-added devices */
		if (dev->is_added)
L
Linus Torvalds 已提交
152
			continue;
153 154 155
		retval = pci_bus_add_device(dev);
		if (retval)
			dev_err(&dev->dev, "Error adding device, continuing\n");
L
Linus Torvalds 已提交
156 157 158
	}

	list_for_each_entry(dev, &bus->devices, bus_list) {
159
		BUG_ON(!dev->is_added);
L
Linus Torvalds 已提交
160

Y
Yu Zhao 已提交
161
		child = dev->subordinate;
L
Linus Torvalds 已提交
162 163 164 165
		/*
		 * If there is an unattached subordinate bus, attach
		 * it and then scan for unattached PCI devices.
		 */
Y
Yu Zhao 已提交
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
		if (!child)
			continue;
		if (list_empty(&child->node)) {
			down_write(&pci_bus_sem);
			list_add_tail(&child->node, &dev->bus->children);
			up_write(&pci_bus_sem);
		}
		pci_bus_add_devices(child);

		/*
		 * register the bus with sysfs as the parent is now
		 * properly registered.
		 */
		if (child->is_added)
			continue;
181
		retval = pci_bus_add_child(child);
Y
Yu Zhao 已提交
182
		if (retval)
183
			dev_err(&dev->dev, "Error adding bus, continuing\n");
L
Linus Torvalds 已提交
184 185 186 187 188 189
	}
}

void pci_enable_bridges(struct pci_bus *bus)
{
	struct pci_dev *dev;
190
	int retval;
L
Linus Torvalds 已提交
191 192 193

	list_for_each_entry(dev, &bus->devices, bus_list) {
		if (dev->subordinate) {
194
			if (!pci_is_enabled(dev)) {
195 196 197
				retval = pci_enable_device(dev);
				pci_set_master(dev);
			}
L
Linus Torvalds 已提交
198 199 200 201 202
			pci_enable_bridges(dev->subordinate);
		}
	}
}

203 204 205 206 207 208 209 210
/** 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.
211 212 213 214
 *
 *  We check the return of @cb each time. If it returns anything
 *  other than 0, we break out.
 *
215
 */
216
void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),
217 218 219 220 221
		  void *userdata)
{
	struct pci_dev *dev;
	struct pci_bus *bus;
	struct list_head *next;
222
	int retval;
223 224

	bus = top;
225
	down_read(&pci_bus_sem);
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243
	next = top->devices.next;
	for (;;) {
		if (next == &bus->devices) {
			/* end of this bus, go up or finish */
			if (bus == top)
				break;
			next = bus->self->bus_list.next;
			bus = bus->self->bus;
			continue;
		}
		dev = list_entry(next, struct pci_dev, bus_list);
		if (dev->subordinate) {
			/* this is a pci-pci bridge, do its devices next */
			next = dev->subordinate->devices.next;
			bus = dev->subordinate;
		} else
			next = dev->bus_list.next;

244 245
		/* Run device routines with the device locked */
		down(&dev->dev.sem);
246
		retval = cb(dev, userdata);
247
		up(&dev->dev.sem);
248 249
		if (retval)
			break;
250
	}
251
	up_read(&pci_bus_sem);
252 253
}

L
Linus Torvalds 已提交
254 255 256 257
EXPORT_SYMBOL(pci_bus_alloc_resource);
EXPORT_SYMBOL_GPL(pci_bus_add_device);
EXPORT_SYMBOL(pci_bus_add_devices);
EXPORT_SYMBOL(pci_enable_bridges);