• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/rtc/
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	status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
136
137	printk(KERN_WARNING "write status is %d\n", (int)status);
138
139	return status == EFI_SUCCESS ? 0 : -EINVAL;
140}
141
142static int efi_read_time(struct device *dev, struct rtc_time *tm)
143{
144	efi_status_t status;
145	efi_time_t eft;
146	efi_time_cap_t cap;
147
148	status = efi.get_time(&eft, &cap);
149
150	if (status != EFI_SUCCESS) {
151		/* should never happen */
152		printk(KERN_ERR "efitime: can't read time\n");
153		return -EINVAL;
154	}
155
156	convert_from_efi_time(&eft, tm);
157
158	return rtc_valid_tm(tm);
159}
160
161static int efi_set_time(struct device *dev, struct rtc_time *tm)
162{
163	efi_status_t status;
164	efi_time_t eft;
165
166	convert_to_efi_time(tm, &eft);
167
168	status = efi.set_time(&eft);
169
170	return status == EFI_SUCCESS ? 0 : -EINVAL;
171}
172
173static const struct rtc_class_ops efi_rtc_ops = {
174	.read_time = efi_read_time,
175	.set_time = efi_set_time,
176	.read_alarm = efi_read_alarm,
177	.set_alarm = efi_set_alarm,
178};
179
180static int __init efi_rtc_probe(struct platform_device *dev)
181{
182	struct rtc_device *rtc;
183
184	rtc = rtc_device_register("rtc-efi", &dev->dev, &efi_rtc_ops,
185					THIS_MODULE);
186	if (IS_ERR(rtc))
187		return PTR_ERR(rtc);
188
189	platform_set_drvdata(dev, rtc);
190
191	return 0;
192}
193
194static int __exit efi_rtc_remove(struct platform_device *dev)
195{
196	struct rtc_device *rtc = platform_get_drvdata(dev);
197
198	rtc_device_unregister(rtc);
199
200	return 0;
201}
202
203static struct platform_driver efi_rtc_driver = {
204	.driver = {
205		.name = "rtc-efi",
206		.owner = THIS_MODULE,
207	},
208	.probe = efi_rtc_probe,
209	.remove = __exit_p(efi_rtc_remove),
210};
211
212static int __init efi_rtc_init(void)
213{
214	return platform_driver_probe(&efi_rtc_driver, efi_rtc_probe);
215}
216
217static void __exit efi_rtc_exit(void)
218{
219	platform_driver_unregister(&efi_rtc_driver);
220}
221
222module_init(efi_rtc_init);
223module_exit(efi_rtc_exit);
224
225MODULE_AUTHOR("dann frazier <dannf@hp.com>");
226MODULE_LICENSE("GPL");
227MODULE_DESCRIPTION("EFI RTC driver");
228