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