Fix the ACPI 1.0 vs ACPI 2.0 suspend ordering issue (rev. 3)
Subsystems (10)
cover Fix the ACPI 1.0 vs ACPI 2.0 suspend ordering issue (rev. 3)
Len,
The following patchset is intended to fix the ACPI 1.0 vs ACPI 2.0 suspend ordering issue described at http://bugzilla.kernel.org/show_bug.cgi?id=9528 and in a recent LKML thread (http://lkml.org/lkml/2007/12/25/37).
The patches actually do more than that, as I think it's reasonable to untangle some ACPI-specific suspend code when we're at it. The details are described in the changelogs.
The patches are intended for 2.6.25.
Please add them to the suspend branch.
Thanks,
Rafael
-
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH 1/7 Suspend: Introduce begin() and end() callbacks
From: Rafael J. Wysocki <[email protected]>
On ACPI systems the target state set by acpi_pm_set_target() is reset by acpi_pm_finish(), but that need not be called if the suspend fails. All platforms that use the .set_target() global suspend callback are affected by analogous issues.
For this reason, we need an additional global suspend callback that will reset the target state regardless of whether or not the suspend is successful. Also, it is reasonable to rename the .set_target() callback, since it will be used for a different purpose on ACPI systems (due to ACPI 1.0x code ordering requirements).
Introduce the global suspend callback .end() to be executed at the end of the suspend sequence and rename the .set_target() global suspend callback to .begin().
--- arch/arm/mach-at91/pm.c | 17 +++++++++++++---- arch/powerpc/platforms/52xx/lite5200_pm.c | 10 ++++++++-- drivers/acpi/sleep/main.c | 22 ++++++++++++++++++---- include/linux/suspend.h | 29 ++++++++++++++++++----------- kernel/power/main.c | 9 ++++++--- 5 files changed, 63 insertions(+), 24 deletions(-)
include/linux/suspend.h +18 -11
@@ -38,18 +38,16 @@ typedef int __bitwise suspend_state_t;* There is the %suspend_valid_only_mem function available that can be * assigned to this if the platform only supports mem sleep. * -* @set_target: Tell the platform which system sleep state is going to be -* entered. -* @set_target() is executed right prior to suspending devices. The -* information conveyed to the platform code by @set_target() should be -* disregarded by the platform as soon as @finish() is executed and if -* @prepare() fails. If @set_target() fails (ie. returns nonzero), +* @begin: Initialise a transition to given system sleep state. +* @begin() is executed right prior to suspending devices. The information +* conveyed to the platform code by @begin() should be disregarded by it as +* soon as @end() is executed. If @begin() fails (ie. returns nonzero), * @prepare(), @enter() and @finish() will not be called by the PM core. * This callback is optional. However, if it is implemented, the argument -* passed to @enter() is meaningless and should be ignored. +* passed to @enter() is redundant and should be ignored. * * @prepare: Prepare the platform for entering the system sleep state indicated -* by @set_target(). +* by @begin(). * @prepare() is called right after devices have been suspended (ie. the * appropriate .suspend() method has been executed for each device) and * before the nonboot CPUs are disabled (it is executed with IRQs enabled).
@@ -57,8 +55,8 @@ typedef int __bitwise suspend_state_t;* error code otherwise, in which case the system cannot enter the desired * sleep state (@enter() and @finish() will not be called in that case). * -* @enter: Enter the system sleep state indicated by @set_target() or -* represented by the argument if @set_target() is not implemented. +* @enter: Enter the system sleep state indicated by @begin() or represented by +* the argument if @begin() is not implemented. * This callback is mandatory. It returns 0 on success or a negative * error code otherwise, in which case the system cannot enter the desired * sleep state.
@@ -69,13 +67,22 @@ typedef int __bitwise suspend_state_t;* This callback is optional, but should be implemented by the platforms * that implement @prepare(). If implemented, it is always called after * @enter() (even if @enter() fails). +* +* @end: Called by the PM core right after resuming devices, to indicate to +* the platform that the system has returned to the working state or +* the transition to the sleep state has been aborted. +* This callback is optional, but should be implemented by the platforms +* that implement @begin(), but platforms implementing @begin() should +* also provide a @end() which cleans up transitions aborted before +* @enter(). */ struct platform_suspend_ops {int (*valid)(suspend_state_t state); -int (*set_target)(suspend_state_t state); +int (*begin)(suspend_state_t state);int (*prepare)(void );int (*enter)(suspend_state_t state);void (*finish)(void ); +void (*end)(void ); };#ifdef CONFIG_SUSPEND
drivers/acpi/sleep/main.c +18 -4
@@ -63,11 +63,11 @@ 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 ;
@@ -164,7 +164,7 @@ static int acpi_pm_enter(suspend_state_t }/** -* 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).
@@ -190,6 +190,19 @@ 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 during a +* failing transition to a sleep state. +*/ + acpi_target_sleep_state =ACPI_STATE_S0 ; +} +static int acpi_pm_state_valid (suspend_state_t pm_state) {u32 acpi_state;
@@ -208,10 +221,11 @@ static int acpi_pm_state_valid(suspend_sstatic 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, };/*
arch/arm/mach-at91/pm.c +13 -4
@@ -52,7 +52,7 @@ static suspend_state_t target_state;/* * Called after processes are frozen, but before we shutdown devices. */ -static int at91_pm_set_target (suspend_state_t state) +static int at91_pm_begin (suspend_state_t state) { target_state = state;return 0 ;
@@ -197,11 +197,20 @@ error:return 0 ; } +/* +* Called right prior to thawing processes. +*/ +static void at91_pm_end (void ) +{ + target_state =PM_SUSPEND_ON ; +} +static struct platform_suspend_ops at91_pm_ops ={ - .valid = at91_pm_valid_state, - .set_target = at91_pm_set_target, - .enter = at91_pm_enter, + .valid = at91_pm_valid_state, + .begin = at91_pm_begin, + .enter = at91_pm_enter, + .end = at91_pm_end, };static int __init at91_pm_init (void )
kernel/power/main.c +6 -3
@@ -253,10 +253,10 @@ int suspend_devices_and_enter(suspend_stif (!suspend_ops)return -ENOSYS ; -if (suspend_ops->set_target) { - error = suspend_ops->set_target (state); +if (suspend_ops->begin) { + error = suspend_ops->begin (state);if (error) -return error; + goto Close; }suspend_console (); error =device_suspend (PMSG_SUSPEND );
@@ -289,6 +289,9 @@ int suspend_devices_and_enter(suspend_stdevice_resume (); Resume_console:resume_console (); + Close: +if (suspend_ops->end) + suspend_ops->end ();return error; }
arch/powerpc/platforms/52xx/lite5200_pm.c +8 -2
@@ -31,7 +31,7 @@ static int lite5200_pm_valid(suspend_sta } } -static int lite5200_pm_set_target (suspend_state_t state) +static int lite5200_pm_begin (suspend_state_t state) {if (lite5200_pm_valid (state)) { lite5200_pm_target_state = state;
@@ -208,12 +208,18 @@ static void lite5200_pm_finish(void)mpc52xx_pm_finish (); } +static void lite5200_pm_end (void ) +{ + lite5200_pm_target_state =PM_SUSPEND_ON ; +} +static struct platform_suspend_ops lite5200_pm_ops = { .valid = lite5200_pm_valid, - .set_target = lite5200_pm_set_target, + .begin = lite5200_pm_begin, .prepare = lite5200_pm_prepare, .enter = lite5200_pm_enter, .finish = lite5200_pm_finish, + .end = lite5200_pm_end, };int __init lite5200_pm_init (void )
PATCH 1/7 Suspend: Introduce begin() and end() callbacks
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
On ACPI systems the target state set by acpi_pm_set_target() is reset by acpi_pm_finish(), but that need not be called if the suspend fails. All platforms that use the .set_target() global suspend callback are affected by analogous issues.
For this reason, we need an additional global suspend callback that will reset the target state regardless of whether or not the suspend is successful. Also, it is reasonable to rename the .set_target() callback, since it will be used for a different purpose on ACPI systems (due to ACPI 1.0x code ordering requirements).
Introduce the global suspend callback .end() to be executed at the end of the suspend sequence and rename the .set_target() global suspend callback to .begin().
--- arch/arm/mach-at91/pm.c | 17 +++++++++++++---- arch/powerpc/platforms/52xx/lite5200_pm.c | 10 ++++++++-- drivers/acpi/sleep/main.c | 22 ++++++++++++++++++---- include/linux/suspend.h | 29 ++++++++++++++++++----------- kernel/power/main.c | 9 ++++++--- 5 files changed, 63 insertions(+), 24 deletions(-)
include/linux/suspend.h +18 -11
@@ -38,18 +38,16 @@ typedef int __bitwise suspend_state_t;* There is the %suspend_valid_only_mem function available that can be * assigned to this if the platform only supports mem sleep. * -* @set_target: Tell the platform which system sleep state is going to be -* entered. -* @set_target() is executed right prior to suspending devices. The -* information conveyed to the platform code by @set_target() should be -* disregarded by the platform as soon as @finish() is executed and if -* @prepare() fails. If @set_target() fails (ie. returns nonzero), +* @begin: Initialise a transition to given system sleep state. +* @begin() is executed right prior to suspending devices. The information +* conveyed to the platform code by @begin() should be disregarded by it as +* soon as @end() is executed. If @begin() fails (ie. returns nonzero), * @prepare(), @enter() and @finish() will not be called by the PM core. * This callback is optional. However, if it is implemented, the argument -* passed to @enter() is meaningless and should be ignored. +* passed to @enter() is redundant and should be ignored. * * @prepare: Prepare the platform for entering the system sleep state indicated -* by @set_target(). +* by @begin(). * @prepare() is called right after devices have been suspended (ie. the * appropriate .suspend() method has been executed for each device) and * before the nonboot CPUs are disabled (it is executed with IRQs enabled).
@@ -57,8 +55,8 @@ typedef int __bitwise suspend_state_t;* error code otherwise, in which case the system cannot enter the desired * sleep state (@enter() and @finish() will not be called in that case). * -* @enter: Enter the system sleep state indicated by @set_target() or -* represented by the argument if @set_target() is not implemented. +* @enter: Enter the system sleep state indicated by @begin() or represented by +* the argument if @begin() is not implemented. * This callback is mandatory. It returns 0 on success or a negative * error code otherwise, in which case the system cannot enter the desired * sleep state.
@@ -69,13 +67,22 @@ typedef int __bitwise suspend_state_t;* This callback is optional, but should be implemented by the platforms * that implement @prepare(). If implemented, it is always called after * @enter() (even if @enter() fails). +* +* @end: Called by the PM core right after resuming devices, to indicate to +* the platform that the system has returned to the working state or +* the transition to the sleep state has been aborted. +* This callback is optional, but should be implemented by the platforms +* that implement @begin(), but platforms implementing @begin() should +* also provide a @end() which cleans up transitions aborted before +* @enter(). */ struct platform_suspend_ops {int (*valid)(suspend_state_t state); -int (*set_target)(suspend_state_t state); +int (*begin)(suspend_state_t state);int (*prepare)(void );int (*enter)(suspend_state_t state);void (*finish)(void ); +void (*end)(void ); };#ifdef CONFIG_SUSPEND
drivers/acpi/sleep/main.c +18 -4
@@ -63,11 +63,11 @@ 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 ;
@@ -164,7 +164,7 @@ static int acpi_pm_enter(suspend_state_t }/** -* 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).
@@ -190,6 +190,19 @@ 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 during a +* failing transition to a sleep state. +*/ + acpi_target_sleep_state =ACPI_STATE_S0 ; +} +static int acpi_pm_state_valid (suspend_state_t pm_state) {u32 acpi_state;
@@ -208,10 +221,11 @@ static int acpi_pm_state_valid(suspend_sstatic 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, };/*
arch/arm/mach-at91/pm.c +13 -4
@@ -52,7 +52,7 @@ static suspend_state_t target_state;/* * Called after processes are frozen, but before we shutdown devices. */ -static int at91_pm_set_target (suspend_state_t state) +static int at91_pm_begin (suspend_state_t state) { target_state = state;return 0 ;
@@ -197,11 +197,20 @@ error:return 0 ; } +/* +* Called right prior to thawing processes. +*/ +static void at91_pm_end (void ) +{ + target_state =PM_SUSPEND_ON ; +} +static struct platform_suspend_ops at91_pm_ops ={ - .valid = at91_pm_valid_state, - .set_target = at91_pm_set_target, - .enter = at91_pm_enter, + .valid = at91_pm_valid_state, + .begin = at91_pm_begin, + .enter = at91_pm_enter, + .end = at91_pm_end, };static int __init at91_pm_init (void )
kernel/power/main.c +6 -3
@@ -253,10 +253,10 @@ int suspend_devices_and_enter(suspend_stif (!suspend_ops)return -ENOSYS ; -if (suspend_ops->set_target) { - error = suspend_ops->set_target (state); +if (suspend_ops->begin) { + error = suspend_ops->begin (state);if (error) -return error; + goto Close; }suspend_console (); error =device_suspend (PMSG_SUSPEND );
@@ -289,6 +289,9 @@ int suspend_devices_and_enter(suspend_stdevice_resume (); Resume_console:resume_console (); + Close: +if (suspend_ops->end) + suspend_ops->end ();return error; }
arch/powerpc/platforms/52xx/lite5200_pm.c +8 -2
@@ -31,7 +31,7 @@ static int lite5200_pm_valid(suspend_sta } } -static int lite5200_pm_set_target (suspend_state_t state) +static int lite5200_pm_begin (suspend_state_t state) {if (lite5200_pm_valid (state)) { lite5200_pm_target_state = state;
@@ -208,12 +208,18 @@ static void lite5200_pm_finish(void)mpc52xx_pm_finish (); } +static void lite5200_pm_end (void ) +{ + lite5200_pm_target_state =PM_SUSPEND_ON ; +} +static struct platform_suspend_ops lite5200_pm_ops = { .valid = lite5200_pm_valid, - .set_target = lite5200_pm_set_target, + .begin = lite5200_pm_begin, .prepare = lite5200_pm_prepare, .enter = lite5200_pm_enter, .finish = lite5200_pm_finish, + .end = lite5200_pm_end, };int __init lite5200_pm_init (void )
-
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH 2/7 ACPI: Separate invocations of _GTS and _BFS from _PTS and _WAK
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
The execution of ACPI global control methods _GTS and _BFS is currently tied to the preparation to enter a sleep state and to the leaving of the sleep state, respectively. However, these functions are called before disabling the nonboot CPUs and after enabling them, respectively (in fact, on ACPI 1.0x systems the first of them ought to be called before suspending devices), while according to the ACPI specification, _GTS is to be executed right prior to entering the system sleep state and _BFS is to be executed right after the platfor firmware has returned control to the OS on wake up.
Move the execution of _GTS and _BFS to the right places.
--- drivers/acpi/hardware/hwsleep.c | 75 ++++++++++++++++++++++++++++++---------- drivers/acpi/sleep/main.c | 7 +++ include/acpi/acpixf.h | 2 + 3 files changed, 66 insertions(+), 18 deletions(-)
drivers/acpi/hardware/hwsleep.c +57 -18
@@ -192,18 +192,13 @@ acpi_status acpi_enter_sleep_state_prep( arg.type =ACPI_TYPE_INTEGER ; arg.integer.value = sleep_state; -/* Run the _PTS and _GTS methods */ +/* Run the _PTS method */ status =acpi_evaluate_object (NULL ,METHOD_NAME__PTS , &arg_list,NULL );if (ACPI_FAILURE (status) && status !=AE_NOT_FOUND ) {return_ACPI_STATUS (status); } - status =acpi_evaluate_object (NULL ,METHOD_NAME__GTS , &arg_list,NULL ); -if (ACPI_FAILURE (status) && status !=AE_NOT_FOUND ) { -return_ACPI_STATUS (status); - } -/* Setup the argument to _SST */ switch (sleep_state) {
@@ -262,6 +257,8 @@ acpi_status asmlinkage acpi_enter_sleep_struct acpi_bit_register_info *sleep_type_reg_info;struct acpi_bit_register_info *sleep_enable_reg_info;u32 in_value; +struct acpi_object_list arg_list; +union acpi_object arg;acpi_status status;ACPI_FUNCTION_TRACE (acpi_enter_sleep_state);
@@ -307,6 +304,18 @@ acpi_status asmlinkage acpi_enter_sleep_return_ACPI_STATUS (status); } +/* Execute the _GTS method */ + + arg_list.count =1 ; + arg_list.pointer = &arg; + arg.type =ACPI_TYPE_INTEGER ; + arg.integer.value = sleep_state; + + status =acpi_evaluate_object (NULL ,METHOD_NAME__GTS , &arg_list,NULL ); +if (ACPI_FAILURE (status) && status !=AE_NOT_FOUND ) { +return_ACPI_STATUS (status); + } +/* Get current value of PM1A control */ status =acpi_hw_register_read (ACPI_REGISTER_PM1_CONTROL , &PM1Acontrol);
@@ -473,17 +482,18 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_stat/******************************************************************************* * -* FUNCTION: acpi_leave_sleep_state +* FUNCTION: acpi_leave_sleep_state_prep * -* PARAMETERS: sleep_state - Which sleep state we just exited +* PARAMETERS: sleep_state - Which sleep state we are exiting * * RETURN: Status * -* DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep -* Called with interrupts ENABLED. +* DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a +* sleep. +* Called with interrupts DISABLED. * ******************************************************************************/ -acpi_status acpi_leave_sleep_state (u8 sleep_state) +acpi_status acpi_leave_sleep_state_prep (u8 sleep_state) {struct acpi_object_list arg_list;union acpi_object arg;
@@ -493,7 +503,7 @@ acpi_status acpi_leave_sleep_state(u8 slu32 PM1Acontrol;u32 PM1Bcontrol; -ACPI_FUNCTION_TRACE (acpi_leave_sleep_state); +ACPI_FUNCTION_TRACE (acpi_leave_sleep_state_prep);/* * Set SLP_TYPE and SLP_EN to state S0.
@@ -540,6 +550,41 @@ acpi_status acpi_leave_sleep_state(u8 sl } } +/* Execute the _BFS method */ + + arg_list.count =1 ; + arg_list.pointer = &arg; + arg.type =ACPI_TYPE_INTEGER ; + arg.integer.value = sleep_state; + + status =acpi_evaluate_object (NULL ,METHOD_NAME__BFS , &arg_list,NULL ); +if (ACPI_FAILURE (status) && status !=AE_NOT_FOUND ) { +ACPI_EXCEPTION ((AE_INFO , status,"During Method _BFS" )); + } + +return_ACPI_STATUS (status); +} + +/******************************************************************************* +* +* FUNCTION: acpi_leave_sleep_state +* +* PARAMETERS: sleep_state - Which sleep state we just exited +* +* RETURN: Status +* +* DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep +* Called with interrupts ENABLED. +* +******************************************************************************/ +acpi_status acpi_leave_sleep_state (u8 sleep_state) +{ +struct acpi_object_list arg_list; +union acpi_object arg; +acpi_status status; + +ACPI_FUNCTION_TRACE (acpi_leave_sleep_state); +/* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */ acpi_gbl_sleep_type_a =ACPI_SLEEP_TYPE_INVALID ;
@@ -558,12 +603,6 @@ acpi_status acpi_leave_sleep_state(u8 slACPI_EXCEPTION ((AE_INFO , status,"During Method _SST" )); } - arg.integer.value = sleep_state; - status =acpi_evaluate_object (NULL ,METHOD_NAME__BFS , &arg_list,NULL ); -if (ACPI_FAILURE (status) && status !=AE_NOT_FOUND ) { -ACPI_EXCEPTION ((AE_INFO , status,"During Method _BFS" )); - } -/* * GPEs must be enabled before _WAK is called as GPEs * might get fired there
drivers/acpi/sleep/main.c +7 -0
@@ -139,6 +139,9 @@ static int acpi_pm_enter(suspend_state_tbreak ; } +/* 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 ]
@@ -272,6 +275,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 ;
@@ -284,6 +289,8 @@ 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 )
include/acpi/acpixf.h +2 -0
@@ -335,6 +335,8 @@ acpi_status asmlinkage acpi_enter_sleep_ acpi_status asmlinkage acpi_enter_sleep_state_s4bios(void); +acpi_status acpi_leave_sleep_state_prep (u8 sleep_state); +acpi_status acpi_leave_sleep_state (u8 sleep_state);#endif /* __ACXFACE_H__ */
PATCH 2/7 ACPI: Separate invocations of _GTS and _BFS from _PTS and _WAK
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
The execution of ACPI global control methods _GTS and _BFS is currently tied to the preparation to enter a sleep state and to the leaving of the sleep state, respectively. However, these functions are called before disabling the nonboot CPUs and after enabling them, respectively (in fact, on ACPI 1.0x systems the first of them ought to be called before suspending devices), while according to the ACPI specification, _GTS is to be executed right prior to entering the system sleep state and _BFS is to be executed right after the platfor firmware has returned control to the OS on wake up.
Move the execution of _GTS and _BFS to the right places.
--- drivers/acpi/hardware/hwsleep.c | 75 ++++++++++++++++++++++++++++++---------- drivers/acpi/sleep/main.c | 7 +++ include/acpi/acpixf.h | 2 + 3 files changed, 66 insertions(+), 18 deletions(-)
drivers/acpi/hardware/hwsleep.c +57 -18
@@ -192,18 +192,13 @@ acpi_status acpi_enter_sleep_state_prep( arg.type =ACPI_TYPE_INTEGER ; arg.integer.value = sleep_state; -/* Run the _PTS and _GTS methods */ +/* Run the _PTS method */ status =acpi_evaluate_object (NULL ,METHOD_NAME__PTS , &arg_list,NULL );if (ACPI_FAILURE (status) && status !=AE_NOT_FOUND ) {return_ACPI_STATUS (status); } - status =acpi_evaluate_object (NULL ,METHOD_NAME__GTS , &arg_list,NULL ); -if (ACPI_FAILURE (status) && status !=AE_NOT_FOUND ) { -return_ACPI_STATUS (status); - } -/* Setup the argument to _SST */ switch (sleep_state) {
@@ -262,6 +257,8 @@ acpi_status asmlinkage acpi_enter_sleep_struct acpi_bit_register_info *sleep_type_reg_info;struct acpi_bit_register_info *sleep_enable_reg_info;u32 in_value; +struct acpi_object_list arg_list; +union acpi_object arg;acpi_status status;ACPI_FUNCTION_TRACE (acpi_enter_sleep_state);
@@ -307,6 +304,18 @@ acpi_status asmlinkage acpi_enter_sleep_return_ACPI_STATUS (status); } +/* Execute the _GTS method */ + + arg_list.count =1 ; + arg_list.pointer = &arg; + arg.type =ACPI_TYPE_INTEGER ; + arg.integer.value = sleep_state; + + status =acpi_evaluate_object (NULL ,METHOD_NAME__GTS , &arg_list,NULL ); +if (ACPI_FAILURE (status) && status !=AE_NOT_FOUND ) { +return_ACPI_STATUS (status); + } +/* Get current value of PM1A control */ status =acpi_hw_register_read (ACPI_REGISTER_PM1_CONTROL , &PM1Acontrol);
@@ -473,17 +482,18 @@ ACPI_EXPORT_SYMBOL(acpi_enter_sleep_stat/******************************************************************************* * -* FUNCTION: acpi_leave_sleep_state +* FUNCTION: acpi_leave_sleep_state_prep * -* PARAMETERS: sleep_state - Which sleep state we just exited +* PARAMETERS: sleep_state - Which sleep state we are exiting * * RETURN: Status * -* DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep -* Called with interrupts ENABLED. +* DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a +* sleep. +* Called with interrupts DISABLED. * ******************************************************************************/ -acpi_status acpi_leave_sleep_state (u8 sleep_state) +acpi_status acpi_leave_sleep_state_prep (u8 sleep_state) {struct acpi_object_list arg_list;union acpi_object arg;
@@ -493,7 +503,7 @@ acpi_status acpi_leave_sleep_state(u8 slu32 PM1Acontrol;u32 PM1Bcontrol; -ACPI_FUNCTION_TRACE (acpi_leave_sleep_state); +ACPI_FUNCTION_TRACE (acpi_leave_sleep_state_prep);/* * Set SLP_TYPE and SLP_EN to state S0.
@@ -540,6 +550,41 @@ acpi_status acpi_leave_sleep_state(u8 sl } } +/* Execute the _BFS method */ + + arg_list.count =1 ; + arg_list.pointer = &arg; + arg.type =ACPI_TYPE_INTEGER ; + arg.integer.value = sleep_state; + + status =acpi_evaluate_object (NULL ,METHOD_NAME__BFS , &arg_list,NULL ); +if (ACPI_FAILURE (status) && status !=AE_NOT_FOUND ) { +ACPI_EXCEPTION ((AE_INFO , status,"During Method _BFS" )); + } + +return_ACPI_STATUS (status); +} + +/******************************************************************************* +* +* FUNCTION: acpi_leave_sleep_state +* +* PARAMETERS: sleep_state - Which sleep state we just exited +* +* RETURN: Status +* +* DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep +* Called with interrupts ENABLED. +* +******************************************************************************/ +acpi_status acpi_leave_sleep_state (u8 sleep_state) +{ +struct acpi_object_list arg_list; +union acpi_object arg; +acpi_status status; + +ACPI_FUNCTION_TRACE (acpi_leave_sleep_state); +/* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */ acpi_gbl_sleep_type_a =ACPI_SLEEP_TYPE_INVALID ;
@@ -558,12 +603,6 @@ acpi_status acpi_leave_sleep_state(u8 slACPI_EXCEPTION ((AE_INFO , status,"During Method _SST" )); } - arg.integer.value = sleep_state; - status =acpi_evaluate_object (NULL ,METHOD_NAME__BFS , &arg_list,NULL ); -if (ACPI_FAILURE (status) && status !=AE_NOT_FOUND ) { -ACPI_EXCEPTION ((AE_INFO , status,"During Method _BFS" )); - } -/* * GPEs must be enabled before _WAK is called as GPEs * might get fired there
drivers/acpi/sleep/main.c +7 -0
@@ -139,6 +139,9 @@ static int acpi_pm_enter(suspend_state_tbreak ; } +/* 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 ]
@@ -272,6 +275,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 ;
@@ -284,6 +289,8 @@ 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 )
include/acpi/acpixf.h +2 -0
@@ -335,6 +335,8 @@ acpi_status asmlinkage acpi_enter_sleep_ acpi_status asmlinkage acpi_enter_sleep_state_s4bios(void); +acpi_status acpi_leave_sleep_state_prep (u8 sleep_state); +acpi_status acpi_leave_sleep_state (u8 sleep_state);#endif /* __ACXFACE_H__ */
PATCH 3/7 ACPI: Separate disabling of GPEs from _PTS
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
The preparation to enter an ACPI system sleep state is now tied to the disabling of GPEs, but the GPEs should not be disabled before suspending devices. Since on ACPI 1.0x systems the _PTS global control method should be executed before suspending devices, we need to disable GPEs separately.
--- drivers/acpi/hardware/hwsleep.c | 4 ---- drivers/acpi/sleep/main.c | 17 +++++++++++++++-- 2 files changed, 15 insertions(+), 6 deletions(-)
drivers/acpi/hardware/hwsleep.c +0 -4
@@ -229,10 +229,6 @@ acpi_status acpi_enter_sleep_state_prep( "While executing method _SST")); } -/* Disable/Clear all GPEs */ - - status =acpi_hw_disable_all_gpes (); -return_ACPI_STATUS (status); }
drivers/acpi/sleep/main.c +15 -2
@@ -91,10 +91,13 @@ static int acpi_pm_open(suspend_state_tstatic int acpi_pm_prepare (void ) { -int error =acpi_sleep_prepare (acpi_target_sleep_state); +int error; + error =acpi_sleep_prepare (acpi_target_sleep_state);if (error) acpi_target_sleep_state =ACPI_STATE_S0 ; +else if (!ACPI_SUCCESS (acpi_hw_disable_all_gpes ())) + error = -EFAULT ;return error; }
@@ -261,7 +264,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 )
@@ -432,6 +444,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 )
PATCH 3/7 ACPI: Separate disabling of GPEs from _PTS
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
The preparation to enter an ACPI system sleep state is now tied to the disabling of GPEs, but the GPEs should not be disabled before suspending devices. Since on ACPI 1.0x systems the _PTS global control method should be executed before suspending devices, we need to disable GPEs separately.
--- drivers/acpi/hardware/hwsleep.c | 4 ---- drivers/acpi/sleep/main.c | 17 +++++++++++++++-- 2 files changed, 15 insertions(+), 6 deletions(-)
drivers/acpi/hardware/hwsleep.c +0 -4
@@ -229,10 +229,6 @@ acpi_status acpi_enter_sleep_state_prep( "While executing method _SST")); } -/* Disable/Clear all GPEs */ - - status =acpi_hw_disable_all_gpes (); -return_ACPI_STATUS (status); }
drivers/acpi/sleep/main.c +15 -2
@@ -91,10 +91,13 @@ static int acpi_pm_open(suspend_state_tstatic int acpi_pm_prepare (void ) { -int error =acpi_sleep_prepare (acpi_target_sleep_state); +int error; + error =acpi_sleep_prepare (acpi_target_sleep_state);if (error) acpi_target_sleep_state =ACPI_STATE_S0 ; +else if (!ACPI_SUCCESS (acpi_hw_disable_all_gpes ())) + error = -EFAULT ;return error; }
@@ -261,7 +264,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 )
@@ -432,6 +444,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 )
PATCH 4/7 ACPI suspend: Call _PTS before suspending devices
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
The ACPI 1.0 specification wants us to put devices into low power states after executing the _PTS global control method, while ACPI 2.0 and later want us to do that in the reverse order. The current suspend code follows ACPI 2.0 in that respect which causes some ACPI 1.0x systems to hang during suspend (ref.
http://bugzilla.kernel.org/show_bug.cgi?id=9528).
Make the suspend code execute _PTS before putting devices into low power states (ie. in accordance with ACPI 1.0x) and provide a command line option to override the default if need be.
--- Documentation/kernel-parameters.txt | 5 +++ drivers/acpi/sleep/main.c | 51 ++++++++++++++++++++++++++---------- 2 files changed, 43 insertions(+), 13 deletions(-)
drivers/acpi/sleep/main.c +38 -13
@@ -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)
@@ -74,6 +89,14 @@ static int acpi_pm_begin(suspend_state_tif (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,15 +114,17 @@ static int acpi_pm_begin(suspend_state_tstatic int acpi_pm_prepare (void ) { -int error; +if (new_pts_ordering) { +int error =acpi_sleep_prepare (acpi_target_sleep_state); - error =acpi_sleep_prepare (acpi_target_sleep_state); -if (error) - acpi_target_sleep_state =ACPI_STATE_S0 ; -else if (!ACPI_SUCCESS (acpi_hw_disable_all_gpes ())) - error = -EFAULT ; +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 ; }/**
@@ -123,10 +148,8 @@ static int acpi_pm_enter(suspend_state_tif (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);
@@ -187,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) {
@@ -203,10 +227,11 @@ static void acpi_pm_finish(void)static void acpi_pm_end (void ) {/* -* This is necessary in case acpi_pm_finish() is not called during a -* failing transition to a sleep state. +* This is necessary in case acpi_pm_finish() is not called directly +* during a failing transition to a sleep state. */ - acpi_target_sleep_state =ACPI_STATE_S0 ; +if (acpi_sleep_finish_wake_up) +acpi_pm_finish (); }static int acpi_pm_state_valid (suspend_state_t pm_state)
Documentation/kernel-parameters.txt +5 -0
@@ -167,6 +167,11 @@ and is between 256 and 4096 characters. acpi_irq_isa= [HW,ACPI] If irq_balance, mark listed IRQs used by ISA Format: <irq>,<irq>... + acpi_new_pts_ordering [HW,ACPI] + Enforce the ACPI 2.0 ordering of the _PTS control + method wrt putting devices into low power states + default: pre ACPI 2.0 ordering of _PTS + acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS
PATCH 4/7 ACPI suspend: Call _PTS before suspending devices
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
The ACPI 1.0 specification wants us to put devices into low power states after executing the _PTS global control method, while ACPI 2.0 and later want us to do that in the reverse order. The current suspend code follows ACPI 2.0 in that respect which causes some ACPI 1.0x systems to hang during suspend (ref.
http://bugzilla.kernel.org/show_bug.cgi?id=9528).
Make the suspend code execute _PTS before putting devices into low power states (ie. in accordance with ACPI 1.0x) and provide a command line option to override the default if need be.
--- Documentation/kernel-parameters.txt | 5 +++ drivers/acpi/sleep/main.c | 51 ++++++++++++++++++++++++++---------- 2 files changed, 43 insertions(+), 13 deletions(-)
drivers/acpi/sleep/main.c +38 -13
@@ -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)
@@ -74,6 +89,14 @@ static int acpi_pm_begin(suspend_state_tif (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,15 +114,17 @@ static int acpi_pm_begin(suspend_state_tstatic int acpi_pm_prepare (void ) { -int error; +if (new_pts_ordering) { +int error =acpi_sleep_prepare (acpi_target_sleep_state); - error =acpi_sleep_prepare (acpi_target_sleep_state); -if (error) - acpi_target_sleep_state =ACPI_STATE_S0 ; -else if (!ACPI_SUCCESS (acpi_hw_disable_all_gpes ())) - error = -EFAULT ; +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 ; }/**
@@ -123,10 +148,8 @@ static int acpi_pm_enter(suspend_state_tif (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);
@@ -187,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) {
@@ -203,10 +227,11 @@ static void acpi_pm_finish(void)static void acpi_pm_end (void ) {/* -* This is necessary in case acpi_pm_finish() is not called during a -* failing transition to a sleep state. +* This is necessary in case acpi_pm_finish() is not called directly +* during a failing transition to a sleep state. */ - acpi_target_sleep_state =ACPI_STATE_S0 ; +if (acpi_sleep_finish_wake_up) +acpi_pm_finish (); }static int acpi_pm_state_valid (suspend_state_t pm_state)
Documentation/kernel-parameters.txt +5 -0
@@ -167,6 +167,11 @@ and is between 256 and 4096 characters. acpi_irq_isa= [HW,ACPI] If irq_balance, mark listed IRQs used by ISA Format: <irq>,<irq>... + acpi_new_pts_ordering [HW,ACPI] + Enforce the ACPI 2.0 ordering of the _PTS control + method wrt putting devices into low power states + default: pre ACPI 2.0 ordering of _PTS + acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS
PATCH 5/7 Hibernation: Introduce begin() and end() callbacks
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Introduce global hibernation callback .end() and rename global hibernation callback .start() to .begin(), in analogy with the recent modifications of the global suspend callbacks.
--- drivers/acpi/sleep/main.c | 14 ++++++++++++-- include/linux/suspend.h | 14 +++++++++----- kernel/power/disk.c | 33 ++++++++++++++++++++++++--------- 3 files changed, 45 insertions(+), 16 deletions(-)
include/linux/suspend.h +9 -5
@@ -136,14 +136,17 @@ extern void mark_free_pages(struct zone/** * struct platform_hibernation_ops - hibernation platform support * -* The methods in this structure allow a platform to override the default -* mechanism of shutting down the machine during a hibernation transition. +* The methods in this structure allow a platform to carry out special +* operations required by it during a hibernation transition. * -* All three methods must be assigned. +* All the methods below must be implemented. * -* @start: Tell the platform driver that we're starting hibernation. +* @begin: Tell the platform driver that we're starting hibernation. * Called right after shrinking memory and before freezing devices. * +* @end: Called by the PM core right after resuming devices, to indicate to +* the platform that the system has returned to the working state. +* * @pre_snapshot: Prepare the platform for creating the hibernation image. * Called right after devices have been frozen and before the nonboot * CPUs are disabled (runs with IRQs on).
@@ -178,7 +181,8 @@ extern void mark_free_pages(struct zone* thawing devices (runs with IRQs on). */ struct platform_hibernation_ops { -int (*start)(void ); +int (*begin)(void ); +void (*end)(void );int (*pre_snapshot)(void );void (*finish)(void );int (*prepare)(void );
kernel/power/disk.c +24 -9
@@ -54,8 +54,8 @@ static struct platform_hibernation_ops *void hibernation_set_ops (struct platform_hibernation_ops *ops) { -if (ops && !(ops->start && ops->pre_snapshot && ops->finish - && ops->prepare && ops->enter && ops->pre_restore +if (ops && !(ops->begin && ops->end && ops->pre_snapshot + && ops->prepare && ops->finish && ops->enter && ops->pre_restore && ops->restore_cleanup)) {WARN_ON (1 );return ;
@@ -100,14 +100,25 @@ static int hibernation_test(int level) {#endif /* !CONFIG_PM_DEBUG */ /** -* platform_start - tell the platform driver that we're starting +* platform_begin - tell the platform driver that we're starting * hibernation */ -static int platform_start (int platform_mode) +static int platform_begin (int platform_mode) {return (platform_mode && hibernation_ops) ? - hibernation_ops->start () :0 ; + hibernation_ops->begin () :0 ; +} + +/** +* platform_end - tell the platform driver that we've entered the +* working state +*/ + +static void platform_end (int platform_mode) +{ +if (platform_mode && hibernation_ops) + hibernation_ops->end (); }/**
@@ -237,9 +248,9 @@ int hibernation_snapshot(int platform_moif (error)return error; - error =platform_start (platform_mode); + error =platform_begin (platform_mode);if (error) -return error; + goto Close;suspend_console (); error =device_suspend (PMSG_FREEZE );
@@ -272,6 +283,8 @@ int hibernation_snapshot(int platform_modevice_resume (); Resume_console:resume_console (); + Close: +platform_end (platform_mode);return error; }
@@ -373,9 +386,9 @@ int hibernation_platform_enter(void)* hibernation_ops->finish() before saving the image, so we should let * the firmware know that we're going to enter the sleep state after all */ - error = hibernation_ops->start (); + error = hibernation_ops->begin ();if (error) -return error; + goto Close;suspend_console (); error =device_suspend (PMSG_SUSPEND );
@@ -409,6 +422,8 @@ int hibernation_platform_enter(void)device_resume (); Resume_console:resume_console (); + Close: + hibernation_ops->end ();return error; }
drivers/acpi/sleep/main.c +12 -2
@@ -281,7 +281,7 @@ static struct dmi_system_id __initdata a#endif /* CONFIG_SUSPEND */ #ifdef CONFIG_HIBERNATION -static int acpi_hibernation_start (void ) +static int acpi_hibernation_begin (void ) { acpi_target_sleep_state =ACPI_STATE_S4 ;return 0 ;
@@ -341,6 +341,15 @@ static void acpi_hibernation_finish(void acpi_target_sleep_state =ACPI_STATE_S0 ; } +static void acpi_hibernation_end (void ) +{ +/* +* This is necessary in case acpi_hibernation_finish() is not called +* during a failing transition to the sleep state. +*/ + acpi_target_sleep_state =ACPI_STATE_S0 ; +} +static int acpi_hibernation_pre_restore (void ) {acpi_status status;
@@ -356,7 +365,8 @@ static void acpi_hibernation_restore_cle }static struct platform_hibernation_ops acpi_hibernation_ops = { - .start = acpi_hibernation_start, + .begin = acpi_hibernation_begin, + .end = acpi_hibernation_end, .pre_snapshot = acpi_hibernation_prepare, .finish = acpi_hibernation_finish, .prepare = acpi_hibernation_prepare,
PATCH 5/7 Hibernation: Introduce begin() and end() callbacks
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Introduce global hibernation callback .end() and rename global hibernation callback .start() to .begin(), in analogy with the recent modifications of the global suspend callbacks.
--- drivers/acpi/sleep/main.c | 14 ++++++++++++-- include/linux/suspend.h | 14 +++++++++----- kernel/power/disk.c | 33 ++++++++++++++++++++++++--------- 3 files changed, 45 insertions(+), 16 deletions(-)
include/linux/suspend.h +9 -5
@@ -136,14 +136,17 @@ extern void mark_free_pages(struct zone/** * struct platform_hibernation_ops - hibernation platform support * -* The methods in this structure allow a platform to override the default -* mechanism of shutting down the machine during a hibernation transition. +* The methods in this structure allow a platform to carry out special +* operations required by it during a hibernation transition. * -* All three methods must be assigned. +* All the methods below must be implemented. * -* @start: Tell the platform driver that we're starting hibernation. +* @begin: Tell the platform driver that we're starting hibernation. * Called right after shrinking memory and before freezing devices. * +* @end: Called by the PM core right after resuming devices, to indicate to +* the platform that the system has returned to the working state. +* * @pre_snapshot: Prepare the platform for creating the hibernation image. * Called right after devices have been frozen and before the nonboot * CPUs are disabled (runs with IRQs on).
@@ -178,7 +181,8 @@ extern void mark_free_pages(struct zone* thawing devices (runs with IRQs on). */ struct platform_hibernation_ops { -int (*start)(void ); +int (*begin)(void ); +void (*end)(void );int (*pre_snapshot)(void );void (*finish)(void );int (*prepare)(void );
kernel/power/disk.c +24 -9
@@ -54,8 +54,8 @@ static struct platform_hibernation_ops *void hibernation_set_ops (struct platform_hibernation_ops *ops) { -if (ops && !(ops->start && ops->pre_snapshot && ops->finish - && ops->prepare && ops->enter && ops->pre_restore +if (ops && !(ops->begin && ops->end && ops->pre_snapshot + && ops->prepare && ops->finish && ops->enter && ops->pre_restore && ops->restore_cleanup)) {WARN_ON (1 );return ;
@@ -100,14 +100,25 @@ static int hibernation_test(int level) {#endif /* !CONFIG_PM_DEBUG */ /** -* platform_start - tell the platform driver that we're starting +* platform_begin - tell the platform driver that we're starting * hibernation */ -static int platform_start (int platform_mode) +static int platform_begin (int platform_mode) {return (platform_mode && hibernation_ops) ? - hibernation_ops->start () :0 ; + hibernation_ops->begin () :0 ; +} + +/** +* platform_end - tell the platform driver that we've entered the +* working state +*/ + +static void platform_end (int platform_mode) +{ +if (platform_mode && hibernation_ops) + hibernation_ops->end (); }/**
@@ -237,9 +248,9 @@ int hibernation_snapshot(int platform_moif (error)return error; - error =platform_start (platform_mode); + error =platform_begin (platform_mode);if (error) -return error; + goto Close;suspend_console (); error =device_suspend (PMSG_FREEZE );
@@ -272,6 +283,8 @@ int hibernation_snapshot(int platform_modevice_resume (); Resume_console:resume_console (); + Close: +platform_end (platform_mode);return error; }
@@ -373,9 +386,9 @@ int hibernation_platform_enter(void)* hibernation_ops->finish() before saving the image, so we should let * the firmware know that we're going to enter the sleep state after all */ - error = hibernation_ops->start (); + error = hibernation_ops->begin ();if (error) -return error; + goto Close;suspend_console (); error =device_suspend (PMSG_SUSPEND );
@@ -409,6 +422,8 @@ int hibernation_platform_enter(void)device_resume (); Resume_console:resume_console (); + Close: + hibernation_ops->end ();return error; }
drivers/acpi/sleep/main.c +12 -2
@@ -281,7 +281,7 @@ static struct dmi_system_id __initdata a#endif /* CONFIG_SUSPEND */ #ifdef CONFIG_HIBERNATION -static int acpi_hibernation_start (void ) +static int acpi_hibernation_begin (void ) { acpi_target_sleep_state =ACPI_STATE_S4 ;return 0 ;
@@ -341,6 +341,15 @@ static void acpi_hibernation_finish(void acpi_target_sleep_state =ACPI_STATE_S0 ; } +static void acpi_hibernation_end (void ) +{ +/* +* This is necessary in case acpi_hibernation_finish() is not called +* during a failing transition to the sleep state. +*/ + acpi_target_sleep_state =ACPI_STATE_S0 ; +} +static int acpi_hibernation_pre_restore (void ) {acpi_status status;
@@ -356,7 +365,8 @@ static void acpi_hibernation_restore_cle }static struct platform_hibernation_ops acpi_hibernation_ops = { - .start = acpi_hibernation_start, + .begin = acpi_hibernation_begin, + .end = acpi_hibernation_end, .pre_snapshot = acpi_hibernation_prepare, .finish = acpi_hibernation_finish, .prepare = acpi_hibernation_prepare,
PATCH 6/7 ACPI hibernation: Call _PTS before suspending devices
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
The ACPI 1.0 specification wants us to put devices into low power states after executing the _PTS global control method, while ACPI 2.0 and later want us to do that in the reverse order. The current hibernation code follows ACPI 2.0 in that respect which may cause some ACPI 1.0x systems to hang during hibernation (ref.
http://bugzilla.kernel.org/show_bug.cgi?id=9528).
Make the hibernation code execute _PTS before putting devices into low power states (ie. in accordance with ACPI 1.0x) with the possibility to override that using the 'acpi_new_pts_ordering' kernel command line option.
--- drivers/acpi/sleep/main.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-)
drivers/acpi/sleep/main.c +25 -11
@@ -283,22 +283,34 @@ static struct dmi_system_id __initdata a#ifdef CONFIG_HIBERNATION static int acpi_hibernation_begin (void ) { +int error; + acpi_target_sleep_state =ACPI_STATE_S4 ; -return 0 ; +if (new_pts_ordering) +return 0 ; + + error =acpi_sleep_prepare (ACPI_STATE_S4 ); +if (error) + acpi_target_sleep_state =ACPI_STATE_S0 ; +else + acpi_sleep_finish_wake_up = true; + +return error; }static int acpi_hibernation_prepare (void ) { -int error; - - error =acpi_sleep_prepare (ACPI_STATE_S4 ); -if (error) -return error; +if (new_pts_ordering) { +int error =acpi_sleep_prepare (ACPI_STATE_S4 ); -if (!ACPI_SUCCESS (acpi_hw_disable_all_gpes ())) - error = -EFAULT ; +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 ; }static int acpi_hibernation_enter (void )
@@ -339,15 +351,17 @@ static void acpi_hibernation_finish(voidacpi_set_firmware_waking_vector ((acpi_physical_address )0 ); acpi_target_sleep_state =ACPI_STATE_S0 ; + acpi_sleep_finish_wake_up = false; }static void acpi_hibernation_end (void ) {/* * This is necessary in case acpi_hibernation_finish() is not called -* during a failing transition to the sleep state. +* directly during a failing transition to the sleep state. */ - acpi_target_sleep_state =ACPI_STATE_S0 ; +if (acpi_sleep_finish_wake_up) +acpi_hibernation_finish (); }static int acpi_hibernation_pre_restore (void )
PATCH 6/7 ACPI hibernation: Call _PTS before suspending devices
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
The ACPI 1.0 specification wants us to put devices into low power states after executing the _PTS global control method, while ACPI 2.0 and later want us to do that in the reverse order. The current hibernation code follows ACPI 2.0 in that respect which may cause some ACPI 1.0x systems to hang during hibernation (ref.
http://bugzilla.kernel.org/show_bug.cgi?id=9528).
Make the hibernation code execute _PTS before putting devices into low power states (ie. in accordance with ACPI 1.0x) with the possibility to override that using the 'acpi_new_pts_ordering' kernel command line option.
--- drivers/acpi/sleep/main.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-)
drivers/acpi/sleep/main.c +25 -11
@@ -283,22 +283,34 @@ static struct dmi_system_id __initdata a#ifdef CONFIG_HIBERNATION static int acpi_hibernation_begin (void ) { +int error; + acpi_target_sleep_state =ACPI_STATE_S4 ; -return 0 ; +if (new_pts_ordering) +return 0 ; + + error =acpi_sleep_prepare (ACPI_STATE_S4 ); +if (error) + acpi_target_sleep_state =ACPI_STATE_S0 ; +else + acpi_sleep_finish_wake_up = true; + +return error; }static int acpi_hibernation_prepare (void ) { -int error; - - error =acpi_sleep_prepare (ACPI_STATE_S4 ); -if (error) -return error; +if (new_pts_ordering) { +int error =acpi_sleep_prepare (ACPI_STATE_S4 ); -if (!ACPI_SUCCESS (acpi_hw_disable_all_gpes ())) - error = -EFAULT ; +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 ; }static int acpi_hibernation_enter (void )
@@ -339,15 +351,17 @@ static void acpi_hibernation_finish(voidacpi_set_firmware_waking_vector ((acpi_physical_address )0 ); acpi_target_sleep_state =ACPI_STATE_S0 ; + acpi_sleep_finish_wake_up = false; }static void acpi_hibernation_end (void ) {/* * This is necessary in case acpi_hibernation_finish() is not called -* during a failing transition to the sleep state. +* directly during a failing transition to the sleep state. */ - acpi_target_sleep_state =ACPI_STATE_S0 ; +if (acpi_sleep_finish_wake_up) +acpi_hibernation_finish (); }static int acpi_hibernation_pre_restore (void )
PATCH 7/7 ACPI: Print message before calling _PTS
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Make acpi_sleep_prepare() static and cause it to print a message specifying the ACPI system sleep state to be entered (helpful for debugging the suspend/hibernation code).
--- drivers/acpi/sleep/main.c | 4 +++- drivers/acpi/sleep/sleep.h | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-)
drivers/acpi/sleep/main.c +3 -1
@@ -43,7 +43,7 @@ static int __init acpi_new_pts_ordering(__setup ("acpi_new_pts_ordering" , acpi_new_pts_ordering);#endif -int acpi_sleep_prepare (u32 acpi_state) +static int acpi_sleep_prepare (u32 acpi_state) {#ifdef CONFIG_ACPI_SLEEP /* do we have a wakeup address for S2 and S3? */
@@ -59,6 +59,8 @@ int acpi_sleep_prepare(u32 acpi_state)ACPI_FLUSH_CPU_CACHE ();acpi_enable_wakeup_device_prep (acpi_state);#endif + printk(KERN_INFO PREFIX "Preparing to enter system sleep state S%d\n", + acpi_state);acpi_enter_sleep_state_prep (acpi_state);return 0 ; }
drivers/acpi/sleep/sleep.h +0 -2
@@ -5,5 +5,3 @@ extern int acpi_suspend (u32 state);extern void acpi_enable_wakeup_device_prep (u8 sleep_state);extern void acpi_enable_wakeup_device (u8 sleep_state);extern void acpi_disable_wakeup_device (u8 sleep_state); - -extern int acpi_sleep_prepare (u32 acpi_state);
PATCH 7/7 ACPI: Print message before calling _PTS
In reply to Rafael J. WysockiFrom: Rafael J. Wysocki <[email protected]>
Make acpi_sleep_prepare() static and cause it to print a message specifying the ACPI system sleep state to be entered (helpful for debugging the suspend/hibernation code).
--- drivers/acpi/sleep/main.c | 4 +++- drivers/acpi/sleep/sleep.h | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-)
drivers/acpi/sleep/main.c +3 -1
@@ -43,7 +43,7 @@ static int __init acpi_new_pts_ordering(__setup ("acpi_new_pts_ordering" , acpi_new_pts_ordering);#endif -int acpi_sleep_prepare (u32 acpi_state) +static int acpi_sleep_prepare (u32 acpi_state) {#ifdef CONFIG_ACPI_SLEEP /* do we have a wakeup address for S2 and S3? */
@@ -59,6 +59,8 @@ int acpi_sleep_prepare(u32 acpi_state)ACPI_FLUSH_CPU_CACHE ();acpi_enable_wakeup_device_prep (acpi_state);#endif + printk(KERN_INFO PREFIX "Preparing to enter system sleep state S%d\n", + acpi_state);acpi_enter_sleep_state_prep (acpi_state);return 0 ; }
drivers/acpi/sleep/sleep.h +0 -2
@@ -5,5 +5,3 @@ extern int acpi_suspend (u32 state);extern void acpi_enable_wakeup_device_prep (u8 sleep_state);extern void acpi_enable_wakeup_device (u8 sleep_state);extern void acpi_disable_wakeup_device (u8 sleep_state); - -extern int acpi_sleep_prepare (u32 acpi_state);