diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/acpi/sleep/main.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/acpi/sleep/main.c')
-rw-r--r-- | drivers/acpi/sleep/main.c | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c new file mode 100644 index 00000000000..0a5d2a94131 --- /dev/null +++ b/drivers/acpi/sleep/main.c | |||
@@ -0,0 +1,234 @@ | |||
1 | /* | ||
2 | * sleep.c - ACPI sleep support. | ||
3 | * | ||
4 | * Copyright (c) 2004 David Shaohua Li <shaohua.li@intel.com> | ||
5 | * Copyright (c) 2000-2003 Patrick Mochel | ||
6 | * Copyright (c) 2003 Open Source Development Lab | ||
7 | * | ||
8 | * This file is released under the GPLv2. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/delay.h> | ||
13 | #include <linux/irq.h> | ||
14 | #include <linux/dmi.h> | ||
15 | #include <linux/device.h> | ||
16 | #include <linux/suspend.h> | ||
17 | #include <asm/io.h> | ||
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 | |||
26 | extern void do_suspend_lowlevel_s4bios(void); | ||
27 | extern void do_suspend_lowlevel(void); | ||
28 | |||
29 | static u32 acpi_suspend_states[] = { | ||
30 | [PM_SUSPEND_ON] = ACPI_STATE_S0, | ||
31 | [PM_SUSPEND_STANDBY] = ACPI_STATE_S1, | ||
32 | [PM_SUSPEND_MEM] = ACPI_STATE_S3, | ||
33 | [PM_SUSPEND_DISK] = ACPI_STATE_S4, | ||
34 | }; | ||
35 | |||
36 | static int init_8259A_after_S1; | ||
37 | |||
38 | /** | ||
39 | * acpi_pm_prepare - Do preliminary suspend work. | ||
40 | * @pm_state: suspend state we're entering. | ||
41 | * | ||
42 | * Make sure we support the state. If we do, and we need it, set the | ||
43 | * firmware waking vector and do arch-specific nastiness to get the | ||
44 | * wakeup code to the waking vector. | ||
45 | */ | ||
46 | |||
47 | static int acpi_pm_prepare(suspend_state_t pm_state) | ||
48 | { | ||
49 | u32 acpi_state = acpi_suspend_states[pm_state]; | ||
50 | |||
51 | if (!sleep_states[acpi_state]) | ||
52 | return -EPERM; | ||
53 | |||
54 | /* do we have a wakeup address for S2 and S3? */ | ||
55 | /* Here, we support only S4BIOS, those we set the wakeup address */ | ||
56 | /* S4OS is only supported for now via swsusp.. */ | ||
57 | if (pm_state == PM_SUSPEND_MEM || pm_state == PM_SUSPEND_DISK) { | ||
58 | if (!acpi_wakeup_address) | ||
59 | return -EFAULT; | ||
60 | acpi_set_firmware_waking_vector( | ||
61 | (acpi_physical_address) virt_to_phys( | ||
62 | (void *)acpi_wakeup_address)); | ||
63 | } | ||
64 | ACPI_FLUSH_CPU_CACHE(); | ||
65 | acpi_enable_wakeup_device_prep(acpi_state); | ||
66 | acpi_enter_sleep_state_prep(acpi_state); | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | |||
71 | /** | ||
72 | * acpi_pm_enter - Actually enter a sleep state. | ||
73 | * @pm_state: State we're entering. | ||
74 | * | ||
75 | * Flush caches and go to sleep. For STR or STD, we have to call | ||
76 | * arch-specific assembly, which in turn call acpi_enter_sleep_state(). | ||
77 | * It's unfortunate, but it works. Please fix if you're feeling frisky. | ||
78 | */ | ||
79 | |||
80 | static int acpi_pm_enter(suspend_state_t pm_state) | ||
81 | { | ||
82 | acpi_status status = AE_OK; | ||
83 | unsigned long flags = 0; | ||
84 | u32 acpi_state = acpi_suspend_states[pm_state]; | ||
85 | |||
86 | ACPI_FLUSH_CPU_CACHE(); | ||
87 | |||
88 | /* Do arch specific saving of state. */ | ||
89 | if (pm_state > PM_SUSPEND_STANDBY) { | ||
90 | int error = acpi_save_state_mem(); | ||
91 | if (error) | ||
92 | return error; | ||
93 | } | ||
94 | |||
95 | |||
96 | local_irq_save(flags); | ||
97 | acpi_enable_wakeup_device(acpi_state); | ||
98 | switch (pm_state) | ||
99 | { | ||
100 | case PM_SUSPEND_STANDBY: | ||
101 | barrier(); | ||
102 | status = acpi_enter_sleep_state(acpi_state); | ||
103 | break; | ||
104 | |||
105 | case PM_SUSPEND_MEM: | ||
106 | do_suspend_lowlevel(); | ||
107 | break; | ||
108 | |||
109 | case PM_SUSPEND_DISK: | ||
110 | if (acpi_pm_ops.pm_disk_mode == PM_DISK_PLATFORM) | ||
111 | status = acpi_enter_sleep_state(acpi_state); | ||
112 | else | ||
113 | do_suspend_lowlevel_s4bios(); | ||
114 | break; | ||
115 | default: | ||
116 | return -EINVAL; | ||
117 | } | ||
118 | local_irq_restore(flags); | ||
119 | printk(KERN_DEBUG "Back to C!\n"); | ||
120 | |||
121 | /* restore processor state | ||
122 | * We should only be here if we're coming back from STR or STD. | ||
123 | * And, in the case of the latter, the memory image should have already | ||
124 | * been loaded from disk. | ||
125 | */ | ||
126 | if (pm_state > PM_SUSPEND_STANDBY) | ||
127 | acpi_restore_state_mem(); | ||
128 | |||
129 | |||
130 | return ACPI_SUCCESS(status) ? 0 : -EFAULT; | ||
131 | } | ||
132 | |||
133 | |||
134 | /** | ||
135 | * acpi_pm_finish - Finish up suspend sequence. | ||
136 | * @pm_state: State we're coming out of. | ||
137 | * | ||
138 | * This is called after we wake back up (or if entering the sleep state | ||
139 | * failed). | ||
140 | */ | ||
141 | |||
142 | static int acpi_pm_finish(suspend_state_t pm_state) | ||
143 | { | ||
144 | u32 acpi_state = acpi_suspend_states[pm_state]; | ||
145 | |||
146 | acpi_leave_sleep_state(acpi_state); | ||
147 | acpi_disable_wakeup_device(acpi_state); | ||
148 | |||
149 | /* reset firmware waking vector */ | ||
150 | acpi_set_firmware_waking_vector((acpi_physical_address) 0); | ||
151 | |||
152 | if (init_8259A_after_S1) { | ||
153 | printk("Broken toshiba laptop -> kicking interrupts\n"); | ||
154 | init_8259A(0); | ||
155 | } | ||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | |||
160 | int acpi_suspend(u32 acpi_state) | ||
161 | { | ||
162 | suspend_state_t states[] = { | ||
163 | [1] = PM_SUSPEND_STANDBY, | ||
164 | [3] = PM_SUSPEND_MEM, | ||
165 | [4] = PM_SUSPEND_DISK, | ||
166 | }; | ||
167 | |||
168 | if (acpi_state <= 4 && states[acpi_state]) | ||
169 | return pm_suspend(states[acpi_state]); | ||
170 | return -EINVAL; | ||
171 | } | ||
172 | |||
173 | static struct pm_ops acpi_pm_ops = { | ||
174 | .prepare = acpi_pm_prepare, | ||
175 | .enter = acpi_pm_enter, | ||
176 | .finish = acpi_pm_finish, | ||
177 | }; | ||
178 | |||
179 | |||
180 | /* | ||
181 | * Toshiba fails to preserve interrupts over S1, reinitialization | ||
182 | * of 8259 is needed after S1 resume. | ||
183 | */ | ||
184 | static int __init init_ints_after_s1(struct dmi_system_id *d) | ||
185 | { | ||
186 | printk(KERN_WARNING "%s with broken S1 detected.\n", d->ident); | ||
187 | init_8259A_after_S1 = 1; | ||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | static struct dmi_system_id __initdata acpisleep_dmi_table[] = { | ||
192 | { | ||
193 | .callback = init_ints_after_s1, | ||
194 | .ident = "Toshiba Satellite 4030cdt", | ||
195 | .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"), }, | ||
196 | }, | ||
197 | { }, | ||
198 | }; | ||
199 | |||
200 | static int __init acpi_sleep_init(void) | ||
201 | { | ||
202 | int i = 0; | ||
203 | |||
204 | dmi_check_system(acpisleep_dmi_table); | ||
205 | |||
206 | if (acpi_disabled) | ||
207 | return 0; | ||
208 | |||
209 | printk(KERN_INFO PREFIX "(supports"); | ||
210 | for (i=0; i < ACPI_S_STATE_COUNT; i++) { | ||
211 | acpi_status status; | ||
212 | u8 type_a, type_b; | ||
213 | status = acpi_get_sleep_type_data(i, &type_a, &type_b); | ||
214 | if (ACPI_SUCCESS(status)) { | ||
215 | sleep_states[i] = 1; | ||
216 | printk(" S%d", i); | ||
217 | } | ||
218 | if (i == ACPI_STATE_S4) { | ||
219 | if (acpi_gbl_FACS->S4bios_f) { | ||
220 | sleep_states[i] = 1; | ||
221 | printk(" S4bios"); | ||
222 | acpi_pm_ops.pm_disk_mode = PM_DISK_FIRMWARE; | ||
223 | } | ||
224 | if (sleep_states[i]) | ||
225 | acpi_pm_ops.pm_disk_mode = PM_DISK_PLATFORM; | ||
226 | } | ||
227 | } | ||
228 | printk(")\n"); | ||
229 | |||
230 | pm_set_ops(&acpi_pm_ops); | ||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | late_initcall(acpi_sleep_init); | ||