Commit 73fb7306 authored by Patrick Mochel's avatar Patrick Mochel

Merge osdl.org:/home/mochel/src/kernel/linux-2.5-virgin

into osdl.org:/home/mochel/src/kernel/linux-2.5-power
parents d742b6ed 48d1e784
...@@ -96,6 +96,7 @@ drivers-$(CONFIG_MATH_EMULATION) += arch/i386/math-emu/ ...@@ -96,6 +96,7 @@ drivers-$(CONFIG_MATH_EMULATION) += arch/i386/math-emu/
drivers-$(CONFIG_PCI) += arch/i386/pci/ drivers-$(CONFIG_PCI) += arch/i386/pci/
# must be linked after kernel/ # must be linked after kernel/
drivers-$(CONFIG_OPROFILE) += arch/i386/oprofile/ drivers-$(CONFIG_OPROFILE) += arch/i386/oprofile/
drivers-$(CONFIG_PM) += arch/i386/power/
CFLAGS += $(mflags-y) CFLAGS += $(mflags-y)
AFLAGS += $(mflags-y) AFLAGS += $(mflags-y)
......
...@@ -17,9 +17,7 @@ obj-$(CONFIG_MCA) += mca.o ...@@ -17,9 +17,7 @@ obj-$(CONFIG_MCA) += mca.o
obj-$(CONFIG_X86_MSR) += msr.o obj-$(CONFIG_X86_MSR) += msr.o
obj-$(CONFIG_X86_CPUID) += cpuid.o obj-$(CONFIG_X86_CPUID) += cpuid.o
obj-$(CONFIG_MICROCODE) += microcode.o obj-$(CONFIG_MICROCODE) += microcode.o
obj-$(CONFIG_PM) += suspend.o
obj-$(CONFIG_APM) += apm.o obj-$(CONFIG_APM) += apm.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend_asm.o
obj-$(CONFIG_X86_SMP) += smp.o smpboot.o obj-$(CONFIG_X86_SMP) += smp.o smpboot.o
obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o
obj-$(CONFIG_X86_MPPARSE) += mpparse.o obj-$(CONFIG_X86_MPPARSE) += mpparse.o
......
obj-$(CONFIG_PM) += cpu.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o
...@@ -8,11 +8,10 @@ ...@@ -8,11 +8,10 @@
.text .text
ENTRY(do_magic) ENTRY(swsusp_arch_suspend)
pushl %ebx pushl %ebx
cmpl $0,8(%esp) cmpl $0,8(%esp)
jne .L1450 jne .L1450
call do_magic_suspend_1
call save_processor_state call save_processor_state
movl %esp, saved_context_esp movl %esp, saved_context_esp
...@@ -25,14 +24,13 @@ ENTRY(do_magic) ...@@ -25,14 +24,13 @@ ENTRY(do_magic)
movl %edi, saved_context_edi movl %edi, saved_context_edi
pushfl ; popl saved_context_eflags pushfl ; popl saved_context_eflags
call do_magic_suspend_2 call swsusp_suspend
jmp .L1449 jmp .L1449
.p2align 4,,7 .p2align 4,,7
.L1450: .L1450:
movl $swapper_pg_dir-__PAGE_OFFSET,%ecx movl $swapper_pg_dir-__PAGE_OFFSET,%ecx
movl %ecx,%cr3 movl %ecx,%cr3
call do_magic_resume_1
movl $0,loop movl $0,loop
cmpl $0,nr_copy_pages cmpl $0,nr_copy_pages
je .L1453 je .L1453
...@@ -80,7 +78,7 @@ ENTRY(do_magic) ...@@ -80,7 +78,7 @@ ENTRY(do_magic)
movl saved_context_edi, %edi movl saved_context_edi, %edi
call restore_processor_state call restore_processor_state
pushl saved_context_eflags ; popfl pushl saved_context_eflags ; popfl
call do_magic_resume_2 call swsusp_resume
.L1449: .L1449:
popl %ebx popl %ebx
ret ret
......
...@@ -41,7 +41,6 @@ static u32 acpi_suspend_states[] = { ...@@ -41,7 +41,6 @@ static u32 acpi_suspend_states[] = {
static int acpi_pm_prepare(u32 state) static int acpi_pm_prepare(u32 state)
{ {
int error = 0;
u32 acpi_state = acpi_suspend_states[state]; u32 acpi_state = acpi_suspend_states[state];
if (!sleep_states[acpi_state]) if (!sleep_states[acpi_state])
...@@ -56,21 +55,9 @@ static int acpi_pm_prepare(u32 state) ...@@ -56,21 +55,9 @@ static int acpi_pm_prepare(u32 state)
acpi_set_firmware_waking_vector( acpi_set_firmware_waking_vector(
(acpi_physical_address) acpi_wakeup_address); (acpi_physical_address) acpi_wakeup_address);
} }
ACPI_FLUSH_CPU_CACHE(); ACPI_FLUSH_CPU_CACHE();
/* Do arch specific saving of state. */
if (state > PM_SUSPEND_STANDBY) {
if ((error = acpi_save_state_mem()))
goto Err;
}
acpi_enter_sleep_state_prep(acpi_state); acpi_enter_sleep_state_prep(acpi_state);
return 0; return 0;
Err:
acpi_set_firmware_waking_vector(0);
return error;
} }
...@@ -90,6 +77,15 @@ static int acpi_pm_enter(u32 state) ...@@ -90,6 +77,15 @@ static int acpi_pm_enter(u32 state)
u32 acpi_state = acpi_suspend_states[state]; u32 acpi_state = acpi_suspend_states[state];
ACPI_FLUSH_CPU_CACHE(); ACPI_FLUSH_CPU_CACHE();
/* Do arch specific saving of state. */
if (state > PM_SUSPEND_STANDBY) {
int error = acpi_save_state_mem();
if (error)
return error;
}
local_irq_save(flags); local_irq_save(flags);
switch (state) switch (state)
{ {
...@@ -114,6 +110,15 @@ static int acpi_pm_enter(u32 state) ...@@ -114,6 +110,15 @@ static int acpi_pm_enter(u32 state)
local_irq_restore(flags); local_irq_restore(flags);
printk(KERN_DEBUG "Back to C!\n"); printk(KERN_DEBUG "Back to C!\n");
/* restore processor state
* We should only be here if we're coming back from STR or STD.
* And, in the case of the latter, the memory image should have already
* been loaded from disk.
*/
if (state > PM_SUSPEND_STANDBY)
acpi_restore_state_mem();
return ACPI_SUCCESS(status) ? 0 : -EFAULT; return ACPI_SUCCESS(status) ? 0 : -EFAULT;
} }
...@@ -130,14 +135,6 @@ static int acpi_pm_finish(u32 state) ...@@ -130,14 +135,6 @@ static int acpi_pm_finish(u32 state)
{ {
acpi_leave_sleep_state(state); acpi_leave_sleep_state(state);
/* restore processor state
* We should only be here if we're coming back from STR or STD.
* And, in the case of the latter, the memory image should have already
* been loaded from disk.
*/
if (state > ACPI_STATE_S1)
acpi_restore_state_mem();
/* reset firmware waking vector */ /* reset firmware waking vector */
acpi_set_firmware_waking_vector((acpi_physical_address) 0); acpi_set_firmware_waking_vector((acpi_physical_address) 0);
...@@ -149,6 +146,20 @@ static int acpi_pm_finish(u32 state) ...@@ -149,6 +146,20 @@ static int acpi_pm_finish(u32 state)
} }
int acpi_suspend(u32 acpi_state)
{
u32 states[] = {
[1] = PM_SUSPEND_STANDBY,
[3] = PM_SUSPEND_MEM,
[4] = PM_SUSPEND_DISK,
};
if (acpi_state <= 4 && states[acpi_state])
return pm_suspend(states[acpi_state]);
return -EINVAL;
}
static struct pm_ops acpi_pm_ops = { static struct pm_ops acpi_pm_ops = {
.prepare = acpi_pm_prepare, .prepare = acpi_pm_prepare,
.enter = acpi_pm_enter, .enter = acpi_pm_enter,
......
...@@ -13,12 +13,71 @@ ...@@ -13,12 +13,71 @@
#include "sleep.h" #include "sleep.h"
#define ACPI_SYSTEM_FILE_SLEEP "sleep"
#define ACPI_SYSTEM_FILE_ALARM "alarm" #define ACPI_SYSTEM_FILE_ALARM "alarm"
#define _COMPONENT ACPI_SYSTEM_COMPONENT #define _COMPONENT ACPI_SYSTEM_COMPONENT
ACPI_MODULE_NAME ("sleep") ACPI_MODULE_NAME ("sleep")
static int acpi_system_sleep_seq_show(struct seq_file *seq, void *offset)
{
int i;
ACPI_FUNCTION_TRACE("acpi_system_sleep_seq_show");
for (i = 0; i <= ACPI_STATE_S5; i++) {
if (sleep_states[i]) {
seq_printf(seq,"S%d ", i);
if (i == ACPI_STATE_S4 && acpi_gbl_FACS->S4bios_f)
seq_printf(seq, "S4bios ");
}
}
seq_puts(seq, "\n");
return 0;
}
static int acpi_system_sleep_open_fs(struct inode *inode, struct file *file)
{
return single_open(file, acpi_system_sleep_seq_show, PDE(inode)->data);
}
static int
acpi_system_write_sleep (
struct file *file,
const char *buffer,
size_t count,
loff_t *ppos)
{
char str[12];
u32 state = 0;
int error = 0;
if (count > sizeof(str) - 1)
goto Done;
memset(str,0,sizeof(str));
if (copy_from_user(str, buffer, count))
return -EFAULT;
/* Check for S4 bios request */
if (!strcmp(str,"4b")) {
error = acpi_suspend(4);
goto Done;
}
state = simple_strtoul(str, NULL, 0);
#ifdef CONFIG_SOFTWARE_SUSPEND
if (state == 4) {
error = software_suspend();
goto Done;
}
#endif
error = acpi_suspend(state);
Done:
return error ? error : count;
}
static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset) static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset)
{ {
u32 sec, min, hr; u32 sec, min, hr;
...@@ -294,6 +353,14 @@ acpi_system_write_alarm ( ...@@ -294,6 +353,14 @@ acpi_system_write_alarm (
} }
static struct file_operations acpi_system_sleep_fops = {
.open = acpi_system_sleep_open_fs,
.read = seq_read,
.write = acpi_system_write_sleep,
.llseek = seq_lseek,
.release = single_release,
};
static struct file_operations acpi_system_alarm_fops = { static struct file_operations acpi_system_alarm_fops = {
.open = acpi_system_alarm_open_fs, .open = acpi_system_alarm_open_fs,
.read = seq_read, .read = seq_read,
...@@ -307,6 +374,12 @@ static int acpi_sleep_proc_init(void) ...@@ -307,6 +374,12 @@ static int acpi_sleep_proc_init(void)
{ {
struct proc_dir_entry *entry = NULL; struct proc_dir_entry *entry = NULL;
/* 'sleep' [R/W]*/
entry = create_proc_entry(ACPI_SYSTEM_FILE_SLEEP,
S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir);
if (entry)
entry->proc_fops = &acpi_system_sleep_fops;
/* 'alarm' [R/W] */ /* 'alarm' [R/W] */
entry = create_proc_entry(ACPI_SYSTEM_FILE_ALARM, entry = create_proc_entry(ACPI_SYSTEM_FILE_ALARM,
S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir); S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir);
......
extern u8 sleep_states[]; extern u8 sleep_states[];
extern int acpi_suspend (u32 state);
extern acpi_status acpi_suspend (u32 state);
...@@ -225,28 +225,30 @@ int device_add(struct device *dev) ...@@ -225,28 +225,30 @@ int device_add(struct device *dev)
dev->kobj.parent = &parent->kobj; dev->kobj.parent = &parent->kobj;
if ((error = kobject_add(&dev->kobj))) if ((error = kobject_add(&dev->kobj)))
goto register_done; goto Error;
if ((error = device_pm_add(dev)))
/* now take care of our own registration */ goto PMError;
if ((error = bus_add_device(dev)))
goto BusError;
down_write(&devices_subsys.rwsem); down_write(&devices_subsys.rwsem);
if (parent) if (parent)
list_add_tail(&dev->node,&parent->children); list_add_tail(&dev->node,&parent->children);
up_write(&devices_subsys.rwsem); up_write(&devices_subsys.rwsem);
bus_add_device(dev);
device_pm_add(dev);
/* notify platform of device entry */ /* notify platform of device entry */
if (platform_notify) if (platform_notify)
platform_notify(dev); platform_notify(dev);
Done:
register_done:
if (error && parent)
put_device(parent);
put_device(dev); put_device(dev);
return error; return error;
BusError:
device_pm_remove(dev);
PMError:
kobject_unregister(&dev->kobj);
Error:
if (parent)
put_device(parent);
goto Done;
} }
...@@ -312,8 +314,6 @@ void device_del(struct device * dev) ...@@ -312,8 +314,6 @@ void device_del(struct device * dev)
{ {
struct device * parent = dev->parent; struct device * parent = dev->parent;
device_pm_remove(dev);
down_write(&devices_subsys.rwsem); down_write(&devices_subsys.rwsem);
if (parent) if (parent)
list_del_init(&dev->node); list_del_init(&dev->node);
...@@ -324,14 +324,11 @@ void device_del(struct device * dev) ...@@ -324,14 +324,11 @@ void device_del(struct device * dev)
*/ */
if (platform_notify_remove) if (platform_notify_remove)
platform_notify_remove(dev); platform_notify_remove(dev);
bus_remove_device(dev); bus_remove_device(dev);
device_pm_remove(dev);
kobject_del(&dev->kobj); kobject_del(&dev->kobj);
if (parent) if (parent)
put_device(parent); put_device(parent);
} }
/** /**
......
...@@ -36,12 +36,14 @@ DECLARE_MUTEX(dpm_sem); ...@@ -36,12 +36,14 @@ DECLARE_MUTEX(dpm_sem);
static inline void device_pm_hold(struct device * dev) static inline void device_pm_hold(struct device * dev)
{ {
if (dev)
atomic_inc(&dev->power.pm_users); atomic_inc(&dev->power.pm_users);
} }
static inline void device_pm_release(struct device * dev) static inline void device_pm_release(struct device * dev)
{ {
atomic_inc(&dev->power.pm_users); if (dev)
atomic_dec(&dev->power.pm_users);
} }
...@@ -61,10 +63,8 @@ static inline void device_pm_release(struct device * dev) ...@@ -61,10 +63,8 @@ static inline void device_pm_release(struct device * dev)
void device_pm_set_parent(struct device * dev, struct device * parent) void device_pm_set_parent(struct device * dev, struct device * parent)
{ {
struct device * old_parent = dev->power.pm_parent; struct device * old_parent = dev->power.pm_parent;
if (old_parent)
device_pm_release(old_parent); device_pm_release(old_parent);
dev->power.pm_parent = parent; dev->power.pm_parent = parent;
if (parent)
device_pm_hold(parent); device_pm_hold(parent);
} }
EXPORT_SYMBOL(device_pm_set_parent); EXPORT_SYMBOL(device_pm_set_parent);
...@@ -91,6 +91,7 @@ void device_pm_remove(struct device * dev) ...@@ -91,6 +91,7 @@ void device_pm_remove(struct device * dev)
dev->bus ? dev->bus->name : "No Bus", dev->kobj.name); dev->bus ? dev->bus->name : "No Bus", dev->kobj.name);
down(&dpm_sem); down(&dpm_sem);
dpm_sysfs_remove(dev); dpm_sysfs_remove(dev);
device_pm_release(dev->power.pm_parent);
list_del(&dev->power.entry); list_del(&dev->power.entry);
up(&dpm_sem); up(&dpm_sem);
} }
......
...@@ -58,7 +58,8 @@ extern void dpm_sysfs_remove(struct device *); ...@@ -58,7 +58,8 @@ extern void dpm_sysfs_remove(struct device *);
/* /*
* resume.c * resume.c
*/ */
extern int dpm_resume(void);
extern void dpm_resume(void);
extern void dpm_power_up(void); extern void dpm_power_up(void);
extern int resume_device(struct device *); extern int resume_device(struct device *);
......
...@@ -28,6 +28,19 @@ int resume_device(struct device * dev) ...@@ -28,6 +28,19 @@ int resume_device(struct device * dev)
} }
void dpm_resume(void)
{
while(!list_empty(&dpm_off)) {
struct list_head * entry = dpm_off.next;
struct device * dev = to_device(entry);
list_del_init(entry);
resume_device(dev);
list_add_tail(entry,&dpm_active);
}
}
/** /**
* device_resume - Restore state of each device in system. * device_resume - Restore state of each device in system.
* *
...@@ -38,13 +51,7 @@ int resume_device(struct device * dev) ...@@ -38,13 +51,7 @@ int resume_device(struct device * dev)
void device_resume(void) void device_resume(void)
{ {
down(&dpm_sem); down(&dpm_sem);
while(!list_empty(&dpm_off)) { dpm_resume();
struct list_head * entry = dpm_off.next;
struct device * dev = to_device(entry);
list_del_init(entry);
resume_device(dev);
list_add_tail(entry,&dpm_active);
}
up(&dpm_sem); up(&dpm_sem);
} }
......
...@@ -81,14 +81,18 @@ int device_suspend(u32 state) ...@@ -81,14 +81,18 @@ int device_suspend(u32 state)
while(!list_empty(&dpm_active)) { while(!list_empty(&dpm_active)) {
struct list_head * entry = dpm_active.prev; struct list_head * entry = dpm_active.prev;
struct device * dev = to_device(entry); struct device * dev = to_device(entry);
if ((error = suspend_device(dev,state))) if ((error = suspend_device(dev,state))) {
if (error != -EAGAIN)
goto Error; goto Error;
else
error = 0;
}
} }
Done: Done:
up(&dpm_sem); up(&dpm_sem);
return error; return error;
Error: Error:
device_resume(); dpm_resume();
goto Done; goto Done;
} }
......
...@@ -6,11 +6,12 @@ ...@@ -6,11 +6,12 @@
#include <asm/desc.h> #include <asm/desc.h>
#include <asm/i387.h> #include <asm/i387.h>
static inline void static inline int
arch_prepare_suspend(void) arch_prepare_suspend(void)
{ {
if (!cpu_has_pse) if (!cpu_has_pse)
panic("pse required"); return -EPERM;
return 0;
} }
/* image of the saved processor state */ /* image of the saved processor state */
...@@ -38,8 +39,6 @@ struct saved_context { ...@@ -38,8 +39,6 @@ struct saved_context {
extern void save_processor_state(void); extern void save_processor_state(void);
extern void restore_processor_state(void); extern void restore_processor_state(void);
extern int do_magic(int resume);
#ifdef CONFIG_ACPI_SLEEP #ifdef CONFIG_ACPI_SLEEP
extern unsigned long saved_eip; extern unsigned long saved_eip;
extern unsigned long saved_esp; extern unsigned long saved_esp;
......
...@@ -53,6 +53,7 @@ extern suspend_pagedir_t *pagedir_nosave __nosavedata; ...@@ -53,6 +53,7 @@ extern suspend_pagedir_t *pagedir_nosave __nosavedata;
extern void do_suspend_lowlevel(int resume); extern void do_suspend_lowlevel(int resume);
extern void do_suspend_lowlevel_s4bios(int resume); extern void do_suspend_lowlevel_s4bios(int resume);
extern int software_suspend(void);
#else /* CONFIG_SOFTWARE_SUSPEND */ #else /* CONFIG_SOFTWARE_SUSPEND */
static inline int software_suspend(void) static inline int software_suspend(void)
{ {
......
obj-y := main.o process.o console.o pm.o obj-y := main.o process.o console.o pm.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o obj-$(CONFIG_SOFTWARE_SUSPEND) += disk.o swsusp.o
obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
#include <linux/kbd_kern.h> #include <linux/kbd_kern.h>
#include "power.h" #include "power.h"
static int new_loglevel = 7; static int new_loglevel = 10;
static int orig_loglevel; static int orig_loglevel;
static int orig_fgconsole, orig_kmsg; static int orig_fgconsole, orig_kmsg;
......
/*
* kernel/power/disk.c - Suspend-to-disk support.
*
* Copyright (c) 2003 Patrick Mochel
* Copyright (c) 2003 Open Source Development Lab
*
* This file is release under the GPLv2
*
*/
#define DEBUG
#include <linux/suspend.h>
#include <linux/reboot.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include "power.h"
extern u32 pm_disk_mode;
extern struct pm_ops * pm_ops;
extern int swsusp_save(void);
extern int swsusp_write(void);
extern int swsusp_read(void);
extern int swsusp_restore(void);
extern int swsusp_free(void);
extern long sys_sync(void);
/**
* power_down - Shut machine down for hibernate.
* @mode: Suspend-to-disk mode
*
* Use the platform driver, if configured so, and return gracefully if it
* fails.
* Otherwise, try to power off and reboot. If they fail, halt the machine,
* there ain't no turning back.
*/
static int power_down(u32 mode)
{
unsigned long flags;
int error = 0;
local_irq_save(flags);
device_power_down(PM_SUSPEND_DISK);
switch(mode) {
case PM_DISK_PLATFORM:
error = pm_ops->enter(PM_SUSPEND_DISK);
break;
case PM_DISK_SHUTDOWN:
printk("Powering off system\n");
machine_power_off();
break;
case PM_DISK_REBOOT:
machine_restart(NULL);
break;
}
machine_halt();
device_power_up();
local_irq_restore(flags);
return 0;
}
static int in_suspend __nosavedata = 0;
/**
* free_some_memory - Try to free as much memory as possible
*
* ... but do not OOM-kill anyone
*
* Notice: all userland should be stopped at this point, or
* livelock is possible.
*/
static void free_some_memory(void)
{
printk("Freeing memory: ");
while (shrink_all_memory(10000))
printk(".");
printk("|\n");
blk_run_queues();
}
static inline void platform_finish(void)
{
if (pm_disk_mode == PM_DISK_PLATFORM) {
if (pm_ops && pm_ops->finish)
pm_ops->finish(PM_SUSPEND_DISK);
}
}
static void finish(void)
{
device_resume();
platform_finish();
thaw_processes();
pm_restore_console();
}
static int prepare(void)
{
int error;
pm_prepare_console();
sys_sync();
if (freeze_processes()) {
error = -EBUSY;
goto Thaw;
}
if (pm_disk_mode == PM_DISK_PLATFORM) {
if (pm_ops && pm_ops->prepare) {
if ((error = pm_ops->prepare(PM_SUSPEND_DISK)))
goto Thaw;
}
}
/* Free memory before shutting down devices. */
free_some_memory();
if ((error = device_suspend(PM_SUSPEND_DISK)))
goto Finish;
return 0;
Finish:
platform_finish();
Thaw:
thaw_processes();
pm_restore_console();
return error;
}
/**
* pm_suspend_disk - The granpappy of power management.
*
* If we're going through the firmware, then get it over with quickly.
*
* If not, then call swsusp to do it's thing, then figure out how
* to power down the system.
*/
int pm_suspend_disk(void)
{
int error;
if ((error = prepare()))
return error;
pr_debug("PM: Attempting to suspend to disk.\n");
if (pm_disk_mode == PM_DISK_FIRMWARE)
return pm_ops->enter(PM_SUSPEND_DISK);
pr_debug("PM: snapshotting memory.\n");
in_suspend = 1;
local_irq_disable();
if ((error = swsusp_save()))
goto Done;
pr_debug("PM: writing image.\n");
/*
* FIXME: Leftover from swsusp. Are they necessary?
*/
mb();
barrier();
error = swsusp_write();
if (!error && in_suspend) {
error = power_down(pm_disk_mode);
pr_debug("PM: Power down failed.\n");
} else
pr_debug("PM: Image restored successfully.\n");
swsusp_free();
Done:
local_irq_enable();
finish();
return error;
}
/**
* pm_resume - Resume from a saved 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 pm_suspend_disk() ) if everything goes well.
* Otherwise, we fail gracefully and return to the normally
* scheduled program.
*
*/
static int pm_resume(void)
{
int error;
pr_debug("PM: Reading swsusp image.\n");
if ((error = swsusp_read()))
goto Done;
pr_debug("PM: Preparing system for restore.\n");
if ((error = prepare()))
goto Free;
barrier();
mb();
local_irq_disable();
/* FIXME: The following (comment and mdelay()) are from swsusp.
* Are they really necessary?
*
* We do not want some readahead with DMA to corrupt our memory, right?
* Do it with disabled interrupts for best effect. That way, if some
* driver scheduled DMA, we have good chance for DMA to finish ;-).
*/
pr_debug("PM: Waiting for DMAs to settle down.\n");
mdelay(1000);
pr_debug("PM: Restoring saved image.\n");
swsusp_restore();
local_irq_enable();
pr_debug("PM: Restore failed, recovering.n");
finish();
Free:
swsusp_free();
Done:
pr_debug("PM: Resume from disk failed.\n");
return 0;
}
late_initcall(pm_resume);
static char * pm_disk_modes[] = {
[PM_DISK_FIRMWARE] = "firmware",
[PM_DISK_PLATFORM] = "platform",
[PM_DISK_SHUTDOWN] = "shutdown",
[PM_DISK_REBOOT] = "reboot",
};
/**
* disk - Control suspend-to-disk mode
*
* Suspend-to-disk can be handled in several ways. The greatest
* distinction is who writes memory to disk - the firmware or the OS.
* If the firmware does it, we assume that it also handles suspending
* the system.
* If the OS does it, then we have three options for putting the system
* to sleep - using the platform driver (e.g. ACPI or other PM registers),
* powering off the system or rebooting the system (for testing).
*
* The system will support either 'firmware' or 'platform', and that is
* known a priori (and encoded in pm_ops). But, the user may choose
* 'shutdown' or 'reboot' as alternatives.
*
* show() will display what the mode is currently set to.
* store() will accept one of
*
* 'firmware'
* 'platform'
* 'shutdown'
* 'reboot'
*
* It will only change to 'firmware' or 'platform' if the system
* supports it (as determined from pm_ops->pm_disk_mode).
*/
static ssize_t disk_show(struct subsystem * subsys, char * buf)
{
return sprintf(buf,"%s\n",pm_disk_modes[pm_disk_mode]);
}
static ssize_t disk_store(struct subsystem * s, const char * buf, size_t n)
{
int error = 0;
int i;
u32 mode = 0;
down(&pm_sem);
for (i = PM_DISK_FIRMWARE; i < PM_DISK_MAX; i++) {
if (!strcmp(buf,pm_disk_modes[i])) {
mode = i;
break;
}
}
if (mode) {
if (mode == PM_DISK_SHUTDOWN || mode == PM_DISK_REBOOT)
pm_disk_mode = mode;
else {
if (pm_ops && pm_ops->enter &&
(mode == pm_ops->pm_disk_mode))
pm_disk_mode = mode;
else
error = -EINVAL;
}
} else
error = -EINVAL;
pr_debug("PM: suspend-to-disk mode set to '%s'\n",
pm_disk_modes[mode]);
up(&pm_sem);
return error ? error : n;
}
power_attr(disk);
static struct attribute * g[] = {
&disk_attr.attr,
NULL,
};
static struct attribute_group attr_group = {
.attrs = g,
};
static int __init pm_disk_init(void)
{
return sysfs_create_group(&power_subsys.kset.kobj,&attr_group);
}
core_initcall(pm_disk_init);
This diff is collapsed.
...@@ -10,34 +10,27 @@ ...@@ -10,34 +10,27 @@
#ifdef CONFIG_SOFTWARE_SUSPEND #ifdef CONFIG_SOFTWARE_SUSPEND
extern int swsusp_save(void); extern int pm_suspend_disk(void);
extern int swsusp_write(void);
extern int swsusp_read(void);
extern int swsusp_restore(void);
extern int swsusp_free(void);
#else #else
static inline int swsusp_save(void) static inline int pm_suspend_disk(void)
{
return 0;
}
static inline int swsusp_write(void)
{ {
return 0; return -EPERM;
}
static inline int swsusp_read(void)
{
return 0;
}
static inline int swsusp_restore(void)
{
return 0;
}
static inline int swsusp_free(void)
{
return 0;
} }
#endif #endif
extern struct semaphore pm_sem;
#define power_attr(_name) \
static struct subsys_attribute _name##_attr = { \
.attr = { \
.name = __stringify(_name), \
.mode = 0644, \
}, \
.show = _name##_show, \
.store = _name##_store, \
}
extern struct subsystem power_subsys;
extern int freeze_processes(void); extern int freeze_processes(void);
extern void thaw_processes(void); extern void thaw_processes(void);
......
This diff is collapsed.
...@@ -456,7 +456,7 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user ...@@ -456,7 +456,7 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user
#ifdef CONFIG_SOFTWARE_SUSPEND #ifdef CONFIG_SOFTWARE_SUSPEND
case LINUX_REBOOT_CMD_SW_SUSPEND: case LINUX_REBOOT_CMD_SW_SUSPEND:
if (!pm_suspend(PM_SUSPEND_DISK)) if (!software_suspend())
break; break;
do_exit(0); do_exit(0);
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment