From 2d29f5af46ba7fe2afc000badf5746c34e42f0cf Mon Sep 17 00:00:00 2001 From: Guo Mang Date: Thu, 2 Jun 2016 13:59:07 +0800 Subject: ChvRefCodePkg: Add PchSmiDispatcher module. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Guo Mang --- .../PchSmiDispatcher/Smm/PchSmiDispatcher.inf | 89 ++ .../SouthCluster/PchSmiDispatcher/Smm/PchSmm.h | 748 ++++++++++++++++ .../SouthCluster/PchSmiDispatcher/Smm/PchSmmCore.c | 775 ++++++++++++++++ .../SouthCluster/PchSmiDispatcher/Smm/PchSmmGpi.c | 56 ++ .../PchSmiDispatcher/Smm/PchSmmHelpers.c | 308 +++++++ .../PchSmiDispatcher/Smm/PchSmmHelpers.h | 143 +++ .../SouthCluster/PchSmiDispatcher/Smm/PchSmmIchn.c | 987 +++++++++++++++++++++ .../PchSmiDispatcher/Smm/PchSmmPeriodicTimer.c | 555 ++++++++++++ .../PchSmiDispatcher/Smm/PchSmmPowerButton.c | 95 ++ .../SouthCluster/PchSmiDispatcher/Smm/PchSmmSw.c | 161 ++++ .../SouthCluster/PchSmiDispatcher/Smm/PchSmmSx.c | 257 ++++++ .../SouthCluster/PchSmiDispatcher/Smm/PchSmmUsb.c | 249 ++++++ .../PchSmiDispatcher/Smm/PchxSmmHelpers.c | 730 +++++++++++++++ .../PchSmiDispatcher/Smm/PchxSmmHelpers.h | 112 +++ 14 files changed, 5265 insertions(+) create mode 100644 ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmiDispatcher.inf create mode 100644 ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmm.h create mode 100644 ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmCore.c create mode 100644 ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmGpi.c create mode 100644 ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmHelpers.c create mode 100644 ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmHelpers.h create mode 100644 ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmIchn.c create mode 100644 ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmPeriodicTimer.c create mode 100644 ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmPowerButton.c create mode 100644 ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmSw.c create mode 100644 ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmSx.c create mode 100644 ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmUsb.c create mode 100644 ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchxSmmHelpers.c create mode 100644 ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchxSmmHelpers.h diff --git a/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmiDispatcher.inf b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmiDispatcher.inf new file mode 100644 index 0000000000..a86c37b8df --- /dev/null +++ b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmiDispatcher.inf @@ -0,0 +1,89 @@ +## @file +# SMM Dispatcher Driver +# +# This module provides the ability for system drivers to register and unregister +# handlers for specific SMI events. +# +# Copyright (c) 2012 - 2015, Intel Corporation. All rights reserved.
+# +# This program and the accompanying materials +# are licensed and made available under the terms and conditions of the BSD License +# which accompanies this distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php. +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# +## + +[defines] + INF_VERSION = 0x00010005 + BASE_NAME = PchSmiDispatcher + FILE_GUID = E7AEA323-2363-45BE-ADF5-2C69A4E5C6FD + MODULE_TYPE = DXE_SMM_DRIVER + VERSION_STRING = 1.0 + PI_SPECIFICATION_VERSION = 0x0001000A + ENTRY_POINT = InitializePchSmmDispatcher + +[sources.common] + PchSmm.h + PchSmmCore.c + PchSmmHelpers.h + PchSmmHelpers.c + PchxSmmHelpers.h + PchxSmmHelpers.c + PchSmmUsb.c + PchSmmGpi.c + PchSmmPowerButton.c + PchSmmSw.c + PchSmmSx.c + PchSmmIchn.c + PchSmmPeriodicTimer.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + ChvRefCodePkg/ChvRefCodePkg.dec + + +[LibraryClasses] + UefiBootServicesTableLib + UefiDriverEntryPoint + IoLib + DebugLib + PcdLib + BaseLib + BaseMemoryLib + DevicePathLib + PchPlatformLib + SmmServicesTableLib + ReportStatusCodeLib + PerformanceLib + DxeServicesTableLib + ReportStatusCodeLib + +[Protocols] + gEfiPciRootBridgeIoProtocolGuid #CONSUMES + gEfiSmmGpiDispatch2ProtocolGuid #PRODUCES + gEfiSmmSxDispatch2ProtocolGuid #PRODUCES + gEfiSmmSwDispatch2ProtocolGuid #PRODUCES + gEfiSmmIchnDispatchProtocolGuid #PRODUCES + gEfiSmmUsbDispatch2ProtocolGuid #PRODUCES + gEfiSmmIchnDispatchExProtocolGuid #PRODUCES + gEfiSmmPowerButtonDispatch2ProtocolGuid #PRODUCES + gEfiSmmPeriodicTimerDispatch2ProtocolGuid #PRODUCES + gEfiSmmBase2ProtocolGuid #CONSUMES + gEfiSmmCpuProtocolGuid #CONSUMES + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdPciExpressBaseAddress ## SOMETIMES_CONSUMES + gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeS3SuspendEnd ## CONSUMES + +[Depex] + gEfiSmmCpuProtocolGuid AND + gEfiSmmBase2ProtocolGuid AND + gEfiPciRootBridgeIoProtocolGuid + +[BuildOptions] + *_*_X64_CC_FLAGS = -D X64_BUILD_SUPPORT=1 + diff --git a/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmm.h b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmm.h new file mode 100644 index 0000000000..3ce4fac529 --- /dev/null +++ b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmm.h @@ -0,0 +1,748 @@ +/** @file + Prototypes and defines for the PCH SMM Dispatcher. + + Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef PCH_SMM_H +#define PCH_SMM_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (defined X64_BUILD_SUPPORT) && (X64_BUILD_SUPPORT == 1) +#define EFI_BAD_POINTER 0xAFAFAFAFAFAFAFAFULL +#else +#define EFI_BAD_POINTER 0xAFAFAFAFUL +#endif + +/// +/// Define an enumeration for all the supported protocols +/// +typedef enum { + UsbType, + SxType, + SwType, + GpiType, + IchnType, + IchnExType, + PowerButtonType, + PeriodicTimerType, + PchSmmProtocolTypeMax +} PCH_SMM_PROTOCOL_TYPE; + +/// +/// SPECIFYING A REGISTER +/// We want a general way of referring to addresses. For this case, we'll only +/// need addresses in the ACPI table (and the TCO entries within the ACPI table). +/// However, it's interesting to consider what it would take to support other types +/// of addresses. To address Will's concern, I think it prudent to accommodate it +/// early on in the design. +/// +/// Addresses we need to consider: +/// +/// Type: Required: +/// I/O Yes +/// ACPI (special case of I/O) Only if we want to +/// TCO (special case of ACPI) Only if we want to +/// Memory (or Memory Mapped I/O) Only if we want to +/// PCIE Yes, for BiosWp + +/// +typedef enum { + /// + /// IO_ADDR_TYPE, /// unimplemented + /// + ACPI_ADDR_TYPE, + /// + /// MEMORY_ADDR_TYPE, /// unimplemented + /// + MEMORY_MAPPED_IO_ADDRESS_TYPE, +#ifdef PCIESC_SUPPORT + PCIE_ADDR_TYPE, +#endif + NUM_ADDR_TYPES, /// count of items in this enum + PCH_SMM_ADDR_TYPE_NULL = -1 /// sentinel to indicate NULL or to signal end of arrays +} ADDR_TYPE; + +/// +/// Assumption: 32-bits -- enum's evaluate to integer +/// Assumption: This code will only run on IA-32. Justification: IA-64 doesn't have SMIs. +/// We don't have to worry about 64-bit addresses. +/// Typedef the size of addresses in case the numbers I'm using are wrong or in case +/// this changes. This is a good idea because PCI_ADDR will change, for example, when +/// we add support for PciExpress. +/// +typedef UINT16 IO_ADDR; +typedef IO_ADDR ACPI_ADDR; /// can omit +typedef IO_ADDR TCO_ADDR; /// can omit +typedef UINTN MEM_ADDR; +typedef MEM_ADDR *MEMORY_MAPPED_IO_ADDRESS; +#ifdef PCIESC_SUPPORT +typedef union { + UINT32 Raw; + struct { + UINT8 Reg; + UINT8 Fnc; + UINT8 Dev; + UINT8 Bus; + } Fields; +} PCIE_ADDR; +#endif + +typedef struct { + ADDR_TYPE Type; + union { + /// + /// used to initialize during declaration/definition + /// + UINT32 raw; + + /// + /// used to access useful data + /// + IO_ADDR io; + ACPI_ADDR acpi; + TCO_ADDR tco; + MEM_ADDR mem; + MEMORY_MAPPED_IO_ADDRESS Mmio; +#ifdef PCIESC_SUPPORT + PCIE_ADDR pcie; +#endif + + } Data; + +} PCH_SMM_ADDRESS; + +/// +/// SPECIFYING BITS WITHIN A REGISTER +/// Here's a struct that helps us specify a source or enable bit. +/// +typedef struct { + PCH_SMM_ADDRESS Reg; + UINT8 SizeInBytes; ///< of the register + UINT8 Bit; +} PCH_SMM_BIT_DESC; + +// +// Sometimes, we'll have bit descriptions that are unused. It'd be great to have a +// way to easily identify them: +// +#define IS_BIT_DESC_NULL(BitDesc) ((BitDesc).Reg.Type == PCH_SMM_ADDR_TYPE_NULL) ///< "returns" true when BitDesc is NULL +#define NULL_THIS_BIT_DESC(BitDesc) ((BitDesc).Reg.Type = PCH_SMM_ADDR_TYPE_NULL) ///< will "return" an integer w/ value of 0 +#define NULL_BIT_DESC_INITIALIZER \ + { \ + { \ + PCH_SMM_ADDR_TYPE_NULL, \ + { \ + 0 \ + } \ + }, \ + 0, 0 \ + } +// +// I'd like a type to specify the callback's Sts & En bits because they'll +// be commonly used together: +// +#define NUM_EN_BITS 2 +#define NUM_STS_BITS 1 + +// +// Flags +// +typedef UINT8 PCH_SMM_SOURCE_FLAGS; + +// +// Flags required today +// +#define PCH_SMM_NO_FLAGS 0 +#define PCH_SMM_SCI_EN_DEPENDENT 1 + +// +// Flags that might be required tomorrow +// +/// +/// #define PCH_SMM_CLEAR_WITH_ONE 2 /// may need to support bits that clear by writing 0 +/// #define PCH_SMM_MULTIBIT_FIELD 3 /// may need to support status/enable fields 2 bits wide +/// +typedef struct { + PCH_SMM_SOURCE_FLAGS Flags; + PCH_SMM_BIT_DESC En[NUM_EN_BITS]; + PCH_SMM_BIT_DESC Sts[NUM_STS_BITS]; +} PCH_SMM_SOURCE_DESC; +// +// 31 bytes, I think +// +#define NULL_SOURCE_DESC_INITIALIZER \ + { \ + PCH_SMM_NO_FLAGS, \ + { \ + NULL_BIT_DESC_INITIALIZER, NULL_BIT_DESC_INITIALIZER \ + }, \ + { \ + NULL_BIT_DESC_INITIALIZER \ + } \ + } + +/// +/// CHILD CONTEXTS +/// To keep consistent w/ the architecture, we'll need to provide the context +/// to the child when we call its callback function. After talking with Will, +/// we agreed that we'll need functions to "dig" the context out of the hardware +/// in many cases (Sx, Trap, Gpi, etc), and we'll need a function to compare those +/// contexts to prevent unnecessary dispatches. I'd like a general type for these +/// "GetContext" functions, so I'll need a union of all the protocol contexts for +/// our internal use: +/// +typedef union { + // + // (in no particular order) + // + EFI_SMM_ICHN_DISPATCH_CONTEXT Ichn; + EFI_SMM_ICHN_DISPATCH_EX_CONTEXT IchnEx; + EFI_SMM_SX_REGISTER_CONTEXT Sx; + EFI_SMM_PERIODIC_TIMER_REGISTER_CONTEXT PeriodicTimer; + EFI_SMM_SW_REGISTER_CONTEXT Sw; + EFI_SMM_POWER_BUTTON_REGISTER_CONTEXT PowerButton; + EFI_SMM_USB_REGISTER_CONTEXT Usb; + EFI_SMM_GPI_REGISTER_CONTEXT Gpi; +} PCH_SMM_CONTEXT; + +/// +/// Misc data for PchDispatcher usage. +/// For PeriodicTimer, since the ElapsedTime is removed from EFI_SMM_PERIODIC_TIMER_REGISTER_CONTEXT of EDKII, +/// and PchDispatcher needs it for every record. Thus move it here to support ElapsedTime. +/// +typedef union { + UINTN ElapsedTime; +}PCH_SMM_MISC_DATA; + +// +// Assumption: PeriodicTimer largest at 3x64-bits or 24 bytes +// +typedef struct _DATABASE_RECORD DATABASE_RECORD; + +/// +/// Assumption: the GET_CONTEXT function will be as small and simple as possible. +/// Assumption: We don't need to pass in an enumeration for the protocol because each +/// GET_CONTEXT function is written for only one protocol. +/// We also need a function to compare contexts to see if the child should be dispatched +/// In addition, we need a function to acquire CommBuffer and CommBufferSize for +/// dispatch callback function of EDKII native support. +/// +typedef +VOID +(EFIAPI *GET_CONTEXT) ( + IN DATABASE_RECORD * Record, + OUT PCH_SMM_CONTEXT * Context + ); + +typedef +BOOLEAN +(EFIAPI *CMP_CONTEXT) ( + IN PCH_SMM_CONTEXT * Context1, + IN PCH_SMM_CONTEXT * Context2 + ); + +typedef +VOID +(EFIAPI *GET_COMMBUFFER) ( + IN DATABASE_RECORD * Record, + OUT VOID **CommBuffer, + OUT UINTN * CommBufferSize + ); + +/// +/// Finally, every protocol will require a "Get Context" and "Compare Context" call, so +/// we may as well wrap that up in a table, too. +/// +typedef struct { + GET_CONTEXT GetContext; + CMP_CONTEXT CmpContext; + GET_COMMBUFFER GetCommBuffer; +} CONTEXT_FUNCTIONS; + +extern CONTEXT_FUNCTIONS ContextFunctions[PchSmmProtocolTypeMax]; + +/// +/// MAPPING CONTEXT TO BIT DESCRIPTIONS +/// I'd like to have a general approach to mapping contexts to bit descriptions. +/// Sometimes, we'll find that we can use table lookups or constant assignments; +/// other times, we'll find that we'll need to use a function to perform the mapping. +/// If we define a macro to mask that process, we'll never have to change the code. +/// I don't know if this is desirable or not -- if it isn't, then we can get rid +/// of the macros and just use function calls or variable assignments. Doesn't matter +/// to me. +/// Mapping complex contexts requires a function +/// + +/** + Maps a USB context to a source description. + + @param[in] Context The context we need to map. Type must be USB. + @param[out] SrcDesc The source description that corresponds to the given context. + +**/ +VOID +MapUsbToSrcDesc ( + IN PCH_SMM_CONTEXT *Context, + OUT PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + Figure out which timer the child is requesting and + send back the source description + + @param[in] DispatchContext The pointer to the Dispatch Context instances + @param[out] SrcDesc The pointer to the source description + +**/ +VOID +MapPeriodicTimerToSrcDesc ( + IN PCH_SMM_CONTEXT *DispatchContext, + OUT PCH_SMM_SOURCE_DESC *SrcDesc + ); + +// +// Mapping simple contexts can be done by assignment or lookup table +// +extern CONST PCH_SMM_SOURCE_DESC SW_SOURCE_DESC; +extern CONST PCH_SMM_SOURCE_DESC SX_SOURCE_DESC; +extern CONST PCH_SMM_SOURCE_DESC POWER_BUTTON_SOURCE_DESC; + +// +// With the changes we've made to the protocols, we can now use table +// lookups for the following protocols: +// +#define NUM_SUPPORTED_GPIS 16 +extern CONST PCH_SMM_SOURCE_DESC GPI_SOURCE_DESC[NUM_SUPPORTED_GPIS]; + +extern PCH_SMM_SOURCE_DESC ICHN_SOURCE_DESCS[NUM_ICHN_TYPES]; +extern PCH_SMM_SOURCE_DESC ICHN_SOURCE_DESCS_AX[NUM_ICHN_TYPES]; +extern PCH_SMM_SOURCE_DESC ICHN_EX_SOURCE_DESCS[IchnExTypeMAX - IchnExPciExpress]; + +/// +/// For PCHx, APMC is UINT8 port, so the MAX SWI Value is 0xFF. +/// +#define MAXIMUM_SWI_VALUE 0xFF +/// +/// Open: Need to make sure this kind of type cast will actually work. +/// May need an intermediate form w/ two VOID* arguments. I'll figure +/// that out when I start compiling. +/// +typedef +VOID +(EFIAPI *PCH_SMM_CLEAR_SOURCE) ( + PCH_SMM_SOURCE_DESC * SrcDesc + ); + +/// +/// "DATABASE" RECORD +/// Linked list data structures +/// +#define DATABASE_RECORD_SIGNATURE SIGNATURE_32 ('D', 'B', 'R', 'C') + +struct _DATABASE_RECORD { + UINT32 Signature; + LIST_ENTRY Link; + BOOLEAN Processed; + + /// + /// Status and Enable bit description + /// + PCH_SMM_SOURCE_DESC SrcDesc; + + /// + /// Callback function + /// + EFI_SMM_HANDLER_ENTRY_POINT2 Callback; + PCH_SMM_CONTEXT ChildContext; + + /// + /// Special handling hooks -- init them to NULL if unused/unneeded + /// + PCH_SMM_CLEAR_SOURCE ClearSource; ///< needed for SWSMI timer + + /// + /// Functions required to make callback code general + /// + CONTEXT_FUNCTIONS ContextFunctions; + + /// + /// The protocol that this record dispatches + /// + PCH_SMM_PROTOCOL_TYPE ProtocolType; + + /// + /// Misc data for private usage + /// + PCH_SMM_MISC_DATA MiscData; +}; + +#define DATABASE_RECORD_FROM_LINK(_record) CR (_record, DATABASE_RECORD, Link, DATABASE_RECORD_SIGNATURE) +#define DATABASE_RECORD_FROM_CHILDCONTEXT(_record) CR (_record, DATABASE_RECORD, ChildContext, DATABASE_RECORD_SIGNATURE) + +/// +/// HOOKING INTO THE ARCHITECTURE +/// +typedef +EFI_STATUS +(EFIAPI *PCH_SMM_GENERIC_REGISTER) ( + IN VOID **This, + IN VOID *DispatchFunction, + IN VOID *DispatchContext, + OUT EFI_HANDLE * DispatchHandle + ); +typedef +EFI_STATUS +(EFIAPI *PCH_SMM_GENERIC_UNREGISTER) ( + IN VOID **This, + IN EFI_HANDLE DispatchHandle + ); + +/// +/// Define a memory "stamp" equivalent in size and function to most of the protocols +/// +typedef struct { + PCH_SMM_GENERIC_REGISTER Register; + PCH_SMM_GENERIC_UNREGISTER Unregister; + UINTN Extra1; + UINTN Extra2; ///< may not need this one +} PCH_SMM_GENERIC_PROTOCOL; + +/** + Register a child SMI dispatch function with a parent SMM driver. + + @param[in] This Pointer to the PCH_SMM_GENERIC_PROTOCOL instance. + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source. + @param[in] DispatchContext Pointer to the dispatch function's context. + @param[out] DispatchHandle Handle of dispatch function, for when interfacing + with the parent SMM driver, will be the address of linked + list link in the call back record. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to create database record + @retval EFI_INVALID_PARAMETER The input parameter is invalid + @retval EFI_SUCCESS The dispatch function has been successfully + registered and the SMI source has been enabled. +**/ +EFI_STATUS +PchSmmCoreRegister ( + IN PCH_SMM_GENERIC_PROTOCOL *This, + IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction, + IN PCH_SMM_CONTEXT *DispatchContext, + OUT EFI_HANDLE *DispatchHandle + ); + +/** + Unregister a child SMI source dispatch function with a parent SMM driver. + + @param[in] This Pointer to the EFI_SMM_IO_TRAP_DISPATCH_PROTOCOL instance. + @param[in] DispatchHandle Handle of dispatch function to deregister. + + @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 Handle is invalid. +**/ +EFI_STATUS +PchSmmCoreUnRegister ( + IN PCH_SMM_GENERIC_PROTOCOL *This, + IN EFI_HANDLE *DispatchHandle + ); + +typedef union { + PCH_SMM_GENERIC_PROTOCOL Generic; + + EFI_SMM_USB_DISPATCH2_PROTOCOL Usb; + EFI_SMM_SX_DISPATCH2_PROTOCOL Sx; + EFI_SMM_SW_DISPATCH2_PROTOCOL Sw; + EFI_SMM_GPI_DISPATCH2_PROTOCOL Gpi; + EFI_SMM_POWER_BUTTON_DISPATCH2_PROTOCOL PowerButton; + EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL PeriodicTimer; + EFI_SMM_ICHN_DISPATCH_PROTOCOL Ichn; + EFI_SMM_ICHN_DISPATCH_EX_PROTOCOL IchnEx; +} PCH_SMM_PROTOCOL; + +/// +/// Define a structure to help us identify the generic protocol +/// +#define PROTOCOL_SIGNATURE SIGNATURE_32 ('P', 'R', 'O', 'T') + +typedef struct { + UINTN Signature; + + PCH_SMM_PROTOCOL_TYPE Type; + EFI_GUID *Guid; + PCH_SMM_PROTOCOL Protocols; +} PCH_SMM_QUALIFIED_PROTOCOL; + +#define QUALIFIED_PROTOCOL_FROM_GENERIC(_generic) \ + CR ( \ + _generic, \ + PCH_SMM_QUALIFIED_PROTOCOL, \ + Protocols, \ + PROTOCOL_SIGNATURE \ + ) + +/// +/// Create private data for the protocols that we'll publish +/// +typedef struct { + LIST_ENTRY CallbackDataBase; + EFI_HANDLE SmiHandle; + EFI_HANDLE InstallMultProtHandle; + PCH_SMM_QUALIFIED_PROTOCOL Protocols[PchSmmProtocolTypeMax]; +} PRIVATE_DATA; + +extern PRIVATE_DATA mPrivateData; +extern UINT32 AcpiBase; + +/** + Get the Software Smi value + + @param[in] Record No use + @param[out] Context The context that includes Software Smi value to be filled + +**/ +VOID +EFIAPI +SwGetContext ( + IN DATABASE_RECORD *Record, + OUT PCH_SMM_CONTEXT *Context + ); + +/** + Check whether software SMI value of two contexts match + + @param[in] Context1 Context 1 that includes software SMI value 1 + @param[in] Context2 Context 2 that includes software SMI value 2 + + @retval FALSE Software SMI value match + @retval TRUE Software SMI value don't match +**/ +BOOLEAN +EFIAPI +SwCmpContext ( + IN PCH_SMM_CONTEXT *Context1, + IN PCH_SMM_CONTEXT *Context2 + ); + +/** + Gather the CommBuffer information of SmmSwDispatch2. + + @param[in] Record No use + @param[out] CommBuffer Point to the CommBuffer structure + @param[out] CommBufferSize Point to the Size of CommBuffer structure + +**/ +VOID +EFIAPI +SwGetCommBuffer ( + IN DATABASE_RECORD *Record, + OUT VOID **CommBuffer, + OUT UINTN *CommBufferSize + ); + +/** + Get the Sleep type + + @param[in] Record No use + @param[out] Context The context that includes SLP_TYP bits to be filled + +**/ +VOID +EFIAPI +SxGetContext ( + IN DATABASE_RECORD *Record, + OUT PCH_SMM_CONTEXT *Context + ); + +/** + Init required protocol for Pch Sw Dispatch protocol. + +**/ +VOID +PchSwDispatchInit ( + VOID + ); + +/** + Check whether sleep type of two contexts match + + @param[in] Context1 Context 1 that includes sleep type 1 + @param[in] Context2 Context 2 that includes sleep type 2 + + @retval FALSE Sleep types match + @retval TRUE Sleep types don't match +**/ +BOOLEAN +EFIAPI +SxCmpContext ( + IN PCH_SMM_CONTEXT *Context1, + IN PCH_SMM_CONTEXT *Context2 + ); + +/** + Update the elapsed time from the Interval data of DATABASE_RECORD + + @param[in] Record The pointer to the DATABASE_RECORD. + @param[out] HwContext The Context to be updated. + +**/ +VOID +EFIAPI +PeriodicTimerGetContext ( + IN DATABASE_RECORD *Record, + OUT PCH_SMM_CONTEXT *Context + ); + +/** + Check whether Periodic Timer of two contexts match + + @param[in] Context1 Context 1 that includes Periodic Timer 1 + @param[in] Context2 Context 2 that includes Periodic Timer 2 + + @retval FALSE Periodic Timer match + @retval TRUE Periodic Timer don't match +**/ +BOOLEAN +EFIAPI +PeriodicTimerCmpContext ( + IN PCH_SMM_CONTEXT *Context1, + IN PCH_SMM_CONTEXT *Context2 + ); + +/** + Gather the CommBuffer information of SmmPeriodicTimerDispatch2. + + @param[in] Record No use + @param[out] CommBuffer Point to the CommBuffer structure + @param[out] CommBufferSize Point to the Size of CommBuffer structure + +**/ +VOID +EFIAPI +PeriodicTimerGetCommBuffer ( + IN DATABASE_RECORD *Record, + OUT VOID **CommBuffer, + OUT UINTN *CommBufferSize + ); + +/** + Get the power button status. + + @param[in] Record The pointer to the DATABASE_RECORD. + @param[out] Context Calling context from the hardware, will be updated with the current power button status. + +**/ +VOID +EFIAPI +PowerButtonGetContext ( + IN DATABASE_RECORD *Record, + OUT PCH_SMM_CONTEXT *Context + ); + +/** + Check whether Power Button status of two contexts match + + @param[in] Context1 Context 1 that includes Power Button status 1 + @param[in] Context2 Context 2 that includes Power Button status 2 + + @retval FALSE Power Button status match + @retval TRUE Power Button status don't match +**/ +BOOLEAN +EFIAPI +PowerButtonCmpContext ( + IN PCH_SMM_CONTEXT *Context1, + IN PCH_SMM_CONTEXT *Context2 + ); + +/** + This function is responsible for calculating and enabling any timers that are required + to dispatch messages to children. The SrcDesc argument isn't acutally used. + + @param[in] SrcDesc Pointer to the PCH_SMM_SOURCE_DESC instance. + +**/ +VOID +EFIAPI +PchSmmPeriodicTimerClearSource ( + IN PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + This services returns the next SMI tick period that is supported by the chipset. + The order returned is from longest to shortest interval period. + + @param[in] This Pointer to the EFI_SMM_PERIODIC_TIMER_DISPATCH_PROTOCOL instance. + @param[in,out] SmiTickInterval Pointer to pointer of the next shorter SMI interval period that is supported by the child. + + @retval EFI_SUCCESS The service returned successfully. + @retval EFI_INVALID_PARAMETER The parameter SmiTickInterval is invalid. +**/ +EFI_STATUS +PchSmmPeriodicTimerDispatchGetNextShorterInterval ( + IN CONST EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL *This, + IN OUT UINT64 **SmiTickInterval + ); + +/** + When we get an SMI that indicates that we are transitioning to a sleep state, + we need to actually transition to that state. We do this by disabling the + "SMI on sleep enable" feature, which generates an SMI when the operating system + tries to put the system to sleep, and then physically putting the system to sleep. + +**/ +VOID +PchSmmSxGoToSleep ( + VOID + ); + +/** + Clear the SMI status bit after the SMI handling is done + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +EFIAPI +PchSmmIchnClearSource ( + IN PCH_SMM_SOURCE_DESC *SrcDesc + ); + +#endif diff --git a/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmCore.c b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmCore.c new file mode 100644 index 0000000000..b1ba80f435 --- /dev/null +++ b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmCore.c @@ -0,0 +1,775 @@ +/** @file + This driver is responsible for the registration of child drivers + and the abstraction of the PCH SMI sources. + + Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PchSmm.h" +#include "PchSmmHelpers.h" + +#include +#include +// +// MODULE / GLOBAL DATA +// +// Module variables used by the both the main dispatcher and the source dispatchers +// Declared in PchSmmSources.h +// + +UINT32 AcpiBase; +PRIVATE_DATA mPrivateData = { ///< for the structure + { + NULL + }, ///< CallbackDataBase linked list head + NULL, ///< EFI handle returned when calling InstallMultipleProtocolInterfaces + NULL, ///< EFI handle returned when calling InstallMultipleProtocolInterfaces + { ///< protocol arrays + /// + /// elements within the array + /// + { + PROTOCOL_SIGNATURE, + UsbType, + &gEfiSmmUsbDispatch2ProtocolGuid, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister + } + }, + { + PROTOCOL_SIGNATURE, + SxType, + &gEfiSmmSxDispatch2ProtocolGuid, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister + } + }, + { + PROTOCOL_SIGNATURE, + SwType, + &gEfiSmmSwDispatch2ProtocolGuid, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister, + (UINTN) MAXIMUM_SWI_VALUE + } + }, + { + PROTOCOL_SIGNATURE, + GpiType, + &gEfiSmmGpiDispatch2ProtocolGuid, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister, + (UINTN) NUM_SUPPORTED_GPIS + } + }, + { + PROTOCOL_SIGNATURE, + IchnType, + &gEfiSmmIchnDispatchProtocolGuid, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister + } + }, + { + PROTOCOL_SIGNATURE, + IchnExType, + &gEfiSmmIchnDispatchExProtocolGuid, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister + } + }, + { + PROTOCOL_SIGNATURE, + PowerButtonType, + &gEfiSmmPowerButtonDispatch2ProtocolGuid, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister + } + }, + { + PROTOCOL_SIGNATURE, + PeriodicTimerType, + &gEfiSmmPeriodicTimerDispatch2ProtocolGuid, + { + (PCH_SMM_GENERIC_REGISTER) PchSmmCoreRegister, + (PCH_SMM_GENERIC_UNREGISTER) PchSmmCoreUnRegister, + (UINTN) PchSmmPeriodicTimerDispatchGetNextShorterInterval + } + }, + } +}; + +CONTEXT_FUNCTIONS mContextFunctions[PchSmmProtocolTypeMax] = { + { + NULL, + NULL, + NULL + }, + { + SxGetContext, + SxCmpContext, + NULL + }, + { + SwGetContext, + SwCmpContext, + SwGetCommBuffer + }, + { + NULL, + NULL, + NULL + }, + { + NULL, + NULL, + NULL + }, + { + NULL, + NULL, + NULL + }, + { + PowerButtonGetContext, + PowerButtonCmpContext, + NULL + }, + { + PeriodicTimerGetContext, + PeriodicTimerCmpContext, + PeriodicTimerGetCommBuffer + }, +}; + +// +// PROTOTYPES +// +// Functions use only in this file +// +EFI_STATUS +EFIAPI +PchSmmCoreDispatcher ( + IN EFI_HANDLE SmmImageHandle, + IN CONST VOID *ContextData, OPTIONAL + IN OUT VOID *CommunicationBuffer, OPTIONAL + IN OUT UINTN *SourceSize OPTIONAL + ); + +// +// FUNCTIONS +// +/** + Initializes the PCH SMM Dispatcher + + @param[in] ImageHandle Pointer to the loaded image protocol for this driver + @param[in] SystemTable Pointer to the EFI System Table + + @retval EFI_SUCCESS PchSmmDispatcher Initialization completed. +**/ +EFI_STATUS +EFIAPI +InitializePchSmmDispatcher ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + // + // Access ACPI Base Addresses Register + // + AcpiBase = (UINT32) (MmioRead16 ( + MmPciAddress (0, + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_LPC, + PCI_FUNCTION_NUMBER_PCH_LPC, + R_PCH_LPC_ACPI_BASE + ) + ) & B_PCH_LPC_ACPI_BASE_BAR); + ASSERT (AcpiBase != 0); + + /// + /// Init required protocol for Pch Sw Dispatch protocol. + /// + PchSwDispatchInit (); + + /// + /// Register a callback function to handle subsequent SMIs. This callback + /// will be called by SmmCoreDispatcher. + /// + Status = gSmst->SmiHandlerRegister (PchSmmCoreDispatcher, NULL, &mPrivateData.SmiHandle); + ASSERT_EFI_ERROR (Status); + + /// + /// Initialize Callback DataBase + /// + InitializeListHead (&mPrivateData.CallbackDataBase); + PchSmmPublishDispatchProtocols (); + + /// + /// Enable SMIs on the PCH now that we have a callback + /// + PchSmmInitHardware (); + + return EFI_SUCCESS; +} + +/** + Check the Fed SwSmiInputValue to see if there is a duplicated one in the database + + @param[in] FedSwSmiInputValue Fed SwSmiInputValue + + @retval EFI_SUCCESS There is no duplicated SwSmiInputValue + @retval EFI_INVALID_PARAMETER There is a duplicated SwSmiInputValue +**/ +EFI_STATUS +SmiInputValueDuplicateCheck ( + UINTN FedSwSmiInputValue + ) +{ + + DATABASE_RECORD *RecordInDb; + LIST_ENTRY *LinkInDb; + + LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase); + while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb); + + if (RecordInDb->ProtocolType == SwType) { + if (RecordInDb->ChildContext.Sw.SwSmiInputValue == FedSwSmiInputValue) { + return EFI_INVALID_PARAMETER; + } + } + + LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link); + } + + return EFI_SUCCESS; +} + +/** + Register a child SMI dispatch function with a parent SMM driver. + + @param[in] This Pointer to the PCH_SMM_GENERIC_PROTOCOL instance. + @param[in] DispatchFunction Pointer to dispatch function to be invoked for this SMI source. + @param[in] DispatchContext Pointer to the dispatch function's context. + @param[out] DispatchHandle Handle of dispatch function, for when interfacing + with the parent SMM driver, will be the address of linked + list link in the call back record. + + @retval EFI_OUT_OF_RESOURCES Insufficient resources to create database record + @retval EFI_INVALID_PARAMETER The input parameter is invalid + @retval EFI_SUCCESS The dispatch function has been successfully + registered and the SMI source has been enabled. +**/ +EFI_STATUS +PchSmmCoreRegister ( + IN PCH_SMM_GENERIC_PROTOCOL *This, + IN EFI_SMM_HANDLER_ENTRY_POINT2 DispatchFunction, + IN PCH_SMM_CONTEXT *DispatchContext, + OUT EFI_HANDLE *DispatchHandle + ) +{ + EFI_STATUS Status; + DATABASE_RECORD *Record; + PCH_SMM_QUALIFIED_PROTOCOL *Qualified; + PCH_SMM_SOURCE_DESC NullSourceDesc = NULL_SOURCE_DESC_INITIALIZER; + UINTN Index; + + Index = 0; + /// + /// Create database record and add to database + /// + + Status = gSmst->SmmAllocatePool (EfiRuntimeServicesData, sizeof (DATABASE_RECORD), (VOID **) &Record); + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + return EFI_OUT_OF_RESOURCES; + } + /// + /// Gather information about the registration request + /// + Record->Callback = DispatchFunction; + + Record->ChildContext = *DispatchContext; + + Qualified = QUALIFIED_PROTOCOL_FROM_GENERIC (This); + + Record->ProtocolType = Qualified->Type; + + Record->ContextFunctions = mContextFunctions[Qualified->Type]; + /// + /// Perform linked list housekeeping + /// + Record->Signature = DATABASE_RECORD_SIGNATURE; + + switch (Qualified->Type) { + /// + /// By the end of this switch statement, we'll know the + /// source description the child is registering for + /// + case UsbType: + /// + /// Check the validity of Context Type + /// + if ((Record->ChildContext.Usb.Type < UsbLegacy) || (Record->ChildContext.Usb.Type > UsbWake)) { + goto Error; + } + + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + MapUsbToSrcDesc (DispatchContext, &(Record->SrcDesc)); + Record->ClearSource = NULL; + /// + /// use default clear source function + /// + break; + + case SxType: + /// + /// Check the validity of Context Type and Phase + /// + if ((Record->ChildContext.Sx.Type < SxS0) || + (Record->ChildContext.Sx.Type >= EfiMaximumSleepType) || + (Record->ChildContext.Sx.Phase < SxEntry) || + (Record->ChildContext.Sx.Phase >= EfiMaximumPhase) + ) { + goto Error; + } + + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + CopyMem ((VOID *) &(Record->SrcDesc), (VOID *) (&SX_SOURCE_DESC), sizeof (PCH_SMM_SOURCE_DESC)); + Record->ClearSource = NULL; + /// + /// use default clear source function + /// + break; + + case SwType: + /// + /// Check the validity of Context Value + /// + if (Record->ChildContext.Sw.SwSmiInputValue == (UINTN)-1) { + for (Index = 1; Index < MAXIMUM_SWI_VALUE; Index++) { + if (!EFI_ERROR (SmiInputValueDuplicateCheck (Index))) { + Record->ChildContext.Sw.SwSmiInputValue = Index; + break; + } + } + if (Record->ChildContext.Sw.SwSmiInputValue == (UINTN)-1) { + goto Error; + } + } + if (Record->ChildContext.Sw.SwSmiInputValue > MAXIMUM_SWI_VALUE) { + goto Error; + } + + if (EFI_ERROR (SmiInputValueDuplicateCheck (Record->ChildContext.Sw.SwSmiInputValue))) { + goto Error; + } + + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + CopyMem ((VOID *) &(Record->SrcDesc), (VOID *) (&SW_SOURCE_DESC), sizeof (PCH_SMM_SOURCE_DESC)); + Record->ClearSource = NULL; + /// + /// use default clear source function + /// + break; + + case GpiType: + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + CopyMem ( + (VOID *) &(Record->SrcDesc), + (VOID *) &(GPI_SOURCE_DESC[Record->ChildContext.Gpi.GpiNum]), + sizeof (PCH_SMM_SOURCE_DESC) + ); + Record->ClearSource = NULL; + /// + /// use default clear source function + /// + break; + + case IchnType: + /// + /// Check the validity of Context Type + /// + if (Record->ChildContext.Ichn.Type >= NUM_ICHN_TYPES) { + goto Error; + } + + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + if (SocStepping () < SocB0) { + CopyMem ( + (VOID *) &(Record->SrcDesc), + (VOID *) &(ICHN_SOURCE_DESCS_AX[Record->ChildContext.Ichn.Type]), + sizeof (PCH_SMM_SOURCE_DESC) + ); + } else { + CopyMem ( + (VOID *) &(Record->SrcDesc), + (VOID *) &(ICHN_SOURCE_DESCS[Record->ChildContext.Ichn.Type]), + sizeof (PCH_SMM_SOURCE_DESC) + ); + } + Record->ClearSource = PchSmmIchnClearSource; + break; + + case IchnExType: + /// + /// Check the validity of Context Type + /// + if ((Record->ChildContext.IchnEx.Type < IchnExPciExpress) || (Record->ChildContext.IchnEx.Type >= IchnExTypeMAX)) { + goto Error; + } + + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + CopyMem ( + (VOID *) &(Record->SrcDesc), + (VOID *) &(ICHN_EX_SOURCE_DESCS[Record->ChildContext.IchnEx.Type - IchnExPciExpress]), + sizeof (PCH_SMM_SOURCE_DESC) + ); + Record->ClearSource = NULL; + break; + + case PowerButtonType: + /// + /// Check the validity of Context Phase + /// + if ((Record->ChildContext.PowerButton.Phase < EfiPowerButtonEntry) || + (Record->ChildContext.PowerButton.Phase > EfiPowerButtonExit) + ) + { + goto Error; + } + + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + CopyMem ((VOID *) &(Record->SrcDesc), (VOID *) &POWER_BUTTON_SOURCE_DESC, sizeof (PCH_SMM_SOURCE_DESC)); + Record->ClearSource = NULL; + /// + /// use default clear source function + /// + break; + + case PeriodicTimerType: + /// + /// Check the validity of timer value + /// + if (DispatchContext->PeriodicTimer.SmiTickInterval <= 0) { + goto Error; + } + + InsertTailList (&mPrivateData.CallbackDataBase, &Record->Link); + MapPeriodicTimerToSrcDesc (DispatchContext, &(Record->SrcDesc)); + Record->ClearSource = PchSmmPeriodicTimerClearSource; + break; + + default: + goto Error; + break; + } + + if (CompareSources (&Record->SrcDesc, &NullSourceDesc)) { + goto Error; + } + + if (Record->ClearSource == NULL) { + /// + /// Clear the SMI associated w/ the source using the default function + /// + PchSmmClearSource (&Record->SrcDesc); + } else { + /// + /// This source requires special handling to clear + /// + Record->ClearSource (&Record->SrcDesc); + } + + PchSmmEnableSource (&Record->SrcDesc); + + /// + /// Child's handle will be the address linked list link in the record + /// + *DispatchHandle = (EFI_HANDLE) (&Record->Link); + *DispatchContext = Record->ChildContext; + + return EFI_SUCCESS; + +Error: + Status = gSmst->SmmFreePool (Record); + /// + /// DEBUG((EFI_D_ERROR,"Free pool status %d\n", Status )); + /// + return EFI_INVALID_PARAMETER; +} + +/** + Unregister a child SMI source dispatch function with a parent SMM driver. + + @param[in] This Pointer to the EFI_SMM_IO_TRAP_DISPATCH_PROTOCOL instance. + @param[in] DispatchHandle Handle of dispatch function to deregister. + + @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 Handle is invalid. +**/ +EFI_STATUS +PchSmmCoreUnRegister ( + IN PCH_SMM_GENERIC_PROTOCOL *This, + IN EFI_HANDLE *DispatchHandle + ) +{ + /// + /// BOOLEAN SafeToDisable; + /// + DATABASE_RECORD *RecordToDelete; + + /// + /// DATABASE_RECORD *RecordInDb; + /// EFI_LIST_NODE *LinkInDb; + /// + if (DispatchHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + RecordToDelete = DATABASE_RECORD_FROM_LINK (DispatchHandle); + + /// + /// Take the entry out of the linked list + /// + if (RecordToDelete->Link.ForwardLink == (LIST_ENTRY *) EFI_BAD_POINTER) { + return EFI_INVALID_PARAMETER; + } + + RemoveEntryList (&RecordToDelete->Link); + + return EFI_SUCCESS; +} + +/** + The callback function to handle subsequent SMIs. This callback will be called by SmmCoreDispatcher. + + @param[in] SmmImageHandle Not used + @param[in,out] CommunicationBuffer Not used + @param[in,out] SourceSize Not used + + @retval EFI_SUCCESS Function successfully completed +**/ +EFI_STATUS +EFIAPI +PchSmmCoreDispatcher ( + IN EFI_HANDLE SmmImageHandle, + IN CONST VOID *ContextData, OPTIONAL + IN OUT VOID *CommunicationBuffer, OPTIONAL + IN OUT UINTN *SourceSize OPTIONAL + ) +{ + /// + /// Used to prevent infinite loops + /// + UINTN EscapeCount; + + BOOLEAN ContextsMatch; + BOOLEAN EosSet; + BOOLEAN SxChildWasDispatched; + + DATABASE_RECORD *RecordInDb; + LIST_ENTRY *LinkInDb; + DATABASE_RECORD *RecordToExhaust; + LIST_ENTRY *LinkToExhaust; + + PCH_SMM_CONTEXT Context; + VOID *CommBuffer; + UINTN CommBufferSize; + + EFI_STATUS Status; + + PCH_SMM_SOURCE_DESC ActiveSource = NULL_SOURCE_DESC_INITIALIZER; + + EscapeCount = 100; + ContextsMatch = FALSE; + EosSet = FALSE; + SxChildWasDispatched = FALSE; + Status = EFI_SUCCESS; + + if (!IsListEmpty (&mPrivateData.CallbackDataBase)) { + /// + /// We have children registered w/ us -- continue + /// + while ((!EosSet) && (EscapeCount > 0)) { + EscapeCount--; + + LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase); + + while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb); + + /// + /// look for the first active source + /// + if (!SourceIsActive (&RecordInDb->SrcDesc)) { + /// + /// Didn't find the source yet, keep looking + /// + LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link); + + /// + /// if it's the last one, try to clear EOS + /// + if (IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + EosSet = PchSmmSetAndCheckEos (); + } + } else { + /// + /// We found a source. If this is a sleep type, we have to go to + /// appropriate sleep state anyway.No matter there is sleep child or not + /// + if (RecordInDb->ProtocolType == SxType) { + SxChildWasDispatched = TRUE; + } + /// + /// "cache" the source description and don't query I/O anymore + /// + CopyMem ((VOID *) &ActiveSource, (VOID *) &(RecordInDb->SrcDesc), sizeof (PCH_SMM_SOURCE_DESC)); + LinkToExhaust = LinkInDb; + + /// + /// exhaust the rest of the queue looking for the same source + /// + while (!IsNull (&mPrivateData.CallbackDataBase, LinkToExhaust)) { + RecordToExhaust = DATABASE_RECORD_FROM_LINK (LinkToExhaust); + /// + /// RecordToExhaust->Link might be removed (unregistered) by Callback function, and then the + /// system will hang in ASSERT() while calling GetNextNode(). + /// To prevent the issue, we need to get next record in DB here (before Callback function). + /// + LinkToExhaust = GetNextNode (&mPrivateData.CallbackDataBase, &RecordToExhaust->Link); + + if (CompareSources (&RecordToExhaust->SrcDesc, &ActiveSource)) { + /// + /// These source descriptions are equal, so this callback should be + /// dispatched. + /// + if (RecordToExhaust->ContextFunctions.GetContext != NULL) { + /// + /// This child requires that we get a calling context from + /// hardware and compare that context to the one supplied + /// by the child. + /// + ASSERT (RecordToExhaust->ContextFunctions.CmpContext != NULL); + + /// + /// Make sure contexts match before dispatching event to child + /// + RecordToExhaust->ContextFunctions.GetContext (RecordToExhaust, &Context); + ContextsMatch = RecordToExhaust->ContextFunctions.CmpContext (&Context, &RecordToExhaust->ChildContext); + + } else { + /// + /// This child doesn't require any more calling context beyond what + /// it supplied in registration. Simply pass back what it gave us. + /// + ASSERT (RecordToExhaust->Callback != NULL); + Context = RecordToExhaust->ChildContext; + ContextsMatch = TRUE; + } + + if (ContextsMatch) { + + if (RecordToExhaust->Callback != NULL) { + if (RecordToExhaust->ContextFunctions.GetCommBuffer != NULL) { + /// + /// This callback function needs CommBuffer and CommBufferSize. + /// Get those from child and then pass to callback function. + /// + RecordToExhaust->ContextFunctions.GetCommBuffer (RecordToExhaust, &CommBuffer, &CommBufferSize); + } else { + /// + /// Child doesn't support the CommBuffer and CommBufferSize. + /// Just pass NULL value to callback function. + /// + CommBuffer = NULL; + CommBufferSize = 0; + } + + PERF_START_EX (NULL, "SmmFunction", NULL, AsmReadTsc(), RecordToExhaust->ProtocolType); + RecordToExhaust->Callback ((EFI_HANDLE) & RecordToExhaust->Link, &Context, CommBuffer, &CommBufferSize); + PERF_END_EX (NULL, "SmmFunction", NULL, AsmReadTsc(), RecordToExhaust->ProtocolType); + if (RecordToExhaust->ProtocolType == SxType) { + SxChildWasDispatched = TRUE; + } + } else { + ASSERT (FALSE); + } + } + } + } + + if (RecordInDb->ClearSource == NULL) { + /// + /// Clear the SMI associated w/ the source using the default function + /// + PchSmmClearSource (&ActiveSource); + } else { + /// + /// This source requires special handling to clear + /// + RecordInDb->ClearSource (&ActiveSource); + } + /// + /// Also, try to clear EOS + /// + EosSet = PchSmmSetAndCheckEos (); + /// + /// Queue is empty, reset the search + /// + break; + } + } + } + } + /// + /// If you arrive here, there are two possible reasons: + /// (1) you've got problems with clearing the SMI status bits in the + /// ACPI table. If you don't properly clear the SMI bits, then you won't be able to set the + /// EOS bit. If this happens too many times, the loop exits. + /// (2) there was a SMM communicate for callback messages that was received prior + /// to this driver. + /// If there is an asynchronous SMI that occurs while processing the Callback, let + /// all of the drivers (including this one) have an opportunity to scan for the SMI + /// and handle it. + /// If not, we don't want to exit and have the foreground app. clear EOS without letting + /// these other sources get serviced. + /// + /// This assert is not valid with CSM legacy solution because it generates software SMI + /// to test for legacy USB support presence. + /// This may not be illegal, so we cannot assert at this time. + /// + /// ASSERT (EscapeCount > 0); + /// + if (SxChildWasDispatched) { + /// + /// A child of the SmmSxDispatch protocol was dispatched during this call; + /// put the system to sleep. + /// + PchSmmSxGoToSleep (); + } + + return Status; +} diff --git a/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmGpi.c b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmGpi.c new file mode 100644 index 0000000000..1004810a40 --- /dev/null +++ b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmGpi.c @@ -0,0 +1,56 @@ +/** @file + File to contain all the hardware specific stuff for the Smm Gpi dispatch protocol. + + Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PchSmmHelpers.h" + +#define GPI_INIT_ELEMENT(num) { \ + PCH_SMM_NO_FLAGS, \ + { \ + { \ + { \ + ACPI_ADDR_TYPE, R_PCH_ALT_GP_SMI_EN \ + }, \ + S_PCH_ALT_GP_SMI_EN, num, \ + }, \ + NULL_BIT_DESC_INITIALIZER \ + }, \ + { \ + { \ + { \ + ACPI_ADDR_TYPE, R_PCH_ALT_GP_SMI_STS \ + }, \ + S_PCH_ALT_GP_SMI_STS, (num), \ + }, \ + } \ + } + +CONST PCH_SMM_SOURCE_DESC GPI_SOURCE_DESC[NUM_SUPPORTED_GPIS] = { + GPI_INIT_ELEMENT(0), + GPI_INIT_ELEMENT(1), + GPI_INIT_ELEMENT(2), + GPI_INIT_ELEMENT(3), + GPI_INIT_ELEMENT(4), + GPI_INIT_ELEMENT(5), + GPI_INIT_ELEMENT(6), + GPI_INIT_ELEMENT(7), + GPI_INIT_ELEMENT(8), + GPI_INIT_ELEMENT(9), + GPI_INIT_ELEMENT(10), + GPI_INIT_ELEMENT(11), + GPI_INIT_ELEMENT(12), + GPI_INIT_ELEMENT(13), + GPI_INIT_ELEMENT(14), + GPI_INIT_ELEMENT(15), +}; diff --git a/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmHelpers.c b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmHelpers.c new file mode 100644 index 0000000000..eb2a4a6d9f --- /dev/null +++ b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmHelpers.c @@ -0,0 +1,308 @@ +/** @file + Helper functions for PCH SMM dispatcher. + + Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PchSmmHelpers.h" + +/// +/// #define BIT_ZERO 0x00000001 +/// +CONST UINT32 BIT_ZERO = 0x00000001; + +/// +/// SUPPORT / HELPER FUNCTIONS (PCH version-independent) +/// + +/** + Compare 2 SMM source descriptors' enable settings. + + @param[in] Src1 Pointer to the PCH SMI source description table 1 + @param[in] Src2 Pointer to the PCH SMI source description table 2 + + @retval TRUE The enable settings of the 2 SMM source descriptors are identical. + @retval FALSE The enable settings of the 2 SMM source descriptors are not identical. +**/ +BOOLEAN +CompareEnables ( + CONST IN PCH_SMM_SOURCE_DESC *Src1, + CONST IN PCH_SMM_SOURCE_DESC *Src2 + ) +{ + BOOLEAN IsEqual; + UINTN loopvar; + + IsEqual = TRUE; + for (loopvar = 0; loopvar < NUM_EN_BITS; loopvar++) { + /// + /// It's okay to compare a NULL bit description to a non-NULL bit description. + /// They are unequal and these tests will generate the correct result. + /// + if (Src1->En[loopvar].Bit != Src2->En[loopvar].Bit || + Src1->En[loopvar].Reg.Type != Src2->En[loopvar].Reg.Type || + Src1->En[loopvar].Reg.Data.raw != Src2->En[loopvar].Reg.Data.raw + ) { + IsEqual = FALSE; + break; + /// + /// out of for loop + /// + } + } + + return IsEqual; +} + +/** + Compare 2 SMM source descriptors' statuses. + + @param[in] Src1 Pointer to the PCH SMI source description table 1 + @param[in] Src2 Pointer to the PCH SMI source description table 2 + + @retval TRUE The statuses of the 2 SMM source descriptors are identical. + @retval FALSE The statuses of the 2 SMM source descriptors are not identical. +**/ +BOOLEAN +CompareStatuses ( + CONST IN PCH_SMM_SOURCE_DESC *Src1, + CONST IN PCH_SMM_SOURCE_DESC *Src2 + ) +{ + BOOLEAN IsEqual; + UINTN loopvar; + + IsEqual = TRUE; + + for (loopvar = 0; loopvar < NUM_STS_BITS; loopvar++) { + /// + /// It's okay to compare a NULL bit description to a non-NULL bit description. + /// They are unequal and these tests will generate the correct result. + /// + if (Src1->Sts[loopvar].Bit != Src2->Sts[loopvar].Bit || + Src1->Sts[loopvar].Reg.Type != Src2->Sts[loopvar].Reg.Type || + Src1->Sts[loopvar].Reg.Data.raw != Src2->Sts[loopvar].Reg.Data.raw + ) { + IsEqual = FALSE; + break; + /// + /// out of for loop + /// + } + } + + return IsEqual; +} + +/** + Compare 2 SMM source descriptors, based on Enable settings and Status settings of them. + + @param[in] Src1 Pointer to the PCH SMI source description table 1 + @param[in] Src2 Pointer to the PCH SMI source description table 2 + + @retval TRUE The 2 SMM source descriptors are identical. + @retval FALSE The 2 SMM source descriptors are not identical. +**/ +BOOLEAN +CompareSources ( + CONST IN PCH_SMM_SOURCE_DESC *Src1, + CONST IN PCH_SMM_SOURCE_DESC *Src2 + ) +{ + return (BOOLEAN) (CompareEnables (Src1, Src2) && CompareStatuses (Src1, Src2)); +} + +/** + Check if an SMM source is active. + + @param[in] Src Pointer to the PCH SMI source description table + + @retval TRUE It is active. + @retval FALSE It is inactive. +**/ +BOOLEAN +SourceIsActive ( + CONST IN PCH_SMM_SOURCE_DESC *Src + ) +{ + BOOLEAN IsActive; + UINTN loopvar; + + BOOLEAN SciEn; + + IsActive = TRUE; + + SciEn = PchSmmGetSciEn (); + + if ((Src->Flags & PCH_SMM_SCI_EN_DEPENDENT) && (SciEn)) { + /// + /// This source is dependent on SciEn, and SciEn == 1. An ACPI OS is present, + /// so we shouldn't do anything w/ this source until SciEn == 0. + /// + IsActive = FALSE; + + } else { + /// + /// Read each bit desc from hardware and make sure it's a one + /// + for (loopvar = 0; loopvar < NUM_EN_BITS; loopvar++) { + + if (!IS_BIT_DESC_NULL (Src->En[loopvar])) { + + if (ReadBitDesc (&Src->En[loopvar]) == 0) { + IsActive = FALSE; + break; + /// + /// out of for loop + /// + } + + } + } + + if (IsActive) { + /// + /// Read each bit desc from hardware and make sure it's a one + /// + for (loopvar = 0; loopvar < NUM_STS_BITS; loopvar++) { + + if (!IS_BIT_DESC_NULL (Src->Sts[loopvar])) { + + if (ReadBitDesc (&Src->Sts[loopvar]) == 0) { + IsActive = FALSE; + break; + /// + /// out of for loop + /// + } + + } + } + } + } + + return IsActive; +} + +/** + Enable the SMI source event by set the SMI enable bit, this function would also clear SMI + status bit to make initial state is correct + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmEnableSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + UINTN loopvar; + + /// + /// Set enables to 1 by writing a 1 + /// + for (loopvar = 0; loopvar < NUM_EN_BITS; loopvar++) { + if (!IS_BIT_DESC_NULL (SrcDesc->En[loopvar])) { + WriteBitDesc (&SrcDesc->En[loopvar], 1, FALSE); + } + } + /// + /// Clear statuses to 0 by writing a 1 + /// + for (loopvar = 0; loopvar < NUM_STS_BITS; loopvar++) { + if (!IS_BIT_DESC_NULL (SrcDesc->Sts[loopvar])) { + WriteBitDesc (&SrcDesc->Sts[loopvar], 1, TRUE); + } + } +} + +/** + Disable the SMI source event by clear the SMI enable bit + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmDisableSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + UINTN loopvar; + + for (loopvar = 0; loopvar < NUM_EN_BITS; loopvar++) { + if (!IS_BIT_DESC_NULL (SrcDesc->En[loopvar])) { + WriteBitDesc (&SrcDesc->En[loopvar], 0, FALSE); + } + } +} + +/** + Clear the SMI status bit by set the source bit of SMI status register + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmClearSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + UINTN loopvar; + + for (loopvar = 0; loopvar < NUM_STS_BITS; loopvar++) { + if (!IS_BIT_DESC_NULL (SrcDesc->Sts[loopvar])) { + WriteBitDesc (&SrcDesc->Sts[loopvar], 1, TRUE); + } + } +} + +/** + Sets the source to a 1 and then waits for it to clear. + Be very careful when calling this function -- it will not + ASSERT. An acceptable case to call the function is when + waiting for the NEWCENTURY_STS bit to clear (which takes + 3 RTCCLKs). + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmClearSourceAndBlock ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + UINTN loopvar; + BOOLEAN IsSet; + + for (loopvar = 0; loopvar < NUM_STS_BITS; loopvar++) { + + if (!IS_BIT_DESC_NULL (SrcDesc->Sts[loopvar])) { + /// + /// Write the bit + /// + WriteBitDesc (&SrcDesc->Sts[loopvar], 1, TRUE); + + /// + /// Don't return until the bit actually clears. + /// + IsSet = TRUE; + while (IsSet) { + IsSet = ReadBitDesc (&SrcDesc->Sts[loopvar]); + /// + /// IsSet will eventually clear -- or else we'll have + /// an infinite loop. + /// + } + } + } +} + diff --git a/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmHelpers.h b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmHelpers.h new file mode 100644 index 0000000000..8a54816ccb --- /dev/null +++ b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmHelpers.h @@ -0,0 +1,143 @@ +/** @file + Helper functions for PCH SMM + + Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef PCH_SMM_HELPERS_H +#define PCH_SMM_HELPERS_H + +#include "PchSmm.h" +#include "PchxSmmHelpers.h" + +// +// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// SUPPORT / HELPER FUNCTIONS (PCH version-independent) +// + +/** + Publish SMI Dispatch protocols. + +**/ +VOID +PchSmmPublishDispatchProtocols ( + VOID + ); + +/** + Compare 2 SMM source descriptors' enable settings. + + @param[in] Src1 Pointer to the PCH SMI source description table 1 + @param[in] Src2 Pointer to the PCH SMI source description table 2 + + @retval TRUE The enable settings of the 2 SMM source descriptors are identical. + @retval FALSE The enable settings of the 2 SMM source descriptors are not identical. +**/ +BOOLEAN +CompareEnables ( + CONST IN PCH_SMM_SOURCE_DESC *Src1, + CONST IN PCH_SMM_SOURCE_DESC *Src2 + ); + +/** + Compare 2 SMM source descriptors' statuses. + + @param[in] Src1 Pointer to the PCH SMI source description table 1 + @param[in] Src2 Pointer to the PCH SMI source description table 2 + + @retval TRUE The statuses of the 2 SMM source descriptors are identical. + @retval FALSE The statuses of the 2 SMM source descriptors are not identical. +**/ +BOOLEAN +CompareStatuses ( + CONST IN PCH_SMM_SOURCE_DESC *Src1, + CONST IN PCH_SMM_SOURCE_DESC *Src2 + ); + +/** + Compare 2 SMM source descriptors, based on Enable settings and Status settings of them. + + @param[in] Src1 Pointer to the PCH SMI source description table 1 + @param[in] Src2 Pointer to the PCH SMI source description table 2 + + @retval TRUE The 2 SMM source descriptors are identical. + @retval FALSE The 2 SMM source descriptors are not identical. +**/ +BOOLEAN +CompareSources ( + CONST IN PCH_SMM_SOURCE_DESC *Src1, + CONST IN PCH_SMM_SOURCE_DESC *Src2 + ); + +/** + Check if an SMM source is active. + + @param[in] Src Pointer to the PCH SMI source description table + + @retval TRUE It is active. + @retval FALSE It is inactive. +**/ +BOOLEAN +SourceIsActive ( + CONST IN PCH_SMM_SOURCE_DESC *Src + ); + +/** + Enable the SMI source event by set the SMI enable bit, this function would also clear SMI + status bit to make initial state is correct + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmEnableSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + Disable the SMI source event by clear the SMI enable bit + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmDisableSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + Clear the SMI status bit by set the source bit of SMI status register + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmClearSource ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ); + +/** + Sets the source to a 1 and then waits for it to clear. + Be very careful when calling this function -- it will not + ASSERT. An acceptable case to call the function is when + waiting for the NEWCENTURY_STS bit to clear (which takes + 3 RTCCLKs). + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +PchSmmClearSourceAndBlock ( + CONST PCH_SMM_SOURCE_DESC *SrcDesc + ); + +#endif diff --git a/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmIchn.c b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmIchn.c new file mode 100644 index 0000000000..dd0061d035 --- /dev/null +++ b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmIchn.c @@ -0,0 +1,987 @@ +/** @file + File to contain all the hardware specific stuff for the Smm Ichn dispatch protocol. + + Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PchSmmHelpers.h" +#include "PlatformBaseAddresses.h" +#include + +PCH_SMM_SOURCE_DESC ICHN_SOURCE_DESCS[NUM_ICHN_TYPES] = { + /// + /// IchnMch + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnPme + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnRtcAlarm + /// + { + PCH_SMM_SCI_EN_DEPENDENT, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_PM1_EN + }, + S_PCH_ACPI_PM1_EN, + N_PCH_ACPI_PM1_EN_RTC + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_PM1_STS + }, + S_PCH_ACPI_PM1_STS, + N_PCH_ACPI_PM1_STS_RTC + } + } + }, + /// + /// IchnRingIndicate + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnAc97Wake + /// Not supported, + /// we just fill in invalid initializer and not use it. + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnSerialIrq + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_STS + }, + S_PCH_SMI_STS, + N_PCH_SMI_STS_ILB + } + } + }, + /// + /// IchnY2KRollover + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnTcoTimeout + /// + { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_GPE0a_EN + }, + S_PCH_ACPI_GPE0a_EN, + N_PCH_ACPI_GPE0a_EN_TCO + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_TCO_STS + }, + S_PCH_TCO_STS, + N_PCH_TCO_STS_TIMEOUT + } + } + }, + /// + /// IchnOsTco + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnNmi + /// + { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_GPE0a_EN + }, + S_PCH_ACPI_GPE0a_EN, + N_PCH_ACPI_GPE0a_EN_TCO + }, + { + { + MEMORY_MAPPED_IO_ADDRESS_TYPE, + (ILB_BASE_ADDRESS + R_PCH_ILB_GNMI) + }, + S_PCH_ILB_GNMI, + N_PCH_ILB_GNMI_NMI2SMIEN + } + }, + { + { + { + MEMORY_MAPPED_IO_ADDRESS_TYPE, + (ILB_BASE_ADDRESS + R_PCH_ILB_GNMI) + }, + S_PCH_ILB_GNMI, + N_PCH_ILB_GNMI_NMI2SMIST + } + } + }, + /// + /// IchnIntruderDetect + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnBiosWp + /// + { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_GPE0a_EN + }, + S_PCH_ACPI_GPE0a_EN, + N_PCH_ACPI_GPE0a_EN_TCO + }, + { + { + MEMORY_MAPPED_IO_ADDRESS_TYPE, + (SPI_BASE_ADDRESS + R_PCH_SPI_SCS) + }, + S_PCH_SPI_SCS, + N_PCH_SPI_SCS_SMIWPEN + } + }, + { + { + { + MEMORY_MAPPED_IO_ADDRESS_TYPE, + (SPI_BASE_ADDRESS + R_PCH_SPI_SCS) + }, + S_PCH_SPI_SCS, + N_PCH_SPI_SCS_SMIWPST + } + } + }, + /// + /// IchnMcSmi + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnPmeB0 + /// + { + PCH_SMM_SCI_EN_DEPENDENT, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_GPE0a_EN + }, + S_PCH_ACPI_GPE0a_EN, + N_PCH_ACPI_GPE0a_EN_PME_B0 + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_GPE0a_STS + }, + S_PCH_ACPI_GPE0a_STS, + N_PCH_ACPI_GPE0a_STS_PME_B0 + } + } + }, + /// + /// IchnThrmSts + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnSmBus + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_STS + }, + S_PCH_SMI_STS, + N_PCH_SMI_STS_SMBUS + } + } + }, + /// + /// IchnIntelUsb2 + /// + { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_EN + }, + S_PCH_SMI_EN, + N_PCH_SMI_EN_INTEL_USB2 + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_STS + }, + S_PCH_SMI_STS, + N_PCH_SMI_STS_INTEL_USB2 + } + } + }, + /// + /// IchnMonSmi7 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnMonSmi6 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnMonSmi5 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnMonSmi4 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap13 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap12, KBC_ACT_STS + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap11 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap10 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap9, PIRQDH_ACT_STS + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap8, PIRQCG_ACT_STS + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap7, PIRQBF_ACT_STS + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap6, PIRQAE_ACT_STS + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap5 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap3 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap2 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap1 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap0, IDE_ACT_STS + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// PCH I/O Trap register 3 monitor, + /// The "PCH_RCRB_BASE_NEED_FIX" should be fixed since the RCRB base should get from the RCBA register filled by platform module. + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// PCH I/O Trap register 2 monitor + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// PCH I/O Trap register 1 monitor + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// PCH I/O Trap register 0 monitor + /// + NULL_SOURCE_DESC_INITIALIZER, +}; + +PCH_SMM_SOURCE_DESC ICHN_SOURCE_DESCS_AX[NUM_ICHN_TYPES] = { + /// + /// IchnMch + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnPme + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnRtcAlarm + /// + { + PCH_SMM_SCI_EN_DEPENDENT, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_PM1_EN + }, + S_PCH_ACPI_PM1_EN, + N_PCH_ACPI_PM1_EN_RTC + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_PM1_STS + }, + S_PCH_ACPI_PM1_STS, + N_PCH_ACPI_PM1_STS_RTC + } + } + }, + /// + /// IchnRingIndicate + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnAc97Wake + /// Not supported, + /// we just fill in invalid initializer and not use it. + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnSerialIrq + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_STS + }, + S_PCH_SMI_STS, + N_PCH_SMI_STS_ILB + } + } + }, + /// + /// IchnY2KRollover + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnTcoTimeout + /// + { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_EN + }, + S_PCH_SMI_EN, + N_PCH_SMI_EN_TCO + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_TCO_STS + }, + S_PCH_TCO_STS, + N_PCH_TCO_STS_TIMEOUT + } + } + }, + /// + /// IchnOsTco + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnNmi + /// + { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_EN + }, + S_PCH_SMI_EN, + N_PCH_SMI_EN_TCO + }, + { + { + MEMORY_MAPPED_IO_ADDRESS_TYPE, + (ILB_BASE_ADDRESS + R_PCH_ILB_GNMI) + }, + S_PCH_ILB_GNMI, + N_PCH_ILB_GNMI_NMI2SMIEN + } + }, + { + { + { + MEMORY_MAPPED_IO_ADDRESS_TYPE, + (ILB_BASE_ADDRESS + R_PCH_ILB_GNMI) + }, + S_PCH_ILB_GNMI, + N_PCH_ILB_GNMI_NMI2SMIST + } + } + }, + /// + /// IchnIntruderDetect + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnBiosWp + /// + { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_EN + }, + S_PCH_SMI_EN, + N_PCH_SMI_EN_TCO + }, + { + { + MEMORY_MAPPED_IO_ADDRESS_TYPE, + (SPI_BASE_ADDRESS + R_PCH_SPI_SCS) + }, + S_PCH_SPI_SCS, + N_PCH_SPI_SCS_SMIWPEN + } + }, + { + { + { + MEMORY_MAPPED_IO_ADDRESS_TYPE, + (SPI_BASE_ADDRESS + R_PCH_SPI_SCS) + }, + S_PCH_SPI_SCS, + N_PCH_SPI_SCS_SMIWPST + } + } + }, + /// + /// IchnMcSmi + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnPmeB0 + /// + { + PCH_SMM_SCI_EN_DEPENDENT, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_GPE0a_EN + }, + S_PCH_ACPI_GPE0a_EN, + N_PCH_ACPI_GPE0a_EN_PME_B0 + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_GPE0a_STS + }, + S_PCH_ACPI_GPE0a_STS, + N_PCH_ACPI_GPE0a_STS_PME_B0 + } + } + }, + /// + /// IchnThrmSts + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnSmBus + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_STS + }, + S_PCH_SMI_STS, + N_PCH_SMI_STS_SMBUS + } + } + }, + /// + /// IchnIntelUsb2 + /// + { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_EN + }, + S_PCH_SMI_EN, + N_PCH_SMI_EN_INTEL_USB2 + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_STS + }, + S_PCH_SMI_STS, + N_PCH_SMI_STS_INTEL_USB2 + } + } + }, + /// + /// IchnMonSmi7 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnMonSmi6 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnMonSmi5 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnMonSmi4 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap13 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap12, KBC_ACT_STS + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap11 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap10 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap9, PIRQDH_ACT_STS + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap8, PIRQCG_ACT_STS + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap7, PIRQBF_ACT_STS + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap6, PIRQAE_ACT_STS + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap5 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap3 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap2 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap1 + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnDevTrap0, IDE_ACT_STS + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// PCH I/O Trap register 3 monitor, + /// The "PCH_RCRB_BASE_NEED_FIX" should be fixed since the RCRB base should get from the RCBA register filled by platform module. + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// PCH I/O Trap register 2 monitor + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// PCH I/O Trap register 1 monitor + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// PCH I/O Trap register 0 monitor + /// + NULL_SOURCE_DESC_INITIALIZER, +}; + +PCH_SMM_SOURCE_DESC ICHN_EX_SOURCE_DESCS[IchnExTypeMAX - IchnExPciExpress] = { + /// + /// IchnExPciExpress + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnExMonitor + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnExSpi + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnExQRT + /// + NULL_SOURCE_DESC_INITIALIZER, + /// + /// IchnExGpioUnlock + /// + { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + NULL_BIT_DESC_INITIALIZER + } + }, + /// + /// IchnExTmrOverflow + /// + { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_PM1_EN + }, + S_PCH_ACPI_PM1_EN, + N_PCH_ACPI_PM1_EN_TMROF + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_PM1_STS + }, + S_PCH_ACPI_PM1_STS, + N_PCH_ACPI_PM1_STS_TMROF + } + } + }, + +}; + +/// +/// TCO_STS bit that needs to be cleared +/// +PCH_SMM_SOURCE_DESC TCO_STS = { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_GPE0a_STS + }, + S_PCH_ACPI_GPE0a_STS, + N_PCH_ACPI_GPE0a_STS_TCO + } + } +}; + +PCH_SMM_SOURCE_DESC TCO_STS_AX = { + PCH_SMM_NO_FLAGS, + { + NULL_BIT_DESC_INITIALIZER, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_STS + }, + S_PCH_SMI_STS, + N_PCH_SMI_STS_TCO + } + } +}; + +/** + Clear the SMI status bit after the SMI handling is done + + @param[in] SrcDesc Pointer to the PCH SMI source description table + +**/ +VOID +EFIAPI +PchSmmIchnClearSource ( + PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + PchSmmClearSource (SrcDesc); + /// + /// Any TCO-based status bits require special handling. + /// SMI_STS.TCO_STS must be cleared in addition to the status bit in the TCO registers + /// + if (SocStepping () < SocB0) { + PchSmmClearSource (&TCO_STS_AX); + } else { + PchSmmClearSource (&TCO_STS); + } +} diff --git a/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmPeriodicTimer.c b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmPeriodicTimer.c new file mode 100644 index 0000000000..c8e43f2d86 --- /dev/null +++ b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmPeriodicTimer.c @@ -0,0 +1,555 @@ +/** @file + File to contain all the hardware specific stuff for the Periodical Timer dispatch protocol. + + Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PchSmmHelpers.h" + +// +// There is only one instance for PeriodicTimerCommBuffer. +// It's safe in SMM since there is no re-entry for the function. +// +EFI_SMM_PERIODIC_TIMER_CONTEXT mPchPeriodicTimerCommBuffer; + +typedef enum { + PERIODIC_TIMER= 0, + SWSMI_TIMER, + NUM_TIMERS +} SUPPORTED_TIMER; + +typedef struct _TIMER_INTERVAL { + UINT64 Interval; + UINT8 AssociatedTimer; +} TIMER_INTERVAL; + +#define NUM_INTERVALS 8 + +// +// Time constants, in 100 nano-second units +// +#define TIME_64s 640000000 ///< 64 s +#define TIME_32s 320000000 ///< 32 s +#define TIME_16s 160000000 ///< 16 s +#define TIME_8s 80000000 ///< 8 s +#define TIME_64ms 640000 ///< 64 ms +#define TIME_32ms 320000 ///< 32 ms +#define TIME_16ms 160000 ///< 16 ms +#define TIME_1_5ms 15000 ///< 1.5 ms + +typedef enum { + INDEX_TIME_64s = 0, + INDEX_TIME_32s, + INDEX_TIME_16s, + INDEX_TIME_8s, + INDEX_TIME_64ms, + INDEX_TIME_32ms, + INDEX_TIME_16ms, + INDEX_TIME_1_5ms, + INDEX_TIME_MAX +} TIMER_INTERVAL_INDEX; + +static TIMER_INTERVAL mSmmPeriodicTimerIntervals[NUM_INTERVALS] = { + { + TIME_64s, + PERIODIC_TIMER + }, + { + TIME_32s, + PERIODIC_TIMER + }, + { + TIME_16s, + PERIODIC_TIMER + }, + { + TIME_8s, + PERIODIC_TIMER + }, + { + TIME_64ms, + SWSMI_TIMER + }, + { + TIME_32ms, + SWSMI_TIMER + }, + { + TIME_16ms, + SWSMI_TIMER + }, + { + TIME_1_5ms, + SWSMI_TIMER + }, +}; + +typedef struct _TIMER_INFO { + UINTN NumChildren; ///< number of children using this timer + UINT64 MinReqInterval; ///< minimum interval required by children + UINTN CurrentSetting; ///< interval this timer is set at right now (index into interval table) +} TIMER_INFO; + +TIMER_INFO mTimers[NUM_TIMERS]; + +PCH_SMM_SOURCE_DESC mTIMER_SOURCE_DESCS[NUM_TIMERS] = { + { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_EN + }, + S_PCH_SMI_EN, + N_PCH_SMI_EN_PERIODIC + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_STS + }, + S_PCH_SMI_STS, + N_PCH_SMI_STS_PERIODIC + } + } + }, + { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_EN + }, + S_PCH_SMI_EN, + N_PCH_SMI_EN_SWSMI_TMR + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_STS + }, + S_PCH_SMI_STS, + N_PCH_SMI_STS_SWSMI_TMR + } + } + } +}; + +VOID +PchSmmPeriodicTimerProgramTimers ( + VOID + ); + +/** + Convert the dispatch context to the timer interval, this function will assert if then either: + (1) The context contains an invalid interval + (2) The timer interval table is corrupt + + @param[in] DispatchContext The pointer to the Dispatch Context + + @retval TIMER_INTERVAL The timer interval of input dispatch context +**/ +TIMER_INTERVAL * +ContextToTimerInterval ( + IN PCH_SMM_CONTEXT *DispatchContext + ) +{ + UINTN loopvar; + + /// + /// Determine which timer this child is using + /// + for (loopvar = 0; loopvar < NUM_INTERVALS; loopvar++) { + if (((DispatchContext->PeriodicTimer.SmiTickInterval == 0) && + (DispatchContext->PeriodicTimer.Period >= mSmmPeriodicTimerIntervals[loopvar].Interval)) || + (DispatchContext->PeriodicTimer.SmiTickInterval == mSmmPeriodicTimerIntervals[loopvar].Interval)) { + return &mSmmPeriodicTimerIntervals[loopvar]; + } + } + /// + /// If this assertion fires, then either: + /// (1) the context contains an invalid interval + /// (2) the timer interval table is corrupt + /// + ASSERT (FALSE); + + return NULL; +} + +/** + Figure out which timer the child is requesting and + send back the source description + + @param[in] DispatchContext The pointer to the Dispatch Context instances + @param[out] SrcDesc The pointer to the source description + +**/ +VOID +MapPeriodicTimerToSrcDesc ( + IN PCH_SMM_CONTEXT *DispatchContext, + OUT PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + TIMER_INTERVAL *TimerInterval; + + /// + /// Figure out which timer the child is requesting and + /// send back the source description + /// + TimerInterval = ContextToTimerInterval (DispatchContext); + if (TimerInterval == NULL) { + return; + } + + CopyMem ( + (VOID *) SrcDesc, + (VOID *) (&mTIMER_SOURCE_DESCS[TimerInterval->AssociatedTimer]), + sizeof (PCH_SMM_SOURCE_DESC) + ); + + /// + /// Program the value of the interval into hardware + /// + PchSmmPeriodicTimerProgramTimers (); +} + +/** + Update the elapsed time from the Interval data of DATABASE_RECORD + + @param[in] Record The pointer to the DATABASE_RECORD. + @param[out] HwContext The Context to be updated. + +**/ +VOID +EFIAPI +PeriodicTimerGetContext ( + IN DATABASE_RECORD *Record, + OUT PCH_SMM_CONTEXT *HwContext + ) +{ + TIMER_INTERVAL *TimerInterval; + + ASSERT (Record->ProtocolType == PeriodicTimerType); + + TimerInterval = ContextToTimerInterval (&Record->ChildContext); + if (TimerInterval == NULL) { + return; + } + /// + /// Ignore the hardware context. It's not required for this protocol. + /// Instead, just increment the child's context. + /// Update the elapsed time w/ the data from our tables + /// +#if (defined X64_BUILD_SUPPORT) && (X64_BUILD_SUPPORT == 1) + Record->MiscData.ElapsedTime += TimerInterval->Interval; +#else + Record->MiscData.ElapsedTime += (UINT32)TimerInterval->Interval; +#endif + *HwContext = Record->ChildContext; +} + +/** + Check whether Periodic Timer of two contexts match + + @param[in] Context1 Context 1 that includes Periodic Timer 1 + @param[in] Context2 Context 2 that includes Periodic Timer 2 + + @retval FALSE Periodic Timer match + @retval TRUE Periodic Timer don't match +**/ +BOOLEAN +EFIAPI +PeriodicTimerCmpContext ( + IN PCH_SMM_CONTEXT *HwContext, + IN PCH_SMM_CONTEXT *ChildContext + ) +{ + DATABASE_RECORD *Record; + Record = DATABASE_RECORD_FROM_CHILDCONTEXT (ChildContext); + + if (Record->MiscData.ElapsedTime >= ChildContext->PeriodicTimer.Period) { + /// + /// This child should be dispatched + /// Need reset ElapsedTime, or SMI handler will be invoked during SmiTickInterval instead of Period. + /// + /// + /// For EDKII, the ElapsedTime is reset when PeriodicTimerGetCommBuffer + /// + return TRUE; + } else { + return FALSE; + } +} + +/** + Gather the CommBuffer information of SmmPeriodicTimerDispatch2. + + @param[in] Record No use + @param[out] CommBuffer Point to the CommBuffer structure + @param[out] CommBufferSize Point to the Size of CommBuffer structure + +**/ +VOID +EFIAPI +PeriodicTimerGetCommBuffer ( + IN DATABASE_RECORD *Record, + OUT VOID **CommBuffer, + OUT UINTN *CommBufferSize + ) +{ + ASSERT (Record->ProtocolType == PeriodicTimerType); + + mPchPeriodicTimerCommBuffer.ElapsedTime = Record->MiscData.ElapsedTime; + + /// + /// For EDKII, the ElapsedTime is reset here + /// + Record->MiscData.ElapsedTime = 0; + + /// + /// Return the CommBuffer + /// + *CommBuffer = (VOID *) &mPchPeriodicTimerCommBuffer; + *CommBufferSize = sizeof (EFI_SMM_PERIODIC_TIMER_CONTEXT); +} + +/** + Program Smm Periodic Timer + +**/ +VOID +PchSmmPeriodicTimerProgramTimers ( + VOID + ) +{ + UINT8 GenPmCon1; + UINT8 GenPmCon2; + SUPPORTED_TIMER Timer; + DATABASE_RECORD *RecordInDb; + LIST_ENTRY *LinkInDb; + UINTN PciD31F0RegBase; + UINT32 PmcBase; + TIMER_INTERVAL *TimerInterval; + + PciD31F0RegBase = MmPciAddress (0, + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_LPC, + PCI_FUNCTION_NUMBER_PCH_LPC, + 0 + ); + PmcBase = MmioRead32 (PciD31F0RegBase + R_PCH_LPC_PMC_BASE) & B_PCH_LPC_PMC_BASE_BAR; + /// + /// Find the minimum required interval for each timer + /// + for (Timer = 0; Timer < NUM_TIMERS; Timer++) { + mTimers[Timer].MinReqInterval = ~ (UINT64) 0x0; + mTimers[Timer].NumChildren = 0; + } + + LinkInDb = GetFirstNode (&mPrivateData.CallbackDataBase); + while (!IsNull (&mPrivateData.CallbackDataBase, LinkInDb)) { + RecordInDb = DATABASE_RECORD_FROM_LINK (LinkInDb); + if (RecordInDb->ProtocolType == PeriodicTimerType) { + /// + /// This child is registerd with the PeriodicTimer protocol + /// + TimerInterval = ContextToTimerInterval (&RecordInDb->ChildContext); + if (TimerInterval == NULL) { + return; + } + + Timer = TimerInterval->AssociatedTimer; + if (Timer < 0 || Timer >= NUM_TIMERS) { + ASSERT (FALSE); + CpuDeadLoop (); + } + + if (mTimers[Timer].MinReqInterval > RecordInDb->ChildContext.PeriodicTimer.SmiTickInterval) { + mTimers[Timer].MinReqInterval = RecordInDb->ChildContext.PeriodicTimer.SmiTickInterval; + } + + mTimers[Timer].NumChildren++; + } + + LinkInDb = GetNextNode (&mPrivateData.CallbackDataBase, &RecordInDb->Link); + } + /// + /// Program the hardware + /// + if (mTimers[PERIODIC_TIMER].NumChildren > 0) { + GenPmCon2 = MmioRead8 (PmcBase + R_PCH_PMC_GEN_PMCON_2); + + GenPmCon2 &= ~B_PCH_PMC_GEN_PMCON_PER_SMI_SEL; + switch (mTimers[PERIODIC_TIMER].MinReqInterval) { + case TIME_64s: + GenPmCon2 |= V_PCH_PMC_GEN_PMCON_PER_SMI_64S; + mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_64s; + break; + + case TIME_32s: + GenPmCon2 |= V_PCH_PMC_GEN_PMCON_PER_SMI_32S; + mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_32s; + break; + + case TIME_16s: + GenPmCon2 |= V_PCH_PMC_GEN_PMCON_PER_SMI_16S; + mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_16s; + break; + + case TIME_8s: + GenPmCon2 |= V_PCH_PMC_GEN_PMCON_PER_SMI_8S; + mTimers[PERIODIC_TIMER].CurrentSetting = INDEX_TIME_8s; + break; + + default: + ASSERT (FALSE); + break; + } + + MmioWrite8 (PmcBase + R_PCH_PMC_GEN_PMCON_2, GenPmCon2); + + /// + /// Restart the timer here, just need to clear the SMI + /// + PchSmmClearSource (&mTIMER_SOURCE_DESCS[PERIODIC_TIMER]); + } else { + PchSmmDisableSource (&mTIMER_SOURCE_DESCS[PERIODIC_TIMER]); + } + + if (mTimers[SWSMI_TIMER].NumChildren > 0) { + /// + /// ICH9, ICH10 and PCH share the same bit positions for SW SMI Rate settings + /// + GenPmCon1 = MmioRead8 (PmcBase + R_PCH_PMC_GEN_PMCON_1); + GenPmCon1 &= ~B_PCH_PMC_GEN_PMCON_SWSMI_RTSL; + switch (mTimers[SWSMI_TIMER].MinReqInterval) { + case TIME_64ms: + GenPmCon1 |= V_PCH_PMC_GEN_PMCON_SWSMI_RTSL_64MS; + mTimers[SWSMI_TIMER].CurrentSetting = INDEX_TIME_64ms; + break; + + case TIME_32ms: + GenPmCon1 |= V_PCH_PMC_GEN_PMCON_SWSMI_RTSL_32MS; + mTimers[SWSMI_TIMER].CurrentSetting = INDEX_TIME_32ms; + break; + + case TIME_16ms: + GenPmCon1 |= V_PCH_PMC_GEN_PMCON_SWSMI_RTSL_16MS; + mTimers[SWSMI_TIMER].CurrentSetting = INDEX_TIME_16ms; + break; + + case TIME_1_5ms: + GenPmCon1 |= V_PCH_PMC_GEN_PMCON_SWSMI_RTSL_1_5MS; + mTimers[SWSMI_TIMER].CurrentSetting = INDEX_TIME_1_5ms; + break; + + default: + ASSERT (FALSE); + break; + } + + MmioWrite8 (PmcBase + R_PCH_PMC_GEN_PMCON_1, GenPmCon1); + + /// + /// Restart the timer here, need to disable, clear, then enable to restart this timer + /// + PchSmmDisableSource (&mTIMER_SOURCE_DESCS[SWSMI_TIMER]); + PchSmmClearSource (&mTIMER_SOURCE_DESCS[SWSMI_TIMER]); + PchSmmEnableSource (&mTIMER_SOURCE_DESCS[SWSMI_TIMER]); + } else { + PchSmmDisableSource (&mTIMER_SOURCE_DESCS[SWSMI_TIMER]); + } +} + +/** + This services returns the next SMI tick period that is supported by the chipset. + The order returned is from longest to shortest interval period. + + @param[in] This Pointer to the EFI_SMM_PERIODIC_TIMER_DISPATCH_PROTOCOL instance. + @param[in,out] SmiTickInterval Pointer to pointer of the next shorter SMI interval period that is supported by the child. + + @retval EFI_SUCCESS The service returned successfully. + @retval EFI_INVALID_PARAMETER The parameter SmiTickInterval is invalid. +**/ +EFI_STATUS +PchSmmPeriodicTimerDispatchGetNextShorterInterval ( + IN CONST EFI_SMM_PERIODIC_TIMER_DISPATCH2_PROTOCOL *This, + IN OUT UINT64 **SmiTickInterval + ) +{ + TIMER_INTERVAL *IntervalPointer; + + if (SmiTickInterval == NULL) { + ASSERT (FALSE); + return EFI_INVALID_PARAMETER; + } + + IntervalPointer = (TIMER_INTERVAL *) *SmiTickInterval; + + if (IntervalPointer == NULL) { + /// + /// The first time child requesting an interval + /// + IntervalPointer = &mSmmPeriodicTimerIntervals[0]; + } else if (IntervalPointer == &mSmmPeriodicTimerIntervals[NUM_INTERVALS - 1]) { + /// + /// At end of the list + /// + IntervalPointer = NULL; + } else { + if ((IntervalPointer >= &mSmmPeriodicTimerIntervals[0]) && + (IntervalPointer < &mSmmPeriodicTimerIntervals[NUM_INTERVALS - 1]) + ) { + /// + /// Get the next interval in the list + /// + IntervalPointer++; + } else { + /// + /// Input is out of range + /// + return EFI_INVALID_PARAMETER; + } + } + + if (IntervalPointer != NULL) { + *SmiTickInterval = &IntervalPointer->Interval; + } else { + *SmiTickInterval = NULL; + } + + return EFI_SUCCESS; +} + +/** + This function is responsible for calculating and enabling any timers that are required + to dispatch messages to children. The SrcDesc argument isn't acutally used. + + @param[in] SrcDesc Pointer to the PCH_SMM_SOURCE_DESC instance. + +**/ +VOID +EFIAPI +PchSmmPeriodicTimerClearSource ( + IN PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + PchSmmPeriodicTimerProgramTimers (); +} diff --git a/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmPowerButton.c b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmPowerButton.c new file mode 100644 index 0000000000..efe8c61fc0 --- /dev/null +++ b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmPowerButton.c @@ -0,0 +1,95 @@ +/** @file + File to contain all the hardware specific stuff for the Smm Power Button dispatch protocol. + + Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include + +CONST PCH_SMM_SOURCE_DESC POWER_BUTTON_SOURCE_DESC = { + PCH_SMM_SCI_EN_DEPENDENT, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_PM1_EN + }, + S_PCH_ACPI_PM1_EN, + N_PCH_ACPI_PM1_EN_PWRBTN + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_ACPI_PM1_STS + }, + S_PCH_ACPI_PM1_STS, + N_PCH_ACPI_PM1_STS_PWRBTN + } + } +}; + +/** + Get the power button status. + + @param[in] Record The pointer to the DATABASE_RECORD. + @param[out] Context Calling context from the hardware, will be updated with the current power button status. + +**/ +VOID +EFIAPI +PowerButtonGetContext ( + IN DATABASE_RECORD *Record, + OUT PCH_SMM_CONTEXT *Context + ) +{ + UINT16 GenPmCon2; + UINT32 PmcBase; + + PmcBase = MmioRead32 ( + MmPciAddress (0, + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_LPC, + PCI_FUNCTION_NUMBER_PCH_LPC, + R_PCH_LPC_PMC_BASE + ) + ) & B_PCH_LPC_PMC_BASE_BAR; + + GenPmCon2 = MmioRead16 (PmcBase + R_PCH_PMC_GEN_PMCON_2); + + if ((GenPmCon2 & B_PCH_PMC_GEN_PMCON_PWRBTN_LVL) == 0) { + Context->PowerButton.Phase = EfiPowerButtonExit; + } else { + Context->PowerButton.Phase = EfiPowerButtonEntry; + } +} + +/** + Check whether Power Button status of two contexts match + + @param[in] Context1 Context 1 that includes Power Button status 1 + @param[in] Context2 Context 2 that includes Power Button status 2 + + @retval FALSE Power Button status match + @retval TRUE Power Button status don't match +**/ +BOOLEAN +EFIAPI +PowerButtonCmpContext ( + IN PCH_SMM_CONTEXT *Context1, + IN PCH_SMM_CONTEXT *Context2 + ) +{ + return (BOOLEAN) (Context1->PowerButton.Phase == Context2->PowerButton.Phase); +} diff --git a/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmSw.c b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmSw.c new file mode 100644 index 0000000000..eedc3b811c --- /dev/null +++ b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmSw.c @@ -0,0 +1,161 @@ +/** @file + File to contain all the hardware specific stuff for the Smm Sw dispatch protocol. + + Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PchSmmHelpers.h" +#include "PlatformBaseAddresses.h" +#include +// +// There is only one instance for SwCommBuffer. +// It's safe in SMM since there is no re-entry for the function. +// +EFI_SMM_SW_CONTEXT mPchSwCommBuffer; +EFI_SMM_CPU_PROTOCOL *mSmmCpuProtocol; + +CONST PCH_SMM_SOURCE_DESC SW_SOURCE_DESC = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_EN + }, + S_PCH_SMI_EN, + N_PCH_SMI_EN_APMC + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_STS + }, + S_PCH_SMI_STS, + N_PCH_SMI_STS_APM + } + } +}; + +/** + Get the Software Smi value + + @param[in] Record No use + @param[out] Context The context that includes Software Smi value to be filled + +**/ +VOID +EFIAPI +SwGetContext ( + IN DATABASE_RECORD *Record, + OUT PCH_SMM_CONTEXT *Context + ) +{ + UINT8 ApmCnt; + + ApmCnt = IoRead8 ((UINTN) R_PCH_APM_CNT); + + Context->Sw.SwSmiInputValue = ApmCnt; +} + +/** + Check whether software SMI value of two contexts match + + @param[in] Context1 Context 1 that includes software SMI value 1 + @param[in] Context2 Context 2 that includes software SMI value 2 + + @retval FALSE Software SMI value match + @retval TRUE Software SMI value don't match +**/ +BOOLEAN +EFIAPI +SwCmpContext ( + IN PCH_SMM_CONTEXT *Context1, + IN PCH_SMM_CONTEXT *Context2 + ) +{ + return (BOOLEAN) (Context1->Sw.SwSmiInputValue == Context2->Sw.SwSmiInputValue); +} +/** + Gather the CommBuffer information of SmmSwDispatch2. + + @param[in] Record No use + @param[out] CommBuffer Point to the CommBuffer structure + @param[out] CommBufferSize Point to the Size of CommBuffer structure + +**/ +VOID +EFIAPI +SwGetCommBuffer ( + IN DATABASE_RECORD *Record, + OUT VOID **CommBuffer, + OUT UINTN *CommBufferSize + ) +{ + EFI_STATUS Status; + EFI_SMM_SAVE_STATE_IO_INFO SmiIoInfo; + UINTN Index; + + ASSERT (Record->ProtocolType == SwType); + + mPchSwCommBuffer.CommandPort = IoRead8 (R_PCH_APM_CNT); + mPchSwCommBuffer.DataPort = IoRead8 (R_PCH_APM_STS); + + // + // Try to find which CPU trigger SWSMI + // + mPchSwCommBuffer.SwSmiCpuIndex = 0; + for (Index = 0; Index < gSmst->NumberOfCpus; Index++) { + Status = mSmmCpuProtocol->ReadSaveState ( + mSmmCpuProtocol, + sizeof(EFI_SMM_SAVE_STATE_IO_INFO), + EFI_SMM_SAVE_STATE_REGISTER_IO, + Index, + &SmiIoInfo + ); + if (EFI_ERROR (Status)) { + continue; + } + if (SmiIoInfo.IoPort == R_PCH_APM_CNT) { + // + // Find matched CPU. + // + mPchSwCommBuffer.SwSmiCpuIndex = Index; + break; + } + } + + /// + /// Return the CommBuffer + /// + *CommBuffer = (VOID *) &mPchSwCommBuffer; + *CommBufferSize = sizeof (EFI_SMM_SW_CONTEXT); +} + +/** + Init required protocol for Pch Sw Dispatch protocol. + +**/ +VOID +PchSwDispatchInit ( + VOID + ) +{ + EFI_STATUS Status; + // + // Locate PI SMM CPU protocol + // + Status = gSmst->SmmLocateProtocol (&gEfiSmmCpuProtocolGuid, NULL, (VOID **)&mSmmCpuProtocol); + ASSERT_EFI_ERROR (Status); +} diff --git a/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmSx.c b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmSx.c new file mode 100644 index 0000000000..daa7ad86f7 --- /dev/null +++ b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmSx.c @@ -0,0 +1,257 @@ +/** @file + File to contain all the hardware specific stuff for the Smm Sx dispatch protocol. + + Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PchSmmHelpers.h" +#ifndef PROGRESS_CODE_S3_SUSPEND_END +#define PROGRESS_CODE_S3_SUSPEND_END (EFI_SOFTWARE_SMM_DRIVER | (EFI_OEM_SPECIFIC | 0x00000001)) +#endif +#include + +CONST PCH_SMM_SOURCE_DESC SX_SOURCE_DESC = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_EN + }, + S_PCH_SMI_EN, + N_PCH_SMI_EN_ON_SLP_EN + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_STS + }, + S_PCH_SMI_STS, + N_PCH_SMI_STS_ON_SLP_EN + } + } +}; + +/** + Get the Sleep type + + @param[in] Record No use + @param[out] Context The context that includes SLP_TYP bits to be filled + +**/ +VOID +EFIAPI +SxGetContext ( + IN DATABASE_RECORD *Record, + OUT PCH_SMM_CONTEXT *Context + ) +{ + UINT32 Pm1Cnt; + + Pm1Cnt = IoRead32 ((UINTN) (AcpiBase + R_PCH_ACPI_PM1_CNT)); + + /// + /// By design, the context phase will always be ENTRY + /// + Context->Sx.Phase = SxEntry; + + /// + /// Map the PM1_CNT register's SLP_TYP bits to the context type + /// + switch (Pm1Cnt & B_PCH_ACPI_PM1_CNT_SLP_TYP) { + case V_PCH_ACPI_PM1_CNT_S0: + Context->Sx.Type = SxS0; + break; + + case V_PCH_ACPI_PM1_CNT_S1: + Context->Sx.Type = SxS1; + break; + + case V_PCH_ACPI_PM1_CNT_S3: + Context->Sx.Type = SxS3; + break; + + case V_PCH_ACPI_PM1_CNT_S4: + Context->Sx.Type = SxS4; + break; + + case V_PCH_ACPI_PM1_CNT_S5: + Context->Sx.Type = SxS5; + break; + + default: + ASSERT (FALSE); + break; + } +} + +/** + Check whether sleep type of two contexts match + + @param[in] Context1 Context 1 that includes sleep type 1 + @param[in] Context2 Context 2 that includes sleep type 2 + + @retval FALSE Sleep types match + @retval TRUE Sleep types don't match +**/ +BOOLEAN +EFIAPI +SxCmpContext ( + IN PCH_SMM_CONTEXT *Context1, + IN PCH_SMM_CONTEXT *Context2 + ) +{ + return (BOOLEAN) (Context1->Sx.Type == Context2->Sx.Type); +} + +/** + Additional xHCI Controller Configurations Prior to Entering S3/S4/S5 + +**/ +VOID +XhciSxWorkaround ( + VOID + ) +{ + UINTN PciD31F0RegBase; + UINT32 PmcBase; + UINTN XhciPciMmBase; + + PciD31F0RegBase = MmPciAddress (0, + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_LPC, + PCI_FUNCTION_NUMBER_PCH_LPC, + 0 + ); + PmcBase = MmioRead32 (PciD31F0RegBase + R_PCH_LPC_PMC_BASE) & B_PCH_LPC_PMC_BASE_BAR; + /// + /// Check if XHCI controller is enabled + /// + if ((MmioRead32 (PmcBase + R_PCH_PMC_FUNC_DIS) & (UINT32) B_PCH_PMC_FUNC_DIS_USH) != 0) { + return ; + } + XhciPciMmBase = MmPciAddress ( + 0, + DEFAULT_PCI_BUS_NUMBER_PCH, + PCI_DEVICE_NUMBER_PCH_XHCI, + PCI_FUNCTION_NUMBER_PCH_XHCI, + 0 + ); + /// + /// Set "PME Enable" bit of PWR_CNTL_STS register, D20:F0:74h[8] = 1h + /// + MmioOr16 ((XhciPciMmBase + R_PCH_XHCI_PWR_CNTL_STS), (UINT16) (B_PCH_XHCI_PWR_CNTL_STS_PME_EN)); +} + +/** + When we get an SMI that indicates that we are transitioning to a sleep state, + we need to actually transition to that state. We do this by disabling the + "SMI on sleep enable" feature, which generates an SMI when the operating system + tries to put the system to sleep, and then physically putting the system to sleep. + +**/ +VOID +PchSmmSxGoToSleep ( + VOID + ) +{ + UINT32 Pm1Cnt; + + /// + /// Flush cache into memory before we go to sleep. It is necessary for S3 sleep + /// because we may update memory in SMM Sx sleep handlers -- the updates are in cache now + /// + AsmWbinvd (); + + /// + /// Disable SMIs + /// + PchSmmClearSource (&SX_SOURCE_DESC); + PchSmmDisableSource (&SX_SOURCE_DESC); + + /// + /// Get Power Management 1 Control Register Value + /// + Pm1Cnt = IoRead32 ((UINTN) (AcpiBase + R_PCH_ACPI_PM1_CNT)); + + if ((Pm1Cnt & B_PCH_ACPI_PM1_CNT_SLP_TYP) == V_PCH_ACPI_PM1_CNT_S3) { + // + // Report status code before goto S3 sleep + // + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeS3SuspendEnd)); + + // + // Flush cache into memory before we go to sleep + // + AsmWbinvd (); + } + /// + /// CHV BIOS Spec, Section 18.2.6 + /// Additional Programming Requirements prior to enter S4/S5 + /// + if (((Pm1Cnt & B_PCH_ACPI_PM1_CNT_SLP_TYP) == V_PCH_ACPI_PM1_CNT_S3) || + ((Pm1Cnt & B_PCH_ACPI_PM1_CNT_SLP_TYP) == V_PCH_ACPI_PM1_CNT_S4) || + ((Pm1Cnt & B_PCH_ACPI_PM1_CNT_SLP_TYP) == V_PCH_ACPI_PM1_CNT_S5)) { + XhciSxWorkaround (); + } + /// + /// Record S3 suspend performance data + /// + if ((Pm1Cnt & B_PCH_ACPI_PM1_CNT_SLP_TYP) == V_PCH_ACPI_PM1_CNT_S3) { + /// + /// Report status code before goto S3 sleep + /// + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PROGRESS_CODE_S3_SUSPEND_END); + + /// + /// Flush cache into memory before we go to sleep. + /// + AsmWbinvd (); + } + + /// + /// Now that SMIs are disabled, write to the SLP_EN bit again to trigger the sleep + /// + Pm1Cnt |= B_PCH_ACPI_PM1_CNT_SLP_EN; + + IoWrite32 ((UINTN) (AcpiBase + R_PCH_ACPI_PM1_CNT), Pm1Cnt); + + /// + /// Should only proceed if wake event is generated. + /// + if ((Pm1Cnt & B_PCH_ACPI_PM1_CNT_SLP_TYP) == V_PCH_ACPI_PM1_CNT_S1) { + while (((IoRead16 ((UINTN) (AcpiBase + R_PCH_ACPI_PM1_STS))) & B_PCH_ACPI_PM1_STS_WAK) == 0x0); + } else { + CpuDeadLoop (); + } + /// + /// The system just went to sleep. If the sleep state was S1, then code execution will resume + /// here when the system wakes up. + /// + Pm1Cnt = IoRead32 ((UINTN) (AcpiBase + R_PCH_ACPI_PM1_CNT)); + + if ((Pm1Cnt & B_PCH_ACPI_PM1_CNT_SCI_EN) == 0) { + /// + /// An ACPI OS isn't present, clear the sleep information + /// + Pm1Cnt &= ~B_PCH_ACPI_PM1_CNT_SLP_TYP; + Pm1Cnt |= V_PCH_ACPI_PM1_CNT_S0; + + IoWrite32 ((UINTN) (AcpiBase + R_PCH_ACPI_PM1_CNT), Pm1Cnt); + } + + PchSmmClearSource (&SX_SOURCE_DESC); + PchSmmEnableSource (&SX_SOURCE_DESC); +} diff --git a/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmUsb.c b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmUsb.c new file mode 100644 index 0000000000..4fba82cc5e --- /dev/null +++ b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchSmmUsb.c @@ -0,0 +1,249 @@ +/** @file + File to contain all the hardware specific stuff for the Smm USB dispatch protocol. + + Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PchSmmHelpers.h" +#include + +PCH_SMM_SOURCE_DESC mUSB2_WAKE = { + PCH_SMM_SCI_EN_DEPENDENT, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_EN + }, + S_PCH_SMI_EN, + N_PCH_SMI_EN_INTEL_USB2 + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_STS + }, + S_PCH_SMI_STS, + N_PCH_SMI_STS_INTEL_USB2 + } + } +}; + +PCH_SMM_SOURCE_DESC mUSB2_LEGACY = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_EN + }, + S_PCH_SMI_EN, + N_PCH_SMI_EN_LEGACY_USB + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_STS + }, + S_PCH_SMI_STS, + N_PCH_SMI_STS_ILB + } + } +}; + +PCH_SMM_SOURCE_DESC mUSB3_LEGACY = { + PCH_SMM_NO_FLAGS, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_EN + }, + S_PCH_SMI_EN, + N_PCH_SMI_EN_LEGACY_USB + }, + NULL_BIT_DESC_INITIALIZER + }, + { + { + { + ACPI_ADDR_TYPE, + R_PCH_SMI_STS + }, + S_PCH_SMI_STS, + N_PCH_SMI_STS_LEGACY_USB + } + } +}; + +typedef enum { + PchUsbControllerLpc0 = 0, + PchUsbControllerXhci, + PchUsbControllerTypeMax +} PCH_USB_CONTROLLER_TYPE; + +typedef struct { + UINT8 Function; + UINT8 Device; + PCH_USB_CONTROLLER_TYPE UsbConType; +} USB_CONTROLLER; + +USB_CONTROLLER mUsbControllersMap[] = { + { + PCI_FUNCTION_NUMBER_PCH_LPC, + PCI_DEVICE_NUMBER_PCH_LPC, + PchUsbControllerLpc0 + }, + { + PCI_FUNCTION_NUMBER_PCH_XHCI, + PCI_DEVICE_NUMBER_PCH_XHCI, + PchUsbControllerXhci + } +}; + +/** + Find the handle that best matches the input Device Path and return the USB controller type + + @param[in] DevicePath Pointer to the device Path table + @param[out] Controller Returned with the USB controller type of the input device path + + @retval EFI_SUCCESS Find the handle that best matches the input Device Path + @exception EFI_UNSUPPORTED Invalid device Path table or can't find any match USB device path + PCH_USB_CONTROLLER_TYPE The USB controller type of the input + device path +**/ +EFI_STATUS +DevicePathToSupportedController ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT PCH_USB_CONTROLLER_TYPE *Controller + ) +{ + EFI_STATUS Status; + EFI_HANDLE DeviceHandle; + ACPI_HID_DEVICE_PATH *AcpiNode; + PCI_DEVICE_PATH *PciNode; + EFI_DEVICE_PATH_PROTOCOL *RemaingDevicePath; + UINT8 UsbIndex; + /// + /// Find the handle that best matches the Device Path. If it is only a + /// partial match the remaining part of the device path is returned in + /// RemainingDevicePath. + /// + RemaingDevicePath = DevicePath; + Status = gBS->LocateDevicePath ( + &gEfiPciRootBridgeIoProtocolGuid, + &DevicePath, + &DeviceHandle + ); + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + DevicePath = RemaingDevicePath; + + /// + /// Get first node: Acpi Node + /// + AcpiNode = (ACPI_HID_DEVICE_PATH *) RemaingDevicePath; + + if (AcpiNode->Header.Type != ACPI_DEVICE_PATH || + AcpiNode->Header.SubType != ACPI_DP || + DevicePathNodeLength (&AcpiNode->Header) != sizeof (ACPI_HID_DEVICE_PATH) || + AcpiNode->HID != EISA_PNP_ID (0x0A03) || + AcpiNode->UID != 0 + ) { + return EFI_UNSUPPORTED; + } else { + /// + /// Get the next node: Pci Node + /// + RemaingDevicePath = NextDevicePathNode (RemaingDevicePath); + PciNode = (PCI_DEVICE_PATH *) RemaingDevicePath; + if (PciNode->Header.Type != HARDWARE_DEVICE_PATH || + PciNode->Header.SubType != HW_PCI_DP || + DevicePathNodeLength (&PciNode->Header) != sizeof (PCI_DEVICE_PATH) + ) { + return EFI_UNSUPPORTED; + } + + for (UsbIndex = 0; UsbIndex < sizeof (mUsbControllersMap) / sizeof (USB_CONTROLLER); UsbIndex++) { + if ((PciNode->Device == mUsbControllersMap[UsbIndex].Device) && + (PciNode->Function == mUsbControllersMap[UsbIndex].Function)) { + *Controller = mUsbControllersMap[UsbIndex].UsbConType; + return EFI_SUCCESS; + } + } + + return EFI_UNSUPPORTED; + } +} + +/** + Maps a USB context to a source description. + + @param[in] Context The context we need to map. Type must be USB. + @param[in] SrcDesc The source description that corresponds to the given context. + + +**/ +VOID +MapUsbToSrcDesc ( + IN PCH_SMM_CONTEXT *Context, + OUT PCH_SMM_SOURCE_DESC *SrcDesc + ) +{ + PCH_USB_CONTROLLER_TYPE Controller; + EFI_STATUS Status; + + Status = DevicePathToSupportedController (Context->Usb.Device, &Controller); + /// + /// Either the device path passed in by the child is incorrect or + /// the ones stored here internally are incorrect. + /// + ASSERT_EFI_ERROR (Status); + + switch (Context->Usb.Type) { + case UsbLegacy: + switch (Controller) { + case PchUsbControllerLpc0: + CopyMem ((VOID *) SrcDesc, (VOID *) (&mUSB2_LEGACY), sizeof (PCH_SMM_SOURCE_DESC)); + break; + case PchUsbControllerXhci: + CopyMem ((VOID *) SrcDesc, (VOID *) (&mUSB3_LEGACY), sizeof (PCH_SMM_SOURCE_DESC)); + break; + + default: + ASSERT (FALSE); + break; + } + break; + + case UsbWake: + switch (Controller) { + + default: + ASSERT (FALSE); + break; + } + break; + + default: + ASSERT (FALSE); + break; + } +} diff --git a/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchxSmmHelpers.c b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchxSmmHelpers.c new file mode 100644 index 0000000000..6cb899ea44 --- /dev/null +++ b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchxSmmHelpers.c @@ -0,0 +1,730 @@ +/** @file + This driver is responsible for the registration of child drivers + and the abstraction of the PCH SMI sources. + + Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "PchSmmHelpers.h" + +// +// Help handle porting bit shifts to IA-64. +// +#define BIT_ZERO 0x00000001 + +/** + Publish SMI Dispatch protocols. + +**/ +VOID +PchSmmPublishDispatchProtocols ( + VOID + ) +{ + EFI_STATUS Status = EFI_SUCCESS; + + UINTN Index; + // + // Install protocol interfaces. + // + for (Index = 0; Index < PchSmmProtocolTypeMax; Index++) { + Status = gSmst->SmmInstallProtocolInterface ( + &mPrivateData.InstallMultProtHandle, + mPrivateData.Protocols[Index].Guid, + EFI_NATIVE_INTERFACE, + &mPrivateData.Protocols[Index].Protocols.Generic + ); + } + ASSERT_EFI_ERROR (Status); +} + +/** + Initialize bits that aren't necessarily related to an SMI source. + + @retval EFI_SUCCESS SMI source initialization completed. + @retval Asserts Global Smi Bit is not enabled successfully. +**/ +EFI_STATUS +PchSmmInitHardware ( + VOID + ) +{ + EFI_STATUS Status; + + /// + /// Clear all SMIs + /// + PchSmmClearSmi (); + + Status = PchSmmEnableGlobalSmiBit (); + ASSERT_EFI_ERROR (Status); + + /// + /// Be *really* sure to clear all SMIs + /// + PchSmmClearSmi (); + + return EFI_SUCCESS; +} + +/** + Enables the PCH to generate SMIs. Note that no SMIs will be generated + if no SMI sources are enabled. Conversely, no enabled SMI source will + generate SMIs if SMIs are not globally enabled. This is the main + switchbox for SMI generation. + + @retval EFI_SUCCESS Enable Global Smi Bit completed +**/ +EFI_STATUS +PchSmmEnableGlobalSmiBit ( + VOID + ) +{ + UINT32 SmiEn; + + SmiEn = IoRead32 ((UINTN) (AcpiBase + R_PCH_SMI_EN)); + + /// + /// Set the "global smi enable" bit + /// + SmiEn |= B_PCH_SMI_EN_GBL_SMI; + + IoWrite32 ((UINTN) (AcpiBase + R_PCH_SMI_EN), SmiEn); + + return EFI_SUCCESS; +} + +/** + Clears the SMI after all SMI source have been processed. + Note that this function will not work correctly (as it is + written) unless all SMI sources have been processed. + A revision of this function could manually clear all SMI + status bits to guarantee success. + + @retval EFI_SUCCESS Clears the SMIs completed + @retval Asserts EOS was not set to a 1 +**/ +EFI_STATUS +PchSmmClearSmi ( + VOID + ) +{ + BOOLEAN EosSet; + BOOLEAN SciEn; + UINT32 Pm1Cnt; + UINT16 Pm1Sts; + UINT32 Gpe0aStsLow; + UINT32 SmiSts; + UINT16 AltGpiSmiSts; + UINT32 TcoSts; + + /// + /// Determine whether an ACPI OS is present (via the SCI_EN bit) + /// + Pm1Cnt = IoRead32 ((UINTN) (AcpiBase + R_PCH_ACPI_PM1_CNT)); + SciEn = (BOOLEAN) ((Pm1Cnt & B_PCH_ACPI_PM1_CNT_SCI_EN) == B_PCH_ACPI_PM1_CNT_SCI_EN); + if (!SciEn) { + /// + /// Clear any SMIs that double as SCIs (when SCI_EN==0) + /// + Pm1Sts = IoRead16 ((UINTN) (AcpiBase + R_PCH_ACPI_PM1_STS)); + Gpe0aStsLow = IoRead32 ((UINTN) (AcpiBase + R_PCH_ACPI_GPE0a_STS)); + + Pm1Sts |= + ( + B_PCH_ACPI_PM1_STS_WAK | +#ifdef PCIESC_SUPPORT + B_PCH_ACPI_PM1_STS_WAK_PCIE | +#endif + B_PCH_ACPI_PM1_STS_PRBTNOR | + B_PCH_ACPI_PM1_STS_RTC | + B_PCH_ACPI_PM1_STS_PWRBTN | + B_PCH_ACPI_PM1_STS_GBL | + B_PCH_ACPI_PM1_STS_TMROF + ); + + Gpe0aStsLow |= + ( + B_PCH_ACPI_GPE0a_STS_TCO | + B_PCH_ACPI_GPE0a_STS_PME_B0 | + B_PCH_ACPI_GPE0a_STS_BATLOW | +#ifdef PCIESC_SUPPORT + B_PCH_ACPI_GPE0a_STS_PCI_EXP | + B_PCH_ACPI_GPE0a_STS_PCIE_WAKE3 | + B_PCH_ACPI_GPE0a_STS_PCIE_WAKE2 | + B_PCH_ACPI_GPE0a_STS_PCIE_WAKE1 | + B_PCH_ACPI_GPE0a_STS_PCIE_WAKE0 | +#endif + B_PCH_ACPI_GPE0a_STS_GUNIT_SCI | + B_PCH_ACPI_GPE0a_STS_PUNIT_SCI | + B_PCH_ACPI_GPE0a_STS_SWGPE | + B_PCH_ACPI_GPE0a_STS_HOT_PLUG | + B_PCH_ACPI_GPE0a_STS_PMU_WAKEB + ); + + IoWrite16 ((UINTN) (AcpiBase + R_PCH_ACPI_PM1_STS), (UINT16) Pm1Sts); + IoWrite32 ((UINTN) (AcpiBase + R_PCH_ACPI_GPE0a_STS), (UINT32) Gpe0aStsLow); + } + /// + /// Clear all SMIs that are unaffected by SCI_EN + /// + AltGpiSmiSts = IoRead16 ((UINTN) (AcpiBase + R_PCH_ALT_GP_SMI_STS)); + SmiSts = IoRead32 ((UINTN) (AcpiBase + R_PCH_SMI_STS)); + TcoSts = IoRead32 ((UINTN) (AcpiBase + R_PCH_TCO_STS)); + + SmiSts |= + ( + B_PCH_SMI_STS_SMBUS | + B_PCH_SMI_STS_PERIODIC | + B_PCH_SMI_STS_TCO | + B_PCH_SMI_STS_SWSMI_TMR | + B_PCH_SMI_STS_APM | + B_PCH_SMI_STS_ON_SLP_EN | + B_PCH_SMI_STS_BIOS + ); + AltGpiSmiSts |= (B_PCH_ALT_GP_SMI_STS_CORE_GPIO | B_PCH_ALT_GP_SMI_STS_SUS_GPIO); + + TcoSts |= + ( + B_PCH_TCO_STS_SECOND_TO | + B_PCH_TCO_STS_TIMEOUT + ); + + IoWrite16 ((UINTN) (AcpiBase + R_PCH_ALT_GP_SMI_STS), AltGpiSmiSts); + + IoWrite32 ((UINTN) (AcpiBase + R_PCH_TCO_STS), TcoSts); + + IoWrite32 ((UINTN) (AcpiBase + R_PCH_SMI_STS), SmiSts); + + /// + /// Try to clear the EOS bit. ASSERT on an error + /// + EosSet = PchSmmSetAndCheckEos (); + ASSERT (EosSet); + + return EFI_SUCCESS; +} + +/** + Set the SMI EOS bit after all SMI source have been processed. + + @retval FALSE EOS was not set to a 1; this is an error + @retval TRUE EOS was correctly set to a 1 +**/ +BOOLEAN +PchSmmSetAndCheckEos ( + VOID + ) +{ + UINT32 SmiEn; + + SmiEn = IoRead32 ((UINTN) (AcpiBase + R_PCH_SMI_EN)); + + /// + /// Reset the PCH to generate subsequent SMIs + /// + SmiEn |= B_PCH_SMI_EN_EOS; + + IoWrite32 ((UINTN) (AcpiBase + R_PCH_SMI_EN), SmiEn); + + /// + /// Double check that the assert worked + /// + SmiEn = IoRead32 ((UINTN) (AcpiBase + R_PCH_SMI_EN)); + + /// + /// Return TRUE if EOS is set correctly + /// + if ((SmiEn & B_PCH_SMI_EN_EOS) == 0) { + /// + /// EOS was not set to a 1; this is an error + /// + return FALSE; + } else { + /// + /// EOS was correctly set to a 1 + /// + return TRUE; + } +} + +/** + Determine whether an ACPI OS is present (via the SCI_EN bit) + + @retval TRUE ACPI OS is present + @retval FALSE ACPI OS is not present +**/ +BOOLEAN +PchSmmGetSciEn ( + VOID + ) +{ + BOOLEAN SciEn; + UINT32 Pm1Cnt; + + /// + /// Determine whether an ACPI OS is present (via the SCI_EN bit) + /// + Pm1Cnt = IoRead32 ((UINTN) (AcpiBase + R_PCH_ACPI_PM1_CNT)); + SciEn = (BOOLEAN) ((Pm1Cnt & B_PCH_ACPI_PM1_CNT_SCI_EN) == B_PCH_ACPI_PM1_CNT_SCI_EN); + + return SciEn; +} + +/** + Read a specifying bit with the register + These may or may not need to change w/ the PCH version; they're highly IA-32 dependent, though. + + @param[in] BitDesc The struct that includes register address, size in byte and bit number + + @retval TRUE The bit is enabled + @retval FALSE The bit is disabled +**/ +BOOLEAN +ReadBitDesc ( + CONST PCH_SMM_BIT_DESC *BitDesc + ) +{ + EFI_STATUS Status; + UINT64 Register; +#ifdef PCIESC_SUPPORT + UINT32 PciBus; + UINT32 PciDev; + UINT32 PciFun; + UINT32 PciReg; +#endif + UINTN RegSize; + BOOLEAN BitWasOne; + UINTN ShiftCount; + UINTN RegisterOffset; + + if ((BitDesc == NULL) || + IS_BIT_DESC_NULL (*BitDesc)) { + ASSERT (FALSE); + return FALSE; + } + + RegSize = 0; + Register = 0; + ShiftCount = 0; + BitWasOne = FALSE; + + switch (BitDesc->Reg.Type) { + case ACPI_ADDR_TYPE: + switch (BitDesc->SizeInBytes) { + case 0: + /// + /// Chances are that this field didn't get initialized. + /// Check your assignments to bit descriptions. + /// + ASSERT (FALSE); + break; + + case 1: + RegSize = SMM_IO_UINT8; + break; + + case 2: + RegSize = SMM_IO_UINT16; + break; + + case 4: + RegSize = SMM_IO_UINT32; + break; + + case 8: + RegSize = SMM_IO_UINT64; + break; + + default: + /// + /// Unsupported or invalid register size + /// + ASSERT (FALSE); + break; + } + /// + /// Double check that we correctly read in the ACPI base address + /// + ASSERT ((AcpiBase != 0x0) && ((AcpiBase & B_PCH_LPC_ACPI_BASE_EN) != B_PCH_LPC_ACPI_BASE_EN)); + + RegisterOffset = BitDesc->Reg.Data.acpi; + ShiftCount = BitDesc->Bit; + /// + /// As current CPU Smm Io can only support at most + /// 32-bit read/write,if Operation is 64 bit, + /// we do a 32 bit operation according to BitDesc->Bit + /// + if (RegSize == SMM_IO_UINT64) { + RegSize = SMM_IO_UINT32; + /// + /// If the operation is for high 32 bits + /// + if (BitDesc->Bit >= 32) { + RegisterOffset += 4; + ShiftCount -= 32; + } + } + + Status = gSmst->SmmIo.Io.Read ( + &gSmst->SmmIo, + RegSize, + AcpiBase + RegisterOffset, + 1, + &Register + ); + ASSERT_EFI_ERROR (Status); + + if ((Register & (LShiftU64 (BIT_ZERO, ShiftCount))) != 0) { + BitWasOne = TRUE; + } else { + BitWasOne = FALSE; + } + break; + + case MEMORY_MAPPED_IO_ADDRESS_TYPE: + /// + /// Read the register, and it with the bit to read + /// + switch (BitDesc->SizeInBytes) { + case 1: + Register = (UINT64) MmioRead8 ((UINTN) BitDesc->Reg.Data.Mmio); + break; + + case 2: + Register = (UINT64) MmioRead16 ((UINTN) BitDesc->Reg.Data.Mmio); + break; + + case 4: + Register = (UINT64) MmioRead32 ((UINTN) BitDesc->Reg.Data.Mmio); + break; + + case 8: + Register = (UINT64) MmioRead32 ((UINTN) BitDesc->Reg.Data.Mmio); + *((UINT32 *) (&Register) + 1) = MmioRead32 ((UINTN) BitDesc->Reg.Data.Mmio + 4); + break; + + default: + /// + /// Unsupported or invalid register size + /// + ASSERT (FALSE); + break; + } + + Register = Register & (LShiftU64 (BIT0, BitDesc->Bit)); + if (Register) { + BitWasOne = TRUE; + } else { + BitWasOne = FALSE; + } + break; + +#ifdef PCIESC_SUPPORT + case PCIE_ADDR_TYPE: + PciBus = BitDesc->Reg.Data.pcie.Fields.Bus; + PciDev = BitDesc->Reg.Data.pcie.Fields.Dev; + PciFun = BitDesc->Reg.Data.pcie.Fields.Fnc; + PciReg = BitDesc->Reg.Data.pcie.Fields.Reg; + switch (BitDesc->SizeInBytes) { + + case 0: + /// + /// Chances are that this field didn't get initialized. + /// Check your assignments to bit descriptions. + /// + ASSERT (FALSE); + break; + + case 1: + Register = (UINT64) MmioRead8 (MmPciAddress (0, PciBus, PciDev, PciFun, PciReg)); + break; + + case 2: + Register = (UINT64) MmioRead16 (MmPciAddress (0, PciBus, PciDev, PciFun, PciReg)); + break; + + case 4: + Register = (UINT64) MmioRead32 (MmPciAddress (0, PciBus, PciDev, PciFun, PciReg)); + break; + + default: + /// + /// Unsupported or invalid register size + /// + ASSERT (FALSE); + break; + } + + if ((Register & (LShiftU64 (BIT_ZERO, BitDesc->Bit))) != 0) { + BitWasOne = TRUE; + } else { + BitWasOne = FALSE; + } + break; +#endif + + default: + /// + /// This address type is not yet implemented + /// + ASSERT (FALSE); + break; + } + + return BitWasOne; +} + +/** + Write a specifying bit with the register + + @param[in] BitDesc The struct that includes register address, size in byte and bit number + @param[in] ValueToWrite The value to be wrote + @param[in] WriteClear If the rest bits of the register is write clear + + +**/ +VOID +WriteBitDesc ( + CONST PCH_SMM_BIT_DESC *BitDesc, + CONST BOOLEAN ValueToWrite, + CONST BOOLEAN WriteClear + ) +{ + EFI_STATUS Status; + UINT64 Register; + UINT64 AndVal; + UINT64 OrVal; + UINT32 RegSize; +#ifdef PCIESC_SUPPORT + UINT32 PciBus; + UINT32 PciDev; + UINT32 PciFun; + UINT32 PciReg; +#endif + UINTN RegisterOffset; + + ASSERT (BitDesc != NULL); + + if ((BitDesc == NULL) || + IS_BIT_DESC_NULL (*BitDesc)) + { + ASSERT (FALSE); + return; + } + + RegSize = 0; + Register = 0; + + if (WriteClear) { + AndVal = LShiftU64 (BIT_ZERO, BitDesc->Bit); + } else { + AndVal = ~(LShiftU64 (BIT_ZERO, BitDesc->Bit)); + } + + OrVal = (LShiftU64 ((UINT32) ValueToWrite, BitDesc->Bit)); + + switch (BitDesc->Reg.Type) { + + case ACPI_ADDR_TYPE: + switch (BitDesc->SizeInBytes) { + + case 0: + /// + /// Chances are that this field didn't get initialized. + /// Check your assignments to bit descriptions. + /// + ASSERT (FALSE); + break; + + case 1: + RegSize = SMM_IO_UINT8; + break; + + case 2: + RegSize = SMM_IO_UINT16; + break; + + case 4: + RegSize = SMM_IO_UINT32; + break; + + case 8: + RegSize = SMM_IO_UINT64; + break; + + default: + /// + /// Unsupported or invalid register size + /// + ASSERT (FALSE); + break; + } + /// + /// Double check that we correctly read in the ACPI base address + /// + ASSERT ((AcpiBase != 0x0) && ((AcpiBase & B_PCH_LPC_ACPI_BASE_EN) != B_PCH_LPC_ACPI_BASE_EN)); + + RegisterOffset = BitDesc->Reg.Data.acpi; + /// + /// As current CPU Smm Io can only support at most + /// 32-bit read/write,if Operation is 64 bit, + /// we do a 32 bit operation according to BitDesc->Bit + /// + if (RegSize == SMM_IO_UINT64) { + RegSize = SMM_IO_UINT32; + /// + /// If the operation is for high 32 bits + /// + if (BitDesc->Bit >= 32) { + RegisterOffset += 4; + + if (WriteClear) { + AndVal = LShiftU64 (BIT_ZERO, BitDesc->Bit - 32); + } else { + AndVal = ~(LShiftU64 (BIT_ZERO, BitDesc->Bit - 32)); + } + + OrVal = LShiftU64 ((UINT32) ValueToWrite, BitDesc->Bit - 32); + } + } + + Status = gSmst->SmmIo.Io.Read ( + &gSmst->SmmIo, + RegSize, + AcpiBase + RegisterOffset, + 1, + &Register + ); + ASSERT_EFI_ERROR (Status); + + Register &= AndVal; + Register |= OrVal; + + Status = gSmst->SmmIo.Io.Write ( + &gSmst->SmmIo, + RegSize, + AcpiBase + RegisterOffset, + 1, + &Register + ); + ASSERT_EFI_ERROR (Status); + break; + + case MEMORY_MAPPED_IO_ADDRESS_TYPE: + /// + /// Read the register, or it with the bit to set, then write it back. + /// + switch (BitDesc->SizeInBytes) { + case 1: + Register = (UINT64) MmioRead8 ((UINTN) BitDesc->Reg.Data.Mmio); + break; + + case 2: + Register = (UINT64) MmioRead16 ((UINTN) BitDesc->Reg.Data.Mmio); + break; + + case 4: + Register = (UINT64) MmioRead32 ((UINTN) BitDesc->Reg.Data.Mmio); + break; + + case 8: + Register = (UINT64) MmioRead32 ((UINTN) BitDesc->Reg.Data.Mmio); + *((UINT32 *) (&Register) + 1) = MmioRead32 ((UINTN) BitDesc->Reg.Data.Mmio + 4); + break; + + default: + /// + /// Unsupported or invalid register size + /// + ASSERT (FALSE); + break; + } + + Register &= AndVal; + Register |= OrVal; + /// + /// Read the register, or it with the bit to set, then write it back. + /// + switch (BitDesc->SizeInBytes) { + case 1: + MmioWrite8 ((UINTN) BitDesc->Reg.Data.Mmio, (UINT8) Register); + break; + + case 2: + MmioWrite16 ((UINTN) BitDesc->Reg.Data.Mmio, (UINT16) Register); + break; + + case 4: + MmioWrite32 ((UINTN) BitDesc->Reg.Data.Mmio, (UINT32) Register); + break; + + case 8: + MmioWrite32 ((UINTN) BitDesc->Reg.Data.Mmio, (UINT32) Register); + MmioWrite32 ((UINTN) BitDesc->Reg.Data.Mmio + 4, *((UINT32 *) (&Register) + 1)); + break; + + default: + /// + /// Unsupported or invalid register size + /// + ASSERT (FALSE); + break; + } + break; + +#ifdef PCIESC_SUPPORT + case PCIE_ADDR_TYPE: + PciBus = BitDesc->Reg.Data.pcie.Fields.Bus; + PciDev = BitDesc->Reg.Data.pcie.Fields.Dev; + PciFun = BitDesc->Reg.Data.pcie.Fields.Fnc; + PciReg = BitDesc->Reg.Data.pcie.Fields.Reg; + switch (BitDesc->SizeInBytes) { + + case 0: + /// + /// Chances are that this field didn't get initialized -- check your assignments + /// to bit descriptions. + /// + ASSERT (FALSE); + break; + + case 1: + MmioAndThenOr8 (MmPciAddress (0, PciBus, PciDev, PciFun, PciReg), (UINT8) AndVal, (UINT8) OrVal); + break; + + case 2: + MmioAndThenOr16 (MmPciAddress (0, PciBus, PciDev, PciFun, PciReg), (UINT16) AndVal, (UINT16) OrVal); + break; + + case 4: + MmioAndThenOr32 (MmPciAddress (0, PciBus, PciDev, PciFun, PciReg), (UINT32) AndVal, (UINT32) OrVal); + break; + + default: + /// + /// Unsupported or invalid register size + /// + ASSERT (FALSE); + break; + } + break; +#endif + + default: + /// + /// This address type is not yet implemented + /// + ASSERT (FALSE); + break; + } +} diff --git a/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchxSmmHelpers.h b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchxSmmHelpers.h new file mode 100644 index 0000000000..fccd7bcc48 --- /dev/null +++ b/ChvRefCodePkg/CherryViewSoc/SouthCluster/PchSmiDispatcher/Smm/PchxSmmHelpers.h @@ -0,0 +1,112 @@ +/** @file + This driver is responsible for the registration of child drivers + and the abstraction of the PCH SMI sources. + + Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.
+ + This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php. + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef _PCHX_SMM_HELPERS_H_ +#define _PCHX_SMM_HELPERS_H_ + +#include "PchSmm.h" +#include + +/** + Initialize bits that aren't necessarily related to an SMI source. + + @retval EFI_SUCCESS SMI source initialization completed. + @retval Asserts Global Smi Bit is not enabled successfully. +**/ +EFI_STATUS +PchSmmInitHardware ( + VOID + ); + +/** + Enables the PCH to generate SMIs. Note that no SMIs will be generated + if no SMI sources are enabled. Conversely, no enabled SMI source will + generate SMIs if SMIs are not globally enabled. This is the main + switchbox for SMI generation. + + @retval EFI_SUCCESS Enable Global Smi Bit completed +**/ +EFI_STATUS +PchSmmEnableGlobalSmiBit ( + VOID + ); + +/** + Clears the SMI after all SMI source have been processed. + Note that this function will not work correctly (as it is + written) unless all SMI sources have been processed. + A revision of this function could manually clear all SMI + status bits to guarantee success. + + @retval EFI_SUCCESS Clears the SMIs completed + @retval Asserts EOS was not set to a 1 +**/ +EFI_STATUS +PchSmmClearSmi ( + VOID + ); + +/** + Set the SMI EOS bit after all SMI source have been processed. + + @retval FALSE EOS was not set to a 1; this is an error + @retval TRUE EOS was correctly set to a 1 +**/ +BOOLEAN +PchSmmSetAndCheckEos ( + VOID + ); + +/** + Determine whether an ACPI OS is present (via the SCI_EN bit) + + @retval TRUE ACPI OS is present + @retval FALSE ACPI OS is not present +**/ +BOOLEAN +PchSmmGetSciEn ( + VOID + ); + +/** + Read a specifying bit with the register + + @param[in] BitDesc The struct that includes register address, size in byte and bit number + + @retval TRUE The bit is enabled + @retval FALSE The bit is disabled +**/ +BOOLEAN +ReadBitDesc ( + CONST PCH_SMM_BIT_DESC *BitDesc + ); + +/** + Write a specifying bit with the register + + @param[in] BitDesc The struct that includes register address, size in byte and bit number + @param[in] ValueToWrite The value to be wrote + @param[in] WriteClear If the rest bits of the register is write clear + +**/ +VOID +WriteBitDesc ( + CONST PCH_SMM_BIT_DESC *BitDesc, + CONST BOOLEAN ValueToWrite, + CONST BOOLEAN WriteClear + ); + +#endif -- cgit v1.2.3