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