diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-06-15 18:37:59 -0400 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2013-06-19 17:37:08 -0400 |
commit | fa1675b56537651270e79967b7f1ee4202c83bf6 (patch) | |
tree | 0c294f009d636378d79fd8300bb5c936ffdb656f /drivers/acpi | |
parent | 4c164ae7d8a7ee1f39b773d97794535c2c193b12 (diff) |
ACPI / PM: Rework and clean up acpi_dev_pm_get_state()
The acpi_dev_pm_get_state() function defined in device_pm.c is quite
convoluted, which isn't really necessary, and it doesn't validate the
values returned by the ACPI methods executed by it appropriately.
To address these shortcomings modify it in the following way.
(1) Make its return value only mean whether or not it succeeded and
pass the device power states determined by it through pointers.
(2) Drop the d_max_in argument, used by only one of its callers,
from it, and move the code related to d_max_in into that caller,
acpi_pm_device_sleep_state().
(3) Make it always check the return value of acpi_evaluate_integer()
and handle failures as appropriate. Moreover, make it check if
the values returned by the executed ACPI methods are not out of
range.
(4) Make it check if the values returned by the executed ACPI
methods represent valid power states of the given device and
handle situations in which that's not the case gracefully.
Also update the kerneldoc comments of acpi_dev_pm_get_state() and
acpi_pm_device_sleep_state() to reflect the code changes.
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/device_pm.c | 158 |
1 files changed, 92 insertions, 66 deletions
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index afc808e93855..fd363b57a596 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c | |||
@@ -403,44 +403,37 @@ EXPORT_SYMBOL(acpi_bus_can_wakeup); | |||
403 | * @dev: Device whose preferred target power state to return. | 403 | * @dev: Device whose preferred target power state to return. |
404 | * @adev: ACPI device node corresponding to @dev. | 404 | * @adev: ACPI device node corresponding to @dev. |
405 | * @target_state: System state to match the resultant device state. | 405 | * @target_state: System state to match the resultant device state. |
406 | * @d_max_in: Deepest low-power state to take into consideration. | 406 | * @d_min_p: Location to store the highest power state available to the device. |
407 | * @d_min_p: Location to store the upper limit of the allowed states range. | 407 | * @d_max_p: Location to store the lowest power state available to the device. |
408 | * Return value: Preferred power state of the device on success, -ENODEV | ||
409 | * (if there's no 'struct acpi_device' for @dev) or -EINVAL on failure | ||
410 | * | 408 | * |
411 | * Find the lowest power (highest number) ACPI device power state that the | 409 | * Find the lowest power (highest number) and highest power (lowest number) ACPI |
412 | * device can be in while the system is in the state represented by | 410 | * device power states that the device can be in while the system is in the |
413 | * @target_state. If @d_min_p is set, the highest power (lowest number) device | 411 | * state represented by @target_state. Store the integer numbers representing |
414 | * power state that @dev can be in for the given system sleep state is stored | 412 | * those stats in the memory locations pointed to by @d_max_p and @d_min_p, |
415 | * at the location pointed to by it. | 413 | * respectively. |
416 | * | 414 | * |
417 | * Callers must ensure that @dev and @adev are valid pointers and that @adev | 415 | * Callers must ensure that @dev and @adev are valid pointers and that @adev |
418 | * actually corresponds to @dev before using this function. | 416 | * actually corresponds to @dev before using this function. |
417 | * | ||
418 | * Returns 0 on success or -ENODATA when one of the ACPI methods fails or | ||
419 | * returns a value that doesn't make sense. The memory locations pointed to by | ||
420 | * @d_max_p and @d_min_p are only modified on success. | ||
419 | */ | 421 | */ |
420 | static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev, | 422 | static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev, |
421 | u32 target_state, int d_max_in, int *d_min_p) | 423 | u32 target_state, int *d_min_p, int *d_max_p) |
422 | { | 424 | { |
423 | char acpi_method[] = "_SxD"; | 425 | char method[] = { '_', 'S', '0' + target_state, 'D', '\0' }; |
424 | unsigned long long d_min, d_max; | 426 | acpi_handle handle = adev->handle; |
427 | unsigned long long ret; | ||
428 | int d_min, d_max; | ||
425 | bool wakeup = false; | 429 | bool wakeup = false; |
430 | acpi_status status; | ||
426 | 431 | ||
427 | if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3_COLD) | ||
428 | return -EINVAL; | ||
429 | |||
430 | if (d_max_in > ACPI_STATE_D3_HOT) { | ||
431 | enum pm_qos_flags_status stat; | ||
432 | |||
433 | stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF); | ||
434 | if (stat == PM_QOS_FLAGS_ALL) | ||
435 | d_max_in = ACPI_STATE_D3_HOT; | ||
436 | } | ||
437 | |||
438 | acpi_method[2] = '0' + target_state; | ||
439 | /* | 432 | /* |
440 | * If the sleep state is S0, the lowest limit from ACPI is D3, | 433 | * If the system state is S0, the lowest power state the device can be |
441 | * but if the device has _S0W, we will use the value from _S0W | 434 | * in is D3cold, unless the device has _S0W and is supposed to signal |
442 | * as the lowest limit from ACPI. Finally, we will constrain | 435 | * wakeup, in which case the return value of _S0W has to be used as the |
443 | * the lowest limit with the specified one. | 436 | * lowest power state available to the device. |
444 | */ | 437 | */ |
445 | d_min = ACPI_STATE_D0; | 438 | d_min = ACPI_STATE_D0; |
446 | d_max = ACPI_STATE_D3_COLD; | 439 | d_max = ACPI_STATE_D3_COLD; |
@@ -449,12 +442,30 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev, | |||
449 | * If present, _SxD methods return the minimum D-state (highest power | 442 | * If present, _SxD methods return the minimum D-state (highest power |
450 | * state) we can use for the corresponding S-states. Otherwise, the | 443 | * state) we can use for the corresponding S-states. Otherwise, the |
451 | * minimum D-state is D0 (ACPI 3.x). | 444 | * minimum D-state is D0 (ACPI 3.x). |
452 | * | ||
453 | * NOTE: We rely on acpi_evaluate_integer() not clobbering the integer | ||
454 | * provided -- that's our fault recovery, we ignore retval. | ||
455 | */ | 445 | */ |
456 | if (target_state > ACPI_STATE_S0) { | 446 | if (target_state > ACPI_STATE_S0) { |
457 | acpi_evaluate_integer(adev->handle, acpi_method, NULL, &d_min); | 447 | /* |
448 | * We rely on acpi_evaluate_integer() not clobbering the integer | ||
449 | * provided if AE_NOT_FOUND is returned. | ||
450 | */ | ||
451 | ret = d_min; | ||
452 | status = acpi_evaluate_integer(handle, method, NULL, &ret); | ||
453 | if ((ACPI_FAILURE(status) && status != AE_NOT_FOUND) | ||
454 | || ret > ACPI_STATE_D3_COLD) | ||
455 | return -ENODATA; | ||
456 | |||
457 | /* | ||
458 | * We need to handle legacy systems where D3hot and D3cold are | ||
459 | * the same and 3 is returned in both cases, so fall back to | ||
460 | * D3cold if D3hot is not a valid state. | ||
461 | */ | ||
462 | if (!adev->power.states[ret].flags.valid) { | ||
463 | if (ret == ACPI_STATE_D3_HOT) | ||
464 | ret = ACPI_STATE_D3_COLD; | ||
465 | else | ||
466 | return -ENODATA; | ||
467 | } | ||
468 | d_min = ret; | ||
458 | wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid | 469 | wakeup = device_may_wakeup(dev) && adev->wakeup.flags.valid |
459 | && adev->wakeup.sleep_state >= target_state; | 470 | && adev->wakeup.sleep_state >= target_state; |
460 | } else if (dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) != | 471 | } else if (dev_pm_qos_flags(dev, PM_QOS_FLAG_REMOTE_WAKEUP) != |
@@ -470,36 +481,29 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev, | |||
470 | * can wake the system. _S0W may be valid, too. | 481 | * can wake the system. _S0W may be valid, too. |
471 | */ | 482 | */ |
472 | if (wakeup) { | 483 | if (wakeup) { |
473 | acpi_status status; | 484 | method[3] = 'W'; |
474 | 485 | status = acpi_evaluate_integer(handle, method, NULL, &ret); | |
475 | acpi_method[3] = 'W'; | 486 | if (status == AE_NOT_FOUND) { |
476 | status = acpi_evaluate_integer(adev->handle, acpi_method, NULL, | 487 | if (target_state > ACPI_STATE_S0) |
477 | &d_max); | ||
478 | if (ACPI_FAILURE(status)) { | ||
479 | if (target_state != ACPI_STATE_S0 || | ||
480 | status != AE_NOT_FOUND) | ||
481 | d_max = d_min; | 488 | d_max = d_min; |
482 | } else if (d_max < d_min) { | 489 | } else if (ACPI_SUCCESS(status) && ret <= ACPI_STATE_D3_COLD) { |
483 | /* Warn the user of the broken DSDT */ | 490 | /* Fall back to D3cold if ret is not a valid state. */ |
484 | printk(KERN_WARNING "ACPI: Wrong value from %s\n", | 491 | if (!adev->power.states[ret].flags.valid) |
485 | acpi_method); | 492 | ret = ACPI_STATE_D3_COLD; |
486 | /* Sanitize it */ | 493 | |
487 | d_min = d_max; | 494 | d_max = ret > d_min ? ret : d_min; |
495 | } else { | ||
496 | return -ENODATA; | ||
488 | } | 497 | } |
489 | } | 498 | } |
490 | 499 | ||
491 | if (d_max_in < d_min) | ||
492 | return -EINVAL; | ||
493 | if (d_min_p) | 500 | if (d_min_p) |
494 | *d_min_p = d_min; | 501 | *d_min_p = d_min; |
495 | /* constrain d_max with specified lowest limit (max number) */ | 502 | |
496 | if (d_max > d_max_in) { | 503 | if (d_max_p) |
497 | for (d_max = d_max_in; d_max > d_min; d_max--) { | 504 | *d_max_p = d_max; |
498 | if (adev->power.states[d_max].flags.valid) | 505 | |
499 | break; | 506 | return 0; |
500 | } | ||
501 | } | ||
502 | return d_max; | ||
503 | } | 507 | } |
504 | 508 | ||
505 | /** | 509 | /** |
@@ -508,7 +512,8 @@ static int acpi_dev_pm_get_state(struct device *dev, struct acpi_device *adev, | |||
508 | * @d_min_p: Location to store the upper limit of the allowed states range. | 512 | * @d_min_p: Location to store the upper limit of the allowed states range. |
509 | * @d_max_in: Deepest low-power state to take into consideration. | 513 | * @d_max_in: Deepest low-power state to take into consideration. |
510 | * Return value: Preferred power state of the device on success, -ENODEV | 514 | * Return value: Preferred power state of the device on success, -ENODEV |
511 | * (if there's no 'struct acpi_device' for @dev) or -EINVAL on failure | 515 | * if there's no 'struct acpi_device' for @dev, -EINVAL if @d_max_in is |
516 | * incorrect, or -ENODATA on ACPI method failure. | ||
512 | * | 517 | * |
513 | * The caller must ensure that @dev is valid before using this function. | 518 | * The caller must ensure that @dev is valid before using this function. |
514 | */ | 519 | */ |
@@ -516,14 +521,39 @@ int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p, int d_max_in) | |||
516 | { | 521 | { |
517 | acpi_handle handle = DEVICE_ACPI_HANDLE(dev); | 522 | acpi_handle handle = DEVICE_ACPI_HANDLE(dev); |
518 | struct acpi_device *adev; | 523 | struct acpi_device *adev; |
524 | int ret, d_max; | ||
525 | |||
526 | if (d_max_in < ACPI_STATE_D0 || d_max_in > ACPI_STATE_D3_COLD) | ||
527 | return -EINVAL; | ||
528 | |||
529 | if (d_max_in > ACPI_STATE_D3_HOT) { | ||
530 | enum pm_qos_flags_status stat; | ||
531 | |||
532 | stat = dev_pm_qos_flags(dev, PM_QOS_FLAG_NO_POWER_OFF); | ||
533 | if (stat == PM_QOS_FLAGS_ALL) | ||
534 | d_max_in = ACPI_STATE_D3_HOT; | ||
535 | } | ||
519 | 536 | ||
520 | if (!handle || acpi_bus_get_device(handle, &adev)) { | 537 | if (!handle || acpi_bus_get_device(handle, &adev)) { |
521 | dev_dbg(dev, "ACPI handle without context in %s!\n", __func__); | 538 | dev_dbg(dev, "ACPI handle without context in %s!\n", __func__); |
522 | return -ENODEV; | 539 | return -ENODEV; |
523 | } | 540 | } |
524 | 541 | ||
525 | return acpi_dev_pm_get_state(dev, adev, acpi_target_system_state(), | 542 | ret = acpi_dev_pm_get_state(dev, adev, acpi_target_system_state(), |
526 | d_max_in, d_min_p); | 543 | d_min_p, &d_max); |
544 | if (ret) | ||
545 | return ret; | ||
546 | |||
547 | if (d_max_in < *d_min_p) | ||
548 | return -EINVAL; | ||
549 | |||
550 | if (d_max > d_max_in) { | ||
551 | for (d_max = d_max_in; d_max > *d_min_p; d_max--) { | ||
552 | if (adev->power.states[d_max].flags.valid) | ||
553 | break; | ||
554 | } | ||
555 | } | ||
556 | return d_max; | ||
527 | } | 557 | } |
528 | EXPORT_SYMBOL(acpi_pm_device_sleep_state); | 558 | EXPORT_SYMBOL(acpi_pm_device_sleep_state); |
529 | 559 | ||
@@ -674,17 +704,13 @@ struct acpi_device *acpi_dev_pm_get_node(struct device *dev) | |||
674 | static int acpi_dev_pm_low_power(struct device *dev, struct acpi_device *adev, | 704 | static int acpi_dev_pm_low_power(struct device *dev, struct acpi_device *adev, |
675 | u32 system_state) | 705 | u32 system_state) |
676 | { | 706 | { |
677 | int power_state; | 707 | int ret, state; |
678 | 708 | ||
679 | if (!acpi_device_power_manageable(adev)) | 709 | if (!acpi_device_power_manageable(adev)) |
680 | return 0; | 710 | return 0; |
681 | 711 | ||
682 | power_state = acpi_dev_pm_get_state(dev, adev, system_state, | 712 | ret = acpi_dev_pm_get_state(dev, adev, system_state, NULL, &state); |
683 | ACPI_STATE_D3_COLD, NULL); | 713 | return ret ? ret : acpi_device_set_power(adev, state); |
684 | if (power_state < ACPI_STATE_D0 || power_state > ACPI_STATE_D3_COLD) | ||
685 | return -EIO; | ||
686 | |||
687 | return acpi_device_set_power(adev, power_state); | ||
688 | } | 714 | } |
689 | 715 | ||
690 | /** | 716 | /** |