Skip to content

Commit

Permalink
regmap: irq: add an option to clear status registers on unmask
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Bartosz Golaszewski authored and Mark Brown committed Dec 19, 2018
1 parent 1c2928e commit c82ea33
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 0 deletions.
23 changes: 23 additions & 0 deletions drivers/base/regmap/regmap-irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions include/linux/regmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;

Expand Down

0 comments on commit c82ea33

Please sign in to comment.