TianoCore EDK2 master
Loading...
Searching...
No Matches
TimerDxe.c
Go to the documentation of this file.
1
10#include <PiDxe.h>
11
12#include <Library/ArmLib.h>
13#include <Library/BaseLib.h>
14#include <Library/DebugLib.h>
17#include <Library/UefiLib.h>
18#include <Library/PcdLib.h>
19#include <Library/IoLib.h>
21
22#include <Protocol/Timer.h>
24
25// The notification function to call on every timer interrupt.
26EFI_TIMER_NOTIFY mTimerNotifyFunction = (EFI_TIMER_NOTIFY)NULL;
27EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL;
28
29// The current period of the timer interrupt
30UINT64 mTimerPeriod = 0;
31// The latest Timer Tick calculated for mTimerPeriod
32UINT64 mTimerTicks = 0;
33// Number of elapsed period since the last Timer interrupt
34UINT64 mElapsedPeriod = 1;
35
36// Cached copy of the Hardware Interrupt protocol instance
38
68EFIAPI
72 )
73{
74 if ((NotifyFunction == NULL) && (mTimerNotifyFunction == NULL)) {
75 return EFI_INVALID_PARAMETER;
76 }
77
78 if ((NotifyFunction != NULL) && (mTimerNotifyFunction != NULL)) {
79 return EFI_ALREADY_STARTED;
80 }
81
82 mTimerNotifyFunction = NotifyFunction;
83
84 return EFI_SUCCESS;
85}
86
90VOID
91EFIAPI
93 IN EFI_EVENT Event,
94 IN VOID *Context
95 )
96{
97 ArmGenericTimerDisableTimer ();
98}
99
129EFIAPI
132 IN UINT64 TimerPeriod
133 )
134{
135 UINT64 CounterValue;
136 UINT64 TimerTicks;
137 EFI_TPL OriginalTPL;
138
139 // Always disable the timer
140 ArmGenericTimerDisableTimer ();
141
142 if (TimerPeriod != 0) {
143 // mTimerTicks = TimerPeriod in 1ms unit x Frequency.10^-3
144 // = TimerPeriod.10^-4 x Frequency.10^-3
145 // = (TimerPeriod x Frequency) x 10^-7
146 TimerTicks = MultU64x32 (TimerPeriod, ArmGenericTimerGetTimerFreq ());
147 TimerTicks = DivU64x32 (TimerTicks, 10000000U);
148
149 // Raise TPL to update the mTimerTicks and mTimerPeriod to ensure these values
150 // are coherent in the interrupt handler
151 OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
152
153 mTimerTicks = TimerTicks;
154 mTimerPeriod = TimerPeriod;
155 mElapsedPeriod = 1;
156
157 gBS->RestoreTPL (OriginalTPL);
158
159 // Get value of the current timer
160 CounterValue = ArmGenericTimerGetSystemCount ();
161 // Set the interrupt in Current Time + mTimerTick
162 ArmGenericTimerSetCompareVal (CounterValue + mTimerTicks);
163
164 // Enable the timer
165 ArmGenericTimerEnableTimer ();
166 } else {
167 // Save the new timer period
168 mTimerPeriod = TimerPeriod;
169 // Reset the elapsed period
170 mElapsedPeriod = 1;
171 }
172
173 return EFI_SUCCESS;
174}
175
192EFIAPI
195 OUT UINT64 *TimerPeriod
196 )
197{
198 if (TimerPeriod == NULL) {
199 return EFI_INVALID_PARAMETER;
200 }
201
202 *TimerPeriod = mTimerPeriod;
203 return EFI_SUCCESS;
204}
205
222EFIAPI
225 )
226{
227 return EFI_UNSUPPORTED;
228}
229
269};
270
284VOID
285EFIAPI
287 IN HARDWARE_INTERRUPT_SOURCE Source,
288 IN EFI_SYSTEM_CONTEXT SystemContext
289 )
290{
291 EFI_TPL OriginalTPL;
292 UINT64 CurrentValue;
293 UINT64 CompareValue;
294
295 //
296 // DXE core uses this callback for the EFI timer tick. The DXE core uses locks
297 // that raise to TPL_HIGH and then restore back to current level. Thus we need
298 // to make sure TPL level is set to TPL_HIGH while we are handling the timer tick.
299 //
300 OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
301
302 // Signal end of interrupt early to help avoid losing subsequent ticks
303 // from long duration handlers
304 gInterrupt->EndOfInterrupt (gInterrupt, Source);
305
306 // Check if the timer interrupt is active
307 if ((ArmGenericTimerGetTimerCtrlReg ()) & ARM_ARCH_TIMER_ISTATUS) {
308 if (mTimerNotifyFunction != 0) {
309 mTimerNotifyFunction (mTimerPeriod * mElapsedPeriod);
310 }
311
312 //
313 // Reload the Timer
314 //
315
316 // Get current counter value
317 CurrentValue = ArmGenericTimerGetSystemCount ();
318 // Get the counter value to compare with
319 CompareValue = ArmGenericTimerGetCompareVal ();
320
321 // This loop is needed in case we missed interrupts (eg: case when the interrupt handling
322 // has taken longer than mTickPeriod).
323 // Note: Physical Counter is counting up
324 mElapsedPeriod = 0;
325 do {
326 CompareValue += mTimerTicks;
327 mElapsedPeriod++;
328 } while (CompareValue < CurrentValue);
329
330 // Set next compare value
331 ArmGenericTimerSetCompareVal (CompareValue);
332 ArmGenericTimerReenableTimer ();
333 ArmInstructionSynchronizationBarrier ();
334 }
335
336 gBS->RestoreTPL (OriginalTPL);
337}
338
353EFIAPI
355 IN EFI_HANDLE ImageHandle,
356 IN EFI_SYSTEM_TABLE *SystemTable
357 )
358{
359 EFI_HANDLE Handle;
360 EFI_STATUS Status;
361 UINTN TimerCtrlReg;
362 UINT32 TimerHypIntrNum;
363
364 if (ArmIsArchTimerImplemented () == 0) {
365 DEBUG ((DEBUG_ERROR, "ARM Architectural Timer is not available in the CPU, hence can't use this Driver \n"));
366 ASSERT (0);
367 }
368
369 // Find the interrupt controller protocol. ASSERT if not found.
370 Status = gBS->LocateProtocol (&gHardwareInterruptProtocolGuid, NULL, (VOID **)&gInterrupt);
371 ASSERT_EFI_ERROR (Status);
372
373 // Disable the timer
374 TimerCtrlReg = ArmGenericTimerGetTimerCtrlReg ();
375 TimerCtrlReg |= ARM_ARCH_TIMER_IMASK;
376 TimerCtrlReg &= ~ARM_ARCH_TIMER_ENABLE;
377 ArmGenericTimerSetTimerCtrlReg (TimerCtrlReg);
378 Status = TimerDriverSetTimerPeriod (&gTimer, 0);
379 ASSERT_EFI_ERROR (Status);
380
381 // Install secure and Non-secure interrupt handlers
382 // Note: Because it is not possible to determine the security state of the
383 // CPU dynamically, we just install interrupt handler for both sec and non-sec
384 // timer PPI
385 Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerVirtIntrNum), TimerInterruptHandler);
386 ASSERT_EFI_ERROR (Status);
387
388 //
389 // The hypervisor timer interrupt may be omitted by implementations that
390 // execute under virtualization.
391 //
392 TimerHypIntrNum = PcdGet32 (PcdArmArchTimerHypIntrNum);
393 if (TimerHypIntrNum != 0) {
394 Status = gInterrupt->RegisterInterruptSource (gInterrupt, TimerHypIntrNum, TimerInterruptHandler);
395 ASSERT_EFI_ERROR (Status);
396 }
397
398 Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerSecIntrNum), TimerInterruptHandler);
399 ASSERT_EFI_ERROR (Status);
400
401 Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerIntrNum), TimerInterruptHandler);
402 ASSERT_EFI_ERROR (Status);
403
404 // Set up default timer
405 Status = TimerDriverSetTimerPeriod (&gTimer, FixedPcdGet32 (PcdTimerPeriod)); // TIMER_DEFAULT_PERIOD
406 ASSERT_EFI_ERROR (Status);
407
408 Handle = NULL;
409 // Install the Timer Architectural Protocol onto a new handle
410 Status = gBS->InstallMultipleProtocolInterfaces (
411 &Handle,
412 &gEfiTimerArchProtocolGuid,
413 &gTimer,
414 NULL
415 );
416 ASSERT_EFI_ERROR (Status);
417
418 // Everything is ready, unmask and enable timer interrupts
419 TimerCtrlReg = ARM_ARCH_TIMER_ENABLE;
420 ArmGenericTimerSetTimerCtrlReg (TimerCtrlReg);
421
422 // Register for an ExitBootServicesEvent
423 Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent);
424 ASSERT_EFI_ERROR (Status);
425
426 return Status;
427}
UINT64 UINTN
UINT64 EFIAPI DivU64x32(IN UINT64 Dividend, IN UINT32 Divisor)
Definition: DivU64x32.c:29
UINT64 EFIAPI MultU64x32(IN UINT64 Multiplicand, IN UINT32 Multiplier)
Definition: MultU64x32.c:27
#define NULL
Definition: Base.h:319
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define ASSERT_EFI_ERROR(StatusParameter)
Definition: DebugLib.h:462
#define DEBUG(Expression)
Definition: DebugLib.h:434
VOID(EFIAPI * EFI_TIMER_NOTIFY)(IN UINT64 Time)
Definition: Timer.h:41
#define FixedPcdGet32(TokenName)
Definition: PcdLib.h:92
#define PcdGet32(TokenName)
Definition: PcdLib.h:362
VOID EFIAPI NotifyFunction(IN EFI_EVENT Event, IN VOID *Context)
Definition: ScsiBus.c:1492
VOID EFIAPI ExitBootServicesEvent(IN EFI_EVENT Event, IN VOID *Context)
Definition: TimerDxe.c:92
EFI_STATUS EFIAPI TimerDriverRegisterHandler(IN EFI_TIMER_ARCH_PROTOCOL *This, IN EFI_TIMER_NOTIFY NotifyFunction)
Definition: TimerDxe.c:69
EFI_STATUS EFIAPI TimerInitialize(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable)
Definition: TimerDxe.c:354
EFI_STATUS EFIAPI TimerDriverGetTimerPeriod(IN EFI_TIMER_ARCH_PROTOCOL *This, OUT UINT64 *TimerPeriod)
Definition: TimerDxe.c:193
EFI_STATUS EFIAPI TimerDriverSetTimerPeriod(IN EFI_TIMER_ARCH_PROTOCOL *This, IN UINT64 TimerPeriod)
Definition: TimerDxe.c:130
EFI_STATUS EFIAPI TimerDriverGenerateSoftInterrupt(IN EFI_TIMER_ARCH_PROTOCOL *This)
Definition: TimerDxe.c:223
EFI_TIMER_ARCH_PROTOCOL gTimer
Definition: TimerDxe.c:264
VOID EFIAPI TimerInterruptHandler(IN HARDWARE_INTERRUPT_SOURCE Source, IN EFI_SYSTEM_CONTEXT SystemContext)
Definition: TimerDxe.c:286
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
VOID * EFI_EVENT
Definition: UefiBaseType.h:37
UINTN EFI_TPL
Definition: UefiBaseType.h:41
VOID * EFI_HANDLE
Definition: UefiBaseType.h:33
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
EFI_BOOT_SERVICES * gBS