hwsleep.c 12.5 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8

/******************************************************************************
 *
 * Name: hwsleep.c - ACPI Hardware Sleep/Wake Interface
 *
 *****************************************************************************/

/*
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
 * Copyright (C) 2000 - 2003, R. Byron Moore
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer,
 *    without modification.
 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
 *    substantially similar to the "NO WARRANTY" disclaimer below
 *    ("Disclaimer") and any redistribution must be conditioned upon
 *    including a substantially similar Disclaimer requirement for further
 *    binary redistribution.
 * 3. Neither the names of the above-listed copyright holders nor the names
 *    of any contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * Alternatively, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") version 2 as published by the Free
 * Software Foundation.
 *
 * NO WARRANTY
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
Linus Torvalds's avatar
Linus Torvalds committed
43 44
 */

45
#include <acpi/acpi.h>
Linus Torvalds's avatar
Linus Torvalds committed
46

Linus Torvalds's avatar
Linus Torvalds committed
47
#define _COMPONENT          ACPI_HARDWARE
Andy Grover's avatar
Andy Grover committed
48
	 ACPI_MODULE_NAME    ("hwsleep")
Linus Torvalds's avatar
Linus Torvalds committed
49 50 51 52


/******************************************************************************
 *
53
 * FUNCTION:    acpi_set_firmware_waking_vector
Linus Torvalds's avatar
Linus Torvalds committed
54
 *
55
 * PARAMETERS:  physical_address    - Physical address of ACPI real mode
Linus Torvalds's avatar
Linus Torvalds committed
56 57
 *                                    entry point.
 *
Andy Grover's avatar
Andy Grover committed
58
 * RETURN:      Status
Linus Torvalds's avatar
Linus Torvalds committed
59
 *
60
 * DESCRIPTION: access function for d_firmware_waking_vector field in FACS
Linus Torvalds's avatar
Linus Torvalds committed
61 62 63
 *
 ******************************************************************************/

Linus Torvalds's avatar
Linus Torvalds committed
64
acpi_status
Linus Torvalds's avatar
Linus Torvalds committed
65
acpi_set_firmware_waking_vector (
66
	acpi_physical_address physical_address)
Linus Torvalds's avatar
Linus Torvalds committed
67 68
{

69
	ACPI_FUNCTION_TRACE ("acpi_set_firmware_waking_vector");
Linus Torvalds's avatar
Linus Torvalds committed
70 71 72 73


	/* Set the vector */

Andy Grover's avatar
Andy Grover committed
74
	if (acpi_gbl_common_fACS.vector_width == 32) {
75 76
		*(ACPI_CAST_PTR (u32, acpi_gbl_common_fACS.firmware_waking_vector))
				= (u32) physical_address;
Linus Torvalds's avatar
Linus Torvalds committed
77 78
	}
	else {
79 80
		*acpi_gbl_common_fACS.firmware_waking_vector
				= physical_address;
Linus Torvalds's avatar
Linus Torvalds committed
81 82
	}

Linus Torvalds's avatar
Linus Torvalds committed
83
	return_ACPI_STATUS (AE_OK);
Linus Torvalds's avatar
Linus Torvalds committed
84 85 86 87 88
}


/******************************************************************************
 *
89
 * FUNCTION:    acpi_get_firmware_waking_vector
Linus Torvalds's avatar
Linus Torvalds committed
90
 *
91 92
 * PARAMETERS:  *physical_address   - Output buffer where contents of
 *                                    the firmware_waking_vector field of
Linus Torvalds's avatar
Linus Torvalds committed
93 94 95 96
 *                                    the FACS will be stored.
 *
 * RETURN:      Status
 *
97
 * DESCRIPTION: Access function for firmware_waking_vector field in FACS
Linus Torvalds's avatar
Linus Torvalds committed
98 99 100
 *
 ******************************************************************************/

Linus Torvalds's avatar
Linus Torvalds committed
101
acpi_status
Linus Torvalds's avatar
Linus Torvalds committed
102
acpi_get_firmware_waking_vector (
103
	acpi_physical_address *physical_address)
Linus Torvalds's avatar
Linus Torvalds committed
104 105
{

106
	ACPI_FUNCTION_TRACE ("acpi_get_firmware_waking_vector");
Linus Torvalds's avatar
Linus Torvalds committed
107

Linus Torvalds's avatar
Linus Torvalds committed
108 109

	if (!physical_address) {
Linus Torvalds's avatar
Linus Torvalds committed
110
		return_ACPI_STATUS (AE_BAD_PARAMETER);
Linus Torvalds's avatar
Linus Torvalds committed
111 112 113 114
	}

	/* Get the vector */

Andy Grover's avatar
Andy Grover committed
115
	if (acpi_gbl_common_fACS.vector_width == 32) {
116
		*physical_address = (acpi_physical_address)
117
			*(ACPI_CAST_PTR (u32, acpi_gbl_common_fACS.firmware_waking_vector));
Linus Torvalds's avatar
Linus Torvalds committed
118 119
	}
	else {
120 121
		*physical_address =
			*acpi_gbl_common_fACS.firmware_waking_vector;
Linus Torvalds's avatar
Linus Torvalds committed
122 123
	}

Linus Torvalds's avatar
Linus Torvalds committed
124
	return_ACPI_STATUS (AE_OK);
Linus Torvalds's avatar
Linus Torvalds committed
125 126
}

Andy Grover's avatar
Andy Grover committed
127

Linus Torvalds's avatar
Linus Torvalds committed
128 129
/******************************************************************************
 *
130
 * FUNCTION:    acpi_enter_sleep_state_prep
Linus Torvalds's avatar
Linus Torvalds committed
131
 *
132
 * PARAMETERS:  sleep_state         - Which sleep state to enter
Linus Torvalds's avatar
Linus Torvalds committed
133 134 135
 *
 * RETURN:      Status
 *
Andy Grover's avatar
Andy Grover committed
136 137 138 139
 * DESCRIPTION: Prepare to enter a system sleep state (see ACPI 2.0 spec p 231)
 *              This function must execute with interrupts enabled.
 *              We break sleeping into 2 stages so that OSPM can handle
 *              various OS-specific tasks between the two steps.
Linus Torvalds's avatar
Linus Torvalds committed
140 141 142
 *
 ******************************************************************************/

Linus Torvalds's avatar
Linus Torvalds committed
143
acpi_status
Andy Grover's avatar
Andy Grover committed
144
acpi_enter_sleep_state_prep (
145
	u8                          sleep_state)
Linus Torvalds's avatar
Linus Torvalds committed
146
{
147 148 149
	acpi_status                 status;
	struct acpi_object_list     arg_list;
	union acpi_object           arg;
Linus Torvalds's avatar
Linus Torvalds committed
150

Linus Torvalds's avatar
Linus Torvalds committed
151

152
	ACPI_FUNCTION_TRACE ("acpi_enter_sleep_state_prep");
Linus Torvalds's avatar
Linus Torvalds committed
153 154


Linus Torvalds's avatar
Linus Torvalds committed
155 156 157
	/*
	 * _PSW methods could be run here to enable wake-on keyboard, LAN, etc.
	 */
158
	status = acpi_get_sleep_type_data (sleep_state,
Andy Grover's avatar
Andy Grover committed
159 160 161
			  &acpi_gbl_sleep_type_a, &acpi_gbl_sleep_type_b);
	if (ACPI_FAILURE (status)) {
		return_ACPI_STATUS (status);
Linus Torvalds's avatar
Linus Torvalds committed
162 163
	}

Andy Grover's avatar
Andy Grover committed
164
	/* Setup parameter object */
Linus Torvalds's avatar
Linus Torvalds committed
165

Linus Torvalds's avatar
Linus Torvalds committed
166 167 168 169 170 171
	arg_list.count = 1;
	arg_list.pointer = &arg;

	arg.type = ACPI_TYPE_INTEGER;
	arg.integer.value = sleep_state;

Andy Grover's avatar
Andy Grover committed
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
	/* Run the _PTS and _GTS methods */

	status = acpi_evaluate_object (NULL, "\\_PTS", &arg_list, NULL);
	if (ACPI_FAILURE (status) && status != AE_NOT_FOUND) {
		return_ACPI_STATUS (status);
	}

	status = acpi_evaluate_object (NULL, "\\_GTS", &arg_list, NULL);
	if (ACPI_FAILURE (status) && status != AE_NOT_FOUND) {
		return_ACPI_STATUS (status);
	}

	return_ACPI_STATUS (AE_OK);
}


/******************************************************************************
 *
190
 * FUNCTION:    acpi_enter_sleep_state
Andy Grover's avatar
Andy Grover committed
191
 *
192
 * PARAMETERS:  sleep_state         - Which sleep state to enter
Andy Grover's avatar
Andy Grover committed
193 194 195 196 197 198 199 200 201 202
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Enter a system sleep state (see ACPI 2.0 spec p 231)
 *              THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
 *
 ******************************************************************************/

acpi_status
acpi_enter_sleep_state (
203
	u8                              sleep_state)
Andy Grover's avatar
Andy Grover committed
204
{
205 206 207 208 209 210
	u32                             PM1Acontrol;
	u32                             PM1Bcontrol;
	struct acpi_bit_register_info   *sleep_type_reg_info;
	struct acpi_bit_register_info   *sleep_enable_reg_info;
	u32                             in_value;
	acpi_status                     status;
Andy Grover's avatar
Andy Grover committed
211 212


213
	ACPI_FUNCTION_TRACE ("acpi_enter_sleep_state");
Andy Grover's avatar
Andy Grover committed
214 215 216 217


	if ((acpi_gbl_sleep_type_a > ACPI_SLEEP_TYPE_MAX) ||
		(acpi_gbl_sleep_type_b > ACPI_SLEEP_TYPE_MAX)) {
218
		ACPI_REPORT_ERROR (("Sleep values out of range: A=%X B=%X\n",
Andy Grover's avatar
Andy Grover committed
219 220 221 222
			acpi_gbl_sleep_type_a, acpi_gbl_sleep_type_b));
		return_ACPI_STATUS (AE_AML_OPERAND_VALUE);
	}

Linus Torvalds's avatar
Linus Torvalds committed
223

Andy Grover's avatar
Andy Grover committed
224 225
	sleep_type_reg_info = acpi_hw_get_bit_register_info (ACPI_BITREG_SLEEP_TYPE_A);
	sleep_enable_reg_info = acpi_hw_get_bit_register_info (ACPI_BITREG_SLEEP_ENABLE);
Linus Torvalds's avatar
Linus Torvalds committed
226

Andy Grover's avatar
Andy Grover committed
227
	/* Clear wake status */
Linus Torvalds's avatar
Linus Torvalds committed
228

229 230 231 232
	status = acpi_set_register (ACPI_BITREG_WAKE_STATUS, 1, ACPI_MTX_LOCK);
	if (ACPI_FAILURE (status)) {
		return_ACPI_STATUS (status);
	}
Andy Grover's avatar
Andy Grover committed
233

234 235 236 237
	status = acpi_hw_clear_acpi_status();
	if (ACPI_FAILURE (status)) {
		return_ACPI_STATUS (status);
	}
Linus Torvalds's avatar
Linus Torvalds committed
238

239 240 241 242 243 244 245 246 247 248 249
	/* Disable BM arbitration */

	status = acpi_set_register (ACPI_BITREG_ARB_DISABLE, 1, ACPI_MTX_LOCK);
	if (ACPI_FAILURE (status)) {
		return_ACPI_STATUS (status);
	}

	status = acpi_hw_disable_non_wakeup_gpes();
	if (ACPI_FAILURE (status)) {
		return_ACPI_STATUS (status);
	}
Linus Torvalds's avatar
Linus Torvalds committed
250

Andy Grover's avatar
Andy Grover committed
251
	/* Get current value of PM1A control */
Linus Torvalds's avatar
Linus Torvalds committed
252

253 254 255 256
	status = acpi_hw_register_read (ACPI_MTX_LOCK, ACPI_REGISTER_PM1_CONTROL, &PM1Acontrol);
	if (ACPI_FAILURE (status)) {
		return_ACPI_STATUS (status);
	}
257
	ACPI_DEBUG_PRINT ((ACPI_DB_INIT, "Entering sleep state [S%d]\n", sleep_state));
Linus Torvalds's avatar
Linus Torvalds committed
258

Andy Grover's avatar
Andy Grover committed
259
	/* Clear SLP_EN and SLP_TYP fields */
Linus Torvalds's avatar
Linus Torvalds committed
260

Andy Grover's avatar
Andy Grover committed
261
	PM1Acontrol &= ~(sleep_type_reg_info->access_bit_mask | sleep_enable_reg_info->access_bit_mask);
Linus Torvalds's avatar
Linus Torvalds committed
262
	PM1Bcontrol = PM1Acontrol;
Linus Torvalds's avatar
Linus Torvalds committed
263

Andy Grover's avatar
Andy Grover committed
264
	/* Insert SLP_TYP bits */
Linus Torvalds's avatar
Linus Torvalds committed
265

Andy Grover's avatar
Andy Grover committed
266 267
	PM1Acontrol |= (acpi_gbl_sleep_type_a << sleep_type_reg_info->bit_position);
	PM1Bcontrol |= (acpi_gbl_sleep_type_b << sleep_type_reg_info->bit_position);
Linus Torvalds's avatar
Linus Torvalds committed
268

Andy Grover's avatar
Andy Grover committed
269
	/* Write #1: fill in SLP_TYP data */
Linus Torvalds's avatar
Linus Torvalds committed
270

271 272 273 274 275 276 277 278 279
	status = acpi_hw_register_write (ACPI_MTX_LOCK, ACPI_REGISTER_PM1A_CONTROL, PM1Acontrol);
	if (ACPI_FAILURE (status)) {
		return_ACPI_STATUS (status);
	}

	status = acpi_hw_register_write (ACPI_MTX_LOCK, ACPI_REGISTER_PM1B_CONTROL, PM1Bcontrol);
	if (ACPI_FAILURE (status)) {
		return_ACPI_STATUS (status);
	}
Linus Torvalds's avatar
Linus Torvalds committed
280

Andy Grover's avatar
Andy Grover committed
281
	/* Insert SLP_ENABLE bit */
Linus Torvalds's avatar
Linus Torvalds committed
282

Andy Grover's avatar
Andy Grover committed
283 284
	PM1Acontrol |= sleep_enable_reg_info->access_bit_mask;
	PM1Bcontrol |= sleep_enable_reg_info->access_bit_mask;
Linus Torvalds's avatar
Linus Torvalds committed
285

Andy Grover's avatar
Andy Grover committed
286
	/* Write #2: SLP_TYP + SLP_EN */
Linus Torvalds's avatar
Linus Torvalds committed
287

288 289 290 291 292 293 294 295 296 297 298
	ACPI_FLUSH_CPU_CACHE ();

	status = acpi_hw_register_write (ACPI_MTX_LOCK, ACPI_REGISTER_PM1A_CONTROL, PM1Acontrol);
	if (ACPI_FAILURE (status)) {
		return_ACPI_STATUS (status);
	}

	status = acpi_hw_register_write (ACPI_MTX_LOCK, ACPI_REGISTER_PM1B_CONTROL, PM1Bcontrol);
	if (ACPI_FAILURE (status)) {
		return_ACPI_STATUS (status);
	}
Linus Torvalds's avatar
Linus Torvalds committed
299 300 301 302 303

	/*
	 * Wait a second, then try again. This is to get S4/5 to work on all machines.
	 */
	if (sleep_state > ACPI_STATE_S3) {
304 305 306 307 308 309 310 311 312 313 314 315
		/*
		 * We wait so long to allow chipsets that poll this reg very slowly to
		 * still read the right value. Ideally, this entire block would go
		 * away entirely.
		 */
		acpi_os_stall (10000000);

		status = acpi_hw_register_write (ACPI_MTX_LOCK, ACPI_REGISTER_PM1_CONTROL,
				 sleep_enable_reg_info->access_bit_mask);
		if (ACPI_FAILURE (status)) {
			return_ACPI_STATUS (status);
		}
Linus Torvalds's avatar
Linus Torvalds committed
316 317
	}

Andy Grover's avatar
Andy Grover committed
318
	/* Wait until we enter sleep state */
Linus Torvalds's avatar
Linus Torvalds committed
319

320 321 322 323 324 325
	do {
		status = acpi_get_register (ACPI_BITREG_WAKE_STATUS, &in_value, ACPI_MTX_LOCK);
		if (ACPI_FAILURE (status)) {
			return_ACPI_STATUS (status);
		}

Andy Grover's avatar
Andy Grover committed
326
		/* Spin until we wake */
327 328

	} while (!in_value);
Linus Torvalds's avatar
Linus Torvalds committed
329

330 331 332 333 334
	status = acpi_set_register (ACPI_BITREG_ARB_DISABLE, 0, ACPI_MTX_LOCK);
	if (ACPI_FAILURE (status)) {
		return_ACPI_STATUS (status);
	}

Linus Torvalds's avatar
Linus Torvalds committed
335 336 337
	return_ACPI_STATUS (AE_OK);
}

338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382

/******************************************************************************
 *
 * FUNCTION:    acpi_enter_sleep_state_s4bios
 *
 * PARAMETERS:  None
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Perform a S4 bios request.
 *              THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
 *
 ******************************************************************************/

acpi_status
acpi_enter_sleep_state_s4bios (
	void)
{
	u32                             in_value;
	acpi_status                     status;


	ACPI_FUNCTION_TRACE ("acpi_enter_sleep_state_s4bios");

	acpi_set_register (ACPI_BITREG_WAKE_STATUS, 1, ACPI_MTX_LOCK);
	acpi_hw_clear_acpi_status();

	acpi_hw_disable_non_wakeup_gpes();

	ACPI_FLUSH_CPU_CACHE();

	status = acpi_os_write_port (acpi_gbl_FADT->smi_cmd, (acpi_integer) acpi_gbl_FADT->S4bios_req, 8);

	do {
		acpi_os_stall(1000);
		status = acpi_get_register (ACPI_BITREG_WAKE_STATUS, &in_value, ACPI_MTX_LOCK);
		if (ACPI_FAILURE (status)) {
			return_ACPI_STATUS (status);
		}
	} while (!in_value);

	return_ACPI_STATUS (AE_OK);
}


Linus Torvalds's avatar
Linus Torvalds committed
383 384
/******************************************************************************
 *
385
 * FUNCTION:    acpi_leave_sleep_state
Linus Torvalds's avatar
Linus Torvalds committed
386
 *
387
 * PARAMETERS:  sleep_state         - Which sleep state we just exited
Linus Torvalds's avatar
Linus Torvalds committed
388 389 390 391 392 393 394 395 396
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
 *
 ******************************************************************************/

acpi_status
acpi_leave_sleep_state (
397
	u8                          sleep_state)
Linus Torvalds's avatar
Linus Torvalds committed
398
{
399 400 401
	struct acpi_object_list     arg_list;
	union acpi_object           arg;
	acpi_status                 status;
Linus Torvalds's avatar
Linus Torvalds committed
402 403


404
	ACPI_FUNCTION_TRACE ("acpi_leave_sleep_state");
Linus Torvalds's avatar
Linus Torvalds committed
405 406


407
	/* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */
Andy Grover's avatar
Andy Grover committed
408 409 410 411 412

	acpi_gbl_sleep_type_a = ACPI_SLEEP_TYPE_INVALID;

	/* Setup parameter object */

Linus Torvalds's avatar
Linus Torvalds committed
413 414 415 416 417 418
	arg_list.count = 1;
	arg_list.pointer = &arg;

	arg.type = ACPI_TYPE_INTEGER;
	arg.integer.value = sleep_state;

Andy Grover's avatar
Andy Grover committed
419 420 421 422 423 424 425 426 427 428 429
	/* Ignore any errors from these methods */

	status = acpi_evaluate_object (NULL, "\\_BFS", &arg_list, NULL);
	if (ACPI_FAILURE (status) && status != AE_NOT_FOUND) {
		ACPI_REPORT_ERROR (("Method _BFS failed, %s\n", acpi_format_exception (status)));
	}

	status = acpi_evaluate_object (NULL, "\\_WAK", &arg_list, NULL);
	if (ACPI_FAILURE (status) && status != AE_NOT_FOUND) {
		ACPI_REPORT_ERROR (("Method _WAK failed, %s\n", acpi_format_exception (status)));
	}
Linus Torvalds's avatar
Linus Torvalds committed
430 431

	/* _WAK returns stuff - do we want to look at it? */
Linus Torvalds's avatar
Linus Torvalds committed
432

433 434 435 436
	status = acpi_hw_enable_non_wakeup_gpes();
	if (ACPI_FAILURE (status)) {
		return_ACPI_STATUS (status);
	}
Linus Torvalds's avatar
Linus Torvalds committed
437

438 439 440 441
	/* Disable BM arbitration */
	status = acpi_set_register (ACPI_BITREG_ARB_DISABLE, 0, ACPI_MTX_LOCK);

	return_ACPI_STATUS (status);
Linus Torvalds's avatar
Linus Torvalds committed
442
}