Commit 5abf1ee2 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 06cdb4a9 7b935493
......@@ -96,6 +96,7 @@ drivers-$(CONFIG_MATH_EMULATION) += arch/i386/math-emu/
drivers-$(CONFIG_PCI) += arch/i386/pci/
# must be linked after kernel/
drivers-$(CONFIG_OPROFILE) += arch/i386/oprofile/
drivers-$(CONFIG_PM) += arch/i386/power/
CFLAGS += $(mflags-y)
AFLAGS += $(mflags-y)
......
......@@ -17,9 +17,7 @@ obj-$(CONFIG_MCA) += mca.o
obj-$(CONFIG_X86_MSR) += msr.o
obj-$(CONFIG_X86_CPUID) += cpuid.o
obj-$(CONFIG_MICROCODE) += microcode.o
obj-$(CONFIG_PM) += suspend.o
obj-$(CONFIG_APM) += apm.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += suspend_asm.o
obj-$(CONFIG_X86_SMP) += smp.o smpboot.o
obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o
obj-$(CONFIG_X86_MPPARSE) += mpparse.o
......
obj-$(CONFIG_PM) += cpu.o
obj-$(CONFIG_SOFTWARE_SUSPEND) += swsusp.o
......@@ -8,11 +8,10 @@
.text
ENTRY(do_magic)
ENTRY(swsusp_arch_suspend)
pushl %ebx
cmpl $0,8(%esp)
jne .L1450
call do_magic_suspend_1
call save_processor_state
movl %esp, saved_context_esp
......@@ -25,14 +24,13 @@ ENTRY(do_magic)
movl %edi, saved_context_edi
pushfl ; popl saved_context_eflags
call do_magic_suspend_2
call swsusp_suspend
jmp .L1449
.p2align 4,,7
.L1450:
movl $swapper_pg_dir-__PAGE_OFFSET,%ecx
movl %ecx,%cr3
call do_magic_resume_1
movl $0,loop
cmpl $0,nr_copy_pages
je .L1453
......@@ -80,7 +78,7 @@ ENTRY(do_magic)
movl saved_context_edi, %edi
call restore_processor_state
pushl saved_context_eflags ; popfl
call do_magic_resume_2
call swsusp_resume
.L1449:
popl %ebx
ret
......
......@@ -41,7 +41,6 @@ static u32 acpi_suspend_states[] = {
static int acpi_pm_prepare(u32 state)
{
int error = 0;
u32 acpi_state = acpi_suspend_states[state];
if (!sleep_states[acpi_state])
......@@ -56,21 +55,9 @@ static int acpi_pm_prepare(u32 state)
acpi_set_firmware_waking_vector(
(acpi_physical_address) acpi_wakeup_address);
}
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);
return 0;
Err:
acpi_set_firmware_waking_vector(0);
return error;
}
......@@ -90,6 +77,15 @@ static int acpi_pm_enter(u32 state)
u32 acpi_state = acpi_suspend_states[state];
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);
switch (state)
{
......@@ -114,6 +110,15 @@ static int acpi_pm_enter(u32 state)
local_irq_restore(flags);
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;
}
......@@ -130,14 +135,6 @@ static int acpi_pm_finish(u32 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 */
acpi_set_firmware_waking_vector((acpi_physical_address) 0);
......@@ -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 = {
.prepare = acpi_pm_prepare,
.enter = acpi_pm_enter,
......
......@@ -13,12 +13,71 @@
#include "sleep.h"
#define ACPI_SYSTEM_FILE_SLEEP "sleep"
#define ACPI_SYSTEM_FILE_ALARM "alarm"
#define _COMPONENT ACPI_SYSTEM_COMPONENT
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)
{
u32 sec, min, hr;
......@@ -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 = {
.open = acpi_system_alarm_open_fs,
.read = seq_read,
......@@ -307,6 +374,12 @@ static int acpi_sleep_proc_init(void)
{
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] */
entry = create_proc_entry(ACPI_SYSTEM_FILE_ALARM,
S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir);
......
extern u8 sleep_states[];
extern acpi_status acpi_suspend (u32 state);
extern int acpi_suspend (u32 state);
......@@ -225,28 +225,30 @@ int device_add(struct device *dev)
dev->kobj.parent = &parent->kobj;
if ((error = kobject_add(&dev->kobj)))
goto register_done;
/* now take care of our own registration */
goto Error;
if ((error = device_pm_add(dev)))
goto PMError;
if ((error = bus_add_device(dev)))
goto BusError;
down_write(&devices_subsys.rwsem);
if (parent)
list_add_tail(&dev->node,&parent->children);
up_write(&devices_subsys.rwsem);
bus_add_device(dev);
device_pm_add(dev);
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
register_done:
if (error && parent)
put_device(parent);
Done:
put_device(dev);
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)
{
struct device * parent = dev->parent;
device_pm_remove(dev);
down_write(&devices_subsys.rwsem);
if (parent)
list_del_init(&dev->node);
......@@ -324,14 +324,11 @@ void device_del(struct device * dev)
*/
if (platform_notify_remove)
platform_notify_remove(dev);
bus_remove_device(dev);
device_pm_remove(dev);
kobject_del(&dev->kobj);
if (parent)
put_device(parent);
}
/**
......
......@@ -36,12 +36,14 @@ DECLARE_MUTEX(dpm_sem);
static inline void device_pm_hold(struct device * dev)
{
if (dev)
atomic_inc(&dev->power.pm_users);
}
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)
void device_pm_set_parent(struct device * dev, struct device * parent)
{
struct device * old_parent = dev->power.pm_parent;
if (old_parent)
device_pm_release(old_parent);
dev->power.pm_parent = parent;
if (parent)
device_pm_hold(parent);
}
EXPORT_SYMBOL(device_pm_set_parent);
......@@ -91,6 +91,7 @@ void device_pm_remove(struct device * dev)
dev->bus ? dev->bus->name : "No Bus", dev->kobj.name);
down(&dpm_sem);
dpm_sysfs_remove(dev);
device_pm_release(dev->power.pm_parent);
list_del(&dev->power.entry);
up(&dpm_sem);
}
......
......@@ -58,7 +58,8 @@ extern void dpm_sysfs_remove(struct device *);
/*
* resume.c
*/
extern int dpm_resume(void);
extern void dpm_resume(void);
extern void dpm_power_up(void);
extern int resume_device(struct device *);
......
......@@ -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.
*
......@@ -38,13 +51,7 @@ int resume_device(struct device * dev)
void device_resume(void)
{
down(&dpm_sem);
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);
}
dpm_resume();
up(&dpm_sem);
}
......
......@@ -81,14 +81,18 @@ int device_suspend(u32 state)
while(!list_empty(&dpm_active)) {
struct list_head * entry = dpm_active.prev;
struct device * dev = to_device(entry);
if ((error = suspend_device(dev,state)))
if ((error = suspend_device(dev,state))) {
if (error != -EAGAIN)
goto Error;
else
error = 0;
}
}
Done:
up(&dpm_sem);
return error;
Error:
device_resume();
dpm_resume();
goto Done;
}
......
......@@ -6,11 +6,12 @@
#include <asm/desc.h>
#include <asm/i387.h>
static inline void
static inline int
arch_prepare_suspend(void)
{
if (!cpu_has_pse)
panic("pse required");
return -EPERM;
return 0;
}
/* image of the saved processor state */
......@@ -38,8 +39,6 @@ struct saved_context {
extern void save_processor_state(void);
extern void restore_processor_state(void);
extern int do_magic(int resume);
#ifdef CONFIG_ACPI_SLEEP
extern unsigned long saved_eip;
extern unsigned long saved_esp;
......
......@@ -53,6 +53,7 @@ extern suspend_pagedir_t *pagedir_nosave __nosavedata;
extern void do_suspend_lowlevel(int resume);
extern void do_suspend_lowlevel_s4bios(int resume);
extern int software_suspend(void);
#else /* CONFIG_SOFTWARE_SUSPEND */
static inline int software_suspend(void)
{
......
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
......@@ -8,7 +8,7 @@
#include <linux/kbd_kern.h>
#include "power.h"
static int new_loglevel = 7;
static int new_loglevel = 10;
static int orig_loglevel;
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 @@
#ifdef CONFIG_SOFTWARE_SUSPEND
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 int pm_suspend_disk(void);
#else
static inline int swsusp_save(void)
{
return 0;
}
static inline int swsusp_write(void)
static inline int pm_suspend_disk(void)
{
return 0;
}
static inline int swsusp_read(void)
{
return 0;
}
static inline int swsusp_restore(void)
{
return 0;
}
static inline int swsusp_free(void)
{
return 0;
return -EPERM;
}
#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 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
#ifdef CONFIG_SOFTWARE_SUSPEND
case LINUX_REBOOT_CMD_SW_SUSPEND:
if (!pm_suspend(PM_SUSPEND_DISK))
if (!software_suspend())
break;
do_exit(0);
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