]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/PcatRealTimeClockRuntimeDxe/PcRtc.c
Enable storage of daylight saving and time zone data of SetTime() service, and fix...
[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
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
ec35e997 180 // The value of bit AIE should be reserved.\r
8cd4d17c 181 //\r
ec35e997 182 RtcWrite (RTC_ADDRESS_REGISTER_B, RTC_INIT_REGISTER_B | (RegisterB.Data & BIT5));\r
8cd4d17c 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
ec35e997
LG
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
8cd4d17c 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
231EFI_STATUS\r
232PcRtcGetTime (\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
239Routine 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
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
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
573EFI_STATUS\r
574PcRtcSetWakeupTime (\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
581Routine Description:\r
582\r
583 Arguments:\r
584\r
585\r
586\r
587Returns:\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
689EFI_STATUS\r
690RtcTestCenturyRegister (\r
691 VOID\r
692 )\r
693/*++\r
694\r
695Routine Description:\r
696\r
697 Arguments:\r
698\r
699\r
700\r
701Returns:\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
722VOID\r
723ConvertRtcTimeToEfiTime (\r
724 IN EFI_TIME *Time,\r
725 IN RTC_REGISTER_B RegisterB\r
726 )\r
727/*++\r
728\r
729Routine Description:\r
730\r
731 Arguments:\r
732\r
733\r
734\r
735Returns:\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
776EFI_STATUS\r
777RtcWaitToUpdate (\r
778 UINTN Timeout\r
779 )\r
780/*++\r
781\r
782Routine Description:\r
783\r
784 Arguments:\r
785\r
786\r
787Returns:\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
824EFI_STATUS\r
825RtcTimeFieldsValid (\r
826 IN EFI_TIME *Time\r
827 )\r
828/*++\r
829\r
830Routine 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
858BOOLEAN\r
859DayValid (\r
860 IN EFI_TIME *Time\r
861 )\r
862/*++\r
863\r
864Routine Description:\r
865\r
866 GC_TODO: Add function description\r
867\r
868Arguments:\r
869\r
870 Time - GC_TODO: add argument description\r
871\r
872Returns:\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
903BOOLEAN\r
904IsLeapYear (\r
905 IN EFI_TIME *Time\r
906 )\r
907/*++\r
908\r
909Routine Description:\r
910\r
911 GC_TODO: Add function description\r
912\r
913Arguments:\r
914\r
915 Time - GC_TODO: add argument description\r
916\r
917Returns:\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
938VOID\r
939ConvertEfiTimeToRtcTime (\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
946Routine Description:\r
947\r
948 Arguments:\r
949\r
950\r
951Returns:\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
997STATIC\r
998INTN\r
999CompareHMS (\r
1000 IN EFI_TIME *From,\r
1001 IN EFI_TIME *To\r
1002 )\r
1003/*++\r
1004\r
1005Routine 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
1010Arguments:\r
1011\r
1012 From - the first time\r
1013 To - the second time\r
1014\r
1015Returns:\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
1034STATIC\r
1035BOOLEAN\r
1036IsWithinOneDay (\r
1037 IN EFI_TIME *From,\r
1038 IN EFI_TIME *To\r
1039 )\r
1040/*++\r
1041\r
1042Routine Description:\r
1043\r
1044 Judge whether two days are adjacent.\r
1045\r
1046Arguments:\r
1047\r
1048 From - the first day\r
1049 To - the second day\r
1050\r
1051Returns:\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
1f4cf7b1 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
8cd4d17c 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