477 lines
15 KiB
C
477 lines
15 KiB
C
/** @file
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2014, 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 "XhciSmiDispatcher.h"
|
|
//
|
|
// The device path used for SMI registration.
|
|
// This example sets the XHCI device plugged behind bridge BRIDGE_DEV_FOR_XHCI/BRIDGE_FUN_FOR_XHCI
|
|
// Please based on platform setting to change the device path for XHCI device
|
|
//
|
|
struct {
|
|
ACPI_HID_DEVICE_PATH Acpi;
|
|
PCI_DEVICE_PATH RootBridge;
|
|
PCI_DEVICE_PATH Pci;
|
|
EFI_DEVICE_PATH_PROTOCOL End;
|
|
} mXhciDevicePath = {
|
|
{ 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), 0, 0 },
|
|
{ HARDWARE_DEVICE_PATH, HW_PCI_DP, (UINT8)(sizeof(PCI_DEVICE_PATH)), (UINT8)((sizeof(PCI_DEVICE_PATH)) >> 8), XHCI_FUN, XHCI_DEV },
|
|
{ END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, END_DEVICE_PATH_LENGTH, 0 }
|
|
};
|
|
|
|
//#545659 SKL-U/SKL-Y PCH EDS
|
|
//Below are the PCH GPIOs that can be routed to generate SMI# or NMI:
|
|
//!E GPP_B14, GPP_B20, GPP_B23
|
|
//!E GPP_C[23:22]
|
|
//!E GPP_D[4:0]
|
|
//!E GPP_E[8:0], GPP_E[16:13]
|
|
UINT32 mPchGpioSmiPad[] = {
|
|
GPIO_SKL_LP_GPP_B14,
|
|
GPIO_SKL_LP_GPP_B20,
|
|
GPIO_SKL_LP_GPP_B23,
|
|
GPIO_SKL_LP_GPP_C22,
|
|
GPIO_SKL_LP_GPP_C23,
|
|
GPIO_SKL_LP_GPP_D0,
|
|
GPIO_SKL_LP_GPP_D1,
|
|
GPIO_SKL_LP_GPP_D2,
|
|
GPIO_SKL_LP_GPP_D3,
|
|
GPIO_SKL_LP_GPP_D4,
|
|
GPIO_SKL_LP_GPP_E0,
|
|
GPIO_SKL_LP_GPP_E1,
|
|
GPIO_SKL_LP_GPP_E2,
|
|
GPIO_SKL_LP_GPP_E3,
|
|
GPIO_SKL_LP_GPP_E4,
|
|
GPIO_SKL_LP_GPP_E5,
|
|
GPIO_SKL_LP_GPP_E6,
|
|
GPIO_SKL_LP_GPP_E7,
|
|
GPIO_SKL_LP_GPP_E8,
|
|
GPIO_SKL_LP_GPP_E13,
|
|
GPIO_SKL_LP_GPP_E14,
|
|
GPIO_SKL_LP_GPP_E15,
|
|
GPIO_SKL_LP_GPP_E16
|
|
};
|
|
|
|
EFI_SMM_USB_DISPATCH2_PROTOCOL mStockUsbDispatch;
|
|
DATABASE_RECORD *mRecord;
|
|
UINT32 mXhciCmd;
|
|
UINT32 mXhciPwr;
|
|
UINT32 mXhciXecp;
|
|
UINT32 mXhciPcdSetting;
|
|
/**
|
|
|
|
Entry point for EFI drivers.
|
|
|
|
@param ImageHandle EFI_HANDLE
|
|
@param SystemTable EFI_SYSTEM_TABLE
|
|
|
|
@retval EFI_SUCCESS Success
|
|
@retval EFI_DEVICE_ERROR Fail
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
XhciSmiDispatcherDriverEntryPoint (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_SMM_USB_DISPATCH2_PROTOCOL *UsbDispatch;
|
|
//
|
|
// Checking the PCD to
|
|
//
|
|
mXhciPcdSetting = PcdGet32 (PcdXhciAddonCardSetting);
|
|
if (mXhciPcdSetting == 0) {
|
|
//
|
|
// Quit if no PCD specify
|
|
//
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
//
|
|
// Get the stock SmmUsbDispatcher protocol
|
|
//
|
|
Status = gSmst->SmmLocateProtocol (
|
|
&gEfiSmmUsbDispatch2ProtocolGuid,
|
|
NULL,
|
|
(VOID **)&UsbDispatch
|
|
);
|
|
if (EFI_ERROR(Status)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
//
|
|
// Patch the SmmUsbDispatcher protocol
|
|
//
|
|
CopyMem (&mStockUsbDispatch, UsbDispatch, sizeof (EFI_SMM_USB_DISPATCH2_PROTOCOL));
|
|
UsbDispatch->Register = XhciSmiRegister;
|
|
UsbDispatch->UnRegister = XhciSmiUnregister;
|
|
//
|
|
// Setup internal variables
|
|
//
|
|
mRecord = NULL;
|
|
mXhciDevicePath.RootBridge.Function = (UINT8)BRIDGE_FUN_FOR_XHCI;
|
|
mXhciDevicePath.RootBridge.Device = (UINT8)BRIDGE_DEV_FOR_XHCI;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
|
|
Provides the parent dispatch service for the USB SMI source generator.
|
|
|
|
This service registers a function (DispatchFunction) which will be called when the USB-
|
|
related SMI specified by RegisterContext has occurred. On return, DispatchHandle
|
|
contains a unique handle which may be used later to unregister the function using UnRegister().
|
|
The DispatchFunction will be called with Context set to the same value as was passed into
|
|
this function in RegisterContext and with CommBuffer containing NULL and
|
|
CommBufferSize containing zero.
|
|
|
|
@param[in] This Pointer to the EFI_SMM_USB_DISPATCH2_PROTOCOL instance.
|
|
@param[in] DispatchFunction Function to register for handler when a USB-related SMI occurs.
|
|
@param[in] RegisterContext Pointer to the dispatch function's context.
|
|
The caller fills this context in before calling
|
|
the register function to indicate to the register
|
|
function the USB SMI types for which the dispatch
|
|
function should be invoked.
|
|
@param[out] DispatchHandle Handle generated by the dispatcher to track the function instance.
|
|
|
|
@retval EFI_SUCCESS The dispatch function has been successfully
|
|
registered and the SMI source has been enabled.
|
|
@retval EFI_DEVICE_ERROR The driver was unable to enable the SMI source.
|
|
@retval EFI_INVALID_PARAMETER RegisterContext is invalid. The USB SMI type
|
|
is not within valid range.
|
|
@retval EFI_OUT_OF_RESOURCES There is not enough memory (system or SMM) to manage this child.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
XhciSmiRegister (
|
|
IN CONST EFI_SMM_USB_DISPATCH2_PROTOCOL *This,
|
|
IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction,
|
|
IN CONST EFI_SMM_USB_REGISTER_CONTEXT *RegisterContext,
|
|
OUT EFI_HANDLE *DispatchHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
DATABASE_RECORD *Record;
|
|
EFI_HANDLE Handle;
|
|
UINT32 Index;
|
|
UINT32 Data;
|
|
UINT32 Bus;
|
|
UINT32 Bar;
|
|
//
|
|
// Check the owner
|
|
//
|
|
if (RegisterContext->Type == UsbLegacy && CompareMem(RegisterContext->Device, &mXhciDevicePath, sizeof(mXhciDevicePath)) == 0) {
|
|
//
|
|
// Create a XHCI SMI registration
|
|
//
|
|
Status = gSmst->SmmAllocatePool (
|
|
EfiRuntimeServicesData,
|
|
sizeof (DATABASE_RECORD),
|
|
(VOID **)&Record
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
Record->Signature = DATABASE_RECORD_SIGNATURE;
|
|
Record->Next = mRecord;
|
|
Record->DispatchFunction = DispatchFunction;
|
|
Record->RegisterContext = *RegisterContext;
|
|
if (!mRecord) {
|
|
//
|
|
// Setup the related registers and SMI root callback on first time setting
|
|
//
|
|
gSmst->SmiHandlerRegister (
|
|
XhciSmmCoreDispatcher,
|
|
NULL,
|
|
&Handle
|
|
);
|
|
|
|
//
|
|
// Setup GPIO INV for invert signal GPIO registers for XHCI SMIB pin
|
|
//
|
|
Status = GpioSetInputInversion (GPIO_PIN_FOR_XHCI_SMIB, INVERSE_GPIO_SIGNAL);
|
|
ASSERT_EFI_ERROR (Status);
|
|
//
|
|
// Make sure the GPIO_ROUT for XHCI SMIB pin is inactive before set the ALT_GP_SMI
|
|
//
|
|
Status = GpioClearGpiSmiSts(GPIO_PIN_FOR_XHCI_SMIB);
|
|
ASSERT_EFI_ERROR (Status);
|
|
//
|
|
// Setup ALT_GP_SMI for XHCI SMIB pin
|
|
//
|
|
Status = GpioEnableGpiSmi (GPIO_PIN_FOR_XHCI_SMIB ,TRUE);
|
|
ASSERT_EFI_ERROR (Status);
|
|
//
|
|
// Setting the record;
|
|
//
|
|
mRecord = Record;
|
|
if (SKIP_HAND_OFF_TO_BIOS_EVENT) {
|
|
Bus = (PciRead32 (PCI_LIB_ADDRESS (0, BRIDGE_DEV_FOR_XHCI, BRIDGE_FUN_FOR_XHCI, 0x18)) >> 8) & 0xff;
|
|
Bar = PciRead32 (PCI_LIB_ADDRESS (Bus, XHCI_DEV, XHCI_FUN, 0x10)) & ~0x0f;
|
|
mXhciCmd = PCI_LIB_ADDRESS (Bus, XHCI_DEV, XHCI_FUN, 0x04);
|
|
//
|
|
// Searching for USB Legacy Support Capability
|
|
//
|
|
mXhciXecp = 0;
|
|
Index = ((*(UINT32*)(UINTN)(Bar + 0x10) & 0xffff0000) >> 14);
|
|
while (((Data = *(UINT32*)(UINTN)(Bar + Index)) & 0xff) != 0x01 && (Data & 0xff00) != 0) {
|
|
Index += ((Data & 0xff00) >> (8 - 2));
|
|
}
|
|
if ((Data & 0xff) == 0x01) {
|
|
mXhciXecp = Bar + Index;
|
|
}
|
|
//
|
|
// Get the offset of Power Control Status Register
|
|
//
|
|
for (Index = 0x34, Data = 0; Index < 0x100;) {
|
|
Data = PciRead16 (PCI_LIB_ADDRESS (Bus, XHCI_DEV, XHCI_FUN, Index));
|
|
if (Index != 0x34) {
|
|
if ((Data & 0xff) == 0x01) {
|
|
mXhciPwr = PCI_LIB_ADDRESS (Bus, XHCI_DEV, XHCI_FUN, Index + 4);
|
|
break;
|
|
} else {
|
|
//
|
|
// The position of next ptr will be next of ID
|
|
//
|
|
Index = (Data & 0xff00) >> 8;
|
|
}
|
|
} else {
|
|
Index = Data & 0xff;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*DispatchHandle = (EFI_HANDLE)Record;
|
|
Status = EFI_SUCCESS;
|
|
} else {
|
|
//
|
|
// Transfer to stock function
|
|
//
|
|
Status = mStockUsbDispatch.Register (
|
|
This,
|
|
DispatchFunction,
|
|
RegisterContext,
|
|
DispatchHandle
|
|
);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
|
|
Unregisters a USB service.
|
|
|
|
This service removes the handler associated with DispatchHandle so that it will no longer be
|
|
called when the USB event occurs.
|
|
|
|
@param[in] This Pointer to the EFI_SMM_USB_DISPATCH2_PROTOCOL instance.
|
|
@param[in] DispatchHandle Handle of the service to remove.
|
|
|
|
@retval EFI_SUCCESS The dispatch function has been successfully
|
|
unregistered and the SMI source has been disabled
|
|
if there are no other registered child dispatch
|
|
functions for this SMI source.
|
|
@retval EFI_INVALID_PARAMETER The DispatchHandle was not valid.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
XhciSmiUnregister (
|
|
IN CONST EFI_SMM_USB_DISPATCH2_PROTOCOL *This,
|
|
IN EFI_HANDLE DispatchHandle
|
|
)
|
|
{
|
|
DATABASE_RECORD *Record;
|
|
DATABASE_RECORD *Prev;
|
|
EFI_STATUS Status;
|
|
|
|
if (DispatchHandle == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
Record = mRecord;
|
|
Prev = NULL;
|
|
while (Record != NULL) {
|
|
if (Record == DispatchHandle) {
|
|
//
|
|
// Don't free the record due to SmmBase is DXE driver and can't accessed during runtime
|
|
//
|
|
if (Prev) {
|
|
Prev->Next = Record->Next;
|
|
}
|
|
if (mRecord == DispatchHandle) {
|
|
//
|
|
// Turn off ALT_GP_SMI for XHCI SMIB pin
|
|
//
|
|
Status = GpioEnableGpiSmi (GPIO_PIN_FOR_XHCI_SMIB ,FALSE);
|
|
ASSERT_EFI_ERROR (Status);
|
|
//
|
|
// Remove the record;
|
|
//
|
|
mRecord = NULL;
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
Prev = Record;
|
|
Record = Record->Next;
|
|
}
|
|
//
|
|
// Transfer to stock function
|
|
//
|
|
return mStockUsbDispatch.UnRegister (
|
|
This,
|
|
DispatchHandle
|
|
);
|
|
}
|
|
|
|
/**
|
|
|
|
Stall in microsecond.
|
|
|
|
@param[in] MicroSecond Microsecond for stall
|
|
|
|
**/
|
|
STATIC
|
|
VOID
|
|
Stall (
|
|
IN UINTN MicroSecond
|
|
)
|
|
{
|
|
MicroSecondDelay(MicroSecond);
|
|
}
|
|
|
|
/**
|
|
|
|
Main entry point for an SMM handler dispatch or communicate-based callback.
|
|
|
|
@param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister().
|
|
@param[in] Context Points to an optional handler context which was specified when the
|
|
handler was registered.
|
|
@param[in,out] CommBuffer A pointer to a collection of data in memory that will
|
|
be conveyed from a non-SMM environment into an SMM environment.
|
|
@param[in,out] CommBufferSize The size of the CommBuffer.
|
|
|
|
@retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers
|
|
should still be called.
|
|
@retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should
|
|
still be called.
|
|
@retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still
|
|
be called.
|
|
@retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
XhciSmmCoreDispatcher (
|
|
IN EFI_HANDLE DispatchHandle,
|
|
IN CONST VOID *Context OPTIONAL,
|
|
IN OUT VOID *CommBuffer OPTIONAL,
|
|
IN OUT UINTN *CommBufferSize OPTIONAL
|
|
)
|
|
{
|
|
DATABASE_RECORD *Record;
|
|
GPIO_CONFIG GpioPadConfig;
|
|
UINT32 Data32;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Check GPIO SMI for XHCI SMIB pin
|
|
//
|
|
Status = GpioGetPadConfig (GPIO_PIN_FOR_XHCI_SMIB, &GpioPadConfig);
|
|
ASSERT_EFI_ERROR (Status);
|
|
Status = GpioGetGpiSmiSts (GPIO_PIN_FOR_XHCI_SMIB, &Data32);
|
|
ASSERT_EFI_ERROR (Status);
|
|
if (((GpioPadConfig.InterruptConfig & GpioIntSmi) == GpioIntSmi) && Data32) {
|
|
Record = mRecord;
|
|
while (Record != NULL) {
|
|
if (SKIP_HAND_OFF_TO_BIOS_EVENT) {
|
|
UINT16 CmdState = 0x06;
|
|
UINT16 PwrState = 0x00;
|
|
BOOLEAN QuitFlag = FALSE;
|
|
if (*(UINT32*)(UINTN)(mXhciXecp + 0) == 0xffffffff) {
|
|
//
|
|
// Setup and backup cmd and power state
|
|
//
|
|
CmdState = PciRead16 (mXhciCmd);
|
|
if ((CmdState & 0x06) != 0x06) {
|
|
PciWrite16 (mXhciCmd, CmdState | 0x06);
|
|
}
|
|
PwrState = PciRead16 (mXhciPwr);
|
|
if (PwrState & 0x03) {
|
|
PciWrite16 (mXhciPwr, PwrState & ~0x03);
|
|
}
|
|
//
|
|
// Stall 10ms to waiting for signal stable
|
|
//
|
|
Stall(10 * 1000);
|
|
}
|
|
if (*(UINT32*)(UINTN)(mXhciXecp + 4) & 0x20000000) {
|
|
//
|
|
// Xhci owner changed
|
|
//
|
|
if (!(*(UINT32*)(UINTN)(mXhciXecp + 0) & 0x01000000)) {
|
|
//
|
|
// OS to BIOS, just to setup the Xecp register and leave
|
|
//
|
|
*(volatile UINT32*)(UINTN)(mXhciXecp + 0) = *(volatile UINT32*)(UINTN)(mXhciXecp + 0) | 0x00010000;
|
|
*(volatile UINT32*)(UINTN)(mXhciXecp + 4) = *(volatile UINT32*)(UINTN)(mXhciXecp + 4) | 0x20000000;
|
|
//
|
|
// Stall 1ms to waiting for signal stable
|
|
//
|
|
Stall(1000);
|
|
QuitFlag = TRUE;
|
|
}
|
|
}
|
|
if ((CmdState & 0x06) != 0x06) {
|
|
//
|
|
// Restore the cmd
|
|
//
|
|
PciWrite16 (mXhciCmd, CmdState);
|
|
//
|
|
// Stall 10ms to waiting for signal stable
|
|
//
|
|
Stall(10 * 1000);
|
|
}
|
|
if (PwrState & 0x03) {
|
|
//
|
|
// Restore the power state
|
|
//
|
|
PciWrite16 (mXhciPwr, PwrState);
|
|
//
|
|
// Stall 10ms to waiting for signal stable
|
|
//
|
|
Stall(10 * 1000);
|
|
}
|
|
if (QuitFlag) {
|
|
Record = Record->Next;
|
|
continue;
|
|
}
|
|
}
|
|
Record->DispatchFunction (
|
|
(EFI_HANDLE)Record,
|
|
&Record->RegisterContext,
|
|
NULL,
|
|
NULL
|
|
);
|
|
Record = Record->Next;
|
|
}
|
|
//
|
|
// Clear source
|
|
//
|
|
Status = GpioClearGpiSmiSts(GPIO_PIN_FOR_XHCI_SMIB);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
//
|
|
// Return EFI_WARN_INTERRUPT_SOURCE_QUIESCED to make subsequent root SMM handler get called
|
|
//
|
|
return EFI_WARN_INTERRUPT_SOURCE_QUIESCED;
|
|
}
|
|
|