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
12 /* INCLUDES *******************************************************************/
18 /* GLOBALS ********************************************************************/
20 const UCHAR HalpClockVector
= 0xD1;
21 BOOLEAN HalpClockSetMSRate
;
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 */
29 \brief Converts the CMOS RTC rate into the time increment in 100ns intervals.
31 Rate Freqency Interval (ms) Result
32 -------------------------------------
53 RtcClockRateToIncrement(UCHAR Rate
)
55 /* Calculate frequency */
56 ULONG Freqency
= 32768 >> (Rate
- 1);
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
;
64 RtcSetClockRate(UCHAR ClockRate
)
68 /* Update the global values */
69 HalpCurrentRate
= ClockRate
;
70 HalpCurrentTimeIncrement
= RtcClockRateToIncrement(ClockRate
);
72 /* Acquire CMOS lock */
73 HalpAcquireCmosSpinLock();
77 /* Read value of register A */
78 RegisterA
= HalpReadCmos(RTC_REGISTER_A
);
80 /* Change lower 4 bits to new rate */
82 RegisterA
|= ClockRate
;
84 /* Write the new value */
85 HalpWriteCmos(RTC_REGISTER_A
, RegisterA
);
87 /* Release CMOS lock */
88 HalpReleaseCmosSpinLock();
94 HalpInitializeClock(VOID
)
99 /* Save EFlags and disable interrupts */
100 EFlags
= __readeflags();
105 /* Acquire CMOS lock */
106 HalpAcquireCmosSpinLock();
108 /* Enable the periodic interrupt in the CMOS */
109 RegisterB
= HalpReadCmos(RTC_REGISTER_B
);
110 HalpWriteCmos(RTC_REGISTER_B
, RegisterB
| RTC_REG_B_PI
);
112 /* Release CMOS lock */
113 HalpReleaseCmosSpinLock();
115 /* Set initial rate */
116 RtcSetClockRate(HalpCurrentRate
);
118 /* Restore interrupt state */
119 __writeeflags(EFlags
);
121 /* Notify the kernel about the maximum and minimum increment */
122 KeSetTimeIncrement(RtcClockRateToIncrement(RtcMaximumClockRate
),
123 RtcClockRateToIncrement(RtcMinimumClockRate
));
126 DPRINT1("Clock initialized\n");
131 HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame
)
137 KiEnterInterruptTrap(TrapFrame
);
139 /* This is for debugging */
140 TrapFrame
->ErrorCode
= 0xc10c4;
143 /* Start the interrupt */
144 if (!HalBeginSystemInterrupt(CLOCK_LEVEL
, HalpClockVector
, &Irql
))
146 /* Spurious, just end the interrupt */
147 KiEoiHelper(TrapFrame
);
150 /* Read register C, so that the next interrupt can happen */
151 HalpReadCmos(RTC_REGISTER_C
);
154 LastIncrement
= HalpCurrentTimeIncrement
;
156 /* Check if someone changed the time rate */
157 if (HalpClockSetMSRate
)
159 /* Set new clock rate */
160 RtcSetClockRate(HalpNextMSRate
);
163 HalpClockSetMSRate
= FALSE
;
166 /* Update the system time -- on x86 the kernel will exit this trap */
167 KeUpdateSystemTime(TrapFrame
, LastIncrement
, Irql
);
172 HalpProfileInterruptHandler(IN PKTRAP_FRAME TrapFrame
)
179 HalSetTimeIncrement(IN ULONG Increment
)
183 /* Lookup largest value below given Increment */
184 for (Rate
= RtcMinimumClockRate
; Rate
<= RtcMaximumClockRate
; Rate
++)
186 /* Check if this is the largest rate possible */
187 if (RtcClockRateToIncrement(Rate
+ 1) > Increment
) break;
190 /* Set the rate and tell HAL we want to change it */
191 HalpNextMSRate
= Rate
;
192 HalpClockSetMSRate
= TRUE
;
194 /* Return the real increment */
195 return RtcClockRateToIncrement(Rate
);