Commit | Line | Data |
---|---|---|
7f3a1fb9 JC |
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> | |
5a0e3ad6 | 17 | #include <linux/slab.h> |
7f3a1fb9 | 18 | #include <linux/rtc.h> |
06458e27 JC |
19 | #include <linux/iio/iio.h> |
20 | #include <linux/iio/trigger.h> | |
7f3a1fb9 | 21 | |
d1d8abdb GKH |
22 | static LIST_HEAD(iio_prtc_trigger_list); |
23 | static DEFINE_MUTEX(iio_prtc_trigger_list_lock); | |
7f3a1fb9 JC |
24 | |
25 | struct iio_prtc_trigger_info { | |
26 | struct rtc_device *rtc; | |
1a179a14 | 27 | unsigned int frequency; |
7f3a1fb9 | 28 | struct rtc_task task; |
9d2f79ab | 29 | bool state; |
7f3a1fb9 JC |
30 | }; |
31 | ||
32 | static int iio_trig_periodic_rtc_set_state(struct iio_trigger *trig, bool state) | |
33 | { | |
1e9663c6 | 34 | struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig); |
9d2f79ab | 35 | int ret; |
714ab9bd | 36 | |
9d2f79ab | 37 | if (trig_info->frequency == 0 && state) |
7f3a1fb9 | 38 | return -EINVAL; |
1a179a14 | 39 | dev_dbg(&trig_info->rtc->dev, "trigger frequency is %u\n", |
8463f6fb | 40 | trig_info->frequency); |
9d2f79ab PM |
41 | ret = rtc_irq_set_state(trig_info->rtc, &trig_info->task, state); |
42 | if (ret == 0) | |
43 | trig_info->state = state; | |
44 | ||
45 | return ret; | |
7f3a1fb9 JC |
46 | } |
47 | ||
48 | static ssize_t iio_trig_periodic_read_freq(struct device *dev, | |
49 | struct device_attribute *attr, | |
50 | char *buf) | |
51 | { | |
4bf81727 | 52 | struct iio_trigger *trig = to_iio_trigger(dev); |
1e9663c6 | 53 | struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig); |
714ab9bd | 54 | |
7f3a1fb9 JC |
55 | return sprintf(buf, "%u\n", trig_info->frequency); |
56 | } | |
57 | ||
58 | static ssize_t iio_trig_periodic_write_freq(struct device *dev, | |
59 | struct device_attribute *attr, | |
60 | const char *buf, | |
61 | size_t len) | |
62 | { | |
4bf81727 | 63 | struct iio_trigger *trig = to_iio_trigger(dev); |
1e9663c6 | 64 | struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig); |
1a179a14 | 65 | unsigned int val; |
7f3a1fb9 JC |
66 | int ret; |
67 | ||
1a179a14 | 68 | ret = kstrtouint(buf, 10, &val); |
7f3a1fb9 JC |
69 | if (ret) |
70 | goto error_ret; | |
71 | ||
9d2f79ab PM |
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) | |
a2bc425b DP |
75 | ret = rtc_irq_set_state(trig_info->rtc, |
76 | &trig_info->task, 1); | |
a6d748e3 | 77 | } else { |
1a179a14 | 78 | ret = rtc_irq_set_state(trig_info->rtc, &trig_info->task, 0); |
a6d748e3 | 79 | } |
7f3a1fb9 JC |
80 | if (ret) |
81 | goto error_ret; | |
82 | ||
83 | trig_info->frequency = val; | |
84 | ||
85 | return len; | |
86 | ||
87 | error_ret: | |
88 | return ret; | |
89 | } | |
90 | ||
2fdec576 | 91 | static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR, |
7f3a1fb9 JC |
92 | iio_trig_periodic_read_freq, |
93 | iio_trig_periodic_write_freq); | |
94 | ||
95 | static struct attribute *iio_trig_prtc_attrs[] = { | |
96 | &dev_attr_frequency.attr, | |
7f3a1fb9 JC |
97 | NULL, |
98 | }; | |
59c85e82 | 99 | |
7f3a1fb9 JC |
100 | static const struct attribute_group iio_trig_prtc_attr_group = { |
101 | .attrs = iio_trig_prtc_attrs, | |
102 | }; | |
103 | ||
59c85e82 JC |
104 | static const struct attribute_group *iio_trig_prtc_attr_groups[] = { |
105 | &iio_trig_prtc_attr_group, | |
106 | NULL | |
107 | }; | |
108 | ||
7f3a1fb9 JC |
109 | static void iio_prtc_trigger_poll(void *private_data) |
110 | { | |
398fd22b | 111 | iio_trigger_poll(private_data); |
7f3a1fb9 JC |
112 | } |
113 | ||
d29f73db JC |
114 | static 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 | ||
4ae1c61f | 119 | static int iio_trig_periodic_rtc_probe(struct platform_device *dev) |
7f3a1fb9 JC |
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++) { | |
e84d0724 | 128 | if (!pdata[i]) |
7f3a1fb9 | 129 | break; |
7cbb7537 | 130 | trig = iio_trigger_alloc("periodic%s", pdata[i]); |
7f3a1fb9 JC |
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 | } | |
1e9663c6 | 142 | iio_trigger_set_drvdata(trig, trig_info); |
d29f73db | 143 | trig->ops = &iio_prtc_trigger_ops; |
7f3a1fb9 | 144 | /* RTC access */ |
1c70f7be | 145 | trig_info->rtc = rtc_class_open(pdata[i]); |
e84d0724 | 146 | if (!trig_info->rtc) { |
7f3a1fb9 | 147 | ret = -EINVAL; |
59c85e82 | 148 | goto error_free_trig_info; |
7f3a1fb9 JC |
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; | |
59c85e82 | 155 | trig->dev.groups = iio_trig_prtc_attr_groups; |
7f3a1fb9 JC |
156 | ret = iio_trigger_register(trig); |
157 | if (ret) | |
158 | goto error_unregister_rtc_irq; | |
159 | } | |
160 | return 0; | |
161 | error_unregister_rtc_irq: | |
162 | rtc_irq_unregister(trig_info->rtc, &trig_info->task); | |
163 | error_close_rtc: | |
164 | rtc_class_close(trig_info->rtc); | |
7f3a1fb9 JC |
165 | error_free_trig_info: |
166 | kfree(trig_info); | |
167 | error_put_trigger_and_remove_from_list: | |
168 | list_del(&trig->alloc_list); | |
7cbb7537 | 169 | iio_trigger_put(trig); |
7f3a1fb9 JC |
170 | error_free_completed_registrations: |
171 | list_for_each_entry_safe(trig, | |
172 | trig2, | |
173 | &iio_prtc_trigger_list, | |
174 | alloc_list) { | |
1e9663c6 | 175 | trig_info = iio_trigger_get_drvdata(trig); |
7f3a1fb9 JC |
176 | rtc_irq_unregister(trig_info->rtc, &trig_info->task); |
177 | rtc_class_close(trig_info->rtc); | |
7f3a1fb9 JC |
178 | kfree(trig_info); |
179 | iio_trigger_unregister(trig); | |
180 | } | |
181 | return ret; | |
182 | } | |
183 | ||
447d4f29 | 184 | static int iio_trig_periodic_rtc_remove(struct platform_device *dev) |
7f3a1fb9 JC |
185 | { |
186 | struct iio_trigger *trig, *trig2; | |
187 | struct iio_prtc_trigger_info *trig_info; | |
714ab9bd | 188 | |
7f3a1fb9 JC |
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) { | |
1e9663c6 | 194 | trig_info = iio_trigger_get_drvdata(trig); |
7f3a1fb9 JC |
195 | rtc_irq_unregister(trig_info->rtc, &trig_info->task); |
196 | rtc_class_close(trig_info->rtc); | |
7f3a1fb9 JC |
197 | kfree(trig_info); |
198 | iio_trigger_unregister(trig); | |
199 | } | |
200 | mutex_unlock(&iio_prtc_trigger_list_lock); | |
201 | return 0; | |
202 | } | |
203 | ||
204 | static struct platform_driver iio_trig_periodic_rtc_driver = { | |
205 | .probe = iio_trig_periodic_rtc_probe, | |
e543acf0 | 206 | .remove = iio_trig_periodic_rtc_remove, |
7f3a1fb9 JC |
207 | .driver = { |
208 | .name = "iio_prtc_trigger", | |
7f3a1fb9 JC |
209 | }, |
210 | }; | |
211 | ||
5f953732 | 212 | module_platform_driver(iio_trig_periodic_rtc_driver); |
7f3a1fb9 | 213 | |
0f8c9620 | 214 | MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); |
1c70f7be | 215 | MODULE_DESCRIPTION("Periodic realtime clock trigger for the iio subsystem"); |
7f3a1fb9 | 216 | MODULE_LICENSE("GPL v2"); |