]> git.proxmox.com Git - mirror_edk2.git/blame - PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.c
BaseTools: Rename String to StringUtils.
[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
bf46bd46 4Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>\r
8794bf26
LD
5Copyright (c) 2017, AMD Inc. All rights reserved.<BR>\r
6\r
95d48e82 7This program and the accompanying materials\r
3cfb790c 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
8cd4d17c 14\r
fb0b259e 15**/\r
8cd4d17c 16\r
17#include "PcRtc.h"\r
18\r
bf46bd46
RN
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
8d85dc31 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
8cd4d17c 41INTN\r
42CompareHMS (\r
43 IN EFI_TIME *From,\r
44 IN EFI_TIME *To\r
45 );\r
46\r
8d85dc31 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
8cd4d17c 56BOOLEAN\r
57IsWithinOneDay (\r
58 IN EFI_TIME *From,\r
59 IN EFI_TIME *To\r
60 );\r
61\r
8d85dc31 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
8cd4d17c 70UINT8\r
71RtcRead (\r
72 IN UINT8 Address\r
73 )\r
8cd4d17c 74{\r
75 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));\r
76 return IoRead8 (PCAT_RTC_DATA_REGISTER);\r
77}\r
78\r
8d85dc31 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
8cd4d17c 87VOID\r
88RtcWrite (\r
89 IN UINT8 Address,\r
90 IN UINT8 Data\r
91 )\r
8cd4d17c 92{\r
93 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));\r
94 IoWrite8 (PCAT_RTC_DATA_REGISTER, Data);\r
95}\r
96\r
8d85dc31 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
8cd4d17c 106EFI_STATUS\r
107PcRtcInit (\r
108 IN PC_RTC_MODULE_GLOBALS *Global\r
109 )\r
8cd4d17c 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
8cd4d17c 115 EFI_TIME Time;\r
ec35e997
LG
116 UINTN DataSize;\r
117 UINT32 TimerVar;\r
d431bf6e
EL
118 BOOLEAN Enabled;\r
119 BOOLEAN Pending;\r
8cd4d17c 120\r
121 //\r
122 // Acquire RTC Lock to make access to RTC atomic\r
123 //\r
8cd4d17c 124 if (!EfiAtRuntime ()) {\r
8d85dc31 125 EfiAcquireLock (&Global->RtcLock);\r
8cd4d17c 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
8794bf26 133 RegisterA.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterA);\r
8cd4d17c 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
8794bf26 149 RegisterD.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterD);\r
8cd4d17c 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
f8ea3026 155 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
8cd4d17c 156 if (EFI_ERROR (Status)) {\r
6ff84d99 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
8cd4d17c 162 if (!EfiAtRuntime ()) {\r
8d85dc31 163 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 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
8cd4d17c 177 //\r
178 // Set RTC configuration after get original time\r
ec35e997 179 // The value of bit AIE should be reserved.\r
8cd4d17c 180 //\r
8794bf26 181 RegisterB.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterB) | (RegisterB.Data & BIT5);\r
3e2744e4 182 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
8cd4d17c 183\r
184 //\r
185 // Release RTC Lock.\r
186 //\r
8cd4d17c 187 if (!EfiAtRuntime ()) {\r
8d85dc31 188 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 189 }\r
254ba247 190 \r
6bfa178c 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
bf46bd46 197 mTimeZoneVariableName,\r
6bfa178c 198 &gEfiCallerIdGuid,\r
199 NULL,\r
200 &DataSize,\r
bf46bd46 201 &TimerVar\r
6bfa178c 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
8cd4d17c 211 //\r
212 // Validate time fields\r
213 //\r
fe320967 214 Status = ConvertRtcTimeToEfiTime (&Time, RegisterB);\r
254ba247 215 if (!EFI_ERROR (Status)) {\r
216 Status = RtcTimeFieldsValid (&Time);\r
217 }\r
8cd4d17c 218 if (EFI_ERROR (Status)) {\r
44d52203 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
8cd4d17c 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
fe320967 231 Time.Year = PcdGet16 (PcdMinimalValidYear);\r
2ba53a4f 232 Time.Nanosecond = 0;\r
9bb1fced 233 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
234 Time.Daylight = 0;\r
8cd4d17c 235 }\r
ec35e997 236\r
ec35e997 237 //\r
8cd4d17c 238 // Reset time value according to new RTC configuration\r
239 //\r
2ba53a4f 240 Status = PcRtcSetTime (&Time, Global);\r
d431bf6e
EL
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
2ba53a4f 252 return EFI_SUCCESS;\r
d431bf6e
EL
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
fe320967 264 Time.Year = PcdGet16 (PcdMinimalValidYear);\r
d431bf6e
EL
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
2ba53a4f 283 return EFI_DEVICE_ERROR;\r
284 }\r
fe320967
RN
285\r
286 ConvertEfiTimeToRtcTime (&Time, RegisterB);\r
d431bf6e
EL
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
8cd4d17c 331}\r
332\r
8d85dc31 333/**\r
334 Returns the current time and date information, and the time-keeping capabilities\r
335 of the hardware platform.\r
8cd4d17c 336\r
8d85dc31 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
8cd4d17c 341\r
8d85dc31 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
8cd4d17c 345\r
8d85dc31 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
8cd4d17c 353{\r
354 EFI_STATUS Status;\r
355 RTC_REGISTER_B RegisterB;\r
8cd4d17c 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
8cd4d17c 367 if (!EfiAtRuntime ()) {\r
8d85dc31 368 EfiAcquireLock (&Global->RtcLock);\r
8cd4d17c 369 }\r
370 //\r
371 // Wait for up to 0.1 seconds for the RTC to be updated\r
372 //\r
f8ea3026 373 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
8cd4d17c 374 if (EFI_ERROR (Status)) {\r
8cd4d17c 375 if (!EfiAtRuntime ()) {\r
8d85dc31 376 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 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
8cd4d17c 395 //\r
396 // Release RTC Lock.\r
397 //\r
8cd4d17c 398 if (!EfiAtRuntime ()) {\r
8d85dc31 399 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 400 }\r
6bfa178c 401\r
8cd4d17c 402 //\r
2d4117c0 403 // Get the variable that contains the TimeZone and Daylight fields\r
8cd4d17c 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
fe320967 411 Status = ConvertRtcTimeToEfiTime (Time, RegisterB);\r
254ba247 412 if (!EFI_ERROR (Status)) {\r
413 Status = RtcTimeFieldsValid (Time);\r
414 }\r
8cd4d17c 415 if (EFI_ERROR (Status)) {\r
416 return EFI_DEVICE_ERROR;\r
417 }\r
6bfa178c 418\r
8cd4d17c 419 //\r
420 // Fill in Capabilities if it was passed in\r
421 //\r
8d85dc31 422 if (Capabilities != NULL) {\r
8cd4d17c 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
8d85dc31 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
8cd4d17c 448EFI_STATUS\r
449PcRtcSetTime (\r
450 IN EFI_TIME *Time,\r
451 IN PC_RTC_MODULE_GLOBALS *Global\r
452 )\r
8cd4d17c 453{\r
454 EFI_STATUS Status;\r
455 EFI_TIME RtcTime;\r
456 RTC_REGISTER_B RegisterB;\r
ec35e997 457 UINT32 TimerVar;\r
8cd4d17c 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
8cd4d17c 475 if (!EfiAtRuntime ()) {\r
8d85dc31 476 EfiAcquireLock (&Global->RtcLock);\r
8cd4d17c 477 }\r
478 //\r
479 // Wait for up to 0.1 seconds for the RTC to be updated\r
480 //\r
f8ea3026 481 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
8cd4d17c 482 if (EFI_ERROR (Status)) {\r
8cd4d17c 483 if (!EfiAtRuntime ()) {\r
ca08e40b 484 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 485 }\r
486 return Status;\r
487 }\r
0e991a2f
EL
488 \r
489 //\r
490 // Write timezone and daylight to RTC variable\r
491 //\r
0119b066 492 if ((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) && (Time->Daylight == 0)) {\r
bf46bd46
RN
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
0e991a2f
EL
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
8cd4d17c 522 //\r
523 // Read Register B, and inhibit updates of the RTC\r
524 //\r
525 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
24115e44 526 RegisterB.Bits.Set = 1;\r
8cd4d17c 527 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
528\r
41628cbc
RN
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
fe320967 536 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);\r
8cd4d17c 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
8cd4d17c 544\r
545 //\r
546 // Allow updates of the RTC registers\r
547 //\r
24115e44 548 RegisterB.Bits.Set = 0;\r
8cd4d17c 549 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
550\r
551 //\r
552 // Release RTC Lock.\r
553 //\r
8cd4d17c 554 if (!EfiAtRuntime ()) {\r
8d85dc31 555 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 556 }\r
557 //\r
2d4117c0 558 // Set the variable that contains the TimeZone and Daylight fields\r
8cd4d17c 559 //\r
560 Global->SavedTimeZone = Time->TimeZone;\r
561 Global->Daylight = Time->Daylight;\r
ec35e997 562\r
ec35e997 563 return EFI_SUCCESS;\r
8cd4d17c 564}\r
565\r
8d85dc31 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
2d4117c0 570 @param Pending Indicates if the alarm signal is pending and requires acknowledgment.\r
8d85dc31 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
8cd4d17c 582EFI_STATUS\r
583PcRtcGetWakeupTime (\r
584 OUT BOOLEAN *Enabled,\r
585 OUT BOOLEAN *Pending,\r
586 OUT EFI_TIME *Time,\r
8d85dc31 587 IN PC_RTC_MODULE_GLOBALS *Global\r
8cd4d17c 588 )\r
8cd4d17c 589{\r
590 EFI_STATUS Status;\r
591 RTC_REGISTER_B RegisterB;\r
592 RTC_REGISTER_C RegisterC;\r
7018623c 593 EFI_TIME RtcTime;\r
594 UINTN DataSize;\r
8cd4d17c 595\r
596 //\r
2d4117c0 597 // Check parameters for null pointers\r
8cd4d17c 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
8cd4d17c 606 if (!EfiAtRuntime ()) {\r
8d85dc31 607 EfiAcquireLock (&Global->RtcLock);\r
8cd4d17c 608 }\r
609 //\r
610 // Wait for up to 0.1 seconds for the RTC to be updated\r
611 //\r
f8ea3026 612 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
8cd4d17c 613 if (EFI_ERROR (Status)) {\r
8cd4d17c 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
24115e44 628 *Enabled = RegisterB.Bits.Aie;\r
7018623c 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
8cd4d17c 639\r
8cd4d17c 640 //\r
7018623c 641 // Get the alarm info from variable\r
8cd4d17c 642 //\r
ffa1e783 643 DataSize = sizeof (EFI_TIME);\r
7018623c 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
8cd4d17c 658 }\r
6bfa178c 659\r
660 //\r
7018623c 661 // Release RTC Lock.\r
6bfa178c 662 //\r
7018623c 663 if (!EfiAtRuntime ()) {\r
664 EfiReleaseLock (&Global->RtcLock);\r
665 }\r
6bfa178c 666\r
8cd4d17c 667 //\r
668 // Make sure all field values are in correct range\r
669 //\r
fe320967 670 Status = ConvertRtcTimeToEfiTime (Time, RegisterB);\r
254ba247 671 if (!EFI_ERROR (Status)) {\r
672 Status = RtcTimeFieldsValid (Time);\r
673 }\r
8cd4d17c 674 if (EFI_ERROR (Status)) {\r
675 return EFI_DEVICE_ERROR;\r
676 }\r
677\r
8cd4d17c 678 return EFI_SUCCESS;\r
679}\r
680\r
8d85dc31 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
8cd4d17c 696EFI_STATUS\r
697PcRtcSetWakeupTime (\r
698 IN BOOLEAN Enable,\r
8d85dc31 699 IN EFI_TIME *Time, OPTIONAL\r
8cd4d17c 700 IN PC_RTC_MODULE_GLOBALS *Global\r
701 )\r
8cd4d17c 702{\r
703 EFI_STATUS Status;\r
704 EFI_TIME RtcTime;\r
705 RTC_REGISTER_B RegisterB;\r
8cd4d17c 706 EFI_TIME_CAPABILITIES Capabilities;\r
707\r
aca70749 708 ZeroMem (&RtcTime, sizeof (RtcTime));\r
7018623c 709\r
8cd4d17c 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
79864f54 726 Status = RtcTimeFieldsValid (&RtcTime);\r
727 if (EFI_ERROR (Status)) {\r
728 return EFI_DEVICE_ERROR;\r
729 }\r
8cd4d17c 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
8cd4d17c 742 if (!EfiAtRuntime ()) {\r
8d85dc31 743 EfiAcquireLock (&Global->RtcLock);\r
8cd4d17c 744 }\r
745 //\r
746 // Wait for up to 0.1 seconds for the RTC to be updated\r
747 //\r
f8ea3026 748 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
8cd4d17c 749 if (EFI_ERROR (Status)) {\r
8cd4d17c 750 if (!EfiAtRuntime ()) {\r
751 EfiReleaseLock (&Global->RtcLock);\r
752 }\r
753 return EFI_DEVICE_ERROR;\r
754 }\r
755 //\r
0e991a2f 756 // Read Register B\r
8cd4d17c 757 //\r
0e991a2f 758 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
8cd4d17c 759\r
760 if (Enable) {\r
fe320967 761 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);\r
8cd4d17c 762 } else {\r
7018623c 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
8cd4d17c 774 }\r
8cd4d17c 775\r
7018623c 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
0e991a2f
EL
787 if (!EfiAtRuntime ()) {\r
788 EfiReleaseLock (&Global->RtcLock);\r
789 }\r
7018623c 790 return EFI_DEVICE_ERROR;\r
791 }\r
0e991a2f
EL
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
7018623c 817\r
8cd4d17c 818 //\r
819 // Release RTC Lock.\r
820 //\r
8cd4d17c 821 if (!EfiAtRuntime ()) {\r
8d85dc31 822 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 823 }\r
824 return EFI_SUCCESS;\r
825}\r
826\r
8cd4d17c 827\r
254ba247 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
8d85dc31 835 @param Value The 8-bit BCD value to check and convert\r
254ba247 836\r
8d85dc31 837 @return The 8-bit value converted. Or 0xff if Value is invalid.\r
254ba247 838\r
839**/\r
840UINT8\r
841CheckAndConvertBcd8ToDecimal8 (\r
842 IN UINT8 Value\r
8cd4d17c 843 )\r
254ba247 844{\r
845 if ((Value < 0xa0) && ((Value & 0xf) < 0xa)) {\r
846 return BcdToDecimal8 (Value);\r
847 }\r
8cd4d17c 848\r
254ba247 849 return 0xff;\r
850}\r
8cd4d17c 851\r
254ba247 852/**\r
853 Converts time read from RTC to EFI_TIME format defined by UEFI spec.\r
8cd4d17c 854\r
254ba247 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
8cd4d17c 859\r
254ba247 860 @param Time On input, the time data read from RTC to convert\r
861 On output, the time converted to UEFI format\r
254ba247 862 @param RegisterB Value of Register B of RTC, indicating data mode\r
863 and hour format.\r
8cd4d17c 864\r
8d85dc31 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
254ba247 868**/\r
869EFI_STATUS\r
870ConvertRtcTimeToEfiTime (\r
871 IN OUT EFI_TIME *Time,\r
254ba247 872 IN RTC_REGISTER_B RegisterB\r
873 )\r
8cd4d17c 874{\r
8d85dc31 875 BOOLEAN IsPM;\r
fe320967 876 UINT8 Century;\r
8cd4d17c 877\r
21176834 878 if ((Time->Hour & 0x80) != 0) {\r
8d85dc31 879 IsPM = TRUE;\r
8cd4d17c 880 } else {\r
8d85dc31 881 IsPM = FALSE;\r
8cd4d17c 882 }\r
883\r
884 Time->Hour = (UINT8) (Time->Hour & 0x7f);\r
885\r
24115e44 886 if (RegisterB.Bits.Dm == 0) {\r
254ba247 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
254ba247 893 }\r
894\r
895 if (Time->Year == 0xff || Time->Month == 0xff || Time->Day == 0xff ||\r
fe320967 896 Time->Hour == 0xff || Time->Minute == 0xff || Time->Second == 0xff) {\r
254ba247 897 return EFI_INVALID_PARAMETER;\r
8cd4d17c 898 }\r
254ba247 899\r
fe320967
RN
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
254ba247 909 Time->Year = (UINT16) (Century * 100 + Time->Year);\r
910\r
8cd4d17c 911 //\r
912 // If time is in 12 hour format, convert it to 24 hour format\r
913 //\r
24115e44 914 if (RegisterB.Bits.Mil == 0) {\r
8d85dc31 915 if (IsPM && Time->Hour < 12) {\r
8cd4d17c 916 Time->Hour = (UINT8) (Time->Hour + 12);\r
917 }\r
918\r
8d85dc31 919 if (!IsPM && Time->Hour == 12) {\r
8cd4d17c 920 Time->Hour = 0;\r
921 }\r
922 }\r
923\r
924 Time->Nanosecond = 0;\r
254ba247 925\r
926 return EFI_SUCCESS;\r
8cd4d17c 927}\r
928\r
8d85dc31 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
8cd4d17c 937EFI_STATUS\r
938RtcWaitToUpdate (\r
939 UINTN Timeout\r
940 )\r
8cd4d17c 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
24115e44 950 if (RegisterD.Bits.Vrt == 0) {\r
8cd4d17c 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
24115e44 958 while (RegisterA.Bits.Uip == 1 && Timeout > 0) {\r
8cd4d17c 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
24115e44 965 if (Timeout == 0 || RegisterD.Bits.Vrt == 0) {\r
8cd4d17c 966 return EFI_DEVICE_ERROR;\r
967 }\r
968\r
969 return EFI_SUCCESS;\r
970}\r
971\r
8d85dc31 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
8cd4d17c 981EFI_STATUS\r
982RtcTimeFieldsValid (\r
983 IN EFI_TIME *Time\r
984 )\r
8cd4d17c 985{\r
1e5fff63
EL
986 if (Time->Year < PcdGet16 (PcdMinimalValidYear) ||\r
987 Time->Year > PcdGet16 (PcdMaximalValidYear) ||\r
8cd4d17c 988 Time->Month < 1 ||\r
989 Time->Month > 12 ||\r
6bfa178c 990 (!DayValid (Time)) ||\r
8cd4d17c 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
6bfa178c 996 ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0)) {\r
997 return EFI_INVALID_PARAMETER;\r
8cd4d17c 998 }\r
999\r
1000 return EFI_SUCCESS;\r
1001}\r
1002\r
8d85dc31 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
8cd4d17c 1011BOOLEAN\r
1012DayValid (\r
1013 IN EFI_TIME *Time\r
1014 )\r
8cd4d17c 1015{\r
96a5ac5b 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
8cd4d17c 1021 if (Time->Day < 1 ||\r
bf46bd46 1022 Time->Day > mDayOfMonth[Time->Month - 1] ||\r
8cd4d17c 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
8d85dc31 1031/**\r
2d4117c0 1032 Check if it is a leap year.\r
8d85dc31 1033\r
1034 @param Time The time to be checked.\r
1035\r
2d4117c0 1036 @retval TRUE It is a leap year.\r
1037 @retval FALSE It is NOT a leap year.\r
8d85dc31 1038**/\r
8cd4d17c 1039BOOLEAN\r
1040IsLeapYear (\r
1041 IN EFI_TIME *Time\r
1042 )\r
8cd4d17c 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
8d85dc31 1059/**\r
1060 Converts time from EFI_TIME format defined by UEFI spec to RTC's.\r
8cd4d17c 1061\r
8d85dc31 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
8cd4d17c 1065\r
8d85dc31 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
8d85dc31 1069**/\r
1070VOID\r
1071ConvertEfiTimeToRtcTime (\r
1072 IN OUT EFI_TIME *Time,\r
fe320967 1073 IN RTC_REGISTER_B RegisterB\r
8d85dc31 1074 )\r
8cd4d17c 1075{\r
8d85dc31 1076 BOOLEAN IsPM;\r
8cd4d17c 1077\r
8d85dc31 1078 IsPM = TRUE;\r
8cd4d17c 1079 //\r
8d85dc31 1080 // Adjust hour field if RTC is in 12 hour mode\r
8cd4d17c 1081 //\r
24115e44 1082 if (RegisterB.Bits.Mil == 0) {\r
8cd4d17c 1083 if (Time->Hour < 12) {\r
8d85dc31 1084 IsPM = FALSE;\r
8cd4d17c 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
fe320967 1094 // Set the Time/Date values.\r
8cd4d17c 1095 //\r
8cd4d17c 1096 Time->Year = (UINT16) (Time->Year % 100);\r
1097\r
24115e44 1098 if (RegisterB.Bits.Dm == 0) {\r
8cd4d17c 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
24115e44 1109 if (RegisterB.Bits.Mil == 0 && IsPM) {\r
8cd4d17c 1110 Time->Hour = (UINT8) (Time->Hour | 0x80);\r
1111 }\r
1112}\r
1113\r
8d85dc31 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
8cd4d17c 1126INTN\r
1127CompareHMS (\r
1128 IN EFI_TIME *From,\r
1129 IN EFI_TIME *To\r
1130 )\r
8cd4d17c 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
8d85dc31 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
8cd4d17c 1152BOOLEAN\r
1153IsWithinOneDay (\r
1154 IN EFI_TIME *From,\r
1155 IN EFI_TIME *To\r
1156 )\r
8cd4d17c 1157{\r
1f4cf7b1 1158 BOOLEAN Adjacent;\r
1159\r
1f4cf7b1 1160 Adjacent = FALSE;\r
8cd4d17c 1161\r
96a5ac5b 1162 //\r
4cfc3293 1163 // The validity of From->Month field should be checked before\r
96a5ac5b 1164 //\r
1165 ASSERT (From->Month >=1);\r
1166 ASSERT (From->Month <=12);\r
1167 \r
8cd4d17c 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
bf46bd46 1186 } else if (From->Day == mDayOfMonth[From->Month - 1]) {\r
8cd4d17c 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
41628cbc
RN
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
5ce6fb99
RN
1235\r
1236 if (Table == NULL) {\r
1237 continue;\r
ea99ba10 1238 }\r
5ce6fb99 1239\r
41628cbc
RN
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
9e11e922 1249 Get the century RTC address from the ACPI FADT table.\r
41628cbc 1250\r
9e11e922 1251 @return The century RTC address or 0 if not found.\r
41628cbc 1252**/\r
9e11e922
RN
1253UINT8\r
1254GetCenturyRtcAddress (\r
1255 VOID\r
41628cbc
RN
1256 )\r
1257{\r
1258 EFI_STATUS Status;\r
1259 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp;\r
41628cbc 1260 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;\r
41628cbc
RN
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
c8ecaaf5 1267 if (EFI_ERROR (Status) || (Rsdp == NULL)) {\r
9e11e922 1268 return 0;\r
41628cbc
RN
1269 }\r
1270\r
c8ecaaf5 1271 Fadt = NULL;\r
0e3f62b6 1272\r
41628cbc
RN
1273 //\r
1274 // Find FADT in XSDT\r
1275 //\r
c8ecaaf5
RN
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
41628cbc
RN
1282 }\r
1283\r
c8ecaaf5
RN
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
41628cbc
RN
1293 }\r
1294\r
1295 if ((Fadt != NULL) &&\r
9e11e922 1296 (Fadt->Century > RTC_ADDRESS_REGISTER_D) && (Fadt->Century < 0x80)\r
41628cbc 1297 ) {\r
9e11e922
RN
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
41628cbc
RN
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