2 RTC Architectural Protocol GUID as defined in DxeCis 0.96.
4 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2017, AMD Inc. All rights reserved.<BR>
6 Copyright (c) 2018 - 2020, ARM Limited. All rights reserved.<BR>
8 SPDX-License-Identifier: BSD-2-Clause-Patent
14 extern UINTN mRtcIndexRegister
;
15 extern UINTN mRtcTargetRegister
;
20 UINTN mDayOfMonth
[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
23 // The name of NV variable to store the timezone and daylight saving information.
25 CHAR16 mTimeZoneVariableName
[] = L
"RTC";
28 Compare the Hour, Minute and Second of the From time and the To time.
30 Only compare H/M/S in EFI_TIME and ignore other fields here.
32 @param From the first time
33 @param To the second time
35 @return >0 The H/M/S of the From time is later than those of To time
36 @return ==0 The H/M/S of the From time is same as those of To time
37 @return <0 The H/M/S of the From time is earlier than those of To time
46 To check if second date is later than first date within 24 hours.
48 @param From the first date
49 @param To the second date
51 @retval TRUE From is previous to To within 24 hours.
52 @retval FALSE From is later, or it is previous to To more than 24 hours.
61 Read RTC content through its registers using IO access.
63 @param Address Address offset of RTC. It is recommended to use
64 macros such as RTC_ADDRESS_SECONDS.
66 @return The data of UINT8 type read from RTC.
75 PcdGet8 (PcdRtcIndexRegister
),
76 (UINT8
)(Address
| (UINT8
)(IoRead8 (PcdGet8 (PcdRtcIndexRegister
)) & 0x80))
78 return IoRead8 (PcdGet8 (PcdRtcTargetRegister
));
82 Write RTC through its registers using IO access.
84 @param Address Address offset of RTC. It is recommended to use
85 macros such as RTC_ADDRESS_SECONDS.
86 @param Data The content you want to write into RTC.
97 PcdGet8 (PcdRtcIndexRegister
),
98 (UINT8
)(Address
| (UINT8
)(IoRead8 (PcdGet8 (PcdRtcIndexRegister
)) & 0x80))
100 IoWrite8 (PcdGet8 (PcdRtcTargetRegister
), Data
);
104 Read RTC content through its registers using MMIO access.
106 @param Address Address offset of RTC. It is recommended to use
107 macros such as RTC_ADDRESS_SECONDS.
109 @return The data of UINT8 type read from RTC.
119 (UINT8
)(Address
| (UINT8
)(MmioRead8 (mRtcIndexRegister
) & 0x80))
121 return MmioRead8 (mRtcTargetRegister
);
125 Write RTC through its registers using MMIO access.
127 @param Address Address offset of RTC. It is recommended to use
128 macros such as RTC_ADDRESS_SECONDS.
129 @param Data The content you want to write into RTC.
141 (UINT8
)(Address
| (UINT8
)(MmioRead8 (mRtcIndexRegister
) & 0x80))
143 MmioWrite8 (mRtcTargetRegister
, Data
);
147 Read RTC content through its registers.
149 @param Address Address offset of RTC. It is recommended to use
150 macros such as RTC_ADDRESS_SECONDS.
152 @return The data of UINT8 type read from RTC.
160 if (FeaturePcdGet (PcdRtcUseMmio
)) {
161 return MmioRtcRead (Address
);
164 return IoRtcRead (Address
);
168 Write RTC through its registers.
170 @param Address Address offset of RTC. It is recommended to use
171 macros such as RTC_ADDRESS_SECONDS.
172 @param Data The content you want to write into RTC.
182 if (FeaturePcdGet (PcdRtcUseMmio
)) {
183 MmioRtcWrite (Address
, Data
);
185 IoRtcWrite (Address
, Data
);
192 @param Global For global use inside this module.
194 @retval EFI_DEVICE_ERROR Initialization failed due to device error.
195 @retval EFI_SUCCESS Initialization successful.
200 IN PC_RTC_MODULE_GLOBALS
*Global
204 RTC_REGISTER_A RegisterA
;
205 RTC_REGISTER_B RegisterB
;
206 RTC_REGISTER_D RegisterD
;
214 // Acquire RTC Lock to make access to RTC atomic
216 if (!EfiAtRuntime ()) {
217 EfiAcquireLock (&Global
->RtcLock
);
220 // Initialize RTC Register
222 // Make sure Division Chain is properly configured,
223 // or RTC clock won't "tick" -- time won't increment
225 RegisterA
.Data
= FixedPcdGet8 (PcdInitialValueRtcRegisterA
);
226 RtcWrite (RTC_ADDRESS_REGISTER_A
, RegisterA
.Data
);
231 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
234 // Clear RTC flag register
236 RtcRead (RTC_ADDRESS_REGISTER_C
);
239 // Clear RTC register D
241 RegisterD
.Data
= FixedPcdGet8 (PcdInitialValueRtcRegisterD
);
242 RtcWrite (RTC_ADDRESS_REGISTER_D
, RegisterD
.Data
);
245 // Wait for up to 0.1 seconds for the RTC to be updated
247 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
248 if (EFI_ERROR (Status
)) {
250 // Set the variable with default value if the RTC is functioning incorrectly.
252 Global
->SavedTimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
253 Global
->Daylight
= 0;
254 if (!EfiAtRuntime ()) {
255 EfiReleaseLock (&Global
->RtcLock
);
257 return EFI_DEVICE_ERROR
;
260 // Get the Time/Date/Daylight Savings values.
262 Time
.Second
= RtcRead (RTC_ADDRESS_SECONDS
);
263 Time
.Minute
= RtcRead (RTC_ADDRESS_MINUTES
);
264 Time
.Hour
= RtcRead (RTC_ADDRESS_HOURS
);
265 Time
.Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
266 Time
.Month
= RtcRead (RTC_ADDRESS_MONTH
);
267 Time
.Year
= RtcRead (RTC_ADDRESS_YEAR
);
270 // Set RTC configuration after get original time
271 // The value of bit AIE should be reserved.
273 RegisterB
.Data
= FixedPcdGet8 (PcdInitialValueRtcRegisterB
) | (RegisterB
.Data
& BIT5
);
274 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
279 if (!EfiAtRuntime ()) {
280 EfiReleaseLock (&Global
->RtcLock
);
284 // Get the data of Daylight saving and time zone, if they have been
285 // stored in NV variable during previous boot.
287 DataSize
= sizeof (UINT32
);
288 Status
= EfiGetVariable (
289 mTimeZoneVariableName
,
295 if (!EFI_ERROR (Status
)) {
296 Time
.TimeZone
= (INT16
) TimerVar
;
297 Time
.Daylight
= (UINT8
) (TimerVar
>> 16);
299 Time
.TimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
304 // Validate time fields
306 Status
= ConvertRtcTimeToEfiTime (&Time
, RegisterB
);
307 if (!EFI_ERROR (Status
)) {
308 Status
= RtcTimeFieldsValid (&Time
);
310 if (EFI_ERROR (Status
)) {
312 // Report Status Code to indicate that the RTC has bad date and time
315 EFI_ERROR_CODE
| EFI_ERROR_MINOR
,
316 (EFI_SOFTWARE_DXE_RT_DRIVER
| EFI_SW_EC_BAD_DATE_TIME
)
318 Time
.Second
= RTC_INIT_SECOND
;
319 Time
.Minute
= RTC_INIT_MINUTE
;
320 Time
.Hour
= RTC_INIT_HOUR
;
321 Time
.Day
= RTC_INIT_DAY
;
322 Time
.Month
= RTC_INIT_MONTH
;
323 Time
.Year
= PcdGet16 (PcdMinimalValidYear
);
325 Time
.TimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
330 // Reset time value according to new RTC configuration
332 Status
= PcRtcSetTime (&Time
, Global
);
333 if (EFI_ERROR (Status
)) {
334 return EFI_DEVICE_ERROR
;
338 // Reset wakeup time value to valid state when wakeup alarm is disabled and wakeup time is invalid.
339 // Global variable has already had valid SavedTimeZone and Daylight,
340 // so we can use them to get and set wakeup time.
342 Status
= PcRtcGetWakeupTime (&Enabled
, &Pending
, &Time
, Global
);
343 if ((Enabled
) || (!EFI_ERROR (Status
))) {
348 // When wakeup time is disabled and invalid, reset wakeup time register to valid state
349 // but keep wakeup alarm disabled.
351 Time
.Second
= RTC_INIT_SECOND
;
352 Time
.Minute
= RTC_INIT_MINUTE
;
353 Time
.Hour
= RTC_INIT_HOUR
;
354 Time
.Day
= RTC_INIT_DAY
;
355 Time
.Month
= RTC_INIT_MONTH
;
356 Time
.Year
= PcdGet16 (PcdMinimalValidYear
);
358 Time
.TimeZone
= Global
->SavedTimeZone
;
359 Time
.Daylight
= Global
->Daylight
;;
362 // Acquire RTC Lock to make access to RTC atomic
364 if (!EfiAtRuntime ()) {
365 EfiAcquireLock (&Global
->RtcLock
);
368 // Wait for up to 0.1 seconds for the RTC to be updated
370 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
371 if (EFI_ERROR (Status
)) {
372 if (!EfiAtRuntime ()) {
373 EfiReleaseLock (&Global
->RtcLock
);
375 return EFI_DEVICE_ERROR
;
378 ConvertEfiTimeToRtcTime (&Time
, RegisterB
);
381 // Set the Y/M/D info to variable as it has no corresponding hw registers.
383 Status
= EfiSetVariable (
386 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_NON_VOLATILE
,
390 if (EFI_ERROR (Status
)) {
391 if (!EfiAtRuntime ()) {
392 EfiReleaseLock (&Global
->RtcLock
);
394 return EFI_DEVICE_ERROR
;
398 // Inhibit updates of the RTC
400 RegisterB
.Bits
.Set
= 1;
401 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
404 // Set RTC alarm time registers
406 RtcWrite (RTC_ADDRESS_SECONDS_ALARM
, Time
.Second
);
407 RtcWrite (RTC_ADDRESS_MINUTES_ALARM
, Time
.Minute
);
408 RtcWrite (RTC_ADDRESS_HOURS_ALARM
, Time
.Hour
);
411 // Allow updates of the RTC registers
413 RegisterB
.Bits
.Set
= 0;
414 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
419 if (!EfiAtRuntime ()) {
420 EfiReleaseLock (&Global
->RtcLock
);
426 Returns the current time and date information, and the time-keeping capabilities
427 of the hardware platform.
429 @param Time A pointer to storage to receive a snapshot of the current time.
430 @param Capabilities An optional pointer to a buffer to receive the real time clock
431 device's capabilities.
432 @param Global For global use inside this module.
434 @retval EFI_SUCCESS The operation completed successfully.
435 @retval EFI_INVALID_PARAMETER Time is NULL.
436 @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.
442 OUT EFI_TIME_CAPABILITIES
*Capabilities
, OPTIONAL
443 IN PC_RTC_MODULE_GLOBALS
*Global
447 RTC_REGISTER_B RegisterB
;
450 // Check parameters for null pointer
453 return EFI_INVALID_PARAMETER
;
457 // Acquire RTC Lock to make access to RTC atomic
459 if (!EfiAtRuntime ()) {
460 EfiAcquireLock (&Global
->RtcLock
);
463 // Wait for up to 0.1 seconds for the RTC to be updated
465 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
466 if (EFI_ERROR (Status
)) {
467 if (!EfiAtRuntime ()) {
468 EfiReleaseLock (&Global
->RtcLock
);
475 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
478 // Get the Time/Date/Daylight Savings values.
480 Time
->Second
= RtcRead (RTC_ADDRESS_SECONDS
);
481 Time
->Minute
= RtcRead (RTC_ADDRESS_MINUTES
);
482 Time
->Hour
= RtcRead (RTC_ADDRESS_HOURS
);
483 Time
->Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
484 Time
->Month
= RtcRead (RTC_ADDRESS_MONTH
);
485 Time
->Year
= RtcRead (RTC_ADDRESS_YEAR
);
490 if (!EfiAtRuntime ()) {
491 EfiReleaseLock (&Global
->RtcLock
);
495 // Get the variable that contains the TimeZone and Daylight fields
497 Time
->TimeZone
= Global
->SavedTimeZone
;
498 Time
->Daylight
= Global
->Daylight
;
501 // Make sure all field values are in correct range
503 Status
= ConvertRtcTimeToEfiTime (Time
, RegisterB
);
504 if (!EFI_ERROR (Status
)) {
505 Status
= RtcTimeFieldsValid (Time
);
507 if (EFI_ERROR (Status
)) {
508 return EFI_DEVICE_ERROR
;
512 // Fill in Capabilities if it was passed in
514 if (Capabilities
!= NULL
) {
515 Capabilities
->Resolution
= 1;
519 Capabilities
->Accuracy
= 50000000;
523 Capabilities
->SetsToZero
= FALSE
;
530 Sets the current local time and date information.
532 @param Time A pointer to the current time.
533 @param Global For global use inside this module.
535 @retval EFI_SUCCESS The operation completed successfully.
536 @retval EFI_INVALID_PARAMETER A time field is out of range.
537 @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.
543 IN PC_RTC_MODULE_GLOBALS
*Global
548 RTC_REGISTER_B RegisterB
;
552 return EFI_INVALID_PARAMETER
;
555 // Make sure that the time fields are valid
557 Status
= RtcTimeFieldsValid (Time
);
558 if (EFI_ERROR (Status
)) {
562 CopyMem (&RtcTime
, Time
, sizeof (EFI_TIME
));
565 // Acquire RTC Lock to make access to RTC atomic
567 if (!EfiAtRuntime ()) {
568 EfiAcquireLock (&Global
->RtcLock
);
571 // Wait for up to 0.1 seconds for the RTC to be updated
573 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
574 if (EFI_ERROR (Status
)) {
575 if (!EfiAtRuntime ()) {
576 EfiReleaseLock (&Global
->RtcLock
);
582 // Write timezone and daylight to RTC variable
584 if ((Time
->TimeZone
== EFI_UNSPECIFIED_TIMEZONE
) && (Time
->Daylight
== 0)) {
585 Status
= EfiSetVariable (
586 mTimeZoneVariableName
,
592 if (Status
== EFI_NOT_FOUND
) {
593 Status
= EFI_SUCCESS
;
596 TimerVar
= Time
->Daylight
;
597 TimerVar
= (UINT32
) ((TimerVar
<< 16) | (UINT16
)(Time
->TimeZone
));
598 Status
= EfiSetVariable (
599 mTimeZoneVariableName
,
601 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_NON_VOLATILE
,
607 if (EFI_ERROR (Status
)) {
608 if (!EfiAtRuntime ()) {
609 EfiReleaseLock (&Global
->RtcLock
);
611 return EFI_DEVICE_ERROR
;
615 // Read Register B, and inhibit updates of the RTC
617 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
618 RegisterB
.Bits
.Set
= 1;
619 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
622 // Store the century value to RTC before converting to BCD format.
624 if (Global
->CenturyRtcAddress
!= 0) {
625 RtcWrite (Global
->CenturyRtcAddress
, DecimalToBcd8 ((UINT8
) (RtcTime
.Year
/ 100)));
628 ConvertEfiTimeToRtcTime (&RtcTime
, RegisterB
);
630 RtcWrite (RTC_ADDRESS_SECONDS
, RtcTime
.Second
);
631 RtcWrite (RTC_ADDRESS_MINUTES
, RtcTime
.Minute
);
632 RtcWrite (RTC_ADDRESS_HOURS
, RtcTime
.Hour
);
633 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH
, RtcTime
.Day
);
634 RtcWrite (RTC_ADDRESS_MONTH
, RtcTime
.Month
);
635 RtcWrite (RTC_ADDRESS_YEAR
, (UINT8
) RtcTime
.Year
);
638 // Allow updates of the RTC registers
640 RegisterB
.Bits
.Set
= 0;
641 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
646 if (!EfiAtRuntime ()) {
647 EfiReleaseLock (&Global
->RtcLock
);
650 // Set the variable that contains the TimeZone and Daylight fields
652 Global
->SavedTimeZone
= Time
->TimeZone
;
653 Global
->Daylight
= Time
->Daylight
;
659 Returns the current wakeup alarm clock setting.
661 @param Enabled Indicates if the alarm is currently enabled or disabled.
662 @param Pending Indicates if the alarm signal is pending and requires acknowledgment.
663 @param Time The current alarm setting.
664 @param Global For global use inside this module.
666 @retval EFI_SUCCESS The alarm settings were returned.
667 @retval EFI_INVALID_PARAMETER Enabled is NULL.
668 @retval EFI_INVALID_PARAMETER Pending is NULL.
669 @retval EFI_INVALID_PARAMETER Time is NULL.
670 @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.
671 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
676 OUT BOOLEAN
*Enabled
,
677 OUT BOOLEAN
*Pending
,
679 IN PC_RTC_MODULE_GLOBALS
*Global
683 RTC_REGISTER_B RegisterB
;
684 RTC_REGISTER_C RegisterC
;
689 // Check parameters for null pointers
691 if ((Enabled
== NULL
) || (Pending
== NULL
) || (Time
== NULL
)) {
692 return EFI_INVALID_PARAMETER
;
696 // Acquire RTC Lock to make access to RTC atomic
698 if (!EfiAtRuntime ()) {
699 EfiAcquireLock (&Global
->RtcLock
);
702 // Wait for up to 0.1 seconds for the RTC to be updated
704 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
705 if (EFI_ERROR (Status
)) {
706 if (!EfiAtRuntime ()) {
707 EfiReleaseLock (&Global
->RtcLock
);
709 return EFI_DEVICE_ERROR
;
712 // Read Register B and Register C
714 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
715 RegisterC
.Data
= RtcRead (RTC_ADDRESS_REGISTER_C
);
718 // Get the Time/Date/Daylight Savings values.
720 *Enabled
= RegisterB
.Bits
.Aie
;
721 *Pending
= RegisterC
.Bits
.Af
;
723 Time
->Second
= RtcRead (RTC_ADDRESS_SECONDS_ALARM
);
724 Time
->Minute
= RtcRead (RTC_ADDRESS_MINUTES_ALARM
);
725 Time
->Hour
= RtcRead (RTC_ADDRESS_HOURS_ALARM
);
726 Time
->Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
727 Time
->Month
= RtcRead (RTC_ADDRESS_MONTH
);
728 Time
->Year
= RtcRead (RTC_ADDRESS_YEAR
);
729 Time
->TimeZone
= Global
->SavedTimeZone
;
730 Time
->Daylight
= Global
->Daylight
;
733 // Get the alarm info from variable
735 DataSize
= sizeof (EFI_TIME
);
736 Status
= EfiGetVariable (
743 if (!EFI_ERROR (Status
)) {
745 // The alarm variable exists. In this case, we read variable to get info.
747 Time
->Day
= RtcTime
.Day
;
748 Time
->Month
= RtcTime
.Month
;
749 Time
->Year
= RtcTime
.Year
;
755 if (!EfiAtRuntime ()) {
756 EfiReleaseLock (&Global
->RtcLock
);
760 // Make sure all field values are in correct range
762 Status
= ConvertRtcTimeToEfiTime (Time
, RegisterB
);
763 if (!EFI_ERROR (Status
)) {
764 Status
= RtcTimeFieldsValid (Time
);
766 if (EFI_ERROR (Status
)) {
767 return EFI_DEVICE_ERROR
;
774 Sets the system wakeup alarm clock time.
776 @param Enabled Enable or disable the wakeup alarm.
777 @param Time If Enable is TRUE, the time to set the wakeup alarm for.
778 If Enable is FALSE, then this parameter is optional, and may be NULL.
779 @param Global For global use inside this module.
781 @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled.
782 If Enable is FALSE, then the wakeup alarm was disabled.
783 @retval EFI_INVALID_PARAMETER A time field is out of range.
784 @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.
785 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
791 IN EFI_TIME
*Time
, OPTIONAL
792 IN PC_RTC_MODULE_GLOBALS
*Global
797 RTC_REGISTER_B RegisterB
;
798 EFI_TIME_CAPABILITIES Capabilities
;
800 ZeroMem (&RtcTime
, sizeof (RtcTime
));
805 return EFI_INVALID_PARAMETER
;
808 // Make sure that the time fields are valid
810 Status
= RtcTimeFieldsValid (Time
);
811 if (EFI_ERROR (Status
)) {
812 return EFI_INVALID_PARAMETER
;
815 // Just support set alarm time within 24 hours
817 PcRtcGetTime (&RtcTime
, &Capabilities
, Global
);
818 Status
= RtcTimeFieldsValid (&RtcTime
);
819 if (EFI_ERROR (Status
)) {
820 return EFI_DEVICE_ERROR
;
822 if (!IsWithinOneDay (&RtcTime
, Time
)) {
823 return EFI_UNSUPPORTED
;
826 // Make a local copy of the time and date
828 CopyMem (&RtcTime
, Time
, sizeof (EFI_TIME
));
832 // Acquire RTC Lock to make access to RTC atomic
834 if (!EfiAtRuntime ()) {
835 EfiAcquireLock (&Global
->RtcLock
);
838 // Wait for up to 0.1 seconds for the RTC to be updated
840 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
841 if (EFI_ERROR (Status
)) {
842 if (!EfiAtRuntime ()) {
843 EfiReleaseLock (&Global
->RtcLock
);
845 return EFI_DEVICE_ERROR
;
850 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
853 ConvertEfiTimeToRtcTime (&RtcTime
, RegisterB
);
856 // if the alarm is disable, record the current setting.
858 RtcTime
.Second
= RtcRead (RTC_ADDRESS_SECONDS_ALARM
);
859 RtcTime
.Minute
= RtcRead (RTC_ADDRESS_MINUTES_ALARM
);
860 RtcTime
.Hour
= RtcRead (RTC_ADDRESS_HOURS_ALARM
);
861 RtcTime
.Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
862 RtcTime
.Month
= RtcRead (RTC_ADDRESS_MONTH
);
863 RtcTime
.Year
= RtcRead (RTC_ADDRESS_YEAR
);
864 RtcTime
.TimeZone
= Global
->SavedTimeZone
;
865 RtcTime
.Daylight
= Global
->Daylight
;
869 // Set the Y/M/D info to variable as it has no corresponding hw registers.
871 Status
= EfiSetVariable (
874 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_NON_VOLATILE
,
878 if (EFI_ERROR (Status
)) {
879 if (!EfiAtRuntime ()) {
880 EfiReleaseLock (&Global
->RtcLock
);
882 return EFI_DEVICE_ERROR
;
886 // Inhibit updates of the RTC
888 RegisterB
.Bits
.Set
= 1;
889 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
893 // Set RTC alarm time
895 RtcWrite (RTC_ADDRESS_SECONDS_ALARM
, RtcTime
.Second
);
896 RtcWrite (RTC_ADDRESS_MINUTES_ALARM
, RtcTime
.Minute
);
897 RtcWrite (RTC_ADDRESS_HOURS_ALARM
, RtcTime
.Hour
);
899 RegisterB
.Bits
.Aie
= 1;
902 RegisterB
.Bits
.Aie
= 0;
905 // Allow updates of the RTC registers
907 RegisterB
.Bits
.Set
= 0;
908 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
913 if (!EfiAtRuntime ()) {
914 EfiReleaseLock (&Global
->RtcLock
);
921 Checks an 8-bit BCD value, and converts to an 8-bit value if valid.
923 This function checks the 8-bit BCD value specified by Value.
924 If valid, the function converts it to an 8-bit value and returns it.
925 Otherwise, return 0xff.
927 @param Value The 8-bit BCD value to check and convert
929 @return The 8-bit value converted. Or 0xff if Value is invalid.
933 CheckAndConvertBcd8ToDecimal8 (
937 if ((Value
< 0xa0) && ((Value
& 0xf) < 0xa)) {
938 return BcdToDecimal8 (Value
);
945 Converts time read from RTC to EFI_TIME format defined by UEFI spec.
947 This function converts raw time data read from RTC to the EFI_TIME format
948 defined by UEFI spec.
949 If data mode of RTC is BCD, then converts it to decimal,
950 If RTC is in 12-hour format, then converts it to 24-hour format.
952 @param Time On input, the time data read from RTC to convert
953 On output, the time converted to UEFI format
954 @param RegisterB Value of Register B of RTC, indicating data mode
957 @retval EFI_INVALID_PARAMETER Parameters passed in are invalid.
958 @retval EFI_SUCCESS Convert RTC time to EFI time successfully.
962 ConvertRtcTimeToEfiTime (
963 IN OUT EFI_TIME
*Time
,
964 IN RTC_REGISTER_B RegisterB
970 if ((Time
->Hour
& 0x80) != 0) {
976 Time
->Hour
= (UINT8
) (Time
->Hour
& 0x7f);
978 if (RegisterB
.Bits
.Dm
== 0) {
979 Time
->Year
= CheckAndConvertBcd8ToDecimal8 ((UINT8
) Time
->Year
);
980 Time
->Month
= CheckAndConvertBcd8ToDecimal8 (Time
->Month
);
981 Time
->Day
= CheckAndConvertBcd8ToDecimal8 (Time
->Day
);
982 Time
->Hour
= CheckAndConvertBcd8ToDecimal8 (Time
->Hour
);
983 Time
->Minute
= CheckAndConvertBcd8ToDecimal8 (Time
->Minute
);
984 Time
->Second
= CheckAndConvertBcd8ToDecimal8 (Time
->Second
);
987 if (Time
->Year
== 0xff || Time
->Month
== 0xff || Time
->Day
== 0xff ||
988 Time
->Hour
== 0xff || Time
->Minute
== 0xff || Time
->Second
== 0xff) {
989 return EFI_INVALID_PARAMETER
;
993 // For minimal/maximum year range [1970, 2069],
994 // Century is 19 if RTC year >= 70,
995 // Century is 20 otherwise.
997 Century
= (UINT8
) (PcdGet16 (PcdMinimalValidYear
) / 100);
998 if (Time
->Year
< PcdGet16 (PcdMinimalValidYear
) % 100) {
1001 Time
->Year
= (UINT16
) (Century
* 100 + Time
->Year
);
1004 // If time is in 12 hour format, convert it to 24 hour format
1006 if (RegisterB
.Bits
.Mil
== 0) {
1007 if (IsPM
&& Time
->Hour
< 12) {
1008 Time
->Hour
= (UINT8
) (Time
->Hour
+ 12);
1011 if (!IsPM
&& Time
->Hour
== 12) {
1016 Time
->Nanosecond
= 0;
1022 Wait for a period for the RTC to be ready.
1024 @param Timeout Tell how long it should take to wait.
1026 @retval EFI_DEVICE_ERROR RTC device error.
1027 @retval EFI_SUCCESS RTC is updated and ready.
1034 RTC_REGISTER_A RegisterA
;
1035 RTC_REGISTER_D RegisterD
;
1038 // See if the RTC is functioning correctly
1040 RegisterD
.Data
= RtcRead (RTC_ADDRESS_REGISTER_D
);
1042 if (RegisterD
.Bits
.Vrt
== 0) {
1043 return EFI_DEVICE_ERROR
;
1046 // Wait for up to 0.1 seconds for the RTC to be ready.
1048 Timeout
= (Timeout
/ 10) + 1;
1049 RegisterA
.Data
= RtcRead (RTC_ADDRESS_REGISTER_A
);
1050 while (RegisterA
.Bits
.Uip
== 1 && Timeout
> 0) {
1051 MicroSecondDelay (10);
1052 RegisterA
.Data
= RtcRead (RTC_ADDRESS_REGISTER_A
);
1056 RegisterD
.Data
= RtcRead (RTC_ADDRESS_REGISTER_D
);
1057 if (Timeout
== 0 || RegisterD
.Bits
.Vrt
== 0) {
1058 return EFI_DEVICE_ERROR
;
1065 See if all fields of a variable of EFI_TIME type is correct.
1067 @param Time The time to be checked.
1069 @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.
1070 @retval EFI_SUCCESS Time is a valid EFI_TIME variable.
1074 RtcTimeFieldsValid (
1078 if (Time
->Year
< PcdGet16 (PcdMinimalValidYear
) ||
1079 Time
->Year
> PcdGet16 (PcdMaximalValidYear
) ||
1082 (!DayValid (Time
)) ||
1084 Time
->Minute
> 59 ||
1085 Time
->Second
> 59 ||
1086 Time
->Nanosecond
> 999999999 ||
1087 (!(Time
->TimeZone
== EFI_UNSPECIFIED_TIMEZONE
|| (Time
->TimeZone
>= -1440 && Time
->TimeZone
<= 1440))) ||
1088 ((Time
->Daylight
& (~(EFI_TIME_ADJUST_DAYLIGHT
| EFI_TIME_IN_DAYLIGHT
))) != 0)) {
1089 return EFI_INVALID_PARAMETER
;
1096 See if field Day of an EFI_TIME is correct.
1098 @param Time Its Day field is to be checked.
1100 @retval TRUE Day field of Time is correct.
1101 @retval FALSE Day field of Time is NOT correct.
1109 // The validity of Time->Month field should be checked before
1111 ASSERT (Time
->Month
>=1);
1112 ASSERT (Time
->Month
<=12);
1113 if (Time
->Day
< 1 ||
1114 Time
->Day
> mDayOfMonth
[Time
->Month
- 1] ||
1115 (Time
->Month
== 2 && (!IsLeapYear (Time
) && Time
->Day
> 28))
1124 Check if it is a leap year.
1126 @param Time The time to be checked.
1128 @retval TRUE It is a leap year.
1129 @retval FALSE It is NOT a leap year.
1136 if (Time
->Year
% 4 == 0) {
1137 if (Time
->Year
% 100 == 0) {
1138 if (Time
->Year
% 400 == 0) {
1152 Converts time from EFI_TIME format defined by UEFI spec to RTC format.
1154 This function converts time from EFI_TIME format defined by UEFI spec to RTC format.
1155 If data mode of RTC is BCD, then converts EFI_TIME to it.
1156 If RTC is in 12-hour format, then converts EFI_TIME to it.
1158 @param Time On input, the time data read from UEFI to convert
1159 On output, the time converted to RTC format
1160 @param RegisterB Value of Register B of RTC, indicating data mode
1163 ConvertEfiTimeToRtcTime (
1164 IN OUT EFI_TIME
*Time
,
1165 IN RTC_REGISTER_B RegisterB
1172 // Adjust hour field if RTC is in 12 hour mode
1174 if (RegisterB
.Bits
.Mil
== 0) {
1175 if (Time
->Hour
< 12) {
1179 if (Time
->Hour
>= 13) {
1180 Time
->Hour
= (UINT8
) (Time
->Hour
- 12);
1181 } else if (Time
->Hour
== 0) {
1186 // Set the Time/Date values.
1188 Time
->Year
= (UINT16
) (Time
->Year
% 100);
1190 if (RegisterB
.Bits
.Dm
== 0) {
1191 Time
->Year
= DecimalToBcd8 ((UINT8
) Time
->Year
);
1192 Time
->Month
= DecimalToBcd8 (Time
->Month
);
1193 Time
->Day
= DecimalToBcd8 (Time
->Day
);
1194 Time
->Hour
= DecimalToBcd8 (Time
->Hour
);
1195 Time
->Minute
= DecimalToBcd8 (Time
->Minute
);
1196 Time
->Second
= DecimalToBcd8 (Time
->Second
);
1199 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
1201 if (RegisterB
.Bits
.Mil
== 0 && IsPM
) {
1202 Time
->Hour
= (UINT8
) (Time
->Hour
| 0x80);
1207 Compare the Hour, Minute and Second of the From time and the To time.
1209 Only compare H/M/S in EFI_TIME and ignore other fields here.
1211 @param From the first time
1212 @param To the second time
1214 @return >0 The H/M/S of the From time is later than those of To time
1215 @return ==0 The H/M/S of the From time is same as those of To time
1216 @return <0 The H/M/S of the From time is earlier than those of To time
1224 if ((From
->Hour
> To
->Hour
) ||
1225 ((From
->Hour
== To
->Hour
) && (From
->Minute
> To
->Minute
)) ||
1226 ((From
->Hour
== To
->Hour
) && (From
->Minute
== To
->Minute
) && (From
->Second
> To
->Second
))) {
1228 } else if ((From
->Hour
== To
->Hour
) && (From
->Minute
== To
->Minute
) && (From
->Second
== To
->Second
)) {
1236 To check if second date is later than first date within 24 hours.
1238 @param From the first date
1239 @param To the second date
1241 @retval TRUE From is previous to To within 24 hours.
1242 @retval FALSE From is later, or it is previous to To more than 24 hours.
1255 // The validity of From->Month field should be checked before
1257 ASSERT (From
->Month
>=1);
1258 ASSERT (From
->Month
<=12);
1260 if (From
->Year
== To
->Year
) {
1261 if (From
->Month
== To
->Month
) {
1262 if ((From
->Day
+ 1) == To
->Day
) {
1263 if ((CompareHMS(From
, To
) >= 0)) {
1266 } else if (From
->Day
== To
->Day
) {
1267 if ((CompareHMS(From
, To
) <= 0)) {
1271 } else if (((From
->Month
+ 1) == To
->Month
) && (To
->Day
== 1)) {
1272 if ((From
->Month
== 2) && !IsLeapYear(From
)) {
1273 if (From
->Day
== 28) {
1274 if ((CompareHMS(From
, To
) >= 0)) {
1278 } else if (From
->Day
== mDayOfMonth
[From
->Month
- 1]) {
1279 if ((CompareHMS(From
, To
) >= 0)) {
1284 } else if (((From
->Year
+ 1) == To
->Year
) &&
1285 (From
->Month
== 12) &&
1286 (From
->Day
== 31) &&
1289 if ((CompareHMS(From
, To
) >= 0)) {
1298 Get the century RTC address from the ACPI FADT table.
1300 @return The century RTC address or 0 if not found.
1303 GetCenturyRtcAddress (
1307 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
1309 Fadt
= (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*) EfiLocateFirstAcpiTable (
1310 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
1313 if ((Fadt
!= NULL
) &&
1314 (Fadt
->Century
> RTC_ADDRESS_REGISTER_D
) && (Fadt
->Century
< 0x80)
1316 return Fadt
->Century
;
1323 Notification function of ACPI Table change.
1325 This is a notification function registered on ACPI Table change event.
1326 It saves the Century address stored in ACPI FADT table.
1328 @param Event Event whose notification function is being invoked.
1329 @param Context Pointer to the notification function's context.
1334 PcRtcAcpiTableChangeCallback (
1341 UINT8 CenturyRtcAddress
;
1344 CenturyRtcAddress
= GetCenturyRtcAddress ();
1345 if ((CenturyRtcAddress
!= 0) && (mModuleGlobal
.CenturyRtcAddress
!= CenturyRtcAddress
)) {
1346 mModuleGlobal
.CenturyRtcAddress
= CenturyRtcAddress
;
1347 Status
= PcRtcGetTime (&Time
, NULL
, &mModuleGlobal
);
1348 if (!EFI_ERROR (Status
)) {
1349 Century
= (UINT8
) (Time
.Year
/ 100);
1350 Century
= DecimalToBcd8 (Century
);
1351 DEBUG ((EFI_D_INFO
, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century
, mModuleGlobal
.CenturyRtcAddress
));
1352 RtcWrite (mModuleGlobal
.CenturyRtcAddress
, Century
);