alder_lake_bios/Intel/AlderLake/AlderLakeChipsetPkg/UsbLegacyControlSmm/UsbLegacyControl.c

319 lines
9.6 KiB
C

/** @file
Usb legacy keyboard/mouse control driver
;******************************************************************************
;* Copyright (c) 2014 - 2015, Insyde Software Corporation. 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 <UsbLegacyControl.h>
UINT32 mHidOutputData = 0;
UINT32 mHidStatusData = 0;
UINT16 mSoftIrqGeneratePort;
EFI_GUID mUsbLegacyControlProtocolGuid = USB_LEGACY_CONTROL_PROTOCOL_GUID;
//
// The device path used for SMI registration. Use dev:0x1f fun:0x00 to it
//
struct {
ACPI_HID_DEVICE_PATH Acpi;
PCI_DEVICE_PATH Pci;
EFI_DEVICE_PATH_PROTOCOL End;
} mDevicePath = {
{ ACPI_DEVICE_PATH, ACPI_DP, (UINT8)(sizeof(ACPI_HID_DEVICE_PATH)), (UINT8)((sizeof(ACPI_HID_DEVICE_PATH)) >> 8), EISA_PNP_ID(0x0A03), 0 },
{ HARDWARE_DEVICE_PATH, HW_PCI_DP, (UINT8)(sizeof(PCI_DEVICE_PATH)), (UINT8)((sizeof(PCI_DEVICE_PATH)) >> 8), 0x00, 0x1f },
{ END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, END_DEVICE_PATH_LENGTH, 0 }
};
//
// The body of UsbLegacyControlProtocol used for protocol installation
//
typedef struct {
USB_LEGACY_CONTROL_PROTOCOL UsbLegacyControlProtocol;
BOOLEAN SmiHandlerInstalled;
} USB_LEGACY_CONTROL_PRIVATE;
USB_LEGACY_CONTROL_PRIVATE mPrivate = {
{ UsbLegacyControl, NULL, NULL, FALSE },
FALSE
};
EFI_SMM_SYSTEM_TABLE2 *mSmst;
/**
Entry point for EFI drivers.
@param ImageHandle EFI_HANDLE
@param SystemTable EFI_SYSTEM_TABLE
@retval EFI_SUCCESS Success
@retval Others Fail
**/
EFI_STATUS
EFIAPI
UsbLegacyControlDriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
EFI_HANDLE Handle;
UINT32 TcoBase;
EFI_SMM_USB_DISPATCH2_PROTOCOL *UsbDispatch;
EFI_SMM_USB_REGISTER_CONTEXT UsbContext;
EFI_SMM_BASE2_PROTOCOL *SmmBase;
BOOLEAN InSmm;
//
// if port 64 is "0xff", it's legacyFree
//
if ( IoRead8 (0x64) != 0xFF) {
return EFI_UNSUPPORTED ;
}
SmmBase = NULL;
Status = gBS->LocateProtocol (&gEfiSmmBase2ProtocolGuid, NULL, (VOID **)&SmmBase);
InSmm = FALSE;
if (!EFI_ERROR (Status)) {
SmmBase->InSmm (SmmBase, &InSmm);
}
//
// Register USB legacy SMI handler
//
if (InSmm){
Status = SmmBase->GetSmstLocation (SmmBase, &mSmst);
if (EFI_ERROR (Status)) {
return Status;
}
Status = mSmst->SmmLocateProtocol (
&gEfiSmmUsbDispatch2ProtocolGuid,
NULL,
(VOID **)&UsbDispatch
);
if (EFI_ERROR(Status)) {
return EFI_UNSUPPORTED;
}
UsbContext.Type = UsbLegacy;
UsbContext.Device = (EFI_DEVICE_PATH_PROTOCOL*)&mDevicePath;
Status = UsbDispatch->Register(
UsbDispatch,
(EFI_SMM_HANDLER_ENTRY_POINT2)UsbLegacyControlSmiHandler,
&UsbContext,
&Handle
);
if (EFI_ERROR(Status)) {
return EFI_UNSUPPORTED;
}
//
// This is SMM instance
//
mPrivate.UsbLegacyControlProtocol.InSmm = TRUE;
}
//
// Install USB_LEGACY_CONTROL_PROTOCOL
//
Handle = NULL;
Status = gBS->InstallProtocolInterface (
&Handle,
&mUsbLegacyControlProtocolGuid,
EFI_NATIVE_INTERFACE,
&mPrivate.UsbLegacyControlProtocol
);
//
// Setup the Soft IRQ generate port
//
TcoBase = PciRead32 (PCI_LIB_ADDRESS (SMBUS_BUS, SMBUS_DEV, SMBUS_FUN, R_SMBUS_TCOBASE)) & B_SMBUS_TCOBASE_MASK;
mSoftIrqGeneratePort = (UINT16)(TcoBase + R_TCO_SW_IRQ_GEN);
return Status;
}
/**
Main routine for the USB legacy control
@param Command USB_LEGACY_CONTROL_SETUP_EMULATION
USB_LEGACY_CONTROL_GET_KBC_DATA_POINTER
USB_LEGACY_CONTROL_GET_KBC_STATUS_POINTER
USB_LEGACY_CONTROL_GENERATE_IRQ
@param Param The parameter for the command
@retval EFI_SUCCESS Success
@retval Others Fail
**/
EFI_STATUS
EFIAPI
UsbLegacyControl (
IN UINTN Command,
IN VOID *Param
)
{
UINT8 Data8;
UINT32 Data32;
switch (Command) {
case USB_LEGACY_CONTROL_SETUP_EMULATION:
//
// Used for emulation on/off
//
Data32 = PciRead32 (PCI_LIB_ADDRESS (LPC_BUS, LPC_DEV, LPC_FUN, R_ULKMC));
//
// Always turn off USBSMIEN && USBPIRQEN
//
Data32 &= ~(B_ULKMC_USBSMIEN | B_ULKMC_USBPIRQEN);
if (!Param) {
//
// Turn off the trap SMI
//
Data32 &= ~(B_ULKMC_TRAP_60R | B_ULKMC_TRAP_60W | B_ULKMC_TRAP_64R | B_ULKMC_TRAP_64W);
} else {
//
// Turn on the trap SMI
//
Data32 |= (B_ULKMC_TRAP_60R | B_ULKMC_TRAP_60W | B_ULKMC_TRAP_64R | B_ULKMC_TRAP_64W);
}
PciWrite32 (PCI_LIB_ADDRESS (LPC_BUS, LPC_DEV, LPC_FUN, R_ULKMC), Data32);
//
// Inspect the SMI handler data to see is it need to update
//
if (Param && !mPrivate.SmiHandlerInstalled && mPrivate.UsbLegacyControlProtocol.SmiHandler) {
mPrivate.SmiHandlerInstalled = TRUE;
//
// Trap SMI if now in BS to pass the SMI handler related data to SMM instance
//
if (mPrivate.UsbLegacyControlProtocol.InSmm == FALSE) {
//
// Trigger SMI through 64 port read
//
IoRead8 (0x64);
}
}
break;
case USB_LEGACY_CONTROL_GET_KBC_DATA_POINTER:
//
// Get the "pointer" of KBC data (port 0x60)
//
*(UINT32**)Param = &mHidOutputData;
break;
case USB_LEGACY_CONTROL_GET_KBC_STATUS_POINTER:
//
// Get the "pointer" of KBC status (port 0x64)
//
*(UINT32**)Param = &mHidStatusData;
break;
case USB_LEGACY_CONTROL_GENERATE_IRQ:
//
// IRQ 1/12 generating
//
Data8 = IoRead8 (mSoftIrqGeneratePort);
//
// Make signal as low
//
Data8 &= ~(0x01 << ((mHidStatusData & 0x20) ? IRQ12 : IRQ1));
IoWrite8 (mSoftIrqGeneratePort, Data8);
//
// make signal as high
//
IoWrite8 (mSoftIrqGeneratePort, 0x03);
//
// We don't need the output data right now
//
*(BOOLEAN*)Param = FALSE;
break;
case USB_LEGACY_CONTROL_GET_CAPABILITY:
default:
break;
}
return EFI_SUCCESS;
}
/**
The UsbLegacyControl SMI main handler
@param Handle
@param Context
@retval VOID
**/
VOID
UsbLegacyControlSmiHandler (
IN EFI_HANDLE Handle,
IN CONST EFI_SMM_USB_REGISTER_CONTEXT *Context
)
{
EFI_STATUS Status;
UINT32 Data32;
UINT32 TrapEvent;
UINTN HandleCount;
EFI_HANDLE *HandleBuffer;
UINTN Index;
USB_LEGACY_CONTROL_PRIVATE *Private;
EFI_TPL OldTpl;
TrapEvent = 0;
Data32 = PciRead32 (PCI_LIB_ADDRESS (LPC_BUS, LPC_DEV, LPC_FUN, R_ULKMC));
if (Data32 & B_ULKMC_TRAP_STATUS) {
//
// Clear the status
//
PciWrite32 (PCI_LIB_ADDRESS (LPC_BUS, LPC_DEV, LPC_FUN, R_ULKMC), Data32);
TrapEvent = (Data32 & B_ULKMC_TRAP_STATUS) >> N_ULKMC_TRAP_STATUS;
}
//
// Call sub handler
//
if (mPrivate.UsbLegacyControlProtocol.SmiHandler) {
mPrivate.UsbLegacyControlProtocol.SmiHandler (TrapEvent, mPrivate.UsbLegacyControlProtocol.SmiContext);
}
//
// Check is the sub handler "SmiHandler" been installed. Must launched in POST
//
if (!mPrivate.SmiHandlerInstalled) {
OldTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
gBS->RestoreTPL (OldTpl);
if (OldTpl == TPL_HIGH_LEVEL) {
gBS->RestoreTPL (TPL_CALLBACK);
}
mPrivate.SmiHandlerInstalled = TRUE;
Status = gBS->LocateHandleBuffer (
ByProtocol,
&mUsbLegacyControlProtocolGuid,
NULL,
&HandleCount,
&HandleBuffer
);
if (!EFI_ERROR (Status)) {
for (Index = 0; Index < HandleCount; Index++) {
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&mUsbLegacyControlProtocolGuid,
(VOID**)&Private
);
if (!EFI_ERROR (Status) && Private->UsbLegacyControlProtocol.InSmm == FALSE) {
//
// Get the SMI handler related data from BS driver
//
mPrivate.UsbLegacyControlProtocol.SmiHandler = Private->UsbLegacyControlProtocol.SmiHandler;
mPrivate.UsbLegacyControlProtocol.SmiContext = Private->UsbLegacyControlProtocol.SmiContext;
break;
}
}
}
gBS->FreePool (HandleBuffer);
if (OldTpl == TPL_HIGH_LEVEL) {
gBS->RaiseTPL (TPL_HIGH_LEVEL);
}
}
}