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 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
22 UINTN mDayOfMonth
[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
25 // The name of NV variable to store the timezone and daylight saving information.
27 CHAR16 mTimeZoneVariableName
[] = L
"RTC";
30 Compare the Hour, Minute and Second of the From time and the To time.
32 Only compare H/M/S in EFI_TIME and ignore other fields here.
34 @param From the first time
35 @param To the second time
37 @return >0 The H/M/S of the From time is later than those of To time
38 @return ==0 The H/M/S of the From time is same as those of To time
39 @return <0 The H/M/S of the From time is earlier than those of To time
48 To check if second date is later than first date within 24 hours.
50 @param From the first date
51 @param To the second date
53 @retval TRUE From is previous to To within 24 hours.
54 @retval FALSE From is later, or it is previous to To more than 24 hours.
63 Read RTC content through its registers.
65 @param Address Address offset of RTC. It is recommended to use macros such as
68 @return The data of UINT8 type read from RTC.
75 IoWrite8 (PcdGet8 (PcdRtcIndexRegister
), (UINT8
) (Address
| (UINT8
) (IoRead8 (PcdGet8 (PcdRtcIndexRegister
)) & 0x80)));
76 return IoRead8 (PcdGet8 (PcdRtcTargetRegister
));
80 Write RTC through its registers.
82 @param Address Address offset of RTC. It is recommended to use macros such as
84 @param Data The content you want to write into RTC.
93 IoWrite8 (PcdGet8 (PcdRtcIndexRegister
), (UINT8
) (Address
| (UINT8
) (IoRead8 (PcdGet8 (PcdRtcIndexRegister
)) & 0x80)));
94 IoWrite8 (PcdGet8 (PcdRtcTargetRegister
), Data
);
100 @param Global For global use inside this module.
102 @retval EFI_DEVICE_ERROR Initialization failed due to device error.
103 @retval EFI_SUCCESS Initialization successful.
108 IN PC_RTC_MODULE_GLOBALS
*Global
112 RTC_REGISTER_A RegisterA
;
113 RTC_REGISTER_B RegisterB
;
114 RTC_REGISTER_D RegisterD
;
122 // Acquire RTC Lock to make access to RTC atomic
124 if (!EfiAtRuntime ()) {
125 EfiAcquireLock (&Global
->RtcLock
);
128 // Initialize RTC Register
130 // Make sure Division Chain is properly configured,
131 // or RTC clock won't "tick" -- time won't increment
133 RegisterA
.Data
= FixedPcdGet8 (PcdInitialValueRtcRegisterA
);
134 RtcWrite (RTC_ADDRESS_REGISTER_A
, RegisterA
.Data
);
139 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
142 // Clear RTC flag register
144 RtcRead (RTC_ADDRESS_REGISTER_C
);
147 // Clear RTC register D
149 RegisterD
.Data
= FixedPcdGet8 (PcdInitialValueRtcRegisterD
);
150 RtcWrite (RTC_ADDRESS_REGISTER_D
, RegisterD
.Data
);
153 // Wait for up to 0.1 seconds for the RTC to be updated
155 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
156 if (EFI_ERROR (Status
)) {
158 // Set the variable with default value if the RTC is functioning incorrectly.
160 Global
->SavedTimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
161 Global
->Daylight
= 0;
162 if (!EfiAtRuntime ()) {
163 EfiReleaseLock (&Global
->RtcLock
);
165 return EFI_DEVICE_ERROR
;
168 // Get the Time/Date/Daylight Savings values.
170 Time
.Second
= RtcRead (RTC_ADDRESS_SECONDS
);
171 Time
.Minute
= RtcRead (RTC_ADDRESS_MINUTES
);
172 Time
.Hour
= RtcRead (RTC_ADDRESS_HOURS
);
173 Time
.Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
174 Time
.Month
= RtcRead (RTC_ADDRESS_MONTH
);
175 Time
.Year
= RtcRead (RTC_ADDRESS_YEAR
);
178 // Set RTC configuration after get original time
179 // The value of bit AIE should be reserved.
181 RegisterB
.Data
= FixedPcdGet8 (PcdInitialValueRtcRegisterB
) | (RegisterB
.Data
& BIT5
);
182 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
187 if (!EfiAtRuntime ()) {
188 EfiReleaseLock (&Global
->RtcLock
);
192 // Get the data of Daylight saving and time zone, if they have been
193 // stored in NV variable during previous boot.
195 DataSize
= sizeof (UINT32
);
196 Status
= EfiGetVariable (
197 mTimeZoneVariableName
,
203 if (!EFI_ERROR (Status
)) {
204 Time
.TimeZone
= (INT16
) TimerVar
;
205 Time
.Daylight
= (UINT8
) (TimerVar
>> 16);
207 Time
.TimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
212 // Validate time fields
214 Status
= ConvertRtcTimeToEfiTime (&Time
, RegisterB
);
215 if (!EFI_ERROR (Status
)) {
216 Status
= RtcTimeFieldsValid (&Time
);
218 if (EFI_ERROR (Status
)) {
220 // Report Status Code to indicate that the RTC has bad date and time
223 EFI_ERROR_CODE
| EFI_ERROR_MINOR
,
224 (EFI_SOFTWARE_DXE_RT_DRIVER
| EFI_SW_EC_BAD_DATE_TIME
)
226 Time
.Second
= RTC_INIT_SECOND
;
227 Time
.Minute
= RTC_INIT_MINUTE
;
228 Time
.Hour
= RTC_INIT_HOUR
;
229 Time
.Day
= RTC_INIT_DAY
;
230 Time
.Month
= RTC_INIT_MONTH
;
231 Time
.Year
= PcdGet16 (PcdMinimalValidYear
);
233 Time
.TimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
238 // Reset time value according to new RTC configuration
240 Status
= PcRtcSetTime (&Time
, Global
);
241 if (EFI_ERROR (Status
)) {
242 return EFI_DEVICE_ERROR
;
246 // Reset wakeup time value to valid state when wakeup alarm is disabled and wakeup time is invalid.
247 // Global variable has already had valid SavedTimeZone and Daylight,
248 // so we can use them to get and set wakeup time.
250 Status
= PcRtcGetWakeupTime (&Enabled
, &Pending
, &Time
, Global
);
251 if ((Enabled
) || (!EFI_ERROR (Status
))) {
256 // When wakeup time is disabled and invalid, reset wakeup time register to valid state
257 // but keep wakeup alarm disabled.
259 Time
.Second
= RTC_INIT_SECOND
;
260 Time
.Minute
= RTC_INIT_MINUTE
;
261 Time
.Hour
= RTC_INIT_HOUR
;
262 Time
.Day
= RTC_INIT_DAY
;
263 Time
.Month
= RTC_INIT_MONTH
;
264 Time
.Year
= PcdGet16 (PcdMinimalValidYear
);
266 Time
.TimeZone
= Global
->SavedTimeZone
;
267 Time
.Daylight
= Global
->Daylight
;;
270 // Acquire RTC Lock to make access to RTC atomic
272 if (!EfiAtRuntime ()) {
273 EfiAcquireLock (&Global
->RtcLock
);
276 // Wait for up to 0.1 seconds for the RTC to be updated
278 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
279 if (EFI_ERROR (Status
)) {
280 if (!EfiAtRuntime ()) {
281 EfiReleaseLock (&Global
->RtcLock
);
283 return EFI_DEVICE_ERROR
;
286 ConvertEfiTimeToRtcTime (&Time
, RegisterB
);
289 // Set the Y/M/D info to variable as it has no corresponding hw registers.
291 Status
= EfiSetVariable (
294 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_NON_VOLATILE
,
298 if (EFI_ERROR (Status
)) {
299 if (!EfiAtRuntime ()) {
300 EfiReleaseLock (&Global
->RtcLock
);
302 return EFI_DEVICE_ERROR
;
306 // Inhibit updates of the RTC
308 RegisterB
.Bits
.Set
= 1;
309 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
312 // Set RTC alarm time registers
314 RtcWrite (RTC_ADDRESS_SECONDS_ALARM
, Time
.Second
);
315 RtcWrite (RTC_ADDRESS_MINUTES_ALARM
, Time
.Minute
);
316 RtcWrite (RTC_ADDRESS_HOURS_ALARM
, Time
.Hour
);
319 // Allow updates of the RTC registers
321 RegisterB
.Bits
.Set
= 0;
322 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
327 if (!EfiAtRuntime ()) {
328 EfiReleaseLock (&Global
->RtcLock
);
334 Returns the current time and date information, and the time-keeping capabilities
335 of the hardware platform.
337 @param Time A pointer to storage to receive a snapshot of the current time.
338 @param Capabilities An optional pointer to a buffer to receive the real time clock
339 device's capabilities.
340 @param Global For global use inside this module.
342 @retval EFI_SUCCESS The operation completed successfully.
343 @retval EFI_INVALID_PARAMETER Time is NULL.
344 @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.
350 OUT EFI_TIME_CAPABILITIES
*Capabilities
, OPTIONAL
351 IN PC_RTC_MODULE_GLOBALS
*Global
355 RTC_REGISTER_B RegisterB
;
358 // Check parameters for null pointer
361 return EFI_INVALID_PARAMETER
;
365 // Acquire RTC Lock to make access to RTC atomic
367 if (!EfiAtRuntime ()) {
368 EfiAcquireLock (&Global
->RtcLock
);
371 // Wait for up to 0.1 seconds for the RTC to be updated
373 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
374 if (EFI_ERROR (Status
)) {
375 if (!EfiAtRuntime ()) {
376 EfiReleaseLock (&Global
->RtcLock
);
383 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
386 // Get the Time/Date/Daylight Savings values.
388 Time
->Second
= RtcRead (RTC_ADDRESS_SECONDS
);
389 Time
->Minute
= RtcRead (RTC_ADDRESS_MINUTES
);
390 Time
->Hour
= RtcRead (RTC_ADDRESS_HOURS
);
391 Time
->Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
392 Time
->Month
= RtcRead (RTC_ADDRESS_MONTH
);
393 Time
->Year
= RtcRead (RTC_ADDRESS_YEAR
);
398 if (!EfiAtRuntime ()) {
399 EfiReleaseLock (&Global
->RtcLock
);
403 // Get the variable that contains the TimeZone and Daylight fields
405 Time
->TimeZone
= Global
->SavedTimeZone
;
406 Time
->Daylight
= Global
->Daylight
;
409 // Make sure all field values are in correct range
411 Status
= ConvertRtcTimeToEfiTime (Time
, RegisterB
);
412 if (!EFI_ERROR (Status
)) {
413 Status
= RtcTimeFieldsValid (Time
);
415 if (EFI_ERROR (Status
)) {
416 return EFI_DEVICE_ERROR
;
420 // Fill in Capabilities if it was passed in
422 if (Capabilities
!= NULL
) {
423 Capabilities
->Resolution
= 1;
427 Capabilities
->Accuracy
= 50000000;
431 Capabilities
->SetsToZero
= FALSE
;
438 Sets the current local time and date information.
440 @param Time A pointer to the current time.
441 @param Global For global use inside this module.
443 @retval EFI_SUCCESS The operation completed successfully.
444 @retval EFI_INVALID_PARAMETER A time field is out of range.
445 @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.
451 IN PC_RTC_MODULE_GLOBALS
*Global
456 RTC_REGISTER_B RegisterB
;
460 return EFI_INVALID_PARAMETER
;
463 // Make sure that the time fields are valid
465 Status
= RtcTimeFieldsValid (Time
);
466 if (EFI_ERROR (Status
)) {
470 CopyMem (&RtcTime
, Time
, sizeof (EFI_TIME
));
473 // Acquire RTC Lock to make access to RTC atomic
475 if (!EfiAtRuntime ()) {
476 EfiAcquireLock (&Global
->RtcLock
);
479 // Wait for up to 0.1 seconds for the RTC to be updated
481 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
482 if (EFI_ERROR (Status
)) {
483 if (!EfiAtRuntime ()) {
484 EfiReleaseLock (&Global
->RtcLock
);
490 // Write timezone and daylight to RTC variable
492 if ((Time
->TimeZone
== EFI_UNSPECIFIED_TIMEZONE
) && (Time
->Daylight
== 0)) {
493 Status
= EfiSetVariable (
494 mTimeZoneVariableName
,
500 if (Status
== EFI_NOT_FOUND
) {
501 Status
= EFI_SUCCESS
;
504 TimerVar
= Time
->Daylight
;
505 TimerVar
= (UINT32
) ((TimerVar
<< 16) | (UINT16
)(Time
->TimeZone
));
506 Status
= EfiSetVariable (
507 mTimeZoneVariableName
,
509 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_NON_VOLATILE
,
515 if (EFI_ERROR (Status
)) {
516 if (!EfiAtRuntime ()) {
517 EfiReleaseLock (&Global
->RtcLock
);
519 return EFI_DEVICE_ERROR
;
523 // Read Register B, and inhibit updates of the RTC
525 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
526 RegisterB
.Bits
.Set
= 1;
527 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
530 // Store the century value to RTC before converting to BCD format.
532 if (Global
->CenturyRtcAddress
!= 0) {
533 RtcWrite (Global
->CenturyRtcAddress
, DecimalToBcd8 ((UINT8
) (RtcTime
.Year
/ 100)));
536 ConvertEfiTimeToRtcTime (&RtcTime
, RegisterB
);
538 RtcWrite (RTC_ADDRESS_SECONDS
, RtcTime
.Second
);
539 RtcWrite (RTC_ADDRESS_MINUTES
, RtcTime
.Minute
);
540 RtcWrite (RTC_ADDRESS_HOURS
, RtcTime
.Hour
);
541 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH
, RtcTime
.Day
);
542 RtcWrite (RTC_ADDRESS_MONTH
, RtcTime
.Month
);
543 RtcWrite (RTC_ADDRESS_YEAR
, (UINT8
) RtcTime
.Year
);
546 // Allow updates of the RTC registers
548 RegisterB
.Bits
.Set
= 0;
549 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
554 if (!EfiAtRuntime ()) {
555 EfiReleaseLock (&Global
->RtcLock
);
558 // Set the variable that contains the TimeZone and Daylight fields
560 Global
->SavedTimeZone
= Time
->TimeZone
;
561 Global
->Daylight
= Time
->Daylight
;
567 Returns the current wakeup alarm clock setting.
569 @param Enabled Indicates if the alarm is currently enabled or disabled.
570 @param Pending Indicates if the alarm signal is pending and requires acknowledgment.
571 @param Time The current alarm setting.
572 @param Global For global use inside this module.
574 @retval EFI_SUCCESS The alarm settings were returned.
575 @retval EFI_INVALID_PARAMETER Enabled is NULL.
576 @retval EFI_INVALID_PARAMETER Pending is NULL.
577 @retval EFI_INVALID_PARAMETER Time is NULL.
578 @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.
579 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
584 OUT BOOLEAN
*Enabled
,
585 OUT BOOLEAN
*Pending
,
587 IN PC_RTC_MODULE_GLOBALS
*Global
591 RTC_REGISTER_B RegisterB
;
592 RTC_REGISTER_C RegisterC
;
597 // Check parameters for null pointers
599 if ((Enabled
== NULL
) || (Pending
== NULL
) || (Time
== NULL
)) {
600 return EFI_INVALID_PARAMETER
;
604 // Acquire RTC Lock to make access to RTC atomic
606 if (!EfiAtRuntime ()) {
607 EfiAcquireLock (&Global
->RtcLock
);
610 // Wait for up to 0.1 seconds for the RTC to be updated
612 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
613 if (EFI_ERROR (Status
)) {
614 if (!EfiAtRuntime ()) {
615 EfiReleaseLock (&Global
->RtcLock
);
617 return EFI_DEVICE_ERROR
;
620 // Read Register B and Register C
622 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
623 RegisterC
.Data
= RtcRead (RTC_ADDRESS_REGISTER_C
);
626 // Get the Time/Date/Daylight Savings values.
628 *Enabled
= RegisterB
.Bits
.Aie
;
629 *Pending
= RegisterC
.Bits
.Af
;
631 Time
->Second
= RtcRead (RTC_ADDRESS_SECONDS_ALARM
);
632 Time
->Minute
= RtcRead (RTC_ADDRESS_MINUTES_ALARM
);
633 Time
->Hour
= RtcRead (RTC_ADDRESS_HOURS_ALARM
);
634 Time
->Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
635 Time
->Month
= RtcRead (RTC_ADDRESS_MONTH
);
636 Time
->Year
= RtcRead (RTC_ADDRESS_YEAR
);
637 Time
->TimeZone
= Global
->SavedTimeZone
;
638 Time
->Daylight
= Global
->Daylight
;
641 // Get the alarm info from variable
643 DataSize
= sizeof (EFI_TIME
);
644 Status
= EfiGetVariable (
651 if (!EFI_ERROR (Status
)) {
653 // The alarm variable exists. In this case, we read variable to get info.
655 Time
->Day
= RtcTime
.Day
;
656 Time
->Month
= RtcTime
.Month
;
657 Time
->Year
= RtcTime
.Year
;
663 if (!EfiAtRuntime ()) {
664 EfiReleaseLock (&Global
->RtcLock
);
668 // Make sure all field values are in correct range
670 Status
= ConvertRtcTimeToEfiTime (Time
, RegisterB
);
671 if (!EFI_ERROR (Status
)) {
672 Status
= RtcTimeFieldsValid (Time
);
674 if (EFI_ERROR (Status
)) {
675 return EFI_DEVICE_ERROR
;
682 Sets the system wakeup alarm clock time.
684 @param Enabled Enable or disable the wakeup alarm.
685 @param Time If Enable is TRUE, the time to set the wakeup alarm for.
686 If Enable is FALSE, then this parameter is optional, and may be NULL.
687 @param Global For global use inside this module.
689 @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled.
690 If Enable is FALSE, then the wakeup alarm was disabled.
691 @retval EFI_INVALID_PARAMETER A time field is out of range.
692 @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.
693 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
699 IN EFI_TIME
*Time
, OPTIONAL
700 IN PC_RTC_MODULE_GLOBALS
*Global
705 RTC_REGISTER_B RegisterB
;
706 EFI_TIME_CAPABILITIES Capabilities
;
708 ZeroMem (&RtcTime
, sizeof (RtcTime
));
713 return EFI_INVALID_PARAMETER
;
716 // Make sure that the time fields are valid
718 Status
= RtcTimeFieldsValid (Time
);
719 if (EFI_ERROR (Status
)) {
720 return EFI_INVALID_PARAMETER
;
723 // Just support set alarm time within 24 hours
725 PcRtcGetTime (&RtcTime
, &Capabilities
, Global
);
726 Status
= RtcTimeFieldsValid (&RtcTime
);
727 if (EFI_ERROR (Status
)) {
728 return EFI_DEVICE_ERROR
;
730 if (!IsWithinOneDay (&RtcTime
, Time
)) {
731 return EFI_UNSUPPORTED
;
734 // Make a local copy of the time and date
736 CopyMem (&RtcTime
, Time
, sizeof (EFI_TIME
));
740 // Acquire RTC Lock to make access to RTC atomic
742 if (!EfiAtRuntime ()) {
743 EfiAcquireLock (&Global
->RtcLock
);
746 // Wait for up to 0.1 seconds for the RTC to be updated
748 Status
= RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout
));
749 if (EFI_ERROR (Status
)) {
750 if (!EfiAtRuntime ()) {
751 EfiReleaseLock (&Global
->RtcLock
);
753 return EFI_DEVICE_ERROR
;
758 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
761 ConvertEfiTimeToRtcTime (&RtcTime
, RegisterB
);
764 // if the alarm is disable, record the current setting.
766 RtcTime
.Second
= RtcRead (RTC_ADDRESS_SECONDS_ALARM
);
767 RtcTime
.Minute
= RtcRead (RTC_ADDRESS_MINUTES_ALARM
);
768 RtcTime
.Hour
= RtcRead (RTC_ADDRESS_HOURS_ALARM
);
769 RtcTime
.Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
770 RtcTime
.Month
= RtcRead (RTC_ADDRESS_MONTH
);
771 RtcTime
.Year
= RtcRead (RTC_ADDRESS_YEAR
);
772 RtcTime
.TimeZone
= Global
->SavedTimeZone
;
773 RtcTime
.Daylight
= Global
->Daylight
;
777 // Set the Y/M/D info to variable as it has no corresponding hw registers.
779 Status
= EfiSetVariable (
782 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
| EFI_VARIABLE_NON_VOLATILE
,
786 if (EFI_ERROR (Status
)) {
787 if (!EfiAtRuntime ()) {
788 EfiReleaseLock (&Global
->RtcLock
);
790 return EFI_DEVICE_ERROR
;
794 // Inhibit updates of the RTC
796 RegisterB
.Bits
.Set
= 1;
797 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
801 // Set RTC alarm time
803 RtcWrite (RTC_ADDRESS_SECONDS_ALARM
, RtcTime
.Second
);
804 RtcWrite (RTC_ADDRESS_MINUTES_ALARM
, RtcTime
.Minute
);
805 RtcWrite (RTC_ADDRESS_HOURS_ALARM
, RtcTime
.Hour
);
807 RegisterB
.Bits
.Aie
= 1;
810 RegisterB
.Bits
.Aie
= 0;
813 // Allow updates of the RTC registers
815 RegisterB
.Bits
.Set
= 0;
816 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
821 if (!EfiAtRuntime ()) {
822 EfiReleaseLock (&Global
->RtcLock
);
829 Checks an 8-bit BCD value, and converts to an 8-bit value if valid.
831 This function checks the 8-bit BCD value specified by Value.
832 If valid, the function converts it to an 8-bit value and returns it.
833 Otherwise, return 0xff.
835 @param Value The 8-bit BCD value to check and convert
837 @return The 8-bit value converted. Or 0xff if Value is invalid.
841 CheckAndConvertBcd8ToDecimal8 (
845 if ((Value
< 0xa0) && ((Value
& 0xf) < 0xa)) {
846 return BcdToDecimal8 (Value
);
853 Converts time read from RTC to EFI_TIME format defined by UEFI spec.
855 This function converts raw time data read from RTC to the EFI_TIME format
856 defined by UEFI spec.
857 If data mode of RTC is BCD, then converts it to decimal,
858 If RTC is in 12-hour format, then converts it to 24-hour format.
860 @param Time On input, the time data read from RTC to convert
861 On output, the time converted to UEFI format
862 @param RegisterB Value of Register B of RTC, indicating data mode
865 @retval EFI_INVALID_PARAMETER Parameters passed in are invalid.
866 @retval EFI_SUCCESS Convert RTC time to EFI time successfully.
870 ConvertRtcTimeToEfiTime (
871 IN OUT EFI_TIME
*Time
,
872 IN RTC_REGISTER_B RegisterB
878 if ((Time
->Hour
& 0x80) != 0) {
884 Time
->Hour
= (UINT8
) (Time
->Hour
& 0x7f);
886 if (RegisterB
.Bits
.Dm
== 0) {
887 Time
->Year
= CheckAndConvertBcd8ToDecimal8 ((UINT8
) Time
->Year
);
888 Time
->Month
= CheckAndConvertBcd8ToDecimal8 (Time
->Month
);
889 Time
->Day
= CheckAndConvertBcd8ToDecimal8 (Time
->Day
);
890 Time
->Hour
= CheckAndConvertBcd8ToDecimal8 (Time
->Hour
);
891 Time
->Minute
= CheckAndConvertBcd8ToDecimal8 (Time
->Minute
);
892 Time
->Second
= CheckAndConvertBcd8ToDecimal8 (Time
->Second
);
895 if (Time
->Year
== 0xff || Time
->Month
== 0xff || Time
->Day
== 0xff ||
896 Time
->Hour
== 0xff || Time
->Minute
== 0xff || Time
->Second
== 0xff) {
897 return EFI_INVALID_PARAMETER
;
901 // For minimal/maximum year range [1970, 2069],
902 // Century is 19 if RTC year >= 70,
903 // Century is 20 otherwise.
905 Century
= (UINT8
) (PcdGet16 (PcdMinimalValidYear
) / 100);
906 if (Time
->Year
< PcdGet16 (PcdMinimalValidYear
) % 100) {
909 Time
->Year
= (UINT16
) (Century
* 100 + Time
->Year
);
912 // If time is in 12 hour format, convert it to 24 hour format
914 if (RegisterB
.Bits
.Mil
== 0) {
915 if (IsPM
&& Time
->Hour
< 12) {
916 Time
->Hour
= (UINT8
) (Time
->Hour
+ 12);
919 if (!IsPM
&& Time
->Hour
== 12) {
924 Time
->Nanosecond
= 0;
930 Wait for a period for the RTC to be ready.
932 @param Timeout Tell how long it should take to wait.
934 @retval EFI_DEVICE_ERROR RTC device error.
935 @retval EFI_SUCCESS RTC is updated and ready.
942 RTC_REGISTER_A RegisterA
;
943 RTC_REGISTER_D RegisterD
;
946 // See if the RTC is functioning correctly
948 RegisterD
.Data
= RtcRead (RTC_ADDRESS_REGISTER_D
);
950 if (RegisterD
.Bits
.Vrt
== 0) {
951 return EFI_DEVICE_ERROR
;
954 // Wait for up to 0.1 seconds for the RTC to be ready.
956 Timeout
= (Timeout
/ 10) + 1;
957 RegisterA
.Data
= RtcRead (RTC_ADDRESS_REGISTER_A
);
958 while (RegisterA
.Bits
.Uip
== 1 && Timeout
> 0) {
959 MicroSecondDelay (10);
960 RegisterA
.Data
= RtcRead (RTC_ADDRESS_REGISTER_A
);
964 RegisterD
.Data
= RtcRead (RTC_ADDRESS_REGISTER_D
);
965 if (Timeout
== 0 || RegisterD
.Bits
.Vrt
== 0) {
966 return EFI_DEVICE_ERROR
;
973 See if all fields of a variable of EFI_TIME type is correct.
975 @param Time The time to be checked.
977 @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.
978 @retval EFI_SUCCESS Time is a valid EFI_TIME variable.
986 if (Time
->Year
< PcdGet16 (PcdMinimalValidYear
) ||
987 Time
->Year
> PcdGet16 (PcdMaximalValidYear
) ||
990 (!DayValid (Time
)) ||
994 Time
->Nanosecond
> 999999999 ||
995 (!(Time
->TimeZone
== EFI_UNSPECIFIED_TIMEZONE
|| (Time
->TimeZone
>= -1440 && Time
->TimeZone
<= 1440))) ||
996 ((Time
->Daylight
& (~(EFI_TIME_ADJUST_DAYLIGHT
| EFI_TIME_IN_DAYLIGHT
))) != 0)) {
997 return EFI_INVALID_PARAMETER
;
1004 See if field Day of an EFI_TIME is correct.
1006 @param Time Its Day field is to be checked.
1008 @retval TRUE Day field of Time is correct.
1009 @retval FALSE Day field of Time is NOT correct.
1017 // The validity of Time->Month field should be checked before
1019 ASSERT (Time
->Month
>=1);
1020 ASSERT (Time
->Month
<=12);
1021 if (Time
->Day
< 1 ||
1022 Time
->Day
> mDayOfMonth
[Time
->Month
- 1] ||
1023 (Time
->Month
== 2 && (!IsLeapYear (Time
) && Time
->Day
> 28))
1032 Check if it is a leap year.
1034 @param Time The time to be checked.
1036 @retval TRUE It is a leap year.
1037 @retval FALSE It is NOT a leap year.
1044 if (Time
->Year
% 4 == 0) {
1045 if (Time
->Year
% 100 == 0) {
1046 if (Time
->Year
% 400 == 0) {
1060 Converts time from EFI_TIME format defined by UEFI spec to RTC's.
1062 This function converts time from EFI_TIME format defined by UEFI spec to RTC's.
1063 If data mode of RTC is BCD, then converts EFI_TIME to it.
1064 If RTC is in 12-hour format, then converts EFI_TIME to it.
1066 @param Time On input, the time data read from UEFI to convert
1067 On output, the time converted to RTC format
1068 @param RegisterB Value of Register B of RTC, indicating data mode
1071 ConvertEfiTimeToRtcTime (
1072 IN OUT EFI_TIME
*Time
,
1073 IN RTC_REGISTER_B RegisterB
1080 // Adjust hour field if RTC is in 12 hour mode
1082 if (RegisterB
.Bits
.Mil
== 0) {
1083 if (Time
->Hour
< 12) {
1087 if (Time
->Hour
>= 13) {
1088 Time
->Hour
= (UINT8
) (Time
->Hour
- 12);
1089 } else if (Time
->Hour
== 0) {
1094 // Set the Time/Date values.
1096 Time
->Year
= (UINT16
) (Time
->Year
% 100);
1098 if (RegisterB
.Bits
.Dm
== 0) {
1099 Time
->Year
= DecimalToBcd8 ((UINT8
) Time
->Year
);
1100 Time
->Month
= DecimalToBcd8 (Time
->Month
);
1101 Time
->Day
= DecimalToBcd8 (Time
->Day
);
1102 Time
->Hour
= DecimalToBcd8 (Time
->Hour
);
1103 Time
->Minute
= DecimalToBcd8 (Time
->Minute
);
1104 Time
->Second
= DecimalToBcd8 (Time
->Second
);
1107 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
1109 if (RegisterB
.Bits
.Mil
== 0 && IsPM
) {
1110 Time
->Hour
= (UINT8
) (Time
->Hour
| 0x80);
1115 Compare the Hour, Minute and Second of the From time and the To time.
1117 Only compare H/M/S in EFI_TIME and ignore other fields here.
1119 @param From the first time
1120 @param To the second time
1122 @return >0 The H/M/S of the From time is later than those of To time
1123 @return ==0 The H/M/S of the From time is same as those of To time
1124 @return <0 The H/M/S of the From time is earlier than those of To time
1132 if ((From
->Hour
> To
->Hour
) ||
1133 ((From
->Hour
== To
->Hour
) && (From
->Minute
> To
->Minute
)) ||
1134 ((From
->Hour
== To
->Hour
) && (From
->Minute
== To
->Minute
) && (From
->Second
> To
->Second
))) {
1136 } else if ((From
->Hour
== To
->Hour
) && (From
->Minute
== To
->Minute
) && (From
->Second
== To
->Second
)) {
1144 To check if second date is later than first date within 24 hours.
1146 @param From the first date
1147 @param To the second date
1149 @retval TRUE From is previous to To within 24 hours.
1150 @retval FALSE From is later, or it is previous to To more than 24 hours.
1163 // The validity of From->Month field should be checked before
1165 ASSERT (From
->Month
>=1);
1166 ASSERT (From
->Month
<=12);
1168 if (From
->Year
== To
->Year
) {
1169 if (From
->Month
== To
->Month
) {
1170 if ((From
->Day
+ 1) == To
->Day
) {
1171 if ((CompareHMS(From
, To
) >= 0)) {
1174 } else if (From
->Day
== To
->Day
) {
1175 if ((CompareHMS(From
, To
) <= 0)) {
1179 } else if (((From
->Month
+ 1) == To
->Month
) && (To
->Day
== 1)) {
1180 if ((From
->Month
== 2) && !IsLeapYear(From
)) {
1181 if (From
->Day
== 28) {
1182 if ((CompareHMS(From
, To
) >= 0)) {
1186 } else if (From
->Day
== mDayOfMonth
[From
->Month
- 1]) {
1187 if ((CompareHMS(From
, To
) >= 0)) {
1192 } else if (((From
->Year
+ 1) == To
->Year
) &&
1193 (From
->Month
== 12) &&
1194 (From
->Day
== 31) &&
1197 if ((CompareHMS(From
, To
) >= 0)) {
1206 This function find ACPI table with the specified signature in RSDT or XSDT.
1208 @param Sdt ACPI RSDT or XSDT.
1209 @param Signature ACPI table signature.
1210 @param TablePointerSize Size of table pointer: 4 or 8.
1212 @return ACPI table or NULL if not found.
1216 IN EFI_ACPI_DESCRIPTION_HEADER
*Sdt
,
1217 IN UINT32 Signature
,
1218 IN UINTN TablePointerSize
1224 EFI_ACPI_DESCRIPTION_HEADER
*Table
;
1226 EntryCount
= (Sdt
->Length
- sizeof (EFI_ACPI_DESCRIPTION_HEADER
)) / TablePointerSize
;
1228 EntryBase
= (UINTN
) (Sdt
+ 1);
1229 for (Index
= 0; Index
< EntryCount
; Index
++) {
1231 // When TablePointerSize is 4 while sizeof (VOID *) is 8, make sure the upper 4 bytes are zero.
1234 CopyMem (&Table
, (VOID
*) (EntryBase
+ Index
* TablePointerSize
), TablePointerSize
);
1236 if (Table
== NULL
) {
1240 if (Table
->Signature
== Signature
) {
1249 Get the century RTC address from the ACPI FADT table.
1251 @return The century RTC address or 0 if not found.
1254 GetCenturyRtcAddress (
1259 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER
*Rsdp
;
1260 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
1262 Status
= EfiGetSystemConfigurationTable (&gEfiAcpiTableGuid
, (VOID
**) &Rsdp
);
1263 if (EFI_ERROR (Status
)) {
1264 Status
= EfiGetSystemConfigurationTable (&gEfiAcpi10TableGuid
, (VOID
**) &Rsdp
);
1267 if (EFI_ERROR (Status
) || (Rsdp
== NULL
)) {
1274 // Find FADT in XSDT
1276 if (Rsdp
->Revision
>= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION
&& Rsdp
->XsdtAddress
!= 0) {
1277 Fadt
= ScanTableInSDT (
1278 (EFI_ACPI_DESCRIPTION_HEADER
*) (UINTN
) Rsdp
->XsdtAddress
,
1279 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
,
1285 // Find FADT in RSDT
1287 if (Fadt
== NULL
&& Rsdp
->RsdtAddress
!= 0) {
1288 Fadt
= ScanTableInSDT (
1289 (EFI_ACPI_DESCRIPTION_HEADER
*) (UINTN
) Rsdp
->RsdtAddress
,
1290 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
,
1295 if ((Fadt
!= NULL
) &&
1296 (Fadt
->Century
> RTC_ADDRESS_REGISTER_D
) && (Fadt
->Century
< 0x80)
1298 return Fadt
->Century
;
1305 Notification function of ACPI Table change.
1307 This is a notification function registered on ACPI Table change event.
1308 It saves the Century address stored in ACPI FADT table.
1310 @param Event Event whose notification function is being invoked.
1311 @param Context Pointer to the notification function's context.
1316 PcRtcAcpiTableChangeCallback (
1323 UINT8 CenturyRtcAddress
;
1326 CenturyRtcAddress
= GetCenturyRtcAddress ();
1327 if ((CenturyRtcAddress
!= 0) && (mModuleGlobal
.CenturyRtcAddress
!= CenturyRtcAddress
)) {
1328 mModuleGlobal
.CenturyRtcAddress
= CenturyRtcAddress
;
1329 Status
= PcRtcGetTime (&Time
, NULL
, &mModuleGlobal
);
1330 if (!EFI_ERROR (Status
)) {
1331 Century
= (UINT8
) (Time
.Year
/ 100);
1332 Century
= DecimalToBcd8 (Century
);
1333 DEBUG ((EFI_D_INFO
, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century
, mModuleGlobal
.CenturyRtcAddress
));
1334 RtcWrite (mModuleGlobal
.CenturyRtcAddress
, Century
);