/** @file
  Implements HECI Runtime Services

 @copyright
  INTEL CONFIDENTIAL
  Copyright 2016 - 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 "HeciRuntimeDxe.h"

STATIC UINTN     mHeci1BaseVirtualAddr;
STATIC UINTN     mHeci2Bar0VirtualAddr;
STATIC UINTN     mHeci2BarPhysicalAddr = 0xFFFFFFFF;

EFI_EVENT  mVirtualAddressChangeEvent = NULL;

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

/**
Notification function of EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE.

This is a notification function registered on EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event.
It convers pointer to new virtual address.

@param[in]  Event        Event whose notification function is being invoked.
@param[in]  Context      Pointer to the notification function's context.

**/
VOID
EFIAPI
CseHeciRuntimeAddressChangeEvent (
  IN EFI_EVENT             Event,
  IN VOID                  *Context
  )
{
  EfiConvertPointer (0x0, (VOID **) &mHeci1BaseVirtualAddr);
  EfiConvertPointer (0x0, (VOID **) &mHeci2Bar0VirtualAddr);

  EfiConvertPointer (0x0, (VOID **) &mHeciRuntimeProtocol.SendwACK);
  EfiConvertPointer (0x0, (VOID **) &mHeciRuntimeProtocol.ReadMsg);
  EfiConvertPointer (0x0, (VOID **) &mHeciRuntimeProtocol.SendMsg);
  EfiConvertPointer (0x0, (VOID **) &mHeciRuntimeProtocol.ResetHeci);
  EfiConvertPointer (0x0, (VOID **) &mHeciRuntimeProtocol.InitHeci);
  EfiConvertPointer (0x0, (VOID **) &mHeciRuntimeProtocol.SeCResetWait);
  EfiConvertPointer (0x0, (VOID **) &mHeciRuntimeProtocol.ReInitHeci);
  EfiConvertPointer (0x0, (VOID **) &mHeciRuntimeProtocol.GetSeCStatus);
  EfiConvertPointer (0x0, (VOID **) &mHeciRuntimeProtocol.GetSeCMode);
}

/**
  Sets the Runtime attribute for HECI2 MMIO space.

**/
EFI_STATUS
EFIAPI
HeciSetRuntimeMemoryAttributes (
  VOID
  )
{
  EFI_STATUS                         Status;
  EFI_GCD_MEMORY_SPACE_DESCRIPTOR    PciMemorySpaceDescriptor;
  EFI_PHYSICAL_ADDRESS               BarAddress;
  EFI_PHYSICAL_ADDRESS               BaseAddress;
  UINT64                             Length;
  UINT64                             Attributes;
  UINTN                              Heci1BaseAddress;

  DEBUG ((DEBUG_INFO, "HeciSetRuntimeMemoryAtrributes() Start\n"));

  //
  // Get HECI2 BAR0 address to convert for Virtual address for RT access
  // Required to access SEC, HECI Control registers in RT
  //
  ASSERT (mHeci2BarPhysicalAddr != 0xFFFFFFFF);
  DEBUG ((DEBUG_INFO, "HECI2 BAR : 0x%x.\n", mHeci2BarPhysicalAddr));

  //
  //  Change Local APIC memory space attribute to Runtime Memory
  //
  BarAddress = (EFI_PHYSICAL_ADDRESS) mHeci2BarPhysicalAddr;
  Length = SIZE_4KB;
  Status = gDS->GetMemorySpaceDescriptor (BarAddress, &PciMemorySpaceDescriptor);
  ASSERT (!EFI_ERROR (Status) || Status == EFI_NOT_FOUND);

  if (Status == EFI_NOT_FOUND) {
    Status = gDS->AddMemorySpace (
                    EfiGcdMemoryTypeMemoryMappedIo,
                    BarAddress,
                    Length,
                    0
                    );
    ASSERT_EFI_ERROR (Status);
  }

  Attributes = PciMemorySpaceDescriptor.Attributes | EFI_MEMORY_RUNTIME;
  Status = gDS->SetMemorySpaceAttributes (
                  BarAddress,
                  Length,
                  Attributes
                  );
  ASSERT_EFI_ERROR (Status);

  mHeci2Bar0VirtualAddr = BarAddress;

  DEBUG ((
    EFI_D_INFO,
    "HECI2 BAR Physical Address = 0X%X,  HECI2 BAR Virtual Address = 0x%x",
    mHeci2BarPhysicalAddr,
    mHeci2Bar0VirtualAddr
    ));

  //
  // Get HECI1 Base address to convert for Virtual address for RT access
  // Required to access Status register in RT
  //
  Heci1BaseAddress = MmPciBase (SEC_BUS, SEC_DEVICE_NUMBER, HECI_FUNCTION_NUMBER);

  //
  //  Change Local APIC memory space attribute to Runtime Memory
  //
  BaseAddress = (EFI_PHYSICAL_ADDRESS) Heci1BaseAddress;
  Length      = SIZE_4KB;
  Status      = gDS->GetMemorySpaceDescriptor (BaseAddress, &PciMemorySpaceDescriptor);
  ASSERT (!EFI_ERROR (Status) || Status == EFI_NOT_FOUND);

  if (Status == EFI_NOT_FOUND) {
    Status = gDS->AddMemorySpace (
                    EfiGcdMemoryTypeMemoryMappedIo,
                    BaseAddress,
                    Length,
                    0
                    );
    ASSERT_EFI_ERROR (Status);
  }

  Attributes = PciMemorySpaceDescriptor.Attributes | EFI_MEMORY_RUNTIME;
  Status = gDS->SetMemorySpaceAttributes (
                  BaseAddress,
                  Length,
                  Attributes
                  );
  ASSERT_EFI_ERROR (Status);

  mHeci1BaseVirtualAddr = BaseAddress;

  DEBUG ((
    EFI_D_INFO,
    "Heci1BaseAddress = 0X%X,  mHeci1BaseVirtualAddr = 0x%x",
    Heci1BaseAddress,
    mHeci1BaseVirtualAddr
    ));

  DEBUG ((DEBUG_INFO, "HeciSetRuntimeMemoryAtrributes() End\n"));

  return Status;
}

/*
  Returns whether HECI2 is in idle (D0i3).

  @params  None.
  @retval  None.
*/
BOOLEAN
EFIAPI
IsHeci2Idle(
  VOID
  )
{
  return (Mmio32 (mHeci2Bar0VirtualAddr, 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
  //
  if (IsHeci2Idle ()) {
    return;
  }

  // 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 (mHeci2Bar0VirtualAddr, R_HECI_DEVIDLEC) & B_HECI_DEVIDLEC_CIP) == B_HECI_DEVIDLEC_CIP);

  // Set DEVIDLE bit in HECI2_DEVIDLEC register
  Mmio32Or (mHeci2Bar0VirtualAddr, R_HECI_DEVIDLEC, B_HECI_DEVIDLEC_DEVIDLE);
}

/**
  Activate HECI2 for runtime access.

  @param[in]    None

  @retval       EFI_SUCCESS   Send message success.
**/
EFI_STATUS
EFIAPI
ActivateHeci2RuntimeAccess (
  VOID
  )
{
  EFI_STATUS                           Status;
  volatile HECI_HOST_CONTROL_REGISTER  *HostControlReg;
  volatile HECI_SEC_CONTROL_REGISTER   *SecControlReg;

  HostControlReg = (volatile HECI_HOST_CONTROL_REGISTER *) (UINTN) (mHeci2Bar0VirtualAddr + H_CSR);
  SecControlReg  = (volatile HECI_SEC_CONTROL_REGISTER  *) (UINTN) (mHeci2Bar0VirtualAddr + SEC_CSR_HA);

  // Note: Do not use DEBUG prints in this runtime function.

  //
  // Check for CSE reset, CSE not ready, or Host not ready
  //
  if ((SecControlReg->r.SEC_RDY_HRA == 0) || (SecControlReg->r.SEC_RST_HRA == 1) || (HostControlReg->r.H_RDY == 0)) {
    //CSE reset happen or CSE/HECI not ready, require HECI Reset/Heci Init
    Status = EfiHeciInit (HECI2_DEVICE);
    if (EFI_ERROR(Status)) {
      return Status;
    }
  }

  // 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 (mHeci2Bar0VirtualAddr, R_HECI_DEVIDLEC) & B_HECI_DEVIDLEC_CIP) == B_HECI_DEVIDLEC_CIP);

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

  // 4. Set DEVIDLE = 0 and write the DEVIDLEC register value (RR [3:3] is cleared by SW by writing '1')
  Mmio32And (mHeci2Bar0VirtualAddr, 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 (mHeci2Bar0VirtualAddr, 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;
  }

  return EFI_SUCCESS;
}

/**
  Send Heci message to CSE and get the 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
EFIAPI
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 = EFI_SUCCESS;

  //
  // Send the message to CSE
  //
  Status = EfiHeciSendMessage(HeciDev, Message, Length, HostAddress, SeCAddress);
  if (EFI_ERROR(Status)) {
    return Status;
  }

  //
  // Receive message from CSE
  //
  Status = EfiHeciReadMessage(HeciDev, BLOCKING, Message, RecLength);

  return Status;
}

/**
  Read the Heci message

  @param[in]      HeciDev      HECI Device ID.
  @param[in]      Blocking      Indicate does waiting for response.
  @param[in]      MessageBody Message data buffer.
  @param[in,out]  Length          Message data buffer size.

  @retval  EFI_SUCCESS   Send message success.
  @retval  Others              Send message failed.
**/
EFI_STATUS
EFIAPI
EfiHeciReadMessage (
  IN      HECI_DEVICE      HeciDev,
  IN      UINT32           Blocking,
  IN      UINT32           *MessageBody,
  IN OUT  UINT32           *Length
  )
{
  EFI_STATUS           Status;
  UINTN                MaxBuffer;
  UINTN                ReadSize;
  UINTN                Index;
  UINTN                StallCount;
  UINTN                MaxCount;
  UINTN                OverAllDelay;

  BOOLEAN              TimeOut;
  HECI_MESSAGE_HEADER  MessageHeader;

  volatile UINT32                      *ReadBuffer;
  volatile HECI_HOST_CONTROL_REGISTER  *HostControlReg;
  volatile HECI_SEC_CONTROL_REGISTER   *SecControlReg;

  Status = ActivateHeci2RuntimeAccess ();
  if (EFI_ERROR (Status)) {
    return EFI_DEVICE_ERROR;
  }

  HostControlReg = (volatile HECI_HOST_CONTROL_REGISTER  *) (UINTN) (mHeci2Bar0VirtualAddr + H_CSR);
  SecControlReg = (volatile HECI_SEC_CONTROL_REGISTER  *) (UINTN) (mHeci2Bar0VirtualAddr + SEC_CSR_HA);

  ReadBuffer = (UINT32 *) (UINTN) (mHeci2Bar0VirtualAddr + SEC_CB_RW);

  MaxBuffer = HostControlReg->r.H_CBD;
  ReadSize = 0;
  OverAllDelay = 0;
  TimeOut = FALSE;

  MaxCount = CSE_WAIT_TIMEOUT * 1000;  //  50 Sec to wait for CSE response

  while (TRUE) {
    StallCount = 0;
    while ((HostControlReg->r.H_IS == 0) && (SecControlReg->r.SEC_CBRP_HRA == SecControlReg->r.SEC_CBWP_HRA) && (StallCount < MaxCount)) {
      MicroSecondDelay (STALL_1_MILLISECOND);
      StallCount += 1;
    }
    if (StallCount == MaxCount) {
      TimeOut = TRUE;
      break;
    }

    OverAllDelay += StallCount;  // in Millisec

    HostControlReg->r.H_RDY = 1;
    HostControlReg->r.H_IE = 0;

    StallCount = 0;
    while ((SecControlReg->r.SEC_CBRP_HRA == SecControlReg->r.SEC_CBWP_HRA) && (StallCount < MaxCount)) {
      MicroSecondDelay (STALL_1_MILLISECOND);
      StallCount += 1;
    }
    if (StallCount >= MaxCount) {
      TimeOut = TRUE;
      break;
    }

    OverAllDelay += StallCount;  // in Millisec

    MessageHeader.Data = *ReadBuffer;
    *Length = MessageHeader.Fields.Length;

    for (Index = 0; Index < (MessageHeader.Fields.Length + 3) / 4; Index++) {
      StallCount = 0;
      while ((SecControlReg->r.SEC_CBRP_HRA == SecControlReg->r.SEC_CBWP_HRA) && (StallCount < MaxCount)) {
        MicroSecondDelay (STALL_1_MILLISECOND);
        StallCount += 1;
      }
      if (StallCount >= MaxCount) {
        TimeOut = TRUE;
        break;
      }
      OverAllDelay += StallCount;  // in Millisec
      MessageBody[Index + ReadSize] = *ReadBuffer;
    }

    HostControlReg->r.H_IS = 1;
    HostControlReg->r.H_RDY = 1;
    HostControlReg->r.H_IE = 1;
    HostControlReg->r.H_IG = 1;

    if (MessageHeader.Fields.MessageComplete == 1) {
      TimeOut = FALSE;
      break;
    }
    else {
      MicroSecondDelay (STALL_1_MILLISECOND);
      OverAllDelay += 1;
      ReadSize += Index;
      if (OverAllDelay >= MaxCount) {
        TimeOut = TRUE;
        break;
      }
    }
  }

  SetHeci2Idle ();
  return ((TimeOut) ? EFI_TIMEOUT : EFI_SUCCESS);
}

/**
  Send Heci message without response

  @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     Send message success.
  @retval  Others          Send message failed.
**/
EFI_STATUS
EFIAPI
EfiHeciSendMessage(
  IN      HECI_DEVICE      HeciDev,
  IN      UINT32           *Message,
  IN      UINT32           Length,
  IN      UINT8            HostAddress,
  IN      UINT8            SeCAddress
  )
{
  EFI_STATUS           Status;
  UINTN                LeftSize;
  UINTN                MaxBuffer;
  UINTN                WriteSize;
  UINTN                Size;
  UINTN                Index;
  UINTN                StallCount;
  UINTN                MaxCount;
  UINTN                OverAllDelay;

  BOOLEAN              TimeOut;
  HECI_MESSAGE_HEADER  MessageHeader;

  volatile UINT32                      *WriteBuffer;
  volatile HECI_HOST_CONTROL_REGISTER  *HostControlReg;
  volatile HECI_SEC_CONTROL_REGISTER   *SecControlReg;

  Status = ActivateHeci2RuntimeAccess();
  if (EFI_ERROR(Status)) {
    return EFI_DEVICE_ERROR;
  }

  HostControlReg = (volatile HECI_HOST_CONTROL_REGISTER  *)(UINTN)(mHeci2Bar0VirtualAddr + H_CSR);
  SecControlReg = (volatile HECI_SEC_CONTROL_REGISTER  *)(UINTN)(mHeci2Bar0VirtualAddr + SEC_CSR_HA);

  WriteBuffer = (UINT32 *)(UINTN)(mHeci2Bar0VirtualAddr + H_CB_WW);

  MaxBuffer = HostControlReg->r.H_CBD;
  OverAllDelay = 0;
  TimeOut = FALSE;

  MaxCount = CSE_WAIT_TIMEOUT * 1000;  //  50 Sec to wait for CSE response

  //
  // The first DWORD is used for send MessageHeader, so usable buffer size should Be MaxBuffer-1;
  //
  MaxBuffer -= 1;
  LeftSize = (Length + 3) / 4;
  WriteSize = 0;
  HostControlReg->r.H_RDY = 1;

  while (LeftSize > 0) {

    StallCount = 0;
    while ((SecControlReg->r.SEC_RDY_HRA == 0) && (StallCount < MaxCount)) {
      MicroSecondDelay(STALL_1_MILLISECOND);
      StallCount += 1;
    }
    if (StallCount == MaxCount) {
      TimeOut = TRUE;
      break;
    }

    HostControlReg->r.H_RDY = 1;
    HostControlReg->r.H_IE = 0;

    Size = (LeftSize > MaxBuffer) ? MaxBuffer : LeftSize;
    LeftSize -= Size;

    //
    // Prepare message header
    //
    MessageHeader.Data = 0;
    MessageHeader.Fields.SeCAddress = SeCAddress;
    MessageHeader.Fields.HostAddress = HostAddress;
    MessageHeader.Fields.MessageComplete = (LeftSize > 0) ? 0 : 1;
    MessageHeader.Fields.Length = (UINT32)((LeftSize > 0) ? Size * sizeof (UINT32) : Length - WriteSize * sizeof (UINT32));

    //
    // Wait for the Circular buffer Empty.
    // When H_CBRP == H_CBWP, then Host side circular buffer is empty, and allowed to write into it.
    //
    StallCount = 0;
    while ((HostControlReg->r.H_CBRP != HostControlReg->r.H_CBWP) && (StallCount < MaxCount)) {
      MicroSecondDelay(STALL_1_MILLISECOND);
      StallCount += 1;
    }
    if (StallCount == MaxCount) {
      TimeOut = TRUE;
      break;
    }
    *WriteBuffer = MessageHeader.Data;
    for (Index = 0; Index < Size; Index++) {
      *WriteBuffer = Message[Index + WriteSize];
    }

    //
    // Send the Interrupt;
    //
    HostControlReg->r.H_IS = 1;
    HostControlReg->r.H_RDY = 1;
    HostControlReg->r.H_IE = 1;
    HostControlReg->r.H_IG = 1;

    WriteSize += Size;
    if (LeftSize > 0) {
      StallCount = 0;
      while ((HostControlReg->r.H_IS == 0) && (StallCount < MaxCount)) {
        MicroSecondDelay(STALL_1_MILLISECOND);
        StallCount += 1;
      }
      if (StallCount == MaxCount) {
        TimeOut = TRUE;
        break;
      }
    }
  }

  SetHeci2Idle ();
  return ((TimeOut) ? EFI_TIMEOUT : EFI_SUCCESS);
}

/**
  Reset the HECI device.

  @param[in]   HeciDev  HECI Device ID.

  @retval  EFI_SUCCESS  Reset HECI success.
  @retval  Others       Reset HECI failed.
**/
EFI_STATUS
EFIAPI
EfiHeciReset(
  IN      HECI_DEVICE      HeciDev
  )
{
  if (HeciDev != HECI2_DEVICE) {
    return EFI_UNSUPPORTED;
  }
  return EFI_UNSUPPORTED;
}

/**
  Init the HECI device

  @param[in]   HeciDev   HECI Device ID.

  @retval  EFI_SUCCESS   Init HECI success.
  @retval  Others        Init HECI failed.
**/
EFI_STATUS
EFIAPI
EfiHeciInit(
  IN      HECI_DEVICE      HeciDev
  )
{
  UINTN                               HeciBaseAddress;
  HECI_HOST_CONTROL_REGISTER          HeciRegHCsr;
  volatile HECI_HOST_CONTROL_REGISTER *HeciRegHCsrPtr;
  HECI_FWS_REGISTER                   SeCFirmwareStatus;
  UINT32                              SeCMode;

  if (HeciDev != HECI2_DEVICE) {
    return EFI_UNSUPPORTED;
  }

  SeCMode = SEC_MODE_NORMAL;

  //
  // Make sure that HECI device BAR is correct and device is enabled.
  //
  HeciBaseAddress = mHeci1BaseVirtualAddr;

  HeciRegHCsrPtr = (VOID *) (UINTN) (mHeci2Bar0VirtualAddr + H_CSR);
  HeciRegHCsr.ul = HeciRegHCsrPtr->ul;

  //
  // Read H_RDY bit to check if we're already initialized
  //
  if (HeciRegHCsr.r.H_RDY == 1) {
    return EFI_SUCCESS;
  }
  SeCFirmwareStatus.ul = MmioRead32(HeciBaseAddress + R_SEC_FW_STS0);
  if (SeCFirmwareStatus.ul == 0 || SeCFirmwareStatus.ul == 0xFFFFFFFF) {
    return EFI_DEVICE_ERROR;
  }
  //
  // Check for CSE FW status and proceed only if CSE is in normal mode
  //

  if ((SeCFirmwareStatus.r.FwUpdateInprogress != 0) ||
    (SeCFirmwareStatus.r.FptBad != 0) ||
    (SeCFirmwareStatus.r.ErrorCode != SEC_ERROR_CODE_NO_ERROR) ||
    (SeCFirmwareStatus.r.SeCOperationMode == SEC_OPERATION_MODE_SECOVR_JMPR) ||
    (SeCFirmwareStatus.r.SeCOperationMode == SEC_OPERATION_MODE_ALT_DISABLED)
    )
  {
    return EFI_UNSUPPORTED;
  }

  //
  // Set HECI interrupt delivery mode.
  // HECI-1 uses legacy/MSI interrupt
  //
  MmioAnd8 (HeciBaseAddress + R_HIDM, 0xFC);

  //
  // Need to do following on CSE Init:
  //
  //  1) Wait for SEC_CSR_HA reg SEC_RDY bit set
  //
  //  Status = WaitForCseReady(HeciDev);   // TODO: Add this
  //
  //  2) Setup H_CSR reg as follows:
  //     a) Make sure H_RST is clear
  //     b) Set H_RDY
  //     c) Set H_IG
  //
  HeciRegHCsr.ul = HeciRegHCsrPtr->ul;

  if (HeciRegHCsrPtr->r.H_RDY == 0) {
    HeciRegHCsr.r.H_RST = 0;
    HeciRegHCsr.r.H_RDY = 1;
    HeciRegHCsr.r.H_IG = 1;
    HeciRegHCsrPtr->ul = HeciRegHCsr.ul;
  }

  return EFI_SUCCESS;
}

/**
  Reinit the HECI device

  @param[in]            HeciDev HECI Device ID.

  @retval  EFI_SUCCESS  HECI reinitialized successfully
  @retval  Others       HECI failed to reinitialize successfully
**/
EFI_STATUS
EFIAPI
EfiHeciReinit (
  IN      HECI_DEVICE      HeciDev
  )
{
  if (HeciDev != HECI2_DEVICE) {
    return EFI_UNSUPPORTED;
  }
  return EFI_UNSUPPORTED;
}

/**
  Reset the HECI device and waiting for 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
EFIAPI
EfiHeciResetWait (
  IN      HECI_DEVICE      HeciDev,
  IN      UINT32           Delay
  )
{
  if (HeciDev != HECI2_DEVICE) {
    return EFI_UNSUPPORTED;
  }
  return EFI_UNSUPPORTED;
}

/**
  Get the HECI status

  @param[out]  Status    HECI Staus.

  @retval  EFI_SUCCESS   Get status success.
  @retval  Others        Get status failed.
**/
EFI_STATUS
EFIAPI
  EfiHeciGetSecStatus (
  OUT UINT32               *Status2
  )
{
  return EFI_UNSUPPORTED;
}

/**
  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
EFIAPI
EfiHeciGetSecMode(
  IN  HECI_DEVICE                 HeciDev,
  OUT  UINT32                     *Mode
  )
{
  if (HeciDev != HECI2_DEVICE) {
    return EFI_UNSUPPORTED;
  }
  return EFI_UNSUPPORTED;
}

/**
  Install a HECI Protocol Interfaces for OS Runtime access

  @param[in]  ImageHandle  The image handle.
  @param[in]  SystemTable  The system table.

  @retval     EFI_SUCCESS  The protocol was installed successfully.
  @retval     Others       Protocol could not be installed.
**/
EFI_STATUS
EFIAPI
HeciRuntimeDxeInitialize (
  IN EFI_HANDLE         ImageHandle,
  IN EFI_SYSTEM_TABLE   *SystemTable
  )
{
  EFI_STATUS  Status;
  EFI_HANDLE  Handle;

  Handle = NULL;

  DEBUG ((EFI_D_INFO, "HeciRuntimeDxeInitialize() Entry\n"));

  mHeci2BarPhysicalAddr = Heci2PciRead32 (R_HECIMBAR0) & 0xFFFFFFF0;
  DEBUG ((DEBUG_INFO, "HECI2 BAR read in HeciRuntimeDxe = 0x%x.\n", mHeci2BarPhysicalAddr));

  Status = HeciSetRuntimeMemoryAttributes ();
  ASSERT_EFI_ERROR (Status);

  Status = gBS->CreateEventEx(
                  EVT_NOTIFY_SIGNAL,
                  TPL_NOTIFY,
                  CseHeciRuntimeAddressChangeEvent,
                  NULL,
                  &gEfiEventVirtualAddressChangeGuid,
                  &mVirtualAddressChangeEvent
                  );
  ASSERT_EFI_ERROR (Status);

  //
  // Install the Heci RT Protocol on a new handle.
  //
  Status = gBS->InstallProtocolInterface (
                  &Handle,
                  &gEfiHeciRuntimeProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  &mHeciRuntimeProtocol
                  );
  ASSERT_EFI_ERROR(Status);

  return Status;
}