diff options
-rw-r--r-- | drivers/base/regmap/regmap-irq.c | 97 | ||||
-rw-r--r-- | drivers/mfd/Kconfig | 3 | ||||
-rw-r--r-- | include/linux/regmap.h | 1 |
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 | |||
37 | struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data, | 39 | struct 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 | ||
43 | static void regmap_irq_lock(struct irq_data *data) | 45 | static 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 | ||
169 | static 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, ®map_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 | |||
189 | static 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 | ®map_domain_ops, d); |
256 | irq_set_chip_data(cur_irq, d); | 285 | else |
257 | irq_set_chip_and_handler(cur_irq, ®map_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 | ®map_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 | ||
304 | err_domain: | ||
305 | /* Should really dispose of the domain but... */ | ||
279 | err_alloc: | 306 | err_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 | */ |
316 | int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data) | 344 | int 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 | } |
320 | EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base); | 349 | EXPORT_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 | */ | ||
359 | int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq) | ||
360 | { | ||
361 | return irq_create_mapping(data->domain, irq); | ||
362 | } | ||
363 | EXPORT_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 | ||
377 | config MFD_DA9052_SPI | 377 | config 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 | ||
389 | config MFD_DA9052_I2C | 390 | config 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); |
246 | void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *data); | 246 | void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *data); |
247 | int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data); | 247 | int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data); |
248 | int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq); | ||
248 | 249 | ||
249 | #else | 250 | #else |
250 | 251 | ||