/* * 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. */ #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 "../iio.h" #include "../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 IIO_TRIGGER_NAME_ATTR; 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 *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_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->irq = irq; trig->owner = THIS_MODULE; trig->name = kasprintf(GFP_KERNEL, "irqtrig%d", irq); if (trig->name == NULL) { ret = -ENOMEM; goto error_free_trig_info; } 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_name; } 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_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->irq), trig); kfree(trig->name); 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 = trig->private_data; iio_trigger_unregister(trig); free_irq(trig_info->irq, trig); 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");