]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/PcatRealTimeClockRuntimeDxe/PcRtc.c
Array boundary evaluation expression fixed.
[mirror_edk2.git] / MdeModulePkg / Universal / PcatRealTimeClockRuntimeDxe / PcRtc.c
1 /** @file
2 RTC Architectural Protocol GUID as defined in DxeCis 0.96.
3
4 Copyright (c) 2006 - 2007, Intel Corporation
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "PcRtc.h"
16
17 /**
18 Compare the Hour, Minute and Second of the From time and the To time.
19
20 Only compare H/M/S in EFI_TIME and ignore other fields here.
21
22 @param From the first time
23 @param To the second time
24
25 @return >0 The H/M/S of the From time is later than those of To time
26 @return ==0 The H/M/S of the From time is same as those of To time
27 @return <0 The H/M/S of the From time is earlier than those of To time
28 **/
29 INTN
30 CompareHMS (
31 IN EFI_TIME *From,
32 IN EFI_TIME *To
33 );
34
35 /**
36 To check if second date is later than first date within 24 hours.
37
38 @param From the first date
39 @param To the second date
40
41 @retval TRUE From is previous to To within 24 hours.
42 @retval FALSE From is later, or it is previous to To more than 24 hours.
43 **/
44 BOOLEAN
45 IsWithinOneDay (
46 IN EFI_TIME *From,
47 IN EFI_TIME *To
48 );
49
50 /**
51 Read RTC content through its registers.
52
53 @param Address Address offset of RTC. It is recommended to use macros such as
54 RTC_ADDRESS_SECONDS.
55
56 @return The data of UINT8 type read from RTC.
57 **/
58 UINT8
59 RtcRead (
60 IN UINT8 Address
61 )
62 {
63 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));
64 return IoRead8 (PCAT_RTC_DATA_REGISTER);
65 }
66
67 /**
68 Write RTC through its registers.
69
70 @param Address Address offset of RTC. It is recommended to use macros such as
71 RTC_ADDRESS_SECONDS.
72 @param Data The content you want to write into RTC.
73
74 **/
75 VOID
76 RtcWrite (
77 IN UINT8 Address,
78 IN UINT8 Data
79 )
80 {
81 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER, (UINT8) (Address | (UINT8) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER) & 0x80)));
82 IoWrite8 (PCAT_RTC_DATA_REGISTER, Data);
83 }
84
85 /**
86 Initialize RTC.
87
88 @param Global For global use inside this module.
89
90 @retval EFI_DEVICE_ERROR Initialization failed due to device error.
91 @retval EFI_SUCCESS Initialization successful.
92
93 **/
94 EFI_STATUS
95 PcRtcInit (
96 IN PC_RTC_MODULE_GLOBALS *Global
97 )
98 {
99 EFI_STATUS Status;
100 RTC_REGISTER_A RegisterA;
101 RTC_REGISTER_B RegisterB;
102 RTC_REGISTER_D RegisterD;
103 UINT8 Century;
104 EFI_TIME Time;
105 UINTN DataSize;
106 UINT32 TimerVar;
107
108 //
109 // Acquire RTC Lock to make access to RTC atomic
110 //
111 //BugBug: the EfiAtRuntime should be encapsulated in EfiAcquireLock or
112 // provide a new instance for EfiAcquireLock, say, RtEfiAcquireLock
113 if (!EfiAtRuntime ()) {
114 EfiAcquireLock (&Global->RtcLock);
115 }
116 //
117 // Initialize RTC Register
118 //
119 // Make sure Division Chain is properly configured,
120 // or RTC clock won't "tick" -- time won't increment
121 //
122 RegisterA.Data = RTC_INIT_REGISTER_A;
123 RtcWrite (RTC_ADDRESS_REGISTER_A, RegisterA.Data);
124
125 //
126 // Read Register B
127 //
128 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
129
130 //
131 // Clear RTC flag register
132 //
133 RtcRead (RTC_ADDRESS_REGISTER_C);
134
135 //
136 // Clear RTC register D
137 //
138 RegisterD.Data = RTC_INIT_REGISTER_D;
139 RtcWrite (RTC_ADDRESS_REGISTER_D, RegisterD.Data);
140
141 //
142 // Wait for up to 0.1 seconds for the RTC to be updated
143 //
144 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
145 if (EFI_ERROR (Status)) {
146 //BugBug: the EfiAtRuntime should be encapsulated in EfiAcquireLock or
147 // provide a new instance for EfiAcquireLock, say, RtEfiAcquireLock
148 if (!EfiAtRuntime ()) {
149 EfiReleaseLock (&Global->RtcLock);
150 }
151 return EFI_DEVICE_ERROR;
152 }
153 //
154 // Get the Time/Date/Daylight Savings values.
155 //
156 Time.Second = RtcRead (RTC_ADDRESS_SECONDS);
157 Time.Minute = RtcRead (RTC_ADDRESS_MINUTES);
158 Time.Hour = RtcRead (RTC_ADDRESS_HOURS);
159 Time.Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
160 Time.Month = RtcRead (RTC_ADDRESS_MONTH);
161 Time.Year = RtcRead (RTC_ADDRESS_YEAR);
162
163 if (RtcTestCenturyRegister () == EFI_SUCCESS) {
164 Century = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f);
165 } else {
166 Century = RtcRead (RTC_ADDRESS_CENTURY);
167 }
168
169 //
170 // Set RTC configuration after get original time
171 // The value of bit AIE should be reserved.
172 //
173 RtcWrite (RTC_ADDRESS_REGISTER_B, (UINT8)(RTC_INIT_REGISTER_B | (RegisterB.Data & BIT5)));
174
175 //
176 // Release RTC Lock.
177 //
178 //BugBug: the EfiAtRuntime should be encapsulated in EfiAcquireLock or
179 // provide a new instance for EfiAcquireLock, say, RtEfiAcquireLock
180 //
181 if (!EfiAtRuntime ()) {
182 EfiReleaseLock (&Global->RtcLock);
183 }
184
185 //
186 // Validate time fields
187 //
188 Status = ConvertRtcTimeToEfiTime (&Time, Century, RegisterB);
189 if (!EFI_ERROR (Status)) {
190 Status = RtcTimeFieldsValid (&Time);
191 }
192 if (EFI_ERROR (Status)) {
193 Time.Second = RTC_INIT_SECOND;
194 Time.Minute = RTC_INIT_MINUTE;
195 Time.Hour = RTC_INIT_HOUR;
196 Time.Day = RTC_INIT_DAY;
197 Time.Month = RTC_INIT_MONTH;
198 Time.Year = RTC_INIT_YEAR;
199 }
200 //
201 // Get the data of Daylight saving and time zone, if they have been
202 // stored in NV variable during previous boot.
203 //
204 DataSize = sizeof (UINT32);
205 Status = EfiGetVariable (
206 L"TimerVar",
207 &gEfiGenericPlatformVariableGuid,
208 NULL,
209 &DataSize,
210 (VOID *) &TimerVar
211 );
212 if (!EFI_ERROR (Status)) {
213 Global->SavedTimeZone = (INT16) TimerVar;
214 Global->Daylight = (UINT8) (TimerVar >> 16);
215
216 Time.TimeZone = Global->SavedTimeZone;
217 Time.Daylight = Global->Daylight;
218 }
219 //
220 // Reset time value according to new RTC configuration
221 //
222 PcRtcSetTime (&Time, Global);
223
224 return EFI_SUCCESS;
225 }
226
227 /**
228 Returns the current time and date information, and the time-keeping capabilities
229 of the hardware platform.
230
231 @param Time A pointer to storage to receive a snapshot of the current time.
232 @param Capabilities An optional pointer to a buffer to receive the real time clock
233 device's capabilities.
234 @param Global For global use inside this module.
235
236 @retval EFI_SUCCESS The operation completed successfully.
237 @retval EFI_INVALID_PARAMETER Time is NULL.
238 @retval EFI_DEVICE_ERROR The time could not be retrieved due to hardware error.
239
240 **/
241 EFI_STATUS
242 PcRtcGetTime (
243 OUT EFI_TIME *Time,
244 OUT EFI_TIME_CAPABILITIES *Capabilities, OPTIONAL
245 IN PC_RTC_MODULE_GLOBALS *Global
246 )
247 {
248 EFI_STATUS Status;
249 RTC_REGISTER_B RegisterB;
250 UINT8 Century;
251
252 //
253 // Check parameters for null pointer
254 //
255 if (Time == NULL) {
256 return EFI_INVALID_PARAMETER;
257
258 }
259 //
260 // Acquire RTC Lock to make access to RTC atomic
261 //
262 //BugBug: the EfiAtRuntime should be encapsulated in EfiAcquireLock or
263 // provide a new instance for EfiAcquireLock, say, RtEfiAcquireLock
264 if (!EfiAtRuntime ()) {
265 EfiAcquireLock (&Global->RtcLock);
266 }
267 //
268 // Wait for up to 0.1 seconds for the RTC to be updated
269 //
270 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
271 if (EFI_ERROR (Status)) {
272 //BugBug: the EfiAtRuntime should be encapsulated in EfiReleaseLock or
273 // provide a new instance for EfiReleaseLock, say, RtEfiReleaseLock
274 if (!EfiAtRuntime ()) {
275 EfiReleaseLock (&Global->RtcLock);
276 }
277 return Status;
278 }
279 //
280 // Read Register B
281 //
282 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
283
284 //
285 // Get the Time/Date/Daylight Savings values.
286 //
287 Time->Second = RtcRead (RTC_ADDRESS_SECONDS);
288 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES);
289 Time->Hour = RtcRead (RTC_ADDRESS_HOURS);
290 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
291 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
292 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
293
294 if (RtcTestCenturyRegister () == EFI_SUCCESS) {
295 Century = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f);
296 } else {
297 Century = RtcRead (RTC_ADDRESS_CENTURY);
298 }
299
300 //
301 // Release RTC Lock.
302 //
303 //BugBug: the EfiAtRuntime should be encapsulated in EfiReleaseLock or
304 // provide a new instance for EfiReleaseLock, say, RtEfiReleaseLock
305 if (!EfiAtRuntime ()) {
306 EfiReleaseLock (&Global->RtcLock);
307 }
308 //
309 // Get the variable that contains the TimeZone and Daylight fields
310 //
311 Time->TimeZone = Global->SavedTimeZone;
312 Time->Daylight = Global->Daylight;
313
314 //
315 // Make sure all field values are in correct range
316 //
317 Status = ConvertRtcTimeToEfiTime (Time, Century, RegisterB);
318 if (!EFI_ERROR (Status)) {
319 Status = RtcTimeFieldsValid (Time);
320 }
321 if (EFI_ERROR (Status)) {
322 return EFI_DEVICE_ERROR;
323 }
324 //
325 // Fill in Capabilities if it was passed in
326 //
327 if (Capabilities != NULL) {
328 Capabilities->Resolution = 1;
329 //
330 // 1 hertz
331 //
332 Capabilities->Accuracy = 50000000;
333 //
334 // 50 ppm
335 //
336 Capabilities->SetsToZero = FALSE;
337 }
338
339 return EFI_SUCCESS;
340 }
341
342 /**
343 Sets the current local time and date information.
344
345 @param Time A pointer to the current time.
346 @param Global For global use inside this module.
347
348 @retval EFI_SUCCESS The operation completed successfully.
349 @retval EFI_INVALID_PARAMETER A time field is out of range.
350 @retval EFI_DEVICE_ERROR The time could not be set due due to hardware error.
351
352 **/
353 EFI_STATUS
354 PcRtcSetTime (
355 IN EFI_TIME *Time,
356 IN PC_RTC_MODULE_GLOBALS *Global
357 )
358 {
359 EFI_STATUS Status;
360 EFI_TIME RtcTime;
361 RTC_REGISTER_B RegisterB;
362 UINT8 Century;
363 UINT32 TimerVar;
364
365 if (Time == NULL) {
366 return EFI_INVALID_PARAMETER;
367 }
368 //
369 // Make sure that the time fields are valid
370 //
371 Status = RtcTimeFieldsValid (Time);
372 if (EFI_ERROR (Status)) {
373 return Status;
374 }
375
376 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
377
378 //
379 // Acquire RTC Lock to make access to RTC atomic
380 //
381 //BugBug: the EfiAtRuntime should be encapsulated in EfiAcquireLock or
382 // provide a new instance for EfiAcquireLock, say, RtEfiAcquireLock
383 if (!EfiAtRuntime ()) {
384 EfiAcquireLock (&Global->RtcLock);
385 }
386 //
387 // Wait for up to 0.1 seconds for the RTC to be updated
388 //
389 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
390 if (EFI_ERROR (Status)) {
391 //BugBug: the EfiAtRuntime should be encapsulated in EfiReleaseLock or
392 // provide a new instance for EfiReleaseLock, say, RtEfiReleaseLock
393 if (!EfiAtRuntime ()) {
394 EfiReleaseLock (&Global->RtcLock);
395 }
396 return Status;
397 }
398 //
399 // Read Register B, and inhibit updates of the RTC
400 //
401 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
402 RegisterB.Bits.SET = 1;
403 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
404
405 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);
406
407 RtcWrite (RTC_ADDRESS_SECONDS, RtcTime.Second);
408 RtcWrite (RTC_ADDRESS_MINUTES, RtcTime.Minute);
409 RtcWrite (RTC_ADDRESS_HOURS, RtcTime.Hour);
410 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH, RtcTime.Day);
411 RtcWrite (RTC_ADDRESS_MONTH, RtcTime.Month);
412 RtcWrite (RTC_ADDRESS_YEAR, (UINT8) RtcTime.Year);
413 if (RtcTestCenturyRegister () == EFI_SUCCESS) {
414 Century = (UINT8) ((Century & 0x7f) | (RtcRead (RTC_ADDRESS_CENTURY) & 0x80));
415 }
416
417 RtcWrite (RTC_ADDRESS_CENTURY, Century);
418
419 //
420 // Allow updates of the RTC registers
421 //
422 RegisterB.Bits.SET = 0;
423 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
424
425 //
426 // Release RTC Lock.
427 //
428 //BugBug: the EfiAtRuntime should be encapsulated in EfiReleaseLock or
429 // provide a new instance for EfiReleaseLock, say, RtEfiReleaseLock
430 if (!EfiAtRuntime ()) {
431 EfiReleaseLock (&Global->RtcLock);
432 }
433 //
434 // Set the variable that contains the TimeZone and Daylight fields
435 //
436 Global->SavedTimeZone = Time->TimeZone;
437 Global->Daylight = Time->Daylight;
438
439 TimerVar = Time->Daylight;
440 TimerVar = (UINT32) ((TimerVar << 16) | Time->TimeZone);
441 Status = EfiSetVariable (
442 L"TimerVar",
443 &gEfiGenericPlatformVariableGuid,
444 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
445 sizeof (TimerVar),
446 &TimerVar
447 );
448 ASSERT_EFI_ERROR (Status);
449
450 return EFI_SUCCESS;
451 }
452
453 /**
454 Returns the current wakeup alarm clock setting.
455
456 @param Enabled Indicates if the alarm is currently enabled or disabled.
457 @param Pending Indicates if the alarm signal is pending and requires acknowledgment.
458 @param Time The current alarm setting.
459 @param Global For global use inside this module.
460
461 @retval EFI_SUCCESS The alarm settings were returned.
462 @retval EFI_INVALID_PARAMETER Enabled is NULL.
463 @retval EFI_INVALID_PARAMETER Pending is NULL.
464 @retval EFI_INVALID_PARAMETER Time is NULL.
465 @retval EFI_DEVICE_ERROR The wakeup time could not be retrieved due to a hardware error.
466 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
467
468 **/
469 EFI_STATUS
470 PcRtcGetWakeupTime (
471 OUT BOOLEAN *Enabled,
472 OUT BOOLEAN *Pending,
473 OUT EFI_TIME *Time,
474 IN PC_RTC_MODULE_GLOBALS *Global
475 )
476 {
477 EFI_STATUS Status;
478 RTC_REGISTER_B RegisterB;
479 RTC_REGISTER_C RegisterC;
480 UINT8 Century;
481
482 //
483 // Check parameters for null pointers
484 //
485 if ((Enabled == NULL) || (Pending == NULL) || (Time == NULL)) {
486 return EFI_INVALID_PARAMETER;
487
488 }
489 //
490 // Acquire RTC Lock to make access to RTC atomic
491 //
492 //BugBug: the EfiAtRuntime should be encapsulated in EfiAcquireLock or
493 // provide a new instance for EfiAcquireLock, say, RtEfiAcquireLock
494 if (!EfiAtRuntime ()) {
495 EfiAcquireLock (&Global->RtcLock);
496 }
497 //
498 // Wait for up to 0.1 seconds for the RTC to be updated
499 //
500 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
501 if (EFI_ERROR (Status)) {
502 //BugBug: the EfiAtRuntime should be encapsulated in EfiReleaseLock or
503 // provide a new instance for EfiReleaseLock, say, RtEfiReleaseLock
504 if (!EfiAtRuntime ()) {
505 EfiReleaseLock (&Global->RtcLock);
506 }
507 return EFI_DEVICE_ERROR;
508 }
509 //
510 // Read Register B and Register C
511 //
512 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
513 RegisterC.Data = RtcRead (RTC_ADDRESS_REGISTER_C);
514
515 //
516 // Get the Time/Date/Daylight Savings values.
517 //
518 *Enabled = RegisterB.Bits.AIE;
519 if (*Enabled) {
520 Time->Second = RtcRead (RTC_ADDRESS_SECONDS_ALARM);
521 Time->Minute = RtcRead (RTC_ADDRESS_MINUTES_ALARM);
522 Time->Hour = RtcRead (RTC_ADDRESS_HOURS_ALARM);
523 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
524 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
525 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
526 } else {
527 Time->Second = 0;
528 Time->Minute = 0;
529 Time->Hour = 0;
530 Time->Day = RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH);
531 Time->Month = RtcRead (RTC_ADDRESS_MONTH);
532 Time->Year = RtcRead (RTC_ADDRESS_YEAR);
533 }
534
535 if (RtcTestCenturyRegister () == EFI_SUCCESS) {
536 Century = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f);
537 } else {
538 Century = RtcRead (RTC_ADDRESS_CENTURY);
539 }
540
541 //
542 // Release RTC Lock.
543 //
544 //BugBug: the EfiAtRuntime should be encapsulated in EfiReleaseLock or
545 // provide a new instance for EfiReleaseLock, say, RtEfiReleaseLock
546 if (!EfiAtRuntime ()) {
547 EfiReleaseLock (&Global->RtcLock);
548 }
549 //
550 // Make sure all field values are in correct range
551 //
552 Status = ConvertRtcTimeToEfiTime (Time, Century, RegisterB);
553 if (!EFI_ERROR (Status)) {
554 Status = RtcTimeFieldsValid (Time);
555 }
556 if (EFI_ERROR (Status)) {
557 return EFI_DEVICE_ERROR;
558 }
559
560 *Pending = RegisterC.Bits.AF;
561
562 return EFI_SUCCESS;
563 }
564
565 /**
566 Sets the system wakeup alarm clock time.
567
568 @param Enabled Enable or disable the wakeup alarm.
569 @param Time If Enable is TRUE, the time to set the wakeup alarm for.
570 If Enable is FALSE, then this parameter is optional, and may be NULL.
571 @param Global For global use inside this module.
572
573 @retval EFI_SUCCESS If Enable is TRUE, then the wakeup alarm was enabled.
574 If Enable is FALSE, then the wakeup alarm was disabled.
575 @retval EFI_INVALID_PARAMETER A time field is out of range.
576 @retval EFI_DEVICE_ERROR The wakeup time could not be set due to a hardware error.
577 @retval EFI_UNSUPPORTED A wakeup timer is not supported on this platform.
578
579 **/
580 EFI_STATUS
581 PcRtcSetWakeupTime (
582 IN BOOLEAN Enable,
583 IN EFI_TIME *Time, OPTIONAL
584 IN PC_RTC_MODULE_GLOBALS *Global
585 )
586 {
587 EFI_STATUS Status;
588 EFI_TIME RtcTime;
589 RTC_REGISTER_B RegisterB;
590 UINT8 Century;
591 EFI_TIME_CAPABILITIES Capabilities;
592
593 if (Enable) {
594
595 if (Time == NULL) {
596 return EFI_INVALID_PARAMETER;
597 }
598 //
599 // Make sure that the time fields are valid
600 //
601 Status = RtcTimeFieldsValid (Time);
602 if (EFI_ERROR (Status)) {
603 return EFI_INVALID_PARAMETER;
604 }
605 //
606 // Just support set alarm time within 24 hours
607 //
608 PcRtcGetTime (&RtcTime, &Capabilities, Global);
609 if (!IsWithinOneDay (&RtcTime, Time)) {
610 return EFI_UNSUPPORTED;
611 }
612 //
613 // Make a local copy of the time and date
614 //
615 CopyMem (&RtcTime, Time, sizeof (EFI_TIME));
616
617 }
618 //
619 // Acquire RTC Lock to make access to RTC atomic
620 //
621 //BugBug: the EfiAtRuntime should be encapsulated in EfiAcquireLock or
622 // provide a new instance for EfiAcquireLock, say, RtEfiAcquireLock
623 if (!EfiAtRuntime ()) {
624 EfiAcquireLock (&Global->RtcLock);
625 }
626 //
627 // Wait for up to 0.1 seconds for the RTC to be updated
628 //
629 Status = RtcWaitToUpdate (PcdGet32 (PcdRealTimeClockUpdateTimeout));
630 if (EFI_ERROR (Status)) {
631 //BugBug: the EfiAtRuntime should be encapsulated in EfiReleaseLock or
632 // provide a new instance for EfiReleaseLock, say, RtEfiReleaseLock
633 if (!EfiAtRuntime ()) {
634 EfiReleaseLock (&Global->RtcLock);
635 }
636 return EFI_DEVICE_ERROR;
637 }
638 //
639 // Read Register B, and inhibit updates of the RTC
640 //
641 RegisterB.Data = RtcRead (RTC_ADDRESS_REGISTER_B);
642
643 RegisterB.Bits.SET = 1;
644 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
645
646 if (Enable) {
647 ConvertEfiTimeToRtcTime (&RtcTime, RegisterB, &Century);
648
649 //
650 // Set RTC alarm time
651 //
652 RtcWrite (RTC_ADDRESS_SECONDS_ALARM, RtcTime.Second);
653 RtcWrite (RTC_ADDRESS_MINUTES_ALARM, RtcTime.Minute);
654 RtcWrite (RTC_ADDRESS_HOURS_ALARM, RtcTime.Hour);
655
656 RegisterB.Bits.AIE = 1;
657
658 } else {
659 RegisterB.Bits.AIE = 0;
660 }
661 //
662 // Allow updates of the RTC registers
663 //
664 RegisterB.Bits.SET = 0;
665 RtcWrite (RTC_ADDRESS_REGISTER_B, RegisterB.Data);
666
667 //
668 // Release RTC Lock.
669 //
670 //BugBug: the EfiAtRuntime should be encapsulated in EfiReleaseLock or
671 // provide a new instance for EfiReleaseLock, say, RtEfiReleaseLock
672 if (!EfiAtRuntime ()) {
673 EfiReleaseLock (&Global->RtcLock);
674 }
675 return EFI_SUCCESS;
676 }
677
678 /**
679 See if century register of RTC is valid.
680
681 @retval EFI_SUCCESS Century register is valid.
682 @retval EFI_DEVICE_ERROR Century register is NOT valid.
683 **/
684 EFI_STATUS
685 RtcTestCenturyRegister (
686 VOID
687 )
688 {
689 UINT8 Century;
690 UINT8 Temp;
691
692 Century = RtcRead (RTC_ADDRESS_CENTURY);
693 Temp = (UINT8) (RtcRead (RTC_ADDRESS_CENTURY) & 0x7f);
694 RtcWrite (RTC_ADDRESS_CENTURY, Century);
695 if (Temp == 0x19 || Temp == 0x20) {
696 return EFI_SUCCESS;
697 }
698
699 return EFI_DEVICE_ERROR;
700 }
701
702 /**
703 Checks an 8-bit BCD value, and converts to an 8-bit value if valid.
704
705 This function checks the 8-bit BCD value specified by Value.
706 If valid, the function converts it to an 8-bit value and returns it.
707 Otherwise, return 0xff.
708
709 @param Value The 8-bit BCD value to check and convert
710
711 @return The 8-bit value converted. Or 0xff if Value is invalid.
712
713 **/
714 UINT8
715 CheckAndConvertBcd8ToDecimal8 (
716 IN UINT8 Value
717 )
718 {
719 if ((Value < 0xa0) && ((Value & 0xf) < 0xa)) {
720 return BcdToDecimal8 (Value);
721 }
722
723 return 0xff;
724 }
725
726 /**
727 Converts time read from RTC to EFI_TIME format defined by UEFI spec.
728
729 This function converts raw time data read from RTC to the EFI_TIME format
730 defined by UEFI spec.
731 If data mode of RTC is BCD, then converts it to decimal,
732 If RTC is in 12-hour format, then converts it to 24-hour format.
733
734 @param Time On input, the time data read from RTC to convert
735 On output, the time converted to UEFI format
736 @param Century Value of century read from RTC.
737 @param RegisterB Value of Register B of RTC, indicating data mode
738 and hour format.
739
740 @retval EFI_INVALID_PARAMETER Parameters passed in are invalid.
741 @retval EFI_SUCCESS Convert RTC time to EFI time successfully.
742
743 **/
744 EFI_STATUS
745 ConvertRtcTimeToEfiTime (
746 IN OUT EFI_TIME *Time,
747 IN UINT8 Century,
748 IN RTC_REGISTER_B RegisterB
749 )
750 {
751 BOOLEAN IsPM;
752
753 if ((Time->Hour & 0x80) != 0) {
754 IsPM = TRUE;
755 } else {
756 IsPM = FALSE;
757 }
758
759 Time->Hour = (UINT8) (Time->Hour & 0x7f);
760
761 if (RegisterB.Bits.DM == 0) {
762 Time->Year = CheckAndConvertBcd8ToDecimal8 ((UINT8) Time->Year);
763 Time->Month = CheckAndConvertBcd8ToDecimal8 (Time->Month);
764 Time->Day = CheckAndConvertBcd8ToDecimal8 (Time->Day);
765 Time->Hour = CheckAndConvertBcd8ToDecimal8 (Time->Hour);
766 Time->Minute = CheckAndConvertBcd8ToDecimal8 (Time->Minute);
767 Time->Second = CheckAndConvertBcd8ToDecimal8 (Time->Second);
768 Century = CheckAndConvertBcd8ToDecimal8 (Century);
769 }
770
771 if (Time->Year == 0xff || Time->Month == 0xff || Time->Day == 0xff ||
772 Time->Hour == 0xff || Time->Minute == 0xff || Time->Second == 0xff ||
773 Century == 0xff) {
774 return EFI_INVALID_PARAMETER;
775 }
776
777 Time->Year = (UINT16) (Century * 100 + Time->Year);
778
779 //
780 // If time is in 12 hour format, convert it to 24 hour format
781 //
782 if (RegisterB.Bits.MIL == 0) {
783 if (IsPM && Time->Hour < 12) {
784 Time->Hour = (UINT8) (Time->Hour + 12);
785 }
786
787 if (!IsPM && Time->Hour == 12) {
788 Time->Hour = 0;
789 }
790 }
791
792 Time->Nanosecond = 0;
793 Time->TimeZone = EFI_UNSPECIFIED_TIMEZONE;
794 Time->Daylight = 0;
795
796 return EFI_SUCCESS;
797 }
798
799 /**
800 Wait for a period for the RTC to be ready.
801
802 @param Timeout Tell how long it should take to wait.
803
804 @retval EFI_DEVICE_ERROR RTC device error.
805 @retval EFI_SUCCESS RTC is updated and ready.
806 **/
807 EFI_STATUS
808 RtcWaitToUpdate (
809 UINTN Timeout
810 )
811 {
812 RTC_REGISTER_A RegisterA;
813 RTC_REGISTER_D RegisterD;
814
815 //
816 // See if the RTC is functioning correctly
817 //
818 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
819
820 if (RegisterD.Bits.VRT == 0) {
821 return EFI_DEVICE_ERROR;
822 }
823 //
824 // Wait for up to 0.1 seconds for the RTC to be ready.
825 //
826 Timeout = (Timeout / 10) + 1;
827 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
828 while (RegisterA.Bits.UIP == 1 && Timeout > 0) {
829 MicroSecondDelay (10);
830 RegisterA.Data = RtcRead (RTC_ADDRESS_REGISTER_A);
831 Timeout--;
832 }
833
834 RegisterD.Data = RtcRead (RTC_ADDRESS_REGISTER_D);
835 if (Timeout == 0 || RegisterD.Bits.VRT == 0) {
836 return EFI_DEVICE_ERROR;
837 }
838
839 return EFI_SUCCESS;
840 }
841
842 /**
843 See if all fields of a variable of EFI_TIME type is correct.
844
845 @param Time The time to be checked.
846
847 @retval EFI_INVALID_PARAMETER Some fields of Time are not correct.
848 @retval EFI_SUCCESS Time is a valid EFI_TIME variable.
849
850 **/
851 EFI_STATUS
852 RtcTimeFieldsValid (
853 IN EFI_TIME *Time
854 )
855 {
856 if (Time->Year < 1998 ||
857 Time->Year > 2099 ||
858 Time->Month < 1 ||
859 Time->Month > 12 ||
860 (!DayValid (Time)) ||
861 Time->Hour > 23 ||
862 Time->Minute > 59 ||
863 Time->Second > 59 ||
864 Time->Nanosecond > 999999999 ||
865 (!(Time->TimeZone == EFI_UNSPECIFIED_TIMEZONE || (Time->TimeZone >= -1440 && Time->TimeZone <= 1440))) ||
866 ((Time->Daylight & (~(EFI_TIME_ADJUST_DAYLIGHT | EFI_TIME_IN_DAYLIGHT))) != 0)
867 ) {
868 return EFI_INVALID_PARAMETER;
869 }
870
871 return EFI_SUCCESS;
872 }
873
874 /**
875 See if field Day of an EFI_TIME is correct.
876
877 @param Time Its Day field is to be checked.
878
879 @retval TRUE Day field of Time is correct.
880 @retval FALSE Day field of Time is NOT correct.
881 **/
882 BOOLEAN
883 DayValid (
884 IN EFI_TIME *Time
885 )
886 {
887 INTN DayOfMonth[12];
888
889 DayOfMonth[0] = 31;
890 DayOfMonth[1] = 29;
891 DayOfMonth[2] = 31;
892 DayOfMonth[3] = 30;
893 DayOfMonth[4] = 31;
894 DayOfMonth[5] = 30;
895 DayOfMonth[6] = 31;
896 DayOfMonth[7] = 31;
897 DayOfMonth[8] = 30;
898 DayOfMonth[9] = 31;
899 DayOfMonth[10] = 30;
900 DayOfMonth[11] = 31;
901
902 if (Time->Day < 1 ||
903 Time->Day > DayOfMonth[Time->Month - 1] ||
904 (Time->Month == 2 && (!IsLeapYear (Time) && Time->Day > 28))
905 ) {
906 return FALSE;
907 }
908
909 return TRUE;
910 }
911
912 /**
913 Check if it is a leap year.
914
915 @param Time The time to be checked.
916
917 @retval TRUE It is a leap year.
918 @retval FALSE It is NOT a leap year.
919 **/
920 BOOLEAN
921 IsLeapYear (
922 IN EFI_TIME *Time
923 )
924 {
925 if (Time->Year % 4 == 0) {
926 if (Time->Year % 100 == 0) {
927 if (Time->Year % 400 == 0) {
928 return TRUE;
929 } else {
930 return FALSE;
931 }
932 } else {
933 return TRUE;
934 }
935 } else {
936 return FALSE;
937 }
938 }
939
940 /**
941 Converts time from EFI_TIME format defined by UEFI spec to RTC's.
942
943 This function converts time from EFI_TIME format defined by UEFI spec to RTC's.
944 If data mode of RTC is BCD, then converts EFI_TIME to it.
945 If RTC is in 12-hour format, then converts EFI_TIME to it.
946
947 @param Time On input, the time data read from UEFI to convert
948 On output, the time converted to RTC format
949 @param RegisterB Value of Register B of RTC, indicating data mode
950 @param Century It is set according to EFI_TIME Time.
951
952 **/
953 VOID
954 ConvertEfiTimeToRtcTime (
955 IN OUT EFI_TIME *Time,
956 IN RTC_REGISTER_B RegisterB,
957 OUT UINT8 *Century
958 )
959 {
960 BOOLEAN IsPM;
961
962 IsPM = TRUE;
963 //
964 // Adjust hour field if RTC is in 12 hour mode
965 //
966 if (RegisterB.Bits.MIL == 0) {
967 if (Time->Hour < 12) {
968 IsPM = FALSE;
969 }
970
971 if (Time->Hour >= 13) {
972 Time->Hour = (UINT8) (Time->Hour - 12);
973 } else if (Time->Hour == 0) {
974 Time->Hour = 12;
975 }
976 }
977 //
978 // Set the Time/Date/Daylight Savings values.
979 //
980 *Century = DecimalToBcd8 ((UINT8) (Time->Year / 100));
981
982 Time->Year = (UINT16) (Time->Year % 100);
983
984 if (RegisterB.Bits.DM == 0) {
985 Time->Year = DecimalToBcd8 ((UINT8) Time->Year);
986 Time->Month = DecimalToBcd8 (Time->Month);
987 Time->Day = DecimalToBcd8 (Time->Day);
988 Time->Hour = DecimalToBcd8 (Time->Hour);
989 Time->Minute = DecimalToBcd8 (Time->Minute);
990 Time->Second = DecimalToBcd8 (Time->Second);
991 }
992 //
993 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
994 //
995 if (RegisterB.Bits.MIL == 0 && IsPM) {
996 Time->Hour = (UINT8) (Time->Hour | 0x80);
997 }
998 }
999
1000 /**
1001 Compare the Hour, Minute and Second of the From time and the To time.
1002
1003 Only compare H/M/S in EFI_TIME and ignore other fields here.
1004
1005 @param From the first time
1006 @param To the second time
1007
1008 @return >0 The H/M/S of the From time is later than those of To time
1009 @return ==0 The H/M/S of the From time is same as those of To time
1010 @return <0 The H/M/S of the From time is earlier than those of To time
1011 **/
1012 INTN
1013 CompareHMS (
1014 IN EFI_TIME *From,
1015 IN EFI_TIME *To
1016 )
1017 {
1018 if ((From->Hour > To->Hour) ||
1019 ((From->Hour == To->Hour) && (From->Minute > To->Minute)) ||
1020 ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second > To->Second))) {
1021 return 1;
1022 } else if ((From->Hour == To->Hour) && (From->Minute == To->Minute) && (From->Second == To->Second)) {
1023 return 0;
1024 } else {
1025 return -1;
1026 }
1027 }
1028
1029 /**
1030 To check if second date is later than first date within 24 hours.
1031
1032 @param From the first date
1033 @param To the second date
1034
1035 @retval TRUE From is previous to To within 24 hours.
1036 @retval FALSE From is later, or it is previous to To more than 24 hours.
1037 **/
1038 BOOLEAN
1039 IsWithinOneDay (
1040 IN EFI_TIME *From,
1041 IN EFI_TIME *To
1042 )
1043 {
1044 UINT8 DayOfMonth[12];
1045 BOOLEAN Adjacent;
1046
1047 DayOfMonth[0] = 31;
1048 DayOfMonth[1] = 29;
1049 DayOfMonth[2] = 31;
1050 DayOfMonth[3] = 30;
1051 DayOfMonth[4] = 31;
1052 DayOfMonth[5] = 30;
1053 DayOfMonth[6] = 31;
1054 DayOfMonth[7] = 31;
1055 DayOfMonth[8] = 30;
1056 DayOfMonth[9] = 31;
1057 DayOfMonth[10] = 30;
1058 DayOfMonth[11] = 31;
1059
1060 Adjacent = FALSE;
1061
1062 if (From->Year == To->Year) {
1063 if (From->Month == To->Month) {
1064 if ((From->Day + 1) == To->Day) {
1065 if ((CompareHMS(From, To) >= 0)) {
1066 Adjacent = TRUE;
1067 }
1068 } else if (From->Day == To->Day) {
1069 if ((CompareHMS(From, To) <= 0)) {
1070 Adjacent = TRUE;
1071 }
1072 }
1073 } else if (((From->Month + 1) == To->Month) && (To->Day == 1)) {
1074 if ((From->Month == 2) && !IsLeapYear(From)) {
1075 if (From->Day == 28) {
1076 if ((CompareHMS(From, To) >= 0)) {
1077 Adjacent = TRUE;
1078 }
1079 }
1080 } else if (From->Day == DayOfMonth[From->Month - 1]) {
1081 if ((CompareHMS(From, To) >= 0)) {
1082 Adjacent = TRUE;
1083 }
1084 }
1085 }
1086 } else if (((From->Year + 1) == To->Year) &&
1087 (From->Month == 12) &&
1088 (From->Day == 31) &&
1089 (To->Month == 1) &&
1090 (To->Day == 1)) {
1091 if ((CompareHMS(From, To) >= 0)) {
1092 Adjacent = TRUE;
1093 }
1094 }
1095
1096 return Adjacent;
1097 }
1098