Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 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> |
Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 17 | #include <linux/slab.h> |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 18 | #include <linux/rtc.h> |
Jonathan Cameron | 06458e2 | 2012-04-25 15:54:58 +0100 | [diff] [blame] | 19 | #include <linux/iio/iio.h> |
| 20 | #include <linux/iio/trigger.h> |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 21 | |
Greg Kroah-Hartman | d1d8abd | 2010-05-04 22:31:22 -0700 | [diff] [blame] | 22 | static LIST_HEAD(iio_prtc_trigger_list); |
| 23 | static DEFINE_MUTEX(iio_prtc_trigger_list_lock); |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 24 | |
| 25 | struct iio_prtc_trigger_info { |
| 26 | struct rtc_device *rtc; |
Rickard Strandqvist | 1a179a1 | 2015-02-08 19:22:54 +0100 | [diff] [blame] | 27 | unsigned int frequency; |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 28 | struct rtc_task task; |
Peter Meerwald | 9d2f79a | 2014-12-06 06:00:00 +0000 | [diff] [blame] | 29 | bool state; |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 30 | }; |
| 31 | |
| 32 | static int iio_trig_periodic_rtc_set_state(struct iio_trigger *trig, bool state) |
| 33 | { |
Lars-Peter Clausen | 1e9663c | 2013-03-25 08:58:00 +0000 | [diff] [blame] | 34 | struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig); |
Peter Meerwald | 9d2f79a | 2014-12-06 06:00:00 +0000 | [diff] [blame] | 35 | int ret; |
Catalina Mocanu | 714ab9b | 2014-09-19 14:32:09 -0700 | [diff] [blame] | 36 | |
Peter Meerwald | 9d2f79a | 2014-12-06 06:00:00 +0000 | [diff] [blame] | 37 | if (trig_info->frequency == 0 && state) |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 38 | return -EINVAL; |
Rickard Strandqvist | 1a179a1 | 2015-02-08 19:22:54 +0100 | [diff] [blame] | 39 | dev_dbg(&trig_info->rtc->dev, "trigger frequency is %u\n", |
Cristina Opriceana | 2f3403a | 2015-08-11 13:18:18 +0300 | [diff] [blame] | 40 | trig_info->frequency); |
Peter Meerwald | 9d2f79a | 2014-12-06 06:00:00 +0000 | [diff] [blame] | 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; |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 46 | } |
| 47 | |
| 48 | static ssize_t iio_trig_periodic_read_freq(struct device *dev, |
| 49 | struct device_attribute *attr, |
| 50 | char *buf) |
| 51 | { |
Lars-Peter Clausen | 4bf8172 | 2012-06-21 19:10:59 +0200 | [diff] [blame] | 52 | struct iio_trigger *trig = to_iio_trigger(dev); |
Lars-Peter Clausen | 1e9663c | 2013-03-25 08:58:00 +0000 | [diff] [blame] | 53 | struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig); |
Catalina Mocanu | 714ab9b | 2014-09-19 14:32:09 -0700 | [diff] [blame] | 54 | |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 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 | { |
Lars-Peter Clausen | 4bf8172 | 2012-06-21 19:10:59 +0200 | [diff] [blame] | 63 | struct iio_trigger *trig = to_iio_trigger(dev); |
Lars-Peter Clausen | 1e9663c | 2013-03-25 08:58:00 +0000 | [diff] [blame] | 64 | struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig); |
Rickard Strandqvist | 1a179a1 | 2015-02-08 19:22:54 +0100 | [diff] [blame] | 65 | unsigned int val; |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 66 | int ret; |
| 67 | |
Rickard Strandqvist | 1a179a1 | 2015-02-08 19:22:54 +0100 | [diff] [blame] | 68 | ret = kstrtouint(buf, 10, &val); |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 69 | if (ret) |
| 70 | goto error_ret; |
| 71 | |
Peter Meerwald | 9d2f79a | 2014-12-06 06:00:00 +0000 | [diff] [blame] | 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) |
Darshana Padmadas | a2bc425 | 2014-10-04 21:55:27 +0530 | [diff] [blame] | 75 | ret = rtc_irq_set_state(trig_info->rtc, |
| 76 | &trig_info->task, 1); |
Cristina Opriceana | c71e16e | 2015-08-11 13:20:53 +0300 | [diff] [blame] | 77 | } else { |
Rickard Strandqvist | 1a179a1 | 2015-02-08 19:22:54 +0100 | [diff] [blame] | 78 | ret = rtc_irq_set_state(trig_info->rtc, &trig_info->task, 0); |
Cristina Opriceana | c71e16e | 2015-08-11 13:20:53 +0300 | [diff] [blame] | 79 | } |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 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 | |
Randy Dunlap | 2fdec57 | 2009-08-21 13:59:08 -0700 | [diff] [blame] | 91 | static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR, |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 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, |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 97 | NULL, |
| 98 | }; |
Jonathan Cameron | 59c85e8 | 2011-05-18 14:42:22 +0100 | [diff] [blame] | 99 | |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 100 | static const struct attribute_group iio_trig_prtc_attr_group = { |
| 101 | .attrs = iio_trig_prtc_attrs, |
| 102 | }; |
| 103 | |
Jonathan Cameron | 59c85e8 | 2011-05-18 14:42:22 +0100 | [diff] [blame] | 104 | static const struct attribute_group *iio_trig_prtc_attr_groups[] = { |
| 105 | &iio_trig_prtc_attr_group, |
| 106 | NULL |
| 107 | }; |
| 108 | |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 109 | static void iio_prtc_trigger_poll(void *private_data) |
| 110 | { |
Peter Meerwald | 398fd22 | 2014-12-06 06:46:00 +0000 | [diff] [blame] | 111 | iio_trigger_poll(private_data); |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 112 | } |
| 113 | |
Jonathan Cameron | d29f73d | 2011-08-12 17:08:38 +0100 | [diff] [blame] | 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 | |
Bill Pemberton | 4ae1c61 | 2012-11-19 13:21:57 -0500 | [diff] [blame] | 119 | static int iio_trig_periodic_rtc_probe(struct platform_device *dev) |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 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++) { |
Cristina Opriceana | e84d072 | 2015-03-31 13:03:12 +0300 | [diff] [blame] | 128 | if (!pdata[i]) |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 129 | break; |
Lars-Peter Clausen | 7cbb753 | 2012-04-26 13:35:01 +0200 | [diff] [blame] | 130 | trig = iio_trigger_alloc("periodic%s", pdata[i]); |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 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 | } |
Lars-Peter Clausen | 1e9663c | 2013-03-25 08:58:00 +0000 | [diff] [blame] | 142 | iio_trigger_set_drvdata(trig, trig_info); |
Jonathan Cameron | d29f73d | 2011-08-12 17:08:38 +0100 | [diff] [blame] | 143 | trig->ops = &iio_prtc_trigger_ops; |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 144 | /* RTC access */ |
Peter Meerwald | 1c70f7b | 2014-12-06 06:00:00 +0000 | [diff] [blame] | 145 | trig_info->rtc = rtc_class_open(pdata[i]); |
Cristina Opriceana | e84d072 | 2015-03-31 13:03:12 +0300 | [diff] [blame] | 146 | if (!trig_info->rtc) { |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 147 | ret = -EINVAL; |
Jonathan Cameron | 59c85e8 | 2011-05-18 14:42:22 +0100 | [diff] [blame] | 148 | goto error_free_trig_info; |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 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; |
Jonathan Cameron | 59c85e8 | 2011-05-18 14:42:22 +0100 | [diff] [blame] | 155 | trig->dev.groups = iio_trig_prtc_attr_groups; |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 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); |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 165 | error_free_trig_info: |
| 166 | kfree(trig_info); |
| 167 | error_put_trigger_and_remove_from_list: |
| 168 | list_del(&trig->alloc_list); |
Lars-Peter Clausen | 7cbb753 | 2012-04-26 13:35:01 +0200 | [diff] [blame] | 169 | iio_trigger_put(trig); |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 170 | error_free_completed_registrations: |
| 171 | list_for_each_entry_safe(trig, |
| 172 | trig2, |
| 173 | &iio_prtc_trigger_list, |
| 174 | alloc_list) { |
Lars-Peter Clausen | 1e9663c | 2013-03-25 08:58:00 +0000 | [diff] [blame] | 175 | trig_info = iio_trigger_get_drvdata(trig); |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 176 | rtc_irq_unregister(trig_info->rtc, &trig_info->task); |
| 177 | rtc_class_close(trig_info->rtc); |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 178 | kfree(trig_info); |
| 179 | iio_trigger_unregister(trig); |
| 180 | } |
| 181 | return ret; |
| 182 | } |
| 183 | |
Bill Pemberton | 447d4f2 | 2012-11-19 13:26:37 -0500 | [diff] [blame] | 184 | static int iio_trig_periodic_rtc_remove(struct platform_device *dev) |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 185 | { |
| 186 | struct iio_trigger *trig, *trig2; |
| 187 | struct iio_prtc_trigger_info *trig_info; |
Catalina Mocanu | 714ab9b | 2014-09-19 14:32:09 -0700 | [diff] [blame] | 188 | |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 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) { |
Lars-Peter Clausen | 1e9663c | 2013-03-25 08:58:00 +0000 | [diff] [blame] | 194 | trig_info = iio_trigger_get_drvdata(trig); |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 195 | rtc_irq_unregister(trig_info->rtc, &trig_info->task); |
| 196 | rtc_class_close(trig_info->rtc); |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 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, |
Bill Pemberton | e543acf | 2012-11-19 13:21:38 -0500 | [diff] [blame] | 206 | .remove = iio_trig_periodic_rtc_remove, |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 207 | .driver = { |
| 208 | .name = "iio_prtc_trigger", |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 209 | }, |
| 210 | }; |
| 211 | |
Michael Hennerich | 5f95373 | 2012-03-01 10:51:05 +0100 | [diff] [blame] | 212 | module_platform_driver(iio_trig_periodic_rtc_driver); |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 213 | |
Jonathan Cameron | 0f8c962 | 2012-09-02 21:34:59 +0100 | [diff] [blame] | 214 | MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); |
Peter Meerwald | 1c70f7b | 2014-12-06 06:00:00 +0000 | [diff] [blame] | 215 | MODULE_DESCRIPTION("Periodic realtime clock trigger for the iio subsystem"); |
Jonathan Cameron | 7f3a1fb | 2009-08-18 18:06:30 +0100 | [diff] [blame] | 216 | MODULE_LICENSE("GPL v2"); |