From 7f3a1fb998e11a45d14556cc17f72d4f8aa89732 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Tue, 18 Aug 2009 18:06:30 +0100 Subject: [PATCH] Staging: IIO: Periodic timer based trigger The original posting of this driver led to a discussion in which it was commented that a better system was needed for dealing with the many possible periodic interrupt sources available on some SoCs. Unfortunately that is a big task and as far as I know, no-one has taken it on as yet. So in the meantime this driver is still in here. Signed-off-by: Jonathan Cameron Signed-off-by: Greg Kroah-Hartman --- drivers/staging/iio/Kconfig | 3 + drivers/staging/iio/Makefile | 4 +- drivers/staging/iio/trigger/Kconfig | 15 ++ drivers/staging/iio/trigger/Makefile | 5 + .../iio/trigger/iio-trig-periodic-rtc.c | 228 ++++++++++++++++++ 5 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/iio/trigger/Kconfig create mode 100644 drivers/staging/iio/trigger/Makefile create mode 100644 drivers/staging/iio/trigger/iio-trig-periodic-rtc.c diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index 869433f541cf..beb99a547f09 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -37,8 +37,11 @@ config IIO_TRIGGER ring buffers. The triggers are effectively a 'capture data now' interrupt. + source "drivers/staging/iio/accel/Kconfig" source "drivers/staging/iio/adc/Kconfig" source "drivers/staging/iio/light/Kconfig" +source "drivers/staging/iio/trigger/Kconfig" + endif # IIO diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index dea4536ff827..7ec021810a77 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -11,4 +11,6 @@ obj-$(CONFIG_IIO_SW_RING) += ring_sw.o obj-y += accel/ obj-y += adc/ -obj-y += light/ \ No newline at end of file +obj-y += light/ + +obj-y += trigger/ \ No newline at end of file diff --git a/drivers/staging/iio/trigger/Kconfig b/drivers/staging/iio/trigger/Kconfig new file mode 100644 index 000000000000..05ed139e1535 --- /dev/null +++ b/drivers/staging/iio/trigger/Kconfig @@ -0,0 +1,15 @@ +# +# Industrial I/O standalone triggers +# +comment "Triggers - standalone" + +if IIO_TRIGGER + +config IIO_PERIODIC_RTC_TRIGGER + tristate "Periodic RTC triggers" + depends on RTC_CLASS + help + Provides support for using periodic capable real time + clocks as IIO triggers. + +endif # IIO_TRIGGER diff --git a/drivers/staging/iio/trigger/Makefile b/drivers/staging/iio/trigger/Makefile new file mode 100644 index 000000000000..4ae55b9abae0 --- /dev/null +++ b/drivers/staging/iio/trigger/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for triggers not associated with iio-devices +# +obj-$(CONFIG_IIO_PERIODIC_RTC_TRIGGER) += iio-trig-periodic-rtc.o + 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 000000000000..28c1d2d4a42b --- /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 +#include +#include +#include +#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); +} + +DEVICE_ATTR(name, S_IRUGO, + iio_trig_periodic_read_name, + NULL); +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 "); +MODULE_DESCRIPTION("Periodic realtime clock trigger for the iio subsystem"); +MODULE_LICENSE("GPL v2"); -- GitLab