From 43fac3238c1d9363b2a93d8d56c2be0c29c64e6c Mon Sep 17 00:00:00 2001 From: Tony Xie Date: Tue, 30 Oct 2018 18:07:56 +0800 Subject: [PATCH 1/5] regmap: add a new macro:REGMAP_IRQ_REG_LINE(_id, _reg_bits) if there are lots of irqs for a device and the register addresses for these irqs is continuous, we can use this macro to initialize regmap_irq value. Signed-off-by: Tony Xie Signed-off-by: Mark Brown --- include/linux/regmap.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a367d59c301d..3930f3331652 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -1110,6 +1110,12 @@ struct regmap_irq { #define REGMAP_IRQ_REG(_irq, _off, _mask) \ [_irq] = { .reg_offset = (_off), .mask = (_mask) } +#define REGMAP_IRQ_REG_LINE(_id, _reg_bits) \ + [_id] = { \ + .mask = BIT((_id) % (_reg_bits)), \ + .reg_offset = (_id) / (_reg_bits), \ + } + /** * struct regmap_irq_chip - Description of a generic regmap irq_chip. * From bc998a730367a69a1449320d321187d7414668fa Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 7 Dec 2018 14:04:52 +0100 Subject: [PATCH 2/5] regmap: irq: handle HW using separate rising/falling edge interrupts Some interrupt controllers use separate bits for controlling rising and falling edge interrupts in the mask register i.e. they have one interrupt for rising edge and one for falling. We already handle the case where we have a single interrupt in the mask register and a separate type configuration register. Add a new switch to regmap_irq_chip which tells the framework to use the mask_base address for configuring the edge of the interrupts that define type_falling/rising_mask values. For such interrupts we never update the type_base bits. For interrupts that don't define type masks or their regmap irq chip doesn't set the type_in_mask to true everything stays the same. Signed-off-by: Bartosz Golaszewski Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 64 ++++++++++++++++++++++---------- include/linux/regmap.h | 4 ++ 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 429ca8ed7e51..603b1554f81c 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -157,20 +157,23 @@ static void regmap_irq_sync_unlock(struct irq_data *data) } } - for (i = 0; i < d->chip->num_type_reg; i++) { - if (!d->type_buf_def[i]) - continue; - reg = d->chip->type_base + - (i * map->reg_stride * d->type_reg_stride); - if (d->chip->type_invert) - ret = regmap_irq_update_bits(d, reg, - d->type_buf_def[i], ~d->type_buf[i]); - else - ret = regmap_irq_update_bits(d, reg, - d->type_buf_def[i], d->type_buf[i]); - if (ret != 0) - dev_err(d->map->dev, "Failed to sync type in %x\n", - reg); + /* Don't update the type bits if we're using mask bits for irq type. */ + if (!d->chip->type_in_mask) { + for (i = 0; i < d->chip->num_type_reg; i++) { + if (!d->type_buf_def[i]) + continue; + reg = d->chip->type_base + + (i * map->reg_stride * d->type_reg_stride); + if (d->chip->type_invert) + ret = regmap_irq_update_bits(d, reg, + d->type_buf_def[i], ~d->type_buf[i]); + else + ret = regmap_irq_update_bits(d, reg, + d->type_buf_def[i], d->type_buf[i]); + if (ret != 0) + dev_err(d->map->dev, "Failed to sync type in %x\n", + reg); + } } if (d->chip->runtime_pm) @@ -194,8 +197,27 @@ static void regmap_irq_enable(struct irq_data *data) struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); struct regmap *map = d->map; const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); + unsigned int mask, type; - d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask; + type = irq_data->type_falling_mask | irq_data->type_rising_mask; + + /* + * The type_in_mask flag means that the underlying hardware uses + * separate mask bits for rising and falling edge interrupts, but + * we want to make them into a single virtual interrupt with + * configurable edge. + * + * If the interrupt we're enabling defines the falling or rising + * masks then instead of using the regular mask bits for this + * interrupt, use the value previously written to the type buffer + * at the corresponding offset in regmap_irq_set_type(). + */ + if (d->chip->type_in_mask && type) + mask = d->type_buf[irq_data->reg_offset / map->reg_stride]; + else + mask = irq_data->mask; + + d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~mask; } static void regmap_irq_disable(struct irq_data *data) @@ -430,6 +452,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, struct regmap_irq_chip_data *d; int i; int ret = -ENOMEM; + int num_type_reg; u32 reg; u32 unmask_offset; @@ -479,13 +502,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, goto err_alloc; } - if (chip->num_type_reg) { - d->type_buf_def = kcalloc(chip->num_type_reg, - sizeof(unsigned int), GFP_KERNEL); + num_type_reg = chip->type_in_mask ? chip->num_regs : chip->num_type_reg; + if (num_type_reg) { + d->type_buf_def = kcalloc(num_type_reg, + sizeof(unsigned int), GFP_KERNEL); if (!d->type_buf_def) goto err_alloc; - d->type_buf = kcalloc(chip->num_type_reg, sizeof(unsigned int), + d->type_buf = kcalloc(num_type_reg, sizeof(unsigned int), GFP_KERNEL); if (!d->type_buf) goto err_alloc; @@ -600,7 +624,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, } } - if (chip->num_type_reg) { + if (chip->num_type_reg && !chip->type_in_mask) { for (i = 0; i < chip->num_irqs; i++) { reg = chip->irqs[i].type_reg_offset / map->reg_stride; d->type_buf_def[reg] |= chip->irqs[i].type_rising_mask | diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 3930f3331652..c54c778f3051 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -1137,6 +1137,9 @@ struct regmap_irq { * @ack_invert: Inverted ack register: cleared bits for ack. * @wake_invert: Inverted wake register: cleared bits are wake enabled. * @type_invert: Invert the type flags. + * @type_in_mask: Use the mask registers for controlling irq type. For + * interrupts defining type_rising/falling_mask use mask_base + * for edge configuration and never update bits in type_base. * @runtime_pm: Hold a runtime PM lock on the device when accessing it. * * @num_regs: Number of registers in each control bank. @@ -1175,6 +1178,7 @@ struct regmap_irq_chip { bool wake_invert:1; bool runtime_pm:1; bool type_invert:1; + bool type_in_mask:1; int num_regs; From 84267d1b18abee0c141553396f52a23db71660d3 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 18 Dec 2018 12:58:13 +0200 Subject: [PATCH 3/5] regmap: regmap-irq: Remove default irq type setting from core The common code should not set IRQ type. Read HW defaults to the cache at startup instead of forcing type to EDGE_BOTH. If default setting is needed this should be done via normal mechanisms or by chip specific code if normal mechanisms are not suitable for some reason. Common regmap-irq code should not have defaults hard-coded but keep the HW/boot defaults untouched. Signed-off-by: Matti Vaittinen Tested-by: Bartosz Golaszewski Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 603b1554f81c..8b216b2e2c19 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -625,26 +625,20 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, } if (chip->num_type_reg && !chip->type_in_mask) { - for (i = 0; i < chip->num_irqs; i++) { - reg = chip->irqs[i].type_reg_offset / map->reg_stride; - d->type_buf_def[reg] |= chip->irqs[i].type_rising_mask | - chip->irqs[i].type_falling_mask; - } for (i = 0; i < chip->num_type_reg; ++i) { if (!d->type_buf_def[i]) continue; reg = chip->type_base + (i * map->reg_stride * d->type_reg_stride); - if (chip->type_invert) - ret = regmap_irq_update_bits(d, reg, - d->type_buf_def[i], 0xFF); - else - ret = regmap_irq_update_bits(d, reg, - d->type_buf_def[i], 0x0); - if (ret != 0) { - dev_err(map->dev, - "Failed to set type in 0x%x: %x\n", + + ret = regmap_read(map, reg, &d->type_buf_def[i]); + + if (d->chip->type_invert) + d->type_buf_def[i] = ~d->type_buf_def[i]; + + if (ret) { + dev_err(map->dev, "Failed to get type defaults at 0x%x: %d\n", reg, ret); goto err_alloc; } From 1c2928e3e3212252b505b746ec10951027a95813 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 18 Dec 2018 13:59:31 +0200 Subject: [PATCH 4/5] regmap: regmap-irq/gpio-max77620: add level-irq support Add level active IRQ support to regmap-irq irqchip. Change breaks existing regmap-irq type setting. Convert the existing drivers which use regmap-irq with trigger type setting (gpio-max77620) to work with this new approach. So we do not magically support level-active IRQs on gpio-max77620 - but add support to the regmap-irq for chips which support them =) We do not support distinguishing situation where HW supports rising and falling edge detection but not both. Separating this would require inventing yet another flags for IRQ types. Signed-off-by: Matti Vaittinen Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 35 ++++++++---- drivers/gpio/gpio-max77620.c | 96 +++++++++++++++++++++----------- include/linux/regmap.h | 27 +++++++-- 3 files changed, 110 insertions(+), 48 deletions(-) diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 8b216b2e2c19..31d23c9a5ae7 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -199,7 +199,7 @@ static void regmap_irq_enable(struct irq_data *data) const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); unsigned int mask, type; - type = irq_data->type_falling_mask | irq_data->type_rising_mask; + type = irq_data->type.type_falling_val | irq_data->type.type_rising_val; /* * The type_in_mask flag means that the underlying hardware uses @@ -234,27 +234,42 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type) struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); struct regmap *map = d->map; const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); - int reg = irq_data->type_reg_offset / map->reg_stride; + int reg; + const struct regmap_irq_type *t = &irq_data->type; - if (!(irq_data->type_rising_mask | irq_data->type_falling_mask)) - return 0; + if ((t->types_supported & type) != type) + return -ENOTSUPP; - d->type_buf[reg] &= ~(irq_data->type_falling_mask | - irq_data->type_rising_mask); + reg = t->type_reg_offset / map->reg_stride; + + if (t->type_reg_mask) + d->type_buf[reg] &= ~t->type_reg_mask; + else + d->type_buf[reg] &= ~(t->type_falling_val | + t->type_rising_val | + t->type_level_low_val | + t->type_level_high_val); switch (type) { case IRQ_TYPE_EDGE_FALLING: - d->type_buf[reg] |= irq_data->type_falling_mask; + d->type_buf[reg] |= t->type_falling_val; break; case IRQ_TYPE_EDGE_RISING: - d->type_buf[reg] |= irq_data->type_rising_mask; + d->type_buf[reg] |= t->type_rising_val; break; case IRQ_TYPE_EDGE_BOTH: - d->type_buf[reg] |= (irq_data->type_falling_mask | - irq_data->type_rising_mask); + d->type_buf[reg] |= (t->type_falling_val | + t->type_rising_val); break; + case IRQ_TYPE_LEVEL_HIGH: + d->type_buf[reg] |= t->type_level_high_val; + break; + + case IRQ_TYPE_LEVEL_LOW: + d->type_buf[reg] |= t->type_level_low_val; + break; default: return -EINVAL; } diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 538bce4b5b42..65fa3a198ebd 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -25,60 +25,92 @@ struct max77620_gpio { static const struct regmap_irq max77620_gpio_irqs[] = { [0] = { - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE0, - .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, .reg_offset = 0, - .type_reg_offset = 0, + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE0, + .type = { + .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, + .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, + .type_reg_offset = 0, + .types_supported = IRQ_TYPE_EDGE_BOTH, + }, }, [1] = { - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE1, - .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, .reg_offset = 0, - .type_reg_offset = 1, + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE1, + .type = { + .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, + .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, + .type_reg_offset = 1, + .types_supported = IRQ_TYPE_EDGE_BOTH, + }, }, [2] = { - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE2, - .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, .reg_offset = 0, - .type_reg_offset = 2, + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE2, + .type = { + .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, + .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, + .type_reg_offset = 2, + .types_supported = IRQ_TYPE_EDGE_BOTH, + }, }, [3] = { - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE3, - .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, .reg_offset = 0, - .type_reg_offset = 3, + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE3, + .type = { + .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, + .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, + .type_reg_offset = 3, + .types_supported = IRQ_TYPE_EDGE_BOTH, + }, }, [4] = { - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE4, - .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, .reg_offset = 0, - .type_reg_offset = 4, + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE4, + .type = { + .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, + .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, + .type_reg_offset = 4, + .types_supported = IRQ_TYPE_EDGE_BOTH, + }, }, [5] = { - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE5, - .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, .reg_offset = 0, - .type_reg_offset = 5, + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE5, + .type = { + .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, + .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, + .type_reg_offset = 5, + .types_supported = IRQ_TYPE_EDGE_BOTH, + }, }, [6] = { - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE6, - .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, .reg_offset = 0, - .type_reg_offset = 6, + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE6, + .type = { + .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, + .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, + .type_reg_offset = 6, + .types_supported = IRQ_TYPE_EDGE_BOTH, + }, }, [7] = { - .mask = MAX77620_IRQ_LVL2_GPIO_EDGE7, - .type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, - .type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING, .reg_offset = 0, - .type_reg_offset = 7, + .mask = MAX77620_IRQ_LVL2_GPIO_EDGE7, + .type = { + .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, + .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, + .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, + .type_reg_offset = 7, + .types_supported = IRQ_TYPE_EDGE_BOTH, + }, }, }; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index c54c778f3051..0f1832e4c2c8 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -1089,22 +1089,37 @@ int regmap_fields_read(struct regmap_field *field, unsigned int id, int regmap_fields_update_bits_base(struct regmap_field *field, unsigned int id, unsigned int mask, unsigned int val, bool *change, bool async, bool force); +/** + * struct regmap_irq_type - IRQ type definitions. + * + * @type_reg_offset: Offset register for the irq type setting. + * @type_rising_val: Register value to configure RISING type irq. + * @type_falling_val: Register value to configure FALLING type irq. + * @type_level_low_val: Register value to configure LEVEL_LOW type irq. + * @type_level_high_val: Register value to configure LEVEL_HIGH type irq. + * @types_supported: logical OR of IRQ_TYPE_* flags indicating supported types. + */ +struct regmap_irq_type { + unsigned int type_reg_offset; + unsigned int type_reg_mask; + unsigned int type_rising_val; + unsigned int type_falling_val; + unsigned int type_level_low_val; + unsigned int type_level_high_val; + unsigned int types_supported; +}; /** * struct regmap_irq - Description of an IRQ for the generic regmap irq_chip. * * @reg_offset: Offset of the status/mask register within the bank * @mask: Mask used to flag/control the register. - * @type_reg_offset: Offset register for the irq type setting. - * @type_rising_mask: Mask bit to configure RISING type irq. - * @type_falling_mask: Mask bit to configure FALLING type irq. + * @type: IRQ trigger type setting details if supported. */ struct regmap_irq { unsigned int reg_offset; unsigned int mask; - unsigned int type_reg_offset; - unsigned int type_rising_mask; - unsigned int type_falling_mask; + struct regmap_irq_type type; }; #define REGMAP_IRQ_REG(_irq, _off, _mask) \ From c82ea33ead18801605b236523f21e5c893c7c253 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 19 Dec 2018 12:18:05 +0100 Subject: [PATCH 5/5] regmap: irq: add an option to clear status registers on unmask Some interrupt controllers whose interrupts are acked on read will set the status bits for masked interrupts without changing the state of the IRQ line. Some chips have an additional "feature" where if those set bits are not cleared before unmasking their respective interrupts, the IRQ line will change the state and we'll interpret this as an interrupt although it actually fired when it was masked. Add a new field to the irq chip struct that tells the regmap irq chip code to always clear the status registers before actually changing the irq mask values. Signed-off-by: Bartosz Golaszewski Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 23 +++++++++++++++++++++++ include/linux/regmap.h | 4 ++++ 2 files changed, 27 insertions(+) diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 31d23c9a5ae7..1bd1145ad8b5 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -44,6 +44,8 @@ struct regmap_irq_chip_data { unsigned int irq_reg_stride; unsigned int type_reg_stride; + + bool clear_status:1; }; static inline const @@ -77,6 +79,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) int i, ret; u32 reg; u32 unmask_offset; + u32 val; if (d->chip->runtime_pm) { ret = pm_runtime_get_sync(map->dev); @@ -85,6 +88,20 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ret); } + if (d->clear_status) { + for (i = 0; i < d->chip->num_regs; i++) { + reg = d->chip->status_base + + (i * map->reg_stride * d->irq_reg_stride); + + ret = regmap_read(map, reg, &val); + if (ret) + dev_err(d->map->dev, + "Failed to clear the interrupt status bits\n"); + } + + d->clear_status = false; + } + /* * If there's been a change in the mask write it back to the * hardware. We rely on the use of the regmap core cache to @@ -217,6 +234,9 @@ static void regmap_irq_enable(struct irq_data *data) else mask = irq_data->mask; + if (d->chip->clear_on_unmask) + d->clear_status = true; + d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~mask; } @@ -474,6 +494,9 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, if (chip->num_regs <= 0) return -EINVAL; + if (chip->clear_on_unmask && (chip->ack_base || chip->use_ack)) + return -EINVAL; + for (i = 0; i < chip->num_irqs; i++) { if (chip->irqs[i].reg_offset % map->reg_stride) return -EINVAL; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 0f1832e4c2c8..1781b6cb793c 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -1155,6 +1155,9 @@ struct regmap_irq { * @type_in_mask: Use the mask registers for controlling irq type. For * interrupts defining type_rising/falling_mask use mask_base * for edge configuration and never update bits in type_base. + * @clear_on_unmask: For chips with interrupts cleared on read: read the status + * registers before unmasking interrupts to clear any bits + * set when they were masked. * @runtime_pm: Hold a runtime PM lock on the device when accessing it. * * @num_regs: Number of registers in each control bank. @@ -1194,6 +1197,7 @@ struct regmap_irq_chip { bool runtime_pm:1; bool type_invert:1; bool type_in_mask:1; + bool clear_on_unmask:1; int num_regs;