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