/** @file
  HECI SMM driver

 @copyright
  INTEL CONFIDENTIAL
  Copyright 2007 - 2017 Intel Corporation.

  The source code contained or described herein and all documents related to the
  source code ("Material") are owned by Intel Corporation or its suppliers or
  licensors. Title to the Material remains with Intel Corporation or its suppliers
  and licensors. The Material may contain trade secrets and proprietary and
  confidential information of Intel Corporation and its suppliers and licensors,
  and is protected by worldwide copyright and trade secret laws and treaty
  provisions. No part of the Material may be used, copied, reproduced, modified,
  published, uploaded, posted, transmitted, distributed, or disclosed in any way
  without Intel's prior express written permission.

  No license under any patent, copyright, trade secret or other intellectual
  property right is granted to or conferred upon you by disclosure or delivery
  of the Materials, either expressly, by implication, inducement, estoppel or
  otherwise. Any license under such intellectual property rights must be
  express and approved by Intel in writing.

  Unless otherwise agreed by Intel in writing, you may not remove or alter
  this notice or any other notice embedded in Materials by Intel or
  Intel's suppliers or licensors in any way.

  This file contains an 'Intel Peripheral Driver' and is uniquely identified as
  "Intel Reference Module" and is licensed for Intel CPUs and chipsets under
  the terms of your license agreement with Intel or your vendor. This file may
  be modified by the user, subject to additional terms of the license agreement.

@par Specification Reference:

**/
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/LockBoxLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PciLib.h>
#include <Library/PerformanceLib.h>
#include <Library/SmmServicesTableLib.h>
#include <Library/TimerLib.h>
#include <Library/UefiBootServicesTableLib.h>

#include <Protocol/Heci.h>
#include <Protocol/Heci2Pm.h>
#include <Protocol/SmmAccess2.h>
#include <Protocol/SmmEndOfDxe.h>
#include <Protocol/SmmSwDispatch2.h>
#include <Protocol/SmmSxDispatch2.h>

#include <Private/Library/HeciInitLib.h>
#include <HeciRegs.h>
#include <HeciSmm.h>
#include <SeCAccess.h>
#include <SeCState.h>

STATIC UINTN   mHeci2BarAddress  = 0;
STATIC BOOLEAN mAtRuntime        = FALSE;

//
// TODO: This needs to be implemented properly
//
//       The function headers below need to be moved
//       to a header file and included properly.
//

EFI_STATUS
EfiHeciReadMessage (
  IN      HECI_DEVICE      HeciDev,
  IN      UINT32           Blocking,
  IN      UINT32           *MessageBody,
  IN OUT  UINT32           *Length
  );

EFI_STATUS
EFIAPI
Heci2Send (
  IN OUT  UINT32  *Message,
  IN      UINT32  Length,
  IN OUT  UINT32  *RecLength,
  IN      UINT8   HostAddress,
  IN      UINT8   SeCAddress,
  IN      BOOLEAN  SendWithAck
  );

/**
  Return whether at runtime.

  @param    None.

  @retval   TRUE    If at runtime.
            FALSE   Not at runtime.
**/
BOOLEAN
EFIAPI
AtRuntime (
  VOID
  )
{
  return mAtRuntime;
}

/*
  is there Heci Response?

  @retval  BOOLEAN
*/
BOOLEAN
IsHeciCseResponse (
  VOID
  )
{
  volatile HECI_SEC_CONTROL_REGISTER  *SecControlReg;

  SecControlReg = (volatile HECI_SEC_CONTROL_REGISTER  *) (UINTN) (mHeci2BarAddress + SEC_CSR_HA);
  return (SecControlReg->r.SEC_CBRP_HRA != SecControlReg->r.SEC_CBWP_HRA);
}

BOOLEAN
EFIAPI
IsHeci2Idle (
  VOID
  )
{
  return (Mmio32 (mHeci2BarAddress, R_HECI_DEVIDLEC) & B_HECI_DEVIDLEC_DEVIDLE) == B_HECI_DEVIDLEC_DEVIDLE;
}

/*
  Set HECI2 to idle (D0i3).

  @params  None.
  @retval  None.
 */
VOID
EFIAPI
SetHeci2Idle (
  VOID
  )
{
  //
  // No need to continue if HECI2 is already in idle or in BIOS boot time, i.e., before ExitBootService.
  // HECI2 should keep in Idle only in OS environment.
  //
  if (IsHeci2Idle() || !AtRuntime()) {
    return;
  }

  DEBUG ((EFI_D_INFO, "SetHeci2Idle: HECI2 was active. Setting HECI2 to idle...\n"));

  Mmio32 (mHeci2BarAddress, R_HECI_DEVIDLEC) = (B_HECI_DEVIDLEC_DEVIDLE | B_HECI_DEVIDLEC_IR);

  // 1.Read the DEVIDLEC register value
  // 2.If CIP =1, wait until cleared (if host is not using interrupts - poll on the bit value until cleared)
  while ((Mmio32 (mHeci2BarAddress, R_HECI_DEVIDLEC) & B_HECI_DEVIDLEC_CIP) == B_HECI_DEVIDLEC_CIP);
}

/*
  Bring out HECI2 out of D0i3.

  @params  None.
  @retval  None.
 */
VOID
EFIAPI
SetHeci2Active (
  VOID
  )
{
  volatile HECI_HOST_CONTROL_REGISTER  *HostControlReg;
  EFI_STATUS          Status;

  DEBUG ((EFI_D_INFO, "SetHeci2Active: Setting HECI2 to active...\n"));
  if (CheckForHeciReset(HECI2_DEVICE)) {
    ///
    /// If HECI reset than try to re-init HECI
    ///
    DEBUG((EFI_D_INFO, "SetHeci2Active: HECI2 Reset happen, Initializing...\n"));
    Status = HeciInitialize(HECI2_DEVICE);
    if (EFI_ERROR(Status)) {
      return;
    }
  }

  HostControlReg = (volatile HECI_HOST_CONTROL_REGISTER *) (UINTN) (mHeci2BarAddress + H_CSR);

  // 1.Read the DEVIDLEC register value
  // 2.If CIP = 1, wait until cleared (if host is not using interrupts - poll on the bit value until cleared)
  while ((Mmio32 (mHeci2BarAddress, R_HECI_DEVIDLEC) & B_HECI_DEVIDLEC_CIP) == B_HECI_DEVIDLEC_CIP);

  // 3.If PG is disabled or if DEVIDLE = 0, device is already in D0i3 state - nothing to do, exit
  if (!IsHeci2Idle ()) {
    return;
  }

  // 4. Set DEVIDLE = 0 and write the DEVIDLEC register value (RR [3:3] is cleared by SW by writing '1')
  Mmio32And (mHeci2BarAddress, R_HECI_DEVIDLEC, (UINT32) (~(B_HECI_DEVIDLEC_DEVIDLE | B_HECI_DEVIDLEC_RR)));

  // 5. Read the DEVIDLEC register value
  // 6. If CIP=1, wait until cleared (if host is not using interrupts - poll on the bit value until cleared)
  while ((Mmio32 (mHeci2BarAddress, R_HECI_DEVIDLEC) & B_HECI_DEVIDLEC_CIP) == B_HECI_DEVIDLEC_CIP);

  //
  // Clear interrupt status (if any).
  //
  if (HostControlReg->r.H_DEVIDLEC_IS == 1) {
    HostControlReg->r.H_DEVIDLEC_IS = 1;
  }

  DEBUG ((EFI_D_INFO, "SetHeci2Active: HECI2 DEVIDLEC register value (should be active) = 0x%x.\n", Mmio32 (mHeci2BarAddress, R_HECI_DEVIDLEC)));
}

UINTN
EFIAPI
GetHeci2Bar (
  VOID
  )
{
  return mHeci2BarAddress;
}

/*
  Disable Heci interrupt

*/
VOID
HeciDisableInterrupt(
  VOID
  )
{
  volatile HECI_HOST_CONTROL_REGISTER  *HostControlReg;

  HostControlReg = (volatile HECI_HOST_CONTROL_REGISTER  *) (UINTN) (mHeci2BarAddress + H_CSR);
  HostControlReg->r.H_IE = 0;
}

/*
  Enable Heci Interrupt

*/
VOID
HeciEnableInterrupt(
  VOID
  )
{
  volatile HECI_HOST_CONTROL_REGISTER  *HostControlReg;

  HostControlReg = (volatile HECI_HOST_CONTROL_REGISTER  *) (UINTN) (mHeci2BarAddress + H_CSR);
  HostControlReg->r.H_IE = 1;
}

/*
  Clear Heci Interrupt Flag

*/
VOID
HeciClearInterrupt(
  VOID
  )
{
  volatile HECI_HOST_CONTROL_REGISTER  *HostControlReg;

  HostControlReg = (volatile HECI_HOST_CONTROL_REGISTER  *) (UINTN) (mHeci2BarAddress + H_CSR);
  HostControlReg->r.H_IS = 1;
}

/*
  BIOS to inform CSE to take over NVM ownership by setting bit 3 on
  Host General Status register 3 (GS3).
  [0x74] <--  0x00080008
*/
VOID ProxyStopNotify(
  VOID
)
{
  HeciPciWrite32(PCI_CFG_GS3, (PCI_CFG_GS3_PANIC_SUPPORTED | PCI_CFG_GS3_PANIC));
}
/*
  SMI Callback function to inform CSE to take NVM ownership.

  @param[in]  DispatchHandle
  @param[in]  RegisterContext
  @param[in]  CommBuffer
  @param[in]  CommBufferSize

  @retval  EFI_STATUS
*/
EFI_STATUS
EFIAPI
SystemResetCallback (
  IN EFI_HANDLE                  DispatchHandle,
  IN CONST VOID                  *Context,
  IN OUT VOID                    *CommBuffer,
  IN OUT UINTN                   *CommBufferSize
  )
{
  ProxyStopNotify ();

  return EFI_SUCCESS;
}

/*
  Registering SMI Callback function for System Reset Notification

  @param[in]  None

  @retval  None
*/
VOID
InitSystemResetHandle (
  VOID
  )
{
  EFI_STATUS Status;
  EFI_SMM_SW_DISPATCH2_PROTOCOL  *SwDispatch;
  EFI_SMM_SW_REGISTER_CONTEXT    SwContext;
  EFI_HANDLE                     SwHandle;

  //
  // Get the Sw dispatch protocol and register SMI callback functions.
  //
  Status = gSmst->SmmLocateProtocol (&gEfiSmmSwDispatch2ProtocolGuid, NULL, (VOID**) &SwDispatch);
  ASSERT_EFI_ERROR (Status);

  SwContext.SwSmiInputValue = (UINTN) HECI_SYSTEM_RESET_NOTIFY;
  Status = SwDispatch->Register (SwDispatch, SystemResetCallback, &SwContext, &SwHandle);
  ASSERT_EFI_ERROR (Status);
}

/*
  SMM handle to dispatch request

  @param[in]  DispatchHandle
  @param[in]  RegisterContext
  @param[in]  CommBuffer
  @param[in]  CommBufferSize

  @retval  EFI_STATUS
*/
EFI_STATUS
EFIAPI
HeciSmmHandler (
  IN       EFI_HANDLE  DispatchHandle,
  IN CONST VOID        *RegisterContext, OPTIONAL
  IN OUT   VOID        *CommBuffer,
  IN OUT   UINTN       *CommBufferSize
  )
{
  EFI_STATUS                       Status = EFI_SUCCESS;
  SMM_HECI_COMMUNICATE_HEADER      *SmmHeciFunctionHeader;
  SMM_HECI_READ_MESSAGE_BODY       *ReadMessageBody;
  SMM_HECI_SEND_MESSAGE_BODY       *SendMessageBody;
  SMM_HECI_DEV                     *HeciDevReset;
  SMM_HECI_DEV                     *HeciDevInit;
  SMM_HECI_DEV                     *HeciDevReInit;
  SMM_HECI_RESETWAIT               *HeciResetWait;
  SMM_HECI_GET_MODE                *HeciGetMode;
  EFI_HANDLE                       HeciHandle;
  UINT32                           SecMode;
  UINT32                          *SecStatus;

  //
  // If input is invalid, stop processing this SMI
  //
  if (CommBuffer == NULL || CommBufferSize == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  SecMode = HeciPciRead32 (R_SEC_FW_STS0);
  if (SEC_MODE_NORMAL != (SecMode & 0xF0000)) {
    return EFI_UNSUPPORTED;
  }

  SmmHeciFunctionHeader = (VOID *) CommBuffer;

  switch (SmmHeciFunctionHeader->Function) {
    //
    // This SMI handler is used for HECI2 read and write requests
    //
    case SMM_HECI_FUNCTION_SEND_MESSAGE_WITH_ACK:
      DEBUG ((EFI_D_INFO, "SMM_HECI_FUNCTION_SEND_MESSAGE_WITH_ACK\n"));

      SendMessageBody = (SMM_HECI_SEND_MESSAGE_BODY*) SmmHeciFunctionHeader->Data;
      Status = Heci2Send (
                 (UINT32 *) SendMessageBody->MessageData,
                 SendMessageBody->Length,
                 &SendMessageBody->RecLength,
                 (UINT8) SendMessageBody->HostAddress,
                 (UINT8) SendMessageBody->CSEAddress,
                 TRUE
                 );

      DEBUG ((EFI_D_INFO, "SMM_HECI_FUNCTION_SEND_MESSAGE_WITH_ACK %r %x\n", Status, *(UINT32 *) SendMessageBody->MessageData));
      break;

    case SMM_HECI_FUNCTION_READ_MESSAGE:
      ReadMessageBody = (SMM_HECI_READ_MESSAGE_BODY *) SmmHeciFunctionHeader->Data;
      Status = EfiHeciReadMessage (
                 HECI2_DEVICE,
                 ReadMessageBody->Blocking,
                 (UINT32 *)&ReadMessageBody->MessageData,
                 &ReadMessageBody->Length
                 );
      break;

    case SMM_HECI_FUNCTION_SEND_MESSAGE:
      DEBUG ((EFI_D_INFO, "SMM_HECI_FUNCTION_SEND_MESSAGE\n"));

      SendMessageBody = (SMM_HECI_SEND_MESSAGE_BODY*) SmmHeciFunctionHeader->Data;
      Status = Heci2Send (
                 (UINT32 *) SendMessageBody->MessageData,
                 SendMessageBody->Length,
                 &SendMessageBody->RecLength,
                 (UINT8) SendMessageBody->HostAddress,
                 (UINT8) SendMessageBody->CSEAddress,
                 FALSE
                 );

      DEBUG ((EFI_D_INFO, "SMM_HECI_FUNCTION_SEND_MESSAGE %r %x\n", Status, *(UINT32 *) SendMessageBody->MessageData));
      break;

    case SMM_HECI_FUNCTION_RESET:
      DEBUG((EFI_D_INFO, "SMM_HECI_FUNCTION_RESET\n"));
      HeciDevReset = (SMM_HECI_DEV *)SmmHeciFunctionHeader->Data;
      Status = HeciReset (HeciDevReset->HeciDev);
      break;

    case SMM_HECI_FUNCTION_INIT:
      DEBUG((EFI_D_INFO, "SMM_HECI_FUNCTION_INIT\n"));
      HeciDevInit = (SMM_HECI_DEV *)SmmHeciFunctionHeader->Data;
      Status = HeciInitialize (HeciDevInit->HeciDev);
      DEBUG((EFI_D_INFO, "SMM_HECI_FUNCTION_INIT Status %r \n", Status));
      break;

    case SMM_HECI_FUNCTION_RESETWAIT:
      DEBUG((EFI_D_INFO, "SMM_HECI_FUNCTION_RESETWAIT\n"));
      HeciResetWait = (SMM_HECI_RESETWAIT *)SmmHeciFunctionHeader->Data;
      Status = SeCResetWait (HeciResetWait->HeciDev, HeciResetWait->Delay);
      DEBUG((EFI_D_INFO, "SMM_HECI_FUNCTION_RESETWAIT Status %r \n", Status));
      break;

    case SMM_HECI_FUNCTION_REINIT:
      DEBUG((EFI_D_INFO, "SMM_HECI_FUNCTION_REINIT\n"));
      HeciDevReInit = (SMM_HECI_DEV *)SmmHeciFunctionHeader->Data;
      Status = HeciReInitialize (HeciDevReInit->HeciDev);
      DEBUG((EFI_D_INFO, "SMM_HECI_FUNCTION_REINIT Status %r \n", Status));
      break;

    case SMM_HECI_FUNCTION_GET_STATUS:
      DEBUG((EFI_D_INFO, "SMM_HECI_FUNCTION_GET_STATUS\n"));
      SecStatus = (UINT32 *)SmmHeciFunctionHeader->Data;
      Status = HeciGetSeCStatus(SecStatus);
      DEBUG((EFI_D_INFO, "SMM_HECI_FUNCTION_GET_STATUS Value %x \n", *SecStatus));
      break;

    case SMM_HECI_FUNCTION_GET_MODE:
      DEBUG((EFI_D_INFO, "SMM_HECI_FUNCTION_GET_MODE\n"));
      HeciGetMode = (SMM_HECI_GET_MODE *)SmmHeciFunctionHeader->Data;
      Status = HeciGetSeCMode(HeciGetMode->HeciDev, (UINT32 *)HeciGetMode->Mode);
      DEBUG((EFI_D_INFO, "SMM_HECI_FUNCTION_GET_MODE Status %r \n", Status));
      break;

    case SMM_HECI_MESSAGE_END_OF_POST:
      DEBUG ((EFI_D_INFO, "HeciSmmHandler SMM_HECI_MESSAGE_END_OF_POST\n"));
      InitSystemResetHandle ();
      HeciHandle = NULL;
      Status = gSmst->SmmInstallProtocolInterface (
                        &HeciHandle,
                        &gEfiCseEndofPostProtocolGuid,
                        EFI_NATIVE_INTERFACE,
                        NULL
                        );
      break;

    case SMM_HECI_MESSAGE_END_OF_SERVICES:
      DEBUG ((EFI_D_INFO, "HeciSmmHandler SMM_HECI_MESSAGE_END_OF_SERVICES\n"));
      HeciHandle = NULL;
      Status = gSmst->SmmInstallProtocolInterface (
                          &HeciHandle,
                          &gEfiCseEndofServicesProtocolGuid,
                          EFI_NATIVE_INTERFACE,
                          NULL
                          );
      mAtRuntime = TRUE;
      break;

    default:
      Status = EFI_UNSUPPORTED;
  }
  SmmHeciFunctionHeader->ReturnStatus = Status;

  return EFI_SUCCESS;
}

/*
  SmmEndOfDxeCallback

  @param[in]  Protocol
  @param[in]  Interface
  @param[in]  Handle

  @retval  EFI_STATUS
*/
EFI_STATUS
EFIAPI
SmmEndOfDxeCallback (
  IN CONST EFI_GUID                       *Protocol,
  IN VOID                                 *Interface,
  IN EFI_HANDLE                           Handle
  )
{
  return EFI_SUCCESS;
}


/**
  Send Heci message with response

  @param[in]       HeciDev       HECI device
  @param[in,out] Message       Message Data
  @param[in,out] Length          Message Data Length
  @param[in,out] RecLength     Return message buffer length
  @param[in]       HostAddress Host Address
  @param[in]       SECAddress CSE Address

  @retval  EFI_SUCCESS   Send message success.
  @retval  Others              Send message failed.
**/
EFI_STATUS
EfiHeciSendwack (
  IN      HECI_DEVICE      HeciDev,
  IN OUT  UINT32           *Message,
  IN OUT  UINT32           Length,
  IN OUT  UINT32           *RecLength,
  IN      UINT8            HostAddress,
  IN      UINT8            SECAddress
  )
{
  EFI_STATUS Status;

  SetHeci2Active();
  Status = Heci2Send (
             Message,
             Length,
             RecLength,
             HostAddress,
             SECAddress,
             TRUE
             );
  SetHeci2Idle();

  return Status;
}

/**
  Reads data via HECI.
  And puts HECI2 to idle state in case of Runtime.

  @param[in]      HeciDev       HECI Device ID.
  @param[in]      Blocking      Indicates whether the message should block for a response.
  @param[out]     MessageBody   Message data buffer.
  @param[out]     Length        Message data size.

  @retval         EFI_SUCCESS   The message was read successfully.
  @retval         Others        The message could not be read.
**/
EFI_STATUS
EfiHeciReadMessage (
  IN      HECI_DEVICE      HeciDev,
  IN      UINT32           Blocking,
  OUT     UINT32           *MessageBody,
  OUT     UINT32           *Length
  )
{
  EFI_STATUS            Status;
  BOOLEAN               SetHeci2Idle;
  EFI_HECI2_PM_PROTOCOL *Heci2PmProtocol = NULL;

  if (HeciDev == HECI2_DEVICE) {
    Status = ActivateHeci2Pm (&SetHeci2Idle, &Heci2PmProtocol);
    if (EFI_ERROR (Status)) {
      ASSERT_EFI_ERROR (Status);
      return Status;
    }
  }

  Status = HeciReceive (HeciDev, Blocking, MessageBody, Length);

  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "EfiHeciReadMessage: Error in HeciReceive = %r.\n", Status));
  }

  //
  //If this is HECI2 device and was in idle state before then put back to idle state again
  //
  if ((IsHeci2Idle () != TRUE) && mAtRuntime) {
    Heci2PmProtocol->SetIdle();
  }
  return Status;
}


/**
  Sends data via HECI without a response.
  In case of error, puts HECI2 to idle state.

  @param[in]      HeciDev       HECI Device ID.
  @param[in]      Message       Message Data.
  @param[in]      Length        Message Data length.
  @param[in]      HostAddress   Host Address.
  @param[in]      SECAddress    CSE Address.

  @retval         EFI_SUCCESS   The message was sent successfully.
  @retval         Others        The message could not be sent.
**/
EFI_STATUS
EfiHeciSendMessage (
  IN      HECI_DEVICE   HeciDev,
  IN      UINT32        *Message,
  IN      UINT32        Length,
  IN      UINT8         HostAddress,
  IN      UINT8         SECAddress
  )
{
  EFI_STATUS            Status;
  BOOLEAN               SetHeci2Idle;
  EFI_HECI2_PM_PROTOCOL *Heci2PmProtocol = NULL;

  if (HeciDev == HECI2_DEVICE) {
    Status = ActivateHeci2Pm (&SetHeci2Idle, &Heci2PmProtocol);
    if (EFI_ERROR (Status)) {
      ASSERT_EFI_ERROR (Status);
      return Status;
    }
  }

  Status = HeciSend (HeciDev, Message, Length, HostAddress, SECAddress);
  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "EfiHeciSendMessage: Error in HeciSend = %r.\n", Status));
    //
    // Keep Heci device Idle.
    // In case of command is failure, make sure HECI2 goes to idle
    // In case of command is success, EfiHeciReadMessage will make HECI2 to idle
    // 
    if (HeciDev == HECI2_DEVICE && SetHeci2Idle) {
      Heci2PmProtocol->SetIdle();
    }
  }
  return Status;
}

/**
  Reset the heci device

  @param[in]      HeciDev HECI Device ID.

  @retval  EFI_SUCCESS   Reset HECI success.
  @retval  Others              Reset HECI failed.
**/
EFI_STATUS
EfiHeciReset (
  IN      HECI_DEVICE      HeciDev
  )
{
  return HeciReset(HeciDev);
}

/**
  Init the heci device

  @param[in]      HeciDev HECI Device ID.

  @retval  EFI_SUCCESS   Init HECI success.
  @retval  Others              Init HECI failed.
**/
EFI_STATUS
EfiHeciInit (
  IN    HECI_DEVICE      HeciDev
  )
{
  return HeciInitialize(HeciDev);
}

/**
  Reinit the heci device

  @param[in]      HeciDev HECI Device ID.

  @retval  EFI_SUCCESS   Reinit HECI success.
  @retval  Others              Reinit HECI failed.
**/
EFI_STATUS
EfiHeciReinit (
  IN    HECI_DEVICE      HeciDev
  )
{
  return HeciReInitialize(HeciDev);
}

/**
  Waiting for Heci device Ready for given Delay time

  @param[in]      HeciDev HECI Device ID.
  @param[in]      Delay     The time waiting for reset.

  @retval  EFI_SUCCESS   Reset success.
  @retval  Others              Reset failed.
**/
EFI_STATUS
EfiHeciResetWait (
  IN    HECI_DEVICE      HeciDev,
  IN    UINT32           Delay
  )
{
  return SeCResetWait(HeciDev, Delay);
}

/**
  Get the Heci status

  @param[out]  Status        HECI Staus.

  @retval  EFI_SUCCESS   Get status success.
  @retval  Others              Get status failed.
**/
EFI_STATUS
EfiHeciGetSecStatus (
  IN UINT32                       *Status
  )
{
  return HeciGetSeCStatus(Status);
}

/**
  Get the heci mode

  @param[in]      HeciDev  HECI Device ID.
  @param[out]    Mode      Heci Mode

  @retval  EFI_SUCCESS   Get mode success.
  @retval  Others              Get mode failed.
**/
EFI_STATUS
EfiHeciGetSecMode (
  IN HECI_DEVICE      HeciDev,
  IN UINT32           *Mode
  )
{
  return HeciGetSeCMode(HeciDev, Mode);
}

EFI_HECI_PROTOCOL mHeciSmmProtocol = {
  EfiHeciSendwack,
  EfiHeciReadMessage,
  EfiHeciSendMessage,
  EfiHeciReset,
  EfiHeciInit,
  EfiHeciResetWait,
  EfiHeciReinit,
  EfiHeciGetSecStatus,
  EfiHeciGetSecMode
};

EFI_HECI2_PM_PROTOCOL mHeci2PmSmmProtocol = {
  IsHeci2Idle,
  SetHeci2Active,
  SetHeci2Idle,
  GetHeci2Bar,
  AtRuntime
};

/**
  HECI SMM module main entry point.

  @param[in] ImageHandle    The firmware allocated handle for the EFI image.
  @param[in] SystemTable    A pointer to the EFI System Table.

  @retval EFI_SUCCESS       The module entry point executed succesfully.

**/
EFI_STATUS
EFIAPI
HeciSmmInitialize (
  IN EFI_HANDLE          ImageHandle,
  IN EFI_SYSTEM_TABLE    *SystemTable
  )
{
  EFI_STATUS             Status;
  EFI_HANDLE             HeciHandle;
  VOID                   *SmmEndOfDxeRegistration;

  if (Heci2PciRead16 (R_SEC_DevID_VID) == 0xFFFF) {
    return EFI_SUCCESS;
  }

  //
  // Install the HECI SMM Protocol on a new handle.
  //
  HeciHandle = NULL;
  Status = gSmst->SmmInstallProtocolInterface (
                    &HeciHandle,
                    &gEfiHeciSmmProtocolGuid,
                    EFI_NATIVE_INTERFACE,
                    &mHeciSmmProtocol
                    );
  ASSERT_EFI_ERROR (Status);

  HeciHandle = NULL;
  Status = gSmst->SmmInstallProtocolInterface (
                    &HeciHandle,
                    &gEfiHeci2PmProtocolGuid,
                    EFI_NATIVE_INTERFACE,
                    &mHeci2PmSmmProtocol
                    );
  ASSERT_EFI_ERROR (Status);

  //
  // Register the HECI SMI handler
  //
  Status = gSmst->SmiHandlerRegister (HeciSmmHandler, &gEfiHeciSmmProtocolGuid, &HeciHandle);
  ASSERT_EFI_ERROR (Status);

  //
  // Register EFI_SMM_END_OF_DXE_PROTOCOL_GUID notify function.
  //
  Status = gSmst->SmmRegisterProtocolNotify (
                    &gEfiSmmEndOfDxeProtocolGuid,
                    SmmEndOfDxeCallback,
                    &SmmEndOfDxeRegistration
                    );
  ASSERT_EFI_ERROR (Status);

  DEBUG ((EFI_D_INFO, " Start Init Heci2 SMI Interrupt\n"));

  PERF_START_EX (NULL, NULL, NULL, 0, 0x8100);
  HeciReset (HECI2_DEVICE);
  PERF_END_EX (NULL, NULL, NULL, 0, 0x8101);

  mHeci2BarAddress = (UINTN) Heci2PciRead32 (R_HECIMBAR0) & 0xFFFFFFF0;
  DEBUG ((DEBUG_INFO, "HECI2 BAR read in HeciSmm = 0x%x.\n", mHeci2BarAddress));
  HeciDisableInterrupt ();
  HeciClearInterrupt ();
  HeciEnableInterrupt ();

  DEBUG ((EFI_D_INFO, "Start set HIDM %x %x\n", Heci2PciRead16 (0), Heci2PciRead16 (2)));
  Heci2PciWrite32 (0xa0, 2);

  return EFI_SUCCESS;
}


