2 * rtc-efi: RTC Class Driver for EFI-based systems
4 * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
6 * Author: dann frazier <dannf@hp.com>
7 * Based on efirtc.c by Stephane Eranian
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/time.h>
19 #include <linux/platform_device.h>
20 #include <linux/rtc.h>
21 #include <linux/efi.h>
23 #define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT)
25 * EFI Epoch is 1/1/1998
27 #define EFI_RTC_EPOCH 1998
30 * returns day of the year [0-365]
33 compute_yday(efi_time_t
*eft
)
35 /* efi_time_t.month is in the [1-12] so, we need -1 */
36 return rtc_year_days(eft
->day
- 1, eft
->month
- 1, eft
->year
);
39 * returns day of the week [0-6] 0=Sunday
41 * Don't try to provide a year that's before 1998, please !
44 compute_wday(efi_time_t
*eft
)
49 if (eft
->year
< 1998) {
50 printk(KERN_ERR
"efirtc: EFI year < 1998, invalid date\n");
54 for (y
= EFI_RTC_EPOCH
; y
< eft
->year
; y
++)
55 ndays
+= 365 + (is_leap_year(y
) ? 1 : 0);
57 ndays
+= compute_yday(eft
);
60 * 4=1/1/1998 was a Thursday
62 return (ndays
+ 4) % 7;
66 convert_to_efi_time(struct rtc_time
*wtime
, efi_time_t
*eft
)
68 eft
->year
= wtime
->tm_year
+ 1900;
69 eft
->month
= wtime
->tm_mon
+ 1;
70 eft
->day
= wtime
->tm_mday
;
71 eft
->hour
= wtime
->tm_hour
;
72 eft
->minute
= wtime
->tm_min
;
73 eft
->second
= wtime
->tm_sec
;
75 eft
->daylight
= wtime
->tm_isdst
? EFI_ISDST
: 0;
76 eft
->timezone
= EFI_UNSPECIFIED_TIMEZONE
;
80 convert_from_efi_time(efi_time_t
*eft
, struct rtc_time
*wtime
)
82 memset(wtime
, 0, sizeof(*wtime
));
83 wtime
->tm_sec
= eft
->second
;
84 wtime
->tm_min
= eft
->minute
;
85 wtime
->tm_hour
= eft
->hour
;
86 wtime
->tm_mday
= eft
->day
;
87 wtime
->tm_mon
= eft
->month
- 1;
88 wtime
->tm_year
= eft
->year
- 1900;
90 /* day of the week [0-6], Sunday=0 */
91 wtime
->tm_wday
= compute_wday(eft
);
93 /* day in the year [1-365]*/
94 wtime
->tm_yday
= compute_yday(eft
);
97 switch (eft
->daylight
& EFI_ISDST
) {
101 case EFI_TIME_ADJUST_DAYLIGHT
:
105 wtime
->tm_isdst
= -1;
109 static int efi_read_alarm(struct device
*dev
, struct rtc_wkalrm
*wkalrm
)
115 * As of EFI v1.10, this call always returns an unsupported status
117 status
= efi
.get_wakeup_time((efi_bool_t
*)&wkalrm
->enabled
,
118 (efi_bool_t
*)&wkalrm
->pending
, &eft
);
120 if (status
!= EFI_SUCCESS
)
123 convert_from_efi_time(&eft
, &wkalrm
->time
);
125 return rtc_valid_tm(&wkalrm
->time
);
128 static int efi_set_alarm(struct device
*dev
, struct rtc_wkalrm
*wkalrm
)
133 convert_to_efi_time(&wkalrm
->time
, &eft
);
137 * As of EFI 0.92 with the firmware I have on my
138 * machine this call does not seem to work quite
141 * As of v1.10, this call always returns an unsupported status
143 status
= efi
.set_wakeup_time((efi_bool_t
)wkalrm
->enabled
, &eft
);
145 printk(KERN_WARNING
"write status is %d\n", (int)status
);
147 return status
== EFI_SUCCESS
? 0 : -EINVAL
;
150 static int efi_read_time(struct device
*dev
, struct rtc_time
*tm
)
156 status
= efi
.get_time(&eft
, &cap
);
158 if (status
!= EFI_SUCCESS
) {
159 /* should never happen */
160 printk(KERN_ERR
"efitime: can't read time\n");
164 convert_from_efi_time(&eft
, tm
);
166 return rtc_valid_tm(tm
);
169 static int efi_set_time(struct device
*dev
, struct rtc_time
*tm
)
174 convert_to_efi_time(tm
, &eft
);
176 status
= efi
.set_time(&eft
);
178 return status
== EFI_SUCCESS
? 0 : -EINVAL
;
181 static const struct rtc_class_ops efi_rtc_ops
= {
182 .read_time
= efi_read_time
,
183 .set_time
= efi_set_time
,
184 .read_alarm
= efi_read_alarm
,
185 .set_alarm
= efi_set_alarm
,
188 static int __init
efi_rtc_probe(struct platform_device
*dev
)
190 struct rtc_device
*rtc
;
192 rtc
= rtc_device_register("rtc-efi", &dev
->dev
, &efi_rtc_ops
,
197 platform_set_drvdata(dev
, rtc
);
202 static int __exit
efi_rtc_remove(struct platform_device
*dev
)
204 struct rtc_device
*rtc
= platform_get_drvdata(dev
);
206 rtc_device_unregister(rtc
);
211 static struct platform_driver efi_rtc_driver
= {
214 .owner
= THIS_MODULE
,
216 .remove
= __exit_p(efi_rtc_remove
),
219 static int __init
efi_rtc_init(void)
221 return platform_driver_probe(&efi_rtc_driver
, efi_rtc_probe
);
224 static void __exit
efi_rtc_exit(void)
226 platform_driver_unregister(&efi_rtc_driver
);
229 module_init(efi_rtc_init
);
230 module_exit(efi_rtc_exit
);
232 MODULE_AUTHOR("dann frazier <dannf@hp.com>");
233 MODULE_LICENSE("GPL");
234 MODULE_DESCRIPTION("EFI RTC driver");