diff options
Diffstat (limited to 'drivers/pci/hotplug/acpi_pcihp.c')
-rw-r--r-- | drivers/pci/hotplug/acpi_pcihp.c | 85 |
1 files changed, 81 insertions, 4 deletions
diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index f8c187a763bd..93e37f0666ab 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/types.h> | 30 | #include <linux/types.h> |
31 | #include <linux/pci.h> | 31 | #include <linux/pci.h> |
32 | #include <linux/pci_hotplug.h> | 32 | #include <linux/pci_hotplug.h> |
33 | #include <linux/pci-acpi.h> | ||
33 | #include <acpi/acpi.h> | 34 | #include <acpi/acpi.h> |
34 | #include <acpi/acpi_bus.h> | 35 | #include <acpi/acpi_bus.h> |
35 | #include <acpi/actypes.h> | 36 | #include <acpi/actypes.h> |
@@ -299,7 +300,7 @@ free_and_return: | |||
299 | * | 300 | * |
300 | * @handle - the handle of the hotplug controller. | 301 | * @handle - the handle of the hotplug controller. |
301 | */ | 302 | */ |
302 | acpi_status acpi_run_oshp(acpi_handle handle) | 303 | static acpi_status acpi_run_oshp(acpi_handle handle) |
303 | { | 304 | { |
304 | acpi_status status; | 305 | acpi_status status; |
305 | struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; | 306 | struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; |
@@ -322,9 +323,6 @@ acpi_status acpi_run_oshp(acpi_handle handle) | |||
322 | kfree(string.pointer); | 323 | kfree(string.pointer); |
323 | return status; | 324 | return status; |
324 | } | 325 | } |
325 | EXPORT_SYMBOL_GPL(acpi_run_oshp); | ||
326 | |||
327 | |||
328 | 326 | ||
329 | /* acpi_get_hp_params_from_firmware | 327 | /* acpi_get_hp_params_from_firmware |
330 | * | 328 | * |
@@ -374,6 +372,85 @@ acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, | |||
374 | } | 372 | } |
375 | EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware); | 373 | EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware); |
376 | 374 | ||
375 | /** | ||
376 | * acpi_get_hp_hw_control_from_firmware | ||
377 | * @dev: the pci_dev of the bridge that has a hotplug controller | ||
378 | * @flags: requested control bits for _OSC | ||
379 | * | ||
380 | * Attempt to take hotplug control from firmware. | ||
381 | */ | ||
382 | int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags) | ||
383 | { | ||
384 | acpi_status status; | ||
385 | acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev)); | ||
386 | struct pci_dev *pdev = dev; | ||
387 | struct pci_bus *parent; | ||
388 | struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
389 | |||
390 | flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | | ||
391 | OSC_SHPC_NATIVE_HP_CONTROL | | ||
392 | OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); | ||
393 | if (!flags) { | ||
394 | err("Invalid flags %u specified!\n", flags); | ||
395 | return -EINVAL; | ||
396 | } | ||
397 | |||
398 | /* | ||
399 | * Per PCI firmware specification, we should run the ACPI _OSC | ||
400 | * method to get control of hotplug hardware before using it. If | ||
401 | * an _OSC is missing, we look for an OSHP to do the same thing. | ||
402 | * To handle different BIOS behavior, we look for _OSC and OSHP | ||
403 | * within the scope of the hotplug controller and its parents, | ||
404 | * upto the host bridge under which this controller exists. | ||
405 | */ | ||
406 | while (!handle) { | ||
407 | /* | ||
408 | * This hotplug controller was not listed in the ACPI name | ||
409 | * space at all. Try to get acpi handle of parent pci bus. | ||
410 | */ | ||
411 | if (!pdev || !pdev->bus->parent) | ||
412 | break; | ||
413 | parent = pdev->bus->parent; | ||
414 | dbg("Could not find %s in acpi namespace, trying parent\n", | ||
415 | pci_name(pdev)); | ||
416 | if (!parent->self) | ||
417 | /* Parent must be a host bridge */ | ||
418 | handle = acpi_get_pci_rootbridge_handle( | ||
419 | pci_domain_nr(parent), | ||
420 | parent->number); | ||
421 | else | ||
422 | handle = DEVICE_ACPI_HANDLE(&(parent->self->dev)); | ||
423 | pdev = parent->self; | ||
424 | } | ||
425 | |||
426 | while (handle) { | ||
427 | acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); | ||
428 | dbg("Trying to get hotplug control for %s \n", | ||
429 | (char *)string.pointer); | ||
430 | status = pci_osc_control_set(handle, flags); | ||
431 | if (status == AE_NOT_FOUND) | ||
432 | status = acpi_run_oshp(handle); | ||
433 | if (ACPI_SUCCESS(status)) { | ||
434 | dbg("Gained control for hotplug HW for pci %s (%s)\n", | ||
435 | pci_name(dev), (char *)string.pointer); | ||
436 | kfree(string.pointer); | ||
437 | return 0; | ||
438 | } | ||
439 | if (acpi_root_bridge(handle)) | ||
440 | break; | ||
441 | chandle = handle; | ||
442 | status = acpi_get_parent(chandle, &handle); | ||
443 | if (ACPI_FAILURE(status)) | ||
444 | break; | ||
445 | } | ||
446 | |||
447 | dbg("Cannot get control of hotplug hardware for pci %s\n", | ||
448 | pci_name(dev)); | ||
449 | |||
450 | kfree(string.pointer); | ||
451 | return -ENODEV; | ||
452 | } | ||
453 | EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware); | ||
377 | 454 | ||
378 | /* acpi_root_bridge - check to see if this acpi object is a root bridge | 455 | /* acpi_root_bridge - check to see if this acpi object is a root bridge |
379 | * | 456 | * |