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