HpetTimer.c 36 KB

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