Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Suspend: Introduce begin() and end() callbacks

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().

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Len Brown <len.brown@intel.com>

authored by

Rafael J. Wysocki and committed by
Len Brown
c697eece 7671b8ae

+63 -24
+13 -4
arch/arm/mach-at91/pm.c
··· 52 /* 53 * Called after processes are frozen, but before we shutdown devices. 54 */ 55 - static int at91_pm_set_target(suspend_state_t state) 56 { 57 target_state = state; 58 return 0; ··· 202 return 0; 203 } 204 205 206 static struct platform_suspend_ops at91_pm_ops ={ 207 - .valid = at91_pm_valid_state, 208 - .set_target = at91_pm_set_target, 209 - .enter = at91_pm_enter, 210 }; 211 212 static int __init at91_pm_init(void)
··· 52 /* 53 * Called after processes are frozen, but before we shutdown devices. 54 */ 55 + static int at91_pm_begin(suspend_state_t state) 56 { 57 target_state = state; 58 return 0; ··· 202 return 0; 203 } 204 205 + /* 206 + * Called right prior to thawing processes. 207 + */ 208 + static void at91_pm_end(void) 209 + { 210 + target_state = PM_SUSPEND_ON; 211 + } 212 + 213 214 static struct platform_suspend_ops at91_pm_ops ={ 215 + .valid = at91_pm_valid_state, 216 + .begin = at91_pm_begin, 217 + .enter = at91_pm_enter, 218 + .end = at91_pm_end, 219 }; 220 221 static int __init at91_pm_init(void)
+8 -2
arch/powerpc/platforms/52xx/lite5200_pm.c
··· 31 } 32 } 33 34 - static int lite5200_pm_set_target(suspend_state_t state) 35 { 36 if (lite5200_pm_valid(state)) { 37 lite5200_pm_target_state = state; ··· 219 mpc52xx_pm_finish(); 220 } 221 222 static struct platform_suspend_ops lite5200_pm_ops = { 223 .valid = lite5200_pm_valid, 224 - .set_target = lite5200_pm_set_target, 225 .prepare = lite5200_pm_prepare, 226 .enter = lite5200_pm_enter, 227 .finish = lite5200_pm_finish, 228 }; 229 230 int __init lite5200_pm_init(void)
··· 31 } 32 } 33 34 + static int lite5200_pm_begin(suspend_state_t state) 35 { 36 if (lite5200_pm_valid(state)) { 37 lite5200_pm_target_state = state; ··· 219 mpc52xx_pm_finish(); 220 } 221 222 + static void lite5200_pm_end(void) 223 + { 224 + lite5200_pm_target_state = PM_SUSPEND_ON; 225 + } 226 + 227 static struct platform_suspend_ops lite5200_pm_ops = { 228 .valid = lite5200_pm_valid, 229 + .begin = lite5200_pm_begin, 230 .prepare = lite5200_pm_prepare, 231 .enter = lite5200_pm_enter, 232 .finish = lite5200_pm_finish, 233 + .end = lite5200_pm_end, 234 }; 235 236 int __init lite5200_pm_init(void)
+18 -4
drivers/acpi/sleep/main.c
··· 63 static int init_8259A_after_S1; 64 65 /** 66 - * acpi_pm_set_target - Set the target system sleep state to the state 67 * associated with given @pm_state, if supported. 68 */ 69 70 - static int acpi_pm_set_target(suspend_state_t pm_state) 71 { 72 u32 acpi_state = acpi_suspend_states[pm_state]; 73 int error = 0; ··· 164 } 165 166 /** 167 - * acpi_pm_finish - Finish up suspend sequence. 168 * 169 * This is called after we wake back up (or if entering the sleep state 170 * failed). ··· 190 #endif 191 } 192 193 static int acpi_pm_state_valid(suspend_state_t pm_state) 194 { 195 u32 acpi_state; ··· 221 222 static struct platform_suspend_ops acpi_pm_ops = { 223 .valid = acpi_pm_state_valid, 224 - .set_target = acpi_pm_set_target, 225 .prepare = acpi_pm_prepare, 226 .enter = acpi_pm_enter, 227 .finish = acpi_pm_finish, 228 }; 229 230 /*
··· 63 static int init_8259A_after_S1; 64 65 /** 66 + * acpi_pm_begin - Set the target system sleep state to the state 67 * associated with given @pm_state, if supported. 68 */ 69 70 + static int acpi_pm_begin(suspend_state_t pm_state) 71 { 72 u32 acpi_state = acpi_suspend_states[pm_state]; 73 int error = 0; ··· 164 } 165 166 /** 167 + * acpi_pm_finish - Instruct the platform to leave a sleep state. 168 * 169 * This is called after we wake back up (or if entering the sleep state 170 * failed). ··· 190 #endif 191 } 192 193 + /** 194 + * acpi_pm_end - Finish up suspend sequence. 195 + */ 196 + 197 + static void acpi_pm_end(void) 198 + { 199 + /* 200 + * This is necessary in case acpi_pm_finish() is not called during a 201 + * failing transition to a sleep state. 202 + */ 203 + acpi_target_sleep_state = ACPI_STATE_S0; 204 + } 205 + 206 static int acpi_pm_state_valid(suspend_state_t pm_state) 207 { 208 u32 acpi_state; ··· 208 209 static struct platform_suspend_ops acpi_pm_ops = { 210 .valid = acpi_pm_state_valid, 211 + .begin = acpi_pm_begin, 212 .prepare = acpi_pm_prepare, 213 .enter = acpi_pm_enter, 214 .finish = acpi_pm_finish, 215 + .end = acpi_pm_end, 216 }; 217 218 /*
+18 -11
include/linux/suspend.h
··· 38 * There is the %suspend_valid_only_mem function available that can be 39 * assigned to this if the platform only supports mem sleep. 40 * 41 - * @set_target: Tell the platform which system sleep state is going to be 42 - * entered. 43 - * @set_target() is executed right prior to suspending devices. The 44 - * information conveyed to the platform code by @set_target() should be 45 - * disregarded by the platform as soon as @finish() is executed and if 46 - * @prepare() fails. If @set_target() fails (ie. returns nonzero), 47 * @prepare(), @enter() and @finish() will not be called by the PM core. 48 * This callback is optional. However, if it is implemented, the argument 49 - * passed to @enter() is meaningless and should be ignored. 50 * 51 * @prepare: Prepare the platform for entering the system sleep state indicated 52 - * by @set_target(). 53 * @prepare() is called right after devices have been suspended (ie. the 54 * appropriate .suspend() method has been executed for each device) and 55 * before the nonboot CPUs are disabled (it is executed with IRQs enabled). ··· 55 * error code otherwise, in which case the system cannot enter the desired 56 * sleep state (@enter() and @finish() will not be called in that case). 57 * 58 - * @enter: Enter the system sleep state indicated by @set_target() or 59 - * represented by the argument if @set_target() is not implemented. 60 * This callback is mandatory. It returns 0 on success or a negative 61 * error code otherwise, in which case the system cannot enter the desired 62 * sleep state. ··· 67 * This callback is optional, but should be implemented by the platforms 68 * that implement @prepare(). If implemented, it is always called after 69 * @enter() (even if @enter() fails). 70 */ 71 struct platform_suspend_ops { 72 int (*valid)(suspend_state_t state); 73 - int (*set_target)(suspend_state_t state); 74 int (*prepare)(void); 75 int (*enter)(suspend_state_t state); 76 void (*finish)(void); 77 }; 78 79 #ifdef CONFIG_SUSPEND
··· 38 * There is the %suspend_valid_only_mem function available that can be 39 * assigned to this if the platform only supports mem sleep. 40 * 41 + * @begin: Initialise a transition to given system sleep state. 42 + * @begin() is executed right prior to suspending devices. The information 43 + * conveyed to the platform code by @begin() should be disregarded by it as 44 + * soon as @end() is executed. If @begin() fails (ie. returns nonzero), 45 * @prepare(), @enter() and @finish() will not be called by the PM core. 46 * This callback is optional. However, if it is implemented, the argument 47 + * passed to @enter() is redundant and should be ignored. 48 * 49 * @prepare: Prepare the platform for entering the system sleep state indicated 50 + * by @begin(). 51 * @prepare() is called right after devices have been suspended (ie. the 52 * appropriate .suspend() method has been executed for each device) and 53 * before the nonboot CPUs are disabled (it is executed with IRQs enabled). ··· 57 * error code otherwise, in which case the system cannot enter the desired 58 * sleep state (@enter() and @finish() will not be called in that case). 59 * 60 + * @enter: Enter the system sleep state indicated by @begin() or represented by 61 + * the argument if @begin() is not implemented. 62 * This callback is mandatory. It returns 0 on success or a negative 63 * error code otherwise, in which case the system cannot enter the desired 64 * sleep state. ··· 69 * This callback is optional, but should be implemented by the platforms 70 * that implement @prepare(). If implemented, it is always called after 71 * @enter() (even if @enter() fails). 72 + * 73 + * @end: Called by the PM core right after resuming devices, to indicate to 74 + * the platform that the system has returned to the working state or 75 + * the transition to the sleep state has been aborted. 76 + * This callback is optional, but should be implemented by the platforms 77 + * that implement @begin(), but platforms implementing @begin() should 78 + * also provide a @end() which cleans up transitions aborted before 79 + * @enter(). 80 */ 81 struct platform_suspend_ops { 82 int (*valid)(suspend_state_t state); 83 + int (*begin)(suspend_state_t state); 84 int (*prepare)(void); 85 int (*enter)(suspend_state_t state); 86 void (*finish)(void); 87 + void (*end)(void); 88 }; 89 90 #ifdef CONFIG_SUSPEND
+6 -3
kernel/power/main.c
··· 258 if (!suspend_ops) 259 return -ENOSYS; 260 261 - if (suspend_ops->set_target) { 262 - error = suspend_ops->set_target(state); 263 if (error) 264 - return error; 265 } 266 suspend_console(); 267 error = device_suspend(PMSG_SUSPEND); ··· 294 device_resume(); 295 Resume_console: 296 resume_console(); 297 return error; 298 } 299
··· 258 if (!suspend_ops) 259 return -ENOSYS; 260 261 + if (suspend_ops->begin) { 262 + error = suspend_ops->begin(state); 263 if (error) 264 + goto Close; 265 } 266 suspend_console(); 267 error = device_suspend(PMSG_SUSPEND); ··· 294 device_resume(); 295 Resume_console: 296 resume_console(); 297 + Close: 298 + if (suspend_ops->end) 299 + suspend_ops->end(); 300 return error; 301 } 302