Merge with 2.5.75.
[linux-2.6/linux-mips.git] / drivers / acpi / sleep / main.c
blob3b17c9d2e87ac475354b9e25ee94c90c746fc010
1 /*
2 * sleep.c - ACPI sleep support.
3 *
4 * Copyright (c) 2000-2003 Patrick Mochel
6 * Portions are
7 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
8 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
9 */
11 #include <linux/delay.h>
12 #include <linux/irq.h>
13 #include <linux/device.h>
14 #include <linux/suspend.h>
15 #include <acpi/acpi_bus.h>
16 #include <acpi/acpi_drivers.h>
17 #include "sleep.h"
19 #define _COMPONENT ACPI_SYSTEM_COMPONENT
20 ACPI_MODULE_NAME ("sleep")
22 u8 sleep_states[ACPI_S_STATE_COUNT];
24 /**
25 * acpi_system_restore_state - OS-specific restoration of state
26 * @state: sleep state we're exiting
28 * Note that if we're coming back from S4, the memory image should have already
29 * been loaded from the disk and is already in place. (Otherwise how else would we
30 * be here?).
32 acpi_status
33 acpi_system_restore_state (
34 u32 state)
36 /* restore processor state
37 * We should only be here if we're coming back from STR or STD.
38 * And, in the case of the latter, the memory image should have already
39 * been loaded from disk.
41 if (state > ACPI_STATE_S1)
42 acpi_restore_state_mem();
44 /* wait for power to come back */
45 mdelay(10);
47 /* turn all the devices back on */
48 device_resume(RESUME_POWER_ON);
50 /* enable interrupts once again */
51 ACPI_ENABLE_IRQS();
53 /* restore device context */
54 device_resume(RESUME_RESTORE_STATE);
56 if (dmi_broken & BROKEN_INIT_AFTER_S1) {
57 printk("Broken toshiba laptop -> kicking interrupts\n");
58 init_8259A(0);
61 return AE_OK;
64 /**
65 * acpi_system_save_state - save OS specific state and power down devices
66 * @state: sleep state we're entering.
68 * This handles saving all context to memory, and possibly disk.
69 * First, we call to the device driver layer to save device state.
70 * Once we have that, we save whatevery processor and kernel state we
71 * need to memory.
72 * If we're entering S4, we then write the memory image to disk.
74 * Only then is it safe for us to power down devices, since we may need
75 * the disks and upstream buses to write to.
77 acpi_status
78 acpi_system_save_state(
79 u32 state)
81 int error = 0;
83 /* Send notification to devices that they will be suspended.
84 * If any device or driver cannot make the transition, either up
85 * or down, we'll get an error back.
87 error = device_suspend(state, SUSPEND_NOTIFY);
88 if (error)
89 return AE_ERROR;
91 if (state < ACPI_STATE_S5) {
93 /* Tell devices to stop I/O and actually save their state.
94 * It is theoretically possible that something could fail,
95 * so handle that gracefully..
97 error = device_suspend(state, SUSPEND_SAVE_STATE);
98 if (error) {
99 /* tell devices to restore state if they have
100 * it saved and to start taking I/O requests.
102 device_resume(RESUME_RESTORE_STATE);
103 return error;
106 /* flush caches */
107 ACPI_FLUSH_CPU_CACHE();
109 /* Do arch specific saving of state. */
110 if (state > ACPI_STATE_S1) {
111 error = acpi_save_state_mem();
113 if (!error && (state == ACPI_STATE_S4))
114 error = acpi_save_state_disk();
116 if (error) {
117 device_resume(RESUME_RESTORE_STATE);
118 return error;
123 /* disable interrupts
124 * Note that acpi_suspend -- our caller -- will do this once we return.
125 * But, we want it done early, so we don't get any suprises during
126 * the device suspend sequence.
128 ACPI_DISABLE_IRQS();
130 /* Unconditionally turn off devices.
131 * Obvious if we enter a sleep state.
132 * If entering S5 (soft off), this should put devices in a
133 * quiescent state.
135 error = device_suspend(state, SUSPEND_POWER_DOWN);
137 /* We're pretty screwed if we got an error from this.
138 * We try to recover by simply calling our own restore_state
139 * function; see above for definition.
141 * If it's S5 though, go through with it anyway..
143 if (error && state != ACPI_STATE_S5)
144 acpi_system_restore_state(state);
146 return error ? AE_ERROR : AE_OK;
150 /****************************************************************************
152 * FUNCTION: acpi_system_suspend
154 * PARAMETERS: %state: Sleep state to enter.
156 * RETURN: acpi_status, whether or not we successfully entered and
157 * exited sleep.
159 * DESCRIPTION: Perform OS-specific action to enter sleep state.
160 * This is the final step in going to sleep, per spec. If we
161 * know we're coming back (i.e. not entering S5), we save the
162 * processor flags. [ We'll have to save and restore them anyway,
163 * so we use the arch-agnostic save_flags and restore_flags
164 * here.] We then set the place to return to in arch-specific
165 * globals using arch_set_return_point. Finally, we call the
166 * ACPI function to write the proper values to I/O ports.
168 ****************************************************************************/
170 acpi_status
171 acpi_system_suspend(
172 u32 state)
174 acpi_status status = AE_ERROR;
175 unsigned long flags = 0;
177 local_irq_save(flags);
179 switch (state)
181 case ACPI_STATE_S1:
182 barrier();
183 status = acpi_enter_sleep_state(state);
184 break;
186 #ifdef CONFIG_SOFTWARE_SUSPEND
187 case ACPI_STATE_S2:
188 case ACPI_STATE_S3:
189 do_suspend_lowlevel(0);
190 break;
191 #endif
192 case ACPI_STATE_S4:
193 do_suspend_lowlevel_s4bios(0);
194 break;
195 default:
196 printk(KERN_WARNING PREFIX "don't know how to handle %d state.\n", state);
197 break;
199 local_irq_restore(flags);
200 printk(KERN_CRIT "Back to C!\n");
202 return status;
207 * acpi_suspend - OS-agnostic system suspend/resume support (S? states)
208 * @state: state we're entering
211 acpi_status
212 acpi_suspend (
213 u32 state)
215 acpi_status status;
217 /* Suspend is hard to get right on SMP. */
218 if (num_online_cpus() != 1)
219 return AE_ERROR;
221 /* get out if state is invalid */
222 if (state < ACPI_STATE_S1 || state > ACPI_STATE_S5)
223 return AE_ERROR;
225 /* Since we handle S4OS via a different path (swsusp), give up if no s4bios. */
226 if (state == ACPI_STATE_S4 && !acpi_gbl_FACS->S4bios_f)
227 return AE_ERROR;
230 * TBD: S1 can be done without device_suspend. Make a CONFIG_XX
231 * to handle however when S1 failed without device_suspend.
233 if (freeze_processes()) {
234 thaw_processes();
235 return AE_ERROR; /* device_suspend needs processes to be stopped */
238 /* do we have a wakeup address for S2 and S3? */
239 /* Here, we support only S4BIOS, those we set the wakeup address */
240 /* S4OS is only supported for now via swsusp.. */
241 if (state == ACPI_STATE_S2 || state == ACPI_STATE_S3 || state == ACPI_STATE_S4) {
242 if (!acpi_wakeup_address)
243 return AE_ERROR;
244 acpi_set_firmware_waking_vector((acpi_physical_address) acpi_wakeup_address);
247 status = acpi_system_save_state(state);
248 if (!ACPI_SUCCESS(status))
249 return status;
251 acpi_enter_sleep_state_prep(state);
253 /* disable interrupts and flush caches */
254 ACPI_DISABLE_IRQS();
255 ACPI_FLUSH_CPU_CACHE();
257 /* perform OS-specific sleep actions */
258 status = acpi_system_suspend(state);
260 /* Even if we failed to go to sleep, all of the devices are in an suspended
261 * mode. So, we run these unconditionaly to make sure we have a usable system
262 * no matter what.
264 acpi_leave_sleep_state(state);
265 acpi_system_restore_state(state);
267 /* make sure interrupts are enabled */
268 ACPI_ENABLE_IRQS();
270 /* reset firmware waking vector */
271 acpi_set_firmware_waking_vector((acpi_physical_address) 0);
272 thaw_processes();
274 return status;
277 static int __init acpi_sleep_init(void)
279 int i = 0;
281 ACPI_FUNCTION_TRACE("acpi_system_add_fs");
283 if (acpi_disabled)
284 return_VALUE(0);
286 printk(KERN_INFO PREFIX "(supports");
287 for (i=0; i<ACPI_S_STATE_COUNT; i++) {
288 acpi_status status;
289 u8 type_a, type_b;
290 status = acpi_get_sleep_type_data(i, &type_a, &type_b);
291 if (ACPI_SUCCESS(status)) {
292 sleep_states[i] = 1;
293 printk(" S%d", i);
295 if (i == ACPI_STATE_S4 && acpi_gbl_FACS->S4bios_f) {
296 sleep_states[i] = 1;
297 printk(" S4bios");
300 printk(")\n");
302 return_VALUE(0);
305 late_initcall(acpi_sleep_init);