VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/CpuTimerDxeRiscV64/Timer.c@ 106411

最後變更 在這個檔案從106411是 105670,由 vboxsync 提交於 6 月 前

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

  • 屬性 svn:eol-style 設為 native
檔案大小: 11.5 KB
 
1/** @file
2 RISC-V Timer Architectural Protocol
3
4 Copyright (c) 2019, Hewlett Packard Enterprise Development LP. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8**/
9
10#include <Library/BaseLib.h>
11#include <Library/BaseRiscVSbiLib.h>
12#include <Library/UefiLib.h>
13#include "Timer.h"
14
15//
16// The handle onto which the Timer Architectural Protocol will be installed
17//
18STATIC EFI_HANDLE mTimerHandle = NULL;
19
20//
21// The Timer Architectural Protocol that this driver produces
22//
23EFI_TIMER_ARCH_PROTOCOL mTimer = {
24 TimerDriverRegisterHandler,
25 TimerDriverSetTimerPeriod,
26 TimerDriverGetTimerPeriod,
27 TimerDriverGenerateSoftInterrupt
28};
29
30//
31// Pointer to the CPU Architectural Protocol instance
32//
33EFI_CPU_ARCH_PROTOCOL *mCpu;
34
35//
36// The notification function to call on every timer interrupt.
37// A bug in the compiler prevents us from initializing this here.
38//
39STATIC EFI_TIMER_NOTIFY mTimerNotifyFunction;
40
41//
42// The current period of the timer interrupt
43//
44STATIC UINT64 mTimerPeriod = 0;
45STATIC UINT64 mLastPeriodStart = 0;
46
47//
48// Sstc support
49//
50STATIC BOOLEAN mSstcEnabled = FALSE;
51
52/**
53 Program the timer.
54
55 Program either using stimecmp (when Sstc extension is enabled) or using SBI
56 TIME call.
57
58 @param NextValue Core tick value the timer should expire.
59**/
60STATIC
61VOID
62RiscVProgramTimer (
63 UINT64 NextValue
64 )
65{
66 if (mSstcEnabled) {
67 RiscVSetSupervisorTimeCompareRegister (NextValue);
68 } else {
69 SbiSetTimer (NextValue);
70 }
71}
72
73/**
74 Check whether Sstc is enabled in PCD.
75
76**/
77STATIC
78BOOLEAN
79RiscVIsSstcEnabled (
80 VOID
81 )
82{
83 return ((PcdGet64 (PcdRiscVFeatureOverride) & RISCV_CPU_FEATURE_SSTC_BITMASK) != 0);
84}
85
86/**
87 Timer Interrupt Handler.
88
89 @param InterruptType The type of interrupt that occured
90 @param SystemContext A pointer to the system context when the interrupt occured
91**/
92VOID
93EFIAPI
94TimerInterruptHandler (
95 IN EFI_EXCEPTION_TYPE InterruptType,
96 IN EFI_SYSTEM_CONTEXT SystemContext
97 )
98{
99 EFI_TPL OriginalTPL;
100 UINT64 PeriodStart;
101
102 PeriodStart = RiscVReadTimer ();
103
104 OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL);
105 if (mTimerNotifyFunction != NULL) {
106 //
107 // For any number of reasons, the ticks could be coming
108 // in slower than mTimerPeriod. For example, some code
109 // is doing a *lot* of stuff inside an EFI_TPL_HIGH
110 // critical section, and this should not cause the EFI
111 // time to increment slower. So when we take an interrupt,
112 // account for the actual time passed.
113 //
114 mTimerNotifyFunction (
115 DivU64x32 (
116 EFI_TIMER_PERIOD_SECONDS (PeriodStart - mLastPeriodStart),
117 PcdGet64 (PcdCpuCoreCrystalClockFrequency)
118 )
119 );
120 }
121
122 if (mTimerPeriod == 0) {
123 RiscVDisableTimerInterrupt ();
124 gBS->RestoreTPL (OriginalTPL);
125 return;
126 }
127
128 mLastPeriodStart = PeriodStart;
129 PeriodStart += DivU64x32 (
130 MultU64x32 (
131 mTimerPeriod,
132 PcdGet64 (PcdCpuCoreCrystalClockFrequency)
133 ),
134 1000000u
135 ); // convert to tick
136 RiscVProgramTimer (PeriodStart);
137 RiscVEnableTimerInterrupt (); // enable SMode timer int
138 gBS->RestoreTPL (OriginalTPL);
139}
140
141/**
142
143 This function registers the handler NotifyFunction so it is called every time
144 the timer interrupt fires. It also passes the amount of time since the last
145 handler call to the NotifyFunction. If NotifyFunction is NULL, then the
146 handler is unregistered. If the handler is registered, then EFI_SUCCESS is
147 returned. If the CPU does not support registering a timer interrupt handler,
148 then EFI_UNSUPPORTED is returned. If an attempt is made to register a handler
149 when a handler is already registered, then EFI_ALREADY_STARTED is returned.
150 If an attempt is made to unregister a handler when a handler is not registered,
151 then EFI_INVALID_PARAMETER is returned. If an error occurs attempting to
152 register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR
153 is returned.
154
155 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
156 @param NotifyFunction The function to call when a timer interrupt fires. This
157 function executes at TPL_HIGH_LEVEL. The DXE Core will
158 register a handler for the timer interrupt, so it can know
159 how much time has passed. This information is used to
160 signal timer based events. NULL will unregister the handler.
161
162 @retval EFI_SUCCESS The timer handler was registered.
163 @retval EFI_UNSUPPORTED The platform does not support timer interrupts.
164 @retval EFI_ALREADY_STARTED NotifyFunction is not NULL, and a handler is already
165 registered.
166 @retval EFI_INVALID_PARAMETER NotifyFunction is NULL, and a handler was not
167 previously registered.
168 @retval EFI_DEVICE_ERROR The timer handler could not be registered.
169
170**/
171EFI_STATUS
172EFIAPI
173TimerDriverRegisterHandler (
174 IN EFI_TIMER_ARCH_PROTOCOL *This,
175 IN EFI_TIMER_NOTIFY NotifyFunction
176 )
177{
178 DEBUG ((DEBUG_INFO, "TimerDriverRegisterHandler(0x%lx) called\n", NotifyFunction));
179 mTimerNotifyFunction = NotifyFunction;
180 return EFI_SUCCESS;
181}
182
183/**
184
185 This function adjusts the period of timer interrupts to the value specified
186 by TimerPeriod. If the timer period is updated, then the selected timer
187 period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If
188 the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
189 If an error occurs while attempting to update the timer period, then the
190 timer hardware will be put back in its state prior to this call, and
191 EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt
192 is disabled. This is not the same as disabling the CPU's interrupts.
193 Instead, it must either turn off the timer hardware, or it must adjust the
194 interrupt controller so that a CPU interrupt is not generated when the timer
195 interrupt fires.
196
197
198 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
199 @param TimerPeriod The rate to program the timer interrupt in 100 nS units. If
200 the timer hardware is not programmable, then EFI_UNSUPPORTED is
201 returned. If the timer is programmable, then the timer period
202 will be rounded up to the nearest timer period that is supported
203 by the timer hardware. If TimerPeriod is set to 0, then the
204 timer interrupts will be disabled.
205
206 @retval EFI_SUCCESS The timer period was changed.
207 @retval EFI_UNSUPPORTED The platform cannot change the period of the timer interrupt.
208 @retval EFI_DEVICE_ERROR The timer period could not be changed due to a device error.
209
210**/
211EFI_STATUS
212EFIAPI
213TimerDriverSetTimerPeriod (
214 IN EFI_TIMER_ARCH_PROTOCOL *This,
215 IN UINT64 TimerPeriod
216 )
217{
218 UINT64 PeriodStart;
219
220 DEBUG ((DEBUG_INFO, "TimerDriverSetTimerPeriod(0x%lx)\n", TimerPeriod));
221
222 if (TimerPeriod == 0) {
223 mTimerPeriod = 0;
224 RiscVDisableTimerInterrupt (); // Disable SMode timer int
225 return EFI_SUCCESS;
226 }
227
228 mTimerPeriod = TimerPeriod / 10; // convert unit from 100ns to 1us
229
230 mLastPeriodStart = RiscVReadTimer ();
231 PeriodStart = mLastPeriodStart;
232 PeriodStart += DivU64x32 (
233 MultU64x32 (
234 mTimerPeriod,
235 PcdGet64 (PcdCpuCoreCrystalClockFrequency)
236 ),
237 1000000u
238 ); // convert to tick
239 RiscVProgramTimer (PeriodStart);
240 mCpu->EnableInterrupt (mCpu);
241 RiscVEnableTimerInterrupt (); // enable SMode timer int
242 return EFI_SUCCESS;
243}
244
245/**
246
247 This function retrieves the period of timer interrupts in 100 ns units,
248 returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod
249 is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is
250 returned, then the timer is currently disabled.
251
252
253 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
254 @param TimerPeriod A pointer to the timer period to retrieve in 100 ns units. If
255 0 is returned, then the timer is currently disabled.
256
257 @retval EFI_SUCCESS The timer period was returned in TimerPeriod.
258 @retval EFI_INVALID_PARAMETER TimerPeriod is NULL.
259
260**/
261EFI_STATUS
262EFIAPI
263TimerDriverGetTimerPeriod (
264 IN EFI_TIMER_ARCH_PROTOCOL *This,
265 OUT UINT64 *TimerPeriod
266 )
267{
268 *TimerPeriod = mTimerPeriod;
269 return EFI_SUCCESS;
270}
271
272/**
273
274 This function generates a soft timer interrupt. If the platform does not support soft
275 timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.
276 If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()
277 service, then a soft timer interrupt will be generated. If the timer interrupt is
278 enabled when this service is called, then the registered handler will be invoked. The
279 registered handler should not be able to distinguish a hardware-generated timer
280 interrupt from a software-generated timer interrupt.
281
282
283 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
284
285 @retval EFI_SUCCESS The soft timer interrupt was generated.
286 @retval EFI_UNSUPPORTEDT The platform does not support the generation of soft timer interrupts.
287
288**/
289EFI_STATUS
290EFIAPI
291TimerDriverGenerateSoftInterrupt (
292 IN EFI_TIMER_ARCH_PROTOCOL *This
293 )
294{
295 return EFI_SUCCESS;
296}
297
298/**
299 Initialize the Timer Architectural Protocol driver
300
301 @param ImageHandle ImageHandle of the loaded driver
302 @param SystemTable Pointer to the System Table
303
304 @retval EFI_SUCCESS Timer Architectural Protocol created
305 @retval EFI_OUT_OF_RESOURCES Not enough resources available to initialize driver.
306 @retval EFI_DEVICE_ERROR A device error occured attempting to initialize the driver.
307
308**/
309EFI_STATUS
310EFIAPI
311TimerDriverInitialize (
312 IN EFI_HANDLE ImageHandle,
313 IN EFI_SYSTEM_TABLE *SystemTable
314 )
315{
316 EFI_STATUS Status;
317
318 //
319 // Initialize the pointer to our notify function.
320 //
321 mTimerNotifyFunction = NULL;
322
323 if (RiscVIsSstcEnabled ()) {
324 mSstcEnabled = TRUE;
325 DEBUG ((DEBUG_INFO, "TimerDriverInitialize: Timer interrupt is via Sstc extension\n"));
326 }
327
328 //
329 // Make sure the Timer Architectural Protocol is not already installed in the system
330 //
331 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiTimerArchProtocolGuid);
332
333 //
334 // Find the CPU architectural protocol.
335 //
336 Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu);
337 ASSERT_EFI_ERROR (Status);
338
339 //
340 // Force the timer to be disabled
341 //
342 Status = TimerDriverSetTimerPeriod (&mTimer, 0);
343 ASSERT_EFI_ERROR (Status);
344
345 //
346 // Install interrupt handler for RISC-V Timer.
347 //
348 Status = mCpu->RegisterInterruptHandler (
349 mCpu,
350 EXCEPT_RISCV_IRQ_TIMER_FROM_SMODE,
351 TimerInterruptHandler
352 );
353 ASSERT_EFI_ERROR (Status);
354
355 //
356 // Force the timer to be enabled at its default period
357 //
358 Status = TimerDriverSetTimerPeriod (&mTimer, DEFAULT_TIMER_TICK_DURATION);
359 ASSERT_EFI_ERROR (Status);
360
361 //
362 // Install the Timer Architectural Protocol onto a new handle
363 //
364 Status = gBS->InstallMultipleProtocolInterfaces (
365 &mTimerHandle,
366 &gEfiTimerArchProtocolGuid,
367 &mTimer,
368 NULL
369 );
370 ASSERT_EFI_ERROR (Status);
371 return Status;
372}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette