/* * 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 #include #include #include #include #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; } static 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 "); MODULE_DESCRIPTION("Example gpio trigger for the iio subsystem"); MODULE_LICENSE("GPL v2");