Commit 632f2e90 authored by Patrick Mochel's avatar Patrick Mochel

[acpi] Fix CONFIG_ACPI_HT dependencies. Again.

Hyperthreading is a Pentium 4-specific feature. It should only depend on 
whether the user has configured P4 support in. 
parents 76e8b3a4 da94d134
...@@ -6,7 +6,7 @@ menu "ACPI (Advanced Configuration and Power Interface) Support" ...@@ -6,7 +6,7 @@ menu "ACPI (Advanced Configuration and Power Interface) Support"
config ACPI_HT config ACPI_HT
bool "ACPI Processor Enumeration for HT" bool "ACPI Processor Enumeration for HT"
depends on X86 depends on MPENTIUM4
default y default y
---help--- ---help---
ACPI enumerates both logical (a.k.a. Hyper-Threaded -- HT) ACPI enumerates both logical (a.k.a. Hyper-Threaded -- HT)
...@@ -30,7 +30,7 @@ config ACPI ...@@ -30,7 +30,7 @@ config ACPI
bool "Full ACPI Support" bool "Full ACPI Support"
depends on !X86_VISWS depends on !X86_VISWS
depends on !IA64_HP_SIM depends on !IA64_HP_SIM
depends on IA64 || (X86 && ACPI_HT) depends on IA64 || (X86 || ACPI_HT)
default y default y
---help--- ---help---
Advanced Configuration and Power Interface (ACPI) support for Advanced Configuration and Power Interface (ACPI) support for
......
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
* sleep.c - ACPI sleep support. * sleep.c - ACPI sleep support.
* *
* Copyright (c) 2000-2003 Patrick Mochel * Copyright (c) 2000-2003 Patrick Mochel
* Copyright (c) 2003 Open Source Development Lab
*
* This file is released under the GPLv2.
* *
* Portions are
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
*/ */
#include <linux/delay.h> #include <linux/delay.h>
...@@ -16,274 +16,151 @@ ...@@ -16,274 +16,151 @@
#include <acpi/acpi_drivers.h> #include <acpi/acpi_drivers.h>
#include "sleep.h" #include "sleep.h"
#define _COMPONENT ACPI_SYSTEM_COMPONENT
ACPI_MODULE_NAME ("sleep")
u8 sleep_states[ACPI_S_STATE_COUNT]; u8 sleep_states[ACPI_S_STATE_COUNT];
static struct pm_ops acpi_pm_ops;
extern void do_suspend_lowlevel_s4bios(int); extern void do_suspend_lowlevel_s4bios(int);
extern void do_suspend_lowlevel(int); extern void do_suspend_lowlevel(int);
/** static u32 acpi_suspend_states[] = {
* acpi_system_restore_state - OS-specific restoration of state [PM_SUSPEND_ON] = ACPI_STATE_S0,
* @state: sleep state we're exiting [PM_SUSPEND_STANDBY] = ACPI_STATE_S1,
* [PM_SUSPEND_MEM] = ACPI_STATE_S3,
* Note that if we're coming back from S4, the memory image should have already [PM_SUSPEND_DISK] = ACPI_STATE_S4,
* been loaded from the disk and is already in place. (Otherwise how else would we };
* be here?).
*/
acpi_status
acpi_system_restore_state (
u32 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();
/* wait for power to come back */
mdelay(10);
/* turn all the devices back on */
device_resume(RESUME_POWER_ON);
/* enable interrupts once again */
ACPI_ENABLE_IRQS();
/* restore device context */
device_resume(RESUME_RESTORE_STATE);
if (dmi_broken & BROKEN_INIT_AFTER_S1) {
printk("Broken toshiba laptop -> kicking interrupts\n");
init_8259A(0);
}
return AE_OK;
}
/** /**
* acpi_system_save_state - save OS specific state and power down devices * acpi_pm_prepare - Do preliminary suspend work.
* @state: sleep state we're entering. * @state: suspend state we're entering.
* *
* This handles saving all context to memory, and possibly disk. * Make sure we support the state. If we do, and we need it, set the
* First, we call to the device driver layer to save device state. * firmware waking vector and do arch-specific nastiness to get the
* Once we have that, we save whatevery processor and kernel state we * wakeup code to the waking vector.
* need to memory.
*/ */
acpi_status
acpi_system_save_state( static int acpi_pm_prepare(u32 state)
u32 state)
{ {
int error = 0; int error = 0;
u32 acpi_state = acpi_suspend_states[state];
/* Send notification to devices that they will be suspended. if (!sleep_states[acpi_state])
* If any device or driver cannot make the transition, either up return -EPERM;
* or down, we'll get an error back.
*/
error = device_suspend(state, SUSPEND_NOTIFY);
if (error)
return AE_ERROR;
if (state < ACPI_STATE_S5) {
/* Tell devices to stop I/O and actually save their state. /* do we have a wakeup address for S2 and S3? */
* It is theoretically possible that something could fail, /* Here, we support only S4BIOS, those we set the wakeup address */
* so handle that gracefully.. /* S4OS is only supported for now via swsusp.. */
*/ if (state == PM_SUSPEND_MEM || state == PM_SUSPEND_DISK) {
error = device_suspend(state, SUSPEND_SAVE_STATE); if (!acpi_wakeup_address)
if (error) { return -EFAULT;
/* tell devices to restore state if they have acpi_set_firmware_waking_vector(
* it saved and to start taking I/O requests. (acpi_physical_address) acpi_wakeup_address);
*/
device_resume(RESUME_RESTORE_STATE);
return error;
} }
/* flush caches */
ACPI_FLUSH_CPU_CACHE(); ACPI_FLUSH_CPU_CACHE();
/* Do arch specific saving of state. */ /* Do arch specific saving of state. */
if (state > ACPI_STATE_S1) { if (state > PM_SUSPEND_STANDBY) {
error = acpi_save_state_mem(); if ((error = acpi_save_state_mem()))
goto Err;
if (!error && (state == ACPI_STATE_S4))
error = acpi_save_state_disk();
if (error) {
device_resume(RESUME_RESTORE_STATE);
return error;
}
}
} }
/* disable interrupts acpi_enter_sleep_state_prep(acpi_state);
* Note that acpi_suspend -- our caller -- will do this once we return.
* But, we want it done early, so we don't get any suprises during
* the device suspend sequence.
*/
ACPI_DISABLE_IRQS();
/* Unconditionally turn off devices.
* Obvious if we enter a sleep state.
* If entering S5 (soft off), this should put devices in a
* quiescent state.
*/
error = device_suspend(state, SUSPEND_POWER_DOWN);
/* We're pretty screwed if we got an error from this.
* We try to recover by simply calling our own restore_state
* function; see above for definition.
*
* If it's S5 though, go through with it anyway..
*/
if (error && state != ACPI_STATE_S5)
acpi_system_restore_state(state);
return error ? AE_ERROR : AE_OK; return 0;
Err:
acpi_set_firmware_waking_vector(0);
return error;
} }
/**************************************************************************** /**
* * acpi_pm_enter - Actually enter a sleep state.
* FUNCTION: acpi_system_suspend * @state: State we're entering.
*
* PARAMETERS: %state: Sleep state to enter.
*
* RETURN: acpi_status, whether or not we successfully entered and
* exited sleep.
*
* DESCRIPTION: Perform OS-specific action to enter sleep state.
* This is the final step in going to sleep, per spec. If we
* know we're coming back (i.e. not entering S5), we save the
* processor flags. [ We'll have to save and restore them anyway,
* so we use the arch-agnostic save_flags and restore_flags
* here.] We then set the place to return to in arch-specific
* globals using arch_set_return_point. Finally, we call the
* ACPI function to write the proper values to I/O ports.
* *
****************************************************************************/ * Flush caches and go to sleep. For STR or STD, we have to call
* arch-specific assembly, which in turn call acpi_enter_sleep_state().
* It's unfortunate, but it works. Please fix if you're feeling frisky.
*/
acpi_status static int acpi_pm_enter(u32 state)
acpi_system_suspend(
u32 state)
{ {
acpi_status status = AE_ERROR; acpi_status status = AE_OK;
unsigned long flags = 0; unsigned long flags = 0;
u32 acpi_state = acpi_suspend_states[state];
ACPI_FLUSH_CPU_CACHE();
local_irq_save(flags); local_irq_save(flags);
switch (state) switch (state)
{ {
case ACPI_STATE_S1: case PM_SUSPEND_STANDBY:
barrier(); barrier();
status = acpi_enter_sleep_state(state); status = acpi_enter_sleep_state(acpi_state);
break; break;
case ACPI_STATE_S2: case PM_SUSPEND_MEM:
case ACPI_STATE_S3:
do_suspend_lowlevel(0); do_suspend_lowlevel(0);
break; break;
case ACPI_STATE_S4: case PM_SUSPEND_DISK:
if (acpi_pm_ops.pm_disk_mode == PM_DISK_PLATFORM)
status = acpi_enter_sleep_state(acpi_state);
else
do_suspend_lowlevel_s4bios(0); do_suspend_lowlevel_s4bios(0);
break; break;
default: default:
printk(KERN_WARNING PREFIX "don't know how to handle %d state.\n", state); return -EINVAL;
break;
} }
local_irq_restore(flags); local_irq_restore(flags);
printk(KERN_DEBUG "Back to C!\n"); printk(KERN_DEBUG "Back to C!\n");
return status; return ACPI_SUCCESS(status) ? 0 : -EFAULT;
} }
/** /**
* acpi_suspend - OS-agnostic system suspend/resume support (S? states) * acpi_pm_finish - Finish up suspend sequence.
* @state: state we're entering * @state: State we're coming out of.
* *
* This is called after we wake back up (or if entering the sleep state
* failed).
*/ */
acpi_status
acpi_suspend (
u32 state)
{
acpi_status status;
/* Suspend is hard to get right on SMP. */
if (num_online_cpus() != 1)
return AE_ERROR;
/* get out if state is invalid */
if (state < ACPI_STATE_S1 || state > ACPI_STATE_S5)
return AE_ERROR;
/* Since we handle S4OS via a different path (swsusp), give up if no s4bios. */
if (state == ACPI_STATE_S4 && !acpi_gbl_FACS->S4bios_f)
return AE_ERROR;
pm_prepare_console();
/*
* TBD: S1 can be done without device_suspend. Make a CONFIG_XX
* to handle however when S1 failed without device_suspend.
*/
if (freeze_processes()) {
status = AE_ERROR;
goto Done;
}
/* do we have a wakeup address for S2 and S3? */
/* Here, we support only S4BIOS, those we set the wakeup address */
/* S4OS is only supported for now via swsusp.. */
if (state == ACPI_STATE_S2 || state == ACPI_STATE_S3 || state == ACPI_STATE_S4) {
if (!acpi_wakeup_address)
return AE_ERROR;
acpi_set_firmware_waking_vector((acpi_physical_address) acpi_wakeup_address);
}
status = acpi_system_save_state(state);
if (!ACPI_SUCCESS(status))
return status;
acpi_enter_sleep_state_prep(state);
/* disable interrupts and flush caches */
ACPI_DISABLE_IRQS();
ACPI_FLUSH_CPU_CACHE();
/* perform OS-specific sleep actions */ static int acpi_pm_finish(u32 state)
status = acpi_system_suspend(state); {
/* Even if we failed to go to sleep, all of the devices are in an suspended
* mode. So, we run these unconditionaly to make sure we have a usable system
* no matter what.
*/
acpi_leave_sleep_state(state); acpi_leave_sleep_state(state);
acpi_system_restore_state(state);
/* make sure interrupts are enabled */ /* restore processor state
ACPI_ENABLE_IRQS(); * 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);
Done: if (dmi_broken & BROKEN_INIT_AFTER_S1) {
thaw_processes(); printk("Broken toshiba laptop -> kicking interrupts\n");
pm_restore_console(); init_8259A(0);
return status; }
return 0;
} }
static struct pm_ops acpi_pm_ops = {
.prepare = acpi_pm_prepare,
.enter = acpi_pm_enter,
.finish = acpi_pm_finish,
};
static int __init acpi_sleep_init(void) static int __init acpi_sleep_init(void)
{ {
int i = 0; int i = 0;
ACPI_FUNCTION_TRACE("acpi_system_add_fs");
if (acpi_disabled) if (acpi_disabled)
return_VALUE(0); return 0;
printk(KERN_INFO PREFIX "(supports"); printk(KERN_INFO PREFIX "(supports");
for (i=0; i<ACPI_S_STATE_COUNT; i++) { for (i=0; i<ACPI_S_STATE_COUNT; i++) {
...@@ -294,14 +171,19 @@ static int __init acpi_sleep_init(void) ...@@ -294,14 +171,19 @@ static int __init acpi_sleep_init(void)
sleep_states[i] = 1; sleep_states[i] = 1;
printk(" S%d", i); printk(" S%d", i);
} }
if (i == ACPI_STATE_S4 && acpi_gbl_FACS->S4bios_f) { if (i == ACPI_STATE_S4) {
if (acpi_gbl_FACS->S4bios_f) {
sleep_states[i] = 1; sleep_states[i] = 1;
printk(" S4bios"); printk(" S4bios");
acpi_pm_ops.pm_disk_mode = PM_DISK_FIRMWARE;
} else if (sleep_states[i])
acpi_pm_ops.pm_disk_mode = PM_DISK_PLATFORM;
} }
} }
printk(")\n"); printk(")\n");
return_VALUE(0); pm_set_ops(&acpi_pm_ops);
return 0;
} }
late_initcall(acpi_sleep_init); late_initcall(acpi_sleep_init);
...@@ -13,80 +13,12 @@ ...@@ -13,80 +13,12 @@
#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)
{
acpi_status status = AE_ERROR;
char state_string[12] = {'\0'};
u32 state = 0;
ACPI_FUNCTION_TRACE("acpi_system_write_sleep");
if (count > sizeof(state_string) - 1)
goto Done;
if (copy_from_user(state_string, buffer, count))
return_VALUE(-EFAULT);
state_string[count] = '\0';
state = simple_strtoul(state_string, NULL, 0);
if (state < 1 || state > 4)
goto Done;
if (!sleep_states[state])
goto Done;
#ifdef CONFIG_SOFTWARE_SUSPEND
if (state == 4) {
software_suspend();
goto Done;
}
#endif
status = acpi_suspend(state);
Done:
if (ACPI_FAILURE(status))
return_VALUE(-EINVAL);
else
return_VALUE(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;
...@@ -362,14 +294,6 @@ acpi_system_write_alarm ( ...@@ -362,14 +294,6 @@ 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,
...@@ -383,12 +307,6 @@ static int acpi_sleep_proc_init(void) ...@@ -383,12 +307,6 @@ 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);
......
...@@ -76,6 +76,7 @@ int device_pm_add(struct device * dev) ...@@ -76,6 +76,7 @@ int device_pm_add(struct device * dev)
pr_debug("PM: Adding info for %s:%s\n", pr_debug("PM: Adding info for %s:%s\n",
dev->bus ? dev->bus->name : "No Bus", dev->kobj.name); dev->bus ? dev->bus->name : "No Bus", dev->kobj.name);
atomic_set(&dev->power.pm_users,0);
down(&dpm_sem); down(&dpm_sem);
list_add_tail(&dev->power.entry,&dpm_active); list_add_tail(&dev->power.entry,&dpm_active);
device_pm_set_parent(dev,dev->parent); device_pm_set_parent(dev,dev->parent);
......
...@@ -186,9 +186,46 @@ static inline void pm_dev_idle(struct pm_dev *dev) {} ...@@ -186,9 +186,46 @@ static inline void pm_dev_idle(struct pm_dev *dev) {}
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
/*
* Callbacks for platform drivers to implement.
*/
extern void (*pm_idle)(void); extern void (*pm_idle)(void);
extern void (*pm_power_off)(void); extern void (*pm_power_off)(void);
enum {
PM_SUSPEND_ON,
PM_SUSPEND_STANDBY,
PM_SUSPEND_MEM,
PM_SUSPEND_DISK,
PM_SUSPEND_MAX,
};
enum {
PM_DISK_FIRMWARE = 1,
PM_DISK_PLATFORM,
PM_DISK_SHUTDOWN,
PM_DISK_REBOOT,
PM_DISK_MAX,
};
struct pm_ops {
u32 pm_disk_mode;
int (*prepare)(u32 state);
int (*enter)(u32 state);
int (*finish)(u32 state);
};
extern void pm_set_ops(struct pm_ops *);
extern int pm_suspend(u32 state);
/*
* Device power management
*/
struct device; struct device;
struct dev_pm_info { struct dev_pm_info {
......
...@@ -8,49 +8,338 @@ ...@@ -8,49 +8,338 @@
* *
*/ */
#include <linux/suspend.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/reboot.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/pm.h> #include <linux/pm.h>
#include "power.h"
static DECLARE_MUTEX(pm_sem);
static int standby(void) static struct pm_ops * pm_ops = NULL;
static u32 pm_disk_mode = PM_DISK_SHUTDOWN;
#ifdef CONFIG_SOFTWARE_SUSPEND
static int have_swsusp = 1;
#else
static int have_swsusp = 0;
#endif
/**
* pm_set_ops - Set the global power method table.
* @ops: Pointer to ops structure.
*/
void pm_set_ops(struct pm_ops * ops)
{ {
return 0; down(&pm_sem);
pm_ops = ops;
if (ops->pm_disk_mode && ops->pm_disk_mode < PM_DISK_MAX)
pm_disk_mode = ops->pm_disk_mode;
up(&pm_sem);
} }
static int suspend(void)
/**
* pm_suspend_standby - Enter 'standby' state.
*
* 'standby' is also known as 'Power-On Suspend'. Here, we power down
* devices, disable interrupts, and enter the state.
*/
static int pm_suspend_standby(void)
{ {
return 0; int error = 0;
unsigned long flags;
if (!pm_ops || !pm_ops->enter)
return -EPERM;
if ((error = device_pm_power_down(PM_SUSPEND_STANDBY)))
goto Done;
local_irq_save(flags);
error = pm_ops->enter(PM_SUSPEND_STANDBY);
local_irq_restore(flags);
device_pm_power_up();
Done:
return error;
}
/**
* pm_suspend_mem - Enter suspend-to-RAM state.
*
* Identical to pm_suspend_standby() - we power down devices, disable
* interrupts, and enter the low-power state.
*/
static int pm_suspend_mem(void)
{
int error = 0;
unsigned long flags;
if (!pm_ops || !pm_ops->enter)
return -EPERM;
if ((error = device_pm_power_down(PM_SUSPEND_STANDBY)))
goto Done;
local_irq_save(flags);
error = pm_ops->enter(PM_SUSPEND_STANDBY);
local_irq_restore(flags);
device_pm_power_up();
Done:
return error;
} }
static int hibernate(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)
{ {
switch(mode) {
case PM_DISK_PLATFORM:
return pm_ops->enter(PM_SUSPEND_DISK);
case PM_DISK_SHUTDOWN:
machine_power_off();
break;
case PM_DISK_REBOOT:
machine_restart(NULL);
break;
}
machine_halt();
return 0; return 0;
} }
static int in_suspend __nosavedata = 0;
/**
* 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.
*/
static int pm_suspend_disk(void)
{
int error;
pr_debug("PM: Attempting to suspend to disk.\n");
if (pm_disk_mode == PM_DISK_FIRMWARE)
return pm_ops->enter(PM_SUSPEND_DISK);
if (!have_swsusp)
return -EPERM;
pr_debug("PM: snapshotting memory.\n");
in_suspend = 1;
if ((error = swsusp_save()))
goto Done;
if (in_suspend) {
pr_debug("PM: writing image.\n");
error = swsusp_write();
if (!error)
error = power_down(pm_disk_mode);
pr_debug("PM: Power down failed.\n");
} else
pr_debug("PM: Image restored successfully.\n");
swsusp_free();
Done:
return error;
}
#define decl_state(_name) \ #define decl_state(_name) \
{ .name = __stringify(_name), .fn = _name } { .name = __stringify(_name), .fn = pm_suspend_##_name }
struct pm_state { struct pm_state {
char * name; char * name;
int (*fn)(void); int (*fn)(void);
} pm_states[] = { } pm_states[] = {
decl_state(standby), [PM_SUSPEND_STANDBY] = decl_state(standby),
decl_state(suspend), [PM_SUSPEND_MEM] = decl_state(mem),
decl_state(hibernate), [PM_SUSPEND_DISK] = decl_state(disk),
{ NULL }, { NULL },
}; };
static int enter_state(struct pm_state * state) /**
* suspend_prepare - Do prep work before entering low-power state.
* @state: State we're entering.
*
* This is common code that is called for each state that we're
* entering. Allocate a console, stop all processes, then make sure
* the platform can enter the requested state.
*/
static int suspend_prepare(u32 state)
{
int error = 0;
pm_prepare_console();
if (freeze_processes()) {
error = -EAGAIN;
goto Thaw;
}
if (pm_ops && pm_ops->prepare) {
if ((error = pm_ops->prepare(state)))
goto Thaw;
}
if ((error = device_pm_suspend(state)))
goto Finish;
return 0;
Done:
pm_restore_console();
return error;
Finish:
if (pm_ops && pm_ops->finish)
pm_ops->finish(state);
Thaw:
thaw_processes();
goto Done;
}
/**
* suspend_finish - Do final work before exiting suspend sequence.
* @state: State we're coming out of.
*
* Call platform code to clean up, restart processes, and free the
* console that we've allocated.
*/
static void suspend_finish(u32 state)
{
device_pm_resume();
if (pm_ops && pm_ops->finish)
pm_ops->finish(state);
thaw_processes();
pm_restore_console();
}
/**
* enter_state - Do common work of entering low-power state.
* @state: pm_state structure for state we're entering.
*
* Make sure we're the only ones trying to enter a sleep state. Fail
* if someone has beat us to it, since we don't want anything weird to
* happen when we wake up.
* Then, do the setup for suspend, enter the state, and cleaup (after
* we've woken up).
*/
static int enter_state(u32 state)
{
int error;
struct pm_state * s = &pm_states[state];
if (down_trylock(&pm_sem))
return -EBUSY;
/* Suspend is hard to get right on SMP. */
if (num_online_cpus() != 1) {
error = -EPERM;
goto Unlock;
}
pr_debug("PM: Preparing system for suspend.\n");
if ((error = suspend_prepare(state)))
goto Unlock;
pr_debug("PM: Entering state.\n");
error = s->fn();
pr_debug("PM: Finishing up.\n");
suspend_finish(state);
Unlock:
up(&pm_sem);
return error;
}
/**
* pm_suspend - Externally visible function for suspending system.
* @state: Enumarted value of state to enter.
*
* Determine whether or not value is within range, get state
* structure, and enter (above).
*/
int pm_suspend(u32 state)
{ {
return state->fn(); if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX)
return enter_state(state);
return -EINVAL;
} }
/**
* 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;
if (!have_swsusp)
return 0;
pr_debug("PM: Reading swsusp image.\n");
if ((error = swsusp_read()))
goto Done;
pr_debug("PM: Preparing system for restore.\n");
if ((error = suspend_prepare(PM_SUSPEND_DISK)))
goto Free;
pr_debug("PM: Restoring saved image.\n");
swsusp_restore();
pr_debug("PM: Restore failed, recovering.n");
suspend_finish(PM_SUSPEND_DISK);
Free:
swsusp_free();
Done:
pr_debug("PM: Resume from disk failed.\n");
return 0;
}
late_initcall(pm_resume);
decl_subsys(power,NULL,NULL); decl_subsys(power,NULL,NULL);
...@@ -65,12 +354,87 @@ static struct subsys_attribute _name##_attr = { \ ...@@ -65,12 +354,87 @@ static struct subsys_attribute _name##_attr = { \
.store = _name##_store, \ .store = _name##_store, \
} }
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);
/** /**
* state - control system power state. * state - control system power state.
* *
* show() returns what states are supported, which is hard-coded to * show() returns what states are supported, which is hard-coded to
* 'standby' (Power-On Suspend), 'suspend' (Suspend-to-RAM), and * 'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and
* 'hibernate' (Suspend-to-Disk). * 'disk' (Suspend-to-Disk).
* *
* store() accepts one of those strings, translates it into the * store() accepts one of those strings, translates it into the
* proper enumerated value, and initiates a suspend transition. * proper enumerated value, and initiates a suspend transition.
...@@ -87,22 +451,21 @@ static ssize_t state_show(struct subsystem * subsys, char * buf) ...@@ -87,22 +451,21 @@ static ssize_t state_show(struct subsystem * subsys, char * buf)
return (s - buf); return (s - buf);
} }
static ssize_t state_store(struct subsystem * s, const char * buf, size_t n) static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n)
{ {
struct pm_state * state; u32 state;
struct pm_state * s;
int error; int error;
char * end = strchr(buf,'\n');
if (end) for (state = 0; state < PM_SUSPEND_MAX; state++) {
*end = '\0'; s = &pm_states[state];
if (s->name && !strcmp(buf,s->name))
for (state = &pm_states[0]; state; state++) {
if (!strcmp(buf,state->name))
break; break;
} }
if (!state) if (s)
return -EINVAL;
error = enter_state(state); error = enter_state(state);
else
error = -EINVAL;
return error ? error : n; return error ? error : n;
} }
...@@ -110,6 +473,7 @@ power_attr(state); ...@@ -110,6 +473,7 @@ power_attr(state);
static struct attribute * g[] = { static struct attribute * g[] = {
&state_attr.attr, &state_attr.attr,
&disk_attr.attr,
NULL, NULL,
}; };
......
...@@ -7,3 +7,33 @@ ...@@ -7,3 +7,33 @@
#if defined(CONFIG_VT) && defined(CONFIG_VT_CONSOLE) #if defined(CONFIG_VT) && defined(CONFIG_VT_CONSOLE)
#define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1) #define SUSPEND_CONSOLE (MAX_NR_CONSOLES-1)
#endif #endif
#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);
#else
static inline int swsusp_save(void)
{
return 0;
}
static inline int swsusp_write(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;
}
#endif
...@@ -1062,6 +1062,56 @@ static int software_resume(void) ...@@ -1062,6 +1062,56 @@ static int software_resume(void)
late_initcall(software_resume); late_initcall(software_resume);
/**
* swsusp_save - Snapshot memory
*/
int swsusp_save(void)
{
return -EPERM;
}
/**
* swsusp_write - Write saved memory image to swap.
*/
int swsusp_write(void)
{
return 0;
}
/**
* swsusp_read - Read saved image from swap.
*/
int swsusp_read(void)
{
return -ENOENT;
}
/**
* swsusp_restore - Replace running kernel with saved image.
*/
int swsusp_restore(void)
{
return 0;
}
/**
* swsusp_free - Free memory allocated to hold snapshot.
*/
int swsusp_free(void)
{
return 0;
}
static int __init resume_setup(char *str) static int __init resume_setup(char *str)
{ {
strncpy( resume_file, str, 255 ); strncpy( resume_file, str, 255 );
......
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