This patch adds high resolution timer trigger support for iio framework. Signed-off-by: Denis Ciocca <denis.ciocca@xxxxxx> --- drivers/iio/trigger/Kconfig | 9 + drivers/iio/trigger/Makefile | 1 + drivers/iio/trigger/iio-trig-hrtimer.c | 292 ++++++++++++++++++++++++++++++++ 3 files changed, 302 insertions(+) create mode 100644 drivers/iio/trigger/iio-trig-hrtimer.c diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig index 7999612..f0da684 100644 --- a/drivers/iio/trigger/Kconfig +++ b/drivers/iio/trigger/Kconfig @@ -5,6 +5,15 @@ menu "Triggers - standalone" +config IIO_HRTIMER_TRIGGER + tristate "HRTIMER trigger" + help + Provides support for using HRTIMER entries as IIO triggers. + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called iio-trig-hrtimer. + config IIO_INTERRUPT_TRIGGER tristate "Generic interrupt trigger" help diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile index 0694dae..424032c 100644 --- a/drivers/iio/trigger/Makefile +++ b/drivers/iio/trigger/Makefile @@ -3,5 +3,6 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o diff --git a/drivers/iio/trigger/iio-trig-hrtimer.c b/drivers/iio/trigger/iio-trig-hrtimer.c new file mode 100644 index 0000000..245c51d --- /dev/null +++ b/drivers/iio/trigger/iio-trig-hrtimer.c @@ -0,0 +1,292 @@ +/* + * Industrial I/O - hrtimer trigger support + * + * Copyright 2013 STMicroelectronics Inc. + * Denis Ciocca <denis.ciocca@xxxxxx> + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/hrtimer.h> +#include <linux/ktime.h> +#include <linux/slab.h> +#include <linux/list.h> + +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> + +struct iio_hrtimer_trigger_data { + struct iio_trigger *trig; + struct hrtimer timer; + struct list_head l; + ktime_t period; + u16 freq; + int id; +}; + +static LIST_HEAD(iio_hrtimer_trigger_list); +static DEFINE_MUTEX(iio_hrtimer_trigger_list_mut); + +static int iio_hrtimer_trigger_probe(int id); +static int iio_hrtimer_trigger_remove(int id); + +static ssize_t iio_sysfs_hrtimer_trig_add(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret; + unsigned int input; + + ret = kstrtouint(buf, 10, &input); + if (ret) + return ret; + + ret = iio_hrtimer_trigger_probe(input); + if (ret) + return ret; + + return len; +} +static DEVICE_ATTR(add_trigger, S_IWUSR, NULL, &iio_sysfs_hrtimer_trig_add); + +static ssize_t iio_sysfs_hrtimer_trig_remove(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret; + unsigned int input; + + ret = kstrtouint(buf, 10, &input); + if (ret) + return ret; + + ret = iio_hrtimer_trigger_remove(input); + if (ret) + return ret; + + return len; +} +static DEVICE_ATTR(remove_trigger, S_IWUSR, + NULL, &iio_sysfs_hrtimer_trig_remove); + +static struct attribute *iio_hrtimer_trig_attrs[] = { + &dev_attr_add_trigger.attr, + &dev_attr_remove_trigger.attr, + NULL, +}; + +static const struct attribute_group iio_hrtimer_trig_group = { + .attrs = iio_hrtimer_trig_attrs, +}; + +static const struct attribute_group *iio_hrtimer_trig_groups[] = { + &iio_hrtimer_trig_group, + NULL, +}; + +static struct device iio_hrtimer_trig_dev = { + .bus = &iio_bus_type, + .groups = iio_hrtimer_trig_groups, +}; + +static int iio_hrtimer_trig_set_state(struct iio_trigger *trig, bool state) +{ + struct iio_hrtimer_trigger_data *trig_data = + iio_trigger_get_drvdata(trig); + + if (trig_data->freq == 0) + return -EINVAL; + + if (state) + hrtimer_start(&trig_data->timer, + trig_data->period, HRTIMER_MODE_REL); + else + hrtimer_cancel(&trig_data->timer); + + return 0; +} + +static ssize_t iio_hrtimer_trigger_set_freq_value(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret; + u16 frequency; + struct iio_trigger *trig = to_iio_trigger(dev); + struct iio_hrtimer_trigger_data *trig_data = + iio_trigger_get_drvdata(trig); + + ret = kstrtou16(buf, 10, &frequency); + if (ret < 0) + return ret; + + trig_data->freq = frequency; + + if (frequency) + trig_data->period = + ktime_set(0, NSEC_PER_SEC / trig_data->freq); + + return len; +} + +static ssize_t iio_hrtimer_trigger_get_freq_value(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_trigger *trig = to_iio_trigger(dev); + struct iio_hrtimer_trigger_data *trig_data = + iio_trigger_get_drvdata(trig); + + return sprintf(buf, "%hu\n", trig_data->freq); +} + +static DEVICE_ATTR(frequency, S_IWUSR | S_IRUGO, + iio_hrtimer_trigger_get_freq_value, + iio_hrtimer_trigger_set_freq_value); + +static ssize_t iio_hrtimer_trigger_get_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_trigger *trig = to_iio_trigger(dev); + struct iio_hrtimer_trigger_data *trig_data = + iio_trigger_get_drvdata(trig); + + return sprintf(buf, "%d\n", trig_data->id); +} + +static DEVICE_ATTR(id, S_IRUGO, iio_hrtimer_trigger_get_id, NULL); + +static struct attribute *iio_hrtimer_trigger_attrs[] = { + &dev_attr_frequency.attr, + &dev_attr_id.attr, + NULL, +}; + +static const struct attribute_group iio_hrtimer_trigger_attr_group = { + .attrs = iio_hrtimer_trigger_attrs, +}; + +static const struct attribute_group *iio_hrtimer_trigger_attr_groups[] = { + &iio_hrtimer_trigger_attr_group, + NULL, +}; + +static const struct iio_trigger_ops iio_hrtimer_trigger_ops = { + .owner = THIS_MODULE, + .set_trigger_state = &iio_hrtimer_trig_set_state, +}; + +enum hrtimer_restart iio_hrtimer_trigger_func(struct hrtimer *timer) +{ + struct iio_hrtimer_trigger_data *trig_data; + + trig_data = container_of(timer, struct iio_hrtimer_trigger_data, timer); + + hrtimer_forward_now(timer, trig_data->period); + iio_trigger_poll(trig_data->trig, iio_get_time_ns()); + + return HRTIMER_RESTART; +} + +static int iio_hrtimer_trigger_probe(int id) +{ + int err; + bool foundit = false; + struct iio_hrtimer_trigger_data *trig_data; + + mutex_lock(&iio_hrtimer_trigger_list_mut); + list_for_each_entry(trig_data, &iio_hrtimer_trigger_list, l) { + if (id == trig_data->id) { + foundit = true; + break; + } + } + if (foundit) { + err = -EINVAL; + goto iio_hrtimer_mutex_unlock; + } + + trig_data = kmalloc(sizeof(*trig_data), GFP_KERNEL); + if (trig_data == NULL) { + err = -ENOMEM; + goto iio_hrtimer_mutex_unlock; + } + + trig_data->id = id; + trig_data->trig = iio_trigger_alloc("hrtimer_trig%d", id); + if (!trig_data->trig) { + err = -ENOMEM; + goto iio_hrtimer_free_trig_data; + } + + trig_data->trig->dev.groups = iio_hrtimer_trigger_attr_groups; + trig_data->trig->ops = &iio_hrtimer_trigger_ops; + trig_data->trig->dev.parent = &iio_hrtimer_trig_dev; + iio_trigger_set_drvdata(trig_data->trig, trig_data); + + trig_data->freq = 0; + hrtimer_init(&trig_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + trig_data->timer.function = &iio_hrtimer_trigger_func; + + err = iio_trigger_register(trig_data->trig); + if (err) + goto iio_hrtimer_free_trig_data; + + list_add(&trig_data->l, &iio_hrtimer_trigger_list); + __module_get(THIS_MODULE); + mutex_unlock(&iio_hrtimer_trigger_list_mut); + + return 0; + +iio_hrtimer_free_trig_data: + kfree(trig_data); +iio_hrtimer_mutex_unlock: + mutex_unlock(&iio_hrtimer_trigger_list_mut); + return err; +} + +static int iio_hrtimer_trigger_remove(int id) +{ + bool foundit = false; + struct iio_hrtimer_trigger_data *trig_data; + + mutex_lock(&iio_hrtimer_trigger_list_mut); + list_for_each_entry(trig_data, &iio_hrtimer_trigger_list, l) { + if (id == trig_data->id) { + foundit = true; + break; + } + } + if (!foundit) { + mutex_unlock(&iio_hrtimer_trigger_list_mut); + return -EINVAL; + } + + iio_trigger_unregister(trig_data->trig); + iio_trigger_free(trig_data->trig); + + list_del(&trig_data->l); + kfree(trig_data); + module_put(THIS_MODULE); + mutex_unlock(&iio_hrtimer_trigger_list_mut); + + return 0; +} + +static int __init iio_hrtimer_trig_init(void) +{ + device_initialize(&iio_hrtimer_trig_dev); + dev_set_name(&iio_hrtimer_trig_dev, "iio_hrtimer_trigger"); + return device_add(&iio_hrtimer_trig_dev); +} +module_init(iio_hrtimer_trig_init); + +static void __exit iio_hrtimer_trig_exit(void) +{ + device_unregister(&iio_hrtimer_trig_dev); +} +module_exit(iio_hrtimer_trig_exit); + +MODULE_AUTHOR("Denis Ciocca <denis.ciocca@xxxxxx>"); +MODULE_DESCRIPTION("Hrtimer trigger for the iio subsystem"); +MODULE_LICENSE("GPL v2"); -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html