diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/pci/hotplug/rpaphp_pci.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/pci/hotplug/rpaphp_pci.c')
-rw-r--r-- | drivers/pci/hotplug/rpaphp_pci.c | 538 |
1 files changed, 538 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c new file mode 100644 index 000000000000..d8305a935aab --- /dev/null +++ b/drivers/pci/hotplug/rpaphp_pci.c | |||
@@ -0,0 +1,538 @@ | |||
1 | /* | ||
2 | * PCI Hot Plug Controller Driver for RPA-compliant PPC64 platform. | ||
3 | * Copyright (C) 2003 Linda Xie <lxie@us.ibm.com> | ||
4 | * | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or (at | ||
10 | * your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, but | ||
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | ||
15 | * NON INFRINGEMENT. See the GNU General Public License for more | ||
16 | * details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | * | ||
22 | * Send feedback to <lxie@us.ibm.com> | ||
23 | * | ||
24 | */ | ||
25 | #include <linux/pci.h> | ||
26 | #include <asm/pci-bridge.h> | ||
27 | #include <asm/rtas.h> | ||
28 | #include <asm/machdep.h> | ||
29 | #include "../pci.h" /* for pci_add_new_bus */ | ||
30 | |||
31 | #include "rpaphp.h" | ||
32 | |||
33 | struct pci_dev *rpaphp_find_pci_dev(struct device_node *dn) | ||
34 | { | ||
35 | struct pci_dev *dev = NULL; | ||
36 | char bus_id[BUS_ID_SIZE]; | ||
37 | |||
38 | sprintf(bus_id, "%04x:%02x:%02x.%d", dn->phb->global_number, | ||
39 | dn->busno, PCI_SLOT(dn->devfn), PCI_FUNC(dn->devfn)); | ||
40 | for_each_pci_dev(dev) { | ||
41 | if (!strcmp(pci_name(dev), bus_id)) { | ||
42 | break; | ||
43 | } | ||
44 | } | ||
45 | return dev; | ||
46 | } | ||
47 | |||
48 | EXPORT_SYMBOL_GPL(rpaphp_find_pci_dev); | ||
49 | |||
50 | int rpaphp_claim_resource(struct pci_dev *dev, int resource) | ||
51 | { | ||
52 | struct resource *res = &dev->resource[resource]; | ||
53 | struct resource *root = pci_find_parent_resource(dev, res); | ||
54 | char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge"; | ||
55 | int err = -EINVAL; | ||
56 | |||
57 | if (root != NULL) { | ||
58 | err = request_resource(root, res); | ||
59 | } | ||
60 | |||
61 | if (err) { | ||
62 | err("PCI: %s region %d of %s %s [%lx:%lx]\n", | ||
63 | root ? "Address space collision on" : | ||
64 | "No parent found for", | ||
65 | resource, dtype, pci_name(dev), res->start, res->end); | ||
66 | } | ||
67 | return err; | ||
68 | } | ||
69 | |||
70 | EXPORT_SYMBOL_GPL(rpaphp_claim_resource); | ||
71 | |||
72 | static struct pci_dev *rpaphp_find_bridge_pdev(struct slot *slot) | ||
73 | { | ||
74 | return rpaphp_find_pci_dev(slot->dn); | ||
75 | } | ||
76 | |||
77 | static int rpaphp_get_sensor_state(struct slot *slot, int *state) | ||
78 | { | ||
79 | int rc; | ||
80 | int setlevel; | ||
81 | |||
82 | rc = rtas_get_sensor(DR_ENTITY_SENSE, slot->index, state); | ||
83 | |||
84 | if (rc < 0) { | ||
85 | if (rc == -EFAULT || rc == -EEXIST) { | ||
86 | dbg("%s: slot must be power up to get sensor-state\n", | ||
87 | __FUNCTION__); | ||
88 | |||
89 | /* some slots have to be powered up | ||
90 | * before get-sensor will succeed. | ||
91 | */ | ||
92 | rc = rtas_set_power_level(slot->power_domain, POWER_ON, | ||
93 | &setlevel); | ||
94 | if (rc < 0) { | ||
95 | dbg("%s: power on slot[%s] failed rc=%d.\n", | ||
96 | __FUNCTION__, slot->name, rc); | ||
97 | } else { | ||
98 | rc = rtas_get_sensor(DR_ENTITY_SENSE, | ||
99 | slot->index, state); | ||
100 | } | ||
101 | } else if (rc == -ENODEV) | ||
102 | info("%s: slot is unusable\n", __FUNCTION__); | ||
103 | else | ||
104 | err("%s failed to get sensor state\n", __FUNCTION__); | ||
105 | } | ||
106 | return rc; | ||
107 | } | ||
108 | |||
109 | /** | ||
110 | * get_pci_adapter_status - get the status of a slot | ||
111 | * | ||
112 | * 0-- slot is empty | ||
113 | * 1-- adapter is configured | ||
114 | * 2-- adapter is not configured | ||
115 | * 3-- not valid | ||
116 | */ | ||
117 | int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value) | ||
118 | { | ||
119 | int state, rc; | ||
120 | struct device_node *child_dn; | ||
121 | struct pci_dev *child_dev = NULL; | ||
122 | |||
123 | *value = NOT_VALID; | ||
124 | rc = rpaphp_get_sensor_state(slot, &state); | ||
125 | if (rc) | ||
126 | goto exit; | ||
127 | |||
128 | if ((state == EMPTY) || (slot->type == PHB)) { | ||
129 | dbg("slot is empty\n"); | ||
130 | *value = EMPTY; | ||
131 | } | ||
132 | else if (state == PRESENT) { | ||
133 | if (!is_init) { | ||
134 | /* at run-time slot->state can be changed by */ | ||
135 | /* config/unconfig adapter */ | ||
136 | *value = slot->state; | ||
137 | } else { | ||
138 | child_dn = slot->dn->child; | ||
139 | if (child_dn) | ||
140 | child_dev = rpaphp_find_pci_dev(child_dn); | ||
141 | |||
142 | if (child_dev) | ||
143 | *value = CONFIGURED; | ||
144 | else if (!child_dn) | ||
145 | dbg("%s: %s is not valid OFDT node\n", | ||
146 | __FUNCTION__, slot->dn->full_name); | ||
147 | else { | ||
148 | err("%s: can't find pdev of adapter in slot[%s]\n", | ||
149 | __FUNCTION__, slot->dn->full_name); | ||
150 | *value = NOT_CONFIGURED; | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | exit: | ||
155 | return rc; | ||
156 | } | ||
157 | |||
158 | /* Must be called before pci_bus_add_devices */ | ||
159 | static void | ||
160 | rpaphp_fixup_new_pci_devices(struct pci_bus *bus, int fix_bus) | ||
161 | { | ||
162 | struct pci_dev *dev; | ||
163 | |||
164 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
165 | /* | ||
166 | * Skip already-present devices (which are on the | ||
167 | * global device list.) | ||
168 | */ | ||
169 | if (list_empty(&dev->global_list)) { | ||
170 | int i; | ||
171 | |||
172 | /* Need to setup IOMMU tables */ | ||
173 | ppc_md.iommu_dev_setup(dev); | ||
174 | |||
175 | if(fix_bus) | ||
176 | pcibios_fixup_device_resources(dev, bus); | ||
177 | pci_read_irq_line(dev); | ||
178 | for (i = 0; i < PCI_NUM_RESOURCES; i++) { | ||
179 | struct resource *r = &dev->resource[i]; | ||
180 | |||
181 | if (r->parent || !r->start || !r->flags) | ||
182 | continue; | ||
183 | rpaphp_claim_resource(dev, i); | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | } | ||
188 | |||
189 | static int rpaphp_pci_config_bridge(struct pci_dev *dev); | ||
190 | |||
191 | /***************************************************************************** | ||
192 | rpaphp_pci_config_slot() will configure all devices under the | ||
193 | given slot->dn and return the the first pci_dev. | ||
194 | *****************************************************************************/ | ||
195 | static struct pci_dev * | ||
196 | rpaphp_pci_config_slot(struct device_node *dn, struct pci_bus *bus) | ||
197 | { | ||
198 | struct device_node *eads_first_child = dn->child; | ||
199 | struct pci_dev *dev = NULL; | ||
200 | int num; | ||
201 | |||
202 | dbg("Enter %s: dn=%s bus=%s\n", __FUNCTION__, dn->full_name, bus->name); | ||
203 | |||
204 | if (eads_first_child) { | ||
205 | /* pci_scan_slot should find all children of EADs */ | ||
206 | num = pci_scan_slot(bus, PCI_DEVFN(PCI_SLOT(eads_first_child->devfn), 0)); | ||
207 | if (num) { | ||
208 | rpaphp_fixup_new_pci_devices(bus, 1); | ||
209 | pci_bus_add_devices(bus); | ||
210 | } | ||
211 | dev = rpaphp_find_pci_dev(eads_first_child); | ||
212 | if (!dev) { | ||
213 | err("No new device found\n"); | ||
214 | return NULL; | ||
215 | } | ||
216 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) | ||
217 | rpaphp_pci_config_bridge(dev); | ||
218 | } | ||
219 | return dev; | ||
220 | } | ||
221 | |||
222 | static int rpaphp_pci_config_bridge(struct pci_dev *dev) | ||
223 | { | ||
224 | u8 sec_busno; | ||
225 | struct pci_bus *child_bus; | ||
226 | struct pci_dev *child_dev; | ||
227 | |||
228 | dbg("Enter %s: BRIDGE dev=%s\n", __FUNCTION__, pci_name(dev)); | ||
229 | |||
230 | /* get busno of downstream bus */ | ||
231 | pci_read_config_byte(dev, PCI_SECONDARY_BUS, &sec_busno); | ||
232 | |||
233 | /* add to children of PCI bridge dev->bus */ | ||
234 | child_bus = pci_add_new_bus(dev->bus, dev, sec_busno); | ||
235 | if (!child_bus) { | ||
236 | err("%s: could not add second bus\n", __FUNCTION__); | ||
237 | return -EIO; | ||
238 | } | ||
239 | sprintf(child_bus->name, "PCI Bus #%02x", child_bus->number); | ||
240 | /* do pci_scan_child_bus */ | ||
241 | pci_scan_child_bus(child_bus); | ||
242 | |||
243 | list_for_each_entry(child_dev, &child_bus->devices, bus_list) { | ||
244 | eeh_add_device_late(child_dev); | ||
245 | } | ||
246 | |||
247 | /* fixup new pci devices without touching bus struct */ | ||
248 | rpaphp_fixup_new_pci_devices(child_bus, 0); | ||
249 | |||
250 | /* Make the discovered devices available */ | ||
251 | pci_bus_add_devices(child_bus); | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static void enable_eeh(struct device_node *dn) | ||
256 | { | ||
257 | struct device_node *sib; | ||
258 | |||
259 | for (sib = dn->child; sib; sib = sib->sibling) | ||
260 | enable_eeh(sib); | ||
261 | eeh_add_device_early(dn); | ||
262 | return; | ||
263 | |||
264 | } | ||
265 | |||
266 | static void print_slot_pci_funcs(struct slot *slot) | ||
267 | { | ||
268 | struct pci_dev *dev; | ||
269 | |||
270 | if (slot->dev_type == PCI_DEV) { | ||
271 | dbg("%s: pci_devs of slot[%s]\n", __FUNCTION__, slot->name); | ||
272 | list_for_each_entry (dev, slot->dev.pci_devs, bus_list) | ||
273 | dbg("\t%s\n", pci_name(dev)); | ||
274 | } | ||
275 | return; | ||
276 | } | ||
277 | |||
278 | static int rpaphp_config_pci_adapter(struct slot *slot) | ||
279 | { | ||
280 | struct pci_bus *pci_bus; | ||
281 | struct pci_dev *dev; | ||
282 | int rc = -ENODEV; | ||
283 | |||
284 | dbg("Entry %s: slot[%s]\n", __FUNCTION__, slot->name); | ||
285 | |||
286 | if (slot->bridge) { | ||
287 | |||
288 | pci_bus = slot->bridge->subordinate; | ||
289 | if (!pci_bus) { | ||
290 | err("%s: can't find bus structure\n", __FUNCTION__); | ||
291 | goto exit; | ||
292 | } | ||
293 | enable_eeh(slot->dn); | ||
294 | dev = rpaphp_pci_config_slot(slot->dn, pci_bus); | ||
295 | if (!dev) { | ||
296 | err("%s: can't find any devices.\n", __FUNCTION__); | ||
297 | goto exit; | ||
298 | } | ||
299 | print_slot_pci_funcs(slot); | ||
300 | rc = 0; | ||
301 | } else { | ||
302 | /* slot is not enabled */ | ||
303 | err("slot doesn't have pci_dev structure\n"); | ||
304 | } | ||
305 | exit: | ||
306 | dbg("Exit %s: rc=%d\n", __FUNCTION__, rc); | ||
307 | return rc; | ||
308 | } | ||
309 | |||
310 | static void rpaphp_eeh_remove_bus_device(struct pci_dev *dev) | ||
311 | { | ||
312 | eeh_remove_device(dev); | ||
313 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) { | ||
314 | struct pci_bus *bus = dev->subordinate; | ||
315 | struct list_head *ln; | ||
316 | if (!bus) | ||
317 | return; | ||
318 | for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) { | ||
319 | struct pci_dev *pdev = pci_dev_b(ln); | ||
320 | if (pdev) | ||
321 | rpaphp_eeh_remove_bus_device(pdev); | ||
322 | } | ||
323 | |||
324 | } | ||
325 | return; | ||
326 | } | ||
327 | |||
328 | int rpaphp_unconfig_pci_adapter(struct slot *slot) | ||
329 | { | ||
330 | struct pci_dev *dev; | ||
331 | int retval = 0; | ||
332 | |||
333 | list_for_each_entry(dev, slot->dev.pci_devs, bus_list) | ||
334 | rpaphp_eeh_remove_bus_device(dev); | ||
335 | |||
336 | pci_remove_behind_bridge(slot->bridge); | ||
337 | slot->state = NOT_CONFIGURED; | ||
338 | info("%s: devices in slot[%s] unconfigured.\n", __FUNCTION__, | ||
339 | slot->name); | ||
340 | return retval; | ||
341 | } | ||
342 | |||
343 | static int setup_pci_hotplug_slot_info(struct slot *slot) | ||
344 | { | ||
345 | dbg("%s Initilize the PCI slot's hotplug->info structure ...\n", | ||
346 | __FUNCTION__); | ||
347 | rpaphp_get_power_status(slot, &slot->hotplug_slot->info->power_status); | ||
348 | rpaphp_get_pci_adapter_status(slot, 1, | ||
349 | &slot->hotplug_slot->info-> | ||
350 | adapter_status); | ||
351 | if (slot->hotplug_slot->info->adapter_status == NOT_VALID) { | ||
352 | err("%s: NOT_VALID: skip dn->full_name=%s\n", | ||
353 | __FUNCTION__, slot->dn->full_name); | ||
354 | return -EINVAL; | ||
355 | } | ||
356 | return 0; | ||
357 | } | ||
358 | |||
359 | static int set_phb_slot_name(struct slot *slot) | ||
360 | { | ||
361 | struct device_node *dn; | ||
362 | struct pci_controller *phb; | ||
363 | struct pci_bus *bus; | ||
364 | |||
365 | dn = slot->dn; | ||
366 | if (!dn) { | ||
367 | return -EINVAL; | ||
368 | } | ||
369 | phb = dn->phb; | ||
370 | if (!phb) { | ||
371 | return -EINVAL; | ||
372 | } | ||
373 | bus = phb->bus; | ||
374 | if (!bus) { | ||
375 | return -EINVAL; | ||
376 | } | ||
377 | |||
378 | sprintf(slot->name, "%04x:%02x:%02x.%x", pci_domain_nr(bus), | ||
379 | bus->number, 0, 0); | ||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static int setup_pci_slot(struct slot *slot) | ||
384 | { | ||
385 | struct pci_bus *bus; | ||
386 | int rc; | ||
387 | |||
388 | if (slot->type == PHB) { | ||
389 | rc = set_phb_slot_name(slot); | ||
390 | if (rc < 0) { | ||
391 | err("%s: failed to set phb slot name\n", __FUNCTION__); | ||
392 | goto exit_rc; | ||
393 | } | ||
394 | } else { | ||
395 | slot->bridge = rpaphp_find_bridge_pdev(slot); | ||
396 | if (!slot->bridge) { | ||
397 | /* slot being added doesn't have pci_dev yet */ | ||
398 | err("%s: no pci_dev for bridge dn %s\n", | ||
399 | __FUNCTION__, slot->name); | ||
400 | goto exit_rc; | ||
401 | } | ||
402 | |||
403 | bus = slot->bridge->subordinate; | ||
404 | if (!bus) | ||
405 | goto exit_rc; | ||
406 | slot->dev.pci_devs = &bus->devices; | ||
407 | |||
408 | dbg("%s set slot->name to %s\n", __FUNCTION__, | ||
409 | pci_name(slot->bridge)); | ||
410 | strcpy(slot->name, pci_name(slot->bridge)); | ||
411 | } | ||
412 | |||
413 | /* find slot's pci_dev if it's not empty */ | ||
414 | if (slot->hotplug_slot->info->adapter_status == EMPTY) { | ||
415 | slot->state = EMPTY; /* slot is empty */ | ||
416 | } else { | ||
417 | /* slot is occupied */ | ||
418 | if (!(slot->dn->child)) { | ||
419 | /* non-empty slot has to have child */ | ||
420 | err("%s: slot[%s]'s device_node doesn't have child for adapter\n", | ||
421 | __FUNCTION__, slot->name); | ||
422 | goto exit_rc; | ||
423 | } | ||
424 | |||
425 | if (slot->hotplug_slot->info->adapter_status == NOT_CONFIGURED) { | ||
426 | dbg("%s CONFIGURING pci adapter in slot[%s]\n", | ||
427 | __FUNCTION__, slot->name); | ||
428 | if (rpaphp_config_pci_adapter(slot)) { | ||
429 | err("%s: CONFIG pci adapter failed\n", __FUNCTION__); | ||
430 | goto exit_rc; | ||
431 | } | ||
432 | |||
433 | } else if (slot->hotplug_slot->info->adapter_status != CONFIGURED) { | ||
434 | err("%s: slot[%s]'s adapter_status is NOT_VALID.\n", | ||
435 | __FUNCTION__, slot->name); | ||
436 | goto exit_rc; | ||
437 | } | ||
438 | print_slot_pci_funcs(slot); | ||
439 | if (!list_empty(slot->dev.pci_devs)) { | ||
440 | slot->state = CONFIGURED; | ||
441 | } else { | ||
442 | /* DLPAR add as opposed to | ||
443 | * boot time */ | ||
444 | slot->state = NOT_CONFIGURED; | ||
445 | } | ||
446 | } | ||
447 | return 0; | ||
448 | exit_rc: | ||
449 | dealloc_slot_struct(slot); | ||
450 | return -EINVAL; | ||
451 | } | ||
452 | |||
453 | int register_pci_slot(struct slot *slot) | ||
454 | { | ||
455 | int rc = -EINVAL; | ||
456 | |||
457 | slot->dev_type = PCI_DEV; | ||
458 | if ((slot->type == EMBEDDED) || (slot->type == PHB)) | ||
459 | slot->removable = 0; | ||
460 | else | ||
461 | slot->removable = 1; | ||
462 | if (setup_pci_hotplug_slot_info(slot)) | ||
463 | goto exit_rc; | ||
464 | if (setup_pci_slot(slot)) | ||
465 | goto exit_rc; | ||
466 | rc = register_slot(slot); | ||
467 | exit_rc: | ||
468 | return rc; | ||
469 | } | ||
470 | |||
471 | int rpaphp_enable_pci_slot(struct slot *slot) | ||
472 | { | ||
473 | int retval = 0, state; | ||
474 | |||
475 | retval = rpaphp_get_sensor_state(slot, &state); | ||
476 | if (retval) | ||
477 | goto exit; | ||
478 | dbg("%s: sensor state[%d]\n", __FUNCTION__, state); | ||
479 | /* if slot is not empty, enable the adapter */ | ||
480 | if (state == PRESENT) { | ||
481 | dbg("%s : slot[%s] is occupied.\n", __FUNCTION__, slot->name); | ||
482 | retval = rpaphp_config_pci_adapter(slot); | ||
483 | if (!retval) { | ||
484 | slot->state = CONFIGURED; | ||
485 | dbg("%s: PCI devices in slot[%s] has been configured\n", | ||
486 | __FUNCTION__, slot->name); | ||
487 | } else { | ||
488 | slot->state = NOT_CONFIGURED; | ||
489 | dbg("%s: no pci_dev struct for adapter in slot[%s]\n", | ||
490 | __FUNCTION__, slot->name); | ||
491 | } | ||
492 | } else if (state == EMPTY) { | ||
493 | dbg("%s : slot[%s] is empty\n", __FUNCTION__, slot->name); | ||
494 | slot->state = EMPTY; | ||
495 | } else { | ||
496 | err("%s: slot[%s] is in invalid state\n", __FUNCTION__, | ||
497 | slot->name); | ||
498 | slot->state = NOT_VALID; | ||
499 | retval = -EINVAL; | ||
500 | } | ||
501 | exit: | ||
502 | dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval); | ||
503 | return retval; | ||
504 | } | ||
505 | |||
506 | struct hotplug_slot *rpaphp_find_hotplug_slot(struct pci_dev *dev) | ||
507 | { | ||
508 | struct list_head *tmp, *n; | ||
509 | struct slot *slot; | ||
510 | |||
511 | list_for_each_safe(tmp, n, &rpaphp_slot_head) { | ||
512 | struct pci_bus *bus; | ||
513 | struct list_head *ln; | ||
514 | |||
515 | slot = list_entry(tmp, struct slot, rpaphp_slot_list); | ||
516 | if (slot->bridge == NULL) { | ||
517 | if (slot->dev_type == PCI_DEV) { | ||
518 | printk(KERN_WARNING "PCI slot missing bridge %s %s \n", | ||
519 | slot->name, slot->location); | ||
520 | } | ||
521 | continue; | ||
522 | } | ||
523 | |||
524 | bus = slot->bridge->subordinate; | ||
525 | if (!bus) { | ||
526 | continue; /* should never happen? */ | ||
527 | } | ||
528 | for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) { | ||
529 | struct pci_dev *pdev = pci_dev_b(ln); | ||
530 | if (pdev == dev) | ||
531 | return slot->hotplug_slot; | ||
532 | } | ||
533 | } | ||
534 | |||
535 | return NULL; | ||
536 | } | ||
537 | |||
538 | EXPORT_SYMBOL_GPL(rpaphp_find_hotplug_slot); | ||