4 Copyright (c) 2007, Intel Corporation. All rights reserved.<BR>
5 Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
26 #define PCAT_RTC_ADDRESS_REGISTER 0x70
27 #define PCAT_RTC_DATA_REGISTER 0x71
30 // Dallas DS12C887 Real Time Clock
32 #define RTC_ADDRESS_SECONDS 0 // R/W Range 0..59
33 #define RTC_ADDRESS_SECONDS_ALARM 1 // R/W Range 0..59
34 #define RTC_ADDRESS_MINUTES 2 // R/W Range 0..59
35 #define RTC_ADDRESS_MINUTES_ALARM 3 // R/W Range 0..59
36 #define RTC_ADDRESS_HOURS 4 // R/W Range 1..12 or 0..23 Bit 7 is AM/PM
37 #define RTC_ADDRESS_HOURS_ALARM 5 // R/W Range 1..12 or 0..23 Bit 7 is AM/PM
38 #define RTC_ADDRESS_DAY_OF_THE_WEEK 6 // R/W Range 1..7
39 #define RTC_ADDRESS_DAY_OF_THE_MONTH 7 // R/W Range 1..31
40 #define RTC_ADDRESS_MONTH 8 // R/W Range 1..12
41 #define RTC_ADDRESS_YEAR 9 // R/W Range 0..99
42 #define RTC_ADDRESS_REGISTER_A 10 // R/W[0..6] R0[7]
43 #define RTC_ADDRESS_REGISTER_B 11 // R/W
44 #define RTC_ADDRESS_REGISTER_C 12 // RO
45 #define RTC_ADDRESS_REGISTER_D 13 // RO
46 #define RTC_ADDRESS_CENTURY 50 // R/W Range 19..20 Bit 8 is R/W
48 // Date and time initial values.
49 // They are used if the RTC values are invalid during driver initialization
51 #define RTC_INIT_SECOND 0
52 #define RTC_INIT_MINUTE 0
53 #define RTC_INIT_HOUR 0
54 #define RTC_INIT_DAY 1
55 #define RTC_INIT_MONTH 1
56 #define RTC_INIT_YEAR 2001
59 // Register initial values
61 #define RTC_INIT_REGISTER_A 0x26
62 #define RTC_INIT_REGISTER_B 0x02
63 #define RTC_INIT_REGISTER_D 0x0
70 UINT8 RS
: 4; // Rate Selection Bits
71 UINT8 DV
: 3; // Divisor
72 UINT8 UIP
: 1; // Update in progress
73 } RTC_REGISTER_A_BITS
;
76 RTC_REGISTER_A_BITS Bits
;
84 UINT8 DSE
: 1; // 0 - Daylight saving disabled 1 - Daylight savings enabled
85 UINT8 MIL
: 1; // 0 - 12 hour mode 1 - 24 hour mode
86 UINT8 DM
: 1; // 0 - BCD Format 1 - Binary Format
87 UINT8 SQWE
: 1; // 0 - Disable SQWE output 1 - Enable SQWE output
88 UINT8 UIE
: 1; // 0 - Update INT disabled 1 - Update INT enabled
89 UINT8 AIE
: 1; // 0 - Alarm INT disabled 1 - Alarm INT Enabled
90 UINT8 PIE
: 1; // 0 - Periodic INT disabled 1 - Periodic INT Enabled
91 UINT8 SET
: 1; // 0 - Normal operation. 1 - Updates inhibited
92 } RTC_REGISTER_B_BITS
;
95 RTC_REGISTER_B_BITS Bits
;
103 UINT8 Reserved
: 4; // Read as zero. Can not be written.
104 UINT8 UF
: 1; // Update End Interrupt Flag
105 UINT8 AF
: 1; // Alarm Interrupt Flag
106 UINT8 PF
: 1; // Periodic Interrupt Flag
107 UINT8 IRQF
: 1; // Iterrupt Request Flag = PF & PIE | AF & AIE | UF & UIE
108 } RTC_REGISTER_C_BITS
;
111 RTC_REGISTER_C_BITS Bits
;
119 UINT8 Reserved
: 7; // Read as zero. Can not be written.
120 UINT8 VRT
: 1; // Valid RAM and Time
121 } RTC_REGISTER_D_BITS
;
124 RTC_REGISTER_D_BITS Bits
;
137 if (Time
->Year
% 4 == 0) {
138 if (Time
->Year
% 100 == 0) {
139 if (Time
->Year
% 400 == 0) {
153 const INTN mDayOfMonth
[12] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
161 Time
->Day
> mDayOfMonth
[Time
->Month
- 1] ||
162 (Time
->Month
== 2 && (!IsLeapYear (Time
) && Time
->Day
> 28))
179 High
= DecValue
/ 10;
180 Low
= DecValue
- (High
* 10);
182 return (UINT8
) (Low
+ (High
<< 4));
193 High
= BcdValue
>> 4;
194 Low
= BcdValue
- (High
<< 4);
196 return (UINT8
) (Low
+ (High
* 10));
203 ConvertEfiTimeToRtcTime (
205 IN RTC_REGISTER_B RegisterB
,
213 // Adjust hour field if RTC in in 12 hour mode
215 if (RegisterB
.Bits
.MIL
== 0) {
216 if (Time
->Hour
< 12) {
220 if (Time
->Hour
>= 13) {
221 Time
->Hour
= (UINT8
) (Time
->Hour
- 12);
222 } else if (Time
->Hour
== 0) {
227 // Set the Time/Date/Daylight Savings values.
229 *Century
= DecimaltoBcd ((UINT8
) (Time
->Year
/ 100));
231 Time
->Year
= (UINT16
) (Time
->Year
% 100);
233 if (RegisterB
.Bits
.DM
== 0) {
234 Time
->Year
= DecimaltoBcd ((UINT8
) Time
->Year
);
235 Time
->Month
= DecimaltoBcd (Time
->Month
);
236 Time
->Day
= DecimaltoBcd (Time
->Day
);
237 Time
->Hour
= DecimaltoBcd (Time
->Hour
);
238 Time
->Minute
= DecimaltoBcd (Time
->Minute
);
239 Time
->Second
= DecimaltoBcd (Time
->Second
);
242 // If we are in 12 hour mode and PM is set, then set bit 7 of the Hour field.
244 if (RegisterB
.Bits
.MIL
== 0 && PM
) {
245 Time
->Hour
= (UINT8
) (Time
->Hour
| 0x80);
261 // TODO: Time - add argument and description to function comment
262 // TODO: EFI_INVALID_PARAMETER - add return value to function comment
263 // TODO: EFI_SUCCESS - add return value to function comment
265 if (Time
->Year
< 1998 ||
269 (!DayValid (Time
)) ||
273 Time
->Nanosecond
> 999999999 ||
274 (!(Time
->TimeZone
== EFI_UNSPECIFIED_TIMEZONE
|| (Time
->TimeZone
>= -1440 && Time
->TimeZone
<= 1440))) ||
275 (Time
->Daylight
& (~(EFI_TIME_ADJUST_DAYLIGHT
| EFI_TIME_IN_DAYLIGHT
)))
277 return EFI_INVALID_PARAMETER
;
288 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER
, (UINT8
) (Address
| (UINT8
) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER
) & 0x80)));
289 return IoRead8 (PCAT_RTC_DATA_REGISTER
);
298 IoWrite8 (PCAT_RTC_ADDRESS_REGISTER
, (UINT8
) (Address
| (UINT8
) (IoRead8 (PCAT_RTC_ADDRESS_REGISTER
) & 0x80)));
299 IoWrite8 (PCAT_RTC_DATA_REGISTER
, Data
);
304 RtcTestCenturyRegister (
311 Century
= RtcRead (RTC_ADDRESS_CENTURY
);
313 // RtcWrite (RTC_ADDRESS_CENTURY, 0x00);
315 Temp
= (UINT8
) (RtcRead (RTC_ADDRESS_CENTURY
) & 0x7f);
316 RtcWrite (RTC_ADDRESS_CENTURY
, Century
);
317 if (Temp
== 0x19 || Temp
== 0x20) {
321 return EFI_DEVICE_ERROR
;
325 ConvertRtcTimeToEfiTime (
327 IN RTC_REGISTER_B RegisterB
332 if ((Time
->Hour
) & 0x80) {
338 Time
->Hour
= (UINT8
) (Time
->Hour
& 0x7f);
340 if (RegisterB
.Bits
.DM
== 0) {
341 Time
->Year
= BcdToDecimal ((UINT8
) Time
->Year
);
342 Time
->Month
= BcdToDecimal (Time
->Month
);
343 Time
->Day
= BcdToDecimal (Time
->Day
);
344 Time
->Hour
= BcdToDecimal (Time
->Hour
);
345 Time
->Minute
= BcdToDecimal (Time
->Minute
);
346 Time
->Second
= BcdToDecimal (Time
->Second
);
349 // If time is in 12 hour format, convert it to 24 hour format
351 if (RegisterB
.Bits
.MIL
== 0) {
352 if (PM
&& Time
->Hour
< 12) {
353 Time
->Hour
= (UINT8
) (Time
->Hour
+ 12);
356 if (!PM
&& Time
->Hour
== 12) {
361 Time
->Nanosecond
= 0;
362 Time
->TimeZone
= EFI_UNSPECIFIED_TIMEZONE
;
371 RTC_REGISTER_A RegisterA
;
372 RTC_REGISTER_D RegisterD
;
375 // See if the RTC is functioning correctly
377 RegisterD
.Data
= RtcRead (RTC_ADDRESS_REGISTER_D
);
379 if (RegisterD
.Bits
.VRT
== 0) {
380 return EFI_DEVICE_ERROR
;
383 // Wait for up to 0.1 seconds for the RTC to be ready.
385 Timeout
= (Timeout
/ 10) + 1;
386 RegisterA
.Data
= RtcRead (RTC_ADDRESS_REGISTER_A
);
387 while (RegisterA
.Bits
.UIP
== 1 && Timeout
> 0) {
388 MicroSecondDelay (10);
389 RegisterA
.Data
= RtcRead (RTC_ADDRESS_REGISTER_A
);
393 RegisterD
.Data
= RtcRead (RTC_ADDRESS_REGISTER_D
);
394 if (Timeout
== 0 || RegisterD
.Bits
.VRT
== 0) {
395 return EFI_DEVICE_ERROR
;
404 OUT EFI_TIME_CAPABILITIES
*Capabilities
408 RTC_REGISTER_B RegisterB
;
413 // Check parameters for null pointer
416 return EFI_INVALID_PARAMETER
;
420 // Acquire RTC Lock to make access to RTC atomic
422 EfiAcquireLock (&mRtc
.RtcLock
);
425 // Wait for up to 0.1 seconds for the RTC to be updated
427 Status
= RtcWaitToUpdate (100000);
428 if (EFI_ERROR (Status
)) {
429 EfiReleaseLock (&mRtc
.RtcLock
);
435 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
438 // Get the Time/Date/Daylight Savings values.
440 Time
->Second
= RtcRead (RTC_ADDRESS_SECONDS
);
441 Time
->Minute
= RtcRead (RTC_ADDRESS_MINUTES
);
442 Time
->Hour
= RtcRead (RTC_ADDRESS_HOURS
);
443 Time
->Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
444 Time
->Month
= RtcRead (RTC_ADDRESS_MONTH
);
445 Time
->Year
= RtcRead (RTC_ADDRESS_YEAR
);
447 ConvertRtcTimeToEfiTime (Time
, RegisterB
);
449 if (RtcTestCenturyRegister () == EFI_SUCCESS
) {
450 Century
= BcdToDecimal ((UINT8
) (RtcRead (RTC_ADDRESS_CENTURY
) & 0x7f));
452 Century
= BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY
));
455 Time
->Year
= (UINT16
) (Century
* 100 + Time
->Year
);
460 EfiReleaseLock (&mRtc
.RtcLock
);
463 // Get the variable that containts the TimeZone and Daylight fields
465 Time
->TimeZone
= mRtc
.SavedTimeZone
;
466 Time
->Daylight
= mRtc
.Daylight
;
468 BufferSize
= sizeof (INT16
) + sizeof (UINT8
);
471 // Make sure all field values are in correct range
473 Status
= RtcTimeFieldsValid (Time
);
474 if (EFI_ERROR (Status
)) {
475 return EFI_DEVICE_ERROR
;
478 // Fill in Capabilities if it was passed in
481 Capabilities
->Resolution
= 1;
485 Capabilities
->Accuracy
= 50000000;
489 Capabilities
->SetsToZero
= FALSE
;
504 RTC_REGISTER_B RegisterB
;
508 return EFI_INVALID_PARAMETER
;
511 // Make sure that the time fields are valid
513 Status
= RtcTimeFieldsValid (Time
);
514 if (EFI_ERROR (Status
)) {
518 CopyMem (&RtcTime
, Time
, sizeof (EFI_TIME
));
521 // Acquire RTC Lock to make access to RTC atomic
523 EfiAcquireLock (&mRtc
.RtcLock
);
526 // Wait for up to 0.1 seconds for the RTC to be updated
528 Status
= RtcWaitToUpdate (100000);
529 if (EFI_ERROR (Status
)) {
530 EfiReleaseLock (&mRtc
.RtcLock
);
534 // Read Register B, and inhibit updates of the RTC
536 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
537 RegisterB
.Bits
.SET
= 1;
538 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
540 ConvertEfiTimeToRtcTime (&RtcTime
, RegisterB
, &Century
);
542 RtcWrite (RTC_ADDRESS_SECONDS
, RtcTime
.Second
);
543 RtcWrite (RTC_ADDRESS_MINUTES
, RtcTime
.Minute
);
544 RtcWrite (RTC_ADDRESS_HOURS
, RtcTime
.Hour
);
545 RtcWrite (RTC_ADDRESS_DAY_OF_THE_MONTH
, RtcTime
.Day
);
546 RtcWrite (RTC_ADDRESS_MONTH
, RtcTime
.Month
);
547 RtcWrite (RTC_ADDRESS_YEAR
, (UINT8
) RtcTime
.Year
);
548 if (RtcTestCenturyRegister () == EFI_SUCCESS
) {
549 Century
= (UINT8
) ((Century
& 0x7f) | (RtcRead (RTC_ADDRESS_CENTURY
) & 0x80));
552 RtcWrite (RTC_ADDRESS_CENTURY
, Century
);
555 // Allow updates of the RTC registers
557 RegisterB
.Bits
.SET
= 0;
558 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
563 EfiReleaseLock (&mRtc
.RtcLock
);
566 // Set the variable that containts the TimeZone and Daylight fields
568 mRtc
.SavedTimeZone
= Time
->TimeZone
;
569 mRtc
.Daylight
= Time
->Daylight
;
575 OUT BOOLEAN
*Enabled
,
576 OUT BOOLEAN
*Pending
,
581 RTC_REGISTER_B RegisterB
;
582 RTC_REGISTER_C RegisterC
;
586 // Check paramters for null pointers
588 if ((Enabled
== NULL
) || (Pending
== NULL
) || (Time
== NULL
)) {
589 return EFI_INVALID_PARAMETER
;
593 // Acquire RTC Lock to make access to RTC atomic
595 EfiAcquireLock (&mRtc
.RtcLock
);
598 // Wait for up to 0.1 seconds for the RTC to be updated
600 Status
= RtcWaitToUpdate (100000);
601 if (EFI_ERROR (Status
)) {
602 EfiReleaseLock (&mRtc
.RtcLock
);
603 return EFI_DEVICE_ERROR
;
606 // Read Register B and Register C
608 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
609 RegisterC
.Data
= RtcRead (RTC_ADDRESS_REGISTER_C
);
612 // Get the Time/Date/Daylight Savings values.
614 *Enabled
= RegisterB
.Bits
.AIE
;
616 Time
->Second
= RtcRead (RTC_ADDRESS_SECONDS_ALARM
);
617 Time
->Minute
= RtcRead (RTC_ADDRESS_MINUTES_ALARM
);
618 Time
->Hour
= RtcRead (RTC_ADDRESS_HOURS_ALARM
);
619 Time
->Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
620 Time
->Month
= RtcRead (RTC_ADDRESS_MONTH
);
621 Time
->Year
= RtcRead (RTC_ADDRESS_YEAR
);
626 Time
->Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
627 Time
->Month
= RtcRead (RTC_ADDRESS_MONTH
);
628 Time
->Year
= RtcRead (RTC_ADDRESS_YEAR
);
631 ConvertRtcTimeToEfiTime (Time
, RegisterB
);
633 if (RtcTestCenturyRegister () == EFI_SUCCESS
) {
634 Century
= BcdToDecimal ((UINT8
) (RtcRead (RTC_ADDRESS_CENTURY
) & 0x7f));
636 Century
= BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY
));
639 Time
->Year
= (UINT16
) (Century
* 100 + Time
->Year
);
644 EfiReleaseLock (&mRtc
.RtcLock
);
647 // Make sure all field values are in correct range
649 Status
= RtcTimeFieldsValid (Time
);
650 if (EFI_ERROR (Status
)) {
651 return EFI_DEVICE_ERROR
;
654 *Pending
= RegisterC
.Bits
.AF
;
667 RTC_REGISTER_B RegisterB
;
669 EFI_TIME_CAPABILITIES Capabilities
;
674 return EFI_INVALID_PARAMETER
;
677 // Make sure that the time fields are valid
679 Status
= RtcTimeFieldsValid (Time
);
680 if (EFI_ERROR (Status
)) {
681 return EFI_INVALID_PARAMETER
;
684 // Just support set alarm time within 24 hours
686 LibGetTime (&RtcTime
, &Capabilities
);
687 if (Time
->Year
!= RtcTime
.Year
||
688 Time
->Month
!= RtcTime
.Month
||
689 (Time
->Day
!= RtcTime
.Day
&& Time
->Day
!= (RtcTime
.Day
+ 1))
691 return EFI_UNSUPPORTED
;
694 // Make a local copy of the time and date
696 CopyMem (&RtcTime
, Time
, sizeof (EFI_TIME
));
700 // Acquire RTC Lock to make access to RTC atomic
702 EfiAcquireLock (&mRtc
.RtcLock
);
705 // Wait for up to 0.1 seconds for the RTC to be updated
707 Status
= RtcWaitToUpdate (100000);
708 if (EFI_ERROR (Status
)) {
709 EfiReleaseLock (&mRtc
.RtcLock
);
710 return EFI_DEVICE_ERROR
;
713 // Read Register B, and inhibit updates of the RTC
715 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
717 RegisterB
.Bits
.SET
= 1;
718 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
721 ConvertEfiTimeToRtcTime (&RtcTime
, RegisterB
, &Century
);
724 // Set RTC alarm time
726 RtcWrite (RTC_ADDRESS_SECONDS_ALARM
, RtcTime
.Second
);
727 RtcWrite (RTC_ADDRESS_MINUTES_ALARM
, RtcTime
.Minute
);
728 RtcWrite (RTC_ADDRESS_HOURS_ALARM
, RtcTime
.Hour
);
730 RegisterB
.Bits
.AIE
= 1;
733 RegisterB
.Bits
.AIE
= 0;
736 // Allow updates of the RTC registers
738 RegisterB
.Bits
.SET
= 0;
739 RtcWrite (RTC_ADDRESS_REGISTER_B
, RegisterB
.Data
);
744 EfiReleaseLock (&mRtc
.RtcLock
);
752 LibRtcVirtualAddressChangeEvent (
765 RTC_REGISTER_A RegisterA
;
766 RTC_REGISTER_B RegisterB
;
767 RTC_REGISTER_C RegisterC
;
768 RTC_REGISTER_D RegisterD
;
773 // Acquire RTC Lock to make access to RTC atomic
775 EfiAcquireLock (&mRtc
.RtcLock
);
778 // Initialize RTC Register
780 // Make sure Division Chain is properly configured,
781 // or RTC clock won't "tick" -- time won't increment
783 RegisterA
.Data
= RTC_INIT_REGISTER_A
;
784 RtcWrite (RTC_ADDRESS_REGISTER_A
, RegisterA
.Data
);
789 RegisterB
.Data
= RtcRead (RTC_ADDRESS_REGISTER_B
);
792 // Clear RTC flag register
794 RegisterC
.Data
= RtcRead (RTC_ADDRESS_REGISTER_C
);
797 // Clear RTC register D
799 RegisterD
.Data
= RTC_INIT_REGISTER_D
;
800 RtcWrite (RTC_ADDRESS_REGISTER_D
, RegisterD
.Data
);
803 // Wait for up to 0.1 seconds for the RTC to be updated
805 Status
= RtcWaitToUpdate (100000);
806 if (EFI_ERROR (Status
)) {
807 EfiReleaseLock (&mRtc
.RtcLock
);
812 // Get the Time/Date/Daylight Savings values.
814 Time
.Second
= RtcRead (RTC_ADDRESS_SECONDS
);
815 Time
.Minute
= RtcRead (RTC_ADDRESS_MINUTES
);
816 Time
.Hour
= RtcRead (RTC_ADDRESS_HOURS
);
817 Time
.Day
= RtcRead (RTC_ADDRESS_DAY_OF_THE_MONTH
);
818 Time
.Month
= RtcRead (RTC_ADDRESS_MONTH
);
819 Time
.Year
= RtcRead (RTC_ADDRESS_YEAR
);
821 ConvertRtcTimeToEfiTime (&Time
, RegisterB
);
823 if (RtcTestCenturyRegister () == EFI_SUCCESS
) {
824 Century
= BcdToDecimal ((UINT8
) (RtcRead (RTC_ADDRESS_CENTURY
) & 0x7f));
826 Century
= BcdToDecimal (RtcRead (RTC_ADDRESS_CENTURY
));
829 Time
.Year
= (UINT16
) (Century
* 100 + Time
.Year
);
832 // Set RTC configuration after get original time
834 RtcWrite (RTC_ADDRESS_REGISTER_B
, RTC_INIT_REGISTER_B
);
839 EfiReleaseLock (&mRtc
.RtcLock
);
842 // Validate time fields
844 Status
= RtcTimeFieldsValid (&Time
);
845 if (EFI_ERROR (Status
)) {
846 Time
.Second
= RTC_INIT_SECOND
;
847 Time
.Minute
= RTC_INIT_MINUTE
;
848 Time
.Hour
= RTC_INIT_HOUR
;
849 Time
.Day
= RTC_INIT_DAY
;
850 Time
.Month
= RTC_INIT_MONTH
;
851 Time
.Year
= RTC_INIT_YEAR
;
854 // Reset time value according to new RTC configuration