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