1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Industrial I/O - generic interrupt based trigger support |
4 | * |
5 | * Copyright (c) 2008-2013 Jonathan Cameron |
6 | */ |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/module.h> |
10 | #include <linux/platform_device.h> |
11 | #include <linux/interrupt.h> |
12 | #include <linux/slab.h> |
13 | |
14 | #include <linux/iio/iio.h> |
15 | #include <linux/iio/trigger.h> |
16 | |
17 | |
18 | struct iio_interrupt_trigger_info { |
19 | unsigned int irq; |
20 | }; |
21 | |
22 | static irqreturn_t iio_interrupt_trigger_poll(int irq, void *private) |
23 | { |
24 | iio_trigger_poll(trig: private); |
25 | return IRQ_HANDLED; |
26 | } |
27 | |
28 | static int iio_interrupt_trigger_probe(struct platform_device *pdev) |
29 | { |
30 | struct iio_interrupt_trigger_info *trig_info; |
31 | struct iio_trigger *trig; |
32 | unsigned long irqflags; |
33 | struct resource *irq_res; |
34 | int irq, ret = 0; |
35 | |
36 | irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); |
37 | |
38 | if (irq_res == NULL) |
39 | return -ENODEV; |
40 | |
41 | irqflags = (irq_res->flags & IRQF_TRIGGER_MASK) | IRQF_SHARED; |
42 | |
43 | irq = irq_res->start; |
44 | |
45 | trig = iio_trigger_alloc(NULL, "irqtrig%d" , irq); |
46 | if (!trig) { |
47 | ret = -ENOMEM; |
48 | goto error_ret; |
49 | } |
50 | |
51 | trig_info = kzalloc(size: sizeof(*trig_info), GFP_KERNEL); |
52 | if (!trig_info) { |
53 | ret = -ENOMEM; |
54 | goto error_free_trigger; |
55 | } |
56 | iio_trigger_set_drvdata(trig, data: trig_info); |
57 | trig_info->irq = irq; |
58 | ret = request_irq(irq, handler: iio_interrupt_trigger_poll, |
59 | flags: irqflags, name: trig->name, dev: trig); |
60 | if (ret) { |
61 | dev_err(&pdev->dev, |
62 | "request IRQ-%d failed" , irq); |
63 | goto error_free_trig_info; |
64 | } |
65 | |
66 | ret = iio_trigger_register(trig_info: trig); |
67 | if (ret) |
68 | goto error_release_irq; |
69 | platform_set_drvdata(pdev, data: trig); |
70 | |
71 | return 0; |
72 | |
73 | /* First clean up the partly allocated trigger */ |
74 | error_release_irq: |
75 | free_irq(irq, trig); |
76 | error_free_trig_info: |
77 | kfree(objp: trig_info); |
78 | error_free_trigger: |
79 | iio_trigger_free(trig); |
80 | error_ret: |
81 | return ret; |
82 | } |
83 | |
84 | static void iio_interrupt_trigger_remove(struct platform_device *pdev) |
85 | { |
86 | struct iio_trigger *trig; |
87 | struct iio_interrupt_trigger_info *trig_info; |
88 | |
89 | trig = platform_get_drvdata(pdev); |
90 | trig_info = iio_trigger_get_drvdata(trig); |
91 | iio_trigger_unregister(trig_info: trig); |
92 | free_irq(trig_info->irq, trig); |
93 | kfree(objp: trig_info); |
94 | iio_trigger_free(trig); |
95 | } |
96 | |
97 | static struct platform_driver iio_interrupt_trigger_driver = { |
98 | .probe = iio_interrupt_trigger_probe, |
99 | .remove_new = iio_interrupt_trigger_remove, |
100 | .driver = { |
101 | .name = "iio_interrupt_trigger" , |
102 | }, |
103 | }; |
104 | |
105 | module_platform_driver(iio_interrupt_trigger_driver); |
106 | |
107 | MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>" ); |
108 | MODULE_DESCRIPTION("Interrupt trigger for the iio subsystem" ); |
109 | MODULE_LICENSE("GPL v2" ); |
110 | |