aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/ia64/kernel/time.c16
-rw-r--r--drivers/rtc/Kconfig10
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-efi.c235
4 files changed, 262 insertions, 0 deletions
diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index f0ebb342409d..d6747bae52d8 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -20,6 +20,7 @@
20#include <linux/efi.h> 20#include <linux/efi.h>
21#include <linux/timex.h> 21#include <linux/timex.h>
22#include <linux/clocksource.h> 22#include <linux/clocksource.h>
23#include <linux/platform_device.h>
23 24
24#include <asm/machvec.h> 25#include <asm/machvec.h>
25#include <asm/delay.h> 26#include <asm/delay.h>
@@ -405,6 +406,21 @@ static struct irqaction timer_irqaction = {
405 .name = "timer" 406 .name = "timer"
406}; 407};
407 408
409static struct platform_device rtc_efi_dev = {
410 .name = "rtc-efi",
411 .id = -1,
412};
413
414static int __init rtc_init(void)
415{
416 if (platform_device_register(&rtc_efi_dev) < 0)
417 printk(KERN_ERR "unable to register rtc device...\n");
418
419 /* not necessarily an error */
420 return 0;
421}
422module_init(rtc_init);
423
408void __init 424void __init
409time_init (void) 425time_init (void)
410{ 426{
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 81450fbd8b12..d669b9169278 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -440,6 +440,16 @@ config RTC_DRV_DS1742
440 This driver can also be built as a module. If so, the module 440 This driver can also be built as a module. If so, the module
441 will be called rtc-ds1742. 441 will be called rtc-ds1742.
442 442
443config RTC_DRV_EFI
444 tristate "EFI RTC"
445 depends on IA64
446 help
447 If you say yes here you will get support for the EFI
448 Real Time Clock.
449
450 This driver can also be built as a module. If so, the module
451 will be called rtc-efi.
452
443config RTC_DRV_STK17TA8 453config RTC_DRV_STK17TA8
444 tristate "Simtek STK17TA8" 454 tristate "Simtek STK17TA8"
445 depends on RTC_CLASS 455 depends on RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 0e697aa51caa..e7b09986d26e 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
36obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o 36obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
37obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o 37obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
38obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o 38obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
39obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
39obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o 40obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
40obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o 41obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
41obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o 42obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
diff --git a/drivers/rtc/rtc-efi.c b/drivers/rtc/rtc-efi.c
new file mode 100644
index 000000000000..550292304b0f
--- /dev/null
+++ b/drivers/rtc/rtc-efi.c
@@ -0,0 +1,235 @@
1/*
2 * rtc-efi: RTC Class Driver for EFI-based systems
3 *
4 * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
5 *
6 * Author: dann frazier <dannf@hp.com>
7 * Based on efirtc.c by Stephane Eranian
8 *
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.
13 *
14 */
15
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>
22
23#define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT)
24/*
25 * EFI Epoch is 1/1/1998
26 */
27#define EFI_RTC_EPOCH 1998
28
29/*
30 * returns day of the year [0-365]
31 */
32static inline int
33compute_yday(efi_time_t *eft)
34{
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);
37}
38/*
39 * returns day of the week [0-6] 0=Sunday
40 *
41 * Don't try to provide a year that's before 1998, please !
42 */
43static int
44compute_wday(efi_time_t *eft)
45{
46 int y;
47 int ndays = 0;
48
49 if (eft->year < 1998) {
50 printk(KERN_ERR "efirtc: EFI year < 1998, invalid date\n");
51 return -1;
52 }
53
54 for (y = EFI_RTC_EPOCH; y < eft->year; y++)
55 ndays += 365 + (is_leap_year(y) ? 1 : 0);
56
57 ndays += compute_yday(eft);
58
59 /*
60 * 4=1/1/1998 was a Thursday
61 */
62 return (ndays + 4) % 7;
63}
64
65static void
66convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
67{
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;
74 eft->nanosecond = 0;
75 eft->daylight = wtime->tm_isdst ? EFI_ISDST : 0;
76 eft->timezone = EFI_UNSPECIFIED_TIMEZONE;
77}
78
79static void
80convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
81{
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;
89
90 /* day of the week [0-6], Sunday=0 */
91 wtime->tm_wday = compute_wday(eft);
92
93 /* day in the year [1-365]*/
94 wtime->tm_yday = compute_yday(eft);
95
96
97 switch (eft->daylight & EFI_ISDST) {
98 case EFI_ISDST:
99 wtime->tm_isdst = 1;
100 break;
101 case EFI_TIME_ADJUST_DAYLIGHT:
102 wtime->tm_isdst = 0;
103 break;
104 default:
105 wtime->tm_isdst = -1;
106 }
107}
108
109static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
110{
111 efi_time_t eft;
112 efi_status_t status;
113
114 /*
115 * As of EFI v1.10, this call always returns an unsupported status
116 */
117 status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
118 (efi_bool_t *)&wkalrm->pending, &eft);
119
120 if (status != EFI_SUCCESS)
121 return -EINVAL;
122
123 convert_from_efi_time(&eft, &wkalrm->time);
124
125 return rtc_valid_tm(&wkalrm->time);
126}
127
128static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
129{
130 efi_time_t eft;
131 efi_status_t status;
132
133 convert_to_efi_time(&wkalrm->time, &eft);
134
135 /*
136 * XXX Fixme:
137 * As of EFI 0.92 with the firmware I have on my
138 * machine this call does not seem to work quite
139 * right
140 *
141 * As of v1.10, this call always returns an unsupported status
142 */
143 status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
144
145 printk(KERN_WARNING "write status is %d\n", (int)status);
146
147 return status == EFI_SUCCESS ? 0 : -EINVAL;
148}
149
150static int efi_read_time(struct device *dev, struct rtc_time *tm)
151{
152 efi_status_t status;
153 efi_time_t eft;
154 efi_time_cap_t cap;
155
156 status = efi.get_time(&eft, &cap);
157
158 if (status != EFI_SUCCESS) {
159 /* should never happen */
160 printk(KERN_ERR "efitime: can't read time\n");
161 return -EINVAL;
162 }
163
164 convert_from_efi_time(&eft, tm);
165
166 return rtc_valid_tm(tm);
167}
168
169static int efi_set_time(struct device *dev, struct rtc_time *tm)
170{
171 efi_status_t status;
172 efi_time_t eft;
173
174 convert_to_efi_time(tm, &eft);
175
176 status = efi.set_time(&eft);
177
178 return status == EFI_SUCCESS ? 0 : -EINVAL;
179}
180
181static 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,
186};
187
188static int __init efi_rtc_probe(struct platform_device *dev)
189{
190 struct rtc_device *rtc;
191
192 rtc = rtc_device_register("rtc-efi", &dev->dev, &efi_rtc_ops,
193 THIS_MODULE);
194 if (IS_ERR(rtc))
195 return PTR_ERR(rtc);
196
197 platform_set_drvdata(dev, rtc);
198
199 return 0;
200}
201
202static int __exit efi_rtc_remove(struct platform_device *dev)
203{
204 struct rtc_device *rtc = platform_get_drvdata(dev);
205
206 rtc_device_unregister(rtc);
207
208 return 0;
209}
210
211static struct platform_driver efi_rtc_driver = {
212 .driver = {
213 .name = "rtc-efi",
214 .owner = THIS_MODULE,
215 },
216 .probe = efi_rtc_probe,
217 .remove = __exit_p(efi_rtc_remove),
218};
219
220static int __init efi_rtc_init(void)
221{
222 return platform_driver_probe(&efi_rtc_driver, efi_rtc_probe);
223}
224
225static void __exit efi_rtc_exit(void)
226{
227 platform_driver_unregister(&efi_rtc_driver);
228}
229
230module_init(efi_rtc_init);
231module_exit(efi_rtc_exit);
232
233MODULE_AUTHOR("dann frazier <dannf@hp.com>");
234MODULE_LICENSE("GPL");
235MODULE_DESCRIPTION("EFI RTC driver");