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