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