Reactos
at master 251 lines 7.1 kB view raw
1/* 2 * PROJECT: ReactOS HAL 3 * LICENSE: GNU GPL - See COPYING in the top level directory 4 * FILE: hal/halx86/apic/rtctimer.c 5 * PURPOSE: HAL APIC Management and Control Code 6 * PROGRAMMERS: Timo Kreuzer (timo.kreuzer@reactos.org) 7 * REFERENCES: https://wiki.osdev.org/RTC 8 * https://forum.osdev.org/viewtopic.php?f=13&t=20825&start=0 9 * https://web.archive.org/web/20240119203005/http://www.bioscentral.com/misc/cmosmap.htm 10 */ 11 12/* INCLUDES *******************************************************************/ 13 14#include <hal.h> 15#include "apicp.h" 16#include <smp.h> 17#define NDEBUG 18#include <debug.h> 19 20/* GLOBALS ********************************************************************/ 21 22static const UCHAR RtcMinimumClockRate = 6; /* Minimum rate 6: 1024 Hz / 0.97 ms */ 23static const UCHAR RtcMaximumClockRate = 10; /* Maximum rate 10: 64 Hz / 15.6 ms */ 24static UCHAR HalpCurrentClockRate = 10; /* Initial rate 10: 64 Hz / 15.6 ms */ 25static ULONG HalpCurrentTimeIncrement; 26static ULONG HalpMinimumTimeIncrement; 27static ULONG HalpMaximumTimeIncrement; 28static ULONG HalpCurrentFractionalIncrement; 29static ULONG HalpRunningFraction; 30static BOOLEAN HalpSetClockRate; 31static UCHAR HalpNextClockRate; 32 33/*! 34 \brief Converts the CMOS RTC rate into the time increment in 0.1ns intervals. 35 36 Rate Frequency Interval (ms) Precise increment (0.1ns) 37 ------------------------------------------------------ 38 0 disabled 39 1 32768 0.03052 305,175 40 2 16384 0.06103 610,351 41 3 8192 0.12207 1,220,703 42 4 4096 0.24414 2,441,406 43 5 2048 0.48828 4,882,812 44 6 1024 0.97656 9,765,625 <- minimum 45 7 512 1.95313 19,531,250 46 8 256 3.90625 39,062,500 47 9 128 7.8125 78,125,000 48 10 64 15.6250 156,250,000 <- maximum / default 49 11 32 31.25 312,500,000 50 12 16 62.5 625,000,000 51 13 8 125 1,250,000,000 52 14 4 250 2,500,000,000 53 15 2 500 5,000,000,000 54 55*/ 56FORCEINLINE 57ULONG 58RtcClockRateToPreciseIncrement(UCHAR Rate) 59{ 60 /* Calculate frequency */ 61 ULONG Frequency = 32768 >> (Rate - 1); 62 63 /* Calculate interval in 0.1ns interval: Interval = (1 / Frequency) * 10,000,000,000 */ 64 return 10000000000ULL / Frequency; 65} 66 67VOID 68RtcSetClockRate(UCHAR ClockRate) 69{ 70 UCHAR RegisterA; 71 ULONG PreciseIncrement; 72 73 /* Update the global values */ 74 HalpCurrentClockRate = ClockRate; 75 PreciseIncrement = RtcClockRateToPreciseIncrement(ClockRate); 76 HalpCurrentTimeIncrement = PreciseIncrement / 1000; 77 HalpCurrentFractionalIncrement = PreciseIncrement % 1000; 78 79 /* Acquire CMOS lock */ 80 HalpAcquireCmosSpinLock(); 81 82 // TODO: disable NMI 83 84 /* Read value of register A */ 85 RegisterA = HalpReadCmos(RTC_REGISTER_A); 86 87 /* Change lower 4 bits to new rate */ 88 RegisterA &= 0xF0; 89 RegisterA |= ClockRate; 90 91 /* Write the new value */ 92 HalpWriteCmos(RTC_REGISTER_A, RegisterA); 93 94 /* Release CMOS lock */ 95 HalpReleaseCmosSpinLock(); 96} 97 98CODE_SEG("INIT") 99VOID 100NTAPI 101HalpInitializeClock(VOID) 102{ 103 ULONG_PTR EFlags; 104 UCHAR RegisterB; 105 106 /* Save EFlags and disable interrupts */ 107 EFlags = __readeflags(); 108 _disable(); 109 110 // TODO: disable NMI 111 112 /* Acquire CMOS lock */ 113 HalpAcquireCmosSpinLock(); 114 115 /* Enable the periodic interrupt in the CMOS */ 116 RegisterB = HalpReadCmos(RTC_REGISTER_B); 117 HalpWriteCmos(RTC_REGISTER_B, RegisterB | RTC_REG_B_PI); 118 119 /* Release CMOS lock */ 120 HalpReleaseCmosSpinLock(); 121 122 /* Set initial rate */ 123 RtcSetClockRate(HalpCurrentClockRate); 124 125 /* Restore interrupt state */ 126 __writeeflags(EFlags); 127 128 /* Calculate minumum and maximum increment */ 129 HalpMinimumTimeIncrement = RtcClockRateToPreciseIncrement(RtcMinimumClockRate) / 1000; 130 HalpMaximumTimeIncrement = RtcClockRateToPreciseIncrement(RtcMaximumClockRate) / 1000; 131 132 /* Notify the kernel about the maximum and minimum increment */ 133 KeSetTimeIncrement(HalpMaximumTimeIncrement, HalpMinimumTimeIncrement); 134 135 /* Enable the timer interrupt */ 136 HalEnableSystemInterrupt(APIC_CLOCK_VECTOR, CLOCK_LEVEL, Latched); 137 138 DPRINT1("Clock initialized\n"); 139} 140 141VOID 142FASTCALL 143HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame) 144{ 145 ULONG LastIncrement; 146 KIRQL Irql; 147 148 /* Enter trap */ 149 KiEnterInterruptTrap(TrapFrame); 150#ifdef _M_AMD64 151 /* This is for debugging */ 152 TrapFrame->ErrorCode = 0xc10c4; 153#endif 154 155 /* Start the interrupt */ 156 if (!HalBeginSystemInterrupt(CLOCK_LEVEL, APIC_CLOCK_VECTOR, &Irql)) 157 { 158 /* Spurious, just end the interrupt */ 159#ifdef _M_IX86 160 KiEoiHelper(TrapFrame); 161#endif 162 return; 163 } 164 165 /* Read register C, so that the next interrupt can happen */ 166 HalpReadCmos(RTC_REGISTER_C); 167 168 /* Save increment */ 169 LastIncrement = HalpCurrentTimeIncrement; 170 171 /* Check if the running fraction has accounted for 100 ns */ 172 HalpRunningFraction += HalpCurrentFractionalIncrement; 173 if (HalpRunningFraction >= 1000) 174 { 175 LastIncrement++; 176 HalpRunningFraction -= 1000; 177 } 178 179 /* Check if someone changed the time rate */ 180 if (HalpSetClockRate) 181 { 182 /* Set new clock rate */ 183 RtcSetClockRate(HalpNextClockRate); 184 185 /* We're done */ 186 HalpSetClockRate = FALSE; 187 } 188 189 /* Send the clock IPI to all other CPUs */ 190 HalpBroadcastClockIpi(CLOCK_IPI_VECTOR); 191 192 /* Update the system time -- on x86 the kernel will exit this trap */ 193 KeUpdateSystemTime(TrapFrame, LastIncrement, Irql); 194 195 /* End the interrupt */ 196 KiEndInterrupt(Irql, TrapFrame); 197} 198 199VOID 200FASTCALL 201HalpClockIpiHandler(IN PKTRAP_FRAME TrapFrame) 202{ 203 KIRQL Irql; 204 205 /* Enter trap */ 206 KiEnterInterruptTrap(TrapFrame); 207#ifdef _M_AMD64 208 /* This is for debugging */ 209 TrapFrame->ErrorCode = 0xc10c4; 210#endif 211 212 /* Start the interrupt */ 213 if (!HalBeginSystemInterrupt(CLOCK_LEVEL, CLOCK_IPI_VECTOR, &Irql)) 214 { 215 /* Spurious, just end the interrupt */ 216#ifdef _M_IX86 217 KiEoiHelper(TrapFrame); 218#endif 219 return; 220 } 221 222 /* Call the kernel to update runtimes */ 223 KeUpdateRunTime(TrapFrame, Irql); 224 225 /* End the interrupt */ 226 KiEndInterrupt(Irql, TrapFrame); 227} 228 229ULONG 230NTAPI 231HalSetTimeIncrement(IN ULONG Increment) 232{ 233 UCHAR Rate; 234 ULONG NextIncrement; 235 236 /* Lookup largest value below given Increment */ 237 for (Rate = RtcMinimumClockRate; Rate < RtcMaximumClockRate; Rate++) 238 { 239 /* Check if this is the largest rate possible */ 240 NextIncrement = RtcClockRateToPreciseIncrement(Rate + 1) / 1000; 241 if (NextIncrement > Increment) 242 break; 243 } 244 245 /* Set the rate and tell HAL we want to change it */ 246 HalpNextClockRate = Rate; 247 HalpSetClockRate = TRUE; 248 249 /* Return the real increment */ 250 return RtcClockRateToPreciseIncrement(Rate) / 1000; 251}