bus.c 7.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 *	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>
17
#include <linux/slab.h>
L
Linus Torvalds 已提交
18 19 20

#include "pci.h"

21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
void pci_bus_add_resource(struct pci_bus *bus, struct resource *res,
			  unsigned int flags)
{
	struct pci_bus_resource *bus_res;

	bus_res = kzalloc(sizeof(struct pci_bus_resource), GFP_KERNEL);
	if (!bus_res) {
		dev_err(&bus->dev, "can't add %pR resource\n", res);
		return;
	}

	bus_res->res = res;
	bus_res->flags = flags;
	list_add_tail(&bus_res->list, &bus->resources);
}

struct resource *pci_bus_resource_n(const struct pci_bus *bus, int n)
{
	struct pci_bus_resource *bus_res;

	if (n < PCI_BRIDGE_RESOURCE_NUM)
		return bus->resource[n];

	n -= PCI_BRIDGE_RESOURCE_NUM;
	list_for_each_entry(bus_res, &bus->resources, list) {
		if (n-- == 0)
			return bus_res->res;
	}
	return NULL;
}
EXPORT_SYMBOL_GPL(pci_bus_resource_n);

void pci_bus_remove_resources(struct pci_bus *bus)
{
	struct pci_bus_resource *bus_res, *tmp;
	int i;

	for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++)
59
		bus->resource[i] = NULL;
60 61 62 63 64 65 66

	list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) {
		list_del(&bus_res->list);
		kfree(bus_res);
	}
}

L
Linus Torvalds 已提交
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
/**
 * 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,
84 85
		resource_size_t size, resource_size_t align,
		resource_size_t min, unsigned int type_mask,
86
		resource_size_t (*alignf)(void *,
87
					  const struct resource *,
88 89
					  resource_size_t,
					  resource_size_t),
90
		void *alignf_data)
L
Linus Torvalds 已提交
91 92
{
	int i, ret = -ENOMEM;
93
	struct resource *r;
94
	resource_size_t max = -1;
L
Linus Torvalds 已提交
95 96 97

	type_mask |= IORESOURCE_IO | IORESOURCE_MEM;

98 99 100 101
	/* don't allocate too high if the pref mem doesn't support 64bit*/
	if (!(res->flags & IORESOURCE_MEM_64))
		max = PCIBIOS_MAX_MEM_32;

102
	pci_bus_for_each_resource(bus, r, i) {
L
Linus Torvalds 已提交
103 104 105 106 107 108 109 110 111 112 113 114 115 116
		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.. */
117 118
		ret = allocate_resource(r, res, size,
					r->start ? : min,
119
					max, align,
L
Linus Torvalds 已提交
120 121 122 123 124 125 126 127
					alignf, alignf_data);
		if (ret == 0)
			break;
	}
	return ret;
}

/**
Y
Yu Zhao 已提交
128
 * pci_bus_add_device - add a single device
L
Linus Torvalds 已提交
129 130 131 132 133
 * @dev: device to add
 *
 * This adds a single pci device to the global
 * device list and adds sysfs and procfs entries
 */
134
int pci_bus_add_device(struct pci_dev *dev)
L
Linus Torvalds 已提交
135
{
136 137 138 139
	int retval;
	retval = device_add(&dev->dev);
	if (retval)
		return retval;
L
Linus Torvalds 已提交
140

141
	dev->is_added = 1;
L
Linus Torvalds 已提交
142 143
	pci_proc_attach_device(dev);
	pci_create_sysfs_dev_files(dev);
144
	return 0;
L
Linus Torvalds 已提交
145 146
}

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
/**
 * 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 已提交
178 179 180 181 182 183 184 185 186 187 188 189
/**
 * 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.
 */
190
void pci_bus_add_devices(const struct pci_bus *bus)
L
Linus Torvalds 已提交
191 192
{
	struct pci_dev *dev;
Y
Yu Zhao 已提交
193
	struct pci_bus *child;
194
	int retval;
L
Linus Torvalds 已提交
195 196

	list_for_each_entry(dev, &bus->devices, bus_list) {
197 198
		/* Skip already-added devices */
		if (dev->is_added)
L
Linus Torvalds 已提交
199
			continue;
200 201 202
		retval = pci_bus_add_device(dev);
		if (retval)
			dev_err(&dev->dev, "Error adding device, continuing\n");
L
Linus Torvalds 已提交
203 204 205
	}

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

Y
Yu Zhao 已提交
208
		child = dev->subordinate;
L
Linus Torvalds 已提交
209 210 211 212
		/*
		 * If there is an unattached subordinate bus, attach
		 * it and then scan for unattached PCI devices.
		 */
Y
Yu Zhao 已提交
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
		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;
228
		retval = pci_bus_add_child(child);
Y
Yu Zhao 已提交
229
		if (retval)
230
			dev_err(&dev->dev, "Error adding bus, continuing\n");
L
Linus Torvalds 已提交
231 232 233 234 235 236
	}
}

void pci_enable_bridges(struct pci_bus *bus)
{
	struct pci_dev *dev;
237
	int retval;
L
Linus Torvalds 已提交
238 239 240

	list_for_each_entry(dev, &bus->devices, bus_list) {
		if (dev->subordinate) {
241
			if (!pci_is_enabled(dev)) {
242
				retval = pci_enable_device(dev);
243 244
				if (retval)
					dev_err(&dev->dev, "Error enabling bridge (%d), continuing\n", retval);
245 246
				pci_set_master(dev);
			}
L
Linus Torvalds 已提交
247 248 249 250 251
			pci_enable_bridges(dev->subordinate);
		}
	}
}

252 253 254 255 256 257 258 259
/** 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.
260 261 262 263
 *
 *  We check the return of @cb each time. If it returns anything
 *  other than 0, we break out.
 *
264
 */
265
void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),
266 267 268 269 270
		  void *userdata)
{
	struct pci_dev *dev;
	struct pci_bus *bus;
	struct list_head *next;
271
	int retval;
272 273

	bus = top;
274
	down_read(&pci_bus_sem);
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
	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;

293
		/* Run device routines with the device locked */
294
		device_lock(&dev->dev);
295
		retval = cb(dev, userdata);
296
		device_unlock(&dev->dev);
297 298
		if (retval)
			break;
299
	}
300
	up_read(&pci_bus_sem);
301
}
302
EXPORT_SYMBOL_GPL(pci_walk_bus);
303

L
Linus Torvalds 已提交
304 305 306 307
EXPORT_SYMBOL(pci_bus_alloc_resource);
EXPORT_SYMBOL_GPL(pci_bus_add_device);
EXPORT_SYMBOL(pci_bus_add_devices);
EXPORT_SYMBOL(pci_enable_bridges);