[REACTOS] Cleanup INIT and some PAGE section allocations
[reactos.git] / hal / halx86 / apic / rtctimer.c
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 }