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