|
|
|
@ -25,7 +25,6 @@
|
|
|
|
|
#include <linux/gfp.h>
|
|
|
|
|
#include <linux/syscore_ops.h>
|
|
|
|
|
#include <scsi/scsi_scan.h>
|
|
|
|
|
#include <asm/suspend.h>
|
|
|
|
|
|
|
|
|
|
#include "power.h"
|
|
|
|
|
|
|
|
|
@ -55,10 +54,9 @@ static int hibernation_mode = HIBERNATION_SHUTDOWN;
|
|
|
|
|
static const struct platform_hibernation_ops *hibernation_ops;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hibernation_set_ops - set the global hibernate operations
|
|
|
|
|
* @ops: the hibernation operations to use in subsequent hibernation transitions
|
|
|
|
|
* hibernation_set_ops - Set the global hibernate operations.
|
|
|
|
|
* @ops: Hibernation operations to use in subsequent hibernation transitions.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void hibernation_set_ops(const struct platform_hibernation_ops *ops)
|
|
|
|
|
{
|
|
|
|
|
if (ops && !(ops->begin && ops->end && ops->pre_snapshot
|
|
|
|
@ -115,10 +113,9 @@ static int hibernation_test(int level) { return 0; }
|
|
|
|
|
#endif /* !CONFIG_PM_DEBUG */
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* platform_begin - tell the platform driver that we're starting
|
|
|
|
|
* hibernation
|
|
|
|
|
* platform_begin - Call platform to start hibernation.
|
|
|
|
|
* @platform_mode: Whether or not to use the platform driver.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int platform_begin(int platform_mode)
|
|
|
|
|
{
|
|
|
|
|
return (platform_mode && hibernation_ops) ?
|
|
|
|
@ -126,10 +123,9 @@ static int platform_begin(int platform_mode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* platform_end - tell the platform driver that we've entered the
|
|
|
|
|
* working state
|
|
|
|
|
* platform_end - Call platform to finish transition to the working state.
|
|
|
|
|
* @platform_mode: Whether or not to use the platform driver.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static void platform_end(int platform_mode)
|
|
|
|
|
{
|
|
|
|
|
if (platform_mode && hibernation_ops)
|
|
|
|
@ -137,8 +133,11 @@ static void platform_end(int platform_mode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* platform_pre_snapshot - prepare the machine for hibernation using the
|
|
|
|
|
* platform driver if so configured and return an error code if it fails
|
|
|
|
|
* platform_pre_snapshot - Call platform to prepare the machine for hibernation.
|
|
|
|
|
* @platform_mode: Whether or not to use the platform driver.
|
|
|
|
|
*
|
|
|
|
|
* Use the platform driver to prepare the system for creating a hibernate image,
|
|
|
|
|
* if so configured, and return an error code if that fails.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int platform_pre_snapshot(int platform_mode)
|
|
|
|
@ -148,10 +147,14 @@ static int platform_pre_snapshot(int platform_mode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* platform_leave - prepare the machine for switching to the normal mode
|
|
|
|
|
* of operation using the platform driver (called with interrupts disabled)
|
|
|
|
|
* platform_leave - Call platform to prepare a transition to the working state.
|
|
|
|
|
* @platform_mode: Whether or not to use the platform driver.
|
|
|
|
|
*
|
|
|
|
|
* Use the platform driver prepare to prepare the machine for switching to the
|
|
|
|
|
* normal mode of operation.
|
|
|
|
|
*
|
|
|
|
|
* This routine is called on one CPU with interrupts disabled.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static void platform_leave(int platform_mode)
|
|
|
|
|
{
|
|
|
|
|
if (platform_mode && hibernation_ops)
|
|
|
|
@ -159,10 +162,14 @@ static void platform_leave(int platform_mode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* platform_finish - switch the machine to the normal mode of operation
|
|
|
|
|
* using the platform driver (must be called after platform_prepare())
|
|
|
|
|
* platform_finish - Call platform to switch the system to the working state.
|
|
|
|
|
* @platform_mode: Whether or not to use the platform driver.
|
|
|
|
|
*
|
|
|
|
|
* Use the platform driver to switch the machine to the normal mode of
|
|
|
|
|
* operation.
|
|
|
|
|
*
|
|
|
|
|
* This routine must be called after platform_prepare().
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static void platform_finish(int platform_mode)
|
|
|
|
|
{
|
|
|
|
|
if (platform_mode && hibernation_ops)
|
|
|
|
@ -170,11 +177,15 @@ static void platform_finish(int platform_mode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* platform_pre_restore - prepare the platform for the restoration from a
|
|
|
|
|
* hibernation image. If the restore fails after this function has been
|
|
|
|
|
* called, platform_restore_cleanup() must be called.
|
|
|
|
|
* platform_pre_restore - Prepare for hibernate image restoration.
|
|
|
|
|
* @platform_mode: Whether or not to use the platform driver.
|
|
|
|
|
*
|
|
|
|
|
* Use the platform driver to prepare the system for resume from a hibernation
|
|
|
|
|
* image.
|
|
|
|
|
*
|
|
|
|
|
* If the restore fails after this function has been called,
|
|
|
|
|
* platform_restore_cleanup() must be called.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int platform_pre_restore(int platform_mode)
|
|
|
|
|
{
|
|
|
|
|
return (platform_mode && hibernation_ops) ?
|
|
|
|
@ -182,12 +193,16 @@ static int platform_pre_restore(int platform_mode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* platform_restore_cleanup - switch the platform to the normal mode of
|
|
|
|
|
* operation after a failing restore. If platform_pre_restore() has been
|
|
|
|
|
* called before the failing restore, this function must be called too,
|
|
|
|
|
* regardless of the result of platform_pre_restore().
|
|
|
|
|
* platform_restore_cleanup - Switch to the working state after failing restore.
|
|
|
|
|
* @platform_mode: Whether or not to use the platform driver.
|
|
|
|
|
*
|
|
|
|
|
* Use the platform driver to switch the system to the normal mode of operation
|
|
|
|
|
* after a failing restore.
|
|
|
|
|
*
|
|
|
|
|
* If platform_pre_restore() has been called before the failing restore, this
|
|
|
|
|
* function must be called too, regardless of the result of
|
|
|
|
|
* platform_pre_restore().
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static void platform_restore_cleanup(int platform_mode)
|
|
|
|
|
{
|
|
|
|
|
if (platform_mode && hibernation_ops)
|
|
|
|
@ -195,10 +210,9 @@ static void platform_restore_cleanup(int platform_mode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* platform_recover - recover the platform from a failure to suspend
|
|
|
|
|
* devices.
|
|
|
|
|
* platform_recover - Recover from a failure to suspend devices.
|
|
|
|
|
* @platform_mode: Whether or not to use the platform driver.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static void platform_recover(int platform_mode)
|
|
|
|
|
{
|
|
|
|
|
if (platform_mode && hibernation_ops && hibernation_ops->recover)
|
|
|
|
@ -206,13 +220,12 @@ static void platform_recover(int platform_mode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* swsusp_show_speed - print the time elapsed between two events.
|
|
|
|
|
* @start: Starting event.
|
|
|
|
|
* @stop: Final event.
|
|
|
|
|
* @nr_pages - number of pages processed between @start and @stop
|
|
|
|
|
* @msg - introductory message to print
|
|
|
|
|
* swsusp_show_speed - Print time elapsed between two events during hibernation.
|
|
|
|
|
* @start: Starting event.
|
|
|
|
|
* @stop: Final event.
|
|
|
|
|
* @nr_pages: Number of memory pages processed between @start and @stop.
|
|
|
|
|
* @msg: Additional diagnostic message to print.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void swsusp_show_speed(struct timeval *start, struct timeval *stop,
|
|
|
|
|
unsigned nr_pages, char *msg)
|
|
|
|
|
{
|
|
|
|
@ -235,25 +248,18 @@ void swsusp_show_speed(struct timeval *start, struct timeval *stop,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* create_image - freeze devices that need to be frozen with interrupts
|
|
|
|
|
* off, create the hibernation image and thaw those devices. Control
|
|
|
|
|
* reappears in this routine after a restore.
|
|
|
|
|
* create_image - Create a hibernation image.
|
|
|
|
|
* @platform_mode: Whether or not to use the platform driver.
|
|
|
|
|
*
|
|
|
|
|
* Execute device drivers' .freeze_noirq() callbacks, create a hibernation image
|
|
|
|
|
* and execute the drivers' .thaw_noirq() callbacks.
|
|
|
|
|
*
|
|
|
|
|
* Control reappears in this routine after the subsequent restore.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int create_image(int platform_mode)
|
|
|
|
|
{
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
error = arch_prepare_suspend();
|
|
|
|
|
if (error)
|
|
|
|
|
return error;
|
|
|
|
|
|
|
|
|
|
/* At this point, dpm_suspend_start() has been called, but *not*
|
|
|
|
|
* dpm_suspend_noirq(). We *must* call dpm_suspend_noirq() now.
|
|
|
|
|
* Otherwise, drivers for some devices (e.g. interrupt controllers)
|
|
|
|
|
* become desynchronized with the actual state of the hardware
|
|
|
|
|
* at resume time, and evil weirdness ensues.
|
|
|
|
|
*/
|
|
|
|
|
error = dpm_suspend_noirq(PMSG_FREEZE);
|
|
|
|
|
if (error) {
|
|
|
|
|
printk(KERN_ERR "PM: Some devices failed to power down, "
|
|
|
|
@ -297,9 +303,6 @@ static int create_image(int platform_mode)
|
|
|
|
|
|
|
|
|
|
Power_up:
|
|
|
|
|
syscore_resume();
|
|
|
|
|
/* NOTE: dpm_resume_noirq() is just a resume() for devices
|
|
|
|
|
* that suspended with irqs off ... no overall powerup.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
Enable_irqs:
|
|
|
|
|
local_irq_enable();
|
|
|
|
@ -317,14 +320,11 @@ static int create_image(int platform_mode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hibernation_snapshot - quiesce devices and create the hibernation
|
|
|
|
|
* snapshot image.
|
|
|
|
|
* @platform_mode - if set, use the platform driver, if available, to
|
|
|
|
|
* prepare the platform firmware for the power transition.
|
|
|
|
|
* hibernation_snapshot - Quiesce devices and create a hibernation image.
|
|
|
|
|
* @platform_mode: If set, use platform driver to prepare for the transition.
|
|
|
|
|
*
|
|
|
|
|
* Must be called with pm_mutex held
|
|
|
|
|
* This routine must be called with pm_mutex held.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int hibernation_snapshot(int platform_mode)
|
|
|
|
|
{
|
|
|
|
|
pm_message_t msg = PMSG_RECOVER;
|
|
|
|
@ -384,13 +384,14 @@ int hibernation_snapshot(int platform_mode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* resume_target_kernel - prepare devices that need to be suspended with
|
|
|
|
|
* interrupts off, restore the contents of highmem that have not been
|
|
|
|
|
* restored yet from the image and run the low level code that will restore
|
|
|
|
|
* the remaining contents of memory and switch to the just restored target
|
|
|
|
|
* kernel.
|
|
|
|
|
* resume_target_kernel - Restore system state from a hibernation image.
|
|
|
|
|
* @platform_mode: Whether or not to use the platform driver.
|
|
|
|
|
*
|
|
|
|
|
* Execute device drivers' .freeze_noirq() callbacks, restore the contents of
|
|
|
|
|
* highmem that have not been restored yet from the image and run the low-level
|
|
|
|
|
* code that will restore the remaining contents of memory and switch to the
|
|
|
|
|
* just restored target kernel.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int resume_target_kernel(bool platform_mode)
|
|
|
|
|
{
|
|
|
|
|
int error;
|
|
|
|
@ -416,24 +417,26 @@ static int resume_target_kernel(bool platform_mode)
|
|
|
|
|
if (error)
|
|
|
|
|
goto Enable_irqs;
|
|
|
|
|
|
|
|
|
|
/* We'll ignore saved state, but this gets preempt count (etc) right */
|
|
|
|
|
save_processor_state();
|
|
|
|
|
error = restore_highmem();
|
|
|
|
|
if (!error) {
|
|
|
|
|
error = swsusp_arch_resume();
|
|
|
|
|
/*
|
|
|
|
|
* The code below is only ever reached in case of a failure.
|
|
|
|
|
* Otherwise execution continues at place where
|
|
|
|
|
* swsusp_arch_suspend() was called
|
|
|
|
|
* Otherwise, execution continues at the place where
|
|
|
|
|
* swsusp_arch_suspend() was called.
|
|
|
|
|
*/
|
|
|
|
|
BUG_ON(!error);
|
|
|
|
|
/* This call to restore_highmem() undos the previous one */
|
|
|
|
|
/*
|
|
|
|
|
* This call to restore_highmem() reverts the changes made by
|
|
|
|
|
* the previous one.
|
|
|
|
|
*/
|
|
|
|
|
restore_highmem();
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
* The only reason why swsusp_arch_resume() can fail is memory being
|
|
|
|
|
* very tight, so we have to free it as soon as we can to avoid
|
|
|
|
|
* subsequent failures
|
|
|
|
|
* subsequent failures.
|
|
|
|
|
*/
|
|
|
|
|
swsusp_free();
|
|
|
|
|
restore_processor_state();
|
|
|
|
@ -456,14 +459,12 @@ static int resume_target_kernel(bool platform_mode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hibernation_restore - quiesce devices and restore the hibernation
|
|
|
|
|
* snapshot image. If successful, control returns in hibernation_snaphot()
|
|
|
|
|
* @platform_mode - if set, use the platform driver, if available, to
|
|
|
|
|
* prepare the platform firmware for the transition.
|
|
|
|
|
* hibernation_restore - Quiesce devices and restore from a hibernation image.
|
|
|
|
|
* @platform_mode: If set, use platform driver to prepare for the transition.
|
|
|
|
|
*
|
|
|
|
|
* Must be called with pm_mutex held
|
|
|
|
|
* This routine must be called with pm_mutex held. If it is successful, control
|
|
|
|
|
* reappears in the restored target kernel in hibernation_snaphot().
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int hibernation_restore(int platform_mode)
|
|
|
|
|
{
|
|
|
|
|
int error;
|
|
|
|
@ -483,10 +484,8 @@ int hibernation_restore(int platform_mode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hibernation_platform_enter - enter the hibernation state using the
|
|
|
|
|
* platform driver (if available)
|
|
|
|
|
* hibernation_platform_enter - Power off the system using the platform driver.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int hibernation_platform_enter(void)
|
|
|
|
|
{
|
|
|
|
|
int error;
|
|
|
|
@ -557,12 +556,12 @@ int hibernation_platform_enter(void)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* power_down - Shut the machine down for hibernation.
|
|
|
|
|
* power_down - Shut the machine down for hibernation.
|
|
|
|
|
*
|
|
|
|
|
* Use the platform driver, if configured so; otherwise try
|
|
|
|
|
* to power off or reboot.
|
|
|
|
|
* Use the platform driver, if configured, to put the system into the sleep
|
|
|
|
|
* state corresponding to hibernation, or try to power it off or reboot,
|
|
|
|
|
* depending on the value of hibernation_mode.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static void power_down(void)
|
|
|
|
|
{
|
|
|
|
|
switch (hibernation_mode) {
|
|
|
|
@ -599,9 +598,8 @@ static int prepare_processes(void)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* hibernate - The granpappy of the built-in hibernation management
|
|
|
|
|
* hibernate - Carry out system hibernation, including saving the image.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int hibernate(void)
|
|
|
|
|
{
|
|
|
|
|
int error;
|
|
|
|
@ -679,17 +677,20 @@ int hibernate(void)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* software_resume - Resume from a saved image.
|
|
|
|
|
* software_resume - Resume from a saved hibernation image.
|
|
|
|
|
*
|
|
|
|
|
* Called as a late_initcall (so all devices are discovered and
|
|
|
|
|
* initialized), we call swsusp to see if we have a saved image or not.
|
|
|
|
|
* If so, we quiesce devices, the restore the saved image. We will
|
|
|
|
|
* return above (in hibernate() ) if everything goes well.
|
|
|
|
|
* Otherwise, we fail gracefully and return to the normally
|
|
|
|
|
* scheduled program.
|
|
|
|
|
* This routine is called as a late initcall, when all devices have been
|
|
|
|
|
* discovered and initialized already.
|
|
|
|
|
*
|
|
|
|
|
* The image reading code is called to see if there is a hibernation image
|
|
|
|
|
* available for reading. If that is the case, devices are quiesced and the
|
|
|
|
|
* contents of memory is restored from the saved image.
|
|
|
|
|
*
|
|
|
|
|
* If this is successful, control reappears in the restored target kernel in
|
|
|
|
|
* hibernation_snaphot() which returns to hibernate(). Otherwise, the routine
|
|
|
|
|
* attempts to recover gracefully and make the kernel return to the normal mode
|
|
|
|
|
* of operation.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static int software_resume(void)
|
|
|
|
|
{
|
|
|
|
|
int error;
|
|
|
|
@ -819,21 +820,17 @@ static const char * const hibernation_modes[] = {
|
|
|
|
|
[HIBERNATION_TESTPROC] = "testproc",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* disk - Control hibernation mode
|
|
|
|
|
/*
|
|
|
|
|
* /sys/power/disk - Control hibernation mode.
|
|
|
|
|
*
|
|
|
|
|
* Suspend-to-disk can be handled in several ways. We have a few options
|
|
|
|
|
* for putting the system to sleep - using the platform driver (e.g. ACPI
|
|
|
|
|
* or other hibernation_ops), powering off the system or rebooting the
|
|
|
|
|
* system (for testing) as well as the two test modes.
|
|
|
|
|
* Hibernation can be handled in several ways. There are a few different ways
|
|
|
|
|
* to put the system into the sleep state: using the platform driver (e.g. ACPI
|
|
|
|
|
* or other hibernation_ops), powering it off or rebooting it (for testing
|
|
|
|
|
* mostly), or using one of the two available test modes.
|
|
|
|
|
*
|
|
|
|
|
* The system can support 'platform', and that is known a priori (and
|
|
|
|
|
* encoded by the presence of hibernation_ops). However, the user may
|
|
|
|
|
* choose 'shutdown' or 'reboot' as alternatives, as well as one fo the
|
|
|
|
|
* test modes, 'test' or 'testproc'.
|
|
|
|
|
*
|
|
|
|
|
* show() will display what the mode is currently set to.
|
|
|
|
|
* store() will accept one of
|
|
|
|
|
* The sysfs file /sys/power/disk provides an interface for selecting the
|
|
|
|
|
* hibernation mode to use. Reading from this file causes the available modes
|
|
|
|
|
* to be printed. There are 5 modes that can be supported:
|
|
|
|
|
*
|
|
|
|
|
* 'platform'
|
|
|
|
|
* 'shutdown'
|
|
|
|
@ -841,8 +838,14 @@ static const char * const hibernation_modes[] = {
|
|
|
|
|
* 'test'
|
|
|
|
|
* 'testproc'
|
|
|
|
|
*
|
|
|
|
|
* It will only change to 'platform' if the system
|
|
|
|
|
* supports it (as determined by having hibernation_ops).
|
|
|
|
|
* If a platform hibernation driver is in use, 'platform' will be supported
|
|
|
|
|
* and will be used by default. Otherwise, 'shutdown' will be used by default.
|
|
|
|
|
* The selected option (i.e. the one corresponding to the current value of
|
|
|
|
|
* hibernation_mode) is enclosed by a square bracket.
|
|
|
|
|
*
|
|
|
|
|
* To select a given hibernation mode it is necessary to write the mode's
|
|
|
|
|
* string representation (as returned by reading from /sys/power/disk) back
|
|
|
|
|
* into /sys/power/disk.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
|
|
@ -875,7 +878,6 @@ static ssize_t disk_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|
|
|
|
return buf-start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static ssize_t disk_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|
|
|
|
const char *buf, size_t n)
|
|
|
|
|
{
|
|
|
|
|