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 17 18 19
/*
 *	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"

20 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 59 60 61 62 63 64 65
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++)
		bus->resource[i] = 0;

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

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

	type_mask |= IORESOURCE_IO | IORESOURCE_MEM;

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

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

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

140
	dev->is_added = 1;
L
Linus Torvalds 已提交
141 142
	pci_proc_attach_device(dev);
	pci_create_sysfs_dev_files(dev);
143
	return 0;
L
Linus Torvalds 已提交
144 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
/**
 * 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 已提交
177 178 179 180 181 182 183 184 185 186 187 188
/**
 * 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.
 */
189
void pci_bus_add_devices(const struct pci_bus *bus)
L
Linus Torvalds 已提交
190 191
{
	struct pci_dev *dev;
Y
Yu Zhao 已提交
192
	struct pci_bus *child;
193
	int retval;
L
Linus Torvalds 已提交
194 195

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

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

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

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

	list_for_each_entry(dev, &bus->devices, bus_list) {
		if (dev->subordinate) {
240
			if (!pci_is_enabled(dev)) {
241 242 243
				retval = pci_enable_device(dev);
				pci_set_master(dev);
			}
L
Linus Torvalds 已提交
244 245 246 247 248
			pci_enable_bridges(dev->subordinate);
		}
	}
}

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

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

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

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