summaryrefslogtreecommitdiff
path: root/drivers/base/regmap
diff options
context:
space:
mode:
authorBartosz Golaszewski <bgolaszewski@baylibre.com>2018-12-19 14:18:05 +0300
committerMark Brown <broonie@kernel.org>2018-12-19 21:38:13 +0300
commitc82ea33ead18801605b236523f21e5c893c7c253 (patch)
treeb4b523aac45485555343e4ff9133c529a6ecd38e /drivers/base/regmap
parent1c2928e3e3212252b505b746ec10951027a95813 (diff)
downloadlinux-c82ea33ead18801605b236523f21e5c893c7c253.tar.xz
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 <bgolaszewski@baylibre.com> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/base/regmap')
-rw-r--r--drivers/base/regmap/regmap-irq.c23
1 files changed, 23 insertions, 0 deletions
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;