Skip to content

Commit

Permalink
soc: qcom: smsm: Fix missed interrupts if state changes while masked
Browse files Browse the repository at this point in the history
[ Upstream commit e3d4571 ]

The SMSM driver detects interrupt edges by tracking the last state
it has seen (and has triggered the interrupt handler for). This works
fine, but only if the interrupt does not change state while masked.

For example, if an interrupt is unmasked while the state is HIGH,
the stored last_value for that interrupt might still be LOW. Then,
when the remote processor triggers smsm_intr() we assume that nothing
has changed, even though the state might have changed from HIGH to LOW.

Attempt to fix this by checking the current remote state before
unmasking an IRQ. Use atomic operations to avoid the interrupt handler
from interfering with the unmask function.

This fixes modem crashes in some edge cases with the BAM-DMUX driver.
Specifically, the BAM-DMUX interrupt handler is not called for the
HIGH -> LOW smsm state transition if the BAM-DMUX driver is loaded
(and therefore unmasks the interrupt) after the modem was already started:

qcom-q6v5-mss 4080000.remoteproc: fatal error received: a2_task.c:3188:
  Assert FALSE failed: A2 DL PER deadlock timer expired waiting for Apps ACK

Fixes: c97c409 ("soc: qcom: smsm: Add driver for Qualcomm SMSM")
Signed-off-by: Stephan Gerhold <stephan@gerhold.net>
Link: https://lore.kernel.org/r/20210712135703.324748-2-stephan@gerhold.net
Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
stephan-gh authored and gregkh committed Sep 15, 2021
1 parent 8a77e3e commit 787f82d
Showing 1 changed file with 8 additions and 3 deletions.
11 changes: 8 additions & 3 deletions drivers/soc/qcom/smsm.c
Expand Up @@ -109,7 +109,7 @@ struct smsm_entry {
DECLARE_BITMAP(irq_enabled, 32);
DECLARE_BITMAP(irq_rising, 32);
DECLARE_BITMAP(irq_falling, 32);
u32 last_value;
unsigned long last_value;

u32 *remote_state;
u32 *subscription;
Expand Down Expand Up @@ -204,8 +204,7 @@ static irqreturn_t smsm_intr(int irq, void *data)
u32 val;

val = readl(entry->remote_state);
changed = val ^ entry->last_value;
entry->last_value = val;
changed = val ^ xchg(&entry->last_value, val);

for_each_set_bit(i, entry->irq_enabled, 32) {
if (!(changed & BIT(i)))
Expand Down Expand Up @@ -264,6 +263,12 @@ static void smsm_unmask_irq(struct irq_data *irqd)
struct qcom_smsm *smsm = entry->smsm;
u32 val;

/* Make sure our last cached state is up-to-date */
if (readl(entry->remote_state) & BIT(irq))
set_bit(irq, &entry->last_value);
else
clear_bit(irq, &entry->last_value);

set_bit(irq, entry->irq_enabled);

if (entry->subscription) {
Expand Down

0 comments on commit 787f82d

Please sign in to comment.