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 * 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 22 static const UCHAR RtcMinimumClockRate = 6; /* Minimum rate 6: 1024 Hz / 0.97 ms */ 23 static const UCHAR RtcMaximumClockRate = 10; /* Maximum rate 10: 64 Hz / 15.6 ms */ 24 static UCHAR HalpCurrentClockRate = 10; /* Initial rate 10: 64 Hz / 15.6 ms */ 25 static ULONG HalpCurrentTimeIncrement; 26 static ULONG HalpMinimumTimeIncrement; 27 static ULONG HalpMaximumTimeIncrement; 28 static ULONG HalpCurrentFractionalIncrement; 29 static ULONG HalpRunningFraction; 30 static BOOLEAN HalpSetClockRate; 31 static 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 */ 56 FORCEINLINE 57 ULONG 58 RtcClockRateToPreciseIncrement(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 67 VOID 68 RtcSetClockRate(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 98 CODE_SEG("INIT") 99 VOID 100 NTAPI 101 HalpInitializeClock(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 141 VOID 142 FASTCALL 143 HalpClockInterruptHandler(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 KiEoiHelper(TrapFrame); 160 } 161 162 /* Read register C, so that the next interrupt can happen */ 163 HalpReadCmos(RTC_REGISTER_C); 164 165 /* Save increment */ 166 LastIncrement = HalpCurrentTimeIncrement; 167 168 /* Check if the running fraction has accounted for 100 ns */ 169 HalpRunningFraction += HalpCurrentFractionalIncrement; 170 if (HalpRunningFraction >= 1000) 171 { 172 LastIncrement++; 173 HalpRunningFraction -= 1000; 174 } 175 176 /* Check if someone changed the time rate */ 177 if (HalpSetClockRate) 178 { 179 /* Set new clock rate */ 180 RtcSetClockRate(HalpNextClockRate); 181 182 /* We're done */ 183 HalpSetClockRate = FALSE; 184 } 185 186 /* Send the clock IPI to all other CPUs */ 187 HalpBroadcastClockIpi(CLOCK_IPI_VECTOR); 188 189 /* Update the system time -- on x86 the kernel will exit this trap */ 190 KeUpdateSystemTime(TrapFrame, LastIncrement, Irql); 191 } 192 193 VOID 194 FASTCALL 195 HalpClockIpiHandler(IN PKTRAP_FRAME TrapFrame) 196 { 197 KIRQL Irql; 198 199 /* Enter trap */ 200 KiEnterInterruptTrap(TrapFrame); 201 #ifdef _M_AMD64 202 /* This is for debugging */ 203 TrapFrame->ErrorCode = 0xc10c4; 204 #endif 205 206 /* Start the interrupt */ 207 if (!HalBeginSystemInterrupt(CLOCK_LEVEL, CLOCK_IPI_VECTOR, &Irql)) 208 { 209 /* Spurious, just end the interrupt */ 210 KiEoiHelper(TrapFrame); 211 } 212 213 /* Call the kernel to update runtimes */ 214 KeUpdateRunTime(TrapFrame, Irql); 215 216 /* End the interrupt */ 217 KiEndInterrupt(Irql, TrapFrame); 218 } 219 220 ULONG 221 NTAPI 222 HalSetTimeIncrement(IN ULONG Increment) 223 { 224 UCHAR Rate; 225 ULONG NextIncrement; 226 227 /* Lookup largest value below given Increment */ 228 for (Rate = RtcMinimumClockRate; Rate < RtcMaximumClockRate; Rate++) 229 { 230 /* Check if this is the largest rate possible */ 231 NextIncrement = RtcClockRateToPreciseIncrement(Rate + 1) / 1000; 232 if (NextIncrement > Increment) 233 break; 234 } 235 236 /* Set the rate and tell HAL we want to change it */ 237 HalpNextClockRate = Rate; 238 HalpSetClockRate = TRUE; 239 240 /* Return the real increment */ 241 return RtcClockRateToPreciseIncrement(Rate) / 1000; 242 } 243