xref: /reactos/hal/halx86/generic/cmos.c (revision 84344399)
1 /*
2  * PROJECT:         ReactOS HAL
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * PURPOSE:         CMOS Access Routines (Real Time Clock and LastKnownGood)
5  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
6  *                  Eric Kohl
7  */
8 
9 /* INCLUDES ******************************************************************/
10 
11 #include <hal.h>
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS *******************************************************************/
17 
18 UCHAR HalpCmosCenturyOffset;
19 
20 /* PRIVATE FUNCTIONS *********************************************************/
21 
22 _Requires_lock_held_(HalpSystemHardwareLock)
23 UCHAR
24 NTAPI
25 HalpReadCmos(IN UCHAR Reg)
26 {
27     /* Select the register */
28     WRITE_PORT_UCHAR(CMOS_CONTROL_PORT, Reg);
29 
30     /* Query the value */
31     return READ_PORT_UCHAR(CMOS_DATA_PORT);
32 }
33 
34 _Requires_lock_held_(HalpSystemHardwareLock)
35 VOID
36 NTAPI
37 HalpWriteCmos(IN UCHAR Reg,
38               IN UCHAR Value)
39 {
40     /* Select the register */
41     WRITE_PORT_UCHAR(CMOS_CONTROL_PORT, Reg);
42 
43     /* Write the value */
44     WRITE_PORT_UCHAR(CMOS_DATA_PORT, Value);
45 }
46 
47 ULONG
48 NTAPI
49 HalpGetCmosData(
50     _In_ ULONG BusNumber,
51     _In_ ULONG SlotNumber,
52     _Out_writes_bytes_(Length) PVOID Buffer,
53     _In_ ULONG Length)
54 {
55     PUCHAR Ptr = (PUCHAR)Buffer;
56     ULONG Address = SlotNumber;
57     ULONG Len = Length;
58 
59     /* Do nothing if we don't have a length */
60     if (!Length) return 0;
61 
62     /* Acquire CMOS Lock */
63     HalpAcquireCmosSpinLock();
64 
65     /* Check if this is simple CMOS */
66     if (BusNumber == 0)
67     {
68         /* Loop the buffer up to 0xFF */
69         while ((Len > 0) && (Address < 0x100))
70         {
71             /* Read the data */
72             *Ptr = HalpReadCmos((UCHAR)Address);
73 
74             /* Update position and length */
75             Ptr++;
76             Address++;
77             Len--;
78         }
79     }
80     else if (BusNumber == 1)
81     {
82         /* Loop the buffer up to 0xFFFF */
83         while ((Len > 0) && (Address < 0x10000))
84         {
85             /* Write the data */
86             *Ptr = HalpReadCmos((UCHAR)Address);
87 
88             /* Update position and length */
89             Ptr++;
90             Address++;
91             Len--;
92         }
93     }
94 
95     /* Release CMOS Lock */
96     HalpReleaseCmosSpinLock();
97 
98     /* Return length read */
99     return Length - Len;
100 }
101 
102 ULONG
103 NTAPI
104 HalpSetCmosData(
105     _In_ ULONG BusNumber,
106     _In_ ULONG SlotNumber,
107     _In_reads_bytes_(Length) PVOID Buffer,
108     _In_ ULONG Length)
109 {
110     PUCHAR Ptr = (PUCHAR)Buffer;
111     ULONG Address = SlotNumber;
112     ULONG Len = Length;
113 
114     /* Do nothing if we don't have a length */
115     if (!Length) return 0;
116 
117     /* Acquire CMOS Lock */
118     HalpAcquireCmosSpinLock();
119 
120     /* Check if this is simple CMOS */
121     if (BusNumber == 0)
122     {
123         /* Loop the buffer up to 0xFF */
124         while ((Len > 0) && (Address < 0x100))
125         {
126             /* Write the data */
127             HalpWriteCmos((UCHAR)Address, *Ptr);
128 
129             /* Update position and length */
130             Ptr++;
131             Address++;
132             Len--;
133         }
134     }
135     else if (BusNumber == 1)
136     {
137         /* Loop the buffer up to 0xFFFF */
138         while ((Len > 0) && (Address < 0x10000))
139         {
140             /* Write the data */
141             HalpWriteCmos((UCHAR)Address, *Ptr);
142 
143             /* Update position and length */
144             Ptr++;
145             Address++;
146             Len--;
147         }
148     }
149 
150     /* Release CMOS Lock */
151     HalpReleaseCmosSpinLock();
152 
153     /* Return length read */
154     return Length - Len;
155 }
156 
157 CODE_SEG("INIT")
158 VOID
159 NTAPI
160 HalpInitializeCmos(VOID)
161 {
162     /* Set default century offset byte */
163     HalpCmosCenturyOffset = 50;
164 
165     /* No support for EISA or MCA */
166     ASSERT(HalpBusType == MACHINE_TYPE_ISA);
167 }
168 
169 /* PUBLIC FUNCTIONS **********************************************************/
170 
171 /*
172  * @implemented
173  */
174 ARC_STATUS
175 NTAPI
176 HalGetEnvironmentVariable(
177     _In_ PCH Name,
178     _In_ USHORT ValueLength,
179     _Out_writes_z_(ValueLength) PCH Value)
180 {
181     UCHAR Val;
182 
183     /* Only variable supported on x86 */
184     if (_stricmp(Name, "LastKnownGood")) return ENOENT;
185 
186     /* Acquire CMOS Lock */
187     HalpAcquireCmosSpinLock();
188 
189     /* Query the current value */
190     Val = HalpReadCmos(RTC_REGISTER_B) & 0x01;
191 
192     /* Release CMOS lock */
193     HalpReleaseCmosSpinLock();
194 
195     /* Check the flag */
196     if (Val)
197     {
198         /* Return false */
199         strncpy(Value, "FALSE", ValueLength);
200     }
201     else
202     {
203         /* Return true */
204         strncpy(Value, "TRUE", ValueLength);
205     }
206 
207     /* Return success */
208     return ESUCCESS;
209 }
210 
211 /*
212  * @implemented
213  */
214 ARC_STATUS
215 NTAPI
216 HalSetEnvironmentVariable(IN PCH Name,
217                           IN PCH Value)
218 {
219     UCHAR Val;
220 
221     /* Only variable supported on x86 */
222     if (_stricmp(Name, "LastKnownGood")) return ENOMEM;
223 
224     /* Check if this is true or false */
225     if (!_stricmp(Value, "TRUE"))
226     {
227         /* It's true, acquire CMOS lock */
228         HalpAcquireCmosSpinLock();
229 
230         /* Read the current value and add the flag */
231         Val = HalpReadCmos(RTC_REGISTER_B) | 1;
232     }
233     else if (!_stricmp(Value, "FALSE"))
234     {
235         /* It's false, acquire CMOS lock */
236         HalpAcquireCmosSpinLock();
237 
238         /* Read the current value and mask out  the flag */
239         Val = HalpReadCmos(RTC_REGISTER_B) & ~1;
240     }
241     else
242     {
243         /* Fail */
244         return ENOMEM;
245     }
246 
247     /* Write new value */
248     HalpWriteCmos(RTC_REGISTER_B, Val);
249 
250     /* Release the lock and return success */
251     HalpReleaseCmosSpinLock();
252     return ESUCCESS;
253 }
254 
255 /*
256  * @implemented
257  */
258 BOOLEAN
259 NTAPI
260 HalQueryRealTimeClock(OUT PTIME_FIELDS Time)
261 {
262     /* Acquire CMOS Lock */
263     HalpAcquireCmosSpinLock();
264 
265     /* Loop while update is in progress */
266     while ((HalpReadCmos(RTC_REGISTER_A)) & RTC_REG_A_UIP);
267 
268     /* Set the time data */
269     Time->Second = BCD_INT(HalpReadCmos(0));
270     Time->Minute = BCD_INT(HalpReadCmos(2));
271     Time->Hour = BCD_INT(HalpReadCmos(4));
272     Time->Weekday = BCD_INT(HalpReadCmos(6));
273     Time->Day = BCD_INT(HalpReadCmos(7));
274     Time->Month = BCD_INT(HalpReadCmos(8));
275     Time->Year = BCD_INT(HalpReadCmos(9));
276     Time->Milliseconds = 0;
277 
278     /* FIXME: Check century byte */
279 
280     /* Compensate for the century field */
281     Time->Year += (Time->Year > 80) ? 1900: 2000;
282 
283     /* Release CMOS lock */
284     HalpReleaseCmosSpinLock();
285 
286     /* Always return TRUE */
287     return TRUE;
288 }
289 
290 /*
291  * @implemented
292  */
293 BOOLEAN
294 NTAPI
295 HalSetRealTimeClock(IN PTIME_FIELDS Time)
296 {
297     /* Acquire CMOS Lock */
298     HalpAcquireCmosSpinLock();
299 
300     /* Loop while update is in progress */
301     while ((HalpReadCmos(RTC_REGISTER_A)) & RTC_REG_A_UIP);
302 
303     /* Write time fields to CMOS RTC */
304     HalpWriteCmos(0, INT_BCD(Time->Second));
305     HalpWriteCmos(2, INT_BCD(Time->Minute));
306     HalpWriteCmos(4, INT_BCD(Time->Hour));
307     HalpWriteCmos(6, INT_BCD(Time->Weekday));
308     HalpWriteCmos(7, INT_BCD(Time->Day));
309     HalpWriteCmos(8, INT_BCD(Time->Month));
310     HalpWriteCmos(9, INT_BCD(Time->Year % 100));
311 
312     /* FIXME: Set the century byte */
313 
314     /* Release CMOS lock */
315     HalpReleaseCmosSpinLock();
316 
317     /* Always return TRUE */
318     return TRUE;
319 }
320