1 /******************************************************************************
3 * Module Name: amfldio - Aml Field I/O
6 *****************************************************************************/
9 * Copyright (C) 2000, 2001 R. Byron Moore
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 #define _COMPONENT ACPI_EXECUTER
36 MODULE_NAME ("amfldio")
39 /*******************************************************************************
41 * FUNCTION: Acpi_aml_read_field_data
43 * PARAMETERS: *Obj_desc - Field to be read
44 * *Value - Where to store value
45 * Field_bit_width - Field Width in bits (8, 16, or 32)
49 * DESCRIPTION: Retrieve the value of the given field
51 ******************************************************************************/
54 acpi_aml_read_field_data (
55 ACPI_OPERAND_OBJECT
*obj_desc
,
56 u32 field_byte_offset
,
61 ACPI_OPERAND_OBJECT
*rgn_desc
= NULL
;
62 ACPI_PHYSICAL_ADDRESS address
;
67 /* Obj_desc is validated by callers */
70 rgn_desc
= obj_desc
->field
.container
;
74 field_byte_width
= DIV_8 (field_bit_width
);
75 status
= acpi_aml_setup_field (obj_desc
, rgn_desc
, field_bit_width
);
76 if (ACPI_FAILURE (status
)) {
80 /* Setup_field validated Rgn_desc and Field_bit_width */
83 value
= &local_value
; /* support reads without saving value */
88 * Set offset to next multiple of field width,
89 * add region base address and offset within the field
91 address
= rgn_desc
->region
.address
+
92 (obj_desc
->field
.offset
* field_byte_width
) +
96 /* Invoke the appropriate Address_space/Op_region handler */
98 status
= acpi_ev_address_space_dispatch (rgn_desc
, ADDRESS_SPACE_READ
,
99 address
, field_bit_width
, value
);
107 /*******************************************************************************
109 * FUNCTION: Acpi_aml_read_field
111 * PARAMETERS: *Obj_desc - Field to be read
112 * *Value - Where to store value
113 * Field_bit_width - Field Width in bits (8, 16, or 32)
117 * DESCRIPTION: Retrieve the value of the given field
119 ******************************************************************************/
122 acpi_aml_read_field (
123 ACPI_OPERAND_OBJECT
*obj_desc
,
129 u32 byte_granularity
)
132 u32 this_field_byte_offset
;
133 u32 this_field_datum_offset
;
134 u32 previous_raw_datum
;
135 u32 this_raw_datum
= 0;
136 u32 valid_field_bits
;
138 u32 merged_datum
= 0;
142 * Clear the caller's buffer (the whole buffer length as given)
143 * This is very important, especially in the cases where a byte is read,
144 * but the buffer is really a u32 (4 bytes).
147 MEMSET (buffer
, 0, buffer_length
);
149 /* Read the first raw datum to prime the loop */
151 this_field_byte_offset
= 0;
152 this_field_datum_offset
= 0;
154 status
= acpi_aml_read_field_data (obj_desc
, this_field_byte_offset
, bit_granularity
,
155 &previous_raw_datum
);
156 if (ACPI_FAILURE (status
)) {
160 /* We might actually be done if the request fits in one datum */
162 if ((datum_length
== 1) &&
163 ((obj_desc
->field
.bit_offset
+ obj_desc
->field_unit
.length
) <=
164 (u16
) bit_granularity
)) {
165 merged_datum
= previous_raw_datum
;
167 merged_datum
= (merged_datum
>> obj_desc
->field
.bit_offset
);
169 valid_field_bits
= obj_desc
->field_unit
.length
% bit_granularity
;
170 if (valid_field_bits
) {
171 mask
= (((u32
) 1 << valid_field_bits
) - (u32
) 1);
172 merged_datum
&= mask
;
177 * Place the Merged_datum into the proper format and return buffer
181 switch (byte_granularity
) {
183 ((u8
*) buffer
) [this_field_datum_offset
] = (u8
) merged_datum
;
187 MOVE_UNALIGNED16_TO_16 (&(((u16
*) buffer
)[this_field_datum_offset
]), &merged_datum
);
191 MOVE_UNALIGNED32_TO_32 (&(((u32
*) buffer
)[this_field_datum_offset
]), &merged_datum
);
195 this_field_byte_offset
= 1;
196 this_field_datum_offset
= 1;
200 /* We need to get more raw data to complete one or more field data */
202 while (this_field_datum_offset
< datum_length
) {
204 * If the field is aligned on a byte boundary, we don't want
205 * to perform a final read, since this would potentially read
206 * past the end of the region.
208 * TBD: [Investigate] It may make more sense to just split the aligned
209 * and non-aligned cases since the aligned case is so very simple,
211 if ((obj_desc
->field
.bit_offset
!= 0) ||
212 ((obj_desc
->field
.bit_offset
== 0) &&
213 (this_field_datum_offset
< (datum_length
-1)))) {
215 * Get the next raw datum, it contains some or all bits
216 * of the current field datum
219 status
= acpi_aml_read_field_data (obj_desc
,
220 this_field_byte_offset
+ byte_granularity
,
221 bit_granularity
, &this_raw_datum
);
222 if (ACPI_FAILURE (status
)) {
226 /* Before merging the data, make sure the unused bits are clear */
228 switch (byte_granularity
) {
230 this_raw_datum
&= 0x000000FF;
231 previous_raw_datum
&= 0x000000FF;
235 this_raw_datum
&= 0x0000FFFF;
236 previous_raw_datum
&= 0x0000FFFF;
243 * Put together bits of the two raw data to make a complete
248 if (obj_desc
->field
.bit_offset
!= 0) {
250 (previous_raw_datum
>> obj_desc
->field
.bit_offset
) |
251 (this_raw_datum
<< (bit_granularity
- obj_desc
->field
.bit_offset
));
255 merged_datum
= previous_raw_datum
;
259 * Prepare the merged datum for storing into the caller's
260 * buffer. It is possible to have a 32-bit buffer
261 * (Byte_granularity == 4), but a Obj_desc->Field.Length
262 * of 8 or 16, meaning that the upper bytes of merged data
263 * are undesired. This section fixes that.
265 switch (obj_desc
->field
.length
) {
267 merged_datum
&= 0x000000FF;
271 merged_datum
&= 0x0000FFFF;
276 * Now store the datum in the caller's buffer, according to
279 switch (byte_granularity
) {
281 ((u8
*) buffer
) [this_field_datum_offset
] = (u8
) merged_datum
;
285 MOVE_UNALIGNED16_TO_16 (&(((u16
*) buffer
) [this_field_datum_offset
]), &merged_datum
);
289 MOVE_UNALIGNED32_TO_32 (&(((u32
*) buffer
) [this_field_datum_offset
]), &merged_datum
);
294 * Save the most recent datum since it contains bits of
295 * the *next* field datum
298 previous_raw_datum
= this_raw_datum
;
300 this_field_byte_offset
+= byte_granularity
;
301 this_field_datum_offset
++;
312 /*******************************************************************************
314 * FUNCTION: Acpi_aml_write_field_data
316 * PARAMETERS: *Obj_desc - Field to be set
317 * Value - Value to store
318 * Field_bit_width - Field Width in bits (8, 16, or 32)
322 * DESCRIPTION: Store the value into the given field
324 ******************************************************************************/
327 acpi_aml_write_field_data (
328 ACPI_OPERAND_OBJECT
*obj_desc
,
329 u32 field_byte_offset
,
333 ACPI_STATUS status
= AE_OK
;
334 ACPI_OPERAND_OBJECT
*rgn_desc
= NULL
;
335 ACPI_PHYSICAL_ADDRESS address
;
336 u32 field_byte_width
;
339 /* Obj_desc is validated by callers */
342 rgn_desc
= obj_desc
->field
.container
;
345 field_byte_width
= DIV_8 (field_bit_width
);
346 status
= acpi_aml_setup_field (obj_desc
, rgn_desc
, field_bit_width
);
347 if (ACPI_FAILURE (status
)) {
353 * Set offset to next multiple of field width,
354 * add region base address and offset within the field
356 address
= rgn_desc
->region
.address
+
357 (obj_desc
->field
.offset
* field_byte_width
) +
360 /* Invoke the appropriate Address_space/Op_region handler */
362 status
= acpi_ev_address_space_dispatch (rgn_desc
, ADDRESS_SPACE_WRITE
,
363 address
, field_bit_width
, &value
);
371 /*****************************************************************************
373 * FUNCTION: Acpi_aml_write_field_data_with_update_rule
375 * PARAMETERS: *Obj_desc - Field to be set
376 * Value - Value to store
377 * Field_bit_width - Field Width in bits (8, 16, or 32)
381 * DESCRIPTION: Apply the field update rule to a field write
383 ****************************************************************************/
386 acpi_aml_write_field_data_with_update_rule (
387 ACPI_OPERAND_OBJECT
*obj_desc
,
390 u32 this_field_byte_offset
,
393 ACPI_STATUS status
= AE_OK
;
398 /* Start with the new bits */
400 merged_value
= field_value
;
403 /* Decode the update rule */
405 switch (obj_desc
->field
.update_rule
) {
407 case UPDATE_PRESERVE
:
409 /* Check if update rule needs to be applied (not if mask is all ones) */
411 /* The left shift drops the bits we want to ignore. */
412 if ((~mask
<< (sizeof(mask
)*8 - bit_granularity
)) != 0) {
414 * Read the current contents of the byte/word/dword containing
415 * the field, and merge with the new field value.
417 status
= acpi_aml_read_field_data (obj_desc
, this_field_byte_offset
,
418 bit_granularity
, ¤t_value
);
419 merged_value
|= (current_value
& ~mask
);
424 case UPDATE_WRITE_AS_ONES
:
426 /* Set positions outside the field to all ones */
428 merged_value
|= ~mask
;
432 case UPDATE_WRITE_AS_ZEROS
:
434 /* Set positions outside the field to all zeros */
436 merged_value
&= mask
;
441 status
= AE_AML_OPERAND_VALUE
;
445 /* Write the merged value */
447 if (ACPI_SUCCESS (status
)) {
448 status
= acpi_aml_write_field_data (obj_desc
, this_field_byte_offset
,
449 bit_granularity
, merged_value
);
456 /*****************************************************************************
458 * FUNCTION: Acpi_aml_write_field
460 * PARAMETERS: *Obj_desc - Field to be set
461 * Value - Value to store
462 * Field_bit_width - Field Width in bits (8, 16, or 32)
466 * DESCRIPTION: Store the value into the given field
468 ****************************************************************************/
471 acpi_aml_write_field (
472 ACPI_OPERAND_OBJECT
*obj_desc
,
478 u32 byte_granularity
)
481 u32 this_field_byte_offset
;
482 u32 this_field_datum_offset
;
485 u32 previous_raw_datum
;
488 u32 valid_field_bits
;
492 * Break the request into up to three parts:
493 * non-aligned part at start, aligned part in middle, non-aligned part
494 * at end --- Just like an I/O request ---
497 this_field_byte_offset
= 0;
498 this_field_datum_offset
= 0;
502 switch (byte_granularity
) {
504 previous_raw_datum
= ((u8
*) buffer
) [this_field_datum_offset
];
508 MOVE_UNALIGNED16_TO_32 (&previous_raw_datum
, &(((u16
*) buffer
) [this_field_datum_offset
]));
512 MOVE_UNALIGNED32_TO_32 (&previous_raw_datum
, &(((u32
*) buffer
) [this_field_datum_offset
]));
516 status
= AE_AML_OPERAND_VALUE
;
522 * Write a partial field datum if field does not begin on a datum boundary
524 * Construct Mask with 1 bits where the field is, 0 bits elsewhere
526 * 1) Bits above the field
529 mask
= (((u32
)(-1)) << (u32
)obj_desc
->field
.bit_offset
);
531 /* 2) Only the bottom 5 bits are valid for a shift operation. */
533 if ((obj_desc
->field
.bit_offset
+ obj_desc
->field_unit
.length
) < 32) {
534 /* Bits above the field */
536 mask
&= (~(((u32
)(-1)) << ((u32
)obj_desc
->field
.bit_offset
+
537 (u32
)obj_desc
->field_unit
.length
)));
540 /* 3) Shift and mask the value into the field position */
542 field_value
= (previous_raw_datum
<< obj_desc
->field
.bit_offset
) & mask
;
544 status
= acpi_aml_write_field_data_with_update_rule (obj_desc
, mask
, field_value
,
545 this_field_byte_offset
,
547 if (ACPI_FAILURE (status
)) {
552 /* If the field fits within one datum, we are done. */
554 if ((datum_length
== 1) &&
555 ((obj_desc
->field
.bit_offset
+ obj_desc
->field_unit
.length
) <=
556 (u16
) bit_granularity
)) {
561 * We don't need to worry about the update rule for these data, because
562 * all of the bits are part of the field.
564 * Can't write the last datum, however, because it might contain bits that
565 * are not part of the field -- the update rule must be applied.
568 while (this_field_datum_offset
< (datum_length
- 1)) {
569 this_field_datum_offset
++;
571 /* Get the next raw datum, it contains bits of the current field datum... */
573 switch (byte_granularity
) {
575 this_raw_datum
= ((u8
*) buffer
) [this_field_datum_offset
];
579 MOVE_UNALIGNED16_TO_32 (&this_raw_datum
, &(((u16
*) buffer
) [this_field_datum_offset
]));
583 MOVE_UNALIGNED32_TO_32 (&this_raw_datum
, &(((u32
*) buffer
) [this_field_datum_offset
]));
587 status
= AE_AML_OPERAND_VALUE
;
592 * Put together bits of the two raw data to make a complete field
596 if (obj_desc
->field
.bit_offset
!= 0) {
598 (previous_raw_datum
>> (bit_granularity
- obj_desc
->field
.bit_offset
)) |
599 (this_raw_datum
<< obj_desc
->field
.bit_offset
);
603 merged_datum
= this_raw_datum
;
606 /* Now write the completed datum */
609 status
= acpi_aml_write_field_data (obj_desc
,
610 this_field_byte_offset
+ byte_granularity
,
611 bit_granularity
, merged_datum
);
612 if (ACPI_FAILURE (status
)) {
618 * Save the most recent datum since it contains bits of
619 * the *next* field datum
622 previous_raw_datum
= this_raw_datum
;
624 this_field_byte_offset
+= byte_granularity
;
629 /* Write a partial field datum if field does not end on a datum boundary */
631 if ((obj_desc
->field_unit
.length
+ obj_desc
->field_unit
.bit_offset
) %
633 switch (byte_granularity
) {
635 this_raw_datum
= ((u8
*) buffer
) [this_field_datum_offset
];
639 MOVE_UNALIGNED16_TO_32 (&this_raw_datum
, &(((u16
*) buffer
) [this_field_datum_offset
]));
643 MOVE_UNALIGNED32_TO_32 (&this_raw_datum
, &(((u32
*) buffer
) [this_field_datum_offset
]));
647 /* Construct Mask with 1 bits where the field is, 0 bits elsewhere */
649 valid_field_bits
= ((obj_desc
->field_unit
.length
% bit_granularity
) +
650 obj_desc
->field
.bit_offset
);
652 mask
= (((u32
) 1 << valid_field_bits
) - (u32
) 1);
654 /* Shift and mask the value into the field position */
656 field_value
= (previous_raw_datum
>>
657 (bit_granularity
- obj_desc
->field
.bit_offset
)) & mask
;
659 status
= acpi_aml_write_field_data_with_update_rule (obj_desc
, mask
, field_value
,
660 this_field_byte_offset
+ byte_granularity
,
662 if (ACPI_FAILURE (status
)) {