Staging: IIO: Proof of concept gpio trigger
authorJonathan Cameron <jic23@cam.ac.uk>
Tue, 18 Aug 2009 17:06:31 +0000 (18:06 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 15 Sep 2009 19:02:25 +0000 (12:02 -0700)
Simple example of how a gpio trigger driver would work.
Things to be added include interupt type control (high, low).

Signed-off-by: Jonathan Cameron <jic23@cam.ac.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/staging/iio/trigger/Kconfig
drivers/staging/iio/trigger/Makefile
drivers/staging/iio/trigger/iio-trig-gpio.c [new file with mode: 0644]

index 05ed139e15356f585fd6a228eb5873911822625e..fdd9301271a437177b55ea998f9148fc1694950d 100644 (file)
@@ -12,4 +12,10 @@ config IIO_PERIODIC_RTC_TRIGGER
            Provides support for using periodic capable real time
            clocks as IIO triggers.
 
+config IIO_GPIO_TRIGGER
+       tristate "GPIO trigger"
+       depends on GENERIC_GPIO
+       help
+           Provides support for using GPIO pins as IIO triggers.
+
 endif # IIO_TRIGGER
index 4ae55b9abae01d0056eae5f34203652ac733a4cf..e5f96d2fe64a2392fede5c2e17f5324566ea0dba 100644 (file)
@@ -2,4 +2,4 @@
 # 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
\ No newline at end of file
diff --git a/drivers/staging/iio/trigger/iio-trig-gpio.c b/drivers/staging/iio/trigger/iio-trig-gpio.c
new file mode 100644 (file)
index 0000000..0b3b43e
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * 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 that a fully
+ * fledged trigger driver.
+ *
+ * TODO:
+ *
+ * Add board config elements to allow specification of startup settings.
+ * Add configuration settings (irq type etc)
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+
+#include "../iio.h"
+#include "../trigger.h"
+
+LIST_HEAD(iio_gpio_trigger_list);
+DEFINE_MUTEX(iio_gpio_trigger_list_lock);
+
+struct iio_gpio_trigger_info {
+       struct mutex in_use;
+       int gpio;
+};
+/*
+ * 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)
+{
+       iio_trigger_poll(private);
+       return IRQ_HANDLED;
+}
+
+DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL);
+
+static struct attribute *iio_gpio_trigger_attrs[] = {
+       &dev_attr_name.attr,
+       NULL,
+};
+
+static const struct attribute_group iio_gpio_trigger_attr_group = {
+       .attrs = iio_gpio_trigger_attrs,
+};
+
+static int iio_gpio_trigger_probe(struct platform_device *dev)
+{
+       int *pdata = dev->dev.platform_data;
+       struct iio_gpio_trigger_info *trig_info;
+       struct iio_trigger *trig, *trig2;
+       int i, irq, ret = 0;
+       if (!pdata) {
+               printk(KERN_ERR "No IIO gpio trigger platform data found\n");
+               goto error_ret;
+       }
+       for (i = 0;; i++) {
+               if (!gpio_is_valid(pdata[i]))
+                       break;
+               trig = iio_allocate_trigger();
+               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;
+               }
+               trig->control_attrs = &iio_gpio_trigger_attr_group;
+               trig->private_data = trig_info;
+               trig_info->gpio = pdata[i];
+               trig->owner = THIS_MODULE;
+               trig->name = kmalloc(IIO_TRIGGER_NAME_LENGTH, GFP_KERNEL);
+               if (!trig->name) {
+                       ret = -ENOMEM;
+                       goto error_free_trig_info;
+               }
+               snprintf((char *)trig->name,
+                        IIO_TRIGGER_NAME_LENGTH,
+                        "gpiotrig%d",
+                        pdata[i]);
+               ret = gpio_request(trig_info->gpio, trig->name);
+               if (ret)
+                       goto error_free_name;
+
+               ret = gpio_direction_input(trig_info->gpio);
+               if (ret)
+                       goto error_release_gpio;
+
+               irq = gpio_to_irq(trig_info->gpio);
+               if (irq < 0) {
+                       ret = irq;
+                       goto error_release_gpio;
+               }
+
+               ret = request_irq(irq, iio_gpio_trigger_poll,
+                                 IRQF_TRIGGER_RISING,
+                                 trig->name,
+                                 trig);
+               if (ret)
+                       goto error_release_gpio;
+
+               ret = iio_trigger_register(trig);
+               if (ret)
+                       goto error_release_irq;
+
+               list_add_tail(&trig->alloc_list, &iio_gpio_trigger_list);
+
+       }
+       return 0;
+
+/* First clean up the partly allocated trigger */
+error_release_irq:
+       free_irq(irq, trig);
+error_release_gpio:
+       gpio_free(trig_info->gpio);
+error_free_name:
+       kfree(trig->name);
+error_free_trig_info:
+       kfree(trig_info);
+error_put_trigger:
+       iio_put_trigger(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 = trig->private_data;
+               free_irq(gpio_to_irq(trig_info->gpio), trig);
+               gpio_free(trig_info->gpio);
+               kfree(trig->name);
+               kfree(trig_info);
+               iio_trigger_unregister(trig);
+       }
+
+error_ret:
+       return ret;
+}
+
+static int iio_gpio_trigger_remove(struct platform_device *dev)
+{
+       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 = trig->private_data;
+               iio_trigger_unregister(trig);
+               free_irq(gpio_to_irq(trig_info->gpio), trig);
+               gpio_free(trig_info->gpio);
+               kfree(trig->name);
+               kfree(trig_info);
+               iio_put_trigger(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,
+       },
+};
+
+static int __init iio_gpio_trig_init(void)
+{
+       return platform_driver_register(&iio_gpio_trigger_driver);
+}
+module_init(iio_gpio_trig_init);
+
+static void __exit iio_gpio_trig_exit(void)
+{
+       platform_driver_unregister(&iio_gpio_trigger_driver);
+}
+module_exit(iio_gpio_trig_exit);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_DESCRIPTION("Example gpio trigger for the iio subsystem");
+MODULE_LICENSE("GPL v2");