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
48acpi_status
49acpi_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
89acpi_status
90acpi_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
131acpi_status
132acpi_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
245acpi_status
246acpi_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}
273