Leaked source code of windows server 2003
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.

433 lines
8.2 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. pmtimer.c
  5. Abstract:
  6. This module implements the code for ACPI-related timer
  7. functions.
  8. Author:
  9. Jake Oshins (jakeo) March 28, 1997
  10. Environment:
  11. Kernel mode only.
  12. Revision History:
  13. Split from pmclock.asm due to PIIX4 bugs.
  14. Forrest Foltz (forrestf) 23-Oct-2000
  15. Ported from pmtimer.asm to pmtimer.c
  16. --*/
  17. #include "halcmn.h"
  18. #define TSC 0x10
  19. #define PM_TMR_FREQ 3579545
  20. #define TIMER_ROUNDING 10000
  21. #define __1MHz 1000000
  22. //
  23. // HalpCurrentTime is the value of the hardware timer. It is updated at
  24. // every timer tick.
  25. //
  26. volatile ULONG64 HalpCurrentTime;
  27. volatile ULONG HalpGlobalVolatile;
  28. //
  29. // HalpHardwareTimeRollover represents the maximum count + 1 of the
  30. // hardware timer.
  31. //
  32. // The hardware is either 24- or 32-bits. HalpHardwareTimeRollover will
  33. // therefore have a vale of either 0x1000000 or 0x100000000.
  34. //
  35. // ACPI generates an interrupt whenever the MSb of the hardware timer
  36. // changes.
  37. //
  38. ULONG64 HalpHardwareTimeRollover;
  39. ULONG64 HalpTimeBias = 0;
  40. //
  41. // HalpCurrentTimePort is the port number of the 32-bit hardware timer.
  42. //
  43. ULONG HalpCurrentTimePort;
  44. FORCEINLINE
  45. ULONG
  46. HalpReadPmTimer (
  47. VOID
  48. )
  49. {
  50. ULONG value;
  51. ASSERT(HalpCurrentTimePort != 0);
  52. value = READ_PORT_ULONG(UlongToPtr(HalpCurrentTimePort));
  53. return value;
  54. }
  55. ULONG64
  56. HalpQueryPerformanceCounter (
  57. VOID
  58. )
  59. {
  60. ULONG64 currentTime;
  61. ULONG hardwareTime;
  62. ULONG lastHardwareTime;
  63. //
  64. // Get a local copy of HalpCurrentTime and the value of the hardware
  65. // timer, in that order.
  66. //
  67. currentTime = HalpCurrentTime;
  68. hardwareTime = HalpReadPmTimer();
  69. //
  70. // Extract the hardware portion of the currentTime.
  71. //
  72. lastHardwareTime = (ULONG)(currentTime & (HalpHardwareTimeRollover - 1));
  73. //
  74. // Replace the lastHardwareTime component of currentTime with the
  75. // current hardware time.
  76. //
  77. currentTime ^= lastHardwareTime;
  78. currentTime |= hardwareTime;
  79. //
  80. // Check and compensate for hardware timer rollover
  81. //
  82. if (lastHardwareTime > hardwareTime) {
  83. currentTime += HalpHardwareTimeRollover;
  84. }
  85. return currentTime;
  86. }
  87. LARGE_INTEGER
  88. KeQueryPerformanceCounter (
  89. OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
  90. )
  91. {
  92. LARGE_INTEGER value;
  93. if (ARGUMENT_PRESENT(PerformanceFrequency)) {
  94. PerformanceFrequency->QuadPart = PM_TMR_FREQ;
  95. }
  96. value.QuadPart = HalpQueryPerformanceCounter() + HalpTimeBias;
  97. return value;
  98. }
  99. VOID
  100. HalAcpiTimerCarry (
  101. VOID
  102. )
  103. /*++
  104. Routine Description:
  105. This routine is called to service the PM timer carry interrupt
  106. N.B. This function is called at interrupt time and assumes the
  107. caller clears the interrupt
  108. Arguments:
  109. None
  110. Return Value:
  111. None
  112. --*/
  113. {
  114. ULONG hardwareTime;
  115. ULONG64 currentTime;
  116. ULONG64 halfRollover;
  117. currentTime = HalpCurrentTime;
  118. hardwareTime = HalpReadPmTimer();
  119. //
  120. // ACPI generates an interrupt whenever the MSb of the hardware timer
  121. // changes. Each interrupt represents, therefore, half a rollover.
  122. //
  123. halfRollover = HalpHardwareTimeRollover / 2;
  124. currentTime += halfRollover;
  125. //
  126. // Make sure the MSb of the hardware matches the software MSb. Breaking
  127. // into the debugger might have gotten these out of sync.
  128. //
  129. currentTime += halfRollover & (currentTime ^ hardwareTime);
  130. //
  131. // Finally, store the new current time back into the global
  132. //
  133. HalpCurrentTime = currentTime;
  134. }
  135. VOID
  136. HalaAcpiTimerInit(
  137. IN ULONG TimerPort,
  138. IN BOOLEAN TimerValExt
  139. )
  140. {
  141. HalpCurrentTimePort = TimerPort;
  142. if (TimerValExt) {
  143. HalpHardwareTimeRollover = 0x100000000;
  144. } else {
  145. HalpHardwareTimeRollover = 0x1000000;
  146. }
  147. }
  148. VOID
  149. HalpPmTimerSpecialStall(
  150. IN ULONG Ticks
  151. )
  152. /*++
  153. Routine Description:
  154. Arguments:
  155. Ticks - Number of PM timer ticks to stall
  156. Return Value:
  157. TRUE if we were able to stall for the correct interval,
  158. otherwise FALSE
  159. --*/
  160. {
  161. ULONG64 currentCounter;
  162. ULONG64 lastCounter;
  163. ULONG64 baseTime;
  164. ULONG64 currentTime;
  165. ULONG64 endTime;
  166. ULONG64 newCurrentTime;
  167. ASSERT(HalpHardwareTimeRollover != 0);
  168. baseTime = 0;
  169. lastCounter = HalpReadPmTimer();
  170. endTime = lastCounter + Ticks;
  171. do {
  172. currentCounter = HalpReadPmTimer();
  173. if (currentCounter < lastCounter) {
  174. baseTime += HalpHardwareTimeRollover;
  175. }
  176. lastCounter = currentCounter;
  177. currentTime = baseTime + currentCounter;
  178. if (currentTime >= endTime) {
  179. break;
  180. }
  181. HalpWasteTime(200);
  182. } while (TRUE);
  183. }
  184. BOOLEAN
  185. HalpPmTimerScaleTimers(
  186. VOID
  187. )
  188. /*++
  189. Routine Description:
  190. Determines the frequency of the APIC timer, this routine is run
  191. during initialization
  192. Arguments:
  193. None
  194. Return Value:
  195. None
  196. --*/
  197. {
  198. ULONG eflags;
  199. PHALPCR HalPCR;
  200. PKPCR PCR;
  201. ULONG ApicHz;
  202. ULONGLONG TscHz;
  203. ULONG RoundApicHz;
  204. ULONGLONG RoundTscHz;
  205. ULONGLONG RoundTscMhz;
  206. ULONGLONG currentTime;
  207. ULONGLONG startTime;
  208. ULONGLONG endTime;
  209. //
  210. // Disable interrupts on this processor
  211. //
  212. eflags = HalpDisableInterrupts();
  213. PCR = KeGetPcr();
  214. HalPCR = HalpGetCurrentHalPcr();
  215. //
  216. // Configure APIC timer
  217. //
  218. LOCAL_APIC(LU_TIMER_VECTOR) = INTERRUPT_MASKED |
  219. PERIODIC_TIMER |
  220. APIC_PROFILE_VECTOR;
  221. LOCAL_APIC(LU_DIVIDER_CONFIG) = LU_DIVIDE_BY_1;
  222. //
  223. // Make sure the write has completed, zero the perf counter,
  224. // and insert a processor fence
  225. //
  226. HalPCR->PerfCounter = 0;
  227. LOCAL_APIC(LU_DIVIDER_CONFIG);
  228. PROCESSOR_FENCE;
  229. //
  230. // Reset APIC counter and TSC
  231. //
  232. LOCAL_APIC(LU_INITIAL_COUNT) = 0xFFFFFFFF;
  233. WRMSR(TSC, 0);
  234. //
  235. // Stall for an eighth of a second
  236. //
  237. HalpPmTimerSpecialStall(PM_TMR_FREQ / 8);
  238. TscHz = ReadTimeStampCounter() * 8;
  239. ApicHz = (0 - LOCAL_APIC(LU_CURRENT_COUNT)) * 8;
  240. //
  241. // Round APIC frequency
  242. //
  243. RoundApicHz = ((ApicHz + (TIMER_ROUNDING / 2)) / TIMER_ROUNDING) *
  244. TIMER_ROUNDING;
  245. HalPCR->ApicClockFreqHz = RoundApicHz;
  246. //
  247. // Round TSC frequency
  248. //
  249. RoundTscHz = ((TscHz + (TIMER_ROUNDING / 2)) / TIMER_ROUNDING) *
  250. TIMER_ROUNDING;
  251. HalPCR->TSCHz = RoundTscHz;
  252. //
  253. // Convert TSC frequency to MHz
  254. //
  255. RoundTscMhz = (RoundTscHz + (__1MHz / 2)) / __1MHz;
  256. PCR->StallScaleFactor = (ULONG)RoundTscMhz;
  257. HalPCR->ProfileCountDown = RoundApicHz;
  258. LOCAL_APIC(LU_INITIAL_COUNT) = RoundApicHz;
  259. //
  260. // Restore interrupt state
  261. //
  262. HalpRestoreInterrupts(eflags);
  263. return TRUE;
  264. }
  265. VOID
  266. HalpWasteTime (
  267. ULONG Ticks
  268. )
  269. {
  270. ULONG i;
  271. for (i = 0; i < Ticks; i++) {
  272. HalpGlobalVolatile *= i;
  273. }
  274. }
  275. VOID
  276. #if defined(MMTIMER)
  277. HalpPmTimerCalibratePerfCount (
  278. #else
  279. HalCalibratePerformanceCounter (
  280. #endif
  281. IN LONG volatile *Number,
  282. IN ULONGLONG NewCount
  283. )
  284. /*++
  285. Routine Description:
  286. This routine sets the performance counter value for the current
  287. processor to the specified value. The reset is done such that the
  288. resulting value is closely synchronized with other processors in
  289. the configuration.
  290. Arguments:
  291. Number - Supplies a pointer to count of the number of processors in
  292. the configuration.
  293. NewCount - Supplies the value to synchronize the counter to
  294. Return Value:
  295. None.
  296. --*/
  297. {
  298. ULONG64 CurrentTime;
  299. if(KeGetCurrentProcessorNumber() == 0) {
  300. CurrentTime = HalpQueryPerformanceCounter();
  301. HalpTimeBias = NewCount - CurrentTime;
  302. }
  303. InterlockedDecrement(Number);
  304. //
  305. // Wait for all processors to signal
  306. //
  307. while (*Number > 0) {
  308. }
  309. }