xref: /freebsd/sys/contrib/dev/acpica/components/hardware/hwregs.c (revision 6c8eb57da559393de97e46f23efbd0feb0312d8e)
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 <contrib/dev/acpica/include/acpi.h>
46 #include <contrib/dev/acpica/include/accommon.h>
47 #include <contrib/dev/acpica/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
89 AcpiHwValidateRegister (
90     ACPI_GENERIC_ADDRESS    *Reg,
91     UINT8                   MaxBitWidth,
92     UINT64                  *Address)
93 {
94     UINT8                   BitWidth;
95     UINT8                   BitEnd;
96     UINT8                   AccessWidth;
97 
98 
99     /* Must have a valid pointer to a GAS structure */
100 
101     if (!Reg)
102     {
103         return (AE_BAD_PARAMETER);
104     }
105 
106     /*
107      * Copy the target address. This handles possible alignment issues.
108      * Address must not be null. A null address also indicates an optional
109      * ACPI register that is not supported, so no error message.
110      */
111     ACPI_MOVE_64_TO_64 (Address, &Reg->Address);
112     if (!(*Address))
113     {
114         return (AE_BAD_ADDRESS);
115     }
116 
117     /* Validate the SpaceID */
118 
119     if ((Reg->SpaceId != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
120         (Reg->SpaceId != ACPI_ADR_SPACE_SYSTEM_IO))
121     {
122         ACPI_ERROR ((AE_INFO,
123             "Unsupported address space: 0x%X", Reg->SpaceId));
124         return (AE_SUPPORT);
125     }
126 
127     /* Validate the AccessWidth */
128 
129     if (Reg->AccessWidth > 4)
130     {
131         ACPI_ERROR ((AE_INFO,
132             "Unsupported register access width: 0x%X", Reg->AccessWidth));
133         return (AE_SUPPORT);
134     }
135 
136     /* Validate the BitWidth, convert AccessWidth into number of bits */
137 
138     BitEnd = Reg->BitOffset + Reg->BitWidth;
139     AccessWidth = Reg->AccessWidth ? Reg->AccessWidth : 1;
140     AccessWidth = 1 << (AccessWidth + 2);
141     BitWidth = ACPI_ROUND_UP (BitEnd, AccessWidth) -
142         ACPI_ROUND_DOWN (Reg->BitOffset, AccessWidth);
143     if (MaxBitWidth < BitWidth)
144     {
145         ACPI_WARNING ((AE_INFO,
146             "Requested bit width 0x%X is smaller than register bit width 0x%X",
147             MaxBitWidth, BitWidth));
148         return (AE_SUPPORT);
149     }
150 
151     return (AE_OK);
152 }
153 
154 
155 /******************************************************************************
156  *
157  * FUNCTION:    AcpiHwRead
158  *
159  * PARAMETERS:  Value               - Where the value is returned
160  *              Reg                 - GAS register structure
161  *
162  * RETURN:      Status
163  *
164  * DESCRIPTION: Read from either memory or IO space. This is a 32-bit max
165  *              version of AcpiRead, used internally since the overhead of
166  *              64-bit values is not needed.
167  *
168  * LIMITATIONS: <These limitations also apply to AcpiHwWrite>
169  *      BitWidth must be exactly 8, 16, or 32.
170  *      SpaceID must be SystemMemory or SystemIO.
171  *      BitOffset and AccessWidth are currently ignored, as there has
172  *          not been a need to implement these.
173  *
174  ******************************************************************************/
175 
176 ACPI_STATUS
177 AcpiHwRead (
178     UINT32                  *Value,
179     ACPI_GENERIC_ADDRESS    *Reg)
180 {
181     UINT64                  Address;
182     UINT64                  Value64;
183     ACPI_STATUS             Status;
184 
185 
186     ACPI_FUNCTION_NAME (HwRead);
187 
188 
189     /* Validate contents of the GAS register */
190 
191     Status = AcpiHwValidateRegister (Reg, 32, &Address);
192     if (ACPI_FAILURE (Status))
193     {
194         return (Status);
195     }
196 
197     /* Initialize entire 32-bit return value to zero */
198 
199     *Value = 0;
200 
201     /*
202      * Two address spaces supported: Memory or IO. PCI_Config is
203      * not supported here because the GAS structure is insufficient
204      */
205     if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_MEMORY)
206     {
207         Status = AcpiOsReadMemory ((ACPI_PHYSICAL_ADDRESS)
208             Address, &Value64, Reg->BitWidth);
209 
210         *Value = (UINT32) Value64;
211     }
212     else /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
213     {
214         Status = AcpiHwReadPort ((ACPI_IO_ADDRESS)
215             Address, Value, Reg->BitWidth);
216     }
217 
218     ACPI_DEBUG_PRINT ((ACPI_DB_IO,
219         "Read:  %8.8X width %2d from %8.8X%8.8X (%s)\n",
220         *Value, Reg->BitWidth, ACPI_FORMAT_UINT64 (Address),
221         AcpiUtGetRegionName (Reg->SpaceId)));
222 
223     return (Status);
224 }
225 
226 
227 /******************************************************************************
228  *
229  * FUNCTION:    AcpiHwWrite
230  *
231  * PARAMETERS:  Value               - Value to be written
232  *              Reg                 - GAS register structure
233  *
234  * RETURN:      Status
235  *
236  * DESCRIPTION: Write to either memory or IO space. This is a 32-bit max
237  *              version of AcpiWrite, used internally since the overhead of
238  *              64-bit values is not needed.
239  *
240  ******************************************************************************/
241 
242 ACPI_STATUS
243 AcpiHwWrite (
244     UINT32                  Value,
245     ACPI_GENERIC_ADDRESS    *Reg)
246 {
247     UINT64                  Address;
248     ACPI_STATUS             Status;
249 
250 
251     ACPI_FUNCTION_NAME (HwWrite);
252 
253 
254     /* Validate contents of the GAS register */
255 
256     Status = AcpiHwValidateRegister (Reg, 32, &Address);
257     if (ACPI_FAILURE (Status))
258     {
259         return (Status);
260     }
261 
262     /*
263      * Two address spaces supported: Memory or IO. PCI_Config is
264      * not supported here because the GAS structure is insufficient
265      */
266     if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_MEMORY)
267     {
268         Status = AcpiOsWriteMemory ((ACPI_PHYSICAL_ADDRESS)
269             Address, (UINT64) Value, Reg->BitWidth);
270     }
271     else /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
272     {
273         Status = AcpiHwWritePort ((ACPI_IO_ADDRESS)
274             Address, Value, Reg->BitWidth);
275     }
276 
277     ACPI_DEBUG_PRINT ((ACPI_DB_IO,
278         "Wrote: %8.8X width %2d   to %8.8X%8.8X (%s)\n",
279         Value, Reg->BitWidth, ACPI_FORMAT_UINT64 (Address),
280         AcpiUtGetRegionName (Reg->SpaceId)));
281 
282     return (Status);
283 }
284 
285 
286 #if (!ACPI_REDUCED_HARDWARE)
287 /*******************************************************************************
288  *
289  * FUNCTION:    AcpiHwClearAcpiStatus
290  *
291  * PARAMETERS:  None
292  *
293  * RETURN:      Status
294  *
295  * DESCRIPTION: Clears all fixed and general purpose status bits
296  *
297  ******************************************************************************/
298 
299 ACPI_STATUS
300 AcpiHwClearAcpiStatus (
301     void)
302 {
303     ACPI_STATUS             Status;
304     ACPI_CPU_FLAGS          LockFlags = 0;
305 
306 
307     ACPI_FUNCTION_TRACE (HwClearAcpiStatus);
308 
309 
310     ACPI_DEBUG_PRINT ((ACPI_DB_IO, "About to write %04X to %8.8X%8.8X\n",
311         ACPI_BITMASK_ALL_FIXED_STATUS,
312         ACPI_FORMAT_UINT64 (AcpiGbl_XPm1aStatus.Address)));
313 
314     LockFlags = AcpiOsAcquireLock (AcpiGbl_HardwareLock);
315 
316     /* Clear the fixed events in PM1 A/B */
317 
318     Status = AcpiHwRegisterWrite (ACPI_REGISTER_PM1_STATUS,
319         ACPI_BITMASK_ALL_FIXED_STATUS);
320 
321     AcpiOsReleaseLock (AcpiGbl_HardwareLock, LockFlags);
322 
323     if (ACPI_FAILURE (Status))
324     {
325         goto Exit;
326     }
327 
328     /* Clear the GPE Bits in all GPE registers in all GPE blocks */
329 
330     Status = AcpiEvWalkGpeList (AcpiHwClearGpeBlock, NULL);
331 
332 Exit:
333     return_ACPI_STATUS (Status);
334 }
335 
336 
337 /*******************************************************************************
338  *
339  * FUNCTION:    AcpiHwGetBitRegisterInfo
340  *
341  * PARAMETERS:  RegisterId          - Index of ACPI Register to access
342  *
343  * RETURN:      The bitmask to be used when accessing the register
344  *
345  * DESCRIPTION: Map RegisterId into a register bitmask.
346  *
347  ******************************************************************************/
348 
349 ACPI_BIT_REGISTER_INFO *
350 AcpiHwGetBitRegisterInfo (
351     UINT32                  RegisterId)
352 {
353     ACPI_FUNCTION_ENTRY ();
354 
355 
356     if (RegisterId > ACPI_BITREG_MAX)
357     {
358         ACPI_ERROR ((AE_INFO, "Invalid BitRegister ID: 0x%X", RegisterId));
359         return (NULL);
360     }
361 
362     return (&AcpiGbl_BitRegisterInfo[RegisterId]);
363 }
364 
365 
366 /******************************************************************************
367  *
368  * FUNCTION:    AcpiHwWritePm1Control
369  *
370  * PARAMETERS:  Pm1aControl         - Value to be written to PM1A control
371  *              Pm1bControl         - Value to be written to PM1B control
372  *
373  * RETURN:      Status
374  *
375  * DESCRIPTION: Write the PM1 A/B control registers. These registers are
376  *              different than than the PM1 A/B status and enable registers
377  *              in that different values can be written to the A/B registers.
378  *              Most notably, the SLP_TYP bits can be different, as per the
379  *              values returned from the _Sx predefined methods.
380  *
381  ******************************************************************************/
382 
383 ACPI_STATUS
384 AcpiHwWritePm1Control (
385     UINT32                  Pm1aControl,
386     UINT32                  Pm1bControl)
387 {
388     ACPI_STATUS             Status;
389 
390 
391     ACPI_FUNCTION_TRACE (HwWritePm1Control);
392 
393 
394     Status = AcpiHwWrite (Pm1aControl, &AcpiGbl_FADT.XPm1aControlBlock);
395     if (ACPI_FAILURE (Status))
396     {
397         return_ACPI_STATUS (Status);
398     }
399 
400     if (AcpiGbl_FADT.XPm1bControlBlock.Address)
401     {
402         Status = AcpiHwWrite (Pm1bControl, &AcpiGbl_FADT.XPm1bControlBlock);
403     }
404     return_ACPI_STATUS (Status);
405 }
406 
407 
408 /******************************************************************************
409  *
410  * FUNCTION:    AcpiHwRegisterRead
411  *
412  * PARAMETERS:  RegisterId          - ACPI Register ID
413  *              ReturnValue         - Where the register value is returned
414  *
415  * RETURN:      Status and the value read.
416  *
417  * DESCRIPTION: Read from the specified ACPI register
418  *
419  ******************************************************************************/
420 
421 ACPI_STATUS
422 AcpiHwRegisterRead (
423     UINT32                  RegisterId,
424     UINT32                  *ReturnValue)
425 {
426     UINT32                  Value = 0;
427     ACPI_STATUS             Status;
428 
429 
430     ACPI_FUNCTION_TRACE (HwRegisterRead);
431 
432 
433     switch (RegisterId)
434     {
435     case ACPI_REGISTER_PM1_STATUS:           /* PM1 A/B: 16-bit access each */
436 
437         Status = AcpiHwReadMultiple (&Value,
438             &AcpiGbl_XPm1aStatus,
439             &AcpiGbl_XPm1bStatus);
440         break;
441 
442     case ACPI_REGISTER_PM1_ENABLE:           /* PM1 A/B: 16-bit access each */
443 
444         Status = AcpiHwReadMultiple (&Value,
445             &AcpiGbl_XPm1aEnable,
446             &AcpiGbl_XPm1bEnable);
447         break;
448 
449     case ACPI_REGISTER_PM1_CONTROL:          /* PM1 A/B: 16-bit access each */
450 
451         Status = AcpiHwReadMultiple (&Value,
452             &AcpiGbl_FADT.XPm1aControlBlock,
453             &AcpiGbl_FADT.XPm1bControlBlock);
454 
455         /*
456          * Zero the write-only bits. From the ACPI specification, "Hardware
457          * Write-Only Bits": "Upon reads to registers with write-only bits,
458          * software masks out all write-only bits."
459          */
460         Value &= ~ACPI_PM1_CONTROL_WRITEONLY_BITS;
461         break;
462 
463     case ACPI_REGISTER_PM2_CONTROL:          /* 8-bit access */
464 
465         Status = AcpiHwRead (&Value, &AcpiGbl_FADT.XPm2ControlBlock);
466         break;
467 
468     case ACPI_REGISTER_PM_TIMER:             /* 32-bit access */
469 
470         Status = AcpiHwRead (&Value, &AcpiGbl_FADT.XPmTimerBlock);
471         break;
472 
473     case ACPI_REGISTER_SMI_COMMAND_BLOCK:    /* 8-bit access */
474 
475         Status = AcpiHwReadPort (AcpiGbl_FADT.SmiCommand, &Value, 8);
476         break;
477 
478     default:
479 
480         ACPI_ERROR ((AE_INFO, "Unknown Register ID: 0x%X",
481             RegisterId));
482         Status = AE_BAD_PARAMETER;
483         break;
484     }
485 
486     if (ACPI_SUCCESS (Status))
487     {
488         *ReturnValue = Value;
489     }
490 
491     return_ACPI_STATUS (Status);
492 }
493 
494 
495 /******************************************************************************
496  *
497  * FUNCTION:    AcpiHwRegisterWrite
498  *
499  * PARAMETERS:  RegisterId          - ACPI Register ID
500  *              Value               - The value to write
501  *
502  * RETURN:      Status
503  *
504  * DESCRIPTION: Write to the specified ACPI register
505  *
506  * NOTE: In accordance with the ACPI specification, this function automatically
507  * preserves the value of the following bits, meaning that these bits cannot be
508  * changed via this interface:
509  *
510  * PM1_CONTROL[0] = SCI_EN
511  * PM1_CONTROL[9]
512  * PM1_STATUS[11]
513  *
514  * ACPI References:
515  * 1) Hardware Ignored Bits: When software writes to a register with ignored
516  *      bit fields, it preserves the ignored bit fields
517  * 2) SCI_EN: OSPM always preserves this bit position
518  *
519  ******************************************************************************/
520 
521 ACPI_STATUS
522 AcpiHwRegisterWrite (
523     UINT32                  RegisterId,
524     UINT32                  Value)
525 {
526     ACPI_STATUS             Status;
527     UINT32                  ReadValue;
528 
529 
530     ACPI_FUNCTION_TRACE (HwRegisterWrite);
531 
532 
533     switch (RegisterId)
534     {
535     case ACPI_REGISTER_PM1_STATUS:           /* PM1 A/B: 16-bit access each */
536         /*
537          * Handle the "ignored" bit in PM1 Status. According to the ACPI
538          * specification, ignored bits are to be preserved when writing.
539          * Normally, this would mean a read/modify/write sequence. However,
540          * preserving a bit in the status register is different. Writing a
541          * one clears the status, and writing a zero preserves the status.
542          * Therefore, we must always write zero to the ignored bit.
543          *
544          * This behavior is clarified in the ACPI 4.0 specification.
545          */
546         Value &= ~ACPI_PM1_STATUS_PRESERVED_BITS;
547 
548         Status = AcpiHwWriteMultiple (Value,
549             &AcpiGbl_XPm1aStatus,
550             &AcpiGbl_XPm1bStatus);
551         break;
552 
553     case ACPI_REGISTER_PM1_ENABLE:           /* PM1 A/B: 16-bit access each */
554 
555         Status = AcpiHwWriteMultiple (Value,
556             &AcpiGbl_XPm1aEnable,
557             &AcpiGbl_XPm1bEnable);
558         break;
559 
560     case ACPI_REGISTER_PM1_CONTROL:          /* PM1 A/B: 16-bit access each */
561         /*
562          * Perform a read first to preserve certain bits (per ACPI spec)
563          * Note: This includes SCI_EN, we never want to change this bit
564          */
565         Status = AcpiHwReadMultiple (&ReadValue,
566             &AcpiGbl_FADT.XPm1aControlBlock,
567             &AcpiGbl_FADT.XPm1bControlBlock);
568         if (ACPI_FAILURE (Status))
569         {
570             goto Exit;
571         }
572 
573         /* Insert the bits to be preserved */
574 
575         ACPI_INSERT_BITS (Value, ACPI_PM1_CONTROL_PRESERVED_BITS, ReadValue);
576 
577         /* Now we can write the data */
578 
579         Status = AcpiHwWriteMultiple (Value,
580             &AcpiGbl_FADT.XPm1aControlBlock,
581             &AcpiGbl_FADT.XPm1bControlBlock);
582         break;
583 
584     case ACPI_REGISTER_PM2_CONTROL:          /* 8-bit access */
585         /*
586          * For control registers, all reserved bits must be preserved,
587          * as per the ACPI spec.
588          */
589         Status = AcpiHwRead (&ReadValue, &AcpiGbl_FADT.XPm2ControlBlock);
590         if (ACPI_FAILURE (Status))
591         {
592             goto Exit;
593         }
594 
595         /* Insert the bits to be preserved */
596 
597         ACPI_INSERT_BITS (Value, ACPI_PM2_CONTROL_PRESERVED_BITS, ReadValue);
598 
599         Status = AcpiHwWrite (Value, &AcpiGbl_FADT.XPm2ControlBlock);
600         break;
601 
602     case ACPI_REGISTER_PM_TIMER:             /* 32-bit access */
603 
604         Status = AcpiHwWrite (Value, &AcpiGbl_FADT.XPmTimerBlock);
605         break;
606 
607     case ACPI_REGISTER_SMI_COMMAND_BLOCK:    /* 8-bit access */
608 
609         /* SMI_CMD is currently always in IO space */
610 
611         Status = AcpiHwWritePort (AcpiGbl_FADT.SmiCommand, Value, 8);
612         break;
613 
614     default:
615 
616         ACPI_ERROR ((AE_INFO, "Unknown Register ID: 0x%X",
617             RegisterId));
618         Status = AE_BAD_PARAMETER;
619         break;
620     }
621 
622 Exit:
623     return_ACPI_STATUS (Status);
624 }
625 
626 
627 /******************************************************************************
628  *
629  * FUNCTION:    AcpiHwReadMultiple
630  *
631  * PARAMETERS:  Value               - Where the register value is returned
632  *              RegisterA           - First ACPI register (required)
633  *              RegisterB           - Second ACPI register (optional)
634  *
635  * RETURN:      Status
636  *
637  * DESCRIPTION: Read from the specified two-part ACPI register (such as PM1 A/B)
638  *
639  ******************************************************************************/
640 
641 static ACPI_STATUS
642 AcpiHwReadMultiple (
643     UINT32                  *Value,
644     ACPI_GENERIC_ADDRESS    *RegisterA,
645     ACPI_GENERIC_ADDRESS    *RegisterB)
646 {
647     UINT32                  ValueA = 0;
648     UINT32                  ValueB = 0;
649     ACPI_STATUS             Status;
650 
651 
652     /* The first register is always required */
653 
654     Status = AcpiHwRead (&ValueA, RegisterA);
655     if (ACPI_FAILURE (Status))
656     {
657         return (Status);
658     }
659 
660     /* Second register is optional */
661 
662     if (RegisterB->Address)
663     {
664         Status = AcpiHwRead (&ValueB, RegisterB);
665         if (ACPI_FAILURE (Status))
666         {
667             return (Status);
668         }
669     }
670 
671     /*
672      * OR the two return values together. No shifting or masking is necessary,
673      * because of how the PM1 registers are defined in the ACPI specification:
674      *
675      * "Although the bits can be split between the two register blocks (each
676      * register block has a unique pointer within the FADT), the bit positions
677      * are maintained. The register block with unimplemented bits (that is,
678      * those implemented in the other register block) always returns zeros,
679      * and writes have no side effects"
680      */
681     *Value = (ValueA | ValueB);
682     return (AE_OK);
683 }
684 
685 
686 /******************************************************************************
687  *
688  * FUNCTION:    AcpiHwWriteMultiple
689  *
690  * PARAMETERS:  Value               - The value to write
691  *              RegisterA           - First ACPI register (required)
692  *              RegisterB           - Second ACPI register (optional)
693  *
694  * RETURN:      Status
695  *
696  * DESCRIPTION: Write to the specified two-part ACPI register (such as PM1 A/B)
697  *
698  ******************************************************************************/
699 
700 static ACPI_STATUS
701 AcpiHwWriteMultiple (
702     UINT32                  Value,
703     ACPI_GENERIC_ADDRESS    *RegisterA,
704     ACPI_GENERIC_ADDRESS    *RegisterB)
705 {
706     ACPI_STATUS             Status;
707 
708 
709     /* The first register is always required */
710 
711     Status = AcpiHwWrite (Value, RegisterA);
712     if (ACPI_FAILURE (Status))
713     {
714         return (Status);
715     }
716 
717     /*
718      * Second register is optional
719      *
720      * No bit shifting or clearing is necessary, because of how the PM1
721      * registers are defined in the ACPI specification:
722      *
723      * "Although the bits can be split between the two register blocks (each
724      * register block has a unique pointer within the FADT), the bit positions
725      * are maintained. The register block with unimplemented bits (that is,
726      * those implemented in the other register block) always returns zeros,
727      * and writes have no side effects"
728      */
729     if (RegisterB->Address)
730     {
731         Status = AcpiHwWrite (Value, RegisterB);
732     }
733 
734     return (Status);
735 }
736 
737 #endif /* !ACPI_REDUCED_HARDWARE */
738