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