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