]> git.proxmox.com Git - mirror_edk2.git/blob - PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.c
77ac78d67a804b47461e09398a87f4ab5c98f322
[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 - 2010, 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 EFIAPI
238 PcRtcGetTime (
239 OUT EFI_TIME *Time,
240 OUT EFI_TIME_CAPABILITIES *Capabilities, OPTIONAL
241 IN PC_RTC_MODULE_GLOBALS *Global
242 )
243 {
244 EFI_STATUS Status;
245 RTC_REGISTER_B RegisterB;
246 UINT8 Century;
247
248 //
249 // Check parameters for null pointer
250 //
251 if (Time == NULL) {
252 return EFI_INVALID_PARAMETER;
253
254 }
255 //
256 // Acquire RTC Lock to make access to RTC atomic
257 //
258 if (!EfiAtRuntime ()) {
259 EfiAcquireLock (&Global->RtcLock);
260 }
261 //
262 // Wait for up to 0.1 seconds for the RTC to be updated
263 //
264 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
265 if (EFI_ERROR (Status)) {
266 if (!EfiAtRuntime ()) {
267 EfiReleaseLock (&Global->RtcLock);
268 }
269 return Status;
270 }
271 //
272 // Read Register B
273 //
274 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
275
276 //
277 // Get the Time/Date/Daylight Savings values.
278 //
279 Time->Second = RtcRead (RTC_ADDRESS_SECONDS);
280 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES);
281 Time->Hour = RtcRead (RTC_ADDRESS_HOURS);
282 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
283 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
284 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
285
286 Century = RtcRead (RTC_ADDRESS_CENTURY);
287
288 //
289 // Release RTC Lock.
290 //
291 if (!EfiAtRuntime ()) {
292 EfiReleaseLock (&Global->RtcLock);
293 }
294
295 //
296 // Get the variable that contains the TimeZone and Daylight fields
297 //
298 Time->TimeZone = Global->SavedTimeZone;
299 Time->Daylight = Global->Daylight;
300
301 //
302 // Make sure all field values are in correct range
303 //
304 Status = ConvertRtcTimeToEfiTime (Time, Century, RegisterB);
305 if (!EFI_ERROR (Status)) {
306 Status = RtcTimeFieldsValid (Time);
307 }
308 if (EFI_ERROR (Status)) {
309 return EFI_DEVICE_ERROR;
310 }
311
312 //
313 // Fill in Capabilities if it was passed in
314 //
315 if (Capabilities != NULL) {
316 Capabilities->Resolution = 1;
317 //
318 // 1 hertz
319 //
320 Capabilities->Accuracy = 50000000;
321 //
322 // 50 ppm
323 //
324 Capabilities->SetsToZero = FALSE;
325 }
326
327 return EFI_SUCCESS;
328 }
329
330 /**
331 Sets the current local time and date information.
332
333 @param Time A pointer to the current time.
334 @param Global For global use inside this module.
335
336 @retval EFI_SUCCESS The operation completed successfully.
337 @retval EFI_INVALID_PARAMETER A time field is out of range.
338 @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.
339
340 **/
341 EFI_STATUS
342 EFIAPI
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) | 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 EFIAPI
566 PcRtcSetWakeupTime (
567 IN BOOLEAN Enable,
568 IN EFI_TIME *Time, OPTIONAL
569 IN PC_RTC_MODULE_GLOBALS *Global
570 )
571 {
572 EFI_STATUS Status;
573 EFI_TIME RtcTime;
574 RTC_REGISTER_B RegisterB;
575 UINT8 Century;
576 EFI_TIME_CAPABILITIES Capabilities;
577
578 ZeroMem (&RtcTime, sizeof (RtcTime));
579
580 if (Enable) {
581
582 if (Time == NULL) {
583 return EFI_INVALID_PARAMETER;
584 }
585 //
586 // Make sure that the time fields are valid
587 //
588 Status = RtcTimeFieldsValid (Time);
589 if (EFI_ERROR (Status)) {
590 return EFI_INVALID_PARAMETER;
591 }
592 //
593 // Just support set alarm time within 24 hours
594 //
595 PcRtcGetTime (&RtcTime, &Capabilities, Global);
596 Status = RtcTimeFieldsValid (&RtcTime);
597 if (EFI_ERROR (Status)) {
598 return EFI_DEVICE_ERROR;
599 }
600 if (!IsWithinOneDay (&RtcTime, Time)) {
601 return EFI_UNSUPPORTED;
602 }
603 //
604 // Make a local copy of the time and date
605 //
606 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
607
608 }
609 //
610 // Acquire RTC Lock to make access to RTC atomic
611 //
612 if (!EfiAtRuntime ()) {
613 EfiAcquireLock (&Global->RtcLock);
614 }
615 //
616 // Wait for up to 0.1 seconds for the RTC to be updated
617 //
618 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
619 if (EFI_ERROR (Status)) {
620 if (!EfiAtRuntime ()) {
621 EfiReleaseLock (&Global->RtcLock);
622 }
623 return EFI_DEVICE_ERROR;
624 }
625 //
626 // Read Register B, and inhibit updates of the RTC
627 //
628 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
629
630 RegisterB.Bits.Set = 1;
631 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
632
633 if (Enable) {
634 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);
635
636 //
637 // Set RTC alarm time
638 //
639 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);
640 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);
641 RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);
642
643 RegisterB.Bits.Aie = 1;
644
645 } else {
646 RegisterB.Bits.Aie = 0;
647 //
648 // if the alarm is disable, record the current setting.
649 //
650 RtcTime.Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);
651 RtcTime.Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);
652 RtcTime.Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);
653 RtcTime.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
654 RtcTime.Month = RtcRead (RTC_ADDRESS_MONTH);
655 RtcTime.Year = RtcRead (RTC_ADDRESS_YEAR);
656 RtcTime.TimeZone = Global->SavedTimeZone;
657 RtcTime.Daylight = Global->Daylight;
658 }
659 //
660 // Allow updates of the RTC registers
661 //
662 RegisterB.Bits.Set = 0;
663 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
664
665 //
666 // Set the Y/M/D info to variable as it has no corresponding hw registers.
667 //
668 Status = EfiSetVariable (
669 L"RTCALARM",
670 &gEfiCallerIdGuid,
671 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
672 sizeof (RtcTime),
673 &RtcTime
674 );
675 if (EFI_ERROR (Status)) {
676 return EFI_DEVICE_ERROR;
677 }
678
679 //
680 // Release RTC Lock.
681 //
682 if (!EfiAtRuntime ()) {
683 EfiReleaseLock (&Global->RtcLock);
684 }
685 return EFI_SUCCESS;
686 }
687
688
689 /**
690 Checks an 8-bit BCD value, and converts to an 8-bit value if valid.
691
692 This function checks the 8-bit BCD value specified by Value.
693 If valid, the function converts it to an 8-bit value and returns it.
694 Otherwise, return 0xff.
695
696 @param Value The 8-bit BCD value to check and convert
697
698 @return The 8-bit value converted. Or 0xff if Value is invalid.
699
700 **/
701 UINT8
702 CheckAndConvertBcd8ToDecimal8 (
703 IN UINT8 Value
704 )
705 {
706 if ((Value < 0xa0) && ((Value & 0xf) < 0xa)) {
707 return BcdToDecimal8 (Value);
708 }
709
710 return 0xff;
711 }
712
713 /**
714 Converts time read from RTC to EFI_TIME format defined by UEFI spec.
715
716 This function converts raw time data read from RTC to the EFI_TIME format
717 defined by UEFI spec.
718 If data mode of RTC is BCD, then converts it to decimal,
719 If RTC is in 12-hour format, then converts it to 24-hour format.
720
721 @param Time On input, the time data read from RTC to convert
722 On output, the time converted to UEFI format
723 @param Century Value of century read from RTC.
724 @param RegisterB Value of Register B of RTC, indicating data mode
725 and hour format.
726
727 @retval EFI_INVALID_PARAMETER Parameters passed in are invalid.
728 @retval EFI_SUCCESS Convert RTC time to EFI time successfully.
729
730 **/
731 EFI_STATUS
732 ConvertRtcTimeToEfiTime (
733 IN OUT EFI_TIME *Time,
734 IN UINT8 Century,
735 IN RTC_REGISTER_B RegisterB
736 )
737 {
738 BOOLEAN IsPM;
739
740 if ((Time->Hour & 0x80) != 0) {
741 IsPM = TRUE;
742 } else {
743 IsPM = FALSE;
744 }
745
746 Time->Hour = (UINT8) (Time->Hour & 0x7f);
747
748 if (RegisterB.Bits.Dm == 0) {
749 Time->Year = CheckAndConvertBcd8ToDecimal8 ((UINT8) Time->Year);
750 Time->Month = CheckAndConvertBcd8ToDecimal8 (Time->Month);
751 Time->Day = CheckAndConvertBcd8ToDecimal8 (Time->Day);
752 Time->Hour = CheckAndConvertBcd8ToDecimal8 (Time->Hour);
753 Time->Minute = CheckAndConvertBcd8ToDecimal8 (Time->Minute);
754 Time->Second = CheckAndConvertBcd8ToDecimal8 (Time->Second);
755 }
756 Century = CheckAndConvertBcd8ToDecimal8 (Century);
757
758 if (Time->Year == 0xff || Time->Month == 0xff || Time->Day == 0xff ||
759 Time->Hour == 0xff || Time->Minute == 0xff || Time->Second == 0xff ||
760 Century == 0xff) {
761 return EFI_INVALID_PARAMETER;
762 }
763
764 Time->Year = (UINT16) (Century * 100 + Time->Year);
765
766 //
767 // If time is in 12 hour format, convert it to 24 hour format
768 //
769 if (RegisterB.Bits.Mil == 0) {
770 if (IsPM && Time->Hour < 12) {
771 Time->Hour = (UINT8) (Time->Hour + 12);
772 }
773
774 if (!IsPM && Time->Hour == 12) {
775 Time->Hour = 0;
776 }
777 }
778
779 Time->Nanosecond = 0;
780
781 return EFI_SUCCESS;
782 }
783
784 /**
785 Wait for a period for the RTC to be ready.
786
787 @param Timeout Tell how long it should take to wait.
788
789 @retval EFI_DEVICE_ERROR RTC device error.
790 @retval EFI_SUCCESS RTC is updated and ready.
791 **/
792 EFI_STATUS
793 RtcWaitToUpdate (
794 UINTN Timeout
795 )
796 {
797 RTC_REGISTER_A RegisterA;
798 RTC_REGISTER_D RegisterD;
799
800 //
801 // See if the RTC is functioning correctly
802 //
803 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
804
805 if (RegisterD.Bits.Vrt == 0) {
806 return EFI_DEVICE_ERROR;
807 }
808 //
809 // Wait for up to 0.1 seconds for the RTC to be ready.
810 //
811 Timeout = (Timeout / 10) + 1;
812 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
813 while (RegisterA.Bits.Uip == 1 && Timeout > 0) {
814 MicroSecondDelay (10);
815 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
816 Timeout--;
817 }
818
819 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
820 if (Timeout == 0 || RegisterD.Bits.Vrt == 0) {
821 return EFI_DEVICE_ERROR;
822 }
823
824 return EFI_SUCCESS;
825 }
826
827 /**
828 See if all fields of a variable of EFI_TIME type is correct.
829
830 @param Time The time to be checked.
831
832 @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.
833 @retval EFI_SUCCESS Time is a valid EFI_TIME variable.
834
835 **/
836 EFI_STATUS
837 RtcTimeFieldsValid (
838 IN EFI_TIME *Time
839 )
840 {
841 if (Time->Year < 1998 ||
842 Time->Year > 2099 ||
843 Time->Month < 1 ||
844 Time->Month > 12 ||
845 (!DayValid (Time)) ||
846 Time->Hour > 23 ||
847 Time->Minute > 59 ||
848 Time->Second > 59 ||
849 Time->Nanosecond > 999999999 ||
850 (!(Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE || (Time->TimeZone >= -1440 && Time->TimeZone <= 1440))) ||
851 ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0)) {
852 return EFI_INVALID_PARAMETER;
853 }
854
855 return EFI_SUCCESS;
856 }
857
858 /**
859 See if field Day of an EFI_TIME is correct.
860
861 @param Time Its Day field is to be checked.
862
863 @retval TRUE Day field of Time is correct.
864 @retval FALSE Day field of Time is NOT correct.
865 **/
866 BOOLEAN
867 DayValid (
868 IN EFI_TIME *Time
869 )
870 {
871 INTN DayOfMonth[12];
872
873 DayOfMonth[0] = 31;
874 DayOfMonth[1] = 29;
875 DayOfMonth[2] = 31;
876 DayOfMonth[3] = 30;
877 DayOfMonth[4] = 31;
878 DayOfMonth[5] = 30;
879 DayOfMonth[6] = 31;
880 DayOfMonth[7] = 31;
881 DayOfMonth[8] = 30;
882 DayOfMonth[9] = 31;
883 DayOfMonth[10] = 30;
884 DayOfMonth[11] = 31;
885
886 //
887 // The validity of Time->Month field should be checked before
888 //
889 ASSERT (Time->Month >=1);
890 ASSERT (Time->Month <=12);
891 if (Time->Day < 1 ||
892 Time->Day > DayOfMonth[Time->Month - 1] ||
893 (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28))
894 ) {
895 return FALSE;
896 }
897
898 return TRUE;
899 }
900
901 /**
902 Check if it is a leap year.
903
904 @param Time The time to be checked.
905
906 @retval TRUE It is a leap year.
907 @retval FALSE It is NOT a leap year.
908 **/
909 BOOLEAN
910 IsLeapYear (
911 IN EFI_TIME *Time
912 )
913 {
914 if (Time->Year % 4 == 0) {
915 if (Time->Year % 100 == 0) {
916 if (Time->Year % 400 == 0) {
917 return TRUE;
918 } else {
919 return FALSE;
920 }
921 } else {
922 return TRUE;
923 }
924 } else {
925 return FALSE;
926 }
927 }
928
929 /**
930 Converts time from EFI_TIME format defined by UEFI spec to RTC's.
931
932 This function converts time from EFI_TIME format defined by UEFI spec to RTC's.
933 If data mode of RTC is BCD, then converts EFI_TIME to it.
934 If RTC is in 12-hour format, then converts EFI_TIME to it.
935
936 @param Time On input, the time data read from UEFI to convert
937 On output, the time converted to RTC format
938 @param RegisterB Value of Register B of RTC, indicating data mode
939 @param Century It is set according to EFI_TIME Time.
940
941 **/
942 VOID
943 ConvertEfiTimeToRtcTime (
944 IN OUT EFI_TIME *Time,
945 IN RTC_REGISTER_B RegisterB,
946 OUT UINT8 *Century
947 )
948 {
949 BOOLEAN IsPM;
950
951 IsPM = TRUE;
952 //
953 // Adjust hour field if RTC is in 12 hour mode
954 //
955 if (RegisterB.Bits.Mil == 0) {
956 if (Time->Hour < 12) {
957 IsPM = FALSE;
958 }
959
960 if (Time->Hour >= 13) {
961 Time->Hour = (UINT8) (Time->Hour - 12);
962 } else if (Time->Hour == 0) {
963 Time->Hour = 12;
964 }
965 }
966 //
967 // Set the Time/Date/Daylight Savings values.
968 //
969 *Century = DecimalToBcd8 ((UINT8) (Time->Year / 100));
970
971 Time->Year = (UINT16) (Time->Year % 100);
972
973 if (RegisterB.Bits.Dm == 0) {
974 Time->Year = DecimalToBcd8 ((UINT8) Time->Year);
975 Time->Month = DecimalToBcd8 (Time->Month);
976 Time->Day = DecimalToBcd8 (Time->Day);
977 Time->Hour = DecimalToBcd8 (Time->Hour);
978 Time->Minute = DecimalToBcd8 (Time->Minute);
979 Time->Second = DecimalToBcd8 (Time->Second);
980 }
981 //
982 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
983 //
984 if (RegisterB.Bits.Mil == 0 && IsPM) {
985 Time->Hour = (UINT8) (Time->Hour | 0x80);
986 }
987 }
988
989 /**
990 Compare the Hour, Minute and Second of the From time and the To time.
991
992 Only compare H/M/S in EFI_TIME and ignore other fields here.
993
994 @param From the first time
995 @param To the second time
996
997 @return >0 The H/M/S of the From time is later than those of To time
998 @return ==0 The H/M/S of the From time is same as those of To time
999 @return <0 The H/M/S of the From time is earlier than those of To time
1000 **/
1001 INTN
1002 CompareHMS (
1003 IN EFI_TIME *From,
1004 IN EFI_TIME *To
1005 )
1006 {
1007 if ((From->Hour > To->Hour) ||
1008 ((From->Hour == To->Hour) && (From->Minute > To->Minute)) ||
1009 ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second))) {
1010 return 1;
1011 } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) {
1012 return 0;
1013 } else {
1014 return -1;
1015 }
1016 }
1017
1018 /**
1019 To check if second date is later than first date within 24 hours.
1020
1021 @param From the first date
1022 @param To the second date
1023
1024 @retval TRUE From is previous to To within 24 hours.
1025 @retval FALSE From is later, or it is previous to To more than 24 hours.
1026 **/
1027 BOOLEAN
1028 IsWithinOneDay (
1029 IN EFI_TIME *From,
1030 IN EFI_TIME *To
1031 )
1032 {
1033 UINT8 DayOfMonth[12];
1034 BOOLEAN Adjacent;
1035
1036 DayOfMonth[0] = 31;
1037 DayOfMonth[1] = 29;
1038 DayOfMonth[2] = 31;
1039 DayOfMonth[3] = 30;
1040 DayOfMonth[4] = 31;
1041 DayOfMonth[5] = 30;
1042 DayOfMonth[6] = 31;
1043 DayOfMonth[7] = 31;
1044 DayOfMonth[8] = 30;
1045 DayOfMonth[9] = 31;
1046 DayOfMonth[10] = 30;
1047 DayOfMonth[11] = 31;
1048
1049 Adjacent = FALSE;
1050
1051 //
1052 // The validity of From->Month field should be checked before
1053 //
1054 ASSERT (From->Month >=1);
1055 ASSERT (From->Month <=12);
1056
1057 if (From->Year == To->Year) {
1058 if (From->Month == To->Month) {
1059 if ((From->Day + 1) == To->Day) {
1060 if ((CompareHMS(From, To) >= 0)) {
1061 Adjacent = TRUE;
1062 }
1063 } else if (From->Day == To->Day) {
1064 if ((CompareHMS(From, To) <= 0)) {
1065 Adjacent = TRUE;
1066 }
1067 }
1068 } else if (((From->Month + 1) == To->Month) && (To->Day == 1)) {
1069 if ((From->Month == 2) && !IsLeapYear(From)) {
1070 if (From->Day == 28) {
1071 if ((CompareHMS(From, To) >= 0)) {
1072 Adjacent = TRUE;
1073 }
1074 }
1075 } else if (From->Day == DayOfMonth[From->Month - 1]) {
1076 if ((CompareHMS(From, To) >= 0)) {
1077 Adjacent = TRUE;
1078 }
1079 }
1080 }
1081 } else if (((From->Year + 1) == To->Year) &&
1082 (From->Month == 12) &&
1083 (From->Day == 31) &&
1084 (To->Month == 1) &&
1085 (To->Day == 1)) {
1086 if ((CompareHMS(From, To) >= 0)) {
1087 Adjacent = TRUE;
1088 }
1089 }
1090
1091 return Adjacent;
1092 }
1093