]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / PcAtChipsetPkg / PcatRealTimeClockRuntimeDxe / PcRtc.c
... / ...
CommitLineData
1/** @file\r
2 RTC Architectural Protocol GUID as defined in DxeCis 0.96.\r
3\r
4Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
5Copyright (c) 2017, AMD Inc. All rights reserved.<BR>\r
6Copyright (c) 2018 - 2020, ARM Limited. All rights reserved.<BR>\r
7\r
8SPDX-License-Identifier: BSD-2-Clause-Patent\r
9\r
10**/\r
11\r
12#include "PcRtc.h"\r
13\r
14extern UINTN mRtcIndexRegister;\r
15extern UINTN mRtcTargetRegister;\r
16\r
17//\r
18// Days of month.\r
19//\r
20UINTN mDayOfMonth[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };\r
21\r
22//\r
23// The name of NV variable to store the timezone and daylight saving information.\r
24//\r
25CHAR16 mTimeZoneVariableName[] = L"RTC";\r
26\r
27/**\r
28 Compare the Hour, Minute and Second of the From time and the To time.\r
29\r
30 Only compare H/M/S in EFI_TIME and ignore other fields here.\r
31\r
32 @param From the first time\r
33 @param To the second time\r
34\r
35 @return >0 The H/M/S of the From time is later than those of To time\r
36 @return ==0 The H/M/S of the From time is same as those of To time\r
37 @return <0 The H/M/S of the From time is earlier than those of To time\r
38**/\r
39INTN\r
40CompareHMS (\r
41 IN EFI_TIME *From,\r
42 IN EFI_TIME *To\r
43 );\r
44\r
45/**\r
46 To check if second date is later than first date within 24 hours.\r
47\r
48 @param From the first date\r
49 @param To the second date\r
50\r
51 @retval TRUE From is previous to To within 24 hours.\r
52 @retval FALSE From is later, or it is previous to To more than 24 hours.\r
53**/\r
54BOOLEAN\r
55IsWithinOneDay (\r
56 IN EFI_TIME *From,\r
57 IN EFI_TIME *To\r
58 );\r
59\r
60/**\r
61 Read RTC content through its registers using IO access.\r
62\r
63 @param Address Address offset of RTC. It is recommended to use\r
64 macros such as RTC_ADDRESS_SECONDS.\r
65\r
66 @return The data of UINT8 type read from RTC.\r
67**/\r
68STATIC\r
69UINT8\r
70IoRtcRead (\r
71 IN UINTN Address\r
72 )\r
73{\r
74 IoWrite8 (\r
75 PcdGet8 (PcdRtcIndexRegister),\r
76 (UINT8)(Address | (UINT8)(IoRead8 (PcdGet8 (PcdRtcIndexRegister)) & 0x80))\r
77 );\r
78 return IoRead8 (PcdGet8 (PcdRtcTargetRegister));\r
79}\r
80\r
81/**\r
82 Write RTC through its registers using IO access.\r
83\r
84 @param Address Address offset of RTC. It is recommended to use\r
85 macros such as RTC_ADDRESS_SECONDS.\r
86 @param Data The content you want to write into RTC.\r
87\r
88**/\r
89STATIC\r
90VOID\r
91IoRtcWrite (\r
92 IN UINTN Address,\r
93 IN UINT8 Data\r
94 )\r
95{\r
96 IoWrite8 (\r
97 PcdGet8 (PcdRtcIndexRegister),\r
98 (UINT8)(Address | (UINT8)(IoRead8 (PcdGet8 (PcdRtcIndexRegister)) & 0x80))\r
99 );\r
100 IoWrite8 (PcdGet8 (PcdRtcTargetRegister), Data);\r
101}\r
102\r
103/**\r
104 Read RTC content through its registers using MMIO access.\r
105\r
106 @param Address Address offset of RTC. It is recommended to use\r
107 macros such as RTC_ADDRESS_SECONDS.\r
108\r
109 @return The data of UINT8 type read from RTC.\r
110**/\r
111STATIC\r
112UINT8\r
113MmioRtcRead (\r
114 IN UINTN Address\r
115 )\r
116{\r
117 MmioWrite8 (\r
118 mRtcIndexRegister,\r
119 (UINT8)(Address | (UINT8)(MmioRead8 (mRtcIndexRegister) & 0x80))\r
120 );\r
121 return MmioRead8 (mRtcTargetRegister);\r
122}\r
123\r
124/**\r
125 Write RTC through its registers using MMIO access.\r
126\r
127 @param Address Address offset of RTC. It is recommended to use\r
128 macros such as RTC_ADDRESS_SECONDS.\r
129 @param Data The content you want to write into RTC.\r
130\r
131**/\r
132STATIC\r
133VOID\r
134MmioRtcWrite (\r
135 IN UINTN Address,\r
136 IN UINT8 Data\r
137 )\r
138{\r
139 MmioWrite8 (\r
140 mRtcIndexRegister,\r
141 (UINT8)(Address | (UINT8)(MmioRead8 (mRtcIndexRegister) & 0x80))\r
142 );\r
143 MmioWrite8 (mRtcTargetRegister, Data);\r
144}\r
145\r
146/**\r
147 Read RTC content through its registers.\r
148\r
149 @param Address Address offset of RTC. It is recommended to use\r
150 macros such as RTC_ADDRESS_SECONDS.\r
151\r
152 @return The data of UINT8 type read from RTC.\r
153**/\r
154STATIC\r
155UINT8\r
156RtcRead (\r
157 IN UINTN Address\r
158 )\r
159{\r
160 if (FeaturePcdGet (PcdRtcUseMmio)) {\r
161 return MmioRtcRead (Address);\r
162 }\r
163\r
164 return IoRtcRead (Address);\r
165}\r
166\r
167/**\r
168 Write RTC through its registers.\r
169\r
170 @param Address Address offset of RTC. It is recommended to use\r
171 macros such as RTC_ADDRESS_SECONDS.\r
172 @param Data The content you want to write into RTC.\r
173\r
174**/\r
175STATIC\r
176VOID\r
177RtcWrite (\r
178 IN UINTN Address,\r
179 IN UINT8 Data\r
180 )\r
181{\r
182 if (FeaturePcdGet (PcdRtcUseMmio)) {\r
183 MmioRtcWrite (Address, Data);\r
184 } else {\r
185 IoRtcWrite (Address, Data);\r
186 }\r
187}\r
188\r
189/**\r
190 Initialize RTC.\r
191\r
192 @param Global For global use inside this module.\r
193\r
194 @retval EFI_DEVICE_ERROR Initialization failed due to device error.\r
195 @retval EFI_SUCCESS Initialization successful.\r
196\r
197**/\r
198EFI_STATUS\r
199PcRtcInit (\r
200 IN PC_RTC_MODULE_GLOBALS *Global\r
201 )\r
202{\r
203 EFI_STATUS Status;\r
204 RTC_REGISTER_A RegisterA;\r
205 RTC_REGISTER_B RegisterB;\r
206 RTC_REGISTER_D RegisterD;\r
207 EFI_TIME Time;\r
208 UINTN DataSize;\r
209 UINT32 TimerVar;\r
210 BOOLEAN Enabled;\r
211 BOOLEAN Pending;\r
212\r
213 //\r
214 // Acquire RTC Lock to make access to RTC atomic\r
215 //\r
216 if (!EfiAtRuntime ()) {\r
217 EfiAcquireLock (&Global->RtcLock);\r
218 }\r
219\r
220 //\r
221 // Initialize RTC Register\r
222 //\r
223 // Make sure Division Chain is properly configured,\r
224 // or RTC clock won't "tick" -- time won't increment\r
225 //\r
226 RegisterA.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterA);\r
227 RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);\r
228\r
229 //\r
230 // Read Register B\r
231 //\r
232 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
233\r
234 //\r
235 // Clear RTC flag register\r
236 //\r
237 RtcRead (RTC_ADDRESS_REGISTER_C);\r
238\r
239 //\r
240 // Clear RTC register D\r
241 //\r
242 RegisterD.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterD);\r
243 RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data);\r
244\r
245 //\r
246 // Wait for up to 0.1 seconds for the RTC to be updated\r
247 //\r
248 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
249 if (EFI_ERROR (Status)) {\r
250 //\r
251 // Set the variable with default value if the RTC is functioning incorrectly.\r
252 //\r
253 Global->SavedTimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
254 Global->Daylight = 0;\r
255 if (!EfiAtRuntime ()) {\r
256 EfiReleaseLock (&Global->RtcLock);\r
257 }\r
258\r
259 return EFI_DEVICE_ERROR;\r
260 }\r
261\r
262 //\r
263 // Get the Time/Date/Daylight Savings values.\r
264 //\r
265 Time.Second = RtcRead (RTC_ADDRESS_SECONDS);\r
266 Time.Minute = RtcRead (RTC_ADDRESS_MINUTES);\r
267 Time.Hour = RtcRead (RTC_ADDRESS_HOURS);\r
268 Time.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
269 Time.Month = RtcRead (RTC_ADDRESS_MONTH);\r
270 Time.Year = RtcRead (RTC_ADDRESS_YEAR);\r
271\r
272 //\r
273 // Release RTC Lock.\r
274 //\r
275 if (!EfiAtRuntime ()) {\r
276 EfiReleaseLock (&Global->RtcLock);\r
277 }\r
278\r
279 //\r
280 // Get the data of Daylight saving and time zone, if they have been\r
281 // stored in NV variable during previous boot.\r
282 //\r
283 DataSize = sizeof (UINT32);\r
284 Status = EfiGetVariable (\r
285 mTimeZoneVariableName,\r
286 &gEfiCallerIdGuid,\r
287 NULL,\r
288 &DataSize,\r
289 &TimerVar\r
290 );\r
291 if (!EFI_ERROR (Status)) {\r
292 Time.TimeZone = (INT16)TimerVar;\r
293 Time.Daylight = (UINT8)(TimerVar >> 16);\r
294 } else {\r
295 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
296 Time.Daylight = 0;\r
297 }\r
298\r
299 //\r
300 // Validate time fields\r
301 //\r
302 Status = ConvertRtcTimeToEfiTime (&Time, RegisterB);\r
303 if (!EFI_ERROR (Status)) {\r
304 Status = RtcTimeFieldsValid (&Time);\r
305 }\r
306\r
307 if (EFI_ERROR (Status)) {\r
308 //\r
309 // Report Status Code to indicate that the RTC has bad date and time\r
310 //\r
311 REPORT_STATUS_CODE (\r
312 EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
313 (EFI_SOFTWARE_DXE_RT_DRIVER | EFI_SW_EC_BAD_DATE_TIME)\r
314 );\r
315 Time.Second = RTC_INIT_SECOND;\r
316 Time.Minute = RTC_INIT_MINUTE;\r
317 Time.Hour = RTC_INIT_HOUR;\r
318 Time.Day = RTC_INIT_DAY;\r
319 Time.Month = RTC_INIT_MONTH;\r
320 Time.Year = PcdGet16 (PcdMinimalValidYear);\r
321 Time.Nanosecond = 0;\r
322 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
323 Time.Daylight = 0;\r
324 }\r
325\r
326 //\r
327 // Set RTC configuration after get original time\r
328 // The value of bit AIE should be reserved.\r
329 //\r
330 RegisterB.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterB) | (RegisterB.Data & BIT5);\r
331 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
332\r
333 //\r
334 // Reset time value according to new RTC configuration\r
335 //\r
336 Status = PcRtcSetTime (&Time, Global);\r
337 if (EFI_ERROR (Status)) {\r
338 return EFI_DEVICE_ERROR;\r
339 }\r
340\r
341 //\r
342 // Reset wakeup time value to valid state when wakeup alarm is disabled and wakeup time is invalid.\r
343 // Global variable has already had valid SavedTimeZone and Daylight,\r
344 // so we can use them to get and set wakeup time.\r
345 //\r
346 Status = PcRtcGetWakeupTime (&Enabled, &Pending, &Time, Global);\r
347 if ((Enabled) || (!EFI_ERROR (Status))) {\r
348 return EFI_SUCCESS;\r
349 }\r
350\r
351 //\r
352 // When wakeup time is disabled and invalid, reset wakeup time register to valid state\r
353 // but keep wakeup alarm disabled.\r
354 //\r
355 Time.Second = RTC_INIT_SECOND;\r
356 Time.Minute = RTC_INIT_MINUTE;\r
357 Time.Hour = RTC_INIT_HOUR;\r
358 Time.Day = RTC_INIT_DAY;\r
359 Time.Month = RTC_INIT_MONTH;\r
360 Time.Year = PcdGet16 (PcdMinimalValidYear);\r
361 Time.Nanosecond = 0;\r
362 Time.TimeZone = Global->SavedTimeZone;\r
363 Time.Daylight = Global->Daylight;\r
364\r
365 //\r
366 // Acquire RTC Lock to make access to RTC atomic\r
367 //\r
368 if (!EfiAtRuntime ()) {\r
369 EfiAcquireLock (&Global->RtcLock);\r
370 }\r
371\r
372 //\r
373 // Wait for up to 0.1 seconds for the RTC to be updated\r
374 //\r
375 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
376 if (EFI_ERROR (Status)) {\r
377 if (!EfiAtRuntime ()) {\r
378 EfiReleaseLock (&Global->RtcLock);\r
379 }\r
380\r
381 return EFI_DEVICE_ERROR;\r
382 }\r
383\r
384 ConvertEfiTimeToRtcTime (&Time, RegisterB);\r
385\r
386 //\r
387 // Set the Y/M/D info to variable as it has no corresponding hw registers.\r
388 //\r
389 Status = EfiSetVariable (\r
390 L"RTCALARM",\r
391 &gEfiCallerIdGuid,\r
392 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
393 sizeof (Time),\r
394 &Time\r
395 );\r
396 if (EFI_ERROR (Status)) {\r
397 if (!EfiAtRuntime ()) {\r
398 EfiReleaseLock (&Global->RtcLock);\r
399 }\r
400\r
401 return EFI_DEVICE_ERROR;\r
402 }\r
403\r
404 //\r
405 // Inhibit updates of the RTC\r
406 //\r
407 RegisterB.Bits.Set = 1;\r
408 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
409\r
410 //\r
411 // Set RTC alarm time registers\r
412 //\r
413 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, Time.Second);\r
414 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, Time.Minute);\r
415 RtcWrite (RTC_ADDRESS_HOURS_ALARM, Time.Hour);\r
416\r
417 //\r
418 // Allow updates of the RTC registers\r
419 //\r
420 RegisterB.Bits.Set = 0;\r
421 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
422\r
423 //\r
424 // Release RTC Lock.\r
425 //\r
426 if (!EfiAtRuntime ()) {\r
427 EfiReleaseLock (&Global->RtcLock);\r
428 }\r
429\r
430 return EFI_SUCCESS;\r
431}\r
432\r
433/**\r
434 Returns the current time and date information, and the time-keeping capabilities\r
435 of the hardware platform.\r
436\r
437 @param Time A pointer to storage to receive a snapshot of the current time.\r
438 @param Capabilities An optional pointer to a buffer to receive the real time clock\r
439 device's capabilities.\r
440 @param Global For global use inside this module.\r
441\r
442 @retval EFI_SUCCESS The operation completed successfully.\r
443 @retval EFI_INVALID_PARAMETER Time is NULL.\r
444 @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.\r
445\r
446**/\r
447EFI_STATUS\r
448PcRtcGetTime (\r
449 OUT EFI_TIME *Time,\r
450 OUT EFI_TIME_CAPABILITIES *Capabilities OPTIONAL,\r
451 IN PC_RTC_MODULE_GLOBALS *Global\r
452 )\r
453{\r
454 EFI_STATUS Status;\r
455 RTC_REGISTER_B RegisterB;\r
456\r
457 //\r
458 // Check parameters for null pointer\r
459 //\r
460 if (Time == NULL) {\r
461 return EFI_INVALID_PARAMETER;\r
462 }\r
463\r
464 //\r
465 // Acquire RTC Lock to make access to RTC atomic\r
466 //\r
467 if (!EfiAtRuntime ()) {\r
468 EfiAcquireLock (&Global->RtcLock);\r
469 }\r
470\r
471 //\r
472 // Wait for up to 0.1 seconds for the RTC to be updated\r
473 //\r
474 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
475 if (EFI_ERROR (Status)) {\r
476 if (!EfiAtRuntime ()) {\r
477 EfiReleaseLock (&Global->RtcLock);\r
478 }\r
479\r
480 return Status;\r
481 }\r
482\r
483 //\r
484 // Read Register B\r
485 //\r
486 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
487\r
488 //\r
489 // Get the Time/Date/Daylight Savings values.\r
490 //\r
491 Time->Second = RtcRead (RTC_ADDRESS_SECONDS);\r
492 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES);\r
493 Time->Hour = RtcRead (RTC_ADDRESS_HOURS);\r
494 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
495 Time->Month = RtcRead (RTC_ADDRESS_MONTH);\r
496 Time->Year = RtcRead (RTC_ADDRESS_YEAR);\r
497\r
498 //\r
499 // Release RTC Lock.\r
500 //\r
501 if (!EfiAtRuntime ()) {\r
502 EfiReleaseLock (&Global->RtcLock);\r
503 }\r
504\r
505 //\r
506 // Get the variable that contains the TimeZone and Daylight fields\r
507 //\r
508 Time->TimeZone = Global->SavedTimeZone;\r
509 Time->Daylight = Global->Daylight;\r
510\r
511 //\r
512 // Make sure all field values are in correct range\r
513 //\r
514 Status = ConvertRtcTimeToEfiTime (Time, RegisterB);\r
515 if (!EFI_ERROR (Status)) {\r
516 Status = RtcTimeFieldsValid (Time);\r
517 }\r
518\r
519 if (EFI_ERROR (Status)) {\r
520 return EFI_DEVICE_ERROR;\r
521 }\r
522\r
523 //\r
524 // Fill in Capabilities if it was passed in\r
525 //\r
526 if (Capabilities != NULL) {\r
527 Capabilities->Resolution = 1;\r
528 //\r
529 // 1 hertz\r
530 //\r
531 Capabilities->Accuracy = 50000000;\r
532 //\r
533 // 50 ppm\r
534 //\r
535 Capabilities->SetsToZero = FALSE;\r
536 }\r
537\r
538 return EFI_SUCCESS;\r
539}\r
540\r
541/**\r
542 Sets the current local time and date information.\r
543\r
544 @param Time A pointer to the current time.\r
545 @param Global For global use inside this module.\r
546\r
547 @retval EFI_SUCCESS The operation completed successfully.\r
548 @retval EFI_INVALID_PARAMETER A time field is out of range.\r
549 @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.\r
550\r
551**/\r
552EFI_STATUS\r
553PcRtcSetTime (\r
554 IN EFI_TIME *Time,\r
555 IN PC_RTC_MODULE_GLOBALS *Global\r
556 )\r
557{\r
558 EFI_STATUS Status;\r
559 EFI_TIME RtcTime;\r
560 RTC_REGISTER_B RegisterB;\r
561 UINT32 TimerVar;\r
562\r
563 if (Time == NULL) {\r
564 return EFI_INVALID_PARAMETER;\r
565 }\r
566\r
567 //\r
568 // Make sure that the time fields are valid\r
569 //\r
570 Status = RtcTimeFieldsValid (Time);\r
571 if (EFI_ERROR (Status)) {\r
572 return Status;\r
573 }\r
574\r
575 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));\r
576\r
577 //\r
578 // Acquire RTC Lock to make access to RTC atomic\r
579 //\r
580 if (!EfiAtRuntime ()) {\r
581 EfiAcquireLock (&Global->RtcLock);\r
582 }\r
583\r
584 //\r
585 // Wait for up to 0.1 seconds for the RTC to be updated\r
586 //\r
587 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
588 if (EFI_ERROR (Status)) {\r
589 if (!EfiAtRuntime ()) {\r
590 EfiReleaseLock (&Global->RtcLock);\r
591 }\r
592\r
593 return Status;\r
594 }\r
595\r
596 //\r
597 // Write timezone and daylight to RTC variable\r
598 //\r
599 if ((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) && (Time->Daylight == 0)) {\r
600 Status = EfiSetVariable (\r
601 mTimeZoneVariableName,\r
602 &gEfiCallerIdGuid,\r
603 0,\r
604 0,\r
605 NULL\r
606 );\r
607 if (Status == EFI_NOT_FOUND) {\r
608 Status = EFI_SUCCESS;\r
609 }\r
610 } else {\r
611 TimerVar = Time->Daylight;\r
612 TimerVar = (UINT32)((TimerVar << 16) | (UINT16)(Time->TimeZone));\r
613 Status = EfiSetVariable (\r
614 mTimeZoneVariableName,\r
615 &gEfiCallerIdGuid,\r
616 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
617 sizeof (TimerVar),\r
618 &TimerVar\r
619 );\r
620 }\r
621\r
622 if (EFI_ERROR (Status)) {\r
623 if (!EfiAtRuntime ()) {\r
624 EfiReleaseLock (&Global->RtcLock);\r
625 }\r
626\r
627 return EFI_DEVICE_ERROR;\r
628 }\r
629\r
630 //\r
631 // Read Register B, and inhibit updates of the RTC\r
632 //\r
633 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
634 RegisterB.Bits.Set = 1;\r
635 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
636\r
637 //\r
638 // Store the century value to RTC before converting to BCD format.\r
639 //\r
640 if (Global->CenturyRtcAddress != 0) {\r
641 RtcWrite (Global->CenturyRtcAddress, DecimalToBcd8 ((UINT8)(RtcTime.Year / 100)));\r
642 }\r
643\r
644 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);\r
645\r
646 RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second);\r
647 RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute);\r
648 RtcWrite (RTC_ADDRESS_HOURS, RtcTime.Hour);\r
649 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH, RtcTime.Day);\r
650 RtcWrite (RTC_ADDRESS_MONTH, RtcTime.Month);\r
651 RtcWrite (RTC_ADDRESS_YEAR, (UINT8)RtcTime.Year);\r
652\r
653 //\r
654 // Allow updates of the RTC registers\r
655 //\r
656 RegisterB.Bits.Set = 0;\r
657 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
658\r
659 //\r
660 // Release RTC Lock.\r
661 //\r
662 if (!EfiAtRuntime ()) {\r
663 EfiReleaseLock (&Global->RtcLock);\r
664 }\r
665\r
666 //\r
667 // Set the variable that contains the TimeZone and Daylight fields\r
668 //\r
669 Global->SavedTimeZone = Time->TimeZone;\r
670 Global->Daylight = Time->Daylight;\r
671\r
672 return EFI_SUCCESS;\r
673}\r
674\r
675/**\r
676 Returns the current wakeup alarm clock setting.\r
677\r
678 @param Enabled Indicates if the alarm is currently enabled or disabled.\r
679 @param Pending Indicates if the alarm signal is pending and requires acknowledgment.\r
680 @param Time The current alarm setting.\r
681 @param Global For global use inside this module.\r
682\r
683 @retval EFI_SUCCESS The alarm settings were returned.\r
684 @retval EFI_INVALID_PARAMETER Enabled is NULL.\r
685 @retval EFI_INVALID_PARAMETER Pending is NULL.\r
686 @retval EFI_INVALID_PARAMETER Time is NULL.\r
687 @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.\r
688 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.\r
689\r
690**/\r
691EFI_STATUS\r
692PcRtcGetWakeupTime (\r
693 OUT BOOLEAN *Enabled,\r
694 OUT BOOLEAN *Pending,\r
695 OUT EFI_TIME *Time,\r
696 IN PC_RTC_MODULE_GLOBALS *Global\r
697 )\r
698{\r
699 EFI_STATUS Status;\r
700 RTC_REGISTER_B RegisterB;\r
701 RTC_REGISTER_C RegisterC;\r
702 EFI_TIME RtcTime;\r
703 UINTN DataSize;\r
704\r
705 //\r
706 // Check parameters for null pointers\r
707 //\r
708 if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) {\r
709 return EFI_INVALID_PARAMETER;\r
710 }\r
711\r
712 //\r
713 // Acquire RTC Lock to make access to RTC atomic\r
714 //\r
715 if (!EfiAtRuntime ()) {\r
716 EfiAcquireLock (&Global->RtcLock);\r
717 }\r
718\r
719 //\r
720 // Wait for up to 0.1 seconds for the RTC to be updated\r
721 //\r
722 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
723 if (EFI_ERROR (Status)) {\r
724 if (!EfiAtRuntime ()) {\r
725 EfiReleaseLock (&Global->RtcLock);\r
726 }\r
727\r
728 return EFI_DEVICE_ERROR;\r
729 }\r
730\r
731 //\r
732 // Read Register B and Register C\r
733 //\r
734 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
735 RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);\r
736\r
737 //\r
738 // Get the Time/Date/Daylight Savings values.\r
739 //\r
740 *Enabled = RegisterB.Bits.Aie;\r
741 *Pending = RegisterC.Bits.Af;\r
742\r
743 Time->Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);\r
744 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);\r
745 Time->Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);\r
746 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
747 Time->Month = RtcRead (RTC_ADDRESS_MONTH);\r
748 Time->Year = RtcRead (RTC_ADDRESS_YEAR);\r
749 Time->TimeZone = Global->SavedTimeZone;\r
750 Time->Daylight = Global->Daylight;\r
751\r
752 //\r
753 // Get the alarm info from variable\r
754 //\r
755 DataSize = sizeof (EFI_TIME);\r
756 Status = EfiGetVariable (\r
757 L"RTCALARM",\r
758 &gEfiCallerIdGuid,\r
759 NULL,\r
760 &DataSize,\r
761 &RtcTime\r
762 );\r
763 if (!EFI_ERROR (Status)) {\r
764 //\r
765 // The alarm variable exists. In this case, we read variable to get info.\r
766 //\r
767 Time->Day = RtcTime.Day;\r
768 Time->Month = RtcTime.Month;\r
769 Time->Year = RtcTime.Year;\r
770 }\r
771\r
772 //\r
773 // Release RTC Lock.\r
774 //\r
775 if (!EfiAtRuntime ()) {\r
776 EfiReleaseLock (&Global->RtcLock);\r
777 }\r
778\r
779 //\r
780 // Make sure all field values are in correct range\r
781 //\r
782 Status = ConvertRtcTimeToEfiTime (Time, RegisterB);\r
783 if (!EFI_ERROR (Status)) {\r
784 Status = RtcTimeFieldsValid (Time);\r
785 }\r
786\r
787 if (EFI_ERROR (Status)) {\r
788 return EFI_DEVICE_ERROR;\r
789 }\r
790\r
791 return EFI_SUCCESS;\r
792}\r
793\r
794/**\r
795 Sets the system wakeup alarm clock time.\r
796\r
797 @param Enabled Enable or disable the wakeup alarm.\r
798 @param Time If Enable is TRUE, the time to set the wakeup alarm for.\r
799 If Enable is FALSE, then this parameter is optional, and may be NULL.\r
800 @param Global For global use inside this module.\r
801\r
802 @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled.\r
803 If Enable is FALSE, then the wakeup alarm was disabled.\r
804 @retval EFI_INVALID_PARAMETER A time field is out of range.\r
805 @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.\r
806 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.\r
807\r
808**/\r
809EFI_STATUS\r
810PcRtcSetWakeupTime (\r
811 IN BOOLEAN Enable,\r
812 IN EFI_TIME *Time OPTIONAL,\r
813 IN PC_RTC_MODULE_GLOBALS *Global\r
814 )\r
815{\r
816 EFI_STATUS Status;\r
817 EFI_TIME RtcTime;\r
818 RTC_REGISTER_B RegisterB;\r
819 EFI_TIME_CAPABILITIES Capabilities;\r
820\r
821 ZeroMem (&RtcTime, sizeof (RtcTime));\r
822\r
823 if (Enable) {\r
824 if (Time == NULL) {\r
825 return EFI_INVALID_PARAMETER;\r
826 }\r
827\r
828 //\r
829 // Make sure that the time fields are valid\r
830 //\r
831 Status = RtcTimeFieldsValid (Time);\r
832 if (EFI_ERROR (Status)) {\r
833 return EFI_INVALID_PARAMETER;\r
834 }\r
835\r
836 //\r
837 // Just support set alarm time within 24 hours\r
838 //\r
839 PcRtcGetTime (&RtcTime, &Capabilities, Global);\r
840 Status = RtcTimeFieldsValid (&RtcTime);\r
841 if (EFI_ERROR (Status)) {\r
842 return EFI_DEVICE_ERROR;\r
843 }\r
844\r
845 if (!IsWithinOneDay (&RtcTime, Time)) {\r
846 return EFI_UNSUPPORTED;\r
847 }\r
848\r
849 //\r
850 // Make a local copy of the time and date\r
851 //\r
852 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));\r
853 }\r
854\r
855 //\r
856 // Acquire RTC Lock to make access to RTC atomic\r
857 //\r
858 if (!EfiAtRuntime ()) {\r
859 EfiAcquireLock (&Global->RtcLock);\r
860 }\r
861\r
862 //\r
863 // Wait for up to 0.1 seconds for the RTC to be updated\r
864 //\r
865 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
866 if (EFI_ERROR (Status)) {\r
867 if (!EfiAtRuntime ()) {\r
868 EfiReleaseLock (&Global->RtcLock);\r
869 }\r
870\r
871 return EFI_DEVICE_ERROR;\r
872 }\r
873\r
874 //\r
875 // Read Register B\r
876 //\r
877 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
878\r
879 if (Enable) {\r
880 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);\r
881 } else {\r
882 //\r
883 // if the alarm is disable, record the current setting.\r
884 //\r
885 RtcTime.Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);\r
886 RtcTime.Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);\r
887 RtcTime.Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);\r
888 RtcTime.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);\r
889 RtcTime.Month = RtcRead (RTC_ADDRESS_MONTH);\r
890 RtcTime.Year = RtcRead (RTC_ADDRESS_YEAR);\r
891 RtcTime.TimeZone = Global->SavedTimeZone;\r
892 RtcTime.Daylight = Global->Daylight;\r
893 }\r
894\r
895 //\r
896 // Set the Y/M/D info to variable as it has no corresponding hw registers.\r
897 //\r
898 Status = EfiSetVariable (\r
899 L"RTCALARM",\r
900 &gEfiCallerIdGuid,\r
901 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
902 sizeof (RtcTime),\r
903 &RtcTime\r
904 );\r
905 if (EFI_ERROR (Status)) {\r
906 if (!EfiAtRuntime ()) {\r
907 EfiReleaseLock (&Global->RtcLock);\r
908 }\r
909\r
910 return EFI_DEVICE_ERROR;\r
911 }\r
912\r
913 //\r
914 // Inhibit updates of the RTC\r
915 //\r
916 RegisterB.Bits.Set = 1;\r
917 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
918\r
919 if (Enable) {\r
920 //\r
921 // Set RTC alarm time\r
922 //\r
923 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);\r
924 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);\r
925 RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);\r
926\r
927 RegisterB.Bits.Aie = 1;\r
928 } else {\r
929 RegisterB.Bits.Aie = 0;\r
930 }\r
931\r
932 //\r
933 // Allow updates of the RTC registers\r
934 //\r
935 RegisterB.Bits.Set = 0;\r
936 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
937\r
938 //\r
939 // Release RTC Lock.\r
940 //\r
941 if (!EfiAtRuntime ()) {\r
942 EfiReleaseLock (&Global->RtcLock);\r
943 }\r
944\r
945 return EFI_SUCCESS;\r
946}\r
947\r
948/**\r
949 Checks an 8-bit BCD value, and converts to an 8-bit value if valid.\r
950\r
951 This function checks the 8-bit BCD value specified by Value.\r
952 If valid, the function converts it to an 8-bit value and returns it.\r
953 Otherwise, return 0xff.\r
954\r
955 @param Value The 8-bit BCD value to check and convert\r
956\r
957 @return The 8-bit value converted. Or 0xff if Value is invalid.\r
958\r
959**/\r
960UINT8\r
961CheckAndConvertBcd8ToDecimal8 (\r
962 IN UINT8 Value\r
963 )\r
964{\r
965 if ((Value < 0xa0) && ((Value & 0xf) < 0xa)) {\r
966 return BcdToDecimal8 (Value);\r
967 }\r
968\r
969 return 0xff;\r
970}\r
971\r
972/**\r
973 Converts time read from RTC to EFI_TIME format defined by UEFI spec.\r
974\r
975 This function converts raw time data read from RTC to the EFI_TIME format\r
976 defined by UEFI spec.\r
977 If data mode of RTC is BCD, then converts it to decimal,\r
978 If RTC is in 12-hour format, then converts it to 24-hour format.\r
979\r
980 @param Time On input, the time data read from RTC to convert\r
981 On output, the time converted to UEFI format\r
982 @param RegisterB Value of Register B of RTC, indicating data mode\r
983 and hour format.\r
984\r
985 @retval EFI_INVALID_PARAMETER Parameters passed in are invalid.\r
986 @retval EFI_SUCCESS Convert RTC time to EFI time successfully.\r
987\r
988**/\r
989EFI_STATUS\r
990ConvertRtcTimeToEfiTime (\r
991 IN OUT EFI_TIME *Time,\r
992 IN RTC_REGISTER_B RegisterB\r
993 )\r
994{\r
995 BOOLEAN IsPM;\r
996 UINT8 Century;\r
997\r
998 // IsPM only makes sense for 12-hour format.\r
999 if (RegisterB.Bits.Mil == 0) {\r
1000 if ((Time->Hour & 0x80) != 0) {\r
1001 IsPM = TRUE;\r
1002 } else {\r
1003 IsPM = FALSE;\r
1004 }\r
1005\r
1006 Time->Hour = (UINT8)(Time->Hour & 0x7f);\r
1007 }\r
1008\r
1009 if (RegisterB.Bits.Dm == 0) {\r
1010 Time->Year = CheckAndConvertBcd8ToDecimal8 ((UINT8)Time->Year);\r
1011 Time->Month = CheckAndConvertBcd8ToDecimal8 (Time->Month);\r
1012 Time->Day = CheckAndConvertBcd8ToDecimal8 (Time->Day);\r
1013 Time->Hour = CheckAndConvertBcd8ToDecimal8 (Time->Hour);\r
1014 Time->Minute = CheckAndConvertBcd8ToDecimal8 (Time->Minute);\r
1015 Time->Second = CheckAndConvertBcd8ToDecimal8 (Time->Second);\r
1016 }\r
1017\r
1018 if ((Time->Year == 0xff) || (Time->Month == 0xff) || (Time->Day == 0xff) ||\r
1019 (Time->Hour == 0xff) || (Time->Minute == 0xff) || (Time->Second == 0xff))\r
1020 {\r
1021 return EFI_INVALID_PARAMETER;\r
1022 }\r
1023\r
1024 //\r
1025 // For minimal/maximum year range [1970, 2069],\r
1026 // Century is 19 if RTC year >= 70,\r
1027 // Century is 20 otherwise.\r
1028 //\r
1029 Century = (UINT8)(PcdGet16 (PcdMinimalValidYear) / 100);\r
1030 if (Time->Year < PcdGet16 (PcdMinimalValidYear) % 100) {\r
1031 Century++;\r
1032 }\r
1033\r
1034 Time->Year = (UINT16)(Century * 100 + Time->Year);\r
1035\r
1036 //\r
1037 // If time is in 12 hour format, convert it to 24 hour format\r
1038 //\r
1039 if (RegisterB.Bits.Mil == 0) {\r
1040 if (IsPM && (Time->Hour < 12)) {\r
1041 Time->Hour = (UINT8)(Time->Hour + 12);\r
1042 }\r
1043\r
1044 if (!IsPM && (Time->Hour == 12)) {\r
1045 Time->Hour = 0;\r
1046 }\r
1047 }\r
1048\r
1049 Time->Nanosecond = 0;\r
1050\r
1051 return EFI_SUCCESS;\r
1052}\r
1053\r
1054/**\r
1055 Wait for a period for the RTC to be ready.\r
1056\r
1057 @param Timeout Tell how long it should take to wait.\r
1058\r
1059 @retval EFI_DEVICE_ERROR RTC device error.\r
1060 @retval EFI_SUCCESS RTC is updated and ready.\r
1061**/\r
1062EFI_STATUS\r
1063RtcWaitToUpdate (\r
1064 UINTN Timeout\r
1065 )\r
1066{\r
1067 RTC_REGISTER_A RegisterA;\r
1068 RTC_REGISTER_D RegisterD;\r
1069\r
1070 //\r
1071 // See if the RTC is functioning correctly\r
1072 //\r
1073 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);\r
1074\r
1075 if (RegisterD.Bits.Vrt == 0) {\r
1076 return EFI_DEVICE_ERROR;\r
1077 }\r
1078\r
1079 //\r
1080 // Wait for up to 0.1 seconds for the RTC to be ready.\r
1081 //\r
1082 Timeout = (Timeout / 10) + 1;\r
1083 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);\r
1084 while (RegisterA.Bits.Uip == 1 && Timeout > 0) {\r
1085 MicroSecondDelay (10);\r
1086 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);\r
1087 Timeout--;\r
1088 }\r
1089\r
1090 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);\r
1091 if ((Timeout == 0) || (RegisterD.Bits.Vrt == 0)) {\r
1092 return EFI_DEVICE_ERROR;\r
1093 }\r
1094\r
1095 return EFI_SUCCESS;\r
1096}\r
1097\r
1098/**\r
1099 See if all fields of a variable of EFI_TIME type is correct.\r
1100\r
1101 @param Time The time to be checked.\r
1102\r
1103 @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.\r
1104 @retval EFI_SUCCESS Time is a valid EFI_TIME variable.\r
1105\r
1106**/\r
1107EFI_STATUS\r
1108RtcTimeFieldsValid (\r
1109 IN EFI_TIME *Time\r
1110 )\r
1111{\r
1112 if ((Time->Year < PcdGet16 (PcdMinimalValidYear)) ||\r
1113 (Time->Year > PcdGet16 (PcdMaximalValidYear)) ||\r
1114 (Time->Month < 1) ||\r
1115 (Time->Month > 12) ||\r
1116 (!DayValid (Time)) ||\r
1117 (Time->Hour > 23) ||\r
1118 (Time->Minute > 59) ||\r
1119 (Time->Second > 59) ||\r
1120 (Time->Nanosecond > 999999999) ||\r
1121 (!((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) || ((Time->TimeZone >= -1440) && (Time->TimeZone <= 1440)))) ||\r
1122 ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0))\r
1123 {\r
1124 return EFI_INVALID_PARAMETER;\r
1125 }\r
1126\r
1127 return EFI_SUCCESS;\r
1128}\r
1129\r
1130/**\r
1131 See if field Day of an EFI_TIME is correct.\r
1132\r
1133 @param Time Its Day field is to be checked.\r
1134\r
1135 @retval TRUE Day field of Time is correct.\r
1136 @retval FALSE Day field of Time is NOT correct.\r
1137**/\r
1138BOOLEAN\r
1139DayValid (\r
1140 IN EFI_TIME *Time\r
1141 )\r
1142{\r
1143 //\r
1144 // The validity of Time->Month field should be checked before\r
1145 //\r
1146 ASSERT (Time->Month >= 1);\r
1147 ASSERT (Time->Month <= 12);\r
1148 if ((Time->Day < 1) ||\r
1149 (Time->Day > mDayOfMonth[Time->Month - 1]) ||\r
1150 ((Time->Month == 2) && (!IsLeapYear (Time) && (Time->Day > 28)))\r
1151 )\r
1152 {\r
1153 return FALSE;\r
1154 }\r
1155\r
1156 return TRUE;\r
1157}\r
1158\r
1159/**\r
1160 Check if it is a leap year.\r
1161\r
1162 @param Time The time to be checked.\r
1163\r
1164 @retval TRUE It is a leap year.\r
1165 @retval FALSE It is NOT a leap year.\r
1166**/\r
1167BOOLEAN\r
1168IsLeapYear (\r
1169 IN EFI_TIME *Time\r
1170 )\r
1171{\r
1172 if (Time->Year % 4 == 0) {\r
1173 if (Time->Year % 100 == 0) {\r
1174 if (Time->Year % 400 == 0) {\r
1175 return TRUE;\r
1176 } else {\r
1177 return FALSE;\r
1178 }\r
1179 } else {\r
1180 return TRUE;\r
1181 }\r
1182 } else {\r
1183 return FALSE;\r
1184 }\r
1185}\r
1186\r
1187/**\r
1188 Converts time from EFI_TIME format defined by UEFI spec to RTC format.\r
1189\r
1190 This function converts time from EFI_TIME format defined by UEFI spec to RTC format.\r
1191 If data mode of RTC is BCD, then converts EFI_TIME to it.\r
1192 If RTC is in 12-hour format, then converts EFI_TIME to it.\r
1193\r
1194 @param Time On input, the time data read from UEFI to convert\r
1195 On output, the time converted to RTC format\r
1196 @param RegisterB Value of Register B of RTC, indicating data mode\r
1197**/\r
1198VOID\r
1199ConvertEfiTimeToRtcTime (\r
1200 IN OUT EFI_TIME *Time,\r
1201 IN RTC_REGISTER_B RegisterB\r
1202 )\r
1203{\r
1204 BOOLEAN IsPM;\r
1205\r
1206 IsPM = TRUE;\r
1207 //\r
1208 // Adjust hour field if RTC is in 12 hour mode\r
1209 //\r
1210 if (RegisterB.Bits.Mil == 0) {\r
1211 if (Time->Hour < 12) {\r
1212 IsPM = FALSE;\r
1213 }\r
1214\r
1215 if (Time->Hour >= 13) {\r
1216 Time->Hour = (UINT8)(Time->Hour - 12);\r
1217 } else if (Time->Hour == 0) {\r
1218 Time->Hour = 12;\r
1219 }\r
1220 }\r
1221\r
1222 //\r
1223 // Set the Time/Date values.\r
1224 //\r
1225 Time->Year = (UINT16)(Time->Year % 100);\r
1226\r
1227 if (RegisterB.Bits.Dm == 0) {\r
1228 Time->Year = DecimalToBcd8 ((UINT8)Time->Year);\r
1229 Time->Month = DecimalToBcd8 (Time->Month);\r
1230 Time->Day = DecimalToBcd8 (Time->Day);\r
1231 Time->Hour = DecimalToBcd8 (Time->Hour);\r
1232 Time->Minute = DecimalToBcd8 (Time->Minute);\r
1233 Time->Second = DecimalToBcd8 (Time->Second);\r
1234 }\r
1235\r
1236 //\r
1237 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.\r
1238 //\r
1239 if ((RegisterB.Bits.Mil == 0) && IsPM) {\r
1240 Time->Hour = (UINT8)(Time->Hour | 0x80);\r
1241 }\r
1242}\r
1243\r
1244/**\r
1245 Compare the Hour, Minute and Second of the From time and the To time.\r
1246\r
1247 Only compare H/M/S in EFI_TIME and ignore other fields here.\r
1248\r
1249 @param From the first time\r
1250 @param To the second time\r
1251\r
1252 @return >0 The H/M/S of the From time is later than those of To time\r
1253 @return ==0 The H/M/S of the From time is same as those of To time\r
1254 @return <0 The H/M/S of the From time is earlier than those of To time\r
1255**/\r
1256INTN\r
1257CompareHMS (\r
1258 IN EFI_TIME *From,\r
1259 IN EFI_TIME *To\r
1260 )\r
1261{\r
1262 if ((From->Hour > To->Hour) ||\r
1263 ((From->Hour == To->Hour) && (From->Minute > To->Minute)) ||\r
1264 ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second)))\r
1265 {\r
1266 return 1;\r
1267 } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) {\r
1268 return 0;\r
1269 } else {\r
1270 return -1;\r
1271 }\r
1272}\r
1273\r
1274/**\r
1275 To check if second date is later than first date within 24 hours.\r
1276\r
1277 @param From the first date\r
1278 @param To the second date\r
1279\r
1280 @retval TRUE From is previous to To within 24 hours.\r
1281 @retval FALSE From is later, or it is previous to To more than 24 hours.\r
1282**/\r
1283BOOLEAN\r
1284IsWithinOneDay (\r
1285 IN EFI_TIME *From,\r
1286 IN EFI_TIME *To\r
1287 )\r
1288{\r
1289 BOOLEAN Adjacent;\r
1290\r
1291 Adjacent = FALSE;\r
1292\r
1293 //\r
1294 // The validity of From->Month field should be checked before\r
1295 //\r
1296 ASSERT (From->Month >= 1);\r
1297 ASSERT (From->Month <= 12);\r
1298\r
1299 if (From->Year == To->Year) {\r
1300 if (From->Month == To->Month) {\r
1301 if ((From->Day + 1) == To->Day) {\r
1302 if ((CompareHMS (From, To) >= 0)) {\r
1303 Adjacent = TRUE;\r
1304 }\r
1305 } else if (From->Day == To->Day) {\r
1306 if ((CompareHMS (From, To) <= 0)) {\r
1307 Adjacent = TRUE;\r
1308 }\r
1309 }\r
1310 } else if (((From->Month + 1) == To->Month) && (To->Day == 1)) {\r
1311 if ((From->Month == 2) && !IsLeapYear (From)) {\r
1312 if (From->Day == 28) {\r
1313 if ((CompareHMS (From, To) >= 0)) {\r
1314 Adjacent = TRUE;\r
1315 }\r
1316 }\r
1317 } else if (From->Day == mDayOfMonth[From->Month - 1]) {\r
1318 if ((CompareHMS (From, To) >= 0)) {\r
1319 Adjacent = TRUE;\r
1320 }\r
1321 }\r
1322 }\r
1323 } else if (((From->Year + 1) == To->Year) &&\r
1324 (From->Month == 12) &&\r
1325 (From->Day == 31) &&\r
1326 (To->Month == 1) &&\r
1327 (To->Day == 1))\r
1328 {\r
1329 if ((CompareHMS (From, To) >= 0)) {\r
1330 Adjacent = TRUE;\r
1331 }\r
1332 }\r
1333\r
1334 return Adjacent;\r
1335}\r
1336\r
1337/**\r
1338 Get the century RTC address from the ACPI FADT table.\r
1339\r
1340 @return The century RTC address or 0 if not found.\r
1341**/\r
1342UINT8\r
1343GetCenturyRtcAddress (\r
1344 VOID\r
1345 )\r
1346{\r
1347 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;\r
1348\r
1349 Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *)EfiLocateFirstAcpiTable (\r
1350 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE\r
1351 );\r
1352\r
1353 if ((Fadt != NULL) &&\r
1354 (Fadt->Century > RTC_ADDRESS_REGISTER_D) && (Fadt->Century < 0x80)\r
1355 )\r
1356 {\r
1357 return Fadt->Century;\r
1358 } else {\r
1359 return 0;\r
1360 }\r
1361}\r
1362\r
1363/**\r
1364 Notification function of ACPI Table change.\r
1365\r
1366 This is a notification function registered on ACPI Table change event.\r
1367 It saves the Century address stored in ACPI FADT table.\r
1368\r
1369 @param Event Event whose notification function is being invoked.\r
1370 @param Context Pointer to the notification function's context.\r
1371\r
1372**/\r
1373VOID\r
1374EFIAPI\r
1375PcRtcAcpiTableChangeCallback (\r
1376 IN EFI_EVENT Event,\r
1377 IN VOID *Context\r
1378 )\r
1379{\r
1380 EFI_STATUS Status;\r
1381 EFI_TIME Time;\r
1382 UINT8 CenturyRtcAddress;\r
1383 UINT8 Century;\r
1384\r
1385 CenturyRtcAddress = GetCenturyRtcAddress ();\r
1386 if ((CenturyRtcAddress != 0) && (mModuleGlobal.CenturyRtcAddress != CenturyRtcAddress)) {\r
1387 mModuleGlobal.CenturyRtcAddress = CenturyRtcAddress;\r
1388 Status = PcRtcGetTime (&Time, NULL, &mModuleGlobal);\r
1389 if (!EFI_ERROR (Status)) {\r
1390 Century = (UINT8)(Time.Year / 100);\r
1391 Century = DecimalToBcd8 (Century);\r
1392 DEBUG ((DEBUG_INFO, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century, mModuleGlobal.CenturyRtcAddress));\r
1393 RtcWrite (mModuleGlobal.CenturyRtcAddress, Century);\r
1394 }\r
1395 }\r
1396}\r