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)
9 /* INCLUDES ******************************************************************/
15 /* GLOBALS *******************************************************************/
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
22 BOOLEAN HalpClockSetMSRate
;
23 ULONG HalpCurrentTimeIncrement
;
24 ULONG HalpCurrentRollOver
;
25 ULONG HalpNextMSRate
= 14;
26 ULONG HalpLargestClockMS
= 15;
28 LARGE_INTEGER HalpRolloverTable
[15] =
47 /* PRIVATE FUNCTIONS *********************************************************/
51 HalpInitializeClock(VOID
)
53 PKPRCB Prcb
= KeGetCurrentPrcb();
58 /* Check the CPU Type */
59 if (Prcb
->CpuType
<= 4)
61 /* 486's or equal can't go higher then 10ms */
62 HalpLargestClockMS
= 10;
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
;
70 /* Set the maximum and minimum increment with the kernel */
71 HalpCurrentTimeIncrement
= Increment
;
72 KeSetTimeIncrement(Increment
, HalpRolloverTable
[0].HighPart
);
74 /* Disable interrupts */
75 Flags
= __readeflags();
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);
83 /* Restore interrupts if they were previously enabled */
86 /* Save rollover and return */
87 HalpCurrentRollOver
= RollOver
;
90 /* PUBLIC FUNCTIONS ***********************************************************/
97 HalCalibratePerformanceCounter(IN
volatile PLONG Count
,
98 IN ULONGLONG NewCount
)
102 /* Disable interrupts */
103 Flags
= __readeflags();
106 /* Do a decrement for this CPU */
107 _InterlockedDecrement(Count
);
109 /* Wait for other CPUs */
112 /* Restore interrupts if they were previously enabled */
113 __writeeflags(Flags
);
121 HalSetTimeIncrement(IN ULONG Increment
)
123 /* Round increment to ms */
126 /* Normalize between our minimum (1 ms) and maximum (variable) setting */
127 if (Increment
> HalpLargestClockMS
) Increment
= HalpLargestClockMS
;
128 if (Increment
<= 0) Increment
= 1;
130 /* Set the rate and tell HAL we want to change it */
131 HalpNextMSRate
= Increment
;
132 HalpClockSetMSRate
= TRUE
;
134 /* Return the increment */
135 return HalpRolloverTable
[Increment
- 1].HighPart
;
139 WaitFor8254Wraparound(VOID
)
145 StartTicks
= HalpQuery8254Counter();
149 PrevTicks
= StartTicks
;
150 StartTicks
= HalpQuery8254Counter();
151 Delta
= StartTicks
- PrevTicks
;
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.
167 HalpCalibrateStallExecution(VOID
)
169 ULONG CalibrationBit
;
175 Pcr
= (PKIPCR
)KeGetPcr();
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
);
186 /* Increase the StallScaleFactor */
187 Pcr
->StallScaleFactor
= Pcr
->StallScaleFactor
* 2;
189 if (Pcr
->StallScaleFactor
== 0)
195 /* Get the start ticks */
196 StartTicks
= WaitFor8254Wraparound();
198 /* Wait for a defined time */
199 KeStallExecutionProcessor(MICROSECOND_TO_WAIT
);
201 /* Get the end ticks */
202 EndTicks
= HalpQuery8254Counter();
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
));
209 /* A StallScaleFactor lesser than INITIAL_STALL_COUNT makes no sense */
210 if (Pcr
->StallScaleFactor
>= (INITIAL_STALL_COUNT
* 2))
212 /* Adjust the StallScaleFactor */
213 Pcr
->StallScaleFactor
= Pcr
->StallScaleFactor
/ 2;
215 /* Setup the CalibrationBit */
216 CalibrationBit
= Pcr
->StallScaleFactor
;
220 /* Lower the CalibrationBit */
221 CalibrationBit
= CalibrationBit
/ 2;
222 if (CalibrationBit
== 0)
227 /* Add the CalibrationBit */
228 Pcr
->StallScaleFactor
= Pcr
->StallScaleFactor
+ CalibrationBit
;
230 /* Get the start ticks */
231 StartTicks
= WaitFor8254Wraparound();
233 /* Wait for a defined time */
234 KeStallExecutionProcessor(MICROSECOND_TO_WAIT
);
236 /* Get the end ticks */
237 EndTicks
= HalpQuery8254Counter();
239 DPRINT("Pcr->StallScaleFactor: %d\n", Pcr
->StallScaleFactor
);
240 DPRINT("Time2 : StartTicks %i - EndTicks %i = %i\n",
241 StartTicks
, EndTicks
, StartTicks
- EndTicks
);
243 if ((StartTicks
-EndTicks
) > (TICKCOUNT_TO_WAIT
+OverheadTicks
))
245 /* Too big so subtract the CalibrationBit */
246 Pcr
->StallScaleFactor
= Pcr
->StallScaleFactor
- CalibrationBit
;
249 DPRINT("New StallScaleFactor: %d\n", Pcr
->StallScaleFactor
);
253 /* Set StallScaleFactor to the default */
254 Pcr
->StallScaleFactor
= INITIAL_STALL_COUNT
;
261 DPRINT1("About to start delay loop test\n");
262 DPRINT1("Waiting for a minute...");
263 for (i
= 0; i
< (60*1000*20); i
++)
265 KeStallExecutionProcessor(50);
267 DPRINT1("finished\n");
270 DPRINT1("About to start delay loop test\n");
271 DPRINT1("Waiting for a minute...");
272 for (i
= 0; i
< (60*1000); i
++)
274 KeStallExecutionProcessor(1000);
276 DPRINT1("finished\n");
279 DPRINT1("About to start delay loop test\n");
280 DPRINT1("Waiting for a minute...");
281 for (i
= 0; i
< (60*1000*1000); i
++)
283 KeStallExecutionProcessor(1);
285 DPRINT1("finished\n");
287 DPRINT1("About to start delay loop test\n");
288 DPRINT1("Waiting for a minute...");
289 KeStallExecutionProcessor(60*1000000);
290 DPRINT1("finished\n");