Commit 3cc8a5f4 authored by Len Brown's avatar Len Brown

Merge branch 'suspend' into release

parents d0302bc6 ada9cfdd
...@@ -150,16 +150,20 @@ and is between 256 and 4096 characters. It is defined in the file ...@@ -150,16 +150,20 @@ and is between 256 and 4096 characters. It is defined in the file
default: 0 default: 0
acpi_sleep= [HW,ACPI] Sleep options acpi_sleep= [HW,ACPI] Sleep options
Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig, old_ordering } Format: { s3_bios, s3_mode, s3_beep, s4_nohwsig,
See Documentation/power/video.txt for s3_bios and s3_mode. old_ordering, s4_nonvs }
See Documentation/power/video.txt for information on
s3_bios and s3_mode.
s3_beep is for debugging; it makes the PC's speaker beep s3_beep is for debugging; it makes the PC's speaker beep
as soon as the kernel's real-mode entry point is called. as soon as the kernel's real-mode entry point is called.
s4_nohwsig prevents ACPI hardware signature from being s4_nohwsig prevents ACPI hardware signature from being
used during resume from hibernation. used during resume from hibernation.
old_ordering causes the ACPI 1.0 ordering of the _PTS old_ordering causes the ACPI 1.0 ordering of the _PTS
control method, wrt putting devices into low power control method, with respect to putting devices into
states, to be enforced (the ACPI 2.0 ordering of _PTS is low power states, to be enforced (the ACPI 2.0 ordering
used by default). of _PTS is used by default).
s4_nonvs prevents the kernel from saving/restoring the
ACPI NVS memory during hibernation.
acpi_sci= [HW,ACPI] ACPI System Control Interrupt trigger mode acpi_sci= [HW,ACPI] ACPI System Control Interrupt trigger mode
Format: { level | edge | high | low } Format: { level | edge | high | low }
...@@ -194,7 +198,7 @@ and is between 256 and 4096 characters. It is defined in the file ...@@ -194,7 +198,7 @@ and is between 256 and 4096 characters. It is defined in the file
acpi_skip_timer_override [HW,ACPI] acpi_skip_timer_override [HW,ACPI]
Recognize and ignore IRQ0/pin2 Interrupt Override. Recognize and ignore IRQ0/pin2 Interrupt Override.
For broken nForce2 BIOS resulting in XT-PIC timer. For broken nForce2 BIOS resulting in XT-PIC timer.
acpi_use_timer_override [HW,ACPI} acpi_use_timer_override [HW,ACPI]
Use timer override. For some broken Nvidia NF5 boards Use timer override. For some broken Nvidia NF5 boards
that require a timer override, but don't have that require a timer override, but don't have
HPET HPET
...@@ -861,17 +865,19 @@ and is between 256 and 4096 characters. It is defined in the file ...@@ -861,17 +865,19 @@ and is between 256 and 4096 characters. It is defined in the file
See Documentation/ide/ide.txt. See Documentation/ide/ide.txt.
idle= [X86] idle= [X86]
Format: idle=poll or idle=mwait, idle=halt, idle=nomwait Format: idle=poll, idle=mwait, idle=halt, idle=nomwait
Poll forces a polling idle loop that can slightly improves the performance Poll forces a polling idle loop that can slightly
of waking up a idle CPU, but will use a lot of power and make the system improve the performance of waking up a idle CPU, but
run hot. Not recommended. will use a lot of power and make the system run hot.
idle=mwait. On systems which support MONITOR/MWAIT but the kernel chose Not recommended.
to not use it because it doesn't save as much power as a normal idle idle=mwait: On systems which support MONITOR/MWAIT but
loop use the MONITOR/MWAIT idle loop anyways. Performance should be the same the kernel chose to not use it because it doesn't save
as idle=poll. as much power as a normal idle loop, use the
idle=halt. Halt is forced to be used for CPU idle. MONITOR/MWAIT idle loop anyways. Performance should be
the same as idle=poll.
idle=halt: Halt is forced to be used for CPU idle.
In such case C2/C3 won't be used again. In such case C2/C3 won't be used again.
idle=nomwait. Disable mwait for CPU C-states idle=nomwait: Disable mwait for CPU C-states
ide-pci-generic.all-generic-ide [HW] (E)IDE subsystem ide-pci-generic.all-generic-ide [HW] (E)IDE subsystem
Claim all unknown PCI IDE storage controllers. Claim all unknown PCI IDE storage controllers.
...@@ -1053,8 +1059,8 @@ and is between 256 and 4096 characters. It is defined in the file ...@@ -1053,8 +1059,8 @@ and is between 256 and 4096 characters. It is defined in the file
lapic [X86-32,APIC] Enable the local APIC even if BIOS lapic [X86-32,APIC] Enable the local APIC even if BIOS
disabled it. disabled it.
lapic_timer_c2_ok [X86-32,x86-64,APIC] trust the local apic timer in lapic_timer_c2_ok [X86-32,x86-64,APIC] trust the local apic timer
C2 power state. in C2 power state.
libata.dma= [LIBATA] DMA control libata.dma= [LIBATA] DMA control
libata.dma=0 Disable all PATA and SATA DMA libata.dma=0 Disable all PATA and SATA DMA
...@@ -2242,7 +2248,8 @@ and is between 256 and 4096 characters. It is defined in the file ...@@ -2242,7 +2248,8 @@ and is between 256 and 4096 characters. It is defined in the file
thermal.psv= [HW,ACPI] thermal.psv= [HW,ACPI]
-1: disable all passive trip points -1: disable all passive trip points
<degrees C>: override all passive trip points to this value <degrees C>: override all passive trip points to this
value
thermal.tzp= [HW,ACPI] thermal.tzp= [HW,ACPI]
Specify global default ACPI thermal zone polling rate Specify global default ACPI thermal zone polling rate
......
...@@ -159,6 +159,8 @@ static int __init acpi_sleep_setup(char *str) ...@@ -159,6 +159,8 @@ static int __init acpi_sleep_setup(char *str)
#endif #endif
if (strncmp(str, "old_ordering", 12) == 0) if (strncmp(str, "old_ordering", 12) == 0)
acpi_old_suspend_ordering(); acpi_old_suspend_ordering();
if (strncmp(str, "s4_nonvs", 8) == 0)
acpi_s4_no_nvs();
str = strchr(str, ','); str = strchr(str, ',');
if (str != NULL) if (str != NULL)
str += strspn(str, ", \t"); str += strspn(str, ", \t");
......
...@@ -665,6 +665,27 @@ void __init e820_mark_nosave_regions(unsigned long limit_pfn) ...@@ -665,6 +665,27 @@ void __init e820_mark_nosave_regions(unsigned long limit_pfn)
} }
#endif #endif
#ifdef CONFIG_HIBERNATION
/**
* Mark ACPI NVS memory region, so that we can save/restore it during
* hibernation and the subsequent resume.
*/
static int __init e820_mark_nvs_memory(void)
{
int i;
for (i = 0; i < e820.nr_map; i++) {
struct e820entry *ei = &e820.map[i];
if (ei->type == E820_NVS)
hibernate_nvs_register(ei->addr, ei->size);
}
return 0;
}
core_initcall(e820_mark_nvs_memory);
#endif
/* /*
* Early reserved memory areas. * Early reserved memory areas.
*/ */
......
...@@ -101,6 +101,19 @@ void __init acpi_old_suspend_ordering(void) ...@@ -101,6 +101,19 @@ void __init acpi_old_suspend_ordering(void)
* cases. * cases.
*/ */
static bool set_sci_en_on_resume; static bool set_sci_en_on_resume;
/*
* The ACPI specification wants us to save NVS memory regions during hibernation
* and to restore them during the subsequent resume. However, it is not certain
* if this mechanism is going to work on all machines, so we allow the user to
* disable this mechanism using the 'acpi_sleep=s4_nonvs' kernel command line
* option.
*/
static bool s4_no_nvs;
void __init acpi_s4_no_nvs(void)
{
s4_no_nvs = true;
}
/** /**
* acpi_pm_disable_gpes - Disable the GPEs. * acpi_pm_disable_gpes - Disable the GPEs.
...@@ -394,9 +407,25 @@ void __init acpi_no_s4_hw_signature(void) ...@@ -394,9 +407,25 @@ void __init acpi_no_s4_hw_signature(void)
static int acpi_hibernation_begin(void) static int acpi_hibernation_begin(void)
{ {
acpi_target_sleep_state = ACPI_STATE_S4; int error;
acpi_sleep_tts_switch(acpi_target_sleep_state);
return 0; error = s4_no_nvs ? 0 : hibernate_nvs_alloc();
if (!error) {
acpi_target_sleep_state = ACPI_STATE_S4;
acpi_sleep_tts_switch(acpi_target_sleep_state);
}
return error;
}
static int acpi_hibernation_pre_snapshot(void)
{
int error = acpi_pm_prepare();
if (!error)
hibernate_nvs_save();
return error;
} }
static int acpi_hibernation_enter(void) static int acpi_hibernation_enter(void)
...@@ -417,6 +446,12 @@ static int acpi_hibernation_enter(void) ...@@ -417,6 +446,12 @@ static int acpi_hibernation_enter(void)
return ACPI_SUCCESS(status) ? 0 : -EFAULT; return ACPI_SUCCESS(status) ? 0 : -EFAULT;
} }
static void acpi_hibernation_finish(void)
{
hibernate_nvs_free();
acpi_pm_finish();
}
static void acpi_hibernation_leave(void) static void acpi_hibernation_leave(void)
{ {
/* /*
...@@ -432,6 +467,8 @@ static void acpi_hibernation_leave(void) ...@@ -432,6 +467,8 @@ static void acpi_hibernation_leave(void)
"cannot resume!\n"); "cannot resume!\n");
panic("ACPI S4 hardware signature mismatch"); panic("ACPI S4 hardware signature mismatch");
} }
/* Restore the NVS memory area */
hibernate_nvs_restore();
} }
static void acpi_pm_enable_gpes(void) static void acpi_pm_enable_gpes(void)
...@@ -442,8 +479,8 @@ static void acpi_pm_enable_gpes(void) ...@@ -442,8 +479,8 @@ static void acpi_pm_enable_gpes(void)
static struct platform_hibernation_ops acpi_hibernation_ops = { static struct platform_hibernation_ops acpi_hibernation_ops = {
.begin = acpi_hibernation_begin, .begin = acpi_hibernation_begin,
.end = acpi_pm_end, .end = acpi_pm_end,
.pre_snapshot = acpi_pm_prepare, .pre_snapshot = acpi_hibernation_pre_snapshot,
.finish = acpi_pm_finish, .finish = acpi_hibernation_finish,
.prepare = acpi_pm_prepare, .prepare = acpi_pm_prepare,
.enter = acpi_hibernation_enter, .enter = acpi_hibernation_enter,
.leave = acpi_hibernation_leave, .leave = acpi_hibernation_leave,
...@@ -469,8 +506,22 @@ static int acpi_hibernation_begin_old(void) ...@@ -469,8 +506,22 @@ static int acpi_hibernation_begin_old(void)
error = acpi_sleep_prepare(ACPI_STATE_S4); error = acpi_sleep_prepare(ACPI_STATE_S4);
if (!error) {
if (!s4_no_nvs)
error = hibernate_nvs_alloc();
if (!error)
acpi_target_sleep_state = ACPI_STATE_S4;
}
return error;
}
static int acpi_hibernation_pre_snapshot_old(void)
{
int error = acpi_pm_disable_gpes();
if (!error) if (!error)
acpi_target_sleep_state = ACPI_STATE_S4; hibernate_nvs_save();
return error; return error;
} }
...@@ -481,8 +532,8 @@ static int acpi_hibernation_begin_old(void) ...@@ -481,8 +532,8 @@ static int acpi_hibernation_begin_old(void)
static struct platform_hibernation_ops acpi_hibernation_ops_old = { static struct platform_hibernation_ops acpi_hibernation_ops_old = {
.begin = acpi_hibernation_begin_old, .begin = acpi_hibernation_begin_old,
.end = acpi_pm_end, .end = acpi_pm_end,
.pre_snapshot = acpi_pm_disable_gpes, .pre_snapshot = acpi_hibernation_pre_snapshot_old,
.finish = acpi_pm_finish, .finish = acpi_hibernation_finish,
.prepare = acpi_pm_disable_gpes, .prepare = acpi_pm_disable_gpes,
.enter = acpi_hibernation_enter, .enter = acpi_hibernation_enter,
.leave = acpi_hibernation_leave, .leave = acpi_hibernation_leave,
......
...@@ -270,6 +270,7 @@ int acpi_check_mem_region(resource_size_t start, resource_size_t n, ...@@ -270,6 +270,7 @@ int acpi_check_mem_region(resource_size_t start, resource_size_t n,
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
void __init acpi_no_s4_hw_signature(void); void __init acpi_no_s4_hw_signature(void);
void __init acpi_old_suspend_ordering(void); void __init acpi_old_suspend_ordering(void);
void __init acpi_s4_no_nvs(void);
#endif /* CONFIG_PM_SLEEP */ #endif /* CONFIG_PM_SLEEP */
#else /* CONFIG_ACPI */ #else /* CONFIG_ACPI */
......
...@@ -232,6 +232,11 @@ extern unsigned long get_safe_page(gfp_t gfp_mask); ...@@ -232,6 +232,11 @@ extern unsigned long get_safe_page(gfp_t gfp_mask);
extern void hibernation_set_ops(struct platform_hibernation_ops *ops); extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
extern int hibernate(void); extern int hibernate(void);
extern int hibernate_nvs_register(unsigned long start, unsigned long size);
extern int hibernate_nvs_alloc(void);
extern void hibernate_nvs_free(void);
extern void hibernate_nvs_save(void);
extern void hibernate_nvs_restore(void);
#else /* CONFIG_HIBERNATION */ #else /* CONFIG_HIBERNATION */
static inline int swsusp_page_is_forbidden(struct page *p) { return 0; } static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
static inline void swsusp_set_page_free(struct page *p) {} static inline void swsusp_set_page_free(struct page *p) {}
...@@ -239,6 +244,14 @@ static inline void swsusp_unset_page_free(struct page *p) {} ...@@ -239,6 +244,14 @@ static inline void swsusp_unset_page_free(struct page *p) {}
static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {} static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
static inline int hibernate(void) { return -ENOSYS; } static inline int hibernate(void) { return -ENOSYS; }
static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
{
return 0;
}
static inline int hibernate_nvs_alloc(void) { return 0; }
static inline void hibernate_nvs_free(void) {}
static inline void hibernate_nvs_save(void) {}
static inline void hibernate_nvs_restore(void) {}
#endif /* CONFIG_HIBERNATION */ #endif /* CONFIG_HIBERNATION */
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
......
...@@ -259,12 +259,12 @@ int hibernation_snapshot(int platform_mode) ...@@ -259,12 +259,12 @@ int hibernation_snapshot(int platform_mode)
{ {
int error, ftrace_save; int error, ftrace_save;
/* Free memory before shutting down devices. */ error = platform_begin(platform_mode);
error = swsusp_shrink_memory();
if (error) if (error)
return error; return error;
error = platform_begin(platform_mode); /* Free memory before shutting down devices. */
error = swsusp_shrink_memory();
if (error) if (error)
goto Close; goto Close;
......
This diff is collapsed.
...@@ -262,3 +262,125 @@ int swsusp_shrink_memory(void) ...@@ -262,3 +262,125 @@ int swsusp_shrink_memory(void)
return 0; return 0;
} }
/*
* Platforms, like ACPI, may want us to save some memory used by them during
* hibernation and to restore the contents of this memory during the subsequent
* resume. The code below implements a mechanism allowing us to do that.
*/
struct nvs_page {
unsigned long phys_start;
unsigned int size;
void *kaddr;
void *data;
struct list_head node;
};
static LIST_HEAD(nvs_list);
/**
* hibernate_nvs_register - register platform NVS memory region to save
* @start - physical address of the region
* @size - size of the region
*
* The NVS region need not be page-aligned (both ends) and we arrange
* things so that the data from page-aligned addresses in this region will
* be copied into separate RAM pages.
*/
int hibernate_nvs_register(unsigned long start, unsigned long size)
{
struct nvs_page *entry, *next;
while (size > 0) {
unsigned int nr_bytes;
entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
if (!entry)
goto Error;
list_add_tail(&entry->node, &nvs_list);
entry->phys_start = start;
nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
entry->size = (size < nr_bytes) ? size : nr_bytes;
start += entry->size;
size -= entry->size;
}
return 0;
Error:
list_for_each_entry_safe(entry, next, &nvs_list, node) {
list_del(&entry->node);
kfree(entry);
}
return -ENOMEM;
}
/**
* hibernate_nvs_free - free data pages allocated for saving NVS regions
*/
void hibernate_nvs_free(void)
{
struct nvs_page *entry;
list_for_each_entry(entry, &nvs_list, node)
if (entry->data) {
free_page((unsigned long)entry->data);
entry->data = NULL;
if (entry->kaddr) {
iounmap(entry->kaddr);
entry->kaddr = NULL;
}
}
}
/**
* hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
*/
int hibernate_nvs_alloc(void)
{
struct nvs_page *entry;
list_for_each_entry(entry, &nvs_list, node) {
entry->data = (void *)__get_free_page(GFP_KERNEL);
if (!entry->data) {
hibernate_nvs_free();
return -ENOMEM;
}
}
return 0;
}
/**
* hibernate_nvs_save - save NVS memory regions
*/
void hibernate_nvs_save(void)
{
struct nvs_page *entry;
printk(KERN_INFO "PM: Saving platform NVS memory\n");
list_for_each_entry(entry, &nvs_list, node)
if (entry->data) {
entry->kaddr = ioremap(entry->phys_start, entry->size);
memcpy(entry->data, entry->kaddr, entry->size);
}
}
/**
* hibernate_nvs_restore - restore NVS memory regions
*
* This function is going to be called with interrupts disabled, so it
* cannot iounmap the virtual addresses used to access the NVS region.
*/
void hibernate_nvs_restore(void)
{
struct nvs_page *entry;
printk(KERN_INFO "PM: Restoring platform NVS memory\n");
list_for_each_entry(entry, &nvs_list, node)
if (entry->data)
memcpy(entry->kaddr, entry->data, entry->size);
}
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