Blame view

drivers/rtc/rtc-efi.c 6.57 KB
5e3fd9e58   dann frazier   rtc: add platform...
1
2
3
4
5
  /*
   * rtc-efi: RTC Class Driver for EFI-based systems
   *
   * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
   *
37563e5ec   dann frazier   rtc: efi: Update ...
6
   * Author: dann frazier <dannf@dannf.org>
5e3fd9e58   dann frazier   rtc: add platform...
7
8
9
10
11
12
13
14
   * Based on efirtc.c by Stephane Eranian
   *
   *  This program is free software; you can redistribute  it and/or modify it
   *  under  the terms of  the GNU General  Public License as published by the
   *  Free Software Foundation;  either version 2 of the  License, or (at your
   *  option) any later version.
   *
   */
34650f9ea   Jingoo Han   rtc: rtc-efi: use...
15
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
5e3fd9e58   dann frazier   rtc: add platform...
16
17
  #include <linux/kernel.h>
  #include <linux/module.h>
6e85bab6b   Jan Beulich   drivers/rtc/rtc-e...
18
  #include <linux/stringify.h>
5e3fd9e58   dann frazier   rtc: add platform...
19
20
21
22
23
24
  #include <linux/time.h>
  #include <linux/platform_device.h>
  #include <linux/rtc.h>
  #include <linux/efi.h>
  
  #define EFI_ISDST (EFI_TIME_ADJUST_DAYLIGHT|EFI_TIME_IN_DAYLIGHT)
5e3fd9e58   dann frazier   rtc: add platform...
25
26
27
28
29
30
31
32
  
  /*
   * returns day of the year [0-365]
   */
  static inline int
  compute_yday(efi_time_t *eft)
  {
  	/* efi_time_t.month is in the [1-12] so, we need -1 */
809d96270   Lee, Chun-Yi   drivers/rtc/rtc-e...
33
  	return rtc_year_days(eft->day, eft->month - 1, eft->year);
5e3fd9e58   dann frazier   rtc: add platform...
34
  }
b2bd2370a   Ard Biesheuvel   rtc: efi: use cor...
35

5e3fd9e58   dann frazier   rtc: add platform...
36
37
  /*
   * returns day of the week [0-6] 0=Sunday
5e3fd9e58   dann frazier   rtc: add platform...
38
39
   */
  static int
b2bd2370a   Ard Biesheuvel   rtc: efi: use cor...
40
  compute_wday(efi_time_t *eft, int yday)
5e3fd9e58   dann frazier   rtc: add platform...
41
  {
b2bd2370a   Ard Biesheuvel   rtc: efi: use cor...
42
43
44
45
46
  	int ndays = eft->year * (365 % 7)
  		    + (eft->year - 1) / 4
  		    - (eft->year - 1) / 100
  		    + (eft->year - 1) / 400
  		    + yday;
5e3fd9e58   dann frazier   rtc: add platform...
47
48
  
  	/*
b2bd2370a   Ard Biesheuvel   rtc: efi: use cor...
49
50
  	 * 1/1/0000 may or may not have been a Sunday (if it ever existed at
  	 * all) but assuming it was makes this calculation work correctly.
5e3fd9e58   dann frazier   rtc: add platform...
51
  	 */
b2bd2370a   Ard Biesheuvel   rtc: efi: use cor...
52
  	return ndays % 7;
5e3fd9e58   dann frazier   rtc: add platform...
53
54
55
56
57
58
59
60
61
62
  }
  
  static void
  convert_to_efi_time(struct rtc_time *wtime, efi_time_t *eft)
  {
  	eft->year	= wtime->tm_year + 1900;
  	eft->month	= wtime->tm_mon + 1;
  	eft->day	= wtime->tm_mday;
  	eft->hour	= wtime->tm_hour;
  	eft->minute	= wtime->tm_min;
34650f9ea   Jingoo Han   rtc: rtc-efi: use...
63
  	eft->second	= wtime->tm_sec;
5e3fd9e58   dann frazier   rtc: add platform...
64
65
66
67
  	eft->nanosecond = 0;
  	eft->daylight	= wtime->tm_isdst ? EFI_ISDST : 0;
  	eft->timezone	= EFI_UNSPECIFIED_TIMEZONE;
  }
6e85bab6b   Jan Beulich   drivers/rtc/rtc-e...
68
  static bool
5e3fd9e58   dann frazier   rtc: add platform...
69
70
71
  convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
  {
  	memset(wtime, 0, sizeof(*wtime));
6e85bab6b   Jan Beulich   drivers/rtc/rtc-e...
72
73
74
  
  	if (eft->second >= 60)
  		return false;
5e3fd9e58   dann frazier   rtc: add platform...
75
  	wtime->tm_sec  = eft->second;
6e85bab6b   Jan Beulich   drivers/rtc/rtc-e...
76
77
78
  
  	if (eft->minute >= 60)
  		return false;
5e3fd9e58   dann frazier   rtc: add platform...
79
  	wtime->tm_min  = eft->minute;
6e85bab6b   Jan Beulich   drivers/rtc/rtc-e...
80
81
82
  
  	if (eft->hour >= 24)
  		return false;
5e3fd9e58   dann frazier   rtc: add platform...
83
  	wtime->tm_hour = eft->hour;
6e85bab6b   Jan Beulich   drivers/rtc/rtc-e...
84
85
86
  
  	if (!eft->day || eft->day > 31)
  		return false;
5e3fd9e58   dann frazier   rtc: add platform...
87
  	wtime->tm_mday = eft->day;
6e85bab6b   Jan Beulich   drivers/rtc/rtc-e...
88
89
90
  
  	if (!eft->month || eft->month > 12)
  		return false;
5e3fd9e58   dann frazier   rtc: add platform...
91
  	wtime->tm_mon  = eft->month - 1;
5e3fd9e58   dann frazier   rtc: add platform...
92

b2bd2370a   Ard Biesheuvel   rtc: efi: use cor...
93
  	if (eft->year < 1900 || eft->year > 9999)
6e85bab6b   Jan Beulich   drivers/rtc/rtc-e...
94
  		return false;
b2bd2370a   Ard Biesheuvel   rtc: efi: use cor...
95
  	wtime->tm_year = eft->year - 1900;
5e3fd9e58   dann frazier   rtc: add platform...
96
97
98
  
  	/* day in the year [1-365]*/
  	wtime->tm_yday = compute_yday(eft);
b2bd2370a   Ard Biesheuvel   rtc: efi: use cor...
99
100
  	/* day of the week [0-6], Sunday=0 */
  	wtime->tm_wday = compute_wday(eft, wtime->tm_yday);
5e3fd9e58   dann frazier   rtc: add platform...
101
102
103
104
105
106
107
108
109
110
111
  
  	switch (eft->daylight & EFI_ISDST) {
  	case EFI_ISDST:
  		wtime->tm_isdst = 1;
  		break;
  	case EFI_TIME_ADJUST_DAYLIGHT:
  		wtime->tm_isdst = 0;
  		break;
  	default:
  		wtime->tm_isdst = -1;
  	}
6e85bab6b   Jan Beulich   drivers/rtc/rtc-e...
112
113
  
  	return true;
5e3fd9e58   dann frazier   rtc: add platform...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  }
  
  static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
  {
  	efi_time_t eft;
  	efi_status_t status;
  
  	/*
  	 * As of EFI v1.10, this call always returns an unsupported status
  	 */
  	status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
  				     (efi_bool_t *)&wkalrm->pending, &eft);
  
  	if (status != EFI_SUCCESS)
  		return -EINVAL;
6e85bab6b   Jan Beulich   drivers/rtc/rtc-e...
129
130
  	if (!convert_from_efi_time(&eft, &wkalrm->time))
  		return -EIO;
5e3fd9e58   dann frazier   rtc: add platform...
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  
  	return rtc_valid_tm(&wkalrm->time);
  }
  
  static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
  {
  	efi_time_t eft;
  	efi_status_t status;
  
  	convert_to_efi_time(&wkalrm->time, &eft);
  
  	/*
  	 * XXX Fixme:
  	 * As of EFI 0.92 with the firmware I have on my
  	 * machine this call does not seem to work quite
  	 * right
  	 *
  	 * As of v1.10, this call always returns an unsupported status
  	 */
  	status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
34650f9ea   Jingoo Han   rtc: rtc-efi: use...
151
152
  	dev_warn(dev, "write status is %d
  ", (int)status);
5e3fd9e58   dann frazier   rtc: add platform...
153
154
155
156
157
158
159
160
161
162
163
164
165
166
  
  	return status == EFI_SUCCESS ? 0 : -EINVAL;
  }
  
  static int efi_read_time(struct device *dev, struct rtc_time *tm)
  {
  	efi_status_t status;
  	efi_time_t eft;
  	efi_time_cap_t cap;
  
  	status = efi.get_time(&eft, &cap);
  
  	if (status != EFI_SUCCESS) {
  		/* should never happen */
34650f9ea   Jingoo Han   rtc: rtc-efi: use...
167
168
  		dev_err(dev, "can't read time
  ");
5e3fd9e58   dann frazier   rtc: add platform...
169
170
  		return -EINVAL;
  	}
6e85bab6b   Jan Beulich   drivers/rtc/rtc-e...
171
172
  	if (!convert_from_efi_time(&eft, tm))
  		return -EIO;
5e3fd9e58   dann frazier   rtc: add platform...
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
  
  	return rtc_valid_tm(tm);
  }
  
  static int efi_set_time(struct device *dev, struct rtc_time *tm)
  {
  	efi_status_t status;
  	efi_time_t eft;
  
  	convert_to_efi_time(tm, &eft);
  
  	status = efi.set_time(&eft);
  
  	return status == EFI_SUCCESS ? 0 : -EINVAL;
  }
501385f2a   Geliang Tang   rtc: efi: add efi...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
  static int efi_procfs(struct device *dev, struct seq_file *seq)
  {
  	efi_time_t      eft, alm;
  	efi_time_cap_t  cap;
  	efi_bool_t      enabled, pending;
  
  	memset(&eft, 0, sizeof(eft));
  	memset(&alm, 0, sizeof(alm));
  	memset(&cap, 0, sizeof(cap));
  
  	efi.get_time(&eft, &cap);
  	efi.get_wakeup_time(&enabled, &pending, &alm);
  
  	seq_printf(seq,
  		   "Time\t\t: %u:%u:%u.%09u
  "
  		   "Date\t\t: %u-%u-%u
  "
  		   "Daylight\t: %u
  ",
  		   eft.hour, eft.minute, eft.second, eft.nanosecond,
  		   eft.year, eft.month, eft.day,
  		   eft.daylight);
  
  	if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE)
  		seq_puts(seq, "Timezone\t: unspecified
  ");
  	else
  		/* XXX fixme: convert to string? */
  		seq_printf(seq, "Timezone\t: %u
  ", eft.timezone);
  
  	seq_printf(seq,
  		   "Alarm Time\t: %u:%u:%u.%09u
  "
  		   "Alarm Date\t: %u-%u-%u
  "
  		   "Alarm Daylight\t: %u
  "
  		   "Enabled\t\t: %s
  "
  		   "Pending\t\t: %s
  ",
  		   alm.hour, alm.minute, alm.second, alm.nanosecond,
  		   alm.year, alm.month, alm.day,
  		   alm.daylight,
  		   enabled == 1 ? "yes" : "no",
  		   pending == 1 ? "yes" : "no");
  
  	if (eft.timezone == EFI_UNSPECIFIED_TIMEZONE)
  		seq_puts(seq, "Timezone\t: unspecified
  ");
  	else
  		/* XXX fixme: convert to string? */
  		seq_printf(seq, "Timezone\t: %u
  ", alm.timezone);
  
  	/*
  	 * now prints the capabilities
  	 */
  	seq_printf(seq,
  		   "Resolution\t: %u
  "
  		   "Accuracy\t: %u
  "
  		   "SetstoZero\t: %u
  ",
  		   cap.resolution, cap.accuracy, cap.sets_to_zero);
  
  	return 0;
  }
5e3fd9e58   dann frazier   rtc: add platform...
259
  static const struct rtc_class_ops efi_rtc_ops = {
501385f2a   Geliang Tang   rtc: efi: add efi...
260
261
262
263
264
  	.read_time	= efi_read_time,
  	.set_time	= efi_set_time,
  	.read_alarm	= efi_read_alarm,
  	.set_alarm	= efi_set_alarm,
  	.proc		= efi_procfs,
5e3fd9e58   dann frazier   rtc: add platform...
265
266
267
268
269
  };
  
  static int __init efi_rtc_probe(struct platform_device *dev)
  {
  	struct rtc_device *rtc;
7368c69c0   Alexander Graf   rtc: efi: Fail pr...
270
271
272
273
274
275
  	efi_time_t eft;
  	efi_time_cap_t cap;
  
  	/* First check if the RTC is usable */
  	if (efi.get_time(&eft, &cap) != EFI_SUCCESS)
  		return -ENODEV;
5e3fd9e58   dann frazier   rtc: add platform...
276

6ae15b0eb   Jingoo Han   rtc: rtc-efi: use...
277
  	rtc = devm_rtc_device_register(&dev->dev, "rtc-efi", &efi_rtc_ops,
5e3fd9e58   dann frazier   rtc: add platform...
278
279
280
  					THIS_MODULE);
  	if (IS_ERR(rtc))
  		return PTR_ERR(rtc);
822a02792   Ard Biesheuvel   efi: rtc-efi: Mar...
281
  	rtc->uie_unsupported = 1;
5e3fd9e58   dann frazier   rtc: add platform...
282
283
284
285
  	platform_set_drvdata(dev, rtc);
  
  	return 0;
  }
5e3fd9e58   dann frazier   rtc: add platform...
286
287
288
  static struct platform_driver efi_rtc_driver = {
  	.driver = {
  		.name = "rtc-efi",
5e3fd9e58   dann frazier   rtc: add platform...
289
  	},
5e3fd9e58   dann frazier   rtc: add platform...
290
  };
52c6ecbc8   Jingoo Han   rtc: rtc-efi: use...
291
  module_platform_driver_probe(efi_rtc_driver, efi_rtc_probe);
5e3fd9e58   dann frazier   rtc: add platform...
292

451ff6d40   Pali Rohár   drivers/rtc/rtc-e...
293
  MODULE_ALIAS("platform:rtc-efi");
37563e5ec   dann frazier   rtc: efi: Update ...
294
  MODULE_AUTHOR("dann frazier <dannf@dannf.org>");
5e3fd9e58   dann frazier   rtc: add platform...
295
296
  MODULE_LICENSE("GPL");
  MODULE_DESCRIPTION("EFI RTC driver");
3f71f6da7   Ard Biesheuvel   efi: rtc-efi: Exp...
297
  MODULE_ALIAS("platform:rtc-efi");