summaryrefslogtreecommitdiffstats
path: root/drivers/soundwire/intel.c
diff options
context:
space:
mode:
authorPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>2021-07-14 07:13:49 +0200
committerVinod Koul <vkoul@kernel.org>2021-08-02 05:49:45 +0200
commitff560946ef15fb05b18a660f6b25e9c26fe050e1 (patch)
treebd0df194b4ac7988c17d7e5308682482ea66cbd2 /drivers/soundwire/intel.c
parentsoundwire: dmi-quirks: add quirk for Intel 'Bishop County' NUC M15 (diff)
downloadlinux-ff560946ef15fb05b18a660f6b25e9c26fe050e1.tar.xz
linux-ff560946ef15fb05b18a660f6b25e9c26fe050e1.zip
soundwire: cadence: add paranoid check on self-clearing bits
The Cadence IP exposes a small number of self-clearing bits in the MCP_CONTROL and MCP_CONFIG_UPDATE registers. We currently do not check that those bits are indeed cleared, e.g. during resume operations. That could lead to resuming peripheral devices too early. In addition, if we happen to read these registers, update one of the fields and write the register back, we may be writing stale data that might have been cleared in hardware. These sort of race conditions could lead to e.g. doing a hw_reset twice or stopping a clock that just restarted. There is no clear way of avoiding these potential race conditions other than making sure that these registers fields are cleared before any read-modify-write sequence. If we detect this sort of errors, we only log them since there is no clear recovery possible. The only way out is likely to restart the IP with a suspend/resume cycle. Note that the checks are performed before updating the registers, as well as after the Intel 'sync go' sequence in multi-link mode. That should cover both the start and end of suspend/resume hardware configurations. The Multi-Master mode gates the configuration updates until the 'sync go' signal is asserted, so we only check on init and after the end of the 'sync go' sequence. The duration of the usleep_range() was defined by the GSYNC frequency used in multi-master mode. With a 4kHz frequency, any configuration change might be deferred by up to 250us. Extending the range to 1000-1500us should guarantee that the configuration change is completed without any significant impact on the overall resume time. Suggested-by: Bard Liao <yung-chuan.liao@linux.intel.com> Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com> Link: https://lore.kernel.org/r/20210714051349.13064-1-yung-chuan.liao@linux.intel.com Signed-off-by: Vinod Koul <vkoul@kernel.org>
Diffstat (limited to 'drivers/soundwire/intel.c')
-rw-r--r--drivers/soundwire/intel.c14
1 files changed, 14 insertions, 0 deletions
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
index c11e3d8cd308..9794bc222fb5 100644
--- a/drivers/soundwire/intel.c
+++ b/drivers/soundwire/intel.c
@@ -23,6 +23,7 @@
#include "intel.h"
#define INTEL_MASTER_SUSPEND_DELAY_MS 3000
+#define INTEL_MASTER_RESET_ITERATIONS 10
/*
* debug/config flags for the Intel SoundWire Master.
@@ -1467,6 +1468,8 @@ int intel_link_startup(struct auxiliary_device *auxdev)
goto err_interrupt;
}
}
+ sdw_cdns_check_self_clearing_bits(cdns, __func__,
+ true, INTEL_MASTER_RESET_ITERATIONS);
/* Register DAIs */
ret = intel_register_dai(sdw);
@@ -1783,6 +1786,8 @@ static int __maybe_unused intel_resume(struct device *dev)
return ret;
}
}
+ sdw_cdns_check_self_clearing_bits(cdns, __func__,
+ true, INTEL_MASTER_RESET_ITERATIONS);
/*
* after system resume, the pm_runtime suspend() may kick in
@@ -1867,6 +1872,9 @@ static int __maybe_unused intel_resume_runtime(struct device *dev)
return ret;
}
}
+ sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime TEARDOWN",
+ true, INTEL_MASTER_RESET_ITERATIONS);
+
} else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) {
ret = intel_init(sdw);
if (ret) {
@@ -1940,6 +1948,9 @@ static int __maybe_unused intel_resume_runtime(struct device *dev)
}
}
}
+ sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime BUS_RESET",
+ true, INTEL_MASTER_RESET_ITERATIONS);
+
} else if (!clock_stop_quirks) {
clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
@@ -1963,6 +1974,9 @@ static int __maybe_unused intel_resume_runtime(struct device *dev)
dev_err(dev, "unable to resume master during resume\n");
return ret;
}
+
+ sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime no_quirks",
+ true, INTEL_MASTER_RESET_ITERATIONS);
} else {
dev_err(dev, "%s clock_stop_quirks %x unsupported\n",
__func__, clock_stop_quirks);