1 /*
2 * PROJECT: ReactOS HAL
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: hal/halx86/apic/tsc.c
5 * PURPOSE: HAL Routines for TSC handling
6 * PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org)
7 */
9 /* INCLUDES ******************************************************************/
11 #include <hal.h>
12 #define NDEBUG
13 #include <debug.h>
15 #include "tsc.h"
17 LARGE_INTEGER HalpCpuClockFrequency = {{INITIAL_STALL_COUNT * 1000000}};
19 UCHAR TscCalibrationPhase;
20 ULONG64 TscCalibrationArray[NUM_SAMPLES];
21 UCHAR HalpRtcClockVector = 0xD1;
23 #define RTC_MODE 6 /* Mode 6 is 1024 Hz */
24 #define SAMPLE_FREQENCY ((32768 << 1) >> RTC_MODE)
26 /* PRIVATE FUNCTIONS *********************************************************/
28 static
29 ULONG64
30 DoLinearRegression(
31 ULONG XMax,
32 ULONG64 *ArrayY)
33 {
34 ULONG X, SumXX;
35 ULONG64 SumXY;
37 /* Calculate the sum of the squares of X */
38 SumXX = (XMax * (XMax + 1) * (2*XMax + 1)) / 6;
40 /* Calculate the sum of the differences to the first value
41 weighted by x */
42 for (SumXY = 0, X = 1; X <= XMax; X++)
43 {
44 SumXY += X * (ArrayY[X] - ArrayY[0]);
45 }
47 /* Account for sample frequency */
48 SumXY *= SAMPLE_FREQENCY;
50 /* Return the quotient of the sums */
51 return (SumXY + (SumXX/2)) / SumXX;
52 }
54 VOID
55 NTAPI
56 HalpInitializeTsc(VOID)
57 {
58 ULONG_PTR Flags;
59 KIDTENTRY OldIdtEntry, *IdtPointer;
60 PKPCR Pcr = KeGetPcr();
61 UCHAR RegisterA, RegisterB;
63 /* Check if the CPU supports RDTSC */
64 if (!(KeGetCurrentPrcb()->FeatureBits & KF_RDTSC))
65 {
66 KeBugCheck(HAL_INITIALIZATION_FAILED);
67 }
69 /* Save flags and disable interrupts */
70 Flags = __readeflags();
71 _disable();
73 /* Enable the periodic interrupt in the CMOS */
74 RegisterB = HalpReadCmos(RTC_REGISTER_B);
75 HalpWriteCmos(RTC_REGISTER_B, RegisterB | RTC_REG_B_PI);
77 /* Modify register A to RTC_MODE to get SAMPLE_FREQENCY */
78 RegisterA = HalpReadCmos(RTC_REGISTER_A);
79 RegisterA = (RegisterA & 0xF0) | RTC_MODE;
80 HalpWriteCmos(RTC_REGISTER_A, RegisterA);
82 /* Save old IDT entry */
83 IdtPointer = KiGetIdtEntry(Pcr, HalpRtcClockVector);
84 OldIdtEntry = *IdtPointer;
86 /* Set the calibration ISR */
87 KeRegisterInterruptHandler(HalpRtcClockVector, TscCalibrationISR);
89 /* Reset TSC value to 0 */
90 __writemsr(MSR_RDTSC, 0);
92 /* Enable the timer interrupt */
93 HalEnableSystemInterrupt(HalpRtcClockVector, CLOCK_LEVEL, Latched);
95 /* Read register C, so that the next interrupt can happen */
98 /* Wait for completion */
99 _enable();
100 while (TscCalibrationPhase < NUM_SAMPLES) _ReadWriteBarrier();
101 _disable();
103 /* Disable the periodic interrupt in the CMOS */
104 HalpWriteCmos(RTC_REGISTER_B, RegisterB & ~RTC_REG_B_PI);
106 /* Disable the timer interrupt */
107 HalDisableSystemInterrupt(HalpRtcClockVector, CLOCK_LEVEL);
109 /* Restore old IDT entry */
110 *IdtPointer = OldIdtEntry;
112 /* Calculate an average, using simplified linear regression */
113 HalpCpuClockFrequency.QuadPart = DoLinearRegression(NUM_SAMPLES - 1,
114 TscCalibrationArray);
116 /* Restore flags */
117 __writeeflags(Flags);
119 }
121 VOID
122 NTAPI
123 HalpCalibrateStallExecution(VOID)
124 {
125 // Timer interrupt is now active
127 HalpInitializeTsc();
129 KeGetPcr()->StallScaleFactor = (ULONG)(HalpCpuClockFrequency.QuadPart / 1000000);
130 }
132 /* PUBLIC FUNCTIONS ***********************************************************/
134 LARGE_INTEGER
135 NTAPI
136 KeQueryPerformanceCounter(
137 OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL)
138 {
139 LARGE_INTEGER Result;
141 /* Make sure it's calibrated */
142 ASSERT(HalpCpuClockFrequency.QuadPart != 0);
144 /* Does the caller want the frequency? */
145 if (PerformanceFrequency)
146 {
147 /* Return tsc frequency */
148 *PerformanceFrequency = HalpCpuClockFrequency;
149 }
151 /* Return the current value */
152 Result.QuadPart = __rdtsc();
153 return Result;
154 }
156 VOID
157 NTAPI
158 KeStallExecutionProcessor(ULONG MicroSeconds)
159 {
160 ULONG64 StartTime, EndTime;
162 /* Get the initial time */
163 StartTime = __rdtsc();
165 /* Calculate the ending time */
166 EndTime = StartTime + KeGetPcr()->StallScaleFactor * MicroSeconds;
168 /* Loop until time is elapsed */
169 while (__rdtsc() < EndTime);
170 }
172 VOID
173 NTAPI
174 HalCalibratePerformanceCounter(
175 IN volatile PLONG Count,
176 IN ULONGLONG NewCount)
177 {
178 UNIMPLEMENTED;
179 ASSERT(FALSE);
180 }