Ryzen/AmiModulePkg/KbcEmulation/KbcUhci.c
2022-12-23 15:14:44 +08:00

492 lines
13 KiB
C

//**********************************************************************
//**********************************************************************
//** **
//** (C)Copyright 1985-2018, American Megatrends, Inc. **
//** **
//** All Rights Reserved. **
//** **
//** 5555 Oakbrook Parkway, Suite 200, Norcross, GA 30093 **
//** **
//** Phone: (770)-246-8600 **
//** **
//**********************************************************************
//**********************************************************************
/** @file KbcUhci.c
Handles the SMI events for port 60/64
**/
//---------------------------------------------------------------------------
#include "Token.h"
#include "KbcEmul.h"
#include "KbcUhci.h"
#include "KbcEmulLib.h"
#include "Kbc.h"
//---------------------------------------------------------------------------
extern KBC* gVirtualKBC;
extern BOOLEAN KbcPresent;
#if ICH10_WORKAROUND
EFI_STATUS EnableAllUhciController();
EFI_STATUS RestoreUhciControllerStatus();
#endif
//Carries the LEGKEY status information saved before enable/disable trapping from outside trap handler is
//performed; The saving of the status allows recovering legacy I/O accesss event in case when multiple
//sources are serviced at signle SMI# ( legacy I/O and USB keyboard interupt, for example)
static UINT16 SavedLegcyStatus = 0;
static UINT16 UhciTrapByMask = UHCI_TRAPBY_MASK_WITHOUTKBC;
static UINT16 UhciTrapEnMask = UHCI_TRAPEN_MASK_WITHOUTKBC;
static BOOLEAN gInTrapHandler = FALSE;
UHCI_EMUL_DEVICE Uhci_Hc_Array[] = {
#if defined (UHCI_EMUL_PCI_DEVICES)
UHCI_EMUL_PCI_DEVICES,
#endif
#if defined(INTEL_LEGACY_USB_KBC_EMUL_CONTROL_REG_PCI_DEVICES)
INTEL_LEGACY_USB_KBC_EMUL_CONTROL_REG_PCI_DEVICES,
#endif
{ 0xFFFF, 0xFF, 0xFF, 0xFF, 0xFF }
};
UINTN Uhci_Hc_Array_Size = sizeof(Uhci_Hc_Array) / sizeof(Uhci_Hc_Array[0]);
VOID DisableLegKeyRegs ();
VOID trap64w ();
VOID trap60w ();
VOID trap64r ();
VOID trap60r ();
TRAPDISPATCH trap_dispatcher_WithoutKBC[] = {
{ UHCI_TRAPBY64W, trap64w },
{ UHCI_TRAPBY64R, trap64r },
{ UHCI_TRAPBY60W, trap60w },
{ UHCI_TRAPBY60R, trap60r },
};
TRAPDISPATCH trap_dispatcher_WithKBC[] = {
{ UHCI_TRAPBY64W, trap64w },
{ UHCI_TRAPBY60W, trap60w },
{ UHCI_TRAPBY60R, trap60r },
};
static TRAPDISPATCH *trap_dispatcher;
static UINT16 trap_dispatcher_size;
VOID
EFIAPI
Trap6064_Handler (
IN EFI_HANDLE DispatchHandle,
IN EFI_SMM_USB_REGISTER_CONTEXT * DispatchContext
);
/**
Validate the UHCI controller.
@param Uhc
@retval Boolean
**/
BOOLEAN
ValidateUhc (
UHCI_EMUL_DEVICE *Uhc
)
{
UINT32 Data32 = 0;
// End of the Device List.
if(Uhc->BusDevFunc == 0xFFFF) {
return FALSE;
}
if (ReadPCIConfig (Uhc->BusDevFunc, 0) == 0xFFFFFFFF) {
return FALSE;
}
Data32 = ReadPCIConfig (Uhc->BusDevFunc, 8) >> 8;
if (Data32 != (*((UINT32*)&Uhc->InterfaceType) & 0x00FFFFFF)) {
return FALSE;
}
return TRUE;
}
/**
Enable the SMI for port 6064 access.
@param None
@retval VOID
**/
VOID
Smm_Register ()
{
EFI_SMM_USB_REGISTER_CONTEXT context;
EFI_SMM_USB_DISPATCH2_PROTOCOL* pDispatch;
EFI_HANDLE hDisp;
EFI_STATUS Status;
static FULL_USB_DEVICE_PATH hc_dp = USB1_1_DEVICE_PATH;
UINTN UhciCount;
trap_dispatcher_size = sizeof(trap_dispatcher_WithoutKBC)/sizeof(trap_dispatcher_WithoutKBC[0]);
trap_dispatcher = trap_dispatcher_WithoutKBC;
if(KbcPresent) {
UhciTrapByMask = UHCI_TRAPBY_MASK_WITHKBC;
UhciTrapEnMask = UHCI_TRAPEN_MASK_WITHKBC;
trap_dispatcher_size = sizeof(trap_dispatcher_WithKBC)/sizeof(trap_dispatcher_WithKBC[0]);
trap_dispatcher = trap_dispatcher_WithKBC;
}
//
// Register the USB HW SMI handler
//
Status = gSmst1->SmmLocateProtocol(
&gEfiSmmUsbDispatch2ProtocolGuid,
NULL,
&pDispatch);
ASSERT_EFI_ERROR(Status);
//
// SMI registration routines will install the handlers, set enable bit
// and clear status in PM IO space.
//
for( UhciCount = 0; UhciCount < Uhci_Hc_Array_Size; ++UhciCount ){
//
//Validate the PCI device before regsiter the SMI callback.
//
if (!ValidateUhc(&Uhci_Hc_Array[UhciCount])) {
continue;
}
hc_dp.pci.Device=(Uhci_Hc_Array[UhciCount].BusDevFunc >> 3);
hc_dp.pci.Function=(Uhci_Hc_Array[UhciCount].BusDevFunc & 07);
context.Type = UsbLegacy;
context.Device = (EFI_DEVICE_PATH_PROTOCOL *)&hc_dp;
Status = pDispatch->Register(pDispatch,(EFI_SMM_HANDLER_ENTRY_POINT2)Trap6064_Handler, &context, &hDisp);
ASSERT_EFI_ERROR(Status);
}
SavedLegcyStatus = 0;
//
//The SMI source for the port6064 is disabled. it will be enabled when we are in legacy enviorment. EFI enviorment Emulation is
//Disabled.
//
DisableLegKeyRegs();
}
/**
Write the Value in to all the UHCI controller Legacy Regsiters.
@param Value
@retval VOID
**/
VOID
WriteLegKeyReg (
UINT16 Value
)
{
UINTN UhciCount;
for( UhciCount = 0; UhciCount < Uhci_Hc_Array_Size; ++UhciCount ){
WordWritePCIConfig( Uhci_Hc_Array[UhciCount].BusDevFunc,
Uhci_Hc_Array[UhciCount].LegacyRegOffset, Value);
}
}
/**
Clear the Port6064 SMI Status Reg
@param Value
@retval VOID
**/
VOID
ClearLegKeyStatusReg (
UINT16 Value
)
{
#if ICH10_WORKAROUND
//
// Enable all the UCHI controller
// In ICH10 chipset, we need to clear the Port 6064 SMI status in disabled controller
// also. Otherwise it's keep on generating SMI
//
EnableAllUhciController();
#endif
WriteLegKeyReg(Value);
#if ICH10_WORKAROUND
//
// Restore the UCHI controller's in RCBA Reg
//
RestoreUhciControllerStatus();
#endif
}
/**
Disable the port6064 SMI source based on the Trap enable mask.
@param None
@retval VOID
**/
VOID
DisableLegKeyRegs ()
{
UINT16 legkeyStatus;
UINTN UhciCount;
for( UhciCount = 0; UhciCount < Uhci_Hc_Array_Size; ++UhciCount ){
//
// Skip for Invalid Pci Devices
//
if (!ValidateUhc(&Uhci_Hc_Array[UhciCount])) {
continue;
}
//
//Read the Legacy Keyboard and mouse control register
//
legkeyStatus =(UINT16) ReadPCIConfig(Uhci_Hc_Array[UhciCount].BusDevFunc,
Uhci_Hc_Array[UhciCount].LegacyRegOffset ); // Read the status
//
//Clear the enable bits
//
WordWritePCIConfig( Uhci_Hc_Array[UhciCount].BusDevFunc,
Uhci_Hc_Array[UhciCount].LegacyRegOffset, legkeyStatus | UhciTrapByMask);
//
//Disable the trap
//
WordWritePCIConfig( Uhci_Hc_Array[UhciCount].BusDevFunc,
Uhci_Hc_Array[UhciCount].LegacyRegOffset, legkeyStatus & (~UhciTrapEnMask));
}
return;
}
/**
SMI handler to handle the 64write, 64read, 60 write and 60 read SMI.
@param DispatchHandle - EFI Handle
@param DispatchContext - Pointer to the EFI_SMM_SW_DISPATCH_CONTEXT
@retval VOID
**/
VOID
EFIAPI
Trap6064_Handler (
IN EFI_HANDLE DispatchHandle,
IN EFI_SMM_USB_REGISTER_CONTEXT *DispatchContext
)
{
UINT16 LegcyKeyStatus;
UINT32 TrapFunCount;
UINTN UhciCount;
for( UhciCount = 0; UhciCount < Uhci_Hc_Array_Size; ++UhciCount ){
if (!ValidateUhc(&Uhci_Hc_Array[UhciCount])) {
continue;
}
//
// Read UHCI_PCI_LEGKEY
//
LegcyKeyStatus = (UINT16) ReadPCIConfig( Uhci_Hc_Array[UhciCount].BusDevFunc,
Uhci_Hc_Array[UhciCount].LegacyRegOffset );
SavedLegcyStatus |= LegcyKeyStatus;
if(SavedLegcyStatus==0xFFFF) {
SavedLegcyStatus=0;
continue;
}
if( (SavedLegcyStatus & UhciTrapByMask) != 0 ) {
break;
}
SavedLegcyStatus=0;
}
if(UhciCount >=Uhci_Hc_Array_Size) {
return;
}
//
// External application has chaned UHCI trap enable configuration, exit
//
if ((LegcyKeyStatus & UhciTrapEnMask) != UhciTrapEnMask) {
ClearLegKeyStatusReg( LegcyKeyStatus | UhciTrapByMask);
return;
}
//
// Set the variable that we are processing the Trap
//
gInTrapHandler = TRUE;
//
// Clear the status
//
ClearLegKeyStatusReg( LegcyKeyStatus | UhciTrapByMask);
//
// Disable Traps ( in responce to i/o handler can try to access a real KBC)
//
WriteLegKeyReg( (LegcyKeyStatus & ~UhciTrapEnMask));
//
// Dispatch the interrupt depending on saved status
//
for( TrapFunCount = 0; TrapFunCount < trap_dispatcher_size; ++TrapFunCount ){
if( (LegcyKeyStatus & trap_dispatcher[TrapFunCount].status_bit ) != 0){
(*trap_dispatcher[TrapFunCount].trap_function)();
}
}
//
// Clear the Status
//
ClearLegKeyStatusReg( LegcyKeyStatus | UhciTrapByMask);
//
// Enable Traps
//
WriteLegKeyReg( LegcyKeyStatus | UhciTrapEnMask);
SavedLegcyStatus = 0;
gInTrapHandler = FALSE;
return;
}
/**
Check if trap status is set in UHCI HC.
@param None
@retval BOOLEAN
**/
BOOLEAN
Uhci_HasTrapStatus ()
{
UINT16 LegcyKeyStatus = 0;
UINTN UhciCount;
for( UhciCount = 0; UhciCount < Uhci_Hc_Array_Size; ++UhciCount ){
if (!ValidateUhc(&Uhci_Hc_Array[UhciCount])) {
continue;
}
LegcyKeyStatus |= (UINT16) ReadPCIConfig(Uhci_Hc_Array[UhciCount].BusDevFunc,
Uhci_Hc_Array[UhciCount].LegacyRegOffset );
}
return ((LegcyKeyStatus & UhciTrapEnMask) != 0 );
}
/**
Enable/Disable traping in UHCI HC.
@param TrapEnable
@retval BOOLEAN
**/
BOOLEAN
Uhci_TrapEnable (
BOOLEAN TrapEnable
)
{
UINT16 LegcyKeyStatus;
UINTN UhciCount;
if(gInTrapHandler)
return FALSE;
for( UhciCount = 0; UhciCount < Uhci_Hc_Array_Size; ++UhciCount ){
if (!ValidateUhc(&Uhci_Hc_Array[UhciCount])) {
continue;
}
LegcyKeyStatus =(UINT16) ReadPCIConfig( Uhci_Hc_Array[UhciCount].BusDevFunc ,
Uhci_Hc_Array[UhciCount].LegacyRegOffset );
//
//Record first time that trapping is disabled. Record only trap status bits handled by Trap handler
//
if( (LegcyKeyStatus & UhciTrapEnMask)== UhciTrapEnMask &&
( LegcyKeyStatus & UhciTrapByMask) != 0 ){
//If legacy I/O caused SMI# and this is first time we are in uhci_trapEnable
//then trapping in LEGKEY reg must have been enabled and one trap status is set.
//Any port 60/64 operation within SMI# must be wrapped into
//enable/ disable&clear status. So subsequent trapEnable will not produce any
//trap statuses
SavedLegcyStatus |= LegcyKeyStatus;
}
if(TrapEnable){
//
//clear status(es) that might be asserted by our handlers
//
ClearLegKeyStatusReg( LegcyKeyStatus | UhciTrapByMask);
//
// Enable Traps
//
WriteLegKeyReg( LegcyKeyStatus | UhciTrapEnMask);
} else {
//
//clear status(es) that might be asserted by our handlers
//
ClearLegKeyStatusReg( LegcyKeyStatus | UhciTrapByMask);
//
// Disable Traps
//
WriteLegKeyReg( (LegcyKeyStatus & ~UhciTrapEnMask) );
}
}
return TRUE;
}
//**********************************************************************
//**********************************************************************
//** **
//** (C)Copyright 1985-2018, American Megatrends, Inc. **
//** **
//** All Rights Reserved. **
//** **
//** 5555 Oakbrook Parkway, Suite 200, Norcross, GA 30093 **
//** **
//** Phone: (770)-246-8600 **
//** **
//**********************************************************************
//**********************************************************************