Skip to content

Commit 6a7c7ea

Browse files
committed
PM/Suspend: Introduce two new platform callbacks to avoid breakage
Commit 900af0d (PM: Change suspend code ordering) changed the ordering of suspend code in such a way that the platform .prepare() callback is now executed after the device drivers' late suspend callbacks have run. Unfortunately, this turns out to break ARM platforms that need to talk via I2C to power control devices during the .prepare() callback. For this reason introduce two new platform suspend callbacks, .prepare_late() and .wake(), that will be called just prior to disabling non-boot CPUs and right after bringing them back on line, respectively, and use them instead of .prepare() and .finish() for ACPI suspend. Make the PM core execute the .prepare() and .finish() platform suspend callbacks where they were executed previously (that is, right after calling the regular suspend methods provided by device drivers and right before executing their regular resume methods, respectively). It is not necessary to make analogous changes to the hibernation code and data structures at the moment, because they are only used by ACPI platforms. Signed-off-by: Rafael J. Wysocki <[email protected]> Reported-by: Russell King <[email protected]> Acked-by: Len Brown <[email protected]>
1 parent ff54250 commit 6a7c7ea

File tree

3 files changed

+47
-21
lines changed

3 files changed

+47
-21
lines changed

drivers/acpi/sleep.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,9 @@ static int acpi_suspend_state_valid(suspend_state_t pm_state)
300300
static struct platform_suspend_ops acpi_suspend_ops = {
301301
.valid = acpi_suspend_state_valid,
302302
.begin = acpi_suspend_begin,
303-
.prepare = acpi_pm_prepare,
303+
.prepare_late = acpi_pm_prepare,
304304
.enter = acpi_suspend_enter,
305-
.finish = acpi_pm_finish,
305+
.wake = acpi_pm_finish,
306306
.end = acpi_pm_end,
307307
};
308308

@@ -328,9 +328,9 @@ static int acpi_suspend_begin_old(suspend_state_t pm_state)
328328
static struct platform_suspend_ops acpi_suspend_ops_old = {
329329
.valid = acpi_suspend_state_valid,
330330
.begin = acpi_suspend_begin_old,
331-
.prepare = acpi_pm_disable_gpes,
331+
.prepare_late = acpi_pm_disable_gpes,
332332
.enter = acpi_suspend_enter,
333-
.finish = acpi_pm_finish,
333+
.wake = acpi_pm_finish,
334334
.end = acpi_pm_end,
335335
.recover = acpi_pm_finish,
336336
};

include/linux/suspend.h

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,30 +58,44 @@ typedef int __bitwise suspend_state_t;
5858
* by @begin().
5959
* @prepare() is called right after devices have been suspended (ie. the
6060
* appropriate .suspend() method has been executed for each device) and
61-
* before the nonboot CPUs are disabled (it is executed with IRQs enabled).
62-
* This callback is optional. It returns 0 on success or a negative
63-
* error code otherwise, in which case the system cannot enter the desired
64-
* sleep state (@enter() and @finish() will not be called in that case).
61+
* before device drivers' late suspend callbacks are executed. It returns
62+
* 0 on success or a negative error code otherwise, in which case the
63+
* system cannot enter the desired sleep state (@prepare_late(), @enter(),
64+
* @wake(), and @finish() will not be called in that case).
65+
*
66+
* @prepare_late: Finish preparing the platform for entering the system sleep
67+
* state indicated by @begin().
68+
* @prepare_late is called before disabling nonboot CPUs and after
69+
* device drivers' late suspend callbacks have been executed. It returns
70+
* 0 on success or a negative error code otherwise, in which case the
71+
* system cannot enter the desired sleep state (@enter() and @wake()).
6572
*
6673
* @enter: Enter the system sleep state indicated by @begin() or represented by
6774
* the argument if @begin() is not implemented.
6875
* This callback is mandatory. It returns 0 on success or a negative
6976
* error code otherwise, in which case the system cannot enter the desired
7077
* sleep state.
7178
*
72-
* @finish: Called when the system has just left a sleep state, right after
73-
* the nonboot CPUs have been enabled and before devices are resumed (it is
74-
* executed with IRQs enabled).
79+
* @wake: Called when the system has just left a sleep state, right after
80+
* the nonboot CPUs have been enabled and before device drivers' early
81+
* resume callbacks are executed.
82+
* This callback is optional, but should be implemented by the platforms
83+
* that implement @prepare_late(). If implemented, it is always called
84+
* after @enter(), even if @enter() fails.
85+
*
86+
* @finish: Finish wake-up of the platform.
87+
* @finish is called right prior to calling device drivers' regular suspend
88+
* callbacks.
7589
* This callback is optional, but should be implemented by the platforms
7690
* that implement @prepare(). If implemented, it is always called after
77-
* @enter() (even if @enter() fails).
91+
* @enter() and @wake(), if implemented, even if any of them fails.
7892
*
7993
* @end: Called by the PM core right after resuming devices, to indicate to
8094
* the platform that the system has returned to the working state or
8195
* the transition to the sleep state has been aborted.
8296
* This callback is optional, but should be implemented by the platforms
83-
* that implement @begin(), but platforms implementing @begin() should
84-
* also provide a @end() which cleans up transitions aborted before
97+
* that implement @begin(). Accordingly, platforms implementing @begin()
98+
* should also provide a @end() which cleans up transitions aborted before
8599
* @enter().
86100
*
87101
* @recover: Recover the platform from a suspend failure.
@@ -93,7 +107,9 @@ struct platform_suspend_ops {
93107
int (*valid)(suspend_state_t state);
94108
int (*begin)(suspend_state_t state);
95109
int (*prepare)(void);
110+
int (*prepare_late)(void);
96111
int (*enter)(suspend_state_t state);
112+
void (*wake)(void);
97113
void (*finish)(void);
98114
void (*end)(void);
99115
void (*recover)(void);

kernel/power/main.c

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -291,20 +291,26 @@ static int suspend_enter(suspend_state_t state)
291291

292292
device_pm_lock();
293293

294+
if (suspend_ops->prepare) {
295+
error = suspend_ops->prepare();
296+
if (error)
297+
goto Done;
298+
}
299+
294300
error = device_power_down(PMSG_SUSPEND);
295301
if (error) {
296302
printk(KERN_ERR "PM: Some devices failed to power down\n");
297-
goto Done;
303+
goto Platfrom_finish;
298304
}
299305

300-
if (suspend_ops->prepare) {
301-
error = suspend_ops->prepare();
306+
if (suspend_ops->prepare_late) {
307+
error = suspend_ops->prepare_late();
302308
if (error)
303309
goto Power_up_devices;
304310
}
305311

306312
if (suspend_test(TEST_PLATFORM))
307-
goto Platfrom_finish;
313+
goto Platform_wake;
308314

309315
error = disable_nonboot_cpus();
310316
if (error || suspend_test(TEST_CPUS))
@@ -326,13 +332,17 @@ static int suspend_enter(suspend_state_t state)
326332
Enable_cpus:
327333
enable_nonboot_cpus();
328334

329-
Platfrom_finish:
330-
if (suspend_ops->finish)
331-
suspend_ops->finish();
335+
Platform_wake:
336+
if (suspend_ops->wake)
337+
suspend_ops->wake();
332338

333339
Power_up_devices:
334340
device_power_up(PMSG_RESUME);
335341

342+
Platfrom_finish:
343+
if (suspend_ops->finish)
344+
suspend_ops->finish();
345+
336346
Done:
337347
device_pm_unlock();
338348

0 commit comments

Comments
 (0)