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