@@ -5070,47 +5070,63 @@ static irqreturn_t rt5677_irq(int unused, void *data)
{
struct i2c_client *i2c = data;
struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
- int ret = 0, i, reg_irq, virq;
+ int ret = 0, loop, i, reg_irq, virq;
bool irq_fired;
mutex_lock(&rt5677->irq_lock);
- /* Read interrupt status */
- ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, ®_irq);
- if (ret) {
- dev_err(&i2c->dev,
- "Failed to read IRQ status: %d\n",
- ret);
- goto exit;
- }
/*
- * Clear the interrupt by flipping the polarity of the
- * interrupt source lines that just fired
+ * Loop to handle interrupts until the last i2c read shows no pending
+ * irqs. The interrupt line is shared by multiple interrupt sources.
+ * After the regmap_read() below, a new interrupt source line may
+ * become high before the regmap_write() finishes, so there isn't a
+ * rising edge on the shared interrupt line for the new interrupt. Thus,
+ * the loop is needed to avoid missing irqs.
+ *
+ * A safeguard of 20 loops is used to avoid hanging in the irq handler
+ * if there is something wrong with the interrupt status update. The
+ * interrupt sources here are audio jack plug/unplug events which
+ * shouldn't happen at a high frequency for a long period of time.
+ * Empirically, more than 3 loops have never been seen.
*/
- irq_fired = false;
- for (i = 0; i < RT5677_IRQ_NUM; i++) {
- if (reg_irq & rt5677_irq_descs[i].status_mask) {
- reg_irq ^= rt5677_irq_descs[i].polarity_mask;
- irq_fired = true;
+ for (loop = 0; loop < 20; loop++) {
+ /* Read interrupt status */
+ ret = regmap_read(rt5677->regmap, RT5677_IRQ_CTRL1, ®_irq);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "Failed to read IRQ status: %d\n",
+ ret);
+ goto exit;
+ }
+ /*
+ * Clear the interrupt by flipping the polarity of the
+ * interrupt source lines that just fired
+ */
+ irq_fired = false;
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if (reg_irq & rt5677_irq_descs[i].status_mask) {
+ reg_irq ^= rt5677_irq_descs[i].polarity_mask;
+ irq_fired = true;
+ }
+ }
+ if (!irq_fired)
+ goto exit;
+
+ ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
+ if (ret) {
+ dev_err(&i2c->dev,
+ "Failed to update IRQ status: %d\n",
+ ret);
+ goto exit;
}
- }
- if (!irq_fired)
- goto exit;
-
- ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
- if (ret) {
- dev_err(&i2c->dev,
- "Failed to update IRQ status: %d\n",
- ret);
- goto exit;
- }
- /* Process interrupts */
- for (i = 0; i < RT5677_IRQ_NUM; i++) {
- if ((reg_irq & rt5677_irq_descs[i].enable_mask) &&
- (reg_irq & rt5677_irq_descs[i].status_mask)) {
- virq = irq_find_mapping(rt5677->domain, i);
- if (virq)
- handle_nested_irq(virq);
+ /* Process interrupts */
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if ((reg_irq & rt5677_irq_descs[i].enable_mask) &&
+ (reg_irq & rt5677_irq_descs[i].status_mask)) {
+ virq = irq_find_mapping(rt5677->domain, i);
+ if (virq)
+ handle_nested_irq(virq);
+ }
}
}
exit: