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