]> git.proxmox.com Git - mirror_edk2.git/blob - PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / PcAtChipsetPkg / PcatRealTimeClockRuntimeDxe / PcRtc.c
1 /** @file
2 RTC Architectural Protocol GUID as defined in DxeCis 0.96.
3
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>
7
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9
10 **/
11
12 #include "PcRtc.h"
13
14 extern UINTN mRtcIndexRegister;
15 extern UINTN mRtcTargetRegister;
16
17 //
18 // Days of month.
19 //
20 UINTN mDayOfMonth[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
21
22 //
23 // The name of NV variable to store the timezone and daylight saving information.
24 //
25 CHAR16 mTimeZoneVariableName[] = L"RTC";
26
27 /**
28 Compare the Hour, Minute and Second of the From time and the To time.
29
30 Only compare H/M/S in EFI_TIME and ignore other fields here.
31
32 @param From the first time
33 @param To the second time
34
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
38 **/
39 INTN
40 CompareHMS (
41 IN EFI_TIME *From,
42 IN EFI_TIME *To
43 );
44
45 /**
46 To check if second date is later than first date within 24 hours.
47
48 @param From the first date
49 @param To the second date
50
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.
53 **/
54 BOOLEAN
55 IsWithinOneDay (
56 IN EFI_TIME *From,
57 IN EFI_TIME *To
58 );
59
60 /**
61 Read RTC content through its registers using IO access.
62
63 @param Address Address offset of RTC. It is recommended to use
64 macros such as RTC_ADDRESS_SECONDS.
65
66 @return The data of UINT8 type read from RTC.
67 **/
68 STATIC
69 UINT8
70 IoRtcRead (
71 IN UINTN Address
72 )
73 {
74 IoWrite8 (
75 PcdGet8 (PcdRtcIndexRegister),
76 (UINT8)(Address | (UINT8)(IoRead8 (PcdGet8 (PcdRtcIndexRegister)) & 0x80))
77 );
78 return IoRead8 (PcdGet8 (PcdRtcTargetRegister));
79 }
80
81 /**
82 Write RTC through its registers using IO access.
83
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.
87
88 **/
89 STATIC
90 VOID
91 IoRtcWrite (
92 IN UINTN Address,
93 IN UINT8 Data
94 )
95 {
96 IoWrite8 (
97 PcdGet8 (PcdRtcIndexRegister),
98 (UINT8)(Address | (UINT8)(IoRead8 (PcdGet8 (PcdRtcIndexRegister)) & 0x80))
99 );
100 IoWrite8 (PcdGet8 (PcdRtcTargetRegister), Data);
101 }
102
103 /**
104 Read RTC content through its registers using MMIO access.
105
106 @param Address Address offset of RTC. It is recommended to use
107 macros such as RTC_ADDRESS_SECONDS.
108
109 @return The data of UINT8 type read from RTC.
110 **/
111 STATIC
112 UINT8
113 MmioRtcRead (
114 IN UINTN Address
115 )
116 {
117 MmioWrite8 (
118 mRtcIndexRegister,
119 (UINT8)(Address | (UINT8)(MmioRead8 (mRtcIndexRegister) & 0x80))
120 );
121 return MmioRead8 (mRtcTargetRegister);
122 }
123
124 /**
125 Write RTC through its registers using MMIO access.
126
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.
130
131 **/
132 STATIC
133 VOID
134 MmioRtcWrite (
135 IN UINTN Address,
136 IN UINT8 Data
137 )
138 {
139 MmioWrite8 (
140 mRtcIndexRegister,
141 (UINT8)(Address | (UINT8)(MmioRead8 (mRtcIndexRegister) & 0x80))
142 );
143 MmioWrite8 (mRtcTargetRegister, Data);
144 }
145
146 /**
147 Read RTC content through its registers.
148
149 @param Address Address offset of RTC. It is recommended to use
150 macros such as RTC_ADDRESS_SECONDS.
151
152 @return The data of UINT8 type read from RTC.
153 **/
154 STATIC
155 UINT8
156 RtcRead (
157 IN UINTN Address
158 )
159 {
160 if (FeaturePcdGet (PcdRtcUseMmio)) {
161 return MmioRtcRead (Address);
162 }
163
164 return IoRtcRead (Address);
165 }
166
167 /**
168 Write RTC through its registers.
169
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.
173
174 **/
175 STATIC
176 VOID
177 RtcWrite (
178 IN UINTN Address,
179 IN UINT8 Data
180 )
181 {
182 if (FeaturePcdGet (PcdRtcUseMmio)) {
183 MmioRtcWrite (Address, Data);
184 } else {
185 IoRtcWrite (Address, Data);
186 }
187 }
188
189 /**
190 Initialize RTC.
191
192 @param Global For global use inside this module.
193
194 @retval EFI_DEVICE_ERROR Initialization failed due to device error.
195 @retval EFI_SUCCESS Initialization successful.
196
197 **/
198 EFI_STATUS
199 PcRtcInit (
200 IN PC_RTC_MODULE_GLOBALS *Global
201 )
202 {
203 EFI_STATUS Status;
204 RTC_REGISTER_A RegisterA;
205 RTC_REGISTER_B RegisterB;
206 RTC_REGISTER_D RegisterD;
207 EFI_TIME Time;
208 UINTN DataSize;
209 UINT32 TimerVar;
210 BOOLEAN Enabled;
211 BOOLEAN Pending;
212
213 //
214 // Acquire RTC Lock to make access to RTC atomic
215 //
216 if (!EfiAtRuntime ()) {
217 EfiAcquireLock (&Global->RtcLock);
218 }
219
220 //
221 // Initialize RTC Register
222 //
223 // Make sure Division Chain is properly configured,
224 // or RTC clock won't "tick" -- time won't increment
225 //
226 RegisterA.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterA);
227 RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);
228
229 //
230 // Read Register B
231 //
232 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
233
234 //
235 // Clear RTC flag register
236 //
237 RtcRead (RTC_ADDRESS_REGISTER_C);
238
239 //
240 // Clear RTC register D
241 //
242 RegisterD.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterD);
243 RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data);
244
245 //
246 // Wait for up to 0.1 seconds for the RTC to be updated
247 //
248 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
249 if (EFI_ERROR (Status)) {
250 //
251 // Set the variable with default value if the RTC is functioning incorrectly.
252 //
253 Global->SavedTimeZone = EFI_UNSPECIFIED_TIMEZONE;
254 Global->Daylight = 0;
255 if (!EfiAtRuntime ()) {
256 EfiReleaseLock (&Global->RtcLock);
257 }
258
259 return EFI_DEVICE_ERROR;
260 }
261
262 //
263 // Get the Time/Date/Daylight Savings values.
264 //
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);
271
272 //
273 // Release RTC Lock.
274 //
275 if (!EfiAtRuntime ()) {
276 EfiReleaseLock (&Global->RtcLock);
277 }
278
279 //
280 // Get the data of Daylight saving and time zone, if they have been
281 // stored in NV variable during previous boot.
282 //
283 DataSize = sizeof (UINT32);
284 Status = EfiGetVariable (
285 mTimeZoneVariableName,
286 &gEfiCallerIdGuid,
287 NULL,
288 &DataSize,
289 &TimerVar
290 );
291 if (!EFI_ERROR (Status)) {
292 Time.TimeZone = (INT16)TimerVar;
293 Time.Daylight = (UINT8)(TimerVar >> 16);
294 } else {
295 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
296 Time.Daylight = 0;
297 }
298
299 //
300 // Validate time fields
301 //
302 Status = ConvertRtcTimeToEfiTime (&Time, RegisterB);
303 if (!EFI_ERROR (Status)) {
304 Status = RtcTimeFieldsValid (&Time);
305 }
306
307 if (EFI_ERROR (Status)) {
308 //
309 // Report Status Code to indicate that the RTC has bad date and time
310 //
311 REPORT_STATUS_CODE (
312 EFI_ERROR_CODE | EFI_ERROR_MINOR,
313 (EFI_SOFTWARE_DXE_RT_DRIVER | EFI_SW_EC_BAD_DATE_TIME)
314 );
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);
321 Time.Nanosecond = 0;
322 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
323 Time.Daylight = 0;
324 }
325
326 //
327 // Set RTC configuration after get original time
328 // The value of bit AIE should be reserved.
329 //
330 RegisterB.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterB) | (RegisterB.Data & BIT5);
331 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
332
333 //
334 // Reset time value according to new RTC configuration
335 //
336 Status = PcRtcSetTime (&Time, Global);
337 if (EFI_ERROR (Status)) {
338 return EFI_DEVICE_ERROR;
339 }
340
341 //
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.
345 //
346 Status = PcRtcGetWakeupTime (&Enabled, &Pending, &Time, Global);
347 if ((Enabled) || (!EFI_ERROR (Status))) {
348 return EFI_SUCCESS;
349 }
350
351 //
352 // When wakeup time is disabled and invalid, reset wakeup time register to valid state
353 // but keep wakeup alarm disabled.
354 //
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);
361 Time.Nanosecond = 0;
362 Time.TimeZone = Global->SavedTimeZone;
363 Time.Daylight = Global->Daylight;
364
365 //
366 // Acquire RTC Lock to make access to RTC atomic
367 //
368 if (!EfiAtRuntime ()) {
369 EfiAcquireLock (&Global->RtcLock);
370 }
371
372 //
373 // Wait for up to 0.1 seconds for the RTC to be updated
374 //
375 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
376 if (EFI_ERROR (Status)) {
377 if (!EfiAtRuntime ()) {
378 EfiReleaseLock (&Global->RtcLock);
379 }
380
381 return EFI_DEVICE_ERROR;
382 }
383
384 ConvertEfiTimeToRtcTime (&Time, RegisterB);
385
386 //
387 // Set the Y/M/D info to variable as it has no corresponding hw registers.
388 //
389 Status = EfiSetVariable (
390 L"RTCALARM",
391 &gEfiCallerIdGuid,
392 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
393 sizeof (Time),
394 &Time
395 );
396 if (EFI_ERROR (Status)) {
397 if (!EfiAtRuntime ()) {
398 EfiReleaseLock (&Global->RtcLock);
399 }
400
401 return EFI_DEVICE_ERROR;
402 }
403
404 //
405 // Inhibit updates of the RTC
406 //
407 RegisterB.Bits.Set = 1;
408 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
409
410 //
411 // Set RTC alarm time registers
412 //
413 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, Time.Second);
414 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, Time.Minute);
415 RtcWrite (RTC_ADDRESS_HOURS_ALARM, Time.Hour);
416
417 //
418 // Allow updates of the RTC registers
419 //
420 RegisterB.Bits.Set = 0;
421 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
422
423 //
424 // Release RTC Lock.
425 //
426 if (!EfiAtRuntime ()) {
427 EfiReleaseLock (&Global->RtcLock);
428 }
429
430 return EFI_SUCCESS;
431 }
432
433 /**
434 Returns the current time and date information, and the time-keeping capabilities
435 of the hardware platform.
436
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.
441
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.
445
446 **/
447 EFI_STATUS
448 PcRtcGetTime (
449 OUT EFI_TIME *Time,
450 OUT EFI_TIME_CAPABILITIES *Capabilities OPTIONAL,
451 IN PC_RTC_MODULE_GLOBALS *Global
452 )
453 {
454 EFI_STATUS Status;
455 RTC_REGISTER_B RegisterB;
456
457 //
458 // Check parameters for null pointer
459 //
460 if (Time == NULL) {
461 return EFI_INVALID_PARAMETER;
462 }
463
464 //
465 // Acquire RTC Lock to make access to RTC atomic
466 //
467 if (!EfiAtRuntime ()) {
468 EfiAcquireLock (&Global->RtcLock);
469 }
470
471 //
472 // Wait for up to 0.1 seconds for the RTC to be updated
473 //
474 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
475 if (EFI_ERROR (Status)) {
476 if (!EfiAtRuntime ()) {
477 EfiReleaseLock (&Global->RtcLock);
478 }
479
480 return Status;
481 }
482
483 //
484 // Read Register B
485 //
486 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
487
488 //
489 // Get the Time/Date/Daylight Savings values.
490 //
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);
497
498 //
499 // Release RTC Lock.
500 //
501 if (!EfiAtRuntime ()) {
502 EfiReleaseLock (&Global->RtcLock);
503 }
504
505 //
506 // Get the variable that contains the TimeZone and Daylight fields
507 //
508 Time->TimeZone = Global->SavedTimeZone;
509 Time->Daylight = Global->Daylight;
510
511 //
512 // Make sure all field values are in correct range
513 //
514 Status = ConvertRtcTimeToEfiTime (Time, RegisterB);
515 if (!EFI_ERROR (Status)) {
516 Status = RtcTimeFieldsValid (Time);
517 }
518
519 if (EFI_ERROR (Status)) {
520 return EFI_DEVICE_ERROR;
521 }
522
523 //
524 // Fill in Capabilities if it was passed in
525 //
526 if (Capabilities != NULL) {
527 Capabilities->Resolution = 1;
528 //
529 // 1 hertz
530 //
531 Capabilities->Accuracy = 50000000;
532 //
533 // 50 ppm
534 //
535 Capabilities->SetsToZero = FALSE;
536 }
537
538 return EFI_SUCCESS;
539 }
540
541 /**
542 Sets the current local time and date information.
543
544 @param Time A pointer to the current time.
545 @param Global For global use inside this module.
546
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.
550
551 **/
552 EFI_STATUS
553 PcRtcSetTime (
554 IN EFI_TIME *Time,
555 IN PC_RTC_MODULE_GLOBALS *Global
556 )
557 {
558 EFI_STATUS Status;
559 EFI_TIME RtcTime;
560 RTC_REGISTER_B RegisterB;
561 UINT32 TimerVar;
562
563 if (Time == NULL) {
564 return EFI_INVALID_PARAMETER;
565 }
566
567 //
568 // Make sure that the time fields are valid
569 //
570 Status = RtcTimeFieldsValid (Time);
571 if (EFI_ERROR (Status)) {
572 return Status;
573 }
574
575 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
576
577 //
578 // Acquire RTC Lock to make access to RTC atomic
579 //
580 if (!EfiAtRuntime ()) {
581 EfiAcquireLock (&Global->RtcLock);
582 }
583
584 //
585 // Wait for up to 0.1 seconds for the RTC to be updated
586 //
587 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
588 if (EFI_ERROR (Status)) {
589 if (!EfiAtRuntime ()) {
590 EfiReleaseLock (&Global->RtcLock);
591 }
592
593 return Status;
594 }
595
596 //
597 // Write timezone and daylight to RTC variable
598 //
599 if ((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) && (Time->Daylight == 0)) {
600 Status = EfiSetVariable (
601 mTimeZoneVariableName,
602 &gEfiCallerIdGuid,
603 0,
604 0,
605 NULL
606 );
607 if (Status == EFI_NOT_FOUND) {
608 Status = EFI_SUCCESS;
609 }
610 } else {
611 TimerVar = Time->Daylight;
612 TimerVar = (UINT32)((TimerVar << 16) | (UINT16)(Time->TimeZone));
613 Status = EfiSetVariable (
614 mTimeZoneVariableName,
615 &gEfiCallerIdGuid,
616 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
617 sizeof (TimerVar),
618 &TimerVar
619 );
620 }
621
622 if (EFI_ERROR (Status)) {
623 if (!EfiAtRuntime ()) {
624 EfiReleaseLock (&Global->RtcLock);
625 }
626
627 return EFI_DEVICE_ERROR;
628 }
629
630 //
631 // Read Register B, and inhibit updates of the RTC
632 //
633 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
634 RegisterB.Bits.Set = 1;
635 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
636
637 //
638 // Store the century value to RTC before converting to BCD format.
639 //
640 if (Global->CenturyRtcAddress != 0) {
641 RtcWrite (Global->CenturyRtcAddress, DecimalToBcd8 ((UINT8)(RtcTime.Year / 100)));
642 }
643
644 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);
645
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);
652
653 //
654 // Allow updates of the RTC registers
655 //
656 RegisterB.Bits.Set = 0;
657 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
658
659 //
660 // Release RTC Lock.
661 //
662 if (!EfiAtRuntime ()) {
663 EfiReleaseLock (&Global->RtcLock);
664 }
665
666 //
667 // Set the variable that contains the TimeZone and Daylight fields
668 //
669 Global->SavedTimeZone = Time->TimeZone;
670 Global->Daylight = Time->Daylight;
671
672 return EFI_SUCCESS;
673 }
674
675 /**
676 Returns the current wakeup alarm clock setting.
677
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.
682
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.
689
690 **/
691 EFI_STATUS
692 PcRtcGetWakeupTime (
693 OUT BOOLEAN *Enabled,
694 OUT BOOLEAN *Pending,
695 OUT EFI_TIME *Time,
696 IN PC_RTC_MODULE_GLOBALS *Global
697 )
698 {
699 EFI_STATUS Status;
700 RTC_REGISTER_B RegisterB;
701 RTC_REGISTER_C RegisterC;
702 EFI_TIME RtcTime;
703 UINTN DataSize;
704
705 //
706 // Check parameters for null pointers
707 //
708 if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) {
709 return EFI_INVALID_PARAMETER;
710 }
711
712 //
713 // Acquire RTC Lock to make access to RTC atomic
714 //
715 if (!EfiAtRuntime ()) {
716 EfiAcquireLock (&Global->RtcLock);
717 }
718
719 //
720 // Wait for up to 0.1 seconds for the RTC to be updated
721 //
722 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
723 if (EFI_ERROR (Status)) {
724 if (!EfiAtRuntime ()) {
725 EfiReleaseLock (&Global->RtcLock);
726 }
727
728 return EFI_DEVICE_ERROR;
729 }
730
731 //
732 // Read Register B and Register C
733 //
734 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
735 RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);
736
737 //
738 // Get the Time/Date/Daylight Savings values.
739 //
740 *Enabled = RegisterB.Bits.Aie;
741 *Pending = RegisterC.Bits.Af;
742
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;
751
752 //
753 // Get the alarm info from variable
754 //
755 DataSize = sizeof (EFI_TIME);
756 Status = EfiGetVariable (
757 L"RTCALARM",
758 &gEfiCallerIdGuid,
759 NULL,
760 &DataSize,
761 &RtcTime
762 );
763 if (!EFI_ERROR (Status)) {
764 //
765 // The alarm variable exists. In this case, we read variable to get info.
766 //
767 Time->Day = RtcTime.Day;
768 Time->Month = RtcTime.Month;
769 Time->Year = RtcTime.Year;
770 }
771
772 //
773 // Release RTC Lock.
774 //
775 if (!EfiAtRuntime ()) {
776 EfiReleaseLock (&Global->RtcLock);
777 }
778
779 //
780 // Make sure all field values are in correct range
781 //
782 Status = ConvertRtcTimeToEfiTime (Time, RegisterB);
783 if (!EFI_ERROR (Status)) {
784 Status = RtcTimeFieldsValid (Time);
785 }
786
787 if (EFI_ERROR (Status)) {
788 return EFI_DEVICE_ERROR;
789 }
790
791 return EFI_SUCCESS;
792 }
793
794 /**
795 Sets the system wakeup alarm clock time.
796
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.
801
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.
807
808 **/
809 EFI_STATUS
810 PcRtcSetWakeupTime (
811 IN BOOLEAN Enable,
812 IN EFI_TIME *Time OPTIONAL,
813 IN PC_RTC_MODULE_GLOBALS *Global
814 )
815 {
816 EFI_STATUS Status;
817 EFI_TIME RtcTime;
818 RTC_REGISTER_B RegisterB;
819 EFI_TIME_CAPABILITIES Capabilities;
820
821 ZeroMem (&RtcTime, sizeof (RtcTime));
822
823 if (Enable) {
824 if (Time == NULL) {
825 return EFI_INVALID_PARAMETER;
826 }
827
828 //
829 // Make sure that the time fields are valid
830 //
831 Status = RtcTimeFieldsValid (Time);
832 if (EFI_ERROR (Status)) {
833 return EFI_INVALID_PARAMETER;
834 }
835
836 //
837 // Just support set alarm time within 24 hours
838 //
839 PcRtcGetTime (&RtcTime, &Capabilities, Global);
840 Status = RtcTimeFieldsValid (&RtcTime);
841 if (EFI_ERROR (Status)) {
842 return EFI_DEVICE_ERROR;
843 }
844
845 if (!IsWithinOneDay (&RtcTime, Time)) {
846 return EFI_UNSUPPORTED;
847 }
848
849 //
850 // Make a local copy of the time and date
851 //
852 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
853 }
854
855 //
856 // Acquire RTC Lock to make access to RTC atomic
857 //
858 if (!EfiAtRuntime ()) {
859 EfiAcquireLock (&Global->RtcLock);
860 }
861
862 //
863 // Wait for up to 0.1 seconds for the RTC to be updated
864 //
865 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
866 if (EFI_ERROR (Status)) {
867 if (!EfiAtRuntime ()) {
868 EfiReleaseLock (&Global->RtcLock);
869 }
870
871 return EFI_DEVICE_ERROR;
872 }
873
874 //
875 // Read Register B
876 //
877 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
878
879 if (Enable) {
880 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);
881 } else {
882 //
883 // if the alarm is disable, record the current setting.
884 //
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;
893 }
894
895 //
896 // Set the Y/M/D info to variable as it has no corresponding hw registers.
897 //
898 Status = EfiSetVariable (
899 L"RTCALARM",
900 &gEfiCallerIdGuid,
901 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
902 sizeof (RtcTime),
903 &RtcTime
904 );
905 if (EFI_ERROR (Status)) {
906 if (!EfiAtRuntime ()) {
907 EfiReleaseLock (&Global->RtcLock);
908 }
909
910 return EFI_DEVICE_ERROR;
911 }
912
913 //
914 // Inhibit updates of the RTC
915 //
916 RegisterB.Bits.Set = 1;
917 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
918
919 if (Enable) {
920 //
921 // Set RTC alarm time
922 //
923 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);
924 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);
925 RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);
926
927 RegisterB.Bits.Aie = 1;
928 } else {
929 RegisterB.Bits.Aie = 0;
930 }
931
932 //
933 // Allow updates of the RTC registers
934 //
935 RegisterB.Bits.Set = 0;
936 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
937
938 //
939 // Release RTC Lock.
940 //
941 if (!EfiAtRuntime ()) {
942 EfiReleaseLock (&Global->RtcLock);
943 }
944
945 return EFI_SUCCESS;
946 }
947
948 /**
949 Checks an 8-bit BCD value, and converts to an 8-bit value if valid.
950
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.
954
955 @param Value The 8-bit BCD value to check and convert
956
957 @return The 8-bit value converted. Or 0xff if Value is invalid.
958
959 **/
960 UINT8
961 CheckAndConvertBcd8ToDecimal8 (
962 IN UINT8 Value
963 )
964 {
965 if ((Value < 0xa0) && ((Value & 0xf) < 0xa)) {
966 return BcdToDecimal8 (Value);
967 }
968
969 return 0xff;
970 }
971
972 /**
973 Converts time read from RTC to EFI_TIME format defined by UEFI spec.
974
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.
979
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
983 and hour format.
984
985 @retval EFI_INVALID_PARAMETER Parameters passed in are invalid.
986 @retval EFI_SUCCESS Convert RTC time to EFI time successfully.
987
988 **/
989 EFI_STATUS
990 ConvertRtcTimeToEfiTime (
991 IN OUT EFI_TIME *Time,
992 IN RTC_REGISTER_B RegisterB
993 )
994 {
995 BOOLEAN IsPM;
996 UINT8 Century;
997
998 // IsPM only makes sense for 12-hour format.
999 if (RegisterB.Bits.Mil == 0) {
1000 if ((Time->Hour & 0x80) != 0) {
1001 IsPM = TRUE;
1002 } else {
1003 IsPM = FALSE;
1004 }
1005
1006 Time->Hour = (UINT8)(Time->Hour & 0x7f);
1007 }
1008
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);
1016 }
1017
1018 if ((Time->Year == 0xff) || (Time->Month == 0xff) || (Time->Day == 0xff) ||
1019 (Time->Hour == 0xff) || (Time->Minute == 0xff) || (Time->Second == 0xff))
1020 {
1021 return EFI_INVALID_PARAMETER;
1022 }
1023
1024 //
1025 // For minimal/maximum year range [1970, 2069],
1026 // Century is 19 if RTC year >= 70,
1027 // Century is 20 otherwise.
1028 //
1029 Century = (UINT8)(PcdGet16 (PcdMinimalValidYear) / 100);
1030 if (Time->Year < PcdGet16 (PcdMinimalValidYear) % 100) {
1031 Century++;
1032 }
1033
1034 Time->Year = (UINT16)(Century * 100 + Time->Year);
1035
1036 //
1037 // If time is in 12 hour format, convert it to 24 hour format
1038 //
1039 if (RegisterB.Bits.Mil == 0) {
1040 if (IsPM && (Time->Hour < 12)) {
1041 Time->Hour = (UINT8)(Time->Hour + 12);
1042 }
1043
1044 if (!IsPM && (Time->Hour == 12)) {
1045 Time->Hour = 0;
1046 }
1047 }
1048
1049 Time->Nanosecond = 0;
1050
1051 return EFI_SUCCESS;
1052 }
1053
1054 /**
1055 Wait for a period for the RTC to be ready.
1056
1057 @param Timeout Tell how long it should take to wait.
1058
1059 @retval EFI_DEVICE_ERROR RTC device error.
1060 @retval EFI_SUCCESS RTC is updated and ready.
1061 **/
1062 EFI_STATUS
1063 RtcWaitToUpdate (
1064 UINTN Timeout
1065 )
1066 {
1067 RTC_REGISTER_A RegisterA;
1068 RTC_REGISTER_D RegisterD;
1069
1070 //
1071 // See if the RTC is functioning correctly
1072 //
1073 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
1074
1075 if (RegisterD.Bits.Vrt == 0) {
1076 return EFI_DEVICE_ERROR;
1077 }
1078
1079 //
1080 // Wait for up to 0.1 seconds for the RTC to be ready.
1081 //
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);
1087 Timeout--;
1088 }
1089
1090 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
1091 if ((Timeout == 0) || (RegisterD.Bits.Vrt == 0)) {
1092 return EFI_DEVICE_ERROR;
1093 }
1094
1095 return EFI_SUCCESS;
1096 }
1097
1098 /**
1099 See if all fields of a variable of EFI_TIME type is correct.
1100
1101 @param Time The time to be checked.
1102
1103 @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.
1104 @retval EFI_SUCCESS Time is a valid EFI_TIME variable.
1105
1106 **/
1107 EFI_STATUS
1108 RtcTimeFieldsValid (
1109 IN EFI_TIME *Time
1110 )
1111 {
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))
1123 {
1124 return EFI_INVALID_PARAMETER;
1125 }
1126
1127 return EFI_SUCCESS;
1128 }
1129
1130 /**
1131 See if field Day of an EFI_TIME is correct.
1132
1133 @param Time Its Day field is to be checked.
1134
1135 @retval TRUE Day field of Time is correct.
1136 @retval FALSE Day field of Time is NOT correct.
1137 **/
1138 BOOLEAN
1139 DayValid (
1140 IN EFI_TIME *Time
1141 )
1142 {
1143 //
1144 // The validity of Time->Month field should be checked before
1145 //
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)))
1151 )
1152 {
1153 return FALSE;
1154 }
1155
1156 return TRUE;
1157 }
1158
1159 /**
1160 Check if it is a leap year.
1161
1162 @param Time The time to be checked.
1163
1164 @retval TRUE It is a leap year.
1165 @retval FALSE It is NOT a leap year.
1166 **/
1167 BOOLEAN
1168 IsLeapYear (
1169 IN EFI_TIME *Time
1170 )
1171 {
1172 if (Time->Year % 4 == 0) {
1173 if (Time->Year % 100 == 0) {
1174 if (Time->Year % 400 == 0) {
1175 return TRUE;
1176 } else {
1177 return FALSE;
1178 }
1179 } else {
1180 return TRUE;
1181 }
1182 } else {
1183 return FALSE;
1184 }
1185 }
1186
1187 /**
1188 Converts time from EFI_TIME format defined by UEFI spec to RTC format.
1189
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.
1193
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
1197 **/
1198 VOID
1199 ConvertEfiTimeToRtcTime (
1200 IN OUT EFI_TIME *Time,
1201 IN RTC_REGISTER_B RegisterB
1202 )
1203 {
1204 BOOLEAN IsPM;
1205
1206 IsPM = TRUE;
1207 //
1208 // Adjust hour field if RTC is in 12 hour mode
1209 //
1210 if (RegisterB.Bits.Mil == 0) {
1211 if (Time->Hour < 12) {
1212 IsPM = FALSE;
1213 }
1214
1215 if (Time->Hour >= 13) {
1216 Time->Hour = (UINT8)(Time->Hour - 12);
1217 } else if (Time->Hour == 0) {
1218 Time->Hour = 12;
1219 }
1220 }
1221
1222 //
1223 // Set the Time/Date values.
1224 //
1225 Time->Year = (UINT16)(Time->Year % 100);
1226
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);
1234 }
1235
1236 //
1237 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
1238 //
1239 if ((RegisterB.Bits.Mil == 0) && IsPM) {
1240 Time->Hour = (UINT8)(Time->Hour | 0x80);
1241 }
1242 }
1243
1244 /**
1245 Compare the Hour, Minute and Second of the From time and the To time.
1246
1247 Only compare H/M/S in EFI_TIME and ignore other fields here.
1248
1249 @param From the first time
1250 @param To the second time
1251
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
1255 **/
1256 INTN
1257 CompareHMS (
1258 IN EFI_TIME *From,
1259 IN EFI_TIME *To
1260 )
1261 {
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)))
1265 {
1266 return 1;
1267 } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) {
1268 return 0;
1269 } else {
1270 return -1;
1271 }
1272 }
1273
1274 /**
1275 To check if second date is later than first date within 24 hours.
1276
1277 @param From the first date
1278 @param To the second date
1279
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.
1282 **/
1283 BOOLEAN
1284 IsWithinOneDay (
1285 IN EFI_TIME *From,
1286 IN EFI_TIME *To
1287 )
1288 {
1289 BOOLEAN Adjacent;
1290
1291 Adjacent = FALSE;
1292
1293 //
1294 // The validity of From->Month field should be checked before
1295 //
1296 ASSERT (From->Month >= 1);
1297 ASSERT (From->Month <= 12);
1298
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)) {
1303 Adjacent = TRUE;
1304 }
1305 } else if (From->Day == To->Day) {
1306 if ((CompareHMS (From, To) <= 0)) {
1307 Adjacent = TRUE;
1308 }
1309 }
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)) {
1314 Adjacent = TRUE;
1315 }
1316 }
1317 } else if (From->Day == mDayOfMonth[From->Month - 1]) {
1318 if ((CompareHMS (From, To) >= 0)) {
1319 Adjacent = TRUE;
1320 }
1321 }
1322 }
1323 } else if (((From->Year + 1) == To->Year) &&
1324 (From->Month == 12) &&
1325 (From->Day == 31) &&
1326 (To->Month == 1) &&
1327 (To->Day == 1))
1328 {
1329 if ((CompareHMS (From, To) >= 0)) {
1330 Adjacent = TRUE;
1331 }
1332 }
1333
1334 return Adjacent;
1335 }
1336
1337 /**
1338 Get the century RTC address from the ACPI FADT table.
1339
1340 @return The century RTC address or 0 if not found.
1341 **/
1342 UINT8
1343 GetCenturyRtcAddress (
1344 VOID
1345 )
1346 {
1347 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;
1348
1349 Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *)EfiLocateFirstAcpiTable (
1350 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
1351 );
1352
1353 if ((Fadt != NULL) &&
1354 (Fadt->Century > RTC_ADDRESS_REGISTER_D) && (Fadt->Century < 0x80)
1355 )
1356 {
1357 return Fadt->Century;
1358 } else {
1359 return 0;
1360 }
1361 }
1362
1363 /**
1364 Notification function of ACPI Table change.
1365
1366 This is a notification function registered on ACPI Table change event.
1367 It saves the Century address stored in ACPI FADT table.
1368
1369 @param Event Event whose notification function is being invoked.
1370 @param Context Pointer to the notification function's context.
1371
1372 **/
1373 VOID
1374 EFIAPI
1375 PcRtcAcpiTableChangeCallback (
1376 IN EFI_EVENT Event,
1377 IN VOID *Context
1378 )
1379 {
1380 EFI_STATUS Status;
1381 EFI_TIME Time;
1382 UINT8 CenturyRtcAddress;
1383 UINT8 Century;
1384
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);
1394 }
1395 }
1396 }