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 JC |
18 | #include <linux/rtc.h> |
19 | #include "../iio.h" | |
20 | #include "../trigger.h" | |
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; | |
27 | int frequency; | |
7f3a1fb9 JC |
28 | struct rtc_task task; |
29 | }; | |
30 | ||
31 | static int iio_trig_periodic_rtc_set_state(struct iio_trigger *trig, bool state) | |
32 | { | |
33 | struct iio_prtc_trigger_info *trig_info = trig->private_data; | |
34 | if (trig_info->frequency == 0) | |
35 | return -EINVAL; | |
36 | printk(KERN_INFO "trigger frequency is %d\n", trig_info->frequency); | |
37 | return rtc_irq_set_state(trig_info->rtc, &trig_info->task, state); | |
38 | } | |
39 | ||
40 | static ssize_t iio_trig_periodic_read_freq(struct device *dev, | |
41 | struct device_attribute *attr, | |
42 | char *buf) | |
43 | { | |
44 | struct iio_trigger *trig = dev_get_drvdata(dev); | |
45 | struct iio_prtc_trigger_info *trig_info = trig->private_data; | |
46 | return sprintf(buf, "%u\n", trig_info->frequency); | |
47 | } | |
48 | ||
49 | static ssize_t iio_trig_periodic_write_freq(struct device *dev, | |
50 | struct device_attribute *attr, | |
51 | const char *buf, | |
52 | size_t len) | |
53 | { | |
54 | struct iio_trigger *trig = dev_get_drvdata(dev); | |
55 | struct iio_prtc_trigger_info *trig_info = trig->private_data; | |
56 | unsigned long val; | |
57 | int ret; | |
58 | ||
59 | ret = strict_strtoul(buf, 10, &val); | |
60 | if (ret) | |
61 | goto error_ret; | |
62 | ||
63 | ret = rtc_irq_set_freq(trig_info->rtc, &trig_info->task, val); | |
64 | if (ret) | |
65 | goto error_ret; | |
66 | ||
67 | trig_info->frequency = val; | |
68 | ||
69 | return len; | |
70 | ||
71 | error_ret: | |
72 | return ret; | |
73 | } | |
74 | ||
2fdec576 | 75 | static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR, |
7f3a1fb9 JC |
76 | iio_trig_periodic_read_freq, |
77 | iio_trig_periodic_write_freq); | |
78 | ||
79 | static struct attribute *iio_trig_prtc_attrs[] = { | |
80 | &dev_attr_frequency.attr, | |
7f3a1fb9 JC |
81 | NULL, |
82 | }; | |
59c85e82 | 83 | |
7f3a1fb9 JC |
84 | static const struct attribute_group iio_trig_prtc_attr_group = { |
85 | .attrs = iio_trig_prtc_attrs, | |
86 | }; | |
87 | ||
59c85e82 JC |
88 | static const struct attribute_group *iio_trig_prtc_attr_groups[] = { |
89 | &iio_trig_prtc_attr_group, | |
90 | NULL | |
91 | }; | |
92 | ||
7f3a1fb9 JC |
93 | static void iio_prtc_trigger_poll(void *private_data) |
94 | { | |
7b2c33b1 JC |
95 | /* Timestamp is not provided currently */ |
96 | iio_trigger_poll(private_data, 0); | |
7f3a1fb9 JC |
97 | } |
98 | ||
d29f73db JC |
99 | static const struct iio_trigger_ops iio_prtc_trigger_ops = { |
100 | .owner = THIS_MODULE, | |
101 | .set_trigger_state = &iio_trig_periodic_rtc_set_state, | |
102 | }; | |
103 | ||
7f3a1fb9 JC |
104 | static int iio_trig_periodic_rtc_probe(struct platform_device *dev) |
105 | { | |
106 | char **pdata = dev->dev.platform_data; | |
107 | struct iio_prtc_trigger_info *trig_info; | |
108 | struct iio_trigger *trig, *trig2; | |
109 | ||
110 | int i, ret; | |
111 | ||
112 | for (i = 0;; i++) { | |
113 | if (pdata[i] == NULL) | |
114 | break; | |
59c85e82 | 115 | trig = iio_allocate_trigger("periodic%s", pdata[i]); |
7f3a1fb9 JC |
116 | if (!trig) { |
117 | ret = -ENOMEM; | |
118 | goto error_free_completed_registrations; | |
119 | } | |
120 | list_add(&trig->alloc_list, &iio_prtc_trigger_list); | |
121 | ||
122 | trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); | |
123 | if (!trig_info) { | |
124 | ret = -ENOMEM; | |
125 | goto error_put_trigger_and_remove_from_list; | |
126 | } | |
127 | trig->private_data = trig_info; | |
d29f73db | 128 | trig->ops = &iio_prtc_trigger_ops; |
7f3a1fb9 JC |
129 | /* RTC access */ |
130 | trig_info->rtc | |
131 | = rtc_class_open(pdata[i]); | |
132 | if (trig_info->rtc == NULL) { | |
133 | ret = -EINVAL; | |
59c85e82 | 134 | goto error_free_trig_info; |
7f3a1fb9 JC |
135 | } |
136 | trig_info->task.func = iio_prtc_trigger_poll; | |
137 | trig_info->task.private_data = trig; | |
138 | ret = rtc_irq_register(trig_info->rtc, &trig_info->task); | |
139 | if (ret) | |
140 | goto error_close_rtc; | |
59c85e82 | 141 | trig->dev.groups = iio_trig_prtc_attr_groups; |
7f3a1fb9 JC |
142 | ret = iio_trigger_register(trig); |
143 | if (ret) | |
144 | goto error_unregister_rtc_irq; | |
145 | } | |
146 | return 0; | |
147 | error_unregister_rtc_irq: | |
148 | rtc_irq_unregister(trig_info->rtc, &trig_info->task); | |
149 | error_close_rtc: | |
150 | rtc_class_close(trig_info->rtc); | |
7f3a1fb9 JC |
151 | error_free_trig_info: |
152 | kfree(trig_info); | |
153 | error_put_trigger_and_remove_from_list: | |
154 | list_del(&trig->alloc_list); | |
155 | iio_put_trigger(trig); | |
156 | error_free_completed_registrations: | |
157 | list_for_each_entry_safe(trig, | |
158 | trig2, | |
159 | &iio_prtc_trigger_list, | |
160 | alloc_list) { | |
161 | trig_info = trig->private_data; | |
162 | rtc_irq_unregister(trig_info->rtc, &trig_info->task); | |
163 | rtc_class_close(trig_info->rtc); | |
7f3a1fb9 JC |
164 | kfree(trig_info); |
165 | iio_trigger_unregister(trig); | |
166 | } | |
167 | return ret; | |
168 | } | |
169 | ||
170 | static int iio_trig_periodic_rtc_remove(struct platform_device *dev) | |
171 | { | |
172 | struct iio_trigger *trig, *trig2; | |
173 | struct iio_prtc_trigger_info *trig_info; | |
174 | mutex_lock(&iio_prtc_trigger_list_lock); | |
175 | list_for_each_entry_safe(trig, | |
176 | trig2, | |
177 | &iio_prtc_trigger_list, | |
178 | alloc_list) { | |
179 | trig_info = trig->private_data; | |
180 | rtc_irq_unregister(trig_info->rtc, &trig_info->task); | |
181 | rtc_class_close(trig_info->rtc); | |
7f3a1fb9 JC |
182 | kfree(trig_info); |
183 | iio_trigger_unregister(trig); | |
184 | } | |
185 | mutex_unlock(&iio_prtc_trigger_list_lock); | |
186 | return 0; | |
187 | } | |
188 | ||
189 | static struct platform_driver iio_trig_periodic_rtc_driver = { | |
190 | .probe = iio_trig_periodic_rtc_probe, | |
191 | .remove = iio_trig_periodic_rtc_remove, | |
192 | .driver = { | |
193 | .name = "iio_prtc_trigger", | |
194 | .owner = THIS_MODULE, | |
195 | }, | |
196 | }; | |
197 | ||
198 | static int __init iio_trig_periodic_rtc_init(void) | |
199 | { | |
200 | return platform_driver_register(&iio_trig_periodic_rtc_driver); | |
201 | } | |
202 | ||
203 | static void __exit iio_trig_periodic_rtc_exit(void) | |
204 | { | |
205 | return platform_driver_unregister(&iio_trig_periodic_rtc_driver); | |
206 | } | |
207 | ||
208 | module_init(iio_trig_periodic_rtc_init); | |
209 | module_exit(iio_trig_periodic_rtc_exit); | |
210 | MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>"); | |
211 | MODULE_DESCRIPTION("Periodic realtime clock trigger for the iio subsystem"); | |
212 | MODULE_LICENSE("GPL v2"); |