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