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