Thanks,
Chao
--------
This driver produces Timer Architectural Protocol,Registers a timer interrupt and initializes the timer.REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4054Cc: Bibo Mao <maobibo@loongson.cn>Cc: Chao Li <lichao@loongson.cn>Cc: Leif Lindholm <quic_llindhol@quicinc.com>Cc: Liming Gao <gaoliming@byosoft.com.cn>Cc: Michael D Kinney <michael.d.kinney@intel.com>Signed-off-by: xianglai li <lixianglai@loongson.cn>---.../Drivers/StableTimerDxe/Timer.c | 388 ++++++++++++++++++.../Drivers/StableTimerDxe/Timer.h | 172 ++++++++.../Drivers/StableTimerDxe/TimerConfig.S | 38 ++.../Drivers/StableTimerDxe/TimerDxe.inf | 44 ++4 files changed, 642 insertions(+)create mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.ccreate mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.hcreate mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/TimerConfig.Screate mode 100644 Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/TimerDxe.infdiff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.c b/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.cnew file mode 100644index 0000000000..e09da71272--- /dev/null+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.c@@ -0,0 +1,388 @@+/** @file+ Timer Architectural Protocol as defined in the DXE CIS++ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>++ SPDX-License-Identifier: BSD-2-Clause-Patent++**/++#include <Protocol/Cpu.h>+#include "Library/Cpu.h"+#include <Library/DebugLib.h>+#include <Library/StableTimer.h>+#include "Timer.h"+#include <Library/TimerLib.h>+#include <Library/UefiBootServicesTableLib.h>++//+// The handle onto which the Timer Architectural Protocol will be installed+//+EFI_HANDLE mTimerHandle = NULL;+EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL;++//+// The Timer Architectural Protocol that this driver produces+//+EFI_TIMER_ARCH_PROTOCOL mTimer = {+ TimerDriverRegisterHandler,+ TimerDriverSetTimerPeriod,+ TimerDriverGetTimerPeriod,+ TimerDriverGenerateSoftInterrupt+};++//+// Pointer to the CPU Architectural Protocol instance+//+EFI_CPU_ARCH_PROTOCOL *mCpu;++//+// The notification function to call on every timer interrupt.+// A bug in the compiler prevents us from initializing this here.+//+EFI_TIMER_NOTIFY mTimerNotifyFunction;++//+// The current period of the timer interrupt+//+volatile UINT64 mTimerPeriod = 0;+volatile UINT64 mTimerTicks = 0;++//+// Const frequence in Hz+//+extern UINT32 StableTimerFreq;++/**+ Sets the counter value for timer.++ @param Count The 16-bit counter value to program into stable timer.++ @retval VOID+**/+VOID+SetPitCount (+ IN UINT64 Count+ )+{+ if (Count <= 4) {+ return;+ }++ Count &= LOONGARCH_CSR_TMCFG_TIMEVAL;+ Count |= LOONGARCH_CSR_TMCFG_EN | LOONGARCH_CSR_TMCFG_PERIOD;+ LoongarchWriteqTmcfg (Count);+}++/**+ Timer Interrupt Handler.++ @param InterruptType The type of interrupt that occurred+ @param SystemContext A pointer to the system context when the interrupt occurred++ @retval VOID+**/+VOID+EFIAPI+TimerInterruptHandler (+ IN EFI_EXCEPTION_TYPE InterruptType,+ IN EFI_SYSTEM_CONTEXT SystemContext+ )+{+ EFI_TPL OriginalTPL;++ OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);++ //+ // Clear interrupt.+ //+ LoongarchWriteqTintclr (0x1);++ if (mTimerNotifyFunction != NULL) {+ //+ // @bug : This does not handle missed timer interrupts+ //+ mTimerNotifyFunction (mTimerPeriod);+ }++ gBS->RestoreTPL (OriginalTPL);+}++/**+ This function registers the handler NotifyFunction so it is called every time+ the timer interrupt fires. It also passes the amount of time since the last+ handler call to the NotifyFunction. If NotifyFunction is NULL, then the+ handler is unregistered. If the handler is registered, then EFI_SUCCESS is+ returned. If the CPU does not support registering a timer interrupt handler,+ then EFI_UNSUPPORTED is returned. If an attempt is made to register a handler+ when a handler is already registered, then EFI_ALREADY_STARTED is returned.+ If an attempt is made to unregister a handler when a handler is not registered,+ then EFI_INVALID_PARAMETER is returned. If an error occurs attempting to+ register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR+ is returned.++ @param This The EFI_TIMER_ARCH_PROTOCOL instance.+ @param NotifyFunction The function to call when a timer interrupt fires. This+ function executes at TPL_HIGH_LEVEL. The DXE Core will+ register a handler for the timer interrupt, so it can know+ how much time has passed. This information is used to+ signal timer based events. NULL will unregister the handler.++ @retval EFI_SUCCESS The timer handler was registered.+ @retval EFI_UNSUPPORTED The platform does not support timer interrupts.+ @retval EFI_ALREADY_STARTED NotifyFunction is not NULL, and a handler is already+ registered.+ @retval EFI_INVALID_PARAMETER NotifyFunction is NULL, and a handler was not+ previously registered.+ @retval EFI_DEVICE_ERROR The timer handler could not be registered.+**/+EFI_STATUS+EFIAPI+TimerDriverRegisterHandler (+ IN EFI_TIMER_ARCH_PROTOCOL *This,+ IN EFI_TIMER_NOTIFY NotifyFunction+ )+{+ //+ // Check for invalid parameters+ //+ if ((NotifyFunction == NULL)+ && (mTimerNotifyFunction == NULL))+ {+ return EFI_INVALID_PARAMETER;+ }++ if ((NotifyFunction != NULL)+ && mTimerNotifyFunction != NULL)+ {+ return EFI_ALREADY_STARTED;+ }++ mTimerNotifyFunction = NotifyFunction;++ return EFI_SUCCESS;+}++/**+ This function adjusts the period of timer interrupts to the value specified+ by TimerPeriod. If the timer period is updated, then the selected timer+ period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If+ the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.+ If an error occurs while attempting to update the timer period, then the+ timer hardware will be put back in its state prior to this call, and+ EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt+ is disabled. This is not the same as disabling the CPU's interrupts.+ Instead, it must either turn off the timer hardware, or it must adjust the+ interrupt controller so that a CPU interrupt is not generated when the timer+ interrupt fires.++ @param This The EFI_TIMER_ARCH_PROTOCOL instance.+ @param TimerPeriod The rate to program the timer interrupt in 100 nS units. If+ the timer hardware is not programmable, then EFI_UNSUPPORTED is+ returned. If the timer is programmable, then the timer period+ will be rounded up to the nearest timer period that is supported+ by the timer hardware. If TimerPeriod is set to 0, then the+ timer interrupts will be disabled.++ @retval EFI_SUCCESS The timer period was changed.+ @retval EFI_UNSUPPORTED The platform cannot change the period of the timer interrupt.+ @retval EFI_DEVICE_ERROR The timer period could not be changed due to a device error.+**/+EFI_STATUS+EFIAPI+TimerDriverSetTimerPeriod (+ IN EFI_TIMER_ARCH_PROTOCOL *This,+ IN UINT64 TimerPeriod+ )+{+ UINT64 TimerCount;++ if (TimerPeriod == 0) {+ //+ // Disable timer interrupt for a TimerPeriod of 0+ //+ mCpu->DisableInterrupt (mCpu);+ } else {++ TimerCount = TimerPeriod * StableTimerFreq / 10000000ULL;++ if (TimerCount >= BIT48) {+ TimerCount = 0;+ }++ //+ // Program the stable timer with the new count value+ //+ mTimerTicks = TimerCount;+ SetPitCount (TimerCount);++ //+ // Enable timer interrupt+ //+ mCpu->EnableInterrupt (mCpu);+ }++ //+ // Save the new timer period+ //+ mTimerPeriod = TimerPeriod;++ return EFI_SUCCESS;+}++/**+ This function retrieves the period of timer interrupts in 100 ns units,+ returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod+ is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is+ returned, then the timer is currently disabled.++ @param This The EFI_TIMER_ARCH_PROTOCOL instance.+ @param TimerPeriod A pointer to the timer period to retrieve in 100 ns units. If+ 0 is returned, then the timer is currently disabled.++ @retval EFI_SUCCESS The timer period was returned in TimerPeriod.+ @retval EFI_INVALID_PARAMETER TimerPeriod is NULL.+**/+EFI_STATUS+EFIAPI+TimerDriverGetTimerPeriod (+ IN EFI_TIMER_ARCH_PROTOCOL *This,+ OUT UINT64 *TimerPeriod+ )+{+ if (TimerPeriod == NULL) {+ return EFI_INVALID_PARAMETER;+ }++ *TimerPeriod = mTimerPeriod;++ return EFI_SUCCESS;+}++/**+ Disable the timer+ DXE Core will disable the timer after all the event handlers have run.++ @param[in] Event The Event that is being processed+ @param[in] Context Event Context+**/+VOID+EFIAPI+ExitBootServicesEvent (+ IN EFI_EVENT Event,+ IN VOID *Context+ )+{+ /*+ * Disable timer interrupt when exiting boot service+ */+ LoongarchWriteqTmcfg (0);+}++/**+ This function generates a soft timer interrupt. If the platform does not support soft+ timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.+ If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler ()+ service, then a soft timer interrupt will be generated. If the timer interrupt is+ enabled when this service is called, then the registered handler will be invoked. The+ registered handler should not be able to distinguish a hardware-generated timer+ interrupt from a software-generated timer interrupt.++ @param This The EFI_TIMER_ARCH_PROTOCOL instance.++ @retval EFI_SUCCESS The soft timer interrupt was generated.+ @retval EFI_UNSUPPORTED The platform does not support the generation of soft timer interrupts.+**/+EFI_STATUS+EFIAPI+TimerDriverGenerateSoftInterrupt (+ IN EFI_TIMER_ARCH_PROTOCOL *This+ )+{+ return EFI_UNSUPPORTED;+}++/**+ Initialize the Timer Architectural Protocol driver++ @param ImageHandle ImageHandle of the loaded driver+ @param SystemTable Pointer to the System Table++ @retval EFI_SUCCESS Timer Architectural Protocol created+ @retval EFI_OUT_OF_RESOURCES Not enough resources available to initialize driver.+ @retval EFI_DEVICE_ERROR A device error occurred attempting to initialize the driver.+**/+EFI_STATUS+EFIAPI+StableTimerDriverInitialize (+ IN EFI_HANDLE ImageHandle,+ IN EFI_SYSTEM_TABLE *SystemTable+ )+{+ EFI_STATUS Status;+ UINT32 TimerVector;++ //+ // Initialize the pointer to our notify function.+ //+ mTimerNotifyFunction = NULL;++ //+ // Make sure the Timer Architectural Protocol is not already installed in the system+ //+ ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiTimerArchProtocolGuid);++ //+ // Find the CPU architectural protocol.+ //+ Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &mCpu);+ ASSERT_EFI_ERROR (Status);++ //+ // Force the timer to be disabled+ //+ Status = TimerDriverSetTimerPeriod (&mTimer, 0);+ ASSERT_EFI_ERROR (Status);++ //+ // Calculate const frequence+ //+ StableTimerFreq = CalcConstFreq ();+ DEBUG ((DEBUG_INFO, "===========Stable timer freq %d Hz=============\n", StableTimerFreq));++ //+ // Install interrupt handler for Stable Timer #0 (ISA IRQ0)+ //+ TimerVector = EXCEPT_LOONGARCH_INT_TIMER;+ Status = mCpu->RegisterInterruptHandler (mCpu, TimerVector, TimerInterruptHandler);+ ASSERT_EFI_ERROR (Status);++ //+ // Enable TI local timer interrupt+ //+ CpuSetIP (1 << 11);++ //+ // Force the timer to be enabled at its default period+ //+ Status = TimerDriverSetTimerPeriod (&mTimer, DEFAULT_TIMER_TICK_DURATION);+ ASSERT_EFI_ERROR (Status);++ //+ // Install the Timer Architectural Protocol onto a new handle+ //+ Status = gBS->InstallMultipleProtocolInterfaces (+ &mTimerHandle,+ &gEfiTimerArchProtocolGuid, &mTimer,+ NULL+ );++ ASSERT_EFI_ERROR (Status);++ // Register for an ExitBootServicesEvent+ Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL,+ &EfiExitBootServicesEvent);+ ASSERT_EFI_ERROR (Status);++ return Status;+}diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.h b/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.hnew file mode 100644index 0000000000..84036cab00--- /dev/null+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/Timer.h@@ -0,0 +1,172 @@+/** @file+ Private data structures++ Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>++ SPDX-License-Identifier: BSD-2-Clause-Patent++**/++#ifndef TIMER_H_+#define TIMER_H_++#include <Protocol/Timer.h>++#define DEFAULT_TIMER_TICK_DURATION 100000 //10ms = 100000 100 ns units+#define SR_IP7 (1 << 15)+//+// Function Prototypes+//+extern UINT32 EFIAPI CpuGetCompare(VOID);+extern VOID EFIAPI CpuSetCompare(IN UINT32 val);+extern VOID EFIAPI CpuSetIP(IN UINT32 val);+extern VOID EFIAPI ClearC0Cause(IN UINT32 val);+extern VOID EFIAPI ClearC0Status(IN UINT32 val);+/**+ Initialize the Timer Architectural Protocol driver++ @param ImageHandle ImageHandle of the loaded driver+ @param SystemTable Pointer to the System Table++ @retval EFI_SUCCESS Timer Architectural Protocol created+ @retval EFI_OUT_OF_RESOURCES Not enough resources available to initialize driver.+ @retval EFI_DEVICE_ERROR A device error occurred attempting to initialize the driver.+**/+EFI_STATUS+EFIAPI+TimerDriverInitialize (+ IN EFI_HANDLE ImageHandle,+ IN EFI_SYSTEM_TABLE *SystemTable+ );++/**+ This function adjusts the period of timer interrupts to the value specified+ by TimerPeriod. If the timer period is updated, then the selected timer+ period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If+ the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.+ If an error occurs while attempting to update the timer period, then the+ timer hardware will be put back in its state prior to this call, and+ EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt+ is disabled. This is not the same as disabling the CPU's interrupts.+ Instead, it must either turn off the timer hardware, or it must adjust the+ interrupt controller so that a CPU interrupt is not generated when the timer+ interrupt fires.++ @param This The EFI_TIMER_ARCH_PROTOCOL instance.+ @param NotifyFunction The rate to program the timer interrupt in 100 nS units. If+ the timer hardware is not programmable, then EFI_UNSUPPORTED is+ returned. If the timer is programmable, then the timer period+ will be rounded up to the nearest timer period that is supported+ by the timer hardware. If TimerPeriod is set to 0, then the+ timer interrupts will be disabled.++ @retval EFI_SUCCESS The timer period was changed.+ @retval EFI_UNSUPPORTED The platform cannot change the period of the timer interrupt.+ @retval EFI_DEVICE_ERROR The timer period could not be changed due to a device error.+**/+EFI_STATUS+EFIAPI+TimerDriverRegisterHandler (+ IN EFI_TIMER_ARCH_PROTOCOL *This,+ IN EFI_TIMER_NOTIFY NotifyFunction+ );++/**+ This function adjusts the period of timer interrupts to the value specified+ by TimerPeriod. If the timer period is updated, then the selected timer+ period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If+ the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.+ If an error occurs while attempting to update the timer period, then the+ timer hardware will be put back in its state prior to this call, and+ EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt+ is disabled. This is not the same as disabling the CPU's interrupts.+ Instead, it must either turn off the timer hardware, or it must adjust the+ interrupt controller so that a CPU interrupt is not generated when the timer+ interrupt fires.++ @param This The EFI_TIMER_ARCH_PROTOCOL instance.+ @param TimerPeriod The rate to program the timer interrupt in 100 nS units. If+ the timer hardware is not programmable, then EFI_UNSUPPORTED is+ returned. If the timer is programmable, then the timer period+ will be rounded up to the nearest timer period that is supported+ by the timer hardware. If TimerPeriod is set to 0, then the+ timer interrupts will be disabled.++ @retval EFI_SUCCESS The timer period was changed.+ @retval EFI_UNSUPPORTED The platform cannot change the period of the timer interrupt.+ @retval EFI_DEVICE_ERROR The timer period could not be changed due to a device error.+**/+EFI_STATUS+EFIAPI+TimerDriverSetTimerPeriod (+ IN EFI_TIMER_ARCH_PROTOCOL *This,+ IN UINT64 TimerPeriod+ );++/**+ This function retrieves the period of timer interrupts in 100 ns units,+ returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod+ is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is+ returned, then the timer is currently disabled.++ @param This The EFI_TIMER_ARCH_PROTOCOL instance.+ @param TimerPeriod A pointer to the timer period to retrieve in 100 ns units. If+ 0 is returned, then the timer is currently disabled.++ @retval EFI_SUCCESS The timer period was returned in TimerPeriod.+ @retval EFI_INVALID_PARAMETER TimerPeriod is NULL.+**/+EFI_STATUS+EFIAPI+TimerDriverGetTimerPeriod (+ IN EFI_TIMER_ARCH_PROTOCOL *This,+ OUT UINT64 *TimerPeriod+ );++/**+ This function generates a soft timer interrupt. If the platform does not support soft+ timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.+ If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()+ service, then a soft timer interrupt will be generated. If the timer interrupt is+ enabled when this service is called, then the registered handler will be invoked. The+ registered handler should not be able to distinguish a hardware-generated timer+ interrupt from a software-generated timer interrupt.++ @param This The EFI_TIMER_ARCH_PROTOCOL instance.++ @retval EFI_SUCCESS The soft timer interrupt was generated.+ @retval EFI_UNSUPPORTED The platform does not support the generation of soft timer interrupts.+**/+EFI_STATUS+EFIAPI+TimerDriverGenerateSoftInterrupt (+ IN EFI_TIMER_ARCH_PROTOCOL *This+ );++/**+ Write Csr TMCFG register.++ @param A0 The value used to write to the TMCFG register++ @retval none+**/+extern+VOID+LoongarchWriteqTmcfg (+ IN UINT64 Val+ );++/**+ Write Csr TINTCLR register.++ @param A0 The value used to write to the TINTCLR register++ @retval none+**/+extern+VOID+LoongarchWriteqTintclr (+ IN UINT64 Val+ );++#endif // TIMER_H_diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/TimerConfig.S b/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/TimerConfig.Snew file mode 100644index 0000000000..2f364c193d--- /dev/null+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/TimerConfig.S@@ -0,0 +1,38 @@+#------------------------------------------------------------------------------+#+# Timer Cfg for LoongArch+#+# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>+#+# SPDX-License-Identifier: BSD-2-Clause-Patent+#+#------------------------------------------------------------------------------++#ifndef __ASSEMBLY__+#define __ASSEMBLY__+#endif++#include "Library/Cpu.h"++ASM_GLOBAL ASM_PFX(LoongarchWriteqTmcfg)+ASM_GLOBAL ASM_PFX(LoongarchWriteqTintclr)++#+# Write Csr TMCFG register.+# @param A0 The value used to write to the TMCFG register+# @retval none+#++ASM_PFX(LoongarchWriteqTmcfg):+ csrwr A0, LOONGARCH_CSR_TMCFG+ jirl ZERO, RA,0++#+# Write Csr TINTCLR register.+# @param A0 The value used to write to the TINTCLR register+# @retval none+#++ASM_PFX(LoongarchWriteqTintclr):+ csrwr A0, LOONGARCH_CSR_TINTCLR+ jirl ZERO, RA,0diff --git a/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/TimerDxe.inf b/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/TimerDxe.infnew file mode 100644index 0000000000..d4a07c8759--- /dev/null+++ b/Platform/Loongson/LoongArchQemuPkg/Drivers/StableTimerDxe/TimerDxe.inf@@ -0,0 +1,44 @@+## @file+# Stable timer driver that provides Timer Arch protocol.+#+# Copyright (c) 2022 Loongson Technology Corporation Limited. All rights reserved.<BR>+#+# SPDX-License-Identifier: BSD-2-Clause-Patent+#+##+[Defines]+ INF_VERSION = 0x00010005+ BASE_NAME = Timer+ MODULE_UNI_FILE = Timer.uni+ FILE_GUID = AEBE2648-47A9-40FA-83FD-06AA88443BB2+ MODULE_TYPE = DXE_DRIVER+ VERSION_STRING = 1.0+ ENTRY_POINT = StableTimerDriverInitialize++#+# VALID_ARCHITECTURES = LOONGARCH64+#++[Sources]+ Timer.h+ Timer.c+ TimerConfig.S++[Packages]+ MdePkg/MdePkg.dec+ Platform/Loongson/LoongArchQemuPkg/Loongson.dec++[LibraryClasses]+ UefiBootServicesTableLib+ BaseLib+ DebugLib+ UefiDriverEntryPoint+ IoLib+ TimerLib++[Protocols]+ gEfiCpuArchProtocolGuid ## CONSUMES+ gEfiTimerArchProtocolGuid ## PRODUCES++[depex]+ TRUE--2.31.1