bus.c 10.3 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 *	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>
16
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
17 18 19

#include "pci.h"

20 21
void pci_add_resource_offset(struct list_head *resources, struct resource *res,
			     resource_size_t offset)
22
{
23
	struct resource_entry *entry;
24

25 26
	entry = resource_list_create_entry(res, 0);
	if (!entry) {
27
		printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res);
28 29 30
		return;
	}

31 32
	entry->offset = offset;
	resource_list_add_tail(entry, resources);
33 34 35 36 37 38
}
EXPORT_SYMBOL(pci_add_resource_offset);

void pci_add_resource(struct list_head *resources, struct resource *res)
{
	pci_add_resource_offset(resources, res, 0);
39 40 41 42 43
}
EXPORT_SYMBOL(pci_add_resource);

void pci_free_resource_list(struct list_head *resources)
{
44
	resource_list_free(resources);
45 46 47
}
EXPORT_SYMBOL(pci_free_resource_list);

48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
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)
{
	int i;
83
	struct pci_bus_resource *bus_res, *tmp;
84 85

	for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++)
86
		bus->resource[i] = NULL;
87

88 89 90 91
	list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) {
		list_del(&bus_res->list);
		kfree(bus_res);
	}
92 93
}

94 95 96 97 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
int devm_request_pci_bus_resources(struct device *dev,
				   struct list_head *resources)
{
	struct resource_entry *win;
	struct resource *parent, *res;
	int err;

	resource_list_for_each_entry(win, resources) {
		res = win->res;
		switch (resource_type(res)) {
		case IORESOURCE_IO:
			parent = &ioport_resource;
			break;
		case IORESOURCE_MEM:
			parent = &iomem_resource;
			break;
		default:
			continue;
		}

		err = devm_request_resource(dev, parent, res);
		if (err)
			return err;
	}

	return 0;
}
EXPORT_SYMBOL_GPL(devm_request_pci_bus_resources);

123
static struct pci_bus_region pci_32_bit = {0, 0xffffffffULL};
Yinghai Lu's avatar
Yinghai Lu committed
124
#ifdef CONFIG_PCI_BUS_ADDR_T_64BIT
125
static struct pci_bus_region pci_64_bit = {0,
Yinghai Lu's avatar
Yinghai Lu committed
126 127 128
				(pci_bus_addr_t) 0xffffffffffffffffULL};
static struct pci_bus_region pci_high = {(pci_bus_addr_t) 0x100000000ULL,
				(pci_bus_addr_t) 0xffffffffffffffffULL};
129 130 131 132 133 134 135
#endif

/*
 * @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.
Linus Torvalds's avatar
Linus Torvalds committed
136
 */
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
static void 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;

	if (r.end < r.start)
		res->end = res->start - 1;
	else
		pcibios_bus_to_resource(bus, res, &r);
}

static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
156
		resource_size_t size, resource_size_t align,
157
		resource_size_t min, unsigned long type_mask,
158
		resource_size_t (*alignf)(void *,
159
					  const struct resource *,
160 161
					  resource_size_t,
					  resource_size_t),
162 163
		void *alignf_data,
		struct pci_bus_region *region)
Linus Torvalds's avatar
Linus Torvalds committed
164
{
165 166 167
	int i, ret;
	struct resource *r, avail;
	resource_size_t max;
Linus Torvalds's avatar
Linus Torvalds committed
168

169
	type_mask |= IORESOURCE_TYPE_BITS;
Linus Torvalds's avatar
Linus Torvalds committed
170

171
	pci_bus_for_each_resource(bus, r, i) {
172 173
		resource_size_t min_used = min;

174 175 176
		if (!r)
			continue;

Linus Torvalds's avatar
Linus Torvalds committed
177 178 179 180 181 182 183 184 185 186
		/* 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;

187 188 189
		avail = *r;
		pci_clip_resource_to_region(bus, &avail, region);

190 191 192 193 194 195
		/*
		 * "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".
		 */
196
		if (avail.start)
197
			min_used = avail.start;
198 199

		max = avail.end;
200

Linus Torvalds's avatar
Linus Torvalds committed
201
		/* Ok, try it out.. */
202
		ret = allocate_resource(r, res, size, min_used, max,
203
					align, alignf, alignf_data);
Linus Torvalds's avatar
Linus Torvalds committed
204
		if (ret == 0)
205
			return 0;
Linus Torvalds's avatar
Linus Torvalds committed
206
	}
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
	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.
 */
225
int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
226
		resource_size_t size, resource_size_t align,
227
		resource_size_t min, unsigned long type_mask,
228 229 230 231 232 233
		resource_size_t (*alignf)(void *,
					  const struct resource *,
					  resource_size_t,
					  resource_size_t),
		void *alignf_data)
{
Yinghai Lu's avatar
Yinghai Lu committed
234
#ifdef CONFIG_PCI_BUS_ADDR_T_64BIT
235 236 237 238 239 240 241 242 243
	int rc;

	if (res->flags & IORESOURCE_MEM_64) {
		rc = pci_bus_alloc_from_region(bus, res, size, align, min,
					       type_mask, alignf, alignf_data,
					       &pci_high);
		if (rc == 0)
			return 0;

244 245 246
		return pci_bus_alloc_from_region(bus, res, size, align, min,
						 type_mask, alignf, alignf_data,
						 &pci_64_bit);
Linus Torvalds's avatar
Linus Torvalds committed
247
	}
248 249 250 251 252
#endif

	return pci_bus_alloc_from_region(bus, res, size, align, min,
					 type_mask, alignf, alignf_data,
					 &pci_32_bit);
Linus Torvalds's avatar
Linus Torvalds committed
253
}
254
EXPORT_SYMBOL(pci_bus_alloc_resource);
Linus Torvalds's avatar
Linus Torvalds committed
255

256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
/*
 * 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;
	int i;

	pci_bus_for_each_resource(bus, r, i) {
		resource_size_t start, end;

		if (!r)
			continue;

		if (resource_type(res) != resource_type(r))
			continue;

		start = max(r->start, res->start);
		end = min(r->end, res->end);

		if (start > end)
			continue;	/* no overlap */

		if (res->start == start && res->end == end)
			return false;	/* no change */

		res->start = start;
		res->end = end;
290 291
		res->flags &= ~IORESOURCE_UNSET;
		orig_res.flags &= ~IORESOURCE_UNSET;
292 293 294 295 296 297 298 299 300
		dev_printk(KERN_DEBUG, &dev->dev, "%pR clipped to %pR\n",
				 &orig_res, res);

		return true;
	}

	return false;
}

301 302
void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { }

303 304
void __weak pcibios_bus_add_device(struct pci_dev *pdev) { }

Linus Torvalds's avatar
Linus Torvalds committed
305
/**
306
 * pci_bus_add_device - start driver for a single device
Linus Torvalds's avatar
Linus Torvalds committed
307 308
 * @dev: device to add
 *
309
 * This adds add sysfs entries and start device drivers
Linus Torvalds's avatar
Linus Torvalds committed
310
 */
311
void pci_bus_add_device(struct pci_dev *dev)
Linus Torvalds's avatar
Linus Torvalds committed
312
{
313
	int retval;
314

315 316 317 318
	/*
	 * Can not put in pci_device_add yet because resources
	 * are not assigned yet for some devices.
	 */
319
	pcibios_bus_add_device(dev);
320
	pci_fixup_device(pci_fixup_final, dev);
321
	pci_create_sysfs_dev_files(dev);
322
	pci_proc_attach_device(dev);
323
	pci_bridge_d3_update(dev);
Linus Torvalds's avatar
Linus Torvalds committed
324

325 326
	dev->match_driver = true;
	retval = device_attach(&dev->dev);
327
	if (retval < 0 && retval != -EPROBE_DEFER) {
328 329 330 331 332
		dev_warn(&dev->dev, "device attach failed (%d)\n", retval);
		pci_proc_detach_device(dev);
		pci_remove_sysfs_dev_files(dev);
		return;
	}
333

334
	dev->is_added = 1;
335
}
336
EXPORT_SYMBOL_GPL(pci_bus_add_device);
337

Linus Torvalds's avatar
Linus Torvalds committed
338
/**
339
 * pci_bus_add_devices - start driver for PCI devices
Linus Torvalds's avatar
Linus Torvalds committed
340 341
 * @bus: bus to check for new devices
 *
342
 * Start driver for PCI devices and add some sysfs entries.
Linus Torvalds's avatar
Linus Torvalds committed
343
 */
344
void pci_bus_add_devices(const struct pci_bus *bus)
Linus Torvalds's avatar
Linus Torvalds committed
345 346
{
	struct pci_dev *dev;
Yu Zhao's avatar
Yu Zhao committed
347
	struct pci_bus *child;
Linus Torvalds's avatar
Linus Torvalds committed
348 349

	list_for_each_entry(dev, &bus->devices, bus_list) {
350 351
		/* Skip already-added devices */
		if (dev->is_added)
Linus Torvalds's avatar
Linus Torvalds committed
352
			continue;
353
		pci_bus_add_device(dev);
Linus Torvalds's avatar
Linus Torvalds committed
354 355 356
	}

	list_for_each_entry(dev, &bus->devices, bus_list) {
357 358 359
		/* Skip if device attach failed */
		if (!dev->is_added)
			continue;
Yu Zhao's avatar
Yu Zhao committed
360
		child = dev->subordinate;
361 362
		if (child)
			pci_bus_add_devices(child);
Linus Torvalds's avatar
Linus Torvalds committed
363 364
	}
}
365
EXPORT_SYMBOL(pci_bus_add_devices);
Linus Torvalds's avatar
Linus Torvalds committed
366

367 368 369 370 371 372 373 374
/** 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.
375 376 377 378
 *
 *  We check the return of @cb each time. If it returns anything
 *  other than 0, we break out.
 *
379
 */
380
void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),
381 382 383 384 385
		  void *userdata)
{
	struct pci_dev *dev;
	struct pci_bus *bus;
	struct list_head *next;
386
	int retval;
387 388

	bus = top;
389
	down_read(&pci_bus_sem);
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
	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;

408 409 410
		retval = cb(dev, userdata);
		if (retval)
			break;
411
	}
412
	up_read(&pci_bus_sem);
413
}
414
EXPORT_SYMBOL_GPL(pci_walk_bus);
415

416 417 418 419 420 421 422 423 424 425 426 427 428 429
struct pci_bus *pci_bus_get(struct pci_bus *bus)
{
	if (bus)
		get_device(&bus->dev);
	return bus;
}
EXPORT_SYMBOL(pci_bus_get);

void pci_bus_put(struct pci_bus *bus)
{
	if (bus)
		put_device(&bus->dev);
}
EXPORT_SYMBOL(pci_bus_put);