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