]> git.proxmox.com Git - mirror_edk2.git/blame - PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.c
PcAtChipsetPkg: Apply uncrustify changes
[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
36dd3c78 4Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
8794bf26 5Copyright (c) 2017, AMD Inc. All rights reserved.<BR>\r
015be407 6Copyright (c) 2018 - 2020, ARM Limited. All rights reserved.<BR>\r
8794bf26 7\r
e1d302e5 8SPDX-License-Identifier: BSD-2-Clause-Patent\r
8cd4d17c 9\r
fb0b259e 10**/\r
8cd4d17c 11\r
12#include "PcRtc.h"\r
13\r
015be407
SM
14extern UINTN mRtcIndexRegister;\r
15extern UINTN mRtcTargetRegister;\r
16\r
bf46bd46
RN
17//\r
18// Days of month.\r
19//\r
5220bd21 20UINTN mDayOfMonth[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };\r
bf46bd46
RN
21\r
22//\r
23// The name of NV variable to store the timezone and daylight saving information.\r
24//\r
5220bd21 25CHAR16 mTimeZoneVariableName[] = L"RTC";\r
bf46bd46 26\r
8d85dc31 27/**\r
28 Compare the Hour, Minute and Second of the From time and the To time.\r
5a702acd 29\r
8d85dc31 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
8cd4d17c 39INTN\r
40CompareHMS (\r
5220bd21
MK
41 IN EFI_TIME *From,\r
42 IN EFI_TIME *To\r
8cd4d17c 43 );\r
44\r
8d85dc31 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
8cd4d17c 54BOOLEAN\r
55IsWithinOneDay (\r
5220bd21
MK
56 IN EFI_TIME *From,\r
57 IN EFI_TIME *To\r
8cd4d17c 58 );\r
59\r
015be407
SM
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
5220bd21 71 IN UINTN Address\r
015be407
SM
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
5220bd21
MK
92 IN UINTN Address,\r
93 IN UINT8 Data\r
015be407
SM
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
5220bd21 114 IN UINTN Address\r
015be407
SM
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
5220bd21
MK
135 IN UINTN Address,\r
136 IN UINT8 Data\r
015be407
SM
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
8d85dc31 146/**\r
147 Read RTC content through its registers.\r
148\r
015be407
SM
149 @param Address Address offset of RTC. It is recommended to use\r
150 macros such as RTC_ADDRESS_SECONDS.\r
8d85dc31 151\r
152 @return The data of UINT8 type read from RTC.\r
153**/\r
015be407 154STATIC\r
8cd4d17c 155UINT8\r
156RtcRead (\r
5220bd21 157 IN UINTN Address\r
8cd4d17c 158 )\r
8cd4d17c 159{\r
015be407
SM
160 if (FeaturePcdGet (PcdRtcUseMmio)) {\r
161 return MmioRtcRead (Address);\r
162 }\r
163\r
164 return IoRtcRead (Address);\r
8cd4d17c 165}\r
166\r
8d85dc31 167/**\r
168 Write RTC through its registers.\r
169\r
015be407
SM
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
8d85dc31 173\r
174**/\r
015be407 175STATIC\r
8cd4d17c 176VOID\r
177RtcWrite (\r
5220bd21
MK
178 IN UINTN Address,\r
179 IN UINT8 Data\r
8cd4d17c 180 )\r
8cd4d17c 181{\r
015be407
SM
182 if (FeaturePcdGet (PcdRtcUseMmio)) {\r
183 MmioRtcWrite (Address, Data);\r
184 } else {\r
185 IoRtcWrite (Address, Data);\r
186 }\r
8cd4d17c 187}\r
188\r
8d85dc31 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
8cd4d17c 198EFI_STATUS\r
199PcRtcInit (\r
200 IN PC_RTC_MODULE_GLOBALS *Global\r
201 )\r
8cd4d17c 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
8cd4d17c 207 EFI_TIME Time;\r
ec35e997
LG
208 UINTN DataSize;\r
209 UINT32 TimerVar;\r
d431bf6e
EL
210 BOOLEAN Enabled;\r
211 BOOLEAN Pending;\r
8cd4d17c 212\r
213 //\r
214 // Acquire RTC Lock to make access to RTC atomic\r
215 //\r
8cd4d17c 216 if (!EfiAtRuntime ()) {\r
8d85dc31 217 EfiAcquireLock (&Global->RtcLock);\r
8cd4d17c 218 }\r
5220bd21 219\r
8cd4d17c 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
8794bf26 226 RegisterA.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterA);\r
8cd4d17c 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
8794bf26 242 RegisterD.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterD);\r
8cd4d17c 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
f8ea3026 248 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
8cd4d17c 249 if (EFI_ERROR (Status)) {\r
6ff84d99 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
8cd4d17c 255 if (!EfiAtRuntime ()) {\r
8d85dc31 256 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 257 }\r
5220bd21 258\r
8cd4d17c 259 return EFI_DEVICE_ERROR;\r
260 }\r
5220bd21 261\r
8cd4d17c 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
8cd4d17c 272 //\r
273 // Set RTC configuration after get original time\r
ec35e997 274 // The value of bit AIE should be reserved.\r
8cd4d17c 275 //\r
8794bf26 276 RegisterB.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterB) | (RegisterB.Data & BIT5);\r
3e2744e4 277 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
8cd4d17c 278\r
279 //\r
280 // Release RTC Lock.\r
281 //\r
8cd4d17c 282 if (!EfiAtRuntime ()) {\r
8d85dc31 283 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 284 }\r
5a702acd 285\r
6bfa178c 286 //\r
287 // Get the data of Daylight saving and time zone, if they have been\r
288 // stored in NV variable during previous boot.\r
289 //\r
290 DataSize = sizeof (UINT32);\r
5220bd21
MK
291 Status = EfiGetVariable (\r
292 mTimeZoneVariableName,\r
293 &gEfiCallerIdGuid,\r
294 NULL,\r
295 &DataSize,\r
296 &TimerVar\r
297 );\r
6bfa178c 298 if (!EFI_ERROR (Status)) {\r
5220bd21
MK
299 Time.TimeZone = (INT16)TimerVar;\r
300 Time.Daylight = (UINT8)(TimerVar >> 16);\r
6bfa178c 301 } else {\r
302 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
5a702acd 303 Time.Daylight = 0;\r
6bfa178c 304 }\r
305\r
8cd4d17c 306 //\r
307 // Validate time fields\r
308 //\r
fe320967 309 Status = ConvertRtcTimeToEfiTime (&Time, RegisterB);\r
254ba247 310 if (!EFI_ERROR (Status)) {\r
311 Status = RtcTimeFieldsValid (&Time);\r
312 }\r
5220bd21 313\r
8cd4d17c 314 if (EFI_ERROR (Status)) {\r
44d52203 315 //\r
316 // Report Status Code to indicate that the RTC has bad date and time\r
317 //\r
318 REPORT_STATUS_CODE (\r
319 EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
320 (EFI_SOFTWARE_DXE_RT_DRIVER | EFI_SW_EC_BAD_DATE_TIME)\r
321 );\r
5220bd21
MK
322 Time.Second = RTC_INIT_SECOND;\r
323 Time.Minute = RTC_INIT_MINUTE;\r
324 Time.Hour = RTC_INIT_HOUR;\r
325 Time.Day = RTC_INIT_DAY;\r
326 Time.Month = RTC_INIT_MONTH;\r
327 Time.Year = PcdGet16 (PcdMinimalValidYear);\r
328 Time.Nanosecond = 0;\r
329 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
330 Time.Daylight = 0;\r
8cd4d17c 331 }\r
ec35e997 332\r
ec35e997 333 //\r
8cd4d17c 334 // Reset time value according to new RTC configuration\r
335 //\r
2ba53a4f 336 Status = PcRtcSetTime (&Time, Global);\r
d431bf6e
EL
337 if (EFI_ERROR (Status)) {\r
338 return EFI_DEVICE_ERROR;\r
339 }\r
5a702acd 340\r
d431bf6e
EL
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
2ba53a4f 348 return EFI_SUCCESS;\r
d431bf6e 349 }\r
5a702acd 350\r
d431bf6e 351 //\r
5a702acd 352 // When wakeup time is disabled and invalid, reset wakeup time register to valid state\r
d431bf6e
EL
353 // but keep wakeup alarm disabled.\r
354 //\r
5220bd21
MK
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
d431bf6e
EL
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
5220bd21 371\r
d431bf6e
EL
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
5220bd21 378 EfiReleaseLock (&Global->RtcLock);\r
d431bf6e 379 }\r
5220bd21 380\r
2ba53a4f 381 return EFI_DEVICE_ERROR;\r
382 }\r
fe320967
RN
383\r
384 ConvertEfiTimeToRtcTime (&Time, RegisterB);\r
d431bf6e
EL
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
5220bd21 400\r
d431bf6e
EL
401 return EFI_DEVICE_ERROR;\r
402 }\r
5a702acd 403\r
d431bf6e
EL
404 //\r
405 // Inhibit updates of the RTC\r
406 //\r
5220bd21 407 RegisterB.Bits.Set = 1;\r
d431bf6e 408 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
5a702acd 409\r
d431bf6e
EL
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
5a702acd 422\r
d431bf6e
EL
423 //\r
424 // Release RTC Lock.\r
425 //\r
426 if (!EfiAtRuntime ()) {\r
427 EfiReleaseLock (&Global->RtcLock);\r
428 }\r
5220bd21 429\r
d431bf6e 430 return EFI_SUCCESS;\r
8cd4d17c 431}\r
432\r
8d85dc31 433/**\r
434 Returns the current time and date information, and the time-keeping capabilities\r
435 of the hardware platform.\r
8cd4d17c 436\r
8d85dc31 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
8cd4d17c 441\r
8d85dc31 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
8cd4d17c 445\r
8d85dc31 446**/\r
447EFI_STATUS\r
448PcRtcGetTime (\r
449 OUT EFI_TIME *Time,\r
237295f4 450 OUT EFI_TIME_CAPABILITIES *Capabilities OPTIONAL,\r
8d85dc31 451 IN PC_RTC_MODULE_GLOBALS *Global\r
452 )\r
8cd4d17c 453{\r
454 EFI_STATUS Status;\r
455 RTC_REGISTER_B RegisterB;\r
8cd4d17c 456\r
457 //\r
458 // Check parameters for null pointer\r
459 //\r
460 if (Time == NULL) {\r
461 return EFI_INVALID_PARAMETER;\r
8cd4d17c 462 }\r
5220bd21 463\r
8cd4d17c 464 //\r
465 // Acquire RTC Lock to make access to RTC atomic\r
466 //\r
8cd4d17c 467 if (!EfiAtRuntime ()) {\r
8d85dc31 468 EfiAcquireLock (&Global->RtcLock);\r
8cd4d17c 469 }\r
5220bd21 470\r
8cd4d17c 471 //\r
472 // Wait for up to 0.1 seconds for the RTC to be updated\r
473 //\r
f8ea3026 474 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
8cd4d17c 475 if (EFI_ERROR (Status)) {\r
5220bd21
MK
476 if (!EfiAtRuntime ()) {\r
477 EfiReleaseLock (&Global->RtcLock);\r
478 }\r
479\r
8cd4d17c 480 return Status;\r
481 }\r
5220bd21 482\r
8cd4d17c 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
5220bd21
MK
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
8cd4d17c 497\r
8cd4d17c 498 //\r
499 // Release RTC Lock.\r
500 //\r
8cd4d17c 501 if (!EfiAtRuntime ()) {\r
8d85dc31 502 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 503 }\r
6bfa178c 504\r
8cd4d17c 505 //\r
2d4117c0 506 // Get the variable that contains the TimeZone and Daylight fields\r
8cd4d17c 507 //\r
5220bd21
MK
508 Time->TimeZone = Global->SavedTimeZone;\r
509 Time->Daylight = Global->Daylight;\r
8cd4d17c 510\r
511 //\r
512 // Make sure all field values are in correct range\r
513 //\r
fe320967 514 Status = ConvertRtcTimeToEfiTime (Time, RegisterB);\r
254ba247 515 if (!EFI_ERROR (Status)) {\r
516 Status = RtcTimeFieldsValid (Time);\r
517 }\r
5220bd21 518\r
8cd4d17c 519 if (EFI_ERROR (Status)) {\r
520 return EFI_DEVICE_ERROR;\r
521 }\r
6bfa178c 522\r
8cd4d17c 523 //\r
524 // Fill in Capabilities if it was passed in\r
525 //\r
8d85dc31 526 if (Capabilities != NULL) {\r
8cd4d17c 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
8d85dc31 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
8cd4d17c 552EFI_STATUS\r
553PcRtcSetTime (\r
5220bd21
MK
554 IN EFI_TIME *Time,\r
555 IN PC_RTC_MODULE_GLOBALS *Global\r
8cd4d17c 556 )\r
8cd4d17c 557{\r
558 EFI_STATUS Status;\r
559 EFI_TIME RtcTime;\r
560 RTC_REGISTER_B RegisterB;\r
ec35e997 561 UINT32 TimerVar;\r
8cd4d17c 562\r
563 if (Time == NULL) {\r
564 return EFI_INVALID_PARAMETER;\r
565 }\r
5220bd21 566\r
8cd4d17c 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
8cd4d17c 580 if (!EfiAtRuntime ()) {\r
8d85dc31 581 EfiAcquireLock (&Global->RtcLock);\r
8cd4d17c 582 }\r
5220bd21 583\r
8cd4d17c 584 //\r
585 // Wait for up to 0.1 seconds for the RTC to be updated\r
586 //\r
f8ea3026 587 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
8cd4d17c 588 if (EFI_ERROR (Status)) {\r
5220bd21
MK
589 if (!EfiAtRuntime ()) {\r
590 EfiReleaseLock (&Global->RtcLock);\r
591 }\r
592\r
8cd4d17c 593 return Status;\r
594 }\r
5a702acd 595\r
0e991a2f
EL
596 //\r
597 // Write timezone and daylight to RTC variable\r
598 //\r
0119b066 599 if ((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) && (Time->Daylight == 0)) {\r
bf46bd46
RN
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
5220bd21
MK
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
bf46bd46
RN
620 }\r
621\r
0e991a2f
EL
622 if (EFI_ERROR (Status)) {\r
623 if (!EfiAtRuntime ()) {\r
624 EfiReleaseLock (&Global->RtcLock);\r
625 }\r
5220bd21 626\r
0e991a2f
EL
627 return EFI_DEVICE_ERROR;\r
628 }\r
629\r
8cd4d17c 630 //\r
631 // Read Register B, and inhibit updates of the RTC\r
632 //\r
5220bd21
MK
633 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
634 RegisterB.Bits.Set = 1;\r
8cd4d17c 635 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
636\r
41628cbc
RN
637 //\r
638 // Store the century value to RTC before converting to BCD format.\r
639 //\r
640 if (Global->CenturyRtcAddress != 0) {\r
5220bd21 641 RtcWrite (Global->CenturyRtcAddress, DecimalToBcd8 ((UINT8)(RtcTime.Year / 100)));\r
41628cbc
RN
642 }\r
643\r
fe320967 644 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);\r
8cd4d17c 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
5220bd21 651 RtcWrite (RTC_ADDRESS_YEAR, (UINT8)RtcTime.Year);\r
8cd4d17c 652\r
653 //\r
654 // Allow updates of the RTC registers\r
655 //\r
24115e44 656 RegisterB.Bits.Set = 0;\r
8cd4d17c 657 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
658\r
659 //\r
660 // Release RTC Lock.\r
661 //\r
8cd4d17c 662 if (!EfiAtRuntime ()) {\r
8d85dc31 663 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 664 }\r
5220bd21 665\r
8cd4d17c 666 //\r
2d4117c0 667 // Set the variable that contains the TimeZone and Daylight fields\r
8cd4d17c 668 //\r
669 Global->SavedTimeZone = Time->TimeZone;\r
670 Global->Daylight = Time->Daylight;\r
ec35e997 671\r
ec35e997 672 return EFI_SUCCESS;\r
8cd4d17c 673}\r
674\r
8d85dc31 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
2d4117c0 679 @param Pending Indicates if the alarm signal is pending and requires acknowledgment.\r
8d85dc31 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
8cd4d17c 691EFI_STATUS\r
692PcRtcGetWakeupTime (\r
693 OUT BOOLEAN *Enabled,\r
694 OUT BOOLEAN *Pending,\r
695 OUT EFI_TIME *Time,\r
8d85dc31 696 IN PC_RTC_MODULE_GLOBALS *Global\r
8cd4d17c 697 )\r
8cd4d17c 698{\r
699 EFI_STATUS Status;\r
700 RTC_REGISTER_B RegisterB;\r
701 RTC_REGISTER_C RegisterC;\r
7018623c 702 EFI_TIME RtcTime;\r
703 UINTN DataSize;\r
8cd4d17c 704\r
705 //\r
2d4117c0 706 // Check parameters for null pointers\r
8cd4d17c 707 //\r
708 if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) {\r
709 return EFI_INVALID_PARAMETER;\r
8cd4d17c 710 }\r
5220bd21 711\r
8cd4d17c 712 //\r
713 // Acquire RTC Lock to make access to RTC atomic\r
714 //\r
8cd4d17c 715 if (!EfiAtRuntime ()) {\r
8d85dc31 716 EfiAcquireLock (&Global->RtcLock);\r
8cd4d17c 717 }\r
5220bd21 718\r
8cd4d17c 719 //\r
720 // Wait for up to 0.1 seconds for the RTC to be updated\r
721 //\r
f8ea3026 722 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
8cd4d17c 723 if (EFI_ERROR (Status)) {\r
8cd4d17c 724 if (!EfiAtRuntime ()) {\r
5220bd21 725 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 726 }\r
5220bd21 727\r
8cd4d17c 728 return EFI_DEVICE_ERROR;\r
729 }\r
5220bd21 730\r
8cd4d17c 731 //\r
732 // Read Register B and Register C\r
733 //\r
5220bd21
MK
734 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
735 RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);\r
8cd4d17c 736\r
737 //\r
738 // Get the Time/Date/Daylight Savings values.\r
739 //\r
24115e44 740 *Enabled = RegisterB.Bits.Aie;\r
7018623c 741 *Pending = RegisterC.Bits.Af;\r
742\r
5220bd21
MK
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
7018623c 749 Time->TimeZone = Global->SavedTimeZone;\r
750 Time->Daylight = Global->Daylight;\r
8cd4d17c 751\r
8cd4d17c 752 //\r
7018623c 753 // Get the alarm info from variable\r
8cd4d17c 754 //\r
ffa1e783 755 DataSize = sizeof (EFI_TIME);\r
5220bd21
MK
756 Status = EfiGetVariable (\r
757 L"RTCALARM",\r
758 &gEfiCallerIdGuid,\r
759 NULL,\r
760 &DataSize,\r
761 &RtcTime\r
762 );\r
7018623c 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
8cd4d17c 770 }\r
6bfa178c 771\r
772 //\r
7018623c 773 // Release RTC Lock.\r
6bfa178c 774 //\r
7018623c 775 if (!EfiAtRuntime ()) {\r
776 EfiReleaseLock (&Global->RtcLock);\r
777 }\r
6bfa178c 778\r
8cd4d17c 779 //\r
780 // Make sure all field values are in correct range\r
781 //\r
fe320967 782 Status = ConvertRtcTimeToEfiTime (Time, RegisterB);\r
254ba247 783 if (!EFI_ERROR (Status)) {\r
784 Status = RtcTimeFieldsValid (Time);\r
785 }\r
5220bd21 786\r
8cd4d17c 787 if (EFI_ERROR (Status)) {\r
788 return EFI_DEVICE_ERROR;\r
789 }\r
790\r
8cd4d17c 791 return EFI_SUCCESS;\r
792}\r
793\r
8d85dc31 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
8cd4d17c 809EFI_STATUS\r
810PcRtcSetWakeupTime (\r
811 IN BOOLEAN Enable,\r
237295f4 812 IN EFI_TIME *Time OPTIONAL,\r
8cd4d17c 813 IN PC_RTC_MODULE_GLOBALS *Global\r
814 )\r
8cd4d17c 815{\r
5220bd21
MK
816 EFI_STATUS Status;\r
817 EFI_TIME RtcTime;\r
818 RTC_REGISTER_B RegisterB;\r
819 EFI_TIME_CAPABILITIES Capabilities;\r
8cd4d17c 820\r
aca70749 821 ZeroMem (&RtcTime, sizeof (RtcTime));\r
7018623c 822\r
8cd4d17c 823 if (Enable) {\r
8cd4d17c 824 if (Time == NULL) {\r
825 return EFI_INVALID_PARAMETER;\r
826 }\r
5220bd21 827\r
8cd4d17c 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
5220bd21 835\r
8cd4d17c 836 //\r
837 // Just support set alarm time within 24 hours\r
838 //\r
839 PcRtcGetTime (&RtcTime, &Capabilities, Global);\r
79864f54 840 Status = RtcTimeFieldsValid (&RtcTime);\r
841 if (EFI_ERROR (Status)) {\r
842 return EFI_DEVICE_ERROR;\r
843 }\r
5220bd21 844\r
8cd4d17c 845 if (!IsWithinOneDay (&RtcTime, Time)) {\r
846 return EFI_UNSUPPORTED;\r
847 }\r
5220bd21 848\r
8cd4d17c 849 //\r
850 // Make a local copy of the time and date\r
851 //\r
852 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));\r
8cd4d17c 853 }\r
5220bd21 854\r
8cd4d17c 855 //\r
856 // Acquire RTC Lock to make access to RTC atomic\r
857 //\r
8cd4d17c 858 if (!EfiAtRuntime ()) {\r
8d85dc31 859 EfiAcquireLock (&Global->RtcLock);\r
8cd4d17c 860 }\r
5220bd21 861\r
8cd4d17c 862 //\r
863 // Wait for up to 0.1 seconds for the RTC to be updated\r
864 //\r
f8ea3026 865 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));\r
8cd4d17c 866 if (EFI_ERROR (Status)) {\r
8cd4d17c 867 if (!EfiAtRuntime ()) {\r
5220bd21 868 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 869 }\r
5220bd21 870\r
8cd4d17c 871 return EFI_DEVICE_ERROR;\r
872 }\r
5220bd21 873\r
8cd4d17c 874 //\r
0e991a2f 875 // Read Register B\r
8cd4d17c 876 //\r
0e991a2f 877 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);\r
8cd4d17c 878\r
879 if (Enable) {\r
fe320967 880 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB);\r
8cd4d17c 881 } else {\r
7018623c 882 //\r
883 // if the alarm is disable, record the current setting.\r
884 //\r
5220bd21
MK
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
7018623c 891 RtcTime.TimeZone = Global->SavedTimeZone;\r
892 RtcTime.Daylight = Global->Daylight;\r
8cd4d17c 893 }\r
8cd4d17c 894\r
7018623c 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
0e991a2f
EL
906 if (!EfiAtRuntime ()) {\r
907 EfiReleaseLock (&Global->RtcLock);\r
908 }\r
5220bd21 909\r
7018623c 910 return EFI_DEVICE_ERROR;\r
911 }\r
5a702acd 912\r
0e991a2f
EL
913 //\r
914 // Inhibit updates of the RTC\r
915 //\r
5220bd21 916 RegisterB.Bits.Set = 1;\r
0e991a2f
EL
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
0e991a2f
EL
928 } else {\r
929 RegisterB.Bits.Aie = 0;\r
930 }\r
5220bd21 931\r
0e991a2f
EL
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
7018623c 937\r
8cd4d17c 938 //\r
939 // Release RTC Lock.\r
940 //\r
8cd4d17c 941 if (!EfiAtRuntime ()) {\r
8d85dc31 942 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 943 }\r
5220bd21 944\r
8cd4d17c 945 return EFI_SUCCESS;\r
946}\r
947\r
254ba247 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
8d85dc31 955 @param Value The 8-bit BCD value to check and convert\r
254ba247 956\r
8d85dc31 957 @return The 8-bit value converted. Or 0xff if Value is invalid.\r
254ba247 958\r
959**/\r
960UINT8\r
961CheckAndConvertBcd8ToDecimal8 (\r
962 IN UINT8 Value\r
8cd4d17c 963 )\r
254ba247 964{\r
965 if ((Value < 0xa0) && ((Value & 0xf) < 0xa)) {\r
966 return BcdToDecimal8 (Value);\r
967 }\r
8cd4d17c 968\r
254ba247 969 return 0xff;\r
970}\r
8cd4d17c 971\r
254ba247 972/**\r
973 Converts time read from RTC to EFI_TIME format defined by UEFI spec.\r
8cd4d17c 974\r
254ba247 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
8cd4d17c 979\r
254ba247 980 @param Time On input, the time data read from RTC to convert\r
981 On output, the time converted to UEFI format\r
254ba247 982 @param RegisterB Value of Register B of RTC, indicating data mode\r
983 and hour format.\r
8cd4d17c 984\r
8d85dc31 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
254ba247 988**/\r
989EFI_STATUS\r
990ConvertRtcTimeToEfiTime (\r
991 IN OUT EFI_TIME *Time,\r
254ba247 992 IN RTC_REGISTER_B RegisterB\r
993 )\r
8cd4d17c 994{\r
5220bd21
MK
995 BOOLEAN IsPM;\r
996 UINT8 Century;\r
8cd4d17c 997\r
21176834 998 if ((Time->Hour & 0x80) != 0) {\r
8d85dc31 999 IsPM = TRUE;\r
8cd4d17c 1000 } else {\r
8d85dc31 1001 IsPM = FALSE;\r
8cd4d17c 1002 }\r
1003\r
5220bd21 1004 Time->Hour = (UINT8)(Time->Hour & 0x7f);\r
8cd4d17c 1005\r
24115e44 1006 if (RegisterB.Bits.Dm == 0) {\r
5220bd21
MK
1007 Time->Year = CheckAndConvertBcd8ToDecimal8 ((UINT8)Time->Year);\r
1008 Time->Month = CheckAndConvertBcd8ToDecimal8 (Time->Month);\r
1009 Time->Day = CheckAndConvertBcd8ToDecimal8 (Time->Day);\r
1010 Time->Hour = CheckAndConvertBcd8ToDecimal8 (Time->Hour);\r
1011 Time->Minute = CheckAndConvertBcd8ToDecimal8 (Time->Minute);\r
1012 Time->Second = CheckAndConvertBcd8ToDecimal8 (Time->Second);\r
254ba247 1013 }\r
1014\r
5220bd21
MK
1015 if ((Time->Year == 0xff) || (Time->Month == 0xff) || (Time->Day == 0xff) ||\r
1016 (Time->Hour == 0xff) || (Time->Minute == 0xff) || (Time->Second == 0xff))\r
1017 {\r
254ba247 1018 return EFI_INVALID_PARAMETER;\r
8cd4d17c 1019 }\r
254ba247 1020\r
fe320967
RN
1021 //\r
1022 // For minimal/maximum year range [1970, 2069],\r
1023 // Century is 19 if RTC year >= 70,\r
1024 // Century is 20 otherwise.\r
1025 //\r
5220bd21 1026 Century = (UINT8)(PcdGet16 (PcdMinimalValidYear) / 100);\r
fe320967
RN
1027 if (Time->Year < PcdGet16 (PcdMinimalValidYear) % 100) {\r
1028 Century++;\r
1029 }\r
5220bd21
MK
1030\r
1031 Time->Year = (UINT16)(Century * 100 + Time->Year);\r
254ba247 1032\r
8cd4d17c 1033 //\r
1034 // If time is in 12 hour format, convert it to 24 hour format\r
1035 //\r
24115e44 1036 if (RegisterB.Bits.Mil == 0) {\r
5220bd21
MK
1037 if (IsPM && (Time->Hour < 12)) {\r
1038 Time->Hour = (UINT8)(Time->Hour + 12);\r
8cd4d17c 1039 }\r
1040\r
5220bd21 1041 if (!IsPM && (Time->Hour == 12)) {\r
8cd4d17c 1042 Time->Hour = 0;\r
1043 }\r
1044 }\r
1045\r
5220bd21 1046 Time->Nanosecond = 0;\r
254ba247 1047\r
1048 return EFI_SUCCESS;\r
8cd4d17c 1049}\r
1050\r
8d85dc31 1051/**\r
1052 Wait for a period for the RTC to be ready.\r
1053\r
1054 @param Timeout Tell how long it should take to wait.\r
1055\r
1056 @retval EFI_DEVICE_ERROR RTC device error.\r
5a702acd 1057 @retval EFI_SUCCESS RTC is updated and ready.\r
8d85dc31 1058**/\r
8cd4d17c 1059EFI_STATUS\r
1060RtcWaitToUpdate (\r
5220bd21 1061 UINTN Timeout\r
8cd4d17c 1062 )\r
8cd4d17c 1063{\r
1064 RTC_REGISTER_A RegisterA;\r
1065 RTC_REGISTER_D RegisterD;\r
1066\r
1067 //\r
1068 // See if the RTC is functioning correctly\r
1069 //\r
1070 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);\r
1071\r
24115e44 1072 if (RegisterD.Bits.Vrt == 0) {\r
8cd4d17c 1073 return EFI_DEVICE_ERROR;\r
1074 }\r
5220bd21 1075\r
8cd4d17c 1076 //\r
1077 // Wait for up to 0.1 seconds for the RTC to be ready.\r
1078 //\r
5220bd21
MK
1079 Timeout = (Timeout / 10) + 1;\r
1080 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);\r
24115e44 1081 while (RegisterA.Bits.Uip == 1 && Timeout > 0) {\r
8cd4d17c 1082 MicroSecondDelay (10);\r
1083 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);\r
1084 Timeout--;\r
1085 }\r
1086\r
1087 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);\r
5220bd21 1088 if ((Timeout == 0) || (RegisterD.Bits.Vrt == 0)) {\r
8cd4d17c 1089 return EFI_DEVICE_ERROR;\r
1090 }\r
1091\r
1092 return EFI_SUCCESS;\r
1093}\r
1094\r
8d85dc31 1095/**\r
1096 See if all fields of a variable of EFI_TIME type is correct.\r
1097\r
1098 @param Time The time to be checked.\r
1099\r
1100 @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.\r
1101 @retval EFI_SUCCESS Time is a valid EFI_TIME variable.\r
1102\r
1103**/\r
8cd4d17c 1104EFI_STATUS\r
1105RtcTimeFieldsValid (\r
5220bd21 1106 IN EFI_TIME *Time\r
8cd4d17c 1107 )\r
8cd4d17c 1108{\r
5220bd21
MK
1109 if ((Time->Year < PcdGet16 (PcdMinimalValidYear)) ||\r
1110 (Time->Year > PcdGet16 (PcdMaximalValidYear)) ||\r
1111 (Time->Month < 1) ||\r
1112 (Time->Month > 12) ||\r
6bfa178c 1113 (!DayValid (Time)) ||\r
5220bd21
MK
1114 (Time->Hour > 23) ||\r
1115 (Time->Minute > 59) ||\r
1116 (Time->Second > 59) ||\r
1117 (Time->Nanosecond > 999999999) ||\r
1118 (!((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) || ((Time->TimeZone >= -1440) && (Time->TimeZone <= 1440)))) ||\r
1119 ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0))\r
1120 {\r
6bfa178c 1121 return EFI_INVALID_PARAMETER;\r
8cd4d17c 1122 }\r
1123\r
1124 return EFI_SUCCESS;\r
1125}\r
1126\r
8d85dc31 1127/**\r
1128 See if field Day of an EFI_TIME is correct.\r
1129\r
1130 @param Time Its Day field is to be checked.\r
1131\r
1132 @retval TRUE Day field of Time is correct.\r
1133 @retval FALSE Day field of Time is NOT correct.\r
1134**/\r
8cd4d17c 1135BOOLEAN\r
1136DayValid (\r
1137 IN EFI_TIME *Time\r
1138 )\r
8cd4d17c 1139{\r
96a5ac5b 1140 //\r
1141 // The validity of Time->Month field should be checked before\r
1142 //\r
5220bd21
MK
1143 ASSERT (Time->Month >= 1);\r
1144 ASSERT (Time->Month <= 12);\r
1145 if ((Time->Day < 1) ||\r
1146 (Time->Day > mDayOfMonth[Time->Month - 1]) ||\r
1147 ((Time->Month == 2) && (!IsLeapYear (Time) && (Time->Day > 28)))\r
1148 )\r
1149 {\r
8cd4d17c 1150 return FALSE;\r
1151 }\r
1152\r
1153 return TRUE;\r
1154}\r
1155\r
8d85dc31 1156/**\r
2d4117c0 1157 Check if it is a leap year.\r
8d85dc31 1158\r
1159 @param Time The time to be checked.\r
1160\r
2d4117c0 1161 @retval TRUE It is a leap year.\r
1162 @retval FALSE It is NOT a leap year.\r
8d85dc31 1163**/\r
8cd4d17c 1164BOOLEAN\r
1165IsLeapYear (\r
5220bd21 1166 IN EFI_TIME *Time\r
8cd4d17c 1167 )\r
8cd4d17c 1168{\r
1169 if (Time->Year % 4 == 0) {\r
1170 if (Time->Year % 100 == 0) {\r
1171 if (Time->Year % 400 == 0) {\r
1172 return TRUE;\r
1173 } else {\r
1174 return FALSE;\r
1175 }\r
1176 } else {\r
1177 return TRUE;\r
1178 }\r
1179 } else {\r
1180 return FALSE;\r
1181 }\r
1182}\r
1183\r
8d85dc31 1184/**\r
53b1dd10 1185 Converts time from EFI_TIME format defined by UEFI spec to RTC format.\r
8cd4d17c 1186\r
53b1dd10 1187 This function converts time from EFI_TIME format defined by UEFI spec to RTC format.\r
8d85dc31 1188 If data mode of RTC is BCD, then converts EFI_TIME to it.\r
1189 If RTC is in 12-hour format, then converts EFI_TIME to it.\r
8cd4d17c 1190\r
8d85dc31 1191 @param Time On input, the time data read from UEFI to convert\r
1192 On output, the time converted to RTC format\r
1193 @param RegisterB Value of Register B of RTC, indicating data mode\r
8d85dc31 1194**/\r
1195VOID\r
1196ConvertEfiTimeToRtcTime (\r
1197 IN OUT EFI_TIME *Time,\r
fe320967 1198 IN RTC_REGISTER_B RegisterB\r
8d85dc31 1199 )\r
8cd4d17c 1200{\r
5220bd21 1201 BOOLEAN IsPM;\r
8cd4d17c 1202\r
8d85dc31 1203 IsPM = TRUE;\r
8cd4d17c 1204 //\r
8d85dc31 1205 // Adjust hour field if RTC is in 12 hour mode\r
8cd4d17c 1206 //\r
24115e44 1207 if (RegisterB.Bits.Mil == 0) {\r
8cd4d17c 1208 if (Time->Hour < 12) {\r
8d85dc31 1209 IsPM = FALSE;\r
8cd4d17c 1210 }\r
1211\r
1212 if (Time->Hour >= 13) {\r
5220bd21 1213 Time->Hour = (UINT8)(Time->Hour - 12);\r
8cd4d17c 1214 } else if (Time->Hour == 0) {\r
1215 Time->Hour = 12;\r
1216 }\r
1217 }\r
5220bd21 1218\r
8cd4d17c 1219 //\r
fe320967 1220 // Set the Time/Date values.\r
8cd4d17c 1221 //\r
5220bd21 1222 Time->Year = (UINT16)(Time->Year % 100);\r
8cd4d17c 1223\r
24115e44 1224 if (RegisterB.Bits.Dm == 0) {\r
5220bd21
MK
1225 Time->Year = DecimalToBcd8 ((UINT8)Time->Year);\r
1226 Time->Month = DecimalToBcd8 (Time->Month);\r
1227 Time->Day = DecimalToBcd8 (Time->Day);\r
1228 Time->Hour = DecimalToBcd8 (Time->Hour);\r
1229 Time->Minute = DecimalToBcd8 (Time->Minute);\r
1230 Time->Second = DecimalToBcd8 (Time->Second);\r
8cd4d17c 1231 }\r
5220bd21 1232\r
8cd4d17c 1233 //\r
1234 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.\r
1235 //\r
5220bd21
MK
1236 if ((RegisterB.Bits.Mil == 0) && IsPM) {\r
1237 Time->Hour = (UINT8)(Time->Hour | 0x80);\r
8cd4d17c 1238 }\r
1239}\r
1240\r
8d85dc31 1241/**\r
1242 Compare the Hour, Minute and Second of the From time and the To time.\r
5a702acd 1243\r
8d85dc31 1244 Only compare H/M/S in EFI_TIME and ignore other fields here.\r
1245\r
1246 @param From the first time\r
1247 @param To the second time\r
1248\r
1249 @return >0 The H/M/S of the From time is later than those of To time\r
1250 @return ==0 The H/M/S of the From time is same as those of To time\r
1251 @return <0 The H/M/S of the From time is earlier than those of To time\r
1252**/\r
8cd4d17c 1253INTN\r
1254CompareHMS (\r
5220bd21
MK
1255 IN EFI_TIME *From,\r
1256 IN EFI_TIME *To\r
8cd4d17c 1257 )\r
8cd4d17c 1258{\r
1259 if ((From->Hour > To->Hour) ||\r
5220bd21
MK
1260 ((From->Hour == To->Hour) && (From->Minute > To->Minute)) ||\r
1261 ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second)))\r
1262 {\r
8cd4d17c 1263 return 1;\r
1264 } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) {\r
1265 return 0;\r
1266 } else {\r
1267 return -1;\r
1268 }\r
1269}\r
1270\r
8d85dc31 1271/**\r
1272 To check if second date is later than first date within 24 hours.\r
1273\r
1274 @param From the first date\r
1275 @param To the second date\r
1276\r
1277 @retval TRUE From is previous to To within 24 hours.\r
1278 @retval FALSE From is later, or it is previous to To more than 24 hours.\r
1279**/\r
8cd4d17c 1280BOOLEAN\r
1281IsWithinOneDay (\r
1282 IN EFI_TIME *From,\r
1283 IN EFI_TIME *To\r
1284 )\r
8cd4d17c 1285{\r
5220bd21 1286 BOOLEAN Adjacent;\r
1f4cf7b1 1287\r
1f4cf7b1 1288 Adjacent = FALSE;\r
8cd4d17c 1289\r
96a5ac5b 1290 //\r
4cfc3293 1291 // The validity of From->Month field should be checked before\r
96a5ac5b 1292 //\r
5220bd21
MK
1293 ASSERT (From->Month >= 1);\r
1294 ASSERT (From->Month <= 12);\r
5a702acd 1295\r
8cd4d17c 1296 if (From->Year == To->Year) {\r
1297 if (From->Month == To->Month) {\r
1298 if ((From->Day + 1) == To->Day) {\r
5220bd21 1299 if ((CompareHMS (From, To) >= 0)) {\r
8cd4d17c 1300 Adjacent = TRUE;\r
1301 }\r
1302 } else if (From->Day == To->Day) {\r
5220bd21 1303 if ((CompareHMS (From, To) <= 0)) {\r
8cd4d17c 1304 Adjacent = TRUE;\r
1305 }\r
1306 }\r
1307 } else if (((From->Month + 1) == To->Month) && (To->Day == 1)) {\r
5220bd21 1308 if ((From->Month == 2) && !IsLeapYear (From)) {\r
8cd4d17c 1309 if (From->Day == 28) {\r
5220bd21 1310 if ((CompareHMS (From, To) >= 0)) {\r
8cd4d17c 1311 Adjacent = TRUE;\r
1312 }\r
1313 }\r
bf46bd46 1314 } else if (From->Day == mDayOfMonth[From->Month - 1]) {\r
5220bd21
MK
1315 if ((CompareHMS (From, To) >= 0)) {\r
1316 Adjacent = TRUE;\r
8cd4d17c 1317 }\r
1318 }\r
1319 }\r
1320 } else if (((From->Year + 1) == To->Year) &&\r
1321 (From->Month == 12) &&\r
1322 (From->Day == 31) &&\r
1323 (To->Month == 1) &&\r
5220bd21
MK
1324 (To->Day == 1))\r
1325 {\r
1326 if ((CompareHMS (From, To) >= 0)) {\r
8cd4d17c 1327 Adjacent = TRUE;\r
1328 }\r
1329 }\r
1330\r
1331 return Adjacent;\r
1332}\r
1333\r
41628cbc 1334/**\r
9e11e922 1335 Get the century RTC address from the ACPI FADT table.\r
41628cbc 1336\r
9e11e922 1337 @return The century RTC address or 0 if not found.\r
41628cbc 1338**/\r
9e11e922
RN
1339UINT8\r
1340GetCenturyRtcAddress (\r
1341 VOID\r
41628cbc
RN
1342 )\r
1343{\r
5220bd21 1344 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;\r
41628cbc 1345\r
5220bd21
MK
1346 Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *)EfiLocateFirstAcpiTable (\r
1347 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE\r
1348 );\r
41628cbc
RN
1349\r
1350 if ((Fadt != NULL) &&\r
9e11e922 1351 (Fadt->Century > RTC_ADDRESS_REGISTER_D) && (Fadt->Century < 0x80)\r
5220bd21
MK
1352 )\r
1353 {\r
9e11e922
RN
1354 return Fadt->Century;\r
1355 } else {\r
1356 return 0;\r
1357 }\r
1358}\r
1359\r
1360/**\r
1361 Notification function of ACPI Table change.\r
1362\r
1363 This is a notification function registered on ACPI Table change event.\r
1364 It saves the Century address stored in ACPI FADT table.\r
1365\r
1366 @param Event Event whose notification function is being invoked.\r
1367 @param Context Pointer to the notification function's context.\r
1368\r
1369**/\r
1370VOID\r
1371EFIAPI\r
1372PcRtcAcpiTableChangeCallback (\r
5220bd21
MK
1373 IN EFI_EVENT Event,\r
1374 IN VOID *Context\r
9e11e922
RN
1375 )\r
1376{\r
5220bd21
MK
1377 EFI_STATUS Status;\r
1378 EFI_TIME Time;\r
1379 UINT8 CenturyRtcAddress;\r
1380 UINT8 Century;\r
9e11e922
RN
1381\r
1382 CenturyRtcAddress = GetCenturyRtcAddress ();\r
1383 if ((CenturyRtcAddress != 0) && (mModuleGlobal.CenturyRtcAddress != CenturyRtcAddress)) {\r
1384 mModuleGlobal.CenturyRtcAddress = CenturyRtcAddress;\r
5220bd21 1385 Status = PcRtcGetTime (&Time, NULL, &mModuleGlobal);\r
41628cbc 1386 if (!EFI_ERROR (Status)) {\r
5220bd21 1387 Century = (UINT8)(Time.Year / 100);\r
41628cbc 1388 Century = DecimalToBcd8 (Century);\r
ca56749b 1389 DEBUG ((DEBUG_INFO, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century, mModuleGlobal.CenturyRtcAddress));\r
41628cbc
RN
1390 RtcWrite (mModuleGlobal.CenturyRtcAddress, Century);\r
1391 }\r
1392 }\r
1393}\r