Blame view

drivers/pci/bus.c 7.16 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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>
5a0e3ad6a   Tejun Heo   include cleanup: ...
17
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
  
  #include "pci.h"
2fe2abf89   Bjorn Helgaas   PCI: augment bus ...
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
  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
  ", 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++)
7736a05a3   Stephen Hemminger   PCI: sparse warni...
59
  		bus->resource[i] = NULL;
2fe2abf89   Bjorn Helgaas   PCI: augment bus ...
60
61
62
63
64
65
  
  	list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) {
  		list_del(&bus_res->list);
  		kfree(bus_res);
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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,
e31dd6e45   Greg Kroah-Hartman   [PATCH] 64bit res...
83
84
  		resource_size_t size, resource_size_t align,
  		resource_size_t min, unsigned int type_mask,
b26b2d494   Dominik Brodowski   resource/PCI: ali...
85
  		resource_size_t (*alignf)(void *,
3b7a17fcd   Dominik Brodowski   resource/PCI: mar...
86
  					  const struct resource *,
b26b2d494   Dominik Brodowski   resource/PCI: ali...
87
88
  					  resource_size_t,
  					  resource_size_t),
e31dd6e45   Greg Kroah-Hartman   [PATCH] 64bit res...
89
  		void *alignf_data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
90
  {
6db45b76e   Bjorn Helgaas   Revert "PCI: allo...
91
  	int i, ret = -ENOMEM;
89a74eccc   Bjorn Helgaas   PCI: add pci_bus_...
92
  	struct resource *r;
1f82de10d   Yinghai Lu   PCI/x86: don't as...
93
  	resource_size_t max = -1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
94
95
  
  	type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
1f82de10d   Yinghai Lu   PCI/x86: don't as...
96
97
98
  	/* don't allocate too high if the pref mem doesn't support 64bit*/
  	if (!(res->flags & IORESOURCE_MEM_64))
  		max = PCIBIOS_MAX_MEM_32;
6db45b76e   Bjorn Helgaas   Revert "PCI: allo...
99
100
101
  	pci_bus_for_each_resource(bus, r, i) {
  		if (!r)
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
102
103
104
105
106
107
108
109
110
111
112
  		/* 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.. */
688d19182   Linus Torvalds   pci: make bus res...
113
114
  		ret = allocate_resource(r, res, size,
  					r->start ? : min,
1f82de10d   Yinghai Lu   PCI/x86: don't as...
115
  					max, align,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
116
117
118
119
120
121
122
123
  					alignf, alignf_data);
  		if (ret == 0)
  			break;
  	}
  	return ret;
  }
  
  /**
3fa16fdb4   Yu Zhao   PCI: cleanup pci_...
124
   * pci_bus_add_device - add a single device
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
127
128
129
   * @dev: device to add
   *
   * This adds a single pci device to the global
   * device list and adds sysfs and procfs entries
   */
96bde06a2   Sam Ravnborg   pci: do not mark ...
130
  int pci_bus_add_device(struct pci_dev *dev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
  {
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
132
133
134
135
  	int retval;
  	retval = device_add(&dev->dev);
  	if (retval)
  		return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136

8a1bc9013   Greg Kroah-Hartman   PCI: add is_added...
137
  	dev->is_added = 1;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
139
  	pci_proc_attach_device(dev);
  	pci_create_sysfs_dev_files(dev);
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
140
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
  }
  
  /**
876e501ab   Yu Zhao   PCI: factor pci_b...
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
   * 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;
876e501ab   Yu Zhao   PCI: factor pci_b...
161
162
163
164
165
166
167
  	/* Create legacy_io and legacy_mem files for this bus */
  	pci_create_legacy_files(bus);
  
  	return retval;
  }
  
  /**
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
170
171
172
173
174
175
176
177
178
   * 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.
   */
c48f1670f   akpm@linux-foundation.org   PCI: constify pci...
179
  void pci_bus_add_devices(const struct pci_bus *bus)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
180
181
  {
  	struct pci_dev *dev;
3fa16fdb4   Yu Zhao   PCI: cleanup pci_...
182
  	struct pci_bus *child;
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
183
  	int retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
  
  	list_for_each_entry(dev, &bus->devices, bus_list) {
8a1bc9013   Greg Kroah-Hartman   PCI: add is_added...
186
187
  		/* Skip already-added devices */
  		if (dev->is_added)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
  			continue;
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
189
190
191
192
  		retval = pci_bus_add_device(dev);
  		if (retval)
  			dev_err(&dev->dev, "Error adding device, continuing
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
193
194
195
  	}
  
  	list_for_each_entry(dev, &bus->devices, bus_list) {
8a1bc9013   Greg Kroah-Hartman   PCI: add is_added...
196
  		BUG_ON(!dev->is_added);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197

3fa16fdb4   Yu Zhao   PCI: cleanup pci_...
198
  		child = dev->subordinate;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
199
200
201
202
  		/*
  		 * If there is an unattached subordinate bus, attach
  		 * it and then scan for unattached PCI devices.
  		 */
3fa16fdb4   Yu Zhao   PCI: cleanup pci_...
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
  		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;
876e501ab   Yu Zhao   PCI: factor pci_b...
218
  		retval = pci_bus_add_child(child);
3fa16fdb4   Yu Zhao   PCI: cleanup pci_...
219
  		if (retval)
876e501ab   Yu Zhao   PCI: factor pci_b...
220
221
  			dev_err(&dev->dev, "Error adding bus, continuing
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
222
223
224
225
226
227
  	}
  }
  
  void pci_enable_bridges(struct pci_bus *bus)
  {
  	struct pci_dev *dev;
95a629657   Greg Kroah-Hartman   [PATCH] PCI: star...
228
  	int retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229
230
231
  
  	list_for_each_entry(dev, &bus->devices, bus_list) {
  		if (dev->subordinate) {
296ccb086   Yuji Shimada   PCI: Setup disabl...
232
  			if (!pci_is_enabled(dev)) {
9dd90cafa   Alex Chiang   PCI: do not enabl...
233
  				retval = pci_enable_device(dev);
2eb5ebd36   Junchang Wang   PCI: check return...
234
235
236
  				if (retval)
  					dev_err(&dev->dev, "Error enabling bridge (%d), continuing
  ", retval);
9dd90cafa   Alex Chiang   PCI: do not enabl...
237
238
  				pci_set_master(dev);
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
239
240
241
242
  			pci_enable_bridges(dev->subordinate);
  		}
  	}
  }
cecf4864c   Paul Mackerras   [PATCH] PCI: Add ...
243
244
245
246
247
248
249
250
  /** 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.
70298c6e6   Zhang, Yanmin   PCI AER: support ...
251
252
253
254
   *
   *  We check the return of @cb each time. If it returns anything
   *  other than 0, we break out.
   *
cecf4864c   Paul Mackerras   [PATCH] PCI: Add ...
255
   */
70298c6e6   Zhang, Yanmin   PCI AER: support ...
256
  void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *),
cecf4864c   Paul Mackerras   [PATCH] PCI: Add ...
257
258
259
260
261
  		  void *userdata)
  {
  	struct pci_dev *dev;
  	struct pci_bus *bus;
  	struct list_head *next;
70298c6e6   Zhang, Yanmin   PCI AER: support ...
262
  	int retval;
cecf4864c   Paul Mackerras   [PATCH] PCI: Add ...
263
264
  
  	bus = top;
d71374daf   Zhang Yanmin   [PATCH] PCI: fix ...
265
  	down_read(&pci_bus_sem);
cecf4864c   Paul Mackerras   [PATCH] PCI: Add ...
266
267
268
269
270
271
272
273
274
275
276
  	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);
cecf4864c   Paul Mackerras   [PATCH] PCI: Add ...
277
278
279
280
281
282
  		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;
cecf4864c   Paul Mackerras   [PATCH] PCI: Add ...
283

d71374daf   Zhang Yanmin   [PATCH] PCI: fix ...
284
  		/* Run device routines with the device locked */
8e9394ce2   Greg Kroah-Hartman   Driver core: crea...
285
  		device_lock(&dev->dev);
70298c6e6   Zhang, Yanmin   PCI AER: support ...
286
  		retval = cb(dev, userdata);
8e9394ce2   Greg Kroah-Hartman   Driver core: crea...
287
  		device_unlock(&dev->dev);
70298c6e6   Zhang, Yanmin   PCI AER: support ...
288
289
  		if (retval)
  			break;
cecf4864c   Paul Mackerras   [PATCH] PCI: Add ...
290
  	}
d71374daf   Zhang Yanmin   [PATCH] PCI: fix ...
291
  	up_read(&pci_bus_sem);
cecf4864c   Paul Mackerras   [PATCH] PCI: Add ...
292
  }
7c94def89   Konrad Rzeszutek Wilk   x86/PCI: Export p...
293
  EXPORT_SYMBOL_GPL(pci_walk_bus);
cecf4864c   Paul Mackerras   [PATCH] PCI: Add ...
294

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
296
297
298
  EXPORT_SYMBOL(pci_bus_alloc_resource);
  EXPORT_SYMBOL_GPL(pci_bus_add_device);
  EXPORT_SYMBOL(pci_bus_add_devices);
  EXPORT_SYMBOL(pci_enable_bridges);