[REACTOS] Cleanup INIT and some PAGE section allocations
[reactos.git] / hal / halx86 / generic / timer.c
1 /*
2 * PROJECT: ReactOS HAL
3 * LICENSE: GPL - See COPYING in the top level directory
4 * PURPOSE: HAL Timer Routines
5 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
6 * Timo Kreuzer (timo.kreuzer@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <hal.h>
12
13 #define NDEBUG
14 #include <debug.h>
15
16 /* GLOBALS *******************************************************************/
17
18 #define PIT_LATCH 0x00
19
20 extern HALP_ROLLOVER HalpRolloverTable[15];
21
22 LARGE_INTEGER HalpLastPerfCounter;
23 LARGE_INTEGER HalpPerfCounter;
24 ULONG HalpPerfCounterCutoff;
25 BOOLEAN HalpClockSetMSRate;
26 ULONG HalpCurrentTimeIncrement;
27 ULONG HalpCurrentRollOver;
28 ULONG HalpNextMSRate = 14;
29 ULONG HalpLargestClockMS = 15;
30
31 /* PRIVATE FUNCTIONS *********************************************************/
32
33 FORCEINLINE
34 ULONG
35 HalpRead8254Value(void)
36 {
37 ULONG TimerValue;
38
39 /* Send counter latch command for channel 0 */
40 __outbyte(TIMER_CONTROL_PORT, PIT_LATCH);
41 __nop();
42
43 /* Read the value, LSB first */
44 TimerValue = __inbyte(TIMER_CHANNEL0_DATA_PORT);
45 __nop();
46 TimerValue |= __inbyte(TIMER_CHANNEL0_DATA_PORT) << 8;
47
48 return TimerValue;
49 }
50
51 VOID
52 NTAPI
53 HalpSetTimerRollOver(USHORT RollOver)
54 {
55 ULONG_PTR Flags;
56 TIMER_CONTROL_PORT_REGISTER TimerControl;
57
58 /* Disable interrupts */
59 Flags = __readeflags();
60 _disable();
61
62 /* Program the PIT for binary mode */
63 TimerControl.BcdMode = FALSE;
64
65 /*
66 * Program the PIT to generate a normal rate wave (Mode 2) on channel 0.
67 * Channel 0 is used for the IRQ0 clock interval timer, and channel
68 * 1 is used for DRAM refresh.
69 *
70 * Mode 2 gives much better accuracy than Mode 3.
71 */
72 TimerControl.OperatingMode = PitOperatingMode2;
73 TimerControl.Channel = PitChannel0;
74
75 /* Set the access mode that we'll use to program the reload value */
76 TimerControl.AccessMode = PitAccessModeLowHigh;
77
78 /* Now write the programming bits */
79 __outbyte(TIMER_CONTROL_PORT, TimerControl.Bits);
80
81 /* Next we write the reload value for channel 0 */
82 __outbyte(TIMER_CHANNEL0_DATA_PORT, RollOver & 0xFF);
83 __outbyte(TIMER_CHANNEL0_DATA_PORT, RollOver >> 8);
84
85 /* Restore interrupts if they were previously enabled */
86 __writeeflags(Flags);
87 }
88
89 CODE_SEG("INIT")
90 VOID
91 NTAPI
92 HalpInitializeClock(VOID)
93 {
94 ULONG Increment;
95 USHORT RollOver;
96
97 DPRINT("HalpInitializeClock()\n");
98
99 #if defined(SARCH_PC98)
100 HalpInitializeClockPc98();
101 #endif
102
103 /* Get increment and rollover for the largest time clock ms possible */
104 Increment = HalpRolloverTable[HalpLargestClockMS - 1].Increment;
105 RollOver = (USHORT)HalpRolloverTable[HalpLargestClockMS - 1].RollOver;
106
107 /* Set the maximum and minimum increment with the kernel */
108 KeSetTimeIncrement(Increment, HalpRolloverTable[0].Increment);
109
110 /* Set the rollover value for the timer */
111 HalpSetTimerRollOver(RollOver);
112
113 /* Save rollover and increment */
114 HalpCurrentRollOver = RollOver;
115 HalpCurrentTimeIncrement = Increment;
116 }
117
118 #ifdef _M_IX86
119 #ifndef _MINIHAL_
120 VOID
121 FASTCALL
122 HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame)
123 {
124 ULONG LastIncrement;
125 KIRQL Irql;
126
127 /* Enter trap */
128 KiEnterInterruptTrap(TrapFrame);
129
130 /* Start the interrupt */
131 if (HalBeginSystemInterrupt(CLOCK2_LEVEL, PRIMARY_VECTOR_BASE + PIC_TIMER_IRQ, &Irql))
132 {
133 /* Update the performance counter */
134 HalpPerfCounter.QuadPart += HalpCurrentRollOver;
135 HalpPerfCounterCutoff = KiEnableTimerWatchdog;
136
137 /* Save increment */
138 LastIncrement = HalpCurrentTimeIncrement;
139
140 /* Check if someone changed the time rate */
141 if (HalpClockSetMSRate)
142 {
143 /* Update the global values */
144 HalpCurrentTimeIncrement = HalpRolloverTable[HalpNextMSRate - 1].Increment;
145 HalpCurrentRollOver = HalpRolloverTable[HalpNextMSRate - 1].RollOver;
146
147 /* Set new timer rollover */
148 HalpSetTimerRollOver((USHORT)HalpCurrentRollOver);
149
150 /* We're done */
151 HalpClockSetMSRate = FALSE;
152 }
153
154 /* Update the system time -- the kernel will exit this trap */
155 KeUpdateSystemTime(TrapFrame, LastIncrement, Irql);
156 }
157
158 /* Spurious, just end the interrupt */
159 KiEoiHelper(TrapFrame);
160 }
161
162 VOID
163 FASTCALL
164 HalpProfileInterruptHandler(IN PKTRAP_FRAME TrapFrame)
165 {
166 KIRQL Irql;
167
168 /* Enter trap */
169 KiEnterInterruptTrap(TrapFrame);
170
171 /* Start the interrupt */
172 if (HalBeginSystemInterrupt(PROFILE_LEVEL, PRIMARY_VECTOR_BASE + PIC_RTC_IRQ, &Irql))
173 {
174 #if defined(SARCH_PC98)
175 /* Clear the interrupt flag */
176 HalpAcquireCmosSpinLock();
177 (VOID)__inbyte(RTC_IO_i_INTERRUPT_RESET);
178 HalpReleaseCmosSpinLock();
179 #else
180 /* Spin until the interrupt pending bit is clear */
181 HalpAcquireCmosSpinLock();
182 while (HalpReadCmos(RTC_REGISTER_C) & RTC_REG_C_IRQ)
183 NOTHING;
184 HalpReleaseCmosSpinLock();
185 #endif
186
187 /* If profiling is enabled, call the kernel function */
188 if (!HalpProfilingStopped)
189 {
190 KeProfileInterrupt(TrapFrame);
191 }
192
193 /* Finish the interrupt */
194 _disable();
195 HalEndSystemInterrupt(Irql, TrapFrame);
196 }
197
198 /* Spurious, just end the interrupt */
199 KiEoiHelper(TrapFrame);
200 }
201 #endif /* !_MINIHAL_ */
202
203 #endif /* _M_IX86 */
204
205 /* PUBLIC FUNCTIONS ***********************************************************/
206
207 /*
208 * @implemented
209 */
210 VOID
211 NTAPI
212 HalCalibratePerformanceCounter(IN volatile PLONG Count,
213 IN ULONGLONG NewCount)
214 {
215 ULONG_PTR Flags;
216
217 /* Disable interrupts */
218 Flags = __readeflags();
219 _disable();
220
221 /* Do a decrement for this CPU */
222 _InterlockedDecrement(Count);
223
224 /* Wait for other CPUs */
225 while (*Count);
226
227 /* Restore interrupts if they were previously enabled */
228 __writeeflags(Flags);
229 }
230
231 /*
232 * @implemented
233 */
234 ULONG
235 NTAPI
236 HalSetTimeIncrement(IN ULONG Increment)
237 {
238 /* Round increment to ms */
239 Increment /= 10000;
240
241 /* Normalize between our minimum (1 ms) and maximum (variable) setting */
242 if (Increment > HalpLargestClockMS) Increment = HalpLargestClockMS;
243 if (Increment <= 0) Increment = 1;
244
245 /* Set the rate and tell HAL we want to change it */
246 HalpNextMSRate = Increment;
247 HalpClockSetMSRate = TRUE;
248
249 /* Return the increment */
250 return HalpRolloverTable[Increment - 1].Increment;
251 }
252
253 LARGE_INTEGER
254 NTAPI
255 KeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFrequency)
256 {
257 LARGE_INTEGER CurrentPerfCounter;
258 ULONG CounterValue, ClockDelta;
259 KIRQL OldIrql;
260
261 /* If caller wants performance frequency, return hardcoded value */
262 if (PerformanceFrequency) PerformanceFrequency->QuadPart = PIT_FREQUENCY;
263
264 /* Check if we were called too early */
265 if (HalpCurrentRollOver == 0) return HalpPerfCounter;
266
267 /* Check if interrupts are disabled */
268 if(!(__readeflags() & EFLAGS_INTERRUPT_MASK)) return HalpPerfCounter;
269
270 /* Raise irql to DISPATCH_LEVEL */
271 OldIrql = KeGetCurrentIrql();
272 if (OldIrql < DISPATCH_LEVEL) KfRaiseIrql(DISPATCH_LEVEL);
273
274 do
275 {
276 /* Get the current performance counter value */
277 CurrentPerfCounter = HalpPerfCounter;
278
279 /* Read the 8254 counter value */
280 CounterValue = HalpRead8254Value();
281
282 /* Repeat if the value has changed (a clock interrupt happened) */
283 } while (CurrentPerfCounter.QuadPart != HalpPerfCounter.QuadPart);
284
285 /* After someone changed the clock rate, during the first clock cycle we
286 might see a counter value larger than the rollover. In this case we
287 pretend it already has the new rollover value. */
288 if (CounterValue > HalpCurrentRollOver) CounterValue = HalpCurrentRollOver;
289
290 /* The interrupt is issued on the falling edge of the OUT line, when the
291 counter changes from 1 to max. Calculate a clock delta, so that directly
292 after the interrupt it is 0, going up to (HalpCurrentRollOver - 1). */
293 ClockDelta = HalpCurrentRollOver - CounterValue;
294
295 /* Add the clock delta */
296 CurrentPerfCounter.QuadPart += ClockDelta;
297
298 /* Check if the value is smaller then before, this means, we somehow
299 missed an interrupt. This is a sign that the timer interrupt
300 is very inaccurate. Probably a virtual machine. */
301 if (CurrentPerfCounter.QuadPart < HalpLastPerfCounter.QuadPart)
302 {
303 /* We missed an interrupt. Assume we will receive it later */
304 CurrentPerfCounter.QuadPart += HalpCurrentRollOver;
305 }
306
307 /* Update the last counter value */
308 HalpLastPerfCounter = CurrentPerfCounter;
309
310 /* Restore previous irql */
311 if (OldIrql < DISPATCH_LEVEL) KfLowerIrql(OldIrql);
312
313 /* Return the result */
314 return CurrentPerfCounter;
315 }
316
317 /* EOF */