[ntoskrnl]
[reactos.git] / reactos / hal / halx86 / generic / timer.c
1 /*
2 * PROJECT: ReactOS HAL
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: hal/halx86/generic/timer.c
5 * PURPOSE: HAL Timer Routines
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <hal.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS *******************************************************************/
16
17 /* time to wait */
18 #define MICROSECOND_TO_WAIT 1000
19 /* the tick count for 1 ms is 1193.182 (1193182 Hz) round it up */
20 #define TICKCOUNT_TO_WAIT 1194
21
22 BOOLEAN HalpClockSetMSRate;
23 ULONG HalpCurrentTimeIncrement;
24 ULONG HalpCurrentRollOver;
25 ULONG HalpNextMSRate = 14;
26 ULONG HalpLargestClockMS = 15;
27
28 LARGE_INTEGER HalpRolloverTable[15] =
29 {
30 {{1197, 10032}},
31 {{2394, 20064}},
32 {{3591, 30096}},
33 {{4767, 39952}},
34 {{5964, 49984}},
35 {{7161, 60016}},
36 {{8358, 70048}},
37 {{9555, 80080}},
38 {{10731, 89936}},
39 {{11949, 100144}},
40 {{13125, 110000}},
41 {{14322, 120032}},
42 {{15519, 130064}},
43 {{16695, 139920}},
44 {{17892, 149952}}
45 };
46
47 /* PRIVATE FUNCTIONS *********************************************************/
48
49 VOID
50 NTAPI
51 HalpInitializeClock(VOID)
52 {
53 PKPRCB Prcb = KeGetCurrentPrcb();
54 ULONG Increment;
55 USHORT RollOver;
56 ULONG_PTR Flags;
57
58 /* Check the CPU Type */
59 if (Prcb->CpuType <= 4)
60 {
61 /* 486's or equal can't go higher then 10ms */
62 HalpLargestClockMS = 10;
63 HalpNextMSRate = 9;
64 }
65
66 /* Get increment and rollover for the largest time clock ms possible */
67 Increment= HalpRolloverTable[HalpLargestClockMS - 1].HighPart;
68 RollOver = (USHORT)HalpRolloverTable[HalpLargestClockMS - 1].LowPart;
69
70 /* Set the maximum and minimum increment with the kernel */
71 HalpCurrentTimeIncrement = Increment;
72 KeSetTimeIncrement(Increment, HalpRolloverTable[0].HighPart);
73
74 /* Disable interrupts */
75 Flags = __readeflags();
76 _disable();
77
78 /* Set the rollover */
79 __outbyte(TIMER_CONTROL_PORT, TIMER_SC0 | TIMER_BOTH | TIMER_MD2);
80 __outbyte(TIMER_DATA_PORT0, RollOver & 0xFF);
81 __outbyte(TIMER_DATA_PORT0, RollOver >> 8);
82
83 /* Restore interrupts if they were previously enabled */
84 __writeeflags(Flags);
85
86 /* Save rollover and return */
87 HalpCurrentRollOver = RollOver;
88 }
89
90 /* PUBLIC FUNCTIONS ***********************************************************/
91
92 /*
93 * @implemented
94 */
95 VOID
96 NTAPI
97 HalCalibratePerformanceCounter(IN volatile PLONG Count,
98 IN ULONGLONG NewCount)
99 {
100 ULONG_PTR Flags;
101
102 /* Disable interrupts */
103 Flags = __readeflags();
104 _disable();
105
106 /* Do a decrement for this CPU */
107 _InterlockedDecrement(Count);
108
109 /* Wait for other CPUs */
110 while (*Count);
111
112 /* Restore interrupts if they were previously enabled */
113 __writeeflags(Flags);
114 }
115
116 /*
117 * @implemented
118 */
119 ULONG
120 NTAPI
121 HalSetTimeIncrement(IN ULONG Increment)
122 {
123 /* Round increment to ms */
124 Increment /= 10000;
125
126 /* Normalize between our minimum (1 ms) and maximum (variable) setting */
127 if (Increment > HalpLargestClockMS) Increment = HalpLargestClockMS;
128 if (Increment <= 0) Increment = 1;
129
130 /* Set the rate and tell HAL we want to change it */
131 HalpNextMSRate = Increment;
132 HalpClockSetMSRate = TRUE;
133
134 /* Return the increment */
135 return HalpRolloverTable[Increment - 1].HighPart;
136 }
137
138 ULONG
139 WaitFor8254Wraparound(VOID)
140 {
141 ULONG StartTicks;
142 ULONG PrevTicks;
143 LONG Delta;
144
145 StartTicks = HalpQuery8254Counter();
146
147 do
148 {
149 PrevTicks = StartTicks;
150 StartTicks = HalpQuery8254Counter();
151 Delta = StartTicks - PrevTicks;
152
153 /*
154 * This limit for delta seems arbitrary, but it isn't, it's
155 * slightly above the level of error a buggy Mercury/Neptune
156 * chipset timer can cause.
157 */
158
159 }
160 while (Delta < 300);
161
162 return StartTicks;
163 }
164
165 VOID
166 NTAPI
167 HalpCalibrateStallExecution(VOID)
168 {
169 ULONG CalibrationBit;
170 ULONG EndTicks;
171 ULONG StartTicks;
172 ULONG OverheadTicks;
173 PKIPCR Pcr;
174
175 Pcr = (PKIPCR)KeGetPcr();
176
177 /* Measure the delay for the minimum call overhead in ticks */
178 Pcr->StallScaleFactor = 1;
179 StartTicks = WaitFor8254Wraparound();
180 KeStallExecutionProcessor(1);
181 EndTicks = HalpQuery8254Counter();
182 OverheadTicks = (StartTicks - EndTicks);
183
184 do
185 {
186 /* Increase the StallScaleFactor */
187 Pcr->StallScaleFactor = Pcr->StallScaleFactor * 2;
188
189 if (Pcr->StallScaleFactor == 0)
190 {
191 /* Nothing found */
192 break;
193 }
194
195 /* Get the start ticks */
196 StartTicks = WaitFor8254Wraparound();
197
198 /* Wait for a defined time */
199 KeStallExecutionProcessor(MICROSECOND_TO_WAIT);
200
201 /* Get the end ticks */
202 EndTicks = HalpQuery8254Counter();
203
204 DPRINT("Pcr->StallScaleFactor: %d\n", Pcr->StallScaleFactor);
205 DPRINT("Time1 : StartTicks %i - EndTicks %i = %i\n",
206 StartTicks, EndTicks, StartTicks - EndTicks);
207 } while ((StartTicks - EndTicks) <= (TICKCOUNT_TO_WAIT + OverheadTicks));
208
209 /* A StallScaleFactor lesser than INITIAL_STALL_COUNT makes no sense */
210 if (Pcr->StallScaleFactor >= (INITIAL_STALL_COUNT * 2))
211 {
212 /* Adjust the StallScaleFactor */
213 Pcr->StallScaleFactor = Pcr->StallScaleFactor / 2;
214
215 /* Setup the CalibrationBit */
216 CalibrationBit = Pcr->StallScaleFactor;
217
218 for (;;)
219 {
220 /* Lower the CalibrationBit */
221 CalibrationBit = CalibrationBit / 2;
222 if (CalibrationBit == 0)
223 {
224 break;
225 }
226
227 /* Add the CalibrationBit */
228 Pcr->StallScaleFactor = Pcr->StallScaleFactor + CalibrationBit;
229
230 /* Get the start ticks */
231 StartTicks = WaitFor8254Wraparound();
232
233 /* Wait for a defined time */
234 KeStallExecutionProcessor(MICROSECOND_TO_WAIT);
235
236 /* Get the end ticks */
237 EndTicks = HalpQuery8254Counter();
238
239 DPRINT("Pcr->StallScaleFactor: %d\n", Pcr->StallScaleFactor);
240 DPRINT("Time2 : StartTicks %i - EndTicks %i = %i\n",
241 StartTicks, EndTicks, StartTicks - EndTicks);
242
243 if ((StartTicks-EndTicks) > (TICKCOUNT_TO_WAIT+OverheadTicks))
244 {
245 /* Too big so subtract the CalibrationBit */
246 Pcr->StallScaleFactor = Pcr->StallScaleFactor - CalibrationBit;
247 }
248 }
249 DPRINT("New StallScaleFactor: %d\n", Pcr->StallScaleFactor);
250 }
251 else
252 {
253 /* Set StallScaleFactor to the default */
254 Pcr->StallScaleFactor = INITIAL_STALL_COUNT;
255 }
256
257 #if 0
258 /* For debugging */
259 ULONG i;
260
261 DPRINT1("About to start delay loop test\n");
262 DPRINT1("Waiting for a minute...");
263 for (i = 0; i < (60*1000*20); i++)
264 {
265 KeStallExecutionProcessor(50);
266 }
267 DPRINT1("finished\n");
268
269
270 DPRINT1("About to start delay loop test\n");
271 DPRINT1("Waiting for a minute...");
272 for (i = 0; i < (60*1000); i++)
273 {
274 KeStallExecutionProcessor(1000);
275 }
276 DPRINT1("finished\n");
277
278
279 DPRINT1("About to start delay loop test\n");
280 DPRINT1("Waiting for a minute...");
281 for (i = 0; i < (60*1000*1000); i++)
282 {
283 KeStallExecutionProcessor(1);
284 }
285 DPRINT1("finished\n");
286
287 DPRINT1("About to start delay loop test\n");
288 DPRINT1("Waiting for a minute...");
289 KeStallExecutionProcessor(60*1000000);
290 DPRINT1("finished\n");
291 #endif
292 }
293
294
295 /* EOF */