xref: /reactos/hal/halx86/apic/rtctimer.c (revision e5873161)
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:
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include <hal.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 #if defined(ALLOC_PRAGMA) && !defined(_MINIHAL_)
17 #pragma alloc_text(INIT, HalpInitializeClock)
18 #endif
19 
20 /* GLOBALS ********************************************************************/
21 
22 const UCHAR HalpClockVector = 0xD1;
23 BOOLEAN HalpClockSetMSRate;
24 UCHAR HalpNextMSRate;
25 UCHAR HalpCurrentRate = 9;  /* Initial rate  9: 128 Hz / 7.8 ms */
26 ULONG HalpCurrentTimeIncrement;
27 static UCHAR RtcMinimumClockRate = 6;  /* Minimum rate  6:  16 Hz / 62.5 ms */
28 static UCHAR RtcMaximumClockRate = 10; /* Maximum rate 10: 256 Hz / 3.9 ms */
29 
30 
31 FORCEINLINE
32 ULONG
33 RtcClockRateToIncrement(UCHAR Rate)
34 {
35     ULONG Freqency = ((32768 << 1) >> Rate);
36     return (1000000 + (Freqency/2)) / Freqency;
37 }
38 
39 VOID
40 RtcSetClockRate(UCHAR ClockRate)
41 {
42     UCHAR RegisterA;
43 
44     /* Update the global values */
45     HalpCurrentRate = ClockRate;
46     HalpCurrentTimeIncrement = RtcClockRateToIncrement(ClockRate);
47 
48     /* Acquire CMOS lock */
49     HalpAcquireCmosSpinLock();
50 
51     // TODO: disable NMI
52 
53     /* Read value of register A */
54     RegisterA = HalpReadCmos(RTC_REGISTER_A);
55 
56     /* Change lower 4 bits to new rate */
57     RegisterA &= 0xF0;
58     RegisterA |= ClockRate;
59 
60     /* Write the new value */
61     HalpWriteCmos(RTC_REGISTER_A, RegisterA);
62 
63     /* Release CMOS lock */
64     HalpReleaseCmosSpinLock();
65 }
66 
67 INIT_FUNCTION
68 VOID
69 NTAPI
70 HalpInitializeClock(VOID)
71 {
72     ULONG_PTR EFlags;
73     UCHAR RegisterB;
74 
75     /* Save EFlags and disable interrupts */
76     EFlags = __readeflags();
77     _disable();
78 
79     // TODO: disable NMI
80 
81     /* Acquire CMOS lock */
82     HalpAcquireCmosSpinLock();
83 
84     /* Enable the periodic interrupt in the CMOS */
85     RegisterB = HalpReadCmos(RTC_REGISTER_B);
86     HalpWriteCmos(RTC_REGISTER_B, RegisterB | RTC_REG_B_PI);
87 
88     /* Release CMOS lock */
89     HalpReleaseCmosSpinLock();
90 
91     /* Set initial rate */
92     RtcSetClockRate(HalpCurrentRate);
93 
94     /* Restore interrupt state */
95     __writeeflags(EFlags);
96 
97     /* Notify the kernel about the maximum and minimum increment */
98     KeSetTimeIncrement(RtcClockRateToIncrement(RtcMaximumClockRate),
99                        RtcClockRateToIncrement(RtcMinimumClockRate));
100 
101 
102     DPRINT1("Clock initialized\n");
103 }
104 
105 VOID
106 FASTCALL
107 HalpClockInterruptHandler(IN PKTRAP_FRAME TrapFrame)
108 {
109     ULONG LastIncrement;
110     KIRQL Irql;
111 
112     /* Enter trap */
113     KiEnterInterruptTrap(TrapFrame);
114 #ifdef _M_AMD64
115     /* This is for debugging */
116     TrapFrame->ErrorCode = 0xc10c4;
117 #endif
118 
119     /* Start the interrupt */
120     if (!HalBeginSystemInterrupt(CLOCK_LEVEL, HalpClockVector, &Irql))
121     {
122         /* Spurious, just end the interrupt */
123         KiEoiHelper(TrapFrame);
124     }
125 
126     /* Read register C, so that the next interrupt can happen */
127     HalpReadCmos(RTC_REGISTER_C);
128 
129     /* Save increment */
130     LastIncrement = HalpCurrentTimeIncrement;
131 
132     /* Check if someone changed the time rate */
133     if (HalpClockSetMSRate)
134     {
135         /* Set new clock rate */
136         RtcSetClockRate(HalpNextMSRate);
137 
138         /* We're done */
139         HalpClockSetMSRate = FALSE;
140     }
141 
142     /* Update the system time -- on x86 the kernel will exit this trap  */
143     KeUpdateSystemTime(TrapFrame, LastIncrement, Irql);
144 }
145 
146 VOID
147 FASTCALL
148 HalpProfileInterruptHandler(IN PKTRAP_FRAME TrapFrame)
149 {
150     __debugbreak();
151 }
152 
153 ULONG
154 NTAPI
155 HalSetTimeIncrement(IN ULONG Increment)
156 {
157     UCHAR Rate;
158 
159     /* Lookup largest value below given Increment */
160     for (Rate = RtcMinimumClockRate; Rate <= RtcMaximumClockRate; Rate++)
161     {
162         /* Check if this is the largest rate possible */
163         if (RtcClockRateToIncrement(Rate + 1) > Increment) break;
164     }
165 
166     /* Set the rate and tell HAL we want to change it */
167     HalpNextMSRate = Rate;
168     HalpClockSetMSRate = TRUE;
169 
170     /* Return the real increment */
171     return RtcClockRateToIncrement(Rate);
172 }
173