| /* | 
 |  * Industrial I/O - generic interrupt based trigger support | 
 |  * | 
 |  * Copyright (c) 2008-2013 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. | 
 |  */ | 
 |  | 
 | #include <linux/kernel.h> | 
 | #include <linux/module.h> | 
 | #include <linux/platform_device.h> | 
 | #include <linux/interrupt.h> | 
 | #include <linux/slab.h> | 
 |  | 
 | #include <linux/iio/iio.h> | 
 | #include <linux/iio/trigger.h> | 
 |  | 
 |  | 
 | struct iio_interrupt_trigger_info { | 
 | 	unsigned int irq; | 
 | }; | 
 |  | 
 | static irqreturn_t iio_interrupt_trigger_poll(int irq, void *private) | 
 | { | 
 | 	iio_trigger_poll(private); | 
 | 	return IRQ_HANDLED; | 
 | } | 
 |  | 
 | static const struct iio_trigger_ops iio_interrupt_trigger_ops = { | 
 | }; | 
 |  | 
 | static int iio_interrupt_trigger_probe(struct platform_device *pdev) | 
 | { | 
 | 	struct iio_interrupt_trigger_info *trig_info; | 
 | 	struct iio_trigger *trig; | 
 | 	unsigned long irqflags; | 
 | 	struct resource *irq_res; | 
 | 	int irq, ret = 0; | 
 |  | 
 | 	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | 
 |  | 
 | 	if (irq_res == NULL) | 
 | 		return -ENODEV; | 
 |  | 
 | 	irqflags = (irq_res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED; | 
 |  | 
 | 	irq = irq_res->start; | 
 |  | 
 | 	trig = iio_trigger_alloc("irqtrig%d", irq); | 
 | 	if (!trig) { | 
 | 		ret = -ENOMEM; | 
 | 		goto error_ret; | 
 | 	} | 
 |  | 
 | 	trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); | 
 | 	if (!trig_info) { | 
 | 		ret = -ENOMEM; | 
 | 		goto error_free_trigger; | 
 | 	} | 
 | 	iio_trigger_set_drvdata(trig, trig_info); | 
 | 	trig_info->irq = irq; | 
 | 	trig->ops = &iio_interrupt_trigger_ops; | 
 | 	ret = request_irq(irq, iio_interrupt_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; | 
 | 	platform_set_drvdata(pdev, trig); | 
 |  | 
 | 	return 0; | 
 |  | 
 | /* First clean up the partly allocated trigger */ | 
 | error_release_irq: | 
 | 	free_irq(irq, trig); | 
 | error_free_trig_info: | 
 | 	kfree(trig_info); | 
 | error_free_trigger: | 
 | 	iio_trigger_free(trig); | 
 | error_ret: | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int iio_interrupt_trigger_remove(struct platform_device *pdev) | 
 | { | 
 | 	struct iio_trigger *trig; | 
 | 	struct iio_interrupt_trigger_info *trig_info; | 
 |  | 
 | 	trig = platform_get_drvdata(pdev); | 
 | 	trig_info = iio_trigger_get_drvdata(trig); | 
 | 	iio_trigger_unregister(trig); | 
 | 	free_irq(trig_info->irq, trig); | 
 | 	kfree(trig_info); | 
 | 	iio_trigger_free(trig); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static struct platform_driver iio_interrupt_trigger_driver = { | 
 | 	.probe = iio_interrupt_trigger_probe, | 
 | 	.remove = iio_interrupt_trigger_remove, | 
 | 	.driver = { | 
 | 		.name = "iio_interrupt_trigger", | 
 | 	}, | 
 | }; | 
 |  | 
 | module_platform_driver(iio_interrupt_trigger_driver); | 
 |  | 
 | MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); | 
 | MODULE_DESCRIPTION("Interrupt trigger for the iio subsystem"); | 
 | MODULE_LICENSE("GPL v2"); |