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>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
16 UINTN mDayOfMonth
[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
19 // The name of NV variable to store the timezone and daylight saving information.
21 CHAR16 mTimeZoneVariableName
[] = L
"RTC";
24 Compare the Hour, Minute and Second of the From time and the To time.
26 Only compare H/M/S in EFI_TIME and ignore other fields here.
28 @param From the first time
29 @param To the second time
31 @return >0 The H/M/S of the From time is later than those of To time
32 @return ==0 The H/M/S of the From time is same as those of To time
33 @return <0 The H/M/S of the From time is earlier than those of To time
42 To check if second date is later than first date within 24 hours.
44 @param From the first date
45 @param To the second date
47 @retval TRUE From is previous to To within 24 hours.
48 @retval FALSE From is later, or it is previous to To more than 24 hours.
57 Read RTC content through its registers.
59 @param Address Address offset of RTC. It is recommended to use macros such as
62 @return The data of UINT8 type read from RTC.
69 IoWrite8 (PcdGet8 (PcdRtcIndexRegister
), (UINT8
) (Address
| (UINT8
) (IoRead8 (PcdGet8 (PcdRtcIndexRegister
)) & 0x80)));
70 return IoRead8 (PcdGet8 (PcdRtcTargetRegister
));
74 Write RTC through its registers.
76 @param Address Address offset of RTC. It is recommended to use macros such as
78 @param Data The content you want to write into RTC.
87 IoWrite8 (PcdGet8 (PcdRtcIndexRegister
), (UINT8
) (Address
| (UINT8
) (IoRead8 (PcdGet8 (PcdRtcIndexRegister
)) & 0x80)));
88 IoWrite8 (PcdGet8 (PcdRtcTargetRegister
), Data
);
94 @param Global For global use inside this module.
96 @retval EFI_DEVICE_ERROR Initialization failed due to device error.
97 @retval EFI_SUCCESS Initialization successful.
102 IN PC_RTC_MODULE_GLOBALS
*Global
106 RTC_REGISTER_A RegisterA
;
107 RTC_REGISTER_B RegisterB
;
108 RTC_REGISTER_D RegisterD
;
116 // Acquire RTC Lock to make access to RTC atomic
118 if (!EfiAtRuntime ()) {
119 EfiAcquireLock (&Global
->RtcLock
);
122 // Initialize RTC Register
124 // Make sure Division Chain is properly configured,
125 // or RTC clock won't "tick" -- time won't increment
127 RegisterA
.Data
= FixedPcdGet8 (PcdInitialValueRtcRegisterA
);
128 RtcWrite (RTC_ADDRESS_REGISTER_A
, RegisterA
.Data
);
133 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
136 // Clear RTC flag register
138 RtcRead (RTC_ADDRESS_REGISTER_C
);
141 // Clear RTC register D
143 RegisterD
.Data
= FixedPcdGet8 (PcdInitialValueRtcRegisterD
);
144 RtcWrite (RTC_ADDRESS_REGISTER_D
, RegisterD
.Data
);
147 // Wait for up to 0.1 seconds for the RTC to be updated
149 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
150 if (EFI_ERROR (Status
)) {
152 // Set the variable with default value if the RTC is functioning incorrectly.
154 Global
->SavedTimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
155 Global
->Daylight
= 0;
156 if (!EfiAtRuntime ()) {
157 EfiReleaseLock (&Global
->RtcLock
);
159 return EFI_DEVICE_ERROR
;
162 // Get the Time/Date/Daylight Savings values.
164 Time
.Second
= RtcRead (RTC_ADDRESS_SECONDS
);
165 Time
.Minute
= RtcRead (RTC_ADDRESS_MINUTES
);
166 Time
.Hour
= RtcRead (RTC_ADDRESS_HOURS
);
167 Time
.Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
168 Time
.Month
= RtcRead (RTC_ADDRESS_MONTH
);
169 Time
.Year
= RtcRead (RTC_ADDRESS_YEAR
);
172 // Set RTC configuration after get original time
173 // The value of bit AIE should be reserved.
175 RegisterB
.Data
= FixedPcdGet8 (PcdInitialValueRtcRegisterB
) | (RegisterB
.Data
& BIT5
);
176 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
181 if (!EfiAtRuntime ()) {
182 EfiReleaseLock (&Global
->RtcLock
);
186 // Get the data of Daylight saving and time zone, if they have been
187 // stored in NV variable during previous boot.
189 DataSize
= sizeof (UINT32
);
190 Status
= EfiGetVariable (
191 mTimeZoneVariableName
,
197 if (!EFI_ERROR (Status
)) {
198 Time
.TimeZone
= (INT16
) TimerVar
;
199 Time
.Daylight
= (UINT8
) (TimerVar
>> 16);
201 Time
.TimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
206 // Validate time fields
208 Status
= ConvertRtcTimeToEfiTime (&Time
, RegisterB
);
209 if (!EFI_ERROR (Status
)) {
210 Status
= RtcTimeFieldsValid (&Time
);
212 if (EFI_ERROR (Status
)) {
214 // Report Status Code to indicate that the RTC has bad date and time
217 EFI_ERROR_CODE
| EFI_ERROR_MINOR
,
218 (EFI_SOFTWARE_DXE_RT_DRIVER
| EFI_SW_EC_BAD_DATE_TIME
)
220 Time
.Second
= RTC_INIT_SECOND
;
221 Time
.Minute
= RTC_INIT_MINUTE
;
222 Time
.Hour
= RTC_INIT_HOUR
;
223 Time
.Day
= RTC_INIT_DAY
;
224 Time
.Month
= RTC_INIT_MONTH
;
225 Time
.Year
= PcdGet16 (PcdMinimalValidYear
);
227 Time
.TimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
232 // Reset time value according to new RTC configuration
234 Status
= PcRtcSetTime (&Time
, Global
);
235 if (EFI_ERROR (Status
)) {
236 return EFI_DEVICE_ERROR
;
240 // Reset wakeup time value to valid state when wakeup alarm is disabled and wakeup time is invalid.
241 // Global variable has already had valid SavedTimeZone and Daylight,
242 // so we can use them to get and set wakeup time.
244 Status
= PcRtcGetWakeupTime (&Enabled
, &Pending
, &Time
, Global
);
245 if ((Enabled
) || (!EFI_ERROR (Status
))) {
250 // When wakeup time is disabled and invalid, reset wakeup time register to valid state
251 // but keep wakeup alarm disabled.
253 Time
.Second
= RTC_INIT_SECOND
;
254 Time
.Minute
= RTC_INIT_MINUTE
;
255 Time
.Hour
= RTC_INIT_HOUR
;
256 Time
.Day
= RTC_INIT_DAY
;
257 Time
.Month
= RTC_INIT_MONTH
;
258 Time
.Year
= PcdGet16 (PcdMinimalValidYear
);
260 Time
.TimeZone
= Global
->SavedTimeZone
;
261 Time
.Daylight
= Global
->Daylight
;;
264 // Acquire RTC Lock to make access to RTC atomic
266 if (!EfiAtRuntime ()) {
267 EfiAcquireLock (&Global
->RtcLock
);
270 // Wait for up to 0.1 seconds for the RTC to be updated
272 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
273 if (EFI_ERROR (Status
)) {
274 if (!EfiAtRuntime ()) {
275 EfiReleaseLock (&Global
->RtcLock
);
277 return EFI_DEVICE_ERROR
;
280 ConvertEfiTimeToRtcTime (&Time
, RegisterB
);
283 // Set the Y/M/D info to variable as it has no corresponding hw registers.
285 Status
= EfiSetVariable (
288 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_NON_VOLATILE
,
292 if (EFI_ERROR (Status
)) {
293 if (!EfiAtRuntime ()) {
294 EfiReleaseLock (&Global
->RtcLock
);
296 return EFI_DEVICE_ERROR
;
300 // Inhibit updates of the RTC
302 RegisterB
.Bits
.Set
= 1;
303 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
306 // Set RTC alarm time registers
308 RtcWrite (RTC_ADDRESS_SECONDS_ALARM
, Time
.Second
);
309 RtcWrite (RTC_ADDRESS_MINUTES_ALARM
, Time
.Minute
);
310 RtcWrite (RTC_ADDRESS_HOURS_ALARM
, Time
.Hour
);
313 // Allow updates of the RTC registers
315 RegisterB
.Bits
.Set
= 0;
316 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
321 if (!EfiAtRuntime ()) {
322 EfiReleaseLock (&Global
->RtcLock
);
328 Returns the current time and date information, and the time-keeping capabilities
329 of the hardware platform.
331 @param Time A pointer to storage to receive a snapshot of the current time.
332 @param Capabilities An optional pointer to a buffer to receive the real time clock
333 device's capabilities.
334 @param Global For global use inside this module.
336 @retval EFI_SUCCESS The operation completed successfully.
337 @retval EFI_INVALID_PARAMETER Time is NULL.
338 @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.
344 OUT EFI_TIME_CAPABILITIES
*Capabilities
, OPTIONAL
345 IN PC_RTC_MODULE_GLOBALS
*Global
349 RTC_REGISTER_B RegisterB
;
352 // Check parameters for null pointer
355 return EFI_INVALID_PARAMETER
;
359 // Acquire RTC Lock to make access to RTC atomic
361 if (!EfiAtRuntime ()) {
362 EfiAcquireLock (&Global
->RtcLock
);
365 // Wait for up to 0.1 seconds for the RTC to be updated
367 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
368 if (EFI_ERROR (Status
)) {
369 if (!EfiAtRuntime ()) {
370 EfiReleaseLock (&Global
->RtcLock
);
377 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
380 // Get the Time/Date/Daylight Savings values.
382 Time
->Second
= RtcRead (RTC_ADDRESS_SECONDS
);
383 Time
->Minute
= RtcRead (RTC_ADDRESS_MINUTES
);
384 Time
->Hour
= RtcRead (RTC_ADDRESS_HOURS
);
385 Time
->Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
386 Time
->Month
= RtcRead (RTC_ADDRESS_MONTH
);
387 Time
->Year
= RtcRead (RTC_ADDRESS_YEAR
);
392 if (!EfiAtRuntime ()) {
393 EfiReleaseLock (&Global
->RtcLock
);
397 // Get the variable that contains the TimeZone and Daylight fields
399 Time
->TimeZone
= Global
->SavedTimeZone
;
400 Time
->Daylight
= Global
->Daylight
;
403 // Make sure all field values are in correct range
405 Status
= ConvertRtcTimeToEfiTime (Time
, RegisterB
);
406 if (!EFI_ERROR (Status
)) {
407 Status
= RtcTimeFieldsValid (Time
);
409 if (EFI_ERROR (Status
)) {
410 return EFI_DEVICE_ERROR
;
414 // Fill in Capabilities if it was passed in
416 if (Capabilities
!= NULL
) {
417 Capabilities
->Resolution
= 1;
421 Capabilities
->Accuracy
= 50000000;
425 Capabilities
->SetsToZero
= FALSE
;
432 Sets the current local time and date information.
434 @param Time A pointer to the current time.
435 @param Global For global use inside this module.
437 @retval EFI_SUCCESS The operation completed successfully.
438 @retval EFI_INVALID_PARAMETER A time field is out of range.
439 @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.
445 IN PC_RTC_MODULE_GLOBALS
*Global
450 RTC_REGISTER_B RegisterB
;
454 return EFI_INVALID_PARAMETER
;
457 // Make sure that the time fields are valid
459 Status
= RtcTimeFieldsValid (Time
);
460 if (EFI_ERROR (Status
)) {
464 CopyMem (&RtcTime
, Time
, sizeof (EFI_TIME
));
467 // Acquire RTC Lock to make access to RTC atomic
469 if (!EfiAtRuntime ()) {
470 EfiAcquireLock (&Global
->RtcLock
);
473 // Wait for up to 0.1 seconds for the RTC to be updated
475 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
476 if (EFI_ERROR (Status
)) {
477 if (!EfiAtRuntime ()) {
478 EfiReleaseLock (&Global
->RtcLock
);
484 // Write timezone and daylight to RTC variable
486 if ((Time
->TimeZone
== EFI_UNSPECIFIED_TIMEZONE
) && (Time
->Daylight
== 0)) {
487 Status
= EfiSetVariable (
488 mTimeZoneVariableName
,
494 if (Status
== EFI_NOT_FOUND
) {
495 Status
= EFI_SUCCESS
;
498 TimerVar
= Time
->Daylight
;
499 TimerVar
= (UINT32
) ((TimerVar
<< 16) | (UINT16
)(Time
->TimeZone
));
500 Status
= EfiSetVariable (
501 mTimeZoneVariableName
,
503 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_NON_VOLATILE
,
509 if (EFI_ERROR (Status
)) {
510 if (!EfiAtRuntime ()) {
511 EfiReleaseLock (&Global
->RtcLock
);
513 return EFI_DEVICE_ERROR
;
517 // Read Register B, and inhibit updates of the RTC
519 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
520 RegisterB
.Bits
.Set
= 1;
521 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
524 // Store the century value to RTC before converting to BCD format.
526 if (Global
->CenturyRtcAddress
!= 0) {
527 RtcWrite (Global
->CenturyRtcAddress
, DecimalToBcd8 ((UINT8
) (RtcTime
.Year
/ 100)));
530 ConvertEfiTimeToRtcTime (&RtcTime
, RegisterB
);
532 RtcWrite (RTC_ADDRESS_SECONDS
, RtcTime
.Second
);
533 RtcWrite (RTC_ADDRESS_MINUTES
, RtcTime
.Minute
);
534 RtcWrite (RTC_ADDRESS_HOURS
, RtcTime
.Hour
);
535 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH
, RtcTime
.Day
);
536 RtcWrite (RTC_ADDRESS_MONTH
, RtcTime
.Month
);
537 RtcWrite (RTC_ADDRESS_YEAR
, (UINT8
) RtcTime
.Year
);
540 // Allow updates of the RTC registers
542 RegisterB
.Bits
.Set
= 0;
543 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
548 if (!EfiAtRuntime ()) {
549 EfiReleaseLock (&Global
->RtcLock
);
552 // Set the variable that contains the TimeZone and Daylight fields
554 Global
->SavedTimeZone
= Time
->TimeZone
;
555 Global
->Daylight
= Time
->Daylight
;
561 Returns the current wakeup alarm clock setting.
563 @param Enabled Indicates if the alarm is currently enabled or disabled.
564 @param Pending Indicates if the alarm signal is pending and requires acknowledgment.
565 @param Time The current alarm setting.
566 @param Global For global use inside this module.
568 @retval EFI_SUCCESS The alarm settings were returned.
569 @retval EFI_INVALID_PARAMETER Enabled is NULL.
570 @retval EFI_INVALID_PARAMETER Pending is NULL.
571 @retval EFI_INVALID_PARAMETER Time is NULL.
572 @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.
573 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
578 OUT BOOLEAN
*Enabled
,
579 OUT BOOLEAN
*Pending
,
581 IN PC_RTC_MODULE_GLOBALS
*Global
585 RTC_REGISTER_B RegisterB
;
586 RTC_REGISTER_C RegisterC
;
591 // Check parameters for null pointers
593 if ((Enabled
== NULL
) || (Pending
== NULL
) || (Time
== NULL
)) {
594 return EFI_INVALID_PARAMETER
;
598 // Acquire RTC Lock to make access to RTC atomic
600 if (!EfiAtRuntime ()) {
601 EfiAcquireLock (&Global
->RtcLock
);
604 // Wait for up to 0.1 seconds for the RTC to be updated
606 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
607 if (EFI_ERROR (Status
)) {
608 if (!EfiAtRuntime ()) {
609 EfiReleaseLock (&Global
->RtcLock
);
611 return EFI_DEVICE_ERROR
;
614 // Read Register B and Register C
616 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
617 RegisterC
.Data
= RtcRead (RTC_ADDRESS_REGISTER_C
);
620 // Get the Time/Date/Daylight Savings values.
622 *Enabled
= RegisterB
.Bits
.Aie
;
623 *Pending
= RegisterC
.Bits
.Af
;
625 Time
->Second
= RtcRead (RTC_ADDRESS_SECONDS_ALARM
);
626 Time
->Minute
= RtcRead (RTC_ADDRESS_MINUTES_ALARM
);
627 Time
->Hour
= RtcRead (RTC_ADDRESS_HOURS_ALARM
);
628 Time
->Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
629 Time
->Month
= RtcRead (RTC_ADDRESS_MONTH
);
630 Time
->Year
= RtcRead (RTC_ADDRESS_YEAR
);
631 Time
->TimeZone
= Global
->SavedTimeZone
;
632 Time
->Daylight
= Global
->Daylight
;
635 // Get the alarm info from variable
637 DataSize
= sizeof (EFI_TIME
);
638 Status
= EfiGetVariable (
645 if (!EFI_ERROR (Status
)) {
647 // The alarm variable exists. In this case, we read variable to get info.
649 Time
->Day
= RtcTime
.Day
;
650 Time
->Month
= RtcTime
.Month
;
651 Time
->Year
= RtcTime
.Year
;
657 if (!EfiAtRuntime ()) {
658 EfiReleaseLock (&Global
->RtcLock
);
662 // Make sure all field values are in correct range
664 Status
= ConvertRtcTimeToEfiTime (Time
, RegisterB
);
665 if (!EFI_ERROR (Status
)) {
666 Status
= RtcTimeFieldsValid (Time
);
668 if (EFI_ERROR (Status
)) {
669 return EFI_DEVICE_ERROR
;
676 Sets the system wakeup alarm clock time.
678 @param Enabled Enable or disable the wakeup alarm.
679 @param Time If Enable is TRUE, the time to set the wakeup alarm for.
680 If Enable is FALSE, then this parameter is optional, and may be NULL.
681 @param Global For global use inside this module.
683 @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled.
684 If Enable is FALSE, then the wakeup alarm was disabled.
685 @retval EFI_INVALID_PARAMETER A time field is out of range.
686 @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.
687 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
693 IN EFI_TIME
*Time
, OPTIONAL
694 IN PC_RTC_MODULE_GLOBALS
*Global
699 RTC_REGISTER_B RegisterB
;
700 EFI_TIME_CAPABILITIES Capabilities
;
702 ZeroMem (&RtcTime
, sizeof (RtcTime
));
707 return EFI_INVALID_PARAMETER
;
710 // Make sure that the time fields are valid
712 Status
= RtcTimeFieldsValid (Time
);
713 if (EFI_ERROR (Status
)) {
714 return EFI_INVALID_PARAMETER
;
717 // Just support set alarm time within 24 hours
719 PcRtcGetTime (&RtcTime
, &Capabilities
, Global
);
720 Status
= RtcTimeFieldsValid (&RtcTime
);
721 if (EFI_ERROR (Status
)) {
722 return EFI_DEVICE_ERROR
;
724 if (!IsWithinOneDay (&RtcTime
, Time
)) {
725 return EFI_UNSUPPORTED
;
728 // Make a local copy of the time and date
730 CopyMem (&RtcTime
, Time
, sizeof (EFI_TIME
));
734 // Acquire RTC Lock to make access to RTC atomic
736 if (!EfiAtRuntime ()) {
737 EfiAcquireLock (&Global
->RtcLock
);
740 // Wait for up to 0.1 seconds for the RTC to be updated
742 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
743 if (EFI_ERROR (Status
)) {
744 if (!EfiAtRuntime ()) {
745 EfiReleaseLock (&Global
->RtcLock
);
747 return EFI_DEVICE_ERROR
;
752 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
755 ConvertEfiTimeToRtcTime (&RtcTime
, RegisterB
);
758 // if the alarm is disable, record the current setting.
760 RtcTime
.Second
= RtcRead (RTC_ADDRESS_SECONDS_ALARM
);
761 RtcTime
.Minute
= RtcRead (RTC_ADDRESS_MINUTES_ALARM
);
762 RtcTime
.Hour
= RtcRead (RTC_ADDRESS_HOURS_ALARM
);
763 RtcTime
.Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
764 RtcTime
.Month
= RtcRead (RTC_ADDRESS_MONTH
);
765 RtcTime
.Year
= RtcRead (RTC_ADDRESS_YEAR
);
766 RtcTime
.TimeZone
= Global
->SavedTimeZone
;
767 RtcTime
.Daylight
= Global
->Daylight
;
771 // Set the Y/M/D info to variable as it has no corresponding hw registers.
773 Status
= EfiSetVariable (
776 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_NON_VOLATILE
,
780 if (EFI_ERROR (Status
)) {
781 if (!EfiAtRuntime ()) {
782 EfiReleaseLock (&Global
->RtcLock
);
784 return EFI_DEVICE_ERROR
;
788 // Inhibit updates of the RTC
790 RegisterB
.Bits
.Set
= 1;
791 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
795 // Set RTC alarm time
797 RtcWrite (RTC_ADDRESS_SECONDS_ALARM
, RtcTime
.Second
);
798 RtcWrite (RTC_ADDRESS_MINUTES_ALARM
, RtcTime
.Minute
);
799 RtcWrite (RTC_ADDRESS_HOURS_ALARM
, RtcTime
.Hour
);
801 RegisterB
.Bits
.Aie
= 1;
804 RegisterB
.Bits
.Aie
= 0;
807 // Allow updates of the RTC registers
809 RegisterB
.Bits
.Set
= 0;
810 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
815 if (!EfiAtRuntime ()) {
816 EfiReleaseLock (&Global
->RtcLock
);
823 Checks an 8-bit BCD value, and converts to an 8-bit value if valid.
825 This function checks the 8-bit BCD value specified by Value.
826 If valid, the function converts it to an 8-bit value and returns it.
827 Otherwise, return 0xff.
829 @param Value The 8-bit BCD value to check and convert
831 @return The 8-bit value converted. Or 0xff if Value is invalid.
835 CheckAndConvertBcd8ToDecimal8 (
839 if ((Value
< 0xa0) && ((Value
& 0xf) < 0xa)) {
840 return BcdToDecimal8 (Value
);
847 Converts time read from RTC to EFI_TIME format defined by UEFI spec.
849 This function converts raw time data read from RTC to the EFI_TIME format
850 defined by UEFI spec.
851 If data mode of RTC is BCD, then converts it to decimal,
852 If RTC is in 12-hour format, then converts it to 24-hour format.
854 @param Time On input, the time data read from RTC to convert
855 On output, the time converted to UEFI format
856 @param RegisterB Value of Register B of RTC, indicating data mode
859 @retval EFI_INVALID_PARAMETER Parameters passed in are invalid.
860 @retval EFI_SUCCESS Convert RTC time to EFI time successfully.
864 ConvertRtcTimeToEfiTime (
865 IN OUT EFI_TIME
*Time
,
866 IN RTC_REGISTER_B RegisterB
872 if ((Time
->Hour
& 0x80) != 0) {
878 Time
->Hour
= (UINT8
) (Time
->Hour
& 0x7f);
880 if (RegisterB
.Bits
.Dm
== 0) {
881 Time
->Year
= CheckAndConvertBcd8ToDecimal8 ((UINT8
) Time
->Year
);
882 Time
->Month
= CheckAndConvertBcd8ToDecimal8 (Time
->Month
);
883 Time
->Day
= CheckAndConvertBcd8ToDecimal8 (Time
->Day
);
884 Time
->Hour
= CheckAndConvertBcd8ToDecimal8 (Time
->Hour
);
885 Time
->Minute
= CheckAndConvertBcd8ToDecimal8 (Time
->Minute
);
886 Time
->Second
= CheckAndConvertBcd8ToDecimal8 (Time
->Second
);
889 if (Time
->Year
== 0xff || Time
->Month
== 0xff || Time
->Day
== 0xff ||
890 Time
->Hour
== 0xff || Time
->Minute
== 0xff || Time
->Second
== 0xff) {
891 return EFI_INVALID_PARAMETER
;
895 // For minimal/maximum year range [1970, 2069],
896 // Century is 19 if RTC year >= 70,
897 // Century is 20 otherwise.
899 Century
= (UINT8
) (PcdGet16 (PcdMinimalValidYear
) / 100);
900 if (Time
->Year
< PcdGet16 (PcdMinimalValidYear
) % 100) {
903 Time
->Year
= (UINT16
) (Century
* 100 + Time
->Year
);
906 // If time is in 12 hour format, convert it to 24 hour format
908 if (RegisterB
.Bits
.Mil
== 0) {
909 if (IsPM
&& Time
->Hour
< 12) {
910 Time
->Hour
= (UINT8
) (Time
->Hour
+ 12);
913 if (!IsPM
&& Time
->Hour
== 12) {
918 Time
->Nanosecond
= 0;
924 Wait for a period for the RTC to be ready.
926 @param Timeout Tell how long it should take to wait.
928 @retval EFI_DEVICE_ERROR RTC device error.
929 @retval EFI_SUCCESS RTC is updated and ready.
936 RTC_REGISTER_A RegisterA
;
937 RTC_REGISTER_D RegisterD
;
940 // See if the RTC is functioning correctly
942 RegisterD
.Data
= RtcRead (RTC_ADDRESS_REGISTER_D
);
944 if (RegisterD
.Bits
.Vrt
== 0) {
945 return EFI_DEVICE_ERROR
;
948 // Wait for up to 0.1 seconds for the RTC to be ready.
950 Timeout
= (Timeout
/ 10) + 1;
951 RegisterA
.Data
= RtcRead (RTC_ADDRESS_REGISTER_A
);
952 while (RegisterA
.Bits
.Uip
== 1 && Timeout
> 0) {
953 MicroSecondDelay (10);
954 RegisterA
.Data
= RtcRead (RTC_ADDRESS_REGISTER_A
);
958 RegisterD
.Data
= RtcRead (RTC_ADDRESS_REGISTER_D
);
959 if (Timeout
== 0 || RegisterD
.Bits
.Vrt
== 0) {
960 return EFI_DEVICE_ERROR
;
967 See if all fields of a variable of EFI_TIME type is correct.
969 @param Time The time to be checked.
971 @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.
972 @retval EFI_SUCCESS Time is a valid EFI_TIME variable.
980 if (Time
->Year
< PcdGet16 (PcdMinimalValidYear
) ||
981 Time
->Year
> PcdGet16 (PcdMaximalValidYear
) ||
984 (!DayValid (Time
)) ||
988 Time
->Nanosecond
> 999999999 ||
989 (!(Time
->TimeZone
== EFI_UNSPECIFIED_TIMEZONE
|| (Time
->TimeZone
>= -1440 && Time
->TimeZone
<= 1440))) ||
990 ((Time
->Daylight
& (~(EFI_TIME_ADJUST_DAYLIGHT
| EFI_TIME_IN_DAYLIGHT
))) != 0)) {
991 return EFI_INVALID_PARAMETER
;
998 See if field Day of an EFI_TIME is correct.
1000 @param Time Its Day field is to be checked.
1002 @retval TRUE Day field of Time is correct.
1003 @retval FALSE Day field of Time is NOT correct.
1011 // The validity of Time->Month field should be checked before
1013 ASSERT (Time
->Month
>=1);
1014 ASSERT (Time
->Month
<=12);
1015 if (Time
->Day
< 1 ||
1016 Time
->Day
> mDayOfMonth
[Time
->Month
- 1] ||
1017 (Time
->Month
== 2 && (!IsLeapYear (Time
) && Time
->Day
> 28))
1026 Check if it is a leap year.
1028 @param Time The time to be checked.
1030 @retval TRUE It is a leap year.
1031 @retval FALSE It is NOT a leap year.
1038 if (Time
->Year
% 4 == 0) {
1039 if (Time
->Year
% 100 == 0) {
1040 if (Time
->Year
% 400 == 0) {
1054 Converts time from EFI_TIME format defined by UEFI spec to RTC format.
1056 This function converts time from EFI_TIME format defined by UEFI spec to RTC format.
1057 If data mode of RTC is BCD, then converts EFI_TIME to it.
1058 If RTC is in 12-hour format, then converts EFI_TIME to it.
1060 @param Time On input, the time data read from UEFI to convert
1061 On output, the time converted to RTC format
1062 @param RegisterB Value of Register B of RTC, indicating data mode
1065 ConvertEfiTimeToRtcTime (
1066 IN OUT EFI_TIME
*Time
,
1067 IN RTC_REGISTER_B RegisterB
1074 // Adjust hour field if RTC is in 12 hour mode
1076 if (RegisterB
.Bits
.Mil
== 0) {
1077 if (Time
->Hour
< 12) {
1081 if (Time
->Hour
>= 13) {
1082 Time
->Hour
= (UINT8
) (Time
->Hour
- 12);
1083 } else if (Time
->Hour
== 0) {
1088 // Set the Time/Date values.
1090 Time
->Year
= (UINT16
) (Time
->Year
% 100);
1092 if (RegisterB
.Bits
.Dm
== 0) {
1093 Time
->Year
= DecimalToBcd8 ((UINT8
) Time
->Year
);
1094 Time
->Month
= DecimalToBcd8 (Time
->Month
);
1095 Time
->Day
= DecimalToBcd8 (Time
->Day
);
1096 Time
->Hour
= DecimalToBcd8 (Time
->Hour
);
1097 Time
->Minute
= DecimalToBcd8 (Time
->Minute
);
1098 Time
->Second
= DecimalToBcd8 (Time
->Second
);
1101 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
1103 if (RegisterB
.Bits
.Mil
== 0 && IsPM
) {
1104 Time
->Hour
= (UINT8
) (Time
->Hour
| 0x80);
1109 Compare the Hour, Minute and Second of the From time and the To time.
1111 Only compare H/M/S in EFI_TIME and ignore other fields here.
1113 @param From the first time
1114 @param To the second time
1116 @return >0 The H/M/S of the From time is later than those of To time
1117 @return ==0 The H/M/S of the From time is same as those of To time
1118 @return <0 The H/M/S of the From time is earlier than those of To time
1126 if ((From
->Hour
> To
->Hour
) ||
1127 ((From
->Hour
== To
->Hour
) && (From
->Minute
> To
->Minute
)) ||
1128 ((From
->Hour
== To
->Hour
) && (From
->Minute
== To
->Minute
) && (From
->Second
> To
->Second
))) {
1130 } else if ((From
->Hour
== To
->Hour
) && (From
->Minute
== To
->Minute
) && (From
->Second
== To
->Second
)) {
1138 To check if second date is later than first date within 24 hours.
1140 @param From the first date
1141 @param To the second date
1143 @retval TRUE From is previous to To within 24 hours.
1144 @retval FALSE From is later, or it is previous to To more than 24 hours.
1157 // The validity of From->Month field should be checked before
1159 ASSERT (From
->Month
>=1);
1160 ASSERT (From
->Month
<=12);
1162 if (From
->Year
== To
->Year
) {
1163 if (From
->Month
== To
->Month
) {
1164 if ((From
->Day
+ 1) == To
->Day
) {
1165 if ((CompareHMS(From
, To
) >= 0)) {
1168 } else if (From
->Day
== To
->Day
) {
1169 if ((CompareHMS(From
, To
) <= 0)) {
1173 } else if (((From
->Month
+ 1) == To
->Month
) && (To
->Day
== 1)) {
1174 if ((From
->Month
== 2) && !IsLeapYear(From
)) {
1175 if (From
->Day
== 28) {
1176 if ((CompareHMS(From
, To
) >= 0)) {
1180 } else if (From
->Day
== mDayOfMonth
[From
->Month
- 1]) {
1181 if ((CompareHMS(From
, To
) >= 0)) {
1186 } else if (((From
->Year
+ 1) == To
->Year
) &&
1187 (From
->Month
== 12) &&
1188 (From
->Day
== 31) &&
1191 if ((CompareHMS(From
, To
) >= 0)) {
1200 Get the century RTC address from the ACPI FADT table.
1202 @return The century RTC address or 0 if not found.
1205 GetCenturyRtcAddress (
1209 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
1211 Fadt
= (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*) EfiLocateFirstAcpiTable (
1212 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
1215 if ((Fadt
!= NULL
) &&
1216 (Fadt
->Century
> RTC_ADDRESS_REGISTER_D
) && (Fadt
->Century
< 0x80)
1218 return Fadt
->Century
;
1225 Notification function of ACPI Table change.
1227 This is a notification function registered on ACPI Table change event.
1228 It saves the Century address stored in ACPI FADT table.
1230 @param Event Event whose notification function is being invoked.
1231 @param Context Pointer to the notification function's context.
1236 PcRtcAcpiTableChangeCallback (
1243 UINT8 CenturyRtcAddress
;
1246 CenturyRtcAddress
= GetCenturyRtcAddress ();
1247 if ((CenturyRtcAddress
!= 0) && (mModuleGlobal
.CenturyRtcAddress
!= CenturyRtcAddress
)) {
1248 mModuleGlobal
.CenturyRtcAddress
= CenturyRtcAddress
;
1249 Status
= PcRtcGetTime (&Time
, NULL
, &mModuleGlobal
);
1250 if (!EFI_ERROR (Status
)) {
1251 Century
= (UINT8
) (Time
.Year
/ 100);
1252 Century
= DecimalToBcd8 (Century
);
1253 DEBUG ((EFI_D_INFO
, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century
, mModuleGlobal
.CenturyRtcAddress
));
1254 RtcWrite (mModuleGlobal
.CenturyRtcAddress
, Century
);