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 #define NDEBUG 17 #include <debug.h> 18 19 /* GLOBALS ********************************************************************/ 20 21 static const UCHAR RtcMinimumClockRate = 6; /* Minimum rate 6: 1024 Hz / 0.97 ms */ 22 static const UCHAR RtcMaximumClockRate = 10; /* Maximum rate 10: 64 Hz / 15.6 ms */ 23 static UCHAR HalpCurrentClockRate = 10; /* Initial rate 10: 64 Hz / 15.6 ms */ 24 static ULONG HalpCurrentTimeIncrement; 25 static ULONG HalpMinimumTimeIncrement; 26 static ULONG HalpMaximumTimeIncrement; 27 static ULONG HalpCurrentFractionalIncrement; 28 static ULONG HalpRunningFraction; 29 static BOOLEAN HalpSetClockRate; 30 static UCHAR HalpNextClockRate; 31 32 /*! 33 \brief Converts the CMOS RTC rate into the time increment in 100ns intervals. 34 35 Rate Frequency Interval (ms) Precise increment (0.1ns) 36 ------------------------------------------------------ 37 0 disabled 38 1 32768 0.03052 305,175 39 2 16384 0.06103 610,351 40 3 8192 0.12207 1,220,703 41 4 4096 0.24414 2,441,406 42 5 2048 0.48828 4,882,812 43 6 1024 0.97656 9,765,625 <- minimum 44 7 512 1.95313 19,531,250 45 8 256 3.90625 39,062,500 46 9 128 7.8125 78,125,000 47 10 64 15.6250 156,250,000 <- maximum / default 48 11 32 31.25 312,500,000 49 12 16 62.5 625,000,000 50 13 8 125 1,250,000,000 51 14 4 250 2,500,000,000 52 15 2 500 5,000,000,000 53 54 */ 55 FORCEINLINE 56 ULONG 57 RtcClockRateToPreciseIncrement(UCHAR Rate) 58 { 59 /* Calculate frequency */ 60 ULONG Frequency = 32768 >> (Rate - 1); 61 62 /* Calculate interval in 0.1ns interval: Interval = (1 / Frequency) * 10,000,000,000 */ 63 return 10000000000ULL / Frequency; 64 } 65 66 VOID 67 RtcSetClockRate(UCHAR ClockRate) 68 { 69 UCHAR RegisterA; 70 ULONG PreciseIncrement; 71 72 /* Update the global values */ 73 HalpCurrentClockRate = ClockRate; 74 PreciseIncrement = RtcClockRateToPreciseIncrement(ClockRate); 75 HalpCurrentTimeIncrement = PreciseIncrement / 1000; 76 HalpCurrentFractionalIncrement = PreciseIncrement % 1000; 77 78 /* Acquire CMOS lock */ 79 HalpAcquireCmosSpinLock(); 80 81 // TODO: disable NMI 82 83 /* Read value of register A */ 84 RegisterA = HalpReadCmos(RTC_REGISTER_A); 85 86 /* Change lower 4 bits to new rate */ 87 RegisterA &= 0xF0; 88 RegisterA |= ClockRate; 89 90 /* Write the new value */ 91 HalpWriteCmos(RTC_REGISTER_A, RegisterA); 92 93 /* Release CMOS lock */ 94 HalpReleaseCmosSpinLock(); 95 } 96 97 CODE_SEG("INIT") 98 VOID 99 NTAPI 100 HalpInitializeClock(VOID) 101 { 102 ULONG_PTR EFlags; 103 UCHAR RegisterB; 104 105 /* Save EFlags and disable interrupts */ 106 EFlags = __readeflags(); 107 _disable(); 108 109 // TODO: disable NMI 110 111 /* Acquire CMOS lock */ 112 HalpAcquireCmosSpinLock(); 113 114 /* Enable the periodic interrupt in the CMOS */ 115 RegisterB = HalpReadCmos(RTC_REGISTER_B); 116 HalpWriteCmos(RTC_REGISTER_B, RegisterB | RTC_REG_B_PI); 117 118 /* Release CMOS lock */ 119 HalpReleaseCmosSpinLock(); 120 121 /* Set initial rate */ 122 RtcSetClockRate(HalpCurrentClockRate); 123 124 /* Restore interrupt state */ 125 __writeeflags(EFlags); 126 127 /* Calculate minumum and maximum increment */ 128 HalpMinimumTimeIncrement = RtcClockRateToPreciseIncrement(RtcMinimumClockRate) / 1000; 129 HalpMaximumTimeIncrement = RtcClockRateToPreciseIncrement(RtcMaximumClockRate) / 1000; 130 131 /* Notify the kernel about the maximum and minimum increment */ 132 KeSetTimeIncrement(HalpMaximumTimeIncrement, HalpMinimumTimeIncrement); 133 134 /* Enable the timer interrupt */ 135 HalEnableSystemInterrupt(APIC_CLOCK_VECTOR, CLOCK_LEVEL, Latched); 136 137 DPRINT1("Clock initialized\n"); 138 } 139 140 VOID 141 FASTCALL 142 HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame) 143 { 144 ULONG LastIncrement; 145 KIRQL Irql; 146 147 /* Enter trap */ 148 KiEnterInterruptTrap(TrapFrame); 149 #ifdef _M_AMD64 150 /* This is for debugging */ 151 TrapFrame->ErrorCode = 0xc10c4; 152 #endif 153 154 /* Start the interrupt */ 155 if (!HalBeginSystemInterrupt(CLOCK_LEVEL, APIC_CLOCK_VECTOR, &Irql)) 156 { 157 /* Spurious, just end the interrupt */ 158 KiEoiHelper(TrapFrame); 159 } 160 161 /* Read register C, so that the next interrupt can happen */ 162 HalpReadCmos(RTC_REGISTER_C); 163 164 /* Save increment */ 165 LastIncrement = HalpCurrentTimeIncrement; 166 167 /* Check if the running fraction has accounted for 100 ns */ 168 HalpRunningFraction += HalpCurrentFractionalIncrement; 169 if (HalpRunningFraction >= 1000) 170 { 171 LastIncrement++; 172 HalpRunningFraction -= 1000; 173 } 174 175 /* Check if someone changed the time rate */ 176 if (HalpSetClockRate) 177 { 178 /* Set new clock rate */ 179 RtcSetClockRate(HalpNextClockRate); 180 181 /* We're done */ 182 HalpSetClockRate = FALSE; 183 } 184 185 /* Update the system time -- on x86 the kernel will exit this trap */ 186 KeUpdateSystemTime(TrapFrame, LastIncrement, Irql); 187 } 188 189 ULONG 190 NTAPI 191 HalSetTimeIncrement(IN ULONG Increment) 192 { 193 UCHAR Rate; 194 ULONG CurrentIncrement; 195 196 /* Lookup largest value below given Increment */ 197 for (Rate = RtcMinimumClockRate; Rate <= RtcMaximumClockRate; Rate++) 198 { 199 /* Check if this is the largest rate possible */ 200 CurrentIncrement = RtcClockRateToPreciseIncrement(Rate + 1) / 1000; 201 if (Increment > CurrentIncrement) break; 202 } 203 204 /* Set the rate and tell HAL we want to change it */ 205 HalpNextClockRate = Rate; 206 HalpSetClockRate = TRUE; 207 208 /* Return the real increment */ 209 return RtcClockRateToPreciseIncrement(Rate) / 1000; 210 } 211