alder_lake_bios/Intel/AlderLake/AlderLakeChipsetPkg/HybridGraphicsSmm/HybridGraphicsSmm.c

595 lines
17 KiB
C

/** @file
This driver is for Hybrid Graphics Feature SMM initialize.
;******************************************************************************
;* Copyright (c) 2018, Insyde Software Corp. All Rights Reserved.
;*
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
;* transmit, broadcast, present, recite, release, license or otherwise exploit
;* any part of this publication in any form, by any means, without the prior
;* written permission of Insyde Software Corporation.
;*
;******************************************************************************
*/
#include <HybridGraphicsSmm.h>
#include <Library/GpioLib.h>
#include <Library/GpioNativeLib.h>
#include <Protocol/SmmIoTrapDispatch2.h>
#include <Protocol/PchSmmIoTrapControl.h>
#include <Library/TimerLib.h>
#include <Library/DebugLib.h>
#include <Library/MmPciLib.h>
#include <Library/IoLib.h>
#include <Register/SaPcieDmiRegs.h>
//
// Function Prototypes
//
VOID
MxmInt15Callback (
IN OUT EFI_IA32_REGISTER_SET *CpuRegs,
IN VOID *Context
);
VOID
IntelHgCallback (
IN OUT EFI_IA32_REGISTER_SET *CpuRegs,
IN VOID *Context
);
STATIC
BOOLEAN
SmmGpioRead (
IN UINT8 GpioSupport,
IN UINT32 Gpio
);
STATIC
VOID
SmmGpioWrite (
IN UINT8 GpioSupport,
IN UINT32 Gpio,
IN BOOLEAN Level
);
STATIC
VOID
NvidiaOptimusHandler (
IN EFI_HANDLE DispatchHandle,
IN CONST VOID *DispatchContext,
IN OUT VOID *CommBuffer,
IN OUT UINTN *CommBufferSize
);
EFI_STATUS
EFIAPI
InitialNvidiaOptimusIOTrap (
VOID
);
//
// Module globals
//
EFI_SMM_SYSTEM_TABLE2 *mSmst;
H2O_HG_INFO_PROTOCOL *mHgInfoProtocol;
EFI_SMM_IO_TRAP_DISPATCH2_PROTOCOL *mSmmIoTrapDispatch;
EFI_SMM_IO_TRAP_REGISTER_CONTEXT mIchIoTrapContext64;
PCH_SMM_IO_TRAP_CONTROL_PROTOCOL *mPchSmmIoTrapControl;
EFI_HANDLE mIchIoTrapHandle64;
/**
Hybrid Graphics feature SMM driver entry point.
This driver will install HG and MXM int 15 callback function.
@param[in] ImageHandle Image handle of this driver.
@param[in] SystemTable Pointer to standard EFI system table.
@retval EFI_SUCCESS Hybrid Graphics feature DXE initialized successfully.
@retval !EFI_SUCCESS Hybrid Graphics feature doesn't be supported.
**/
EFI_STATUS
EFIAPI
HybridGraphicsSmmInitEntry (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
BOOLEAN InSmm;
EFI_SMM_BASE2_PROTOCOL *SmmBase2;
EFI_SMM_INT15_SERVICE_PROTOCOL *SmmInt15Service;
EFI_STATUS Status;
InSmm = FALSE;
SmmInt15Service = NULL;
mHgInfoProtocol = NULL;
Status = gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID**)&SmmBase2);
if (EFI_ERROR (Status)) {
return Status;
}
Status = SmmBase2->InSmm (SmmBase2, &InSmm);
if (EFI_ERROR (Status)) {
return Status;
}
if (!InSmm) {
return EFI_SUCCESS;
}
//
// Great! We're now in SMM!
//
Status = SmmBase2->GetSmstLocation (SmmBase2, &mSmst);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Locate the HG Info Protocol
//
Status = gBS->LocateProtocol (&gH2OHybridGraphicsInfoProtocolGuid, NULL, (VOID **)&mHgInfoProtocol);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Locate gH2OHybridGraphicsInfoProtocolGuid Protocol%r\n", Status));
}
if (FeaturePcdGet (PcdNvidiaOptimusSupported)) {
//
// Register SMM handler
//
Status = InitialNvidiaOptimusIOTrap();
}
Status = mSmst->SmmLocateProtocol (&gEfiSmmInt15ServiceProtocolGuid, NULL, (VOID **)&SmmInt15Service);
if (EFI_ERROR(Status)) {
return Status;
}
Status = SmmInt15Service->InstallInt15ProtocolInterface (
SmmInt15Service,
HYBRID_GRAPHICS_INT15_FUN_NUM,
IntelHgCallback,
NULL
);
if (Status == EFI_ALREADY_STARTED) {
//
// use new callback function to replace original one
//
Status = SmmInt15Service->ReinstallInt15ProtocolInterface (
SmmInt15Service,
HYBRID_GRAPHICS_INT15_FUN_NUM,
IntelHgCallback,
NULL
);
if (EFI_ERROR(Status)) {
return Status;
}
}
if (FeaturePcdGet (PcdNvidiaOptimusSupported)) {
Status = SmmInt15Service->InstallInt15ProtocolInterface (
SmmInt15Service,
MXM_INT15_FUN_NUM,
MxmInt15Callback,
NULL
);
if (Status == EFI_ALREADY_STARTED) {
//
// use new callback function to replace original one
//
Status = SmmInt15Service->ReinstallInt15ProtocolInterface (
SmmInt15Service,
MXM_INT15_FUN_NUM,
MxmInt15Callback,
NULL
);
if (EFI_ERROR(Status)) {
return Status;
}
}
}
return EFI_SUCCESS;
}
/**
MXM (Mobile PCI Express Module) INT15 Callback Routine specific function Number 0x5F80
get MXM SIS (System Information Structure) binary or VBIOS.
@param[in] CpuRegs The structure containing CPU Registers (AX, BX, CX, DX etc.).
CX - For most Settings (Configurations) described in the VBIOS Spec.
BX - In most cases, it is a calling register. It also can be used
as a return (for Settings/Configurations) register like CX
AX - Indicate the Status of Function Supported Fail/Success
@param[in] Context Pointer to the context data registerd to the Event.
@return None.
**/
VOID
MxmInt15Callback (
IN OUT EFI_IA32_REGISTER_SET *CpuRegs,
IN VOID *Context
)
{
UINT32 *MxmBinSrc;
UINT16 MxmBinSize;
MxmBinSrc = NULL;
MxmBinSize = 0;
if ((CpuRegs->X.AX & 0xFFFF) != MXM_INT15_FUN_NUM) {
return;
}
switch (CpuRegs->H.BL) {
case 0:
//
// Function 0 -Return Specification Support Level
//
// Entry:
// AX = 5F80h
// BL = 00h
// BH = FFh
// EBX[16:31] = Adapter ID
// CX = Revision of the MXM software specification
// that is supported by the MXM Module
//
// Return:
// AX = 005Fh to indicate that the system bios supports this function
// BL = Revision of the MXM software specification that is supported
// by the System Format is binary coded decimal,
// for example: 30h = 3.0, etc.
// CX = MXM functions supported
// Bit 0 = 1
// Bit 1 = 1 if Function 1 is supported, 0 if not supported
// Bit 2 = 1 if Function 2 is supported, 0 if not supported
// Bit 3 = 1 if Function 3 is supported, 0 if not supported
// Bit 4 = 1 if Function 4 is supported, 0 if not supported
// Bit 7 = 1 if Function 7 is supported, 0 if not supported
// Bit 8 = 1 if Function 8 is supported, 0 if not supported
// Bit 9 = 1 if Function 9 is supported, 0 if not supported
//
CpuRegs->H.BL &= ~(0xFF);
CpuRegs->H.BL |= 0x30; // Support 3.0
if (mHgInfoProtocol->HgMode == HgModeDisabled) {
//
// Return Function supported but failed
//
CpuRegs->X.CX = 0x0000;
CpuRegs->X.AX &= ~(0xFFFF);
CpuRegs->X.AX |= 0x015F;
break;
}
if (mHgInfoProtocol->HgMode == HgModeDgpu) {
//
// Support function 0, 1
//
CpuRegs->X.CX &= ~(0xFFFF);
CpuRegs->X.CX |= 0x0003;
}
if (mHgInfoProtocol->HgMode == HgModeMuxless) {
//
// Support function 0, 1, 7
//
CpuRegs->X.CX &= ~(0xFFFF);
CpuRegs->X.CX |= 0x0083;
}
//
// Return Function supported and successful
//
CpuRegs->X.AX &= ~(0xFFFF);
CpuRegs->X.AX |= 0x005F;
break;
case 1:
//
// Function 1 -Return a Pointer to the MXM Structure
//
// Entry:
// AX = 5F80h
// BL = 01h
// BH = FFh
// EBX[16:31] = Adapter ID
// CX = Identifier for the data block to return
//
// Return:
// AX = 005Fh to indicate that the system bios supports this function
// BX = Vendor ID of data block if CX = 0x80-0x8F, else 0
// ES:DI = Pointer to the MXM structure in real mode memory (< 1 MB)
//
if (mHgInfoProtocol == NULL) {
//
// Return Function supported but failed
//
CpuRegs->X.AX &= ~(0xFFFF);
CpuRegs->X.AX |= 0x015F;
break;
}
if ((CpuRegs->E.EBX & 0xFF000000) > 0x01000000) {
MxmBinSrc = (UINT32 *)mHgInfoProtocol->Dgpu2MxmBinFile.Address;
MxmBinSize = (UINT16)mHgInfoProtocol->Dgpu2MxmBinFile.Size;
} else {
MxmBinSrc = (UINT32 *)mHgInfoProtocol->DgpuMxmBinFile.Address;
MxmBinSize = (UINT16)mHgInfoProtocol->DgpuMxmBinFile.Size;
}
if ((MxmBinSize > 0x1000) || (MxmBinSize == 0) || (MxmBinSrc == 0)) {
//
// Return Function supported but failed
//
CpuRegs->X.AX &= ~(0xFFFF);
CpuRegs->X.AX |= 0x015F;
break;
}
CpuRegs->X.ES = (UINT16)((((UINTN)MxmBinSrc) & ~(0xFFFF)) >> 4);
CpuRegs->E.EDI &= ~(0xFFFF);
CpuRegs->E.EDI |= (UINT32)(((UINTN)MxmBinSrc) & (0xFFFF));
//
// The system bios supports this function but function failed (reserve bit31 - bit16)
//
CpuRegs->X.AX &= ~(0xFFFF);
CpuRegs->X.AX |= 0x005F;
break;
case 7:
//
// Function 7 -Return a Pointer to the VBIOS Image for ROM-Less Adapters
//
// Entry:
// AX = 5F80h
// BL = 07h
// BH = FFh
// EBX[16:31] = Adapter ID
//
// Return:
// AX = 005Fh to indicate that the system bios supports this function
// ESDI = Physical Memory offset to the 128K aperture
// containing the selected VBIOS image.
//
if (mHgInfoProtocol == NULL) {
//
// The system bios supports this function but function failed (reserve bit31 - bit16)
//
CpuRegs->X.AX &= ~(0xffff);
CpuRegs->X.AX |= 0x015F;
break;
}
CpuRegs->X.ES = 0;
CpuRegs->E.EDI &= ~(0xFFFFFFFF);
CpuRegs->E.EDI |= (UINT32)((mHgInfoProtocol->Vbios.Address) & (0xFFFFFFFF));
//
// The system bios supports this function but function failed (reserve bit31 - bit16)
//
CpuRegs->X.AX &= ~(0xffff);
CpuRegs->X.AX |= 0x005F;
break;
default:
break;
}
}
/**
INTEL Hybrid Graphics INT15 Callback Routine specific function Number 0x5F70 get HG mode status.
@param[in] CpuRegs The structure containing CPU Registers (AX, BX, CX, DX etc.).
CX - For most Settings (Configurations) described in the VBIOS Spec.
BX - In most cases, it is a calling register. It also can be used
as a return (for Settings/Configurations) register like CX
AX - Indicate the Status of Function Supported Fail/Success
@param[in] Context Pointer to the context data registerd to the Event.
@return None.
**/
VOID
IntelHgCallback (
IN OUT EFI_IA32_REGISTER_SET *CpuRegs,
IN VOID *Context
)
{
//
// Int15h Hook 5F70h
//
switch (CpuRegs->H.CH) {
case 2:
//
// Get HG/Non-HG Mode
// AX = 5F70h
// CH = 02, Get HG/ Non-HG
// Return Status:
// CL = HG Mode
// 0 - HG Enabled
// 1 - HG Disabled
// AX = Return Status (function not supported if AL! = 5Fh):
// = 015Fh, Function supported but failed
// = 005Fh, Function supported and successful
//
if (mHgInfoProtocol->HgMode == HgModeMuxless) {
//
// HG Enable
//
CpuRegs->H.CL &= ~(0xFF);
CpuRegs->H.CL |= 0x00;
} else {
//
// HG Disabled
//
CpuRegs->H.CL &= ~(0xFF);
CpuRegs->H.CL |= 0x01;
}
//
// Return Function supported and successful
//
CpuRegs->X.AX &= ~(0xFFFF);
CpuRegs->X.AX |= 0x005F;
break;
default:
break;
}
}
/**
HG GPIO Read
@param[in] GpioSupport - HG GPIO Support; 0=Disable, 1=PCH Based, 2=I2C Based
@param[in] Gpio - GPIO active info[31] + GPIO Group[30:16] + GPIO Number[15:0].
**/
BOOLEAN
EFIAPI
SmmGpioRead (
IN UINT8 GpioSupport,
IN UINT32 Gpio
)
{
EFI_STATUS Status;
UINT32 InputVal;
BOOLEAN Active;
if (GpioSupport == 1) {
Active = (BOOLEAN) (Gpio >> 31);
Gpio &= 0x7FFFFFFF;
// PCH based GPIO
Status = GpioGetInputValue (Gpio, &InputVal);
if (Status != EFI_SUCCESS) {
// return;
}
InputVal &= BIT0;
if (Active == 0) {
InputVal = ~InputVal;
}
return (BOOLEAN)(InputVal & 0x1);
}
return 0;
}
/**
HG GPIO Write
@param[in] GpioSupport - HG GPIO Support; 0=Disable, 1=PCH Based, 2=I2C Based
@param[in] Gpio - GPIO active info[31] + GPIO Group[30:16] + GPIO Number[15:0].
@param[in] Level - Write HG GPIO value (0/1)
**/
VOID
EFIAPI
SmmGpioWrite (
IN UINT8 GpioSupport,
IN UINT32 Gpio,
IN BOOLEAN Level
)
{
EFI_STATUS Status;
BOOLEAN Active;
Active = (BOOLEAN) (Gpio >> 31);
Gpio &= 0x7FFFFFFF;
if (Active == 0) {
Level = (~Level) & 0x1;
}
if (GpioSupport == 1) {
// PCH based GPIO
Status = GpioSetOutputValue (Gpio, (UINT32)Level);
if (Status != EFI_SUCCESS) {
return;
}
}
}
EFI_STATUS
EFIAPI
InitialNvidiaOptimusIOTrap (
VOID
)
{
EFI_STATUS Status;
Status = mSmst->SmmLocateProtocol(&gEfiSmmIoTrapDispatch2ProtocolGuid, NULL, &mSmmIoTrapDispatch);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "Locate IoTrap Protocol returned %r\n", Status));
return Status;
}
DEBUG ((DEBUG_ERROR, "Installing EcCsIoTrapHandler Handler \n"));
mIchIoTrapContext64.Type = ReadTrap;
mIchIoTrapContext64.Length = 1;
mIchIoTrapContext64.Address = 0x3CA;
Status = mSmmIoTrapDispatch->Register (
mSmmIoTrapDispatch,
(EFI_SMM_HANDLER_ENTRY_POINT2)NvidiaOptimusHandler,
&mIchIoTrapContext64,
&mIchIoTrapHandle64
);
DEBUG ((DEBUG_ERROR, "IoTrap register Status: %x\n", Status));
//
// Disable IO Trap Handler
//
Status = mSmst->SmmLocateProtocol (&gPchSmmIoTrapControlGuid, NULL, &mPchSmmIoTrapControl);
if (EFI_ERROR (Status)) {
ASSERT_EFI_ERROR (Status);
return Status;
}
Status = mPchSmmIoTrapControl->Pause (
mPchSmmIoTrapControl,
mIchIoTrapHandle64
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "mPchSmmIoTrapControl->Pause returned %r\n", Status));
return Status;
}
return EFI_SUCCESS;
}
VOID
NvidiaOptimusHandler (
IN EFI_HANDLE DispatchHandle,
IN CONST VOID *DispatchContext,
IN OUT VOID *CommBuffer,
IN OUT UINTN *CommBufferSize
)
{
UINTN Peg0BaseAddress;
Peg0BaseAddress = MmPciBase (SA_PEG_BUS_NUM, SA_PEG0_DEV_NUM, SA_PEG0_FUN_NUM);
//
// a. Enable PEX Link between root port and GPU ( it should be Link Disable bit in Link Control register of root port )
//
MmioOr16 (Peg0BaseAddress + R_SA_PEG_LCTL_OFFSET, BIT4);
//
// b. Toggle GPU_EVENT(ENVT) to LOW ( GPU_EVENT is a PCH GPIO assigned by H/W. it might be different from project to project
//
GpioSetOutputValue (FixedPcdGet32 (PcdNvidiaGPUEvent), V_GPIO_PCR_TX_STATE_LOW);
//
// c. Wait 20ms
//
MicroSecondDelay (20);
//
// d. Toggle GPU_EVENT(ENVT) to HIGH
//
GpioSetOutputValue (FixedPcdGet32 (PcdNvidiaGPUEvent), V_GPIO_PCR_TX_STATE_HIGH);
//
//
// e. Touch the following registers on root port to enable PEX Link Bundle power. This is required for SKL-H and KBL-H for GC6 exit.
// Root port PCIE register 0xC20[5:4] = 00b
// Root port PCIE Register 0xC38[3] = 00b
MmioAnd8(Peg0BaseAddress + R_SA_PEG_AFEOVR_OFFSET, (UINT8) ~(BIT5 | BIT4));
MmioAnd8(Peg0BaseAddress + R_SA_PEG_CMNSPARE_OFFSET, (UINT8) ~(BIT3));
}