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