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