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 | ||
