diff options
-rw-r--r-- | Documentation/power/userland-swsusp.txt | 26 | ||||
-rw-r--r-- | drivers/acpi/sleep/main.c | 67 | ||||
-rw-r--r-- | drivers/acpi/sleep/proc.c | 2 | ||||
-rw-r--r-- | drivers/i2c/chips/tps65010.c | 2 | ||||
-rw-r--r-- | include/linux/pm.h | 31 | ||||
-rw-r--r-- | include/linux/suspend.h | 24 | ||||
-rw-r--r-- | kernel/power/disk.c | 195 | ||||
-rw-r--r-- | kernel/power/main.c | 42 | ||||
-rw-r--r-- | kernel/power/power.h | 7 | ||||
-rw-r--r-- | kernel/power/user.c | 13 | ||||
-rw-r--r-- | kernel/sys.c | 2 |
11 files changed, 226 insertions, 185 deletions
diff --git a/Documentation/power/userland-swsusp.txt b/Documentation/power/userland-swsusp.txt index 000556c932e9..e00c6cf09e85 100644 --- a/Documentation/power/userland-swsusp.txt +++ b/Documentation/power/userland-swsusp.txt | |||
@@ -93,21 +93,23 @@ SNAPSHOT_S2RAM - suspend to RAM; using this call causes the kernel to | |||
93 | to resume the system from RAM if there's enough battery power or restore | 93 | to resume the system from RAM if there's enough battery power or restore |
94 | its state on the basis of the saved suspend image otherwise) | 94 | its state on the basis of the saved suspend image otherwise) |
95 | 95 | ||
96 | SNAPSHOT_PMOPS - enable the usage of the pmops->prepare, pmops->enter and | 96 | SNAPSHOT_PMOPS - enable the usage of the hibernation_ops->prepare, |
97 | pmops->finish methods (the in-kernel swsusp knows these as the "platform | 97 | hibernate_ops->enter and hibernation_ops->finish methods (the in-kernel |
98 | method") which are needed on many machines to (among others) speed up | 98 | swsusp knows these as the "platform method") which are needed on many |
99 | the resume by letting the BIOS skip some steps or to let the system | 99 | machines to (among others) speed up the resume by letting the BIOS skip |
100 | recognise the correct state of the hardware after the resume (in | 100 | some steps or to let the system recognise the correct state of the |
101 | particular on many machines this ensures that unplugged AC | 101 | hardware after the resume (in particular on many machines this ensures |
102 | adapters get correctly detected and that kacpid does not run wild after | 102 | that unplugged AC adapters get correctly detected and that kacpid does |
103 | the resume). The last ioctl() argument can take one of the three | 103 | not run wild after the resume). The last ioctl() argument can take one |
104 | values, defined in kernel/power/power.h: | 104 | of the three values, defined in kernel/power/power.h: |
105 | PMOPS_PREPARE - make the kernel carry out the | 105 | PMOPS_PREPARE - make the kernel carry out the |
106 | pm_ops->prepare(PM_SUSPEND_DISK) operation | 106 | hibernation_ops->prepare() operation |
107 | PMOPS_ENTER - make the kernel power off the system by calling | 107 | PMOPS_ENTER - make the kernel power off the system by calling |
108 | pm_ops->enter(PM_SUSPEND_DISK) | 108 | hibernation_ops->enter() |
109 | PMOPS_FINISH - make the kernel carry out the | 109 | PMOPS_FINISH - make the kernel carry out the |
110 | pm_ops->finish(PM_SUSPEND_DISK) operation | 110 | hibernation_ops->finish() operation |
111 | Note that the actual constants are misnamed because they surface | ||
112 | internal kernel implementation details that have changed. | ||
111 | 113 | ||
112 | The device's read() operation can be used to transfer the snapshot image from | 114 | The device's read() operation can be used to transfer the snapshot image from |
113 | the kernel. It has the following limitations: | 115 | the kernel. It has the following limitations: |
diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index f8c63410bcbf..52b23471dd69 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c | |||
@@ -29,7 +29,6 @@ static u32 acpi_suspend_states[] = { | |||
29 | [PM_SUSPEND_ON] = ACPI_STATE_S0, | 29 | [PM_SUSPEND_ON] = ACPI_STATE_S0, |
30 | [PM_SUSPEND_STANDBY] = ACPI_STATE_S1, | 30 | [PM_SUSPEND_STANDBY] = ACPI_STATE_S1, |
31 | [PM_SUSPEND_MEM] = ACPI_STATE_S3, | 31 | [PM_SUSPEND_MEM] = ACPI_STATE_S3, |
32 | [PM_SUSPEND_DISK] = ACPI_STATE_S4, | ||
33 | [PM_SUSPEND_MAX] = ACPI_STATE_S5 | 32 | [PM_SUSPEND_MAX] = ACPI_STATE_S5 |
34 | }; | 33 | }; |
35 | 34 | ||
@@ -94,14 +93,6 @@ static int acpi_pm_enter(suspend_state_t pm_state) | |||
94 | do_suspend_lowlevel(); | 93 | do_suspend_lowlevel(); |
95 | break; | 94 | break; |
96 | 95 | ||
97 | case PM_SUSPEND_DISK: | ||
98 | if (acpi_pm_ops.pm_disk_mode == PM_DISK_PLATFORM) | ||
99 | status = acpi_enter_sleep_state(acpi_state); | ||
100 | break; | ||
101 | case PM_SUSPEND_MAX: | ||
102 | acpi_power_off(); | ||
103 | break; | ||
104 | |||
105 | default: | 96 | default: |
106 | return -EINVAL; | 97 | return -EINVAL; |
107 | } | 98 | } |
@@ -157,12 +148,13 @@ int acpi_suspend(u32 acpi_state) | |||
157 | suspend_state_t states[] = { | 148 | suspend_state_t states[] = { |
158 | [1] = PM_SUSPEND_STANDBY, | 149 | [1] = PM_SUSPEND_STANDBY, |
159 | [3] = PM_SUSPEND_MEM, | 150 | [3] = PM_SUSPEND_MEM, |
160 | [4] = PM_SUSPEND_DISK, | ||
161 | [5] = PM_SUSPEND_MAX | 151 | [5] = PM_SUSPEND_MAX |
162 | }; | 152 | }; |
163 | 153 | ||
164 | if (acpi_state < 6 && states[acpi_state]) | 154 | if (acpi_state < 6 && states[acpi_state]) |
165 | return pm_suspend(states[acpi_state]); | 155 | return pm_suspend(states[acpi_state]); |
156 | if (acpi_state == 4) | ||
157 | return hibernate(); | ||
166 | return -EINVAL; | 158 | return -EINVAL; |
167 | } | 159 | } |
168 | 160 | ||
@@ -189,6 +181,49 @@ static struct pm_ops acpi_pm_ops = { | |||
189 | .finish = acpi_pm_finish, | 181 | .finish = acpi_pm_finish, |
190 | }; | 182 | }; |
191 | 183 | ||
184 | #ifdef CONFIG_SOFTWARE_SUSPEND | ||
185 | static int acpi_hibernation_prepare(void) | ||
186 | { | ||
187 | return acpi_sleep_prepare(ACPI_STATE_S4); | ||
188 | } | ||
189 | |||
190 | static int acpi_hibernation_enter(void) | ||
191 | { | ||
192 | acpi_status status = AE_OK; | ||
193 | unsigned long flags = 0; | ||
194 | |||
195 | ACPI_FLUSH_CPU_CACHE(); | ||
196 | |||
197 | local_irq_save(flags); | ||
198 | acpi_enable_wakeup_device(ACPI_STATE_S4); | ||
199 | /* This shouldn't return. If it returns, we have a problem */ | ||
200 | status = acpi_enter_sleep_state(ACPI_STATE_S4); | ||
201 | local_irq_restore(flags); | ||
202 | |||
203 | return ACPI_SUCCESS(status) ? 0 : -EFAULT; | ||
204 | } | ||
205 | |||
206 | static void acpi_hibernation_finish(void) | ||
207 | { | ||
208 | acpi_leave_sleep_state(ACPI_STATE_S4); | ||
209 | acpi_disable_wakeup_device(ACPI_STATE_S4); | ||
210 | |||
211 | /* reset firmware waking vector */ | ||
212 | acpi_set_firmware_waking_vector((acpi_physical_address) 0); | ||
213 | |||
214 | if (init_8259A_after_S1) { | ||
215 | printk("Broken toshiba laptop -> kicking interrupts\n"); | ||
216 | init_8259A(0); | ||
217 | } | ||
218 | } | ||
219 | |||
220 | static struct hibernation_ops acpi_hibernation_ops = { | ||
221 | .prepare = acpi_hibernation_prepare, | ||
222 | .enter = acpi_hibernation_enter, | ||
223 | .finish = acpi_hibernation_finish, | ||
224 | }; | ||
225 | #endif /* CONFIG_SOFTWARE_SUSPEND */ | ||
226 | |||
192 | /* | 227 | /* |
193 | * Toshiba fails to preserve interrupts over S1, reinitialization | 228 | * Toshiba fails to preserve interrupts over S1, reinitialization |
194 | * of 8259 is needed after S1 resume. | 229 | * of 8259 is needed after S1 resume. |
@@ -227,14 +262,18 @@ int __init acpi_sleep_init(void) | |||
227 | sleep_states[i] = 1; | 262 | sleep_states[i] = 1; |
228 | printk(" S%d", i); | 263 | printk(" S%d", i); |
229 | } | 264 | } |
230 | if (i == ACPI_STATE_S4) { | ||
231 | if (sleep_states[i]) | ||
232 | acpi_pm_ops.pm_disk_mode = PM_DISK_PLATFORM; | ||
233 | } | ||
234 | } | 265 | } |
235 | printk(")\n"); | 266 | printk(")\n"); |
236 | 267 | ||
237 | pm_set_ops(&acpi_pm_ops); | 268 | pm_set_ops(&acpi_pm_ops); |
269 | |||
270 | #ifdef CONFIG_SOFTWARE_SUSPEND | ||
271 | if (sleep_states[ACPI_STATE_S4]) | ||
272 | hibernation_set_ops(&acpi_hibernation_ops); | ||
273 | #else | ||
274 | sleep_states[ACPI_STATE_S4] = 0; | ||
275 | #endif | ||
276 | |||
238 | return 0; | 277 | return 0; |
239 | } | 278 | } |
240 | 279 | ||
diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index 5a76e5be61d5..76b45f0b8341 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c | |||
@@ -60,7 +60,7 @@ acpi_system_write_sleep(struct file *file, | |||
60 | state = simple_strtoul(str, NULL, 0); | 60 | state = simple_strtoul(str, NULL, 0); |
61 | #ifdef CONFIG_SOFTWARE_SUSPEND | 61 | #ifdef CONFIG_SOFTWARE_SUSPEND |
62 | if (state == 4) { | 62 | if (state == 4) { |
63 | error = pm_suspend(PM_SUSPEND_DISK); | 63 | error = hibernate(); |
64 | goto Done; | 64 | goto Done; |
65 | } | 65 | } |
66 | #endif | 66 | #endif |
diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c index 7ed92dc3d833..3c3f2ebf3fc9 100644 --- a/drivers/i2c/chips/tps65010.c +++ b/drivers/i2c/chips/tps65010.c | |||
@@ -354,7 +354,7 @@ static void tps65010_interrupt(struct tps65010 *tps) | |||
354 | * also needs to get error handling and probably | 354 | * also needs to get error handling and probably |
355 | * an #ifdef CONFIG_SOFTWARE_SUSPEND | 355 | * an #ifdef CONFIG_SOFTWARE_SUSPEND |
356 | */ | 356 | */ |
357 | pm_suspend(PM_SUSPEND_DISK); | 357 | hibernate(); |
358 | #endif | 358 | #endif |
359 | poll = 1; | 359 | poll = 1; |
360 | } | 360 | } |
diff --git a/include/linux/pm.h b/include/linux/pm.h index 6e8fa3049e5d..87545e0f0b58 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h | |||
@@ -107,26 +107,11 @@ typedef int __bitwise suspend_state_t; | |||
107 | #define PM_SUSPEND_ON ((__force suspend_state_t) 0) | 107 | #define PM_SUSPEND_ON ((__force suspend_state_t) 0) |
108 | #define PM_SUSPEND_STANDBY ((__force suspend_state_t) 1) | 108 | #define PM_SUSPEND_STANDBY ((__force suspend_state_t) 1) |
109 | #define PM_SUSPEND_MEM ((__force suspend_state_t) 3) | 109 | #define PM_SUSPEND_MEM ((__force suspend_state_t) 3) |
110 | #define PM_SUSPEND_DISK ((__force suspend_state_t) 4) | 110 | #define PM_SUSPEND_MAX ((__force suspend_state_t) 4) |
111 | #define PM_SUSPEND_MAX ((__force suspend_state_t) 5) | ||
112 | |||
113 | typedef int __bitwise suspend_disk_method_t; | ||
114 | |||
115 | /* invalid must be 0 so struct pm_ops initialisers can leave it out */ | ||
116 | #define PM_DISK_INVALID ((__force suspend_disk_method_t) 0) | ||
117 | #define PM_DISK_PLATFORM ((__force suspend_disk_method_t) 1) | ||
118 | #define PM_DISK_SHUTDOWN ((__force suspend_disk_method_t) 2) | ||
119 | #define PM_DISK_REBOOT ((__force suspend_disk_method_t) 3) | ||
120 | #define PM_DISK_TEST ((__force suspend_disk_method_t) 4) | ||
121 | #define PM_DISK_TESTPROC ((__force suspend_disk_method_t) 5) | ||
122 | #define PM_DISK_MAX ((__force suspend_disk_method_t) 6) | ||
123 | 111 | ||
124 | /** | 112 | /** |
125 | * struct pm_ops - Callbacks for managing platform dependent suspend states. | 113 | * struct pm_ops - Callbacks for managing platform dependent suspend states. |
126 | * @valid: Callback to determine whether the given state can be entered. | 114 | * @valid: Callback to determine whether the given state can be entered. |
127 | * If %CONFIG_SOFTWARE_SUSPEND is set then %PM_SUSPEND_DISK is | ||
128 | * always valid and never passed to this call. If not assigned, | ||
129 | * no suspend states are valid. | ||
130 | * Valid states are advertised in /sys/power/state but can still | 115 | * Valid states are advertised in /sys/power/state but can still |
131 | * be rejected by prepare or enter if the conditions aren't right. | 116 | * be rejected by prepare or enter if the conditions aren't right. |
132 | * There is a %pm_valid_only_mem function available that can be assigned | 117 | * There is a %pm_valid_only_mem function available that can be assigned |
@@ -140,24 +125,12 @@ typedef int __bitwise suspend_disk_method_t; | |||
140 | * | 125 | * |
141 | * @finish: Called when the system has left the given state and all devices | 126 | * @finish: Called when the system has left the given state and all devices |
142 | * are resumed. The return value is ignored. | 127 | * are resumed. The return value is ignored. |
143 | * | ||
144 | * @pm_disk_mode: The generic code always allows one of the shutdown methods | ||
145 | * %PM_DISK_SHUTDOWN, %PM_DISK_REBOOT, %PM_DISK_TEST and | ||
146 | * %PM_DISK_TESTPROC. If this variable is set, the mode it is set | ||
147 | * to is allowed in addition to those modes and is also made default. | ||
148 | * When this mode is sent selected, the @prepare call will be called | ||
149 | * before suspending to disk (if present), the @enter call should be | ||
150 | * present and will be called after all state has been saved and the | ||
151 | * machine is ready to be powered off; the @finish callback is called | ||
152 | * after state has been restored. All these calls are called with | ||
153 | * %PM_SUSPEND_DISK as the state. | ||
154 | */ | 128 | */ |
155 | struct pm_ops { | 129 | struct pm_ops { |
156 | int (*valid)(suspend_state_t state); | 130 | int (*valid)(suspend_state_t state); |
157 | int (*prepare)(suspend_state_t state); | 131 | int (*prepare)(suspend_state_t state); |
158 | int (*enter)(suspend_state_t state); | 132 | int (*enter)(suspend_state_t state); |
159 | int (*finish)(suspend_state_t state); | 133 | int (*finish)(suspend_state_t state); |
160 | suspend_disk_method_t pm_disk_mode; | ||
161 | }; | 134 | }; |
162 | 135 | ||
163 | /** | 136 | /** |
@@ -276,8 +249,6 @@ extern void device_power_up(void); | |||
276 | extern void device_resume(void); | 249 | extern void device_resume(void); |
277 | 250 | ||
278 | #ifdef CONFIG_PM | 251 | #ifdef CONFIG_PM |
279 | extern suspend_disk_method_t pm_disk_mode; | ||
280 | |||
281 | extern int device_suspend(pm_message_t state); | 252 | extern int device_suspend(pm_message_t state); |
282 | extern int device_prepare_suspend(pm_message_t state); | 253 | extern int device_prepare_suspend(pm_message_t state); |
283 | 254 | ||
diff --git a/include/linux/suspend.h b/include/linux/suspend.h index 9d2aa1a12aa0..d74da9122b60 100644 --- a/include/linux/suspend.h +++ b/include/linux/suspend.h | |||
@@ -32,6 +32,24 @@ static inline int pm_prepare_console(void) { return 0; } | |||
32 | static inline void pm_restore_console(void) {} | 32 | static inline void pm_restore_console(void) {} |
33 | #endif | 33 | #endif |
34 | 34 | ||
35 | /** | ||
36 | * struct hibernation_ops - hibernation platform support | ||
37 | * | ||
38 | * The methods in this structure allow a platform to override the default | ||
39 | * mechanism of shutting down the machine during a hibernation transition. | ||
40 | * | ||
41 | * All three methods must be assigned. | ||
42 | * | ||
43 | * @prepare: prepare system for hibernation | ||
44 | * @enter: shut down system after state has been saved to disk | ||
45 | * @finish: finish/clean up after state has been reloaded | ||
46 | */ | ||
47 | struct hibernation_ops { | ||
48 | int (*prepare)(void); | ||
49 | int (*enter)(void); | ||
50 | void (*finish)(void); | ||
51 | }; | ||
52 | |||
35 | #if defined(CONFIG_PM) && defined(CONFIG_SOFTWARE_SUSPEND) | 53 | #if defined(CONFIG_PM) && defined(CONFIG_SOFTWARE_SUSPEND) |
36 | /* kernel/power/snapshot.c */ | 54 | /* kernel/power/snapshot.c */ |
37 | extern void __init register_nosave_region(unsigned long, unsigned long); | 55 | extern void __init register_nosave_region(unsigned long, unsigned long); |
@@ -39,11 +57,17 @@ extern int swsusp_page_is_forbidden(struct page *); | |||
39 | extern void swsusp_set_page_free(struct page *); | 57 | extern void swsusp_set_page_free(struct page *); |
40 | extern void swsusp_unset_page_free(struct page *); | 58 | extern void swsusp_unset_page_free(struct page *); |
41 | extern unsigned long get_safe_page(gfp_t gfp_mask); | 59 | extern unsigned long get_safe_page(gfp_t gfp_mask); |
60 | |||
61 | extern void hibernation_set_ops(struct hibernation_ops *ops); | ||
62 | extern int hibernate(void); | ||
42 | #else | 63 | #else |
43 | static inline void register_nosave_region(unsigned long b, unsigned long e) {} | 64 | static inline void register_nosave_region(unsigned long b, unsigned long e) {} |
44 | static inline int swsusp_page_is_forbidden(struct page *p) { return 0; } | 65 | static inline int swsusp_page_is_forbidden(struct page *p) { return 0; } |
45 | static inline void swsusp_set_page_free(struct page *p) {} | 66 | static inline void swsusp_set_page_free(struct page *p) {} |
46 | static inline void swsusp_unset_page_free(struct page *p) {} | 67 | static inline void swsusp_unset_page_free(struct page *p) {} |
68 | |||
69 | static inline void hibernation_set_ops(struct hibernation_ops *ops) {} | ||
70 | static inline int hibernate(void) { return -ENOSYS; } | ||
47 | #endif /* defined(CONFIG_PM) && defined(CONFIG_SOFTWARE_SUSPEND) */ | 71 | #endif /* defined(CONFIG_PM) && defined(CONFIG_SOFTWARE_SUSPEND) */ |
48 | 72 | ||
49 | void save_processor_state(void); | 73 | void save_processor_state(void); |
diff --git a/kernel/power/disk.c b/kernel/power/disk.c index 06331374d862..b5f0543ed84d 100644 --- a/kernel/power/disk.c +++ b/kernel/power/disk.c | |||
@@ -30,30 +30,69 @@ char resume_file[256] = CONFIG_PM_STD_PARTITION; | |||
30 | dev_t swsusp_resume_device; | 30 | dev_t swsusp_resume_device; |
31 | sector_t swsusp_resume_block; | 31 | sector_t swsusp_resume_block; |
32 | 32 | ||
33 | enum { | ||
34 | HIBERNATION_INVALID, | ||
35 | HIBERNATION_PLATFORM, | ||
36 | HIBERNATION_TEST, | ||
37 | HIBERNATION_TESTPROC, | ||
38 | HIBERNATION_SHUTDOWN, | ||
39 | HIBERNATION_REBOOT, | ||
40 | /* keep last */ | ||
41 | __HIBERNATION_AFTER_LAST | ||
42 | }; | ||
43 | #define HIBERNATION_MAX (__HIBERNATION_AFTER_LAST-1) | ||
44 | #define HIBERNATION_FIRST (HIBERNATION_INVALID + 1) | ||
45 | |||
46 | static int hibernation_mode = HIBERNATION_SHUTDOWN; | ||
47 | |||
48 | struct hibernation_ops *hibernation_ops; | ||
49 | |||
50 | /** | ||
51 | * hibernation_set_ops - set the global hibernate operations | ||
52 | * @ops: the hibernation operations to use in subsequent hibernation transitions | ||
53 | */ | ||
54 | |||
55 | void hibernation_set_ops(struct hibernation_ops *ops) | ||
56 | { | ||
57 | if (ops && !(ops->prepare && ops->enter && ops->finish)) { | ||
58 | WARN_ON(1); | ||
59 | return; | ||
60 | } | ||
61 | mutex_lock(&pm_mutex); | ||
62 | hibernation_ops = ops; | ||
63 | if (ops) | ||
64 | hibernation_mode = HIBERNATION_PLATFORM; | ||
65 | else if (hibernation_mode == HIBERNATION_PLATFORM) | ||
66 | hibernation_mode = HIBERNATION_SHUTDOWN; | ||
67 | |||
68 | mutex_unlock(&pm_mutex); | ||
69 | } | ||
70 | |||
71 | |||
33 | /** | 72 | /** |
34 | * platform_prepare - prepare the machine for hibernation using the | 73 | * platform_prepare - prepare the machine for hibernation using the |
35 | * platform driver if so configured and return an error code if it fails | 74 | * platform driver if so configured and return an error code if it fails |
36 | */ | 75 | */ |
37 | 76 | ||
38 | static inline int platform_prepare(void) | 77 | static int platform_prepare(void) |
39 | { | 78 | { |
40 | int error = 0; | 79 | return (hibernation_mode == HIBERNATION_PLATFORM && hibernation_ops) ? |
80 | hibernation_ops->prepare() : 0; | ||
81 | } | ||
41 | 82 | ||
42 | switch (pm_disk_mode) { | 83 | /** |
43 | case PM_DISK_TEST: | 84 | * platform_finish - switch the machine to the normal mode of operation |
44 | case PM_DISK_TESTPROC: | 85 | * using the platform driver (must be called after platform_prepare()) |
45 | case PM_DISK_SHUTDOWN: | 86 | */ |
46 | case PM_DISK_REBOOT: | 87 | |
47 | break; | 88 | static void platform_finish(void) |
48 | default: | 89 | { |
49 | if (pm_ops && pm_ops->prepare) | 90 | if (hibernation_mode == HIBERNATION_PLATFORM && hibernation_ops) |
50 | error = pm_ops->prepare(PM_SUSPEND_DISK); | 91 | hibernation_ops->finish(); |
51 | } | ||
52 | return error; | ||
53 | } | 92 | } |
54 | 93 | ||
55 | /** | 94 | /** |
56 | * power_down - Shut machine down for hibernate. | 95 | * power_down - Shut the machine down for hibernation. |
57 | * | 96 | * |
58 | * Use the platform driver, if configured so; otherwise try | 97 | * Use the platform driver, if configured so; otherwise try |
59 | * to power off or reboot. | 98 | * to power off or reboot. |
@@ -61,20 +100,20 @@ static inline int platform_prepare(void) | |||
61 | 100 | ||
62 | static void power_down(void) | 101 | static void power_down(void) |
63 | { | 102 | { |
64 | switch (pm_disk_mode) { | 103 | switch (hibernation_mode) { |
65 | case PM_DISK_TEST: | 104 | case HIBERNATION_TEST: |
66 | case PM_DISK_TESTPROC: | 105 | case HIBERNATION_TESTPROC: |
67 | break; | 106 | break; |
68 | case PM_DISK_SHUTDOWN: | 107 | case HIBERNATION_SHUTDOWN: |
69 | kernel_power_off(); | 108 | kernel_power_off(); |
70 | break; | 109 | break; |
71 | case PM_DISK_REBOOT: | 110 | case HIBERNATION_REBOOT: |
72 | kernel_restart(NULL); | 111 | kernel_restart(NULL); |
73 | break; | 112 | break; |
74 | default: | 113 | case HIBERNATION_PLATFORM: |
75 | if (pm_ops && pm_ops->enter) { | 114 | if (hibernation_ops) { |
76 | kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); | 115 | kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); |
77 | pm_ops->enter(PM_SUSPEND_DISK); | 116 | hibernation_ops->enter(); |
78 | break; | 117 | break; |
79 | } | 118 | } |
80 | } | 119 | } |
@@ -87,20 +126,6 @@ static void power_down(void) | |||
87 | while(1); | 126 | while(1); |
88 | } | 127 | } |
89 | 128 | ||
90 | static inline void platform_finish(void) | ||
91 | { | ||
92 | switch (pm_disk_mode) { | ||
93 | case PM_DISK_TEST: | ||
94 | case PM_DISK_TESTPROC: | ||
95 | case PM_DISK_SHUTDOWN: | ||
96 | case PM_DISK_REBOOT: | ||
97 | break; | ||
98 | default: | ||
99 | if (pm_ops && pm_ops->finish) | ||
100 | pm_ops->finish(PM_SUSPEND_DISK); | ||
101 | } | ||
102 | } | ||
103 | |||
104 | static void unprepare_processes(void) | 129 | static void unprepare_processes(void) |
105 | { | 130 | { |
106 | thaw_processes(); | 131 | thaw_processes(); |
@@ -120,13 +145,10 @@ static int prepare_processes(void) | |||
120 | } | 145 | } |
121 | 146 | ||
122 | /** | 147 | /** |
123 | * pm_suspend_disk - The granpappy of hibernation power management. | 148 | * hibernate - The granpappy of the built-in hibernation management |
124 | * | ||
125 | * If not, then call swsusp to do its thing, then figure out how | ||
126 | * to power down the system. | ||
127 | */ | 149 | */ |
128 | 150 | ||
129 | int pm_suspend_disk(void) | 151 | int hibernate(void) |
130 | { | 152 | { |
131 | int error; | 153 | int error; |
132 | 154 | ||
@@ -143,7 +165,8 @@ int pm_suspend_disk(void) | |||
143 | if (error) | 165 | if (error) |
144 | goto Finish; | 166 | goto Finish; |
145 | 167 | ||
146 | if (pm_disk_mode == PM_DISK_TESTPROC) { | 168 | mutex_lock(&pm_mutex); |
169 | if (hibernation_mode == HIBERNATION_TESTPROC) { | ||
147 | printk("swsusp debug: Waiting for 5 seconds.\n"); | 170 | printk("swsusp debug: Waiting for 5 seconds.\n"); |
148 | mdelay(5000); | 171 | mdelay(5000); |
149 | goto Thaw; | 172 | goto Thaw; |
@@ -168,7 +191,7 @@ int pm_suspend_disk(void) | |||
168 | if (error) | 191 | if (error) |
169 | goto Enable_cpus; | 192 | goto Enable_cpus; |
170 | 193 | ||
171 | if (pm_disk_mode == PM_DISK_TEST) { | 194 | if (hibernation_mode == HIBERNATION_TEST) { |
172 | printk("swsusp debug: Waiting for 5 seconds.\n"); | 195 | printk("swsusp debug: Waiting for 5 seconds.\n"); |
173 | mdelay(5000); | 196 | mdelay(5000); |
174 | goto Enable_cpus; | 197 | goto Enable_cpus; |
@@ -205,6 +228,7 @@ int pm_suspend_disk(void) | |||
205 | device_resume(); | 228 | device_resume(); |
206 | resume_console(); | 229 | resume_console(); |
207 | Thaw: | 230 | Thaw: |
231 | mutex_unlock(&pm_mutex); | ||
208 | unprepare_processes(); | 232 | unprepare_processes(); |
209 | Finish: | 233 | Finish: |
210 | free_basic_memory_bitmaps(); | 234 | free_basic_memory_bitmaps(); |
@@ -220,7 +244,7 @@ int pm_suspend_disk(void) | |||
220 | * Called as a late_initcall (so all devices are discovered and | 244 | * Called as a late_initcall (so all devices are discovered and |
221 | * initialized), we call swsusp to see if we have a saved image or not. | 245 | * initialized), we call swsusp to see if we have a saved image or not. |
222 | * If so, we quiesce devices, the restore the saved image. We will | 246 | * If so, we quiesce devices, the restore the saved image. We will |
223 | * return above (in pm_suspend_disk() ) if everything goes well. | 247 | * return above (in hibernate() ) if everything goes well. |
224 | * Otherwise, we fail gracefully and return to the normally | 248 | * Otherwise, we fail gracefully and return to the normally |
225 | * scheduled program. | 249 | * scheduled program. |
226 | * | 250 | * |
@@ -315,25 +339,26 @@ static int software_resume(void) | |||
315 | late_initcall(software_resume); | 339 | late_initcall(software_resume); |
316 | 340 | ||
317 | 341 | ||
318 | static const char * const pm_disk_modes[] = { | 342 | static const char * const hibernation_modes[] = { |
319 | [PM_DISK_PLATFORM] = "platform", | 343 | [HIBERNATION_PLATFORM] = "platform", |
320 | [PM_DISK_SHUTDOWN] = "shutdown", | 344 | [HIBERNATION_SHUTDOWN] = "shutdown", |
321 | [PM_DISK_REBOOT] = "reboot", | 345 | [HIBERNATION_REBOOT] = "reboot", |
322 | [PM_DISK_TEST] = "test", | 346 | [HIBERNATION_TEST] = "test", |
323 | [PM_DISK_TESTPROC] = "testproc", | 347 | [HIBERNATION_TESTPROC] = "testproc", |
324 | }; | 348 | }; |
325 | 349 | ||
326 | /** | 350 | /** |
327 | * disk - Control suspend-to-disk mode | 351 | * disk - Control hibernation mode |
328 | * | 352 | * |
329 | * Suspend-to-disk can be handled in several ways. We have a few options | 353 | * Suspend-to-disk can be handled in several ways. We have a few options |
330 | * for putting the system to sleep - using the platform driver (e.g. ACPI | 354 | * for putting the system to sleep - using the platform driver (e.g. ACPI |
331 | * or other pm_ops), powering off the system or rebooting the system | 355 | * or other hibernation_ops), powering off the system or rebooting the |
332 | * (for testing) as well as the two test modes. | 356 | * system (for testing) as well as the two test modes. |
333 | * | 357 | * |
334 | * The system can support 'platform', and that is known a priori (and | 358 | * The system can support 'platform', and that is known a priori (and |
335 | * encoded in pm_ops). However, the user may choose 'shutdown' or 'reboot' | 359 | * encoded by the presence of hibernation_ops). However, the user may |
336 | * as alternatives, as well as the test modes 'test' and 'testproc'. | 360 | * choose 'shutdown' or 'reboot' as alternatives, as well as one fo the |
361 | * test modes, 'test' or 'testproc'. | ||
337 | * | 362 | * |
338 | * show() will display what the mode is currently set to. | 363 | * show() will display what the mode is currently set to. |
339 | * store() will accept one of | 364 | * store() will accept one of |
@@ -345,7 +370,7 @@ static const char * const pm_disk_modes[] = { | |||
345 | * 'testproc' | 370 | * 'testproc' |
346 | * | 371 | * |
347 | * It will only change to 'platform' if the system | 372 | * It will only change to 'platform' if the system |
348 | * supports it (as determined from pm_ops->pm_disk_mode). | 373 | * supports it (as determined by having hibernation_ops). |
349 | */ | 374 | */ |
350 | 375 | ||
351 | static ssize_t disk_show(struct kset *kset, char *buf) | 376 | static ssize_t disk_show(struct kset *kset, char *buf) |
@@ -353,28 +378,25 @@ static ssize_t disk_show(struct kset *kset, char *buf) | |||
353 | int i; | 378 | int i; |
354 | char *start = buf; | 379 | char *start = buf; |
355 | 380 | ||
356 | for (i = PM_DISK_PLATFORM; i < PM_DISK_MAX; i++) { | 381 | for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { |
357 | if (!pm_disk_modes[i]) | 382 | if (!hibernation_modes[i]) |
358 | continue; | 383 | continue; |
359 | switch (i) { | 384 | switch (i) { |
360 | case PM_DISK_SHUTDOWN: | 385 | case HIBERNATION_SHUTDOWN: |
361 | case PM_DISK_REBOOT: | 386 | case HIBERNATION_REBOOT: |
362 | case PM_DISK_TEST: | 387 | case HIBERNATION_TEST: |
363 | case PM_DISK_TESTPROC: | 388 | case HIBERNATION_TESTPROC: |
364 | break; | 389 | break; |
365 | default: | 390 | case HIBERNATION_PLATFORM: |
366 | if (pm_ops && pm_ops->enter && | 391 | if (hibernation_ops) |
367 | (i == pm_ops->pm_disk_mode)) | ||
368 | break; | 392 | break; |
369 | /* not a valid mode, continue with loop */ | 393 | /* not a valid mode, continue with loop */ |
370 | continue; | 394 | continue; |
371 | } | 395 | } |
372 | if (i == pm_disk_mode) | 396 | if (i == hibernation_mode) |
373 | buf += sprintf(buf, "[%s]", pm_disk_modes[i]); | 397 | buf += sprintf(buf, "[%s] ", hibernation_modes[i]); |
374 | else | 398 | else |
375 | buf += sprintf(buf, "%s", pm_disk_modes[i]); | 399 | buf += sprintf(buf, "%s ", hibernation_modes[i]); |
376 | if (i+1 != PM_DISK_MAX) | ||
377 | buf += sprintf(buf, " "); | ||
378 | } | 400 | } |
379 | buf += sprintf(buf, "\n"); | 401 | buf += sprintf(buf, "\n"); |
380 | return buf-start; | 402 | return buf-start; |
@@ -387,39 +409,38 @@ static ssize_t disk_store(struct kset *kset, const char *buf, size_t n) | |||
387 | int i; | 409 | int i; |
388 | int len; | 410 | int len; |
389 | char *p; | 411 | char *p; |
390 | suspend_disk_method_t mode = 0; | 412 | int mode = HIBERNATION_INVALID; |
391 | 413 | ||
392 | p = memchr(buf, '\n', n); | 414 | p = memchr(buf, '\n', n); |
393 | len = p ? p - buf : n; | 415 | len = p ? p - buf : n; |
394 | 416 | ||
395 | mutex_lock(&pm_mutex); | 417 | mutex_lock(&pm_mutex); |
396 | for (i = PM_DISK_PLATFORM; i < PM_DISK_MAX; i++) { | 418 | for (i = HIBERNATION_FIRST; i <= HIBERNATION_MAX; i++) { |
397 | if (!strncmp(buf, pm_disk_modes[i], len)) { | 419 | if (!strncmp(buf, hibernation_modes[i], len)) { |
398 | mode = i; | 420 | mode = i; |
399 | break; | 421 | break; |
400 | } | 422 | } |
401 | } | 423 | } |
402 | if (mode) { | 424 | if (mode != HIBERNATION_INVALID) { |
403 | switch (mode) { | 425 | switch (mode) { |
404 | case PM_DISK_SHUTDOWN: | 426 | case HIBERNATION_SHUTDOWN: |
405 | case PM_DISK_REBOOT: | 427 | case HIBERNATION_REBOOT: |
406 | case PM_DISK_TEST: | 428 | case HIBERNATION_TEST: |
407 | case PM_DISK_TESTPROC: | 429 | case HIBERNATION_TESTPROC: |
408 | pm_disk_mode = mode; | 430 | hibernation_mode = mode; |
409 | break; | 431 | break; |
410 | default: | 432 | case HIBERNATION_PLATFORM: |
411 | if (pm_ops && pm_ops->enter && | 433 | if (hibernation_ops) |
412 | (mode == pm_ops->pm_disk_mode)) | 434 | hibernation_mode = mode; |
413 | pm_disk_mode = mode; | ||
414 | else | 435 | else |
415 | error = -EINVAL; | 436 | error = -EINVAL; |
416 | } | 437 | } |
417 | } else { | 438 | } else |
418 | error = -EINVAL; | 439 | error = -EINVAL; |
419 | } | ||
420 | 440 | ||
421 | pr_debug("PM: suspend-to-disk mode set to '%s'\n", | 441 | if (!error) |
422 | pm_disk_modes[mode]); | 442 | pr_debug("PM: suspend-to-disk mode set to '%s'\n", |
443 | hibernation_modes[mode]); | ||
423 | mutex_unlock(&pm_mutex); | 444 | mutex_unlock(&pm_mutex); |
424 | return error ? error : n; | 445 | return error ? error : n; |
425 | } | 446 | } |
diff --git a/kernel/power/main.c b/kernel/power/main.c index f6dda685e7e2..40d56a31245e 100644 --- a/kernel/power/main.c +++ b/kernel/power/main.c | |||
@@ -30,7 +30,6 @@ | |||
30 | DEFINE_MUTEX(pm_mutex); | 30 | DEFINE_MUTEX(pm_mutex); |
31 | 31 | ||
32 | struct pm_ops *pm_ops; | 32 | struct pm_ops *pm_ops; |
33 | suspend_disk_method_t pm_disk_mode = PM_DISK_SHUTDOWN; | ||
34 | 33 | ||
35 | /** | 34 | /** |
36 | * pm_set_ops - Set the global power method table. | 35 | * pm_set_ops - Set the global power method table. |
@@ -41,10 +40,6 @@ void pm_set_ops(struct pm_ops * ops) | |||
41 | { | 40 | { |
42 | mutex_lock(&pm_mutex); | 41 | mutex_lock(&pm_mutex); |
43 | pm_ops = ops; | 42 | pm_ops = ops; |
44 | if (ops && ops->pm_disk_mode != PM_DISK_INVALID) { | ||
45 | pm_disk_mode = ops->pm_disk_mode; | ||
46 | } else | ||
47 | pm_disk_mode = PM_DISK_SHUTDOWN; | ||
48 | mutex_unlock(&pm_mutex); | 43 | mutex_unlock(&pm_mutex); |
49 | } | 44 | } |
50 | 45 | ||
@@ -184,24 +179,12 @@ static void suspend_finish(suspend_state_t state) | |||
184 | static const char * const pm_states[PM_SUSPEND_MAX] = { | 179 | static const char * const pm_states[PM_SUSPEND_MAX] = { |
185 | [PM_SUSPEND_STANDBY] = "standby", | 180 | [PM_SUSPEND_STANDBY] = "standby", |
186 | [PM_SUSPEND_MEM] = "mem", | 181 | [PM_SUSPEND_MEM] = "mem", |
187 | [PM_SUSPEND_DISK] = "disk", | ||
188 | }; | 182 | }; |
189 | 183 | ||
190 | static inline int valid_state(suspend_state_t state) | 184 | static inline int valid_state(suspend_state_t state) |
191 | { | 185 | { |
192 | /* Suspend-to-disk does not really need low-level support. | 186 | /* All states need lowlevel support and need to be valid |
193 | * It can work with shutdown/reboot if needed. If it isn't | 187 | * to the lowlevel implementation, no valid callback |
194 | * configured, then it cannot be supported. | ||
195 | */ | ||
196 | if (state == PM_SUSPEND_DISK) | ||
197 | #ifdef CONFIG_SOFTWARE_SUSPEND | ||
198 | return 1; | ||
199 | #else | ||
200 | return 0; | ||
201 | #endif | ||
202 | |||
203 | /* all other states need lowlevel support and need to be | ||
204 | * valid to the lowlevel implementation, no valid callback | ||
205 | * implies that none are valid. */ | 188 | * implies that none are valid. */ |
206 | if (!pm_ops || !pm_ops->valid || !pm_ops->valid(state)) | 189 | if (!pm_ops || !pm_ops->valid || !pm_ops->valid(state)) |
207 | return 0; | 190 | return 0; |
@@ -229,11 +212,6 @@ static int enter_state(suspend_state_t state) | |||
229 | if (!mutex_trylock(&pm_mutex)) | 212 | if (!mutex_trylock(&pm_mutex)) |
230 | return -EBUSY; | 213 | return -EBUSY; |
231 | 214 | ||
232 | if (state == PM_SUSPEND_DISK) { | ||
233 | error = pm_suspend_disk(); | ||
234 | goto Unlock; | ||
235 | } | ||
236 | |||
237 | pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); | 215 | pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); |
238 | if ((error = suspend_prepare(state))) | 216 | if ((error = suspend_prepare(state))) |
239 | goto Unlock; | 217 | goto Unlock; |
@@ -251,7 +229,7 @@ static int enter_state(suspend_state_t state) | |||
251 | 229 | ||
252 | /** | 230 | /** |
253 | * pm_suspend - Externally visible function for suspending system. | 231 | * pm_suspend - Externally visible function for suspending system. |
254 | * @state: Enumarted value of state to enter. | 232 | * @state: Enumerated value of state to enter. |
255 | * | 233 | * |
256 | * Determine whether or not value is within range, get state | 234 | * Determine whether or not value is within range, get state |
257 | * structure, and enter (above). | 235 | * structure, and enter (above). |
@@ -289,7 +267,13 @@ static ssize_t state_show(struct kset *kset, char *buf) | |||
289 | if (pm_states[i] && valid_state(i)) | 267 | if (pm_states[i] && valid_state(i)) |
290 | s += sprintf(s,"%s ", pm_states[i]); | 268 | s += sprintf(s,"%s ", pm_states[i]); |
291 | } | 269 | } |
292 | s += sprintf(s,"\n"); | 270 | #ifdef CONFIG_SOFTWARE_SUSPEND |
271 | s += sprintf(s, "%s\n", "disk"); | ||
272 | #else | ||
273 | if (s != buf) | ||
274 | /* convert the last space to a newline */ | ||
275 | *(s-1) = '\n'; | ||
276 | #endif | ||
293 | return (s - buf); | 277 | return (s - buf); |
294 | } | 278 | } |
295 | 279 | ||
@@ -304,6 +288,12 @@ static ssize_t state_store(struct kset *kset, const char *buf, size_t n) | |||
304 | p = memchr(buf, '\n', n); | 288 | p = memchr(buf, '\n', n); |
305 | len = p ? p - buf : n; | 289 | len = p ? p - buf : n; |
306 | 290 | ||
291 | /* First, check if we are requested to hibernate */ | ||
292 | if (!strncmp(buf, "disk", len)) { | ||
293 | error = hibernate(); | ||
294 | return error ? error : n; | ||
295 | } | ||
296 | |||
307 | for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) { | 297 | for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) { |
308 | if (*s && !strncmp(buf, *s, len)) | 298 | if (*s && !strncmp(buf, *s, len)) |
309 | break; | 299 | break; |
diff --git a/kernel/power/power.h b/kernel/power/power.h index 34b43542785a..51381487103f 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h | |||
@@ -25,12 +25,7 @@ struct swsusp_info { | |||
25 | */ | 25 | */ |
26 | #define SPARE_PAGES ((1024 * 1024) >> PAGE_SHIFT) | 26 | #define SPARE_PAGES ((1024 * 1024) >> PAGE_SHIFT) |
27 | 27 | ||
28 | extern int pm_suspend_disk(void); | 28 | extern struct hibernation_ops *hibernation_ops; |
29 | #else | ||
30 | static inline int pm_suspend_disk(void) | ||
31 | { | ||
32 | return -EPERM; | ||
33 | } | ||
34 | #endif | 29 | #endif |
35 | 30 | ||
36 | extern int pfn_is_nosave(unsigned long); | 31 | extern int pfn_is_nosave(unsigned long); |
diff --git a/kernel/power/user.c b/kernel/power/user.c index 040560d9c312..24d7d78e6f42 100644 --- a/kernel/power/user.c +++ b/kernel/power/user.c | |||
@@ -130,16 +130,16 @@ static inline int platform_prepare(void) | |||
130 | { | 130 | { |
131 | int error = 0; | 131 | int error = 0; |
132 | 132 | ||
133 | if (pm_ops && pm_ops->prepare) | 133 | if (hibernation_ops) |
134 | error = pm_ops->prepare(PM_SUSPEND_DISK); | 134 | error = hibernation_ops->prepare(); |
135 | 135 | ||
136 | return error; | 136 | return error; |
137 | } | 137 | } |
138 | 138 | ||
139 | static inline void platform_finish(void) | 139 | static inline void platform_finish(void) |
140 | { | 140 | { |
141 | if (pm_ops && pm_ops->finish) | 141 | if (hibernation_ops) |
142 | pm_ops->finish(PM_SUSPEND_DISK); | 142 | hibernation_ops->finish(); |
143 | } | 143 | } |
144 | 144 | ||
145 | static inline int snapshot_suspend(int platform_suspend) | 145 | static inline int snapshot_suspend(int platform_suspend) |
@@ -384,7 +384,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp, | |||
384 | switch (arg) { | 384 | switch (arg) { |
385 | 385 | ||
386 | case PMOPS_PREPARE: | 386 | case PMOPS_PREPARE: |
387 | if (pm_ops && pm_ops->enter) { | 387 | if (hibernation_ops) { |
388 | data->platform_suspend = 1; | 388 | data->platform_suspend = 1; |
389 | error = 0; | 389 | error = 0; |
390 | } else { | 390 | } else { |
@@ -395,8 +395,7 @@ static int snapshot_ioctl(struct inode *inode, struct file *filp, | |||
395 | case PMOPS_ENTER: | 395 | case PMOPS_ENTER: |
396 | if (data->platform_suspend) { | 396 | if (data->platform_suspend) { |
397 | kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); | 397 | kernel_shutdown_prepare(SYSTEM_SUSPEND_DISK); |
398 | error = pm_ops->enter(PM_SUSPEND_DISK); | 398 | error = hibernation_ops->enter(); |
399 | error = 0; | ||
400 | } | 399 | } |
401 | break; | 400 | break; |
402 | 401 | ||
diff --git a/kernel/sys.c b/kernel/sys.c index 926bf9d7ac45..8ecfe3473779 100644 --- a/kernel/sys.c +++ b/kernel/sys.c | |||
@@ -881,7 +881,7 @@ asmlinkage long sys_reboot(int magic1, int magic2, unsigned int cmd, void __user | |||
881 | #ifdef CONFIG_SOFTWARE_SUSPEND | 881 | #ifdef CONFIG_SOFTWARE_SUSPEND |
882 | case LINUX_REBOOT_CMD_SW_SUSPEND: | 882 | case LINUX_REBOOT_CMD_SW_SUSPEND: |
883 | { | 883 | { |
884 | int ret = pm_suspend(PM_SUSPEND_DISK); | 884 | int ret = hibernate(); |
885 | unlock_kernel(); | 885 | unlock_kernel(); |
886 | return ret; | 886 | return ret; |
887 | } | 887 | } |