TianoCore EDK2 master
Loading...
Searching...
No Matches
PcRtc.c
Go to the documentation of this file.
1
12#include "PcRtc.h"
13
14extern UINTN mRtcIndexRegister;
15extern UINTN mRtcTargetRegister;
16extern UINT16 mRtcDefaultYear;
17extern UINT16 mMinimalValidYear;
18extern UINT16 mMaximalValidYear;
19//
20// Days of month.
21//
22UINTN mDayOfMonth[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
23
24//
25// The name of NV variable to store the timezone and daylight saving information.
26//
27CHAR16 mTimeZoneVariableName[] = L"RTC";
28
41INTN
43 IN EFI_TIME *From,
44 IN EFI_TIME *To
45 );
46
56BOOLEAN
58 IN EFI_TIME *From,
59 IN EFI_TIME *To
60 );
61
71UINT8
73 IN UINTN Address
74 )
75{
76 IoWrite8 (
77 mRtcIndexRegister,
78 (UINT8)(Address | (UINT8)(IoRead8 (mRtcIndexRegister) & 0x80))
79 );
80 return IoRead8 (mRtcTargetRegister);
81}
82
92VOID
94 IN UINTN Address,
95 IN UINT8 Data
96 )
97{
98 IoWrite8 (
99 mRtcIndexRegister,
100 (UINT8)(Address | (UINT8)(IoRead8 (mRtcIndexRegister) & 0x80))
101 );
102 IoWrite8 (mRtcTargetRegister, Data);
103}
104
113STATIC
114UINT8
116 IN UINTN Address
117 )
118{
119 MmioWrite8 (
120 mRtcIndexRegister,
121 (UINT8)(Address | (UINT8)(MmioRead8 (mRtcIndexRegister) & 0x80))
122 );
123 return MmioRead8 (mRtcTargetRegister);
124}
125
134STATIC
135VOID
137 IN UINTN Address,
138 IN UINT8 Data
139 )
140{
141 MmioWrite8 (
142 mRtcIndexRegister,
143 (UINT8)(Address | (UINT8)(MmioRead8 (mRtcIndexRegister) & 0x80))
144 );
145 MmioWrite8 (mRtcTargetRegister, Data);
146}
147
156STATIC
157UINT8
159 IN UINTN Address
160 )
161{
162 if (FeaturePcdGet (PcdRtcUseMmio)) {
163 return MmioRtcRead (Address);
164 }
165
166 return IoRtcRead (Address);
167}
168
177STATIC
178VOID
180 IN UINTN Address,
181 IN UINT8 Data
182 )
183{
184 if (FeaturePcdGet (PcdRtcUseMmio)) {
185 MmioRtcWrite (Address, Data);
186 } else {
187 IoRtcWrite (Address, Data);
188 }
189}
190
204 IN INT16 TimeZone,
205 IN UINT8 Daylight,
207 )
208{
209 EFI_STATUS Status;
210 UINT32 TimerVar;
211
212 ASSERT ((TimeZone == EFI_UNSPECIFIED_TIMEZONE) || ((TimeZone >= -1440) && (TimeZone <= 1440)));
213 ASSERT ((Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) == 0);
214
215 //
216 // Write timezone and daylight to RTC variable
217 //
218 if ((TimeZone == EFI_UNSPECIFIED_TIMEZONE) && (Daylight == 0)) {
219 Status = EfiSetVariable (
220 mTimeZoneVariableName,
221 &gEfiCallerIdGuid,
222 0,
223 0,
224 NULL
225 );
226 if (Status == EFI_NOT_FOUND) {
227 Status = EFI_SUCCESS;
228 }
229 } else {
230 TimerVar = Daylight;
231 TimerVar = (UINT32)((TimerVar << 16) | (UINT16)(TimeZone));
232 Status = EfiSetVariable (
233 mTimeZoneVariableName,
234 &gEfiCallerIdGuid,
235 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
236 sizeof (TimerVar),
237 &TimerVar
238 );
239 }
240
241 //
242 // Set the variable that contains the TimeZone and Daylight fields
243 //
244 if (!EFI_ERROR (Status)) {
245 Global->SavedTimeZone = TimeZone;
246 Global->Daylight = Daylight;
247 }
248
249 return Status;
250}
251
264 )
265{
266 EFI_STATUS Status;
267 RTC_REGISTER_A RegisterA;
268 RTC_REGISTER_B RegisterB;
269 RTC_REGISTER_D RegisterD;
270 EFI_TIME Time;
271 UINTN DataSize;
272 UINT32 TimerVar;
273 BOOLEAN Enabled;
274 BOOLEAN Pending;
275 BOOLEAN NeedRtcUpdate;
276
277 NeedRtcUpdate = FALSE;
278
279 //
280 // Acquire RTC Lock to make access to RTC atomic
281 //
282 if (!EfiAtRuntime ()) {
283 EfiAcquireLock (&Global->RtcLock);
284 }
285
286 //
287 // Initialize RTC Register
288 //
289 // Make sure Division Chain is properly configured,
290 // or RTC clock won't "tick" -- time won't increment
291 //
292 RegisterA.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterA);
293 RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);
294
295 //
296 // Read Register B
297 //
298 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
299
300 //
301 // Clear RTC flag register
302 //
303 RtcRead (RTC_ADDRESS_REGISTER_C);
304
305 //
306 // Clear RTC register D
307 //
308 RegisterD.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterD);
309 RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data);
310
311 //
312 // Wait for up to 0.1 seconds for the RTC to be updated
313 //
314 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
315 if (EFI_ERROR (Status)) {
316 //
317 // Set the variable with default value if the RTC is functioning incorrectly.
318 //
319 Global->SavedTimeZone = EFI_UNSPECIFIED_TIMEZONE;
320 Global->Daylight = 0;
321 if (!EfiAtRuntime ()) {
322 EfiReleaseLock (&Global->RtcLock);
323 }
324
325 return EFI_DEVICE_ERROR;
326 }
327
328 //
329 // Get the Time/Date/Daylight Savings values.
330 //
331 Time.Second = RtcRead (RTC_ADDRESS_SECONDS);
332 Time.Minute = RtcRead (RTC_ADDRESS_MINUTES);
333 Time.Hour = RtcRead (RTC_ADDRESS_HOURS);
334 Time.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
335 Time.Month = RtcRead (RTC_ADDRESS_MONTH);
336 Time.Year = RtcRead (RTC_ADDRESS_YEAR);
337
338 //
339 // Release RTC Lock.
340 //
341 if (!EfiAtRuntime ()) {
342 EfiReleaseLock (&Global->RtcLock);
343 }
344
345 //
346 // Get the data of Daylight saving and time zone, if they have been
347 // stored in NV variable during previous boot.
348 //
349 DataSize = sizeof (UINT32);
350 Status = EfiGetVariable (
351 mTimeZoneVariableName,
352 &gEfiCallerIdGuid,
353 NULL,
354 &DataSize,
355 &TimerVar
356 );
357 if (!EFI_ERROR (Status)) {
358 Time.TimeZone = (INT16)TimerVar;
359 Time.Daylight = (UINT8)(TimerVar >> 16);
360 } else {
361 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
362 Time.Daylight = 0;
363 }
364
365 //
366 // Validate time fields
367 //
368 Status = ConvertRtcTimeToEfiTime (&Time, RegisterB);
369 if (!EFI_ERROR (Status)) {
370 Status = RtcTimeFieldsValid (&Time);
371 }
372
373 if (EFI_ERROR (Status)) {
374 //
375 // Report Status Code to indicate that the RTC has bad date and time
376 //
378 EFI_ERROR_CODE | EFI_ERROR_MINOR,
379 (EFI_SOFTWARE_DXE_RT_DRIVER | EFI_SW_EC_BAD_DATE_TIME)
380 );
381 Time.Second = RTC_INIT_SECOND;
382 Time.Minute = RTC_INIT_MINUTE;
383 Time.Hour = RTC_INIT_HOUR;
384 Time.Day = RTC_INIT_DAY;
385 Time.Month = RTC_INIT_MONTH;
386 Time.Year = MAX (mRtcDefaultYear, mMinimalValidYear);
387 Time.Year = MIN (Time.Year, mMaximalValidYear);
388 Time.Nanosecond = 0;
389 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
390 Time.Daylight = 0;
391 NeedRtcUpdate = TRUE;
392 }
393
394 //
395 // Set RTC configuration after get original time
396 // The value of bit AIE should be reserved.
397 //
398 if ((RegisterB.Data | BIT5) != (FixedPcdGet8 (PcdInitialValueRtcRegisterB) | BIT5)) {
399 RegisterB.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterB) | (RegisterB.Data & BIT5);
400 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
401 NeedRtcUpdate = TRUE;
402 }
403
404 //
405 // Reset time value according to new RTC configuration
406 //
407 if (NeedRtcUpdate) {
408 Status = PcRtcSetTime (&Time, Global);
409 if (EFI_ERROR (Status)) {
410 return EFI_DEVICE_ERROR;
411 }
412 } else {
413 Status = PcRtcSetTimeZone (Time.TimeZone, Time.Daylight, Global);
414 if (EFI_ERROR (Status)) {
415 return EFI_DEVICE_ERROR;
416 }
417 }
418
419 //
420 // Reset wakeup time value to valid state when wakeup alarm is disabled and wakeup time is invalid.
421 // Global variable has already had valid SavedTimeZone and Daylight,
422 // so we can use them to get and set wakeup time.
423 //
424 Status = PcRtcGetWakeupTime (&Enabled, &Pending, &Time, Global);
425 if ((!EFI_ERROR (Status)) || (Enabled)) {
426 return EFI_SUCCESS;
427 }
428
429 //
430 // When wakeup time is disabled and invalid, reset wakeup time register to valid state
431 // but keep wakeup alarm disabled.
432 //
433 Time.Second = RTC_INIT_SECOND;
434 Time.Minute = RTC_INIT_MINUTE;
435 Time.Hour = RTC_INIT_HOUR;
436 Time.Day = RTC_INIT_DAY;
437 Time.Month = RTC_INIT_MONTH;
438 Time.Year = MAX (mRtcDefaultYear, mMinimalValidYear);
439 Time.Year = MIN (Time.Year, mMaximalValidYear);
440 Time.Nanosecond = 0;
441 Time.TimeZone = Global->SavedTimeZone;
442 Time.Daylight = Global->Daylight;
443
444 //
445 // Acquire RTC Lock to make access to RTC atomic
446 //
447 if (!EfiAtRuntime ()) {
448 EfiAcquireLock (&Global->RtcLock);
449 }
450
451 //
452 // Wait for up to 0.1 seconds for the RTC to be updated
453 //
454 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
455 if (EFI_ERROR (Status)) {
456 if (!EfiAtRuntime ()) {
457 EfiReleaseLock (&Global->RtcLock);
458 }
459
460 return EFI_DEVICE_ERROR;
461 }
462
463 ConvertEfiTimeToRtcTime (&Time, RegisterB);
464
465 //
466 // Set the Y/M/D info to variable as it has no corresponding hw registers.
467 //
468 Status = EfiSetVariable (
469 L"RTCALARM",
470 &gEfiCallerIdGuid,
471 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
472 sizeof (Time),
473 &Time
474 );
475 if (EFI_ERROR (Status)) {
476 if (!EfiAtRuntime ()) {
477 EfiReleaseLock (&Global->RtcLock);
478 }
479
480 return EFI_DEVICE_ERROR;
481 }
482
483 //
484 // Inhibit updates of the RTC
485 //
486 RegisterB.Bits.Set = 1;
487 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
488
489 //
490 // Set RTC alarm time registers
491 //
492 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, Time.Second);
493 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, Time.Minute);
494 RtcWrite (RTC_ADDRESS_HOURS_ALARM, Time.Hour);
495
496 //
497 // Allow updates of the RTC registers
498 //
499 RegisterB.Bits.Set = 0;
500 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
501
502 //
503 // Release RTC Lock.
504 //
505 if (!EfiAtRuntime ()) {
506 EfiReleaseLock (&Global->RtcLock);
507 }
508
509 return EFI_SUCCESS;
510}
511
528 OUT EFI_TIME *Time,
529 OUT EFI_TIME_CAPABILITIES *Capabilities OPTIONAL,
531 )
532{
533 EFI_STATUS Status;
534 RTC_REGISTER_B RegisterB;
535
536 //
537 // Check parameters for null pointer
538 //
539 if (Time == NULL) {
540 return EFI_INVALID_PARAMETER;
541 }
542
543 //
544 // Acquire RTC Lock to make access to RTC atomic
545 //
546 if (!EfiAtRuntime ()) {
547 EfiAcquireLock (&Global->RtcLock);
548 }
549
550 //
551 // Wait for up to 0.1 seconds for the RTC to be updated
552 //
553 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
554 if (EFI_ERROR (Status)) {
555 if (!EfiAtRuntime ()) {
556 EfiReleaseLock (&Global->RtcLock);
557 }
558
559 return Status;
560 }
561
562 //
563 // Read Register B
564 //
565 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
566
567 //
568 // Get the Time/Date/Daylight Savings values.
569 //
570 Time->Second = RtcRead (RTC_ADDRESS_SECONDS);
571 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES);
572 Time->Hour = RtcRead (RTC_ADDRESS_HOURS);
573 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
574 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
575 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
576
577 //
578 // Release RTC Lock.
579 //
580 if (!EfiAtRuntime ()) {
581 EfiReleaseLock (&Global->RtcLock);
582 }
583
584 //
585 // Get the variable that contains the TimeZone and Daylight fields
586 //
587 Time->TimeZone = Global->SavedTimeZone;
588 Time->Daylight = Global->Daylight;
589
590 //
591 // Make sure all field values are in correct range
592 //
593 Status = ConvertRtcTimeToEfiTime (Time, RegisterB);
594 if (!EFI_ERROR (Status)) {
595 Status = RtcTimeFieldsValid (Time);
596 }
597
598 if (EFI_ERROR (Status)) {
599 return EFI_DEVICE_ERROR;
600 }
601
602 //
603 // Fill in Capabilities if it was passed in
604 //
605 if (Capabilities != NULL) {
606 Capabilities->Resolution = 1;
607 //
608 // 1 hertz
609 //
610 Capabilities->Accuracy = 50000000;
611 //
612 // 50 ppm
613 //
614 Capabilities->SetsToZero = FALSE;
615 }
616
617 return EFI_SUCCESS;
618}
619
633 IN EFI_TIME *Time,
635 )
636{
637 EFI_STATUS Status;
638 EFI_TIME RtcTime;
639 RTC_REGISTER_A RegisterA;
640 RTC_REGISTER_B RegisterB;
641
642 if (Time == NULL) {
643 return EFI_INVALID_PARAMETER;
644 }
645
646 //
647 // Make sure that the time fields are valid
648 //
649 Status = RtcTimeFieldsValid (Time);
650 if (EFI_ERROR (Status)) {
651 return Status;
652 }
653
654 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
655
656 //
657 // Acquire RTC Lock to make access to RTC atomic
658 //
659 if (!EfiAtRuntime ()) {
660 EfiAcquireLock (&Global->RtcLock);
661 }
662
663 //
664 // Wait for up to 0.1 seconds for the RTC to be updated
665 //
666 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
667 if (EFI_ERROR (Status)) {
668 if (!EfiAtRuntime ()) {
669 EfiReleaseLock (&Global->RtcLock);
670 }
671
672 return Status;
673 }
674
675 Status = PcRtcSetTimeZone (Time->TimeZone, Time->Daylight, Global);
676
677 if (EFI_ERROR (Status)) {
678 if (!EfiAtRuntime ()) {
679 EfiReleaseLock (&Global->RtcLock);
680 }
681
682 return EFI_DEVICE_ERROR;
683 }
684
685 //
686 // Read Register B, and inhibit updates of the RTC
687 //
688 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
689 RegisterB.Bits.Set = 1;
690 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
691
692 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
693 //
694 // Set Divider in Reset status, RTC stops
695 //
696 RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data | RTC_DIV_RESET);
697 //
698 // Store the century value to RTC before converting to BCD format.
699 //
700 if (Global->CenturyRtcAddress != 0) {
701 RtcWrite (Global->CenturyRtcAddress, DecimalToBcd8 ((UINT8)(RtcTime.Year / 100)));
702 }
703
704 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);
705
706 RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second);
707 RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute);
708 RtcWrite (RTC_ADDRESS_HOURS, RtcTime.Hour);
709 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH, RtcTime.Day);
710 RtcWrite (RTC_ADDRESS_MONTH, RtcTime.Month);
711 RtcWrite (RTC_ADDRESS_YEAR, (UINT8)RtcTime.Year);
712
713 //
714 // Allow updates of the RTC registers
715 //
716 RegisterB.Bits.Set = 0;
717 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
718
719 //
720 // Restore Divider status
721 //
722 RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);
723 //
724 // Release RTC Lock.
725 //
726 if (!EfiAtRuntime ()) {
727 EfiReleaseLock (&Global->RtcLock);
728 }
729
730 return EFI_SUCCESS;
731}
732
751 OUT BOOLEAN *Enabled,
752 OUT BOOLEAN *Pending,
753 OUT EFI_TIME *Time,
755 )
756{
757 EFI_STATUS Status;
758 RTC_REGISTER_B RegisterB;
759 RTC_REGISTER_C RegisterC;
760 EFI_TIME RtcTime;
761 UINTN DataSize;
762
763 //
764 // Check parameters for null pointers
765 //
766 if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) {
767 return EFI_INVALID_PARAMETER;
768 }
769
770 //
771 // Acquire RTC Lock to make access to RTC atomic
772 //
773 if (!EfiAtRuntime ()) {
774 EfiAcquireLock (&Global->RtcLock);
775 }
776
777 //
778 // Wait for up to 0.1 seconds for the RTC to be updated
779 //
780 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
781 if (EFI_ERROR (Status)) {
782 if (!EfiAtRuntime ()) {
783 EfiReleaseLock (&Global->RtcLock);
784 }
785
786 return EFI_DEVICE_ERROR;
787 }
788
789 //
790 // Read Register B and Register C
791 //
792 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
793 RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);
794
795 //
796 // Get the Time/Date/Daylight Savings values.
797 //
798 *Enabled = RegisterB.Bits.Aie;
799 *Pending = RegisterC.Bits.Af;
800
801 Time->Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);
802 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);
803 Time->Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);
804 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
805 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
806 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
807 Time->TimeZone = Global->SavedTimeZone;
808 Time->Daylight = Global->Daylight;
809
810 //
811 // Get the alarm info from variable
812 //
813 DataSize = sizeof (EFI_TIME);
814 Status = EfiGetVariable (
815 L"RTCALARM",
816 &gEfiCallerIdGuid,
817 NULL,
818 &DataSize,
819 &RtcTime
820 );
821 if (!EFI_ERROR (Status)) {
822 //
823 // The alarm variable exists. In this case, we read variable to get info.
824 //
825 Time->Day = RtcTime.Day;
826 Time->Month = RtcTime.Month;
827 Time->Year = RtcTime.Year;
828 }
829
830 //
831 // Release RTC Lock.
832 //
833 if (!EfiAtRuntime ()) {
834 EfiReleaseLock (&Global->RtcLock);
835 }
836
837 //
838 // Make sure all field values are in correct range
839 //
840 Status = ConvertRtcTimeToEfiTime (Time, RegisterB);
841 if (!EFI_ERROR (Status)) {
842 Status = RtcTimeFieldsValid (Time);
843 }
844
845 if (EFI_ERROR (Status)) {
846 return EFI_DEVICE_ERROR;
847 }
848
849 return EFI_SUCCESS;
850}
851
869 IN BOOLEAN Enable,
870 IN EFI_TIME *Time OPTIONAL,
872 )
873{
874 EFI_STATUS Status;
875 EFI_TIME RtcTime;
876 RTC_REGISTER_B RegisterB;
877 EFI_TIME_CAPABILITIES Capabilities;
878
879 ZeroMem (&RtcTime, sizeof (RtcTime));
880
881 if (Enable) {
882 if (Time == NULL) {
883 return EFI_INVALID_PARAMETER;
884 }
885
886 //
887 // Make sure that the time fields are valid
888 //
889 Status = RtcTimeFieldsValid (Time);
890 if (EFI_ERROR (Status)) {
891 return EFI_INVALID_PARAMETER;
892 }
893
894 //
895 // Just support set alarm time within 24 hours
896 //
897 Status = PcRtcGetTime (&RtcTime, &Capabilities, Global);
898 if (!EFI_ERROR (Status)) {
899 Status = RtcTimeFieldsValid (&RtcTime);
900 }
901
902 if (EFI_ERROR (Status)) {
903 return EFI_DEVICE_ERROR;
904 }
905
906 if (!IsWithinOneDay (&RtcTime, Time)) {
907 return EFI_UNSUPPORTED;
908 }
909
910 //
911 // Make a local copy of the time and date
912 //
913 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
914 }
915
916 //
917 // Acquire RTC Lock to make access to RTC atomic
918 //
919 if (!EfiAtRuntime ()) {
920 EfiAcquireLock (&Global->RtcLock);
921 }
922
923 //
924 // Wait for up to 0.1 seconds for the RTC to be updated
925 //
926 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
927 if (EFI_ERROR (Status)) {
928 if (!EfiAtRuntime ()) {
929 EfiReleaseLock (&Global->RtcLock);
930 }
931
932 return EFI_DEVICE_ERROR;
933 }
934
935 //
936 // Read Register B
937 //
938 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
939
940 if (Enable) {
941 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);
942 } else {
943 //
944 // if the alarm is disable, record the current setting.
945 //
946 RtcTime.Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);
947 RtcTime.Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);
948 RtcTime.Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);
949 RtcTime.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
950 RtcTime.Month = RtcRead (RTC_ADDRESS_MONTH);
951 RtcTime.Year = RtcRead (RTC_ADDRESS_YEAR);
952 RtcTime.TimeZone = Global->SavedTimeZone;
953 RtcTime.Daylight = Global->Daylight;
954 }
955
956 //
957 // Set the Y/M/D info to variable as it has no corresponding hw registers.
958 //
959 Status = EfiSetVariable (
960 L"RTCALARM",
961 &gEfiCallerIdGuid,
962 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
963 sizeof (RtcTime),
964 &RtcTime
965 );
966 if (EFI_ERROR (Status)) {
967 if (!EfiAtRuntime ()) {
968 EfiReleaseLock (&Global->RtcLock);
969 }
970
971 return EFI_DEVICE_ERROR;
972 }
973
974 //
975 // Inhibit updates of the RTC
976 //
977 RegisterB.Bits.Set = 1;
978 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
979
980 if (Enable) {
981 //
982 // Set RTC alarm time
983 //
984 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);
985 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);
986 RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);
987
988 RegisterB.Bits.Aie = 1;
989 } else {
990 RegisterB.Bits.Aie = 0;
991 }
992
993 //
994 // Allow updates of the RTC registers
995 //
996 RegisterB.Bits.Set = 0;
997 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
998
999 //
1000 // Release RTC Lock.
1001 //
1002 if (!EfiAtRuntime ()) {
1003 EfiReleaseLock (&Global->RtcLock);
1004 }
1005
1006 return EFI_SUCCESS;
1007}
1008
1021UINT8
1023 IN UINT8 Value
1024 )
1025{
1026 if ((Value < 0xa0) && ((Value & 0xf) < 0xa)) {
1027 return BcdToDecimal8 (Value);
1028 }
1029
1030 return 0xff;
1031}
1032
1052 IN OUT EFI_TIME *Time,
1053 IN RTC_REGISTER_B RegisterB
1054 )
1055{
1056 BOOLEAN IsPM;
1057 UINT8 Century;
1058
1059 // IsPM only makes sense for 12-hour format.
1060 if (RegisterB.Bits.Mil == 0) {
1061 if ((Time->Hour & 0x80) != 0) {
1062 IsPM = TRUE;
1063 } else {
1064 IsPM = FALSE;
1065 }
1066
1067 Time->Hour = (UINT8)(Time->Hour & 0x7f);
1068 }
1069
1070 if (RegisterB.Bits.Dm == 0) {
1071 Time->Year = CheckAndConvertBcd8ToDecimal8 ((UINT8)Time->Year);
1072 Time->Month = CheckAndConvertBcd8ToDecimal8 (Time->Month);
1073 Time->Day = CheckAndConvertBcd8ToDecimal8 (Time->Day);
1074 Time->Hour = CheckAndConvertBcd8ToDecimal8 (Time->Hour);
1075 Time->Minute = CheckAndConvertBcd8ToDecimal8 (Time->Minute);
1076 Time->Second = CheckAndConvertBcd8ToDecimal8 (Time->Second);
1077 }
1078
1079 if ((Time->Year == 0xff) || (Time->Month == 0xff) || (Time->Day == 0xff) ||
1080 (Time->Hour == 0xff) || (Time->Minute == 0xff) || (Time->Second == 0xff))
1081 {
1082 return EFI_INVALID_PARAMETER;
1083 }
1084
1085 //
1086 // For minimal/maximum year range [1970, 2069],
1087 // Century is 19 if RTC year >= 70,
1088 // Century is 20 otherwise.
1089 //
1090 Century = (UINT8)(mMinimalValidYear / 100);
1091 if (Time->Year < mMinimalValidYear % 100) {
1092 Century++;
1093 }
1094
1095 Time->Year = (UINT16)(Century * 100 + Time->Year);
1096
1097 //
1098 // If time is in 12 hour format, convert it to 24 hour format
1099 //
1100 if (RegisterB.Bits.Mil == 0) {
1101 if (IsPM && (Time->Hour < 12)) {
1102 Time->Hour = (UINT8)(Time->Hour + 12);
1103 }
1104
1105 if (!IsPM && (Time->Hour == 12)) {
1106 Time->Hour = 0;
1107 }
1108 }
1109
1110 Time->Nanosecond = 0;
1111
1112 return EFI_SUCCESS;
1113}
1114
1125 UINTN Timeout
1126 )
1127{
1128 RTC_REGISTER_A RegisterA;
1129 RTC_REGISTER_D RegisterD;
1130
1131 //
1132 // See if the RTC is functioning correctly
1133 //
1134 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
1135
1136 if (RegisterD.Bits.Vrt == 0) {
1137 return EFI_DEVICE_ERROR;
1138 }
1139
1140 //
1141 // Wait for up to 0.1 seconds for the RTC to be ready.
1142 //
1143 Timeout = (Timeout / 10) + 1;
1144 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
1145 while (RegisterA.Bits.Uip == 1 && Timeout > 0) {
1146 MicroSecondDelay (10);
1147 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
1148 Timeout--;
1149 }
1150
1151 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
1152 if ((Timeout == 0) || (RegisterD.Bits.Vrt == 0)) {
1153 return EFI_DEVICE_ERROR;
1154 }
1155
1156 return EFI_SUCCESS;
1157}
1158
1170 IN EFI_TIME *Time
1171 )
1172{
1173 if ((Time->Year < mMinimalValidYear) ||
1174 (Time->Year > mMaximalValidYear) ||
1175 (Time->Month < 1) ||
1176 (Time->Month > 12) ||
1177 (!DayValid (Time)) ||
1178 (Time->Hour > 23) ||
1179 (Time->Minute > 59) ||
1180 (Time->Second > 59) ||
1181 (Time->Nanosecond > 999999999) ||
1182 (!((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) || ((Time->TimeZone >= -1440) && (Time->TimeZone <= 1440)))) ||
1183 ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0))
1184 {
1185 return EFI_INVALID_PARAMETER;
1186 }
1187
1188 return EFI_SUCCESS;
1189}
1190
1199BOOLEAN
1201 IN EFI_TIME *Time
1202 )
1203{
1204 //
1205 // The validity of Time->Month field should be checked before
1206 //
1207 ASSERT (Time->Month >= 1);
1208 ASSERT (Time->Month <= 12);
1209 if ((Time->Day < 1) ||
1210 (Time->Day > mDayOfMonth[Time->Month - 1]) ||
1211 ((Time->Month == 2) && (!IsLeapYear (Time) && (Time->Day > 28)))
1212 )
1213 {
1214 return FALSE;
1215 }
1216
1217 return TRUE;
1218}
1219
1228BOOLEAN
1230 IN EFI_TIME *Time
1231 )
1232{
1233 if (Time->Year % 4 == 0) {
1234 if (Time->Year % 100 == 0) {
1235 if (Time->Year % 400 == 0) {
1236 return TRUE;
1237 } else {
1238 return FALSE;
1239 }
1240 } else {
1241 return TRUE;
1242 }
1243 } else {
1244 return FALSE;
1245 }
1246}
1247
1259VOID
1261 IN OUT EFI_TIME *Time,
1262 IN RTC_REGISTER_B RegisterB
1263 )
1264{
1265 BOOLEAN IsPM;
1266
1267 IsPM = TRUE;
1268 //
1269 // Adjust hour field if RTC is in 12 hour mode
1270 //
1271 if (RegisterB.Bits.Mil == 0) {
1272 if (Time->Hour < 12) {
1273 IsPM = FALSE;
1274 }
1275
1276 if (Time->Hour >= 13) {
1277 Time->Hour = (UINT8)(Time->Hour - 12);
1278 } else if (Time->Hour == 0) {
1279 Time->Hour = 12;
1280 }
1281 }
1282
1283 //
1284 // Set the Time/Date values.
1285 //
1286 Time->Year = (UINT16)(Time->Year % 100);
1287
1288 if (RegisterB.Bits.Dm == 0) {
1289 Time->Year = DecimalToBcd8 ((UINT8)Time->Year);
1290 Time->Month = DecimalToBcd8 (Time->Month);
1291 Time->Day = DecimalToBcd8 (Time->Day);
1292 Time->Hour = DecimalToBcd8 (Time->Hour);
1293 Time->Minute = DecimalToBcd8 (Time->Minute);
1294 Time->Second = DecimalToBcd8 (Time->Second);
1295 }
1296
1297 //
1298 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
1299 //
1300 if ((RegisterB.Bits.Mil == 0) && IsPM) {
1301 Time->Hour = (UINT8)(Time->Hour | 0x80);
1302 }
1303}
1304
1317INTN
1319 IN EFI_TIME *From,
1320 IN EFI_TIME *To
1321 )
1322{
1323 if ((From->Hour > To->Hour) ||
1324 ((From->Hour == To->Hour) && (From->Minute > To->Minute)) ||
1325 ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second)))
1326 {
1327 return 1;
1328 } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) {
1329 return 0;
1330 } else {
1331 return -1;
1332 }
1333}
1334
1344BOOLEAN
1346 IN EFI_TIME *From,
1347 IN EFI_TIME *To
1348 )
1349{
1350 BOOLEAN Adjacent;
1351
1352 Adjacent = FALSE;
1353
1354 //
1355 // The validity of From->Month field should be checked before
1356 //
1357 ASSERT (From->Month >= 1);
1358 ASSERT (From->Month <= 12);
1359
1360 if (From->Year == To->Year) {
1361 if (From->Month == To->Month) {
1362 if ((From->Day + 1) == To->Day) {
1363 if ((CompareHMS (From, To) >= 0)) {
1364 Adjacent = TRUE;
1365 }
1366 } else if (From->Day == To->Day) {
1367 if ((CompareHMS (From, To) <= 0)) {
1368 Adjacent = TRUE;
1369 }
1370 }
1371 } else if (((From->Month + 1) == To->Month) && (To->Day == 1)) {
1372 if ((From->Month == 2) && !IsLeapYear (From)) {
1373 if (From->Day == 28) {
1374 if ((CompareHMS (From, To) >= 0)) {
1375 Adjacent = TRUE;
1376 }
1377 }
1378 } else if (From->Day == mDayOfMonth[From->Month - 1]) {
1379 if ((CompareHMS (From, To) >= 0)) {
1380 Adjacent = TRUE;
1381 }
1382 }
1383 }
1384 } else if (((From->Year + 1) == To->Year) &&
1385 (From->Month == 12) &&
1386 (From->Day == 31) &&
1387 (To->Month == 1) &&
1388 (To->Day == 1))
1389 {
1390 if ((CompareHMS (From, To) >= 0)) {
1391 Adjacent = TRUE;
1392 }
1393 }
1394
1395 return Adjacent;
1396}
1397
1403UINT8
1405 VOID
1406 )
1407{
1409
1412 );
1413
1414 if ((Fadt != NULL) &&
1415 (Fadt->Century > RTC_ADDRESS_REGISTER_D) && (Fadt->Century < 0x80)
1416 )
1417 {
1418 return Fadt->Century;
1419 } else {
1420 return 0;
1421 }
1422}
1423
1434VOID
1435EFIAPI
1437 IN EFI_EVENT Event,
1438 IN VOID *Context
1439 )
1440{
1441 EFI_STATUS Status;
1442 EFI_TIME Time;
1443 UINT8 CenturyRtcAddress;
1444 UINT8 Century;
1445
1446 CenturyRtcAddress = GetCenturyRtcAddress ();
1447 if ((CenturyRtcAddress != 0) && (mModuleGlobal.CenturyRtcAddress != CenturyRtcAddress)) {
1448 mModuleGlobal.CenturyRtcAddress = CenturyRtcAddress;
1449 Status = PcRtcGetTime (&Time, NULL, &mModuleGlobal);
1450 if (!EFI_ERROR (Status)) {
1451 Century = (UINT8)(Time.Year / 100);
1452 Century = DecimalToBcd8 (Century);
1453 DEBUG ((DEBUG_INFO, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century, mModuleGlobal.CenturyRtcAddress));
1454 RtcWrite (mModuleGlobal.CenturyRtcAddress, Century);
1455 }
1456 }
1457}
UINT64 UINTN
INT64 INTN
#define EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
Definition: Acpi20.h:480
UINTN EFIAPI MicroSecondDelay(IN UINTN MicroSeconds)
UINT8 EFIAPI BcdToDecimal8(IN UINT8 Value)
Definition: String.c:1677
UINT8 EFIAPI DecimalToBcd8(IN UINT8 Value)
Definition: String.c:1653
VOID *EFIAPI CopyMem(OUT VOID *DestinationBuffer, IN CONST VOID *SourceBuffer, IN UINTN Length)
VOID *EFIAPI ZeroMem(OUT VOID *Buffer, IN UINTN Length)
UINT8 EFIAPI IoWrite8(IN UINTN Port, IN UINT8 Value)
Definition: IoLibArmVirt.c:200
UINT8 EFIAPI MmioRead8(IN UINTN Address)
Definition: IoLib.c:82
UINT8 EFIAPI MmioWrite8(IN UINTN Address, IN UINT8 Value)
Definition: IoLib.c:126
UINT8 EFIAPI IoRead8(IN UINTN Port)
Definition: IoLibArmVirt.c:175
#define NULL
Definition: Base.h:319
#define STATIC
Definition: Base.h:264
#define MIN(a, b)
Definition: Base.h:1007
#define TRUE
Definition: Base.h:301
#define FALSE
Definition: Base.h:307
#define IN
Definition: Base.h:279
#define OUT
Definition: Base.h:284
#define MAX(a, b)
Definition: Base.h:992
#define DEBUG(Expression)
Definition: DebugLib.h:434
#define REPORT_STATUS_CODE(Type, Value)
UINT8 GetCenturyRtcAddress(VOID)
Definition: PcRtc.c:1404
STATIC UINT8 MmioRtcRead(IN UINTN Address)
Definition: PcRtc.c:115
BOOLEAN DayValid(IN EFI_TIME *Time)
Definition: PcRtc.c:1200
EFI_STATUS PcRtcSetWakeupTime(IN BOOLEAN Enable, IN EFI_TIME *Time OPTIONAL, IN PC_RTC_MODULE_GLOBALS *Global)
Definition: PcRtc.c:868
EFI_STATUS RtcWaitToUpdate(UINTN Timeout)
Definition: PcRtc.c:1124
EFI_STATUS PcRtcInit(IN PC_RTC_MODULE_GLOBALS *Global)
Definition: PcRtc.c:262
INTN CompareHMS(IN EFI_TIME *From, IN EFI_TIME *To)
Definition: PcRtc.c:1318
STATIC UINT8 IoRtcRead(IN UINTN Address)
Definition: PcRtc.c:72
BOOLEAN IsLeapYear(IN EFI_TIME *Time)
Definition: PcRtc.c:1229
EFI_STATUS ConvertRtcTimeToEfiTime(IN OUT EFI_TIME *Time, IN RTC_REGISTER_B RegisterB)
Definition: PcRtc.c:1051
STATIC VOID RtcWrite(IN UINTN Address, IN UINT8 Data)
Definition: PcRtc.c:179
EFI_STATUS PcRtcGetWakeupTime(OUT BOOLEAN *Enabled, OUT BOOLEAN *Pending, OUT EFI_TIME *Time, IN PC_RTC_MODULE_GLOBALS *Global)
Definition: PcRtc.c:750
EFI_STATUS PcRtcGetTime(OUT EFI_TIME *Time, OUT EFI_TIME_CAPABILITIES *Capabilities OPTIONAL, IN PC_RTC_MODULE_GLOBALS *Global)
Definition: PcRtc.c:527
EFI_STATUS PcRtcSetTimeZone(IN INT16 TimeZone, IN UINT8 Daylight, IN PC_RTC_MODULE_GLOBALS *Global)
Definition: PcRtc.c:203
VOID ConvertEfiTimeToRtcTime(IN OUT EFI_TIME *Time, IN RTC_REGISTER_B RegisterB)
Definition: PcRtc.c:1260
STATIC VOID IoRtcWrite(IN UINTN Address, IN UINT8 Data)
Definition: PcRtc.c:93
UINT8 CheckAndConvertBcd8ToDecimal8(IN UINT8 Value)
Definition: PcRtc.c:1022
EFI_STATUS PcRtcSetTime(IN EFI_TIME *Time, IN PC_RTC_MODULE_GLOBALS *Global)
Definition: PcRtc.c:632
STATIC UINT8 RtcRead(IN UINTN Address)
Definition: PcRtc.c:158
EFI_STATUS RtcTimeFieldsValid(IN EFI_TIME *Time)
Definition: PcRtc.c:1169
VOID EFIAPI PcRtcAcpiTableChangeCallback(IN EFI_EVENT Event, IN VOID *Context)
Definition: PcRtc.c:1436
STATIC VOID MmioRtcWrite(IN UINTN Address, IN UINT8 Data)
Definition: PcRtc.c:136
BOOLEAN IsWithinOneDay(IN EFI_TIME *From, IN EFI_TIME *To)
Definition: PcRtc.c:1345
#define PcdGet32(TokenName)
Definition: PcdLib.h:362
#define FixedPcdGet8(TokenName)
Definition: PcdLib.h:64
#define FeaturePcdGet(TokenName)
Definition: PcdLib.h:50
#define EFI_ERROR_MINOR
Definition: PiStatusCode.h:58
RETURN_STATUS EFI_STATUS
Definition: UefiBaseType.h:29
VOID * EFI_EVENT
Definition: UefiBaseType.h:37
#define EFI_SUCCESS
Definition: UefiBaseType.h:112
VOID EFIAPI EfiReleaseLock(IN EFI_LOCK *Lock)
Definition: UefiLib.c:499
VOID EFIAPI EfiAcquireLock(IN EFI_LOCK *Lock)
Definition: UefiLib.c:434
EFI_ACPI_COMMON_HEADER *EFIAPI EfiLocateFirstAcpiTable(IN UINT32 Signature)
Definition: Acpi.c:441
#define EFI_VARIABLE_NON_VOLATILE
EFI_STATUS EFIAPI EfiGetVariable(IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, OUT UINT32 *Attributes OPTIONAL, IN OUT UINTN *DataSize, OUT VOID *Data)
Definition: RuntimeLib.c:408
BOOLEAN EFIAPI EfiAtRuntime(VOID)
Definition: RuntimeLib.c:167
EFI_STATUS EFIAPI EfiSetVariable(IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, IN UINT32 Attributes, IN UINTN DataSize, IN VOID *Data)
Definition: RuntimeLib.c:498
#define EFI_UNSPECIFIED_TIMEZONE
Definition: UefiSpec.h:58