Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | |
2 | /******************************************************************************* | |
3 | * | |
4 | * Module Name: hwregs - Read/write access functions for the various ACPI | |
5 | * control and status registers. | |
6 | * | |
7 | ******************************************************************************/ | |
8 | ||
9 | /* | |
75a44ce0 | 10 | * Copyright (C) 2000 - 2008, Intel Corp. |
1da177e4 LT |
11 | * All rights reserved. |
12 | * | |
13 | * Redistribution and use in source and binary forms, with or without | |
14 | * modification, are permitted provided that the following conditions | |
15 | * are met: | |
16 | * 1. Redistributions of source code must retain the above copyright | |
17 | * notice, this list of conditions, and the following disclaimer, | |
18 | * without modification. | |
19 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer | |
20 | * substantially similar to the "NO WARRANTY" disclaimer below | |
21 | * ("Disclaimer") and any redistribution must be conditioned upon | |
22 | * including a substantially similar Disclaimer requirement for further | |
23 | * binary redistribution. | |
24 | * 3. Neither the names of the above-listed copyright holders nor the names | |
25 | * of any contributors may be used to endorse or promote products derived | |
26 | * from this software without specific prior written permission. | |
27 | * | |
28 | * Alternatively, this software may be distributed under the terms of the | |
29 | * GNU General Public License ("GPL") version 2 as published by the Free | |
30 | * Software Foundation. | |
31 | * | |
32 | * NO WARRANTY | |
33 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
34 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
35 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR | |
36 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
37 | * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
38 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
39 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
40 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
41 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
42 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
43 | * POSSIBILITY OF SUCH DAMAGES. | |
44 | */ | |
45 | ||
1da177e4 LT |
46 | #include <acpi/acpi.h> |
47 | #include <acpi/acnamesp.h> | |
48 | #include <acpi/acevents.h> | |
49 | ||
50 | #define _COMPONENT ACPI_HARDWARE | |
4be44fcd | 51 | ACPI_MODULE_NAME("hwregs") |
1da177e4 LT |
52 | |
53 | /******************************************************************************* | |
54 | * | |
55 | * FUNCTION: acpi_hw_clear_acpi_status | |
56 | * | |
d8c71b6d | 57 | * PARAMETERS: None |
1da177e4 | 58 | * |
d8c71b6d | 59 | * RETURN: None |
1da177e4 LT |
60 | * |
61 | * DESCRIPTION: Clears all fixed and general purpose status bits | |
62 | * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED | |
63 | * | |
64 | ******************************************************************************/ | |
d8c71b6d | 65 | acpi_status acpi_hw_clear_acpi_status(void) |
1da177e4 | 66 | { |
4be44fcd | 67 | acpi_status status; |
4c90ece2 | 68 | acpi_cpu_flags lock_flags = 0; |
1da177e4 | 69 | |
b229cf92 | 70 | ACPI_FUNCTION_TRACE(hw_clear_acpi_status); |
1da177e4 | 71 | |
4be44fcd LB |
72 | ACPI_DEBUG_PRINT((ACPI_DB_IO, "About to write %04X to %04X\n", |
73 | ACPI_BITMASK_ALL_FIXED_STATUS, | |
f3d2e786 | 74 | (u16) acpi_gbl_FADT.xpm1a_event_block.address)); |
1da177e4 | 75 | |
4c90ece2 | 76 | lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); |
1da177e4 | 77 | |
d30dc9ab | 78 | status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS, |
4be44fcd LB |
79 | ACPI_BITMASK_ALL_FIXED_STATUS); |
80 | if (ACPI_FAILURE(status)) { | |
1da177e4 LT |
81 | goto unlock_and_exit; |
82 | } | |
83 | ||
84 | /* Clear the fixed events */ | |
85 | ||
f3d2e786 | 86 | if (acpi_gbl_FADT.xpm1b_event_block.address) { |
4be44fcd LB |
87 | status = |
88 | acpi_hw_low_level_write(16, ACPI_BITMASK_ALL_FIXED_STATUS, | |
f3d2e786 | 89 | &acpi_gbl_FADT.xpm1b_event_block); |
4be44fcd | 90 | if (ACPI_FAILURE(status)) { |
1da177e4 LT |
91 | goto unlock_and_exit; |
92 | } | |
93 | } | |
94 | ||
95 | /* Clear the GPE Bits in all GPE registers in all GPE blocks */ | |
96 | ||
4be44fcd | 97 | status = acpi_ev_walk_gpe_list(acpi_hw_clear_gpe_block); |
1da177e4 | 98 | |
4be44fcd | 99 | unlock_and_exit: |
4c90ece2 | 100 | acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags); |
4be44fcd | 101 | return_ACPI_STATUS(status); |
1da177e4 LT |
102 | } |
103 | ||
1da177e4 LT |
104 | /******************************************************************************* |
105 | * | |
106 | * FUNCTION: acpi_get_sleep_type_data | |
107 | * | |
108 | * PARAMETERS: sleep_state - Numeric sleep state | |
109 | * *sleep_type_a - Where SLP_TYPa is returned | |
110 | * *sleep_type_b - Where SLP_TYPb is returned | |
111 | * | |
112 | * RETURN: Status - ACPI status | |
113 | * | |
114 | * DESCRIPTION: Obtain the SLP_TYPa and SLP_TYPb values for the requested sleep | |
115 | * state. | |
116 | * | |
117 | ******************************************************************************/ | |
118 | ||
119 | acpi_status | |
4be44fcd | 120 | acpi_get_sleep_type_data(u8 sleep_state, u8 * sleep_type_a, u8 * sleep_type_b) |
1da177e4 | 121 | { |
4be44fcd | 122 | acpi_status status = AE_OK; |
4119532c | 123 | struct acpi_evaluate_info *info; |
1da177e4 | 124 | |
b229cf92 | 125 | ACPI_FUNCTION_TRACE(acpi_get_sleep_type_data); |
1da177e4 | 126 | |
44f6c012 RM |
127 | /* Validate parameters */ |
128 | ||
4be44fcd LB |
129 | if ((sleep_state > ACPI_S_STATES_MAX) || !sleep_type_a || !sleep_type_b) { |
130 | return_ACPI_STATUS(AE_BAD_PARAMETER); | |
1da177e4 LT |
131 | } |
132 | ||
4119532c BM |
133 | /* Allocate the evaluation information block */ |
134 | ||
135 | info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info)); | |
136 | if (!info) { | |
137 | return_ACPI_STATUS(AE_NO_MEMORY); | |
138 | } | |
44f6c012 | 139 | |
4119532c | 140 | info->pathname = |
defba1d8 | 141 | ACPI_CAST_PTR(char, acpi_gbl_sleep_state_names[sleep_state]); |
44f6c012 | 142 | |
4119532c BM |
143 | /* Evaluate the namespace object containing the values for this state */ |
144 | ||
145 | status = acpi_ns_evaluate(info); | |
4be44fcd LB |
146 | if (ACPI_FAILURE(status)) { |
147 | ACPI_DEBUG_PRINT((ACPI_DB_EXEC, | |
b229cf92 | 148 | "%s while evaluating SleepState [%s]\n", |
4be44fcd | 149 | acpi_format_exception(status), |
4119532c | 150 | info->pathname)); |
1da177e4 | 151 | |
4119532c | 152 | goto cleanup; |
1da177e4 LT |
153 | } |
154 | ||
155 | /* Must have a return object */ | |
156 | ||
4119532c | 157 | if (!info->return_object) { |
b8e4d893 | 158 | ACPI_ERROR((AE_INFO, "No Sleep State object returned from [%s]", |
4119532c | 159 | info->pathname)); |
1da177e4 LT |
160 | status = AE_NOT_EXIST; |
161 | } | |
162 | ||
163 | /* It must be of type Package */ | |
164 | ||
4119532c | 165 | else if (ACPI_GET_OBJECT_TYPE(info->return_object) != ACPI_TYPE_PACKAGE) { |
b8e4d893 BM |
166 | ACPI_ERROR((AE_INFO, |
167 | "Sleep State return object is not a Package")); | |
1da177e4 LT |
168 | status = AE_AML_OPERAND_TYPE; |
169 | } | |
170 | ||
44f6c012 | 171 | /* |
967440e3 | 172 | * The package must have at least two elements. NOTE (March 2005): This |
44f6c012 | 173 | * goes against the current ACPI spec which defines this object as a |
967440e3 | 174 | * package with one encoded DWORD element. However, existing practice |
44f6c012 RM |
175 | * by BIOS vendors seems to be to have 2 or more elements, at least |
176 | * one per sleep type (A/B). | |
177 | */ | |
4119532c | 178 | else if (info->return_object->package.count < 2) { |
b8e4d893 BM |
179 | ACPI_ERROR((AE_INFO, |
180 | "Sleep State return package does not have at least two elements")); | |
1da177e4 LT |
181 | status = AE_AML_NO_OPERAND; |
182 | } | |
183 | ||
184 | /* The first two elements must both be of type Integer */ | |
185 | ||
4119532c | 186 | else if ((ACPI_GET_OBJECT_TYPE(info->return_object->package.elements[0]) |
4be44fcd | 187 | != ACPI_TYPE_INTEGER) || |
4119532c | 188 | (ACPI_GET_OBJECT_TYPE(info->return_object->package.elements[1]) |
4be44fcd | 189 | != ACPI_TYPE_INTEGER)) { |
b8e4d893 BM |
190 | ACPI_ERROR((AE_INFO, |
191 | "Sleep State return package elements are not both Integers (%s, %s)", | |
4119532c | 192 | acpi_ut_get_object_type_name(info->return_object-> |
b8e4d893 | 193 | package.elements[0]), |
4119532c | 194 | acpi_ut_get_object_type_name(info->return_object-> |
b8e4d893 | 195 | package.elements[1]))); |
1da177e4 | 196 | status = AE_AML_OPERAND_TYPE; |
4be44fcd | 197 | } else { |
44f6c012 RM |
198 | /* Valid _Sx_ package size, type, and value */ |
199 | ||
200 | *sleep_type_a = (u8) | |
4119532c | 201 | (info->return_object->package.elements[0])->integer.value; |
44f6c012 | 202 | *sleep_type_b = (u8) |
4119532c | 203 | (info->return_object->package.elements[1])->integer.value; |
1da177e4 LT |
204 | } |
205 | ||
4be44fcd | 206 | if (ACPI_FAILURE(status)) { |
b8e4d893 | 207 | ACPI_EXCEPTION((AE_INFO, status, |
b229cf92 | 208 | "While evaluating SleepState [%s], bad Sleep object %p type %s", |
4119532c BM |
209 | info->pathname, info->return_object, |
210 | acpi_ut_get_object_type_name(info-> | |
b8e4d893 | 211 | return_object))); |
1da177e4 LT |
212 | } |
213 | ||
4119532c BM |
214 | acpi_ut_remove_reference(info->return_object); |
215 | ||
216 | cleanup: | |
217 | ACPI_FREE(info); | |
4be44fcd | 218 | return_ACPI_STATUS(status); |
1da177e4 | 219 | } |
1da177e4 | 220 | |
8313524a | 221 | ACPI_EXPORT_SYMBOL(acpi_get_sleep_type_data) |
1da177e4 LT |
222 | |
223 | /******************************************************************************* | |
224 | * | |
225 | * FUNCTION: acpi_hw_get_register_bit_mask | |
226 | * | |
227 | * PARAMETERS: register_id - Index of ACPI Register to access | |
228 | * | |
44f6c012 | 229 | * RETURN: The bitmask to be used when accessing the register |
1da177e4 | 230 | * |
44f6c012 | 231 | * DESCRIPTION: Map register_id into a register bitmask. |
1da177e4 LT |
232 | * |
233 | ******************************************************************************/ | |
4be44fcd | 234 | struct acpi_bit_register_info *acpi_hw_get_bit_register_info(u32 register_id) |
1da177e4 | 235 | { |
4a90c7e8 | 236 | ACPI_FUNCTION_ENTRY(); |
1da177e4 LT |
237 | |
238 | if (register_id > ACPI_BITREG_MAX) { | |
b229cf92 | 239 | ACPI_ERROR((AE_INFO, "Invalid BitRegister ID: %X", |
b8e4d893 | 240 | register_id)); |
1da177e4 LT |
241 | return (NULL); |
242 | } | |
243 | ||
244 | return (&acpi_gbl_bit_register_info[register_id]); | |
245 | } | |
246 | ||
1da177e4 LT |
247 | /******************************************************************************* |
248 | * | |
249 | * FUNCTION: acpi_get_register | |
250 | * | |
251 | * PARAMETERS: register_id - ID of ACPI bit_register to access | |
252 | * return_value - Value that was read from the register | |
1da177e4 | 253 | * |
967440e3 | 254 | * RETURN: Status and the value read from specified Register. Value |
1da177e4 LT |
255 | * returned is normalized to bit0 (is shifted all the way right) |
256 | * | |
257 | * DESCRIPTION: ACPI bit_register read function. | |
258 | * | |
259 | ******************************************************************************/ | |
260 | ||
d30dc9ab | 261 | acpi_status acpi_get_register_unlocked(u32 register_id, u32 * return_value) |
1da177e4 | 262 | { |
4be44fcd LB |
263 | u32 register_value = 0; |
264 | struct acpi_bit_register_info *bit_reg_info; | |
265 | acpi_status status; | |
1da177e4 | 266 | |
b229cf92 | 267 | ACPI_FUNCTION_TRACE(acpi_get_register); |
1da177e4 LT |
268 | |
269 | /* Get the info structure corresponding to the requested ACPI Register */ | |
270 | ||
4be44fcd | 271 | bit_reg_info = acpi_hw_get_bit_register_info(register_id); |
1da177e4 | 272 | if (!bit_reg_info) { |
4be44fcd | 273 | return_ACPI_STATUS(AE_BAD_PARAMETER); |
1da177e4 LT |
274 | } |
275 | ||
1da177e4 LT |
276 | /* Read from the register */ |
277 | ||
d30dc9ab | 278 | status = acpi_hw_register_read(bit_reg_info->parent_register, |
4be44fcd | 279 | ®ister_value); |
1da177e4 | 280 | |
4be44fcd | 281 | if (ACPI_SUCCESS(status)) { |
52fc0b02 | 282 | |
1da177e4 LT |
283 | /* Normalize the value that was read */ |
284 | ||
4be44fcd LB |
285 | register_value = |
286 | ((register_value & bit_reg_info->access_bit_mask) | |
287 | >> bit_reg_info->bit_position); | |
1da177e4 LT |
288 | |
289 | *return_value = register_value; | |
290 | ||
4be44fcd LB |
291 | ACPI_DEBUG_PRINT((ACPI_DB_IO, "Read value %8.8X register %X\n", |
292 | register_value, | |
293 | bit_reg_info->parent_register)); | |
1da177e4 LT |
294 | } |
295 | ||
4be44fcd | 296 | return_ACPI_STATUS(status); |
1da177e4 | 297 | } |
1da177e4 | 298 | |
d30dc9ab AS |
299 | acpi_status acpi_get_register(u32 register_id, u32 * return_value) |
300 | { | |
301 | acpi_status status; | |
302 | acpi_cpu_flags flags; | |
303 | flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); | |
304 | status = acpi_get_register_unlocked(register_id, return_value); | |
305 | acpi_os_release_lock(acpi_gbl_hardware_lock, flags); | |
306 | return status; | |
307 | } | |
308 | ||
8313524a | 309 | ACPI_EXPORT_SYMBOL(acpi_get_register) |
1da177e4 LT |
310 | |
311 | /******************************************************************************* | |
312 | * | |
313 | * FUNCTION: acpi_set_register | |
314 | * | |
315 | * PARAMETERS: register_id - ID of ACPI bit_register to access | |
316 | * Value - (only used on write) value to write to the | |
317 | * Register, NOT pre-normalized to the bit pos | |
1da177e4 LT |
318 | * |
319 | * RETURN: Status | |
320 | * | |
321 | * DESCRIPTION: ACPI Bit Register write function. | |
322 | * | |
323 | ******************************************************************************/ | |
d8c71b6d | 324 | acpi_status acpi_set_register(u32 register_id, u32 value) |
1da177e4 | 325 | { |
4be44fcd LB |
326 | u32 register_value = 0; |
327 | struct acpi_bit_register_info *bit_reg_info; | |
328 | acpi_status status; | |
4c90ece2 | 329 | acpi_cpu_flags lock_flags; |
1da177e4 | 330 | |
b229cf92 | 331 | ACPI_FUNCTION_TRACE_U32(acpi_set_register, register_id); |
1da177e4 LT |
332 | |
333 | /* Get the info structure corresponding to the requested ACPI Register */ | |
334 | ||
4be44fcd | 335 | bit_reg_info = acpi_hw_get_bit_register_info(register_id); |
1da177e4 | 336 | if (!bit_reg_info) { |
b229cf92 | 337 | ACPI_ERROR((AE_INFO, "Bad ACPI HW RegisterId: %X", |
b8e4d893 | 338 | register_id)); |
4be44fcd | 339 | return_ACPI_STATUS(AE_BAD_PARAMETER); |
1da177e4 LT |
340 | } |
341 | ||
4c90ece2 | 342 | lock_flags = acpi_os_acquire_lock(acpi_gbl_hardware_lock); |
1da177e4 LT |
343 | |
344 | /* Always do a register read first so we can insert the new bits */ | |
345 | ||
d30dc9ab | 346 | status = acpi_hw_register_read(bit_reg_info->parent_register, |
4be44fcd LB |
347 | ®ister_value); |
348 | if (ACPI_FAILURE(status)) { | |
1da177e4 LT |
349 | goto unlock_and_exit; |
350 | } | |
351 | ||
352 | /* | |
353 | * Decode the Register ID | |
354 | * Register ID = [Register block ID] | [bit ID] | |
355 | * | |
356 | * Check bit ID to fine locate Register offset. | |
357 | * Check Mask to determine Register offset, and then read-write. | |
358 | */ | |
359 | switch (bit_reg_info->parent_register) { | |
360 | case ACPI_REGISTER_PM1_STATUS: | |
361 | ||
362 | /* | |
967440e3 BM |
363 | * Status Registers are different from the rest. Clear by |
364 | * writing 1, and writing 0 has no effect. So, the only relevant | |
1da177e4 LT |
365 | * information is the single bit we're interested in, all others should |
366 | * be written as 0 so they will be left unchanged. | |
367 | */ | |
4be44fcd LB |
368 | value = ACPI_REGISTER_PREPARE_BITS(value, |
369 | bit_reg_info->bit_position, | |
370 | bit_reg_info-> | |
371 | access_bit_mask); | |
1da177e4 | 372 | if (value) { |
d30dc9ab | 373 | status = acpi_hw_register_write(ACPI_REGISTER_PM1_STATUS, |
4be44fcd | 374 | (u16) value); |
1da177e4 LT |
375 | register_value = 0; |
376 | } | |
377 | break; | |
378 | ||
1da177e4 LT |
379 | case ACPI_REGISTER_PM1_ENABLE: |
380 | ||
4be44fcd LB |
381 | ACPI_REGISTER_INSERT_VALUE(register_value, |
382 | bit_reg_info->bit_position, | |
383 | bit_reg_info->access_bit_mask, | |
384 | value); | |
1da177e4 | 385 | |
d30dc9ab | 386 | status = acpi_hw_register_write(ACPI_REGISTER_PM1_ENABLE, |
4be44fcd | 387 | (u16) register_value); |
1da177e4 LT |
388 | break; |
389 | ||
1da177e4 LT |
390 | case ACPI_REGISTER_PM1_CONTROL: |
391 | ||
392 | /* | |
393 | * Write the PM1 Control register. | |
394 | * Note that at this level, the fact that there are actually TWO | |
395 | * registers (A and B - and B may not exist) is abstracted. | |
396 | */ | |
4be44fcd LB |
397 | ACPI_DEBUG_PRINT((ACPI_DB_IO, "PM1 control: Read %X\n", |
398 | register_value)); | |
1da177e4 | 399 | |
4be44fcd LB |
400 | ACPI_REGISTER_INSERT_VALUE(register_value, |
401 | bit_reg_info->bit_position, | |
402 | bit_reg_info->access_bit_mask, | |
403 | value); | |
1da177e4 | 404 | |
d30dc9ab | 405 | status = acpi_hw_register_write(ACPI_REGISTER_PM1_CONTROL, |
4be44fcd | 406 | (u16) register_value); |
1da177e4 LT |
407 | break; |
408 | ||
1da177e4 LT |
409 | case ACPI_REGISTER_PM2_CONTROL: |
410 | ||
d30dc9ab | 411 | status = acpi_hw_register_read(ACPI_REGISTER_PM2_CONTROL, |
4be44fcd LB |
412 | ®ister_value); |
413 | if (ACPI_FAILURE(status)) { | |
1da177e4 LT |
414 | goto unlock_and_exit; |
415 | } | |
416 | ||
4be44fcd LB |
417 | ACPI_DEBUG_PRINT((ACPI_DB_IO, |
418 | "PM2 control: Read %X from %8.8X%8.8X\n", | |
419 | register_value, | |
f3d2e786 BM |
420 | ACPI_FORMAT_UINT64(acpi_gbl_FADT. |
421 | xpm2_control_block. | |
422 | address))); | |
4be44fcd LB |
423 | |
424 | ACPI_REGISTER_INSERT_VALUE(register_value, | |
425 | bit_reg_info->bit_position, | |
426 | bit_reg_info->access_bit_mask, | |
427 | value); | |
428 | ||
429 | ACPI_DEBUG_PRINT((ACPI_DB_IO, | |
430 | "About to write %4.4X to %8.8X%8.8X\n", | |
431 | register_value, | |
f3d2e786 BM |
432 | ACPI_FORMAT_UINT64(acpi_gbl_FADT. |
433 | xpm2_control_block. | |
434 | address))); | |
4be44fcd | 435 | |
d30dc9ab | 436 | status = acpi_hw_register_write(ACPI_REGISTER_PM2_CONTROL, |
4be44fcd | 437 | (u8) (register_value)); |
1da177e4 LT |
438 | break; |
439 | ||
1da177e4 LT |
440 | default: |
441 | break; | |
442 | } | |
443 | ||
4be44fcd | 444 | unlock_and_exit: |
1da177e4 | 445 | |
4c90ece2 | 446 | acpi_os_release_lock(acpi_gbl_hardware_lock, lock_flags); |
1da177e4 LT |
447 | |
448 | /* Normalize the value that was read */ | |
449 | ||
4be44fcd LB |
450 | ACPI_DEBUG_EXEC(register_value = |
451 | ((register_value & bit_reg_info->access_bit_mask) >> | |
452 | bit_reg_info->bit_position)); | |
1da177e4 | 453 | |
4be44fcd LB |
454 | ACPI_DEBUG_PRINT((ACPI_DB_IO, |
455 | "Set bits: %8.8X actual %8.8X register %X\n", value, | |
456 | register_value, bit_reg_info->parent_register)); | |
457 | return_ACPI_STATUS(status); | |
1da177e4 | 458 | } |
1da177e4 | 459 | |
8313524a | 460 | ACPI_EXPORT_SYMBOL(acpi_set_register) |
1da177e4 LT |
461 | |
462 | /****************************************************************************** | |
463 | * | |
464 | * FUNCTION: acpi_hw_register_read | |
465 | * | |
d30dc9ab | 466 | * PARAMETERS: register_id - ACPI Register ID |
44f6c012 | 467 | * return_value - Where the register value is returned |
1da177e4 LT |
468 | * |
469 | * RETURN: Status and the value read. | |
470 | * | |
967440e3 | 471 | * DESCRIPTION: Read from the specified ACPI register |
1da177e4 LT |
472 | * |
473 | ******************************************************************************/ | |
1da177e4 | 474 | acpi_status |
d30dc9ab | 475 | acpi_hw_register_read(u32 register_id, u32 * return_value) |
1da177e4 | 476 | { |
4be44fcd LB |
477 | u32 value1 = 0; |
478 | u32 value2 = 0; | |
479 | acpi_status status; | |
1da177e4 | 480 | |
b229cf92 | 481 | ACPI_FUNCTION_TRACE(hw_register_read); |
1da177e4 | 482 | |
1da177e4 | 483 | switch (register_id) { |
4be44fcd | 484 | case ACPI_REGISTER_PM1_STATUS: /* 16-bit access */ |
1da177e4 | 485 | |
4be44fcd LB |
486 | status = |
487 | acpi_hw_low_level_read(16, &value1, | |
f3d2e786 | 488 | &acpi_gbl_FADT.xpm1a_event_block); |
4be44fcd | 489 | if (ACPI_FAILURE(status)) { |
d30dc9ab | 490 | goto exit; |
1da177e4 LT |
491 | } |
492 | ||
493 | /* PM1B is optional */ | |
494 | ||
4be44fcd LB |
495 | status = |
496 | acpi_hw_low_level_read(16, &value2, | |
f3d2e786 | 497 | &acpi_gbl_FADT.xpm1b_event_block); |
1da177e4 LT |
498 | value1 |= value2; |
499 | break; | |
500 | ||
4be44fcd | 501 | case ACPI_REGISTER_PM1_ENABLE: /* 16-bit access */ |
1da177e4 | 502 | |
4be44fcd LB |
503 | status = |
504 | acpi_hw_low_level_read(16, &value1, &acpi_gbl_xpm1a_enable); | |
505 | if (ACPI_FAILURE(status)) { | |
d30dc9ab | 506 | goto exit; |
1da177e4 LT |
507 | } |
508 | ||
509 | /* PM1B is optional */ | |
510 | ||
4be44fcd LB |
511 | status = |
512 | acpi_hw_low_level_read(16, &value2, &acpi_gbl_xpm1b_enable); | |
1da177e4 LT |
513 | value1 |= value2; |
514 | break; | |
515 | ||
4be44fcd | 516 | case ACPI_REGISTER_PM1_CONTROL: /* 16-bit access */ |
1da177e4 | 517 | |
4be44fcd LB |
518 | status = |
519 | acpi_hw_low_level_read(16, &value1, | |
f3d2e786 | 520 | &acpi_gbl_FADT.xpm1a_control_block); |
4be44fcd | 521 | if (ACPI_FAILURE(status)) { |
d30dc9ab | 522 | goto exit; |
1da177e4 LT |
523 | } |
524 | ||
4be44fcd LB |
525 | status = |
526 | acpi_hw_low_level_read(16, &value2, | |
f3d2e786 | 527 | &acpi_gbl_FADT.xpm1b_control_block); |
1da177e4 LT |
528 | value1 |= value2; |
529 | break; | |
530 | ||
4be44fcd | 531 | case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ |
1da177e4 | 532 | |
4be44fcd LB |
533 | status = |
534 | acpi_hw_low_level_read(8, &value1, | |
f3d2e786 | 535 | &acpi_gbl_FADT.xpm2_control_block); |
1da177e4 LT |
536 | break; |
537 | ||
4be44fcd | 538 | case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ |
1da177e4 | 539 | |
4be44fcd LB |
540 | status = |
541 | acpi_hw_low_level_read(32, &value1, | |
f3d2e786 | 542 | &acpi_gbl_FADT.xpm_timer_block); |
1da177e4 LT |
543 | break; |
544 | ||
4be44fcd | 545 | case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ |
1da177e4 | 546 | |
f3d2e786 BM |
547 | status = |
548 | acpi_os_read_port(acpi_gbl_FADT.smi_command, &value1, 8); | |
1da177e4 LT |
549 | break; |
550 | ||
551 | default: | |
b8e4d893 | 552 | ACPI_ERROR((AE_INFO, "Unknown Register ID: %X", register_id)); |
1da177e4 LT |
553 | status = AE_BAD_PARAMETER; |
554 | break; | |
555 | } | |
556 | ||
d30dc9ab | 557 | exit: |
1da177e4 | 558 | |
4be44fcd | 559 | if (ACPI_SUCCESS(status)) { |
1da177e4 LT |
560 | *return_value = value1; |
561 | } | |
562 | ||
4be44fcd | 563 | return_ACPI_STATUS(status); |
1da177e4 LT |
564 | } |
565 | ||
1da177e4 LT |
566 | /****************************************************************************** |
567 | * | |
568 | * FUNCTION: acpi_hw_register_write | |
569 | * | |
d30dc9ab | 570 | * PARAMETERS: register_id - ACPI Register ID |
1da177e4 LT |
571 | * Value - The value to write |
572 | * | |
573 | * RETURN: Status | |
574 | * | |
967440e3 BM |
575 | * DESCRIPTION: Write to the specified ACPI register |
576 | * | |
577 | * NOTE: In accordance with the ACPI specification, this function automatically | |
578 | * preserves the value of the following bits, meaning that these bits cannot be | |
579 | * changed via this interface: | |
580 | * | |
581 | * PM1_CONTROL[0] = SCI_EN | |
582 | * PM1_CONTROL[9] | |
583 | * PM1_STATUS[11] | |
584 | * | |
585 | * ACPI References: | |
586 | * 1) Hardware Ignored Bits: When software writes to a register with ignored | |
587 | * bit fields, it preserves the ignored bit fields | |
588 | * 2) SCI_EN: OSPM always preserves this bit position | |
1da177e4 LT |
589 | * |
590 | ******************************************************************************/ | |
591 | ||
d30dc9ab | 592 | acpi_status acpi_hw_register_write(u32 register_id, u32 value) |
1da177e4 | 593 | { |
4be44fcd | 594 | acpi_status status; |
967440e3 | 595 | u32 read_value; |
1da177e4 | 596 | |
b229cf92 | 597 | ACPI_FUNCTION_TRACE(hw_register_write); |
1da177e4 | 598 | |
1da177e4 | 599 | switch (register_id) { |
4be44fcd | 600 | case ACPI_REGISTER_PM1_STATUS: /* 16-bit access */ |
1da177e4 | 601 | |
967440e3 BM |
602 | /* Perform a read first to preserve certain bits (per ACPI spec) */ |
603 | ||
d30dc9ab | 604 | status = acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, |
967440e3 BM |
605 | &read_value); |
606 | if (ACPI_FAILURE(status)) { | |
d30dc9ab | 607 | goto exit; |
967440e3 BM |
608 | } |
609 | ||
610 | /* Insert the bits to be preserved */ | |
611 | ||
612 | ACPI_INSERT_BITS(value, ACPI_PM1_STATUS_PRESERVED_BITS, | |
613 | read_value); | |
614 | ||
615 | /* Now we can write the data */ | |
616 | ||
4be44fcd LB |
617 | status = |
618 | acpi_hw_low_level_write(16, value, | |
f3d2e786 | 619 | &acpi_gbl_FADT.xpm1a_event_block); |
4be44fcd | 620 | if (ACPI_FAILURE(status)) { |
d30dc9ab | 621 | goto exit; |
1da177e4 LT |
622 | } |
623 | ||
624 | /* PM1B is optional */ | |
625 | ||
4be44fcd LB |
626 | status = |
627 | acpi_hw_low_level_write(16, value, | |
f3d2e786 | 628 | &acpi_gbl_FADT.xpm1b_event_block); |
1da177e4 LT |
629 | break; |
630 | ||
4be44fcd | 631 | case ACPI_REGISTER_PM1_ENABLE: /* 16-bit access */ |
1da177e4 | 632 | |
4be44fcd LB |
633 | status = |
634 | acpi_hw_low_level_write(16, value, &acpi_gbl_xpm1a_enable); | |
635 | if (ACPI_FAILURE(status)) { | |
d30dc9ab | 636 | goto exit; |
1da177e4 LT |
637 | } |
638 | ||
639 | /* PM1B is optional */ | |
640 | ||
4be44fcd LB |
641 | status = |
642 | acpi_hw_low_level_write(16, value, &acpi_gbl_xpm1b_enable); | |
1da177e4 LT |
643 | break; |
644 | ||
4be44fcd | 645 | case ACPI_REGISTER_PM1_CONTROL: /* 16-bit access */ |
1da177e4 | 646 | |
967440e3 BM |
647 | /* |
648 | * Perform a read first to preserve certain bits (per ACPI spec) | |
967440e3 | 649 | */ |
d30dc9ab | 650 | status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL, |
967440e3 BM |
651 | &read_value); |
652 | if (ACPI_FAILURE(status)) { | |
d30dc9ab | 653 | goto exit; |
967440e3 BM |
654 | } |
655 | ||
656 | /* Insert the bits to be preserved */ | |
657 | ||
658 | ACPI_INSERT_BITS(value, ACPI_PM1_CONTROL_PRESERVED_BITS, | |
659 | read_value); | |
660 | ||
661 | /* Now we can write the data */ | |
662 | ||
4be44fcd LB |
663 | status = |
664 | acpi_hw_low_level_write(16, value, | |
f3d2e786 | 665 | &acpi_gbl_FADT.xpm1a_control_block); |
4be44fcd | 666 | if (ACPI_FAILURE(status)) { |
d30dc9ab | 667 | goto exit; |
1da177e4 LT |
668 | } |
669 | ||
4be44fcd LB |
670 | status = |
671 | acpi_hw_low_level_write(16, value, | |
f3d2e786 | 672 | &acpi_gbl_FADT.xpm1b_control_block); |
1da177e4 LT |
673 | break; |
674 | ||
4be44fcd | 675 | case ACPI_REGISTER_PM1A_CONTROL: /* 16-bit access */ |
1da177e4 | 676 | |
4be44fcd LB |
677 | status = |
678 | acpi_hw_low_level_write(16, value, | |
f3d2e786 | 679 | &acpi_gbl_FADT.xpm1a_control_block); |
1da177e4 LT |
680 | break; |
681 | ||
4be44fcd | 682 | case ACPI_REGISTER_PM1B_CONTROL: /* 16-bit access */ |
1da177e4 | 683 | |
4be44fcd LB |
684 | status = |
685 | acpi_hw_low_level_write(16, value, | |
f3d2e786 | 686 | &acpi_gbl_FADT.xpm1b_control_block); |
1da177e4 LT |
687 | break; |
688 | ||
4be44fcd | 689 | case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ |
1da177e4 | 690 | |
4be44fcd LB |
691 | status = |
692 | acpi_hw_low_level_write(8, value, | |
f3d2e786 | 693 | &acpi_gbl_FADT.xpm2_control_block); |
1da177e4 LT |
694 | break; |
695 | ||
4be44fcd | 696 | case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ |
1da177e4 | 697 | |
4be44fcd LB |
698 | status = |
699 | acpi_hw_low_level_write(32, value, | |
f3d2e786 | 700 | &acpi_gbl_FADT.xpm_timer_block); |
1da177e4 LT |
701 | break; |
702 | ||
4be44fcd | 703 | case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ |
1da177e4 LT |
704 | |
705 | /* SMI_CMD is currently always in IO space */ | |
706 | ||
f3d2e786 BM |
707 | status = |
708 | acpi_os_write_port(acpi_gbl_FADT.smi_command, value, 8); | |
1da177e4 LT |
709 | break; |
710 | ||
1da177e4 LT |
711 | default: |
712 | status = AE_BAD_PARAMETER; | |
713 | break; | |
714 | } | |
715 | ||
d30dc9ab | 716 | exit: |
4be44fcd | 717 | return_ACPI_STATUS(status); |
1da177e4 LT |
718 | } |
719 | ||
1da177e4 LT |
720 | /****************************************************************************** |
721 | * | |
722 | * FUNCTION: acpi_hw_low_level_read | |
723 | * | |
724 | * PARAMETERS: Width - 8, 16, or 32 | |
725 | * Value - Where the value is returned | |
726 | * Reg - GAS register structure | |
727 | * | |
728 | * RETURN: Status | |
729 | * | |
730 | * DESCRIPTION: Read from either memory or IO space. | |
731 | * | |
732 | ******************************************************************************/ | |
733 | ||
734 | acpi_status | |
4be44fcd | 735 | acpi_hw_low_level_read(u32 width, u32 * value, struct acpi_generic_address *reg) |
1da177e4 | 736 | { |
4be44fcd LB |
737 | u64 address; |
738 | acpi_status status; | |
1da177e4 | 739 | |
b229cf92 | 740 | ACPI_FUNCTION_NAME(hw_low_level_read); |
1da177e4 LT |
741 | |
742 | /* | |
743 | * Must have a valid pointer to a GAS structure, and | |
744 | * a non-zero address within. However, don't return an error | |
745 | * because the PM1A/B code must not fail if B isn't present. | |
746 | */ | |
747 | if (!reg) { | |
748 | return (AE_OK); | |
749 | } | |
750 | ||
967440e3 | 751 | /* Get a local copy of the address. Handles possible alignment issues */ |
1da177e4 | 752 | |
4be44fcd | 753 | ACPI_MOVE_64_TO_64(&address, ®->address); |
1da177e4 LT |
754 | if (!address) { |
755 | return (AE_OK); | |
756 | } | |
757 | *value = 0; | |
758 | ||
759 | /* | |
760 | * Two address spaces supported: Memory or IO. | |
761 | * PCI_Config is not supported here because the GAS struct is insufficient | |
762 | */ | |
f3d2e786 | 763 | switch (reg->space_id) { |
1da177e4 LT |
764 | case ACPI_ADR_SPACE_SYSTEM_MEMORY: |
765 | ||
4be44fcd LB |
766 | status = acpi_os_read_memory((acpi_physical_address) address, |
767 | value, width); | |
1da177e4 LT |
768 | break; |
769 | ||
1da177e4 LT |
770 | case ACPI_ADR_SPACE_SYSTEM_IO: |
771 | ||
59fa8505 BM |
772 | status = |
773 | acpi_os_read_port((acpi_io_address) address, value, width); | |
1da177e4 LT |
774 | break; |
775 | ||
1da177e4 | 776 | default: |
b8e4d893 | 777 | ACPI_ERROR((AE_INFO, |
f3d2e786 | 778 | "Unsupported address space: %X", reg->space_id)); |
1da177e4 LT |
779 | return (AE_BAD_PARAMETER); |
780 | } | |
781 | ||
4be44fcd LB |
782 | ACPI_DEBUG_PRINT((ACPI_DB_IO, |
783 | "Read: %8.8X width %2d from %8.8X%8.8X (%s)\n", | |
59fa8505 | 784 | *value, width, ACPI_FORMAT_UINT64(address), |
f3d2e786 | 785 | acpi_ut_get_region_name(reg->space_id))); |
1da177e4 LT |
786 | |
787 | return (status); | |
788 | } | |
789 | ||
1da177e4 LT |
790 | /****************************************************************************** |
791 | * | |
792 | * FUNCTION: acpi_hw_low_level_write | |
793 | * | |
794 | * PARAMETERS: Width - 8, 16, or 32 | |
795 | * Value - To be written | |
796 | * Reg - GAS register structure | |
797 | * | |
798 | * RETURN: Status | |
799 | * | |
800 | * DESCRIPTION: Write to either memory or IO space. | |
801 | * | |
802 | ******************************************************************************/ | |
803 | ||
804 | acpi_status | |
4be44fcd | 805 | acpi_hw_low_level_write(u32 width, u32 value, struct acpi_generic_address * reg) |
1da177e4 | 806 | { |
4be44fcd LB |
807 | u64 address; |
808 | acpi_status status; | |
1da177e4 | 809 | |
b229cf92 | 810 | ACPI_FUNCTION_NAME(hw_low_level_write); |
1da177e4 LT |
811 | |
812 | /* | |
813 | * Must have a valid pointer to a GAS structure, and | |
814 | * a non-zero address within. However, don't return an error | |
815 | * because the PM1A/B code must not fail if B isn't present. | |
816 | */ | |
817 | if (!reg) { | |
818 | return (AE_OK); | |
819 | } | |
820 | ||
967440e3 | 821 | /* Get a local copy of the address. Handles possible alignment issues */ |
1da177e4 | 822 | |
4be44fcd | 823 | ACPI_MOVE_64_TO_64(&address, ®->address); |
1da177e4 LT |
824 | if (!address) { |
825 | return (AE_OK); | |
826 | } | |
827 | ||
828 | /* | |
829 | * Two address spaces supported: Memory or IO. | |
830 | * PCI_Config is not supported here because the GAS struct is insufficient | |
831 | */ | |
f3d2e786 | 832 | switch (reg->space_id) { |
1da177e4 LT |
833 | case ACPI_ADR_SPACE_SYSTEM_MEMORY: |
834 | ||
4be44fcd LB |
835 | status = acpi_os_write_memory((acpi_physical_address) address, |
836 | value, width); | |
1da177e4 LT |
837 | break; |
838 | ||
1da177e4 LT |
839 | case ACPI_ADR_SPACE_SYSTEM_IO: |
840 | ||
59fa8505 BM |
841 | status = acpi_os_write_port((acpi_io_address) address, value, |
842 | width); | |
1da177e4 LT |
843 | break; |
844 | ||
1da177e4 | 845 | default: |
b8e4d893 | 846 | ACPI_ERROR((AE_INFO, |
f3d2e786 | 847 | "Unsupported address space: %X", reg->space_id)); |
1da177e4 LT |
848 | return (AE_BAD_PARAMETER); |
849 | } | |
850 | ||
4be44fcd LB |
851 | ACPI_DEBUG_PRINT((ACPI_DB_IO, |
852 | "Wrote: %8.8X width %2d to %8.8X%8.8X (%s)\n", | |
59fa8505 | 853 | value, width, ACPI_FORMAT_UINT64(address), |
f3d2e786 | 854 | acpi_ut_get_region_name(reg->space_id))); |
1da177e4 LT |
855 | |
856 | return (status); | |
857 | } |