]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/PcatRealTimeClockRuntimeDxe/PcRtc.c
ccd1c4f2977fa9935f8bcf810809c7cac9c049f0
[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 (PcdGet32 (PcdRealTimeClockUpdateTimeout));
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 if (RtcTestCenturyRegister () == EFI_SUCCESS) {
169 Century = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f);
170 } else {
171 Century = RtcRead (RTC_ADDRESS_CENTURY);
172 }
173
174 //
175 // Set RTC configuration after get original time
176 // The value of bit AIE should be reserved.
177 //
178 RtcWrite (RTC_ADDRESS_REGISTER_B, (UINT8)(RTC_INIT_REGISTER_B | (RegisterB.Data & BIT5)));
179
180 //
181 // Release RTC Lock.
182 //
183 //BugBug: the EfiAtRuntime should be encapsulated in EfiAcquireLock or
184 // provide a new instance for EfiAcquireLock, say, RtEfiAcquireLock
185 //
186 if (!EfiAtRuntime ()) {
187 EfiReleaseLock (&Global->RtcLock);
188 }
189
190 //
191 // Validate time fields
192 //
193 Status = ConvertRtcTimeToEfiTime (&Time, Century, RegisterB);
194 if (!EFI_ERROR (Status)) {
195 Status = RtcTimeFieldsValid (&Time);
196 }
197 if (EFI_ERROR (Status)) {
198 Time.Second = RTC_INIT_SECOND;
199 Time.Minute = RTC_INIT_MINUTE;
200 Time.Hour = RTC_INIT_HOUR;
201 Time.Day = RTC_INIT_DAY;
202 Time.Month = RTC_INIT_MONTH;
203 Time.Year = RTC_INIT_YEAR;
204 }
205 //
206 // Get the data of Daylight saving and time zone, if they have been
207 // stored in NV variable during previous boot.
208 //
209 DataSize = sizeof (UINT32);
210 Status = EfiGetVariable (
211 L"TimerVar",
212 &gEfiGenericPlatformVariableGuid,
213 NULL,
214 &DataSize,
215 (VOID *) &TimerVar
216 );
217 if (!EFI_ERROR (Status)) {
218 Global->SavedTimeZone = (INT16) TimerVar;
219 Global->Daylight = (UINT8) (TimerVar >> 16);
220
221 Time.TimeZone = Global->SavedTimeZone;
222 Time.Daylight = Global->Daylight;
223 }
224 //
225 // Reset time value according to new RTC configuration
226 //
227 PcRtcSetTime (&Time, Global);
228
229 return EFI_SUCCESS;
230 }
231
232 EFI_STATUS
233 PcRtcGetTime (
234 OUT EFI_TIME *Time,
235 IN EFI_TIME_CAPABILITIES *Capabilities,
236 IN PC_RTC_MODULE_GLOBALS *Global
237 )
238 /*++
239
240 Routine Description:
241
242 Arguments:
243
244 Returns:
245 --*/
246 // GC_TODO: Time - add argument and description to function comment
247 // GC_TODO: Capabilities - add argument and description to function comment
248 // GC_TODO: Global - add argument and description to function comment
249 // GC_TODO: EFI_INVALID_PARAMETER - add return value to function comment
250 // GC_TODO: EFI_DEVICE_ERROR - add return value to function comment
251 // GC_TODO: EFI_SUCCESS - add return value to function comment
252 {
253 EFI_STATUS Status;
254 RTC_REGISTER_B RegisterB;
255 UINT8 Century;
256
257 //
258 // Check parameters for null pointer
259 //
260 if (Time == NULL) {
261 return EFI_INVALID_PARAMETER;
262
263 }
264 //
265 // Acquire RTC Lock to make access to RTC atomic
266 //
267 //BugBug: the EfiAtRuntime should be encapsulated in EfiAcquireLock or
268 // provide a new instance for EfiAcquireLock, say, RtEfiAcquireLock
269 if (!EfiAtRuntime ()) {
270 EfiAcquireLock (&Global->RtcLock);
271 }
272 //
273 // Wait for up to 0.1 seconds for the RTC to be updated
274 //
275 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
276 if (EFI_ERROR (Status)) {
277 //BugBug: the EfiAtRuntime should be encapsulated in EfiReleaseLock or
278 // provide a new instance for EfiReleaseLock, say, RtEfiReleaseLock
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 if (RtcTestCenturyRegister () == EFI_SUCCESS) {
300 Century = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f);
301 } else {
302 Century = RtcRead (RTC_ADDRESS_CENTURY);
303 }
304
305 //
306 // Release RTC Lock.
307 //
308 //BugBug: the EfiAtRuntime should be encapsulated in EfiReleaseLock or
309 // provide a new instance for EfiReleaseLock, say, RtEfiReleaseLock
310 if (!EfiAtRuntime ()) {
311 EfiReleaseLock (&Global->RtcLock);
312 }
313 //
314 // Get the variable that containts the TimeZone and Daylight fields
315 //
316 Time->TimeZone = Global->SavedTimeZone;
317 Time->Daylight = Global->Daylight;
318
319 //
320 // Make sure all field values are in correct range
321 //
322 Status = ConvertRtcTimeToEfiTime (Time, Century, RegisterB);
323 if (!EFI_ERROR (Status)) {
324 Status = RtcTimeFieldsValid (Time);
325 }
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 (PcdGet32 (PcdRealTimeClockUpdateTimeout));
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 (PcdGet32 (PcdRealTimeClockUpdateTimeout));
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 if (RtcTestCenturyRegister () == EFI_SUCCESS) {
543 Century = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f);
544 } else {
545 Century = RtcRead (RTC_ADDRESS_CENTURY);
546 }
547
548 //
549 // Release RTC Lock.
550 //
551 //BugBug: the EfiAtRuntime should be encapsulated in EfiReleaseLock or
552 // provide a new instance for EfiReleaseLock, say, RtEfiReleaseLock
553 if (!EfiAtRuntime ()) {
554 EfiReleaseLock (&Global->RtcLock);
555 }
556 //
557 // Make sure all field values are in correct range
558 //
559 Status = ConvertRtcTimeToEfiTime (Time, Century, RegisterB);
560 if (!EFI_ERROR (Status)) {
561 Status = RtcTimeFieldsValid (Time);
562 }
563 if (EFI_ERROR (Status)) {
564 return EFI_DEVICE_ERROR;
565 }
566
567 *Pending = RegisterC.Bits.AF;
568
569 return EFI_SUCCESS;
570 }
571
572 EFI_STATUS
573 PcRtcSetWakeupTime (
574 IN BOOLEAN Enable,
575 OUT EFI_TIME *Time,
576 IN PC_RTC_MODULE_GLOBALS *Global
577 )
578 /*++
579
580 Routine Description:
581
582 Arguments:
583
584
585
586 Returns:
587 --*/
588 // GC_TODO: Enable - add argument and description to function comment
589 // GC_TODO: Time - add argument and description to function comment
590 // GC_TODO: Global - add argument and description to function comment
591 // GC_TODO: EFI_INVALID_PARAMETER - add return value to function comment
592 // GC_TODO: EFI_INVALID_PARAMETER - add return value to function comment
593 // GC_TODO: EFI_UNSUPPORTED - add return value to function comment
594 // GC_TODO: EFI_DEVICE_ERROR - add return value to function comment
595 // GC_TODO: EFI_SUCCESS - add return value to function comment
596 {
597 EFI_STATUS Status;
598 EFI_TIME RtcTime;
599 RTC_REGISTER_B RegisterB;
600 UINT8 Century;
601 EFI_TIME_CAPABILITIES Capabilities;
602
603 if (Enable) {
604
605 if (Time == NULL) {
606 return EFI_INVALID_PARAMETER;
607 }
608 //
609 // Make sure that the time fields are valid
610 //
611 Status = RtcTimeFieldsValid (Time);
612 if (EFI_ERROR (Status)) {
613 return EFI_INVALID_PARAMETER;
614 }
615 //
616 // Just support set alarm time within 24 hours
617 //
618 PcRtcGetTime (&RtcTime, &Capabilities, Global);
619 if (!IsWithinOneDay (&RtcTime, Time)) {
620 return EFI_UNSUPPORTED;
621 }
622 //
623 // Make a local copy of the time and date
624 //
625 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
626
627 }
628 //
629 // Acquire RTC Lock to make access to RTC atomic
630 //
631 //BugBug: the EfiAtRuntime should be encapsulated in EfiAcquireLock or
632 // provide a new instance for EfiAcquireLock, say, RtEfiAcquireLock
633 if (!EfiAtRuntime ()) {
634 EfiAcquireLock (&Global->RtcLock);
635 }
636 //
637 // Wait for up to 0.1 seconds for the RTC to be updated
638 //
639 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
640 if (EFI_ERROR (Status)) {
641 //BugBug: the EfiAtRuntime should be encapsulated in EfiReleaseLock or
642 // provide a new instance for EfiReleaseLock, say, RtEfiReleaseLock
643 if (!EfiAtRuntime ()) {
644 EfiReleaseLock (&Global->RtcLock);
645 }
646 return EFI_DEVICE_ERROR;
647 }
648 //
649 // Read Register B, and inhibit updates of the RTC
650 //
651 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
652
653 RegisterB.Bits.SET = 1;
654 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
655
656 if (Enable) {
657 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);
658
659 //
660 // Set RTC alarm time
661 //
662 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);
663 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);
664 RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);
665
666 RegisterB.Bits.AIE = 1;
667
668 } else {
669 RegisterB.Bits.AIE = 0;
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 // Release RTC Lock.
679 //
680 //BugBug: the EfiAtRuntime should be encapsulated in EfiReleaseLock or
681 // provide a new instance for EfiReleaseLock, say, RtEfiReleaseLock
682 if (!EfiAtRuntime ()) {
683 EfiReleaseLock (&Global->RtcLock);
684 }
685 return EFI_SUCCESS;
686 }
687
688 EFI_STATUS
689 RtcTestCenturyRegister (
690 VOID
691 )
692 /*++
693
694 Routine Description:
695
696 Arguments:
697
698
699
700 Returns:
701 --*/
702 // GC_TODO: EFI_SUCCESS - add return value to function comment
703 // GC_TODO: EFI_DEVICE_ERROR - add return value to function comment
704 {
705 UINT8 Century;
706 UINT8 Temp;
707
708 Century = RtcRead (RTC_ADDRESS_CENTURY);
709 //
710 // RtcWrite (RTC_ADDRESS_CENTURY, 0x00);
711 //
712 Temp = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f);
713 RtcWrite (RTC_ADDRESS_CENTURY, Century);
714 if (Temp == 0x19 || Temp == 0x20) {
715 return EFI_SUCCESS;
716 }
717
718 return EFI_DEVICE_ERROR;
719 }
720
721 /**
722 Checks an 8-bit BCD value, and converts to an 8-bit value if valid.
723
724 This function checks the 8-bit BCD value specified by Value.
725 If valid, the function converts it to an 8-bit value and returns it.
726 Otherwise, return 0xff.
727
728 @param Value The 8-bit BCD value to check and convert
729
730 @return The 8-bit value converted.
731 0xff if Value is invalid.
732
733 **/
734 UINT8
735 CheckAndConvertBcd8ToDecimal8 (
736 IN UINT8 Value
737 )
738 {
739 if ((Value < 0xa0) && ((Value & 0xf) < 0xa)) {
740 return BcdToDecimal8 (Value);
741 }
742
743 return 0xff;
744 }
745
746 /**
747 Converts time read from RTC to EFI_TIME format defined by UEFI spec.
748
749 This function converts raw time data read from RTC to the EFI_TIME format
750 defined by UEFI spec.
751 If data mode of RTC is BCD, then converts it to decimal,
752 If RTC is in 12-hour format, then converts it to 24-hour format.
753
754 @param Time On input, the time data read from RTC to convert
755 On output, the time converted to UEFI format
756 @param Century Value of century read from RTC.
757 @param RegisterB Value of Register B of RTC, indicating data mode
758 and hour format.
759
760 **/
761 EFI_STATUS
762 ConvertRtcTimeToEfiTime (
763 IN OUT EFI_TIME *Time,
764 IN UINT8 Century,
765 IN RTC_REGISTER_B RegisterB
766 )
767 {
768 BOOLEAN PM;
769
770 if ((Time->Hour) & 0x80) {
771 PM = TRUE;
772 } else {
773 PM = FALSE;
774 }
775
776 Time->Hour = (UINT8) (Time->Hour & 0x7f);
777
778 if (RegisterB.Bits.DM == 0) {
779 Time->Year = CheckAndConvertBcd8ToDecimal8 ((UINT8) Time->Year);
780 Time->Month = CheckAndConvertBcd8ToDecimal8 (Time->Month);
781 Time->Day = CheckAndConvertBcd8ToDecimal8 (Time->Day);
782 Time->Hour = CheckAndConvertBcd8ToDecimal8 (Time->Hour);
783 Time->Minute = CheckAndConvertBcd8ToDecimal8 (Time->Minute);
784 Time->Second = CheckAndConvertBcd8ToDecimal8 (Time->Second);
785 Century = CheckAndConvertBcd8ToDecimal8 (Century);
786 }
787
788 if (Time->Year == 0xff || Time->Month == 0xff || Time->Day == 0xff ||
789 Time->Hour == 0xff || Time->Minute == 0xff || Time->Second == 0xff ||
790 Century == 0xff) {
791 return EFI_INVALID_PARAMETER;
792 }
793
794 Time->Year = (UINT16) (Century * 100 + Time->Year);
795
796 //
797 // If time is in 12 hour format, convert it to 24 hour format
798 //
799 if (RegisterB.Bits.MIL == 0) {
800 if (PM && Time->Hour < 12) {
801 Time->Hour = (UINT8) (Time->Hour + 12);
802 }
803
804 if (!PM && Time->Hour == 12) {
805 Time->Hour = 0;
806 }
807 }
808
809 Time->Nanosecond = 0;
810 Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
811 Time->Daylight = 0;
812
813 return EFI_SUCCESS;
814 }
815
816 EFI_STATUS
817 RtcWaitToUpdate (
818 UINTN Timeout
819 )
820 /*++
821
822 Routine Description:
823
824 Arguments:
825
826
827 Returns:
828 --*/
829 // GC_TODO: Timeout - add argument and description to function comment
830 // GC_TODO: EFI_DEVICE_ERROR - add return value to function comment
831 // GC_TODO: EFI_DEVICE_ERROR - add return value to function comment
832 // GC_TODO: EFI_SUCCESS - add return value to function comment
833 {
834 RTC_REGISTER_A RegisterA;
835 RTC_REGISTER_D RegisterD;
836
837 //
838 // See if the RTC is functioning correctly
839 //
840 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
841
842 if (RegisterD.Bits.VRT == 0) {
843 return EFI_DEVICE_ERROR;
844 }
845 //
846 // Wait for up to 0.1 seconds for the RTC to be ready.
847 //
848 Timeout = (Timeout / 10) + 1;
849 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
850 while (RegisterA.Bits.UIP == 1 && Timeout > 0) {
851 MicroSecondDelay (10);
852 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
853 Timeout--;
854 }
855
856 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
857 if (Timeout == 0 || RegisterD.Bits.VRT == 0) {
858 return EFI_DEVICE_ERROR;
859 }
860
861 return EFI_SUCCESS;
862 }
863
864 EFI_STATUS
865 RtcTimeFieldsValid (
866 IN EFI_TIME *Time
867 )
868 /*++
869
870 Routine Description:
871
872 Arguments:
873
874 Returns:
875 --*/
876 // GC_TODO: Time - add argument and description to function comment
877 // GC_TODO: EFI_INVALID_PARAMETER - add return value to function comment
878 // GC_TODO: EFI_SUCCESS - add return value to function comment
879 {
880 if (Time->Year < 1998 ||
881 Time->Year > 2099 ||
882 Time->Month < 1 ||
883 Time->Month > 12 ||
884 (!DayValid (Time)) ||
885 Time->Hour > 23 ||
886 Time->Minute > 59 ||
887 Time->Second > 59 ||
888 Time->Nanosecond > 999999999 ||
889 (!(Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE || (Time->TimeZone >= -1440 && Time->TimeZone <= 1440))) ||
890 (Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT)))
891 ) {
892 return EFI_INVALID_PARAMETER;
893 }
894
895 return EFI_SUCCESS;
896 }
897
898 BOOLEAN
899 DayValid (
900 IN EFI_TIME *Time
901 )
902 /*++
903
904 Routine Description:
905
906 GC_TODO: Add function description
907
908 Arguments:
909
910 Time - GC_TODO: add argument description
911
912 Returns:
913
914 GC_TODO: add return values
915
916 --*/
917 {
918 INTN DayOfMonth[12];
919
920 DayOfMonth[0] = 31;
921 DayOfMonth[1] = 29;
922 DayOfMonth[2] = 31;
923 DayOfMonth[3] = 30;
924 DayOfMonth[4] = 31;
925 DayOfMonth[5] = 30;
926 DayOfMonth[6] = 31;
927 DayOfMonth[7] = 31;
928 DayOfMonth[8] = 30;
929 DayOfMonth[9] = 31;
930 DayOfMonth[10] = 30;
931 DayOfMonth[11] = 31;
932
933 if (Time->Day < 1 ||
934 Time->Day > DayOfMonth[Time->Month - 1] ||
935 (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28))
936 ) {
937 return FALSE;
938 }
939
940 return TRUE;
941 }
942
943 BOOLEAN
944 IsLeapYear (
945 IN EFI_TIME *Time
946 )
947 /*++
948
949 Routine Description:
950
951 GC_TODO: Add function description
952
953 Arguments:
954
955 Time - GC_TODO: add argument description
956
957 Returns:
958
959 GC_TODO: add return values
960
961 --*/
962 {
963 if (Time->Year % 4 == 0) {
964 if (Time->Year % 100 == 0) {
965 if (Time->Year % 400 == 0) {
966 return TRUE;
967 } else {
968 return FALSE;
969 }
970 } else {
971 return TRUE;
972 }
973 } else {
974 return FALSE;
975 }
976 }
977
978 VOID
979 ConvertEfiTimeToRtcTime (
980 IN EFI_TIME *Time,
981 IN RTC_REGISTER_B RegisterB,
982 IN UINT8 *Century
983 )
984 /*++
985
986 Routine Description:
987
988 Arguments:
989
990
991 Returns:
992 --*/
993 // GC_TODO: Time - add argument and description to function comment
994 // GC_TODO: RegisterB - add argument and description to function comment
995 // GC_TODO: Century - add argument and description to function comment
996 {
997 BOOLEAN PM;
998
999 PM = TRUE;
1000 //
1001 // Adjust hour field if RTC in in 12 hour mode
1002 //
1003 if (RegisterB.Bits.MIL == 0) {
1004 if (Time->Hour < 12) {
1005 PM = FALSE;
1006 }
1007
1008 if (Time->Hour >= 13) {
1009 Time->Hour = (UINT8) (Time->Hour - 12);
1010 } else if (Time->Hour == 0) {
1011 Time->Hour = 12;
1012 }
1013 }
1014 //
1015 // Set the Time/Date/Daylight Savings values.
1016 //
1017 *Century = DecimalToBcd8 ((UINT8) (Time->Year / 100));
1018
1019 Time->Year = (UINT16) (Time->Year % 100);
1020
1021 if (RegisterB.Bits.DM == 0) {
1022 Time->Year = DecimalToBcd8 ((UINT8) Time->Year);
1023 Time->Month = DecimalToBcd8 (Time->Month);
1024 Time->Day = DecimalToBcd8 (Time->Day);
1025 Time->Hour = DecimalToBcd8 (Time->Hour);
1026 Time->Minute = DecimalToBcd8 (Time->Minute);
1027 Time->Second = DecimalToBcd8 (Time->Second);
1028 }
1029 //
1030 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
1031 //
1032 if (RegisterB.Bits.MIL == 0 && PM) {
1033 Time->Hour = (UINT8) (Time->Hour | 0x80);
1034 }
1035 }
1036
1037 STATIC
1038 INTN
1039 CompareHMS (
1040 IN EFI_TIME *From,
1041 IN EFI_TIME *To
1042 )
1043 /*++
1044
1045 Routine Description:
1046
1047 Compare the Hour, Minute and Second of the 'From' time and the 'To' time.
1048 Only compare H/M/S in EFI_TIME and ignore other fields here.
1049
1050 Arguments:
1051
1052 From - the first time
1053 To - the second time
1054
1055 Returns:
1056
1057 >0 : The H/M/S of the 'From' time is later than those of 'To' time
1058 ==0 : The H/M/S of the 'From' time is same as those of 'To' time
1059 <0 : The H/M/S of the 'From' time is earlier than those of 'To' time
1060
1061 --*/
1062 {
1063 if ((From->Hour > To->Hour) ||
1064 ((From->Hour == To->Hour) && (From->Minute > To->Minute)) ||
1065 ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second))) {
1066 return 1;
1067 } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) {
1068 return 0;
1069 } else {
1070 return -1;
1071 }
1072 }
1073
1074 STATIC
1075 BOOLEAN
1076 IsWithinOneDay (
1077 IN EFI_TIME *From,
1078 IN EFI_TIME *To
1079 )
1080 /*++
1081
1082 Routine Description:
1083
1084 Judge whether two days are adjacent.
1085
1086 Arguments:
1087
1088 From - the first day
1089 To - the second day
1090
1091 Returns:
1092
1093 TRUE - The interval of two days are within one day.
1094 FALSE - The interval of two days exceed ony day or parameter error.
1095
1096 --*/
1097 {
1098 UINT8 DayOfMonth[12];
1099 BOOLEAN Adjacent;
1100
1101 DayOfMonth[0] = 31;
1102 DayOfMonth[1] = 29;
1103 DayOfMonth[2] = 31;
1104 DayOfMonth[3] = 30;
1105 DayOfMonth[4] = 31;
1106 DayOfMonth[5] = 30;
1107 DayOfMonth[6] = 31;
1108 DayOfMonth[7] = 31;
1109 DayOfMonth[8] = 30;
1110 DayOfMonth[9] = 31;
1111 DayOfMonth[10] = 30;
1112 DayOfMonth[11] = 31;
1113
1114 Adjacent = FALSE;
1115
1116 if (From->Year == To->Year) {
1117 if (From->Month == To->Month) {
1118 if ((From->Day + 1) == To->Day) {
1119 if ((CompareHMS(From, To) >= 0)) {
1120 Adjacent = TRUE;
1121 }
1122 } else if (From->Day == To->Day) {
1123 if ((CompareHMS(From, To) <= 0)) {
1124 Adjacent = TRUE;
1125 }
1126 }
1127 } else if (((From->Month + 1) == To->Month) && (To->Day == 1)) {
1128 if ((From->Month == 2) && !IsLeapYear(From)) {
1129 if (From->Day == 28) {
1130 if ((CompareHMS(From, To) >= 0)) {
1131 Adjacent = TRUE;
1132 }
1133 }
1134 } else if (From->Day == DayOfMonth[From->Month - 1]) {
1135 if ((CompareHMS(From, To) >= 0)) {
1136 Adjacent = TRUE;
1137 }
1138 }
1139 }
1140 } else if (((From->Year + 1) == To->Year) &&
1141 (From->Month == 12) &&
1142 (From->Day == 31) &&
1143 (To->Month == 1) &&
1144 (To->Day == 1)) {
1145 if ((CompareHMS(From, To) >= 0)) {
1146 Adjacent = TRUE;
1147 }
1148 }
1149
1150 return Adjacent;
1151 }
1152