Skip to content

Commit

Permalink
ACPI / sleep: Add extra checks for HW Reduced ACPI mode sleep states
Browse files Browse the repository at this point in the history
If the HW Reduced ACPI mode bit is set in the FADT, ACPICA uses
the optional sleep control and sleep status registers for making
the system enter sleep states (including S5), so it is not possible
to use system sleep states or power it off using ACPI if the HW
Reduced ACPI mode bit is set and those registers are not available.

For this reason, add a new function, acpi_sleep_state_supported(),
checking if the HW Reduced ACPI mode bit is set and whether or not
system sleep states are usable in that case in addition to checking
the return value of acpi_get_sleep_type_data() and make the ACPI
sleep setup routines use that function to check the availability of
system sleep states.

Among other things, this prevents the kernel from attempting to
use ACPI for powering off HW Reduced ACPI systems without the sleep
control and sleep status registers, because ACPI power off doesn't
have a chance to work on them.  That allows alternative power off
mechanisms that may actually work to be used on those systems.  The
affected machines include Dell Venue 8 Pro, Asus T100TA, Haswell
Desktop SDP and Ivy Bridge EP Demo depot.

References: https://bugzilla.kernel.org/show_bug.cgi?id=70931
Reported-by: Adam Williamson <awilliam@redhat.com>
Tested-by: Aubrey Li <aubrey.li@linux.intel.com>
Cc: 3.4+ <stable@vger.kernel.org> # 3.4+
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
  • Loading branch information
Rafael J. Wysocki committed Mar 13, 2014
1 parent fa389e2 commit a4e90be
Showing 1 changed file with 15 additions and 17 deletions.
32 changes: 15 additions & 17 deletions drivers/acpi/sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,17 @@ static int acpi_sleep_prepare(u32 acpi_state)
return 0;
}

static bool acpi_sleep_state_supported(u8 sleep_state)
{
acpi_status status;
u8 type_a, type_b;

status = acpi_get_sleep_type_data(sleep_state, &type_a, &type_b);
return ACPI_SUCCESS(status) && (!acpi_gbl_reduced_hardware
|| (acpi_gbl_FADT.sleep_control.address
&& acpi_gbl_FADT.sleep_status.address));
}

#ifdef CONFIG_ACPI_SLEEP
static u32 acpi_target_sleep_state = ACPI_STATE_S0;

Expand Down Expand Up @@ -604,15 +615,9 @@ static void acpi_sleep_suspend_setup(void)
{
int i;

for (i = ACPI_STATE_S1; i < ACPI_STATE_S4; i++) {
acpi_status status;
u8 type_a, type_b;

status = acpi_get_sleep_type_data(i, &type_a, &type_b);
if (ACPI_SUCCESS(status)) {
for (i = ACPI_STATE_S1; i < ACPI_STATE_S4; i++)
if (acpi_sleep_state_supported(i))
sleep_states[i] = 1;
}
}

suspend_set_ops(old_suspend_ordering ?
&acpi_suspend_ops_old : &acpi_suspend_ops);
Expand Down Expand Up @@ -740,11 +745,7 @@ static const struct platform_hibernation_ops acpi_hibernation_ops_old = {

static void acpi_sleep_hibernate_setup(void)
{
acpi_status status;
u8 type_a, type_b;

status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b);
if (ACPI_FAILURE(status))
if (!acpi_sleep_state_supported(ACPI_STATE_S4))
return;

hibernation_set_ops(old_suspend_ordering ?
Expand Down Expand Up @@ -793,8 +794,6 @@ static void acpi_power_off(void)

int __init acpi_sleep_init(void)
{
acpi_status status;
u8 type_a, type_b;
char supported[ACPI_S_STATE_COUNT * 3 + 1];
char *pos = supported;
int i;
Expand All @@ -806,8 +805,7 @@ int __init acpi_sleep_init(void)
acpi_sleep_suspend_setup();
acpi_sleep_hibernate_setup();

status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b);
if (ACPI_SUCCESS(status)) {
if (acpi_sleep_state_supported(ACPI_STATE_S5)) {
sleep_states[ACPI_STATE_S5] = 1;
pm_power_off_prepare = acpi_power_off_prepare;
pm_power_off = acpi_power_off;
Expand Down

0 comments on commit a4e90be

Please sign in to comment.