diff options
Diffstat (limited to 'Silicon/Intel/LewisburgPkg/Library/PeiDxeSmmGpioLib/GpioLib.c')
-rw-r--r-- | Silicon/Intel/LewisburgPkg/Library/PeiDxeSmmGpioLib/GpioLib.c | 2744 |
1 files changed, 2744 insertions, 0 deletions
diff --git a/Silicon/Intel/LewisburgPkg/Library/PeiDxeSmmGpioLib/GpioLib.c b/Silicon/Intel/LewisburgPkg/Library/PeiDxeSmmGpioLib/GpioLib.c new file mode 100644 index 0000000000..0b82c2b8df --- /dev/null +++ b/Silicon/Intel/LewisburgPkg/Library/PeiDxeSmmGpioLib/GpioLib.c @@ -0,0 +1,2744 @@ +/** @file + +Copyright (c) 2018, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "GpioLibrary.h" +#include <Uefi/UefiMultiPhase.h> +#include <Pi/PiMultiPhase.h> +#include <Library/HobLib.h> + +// +// Possible registers to be accessed using GpioReadWriteReg() function +// +typedef enum { + GpioHostOwnershipRegister = 0, + GpioGpeEnableRegister, + GpioSmiEnableRegister, + GpioNmiEnableRegister, + GpioPadConfigLockRegister, + GpioPadLockOutputRegister +} GPIO_REG; + +/** + This procedure will write or read GPIO Pad Configuration register + + @param[in] GpioPad GPIO pad + @param[in] DwReg Choose PADCFG register: 0:DW0, 1:DW1 + @param[in] Mask Mask + @param[in] Write Perform read(0) or write(1) + @param[in,out] ReadWriteValue Read/Write data + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number + @retval EFI_UNSUPPORTED Host cannot access this pad +**/ +static +EFI_STATUS +GpioReadWritePadCfgReg ( + IN GPIO_PAD GpioPad, + IN UINT8 DwReg, + IN UINT32 Mask, + IN BOOLEAN Write, + IN OUT UINT32 *ReadWriteVal + ) +{ + UINT32 PadCfgReg; + GPIO_GROUP_INFO *GpioGroupInfo; + UINTN GpioGroupInfoLength; + UINT32 GroupIndex; + UINT32 PadNumber; + + + GPIO_PAD_OWN PadOwnVal; + + + GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad); + PadNumber = GpioGetPadNumberFromGpioPad (GpioPad); + +DEBUG_CODE_BEGIN(); + if (!GpioIsCorrectPadForThisChipset (GpioPad)) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Incorrect GpioPad define used on this chipset (Group=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + return EFI_UNSUPPORTED; + } +DEBUG_CODE_END(); + + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + // + // Check if group argument exceeds GPIO GROUP INFO array + // + if ((UINTN)GroupIndex >= GpioGroupInfoLength) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group argument (%d) exceeds GPIO group range\n", GroupIndex)); + return EFI_INVALID_PARAMETER; + } + + // + // Check if legal pin number + // + if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pin number (%d) exceeds possible range for this group\n", PadNumber)); + return EFI_INVALID_PARAMETER; + } + + if (Write && (DwReg == 1 || (Mask & ~B_PCH_GPIO_TX_STATE) != 0) && GpioIsPadLocked (GroupIndex, PadNumber)) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pad is locked (Group=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + return EFI_WRITE_PROTECTED; + } + +DEBUG_CODE_BEGIN(); + // + // Check if selected GPIO Pad is not owned by CSME/ISH + // If GPIO is not owned by Host all access to PadCfg will be dropped + // + GpioGetPadOwnership (GpioPad, &PadOwnVal); + if (PadOwnVal != GpioPadOwnHost) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Accessing pad not owned by host (Group=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + return EFI_UNSUPPORTED; + } + + // +DEBUG_CODE_END(); + + // + // Create Pad Configuration register offset + // + PadCfgReg = 0x8 * PadNumber + GpioGroupInfo[GroupIndex].PadCfgOffset; + if(DwReg == 1) { + PadCfgReg += 0x4; + } + + if (Write) { + MmioAndThenOr32 ( + (UINTN)PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, PadCfgReg), + (UINT32)(~Mask), + (UINT32)(*ReadWriteVal & Mask) + ); + } else { + *ReadWriteVal = MmioRead32 (PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, PadCfgReg)); + *ReadWriteVal &= Mask; + } + + return EFI_SUCCESS; +} + +/** + This procedure will write or read GPIO register + + @param[in] RegType GPIO register type + @param[in] Group GPIO group + @param[in] DwNum Register number for current group (parameter applicable in accessing whole register). + For group which has less then 32 pads per group DwNum must be 0. + @param[in] GpioPad GPIO pad + @param[in] Write Perform read(0) or write(1) + @param[in] OnePad Access whole register(0) or one pad(1) + @param[in,out] ReadWriteValue Read/Write data + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group, pad or DwNum parameter number +**/ +static +EFI_STATUS +GpioReadWriteReg ( + IN GPIO_REG RegType, + IN GPIO_GROUP Group, + IN UINT32 DwNum, + IN GPIO_PAD GpioPad, + IN BOOLEAN Write, + IN BOOLEAN OnePad, + IN OUT UINT32 *ReadWriteVal + ) +{ + UINT32 Mask; + UINT32 RegOffset; + UINT32 GroupIndex; + UINT32 PadNumber; + GPIO_GROUP_INFO *GpioGroupInfo; + UINTN GpioGroupInfoLength; + + RegOffset = 0; + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + if (OnePad) { + GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad); + PadNumber = GpioGetPadNumberFromGpioPad (GpioPad); +DEBUG_CODE_BEGIN(); + if (!GpioIsCorrectPadForThisChipset (GpioPad)) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Incorrect GpioPad define used on this chipset (Group=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + return EFI_UNSUPPORTED; + } +DEBUG_CODE_END(); + } else { + GroupIndex = GpioGetGroupIndexFromGroup (Group); + PadNumber = 0; + } + // + // Check if group argument exceeds GPIO GROUP INFO array + // + if ((UINTN)GroupIndex >= GpioGroupInfoLength) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group argument (%d) exceeds GPIO group range\n", GroupIndex)); + return EFI_INVALID_PARAMETER; + } + + switch (RegType) { + case GpioHostOwnershipRegister: + RegOffset = GpioGroupInfo[GroupIndex].HostOwnOffset; + break; + case GpioGpeEnableRegister: + RegOffset = GpioGroupInfo[GroupIndex].GpiGpeEnOffset; + break; + case GpioSmiEnableRegister: + RegOffset = GpioGroupInfo[GroupIndex].SmiEnOffset; + break; + case GpioNmiEnableRegister: + RegOffset = GpioGroupInfo[GroupIndex].NmiEnOffset; + break; + case GpioPadConfigLockRegister: + RegOffset = GpioGroupInfo[GroupIndex].PadCfgLockOffset; + break; + case GpioPadLockOutputRegister: + RegOffset = GpioGroupInfo[GroupIndex].PadCfgLockTxOffset; + break; + default: + ASSERT (FALSE); + break; + } + + // + // Check if selected register exists + // + if (RegOffset == NO_REGISTER_FOR_PROPERTY) { + return EFI_INVALID_PARAMETER; + } + + // + // Access one GPIO Pad + // + if (OnePad) { + // + // Check if legal pin number + // + if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup){ + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pin number (%d) exceeds possible range for this group\n", PadNumber)); + return EFI_INVALID_PARAMETER; + } + // + // For future use. If there are more then 32 pads per group then certain + // group information would be split into more then one DWord register. + // + RegOffset += (PadNumber >> 5) * 0x4; + // + // Calculate pad bit position within DWord register + // + PadNumber %= 32; + Mask = BIT0 << PadNumber; + + if (Write) { + MmioAndThenOr32 ( + (UINTN)PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, RegOffset), + (UINT32)(~Mask), + (UINT32)((*ReadWriteVal << PadNumber) & Mask) + ); + } else { + *ReadWriteVal = MmioRead32 (PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, RegOffset)); + *ReadWriteVal = (*ReadWriteVal & Mask) >> PadNumber; + } + // + // Access whole register + // + } else { + // + // Check if DwNum argument does not exceed number of DWord registers + // resulting from available pads for certain group + // + if (DwNum > ((GpioGroupInfo[GroupIndex].PadPerGroup - 1) >> 5)){ + return EFI_INVALID_PARAMETER; + } + // + // For future use. If there are more then 32 pads per group then certain + // group information would be split into more then one DWord register. + // For SKL PCH DwNum must be 0. + // + RegOffset += DwNum *0x4; + + if (Write) { + MmioWrite32 ( + (UINTN)PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, RegOffset), + (UINT32)(*ReadWriteVal) + ); + } else { + *ReadWriteVal = MmioRead32 (PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, RegOffset)); + } + } + + return EFI_SUCCESS; +} + +/** + This procedure will write GPIO Lock/LockTx register using SBI. + + @param[in] RegType GPIO register (Lock or LockTx) + @param[in] Unlock Lock pads(0) or unlock(1) + @param[in] Group GPIO group number + @param[in] DwNum Register number for current group (parameter applicable in accessing whole register). + For group which has less then 32 pads per group DwNum must be 0. + @param[in] PadsToModify Bit mask for pads that are going to be modified + @param[in] GpioPad GPIO pad + @param[in] OnePad Access whole register(0) or one pad(1) + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group, pad or DwNum parameter number +**/ +static +EFI_STATUS +GpioLockPadsUsingSbi ( + IN GPIO_REG RegType, + IN BOOLEAN Unlock, + IN GPIO_GROUP Group, + IN UINT32 DwNum, + IN UINT32 PadsToModify, + IN GPIO_PAD GpioPad, + IN BOOLEAN OnePad + ) +{ + UINT8 Response; + EFI_STATUS Status; + GPIO_GROUP_INFO *GpioGroupInfo; + UINTN GpioGroupInfoLength; + UINT32 RegOffset; + UINT32 OldPadCfgLockRegVal; + UINT32 NewPadCfgLockRegVal; + UINT32 GroupIndex; + UINT32 PadNumber; + + RegOffset = 0; + OldPadCfgLockRegVal = 0; + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + if (OnePad) { + GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad); + PadNumber = GpioGetPadNumberFromGpioPad (GpioPad); + Group = GpioGetGroupFromGpioPad (GpioPad); +DEBUG_CODE_BEGIN(); + if (!GpioIsCorrectPadForThisChipset (GpioPad)) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Incorrect GpioPad define used on this chipset (Group=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + return EFI_UNSUPPORTED; + } +DEBUG_CODE_END(); + } else { + GroupIndex = GpioGetGroupIndexFromGroup (Group); + PadNumber = 0; + } + + // + // Check if group argument exceeds GPIO GROUP INFO array + // + if ((UINTN)GroupIndex >= GpioGroupInfoLength) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group argument (%d) exceeds GPIO group range\n", GroupIndex)); + return EFI_INVALID_PARAMETER; + } + + switch (RegType) { + case GpioPadConfigLockRegister: + RegOffset = GpioGroupInfo[GroupIndex].PadCfgLockOffset; + break; + case GpioPadLockOutputRegister: + RegOffset = GpioGroupInfo[GroupIndex].PadCfgLockTxOffset; + break; + default: + ASSERT (FALSE); + break; + } + + // + // Check if selected register exists + // + if (RegOffset == NO_REGISTER_FOR_PROPERTY) { + return EFI_INVALID_PARAMETER; + } + + // + // Access one GPIO Pad + // + if (OnePad) { + // + // Check if legal pin number + // + if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup){ + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pin number (%d) exceeds possible range for this group\n", PadNumber)); + return EFI_INVALID_PARAMETER; + } + + // + // For future use. If there are more then 32 pads per group then certain + // group information would be split into more then one DWord register. + // + DwNum = (PadNumber >> 5); + RegOffset += DwNum * 0x4; + // + // Calculate pad bit position within DWord register + // + PadNumber %= 32; + + switch (RegType) { + case GpioPadConfigLockRegister: + GpioGetPadCfgLockForGroupDw (Group, DwNum, &OldPadCfgLockRegVal); + break; + case GpioPadLockOutputRegister: + GpioGetPadCfgLockTxForGroupDw (Group, DwNum, &OldPadCfgLockRegVal); + break; + } + if (Unlock) { + NewPadCfgLockRegVal = OldPadCfgLockRegVal & (~(0x1 << PadNumber)); + } else { + NewPadCfgLockRegVal = OldPadCfgLockRegVal | (0x1 << PadNumber); + } + + } else { + // + // Access whole register + // + + // + // Check if DwNum argument does not exceed number of DWord registers + // resulting from available pads for certain group + // + if (DwNum > ((GpioGroupInfo[GroupIndex].PadPerGroup - 1) >> 5)){ + return EFI_INVALID_PARAMETER; + } + // + // For future use. If there are more then 32 pads per group then certain + // group information would be split into more then one DWord register. + // For SKL PCH DwNum must be 0. + // + RegOffset += DwNum *0x4; + + switch (RegType) { + case GpioPadConfigLockRegister: + GpioGetPadCfgLockForGroupDw (Group, DwNum, &OldPadCfgLockRegVal); + break; + case GpioPadLockOutputRegister: + GpioGetPadCfgLockTxForGroupDw (Group, DwNum, &OldPadCfgLockRegVal); + break; + } + if (Unlock) { + NewPadCfgLockRegVal = OldPadCfgLockRegVal & (~PadsToModify); + } else { + NewPadCfgLockRegVal = OldPadCfgLockRegVal | PadsToModify; + } + } + + Status = PchSbiExecution ( + GpioGroupInfo[GroupIndex].Community, + RegOffset, + GpioLockUnlock, + FALSE, + &NewPadCfgLockRegVal, + &Response + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will read multiple GPIO settings + + @param[in] GpioPad GPIO Pad + @param[out] GpioData GPIO data structure + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioGetPadConfig ( + IN GPIO_PAD GpioPad, + OUT GPIO_CONFIG *GpioData + ) +{ + UINT32 Dw0Reg; + UINT32 Dw1Reg; + UINT32 PadCfgReg; + UINT32 RegVal; + GPIO_GROUP_INFO *GpioGroupInfo; + UINTN GpioGroupInfoLength; + GPIO_GROUP Group; + UINT32 GroupIndex; + UINT32 PadNumber; + + GPIO_PAD_OWN PadOwnVal; + + + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + Group = GpioGetGroupFromGpioPad (GpioPad); + GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad); + PadNumber = GpioGetPadNumberFromGpioPad (GpioPad); + +DEBUG_CODE_BEGIN(); + if (!GpioIsCorrectPadForThisChipset (GpioPad)) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Incorrect GpioPad define used on this chipset (Group=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + GpioGetPadOwnership (GpioPad, &PadOwnVal); + if (PadOwnVal != GpioPadOwnHost) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Accessing pad not owned by host (Group=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + return EFI_UNSUPPORTED; + } +DEBUG_CODE_END(); + + // + // Check if group argument exceeds GPIO group range + // + if ((Group < GpioGetLowestGroup ()) || (Group > GpioGetHighestGroup ())) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group argument (%d) exceeds GPIO group range\n", GroupIndex)); + return EFI_INVALID_PARAMETER; + } + + // + // Check if legal pin number + // + if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup){ + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pin number (%d) exceeds possible range for this group\n", PadNumber)); + return EFI_INVALID_PARAMETER; + } + + // + // Create PADCFG register offset using group and pad number + // + PadCfgReg = 0x8 * PadNumber + GpioGroupInfo[GroupIndex].PadCfgOffset; + + // + // Read PADCFG DW0 register + // + Dw0Reg = MmioRead32 ((UINTN)PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, PadCfgReg)); + + // + // Read PADCFG DW1 register + // + Dw1Reg = MmioRead32 ((UINTN)PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, PadCfgReg + 0x4)); + + + // + // Get Reset Type (PadRstCfg) + // + GpioData->PowerConfig = ((Dw0Reg & B_PCH_GPIO_RST_CONF) >> (N_PCH_GPIO_RST_CONF - (GPIO_CONF_RESET_BIT_POS + 1))) | (0x1 << GPIO_CONF_RESET_BIT_POS); + + // + // Get how interrupt is triggered (RxEvCfg) + // + GpioData->InterruptConfig = ((Dw0Reg & B_PCH_GPIO_RX_LVL_EDG) >> (N_PCH_GPIO_RX_LVL_EDG - (GPIO_CONF_INT_TRIG_BIT_POS + 1))) | (0x1 << GPIO_CONF_INT_TRIG_BIT_POS); + + // + // Get interrupt generation (GPIRoutIOxAPIC/SCI/SMI/NMI) + // + GpioData->InterruptConfig |= ((Dw0Reg & (B_PCH_GPIO_RX_NMI_ROUTE | B_PCH_GPIO_RX_SCI_ROUTE | B_PCH_GPIO_RX_SMI_ROUTE | B_PCH_GPIO_RX_APIC_ROUTE)) >> (N_PCH_GPIO_RX_NMI_ROUTE - (GPIO_CONF_INT_ROUTE_BIT_POS + 1))) | (0x1 << GPIO_CONF_INT_ROUTE_BIT_POS); + + // + // Get GPIO direction (GPIORxDis and GPIOTxDis) + // + GpioData->Direction = ((Dw0Reg & (B_PCH_GPIO_RXDIS | B_PCH_GPIO_TXDIS)) >> (N_PCH_GPIO_TXDIS - (GPIO_CONF_DIR_BIT_POS + 1))) | (0x1 << GPIO_CONF_DIR_BIT_POS); + + // + // Get GPIO input inversion (RXINV) + // + GpioData->Direction |= ((Dw0Reg & B_PCH_GPIO_RXINV) >> (N_PCH_GPIO_RXINV - (GPIO_CONF_INV_BIT_POS + 1))) | (0x1 << GPIO_CONF_INV_BIT_POS); + + // + // Get GPIO output state (GPIOTxState) + // + GpioData->OutputState = ((Dw0Reg & B_PCH_GPIO_TX_STATE) << (N_PCH_GPIO_TX_STATE + (GPIO_CONF_OUTPUT_BIT_POS + 1))) | (0x1 << GPIO_CONF_OUTPUT_BIT_POS) ; + + // + // Configure GPIO RX raw override to '1' (RXRAW1) + // + GpioData->OtherSettings = ((Dw0Reg & B_PCH_GPIO_RX_RAW1) >> (N_PCH_GPIO_RX_RAW1 - (GPIO_CONF_RXRAW_BIT_POS + 1))) | (0x1 << GPIO_CONF_RXRAW_BIT_POS) ; + + // + // Get GPIO Pad Mode (PMode) + // + GpioData->PadMode = ((Dw0Reg & B_PCH_GPIO_PAD_MODE) >> (N_PCH_GPIO_PAD_MODE - (GPIO_CONF_PAD_MODE_BIT_POS + 1))) | (0x1 << GPIO_CONF_PAD_MODE_BIT_POS); + + // + // Get GPIO termination (Term) + // + GpioData->ElectricalConfig = ((Dw1Reg & B_PCH_GPIO_TERM) >> (N_PCH_GPIO_TERM - (GPIO_CONF_TERM_BIT_POS + 1))) | (0x1 << GPIO_CONF_TERM_BIT_POS) ; + + // + // Get GPIO pad tolerance (padtol) + // + GpioData->ElectricalConfig |= ((Dw1Reg & B_PCH_GPIO_PADTOL) >> (N_PCH_GPIO_PADTOL - (GPIO_CONF_PADTOL_BIT_POS + 1))) | (0x1 << GPIO_CONF_PADTOL_BIT_POS) ; + + // + // Read HOSTSW_OWN registers + // + RegVal = MmioRead32 ((UINTN)PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, GpioGroupInfo[GroupIndex].HostOwnOffset)); + + // + // Get Host Software Ownership + // + GpioData->HostSoftPadOwn = (((RegVal >> PadNumber) & 0x1) << (GPIO_CONF_HOST_OWN_BIT_POS + 1)) | (0x1 << GPIO_CONF_HOST_OWN_BIT_POS); + + // + // Read PADCFGLOCK register + // + RegVal = MmioRead32 ((UINTN)PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, GpioGroupInfo[GroupIndex].PadCfgLockOffset)); + + // + // Get Pad Configuration Lock state + // + GpioData->LockConfig = (((RegVal >> PadNumber) & 0x1) << 1) | 0x1; + + // + // Read PADCFGLOCKTX register + // + RegVal = MmioRead32 ((UINTN)PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, GpioGroupInfo[GroupIndex].PadCfgLockTxOffset)); + + // + // Get Pad Configuration Lock Tx state + // + GpioData->LockConfig |= (((RegVal >> PadNumber) & 0x1) << 2) | 0x1; + + return EFI_SUCCESS; +} + +/** + This procedure will configure multiple GPIO settings + + @param[in] GpioPad GPIO Pad + @param[in] GpioData GPIO data structure + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioSetPadConfig ( + IN GPIO_PAD GpioPad, + IN GPIO_CONFIG *GpioData + ) +{ + UINT32 Dw0Reg; + UINT32 Dw0RegMask; + UINT32 Dw1Reg; + UINT32 Dw1RegMask; + UINT32 PadCfgReg; + UINT32 HostSoftOwnReg; + UINT32 HostSoftOwnRegMask; + UINT32 GpiGpeEnReg; + UINT32 GpiGpeEnRegMask; + GPIO_GROUP_INFO *GpioGroupInfo; + UINTN GpioGroupInfoLength; + GPIO_GROUP Group; + UINT32 GroupIndex; + UINT32 PadNumber; + + GPIO_PAD_OWN PadOwnVal; + + + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + Dw0RegMask = 0; + Dw0Reg = 0; + Dw1RegMask = 0; + Dw1Reg = 0; + + Group = GpioGetGroupFromGpioPad (GpioPad); + GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad); + PadNumber = GpioGetPadNumberFromGpioPad (GpioPad); + +DEBUG_CODE_BEGIN(); + if (!GpioIsCorrectPadForThisChipset (GpioPad)) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Incorrect GpioPad define used on this chipset (Group=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + GpioGetPadOwnership (GpioPad, &PadOwnVal); + if (PadOwnVal != GpioPadOwnHost) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Accessing pad not owned by host (Group=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + return EFI_UNSUPPORTED; + } +DEBUG_CODE_END(); + // + // Check if group argument exceeds GPIO group range + // + if ((Group < GpioGetLowestGroup ()) || (Group > GpioGetHighestGroup ())) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group argument (%d) exceeds GPIO group range\n", GroupIndex)); + return EFI_INVALID_PARAMETER; + } + + // + // Check if legal pin number + // + if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pin number (%d) exceeds possible range for this group\n", PadNumber)); + return EFI_INVALID_PARAMETER; + } + + if (GpioIsPadLocked (GroupIndex, PadNumber)) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pad is locked (Group=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + return EFI_WRITE_PROTECTED; + } + + // + // Configure Reset Type (PadRstCfg) + // + Dw0RegMask |= ((((GpioData->PowerConfig & GPIO_CONF_RESET_MASK) >> GPIO_CONF_RESET_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_RST_CONF); + Dw0Reg |= (((GpioData->PowerConfig & GPIO_CONF_RESET_MASK) >> (GPIO_CONF_RESET_BIT_POS + 1)) << N_PCH_GPIO_RST_CONF); + + // + // Configure how interrupt is triggered (RxEvCfg) + // + Dw0RegMask |= ((((GpioData->InterruptConfig & GPIO_CONF_INT_TRIG_MASK) >> GPIO_CONF_INT_TRIG_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_RX_LVL_EDG); + Dw0Reg |= (((GpioData->InterruptConfig & GPIO_CONF_INT_TRIG_MASK) >> (GPIO_CONF_INT_TRIG_BIT_POS + 1)) << N_PCH_GPIO_RX_LVL_EDG); + + // + // Configure interrupt generation (GPIRoutIOxAPIC/SCI/SMI/NMI) + // + Dw0RegMask |= ((((GpioData->InterruptConfig & GPIO_CONF_INT_ROUTE_MASK) >> GPIO_CONF_INT_ROUTE_BIT_POS) == GpioHardwareDefault) ? 0x0 : (B_PCH_GPIO_RX_NMI_ROUTE | B_PCH_GPIO_RX_SCI_ROUTE | B_PCH_GPIO_RX_SMI_ROUTE | B_PCH_GPIO_RX_APIC_ROUTE)); + Dw0Reg |= (((GpioData->InterruptConfig & GPIO_CONF_INT_ROUTE_MASK) >> (GPIO_CONF_INT_ROUTE_BIT_POS + 1)) << N_PCH_GPIO_RX_NMI_ROUTE); + + // + // Configure GPIO direction (GPIORxDis and GPIOTxDis) + // + Dw0RegMask |= ((((GpioData->Direction & GPIO_CONF_DIR_MASK) >> GPIO_CONF_DIR_BIT_POS) == GpioHardwareDefault) ? 0x0 : (B_PCH_GPIO_RXDIS | B_PCH_GPIO_TXDIS)); + Dw0Reg |= (((GpioData->Direction & GPIO_CONF_DIR_MASK) >> (GPIO_CONF_DIR_BIT_POS + 1)) << N_PCH_GPIO_TXDIS); + + // + // Configure GPIO input inversion (RXINV) + // + Dw0RegMask |= ((((GpioData->Direction & GPIO_CONF_INV_MASK) >> GPIO_CONF_INV_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_RXINV); + Dw0Reg |= (((GpioData->Direction & GPIO_CONF_INV_MASK) >> (GPIO_CONF_INV_BIT_POS + 1)) << N_PCH_GPIO_RXINV); + + // + // Configure GPIO output state (GPIOTxState) + // + Dw0RegMask |= ((((GpioData->OutputState & GPIO_CONF_OUTPUT_MASK) >> GPIO_CONF_OUTPUT_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_TX_STATE); + Dw0Reg |= (((GpioData->OutputState & GPIO_CONF_OUTPUT_MASK) >> (GPIO_CONF_OUTPUT_BIT_POS + 1)) << N_PCH_GPIO_TX_STATE); + + // + // Configure GPIO RX raw override to '1' (RXRAW1) + // + Dw0RegMask |= ((((GpioData->OtherSettings & GPIO_CONF_RXRAW_MASK) >> GPIO_CONF_RXRAW_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_RX_RAW1); + Dw0Reg |= (((GpioData->OtherSettings & GPIO_CONF_RXRAW_MASK) >> (GPIO_CONF_RXRAW_BIT_POS + 1)) << N_PCH_GPIO_RX_RAW1); + + // + // Configure GPIO Pad Mode (PMode) + // + Dw0RegMask |= ((((GpioData->PadMode & GPIO_CONF_PAD_MODE_MASK) >> GPIO_CONF_PAD_MODE_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_PAD_MODE); + Dw0Reg |= (((GpioData->PadMode & GPIO_CONF_PAD_MODE_MASK) >> (GPIO_CONF_PAD_MODE_BIT_POS + 1)) << N_PCH_GPIO_PAD_MODE); + + // + // Configure GPIO termination (Term) + // + Dw1RegMask |= ((((GpioData->ElectricalConfig & GPIO_CONF_TERM_MASK) >> GPIO_CONF_TERM_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_TERM); + Dw1Reg |= (((GpioData->ElectricalConfig & GPIO_CONF_TERM_MASK) >> (GPIO_CONF_TERM_BIT_POS + 1)) << N_PCH_GPIO_TERM); + + // + // Configure GPIO pad tolerance (padtol) + // + Dw1RegMask |= ((((GpioData->ElectricalConfig & GPIO_CONF_PADTOL_MASK) >> GPIO_CONF_PADTOL_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_PADTOL); + Dw1Reg |= (((GpioData->ElectricalConfig & GPIO_CONF_PADTOL_MASK) >> (GPIO_CONF_PADTOL_BIT_POS + 1)) << N_PCH_GPIO_PADTOL); + + // + // Create PADCFG register offset using group and pad number + // + PadCfgReg = 0x8 * PadNumber + GpioGroupInfo[GroupIndex].PadCfgOffset; + + // + // Write PADCFG DW0 register + // + MmioAndThenOr32 ( + (UINTN)PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, PadCfgReg), + ~(UINT32)Dw0RegMask, + (UINT32)Dw0Reg + ); + + // + // Write PADCFG DW1 register + // + MmioAndThenOr32 ( + (UINTN)PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, PadCfgReg + 0x4), + ~(UINT32)Dw1RegMask, + (UINT32)Dw1Reg + ); + + // + // Update value to be programmed in HOSTSW_OWN register + // + HostSoftOwnRegMask = (GpioData->HostSoftPadOwn & 0x1) << PadNumber; + HostSoftOwnReg = (GpioData->HostSoftPadOwn >> 0x1) << PadNumber; + + // + // Write HOSTSW_OWN registers + // + MmioAndThenOr32 ( + (UINTN)PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, GpioGroupInfo[GroupIndex].HostOwnOffset), + ~(UINT32)HostSoftOwnRegMask, + (UINT32)HostSoftOwnReg + ); + + // + // Update value to be programmed in GPI_GPE_EN register + // + GpiGpeEnRegMask = (GpioData->InterruptConfig & 0x1) << PadNumber; + GpiGpeEnReg = ((GpioData->InterruptConfig & GpioIntSci) >> 3) << PadNumber; + + // + // Write GPI_GPE_EN registers + // + MmioAndThenOr32 ( + (UINTN)PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, GpioGroupInfo[GroupIndex].GpiGpeEnOffset), + ~(UINT32)GpiGpeEnRegMask, + (UINT32)GpiGpeEnReg + ); + + // + // Program Pad Configuration Lock + // + if ((GpioData->LockConfig & GpioPadConfigLock) == GpioPadConfigLock) { + GpioLockPadsUsingSbi ( + GpioPadConfigLockRegister, + FALSE, + 0, + 0, + 0, + GpioPad, + TRUE + ); + } + + // + // Program Pad Configuration Lock Tx + // + if ((GpioData->LockConfig & GpioOutputStateLock) == GpioOutputStateLock) { + GpioLockPadsUsingSbi ( + GpioPadLockOutputRegister, + FALSE, + 0, + 0, + 0, + GpioPad, + TRUE + ); + } + return EFI_SUCCESS; +} + +/** + This procedure will set GPIO output level + + @param[in] GpioPad GPIO pad + @param[in] Value Output value + 0: OutputLow, 1: OutputHigh + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioSetOutputValue ( + IN GPIO_PAD GpioPad, + IN UINT32 Value + ) +{ + EFI_STATUS Status; + + Status = GpioReadWritePadCfgReg ( + GpioPad, + 0, + B_PCH_GPIO_TX_STATE, + TRUE, + &Value + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will get GPIO output level + + @param[in] GpioPad GPIO pad + @param[out] OutputVal GPIO Output value + 0: OutputLow, 1: OutputHigh + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioGetOutputValue ( + IN GPIO_PAD GpioPad, + OUT UINT32 *OutputVal + ) +{ + EFI_STATUS Status; + + Status = GpioReadWritePadCfgReg ( + GpioPad, + 0, + B_PCH_GPIO_TX_STATE, + FALSE, + OutputVal + ); + ASSERT_EFI_ERROR (Status); + *OutputVal >>= N_PCH_GPIO_TX_STATE; + + return Status; +} + +/** + This procedure will get GPIO input level + + @param[in] GpioPad GPIO pad + @param[out] InputVal GPIO Input value + 0: InputLow, 1: InpuHigh + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioGetInputValue ( + IN GPIO_PAD GpioPad, + OUT UINT32 *InputVal + ) +{ + EFI_STATUS Status; + + Status = GpioReadWritePadCfgReg ( + GpioPad, + 0, + B_PCH_GPIO_RX_STATE, + FALSE, + InputVal + ); + ASSERT_EFI_ERROR (Status); + *InputVal >>= N_PCH_GPIO_RX_STATE; + + return Status; +} + +/** + This procedure will get GPIO IOxAPIC interrupt number + + @param[in] GpioPad GPIO pad + @param[out] IrqNum IRQ number + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioGetPadIoApicIrqNumber ( + IN GPIO_PAD GpioPad, + OUT UINT32 *IrqNum + ) +{ + EFI_STATUS Status; + + Status = GpioReadWritePadCfgReg ( + GpioPad, + 1, + B_PCH_GPIO_INTSEL, + FALSE, + IrqNum + ); + ASSERT_EFI_ERROR (Status); + *IrqNum >>= N_PCH_GPIO_INTSEL; + + return Status; +} + +/** + This procedure will configure GPIO input inversion + + @param[in] GpioPad GPIO pad + @param[in] Value Value for GPIO input inversion + 0: No input inversion, 1: Invert input + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioSetInputInversion ( + IN GPIO_PAD GpioPad, + IN UINT32 Value + ) +{ + EFI_STATUS Status; + + Value <<= N_PCH_GPIO_RXINV; + Status = GpioReadWritePadCfgReg ( + GpioPad, + 0, + B_PCH_GPIO_RXINV, + TRUE, + &Value + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will get GPIO pad input inversion value + + @param[in] GpioPad GPIO pad + @param[out] InvertState GPIO inversion state + 0: No input inversion, 1: Inverted input + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioGetInputInversion ( + IN GPIO_PAD GpioPad, + OUT UINT32 *InvertState + ) +{ + EFI_STATUS Status; + + Status = GpioReadWritePadCfgReg ( + GpioPad, + 0, + B_PCH_GPIO_RXINV, + FALSE, + InvertState + ); + ASSERT_EFI_ERROR (Status); + *InvertState >>= N_PCH_GPIO_RXINV; + + return Status; +} + +/** + This procedure will set GPIO interrupt settings + + @param[in] GpioPad GPIO pad + @param[in] Value Value of Level/Edge + use GPIO_INT_CONFIG as argument + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioSetPadInterruptConfig ( + IN GPIO_PAD GpioPad, + IN GPIO_INT_CONFIG Value + ) +{ + EFI_STATUS Status; + UINT32 RxLvlEdgeValue; + UINT32 IntRouteValue; + UINT32 GpeEnable; + + Status = EFI_SUCCESS; + + if (((Value & GPIO_CONF_INT_TRIG_MASK) >> GPIO_CONF_INT_TRIG_BIT_POS) != GpioHardwareDefault) { + RxLvlEdgeValue = ((Value & GPIO_CONF_INT_TRIG_MASK) >> (GPIO_CONF_INT_TRIG_BIT_POS + 1)) << N_PCH_GPIO_RX_LVL_EDG; + + Status = GpioReadWritePadCfgReg ( + GpioPad, + 0, + B_PCH_GPIO_RX_LVL_EDG, + TRUE, + &RxLvlEdgeValue + ); + ASSERT_EFI_ERROR (Status); + } + + if (((Value & GPIO_CONF_INT_ROUTE_MASK) >> GPIO_CONF_INT_ROUTE_BIT_POS) != GpioHardwareDefault) { + + IntRouteValue = ((Value & GPIO_CONF_INT_ROUTE_MASK) >> (GPIO_CONF_INT_ROUTE_BIT_POS + 1)) << N_PCH_GPIO_RX_NMI_ROUTE; + + Status = GpioReadWritePadCfgReg ( + GpioPad, + 0, + (B_PCH_GPIO_RX_NMI_ROUTE | B_PCH_GPIO_RX_SCI_ROUTE | B_PCH_GPIO_RX_SMI_ROUTE | B_PCH_GPIO_RX_APIC_ROUTE), + TRUE, + &IntRouteValue + ); + ASSERT_EFI_ERROR (Status); + + if ((Value & GpioIntSci) == GpioIntSci) { + GpeEnable = 0x1; + } else { + GpeEnable = 0x0; + } + + Status = GpioReadWriteReg ( + GpioGpeEnableRegister, + 0, + 0, + GpioPad, + TRUE, + TRUE, + &GpeEnable + ); + ASSERT_EFI_ERROR (Status); + } + + return Status; +} + +/** + This procedure will set GPIO electrical settings + + @param[in] GpioPad GPIO pad + @param[in] Value Value of termination + use GPIO_ELECTRICAL_CONFIG as argument + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioSetPadElectricalConfig ( + IN GPIO_PAD GpioPad, + IN GPIO_ELECTRICAL_CONFIG Value + ) +{ + EFI_STATUS Status; + UINT32 TermValue; + UINT32 PadTolValue; + + Status = EFI_SUCCESS; + + if (((Value & GPIO_CONF_TERM_MASK) >> GPIO_CONF_TERM_BIT_POS) != GpioHardwareDefault) { + TermValue = ((Value & GPIO_CONF_TERM_MASK) >> (GPIO_CONF_TERM_BIT_POS + 1)) << N_PCH_GPIO_TERM; + + Status = GpioReadWritePadCfgReg ( + GpioPad, + 1, + B_PCH_GPIO_TERM, + TRUE, + &TermValue + ); + ASSERT_EFI_ERROR (Status); + } + + if (((Value & GPIO_CONF_PADTOL_MASK) >> GPIO_CONF_PADTOL_BIT_POS) != GpioHardwareDefault) { + PadTolValue = ((Value & GPIO_CONF_PADTOL_MASK) >> (GPIO_CONF_PADTOL_BIT_POS + 1)) << N_PCH_GPIO_PADTOL; + + Status = GpioReadWritePadCfgReg ( + GpioPad, + 1, + B_PCH_GPIO_PADTOL, + TRUE, + &PadTolValue + ); + ASSERT_EFI_ERROR (Status); + } + return Status; +} + +/** + This procedure will set GPIO Reset settings + + @param[in] GpioPad GPIO pad + @param[in] Value Value for Pad Reset Configuration + use GPIO_RESET_CONFIG as argument + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioSetPadResetConfig ( + IN GPIO_PAD GpioPad, + IN GPIO_RESET_CONFIG Value + ) +{ + EFI_STATUS Status; + UINT32 ResetValue; + + Status = EFI_SUCCESS; + + if (((Value & GPIO_CONF_RESET_MASK) >> GPIO_CONF_RESET_BIT_POS) != GpioHardwareDefault) { + ResetValue = ((Value & GPIO_CONF_RESET_MASK) >> (GPIO_CONF_RESET_BIT_POS + 1)) << N_PCH_GPIO_RST_CONF; + + Status = GpioReadWritePadCfgReg ( + GpioPad, + 0, + B_PCH_GPIO_RST_CONF, + TRUE, + &ResetValue + ); + ASSERT_EFI_ERROR (Status); + } + return Status; +} + +/** + This procedure will get GPIO Reset settings + + @param[in] GpioPad GPIO pad + @param[in] Value Value of Pad Reset Configuration + based on GPIO_RESET_CONFIG + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioGetPadResetConfig ( + IN GPIO_PAD GpioPad, + IN GPIO_RESET_CONFIG *Value + ) +{ + EFI_STATUS Status; + UINT32 ResetValue; + + Status = GpioReadWritePadCfgReg ( + GpioPad, + 0, + B_PCH_GPIO_RST_CONF, + FALSE, + &ResetValue + ); + ASSERT_EFI_ERROR (Status); + + // + // Get Reset Type (PadRstCfg) + // + *Value = (ResetValue >> (N_PCH_GPIO_RST_CONF - (GPIO_CONF_RESET_BIT_POS + 1))) | (0x1 << GPIO_CONF_RESET_BIT_POS); + + return Status; +} + +/** + This procedure will get GPIO Host Software Pad Ownership for certain group + + @param[in] Group GPIO group + @param[in] DwNum Host Ownership register number for current group. + For group which has less then 32 pads per group DwNum must be 0. + @param[out] HostSwRegVal Value of Host Software Pad Ownership register + Bit position - PadNumber + Bit value - 0: ACPI Mode, 1: GPIO Driver mode + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or DwNum parameter number +**/ +EFI_STATUS +GpioGetHostSwOwnershipForGroupDw ( + IN GPIO_GROUP Group, + IN UINT32 DwNum, + OUT UINT32 *HostSwRegVal + ) +{ + EFI_STATUS Status; + + Status = GpioReadWriteReg ( + GpioHostOwnershipRegister, + Group, + DwNum, + 0, + FALSE, + FALSE, + HostSwRegVal + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will get GPIO Host Software Pad Ownership for certain group + + @param[in] Group GPIO group + @param[in] DwNum Host Ownership register number for current group + For group which has less then 32 pads per group DwNum must be 0. + @param[in] HostSwRegVal Value of Host Software Pad Ownership register + Bit position - PadNumber + Bit value - 0: ACPI Mode, 1: GPIO Driver mode + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or DwNum parameter number +**/ +EFI_STATUS +GpioSetHostSwOwnershipForGroupDw ( + IN GPIO_GROUP Group, + IN UINT32 DwNum, + IN UINT32 HostSwRegVal + ) +{ + EFI_STATUS Status; + + Status = GpioReadWriteReg ( + GpioHostOwnershipRegister, + Group, + DwNum, + 0, + TRUE, + FALSE, + &HostSwRegVal + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will get Gpio Pad Host Software Ownership + + @param[in] GpioPad GPIO pad + @param[out] PadHostSwOwn Value of Host Software Pad Owner + 0: ACPI Mode, 1: GPIO Driver mode + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioGetHostSwOwnershipForPad ( + IN GPIO_PAD GpioPad, + OUT UINT32 *PadHostSwOwn + ) +{ + EFI_STATUS Status; + + Status = GpioReadWriteReg ( + GpioHostOwnershipRegister, + 0, + 0, + GpioPad, + FALSE, + TRUE, + PadHostSwOwn + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will set Gpio Pad Host Software Ownership + + @param[in] GpioPad GPIO pad + @param[in] PadHostSwOwn Pad Host Software Owner + 0: ACPI Mode, 1: GPIO Driver mode + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioSetHostSwOwnershipForPad ( + IN GPIO_PAD GpioPad, + IN UINT32 PadHostSwOwn + ) +{ + EFI_STATUS Status; + + Status = GpioReadWriteReg ( + GpioHostOwnershipRegister, + 0, + 0, + GpioPad, + TRUE, + TRUE, + &PadHostSwOwn + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will get Gpio Pad Ownership + + @param[in] GpioPad GPIO pad + @param[out] PadOwnVal Value of Pad Ownership + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioGetPadOwnership ( + IN GPIO_PAD GpioPad, + OUT GPIO_PAD_OWN *PadOwnVal + ) +{ + UINT32 Mask; + UINT32 RegOffset; + UINT32 GroupIndex; + UINT32 PadNumber; + GPIO_GROUP_INFO *GpioGroupInfo; + UINTN GpioGroupInfoLength; + UINT32 PadOwnRegValue; + + GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad); + PadNumber = GpioGetPadNumberFromGpioPad (GpioPad); + + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + // + // Check if group argument exceeds GPIO GROUP INFO array + // + if ((UINTN)GroupIndex >= GpioGroupInfoLength) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group argument (%d) exceeds GPIO group range\n", GroupIndex)); + ASSERT(FALSE); + return EFI_INVALID_PARAMETER; + } + + // + // Check if legal pin number + // + if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pin number (%d) exceeds possible range for this group\n", PadNumber)); + ASSERT(FALSE); + return EFI_INVALID_PARAMETER; + } + // + // Calculate RegOffset using Pad Ownership offset and GPIO Pad number. + // One DWord register contains information for 8 pads. + // + RegOffset = GpioGroupInfo[GroupIndex].PadOwnOffset + (PadNumber >> 3) * 0x4; + + // + // Calculate pad bit position within DWord register + // + PadNumber %= 8; + Mask = (BIT1 | BIT0) << (PadNumber * 4); + + PadOwnRegValue = MmioRead32 (PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, RegOffset)); + + *PadOwnVal = (GPIO_PAD_OWN)((PadOwnRegValue & Mask) >> (PadNumber * 4)); + + return EFI_SUCCESS; +} + +/** + This procedure will check state of Pad Config Lock for pads within one group + + @param[in] Group GPIO group + @param[in] DwNum PadCfgLock register number for current group. + For group which has less then 32 pads per group DwNum must be 0. + @param[out] PadCfgLockRegVal Value of PadCfgLock register + Bit position - PadNumber + Bit value - 0: NotLocked, 1: Locked + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or DwNum parameter number +**/ +EFI_STATUS +GpioGetPadCfgLockForGroupDw ( + IN GPIO_GROUP Group, + IN UINT32 DwNum, + OUT UINT32 *PadCfgLockRegVal + ) +{ + EFI_STATUS Status; + + Status = GpioReadWriteReg ( + GpioPadConfigLockRegister, + Group, + DwNum, + 0, + FALSE, + FALSE, + PadCfgLockRegVal + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will check state of Pad Config Lock for selected pad + + @param[in] GpioPad GPIO pad + @param[out] PadCfgLock PadCfgLock for selected pad + 0: NotLocked, 1: Locked + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioGetPadCfgLock ( + IN GPIO_PAD GpioPad, + OUT UINT32 *PadCfgLock + ) +{ + EFI_STATUS Status; + + Status = GpioReadWriteReg ( + GpioPadConfigLockRegister, + 0, + 0, + GpioPad, + FALSE, + TRUE, + PadCfgLock + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will check state of Pad Config Tx Lock for pads within one group + + @param[in] Group GPIO group + @param[in] DwNum PadCfgLockTx register number for current group. + For group which has less then 32 pads per group DwNum must be 0. + @param[out] PadCfgLockTxRegVal Value of PadCfgLockTx register + Bit position - PadNumber + Bit value - 0: NotLockedTx, 1: LockedTx + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or DwNum parameter number +**/ +EFI_STATUS +GpioGetPadCfgLockTxForGroupDw ( + IN GPIO_GROUP Group, + IN UINT32 DwNum, + OUT UINT32 *PadCfgLockTxRegVal + ) +{ + EFI_STATUS Status; + + Status = GpioReadWriteReg ( + GpioPadLockOutputRegister, + Group, + DwNum, + 0, + FALSE, + FALSE, + PadCfgLockTxRegVal + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will check state of Pad Config Tx Lock for selected pad + + @param[in] GpioPad GPIO pad + @param[out] PadCfgLock PadCfgLockTx for selected pad + 0: NotLockedTx, 1: LockedTx + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioGetPadCfgLockTx ( + IN GPIO_PAD GpioPad, + OUT UINT32 *PadCfgLockTx + ) +{ + EFI_STATUS Status; + + Status = GpioReadWriteReg ( + GpioPadLockOutputRegister, + 0, + 0, + GpioPad, + FALSE, + TRUE, + PadCfgLockTx + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will clear PadCfgLock for selected pads within one group. + This function should be used only inside SMI. + + @param[in] Group GPIO group + @param[in] DwNum PadCfgLock register number for current group. + For group which has less then 32 pads per group DwNum must be 0. + @param[in] PadsToUnlock Bitmask for pads which are going to be unlocked, + Bit position - PadNumber + Bit value - 0: DoNotUnlock, 1: Unlock + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioUnlockPadCfgForGroupDw ( + IN GPIO_GROUP Group, + IN UINT32 DwNum, + IN UINT32 PadsToUnlock + ) +{ + EFI_STATUS Status; + + Status = GpioLockPadsUsingSbi ( + GpioPadConfigLockRegister, + TRUE, + Group, + DwNum, + PadsToUnlock, + 0, + FALSE + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will clear PadCfgLock for selected pad. + This function should be used only inside SMI. + + @param[in] GpioPad GPIO pad + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioUnlockPadCfg ( + IN GPIO_PAD GpioPad + ) +{ + EFI_STATUS Status; + + Status = GpioLockPadsUsingSbi ( + GpioPadConfigLockRegister, + TRUE, + 0, + 0, + 0, + GpioPad, + TRUE + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will set PadCfgLock for selected pads within one group + + @param[in] Group GPIO group + @param[in] DwNum PadCfgLock register number for current group. + For group which has less then 32 pads per group DwNum must be 0. + @param[in] PadsToLock Bitmask for pads which are going to be locked + Bit position - PadNumber + Bit value - 0: DoNotLock, 1: Lock + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or DwNum parameter number +**/ +EFI_STATUS +GpioLockPadCfgForGroupDw ( + IN GPIO_GROUP Group, + IN UINT32 DwNum, + IN UINT32 PadsToLock + ) +{ + EFI_STATUS Status; + + Status = GpioLockPadsUsingSbi ( + GpioPadConfigLockRegister, + FALSE, + Group, + DwNum, + PadsToLock, + 0, + FALSE + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will set PadCfgLock for selected pad + + @param[in] GpioPad GPIO pad + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioLockPadCfg ( + IN GPIO_PAD GpioPad + ) +{ + EFI_STATUS Status; + + Status = GpioLockPadsUsingSbi ( + GpioPadConfigLockRegister, + FALSE, + 0, + 0, + 0, + GpioPad, + TRUE + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will clear PadCfgLockTx for selected pads within one group. + This function should be used only inside SMI. + + @param[in] Group GPIO group + @param[in] DwNum PadCfgLockTx register number for current group. + For group which has less then 32 pads per group DwNum must be 0. + @param[in] PadsToUnlockTx Bitmask for pads which are going to be unlocked, + Bit position - PadNumber + Bit value - 0: DoNotUnLockTx, 1: LockTx + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioUnlockPadCfgTxForGroupDw ( + IN GPIO_GROUP Group, + IN UINT32 DwNum, + IN UINT32 PadsToUnlockTx + ) +{ + EFI_STATUS Status; + + Status = GpioLockPadsUsingSbi ( + GpioPadLockOutputRegister, + TRUE, + Group, + DwNum, + PadsToUnlockTx, + 0, + FALSE + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will clear PadCfgLockTx for selected pad. + This function should be used only inside SMI. + + @param[in] GpioPad GPIO pad + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioUnlockPadCfgTx ( + IN GPIO_PAD GpioPad + ) +{ + EFI_STATUS Status; + + Status = GpioLockPadsUsingSbi ( + GpioPadLockOutputRegister, + TRUE, + 0, + 0, + 0, + GpioPad, + TRUE + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will set PadCfgLockTx for selected pads within one group + + @param[in] Group GPIO group + @param[in] DwNum PadCfgLock register number for current group. + For group which has less then 32 pads per group DwNum must be 0. + @param[in] PadsToLockTx Bitmask for pads which are going to be locked, + Bit position - PadNumber + Bit value - 0: DoNotLockTx, 1: LockTx + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or DwNum parameter number +**/ +EFI_STATUS +GpioLockPadCfgTxForGroupDw ( + IN GPIO_GROUP Group, + IN UINT32 DwNum, + IN UINT32 PadsToLockTx + ) +{ + EFI_STATUS Status; + + Status = GpioLockPadsUsingSbi ( + GpioPadLockOutputRegister, + FALSE, + Group, + DwNum, + PadsToLockTx, + 0, + FALSE + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will set PadCfgLockTx for selected pad + + @param[in] GpioPad GPIO pad + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioLockPadCfgTx ( + IN GPIO_PAD GpioPad + ) +{ + EFI_STATUS Status; + + Status = GpioLockPadsUsingSbi ( + GpioPadLockOutputRegister, + FALSE, + 0, + 0, + 0, + GpioPad, + TRUE + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + + +/** + This procedure will get Group to GPE mapping. + + @param[out] GroupToGpeDw0 GPIO group to be mapped to GPE_DW0 + @param[out] GroupToGpeDw1 GPIO group to be mapped to GPE_DW1 + @param[out] GroupToGpeDw2 GPIO group to be mapped to GPE_DW2 + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioGetGroupToGpeDwX ( + IN GPIO_GROUP *GroupToGpeDw0, + IN GPIO_GROUP *GroupToGpeDw1, + IN GPIO_GROUP *GroupToGpeDw2 + ) +{ + UINT32 Data32; + UINT32 PchPwrmBase; + GPIO_GROUP GpioGroupOffset; + + GpioGroupOffset = GpioGetLowestGroup (); + + + PchPwrmBaseGet (&PchPwrmBase); + + Data32 = MmioRead32 ((UINTN) (PchPwrmBase + R_PCH_PWRM_GPIO_CFG)); + + *GroupToGpeDw0 = ((Data32 & B_PCH_PWRM_GPIO_CFG_GPE0_DW0) >> N_PCH_PWRM_GPIO_CFG_GPE0_DW0) + GpioGroupOffset; + *GroupToGpeDw1 = ((Data32 & B_PCH_PWRM_GPIO_CFG_GPE0_DW1) >> N_PCH_PWRM_GPIO_CFG_GPE0_DW1) + GpioGroupOffset; + *GroupToGpeDw2 = ((Data32 & B_PCH_PWRM_GPIO_CFG_GPE0_DW2) >> N_PCH_PWRM_GPIO_CFG_GPE0_DW2) + GpioGroupOffset; + + return EFI_SUCCESS; +} + +/** + This procedure will set Group to GPE mapping. + + @param[in] GroupToGpeDw0 GPIO group to be mapped to GPE_DW0 + @param[in] GroupToGpeDw1 GPIO group to be mapped to GPE_DW1 + @param[in] GroupToGpeDw2 GPIO group to be mapped to GPE_DW2 + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioSetGroupToGpeDwX ( + IN GPIO_GROUP GroupToGpeDw0, + IN GPIO_GROUP GroupToGpeDw1, + IN GPIO_GROUP GroupToGpeDw2 + ) +{ + UINT32 Data32Or; + UINT32 Data32And; + UINT32 PchPwrmBase; + GPIO_GROUP GpioGroupLowest; + GPIO_GROUP GpioGroupHighest; + + GpioGroupLowest = GpioGetLowestGroup (); + GpioGroupHighest = GpioGetHighestGroup (); + + // + // Check if group argument exceeds GPIO group range + // + if (((UINT32)GroupToGpeDw0 < GpioGroupLowest) || ((UINT32)GroupToGpeDw0 > GpioGroupHighest) || + ((UINT32)GroupToGpeDw1 < GpioGroupLowest) || ((UINT32)GroupToGpeDw1 > GpioGroupHighest) || + ((UINT32)GroupToGpeDw2 < GpioGroupLowest) || ((UINT32)GroupToGpeDw2 > GpioGroupHighest)) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group argument exceeds GPIO group range\n")); + return EFI_INVALID_PARAMETER; + } + + // + // Check if each group number is unique + // + if ((GroupToGpeDw0 == GroupToGpeDw1) || + (GroupToGpeDw0 == GroupToGpeDw2) || + (GroupToGpeDw1 == GroupToGpeDw2)) { + return EFI_INVALID_PARAMETER; + } + + // + // Values in GPE0_DWx registers are 0 based (GPP_A = 0h) + // + GroupToGpeDw0 = GpioGetGroupIndexFromGroup(GroupToGpeDw0); + GroupToGpeDw1 = GpioGetGroupIndexFromGroup(GroupToGpeDw1); + GroupToGpeDw2 = GpioGetGroupIndexFromGroup(GroupToGpeDw2); + + PchPwrmBaseGet (&PchPwrmBase); + + // + // Program GPIO_CFG (PMRMBASE + 120h) register + // + Data32And = (UINT32) ~(B_PCH_PWRM_GPIO_CFG_GPE0_DW2 | B_PCH_PWRM_GPIO_CFG_GPE0_DW1 | B_PCH_PWRM_GPIO_CFG_GPE0_DW0); + Data32Or = (UINT32)((GroupToGpeDw2 << N_PCH_PWRM_GPIO_CFG_GPE0_DW2) | + (GroupToGpeDw1 << N_PCH_PWRM_GPIO_CFG_GPE0_DW1) | + (GroupToGpeDw0 << N_PCH_PWRM_GPIO_CFG_GPE0_DW0)); + + MmioAndThenOr32 ( + (UINTN) (PchPwrmBase + R_PCH_PWRM_GPIO_CFG), + Data32And, + Data32Or + ); + + Data32And = (UINT32) ~(B_PCH_PCR_GPIO_MISCCFG_GPE0_DW2 | B_PCH_PCR_GPIO_MISCCFG_GPE0_DW1 | B_PCH_PCR_GPIO_MISCCFG_GPE0_DW0); + Data32Or = (UINT32)((GroupToGpeDw2 << N_PCH_PCR_GPIO_MISCCFG_GPE0_DW2) | + (GroupToGpeDw1 << N_PCH_PCR_GPIO_MISCCFG_GPE0_DW1) | + (GroupToGpeDw0 << N_PCH_PCR_GPIO_MISCCFG_GPE0_DW0)); + // + // Program MISCCFG register for Community 0 + // + MmioAndThenOr32 ( + (UINTN)PCH_PCR_ADDRESS (PID_GPIOCOM0, R_PCH_PCR_GPIO_MISCCFG), + Data32And, + Data32Or + ); + + // + // Program MISCCFG register for Community 1 + // + MmioAndThenOr32 ( + (UINTN)PCH_PCR_ADDRESS (PID_GPIOCOM1, R_PCH_PCR_GPIO_MISCCFG), + Data32And, + Data32Or + ); + + // + // Program MISCCFG register for Community 2 + // + MmioAndThenOr32 ( + (UINTN)PCH_PCR_ADDRESS (PID_GPIOCOM2, R_PCH_PCR_GPIO_MISCCFG), + Data32And, + Data32Or + ); + + // + // Program MISCCFG register for Community 3 + // + MmioAndThenOr32 ( + (UINTN)PCH_PCR_ADDRESS (PID_GPIOCOM3, R_PCH_PCR_GPIO_MISCCFG), + Data32And, + Data32Or + ); + + return EFI_SUCCESS; +} + +/** + This procedure will get GPE number for provided GpioPad. + PCH allows to configure mapping between GPIO groups and related GPE (GpioSetGroupToGpeDwX()) + what results in the fact that certain Pad can cause different General Purpose Event. Only three + GPIO groups can be mapped to cause unique GPE (1-tier), all others groups will be under one common + event (GPE_111 for 2-tier). + + 1-tier: + Returned GpeNumber is in range <0,95>. GpioGetGpeNumber() can be used + to determine what _LXX ACPI method would be called on event on selected GPIO pad + + 2-tier: + Returned GpeNumber is 0x6F (111). All GPIO pads which are not mapped to 1-tier GPE + will be under one master GPE_111 which is linked to _L6F ACPI method. If it is needed to determine + what Pad from 2-tier has caused the event, _L6F method should check GPI_GPE_STS and GPI_GPE_EN + registers for all GPIO groups not mapped to 1-tier GPE. + + @param[in] GpioPad GPIO pad + @param[out] GpeNumber GPE number + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioGetGpeNumber ( + IN GPIO_PAD GpioPad, + OUT UINT32 *GpeNumber + ) +{ + GPIO_GROUP GroupToGpeDw0; + GPIO_GROUP GroupToGpeDw1; + GPIO_GROUP GroupToGpeDw2; + GPIO_GROUP GpioGroupLowest; + GPIO_GROUP GpioGroupHighest; + UINT32 GroupIndex; + GPIO_GROUP Group; + UINT32 PadNumber; + GPIO_GROUP_INFO *GpioGroupInfo; + UINTN GpioGroupInfoLength; + + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + Group = GpioGetGroupFromGpioPad (GpioPad); + GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad); + PadNumber = GpioGetPadNumberFromGpioPad (GpioPad); + +DEBUG_CODE_BEGIN(); + if (!GpioIsCorrectPadForThisChipset (GpioPad)) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Incorrect GpioPad define used on this chipset (Group=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } +DEBUG_CODE_END(); + + GpioGroupLowest = GpioGetLowestGroup (); + GpioGroupHighest = GpioGetHighestGroup (); + + // + // Check if group argument exceeds GPIO group range + // + if ((GroupIndex < GpioGetGroupIndexFromGroup (GpioGroupLowest)) || (GroupIndex > GpioGetGroupIndexFromGroup (GpioGroupHighest))) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group argument (%d) exceeds GPIO group range\n", GroupIndex)); + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + // + // Check if legal pad number + // + if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pin number (%d) exceeds possible range for this group\n", PadNumber)); + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + // + // Get GPIO groups mapping to 1-tier GPE + // + GpioGetGroupToGpeDwX (&GroupToGpeDw0,&GroupToGpeDw1,&GroupToGpeDw2); + + if (Group == GroupToGpeDw0) { + *GpeNumber = PadNumber; + } else if (Group== GroupToGpeDw1) { + *GpeNumber = PadNumber + 32; + } else if (Group == GroupToGpeDw2) { + *GpeNumber = PadNumber + 64; + } else { + // + // If Group number doesn't match any of above then + // it means than certain pad is routed to 2-tier GPE + // which all are under GPE_111 (0x6F) + // + *GpeNumber = PCH_GPIO_2_TIER_MASTER_GPE_NUMBER; + } + + return EFI_SUCCESS; +} + +/** + This procedure is used to clear SMI STS for a specified Pad + + @param[in] GpioPad GPIO pad + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioClearGpiSmiSts ( + IN GPIO_PAD GpioPad + ) +{ + UINT32 GroupIndex; + UINT32 PadNumber; + GPIO_GROUP_INFO *GpioGroupInfo; + UINTN GpioGroupInfoLength; + + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad); + PadNumber = GpioGetPadNumberFromGpioPad (GpioPad); + +DEBUG_CODE_BEGIN(); + if (!GpioIsCorrectPadForThisChipset (GpioPad)) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Incorrect GpioPad define used on this chipset (Group=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } +DEBUG_CODE_END(); + + // + // Check if group argument exceeds GPIO GROUP INFO array + // + if ((UINTN)GroupIndex >= GpioGroupInfoLength) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group argument (%d) exceeds GPIO group range\n", GroupIndex)); + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + // + // Check if legal pad number + // + if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pin number (%d) exceeds possible range for this group\n", PadNumber)); + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + // + // Check if group has GPI SMI register + // + if (GpioGroupInfo[GroupIndex].SmiStsOffset == NO_REGISTER_FOR_PROPERTY) { + return EFI_INVALID_PARAMETER; + } + // + // Clear all GPI SMI Status bits by writing '1' + // + MmioWrite32 ( + PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, GpioGroupInfo[GroupIndex].SmiStsOffset), + (UINT32)(BIT0 << PadNumber) + ); + + return EFI_SUCCESS; +} + +/** + This procedure is used by PchSmiDispatcher and will clear + all GPI SMI Status bits + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioClearAllGpiSmiSts ( + VOID + ) +{ + UINT32 GroupIndex; + GPIO_GROUP_INFO *GpioGroupInfo; + UINTN GpioGroupInfoLength; + + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + for (GroupIndex = 0; GroupIndex < GpioGroupInfoLength; GroupIndex++) { + // + // Check if group has GPI SMI register + // + if (GpioGroupInfo[GroupIndex].SmiStsOffset == NO_REGISTER_FOR_PROPERTY) { + continue; + } + // + // Clear all GPI SMI Status bits by writing '1' + // + MmioWrite32 ( + PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, GpioGroupInfo[GroupIndex].SmiStsOffset), + (UINT32)0xFFFFFFFF + ); + } + return EFI_SUCCESS; +} + +/** + This procedure is used to disable all GPI SMI + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioDisableAllGpiSmi ( + VOID + ) +{ + GPIO_GROUP_INFO *GpioGroupInfo; + UINT32 GroupIndex; + UINTN GpioGroupInfoLength; + + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + for (GroupIndex = 0; GroupIndex < GpioGroupInfoLength; GroupIndex++) { + // + // Check if group has GPI SMI register + // + if (GpioGroupInfo[GroupIndex].SmiEnOffset == NO_REGISTER_FOR_PROPERTY) { + continue; + } + + // + // Disable all GPI SMI + // + MmioWrite32 ( + PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, GpioGroupInfo[GroupIndex].SmiEnOffset), + (UINT32)0x0 + ); + } + return EFI_SUCCESS; +} + +/** + This procedure is used to register GPI SMI dispatch function. + + @param[in] GpioPad GPIO pad + @param[out] GpiNum GPI number + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioGetGpiSmiNum ( + IN GPIO_PAD GpioPad, + OUT UINTN *GpiNum + ) +{ + UINT32 GroupIndex; + UINT32 Index; + UINT32 PadNumber; + GPIO_GROUP_INFO *GpioGroupInfo; + UINTN GpioGroupInfoLength; + + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad); + PadNumber = GpioGetPadNumberFromGpioPad (GpioPad); + +DEBUG_CODE_BEGIN(); + if (!GpioIsCorrectPadForThisChipset (GpioPad)) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Incorrect GpioPad define used on this chipset (Group=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } +DEBUG_CODE_END(); + + // + // Check if group argument exceeds GPIO GROUP INFO array + // + if ((UINTN)GroupIndex >= GpioGroupInfoLength) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group argument (%d) exceeds GPIO group range\n", GroupIndex)); + return EFI_INVALID_PARAMETER; + } + + // + // Check if legal pad number + // + if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup){ + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pin number (%d) exceeds possible range for this group\n", PadNumber)); + return EFI_INVALID_PARAMETER; + } + + *GpiNum = 0; + + for (Index = 0; Index < (UINT32)GroupIndex; Index++) { + *GpiNum += (UINTN)(GpioGroupInfo[Index].PadPerGroup); + } + *GpiNum += (UINTN)PadNumber; + + return EFI_SUCCESS; +} + +/** + This procedure is used to check GPIO inputs belongs to 2 tier or 1 tier architecture + + @param[in] GpioPad GPIO pad + + @retval Data 0 means 1-tier, 1 means 2-tier +**/ +BOOLEAN +GpioCheckFor2Tier ( + IN GPIO_PAD GpioPad + ) +{ + UINT32 Data32; + + GpioGetGpeNumber (GpioPad, &Data32); + if(Data32 == PCH_GPIO_2_TIER_MASTER_GPE_NUMBER) { + return TRUE; + } + + return FALSE; +} + +/** + This procedure is used to clear GPE STS for a specified GpioPad + + @param[in] GpioPad GPIO pad + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioClearGpiGpeSts ( + IN GPIO_PAD GpioPad + ) +{ + UINT32 GroupIndex; + UINT32 PadNumber; + GPIO_GROUP_INFO *GpioGroupInfo; + UINTN GpioGroupInfoLength; + + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad); + PadNumber = GpioGetPadNumberFromGpioPad (GpioPad); + + // + // Check if group argument exceeds GPIO GROUP INFO array + // + if ((UINTN)GroupIndex >= GpioGroupInfoLength) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group argument (%d) exceeds GPIO group range\n", GroupIndex)); + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + // + // Check if legal pad number + // + if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pin number (%d) exceeds possible range for this group\n", PadNumber)); + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + // + // Check if group has GPI GPE register + // + if (GpioGroupInfo[GroupIndex].GpiGpeStsOffset == NO_REGISTER_FOR_PROPERTY) { + return EFI_INVALID_PARAMETER; + } + + // Check for 2-tier + if(!(GpioCheckFor2Tier (GpioPad))) { + return EFI_INVALID_PARAMETER; + } + + // + // Clear all GPI SMI Status bits by writing '1' + // + MmioWrite32 ( + PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, GpioGroupInfo[GroupIndex].GpiGpeStsOffset), + (UINT32)(BIT0 << PadNumber) + ); + + return EFI_SUCCESS; +} + +/** + This procedure is used to read GPE STS for a specified Pad + + @param[in] GpioPad GPIO pad + @param[out] Data GPE STS data + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioGetGpiGpeSts ( + IN GPIO_PAD GpioPad, + OUT UINT32* Data + ) +{ + UINT32 Data32; + UINT32 Mask; + UINT32 GroupIndex; + UINT32 PadNumber; + GPIO_GROUP_INFO *GpioGroupInfo; + UINTN GpioGroupInfoLength; + + *Data = 0xFFFFFFFF; + + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad); + PadNumber = GpioGetPadNumberFromGpioPad (GpioPad); + + // + // Check if group argument exceeds GPIO GROUP INFO array + // + if ((UINTN)GroupIndex >= GpioGroupInfoLength) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group argument (%d) exceeds GPIO group range\n", GroupIndex)); + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + // + // Check if legal pad number + // + if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pin number (%d) exceeds possible range for this group\n", PadNumber)); + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + // + // Check if group has GPI GPE register + // + if (GpioGroupInfo[GroupIndex].GpiGpeStsOffset == NO_REGISTER_FOR_PROPERTY) { + return EFI_INVALID_PARAMETER; + } + + // Check for 2-tier + if(!(GpioCheckFor2Tier (GpioPad))) { + return EFI_INVALID_PARAMETER; + } + + // + // Read GPI GPE Status bits + // + Data32 = MmioRead32( + PCH_PCR_ADDRESS(GpioGroupInfo[GroupIndex].Community, GpioGroupInfo[GroupIndex].GpiGpeStsOffset) + ); + + Mask = (UINT32)(BIT0 << PadNumber); + Data32 = (Data32 & Mask) >> PadNumber; + *Data = Data32; + + return EFI_SUCCESS; +} + +/** + This procedure will set GPIO Input Rout SCI + + @param[in] GpioPad GPIO pad + @param[in] Value Value for GPIRoutSCI + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioSetGpiRoutSci ( + IN GPIO_PAD GpioPad, + IN UINT32 Value + ) +{ + EFI_STATUS Status; + + Value <<= N_PCH_GPIO_RX_SCI_ROUTE; + Status = GpioReadWritePadCfgReg ( + GpioPad, + 0, + B_PCH_GPIO_RX_SCI_ROUTE, + TRUE, + &Value + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will set GPIO Input Rout SMI + + @param[in] GpioPad GPIO pad + @param[in] Value Value for GPIRoutSMI + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioSetGpiRoutSmi ( + IN GPIO_PAD GpioPad, + IN UINT32 Value + ) +{ + EFI_STATUS Status; + + Value <<= N_PCH_GPIO_RX_SMI_ROUTE; + Status = GpioReadWritePadCfgReg ( + GpioPad, + 0, + B_PCH_GPIO_RX_SMI_ROUTE, + TRUE, + &Value + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will set GPI SMI Enable setting for selected pad + + @param[in] GpioPad GPIO pad + @param[in] PadGpiSmiEn GPI SMI Enable setting for selected pad + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioSetGpiSmiPadEn ( + IN GPIO_PAD GpioPad, + IN UINT32 PadGpiSmiEn + ) +{ + GPIO_GROUP Group; + GPIO_GROUP GpioGroupOffset; + UINT32 NumberOfGroups; + EFI_STATUS Status; + + GpioGroupOffset = GpioGetLowestGroup (); + NumberOfGroups = GpioGetNumberOfGroups (); + + Group = GpioGetGroupFromGpioPad (GpioPad); + + // + // Check if group argument exceeds GPIO group range + // + if ((Group < GpioGroupOffset) || (Group >= NumberOfGroups + GpioGroupOffset)) { + return EFI_INVALID_PARAMETER; + } + + Status = GpioReadWriteReg ( + GpioSmiEnableRegister, + Group, + 0, + GpioPad, + TRUE, + TRUE, + &PadGpiSmiEn + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + This procedure will set GPI General Purpose Event Enable setting for selected pad + + @param[in] GpioPad GPIO pad + @param[in] PadGpiGpeEn GPI General Purpose Event Enable setting for selected pad + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioSetGpiGpePadEn ( + IN GPIO_PAD GpioPad, + IN UINT32 PadGpiGpeEn + ) +{ + GPIO_GROUP Group; + GPIO_GROUP GpioGroupOffset; + UINT32 NumberOfGroups; + EFI_STATUS Status; + + GpioGroupOffset = GpioGetLowestGroup (); + NumberOfGroups = GpioGetNumberOfGroups (); + + Group = GpioGetGroupFromGpioPad (GpioPad); + + // + // Check if group argument exceeds GPIO group range + // + if ((Group < GpioGroupOffset) || (Group >= NumberOfGroups + GpioGroupOffset)) { + return EFI_INVALID_PARAMETER; + } + + + Status = GpioReadWriteReg ( + GpioGpeEnableRegister, + Group, + 0, + GpioPad, + TRUE, + TRUE, + &PadGpiGpeEn + ); + ASSERT_EFI_ERROR (Status); + return Status; +} + +/** + Check if given GPIO Pad is locked + + @param[in] GroupIndex GPIO group index + @param[in] PadNumber GPIO pad number + + @retval TRUE Pad is locked + @retval FALSE Pad is not locked +**/ +BOOLEAN +GpioIsPadLocked ( + IN UINT32 GroupIndex, + IN GPIO_PAD PadNumber + ) +{ + UINT32 RegVal; + GPIO_GROUP_INFO *GpioGroupInfo; + UINTN GpioGroupInfoLength; + + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + // Read PADCFGLOCK register + // + RegVal = MmioRead32 ((UINTN)PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, GpioGroupInfo[GroupIndex].PadCfgLockOffset)); + + return (((RegVal >> PadNumber) & 0x1) == 1); +} + +/** + Locks multiple GPIO pads using GPIO_INIT_CONFIG array. + Only locking is applied and no other GPIO pad configuration is changed. + + @param[in] NumberOfItems Number of GPIO pads to be locked + @param[in] GpioInitTableAddress GPIO initialization table + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number + @retval EFI_UNSUPPORTED Incorrect GPIO pad definition +**/ +static +EFI_STATUS +GpioLockPads ( + IN UINT32 NumberOfItems, + IN GPIO_INIT_CONFIG *GpioInitTableAddress + ) +{ + UINT32 Index; + UINT32 PadsToLock[V_PCH_GPIO_GROUP_MAX]; + UINT32 PadsToLockTx[V_PCH_GPIO_GROUP_MAX]; + GPIO_GROUP_INFO *GpioGroupInfo; + UINTN GpioGroupInfoLength; + GPIO_GROUP GpioGroupOffset; + UINT32 NumberOfGroups; + GPIO_PAD_OWN PadOwnVal; + GPIO_INIT_CONFIG *GpioData; + GPIO_GROUP Group; + UINT32 GroupIndex; + UINT32 PadNumber; + PCH_SERIES PchSeries; + + PchSeries = GetPchSeries (); + PadOwnVal = GpioPadOwnHost; + + ZeroMem (PadsToLock, sizeof (PadsToLock)); + ZeroMem (PadsToLockTx, sizeof (PadsToLockTx)); + + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + GpioGroupOffset = GpioGetLowestGroup (); + NumberOfGroups = GpioGetNumberOfGroups (); + + for (Index = 0; Index < NumberOfItems; Index ++) { + + GpioData = &GpioInitTableAddress[Index]; + + Group = GpioGetGroupFromGpioPad (GpioData->GpioPad); + GroupIndex = GpioGetGroupIndexFromGpioPad (GpioData->GpioPad); + PadNumber = GpioGetPadNumberFromGpioPad (GpioData->GpioPad); + + // + // Checking GroupIndex to avoid Buffer Overflows or Array Out of Index + // + if (GroupIndex >= V_PCH_GPIO_GROUP_MAX) { + ASSERT (FALSE); + continue; + } + + // + // Check if group argument exceeds GPIO group range + // + if ((Group < GpioGroupOffset) || (Group >= NumberOfGroups + GpioGroupOffset)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check if legal pin number + // + if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup){ + return EFI_INVALID_PARAMETER; + } + + // + // Check if selected GPIO Pad is not owned by CSME/ISH + // + GpioGetPadOwnership (GpioData->GpioPad, &PadOwnVal); + + if (PadOwnVal != GpioPadOwnHost) { + continue; + } + + // + // Update information on Pad Configuration Lock + // + PadsToLock[GroupIndex] |= ((GpioData->GpioConfig.LockConfig >> 0x1) & 0x1) << PadNumber; + + // + // Update information on Pad Configuration Lock Tx + // + PadsToLockTx[GroupIndex] |= ((GpioData->GpioConfig.LockConfig >> 0x2) & 0x1) << PadNumber; + } + + for (Index = 0; Index < NumberOfGroups; Index++) { + // + // Write Pad Configuration Lock + // + if (PadsToLock[Index] != 0) { + GpioLockPadCfgForGroupDw (Index + GpioGroupOffset, 0, PadsToLock[Index]); + } + + // + // Write Pad Configuration Lock Tx + // + if (PadsToLockTx[Index] != 0) { + GpioLockPadCfgTxForGroupDw (Index + GpioGroupOffset, 0, PadsToLockTx[Index]); + } + } + + return EFI_SUCCESS; +} + +/** + Locks GPIO pads according to GPIO_INIT_CONFIG array from + gPlatformGpioConfigGuid HOB. Only locking is applied and no other GPIO pad + configuration is changed. + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_NOT_FOUND gPlatformGpioConfigGuid not found +**/ +EFI_STATUS +GpioLockGpios ( + VOID + ) +{ + EFI_HOB_GUID_TYPE *GpioConfigHob; + GPIO_INIT_CONFIG *GpioConfig; + UINT16 GpioConfigSize; + + GpioConfigHob = GetFirstGuidHob (&gPlatformGpioConfigGuid); + if (GpioConfigHob == NULL) { + return EFI_NOT_FOUND; + } + ASSERT (GET_GUID_HOB_DATA_SIZE (GpioConfigHob) % sizeof (GpioConfig[0]) == 0); + GpioConfigSize = GET_GUID_HOB_DATA_SIZE (GpioConfigHob) / sizeof (GpioConfig[0]); + GpioConfig = GET_GUID_HOB_DATA (GpioConfigHob); + GpioLockPads (GpioConfigSize, GpioConfig); + + return EFI_SUCCESS; +} + +/** + Unlocks all PCH GPIO pads + + @retval None +**/ +VOID +GpioUnlockAllGpios ( + VOID + ) +{ + GPIO_GROUP GpioGroupOffset; + UINT32 NumberOfGroups; + UINT32 Index; + + GpioGroupOffset = GpioGetLowestGroup (); + NumberOfGroups = GpioGetNumberOfGroups (); + + for (Index = 0; Index < NumberOfGroups; Index++) { + // + // Reset Pad Configuration Lock + // + GpioUnlockPadCfgForGroupDw (Index + GpioGroupOffset, 0, 0xFFFFFFFF); + + // + // Reset Pad Configuration Lock Tx + // + GpioUnlockPadCfgTxForGroupDw (Index + GpioGroupOffset, 0, 0xFFFFFFFF); + } +} + |