diff options
-rw-r--r-- | drivers/base/regmap/regmap-irq.c | 125 | ||||
-rw-r--r-- | drivers/mfd/wm5102-tables.c | 1 | ||||
-rw-r--r-- | drivers/mfd/wm5110-tables.c | 1 | ||||
-rw-r--r-- | include/linux/regmap.h | 1 |
4 files changed, 102 insertions, 26 deletions
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 5972ad958544..4706c63d0bc6 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c | |||
@@ -34,6 +34,7 @@ struct regmap_irq_chip_data { | |||
34 | int irq; | 34 | int irq; |
35 | int wake_count; | 35 | int wake_count; |
36 | 36 | ||
37 | void *status_reg_buf; | ||
37 | unsigned int *status_buf; | 38 | unsigned int *status_buf; |
38 | unsigned int *mask_buf; | 39 | unsigned int *mask_buf; |
39 | unsigned int *mask_buf_def; | 40 | unsigned int *mask_buf_def; |
@@ -87,6 +88,23 @@ static void regmap_irq_sync_unlock(struct irq_data *data) | |||
87 | if (ret != 0) | 88 | if (ret != 0) |
88 | dev_err(d->map->dev, "Failed to sync masks in %x\n", | 89 | dev_err(d->map->dev, "Failed to sync masks in %x\n", |
89 | reg); | 90 | reg); |
91 | |||
92 | reg = d->chip->wake_base + | ||
93 | (i * map->reg_stride * d->irq_reg_stride); | ||
94 | if (d->wake_buf) { | ||
95 | if (d->chip->wake_invert) | ||
96 | ret = regmap_update_bits(d->map, reg, | ||
97 | d->mask_buf_def[i], | ||
98 | ~d->wake_buf[i]); | ||
99 | else | ||
100 | ret = regmap_update_bits(d->map, reg, | ||
101 | d->mask_buf_def[i], | ||
102 | d->wake_buf[i]); | ||
103 | if (ret != 0) | ||
104 | dev_err(d->map->dev, | ||
105 | "Failed to sync wakes in %x: %d\n", | ||
106 | reg, ret); | ||
107 | } | ||
90 | } | 108 | } |
91 | 109 | ||
92 | if (d->chip->runtime_pm) | 110 | if (d->chip->runtime_pm) |
@@ -129,16 +147,15 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on) | |||
129 | struct regmap *map = d->map; | 147 | struct regmap *map = d->map; |
130 | const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); | 148 | const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); |
131 | 149 | ||
132 | if (!d->chip->wake_base) | ||
133 | return -EINVAL; | ||
134 | |||
135 | if (on) { | 150 | if (on) { |
136 | d->wake_buf[irq_data->reg_offset / map->reg_stride] | 151 | if (d->wake_buf) |
137 | &= ~irq_data->mask; | 152 | d->wake_buf[irq_data->reg_offset / map->reg_stride] |
153 | &= ~irq_data->mask; | ||
138 | d->wake_count++; | 154 | d->wake_count++; |
139 | } else { | 155 | } else { |
140 | d->wake_buf[irq_data->reg_offset / map->reg_stride] | 156 | if (d->wake_buf) |
141 | |= irq_data->mask; | 157 | d->wake_buf[irq_data->reg_offset / map->reg_stride] |
158 | |= irq_data->mask; | ||
142 | d->wake_count--; | 159 | d->wake_count--; |
143 | } | 160 | } |
144 | 161 | ||
@@ -172,25 +189,69 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) | |||
172 | } | 189 | } |
173 | 190 | ||
174 | /* | 191 | /* |
175 | * Ignore masked IRQs and ack if we need to; we ack early so | 192 | * Read in the statuses, using a single bulk read if possible |
176 | * there is no race between handling and acknowleding the | 193 | * in order to reduce the I/O overheads. |
177 | * interrupt. We assume that typically few of the interrupts | ||
178 | * will fire simultaneously so don't worry about overhead from | ||
179 | * doing a write per register. | ||
180 | */ | 194 | */ |
181 | for (i = 0; i < data->chip->num_regs; i++) { | 195 | if (!map->use_single_rw && map->reg_stride == 1 && |
182 | ret = regmap_read(map, chip->status_base + (i * map->reg_stride | 196 | data->irq_reg_stride == 1) { |
183 | * data->irq_reg_stride), | 197 | u8 *buf8 = data->status_reg_buf; |
184 | &data->status_buf[i]); | 198 | u16 *buf16 = data->status_reg_buf; |
199 | u32 *buf32 = data->status_reg_buf; | ||
185 | 200 | ||
201 | BUG_ON(!data->status_reg_buf); | ||
202 | |||
203 | ret = regmap_bulk_read(map, chip->status_base, | ||
204 | data->status_reg_buf, | ||
205 | chip->num_regs); | ||
186 | if (ret != 0) { | 206 | if (ret != 0) { |
187 | dev_err(map->dev, "Failed to read IRQ status: %d\n", | 207 | dev_err(map->dev, "Failed to read IRQ status: %d\n", |
188 | ret); | 208 | ret); |
189 | if (chip->runtime_pm) | ||
190 | pm_runtime_put(map->dev); | ||
191 | return IRQ_NONE; | 209 | return IRQ_NONE; |
192 | } | 210 | } |
193 | 211 | ||
212 | for (i = 0; i < data->chip->num_regs; i++) { | ||
213 | switch (map->format.val_bytes) { | ||
214 | case 1: | ||
215 | data->status_buf[i] = buf8[i]; | ||
216 | break; | ||
217 | case 2: | ||
218 | data->status_buf[i] = buf16[i]; | ||
219 | break; | ||
220 | case 4: | ||
221 | data->status_buf[i] = buf32[i]; | ||
222 | break; | ||
223 | default: | ||
224 | BUG(); | ||
225 | return IRQ_NONE; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | } else { | ||
230 | for (i = 0; i < data->chip->num_regs; i++) { | ||
231 | ret = regmap_read(map, chip->status_base + | ||
232 | (i * map->reg_stride | ||
233 | * data->irq_reg_stride), | ||
234 | &data->status_buf[i]); | ||
235 | |||
236 | if (ret != 0) { | ||
237 | dev_err(map->dev, | ||
238 | "Failed to read IRQ status: %d\n", | ||
239 | ret); | ||
240 | if (chip->runtime_pm) | ||
241 | pm_runtime_put(map->dev); | ||
242 | return IRQ_NONE; | ||
243 | } | ||
244 | } | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * Ignore masked IRQs and ack if we need to; we ack early so | ||
249 | * there is no race between handling and acknowleding the | ||
250 | * interrupt. We assume that typically few of the interrupts | ||
251 | * will fire simultaneously so don't worry about overhead from | ||
252 | * doing a write per register. | ||
253 | */ | ||
254 | for (i = 0; i < data->chip->num_regs; i++) { | ||
194 | data->status_buf[i] &= ~data->mask_buf[i]; | 255 | data->status_buf[i] &= ~data->mask_buf[i]; |
195 | 256 | ||
196 | if (data->status_buf[i] && chip->ack_base) { | 257 | if (data->status_buf[i] && chip->ack_base) { |
@@ -316,11 +377,6 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, | |||
316 | 377 | ||
317 | d->irq_chip = regmap_irq_chip; | 378 | d->irq_chip = regmap_irq_chip; |
318 | d->irq_chip.name = chip->name; | 379 | d->irq_chip.name = chip->name; |
319 | if (!chip->wake_base) { | ||
320 | d->irq_chip.irq_set_wake = NULL; | ||
321 | d->irq_chip.flags |= IRQCHIP_MASK_ON_SUSPEND | | ||
322 | IRQCHIP_SKIP_SET_WAKE; | ||
323 | } | ||
324 | d->irq = irq; | 380 | d->irq = irq; |
325 | d->map = map; | 381 | d->map = map; |
326 | d->chip = chip; | 382 | d->chip = chip; |
@@ -331,6 +387,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, | |||
331 | else | 387 | else |
332 | d->irq_reg_stride = 1; | 388 | d->irq_reg_stride = 1; |
333 | 389 | ||
390 | if (!map->use_single_rw && map->reg_stride == 1 && | ||
391 | d->irq_reg_stride == 1) { | ||
392 | d->status_reg_buf = kmalloc(map->format.val_bytes * | ||
393 | chip->num_regs, GFP_KERNEL); | ||
394 | if (!d->status_reg_buf) | ||
395 | goto err_alloc; | ||
396 | } | ||
397 | |||
334 | mutex_init(&d->lock); | 398 | mutex_init(&d->lock); |
335 | 399 | ||
336 | for (i = 0; i < chip->num_irqs; i++) | 400 | for (i = 0; i < chip->num_irqs; i++) |
@@ -361,8 +425,15 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, | |||
361 | d->wake_buf[i] = d->mask_buf_def[i]; | 425 | d->wake_buf[i] = d->mask_buf_def[i]; |
362 | reg = chip->wake_base + | 426 | reg = chip->wake_base + |
363 | (i * map->reg_stride * d->irq_reg_stride); | 427 | (i * map->reg_stride * d->irq_reg_stride); |
364 | ret = regmap_update_bits(map, reg, d->wake_buf[i], | 428 | |
365 | d->wake_buf[i]); | 429 | if (chip->wake_invert) |
430 | ret = regmap_update_bits(map, reg, | ||
431 | d->mask_buf_def[i], | ||
432 | 0); | ||
433 | else | ||
434 | ret = regmap_update_bits(map, reg, | ||
435 | d->mask_buf_def[i], | ||
436 | d->wake_buf[i]); | ||
366 | if (ret != 0) { | 437 | if (ret != 0) { |
367 | dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", | 438 | dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", |
368 | reg, ret); | 439 | reg, ret); |
@@ -401,6 +472,7 @@ err_alloc: | |||
401 | kfree(d->mask_buf_def); | 472 | kfree(d->mask_buf_def); |
402 | kfree(d->mask_buf); | 473 | kfree(d->mask_buf); |
403 | kfree(d->status_buf); | 474 | kfree(d->status_buf); |
475 | kfree(d->status_reg_buf); | ||
404 | kfree(d); | 476 | kfree(d); |
405 | return ret; | 477 | return ret; |
406 | } | 478 | } |
@@ -422,6 +494,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) | |||
422 | kfree(d->wake_buf); | 494 | kfree(d->wake_buf); |
423 | kfree(d->mask_buf_def); | 495 | kfree(d->mask_buf_def); |
424 | kfree(d->mask_buf); | 496 | kfree(d->mask_buf); |
497 | kfree(d->status_reg_buf); | ||
425 | kfree(d->status_buf); | 498 | kfree(d->status_buf); |
426 | kfree(d); | 499 | kfree(d); |
427 | } | 500 | } |
diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 1133a64c2dc9..f6fcb87b3504 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c | |||
@@ -96,6 +96,7 @@ const struct regmap_irq_chip wm5102_aod = { | |||
96 | .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1, | 96 | .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1, |
97 | .ack_base = ARIZONA_AOD_IRQ1, | 97 | .ack_base = ARIZONA_AOD_IRQ1, |
98 | .wake_base = ARIZONA_WAKE_CONTROL, | 98 | .wake_base = ARIZONA_WAKE_CONTROL, |
99 | .wake_invert = 1, | ||
99 | .num_regs = 1, | 100 | .num_regs = 1, |
100 | .irqs = wm5102_aod_irqs, | 101 | .irqs = wm5102_aod_irqs, |
101 | .num_irqs = ARRAY_SIZE(wm5102_aod_irqs), | 102 | .num_irqs = ARRAY_SIZE(wm5102_aod_irqs), |
diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index adda6b10b90d..c41599815299 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c | |||
@@ -255,6 +255,7 @@ const struct regmap_irq_chip wm5110_aod = { | |||
255 | .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1, | 255 | .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1, |
256 | .ack_base = ARIZONA_AOD_IRQ1, | 256 | .ack_base = ARIZONA_AOD_IRQ1, |
257 | .wake_base = ARIZONA_WAKE_CONTROL, | 257 | .wake_base = ARIZONA_WAKE_CONTROL, |
258 | .wake_invert = 1, | ||
258 | .num_regs = 1, | 259 | .num_regs = 1, |
259 | .irqs = wm5110_aod_irqs, | 260 | .irqs = wm5110_aod_irqs, |
260 | .num_irqs = ARRAY_SIZE(wm5110_aod_irqs), | 261 | .num_irqs = ARRAY_SIZE(wm5110_aod_irqs), |
diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 7e2a48e25bd4..9e790f956025 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h | |||
@@ -398,6 +398,7 @@ struct regmap_irq_chip { | |||
398 | unsigned int wake_base; | 398 | unsigned int wake_base; |
399 | unsigned int irq_reg_stride; | 399 | unsigned int irq_reg_stride; |
400 | unsigned int mask_invert; | 400 | unsigned int mask_invert; |
401 | unsigned int wake_invert; | ||
401 | bool runtime_pm; | 402 | bool runtime_pm; |
402 | 403 | ||
403 | int num_regs; | 404 | int num_regs; |