import of ftp.dlink.com/GPL/DSMG-600_reB/ppclinux.tar.gz
[linux-2.4.21-pre4.git] / drivers / acpi / hardware / hwsleep.c
1
2 /******************************************************************************
3  *
4  * Name: hwsleep.c - ACPI Hardware Sleep/Wake Interface
5  *              $Revision: 1.1.1.1 $
6  *
7  *****************************************************************************/
8
9 /*
10  *  Copyright (C) 2000, 2001 R. Byron Moore
11  *
12  *  This program is free software; you can redistribute it and/or modify
13  *  it under the terms of the GNU General Public License as published by
14  *  the Free Software Foundation; either version 2 of the License, or
15  *  (at your option) any later version.
16  *
17  *  This program is distributed in the hope that it will be useful,
18  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *  GNU General Public License for more details.
21  *
22  *  You should have received a copy of the GNU General Public License
23  *  along with this program; if not, write to the Free Software
24  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25  */
26
27 #include "acpi.h"
28 #include "acnamesp.h"
29 #include "achware.h"
30
31 #define _COMPONENT          ACPI_HARDWARE
32          MODULE_NAME         ("hwsleep")
33
34
35 /******************************************************************************
36  *
37  * FUNCTION:    Acpi_set_firmware_waking_vector
38  *
39  * PARAMETERS:  Physical_address    - Physical address of ACPI real mode
40  *                                    entry point.
41  *
42  * RETURN:      AE_OK or AE_ERROR
43  *
44  * DESCRIPTION: Access function for d_firmware_waking_vector field in FACS
45  *
46  ******************************************************************************/
47
48 acpi_status
49 acpi_set_firmware_waking_vector (
50         ACPI_PHYSICAL_ADDRESS physical_address)
51 {
52
53         FUNCTION_TRACE ("Acpi_set_firmware_waking_vector");
54
55
56         /* Make sure that we have an FACS */
57
58         if (!acpi_gbl_FACS) {
59                 return_ACPI_STATUS (AE_NO_ACPI_TABLES);
60         }
61
62         /* Set the vector */
63
64         if (acpi_gbl_FACS->vector_width == 32) {
65                 * (u32 *) acpi_gbl_FACS->firmware_waking_vector = (u32) physical_address;
66         }
67         else {
68                 *acpi_gbl_FACS->firmware_waking_vector = physical_address;
69         }
70
71         return_ACPI_STATUS (AE_OK);
72 }
73
74
75 /******************************************************************************
76  *
77  * FUNCTION:    Acpi_get_firmware_waking_vector
78  *
79  * PARAMETERS:  *Physical_address   - Output buffer where contents of
80  *                                    the Firmware_waking_vector field of
81  *                                    the FACS will be stored.
82  *
83  * RETURN:      Status
84  *
85  * DESCRIPTION: Access function for d_firmware_waking_vector field in FACS
86  *
87  ******************************************************************************/
88
89 acpi_status
90 acpi_get_firmware_waking_vector (
91         ACPI_PHYSICAL_ADDRESS *physical_address)
92 {
93
94         FUNCTION_TRACE ("Acpi_get_firmware_waking_vector");
95
96
97         if (!physical_address) {
98                 return_ACPI_STATUS (AE_BAD_PARAMETER);
99         }
100
101         /* Make sure that we have an FACS */
102
103         if (!acpi_gbl_FACS) {
104                 return_ACPI_STATUS (AE_NO_ACPI_TABLES);
105         }
106
107         /* Get the vector */
108
109         if (acpi_gbl_FACS->vector_width == 32) {
110                 *physical_address = * (u32 *) acpi_gbl_FACS->firmware_waking_vector;
111         }
112         else {
113                 *physical_address = *acpi_gbl_FACS->firmware_waking_vector;
114         }
115
116         return_ACPI_STATUS (AE_OK);
117 }
118
119 /******************************************************************************
120  *
121  * FUNCTION:    Acpi_enter_sleep_state
122  *
123  * PARAMETERS:  Sleep_state         - Which sleep state to enter
124  *
125  * RETURN:      Status
126  *
127  * DESCRIPTION: Enter a system sleep state (see ACPI 2.0 spec p 231)
128  *
129  ******************************************************************************/
130
131 acpi_status
132 acpi_enter_sleep_state (
133         u8                  sleep_state)
134 {
135         acpi_status         status;
136         acpi_object_list    arg_list;
137         acpi_object         arg;
138         u8                  type_a;
139         u8                  type_b;
140         u16                 PM1Acontrol;
141         u16                 PM1Bcontrol;
142
143
144         FUNCTION_TRACE ("Acpi_enter_sleep_state");
145
146
147         /*
148          * _PSW methods could be run here to enable wake-on keyboard, LAN, etc.
149          */
150         status = acpi_hw_obtain_sleep_type_register_data (sleep_state, &type_a, &type_b);
151         if (!ACPI_SUCCESS (status)) {
152                 return status;
153         }
154
155         /* run the _PTS and _GTS methods */
156
157         MEMSET(&arg_list, 0, sizeof(arg_list));
158         arg_list.count = 1;
159         arg_list.pointer = &arg;
160
161         MEMSET(&arg, 0, sizeof(arg));
162         arg.type = ACPI_TYPE_INTEGER;
163         arg.integer.value = sleep_state;
164
165         acpi_evaluate_object (NULL, "\\_PTS", &arg_list, NULL);
166         acpi_evaluate_object (NULL, "\\_GTS", &arg_list, NULL);
167
168         /* clear wake status */
169
170         acpi_hw_register_bit_access (ACPI_WRITE, ACPI_MTX_LOCK, WAK_STS, 1);
171
172         disable ();
173
174         acpi_hw_disable_non_wakeup_gpes();
175
176         PM1Acontrol = (u16) acpi_hw_register_read (ACPI_MTX_LOCK, PM1_CONTROL);
177
178         ACPI_DEBUG_PRINT ((ACPI_DB_OK, "Entering S%d\n", sleep_state));
179
180         /* mask off SLP_EN and SLP_TYP fields */
181
182         PM1Acontrol &= ~(SLP_TYPE_X_MASK | SLP_EN_MASK);
183         PM1Bcontrol = PM1Acontrol;
184
185         /* mask in SLP_TYP */
186
187         PM1Acontrol |= (type_a << acpi_hw_get_bit_shift (SLP_TYPE_X_MASK));
188         PM1Bcontrol |= (type_b << acpi_hw_get_bit_shift (SLP_TYPE_X_MASK));
189
190         /* write #1: fill in SLP_TYP data */
191
192         acpi_hw_register_write (ACPI_MTX_LOCK, PM1A_CONTROL, PM1Acontrol);
193         acpi_hw_register_write (ACPI_MTX_LOCK, PM1B_CONTROL, PM1Bcontrol);
194
195         /* mask in SLP_EN */
196
197         PM1Acontrol |= (1 << acpi_hw_get_bit_shift (SLP_EN_MASK));
198         PM1Bcontrol |= (1 << acpi_hw_get_bit_shift (SLP_EN_MASK));
199
200         /* flush caches */
201
202         wbinvd();
203
204         /* write #2: SLP_TYP + SLP_EN */
205
206         acpi_hw_register_write (ACPI_MTX_LOCK, PM1A_CONTROL, PM1Acontrol);
207         acpi_hw_register_write (ACPI_MTX_LOCK, PM1B_CONTROL, PM1Bcontrol);
208
209         /*
210          * Wait a second, then try again. This is to get S4/5 to work on all machines.
211          */
212         if (sleep_state > ACPI_STATE_S3) {
213                 acpi_os_stall(1000000);
214
215                 acpi_hw_register_write (ACPI_MTX_LOCK, PM1_CONTROL,
216                         (1 << acpi_hw_get_bit_shift (SLP_EN_MASK)));
217         }
218
219         /* wait until we enter sleep state */
220
221         do {
222                 acpi_os_stall(10000);
223         }
224         while (!acpi_hw_register_bit_access (ACPI_READ, ACPI_MTX_LOCK, WAK_STS));
225
226         acpi_hw_enable_non_wakeup_gpes();
227
228         enable ();
229
230         return_ACPI_STATUS (AE_OK);
231 }
232
233 /******************************************************************************
234  *
235  * FUNCTION:    Acpi_leave_sleep_state
236  *
237  * PARAMETERS:  Sleep_state         - Which sleep state we just exited
238  *
239  * RETURN:      Status
240  *
241  * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
242  *
243  ******************************************************************************/
244
245 acpi_status
246 acpi_leave_sleep_state (
247         u8                  sleep_state)
248 {
249         acpi_object_list    arg_list;
250         acpi_object         arg;
251
252
253         FUNCTION_TRACE ("Acpi_leave_sleep_state");
254
255
256         MEMSET (&arg_list, 0, sizeof(arg_list));
257         arg_list.count = 1;
258         arg_list.pointer = &arg;
259
260         MEMSET (&arg, 0, sizeof(arg));
261         arg.type = ACPI_TYPE_INTEGER;
262         arg.integer.value = sleep_state;
263
264         acpi_evaluate_object (NULL, "\\_BFS", &arg_list, NULL);
265         acpi_evaluate_object (NULL, "\\_WAK", &arg_list, NULL);
266
267         /* _WAK returns stuff - do we want to look at it? */
268
269         acpi_hw_enable_non_wakeup_gpes();
270
271         return_ACPI_STATUS (AE_OK);
272 }