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