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.

668 lines
15 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. mmtimer.c
  5. Abstract:
  6. This module contains the HAL's multimedia event timer support
  7. Author:
  8. Eric Nelson (enelson) July 7, 2000
  9. Revision History:
  10. --*/
  11. #include "halp.h"
  12. #include "acpitabl.h"
  13. #include "mmtimer.h"
  14. #include "xxtimer.h"
  15. //
  16. // Event timer block context
  17. //
  18. static ETB_CONTEXT ETBContext = { 0, // Number of event timers
  19. NULL, // VA of event timer block
  20. { 0, 0 }, // PA of event timer block
  21. 100, // Clock period in nanoseconds
  22. 100, // System clock frequency in Hz
  23. 100000, // System clock period in ticks
  24. FALSE, // Multi media HW initialized?
  25. FALSE }; // Change system clock frequency?
  26. //
  27. // Event timer block registers address usage
  28. //
  29. static ADDRESS_USAGE HalpmmTimerResource = {
  30. NULL, CmResourceTypeMemory, DeviceUsage, { 0, 0x400, 0, 0 }
  31. };
  32. //
  33. // Offset is the difference between the multi media timer HW's main
  34. // 32-bit counter register and the HAL's 64-bit software PerfCount:
  35. //
  36. // ASSERT(PerfCount == ETBContext.EventTimer->MainCounter + Offset);
  37. //
  38. static LONGLONG Offset = 0;
  39. static ULONGLONG PerfCount = 0;
  40. #define HAL_PRIMARY_PROCESSOR 0
  41. #define MAX_ULONG 0xFFFFFFFF
  42. #define __4GB 0x100000000
  43. #define __1MHz 1000000
  44. #define __10MHz 10000000
  45. #define __1GHz 1000000000
  46. #define HALF(n) ((n) / 2)
  47. #if DBG || MMTIMER_DEV
  48. static ULONG CounterReads = 0;
  49. #endif
  50. #define MIN_LOOP_QUANTUM 1
  51. static ULONG MinLoopCount = MIN_LOOP_QUANTUM;
  52. static UCHAR StallCount = 0;
  53. #ifdef ALLOC_PRAGMA
  54. #pragma alloc_text(INIT, HalpmmTimer)
  55. #pragma alloc_text(INIT, HalpmmTimerInit)
  56. #pragma alloc_text(INIT, HalpmmTimerClockInit)
  57. #pragma alloc_text(INIT, HalpmmTimerCalibratePerfCount)
  58. #endif
  59. BOOLEAN
  60. HalpmmTimer(
  61. VOID
  62. )
  63. /*++
  64. Routine Description:
  65. This routine is used to determine if multi media timer HW is
  66. present, and has been initialized
  67. note: this routine should only used during HAL init
  68. Arguments:
  69. None
  70. Return Value:
  71. TRUE if the multi media timer HW is present, and has been initialized
  72. --*/
  73. {
  74. return ETBContext.Initialized;
  75. }
  76. ULONG
  77. HalpmmTimerSetTimeIncrement(
  78. IN ULONG DesiredIncrement
  79. )
  80. /*++
  81. Routine Description:
  82. This routine initialize system time clock to generate an
  83. interrupt at every DesiredIncrement interval
  84. Arguments:
  85. DesiredIncrement - Desired interval between every timer tick (in
  86. 100ns unit)
  87. Return Value:
  88. The *REAL* time increment set
  89. --*/
  90. {
  91. //
  92. // For starters we will only support a default system clock
  93. // frequency of 10ms
  94. //
  95. // 100ns = 1/10MHz, and (1/SysClock) / (1/10MHz) == 10MHz/SysClock, .:.
  96. //
  97. return __10MHz / ETBContext.SystemClockFrequency;
  98. }
  99. VOID
  100. HalpmmTimerClockInit(
  101. VOID
  102. )
  103. /*++
  104. Routine Description:
  105. This routine initializes the system clock using the multi media event
  106. timer to generate an interrupt every 10ms
  107. Arguments:
  108. None
  109. Return Value:
  110. None
  111. --*/
  112. {
  113. ULONG MinSysClockFreq;
  114. ULONG MaxSysClockFreq;
  115. ETB_GEN_CONF GenConf;
  116. ETB_CONF_CAPS mmT0ConfCaps;
  117. //
  118. // Reset the main counter and its associated performance variables
  119. // to 0, nobody should be using them this early
  120. //
  121. GenConf.AsULONG = ETBContext.EventTimer->GeneralConfig;
  122. GenConf.GlobalIRQEnable = OFF;
  123. ETBContext.EventTimer->GeneralConfig = GenConf.AsULONG;
  124. ETBContext.EventTimer->MainCounter = 0;
  125. Offset = 0;
  126. PerfCount = 0;
  127. //
  128. // Initialize multi media context for a default system clock
  129. // freuqency of 100Hz, with a period of 10ms
  130. //
  131. ETBContext.SystemClockFrequency = 100;
  132. ETBContext.SystemClockTicks = __1GHz /
  133. (ETBContext.SystemClockFrequency * ETBContext.ClockPeriod);
  134. //
  135. // Setup timer 0 for periodc mode
  136. //
  137. mmT0ConfCaps.AsULONG =
  138. ETBContext.EventTimer->mmTimer[0].ConfigCapabilities;
  139. ASSERT(mmT0ConfCaps.PeriodicCapable == ON);
  140. mmT0ConfCaps.ValueSetConfig = ON;
  141. mmT0ConfCaps.IRQEnable = ON;
  142. mmT0ConfCaps.PeriodicModeEnable = ON;
  143. ETBContext.EventTimer->mmTimer[0].ConfigCapabilities =
  144. mmT0ConfCaps.AsULONG;
  145. //
  146. // Set comparator to the desired system clock frequency
  147. //
  148. ETBContext.EventTimer->mmTimer[0].Comparator = ETBContext.SystemClockTicks;
  149. //
  150. // Fire up the main counter
  151. //
  152. GenConf.AsULONG = ETBContext.EventTimer->GeneralConfig;
  153. GenConf.GlobalIRQEnable = ON;
  154. ETBContext.EventTimer->GeneralConfig = GenConf.AsULONG;
  155. //
  156. // Inform kernel of our supported system clock frequency range in
  157. // 100ns units, but for starters we will only support 10ms default
  158. //
  159. MinSysClockFreq = __10MHz / ETBContext.SystemClockFrequency;
  160. MaxSysClockFreq = MinSysClockFreq;
  161. #ifndef MMTIMER_DEV
  162. KeSetTimeIncrement(MinSysClockFreq, MaxSysClockFreq);
  163. #endif
  164. }
  165. #ifdef MMTIMER_DEV
  166. static ULONG HalpmmTimerClockInts = 0;
  167. #endif
  168. VOID
  169. HalpmmTimerClockInterrupt(
  170. VOID
  171. )
  172. /*++
  173. Routine Description:
  174. This routine is entered as the result of an interrupt generated by
  175. CLOCK, update our performance count and change system clock frequency
  176. if necessary
  177. Arguments:
  178. None
  179. Return Value:
  180. None
  181. --*/
  182. {
  183. //
  184. // Update PerfCount
  185. //
  186. PerfCount += ETBContext.SystemClockTicks;
  187. //
  188. // If the 32-bit counter has wrapped, update Offset accordingly
  189. //
  190. if (PerfCount - Offset > MAX_ULONG) {
  191. Offset += __4GB;
  192. }
  193. #ifdef MMTIMER_DEV
  194. HalpmmTimerClockInts++;
  195. #endif
  196. //
  197. // Check if a new frequency has been requested
  198. //
  199. if (ETBContext.NewClockFrequency) {
  200. //
  201. // ???
  202. //
  203. ETBContext.NewClockFrequency = FALSE;
  204. }
  205. }
  206. VOID
  207. HalpmmTimerInit(
  208. IN ULONG EventTimerBlockID,
  209. IN ULONG BaseAddress
  210. )
  211. /*++
  212. Routine Description:
  213. This routine initializes the multimedia event timer
  214. Arguments:
  215. EventTimerBlockID - Various bits of info, including number of Event
  216. Timers
  217. BaseAddress - Physical Base Address of 1st Event Timer Block
  218. Return Value:
  219. None
  220. --*/
  221. {
  222. ULONG i;
  223. ETB_GEN_CONF GenConf;
  224. ETB_GEN_CAP_ID GenCaps;
  225. PHYSICAL_ADDRESS PhysAddr;
  226. PEVENT_TIMER_BLOCK EventTimer;
  227. TIMER_FUNCTIONS TimerFunctions = { HalpmmTimerStallExecProc,
  228. HalpmmTimerCalibratePerfCount,
  229. HalpmmTimerQueryPerfCount,
  230. HalpmmTimerSetTimeIncrement };
  231. #if MMTIMER_DEV && PICACPI
  232. {
  233. UCHAR Data;
  234. //
  235. // (BUGBUG!) BIOS should enable the device
  236. //
  237. Data = 0x87;
  238. HalpPhase0SetPciDataByOffset(0,
  239. 9,
  240. &Data,
  241. 4,
  242. sizeof(Data));
  243. }
  244. #endif
  245. //
  246. // Establish VA for Multimedia Timer HW Base Address
  247. //
  248. PhysAddr.QuadPart = BaseAddress;
  249. EventTimer = HalpMapPhysicalMemoryWriteThrough(PhysAddr, 1);
  250. //
  251. // Register address usage
  252. //
  253. HalpmmTimerResource.Element[0].Start = BaseAddress;
  254. HalpRegisterAddressUsage(&HalpmmTimerResource);
  255. //
  256. // Read the General Capabilities and ID Register
  257. //
  258. GenCaps.AsULONG = EventTimer->GeneralCapabilities;
  259. //
  260. // Save context
  261. //
  262. ETBContext.TimerCount = GenCaps.TimerCount + 1; // Convert from zero-based
  263. ETBContext.BaseAddress.QuadPart = BaseAddress;
  264. ETBContext.EventTimer = EventTimer;
  265. ETBContext.NewClockFrequency = FALSE;
  266. //
  267. // Save clock period as nanoseconds, convert from femptoseconds so
  268. // we don't have to worry about nasty overflow
  269. //
  270. #ifndef MMTIMER_DEV
  271. ETBContext.ClockPeriod = EventTimer->ClockPeriod / __1MHz;
  272. #else
  273. ETBContext.ClockPeriod = 100; // Proto HW is 10MHz, with a period of 100ns
  274. #endif
  275. //
  276. // Reset the main counter and its associated performance counter
  277. // variables
  278. //
  279. GenConf.AsULONG = EventTimer->GeneralConfig;
  280. GenConf.GlobalIRQEnable = ON;
  281. //GenConf.LegacyIRQRouteEnable = ON;
  282. EventTimer->MainCounter = 0;
  283. Offset = 0;
  284. PerfCount = 0;
  285. EventTimer->GeneralConfig = GenConf.AsULONG;
  286. //
  287. // Set HAL timer functions to use Multimedia Timer HW
  288. //
  289. HalpSetTimerFunctions(&TimerFunctions);
  290. ETBContext.Initialized = TRUE;
  291. }
  292. //ULONG
  293. //HalpmmTimerTicks(
  294. // IN ULONG StartCount,
  295. // IN ULONG EndCount
  296. // )
  297. ///*++
  298. //
  299. //Routine Description:
  300. //
  301. // Calculate the difference in ticks between StartCount and EndCount
  302. // taking into consideraton counter rollover
  303. //
  304. //Arguments:
  305. //
  306. // StartCount - Value of main counter at time t0
  307. //
  308. // EndCount - Value of main counter at end time t1
  309. //
  310. //Return Value:
  311. //
  312. // Returns the positive number of ticks which have elapsed between time
  313. // t0, and t1
  314. //
  315. //--*/
  316. //
  317. #define HalpmmTimerTicks(StartCount, EndCount) (((EndCount) >= (StartCount)) ? (EndCount) - (StartCount): (EndCount) + (MAX_ULONG - (StartCount)) + 1)
  318. #define WHACK_HIGH_DIFF 0xFFFF0000
  319. #define ULONG_BITS 32
  320. VOID
  321. HalpmmTimerStallExecProc(
  322. IN ULONG MicroSeconds
  323. )
  324. /*++
  325. Routine Description:
  326. This function stalls execution for the specified number of microseconds
  327. Arguments:
  328. MicroSeconds - Supplies the number of microseconds that execution is to be
  329. stalled
  330. Return Value:
  331. None
  332. --*/
  333. {
  334. ULONG i;
  335. #ifndef i386
  336. ULONG j;
  337. ULONG Mirror;
  338. #endif
  339. ULONG EndCount;
  340. ULONG StartCount;
  341. ULONG TargetTicks;
  342. ULONG ElapsedTicks;
  343. ULONG CyclesStalled;
  344. ULONG TicksPerMicroSec;
  345. ElapsedTicks = 0;
  346. CyclesStalled = 0;
  347. #if DBG || MMTIMER_DEV
  348. CounterReads = 0;
  349. #endif
  350. TicksPerMicroSec = 1000 / ETBContext.ClockPeriod;
  351. TargetTicks = MicroSeconds * TicksPerMicroSec;
  352. StartCount = ETBContext.EventTimer->MainCounter;
  353. //
  354. // BIAS: We've stalled for .5us already!
  355. //
  356. TargetTicks -= HALF(TicksPerMicroSec);
  357. //
  358. // Get a warm fuzzy for what it's like to stall for more than .5us
  359. //
  360. while (TRUE) {
  361. #ifdef i386
  362. _asm { rep nop }
  363. #endif
  364. i = MinLoopCount;
  365. CyclesStalled += i;
  366. while (i--) {
  367. #ifdef i386
  368. _asm {
  369. xor eax, eax
  370. cpuid
  371. }
  372. #else
  373. Mirror = 0;
  374. for (j = 0; j < ULONG_BITS; j++) {
  375. Mirror <<= 1;
  376. Mirror |= EndCount & 1;
  377. EndCount >>= 1;
  378. }
  379. EndCount = Mirror;
  380. #endif // i386
  381. }
  382. EndCount = ETBContext.EventTimer->MainCounter;
  383. #if DBG || MMTIMER_DEV
  384. CounterReads++;
  385. #endif
  386. ElapsedTicks = HalpmmTimerTicks(StartCount, EndCount);
  387. if (ElapsedTicks >= HALF(TicksPerMicroSec)) {
  388. break;
  389. }
  390. MinLoopCount += MIN_LOOP_QUANTUM;
  391. }
  392. #ifdef MMTIMER_DEV
  393. //
  394. // Something is whack, probably time went backwards! Act as if we
  395. // hit our target of .5us and reset StartCount to the current value
  396. // less ElapsedTicks
  397. //
  398. if (ElapsedTicks > WHACK_HIGH_DIFF) {
  399. ElapsedTicks = HALF(TicksPerMicroSec);
  400. StartCount = EndCount - ElapsedTicks;
  401. }
  402. #endif // MMTIMER_DEV
  403. //
  404. // Now that we have a warm fuzzy, try to approximate a workload that
  405. // will keep us busy for the remainder of microsoeconds
  406. //
  407. while (TargetTicks > ElapsedTicks) {
  408. #ifdef i386
  409. _asm { rep nop }
  410. #endif
  411. i = (TargetTicks - ElapsedTicks) * CyclesStalled / ElapsedTicks;
  412. CyclesStalled += i;
  413. while (i--) {
  414. #ifdef i386
  415. _asm {
  416. xor eax, eax
  417. cpuid
  418. }
  419. #else
  420. Mirror = 0;
  421. for (j = 0; j < ULONG_BITS; j++) {
  422. Mirror <<= 1;
  423. Mirror |= EndCount & 1;
  424. EndCount >>= 1;
  425. }
  426. EndCount = Mirror;
  427. #endif // i386
  428. }
  429. EndCount = ETBContext.EventTimer->MainCounter;
  430. #if DBG || MMTIMER_DEV
  431. CounterReads++;
  432. #endif
  433. ElapsedTicks = HalpmmTimerTicks(StartCount, EndCount);
  434. }
  435. //
  436. // Decrement MinimumLoopCount every 0x100 calls so we don't accidentally
  437. // wind up stalling for longer periods
  438. //
  439. StallCount++;
  440. if ((StallCount == 0) && (MinLoopCount > MIN_LOOP_QUANTUM)) {
  441. MinLoopCount -= MIN_LOOP_QUANTUM;
  442. }
  443. }
  444. VOID
  445. HalpmmTimerCalibratePerfCount(
  446. IN LONG volatile *Number,
  447. IN ULONGLONG NewCount
  448. )
  449. /*++
  450. Routine Description:
  451. This routine resets the performance counter value for the current
  452. processor to zero, the reset is done such that the resulting value
  453. is closely synchronized with other processors in the configuration
  454. Arguments:
  455. Number - Supplies a pointer to count of the number of processors in
  456. the configuration
  457. NewCount - Supplies the value to synchronize the counter too
  458. Return Value:
  459. None
  460. --*/
  461. {
  462. ULONG MainCount;
  463. //
  464. // If this isn't the primary processor, then return
  465. //
  466. if (KeGetCurrentPrcb()->Number != HAL_PRIMARY_PROCESSOR) {
  467. return;
  468. }
  469. MainCount = ETBContext.EventTimer->MainCounter;
  470. PerfCount = NewCount;
  471. Offset = PerfCount - MainCount;
  472. }
  473. LARGE_INTEGER
  474. HalpmmTimerQueryPerfCount(
  475. OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL
  476. )
  477. /*++
  478. Routine Description:
  479. This routine returns current 64-bit performance counter and,
  480. optionally, the Performance Frequency
  481. N.B. The performace counter returned by this routine is
  482. not necessary the value when this routine is just entered,
  483. The value returned is actually the counter value at any point
  484. between the routine is entered and is exited
  485. Arguments:
  486. PerformanceFrequency - optionally, supplies the address of a
  487. variable to receive the performance counter
  488. frequency
  489. Return Value:
  490. Current value of the performance counter will be returned
  491. --*/
  492. {
  493. ULONG MainCount;
  494. LARGE_INTEGER li;
  495. //
  496. // Clock period is in nanoseconds, help the calculation remain
  497. // integer by asserting multi media HW clock frequency is between
  498. // 1MHz and 1GHz, with a period between 1ns and 1Kns, seems
  499. // reasonable to me?
  500. //
  501. if (PerformanceFrequency) {
  502. ASSERT((ETBContext.ClockPeriod > 0) &&
  503. (ETBContext.ClockPeriod <= 1000));
  504. PerformanceFrequency->QuadPart =
  505. (1000 / ETBContext.ClockPeriod) * __1MHz;
  506. }
  507. //
  508. // Read main counter
  509. //
  510. MainCount = ETBContext.EventTimer->MainCounter;
  511. //
  512. // Check if our 32-bit counter has wrapped since we took our last
  513. // clock tick
  514. //
  515. li.QuadPart = (PerfCount - Offset > MainCount) ?
  516. Offset + __4GB + MainCount:
  517. MainCount + Offset;
  518. return li;
  519. }