VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/PcAtChipsetPkg/HpetTimerDxe/HpetTimer.c

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

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

  • 屬性 svn:eol-style 設為 native
檔案大小: 35.1 KB
 
1/** @file
2 Timer Architectural Protocol module using High Precision Event Timer (HPET)
3
4 Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7**/
8
9#include <PiDxe.h>
10
11#include <Protocol/Cpu.h>
12#include <Protocol/Timer.h>
13
14#include <Library/IoLib.h>
15#include <Library/PcdLib.h>
16#include <Library/BaseLib.h>
17#include <Library/DebugLib.h>
18#include <Library/UefiBootServicesTableLib.h>
19#include <Library/LocalApicLib.h>
20#include <Library/IoApicLib.h>
21
22#include <Register/LocalApic.h>
23#include <Register/IoApic.h>
24#include <Register/Hpet.h>
25
26///
27/// Define value for an invalid HPET Timer index.
28///
29#define HPET_INVALID_TIMER_INDEX 0xff
30
31///
32/// Timer Architectural Protocol function prototypes.
33///
34
35/**
36 This function registers the handler NotifyFunction so it is called every time
37 the timer interrupt fires. It also passes the amount of time since the last
38 handler call to the NotifyFunction. If NotifyFunction is NULL, then the
39 handler is unregistered. If the handler is registered, then EFI_SUCCESS is
40 returned. If the CPU does not support registering a timer interrupt handler,
41 then EFI_UNSUPPORTED is returned. If an attempt is made to register a handler
42 when a handler is already registered, then EFI_ALREADY_STARTED is returned.
43 If an attempt is made to unregister a handler when a handler is not registered,
44 then EFI_INVALID_PARAMETER is returned. If an error occurs attempting to
45 register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR
46 is returned.
47
48 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
49 @param NotifyFunction The function to call when a timer interrupt fires.
50 This function executes at TPL_HIGH_LEVEL. The DXE
51 Core will register a handler for the timer interrupt,
52 so it can know how much time has passed. This
53 information is used to signal timer based events.
54 NULL will unregister the handler.
55
56 @retval EFI_SUCCESS The timer handler was registered.
57 @retval EFI_UNSUPPORTED The platform does not support timer interrupts.
58 @retval EFI_ALREADY_STARTED NotifyFunction is not NULL, and a handler is already
59 registered.
60 @retval EFI_INVALID_PARAMETER NotifyFunction is NULL, and a handler was not
61 previously registered.
62 @retval EFI_DEVICE_ERROR The timer handler could not be registered.
63
64**/
65EFI_STATUS
66EFIAPI
67TimerDriverRegisterHandler (
68 IN EFI_TIMER_ARCH_PROTOCOL *This,
69 IN EFI_TIMER_NOTIFY NotifyFunction
70 );
71
72/**
73 This function adjusts the period of timer interrupts to the value specified
74 by TimerPeriod. If the timer period is updated, then the selected timer
75 period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If
76 the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
77 If an error occurs while attempting to update the timer period, then the
78 timer hardware will be put back in its state prior to this call, and
79 EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt
80 is disabled. This is not the same as disabling the CPU's interrupts.
81 Instead, it must either turn off the timer hardware, or it must adjust the
82 interrupt controller so that a CPU interrupt is not generated when the timer
83 interrupt fires.
84
85 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
86 @param TimerPeriod The rate to program the timer interrupt in 100 nS units.
87 If the timer hardware is not programmable, then
88 EFI_UNSUPPORTED is returned. If the timer is programmable,
89 then the timer period will be rounded up to the nearest
90 timer period that is supported by the timer hardware.
91 If TimerPeriod is set to 0, then the timer interrupts
92 will be disabled.
93
94 @retval EFI_SUCCESS The timer period was changed.
95 @retval EFI_UNSUPPORTED The platform cannot change the period of the timer interrupt.
96 @retval EFI_DEVICE_ERROR The timer period could not be changed due to a device error.
97
98**/
99EFI_STATUS
100EFIAPI
101TimerDriverSetTimerPeriod (
102 IN EFI_TIMER_ARCH_PROTOCOL *This,
103 IN UINT64 TimerPeriod
104 );
105
106/**
107 This function retrieves the period of timer interrupts in 100 ns units,
108 returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod
109 is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is
110 returned, then the timer is currently disabled.
111
112 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
113 @param TimerPeriod A pointer to the timer period to retrieve in 100 ns units.
114 If 0 is returned, then the timer is currently disabled.
115
116 @retval EFI_SUCCESS The timer period was returned in TimerPeriod.
117 @retval EFI_INVALID_PARAMETER TimerPeriod is NULL.
118
119**/
120EFI_STATUS
121EFIAPI
122TimerDriverGetTimerPeriod (
123 IN EFI_TIMER_ARCH_PROTOCOL *This,
124 OUT UINT64 *TimerPeriod
125 );
126
127/**
128 This function generates a soft timer interrupt. If the platform does not support soft
129 timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.
130 If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()
131 service, then a soft timer interrupt will be generated. If the timer interrupt is
132 enabled when this service is called, then the registered handler will be invoked. The
133 registered handler should not be able to distinguish a hardware-generated timer
134 interrupt from a software-generated timer interrupt.
135
136 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
137
138 @retval EFI_SUCCESS The soft timer interrupt was generated.
139 @retval EFI_UNSUPPORTED The platform does not support the generation of soft
140 timer interrupts.
141
142**/
143EFI_STATUS
144EFIAPI
145TimerDriverGenerateSoftInterrupt (
146 IN EFI_TIMER_ARCH_PROTOCOL *This
147 );
148
149///
150/// The handle onto which the Timer Architectural Protocol will be installed.
151///
152EFI_HANDLE mTimerHandle = NULL;
153
154///
155/// The Timer Architectural Protocol that this driver produces.
156///
157EFI_TIMER_ARCH_PROTOCOL mTimer = {
158 TimerDriverRegisterHandler,
159 TimerDriverSetTimerPeriod,
160 TimerDriverGetTimerPeriod,
161 TimerDriverGenerateSoftInterrupt
162};
163
164///
165/// Pointer to the CPU Architectural Protocol instance.
166///
167EFI_CPU_ARCH_PROTOCOL *mCpu = NULL;
168
169///
170/// The notification function to call on every timer interrupt.
171///
172EFI_TIMER_NOTIFY mTimerNotifyFunction = NULL;
173
174///
175/// The current period of the HPET timer interrupt in 100 ns units.
176///
177UINT64 mTimerPeriod = 0;
178
179///
180/// The number of HPET timer ticks required for the current HPET rate specified by mTimerPeriod.
181///
182UINT64 mTimerCount;
183
184///
185/// Mask used for counter and comparator calculations to adjust for a 32-bit or 64-bit counter.
186///
187UINT64 mCounterMask;
188
189///
190/// The HPET main counter value from the most recent HPET timer interrupt.
191///
192volatile UINT64 mPreviousMainCounter;
193
194volatile UINT64 mPreviousComparator;
195
196///
197/// The index of the HPET timer being managed by this driver.
198///
199UINTN mTimerIndex;
200
201///
202/// The I/O APIC IRQ that the HPET Timer is mapped if I/O APIC mode is used.
203///
204UINT32 mTimerIrq;
205
206///
207/// Cached state of the HPET General Capabilities register managed by this driver.
208/// Caching the state reduces the number of times the configuration register is read.
209///
210HPET_GENERAL_CAPABILITIES_ID_REGISTER mHpetGeneralCapabilities;
211
212///
213/// Cached state of the HPET General Configuration register managed by this driver.
214/// Caching the state reduces the number of times the configuration register is read.
215///
216HPET_GENERAL_CONFIGURATION_REGISTER mHpetGeneralConfiguration;
217
218///
219/// Cached state of the Configuration register for the HPET Timer managed by
220/// this driver. Caching the state reduces the number of times the configuration
221/// register is read.
222///
223HPET_TIMER_CONFIGURATION_REGISTER mTimerConfiguration;
224
225///
226/// Counts the number of HPET Timer interrupts processed by this driver.
227/// Only required for debug.
228///
229volatile UINTN mNumTicks;
230
231/**
232 Read a 64-bit register from the HPET
233
234 @param Offset Specifies the offset of the HPET register to read.
235
236 @return The 64-bit value read from the HPET register specified by Offset.
237**/
238UINT64
239HpetRead (
240 IN UINTN Offset
241 )
242{
243 return MmioRead64 (PcdGet32 (PcdHpetBaseAddress) + Offset);
244}
245
246/**
247 Write a 64-bit HPET register.
248
249 @param Offset Specifies the offset of the HPET register to write.
250 @param Value Specifies the value to write to the HPET register specified by Offset.
251
252 @return The 64-bit value written to HPET register specified by Offset.
253**/
254UINT64
255HpetWrite (
256 IN UINTN Offset,
257 IN UINT64 Value
258 )
259{
260 return MmioWrite64 (PcdGet32 (PcdHpetBaseAddress) + Offset, Value);
261}
262
263/**
264 Enable or disable the main counter in the HPET Timer.
265
266 @param Enable If TRUE, then enable the main counter in the HPET Timer.
267 If FALSE, then disable the main counter in the HPET Timer.
268**/
269VOID
270HpetEnable (
271 IN BOOLEAN Enable
272 )
273{
274 mHpetGeneralConfiguration.Bits.MainCounterEnable = Enable ? 1 : 0;
275 HpetWrite (HPET_GENERAL_CONFIGURATION_OFFSET, mHpetGeneralConfiguration.Uint64);
276}
277
278/**
279 The interrupt handler for the HPET timer. This handler clears the HPET interrupt
280 and computes the amount of time that has passed since the last HPET timer interrupt.
281 If a notification function is registered, then the amount of time since the last
282 HPET interrupt is passed to that notification function in 100 ns units. The HPET
283 time is updated to generate another interrupt in the required time period.
284
285 @param InterruptType The type of interrupt that occurred.
286 @param SystemContext A pointer to the system context when the interrupt occurred.
287**/
288VOID
289EFIAPI
290TimerInterruptHandler (
291 IN EFI_EXCEPTION_TYPE InterruptType,
292 IN EFI_SYSTEM_CONTEXT SystemContext
293 )
294{
295 UINT64 MainCounter;
296 UINT64 Comparator;
297 UINT64 TimerPeriod;
298 UINT64 Delta;
299
300 //
301 // Count number of ticks
302 //
303 DEBUG_CODE (
304 mNumTicks++;
305 );
306
307 //
308 // Clear HPET timer interrupt status
309 //
310 HpetWrite (HPET_GENERAL_INTERRUPT_STATUS_OFFSET, LShiftU64 (1, mTimerIndex));
311
312 //
313 // Local APIC EOI
314 //
315 SendApicEoi ();
316
317 //
318 // Disable HPET timer when adjusting the COMPARATOR value to prevent a missed interrupt
319 //
320 HpetEnable (FALSE);
321
322 //
323 // Capture main counter value
324 //
325 MainCounter = HpetRead (HPET_MAIN_COUNTER_OFFSET);
326
327 //
328 // Get the previous comparator counter
329 //
330 mPreviousComparator = HpetRead (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);
331
332 //
333 // Set HPET COMPARATOR to the value required for the next timer tick
334 //
335 Comparator = (mPreviousComparator + mTimerCount) & mCounterMask;
336
337 if ((mPreviousMainCounter < MainCounter) && (mPreviousComparator > Comparator)) {
338 //
339 // When comparator overflows
340 //
341 HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, Comparator);
342 } else if ((mPreviousMainCounter > MainCounter) && (mPreviousComparator < Comparator)) {
343 //
344 // When main counter overflows
345 //
346 HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, (MainCounter + mTimerCount) & mCounterMask);
347 } else {
348 //
349 // When both main counter and comparator do not overflow or both do overflow
350 //
351 if (Comparator > MainCounter) {
352 HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, Comparator);
353 } else {
354 HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, (MainCounter + mTimerCount) & mCounterMask);
355 }
356 }
357
358 //
359 // Enable the HPET counter once the new COMPARATOR value has been set.
360 //
361 HpetEnable (TRUE);
362
363 //
364 // Check to see if there is a registered notification function
365 //
366 if (mTimerNotifyFunction != NULL) {
367 //
368 // Compute time since last notification in 100 ns units (10 ^ -7)
369 //
370 if (MainCounter > mPreviousMainCounter) {
371 //
372 // Main counter does not overflow
373 //
374 Delta = MainCounter - mPreviousMainCounter;
375 } else {
376 //
377 // Main counter overflows, first usb, then add
378 //
379 Delta = (mCounterMask - mPreviousMainCounter) + MainCounter;
380 }
381
382 TimerPeriod = DivU64x32 (
383 MultU64x32 (
384 Delta & mCounterMask,
385 mHpetGeneralCapabilities.Bits.CounterClockPeriod
386 ),
387 100000000
388 );
389
390 //
391 // Save main counter value before calling notification function
392 // that may enable interrupts and allow interrupt nesting.
393 //
394 mPreviousMainCounter = MainCounter;
395
396 //
397 // Call registered notification function passing in the time since the last
398 // interrupt in 100 ns units.
399 //
400 mTimerNotifyFunction (TimerPeriod);
401 return;
402 }
403
404 //
405 // Save main counter value
406 //
407 mPreviousMainCounter = MainCounter;
408}
409
410/**
411 This function registers the handler NotifyFunction so it is called every time
412 the timer interrupt fires. It also passes the amount of time since the last
413 handler call to the NotifyFunction. If NotifyFunction is NULL, then the
414 handler is unregistered. If the handler is registered, then EFI_SUCCESS is
415 returned. If the CPU does not support registering a timer interrupt handler,
416 then EFI_UNSUPPORTED is returned. If an attempt is made to register a handler
417 when a handler is already registered, then EFI_ALREADY_STARTED is returned.
418 If an attempt is made to unregister a handler when a handler is not registered,
419 then EFI_INVALID_PARAMETER is returned. If an error occurs attempting to
420 register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR
421 is returned.
422
423 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
424 @param NotifyFunction The function to call when a timer interrupt fires.
425 This function executes at TPL_HIGH_LEVEL. The DXE
426 Core will register a handler for the timer interrupt,
427 so it can know how much time has passed. This
428 information is used to signal timer based events.
429 NULL will unregister the handler.
430
431 @retval EFI_SUCCESS The timer handler was registered.
432 @retval EFI_UNSUPPORTED The platform does not support timer interrupts.
433 @retval EFI_ALREADY_STARTED NotifyFunction is not NULL, and a handler is already
434 registered.
435 @retval EFI_INVALID_PARAMETER NotifyFunction is NULL, and a handler was not
436 previously registered.
437 @retval EFI_DEVICE_ERROR The timer handler could not be registered.
438
439**/
440EFI_STATUS
441EFIAPI
442TimerDriverRegisterHandler (
443 IN EFI_TIMER_ARCH_PROTOCOL *This,
444 IN EFI_TIMER_NOTIFY NotifyFunction
445 )
446{
447 //
448 // Check for invalid parameters
449 //
450 if ((NotifyFunction == NULL) && (mTimerNotifyFunction == NULL)) {
451 return EFI_INVALID_PARAMETER;
452 }
453
454 if ((NotifyFunction != NULL) && (mTimerNotifyFunction != NULL)) {
455 return EFI_ALREADY_STARTED;
456 }
457
458 //
459 // Cache the registered notification function
460 //
461 mTimerNotifyFunction = NotifyFunction;
462
463 return EFI_SUCCESS;
464}
465
466/**
467 This function adjusts the period of timer interrupts to the value specified
468 by TimerPeriod. If the timer period is updated, then the selected timer
469 period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If
470 the timer hardware is not programmable, then EFI_UNSUPPORTED is returned.
471 If an error occurs while attempting to update the timer period, then the
472 timer hardware will be put back in its state prior to this call, and
473 EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt
474 is disabled. This is not the same as disabling the CPU's interrupts.
475 Instead, it must either turn off the timer hardware, or it must adjust the
476 interrupt controller so that a CPU interrupt is not generated when the timer
477 interrupt fires.
478
479 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
480 @param TimerPeriod The rate to program the timer interrupt in 100 nS units.
481 If the timer hardware is not programmable, then
482 EFI_UNSUPPORTED is returned. If the timer is programmable,
483 then the timer period will be rounded up to the nearest
484 timer period that is supported by the timer hardware.
485 If TimerPeriod is set to 0, then the timer interrupts
486 will be disabled.
487
488 @retval EFI_SUCCESS The timer period was changed.
489 @retval EFI_UNSUPPORTED The platform cannot change the period of the timer interrupt.
490 @retval EFI_DEVICE_ERROR The timer period could not be changed due to a device error.
491
492**/
493EFI_STATUS
494EFIAPI
495TimerDriverSetTimerPeriod (
496 IN EFI_TIMER_ARCH_PROTOCOL *This,
497 IN UINT64 TimerPeriod
498 )
499{
500 EFI_TPL Tpl;
501 UINT64 MainCounter;
502 UINT64 Delta;
503 UINT64 CurrentComparator;
504 HPET_TIMER_MSI_ROUTE_REGISTER HpetTimerMsiRoute;
505
506 //
507 // Disable interrupts
508 //
509 Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
510
511 //
512 // Disable HPET timer when adjusting the timer period
513 //
514 HpetEnable (FALSE);
515
516 if (TimerPeriod == 0) {
517 if (mTimerPeriod != 0) {
518 //
519 // Check if there is possibly a pending interrupt
520 //
521 MainCounter = HpetRead (HPET_MAIN_COUNTER_OFFSET);
522 if (MainCounter < mPreviousMainCounter) {
523 Delta = (mCounterMask - mPreviousMainCounter) + MainCounter;
524 } else {
525 Delta = MainCounter - mPreviousMainCounter;
526 }
527
528 if ((Delta & mCounterMask) >= mTimerCount) {
529 //
530 // Interrupt still happens after disable HPET, wait to be processed
531 // Wait until interrupt is processed and comparator is increased
532 //
533 CurrentComparator = HpetRead (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);
534 while (CurrentComparator == mPreviousComparator) {
535 CurrentComparator = HpetRead (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);
536 CpuPause ();
537 }
538 }
539 }
540
541 //
542 // If TimerPeriod is 0, then mask HPET Timer interrupts
543 //
544
545 if ((mTimerConfiguration.Bits.MsiInterruptCapability != 0) && FeaturePcdGet (PcdHpetMsiEnable)) {
546 //
547 // Disable HPET MSI interrupt generation
548 //
549 mTimerConfiguration.Bits.MsiInterruptEnable = 0;
550 } else {
551 //
552 // Disable I/O APIC Interrupt
553 //
554 IoApicEnableInterrupt (mTimerIrq, FALSE);
555 }
556
557 //
558 // Disable HPET timer interrupt
559 //
560 mTimerConfiguration.Bits.InterruptEnable = 0;
561 HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);
562 } else {
563 //
564 // Convert TimerPeriod to femtoseconds and divide by the number if femtoseconds
565 // per tick of the HPET counter to determine the number of HPET counter ticks
566 // in TimerPeriod 100 ns units.
567 //
568 mTimerCount = DivU64x32 (
569 MultU64x32 (TimerPeriod, 100000000),
570 mHpetGeneralCapabilities.Bits.CounterClockPeriod
571 );
572
573 //
574 // Program the HPET Comparator with the number of ticks till the next interrupt
575 //
576 MainCounter = HpetRead (HPET_MAIN_COUNTER_OFFSET);
577 if (MainCounter > mPreviousMainCounter) {
578 Delta = MainCounter - mPreviousMainCounter;
579 } else {
580 Delta = (mCounterMask - mPreviousMainCounter) + MainCounter;
581 }
582
583 if ((Delta & mCounterMask) >= mTimerCount) {
584 HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, (MainCounter + 1) & mCounterMask);
585 } else {
586 HpetWrite (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, (mPreviousMainCounter + mTimerCount) & mCounterMask);
587 }
588
589 //
590 // Enable HPET Timer interrupt generation
591 //
592 if ((mTimerConfiguration.Bits.MsiInterruptCapability != 0) && FeaturePcdGet (PcdHpetMsiEnable)) {
593 //
594 // Program MSI Address and MSI Data values in the selected HPET Timer
595 // Program HPET register with APIC ID of current BSP in case BSP has been switched
596 //
597 HpetTimerMsiRoute.Bits.Address = GetApicMsiAddress ();
598 HpetTimerMsiRoute.Bits.Value = (UINT32)GetApicMsiValue (PcdGet8 (PcdHpetLocalApicVector), LOCAL_APIC_DELIVERY_MODE_LOWEST_PRIORITY, FALSE, FALSE);
599 HpetWrite (HPET_TIMER_MSI_ROUTE_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, HpetTimerMsiRoute.Uint64);
600 //
601 // Enable HPET MSI Interrupt
602 //
603 mTimerConfiguration.Bits.MsiInterruptEnable = 1;
604 } else {
605 //
606 // Enable timer interrupt through I/O APIC
607 // Program IOAPIC register with APIC ID of current BSP in case BSP has been switched
608 //
609 IoApicConfigureInterrupt (mTimerIrq, PcdGet8 (PcdHpetLocalApicVector), IO_APIC_DELIVERY_MODE_LOWEST_PRIORITY, TRUE, FALSE);
610 IoApicEnableInterrupt (mTimerIrq, TRUE);
611 }
612
613 //
614 // Enable HPET Interrupt Generation
615 //
616 mTimerConfiguration.Bits.InterruptEnable = 1;
617 HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);
618 }
619
620 //
621 // Save the new timer period
622 //
623 mTimerPeriod = TimerPeriod;
624
625 //
626 // Enable the HPET counter once new timer period has been established
627 // The HPET counter should run even if the HPET Timer interrupts are
628 // disabled. This is used to account for time passed while the interrupt
629 // is disabled.
630 //
631 HpetEnable (TRUE);
632
633 //
634 // Restore interrupts
635 //
636 gBS->RestoreTPL (Tpl);
637
638 return EFI_SUCCESS;
639}
640
641/**
642 This function retrieves the period of timer interrupts in 100 ns units,
643 returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod
644 is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is
645 returned, then the timer is currently disabled.
646
647 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
648 @param TimerPeriod A pointer to the timer period to retrieve in 100 ns units.
649 If 0 is returned, then the timer is currently disabled.
650
651 @retval EFI_SUCCESS The timer period was returned in TimerPeriod.
652 @retval EFI_INVALID_PARAMETER TimerPeriod is NULL.
653
654**/
655EFI_STATUS
656EFIAPI
657TimerDriverGetTimerPeriod (
658 IN EFI_TIMER_ARCH_PROTOCOL *This,
659 OUT UINT64 *TimerPeriod
660 )
661{
662 if (TimerPeriod == NULL) {
663 return EFI_INVALID_PARAMETER;
664 }
665
666 *TimerPeriod = mTimerPeriod;
667
668 return EFI_SUCCESS;
669}
670
671/**
672 This function generates a soft timer interrupt. If the platform does not support soft
673 timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned.
674 If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler()
675 service, then a soft timer interrupt will be generated. If the timer interrupt is
676 enabled when this service is called, then the registered handler will be invoked. The
677 registered handler should not be able to distinguish a hardware-generated timer
678 interrupt from a software-generated timer interrupt.
679
680 @param This The EFI_TIMER_ARCH_PROTOCOL instance.
681
682 @retval EFI_SUCCESS The soft timer interrupt was generated.
683 @retval EFI_UNSUPPORTED The platform does not support the generation of soft
684 timer interrupts.
685
686**/
687EFI_STATUS
688EFIAPI
689TimerDriverGenerateSoftInterrupt (
690 IN EFI_TIMER_ARCH_PROTOCOL *This
691 )
692{
693 UINT64 MainCounter;
694 EFI_TPL Tpl;
695 UINT64 TimerPeriod;
696 UINT64 Delta;
697
698 //
699 // Disable interrupts
700 //
701 Tpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
702
703 //
704 // Capture main counter value
705 //
706 MainCounter = HpetRead (HPET_MAIN_COUNTER_OFFSET);
707
708 //
709 // Check to see if there is a registered notification function
710 //
711 if (mTimerNotifyFunction != NULL) {
712 //
713 // Compute time since last interrupt in 100 ns units (10 ^ -7)
714 //
715 if (MainCounter > mPreviousMainCounter) {
716 //
717 // Main counter does not overflow
718 //
719 Delta = MainCounter - mPreviousMainCounter;
720 } else {
721 //
722 // Main counter overflows, first usb, then add
723 //
724 Delta = (mCounterMask - mPreviousMainCounter) + MainCounter;
725 }
726
727 TimerPeriod = DivU64x32 (
728 MultU64x32 (
729 Delta & mCounterMask,
730 mHpetGeneralCapabilities.Bits.CounterClockPeriod
731 ),
732 100000000
733 );
734
735 //
736 // Call registered notification function passing in the time since the last
737 // interrupt in 100 ns units.
738 //
739 mTimerNotifyFunction (TimerPeriod);
740 }
741
742 //
743 // Save main counter value
744 //
745 mPreviousMainCounter = MainCounter;
746
747 //
748 // Restore interrupts
749 //
750 gBS->RestoreTPL (Tpl);
751
752 return EFI_SUCCESS;
753}
754
755/**
756 Initialize the Timer Architectural Protocol driver
757
758 @param ImageHandle ImageHandle of the loaded driver
759 @param SystemTable Pointer to the System Table
760
761 @retval EFI_SUCCESS Timer Architectural Protocol created
762 @retval EFI_OUT_OF_RESOURCES Not enough resources available to initialize driver.
763 @retval EFI_DEVICE_ERROR A device error occurred attempting to initialize the driver.
764
765**/
766EFI_STATUS
767EFIAPI
768TimerDriverInitialize (
769 IN EFI_HANDLE ImageHandle,
770 IN EFI_SYSTEM_TABLE *SystemTable
771 )
772{
773 EFI_STATUS Status;
774 UINTN TimerIndex;
775 UINTN MsiTimerIndex;
776 HPET_TIMER_MSI_ROUTE_REGISTER HpetTimerMsiRoute;
777
778 DEBUG ((DEBUG_INFO, "Init HPET Timer Driver\n"));
779
780 //
781 // Make sure the Timer Architectural Protocol is not already installed in the system
782 //
783 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiTimerArchProtocolGuid);
784
785 //
786 // Find the CPU architectural protocol.
787 //
788 Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu);
789 ASSERT_EFI_ERROR (Status);
790
791 //
792 // Retrieve HPET Capabilities and Configuration Information
793 //
794 mHpetGeneralCapabilities.Uint64 = HpetRead (HPET_GENERAL_CAPABILITIES_ID_OFFSET);
795 mHpetGeneralConfiguration.Uint64 = HpetRead (HPET_GENERAL_CONFIGURATION_OFFSET);
796
797 //
798 // If Revision is not valid, then ASSERT() and unload the driver because the HPET
799 // device is not present.
800 //
801 ASSERT (mHpetGeneralCapabilities.Uint64 != 0);
802 ASSERT (mHpetGeneralCapabilities.Uint64 != 0xFFFFFFFFFFFFFFFFULL);
803 if ((mHpetGeneralCapabilities.Uint64 == 0) || (mHpetGeneralCapabilities.Uint64 == 0xFFFFFFFFFFFFFFFFULL)) {
804 DEBUG ((DEBUG_ERROR, "HPET device is not present. Unload HPET driver.\n"));
805 return EFI_DEVICE_ERROR;
806 }
807
808 //
809 // Force the HPET timer to be disabled while setting everything up
810 //
811 HpetEnable (FALSE);
812
813 //
814 // Dump HPET Configuration Information
815 //
816 DEBUG_CODE_BEGIN ();
817 DEBUG ((DEBUG_INFO, "HPET Base Address = 0x%08x\n", PcdGet32 (PcdHpetBaseAddress)));
818 DEBUG ((DEBUG_INFO, " HPET_GENERAL_CAPABILITIES_ID = 0x%016lx\n", mHpetGeneralCapabilities));
819 DEBUG ((DEBUG_INFO, " HPET_GENERAL_CONFIGURATION = 0x%016lx\n", mHpetGeneralConfiguration.Uint64));
820 DEBUG ((DEBUG_INFO, " HPET_GENERAL_INTERRUPT_STATUS = 0x%016lx\n", HpetRead (HPET_GENERAL_INTERRUPT_STATUS_OFFSET)));
821 DEBUG ((DEBUG_INFO, " HPET_MAIN_COUNTER = 0x%016lx\n", HpetRead (HPET_MAIN_COUNTER_OFFSET)));
822 DEBUG ((DEBUG_INFO, " HPET Main Counter Period = %d (fs)\n", mHpetGeneralCapabilities.Bits.CounterClockPeriod));
823 for (TimerIndex = 0; TimerIndex <= mHpetGeneralCapabilities.Bits.NumberOfTimers; TimerIndex++) {
824 DEBUG ((DEBUG_INFO, " HPET_TIMER%d_CONFIGURATION = 0x%016lx\n", TimerIndex, HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + TimerIndex * HPET_TIMER_STRIDE)));
825 DEBUG ((DEBUG_INFO, " HPET_TIMER%d_COMPARATOR = 0x%016lx\n", TimerIndex, HpetRead (HPET_TIMER_COMPARATOR_OFFSET + TimerIndex * HPET_TIMER_STRIDE)));
826 DEBUG ((DEBUG_INFO, " HPET_TIMER%d_MSI_ROUTE = 0x%016lx\n", TimerIndex, HpetRead (HPET_TIMER_MSI_ROUTE_OFFSET + TimerIndex * HPET_TIMER_STRIDE)));
827 }
828
829 DEBUG_CODE_END ();
830
831 //
832 // Capture the current HPET main counter value.
833 //
834 mPreviousMainCounter = HpetRead (HPET_MAIN_COUNTER_OFFSET);
835
836 //
837 // Determine the interrupt mode to use for the HPET Timer.
838 // Look for MSI first, then unused PIC mode interrupt, then I/O APIC mode interrupt
839 //
840 MsiTimerIndex = HPET_INVALID_TIMER_INDEX;
841 mTimerIndex = HPET_INVALID_TIMER_INDEX;
842 for (TimerIndex = 0; TimerIndex <= mHpetGeneralCapabilities.Bits.NumberOfTimers; TimerIndex++) {
843 //
844 // Read the HPET Timer Capabilities and Configuration register
845 //
846 mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + TimerIndex * HPET_TIMER_STRIDE);
847
848 //
849 // Check to see if this HPET Timer supports MSI
850 //
851 if (mTimerConfiguration.Bits.MsiInterruptCapability != 0) {
852 //
853 // Save the index of the first HPET Timer that supports MSI interrupts
854 //
855 if (MsiTimerIndex == HPET_INVALID_TIMER_INDEX) {
856 MsiTimerIndex = TimerIndex;
857 }
858 }
859
860 //
861 // Check to see if this HPET Timer supports I/O APIC interrupts
862 //
863 if (mTimerConfiguration.Bits.InterruptRouteCapability != 0) {
864 //
865 // Save the index of the first HPET Timer that supports I/O APIC interrupts
866 //
867 if (mTimerIndex == HPET_INVALID_TIMER_INDEX) {
868 mTimerIndex = TimerIndex;
869 mTimerIrq = (UINT32)LowBitSet32 (mTimerConfiguration.Bits.InterruptRouteCapability);
870 }
871 }
872 }
873
874 if (FeaturePcdGet (PcdHpetMsiEnable) && (MsiTimerIndex != HPET_INVALID_TIMER_INDEX)) {
875 //
876 // Use MSI interrupt if supported
877 //
878 mTimerIndex = MsiTimerIndex;
879
880 //
881 // Program MSI Address and MSI Data values in the selected HPET Timer
882 //
883 HpetTimerMsiRoute.Bits.Address = GetApicMsiAddress ();
884 HpetTimerMsiRoute.Bits.Value = (UINT32)GetApicMsiValue (PcdGet8 (PcdHpetLocalApicVector), LOCAL_APIC_DELIVERY_MODE_LOWEST_PRIORITY, FALSE, FALSE);
885 HpetWrite (HPET_TIMER_MSI_ROUTE_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, HpetTimerMsiRoute.Uint64);
886
887 //
888 // Read the HPET Timer Capabilities and Configuration register and initialize for MSI mode
889 // Clear LevelTriggeredInterrupt to use edge triggered interrupts when in MSI mode
890 //
891 mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);
892 mTimerConfiguration.Bits.LevelTriggeredInterrupt = 0;
893 } else {
894 //
895 // If no HPET timers support MSI or I/O APIC modes, then ASSERT() and unload the driver.
896 //
897 ASSERT (mTimerIndex != HPET_INVALID_TIMER_INDEX);
898 if (mTimerIndex == HPET_INVALID_TIMER_INDEX) {
899 DEBUG ((DEBUG_ERROR, "No HPET timers support MSI or I/O APIC mode. Unload HPET driver.\n"));
900 return EFI_DEVICE_ERROR;
901 }
902
903 //
904 // Initialize I/O APIC entry for HPET Timer Interrupt
905 // Fixed Delivery Mode, Level Triggered, Asserted Low
906 //
907 IoApicConfigureInterrupt (mTimerIrq, PcdGet8 (PcdHpetLocalApicVector), IO_APIC_DELIVERY_MODE_LOWEST_PRIORITY, TRUE, FALSE);
908
909 //
910 // Read the HPET Timer Capabilities and Configuration register and initialize for I/O APIC mode
911 // Clear MsiInterruptCapability to force rest of driver to use I/O APIC mode
912 // Set LevelTriggeredInterrupt to use level triggered interrupts when in I/O APIC mode
913 // Set InterruptRoute field based in mTimerIrq
914 //
915 mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);
916 mTimerConfiguration.Bits.LevelTriggeredInterrupt = 1;
917 mTimerConfiguration.Bits.InterruptRoute = mTimerIrq;
918 }
919
920 //
921 // Configure the selected HPET Timer with settings common to both MSI mode and I/O APIC mode
922 // Clear InterruptEnable to keep interrupts disabled until full init is complete
923 // Clear PeriodicInterruptEnable to use one-shot mode
924 // Configure as a 32-bit counter
925 //
926 mTimerConfiguration.Bits.InterruptEnable = 0;
927 mTimerConfiguration.Bits.PeriodicInterruptEnable = 0;
928 mTimerConfiguration.Bits.CounterSizeEnable = 1;
929 HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);
930
931 //
932 // Read the HPET Timer Capabilities and Configuration register back again.
933 // CounterSizeEnable will be read back as a 0 if it is a 32-bit only timer
934 //
935 mTimerConfiguration.Uint64 = HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE);
936 if ((mTimerConfiguration.Bits.CounterSizeEnable == 1) && (sizeof (UINTN) == sizeof (UINT64))) {
937 DEBUG ((DEBUG_INFO, "Choose 64-bit HPET timer.\n"));
938 //
939 // 64-bit BIOS can use 64-bit HPET timer
940 //
941 mCounterMask = 0xffffffffffffffffULL;
942 //
943 // Set timer back to 64-bit
944 //
945 mTimerConfiguration.Bits.CounterSizeEnable = 0;
946 HpetWrite (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE, mTimerConfiguration.Uint64);
947 } else {
948 DEBUG ((DEBUG_INFO, "Choose 32-bit HPET timer.\n"));
949 mCounterMask = 0x00000000ffffffffULL;
950 }
951
952 //
953 // Install interrupt handler for selected HPET Timer
954 //
955 Status = mCpu->RegisterInterruptHandler (mCpu, PcdGet8 (PcdHpetLocalApicVector), TimerInterruptHandler);
956 ASSERT_EFI_ERROR (Status);
957 if (EFI_ERROR (Status)) {
958 DEBUG ((DEBUG_ERROR, "Unable to register HPET interrupt with CPU Arch Protocol. Unload HPET driver.\n"));
959 return EFI_DEVICE_ERROR;
960 }
961
962 //
963 // Force the HPET Timer to be enabled at its default period
964 //
965 Status = TimerDriverSetTimerPeriod (&mTimer, PcdGet64 (PcdHpetDefaultTimerPeriod));
966 ASSERT_EFI_ERROR (Status);
967 if (EFI_ERROR (Status)) {
968 DEBUG ((DEBUG_ERROR, "Unable to set HPET default timer rate. Unload HPET driver.\n"));
969 return EFI_DEVICE_ERROR;
970 }
971
972 //
973 // Show state of enabled HPET timer
974 //
975 DEBUG_CODE_BEGIN ();
976 if ((mTimerConfiguration.Bits.MsiInterruptCapability != 0) && FeaturePcdGet (PcdHpetMsiEnable)) {
977 DEBUG ((DEBUG_INFO, "HPET Interrupt Mode MSI\n"));
978 } else {
979 DEBUG ((DEBUG_INFO, "HPET Interrupt Mode I/O APIC\n"));
980 DEBUG ((DEBUG_INFO, "HPET I/O APIC IRQ = 0x%02x\n", mTimerIrq));
981 }
982
983 DEBUG ((DEBUG_INFO, "HPET Interrupt Vector = 0x%02x\n", PcdGet8 (PcdHpetLocalApicVector)));
984 DEBUG ((DEBUG_INFO, "HPET Counter Mask = 0x%016lx\n", mCounterMask));
985 DEBUG ((DEBUG_INFO, "HPET Timer Period = %d\n", mTimerPeriod));
986 DEBUG ((DEBUG_INFO, "HPET Timer Count = 0x%016lx\n", mTimerCount));
987 DEBUG ((DEBUG_INFO, "HPET_TIMER%d_CONFIGURATION = 0x%016lx\n", mTimerIndex, HpetRead (HPET_TIMER_CONFIGURATION_OFFSET + mTimerIndex * HPET_TIMER_STRIDE)));
988 DEBUG ((DEBUG_INFO, "HPET_TIMER%d_COMPARATOR = 0x%016lx\n", mTimerIndex, HpetRead (HPET_TIMER_COMPARATOR_OFFSET + mTimerIndex * HPET_TIMER_STRIDE)));
989 DEBUG ((DEBUG_INFO, "HPET_TIMER%d_MSI_ROUTE = 0x%016lx\n", mTimerIndex, HpetRead (HPET_TIMER_MSI_ROUTE_OFFSET + mTimerIndex * HPET_TIMER_STRIDE)));
990
991 //
992 // Wait for a few timer interrupts to fire before continuing
993 //
994 while (mNumTicks < 10) {
995 }
996
997 DEBUG_CODE_END ();
998
999 //
1000 // Install the Timer Architectural Protocol onto a new handle
1001 //
1002 Status = gBS->InstallMultipleProtocolInterfaces (
1003 &mTimerHandle,
1004 &gEfiTimerArchProtocolGuid,
1005 &mTimer,
1006 NULL
1007 );
1008 ASSERT_EFI_ERROR (Status);
1009
1010 return Status;
1011}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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