blob: a8f22ee726bb6113c16961a865509dadf5badefc [file] [log] [blame]
Alessandro Zummoc5c3e192006-03-27 01:16:39 -08001/*
2 * RTC subsystem, sysfs interface
3 *
4 * Copyright (C) 2005 Tower Technologies
5 * Author: Alessandro Zummo <a.zummo@towertech.it>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10*/
11
12#include <linux/module.h>
13#include <linux/rtc.h>
14
David Brownellab6a2d72007-05-08 00:33:30 -070015#include "rtc-core.h"
16
17
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080018/* device attributes */
19
David Brownell8a0bdfd72008-02-06 01:38:45 -080020/*
21 * NOTE: RTC times displayed in sysfs use the RTC's timezone. That's
22 * ideally UTC. However, PCs that also boot to MS-Windows normally use
23 * the local time and change to match daylight savings time. That affects
24 * attributes including date, time, since_epoch, and wakealarm.
25 */
26
David Brownellcd966202007-05-08 00:33:40 -070027static ssize_t
Greg Kroah-Hartmanf21e6832013-07-24 15:05:22 -070028name_show(struct device *dev, struct device_attribute *attr, char *buf)
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080029{
Alexandre Belloni77a73f32017-06-02 13:57:03 +020030 return sprintf(buf, "%s %s\n", dev_driver_string(dev->parent),
31 dev_name(dev->parent));
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080032}
Greg Kroah-Hartmanf21e6832013-07-24 15:05:22 -070033static DEVICE_ATTR_RO(name);
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080034
David Brownellcd966202007-05-08 00:33:40 -070035static ssize_t
Greg Kroah-Hartmanf21e6832013-07-24 15:05:22 -070036date_show(struct device *dev, struct device_attribute *attr, char *buf)
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080037{
38 ssize_t retval;
39 struct rtc_time tm;
40
David Brownellab6a2d72007-05-08 00:33:30 -070041 retval = rtc_read_time(to_rtc_device(dev), &tm);
Andy Shevchenko5548cbf2018-12-04 23:23:12 +020042 if (retval)
43 return retval;
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080044
Andy Shevchenko5548cbf2018-12-04 23:23:12 +020045 return sprintf(buf, "%ptRd\n", &tm);
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080046}
Greg Kroah-Hartmanf21e6832013-07-24 15:05:22 -070047static DEVICE_ATTR_RO(date);
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080048
David Brownellcd966202007-05-08 00:33:40 -070049static ssize_t
Greg Kroah-Hartmanf21e6832013-07-24 15:05:22 -070050time_show(struct device *dev, struct device_attribute *attr, char *buf)
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080051{
52 ssize_t retval;
53 struct rtc_time tm;
54
David Brownellab6a2d72007-05-08 00:33:30 -070055 retval = rtc_read_time(to_rtc_device(dev), &tm);
Andy Shevchenko5548cbf2018-12-04 23:23:12 +020056 if (retval)
57 return retval;
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080058
Andy Shevchenko5548cbf2018-12-04 23:23:12 +020059 return sprintf(buf, "%ptRt\n", &tm);
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080060}
Greg Kroah-Hartmanf21e6832013-07-24 15:05:22 -070061static DEVICE_ATTR_RO(time);
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080062
David Brownellcd966202007-05-08 00:33:40 -070063static ssize_t
Greg Kroah-Hartmanf21e6832013-07-24 15:05:22 -070064since_epoch_show(struct device *dev, struct device_attribute *attr, char *buf)
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080065{
66 ssize_t retval;
67 struct rtc_time tm;
68
David Brownellab6a2d72007-05-08 00:33:30 -070069 retval = rtc_read_time(to_rtc_device(dev), &tm);
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080070 if (retval == 0) {
Baolin Wang9a06da22017-11-09 15:09:20 +080071 time64_t time;
72
73 time = rtc_tm_to_time64(&tm);
74 retval = sprintf(buf, "%lld\n", time);
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080075 }
76
77 return retval;
78}
Greg Kroah-Hartmanf21e6832013-07-24 15:05:22 -070079static DEVICE_ATTR_RO(since_epoch);
Alessandro Zummoc5c3e192006-03-27 01:16:39 -080080
Bryan Kadzban06c65eb2007-10-16 01:28:22 -070081static ssize_t
Greg Kroah-Hartmanf21e6832013-07-24 15:05:22 -070082max_user_freq_show(struct device *dev, struct device_attribute *attr, char *buf)
Bryan Kadzban06c65eb2007-10-16 01:28:22 -070083{
84 return sprintf(buf, "%d\n", to_rtc_device(dev)->max_user_freq);
85}
86
87static ssize_t
Greg Kroah-Hartmanf21e6832013-07-24 15:05:22 -070088max_user_freq_store(struct device *dev, struct device_attribute *attr,
Bryan Kadzban06c65eb2007-10-16 01:28:22 -070089 const char *buf, size_t n)
90{
91 struct rtc_device *rtc = to_rtc_device(dev);
LABBE Corentinf5712872015-12-17 14:11:04 +010092 unsigned long val;
93 int err;
94
95 err = kstrtoul(buf, 0, &val);
96 if (err)
97 return err;
Bryan Kadzban06c65eb2007-10-16 01:28:22 -070098
99 if (val >= 4096 || val == 0)
100 return -EINVAL;
101
102 rtc->max_user_freq = (int)val;
103
104 return n;
105}
Greg Kroah-Hartmanf21e6832013-07-24 15:05:22 -0700106static DEVICE_ATTR_RW(max_user_freq);
Bryan Kadzban06c65eb2007-10-16 01:28:22 -0700107
David Fries4c24e292012-10-04 17:14:12 -0700108/**
109 * rtc_sysfs_show_hctosys - indicate if the given RTC set the system time
110 *
111 * Returns 1 if the system clock was set by this RTC at the last
112 * boot or resume event.
113 */
Matthew Garrettd8c1acb2009-09-22 16:46:32 -0700114static ssize_t
Greg Kroah-Hartmanf21e6832013-07-24 15:05:22 -0700115hctosys_show(struct device *dev, struct device_attribute *attr, char *buf)
Matthew Garrettd8c1acb2009-09-22 16:46:32 -0700116{
117#ifdef CONFIG_RTC_HCTOSYS_DEVICE
Uwe Kleine-Königd0ab4a42010-03-10 15:20:35 -0800118 if (rtc_hctosys_ret == 0 &&
119 strcmp(dev_name(&to_rtc_device(dev)->dev),
120 CONFIG_RTC_HCTOSYS_DEVICE) == 0)
Matthew Garrettd8c1acb2009-09-22 16:46:32 -0700121 return sprintf(buf, "1\n");
122 else
123#endif
124 return sprintf(buf, "0\n");
125}
Greg Kroah-Hartmanf21e6832013-07-24 15:05:22 -0700126static DEVICE_ATTR_RO(hctosys);
Matthew Garrettd8c1acb2009-09-22 16:46:32 -0700127
David Brownell3925a5c2007-02-12 00:52:47 -0800128static ssize_t
Dmitry Torokhova17ccd12015-07-23 16:01:07 -0700129wakealarm_show(struct device *dev, struct device_attribute *attr, char *buf)
David Brownell3925a5c2007-02-12 00:52:47 -0800130{
131 ssize_t retval;
Baolin Wang9a06da22017-11-09 15:09:20 +0800132 time64_t alarm;
David Brownell3925a5c2007-02-12 00:52:47 -0800133 struct rtc_wkalrm alm;
134
David Brownell8a0bdfd72008-02-06 01:38:45 -0800135 /* Don't show disabled alarms. For uniformity, RTC alarms are
136 * conceptually one-shot, even though some common RTCs (on PCs)
137 * don't actually work that way.
David Brownell3925a5c2007-02-12 00:52:47 -0800138 *
David Brownell8a0bdfd72008-02-06 01:38:45 -0800139 * NOTE: RTC implementations where the alarm doesn't match an
140 * exact YYYY-MM-DD HH:MM[:SS] date *must* disable their RTC
141 * alarms after they trigger, to ensure one-shot semantics.
David Brownell3925a5c2007-02-12 00:52:47 -0800142 */
David Brownellab6a2d72007-05-08 00:33:30 -0700143 retval = rtc_read_alarm(to_rtc_device(dev), &alm);
David Brownell3925a5c2007-02-12 00:52:47 -0800144 if (retval == 0 && alm.enabled) {
Baolin Wang9a06da22017-11-09 15:09:20 +0800145 alarm = rtc_tm_to_time64(&alm.time);
146 retval = sprintf(buf, "%lld\n", alarm);
David Brownell3925a5c2007-02-12 00:52:47 -0800147 }
148
149 return retval;
150}
151
152static ssize_t
Dmitry Torokhova17ccd12015-07-23 16:01:07 -0700153wakealarm_store(struct device *dev, struct device_attribute *attr,
David Brownellcd966202007-05-08 00:33:40 -0700154 const char *buf, size_t n)
David Brownell3925a5c2007-02-12 00:52:47 -0800155{
156 ssize_t retval;
Baolin Wang9a06da22017-11-09 15:09:20 +0800157 time64_t now, alarm;
158 time64_t push = 0;
David Brownell3925a5c2007-02-12 00:52:47 -0800159 struct rtc_wkalrm alm;
David Brownellab6a2d72007-05-08 00:33:30 -0700160 struct rtc_device *rtc = to_rtc_device(dev);
LABBE Corentin84281c22016-08-12 14:46:14 +0200161 const char *buf_ptr;
Zhao Yakuic116bc22008-04-28 02:11:58 -0700162 int adjust = 0;
David Brownell3925a5c2007-02-12 00:52:47 -0800163
164 /* Only request alarms that trigger in the future. Disable them
165 * by writing another time, e.g. 0 meaning Jan 1 1970 UTC.
166 */
David Brownellab6a2d72007-05-08 00:33:30 -0700167 retval = rtc_read_time(rtc, &alm.time);
David Brownell3925a5c2007-02-12 00:52:47 -0800168 if (retval < 0)
169 return retval;
Baolin Wang9a06da22017-11-09 15:09:20 +0800170 now = rtc_tm_to_time64(&alm.time);
David Brownell3925a5c2007-02-12 00:52:47 -0800171
LABBE Corentin84281c22016-08-12 14:46:14 +0200172 buf_ptr = buf;
Zhao Yakuic116bc22008-04-28 02:11:58 -0700173 if (*buf_ptr == '+') {
174 buf_ptr++;
Bernie Thompson1df0a472013-07-03 15:07:03 -0700175 if (*buf_ptr == '=') {
176 buf_ptr++;
177 push = 1;
178 } else
179 adjust = 1;
Zhao Yakuic116bc22008-04-28 02:11:58 -0700180 }
Baolin Wang9a06da22017-11-09 15:09:20 +0800181 retval = kstrtos64(buf_ptr, 0, &alarm);
LABBE Corentinf5712872015-12-17 14:11:04 +0100182 if (retval)
183 return retval;
Zhao Yakuic116bc22008-04-28 02:11:58 -0700184 if (adjust) {
185 alarm += now;
186 }
Bernie Thompson1df0a472013-07-03 15:07:03 -0700187 if (alarm > now || push) {
David Brownell3925a5c2007-02-12 00:52:47 -0800188 /* Avoid accidentally clobbering active alarms; we can't
189 * entirely prevent that here, without even the minimal
190 * locking from the /dev/rtcN api.
191 */
David Brownellab6a2d72007-05-08 00:33:30 -0700192 retval = rtc_read_alarm(rtc, &alm);
David Brownell3925a5c2007-02-12 00:52:47 -0800193 if (retval < 0)
194 return retval;
Bernie Thompson1df0a472013-07-03 15:07:03 -0700195 if (alm.enabled) {
196 if (push) {
Baolin Wang9a06da22017-11-09 15:09:20 +0800197 push = rtc_tm_to_time64(&alm.time);
Bernie Thompson1df0a472013-07-03 15:07:03 -0700198 alarm += push;
199 } else
200 return -EBUSY;
201 } else if (push)
202 return -EINVAL;
David Brownell3925a5c2007-02-12 00:52:47 -0800203 alm.enabled = 1;
204 } else {
205 alm.enabled = 0;
206
207 /* Provide a valid future alarm time. Linux isn't EFI,
208 * this time won't be ignored when disabling the alarm.
209 */
210 alarm = now + 300;
211 }
Baolin Wang9a06da22017-11-09 15:09:20 +0800212 rtc_time64_to_tm(alarm, &alm.time);
David Brownell3925a5c2007-02-12 00:52:47 -0800213
David Brownellab6a2d72007-05-08 00:33:30 -0700214 retval = rtc_set_alarm(rtc, &alm);
David Brownell3925a5c2007-02-12 00:52:47 -0800215 return (retval < 0) ? retval : n;
216}
Dmitry Torokhova17ccd12015-07-23 16:01:07 -0700217static DEVICE_ATTR_RW(wakealarm);
David Brownell3925a5c2007-02-12 00:52:47 -0800218
Joshua Clayton5495a412016-02-05 12:41:12 -0800219static ssize_t
220offset_show(struct device *dev, struct device_attribute *attr, char *buf)
221{
222 ssize_t retval;
223 long offset;
224
225 retval = rtc_read_offset(to_rtc_device(dev), &offset);
226 if (retval == 0)
227 retval = sprintf(buf, "%ld\n", offset);
228
229 return retval;
230}
231
232static ssize_t
233offset_store(struct device *dev, struct device_attribute *attr,
234 const char *buf, size_t n)
235{
236 ssize_t retval;
237 long offset;
238
239 retval = kstrtol(buf, 10, &offset);
240 if (retval == 0)
241 retval = rtc_set_offset(to_rtc_device(dev), offset);
242
243 return (retval < 0) ? retval : n;
244}
245static DEVICE_ATTR_RW(offset);
246
Alexandre Belloni71db0492018-02-17 14:58:40 +0100247static ssize_t
248range_show(struct device *dev, struct device_attribute *attr, char *buf)
249{
250 return sprintf(buf, "[%lld,%llu]\n", to_rtc_device(dev)->range_min,
251 to_rtc_device(dev)->range_max);
252}
253static DEVICE_ATTR_RO(range);
254
Dmitry Torokhov3ee2c402015-07-23 16:01:08 -0700255static struct attribute *rtc_attrs[] = {
256 &dev_attr_name.attr,
257 &dev_attr_date.attr,
258 &dev_attr_time.attr,
259 &dev_attr_since_epoch.attr,
260 &dev_attr_max_user_freq.attr,
261 &dev_attr_hctosys.attr,
262 &dev_attr_wakealarm.attr,
Joshua Clayton5495a412016-02-05 12:41:12 -0800263 &dev_attr_offset.attr,
Alexandre Belloni71db0492018-02-17 14:58:40 +0100264 &dev_attr_range.attr,
Dmitry Torokhov3ee2c402015-07-23 16:01:08 -0700265 NULL,
266};
David Brownell3925a5c2007-02-12 00:52:47 -0800267
268/* The reason to trigger an alarm with no process watching it (via sysfs)
269 * is its side effect: waking from a system state like suspend-to-RAM or
270 * suspend-to-disk. So: no attribute unless that side effect is possible.
271 * (Userspace may disable that mechanism later.)
272 */
Dmitry Torokhovdf100c02015-07-23 16:01:06 -0700273static bool rtc_does_wakealarm(struct rtc_device *rtc)
David Brownell3925a5c2007-02-12 00:52:47 -0800274{
David Brownellcd966202007-05-08 00:33:40 -0700275 if (!device_can_wakeup(rtc->dev.parent))
Dmitry Torokhovdf100c02015-07-23 16:01:06 -0700276 return false;
277
David Brownell3925a5c2007-02-12 00:52:47 -0800278 return rtc->ops->set_alarm != NULL;
279}
280
Dmitry Torokhov3ee2c402015-07-23 16:01:08 -0700281static umode_t rtc_attr_is_visible(struct kobject *kobj,
282 struct attribute *attr, int n)
Alessandro Zummoc5c3e192006-03-27 01:16:39 -0800283{
Dmitry Torokhov3ee2c402015-07-23 16:01:08 -0700284 struct device *dev = container_of(kobj, struct device, kobj);
285 struct rtc_device *rtc = to_rtc_device(dev);
286 umode_t mode = attr->mode;
Alessandro Zummoc5c3e192006-03-27 01:16:39 -0800287
Joshua Clayton5495a412016-02-05 12:41:12 -0800288 if (attr == &dev_attr_wakealarm.attr) {
Dmitry Torokhov3ee2c402015-07-23 16:01:08 -0700289 if (!rtc_does_wakealarm(rtc))
290 mode = 0;
Joshua Clayton5495a412016-02-05 12:41:12 -0800291 } else if (attr == &dev_attr_offset.attr) {
292 if (!rtc->ops->set_offset)
293 mode = 0;
Alexandre Belloni71db0492018-02-17 14:58:40 +0100294 } else if (attr == &dev_attr_range.attr) {
295 if (!(rtc->range_max - rtc->range_min))
296 mode = 0;
Joshua Clayton5495a412016-02-05 12:41:12 -0800297 }
Alessandro Zummoc5c3e192006-03-27 01:16:39 -0800298
Dmitry Torokhov3ee2c402015-07-23 16:01:08 -0700299 return mode;
Alessandro Zummoc5c3e192006-03-27 01:16:39 -0800300}
301
Dmitry Torokhov3ee2c402015-07-23 16:01:08 -0700302static struct attribute_group rtc_attr_group = {
303 .is_visible = rtc_attr_is_visible,
304 .attrs = rtc_attrs,
305};
Alessandro Zummoc5c3e192006-03-27 01:16:39 -0800306
Dmitry Torokhov3ee2c402015-07-23 16:01:08 -0700307static const struct attribute_group *rtc_attr_groups[] = {
308 &rtc_attr_group,
309 NULL
310};
311
312const struct attribute_group **rtc_get_dev_attribute_groups(void)
Alessandro Zummoc5c3e192006-03-27 01:16:39 -0800313{
Dmitry Torokhov3ee2c402015-07-23 16:01:08 -0700314 return rtc_attr_groups;
Alessandro Zummoc5c3e192006-03-27 01:16:39 -0800315}
Denis Osterlanda0a1a1b2018-07-24 11:31:22 +0000316
317int rtc_add_groups(struct rtc_device *rtc, const struct attribute_group **grps)
318{
319 size_t old_cnt = 0, add_cnt = 0, new_cnt;
320 const struct attribute_group **groups, **old;
321
322 if (rtc->registered)
323 return -EINVAL;
324 if (!grps)
325 return -EINVAL;
326
327 groups = rtc->dev.groups;
328 if (groups)
329 for (; *groups; groups++)
330 old_cnt++;
331
332 for (groups = grps; *groups; groups++)
333 add_cnt++;
334
335 new_cnt = old_cnt + add_cnt + 1;
336 groups = devm_kcalloc(&rtc->dev, new_cnt, sizeof(*groups), GFP_KERNEL);
Dan Carpenter777d8ae2018-08-27 12:22:34 +0300337 if (!groups)
338 return -ENOMEM;
Denis Osterlanda0a1a1b2018-07-24 11:31:22 +0000339 memcpy(groups, rtc->dev.groups, old_cnt * sizeof(*groups));
340 memcpy(groups + old_cnt, grps, add_cnt * sizeof(*groups));
341 groups[old_cnt + add_cnt] = NULL;
342
343 old = rtc->dev.groups;
344 rtc->dev.groups = groups;
345 if (old && old != rtc_attr_groups)
346 devm_kfree(&rtc->dev, old);
347
348 return 0;
349}
350EXPORT_SYMBOL(rtc_add_groups);
351
352int rtc_add_group(struct rtc_device *rtc, const struct attribute_group *grp)
353{
354 const struct attribute_group *groups[] = { grp, NULL };
355
356 return rtc_add_groups(rtc, groups);
357}
358EXPORT_SYMBOL(rtc_add_group);