#include <linux/cpu.h>
#include <linux/resume-trace.h>
#include <linux/freezer.h>
+#include <linux/vmstat.h>
#include "power.h"
DEFINE_MUTEX(pm_mutex);
struct pm_ops *pm_ops;
-suspend_disk_method_t pm_disk_mode = PM_DISK_PLATFORM;
+suspend_disk_method_t pm_disk_mode = PM_DISK_SHUTDOWN;
/**
* pm_set_ops - Set the global power method table.
{
mutex_lock(&pm_mutex);
pm_ops = ops;
+ if (ops && ops->pm_disk_mode != PM_DISK_INVALID) {
+ pm_disk_mode = ops->pm_disk_mode;
+ } else
+ pm_disk_mode = PM_DISK_SHUTDOWN;
mutex_unlock(&pm_mutex);
}
+/**
+ * pm_valid_only_mem - generic memory-only valid callback
+ *
+ * pm_ops drivers that implement mem suspend only and only need
+ * to check for that in their .valid callback can use this instead
+ * of rolling their own .valid callback.
+ */
+int pm_valid_only_mem(suspend_state_t state)
+{
+ return state == PM_SUSPEND_MEM;
+}
+
+
+static inline void pm_finish(suspend_state_t state)
+{
+ if (pm_ops->finish)
+ pm_ops->finish(state);
+}
/**
* suspend_prepare - Do prep work before entering low-power state.
pm_prepare_console();
- error = disable_nonboot_cpus();
- if (error)
- goto Enable_cpu;
-
if (freeze_processes()) {
error = -EAGAIN;
goto Thaw;
}
- if ((free_pages = nr_free_pages()) < FREE_PAGE_NUMBER) {
+ if ((free_pages = global_page_state(NR_FREE_PAGES))
+ < FREE_PAGE_NUMBER) {
pr_debug("PM: free some memory\n");
shrink_all_memory(FREE_PAGE_NUMBER - free_pages);
if (nr_free_pages() < FREE_PAGE_NUMBER) {
}
suspend_console();
- if ((error = device_suspend(PMSG_SUSPEND))) {
+ error = device_suspend(PMSG_SUSPEND);
+ if (error) {
printk(KERN_ERR "Some devices failed to suspend\n");
- goto Finish;
+ goto Resume_devices;
}
- return 0;
- Finish:
- if (pm_ops->finish)
- pm_ops->finish(state);
+ error = disable_nonboot_cpus();
+ if (!error)
+ return 0;
+
+ enable_nonboot_cpus();
+ Resume_devices:
+ pm_finish(state);
+ device_resume();
+ resume_console();
Thaw:
thaw_processes();
- Enable_cpu:
- enable_nonboot_cpus();
pm_restore_console();
return error;
}
+/* default implementation */
+void __attribute__ ((weak)) arch_suspend_disable_irqs(void)
+{
+ local_irq_disable();
+}
+
+/* default implementation */
+void __attribute__ ((weak)) arch_suspend_enable_irqs(void)
+{
+ local_irq_enable();
+}
int suspend_enter(suspend_state_t state)
{
int error = 0;
- unsigned long flags;
- local_irq_save(flags);
+ arch_suspend_disable_irqs();
+ BUG_ON(!irqs_disabled());
if ((error = device_power_down(PMSG_SUSPEND))) {
printk(KERN_ERR "Some devices failed to power down\n");
error = pm_ops->enter(state);
device_power_up();
Done:
- local_irq_restore(flags);
+ arch_suspend_enable_irqs();
+ BUG_ON(irqs_disabled());
return error;
}
static void suspend_finish(suspend_state_t state)
{
+ enable_nonboot_cpus();
+ pm_finish(state);
device_resume();
resume_console();
thaw_processes();
- enable_nonboot_cpus();
- if (pm_ops && pm_ops->finish)
- pm_ops->finish(state);
pm_restore_console();
}
static const char * const pm_states[PM_SUSPEND_MAX] = {
[PM_SUSPEND_STANDBY] = "standby",
[PM_SUSPEND_MEM] = "mem",
-#ifdef CONFIG_SOFTWARE_SUSPEND
[PM_SUSPEND_DISK] = "disk",
-#endif
};
static inline int valid_state(suspend_state_t state)
{
/* Suspend-to-disk does not really need low-level support.
- * It can work with reboot if needed. */
+ * It can work with shutdown/reboot if needed. If it isn't
+ * configured, then it cannot be supported.
+ */
if (state == PM_SUSPEND_DISK)
+#ifdef CONFIG_SOFTWARE_SUSPEND
return 1;
+#else
+ return 0;
+#endif
- if (pm_ops && pm_ops->valid && !pm_ops->valid(state))
+ /* all other states need lowlevel support and need to be
+ * valid to the lowlevel implementation, no valid callback
+ * implies that none are valid. */
+ if (!pm_ops || !pm_ops->valid || !pm_ops->valid(state))
return 0;
return 1;
}
return error;
}
-/*
- * This is main interface to the outside world. It needs to be
- * called from process context.
- */
-int software_suspend(void)
-{
- return enter_state(PM_SUSPEND_DISK);
-}
-
/**
* pm_suspend - Externally visible function for suspending system.
* proper enumerated value, and initiates a suspend transition.
*/
-static ssize_t state_show(struct subsystem * subsys, char * buf)
+static ssize_t state_show(struct kset *kset, char *buf)
{
int i;
char * s = buf;
return (s - buf);
}
-static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n)
+static ssize_t state_store(struct kset *kset, const char *buf, size_t n)
{
suspend_state_t state = PM_SUSPEND_STANDBY;
const char * const *s;
#ifdef CONFIG_PM_TRACE
int pm_trace_enabled;
-static ssize_t pm_trace_show(struct subsystem * subsys, char * buf)
+static ssize_t pm_trace_show(struct kset *kset, char *buf)
{
return sprintf(buf, "%d\n", pm_trace_enabled);
}
static ssize_t
-pm_trace_store(struct subsystem * subsys, const char * buf, size_t n)
+pm_trace_store(struct kset *kset, const char *buf, size_t n)
{
int val;
{
int error = subsystem_register(&power_subsys);
if (!error)
- error = sysfs_create_group(&power_subsys.kset.kobj,&attr_group);
+ error = sysfs_create_group(&power_subsys.kobj,&attr_group);
return error;
}