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