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