4 Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
5 Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
6 Copyright (c) 2014, ARM Ltd. All rights reserved.
8 This program and the accompanying materials
9 are licensed and made available under the terms and conditions of the BSD License
10 which accompanies this distribution. The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
27 #define PCAT_RTC_ADDRESS_REGISTER 0x70
28 #define PCAT_RTC_DATA_REGISTER 0x71
31 // Dallas DS12C887 Real Time Clock
33 #define RTC_ADDRESS_SECONDS 0 // R/W Range 0..59
34 #define RTC_ADDRESS_SECONDS_ALARM 1 // R/W Range 0..59
35 #define RTC_ADDRESS_MINUTES 2 // R/W Range 0..59
36 #define RTC_ADDRESS_MINUTES_ALARM 3 // R/W Range 0..59
37 #define RTC_ADDRESS_HOURS 4 // R/W Range 1..12 or 0..23 Bit 7 is AM/PM
38 #define RTC_ADDRESS_HOURS_ALARM 5 // R/W Range 1..12 or 0..23 Bit 7 is AM/PM
39 #define RTC_ADDRESS_DAY_OF_THE_WEEK 6 // R/W Range 1..7
40 #define RTC_ADDRESS_DAY_OF_THE_MONTH 7 // R/W Range 1..31
41 #define RTC_ADDRESS_MONTH 8 // R/W Range 1..12
42 #define RTC_ADDRESS_YEAR 9 // R/W Range 0..99
43 #define RTC_ADDRESS_REGISTER_A 10 // R/W[0..6] R0[7]
44 #define RTC_ADDRESS_REGISTER_B 11 // R/W
45 #define RTC_ADDRESS_REGISTER_C 12 // RO
46 #define RTC_ADDRESS_REGISTER_D 13 // RO
47 #define RTC_ADDRESS_CENTURY 50 // R/W Range 19..20 Bit 8 is R/W
49 // Date and time initial values.
50 // They are used if the RTC values are invalid during driver initialization
52 #define RTC_INIT_SECOND 0
53 #define RTC_INIT_MINUTE 0
54 #define RTC_INIT_HOUR 0
55 #define RTC_INIT_DAY 1
56 #define RTC_INIT_MONTH 1
57 #define RTC_INIT_YEAR 2001
60 // Register initial values
62 #define RTC_INIT_REGISTER_A 0x26
63 #define RTC_INIT_REGISTER_B 0x02
64 #define RTC_INIT_REGISTER_D 0x0
71 UINT8 RS
: 4; // Rate Selection Bits
72 UINT8 DV
: 3; // Divisor
73 UINT8 UIP
: 1; // Update in progress
74 } RTC_REGISTER_A_BITS
;
77 RTC_REGISTER_A_BITS Bits
;
85 UINT8 DSE
: 1; // 0 - Daylight saving disabled 1 - Daylight savings enabled
86 UINT8 MIL
: 1; // 0 - 12 hour mode 1 - 24 hour mode
87 UINT8 DM
: 1; // 0 - BCD Format 1 - Binary Format
88 UINT8 SQWE
: 1; // 0 - Disable SQWE output 1 - Enable SQWE output
89 UINT8 UIE
: 1; // 0 - Update INT disabled 1 - Update INT enabled
90 UINT8 AIE
: 1; // 0 - Alarm INT disabled 1 - Alarm INT Enabled
91 UINT8 PIE
: 1; // 0 - Periodic INT disabled 1 - Periodic INT Enabled
92 UINT8 SET
: 1; // 0 - Normal operation. 1 - Updates inhibited
93 } RTC_REGISTER_B_BITS
;
96 RTC_REGISTER_B_BITS Bits
;
104 UINT8 Reserved
: 4; // Read as zero. Can not be written.
105 UINT8 UF
: 1; // Update End Interrupt Flag
106 UINT8 AF
: 1; // Alarm Interrupt Flag
107 UINT8 PF
: 1; // Periodic Interrupt Flag
108 UINT8 IRQF
: 1; // Iterrupt Request Flag = PF & PIE | AF & AIE | UF & UIE
109 } RTC_REGISTER_C_BITS
;
112 RTC_REGISTER_C_BITS Bits
;
120 UINT8 Reserved
: 7; // Read as zero. Can not be written.
121 UINT8 VRT
: 1; // Valid RAM and Time
122 } RTC_REGISTER_D_BITS
;
125 RTC_REGISTER_D_BITS Bits
;
138 if (Time
->Year
% 4 == 0) {
139 if (Time
->Year
% 100 == 0) {
140 if (Time
->Year
% 400 == 0) {
154 const INTN mDayOfMonth
[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
162 Time
->Day
> mDayOfMonth
[Time
->Month
- 1] ||
163 (Time
->Month
== 2 && (!IsLeapYear (Time
) && Time
->Day
> 28))
180 High
= DecValue
/ 10;
181 Low
= DecValue
- (High
* 10);
183 return (UINT8
) (Low
+ (High
<< 4));
194 High
= BcdValue
>> 4;
195 Low
= BcdValue
- (High
<< 4);
197 return (UINT8
) (Low
+ (High
* 10));
204 ConvertEfiTimeToRtcTime (
206 IN RTC_REGISTER_B RegisterB
,
214 // Adjust hour field if RTC in in 12 hour mode
216 if (RegisterB
.Bits
.MIL
== 0) {
217 if (Time
->Hour
< 12) {
221 if (Time
->Hour
>= 13) {
222 Time
->Hour
= (UINT8
) (Time
->Hour
- 12);
223 } else if (Time
->Hour
== 0) {
228 // Set the Time/Date/Daylight Savings values.
230 *Century
= DecimaltoBcd ((UINT8
) (Time
->Year
/ 100));
232 Time
->Year
= (UINT16
) (Time
->Year
% 100);
234 if (RegisterB
.Bits
.DM
== 0) {
235 Time
->Year
= DecimaltoBcd ((UINT8
) Time
->Year
);
236 Time
->Month
= DecimaltoBcd (Time
->Month
);
237 Time
->Day
= DecimaltoBcd (Time
->Day
);
238 Time
->Hour
= DecimaltoBcd (Time
->Hour
);
239 Time
->Minute
= DecimaltoBcd (Time
->Minute
);
240 Time
->Second
= DecimaltoBcd (Time
->Second
);
243 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
245 if (RegisterB
.Bits
.MIL
== 0 && PM
) {
246 Time
->Hour
= (UINT8
) (Time
->Hour
| 0x80);
251 Check the validity of all the fields of a data structure of type EFI_TIME
253 @param[in] Time Pointer to a data structure of type EFI_TIME that defines a date and time
255 @retval EFI_SUCCESS All date and time fields are valid
256 @retval EFI_INVALID_PARAMETER At least one date or time field is not valid
263 if ((Time
->Year
< 1998 ) ||
264 (Time
->Year
> 2099 ) ||
265 (Time
->Month
< 1 ) ||
266 (Time
->Month
> 12 ) ||
267 (!DayValid (Time
)) ||
268 (Time
->Hour
> 23 ) ||
269 (Time
->Minute
> 59 ) ||
270 (Time
->Second
> 59 ) ||
271 (Time
->Nanosecond
> 999999999) ||
272 ((Time
->TimeZone
!= EFI_UNSPECIFIED_TIMEZONE
) &&
273 ((Time
->TimeZone
< -1440) ||
274 (Time
->TimeZone
> 1440 ) ) ) ||
275 (Time
->Daylight
& (~(EFI_TIME_ADJUST_DAYLIGHT
|
276 EFI_TIME_IN_DAYLIGHT
)))
278 return EFI_INVALID_PARAMETER
;
289 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER
, (UINT8
) (Address
| (UINT8
) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER
) & 0x80)));
290 return IoRead8 (PCAT_RTC_DATA_REGISTER
);
299 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER
, (UINT8
) (Address
| (UINT8
) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER
) & 0x80)));
300 IoWrite8 (PCAT_RTC_DATA_REGISTER
, Data
);
305 RtcTestCenturyRegister (
312 Century
= RtcRead (RTC_ADDRESS_CENTURY
);
314 // RtcWrite (RTC_ADDRESS_CENTURY, 0x00);
316 Temp
= (UINT8
) (RtcRead (RTC_ADDRESS_CENTURY
) & 0x7f);
317 RtcWrite (RTC_ADDRESS_CENTURY
, Century
);
318 if (Temp
== 0x19 || Temp
== 0x20) {
322 return EFI_DEVICE_ERROR
;
326 ConvertRtcTimeToEfiTime (
328 IN RTC_REGISTER_B RegisterB
333 if ((Time
->Hour
) & 0x80) {
339 Time
->Hour
= (UINT8
) (Time
->Hour
& 0x7f);
341 if (RegisterB
.Bits
.DM
== 0) {
342 Time
->Year
= BcdToDecimal ((UINT8
) Time
->Year
);
343 Time
->Month
= BcdToDecimal (Time
->Month
);
344 Time
->Day
= BcdToDecimal (Time
->Day
);
345 Time
->Hour
= BcdToDecimal (Time
->Hour
);
346 Time
->Minute
= BcdToDecimal (Time
->Minute
);
347 Time
->Second
= BcdToDecimal (Time
->Second
);
350 // If time is in 12 hour format, convert it to 24 hour format
352 if (RegisterB
.Bits
.MIL
== 0) {
353 if (PM
&& Time
->Hour
< 12) {
354 Time
->Hour
= (UINT8
) (Time
->Hour
+ 12);
357 if (!PM
&& Time
->Hour
== 12) {
362 Time
->Nanosecond
= 0;
363 Time
->TimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
372 RTC_REGISTER_A RegisterA
;
373 RTC_REGISTER_D RegisterD
;
376 // See if the RTC is functioning correctly
378 RegisterD
.Data
= RtcRead (RTC_ADDRESS_REGISTER_D
);
380 if (RegisterD
.Bits
.VRT
== 0) {
381 return EFI_DEVICE_ERROR
;
384 // Wait for up to 0.1 seconds for the RTC to be ready.
386 Timeout
= (Timeout
/ 10) + 1;
387 RegisterA
.Data
= RtcRead (RTC_ADDRESS_REGISTER_A
);
388 while (RegisterA
.Bits
.UIP
== 1 && Timeout
> 0) {
389 MicroSecondDelay (10);
390 RegisterA
.Data
= RtcRead (RTC_ADDRESS_REGISTER_A
);
394 RegisterD
.Data
= RtcRead (RTC_ADDRESS_REGISTER_D
);
395 if (Timeout
== 0 || RegisterD
.Bits
.VRT
== 0) {
396 return EFI_DEVICE_ERROR
;
405 OUT EFI_TIME_CAPABILITIES
*Capabilities
409 RTC_REGISTER_B RegisterB
;
414 // Check parameters for null pointer
417 return EFI_INVALID_PARAMETER
;
421 // Acquire RTC Lock to make access to RTC atomic
423 EfiAcquireLock (&mRtc
.RtcLock
);
426 // Wait for up to 0.1 seconds for the RTC to be updated
428 Status
= RtcWaitToUpdate (100000);
429 if (EFI_ERROR (Status
)) {
430 EfiReleaseLock (&mRtc
.RtcLock
);
436 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
439 // Get the Time/Date/Daylight Savings values.
441 Time
->Second
= RtcRead (RTC_ADDRESS_SECONDS
);
442 Time
->Minute
= RtcRead (RTC_ADDRESS_MINUTES
);
443 Time
->Hour
= RtcRead (RTC_ADDRESS_HOURS
);
444 Time
->Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
445 Time
->Month
= RtcRead (RTC_ADDRESS_MONTH
);
446 Time
->Year
= RtcRead (RTC_ADDRESS_YEAR
);
448 ConvertRtcTimeToEfiTime (Time
, RegisterB
);
450 if (RtcTestCenturyRegister () == EFI_SUCCESS
) {
451 Century
= BcdToDecimal ((UINT8
) (RtcRead (RTC_ADDRESS_CENTURY
) & 0x7f));
453 Century
= BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY
));
456 Time
->Year
= (UINT16
) (Century
* 100 + Time
->Year
);
461 EfiReleaseLock (&mRtc
.RtcLock
);
464 // Get the variable that containts the TimeZone and Daylight fields
466 Time
->TimeZone
= mRtc
.SavedTimeZone
;
467 Time
->Daylight
= mRtc
.Daylight
;
469 BufferSize
= sizeof (INT16
) + sizeof (UINT8
);
472 // Make sure all field values are in correct range
474 Status
= RtcTimeFieldsValid (Time
);
475 if (EFI_ERROR (Status
)) {
476 return EFI_DEVICE_ERROR
;
479 // Fill in Capabilities if it was passed in
482 Capabilities
->Resolution
= 1;
486 Capabilities
->Accuracy
= 50000000;
490 Capabilities
->SetsToZero
= FALSE
;
505 RTC_REGISTER_B RegisterB
;
509 return EFI_INVALID_PARAMETER
;
512 // Make sure that the time fields are valid
514 Status
= RtcTimeFieldsValid (Time
);
515 if (EFI_ERROR (Status
)) {
519 CopyMem (&RtcTime
, Time
, sizeof (EFI_TIME
));
522 // Acquire RTC Lock to make access to RTC atomic
524 EfiAcquireLock (&mRtc
.RtcLock
);
527 // Wait for up to 0.1 seconds for the RTC to be updated
529 Status
= RtcWaitToUpdate (100000);
530 if (EFI_ERROR (Status
)) {
531 EfiReleaseLock (&mRtc
.RtcLock
);
535 // Read Register B, and inhibit updates of the RTC
537 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
538 RegisterB
.Bits
.SET
= 1;
539 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
541 ConvertEfiTimeToRtcTime (&RtcTime
, RegisterB
, &Century
);
543 RtcWrite (RTC_ADDRESS_SECONDS
, RtcTime
.Second
);
544 RtcWrite (RTC_ADDRESS_MINUTES
, RtcTime
.Minute
);
545 RtcWrite (RTC_ADDRESS_HOURS
, RtcTime
.Hour
);
546 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH
, RtcTime
.Day
);
547 RtcWrite (RTC_ADDRESS_MONTH
, RtcTime
.Month
);
548 RtcWrite (RTC_ADDRESS_YEAR
, (UINT8
) RtcTime
.Year
);
549 if (RtcTestCenturyRegister () == EFI_SUCCESS
) {
550 Century
= (UINT8
) ((Century
& 0x7f) | (RtcRead (RTC_ADDRESS_CENTURY
) & 0x80));
553 RtcWrite (RTC_ADDRESS_CENTURY
, Century
);
556 // Allow updates of the RTC registers
558 RegisterB
.Bits
.SET
= 0;
559 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
564 EfiReleaseLock (&mRtc
.RtcLock
);
567 // Set the variable that containts the TimeZone and Daylight fields
569 mRtc
.SavedTimeZone
= Time
->TimeZone
;
570 mRtc
.Daylight
= Time
->Daylight
;
576 OUT BOOLEAN
*Enabled
,
577 OUT BOOLEAN
*Pending
,
582 RTC_REGISTER_B RegisterB
;
583 RTC_REGISTER_C RegisterC
;
587 // Check parameters for null pointers
589 if ((Enabled
== NULL
) || (Pending
== NULL
) || (Time
== NULL
)) {
590 return EFI_INVALID_PARAMETER
;
594 // Acquire RTC Lock to make access to RTC atomic
596 EfiAcquireLock (&mRtc
.RtcLock
);
599 // Wait for up to 0.1 seconds for the RTC to be updated
601 Status
= RtcWaitToUpdate (100000);
602 if (EFI_ERROR (Status
)) {
603 EfiReleaseLock (&mRtc
.RtcLock
);
604 return EFI_DEVICE_ERROR
;
607 // Read Register B and Register C
609 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
610 RegisterC
.Data
= RtcRead (RTC_ADDRESS_REGISTER_C
);
613 // Get the Time/Date/Daylight Savings values.
615 *Enabled
= RegisterB
.Bits
.AIE
;
617 Time
->Second
= RtcRead (RTC_ADDRESS_SECONDS_ALARM
);
618 Time
->Minute
= RtcRead (RTC_ADDRESS_MINUTES_ALARM
);
619 Time
->Hour
= RtcRead (RTC_ADDRESS_HOURS_ALARM
);
620 Time
->Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
621 Time
->Month
= RtcRead (RTC_ADDRESS_MONTH
);
622 Time
->Year
= RtcRead (RTC_ADDRESS_YEAR
);
627 Time
->Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
628 Time
->Month
= RtcRead (RTC_ADDRESS_MONTH
);
629 Time
->Year
= RtcRead (RTC_ADDRESS_YEAR
);
632 ConvertRtcTimeToEfiTime (Time
, RegisterB
);
634 if (RtcTestCenturyRegister () == EFI_SUCCESS
) {
635 Century
= BcdToDecimal ((UINT8
) (RtcRead (RTC_ADDRESS_CENTURY
) & 0x7f));
637 Century
= BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY
));
640 Time
->Year
= (UINT16
) (Century
* 100 + Time
->Year
);
645 EfiReleaseLock (&mRtc
.RtcLock
);
648 // Make sure all field values are in correct range
650 Status
= RtcTimeFieldsValid (Time
);
651 if (EFI_ERROR (Status
)) {
652 return EFI_DEVICE_ERROR
;
655 *Pending
= RegisterC
.Bits
.AF
;
668 RTC_REGISTER_B RegisterB
;
670 EFI_TIME_CAPABILITIES Capabilities
;
675 return EFI_INVALID_PARAMETER
;
678 // Make sure that the time fields are valid
680 Status
= RtcTimeFieldsValid (Time
);
681 if (EFI_ERROR (Status
)) {
682 return EFI_INVALID_PARAMETER
;
685 // Just support set alarm time within 24 hours
687 LibGetTime (&RtcTime
, &Capabilities
);
688 if (Time
->Year
!= RtcTime
.Year
||
689 Time
->Month
!= RtcTime
.Month
||
690 (Time
->Day
!= RtcTime
.Day
&& Time
->Day
!= (RtcTime
.Day
+ 1))
692 return EFI_UNSUPPORTED
;
695 // Make a local copy of the time and date
697 CopyMem (&RtcTime
, Time
, sizeof (EFI_TIME
));
701 // Acquire RTC Lock to make access to RTC atomic
703 EfiAcquireLock (&mRtc
.RtcLock
);
706 // Wait for up to 0.1 seconds for the RTC to be updated
708 Status
= RtcWaitToUpdate (100000);
709 if (EFI_ERROR (Status
)) {
710 EfiReleaseLock (&mRtc
.RtcLock
);
711 return EFI_DEVICE_ERROR
;
714 // Read Register B, and inhibit updates of the RTC
716 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
718 RegisterB
.Bits
.SET
= 1;
719 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
722 ConvertEfiTimeToRtcTime (&RtcTime
, RegisterB
, &Century
);
725 // Set RTC alarm time
727 RtcWrite (RTC_ADDRESS_SECONDS_ALARM
, RtcTime
.Second
);
728 RtcWrite (RTC_ADDRESS_MINUTES_ALARM
, RtcTime
.Minute
);
729 RtcWrite (RTC_ADDRESS_HOURS_ALARM
, RtcTime
.Hour
);
731 RegisterB
.Bits
.AIE
= 1;
734 RegisterB
.Bits
.AIE
= 0;
737 // Allow updates of the RTC registers
739 RegisterB
.Bits
.SET
= 0;
740 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
745 EfiReleaseLock (&mRtc
.RtcLock
);
753 LibRtcVirtualAddressChangeEvent (
766 RTC_REGISTER_A RegisterA
;
767 RTC_REGISTER_B RegisterB
;
768 RTC_REGISTER_C RegisterC
;
769 RTC_REGISTER_D RegisterD
;
774 // Acquire RTC Lock to make access to RTC atomic
776 EfiAcquireLock (&mRtc
.RtcLock
);
779 // Initialize RTC Register
781 // Make sure Division Chain is properly configured,
782 // or RTC clock won't "tick" -- time won't increment
784 RegisterA
.Data
= RTC_INIT_REGISTER_A
;
785 RtcWrite (RTC_ADDRESS_REGISTER_A
, RegisterA
.Data
);
790 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
793 // Clear RTC flag register
795 RegisterC
.Data
= RtcRead (RTC_ADDRESS_REGISTER_C
);
798 // Clear RTC register D
800 RegisterD
.Data
= RTC_INIT_REGISTER_D
;
801 RtcWrite (RTC_ADDRESS_REGISTER_D
, RegisterD
.Data
);
804 // Wait for up to 0.1 seconds for the RTC to be updated
806 Status
= RtcWaitToUpdate (100000);
807 if (EFI_ERROR (Status
)) {
808 EfiReleaseLock (&mRtc
.RtcLock
);
813 // Get the Time/Date/Daylight Savings values.
815 Time
.Second
= RtcRead (RTC_ADDRESS_SECONDS
);
816 Time
.Minute
= RtcRead (RTC_ADDRESS_MINUTES
);
817 Time
.Hour
= RtcRead (RTC_ADDRESS_HOURS
);
818 Time
.Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
819 Time
.Month
= RtcRead (RTC_ADDRESS_MONTH
);
820 Time
.Year
= RtcRead (RTC_ADDRESS_YEAR
);
822 ConvertRtcTimeToEfiTime (&Time
, RegisterB
);
824 if (RtcTestCenturyRegister () == EFI_SUCCESS
) {
825 Century
= BcdToDecimal ((UINT8
) (RtcRead (RTC_ADDRESS_CENTURY
) & 0x7f));
827 Century
= BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY
));
830 Time
.Year
= (UINT16
) (Century
* 100 + Time
.Year
);
833 // Set RTC configuration after get original time
835 RtcWrite (RTC_ADDRESS_REGISTER_B
, RTC_INIT_REGISTER_B
);
840 EfiReleaseLock (&mRtc
.RtcLock
);
843 // Validate time fields
845 Status
= RtcTimeFieldsValid (&Time
);
846 if (EFI_ERROR (Status
)) {
847 Time
.Second
= RTC_INIT_SECOND
;
848 Time
.Minute
= RTC_INIT_MINUTE
;
849 Time
.Hour
= RTC_INIT_HOUR
;
850 Time
.Day
= RTC_INIT_DAY
;
851 Time
.Month
= RTC_INIT_MONTH
;
852 Time
.Year
= RTC_INIT_YEAR
;
855 // Reset time value according to new RTC configuration