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