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