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
);
273 // Set RTC configuration after get original time
274 // The value of bit AIE should be reserved.
276 RegisterB
.Data
= FixedPcdGet8 (PcdInitialValueRtcRegisterB
) | (RegisterB
.Data
& BIT5
);
277 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
282 if (!EfiAtRuntime ()) {
283 EfiReleaseLock (&Global
->RtcLock
);
287 // Get the data of Daylight saving and time zone, if they have been
288 // stored in NV variable during previous boot.
290 DataSize
= sizeof (UINT32
);
291 Status
= EfiGetVariable (
292 mTimeZoneVariableName
,
298 if (!EFI_ERROR (Status
)) {
299 Time
.TimeZone
= (INT16
)TimerVar
;
300 Time
.Daylight
= (UINT8
)(TimerVar
>> 16);
302 Time
.TimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
307 // Validate time fields
309 Status
= ConvertRtcTimeToEfiTime (&Time
, RegisterB
);
310 if (!EFI_ERROR (Status
)) {
311 Status
= RtcTimeFieldsValid (&Time
);
314 if (EFI_ERROR (Status
)) {
316 // Report Status Code to indicate that the RTC has bad date and time
319 EFI_ERROR_CODE
| EFI_ERROR_MINOR
,
320 (EFI_SOFTWARE_DXE_RT_DRIVER
| EFI_SW_EC_BAD_DATE_TIME
)
322 Time
.Second
= RTC_INIT_SECOND
;
323 Time
.Minute
= RTC_INIT_MINUTE
;
324 Time
.Hour
= RTC_INIT_HOUR
;
325 Time
.Day
= RTC_INIT_DAY
;
326 Time
.Month
= RTC_INIT_MONTH
;
327 Time
.Year
= PcdGet16 (PcdMinimalValidYear
);
329 Time
.TimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
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 if ((Time
->Hour
& 0x80) != 0) {
1004 Time
->Hour
= (UINT8
)(Time
->Hour
& 0x7f);
1006 if (RegisterB
.Bits
.Dm
== 0) {
1007 Time
->Year
= CheckAndConvertBcd8ToDecimal8 ((UINT8
)Time
->Year
);
1008 Time
->Month
= CheckAndConvertBcd8ToDecimal8 (Time
->Month
);
1009 Time
->Day
= CheckAndConvertBcd8ToDecimal8 (Time
->Day
);
1010 Time
->Hour
= CheckAndConvertBcd8ToDecimal8 (Time
->Hour
);
1011 Time
->Minute
= CheckAndConvertBcd8ToDecimal8 (Time
->Minute
);
1012 Time
->Second
= CheckAndConvertBcd8ToDecimal8 (Time
->Second
);
1015 if ((Time
->Year
== 0xff) || (Time
->Month
== 0xff) || (Time
->Day
== 0xff) ||
1016 (Time
->Hour
== 0xff) || (Time
->Minute
== 0xff) || (Time
->Second
== 0xff))
1018 return EFI_INVALID_PARAMETER
;
1022 // For minimal/maximum year range [1970, 2069],
1023 // Century is 19 if RTC year >= 70,
1024 // Century is 20 otherwise.
1026 Century
= (UINT8
)(PcdGet16 (PcdMinimalValidYear
) / 100);
1027 if (Time
->Year
< PcdGet16 (PcdMinimalValidYear
) % 100) {
1031 Time
->Year
= (UINT16
)(Century
* 100 + Time
->Year
);
1034 // If time is in 12 hour format, convert it to 24 hour format
1036 if (RegisterB
.Bits
.Mil
== 0) {
1037 if (IsPM
&& (Time
->Hour
< 12)) {
1038 Time
->Hour
= (UINT8
)(Time
->Hour
+ 12);
1041 if (!IsPM
&& (Time
->Hour
== 12)) {
1046 Time
->Nanosecond
= 0;
1052 Wait for a period for the RTC to be ready.
1054 @param Timeout Tell how long it should take to wait.
1056 @retval EFI_DEVICE_ERROR RTC device error.
1057 @retval EFI_SUCCESS RTC is updated and ready.
1064 RTC_REGISTER_A RegisterA
;
1065 RTC_REGISTER_D RegisterD
;
1068 // See if the RTC is functioning correctly
1070 RegisterD
.Data
= RtcRead (RTC_ADDRESS_REGISTER_D
);
1072 if (RegisterD
.Bits
.Vrt
== 0) {
1073 return EFI_DEVICE_ERROR
;
1077 // Wait for up to 0.1 seconds for the RTC to be ready.
1079 Timeout
= (Timeout
/ 10) + 1;
1080 RegisterA
.Data
= RtcRead (RTC_ADDRESS_REGISTER_A
);
1081 while (RegisterA
.Bits
.Uip
== 1 && Timeout
> 0) {
1082 MicroSecondDelay (10);
1083 RegisterA
.Data
= RtcRead (RTC_ADDRESS_REGISTER_A
);
1087 RegisterD
.Data
= RtcRead (RTC_ADDRESS_REGISTER_D
);
1088 if ((Timeout
== 0) || (RegisterD
.Bits
.Vrt
== 0)) {
1089 return EFI_DEVICE_ERROR
;
1096 See if all fields of a variable of EFI_TIME type is correct.
1098 @param Time The time to be checked.
1100 @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.
1101 @retval EFI_SUCCESS Time is a valid EFI_TIME variable.
1105 RtcTimeFieldsValid (
1109 if ((Time
->Year
< PcdGet16 (PcdMinimalValidYear
)) ||
1110 (Time
->Year
> PcdGet16 (PcdMaximalValidYear
)) ||
1111 (Time
->Month
< 1) ||
1112 (Time
->Month
> 12) ||
1113 (!DayValid (Time
)) ||
1114 (Time
->Hour
> 23) ||
1115 (Time
->Minute
> 59) ||
1116 (Time
->Second
> 59) ||
1117 (Time
->Nanosecond
> 999999999) ||
1118 (!((Time
->TimeZone
== EFI_UNSPECIFIED_TIMEZONE
) || ((Time
->TimeZone
>= -1440) && (Time
->TimeZone
<= 1440)))) ||
1119 ((Time
->Daylight
& (~(EFI_TIME_ADJUST_DAYLIGHT
| EFI_TIME_IN_DAYLIGHT
))) != 0))
1121 return EFI_INVALID_PARAMETER
;
1128 See if field Day of an EFI_TIME is correct.
1130 @param Time Its Day field is to be checked.
1132 @retval TRUE Day field of Time is correct.
1133 @retval FALSE Day field of Time is NOT correct.
1141 // The validity of Time->Month field should be checked before
1143 ASSERT (Time
->Month
>= 1);
1144 ASSERT (Time
->Month
<= 12);
1145 if ((Time
->Day
< 1) ||
1146 (Time
->Day
> mDayOfMonth
[Time
->Month
- 1]) ||
1147 ((Time
->Month
== 2) && (!IsLeapYear (Time
) && (Time
->Day
> 28)))
1157 Check if it is a leap year.
1159 @param Time The time to be checked.
1161 @retval TRUE It is a leap year.
1162 @retval FALSE It is NOT a leap year.
1169 if (Time
->Year
% 4 == 0) {
1170 if (Time
->Year
% 100 == 0) {
1171 if (Time
->Year
% 400 == 0) {
1185 Converts time from EFI_TIME format defined by UEFI spec to RTC format.
1187 This function converts time from EFI_TIME format defined by UEFI spec to RTC format.
1188 If data mode of RTC is BCD, then converts EFI_TIME to it.
1189 If RTC is in 12-hour format, then converts EFI_TIME to it.
1191 @param Time On input, the time data read from UEFI to convert
1192 On output, the time converted to RTC format
1193 @param RegisterB Value of Register B of RTC, indicating data mode
1196 ConvertEfiTimeToRtcTime (
1197 IN OUT EFI_TIME
*Time
,
1198 IN RTC_REGISTER_B RegisterB
1205 // Adjust hour field if RTC is in 12 hour mode
1207 if (RegisterB
.Bits
.Mil
== 0) {
1208 if (Time
->Hour
< 12) {
1212 if (Time
->Hour
>= 13) {
1213 Time
->Hour
= (UINT8
)(Time
->Hour
- 12);
1214 } else if (Time
->Hour
== 0) {
1220 // Set the Time/Date values.
1222 Time
->Year
= (UINT16
)(Time
->Year
% 100);
1224 if (RegisterB
.Bits
.Dm
== 0) {
1225 Time
->Year
= DecimalToBcd8 ((UINT8
)Time
->Year
);
1226 Time
->Month
= DecimalToBcd8 (Time
->Month
);
1227 Time
->Day
= DecimalToBcd8 (Time
->Day
);
1228 Time
->Hour
= DecimalToBcd8 (Time
->Hour
);
1229 Time
->Minute
= DecimalToBcd8 (Time
->Minute
);
1230 Time
->Second
= DecimalToBcd8 (Time
->Second
);
1234 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
1236 if ((RegisterB
.Bits
.Mil
== 0) && IsPM
) {
1237 Time
->Hour
= (UINT8
)(Time
->Hour
| 0x80);
1242 Compare the Hour, Minute and Second of the From time and the To time.
1244 Only compare H/M/S in EFI_TIME and ignore other fields here.
1246 @param From the first time
1247 @param To the second time
1249 @return >0 The H/M/S of the From time is later than those of To time
1250 @return ==0 The H/M/S of the From time is same as those of To time
1251 @return <0 The H/M/S of the From time is earlier than those of To time
1259 if ((From
->Hour
> To
->Hour
) ||
1260 ((From
->Hour
== To
->Hour
) && (From
->Minute
> To
->Minute
)) ||
1261 ((From
->Hour
== To
->Hour
) && (From
->Minute
== To
->Minute
) && (From
->Second
> To
->Second
)))
1264 } else if ((From
->Hour
== To
->Hour
) && (From
->Minute
== To
->Minute
) && (From
->Second
== To
->Second
)) {
1272 To check if second date is later than first date within 24 hours.
1274 @param From the first date
1275 @param To the second date
1277 @retval TRUE From is previous to To within 24 hours.
1278 @retval FALSE From is later, or it is previous to To more than 24 hours.
1291 // The validity of From->Month field should be checked before
1293 ASSERT (From
->Month
>= 1);
1294 ASSERT (From
->Month
<= 12);
1296 if (From
->Year
== To
->Year
) {
1297 if (From
->Month
== To
->Month
) {
1298 if ((From
->Day
+ 1) == To
->Day
) {
1299 if ((CompareHMS (From
, To
) >= 0)) {
1302 } else if (From
->Day
== To
->Day
) {
1303 if ((CompareHMS (From
, To
) <= 0)) {
1307 } else if (((From
->Month
+ 1) == To
->Month
) && (To
->Day
== 1)) {
1308 if ((From
->Month
== 2) && !IsLeapYear (From
)) {
1309 if (From
->Day
== 28) {
1310 if ((CompareHMS (From
, To
) >= 0)) {
1314 } else if (From
->Day
== mDayOfMonth
[From
->Month
- 1]) {
1315 if ((CompareHMS (From
, To
) >= 0)) {
1320 } else if (((From
->Year
+ 1) == To
->Year
) &&
1321 (From
->Month
== 12) &&
1322 (From
->Day
== 31) &&
1326 if ((CompareHMS (From
, To
) >= 0)) {
1335 Get the century RTC address from the ACPI FADT table.
1337 @return The century RTC address or 0 if not found.
1340 GetCenturyRtcAddress (
1344 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
1346 Fadt
= (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*)EfiLocateFirstAcpiTable (
1347 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
1350 if ((Fadt
!= NULL
) &&
1351 (Fadt
->Century
> RTC_ADDRESS_REGISTER_D
) && (Fadt
->Century
< 0x80)
1354 return Fadt
->Century
;
1361 Notification function of ACPI Table change.
1363 This is a notification function registered on ACPI Table change event.
1364 It saves the Century address stored in ACPI FADT table.
1366 @param Event Event whose notification function is being invoked.
1367 @param Context Pointer to the notification function's context.
1372 PcRtcAcpiTableChangeCallback (
1379 UINT8 CenturyRtcAddress
;
1382 CenturyRtcAddress
= GetCenturyRtcAddress ();
1383 if ((CenturyRtcAddress
!= 0) && (mModuleGlobal
.CenturyRtcAddress
!= CenturyRtcAddress
)) {
1384 mModuleGlobal
.CenturyRtcAddress
= CenturyRtcAddress
;
1385 Status
= PcRtcGetTime (&Time
, NULL
, &mModuleGlobal
);
1386 if (!EFI_ERROR (Status
)) {
1387 Century
= (UINT8
)(Time
.Year
/ 100);
1388 Century
= DecimalToBcd8 (Century
);
1389 DEBUG ((DEBUG_INFO
, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century
, mModuleGlobal
.CenturyRtcAddress
));
1390 RtcWrite (mModuleGlobal
.CenturyRtcAddress
, Century
);