ASoC: wcd9xxx: Fix MBHC irq handler deadlock scenario
There is a chance to encounter deadlock when either electrical mbhc irq
or mechanical mbhc irq releases resource lock to let polling thread finish
its job. This is due to the possibility of one irq handler can preempt
already running the other type of irq handler.
Introduce a new mutex to avoid this preemption.
CRs-fixed: 428653
Change-Id: Idf24c62224026c9c061de12c31e248c05b47de9d
Signed-off-by: Joonwoo Park <joonwoop@codeaurora.org>
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
index 4c40c9e..fde658b 100644
--- a/drivers/mfd/wcd9xxx-irq.c
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -151,21 +151,35 @@
}
EXPORT_SYMBOL_GPL(wcd9xxx_unlock_sleep);
+void wcd9xxx_nested_irq_lock(struct wcd9xxx *wcd9xxx)
+{
+ mutex_lock(&wcd9xxx->nested_irq_lock);
+}
+
+void wcd9xxx_nested_irq_unlock(struct wcd9xxx *wcd9xxx)
+{
+ mutex_unlock(&wcd9xxx->nested_irq_lock);
+}
+
static void wcd9xxx_irq_dispatch(struct wcd9xxx *wcd9xxx, int irqbit)
{
if ((irqbit <= TABLA_IRQ_MBHC_INSERTION) &&
(irqbit >= TABLA_IRQ_MBHC_REMOVAL)) {
+ wcd9xxx_nested_irq_lock(wcd9xxx);
wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
handle_nested_irq(wcd9xxx->irq_base + irqbit);
+ wcd9xxx_nested_irq_unlock(wcd9xxx);
} else {
+ wcd9xxx_nested_irq_lock(wcd9xxx);
handle_nested_irq(wcd9xxx->irq_base + irqbit);
wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_CLEAR0 +
BIT_BYTE(irqbit), BYTE_BIT_MASK(irqbit));
if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
wcd9xxx_reg_write(wcd9xxx, TABLA_A_INTR_MODE, 0x02);
+ wcd9xxx_nested_irq_unlock(wcd9xxx);
}
}
@@ -223,6 +237,7 @@
unsigned int i, cur_irq;
mutex_init(&wcd9xxx->irq_lock);
+ mutex_init(&wcd9xxx->nested_irq_lock);
if (!wcd9xxx->irq) {
dev_warn(wcd9xxx->dev,
@@ -235,6 +250,9 @@
dev_err(wcd9xxx->dev,
"No interrupt base specified, no interrupts\n");
return 0;
+ mutex_destroy(&wcd9xxx->nested_irq_lock);
+ mutex_destroy(&wcd9xxx->irq_lock);
+ mutex_destroy(&wcd9xxx->nested_irq_lock);
}
/* Mask the individual interrupt sources */
for (i = 0, cur_irq = wcd9xxx->irq_base; i < TABLA_NUM_IRQS; i++,
@@ -296,6 +314,7 @@
if (ret)
mutex_destroy(&wcd9xxx->irq_lock);
+ mutex_destroy(&wcd9xxx->nested_irq_lock);
return ret;
}
@@ -308,4 +327,5 @@
device_init_wakeup(wcd9xxx->dev, 0);
}
mutex_destroy(&wcd9xxx->irq_lock);
+ mutex_destroy(&wcd9xxx->nested_irq_lock);
}