Commit | Line | Data |
---|---|---|
5ea81769 AV |
1 | #include <linux/module.h> |
2 | #include <linux/interrupt.h> | |
0af3678f | 3 | #include <linux/device.h> |
1aeb272c | 4 | #include <linux/gfp.h> |
5ea81769 AV |
5 | |
6 | /* | |
7 | * Device resource management aware IRQ request/free implementation. | |
8 | */ | |
9 | struct irq_devres { | |
10 | unsigned int irq; | |
11 | void *dev_id; | |
12 | }; | |
13 | ||
14 | static void devm_irq_release(struct device *dev, void *res) | |
15 | { | |
16 | struct irq_devres *this = res; | |
17 | ||
18 | free_irq(this->irq, this->dev_id); | |
19 | } | |
20 | ||
21 | static int devm_irq_match(struct device *dev, void *res, void *data) | |
22 | { | |
23 | struct irq_devres *this = res, *match = data; | |
24 | ||
25 | return this->irq == match->irq && this->dev_id == match->dev_id; | |
26 | } | |
27 | ||
28 | /** | |
935bd5b9 | 29 | * devm_request_threaded_irq - allocate an interrupt line for a managed device |
5ea81769 AV |
30 | * @dev: device to request interrupt for |
31 | * @irq: Interrupt line to allocate | |
32 | * @handler: Function to be called when the IRQ occurs | |
935bd5b9 AV |
33 | * @thread_fn: function to be called in a threaded interrupt context. NULL |
34 | * for devices which handle everything in @handler | |
5ea81769 AV |
35 | * @irqflags: Interrupt type flags |
36 | * @devname: An ascii name for the claiming device | |
37 | * @dev_id: A cookie passed back to the handler function | |
38 | * | |
39 | * Except for the extra @dev argument, this function takes the | |
40 | * same arguments and performs the same function as | |
307b28b9 | 41 | * request_threaded_irq(). IRQs requested with this function will be |
5ea81769 AV |
42 | * automatically freed on driver detach. |
43 | * | |
44 | * If an IRQ allocated with this function needs to be freed | |
5c42dc70 | 45 | * separately, devm_free_irq() must be used. |
5ea81769 | 46 | */ |
935bd5b9 AV |
47 | int devm_request_threaded_irq(struct device *dev, unsigned int irq, |
48 | irq_handler_t handler, irq_handler_t thread_fn, | |
49 | unsigned long irqflags, const char *devname, | |
50 | void *dev_id) | |
5ea81769 AV |
51 | { |
52 | struct irq_devres *dr; | |
53 | int rc; | |
54 | ||
55 | dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres), | |
56 | GFP_KERNEL); | |
57 | if (!dr) | |
58 | return -ENOMEM; | |
59 | ||
935bd5b9 AV |
60 | rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname, |
61 | dev_id); | |
5ea81769 | 62 | if (rc) { |
7f30e49e | 63 | devres_free(dr); |
5ea81769 AV |
64 | return rc; |
65 | } | |
66 | ||
67 | dr->irq = irq; | |
68 | dr->dev_id = dev_id; | |
69 | devres_add(dev, dr); | |
70 | ||
71 | return 0; | |
72 | } | |
935bd5b9 | 73 | EXPORT_SYMBOL(devm_request_threaded_irq); |
5ea81769 | 74 | |
0668d306 SB |
75 | /** |
76 | * devm_request_any_context_irq - allocate an interrupt line for a managed device | |
77 | * @dev: device to request interrupt for | |
78 | * @irq: Interrupt line to allocate | |
79 | * @handler: Function to be called when the IRQ occurs | |
80 | * @thread_fn: function to be called in a threaded interrupt context. NULL | |
81 | * for devices which handle everything in @handler | |
82 | * @irqflags: Interrupt type flags | |
83 | * @devname: An ascii name for the claiming device | |
84 | * @dev_id: A cookie passed back to the handler function | |
85 | * | |
86 | * Except for the extra @dev argument, this function takes the | |
87 | * same arguments and performs the same function as | |
88 | * request_any_context_irq(). IRQs requested with this function will be | |
89 | * automatically freed on driver detach. | |
90 | * | |
91 | * If an IRQ allocated with this function needs to be freed | |
92 | * separately, devm_free_irq() must be used. | |
93 | */ | |
94 | int devm_request_any_context_irq(struct device *dev, unsigned int irq, | |
95 | irq_handler_t handler, unsigned long irqflags, | |
96 | const char *devname, void *dev_id) | |
97 | { | |
98 | struct irq_devres *dr; | |
99 | int rc; | |
100 | ||
101 | dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres), | |
102 | GFP_KERNEL); | |
103 | if (!dr) | |
104 | return -ENOMEM; | |
105 | ||
106 | rc = request_any_context_irq(irq, handler, irqflags, devname, dev_id); | |
63781394 | 107 | if (rc < 0) { |
0668d306 SB |
108 | devres_free(dr); |
109 | return rc; | |
110 | } | |
111 | ||
112 | dr->irq = irq; | |
113 | dr->dev_id = dev_id; | |
114 | devres_add(dev, dr); | |
115 | ||
63781394 | 116 | return rc; |
0668d306 SB |
117 | } |
118 | EXPORT_SYMBOL(devm_request_any_context_irq); | |
119 | ||
5ea81769 AV |
120 | /** |
121 | * devm_free_irq - free an interrupt | |
122 | * @dev: device to free interrupt for | |
123 | * @irq: Interrupt line to free | |
124 | * @dev_id: Device identity to free | |
125 | * | |
126 | * Except for the extra @dev argument, this function takes the | |
127 | * same arguments and performs the same function as free_irq(). | |
128 | * This function instead of free_irq() should be used to manually | |
9ce8e498 | 129 | * free IRQs allocated with devm_request_irq(). |
5ea81769 AV |
130 | */ |
131 | void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id) | |
132 | { | |
133 | struct irq_devres match_data = { irq, dev_id }; | |
134 | ||
5ea81769 AV |
135 | WARN_ON(devres_destroy(dev, devm_irq_release, devm_irq_match, |
136 | &match_data)); | |
ae891a1b | 137 | free_irq(irq, dev_id); |
5ea81769 AV |
138 | } |
139 | EXPORT_SYMBOL(devm_free_irq); |