/** @file
  Prototypes and defines for the PCH SMM Dispatcher.

Copyright (c) 2017 - 2019, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/
#ifndef PCH_SMM_H
#define PCH_SMM_H

#include <Uefi.h>
#include <Protocol/PciRootBridgeIo.h>
#include <Protocol/LoadedImage.h>
#include <Protocol/SmmControl2.h>
#include <Protocol/SmmUsbDispatch2.h>
#include <Protocol/SmmSxDispatch2.h>
#include <Protocol/SmmSwDispatch2.h>
#include <Protocol/SmmGpiDispatch2.h>
#include <Protocol/SmmPowerButtonDispatch2.h>
#include <Protocol/SmmPeriodicTimerDispatch2.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/DxeServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Library/IoLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DevicePathLib.h>
#include <Library/SmmServicesTableLib.h>
#include <Library/ReportStatusCodeLib.h>
#include <Library/PerformanceLib.h>
#include <Protocol/SmmReadyToLock.h>
#include <IndustryStandard/Pci30.h>
#include <PchAccess.h>
#include <Library/PchCycleDecodingLib.h>
#include <Library/PchPcieRpLib.h>
#include <Library/PchPcrLib.h>
#include <Library/MmPciLib.h>
#include <Library/GpioLib.h>
#include <Library/PchInfoLib.h>
#include <Library/PchEspiLib.h>
#include <Library/GpioPrivateLib.h>
#include <Protocol/PchTcoSmiDispatch.h>
#include <Protocol/PchPcieSmiDispatch.h>
#include <Protocol/PchAcpiSmiDispatch.h>
#include <Protocol/PchGpioUnlockSmiDispatch.h>
#include <Protocol/PchSmiDispatch.h>
#include <Protocol/PchEspiSmiDispatch.h>
#include "IoTrap.h"

#include <Library/SmiHandlerProfileLib.h>

#define EFI_BAD_POINTER          0xAFAFAFAFAFAFAFAFULL

extern BOOLEAN                   mReadyToLock;

///
/// Define an enumeration for all the supported protocols
///
#define PCH_SMM_PROTOCOL_TYPE_MAX       6

typedef enum {
  UsbType,
  SxType,
  SwType,
  GpiType,
  PowerButtonType,
  PeriodicTimerType,
  PchSmiDispatchType,
  PchSmmProtocolTypeMax
} PCH_SMM_PROTOCOL_TYPE;

///
/// Define all the supported types of PCH SMI
///
typedef enum {
  PchTcoSmiMchType,
  PchTcoSmiTcoTimeoutType,
  PchTcoSmiOsTcoType,
  PchTcoSmiNmiType,
  PchTcoSmiIntruderDetectType,
  PchTcoSmiSpiBiosWpType,
  PchTcoSmiLpcBiosWpType,
  PchTcoSmiNewCenturyType,
  PchPcieSmiRpHotplugType,
  PchPcieSmiRpLinkActiveType,
  PchPcieSmiRpLinkEqType,
  PchAcpiSmiPmeType,
  PchAcpiSmiPmeB0Type,
  PchAcpiSmiRtcAlarmType,
  PchAcpiSmiTmrOverflowType,
  PchGpioUnlockSmiType,
  PchEspiSmiEspiSlaveType,
  PchSmiSerialIrqType,
  PchSmiMcSmiType,
  PchSmiSmBusType,
  PchSmiSpiAsyncType,
  PchIoTrapSmiType                      ///< internal SMI type
} PCH_SMI_TYPES;

///
/// Generic funciton pointer to cover all Pch SMI function pointer types
///
typedef
VOID
(EFIAPI *PCH_SMI_CALLBACK_FUNCTIONS) (
  IN EFI_HANDLE                         DispatchHandle,
  ...
  );


///
/// 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 I/O)    Only if we want to
///  GPIO  (special case of MMIO)    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,
  TCO_ADDR_TYPE,
  ///
  ///  MEMORY_ADDR_TYPE, /// unimplemented
  ///
  GPIO_ADDR_TYPE,
  MEMORY_MAPPED_IO_ADDRESS_TYPE,
  PCIE_ADDR_TYPE,
  PCR_ADDR_TYPE,
  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;
typedef MEM_ADDR *GPIO_ADDR;
typedef union {
  UINT32  Raw;
  struct {
    UINT32 Reg: 16;
    UINT32 Fnc: 3;
    UINT32 Dev: 5;
    UINT32 Bus: 8;
  } Fields;
} PCIE_ADDR;

typedef union {
  UINT32  Raw;
  struct {
    UINT16 Offset;
    UINT8  Pid;
    UINT8  Base;
  } Fields;
} PCR_ADDR;

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;
    GPIO_ADDR                 gpio;
    MEM_ADDR                  mem;
    MEMORY_MAPPED_IO_ADDRESS  Mmio;
    PCIE_ADDR                 pcie;
    PCR_ADDR                  Pcr;

  } 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 to describe the event source
//
#define PCH_SMM_NO_FLAGS          0
#define PCH_SMM_SCI_EN_DEPENDENT  1

typedef struct {
  PCH_SMM_SOURCE_FLAGS  Flags;
  PCH_SMM_BIT_DESC      En[NUM_EN_BITS];    ///< Describes the enable bit(s) for the SMI event
  PCH_SMM_BIT_DESC      Sts[NUM_STS_BITS];  ///< Describes the secondary status bit for the SMI event. Might be the same as TopLevelSmi
  PCH_SMM_BIT_DESC      PmcSmiSts;          ///< Refereing to the top level status bit in PMC SMI_STS, i.e. R_PCH_SMI_STS
} PCH_SMM_SOURCE_DESC;

///
/// Used to initialize null source descriptor
///
#define NULL_SOURCE_DESC_INITIALIZER \
  { \
    PCH_SMM_NO_FLAGS, \
    { \
      NULL_BIT_DESC_INITIALIZER, 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_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[PCH_SMM_PROTOCOL_TYPE_MAX];

///
/// 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:
//
extern CONST PCH_SMM_SOURCE_DESC  PCH_GPI_SOURCE_DESC_TEMPLATE;

///
/// 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;
  EFI_GUID                      *ProtocolGuid;

  ///
  /// Misc data for private usage
  ///
  PCH_SMM_MISC_DATA             MiscData;

  ///
  /// PCH SMI callback function
  ///
  PCH_SMI_CALLBACK_FUNCTIONS    PchSmiCallback;
  ///
  /// Indicate the PCH SMI types.
  ///
  PCH_SMI_TYPES                 PchSmiType;
};

#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
EFIAPI
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 PCH_SMM_GENERIC_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
EFIAPI
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;
} 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[PCH_SMM_PROTOCOL_TYPE_MAX];
} PRIVATE_DATA;

extern PRIVATE_DATA           mPrivateData;
extern UINT16                 mAcpiBaseAddr;
extern UINT16                 mTcoBaseAddr;
/**
  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_DISPATCH2_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
  );

/**
  Install PCH SMM periodic timer control protocol

  @param[in] Handle                     handle for this driver

  @retval EFI_SUCCESS                   Driver initialization completed successfully
**/
EFI_STATUS
EFIAPI
InstallPchSmmPeriodicTimerControlProtocol (
  IN EFI_HANDLE                         Handle
  );

/**
  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
  );

/**
  Check and clear NEWCENTURY_STS.
  It will clear the SMI status once it happens.

  @param[in] SrcDesc                    Pointer to the PCH SMI source description table
**/
VOID
EFIAPI
PchTcoClearNewCenturySts (
  PCH_SMM_SOURCE_DESC                   *SrcDesc
  );

/**
  Install protocols of PCH specifics SMI types, including
  PCH TCO SMI types, PCH PCIE SMI types, PCH ACPI SMI types, PCH MISC SMI types.

  @retval                               the result of protocol installation
**/
EFI_STATUS
InstallPchSmiDispatchProtocols (
  VOID
  );

/**
  The function to dispatch all callback function of PCH SMI types.

  @retval EFI_SUCCESS                   Function successfully completed
  @retval EFI_UNSUPPORTED               no
**/
EFI_STATUS
PchSmiTypeCallbackDispatcher (
  IN  DATABASE_RECORD                   *Record
  );

/**
  The register function used to register SMI handler of IoTrap event.
  This is internal function and only used by Iotrap module.

  @param[in]  DispatchFunction          Pointer to dispatch function to be invoked for this SMI source
  @param[in]  IoTrapIndex               Index number of IOTRAP register
  @param[out] DispatchHandle            Handle of dispatch function to register.

  @retval EFI_INVALID_PARAMETER         Error with NULL SMI source description
  @retval EFI_OUT_OF_RESOURCES          Fail to allocate pool for database record
  @retval EFI_SUCCESS                   The database record is created successfully.
**/
EFI_STATUS
PchInternalIoTrapSmiRegister (
  IN  PCH_SMI_DISPATCH_CALLBACK         DispatchFunction,
  IN  UINTN                             IoTrapIndex,
  OUT EFI_HANDLE                        *DispatchHandle
  );

/**
  Unregister a child SMI source dispatch function with a parent SMM driver

  @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
PchInternalIoTrapSmiUnRegister (
  IN  EFI_HANDLE                        DispatchHandle
  );

/**
  Register an eSPI SMI handler based on the type

  @param[in]  DispatchFunction        Callback in an event of eSPI SMI
  @param[in]  PchSmiTypes             The eSPI type published by PchSmiDispatch
  @param[out] DispatchHandle          The callback handle

  @retval     EFI_INVALID_PARAMETER   Error with NULL SMI source description
  @retval     EFI_OUT_OF_RESOURCES    Fail to allocate pool for database record
  @retval     EFI_SUCCESS             Registration is successful.
**/
EFI_STATUS
PchInternalEspiSmiRegister (
  IN  PCH_SMI_DISPATCH_CALLBACK         DispatchFunction,
  IN  PCH_SMI_TYPES                     PchSmiTypes,
  OUT EFI_HANDLE                        *DispatchHandle
  );

/**
  Unregister an eSPI SMI handler

  @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
PchInternalEspiSmiUnRegister (
  IN  EFI_HANDLE                        DispatchHandle
  );

/**
  The internal function used to create and insert a database record
  for SMI record of Pch Smi types.

  @param[in]  SrcDesc                   The pointer to the SMI source description
  @param[in]  DispatchFunction          Pointer to dispatch function to be invoked for this SMI source
  @param[in]  PchSmiType                Specific SMI type of PCH SMI
  @param[out] DispatchHandle            Handle of dispatch function to register.

  @retval EFI_INVALID_PARAMETER         Error with NULL SMI source description
  @retval EFI_OUT_OF_RESOURCES          Fail to allocate pool for database record
  @retval EFI_SUCCESS                   The database record is created successfully.
**/
EFI_STATUS
PchSmiRecordInsert (
  IN  EFI_GUID                          *ProtocolGuid,
  IN  PCH_SMM_SOURCE_DESC               *SrcDesc,
  IN  PCH_SMI_CALLBACK_FUNCTIONS        DispatchFunction,
  IN  PCH_SMI_TYPES                     PchSmiType,
  OUT EFI_HANDLE                        *DispatchHandle
  );

extern PCH_SMM_SOURCE_DESC mSrcDescSerialIrq;

/**
  Clear the TCO SMI status bit after the SMI handling is done

  @param[in] SrcDesc                    Pointer to the PCH SMI source description table

**/
VOID
EFIAPI
PchTcoSmiClearSource (
  PCH_SMM_SOURCE_DESC                   *SrcDesc
  );

/**
  Initialize Source descriptor structure

  @param[in] SrcDesc                    Pointer to the PCH SMI source description table

**/
VOID
NullInitSourceDesc (
  PCH_SMM_SOURCE_DESC                   *SrcDesc
  );

#endif
