merge ROS Shell without integrated explorer part into trunk
[reactos.git] / reactos / drivers / bus / acpi / executer / amfldio.c
1 /******************************************************************************
2 *
3 * Module Name: amfldio - Aml Field I/O
4 * $Revision: 1.1 $
5 *
6 *****************************************************************************/
7
8 /*
9 * Copyright (C) 2000, 2001 R. Byron Moore
10 *
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.
15 *
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.
20 *
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
24 */
25
26
27 #include "acpi.h"
28 #include "acinterp.h"
29 #include "amlcode.h"
30 #include "acnamesp.h"
31 #include "achware.h"
32 #include "acevents.h"
33
34
35 #define _COMPONENT ACPI_EXECUTER
36 MODULE_NAME ("amfldio")
37
38
39 /*******************************************************************************
40 *
41 * FUNCTION: Acpi_aml_read_field_data
42 *
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)
46 *
47 * RETURN: Status
48 *
49 * DESCRIPTION: Retrieve the value of the given field
50 *
51 ******************************************************************************/
52
53 ACPI_STATUS
54 acpi_aml_read_field_data (
55 ACPI_OPERAND_OBJECT *obj_desc,
56 u32 field_byte_offset,
57 u32 field_bit_width,
58 u32 *value)
59 {
60 ACPI_STATUS status;
61 ACPI_OPERAND_OBJECT *rgn_desc = NULL;
62 ACPI_PHYSICAL_ADDRESS address;
63 u32 local_value = 0;
64 u32 field_byte_width;
65
66
67 /* Obj_desc is validated by callers */
68
69 if (obj_desc) {
70 rgn_desc = obj_desc->field.container;
71 }
72
73
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)) {
77 return (status);
78 }
79
80 /* Setup_field validated Rgn_desc and Field_bit_width */
81
82 if (!value) {
83 value = &local_value; /* support reads without saving value */
84 }
85
86
87 /*
88 * Set offset to next multiple of field width,
89 * add region base address and offset within the field
90 */
91 address = rgn_desc->region.address +
92 (obj_desc->field.offset * field_byte_width) +
93 field_byte_offset;
94
95
96 /* Invoke the appropriate Address_space/Op_region handler */
97
98 status = acpi_ev_address_space_dispatch (rgn_desc, ADDRESS_SPACE_READ,
99 address, field_bit_width, value);
100
101
102
103 return (status);
104 }
105
106
107 /*******************************************************************************
108 *
109 * FUNCTION: Acpi_aml_read_field
110 *
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)
114 *
115 * RETURN: Status
116 *
117 * DESCRIPTION: Retrieve the value of the given field
118 *
119 ******************************************************************************/
120
121 ACPI_STATUS
122 acpi_aml_read_field (
123 ACPI_OPERAND_OBJECT *obj_desc,
124 void *buffer,
125 u32 buffer_length,
126 u32 byte_length,
127 u32 datum_length,
128 u32 bit_granularity,
129 u32 byte_granularity)
130 {
131 ACPI_STATUS status;
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;
137 u32 mask;
138 u32 merged_datum = 0;
139
140
141 /*
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).
145 */
146
147 MEMSET (buffer, 0, buffer_length);
148
149 /* Read the first raw datum to prime the loop */
150
151 this_field_byte_offset = 0;
152 this_field_datum_offset= 0;
153
154 status = acpi_aml_read_field_data (obj_desc, this_field_byte_offset, bit_granularity,
155 &previous_raw_datum);
156 if (ACPI_FAILURE (status)) {
157 goto cleanup;
158 }
159
160 /* We might actually be done if the request fits in one datum */
161
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;
166
167 merged_datum = (merged_datum >> obj_desc->field.bit_offset);
168
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;
173 }
174
175
176 /*
177 * Place the Merged_datum into the proper format and return buffer
178 * field
179 */
180
181 switch (byte_granularity) {
182 case 1:
183 ((u8 *) buffer) [this_field_datum_offset] = (u8) merged_datum;
184 break;
185
186 case 2:
187 MOVE_UNALIGNED16_TO_16 (&(((u16 *) buffer)[this_field_datum_offset]), &merged_datum);
188 break;
189
190 case 4:
191 MOVE_UNALIGNED32_TO_32 (&(((u32 *) buffer)[this_field_datum_offset]), &merged_datum);
192 break;
193 }
194
195 this_field_byte_offset = 1;
196 this_field_datum_offset = 1;
197 }
198
199 else {
200 /* We need to get more raw data to complete one or more field data */
201
202 while (this_field_datum_offset < datum_length) {
203 /*
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.
207 *
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,
210 */
211 if ((obj_desc->field.bit_offset != 0) ||
212 ((obj_desc->field.bit_offset == 0) &&
213 (this_field_datum_offset < (datum_length -1)))) {
214 /*
215 * Get the next raw datum, it contains some or all bits
216 * of the current field datum
217 */
218
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)) {
223 goto cleanup;
224 }
225
226 /* Before merging the data, make sure the unused bits are clear */
227
228 switch (byte_granularity) {
229 case 1:
230 this_raw_datum &= 0x000000FF;
231 previous_raw_datum &= 0x000000FF;
232 break;
233
234 case 2:
235 this_raw_datum &= 0x0000FFFF;
236 previous_raw_datum &= 0x0000FFFF;
237 break;
238 }
239 }
240
241
242 /*
243 * Put together bits of the two raw data to make a complete
244 * field datum
245 */
246
247
248 if (obj_desc->field.bit_offset != 0) {
249 merged_datum =
250 (previous_raw_datum >> obj_desc->field.bit_offset) |
251 (this_raw_datum << (bit_granularity - obj_desc->field.bit_offset));
252 }
253
254 else {
255 merged_datum = previous_raw_datum;
256 }
257
258 /*
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.
264 */
265 switch (obj_desc->field.length) {
266 case 8:
267 merged_datum &= 0x000000FF;
268 break;
269
270 case 16:
271 merged_datum &= 0x0000FFFF;
272 break;
273 }
274
275 /*
276 * Now store the datum in the caller's buffer, according to
277 * the data type
278 */
279 switch (byte_granularity) {
280 case 1:
281 ((u8 *) buffer) [this_field_datum_offset] = (u8) merged_datum;
282 break;
283
284 case 2:
285 MOVE_UNALIGNED16_TO_16 (&(((u16 *) buffer) [this_field_datum_offset]), &merged_datum);
286 break;
287
288 case 4:
289 MOVE_UNALIGNED32_TO_32 (&(((u32 *) buffer) [this_field_datum_offset]), &merged_datum);
290 break;
291 }
292
293 /*
294 * Save the most recent datum since it contains bits of
295 * the *next* field datum
296 */
297
298 previous_raw_datum = this_raw_datum;
299
300 this_field_byte_offset += byte_granularity;
301 this_field_datum_offset++;
302
303 } /* while */
304 }
305
306 cleanup:
307
308 return (status);
309 }
310
311
312 /*******************************************************************************
313 *
314 * FUNCTION: Acpi_aml_write_field_data
315 *
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)
319 *
320 * RETURN: Status
321 *
322 * DESCRIPTION: Store the value into the given field
323 *
324 ******************************************************************************/
325
326 static ACPI_STATUS
327 acpi_aml_write_field_data (
328 ACPI_OPERAND_OBJECT *obj_desc,
329 u32 field_byte_offset,
330 u32 field_bit_width,
331 u32 value)
332 {
333 ACPI_STATUS status = AE_OK;
334 ACPI_OPERAND_OBJECT *rgn_desc = NULL;
335 ACPI_PHYSICAL_ADDRESS address;
336 u32 field_byte_width;
337
338
339 /* Obj_desc is validated by callers */
340
341 if (obj_desc) {
342 rgn_desc = obj_desc->field.container;
343 }
344
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)) {
348 return (status);
349 }
350
351
352 /*
353 * Set offset to next multiple of field width,
354 * add region base address and offset within the field
355 */
356 address = rgn_desc->region.address +
357 (obj_desc->field.offset * field_byte_width) +
358 field_byte_offset;
359
360 /* Invoke the appropriate Address_space/Op_region handler */
361
362 status = acpi_ev_address_space_dispatch (rgn_desc, ADDRESS_SPACE_WRITE,
363 address, field_bit_width, &value);
364
365
366
367 return (status);
368 }
369
370
371 /*****************************************************************************
372 *
373 * FUNCTION: Acpi_aml_write_field_data_with_update_rule
374 *
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)
378 *
379 * RETURN: Status
380 *
381 * DESCRIPTION: Apply the field update rule to a field write
382 *
383 ****************************************************************************/
384
385 static ACPI_STATUS
386 acpi_aml_write_field_data_with_update_rule (
387 ACPI_OPERAND_OBJECT *obj_desc,
388 u32 mask,
389 u32 field_value,
390 u32 this_field_byte_offset,
391 u32 bit_granularity)
392 {
393 ACPI_STATUS status = AE_OK;
394 u32 merged_value;
395 u32 current_value;
396
397
398 /* Start with the new bits */
399
400 merged_value = field_value;
401
402
403 /* Decode the update rule */
404
405 switch (obj_desc->field.update_rule) {
406
407 case UPDATE_PRESERVE:
408
409 /* Check if update rule needs to be applied (not if mask is all ones) */
410
411 /* The left shift drops the bits we want to ignore. */
412 if ((~mask << (sizeof(mask)*8 - bit_granularity)) != 0) {
413 /*
414 * Read the current contents of the byte/word/dword containing
415 * the field, and merge with the new field value.
416 */
417 status = acpi_aml_read_field_data (obj_desc, this_field_byte_offset,
418 bit_granularity, &current_value);
419 merged_value |= (current_value & ~mask);
420 }
421 break;
422
423
424 case UPDATE_WRITE_AS_ONES:
425
426 /* Set positions outside the field to all ones */
427
428 merged_value |= ~mask;
429 break;
430
431
432 case UPDATE_WRITE_AS_ZEROS:
433
434 /* Set positions outside the field to all zeros */
435
436 merged_value &= mask;
437 break;
438
439
440 default:
441 status = AE_AML_OPERAND_VALUE;
442 }
443
444
445 /* Write the merged value */
446
447 if (ACPI_SUCCESS (status)) {
448 status = acpi_aml_write_field_data (obj_desc, this_field_byte_offset,
449 bit_granularity, merged_value);
450 }
451
452 return (status);
453 }
454
455
456 /*****************************************************************************
457 *
458 * FUNCTION: Acpi_aml_write_field
459 *
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)
463 *
464 * RETURN: Status
465 *
466 * DESCRIPTION: Store the value into the given field
467 *
468 ****************************************************************************/
469
470 ACPI_STATUS
471 acpi_aml_write_field (
472 ACPI_OPERAND_OBJECT *obj_desc,
473 void *buffer,
474 u32 buffer_length,
475 u32 byte_length,
476 u32 datum_length,
477 u32 bit_granularity,
478 u32 byte_granularity)
479 {
480 ACPI_STATUS status;
481 u32 this_field_byte_offset;
482 u32 this_field_datum_offset;
483 u32 mask;
484 u32 merged_datum;
485 u32 previous_raw_datum;
486 u32 this_raw_datum;
487 u32 field_value;
488 u32 valid_field_bits;
489
490
491 /*
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 ---
495 */
496
497 this_field_byte_offset = 0;
498 this_field_datum_offset= 0;
499
500 /* Get a datum */
501
502 switch (byte_granularity) {
503 case 1:
504 previous_raw_datum = ((u8 *) buffer) [this_field_datum_offset];
505 break;
506
507 case 2:
508 MOVE_UNALIGNED16_TO_32 (&previous_raw_datum, &(((u16 *) buffer) [this_field_datum_offset]));
509 break;
510
511 case 4:
512 MOVE_UNALIGNED32_TO_32 (&previous_raw_datum, &(((u32 *) buffer) [this_field_datum_offset]));
513 break;
514
515 default:
516 status = AE_AML_OPERAND_VALUE;
517 goto cleanup;
518 }
519
520
521 /*
522 * Write a partial field datum if field does not begin on a datum boundary
523 *
524 * Construct Mask with 1 bits where the field is, 0 bits elsewhere
525 *
526 * 1) Bits above the field
527 */
528
529 mask = (((u32)(-1)) << (u32)obj_desc->field.bit_offset);
530
531 /* 2) Only the bottom 5 bits are valid for a shift operation. */
532
533 if ((obj_desc->field.bit_offset + obj_desc->field_unit.length) < 32) {
534 /* Bits above the field */
535
536 mask &= (~(((u32)(-1)) << ((u32)obj_desc->field.bit_offset +
537 (u32)obj_desc->field_unit.length)));
538 }
539
540 /* 3) Shift and mask the value into the field position */
541
542 field_value = (previous_raw_datum << obj_desc->field.bit_offset) & mask;
543
544 status = acpi_aml_write_field_data_with_update_rule (obj_desc, mask, field_value,
545 this_field_byte_offset,
546 bit_granularity);
547 if (ACPI_FAILURE (status)) {
548 goto cleanup;
549 }
550
551
552 /* If the field fits within one datum, we are done. */
553
554 if ((datum_length == 1) &&
555 ((obj_desc->field.bit_offset + obj_desc->field_unit.length) <=
556 (u16) bit_granularity)) {
557 goto cleanup;
558 }
559
560 /*
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.
563 *
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.
566 */
567
568 while (this_field_datum_offset < (datum_length - 1)) {
569 this_field_datum_offset++;
570
571 /* Get the next raw datum, it contains bits of the current field datum... */
572
573 switch (byte_granularity) {
574 case 1:
575 this_raw_datum = ((u8 *) buffer) [this_field_datum_offset];
576 break;
577
578 case 2:
579 MOVE_UNALIGNED16_TO_32 (&this_raw_datum, &(((u16 *) buffer) [this_field_datum_offset]));
580 break;
581
582 case 4:
583 MOVE_UNALIGNED32_TO_32 (&this_raw_datum, &(((u32 *) buffer) [this_field_datum_offset]));
584 break;
585
586 default:
587 status = AE_AML_OPERAND_VALUE;
588 goto cleanup;
589 }
590
591 /*
592 * Put together bits of the two raw data to make a complete field
593 * datum
594 */
595
596 if (obj_desc->field.bit_offset != 0) {
597 merged_datum =
598 (previous_raw_datum >> (bit_granularity - obj_desc->field.bit_offset)) |
599 (this_raw_datum << obj_desc->field.bit_offset);
600 }
601
602 else {
603 merged_datum = this_raw_datum;
604 }
605
606 /* Now write the completed datum */
607
608
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)) {
613 goto cleanup;
614 }
615
616
617 /*
618 * Save the most recent datum since it contains bits of
619 * the *next* field datum
620 */
621
622 previous_raw_datum = this_raw_datum;
623
624 this_field_byte_offset += byte_granularity;
625
626 } /* while */
627
628
629 /* Write a partial field datum if field does not end on a datum boundary */
630
631 if ((obj_desc->field_unit.length + obj_desc->field_unit.bit_offset) %
632 bit_granularity) {
633 switch (byte_granularity) {
634 case 1:
635 this_raw_datum = ((u8 *) buffer) [this_field_datum_offset];
636 break;
637
638 case 2:
639 MOVE_UNALIGNED16_TO_32 (&this_raw_datum, &(((u16 *) buffer) [this_field_datum_offset]));
640 break;
641
642 case 4:
643 MOVE_UNALIGNED32_TO_32 (&this_raw_datum, &(((u32 *) buffer) [this_field_datum_offset]));
644 break;
645 }
646
647 /* Construct Mask with 1 bits where the field is, 0 bits elsewhere */
648
649 valid_field_bits = ((obj_desc->field_unit.length % bit_granularity) +
650 obj_desc->field.bit_offset);
651
652 mask = (((u32) 1 << valid_field_bits) - (u32) 1);
653
654 /* Shift and mask the value into the field position */
655
656 field_value = (previous_raw_datum >>
657 (bit_granularity - obj_desc->field.bit_offset)) & mask;
658
659 status = acpi_aml_write_field_data_with_update_rule (obj_desc, mask, field_value,
660 this_field_byte_offset + byte_granularity,
661 bit_granularity);
662 if (ACPI_FAILURE (status)) {
663 goto cleanup;
664 }
665 }
666
667
668 cleanup:
669
670 return (status);
671 }
672
673