Commit | Line | Data |
---|---|---|
f8beab2b MB |
1 | /* |
2 | * regmap based irq_chip | |
3 | * | |
4 | * Copyright 2011 Wolfson Microelectronics plc | |
5 | * | |
6 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License version 2 as | |
10 | * published by the Free Software Foundation. | |
11 | */ | |
12 | ||
13 | #include <linux/export.h> | |
51990e82 | 14 | #include <linux/device.h> |
f8beab2b MB |
15 | #include <linux/regmap.h> |
16 | #include <linux/irq.h> | |
17 | #include <linux/interrupt.h> | |
18 | #include <linux/slab.h> | |
19 | ||
20 | #include "internal.h" | |
21 | ||
22 | struct regmap_irq_chip_data { | |
23 | struct mutex lock; | |
24 | ||
25 | struct regmap *map; | |
26 | struct regmap_irq_chip *chip; | |
27 | ||
28 | int irq_base; | |
29 | ||
30 | void *status_reg_buf; | |
31 | unsigned int *status_buf; | |
32 | unsigned int *mask_buf; | |
33 | unsigned int *mask_buf_def; | |
34 | }; | |
35 | ||
36 | static inline const | |
37 | struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data, | |
38 | int irq) | |
39 | { | |
40 | return &data->chip->irqs[irq - data->irq_base]; | |
41 | } | |
42 | ||
43 | static void regmap_irq_lock(struct irq_data *data) | |
44 | { | |
45 | struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
46 | ||
47 | mutex_lock(&d->lock); | |
48 | } | |
49 | ||
50 | static void regmap_irq_sync_unlock(struct irq_data *data) | |
51 | { | |
52 | struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
56806555 | 53 | struct regmap *map = d->map; |
f8beab2b MB |
54 | int i, ret; |
55 | ||
56 | /* | |
57 | * If there's been a change in the mask write it back to the | |
58 | * hardware. We rely on the use of the regmap core cache to | |
59 | * suppress pointless writes. | |
60 | */ | |
61 | for (i = 0; i < d->chip->num_regs; i++) { | |
f01ee60f | 62 | ret = regmap_update_bits(d->map, d->chip->mask_base + |
56806555 | 63 | (i * map->reg_stride), |
f8beab2b MB |
64 | d->mask_buf_def[i], d->mask_buf[i]); |
65 | if (ret != 0) | |
66 | dev_err(d->map->dev, "Failed to sync masks in %x\n", | |
f01ee60f | 67 | d->chip->mask_base + (i * map->reg_stride)); |
f8beab2b MB |
68 | } |
69 | ||
70 | mutex_unlock(&d->lock); | |
71 | } | |
72 | ||
73 | static void regmap_irq_enable(struct irq_data *data) | |
74 | { | |
75 | struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
56806555 | 76 | struct regmap *map = d->map; |
f8beab2b MB |
77 | const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); |
78 | ||
f01ee60f | 79 | d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask; |
f8beab2b MB |
80 | } |
81 | ||
82 | static void regmap_irq_disable(struct irq_data *data) | |
83 | { | |
84 | struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); | |
56806555 | 85 | struct regmap *map = d->map; |
f8beab2b MB |
86 | const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); |
87 | ||
f01ee60f | 88 | d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; |
f8beab2b MB |
89 | } |
90 | ||
91 | static struct irq_chip regmap_irq_chip = { | |
92 | .name = "regmap", | |
93 | .irq_bus_lock = regmap_irq_lock, | |
94 | .irq_bus_sync_unlock = regmap_irq_sync_unlock, | |
95 | .irq_disable = regmap_irq_disable, | |
96 | .irq_enable = regmap_irq_enable, | |
97 | }; | |
98 | ||
99 | static irqreturn_t regmap_irq_thread(int irq, void *d) | |
100 | { | |
101 | struct regmap_irq_chip_data *data = d; | |
102 | struct regmap_irq_chip *chip = data->chip; | |
103 | struct regmap *map = data->map; | |
104 | int ret, i; | |
105 | u8 *buf8 = data->status_reg_buf; | |
106 | u16 *buf16 = data->status_reg_buf; | |
107 | u32 *buf32 = data->status_reg_buf; | |
d23511f9 | 108 | bool handled = false; |
f8beab2b MB |
109 | |
110 | ret = regmap_bulk_read(map, chip->status_base, data->status_reg_buf, | |
111 | chip->num_regs); | |
112 | if (ret != 0) { | |
113 | dev_err(map->dev, "Failed to read IRQ status: %d\n", ret); | |
114 | return IRQ_NONE; | |
115 | } | |
116 | ||
117 | /* | |
118 | * Ignore masked IRQs and ack if we need to; we ack early so | |
119 | * there is no race between handling and acknowleding the | |
120 | * interrupt. We assume that typically few of the interrupts | |
121 | * will fire simultaneously so don't worry about overhead from | |
122 | * doing a write per register. | |
123 | */ | |
124 | for (i = 0; i < data->chip->num_regs; i++) { | |
125 | switch (map->format.val_bytes) { | |
126 | case 1: | |
127 | data->status_buf[i] = buf8[i]; | |
128 | break; | |
129 | case 2: | |
130 | data->status_buf[i] = buf16[i]; | |
131 | break; | |
132 | case 4: | |
133 | data->status_buf[i] = buf32[i]; | |
134 | break; | |
135 | default: | |
136 | BUG(); | |
137 | return IRQ_NONE; | |
138 | } | |
139 | ||
140 | data->status_buf[i] &= ~data->mask_buf[i]; | |
141 | ||
142 | if (data->status_buf[i] && chip->ack_base) { | |
f01ee60f SW |
143 | ret = regmap_write(map, chip->ack_base + |
144 | (i * map->reg_stride), | |
f8beab2b MB |
145 | data->status_buf[i]); |
146 | if (ret != 0) | |
147 | dev_err(map->dev, "Failed to ack 0x%x: %d\n", | |
f01ee60f SW |
148 | chip->ack_base + (i * map->reg_stride), |
149 | ret); | |
f8beab2b MB |
150 | } |
151 | } | |
152 | ||
153 | for (i = 0; i < chip->num_irqs; i++) { | |
f01ee60f SW |
154 | if (data->status_buf[chip->irqs[i].reg_offset / |
155 | map->reg_stride] & chip->irqs[i].mask) { | |
f8beab2b | 156 | handle_nested_irq(data->irq_base + i); |
d23511f9 | 157 | handled = true; |
f8beab2b MB |
158 | } |
159 | } | |
160 | ||
d23511f9 MB |
161 | if (handled) |
162 | return IRQ_HANDLED; | |
163 | else | |
164 | return IRQ_NONE; | |
f8beab2b MB |
165 | } |
166 | ||
167 | /** | |
168 | * regmap_add_irq_chip(): Use standard regmap IRQ controller handling | |
169 | * | |
170 | * map: The regmap for the device. | |
171 | * irq: The IRQ the device uses to signal interrupts | |
172 | * irq_flags: The IRQF_ flags to use for the primary interrupt. | |
173 | * chip: Configuration for the interrupt controller. | |
174 | * data: Runtime data structure for the controller, allocated on success | |
175 | * | |
176 | * Returns 0 on success or an errno on failure. | |
177 | * | |
178 | * In order for this to be efficient the chip really should use a | |
179 | * register cache. The chip driver is responsible for restoring the | |
180 | * register values used by the IRQ controller over suspend and resume. | |
181 | */ | |
182 | int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, | |
183 | int irq_base, struct regmap_irq_chip *chip, | |
184 | struct regmap_irq_chip_data **data) | |
185 | { | |
186 | struct regmap_irq_chip_data *d; | |
187 | int cur_irq, i; | |
188 | int ret = -ENOMEM; | |
189 | ||
f01ee60f SW |
190 | for (i = 0; i < chip->num_irqs; i++) { |
191 | if (chip->irqs[i].reg_offset % map->reg_stride) | |
192 | return -EINVAL; | |
193 | if (chip->irqs[i].reg_offset / map->reg_stride >= | |
194 | chip->num_regs) | |
195 | return -EINVAL; | |
196 | } | |
197 | ||
f8beab2b MB |
198 | irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); |
199 | if (irq_base < 0) { | |
200 | dev_warn(map->dev, "Failed to allocate IRQs: %d\n", | |
201 | irq_base); | |
202 | return irq_base; | |
203 | } | |
204 | ||
205 | d = kzalloc(sizeof(*d), GFP_KERNEL); | |
206 | if (!d) | |
207 | return -ENOMEM; | |
208 | ||
2431d0a1 MB |
209 | *data = d; |
210 | ||
f8beab2b MB |
211 | d->status_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, |
212 | GFP_KERNEL); | |
213 | if (!d->status_buf) | |
214 | goto err_alloc; | |
215 | ||
216 | d->status_reg_buf = kzalloc(map->format.val_bytes * chip->num_regs, | |
217 | GFP_KERNEL); | |
218 | if (!d->status_reg_buf) | |
219 | goto err_alloc; | |
220 | ||
221 | d->mask_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, | |
222 | GFP_KERNEL); | |
223 | if (!d->mask_buf) | |
224 | goto err_alloc; | |
225 | ||
226 | d->mask_buf_def = kzalloc(sizeof(unsigned int) * chip->num_regs, | |
227 | GFP_KERNEL); | |
228 | if (!d->mask_buf_def) | |
229 | goto err_alloc; | |
230 | ||
231 | d->map = map; | |
232 | d->chip = chip; | |
233 | d->irq_base = irq_base; | |
234 | mutex_init(&d->lock); | |
235 | ||
236 | for (i = 0; i < chip->num_irqs; i++) | |
f01ee60f | 237 | d->mask_buf_def[chip->irqs[i].reg_offset / map->reg_stride] |
f8beab2b MB |
238 | |= chip->irqs[i].mask; |
239 | ||
240 | /* Mask all the interrupts by default */ | |
241 | for (i = 0; i < chip->num_regs; i++) { | |
242 | d->mask_buf[i] = d->mask_buf_def[i]; | |
f01ee60f SW |
243 | ret = regmap_write(map, chip->mask_base + (i * map->reg_stride), |
244 | d->mask_buf[i]); | |
f8beab2b MB |
245 | if (ret != 0) { |
246 | dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", | |
f01ee60f | 247 | chip->mask_base + (i * map->reg_stride), ret); |
f8beab2b MB |
248 | goto err_alloc; |
249 | } | |
250 | } | |
251 | ||
252 | /* Register them with genirq */ | |
253 | for (cur_irq = irq_base; | |
254 | cur_irq < chip->num_irqs + irq_base; | |
255 | cur_irq++) { | |
256 | irq_set_chip_data(cur_irq, d); | |
257 | irq_set_chip_and_handler(cur_irq, ®map_irq_chip, | |
258 | handle_edge_irq); | |
259 | irq_set_nested_thread(cur_irq, 1); | |
260 | ||
261 | /* ARM needs us to explicitly flag the IRQ as valid | |
262 | * and will set them noprobe when we do so. */ | |
263 | #ifdef CONFIG_ARM | |
264 | set_irq_flags(cur_irq, IRQF_VALID); | |
265 | #else | |
266 | irq_set_noprobe(cur_irq); | |
267 | #endif | |
268 | } | |
269 | ||
270 | ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags, | |
271 | chip->name, d); | |
272 | if (ret != 0) { | |
273 | dev_err(map->dev, "Failed to request IRQ %d: %d\n", irq, ret); | |
274 | goto err_alloc; | |
275 | } | |
276 | ||
277 | return 0; | |
278 | ||
279 | err_alloc: | |
280 | kfree(d->mask_buf_def); | |
281 | kfree(d->mask_buf); | |
282 | kfree(d->status_reg_buf); | |
283 | kfree(d->status_buf); | |
284 | kfree(d); | |
285 | return ret; | |
286 | } | |
287 | EXPORT_SYMBOL_GPL(regmap_add_irq_chip); | |
288 | ||
289 | /** | |
290 | * regmap_del_irq_chip(): Stop interrupt handling for a regmap IRQ chip | |
291 | * | |
292 | * @irq: Primary IRQ for the device | |
293 | * @d: regmap_irq_chip_data allocated by regmap_add_irq_chip() | |
294 | */ | |
295 | void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) | |
296 | { | |
297 | if (!d) | |
298 | return; | |
299 | ||
300 | free_irq(irq, d); | |
301 | kfree(d->mask_buf_def); | |
302 | kfree(d->mask_buf); | |
303 | kfree(d->status_reg_buf); | |
304 | kfree(d->status_buf); | |
305 | kfree(d); | |
306 | } | |
307 | EXPORT_SYMBOL_GPL(regmap_del_irq_chip); | |
209a6006 MB |
308 | |
309 | /** | |
310 | * regmap_irq_chip_get_base(): Retrieve interrupt base for a regmap IRQ chip | |
311 | * | |
312 | * Useful for drivers to request their own IRQs. | |
313 | * | |
314 | * @data: regmap_irq controller to operate on. | |
315 | */ | |
316 | int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data) | |
317 | { | |
318 | return data->irq_base; | |
319 | } | |
320 | EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base); |