diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-09-22 19:54:53 (GMT) |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-09-22 20:01:40 (GMT) |
commit | ae19ffbadc1b2100285a5b5b3d0a4e0a11390904 (patch) | |
tree | 3c2086ab67398a019089a47ca3f362a4bc6db74f /drivers/staging/iio/trigger/iio-trig-periodic-rtc.c | |
parent | 34e84f39a27d059a3e6ec6e8b94aafa702e6f220 (diff) | |
parent | 9173a8ef24a6b1b8031507b35b8ffe5f85a87692 (diff) | |
download | linux-fsl-qoriq-ae19ffbadc1b2100285a5b5b3d0a4e0a11390904.tar.xz |
Merge branch 'master' into for-linus
Diffstat (limited to 'drivers/staging/iio/trigger/iio-trig-periodic-rtc.c')
-rw-r--r-- | drivers/staging/iio/trigger/iio-trig-periodic-rtc.c | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c new file mode 100644 index 0000000..e310dc00 --- /dev/null +++ b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c @@ -0,0 +1,228 @@ +/* The industrial I/O periodic RTC trigger driver + * + * Copyright (c) 2008 Jonathan Cameron + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This is a heavily rewritten version of the periodic timer system in + * earlier version of industrialio. It supplies the same functionality + * but via a trigger rather than a specific periodic timer system. + */ + +#include <linux/platform_device.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/rtc.h> +#include "../iio.h" +#include "../trigger.h" + +LIST_HEAD(iio_prtc_trigger_list); +DEFINE_MUTEX(iio_prtc_trigger_list_lock); + +struct iio_prtc_trigger_info { + struct rtc_device *rtc; + int frequency; + char *name; + struct rtc_task task; +}; + +static int iio_trig_periodic_rtc_set_state(struct iio_trigger *trig, bool state) +{ + struct iio_prtc_trigger_info *trig_info = trig->private_data; + if (trig_info->frequency == 0) + return -EINVAL; + printk(KERN_INFO "trigger frequency is %d\n", trig_info->frequency); + return rtc_irq_set_state(trig_info->rtc, &trig_info->task, state); +} + +static ssize_t iio_trig_periodic_read_freq(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_trigger *trig = dev_get_drvdata(dev); + struct iio_prtc_trigger_info *trig_info = trig->private_data; + return sprintf(buf, "%u\n", trig_info->frequency); +} + +static ssize_t iio_trig_periodic_write_freq(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_trigger *trig = dev_get_drvdata(dev); + struct iio_prtc_trigger_info *trig_info = trig->private_data; + unsigned long val; + int ret; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + goto error_ret; + + ret = rtc_irq_set_freq(trig_info->rtc, &trig_info->task, val); + if (ret) + goto error_ret; + + trig_info->frequency = val; + + return len; + +error_ret: + return ret; +} + +static ssize_t iio_trig_periodic_read_name(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_trigger *trig = dev_get_drvdata(dev); + struct iio_prtc_trigger_info *trig_info = trig->private_data; + return sprintf(buf, "%s\n", trig_info->name); +} + +static DEVICE_ATTR(name, S_IRUGO, + iio_trig_periodic_read_name, + NULL); +static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR, + iio_trig_periodic_read_freq, + iio_trig_periodic_write_freq); + +static struct attribute *iio_trig_prtc_attrs[] = { + &dev_attr_frequency.attr, + &dev_attr_name.attr, + NULL, +}; +static const struct attribute_group iio_trig_prtc_attr_group = { + .attrs = iio_trig_prtc_attrs, +}; + +static void iio_prtc_trigger_poll(void *private_data) +{ + iio_trigger_poll(private_data); +} + +static int iio_trig_periodic_rtc_probe(struct platform_device *dev) +{ + char **pdata = dev->dev.platform_data; + struct iio_prtc_trigger_info *trig_info; + struct iio_trigger *trig, *trig2; + + int i, ret; + + for (i = 0;; i++) { + if (pdata[i] == NULL) + break; + trig = iio_allocate_trigger(); + if (!trig) { + ret = -ENOMEM; + goto error_free_completed_registrations; + } + list_add(&trig->alloc_list, &iio_prtc_trigger_list); + + trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); + if (!trig_info) { + ret = -ENOMEM; + goto error_put_trigger_and_remove_from_list; + } + trig->private_data = trig_info; + trig->owner = THIS_MODULE; + trig->set_trigger_state = &iio_trig_periodic_rtc_set_state; + trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL); + if (trig->name == NULL) { + ret = -ENOMEM; + goto error_free_trig_info; + } + snprintf((char *)trig->name, + IIO_TRIGGER_NAME_LENGTH, + "periodic%s", + pdata[i]); + trig_info->name = (char *)trig->name; + /* RTC access */ + trig_info->rtc + = rtc_class_open(pdata[i]); + if (trig_info->rtc == NULL) { + ret = -EINVAL; + goto error_free_name; + } + trig_info->task.func = iio_prtc_trigger_poll; + trig_info->task.private_data = trig; + ret = rtc_irq_register(trig_info->rtc, &trig_info->task); + if (ret) + goto error_close_rtc; + trig->control_attrs = &iio_trig_prtc_attr_group; + ret = iio_trigger_register(trig); + if (ret) + goto error_unregister_rtc_irq; + } + return 0; +error_unregister_rtc_irq: + rtc_irq_unregister(trig_info->rtc, &trig_info->task); +error_close_rtc: + rtc_class_close(trig_info->rtc); +error_free_name: + kfree(trig->name); +error_free_trig_info: + kfree(trig_info); +error_put_trigger_and_remove_from_list: + list_del(&trig->alloc_list); + iio_put_trigger(trig); +error_free_completed_registrations: + list_for_each_entry_safe(trig, + trig2, + &iio_prtc_trigger_list, + alloc_list) { + trig_info = trig->private_data; + rtc_irq_unregister(trig_info->rtc, &trig_info->task); + rtc_class_close(trig_info->rtc); + kfree(trig->name); + kfree(trig_info); + iio_trigger_unregister(trig); + } + return ret; +} + +static int iio_trig_periodic_rtc_remove(struct platform_device *dev) +{ + struct iio_trigger *trig, *trig2; + struct iio_prtc_trigger_info *trig_info; + mutex_lock(&iio_prtc_trigger_list_lock); + list_for_each_entry_safe(trig, + trig2, + &iio_prtc_trigger_list, + alloc_list) { + trig_info = trig->private_data; + rtc_irq_unregister(trig_info->rtc, &trig_info->task); + rtc_class_close(trig_info->rtc); + kfree(trig->name); + kfree(trig_info); + iio_trigger_unregister(trig); + } + mutex_unlock(&iio_prtc_trigger_list_lock); + return 0; +} + +static struct platform_driver iio_trig_periodic_rtc_driver = { + .probe = iio_trig_periodic_rtc_probe, + .remove = iio_trig_periodic_rtc_remove, + .driver = { + .name = "iio_prtc_trigger", + .owner = THIS_MODULE, + }, +}; + +static int __init iio_trig_periodic_rtc_init(void) +{ + return platform_driver_register(&iio_trig_periodic_rtc_driver); +} + +static void __exit iio_trig_periodic_rtc_exit(void) +{ + return platform_driver_unregister(&iio_trig_periodic_rtc_driver); +} + +module_init(iio_trig_periodic_rtc_init); +module_exit(iio_trig_periodic_rtc_exit); +MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>"); +MODULE_DESCRIPTION("Periodic realtime clock trigger for the iio subsystem"); +MODULE_LICENSE("GPL v2"); |