Home | History | Annotate | Line # | Download | only in hardware
hwregs.c revision 1.3
      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