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
);
221 // Initialize RTC Register
223 // Make sure Division Chain is properly configured,
224 // or RTC clock won't "tick" -- time won't increment
226 RegisterA
.Data
= FixedPcdGet8 (PcdInitialValueRtcRegisterA
);
227 RtcWrite (RTC_ADDRESS_REGISTER_A
, RegisterA
.Data
);
232 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
235 // Clear RTC flag register
237 RtcRead (RTC_ADDRESS_REGISTER_C
);
240 // Clear RTC register D
242 RegisterD
.Data
= FixedPcdGet8 (PcdInitialValueRtcRegisterD
);
243 RtcWrite (RTC_ADDRESS_REGISTER_D
, RegisterD
.Data
);
246 // Wait for up to 0.1 seconds for the RTC to be updated
248 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
249 if (EFI_ERROR (Status
)) {
251 // Set the variable with default value if the RTC is functioning incorrectly.
253 Global
->SavedTimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
254 Global
->Daylight
= 0;
255 if (!EfiAtRuntime ()) {
256 EfiReleaseLock (&Global
->RtcLock
);
259 return EFI_DEVICE_ERROR
;
263 // Get the Time/Date/Daylight Savings values.
265 Time
.Second
= RtcRead (RTC_ADDRESS_SECONDS
);
266 Time
.Minute
= RtcRead (RTC_ADDRESS_MINUTES
);
267 Time
.Hour
= RtcRead (RTC_ADDRESS_HOURS
);
268 Time
.Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
269 Time
.Month
= RtcRead (RTC_ADDRESS_MONTH
);
270 Time
.Year
= RtcRead (RTC_ADDRESS_YEAR
);
275 if (!EfiAtRuntime ()) {
276 EfiReleaseLock (&Global
->RtcLock
);
280 // Get the data of Daylight saving and time zone, if they have been
281 // stored in NV variable during previous boot.
283 DataSize
= sizeof (UINT32
);
284 Status
= EfiGetVariable (
285 mTimeZoneVariableName
,
291 if (!EFI_ERROR (Status
)) {
292 Time
.TimeZone
= (INT16
)TimerVar
;
293 Time
.Daylight
= (UINT8
)(TimerVar
>> 16);
295 Time
.TimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
300 // Validate time fields
302 Status
= ConvertRtcTimeToEfiTime (&Time
, RegisterB
);
303 if (!EFI_ERROR (Status
)) {
304 Status
= RtcTimeFieldsValid (&Time
);
307 if (EFI_ERROR (Status
)) {
309 // Report Status Code to indicate that the RTC has bad date and time
312 EFI_ERROR_CODE
| EFI_ERROR_MINOR
,
313 (EFI_SOFTWARE_DXE_RT_DRIVER
| EFI_SW_EC_BAD_DATE_TIME
)
315 Time
.Second
= RTC_INIT_SECOND
;
316 Time
.Minute
= RTC_INIT_MINUTE
;
317 Time
.Hour
= RTC_INIT_HOUR
;
318 Time
.Day
= RTC_INIT_DAY
;
319 Time
.Month
= RTC_INIT_MONTH
;
320 Time
.Year
= PcdGet16 (PcdMinimalValidYear
);
322 Time
.TimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
327 // Set RTC configuration after get original time
328 // The value of bit AIE should be reserved.
330 RegisterB
.Data
= FixedPcdGet8 (PcdInitialValueRtcRegisterB
) | (RegisterB
.Data
& BIT5
);
331 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
334 // Reset time value according to new RTC configuration
336 Status
= PcRtcSetTime (&Time
, Global
);
337 if (EFI_ERROR (Status
)) {
338 return EFI_DEVICE_ERROR
;
342 // Reset wakeup time value to valid state when wakeup alarm is disabled and wakeup time is invalid.
343 // Global variable has already had valid SavedTimeZone and Daylight,
344 // so we can use them to get and set wakeup time.
346 Status
= PcRtcGetWakeupTime (&Enabled
, &Pending
, &Time
, Global
);
347 if ((Enabled
) || (!EFI_ERROR (Status
))) {
352 // When wakeup time is disabled and invalid, reset wakeup time register to valid state
353 // but keep wakeup alarm disabled.
355 Time
.Second
= RTC_INIT_SECOND
;
356 Time
.Minute
= RTC_INIT_MINUTE
;
357 Time
.Hour
= RTC_INIT_HOUR
;
358 Time
.Day
= RTC_INIT_DAY
;
359 Time
.Month
= RTC_INIT_MONTH
;
360 Time
.Year
= PcdGet16 (PcdMinimalValidYear
);
362 Time
.TimeZone
= Global
->SavedTimeZone
;
363 Time
.Daylight
= Global
->Daylight
;
366 // Acquire RTC Lock to make access to RTC atomic
368 if (!EfiAtRuntime ()) {
369 EfiAcquireLock (&Global
->RtcLock
);
373 // Wait for up to 0.1 seconds for the RTC to be updated
375 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
376 if (EFI_ERROR (Status
)) {
377 if (!EfiAtRuntime ()) {
378 EfiReleaseLock (&Global
->RtcLock
);
381 return EFI_DEVICE_ERROR
;
384 ConvertEfiTimeToRtcTime (&Time
, RegisterB
);
387 // Set the Y/M/D info to variable as it has no corresponding hw registers.
389 Status
= EfiSetVariable (
392 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_NON_VOLATILE
,
396 if (EFI_ERROR (Status
)) {
397 if (!EfiAtRuntime ()) {
398 EfiReleaseLock (&Global
->RtcLock
);
401 return EFI_DEVICE_ERROR
;
405 // Inhibit updates of the RTC
407 RegisterB
.Bits
.Set
= 1;
408 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
411 // Set RTC alarm time registers
413 RtcWrite (RTC_ADDRESS_SECONDS_ALARM
, Time
.Second
);
414 RtcWrite (RTC_ADDRESS_MINUTES_ALARM
, Time
.Minute
);
415 RtcWrite (RTC_ADDRESS_HOURS_ALARM
, Time
.Hour
);
418 // Allow updates of the RTC registers
420 RegisterB
.Bits
.Set
= 0;
421 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
426 if (!EfiAtRuntime ()) {
427 EfiReleaseLock (&Global
->RtcLock
);
434 Returns the current time and date information, and the time-keeping capabilities
435 of the hardware platform.
437 @param Time A pointer to storage to receive a snapshot of the current time.
438 @param Capabilities An optional pointer to a buffer to receive the real time clock
439 device's capabilities.
440 @param Global For global use inside this module.
442 @retval EFI_SUCCESS The operation completed successfully.
443 @retval EFI_INVALID_PARAMETER Time is NULL.
444 @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.
450 OUT EFI_TIME_CAPABILITIES
*Capabilities OPTIONAL
,
451 IN PC_RTC_MODULE_GLOBALS
*Global
455 RTC_REGISTER_B RegisterB
;
458 // Check parameters for null pointer
461 return EFI_INVALID_PARAMETER
;
465 // Acquire RTC Lock to make access to RTC atomic
467 if (!EfiAtRuntime ()) {
468 EfiAcquireLock (&Global
->RtcLock
);
472 // Wait for up to 0.1 seconds for the RTC to be updated
474 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
475 if (EFI_ERROR (Status
)) {
476 if (!EfiAtRuntime ()) {
477 EfiReleaseLock (&Global
->RtcLock
);
486 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
489 // Get the Time/Date/Daylight Savings values.
491 Time
->Second
= RtcRead (RTC_ADDRESS_SECONDS
);
492 Time
->Minute
= RtcRead (RTC_ADDRESS_MINUTES
);
493 Time
->Hour
= RtcRead (RTC_ADDRESS_HOURS
);
494 Time
->Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
495 Time
->Month
= RtcRead (RTC_ADDRESS_MONTH
);
496 Time
->Year
= RtcRead (RTC_ADDRESS_YEAR
);
501 if (!EfiAtRuntime ()) {
502 EfiReleaseLock (&Global
->RtcLock
);
506 // Get the variable that contains the TimeZone and Daylight fields
508 Time
->TimeZone
= Global
->SavedTimeZone
;
509 Time
->Daylight
= Global
->Daylight
;
512 // Make sure all field values are in correct range
514 Status
= ConvertRtcTimeToEfiTime (Time
, RegisterB
);
515 if (!EFI_ERROR (Status
)) {
516 Status
= RtcTimeFieldsValid (Time
);
519 if (EFI_ERROR (Status
)) {
520 return EFI_DEVICE_ERROR
;
524 // Fill in Capabilities if it was passed in
526 if (Capabilities
!= NULL
) {
527 Capabilities
->Resolution
= 1;
531 Capabilities
->Accuracy
= 50000000;
535 Capabilities
->SetsToZero
= FALSE
;
542 Sets the current local time and date information.
544 @param Time A pointer to the current time.
545 @param Global For global use inside this module.
547 @retval EFI_SUCCESS The operation completed successfully.
548 @retval EFI_INVALID_PARAMETER A time field is out of range.
549 @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.
555 IN PC_RTC_MODULE_GLOBALS
*Global
560 RTC_REGISTER_B RegisterB
;
564 return EFI_INVALID_PARAMETER
;
568 // Make sure that the time fields are valid
570 Status
= RtcTimeFieldsValid (Time
);
571 if (EFI_ERROR (Status
)) {
575 CopyMem (&RtcTime
, Time
, sizeof (EFI_TIME
));
578 // Acquire RTC Lock to make access to RTC atomic
580 if (!EfiAtRuntime ()) {
581 EfiAcquireLock (&Global
->RtcLock
);
585 // Wait for up to 0.1 seconds for the RTC to be updated
587 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
588 if (EFI_ERROR (Status
)) {
589 if (!EfiAtRuntime ()) {
590 EfiReleaseLock (&Global
->RtcLock
);
597 // Write timezone and daylight to RTC variable
599 if ((Time
->TimeZone
== EFI_UNSPECIFIED_TIMEZONE
) && (Time
->Daylight
== 0)) {
600 Status
= EfiSetVariable (
601 mTimeZoneVariableName
,
607 if (Status
== EFI_NOT_FOUND
) {
608 Status
= EFI_SUCCESS
;
611 TimerVar
= Time
->Daylight
;
612 TimerVar
= (UINT32
)((TimerVar
<< 16) | (UINT16
)(Time
->TimeZone
));
613 Status
= EfiSetVariable (
614 mTimeZoneVariableName
,
616 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_NON_VOLATILE
,
622 if (EFI_ERROR (Status
)) {
623 if (!EfiAtRuntime ()) {
624 EfiReleaseLock (&Global
->RtcLock
);
627 return EFI_DEVICE_ERROR
;
631 // Read Register B, and inhibit updates of the RTC
633 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
634 RegisterB
.Bits
.Set
= 1;
635 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
638 // Store the century value to RTC before converting to BCD format.
640 if (Global
->CenturyRtcAddress
!= 0) {
641 RtcWrite (Global
->CenturyRtcAddress
, DecimalToBcd8 ((UINT8
)(RtcTime
.Year
/ 100)));
644 ConvertEfiTimeToRtcTime (&RtcTime
, RegisterB
);
646 RtcWrite (RTC_ADDRESS_SECONDS
, RtcTime
.Second
);
647 RtcWrite (RTC_ADDRESS_MINUTES
, RtcTime
.Minute
);
648 RtcWrite (RTC_ADDRESS_HOURS
, RtcTime
.Hour
);
649 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH
, RtcTime
.Day
);
650 RtcWrite (RTC_ADDRESS_MONTH
, RtcTime
.Month
);
651 RtcWrite (RTC_ADDRESS_YEAR
, (UINT8
)RtcTime
.Year
);
654 // Allow updates of the RTC registers
656 RegisterB
.Bits
.Set
= 0;
657 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
662 if (!EfiAtRuntime ()) {
663 EfiReleaseLock (&Global
->RtcLock
);
667 // Set the variable that contains the TimeZone and Daylight fields
669 Global
->SavedTimeZone
= Time
->TimeZone
;
670 Global
->Daylight
= Time
->Daylight
;
676 Returns the current wakeup alarm clock setting.
678 @param Enabled Indicates if the alarm is currently enabled or disabled.
679 @param Pending Indicates if the alarm signal is pending and requires acknowledgment.
680 @param Time The current alarm setting.
681 @param Global For global use inside this module.
683 @retval EFI_SUCCESS The alarm settings were returned.
684 @retval EFI_INVALID_PARAMETER Enabled is NULL.
685 @retval EFI_INVALID_PARAMETER Pending is NULL.
686 @retval EFI_INVALID_PARAMETER Time is NULL.
687 @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.
688 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
693 OUT BOOLEAN
*Enabled
,
694 OUT BOOLEAN
*Pending
,
696 IN PC_RTC_MODULE_GLOBALS
*Global
700 RTC_REGISTER_B RegisterB
;
701 RTC_REGISTER_C RegisterC
;
706 // Check parameters for null pointers
708 if ((Enabled
== NULL
) || (Pending
== NULL
) || (Time
== NULL
)) {
709 return EFI_INVALID_PARAMETER
;
713 // Acquire RTC Lock to make access to RTC atomic
715 if (!EfiAtRuntime ()) {
716 EfiAcquireLock (&Global
->RtcLock
);
720 // Wait for up to 0.1 seconds for the RTC to be updated
722 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
723 if (EFI_ERROR (Status
)) {
724 if (!EfiAtRuntime ()) {
725 EfiReleaseLock (&Global
->RtcLock
);
728 return EFI_DEVICE_ERROR
;
732 // Read Register B and Register C
734 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
735 RegisterC
.Data
= RtcRead (RTC_ADDRESS_REGISTER_C
);
738 // Get the Time/Date/Daylight Savings values.
740 *Enabled
= RegisterB
.Bits
.Aie
;
741 *Pending
= RegisterC
.Bits
.Af
;
743 Time
->Second
= RtcRead (RTC_ADDRESS_SECONDS_ALARM
);
744 Time
->Minute
= RtcRead (RTC_ADDRESS_MINUTES_ALARM
);
745 Time
->Hour
= RtcRead (RTC_ADDRESS_HOURS_ALARM
);
746 Time
->Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
747 Time
->Month
= RtcRead (RTC_ADDRESS_MONTH
);
748 Time
->Year
= RtcRead (RTC_ADDRESS_YEAR
);
749 Time
->TimeZone
= Global
->SavedTimeZone
;
750 Time
->Daylight
= Global
->Daylight
;
753 // Get the alarm info from variable
755 DataSize
= sizeof (EFI_TIME
);
756 Status
= EfiGetVariable (
763 if (!EFI_ERROR (Status
)) {
765 // The alarm variable exists. In this case, we read variable to get info.
767 Time
->Day
= RtcTime
.Day
;
768 Time
->Month
= RtcTime
.Month
;
769 Time
->Year
= RtcTime
.Year
;
775 if (!EfiAtRuntime ()) {
776 EfiReleaseLock (&Global
->RtcLock
);
780 // Make sure all field values are in correct range
782 Status
= ConvertRtcTimeToEfiTime (Time
, RegisterB
);
783 if (!EFI_ERROR (Status
)) {
784 Status
= RtcTimeFieldsValid (Time
);
787 if (EFI_ERROR (Status
)) {
788 return EFI_DEVICE_ERROR
;
795 Sets the system wakeup alarm clock time.
797 @param Enabled Enable or disable the wakeup alarm.
798 @param Time If Enable is TRUE, the time to set the wakeup alarm for.
799 If Enable is FALSE, then this parameter is optional, and may be NULL.
800 @param Global For global use inside this module.
802 @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled.
803 If Enable is FALSE, then the wakeup alarm was disabled.
804 @retval EFI_INVALID_PARAMETER A time field is out of range.
805 @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.
806 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
812 IN EFI_TIME
*Time OPTIONAL
,
813 IN PC_RTC_MODULE_GLOBALS
*Global
818 RTC_REGISTER_B RegisterB
;
819 EFI_TIME_CAPABILITIES Capabilities
;
821 ZeroMem (&RtcTime
, sizeof (RtcTime
));
825 return EFI_INVALID_PARAMETER
;
829 // Make sure that the time fields are valid
831 Status
= RtcTimeFieldsValid (Time
);
832 if (EFI_ERROR (Status
)) {
833 return EFI_INVALID_PARAMETER
;
837 // Just support set alarm time within 24 hours
839 PcRtcGetTime (&RtcTime
, &Capabilities
, Global
);
840 Status
= RtcTimeFieldsValid (&RtcTime
);
841 if (EFI_ERROR (Status
)) {
842 return EFI_DEVICE_ERROR
;
845 if (!IsWithinOneDay (&RtcTime
, Time
)) {
846 return EFI_UNSUPPORTED
;
850 // Make a local copy of the time and date
852 CopyMem (&RtcTime
, Time
, sizeof (EFI_TIME
));
856 // Acquire RTC Lock to make access to RTC atomic
858 if (!EfiAtRuntime ()) {
859 EfiAcquireLock (&Global
->RtcLock
);
863 // Wait for up to 0.1 seconds for the RTC to be updated
865 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
866 if (EFI_ERROR (Status
)) {
867 if (!EfiAtRuntime ()) {
868 EfiReleaseLock (&Global
->RtcLock
);
871 return EFI_DEVICE_ERROR
;
877 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
880 ConvertEfiTimeToRtcTime (&RtcTime
, RegisterB
);
883 // if the alarm is disable, record the current setting.
885 RtcTime
.Second
= RtcRead (RTC_ADDRESS_SECONDS_ALARM
);
886 RtcTime
.Minute
= RtcRead (RTC_ADDRESS_MINUTES_ALARM
);
887 RtcTime
.Hour
= RtcRead (RTC_ADDRESS_HOURS_ALARM
);
888 RtcTime
.Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
889 RtcTime
.Month
= RtcRead (RTC_ADDRESS_MONTH
);
890 RtcTime
.Year
= RtcRead (RTC_ADDRESS_YEAR
);
891 RtcTime
.TimeZone
= Global
->SavedTimeZone
;
892 RtcTime
.Daylight
= Global
->Daylight
;
896 // Set the Y/M/D info to variable as it has no corresponding hw registers.
898 Status
= EfiSetVariable (
901 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_NON_VOLATILE
,
905 if (EFI_ERROR (Status
)) {
906 if (!EfiAtRuntime ()) {
907 EfiReleaseLock (&Global
->RtcLock
);
910 return EFI_DEVICE_ERROR
;
914 // Inhibit updates of the RTC
916 RegisterB
.Bits
.Set
= 1;
917 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
921 // Set RTC alarm time
923 RtcWrite (RTC_ADDRESS_SECONDS_ALARM
, RtcTime
.Second
);
924 RtcWrite (RTC_ADDRESS_MINUTES_ALARM
, RtcTime
.Minute
);
925 RtcWrite (RTC_ADDRESS_HOURS_ALARM
, RtcTime
.Hour
);
927 RegisterB
.Bits
.Aie
= 1;
929 RegisterB
.Bits
.Aie
= 0;
933 // Allow updates of the RTC registers
935 RegisterB
.Bits
.Set
= 0;
936 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
941 if (!EfiAtRuntime ()) {
942 EfiReleaseLock (&Global
->RtcLock
);
949 Checks an 8-bit BCD value, and converts to an 8-bit value if valid.
951 This function checks the 8-bit BCD value specified by Value.
952 If valid, the function converts it to an 8-bit value and returns it.
953 Otherwise, return 0xff.
955 @param Value The 8-bit BCD value to check and convert
957 @return The 8-bit value converted. Or 0xff if Value is invalid.
961 CheckAndConvertBcd8ToDecimal8 (
965 if ((Value
< 0xa0) && ((Value
& 0xf) < 0xa)) {
966 return BcdToDecimal8 (Value
);
973 Converts time read from RTC to EFI_TIME format defined by UEFI spec.
975 This function converts raw time data read from RTC to the EFI_TIME format
976 defined by UEFI spec.
977 If data mode of RTC is BCD, then converts it to decimal,
978 If RTC is in 12-hour format, then converts it to 24-hour format.
980 @param Time On input, the time data read from RTC to convert
981 On output, the time converted to UEFI format
982 @param RegisterB Value of Register B of RTC, indicating data mode
985 @retval EFI_INVALID_PARAMETER Parameters passed in are invalid.
986 @retval EFI_SUCCESS Convert RTC time to EFI time successfully.
990 ConvertRtcTimeToEfiTime (
991 IN OUT EFI_TIME
*Time
,
992 IN RTC_REGISTER_B RegisterB
998 // IsPM only makes sense for 12-hour format.
999 if (RegisterB
.Bits
.Mil
== 0) {
1000 if ((Time
->Hour
& 0x80) != 0) {
1006 Time
->Hour
= (UINT8
)(Time
->Hour
& 0x7f);
1009 if (RegisterB
.Bits
.Dm
== 0) {
1010 Time
->Year
= CheckAndConvertBcd8ToDecimal8 ((UINT8
)Time
->Year
);
1011 Time
->Month
= CheckAndConvertBcd8ToDecimal8 (Time
->Month
);
1012 Time
->Day
= CheckAndConvertBcd8ToDecimal8 (Time
->Day
);
1013 Time
->Hour
= CheckAndConvertBcd8ToDecimal8 (Time
->Hour
);
1014 Time
->Minute
= CheckAndConvertBcd8ToDecimal8 (Time
->Minute
);
1015 Time
->Second
= CheckAndConvertBcd8ToDecimal8 (Time
->Second
);
1018 if ((Time
->Year
== 0xff) || (Time
->Month
== 0xff) || (Time
->Day
== 0xff) ||
1019 (Time
->Hour
== 0xff) || (Time
->Minute
== 0xff) || (Time
->Second
== 0xff))
1021 return EFI_INVALID_PARAMETER
;
1025 // For minimal/maximum year range [1970, 2069],
1026 // Century is 19 if RTC year >= 70,
1027 // Century is 20 otherwise.
1029 Century
= (UINT8
)(PcdGet16 (PcdMinimalValidYear
) / 100);
1030 if (Time
->Year
< PcdGet16 (PcdMinimalValidYear
) % 100) {
1034 Time
->Year
= (UINT16
)(Century
* 100 + Time
->Year
);
1037 // If time is in 12 hour format, convert it to 24 hour format
1039 if (RegisterB
.Bits
.Mil
== 0) {
1040 if (IsPM
&& (Time
->Hour
< 12)) {
1041 Time
->Hour
= (UINT8
)(Time
->Hour
+ 12);
1044 if (!IsPM
&& (Time
->Hour
== 12)) {
1049 Time
->Nanosecond
= 0;
1055 Wait for a period for the RTC to be ready.
1057 @param Timeout Tell how long it should take to wait.
1059 @retval EFI_DEVICE_ERROR RTC device error.
1060 @retval EFI_SUCCESS RTC is updated and ready.
1067 RTC_REGISTER_A RegisterA
;
1068 RTC_REGISTER_D RegisterD
;
1071 // See if the RTC is functioning correctly
1073 RegisterD
.Data
= RtcRead (RTC_ADDRESS_REGISTER_D
);
1075 if (RegisterD
.Bits
.Vrt
== 0) {
1076 return EFI_DEVICE_ERROR
;
1080 // Wait for up to 0.1 seconds for the RTC to be ready.
1082 Timeout
= (Timeout
/ 10) + 1;
1083 RegisterA
.Data
= RtcRead (RTC_ADDRESS_REGISTER_A
);
1084 while (RegisterA
.Bits
.Uip
== 1 && Timeout
> 0) {
1085 MicroSecondDelay (10);
1086 RegisterA
.Data
= RtcRead (RTC_ADDRESS_REGISTER_A
);
1090 RegisterD
.Data
= RtcRead (RTC_ADDRESS_REGISTER_D
);
1091 if ((Timeout
== 0) || (RegisterD
.Bits
.Vrt
== 0)) {
1092 return EFI_DEVICE_ERROR
;
1099 See if all fields of a variable of EFI_TIME type is correct.
1101 @param Time The time to be checked.
1103 @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.
1104 @retval EFI_SUCCESS Time is a valid EFI_TIME variable.
1108 RtcTimeFieldsValid (
1112 if ((Time
->Year
< PcdGet16 (PcdMinimalValidYear
)) ||
1113 (Time
->Year
> PcdGet16 (PcdMaximalValidYear
)) ||
1114 (Time
->Month
< 1) ||
1115 (Time
->Month
> 12) ||
1116 (!DayValid (Time
)) ||
1117 (Time
->Hour
> 23) ||
1118 (Time
->Minute
> 59) ||
1119 (Time
->Second
> 59) ||
1120 (Time
->Nanosecond
> 999999999) ||
1121 (!((Time
->TimeZone
== EFI_UNSPECIFIED_TIMEZONE
) || ((Time
->TimeZone
>= -1440) && (Time
->TimeZone
<= 1440)))) ||
1122 ((Time
->Daylight
& (~(EFI_TIME_ADJUST_DAYLIGHT
| EFI_TIME_IN_DAYLIGHT
))) != 0))
1124 return EFI_INVALID_PARAMETER
;
1131 See if field Day of an EFI_TIME is correct.
1133 @param Time Its Day field is to be checked.
1135 @retval TRUE Day field of Time is correct.
1136 @retval FALSE Day field of Time is NOT correct.
1144 // The validity of Time->Month field should be checked before
1146 ASSERT (Time
->Month
>= 1);
1147 ASSERT (Time
->Month
<= 12);
1148 if ((Time
->Day
< 1) ||
1149 (Time
->Day
> mDayOfMonth
[Time
->Month
- 1]) ||
1150 ((Time
->Month
== 2) && (!IsLeapYear (Time
) && (Time
->Day
> 28)))
1160 Check if it is a leap year.
1162 @param Time The time to be checked.
1164 @retval TRUE It is a leap year.
1165 @retval FALSE It is NOT a leap year.
1172 if (Time
->Year
% 4 == 0) {
1173 if (Time
->Year
% 100 == 0) {
1174 if (Time
->Year
% 400 == 0) {
1188 Converts time from EFI_TIME format defined by UEFI spec to RTC format.
1190 This function converts time from EFI_TIME format defined by UEFI spec to RTC format.
1191 If data mode of RTC is BCD, then converts EFI_TIME to it.
1192 If RTC is in 12-hour format, then converts EFI_TIME to it.
1194 @param Time On input, the time data read from UEFI to convert
1195 On output, the time converted to RTC format
1196 @param RegisterB Value of Register B of RTC, indicating data mode
1199 ConvertEfiTimeToRtcTime (
1200 IN OUT EFI_TIME
*Time
,
1201 IN RTC_REGISTER_B RegisterB
1208 // Adjust hour field if RTC is in 12 hour mode
1210 if (RegisterB
.Bits
.Mil
== 0) {
1211 if (Time
->Hour
< 12) {
1215 if (Time
->Hour
>= 13) {
1216 Time
->Hour
= (UINT8
)(Time
->Hour
- 12);
1217 } else if (Time
->Hour
== 0) {
1223 // Set the Time/Date values.
1225 Time
->Year
= (UINT16
)(Time
->Year
% 100);
1227 if (RegisterB
.Bits
.Dm
== 0) {
1228 Time
->Year
= DecimalToBcd8 ((UINT8
)Time
->Year
);
1229 Time
->Month
= DecimalToBcd8 (Time
->Month
);
1230 Time
->Day
= DecimalToBcd8 (Time
->Day
);
1231 Time
->Hour
= DecimalToBcd8 (Time
->Hour
);
1232 Time
->Minute
= DecimalToBcd8 (Time
->Minute
);
1233 Time
->Second
= DecimalToBcd8 (Time
->Second
);
1237 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
1239 if ((RegisterB
.Bits
.Mil
== 0) && IsPM
) {
1240 Time
->Hour
= (UINT8
)(Time
->Hour
| 0x80);
1245 Compare the Hour, Minute and Second of the From time and the To time.
1247 Only compare H/M/S in EFI_TIME and ignore other fields here.
1249 @param From the first time
1250 @param To the second time
1252 @return >0 The H/M/S of the From time is later than those of To time
1253 @return ==0 The H/M/S of the From time is same as those of To time
1254 @return <0 The H/M/S of the From time is earlier than those of To time
1262 if ((From
->Hour
> To
->Hour
) ||
1263 ((From
->Hour
== To
->Hour
) && (From
->Minute
> To
->Minute
)) ||
1264 ((From
->Hour
== To
->Hour
) && (From
->Minute
== To
->Minute
) && (From
->Second
> To
->Second
)))
1267 } else if ((From
->Hour
== To
->Hour
) && (From
->Minute
== To
->Minute
) && (From
->Second
== To
->Second
)) {
1275 To check if second date is later than first date within 24 hours.
1277 @param From the first date
1278 @param To the second date
1280 @retval TRUE From is previous to To within 24 hours.
1281 @retval FALSE From is later, or it is previous to To more than 24 hours.
1294 // The validity of From->Month field should be checked before
1296 ASSERT (From
->Month
>= 1);
1297 ASSERT (From
->Month
<= 12);
1299 if (From
->Year
== To
->Year
) {
1300 if (From
->Month
== To
->Month
) {
1301 if ((From
->Day
+ 1) == To
->Day
) {
1302 if ((CompareHMS (From
, To
) >= 0)) {
1305 } else if (From
->Day
== To
->Day
) {
1306 if ((CompareHMS (From
, To
) <= 0)) {
1310 } else if (((From
->Month
+ 1) == To
->Month
) && (To
->Day
== 1)) {
1311 if ((From
->Month
== 2) && !IsLeapYear (From
)) {
1312 if (From
->Day
== 28) {
1313 if ((CompareHMS (From
, To
) >= 0)) {
1317 } else if (From
->Day
== mDayOfMonth
[From
->Month
- 1]) {
1318 if ((CompareHMS (From
, To
) >= 0)) {
1323 } else if (((From
->Year
+ 1) == To
->Year
) &&
1324 (From
->Month
== 12) &&
1325 (From
->Day
== 31) &&
1329 if ((CompareHMS (From
, To
) >= 0)) {
1338 Get the century RTC address from the ACPI FADT table.
1340 @return The century RTC address or 0 if not found.
1343 GetCenturyRtcAddress (
1347 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
1349 Fadt
= (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*)EfiLocateFirstAcpiTable (
1350 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
1353 if ((Fadt
!= NULL
) &&
1354 (Fadt
->Century
> RTC_ADDRESS_REGISTER_D
) && (Fadt
->Century
< 0x80)
1357 return Fadt
->Century
;
1364 Notification function of ACPI Table change.
1366 This is a notification function registered on ACPI Table change event.
1367 It saves the Century address stored in ACPI FADT table.
1369 @param Event Event whose notification function is being invoked.
1370 @param Context Pointer to the notification function's context.
1375 PcRtcAcpiTableChangeCallback (
1382 UINT8 CenturyRtcAddress
;
1385 CenturyRtcAddress
= GetCenturyRtcAddress ();
1386 if ((CenturyRtcAddress
!= 0) && (mModuleGlobal
.CenturyRtcAddress
!= CenturyRtcAddress
)) {
1387 mModuleGlobal
.CenturyRtcAddress
= CenturyRtcAddress
;
1388 Status
= PcRtcGetTime (&Time
, NULL
, &mModuleGlobal
);
1389 if (!EFI_ERROR (Status
)) {
1390 Century
= (UINT8
)(Time
.Year
/ 100);
1391 Century
= DecimalToBcd8 (Century
);
1392 DEBUG ((DEBUG_INFO
, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century
, mModuleGlobal
.CenturyRtcAddress
));
1393 RtcWrite (mModuleGlobal
.CenturyRtcAddress
, Century
);