Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

476 lines
9.7 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. ixclock.c
  5. Abstract:
  6. This module implements the code necessary to field and process the
  7. interval clock interrupts.
  8. Author:
  9. Shie-Lin Tzong (shielint) 12-Jan-1990
  10. Environment:
  11. Kernel mode only.
  12. Revision History:
  13. bryanwi 20-Sep-90
  14. Add KiSetProfileInterval, KiStartProfileInterrupt,
  15. KiStopProfileInterrupt procedures.
  16. KiProfileInterrupt ISR.
  17. KiProfileList, KiProfileLock are delcared here.
  18. shielint 10-Dec-90
  19. Add performance counter support.
  20. Move system clock to irq8, ie we now use RTC to generate system
  21. clock. Performance count and Profile use timer 1 counter 0.
  22. The interval of the irq0 interrupt can be changed by
  23. KiSetProfileInterval. Performance counter does not care about the
  24. interval of the interrupt as long as it knows the rollover count.
  25. Note: Currently I implemented 1 performance counter for the whole
  26. i386 NT.
  27. John Vert (jvert) 11-Jul-1991
  28. Moved from ke\i386 to hal\i386. Removed non-HAL stuff
  29. shie-lin tzong (shielint) 13-March-92
  30. Move System clock back to irq0 and use RTC (irq8) to generate
  31. profile interrupt. Performance counter and system clock use time1
  32. counter 0 of 8254.
  33. Landy Wang (corollary!landy) 04-Dec-92
  34. Move much code into separate modules for easy inclusion by various
  35. HAL builds.
  36. Forrest Foltz (forrestf) 24-Oct-2000
  37. Ported from ixclock.asm to ixclock.c
  38. --*/
  39. #include "halcmn.h"
  40. #define COUNTER_TICKS_AVG_SHIFT 4
  41. #define COUNTER_TICKS_FOR_AVG 16
  42. #define FRAME_COPY_SIZE 64
  43. //
  44. // Convert the interval to rollover count for 8254 Timer1 device.
  45. // Timer1 counts down a 16 bit value at a rate of 1.193181667M counts-per-sec.
  46. // (The main crystal freq is 14.31818, and this is a divide by 12)
  47. //
  48. // The best fit value closest to 10ms is 10.0144012689ms:
  49. // ROLLOVER_COUNT 11949
  50. // TIME_INCREMENT 100144
  51. // Calculated error is -.0109472 s/day
  52. //
  53. //
  54. // The following table contains 8254 values timer values to use at
  55. // any given ms setting from 1ms - 15ms. All values work out to the
  56. // same error per day (-.0109472 s/day).
  57. //
  58. typedef struct _TIMER_TABLE_ENTRY {
  59. USHORT RolloverCount;
  60. ULONG TimeIncrement;
  61. } TIMER_TABLE_ENTRY, *PTIMER_TABLE_ENTRY;
  62. TIMER_TABLE_ENTRY HalpTimerTable[] = {
  63. { 0, 0 }, // dummy entry to zero-base array
  64. { 1197, 10032 }, // 1ms
  65. { 2394, 20064 }, // 2ms
  66. { 3591, 30096 }, // 3ms
  67. { 4767, 39952 }, // 4ms
  68. { 5964, 49984 }, // 5ms
  69. { 7161, 60016 }, // 6ms
  70. { 8358, 70048 }, // 7ms
  71. { 9555, 80080 }, // 8ms
  72. { 10731, 89936 }, // 9ms
  73. { 11949, 100144 } // 10ms
  74. };
  75. #define MIN_TIMER_INCREMENT 1
  76. #define MAX_TIMER_INCREMENT \
  77. (sizeof(HalpTimerTable) / sizeof(TIMER_TABLE_ENTRY) - 1)
  78. #define HalpMinimumTimerTableEntry (&HalpTimerTable[MIN_TIMER_INCREMENT])
  79. #define HalpMaximumTimerTableEntry (&HalpTimerTable[MAX_TIMER_INCREMENT])
  80. //
  81. // External function prototypes
  82. //
  83. VOID
  84. HalpMcaQueueDpc(
  85. VOID
  86. );
  87. //
  88. // External declarations
  89. //
  90. extern ULONG HalpTimerWatchdogEnabled;
  91. //
  92. // Globals
  93. //
  94. ULONG64 HalpWatchdogCount;
  95. ULONG64 HalpWatchdogTsc;
  96. ULONG64 HalpTimeBias;
  97. BOOLEAN HalpClockMcaQueueDpc;
  98. PTIMER_TABLE_ENTRY HalpNextMSRate = NULL;
  99. ULONG HalpCurrentMSRateTableIndex;
  100. #define HalpCurrentMSRate (&HalpTimerTable[HalpCurrentMSRateTableIndex+1])
  101. //
  102. // Forward function declarations
  103. //
  104. VOID
  105. HalpSetMSRate(
  106. IN PTIMER_TABLE_ENTRY TableEntry
  107. );
  108. //
  109. // Inline functions
  110. //
  111. __forceinline
  112. VOID
  113. HalpCalibrateWatchdog(
  114. VOID
  115. )
  116. {
  117. if (HalpTimerWatchdogEnabled == 0) {
  118. return;
  119. }
  120. //
  121. // Initializethe timer latency watchdog
  122. //
  123. HalpWatchdogTsc = ReadTimeStampCounter();
  124. HalpWatchdogCount = 0;
  125. }
  126. __forceinline
  127. VOID
  128. HalpCheckWatchdog(
  129. VOID
  130. )
  131. {
  132. if (HalpTimerWatchdogEnabled == 0) {
  133. return;
  134. }
  135. AMD64_IMPLEMENT;
  136. }
  137. //
  138. // Module functions
  139. //
  140. VOID
  141. HalpInitializeClock (
  142. VOID
  143. )
  144. /*++
  145. Routine Description:
  146. This routine initialize system time clock using 8254 timer1 counter 0
  147. to generate an interrupt at every 15ms interval at 8259 irq0.
  148. See the definitions of TIME_INCREMENT and ROLLOVER_COUNT if clock rate
  149. needs to be changed.
  150. Arguments:
  151. None
  152. Return Value:
  153. None
  154. --*/
  155. {
  156. ULONG maxTimeIncrement;
  157. ULONG minTimeIncrement;
  158. //
  159. // Indicate to the kernel the minimum and maximum tick increments
  160. //
  161. minTimeIncrement = HalpMinimumTimerTableEntry->TimeIncrement;
  162. maxTimeIncrement = HalpMaximumTimerTableEntry->TimeIncrement;
  163. KeSetTimeIncrement(minTimeIncrement,maxTimeIncrement);
  164. //
  165. // Set the initial clock rate to the slowest permissible
  166. //
  167. HalpSetMSRate(HalpMaximumTimerTableEntry);
  168. }
  169. VOID
  170. HalpAcpiTimerCalibratePerfCount (
  171. IN PLONG volatile Number,
  172. IN ULONG64 NewCount
  173. )
  174. /*++
  175. Routine Description:
  176. This routine resets the performance counter value for the current
  177. processor to zero. The reset is done such that the resulting value
  178. is closely synchronized with other processors in the configuration.
  179. Arguments:
  180. Number - Supplies a pointer to count of the number of processors in
  181. the configuration.
  182. NewCount - Supplies the value to synchronize the counter too
  183. Return Value:
  184. None.
  185. --*/
  186. {
  187. PKPCR pcr;
  188. ULONG64 perfCount;
  189. pcr = KeGetPcr();
  190. if (pcr->Number == 0) {
  191. //
  192. // Only execute on the boot processor.
  193. //
  194. perfCount = QueryTimer().QuadPart;
  195. //
  196. // Compute how far the current count is from the target count,
  197. // and adjust TimerInfo.Bias accordingly.
  198. //
  199. HalpTimeBias = NewCount - perfCount;
  200. }
  201. //
  202. // Wait for all processors to reach this point
  203. //
  204. InterlockedDecrement(Number);
  205. while (*Number != 0) {
  206. PAUSE_PROCESSOR
  207. }
  208. }
  209. VOID
  210. HalpSetMSRate(
  211. IN PTIMER_TABLE_ENTRY TableEntry
  212. )
  213. /*++
  214. Routine Description
  215. This routine programs the 8254 with a new rollover count.
  216. Artuments
  217. TableEntry - Supplies a pointer to an entry within HalpTimerTable.
  218. Return value:
  219. None.
  220. --*/
  221. {
  222. USHORT rollover;
  223. ULONG interruptsEnabled;
  224. //
  225. // Program the 8254 to generate the new timer interrupt rate.
  226. //
  227. rollover = TableEntry->RolloverCount;
  228. interruptsEnabled = HalpDisableInterrupts();
  229. WRITE_PORT_UCHAR(TIMER1_CONTROL_PORT,
  230. TIMER_COMMAND_COUNTER0 +
  231. TIMER_COMMAND_RW_16BIT +
  232. TIMER_COMMAND_MODE2);
  233. IO_DELAY();
  234. WRITE_PORT_USHORT_PAIR (TIMER1_DATA_PORT0,
  235. TIMER1_DATA_PORT0,
  236. rollover);
  237. IO_DELAY();
  238. HalpRestoreInterrupts(interruptsEnabled);
  239. //
  240. // Recalibrate the timer watchdog
  241. //
  242. HalpCalibrateWatchdog();
  243. //
  244. // Update the global representing the timer rate
  245. //
  246. HalpCurrentMSRateTableIndex = (ULONG)(TableEntry - HalpTimerTable) - 1;
  247. }
  248. BOOLEAN
  249. HalpClockInterrupt (
  250. IN PKINTERRUPT Interrupt,
  251. IN PVOID ServiceContext
  252. )
  253. /*++
  254. Routine Description
  255. This routine is entered as the result of an interrupt generated by CLOCK.
  256. Its function is to dismiss the interrupt, raise system Irql to
  257. CLOCK2_LEVEL, update performance counter and transfer control to the
  258. standard system routine to update the system time and the execution
  259. time of the current thread
  260. and process.
  261. Arguments:
  262. Interrupt - Supplies a pointer to the interrupt object
  263. ServiceContext - Not used
  264. Return value:
  265. --*/
  266. {
  267. ULONG timeIncrement;
  268. //
  269. // Capture the time increment for the now-occuring tick. This is
  270. // done here because HalpCurrentMSRate will change if a rate change
  271. // is pending.
  272. //
  273. timeIncrement = HalpCurrentMSRate->TimeIncrement;
  274. //
  275. // Give the watchdog timer a chance to do its work
  276. //
  277. HalpCheckWatchdog();
  278. //
  279. // Check whether an MCA dpc should be queued
  280. //
  281. if (HalpClockMcaQueueDpc != FALSE) {
  282. HalpClockMcaQueueDpc = FALSE;
  283. HalpMcaQueueDpc();
  284. }
  285. //
  286. // Check whether the clock interrupt frequency should be changed.
  287. //
  288. if (HalpNextMSRate != NULL) {
  289. HalpNextMSRate = NULL;
  290. HalpSetMSRate(HalpNextMSRate);
  291. }
  292. //
  293. // Indicate to the kernel that a clock tick has occured.
  294. //
  295. KeUpdateSystemTime(Interrupt->TrapFrame,timeIncrement);
  296. return TRUE;
  297. }
  298. ULONG
  299. HalpAcpiTimerSetTimeIncrement (
  300. IN ULONG DesiredIncrement
  301. )
  302. /*++
  303. Routine Description:
  304. This routine initialize system time clock to generate an
  305. interrupt at every DesiredIncrement interval.
  306. Arguments:
  307. DesiredIncrement - desired interval between every timer tick (in
  308. 100ns unit.)
  309. Return Value:
  310. The *REAL* time increment set.
  311. --*/
  312. {
  313. ULONG incMs;
  314. PTIMER_TABLE_ENTRY tableEntry;
  315. //
  316. // Convert the desired time inecrement to milliseconds
  317. //
  318. incMs = DesiredIncrement / 10000;
  319. //
  320. // Place the value within the range supported by this hal
  321. //
  322. if (incMs > MAX_TIMER_INCREMENT) {
  323. incMs = MAX_TIMER_INCREMENT;
  324. } else if (incMs < MIN_TIMER_INCREMENT) {
  325. incMs = MIN_TIMER_INCREMENT;
  326. }
  327. //
  328. // If the new rate is different than the current rate, indicate via
  329. // a non-null HalpNextMSRate that a new timer rate is expected. This
  330. // will be done at the next timer ISR.
  331. //
  332. tableEntry = &HalpTimerTable[incMs];
  333. if (tableEntry != HalpCurrentMSRate) {
  334. HalpNextMSRate = tableEntry;
  335. }
  336. //
  337. // Finally, return the timer increment that will actually be used.
  338. //
  339. return tableEntry->TimeIncrement;
  340. }