xref: /netbsd-src/sys/external/bsd/acpica/dist/hardware/hwregs.c (revision 1580a27b92f58fcdcb23fdfbc04a7c2b54a0b7c8)
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 - 2017, 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 UINT8
58 AcpiHwGetAccessBitWidth (
59     UINT64                  Address,
60     ACPI_GENERIC_ADDRESS    *Reg,
61     UINT8                   MaxBitWidth);
62 
63 static ACPI_STATUS
64 AcpiHwReadMultiple (
65     UINT32                  *Value,
66     ACPI_GENERIC_ADDRESS    *RegisterA,
67     ACPI_GENERIC_ADDRESS    *RegisterB);
68 
69 static ACPI_STATUS
70 AcpiHwWriteMultiple (
71     UINT32                  Value,
72     ACPI_GENERIC_ADDRESS    *RegisterA,
73     ACPI_GENERIC_ADDRESS    *RegisterB);
74 
75 #endif /* !ACPI_REDUCED_HARDWARE */
76 
77 
78 /******************************************************************************
79  *
80  * FUNCTION:    AcpiHwGetAccessBitWidth
81  *
82  * PARAMETERS:  Address             - GAS register address
83  *              Reg                 - GAS register structure
84  *              MaxBitWidth         - Max BitWidth supported (32 or 64)
85  *
86  * RETURN:      Status
87  *
88  * DESCRIPTION: Obtain optimal access bit width
89  *
90  ******************************************************************************/
91 
92 static UINT8
93 AcpiHwGetAccessBitWidth (
94     UINT64                  Address,
95     ACPI_GENERIC_ADDRESS    *Reg,
96     UINT8                   MaxBitWidth)
97 {
98     UINT8                   AccessBitWidth;
99 
100 
101     /*
102      * GAS format "register", used by FADT:
103      *  1. Detected if BitOffset is 0 and BitWidth is 8/16/32/64;
104      *  2. AccessSize field is ignored and BitWidth field is used for
105      *     determining the boundary of the IO accesses.
106      * GAS format "region", used by APEI registers:
107      *  1. Detected if BitOffset is not 0 or BitWidth is not 8/16/32/64;
108      *  2. AccessSize field is used for determining the boundary of the
109      *     IO accesses;
110      *  3. BitOffset/BitWidth fields are used to describe the "region".
111      *
112      * Note: This algorithm assumes that the "Address" fields should always
113      *       contain aligned values.
114      */
115     if (!Reg->BitOffset && Reg->BitWidth &&
116         ACPI_IS_POWER_OF_TWO (Reg->BitWidth) &&
117         ACPI_IS_ALIGNED (Reg->BitWidth, 8))
118     {
119         AccessBitWidth = Reg->BitWidth;
120     }
121     else if (Reg->AccessWidth)
122     {
123         AccessBitWidth = ACPI_ACCESS_BIT_WIDTH (Reg->AccessWidth);
124     }
125     else
126     {
127         AccessBitWidth = ACPI_ROUND_UP_POWER_OF_TWO_8 (
128             Reg->BitOffset + Reg->BitWidth);
129         if (AccessBitWidth <= 8)
130         {
131             AccessBitWidth = 8;
132         }
133         else
134         {
135             while (!ACPI_IS_ALIGNED (Address, AccessBitWidth >> 3))
136             {
137                 AccessBitWidth >>= 1;
138             }
139         }
140     }
141 
142     /* Maximum IO port access bit width is 32 */
143 
144     if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_IO)
145     {
146         MaxBitWidth = 32;
147     }
148 
149     /*
150      * Return access width according to the requested maximum access bit width,
151      * as the caller should know the format of the register and may enforce
152      * a 32-bit accesses.
153      */
154     if (AccessBitWidth < MaxBitWidth)
155     {
156         return (AccessBitWidth);
157     }
158     return (MaxBitWidth);
159 }
160 
161 
162 /******************************************************************************
163  *
164  * FUNCTION:    AcpiHwValidateRegister
165  *
166  * PARAMETERS:  Reg                 - GAS register structure
167  *              MaxBitWidth         - Max BitWidth supported (32 or 64)
168  *              Address             - Pointer to where the gas->address
169  *                                    is returned
170  *
171  * RETURN:      Status
172  *
173  * DESCRIPTION: Validate the contents of a GAS register. Checks the GAS
174  *              pointer, Address, SpaceId, BitWidth, and BitOffset.
175  *
176  ******************************************************************************/
177 
178 ACPI_STATUS
179 AcpiHwValidateRegister (
180     ACPI_GENERIC_ADDRESS    *Reg,
181     UINT8                   MaxBitWidth,
182     UINT64                  *Address)
183 {
184     UINT8                   BitWidth;
185     UINT8                   AccessWidth;
186 
187 
188     /* Must have a valid pointer to a GAS structure */
189 
190     if (!Reg)
191     {
192         return (AE_BAD_PARAMETER);
193     }
194 
195     /*
196      * Copy the target address. This handles possible alignment issues.
197      * Address must not be null. A null address also indicates an optional
198      * ACPI register that is not supported, so no error message.
199      */
200     ACPI_MOVE_64_TO_64 (Address, &Reg->Address);
201     if (!(*Address))
202     {
203         return (AE_BAD_ADDRESS);
204     }
205 
206     /* Validate the SpaceID */
207 
208     if ((Reg->SpaceId != ACPI_ADR_SPACE_SYSTEM_MEMORY) &&
209         (Reg->SpaceId != ACPI_ADR_SPACE_SYSTEM_IO))
210     {
211         ACPI_ERROR ((AE_INFO,
212             "Unsupported address space: 0x%X", Reg->SpaceId));
213         return (AE_SUPPORT);
214     }
215 
216     /* Validate the AccessWidth */
217 
218     if (Reg->AccessWidth > 4)
219     {
220         ACPI_ERROR ((AE_INFO,
221             "Unsupported register access width: 0x%X", Reg->AccessWidth));
222         return (AE_SUPPORT);
223     }
224 
225     /* Validate the BitWidth, convert AccessWidth into number of bits */
226 
227     AccessWidth = AcpiHwGetAccessBitWidth (*Address, Reg, MaxBitWidth);
228     BitWidth = ACPI_ROUND_UP (Reg->BitOffset + Reg->BitWidth, AccessWidth);
229     if (MaxBitWidth < BitWidth)
230     {
231         ACPI_WARNING ((AE_INFO,
232             "Requested bit width 0x%X is smaller than register bit width 0x%X",
233             MaxBitWidth, BitWidth));
234         return (AE_SUPPORT);
235     }
236 
237     return (AE_OK);
238 }
239 
240 
241 /******************************************************************************
242  *
243  * FUNCTION:    AcpiHwRead
244  *
245  * PARAMETERS:  Value               - Where the value is returned
246  *              Reg                 - GAS register structure
247  *
248  * RETURN:      Status
249  *
250  * DESCRIPTION: Read from either memory or IO space. This is a 64-bit max
251  *              version of AcpiRead.
252  *
253  * LIMITATIONS: <These limitations also apply to AcpiHwWrite>
254  *      SpaceID must be SystemMemory or SystemIO.
255  *
256  ******************************************************************************/
257 
258 ACPI_STATUS
259 AcpiHwRead (
260     UINT64                  *Value,
261     ACPI_GENERIC_ADDRESS    *Reg)
262 {
263     UINT64                  Address;
264     UINT8                   AccessWidth;
265     UINT32                  BitWidth;
266     UINT8                   BitOffset;
267     UINT64                  Value64;
268     UINT32                  Value32;
269     UINT8                   Index;
270     ACPI_STATUS             Status;
271 
272 
273     ACPI_FUNCTION_NAME (HwRead);
274 
275 
276     /* Validate contents of the GAS register */
277 
278     Status = AcpiHwValidateRegister (Reg, 64, &Address);
279     if (ACPI_FAILURE (Status))
280     {
281         return (Status);
282     }
283 
284     /*
285      * Initialize entire 64-bit return value to zero, convert AccessWidth
286      * into number of bits based
287      */
288     *Value = 0;
289     AccessWidth = AcpiHwGetAccessBitWidth (Address, Reg, 64);
290     BitWidth = Reg->BitOffset + Reg->BitWidth;
291     BitOffset = Reg->BitOffset;
292 
293     /*
294      * Two address spaces supported: Memory or IO. PCI_Config is
295      * not supported here because the GAS structure is insufficient
296      */
297     Index = 0;
298     while (BitWidth)
299     {
300         if (BitOffset >= AccessWidth)
301         {
302             Value64 = 0;
303             BitOffset -= AccessWidth;
304         }
305         else
306         {
307             if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_MEMORY)
308             {
309                 Status = AcpiOsReadMemory ((ACPI_PHYSICAL_ADDRESS)
310                     Address + Index * ACPI_DIV_8 (AccessWidth),
311                     &Value64, AccessWidth);
312             }
313             else /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
314             {
315                 Status = AcpiHwReadPort ((ACPI_IO_ADDRESS)
316                     Address + Index * ACPI_DIV_8 (AccessWidth),
317                     &Value32, AccessWidth);
318                 Value64 = (UINT64) Value32;
319             }
320         }
321 
322         /*
323          * Use offset style bit writes because "Index * AccessWidth" is
324          * ensured to be less than 64-bits by AcpiHwValidateRegister().
325          */
326         ACPI_SET_BITS (Value, Index * AccessWidth,
327             ACPI_MASK_BITS_ABOVE_64 (AccessWidth), Value64);
328 
329         BitWidth -= BitWidth > AccessWidth ? AccessWidth : BitWidth;
330         Index++;
331     }
332 
333     ACPI_DEBUG_PRINT ((ACPI_DB_IO,
334         "Read:  %8.8X%8.8X width %2d from %8.8X%8.8X (%s)\n",
335         ACPI_FORMAT_UINT64 (*Value), AccessWidth,
336         ACPI_FORMAT_UINT64 (Address), AcpiUtGetRegionName (Reg->SpaceId)));
337 
338     return (Status);
339 }
340 
341 
342 /******************************************************************************
343  *
344  * FUNCTION:    AcpiHwWrite
345  *
346  * PARAMETERS:  Value               - Value to be written
347  *              Reg                 - GAS register structure
348  *
349  * RETURN:      Status
350  *
351  * DESCRIPTION: Write to either memory or IO space. This is a 64-bit max
352  *              version of AcpiWrite.
353  *
354  ******************************************************************************/
355 
356 ACPI_STATUS
357 AcpiHwWrite (
358     UINT64                  Value,
359     ACPI_GENERIC_ADDRESS    *Reg)
360 {
361     UINT64                  Address;
362     UINT8                   AccessWidth;
363     UINT32                  BitWidth;
364     UINT8                   BitOffset;
365     UINT64                  Value64;
366     UINT8                   Index;
367     ACPI_STATUS             Status;
368 
369 
370     ACPI_FUNCTION_NAME (HwWrite);
371 
372 
373     /* Validate contents of the GAS register */
374 
375     Status = AcpiHwValidateRegister (Reg, 64, &Address);
376     if (ACPI_FAILURE (Status))
377     {
378         return (Status);
379     }
380 
381     /* Convert AccessWidth into number of bits based */
382 
383     AccessWidth = AcpiHwGetAccessBitWidth (Address, Reg, 64);
384     BitWidth = Reg->BitOffset + Reg->BitWidth;
385     BitOffset = Reg->BitOffset;
386 
387     /*
388      * Two address spaces supported: Memory or IO. PCI_Config is
389      * not supported here because the GAS structure is insufficient
390      */
391     Index = 0;
392     while (BitWidth)
393     {
394         /*
395          * Use offset style bit reads because "Index * AccessWidth" is
396          * ensured to be less than 64-bits by AcpiHwValidateRegister().
397          */
398         Value64 = ACPI_GET_BITS (&Value, Index * AccessWidth,
399             ACPI_MASK_BITS_ABOVE_64 (AccessWidth));
400 
401         if (BitOffset >= AccessWidth)
402         {
403             BitOffset -= AccessWidth;
404         }
405         else
406         {
407             if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_MEMORY)
408             {
409                 Status = AcpiOsWriteMemory ((ACPI_PHYSICAL_ADDRESS)
410                     Address + Index * ACPI_DIV_8 (AccessWidth),
411                     Value64, AccessWidth);
412             }
413             else /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */
414             {
415                 Status = AcpiHwWritePort ((ACPI_IO_ADDRESS)
416                     Address + Index * ACPI_DIV_8 (AccessWidth),
417                     (UINT32) Value64, AccessWidth);
418             }
419         }
420 
421         /*
422          * Index * AccessWidth is ensured to be less than 32-bits by
423          * AcpiHwValidateRegister().
424          */
425         BitWidth -= BitWidth > AccessWidth ? AccessWidth : BitWidth;
426         Index++;
427     }
428 
429     ACPI_DEBUG_PRINT ((ACPI_DB_IO,
430         "Wrote: %8.8X%8.8X width %2d   to %8.8X%8.8X (%s)\n",
431         ACPI_FORMAT_UINT64 (Value), AccessWidth,
432         ACPI_FORMAT_UINT64 (Address), AcpiUtGetRegionName (Reg->SpaceId)));
433 
434     return (Status);
435 }
436 
437 
438 #if (!ACPI_REDUCED_HARDWARE)
439 /*******************************************************************************
440  *
441  * FUNCTION:    AcpiHwClearAcpiStatus
442  *
443  * PARAMETERS:  None
444  *
445  * RETURN:      Status
446  *
447  * DESCRIPTION: Clears all fixed and general purpose status bits
448  *
449  ******************************************************************************/
450 
451 ACPI_STATUS
452 AcpiHwClearAcpiStatus (
453     void)
454 {
455     ACPI_STATUS             Status;
456     ACPI_CPU_FLAGS          LockFlags = 0;
457 
458 
459     ACPI_FUNCTION_TRACE (HwClearAcpiStatus);
460 
461 
462     ACPI_DEBUG_PRINT ((ACPI_DB_IO, "About to write %04X to %8.8X%8.8X\n",
463         ACPI_BITMASK_ALL_FIXED_STATUS,
464         ACPI_FORMAT_UINT64 (AcpiGbl_XPm1aStatus.Address)));
465 
466     LockFlags = AcpiOsAcquireLock (AcpiGbl_HardwareLock);
467 
468     /* Clear the fixed events in PM1 A/B */
469 
470     Status = AcpiHwRegisterWrite (ACPI_REGISTER_PM1_STATUS,
471         ACPI_BITMASK_ALL_FIXED_STATUS);
472 
473     AcpiOsReleaseLock (AcpiGbl_HardwareLock, LockFlags);
474 
475     if (ACPI_FAILURE (Status))
476     {
477         goto Exit;
478     }
479 
480     /* Clear the GPE Bits in all GPE registers in all GPE blocks */
481 
482     Status = AcpiEvWalkGpeList (AcpiHwClearGpeBlock, NULL);
483 
484 Exit:
485     return_ACPI_STATUS (Status);
486 }
487 
488 
489 /*******************************************************************************
490  *
491  * FUNCTION:    AcpiHwGetBitRegisterInfo
492  *
493  * PARAMETERS:  RegisterId          - Index of ACPI Register to access
494  *
495  * RETURN:      The bitmask to be used when accessing the register
496  *
497  * DESCRIPTION: Map RegisterId into a register bitmask.
498  *
499  ******************************************************************************/
500 
501 ACPI_BIT_REGISTER_INFO *
502 AcpiHwGetBitRegisterInfo (
503     UINT32                  RegisterId)
504 {
505     ACPI_FUNCTION_ENTRY ();
506 
507 
508     if (RegisterId > ACPI_BITREG_MAX)
509     {
510         ACPI_ERROR ((AE_INFO, "Invalid BitRegister ID: 0x%X", RegisterId));
511         return (NULL);
512     }
513 
514     return (&AcpiGbl_BitRegisterInfo[RegisterId]);
515 }
516 
517 
518 /******************************************************************************
519  *
520  * FUNCTION:    AcpiHwWritePm1Control
521  *
522  * PARAMETERS:  Pm1aControl         - Value to be written to PM1A control
523  *              Pm1bControl         - Value to be written to PM1B control
524  *
525  * RETURN:      Status
526  *
527  * DESCRIPTION: Write the PM1 A/B control registers. These registers are
528  *              different than than the PM1 A/B status and enable registers
529  *              in that different values can be written to the A/B registers.
530  *              Most notably, the SLP_TYP bits can be different, as per the
531  *              values returned from the _Sx predefined methods.
532  *
533  ******************************************************************************/
534 
535 ACPI_STATUS
536 AcpiHwWritePm1Control (
537     UINT32                  Pm1aControl,
538     UINT32                  Pm1bControl)
539 {
540     ACPI_STATUS             Status;
541 
542 
543     ACPI_FUNCTION_TRACE (HwWritePm1Control);
544 
545 
546     Status = AcpiHwWrite (Pm1aControl, &AcpiGbl_FADT.XPm1aControlBlock);
547     if (ACPI_FAILURE (Status))
548     {
549         return_ACPI_STATUS (Status);
550     }
551 
552     if (AcpiGbl_FADT.XPm1bControlBlock.Address)
553     {
554         Status = AcpiHwWrite (Pm1bControl, &AcpiGbl_FADT.XPm1bControlBlock);
555     }
556     return_ACPI_STATUS (Status);
557 }
558 
559 
560 /******************************************************************************
561  *
562  * FUNCTION:    AcpiHwRegisterRead
563  *
564  * PARAMETERS:  RegisterId          - ACPI Register ID
565  *              ReturnValue         - Where the register value is returned
566  *
567  * RETURN:      Status and the value read.
568  *
569  * DESCRIPTION: Read from the specified ACPI register
570  *
571  ******************************************************************************/
572 
573 ACPI_STATUS
574 AcpiHwRegisterRead (
575     UINT32                  RegisterId,
576     UINT32                  *ReturnValue)
577 {
578     UINT32                  Value = 0;
579     UINT64                  Value64;
580     ACPI_STATUS             Status;
581 
582 
583     ACPI_FUNCTION_TRACE (HwRegisterRead);
584 
585 
586     switch (RegisterId)
587     {
588     case ACPI_REGISTER_PM1_STATUS:           /* PM1 A/B: 16-bit access each */
589 
590         Status = AcpiHwReadMultiple (&Value,
591             &AcpiGbl_XPm1aStatus,
592             &AcpiGbl_XPm1bStatus);
593         break;
594 
595     case ACPI_REGISTER_PM1_ENABLE:           /* PM1 A/B: 16-bit access each */
596 
597         Status = AcpiHwReadMultiple (&Value,
598             &AcpiGbl_XPm1aEnable,
599             &AcpiGbl_XPm1bEnable);
600         break;
601 
602     case ACPI_REGISTER_PM1_CONTROL:          /* PM1 A/B: 16-bit access each */
603 
604         Status = AcpiHwReadMultiple (&Value,
605             &AcpiGbl_FADT.XPm1aControlBlock,
606             &AcpiGbl_FADT.XPm1bControlBlock);
607 
608         /*
609          * Zero the write-only bits. From the ACPI specification, "Hardware
610          * Write-Only Bits": "Upon reads to registers with write-only bits,
611          * software masks out all write-only bits."
612          */
613         Value &= ~ACPI_PM1_CONTROL_WRITEONLY_BITS;
614         break;
615 
616     case ACPI_REGISTER_PM2_CONTROL:          /* 8-bit access */
617 
618         Status = AcpiHwRead (&Value64, &AcpiGbl_FADT.XPm2ControlBlock);
619         Value = (UINT32) Value64;
620         break;
621 
622     case ACPI_REGISTER_PM_TIMER:             /* 32-bit access */
623 
624         Status = AcpiHwRead (&Value64, &AcpiGbl_FADT.XPmTimerBlock);
625         Value = (UINT32) Value64;
626         break;
627 
628     case ACPI_REGISTER_SMI_COMMAND_BLOCK:    /* 8-bit access */
629 
630         Status = AcpiHwReadPort (AcpiGbl_FADT.SmiCommand, &Value, 8);
631         break;
632 
633     default:
634 
635         ACPI_ERROR ((AE_INFO, "Unknown Register ID: 0x%X",
636             RegisterId));
637         Status = AE_BAD_PARAMETER;
638         break;
639     }
640 
641     if (ACPI_SUCCESS (Status))
642     {
643         *ReturnValue = (UINT32) Value;
644     }
645 
646     return_ACPI_STATUS (Status);
647 }
648 
649 
650 /******************************************************************************
651  *
652  * FUNCTION:    AcpiHwRegisterWrite
653  *
654  * PARAMETERS:  RegisterId          - ACPI Register ID
655  *              Value               - The value to write
656  *
657  * RETURN:      Status
658  *
659  * DESCRIPTION: Write to the specified ACPI register
660  *
661  * NOTE: In accordance with the ACPI specification, this function automatically
662  * preserves the value of the following bits, meaning that these bits cannot be
663  * changed via this interface:
664  *
665  * PM1_CONTROL[0] = SCI_EN
666  * PM1_CONTROL[9]
667  * PM1_STATUS[11]
668  *
669  * ACPI References:
670  * 1) Hardware Ignored Bits: When software writes to a register with ignored
671  *      bit fields, it preserves the ignored bit fields
672  * 2) SCI_EN: OSPM always preserves this bit position
673  *
674  ******************************************************************************/
675 
676 ACPI_STATUS
677 AcpiHwRegisterWrite (
678     UINT32                  RegisterId,
679     UINT32                  Value)
680 {
681     ACPI_STATUS             Status;
682     UINT32                  ReadValue;
683     UINT64                  ReadValue64;
684 
685 
686     ACPI_FUNCTION_TRACE (HwRegisterWrite);
687 
688 
689     switch (RegisterId)
690     {
691     case ACPI_REGISTER_PM1_STATUS:           /* PM1 A/B: 16-bit access each */
692         /*
693          * Handle the "ignored" bit in PM1 Status. According to the ACPI
694          * specification, ignored bits are to be preserved when writing.
695          * Normally, this would mean a read/modify/write sequence. However,
696          * preserving a bit in the status register is different. Writing a
697          * one clears the status, and writing a zero preserves the status.
698          * Therefore, we must always write zero to the ignored bit.
699          *
700          * This behavior is clarified in the ACPI 4.0 specification.
701          */
702         Value &= ~ACPI_PM1_STATUS_PRESERVED_BITS;
703 
704         Status = AcpiHwWriteMultiple (Value,
705             &AcpiGbl_XPm1aStatus,
706             &AcpiGbl_XPm1bStatus);
707         break;
708 
709     case ACPI_REGISTER_PM1_ENABLE:           /* PM1 A/B: 16-bit access each */
710 
711         Status = AcpiHwWriteMultiple (Value,
712             &AcpiGbl_XPm1aEnable,
713             &AcpiGbl_XPm1bEnable);
714         break;
715 
716     case ACPI_REGISTER_PM1_CONTROL:          /* PM1 A/B: 16-bit access each */
717         /*
718          * Perform a read first to preserve certain bits (per ACPI spec)
719          * Note: This includes SCI_EN, we never want to change this bit
720          */
721         Status = AcpiHwReadMultiple (&ReadValue,
722             &AcpiGbl_FADT.XPm1aControlBlock,
723             &AcpiGbl_FADT.XPm1bControlBlock);
724         if (ACPI_FAILURE (Status))
725         {
726             goto Exit;
727         }
728 
729         /* Insert the bits to be preserved */
730 
731         ACPI_INSERT_BITS (Value, ACPI_PM1_CONTROL_PRESERVED_BITS, ReadValue);
732 
733         /* Now we can write the data */
734 
735         Status = AcpiHwWriteMultiple (Value,
736             &AcpiGbl_FADT.XPm1aControlBlock,
737             &AcpiGbl_FADT.XPm1bControlBlock);
738         break;
739 
740     case ACPI_REGISTER_PM2_CONTROL:          /* 8-bit access */
741         /*
742          * For control registers, all reserved bits must be preserved,
743          * as per the ACPI spec.
744          */
745         Status = AcpiHwRead (&ReadValue64, &AcpiGbl_FADT.XPm2ControlBlock);
746         if (ACPI_FAILURE (Status))
747         {
748             goto Exit;
749         }
750         ReadValue = (UINT32) ReadValue64;
751 
752         /* Insert the bits to be preserved */
753 
754         ACPI_INSERT_BITS (Value, ACPI_PM2_CONTROL_PRESERVED_BITS, ReadValue);
755 
756         Status = AcpiHwWrite (Value, &AcpiGbl_FADT.XPm2ControlBlock);
757         break;
758 
759     case ACPI_REGISTER_PM_TIMER:             /* 32-bit access */
760 
761         Status = AcpiHwWrite (Value, &AcpiGbl_FADT.XPmTimerBlock);
762         break;
763 
764     case ACPI_REGISTER_SMI_COMMAND_BLOCK:    /* 8-bit access */
765 
766         /* SMI_CMD is currently always in IO space */
767 
768         Status = AcpiHwWritePort (AcpiGbl_FADT.SmiCommand, Value, 8);
769         break;
770 
771     default:
772 
773         ACPI_ERROR ((AE_INFO, "Unknown Register ID: 0x%X",
774             RegisterId));
775         Status = AE_BAD_PARAMETER;
776         break;
777     }
778 
779 Exit:
780     return_ACPI_STATUS (Status);
781 }
782 
783 
784 /******************************************************************************
785  *
786  * FUNCTION:    AcpiHwReadMultiple
787  *
788  * PARAMETERS:  Value               - Where the register value is returned
789  *              RegisterA           - First ACPI register (required)
790  *              RegisterB           - Second ACPI register (optional)
791  *
792  * RETURN:      Status
793  *
794  * DESCRIPTION: Read from the specified two-part ACPI register (such as PM1 A/B)
795  *
796  ******************************************************************************/
797 
798 static ACPI_STATUS
799 AcpiHwReadMultiple (
800     UINT32                  *Value,
801     ACPI_GENERIC_ADDRESS    *RegisterA,
802     ACPI_GENERIC_ADDRESS    *RegisterB)
803 {
804     UINT32                  ValueA = 0;
805     UINT32                  ValueB = 0;
806     UINT64                  Value64;
807     ACPI_STATUS             Status;
808 
809 
810     /* The first register is always required */
811 
812     Status = AcpiHwRead (&Value64, RegisterA);
813     if (ACPI_FAILURE (Status))
814     {
815         return (Status);
816     }
817     ValueA = (UINT32) Value64;
818 
819     /* Second register is optional */
820 
821     if (RegisterB->Address)
822     {
823         Status = AcpiHwRead (&Value64, RegisterB);
824         if (ACPI_FAILURE (Status))
825         {
826             return (Status);
827         }
828         ValueB = (UINT32) Value64;
829     }
830 
831     /*
832      * OR the two return values together. No shifting or masking is necessary,
833      * because of how the PM1 registers are defined in the ACPI specification:
834      *
835      * "Although the bits can be split between the two register blocks (each
836      * register block has a unique pointer within the FADT), the bit positions
837      * are maintained. The register block with unimplemented bits (that is,
838      * those implemented in the other register block) always returns zeros,
839      * and writes have no side effects"
840      */
841     *Value = (ValueA | ValueB);
842     return (AE_OK);
843 }
844 
845 
846 /******************************************************************************
847  *
848  * FUNCTION:    AcpiHwWriteMultiple
849  *
850  * PARAMETERS:  Value               - The value to write
851  *              RegisterA           - First ACPI register (required)
852  *              RegisterB           - Second ACPI register (optional)
853  *
854  * RETURN:      Status
855  *
856  * DESCRIPTION: Write to the specified two-part ACPI register (such as PM1 A/B)
857  *
858  ******************************************************************************/
859 
860 static ACPI_STATUS
861 AcpiHwWriteMultiple (
862     UINT32                  Value,
863     ACPI_GENERIC_ADDRESS    *RegisterA,
864     ACPI_GENERIC_ADDRESS    *RegisterB)
865 {
866     ACPI_STATUS             Status;
867 
868 
869     /* The first register is always required */
870 
871     Status = AcpiHwWrite (Value, RegisterA);
872     if (ACPI_FAILURE (Status))
873     {
874         return (Status);
875     }
876 
877     /*
878      * Second register is optional
879      *
880      * No bit shifting or clearing is necessary, because of how the PM1
881      * registers are defined in the ACPI specification:
882      *
883      * "Although the bits can be split between the two register blocks (each
884      * register block has a unique pointer within the FADT), the bit positions
885      * are maintained. The register block with unimplemented bits (that is,
886      * those implemented in the other register block) always returns zeros,
887      * and writes have no side effects"
888      */
889     if (RegisterB->Address)
890     {
891         Status = AcpiHwWrite (Value, RegisterB);
892     }
893 
894     return (Status);
895 }
896 
897 #endif /* !ACPI_REDUCED_HARDWARE */
898