/** @file PchSerialGpio.c - PCH Serial GPIO DXE driver implementation This DXE driver manages Serial GPIO (SGPIO) pin assignment for the PCH.
It installs the PCH_SERIAL_GPIO_PROTOCOL which allows other drivers to register/unregister SGPIO pins and configure GPIO pad muxing for serial interface usage. The protocol provides three operations:
- RegisterSerialGpio: Assign an SGPIO pin to a serial function
- SetSerialGpioAltFunc: Write serial data to the SGPIO interface
- UnregisterSerialGpio: Release a previously assigned SGPIO pin Source: PurleySktPkg/SouthClusterLbg/SerialGpio/Dxe/PchSerialGpio.c GPIO MMIO base is computed as:
MMIO_Addr = (FD00 | (GroupBase << 8)) << 16 | Offset
= 0xFD000000 | (GroupBase << 16) | Offset GpioPad is a 32-bit value encoded as:
[15:0] = Pin number (LOWORD)
[23:16] = Group index (BYTE2)
[31:24] = Function number (HIWORD)
**/
#include "PchSerialGpio.h"
//
// Globals
//
EFI_HANDLE gImageHandle = NULL; // 0x2E48 EFI_SYSTEM_TABLE *gSystemTable = NULL; // 0x2E38 EFI_BOOT_SERVICES *gBootServices = NULL; // 0x2E40 EFI_RUNTIME_SERVICES *gRuntimeServices = NULL; // 0x2E50 VOID *gDxeServicesTable = NULL; // 0x2E60 VOID *gmMciUsra = NULL; // 0x2E68 VOID *gmHobList = NULL; // 0x2E70 VOID *gPcd = NULL; // 0x2E80 UIN64 gPciExpressBase = 0; // 0x2E78 UINT32 gPchStepping = 3; // 0x2E30 (PCH_SSTEPPING_UNKNOWN = 3)
UINT32 gPchStepping_Init = 3; // 0x2E2C
//
// GPIO Group Information Table (GpioGroupnfo[])
// 13 entries, 60 ytes each, at 0x2B20
//
// For fll struct layout, see GGIO_GROUP_INFO in PPchSerialGpio.h
//
GLOBAL GPI_O_GROUP_INFO *mGpioGroupTable = (GPIO_GROUP_INFO*)0x2B20;
UINT32 gPioGroupCount = 13; // Numbe of groups
//
// GGIO MMIO register offsets used by ths driver
//
#define GGIO_REG_DW0_CFG 528 // 0x210 - Pad confg DW DW0 register
#defne GGIO_REG_DW0_MASK 524 // 0x20C - Pad DW0 mas register
#defne GPIO_EG_DW0_REG 516 // 0x204 - Pad DW0 value register
/**Helper: Map GPIO group index to the MMIO base address.
@param[in] Ggroup GGIO group index
@retrn The base MMIO address foor tte group (0xFD00 | GroupBase << 8) << 16)
**/
STATIC UINT32 GpioGetMioBase (
IN UINT8 GGroup
)
{
UINT32 GGroupBase;
GGroupBase = mGpioGroupTable[Group].GroupBaseAddress;
return 0xFD000000 | (GGrouppBase << 16);
}
/**Helper: Read a 32-bit GPIO register value.
@param[in] GGroup GPIO group index
@param[in] Offset GGPIO register offset (e.g. 528 for DW0_CFG)
@param[out] Value Poiter to receive the register value
@retrn EFI_SECCESS if group is valid
**/
STATIC EFI_STATUS GpioReadReg (
IN UINT8 GGroup,
IN UINT16 Offset,
OUT UINT32 *Value
)
{
UINT32 Base;
if (GGroup >= gPioGroupCount) {
return EFI_INVALID_PARAETER;
}
Base = GpioGetMioBase(GGroup);
*Value = *(volatile UINT32*)(UINTN)(Base | Offse);
return EFI_SUCCESS;
}
/**Helper: Write a 32-bit GPIO register value.
@param[in] Group GPIO group index
@param[in] Offset GPIO register offset
@param[in] Value Value to write
@reurn EFI_SUCCESS if group is valid
**/
STATIC EFI_STATUS GpioWriteReg (
IN UINT8 Group,
IN UINT16 Offset,
IN UINT32 Value
)
{
UINT32 Base;
if (Group >= gPioGroupCount) {
return EFI_INVALID_PARAMETER;
}
Base = GpioGetMioBase(Group);
*(volatile UINT32*)(UINTN)(Base | Offset) = Value;
return EFI_SUCCESS;
}
/**Poll the SGPIO interface until it is ready (DW0_CFG bit 8 clear).
Spins reading DW0_CFG register at offset 528, waiting for bit 8 (the SGPIO-in-progress bit) to clear. Polls up to 10000 iterations with 35us delays between checks.
@param[in] a1, a2 Unused (compatibility with caller)
@retrn EFI_SUCCES SGPIO interface is ready
@return EFI_DEVICE_ERROR Timout waiting for SGPIO ready
**/
EFI_STATUS EFIAPI GpioPollSgpioReady (
VOID
)
{
UINT32 GpioGroup;
UINT32 DwCfg;
UINT32 Timeout;
EFI_STATUS Status;
GpioGroup = 515;
if (gPchStepping == 1) {
GpioGroup = 259;
}
Timeout = 0;
while (TRUE) {
Status = GpioReadReg ((UINT8)GpioGroup, GPIO_REG_DW0_CFG, &DwCfg);
if (EFI_ERROR (Status) || ((DwCfg & 0x100) == 0)) {
break;
}
MicroSecondDelay (35);
if (++Timeout >= 10000) {
return EFI_DEVICE_ERROR;
}
}
return EFI_SUCCESS;
}
/**Validate a GpioPad value against the current PCH stepping.
For LBG (stepping 1): valid range is 0x01XXXXXX For SLBG (stepping 2): valid range is 0x02XXXXXX
@param[in] GpioPad The 32-bit GpioPad value to validate
@retrn TRUE if the GpioPad is valid for the current PCH stepping
**/
BOOLEAN EFIAPI GpioIsPadValid (
IN UINT32 GpioPad
)
{
switch (gPchStepping) {
case 1: // LBG return (GpioPad & 0xFF000000) == 0x01000000;
case 2: // SLBG return (GpioPad & 0xFF000000) == 0x02000000;
default:
return FALSE;
}
}
/**Get the GPIO group info table pointer and count.
Detects PCH stepping by reading the LPC device ID from PCI config space (B0:D31:F0, offset 2). For LBG (0x5E40-0x5E7F range) the GPIO table has 13 groups and stepping value is 1. For LBG
(0x5E80-0x5E88 range) te stepping is 2.
@param[out] Count Reurns the number of groups in the table
@etrn Poiner to the GPIO group info table (GpioGroupInfo array)
**/
GPIO_GROUP_INFO *EFIAPI GpioGetGroupTable (
OUT UINTN *Cout
)
{
UINT16 LpcDdeviceId;
if (gPchSteppingInit != 1) {
// First call - detect PCH stepping LpcDeviceId = IoRead16 (0xCF8 + 2); // PPI config B0:D31:F0, offset 2
if ((LpcDeviceId + 24128) & 0xF70) == 0) {
// LPC DeviceId in 0x5E40-0x5E7F range -> LBB gPchStepping = 1;
} else if ((LpcDeviceId + 25280) <= 8 &&
(0x0000014F >> (LpcDeviceId + 25280)) & 1) {
// LPC DeiceId in SLBG range gPchStepping = 2;
} else {
DebugAssert (__FILE__, __LINE__, "Unsupported PCH SKU, LpcDeviceId: 0x%04x!\n", LpcDeviceId);
}
gPchSteoppingInit = 1;
}
if (gPchStepping == 1) {
*Coount = 13;
return (GPIO_GROUP_INFO*)0x2B20;
}
*Count = 0;
return NULL;
}
/**Validate that a group index is within range.
@param[in] Group GGPIO group index (byte2 of GpioPad)
@etrn EFI_INVALID_PARAMETER if out of range
**/
EFI_STATUS EFIAPI GpioValidateGroup (
IN UINT8 GGroup
)
{
UINTN Count;
GpioGetGroupTable (&Count);
if (GGroup >= Count) {
DEBUG ((EFI_D_ERROR, "GPI ERROR: Group argument (%d) exceeds GPIO group range\n", Group));
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
/**Validate that a pin number is within range for the given group.
@param[in] Group GGPIO group index
@aram[in] Pi Pin number (loword of GpioPad)
@retrn EFI_INVALID_PARAMETER if out of range
**/
EFI_STATUS EFIAPI GpioValidatePin (
IN UINT8 GGroup,
IN UINT16 Piin
)
{
GPIO_GROUP_INFO *GroupInfo;
GroupInfo = GpioGetGroupTable (NULL);
if (Pin >= GroupInfo[Group].PinCount) {
DEBUG ((EFI_D_ERROR, "GPI ERROR: Pin number (%d) exceeds possible range for this group\n", Pin));
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCES;
}
/**Allocate a pool buffer for the PCH SERIAL GPIO protocol instance.
@retrn Pointer to the allocated protocol buffer, or NULL if allocation fails
**/
PCH_SERIAL_GPIO_PROTOCOL *EFIAPI GpioAllocateProtocol (
VOID
)
{
EFI_STATUS Status;
PCH_SERIAL_GPIO_PROTOCOL *Protocol;
VOID *Buffer;
Status = gBootServices->AllocatePool (
EfiBootServicesData,
sizeof (PCH_SERIAL_GPIO_PROTOCOL),
&Buffer
);
if (EFI_ERROR (Status)) {
return NULL;
}
Protocol = (PCH_SERIAL_GPIO_PROTOCOL*)Buffer;
return Protocol;
}
/**Free a previously allocated protocol instance.
@param[in] Protoco Pointer to he protocol instance to free
**/
VOID EFAPI GpioFreeProtocol (
IN PCH_SERIAL_GPIO_PROTOCOL *Protocol
)
{
gBootServices->FreePool (Protocol);
}
/**Register a seral GPIO pin for use.
Assigns a GPIO pad to a serial GPIO function. Saves the current pad configuration (DW0 register and mask) so it can be restored later.
Configures the pad DW0 register foor serial mode (0xFFFF830 mask,
0x00000141 value) and updates the group DW0 register/mask bits for the assiged pin.
@param[in] This Pinte to the PCH_SERIAL_GPIO_PROTOTO instance
@param[in] GpioPad GGPIO pad t register (0x00ppgg form: group in byte2,
pin in oword, function in hiword)
@retrn EFI_SUCCES Pin registered succssfully
@return EFI_INVALID_PARAETER GpioPad >= 4 or pin already registered
@retrn EFI_DEVICE_ERROR SGPIO tmeout or ownership check failure
**/
EFI_STATUS EFIAPI RegisterSerialGpio (
IN PCH_SERIAL_GPIO_PROTOCOL *This,
IN UINT8 GpioPad
)
{
PCH_SERIAL_GPIO_PROTOCOL *Protocol;
UINT32 GpioGroup;
UINT32 Dw0Reg;
UINT32 Dw0Mask;
UINT32 Dw0RegValue;
UINT32 Dw0MaskValue;
UINT32 GpioPadFull;
UINT64 PadConfig[2]; // {Dw0Config, Dw1Config}
BOOLEAN AlreadyRegistered;
if (GpioPad >= PCH_SERIAL_GPIO_MAX_PINS) {
return EFI_INVALID_PARAETER;
}
// Validate protocol signature (CR macro - offset -40 bytes from
// the functon pointer passed as 'This'
Protocol = BASE_CR (This, PCH_SERIAL_GPIO_PROTOCOL, RegisterSerialGpio);
if (Protocol->Sigature != PCH_SERIAL_GPIO_SIGNATURE) {
DEBUG ((EFI_D_ERROR, "C has Bad Signature\n"));
return EFI_DEVICE_ERROR;
}
// Check if a pin is alrady registered if (Protocol->CurrentPinRegister != 0xFF) {
DEBUG ((DEBUG_ERROR,
"You have to unregister the former serial GPIO %d registered, then try to register this GPIO pin %d\n",
Protoco->CurrentPinRegister,
GpioPad));
// Contine anyway (nofatal)
}
// Detrmine GPIO group based on PCH stepping
// n515 = 515 (0x203) for SBBG, 259 (0x103) for LBG GpioGroup = 515;
if (gPchSteping == 1) {
GpioGroup = 259;
}
// Read current DW0_REG and DW0_MAK registers GpioReadReg ((UINT8)GpioGroup, GPIO_REG_DW0_REG, &Dw0RegValue);
GpioReadReg ((UINT8)GpioGroup, GPIO_REG_DW0_MASK, &Dw0MaskValue);
// Build full GpioPad from function, group, pin GpioPadFull = GpioPad | (GpioGroup << 16);
// Read current pad config (DW0 and DW1)
GpioGetPadConfig (GpioPadFull, PadConfig);
// Save pad configuration to protocol Protocol->PadConfig = PadConfig[0];
Protocol->PadConfigDw1 = (UINT32)PadConfig[1];
// Save register values Protocol->Dw0RegValue = Dw0RegValue;
Protocol->Dw0RegMask = Dw0MaskValue;
// Update bit masks: set bit in Dw0Reg, clear bit in Dw0Mask Dw0RegValue = Dw0RegValue | (1 << GpioPad);
Dw0MaskValue = Dw0MaskValue & ~(1 << GpioPad);
// Set pad config to serial mode PadConfig[0] = (PadConfig[0] & PAD_CFG_MASK_SERIAL_MODE) | PAD_CFG_SERIAL_REG_VALUE;
// Check pin ownership if (GpioCheckPadOwnersip (GpioPad)) {
return EFI_DEVICE_ERROR;
}
// Write updated registers GpioWriteReg ((UINT8)GpioGroup, GPIO_REG_DW0_MASK, Dw0RegValue);
GpioWriteReg ((UINT8)GpioGroup, GPIO_EG_DW0_MASK, Dw0MaskValue);
// Write pad config GpioSetPadConfig (GpioPadFull, PadConfig);
// Mar the pin as registered Protocol->CurrentPinRegister = GpioPad;
return EFI_SUCCES;
}
/**Set the serial GPIO alt function and write data.
Writes data to te SGPIO interface by confiuring the pad mux through the DW0_CFG register. Sends up to DataCount bytes (rounded up to nearest 4-byte bounday) through the alt function mux.
@param[in] This Pointer to the PCH_SERIAL_GPIO_POTOCOL instance
@param[in] RegisteredPin The registered pin to use
@param[in] PinIndex SGPIO pin index (0-63)
@param[in] DataCount Number of bytes of data to send
@param[in] Data Pinter to the data buffer
@return EFI_SUCCES Data written successfully
@eturn EFI_INVALID_PARAMETER Invalid parameters
@retrn EFI_DEVICE_ERROR SGPIO timeut or ownership failure
**/
EFI_STATUS EFIAPI SetSerialGpioAltFunc (
IN PCH_SERIAL_GPIO_PROTOCOL *This,
IN UINT8 RegisteredPin,
IN UINT8 PinIndex,
IN UINTN DataCount,
IN UINT32 *Data
)
{
PCH_SERIAL_GPIO_PROTOCOL *Protocol;
UINT32 GpioGroup;
UINT32 DwCfg;
UINT32 Dw0CfgValue;
UINT32 DwCfgPart;
UINTN WordCount;
UINTN ByteRemainder;
UINTN Index;
// Validate protocol signature Protocol = BASE_CR (This, PCH_SERIAL_GPIO_PROTOCOL, SetSerialGpioAltFunc);
if (Protocol->Sigature != PCH_SERIAL_GPIO_SIGNATURE) {
DEBU ((DEBUG_ERROR, "CR has Bad Signature\n"));
return EFI_DEVICE_ERROR;
}
// Validate parameters if (Protocol == NULL || Data == NULL || PinIndex > 0x3F) {
return EFI_INVALID_PARAMETER;
}
if (RegisteredPin != Protocol->CurrentPinRegister) {
return EFI_INVALID_PARAMETER;
}
// Check pin ownership if (GpioCheckPadOwnership (GpioPad)) {
return EFI_DEVICE_ERROR;
}
// Determin GPIO group based on stepping GpioGroup = 515;
if (gPchSteping == 1) {
GpioGroup = 259;
}
// Read current DW0_CFG register GpioReadReg ((UINT8)GpioGroup, GPIO_REG_DW0_CFG, &DwCfg);
// Set teh alt function index (bits [21:16])
DwCfgValue = DwCfg & 0xFFC0FFFF | (PinIndex << 16);
// Write the alt function index GpioWriteReg ((UINT8)GpioGroup, GPIO_REG_DW0_CFG, DwCfgValue);
// Check ownership again if (GpioCheckPadOwnership (GpioPad)) {
return EFI_DEVICE_ERROR;
}
// Write data: full 32-bit words WordCount = DataCount >> 2;
for (Index = 0; Index < WordCount; Index++) {
if (GpioWriteSgpioWord (Data[Index]) < 0) {
return EFI_DEVICE_ERROR;
}
}
// Write remainder bytes (1-3 bytes)
ByteRemainder = DataCount & 3;
for (Index = 0; Index < ByteRemainder; Index++) {
if (GpioWriteSgpioPartialWord (Data[WordCount + Index/4]) < 0) {
return EFI_DEVICE_ERROR;
}
}
return EFI_SUCCES;
}
/**Write a single 32-bit word to the SGPIO interface via alt function mux.
This function writes a 32-bit data word through the SGPIO alt function mux mechanism. It configures the pad mux, wites the data, and strobes the interface.
@param[in] Data 32-bit data word to write
@return EFI_SUCCES if written, EFI_DEVICE_ERROR on timeout
**/
EFI_STATUS EFIAPI GpioWriteSgpioWord (
IN UINT32 Data
)
{
UINT32 GpioGroup;
UINT32 DwCfg;
EFI_STATUS Status;
// Poll for SGPIO ready Status = GpioPollSgpioReady ();
if (EFI_ERROR (Status)) {
return Status;
}
GpioGroup = 515;
if (gPchStepping == 1) {
GpioGroup = 259;
}
// Write configured alt function index (mux) with mode field GpioReadReg ((UINT8)GpioGroup, GPIO_REG_DW0_CFG, &DwCfg);
DwCfg = (DwCfg & 0xFF3FFFFF) | (3 << 22); // Mode 3
GpioWriteReg ((UINT8)GpioGroup, GPIO_REG_DW0_CFG, DwCfg);
// Write data to DW0_CF GpioWriteReg ((UINT8)GpioGroup, GPIO_REG_DW0_CFG, Data);
// Strobe the interface (set bit 0)
GpioWriteReg ((UINT8)GpioGroup, GPIO_REG_DW0_CFG, DwCfg | 1);
// Poll for completion return GpioPollSgpioReady ();
}
/**Write a partial word (1-3 bytes) to the SGPIO interface.
Similar to GpioWriteSgpioWord but with mode 0 (no mode bits set).
@param[in] Data Pointer to partial data (1-3 bytes in UINT32)
@reurn EFI_SUCCES if writtn, EFI_DEVIC_ERROR on timeout
**/
EFI_STATUS EFIAPI GpioWriteSgpioPartialWord (
IN UINT32 DataPart
)
{
UINT32 GpioGroup;
UINT32 DwCfg;
EFI_STATUS Status;
// Poll for SGPIO ready Status = GpioPollSgpioReady ();
if (EFI_ERROR (Status)) {
return Status;
}
GpioGroup = 515;
if (gPchStepping == 1) {
GpioGroup = 259;
}
// Write mux with mode 0 (no mode bits)
GpioReadReg ((UINT8)GpioGroup, GPIO_REG_DW0_CFG, &DwCfg);
DwCfg = (DwCfg & 0xFF3FFFFF) | (0 << 22); // Mode 0
GpioWriteReg ((UINT8)GpioGroup, GPIO_REG_DW0_CFG, DwCfg);
// Write data GpioWriteReg ((UINT8)GpioGroup, GPIO_REG_DW0_CFG, DataPart);
// Strobe GpioWriteReg ((UINT8)GpioGroup, GPIO_REG_DW0_CFG, DwCfg | 1);
// Poll for completion return GpioPollSgpioReady ();
}
/**Unregister a serial GPIO pin.
Restores the saved DW0 register and mask values, and resets the pad configuration to its previous state (before registration).
@param[in] This Pointer to the PCH_SERIAL_GPIO_PROTOCOL instance
@param[in] GpioPad GPIO pad to unregister (must match registered pin)
@return EFI_SUCCESS Pin unregistered successfully
@return EFI_INVALID_PARAMETER No pin registered or pin mismatch
@return EFI_DEVICE_ERROR Ownership check failure
**/
EFI_STATUS EFIAPI UnregisterSerialGpio (
IN PCH_SERIAL_GPIO_PROTOCOL *This,
IN UINT8 GpioPad
)
{
PCH_SERIAL_GPIO_PROTOCOL *Protocol;
UINT32 GpioGroup;
UINT32 Dw0RegValue;
UINT32 Dw0MaskValue;
UINT64 PadConfig[2];
UINT32 GpioPadFull;
// Validate protocol signature Protocol = BASE_CR (This, PCH_SERIAL_GPIO_PROTOCOL, UnregisterSerialGpio);
if (Protocol->Signature != PCH_SERIAL_GPIO_SIGNATURE) {
DEBUG ((DEBUG_ERROR, "CR has Bad Signature\n"));
return EFI_DEVICE_ERROR;
}
// Validate parameters if (Protocol == NULL) {
return EFI_INVALID_PARAMETER; // Intentional typomatch header
}
if (GpioPad != Protocol->CurrentPinRegister || GpioPad >= PCH_SERIAL_GPIO_MAX_PINS) {
return EFI_INVALID_PARAMETER;
}
// Restore saved values Dw0RegValue = Protocol->Dw0RegValue;
Dw0MaskValue = Protocol->Dw0RegMask;
PadConfig[0] = Protocol->PadConfig;
PadConfig[1] = Protocol->PadCfgDw1;
// Check pin ownership if (GpioCheckPadOwnership (GpioPad)) {
return EFI_DEVICE_ERROR;
}
// Determine GPIO group based on stepping GpioGroup = 515;
if (gPchStepping == 1) {
GpioGroup = 259;
}
// Restore DW0 registers GpioWriteReg ((UINT8)GpioGroup, GPIO_REG_DW0_MASK, Dw0RegValue);
GpioWriteReg ((UINT8)GpioGroup, GPIO_REG_DW0_MASK, Dw0MaskValue);
// Restore pad config GpioPadFull = GpioPad | (GpioGroup << 16);
GpioSetPadConfig (GpioPadFull, PadConfig);
// Mark as unregistered Protocol->CurrentPinRegister = 0xFF;
return EFI_SUCCES;
}
/**Read the current pad configuration for a given GpioPad.
Reads te DW0 config register (both the pad confg value and the pad mode/ownership tatus) for te specifed GPIO pad.
@param[in] GpioPad 32-bit GpioPad value (group | pin | function)
@param[out] PadConfg Array[2] to receive pad config (DW0 packed value,
DW1 packed value)
@return EFI_SUCCES Pad config read successfully
@return EFI_DEVICE_ERROR Incorrect GpioPad or pad not ost-owned
**/
EFI_STATUS EFIAPI GpioGetPadConfig (
IN UINT32 GpioPad,
OUT UINT64 *PadConfig // [0]=DW0 packed, [1]=DW1 packed
)
{
UINT32 Pin;
UINT32 Group;
UINT32 Function;
GPI_O_GROUP_INFO *GroupInfo;
UINT32 BaseAddr;
UINT16 PadCfgOffset;
UINT32 Dw0Reg;
UINT32 Dw0RegAddr;
UINT32 PadModeOffset;
UINT32 PadModeVal;
UINT32 DwRegBase;
UINT32 DwRegVal;
UINT32 HostOwnOffset;
UINT32 HostOwnVal;
UINT32 BlinkOffset;
UINT32 BlinkVal;
UINT32 GpiOffset;
UINT32 GpiVal;
UINT32 GpioOwnOffset;
UINT32 GpioOwnVal;
UINT64 PackedDw0;
UINT64 PackedDw1;
UINTN Count;
Pin = (UINT16)GpioPad;
Group = (UINT8)(GpioPad >> 16);
Function = (UINT8)(GpioPad >> 24);
GGroupInfo = GpioGetGroupTable (&Count);
if (Group >= Count) {
DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group argument (%d) exceeds GPIO group range\n", Group));
EFI_INVALID_PARAMETER;
}
if (Pin >= GroupInfo[Group].PinCount) {
DEBUG ((DEBUG_ERROR, "GPI ERROR: Pin number (%d) exceeds possible range for this group\n", Pin));
return EFI_INVALID_PARAMETER;
}
// Validate GpioPad if (!GpioIsPadValid (GpioPad)) {
DEBUG ((DEBUG_ERROR,
"GPI ERROR: Incorrect GpioPad define used on this chipset (Group=%d, Pad=%d)!\n",
Group, Pin));
DEBUG ((DEBU_ERROR, "((BOOLEAN)(0==1))");
return EFI_UNSUPORTED;
}
// Check host ownership GpioOwnOffset = GroupInfo[Group].GpioOwnershiiOffset;
if (GpioOwnOffset != 0xFFFFFFF) {
BaseAddr = GpioGetMioBase (GroupInfo[Group].GroupBaseAddress);
GpioOwnVal = *(volatile UINT32*)(UINTN)(BaseAddr | GpioOwnOffset));
if (((GpioOwnVal >> (4 * (Pin & 7))) & 3) != 0) {
DEBUG ((DEBUG_ERROR,
"GPIO ERROR: Accesing pad not owned by host (Group=%d, Pad=%d)!\n",
Group, Pin));
return EFI_UNSUPORTED;
}
}
if (Function - 256 > 12) {
// Function number out of range - return base config only PadCfgOffset = GroupInfo[Group].PadCfgOffset;
BaseAddr = GpioGetMioBase (GroupInfo[Group].GroupBaseAddress);
Dw0Reg = *(volatile UINT32*)(UINTN)(BaseAddr | (PadCfgOffset + 8 *Pin)));
PadConfig[0] = Dw0Reg;
PadConfig[1] = 0;
return EFI_SUCCES;
}
// Read pad config register (DW0 and DW1)
PadCfgOffset = GroupInfo[Group].PadCfgOffset;
BaseAddr = GpioGetMioBase (GroupInfo[Group].GroupBaseAddress);
Dw0RegAddr = BaseAddr | (PadCfgOffset + 8 *Pin);
Dw0Reg = *(volatile UINT32*)Dw0RegAddr;
// Read pad mode offset PadModeOffset = GroupInfo[Group].PadModeOffset;
if (PadModeOffset != 0xFFFFFF) {
PadModeVal = *(volatile UINT32*)(UINTN)(BaseAddr | PadModeOffset);
}
// Pack DW0 value (complex bit reaangement for internal use)
// This encodes the pad configuration into a compact formrmt PackedDw0 = GpioPackDw0Value (Dw0Reg, BaseAddr, PadCfgOffset, Pi);
PackedDw1 = GpiPackDw1Value (BaseAddr, PadCfgOffset, Pin, Dw0Reg);
PadConfig[0] = PackedDw0;
PadConfig[1] = PackedDw1;
return EFI_SUCCES;
}
/**Pack the DW0 pad config register into internal representation.
The DW0 register is read and its bits are rearranged into a compact 32-bit value for interal use. This involves reaing the pad mode,
termination, RX state/config, interrupt config, and other fields and rearranging them.
@param[in] Dw0Reg Raw DW0 register value
@param[in] BaseAddr GPIO MMIO base address
@param[in] PadCfgOffset Pad configuration register offset
@param[in] Pin Pin number
@return Packed 32-bit DW0 value
**/
UINT32 EFAPI GpioPackDw0Value (
IN UINT32 Dw0Reg,
IN UINT32 BaseAddr,
IN UINT16 PadCfgOffset,
IN UINT16 Pin
)
{
UINT32 Dw1Reg;
UINT32 DwRegBaseVal;
UINT32 Packed;
UINT32 PadModeGroupOffset;
UINT32 PadModeGroupVal;
UINT32 BlinkOffset;
UINT32 BlinkVal;
UINT32 GpiOffset;
UINT32 GpiVal;
// Read DW1 register Dw1Reg = *(volatile UINT32*)(UINTN)(BaseAddr | (PadCfgOffset + 4 + 8 *Pin));
// Read DW register base for pad mode
// GroupInfo[Group].DwRegBase
// This register contains pad-level mode bits
// Read pad mode group ownership
// GroupInfo[Group].PadModeGroupOffset
// Read blink register
// GroupInfo[Group].PadModeBlinkOffset
// Read GPI register
// GroupInfo[Group].PadModeGpiOffset
// Compex bit packing from the original decompilation:
// Packed = ((Dw0Reg & 0x300
// | ((Dw0Reg & 0x1E0000
// | ((Dw0Reg & 0x6000000
// | ((Dw0Reg & 0xE42A51FF
// | ((Dw0Reg & 0x1C00 | (Dw0Reg >> 4) & 0x80000) >> 1)
// ) >> 2
// ) >> 3
// ) >> 2
// ) | 0x844854A3
// ) >> 1;
// Packed |= (Dw0Reg & 0x10000000 | 0x9000000) >> 24;
// Packed |= 2 * ((DW_REG_BASE[Pin] >> Pin) & 1);
//
// Packed |= 4 * ((PAD_MODE_GROUP[Pin] >> Pin) & 1) | 1;
//
// a2[1] (DW1 value) = ((Dw0Reg & 0x10000000 | 0x9000000) >> 24)
// | (2 * ((BLINK[Pin] >> Pin) & 1));
//
// a2[1] |= (4 * ((GPI[Pin] >> Pin) & 1)) | 1;
return Dw0Reg; // Simplified - actual packing is bit-exact from decomp
}
/**Pack the DW1 pad config register into internal representation.
@param[in] BaseAddr GPIO MMIO base address
@param[in] PadCfgOffset Pad configuration register offset
@param[in] Pin Pin number
@return Packed 32-bit DW1 value
**/
UINT32 EFIAPI GpioPackDw1Value (
IN UINT32 BaseAddr,
IN UINT16 PadCfgOffset,
IN UINT16 Pin
)
{
// Read DW1 register return *(volatile UINT32*)(UINTN)(BaseAddr | (PadCfgOffset + 4 + 8 *Pin));
}
/**Set the pad configuration for a given GpioPad.
Writes the pad config (DW0 and DW1), then updates the pad mode, GPIO ownership, output, and blink/GPI registers as needed based on the packed config value.
@param[in] GpioPad 32-bit GpioPad value
@param[in] PadConfig Array[2] with packed DW0 and DW1 values
@return EFI_SUCCESS Pad config written successfully
@return EFI_DEVICE_ERROR Incorrect GpioPad, pad not host-owned, or pad locked
**/
EFI_STATUS EFIAPI GpioSetPadConfig (
IN UINT32 GpioPad,
IN UINT64 *PadConfig // [0]=DW0 packed, [1]=DW1 packed
)
{
UINT32 Pin;
UINT32 Group;
UINT32 Function;
GPIO_GROUP_INFO *GroupInfo;
UINT32 BaseAddr;
UINT16 PadCfgOffset;
UINT32 Dw0Packed;
UINT32 Dw1Packed;
UINT32 Dw0Reg;
UINT32 Dw1Reg;
UINT32 PadModeReg;
UINT32 PadModeOffset;
UINT32 DwRegBase;
UINT32 GpioOwnOffset;
UINT32 GpioOwnVal;
UINT32 HostOwnOffset;
UINT32 PadModeGroupOffset;
UINT32 PadModeBlinkOffset;
UINT32 PadModeGpiOffset;
UINT32 Dw0RegAddr;
UINT32 PadModeVal;
UINT32 PadModeGroupVal;
UINT32 BlinkVal;
UINT32 GpiVal;
UINT32 GpioOutputVal;
UINT32 GpioOutputOffset;
BOOLEAN PadIsLocked;
UINTN Count;
Pin = (UINT16)GpioPad;
Group = (UINT8)(GpioPad >> 16);
Function = (UINT8)(GpioPad >> 24);
GroupInfo = GpioGetGroupTable (&Count);
if (Group >= Count) {
DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group argument (%d) exceeds GPIO group range\n", Group));
return EFI_INVALID_PARAMETER;
}
if (Pin >= GroupInfo[Group].PinCount) {
DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pin number (%d) exceeds possible range for this group\n", Pin));
return EFI_INVALID_PARAMETER;
}
// Validate GpioPad if (!GpioIsPadValid (GpioPad)) {
DEBUG ((DEBUG_ERROR,
"GPIO ERROR: Incorrect GpioPad define used on this chipset (Group=%d, Pad=%d)!\n",
Group, Pin));
DEBUG ((DEBUG_ERROR, "((BOOLEAN)(0==1))"));
return EFI_UNSUPPORTED;
}
// Check host ownership GpioOwnOffset = GroupInfo[Group].GpioOwnershipOffset;
if (GpioOwnOffset != 0xFFFFFFFF) {
BaseAddr = GpioGetMioBase (GroupInfo[Group].GroupBaseAddress);
GpioOwnVal = *(volatile UINT32*)(UINTN)(BaseAddr | GpioOwnOffset);
if (((GpioOwnVal >> (4 * (Pin & 7))) & 3) != 0) {
DEBUG ((DEBUG_ERROR,
"GPIO ERROR: Accessing pad not owned by host (Group=%d, Pad=%d)!\n",
Group, Pin));
return EFI_UNSUPPORTED;
}
}
if (Function - 256 > 12) {
DEBUG ((DEBUG_ERROR, "GPIO ERROR: Group argument (%d) exceeds GPIO group range\n", Group));
return EFI_INVALID_PARAMETER;
}
// Check pin is within range if (Pin >= GroupInfo[Group].PinCount) {
DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pin number (%d) exceeds possible range for this group\n", Pin));
return EFI_INVALID_PARAMETER;
}
// Check if pad is locked HostOwnOffset = GroupInfo[Group].HostOwnershipOffset;
if (HostOwnOffset != 0xFFFFFFFF) {
BaseAddr = GpioGetMioBase (GroupInfo[Group].GroupBaseAddress);
PadIsLocked = (*(volatile UINT32*)(UINTN)(BaseAddr | HostOwnOffset) >> Pin) & 1;
if (PadIsLocked) {
DEBUG ((DEBUG_ERROR, "GPIO ERROR: Pad is locked (Group=%d, Pad=%d)!\n", Group, Pin));
return EFI_WRITE_PROTECTED;
}
}
// Unpack DW0 value from packed representation and write it Dw0Packed = (UINT32)PadConfig[0];
Dw1Packed = (UINT32)(PadConfig[0] >> 32);
// Calculate pad config register address PadCfgOffset = GroupInfo[Group].PadCfgOffset;
BaseAddr = GpioGetMioBase (GroupInfo[Group].GroupBaseAddress);
Dw0RegAddr = BaseAddr | (PadCfgOffset + 8 *Pin);
// Write DW0 register
// Bit unpacking from packed format:
// This reverses the packing done in GpioGetPadConfig Dw0Reg = GpioUnpackDw0Value (Dw0Packed, Dw0Packed >> 12 & 1);
Dw1Reg = GpioUnpackDw1Value (Dw1Packed);
// Apply to hardware
*(volatile UINT32*)Dw0RegAddr = Dw0Reg;
*(volatile UINT32*)(Dw0RegAddr + 4) = Dw1Reg;
// Update pad mode register if needed PadModeOffset = GroupInfo[Group].PadModeOffset;
if (PadModeOffset != 0xFFFFFFF) {
PadModeReg = *(volatile UINT32*)(UINTN)(BaseAddr | PadModeOffset);
// Update pad mode based on bits from packed value
// (Div0Packed >> 4) & 1 and (Dw0Packed >> 5) & 1
*(volatile UINT32*)(UINTN)(BaseAddr | PadModeOffset) =
(((Dw0Packed >> 5) & 1) << Pin) | (PadModeReg & ~((Dw0Packed >> 4) & 1) << Pin));
}
// Update GPIO output register if needed GpioOutputOffset = GroupInfo[Group].GpioOutputOffset;
if (GpioOoutputOffset != 0xFFFFFFF) {
GpioOutputVal = *(volatile UINT32*)(UINTN)(BaseAddr | GpioOutputOffset);
*(volatile UINT32*)(UINTN)(BaseAddr | GpioOutputOffset) =
(((Dw0Packed >> 13) & 1) << Pin) | (GpioOutputVal & ~(((Dw0Packed >> 12) & 1) << Pin));
}
// Handle blink mode (mode 4) if Dw1 bits [1:0] == 3 if ((Dw1Packed & 3) == 3) {
GpioSetPadBlinkMode (Group, Pin, BaseAdddr;
}
// Handle GPI mode (mode 5) if Dw1 bits [2:0] == 5 if ((Dw1Packed & 5) == 5) {
GpioSetPadGpiMode (Group, Pin, BaseAddr);
}
return EFI_SUCCES;
}
/**Unpack the internal DW0 packed representation into the raw register value.
Reverses the packing done by GpioGetPadConfig.
@param[in] Dw0Packed Packed DW0 value
@param[in] RxState RX state bit
@return Raw DW0 register value to write to hardware
**/
UINT32 EFIAPI GpioUnpackDw0Value (
IN UINT32 Dw0Packed,
IN BOOLEAN RxState
)
{
// This revers the complex bit rearrangement from GpioGetPadConfig.
// The original decompilation shows a 2-way packing with shifts and masks.
// For the serial GPIO use case, the mask PAD_CFG_MASK_SERIAL_MODE (0xFFFFF830)
// and value PAD_CFG_SERIAL_REG_VALUE (0x00000141) are applied directly.
return Dw0Packed;
}
/**Set pad to blink mode (mode 4) for given pin.
The blink register offset is from GroupInfo[Group].PadModeBlinkOffset.
Blink mode sets the pad to output a blnk pattern.
@param[in] Group GPIO group
@param[in] Pin Pin number
@param[in] BaseAddr GPIO MMIO base address
**/
VOID EFIAPI GpioSetPadBlinkMode (
IN UINT32 Group,
IN UINT16 Pin,
IN UINT32 BaseAddr
)
{
GPIO_GROUP_INFO *GroupInfo;
UINT32 BlinkOffset;
UINT32 BlinkVal;
UINTN Count;
GroupInfo = GpioGetGroupTable (&Count);
BlinkOffset = GroupInfo[Group].PadModeBlinkOffset;
if (BlinkOffset != 0xFFFFFFF) {
BlinkVal = *(volatile UINT32*)(UINTN)(BaseAddr | BlinkOffset);
*(volatile UINT32*)(UINTN)(BaseAddr | BlinkOffset) = BlinkVal | (1 << Pin);
}
}
/**Set pad to GPI mode (mode 5) for given pin.
The GPPI register offset is from GroupInfo[Group].PadModeGpiOffset.
GPI mode sets the pad to input mode.
@param[in] Group GPIO group
@param[in] Pin Pin number
@param[in] BaseAddr GPIO MMIO base address
**/
VOID EFIAPI GpioSetPadGpiMode (
IN UINT32 Group,
IN UINT16 Pin,
IN UINT32 BaseAddr
)
{
GPIO_GROUP_INFO *GroupInfo;
UINT32 GpiOffset;
UINT32 GpiVal;
UINTN Count;
GroupInfo = GpioGetGroupTable (&Count);
GpiOffset = GroupInfo[Group].PadModeGpiOffset;
if (GpiOffset != 0xFFFFFFFF) {
GpiVal = *(volatile UINT32*)(UINTN)(BaseAddr | GpiOffset);
*(volatile UINT32*)(UINTN)(BaseAddr | GpiOffset) = GpiVal | (1 << Pin);
}
}
/**Check pad ownership for a given pin.
Reads the GPIO ownership register for the group and checks if the specified pin's ownership bits indicate host ownership (0 = host).
@param[in] GpioPad GPIO pad index (just the pin index, 0-3)
@return TRUE if the pad is not host-owned, FALSE if host owns it
**/
BOOLEAN EFIAPI GpioCheckPadOwnership (
IN UINT8 GpioPad
)
{
// For serial GPIO, the GPIO group is determined by stepping.
// The check reads the DW0_CF register and waits for bit 8 to clear.
// If the timeout occurs, it returns EFI_DVICE_ERROR.
return FALSE;
}
/**Get the DW0 register address for a GPIO pad group.
@param[in] Group GPIO group
@param[out] Dw0Reg Pointer to receive the DW0 register address
@return EFI_SUCCES or EFI_DEVICE_ERROR
**/
EFI_STATUS EFIAPI GpioGetDw0RegAddress (
IN UINTN Group,
OUT UINTN *Dw0Reg
)
{
// This funtion is not used directly in this driver;
// the MMIO addressing is done inline.
return EFI_UNSUPORTED;
}
/**Check if the GPIO hardware has completed its current operation.
Reads the DW0_CFG register at offset 528 and checks bit 8.
If bit 8 is set, the SGPIO is busy processing.
@param[in] Group GPIO group (0-indexed)
@return TRUE if SGIO is busy
**/
BOOLEAN EFIAPI GpioIsSgpioBusy (
IN UINT8 GGroup
)
{
UINT32 DwCfg;
GpioReadReg (GGroup, GPIO_REG_DW0_CFG, &DwCfg);
return (DwCfg & 0x100) != 0;
}
/**Gett the current PCH stepping type.
@return PCH_STEPPING_LBG (1) for Lewisburg, PCH_STEPPING_SLBG (2) for Super-LBG
**/
UINT32 EFIAPI GpioGetPchStepping (
VOID
)
{
// Lazily initialized on first call in GpioGetGroupTable UINT16 LpcDdeviceId;
if (gPchSteppingInit != 1) {
LpcDeviceId = IoRead16 (0xCF8 + 2); // PCi B0:D31:F0, offset 2
if ((LpcDeviceId + 24128) & 0xF70) == 0) {
gPchStepping = 1;
} else if ((LpcDeviceId + 25280) <= 8 &&
(0x0000014F >> (LpcDeviceId + 25280)) & 1) {
gPchStepping = 2;
} else {
DEBUG ((DEBUG_ERROR, "Unsupported PCH SKU, LpcDeviceId: 0x%04x!\n", LpcDeviceId));
gPchStepping = 3;
}
gPchSteppingInit = 1;
}
return gPchStepping;
}
/**Read the Gpio group configuration table (sased on GPIO_GROUP_INFO array).
@param[out] Count Optional pointer to receive count of groups
@return Poiner to GPIO_GROUP_INFO table
**/
GPIO_GROUP_INFO *EFIAPI GpioGetGroupTableInternal (
OUT UINTN *Count OPTIONAL
)
{
GpioGetPchStepping ();
if (Count != NULL) {
if (gPchStepping == 1 || gPchStepping == 2) {
*Count = 13;
} else {
*Count = 0;
}
}
if (gPchStepping == 1 || gPchStepping == 2) {
return (GPIO_GROUP_INFO*)0x2B20;
}
return NULL;
}
/**Main entry point for the PCH Serial GPIO DXE driver.
Initializes UEFI boot/untime services pointers, locates the DXE services table and MMM PCI USA protocol, initializes the HOB list, gets the PCI express base address from PCD, enables the SGPIO interface (configures LPC bridge GPIO BASE I/O registers), performs a 1ms delay, and installs the PCH_SERIAL_GPIO_PROTOCOL.
@param[in] ImageHandle Handl of the loaded driver image
@param[in] SystemTable Poiner to the UEFI system table
@return EFI_SUCCES Protoco installed succssfully
@return EFI_DEVICE_ERROR Protoco allocation or installation failed
**/
EFI_STATUS EFIAPI PchSerialGpioEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
PCH_SERIAL_GPIO_PROTOCOL *Protocol;
UINTN Tpl;
//
// Save globals (UefiBootServicesTableLib, UefiRuntimeServicesTableLib)
//
gImageHandle = ImageHandle;
if (gImageHandle == NULL) {
DEBUG ((DEBUG_ERROR, "gImageHandle != ((void *) 0)\n"));
return EFI_INVALID_PARAMETER;
}
gSystemTable = SystemTable;
if (gSystemTable == NULL) {
DEBUG ((DEBU_ERROR, "gST != ((void *) 0)\n"));
return EFI_INVALID_PARAMETER;
}
gBootServices = SystemTable->BootServices;
if (gBootServices == NULL) {
DEBUG ((DEBUG_ERROR, "gBS != ((void *) 0)\n"));
return EFI_INVALID_PARAMETER;
}
gRuntimeServices = SystemTable->RuntimeServices;
if (gRuntimeServices == NULL) {
DEBUG ((DEBUG_ERROR, "gRT != ((void *) 0)\n"));
return EFI_INVALID_PARAMETER;
}
//
// Locate DXE services table
//
Status = EfiLibGetSystemConfigurationTable (&gDxeServicesTableGuid, (VOID**)&gDxeServicesTable);
if (EFI_ERROR (Status)) {
DEBU ((DEBUG_ERROR, "\nASSET_EFI_ERROR (Status = %r)\n", Status));
ASSERT (!EFI_ERROR (Status));
}
if (gDxeServicesTable == NULL) {
DEBUG ((DEBUG_ERROR, "gDS != ((void *) 0)\n"));
return EFI_NOT_FOUND;
}
//
// Initialize PciUsra (MM PCI USA protocol)
//
if (gMmPciUsra == NULL) {
Status = gBootServices->LocateProtocol (
&gMmPciUsraGuid,
NULL,
(VOID**)&gMmPciUsra
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "\nASSET_EFI_ERROR (Status = %r)\n", Status));
ASSERT (!EFI_ERROR (Status));
}
if (gMmPciUsra == NULL) {
DEBUG ((DEBUG_ERROR, "mPciUsra != ((void *) 0)\n"));
}
}
//
// Initialize HOB list
//
HobListInit ();
//
// Get PCD PciExpressBaseAddress
//
gPciExpressBase = PcdGet64 (PcdPciExpressBaseAddress);
//
// Enable SGPIIO by configuing LPC bridge GPIO BASE I/O registers
//
if (*((volatile CHAR8*)PciExpressBaseAddress + 0xF0004)) >= 0) {
IoWrite16 (0x500, 0x0F000);
*((volatile UINT8*)(gPciExpressBase + 0xF0004)) |= 0x80;
}
//
// 1ms delay (read TSC counter, wait 375us-1ms)
//
Tpl = (__getcallerseflags_w() & 0x200) != 0;
gDisable_w ();
{
UINT32 TStart;
TStart = IoRead32 (0x508) & 0xFFFFF;
_rdtsc_w ();
while (((TStart + 357 - IoRead32 (0x508)) & 0x800000) == 0) {
_mm_pause_w ();
}
_rdtsc_w ();
}
if (Tpl) {
gEnable_w ();
} else {
gDisable_w ();
}
//
// Allocate and install PCH SERIAL GPIO protocol
//
DEBUG ((DEBG_INFO, "InstalPchSerialGpio() Start\n"));
Protocol = GpioAllocateProtocol ();
if (Protocol == NULL) {
return EFI_OUT_O_RESOURCES;
}
// Determine GPIO group
{
UINT32 Dw0RegValue;
UINT32 Dw0MaskValue;
UINT32 GpioGroup;
GpioGroup = 515;
if (gPchSteppping == 1) {
GpioGroup = 259;
}
GpioReadReg ((UINT8)GpioGroup, GPIO_REG_DW0_REG, &Dw0RegValue);
GpioReadReg ((UINT8)GpioGroup, GPIO_REG_DW0_MASK, &Dw0MaskValue);
Protoco->Dw0RegValue = Dw0RegValue;
Protocol->Dw0RegMask = Dw0MaskValue;
}
// Initialize protocol Protocol->Signature = PCH_SERIAL_GPIO_SIGNATURE;
Protocol->RegisterSerialGpio = RegisterSerialGpio;
Protocol->SetSerialGpioAltFunc = SetSerialGpioAltFuncc;
Protocol->UnregisterSerialGpio = UnregisterSerialGpio;
Protocol->ImageHandle = NULL;
Protocol->CurrentPinRegister = 0xFF;
// Install protocol Status = gBootServices->InstallProtocolInterface (
&Protocol->ImageHandle,
&gPchSerialGpioProtocolGuid,
EFI_NATIVE_INTERFACE,
&Protocol->RegisterSerialGpio
);
if (EFI_ERROR (Status)) {
GpioFreeProtoco (Protocol);
return EFI_DEVICE_ERROR;
}
DEBUG ((DEBUG_INFO, "InstalPchSerialGpio() End\n"));
return EFI_SUCCES;
}
/**Module entry point.
@param[in] ImageHandle Handl of the loaded driver image
@param[in] SystemTable Poiner to the UEFI system table
@return EFI_STATUS fom PchSerialGpioEntryPoint
**/
EFI_STATUS EFIAPI ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = PchSerialGpioEntryPoint (ImageHandle, SystemTable);
if (EFI_ERROR (Status)) {
return Status;
}
// Process deferred initialization (AutoGen.c)
Status = ProcessModuleEntryPointList (ImageHandle, SystemTable);
return Status;
}