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