]> git.proxmox.com Git - mirror_edk2.git/blame - PcAtChipsetPkg/PcatRealTimeClockRuntimeDxe/PcRtc.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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 // Release RTC Lock.\r
274 //\r
8cd4d17c 275 if (!EfiAtRuntime ()) {\r
8d85dc31 276 EfiReleaseLock (&Global->RtcLock);\r
8cd4d17c 277 }\r
5a702acd 278\r
6bfa178c 279 //\r
280 // Get the data of Daylight saving and time zone, if they have been\r
281 // stored in NV variable during previous boot.\r
282 //\r
283 DataSize = sizeof (UINT32);\r
5220bd21
MK
284 Status = EfiGetVariable (\r
285 mTimeZoneVariableName,\r
286 &gEfiCallerIdGuid,\r
287 NULL,\r
288 &DataSize,\r
289 &TimerVar\r
290 );\r
6bfa178c 291 if (!EFI_ERROR (Status)) {\r
5220bd21
MK
292 Time.TimeZone = (INT16)TimerVar;\r
293 Time.Daylight = (UINT8)(TimerVar >> 16);\r
6bfa178c 294 } else {\r
295 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
5a702acd 296 Time.Daylight = 0;\r
6bfa178c 297 }\r
298\r
8cd4d17c 299 //\r
300 // Validate time fields\r
301 //\r
fe320967 302 Status = ConvertRtcTimeToEfiTime (&Time, RegisterB);\r
254ba247 303 if (!EFI_ERROR (Status)) {\r
304 Status = RtcTimeFieldsValid (&Time);\r
305 }\r
5220bd21 306\r
8cd4d17c 307 if (EFI_ERROR (Status)) {\r
44d52203 308 //\r
309 // Report Status Code to indicate that the RTC has bad date and time\r
310 //\r
311 REPORT_STATUS_CODE (\r
312 EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
313 (EFI_SOFTWARE_DXE_RT_DRIVER | EFI_SW_EC_BAD_DATE_TIME)\r
314 );\r
5220bd21
MK
315 Time.Second = RTC_INIT_SECOND;\r
316 Time.Minute = RTC_INIT_MINUTE;\r
317 Time.Hour = RTC_INIT_HOUR;\r
318 Time.Day = RTC_INIT_DAY;\r
319 Time.Month = RTC_INIT_MONTH;\r
320 Time.Year = PcdGet16 (PcdMinimalValidYear);\r
321 Time.Nanosecond = 0;\r
322 Time.TimeZone = EFI_UNSPECIFIED_TIMEZONE;\r
323 Time.Daylight = 0;\r
8cd4d17c 324 }\r
ec35e997 325\r
29ae55a0
CZ
326 //\r
327 // Set RTC configuration after get original time\r
328 // The value of bit AIE should be reserved.\r
329 //\r
330 RegisterB.Data = FixedPcdGet8 (PcdInitialValueRtcRegisterB) | (RegisterB.Data & BIT5);\r
331 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);\r
332\r
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
29ae55a0
CZ
998 // IsPM only makes sense for 12-hour format.\r
999 if (RegisterB.Bits.Mil == 0) {\r
1000 if ((Time->Hour & 0x80) != 0) {\r
1001 IsPM = TRUE;\r
1002 } else {\r
1003 IsPM = FALSE;\r
1004 }\r
8cd4d17c 1005\r
29ae55a0
CZ
1006 Time->Hour = (UINT8)(Time->Hour & 0x7f);\r
1007 }\r
8cd4d17c 1008\r
24115e44 1009 if (RegisterB.Bits.Dm == 0) {\r
5220bd21
MK
1010 Time->Year = CheckAndConvertBcd8ToDecimal8 ((UINT8)Time->Year);\r
1011 Time->Month = CheckAndConvertBcd8ToDecimal8 (Time->Month);\r
1012 Time->Day = CheckAndConvertBcd8ToDecimal8 (Time->Day);\r
1013 Time->Hour = CheckAndConvertBcd8ToDecimal8 (Time->Hour);\r
1014 Time->Minute = CheckAndConvertBcd8ToDecimal8 (Time->Minute);\r
1015 Time->Second = CheckAndConvertBcd8ToDecimal8 (Time->Second);\r
254ba247 1016 }\r
1017\r
5220bd21
MK
1018 if ((Time->Year == 0xff) || (Time->Month == 0xff) || (Time->Day == 0xff) ||\r
1019 (Time->Hour == 0xff) || (Time->Minute == 0xff) || (Time->Second == 0xff))\r
1020 {\r
254ba247 1021 return EFI_INVALID_PARAMETER;\r
8cd4d17c 1022 }\r
254ba247 1023\r
fe320967
RN
1024 //\r
1025 // For minimal/maximum year range [1970, 2069],\r
1026 // Century is 19 if RTC year >= 70,\r
1027 // Century is 20 otherwise.\r
1028 //\r
5220bd21 1029 Century = (UINT8)(PcdGet16 (PcdMinimalValidYear) / 100);\r
fe320967
RN
1030 if (Time->Year < PcdGet16 (PcdMinimalValidYear) % 100) {\r
1031 Century++;\r
1032 }\r
5220bd21
MK
1033\r
1034 Time->Year = (UINT16)(Century * 100 + Time->Year);\r
254ba247 1035\r
8cd4d17c 1036 //\r
1037 // If time is in 12 hour format, convert it to 24 hour format\r
1038 //\r
24115e44 1039 if (RegisterB.Bits.Mil == 0) {\r
5220bd21
MK
1040 if (IsPM && (Time->Hour < 12)) {\r
1041 Time->Hour = (UINT8)(Time->Hour + 12);\r
8cd4d17c 1042 }\r
1043\r
5220bd21 1044 if (!IsPM && (Time->Hour == 12)) {\r
8cd4d17c 1045 Time->Hour = 0;\r
1046 }\r
1047 }\r
1048\r
5220bd21 1049 Time->Nanosecond = 0;\r
254ba247 1050\r
1051 return EFI_SUCCESS;\r
8cd4d17c 1052}\r
1053\r
8d85dc31 1054/**\r
1055 Wait for a period for the RTC to be ready.\r
1056\r
1057 @param Timeout Tell how long it should take to wait.\r
1058\r
1059 @retval EFI_DEVICE_ERROR RTC device error.\r
5a702acd 1060 @retval EFI_SUCCESS RTC is updated and ready.\r
8d85dc31 1061**/\r
8cd4d17c 1062EFI_STATUS\r
1063RtcWaitToUpdate (\r
5220bd21 1064 UINTN Timeout\r
8cd4d17c 1065 )\r
8cd4d17c 1066{\r
1067 RTC_REGISTER_A RegisterA;\r
1068 RTC_REGISTER_D RegisterD;\r
1069\r
1070 //\r
1071 // See if the RTC is functioning correctly\r
1072 //\r
1073 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);\r
1074\r
24115e44 1075 if (RegisterD.Bits.Vrt == 0) {\r
8cd4d17c 1076 return EFI_DEVICE_ERROR;\r
1077 }\r
5220bd21 1078\r
8cd4d17c 1079 //\r
1080 // Wait for up to 0.1 seconds for the RTC to be ready.\r
1081 //\r
5220bd21
MK
1082 Timeout = (Timeout / 10) + 1;\r
1083 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);\r
24115e44 1084 while (RegisterA.Bits.Uip == 1 && Timeout > 0) {\r
8cd4d17c 1085 MicroSecondDelay (10);\r
1086 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);\r
1087 Timeout--;\r
1088 }\r
1089\r
1090 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);\r
5220bd21 1091 if ((Timeout == 0) || (RegisterD.Bits.Vrt == 0)) {\r
8cd4d17c 1092 return EFI_DEVICE_ERROR;\r
1093 }\r
1094\r
1095 return EFI_SUCCESS;\r
1096}\r
1097\r
8d85dc31 1098/**\r
1099 See if all fields of a variable of EFI_TIME type is correct.\r
1100\r
1101 @param Time The time to be checked.\r
1102\r
1103 @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.\r
1104 @retval EFI_SUCCESS Time is a valid EFI_TIME variable.\r
1105\r
1106**/\r
8cd4d17c 1107EFI_STATUS\r
1108RtcTimeFieldsValid (\r
5220bd21 1109 IN EFI_TIME *Time\r
8cd4d17c 1110 )\r
8cd4d17c 1111{\r
5220bd21
MK
1112 if ((Time->Year < PcdGet16 (PcdMinimalValidYear)) ||\r
1113 (Time->Year > PcdGet16 (PcdMaximalValidYear)) ||\r
1114 (Time->Month < 1) ||\r
1115 (Time->Month > 12) ||\r
6bfa178c 1116 (!DayValid (Time)) ||\r
5220bd21
MK
1117 (Time->Hour > 23) ||\r
1118 (Time->Minute > 59) ||\r
1119 (Time->Second > 59) ||\r
1120 (Time->Nanosecond > 999999999) ||\r
1121 (!((Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE) || ((Time->TimeZone >= -1440) && (Time->TimeZone <= 1440)))) ||\r
1122 ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0))\r
1123 {\r
6bfa178c 1124 return EFI_INVALID_PARAMETER;\r
8cd4d17c 1125 }\r
1126\r
1127 return EFI_SUCCESS;\r
1128}\r
1129\r
8d85dc31 1130/**\r
1131 See if field Day of an EFI_TIME is correct.\r
1132\r
1133 @param Time Its Day field is to be checked.\r
1134\r
1135 @retval TRUE Day field of Time is correct.\r
1136 @retval FALSE Day field of Time is NOT correct.\r
1137**/\r
8cd4d17c 1138BOOLEAN\r
1139DayValid (\r
1140 IN EFI_TIME *Time\r
1141 )\r
8cd4d17c 1142{\r
96a5ac5b 1143 //\r
1144 // The validity of Time->Month field should be checked before\r
1145 //\r
5220bd21
MK
1146 ASSERT (Time->Month >= 1);\r
1147 ASSERT (Time->Month <= 12);\r
1148 if ((Time->Day < 1) ||\r
1149 (Time->Day > mDayOfMonth[Time->Month - 1]) ||\r
1150 ((Time->Month == 2) && (!IsLeapYear (Time) && (Time->Day > 28)))\r
1151 )\r
1152 {\r
8cd4d17c 1153 return FALSE;\r
1154 }\r
1155\r
1156 return TRUE;\r
1157}\r
1158\r
8d85dc31 1159/**\r
2d4117c0 1160 Check if it is a leap year.\r
8d85dc31 1161\r
1162 @param Time The time to be checked.\r
1163\r
2d4117c0 1164 @retval TRUE It is a leap year.\r
1165 @retval FALSE It is NOT a leap year.\r
8d85dc31 1166**/\r
8cd4d17c 1167BOOLEAN\r
1168IsLeapYear (\r
5220bd21 1169 IN EFI_TIME *Time\r
8cd4d17c 1170 )\r
8cd4d17c 1171{\r
1172 if (Time->Year % 4 == 0) {\r
1173 if (Time->Year % 100 == 0) {\r
1174 if (Time->Year % 400 == 0) {\r
1175 return TRUE;\r
1176 } else {\r
1177 return FALSE;\r
1178 }\r
1179 } else {\r
1180 return TRUE;\r
1181 }\r
1182 } else {\r
1183 return FALSE;\r
1184 }\r
1185}\r
1186\r
8d85dc31 1187/**\r
53b1dd10 1188 Converts time from EFI_TIME format defined by UEFI spec to RTC format.\r
8cd4d17c 1189\r
53b1dd10 1190 This function converts time from EFI_TIME format defined by UEFI spec to RTC format.\r
8d85dc31 1191 If data mode of RTC is BCD, then converts EFI_TIME to it.\r
1192 If RTC is in 12-hour format, then converts EFI_TIME to it.\r
8cd4d17c 1193\r
8d85dc31 1194 @param Time On input, the time data read from UEFI to convert\r
1195 On output, the time converted to RTC format\r
1196 @param RegisterB Value of Register B of RTC, indicating data mode\r
8d85dc31 1197**/\r
1198VOID\r
1199ConvertEfiTimeToRtcTime (\r
1200 IN OUT EFI_TIME *Time,\r
fe320967 1201 IN RTC_REGISTER_B RegisterB\r
8d85dc31 1202 )\r
8cd4d17c 1203{\r
5220bd21 1204 BOOLEAN IsPM;\r
8cd4d17c 1205\r
8d85dc31 1206 IsPM = TRUE;\r
8cd4d17c 1207 //\r
8d85dc31 1208 // Adjust hour field if RTC is in 12 hour mode\r
8cd4d17c 1209 //\r
24115e44 1210 if (RegisterB.Bits.Mil == 0) {\r
8cd4d17c 1211 if (Time->Hour < 12) {\r
8d85dc31 1212 IsPM = FALSE;\r
8cd4d17c 1213 }\r
1214\r
1215 if (Time->Hour >= 13) {\r
5220bd21 1216 Time->Hour = (UINT8)(Time->Hour - 12);\r
8cd4d17c 1217 } else if (Time->Hour == 0) {\r
1218 Time->Hour = 12;\r
1219 }\r
1220 }\r
5220bd21 1221\r
8cd4d17c 1222 //\r
fe320967 1223 // Set the Time/Date values.\r
8cd4d17c 1224 //\r
5220bd21 1225 Time->Year = (UINT16)(Time->Year % 100);\r
8cd4d17c 1226\r
24115e44 1227 if (RegisterB.Bits.Dm == 0) {\r
5220bd21
MK
1228 Time->Year = DecimalToBcd8 ((UINT8)Time->Year);\r
1229 Time->Month = DecimalToBcd8 (Time->Month);\r
1230 Time->Day = DecimalToBcd8 (Time->Day);\r
1231 Time->Hour = DecimalToBcd8 (Time->Hour);\r
1232 Time->Minute = DecimalToBcd8 (Time->Minute);\r
1233 Time->Second = DecimalToBcd8 (Time->Second);\r
8cd4d17c 1234 }\r
5220bd21 1235\r
8cd4d17c 1236 //\r
1237 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.\r
1238 //\r
5220bd21
MK
1239 if ((RegisterB.Bits.Mil == 0) && IsPM) {\r
1240 Time->Hour = (UINT8)(Time->Hour | 0x80);\r
8cd4d17c 1241 }\r
1242}\r
1243\r
8d85dc31 1244/**\r
1245 Compare the Hour, Minute and Second of the From time and the To time.\r
5a702acd 1246\r
8d85dc31 1247 Only compare H/M/S in EFI_TIME and ignore other fields here.\r
1248\r
1249 @param From the first time\r
1250 @param To the second time\r
1251\r
1252 @return >0 The H/M/S of the From time is later than those of To time\r
1253 @return ==0 The H/M/S of the From time is same as those of To time\r
1254 @return <0 The H/M/S of the From time is earlier than those of To time\r
1255**/\r
8cd4d17c 1256INTN\r
1257CompareHMS (\r
5220bd21
MK
1258 IN EFI_TIME *From,\r
1259 IN EFI_TIME *To\r
8cd4d17c 1260 )\r
8cd4d17c 1261{\r
1262 if ((From->Hour > To->Hour) ||\r
5220bd21
MK
1263 ((From->Hour == To->Hour) && (From->Minute > To->Minute)) ||\r
1264 ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second)))\r
1265 {\r
8cd4d17c 1266 return 1;\r
1267 } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) {\r
1268 return 0;\r
1269 } else {\r
1270 return -1;\r
1271 }\r
1272}\r
1273\r
8d85dc31 1274/**\r
1275 To check if second date is later than first date within 24 hours.\r
1276\r
1277 @param From the first date\r
1278 @param To the second date\r
1279\r
1280 @retval TRUE From is previous to To within 24 hours.\r
1281 @retval FALSE From is later, or it is previous to To more than 24 hours.\r
1282**/\r
8cd4d17c 1283BOOLEAN\r
1284IsWithinOneDay (\r
1285 IN EFI_TIME *From,\r
1286 IN EFI_TIME *To\r
1287 )\r
8cd4d17c 1288{\r
5220bd21 1289 BOOLEAN Adjacent;\r
1f4cf7b1 1290\r
1f4cf7b1 1291 Adjacent = FALSE;\r
8cd4d17c 1292\r
96a5ac5b 1293 //\r
4cfc3293 1294 // The validity of From->Month field should be checked before\r
96a5ac5b 1295 //\r
5220bd21
MK
1296 ASSERT (From->Month >= 1);\r
1297 ASSERT (From->Month <= 12);\r
5a702acd 1298\r
8cd4d17c 1299 if (From->Year == To->Year) {\r
1300 if (From->Month == To->Month) {\r
1301 if ((From->Day + 1) == To->Day) {\r
5220bd21 1302 if ((CompareHMS (From, To) >= 0)) {\r
8cd4d17c 1303 Adjacent = TRUE;\r
1304 }\r
1305 } else if (From->Day == To->Day) {\r
5220bd21 1306 if ((CompareHMS (From, To) <= 0)) {\r
8cd4d17c 1307 Adjacent = TRUE;\r
1308 }\r
1309 }\r
1310 } else if (((From->Month + 1) == To->Month) && (To->Day == 1)) {\r
5220bd21 1311 if ((From->Month == 2) && !IsLeapYear (From)) {\r
8cd4d17c 1312 if (From->Day == 28) {\r
5220bd21 1313 if ((CompareHMS (From, To) >= 0)) {\r
8cd4d17c 1314 Adjacent = TRUE;\r
1315 }\r
1316 }\r
bf46bd46 1317 } else if (From->Day == mDayOfMonth[From->Month - 1]) {\r
5220bd21
MK
1318 if ((CompareHMS (From, To) >= 0)) {\r
1319 Adjacent = TRUE;\r
8cd4d17c 1320 }\r
1321 }\r
1322 }\r
1323 } else if (((From->Year + 1) == To->Year) &&\r
1324 (From->Month == 12) &&\r
1325 (From->Day == 31) &&\r
1326 (To->Month == 1) &&\r
5220bd21
MK
1327 (To->Day == 1))\r
1328 {\r
1329 if ((CompareHMS (From, To) >= 0)) {\r
8cd4d17c 1330 Adjacent = TRUE;\r
1331 }\r
1332 }\r
1333\r
1334 return Adjacent;\r
1335}\r
1336\r
41628cbc 1337/**\r
9e11e922 1338 Get the century RTC address from the ACPI FADT table.\r
41628cbc 1339\r
9e11e922 1340 @return The century RTC address or 0 if not found.\r
41628cbc 1341**/\r
9e11e922
RN
1342UINT8\r
1343GetCenturyRtcAddress (\r
1344 VOID\r
41628cbc
RN
1345 )\r
1346{\r
5220bd21 1347 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;\r
41628cbc 1348\r
5220bd21
MK
1349 Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *)EfiLocateFirstAcpiTable (\r
1350 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE\r
1351 );\r
41628cbc
RN
1352\r
1353 if ((Fadt != NULL) &&\r
9e11e922 1354 (Fadt->Century > RTC_ADDRESS_REGISTER_D) && (Fadt->Century < 0x80)\r
5220bd21
MK
1355 )\r
1356 {\r
9e11e922
RN
1357 return Fadt->Century;\r
1358 } else {\r
1359 return 0;\r
1360 }\r
1361}\r
1362\r
1363/**\r
1364 Notification function of ACPI Table change.\r
1365\r
1366 This is a notification function registered on ACPI Table change event.\r
1367 It saves the Century address stored in ACPI FADT table.\r
1368\r
1369 @param Event Event whose notification function is being invoked.\r
1370 @param Context Pointer to the notification function's context.\r
1371\r
1372**/\r
1373VOID\r
1374EFIAPI\r
1375PcRtcAcpiTableChangeCallback (\r
5220bd21
MK
1376 IN EFI_EVENT Event,\r
1377 IN VOID *Context\r
9e11e922
RN
1378 )\r
1379{\r
5220bd21
MK
1380 EFI_STATUS Status;\r
1381 EFI_TIME Time;\r
1382 UINT8 CenturyRtcAddress;\r
1383 UINT8 Century;\r
9e11e922
RN
1384\r
1385 CenturyRtcAddress = GetCenturyRtcAddress ();\r
1386 if ((CenturyRtcAddress != 0) && (mModuleGlobal.CenturyRtcAddress != CenturyRtcAddress)) {\r
1387 mModuleGlobal.CenturyRtcAddress = CenturyRtcAddress;\r
5220bd21 1388 Status = PcRtcGetTime (&Time, NULL, &mModuleGlobal);\r
41628cbc 1389 if (!EFI_ERROR (Status)) {\r
5220bd21 1390 Century = (UINT8)(Time.Year / 100);\r
41628cbc 1391 Century = DecimalToBcd8 (Century);\r
ca56749b 1392 DEBUG ((DEBUG_INFO, "PcRtc: Write 0x%x to CMOS location 0x%x\n", Century, mModuleGlobal.CenturyRtcAddress));\r
41628cbc
RN
1393 RtcWrite (mModuleGlobal.CenturyRtcAddress, Century);\r
1394 }\r
1395 }\r
1396}\r