]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/PcatRealTimeClockRuntimeDxe/PcRtc.c
Add logic to check uninitialized RTC value in PcRtc driver in MdeModulePkg.
[mirror_edk2.git] / MdeModulePkg / Universal / PcatRealTimeClockRuntimeDxe / PcRtc.c
CommitLineData
fb0b259e 1/** @file\r
2 RTC Architectural Protocol GUID as defined in DxeCis 0.96.\r
8cd4d17c 3\r
fb0b259e 4Copyright (c) 2006 - 2007, Intel Corporation\r
3cfb790c 5All rights reserved. This program and the accompanying materials\r
6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
8cd4d17c 12\r
fb0b259e 13**/\r
8cd4d17c 14\r
15#include "PcRtc.h"\r
16\r
17STATIC\r
18INTN\r
19CompareHMS (\r
20 IN EFI_TIME *From,\r
21 IN EFI_TIME *To\r
22 );\r
23\r
24STATIC\r
25BOOLEAN\r
26IsWithinOneDay (\r
27 IN EFI_TIME *From,\r
28 IN EFI_TIME *To\r
29 );\r
30\r
31STATIC\r
32UINT8\r
33RtcRead (\r
34 IN UINT8 Address\r
35 )\r
36/*++\r
37\r
38Routine Description:\r
39\r
40 GC_TODO: Add function description\r
41\r
42Arguments:\r
43\r
44 Address - GC_TODO: add argument description\r
45\r
46Returns:\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
56STATIC\r
57VOID\r
58RtcWrite (\r
59 IN UINT8 Address,\r
60 IN UINT8 Data\r
61 )\r
62/*++\r
63\r
64Routine Description:\r
65\r
66 GC_TODO: Add function description\r
67\r
68Arguments:\r
69\r
70 Address - GC_TODO: add argument description\r
71 Data - GC_TODO: add argument description\r
72\r
73Returns:\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
83EFI_STATUS\r
84PcRtcInit (\r
85 IN PC_RTC_MODULE_GLOBALS *Global\r
86 )\r
87/*++\r
88\r
89Routine Description:\r
90\r
91 GC_TODO: Add function description\r
92\r
93Arguments:\r
94\r
95 Global - GC_TODO: add argument description\r
96\r
97Returns:\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
ec35e997
LG
110 UINTN DataSize;\r
111 UINT32 TimerVar;\r
8cd4d17c 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
8cd4d17c 168 if (RtcTestCenturyRegister () == EFI_SUCCESS) {\r
254ba247 169 Century = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f);\r
8cd4d17c 170 } else {\r
254ba247 171 Century = RtcRead (RTC_ADDRESS_CENTURY);\r
8cd4d17c 172 }\r
173\r
8cd4d17c 174 //\r
175 // Set RTC configuration after get original time\r
ec35e997 176 // The value of bit AIE should be reserved.\r
8cd4d17c 177 //\r
f7033880 178 RtcWrite (RTC_ADDRESS_REGISTER_B, (UINT8)(RTC_INIT_REGISTER_B | (RegisterB.Data & BIT5)));\r
8cd4d17c 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
254ba247 185 //\r
8cd4d17c 186 if (!EfiAtRuntime ()) {\r
187 EfiReleaseLock (&Global->RtcLock);\r
188 }\r
254ba247 189 \r
8cd4d17c 190 //\r
191 // Validate time fields\r
192 //\r
254ba247 193 Status = ConvertRtcTimeToEfiTime (&Time, Century, RegisterB);\r
194 if (!EFI_ERROR (Status)) {\r
195 Status = RtcTimeFieldsValid (&Time);\r
196 }\r
8cd4d17c 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
ec35e997
LG
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
8cd4d17c 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
232EFI_STATUS\r
233PcRtcGetTime (\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
240Routine 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
8cd4d17c 299 if (RtcTestCenturyRegister () == EFI_SUCCESS) {\r
254ba247 300 Century = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f);\r
8cd4d17c 301 } else {\r
254ba247 302 Century = RtcRead (RTC_ADDRESS_CENTURY);\r
8cd4d17c 303 }\r
304\r
8cd4d17c 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
254ba247 322 Status = ConvertRtcTimeToEfiTime (Time, Century, RegisterB);\r
323 if (!EFI_ERROR (Status)) {\r
324 Status = RtcTimeFieldsValid (Time);\r
325 }\r
8cd4d17c 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
347EFI_STATUS\r
348PcRtcSetTime (\r
349 IN EFI_TIME *Time,\r
350 IN PC_RTC_MODULE_GLOBALS *Global\r
351 )\r
352/*++\r
353\r
354Routine 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
ec35e997 368 UINT32 TimerVar;\r
8cd4d17c 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
ec35e997
LG
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
8cd4d17c 456}\r
457\r
458EFI_STATUS\r
459PcRtcGetWakeupTime (\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
467Routine Description:\r
468\r
469 Arguments:\r
470\r
471\r
472\r
473Returns:\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
8cd4d17c 542 if (RtcTestCenturyRegister () == EFI_SUCCESS) {\r
254ba247 543 Century = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f);\r
8cd4d17c 544 } else {\r
254ba247 545 Century = RtcRead (RTC_ADDRESS_CENTURY);\r
8cd4d17c 546 }\r
547\r
8cd4d17c 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
254ba247 559 Status = ConvertRtcTimeToEfiTime (Time, Century, RegisterB);\r
560 if (!EFI_ERROR (Status)) {\r
561 Status = RtcTimeFieldsValid (Time);\r
562 }\r
8cd4d17c 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
572EFI_STATUS\r
573PcRtcSetWakeupTime (\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
580Routine Description:\r
581\r
582 Arguments:\r
583\r
584\r
585\r
586Returns:\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
688EFI_STATUS\r
689RtcTestCenturyRegister (\r
690 VOID\r
691 )\r
692/*++\r
693\r
694Routine Description:\r
695\r
696 Arguments:\r
697\r
698\r
699\r
700Returns:\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
254ba247 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
734UINT8\r
735CheckAndConvertBcd8ToDecimal8 (\r
736 IN UINT8 Value\r
8cd4d17c 737 )\r
254ba247 738{\r
739 if ((Value < 0xa0) && ((Value & 0xf) < 0xa)) {\r
740 return BcdToDecimal8 (Value);\r
741 }\r
8cd4d17c 742\r
254ba247 743 return 0xff;\r
744}\r
8cd4d17c 745\r
254ba247 746/**\r
747 Converts time read from RTC to EFI_TIME format defined by UEFI spec.\r
8cd4d17c 748\r
254ba247 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
8cd4d17c 753\r
254ba247 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
8cd4d17c 759\r
254ba247 760**/\r
761EFI_STATUS\r
762ConvertRtcTimeToEfiTime (\r
763 IN OUT EFI_TIME *Time,\r
764 IN UINT8 Century,\r
765 IN RTC_REGISTER_B RegisterB\r
766 )\r
8cd4d17c 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
254ba247 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
8cd4d17c 792 }\r
254ba247 793\r
794 Time->Year = (UINT16) (Century * 100 + Time->Year);\r
795\r
8cd4d17c 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
254ba247 812\r
813 return EFI_SUCCESS;\r
8cd4d17c 814}\r
815\r
816EFI_STATUS\r
817RtcWaitToUpdate (\r
818 UINTN Timeout\r
819 )\r
820/*++\r
821\r
822Routine Description:\r
823\r
824 Arguments:\r
825\r
826\r
827Returns:\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
864EFI_STATUS\r
865RtcTimeFieldsValid (\r
866 IN EFI_TIME *Time\r
867 )\r
868/*++\r
869\r
870Routine 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
898BOOLEAN\r
899DayValid (\r
900 IN EFI_TIME *Time\r
901 )\r
902/*++\r
903\r
904Routine Description:\r
905\r
906 GC_TODO: Add function description\r
907\r
908Arguments:\r
909\r
910 Time - GC_TODO: add argument description\r
911\r
912Returns:\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
943BOOLEAN\r
944IsLeapYear (\r
945 IN EFI_TIME *Time\r
946 )\r
947/*++\r
948\r
949Routine Description:\r
950\r
951 GC_TODO: Add function description\r
952\r
953Arguments:\r
954\r
955 Time - GC_TODO: add argument description\r
956\r
957Returns:\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
978VOID\r
979ConvertEfiTimeToRtcTime (\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
986Routine Description:\r
987\r
988 Arguments:\r
989\r
990\r
991Returns:\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
1037STATIC\r
1038INTN\r
1039CompareHMS (\r
1040 IN EFI_TIME *From,\r
1041 IN EFI_TIME *To\r
1042 )\r
1043/*++\r
1044\r
1045Routine 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
1050Arguments:\r
1051\r
1052 From - the first time\r
1053 To - the second time\r
1054\r
1055Returns:\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
1074STATIC\r
1075BOOLEAN\r
1076IsWithinOneDay (\r
1077 IN EFI_TIME *From,\r
1078 IN EFI_TIME *To\r
1079 )\r
1080/*++\r
1081\r
1082Routine Description:\r
1083\r
1084 Judge whether two days are adjacent.\r
1085\r
1086Arguments:\r
1087\r
1088 From - the first day\r
1089 To - the second day\r
1090\r
1091Returns:\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
1f4cf7b1 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
8cd4d17c 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