Commit 60888ef8 authored by Pierre-Louis Bossart's avatar Pierre-Louis Bossart Committed by Mark Brown

ASoC: rt700-sdw: fix race condition on system suspend

In previous commits we cancelled deferred work, but there is still a
window of time where a new interrupt could result in new deferred work
executed after the link is disabled, leading to an IO error.

This patch uses an 'disable_irq_lock' mutex to prevent new interrupts
from happening after the start of the system suspend. The choice of a
mutex v. a spinlock is mainly due to the time required to clear
interrupts, which requires a command to be transmitted by the
SoundWire host IP and acknowledged with an interrupt. The
'interrupt_callback' routine is also not meant to be called from an
interrupt context.

An additional 'disable_irq' flag prevents race conditions where the
status changes before the interrupts are disabled, but the workqueue
handling status changes is scheduled after the completion of the
system suspend. On resume the interrupts are re-enabled already by the
io_init routine so we only clear the flag.

BugLink: https://github.com/thesofproject/linux/issues/2943
Fixes: 5f2df2a4 ('ASoC: rt700: wait for the delayed work to finish when the system suspends')
Signed-off-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: default avatarBard Liao <bard.liao@intel.com>
Reviewed-by: default avatarPéter Ujfalusi <peter.ujfalusi@linux.intel.com>
Link: https://lore.kernel.org/r/20210614180815.153711-3-pierre-louis.bossart@linux.intel.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent d38ebaf2
......@@ -418,10 +418,12 @@ static int rt700_interrupt_callback(struct sdw_slave *slave,
dev_dbg(&slave->dev,
"%s control_port_stat=%x", __func__, status->control_port);
if (status->control_port & 0x4) {
mutex_lock(&rt700->disable_irq_lock);
if (status->control_port & 0x4 && !rt700->disable_irq) {
mod_delayed_work(system_power_efficient_wq,
&rt700->jack_detect_work, msecs_to_jiffies(250));
}
mutex_unlock(&rt700->disable_irq_lock);
return 0;
}
......@@ -490,6 +492,34 @@ static int __maybe_unused rt700_dev_suspend(struct device *dev)
return 0;
}
static int __maybe_unused rt700_dev_system_suspend(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt700_priv *rt700 = dev_get_drvdata(dev);
int ret;
if (!rt700->hw_init)
return 0;
/*
* prevent new interrupts from being handled after the
* deferred work completes and before the parent disables
* interrupts on the link
*/
mutex_lock(&rt700->disable_irq_lock);
rt700->disable_irq = true;
ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1,
SDW_SCP_INT1_IMPL_DEF, 0);
mutex_unlock(&rt700->disable_irq_lock);
if (ret < 0) {
/* log but don't prevent suspend from happening */
dev_dbg(&slave->dev, "%s: could not disable imp-def interrupts\n:", __func__);
}
return rt700_dev_suspend(dev);
}
#define RT700_PROBE_TIMEOUT 5000
static int __maybe_unused rt700_dev_resume(struct device *dev)
......@@ -521,7 +551,7 @@ static int __maybe_unused rt700_dev_resume(struct device *dev)
}
static const struct dev_pm_ops rt700_pm = {
SET_SYSTEM_SLEEP_PM_OPS(rt700_dev_suspend, rt700_dev_resume)
SET_SYSTEM_SLEEP_PM_OPS(rt700_dev_system_suspend, rt700_dev_resume)
SET_RUNTIME_PM_OPS(rt700_dev_suspend, rt700_dev_resume, NULL)
};
......
......@@ -1112,6 +1112,8 @@ int rt700_init(struct device *dev, struct regmap *sdw_regmap,
rt700->sdw_regmap = sdw_regmap;
rt700->regmap = regmap;
mutex_init(&rt700->disable_irq_lock);
/*
* Mark hw_init to false
* HW init will be performed when device reports present
......@@ -1133,6 +1135,8 @@ int rt700_io_init(struct device *dev, struct sdw_slave *slave)
{
struct rt700_priv *rt700 = dev_get_drvdata(dev);
rt700->disable_irq = false;
if (rt700->hw_init)
return 0;
......
......@@ -23,6 +23,8 @@ struct rt700_priv {
struct delayed_work jack_detect_work;
struct delayed_work jack_btn_check_work;
int jack_type;
struct mutex disable_irq_lock; /* imp-def irq lock protection */
bool disable_irq;
};
struct sdw_stream_data {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment