xref: /titanic_50/usr/src/uts/intel/io/acpica/hardware/hwregs.c (revision 356f72340a69936724c69f2f87fffa6f5887f885)
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
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
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
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
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 *
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
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
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
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
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
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