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