EmbeddedPkg: Fix typos in comments
[mirror_edk2.git] / EmbeddedPkg / Library / HalRuntimeServicesExampleLib / Rtc.c
CommitLineData
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
21typedef 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
70typedef 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
76typedef 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
84typedef 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
95typedef 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
103typedef 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
111typedef 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
119typedef 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
124typedef 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
131PC_RTC_GLOBALS mRtc;\r
132\r
133BOOLEAN\r
134IsLeapYear (\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
154const INTN mDayOfMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };\r
155\r
156BOOLEAN\r
157DayValid (\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
172UINT8\r
173DecimaltoBcd (\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
186UINT8\r
187BcdToDecimal (\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
203VOID\r
204ConvertEfiTimeToRtcTime (\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
258EFI_STATUS\r
259RtcTimeFieldsValid (\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
284UINT8\r
285RtcRead (\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
293VOID\r
294RtcWrite (\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
304EFI_STATUS\r
305RtcTestCenturyRegister (\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
325VOID\r
326ConvertRtcTimeToEfiTime (\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
367EFI_STATUS\r
368RtcWaitToUpdate (\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
402EFI_STATUS\r
403LibGetTime (\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
498EFI_STATUS\r
499LibSetTime (\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
574EFI_STATUS\r
575libGetWakeupTime (\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
660EFI_STATUS\r
661LibSetWakeupTime (\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
752VOID\r
753LibRtcVirtualAddressChangeEvent (\r
754 VOID\r
755 )\r
756{\r
757}\r
758\r
759\r
760VOID\r
761LibRtcInitialize (\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