diff options
Diffstat (limited to 'Silicon/Intel/LewisburgPkg/Library/PeiDxeSmmGpioLib/GpioInit.c')
-rw-r--r-- | Silicon/Intel/LewisburgPkg/Library/PeiDxeSmmGpioLib/GpioInit.c | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/Silicon/Intel/LewisburgPkg/Library/PeiDxeSmmGpioLib/GpioInit.c b/Silicon/Intel/LewisburgPkg/Library/PeiDxeSmmGpioLib/GpioInit.c new file mode 100644 index 0000000000..422ecfcee1 --- /dev/null +++ b/Silicon/Intel/LewisburgPkg/Library/PeiDxeSmmGpioLib/GpioInit.c @@ -0,0 +1,409 @@ +/** @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" + + +/** + This procedure will handle requirement on SATA DEVSLPx pins. + + @param[in] GpioPad GPIO pad + @param[in] PadMode GPIO PadMode value + @param[in out] Dw0Reg Value for PADCFG_DW0 register + @param[in out] Dw0RegMask Mask of bits which will change in PADCFG_DWO register + + @retval None + +**/ +static +VOID +GpioHandleSataDevSlpPad ( + IN GPIO_PAD GpioPad, + IN UINT32 PadMode, + IN OUT UINT32 *Dw0Reg, + IN OUT UINT32 *Dw0RegMask + ) +{ + // + // For SATA DEVSLPx pins if used in native 1 mode then ensure that PadRstCfg + // is set to "00" - Powergood + // + if (GpioIsPadASataDevSlpPin (GpioPad, PadMode)) { + // + // Set PadRstCfg to Powergood + // + *Dw0RegMask |= B_PCH_GPIO_RST_CONF; + *Dw0Reg |= ((GpioResetPwrGood >> 1) << N_PCH_GPIO_RST_CONF); + } +} + +/** + This SKL PCH specific procedure will initialize multiple SKL PCH GPIO pins + + @param[in] NumberofItem Number of GPIO pads to be updated + @param[in] GpioInitTableAddress GPIO initialization table + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +static +EFI_STATUS +GpioConfigureSklPch ( + IN UINT32 NumberOfItems, + IN GPIO_INIT_CONFIG *GpioInitTableAddress + ) +{ + UINT32 Index; + UINT32 Dw0Reg; + UINT32 Dw0RegMask; + UINT32 Dw1Reg; + UINT32 Dw1RegMask; + UINT32 PadCfgReg; + UINT32 HostSoftOwnReg[V_PCH_GPIO_GROUP_MAX]; + UINT32 HostSoftOwnRegMask[V_PCH_GPIO_GROUP_MAX]; + UINT32 GpiGpeEnReg[V_PCH_GPIO_GROUP_MAX]; + UINT32 GpiGpeEnRegMask[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 (HostSoftOwnReg, sizeof (HostSoftOwnReg)); + ZeroMem (HostSoftOwnRegMask, sizeof (HostSoftOwnRegMask)); + ZeroMem (GpiGpeEnReg, sizeof (GpiGpeEnReg)); + ZeroMem (GpiGpeEnRegMask, sizeof (GpiGpeEnRegMask)); + + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + GpioGroupOffset = GpioGetLowestGroup (); + NumberOfGroups = GpioGetNumberOfGroups (); + + for (Index = 0; Index < NumberOfItems; Index ++) { + + Dw0RegMask = 0; + Dw0Reg = 0; + Dw1RegMask = 0; + Dw1Reg = 0; + + GpioData = &GpioInitTableAddress[Index]; + + Group = GpioGetGroupFromGpioPad (GpioData->GpioPad); + GroupIndex = GpioGetGroupIndexFromGpioPad (GpioData->GpioPad); + PadNumber = GpioGetPadNumberFromGpioPad (GpioData->GpioPad); + + if (GroupIndex >= V_PCH_GPIO_GROUP_MAX) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Invalid Group Index (GroupIndex=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + ASSERT (FALSE); + continue; + } + +DEBUG_CODE_BEGIN(); + if (!(((PchSeries == PchH) && (GPIO_GET_CHIPSET_ID(GpioData->GpioPad) == GPIO_SKL_H_CHIPSET_ID)) || + ((PchSeries == PchLp) && (GPIO_GET_CHIPSET_ID(GpioData->GpioPad) == GPIO_SKL_LP_CHIPSET_ID)))) { + 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 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; + } + + if (GpioIsPadLocked (GroupIndex, PadNumber)) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pad is locked (Group=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + continue; + } + + if (DebugCodeEnabled ()) { + + // + // Check if selected GPIO Pad is not owned by CSME/ISH + // + GpioGetPadOwnership (GpioData->GpioPad, &PadOwnVal); + + if (PadOwnVal != GpioPadOwnHost) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Accessing pad not owned by host (Group=%d, Pad=%d)!\n", GroupIndex, PadNumber)); + DEBUG ((DEBUG_ERROR, "** Please make sure the GPIO usage in sync between CSME and BIOS configuration. \n")); + DEBUG ((DEBUG_ERROR, "** All the GPIO occupied by CSME should not do any configuration by BIOS.\n")); + continue; + } + + } + + // + // Configure Reset Type (PadRstCfg) + // + Dw0RegMask |= ((((GpioData->GpioConfig.PowerConfig & GPIO_CONF_RESET_MASK) >> GPIO_CONF_RESET_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_RST_CONF); + Dw0Reg |= (((GpioData->GpioConfig.PowerConfig & GPIO_CONF_RESET_MASK) >> (GPIO_CONF_RESET_BIT_POS + 1)) << N_PCH_GPIO_RST_CONF); + + // + // Configure how interrupt is triggered (RxEvCfg) + // + Dw0RegMask |= ((((GpioData->GpioConfig.InterruptConfig & GPIO_CONF_INT_TRIG_MASK) >> GPIO_CONF_INT_TRIG_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_RX_LVL_EDG); + Dw0Reg |= (((GpioData->GpioConfig.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->GpioConfig.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->GpioConfig.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->GpioConfig.Direction & GPIO_CONF_DIR_MASK) >> GPIO_CONF_DIR_BIT_POS) == GpioHardwareDefault) ? 0x0 : (B_PCH_GPIO_RXDIS | B_PCH_GPIO_TXDIS)); + Dw0Reg |= (((GpioData->GpioConfig.Direction & GPIO_CONF_DIR_MASK) >> (GPIO_CONF_DIR_BIT_POS + 1)) << N_PCH_GPIO_TXDIS); + + // + // Configure GPIO input inversion (RXINV) + // + Dw0RegMask |= ((((GpioData->GpioConfig.Direction & GPIO_CONF_INV_MASK) >> GPIO_CONF_INV_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_RXINV); + Dw0Reg |= (((GpioData->GpioConfig.Direction & GPIO_CONF_INV_MASK) >> (GPIO_CONF_INV_BIT_POS + 1)) << N_PCH_GPIO_RXINV); + + // + // Configure GPIO output state (GPIOTxState) + // + Dw0RegMask |= ((((GpioData->GpioConfig.OutputState & GPIO_CONF_OUTPUT_MASK) >> GPIO_CONF_OUTPUT_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_TX_STATE); + Dw0Reg |= (((GpioData->GpioConfig.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->GpioConfig.OtherSettings & GPIO_CONF_RXRAW_MASK) >> GPIO_CONF_RXRAW_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_RX_RAW1); + Dw0Reg |= (((GpioData->GpioConfig.OtherSettings & GPIO_CONF_RXRAW_MASK) >> (GPIO_CONF_RXRAW_BIT_POS + 1)) << N_PCH_GPIO_RX_RAW1); + + // + // Configure GPIO Pad Mode (PMode) + // + if (((GpioData->GpioPad == GPIO_SKL_H_GPP_B2) || + (GpioData->GpioPad == GPIO_SKL_H_GPD7) || + (GpioData->GpioPad == GPIO_SKL_H_GPD9)) && + (GpioData->GpioConfig.PadMode != GpioPadModeGpio)) { + DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group/Index: %d/%d, Pad: %d cannot be set as NATIVE. Force it to GPIO mode!\n", Group, GroupIndex, PadNumber)); + Dw0RegMask |= B_PCH_GPIO_PAD_MODE; + Dw0Reg |= ((GpioPadModeGpio >> (GPIO_CONF_PAD_MODE_BIT_POS + 1)) << N_PCH_GPIO_PAD_MODE); + } else { + Dw0RegMask |= ((((GpioData->GpioConfig.PadMode & GPIO_CONF_PAD_MODE_MASK) >> GPIO_CONF_PAD_MODE_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_PAD_MODE); + Dw0Reg |= (((GpioData->GpioConfig.PadMode & GPIO_CONF_PAD_MODE_MASK) >> (GPIO_CONF_PAD_MODE_BIT_POS + 1)) << N_PCH_GPIO_PAD_MODE); + } + + // + // Configure GPIO termination (Term) + // + Dw1RegMask |= ((((GpioData->GpioConfig.ElectricalConfig & GPIO_CONF_TERM_MASK) >> GPIO_CONF_TERM_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_TERM); + Dw1Reg |= (((GpioData->GpioConfig.ElectricalConfig & GPIO_CONF_TERM_MASK) >> (GPIO_CONF_TERM_BIT_POS + 1)) << N_PCH_GPIO_TERM); + + // + // Configure GPIO pad tolerance (padtol) + // + Dw1RegMask |= ((((GpioData->GpioConfig.ElectricalConfig & GPIO_CONF_PADTOL_MASK) >> GPIO_CONF_PADTOL_BIT_POS) == GpioHardwareDefault) ? 0x0 : B_PCH_GPIO_PADTOL); + Dw1Reg |= (((GpioData->GpioConfig.ElectricalConfig & GPIO_CONF_PADTOL_MASK) >> (GPIO_CONF_PADTOL_BIT_POS + 1)) << N_PCH_GPIO_PADTOL); + + // + // Check for additional requirements on setting PADCFG register + // + GpioHandleSataDevSlpPad (GpioData->GpioPad, GpioData->GpioConfig.PadMode, &Dw0Reg, &Dw0RegMask); + + // + // 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[GroupIndex] |= (GpioData->GpioConfig.HostSoftPadOwn & 0x1) << PadNumber; + HostSoftOwnReg[GroupIndex] |= (GpioData->GpioConfig.HostSoftPadOwn >> 0x1) << PadNumber; + + // + // Update value to be programmed in GPI_GPE_EN register + // + GpiGpeEnRegMask[GroupIndex] |= (GpioData->GpioConfig.InterruptConfig & 0x1) << PadNumber; + GpiGpeEnReg[GroupIndex] |= ((GpioData->GpioConfig.InterruptConfig & GpioIntSci) >> 3) << PadNumber; + } + + for (Index = 0; Index < NumberOfGroups; Index++) { + // + // Write HOSTSW_OWN registers + // + if (GpioGroupInfo[Index].HostOwnOffset != NO_REGISTER_FOR_PROPERTY) { + MmioAndThenOr32 ( + (UINTN)PCH_PCR_ADDRESS (GpioGroupInfo[Index].Community, GpioGroupInfo[Index].HostOwnOffset), + ~(UINT32)HostSoftOwnRegMask[Index], + (UINT32)HostSoftOwnReg[Index] + ); + } + + // + // Write GPI_GPE_EN registers + // + if (GpioGroupInfo[Index].GpiGpeEnOffset != NO_REGISTER_FOR_PROPERTY) { + MmioAndThenOr32 ( + (UINTN)PCH_PCR_ADDRESS (GpioGroupInfo[Index].Community, GpioGroupInfo[Index].GpiGpeEnOffset), + ~(UINT32)GpiGpeEnRegMask[Index], + (UINT32)GpiGpeEnReg[Index] + ); + } + } + + return EFI_SUCCESS; +} + +/** + This procedure will clear all status bits of any GPIO interrupts. + + @param[in] none + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +static +EFI_STATUS +GpioClearAllGpioInterrupts ( + VOID + ) +{ + GPIO_GROUP Group; + GPIO_GROUP_INFO *GpioGroupInfo; + GPIO_GROUP GpioGroupLowest; + GPIO_GROUP GpioGroupHighest; + UINT32 GroupIndex; + UINTN GpioGroupInfoLength; + + GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength); + + GpioGroupLowest = GpioGetLowestGroup (); + GpioGroupHighest = GpioGetHighestGroup (); + + for (Group = GpioGroupLowest; Group <= GpioGroupHighest; Group++) { + GroupIndex = GpioGetGroupIndexFromGroup (Group); + // + // Check if group has GPI IS register + // + if (GpioGroupInfo[Group].GpiIsOffset != NO_REGISTER_FOR_PROPERTY) { + // + // Clear all GPI_IS Status bits by writing '1' + // + MmioWrite32 ( + PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, GpioGroupInfo[GroupIndex].GpiIsOffset), + (UINT32)0xFFFFFFFF + ); + } + + // + // Check if group has GPI_GPE_STS register + // + if (GpioGroupInfo[GroupIndex].GpiGpeStsOffset != NO_REGISTER_FOR_PROPERTY) { + // + // Clear all GPI_GPE_STS Status bits by writing '1' + // + MmioWrite32 ( + PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, GpioGroupInfo[GroupIndex].GpiGpeStsOffset), + (UINT32)0xFFFFFFFF + ); + } + + // + // Check if group has SMI_STS register + // + if (GpioGroupInfo[GroupIndex].SmiStsOffset != NO_REGISTER_FOR_PROPERTY) { + // + // Clear all SMI_STS Status bits by writing '1' + // + MmioWrite32 ( + PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, GpioGroupInfo[GroupIndex].SmiStsOffset), + (UINT32)0xFFFFFFFF + ); + } + + // + // Check if group has NMI_STS register + // + if (GpioGroupInfo[GroupIndex].NmiStsOffset != NO_REGISTER_FOR_PROPERTY) { + // + // Clear all NMI_STS Status bits by writing '1' + // + MmioWrite32 ( + PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, GpioGroupInfo[GroupIndex].NmiStsOffset), + (UINT32)0xFFFFFFFF + ); + } + + } + return EFI_SUCCESS; +} + +/** + This procedure will initialize multiple GPIO pins. Use GPIO_INIT_CONFIG structure. + Structure contains fields that can be used to configure each pad. + Pad not configured using GPIO_INIT_CONFIG will be left with hardware default values. + Separate fields could be set to hardware default if it does not matter, except + GpioPad and PadMode. + Some GpioPads are configured and switched to native mode by RC, those include: + SerialIo pins, ISH pins, ClkReq Pins + + @param[in] NumberofItem Number of GPIO pads to be updated + @param[in] GpioInitTableAddress GPIO initialization table + + @retval EFI_SUCCESS The function completed successfully + @retval EFI_INVALID_PARAMETER Invalid group or pad number +**/ +EFI_STATUS +GpioConfigurePads ( + IN UINT32 NumberOfItems, + IN GPIO_INIT_CONFIG *GpioInitTableAddress + ) +{ + EFI_STATUS Status; + Status = GpioConfigureSklPch (NumberOfItems, GpioInitTableAddress); + GpioClearAllGpioInterrupts (); + return Status; +} |