]> git.proxmox.com Git - mirror_edk2.git/blob - PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.c
a2a20c8e1cf8c35e388300ed543f4a5df53f6eb0
[mirror_edk2.git] / PcAtChipsetPkg / PcatRealTimeClockRuntimeDxe / PcRtc.c
1 /** @file
2 RTC Architectural Protocol GUID as defined in DxeCis 0.96.
3
4 Copyright (c) 2006 - 2007, Intel Corporation
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "PcRtc.h"
16
17 /**
18 Compare the Hour, Minute and Second of the From time and the To time.
19
20 Only compare H/M/S in EFI_TIME and ignore other fields here.
21
22 @param From the first time
23 @param To the second time
24
25 @return >0 The H/M/S of the From time is later than those of To time
26 @return ==0 The H/M/S of the From time is same as those of To time
27 @return <0 The H/M/S of the From time is earlier than those of To time
28 **/
29 INTN
30 CompareHMS (
31 IN EFI_TIME *From,
32 IN EFI_TIME *To
33 );
34
35 /**
36 To check if second date is later than first date within 24 hours.
37
38 @param From the first date
39 @param To the second date
40
41 @retval TRUE From is previous to To within 24 hours.
42 @retval FALSE From is later, or it is previous to To more than 24 hours.
43 **/
44 BOOLEAN
45 IsWithinOneDay (
46 IN EFI_TIME *From,
47 IN EFI_TIME *To
48 );
49
50 /**
51 Read RTC content through its registers.
52
53 @param Address Address offset of RTC. It is recommended to use macros such as
54 RTC_ADDRESS_SECONDS.
55
56 @return The data of UINT8 type read from RTC.
57 **/
58 UINT8
59 RtcRead (
60 IN UINT8 Address
61 )
62 {
63 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));
64 return IoRead8 (PCAT_RTC_DATA_REGISTER);
65 }
66
67 /**
68 Write RTC through its registers.
69
70 @param Address Address offset of RTC. It is recommended to use macros such as
71 RTC_ADDRESS_SECONDS.
72 @param Data The content you want to write into RTC.
73
74 **/
75 VOID
76 RtcWrite (
77 IN UINT8 Address,
78 IN UINT8 Data
79 )
80 {
81 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));
82 IoWrite8 (PCAT_RTC_DATA_REGISTER, Data);
83 }
84
85 /**
86 Initialize RTC.
87
88 @param Global For global use inside this module.
89
90 @retval EFI_DEVICE_ERROR Initialization failed due to device error.
91 @retval EFI_SUCCESS Initialization successful.
92
93 **/
94 EFI_STATUS
95 PcRtcInit (
96 IN PC_RTC_MODULE_GLOBALS *Global
97 )
98 {
99 EFI_STATUS Status;
100 RTC_REGISTER_A RegisterA;
101 RTC_REGISTER_B RegisterB;
102 RTC_REGISTER_D RegisterD;
103 UINT8 Century;
104 EFI_TIME Time;
105 UINTN DataSize;
106 UINT32 TimerVar;
107
108 //
109 // Acquire RTC Lock to make access to RTC atomic
110 //
111 if (!EfiAtRuntime ()) {
112 EfiAcquireLock (&Global->RtcLock);
113 }
114 //
115 // Initialize RTC Register
116 //
117 // Make sure Division Chain is properly configured,
118 // or RTC clock won't "tick" -- time won't increment
119 //
120 RegisterA.Data = RTC_INIT_REGISTER_A;
121 RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);
122
123 //
124 // Read Register B
125 //
126 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
127
128 //
129 // Clear RTC flag register
130 //
131 RtcRead (RTC_ADDRESS_REGISTER_C);
132
133 //
134 // Clear RTC register D
135 //
136 RegisterD.Data = RTC_INIT_REGISTER_D;
137 RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data);
138
139 //
140 // Wait for up to 0.1 seconds for the RTC to be updated
141 //
142 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
143 if (EFI_ERROR (Status)) {
144 if (!EfiAtRuntime ()) {
145 EfiReleaseLock (&Global->RtcLock);
146 }
147 return EFI_DEVICE_ERROR;
148 }
149 //
150 // Get the Time/Date/Daylight Savings values.
151 //
152 Time.Second = RtcRead (RTC_ADDRESS_SECONDS);
153 Time.Minute = RtcRead (RTC_ADDRESS_MINUTES);
154 Time.Hour = RtcRead (RTC_ADDRESS_HOURS);
155 Time.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
156 Time.Month = RtcRead (RTC_ADDRESS_MONTH);
157 Time.Year = RtcRead (RTC_ADDRESS_YEAR);
158
159 Century = RtcRead (RTC_ADDRESS_CENTURY);
160
161 //
162 // Set RTC configuration after get original time
163 // The value of bit AIE should be reserved.
164 //
165 RtcWrite (RTC_ADDRESS_REGISTER_B, (UINT8)(RTC_INIT_REGISTER_B | (RegisterB.Data & BIT5)));
166
167 //
168 // Release RTC Lock.
169 //
170 if (!EfiAtRuntime ()) {
171 EfiReleaseLock (&Global->RtcLock);
172 }
173
174 //
175 // Get the data of Daylight saving and time zone, if they have been
176 // stored in NV variable during previous boot.
177 //
178 DataSize = sizeof (UINT32);
179 Status = EfiGetVariable (
180 L"RTC",
181 &gEfiCallerIdGuid,
182 NULL,
183 &DataSize,
184 (VOID *) &TimerVar
185 );
186 if (!EFI_ERROR (Status)) {
187 Time.TimeZone = (INT16) TimerVar;
188 Time.Daylight = (UINT8) (TimerVar >> 16);
189 } else {
190 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
191 Time.Daylight = 0;
192 }
193
194 //
195 // Validate time fields
196 //
197 Status = ConvertRtcTimeToEfiTime (&Time, Century, RegisterB);
198 if (!EFI_ERROR (Status)) {
199 Status = RtcTimeFieldsValid (&Time);
200 }
201 if (EFI_ERROR (Status)) {
202 Time.Second = RTC_INIT_SECOND;
203 Time.Minute = RTC_INIT_MINUTE;
204 Time.Hour = RTC_INIT_HOUR;
205 Time.Day = RTC_INIT_DAY;
206 Time.Month = RTC_INIT_MONTH;
207 Time.Year = RTC_INIT_YEAR;
208 }
209
210 //
211 // Reset time value according to new RTC configuration
212 //
213 PcRtcSetTime (&Time, Global);
214
215 return EFI_SUCCESS;
216 }
217
218 /**
219 Returns the current time and date information, and the time-keeping capabilities
220 of the hardware platform.
221
222 @param Time A pointer to storage to receive a snapshot of the current time.
223 @param Capabilities An optional pointer to a buffer to receive the real time clock
224 device's capabilities.
225 @param Global For global use inside this module.
226
227 @retval EFI_SUCCESS The operation completed successfully.
228 @retval EFI_INVALID_PARAMETER Time is NULL.
229 @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.
230
231 **/
232 EFI_STATUS
233 PcRtcGetTime (
234 OUT EFI_TIME *Time,
235 OUT EFI_TIME_CAPABILITIES *Capabilities, OPTIONAL
236 IN PC_RTC_MODULE_GLOBALS *Global
237 )
238 {
239 EFI_STATUS Status;
240 RTC_REGISTER_B RegisterB;
241 UINT8 Century;
242
243 //
244 // Check parameters for null pointer
245 //
246 if (Time == NULL) {
247 return EFI_INVALID_PARAMETER;
248
249 }
250 //
251 // Acquire RTC Lock to make access to RTC atomic
252 //
253 if (!EfiAtRuntime ()) {
254 EfiAcquireLock (&Global->RtcLock);
255 }
256 //
257 // Wait for up to 0.1 seconds for the RTC to be updated
258 //
259 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
260 if (EFI_ERROR (Status)) {
261 if (!EfiAtRuntime ()) {
262 EfiReleaseLock (&Global->RtcLock);
263 }
264 return Status;
265 }
266 //
267 // Read Register B
268 //
269 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
270
271 //
272 // Get the Time/Date/Daylight Savings values.
273 //
274 Time->Second = RtcRead (RTC_ADDRESS_SECONDS);
275 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES);
276 Time->Hour = RtcRead (RTC_ADDRESS_HOURS);
277 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
278 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
279 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
280
281 Century = RtcRead (RTC_ADDRESS_CENTURY);
282
283 //
284 // Release RTC Lock.
285 //
286 if (!EfiAtRuntime ()) {
287 EfiReleaseLock (&Global->RtcLock);
288 }
289
290 //
291 // Get the variable that contains the TimeZone and Daylight fields
292 //
293 Time->TimeZone = Global->SavedTimeZone;
294 Time->Daylight = Global->Daylight;
295
296 //
297 // Make sure all field values are in correct range
298 //
299 Status = ConvertRtcTimeToEfiTime (Time, Century, RegisterB);
300 if (!EFI_ERROR (Status)) {
301 Status = RtcTimeFieldsValid (Time);
302 }
303 if (EFI_ERROR (Status)) {
304 return EFI_DEVICE_ERROR;
305 }
306
307 //
308 // Fill in Capabilities if it was passed in
309 //
310 if (Capabilities != NULL) {
311 Capabilities->Resolution = 1;
312 //
313 // 1 hertz
314 //
315 Capabilities->Accuracy = 50000000;
316 //
317 // 50 ppm
318 //
319 Capabilities->SetsToZero = FALSE;
320 }
321
322 return EFI_SUCCESS;
323 }
324
325 /**
326 Sets the current local time and date information.
327
328 @param Time A pointer to the current time.
329 @param Global For global use inside this module.
330
331 @retval EFI_SUCCESS The operation completed successfully.
332 @retval EFI_INVALID_PARAMETER A time field is out of range.
333 @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.
334
335 **/
336 EFI_STATUS
337 PcRtcSetTime (
338 IN EFI_TIME *Time,
339 IN PC_RTC_MODULE_GLOBALS *Global
340 )
341 {
342 EFI_STATUS Status;
343 EFI_TIME RtcTime;
344 RTC_REGISTER_B RegisterB;
345 UINT8 Century;
346 UINT32 TimerVar;
347
348 if (Time == NULL) {
349 return EFI_INVALID_PARAMETER;
350 }
351 //
352 // Make sure that the time fields are valid
353 //
354 Status = RtcTimeFieldsValid (Time);
355 if (EFI_ERROR (Status)) {
356 return Status;
357 }
358
359 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
360
361 //
362 // Acquire RTC Lock to make access to RTC atomic
363 //
364 if (!EfiAtRuntime ()) {
365 EfiAcquireLock (&Global->RtcLock);
366 }
367 //
368 // Wait for up to 0.1 seconds for the RTC to be updated
369 //
370 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
371 if (EFI_ERROR (Status)) {
372 if (!EfiAtRuntime ()) {
373 EfiReleaseLock (&Global->RtcLock);
374 }
375 return Status;
376 }
377 //
378 // Read Register B, and inhibit updates of the RTC
379 //
380 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
381 RegisterB.Bits.SET = 1;
382 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
383
384 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);
385
386 RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second);
387 RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute);
388 RtcWrite (RTC_ADDRESS_HOURS, RtcTime.Hour);
389 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH, RtcTime.Day);
390 RtcWrite (RTC_ADDRESS_MONTH, RtcTime.Month);
391 RtcWrite (RTC_ADDRESS_YEAR, (UINT8) RtcTime.Year);
392 RtcWrite (RTC_ADDRESS_CENTURY, Century);
393
394 //
395 // Allow updates of the RTC registers
396 //
397 RegisterB.Bits.SET = 0;
398 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
399
400 //
401 // Release RTC Lock.
402 //
403 if (!EfiAtRuntime ()) {
404 EfiReleaseLock (&Global->RtcLock);
405 }
406 //
407 // Set the variable that contains the TimeZone and Daylight fields
408 //
409 Global->SavedTimeZone = Time->TimeZone;
410 Global->Daylight = Time->Daylight;
411
412 TimerVar = Time->Daylight;
413 TimerVar = (UINT32) ((TimerVar << 16) | Time->TimeZone);
414 Status = EfiSetVariable (
415 L"RTC",
416 &gEfiCallerIdGuid,
417 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
418 sizeof (TimerVar),
419 &TimerVar
420 );
421 ASSERT_EFI_ERROR (Status);
422
423 return EFI_SUCCESS;
424 }
425
426 /**
427 Returns the current wakeup alarm clock setting.
428
429 @param Enabled Indicates if the alarm is currently enabled or disabled.
430 @param Pending Indicates if the alarm signal is pending and requires acknowledgment.
431 @param Time The current alarm setting.
432 @param Global For global use inside this module.
433
434 @retval EFI_SUCCESS The alarm settings were returned.
435 @retval EFI_INVALID_PARAMETER Enabled is NULL.
436 @retval EFI_INVALID_PARAMETER Pending is NULL.
437 @retval EFI_INVALID_PARAMETER Time is NULL.
438 @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.
439 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
440
441 **/
442 EFI_STATUS
443 PcRtcGetWakeupTime (
444 OUT BOOLEAN *Enabled,
445 OUT BOOLEAN *Pending,
446 OUT EFI_TIME *Time,
447 IN PC_RTC_MODULE_GLOBALS *Global
448 )
449 {
450 EFI_STATUS Status;
451 RTC_REGISTER_B RegisterB;
452 RTC_REGISTER_C RegisterC;
453 UINT8 Century;
454
455 //
456 // Check parameters for null pointers
457 //
458 if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) {
459 return EFI_INVALID_PARAMETER;
460
461 }
462 //
463 // Acquire RTC Lock to make access to RTC atomic
464 //
465 if (!EfiAtRuntime ()) {
466 EfiAcquireLock (&Global->RtcLock);
467 }
468 //
469 // Wait for up to 0.1 seconds for the RTC to be updated
470 //
471 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
472 if (EFI_ERROR (Status)) {
473 if (!EfiAtRuntime ()) {
474 EfiReleaseLock (&Global->RtcLock);
475 }
476 return EFI_DEVICE_ERROR;
477 }
478 //
479 // Read Register B and Register C
480 //
481 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
482 RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);
483
484 //
485 // Get the Time/Date/Daylight Savings values.
486 //
487 *Enabled = RegisterB.Bits.AIE;
488 if (*Enabled) {
489 Time->Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);
490 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);
491 Time->Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);
492 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
493 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
494 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
495 } else {
496 Time->Second = 0;
497 Time->Minute = 0;
498 Time->Hour = 0;
499 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
500 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
501 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
502 }
503
504 Century = RtcRead (RTC_ADDRESS_CENTURY);
505
506 //
507 // Release RTC Lock.
508 //
509 if (!EfiAtRuntime ()) {
510 EfiReleaseLock (&Global->RtcLock);
511 }
512
513 //
514 // Get the variable that contains the TimeZone and Daylight fields
515 //
516 Time->TimeZone = Global->SavedTimeZone;
517 Time->Daylight = Global->Daylight;
518
519 //
520 // Make sure all field values are in correct range
521 //
522 Status = ConvertRtcTimeToEfiTime (Time, Century, RegisterB);
523 if (!EFI_ERROR (Status)) {
524 Status = RtcTimeFieldsValid (Time);
525 }
526 if (EFI_ERROR (Status)) {
527 return EFI_DEVICE_ERROR;
528 }
529
530 *Pending = RegisterC.Bits.AF;
531
532 return EFI_SUCCESS;
533 }
534
535 /**
536 Sets the system wakeup alarm clock time.
537
538 @param Enabled Enable or disable the wakeup alarm.
539 @param Time If Enable is TRUE, the time to set the wakeup alarm for.
540 If Enable is FALSE, then this parameter is optional, and may be NULL.
541 @param Global For global use inside this module.
542
543 @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled.
544 If Enable is FALSE, then the wakeup alarm was disabled.
545 @retval EFI_INVALID_PARAMETER A time field is out of range.
546 @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.
547 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
548
549 **/
550 EFI_STATUS
551 PcRtcSetWakeupTime (
552 IN BOOLEAN Enable,
553 IN EFI_TIME *Time, OPTIONAL
554 IN PC_RTC_MODULE_GLOBALS *Global
555 )
556 {
557 EFI_STATUS Status;
558 EFI_TIME RtcTime;
559 RTC_REGISTER_B RegisterB;
560 UINT8 Century;
561 EFI_TIME_CAPABILITIES Capabilities;
562
563 if (Enable) {
564
565 if (Time == NULL) {
566 return EFI_INVALID_PARAMETER;
567 }
568 //
569 // Make sure that the time fields are valid
570 //
571 Status = RtcTimeFieldsValid (Time);
572 if (EFI_ERROR (Status)) {
573 return EFI_INVALID_PARAMETER;
574 }
575 //
576 // Just support set alarm time within 24 hours
577 //
578 PcRtcGetTime (&RtcTime, &Capabilities, Global);
579 Status = RtcTimeFieldsValid (&RtcTime);
580 if (EFI_ERROR (Status)) {
581 return EFI_DEVICE_ERROR;
582 }
583 if (!IsWithinOneDay (&RtcTime, Time)) {
584 return EFI_UNSUPPORTED;
585 }
586 //
587 // Make a local copy of the time and date
588 //
589 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
590
591 }
592 //
593 // Acquire RTC Lock to make access to RTC atomic
594 //
595 if (!EfiAtRuntime ()) {
596 EfiAcquireLock (&Global->RtcLock);
597 }
598 //
599 // Wait for up to 0.1 seconds for the RTC to be updated
600 //
601 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
602 if (EFI_ERROR (Status)) {
603 if (!EfiAtRuntime ()) {
604 EfiReleaseLock (&Global->RtcLock);
605 }
606 return EFI_DEVICE_ERROR;
607 }
608 //
609 // Read Register B, and inhibit updates of the RTC
610 //
611 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
612
613 RegisterB.Bits.SET = 1;
614 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
615
616 if (Enable) {
617 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);
618
619 //
620 // Set RTC alarm time
621 //
622 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);
623 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);
624 RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);
625
626 RegisterB.Bits.AIE = 1;
627
628 } else {
629 RegisterB.Bits.AIE = 0;
630 }
631 //
632 // Allow updates of the RTC registers
633 //
634 RegisterB.Bits.SET = 0;
635 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
636
637 //
638 // Release RTC Lock.
639 //
640 if (!EfiAtRuntime ()) {
641 EfiReleaseLock (&Global->RtcLock);
642 }
643 return EFI_SUCCESS;
644 }
645
646
647 /**
648 Checks an 8-bit BCD value, and converts to an 8-bit value if valid.
649
650 This function checks the 8-bit BCD value specified by Value.
651 If valid, the function converts it to an 8-bit value and returns it.
652 Otherwise, return 0xff.
653
654 @param Value The 8-bit BCD value to check and convert
655
656 @return The 8-bit value converted. Or 0xff if Value is invalid.
657
658 **/
659 UINT8
660 CheckAndConvertBcd8ToDecimal8 (
661 IN UINT8 Value
662 )
663 {
664 if ((Value < 0xa0) && ((Value & 0xf) < 0xa)) {
665 return BcdToDecimal8 (Value);
666 }
667
668 return 0xff;
669 }
670
671 /**
672 Converts time read from RTC to EFI_TIME format defined by UEFI spec.
673
674 This function converts raw time data read from RTC to the EFI_TIME format
675 defined by UEFI spec.
676 If data mode of RTC is BCD, then converts it to decimal,
677 If RTC is in 12-hour format, then converts it to 24-hour format.
678
679 @param Time On input, the time data read from RTC to convert
680 On output, the time converted to UEFI format
681 @param Century Value of century read from RTC.
682 @param RegisterB Value of Register B of RTC, indicating data mode
683 and hour format.
684
685 @retval EFI_INVALID_PARAMETER Parameters passed in are invalid.
686 @retval EFI_SUCCESS Convert RTC time to EFI time successfully.
687
688 **/
689 EFI_STATUS
690 ConvertRtcTimeToEfiTime (
691 IN OUT EFI_TIME *Time,
692 IN UINT8 Century,
693 IN RTC_REGISTER_B RegisterB
694 )
695 {
696 BOOLEAN IsPM;
697
698 if ((Time->Hour & 0x80) != 0) {
699 IsPM = TRUE;
700 } else {
701 IsPM = FALSE;
702 }
703
704 Time->Hour = (UINT8) (Time->Hour & 0x7f);
705
706 if (RegisterB.Bits.DM == 0) {
707 Time->Year = CheckAndConvertBcd8ToDecimal8 ((UINT8) Time->Year);
708 Time->Month = CheckAndConvertBcd8ToDecimal8 (Time->Month);
709 Time->Day = CheckAndConvertBcd8ToDecimal8 (Time->Day);
710 Time->Hour = CheckAndConvertBcd8ToDecimal8 (Time->Hour);
711 Time->Minute = CheckAndConvertBcd8ToDecimal8 (Time->Minute);
712 Time->Second = CheckAndConvertBcd8ToDecimal8 (Time->Second);
713 }
714 Century = CheckAndConvertBcd8ToDecimal8 (Century);
715
716 if (Time->Year == 0xff || Time->Month == 0xff || Time->Day == 0xff ||
717 Time->Hour == 0xff || Time->Minute == 0xff || Time->Second == 0xff ||
718 Century == 0xff) {
719 return EFI_INVALID_PARAMETER;
720 }
721
722 Time->Year = (UINT16) (Century * 100 + Time->Year);
723
724 //
725 // If time is in 12 hour format, convert it to 24 hour format
726 //
727 if (RegisterB.Bits.MIL == 0) {
728 if (IsPM && Time->Hour < 12) {
729 Time->Hour = (UINT8) (Time->Hour + 12);
730 }
731
732 if (!IsPM && Time->Hour == 12) {
733 Time->Hour = 0;
734 }
735 }
736
737 Time->Nanosecond = 0;
738
739 return EFI_SUCCESS;
740 }
741
742 /**
743 Wait for a period for the RTC to be ready.
744
745 @param Timeout Tell how long it should take to wait.
746
747 @retval EFI_DEVICE_ERROR RTC device error.
748 @retval EFI_SUCCESS RTC is updated and ready.
749 **/
750 EFI_STATUS
751 RtcWaitToUpdate (
752 UINTN Timeout
753 )
754 {
755 RTC_REGISTER_A RegisterA;
756 RTC_REGISTER_D RegisterD;
757
758 //
759 // See if the RTC is functioning correctly
760 //
761 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
762
763 if (RegisterD.Bits.VRT == 0) {
764 return EFI_DEVICE_ERROR;
765 }
766 //
767 // Wait for up to 0.1 seconds for the RTC to be ready.
768 //
769 Timeout = (Timeout / 10) + 1;
770 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
771 while (RegisterA.Bits.UIP == 1 && Timeout > 0) {
772 MicroSecondDelay (10);
773 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
774 Timeout--;
775 }
776
777 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
778 if (Timeout == 0 || RegisterD.Bits.VRT == 0) {
779 return EFI_DEVICE_ERROR;
780 }
781
782 return EFI_SUCCESS;
783 }
784
785 /**
786 See if all fields of a variable of EFI_TIME type is correct.
787
788 @param Time The time to be checked.
789
790 @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.
791 @retval EFI_SUCCESS Time is a valid EFI_TIME variable.
792
793 **/
794 EFI_STATUS
795 RtcTimeFieldsValid (
796 IN EFI_TIME *Time
797 )
798 {
799 if (Time->Year < 1998 ||
800 Time->Year > 2099 ||
801 Time->Month < 1 ||
802 Time->Month > 12 ||
803 (!DayValid (Time)) ||
804 Time->Hour > 23 ||
805 Time->Minute > 59 ||
806 Time->Second > 59 ||
807 Time->Nanosecond > 999999999 ||
808 (!(Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE || (Time->TimeZone >= -1440 && Time->TimeZone <= 1440))) ||
809 ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0)) {
810 return EFI_INVALID_PARAMETER;
811 }
812
813 return EFI_SUCCESS;
814 }
815
816 /**
817 See if field Day of an EFI_TIME is correct.
818
819 @param Time Its Day field is to be checked.
820
821 @retval TRUE Day field of Time is correct.
822 @retval FALSE Day field of Time is NOT correct.
823 **/
824 BOOLEAN
825 DayValid (
826 IN EFI_TIME *Time
827 )
828 {
829 INTN DayOfMonth[12];
830
831 DayOfMonth[0] = 31;
832 DayOfMonth[1] = 29;
833 DayOfMonth[2] = 31;
834 DayOfMonth[3] = 30;
835 DayOfMonth[4] = 31;
836 DayOfMonth[5] = 30;
837 DayOfMonth[6] = 31;
838 DayOfMonth[7] = 31;
839 DayOfMonth[8] = 30;
840 DayOfMonth[9] = 31;
841 DayOfMonth[10] = 30;
842 DayOfMonth[11] = 31;
843
844 //
845 // The validity of Time->Month field should be checked before
846 //
847 ASSERT (Time->Month >=1);
848 ASSERT (Time->Month <=12);
849 if (Time->Day < 1 ||
850 Time->Day > DayOfMonth[Time->Month - 1] ||
851 (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28))
852 ) {
853 return FALSE;
854 }
855
856 return TRUE;
857 }
858
859 /**
860 Check if it is a leap year.
861
862 @param Time The time to be checked.
863
864 @retval TRUE It is a leap year.
865 @retval FALSE It is NOT a leap year.
866 **/
867 BOOLEAN
868 IsLeapYear (
869 IN EFI_TIME *Time
870 )
871 {
872 if (Time->Year % 4 == 0) {
873 if (Time->Year % 100 == 0) {
874 if (Time->Year % 400 == 0) {
875 return TRUE;
876 } else {
877 return FALSE;
878 }
879 } else {
880 return TRUE;
881 }
882 } else {
883 return FALSE;
884 }
885 }
886
887 /**
888 Converts time from EFI_TIME format defined by UEFI spec to RTC's.
889
890 This function converts time from EFI_TIME format defined by UEFI spec to RTC's.
891 If data mode of RTC is BCD, then converts EFI_TIME to it.
892 If RTC is in 12-hour format, then converts EFI_TIME to it.
893
894 @param Time On input, the time data read from UEFI to convert
895 On output, the time converted to RTC format
896 @param RegisterB Value of Register B of RTC, indicating data mode
897 @param Century It is set according to EFI_TIME Time.
898
899 **/
900 VOID
901 ConvertEfiTimeToRtcTime (
902 IN OUT EFI_TIME *Time,
903 IN RTC_REGISTER_B RegisterB,
904 OUT UINT8 *Century
905 )
906 {
907 BOOLEAN IsPM;
908
909 IsPM = TRUE;
910 //
911 // Adjust hour field if RTC is in 12 hour mode
912 //
913 if (RegisterB.Bits.MIL == 0) {
914 if (Time->Hour < 12) {
915 IsPM = FALSE;
916 }
917
918 if (Time->Hour >= 13) {
919 Time->Hour = (UINT8) (Time->Hour - 12);
920 } else if (Time->Hour == 0) {
921 Time->Hour = 12;
922 }
923 }
924 //
925 // Set the Time/Date/Daylight Savings values.
926 //
927 *Century = DecimalToBcd8 ((UINT8) (Time->Year / 100));
928
929 Time->Year = (UINT16) (Time->Year % 100);
930
931 if (RegisterB.Bits.DM == 0) {
932 Time->Year = DecimalToBcd8 ((UINT8) Time->Year);
933 Time->Month = DecimalToBcd8 (Time->Month);
934 Time->Day = DecimalToBcd8 (Time->Day);
935 Time->Hour = DecimalToBcd8 (Time->Hour);
936 Time->Minute = DecimalToBcd8 (Time->Minute);
937 Time->Second = DecimalToBcd8 (Time->Second);
938 }
939 //
940 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
941 //
942 if (RegisterB.Bits.MIL == 0 && IsPM) {
943 Time->Hour = (UINT8) (Time->Hour | 0x80);
944 }
945 }
946
947 /**
948 Compare the Hour, Minute and Second of the From time and the To time.
949
950 Only compare H/M/S in EFI_TIME and ignore other fields here.
951
952 @param From the first time
953 @param To the second time
954
955 @return >0 The H/M/S of the From time is later than those of To time
956 @return ==0 The H/M/S of the From time is same as those of To time
957 @return <0 The H/M/S of the From time is earlier than those of To time
958 **/
959 INTN
960 CompareHMS (
961 IN EFI_TIME *From,
962 IN EFI_TIME *To
963 )
964 {
965 if ((From->Hour > To->Hour) ||
966 ((From->Hour == To->Hour) && (From->Minute > To->Minute)) ||
967 ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second))) {
968 return 1;
969 } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) {
970 return 0;
971 } else {
972 return -1;
973 }
974 }
975
976 /**
977 To check if second date is later than first date within 24 hours.
978
979 @param From the first date
980 @param To the second date
981
982 @retval TRUE From is previous to To within 24 hours.
983 @retval FALSE From is later, or it is previous to To more than 24 hours.
984 **/
985 BOOLEAN
986 IsWithinOneDay (
987 IN EFI_TIME *From,
988 IN EFI_TIME *To
989 )
990 {
991 UINT8 DayOfMonth[12];
992 BOOLEAN Adjacent;
993
994 DayOfMonth[0] = 31;
995 DayOfMonth[1] = 29;
996 DayOfMonth[2] = 31;
997 DayOfMonth[3] = 30;
998 DayOfMonth[4] = 31;
999 DayOfMonth[5] = 30;
1000 DayOfMonth[6] = 31;
1001 DayOfMonth[7] = 31;
1002 DayOfMonth[8] = 30;
1003 DayOfMonth[9] = 31;
1004 DayOfMonth[10] = 30;
1005 DayOfMonth[11] = 31;
1006
1007 Adjacent = FALSE;
1008
1009 //
1010 // The validity of From->Month field should be checked before
1011 //
1012 ASSERT (From->Month >=1);
1013 ASSERT (From->Month <=12);
1014
1015 if (From->Year == To->Year) {
1016 if (From->Month == To->Month) {
1017 if ((From->Day + 1) == To->Day) {
1018 if ((CompareHMS(From, To) >= 0)) {
1019 Adjacent = TRUE;
1020 }
1021 } else if (From->Day == To->Day) {
1022 if ((CompareHMS(From, To) <= 0)) {
1023 Adjacent = TRUE;
1024 }
1025 }
1026 } else if (((From->Month + 1) == To->Month) && (To->Day == 1)) {
1027 if ((From->Month == 2) && !IsLeapYear(From)) {
1028 if (From->Day == 28) {
1029 if ((CompareHMS(From, To) >= 0)) {
1030 Adjacent = TRUE;
1031 }
1032 }
1033 } else if (From->Day == DayOfMonth[From->Month - 1]) {
1034 if ((CompareHMS(From, To) >= 0)) {
1035 Adjacent = TRUE;
1036 }
1037 }
1038 }
1039 } else if (((From->Year + 1) == To->Year) &&
1040 (From->Month == 12) &&
1041 (From->Day == 31) &&
1042 (To->Month == 1) &&
1043 (To->Day == 1)) {
1044 if ((CompareHMS(From, To) >= 0)) {
1045 Adjacent = TRUE;
1046 }
1047 }
1048
1049 return Adjacent;
1050 }
1051