Skip to content

Commit

Permalink
net: ipa: disable ipa interrupt during suspend
Browse files Browse the repository at this point in the history
[ Upstream commit 9ec9b2a ]

The IPA interrupt can fire when pm_runtime is disabled due to it racing
with the PM suspend/resume code. This causes a splat in the interrupt
handler when it tries to call pm_runtime_get().

Explicitly disable the interrupt in our ->suspend callback, and
re-enable it in ->resume to avoid this. If there is an interrupt pending
it will be handled after resuming. The interrupt is a wake_irq, as a
result even when disabled if it fires it will cause the system to wake
from suspend as well as cancel any suspend transition that may be in
progress. If there is an interrupt pending, the ipa_isr_thread handler
will be called after resuming.

Fixes: 1aac309 ("net: ipa: use autosuspend")
Signed-off-by: Caleb Connolly <caleb.connolly@linaro.org>
Reviewed-by: Alex Elder <elder@linaro.org>
Link: https://lore.kernel.org/r/20230115175925.465918-1-caleb.connolly@linaro.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
calebccff authored and gregkh committed Feb 1, 2023
1 parent 17511bd commit e401ecb
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 0 deletions.
10 changes: 10 additions & 0 deletions drivers/net/ipa/ipa_interrupt.c
Expand Up @@ -127,6 +127,16 @@ static irqreturn_t ipa_isr_thread(int irq, void *dev_id)
return IRQ_HANDLED;
}

void ipa_interrupt_irq_disable(struct ipa *ipa)
{
disable_irq(ipa->interrupt->irq);
}

void ipa_interrupt_irq_enable(struct ipa *ipa)
{
enable_irq(ipa->interrupt->irq);
}

/* Common function used to enable/disable TX_SUSPEND for an endpoint */
static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt,
u32 endpoint_id, bool enable)
Expand Down
16 changes: 16 additions & 0 deletions drivers/net/ipa/ipa_interrupt.h
Expand Up @@ -85,6 +85,22 @@ void ipa_interrupt_suspend_clear_all(struct ipa_interrupt *interrupt);
*/
void ipa_interrupt_simulate_suspend(struct ipa_interrupt *interrupt);

/**
* ipa_interrupt_irq_enable() - Enable IPA interrupts
* @ipa: IPA pointer
*
* This enables the IPA interrupt line
*/
void ipa_interrupt_irq_enable(struct ipa *ipa);

/**
* ipa_interrupt_irq_disable() - Disable IPA interrupts
* @ipa: IPA pointer
*
* This disables the IPA interrupt line
*/
void ipa_interrupt_irq_disable(struct ipa *ipa);

/**
* ipa_interrupt_config() - Configure the IPA interrupt framework
* @ipa: IPA pointer
Expand Down
17 changes: 17 additions & 0 deletions drivers/net/ipa/ipa_power.c
Expand Up @@ -181,6 +181,17 @@ static int ipa_suspend(struct device *dev)

__set_bit(IPA_POWER_FLAG_SYSTEM, ipa->power->flags);

/* Increment the disable depth to ensure that the IRQ won't
* be re-enabled until the matching _enable call in
* ipa_resume(). We do this to ensure that the interrupt
* handler won't run whilst PM runtime is disabled.
*
* Note that disabling the IRQ is NOT the same as disabling
* irq wake. If wakeup is enabled for the IPA then the IRQ
* will still cause the system to wake up, see irq_set_irq_wake().
*/
ipa_interrupt_irq_disable(ipa);

return pm_runtime_force_suspend(dev);
}

Expand All @@ -193,6 +204,12 @@ static int ipa_resume(struct device *dev)

__clear_bit(IPA_POWER_FLAG_SYSTEM, ipa->power->flags);

/* Now that PM runtime is enabled again it's safe
* to turn the IRQ back on and process any data
* that was received during suspend.
*/
ipa_interrupt_irq_enable(ipa);

return ret;
}

Expand Down

0 comments on commit e401ecb

Please sign in to comment.