]>
Commit | Line | Data |
---|---|---|
2ef2b01e A |
1 | /** @file\r |
2 | Simple PC RTC\r | |
3 | \r | |
60274cca HT |
4 | Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>\r |
5 | Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>\r | |
f4b491b5 | 6 | Copyright (c) 2014, ARM Ltd. All rights reserved.\r |
2ef2b01e | 7 | \r |
60274cca | 8 | This program and the accompanying materials\r |
2ef2b01e A |
9 | are licensed and made available under the terms and conditions of the BSD License\r |
10 | which accompanies this distribution. The full text of the license may be found at\r | |
11 | http://opensource.org/licenses/bsd-license.php\r | |
12 | \r | |
13 | THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r | |
14 | WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r | |
15 | \r | |
16 | \r | |
17 | **/\r | |
3402aac7 | 18 | \r |
2ef2b01e A |
19 | \r |
20 | \r | |
21 | typedef struct {\r | |
22 | EFI_LOCK RtcLock;\r | |
23 | UINT16 SavedTimeZone;\r | |
24 | UINT8 Daylight;\r | |
25 | } PC_RTC_GLOBALS;\r | |
26 | \r | |
27 | #define PCAT_RTC_ADDRESS_REGISTER 0x70\r | |
28 | #define PCAT_RTC_DATA_REGISTER 0x71\r | |
29 | \r | |
30 | //\r | |
31 | // Dallas DS12C887 Real Time Clock\r | |
32 | //\r | |
33 | #define RTC_ADDRESS_SECONDS 0 // R/W Range 0..59\r | |
34 | #define RTC_ADDRESS_SECONDS_ALARM 1 // R/W Range 0..59\r | |
35 | #define RTC_ADDRESS_MINUTES 2 // R/W Range 0..59\r | |
36 | #define RTC_ADDRESS_MINUTES_ALARM 3 // R/W Range 0..59\r | |
37 | #define RTC_ADDRESS_HOURS 4 // R/W Range 1..12 or 0..23 Bit 7 is AM/PM\r | |
38 | #define RTC_ADDRESS_HOURS_ALARM 5 // R/W Range 1..12 or 0..23 Bit 7 is AM/PM\r | |
39 | #define RTC_ADDRESS_DAY_OF_THE_WEEK 6 // R/W Range 1..7\r | |
40 | #define RTC_ADDRESS_DAY_OF_THE_MONTH 7 // R/W Range 1..31\r | |
41 | #define RTC_ADDRESS_MONTH 8 // R/W Range 1..12\r | |
42 | #define RTC_ADDRESS_YEAR 9 // R/W Range 0..99\r | |
43 | #define RTC_ADDRESS_REGISTER_A 10 // R/W[0..6] R0[7]\r | |
44 | #define RTC_ADDRESS_REGISTER_B 11 // R/W\r | |
45 | #define RTC_ADDRESS_REGISTER_C 12 // RO\r | |
46 | #define RTC_ADDRESS_REGISTER_D 13 // RO\r | |
47 | #define RTC_ADDRESS_CENTURY 50 // R/W Range 19..20 Bit 8 is R/W\r | |
48 | //\r | |
49 | // Date and time initial values.\r | |
50 | // They are used if the RTC values are invalid during driver initialization\r | |
51 | //\r | |
52 | #define RTC_INIT_SECOND 0\r | |
53 | #define RTC_INIT_MINUTE 0\r | |
54 | #define RTC_INIT_HOUR 0\r | |
55 | #define RTC_INIT_DAY 1\r | |
56 | #define RTC_INIT_MONTH 1\r | |
57 | #define RTC_INIT_YEAR 2001\r | |
58 | \r | |
59 | //\r | |
60 | // Register initial values\r | |
61 | //\r | |
62 | #define RTC_INIT_REGISTER_A 0x26\r | |
63 | #define RTC_INIT_REGISTER_B 0x02\r | |
64 | #define RTC_INIT_REGISTER_D 0x0\r | |
65 | \r | |
66 | #pragma pack(1)\r | |
67 | //\r | |
68 | // Register A\r | |
69 | //\r | |
70 | typedef struct {\r | |
71 | UINT8 RS : 4; // Rate Selection Bits\r | |
72 | UINT8 DV : 3; // Divisor\r | |
73 | UINT8 UIP : 1; // Update in progress\r | |
74 | } RTC_REGISTER_A_BITS;\r | |
75 | \r | |
76 | typedef union {\r | |
77 | RTC_REGISTER_A_BITS Bits;\r | |
78 | UINT8 Data;\r | |
79 | } RTC_REGISTER_A;\r | |
80 | \r | |
81 | //\r | |
82 | // Register B\r | |
83 | //\r | |
84 | typedef struct {\r | |
85 | UINT8 DSE : 1; // 0 - Daylight saving disabled 1 - Daylight savings enabled\r | |
86 | UINT8 MIL : 1; // 0 - 12 hour mode 1 - 24 hour mode\r | |
87 | UINT8 DM : 1; // 0 - BCD Format 1 - Binary Format\r | |
88 | UINT8 SQWE : 1; // 0 - Disable SQWE output 1 - Enable SQWE output\r | |
89 | UINT8 UIE : 1; // 0 - Update INT disabled 1 - Update INT enabled\r | |
90 | UINT8 AIE : 1; // 0 - Alarm INT disabled 1 - Alarm INT Enabled\r | |
91 | UINT8 PIE : 1; // 0 - Periodic INT disabled 1 - Periodic INT Enabled\r | |
92 | UINT8 SET : 1; // 0 - Normal operation. 1 - Updates inhibited\r | |
93 | } RTC_REGISTER_B_BITS;\r | |
94 | \r | |
95 | typedef union {\r | |
96 | RTC_REGISTER_B_BITS Bits;\r | |
97 | UINT8 Data;\r | |
98 | } RTC_REGISTER_B;\r | |
99 | \r | |
100 | //\r | |
101 | // Register C\r | |
102 | //\r | |
103 | typedef struct {\r | |
104 | UINT8 Reserved : 4; // Read as zero. Can not be written.\r | |
105 | UINT8 UF : 1; // Update End Interrupt Flag\r | |
106 | UINT8 AF : 1; // Alarm Interrupt Flag\r | |
107 | UINT8 PF : 1; // Periodic Interrupt Flag\r | |
108 | UINT8 IRQF : 1; // Iterrupt Request Flag = PF & PIE | AF & AIE | UF & UIE\r | |
109 | } RTC_REGISTER_C_BITS;\r | |
110 | \r | |
111 | typedef union {\r | |
112 | RTC_REGISTER_C_BITS Bits;\r | |
113 | UINT8 Data;\r | |
114 | } RTC_REGISTER_C;\r | |
115 | \r | |
116 | //\r | |
117 | // Register D\r | |
118 | //\r | |
119 | typedef struct {\r | |
120 | UINT8 Reserved : 7; // Read as zero. Can not be written.\r | |
121 | UINT8 VRT : 1; // Valid RAM and Time\r | |
122 | } RTC_REGISTER_D_BITS;\r | |
123 | \r | |
124 | typedef union {\r | |
125 | RTC_REGISTER_D_BITS Bits;\r | |
126 | UINT8 Data;\r | |
127 | } RTC_REGISTER_D;\r | |
128 | \r | |
129 | #pragma pack()\r | |
130 | \r | |
131 | PC_RTC_GLOBALS mRtc;\r | |
132 | \r | |
133 | BOOLEAN\r | |
134 | IsLeapYear (\r | |
135 | IN EFI_TIME *Time\r | |
136 | )\r | |
137 | {\r | |
138 | if (Time->Year % 4 == 0) {\r | |
139 | if (Time->Year % 100 == 0) {\r | |
140 | if (Time->Year % 400 == 0) {\r | |
141 | return TRUE;\r | |
142 | } else {\r | |
143 | return FALSE;\r | |
144 | }\r | |
145 | } else {\r | |
146 | return TRUE;\r | |
147 | }\r | |
148 | } else {\r | |
149 | return FALSE;\r | |
150 | }\r | |
151 | }\r | |
152 | \r | |
153 | \r | |
154 | const INTN mDayOfMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };\r | |
155 | \r | |
156 | BOOLEAN\r | |
157 | DayValid (\r | |
158 | IN EFI_TIME *Time\r | |
159 | )\r | |
160 | {\r | |
161 | if (Time->Day < 1 ||\r | |
162 | Time->Day > mDayOfMonth[Time->Month - 1] ||\r | |
163 | (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28))\r | |
164 | ) {\r | |
165 | return FALSE;\r | |
166 | }\r | |
167 | \r | |
168 | return TRUE;\r | |
169 | }\r | |
170 | \r | |
171 | \r | |
172 | UINT8\r | |
173 | DecimaltoBcd (\r | |
174 | IN UINT8 DecValue\r | |
175 | )\r | |
176 | {\r | |
177 | UINTN High;\r | |
178 | UINTN Low;\r | |
179 | \r | |
180 | High = DecValue / 10;\r | |
181 | Low = DecValue - (High * 10);\r | |
182 | \r | |
183 | return (UINT8) (Low + (High << 4));\r | |
184 | }\r | |
185 | \r | |
186 | UINT8\r | |
187 | BcdToDecimal (\r | |
188 | IN UINT8 BcdValue\r | |
189 | )\r | |
190 | {\r | |
191 | UINTN High;\r | |
192 | UINTN Low;\r | |
193 | \r | |
194 | High = BcdValue >> 4;\r | |
195 | Low = BcdValue - (High << 4);\r | |
196 | \r | |
197 | return (UINT8) (Low + (High * 10));\r | |
198 | }\r | |
199 | \r | |
200 | \r | |
201 | \r | |
202 | \r | |
203 | VOID\r | |
204 | ConvertEfiTimeToRtcTime (\r | |
205 | IN EFI_TIME *Time,\r | |
206 | IN RTC_REGISTER_B RegisterB,\r | |
207 | IN UINT8 *Century\r | |
208 | )\r | |
209 | {\r | |
210 | BOOLEAN PM;\r | |
211 | \r | |
212 | PM = TRUE;\r | |
213 | //\r | |
214 | // Adjust hour field if RTC in in 12 hour mode\r | |
215 | //\r | |
216 | if (RegisterB.Bits.MIL == 0) {\r | |
217 | if (Time->Hour < 12) {\r | |
218 | PM = FALSE;\r | |
219 | }\r | |
220 | \r | |
221 | if (Time->Hour >= 13) {\r | |
222 | Time->Hour = (UINT8) (Time->Hour - 12);\r | |
223 | } else if (Time->Hour == 0) {\r | |
224 | Time->Hour = 12;\r | |
225 | }\r | |
226 | }\r | |
227 | //\r | |
228 | // Set the Time/Date/Daylight Savings values.\r | |
229 | //\r | |
230 | *Century = DecimaltoBcd ((UINT8) (Time->Year / 100));\r | |
231 | \r | |
232 | Time->Year = (UINT16) (Time->Year % 100);\r | |
233 | \r | |
234 | if (RegisterB.Bits.DM == 0) {\r | |
235 | Time->Year = DecimaltoBcd ((UINT8) Time->Year);\r | |
236 | Time->Month = DecimaltoBcd (Time->Month);\r | |
237 | Time->Day = DecimaltoBcd (Time->Day);\r | |
238 | Time->Hour = DecimaltoBcd (Time->Hour);\r | |
239 | Time->Minute = DecimaltoBcd (Time->Minute);\r | |
240 | Time->Second = DecimaltoBcd (Time->Second);\r | |
241 | }\r | |
242 | //\r | |
243 | // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.\r | |
244 | //\r | |
245 | if (RegisterB.Bits.MIL == 0 && PM) {\r | |
246 | Time->Hour = (UINT8) (Time->Hour | 0x80);\r | |
247 | }\r | |
248 | }\r | |
249 | \r | |
f4b491b5 RC |
250 | /**\r |
251 | Check the validity of all the fields of a data structure of type EFI_TIME\r | |
252 | \r | |
253 | @param[in] Time Pointer to a data structure of type EFI_TIME that defines a date and time\r | |
254 | \r | |
255 | @retval EFI_SUCCESS All date and time fields are valid\r | |
256 | @retval EFI_INVALID_PARAMETER At least one date or time field is not valid\r | |
257 | **/\r | |
2ef2b01e A |
258 | EFI_STATUS\r |
259 | RtcTimeFieldsValid (\r | |
260 | IN EFI_TIME *Time\r | |
261 | )\r | |
2ef2b01e | 262 | {\r |
f4b491b5 RC |
263 | if ((Time->Year < 1998 ) ||\r |
264 | (Time->Year > 2099 ) ||\r | |
265 | (Time->Month < 1 ) ||\r | |
266 | (Time->Month > 12 ) ||\r | |
267 | (!DayValid (Time)) ||\r | |
268 | (Time->Hour > 23 ) ||\r | |
269 | (Time->Minute > 59 ) ||\r | |
270 | (Time->Second > 59 ) ||\r | |
271 | (Time->Nanosecond > 999999999) ||\r | |
272 | ((Time->TimeZone != EFI_UNSPECIFIED_TIMEZONE) &&\r | |
273 | ((Time->TimeZone < -1440) ||\r | |
274 | (Time->TimeZone > 1440 ) ) ) ||\r | |
275 | (Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT |\r | |
276 | EFI_TIME_IN_DAYLIGHT )))\r | |
2ef2b01e A |
277 | ) {\r |
278 | return EFI_INVALID_PARAMETER;\r | |
279 | }\r | |
280 | \r | |
281 | return EFI_SUCCESS;\r | |
282 | }\r | |
283 | \r | |
284 | UINT8\r | |
285 | RtcRead (\r | |
286 | IN UINT8 Address\r | |
287 | )\r | |
288 | {\r | |
289 | IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));\r | |
290 | return IoRead8 (PCAT_RTC_DATA_REGISTER);\r | |
291 | }\r | |
292 | \r | |
293 | VOID\r | |
294 | RtcWrite (\r | |
295 | IN UINT8 Address,\r | |
296 | IN UINT8 Data\r | |
297 | )\r | |
298 | {\r | |
299 | IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));\r | |
300 | IoWrite8 (PCAT_RTC_DATA_REGISTER, Data);\r | |
301 | }\r | |
302 | \r | |
303 | \r | |
304 | EFI_STATUS\r | |
305 | RtcTestCenturyRegister (\r | |
306 | VOID\r | |
307 | )\r | |
308 | {\r | |
309 | UINT8 Century;\r | |
310 | UINT8 Temp;\r | |
311 | \r | |
312 | Century = RtcRead (RTC_ADDRESS_CENTURY);\r | |
313 | //\r | |
314 | // RtcWrite (RTC_ADDRESS_CENTURY, 0x00);\r | |
315 | //\r | |
316 | Temp = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f);\r | |
317 | RtcWrite (RTC_ADDRESS_CENTURY, Century);\r | |
318 | if (Temp == 0x19 || Temp == 0x20) {\r | |
319 | return EFI_SUCCESS;\r | |
320 | }\r | |
321 | \r | |
322 | return EFI_DEVICE_ERROR;\r | |
323 | }\r | |
324 | \r | |
325 | VOID\r | |
326 | ConvertRtcTimeToEfiTime (\r | |
327 | IN EFI_TIME *Time,\r | |
328 | IN RTC_REGISTER_B RegisterB\r | |
329 | )\r | |
330 | {\r | |
331 | BOOLEAN PM;\r | |
332 | \r | |
333 | if ((Time->Hour) & 0x80) {\r | |
334 | PM = TRUE;\r | |
335 | } else {\r | |
336 | PM = FALSE;\r | |
337 | }\r | |
338 | \r | |
339 | Time->Hour = (UINT8) (Time->Hour & 0x7f);\r | |
340 | \r | |
341 | if (RegisterB.Bits.DM == 0) {\r | |
342 | Time->Year = BcdToDecimal ((UINT8) Time->Year);\r | |
343 | Time->Month = BcdToDecimal (Time->Month);\r | |
344 | Time->Day = BcdToDecimal (Time->Day);\r | |
345 | Time->Hour = BcdToDecimal (Time->Hour);\r | |
346 | Time->Minute = BcdToDecimal (Time->Minute);\r | |
347 | Time->Second = BcdToDecimal (Time->Second);\r | |
348 | }\r | |
349 | //\r | |
350 | // If time is in 12 hour format, convert it to 24 hour format\r | |
351 | //\r | |
352 | if (RegisterB.Bits.MIL == 0) {\r | |
353 | if (PM && Time->Hour < 12) {\r | |
354 | Time->Hour = (UINT8) (Time->Hour + 12);\r | |
355 | }\r | |
356 | \r | |
357 | if (!PM && Time->Hour == 12) {\r | |
358 | Time->Hour = 0;\r | |
359 | }\r | |
360 | }\r | |
361 | \r | |
362 | Time->Nanosecond = 0;\r | |
363 | Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r | |
364 | Time->Daylight = 0;\r | |
365 | }\r | |
366 | \r | |
367 | EFI_STATUS\r | |
368 | RtcWaitToUpdate (\r | |
369 | UINTN Timeout\r | |
370 | )\r | |
371 | {\r | |
372 | RTC_REGISTER_A RegisterA;\r | |
373 | RTC_REGISTER_D RegisterD;\r | |
374 | \r | |
375 | //\r | |
376 | // See if the RTC is functioning correctly\r | |
377 | //\r | |
378 | RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);\r | |
379 | \r | |
380 | if (RegisterD.Bits.VRT == 0) {\r | |
381 | return EFI_DEVICE_ERROR;\r | |
382 | }\r | |
383 | //\r | |
384 | // Wait for up to 0.1 seconds for the RTC to be ready.\r | |
385 | //\r | |
386 | Timeout = (Timeout / 10) + 1;\r | |
387 | RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);\r | |
388 | while (RegisterA.Bits.UIP == 1 && Timeout > 0) {\r | |
389 | MicroSecondDelay (10);\r | |
390 | RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);\r | |
391 | Timeout--;\r | |
392 | }\r | |
393 | \r | |
394 | RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);\r | |
395 | if (Timeout == 0 || RegisterD.Bits.VRT == 0) {\r | |
396 | return EFI_DEVICE_ERROR;\r | |
397 | }\r | |
398 | \r | |
399 | return EFI_SUCCESS;\r | |
400 | }\r | |
401 | \r | |
402 | EFI_STATUS\r | |
403 | LibGetTime (\r | |
404 | OUT EFI_TIME *Time,\r | |
405 | OUT EFI_TIME_CAPABILITIES *Capabilities\r | |
406 | )\r | |
407 | {\r | |
408 | EFI_STATUS Status;\r | |
409 | RTC_REGISTER_B RegisterB;\r | |
410 | UINT8 Century;\r | |
411 | UINTN BufferSize;\r | |
412 | \r | |
413 | //\r | |
414 | // Check parameters for null pointer\r | |
415 | //\r | |
416 | if (Time == NULL) {\r | |
417 | return EFI_INVALID_PARAMETER;\r | |
418 | \r | |
419 | }\r | |
420 | //\r | |
421 | // Acquire RTC Lock to make access to RTC atomic\r | |
422 | //\r | |
423 | EfiAcquireLock (&mRtc.RtcLock);\r | |
424 | \r | |
425 | //\r | |
426 | // Wait for up to 0.1 seconds for the RTC to be updated\r | |
427 | //\r | |
428 | Status = RtcWaitToUpdate (100000);\r | |
429 | if (EFI_ERROR (Status)) {\r | |
430 | EfiReleaseLock (&mRtc.RtcLock);\r | |
431 | return Status;\r | |
432 | }\r | |
433 | //\r | |
434 | // Read Register B\r | |
435 | //\r | |
436 | RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r | |
437 | \r | |
438 | //\r | |
439 | // Get the Time/Date/Daylight Savings values.\r | |
440 | //\r | |
441 | Time->Second = RtcRead (RTC_ADDRESS_SECONDS);\r | |
442 | Time->Minute = RtcRead (RTC_ADDRESS_MINUTES);\r | |
443 | Time->Hour = RtcRead (RTC_ADDRESS_HOURS);\r | |
444 | Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r | |
445 | Time->Month = RtcRead (RTC_ADDRESS_MONTH);\r | |
446 | Time->Year = RtcRead (RTC_ADDRESS_YEAR);\r | |
447 | \r | |
448 | ConvertRtcTimeToEfiTime (Time, RegisterB);\r | |
449 | \r | |
450 | if (RtcTestCenturyRegister () == EFI_SUCCESS) {\r | |
451 | Century = BcdToDecimal ((UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f));\r | |
452 | } else {\r | |
453 | Century = BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY));\r | |
454 | }\r | |
455 | \r | |
456 | Time->Year = (UINT16) (Century * 100 + Time->Year);\r | |
457 | \r | |
458 | //\r | |
459 | // Release RTC Lock.\r | |
460 | //\r | |
461 | EfiReleaseLock (&mRtc.RtcLock);\r | |
462 | \r | |
463 | //\r | |
464 | // Get the variable that containts the TimeZone and Daylight fields\r | |
465 | //\r | |
466 | Time->TimeZone = mRtc.SavedTimeZone;\r | |
467 | Time->Daylight = mRtc.Daylight;\r | |
468 | \r | |
469 | BufferSize = sizeof (INT16) + sizeof (UINT8);\r | |
470 | \r | |
471 | //\r | |
472 | // Make sure all field values are in correct range\r | |
473 | //\r | |
474 | Status = RtcTimeFieldsValid (Time);\r | |
475 | if (EFI_ERROR (Status)) {\r | |
476 | return EFI_DEVICE_ERROR;\r | |
477 | }\r | |
478 | //\r | |
479 | // Fill in Capabilities if it was passed in\r | |
480 | //\r | |
481 | if (Capabilities) {\r | |
482 | Capabilities->Resolution = 1;\r | |
483 | //\r | |
484 | // 1 hertz\r | |
485 | //\r | |
486 | Capabilities->Accuracy = 50000000;\r | |
487 | //\r | |
488 | // 50 ppm\r | |
489 | //\r | |
490 | Capabilities->SetsToZero = FALSE;\r | |
491 | }\r | |
492 | \r | |
493 | return EFI_SUCCESS;\r | |
494 | }\r | |
495 | \r | |
496 | \r | |
497 | \r | |
498 | EFI_STATUS\r | |
499 | LibSetTime (\r | |
500 | IN EFI_TIME *Time\r | |
501 | )\r | |
502 | {\r | |
503 | EFI_STATUS Status;\r | |
504 | EFI_TIME RtcTime;\r | |
505 | RTC_REGISTER_B RegisterB;\r | |
506 | UINT8 Century;\r | |
507 | \r | |
508 | if (Time == NULL) {\r | |
509 | return EFI_INVALID_PARAMETER;\r | |
510 | }\r | |
511 | //\r | |
512 | // Make sure that the time fields are valid\r | |
513 | //\r | |
514 | Status = RtcTimeFieldsValid (Time);\r | |
515 | if (EFI_ERROR (Status)) {\r | |
516 | return Status;\r | |
517 | }\r | |
518 | \r | |
519 | CopyMem (&RtcTime, Time, sizeof (EFI_TIME));\r | |
520 | \r | |
521 | //\r | |
522 | // Acquire RTC Lock to make access to RTC atomic\r | |
523 | //\r | |
524 | EfiAcquireLock (&mRtc.RtcLock);\r | |
525 | \r | |
526 | //\r | |
527 | // Wait for up to 0.1 seconds for the RTC to be updated\r | |
528 | //\r | |
529 | Status = RtcWaitToUpdate (100000);\r | |
530 | if (EFI_ERROR (Status)) {\r | |
531 | EfiReleaseLock (&mRtc.RtcLock);\r | |
532 | return Status;\r | |
533 | }\r | |
534 | //\r | |
535 | // Read Register B, and inhibit updates of the RTC\r | |
536 | //\r | |
537 | RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r | |
538 | RegisterB.Bits.SET = 1;\r | |
539 | RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r | |
540 | \r | |
541 | ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);\r | |
542 | \r | |
543 | RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second);\r | |
544 | RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute);\r | |
545 | RtcWrite (RTC_ADDRESS_HOURS, RtcTime.Hour);\r | |
546 | RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH, RtcTime.Day);\r | |
547 | RtcWrite (RTC_ADDRESS_MONTH, RtcTime.Month);\r | |
548 | RtcWrite (RTC_ADDRESS_YEAR, (UINT8) RtcTime.Year);\r | |
549 | if (RtcTestCenturyRegister () == EFI_SUCCESS) {\r | |
550 | Century = (UINT8) ((Century & 0x7f) | (RtcRead (RTC_ADDRESS_CENTURY) & 0x80));\r | |
551 | }\r | |
552 | \r | |
553 | RtcWrite (RTC_ADDRESS_CENTURY, Century);\r | |
554 | \r | |
555 | //\r | |
556 | // Allow updates of the RTC registers\r | |
557 | //\r | |
558 | RegisterB.Bits.SET = 0;\r | |
559 | RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r | |
560 | \r | |
561 | //\r | |
562 | // Release RTC Lock.\r | |
563 | //\r | |
564 | EfiReleaseLock (&mRtc.RtcLock);\r | |
565 | \r | |
566 | //\r | |
567 | // Set the variable that containts the TimeZone and Daylight fields\r | |
568 | //\r | |
569 | mRtc.SavedTimeZone = Time->TimeZone;\r | |
570 | mRtc.Daylight = Time->Daylight;\r | |
571 | return Status;\r | |
572 | }\r | |
573 | \r | |
574 | EFI_STATUS\r | |
575 | libGetWakeupTime (\r | |
576 | OUT BOOLEAN *Enabled,\r | |
577 | OUT BOOLEAN *Pending,\r | |
578 | OUT EFI_TIME *Time\r | |
579 | )\r | |
580 | {\r | |
581 | EFI_STATUS Status;\r | |
582 | RTC_REGISTER_B RegisterB;\r | |
583 | RTC_REGISTER_C RegisterC;\r | |
584 | UINT8 Century;\r | |
585 | \r | |
586 | //\r | |
4f0624e8 | 587 | // Check parameters for null pointers\r |
2ef2b01e A |
588 | //\r |
589 | if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) {\r | |
590 | return EFI_INVALID_PARAMETER;\r | |
591 | \r | |
592 | }\r | |
593 | //\r | |
594 | // Acquire RTC Lock to make access to RTC atomic\r | |
595 | //\r | |
596 | EfiAcquireLock (&mRtc.RtcLock);\r | |
597 | \r | |
598 | //\r | |
599 | // Wait for up to 0.1 seconds for the RTC to be updated\r | |
600 | //\r | |
601 | Status = RtcWaitToUpdate (100000);\r | |
602 | if (EFI_ERROR (Status)) {\r | |
603 | EfiReleaseLock (&mRtc.RtcLock);\r | |
604 | return EFI_DEVICE_ERROR;\r | |
605 | }\r | |
606 | //\r | |
607 | // Read Register B and Register C\r | |
608 | //\r | |
609 | RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r | |
610 | RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);\r | |
611 | \r | |
612 | //\r | |
613 | // Get the Time/Date/Daylight Savings values.\r | |
614 | //\r | |
615 | *Enabled = RegisterB.Bits.AIE;\r | |
616 | if (*Enabled) {\r | |
617 | Time->Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);\r | |
618 | Time->Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);\r | |
619 | Time->Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);\r | |
620 | Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r | |
621 | Time->Month = RtcRead (RTC_ADDRESS_MONTH);\r | |
622 | Time->Year = RtcRead (RTC_ADDRESS_YEAR);\r | |
623 | } else {\r | |
624 | Time->Second = 0;\r | |
625 | Time->Minute = 0;\r | |
626 | Time->Hour = 0;\r | |
627 | Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r | |
628 | Time->Month = RtcRead (RTC_ADDRESS_MONTH);\r | |
629 | Time->Year = RtcRead (RTC_ADDRESS_YEAR);\r | |
630 | }\r | |
631 | \r | |
632 | ConvertRtcTimeToEfiTime (Time, RegisterB);\r | |
633 | \r | |
634 | if (RtcTestCenturyRegister () == EFI_SUCCESS) {\r | |
635 | Century = BcdToDecimal ((UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f));\r | |
636 | } else {\r | |
637 | Century = BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY));\r | |
638 | }\r | |
639 | \r | |
640 | Time->Year = (UINT16) (Century * 100 + Time->Year);\r | |
641 | \r | |
642 | //\r | |
643 | // Release RTC Lock.\r | |
644 | //\r | |
645 | EfiReleaseLock (&mRtc.RtcLock);\r | |
646 | \r | |
647 | //\r | |
648 | // Make sure all field values are in correct range\r | |
649 | //\r | |
650 | Status = RtcTimeFieldsValid (Time);\r | |
651 | if (EFI_ERROR (Status)) {\r | |
652 | return EFI_DEVICE_ERROR;\r | |
653 | }\r | |
654 | \r | |
655 | *Pending = RegisterC.Bits.AF;\r | |
656 | \r | |
657 | return EFI_SUCCESS;\r | |
658 | }\r | |
659 | \r | |
660 | EFI_STATUS\r | |
661 | LibSetWakeupTime (\r | |
662 | IN BOOLEAN Enabled,\r | |
663 | OUT EFI_TIME *Time\r | |
664 | )\r | |
665 | {\r | |
666 | EFI_STATUS Status;\r | |
667 | EFI_TIME RtcTime;\r | |
668 | RTC_REGISTER_B RegisterB;\r | |
669 | UINT8 Century;\r | |
670 | EFI_TIME_CAPABILITIES Capabilities;\r | |
671 | \r | |
672 | if (Enabled) {\r | |
673 | \r | |
674 | if (Time == NULL) {\r | |
675 | return EFI_INVALID_PARAMETER;\r | |
676 | }\r | |
677 | //\r | |
678 | // Make sure that the time fields are valid\r | |
679 | //\r | |
680 | Status = RtcTimeFieldsValid (Time);\r | |
681 | if (EFI_ERROR (Status)) {\r | |
682 | return EFI_INVALID_PARAMETER;\r | |
683 | }\r | |
684 | //\r | |
685 | // Just support set alarm time within 24 hours\r | |
686 | //\r | |
687 | LibGetTime (&RtcTime, &Capabilities);\r | |
688 | if (Time->Year != RtcTime.Year ||\r | |
689 | Time->Month != RtcTime.Month ||\r | |
690 | (Time->Day != RtcTime.Day && Time->Day != (RtcTime.Day + 1))\r | |
691 | ) {\r | |
692 | return EFI_UNSUPPORTED;\r | |
693 | }\r | |
694 | //\r | |
695 | // Make a local copy of the time and date\r | |
696 | //\r | |
697 | CopyMem (&RtcTime, Time, sizeof (EFI_TIME));\r | |
698 | \r | |
699 | }\r | |
700 | //\r | |
701 | // Acquire RTC Lock to make access to RTC atomic\r | |
702 | //\r | |
703 | EfiAcquireLock (&mRtc.RtcLock);\r | |
704 | \r | |
705 | //\r | |
706 | // Wait for up to 0.1 seconds for the RTC to be updated\r | |
707 | //\r | |
708 | Status = RtcWaitToUpdate (100000);\r | |
709 | if (EFI_ERROR (Status)) {\r | |
710 | EfiReleaseLock (&mRtc.RtcLock);\r | |
711 | return EFI_DEVICE_ERROR;\r | |
712 | }\r | |
713 | //\r | |
714 | // Read Register B, and inhibit updates of the RTC\r | |
715 | //\r | |
716 | RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r | |
717 | \r | |
718 | RegisterB.Bits.SET = 1;\r | |
719 | RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r | |
720 | \r | |
721 | if (Enabled) {\r | |
722 | ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);\r | |
723 | \r | |
724 | //\r | |
725 | // Set RTC alarm time\r | |
726 | //\r | |
727 | RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);\r | |
728 | RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);\r | |
729 | RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);\r | |
730 | \r | |
731 | RegisterB.Bits.AIE = 1;\r | |
732 | \r | |
733 | } else {\r | |
734 | RegisterB.Bits.AIE = 0;\r | |
735 | }\r | |
736 | //\r | |
737 | // Allow updates of the RTC registers\r | |
738 | //\r | |
739 | RegisterB.Bits.SET = 0;\r | |
740 | RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r | |
741 | \r | |
742 | //\r | |
743 | // Release RTC Lock.\r | |
744 | //\r | |
745 | EfiReleaseLock (&mRtc.RtcLock);\r | |
746 | \r | |
747 | return EFI_SUCCESS;\r | |
748 | }\r | |
749 | \r | |
750 | \r | |
751 | \r | |
752 | VOID\r | |
753 | LibRtcVirtualAddressChangeEvent (\r | |
754 | VOID\r | |
755 | )\r | |
756 | {\r | |
757 | }\r | |
758 | \r | |
759 | \r | |
760 | VOID\r | |
761 | LibRtcInitialize (\r | |
762 | VOID\r | |
763 | )\r | |
764 | {\r | |
765 | EFI_STATUS Status;\r | |
766 | RTC_REGISTER_A RegisterA;\r | |
767 | RTC_REGISTER_B RegisterB;\r | |
768 | RTC_REGISTER_C RegisterC;\r | |
769 | RTC_REGISTER_D RegisterD;\r | |
770 | UINT8 Century;\r | |
771 | EFI_TIME Time;\r | |
772 | \r | |
773 | //\r | |
774 | // Acquire RTC Lock to make access to RTC atomic\r | |
775 | //\r | |
776 | EfiAcquireLock (&mRtc.RtcLock);\r | |
777 | \r | |
778 | //\r | |
779 | // Initialize RTC Register\r | |
780 | //\r | |
781 | // Make sure Division Chain is properly configured,\r | |
782 | // or RTC clock won't "tick" -- time won't increment\r | |
783 | //\r | |
784 | RegisterA.Data = RTC_INIT_REGISTER_A;\r | |
785 | RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);\r | |
786 | \r | |
787 | //\r | |
788 | // Read Register B\r | |
789 | //\r | |
790 | RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r | |
791 | \r | |
792 | //\r | |
793 | // Clear RTC flag register\r | |
794 | //\r | |
795 | RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);\r | |
796 | \r | |
797 | //\r | |
798 | // Clear RTC register D\r | |
799 | //\r | |
800 | RegisterD.Data = RTC_INIT_REGISTER_D;\r | |
801 | RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data);\r | |
802 | \r | |
803 | //\r | |
804 | // Wait for up to 0.1 seconds for the RTC to be updated\r | |
805 | //\r | |
806 | Status = RtcWaitToUpdate (100000);\r | |
807 | if (EFI_ERROR (Status)) {\r | |
808 | EfiReleaseLock (&mRtc.RtcLock);\r | |
809 | return;\r | |
810 | }\r | |
811 | \r | |
812 | //\r | |
813 | // Get the Time/Date/Daylight Savings values.\r | |
814 | //\r | |
815 | Time.Second = RtcRead (RTC_ADDRESS_SECONDS);\r | |
816 | Time.Minute = RtcRead (RTC_ADDRESS_MINUTES);\r | |
817 | Time.Hour = RtcRead (RTC_ADDRESS_HOURS);\r | |
818 | Time.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r | |
819 | Time.Month = RtcRead (RTC_ADDRESS_MONTH);\r | |
820 | Time.Year = RtcRead (RTC_ADDRESS_YEAR);\r | |
821 | \r | |
822 | ConvertRtcTimeToEfiTime (&Time, RegisterB);\r | |
823 | \r | |
824 | if (RtcTestCenturyRegister () == EFI_SUCCESS) {\r | |
825 | Century = BcdToDecimal ((UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f));\r | |
826 | } else {\r | |
827 | Century = BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY));\r | |
828 | }\r | |
829 | \r | |
830 | Time.Year = (UINT16) (Century * 100 + Time.Year);\r | |
831 | \r | |
832 | //\r | |
833 | // Set RTC configuration after get original time\r | |
834 | //\r | |
835 | RtcWrite (RTC_ADDRESS_REGISTER_B, RTC_INIT_REGISTER_B);\r | |
836 | \r | |
837 | //\r | |
838 | // Release RTC Lock.\r | |
839 | //\r | |
840 | EfiReleaseLock (&mRtc.RtcLock);\r | |
841 | \r | |
842 | //\r | |
843 | // Validate time fields\r | |
844 | //\r | |
845 | Status = RtcTimeFieldsValid (&Time);\r | |
846 | if (EFI_ERROR (Status)) {\r | |
847 | Time.Second = RTC_INIT_SECOND;\r | |
848 | Time.Minute = RTC_INIT_MINUTE;\r | |
849 | Time.Hour = RTC_INIT_HOUR;\r | |
850 | Time.Day = RTC_INIT_DAY;\r | |
851 | Time.Month = RTC_INIT_MONTH;\r | |
852 | Time.Year = RTC_INIT_YEAR;\r | |
853 | }\r | |
854 | //\r | |
855 | // Reset time value according to new RTC configuration\r | |
856 | //\r | |
857 | LibSetTime (&Time);\r | |
858 | \r | |
859 | return;\r | |
860 | }\r | |
861 | \r | |
862 | \r |