aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/base/regmap/regmap-irq.c97
-rw-r--r--drivers/mfd/Kconfig3
-rw-r--r--include/linux/regmap.h1
3 files changed, 74 insertions, 27 deletions
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 0d233cc1c7fa..db5305b37c9d 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -15,6 +15,7 @@
15#include <linux/regmap.h> 15#include <linux/regmap.h>
16#include <linux/irq.h> 16#include <linux/irq.h>
17#include <linux/interrupt.h> 17#include <linux/interrupt.h>
18#include <linux/irqdomain.h>
18#include <linux/slab.h> 19#include <linux/slab.h>
19 20
20#include "internal.h" 21#include "internal.h"
@@ -26,6 +27,7 @@ struct regmap_irq_chip_data {
26 struct regmap_irq_chip *chip; 27 struct regmap_irq_chip *chip;
27 28
28 int irq_base; 29 int irq_base;
30 struct irq_domain *domain;
29 31
30 void *status_reg_buf; 32 void *status_reg_buf;
31 unsigned int *status_buf; 33 unsigned int *status_buf;
@@ -37,7 +39,7 @@ static inline const
37struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data, 39struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data,
38 int irq) 40 int irq)
39{ 41{
40 return &data->chip->irqs[irq - data->irq_base]; 42 return &data->chip->irqs[irq];
41} 43}
42 44
43static void regmap_irq_lock(struct irq_data *data) 45static void regmap_irq_lock(struct irq_data *data)
@@ -74,7 +76,7 @@ static void regmap_irq_enable(struct irq_data *data)
74{ 76{
75 struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); 77 struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
76 struct regmap *map = d->map; 78 struct regmap *map = d->map;
77 const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); 79 const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
78 80
79 d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask; 81 d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask;
80} 82}
@@ -83,7 +85,7 @@ static void regmap_irq_disable(struct irq_data *data)
83{ 85{
84 struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); 86 struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
85 struct regmap *map = d->map; 87 struct regmap *map = d->map;
86 const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); 88 const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
87 89
88 d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; 90 d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask;
89} 91}
@@ -153,7 +155,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
153 for (i = 0; i < chip->num_irqs; i++) { 155 for (i = 0; i < chip->num_irqs; i++) {
154 if (data->status_buf[chip->irqs[i].reg_offset / 156 if (data->status_buf[chip->irqs[i].reg_offset /
155 map->reg_stride] & chip->irqs[i].mask) { 157 map->reg_stride] & chip->irqs[i].mask) {
156 handle_nested_irq(data->irq_base + i); 158 handle_nested_irq(irq_find_mapping(data->domain, i));
157 handled = true; 159 handled = true;
158 } 160 }
159 } 161 }
@@ -164,6 +166,31 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
164 return IRQ_NONE; 166 return IRQ_NONE;
165} 167}
166 168
169static int regmap_irq_map(struct irq_domain *h, unsigned int virq,
170 irq_hw_number_t hw)
171{
172 struct regmap_irq_chip_data *data = h->host_data;
173
174 irq_set_chip_data(virq, data);
175 irq_set_chip_and_handler(virq, &regmap_irq_chip, handle_edge_irq);
176 irq_set_nested_thread(virq, 1);
177
178 /* ARM needs us to explicitly flag the IRQ as valid
179 * and will set them noprobe when we do so. */
180#ifdef CONFIG_ARM
181 set_irq_flags(virq, IRQF_VALID);
182#else
183 irq_set_noprobe(virq);
184#endif
185
186 return 0;
187}
188
189static struct irq_domain_ops regmap_domain_ops = {
190 .map = regmap_irq_map,
191 .xlate = irq_domain_xlate_twocell,
192};
193
167/** 194/**
168 * regmap_add_irq_chip(): Use standard regmap IRQ controller handling 195 * regmap_add_irq_chip(): Use standard regmap IRQ controller handling
169 * 196 *
@@ -184,7 +211,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
184 struct regmap_irq_chip_data **data) 211 struct regmap_irq_chip_data **data)
185{ 212{
186 struct regmap_irq_chip_data *d; 213 struct regmap_irq_chip_data *d;
187 int cur_irq, i; 214 int i;
188 int ret = -ENOMEM; 215 int ret = -ENOMEM;
189 216
190 for (i = 0; i < chip->num_irqs; i++) { 217 for (i = 0; i < chip->num_irqs; i++) {
@@ -195,11 +222,13 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
195 return -EINVAL; 222 return -EINVAL;
196 } 223 }
197 224
198 irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); 225 if (irq_base) {
199 if (irq_base < 0) { 226 irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0);
200 dev_warn(map->dev, "Failed to allocate IRQs: %d\n", 227 if (irq_base < 0) {
201 irq_base); 228 dev_warn(map->dev, "Failed to allocate IRQs: %d\n",
202 return irq_base; 229 irq_base);
230 return irq_base;
231 }
203 } 232 }
204 233
205 d = kzalloc(sizeof(*d), GFP_KERNEL); 234 d = kzalloc(sizeof(*d), GFP_KERNEL);
@@ -249,33 +278,31 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
249 } 278 }
250 } 279 }
251 280
252 /* Register them with genirq */ 281 if (irq_base)
253 for (cur_irq = irq_base; 282 d->domain = irq_domain_add_legacy(map->dev->of_node,
254 cur_irq < chip->num_irqs + irq_base; 283 chip->num_irqs, irq_base, 0,
255 cur_irq++) { 284 &regmap_domain_ops, d);
256 irq_set_chip_data(cur_irq, d); 285 else
257 irq_set_chip_and_handler(cur_irq, &regmap_irq_chip, 286 d->domain = irq_domain_add_linear(map->dev->of_node,
258 handle_edge_irq); 287 chip->num_irqs,
259 irq_set_nested_thread(cur_irq, 1); 288 &regmap_domain_ops, d);
260 289 if (!d->domain) {
261 /* ARM needs us to explicitly flag the IRQ as valid 290 dev_err(map->dev, "Failed to create IRQ domain\n");
262 * and will set them noprobe when we do so. */ 291 ret = -ENOMEM;
263#ifdef CONFIG_ARM 292 goto err_alloc;
264 set_irq_flags(cur_irq, IRQF_VALID);
265#else
266 irq_set_noprobe(cur_irq);
267#endif
268 } 293 }
269 294
270 ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags, 295 ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags,
271 chip->name, d); 296 chip->name, d);
272 if (ret != 0) { 297 if (ret != 0) {
273 dev_err(map->dev, "Failed to request IRQ %d: %d\n", irq, ret); 298 dev_err(map->dev, "Failed to request IRQ %d: %d\n", irq, ret);
274 goto err_alloc; 299 goto err_domain;
275 } 300 }
276 301
277 return 0; 302 return 0;
278 303
304err_domain:
305 /* Should really dispose of the domain but... */
279err_alloc: 306err_alloc:
280 kfree(d->mask_buf_def); 307 kfree(d->mask_buf_def);
281 kfree(d->mask_buf); 308 kfree(d->mask_buf);
@@ -298,6 +325,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
298 return; 325 return;
299 326
300 free_irq(irq, d); 327 free_irq(irq, d);
328 /* We should unmap the domain but... */
301 kfree(d->mask_buf_def); 329 kfree(d->mask_buf_def);
302 kfree(d->mask_buf); 330 kfree(d->mask_buf);
303 kfree(d->status_reg_buf); 331 kfree(d->status_reg_buf);
@@ -315,6 +343,21 @@ EXPORT_SYMBOL_GPL(regmap_del_irq_chip);
315 */ 343 */
316int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data) 344int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data)
317{ 345{
346 WARN_ON(!data->irq_base);
318 return data->irq_base; 347 return data->irq_base;
319} 348}
320EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base); 349EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base);
350
351/**
352 * regmap_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ
353 *
354 * Useful for drivers to request their own IRQs.
355 *
356 * @data: regmap_irq controller to operate on.
357 * @irq: index of the interrupt requested in the chip IRQs
358 */
359int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq)
360{
361 return irq_create_mapping(data->domain, irq);
362}
363EXPORT_SYMBOL_GPL(regmap_irq_get_virq);
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 11e44386fa9b..697d686927f8 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -376,6 +376,7 @@ config PMIC_DA9052
376 376
377config MFD_DA9052_SPI 377config MFD_DA9052_SPI
378 bool "Support Dialog Semiconductor DA9052/53 PMIC variants with SPI" 378 bool "Support Dialog Semiconductor DA9052/53 PMIC variants with SPI"
379 select IRQ_DOMAIN
379 select REGMAP_SPI 380 select REGMAP_SPI
380 select REGMAP_IRQ 381 select REGMAP_IRQ
381 select PMIC_DA9052 382 select PMIC_DA9052
@@ -388,6 +389,7 @@ config MFD_DA9052_SPI
388 389
389config MFD_DA9052_I2C 390config MFD_DA9052_I2C
390 bool "Support Dialog Semiconductor DA9052/53 PMIC variants with I2C" 391 bool "Support Dialog Semiconductor DA9052/53 PMIC variants with I2C"
392 select IRQ_DOMAIN
391 select REGMAP_I2C 393 select REGMAP_I2C
392 select REGMAP_IRQ 394 select REGMAP_IRQ
393 select PMIC_DA9052 395 select PMIC_DA9052
@@ -558,6 +560,7 @@ config MFD_WM8994
558 bool "Support Wolfson Microelectronics WM8994" 560 bool "Support Wolfson Microelectronics WM8994"
559 select MFD_CORE 561 select MFD_CORE
560 select REGMAP_I2C 562 select REGMAP_I2C
563 select IRQ_DOMAIN
561 select REGMAP_IRQ 564 select REGMAP_IRQ
562 depends on I2C=y && GENERIC_HARDIRQS 565 depends on I2C=y && GENERIC_HARDIRQS
563 help 566 help
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 9dbc9a1bec43..720866887ae3 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -245,6 +245,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
245 struct regmap_irq_chip_data **data); 245 struct regmap_irq_chip_data **data);
246void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *data); 246void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *data);
247int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data); 247int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data);
248int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq);
248 249
249#else 250#else
250 251