summaryrefslogtreecommitdiff
path: root/drivers/staging/iio/trigger
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/staging/iio/trigger
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/staging/iio/trigger')
-rw-r--r--drivers/staging/iio/trigger/Kconfig43
-rw-r--r--drivers/staging/iio/trigger/Makefile8
-rw-r--r--drivers/staging/iio/trigger/iio-trig-bfin-timer.c307
-rw-r--r--drivers/staging/iio/trigger/iio-trig-bfin-timer.h24
-rw-r--r--drivers/staging/iio/trigger/iio-trig-gpio.c167
-rw-r--r--drivers/staging/iio/trigger/iio-trig-periodic-rtc.c202
-rw-r--r--drivers/staging/iio/trigger/iio-trig-sysfs.c227
7 files changed, 978 insertions, 0 deletions
diff --git a/drivers/staging/iio/trigger/Kconfig b/drivers/staging/iio/trigger/Kconfig
new file mode 100644
index 00000000..1a051da6
--- /dev/null
+++ b/drivers/staging/iio/trigger/Kconfig
@@ -0,0 +1,43 @@
+#
+# 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.
+
+config IIO_GPIO_TRIGGER
+ tristate "GPIO trigger"
+ depends on GPIOLIB
+ help
+ Provides support for using GPIO pins as IIO triggers.
+
+config IIO_SYSFS_TRIGGER
+ tristate "SYSFS trigger"
+ depends on SYSFS
+ select IRQ_WORK
+ help
+ Provides support for using SYSFS entry 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-sysfs.
+
+config IIO_BFIN_TMR_TRIGGER
+ tristate "Blackfin TIMER trigger"
+ depends on BLACKFIN
+ select BFIN_GPTIMERS
+ help
+ Provides support for using a Blackfin timer 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-bfin-timer.
+
+endif # IIO_TRIGGER
diff --git a/drivers/staging/iio/trigger/Makefile b/drivers/staging/iio/trigger/Makefile
new file mode 100644
index 00000000..b088b57d
--- /dev/null
+++ b/drivers/staging/iio/trigger/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for triggers not associated with iio-devices
+#
+
+obj-$(CONFIG_IIO_PERIODIC_RTC_TRIGGER) += iio-trig-periodic-rtc.o
+obj-$(CONFIG_IIO_GPIO_TRIGGER) += iio-trig-gpio.o
+obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o
+obj-$(CONFIG_IIO_BFIN_TMR_TRIGGER) += iio-trig-bfin-timer.o
diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c
new file mode 100644
index 00000000..38a158b7
--- /dev/null
+++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+
+#include <asm/gptimers.h>
+#include <asm/portmux.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+#include "iio-trig-bfin-timer.h"
+
+struct bfin_timer {
+ unsigned short id, bit;
+ unsigned long irqbit;
+ int irq;
+ int pin;
+};
+
+/*
+ * this covers all hardware timer configurations on
+ * all Blackfin derivatives out there today
+ */
+
+static struct bfin_timer iio_bfin_timer_code[MAX_BLACKFIN_GPTIMERS] = {
+ {TIMER0_id, TIMER0bit, TIMER_STATUS_TIMIL0, IRQ_TIMER0, P_TMR0},
+ {TIMER1_id, TIMER1bit, TIMER_STATUS_TIMIL1, IRQ_TIMER1, P_TMR1},
+ {TIMER2_id, TIMER2bit, TIMER_STATUS_TIMIL2, IRQ_TIMER2, P_TMR2},
+#if (MAX_BLACKFIN_GPTIMERS > 3)
+ {TIMER3_id, TIMER3bit, TIMER_STATUS_TIMIL3, IRQ_TIMER3, P_TMR3},
+ {TIMER4_id, TIMER4bit, TIMER_STATUS_TIMIL4, IRQ_TIMER4, P_TMR4},
+ {TIMER5_id, TIMER5bit, TIMER_STATUS_TIMIL5, IRQ_TIMER5, P_TMR5},
+ {TIMER6_id, TIMER6bit, TIMER_STATUS_TIMIL6, IRQ_TIMER6, P_TMR6},
+ {TIMER7_id, TIMER7bit, TIMER_STATUS_TIMIL7, IRQ_TIMER7, P_TMR7},
+#endif
+#if (MAX_BLACKFIN_GPTIMERS > 8)
+ {TIMER8_id, TIMER8bit, TIMER_STATUS_TIMIL8, IRQ_TIMER8, P_TMR8},
+ {TIMER9_id, TIMER9bit, TIMER_STATUS_TIMIL9, IRQ_TIMER9, P_TMR9},
+ {TIMER10_id, TIMER10bit, TIMER_STATUS_TIMIL10, IRQ_TIMER10, P_TMR10},
+#if (MAX_BLACKFIN_GPTIMERS > 11)
+ {TIMER11_id, TIMER11bit, TIMER_STATUS_TIMIL11, IRQ_TIMER11, P_TMR11},
+#endif
+#endif
+};
+
+struct bfin_tmr_state {
+ struct iio_trigger *trig;
+ struct bfin_timer *t;
+ unsigned timer_num;
+ bool output_enable;
+ unsigned int duty;
+ int irq;
+};
+
+static int iio_bfin_tmr_set_state(struct iio_trigger *trig, bool state)
+{
+ struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig);
+
+ if (get_gptimer_period(st->t->id) == 0)
+ return -EINVAL;
+
+ if (state)
+ enable_gptimers(st->t->bit);
+ else
+ disable_gptimers(st->t->bit);
+
+ return 0;
+}
+
+static ssize_t iio_bfin_tmr_frequency_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_trigger *trig = to_iio_trigger(dev);
+ struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig);
+ unsigned long val;
+ bool enabled;
+ int ret;
+
+ ret = strict_strtoul(buf, 10, &val);
+ if (ret)
+ goto error_ret;
+
+ if (val > 100000) {
+ ret = -EINVAL;
+ goto error_ret;
+ }
+
+ enabled = get_enabled_gptimers() & st->t->bit;
+
+ if (enabled)
+ disable_gptimers(st->t->bit);
+
+ if (!val)
+ goto error_ret;
+
+ val = get_sclk() / val;
+ if (val <= 4 || val <= st->duty) {
+ ret = -EINVAL;
+ goto error_ret;
+ }
+
+ set_gptimer_period(st->t->id, val);
+ set_gptimer_pwidth(st->t->id, val - st->duty);
+
+ if (enabled)
+ enable_gptimers(st->t->bit);
+
+error_ret:
+ return ret ? ret : count;
+}
+
+static ssize_t iio_bfin_tmr_frequency_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_trigger *trig = to_iio_trigger(dev);
+ struct bfin_tmr_state *st = iio_trigger_get_drvdata(trig);
+ unsigned int period = get_gptimer_period(st->t->id);
+ unsigned long val;
+
+ if (period == 0)
+ val = 0;
+ else
+ val = get_sclk() / get_gptimer_period(st->t->id);
+
+ return sprintf(buf, "%lu\n", val);
+}
+
+static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR, iio_bfin_tmr_frequency_show,
+ iio_bfin_tmr_frequency_store);
+
+static struct attribute *iio_bfin_tmr_trigger_attrs[] = {
+ &dev_attr_frequency.attr,
+ NULL,
+};
+
+static const struct attribute_group iio_bfin_tmr_trigger_attr_group = {
+ .attrs = iio_bfin_tmr_trigger_attrs,
+};
+
+static const struct attribute_group *iio_bfin_tmr_trigger_attr_groups[] = {
+ &iio_bfin_tmr_trigger_attr_group,
+ NULL
+};
+
+static irqreturn_t iio_bfin_tmr_trigger_isr(int irq, void *devid)
+{
+ struct bfin_tmr_state *st = devid;
+
+ clear_gptimer_intr(st->t->id);
+ iio_trigger_poll(st->trig, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int iio_bfin_tmr_get_number(int irq)
+{
+ int i;
+
+ for (i = 0; i < MAX_BLACKFIN_GPTIMERS; i++)
+ if (iio_bfin_timer_code[i].irq == irq)
+ return i;
+
+ return -ENODEV;
+}
+
+static const struct iio_trigger_ops iio_bfin_tmr_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = iio_bfin_tmr_set_state,
+};
+
+static int iio_bfin_tmr_trigger_probe(struct platform_device *pdev)
+{
+ struct iio_bfin_timer_trigger_pdata *pdata = pdev->dev.platform_data;
+ struct bfin_tmr_state *st;
+ unsigned int config;
+ int ret;
+
+ st = kzalloc(sizeof(*st), GFP_KERNEL);
+ if (st == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ st->irq = platform_get_irq(pdev, 0);
+ if (!st->irq) {
+ dev_err(&pdev->dev, "No IRQs specified");
+ ret = -ENODEV;
+ goto out1;
+ }
+
+ ret = iio_bfin_tmr_get_number(st->irq);
+ if (ret < 0)
+ goto out1;
+
+ st->timer_num = ret;
+ st->t = &iio_bfin_timer_code[st->timer_num];
+
+ st->trig = iio_trigger_alloc("bfintmr%d", st->timer_num);
+ if (!st->trig) {
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ st->trig->ops = &iio_bfin_tmr_trigger_ops;
+ st->trig->dev.groups = iio_bfin_tmr_trigger_attr_groups;
+ iio_trigger_set_drvdata(st->trig, st);
+ ret = iio_trigger_register(st->trig);
+ if (ret)
+ goto out2;
+
+ ret = request_irq(st->irq, iio_bfin_tmr_trigger_isr,
+ 0, st->trig->name, st);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "request IRQ-%d failed", st->irq);
+ goto out4;
+ }
+
+ config = PWM_OUT | PERIOD_CNT | IRQ_ENA;
+
+ if (pdata && pdata->output_enable) {
+ unsigned long long val;
+
+ st->output_enable = true;
+
+ ret = peripheral_request(st->t->pin, st->trig->name);
+ if (ret)
+ goto out_free_irq;
+
+ val = (unsigned long long)get_sclk() * pdata->duty_ns;
+ do_div(val, NSEC_PER_SEC);
+ st->duty = val;
+
+ /**
+ * The interrupt will be generated at the end of the period,
+ * since we want the interrupt to be generated at end of the
+ * pulse we invert both polarity and duty cycle, so that the
+ * pulse will be generated directly before the interrupt.
+ */
+ if (pdata->active_low)
+ config |= PULSE_HI;
+ } else {
+ st->duty = 1;
+ config |= OUT_DIS;
+ }
+
+ set_gptimer_config(st->t->id, config);
+
+ dev_info(&pdev->dev, "iio trigger Blackfin TMR%d, IRQ-%d",
+ st->timer_num, st->irq);
+ platform_set_drvdata(pdev, st);
+
+ return 0;
+out_free_irq:
+ free_irq(st->irq, st);
+out4:
+ iio_trigger_unregister(st->trig);
+out2:
+ iio_trigger_put(st->trig);
+out1:
+ kfree(st);
+out:
+ return ret;
+}
+
+static int iio_bfin_tmr_trigger_remove(struct platform_device *pdev)
+{
+ struct bfin_tmr_state *st = platform_get_drvdata(pdev);
+
+ disable_gptimers(st->t->bit);
+ if (st->output_enable)
+ peripheral_free(st->t->pin);
+ free_irq(st->irq, st);
+ iio_trigger_unregister(st->trig);
+ iio_trigger_put(st->trig);
+ kfree(st);
+
+ return 0;
+}
+
+static struct platform_driver iio_bfin_tmr_trigger_driver = {
+ .driver = {
+ .name = "iio_bfin_tmr_trigger",
+ .owner = THIS_MODULE,
+ },
+ .probe = iio_bfin_tmr_trigger_probe,
+ .remove = iio_bfin_tmr_trigger_remove,
+};
+
+module_platform_driver(iio_bfin_tmr_trigger_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Blackfin system timer based trigger for the iio subsystem");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:iio-trig-bfin-timer");
diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.h b/drivers/staging/iio/trigger/iio-trig-bfin-timer.h
new file mode 100644
index 00000000..c07321f8
--- /dev/null
+++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.h
@@ -0,0 +1,24 @@
+#ifndef __IIO_BFIN_TIMER_TRIGGER_H__
+#define __IIO_BFIN_TIMER_TRIGGER_H__
+
+/**
+ * struct iio_bfin_timer_trigger_pdata - timer trigger platform data
+ * @output_enable: Enable external trigger pulse generation.
+ * @active_low: Whether the trigger pulse is active low.
+ * @duty_ns: Length of the trigger pulse in nanoseconds.
+ *
+ * This struct is used to configure the output pulse generation of the blackfin
+ * timer trigger. If output_enable is set to true an external trigger signal
+ * will generated on the pin corresponding to the timer. This is useful for
+ * converters which needs an external signal to start conversion. active_low and
+ * duty_ns are used to configure the type of the trigger pulse. If output_enable
+ * is set to false no external trigger pulse will be generated and active_low
+ * and duty_ns are ignored.
+ **/
+struct iio_bfin_timer_trigger_pdata {
+ bool output_enable;
+ bool active_low;
+ unsigned int duty_ns;
+};
+
+#endif
diff --git a/drivers/staging/iio/trigger/iio-trig-gpio.c b/drivers/staging/iio/trigger/iio-trig-gpio.c
new file mode 100644
index 00000000..7c593d18
--- /dev/null
+++ b/drivers/staging/iio/trigger/iio-trig-gpio.c
@@ -0,0 +1,167 @@
+/*
+ * Industrial I/O - gpio based trigger support
+ *
+ * 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.
+ *
+ * Currently this is more of a functioning proof of concept than a full
+ * fledged trigger driver.
+ *
+ * TODO:
+ *
+ * Add board config elements to allow specification of startup settings.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+static LIST_HEAD(iio_gpio_trigger_list);
+static DEFINE_MUTEX(iio_gpio_trigger_list_lock);
+
+struct iio_gpio_trigger_info {
+ struct mutex in_use;
+ unsigned int irq;
+};
+/*
+ * Need to reference count these triggers and only enable gpio interrupts
+ * as appropriate.
+ */
+
+/* So what functionality do we want in here?... */
+/* set high / low as interrupt type? */
+
+static irqreturn_t iio_gpio_trigger_poll(int irq, void *private)
+{
+ /* Timestamp not currently provided */
+ iio_trigger_poll(private, 0);
+ return IRQ_HANDLED;
+}
+
+static const struct iio_trigger_ops iio_gpio_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+static int iio_gpio_trigger_probe(struct platform_device *pdev)
+{
+ struct iio_gpio_trigger_info *trig_info;
+ struct iio_trigger *trig, *trig2;
+ unsigned long irqflags;
+ struct resource *irq_res;
+ int irq, ret = 0, irq_res_cnt = 0;
+
+ do {
+ irq_res = platform_get_resource(pdev,
+ IORESOURCE_IRQ, irq_res_cnt);
+
+ if (irq_res == NULL) {
+ if (irq_res_cnt == 0)
+ dev_err(&pdev->dev, "No GPIO IRQs specified");
+ break;
+ }
+ irqflags = (irq_res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED;
+
+ for (irq = irq_res->start; irq <= irq_res->end; irq++) {
+
+ trig = iio_trigger_alloc("irqtrig%d", irq);
+ if (!trig) {
+ ret = -ENOMEM;
+ goto error_free_completed_registrations;
+ }
+
+ trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL);
+ if (!trig_info) {
+ ret = -ENOMEM;
+ goto error_put_trigger;
+ }
+ iio_trigger_set_drvdata(trig, trig_info);
+ trig_info->irq = irq;
+ trig->ops = &iio_gpio_trigger_ops;
+ ret = request_irq(irq, iio_gpio_trigger_poll,
+ irqflags, trig->name, trig);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "request IRQ-%d failed", irq);
+ goto error_free_trig_info;
+ }
+
+ ret = iio_trigger_register(trig);
+ if (ret)
+ goto error_release_irq;
+
+ list_add_tail(&trig->alloc_list,
+ &iio_gpio_trigger_list);
+ }
+
+ irq_res_cnt++;
+ } while (irq_res != NULL);
+
+
+ return 0;
+
+/* First clean up the partly allocated trigger */
+error_release_irq:
+ free_irq(irq, trig);
+error_free_trig_info:
+ kfree(trig_info);
+error_put_trigger:
+ iio_trigger_put(trig);
+error_free_completed_registrations:
+ /* The rest should have been added to the iio_gpio_trigger_list */
+ list_for_each_entry_safe(trig,
+ trig2,
+ &iio_gpio_trigger_list,
+ alloc_list) {
+ trig_info = iio_trigger_get_drvdata(trig);
+ free_irq(gpio_to_irq(trig_info->irq), trig);
+ kfree(trig_info);
+ iio_trigger_unregister(trig);
+ }
+
+ return ret;
+}
+
+static int iio_gpio_trigger_remove(struct platform_device *pdev)
+{
+ struct iio_trigger *trig, *trig2;
+ struct iio_gpio_trigger_info *trig_info;
+
+ mutex_lock(&iio_gpio_trigger_list_lock);
+ list_for_each_entry_safe(trig,
+ trig2,
+ &iio_gpio_trigger_list,
+ alloc_list) {
+ trig_info = iio_trigger_get_drvdata(trig);
+ iio_trigger_unregister(trig);
+ free_irq(trig_info->irq, trig);
+ kfree(trig_info);
+ iio_trigger_put(trig);
+ }
+ mutex_unlock(&iio_gpio_trigger_list_lock);
+
+ return 0;
+}
+
+static struct platform_driver iio_gpio_trigger_driver = {
+ .probe = iio_gpio_trigger_probe,
+ .remove = iio_gpio_trigger_remove,
+ .driver = {
+ .name = "iio_gpio_trigger",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(iio_gpio_trigger_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("Example gpio trigger for the iio subsystem");
+MODULE_LICENSE("GPL v2");
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 00000000..79695974
--- /dev/null
+++ b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c
@@ -0,0 +1,202 @@
+/* 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/slab.h>
+#include <linux/rtc.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+static LIST_HEAD(iio_prtc_trigger_list);
+static DEFINE_MUTEX(iio_prtc_trigger_list_lock);
+
+struct iio_prtc_trigger_info {
+ struct rtc_device *rtc;
+ int frequency;
+ 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 = iio_trigger_get_drvdata(trig);
+ 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 = to_iio_trigger(dev);
+ struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig);
+ 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 = to_iio_trigger(dev);
+ struct iio_prtc_trigger_info *trig_info = iio_trigger_get_drvdata(trig);
+ 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 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,
+ NULL,
+};
+
+static const struct attribute_group iio_trig_prtc_attr_group = {
+ .attrs = iio_trig_prtc_attrs,
+};
+
+static const struct attribute_group *iio_trig_prtc_attr_groups[] = {
+ &iio_trig_prtc_attr_group,
+ NULL
+};
+
+static void iio_prtc_trigger_poll(void *private_data)
+{
+ /* Timestamp is not provided currently */
+ iio_trigger_poll(private_data, 0);
+}
+
+static const struct iio_trigger_ops iio_prtc_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &iio_trig_periodic_rtc_set_state,
+};
+
+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_trigger_alloc("periodic%s", pdata[i]);
+ 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;
+ }
+ iio_trigger_set_drvdata(trig, trig_info);
+ trig->ops = &iio_prtc_trigger_ops;
+ /* RTC access */
+ trig_info->rtc
+ = rtc_class_open(pdata[i]);
+ if (trig_info->rtc == NULL) {
+ ret = -EINVAL;
+ goto error_free_trig_info;
+ }
+ 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->dev.groups = iio_trig_prtc_attr_groups;
+ 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_trig_info:
+ kfree(trig_info);
+error_put_trigger_and_remove_from_list:
+ list_del(&trig->alloc_list);
+ iio_trigger_put(trig);
+error_free_completed_registrations:
+ list_for_each_entry_safe(trig,
+ trig2,
+ &iio_prtc_trigger_list,
+ alloc_list) {
+ trig_info = iio_trigger_get_drvdata(trig);
+ rtc_irq_unregister(trig_info->rtc, &trig_info->task);
+ rtc_class_close(trig_info->rtc);
+ 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 = iio_trigger_get_drvdata(trig);
+ rtc_irq_unregister(trig_info->rtc, &trig_info->task);
+ rtc_class_close(trig_info->rtc);
+ 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,
+ },
+};
+
+module_platform_driver(iio_trig_periodic_rtc_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("Periodic realtime clock trigger for the iio subsystem");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/trigger/iio-trig-sysfs.c b/drivers/staging/iio/trigger/iio-trig-sysfs.c
new file mode 100644
index 00000000..b727bde8
--- /dev/null
+++ b/drivers/staging/iio/trigger/iio-trig-sysfs.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/irq_work.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+
+struct iio_sysfs_trig {
+ struct iio_trigger *trig;
+ struct irq_work work;
+ int id;
+ struct list_head l;
+};
+
+static LIST_HEAD(iio_sysfs_trig_list);
+static DEFINE_MUTEX(iio_syfs_trig_list_mut);
+
+static int iio_sysfs_trigger_probe(int id);
+static ssize_t iio_sysfs_trig_add(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ int ret;
+ unsigned long input;
+
+ ret = strict_strtoul(buf, 10, &input);
+ if (ret)
+ return ret;
+ ret = iio_sysfs_trigger_probe(input);
+ if (ret)
+ return ret;
+ return len;
+}
+static DEVICE_ATTR(add_trigger, S_IWUSR, NULL, &iio_sysfs_trig_add);
+
+static int iio_sysfs_trigger_remove(int id);
+static ssize_t iio_sysfs_trig_remove(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ int ret;
+ unsigned long input;
+
+ ret = strict_strtoul(buf, 10, &input);
+ if (ret)
+ return ret;
+ ret = iio_sysfs_trigger_remove(input);
+ if (ret)
+ return ret;
+ return len;
+}
+
+static DEVICE_ATTR(remove_trigger, S_IWUSR, NULL, &iio_sysfs_trig_remove);
+
+static struct attribute *iio_sysfs_trig_attrs[] = {
+ &dev_attr_add_trigger.attr,
+ &dev_attr_remove_trigger.attr,
+ NULL,
+};
+
+static const struct attribute_group iio_sysfs_trig_group = {
+ .attrs = iio_sysfs_trig_attrs,
+};
+
+static const struct attribute_group *iio_sysfs_trig_groups[] = {
+ &iio_sysfs_trig_group,
+ NULL
+};
+
+
+/* Nothing to actually do upon release */
+static void iio_trigger_sysfs_release(struct device *dev)
+{
+}
+
+static struct device iio_sysfs_trig_dev = {
+ .bus = &iio_bus_type,
+ .groups = iio_sysfs_trig_groups,
+ .release = &iio_trigger_sysfs_release,
+};
+
+static void iio_sysfs_trigger_work(struct irq_work *work)
+{
+ struct iio_sysfs_trig *trig = container_of(work, struct iio_sysfs_trig,
+ work);
+
+ iio_trigger_poll(trig->trig, 0);
+}
+
+static ssize_t iio_sysfs_trigger_poll(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct iio_trigger *trig = to_iio_trigger(dev);
+ struct iio_sysfs_trig *sysfs_trig = iio_trigger_get_drvdata(trig);
+
+ irq_work_queue(&sysfs_trig->work);
+
+ return count;
+}
+
+static DEVICE_ATTR(trigger_now, S_IWUSR, NULL, iio_sysfs_trigger_poll);
+
+static struct attribute *iio_sysfs_trigger_attrs[] = {
+ &dev_attr_trigger_now.attr,
+ NULL,
+};
+
+static const struct attribute_group iio_sysfs_trigger_attr_group = {
+ .attrs = iio_sysfs_trigger_attrs,
+};
+
+static const struct attribute_group *iio_sysfs_trigger_attr_groups[] = {
+ &iio_sysfs_trigger_attr_group,
+ NULL
+};
+
+static const struct iio_trigger_ops iio_sysfs_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+static int iio_sysfs_trigger_probe(int id)
+{
+ struct iio_sysfs_trig *t;
+ int ret;
+ bool foundit = false;
+ mutex_lock(&iio_syfs_trig_list_mut);
+ list_for_each_entry(t, &iio_sysfs_trig_list, l)
+ if (id == t->id) {
+ foundit = true;
+ break;
+ }
+ if (foundit) {
+ ret = -EINVAL;
+ goto out1;
+ }
+ t = kmalloc(sizeof(*t), GFP_KERNEL);
+ if (t == NULL) {
+ ret = -ENOMEM;
+ goto out1;
+ }
+ t->id = id;
+ t->trig = iio_trigger_alloc("sysfstrig%d", id);
+ if (!t->trig) {
+ ret = -ENOMEM;
+ goto free_t;
+ }
+
+ t->trig->dev.groups = iio_sysfs_trigger_attr_groups;
+ t->trig->ops = &iio_sysfs_trigger_ops;
+ t->trig->dev.parent = &iio_sysfs_trig_dev;
+ iio_trigger_set_drvdata(t->trig, t);
+
+ init_irq_work(&t->work, iio_sysfs_trigger_work);
+
+ ret = iio_trigger_register(t->trig);
+ if (ret)
+ goto out2;
+ list_add(&t->l, &iio_sysfs_trig_list);
+ __module_get(THIS_MODULE);
+ mutex_unlock(&iio_syfs_trig_list_mut);
+ return 0;
+
+out2:
+ iio_trigger_put(t->trig);
+free_t:
+ kfree(t);
+out1:
+ mutex_unlock(&iio_syfs_trig_list_mut);
+ return ret;
+}
+
+static int iio_sysfs_trigger_remove(int id)
+{
+ bool foundit = false;
+ struct iio_sysfs_trig *t;
+ mutex_lock(&iio_syfs_trig_list_mut);
+ list_for_each_entry(t, &iio_sysfs_trig_list, l)
+ if (id == t->id) {
+ foundit = true;
+ break;
+ }
+ if (!foundit) {
+ mutex_unlock(&iio_syfs_trig_list_mut);
+ return -EINVAL;
+ }
+
+ iio_trigger_unregister(t->trig);
+ iio_trigger_free(t->trig);
+
+ list_del(&t->l);
+ kfree(t);
+ module_put(THIS_MODULE);
+ mutex_unlock(&iio_syfs_trig_list_mut);
+ return 0;
+}
+
+
+static int __init iio_sysfs_trig_init(void)
+{
+ device_initialize(&iio_sysfs_trig_dev);
+ dev_set_name(&iio_sysfs_trig_dev, "iio_sysfs_trigger");
+ return device_add(&iio_sysfs_trig_dev);
+}
+module_init(iio_sysfs_trig_init);
+
+static void __exit iio_sysfs_trig_exit(void)
+{
+ device_unregister(&iio_sysfs_trig_dev);
+}
+module_exit(iio_sysfs_trig_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Sysfs based trigger for the iio subsystem");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:iio-trig-sysfs");