Skip to content
Snippets Groups Projects
Commit 31c0eae0 authored by Tobias Kahlki's avatar Tobias Kahlki Committed by Tobias Kahlki
Browse files

[DRIVER][PCA953X] Workaround for int status race condition

Reading the input status register resets the interrupt status register.
If an interrupt from the GPIO-Expander to the SoC is issued and an additional
GPIO read occurs at the same time, the GPIO read might reset the interrupt
status register (by reading the input status register). The IRQ routine of
the GPIO-Expander is then unable to determine on which input an interrupt
occurred. To prevent this from happening, the interrupt status register is
read before each read of the input status register and the result is saved
for later processing. In the IRQ routine, the current interrupt status
register value is then combined with the stored value.
parent 6591dd8f
No related branches found
No related tags found
1 merge request!200Add drivers and devicetrees for Modular Vision with D18
......@@ -200,6 +200,7 @@ struct pca953x_chip {
DECLARE_BITMAP(irq_stat, MAX_LINE);
DECLARE_BITMAP(irq_trig_raise, MAX_LINE);
DECLARE_BITMAP(irq_trig_fall, MAX_LINE);
DECLARE_BITMAP(trigger_stat, MAX_LINE);
struct irq_chip irq_chip;
#endif
atomic_t wakeup_path;
......@@ -460,13 +461,16 @@ exit:
static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
DECLARE_BITMAP(trigger, MAX_LINE);
u8 inreg = pca953x_recalc_addr(chip, chip->regs->input, off);
u8 bit = BIT(off % BANK_SZ);
u32 reg_val;
int ret;
mutex_lock(&chip->i2c_lock);
ret = regmap_read(chip->regmap, inreg, &reg_val);
ret = pca953x_read_regs(chip, PCAL953X_INT_STAT, trigger);
if (!ret)
ret = regmap_read(chip->regmap, inreg, &reg_val);
mutex_unlock(&chip->i2c_lock);
if (ret < 0) {
/*
......@@ -478,6 +482,8 @@ static int pca953x_gpio_get_value(struct gpio_chip *gc, unsigned off)
return 0;
}
bitmap_or(chip->trigger_stat, chip->trigger_stat, trigger, gc->ngpio);
return !!(reg_val & bit);
}
......@@ -516,15 +522,20 @@ static int pca953x_gpio_get_multiple(struct gpio_chip *gc,
unsigned long *mask, unsigned long *bits)
{
struct pca953x_chip *chip = gpiochip_get_data(gc);
DECLARE_BITMAP(trigger, MAX_LINE);
DECLARE_BITMAP(reg_val, MAX_LINE);
int ret;
mutex_lock(&chip->i2c_lock);
ret = pca953x_read_regs(chip, chip->regs->input, reg_val);
ret = pca953x_read_regs(chip, PCAL953X_INT_STAT, trigger);
if (!ret)
ret = pca953x_read_regs(chip, chip->regs->input, reg_val);
mutex_unlock(&chip->i2c_lock);
if (ret)
return ret;
bitmap_or(chip->trigger_stat, chip->trigger_stat, trigger, gc->ngpio);
bitmap_replace(bits, bits, reg_val, mask, gc->ngpio);
return 0;
}
......@@ -741,6 +752,9 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin
if (ret)
return false;
bitmap_or(trigger, trigger, chip->trigger_stat, gc->ngpio);
bitmap_zero(chip->trigger_stat, gc->ngpio);
/* Check latched inputs and clear interrupt status */
ret = pca953x_read_regs(chip, chip->regs->input, cur_stat);
if (ret)
......@@ -870,6 +884,8 @@ static int pca953x_irq_setup(struct pca953x_chip *chip, int irq_base)
girq->threaded = true;
girq->first = irq_base; /* FIXME: get rid of this */
bitmap_zero(chip->trigger_stat, chip->gpio_chip.ngpio);
ret = devm_request_threaded_irq(&client->dev, client->irq,
NULL, pca953x_irq_handler,
IRQF_ONESHOT | IRQF_SHARED,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment