1 /*******************************************************************************
2 *
3 * Module Name: hwregs - Read/write access functions for the various ACPI
4 * control and status registers.
5 *
6 ******************************************************************************/
7
8 /*
9 * Copyright (C) 2000 - 2016, Intel Corp.
10 * All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions, and the following disclaimer,
17 * without modification.
18 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
19 * substantially similar to the "NO WARRANTY" disclaimer below
20 * ("Disclaimer") and any redistribution must be conditioned upon
21 * including a substantially similar Disclaimer requirement for further
22 * binary redistribution.
23 * 3. Neither the names of the above-listed copyright holders nor the names
24 * of any contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * Alternatively, this software may be distributed under the terms of the
28 * GNU General Public License ("GPL") version 2 as published by the Free
29 * Software Foundation.
30 *
31 * NO WARRANTY
32 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
33 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
34 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
35 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
36 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
41 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42 * POSSIBILITY OF SUCH DAMAGES.
43 */
44
45 #include "acpi.h"
46 #include "accommon.h"
47 #include "acevents.h"
48
49 #define _COMPONENT ACPI_HARDWARE
50 ACPI_MODULE_NAME ("hwregs")
51
52
53 #if (!ACPI_REDUCED_HARDWARE)
54
55 /* Local Prototypes */
56
57 static ACPI_STATUS
58 AcpiHwReadMultiple (
59 UINT32 *Value,
60 ACPI_GENERIC_ADDRESS *RegisterA,
61 ACPI_GENERIC_ADDRESS *RegisterB);
62
63 static ACPI_STATUS
64 AcpiHwWriteMultiple (
65 UINT32 Value,
66 ACPI_GENERIC_ADDRESS *RegisterA,
67 ACPI_GENERIC_ADDRESS *RegisterB);
68
69 #endif /* !ACPI_REDUCED_HARDWARE */
70
71
72 /******************************************************************************
73 *
74 * FUNCTION: AcpiHwValidateRegister
75 *
76 * PARAMETERS: Reg - GAS register structure
77 * MaxBitWidth - Max BitWidth supported (32 or 64)
78 * Address - Pointer to where the gas->address
79 * is returned
80 *
81 * RETURN: Status
82 *
83 * DESCRIPTION: Validate the contents of a GAS register. Checks the GAS
84 * pointer, Address, SpaceId, BitWidth, and BitOffset.
85 *
86 ******************************************************************************/
87
88 ACPI_STATUS
AcpiHwValidateRegister(ACPI_GENERIC_ADDRESS * Reg,UINT8 MaxBitWidth,UINT64 * Address)89 AcpiHwValidateRegister (
90 ACPI_GENERIC_ADDRESS *Reg,
91 UINT8 MaxBitWidth,
92 UINT64 *Address)
93 {
94
95 /* Must have a valid pointer to a GAS structure */
96
97 if (!Reg)
98 {
99 return (AE_BAD_PARAMETER);
100 }
101
102 /*
103 * Copy the target address. This handles possible alignment issues.
104 * Address must not be null. A null address also indicates an optional
105 * ACPI register that is not supported, so no error message.
106 */
107 ACPI_MOVE_64_TO_64 (Address, &Reg->Address);
108 if (!(*Address))
109 {
110 return (AE_BAD_ADDRESS);
111 }
112
113 /* Validate the SpaceID */
114
115 if ((Reg->SpaceId != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
116 (Reg->SpaceId != ACPI_ADR_SPACE_SYSTEM_IO))
117 {
118 ACPI_ERROR ((AE_INFO,
119 "Unsupported address space: 0x%X", Reg->SpaceId));
120 return (AE_SUPPORT);
121 }
122
123 /* Validate the BitWidth */
124
125 if ((Reg->BitWidth != 8) &&
126 (Reg->BitWidth != 16) &&
127 (Reg->BitWidth != 32) &&
128 (Reg->BitWidth != MaxBitWidth))
129 {
130 ACPI_ERROR ((AE_INFO,
131 "Unsupported register bit width: 0x%X", Reg->BitWidth));
132 return (AE_SUPPORT);
133 }
134
135 /* Validate the BitOffset. Just a warning for now. */
136
137 if (Reg->BitOffset != 0)
138 {
139 ACPI_WARNING ((AE_INFO,
140 "Unsupported register bit offset: 0x%X", Reg->BitOffset));
141 }
142
143 return (AE_OK);
144 }
145
146
147 /******************************************************************************
148 *
149 * FUNCTION: AcpiHwRead
150 *
151 * PARAMETERS: Value - Where the value is returned
152 * Reg - GAS register structure
153 *
154 * RETURN: Status
155 *
156 * DESCRIPTION: Read from either memory or IO space. This is a 32-bit max
157 * version of AcpiRead, used internally since the overhead of
158 * 64-bit values is not needed.
159 *
160 * LIMITATIONS: <These limitations also apply to AcpiHwWrite>
161 * BitWidth must be exactly 8, 16, or 32.
162 * SpaceID must be SystemMemory or SystemIO.
163 * BitOffset and AccessWidth are currently ignored, as there has
164 * not been a need to implement these.
165 *
166 ******************************************************************************/
167
168 ACPI_STATUS
AcpiHwRead(UINT32 * Value,ACPI_GENERIC_ADDRESS * Reg)169 AcpiHwRead (
170 UINT32 *Value,
171 ACPI_GENERIC_ADDRESS *Reg)
172 {
173 UINT64 Address;
174 UINT64 Value64;
175 ACPI_STATUS Status;
176
177
178 ACPI_FUNCTION_NAME (HwRead);
179
180
181 /* Validate contents of the GAS register */
182
183 Status = AcpiHwValidateRegister (Reg, 32, &Address);
184 if (ACPI_FAILURE (Status))
185 {
186 return (Status);
187 }
188
189 /* Initialize entire 32-bit return value to zero */
190
191 *Value = 0;
192
193 /*
194 * Two address spaces supported: Memory or IO. PCI_Config is
195 * not supported here because the GAS structure is insufficient
196 */
197 if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_MEMORY)
198 {
199 Status = AcpiOsReadMemory ((ACPI_PHYSICAL_ADDRESS)
200 Address, &Value64, Reg->BitWidth);
201
202 *Value = (UINT32) Value64;
203 }
204 else /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
205 {
206 Status = AcpiHwReadPort ((ACPI_IO_ADDRESS)
207 Address, Value, Reg->BitWidth);
208 }
209
210 ACPI_DEBUG_PRINT ((ACPI_DB_IO,
211 "Read: %8.8X width %2d from %8.8X%8.8X (%s)\n",
212 *Value, Reg->BitWidth, ACPI_FORMAT_UINT64 (Address),
213 AcpiUtGetRegionName (Reg->SpaceId)));
214
215 return (Status);
216 }
217
218
219 /******************************************************************************
220 *
221 * FUNCTION: AcpiHwWrite
222 *
223 * PARAMETERS: Value - Value to be written
224 * Reg - GAS register structure
225 *
226 * RETURN: Status
227 *
228 * DESCRIPTION: Write to either memory or IO space. This is a 32-bit max
229 * version of AcpiWrite, used internally since the overhead of
230 * 64-bit values is not needed.
231 *
232 ******************************************************************************/
233
234 ACPI_STATUS
AcpiHwWrite(UINT32 Value,ACPI_GENERIC_ADDRESS * Reg)235 AcpiHwWrite (
236 UINT32 Value,
237 ACPI_GENERIC_ADDRESS *Reg)
238 {
239 UINT64 Address;
240 ACPI_STATUS Status;
241
242
243 ACPI_FUNCTION_NAME (HwWrite);
244
245
246 /* Validate contents of the GAS register */
247
248 Status = AcpiHwValidateRegister (Reg, 32, &Address);
249 if (ACPI_FAILURE (Status))
250 {
251 return (Status);
252 }
253
254 /*
255 * Two address spaces supported: Memory or IO. PCI_Config is
256 * not supported here because the GAS structure is insufficient
257 */
258 if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_MEMORY)
259 {
260 Status = AcpiOsWriteMemory ((ACPI_PHYSICAL_ADDRESS)
261 Address, (UINT64) Value, Reg->BitWidth);
262 }
263 else /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
264 {
265 Status = AcpiHwWritePort ((ACPI_IO_ADDRESS)
266 Address, Value, Reg->BitWidth);
267 }
268
269 ACPI_DEBUG_PRINT ((ACPI_DB_IO,
270 "Wrote: %8.8X width %2d to %8.8X%8.8X (%s)\n",
271 Value, Reg->BitWidth, ACPI_FORMAT_UINT64 (Address),
272 AcpiUtGetRegionName (Reg->SpaceId)));
273
274 return (Status);
275 }
276
277
278 #if (!ACPI_REDUCED_HARDWARE)
279 /*******************************************************************************
280 *
281 * FUNCTION: AcpiHwClearAcpiStatus
282 *
283 * PARAMETERS: None
284 *
285 * RETURN: Status
286 *
287 * DESCRIPTION: Clears all fixed and general purpose status bits
288 *
289 ******************************************************************************/
290
291 ACPI_STATUS
AcpiHwClearAcpiStatus(void)292 AcpiHwClearAcpiStatus (
293 void)
294 {
295 ACPI_STATUS Status;
296 ACPI_CPU_FLAGS LockFlags = 0;
297
298
299 ACPI_FUNCTION_TRACE (HwClearAcpiStatus);
300
301
302 ACPI_DEBUG_PRINT ((ACPI_DB_IO, "About to write %04X to %8.8X%8.8X\n",
303 ACPI_BITMASK_ALL_FIXED_STATUS,
304 ACPI_FORMAT_UINT64 (AcpiGbl_XPm1aStatus.Address)));
305
306 LockFlags = AcpiOsAcquireLock (AcpiGbl_HardwareLock);
307
308 /* Clear the fixed events in PM1 A/B */
309
310 Status = AcpiHwRegisterWrite (ACPI_REGISTER_PM1_STATUS,
311 ACPI_BITMASK_ALL_FIXED_STATUS);
312
313 AcpiOsReleaseLock (AcpiGbl_HardwareLock, LockFlags);
314
315 if (ACPI_FAILURE (Status))
316 {
317 goto Exit;
318 }
319
320 /* Clear the GPE Bits in all GPE registers in all GPE blocks */
321
322 Status = AcpiEvWalkGpeList (AcpiHwClearGpeBlock, NULL);
323
324 Exit:
325 return_ACPI_STATUS (Status);
326 }
327
328
329 /*******************************************************************************
330 *
331 * FUNCTION: AcpiHwGetBitRegisterInfo
332 *
333 * PARAMETERS: RegisterId - Index of ACPI Register to access
334 *
335 * RETURN: The bitmask to be used when accessing the register
336 *
337 * DESCRIPTION: Map RegisterId into a register bitmask.
338 *
339 ******************************************************************************/
340
341 ACPI_BIT_REGISTER_INFO *
AcpiHwGetBitRegisterInfo(UINT32 RegisterId)342 AcpiHwGetBitRegisterInfo (
343 UINT32 RegisterId)
344 {
345 ACPI_FUNCTION_ENTRY ();
346
347
348 if (RegisterId > ACPI_BITREG_MAX)
349 {
350 ACPI_ERROR ((AE_INFO, "Invalid BitRegister ID: 0x%X", RegisterId));
351 return (NULL);
352 }
353
354 return (&AcpiGbl_BitRegisterInfo[RegisterId]);
355 }
356
357
358 /******************************************************************************
359 *
360 * FUNCTION: AcpiHwWritePm1Control
361 *
362 * PARAMETERS: Pm1aControl - Value to be written to PM1A control
363 * Pm1bControl - Value to be written to PM1B control
364 *
365 * RETURN: Status
366 *
367 * DESCRIPTION: Write the PM1 A/B control registers. These registers are
368 * different than than the PM1 A/B status and enable registers
369 * in that different values can be written to the A/B registers.
370 * Most notably, the SLP_TYP bits can be different, as per the
371 * values returned from the _Sx predefined methods.
372 *
373 ******************************************************************************/
374
375 ACPI_STATUS
AcpiHwWritePm1Control(UINT32 Pm1aControl,UINT32 Pm1bControl)376 AcpiHwWritePm1Control (
377 UINT32 Pm1aControl,
378 UINT32 Pm1bControl)
379 {
380 ACPI_STATUS Status;
381
382
383 ACPI_FUNCTION_TRACE (HwWritePm1Control);
384
385
386 Status = AcpiHwWrite (Pm1aControl, &AcpiGbl_FADT.XPm1aControlBlock);
387 if (ACPI_FAILURE (Status))
388 {
389 return_ACPI_STATUS (Status);
390 }
391
392 if (AcpiGbl_FADT.XPm1bControlBlock.Address)
393 {
394 Status = AcpiHwWrite (Pm1bControl, &AcpiGbl_FADT.XPm1bControlBlock);
395 }
396 return_ACPI_STATUS (Status);
397 }
398
399
400 /******************************************************************************
401 *
402 * FUNCTION: AcpiHwRegisterRead
403 *
404 * PARAMETERS: RegisterId - ACPI Register ID
405 * ReturnValue - Where the register value is returned
406 *
407 * RETURN: Status and the value read.
408 *
409 * DESCRIPTION: Read from the specified ACPI register
410 *
411 ******************************************************************************/
412
413 ACPI_STATUS
AcpiHwRegisterRead(UINT32 RegisterId,UINT32 * ReturnValue)414 AcpiHwRegisterRead (
415 UINT32 RegisterId,
416 UINT32 *ReturnValue)
417 {
418 UINT32 Value = 0;
419 ACPI_STATUS Status;
420
421
422 ACPI_FUNCTION_TRACE (HwRegisterRead);
423
424
425 switch (RegisterId)
426 {
427 case ACPI_REGISTER_PM1_STATUS: /* PM1 A/B: 16-bit access each */
428
429 Status = AcpiHwReadMultiple (&Value,
430 &AcpiGbl_XPm1aStatus,
431 &AcpiGbl_XPm1bStatus);
432 break;
433
434 case ACPI_REGISTER_PM1_ENABLE: /* PM1 A/B: 16-bit access each */
435
436 Status = AcpiHwReadMultiple (&Value,
437 &AcpiGbl_XPm1aEnable,
438 &AcpiGbl_XPm1bEnable);
439 break;
440
441 case ACPI_REGISTER_PM1_CONTROL: /* PM1 A/B: 16-bit access each */
442
443 Status = AcpiHwReadMultiple (&Value,
444 &AcpiGbl_FADT.XPm1aControlBlock,
445 &AcpiGbl_FADT.XPm1bControlBlock);
446
447 /*
448 * Zero the write-only bits. From the ACPI specification, "Hardware
449 * Write-Only Bits": "Upon reads to registers with write-only bits,
450 * software masks out all write-only bits."
451 */
452 Value &= ~ACPI_PM1_CONTROL_WRITEONLY_BITS;
453 break;
454
455 case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */
456
457 Status = AcpiHwRead (&Value, &AcpiGbl_FADT.XPm2ControlBlock);
458 break;
459
460 case ACPI_REGISTER_PM_TIMER: /* 32-bit access */
461
462 Status = AcpiHwRead (&Value, &AcpiGbl_FADT.XPmTimerBlock);
463 break;
464
465 case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */
466
467 Status = AcpiHwReadPort (AcpiGbl_FADT.SmiCommand, &Value, 8);
468 break;
469
470 default:
471
472 ACPI_ERROR ((AE_INFO, "Unknown Register ID: 0x%X",
473 RegisterId));
474 Status = AE_BAD_PARAMETER;
475 break;
476 }
477
478 if (ACPI_SUCCESS (Status))
479 {
480 *ReturnValue = Value;
481 }
482
483 return_ACPI_STATUS (Status);
484 }
485
486
487 /******************************************************************************
488 *
489 * FUNCTION: AcpiHwRegisterWrite
490 *
491 * PARAMETERS: RegisterId - ACPI Register ID
492 * Value - The value to write
493 *
494 * RETURN: Status
495 *
496 * DESCRIPTION: Write to the specified ACPI register
497 *
498 * NOTE: In accordance with the ACPI specification, this function automatically
499 * preserves the value of the following bits, meaning that these bits cannot be
500 * changed via this interface:
501 *
502 * PM1_CONTROL[0] = SCI_EN
503 * PM1_CONTROL[9]
504 * PM1_STATUS[11]
505 *
506 * ACPI References:
507 * 1) Hardware Ignored Bits: When software writes to a register with ignored
508 * bit fields, it preserves the ignored bit fields
509 * 2) SCI_EN: OSPM always preserves this bit position
510 *
511 ******************************************************************************/
512
513 ACPI_STATUS
AcpiHwRegisterWrite(UINT32 RegisterId,UINT32 Value)514 AcpiHwRegisterWrite (
515 UINT32 RegisterId,
516 UINT32 Value)
517 {
518 ACPI_STATUS Status;
519 UINT32 ReadValue;
520
521
522 ACPI_FUNCTION_TRACE (HwRegisterWrite);
523
524
525 switch (RegisterId)
526 {
527 case ACPI_REGISTER_PM1_STATUS: /* PM1 A/B: 16-bit access each */
528 /*
529 * Handle the "ignored" bit in PM1 Status. According to the ACPI
530 * specification, ignored bits are to be preserved when writing.
531 * Normally, this would mean a read/modify/write sequence. However,
532 * preserving a bit in the status register is different. Writing a
533 * one clears the status, and writing a zero preserves the status.
534 * Therefore, we must always write zero to the ignored bit.
535 *
536 * This behavior is clarified in the ACPI 4.0 specification.
537 */
538 Value &= ~ACPI_PM1_STATUS_PRESERVED_BITS;
539
540 Status = AcpiHwWriteMultiple (Value,
541 &AcpiGbl_XPm1aStatus,
542 &AcpiGbl_XPm1bStatus);
543 break;
544
545 case ACPI_REGISTER_PM1_ENABLE: /* PM1 A/B: 16-bit access each */
546
547 Status = AcpiHwWriteMultiple (Value,
548 &AcpiGbl_XPm1aEnable,
549 &AcpiGbl_XPm1bEnable);
550 break;
551
552 case ACPI_REGISTER_PM1_CONTROL: /* PM1 A/B: 16-bit access each */
553 /*
554 * Perform a read first to preserve certain bits (per ACPI spec)
555 * Note: This includes SCI_EN, we never want to change this bit
556 */
557 Status = AcpiHwReadMultiple (&ReadValue,
558 &AcpiGbl_FADT.XPm1aControlBlock,
559 &AcpiGbl_FADT.XPm1bControlBlock);
560 if (ACPI_FAILURE (Status))
561 {
562 goto Exit;
563 }
564
565 /* Insert the bits to be preserved */
566
567 ACPI_INSERT_BITS (Value, ACPI_PM1_CONTROL_PRESERVED_BITS, ReadValue);
568
569 /* Now we can write the data */
570
571 Status = AcpiHwWriteMultiple (Value,
572 &AcpiGbl_FADT.XPm1aControlBlock,
573 &AcpiGbl_FADT.XPm1bControlBlock);
574 break;
575
576 case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */
577 /*
578 * For control registers, all reserved bits must be preserved,
579 * as per the ACPI spec.
580 */
581 Status = AcpiHwRead (&ReadValue, &AcpiGbl_FADT.XPm2ControlBlock);
582 if (ACPI_FAILURE (Status))
583 {
584 goto Exit;
585 }
586
587 /* Insert the bits to be preserved */
588
589 ACPI_INSERT_BITS (Value, ACPI_PM2_CONTROL_PRESERVED_BITS, ReadValue);
590
591 Status = AcpiHwWrite (Value, &AcpiGbl_FADT.XPm2ControlBlock);
592 break;
593
594 case ACPI_REGISTER_PM_TIMER: /* 32-bit access */
595
596 Status = AcpiHwWrite (Value, &AcpiGbl_FADT.XPmTimerBlock);
597 break;
598
599 case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */
600
601 /* SMI_CMD is currently always in IO space */
602
603 Status = AcpiHwWritePort (AcpiGbl_FADT.SmiCommand, Value, 8);
604 break;
605
606 default:
607
608 ACPI_ERROR ((AE_INFO, "Unknown Register ID: 0x%X",
609 RegisterId));
610 Status = AE_BAD_PARAMETER;
611 break;
612 }
613
614 Exit:
615 return_ACPI_STATUS (Status);
616 }
617
618
619 /******************************************************************************
620 *
621 * FUNCTION: AcpiHwReadMultiple
622 *
623 * PARAMETERS: Value - Where the register value is returned
624 * RegisterA - First ACPI register (required)
625 * RegisterB - Second ACPI register (optional)
626 *
627 * RETURN: Status
628 *
629 * DESCRIPTION: Read from the specified two-part ACPI register (such as PM1 A/B)
630 *
631 ******************************************************************************/
632
633 static ACPI_STATUS
AcpiHwReadMultiple(UINT32 * Value,ACPI_GENERIC_ADDRESS * RegisterA,ACPI_GENERIC_ADDRESS * RegisterB)634 AcpiHwReadMultiple (
635 UINT32 *Value,
636 ACPI_GENERIC_ADDRESS *RegisterA,
637 ACPI_GENERIC_ADDRESS *RegisterB)
638 {
639 UINT32 ValueA = 0;
640 UINT32 ValueB = 0;
641 ACPI_STATUS Status;
642
643
644 /* The first register is always required */
645
646 Status = AcpiHwRead (&ValueA, RegisterA);
647 if (ACPI_FAILURE (Status))
648 {
649 return (Status);
650 }
651
652 /* Second register is optional */
653
654 if (RegisterB->Address)
655 {
656 Status = AcpiHwRead (&ValueB, RegisterB);
657 if (ACPI_FAILURE (Status))
658 {
659 return (Status);
660 }
661 }
662
663 /*
664 * OR the two return values together. No shifting or masking is necessary,
665 * because of how the PM1 registers are defined in the ACPI specification:
666 *
667 * "Although the bits can be split between the two register blocks (each
668 * register block has a unique pointer within the FADT), the bit positions
669 * are maintained. The register block with unimplemented bits (that is,
670 * those implemented in the other register block) always returns zeros,
671 * and writes have no side effects"
672 */
673 *Value = (ValueA | ValueB);
674 return (AE_OK);
675 }
676
677
678 /******************************************************************************
679 *
680 * FUNCTION: AcpiHwWriteMultiple
681 *
682 * PARAMETERS: Value - The value to write
683 * RegisterA - First ACPI register (required)
684 * RegisterB - Second ACPI register (optional)
685 *
686 * RETURN: Status
687 *
688 * DESCRIPTION: Write to the specified two-part ACPI register (such as PM1 A/B)
689 *
690 ******************************************************************************/
691
692 static ACPI_STATUS
AcpiHwWriteMultiple(UINT32 Value,ACPI_GENERIC_ADDRESS * RegisterA,ACPI_GENERIC_ADDRESS * RegisterB)693 AcpiHwWriteMultiple (
694 UINT32 Value,
695 ACPI_GENERIC_ADDRESS *RegisterA,
696 ACPI_GENERIC_ADDRESS *RegisterB)
697 {
698 ACPI_STATUS Status;
699
700
701 /* The first register is always required */
702
703 Status = AcpiHwWrite (Value, RegisterA);
704 if (ACPI_FAILURE (Status))
705 {
706 return (Status);
707 }
708
709 /*
710 * Second register is optional
711 *
712 * No bit shifting or clearing is necessary, because of how the PM1
713 * registers are defined in the ACPI specification:
714 *
715 * "Although the bits can be split between the two register blocks (each
716 * register block has a unique pointer within the FADT), the bit positions
717 * are maintained. The register block with unimplemented bits (that is,
718 * those implemented in the other register block) always returns zeros,
719 * and writes have no side effects"
720 */
721 if (RegisterB->Address)
722 {
723 Status = AcpiHwWrite (Value, RegisterB);
724 }
725
726 return (Status);
727 }
728
729 #endif /* !ACPI_REDUCED_HARDWARE */
730