bus.c 7.2 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 103 104 105
	pci_bus_for_each_resource(bus, r, i) {
		if (!r)
			continue;

L
Linus Torvalds 已提交
106 107 108 109 110 111 112 113 114 115 116
		/* 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
/**
 * 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;

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

	return retval;
}

L
Linus Torvalds 已提交
172 173 174 175 176 177 178 179 180 181 182 183
/**
 * 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.
 */
184
void pci_bus_add_devices(const struct pci_bus *bus)
L
Linus Torvalds 已提交
185 186
{
	struct pci_dev *dev;
Y
Yu Zhao 已提交
187
	struct pci_bus *child;
188
	int retval;
L
Linus Torvalds 已提交
189 190

	list_for_each_entry(dev, &bus->devices, bus_list) {
191 192
		/* Skip already-added devices */
		if (dev->is_added)
L
Linus Torvalds 已提交
193
			continue;
194 195 196
		retval = pci_bus_add_device(dev);
		if (retval)
			dev_err(&dev->dev, "Error adding device, continuing\n");
L
Linus Torvalds 已提交
197 198 199
	}

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

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

void pci_enable_bridges(struct pci_bus *bus)
{
	struct pci_dev *dev;
231
	int retval;
L
Linus Torvalds 已提交
232 233 234

	list_for_each_entry(dev, &bus->devices, bus_list) {
		if (dev->subordinate) {
235
			if (!pci_is_enabled(dev)) {
236
				retval = pci_enable_device(dev);
237 238
				if (retval)
					dev_err(&dev->dev, "Error enabling bridge (%d), continuing\n", retval);
239 240
				pci_set_master(dev);
			}
L
Linus Torvalds 已提交
241 242 243 244 245
			pci_enable_bridges(dev->subordinate);
		}
	}
}

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

	bus = top;
268
	down_read(&pci_bus_sem);
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
	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;

287
		/* Run device routines with the device locked */
288
		device_lock(&dev->dev);
289
		retval = cb(dev, userdata);
290
		device_unlock(&dev->dev);
291 292
		if (retval)
			break;
293
	}
294
	up_read(&pci_bus_sem);
295
}
296
EXPORT_SYMBOL_GPL(pci_walk_bus);
297

L
Linus Torvalds 已提交
298 299 300 301
EXPORT_SYMBOL(pci_bus_alloc_resource);
EXPORT_SYMBOL_GPL(pci_bus_add_device);
EXPORT_SYMBOL(pci_bus_add_devices);
EXPORT_SYMBOL(pci_enable_bridges);