/** @file
|
This file contains GPIO routines for RC usage
|
|
Copyright (c) 2019 Intel Corporation. All rights reserved. <BR>
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
**/
|
|
#include <Base.h>
|
#include <Uefi/UefiBaseType.h>
|
#include <Library/IoLib.h>
|
#include <Library/DebugLib.h>
|
#include <Library/BaseMemoryLib.h>
|
#include <Library/GpioLib.h>
|
#include <Library/GpioNativeLib.h>
|
#include <Private/Library/GpioPrivateLib.h>
|
#include <Pch/Library/PeiDxeSmmGpioLib/GpioLibrary.h>
|
#include <Private/Library/GpioNameBufferLib.h>
|
#include <Register/PchRegsPcr.h>
|
|
#include "GpioNativePrivateLibInternal.h"
|
|
/**
|
This procedure is used to check if GpioPad is valid for certain chipset
|
|
@param[in] GpioPad GPIO pad
|
|
@retval TRUE This pin is valid on this chipset
|
FALSE Incorrect pin
|
**/
|
BOOLEAN
|
GpioIsCorrectPadForThisChipset (
|
IN GPIO_PAD GpioPad
|
)
|
{
|
return ((GPIO_GET_CHIPSET_ID (GpioPad) == GpioGetThisChipsetId ()) &&
|
(GpioGetGroupIndexFromGpioPad (GpioPad) < GpioGetNumberOfGroups ()));
|
}
|
|
/**
|
This procedure will get value of selected gpio register
|
|
@param[in] Group GPIO group number
|
@param[in] Offset GPIO register offset
|
@param[out] RegVal Value of gpio register
|
|
@retval EFI_SUCCESS The function completed successfully
|
@retval EFI_INVALID_PARAMETER Invalid group or pad number
|
**/
|
EFI_STATUS
|
GpioGetReg (
|
IN GPIO_GROUP Group,
|
IN UINT32 Offset,
|
OUT UINT32 *RegVal
|
)
|
{
|
CONST GPIO_GROUP_INFO *GpioGroupInfo;
|
UINT32 GpioGroupInfoLength;
|
UINT32 GroupIndex;
|
|
GroupIndex = GpioGetGroupIndexFromGroup (Group);
|
GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
|
//
|
// Check if group argument exceeds GPIO GROUP INFO array
|
//
|
if ((UINTN) GroupIndex >= GpioGroupInfoLength) {
|
ASSERT (FALSE);
|
return EFI_INVALID_PARAMETER;
|
}
|
|
*RegVal = MmioRead32 (PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, Offset));
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
This procedure will set value of selected gpio register
|
|
@param[in] Group GPIO group number
|
@param[in] Offset GPIO register offset
|
@param[in] RegVal Value of gpio register
|
|
@retval EFI_SUCCESS The function completed successfully
|
@retval EFI_INVALID_PARAMETER Invalid group or pad number
|
**/
|
EFI_STATUS
|
GpioSetReg (
|
IN GPIO_GROUP Group,
|
IN UINT32 Offset,
|
IN UINT32 RegVal
|
)
|
{
|
CONST GPIO_GROUP_INFO *GpioGroupInfo;
|
UINT32 GpioGroupInfoLength;
|
UINT32 GroupIndex;
|
|
GroupIndex = GpioGetGroupIndexFromGroup (Group);
|
GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
|
//
|
// Check if group argument exceeds GPIO GROUP INFO array
|
//
|
if ((UINTN) GroupIndex >= GpioGroupInfoLength) {
|
ASSERT (FALSE);
|
return EFI_INVALID_PARAMETER;
|
}
|
|
MmioWrite32 (PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, Offset), RegVal);
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
This procedure is used by PchSmiDispatcher and will return information
|
needed to register GPI SMI.
|
|
@param[in] Index GPI SMI number
|
@param[out] GpioPin GPIO pin
|
@param[out] GpiSmiBitOffset GPI SMI bit position within GpiSmi Registers
|
@param[out] GpiHostSwOwnRegAddress Address of HOSTSW_OWN register
|
@param[out] GpiSmiStsRegAddress Address of GPI SMI status register
|
|
@retval EFI_SUCCESS The function completed successfully
|
@retval EFI_INVALID_PARAMETER Invalid group or pad number
|
**/
|
EFI_STATUS
|
GpioGetPadAndSmiRegs (
|
IN UINT32 Index,
|
OUT GPIO_PAD *GpioPin,
|
OUT UINT8 *GpiSmiBitOffset,
|
OUT UINT32 *GpiHostSwOwnRegAddress,
|
OUT UINT32 *GpiSmiStsRegAddress
|
)
|
{
|
UINT32 GroupIndex;
|
UINT32 PadNumber;
|
CONST GPIO_GROUP_INFO *GpioGroupInfo;
|
GPIO_GROUP GpioGroup;
|
UINT32 GpioGroupInfoLength;
|
UINT32 SmiStsRegOffset;
|
UINT32 HostSwOwnRegOffset;
|
GPIO_PAD_OWN PadOwnVal;
|
|
GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
|
|
PadNumber = 0;
|
GroupIndex = 0;
|
for (GroupIndex = 0; GroupIndex < GpioGroupInfoLength; GroupIndex++) {
|
PadNumber = Index;
|
if (PadNumber < GpioGroupInfo[GroupIndex].PadPerGroup) {
|
//
|
// Found group and pad number
|
//
|
break;
|
}
|
Index = Index - GpioGroupInfo[GroupIndex].PadPerGroup;
|
}
|
|
//
|
// Check if legal pad number
|
//
|
if (PadNumber >= GpioGroupInfo[GroupIndex].PadPerGroup){
|
return EFI_INVALID_PARAMETER;
|
}
|
|
//
|
// Check if selected group has GPI SMI Enable and Status registers
|
//
|
if (GpioGroupInfo[GroupIndex].SmiEnOffset == NO_REGISTER_FOR_PROPERTY) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
GpioGroup = GpioGetGroupFromGroupIndex (GroupIndex);
|
*GpioPin = GpioGetGpioPadFromGroupAndPadNumber (GpioGroup, PadNumber);
|
|
DEBUG_CODE_BEGIN ();
|
//
|
// Check if selected GPIO Pad is not owned by CSME/ISH/IE
|
//
|
GpioGetPadOwnership (*GpioPin, &PadOwnVal);
|
if (PadOwnVal != GpioPadOwnHost) {
|
DEBUG ((DEBUG_ERROR, "GPIO ERROR: %a not owned by host!\n", GpioName (*GpioPin)));
|
return EFI_INVALID_PARAMETER;
|
}
|
DEBUG_CODE_END ();
|
|
*GpiSmiBitOffset = (UINT8)(PadNumber % 32);
|
|
HostSwOwnRegOffset = GpioGroupInfo[GroupIndex].HostOwnOffset + (PadNumber / 32) * 0x4;
|
*GpiHostSwOwnRegAddress = PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, HostSwOwnRegOffset);
|
|
SmiStsRegOffset = GpioGroupInfo[GroupIndex].SmiStsOffset + (PadNumber / 32) * 0x4;
|
*GpiSmiStsRegAddress = PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, SmiStsRegOffset);
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
This procedure will set GPIO Driver IRQ number
|
|
@param[in] Irq Irq number
|
|
@retval EFI_SUCCESS The function completed successfully
|
@retval EFI_INVALID_PARAMETER Invalid IRQ number
|
**/
|
EFI_STATUS
|
GpioSetIrq (
|
IN UINT8 Irq
|
)
|
{
|
UINT32 Data32And;
|
UINT32 Data32Or;
|
PCH_SBI_PID *GpioComSbiIds;
|
UINT32 NoOfGpioComs;
|
UINT32 GpioComIndex;
|
|
Data32And = (UINT32)~(B_GPIO_PCR_MISCCFG_IRQ_ROUTE);
|
Data32Or = (UINT32)Irq << N_GPIO_PCR_MISCCFG_IRQ_ROUTE;
|
|
NoOfGpioComs = GpioGetComSbiPortIds (&GpioComSbiIds);
|
|
//
|
// Program MISCCFG register for each community
|
//
|
for (GpioComIndex = 0; GpioComIndex < NoOfGpioComs; GpioComIndex++) {
|
MmioAndThenOr32 (
|
PCH_PCR_ADDRESS (GpioComSbiIds[GpioComIndex], R_GPIO_PCR_MISCCFG),
|
Data32And,
|
Data32Or
|
);
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
This procedure will return Port ID of GPIO Community from GpioPad
|
|
@param[in] GpioPad GpioPad
|
|
@retval GpioCommunityPortId Port ID of GPIO Community
|
**/
|
UINT8
|
GpioGetGpioCommunityPortIdFromGpioPad (
|
IN GPIO_PAD GpioPad
|
)
|
{
|
CONST GPIO_GROUP_INFO *GpioGroupInfo;
|
UINT32 GpioGroupInfoLength;
|
UINT32 GroupIndex;
|
|
GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad);
|
|
GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
|
|
return GpioGroupInfo[GroupIndex].Community;
|
}
|
|
/**
|
This procedure will return PadCfg address from GpioPad
|
|
@param[in] GpioPad GpioPad
|
|
@retval GpioPadCfgAddress PadCfg Address of GpioPad
|
**/
|
UINT32
|
GpioGetGpioPadCfgAddressFromGpioPad (
|
IN GPIO_PAD GpioPad
|
)
|
{
|
UINT32 PadCfgRegAddress;
|
CONST GPIO_GROUP_INFO *GpioGroupInfo;
|
UINT32 GpioGroupInfoLength;
|
UINT32 GroupIndex;
|
UINT32 PadNumber;
|
|
GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad);
|
PadNumber = GpioGetPadNumberFromGpioPad (GpioPad);
|
|
GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
|
|
//
|
// Create Pad Configuration register offset
|
//
|
PadCfgRegAddress = GpioGroupInfo[GroupIndex].PadCfgOffset + S_GPIO_PCR_PADCFG * PadNumber;
|
|
return PadCfgRegAddress;
|
}
|
|
|
/**
|
This procedure will check if GpioPad is owned by host.
|
|
@param[in] GpioPad GPIO pad
|
|
@retval TRUE GPIO pad is owned by host
|
@retval FALSE GPIO pad is not owned by host and should not be used with GPIO lib API
|
**/
|
BOOLEAN
|
GpioIsPadHostOwned (
|
IN GPIO_PAD GpioPad
|
)
|
{
|
GPIO_PAD_OWN PadOwnVal;
|
|
//
|
// 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: %a is not owned by host!\n", GpioName (GpioPad)));
|
return FALSE;
|
}
|
|
return TRUE;
|
}
|
|
/**
|
This procedure will check if GpioPad argument is valid.
|
Function will check below conditions:
|
- GpioPad represents a pad for current PCH
|
- GpioPad belongs to valid GpioGroup
|
- GPIO PadNumber is not greater than number of pads for this group
|
|
@param[in] GpioPad GPIO pad
|
|
@retval TRUE GPIO pad is valid and can be used with GPIO lib API
|
@retval FALSE GPIO pad is invalid and cannot be used with GPIO lib API
|
**/
|
BOOLEAN
|
GpioIsPadValid (
|
IN GPIO_PAD GpioPad
|
)
|
{
|
CONST GPIO_GROUP_INFO *GpioGroupInfo;
|
UINT32 GpioGroupInfoLength;
|
UINT32 PadNumber;
|
|
if (!GpioIsCorrectPadForThisChipset (GpioPad)) {
|
DEBUG ((DEBUG_ERROR, "GPIO ERROR: Incorrect GpioPad (0x%08x) used on this chipset!\n", GpioPad));
|
goto Error;
|
}
|
|
GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
|
|
//
|
// Check if legal pin number
|
//
|
PadNumber = GpioGetPadNumberFromGpioPad (GpioPad);
|
if (PadNumber >= GpioGroupInfo[GpioGetGroupIndexFromGpioPad (GpioPad)].PadPerGroup) {
|
DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pin number (%d) exceeds possible range for this group\n", PadNumber));
|
goto Error;
|
}
|
|
return TRUE;
|
Error:
|
ASSERT (FALSE);
|
return FALSE;
|
}
|
|
/**
|
This procedure will read GPIO Pad Configuration register
|
|
@param[in] GpioPad GPIO pad
|
@param[in] DwReg Choose PADCFG register: 0:DW0, 1:DW1
|
|
@retval PadCfgRegValue PADCFG_DWx value
|
**/
|
UINT32
|
GpioReadPadCfgReg (
|
IN GPIO_PAD GpioPad,
|
IN UINT8 DwReg
|
)
|
{
|
UINT32 PadCfgReg;
|
CONST GPIO_GROUP_INFO *GpioGroupInfo;
|
UINT32 GpioGroupInfoLength;
|
UINT32 GroupIndex;
|
UINT32 PadNumber;
|
|
GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad);
|
PadNumber = GpioGetPadNumberFromGpioPad (GpioPad);
|
|
GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
|
|
//
|
// Create Pad Configuration register offset
|
//
|
PadCfgReg = GpioGroupInfo[GroupIndex].PadCfgOffset + S_GPIO_PCR_PADCFG * PadNumber + 0x4 * DwReg;
|
|
return MmioRead32 (PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, PadCfgReg));
|
}
|
|
/**
|
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] PadCfgAndMask Mask to be AND'ed with PADCFG reg value
|
@param[in] PadCfgOrMask Mask to be OR'ed with PADCFG reg value
|
|
@retval none
|
**/
|
VOID
|
GpioWritePadCfgReg (
|
IN GPIO_PAD GpioPad,
|
IN UINT8 DwReg,
|
IN UINT32 PadCfgAndMask,
|
IN UINT32 PadCfgOrMask
|
)
|
{
|
UINT32 PadCfgReg;
|
CONST GPIO_GROUP_INFO *GpioGroupInfo;
|
UINT32 GpioGroupInfoLength;
|
UINT32 GroupIndex;
|
UINT32 PadNumber;
|
UINT32 PadCfgLock;
|
UINT32 PadCfgLockTx;
|
|
PadCfgLock = 0;
|
PadCfgLockTx = 0;
|
|
//
|
// Check if Pad Configuration (except output state) is to be changed.
|
// If AND and OR masks will indicate that configuration fields (other than output control)
|
// are to be modified it means that there is a need to perform an unlock (if set)
|
//
|
if ((~PadCfgAndMask | PadCfgOrMask) & (UINT32)~B_GPIO_PCR_TX_STATE) {
|
GpioGetPadCfgLock (GpioPad, &PadCfgLock);
|
if (PadCfgLock) {
|
GpioUnlockPadCfg (GpioPad);
|
}
|
}
|
|
//
|
// Check if Pad Output state is to be changed
|
// If AND and OR masks will indicate that output control
|
// is to be modified it means that there is a need to perform an unlock (if set)
|
//
|
if ((~PadCfgAndMask | PadCfgOrMask) & B_GPIO_PCR_TX_STATE) {
|
GpioGetPadCfgLockTx (GpioPad, &PadCfgLockTx);
|
if (PadCfgLockTx) {
|
GpioUnlockPadCfgTx (GpioPad);
|
}
|
}
|
|
GroupIndex = GpioGetGroupIndexFromGpioPad (GpioPad);
|
PadNumber = GpioGetPadNumberFromGpioPad (GpioPad);
|
|
GpioGroupInfo = GpioGetGroupInfoTable (&GpioGroupInfoLength);
|
|
//
|
// Create Pad Configuration register offset
|
//
|
PadCfgReg = GpioGroupInfo[GroupIndex].PadCfgOffset + S_GPIO_PCR_PADCFG * PadNumber + 0x4 * DwReg;
|
|
MmioAndThenOr32 (
|
PCH_PCR_ADDRESS (GpioGroupInfo[GroupIndex].Community, PadCfgReg),
|
PadCfgAndMask,
|
PadCfgOrMask
|
);
|
|
if (PadCfgLock) {
|
GpioLockPadCfg (GpioPad);
|
}
|
if (PadCfgLockTx) {
|
GpioLockPadCfgTx (GpioPad);
|
}
|
}
|
|
/**
|
This procedure will set GPIO mode
|
|
@param[in] GpioPad GPIO pad
|
@param[out] PadModeValue GPIO pad mode value
|
|
@retval EFI_SUCCESS The function completed successfully
|
@retval EFI_INVALID_PARAMETER Invalid group or pad number
|
**/
|
EFI_STATUS
|
GpioSetPadMode (
|
IN GPIO_PAD GpioPad,
|
IN GPIO_PAD_MODE PadModeValue
|
)
|
{
|
UINT32 PadCfgOrMask;
|
|
if (!GpioIsPadValid (GpioPad)) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
if (!GpioIsPadHostOwned (GpioPad)) {
|
return EFI_UNSUPPORTED;
|
}
|
|
if (PadModeValue != (GPIO_PAD_MODE)GpioHardwareDefault) {
|
|
PadCfgOrMask = (((PadModeValue & B_GPIO_PAD_MODE_MASK) >> (N_GPIO_PAD_MODE_BIT_POS + 1)) << N_GPIO_PCR_PAD_MODE);
|
|
GpioWritePadCfgReg (
|
GpioPad,
|
0,
|
(UINT32)~B_GPIO_PCR_PAD_MODE,
|
PadCfgOrMask
|
);
|
}
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
This procedure will get GPIO mode
|
|
@param[in] GpioPad GPIO pad
|
@param[out] PadModeValue GPIO pad mode value
|
|
@retval EFI_SUCCESS The function completed successfully
|
@retval EFI_INVALID_PARAMETER Invalid GpioPad
|
**/
|
EFI_STATUS
|
GpioGetPadMode (
|
IN GPIO_PAD GpioPad,
|
OUT GPIO_PAD_MODE *PadModeValue
|
)
|
{
|
UINT32 PadCfgRegValue;
|
|
if (!GpioIsPadValid (GpioPad)) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
if (!GpioIsPadHostOwned (GpioPad)) {
|
return EFI_UNSUPPORTED;
|
}
|
|
PadCfgRegValue = GpioReadPadCfgReg (GpioPad, 0);
|
|
*PadModeValue = (GPIO_PAD_MODE)(((PadCfgRegValue & B_GPIO_PCR_PAD_MODE) >> (N_GPIO_PCR_PAD_MODE - (N_GPIO_PAD_MODE_BIT_POS + 1))) | (0x1 << N_GPIO_PAD_MODE_BIT_POS));
|
|
return EFI_SUCCESS;
|
}
|
|
/**
|
The function performs GPIO Power Management programming.
|
**/
|
VOID
|
GpioConfigurePm (
|
VOID
|
)
|
{
|
UINT32 Data32Or;
|
UINT32 Data32And;
|
PCH_SBI_PID *GpioComSbiIds;
|
UINT32 NoOfGpioComs;
|
UINT32 GpioComIndex;
|
|
Data32And = (UINT32)~0,
|
//
|
// Enable MISCCFG.GPSIDEDPCGEn, MISCCFG.GPRCOMPCDLCGEn, MISCCFG.GPRTCDLCGEn,
|
// MISCCFG.GPDLCGEn and MISCCFG.GPDPCGEn for GPIO communities
|
//
|
Data32Or = (B_GPIO_PCR_MISCCFG_GPSIDEDPCGEN |
|
B_GPIO_PCR_MISCCFG_GPRCOMPCDLCGEN |
|
B_GPIO_PCR_MISCCFG_GPRTCDLCGEN |
|
B_GPIO_PCR_MISCCFG_GPDLCGEN |
|
B_GPIO_PCR_MISCCFG_GPDPCGEN);
|
|
NoOfGpioComs = GpioGetComSbiPortIds (&GpioComSbiIds);
|
|
//
|
// Configure Clock Gating in each community
|
//
|
for (GpioComIndex = 0; GpioComIndex < NoOfGpioComs; GpioComIndex++) {
|
MmioAndThenOr32 (
|
PCH_PCR_ADDRESS (GpioComSbiIds[GpioComIndex], R_GPIO_PCR_MISCCFG),
|
Data32And,
|
Data32Or
|
);
|
}
|
}
|
|
/**
|
This procedure is used to unlock all GPIO pads.
|
This function can only be called when platform is still in HOSTIA_BOOT_SAI.
|
**/
|
VOID
|
GpioUnlockAllPads (
|
VOID
|
)
|
{
|
UINT32 DwNum;
|
UINT32 GroupIndex;
|
UINT32 NumberOfGroups;
|
GPIO_GROUP Group;
|
UINT32 LockValue;
|
EFI_STATUS Status;
|
|
NumberOfGroups = GpioGetNumberOfGroups ();
|
|
for (GroupIndex = 0; GroupIndex < NumberOfGroups; GroupIndex++) {
|
Group = GpioGetGroupFromGroupIndex (GroupIndex);
|
for (DwNum = 0; DwNum <= GPIO_GET_DW_NUM (GpioGetPadPerGroup (Group)); DwNum++) {
|
|
GpioGetPadCfgLockForGroupDw (Group, DwNum, &LockValue);
|
|
if (LockValue) {
|
Status = GpioUnlockPadCfgForGroupDw (Group, DwNum, ~0u);
|
ASSERT_EFI_ERROR (Status);
|
}
|
|
GpioGetPadCfgLockTxForGroupDw (Group, DwNum, &LockValue);
|
|
if (LockValue) {
|
Status = GpioUnlockPadCfgTxForGroupDw (Group, DwNum, ~0u);
|
ASSERT_EFI_ERROR (Status);
|
}
|
}
|
}
|
}
|
|
/**
|
Generates GPIO name from GpioPad
|
This function returns pointer to the static buffer
|
|
@param[in] GpioPad GpioPad
|
|
@retval CHAR8* Pointer to the gpio name string
|
**/
|
CHAR8*
|
GpioName (
|
IN GPIO_PAD GpioPad
|
)
|
{
|
return GpioGetPadName (GpioPad, GpioGetStaticNameBuffer (), GPIO_NAME_LENGTH_MAX);
|
}
|
|
|
//
|
// For GPIO debounce feature glitch filter clock is used
|
// which is driven by RTC clock with f = 32kHz (T = 31.25us)
|
//
|
#define GPIO_DEB_CLK_PERIOD_IN_NS 31250
|
|
/**
|
This procedure enables debounce feature on a selected pad configured in input mode
|
Debounce time can be specified in microseconds. GPIO HW supports only certain values
|
according to below formula:
|
DebounceTime = (2 ^ PADCFG_DW2.DEBOUNCE)*(glitch filter clock period).
|
RTC clock with f = 32 KHz is used for glitch filter.
|
DebounceTime = (2 ^ PADCFG_DW2.DEBOUNCE)*(31.25 us).
|
Supported DebounceTime values are following:
|
DebounceTime = 0 -> Debounce feature disabled
|
DebounceTime > 0 && < 250us -> Not supported
|
DebounceTime = 250us - 1024000us -> Supported range (DebounceTime = 250us * 2^n)
|
For values not supported by GPIO HW, function will round down
|
to closest supported
|
|
@param[in] GpioPad GPIO pad
|
@param[in, out] DebounceTime Debounce Time in microseconds
|
If Debounce Time = 0, Debouncer feature will be disabled
|
Function will set DebounceTime argument to rounded supported value
|
|
@retval EFI_SUCCESS The function completed successfully
|
@retval EFI_INVALID_PARAMETER Invalid GpioPad or unsupported DebounceDuration value
|
@retval EFI_UNSUPPORTED GpioPad is not owned by host
|
**/
|
EFI_STATUS
|
GpioSetDebounceTimer (
|
IN GPIO_PAD GpioPad,
|
IN OUT UINT32 *DebounceTime
|
)
|
{
|
UINT32 DebounceEnable;
|
UINT32 DebounceValue;
|
UINT32 InRangeDebounceTime;
|
UINT32 SupportedDebounceTime;
|
UINT32 Temp;
|
BOOLEAN SupportedValue;
|
|
if (!GpioIsPadValid (GpioPad)) {
|
return EFI_INVALID_PARAMETER;
|
}
|
|
if (!GpioIsPadHostOwned (GpioPad)) {
|
return EFI_UNSUPPORTED;
|
}
|
|
if (*DebounceTime > 1024000) {
|
InRangeDebounceTime = 1024000;
|
SupportedValue = FALSE;
|
} else if ((*DebounceTime < 250) && (*DebounceTime > 0)) {
|
InRangeDebounceTime = 0;
|
SupportedValue = FALSE;
|
} else {
|
InRangeDebounceTime = *DebounceTime;
|
SupportedValue = TRUE;
|
}
|
|
//
|
// DebounceValue = log2 (InRangeDebounceTime * f_deb_clk)
|
//
|
DebounceValue = 0;
|
Temp = InRangeDebounceTime * 1000 / GPIO_DEB_CLK_PERIOD_IN_NS;
|
|
//
|
// Check if any rounding occurred
|
//
|
if (InRangeDebounceTime != (Temp * GPIO_DEB_CLK_PERIOD_IN_NS / 1000)) {
|
SupportedValue = FALSE;
|
}
|
|
//
|
// Check if value is power of 2
|
//
|
if ((Temp != 0) && ((Temp & (Temp - 1)) != 0)) {
|
SupportedValue = FALSE;
|
}
|
|
//
|
// DebounceValue = log2 (Temp)
|
//
|
while (Temp > 1) {
|
Temp >>= 1;
|
DebounceValue++;
|
}
|
|
if (DebounceValue > 0) {
|
DebounceEnable = B_GPIO_PCR_DEBEN;
|
SupportedDebounceTime = (1 << DebounceValue) * GPIO_DEB_CLK_PERIOD_IN_NS / 1000;
|
} else {
|
DebounceEnable = 0;
|
SupportedDebounceTime = 0;
|
}
|
|
GpioWritePadCfgReg (
|
GpioPad,
|
2,
|
(UINT32)~(B_GPIO_PCR_DEBOUNCE | B_GPIO_PCR_DEBEN),
|
(DebounceValue << N_GPIO_PCR_DEBOUNCE) | DebounceEnable
|
);
|
|
if (!SupportedValue) {
|
DEBUG ((DEBUG_WARN, "GPIO WARNING: %a %dus debounce time rounded down to %dus\n",
|
GpioName (GpioPad),
|
*DebounceTime,
|
SupportedDebounceTime));
|
}
|
|
*DebounceTime = SupportedDebounceTime;
|
|
return EFI_SUCCESS;
|
}
|