hwregs.c 11.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9

/*******************************************************************************
 *
 * Module Name: hwregs - Read/write access functions for the various ACPI
 *                       control and status registers.
 *
 ******************************************************************************/

/*
L
Len Brown 已提交
10
 * Copyright (C) 2000 - 2008, Intel Corp.
L
Linus Torvalds 已提交
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 43 44 45 46
 * 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.
 */

#include <acpi/acpi.h>
L
Len Brown 已提交
47 48 49
#include "accommon.h"
#include "acnamesp.h"
#include "acevents.h"
L
Linus Torvalds 已提交
50 51

#define _COMPONENT          ACPI_HARDWARE
L
Len Brown 已提交
52
ACPI_MODULE_NAME("hwregs")
L
Linus Torvalds 已提交
53

54 55 56 57 58 59 60 61 62 63 64
/* Local Prototypes */
static acpi_status
acpi_hw_read_multiple(u32 *value,
		      struct acpi_generic_address *register_a,
		      struct acpi_generic_address *register_b);

static acpi_status
acpi_hw_write_multiple(u32 value,
		       struct acpi_generic_address *register_a,
		       struct acpi_generic_address *register_b);

L
Linus Torvalds 已提交
65 66 67 68
/*******************************************************************************
 *
 * FUNCTION:    acpi_hw_clear_acpi_status
 *
69
 * PARAMETERS:  None
L
Linus Torvalds 已提交
70
 *
71
 * RETURN:      Status
L
Linus Torvalds 已提交
72 73 74 75 76
 *
 * DESCRIPTION: Clears all fixed and general purpose status bits
 *              THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
 *
 ******************************************************************************/
77

78
acpi_status acpi_hw_clear_acpi_status(void)
L
Linus Torvalds 已提交
79
{
L
Len Brown 已提交
80
	acpi_status status;
B
Bob Moore 已提交
81
	acpi_cpu_flags lock_flags = 0;
L
Linus Torvalds 已提交
82

B
Bob Moore 已提交
83
	ACPI_FUNCTION_TRACE(hw_clear_acpi_status);
L
Linus Torvalds 已提交
84

L
Len Brown 已提交
85 86
	ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %04X\n",
			  ACPI_BITMASK_ALL_FIXED_STATUS,
87
			  (u16) acpi_gbl_xpm1a_status.address));
L
Linus Torvalds 已提交
88

B
Bob Moore 已提交
89
	lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock);
L
Linus Torvalds 已提交
90

91 92
	/* Clear the fixed events */

93
	status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS,
L
Len Brown 已提交
94 95
					ACPI_BITMASK_ALL_FIXED_STATUS);
	if (ACPI_FAILURE(status)) {
L
Linus Torvalds 已提交
96 97 98
		goto unlock_and_exit;
	}

99
	/* Write PM1B register if present */
L
Linus Torvalds 已提交
100

101
	if (acpi_gbl_xpm1b_status.address) {
102
		status = acpi_write(ACPI_BITMASK_ALL_FIXED_STATUS,
103
				    &acpi_gbl_xpm1b_status);
L
Len Brown 已提交
104
		if (ACPI_FAILURE(status)) {
L
Linus Torvalds 已提交
105 106 107 108 109 110
			goto unlock_and_exit;
		}
	}

	/* Clear the GPE Bits in all GPE registers in all GPE blocks */

111
	status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block, NULL);
L
Linus Torvalds 已提交
112

L
Len Brown 已提交
113
      unlock_and_exit:
B
Bob Moore 已提交
114
	acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags);
L
Len Brown 已提交
115
	return_ACPI_STATUS(status);
L
Linus Torvalds 已提交
116 117 118 119 120 121 122 123
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_hw_get_register_bit_mask
 *
 * PARAMETERS:  register_id         - Index of ACPI Register to access
 *
R
Robert Moore 已提交
124
 * RETURN:      The bitmask to be used when accessing the register
L
Linus Torvalds 已提交
125
 *
R
Robert Moore 已提交
126
 * DESCRIPTION: Map register_id into a register bitmask.
L
Linus Torvalds 已提交
127 128
 *
 ******************************************************************************/
129

L
Len Brown 已提交
130
struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id)
L
Linus Torvalds 已提交
131
{
B
Bob Moore 已提交
132
	ACPI_FUNCTION_ENTRY();
L
Linus Torvalds 已提交
133 134

	if (register_id > ACPI_BITREG_MAX) {
B
Bob Moore 已提交
135
		ACPI_ERROR((AE_INFO, "Invalid BitRegister ID: %X",
B
Bob Moore 已提交
136
			    register_id));
L
Linus Torvalds 已提交
137 138 139 140 141 142 143 144 145 146
		return (NULL);
	}

	return (&acpi_gbl_bit_register_info[register_id]);
}

/******************************************************************************
 *
 * FUNCTION:    acpi_hw_register_read
 *
147
 * PARAMETERS:  register_id         - ACPI Register ID
R
Robert Moore 已提交
148
 *              return_value        - Where the register value is returned
L
Linus Torvalds 已提交
149 150 151
 *
 * RETURN:      Status and the value read.
 *
B
Bob Moore 已提交
152
 * DESCRIPTION: Read from the specified ACPI register
L
Linus Torvalds 已提交
153 154 155
 *
 ******************************************************************************/
acpi_status
156
acpi_hw_register_read(u32 register_id, u32 * return_value)
L
Linus Torvalds 已提交
157
{
158
	u32 value = 0;
L
Len Brown 已提交
159
	acpi_status status;
L
Linus Torvalds 已提交
160

B
Bob Moore 已提交
161
	ACPI_FUNCTION_TRACE(hw_register_read);
L
Linus Torvalds 已提交
162 163

	switch (register_id) {
164
	case ACPI_REGISTER_PM1_STATUS:	/* PM1 A/B: 16-bit access each */
L
Linus Torvalds 已提交
165

166 167 168
		status = acpi_hw_read_multiple(&value,
					       &acpi_gbl_xpm1a_status,
					       &acpi_gbl_xpm1b_status);
L
Linus Torvalds 已提交
169 170
		break;

171
	case ACPI_REGISTER_PM1_ENABLE:	/* PM1 A/B: 16-bit access each */
L
Linus Torvalds 已提交
172

173 174 175
		status = acpi_hw_read_multiple(&value,
					       &acpi_gbl_xpm1a_enable,
					       &acpi_gbl_xpm1b_enable);
L
Linus Torvalds 已提交
176 177
		break;

178
	case ACPI_REGISTER_PM1_CONTROL:	/* PM1 A/B: 16-bit access each */
L
Linus Torvalds 已提交
179

180 181 182 183 184
		status = acpi_hw_read_multiple(&value,
					       &acpi_gbl_FADT.
					       xpm1a_control_block,
					       &acpi_gbl_FADT.
					       xpm1b_control_block);
L
Linus Torvalds 已提交
185 186
		break;

L
Len Brown 已提交
187
	case ACPI_REGISTER_PM2_CONTROL:	/* 8-bit access */
L
Linus Torvalds 已提交
188

189
		status = acpi_read(&value, &acpi_gbl_FADT.xpm2_control_block);
L
Linus Torvalds 已提交
190 191
		break;

L
Len Brown 已提交
192
	case ACPI_REGISTER_PM_TIMER:	/* 32-bit access */
L
Linus Torvalds 已提交
193

194
		status = acpi_read(&value, &acpi_gbl_FADT.xpm_timer_block);
L
Linus Torvalds 已提交
195 196
		break;

L
Len Brown 已提交
197
	case ACPI_REGISTER_SMI_COMMAND_BLOCK:	/* 8-bit access */
L
Linus Torvalds 已提交
198

199
		status =
200
		    acpi_os_read_port(acpi_gbl_FADT.smi_command, &value, 8);
L
Linus Torvalds 已提交
201 202 203
		break;

	default:
B
Bob Moore 已提交
204
		ACPI_ERROR((AE_INFO, "Unknown Register ID: %X", register_id));
L
Linus Torvalds 已提交
205 206 207 208
		status = AE_BAD_PARAMETER;
		break;
	}

L
Len Brown 已提交
209
	if (ACPI_SUCCESS(status)) {
210
		*return_value = value;
L
Linus Torvalds 已提交
211 212
	}

L
Len Brown 已提交
213
	return_ACPI_STATUS(status);
L
Linus Torvalds 已提交
214 215 216 217 218 219
}

/******************************************************************************
 *
 * FUNCTION:    acpi_hw_register_write
 *
220
 * PARAMETERS:  register_id         - ACPI Register ID
L
Linus Torvalds 已提交
221 222 223 224
 *              Value               - The value to write
 *
 * RETURN:      Status
 *
B
Bob Moore 已提交
225 226 227 228 229 230 231 232 233 234 235 236 237 238
 * DESCRIPTION: Write to the specified ACPI register
 *
 * NOTE: In accordance with the ACPI specification, this function automatically
 * preserves the value of the following bits, meaning that these bits cannot be
 * changed via this interface:
 *
 * PM1_CONTROL[0] = SCI_EN
 * PM1_CONTROL[9]
 * PM1_STATUS[11]
 *
 * ACPI References:
 * 1) Hardware Ignored Bits: When software writes to a register with ignored
 *      bit fields, it preserves the ignored bit fields
 * 2) SCI_EN: OSPM always preserves this bit position
L
Linus Torvalds 已提交
239 240 241
 *
 ******************************************************************************/

242
acpi_status acpi_hw_register_write(u32 register_id, u32 value)
L
Linus Torvalds 已提交
243
{
L
Len Brown 已提交
244
	acpi_status status;
B
Bob Moore 已提交
245
	u32 read_value;
L
Linus Torvalds 已提交
246

B
Bob Moore 已提交
247
	ACPI_FUNCTION_TRACE(hw_register_write);
L
Linus Torvalds 已提交
248 249

	switch (register_id) {
250
	case ACPI_REGISTER_PM1_STATUS:	/* PM1 A/B: 16-bit access each */
L
Linus Torvalds 已提交
251

B
Bob Moore 已提交
252 253
		/* Perform a read first to preserve certain bits (per ACPI spec) */

254 255 256
		status = acpi_hw_read_multiple(&read_value,
					       &acpi_gbl_xpm1a_status,
					       &acpi_gbl_xpm1b_status);
B
Bob Moore 已提交
257
		if (ACPI_FAILURE(status)) {
258
			goto exit;
B
Bob Moore 已提交
259 260 261 262 263 264 265 266 267
		}

		/* Insert the bits to be preserved */

		ACPI_INSERT_BITS(value, ACPI_PM1_STATUS_PRESERVED_BITS,
				 read_value);

		/* Now we can write the data */

268 269 270
		status = acpi_hw_write_multiple(value,
						&acpi_gbl_xpm1a_status,
						&acpi_gbl_xpm1b_status);
L
Linus Torvalds 已提交
271 272
		break;

273
	case ACPI_REGISTER_PM1_ENABLE:	/* PM1 A/B: 16-bit access */
L
Linus Torvalds 已提交
274

275 276 277
		status = acpi_hw_write_multiple(value,
						&acpi_gbl_xpm1a_enable,
						&acpi_gbl_xpm1b_enable);
L
Linus Torvalds 已提交
278 279
		break;

280
	case ACPI_REGISTER_PM1_CONTROL:	/* PM1 A/B: 16-bit access each */
L
Linus Torvalds 已提交
281

B
Bob Moore 已提交
282 283
		/*
		 * Perform a read first to preserve certain bits (per ACPI spec)
284
		 * Note: This includes SCI_EN, we never want to change this bit
B
Bob Moore 已提交
285
		 */
286 287 288 289 290
		status = acpi_hw_read_multiple(&read_value,
					       &acpi_gbl_FADT.
					       xpm1a_control_block,
					       &acpi_gbl_FADT.
					       xpm1b_control_block);
B
Bob Moore 已提交
291
		if (ACPI_FAILURE(status)) {
292
			goto exit;
B
Bob Moore 已提交
293 294 295 296 297 298 299 300 301
		}

		/* Insert the bits to be preserved */

		ACPI_INSERT_BITS(value, ACPI_PM1_CONTROL_PRESERVED_BITS,
				 read_value);

		/* Now we can write the data */

302 303 304 305 306
		status = acpi_hw_write_multiple(value,
						&acpi_gbl_FADT.
						xpm1a_control_block,
						&acpi_gbl_FADT.
						xpm1b_control_block);
L
Linus Torvalds 已提交
307 308
		break;

L
Len Brown 已提交
309
	case ACPI_REGISTER_PM1A_CONTROL:	/* 16-bit access */
L
Linus Torvalds 已提交
310

311
		status = acpi_write(value, &acpi_gbl_FADT.xpm1a_control_block);
L
Linus Torvalds 已提交
312 313
		break;

L
Len Brown 已提交
314
	case ACPI_REGISTER_PM1B_CONTROL:	/* 16-bit access */
L
Linus Torvalds 已提交
315

316
		status = acpi_write(value, &acpi_gbl_FADT.xpm1b_control_block);
L
Linus Torvalds 已提交
317 318
		break;

L
Len Brown 已提交
319
	case ACPI_REGISTER_PM2_CONTROL:	/* 8-bit access */
L
Linus Torvalds 已提交
320

321
		status = acpi_write(value, &acpi_gbl_FADT.xpm2_control_block);
L
Linus Torvalds 已提交
322 323
		break;

L
Len Brown 已提交
324
	case ACPI_REGISTER_PM_TIMER:	/* 32-bit access */
L
Linus Torvalds 已提交
325

326
		status = acpi_write(value, &acpi_gbl_FADT.xpm_timer_block);
L
Linus Torvalds 已提交
327 328
		break;

L
Len Brown 已提交
329
	case ACPI_REGISTER_SMI_COMMAND_BLOCK:	/* 8-bit access */
L
Linus Torvalds 已提交
330 331 332

		/* SMI_CMD is currently always in IO space */

333 334
		status =
		    acpi_os_write_port(acpi_gbl_FADT.smi_command, value, 8);
L
Linus Torvalds 已提交
335 336 337
		break;

	default:
338
		ACPI_ERROR((AE_INFO, "Unknown Register ID: %X", register_id));
L
Linus Torvalds 已提交
339 340 341 342
		status = AE_BAD_PARAMETER;
		break;
	}

343
      exit:
L
Len Brown 已提交
344
	return_ACPI_STATUS(status);
L
Linus Torvalds 已提交
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 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430

/******************************************************************************
 *
 * FUNCTION:    acpi_hw_read_multiple
 *
 * PARAMETERS:  Value               - Where the register value is returned
 *              register_a           - First ACPI register (required)
 *              register_b           - Second ACPI register (optional)
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Read from the specified two-part ACPI register (such as PM1 A/B)
 *
 ******************************************************************************/

static acpi_status
acpi_hw_read_multiple(u32 *value,
		      struct acpi_generic_address *register_a,
		      struct acpi_generic_address *register_b)
{
	u32 value_a = 0;
	u32 value_b = 0;
	acpi_status status;

	/* The first register is always required */

	status = acpi_read(&value_a, register_a);
	if (ACPI_FAILURE(status)) {
		return (status);
	}

	/* Second register is optional */

	if (register_b->address) {
		status = acpi_read(&value_b, register_b);
		if (ACPI_FAILURE(status)) {
			return (status);
		}
	}

	/* Shift the B bits above the A bits */

	*value = value_a | (value_b << register_a->bit_width);
	return (AE_OK);
}

/******************************************************************************
 *
 * FUNCTION:    acpi_hw_write_multiple
 *
 * PARAMETERS:  Value               - The value to write
 *              register_a           - First ACPI register (required)
 *              register_b           - Second ACPI register (optional)
 *
 * RETURN:      Status
 *
 * DESCRIPTION: Write to the specified two-part ACPI register (such as PM1 A/B)
 *
 ******************************************************************************/

static acpi_status
acpi_hw_write_multiple(u32 value,
		       struct acpi_generic_address *register_a,
		       struct acpi_generic_address *register_b)
{
	acpi_status status;

	/* The first register is always required */

	status = acpi_write(value, register_a);
	if (ACPI_FAILURE(status)) {
		return (status);
	}

	/* Second register is optional */

	if (register_b->address) {

		/* Normalize the B bits before write */

		status = acpi_write(value >> register_a->bit_width, register_b);
	}

	return (status);
}