Files
nix-config/packages/edk2/patches/platforms/0009-Platform-RPi5-Add-real-time-clock-support.patch
2026-01-07 21:28:20 -06:00

612 lines
18 KiB
Diff

From d9b5815d797260ca8c8af1f8cca19e85c8c32a09 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mario=20B=C4=83l=C4=83nic=C4=83?=
<mariobalanica02@gmail.com>
Date: Fri, 29 Dec 2023 23:29:47 +0200
Subject: [PATCH 09/16] Platform/RPi5: Add real-time clock support
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Working get/set time and wake up alarm within UEFI.
Signed-off-by: Mario Bălănică <mariobalanica02@gmail.com>
---
.../Drivers/RpiFirmwareDxe/RpiFirmwareDxe.c | 110 ++++++-
.../Include/IndustryStandard/RpiMbox.h | 2 +
.../Include/Protocol/RpiFirmware.h | 28 ++
.../RaspberryPi/Library/RpiRtcLib/RpiRtcLib.c | 305 ++++++++++++++++++
.../Library/RpiRtcLib/RpiRtcLib.inf | 42 +++
Platform/RaspberryPi/RPi5/RPi5.dsc | 2 +-
6 files changed, 487 insertions(+), 2 deletions(-)
create mode 100644 Platform/RaspberryPi/Library/RpiRtcLib/RpiRtcLib.c
create mode 100644 Platform/RaspberryPi/Library/RpiRtcLib/RpiRtcLib.inf
diff --git a/Platform/RaspberryPi/Drivers/RpiFirmwareDxe/RpiFirmwareDxe.c b/Platform/RaspberryPi/Drivers/RpiFirmwareDxe/RpiFirmwareDxe.c
index 9077f077..b432e828 100644
--- a/Platform/RaspberryPi/Drivers/RpiFirmwareDxe/RpiFirmwareDxe.c
+++ b/Platform/RaspberryPi/Drivers/RpiFirmwareDxe/RpiFirmwareDxe.c
@@ -1,5 +1,6 @@
/** @file
*
+ * Copyright (c) 2023, Mario Bălănică <mariobalanica02@gmail.com>
* Copyright (c) 2020, Pete Batard <pete@akeo.ie>
* Copyright (c) 2019, ARM Limited. All rights reserved.
* Copyright (c) 2017-2020, Andrei Warkentin <andrey.warkentin@gmail.com>
@@ -1332,6 +1333,111 @@ RpiFirmwareNotifyGpioSetCfg (
return Status;
}
+
+#pragma pack()
+typedef struct {
+ UINT32 Register;
+ UINT32 Value;
+} RPI_FW_RTC_TAG;
+
+typedef struct {
+ RPI_FW_BUFFER_HEAD BufferHead;
+ RPI_FW_TAG_HEAD TagHead;
+ RPI_FW_RTC_TAG TagBody;
+ UINT32 EndTag;
+} RPI_FW_RTC_CMD;
+#pragma pack()
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareGetRtc (
+ IN RASPBERRY_PI_RTC_REGISTER Register,
+ OUT UINT32 *Value
+ )
+{
+ RPI_FW_RTC_CMD *Cmd;
+ EFI_STATUS Status;
+ UINT32 Result;
+
+ if (!AcquireSpinLockOrFail (&mMailboxLock)) {
+ DEBUG ((DEBUG_ERROR, "%a: failed to acquire spinlock\n", __FUNCTION__));
+ return EFI_DEVICE_ERROR;
+ }
+
+ Cmd = mDmaBuffer;
+ ZeroMem (Cmd, sizeof (*Cmd));
+
+ Cmd->BufferHead.BufferSize = sizeof (*Cmd);
+ Cmd->BufferHead.Response = 0;
+ Cmd->TagHead.TagId = RPI_MBOX_GET_RTC_REG;
+ Cmd->TagHead.TagSize = sizeof (Cmd->TagBody);
+ Cmd->TagHead.TagValueSize = 0;
+ Cmd->TagBody.Register = Register;
+ Cmd->TagBody.Value = 0;
+ Cmd->EndTag = 0;
+
+ Status = MailboxTransaction (Cmd->BufferHead.BufferSize, RPI_MBOX_VC_CHANNEL, &Result);
+
+ if (EFI_ERROR (Status) ||
+ Cmd->BufferHead.Response != RPI_MBOX_RESP_SUCCESS) {
+ DEBUG ((DEBUG_ERROR,
+ "%a: mailbox transaction error: Status == %r, Response == 0x%x\n",
+ __FUNCTION__, Status, Cmd->BufferHead.Response));
+ Status = EFI_DEVICE_ERROR;
+ } else {
+ *Value = Cmd->TagBody.Value;
+ }
+
+ ReleaseSpinLock (&mMailboxLock);
+
+ return Status;
+}
+
+STATIC
+EFI_STATUS
+EFIAPI
+RpiFirmwareSetRtc (
+ IN RASPBERRY_PI_RTC_REGISTER Register,
+ IN UINT32 Value
+ )
+{
+ RPI_FW_RTC_CMD *Cmd;
+ EFI_STATUS Status;
+ UINT32 Result;
+
+ if (!AcquireSpinLockOrFail (&mMailboxLock)) {
+ DEBUG ((DEBUG_ERROR, "%a: failed to acquire spinlock\n", __FUNCTION__));
+ return EFI_DEVICE_ERROR;
+ }
+
+ Cmd = mDmaBuffer;
+ ZeroMem (Cmd, sizeof (*Cmd));
+
+ Cmd->BufferHead.BufferSize = sizeof (*Cmd);
+ Cmd->BufferHead.Response = 0;
+ Cmd->TagHead.TagId = RPI_MBOX_SET_RTC_REG;
+ Cmd->TagHead.TagSize = sizeof (Cmd->TagBody);
+ Cmd->TagHead.TagValueSize = 0;
+ Cmd->TagBody.Register = Register;
+ Cmd->TagBody.Value = Value;
+ Cmd->EndTag = 0;
+
+ Status = MailboxTransaction (Cmd->BufferHead.BufferSize, RPI_MBOX_VC_CHANNEL, &Result);
+
+ if (EFI_ERROR (Status) ||
+ Cmd->BufferHead.Response != RPI_MBOX_RESP_SUCCESS) {
+ DEBUG ((DEBUG_ERROR,
+ "%a: mailbox transaction error: Status == %r, Response == 0x%x\n",
+ __FUNCTION__, Status, Cmd->BufferHead.Response));
+ Status = EFI_DEVICE_ERROR;
+ }
+
+ ReleaseSpinLock (&mMailboxLock);
+
+ return Status;
+}
+
STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL mRpiFirmwareProtocol = {
RpiFirmwareSetPowerState,
RpiFirmwareGetMacAddress,
@@ -1352,7 +1458,9 @@ STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL mRpiFirmwareProtocol = {
RpiFirmwareNotifyXhciReset,
RpiFirmwareGetCurrentClockState,
RpiFirmwareSetClockState,
- RpiFirmwareNotifyGpioSetCfg
+ RpiFirmwareNotifyGpioSetCfg,
+ RpiFirmwareGetRtc,
+ RpiFirmwareSetRtc,
};
/**
diff --git a/Platform/RaspberryPi/Include/IndustryStandard/RpiMbox.h b/Platform/RaspberryPi/Include/IndustryStandard/RpiMbox.h
index 551c2b82..916b7442 100644
--- a/Platform/RaspberryPi/Include/IndustryStandard/RpiMbox.h
+++ b/Platform/RaspberryPi/Include/IndustryStandard/RpiMbox.h
@@ -93,6 +93,7 @@
#define RPI_MBOX_GET_POE_HAT_VAL 0x00030049
#define RPI_MBOX_SET_POE_HAT_VAL 0x00030050
#define RPI_MBOX_NOTIFY_XHCI_RESET 0x00030058
+#define RPI_MBOX_GET_RTC_REG 0x00030087
#define RPI_MBOX_SET_CLOCK_STATE 0x00038001
#define RPI_MBOX_SET_CLOCK_RATE 0x00038002
@@ -104,6 +105,7 @@
#define RPI_MBOX_SET_SDHOST_CLOCK 0x00038042
#define RPI_MBOX_SET_GPIO_CONFIG 0x00038043
#define RPI_MBOX_SET_PERIPH_REG 0x00038045
+#define RPI_MBOX_SET_RTC_REG 0x00038087
#define RPI_MBOX_ALLOC_FB 0x00040001
#define RPI_MBOX_FB_BLANK 0x00040002
diff --git a/Platform/RaspberryPi/Include/Protocol/RpiFirmware.h b/Platform/RaspberryPi/Include/Protocol/RpiFirmware.h
index 86bf1687..92ab7f84 100644
--- a/Platform/RaspberryPi/Include/Protocol/RpiFirmware.h
+++ b/Platform/RaspberryPi/Include/Protocol/RpiFirmware.h
@@ -1,5 +1,6 @@
/** @file
*
+ * Copyright (c) 2023, Mario Bălănică <mariobalanica02@gmail.com>
* Copyright (c) 2019, ARM Limited. All rights reserved.
* Copyright (c) 2017 - 2020, Andrei Warkentin <andrey.warkentin@gmail.com>
* Copyright (c) 2016, Linaro Limited. All rights reserved.
@@ -14,6 +15,17 @@
#define RASPBERRY_PI_FIRMWARE_PROTOL_GUID \
{ 0x0ACA9535, 0x7AD0, 0x4286, { 0xB0, 0x2E, 0x87, 0xFA, 0x7E, 0x2A, 0x57, 0x11 } }
+typedef enum {
+ RpiRtcTime = 0,
+ RpiRtcAlarm,
+ RpiRtcAlarmPending,
+ RpiRtcAlarmEnable,
+ RpiRtcBatteryChargeVoltage,
+ RpiRtcBatteryChargeVoltageMin,
+ RpiRtcBatteryChargeVoltageMax,
+ RpiRtcBatteryVoltage,
+} RASPBERRY_PI_RTC_REGISTER;
+
typedef
EFI_STATUS
(EFIAPI *SET_POWER_STATE) (
@@ -141,6 +153,20 @@ EFI_STATUS
UINTN State
);
+typedef
+EFI_STATUS
+(EFIAPI *GET_RTC) (
+ IN RASPBERRY_PI_RTC_REGISTER Register,
+ OUT UINT32 *Value
+ );
+
+typedef
+EFI_STATUS
+(EFIAPI *SET_RTC) (
+ IN RASPBERRY_PI_RTC_REGISTER Register,
+ IN UINT32 Value
+ );
+
typedef struct {
SET_POWER_STATE SetPowerState;
GET_MAC_ADDRESS GetMacAddress;
@@ -162,6 +188,8 @@ typedef struct {
GET_CLOCK_STATE GetClockState;
SET_CLOCK_STATE SetClockState;
GPIO_SET_CFG SetGpioConfig;
+ GET_RTC GetRtc;
+ SET_RTC SetRtc;
} RASPBERRY_PI_FIRMWARE_PROTOCOL;
extern EFI_GUID gRaspberryPiFirmwareProtocolGuid;
diff --git a/Platform/RaspberryPi/Library/RpiRtcLib/RpiRtcLib.c b/Platform/RaspberryPi/Library/RpiRtcLib/RpiRtcLib.c
new file mode 100644
index 00000000..298b3678
--- /dev/null
+++ b/Platform/RaspberryPi/Library/RpiRtcLib/RpiRtcLib.c
@@ -0,0 +1,305 @@
+/** @file
+ *
+ * Copyright (c) 2023, Mario Bălănică <mariobalanica02@gmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause-Patent
+ *
+ **/
+
+#include <PiDxe.h>
+
+#include <Library/DebugLib.h>
+#include <Library/RealTimeClockLib.h>
+#include <Library/TimeBaseLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeLib.h>
+
+#include <Protocol/RpiFirmware.h>
+
+STATIC RASPBERRY_PI_FIRMWARE_PROTOCOL *mFwProtocol;
+
+STATIC
+VOID
+EFIAPI
+OffsetTimeZoneEpoch (
+ IN EFI_TIME *Time,
+ IN OUT UINT32 *EpochSeconds,
+ IN BOOLEAN Add
+ )
+{
+ //
+ // Adjust for the correct time zone
+ // The timezone setting also reflects the DST setting of the clock
+ //
+ if (Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) {
+ *EpochSeconds += (Add ? 1 : -1) * Time->TimeZone * SEC_PER_MIN;
+ } else if ((Time->Daylight & EFI_TIME_IN_DAYLIGHT) == EFI_TIME_IN_DAYLIGHT) {
+ // Convert to adjusted time, i.e. spring forwards one hour
+ *EpochSeconds += (Add ? 1 : -1) * SEC_PER_HOUR;
+ }
+}
+
+/**
+ 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.
+ @retval EFI_SECURITY_VIOLATION The time could not be retrieved due to an authentication failure.
+
+**/
+EFI_STATUS
+EFIAPI
+LibGetTime (
+ OUT EFI_TIME *Time,
+ OUT EFI_TIME_CAPABILITIES *Capabilities
+ )
+{
+ EFI_STATUS Status;
+ UINT32 EpochSeconds;
+
+ if (Time == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = mFwProtocol->GetRtc (RpiRtcTime, &EpochSeconds);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ OffsetTimeZoneEpoch (Time, &EpochSeconds, TRUE);
+
+ 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 to hardware error.
+
+**/
+EFI_STATUS
+EFIAPI
+LibSetTime (
+ IN EFI_TIME *Time
+ )
+{
+ EFI_STATUS Status;
+ UINT32 EpochSeconds;
+
+ if (Time == NULL || !IsTimeValid (Time)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ EpochSeconds = (UINT32)EfiTimeToEpoch (Time);
+
+ OffsetTimeZoneEpoch (Time, &EpochSeconds, FALSE);
+
+ Status = mFwProtocol->SetRtc (RpiRtcTime, EpochSeconds);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ 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
+ )
+{
+ EFI_STATUS Status;
+ UINT32 EpochSeconds;
+ UINT32 EnableVal;
+ UINT32 PendingVal;
+
+ if (Time == NULL || Enabled == NULL || Pending == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = mFwProtocol->GetRtc (RpiRtcAlarmEnable, &EnableVal);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = mFwProtocol->GetRtc (RpiRtcAlarmPending, &PendingVal);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *Enabled = EnableVal;
+ *Pending = PendingVal;
+
+ if (*Pending) {
+ // Acknowledge alarm
+ Status = mFwProtocol->SetRtc (RpiRtcAlarmPending, TRUE);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ Status = mFwProtocol->GetRtc (RpiRtcAlarm, &EpochSeconds);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ OffsetTimeZoneEpoch (Time, &EpochSeconds, TRUE);
+
+ EpochToEfiTime (EpochSeconds, Time);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ 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
+ )
+{
+ EFI_STATUS Status;
+ UINT32 EpochSeconds;
+
+ if (Enabled) {
+ if (Time == NULL || !IsTimeValid (Time)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ EpochSeconds = (UINT32)EfiTimeToEpoch (Time);
+
+ OffsetTimeZoneEpoch (Time, &EpochSeconds, FALSE);
+
+ Status = mFwProtocol->SetRtc (RpiRtcAlarm, EpochSeconds);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ Status = mFwProtocol->SetRtc (RpiRtcAlarmEnable, Enabled);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Fixup internal data so that EFI can be call in virtual mode.
+ Call the passed in Child Notify event and convert any pointers in
+ lib to virtual mode.
+
+ @param[in] Event The Event that is being processed
+ @param[in] Context Event Context
+
+**/
+STATIC
+VOID
+EFIAPI
+VirtualAddressChangeNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EfiConvertPointer (0x0, (VOID **)&mFwProtocol);
+}
+
+/**
+ 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_TIME Time;
+ EFI_EVENT VirtualAddressChangeEvent;
+
+ Status = gBS->LocateProtocol (
+ &gRaspberryPiFirmwareProtocolGuid,
+ NULL,
+ (VOID **)&mFwProtocol);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ VirtualAddressChangeNotify,
+ NULL,
+ &gEfiEventVirtualAddressChangeGuid,
+ &VirtualAddressChangeEvent);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Initial RTC time starts off at Epoch = 0, which is out
+ // of UEFI's bounds. Update it to the firmware build time.
+ //
+ Status = LibGetTime (&Time, NULL);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+
+ if (!IsTimeValid (&Time)) {
+ EpochToEfiTime (BUILD_EPOCH, &Time);
+ Status = LibSetTime (&Time);
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/Platform/RaspberryPi/Library/RpiRtcLib/RpiRtcLib.inf b/Platform/RaspberryPi/Library/RpiRtcLib/RpiRtcLib.inf
new file mode 100644
index 00000000..f271f39c
--- /dev/null
+++ b/Platform/RaspberryPi/Library/RpiRtcLib/RpiRtcLib.inf
@@ -0,0 +1,42 @@
+#/** @file
+#
+# Copyright (c) 2023, Mario Bălănică <mariobalanica02@gmail.com>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#**/
+
+[Defines]
+ INF_VERSION = 0x0001001A
+ BASE_NAME = RpiRtcLib
+ FILE_GUID = 2c823916-13b7-48f3-bb6d-a8cf438b91fd
+ MODULE_TYPE = BASE
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = RealTimeClockLib
+
+[Sources.common]
+ RpiRtcLib.c
+
+[Packages]
+ EmbeddedPkg/EmbeddedPkg.dec
+ MdePkg/MdePkg.dec
+ Platform/RaspberryPi/RaspberryPi.dec
+
+[LibraryClasses]
+ DebugLib
+ TimeBaseLib
+ UefiBootServicesTableLib
+ UefiRuntimeLib
+
+[Guids]
+ gEfiEventVirtualAddressChangeGuid
+
+[Protocols]
+ gRaspberryPiFirmwareProtocolGuid ## CONSUMES
+
+[Depex]
+ gRaspberryPiFirmwareProtocolGuid
+
+# Current usage of this library expects GCC in a UNIX-like shell environment with the date command
+[BuildOptions]
+ GCC:*_*_*_CC_FLAGS = -DBUILD_EPOCH=`date +%s`
diff --git a/Platform/RaspberryPi/RPi5/RPi5.dsc b/Platform/RaspberryPi/RPi5/RPi5.dsc
index f8e08de2..f2a1992e 100644
--- a/Platform/RaspberryPi/RPi5/RPi5.dsc
+++ b/Platform/RaspberryPi/RPi5/RPi5.dsc
@@ -547,7 +547,7 @@
EmbeddedPkg/ResetRuntimeDxe/ResetRuntimeDxe.inf
EmbeddedPkg/RealTimeClockRuntimeDxe/RealTimeClockRuntimeDxe.inf {
<LibraryClasses>
- RealTimeClockLib|EmbeddedPkg/Library/VirtualRealTimeClockLib/VirtualRealTimeClockLib.inf
+ RealTimeClockLib|Platform/RaspberryPi/Library/RpiRtcLib/RpiRtcLib.inf
}
EmbeddedPkg/MetronomeDxe/MetronomeDxe.inf
--
2.51.2