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