diff options
Diffstat (limited to 'drivers/acpi/executer/exfldio.c')
-rw-r--r-- | drivers/acpi/executer/exfldio.c | 835 |
1 files changed, 835 insertions, 0 deletions
diff --git a/drivers/acpi/executer/exfldio.c b/drivers/acpi/executer/exfldio.c new file mode 100644 index 000000000000..9d0f9d2e9061 --- /dev/null +++ b/drivers/acpi/executer/exfldio.c | |||
@@ -0,0 +1,835 @@ | |||
1 | /****************************************************************************** | ||
2 | * | ||
3 | * Module Name: exfldio - Aml Field I/O | ||
4 | * | ||
5 | *****************************************************************************/ | ||
6 | |||
7 | /* | ||
8 | * Copyright (C) 2000 - 2005, R. Byron Moore | ||
9 | * All rights reserved. | ||
10 | * | ||
11 | * Redistribution and use in source and binary forms, with or without | ||
12 | * modification, are permitted provided that the following conditions | ||
13 | * are met: | ||
14 | * 1. Redistributions of source code must retain the above copyright | ||
15 | * notice, this list of conditions, and the following disclaimer, | ||
16 | * without modification. | ||
17 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer | ||
18 | * substantially similar to the "NO WARRANTY" disclaimer below | ||
19 | * ("Disclaimer") and any redistribution must be conditioned upon | ||
20 | * including a substantially similar Disclaimer requirement for further | ||
21 | * binary redistribution. | ||
22 | * 3. Neither the names of the above-listed copyright holders nor the names | ||
23 | * of any contributors may be used to endorse or promote products derived | ||
24 | * from this software without specific prior written permission. | ||
25 | * | ||
26 | * Alternatively, this software may be distributed under the terms of the | ||
27 | * GNU General Public License ("GPL") version 2 as published by the Free | ||
28 | * Software Foundation. | ||
29 | * | ||
30 | * NO WARRANTY | ||
31 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
32 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
33 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR | ||
34 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
35 | * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
36 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
37 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
38 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
39 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | ||
40 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
41 | * POSSIBILITY OF SUCH DAMAGES. | ||
42 | */ | ||
43 | |||
44 | |||
45 | #include <acpi/acpi.h> | ||
46 | #include <acpi/acinterp.h> | ||
47 | #include <acpi/amlcode.h> | ||
48 | #include <acpi/acevents.h> | ||
49 | #include <acpi/acdispat.h> | ||
50 | |||
51 | |||
52 | #define _COMPONENT ACPI_EXECUTER | ||
53 | ACPI_MODULE_NAME ("exfldio") | ||
54 | |||
55 | |||
56 | /******************************************************************************* | ||
57 | * | ||
58 | * FUNCTION: acpi_ex_setup_region | ||
59 | * | ||
60 | * PARAMETERS: *obj_desc - Field to be read or written | ||
61 | * field_datum_byte_offset - Byte offset of this datum within the | ||
62 | * parent field | ||
63 | * | ||
64 | * RETURN: Status | ||
65 | * | ||
66 | * DESCRIPTION: Common processing for acpi_ex_extract_from_field and | ||
67 | * acpi_ex_insert_into_field. Initialize the Region if necessary and | ||
68 | * validate the request. | ||
69 | * | ||
70 | ******************************************************************************/ | ||
71 | |||
72 | acpi_status | ||
73 | acpi_ex_setup_region ( | ||
74 | union acpi_operand_object *obj_desc, | ||
75 | u32 field_datum_byte_offset) | ||
76 | { | ||
77 | acpi_status status = AE_OK; | ||
78 | union acpi_operand_object *rgn_desc; | ||
79 | |||
80 | |||
81 | ACPI_FUNCTION_TRACE_U32 ("ex_setup_region", field_datum_byte_offset); | ||
82 | |||
83 | |||
84 | rgn_desc = obj_desc->common_field.region_obj; | ||
85 | |||
86 | /* We must have a valid region */ | ||
87 | |||
88 | if (ACPI_GET_OBJECT_TYPE (rgn_desc) != ACPI_TYPE_REGION) { | ||
89 | ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Needed Region, found type %X (%s)\n", | ||
90 | ACPI_GET_OBJECT_TYPE (rgn_desc), | ||
91 | acpi_ut_get_object_type_name (rgn_desc))); | ||
92 | |||
93 | return_ACPI_STATUS (AE_AML_OPERAND_TYPE); | ||
94 | } | ||
95 | |||
96 | /* | ||
97 | * If the Region Address and Length have not been previously evaluated, | ||
98 | * evaluate them now and save the results. | ||
99 | */ | ||
100 | if (!(rgn_desc->common.flags & AOPOBJ_DATA_VALID)) { | ||
101 | status = acpi_ds_get_region_arguments (rgn_desc); | ||
102 | if (ACPI_FAILURE (status)) { | ||
103 | return_ACPI_STATUS (status); | ||
104 | } | ||
105 | } | ||
106 | |||
107 | if (rgn_desc->region.space_id == ACPI_ADR_SPACE_SMBUS) { | ||
108 | /* SMBus has a non-linear address space */ | ||
109 | |||
110 | return_ACPI_STATUS (AE_OK); | ||
111 | } | ||
112 | |||
113 | #ifdef ACPI_UNDER_DEVELOPMENT | ||
114 | /* | ||
115 | * If the Field access is any_acc, we can now compute the optimal | ||
116 | * access (because we know know the length of the parent region) | ||
117 | */ | ||
118 | if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { | ||
119 | if (ACPI_FAILURE (status)) { | ||
120 | return_ACPI_STATUS (status); | ||
121 | } | ||
122 | } | ||
123 | #endif | ||
124 | |||
125 | /* | ||
126 | * Validate the request. The entire request from the byte offset for a | ||
127 | * length of one field datum (access width) must fit within the region. | ||
128 | * (Region length is specified in bytes) | ||
129 | */ | ||
130 | if (rgn_desc->region.length < (obj_desc->common_field.base_byte_offset | ||
131 | + field_datum_byte_offset | ||
132 | + obj_desc->common_field.access_byte_width)) { | ||
133 | if (acpi_gbl_enable_interpreter_slack) { | ||
134 | /* | ||
135 | * Slack mode only: We will go ahead and allow access to this | ||
136 | * field if it is within the region length rounded up to the next | ||
137 | * access width boundary. | ||
138 | */ | ||
139 | if (ACPI_ROUND_UP (rgn_desc->region.length, | ||
140 | obj_desc->common_field.access_byte_width) >= | ||
141 | (obj_desc->common_field.base_byte_offset + | ||
142 | (acpi_native_uint) obj_desc->common_field.access_byte_width + | ||
143 | field_datum_byte_offset)) { | ||
144 | return_ACPI_STATUS (AE_OK); | ||
145 | } | ||
146 | } | ||
147 | |||
148 | if (rgn_desc->region.length < obj_desc->common_field.access_byte_width) { | ||
149 | /* | ||
150 | * This is the case where the access_type (acc_word, etc.) is wider | ||
151 | * than the region itself. For example, a region of length one | ||
152 | * byte, and a field with Dword access specified. | ||
153 | */ | ||
154 | ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, | ||
155 | "Field [%4.4s] access width (%d bytes) too large for region [%4.4s] (length %X)\n", | ||
156 | acpi_ut_get_node_name (obj_desc->common_field.node), | ||
157 | obj_desc->common_field.access_byte_width, | ||
158 | acpi_ut_get_node_name (rgn_desc->region.node), rgn_desc->region.length)); | ||
159 | } | ||
160 | |||
161 | /* | ||
162 | * Offset rounded up to next multiple of field width | ||
163 | * exceeds region length, indicate an error | ||
164 | */ | ||
165 | ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, | ||
166 | "Field [%4.4s] Base+Offset+Width %X+%X+%X is beyond end of region [%4.4s] (length %X)\n", | ||
167 | acpi_ut_get_node_name (obj_desc->common_field.node), | ||
168 | obj_desc->common_field.base_byte_offset, | ||
169 | field_datum_byte_offset, obj_desc->common_field.access_byte_width, | ||
170 | acpi_ut_get_node_name (rgn_desc->region.node), rgn_desc->region.length)); | ||
171 | |||
172 | return_ACPI_STATUS (AE_AML_REGION_LIMIT); | ||
173 | } | ||
174 | |||
175 | return_ACPI_STATUS (AE_OK); | ||
176 | } | ||
177 | |||
178 | |||
179 | /******************************************************************************* | ||
180 | * | ||
181 | * FUNCTION: acpi_ex_access_region | ||
182 | * | ||
183 | * PARAMETERS: *obj_desc - Field to be read | ||
184 | * field_datum_byte_offset - Byte offset of this datum within the | ||
185 | * parent field | ||
186 | * *Value - Where to store value (must at least | ||
187 | * the size of acpi_integer) | ||
188 | * Function - Read or Write flag plus other region- | ||
189 | * dependent flags | ||
190 | * | ||
191 | * RETURN: Status | ||
192 | * | ||
193 | * DESCRIPTION: Read or Write a single field datum to an Operation Region. | ||
194 | * | ||
195 | ******************************************************************************/ | ||
196 | |||
197 | acpi_status | ||
198 | acpi_ex_access_region ( | ||
199 | union acpi_operand_object *obj_desc, | ||
200 | u32 field_datum_byte_offset, | ||
201 | acpi_integer *value, | ||
202 | u32 function) | ||
203 | { | ||
204 | acpi_status status; | ||
205 | union acpi_operand_object *rgn_desc; | ||
206 | acpi_physical_address address; | ||
207 | |||
208 | |||
209 | ACPI_FUNCTION_TRACE ("ex_access_region"); | ||
210 | |||
211 | |||
212 | /* | ||
213 | * Ensure that the region operands are fully evaluated and verify | ||
214 | * the validity of the request | ||
215 | */ | ||
216 | status = acpi_ex_setup_region (obj_desc, field_datum_byte_offset); | ||
217 | if (ACPI_FAILURE (status)) { | ||
218 | return_ACPI_STATUS (status); | ||
219 | } | ||
220 | |||
221 | /* | ||
222 | * The physical address of this field datum is: | ||
223 | * | ||
224 | * 1) The base of the region, plus | ||
225 | * 2) The base offset of the field, plus | ||
226 | * 3) The current offset into the field | ||
227 | */ | ||
228 | rgn_desc = obj_desc->common_field.region_obj; | ||
229 | address = rgn_desc->region.address | ||
230 | + obj_desc->common_field.base_byte_offset | ||
231 | + field_datum_byte_offset; | ||
232 | |||
233 | if ((function & ACPI_IO_MASK) == ACPI_READ) { | ||
234 | ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "[READ]")); | ||
235 | } | ||
236 | else { | ||
237 | ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "[WRITE]")); | ||
238 | } | ||
239 | |||
240 | ACPI_DEBUG_PRINT_RAW ((ACPI_DB_BFIELD, | ||
241 | " Region [%s:%X], Width %X, byte_base %X, Offset %X at %8.8X%8.8X\n", | ||
242 | acpi_ut_get_region_name (rgn_desc->region.space_id), | ||
243 | rgn_desc->region.space_id, | ||
244 | obj_desc->common_field.access_byte_width, | ||
245 | obj_desc->common_field.base_byte_offset, | ||
246 | field_datum_byte_offset, | ||
247 | ACPI_FORMAT_UINT64 (address))); | ||
248 | |||
249 | /* Invoke the appropriate address_space/op_region handler */ | ||
250 | |||
251 | status = acpi_ev_address_space_dispatch (rgn_desc, function, | ||
252 | address, ACPI_MUL_8 (obj_desc->common_field.access_byte_width), value); | ||
253 | |||
254 | if (ACPI_FAILURE (status)) { | ||
255 | if (status == AE_NOT_IMPLEMENTED) { | ||
256 | ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, | ||
257 | "Region %s(%X) not implemented\n", | ||
258 | acpi_ut_get_region_name (rgn_desc->region.space_id), | ||
259 | rgn_desc->region.space_id)); | ||
260 | } | ||
261 | else if (status == AE_NOT_EXIST) { | ||
262 | ACPI_REPORT_ERROR (( | ||
263 | "Region %s(%X) has no handler\n", | ||
264 | acpi_ut_get_region_name (rgn_desc->region.space_id), | ||
265 | rgn_desc->region.space_id)); | ||
266 | } | ||
267 | } | ||
268 | |||
269 | return_ACPI_STATUS (status); | ||
270 | } | ||
271 | |||
272 | |||
273 | /******************************************************************************* | ||
274 | * | ||
275 | * FUNCTION: acpi_ex_register_overflow | ||
276 | * | ||
277 | * PARAMETERS: *obj_desc - Register(Field) to be written | ||
278 | * Value - Value to be stored | ||
279 | * | ||
280 | * RETURN: TRUE if value overflows the field, FALSE otherwise | ||
281 | * | ||
282 | * DESCRIPTION: Check if a value is out of range of the field being written. | ||
283 | * Used to check if the values written to Index and Bank registers | ||
284 | * are out of range. Normally, the value is simply truncated | ||
285 | * to fit the field, but this case is most likely a serious | ||
286 | * coding error in the ASL. | ||
287 | * | ||
288 | ******************************************************************************/ | ||
289 | |||
290 | u8 | ||
291 | acpi_ex_register_overflow ( | ||
292 | union acpi_operand_object *obj_desc, | ||
293 | acpi_integer value) | ||
294 | { | ||
295 | |||
296 | if (obj_desc->common_field.bit_length >= ACPI_INTEGER_BIT_SIZE) { | ||
297 | /* | ||
298 | * The field is large enough to hold the maximum integer, so we can | ||
299 | * never overflow it. | ||
300 | */ | ||
301 | return (FALSE); | ||
302 | } | ||
303 | |||
304 | if (value >= ((acpi_integer) 1 << obj_desc->common_field.bit_length)) { | ||
305 | /* | ||
306 | * The Value is larger than the maximum value that can fit into | ||
307 | * the register. | ||
308 | */ | ||
309 | return (TRUE); | ||
310 | } | ||
311 | |||
312 | /* The Value will fit into the field with no truncation */ | ||
313 | |||
314 | return (FALSE); | ||
315 | } | ||
316 | |||
317 | |||
318 | /******************************************************************************* | ||
319 | * | ||
320 | * FUNCTION: acpi_ex_field_datum_io | ||
321 | * | ||
322 | * PARAMETERS: *obj_desc - Field to be read | ||
323 | * field_datum_byte_offset - Byte offset of this datum within the | ||
324 | * parent field | ||
325 | * *Value - Where to store value (must be 64 bits) | ||
326 | * read_write - Read or Write flag | ||
327 | * | ||
328 | * RETURN: Status | ||
329 | * | ||
330 | * DESCRIPTION: Read or Write a single datum of a field. The field_type is | ||
331 | * demultiplexed here to handle the different types of fields | ||
332 | * (buffer_field, region_field, index_field, bank_field) | ||
333 | * | ||
334 | ******************************************************************************/ | ||
335 | |||
336 | acpi_status | ||
337 | acpi_ex_field_datum_io ( | ||
338 | union acpi_operand_object *obj_desc, | ||
339 | u32 field_datum_byte_offset, | ||
340 | acpi_integer *value, | ||
341 | u32 read_write) | ||
342 | { | ||
343 | acpi_status status; | ||
344 | acpi_integer local_value; | ||
345 | |||
346 | |||
347 | ACPI_FUNCTION_TRACE_U32 ("ex_field_datum_io", field_datum_byte_offset); | ||
348 | |||
349 | |||
350 | if (read_write == ACPI_READ) { | ||
351 | if (!value) { | ||
352 | local_value = 0; | ||
353 | value = &local_value; /* To support reads without saving return value */ | ||
354 | } | ||
355 | |||
356 | /* Clear the entire return buffer first, [Very Important!] */ | ||
357 | |||
358 | *value = 0; | ||
359 | } | ||
360 | |||
361 | /* | ||
362 | * The four types of fields are: | ||
363 | * | ||
364 | * buffer_field - Read/write from/to a Buffer | ||
365 | * region_field - Read/write from/to a Operation Region. | ||
366 | * bank_field - Write to a Bank Register, then read/write from/to an op_region | ||
367 | * index_field - Write to an Index Register, then read/write from/to a Data Register | ||
368 | */ | ||
369 | switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { | ||
370 | case ACPI_TYPE_BUFFER_FIELD: | ||
371 | /* | ||
372 | * If the buffer_field arguments have not been previously evaluated, | ||
373 | * evaluate them now and save the results. | ||
374 | */ | ||
375 | if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { | ||
376 | status = acpi_ds_get_buffer_field_arguments (obj_desc); | ||
377 | if (ACPI_FAILURE (status)) { | ||
378 | return_ACPI_STATUS (status); | ||
379 | } | ||
380 | } | ||
381 | |||
382 | if (read_write == ACPI_READ) { | ||
383 | /* | ||
384 | * Copy the data from the source buffer. | ||
385 | * Length is the field width in bytes. | ||
386 | */ | ||
387 | ACPI_MEMCPY (value, (obj_desc->buffer_field.buffer_obj)->buffer.pointer | ||
388 | + obj_desc->buffer_field.base_byte_offset | ||
389 | + field_datum_byte_offset, | ||
390 | obj_desc->common_field.access_byte_width); | ||
391 | } | ||
392 | else { | ||
393 | /* | ||
394 | * Copy the data to the target buffer. | ||
395 | * Length is the field width in bytes. | ||
396 | */ | ||
397 | ACPI_MEMCPY ((obj_desc->buffer_field.buffer_obj)->buffer.pointer | ||
398 | + obj_desc->buffer_field.base_byte_offset | ||
399 | + field_datum_byte_offset, | ||
400 | value, obj_desc->common_field.access_byte_width); | ||
401 | } | ||
402 | |||
403 | status = AE_OK; | ||
404 | break; | ||
405 | |||
406 | |||
407 | case ACPI_TYPE_LOCAL_BANK_FIELD: | ||
408 | |||
409 | /* Ensure that the bank_value is not beyond the capacity of the register */ | ||
410 | |||
411 | if (acpi_ex_register_overflow (obj_desc->bank_field.bank_obj, | ||
412 | (acpi_integer) obj_desc->bank_field.value)) { | ||
413 | return_ACPI_STATUS (AE_AML_REGISTER_LIMIT); | ||
414 | } | ||
415 | |||
416 | /* | ||
417 | * For bank_fields, we must write the bank_value to the bank_register | ||
418 | * (itself a region_field) before we can access the data. | ||
419 | */ | ||
420 | status = acpi_ex_insert_into_field (obj_desc->bank_field.bank_obj, | ||
421 | &obj_desc->bank_field.value, | ||
422 | sizeof (obj_desc->bank_field.value)); | ||
423 | if (ACPI_FAILURE (status)) { | ||
424 | return_ACPI_STATUS (status); | ||
425 | } | ||
426 | |||
427 | /* | ||
428 | * Now that the Bank has been selected, fall through to the | ||
429 | * region_field case and write the datum to the Operation Region | ||
430 | */ | ||
431 | |||
432 | /*lint -fallthrough */ | ||
433 | |||
434 | |||
435 | case ACPI_TYPE_LOCAL_REGION_FIELD: | ||
436 | /* | ||
437 | * For simple region_fields, we just directly access the owning | ||
438 | * Operation Region. | ||
439 | */ | ||
440 | status = acpi_ex_access_region (obj_desc, field_datum_byte_offset, value, | ||
441 | read_write); | ||
442 | break; | ||
443 | |||
444 | |||
445 | case ACPI_TYPE_LOCAL_INDEX_FIELD: | ||
446 | |||
447 | |||
448 | /* Ensure that the index_value is not beyond the capacity of the register */ | ||
449 | |||
450 | if (acpi_ex_register_overflow (obj_desc->index_field.index_obj, | ||
451 | (acpi_integer) obj_desc->index_field.value)) { | ||
452 | return_ACPI_STATUS (AE_AML_REGISTER_LIMIT); | ||
453 | } | ||
454 | |||
455 | /* Write the index value to the index_register (itself a region_field) */ | ||
456 | |||
457 | field_datum_byte_offset += obj_desc->index_field.value; | ||
458 | |||
459 | ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, | ||
460 | "Write to Index Register: Value %8.8X\n", | ||
461 | field_datum_byte_offset)); | ||
462 | |||
463 | status = acpi_ex_insert_into_field (obj_desc->index_field.index_obj, | ||
464 | &field_datum_byte_offset, | ||
465 | sizeof (field_datum_byte_offset)); | ||
466 | if (ACPI_FAILURE (status)) { | ||
467 | return_ACPI_STATUS (status); | ||
468 | } | ||
469 | |||
470 | ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, | ||
471 | "I/O to Data Register: value_ptr %p\n", | ||
472 | value)); | ||
473 | |||
474 | if (read_write == ACPI_READ) { | ||
475 | /* Read the datum from the data_register */ | ||
476 | |||
477 | status = acpi_ex_extract_from_field (obj_desc->index_field.data_obj, | ||
478 | value, sizeof (acpi_integer)); | ||
479 | } | ||
480 | else { | ||
481 | /* Write the datum to the data_register */ | ||
482 | |||
483 | status = acpi_ex_insert_into_field (obj_desc->index_field.data_obj, | ||
484 | value, sizeof (acpi_integer)); | ||
485 | } | ||
486 | break; | ||
487 | |||
488 | |||
489 | default: | ||
490 | |||
491 | ACPI_REPORT_ERROR (("Wrong object type in field I/O %X\n", | ||
492 | ACPI_GET_OBJECT_TYPE (obj_desc))); | ||
493 | status = AE_AML_INTERNAL; | ||
494 | break; | ||
495 | } | ||
496 | |||
497 | if (ACPI_SUCCESS (status)) { | ||
498 | if (read_write == ACPI_READ) { | ||
499 | ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Value Read %8.8X%8.8X, Width %d\n", | ||
500 | ACPI_FORMAT_UINT64 (*value), | ||
501 | obj_desc->common_field.access_byte_width)); | ||
502 | } | ||
503 | else { | ||
504 | ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Value Written %8.8X%8.8X, Width %d\n", | ||
505 | ACPI_FORMAT_UINT64 (*value), | ||
506 | obj_desc->common_field.access_byte_width)); | ||
507 | } | ||
508 | } | ||
509 | |||
510 | return_ACPI_STATUS (status); | ||
511 | } | ||
512 | |||
513 | |||
514 | /******************************************************************************* | ||
515 | * | ||
516 | * FUNCTION: acpi_ex_write_with_update_rule | ||
517 | * | ||
518 | * PARAMETERS: *obj_desc - Field to be set | ||
519 | * Value - Value to store | ||
520 | * | ||
521 | * RETURN: Status | ||
522 | * | ||
523 | * DESCRIPTION: Apply the field update rule to a field write | ||
524 | * | ||
525 | ******************************************************************************/ | ||
526 | |||
527 | acpi_status | ||
528 | acpi_ex_write_with_update_rule ( | ||
529 | union acpi_operand_object *obj_desc, | ||
530 | acpi_integer mask, | ||
531 | acpi_integer field_value, | ||
532 | u32 field_datum_byte_offset) | ||
533 | { | ||
534 | acpi_status status = AE_OK; | ||
535 | acpi_integer merged_value; | ||
536 | acpi_integer current_value; | ||
537 | |||
538 | |||
539 | ACPI_FUNCTION_TRACE_U32 ("ex_write_with_update_rule", mask); | ||
540 | |||
541 | |||
542 | /* Start with the new bits */ | ||
543 | |||
544 | merged_value = field_value; | ||
545 | |||
546 | /* If the mask is all ones, we don't need to worry about the update rule */ | ||
547 | |||
548 | if (mask != ACPI_INTEGER_MAX) { | ||
549 | /* Decode the update rule */ | ||
550 | |||
551 | switch (obj_desc->common_field.field_flags & AML_FIELD_UPDATE_RULE_MASK) { | ||
552 | case AML_FIELD_UPDATE_PRESERVE: | ||
553 | /* | ||
554 | * Check if update rule needs to be applied (not if mask is all | ||
555 | * ones) The left shift drops the bits we want to ignore. | ||
556 | */ | ||
557 | if ((~mask << (ACPI_MUL_8 (sizeof (mask)) - | ||
558 | ACPI_MUL_8 (obj_desc->common_field.access_byte_width))) != 0) { | ||
559 | /* | ||
560 | * Read the current contents of the byte/word/dword containing | ||
561 | * the field, and merge with the new field value. | ||
562 | */ | ||
563 | status = acpi_ex_field_datum_io (obj_desc, field_datum_byte_offset, | ||
564 | ¤t_value, ACPI_READ); | ||
565 | if (ACPI_FAILURE (status)) { | ||
566 | return_ACPI_STATUS (status); | ||
567 | } | ||
568 | |||
569 | merged_value |= (current_value & ~mask); | ||
570 | } | ||
571 | break; | ||
572 | |||
573 | case AML_FIELD_UPDATE_WRITE_AS_ONES: | ||
574 | |||
575 | /* Set positions outside the field to all ones */ | ||
576 | |||
577 | merged_value |= ~mask; | ||
578 | break; | ||
579 | |||
580 | case AML_FIELD_UPDATE_WRITE_AS_ZEROS: | ||
581 | |||
582 | /* Set positions outside the field to all zeros */ | ||
583 | |||
584 | merged_value &= mask; | ||
585 | break; | ||
586 | |||
587 | default: | ||
588 | |||
589 | ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, | ||
590 | "write_with_update_rule: Unknown update_rule setting: %X\n", | ||
591 | (obj_desc->common_field.field_flags & AML_FIELD_UPDATE_RULE_MASK))); | ||
592 | return_ACPI_STATUS (AE_AML_OPERAND_VALUE); | ||
593 | } | ||
594 | } | ||
595 | |||
596 | ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, | ||
597 | "Mask %8.8X%8.8X, datum_offset %X, Width %X, Value %8.8X%8.8X, merged_value %8.8X%8.8X\n", | ||
598 | ACPI_FORMAT_UINT64 (mask), | ||
599 | field_datum_byte_offset, | ||
600 | obj_desc->common_field.access_byte_width, | ||
601 | ACPI_FORMAT_UINT64 (field_value), | ||
602 | ACPI_FORMAT_UINT64 (merged_value))); | ||
603 | |||
604 | /* Write the merged value */ | ||
605 | |||
606 | status = acpi_ex_field_datum_io (obj_desc, field_datum_byte_offset, | ||
607 | &merged_value, ACPI_WRITE); | ||
608 | |||
609 | return_ACPI_STATUS (status); | ||
610 | } | ||
611 | |||
612 | |||
613 | /******************************************************************************* | ||
614 | * | ||
615 | * FUNCTION: acpi_ex_extract_from_field | ||
616 | * | ||
617 | * PARAMETERS: obj_desc - Field to be read | ||
618 | * Buffer - Where to store the field data | ||
619 | * buffer_length - Length of Buffer | ||
620 | * | ||
621 | * RETURN: Status | ||
622 | * | ||
623 | * DESCRIPTION: Retrieve the current value of the given field | ||
624 | * | ||
625 | ******************************************************************************/ | ||
626 | |||
627 | acpi_status | ||
628 | acpi_ex_extract_from_field ( | ||
629 | union acpi_operand_object *obj_desc, | ||
630 | void *buffer, | ||
631 | u32 buffer_length) | ||
632 | { | ||
633 | acpi_status status; | ||
634 | acpi_integer raw_datum; | ||
635 | acpi_integer merged_datum; | ||
636 | u32 field_offset = 0; | ||
637 | u32 buffer_offset = 0; | ||
638 | u32 buffer_tail_bits; | ||
639 | u32 datum_count; | ||
640 | u32 field_datum_count; | ||
641 | u32 i; | ||
642 | |||
643 | |||
644 | ACPI_FUNCTION_TRACE ("ex_extract_from_field"); | ||
645 | |||
646 | |||
647 | /* Validate target buffer and clear it */ | ||
648 | |||
649 | if (buffer_length < ACPI_ROUND_BITS_UP_TO_BYTES ( | ||
650 | obj_desc->common_field.bit_length)) { | ||
651 | ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, | ||
652 | "Field size %X (bits) is too large for buffer (%X)\n", | ||
653 | obj_desc->common_field.bit_length, buffer_length)); | ||
654 | |||
655 | return_ACPI_STATUS (AE_BUFFER_OVERFLOW); | ||
656 | } | ||
657 | ACPI_MEMSET (buffer, 0, buffer_length); | ||
658 | |||
659 | /* Compute the number of datums (access width data items) */ | ||
660 | |||
661 | datum_count = ACPI_ROUND_UP_TO ( | ||
662 | obj_desc->common_field.bit_length, | ||
663 | obj_desc->common_field.access_bit_width); | ||
664 | field_datum_count = ACPI_ROUND_UP_TO ( | ||
665 | obj_desc->common_field.bit_length + | ||
666 | obj_desc->common_field.start_field_bit_offset, | ||
667 | obj_desc->common_field.access_bit_width); | ||
668 | |||
669 | /* Priming read from the field */ | ||
670 | |||
671 | status = acpi_ex_field_datum_io (obj_desc, field_offset, &raw_datum, ACPI_READ); | ||
672 | if (ACPI_FAILURE (status)) { | ||
673 | return_ACPI_STATUS (status); | ||
674 | } | ||
675 | merged_datum = raw_datum >> obj_desc->common_field.start_field_bit_offset; | ||
676 | |||
677 | /* Read the rest of the field */ | ||
678 | |||
679 | for (i = 1; i < field_datum_count; i++) { | ||
680 | /* Get next input datum from the field */ | ||
681 | |||
682 | field_offset += obj_desc->common_field.access_byte_width; | ||
683 | status = acpi_ex_field_datum_io (obj_desc, field_offset, | ||
684 | &raw_datum, ACPI_READ); | ||
685 | if (ACPI_FAILURE (status)) { | ||
686 | return_ACPI_STATUS (status); | ||
687 | } | ||
688 | |||
689 | /* Merge with previous datum if necessary */ | ||
690 | |||
691 | merged_datum |= raw_datum << | ||
692 | (obj_desc->common_field.access_bit_width - obj_desc->common_field.start_field_bit_offset); | ||
693 | |||
694 | if (i == datum_count) { | ||
695 | break; | ||
696 | } | ||
697 | |||
698 | /* Write merged datum to target buffer */ | ||
699 | |||
700 | ACPI_MEMCPY (((char *) buffer) + buffer_offset, &merged_datum, | ||
701 | ACPI_MIN(obj_desc->common_field.access_byte_width, | ||
702 | buffer_length - buffer_offset)); | ||
703 | |||
704 | buffer_offset += obj_desc->common_field.access_byte_width; | ||
705 | merged_datum = raw_datum >> obj_desc->common_field.start_field_bit_offset; | ||
706 | } | ||
707 | |||
708 | /* Mask off any extra bits in the last datum */ | ||
709 | |||
710 | buffer_tail_bits = obj_desc->common_field.bit_length % obj_desc->common_field.access_bit_width; | ||
711 | if (buffer_tail_bits) { | ||
712 | merged_datum &= ACPI_MASK_BITS_ABOVE (buffer_tail_bits); | ||
713 | } | ||
714 | |||
715 | /* Write the last datum to the buffer */ | ||
716 | |||
717 | ACPI_MEMCPY (((char *) buffer) + buffer_offset, &merged_datum, | ||
718 | ACPI_MIN(obj_desc->common_field.access_byte_width, | ||
719 | buffer_length - buffer_offset)); | ||
720 | |||
721 | return_ACPI_STATUS (AE_OK); | ||
722 | } | ||
723 | |||
724 | |||
725 | /******************************************************************************* | ||
726 | * | ||
727 | * FUNCTION: acpi_ex_insert_into_field | ||
728 | * | ||
729 | * PARAMETERS: obj_desc - Field to be written | ||
730 | * Buffer - Data to be written | ||
731 | * buffer_length - Length of Buffer | ||
732 | * | ||
733 | * RETURN: Status | ||
734 | * | ||
735 | * DESCRIPTION: Store the Buffer contents into the given field | ||
736 | * | ||
737 | ******************************************************************************/ | ||
738 | |||
739 | acpi_status | ||
740 | acpi_ex_insert_into_field ( | ||
741 | union acpi_operand_object *obj_desc, | ||
742 | void *buffer, | ||
743 | u32 buffer_length) | ||
744 | { | ||
745 | acpi_status status; | ||
746 | acpi_integer mask; | ||
747 | acpi_integer merged_datum; | ||
748 | acpi_integer raw_datum = 0; | ||
749 | u32 field_offset = 0; | ||
750 | u32 buffer_offset = 0; | ||
751 | u32 buffer_tail_bits; | ||
752 | u32 datum_count; | ||
753 | u32 field_datum_count; | ||
754 | u32 i; | ||
755 | |||
756 | |||
757 | ACPI_FUNCTION_TRACE ("ex_insert_into_field"); | ||
758 | |||
759 | |||
760 | /* Validate input buffer */ | ||
761 | |||
762 | if (buffer_length < ACPI_ROUND_BITS_UP_TO_BYTES ( | ||
763 | obj_desc->common_field.bit_length)) { | ||
764 | ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, | ||
765 | "Field size %X (bits) is too large for buffer (%X)\n", | ||
766 | obj_desc->common_field.bit_length, buffer_length)); | ||
767 | |||
768 | return_ACPI_STATUS (AE_BUFFER_OVERFLOW); | ||
769 | } | ||
770 | |||
771 | /* Compute the number of datums (access width data items) */ | ||
772 | |||
773 | mask = ACPI_MASK_BITS_BELOW (obj_desc->common_field.start_field_bit_offset); | ||
774 | datum_count = ACPI_ROUND_UP_TO (obj_desc->common_field.bit_length, | ||
775 | obj_desc->common_field.access_bit_width); | ||
776 | field_datum_count = ACPI_ROUND_UP_TO (obj_desc->common_field.bit_length + | ||
777 | obj_desc->common_field.start_field_bit_offset, | ||
778 | obj_desc->common_field.access_bit_width); | ||
779 | |||
780 | /* Get initial Datum from the input buffer */ | ||
781 | |||
782 | ACPI_MEMCPY (&raw_datum, buffer, | ||
783 | ACPI_MIN(obj_desc->common_field.access_byte_width, | ||
784 | buffer_length - buffer_offset)); | ||
785 | |||
786 | merged_datum = raw_datum << obj_desc->common_field.start_field_bit_offset; | ||
787 | |||
788 | /* Write the entire field */ | ||
789 | |||
790 | for (i = 1; i < field_datum_count; i++) { | ||
791 | /* Write merged datum to the target field */ | ||
792 | |||
793 | merged_datum &= mask; | ||
794 | status = acpi_ex_write_with_update_rule (obj_desc, mask, merged_datum, field_offset); | ||
795 | if (ACPI_FAILURE (status)) { | ||
796 | return_ACPI_STATUS (status); | ||
797 | } | ||
798 | |||
799 | /* Start new output datum by merging with previous input datum */ | ||
800 | |||
801 | field_offset += obj_desc->common_field.access_byte_width; | ||
802 | merged_datum = raw_datum >> | ||
803 | (obj_desc->common_field.access_bit_width - obj_desc->common_field.start_field_bit_offset); | ||
804 | mask = ACPI_INTEGER_MAX; | ||
805 | |||
806 | if (i == datum_count) { | ||
807 | break; | ||
808 | } | ||
809 | |||
810 | /* Get the next input datum from the buffer */ | ||
811 | |||
812 | buffer_offset += obj_desc->common_field.access_byte_width; | ||
813 | ACPI_MEMCPY (&raw_datum, ((char *) buffer) + buffer_offset, | ||
814 | ACPI_MIN(obj_desc->common_field.access_byte_width, | ||
815 | buffer_length - buffer_offset)); | ||
816 | merged_datum |= raw_datum << obj_desc->common_field.start_field_bit_offset; | ||
817 | } | ||
818 | |||
819 | /* Mask off any extra bits in the last datum */ | ||
820 | |||
821 | buffer_tail_bits = (obj_desc->common_field.bit_length + | ||
822 | obj_desc->common_field.start_field_bit_offset) % obj_desc->common_field.access_bit_width; | ||
823 | if (buffer_tail_bits) { | ||
824 | mask &= ACPI_MASK_BITS_ABOVE (buffer_tail_bits); | ||
825 | } | ||
826 | |||
827 | /* Write the last datum to the field */ | ||
828 | |||
829 | merged_datum &= mask; | ||
830 | status = acpi_ex_write_with_update_rule (obj_desc, mask, merged_datum, field_offset); | ||
831 | |||
832 | return_ACPI_STATUS (status); | ||
833 | } | ||
834 | |||
835 | |||