Commit 976513dbfc1547c7b1822566923058655f0c32fd
Committed by
Len Brown
1 parent
26fcaf60fe
Exists in
master
and in
4 other branches
PM / ACPI: Move NVS saving and restoring code to drivers/acpi
The saving of the ACPI NVS area during hibernation and suspend and restoring it during the subsequent resume is entirely specific to ACPI, so move it to drivers/acpi and drop the CONFIG_SUSPEND_NVS configuration option which is redundant. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Len Brown <len.brown@intel.com>
Showing 9 changed files with 161 additions and 166 deletions Side-by-side Diff
arch/x86/kernel/e820.c
drivers/acpi/Makefile
drivers/acpi/internal.h
... | ... | @@ -82,8 +82,16 @@ |
82 | 82 | |
83 | 83 | #ifdef CONFIG_ACPI_SLEEP |
84 | 84 | int acpi_sleep_proc_init(void); |
85 | +int suspend_nvs_alloc(void); | |
86 | +void suspend_nvs_free(void); | |
87 | +int suspend_nvs_save(void); | |
88 | +void suspend_nvs_restore(void); | |
85 | 89 | #else |
86 | 90 | static inline int acpi_sleep_proc_init(void) { return 0; } |
91 | +static inline int suspend_nvs_alloc(void) { return 0; } | |
92 | +static inline void suspend_nvs_free(void) {} | |
93 | +static inline int suspend_nvs_save(void) {} | |
94 | +static inline void suspend_nvs_restore(void) {} | |
87 | 95 | #endif |
88 | 96 | |
89 | 97 | #endif /* _ACPI_INTERNAL_H_ */ |
drivers/acpi/nvs.c
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/slab.h> | |
14 | +#include <linux/suspend.h> | |
15 | + | |
16 | +/* | |
17 | + * Platforms, like ACPI, may want us to save some memory used by them during | |
18 | + * suspend and to restore the contents of this memory during the subsequent | |
19 | + * resume. The code below implements a mechanism allowing us to do that. | |
20 | + */ | |
21 | + | |
22 | +struct nvs_page { | |
23 | + unsigned long phys_start; | |
24 | + unsigned int size; | |
25 | + void *kaddr; | |
26 | + void *data; | |
27 | + struct list_head node; | |
28 | +}; | |
29 | + | |
30 | +static LIST_HEAD(nvs_list); | |
31 | + | |
32 | +/** | |
33 | + * suspend_nvs_register - register platform NVS memory region to save | |
34 | + * @start - physical address of the region | |
35 | + * @size - size of the region | |
36 | + * | |
37 | + * The NVS region need not be page-aligned (both ends) and we arrange | |
38 | + * things so that the data from page-aligned addresses in this region will | |
39 | + * be copied into separate RAM pages. | |
40 | + */ | |
41 | +int suspend_nvs_register(unsigned long start, unsigned long size) | |
42 | +{ | |
43 | + struct nvs_page *entry, *next; | |
44 | + | |
45 | + while (size > 0) { | |
46 | + unsigned int nr_bytes; | |
47 | + | |
48 | + entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL); | |
49 | + if (!entry) | |
50 | + goto Error; | |
51 | + | |
52 | + list_add_tail(&entry->node, &nvs_list); | |
53 | + entry->phys_start = start; | |
54 | + nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK); | |
55 | + entry->size = (size < nr_bytes) ? size : nr_bytes; | |
56 | + | |
57 | + start += entry->size; | |
58 | + size -= entry->size; | |
59 | + } | |
60 | + return 0; | |
61 | + | |
62 | + Error: | |
63 | + list_for_each_entry_safe(entry, next, &nvs_list, node) { | |
64 | + list_del(&entry->node); | |
65 | + kfree(entry); | |
66 | + } | |
67 | + return -ENOMEM; | |
68 | +} | |
69 | + | |
70 | +/** | |
71 | + * suspend_nvs_free - free data pages allocated for saving NVS regions | |
72 | + */ | |
73 | +void suspend_nvs_free(void) | |
74 | +{ | |
75 | + struct nvs_page *entry; | |
76 | + | |
77 | + list_for_each_entry(entry, &nvs_list, node) | |
78 | + if (entry->data) { | |
79 | + free_page((unsigned long)entry->data); | |
80 | + entry->data = NULL; | |
81 | + if (entry->kaddr) { | |
82 | + iounmap(entry->kaddr); | |
83 | + entry->kaddr = NULL; | |
84 | + } | |
85 | + } | |
86 | +} | |
87 | + | |
88 | +/** | |
89 | + * suspend_nvs_alloc - allocate memory necessary for saving NVS regions | |
90 | + */ | |
91 | +int suspend_nvs_alloc(void) | |
92 | +{ | |
93 | + struct nvs_page *entry; | |
94 | + | |
95 | + list_for_each_entry(entry, &nvs_list, node) { | |
96 | + entry->data = (void *)__get_free_page(GFP_KERNEL); | |
97 | + if (!entry->data) { | |
98 | + suspend_nvs_free(); | |
99 | + return -ENOMEM; | |
100 | + } | |
101 | + } | |
102 | + return 0; | |
103 | +} | |
104 | + | |
105 | +/** | |
106 | + * suspend_nvs_save - save NVS memory regions | |
107 | + */ | |
108 | +int suspend_nvs_save(void) | |
109 | +{ | |
110 | + struct nvs_page *entry; | |
111 | + | |
112 | + printk(KERN_INFO "PM: Saving platform NVS memory\n"); | |
113 | + | |
114 | + list_for_each_entry(entry, &nvs_list, node) | |
115 | + if (entry->data) { | |
116 | + entry->kaddr = ioremap(entry->phys_start, entry->size); | |
117 | + if (!entry->kaddr) { | |
118 | + suspend_nvs_free(); | |
119 | + return -ENOMEM; | |
120 | + } | |
121 | + memcpy(entry->data, entry->kaddr, entry->size); | |
122 | + } | |
123 | + | |
124 | + return 0; | |
125 | +} | |
126 | + | |
127 | +/** | |
128 | + * suspend_nvs_restore - restore NVS memory regions | |
129 | + * | |
130 | + * This function is going to be called with interrupts disabled, so it | |
131 | + * cannot iounmap the virtual addresses used to access the NVS region. | |
132 | + */ | |
133 | +void suspend_nvs_restore(void) | |
134 | +{ | |
135 | + struct nvs_page *entry; | |
136 | + | |
137 | + printk(KERN_INFO "PM: Restoring platform NVS memory\n"); | |
138 | + | |
139 | + list_for_each_entry(entry, &nvs_list, node) | |
140 | + if (entry->data) | |
141 | + memcpy(entry->kaddr, entry->data, entry->size); | |
142 | +} |
include/linux/acpi.h
... | ... | @@ -254,6 +254,15 @@ |
254 | 254 | void __init acpi_nvs_nosave(void); |
255 | 255 | #endif /* CONFIG_PM_SLEEP */ |
256 | 256 | |
257 | +#ifdef CONFIG_ACPI_SLEEP | |
258 | +int suspend_nvs_register(unsigned long start, unsigned long size); | |
259 | +#else | |
260 | +static inline int suspend_nvs_register(unsigned long a, unsigned long b) | |
261 | +{ | |
262 | + return 0; | |
263 | +} | |
264 | +#endif | |
265 | + | |
257 | 266 | struct acpi_osc_context { |
258 | 267 | char *uuid_str; /* uuid string */ |
259 | 268 | int rev; |
include/linux/suspend.h
... | ... | @@ -258,23 +258,6 @@ |
258 | 258 | static inline bool system_entering_hibernation(void) { return false; } |
259 | 259 | #endif /* CONFIG_HIBERNATION */ |
260 | 260 | |
261 | -#ifdef CONFIG_SUSPEND_NVS | |
262 | -extern int suspend_nvs_register(unsigned long start, unsigned long size); | |
263 | -extern int suspend_nvs_alloc(void); | |
264 | -extern void suspend_nvs_free(void); | |
265 | -extern int suspend_nvs_save(void); | |
266 | -extern void suspend_nvs_restore(void); | |
267 | -#else /* CONFIG_SUSPEND_NVS */ | |
268 | -static inline int suspend_nvs_register(unsigned long a, unsigned long b) | |
269 | -{ | |
270 | - return 0; | |
271 | -} | |
272 | -static inline int suspend_nvs_alloc(void) { return 0; } | |
273 | -static inline void suspend_nvs_free(void) {} | |
274 | -static inline int suspend_nvs_save(void) {} | |
275 | -static inline void suspend_nvs_restore(void) {} | |
276 | -#endif /* CONFIG_SUSPEND_NVS */ | |
277 | - | |
278 | 261 | #ifdef CONFIG_PM_SLEEP |
279 | 262 | void save_processor_state(void); |
280 | 263 | void restore_processor_state(void); |
kernel/power/Kconfig
... | ... | @@ -100,13 +100,9 @@ |
100 | 100 | depends on PM_ADVANCED_DEBUG |
101 | 101 | default n |
102 | 102 | |
103 | -config SUSPEND_NVS | |
104 | - bool | |
105 | - | |
106 | 103 | config SUSPEND |
107 | 104 | bool "Suspend to RAM and standby" |
108 | 105 | depends on PM && ARCH_SUSPEND_POSSIBLE |
109 | - select SUSPEND_NVS if HAS_IOMEM | |
110 | 106 | default y |
111 | 107 | ---help--- |
112 | 108 | Allow the system to enter sleep states in which main memory is |
... | ... | @@ -140,7 +136,6 @@ |
140 | 136 | depends on PM && SWAP && ARCH_HIBERNATION_POSSIBLE |
141 | 137 | select LZO_COMPRESS |
142 | 138 | select LZO_DECOMPRESS |
143 | - select SUSPEND_NVS if HAS_IOMEM | |
144 | 139 | ---help--- |
145 | 140 | Enable the suspend to disk (STD) functionality, which is usually |
146 | 141 | called "hibernation" in user interfaces. STD checkpoints the |
kernel/power/Makefile
kernel/power/nvs.c
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/slab.h> | |
14 | -#include <linux/suspend.h> | |
15 | - | |
16 | -/* | |
17 | - * Platforms, like ACPI, may want us to save some memory used by them during | |
18 | - * suspend and to restore the contents of this memory during the subsequent | |
19 | - * resume. The code below implements a mechanism allowing us to do that. | |
20 | - */ | |
21 | - | |
22 | -struct nvs_page { | |
23 | - unsigned long phys_start; | |
24 | - unsigned int size; | |
25 | - void *kaddr; | |
26 | - void *data; | |
27 | - struct list_head node; | |
28 | -}; | |
29 | - | |
30 | -static LIST_HEAD(nvs_list); | |
31 | - | |
32 | -/** | |
33 | - * suspend_nvs_register - register platform NVS memory region to save | |
34 | - * @start - physical address of the region | |
35 | - * @size - size of the region | |
36 | - * | |
37 | - * The NVS region need not be page-aligned (both ends) and we arrange | |
38 | - * things so that the data from page-aligned addresses in this region will | |
39 | - * be copied into separate RAM pages. | |
40 | - */ | |
41 | -int suspend_nvs_register(unsigned long start, unsigned long size) | |
42 | -{ | |
43 | - struct nvs_page *entry, *next; | |
44 | - | |
45 | - while (size > 0) { | |
46 | - unsigned int nr_bytes; | |
47 | - | |
48 | - entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL); | |
49 | - if (!entry) | |
50 | - goto Error; | |
51 | - | |
52 | - list_add_tail(&entry->node, &nvs_list); | |
53 | - entry->phys_start = start; | |
54 | - nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK); | |
55 | - entry->size = (size < nr_bytes) ? size : nr_bytes; | |
56 | - | |
57 | - start += entry->size; | |
58 | - size -= entry->size; | |
59 | - } | |
60 | - return 0; | |
61 | - | |
62 | - Error: | |
63 | - list_for_each_entry_safe(entry, next, &nvs_list, node) { | |
64 | - list_del(&entry->node); | |
65 | - kfree(entry); | |
66 | - } | |
67 | - return -ENOMEM; | |
68 | -} | |
69 | - | |
70 | -/** | |
71 | - * suspend_nvs_free - free data pages allocated for saving NVS regions | |
72 | - */ | |
73 | -void suspend_nvs_free(void) | |
74 | -{ | |
75 | - struct nvs_page *entry; | |
76 | - | |
77 | - list_for_each_entry(entry, &nvs_list, node) | |
78 | - if (entry->data) { | |
79 | - free_page((unsigned long)entry->data); | |
80 | - entry->data = NULL; | |
81 | - if (entry->kaddr) { | |
82 | - iounmap(entry->kaddr); | |
83 | - entry->kaddr = NULL; | |
84 | - } | |
85 | - } | |
86 | -} | |
87 | - | |
88 | -/** | |
89 | - * suspend_nvs_alloc - allocate memory necessary for saving NVS regions | |
90 | - */ | |
91 | -int suspend_nvs_alloc(void) | |
92 | -{ | |
93 | - struct nvs_page *entry; | |
94 | - | |
95 | - list_for_each_entry(entry, &nvs_list, node) { | |
96 | - entry->data = (void *)__get_free_page(GFP_KERNEL); | |
97 | - if (!entry->data) { | |
98 | - suspend_nvs_free(); | |
99 | - return -ENOMEM; | |
100 | - } | |
101 | - } | |
102 | - return 0; | |
103 | -} | |
104 | - | |
105 | -/** | |
106 | - * suspend_nvs_save - save NVS memory regions | |
107 | - */ | |
108 | -int suspend_nvs_save(void) | |
109 | -{ | |
110 | - struct nvs_page *entry; | |
111 | - | |
112 | - printk(KERN_INFO "PM: Saving platform NVS memory\n"); | |
113 | - | |
114 | - list_for_each_entry(entry, &nvs_list, node) | |
115 | - if (entry->data) { | |
116 | - entry->kaddr = ioremap(entry->phys_start, entry->size); | |
117 | - if (!entry->kaddr) { | |
118 | - suspend_nvs_free(); | |
119 | - return -ENOMEM; | |
120 | - } | |
121 | - memcpy(entry->data, entry->kaddr, entry->size); | |
122 | - } | |
123 | - | |
124 | - return 0; | |
125 | -} | |
126 | - | |
127 | -/** | |
128 | - * suspend_nvs_restore - restore NVS memory regions | |
129 | - * | |
130 | - * This function is going to be called with interrupts disabled, so it | |
131 | - * cannot iounmap the virtual addresses used to access the NVS region. | |
132 | - */ | |
133 | -void suspend_nvs_restore(void) | |
134 | -{ | |
135 | - struct nvs_page *entry; | |
136 | - | |
137 | - printk(KERN_INFO "PM: Restoring platform NVS memory\n"); | |
138 | - | |
139 | - list_for_each_entry(entry, &nvs_list, node) | |
140 | - if (entry->data) | |
141 | - memcpy(entry->kaddr, entry->data, entry->size); | |
142 | -} |