xref: /reactos/hal/halx86/generic/cmos.c (revision 5c7ce447)
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(IN ULONG BusNumber,
105                 IN ULONG SlotNumber,
106                 IN PVOID Buffer,
107                 IN ULONG Length)
108 {
109     PUCHAR Ptr = (PUCHAR)Buffer;
110     ULONG Address = SlotNumber;
111     ULONG Len = Length;
112 
113     /* Do nothing if we don't have a length */
114     if (!Length) return 0;
115 
116     /* Acquire CMOS Lock */
117     HalpAcquireCmosSpinLock();
118 
119     /* Check if this is simple CMOS */
120     if (BusNumber == 0)
121     {
122         /* Loop the buffer up to 0xFF */
123         while ((Len > 0) && (Address < 0x100))
124         {
125             /* Write the data */
126             HalpWriteCmos((UCHAR)Address, *Ptr);
127 
128             /* Update position and length */
129             Ptr++;
130             Address++;
131             Len--;
132         }
133     }
134     else if (BusNumber == 1)
135     {
136         /* Loop the buffer up to 0xFFFF */
137         while ((Len > 0) && (Address < 0x10000))
138         {
139             /* Write the data */
140             HalpWriteCmos((UCHAR)Address, *Ptr);
141 
142             /* Update position and length */
143             Ptr++;
144             Address++;
145             Len--;
146         }
147     }
148 
149     /* Release CMOS Lock */
150     HalpReleaseCmosSpinLock();
151 
152     /* Return length read */
153     return Length - Len;
154 }
155 
156 CODE_SEG("INIT")
157 VOID
158 NTAPI
159 HalpInitializeCmos(VOID)
160 {
161     /* Set default century offset byte */
162     HalpCmosCenturyOffset = 50;
163 
164     /* No support for EISA or MCA */
165     ASSERT(HalpBusType == MACHINE_TYPE_ISA);
166 }
167 
168 /* PUBLIC FUNCTIONS **********************************************************/
169 
170 /*
171  * @implemented
172  */
173 ARC_STATUS
174 NTAPI
175 HalGetEnvironmentVariable(
176     _In_ PCH Name,
177     _In_ USHORT ValueLength,
178     _Out_writes_z_(ValueLength) PCH Value)
179 {
180     UCHAR Val;
181 
182     /* Only variable supported on x86 */
183     if (_stricmp(Name, "LastKnownGood")) return ENOENT;
184 
185     /* Acquire CMOS Lock */
186     HalpAcquireCmosSpinLock();
187 
188     /* Query the current value */
189     Val = HalpReadCmos(RTC_REGISTER_B) & 0x01;
190 
191     /* Release CMOS lock */
192     HalpReleaseCmosSpinLock();
193 
194     /* Check the flag */
195     if (Val)
196     {
197         /* Return false */
198         strncpy(Value, "FALSE", ValueLength);
199     }
200     else
201     {
202         /* Return true */
203         strncpy(Value, "TRUE", ValueLength);
204     }
205 
206     /* Return success */
207     return ESUCCESS;
208 }
209 
210 /*
211  * @implemented
212  */
213 ARC_STATUS
214 NTAPI
215 HalSetEnvironmentVariable(IN PCH Name,
216                           IN PCH Value)
217 {
218     UCHAR Val;
219 
220     /* Only variable supported on x86 */
221     if (_stricmp(Name, "LastKnownGood")) return ENOMEM;
222 
223     /* Check if this is true or false */
224     if (!_stricmp(Value, "TRUE"))
225     {
226         /* It's true, acquire CMOS lock */
227         HalpAcquireCmosSpinLock();
228 
229         /* Read the current value and add the flag */
230         Val = HalpReadCmos(RTC_REGISTER_B) | 1;
231     }
232     else if (!_stricmp(Value, "FALSE"))
233     {
234         /* It's false, acquire CMOS lock */
235         HalpAcquireCmosSpinLock();
236 
237         /* Read the current value and mask out  the flag */
238         Val = HalpReadCmos(RTC_REGISTER_B) & ~1;
239     }
240     else
241     {
242         /* Fail */
243         return ENOMEM;
244     }
245 
246     /* Write new value */
247     HalpWriteCmos(RTC_REGISTER_B, Val);
248 
249     /* Release the lock and return success */
250     HalpReleaseCmosSpinLock();
251     return ESUCCESS;
252 }
253 
254 /*
255  * @implemented
256  */
257 BOOLEAN
258 NTAPI
259 HalQueryRealTimeClock(OUT PTIME_FIELDS Time)
260 {
261     /* Acquire CMOS Lock */
262     HalpAcquireCmosSpinLock();
263 
264     /* Loop while update is in progress */
265     while ((HalpReadCmos(RTC_REGISTER_A)) & RTC_REG_A_UIP);
266 
267     /* Set the time data */
268     Time->Second = BCD_INT(HalpReadCmos(0));
269     Time->Minute = BCD_INT(HalpReadCmos(2));
270     Time->Hour = BCD_INT(HalpReadCmos(4));
271     Time->Weekday = BCD_INT(HalpReadCmos(6));
272     Time->Day = BCD_INT(HalpReadCmos(7));
273     Time->Month = BCD_INT(HalpReadCmos(8));
274     Time->Year = BCD_INT(HalpReadCmos(9));
275     Time->Milliseconds = 0;
276 
277     /* FIXME: Check century byte */
278 
279     /* Compensate for the century field */
280     Time->Year += (Time->Year > 80) ? 1900: 2000;
281 
282     /* Release CMOS lock */
283     HalpReleaseCmosSpinLock();
284 
285     /* Always return TRUE */
286     return TRUE;
287 }
288 
289 /*
290  * @implemented
291  */
292 BOOLEAN
293 NTAPI
294 HalSetRealTimeClock(IN PTIME_FIELDS Time)
295 {
296     /* Acquire CMOS Lock */
297     HalpAcquireCmosSpinLock();
298 
299     /* Loop while update is in progress */
300     while ((HalpReadCmos(RTC_REGISTER_A)) & RTC_REG_A_UIP);
301 
302     /* Write time fields to CMOS RTC */
303     HalpWriteCmos(0, INT_BCD(Time->Second));
304     HalpWriteCmos(2, INT_BCD(Time->Minute));
305     HalpWriteCmos(4, INT_BCD(Time->Hour));
306     HalpWriteCmos(6, INT_BCD(Time->Weekday));
307     HalpWriteCmos(7, INT_BCD(Time->Day));
308     HalpWriteCmos(8, INT_BCD(Time->Month));
309     HalpWriteCmos(9, INT_BCD(Time->Year % 100));
310 
311     /* FIXME: Set the century byte */
312 
313     /* Release CMOS lock */
314     HalpReleaseCmosSpinLock();
315 
316     /* Always return TRUE */
317     return TRUE;
318 }
319