Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 128089
b: refs/heads/master
c: 3f4b0ef
h: refs/heads/master
i:
  128087: 18345ba
v: v3
  • Loading branch information
Rafael J. Wysocki authored and Len Brown committed Dec 19, 2008
1 parent 41017bd commit b1580ee
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 9 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 3fe0313e6ec572e6bb3f9d247316a834336db4be
refs/heads/master: 3f4b0ef7f2899c91b1d6958779f084b44dd59d32
53 changes: 45 additions & 8 deletions trunk/drivers/acpi/sleep/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -394,9 +394,25 @@ void __init acpi_no_s4_hw_signature(void)

static int acpi_hibernation_begin(void)
{
acpi_target_sleep_state = ACPI_STATE_S4;
acpi_sleep_tts_switch(acpi_target_sleep_state);
return 0;
int error;

error = 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)
Expand All @@ -417,6 +433,12 @@ static int acpi_hibernation_enter(void)
return ACPI_SUCCESS(status) ? 0 : -EFAULT;
}

static void acpi_hibernation_finish(void)
{
hibernate_nvs_free();
acpi_pm_finish();
}

static void acpi_hibernation_leave(void)
{
/*
Expand All @@ -432,6 +454,8 @@ static void acpi_hibernation_leave(void)
"cannot resume!\n");
panic("ACPI S4 hardware signature mismatch");
}
/* Restore the NVS memory area */
hibernate_nvs_restore();
}

static void acpi_pm_enable_gpes(void)
Expand All @@ -442,8 +466,8 @@ static void acpi_pm_enable_gpes(void)
static struct platform_hibernation_ops acpi_hibernation_ops = {
.begin = acpi_hibernation_begin,
.end = acpi_pm_end,
.pre_snapshot = acpi_pm_prepare,
.finish = acpi_pm_finish,
.pre_snapshot = acpi_hibernation_pre_snapshot,
.finish = acpi_hibernation_finish,
.prepare = acpi_pm_prepare,
.enter = acpi_hibernation_enter,
.leave = acpi_hibernation_leave,
Expand All @@ -469,8 +493,21 @@ static int acpi_hibernation_begin_old(void)

error = acpi_sleep_prepare(ACPI_STATE_S4);

if (!error) {
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)
acpi_target_sleep_state = ACPI_STATE_S4;
hibernate_nvs_save();

return error;
}

Expand All @@ -481,8 +518,8 @@ static int acpi_hibernation_begin_old(void)
static struct platform_hibernation_ops acpi_hibernation_ops_old = {
.begin = acpi_hibernation_begin_old,
.end = acpi_pm_end,
.pre_snapshot = acpi_pm_disable_gpes,
.finish = acpi_pm_finish,
.pre_snapshot = acpi_hibernation_pre_snapshot_old,
.finish = acpi_hibernation_finish,
.prepare = acpi_pm_disable_gpes,
.enter = acpi_hibernation_enter,
.leave = acpi_hibernation_leave,
Expand Down
13 changes: 13 additions & 0 deletions trunk/include/linux/suspend.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,26 @@ extern unsigned long get_safe_page(gfp_t gfp_mask);

extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
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 */
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_unset_page_free(struct page *p) {}

static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
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 */

#ifdef CONFIG_PM_SLEEP
Expand Down
122 changes: 122 additions & 0 deletions trunk/kernel/power/swsusp.c
Original file line number Diff line number Diff line change
Expand Up @@ -262,3 +262,125 @@ int swsusp_shrink_memory(void)

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);
}

0 comments on commit b1580ee

Please sign in to comment.