X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Facpi%2Fsleep%2Fmain.c;h=c37c4ead95c98215c275020429a254d5f1c05479;hb=60417f5976df029227450b46d7fa6f0e9b1e654c;hp=2c0b6630f8ba54603530fce4574f8697faaabe9e;hpb=1942971b20817def5fd1142248307c7c3c51fc8a;p=linux-2.6-omap-h63xx.git diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 2c0b6630f8b..c37c4ead95c 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -26,6 +26,21 @@ u8 sleep_states[ACPI_S_STATE_COUNT]; #ifdef CONFIG_PM_SLEEP static u32 acpi_target_sleep_state = ACPI_STATE_S0; +static bool acpi_sleep_finish_wake_up; + +/* + * ACPI 2.0 and later want us to execute _PTS after suspending devices, so we + * allow the user to request that behavior by using the 'acpi_new_pts_ordering' + * kernel command line option that causes the following variable to be set. + */ +static bool new_pts_ordering; + +static int __init acpi_new_pts_ordering(char *str) +{ + new_pts_ordering = true; + return 1; +} +__setup("acpi_new_pts_ordering", acpi_new_pts_ordering); #endif int acpi_sleep_prepare(u32 acpi_state) @@ -63,17 +78,25 @@ static u32 acpi_suspend_states[] = { static int init_8259A_after_S1; /** - * acpi_pm_set_target - Set the target system sleep state to the state + * acpi_pm_begin - Set the target system sleep state to the state * associated with given @pm_state, if supported. */ -static int acpi_pm_set_target(suspend_state_t pm_state) +static int acpi_pm_begin(suspend_state_t pm_state) { u32 acpi_state = acpi_suspend_states[pm_state]; int error = 0; if (sleep_states[acpi_state]) { acpi_target_sleep_state = acpi_state; + if (new_pts_ordering) + return 0; + + error = acpi_sleep_prepare(acpi_state); + if (error) + acpi_target_sleep_state = ACPI_STATE_S0; + else + acpi_sleep_finish_wake_up = true; } else { printk(KERN_ERR "ACPI does not support this state: %d\n", pm_state); @@ -91,12 +114,17 @@ static int acpi_pm_set_target(suspend_state_t pm_state) static int acpi_pm_prepare(void) { - int error = acpi_sleep_prepare(acpi_target_sleep_state); + if (new_pts_ordering) { + int error = acpi_sleep_prepare(acpi_target_sleep_state); - if (error) - acpi_target_sleep_state = ACPI_STATE_S0; + if (error) { + acpi_target_sleep_state = ACPI_STATE_S0; + return error; + } + acpi_sleep_finish_wake_up = true; + } - return error; + return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT; } /** @@ -120,10 +148,8 @@ static int acpi_pm_enter(suspend_state_t pm_state) if (acpi_state == ACPI_STATE_S3) { int error = acpi_save_state_mem(); - if (error) { - acpi_target_sleep_state = ACPI_STATE_S0; + if (error) return error; - } } local_irq_save(flags); @@ -139,6 +165,9 @@ static int acpi_pm_enter(suspend_state_t pm_state) break; } + /* Reprogram control registers and execute _BFS */ + acpi_leave_sleep_state_prep(acpi_state); + /* ACPI 3.0 specs (P62) says that it's the responsabilty * of the OSPM to clear the status bit [ implying that the * POWER_BUTTON event should not reach userspace ] @@ -146,6 +175,13 @@ static int acpi_pm_enter(suspend_state_t pm_state) if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) acpi_clear_event(ACPI_EVENT_POWER_BUTTON); + /* + * Disable and clear GPE status before interrupt is enabled. Some GPEs + * (like wakeup GPE) haven't handler, this can avoid such GPE misfire. + * acpi_leave_sleep_state will reenable specific GPEs later + */ + acpi_hw_disable_all_gpes(); + local_irq_restore(flags); printk(KERN_DEBUG "Back to C!\n"); @@ -157,7 +193,7 @@ static int acpi_pm_enter(suspend_state_t pm_state) } /** - * acpi_pm_finish - Finish up suspend sequence. + * acpi_pm_finish - Instruct the platform to leave a sleep state. * * This is called after we wake back up (or if entering the sleep state * failed). @@ -174,6 +210,7 @@ static void acpi_pm_finish(void) acpi_set_firmware_waking_vector((acpi_physical_address) 0); acpi_target_sleep_state = ACPI_STATE_S0; + acpi_sleep_finish_wake_up = false; #ifdef CONFIG_X86 if (init_8259A_after_S1) { @@ -183,6 +220,20 @@ static void acpi_pm_finish(void) #endif } +/** + * acpi_pm_end - Finish up suspend sequence. + */ + +static void acpi_pm_end(void) +{ + /* + * This is necessary in case acpi_pm_finish() is not called directly + * during a failing transition to a sleep state. + */ + if (acpi_sleep_finish_wake_up) + acpi_pm_finish(); +} + static int acpi_pm_state_valid(suspend_state_t pm_state) { u32 acpi_state; @@ -201,10 +252,11 @@ static int acpi_pm_state_valid(suspend_state_t pm_state) static struct platform_suspend_ops acpi_pm_ops = { .valid = acpi_pm_state_valid, - .set_target = acpi_pm_set_target, + .begin = acpi_pm_begin, .prepare = acpi_pm_prepare, .enter = acpi_pm_enter, .finish = acpi_pm_finish, + .end = acpi_pm_end, }; /* @@ -237,7 +289,16 @@ static int acpi_hibernation_start(void) static int acpi_hibernation_prepare(void) { - return acpi_sleep_prepare(ACPI_STATE_S4); + int error; + + error = acpi_sleep_prepare(ACPI_STATE_S4); + if (error) + return error; + + if (!ACPI_SUCCESS(acpi_hw_disable_all_gpes())) + error = -EFAULT; + + return error; } static int acpi_hibernation_enter(void) @@ -251,6 +312,8 @@ static int acpi_hibernation_enter(void) acpi_enable_wakeup_device(ACPI_STATE_S4); /* This shouldn't return. If it returns, we have a problem */ status = acpi_enter_sleep_state(ACPI_STATE_S4); + /* Reprogram control registers and execute _BFS */ + acpi_leave_sleep_state_prep(ACPI_STATE_S4); local_irq_restore(flags); return ACPI_SUCCESS(status) ? 0 : -EFAULT; @@ -263,15 +326,12 @@ static void acpi_hibernation_leave(void) * enable it here. */ acpi_enable(); + /* Reprogram control registers and execute _BFS */ + acpi_leave_sleep_state_prep(ACPI_STATE_S4); } static void acpi_hibernation_finish(void) { - /* - * If ACPI is not enabled by the BIOS and the boot kernel, we need to - * enable it here. - */ - acpi_enable(); acpi_disable_wakeup_device(ACPI_STATE_S4); acpi_leave_sleep_state(ACPI_STATE_S4); @@ -403,6 +463,7 @@ static void acpi_power_off_prepare(void) { /* Prepare to power off the system */ acpi_sleep_prepare(ACPI_STATE_S5); + acpi_hw_disable_all_gpes(); } static void acpi_power_off(void)