ReactOS  0.4.13-dev-235-g7373cb3
timer.c
Go to the documentation of this file.
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  * Timo Kreuzer (timo.kreuzer@reactos.org)
8  */
9 
10 /* INCLUDES ******************************************************************/
11 
12 #include <hal.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 #if defined(ALLOC_PRAGMA) && !defined(_MINIHAL_)
17 #pragma alloc_text(INIT, HalpInitializeClock)
18 #endif
19 
20 /* GLOBALS *******************************************************************/
21 
22 #define PIT_LATCH 0x00
23 
32 
33 static struct _HALP_ROLLOVER
34 {
37 } HalpRolloverTable[15] =
38 {
39  {1197, 10032},
40  {2394, 20064},
41  {3591, 30096},
42  {4767, 39952},
43  {5964, 49984},
44  {7161, 60016},
45  {8358, 70048},
46  {9555, 80080},
47  {10731, 89936},
48  {11949, 100144},
49  {13125, 110000},
50  {14322, 120032},
51  {15519, 130064},
52  {16695, 139920},
53  {17892, 149952}
54 };
55 
56 /* PRIVATE FUNCTIONS *********************************************************/
57 
59 ULONG
61 {
62  ULONG TimerValue;
63 
64  /* Send counter latch command for channel 0 */
66  __nop();
67 
68  /* Read the value, LSB first */
69  TimerValue = __inbyte(TIMER_CHANNEL0_DATA_PORT);
70  __nop();
71  TimerValue |= __inbyte(TIMER_CHANNEL0_DATA_PORT) << 8;
72 
73  return TimerValue;
74 }
75 
76 VOID
77 NTAPI
79 {
81  TIMER_CONTROL_PORT_REGISTER TimerControl;
82 
83  /* Disable interrupts */
84  Flags = __readeflags();
85  _disable();
86 
87  /* Program the PIT for binary mode */
88  TimerControl.BcdMode = FALSE;
89 
90  /*
91  * Program the PIT to generate a normal rate wave (Mode 3) on channel 0.
92  * Channel 0 is used for the IRQ0 clock interval timer, and channel
93  * 1 is used for DRAM refresh.
94  *
95  * Mode 2 gives much better accuracy than Mode 3.
96  */
97  TimerControl.OperatingMode = PitOperatingMode2;
98  TimerControl.Channel = PitChannel0;
99 
100  /* Set the access mode that we'll use to program the reload value */
101  TimerControl.AccessMode = PitAccessModeLowHigh;
102 
103  /* Now write the programming bits */
104  __outbyte(TIMER_CONTROL_PORT, TimerControl.Bits);
105 
106  /* Next we write the reload value for channel 0 */
107  __outbyte(TIMER_CHANNEL0_DATA_PORT, RollOver & 0xFF);
108  __outbyte(TIMER_CHANNEL0_DATA_PORT, RollOver >> 8);
109 
110  /* Restore interrupts if they were previously enabled */
112 }
113 
114 INIT_FUNCTION
115 VOID
116 NTAPI
118 {
120  USHORT RollOver;
121 
122  DPRINT("HalpInitializeClock()\n");
123 
124  /* Get increment and rollover for the largest time clock ms possible */
126  RollOver = (USHORT)HalpRolloverTable[HalpLargestClockMS - 1].RollOver;
127 
128  /* Set the maximum and minimum increment with the kernel */
130 
131  /* Set the rollover value for the timer */
132  HalpSetTimerRollOver(RollOver);
133 
134  /* Save rollover and increment */
135  HalpCurrentRollOver = RollOver;
137 }
138 
139 #ifdef _M_IX86
140 #ifndef _MINIHAL_
141 VOID
142 FASTCALL
144 {
145  ULONG LastIncrement;
146  KIRQL Irql;
147 
148  /* Enter trap */
149  KiEnterInterruptTrap(TrapFrame);
150 
151  /* Start the interrupt */
153  {
154  /* Update the performance counter */
157 
158  /* Save increment */
159  LastIncrement = HalpCurrentTimeIncrement;
160 
161  /* Check if someone changed the time rate */
162  if (HalpClockSetMSRate)
163  {
164  /* Update the global values */
167 
168  /* Set new timer rollover */
170 
171  /* We're done */
173  }
174 
175  /* Update the system time -- the kernel will exit this trap */
176  KeUpdateSystemTime(TrapFrame, LastIncrement, Irql);
177  }
178 
179  /* Spurious, just end the interrupt */
180  KiEoiHelper(TrapFrame);
181 }
182 
183 VOID
184 FASTCALL
186 {
187  KIRQL Irql;
188 
189  /* Enter trap */
190  KiEnterInterruptTrap(TrapFrame);
191 
192  /* Start the interrupt */
194  {
195  /* Spin until the interrupt pending bit is clear */
198  ;
200 
201  /* If profiling is enabled, call the kernel function */
203  {
204  KeProfileInterrupt(TrapFrame);
205  }
206 
207  /* Finish the interrupt */
208  _disable();
209  HalEndSystemInterrupt(Irql, TrapFrame);
210  }
211 
212  /* Spurious, just end the interrupt */
213  KiEoiHelper(TrapFrame);
214 }
215 #endif
216 
217 #endif
218 
219 /* PUBLIC FUNCTIONS ***********************************************************/
220 
221 /*
222  * @implemented
223  */
224 VOID
225 NTAPI
227  IN ULONGLONG NewCount)
228 {
230 
231  /* Disable interrupts */
232  Flags = __readeflags();
233  _disable();
234 
235  /* Do a decrement for this CPU */
237 
238  /* Wait for other CPUs */
239  while (*Count);
240 
241  /* Restore interrupts if they were previously enabled */
243 }
244 
245 /*
246  * @implemented
247  */
248 ULONG
249 NTAPI
251 {
252  /* Round increment to ms */
253  Increment /= 10000;
254 
255  /* Normalize between our minimum (1 ms) and maximum (variable) setting */
257  if (Increment <= 0) Increment = 1;
258 
259  /* Set the rate and tell HAL we want to change it */
262 
263  /* Return the increment */
264  return HalpRolloverTable[Increment - 1].Increment;
265 }
266 
268 NTAPI
270 {
271  LARGE_INTEGER CurrentPerfCounter;
272  ULONG CounterValue, ClockDelta;
273  KIRQL OldIrql;
274 
275  /* If caller wants performance frequency, return hardcoded value */
276  if (PerformanceFrequency) PerformanceFrequency->QuadPart = PIT_FREQUENCY;
277 
278  /* Check if we were called too early */
279  if (HalpCurrentRollOver == 0) return HalpPerfCounter;
280 
281  /* Check if interrupts are disabled */
283 
284  /* Raise irql to DISPATCH_LEVEL */
287 
288  do
289  {
290  /* Get the current performance counter value */
291  CurrentPerfCounter = HalpPerfCounter;
292 
293  /* Read the 8254 counter value */
294  CounterValue = HalpRead8254Value();
295 
296  /* Repeat if the value has changed (a clock interrupt happened) */
297  } while (CurrentPerfCounter.QuadPart != HalpPerfCounter.QuadPart);
298 
299  /* After someone changed the clock rate, during the first clock cycle we
300  might see a counter value larger than the rollover. In this case we
301  pretend it already has the new rollover value. */
302  if (CounterValue > HalpCurrentRollOver) CounterValue = HalpCurrentRollOver;
303 
304  /* The interrupt is issued on the falling edge of the OUT line, when the
305  counter changes from 1 to max. Calculate a clock delta, so that directly
306  after the interrupt it is 0, going up to (HalpCurrentRollOver - 1). */
307  ClockDelta = HalpCurrentRollOver - CounterValue;
308 
309  /* Add the clock delta */
310  CurrentPerfCounter.QuadPart += ClockDelta;
311 
312  /* Check if the value is smaller then before, this means, we somehow
313  missed an interrupt. This is a sign that the timer interrupt
314  is very inaccurate. Probably a virtual machine. */
315  if (CurrentPerfCounter.QuadPart < HalpLastPerfCounter.QuadPart)
316  {
317  /* We missed an interrupt. Assume we will receive it later */
318  CurrentPerfCounter.QuadPart += HalpCurrentRollOver;
319  }
320 
321  /* Update the last counter value */
322  HalpLastPerfCounter = CurrentPerfCounter;
323 
324  /* Restore previous irql */
326 
327  /* Return the result */
328  return CurrentPerfCounter;
329 }
330 
331 /* EOF */
#define KeGetCurrentIrql()
Definition: env_spec_w32.h:706
VOID NTAPI HalCalibratePerformanceCounter(IN volatile PLONG Count, IN ULONGLONG NewCount)
Definition: timer.c:90
BOOLEAN HalpProfilingStopped
Definition: profil.c:18
#define IN
Definition: typedefs.h:38
VOID NTAPI HalEndSystemInterrupt(IN KIRQL OldIrql, IN PKTRAP_FRAME TrapFrame)
Definition: pic.c:335
LARGE_INTEGER NTAPI KeQueryPerformanceCounter(IN PLARGE_INTEGER PerformanceFreq)
Definition: timer.c:138
#define TRUE
Definition: types.h:120
VOID FASTCALL HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame)
Definition: rtctimer.c:107
VOID NTAPI KeSetTimeIncrement(IN ULONG MaxIncrement, IN ULONG MinIncrement)
Definition: ntoskrnl.c:46
VOID FASTCALL KeUpdateSystemTime(IN PKTRAP_FRAME TrapFrame, IN ULONG Increment, IN KIRQL OldIrql)
Definition: time.c:64
#define PRIMARY_VECTOR_BASE
Definition: halp.h:16
_Inout_ __drv_aliasesMem PSLIST_ENTRY _Inout_ PSLIST_ENTRY _In_ ULONG Count
Definition: exfuncs.h:1015
BOOLEAN NTAPI HalBeginSystemInterrupt(IN KIRQL Irql, IN ULONG Vector, OUT PKIRQL OldIrql)
Definition: pic.c:321
VOID NTAPI KeProfileInterrupt(IN PKTRAP_FRAME TrapFrame)
Definition: profobj.c:296
BOOLEAN HalpClockSetMSRate
Definition: timer.c:17
PPC_QUAL void __outbyte(unsigned long const Port, const unsigned char Data)
Definition: intrin_ppc.h:605
#define FASTCALL
Definition: nt_native.h:50
_Out_ PKIRQL Irql
Definition: csq.h:179
BOOLEAN KiEnableTimerWatchdog
Definition: timerobj.c:20
DECLSPEC_NORETURN VOID FASTCALL KiEoiHelper(IN PKTRAP_FRAME TrapFrame)
Definition: traphdlr.c:126
ULONG Increment
Definition: timer.c:36
uint32_t ULONG_PTR
Definition: typedefs.h:63
UCHAR KIRQL
Definition: env_spec_w32.h:591
_Must_inspect_result_ _In_ ULONG Flags
Definition: wsk.h:170
__INTRIN_INLINE uintptr_t __readeflags(void)
Definition: intrin_x86.h:1555
#define PIT_FREQUENCY
Definition: halp.h:106
ULONG HalpNextMSRate
Definition: timer.c:20
NTSTATUS(* NTAPI)(IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, OUT PULONG ErrorOffset)
Definition: IoEaTest.cpp:117
VOID NTAPI HalpReleaseCmosSpinLock(VOID)
Definition: spinlock.c:244
ULONG HalpPerfCounterCutoff
Definition: timer.c:26
VOID FASTCALL HalpProfileInterruptHandler(IN PKTRAP_FRAME TrapFrame)
Definition: rtctimer.c:148
unsigned char BOOLEAN
long __cdecl _InterlockedDecrement(_Interlocked_operand_ long volatile *_Addend)
#define TIMER_CONTROL_PORT
Definition: halp.h:22
#define FORCEINLINE
Definition: ntbasedef.h:221
UCHAR FORCEINLINE HalpReadCmos(IN UCHAR Reg)
Definition: cmos.c:24
void DPRINT(...)
Definition: polytest.cpp:61
#define TIMER_CHANNEL0_DATA_PORT
Definition: halp.h:111
LARGE_INTEGER HalpRolloverTable[15]
Definition: timer.c:23
KIRQL FASTCALL KfRaiseIrql(IN KIRQL NewIrql)
Definition: pic.c:187
uint64_t ULONGLONG
Definition: typedefs.h:65
#define CLOCK2_LEVEL
Definition: env_spec_w32.h:700
VOID NTAPI HalpAcquireCmosSpinLock(VOID)
Definition: spinlock.c:227
VOID HalpInitializeClock(VOID)
Definition: timer.c:54
ULONG RollOver
Definition: timer.c:35
ULONG HalpCurrentRollOver
Definition: timer.c:19
#define PIT_LATCH
Definition: timer.c:22
_Requires_lock_held_ Interrupt _Releases_lock_ Interrupt _In_ _IRQL_restores_ KIRQL OldIrql
Definition: kefuncs.h:803
__INTRIN_INLINE void __writeeflags(uintptr_t Value)
Definition: intrin_x86.h:1550
#define DISPATCH_LEVEL
Definition: env_spec_w32.h:696
#define RTC_REGISTER_C
Definition: halp.h:56
void __nop(void)
Definition: intrin_x86.h:1939
VOID FASTCALL KfLowerIrql(IN KIRQL NewIrql)
Definition: pic.c:232
FORCEINLINE VOID KiEnterInterruptTrap(IN PKTRAP_FRAME TrapFrame)
Definition: trap_x.h:369
LARGE_INTEGER HalpPerfCounter
Definition: timer.c:25
unsigned short USHORT
Definition: pedump.c:61
#define PROFILE_LEVEL
Definition: env_spec_w32.h:698
ULONG HalpCurrentTimeIncrement
Definition: timer.c:25
#define RTC_REG_C_IRQ
Definition: halp.h:57
LARGE_INTEGER HalpLastPerfCounter
Definition: timer.c:24
void __cdecl _disable(void)
Definition: intrin_arm.h:365
ULONG HalpLargestClockMS
Definition: timer.c:21
unsigned int ULONG
Definition: retypes.h:1
VOID NTAPI HalpSetTimerRollOver(USHORT RollOver)
Definition: timer.c:78
FORCEINLINE ULONG HalpRead8254Value(void)
Definition: timer.c:60
IN OUT PLONG IN OUT PLONG Addend IN OUT PLONG IN LONG Increment
Definition: CrNtStubs.h:42
signed int * PLONG
Definition: retypes.h:5
ULONG NTAPI HalSetTimeIncrement(IN ULONG Increment)
Definition: timer.c:102
LONGLONG QuadPart
Definition: typedefs.h:112
#define EFLAGS_INTERRUPT_MASK
Definition: ketypes.h:126
PPC_QUAL unsigned char __inbyte(const unsigned long Port)
Definition: intrin_ppc.h:539