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