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