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