public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Nhi Pham" <nhi@os.amperecomputing.com>
To: devel@edk2.groups.io
Cc: Vu Nguyen <vunguyen@os.amperecomputing.com>
Subject: [edk2-platforms][PATCH 05/34] JadePkg: Implement RealTimeClockLib for PCF85063
Date: Wed,  9 Dec 2020 16:25:02 +0700	[thread overview]
Message-ID: <20201209092531.30867-6-nhi@os.amperecomputing.com> (raw)
In-Reply-To: <20201209092531.30867-1-nhi@os.amperecomputing.com>

From: Vu Nguyen <vunguyen@os.amperecomputing.com>

This library supports to retrieve and update system datetime over real
RTC PCF85063 device on Jade board instead of using emulator RTC.
Also, timezone is set to UTC as default.

Signed-off-by: Vu Nguyen <vunguyen@os.amperecomputing.com>
---
 Silicon/Ampere/AmpereAltraPkg/Ac01Pkg.dsc.inc                           |   4 +-
 Platform/Ampere/JadePkg/Jade.dsc                                        |   6 +
 Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/PCF85063.inf   |  55 ++
 Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/PCF85063.h     |  86 +++
 Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/PCF85063.c     | 288 ++++++++++
 Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/RtcSystemLib.c | 603 ++++++++++++++++++++
 6 files changed, 1040 insertions(+), 2 deletions(-)

diff --git a/Silicon/Ampere/AmpereAltraPkg/Ac01Pkg.dsc.inc b/Silicon/Ampere/AmpereAltraPkg/Ac01Pkg.dsc.inc
index f2422156d1c3..4dbc7dc35bc7 100755
--- a/Silicon/Ampere/AmpereAltraPkg/Ac01Pkg.dsc.inc
+++ b/Silicon/Ampere/AmpereAltraPkg/Ac01Pkg.dsc.inc
@@ -88,10 +88,10 @@ [LibraryClasses.common]
   PMProLib|Silicon/Ampere/AmpereAltraPkg/Library/PMProLib/PMProLib.inf
   AmpereCpuLib|Silicon/Ampere/AmpereAltraPkg/Library/AmpereCpuLib/AmpereCpuLib.inf
   TimeBaseLib|EmbeddedPkg/Library/TimeBaseLib/TimeBaseLib.inf
+  I2CLib|Silicon/Ampere/AmpereAltraPkg/Library/DWI2CLib/I2CLib.inf
+  DwapbGpioLib|Silicon/Ampere/AmpereAltraPkg/Library/DwapbGpioLib/DwapbGpioLib.inf
   MmCommunicationLib|Silicon/Ampere/AmpereAltraPkg/Library/MmCommunicationLib/MmCommunicationLib.inf
 
-  RealTimeClockLib|EmbeddedPkg/Library/TemplateRealTimeClockLib/TemplateRealTimeClockLib.inf
-
   # ARM PL011 UART Driver
   PL011UartLib|ArmPlatformPkg/Library/PL011UartLib/PL011UartLib.inf
   SerialPortLib|ArmPlatformPkg/Library/PL011SerialPortLib/PL011SerialPortLib.inf
diff --git a/Platform/Ampere/JadePkg/Jade.dsc b/Platform/Ampere/JadePkg/Jade.dsc
index 56c6d53e3629..02157cb52f6e 100755
--- a/Platform/Ampere/JadePkg/Jade.dsc
+++ b/Platform/Ampere/JadePkg/Jade.dsc
@@ -49,6 +49,12 @@ [Defines]
 #
 ################################################################################
 [LibraryClasses]
+
+  #
+  # RTC Library: Common RTC
+  #
+  RealTimeClockLib|Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/PCF85063.inf
+
   #
   # Library for FailSafe support
   #
diff --git a/Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/PCF85063.inf b/Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/PCF85063.inf
new file mode 100644
index 000000000000..7666f313c31d
--- /dev/null
+++ b/Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/PCF85063.inf
@@ -0,0 +1,55 @@
+## @file
+#
+# Copyright (c) 2020, Ampere Computing LLC. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                      = 0x0001001B
+  MODULE_TYPE                      = BASE
+  BASE_NAME                        = RealTimeClockLib
+  FILE_GUID                        = 271569F6-5522-4006-9FF5-F07A59473AAC
+  LIBRARY_CLASS                    = RealTimeClockLib
+  VERSION_STRING                   = 1.0
+
+[Sources.common]
+  PCF85063.c
+  RtcSystemLib.c
+  PCF85063.h
+
+[Packages]
+  ArmPkg/ArmPkg.dec
+  ArmPlatformPkg/ArmPlatformPkg.dec
+  EmbeddedPkg/EmbeddedPkg.dec
+  MdePkg/MdePkg.dec
+  Silicon/Ampere/AmperePkg.dec
+  Silicon/Ampere/AmpereAltraPkg/Ac01Pkg.dec
+
+[LibraryClasses]
+  BaseLib
+  UefiRuntimeLib
+  UefiLib
+  DebugLib
+  TimerLib
+  DxeServicesTableLib
+  ArmLib
+  SMProLib
+  ArmGenericTimerCounterLib
+  I2CLib
+  DwapbGpioLib
+
+[Guids]
+  gEfiEventVirtualAddressChangeGuid
+
+[FixedPcd]
+  gArmPlatformTokenSpaceGuid.PcdCoreCount
+  gArmPlatformTokenSpaceGuid.PcdClusterCount
+
+  gAmpereTokenSpaceGuid.PcdTurboDefaultFreq
+  gAmpereTokenSpaceGuid.PcdSmproDb
+  gAmpereTokenSpaceGuid.PcdSmproDbBaseReg
+  gAmpereTokenSpaceGuid.PcdSmproEfuseShadow0
+  gAmpereTokenSpaceGuid.PcdSmproI2cBmcBusAddr
+  gAmpereTokenSpaceGuid.PcdSmproNsMailboxIndex
diff --git a/Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/PCF85063.h b/Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/PCF85063.h
new file mode 100644
index 000000000000..0779bc229188
--- /dev/null
+++ b/Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/PCF85063.h
@@ -0,0 +1,86 @@
+/** @file
+
+  Copyright (c) 2020, Ampere Computing LLC. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _COMMON_H_
+#define _COMMON_H_
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/RealTimeClockLib.h>
+
+//
+// I2C bus address that RTC connected to
+//
+#define I2C_RTC_BUS_ADDRESS        1
+
+//
+// I2C RTC bus speed
+//
+#define I2C_RTC_BUS_SPEED          100000
+
+//
+// I2C chip address that RTC connected to
+//
+#define I2C_RTC_CHIP_ADDRESS       0x51
+
+//
+// The GPI PIN that tell if RTC can be access
+//
+#define I2C_RTC_ACCESS_GPIO_PIN    28
+
+/**
+ * Returns the current time and date information of the hardware platform.
+ *
+ * @param  Time                  A pointer to storage to receive a snapshot of the current time.
+ *
+ *
+ * @retval EFI_SUCCESS           The operation completed successfully.
+ * @retval EFI_INVALID_PARAMETER Time is NULL.
+ * @retval EFI_DEVICE_ERROR      The time could not be retrieved due to hardware error.
+ */
+EFI_STATUS
+EFIAPI
+PlatformGetTime (
+  OUT EFI_TIME *Time
+  );
+
+/**
+ * Set the time and date information to the hardware platform.
+ *
+ * @param  Time                  A pointer to storage to set the current time to hardware platform.
+ *
+ *
+ * @retval EFI_SUCCESS           The operation completed successfully.
+ * @retval EFI_INVALID_PARAMETER Time is NULL.
+ * @retval EFI_DEVICE_ERROR      The time could not be set due due to hardware error.
+ **/
+EFI_STATUS
+EFIAPI
+PlatformSetTime (
+  IN EFI_TIME *Time
+  );
+
+/**
+ * Callback function for hardware platform to convert data pointers to virtual address
+ */
+VOID
+EFIAPI
+PlatformVirtualAddressChangeEvent (VOID);
+
+/**
+ * Callback function for hardware platform to initialize private data
+ *
+ *
+ * @retval EFI_SUCCESS           The operation completed successfully.
+ * @retval Others                The error status indicates the error
+ */
+EFI_STATUS
+EFIAPI
+PlatformInitialize (VOID);
+
+#endif /* _COMMON_H_ */
diff --git a/Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/PCF85063.c b/Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/PCF85063.c
new file mode 100755
index 000000000000..f1f01ee7b566
--- /dev/null
+++ b/Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/PCF85063.c
@@ -0,0 +1,288 @@
+/** @file
+
+  Copyright (c) 2020, Ampere Computing LLC. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/PcdLib.h>
+#include <Library/I2CLib.h>
+#include <Library/DwapbGpioLib.h>
+#include <Library/TimerLib.h>
+#include <Library/SMProInterface.h>
+#include <Platform/Ac01.h>
+#include "PCF85063.h"
+
+#define RTC_TIMEOUT_WAIT_ACCESS        100000 /* 100 miliseconds */
+#define RTC_DEFAULT_MIN_YEAR           2000
+#define RTC_DEFAULT_MAX_YEAR           2099
+
+/* Runtime needs to be 64K alignment */
+#define RUNTIME_ADDRESS_MASK           (~(SIZE_64KB - 1))
+#define RUNTIME_ADDRESS_LENGTH         SIZE_64KB
+
+#define RTC_ADDR                       0x4
+#define RTC_DATA_BUF_LEN               8
+
+/**
+ * PCF85063 register offsets
+ */
+#define PCF85063_OFFSET_SEC            0x0
+#define PCF85063_OFFSET_MIN            0x1
+#define PCF85063_OFFSET_HR             0x2
+#define PCF85063_OFFSET_DAY            0x3
+#define PCF85063_OFFSET_WKD            0x4
+#define PCF85063_OFFSET_MON            0x5
+#define PCF85063_OFFSET_YEA            0x6
+
+/**
+ * PCF85063 encoding macros
+ */
+#define PCF85063_SEC_ENC(s) (((((s) / 10) & 0x7) << 4) | (((s) % 10) & 0xf))
+#define PCF85063_MIN_ENC(m) (((((m) / 10) & 0x7) << 4) | (((m) % 10) & 0xf))
+#define PCF85063_HR_ENC(h)  (((((h) / 10) & 0x3)  << 4) | (((h) % 10) & 0xf))
+#define PCF85063_DAY_ENC(d) (((((d) / 10) & 0x3) << 4) | (((d) % 10) & 0xf))
+#define PCF85063_WKD_ENC(w) ((w) & 0x7)
+#define PCF85063_MON_ENC(m) (((((m) / 10) & 0x1) << 4) | (((m) % 10) & 0xf))
+#define PCF85063_YEA_ENC(y) (((((y) / 10) & 0xf) << 4) | (((y) % 10) & 0xf))
+
+/**
+ * PCF85063 decoding macros
+ */
+#define PCF85063_SEC_DEC(s) (((((s) & 0x70) >> 4) * 10) + ((s) & 0xf))
+#define PCF85063_MIN_DEC(m) (((((m) & 0x70) >> 4) * 10) + ((m) & 0xf))
+#define PCF85063_HR_DEC(h)  (((((h) & 0x30) >> 4) * 10) + ((h) & 0xf))
+#define PCF85063_DAY_DEC(d) (((((d) & 0x30) >> 4)* 10) + ((d) & 0xf))
+#define PCF85063_WKD_DEC(w) ((w) & 0x7)
+#define PCF85063_MON_DEC(m) (((((m) & 0x10) >> 4) * 10) + ((m) & 0xf))
+#define PCF85063_YEA_DEC(y) (((((y) & 0xf0) >> 4) * 10) + ((y) & 0xf))
+
+/* Buffer pointers to convert Vir2Phys and Phy2Vir */
+STATIC volatile UINT64  RtcBufVir;
+STATIC volatile UINT64  RtcBufPhy;
+
+STATIC volatile UINT64  DBAddr = (UINT64) SMPRO_DB_BASE_REG;
+
+STATIC
+EFI_STATUS
+RtcI2CWaitAccess (VOID)
+{
+  INTN Timeout = RTC_TIMEOUT_WAIT_ACCESS;
+
+  while ((DwapbGpioReadBit (I2C_RTC_ACCESS_GPIO_PIN) != 0) && (Timeout > 0)) {
+    MicroSecondDelay (100);
+    Timeout -= 100;
+  }
+
+  if (Timeout <= 0) {
+    DEBUG ((DEBUG_ERROR, "%a: Timeout while waiting access RTC\n", __FUNCTION__));
+    return EFI_TIMEOUT;
+  }
+
+  return EFI_SUCCESS;
+}
+
+STATIC
+EFI_STATUS
+RtcI2CRead (
+  IN     UINT8  Addr,
+  IN OUT UINT64 Data,
+  IN     UINT32 DataLen
+)
+{
+  EFI_STATUS  Status;
+  UINT32      TmpLen;
+
+  if (EFI_ERROR (RtcI2CWaitAccess ())) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  Status = I2CProbe (I2C_RTC_BUS_ADDRESS, I2C_RTC_BUS_SPEED);
+  if (EFI_ERROR (Status)) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  /* The first byte is the address */
+  TmpLen = 1;
+  Status = I2CWrite (I2C_RTC_BUS_ADDRESS, I2C_RTC_CHIP_ADDRESS, (UINT8 *) &Addr, &TmpLen);
+  if (EFI_ERROR (Status)) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  /* Read back the date */
+  Status = I2CRead (I2C_RTC_BUS_ADDRESS, I2C_RTC_CHIP_ADDRESS, NULL, 0, (UINT8 *) Data, &DataLen);
+  if (EFI_ERROR (Status)) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_STATUS
+RtcI2CWrite (
+  IN  UINT8   Addr,
+  IN  UINT64  Data,
+  IN  UINT32  DataLen
+  )
+{
+  EFI_STATUS  Status;
+  UINT8       TmpBuf[RTC_DATA_BUF_LEN + 1];
+  UINT32      TmpLen;
+
+  if (EFI_ERROR (RtcI2CWaitAccess ())) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  if (DataLen > sizeof (TmpBuf) - 1) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = I2CProbe (I2C_RTC_BUS_ADDRESS, I2C_RTC_BUS_SPEED);
+  if (EFI_ERROR (Status)) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  /* The first byte is the address */
+  TmpBuf[0] = Addr;
+  TmpLen = DataLen + 1;
+  CopyMem ((VOID *) (TmpBuf + 1), (VOID *) Data, DataLen);
+
+  Status = I2CWrite (I2C_RTC_BUS_ADDRESS, I2C_RTC_CHIP_ADDRESS, TmpBuf, &TmpLen);
+  if (EFI_ERROR (Status)) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+ * Returns the current time and date information of the hardware platform.
+ *
+ * @param  Time                  A pointer to storage to receive a snapshot of the current time.
+ *
+ *
+ * @retval EFI_SUCCESS           The operation completed successfully.
+ * @retval EFI_INVALID_PARAMETER Time is NULL.
+ * @retval EFI_DEVICE_ERROR      The time could not be retrieved due to hardware error.
+ */
+EFI_STATUS
+EFIAPI
+PlatformGetTime (
+  OUT EFI_TIME *Time
+  )
+{
+  EFI_STATUS    Status;
+  UINT8         *Data = (UINT8 *) RtcBufVir;
+
+  if (Time == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = RtcI2CRead (RTC_ADDR, RtcBufVir, RTC_DATA_BUF_LEN);
+
+  if (Status == EFI_SUCCESS) {
+    Time->Second = PCF85063_SEC_DEC (Data[PCF85063_OFFSET_SEC]);
+    Time->Minute = PCF85063_MIN_DEC (Data[PCF85063_OFFSET_MIN]);
+    Time->Hour   = PCF85063_HR_DEC (Data[PCF85063_OFFSET_HR]);
+    Time->Day    = PCF85063_DAY_DEC (Data[PCF85063_OFFSET_DAY]);
+    Time->Month  = PCF85063_MON_DEC (Data[PCF85063_OFFSET_MON]);
+    Time->Year   = PCF85063_YEA_DEC (Data[PCF85063_OFFSET_YEA]);
+    Time->Year  += RTC_DEFAULT_MIN_YEAR;
+    if (Time->Year > RTC_DEFAULT_MAX_YEAR) {
+      Time->Year = RTC_DEFAULT_MAX_YEAR;
+    }
+    if (Time->Year < RTC_DEFAULT_MIN_YEAR) {
+      Time->Year = RTC_DEFAULT_MIN_YEAR;
+    }
+  }
+
+  return Status;
+}
+
+/**
+ * Set the time and date information to the hardware platform.
+ *
+ * @param  Time                  A pointer to storage to set the current time to hardware platform.
+ *
+ *
+ * @retval EFI_SUCCESS           The operation completed successfully.
+ * @retval EFI_INVALID_PARAMETER Time is NULL.
+ * @retval EFI_DEVICE_ERROR      The time could not be set due due to hardware error.
+ **/
+EFI_STATUS
+EFIAPI
+PlatformSetTime (
+  IN EFI_TIME *Time
+  )
+{
+  UINT8         *Data = (UINT8 *) RtcBufVir;
+
+  if (Time == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Time->Year < RTC_DEFAULT_MIN_YEAR ||
+      Time->Year > RTC_DEFAULT_MAX_YEAR) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Data[PCF85063_OFFSET_SEC] = PCF85063_SEC_ENC (Time->Second);
+  Data[PCF85063_OFFSET_MIN] = PCF85063_MIN_ENC (Time->Minute);
+  Data[PCF85063_OFFSET_HR] = PCF85063_HR_ENC (Time->Hour);
+  Data[PCF85063_OFFSET_DAY] = PCF85063_DAY_ENC (Time->Day);
+  Data[PCF85063_OFFSET_MON] = PCF85063_MON_ENC (Time->Month);
+  Data[PCF85063_OFFSET_YEA] = PCF85063_YEA_ENC (Time->Year - RTC_DEFAULT_MIN_YEAR);
+
+  return RtcI2CWrite (RTC_ADDR, RtcBufVir, RTC_DATA_BUF_LEN);
+}
+
+/**
+ * Callback function for hardware platform to convert data pointers to virtual address
+ */
+VOID
+EFIAPI
+PlatformVirtualAddressChangeEvent (VOID)
+{
+  EfiConvertPointer (0x0, (VOID **) &RtcBufVir);
+  EfiConvertPointer (0x0, (VOID **) &DBAddr);
+}
+
+/**
+ * Callback function for hardware platform to initialize private data
+ *
+ *
+ * @retval EFI_SUCCESS           The operation completed successfully.
+ * @retval Others                The error status indicates the error
+ */
+EFI_STATUS
+EFIAPI
+PlatformInitialize (VOID)
+{
+  EFI_STATUS                        Status;
+
+  /*
+   * Allocate the buffer for RTC data
+   * The buffer can be accessible after ExitBootServices
+   */
+  RtcBufVir = (UINT64) AllocateRuntimeZeroPool (RTC_DATA_BUF_LEN);
+  ASSERT_EFI_ERROR (RtcBufVir);
+  RtcBufPhy = (UINT64) RtcBufVir;
+
+  Status = I2CSetupRuntime (I2C_RTC_BUS_ADDRESS);
+  ASSERT_EFI_ERROR (Status);
+
+  Status = DwapbGPIOSetupRuntime (I2C_RTC_ACCESS_GPIO_PIN);
+  ASSERT_EFI_ERROR (Status);
+
+  return Status;
+}
diff --git a/Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/RtcSystemLib.c b/Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/RtcSystemLib.c
new file mode 100755
index 000000000000..cfcd6ec24f3a
--- /dev/null
+++ b/Platform/Ampere/JadePkg/Library/PCF85063RealTimeClockLib/RtcSystemLib.c
@@ -0,0 +1,603 @@
+/** @file
+
+  Copyright (c) 2020, Ampere Computing LLC. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include <PiDxe.h>
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/RealTimeClockLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Protocol/RealTimeClock.h>
+#include <Library/ArmGenericTimerCounterLib.h>
+#include <Library/ArmLib.h>
+#include <Guid/EventGroup.h>
+#include "PCF85063.h"
+
+#define TICKS_PER_SEC     (ArmGenericTimerGetTimerFreq ())
+
+#define TIMEZONE_0        0
+
+/**
+ * Define EPOCH (1970-JANUARY-01) in the Julian Date representation
+ */
+#define EPOCH_JULIAN_DATE 2440588
+
+/**
+ * Seconds per unit
+ */
+#define SEC_PER_MIN                    ((UINTN)    60)
+#define SEC_PER_HOUR                   ((UINTN)  3600)
+#define SEC_PER_DAY                    ((UINTN) 86400)
+
+#define SEC_PER_MONTH                  ((UINTN)  2,592,000)
+#define SEC_PER_YEAR                   ((UINTN) 31,536,000)
+
+STATIC EFI_RUNTIME_SERVICES   *mRT;
+STATIC EFI_EVENT              mVirtualAddressChangeEvent = NULL;
+
+STATIC UINT64                 mLastSavedSystemCount = 0;
+STATIC UINT64                 mLastSavedTimeEpoch = 0;
+STATIC CONST CHAR16           mTimeZoneVariableName[] = L"RtcTimeZone";
+STATIC CONST CHAR16           mDaylightVariableName[] = L"RtcDaylight";
+
+STATIC
+BOOLEAN
+IsLeapYear (
+  IN EFI_TIME   *Time
+  )
+{
+  if (Time->Year % 4 == 0) {
+    if (Time->Year % 100 == 0) {
+      if (Time->Year % 400 == 0) {
+        return TRUE;
+      } else {
+        return FALSE;
+      }
+    } else {
+      return TRUE;
+    }
+  } else {
+    return FALSE;
+  }
+}
+
+STATIC
+BOOLEAN
+DayValid (
+  IN  EFI_TIME  *Time
+  )
+{
+  INTN  DayOfMonth[12];
+
+  DayOfMonth[0] = 31;
+  DayOfMonth[1] = 29;
+  DayOfMonth[2] = 31;
+  DayOfMonth[3] = 30;
+  DayOfMonth[4] = 31;
+  DayOfMonth[5] = 30;
+  DayOfMonth[6] = 31;
+  DayOfMonth[7] = 31;
+  DayOfMonth[8] = 30;
+  DayOfMonth[9] = 31;
+  DayOfMonth[10] = 30;
+  DayOfMonth[11] = 31;
+
+  if (Time->Day < 1 ||
+      Time->Day > DayOfMonth[Time->Month - 1] ||
+      (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28))
+     ) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+STATIC
+EFI_STATUS
+RtcTimeFieldsValid (
+  IN EFI_TIME *Time
+  )
+{
+  if (Time->Month < 1 ||
+      Time->Month > 12 ||
+      (!DayValid (Time)) ||
+      Time->Hour > 23 ||
+      Time->Minute > 59 ||
+      Time->Second > 59 ||
+      Time->Nanosecond > 999999999 ||
+      (!(Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE || (Time->TimeZone >= -1440 && Time->TimeZone <= 1440))) ||
+      ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return EFI_SUCCESS;
+}
+
+UINT8
+Bin2Bcd (
+  UINT32 Val
+  )
+{
+  return (((Val / 10) << 4) | (Val % 10));
+}
+
+/**
+ * Converts EFI_TIME to Epoch seconds (elapsed since 1970 JANUARY 01, 00:00:00 UTC)
+ */
+STATIC UINTN
+EfiTimeToEpoch (
+  IN  EFI_TIME  *Time
+  )
+{
+  UINTN a;
+  UINTN y;
+  UINTN m;
+  UINTN JulianDate;
+  UINTN EpochDays;
+  UINTN EpochSeconds;
+
+  a = (14 - Time->Month) / 12 ;
+  y = Time->Year + 4800 - a;
+  m = Time->Month + (12*a) - 3;
+
+  JulianDate = Time->Day + ((153*m + 2)/5) + (365*y) + (y/4) - (y/100) + (y/400) - 32045;
+
+  ASSERT (JulianDate > EPOCH_JULIAN_DATE);
+  EpochDays = JulianDate - EPOCH_JULIAN_DATE;
+
+  EpochSeconds = (EpochDays * SEC_PER_DAY) + ((UINTN)Time->Hour * SEC_PER_HOUR) + (Time->Minute * SEC_PER_MIN) + Time->Second;
+
+  return EpochSeconds;
+}
+
+/**
+ * Converts Epoch seconds (elapsed since 1970 JANUARY 01, 00:00:00 UTC) to EFI_TIME
+ */
+STATIC
+VOID
+EpochToEfiTime (
+  IN  UINTN     EpochSeconds,
+  OUT EFI_TIME  *Time
+  )
+{
+  INTN         a;
+  INTN         b;
+  INTN         c;
+  INTN         d;
+  INTN         g;
+  INTN         j;
+  INTN         m;
+  INTN         y;
+  INTN         da;
+  INTN         db;
+  INTN         dc;
+  INTN         dg;
+  INTN         hh;
+  INTN         mm;
+  INTN         ss;
+  INTN         J;
+
+  J  = (EpochSeconds / 86400) + 2440588;
+  j  = J + 32044;
+  g  = j / 146097;
+  dg = j % 146097;
+  c  = (((dg / 36524) + 1) * 3) / 4;
+  dc = dg - (c * 36524);
+  b  = dc / 1461;
+  db = dc % 1461;
+  a  = (((db / 365) + 1) * 3) / 4;
+  da = db - (a * 365);
+  y  = (g * 400) + (c * 100) + (b * 4) + a;
+  m  = (((da * 5) + 308) / 153) - 2;
+  d  = da - (((m + 4) * 153) / 5) + 122;
+
+  Time->Year  = y - 4800 + ((m + 2) / 12);
+  Time->Month = ((m + 2) % 12) + 1;
+  Time->Day   = d + 1;
+
+  ss = EpochSeconds % 60;
+  a  = (EpochSeconds - ss) / 60;
+  mm = a % 60;
+  b = (a - mm) / 60;
+  hh = b % 24;
+
+  Time->Hour        = hh;
+  Time->Minute      = mm;
+  Time->Second      = ss;
+  Time->Nanosecond  = 0;
+}
+
+/**
+ * Returns the current time and date information, and the time-keeping capabilities
+ * of the hardware platform.
+ *
+ * @param  Time                  A pointer to storage to receive a snapshot of the current time.
+ * @param  Capabilities          An optional pointer to a buffer to receive the real time clock
+ *                               device's capabilities.
+ *
+ *
+ * @retval EFI_SUCCESS           The operation completed successfully.
+ * @retval EFI_INVALID_PARAMETER Time is NULL.
+ * @retval EFI_DEVICE_ERROR      The time could not be retrieved due to hardware error.
+ */
+EFI_STATUS
+EFIAPI
+LibGetTime (
+  OUT EFI_TIME                *Time,
+  OUT  EFI_TIME_CAPABILITIES  *Capabilities
+  )
+
+{
+  EFI_STATUS    Status;
+  UINT64        CurrentSystemCount;
+  UINT64        TimeElapsed;
+  INT16         TimeZone;
+  UINT8         Daylight;
+  UINTN         Size;
+  UINTN         EpochSeconds;
+
+  if (Time == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((mLastSavedTimeEpoch == 0) || EfiAtRuntime ()) {
+    /* SMPro requires physical address for message communication */
+    Status = PlatformGetTime (Time);
+    if (EFI_ERROR (Status)) {
+      /* Failed to read platform RTC so create fake time */
+      Time->Second = 0;
+      Time->Minute = 0;
+      Time->Hour = 10;
+      Time->Day = 1;
+      Time->Month = 1;
+      Time->Year = 2017;
+    }
+    if (!EfiAtRuntime ()) {
+      mLastSavedTimeEpoch = EfiTimeToEpoch (Time);
+      mLastSavedSystemCount = ArmGenericTimerGetSystemCount ();
+    }
+    EpochSeconds = EfiTimeToEpoch (Time);
+  } else {
+    CurrentSystemCount = ArmGenericTimerGetSystemCount ();
+    if (CurrentSystemCount >= mLastSavedSystemCount) {
+      TimeElapsed = (CurrentSystemCount - mLastSavedSystemCount) / MultU64x32 (1, TICKS_PER_SEC);
+      EpochSeconds = mLastSavedTimeEpoch + TimeElapsed;
+    } else {
+      /* System counter overflow 64 bits */
+      /* Call GetTime again to read the date from RTC HW, not using generic timer system counter */
+      mLastSavedTimeEpoch = 0;
+      return LibGetTime (Time, Capabilities);
+    }
+  }
+
+  /* Get the current time zone information from non-volatile storage */
+  Size = sizeof (TimeZone);
+  Status = mRT->GetVariable (
+                  (CHAR16 *) mTimeZoneVariableName,
+                  &gEfiCallerIdGuid,
+                  NULL,
+                  &Size,
+                  (VOID *) &TimeZone
+                  );
+  if (EFI_ERROR (Status)) {
+    /* The time zone variable does not exist in non-volatile storage, so create it. */
+    Time->TimeZone = TIMEZONE_0;
+    /* Store it */
+    Status = mRT->SetVariable (
+                    (CHAR16 *) mTimeZoneVariableName,
+                    &gEfiCallerIdGuid,
+                    EFI_VARIABLE_NON_VOLATILE
+                      | EFI_VARIABLE_BOOTSERVICE_ACCESS
+                      | EFI_VARIABLE_RUNTIME_ACCESS,
+                    Size,
+                    (VOID *) &(Time->TimeZone)
+                    );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((
+        DEBUG_ERROR,
+        "%a: Failed to save %s variable to non-volatile storage, Status = %r\n",
+        __FUNCTION__,
+        mTimeZoneVariableName,
+        Status
+        ));
+      return Status;
+    }
+  } else {
+    /* Got the time zone */
+    Time->TimeZone = TimeZone;
+
+    /* Check TimeZone bounds:   -1440 to 1440 or 2047 */
+    if (((Time->TimeZone < -1440) || (Time->TimeZone > 1440))
+        && (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE)) {
+      Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
+    }
+
+    /* Adjust for the correct time zone */
+    if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) {
+      EpochSeconds -= Time->TimeZone * SEC_PER_MIN;
+    }
+  }
+
+  /* Get the current daylight information from non-volatile storage */
+  Size = sizeof (Daylight);
+  Status = mRT->GetVariable (
+                  (CHAR16 *) mDaylightVariableName,
+                  &gEfiCallerIdGuid,
+                  NULL,
+                  &Size,
+                  (VOID *)&Daylight
+                  );
+
+  if (EFI_ERROR (Status)) {
+    /* The daylight variable does not exist in non-volatile storage, so create it. */
+    Time->Daylight = 0;
+    /* Store it */
+    Status = mRT->SetVariable (
+                    (CHAR16 *) mDaylightVariableName,
+                    &gEfiCallerIdGuid,
+                    EFI_VARIABLE_NON_VOLATILE
+                      | EFI_VARIABLE_BOOTSERVICE_ACCESS
+                      | EFI_VARIABLE_RUNTIME_ACCESS,
+                    Size,
+                    (VOID *) &(Time->Daylight)
+                    );
+    if (EFI_ERROR (Status)) {
+      DEBUG ((
+        DEBUG_ERROR,
+        "%a: Failed to save %s variable to non-volatile storage, Status = %r\n",
+        __FUNCTION__,
+        mDaylightVariableName,
+        Status
+        ));
+      return Status;
+    }
+  } else {
+    /* Got the daylight information */
+    Time->Daylight = Daylight;
+
+    /* Adjust for the correct period */
+    if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {
+      /* Convert to adjusted time, i.e. spring forwards one hour */
+      EpochSeconds += SEC_PER_HOUR;
+    }
+  }
+  EpochToEfiTime (EpochSeconds, Time);
+
+  return EFI_SUCCESS;
+}
+
+/**
+ * Sets the current local time and date information.
+ *
+ * @param  Time                  A pointer to the current time.
+ *
+ * @retval EFI_SUCCESS           The operation completed successfully.
+ * @retval EFI_INVALID_PARAMETER A time field is out of range.
+ * @retval EFI_DEVICE_ERROR      The time could not be set due due to hardware error.
+ */
+EFI_STATUS
+EFIAPI
+LibSetTime (
+  IN EFI_TIME                *Time
+  )
+{
+  EFI_STATUS    Status;
+  UINTN         EpochSeconds;
+
+  if ((Time == NULL) || (RtcTimeFieldsValid (Time) != EFI_SUCCESS)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  /* Always default to UTC time if unspecified */
+  if (Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) {
+    Time->TimeZone = TIMEZONE_0;
+  }
+
+  EpochSeconds = EfiTimeToEpoch (Time);
+
+  /* Adjust for the correct time zone, convert to UTC time zone */
+  EpochSeconds += Time->TimeZone * SEC_PER_MIN;
+
+  /* Adjust for the correct period */
+  if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {
+    EpochSeconds -= SEC_PER_HOUR;
+  }
+
+  Status = PlatformSetTime (Time);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  /* Save the current time zone information into non-volatile storage */
+  Status = mRT->SetVariable (
+                  (CHAR16 *) mTimeZoneVariableName,
+                  &gEfiCallerIdGuid,
+                  EFI_VARIABLE_NON_VOLATILE
+                    | EFI_VARIABLE_BOOTSERVICE_ACCESS
+                    | EFI_VARIABLE_RUNTIME_ACCESS,
+                  sizeof (Time->TimeZone),
+                  (VOID *)&(Time->TimeZone)
+                  );
+  if (EFI_ERROR (Status)) {
+      DEBUG ((
+        DEBUG_ERROR,
+        "%a: Failed to save %s variable to non-volatile storage, Status = %r\n",
+        __FUNCTION__,
+        mTimeZoneVariableName,
+        Status
+        ));
+    return Status;
+  }
+
+  /* Save the current daylight information into non-volatile storage */
+  Status = mRT->SetVariable (
+                  (CHAR16 *) mDaylightVariableName,
+                  &gEfiCallerIdGuid,
+                  EFI_VARIABLE_NON_VOLATILE
+                    | EFI_VARIABLE_BOOTSERVICE_ACCESS
+                    | EFI_VARIABLE_RUNTIME_ACCESS,
+                  sizeof(Time->Daylight),
+                  (VOID *)&(Time->Daylight)
+                  );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((
+      DEBUG_ERROR,
+      "%a: Failed to save %s variable to non-volatile storage, Status = %r\n",
+      __FUNCTION__,
+      mDaylightVariableName,
+      Status
+      ));
+    return Status;
+  }
+
+  EpochToEfiTime (EpochSeconds, Time);
+
+  Status = PlatformSetTime (Time);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  EpochToEfiTime (EpochSeconds, Time);
+
+  if (!EfiAtRuntime ()) {
+    mLastSavedTimeEpoch = EpochSeconds;
+    mLastSavedSystemCount = ArmGenericTimerGetSystemCount ();
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+ * Returns the current wakeup alarm clock setting.
+ *
+ * @param  Enabled               Indicates if the alarm is currently enabled or disabled.
+ * @param  Pending               Indicates if the alarm signal is pending and requires acknowledgement.
+ * @param  Time                  The current alarm setting.
+ *
+ * @retval EFI_SUCCESS           The alarm settings were returned.
+ * @retval EFI_INVALID_PARAMETER Any parameter is NULL.
+ * @retval EFI_DEVICE_ERROR      The wakeup time could not be retrieved due to a hardware error.
+ */
+EFI_STATUS
+EFIAPI
+LibGetWakeupTime (
+  OUT BOOLEAN     *Enabled,
+  OUT BOOLEAN     *Pending,
+  OUT EFI_TIME    *Time
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+ * Sets the system wakeup alarm clock time.
+ *
+ * @param  Enabled               Enable or disable the wakeup alarm.
+ * @param  Time                  If Enable is TRUE, the time to set the wakeup alarm for.
+ *
+ * @retval EFI_SUCCESS           If Enable is TRUE, then the wakeup alarm was enabled. If
+ *                               Enable is FALSE, then the wakeup alarm was disabled.
+ * @retval EFI_INVALID_PARAMETER A time field is out of range.
+ * @retval EFI_DEVICE_ERROR      The wakeup time could not be set due to a hardware error.
+ * @retval EFI_UNSUPPORTED       A wakeup timer is not supported on this platform.
+ */
+EFI_STATUS
+EFIAPI
+LibSetWakeupTime (
+  IN BOOLEAN      Enabled,
+  OUT EFI_TIME    *Time
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+/**
+ * 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  Event        Event whose notification function is being invoked.
+ * @param  Context      Pointer to the notification function's context.
+ */
+STATIC
+VOID
+EFIAPI
+VirtualAddressChangeEvent (
+  IN EFI_EVENT                            Event,
+  IN VOID                                 *Context
+  )
+{
+  EfiConvertPointer (0x0, (VOID**) &mRT);
+  PlatformVirtualAddressChangeEvent ();
+}
+
+/**
+ * This is the declaration of an EFI image entry point. This can be the entry point to an application
+ * written to this specification, an EFI boot service driver, or an EFI runtime driver.
+ *
+ * @param  ImageHandle           Handle that identifies the loaded image.
+ * @param  SystemTable           System Table for this image.
+ *
+ * @retval EFI_SUCCESS           The operation completed successfully.
+ */
+EFI_STATUS
+EFIAPI
+LibRtcInitialize (
+  IN EFI_HANDLE                            ImageHandle,
+  IN EFI_SYSTEM_TABLE                      *SystemTable
+  )
+{
+  EFI_STATUS    Status;
+  EFI_HANDLE    Handle;
+
+  Status = PlatformInitialize ();
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  /*
+   * Register for the virtual address change event
+   */
+  Status = gBS->CreateEventEx (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  VirtualAddressChangeEvent,
+                  NULL,
+                  &gEfiEventVirtualAddressChangeGuid,
+                  &mVirtualAddressChangeEvent
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  /*
+   * Setup the setters and getters
+   */
+  mRT = gRT;
+  mRT->GetTime       = LibGetTime;
+  mRT->SetTime       = LibSetTime;
+  mRT->GetWakeupTime = LibGetWakeupTime;
+  mRT->SetWakeupTime = LibSetWakeupTime;
+
+  /*
+   * Install the protocol
+   */
+  Handle = NULL;
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &Handle,
+                  &gEfiRealTimeClockArchProtocolGuid,  NULL,
+                  NULL
+                 );
+  ASSERT_EFI_ERROR (Status);
+
+  return EFI_SUCCESS;
+}
-- 
2.17.1


  parent reply	other threads:[~2020-12-09  9:24 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-09  9:24 [edk2-platforms][PATCH 00/34] Add new Ampere Mt. Jade platform Nhi Pham
2020-12-09  9:24 ` [edk2-platforms][PATCH 01/34] Initial support for Ampere Altra and " Nhi Pham
2021-01-07 23:57   ` [edk2-devel] " Leif Lindholm
2021-01-15  4:59     ` Vu Nguyen
2020-12-09  9:24 ` [edk2-platforms][PATCH 02/34] Platform/Ampere: Implement FailSafe library Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 03/34] Platform/Ampere: Add FailSafe and WDT support Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 04/34] AmpereAltraPkg: Implement GpioLib and I2cLib modules Nhi Pham
2020-12-09  9:25 ` Nhi Pham [this message]
2020-12-09  9:25 ` [edk2-platforms][PATCH 06/34] Platform/Ampere: Add AcpiPccLib to support ACPI PCCT Table Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 07/34] Platform/Ampere: Add AcpiHelperLib to update ACPI DSDT table Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 08/34] JadePkg: Initial support for static ACPI tables Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 09/34] JadePkg: Install some ACPI tables at runtime Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 10/34] Silicon/Ampere: Support Non Volatile storage for Variable service Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 11/34] Silicon/Ampere: Support PlatformManagerUiLib Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 12/34] AmpereAltraPkg: Add PcieCore Library Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 13/34] AmpereAltraPkg: Add PciHostBridge driver Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 14/34] JadePkg: Add implementation for PcieBoardLib Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 15/34] JadePkg: Enable PCIe support Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 16/34] JadePkg: Add ASpeed GOP driver Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 17/34] Silicon/Ampere: Add Random Number Generator Support Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 18/34] Silicon/Ampere: Fixup runtime memory attribute Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 19/34] JadePkg: Add SMBIOS tables support Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 20/34] AmpereAltraPkg: Add DebugInfoPei module Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 21/34] Silicon/Ampere: Add platform info screen Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 22/34] Silicon/Ampere: Add Memory " Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 23/34] AmpereAltraPkg: Add CPU Configuration for SubNUMA Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 24/34] AmpereAltraPkg: Add ACPI configuration screen Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 25/34] AmpereAltraPkg: Implement PlatformFlashAccessLib instance Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 26/34] JadePkg: Add implementation for UEFI Capsule Update Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 27/34] JadePkg: Add Capsule Update support Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 28/34] Silicon/Ampere: Implement PlatformBootManagerLib for LinuxBoot Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 29/34] Platform/Ampere: Add LinuxBoot image Nhi Pham
2020-12-10 12:40   ` [edk2-devel] " Leif Lindholm
2020-12-11  2:38     ` Nhi Pham
2020-12-11 11:15       ` Leif Lindholm
2020-12-09  9:25 ` [edk2-platforms][PATCH 30/34] JadePkg: Support LinuxBoot DSC/FDF build for Jade platform Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 31/34] AmpereAltraPkg: Add BootProgress support Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 32/34] JadePkg: Add ACPI/APEI tables Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 33/34] Platform/Ampere: Add AcpiApeiLib Nhi Pham
2020-12-09  9:25 ` [edk2-platforms][PATCH 34/34] AmpereAltraPkg, JadePkg: Add RAS setting screen Nhi Pham

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201209092531.30867-6-nhi@os.amperecomputing.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox