aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/suspend.h18
-rw-r--r--kernel/power/Kconfig4
-rw-r--r--kernel/power/Makefile1
-rw-r--r--kernel/power/hibernate_nvs.c135
-rw-r--r--kernel/power/swsusp.c122
5 files changed, 151 insertions, 129 deletions
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index 795032edfc46..cd15df6c63cd 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -245,11 +245,6 @@ extern unsigned long get_safe_page(gfp_t gfp_mask);
245 245
246extern void hibernation_set_ops(struct platform_hibernation_ops *ops); 246extern void hibernation_set_ops(struct platform_hibernation_ops *ops);
247extern int hibernate(void); 247extern int hibernate(void);
248extern int hibernate_nvs_register(unsigned long start, unsigned long size);
249extern int hibernate_nvs_alloc(void);
250extern void hibernate_nvs_free(void);
251extern void hibernate_nvs_save(void);
252extern void hibernate_nvs_restore(void);
253extern bool system_entering_hibernation(void); 248extern bool system_entering_hibernation(void);
254#else /* CONFIG_HIBERNATION */ 249#else /* CONFIG_HIBERNATION */
255static inline int swsusp_page_is_forbidden(struct page *p) { return 0; } 250static inline int swsusp_page_is_forbidden(struct page *p) { return 0; }
@@ -258,6 +253,16 @@ static inline void swsusp_unset_page_free(struct page *p) {}
258 253
259static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {} 254static inline void hibernation_set_ops(struct platform_hibernation_ops *ops) {}
260static inline int hibernate(void) { return -ENOSYS; } 255static inline int hibernate(void) { return -ENOSYS; }
256static inline bool system_entering_hibernation(void) { return false; }
257#endif /* CONFIG_HIBERNATION */
258
259#ifdef CONFIG_HIBERNATION_NVS
260extern int hibernate_nvs_register(unsigned long start, unsigned long size);
261extern int hibernate_nvs_alloc(void);
262extern void hibernate_nvs_free(void);
263extern void hibernate_nvs_save(void);
264extern void hibernate_nvs_restore(void);
265#else /* CONFIG_HIBERNATION_NVS */
261static inline int hibernate_nvs_register(unsigned long a, unsigned long b) 266static inline int hibernate_nvs_register(unsigned long a, unsigned long b)
262{ 267{
263 return 0; 268 return 0;
@@ -266,8 +271,7 @@ static inline int hibernate_nvs_alloc(void) { return 0; }
266static inline void hibernate_nvs_free(void) {} 271static inline void hibernate_nvs_free(void) {}
267static inline void hibernate_nvs_save(void) {} 272static inline void hibernate_nvs_save(void) {}
268static inline void hibernate_nvs_restore(void) {} 273static inline void hibernate_nvs_restore(void) {}
269static inline bool system_entering_hibernation(void) { return false; } 274#endif /* CONFIG_HIBERNATION_NVS */
270#endif /* CONFIG_HIBERNATION */
271 275
272#ifdef CONFIG_PM_SLEEP 276#ifdef CONFIG_PM_SLEEP
273void save_processor_state(void); 277void save_processor_state(void);
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index 23bd4daeb96b..72067cbdb37f 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -116,9 +116,13 @@ config SUSPEND_FREEZER
116 116
117 Turning OFF this setting is NOT recommended! If in doubt, say Y. 117 Turning OFF this setting is NOT recommended! If in doubt, say Y.
118 118
119config HIBERNATION_NVS
120 bool
121
119config HIBERNATION 122config HIBERNATION
120 bool "Hibernation (aka 'suspend to disk')" 123 bool "Hibernation (aka 'suspend to disk')"
121 depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE 124 depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE
125 select HIBERNATION_NVS if HAS_IOMEM
122 ---help--- 126 ---help---
123 Enable the suspend to disk (STD) functionality, which is usually 127 Enable the suspend to disk (STD) functionality, which is usually
124 called "hibernation" in user interfaces. STD checkpoints the 128 called "hibernation" in user interfaces. STD checkpoints the
diff --git a/kernel/power/Makefile b/kernel/power/Makefile
index eadb17fc8f5e..c3b81c30e5d5 100644
--- a/kernel/power/Makefile
+++ b/kernel/power/Makefile
@@ -9,5 +9,6 @@ obj-$(CONFIG_FREEZER) += process.o
9obj-$(CONFIG_SUSPEND) += suspend.o 9obj-$(CONFIG_SUSPEND) += suspend.o
10obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o 10obj-$(CONFIG_PM_TEST_SUSPEND) += suspend_test.o
11obj-$(CONFIG_HIBERNATION) += swsusp.o hibernate.o snapshot.o swap.o user.o 11obj-$(CONFIG_HIBERNATION) += swsusp.o hibernate.o snapshot.o swap.o user.o
12obj-$(CONFIG_HIBERNATION_NVS) += hibernate_nvs.o
12 13
13obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o 14obj-$(CONFIG_MAGIC_SYSRQ) += poweroff.o
diff --git a/kernel/power/hibernate_nvs.c b/kernel/power/hibernate_nvs.c
new file mode 100644
index 000000000000..39ac698ef836
--- /dev/null
+++ b/kernel/power/hibernate_nvs.c
@@ -0,0 +1,135 @@
1/*
2 * linux/kernel/power/hibernate_nvs.c - Routines for handling NVS memory
3 *
4 * Copyright (C) 2008,2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
5 *
6 * This file is released under the GPLv2.
7 */
8
9#include <linux/io.h>
10#include <linux/kernel.h>
11#include <linux/list.h>
12#include <linux/mm.h>
13#include <linux/suspend.h>
14
15/*
16 * Platforms, like ACPI, may want us to save some memory used by them during
17 * hibernation and to restore the contents of this memory during the subsequent
18 * resume. The code below implements a mechanism allowing us to do that.
19 */
20
21struct nvs_page {
22 unsigned long phys_start;
23 unsigned int size;
24 void *kaddr;
25 void *data;
26 struct list_head node;
27};
28
29static LIST_HEAD(nvs_list);
30
31/**
32 * hibernate_nvs_register - register platform NVS memory region to save
33 * @start - physical address of the region
34 * @size - size of the region
35 *
36 * The NVS region need not be page-aligned (both ends) and we arrange
37 * things so that the data from page-aligned addresses in this region will
38 * be copied into separate RAM pages.
39 */
40int hibernate_nvs_register(unsigned long start, unsigned long size)
41{
42 struct nvs_page *entry, *next;
43
44 while (size > 0) {
45 unsigned int nr_bytes;
46
47 entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
48 if (!entry)
49 goto Error;
50
51 list_add_tail(&entry->node, &nvs_list);
52 entry->phys_start = start;
53 nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
54 entry->size = (size < nr_bytes) ? size : nr_bytes;
55
56 start += entry->size;
57 size -= entry->size;
58 }
59 return 0;
60
61 Error:
62 list_for_each_entry_safe(entry, next, &nvs_list, node) {
63 list_del(&entry->node);
64 kfree(entry);
65 }
66 return -ENOMEM;
67}
68
69/**
70 * hibernate_nvs_free - free data pages allocated for saving NVS regions
71 */
72void hibernate_nvs_free(void)
73{
74 struct nvs_page *entry;
75
76 list_for_each_entry(entry, &nvs_list, node)
77 if (entry->data) {
78 free_page((unsigned long)entry->data);
79 entry->data = NULL;
80 if (entry->kaddr) {
81 iounmap(entry->kaddr);
82 entry->kaddr = NULL;
83 }
84 }
85}
86
87/**
88 * hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
89 */
90int hibernate_nvs_alloc(void)
91{
92 struct nvs_page *entry;
93
94 list_for_each_entry(entry, &nvs_list, node) {
95 entry->data = (void *)__get_free_page(GFP_KERNEL);
96 if (!entry->data) {
97 hibernate_nvs_free();
98 return -ENOMEM;
99 }
100 }
101 return 0;
102}
103
104/**
105 * hibernate_nvs_save - save NVS memory regions
106 */
107void hibernate_nvs_save(void)
108{
109 struct nvs_page *entry;
110
111 printk(KERN_INFO "PM: Saving platform NVS memory\n");
112
113 list_for_each_entry(entry, &nvs_list, node)
114 if (entry->data) {
115 entry->kaddr = ioremap(entry->phys_start, entry->size);
116 memcpy(entry->data, entry->kaddr, entry->size);
117 }
118}
119
120/**
121 * hibernate_nvs_restore - restore NVS memory regions
122 *
123 * This function is going to be called with interrupts disabled, so it
124 * cannot iounmap the virtual addresses used to access the NVS region.
125 */
126void hibernate_nvs_restore(void)
127{
128 struct nvs_page *entry;
129
130 printk(KERN_INFO "PM: Restoring platform NVS memory\n");
131
132 list_for_each_entry(entry, &nvs_list, node)
133 if (entry->data)
134 memcpy(entry->kaddr, entry->data, entry->size);
135}
diff --git a/kernel/power/swsusp.c b/kernel/power/swsusp.c
index 87b901cb3927..6a07f4dbf2f8 100644
--- a/kernel/power/swsusp.c
+++ b/kernel/power/swsusp.c
@@ -186,125 +186,3 @@ void swsusp_show_speed(struct timeval *start, struct timeval *stop,
186 centisecs / 100, centisecs % 100, 186 centisecs / 100, centisecs % 100,
187 kps / 1000, (kps % 1000) / 10); 187 kps / 1000, (kps % 1000) / 10);
188} 188}
189
190/*
191 * Platforms, like ACPI, may want us to save some memory used by them during
192 * hibernation and to restore the contents of this memory during the subsequent
193 * resume. The code below implements a mechanism allowing us to do that.
194 */
195
196struct nvs_page {
197 unsigned long phys_start;
198 unsigned int size;
199 void *kaddr;
200 void *data;
201 struct list_head node;
202};
203
204static LIST_HEAD(nvs_list);
205
206/**
207 * hibernate_nvs_register - register platform NVS memory region to save
208 * @start - physical address of the region
209 * @size - size of the region
210 *
211 * The NVS region need not be page-aligned (both ends) and we arrange
212 * things so that the data from page-aligned addresses in this region will
213 * be copied into separate RAM pages.
214 */
215int hibernate_nvs_register(unsigned long start, unsigned long size)
216{
217 struct nvs_page *entry, *next;
218
219 while (size > 0) {
220 unsigned int nr_bytes;
221
222 entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
223 if (!entry)
224 goto Error;
225
226 list_add_tail(&entry->node, &nvs_list);
227 entry->phys_start = start;
228 nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
229 entry->size = (size < nr_bytes) ? size : nr_bytes;
230
231 start += entry->size;
232 size -= entry->size;
233 }
234 return 0;
235
236 Error:
237 list_for_each_entry_safe(entry, next, &nvs_list, node) {
238 list_del(&entry->node);
239 kfree(entry);
240 }
241 return -ENOMEM;
242}
243
244/**
245 * hibernate_nvs_free - free data pages allocated for saving NVS regions
246 */
247void hibernate_nvs_free(void)
248{
249 struct nvs_page *entry;
250
251 list_for_each_entry(entry, &nvs_list, node)
252 if (entry->data) {
253 free_page((unsigned long)entry->data);
254 entry->data = NULL;
255 if (entry->kaddr) {
256 iounmap(entry->kaddr);
257 entry->kaddr = NULL;
258 }
259 }
260}
261
262/**
263 * hibernate_nvs_alloc - allocate memory necessary for saving NVS regions
264 */
265int hibernate_nvs_alloc(void)
266{
267 struct nvs_page *entry;
268
269 list_for_each_entry(entry, &nvs_list, node) {
270 entry->data = (void *)__get_free_page(GFP_KERNEL);
271 if (!entry->data) {
272 hibernate_nvs_free();
273 return -ENOMEM;
274 }
275 }
276 return 0;
277}
278
279/**
280 * hibernate_nvs_save - save NVS memory regions
281 */
282void hibernate_nvs_save(void)
283{
284 struct nvs_page *entry;
285
286 printk(KERN_INFO "PM: Saving platform NVS memory\n");
287
288 list_for_each_entry(entry, &nvs_list, node)
289 if (entry->data) {
290 entry->kaddr = ioremap(entry->phys_start, entry->size);
291 memcpy(entry->data, entry->kaddr, entry->size);
292 }
293}
294
295/**
296 * hibernate_nvs_restore - restore NVS memory regions
297 *
298 * This function is going to be called with interrupts disabled, so it
299 * cannot iounmap the virtual addresses used to access the NVS region.
300 */
301void hibernate_nvs_restore(void)
302{
303 struct nvs_page *entry;
304
305 printk(KERN_INFO "PM: Restoring platform NVS memory\n");
306
307 list_for_each_entry(entry, &nvs_list, node)
308 if (entry->data)
309 memcpy(entry->kaddr, entry->data, entry->size);
310}