]> git.proxmox.com Git - mirror_edk2.git/blob - EmbeddedPkg/Library/HalRuntimeServicesExampleLib/Rtc.c
ARM Packages: Removed trailing spaces
[mirror_edk2.git] / EmbeddedPkg / Library / HalRuntimeServicesExampleLib / Rtc.c
1 /** @file
2 Simple PC RTC
3
4 Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
5 Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
6
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
15
16 **/
17
18
19
20 typedef struct {
21 EFI_LOCK RtcLock;
22 UINT16 SavedTimeZone;
23 UINT8 Daylight;
24 } PC_RTC_GLOBALS;
25
26 #define PCAT_RTC_ADDRESS_REGISTER 0x70
27 #define PCAT_RTC_DATA_REGISTER 0x71
28
29 //
30 // Dallas DS12C887 Real Time Clock
31 //
32 #define RTC_ADDRESS_SECONDS 0 // R/W Range 0..59
33 #define RTC_ADDRESS_SECONDS_ALARM 1 // R/W Range 0..59
34 #define RTC_ADDRESS_MINUTES 2 // R/W Range 0..59
35 #define RTC_ADDRESS_MINUTES_ALARM 3 // R/W Range 0..59
36 #define RTC_ADDRESS_HOURS 4 // R/W Range 1..12 or 0..23 Bit 7 is AM/PM
37 #define RTC_ADDRESS_HOURS_ALARM 5 // R/W Range 1..12 or 0..23 Bit 7 is AM/PM
38 #define RTC_ADDRESS_DAY_OF_THE_WEEK 6 // R/W Range 1..7
39 #define RTC_ADDRESS_DAY_OF_THE_MONTH 7 // R/W Range 1..31
40 #define RTC_ADDRESS_MONTH 8 // R/W Range 1..12
41 #define RTC_ADDRESS_YEAR 9 // R/W Range 0..99
42 #define RTC_ADDRESS_REGISTER_A 10 // R/W[0..6] R0[7]
43 #define RTC_ADDRESS_REGISTER_B 11 // R/W
44 #define RTC_ADDRESS_REGISTER_C 12 // RO
45 #define RTC_ADDRESS_REGISTER_D 13 // RO
46 #define RTC_ADDRESS_CENTURY 50 // R/W Range 19..20 Bit 8 is R/W
47 //
48 // Date and time initial values.
49 // They are used if the RTC values are invalid during driver initialization
50 //
51 #define RTC_INIT_SECOND 0
52 #define RTC_INIT_MINUTE 0
53 #define RTC_INIT_HOUR 0
54 #define RTC_INIT_DAY 1
55 #define RTC_INIT_MONTH 1
56 #define RTC_INIT_YEAR 2001
57
58 //
59 // Register initial values
60 //
61 #define RTC_INIT_REGISTER_A 0x26
62 #define RTC_INIT_REGISTER_B 0x02
63 #define RTC_INIT_REGISTER_D 0x0
64
65 #pragma pack(1)
66 //
67 // Register A
68 //
69 typedef struct {
70 UINT8 RS : 4; // Rate Selection Bits
71 UINT8 DV : 3; // Divisor
72 UINT8 UIP : 1; // Update in progress
73 } RTC_REGISTER_A_BITS;
74
75 typedef union {
76 RTC_REGISTER_A_BITS Bits;
77 UINT8 Data;
78 } RTC_REGISTER_A;
79
80 //
81 // Register B
82 //
83 typedef struct {
84 UINT8 DSE : 1; // 0 - Daylight saving disabled 1 - Daylight savings enabled
85 UINT8 MIL : 1; // 0 - 12 hour mode 1 - 24 hour mode
86 UINT8 DM : 1; // 0 - BCD Format 1 - Binary Format
87 UINT8 SQWE : 1; // 0 - Disable SQWE output 1 - Enable SQWE output
88 UINT8 UIE : 1; // 0 - Update INT disabled 1 - Update INT enabled
89 UINT8 AIE : 1; // 0 - Alarm INT disabled 1 - Alarm INT Enabled
90 UINT8 PIE : 1; // 0 - Periodic INT disabled 1 - Periodic INT Enabled
91 UINT8 SET : 1; // 0 - Normal operation. 1 - Updates inhibited
92 } RTC_REGISTER_B_BITS;
93
94 typedef union {
95 RTC_REGISTER_B_BITS Bits;
96 UINT8 Data;
97 } RTC_REGISTER_B;
98
99 //
100 // Register C
101 //
102 typedef struct {
103 UINT8 Reserved : 4; // Read as zero. Can not be written.
104 UINT8 UF : 1; // Update End Interrupt Flag
105 UINT8 AF : 1; // Alarm Interrupt Flag
106 UINT8 PF : 1; // Periodic Interrupt Flag
107 UINT8 IRQF : 1; // Iterrupt Request Flag = PF & PIE | AF & AIE | UF & UIE
108 } RTC_REGISTER_C_BITS;
109
110 typedef union {
111 RTC_REGISTER_C_BITS Bits;
112 UINT8 Data;
113 } RTC_REGISTER_C;
114
115 //
116 // Register D
117 //
118 typedef struct {
119 UINT8 Reserved : 7; // Read as zero. Can not be written.
120 UINT8 VRT : 1; // Valid RAM and Time
121 } RTC_REGISTER_D_BITS;
122
123 typedef union {
124 RTC_REGISTER_D_BITS Bits;
125 UINT8 Data;
126 } RTC_REGISTER_D;
127
128 #pragma pack()
129
130 PC_RTC_GLOBALS mRtc;
131
132 BOOLEAN
133 IsLeapYear (
134 IN EFI_TIME *Time
135 )
136 {
137 if (Time->Year % 4 == 0) {
138 if (Time->Year % 100 == 0) {
139 if (Time->Year % 400 == 0) {
140 return TRUE;
141 } else {
142 return FALSE;
143 }
144 } else {
145 return TRUE;
146 }
147 } else {
148 return FALSE;
149 }
150 }
151
152
153 const INTN mDayOfMonth[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
154
155 BOOLEAN
156 DayValid (
157 IN EFI_TIME *Time
158 )
159 {
160 if (Time->Day < 1 ||
161 Time->Day > mDayOfMonth[Time->Month - 1] ||
162 (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28))
163 ) {
164 return FALSE;
165 }
166
167 return TRUE;
168 }
169
170
171 UINT8
172 DecimaltoBcd (
173 IN UINT8 DecValue
174 )
175 {
176 UINTN High;
177 UINTN Low;
178
179 High = DecValue / 10;
180 Low = DecValue - (High * 10);
181
182 return (UINT8) (Low + (High << 4));
183 }
184
185 UINT8
186 BcdToDecimal (
187 IN UINT8 BcdValue
188 )
189 {
190 UINTN High;
191 UINTN Low;
192
193 High = BcdValue >> 4;
194 Low = BcdValue - (High << 4);
195
196 return (UINT8) (Low + (High * 10));
197 }
198
199
200
201
202 VOID
203 ConvertEfiTimeToRtcTime (
204 IN EFI_TIME *Time,
205 IN RTC_REGISTER_B RegisterB,
206 IN UINT8 *Century
207 )
208 {
209 BOOLEAN PM;
210
211 PM = TRUE;
212 //
213 // Adjust hour field if RTC in in 12 hour mode
214 //
215 if (RegisterB.Bits.MIL == 0) {
216 if (Time->Hour < 12) {
217 PM = FALSE;
218 }
219
220 if (Time->Hour >= 13) {
221 Time->Hour = (UINT8) (Time->Hour - 12);
222 } else if (Time->Hour == 0) {
223 Time->Hour = 12;
224 }
225 }
226 //
227 // Set the Time/Date/Daylight Savings values.
228 //
229 *Century = DecimaltoBcd ((UINT8) (Time->Year / 100));
230
231 Time->Year = (UINT16) (Time->Year % 100);
232
233 if (RegisterB.Bits.DM == 0) {
234 Time->Year = DecimaltoBcd ((UINT8) Time->Year);
235 Time->Month = DecimaltoBcd (Time->Month);
236 Time->Day = DecimaltoBcd (Time->Day);
237 Time->Hour = DecimaltoBcd (Time->Hour);
238 Time->Minute = DecimaltoBcd (Time->Minute);
239 Time->Second = DecimaltoBcd (Time->Second);
240 }
241 //
242 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
243 //
244 if (RegisterB.Bits.MIL == 0 && PM) {
245 Time->Hour = (UINT8) (Time->Hour | 0x80);
246 }
247 }
248
249 EFI_STATUS
250 RtcTimeFieldsValid (
251 IN EFI_TIME *Time
252 )
253 /*++
254
255 Routine Description:
256
257 Arguments:
258
259 Returns:
260 --*/
261 // TODO: Time - add argument and description to function comment
262 // TODO: EFI_INVALID_PARAMETER - add return value to function comment
263 // TODO: EFI_SUCCESS - add return value to function comment
264 {
265 if (Time->Year < 1998 ||
266 Time->Year > 2099 ||
267 Time->Month < 1 ||
268 Time->Month > 12 ||
269 (!DayValid (Time)) ||
270 Time->Hour > 23 ||
271 Time->Minute > 59 ||
272 Time->Second > 59 ||
273 Time->Nanosecond > 999999999 ||
274 (!(Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE || (Time->TimeZone >= -1440 && Time->TimeZone <= 1440))) ||
275 (Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT)))
276 ) {
277 return EFI_INVALID_PARAMETER;
278 }
279
280 return EFI_SUCCESS;
281 }
282
283 UINT8
284 RtcRead (
285 IN UINT8 Address
286 )
287 {
288 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));
289 return IoRead8 (PCAT_RTC_DATA_REGISTER);
290 }
291
292 VOID
293 RtcWrite (
294 IN UINT8 Address,
295 IN UINT8 Data
296 )
297 {
298 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));
299 IoWrite8 (PCAT_RTC_DATA_REGISTER, Data);
300 }
301
302
303 EFI_STATUS
304 RtcTestCenturyRegister (
305 VOID
306 )
307 {
308 UINT8 Century;
309 UINT8 Temp;
310
311 Century = RtcRead (RTC_ADDRESS_CENTURY);
312 //
313 // RtcWrite (RTC_ADDRESS_CENTURY, 0x00);
314 //
315 Temp = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f);
316 RtcWrite (RTC_ADDRESS_CENTURY, Century);
317 if (Temp == 0x19 || Temp == 0x20) {
318 return EFI_SUCCESS;
319 }
320
321 return EFI_DEVICE_ERROR;
322 }
323
324 VOID
325 ConvertRtcTimeToEfiTime (
326 IN EFI_TIME *Time,
327 IN RTC_REGISTER_B RegisterB
328 )
329 {
330 BOOLEAN PM;
331
332 if ((Time->Hour) & 0x80) {
333 PM = TRUE;
334 } else {
335 PM = FALSE;
336 }
337
338 Time->Hour = (UINT8) (Time->Hour & 0x7f);
339
340 if (RegisterB.Bits.DM == 0) {
341 Time->Year = BcdToDecimal ((UINT8) Time->Year);
342 Time->Month = BcdToDecimal (Time->Month);
343 Time->Day = BcdToDecimal (Time->Day);
344 Time->Hour = BcdToDecimal (Time->Hour);
345 Time->Minute = BcdToDecimal (Time->Minute);
346 Time->Second = BcdToDecimal (Time->Second);
347 }
348 //
349 // If time is in 12 hour format, convert it to 24 hour format
350 //
351 if (RegisterB.Bits.MIL == 0) {
352 if (PM && Time->Hour < 12) {
353 Time->Hour = (UINT8) (Time->Hour + 12);
354 }
355
356 if (!PM && Time->Hour == 12) {
357 Time->Hour = 0;
358 }
359 }
360
361 Time->Nanosecond = 0;
362 Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
363 Time->Daylight = 0;
364 }
365
366 EFI_STATUS
367 RtcWaitToUpdate (
368 UINTN Timeout
369 )
370 {
371 RTC_REGISTER_A RegisterA;
372 RTC_REGISTER_D RegisterD;
373
374 //
375 // See if the RTC is functioning correctly
376 //
377 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
378
379 if (RegisterD.Bits.VRT == 0) {
380 return EFI_DEVICE_ERROR;
381 }
382 //
383 // Wait for up to 0.1 seconds for the RTC to be ready.
384 //
385 Timeout = (Timeout / 10) + 1;
386 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
387 while (RegisterA.Bits.UIP == 1 && Timeout > 0) {
388 MicroSecondDelay (10);
389 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
390 Timeout--;
391 }
392
393 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
394 if (Timeout == 0 || RegisterD.Bits.VRT == 0) {
395 return EFI_DEVICE_ERROR;
396 }
397
398 return EFI_SUCCESS;
399 }
400
401 EFI_STATUS
402 LibGetTime (
403 OUT EFI_TIME *Time,
404 OUT EFI_TIME_CAPABILITIES *Capabilities
405 )
406 {
407 EFI_STATUS Status;
408 RTC_REGISTER_B RegisterB;
409 UINT8 Century;
410 UINTN BufferSize;
411
412 //
413 // Check parameters for null pointer
414 //
415 if (Time == NULL) {
416 return EFI_INVALID_PARAMETER;
417
418 }
419 //
420 // Acquire RTC Lock to make access to RTC atomic
421 //
422 EfiAcquireLock (&mRtc.RtcLock);
423
424 //
425 // Wait for up to 0.1 seconds for the RTC to be updated
426 //
427 Status = RtcWaitToUpdate (100000);
428 if (EFI_ERROR (Status)) {
429 EfiReleaseLock (&mRtc.RtcLock);
430 return Status;
431 }
432 //
433 // Read Register B
434 //
435 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
436
437 //
438 // Get the Time/Date/Daylight Savings values.
439 //
440 Time->Second = RtcRead (RTC_ADDRESS_SECONDS);
441 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES);
442 Time->Hour = RtcRead (RTC_ADDRESS_HOURS);
443 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
444 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
445 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
446
447 ConvertRtcTimeToEfiTime (Time, RegisterB);
448
449 if (RtcTestCenturyRegister () == EFI_SUCCESS) {
450 Century = BcdToDecimal ((UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f));
451 } else {
452 Century = BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY));
453 }
454
455 Time->Year = (UINT16) (Century * 100 + Time->Year);
456
457 //
458 // Release RTC Lock.
459 //
460 EfiReleaseLock (&mRtc.RtcLock);
461
462 //
463 // Get the variable that containts the TimeZone and Daylight fields
464 //
465 Time->TimeZone = mRtc.SavedTimeZone;
466 Time->Daylight = mRtc.Daylight;
467
468 BufferSize = sizeof (INT16) + sizeof (UINT8);
469
470 //
471 // Make sure all field values are in correct range
472 //
473 Status = RtcTimeFieldsValid (Time);
474 if (EFI_ERROR (Status)) {
475 return EFI_DEVICE_ERROR;
476 }
477 //
478 // Fill in Capabilities if it was passed in
479 //
480 if (Capabilities) {
481 Capabilities->Resolution = 1;
482 //
483 // 1 hertz
484 //
485 Capabilities->Accuracy = 50000000;
486 //
487 // 50 ppm
488 //
489 Capabilities->SetsToZero = FALSE;
490 }
491
492 return EFI_SUCCESS;
493 }
494
495
496
497 EFI_STATUS
498 LibSetTime (
499 IN EFI_TIME *Time
500 )
501 {
502 EFI_STATUS Status;
503 EFI_TIME RtcTime;
504 RTC_REGISTER_B RegisterB;
505 UINT8 Century;
506
507 if (Time == NULL) {
508 return EFI_INVALID_PARAMETER;
509 }
510 //
511 // Make sure that the time fields are valid
512 //
513 Status = RtcTimeFieldsValid (Time);
514 if (EFI_ERROR (Status)) {
515 return Status;
516 }
517
518 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
519
520 //
521 // Acquire RTC Lock to make access to RTC atomic
522 //
523 EfiAcquireLock (&mRtc.RtcLock);
524
525 //
526 // Wait for up to 0.1 seconds for the RTC to be updated
527 //
528 Status = RtcWaitToUpdate (100000);
529 if (EFI_ERROR (Status)) {
530 EfiReleaseLock (&mRtc.RtcLock);
531 return Status;
532 }
533 //
534 // Read Register B, and inhibit updates of the RTC
535 //
536 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
537 RegisterB.Bits.SET = 1;
538 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
539
540 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);
541
542 RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second);
543 RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute);
544 RtcWrite (RTC_ADDRESS_HOURS, RtcTime.Hour);
545 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH, RtcTime.Day);
546 RtcWrite (RTC_ADDRESS_MONTH, RtcTime.Month);
547 RtcWrite (RTC_ADDRESS_YEAR, (UINT8) RtcTime.Year);
548 if (RtcTestCenturyRegister () == EFI_SUCCESS) {
549 Century = (UINT8) ((Century & 0x7f) | (RtcRead (RTC_ADDRESS_CENTURY) & 0x80));
550 }
551
552 RtcWrite (RTC_ADDRESS_CENTURY, Century);
553
554 //
555 // Allow updates of the RTC registers
556 //
557 RegisterB.Bits.SET = 0;
558 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
559
560 //
561 // Release RTC Lock.
562 //
563 EfiReleaseLock (&mRtc.RtcLock);
564
565 //
566 // Set the variable that containts the TimeZone and Daylight fields
567 //
568 mRtc.SavedTimeZone = Time->TimeZone;
569 mRtc.Daylight = Time->Daylight;
570 return Status;
571 }
572
573 EFI_STATUS
574 libGetWakeupTime (
575 OUT BOOLEAN *Enabled,
576 OUT BOOLEAN *Pending,
577 OUT EFI_TIME *Time
578 )
579 {
580 EFI_STATUS Status;
581 RTC_REGISTER_B RegisterB;
582 RTC_REGISTER_C RegisterC;
583 UINT8 Century;
584
585 //
586 // Check paramters for null pointers
587 //
588 if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) {
589 return EFI_INVALID_PARAMETER;
590
591 }
592 //
593 // Acquire RTC Lock to make access to RTC atomic
594 //
595 EfiAcquireLock (&mRtc.RtcLock);
596
597 //
598 // Wait for up to 0.1 seconds for the RTC to be updated
599 //
600 Status = RtcWaitToUpdate (100000);
601 if (EFI_ERROR (Status)) {
602 EfiReleaseLock (&mRtc.RtcLock);
603 return EFI_DEVICE_ERROR;
604 }
605 //
606 // Read Register B and Register C
607 //
608 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
609 RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);
610
611 //
612 // Get the Time/Date/Daylight Savings values.
613 //
614 *Enabled = RegisterB.Bits.AIE;
615 if (*Enabled) {
616 Time->Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);
617 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);
618 Time->Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);
619 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
620 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
621 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
622 } else {
623 Time->Second = 0;
624 Time->Minute = 0;
625 Time->Hour = 0;
626 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
627 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
628 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
629 }
630
631 ConvertRtcTimeToEfiTime (Time, RegisterB);
632
633 if (RtcTestCenturyRegister () == EFI_SUCCESS) {
634 Century = BcdToDecimal ((UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f));
635 } else {
636 Century = BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY));
637 }
638
639 Time->Year = (UINT16) (Century * 100 + Time->Year);
640
641 //
642 // Release RTC Lock.
643 //
644 EfiReleaseLock (&mRtc.RtcLock);
645
646 //
647 // Make sure all field values are in correct range
648 //
649 Status = RtcTimeFieldsValid (Time);
650 if (EFI_ERROR (Status)) {
651 return EFI_DEVICE_ERROR;
652 }
653
654 *Pending = RegisterC.Bits.AF;
655
656 return EFI_SUCCESS;
657 }
658
659 EFI_STATUS
660 LibSetWakeupTime (
661 IN BOOLEAN Enabled,
662 OUT EFI_TIME *Time
663 )
664 {
665 EFI_STATUS Status;
666 EFI_TIME RtcTime;
667 RTC_REGISTER_B RegisterB;
668 UINT8 Century;
669 EFI_TIME_CAPABILITIES Capabilities;
670
671 if (Enabled) {
672
673 if (Time == NULL) {
674 return EFI_INVALID_PARAMETER;
675 }
676 //
677 // Make sure that the time fields are valid
678 //
679 Status = RtcTimeFieldsValid (Time);
680 if (EFI_ERROR (Status)) {
681 return EFI_INVALID_PARAMETER;
682 }
683 //
684 // Just support set alarm time within 24 hours
685 //
686 LibGetTime (&RtcTime, &Capabilities);
687 if (Time->Year != RtcTime.Year ||
688 Time->Month != RtcTime.Month ||
689 (Time->Day != RtcTime.Day && Time->Day != (RtcTime.Day + 1))
690 ) {
691 return EFI_UNSUPPORTED;
692 }
693 //
694 // Make a local copy of the time and date
695 //
696 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
697
698 }
699 //
700 // Acquire RTC Lock to make access to RTC atomic
701 //
702 EfiAcquireLock (&mRtc.RtcLock);
703
704 //
705 // Wait for up to 0.1 seconds for the RTC to be updated
706 //
707 Status = RtcWaitToUpdate (100000);
708 if (EFI_ERROR (Status)) {
709 EfiReleaseLock (&mRtc.RtcLock);
710 return EFI_DEVICE_ERROR;
711 }
712 //
713 // Read Register B, and inhibit updates of the RTC
714 //
715 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
716
717 RegisterB.Bits.SET = 1;
718 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
719
720 if (Enabled) {
721 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);
722
723 //
724 // Set RTC alarm time
725 //
726 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);
727 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);
728 RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);
729
730 RegisterB.Bits.AIE = 1;
731
732 } else {
733 RegisterB.Bits.AIE = 0;
734 }
735 //
736 // Allow updates of the RTC registers
737 //
738 RegisterB.Bits.SET = 0;
739 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
740
741 //
742 // Release RTC Lock.
743 //
744 EfiReleaseLock (&mRtc.RtcLock);
745
746 return EFI_SUCCESS;
747 }
748
749
750
751 VOID
752 LibRtcVirtualAddressChangeEvent (
753 VOID
754 )
755 {
756 }
757
758
759 VOID
760 LibRtcInitialize (
761 VOID
762 )
763 {
764 EFI_STATUS Status;
765 RTC_REGISTER_A RegisterA;
766 RTC_REGISTER_B RegisterB;
767 RTC_REGISTER_C RegisterC;
768 RTC_REGISTER_D RegisterD;
769 UINT8 Century;
770 EFI_TIME Time;
771
772 //
773 // Acquire RTC Lock to make access to RTC atomic
774 //
775 EfiAcquireLock (&mRtc.RtcLock);
776
777 //
778 // Initialize RTC Register
779 //
780 // Make sure Division Chain is properly configured,
781 // or RTC clock won't "tick" -- time won't increment
782 //
783 RegisterA.Data = RTC_INIT_REGISTER_A;
784 RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);
785
786 //
787 // Read Register B
788 //
789 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
790
791 //
792 // Clear RTC flag register
793 //
794 RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);
795
796 //
797 // Clear RTC register D
798 //
799 RegisterD.Data = RTC_INIT_REGISTER_D;
800 RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data);
801
802 //
803 // Wait for up to 0.1 seconds for the RTC to be updated
804 //
805 Status = RtcWaitToUpdate (100000);
806 if (EFI_ERROR (Status)) {
807 EfiReleaseLock (&mRtc.RtcLock);
808 return;
809 }
810
811 //
812 // Get the Time/Date/Daylight Savings values.
813 //
814 Time.Second = RtcRead (RTC_ADDRESS_SECONDS);
815 Time.Minute = RtcRead (RTC_ADDRESS_MINUTES);
816 Time.Hour = RtcRead (RTC_ADDRESS_HOURS);
817 Time.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
818 Time.Month = RtcRead (RTC_ADDRESS_MONTH);
819 Time.Year = RtcRead (RTC_ADDRESS_YEAR);
820
821 ConvertRtcTimeToEfiTime (&Time, RegisterB);
822
823 if (RtcTestCenturyRegister () == EFI_SUCCESS) {
824 Century = BcdToDecimal ((UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f));
825 } else {
826 Century = BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY));
827 }
828
829 Time.Year = (UINT16) (Century * 100 + Time.Year);
830
831 //
832 // Set RTC configuration after get original time
833 //
834 RtcWrite (RTC_ADDRESS_REGISTER_B, RTC_INIT_REGISTER_B);
835
836 //
837 // Release RTC Lock.
838 //
839 EfiReleaseLock (&mRtc.RtcLock);
840
841 //
842 // Validate time fields
843 //
844 Status = RtcTimeFieldsValid (&Time);
845 if (EFI_ERROR (Status)) {
846 Time.Second = RTC_INIT_SECOND;
847 Time.Minute = RTC_INIT_MINUTE;
848 Time.Hour = RTC_INIT_HOUR;
849 Time.Day = RTC_INIT_DAY;
850 Time.Month = RTC_INIT_MONTH;
851 Time.Year = RTC_INIT_YEAR;
852 }
853 //
854 // Reset time value according to new RTC configuration
855 //
856 LibSetTime (&Time);
857
858 return;
859 }
860
861