ReactOS  0.4.15-dev-2765-g10e48fa
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  * 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 
21 
30 
31 /* PRIVATE FUNCTIONS *********************************************************/
32 
34 ULONG
36 {
37  ULONG TimerValue;
38 
39  /* Send counter latch command for channel 0 */
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
54 {
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 */
87 }
88 
89 CODE_SEG("INIT")
90 VOID
91 NTAPI
93 {
95  USHORT RollOver;
96 
97  DPRINT("HalpInitializeClock()\n");
98 
99 #if defined(SARCH_PC98)
101 #endif
102 
103  /* Get increment and rollover for the largest time clock ms possible */
105  RollOver = (USHORT)HalpRolloverTable[HalpLargestClockMS - 1].RollOver;
106 
107  /* Set the maximum and minimum increment with the kernel */
109 
110  /* Set the rollover value for the timer */
111  HalpSetTimerRollOver(RollOver);
112 
113  /* Save rollover and increment */
114  HalpCurrentRollOver = RollOver;
116 }
117 
118 #ifdef _M_IX86
119 #ifndef _MINIHAL_
120 VOID
121 FASTCALL
123 {
124  ULONG LastIncrement;
125  KIRQL Irql;
126 
127  /* Enter trap */
128  KiEnterInterruptTrap(TrapFrame);
129 
130  /* Start the interrupt */
132  {
133  /* Update the performance counter */
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 */
146 
147  /* Set new timer rollover */
149 
150  /* We're done */
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
165 {
166  KIRQL Irql;
167 
168  /* Enter trap */
169  KiEnterInterruptTrap(TrapFrame);
170 
171  /* Start the interrupt */
173  {
174 #if defined(SARCH_PC98)
175  /* Clear the interrupt flag */
179 #else
180  /* Spin until the interrupt pending bit is clear */
183  NOTHING;
185 #endif
186 
187  /* If profiling is enabled, call the kernel function */
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
213  IN ULONGLONG NewCount)
214 {
216 
217  /* Disable interrupts */
218  Flags = __readeflags();
219  _disable();
220 
221  /* Do a decrement for this CPU */
223 
224  /* Wait for other CPUs */
225  while (*Count);
226 
227  /* Restore interrupts if they were previously enabled */
229 }
230 
231 /*
232  * @implemented
233  */
234 ULONG
235 NTAPI
237 {
238  /* Round increment to ms */
239  Increment /= 10000;
240 
241  /* Normalize between our minimum (1 ms) and maximum (variable) setting */
243  if (Increment <= 0) Increment = 1;
244 
245  /* Set the rate and tell HAL we want to change it */
248 
249  /* Return the increment */
250  return HalpRolloverTable[Increment - 1].Increment;
251 }
252 
254 NTAPI
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 */
269 
270  /* Raise irql to 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 */
312 
313  /* Return the result */
314  return CurrentPerfCounter;
315 }
316 
317 /* 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 PIC_RTC_IRQ
Definition: halhw.h:157
#define IN
Definition: typedefs.h:39
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
VOID FASTCALL HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame)
Definition: rtctimer.c:131
VOID NTAPI KeSetTimeIncrement(IN ULONG MaxIncrement, IN ULONG MinIncrement)
Definition: ntoskrnl.c:35
#define TRUE
Definition: types.h:120
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
VOID NTAPI HalpInitializeClockPc98(VOID)
Definition: clock.c:62
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
uint32_t ULONG_PTR
Definition: typedefs.h:65
UCHAR KIRQL
Definition: env_spec_w32.h:591
__INTRIN_INLINE uintptr_t __readeflags(void)
Definition: intrin_x86.h:1675
ULONG HalpNextMSRate
Definition: timer.c:20
NTSTATUS(* NTAPI)(IN PFILE_FULL_EA_INFORMATION EaBuffer, IN ULONG EaLength, OUT PULONG ErrorOffset)
Definition: IoEaTest.cpp:117
#define FALSE
Definition: types.h:117
VOID NTAPI HalpReleaseCmosSpinLock(VOID)
Definition: spinlock.c:244
ULONG HalpPerfCounterCutoff
Definition: timer.c:24
unsigned char BOOLEAN
long __cdecl _InterlockedDecrement(_Interlocked_operand_ long volatile *_Addend)
VOID FASTCALL HalpProfileInterruptHandler(_In_ PKTRAP_FRAME TrapFrame)
Definition: apictimer.c:66
#define TIMER_CONTROL_PORT
Definition: halp.h:22
UCHAR FORCEINLINE HalpReadCmos(IN UCHAR Reg)
Definition: cmos.c:24
LARGE_INTEGER HalpRolloverTable[15]
Definition: timer.c:23
KIRQL FASTCALL KfRaiseIrql(IN KIRQL NewIrql)
Definition: pic.c:187
int Count
Definition: noreturn.cpp:7
_Must_inspect_result_ _In_ ULONG Flags
Definition: wsk.h:170
uint64_t ULONGLONG
Definition: typedefs.h:67
#define CLOCK2_LEVEL
Definition: env_spec_w32.h:700
#define RTC_IO_i_INTERRUPT_RESET
Definition: rtc.h:44
#define TIMER_CHANNEL0_DATA_PORT
Definition: halhw.h:67
VOID NTAPI HalpAcquireCmosSpinLock(VOID)
Definition: spinlock.c:227
VOID HalpInitializeClock(VOID)
Definition: timer.c:54
ULONG HalpCurrentRollOver
Definition: timer.c:19
#define PIT_LATCH
Definition: timer.c:18
_Requires_lock_held_ Interrupt _Releases_lock_ Interrupt _In_ _IRQL_restores_ KIRQL OldIrql
Definition: kefuncs.h:790
__INTRIN_INLINE void __writeeflags(uintptr_t Value)
Definition: intrin_x86.h:1670
#define VOID
Definition: acefi.h:82
#define NOTHING
Definition: env_spec_w32.h:461
#define RTC_REGISTER_C
Definition: halhw.h:17
#define DISPATCH_LEVEL
Definition: env_spec_w32.h:696
void __nop(void)
Definition: intrin_x86.h:2043
VOID FASTCALL KfLowerIrql(IN KIRQL NewIrql)
Definition: pic.c:232
ULONG PIT_FREQUENCY
Definition: clock.c:17
FORCEINLINE VOID KiEnterInterruptTrap(IN PKTRAP_FRAME TrapFrame)
Definition: trap_x.h:368
LARGE_INTEGER HalpPerfCounter
Definition: timer.c:23
#define PIC_TIMER_IRQ
Definition: halhw.h:155
unsigned short USHORT
Definition: pedump.c:61
#define PROFILE_LEVEL
Definition: env_spec_w32.h:698
ULONG HalpCurrentTimeIncrement
Definition: timer.c:25
#define FORCEINLINE
Definition: wdftypes.h:67
LARGE_INTEGER HalpLastPerfCounter
Definition: timer.c:22
void __cdecl _disable(void)
Definition: intrin_arm.h:365
ULONG HalpLargestClockMS
Definition: timer.c:21
unsigned int ULONG
Definition: retypes.h:1
#define RTC_REG_C_IRQ
Definition: halhw.h:18
VOID NTAPI HalpSetTimerRollOver(USHORT RollOver)
Definition: timer.c:53
FORCEINLINE ULONG HalpRead8254Value(void)
Definition: timer.c:35
#define DPRINT
Definition: sndvol32.h:71
IN OUT PLONG IN OUT PLONG Addend IN OUT PLONG IN LONG Increment
Definition: CrNtStubs.h:42
signed int * PLONG
Definition: retypes.h:5
static CODE_SEG("PAGE")
Definition: isapnp.c:1482
ULONG NTAPI HalSetTimeIncrement(IN ULONG Increment)
Definition: timer.c:102
LONGLONG QuadPart
Definition: typedefs.h:114
#define EFLAGS_INTERRUPT_MASK
Definition: ketypes.h:126
PPC_QUAL unsigned char __inbyte(const unsigned long Port)
Definition: intrin_ppc.h:539