summaryrefslogtreecommitdiff
path: root/drivers/mfd
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/wcd9xxx-irq.c30
1 files changed, 25 insertions, 5 deletions
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
index 856179b96f37..43c6b0651064 100644
--- a/drivers/mfd/wcd9xxx-irq.c
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -290,7 +290,7 @@ static irqreturn_t wcd9xxx_irq_thread(int irq, void *data)
static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 1);
struct wcd9xxx_core_resource *wcd9xxx_res = data;
int num_irq_regs = wcd9xxx_res->num_irq_regs;
- u8 status[num_irq_regs], status1[num_irq_regs];
+ u8 status[4], status1[4] = {0}, unmask_status[4] = {0};
if (unlikely(wcd9xxx_lock_sleep(wcd9xxx_res) == false)) {
dev_err(wcd9xxx_res->dev, "Failed to hold suspend\n");
@@ -314,6 +314,23 @@ static irqreturn_t wcd9xxx_irq_thread(int irq, void *data)
"Failed to read interrupt status: %d\n", ret);
goto err_disable_irq;
}
+ /*
+ * If status is 0 return without clearing.
+ * status contains: HW status - masked interrupts
+ * status1 contains: unhandled interrupts - masked interrupts
+ * unmasked_status contains: unhandled interrupts
+ */
+ if (unlikely(!memcmp(status, status1, sizeof(status)))) {
+ pr_debug("%s: status is 0\n", __func__);
+ wcd9xxx_unlock_sleep(wcd9xxx_res);
+ return IRQ_HANDLED;
+ }
+
+ /*
+ * Copy status to unmask_status before masking, otherwise SW may miss
+ * to clear masked interrupt in corner case.
+ */
+ memcpy(unmask_status, status, sizeof(unmask_status));
/* Apply masking */
for (i = 0; i < num_irq_regs; i++)
@@ -337,6 +354,8 @@ static irqreturn_t wcd9xxx_irq_thread(int irq, void *data)
wcd9xxx_irq_dispatch(wcd9xxx_res, &irqdata);
status1[BIT_BYTE(irqdata.intr_num)] &=
~BYTE_BIT_MASK(irqdata.intr_num);
+ unmask_status[BIT_BYTE(irqdata.intr_num)] &=
+ ~BYTE_BIT_MASK(irqdata.intr_num);
}
}
@@ -358,12 +377,13 @@ static irqreturn_t wcd9xxx_irq_thread(int irq, void *data)
linebuf, sizeof(linebuf), false);
pr_warn("%s: status1 : %s\n", __func__, linebuf);
}
-
- memset(status, 0xff, num_irq_regs);
-
+ /*
+ * unmask_status contains unhandled interrupts, hence clear all
+ * unhandled interrupts.
+ */
ret = regmap_bulk_write(wcd9xxx_res->wcd_core_regmap,
wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE],
- status, num_irq_regs);
+ unmask_status, num_irq_regs);
if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
regmap_write(wcd9xxx_res->wcd_core_regmap,
wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT],