Reactos
at master 317 lines 8.9 kB view raw
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 20extern HALP_ROLLOVER HalpRolloverTable[15]; 21 22LARGE_INTEGER HalpLastPerfCounter; 23LARGE_INTEGER HalpPerfCounter; 24ULONG HalpPerfCounterCutoff; 25BOOLEAN HalpClockSetMSRate; 26ULONG HalpCurrentTimeIncrement; 27ULONG HalpCurrentRollOver; 28ULONG HalpNextMSRate = 14; 29ULONG HalpLargestClockMS = 15; 30 31/* PRIVATE FUNCTIONS *********************************************************/ 32 33FORCEINLINE 34ULONG 35HalpRead8254Value(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 51VOID 52NTAPI 53HalpSetTimerRollOver(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 89CODE_SEG("INIT") 90VOID 91NTAPI 92HalpInitializeClock(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_ 120VOID 121FASTCALL 122HalpClockInterruptHandler(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 162VOID 163FASTCALL 164HalpProfileInterruptHandler(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 */ 210VOID 211NTAPI 212HalCalibratePerformanceCounter(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 */ 234ULONG 235NTAPI 236HalSetTimeIncrement(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 253LARGE_INTEGER 254NTAPI 255KeQueryPerformanceCounter(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 */