]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * sleep.c - ACPI sleep support. | |
3 | * | |
e2a5b420 | 4 | * Copyright (c) 2005 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com> |
1da177e4 LT |
5 | * Copyright (c) 2004 David Shaohua Li <shaohua.li@intel.com> |
6 | * Copyright (c) 2000-2003 Patrick Mochel | |
7 | * Copyright (c) 2003 Open Source Development Lab | |
8 | * | |
9 | * This file is released under the GPLv2. | |
10 | * | |
11 | */ | |
12 | ||
13 | #include <linux/delay.h> | |
14 | #include <linux/irq.h> | |
15 | #include <linux/dmi.h> | |
16 | #include <linux/device.h> | |
17 | #include <linux/suspend.h> | |
1da177e4 LT |
18 | #include <acpi/acpi_bus.h> |
19 | #include <acpi/acpi_drivers.h> | |
20 | #include "sleep.h" | |
21 | ||
22 | u8 sleep_states[ACPI_S_STATE_COUNT]; | |
23 | ||
24 | static struct pm_ops acpi_pm_ops; | |
25 | ||
1da177e4 LT |
26 | extern void do_suspend_lowlevel(void); |
27 | ||
28 | static u32 acpi_suspend_states[] = { | |
e2a5b420 AS |
29 | [PM_SUSPEND_ON] = ACPI_STATE_S0, |
30 | [PM_SUSPEND_STANDBY] = ACPI_STATE_S1, | |
31 | [PM_SUSPEND_MEM] = ACPI_STATE_S3, | |
e2a5b420 | 32 | [PM_SUSPEND_MAX] = ACPI_STATE_S5 |
1da177e4 LT |
33 | }; |
34 | ||
35 | static int init_8259A_after_S1; | |
36 | ||
37 | /** | |
38 | * acpi_pm_prepare - Do preliminary suspend work. | |
39 | * @pm_state: suspend state we're entering. | |
40 | * | |
41 | * Make sure we support the state. If we do, and we need it, set the | |
42 | * firmware waking vector and do arch-specific nastiness to get the | |
43 | * wakeup code to the waking vector. | |
44 | */ | |
45 | ||
e2a5b420 AS |
46 | extern int acpi_sleep_prepare(u32 acpi_state); |
47 | extern void acpi_power_off(void); | |
48 | ||
1da177e4 LT |
49 | static int acpi_pm_prepare(suspend_state_t pm_state) |
50 | { | |
51 | u32 acpi_state = acpi_suspend_states[pm_state]; | |
52 | ||
e2a5b420 AS |
53 | if (!sleep_states[acpi_state]) { |
54 | printk("acpi_pm_prepare does not support %d \n", pm_state); | |
1da177e4 | 55 | return -EPERM; |
1da177e4 | 56 | } |
e2a5b420 | 57 | return acpi_sleep_prepare(acpi_state); |
1da177e4 LT |
58 | } |
59 | ||
1da177e4 LT |
60 | /** |
61 | * acpi_pm_enter - Actually enter a sleep state. | |
62 | * @pm_state: State we're entering. | |
63 | * | |
64 | * Flush caches and go to sleep. For STR or STD, we have to call | |
65 | * arch-specific assembly, which in turn call acpi_enter_sleep_state(). | |
66 | * It's unfortunate, but it works. Please fix if you're feeling frisky. | |
67 | */ | |
68 | ||
69 | static int acpi_pm_enter(suspend_state_t pm_state) | |
70 | { | |
71 | acpi_status status = AE_OK; | |
72 | unsigned long flags = 0; | |
73 | u32 acpi_state = acpi_suspend_states[pm_state]; | |
74 | ||
75 | ACPI_FLUSH_CPU_CACHE(); | |
76 | ||
77 | /* Do arch specific saving of state. */ | |
78 | if (pm_state > PM_SUSPEND_STANDBY) { | |
79 | int error = acpi_save_state_mem(); | |
80 | if (error) | |
81 | return error; | |
82 | } | |
83 | ||
1da177e4 LT |
84 | local_irq_save(flags); |
85 | acpi_enable_wakeup_device(acpi_state); | |
e2a5b420 | 86 | switch (pm_state) { |
1da177e4 LT |
87 | case PM_SUSPEND_STANDBY: |
88 | barrier(); | |
89 | status = acpi_enter_sleep_state(acpi_state); | |
90 | break; | |
91 | ||
92 | case PM_SUSPEND_MEM: | |
93 | do_suspend_lowlevel(); | |
94 | break; | |
95 | ||
1da177e4 LT |
96 | default: |
97 | return -EINVAL; | |
98 | } | |
872d83d0 AP |
99 | |
100 | /* ACPI 3.0 specs (P62) says that it's the responsabilty | |
101 | * of the OSPM to clear the status bit [ implying that the | |
102 | * POWER_BUTTON event should not reach userspace ] | |
103 | */ | |
104 | if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) | |
105 | acpi_clear_event(ACPI_EVENT_POWER_BUTTON); | |
106 | ||
1da177e4 LT |
107 | local_irq_restore(flags); |
108 | printk(KERN_DEBUG "Back to C!\n"); | |
109 | ||
110 | /* restore processor state | |
111 | * We should only be here if we're coming back from STR or STD. | |
112 | * And, in the case of the latter, the memory image should have already | |
113 | * been loaded from disk. | |
114 | */ | |
115 | if (pm_state > PM_SUSPEND_STANDBY) | |
116 | acpi_restore_state_mem(); | |
117 | ||
1da177e4 LT |
118 | return ACPI_SUCCESS(status) ? 0 : -EFAULT; |
119 | } | |
120 | ||
1da177e4 LT |
121 | /** |
122 | * acpi_pm_finish - Finish up suspend sequence. | |
123 | * @pm_state: State we're coming out of. | |
124 | * | |
125 | * This is called after we wake back up (or if entering the sleep state | |
126 | * failed). | |
127 | */ | |
128 | ||
129 | static int acpi_pm_finish(suspend_state_t pm_state) | |
130 | { | |
131 | u32 acpi_state = acpi_suspend_states[pm_state]; | |
132 | ||
133 | acpi_leave_sleep_state(acpi_state); | |
134 | acpi_disable_wakeup_device(acpi_state); | |
135 | ||
136 | /* reset firmware waking vector */ | |
137 | acpi_set_firmware_waking_vector((acpi_physical_address) 0); | |
138 | ||
139 | if (init_8259A_after_S1) { | |
140 | printk("Broken toshiba laptop -> kicking interrupts\n"); | |
141 | init_8259A(0); | |
142 | } | |
143 | return 0; | |
144 | } | |
145 | ||
1da177e4 LT |
146 | int acpi_suspend(u32 acpi_state) |
147 | { | |
148 | suspend_state_t states[] = { | |
e2a5b420 AS |
149 | [1] = PM_SUSPEND_STANDBY, |
150 | [3] = PM_SUSPEND_MEM, | |
e2a5b420 | 151 | [5] = PM_SUSPEND_MAX |
1da177e4 LT |
152 | }; |
153 | ||
e2a5b420 | 154 | if (acpi_state < 6 && states[acpi_state]) |
1da177e4 | 155 | return pm_suspend(states[acpi_state]); |
a3d25c27 RW |
156 | if (acpi_state == 4) |
157 | return hibernate(); | |
1da177e4 LT |
158 | return -EINVAL; |
159 | } | |
160 | ||
eb9289eb SL |
161 | static int acpi_pm_state_valid(suspend_state_t pm_state) |
162 | { | |
e8c9c502 | 163 | u32 acpi_state; |
eb9289eb | 164 | |
e8c9c502 JB |
165 | switch (pm_state) { |
166 | case PM_SUSPEND_ON: | |
167 | case PM_SUSPEND_STANDBY: | |
168 | case PM_SUSPEND_MEM: | |
169 | acpi_state = acpi_suspend_states[pm_state]; | |
170 | ||
171 | return sleep_states[acpi_state]; | |
172 | default: | |
173 | return 0; | |
174 | } | |
eb9289eb SL |
175 | } |
176 | ||
1da177e4 | 177 | static struct pm_ops acpi_pm_ops = { |
eb9289eb | 178 | .valid = acpi_pm_state_valid, |
e2a5b420 AS |
179 | .prepare = acpi_pm_prepare, |
180 | .enter = acpi_pm_enter, | |
181 | .finish = acpi_pm_finish, | |
1da177e4 LT |
182 | }; |
183 | ||
a3d25c27 RW |
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 | }; | |
fd350943 | 225 | #endif /* CONFIG_SOFTWARE_SUSPEND */ |
a3d25c27 | 226 | |
1da177e4 LT |
227 | /* |
228 | * Toshiba fails to preserve interrupts over S1, reinitialization | |
229 | * of 8259 is needed after S1 resume. | |
230 | */ | |
231 | static int __init init_ints_after_s1(struct dmi_system_id *d) | |
232 | { | |
233 | printk(KERN_WARNING "%s with broken S1 detected.\n", d->ident); | |
234 | init_8259A_after_S1 = 1; | |
235 | return 0; | |
236 | } | |
237 | ||
238 | static struct dmi_system_id __initdata acpisleep_dmi_table[] = { | |
239 | { | |
e2a5b420 AS |
240 | .callback = init_ints_after_s1, |
241 | .ident = "Toshiba Satellite 4030cdt", | |
242 | .matches = {DMI_MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"),}, | |
243 | }, | |
244 | {}, | |
1da177e4 LT |
245 | }; |
246 | ||
aafbcd16 | 247 | int __init acpi_sleep_init(void) |
1da177e4 | 248 | { |
e2a5b420 | 249 | int i = 0; |
1da177e4 LT |
250 | |
251 | dmi_check_system(acpisleep_dmi_table); | |
252 | ||
253 | if (acpi_disabled) | |
254 | return 0; | |
255 | ||
256 | printk(KERN_INFO PREFIX "(supports"); | |
e2a5b420 | 257 | for (i = 0; i < ACPI_S_STATE_COUNT; i++) { |
1da177e4 LT |
258 | acpi_status status; |
259 | u8 type_a, type_b; | |
260 | status = acpi_get_sleep_type_data(i, &type_a, &type_b); | |
261 | if (ACPI_SUCCESS(status)) { | |
262 | sleep_states[i] = 1; | |
263 | printk(" S%d", i); | |
264 | } | |
1da177e4 LT |
265 | } |
266 | printk(")\n"); | |
267 | ||
268 | pm_set_ops(&acpi_pm_ops); | |
a3d25c27 RW |
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 | ||
1da177e4 LT |
277 | return 0; |
278 | } |