diff options
Diffstat (limited to 'drivers/pci/hotplug/rpaphp_pci.c')
-rw-r--r-- | drivers/pci/hotplug/rpaphp_pci.c | 297 |
1 files changed, 113 insertions, 184 deletions
diff --git a/drivers/pci/hotplug/rpaphp_pci.c b/drivers/pci/hotplug/rpaphp_pci.c index d8305a935aab..17a0279ebcb9 100644 --- a/drivers/pci/hotplug/rpaphp_pci.c +++ b/drivers/pci/hotplug/rpaphp_pci.c | |||
@@ -30,22 +30,33 @@ | |||
30 | 30 | ||
31 | #include "rpaphp.h" | 31 | #include "rpaphp.h" |
32 | 32 | ||
33 | struct pci_dev *rpaphp_find_pci_dev(struct device_node *dn) | 33 | static struct pci_bus *find_bus_among_children(struct pci_bus *bus, |
34 | struct device_node *dn) | ||
34 | { | 35 | { |
35 | struct pci_dev *dev = NULL; | 36 | struct pci_bus *child = NULL; |
36 | char bus_id[BUS_ID_SIZE]; | 37 | struct list_head *tmp; |
38 | struct device_node *busdn; | ||
39 | |||
40 | busdn = pci_bus_to_OF_node(bus); | ||
41 | if (busdn == dn) | ||
42 | return bus; | ||
37 | 43 | ||
38 | sprintf(bus_id, "%04x:%02x:%02x.%d", dn->phb->global_number, | 44 | list_for_each(tmp, &bus->children) { |
39 | dn->busno, PCI_SLOT(dn->devfn), PCI_FUNC(dn->devfn)); | 45 | child = find_bus_among_children(pci_bus_b(tmp), dn); |
40 | for_each_pci_dev(dev) { | 46 | if (child) |
41 | if (!strcmp(pci_name(dev), bus_id)) { | ||
42 | break; | 47 | break; |
43 | } | ||
44 | } | 48 | } |
45 | return dev; | 49 | return child; |
46 | } | 50 | } |
47 | 51 | ||
48 | EXPORT_SYMBOL_GPL(rpaphp_find_pci_dev); | 52 | struct pci_bus *rpaphp_find_pci_bus(struct device_node *dn) |
53 | { | ||
54 | if (!dn->phb || !dn->phb->bus) | ||
55 | return NULL; | ||
56 | |||
57 | return find_bus_among_children(dn->phb->bus, dn); | ||
58 | } | ||
59 | EXPORT_SYMBOL_GPL(rpaphp_find_pci_bus); | ||
49 | 60 | ||
50 | int rpaphp_claim_resource(struct pci_dev *dev, int resource) | 61 | int rpaphp_claim_resource(struct pci_dev *dev, int resource) |
51 | { | 62 | { |
@@ -69,11 +80,6 @@ int rpaphp_claim_resource(struct pci_dev *dev, int resource) | |||
69 | 80 | ||
70 | EXPORT_SYMBOL_GPL(rpaphp_claim_resource); | 81 | EXPORT_SYMBOL_GPL(rpaphp_claim_resource); |
71 | 82 | ||
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) | 83 | static int rpaphp_get_sensor_state(struct slot *slot, int *state) |
78 | { | 84 | { |
79 | int rc; | 85 | int rc; |
@@ -116,39 +122,27 @@ static int rpaphp_get_sensor_state(struct slot *slot, int *state) | |||
116 | */ | 122 | */ |
117 | int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value) | 123 | int rpaphp_get_pci_adapter_status(struct slot *slot, int is_init, u8 * value) |
118 | { | 124 | { |
125 | struct pci_bus *bus; | ||
119 | int state, rc; | 126 | int state, rc; |
120 | struct device_node *child_dn; | ||
121 | struct pci_dev *child_dev = NULL; | ||
122 | 127 | ||
123 | *value = NOT_VALID; | 128 | *value = NOT_VALID; |
124 | rc = rpaphp_get_sensor_state(slot, &state); | 129 | rc = rpaphp_get_sensor_state(slot, &state); |
125 | if (rc) | 130 | if (rc) |
126 | goto exit; | 131 | goto exit; |
127 | 132 | ||
128 | if ((state == EMPTY) || (slot->type == PHB)) { | 133 | if (state == EMPTY) |
129 | dbg("slot is empty\n"); | ||
130 | *value = EMPTY; | 134 | *value = EMPTY; |
131 | } | ||
132 | else if (state == PRESENT) { | 135 | else if (state == PRESENT) { |
133 | if (!is_init) { | 136 | if (!is_init) { |
134 | /* at run-time slot->state can be changed by */ | 137 | /* at run-time slot->state can be changed by */ |
135 | /* config/unconfig adapter */ | 138 | /* config/unconfig adapter */ |
136 | *value = slot->state; | 139 | *value = slot->state; |
137 | } else { | 140 | } else { |
138 | child_dn = slot->dn->child; | 141 | bus = rpaphp_find_pci_bus(slot->dn); |
139 | if (child_dn) | 142 | if (bus && !list_empty(&bus->devices)) |
140 | child_dev = rpaphp_find_pci_dev(child_dn); | 143 | *value = CONFIGURED; |
141 | 144 | else | |
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; | 145 | *value = NOT_CONFIGURED; |
151 | } | ||
152 | } | 146 | } |
153 | } | 147 | } |
154 | exit: | 148 | exit: |
@@ -186,39 +180,6 @@ rpaphp_fixup_new_pci_devices(struct pci_bus *bus, int fix_bus) | |||
186 | } | 180 | } |
187 | } | 181 | } |
188 | 182 | ||
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) | 183 | static int rpaphp_pci_config_bridge(struct pci_dev *dev) |
223 | { | 184 | { |
224 | u8 sec_busno; | 185 | u8 sec_busno; |
@@ -252,6 +213,42 @@ static int rpaphp_pci_config_bridge(struct pci_dev *dev) | |||
252 | return 0; | 213 | return 0; |
253 | } | 214 | } |
254 | 215 | ||
216 | /***************************************************************************** | ||
217 | rpaphp_pci_config_slot() will configure all devices under the | ||
218 | given slot->dn and return the the first pci_dev. | ||
219 | *****************************************************************************/ | ||
220 | static struct pci_dev * | ||
221 | rpaphp_pci_config_slot(struct pci_bus *bus) | ||
222 | { | ||
223 | struct device_node *dn = pci_bus_to_OF_node(bus); | ||
224 | struct pci_dev *dev = NULL; | ||
225 | int slotno; | ||
226 | int num; | ||
227 | |||
228 | dbg("Enter %s: dn=%s bus=%s\n", __FUNCTION__, dn->full_name, bus->name); | ||
229 | if (!dn || !dn->child) | ||
230 | return NULL; | ||
231 | |||
232 | slotno = PCI_SLOT(dn->child->devfn); | ||
233 | |||
234 | /* pci_scan_slot should find all children */ | ||
235 | num = pci_scan_slot(bus, PCI_DEVFN(slotno, 0)); | ||
236 | if (num) { | ||
237 | rpaphp_fixup_new_pci_devices(bus, 1); | ||
238 | pci_bus_add_devices(bus); | ||
239 | } | ||
240 | if (list_empty(&bus->devices)) { | ||
241 | err("%s: No new device found\n", __FUNCTION__); | ||
242 | return NULL; | ||
243 | } | ||
244 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
245 | if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) | ||
246 | rpaphp_pci_config_bridge(dev); | ||
247 | } | ||
248 | |||
249 | return dev; | ||
250 | } | ||
251 | |||
255 | static void enable_eeh(struct device_node *dn) | 252 | static void enable_eeh(struct device_node *dn) |
256 | { | 253 | { |
257 | struct device_node *sib; | 254 | struct device_node *sib; |
@@ -263,49 +260,44 @@ static void enable_eeh(struct device_node *dn) | |||
263 | 260 | ||
264 | } | 261 | } |
265 | 262 | ||
266 | static void print_slot_pci_funcs(struct slot *slot) | 263 | static void print_slot_pci_funcs(struct pci_bus *bus) |
267 | { | 264 | { |
265 | struct device_node *dn; | ||
268 | struct pci_dev *dev; | 266 | struct pci_dev *dev; |
269 | 267 | ||
270 | if (slot->dev_type == PCI_DEV) { | 268 | dn = pci_bus_to_OF_node(bus); |
271 | dbg("%s: pci_devs of slot[%s]\n", __FUNCTION__, slot->name); | 269 | if (!dn) |
272 | list_for_each_entry (dev, slot->dev.pci_devs, bus_list) | 270 | return; |
273 | dbg("\t%s\n", pci_name(dev)); | 271 | |
274 | } | 272 | dbg("%s: pci_devs of slot[%s]\n", __FUNCTION__, dn->full_name); |
273 | list_for_each_entry (dev, &bus->devices, bus_list) | ||
274 | dbg("\t%s\n", pci_name(dev)); | ||
275 | return; | 275 | return; |
276 | } | 276 | } |
277 | 277 | ||
278 | static int rpaphp_config_pci_adapter(struct slot *slot) | 278 | int rpaphp_config_pci_adapter(struct pci_bus *bus) |
279 | { | 279 | { |
280 | struct pci_bus *pci_bus; | 280 | struct device_node *dn = pci_bus_to_OF_node(bus); |
281 | struct pci_dev *dev; | 281 | struct pci_dev *dev; |
282 | int rc = -ENODEV; | 282 | int rc = -ENODEV; |
283 | 283 | ||
284 | dbg("Entry %s: slot[%s]\n", __FUNCTION__, slot->name); | 284 | dbg("Entry %s: slot[%s]\n", __FUNCTION__, dn->full_name); |
285 | 285 | if (!dn) | |
286 | if (slot->bridge) { | 286 | goto exit; |
287 | 287 | ||
288 | pci_bus = slot->bridge->subordinate; | 288 | enable_eeh(dn); |
289 | if (!pci_bus) { | 289 | dev = rpaphp_pci_config_slot(bus); |
290 | err("%s: can't find bus structure\n", __FUNCTION__); | 290 | if (!dev) { |
291 | goto exit; | 291 | err("%s: can't find any devices.\n", __FUNCTION__); |
292 | } | 292 | goto exit; |
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 | } | 293 | } |
294 | print_slot_pci_funcs(bus); | ||
295 | rc = 0; | ||
305 | exit: | 296 | exit: |
306 | dbg("Exit %s: rc=%d\n", __FUNCTION__, rc); | 297 | dbg("Exit %s: rc=%d\n", __FUNCTION__, rc); |
307 | return rc; | 298 | return rc; |
308 | } | 299 | } |
300 | EXPORT_SYMBOL_GPL(rpaphp_config_pci_adapter); | ||
309 | 301 | ||
310 | static void rpaphp_eeh_remove_bus_device(struct pci_dev *dev) | 302 | static void rpaphp_eeh_remove_bus_device(struct pci_dev *dev) |
311 | { | 303 | { |
@@ -327,13 +319,14 @@ static void rpaphp_eeh_remove_bus_device(struct pci_dev *dev) | |||
327 | 319 | ||
328 | int rpaphp_unconfig_pci_adapter(struct slot *slot) | 320 | int rpaphp_unconfig_pci_adapter(struct slot *slot) |
329 | { | 321 | { |
330 | struct pci_dev *dev; | 322 | struct pci_dev *dev, *tmp; |
331 | int retval = 0; | 323 | int retval = 0; |
332 | 324 | ||
333 | list_for_each_entry(dev, slot->dev.pci_devs, bus_list) | 325 | list_for_each_entry_safe(dev, tmp, slot->pci_devs, bus_list) { |
334 | rpaphp_eeh_remove_bus_device(dev); | 326 | rpaphp_eeh_remove_bus_device(dev); |
327 | pci_remove_bus_device(dev); | ||
328 | } | ||
335 | 329 | ||
336 | pci_remove_behind_bridge(slot->bridge); | ||
337 | slot->state = NOT_CONFIGURED; | 330 | slot->state = NOT_CONFIGURED; |
338 | info("%s: devices in slot[%s] unconfigured.\n", __FUNCTION__, | 331 | info("%s: devices in slot[%s] unconfigured.\n", __FUNCTION__, |
339 | slot->name); | 332 | slot->name); |
@@ -356,66 +349,41 @@ static int setup_pci_hotplug_slot_info(struct slot *slot) | |||
356 | return 0; | 349 | return 0; |
357 | } | 350 | } |
358 | 351 | ||
359 | static int set_phb_slot_name(struct slot *slot) | 352 | static void set_slot_name(struct slot *slot) |
360 | { | 353 | { |
361 | struct device_node *dn; | 354 | struct pci_bus *bus = slot->bus; |
362 | struct pci_controller *phb; | 355 | struct pci_dev *bridge; |
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 | 356 | ||
378 | sprintf(slot->name, "%04x:%02x:%02x.%x", pci_domain_nr(bus), | 357 | bridge = bus->self; |
379 | bus->number, 0, 0); | 358 | if (bridge) |
380 | return 0; | 359 | strcpy(slot->name, pci_name(bridge)); |
360 | else | ||
361 | sprintf(slot->name, "%04x:%02x:00.0", pci_domain_nr(bus), | ||
362 | bus->number); | ||
381 | } | 363 | } |
382 | 364 | ||
383 | static int setup_pci_slot(struct slot *slot) | 365 | static int setup_pci_slot(struct slot *slot) |
384 | { | 366 | { |
367 | struct device_node *dn = slot->dn; | ||
385 | struct pci_bus *bus; | 368 | struct pci_bus *bus; |
386 | int rc; | ||
387 | 369 | ||
388 | if (slot->type == PHB) { | 370 | BUG_ON(!dn); |
389 | rc = set_phb_slot_name(slot); | 371 | bus = rpaphp_find_pci_bus(dn); |
390 | if (rc < 0) { | 372 | if (!bus) { |
391 | err("%s: failed to set phb slot name\n", __FUNCTION__); | 373 | err("%s: no pci_bus for dn %s\n", __FUNCTION__, dn->full_name); |
392 | goto exit_rc; | 374 | 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 | } | 375 | } |
412 | 376 | ||
377 | slot->bus = bus; | ||
378 | slot->pci_devs = &bus->devices; | ||
379 | set_slot_name(slot); | ||
380 | |||
413 | /* find slot's pci_dev if it's not empty */ | 381 | /* find slot's pci_dev if it's not empty */ |
414 | if (slot->hotplug_slot->info->adapter_status == EMPTY) { | 382 | if (slot->hotplug_slot->info->adapter_status == EMPTY) { |
415 | slot->state = EMPTY; /* slot is empty */ | 383 | slot->state = EMPTY; /* slot is empty */ |
416 | } else { | 384 | } else { |
417 | /* slot is occupied */ | 385 | /* slot is occupied */ |
418 | if (!(slot->dn->child)) { | 386 | if (!dn->child) { |
419 | /* non-empty slot has to have child */ | 387 | /* non-empty slot has to have child */ |
420 | err("%s: slot[%s]'s device_node doesn't have child for adapter\n", | 388 | err("%s: slot[%s]'s device_node doesn't have child for adapter\n", |
421 | __FUNCTION__, slot->name); | 389 | __FUNCTION__, slot->name); |
@@ -425,7 +393,7 @@ static int setup_pci_slot(struct slot *slot) | |||
425 | if (slot->hotplug_slot->info->adapter_status == NOT_CONFIGURED) { | 393 | if (slot->hotplug_slot->info->adapter_status == NOT_CONFIGURED) { |
426 | dbg("%s CONFIGURING pci adapter in slot[%s]\n", | 394 | dbg("%s CONFIGURING pci adapter in slot[%s]\n", |
427 | __FUNCTION__, slot->name); | 395 | __FUNCTION__, slot->name); |
428 | if (rpaphp_config_pci_adapter(slot)) { | 396 | if (rpaphp_config_pci_adapter(slot->bus)) { |
429 | err("%s: CONFIG pci adapter failed\n", __FUNCTION__); | 397 | err("%s: CONFIG pci adapter failed\n", __FUNCTION__); |
430 | goto exit_rc; | 398 | goto exit_rc; |
431 | } | 399 | } |
@@ -435,8 +403,8 @@ static int setup_pci_slot(struct slot *slot) | |||
435 | __FUNCTION__, slot->name); | 403 | __FUNCTION__, slot->name); |
436 | goto exit_rc; | 404 | goto exit_rc; |
437 | } | 405 | } |
438 | print_slot_pci_funcs(slot); | 406 | print_slot_pci_funcs(slot->bus); |
439 | if (!list_empty(slot->dev.pci_devs)) { | 407 | if (!list_empty(slot->pci_devs)) { |
440 | slot->state = CONFIGURED; | 408 | slot->state = CONFIGURED; |
441 | } else { | 409 | } else { |
442 | /* DLPAR add as opposed to | 410 | /* DLPAR add as opposed to |
@@ -454,11 +422,6 @@ int register_pci_slot(struct slot *slot) | |||
454 | { | 422 | { |
455 | int rc = -EINVAL; | 423 | int rc = -EINVAL; |
456 | 424 | ||
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)) | 425 | if (setup_pci_hotplug_slot_info(slot)) |
463 | goto exit_rc; | 426 | goto exit_rc; |
464 | if (setup_pci_slot(slot)) | 427 | if (setup_pci_slot(slot)) |
@@ -479,7 +442,7 @@ int rpaphp_enable_pci_slot(struct slot *slot) | |||
479 | /* if slot is not empty, enable the adapter */ | 442 | /* if slot is not empty, enable the adapter */ |
480 | if (state == PRESENT) { | 443 | if (state == PRESENT) { |
481 | dbg("%s : slot[%s] is occupied.\n", __FUNCTION__, slot->name); | 444 | dbg("%s : slot[%s] is occupied.\n", __FUNCTION__, slot->name); |
482 | retval = rpaphp_config_pci_adapter(slot); | 445 | retval = rpaphp_config_pci_adapter(slot->bus); |
483 | if (!retval) { | 446 | if (!retval) { |
484 | slot->state = CONFIGURED; | 447 | slot->state = CONFIGURED; |
485 | dbg("%s: PCI devices in slot[%s] has been configured\n", | 448 | dbg("%s: PCI devices in slot[%s] has been configured\n", |
@@ -502,37 +465,3 @@ exit: | |||
502 | dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval); | 465 | dbg("%s - Exit: rc[%d]\n", __FUNCTION__, retval); |
503 | return retval; | 466 | return retval; |
504 | } | 467 | } |
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); | ||