1/* The industrial I/O periodic RTC trigger driver
2 *
3 * Copyright (c) 2008 Jonathan Cameron
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This is a heavily rewritten version of the periodic timer system in
10 * earlier version of industrialio.  It supplies the same functionality
11 * but via a trigger rather than a specific periodic timer system.
12 */
13
14#include <linux/platform_device.h>
15#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/slab.h>
18#include <linux/rtc.h>
19#include <linux/iio/iio.h>
20#include <linux/iio/trigger.h>
21
22static LIST_HEAD(iio_prtc_trigger_list);
23static DEFINE_MUTEX(iio_prtc_trigger_list_lock);
24
25struct iio_prtc_trigger_info {
26	struct rtc_device *rtc;
27	unsigned int frequency;
28	struct rtc_task task;
29	bool state;
30};
31
32static int iio_trig_periodic_rtc_set_state(struct iio_trigger *trig, bool state)
33{
34	struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig);
35	int ret;
36
37	if (trig_info->frequency == 0 && state)
38		return -EINVAL;
39	dev_dbg(&trig_info->rtc->dev, "trigger frequency is %u\n",
40		trig_info->frequency);
41	ret = rtc_irq_set_state(trig_info->rtc, &trig_info->task, state);
42	if (!ret)
43		trig_info->state = state;
44
45	return ret;
46}
47
48static ssize_t iio_trig_periodic_read_freq(struct device *dev,
49					   struct device_attribute *attr,
50					   char *buf)
51{
52	struct iio_trigger *trig = to_iio_trigger(dev);
53	struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig);
54
55	return sprintf(buf, "%u\n", trig_info->frequency);
56}
57
58static ssize_t iio_trig_periodic_write_freq(struct device *dev,
59					    struct device_attribute *attr,
60					    const char *buf,
61					    size_t len)
62{
63	struct iio_trigger *trig = to_iio_trigger(dev);
64	struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig);
65	unsigned int val;
66	int ret;
67
68	ret = kstrtouint(buf, 10, &val);
69	if (ret)
70		goto error_ret;
71
72	if (val > 0) {
73		ret = rtc_irq_set_freq(trig_info->rtc, &trig_info->task, val);
74		if (ret == 0 && trig_info->state && trig_info->frequency == 0)
75			ret = rtc_irq_set_state(trig_info->rtc,
76						&trig_info->task, 1);
77	} else {
78		ret = rtc_irq_set_state(trig_info->rtc, &trig_info->task, 0);
79	}
80	if (ret)
81		goto error_ret;
82
83	trig_info->frequency = val;
84
85	return len;
86
87error_ret:
88	return ret;
89}
90
91static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR,
92	    iio_trig_periodic_read_freq,
93	    iio_trig_periodic_write_freq);
94
95static struct attribute *iio_trig_prtc_attrs[] = {
96	&dev_attr_frequency.attr,
97	NULL,
98};
99
100static const struct attribute_group iio_trig_prtc_attr_group = {
101	.attrs = iio_trig_prtc_attrs,
102};
103
104static const struct attribute_group *iio_trig_prtc_attr_groups[] = {
105	&iio_trig_prtc_attr_group,
106	NULL
107};
108
109static void iio_prtc_trigger_poll(void *private_data)
110{
111	iio_trigger_poll(private_data);
112}
113
114static const struct iio_trigger_ops iio_prtc_trigger_ops = {
115	.owner = THIS_MODULE,
116	.set_trigger_state = &iio_trig_periodic_rtc_set_state,
117};
118
119static int iio_trig_periodic_rtc_probe(struct platform_device *dev)
120{
121	char **pdata = dev->dev.platform_data;
122	struct iio_prtc_trigger_info *trig_info;
123	struct iio_trigger *trig, *trig2;
124
125	int i, ret;
126
127	for (i = 0;; i++) {
128		if (!pdata[i])
129			break;
130		trig = iio_trigger_alloc("periodic%s", pdata[i]);
131		if (!trig) {
132			ret = -ENOMEM;
133			goto error_free_completed_registrations;
134		}
135		list_add(&trig->alloc_list, &iio_prtc_trigger_list);
136
137		trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL);
138		if (!trig_info) {
139			ret = -ENOMEM;
140			goto error_put_trigger_and_remove_from_list;
141		}
142		iio_trigger_set_drvdata(trig, trig_info);
143		trig->ops = &iio_prtc_trigger_ops;
144		/* RTC access */
145		trig_info->rtc = rtc_class_open(pdata[i]);
146		if (!trig_info->rtc) {
147			ret = -EINVAL;
148			goto error_free_trig_info;
149		}
150		trig_info->task.func = iio_prtc_trigger_poll;
151		trig_info->task.private_data = trig;
152		ret = rtc_irq_register(trig_info->rtc, &trig_info->task);
153		if (ret)
154			goto error_close_rtc;
155		trig->dev.groups = iio_trig_prtc_attr_groups;
156		ret = iio_trigger_register(trig);
157		if (ret)
158			goto error_unregister_rtc_irq;
159	}
160	return 0;
161error_unregister_rtc_irq:
162	rtc_irq_unregister(trig_info->rtc, &trig_info->task);
163error_close_rtc:
164	rtc_class_close(trig_info->rtc);
165error_free_trig_info:
166	kfree(trig_info);
167error_put_trigger_and_remove_from_list:
168	list_del(&trig->alloc_list);
169	iio_trigger_put(trig);
170error_free_completed_registrations:
171	list_for_each_entry_safe(trig,
172				 trig2,
173				 &iio_prtc_trigger_list,
174				 alloc_list) {
175		trig_info = iio_trigger_get_drvdata(trig);
176		rtc_irq_unregister(trig_info->rtc, &trig_info->task);
177		rtc_class_close(trig_info->rtc);
178		kfree(trig_info);
179		iio_trigger_unregister(trig);
180	}
181	return ret;
182}
183
184static int iio_trig_periodic_rtc_remove(struct platform_device *dev)
185{
186	struct iio_trigger *trig, *trig2;
187	struct iio_prtc_trigger_info *trig_info;
188
189	mutex_lock(&iio_prtc_trigger_list_lock);
190	list_for_each_entry_safe(trig,
191				 trig2,
192				 &iio_prtc_trigger_list,
193				 alloc_list) {
194		trig_info = iio_trigger_get_drvdata(trig);
195		rtc_irq_unregister(trig_info->rtc, &trig_info->task);
196		rtc_class_close(trig_info->rtc);
197		kfree(trig_info);
198		iio_trigger_unregister(trig);
199	}
200	mutex_unlock(&iio_prtc_trigger_list_lock);
201	return 0;
202}
203
204static struct platform_driver iio_trig_periodic_rtc_driver = {
205	.probe = iio_trig_periodic_rtc_probe,
206	.remove = iio_trig_periodic_rtc_remove,
207	.driver = {
208		.name = "iio_prtc_trigger",
209	},
210};
211
212module_platform_driver(iio_trig_periodic_rtc_driver);
213
214MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
215MODULE_DESCRIPTION("Periodic realtime clock trigger for the iio subsystem");
216MODULE_LICENSE("GPL v2");
217