]> git.proxmox.com Git - mirror_edk2.git/blob - PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.c
PcAtChipsetPkg: Add MMIO Support to RTC driver
[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 // Initialize RTC Register
221 //
222 // Make sure Division Chain is properly configured,
223 // or RTC clock won't "tick" -- time won't increment
224 //
225 RegisterA.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterA);
226 RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);
227
228 //
229 // Read Register B
230 //
231 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
232
233 //
234 // Clear RTC flag register
235 //
236 RtcRead (RTC_ADDRESS_REGISTER_C);
237
238 //
239 // Clear RTC register D
240 //
241 RegisterD.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterD);
242 RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data);
243
244 //
245 // Wait for up to 0.1 seconds for the RTC to be updated
246 //
247 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
248 if (EFI_ERROR (Status)) {
249 //
250 // Set the variable with default value if the RTC is functioning incorrectly.
251 //
252 Global->SavedTimeZone = EFI_UNSPECIFIED_TIMEZONE;
253 Global->Daylight = 0;
254 if (!EfiAtRuntime ()) {
255 EfiReleaseLock (&Global->RtcLock);
256 }
257 return EFI_DEVICE_ERROR;
258 }
259 //
260 // Get the Time/Date/Daylight Savings values.
261 //
262 Time.Second = RtcRead (RTC_ADDRESS_SECONDS);
263 Time.Minute = RtcRead (RTC_ADDRESS_MINUTES);
264 Time.Hour = RtcRead (RTC_ADDRESS_HOURS);
265 Time.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
266 Time.Month = RtcRead (RTC_ADDRESS_MONTH);
267 Time.Year = RtcRead (RTC_ADDRESS_YEAR);
268
269 //
270 // Set RTC configuration after get original time
271 // The value of bit AIE should be reserved.
272 //
273 RegisterB.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterB) | (RegisterB.Data & BIT5);
274 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
275
276 //
277 // Release RTC Lock.
278 //
279 if (!EfiAtRuntime ()) {
280 EfiReleaseLock (&Global->RtcLock);
281 }
282
283 //
284 // Get the data of Daylight saving and time zone, if they have been
285 // stored in NV variable during previous boot.
286 //
287 DataSize = sizeof (UINT32);
288 Status = EfiGetVariable (
289 mTimeZoneVariableName,
290 &gEfiCallerIdGuid,
291 NULL,
292 &DataSize,
293 &TimerVar
294 );
295 if (!EFI_ERROR (Status)) {
296 Time.TimeZone = (INT16) TimerVar;
297 Time.Daylight = (UINT8) (TimerVar >> 16);
298 } else {
299 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
300 Time.Daylight = 0;
301 }
302
303 //
304 // Validate time fields
305 //
306 Status = ConvertRtcTimeToEfiTime (&Time, RegisterB);
307 if (!EFI_ERROR (Status)) {
308 Status = RtcTimeFieldsValid (&Time);
309 }
310 if (EFI_ERROR (Status)) {
311 //
312 // Report Status Code to indicate that the RTC has bad date and time
313 //
314 REPORT_STATUS_CODE (
315 EFI_ERROR_CODE | EFI_ERROR_MINOR,
316 (EFI_SOFTWARE_DXE_RT_DRIVER | EFI_SW_EC_BAD_DATE_TIME)
317 );
318 Time.Second = RTC_INIT_SECOND;
319 Time.Minute = RTC_INIT_MINUTE;
320 Time.Hour = RTC_INIT_HOUR;
321 Time.Day = RTC_INIT_DAY;
322 Time.Month = RTC_INIT_MONTH;
323 Time.Year = PcdGet16 (PcdMinimalValidYear);
324 Time.Nanosecond = 0;
325 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;
326 Time.Daylight = 0;
327 }
328
329 //
330 // Reset time value according to new RTC configuration
331 //
332 Status = PcRtcSetTime (&Time, Global);
333 if (EFI_ERROR (Status)) {
334 return EFI_DEVICE_ERROR;
335 }
336
337 //
338 // Reset wakeup time value to valid state when wakeup alarm is disabled and wakeup time is invalid.
339 // Global variable has already had valid SavedTimeZone and Daylight,
340 // so we can use them to get and set wakeup time.
341 //
342 Status = PcRtcGetWakeupTime (&Enabled, &Pending, &Time, Global);
343 if ((Enabled) || (!EFI_ERROR (Status))) {
344 return EFI_SUCCESS;
345 }
346
347 //
348 // When wakeup time is disabled and invalid, reset wakeup time register to valid state
349 // but keep wakeup alarm disabled.
350 //
351 Time.Second = RTC_INIT_SECOND;
352 Time.Minute = RTC_INIT_MINUTE;
353 Time.Hour = RTC_INIT_HOUR;
354 Time.Day = RTC_INIT_DAY;
355 Time.Month = RTC_INIT_MONTH;
356 Time.Year = PcdGet16 (PcdMinimalValidYear);
357 Time.Nanosecond = 0;
358 Time.TimeZone = Global->SavedTimeZone;
359 Time.Daylight = Global->Daylight;;
360
361 //
362 // Acquire RTC Lock to make access to RTC atomic
363 //
364 if (!EfiAtRuntime ()) {
365 EfiAcquireLock (&Global->RtcLock);
366 }
367 //
368 // Wait for up to 0.1 seconds for the RTC to be updated
369 //
370 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
371 if (EFI_ERROR (Status)) {
372 if (!EfiAtRuntime ()) {
373 EfiReleaseLock (&Global->RtcLock);
374 }
375 return EFI_DEVICE_ERROR;
376 }
377
378 ConvertEfiTimeToRtcTime (&Time, RegisterB);
379
380 //
381 // Set the Y/M/D info to variable as it has no corresponding hw registers.
382 //
383 Status = EfiSetVariable (
384 L"RTCALARM",
385 &gEfiCallerIdGuid,
386 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
387 sizeof (Time),
388 &Time
389 );
390 if (EFI_ERROR (Status)) {
391 if (!EfiAtRuntime ()) {
392 EfiReleaseLock (&Global->RtcLock);
393 }
394 return EFI_DEVICE_ERROR;
395 }
396
397 //
398 // Inhibit updates of the RTC
399 //
400 RegisterB.Bits.Set = 1;
401 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
402
403 //
404 // Set RTC alarm time registers
405 //
406 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, Time.Second);
407 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, Time.Minute);
408 RtcWrite (RTC_ADDRESS_HOURS_ALARM, Time.Hour);
409
410 //
411 // Allow updates of the RTC registers
412 //
413 RegisterB.Bits.Set = 0;
414 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
415
416 //
417 // Release RTC Lock.
418 //
419 if (!EfiAtRuntime ()) {
420 EfiReleaseLock (&Global->RtcLock);
421 }
422 return EFI_SUCCESS;
423 }
424
425 /**
426 Returns the current time and date information, and the time-keeping capabilities
427 of the hardware platform.
428
429 @param Time A pointer to storage to receive a snapshot of the current time.
430 @param Capabilities An optional pointer to a buffer to receive the real time clock
431 device's capabilities.
432 @param Global For global use inside this module.
433
434 @retval EFI_SUCCESS The operation completed successfully.
435 @retval EFI_INVALID_PARAMETER Time is NULL.
436 @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.
437
438 **/
439 EFI_STATUS
440 PcRtcGetTime (
441 OUT EFI_TIME *Time,
442 OUT EFI_TIME_CAPABILITIES *Capabilities, OPTIONAL
443 IN PC_RTC_MODULE_GLOBALS *Global
444 )
445 {
446 EFI_STATUS Status;
447 RTC_REGISTER_B RegisterB;
448
449 //
450 // Check parameters for null pointer
451 //
452 if (Time == NULL) {
453 return EFI_INVALID_PARAMETER;
454
455 }
456 //
457 // Acquire RTC Lock to make access to RTC atomic
458 //
459 if (!EfiAtRuntime ()) {
460 EfiAcquireLock (&Global->RtcLock);
461 }
462 //
463 // Wait for up to 0.1 seconds for the RTC to be updated
464 //
465 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
466 if (EFI_ERROR (Status)) {
467 if (!EfiAtRuntime ()) {
468 EfiReleaseLock (&Global->RtcLock);
469 }
470 return Status;
471 }
472 //
473 // Read Register B
474 //
475 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
476
477 //
478 // Get the Time/Date/Daylight Savings values.
479 //
480 Time->Second = RtcRead (RTC_ADDRESS_SECONDS);
481 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES);
482 Time->Hour = RtcRead (RTC_ADDRESS_HOURS);
483 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
484 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
485 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
486
487 //
488 // Release RTC Lock.
489 //
490 if (!EfiAtRuntime ()) {
491 EfiReleaseLock (&Global->RtcLock);
492 }
493
494 //
495 // Get the variable that contains the TimeZone and Daylight fields
496 //
497 Time->TimeZone = Global->SavedTimeZone;
498 Time->Daylight = Global->Daylight;
499
500 //
501 // Make sure all field values are in correct range
502 //
503 Status = ConvertRtcTimeToEfiTime (Time, RegisterB);
504 if (!EFI_ERROR (Status)) {
505 Status = RtcTimeFieldsValid (Time);
506 }
507 if (EFI_ERROR (Status)) {
508 return EFI_DEVICE_ERROR;
509 }
510
511 //
512 // Fill in Capabilities if it was passed in
513 //
514 if (Capabilities != NULL) {
515 Capabilities->Resolution = 1;
516 //
517 // 1 hertz
518 //
519 Capabilities->Accuracy = 50000000;
520 //
521 // 50 ppm
522 //
523 Capabilities->SetsToZero = FALSE;
524 }
525
526 return EFI_SUCCESS;
527 }
528
529 /**
530 Sets the current local time and date information.
531
532 @param Time A pointer to the current time.
533 @param Global For global use inside this module.
534
535 @retval EFI_SUCCESS The operation completed successfully.
536 @retval EFI_INVALID_PARAMETER A time field is out of range.
537 @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.
538
539 **/
540 EFI_STATUS
541 PcRtcSetTime (
542 IN EFI_TIME *Time,
543 IN PC_RTC_MODULE_GLOBALS *Global
544 )
545 {
546 EFI_STATUS Status;
547 EFI_TIME RtcTime;
548 RTC_REGISTER_B RegisterB;
549 UINT32 TimerVar;
550
551 if (Time == NULL) {
552 return EFI_INVALID_PARAMETER;
553 }
554 //
555 // Make sure that the time fields are valid
556 //
557 Status = RtcTimeFieldsValid (Time);
558 if (EFI_ERROR (Status)) {
559 return Status;
560 }
561
562 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
563
564 //
565 // Acquire RTC Lock to make access to RTC atomic
566 //
567 if (!EfiAtRuntime ()) {
568 EfiAcquireLock (&Global->RtcLock);
569 }
570 //
571 // Wait for up to 0.1 seconds for the RTC to be updated
572 //
573 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
574 if (EFI_ERROR (Status)) {
575 if (!EfiAtRuntime ()) {
576 EfiReleaseLock (&Global->RtcLock);
577 }
578 return Status;
579 }
580
581 //
582 // Write timezone and daylight to RTC variable
583 //
584 if ((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) && (Time->Daylight == 0)) {
585 Status = EfiSetVariable (
586 mTimeZoneVariableName,
587 &gEfiCallerIdGuid,
588 0,
589 0,
590 NULL
591 );
592 if (Status == EFI_NOT_FOUND) {
593 Status = EFI_SUCCESS;
594 }
595 } else {
596 TimerVar = Time->Daylight;
597 TimerVar = (UINT32) ((TimerVar << 16) | (UINT16)(Time->TimeZone));
598 Status = EfiSetVariable (
599 mTimeZoneVariableName,
600 &gEfiCallerIdGuid,
601 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
602 sizeof (TimerVar),
603 &TimerVar
604 );
605 }
606
607 if (EFI_ERROR (Status)) {
608 if (!EfiAtRuntime ()) {
609 EfiReleaseLock (&Global->RtcLock);
610 }
611 return EFI_DEVICE_ERROR;
612 }
613
614 //
615 // Read Register B, and inhibit updates of the RTC
616 //
617 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
618 RegisterB.Bits.Set = 1;
619 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
620
621 //
622 // Store the century value to RTC before converting to BCD format.
623 //
624 if (Global->CenturyRtcAddress != 0) {
625 RtcWrite (Global->CenturyRtcAddress, DecimalToBcd8 ((UINT8) (RtcTime.Year / 100)));
626 }
627
628 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);
629
630 RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second);
631 RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute);
632 RtcWrite (RTC_ADDRESS_HOURS, RtcTime.Hour);
633 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH, RtcTime.Day);
634 RtcWrite (RTC_ADDRESS_MONTH, RtcTime.Month);
635 RtcWrite (RTC_ADDRESS_YEAR, (UINT8) RtcTime.Year);
636
637 //
638 // Allow updates of the RTC registers
639 //
640 RegisterB.Bits.Set = 0;
641 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
642
643 //
644 // Release RTC Lock.
645 //
646 if (!EfiAtRuntime ()) {
647 EfiReleaseLock (&Global->RtcLock);
648 }
649 //
650 // Set the variable that contains the TimeZone and Daylight fields
651 //
652 Global->SavedTimeZone = Time->TimeZone;
653 Global->Daylight = Time->Daylight;
654
655 return EFI_SUCCESS;
656 }
657
658 /**
659 Returns the current wakeup alarm clock setting.
660
661 @param Enabled Indicates if the alarm is currently enabled or disabled.
662 @param Pending Indicates if the alarm signal is pending and requires acknowledgment.
663 @param Time The current alarm setting.
664 @param Global For global use inside this module.
665
666 @retval EFI_SUCCESS The alarm settings were returned.
667 @retval EFI_INVALID_PARAMETER Enabled is NULL.
668 @retval EFI_INVALID_PARAMETER Pending is NULL.
669 @retval EFI_INVALID_PARAMETER Time is NULL.
670 @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.
671 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
672
673 **/
674 EFI_STATUS
675 PcRtcGetWakeupTime (
676 OUT BOOLEAN *Enabled,
677 OUT BOOLEAN *Pending,
678 OUT EFI_TIME *Time,
679 IN PC_RTC_MODULE_GLOBALS *Global
680 )
681 {
682 EFI_STATUS Status;
683 RTC_REGISTER_B RegisterB;
684 RTC_REGISTER_C RegisterC;
685 EFI_TIME RtcTime;
686 UINTN DataSize;
687
688 //
689 // Check parameters for null pointers
690 //
691 if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) {
692 return EFI_INVALID_PARAMETER;
693
694 }
695 //
696 // Acquire RTC Lock to make access to RTC atomic
697 //
698 if (!EfiAtRuntime ()) {
699 EfiAcquireLock (&Global->RtcLock);
700 }
701 //
702 // Wait for up to 0.1 seconds for the RTC to be updated
703 //
704 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
705 if (EFI_ERROR (Status)) {
706 if (!EfiAtRuntime ()) {
707 EfiReleaseLock (&Global->RtcLock);
708 }
709 return EFI_DEVICE_ERROR;
710 }
711 //
712 // Read Register B and Register C
713 //
714 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
715 RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);
716
717 //
718 // Get the Time/Date/Daylight Savings values.
719 //
720 *Enabled = RegisterB.Bits.Aie;
721 *Pending = RegisterC.Bits.Af;
722
723 Time->Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);
724 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);
725 Time->Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);
726 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
727 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
728 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
729 Time->TimeZone = Global->SavedTimeZone;
730 Time->Daylight = Global->Daylight;
731
732 //
733 // Get the alarm info from variable
734 //
735 DataSize = sizeof (EFI_TIME);
736 Status = EfiGetVariable (
737 L"RTCALARM",
738 &gEfiCallerIdGuid,
739 NULL,
740 &DataSize,
741 &RtcTime
742 );
743 if (!EFI_ERROR (Status)) {
744 //
745 // The alarm variable exists. In this case, we read variable to get info.
746 //
747 Time->Day = RtcTime.Day;
748 Time->Month = RtcTime.Month;
749 Time->Year = RtcTime.Year;
750 }
751
752 //
753 // Release RTC Lock.
754 //
755 if (!EfiAtRuntime ()) {
756 EfiReleaseLock (&Global->RtcLock);
757 }
758
759 //
760 // Make sure all field values are in correct range
761 //
762 Status = ConvertRtcTimeToEfiTime (Time, RegisterB);
763 if (!EFI_ERROR (Status)) {
764 Status = RtcTimeFieldsValid (Time);
765 }
766 if (EFI_ERROR (Status)) {
767 return EFI_DEVICE_ERROR;
768 }
769
770 return EFI_SUCCESS;
771 }
772
773 /**
774 Sets the system wakeup alarm clock time.
775
776 @param Enabled Enable or disable the wakeup alarm.
777 @param Time If Enable is TRUE, the time to set the wakeup alarm for.
778 If Enable is FALSE, then this parameter is optional, and may be NULL.
779 @param Global For global use inside this module.
780
781 @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled.
782 If Enable is FALSE, then the wakeup alarm was disabled.
783 @retval EFI_INVALID_PARAMETER A time field is out of range.
784 @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.
785 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
786
787 **/
788 EFI_STATUS
789 PcRtcSetWakeupTime (
790 IN BOOLEAN Enable,
791 IN EFI_TIME *Time, OPTIONAL
792 IN PC_RTC_MODULE_GLOBALS *Global
793 )
794 {
795 EFI_STATUS Status;
796 EFI_TIME RtcTime;
797 RTC_REGISTER_B RegisterB;
798 EFI_TIME_CAPABILITIES Capabilities;
799
800 ZeroMem (&RtcTime, sizeof (RtcTime));
801
802 if (Enable) {
803
804 if (Time == NULL) {
805 return EFI_INVALID_PARAMETER;
806 }
807 //
808 // Make sure that the time fields are valid
809 //
810 Status = RtcTimeFieldsValid (Time);
811 if (EFI_ERROR (Status)) {
812 return EFI_INVALID_PARAMETER;
813 }
814 //
815 // Just support set alarm time within 24 hours
816 //
817 PcRtcGetTime (&RtcTime, &Capabilities, Global);
818 Status = RtcTimeFieldsValid (&RtcTime);
819 if (EFI_ERROR (Status)) {
820 return EFI_DEVICE_ERROR;
821 }
822 if (!IsWithinOneDay (&RtcTime, Time)) {
823 return EFI_UNSUPPORTED;
824 }
825 //
826 // Make a local copy of the time and date
827 //
828 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
829
830 }
831 //
832 // Acquire RTC Lock to make access to RTC atomic
833 //
834 if (!EfiAtRuntime ()) {
835 EfiAcquireLock (&Global->RtcLock);
836 }
837 //
838 // Wait for up to 0.1 seconds for the RTC to be updated
839 //
840 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
841 if (EFI_ERROR (Status)) {
842 if (!EfiAtRuntime ()) {
843 EfiReleaseLock (&Global->RtcLock);
844 }
845 return EFI_DEVICE_ERROR;
846 }
847 //
848 // Read Register B
849 //
850 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
851
852 if (Enable) {
853 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);
854 } else {
855 //
856 // if the alarm is disable, record the current setting.
857 //
858 RtcTime.Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);
859 RtcTime.Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);
860 RtcTime.Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);
861 RtcTime.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
862 RtcTime.Month = RtcRead (RTC_ADDRESS_MONTH);
863 RtcTime.Year = RtcRead (RTC_ADDRESS_YEAR);
864 RtcTime.TimeZone = Global->SavedTimeZone;
865 RtcTime.Daylight = Global->Daylight;
866 }
867
868 //
869 // Set the Y/M/D info to variable as it has no corresponding hw registers.
870 //
871 Status = EfiSetVariable (
872 L"RTCALARM",
873 &gEfiCallerIdGuid,
874 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
875 sizeof (RtcTime),
876 &RtcTime
877 );
878 if (EFI_ERROR (Status)) {
879 if (!EfiAtRuntime ()) {
880 EfiReleaseLock (&Global->RtcLock);
881 }
882 return EFI_DEVICE_ERROR;
883 }
884
885 //
886 // Inhibit updates of the RTC
887 //
888 RegisterB.Bits.Set = 1;
889 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
890
891 if (Enable) {
892 //
893 // Set RTC alarm time
894 //
895 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);
896 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);
897 RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);
898
899 RegisterB.Bits.Aie = 1;
900
901 } else {
902 RegisterB.Bits.Aie = 0;
903 }
904 //
905 // Allow updates of the RTC registers
906 //
907 RegisterB.Bits.Set = 0;
908 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
909
910 //
911 // Release RTC Lock.
912 //
913 if (!EfiAtRuntime ()) {
914 EfiReleaseLock (&Global->RtcLock);
915 }
916 return EFI_SUCCESS;
917 }
918
919
920 /**
921 Checks an 8-bit BCD value, and converts to an 8-bit value if valid.
922
923 This function checks the 8-bit BCD value specified by Value.
924 If valid, the function converts it to an 8-bit value and returns it.
925 Otherwise, return 0xff.
926
927 @param Value The 8-bit BCD value to check and convert
928
929 @return The 8-bit value converted. Or 0xff if Value is invalid.
930
931 **/
932 UINT8
933 CheckAndConvertBcd8ToDecimal8 (
934 IN UINT8 Value
935 )
936 {
937 if ((Value < 0xa0) && ((Value & 0xf) < 0xa)) {
938 return BcdToDecimal8 (Value);
939 }
940
941 return 0xff;
942 }
943
944 /**
945 Converts time read from RTC to EFI_TIME format defined by UEFI spec.
946
947 This function converts raw time data read from RTC to the EFI_TIME format
948 defined by UEFI spec.
949 If data mode of RTC is BCD, then converts it to decimal,
950 If RTC is in 12-hour format, then converts it to 24-hour format.
951
952 @param Time On input, the time data read from RTC to convert
953 On output, the time converted to UEFI format
954 @param RegisterB Value of Register B of RTC, indicating data mode
955 and hour format.
956
957 @retval EFI_INVALID_PARAMETER Parameters passed in are invalid.
958 @retval EFI_SUCCESS Convert RTC time to EFI time successfully.
959
960 **/
961 EFI_STATUS
962 ConvertRtcTimeToEfiTime (
963 IN OUT EFI_TIME *Time,
964 IN RTC_REGISTER_B RegisterB
965 )
966 {
967 BOOLEAN IsPM;
968 UINT8 Century;
969
970 if ((Time->Hour & 0x80) != 0) {
971 IsPM = TRUE;
972 } else {
973 IsPM = FALSE;
974 }
975
976 Time->Hour = (UINT8) (Time->Hour & 0x7f);
977
978 if (RegisterB.Bits.Dm == 0) {
979 Time->Year = CheckAndConvertBcd8ToDecimal8 ((UINT8) Time->Year);
980 Time->Month = CheckAndConvertBcd8ToDecimal8 (Time->Month);
981 Time->Day = CheckAndConvertBcd8ToDecimal8 (Time->Day);
982 Time->Hour = CheckAndConvertBcd8ToDecimal8 (Time->Hour);
983 Time->Minute = CheckAndConvertBcd8ToDecimal8 (Time->Minute);
984 Time->Second = CheckAndConvertBcd8ToDecimal8 (Time->Second);
985 }
986
987 if (Time->Year == 0xff || Time->Month == 0xff || Time->Day == 0xff ||
988 Time->Hour == 0xff || Time->Minute == 0xff || Time->Second == 0xff) {
989 return EFI_INVALID_PARAMETER;
990 }
991
992 //
993 // For minimal/maximum year range [1970, 2069],
994 // Century is 19 if RTC year >= 70,
995 // Century is 20 otherwise.
996 //
997 Century = (UINT8) (PcdGet16 (PcdMinimalValidYear) / 100);
998 if (Time->Year < PcdGet16 (PcdMinimalValidYear) % 100) {
999 Century++;
1000 }
1001 Time->Year = (UINT16) (Century * 100 + Time->Year);
1002
1003 //
1004 // If time is in 12 hour format, convert it to 24 hour format
1005 //
1006 if (RegisterB.Bits.Mil == 0) {
1007 if (IsPM && Time->Hour < 12) {
1008 Time->Hour = (UINT8) (Time->Hour + 12);
1009 }
1010
1011 if (!IsPM && Time->Hour == 12) {
1012 Time->Hour = 0;
1013 }
1014 }
1015
1016 Time->Nanosecond = 0;
1017
1018 return EFI_SUCCESS;
1019 }
1020
1021 /**
1022 Wait for a period for the RTC to be ready.
1023
1024 @param Timeout Tell how long it should take to wait.
1025
1026 @retval EFI_DEVICE_ERROR RTC device error.
1027 @retval EFI_SUCCESS RTC is updated and ready.
1028 **/
1029 EFI_STATUS
1030 RtcWaitToUpdate (
1031 UINTN Timeout
1032 )
1033 {
1034 RTC_REGISTER_A RegisterA;
1035 RTC_REGISTER_D RegisterD;
1036
1037 //
1038 // See if the RTC is functioning correctly
1039 //
1040 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
1041
1042 if (RegisterD.Bits.Vrt == 0) {
1043 return EFI_DEVICE_ERROR;
1044 }
1045 //
1046 // Wait for up to 0.1 seconds for the RTC to be ready.
1047 //
1048 Timeout = (Timeout / 10) + 1;
1049 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
1050 while (RegisterA.Bits.Uip == 1 && Timeout > 0) {
1051 MicroSecondDelay (10);
1052 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
1053 Timeout--;
1054 }
1055
1056 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
1057 if (Timeout == 0 || RegisterD.Bits.Vrt == 0) {
1058 return EFI_DEVICE_ERROR;
1059 }
1060
1061 return EFI_SUCCESS;
1062 }
1063
1064 /**
1065 See if all fields of a variable of EFI_TIME type is correct.
1066
1067 @param Time The time to be checked.
1068
1069 @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.
1070 @retval EFI_SUCCESS Time is a valid EFI_TIME variable.
1071
1072 **/
1073 EFI_STATUS
1074 RtcTimeFieldsValid (
1075 IN EFI_TIME *Time
1076 )
1077 {
1078 if (Time->Year < PcdGet16 (PcdMinimalValidYear) ||
1079 Time->Year > PcdGet16 (PcdMaximalValidYear) ||
1080 Time->Month < 1 ||
1081 Time->Month > 12 ||
1082 (!DayValid (Time)) ||
1083 Time->Hour > 23 ||
1084 Time->Minute > 59 ||
1085 Time->Second > 59 ||
1086 Time->Nanosecond > 999999999 ||
1087 (!(Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE || (Time->TimeZone >= -1440 && Time->TimeZone <= 1440))) ||
1088 ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0)) {
1089 return EFI_INVALID_PARAMETER;
1090 }
1091
1092 return EFI_SUCCESS;
1093 }
1094
1095 /**
1096 See if field Day of an EFI_TIME is correct.
1097
1098 @param Time Its Day field is to be checked.
1099
1100 @retval TRUE Day field of Time is correct.
1101 @retval FALSE Day field of Time is NOT correct.
1102 **/
1103 BOOLEAN
1104 DayValid (
1105 IN EFI_TIME *Time
1106 )
1107 {
1108 //
1109 // The validity of Time->Month field should be checked before
1110 //
1111 ASSERT (Time->Month >=1);
1112 ASSERT (Time->Month <=12);
1113 if (Time->Day < 1 ||
1114 Time->Day > mDayOfMonth[Time->Month - 1] ||
1115 (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28))
1116 ) {
1117 return FALSE;
1118 }
1119
1120 return TRUE;
1121 }
1122
1123 /**
1124 Check if it is a leap year.
1125
1126 @param Time The time to be checked.
1127
1128 @retval TRUE It is a leap year.
1129 @retval FALSE It is NOT a leap year.
1130 **/
1131 BOOLEAN
1132 IsLeapYear (
1133 IN EFI_TIME *Time
1134 )
1135 {
1136 if (Time->Year % 4 == 0) {
1137 if (Time->Year % 100 == 0) {
1138 if (Time->Year % 400 == 0) {
1139 return TRUE;
1140 } else {
1141 return FALSE;
1142 }
1143 } else {
1144 return TRUE;
1145 }
1146 } else {
1147 return FALSE;
1148 }
1149 }
1150
1151 /**
1152 Converts time from EFI_TIME format defined by UEFI spec to RTC format.
1153
1154 This function converts time from EFI_TIME format defined by UEFI spec to RTC format.
1155 If data mode of RTC is BCD, then converts EFI_TIME to it.
1156 If RTC is in 12-hour format, then converts EFI_TIME to it.
1157
1158 @param Time On input, the time data read from UEFI to convert
1159 On output, the time converted to RTC format
1160 @param RegisterB Value of Register B of RTC, indicating data mode
1161 **/
1162 VOID
1163 ConvertEfiTimeToRtcTime (
1164 IN OUT EFI_TIME *Time,
1165 IN RTC_REGISTER_B RegisterB
1166 )
1167 {
1168 BOOLEAN IsPM;
1169
1170 IsPM = TRUE;
1171 //
1172 // Adjust hour field if RTC is in 12 hour mode
1173 //
1174 if (RegisterB.Bits.Mil == 0) {
1175 if (Time->Hour < 12) {
1176 IsPM = FALSE;
1177 }
1178
1179 if (Time->Hour >= 13) {
1180 Time->Hour = (UINT8) (Time->Hour - 12);
1181 } else if (Time->Hour == 0) {
1182 Time->Hour = 12;
1183 }
1184 }
1185 //
1186 // Set the Time/Date values.
1187 //
1188 Time->Year = (UINT16) (Time->Year % 100);
1189
1190 if (RegisterB.Bits.Dm == 0) {
1191 Time->Year = DecimalToBcd8 ((UINT8) Time->Year);
1192 Time->Month = DecimalToBcd8 (Time->Month);
1193 Time->Day = DecimalToBcd8 (Time->Day);
1194 Time->Hour = DecimalToBcd8 (Time->Hour);
1195 Time->Minute = DecimalToBcd8 (Time->Minute);
1196 Time->Second = DecimalToBcd8 (Time->Second);
1197 }
1198 //
1199 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
1200 //
1201 if (RegisterB.Bits.Mil == 0 && IsPM) {
1202 Time->Hour = (UINT8) (Time->Hour | 0x80);
1203 }
1204 }
1205
1206 /**
1207 Compare the Hour, Minute and Second of the From time and the To time.
1208
1209 Only compare H/M/S in EFI_TIME and ignore other fields here.
1210
1211 @param From the first time
1212 @param To the second time
1213
1214 @return >0 The H/M/S of the From time is later than those of To time
1215 @return ==0 The H/M/S of the From time is same as those of To time
1216 @return <0 The H/M/S of the From time is earlier than those of To time
1217 **/
1218 INTN
1219 CompareHMS (
1220 IN EFI_TIME *From,
1221 IN EFI_TIME *To
1222 )
1223 {
1224 if ((From->Hour > To->Hour) ||
1225 ((From->Hour == To->Hour) && (From->Minute > To->Minute)) ||
1226 ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second))) {
1227 return 1;
1228 } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) {
1229 return 0;
1230 } else {
1231 return -1;
1232 }
1233 }
1234
1235 /**
1236 To check if second date is later than first date within 24 hours.
1237
1238 @param From the first date
1239 @param To the second date
1240
1241 @retval TRUE From is previous to To within 24 hours.
1242 @retval FALSE From is later, or it is previous to To more than 24 hours.
1243 **/
1244 BOOLEAN
1245 IsWithinOneDay (
1246 IN EFI_TIME *From,
1247 IN EFI_TIME *To
1248 )
1249 {
1250 BOOLEAN Adjacent;
1251
1252 Adjacent = FALSE;
1253
1254 //
1255 // The validity of From->Month field should be checked before
1256 //
1257 ASSERT (From->Month >=1);
1258 ASSERT (From->Month <=12);
1259
1260 if (From->Year == To->Year) {
1261 if (From->Month == To->Month) {
1262 if ((From->Day + 1) == To->Day) {
1263 if ((CompareHMS(From, To) >= 0)) {
1264 Adjacent = TRUE;
1265 }
1266 } else if (From->Day == To->Day) {
1267 if ((CompareHMS(From, To) <= 0)) {
1268 Adjacent = TRUE;
1269 }
1270 }
1271 } else if (((From->Month + 1) == To->Month) && (To->Day == 1)) {
1272 if ((From->Month == 2) && !IsLeapYear(From)) {
1273 if (From->Day == 28) {
1274 if ((CompareHMS(From, To) >= 0)) {
1275 Adjacent = TRUE;
1276 }
1277 }
1278 } else if (From->Day == mDayOfMonth[From->Month - 1]) {
1279 if ((CompareHMS(From, To) >= 0)) {
1280 Adjacent = TRUE;
1281 }
1282 }
1283 }
1284 } else if (((From->Year + 1) == To->Year) &&
1285 (From->Month == 12) &&
1286 (From->Day == 31) &&
1287 (To->Month == 1) &&
1288 (To->Day == 1)) {
1289 if ((CompareHMS(From, To) >= 0)) {
1290 Adjacent = TRUE;
1291 }
1292 }
1293
1294 return Adjacent;
1295 }
1296
1297 /**
1298 Get the century RTC address from the ACPI FADT table.
1299
1300 @return The century RTC address or 0 if not found.
1301 **/
1302 UINT8
1303 GetCenturyRtcAddress (
1304 VOID
1305 )
1306 {
1307 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;
1308
1309 Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *) EfiLocateFirstAcpiTable (
1310 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
1311 );
1312
1313 if ((Fadt != NULL) &&
1314 (Fadt->Century > RTC_ADDRESS_REGISTER_D) && (Fadt->Century < 0x80)
1315 ) {
1316 return Fadt->Century;
1317 } else {
1318 return 0;
1319 }
1320 }
1321
1322 /**
1323 Notification function of ACPI Table change.
1324
1325 This is a notification function registered on ACPI Table change event.
1326 It saves the Century address stored in ACPI FADT table.
1327
1328 @param Event Event whose notification function is being invoked.
1329 @param Context Pointer to the notification function's context.
1330
1331 **/
1332 VOID
1333 EFIAPI
1334 PcRtcAcpiTableChangeCallback (
1335 IN EFI_EVENT Event,
1336 IN VOID *Context
1337 )
1338 {
1339 EFI_STATUS Status;
1340 EFI_TIME Time;
1341 UINT8 CenturyRtcAddress;
1342 UINT8 Century;
1343
1344 CenturyRtcAddress = GetCenturyRtcAddress ();
1345 if ((CenturyRtcAddress != 0) && (mModuleGlobal.CenturyRtcAddress != CenturyRtcAddress)) {
1346 mModuleGlobal.CenturyRtcAddress = CenturyRtcAddress;
1347 Status = PcRtcGetTime (&Time, NULL, &mModuleGlobal);
1348 if (!EFI_ERROR (Status)) {
1349 Century = (UINT8) (Time.Year / 100);
1350 Century = DecimalToBcd8 (Century);
1351 DEBUG ((EFI_D_INFO, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century, mModuleGlobal.CenturyRtcAddress));
1352 RtcWrite (mModuleGlobal.CenturyRtcAddress, Century);
1353 }
1354 }
1355 }