diff options
Diffstat (limited to 'source/components/hardware/hwregs.c')
-rw-r--r-- | source/components/hardware/hwregs.c | 230 |
1 files changed, 197 insertions, 33 deletions
diff --git a/source/components/hardware/hwregs.c b/source/components/hardware/hwregs.c index bc85c96b553f6..744186b87c1dd 100644 --- a/source/components/hardware/hwregs.c +++ b/source/components/hardware/hwregs.c @@ -54,6 +54,12 @@ /* Local Prototypes */ +static UINT8 +AcpiHwGetAccessBitWidth ( + UINT64 Address, + ACPI_GENERIC_ADDRESS *Reg, + UINT8 MaxBitWidth); + static ACPI_STATUS AcpiHwReadMultiple ( UINT32 *Value, @@ -71,6 +77,90 @@ AcpiHwWriteMultiple ( /****************************************************************************** * + * FUNCTION: AcpiHwGetAccessBitWidth + * + * PARAMETERS: Address - GAS register address + * Reg - GAS register structure + * MaxBitWidth - Max BitWidth supported (32 or 64) + * + * RETURN: Status + * + * DESCRIPTION: Obtain optimal access bit width + * + ******************************************************************************/ + +static UINT8 +AcpiHwGetAccessBitWidth ( + UINT64 Address, + ACPI_GENERIC_ADDRESS *Reg, + UINT8 MaxBitWidth) +{ + UINT8 AccessBitWidth; + + + /* + * GAS format "register", used by FADT: + * 1. Detected if BitOffset is 0 and BitWidth is 8/16/32/64; + * 2. AccessSize field is ignored and BitWidth field is used for + * determining the boundary of the IO accesses. + * GAS format "region", used by APEI registers: + * 1. Detected if BitOffset is not 0 or BitWidth is not 8/16/32/64; + * 2. AccessSize field is used for determining the boundary of the + * IO accesses; + * 3. BitOffset/BitWidth fields are used to describe the "region". + * + * Note: This algorithm assumes that the "Address" fields should always + * contain aligned values. + */ + if (!Reg->BitOffset && Reg->BitWidth && + ACPI_IS_POWER_OF_TWO (Reg->BitWidth) && + ACPI_IS_ALIGNED (Reg->BitWidth, 8)) + { + AccessBitWidth = Reg->BitWidth; + } + else if (Reg->AccessWidth) + { + AccessBitWidth = (1 << (Reg->AccessWidth + 2)); + } + else + { + AccessBitWidth = ACPI_ROUND_UP_POWER_OF_TWO_8 ( + Reg->BitOffset + Reg->BitWidth); + if (AccessBitWidth <= 8) + { + AccessBitWidth = 8; + } + else + { + while (!ACPI_IS_ALIGNED (Address, AccessBitWidth >> 3)) + { + AccessBitWidth >>= 1; + } + } + } + + /* Maximum IO port access bit width is 32 */ + + if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_IO) + { + MaxBitWidth = 32; + } + + /* + * Return access width according to the requested maximum access bit width, + * as the caller should know the format of the register and may enforce + * a 32-bit accesses. + */ + if (AccessBitWidth < MaxBitWidth) + { + return (AccessBitWidth); + } + return (MaxBitWidth); +} + + +/****************************************************************************** + * * FUNCTION: AcpiHwValidateRegister * * PARAMETERS: Reg - GAS register structure @@ -91,6 +181,9 @@ AcpiHwValidateRegister ( UINT8 MaxBitWidth, UINT64 *Address) { + UINT8 BitWidth; + UINT8 AccessWidth; + /* Must have a valid pointer to a GAS structure */ @@ -120,24 +213,25 @@ AcpiHwValidateRegister ( return (AE_SUPPORT); } - /* Validate the BitWidth */ + /* Validate the AccessWidth */ - if ((Reg->BitWidth != 8) && - (Reg->BitWidth != 16) && - (Reg->BitWidth != 32) && - (Reg->BitWidth != MaxBitWidth)) + if (Reg->AccessWidth > 4) { ACPI_ERROR ((AE_INFO, - "Unsupported register bit width: 0x%X", Reg->BitWidth)); + "Unsupported register access width: 0x%X", Reg->AccessWidth)); return (AE_SUPPORT); } - /* Validate the BitOffset. Just a warning for now. */ + /* Validate the BitWidth, convert AccessWidth into number of bits */ - if (Reg->BitOffset != 0) + AccessWidth = AcpiHwGetAccessBitWidth (*Address, Reg, MaxBitWidth); + BitWidth = ACPI_ROUND_UP (Reg->BitOffset + Reg->BitWidth, AccessWidth); + if (MaxBitWidth < BitWidth) { ACPI_WARNING ((AE_INFO, - "Unsupported register bit offset: 0x%X", Reg->BitOffset)); + "Requested bit width 0x%X is smaller than register bit width 0x%X", + MaxBitWidth, BitWidth)); + return (AE_SUPPORT); } return (AE_OK); @@ -158,10 +252,7 @@ AcpiHwValidateRegister ( * 64-bit values is not needed. * * LIMITATIONS: <These limitations also apply to AcpiHwWrite> - * BitWidth must be exactly 8, 16, or 32. * SpaceID must be SystemMemory or SystemIO. - * BitOffset and AccessWidth are currently ignored, as there has - * not been a need to implement these. * ******************************************************************************/ @@ -171,7 +262,12 @@ AcpiHwRead ( ACPI_GENERIC_ADDRESS *Reg) { UINT64 Address; + UINT8 AccessWidth; + UINT32 BitWidth; + UINT8 BitOffset; UINT64 Value64; + UINT32 Value32; + UINT8 Index; ACPI_STATUS Status; @@ -186,30 +282,58 @@ AcpiHwRead ( return (Status); } - /* Initialize entire 32-bit return value to zero */ - + /* + * Initialize entire 32-bit return value to zero, convert AccessWidth + * into number of bits based + */ *Value = 0; + AccessWidth = AcpiHwGetAccessBitWidth (Address, Reg, 32); + BitWidth = Reg->BitOffset + Reg->BitWidth; + BitOffset = Reg->BitOffset; /* * Two address spaces supported: Memory or IO. PCI_Config is * not supported here because the GAS structure is insufficient */ - if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_MEMORY) + Index = 0; + while (BitWidth) { - Status = AcpiOsReadMemory ((ACPI_PHYSICAL_ADDRESS) - Address, &Value64, Reg->BitWidth); + if (BitOffset >= AccessWidth) + { + Value32 = 0; + BitOffset -= AccessWidth; + } + else + { + if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_MEMORY) + { + Status = AcpiOsReadMemory ((ACPI_PHYSICAL_ADDRESS) + Address + Index * ACPI_DIV_8 (AccessWidth), + &Value64, AccessWidth); + Value32 = (UINT32) Value64; + } + else /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */ + { + Status = AcpiHwReadPort ((ACPI_IO_ADDRESS) + Address + Index * ACPI_DIV_8 (AccessWidth), + &Value32, AccessWidth); + } + } - *Value = (UINT32) Value64; - } - else /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */ - { - Status = AcpiHwReadPort ((ACPI_IO_ADDRESS) - Address, Value, Reg->BitWidth); + /* + * Use offset style bit writes because "Index * AccessWidth" is + * ensured to be less than 32-bits by AcpiHwValidateRegister(). + */ + ACPI_SET_BITS (Value, Index * AccessWidth, + ACPI_MASK_BITS_ABOVE_32 (AccessWidth), Value32); + + BitWidth -= BitWidth > AccessWidth ? AccessWidth : BitWidth; + Index++; } ACPI_DEBUG_PRINT ((ACPI_DB_IO, "Read: %8.8X width %2d from %8.8X%8.8X (%s)\n", - *Value, Reg->BitWidth, ACPI_FORMAT_UINT64 (Address), + *Value, AccessWidth, ACPI_FORMAT_UINT64 (Address), AcpiUtGetRegionName (Reg->SpaceId))); return (Status); @@ -237,6 +361,12 @@ AcpiHwWrite ( ACPI_GENERIC_ADDRESS *Reg) { UINT64 Address; + UINT8 AccessWidth; + UINT32 BitWidth; + UINT8 BitOffset; + UINT64 Value64; + UINT32 Value32; + UINT8 Index; ACPI_STATUS Status; @@ -251,24 +381,58 @@ AcpiHwWrite ( return (Status); } + /* Convert AccessWidth into number of bits based */ + + AccessWidth = AcpiHwGetAccessBitWidth (Address, Reg, 32); + BitWidth = Reg->BitOffset + Reg->BitWidth; + BitOffset = Reg->BitOffset; + /* * Two address spaces supported: Memory or IO. PCI_Config is * not supported here because the GAS structure is insufficient */ - if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_MEMORY) + Index = 0; + while (BitWidth) { - Status = AcpiOsWriteMemory ((ACPI_PHYSICAL_ADDRESS) - Address, (UINT64) Value, Reg->BitWidth); - } - else /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */ - { - Status = AcpiHwWritePort ((ACPI_IO_ADDRESS) - Address, Value, Reg->BitWidth); + /* + * Use offset style bit reads because "Index * AccessWidth" is + * ensured to be less than 32-bits by AcpiHwValidateRegister(). + */ + Value32 = ACPI_GET_BITS (&Value, Index * AccessWidth, + ACPI_MASK_BITS_ABOVE_32 (AccessWidth)); + + if (BitOffset >= AccessWidth) + { + BitOffset -= AccessWidth; + } + else + { + if (Reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_MEMORY) + { + Value64 = (UINT64) Value32; + Status = AcpiOsWriteMemory ((ACPI_PHYSICAL_ADDRESS) + Address + Index * ACPI_DIV_8 (AccessWidth), + Value64, AccessWidth); + } + else /* ACPI_ADR_SPACE_SYSTEM_IO, validated earlier */ + { + Status = AcpiHwWritePort ((ACPI_IO_ADDRESS) + Address + Index * ACPI_DIV_8 (AccessWidth), + Value32, AccessWidth); + } + } + + /* + * Index * AccessWidth is ensured to be less than 32-bits by + * AcpiHwValidateRegister(). + */ + BitWidth -= BitWidth > AccessWidth ? AccessWidth : BitWidth; + Index++; } ACPI_DEBUG_PRINT ((ACPI_DB_IO, "Wrote: %8.8X width %2d to %8.8X%8.8X (%s)\n", - Value, Reg->BitWidth, ACPI_FORMAT_UINT64 (Address), + Value, AccessWidth, ACPI_FORMAT_UINT64 (Address), AcpiUtGetRegionName (Reg->SpaceId))); return (Status); |