/*
* kernel/power/hibernate.c - Hibernation (a.k.a suspend-to-disk) support.
*
* Copyright (c) 2003 Patrick Mochel
* Copyright (c) 2003 Open Source Development Lab
* Copyright (c) 2004 Pavel Machek <pavel@ucw.cz>
* Copyright (c) 2009 Rafael J. Wysocki, Novell Inc.
*
* This file is released under the GPLv2.
*/
#include <linux/export.h>
#include <linux/suspend.h>
#include <linux/syscalls.h>
#include <linux/reboot.h>
#include <linux/string.h>
#include <linux/device.h>
#include <linux/async.h>
#include <linux/kmod.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/pm.h>
#include <linux/console.h>
#include <linux/cpu.h>
#include <linux/freezer.h>
#include <linux/gfp.h>
#include <linux/syscore_ops.h>
#include <scsi/scsi_scan.h>
#include "power.h"
static int nocompress;
static int noresume;
static int resume_wait;
static int resume_delay;
static char resume_file[256] = CONFIG_PM_STD_PARTITION;
dev_t swsusp_resume_device;
sector_t swsusp_resume_block;
int in_suspend __nosavedata;
enum {
HIBERNATION_INVALID,
HIBERNATION_PLATFORM,
HIBERNATION_TEST,
HIBERNATION_TESTPROC,
HIBERNATION_SHUTDOWN,
HIBERNATION_REBOOT,
/* keep last */
__HIBERNATION_AFTER_LAST
};
#define HIBERNATION_MAX (__HIBERNATION_AFTER_LAST-1)
#define HIBERNATION_FIRST (HIBERNATION_INVALID + 1)
static int hibernation_mode = HIBERNATION_SHUTDOWN;
static const struct platform_hibernation_ops *hibernation_ops;
/**
* 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
&& ops->prepare && ops->finish && ops->enter && ops->pre_restore
&& ops->restore_cleanup && ops->leave)) {
WARN_ON(1);
return;
}
mutex_lock(&pm_mutex);
hibernation_ops = ops;
if (ops)
hibernation_mode = HIBERNATION_PLATFORM;
else if (hibernation_mode == HIBERNATION_PLATFORM)
hibernation_mode = HIBERNATION_SHUTDOWN;
mutex_unlock(&pm_mutex);
}
static bool entering_platform_hibernation;
bool system_entering_hibernation(void)
{
return entering_platform_hibernation;
}
EXPORT_SYMBOL(system_entering_hibernation);
#ifdef CONFIG_PM_DEBUG
static void hibernation_debug_sleep(void)
{
printk(KERN_INFO "hibernation debug: Waiting for 5 seconds.\n");
mdelay(5000);
}
static int hibernation_testmode(int mode)
{
if (hibernation_mode == mode) {
hibernation_debug_sleep();
return 1;
}
return 0;
}
static int hibernation_test(int level)
{
if (pm_test_level == level) {
hibernation_debug_sleep();
return 1;
}
return 0;
}
#else /* !CONFIG_PM_DEBUG */
static int hibernation_testmode(int mode) { return 0; }
static int hibernation_test(int level) { return 0; }
#endif /* !CONFIG_PM_DEBUG */
/**
* 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) ?
hibernation_ops->begin() : 0;
}
/**
* 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)
hibernation_ops->end();
}
/**
* 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)
{
return (platform_mode && hibernation_ops) ?
hibernation_ops->pre_snapshot() : 0;
}
/**
* 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)
hibernation_ops->leave();
}
/**
* 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)
hibernation_ops->finish();
}
/**
* 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) ?
hibernation_ops->pre_restore() : 0;
}
/**
* 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