• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
IsLeapYear(IN EFI_TIME * Time)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
DayValid(IN EFI_TIME * Time)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
DecimaltoBcd(IN UINT8 DecValue)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
BcdToDecimal(IN UINT8 BcdValue)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
ConvertEfiTimeToRtcTime(IN EFI_TIME * Time,IN RTC_REGISTER_B RegisterB,IN UINT8 * Century)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
RtcTimeFieldsValid(IN EFI_TIME * Time)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
RtcRead(IN UINT8 Address)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
RtcWrite(IN UINT8 Address,IN UINT8 Data)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
RtcTestCenturyRegister(VOID)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
ConvertRtcTimeToEfiTime(IN EFI_TIME * Time,IN RTC_REGISTER_B RegisterB)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
RtcWaitToUpdate(UINTN Timeout)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
LibGetTime(OUT EFI_TIME * Time,OUT EFI_TIME_CAPABILITIES * Capabilities)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
LibSetTime(IN EFI_TIME * Time)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
libGetWakeupTime(OUT BOOLEAN * Enabled,OUT BOOLEAN * Pending,OUT EFI_TIME * Time)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 paramters 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
LibSetWakeupTime(IN BOOLEAN Enabled,OUT EFI_TIME * Time)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
LibRtcVirtualAddressChangeEvent(VOID)753 LibRtcVirtualAddressChangeEvent (
754   VOID
755   )
756 {
757 }
758 
759 
760 VOID
LibRtcInitialize(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 
863