[PATCH 2/3] soundwire: intel/cadence: update hardware reset sequence

From: Bard Liao
Date: Wed May 17 2023 - 22:20:43 EST


From: Pierre-Louis Bossart <pierre-louis.bossart@xxxxxxxxxxxxxxx>

Combining hardware reset with the multi-link mode leads to a shortened
hardware reset pattern observed on the bus.

The updated hardware programming sequence is to first enable the clock
with the sync_arm/sync_go pattern, and only in a second step to issue
the hardware reset sequence. Since there is no longer a dependency
between sync_arm/sync_go and hw_reset, the behavior of
sdw_cdns_exit_reset() is changed to wait for the self-clearing
CONFIG_UPDATE to go back to zero,

Link: https://github.com/thesofproject/linux/issues/4170
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@xxxxxxxxxxxxxxx>
Reviewed-by: Rander Wang <rander.wang@xxxxxxxxx>
Signed-off-by: Bard Liao <yung-chuan.liao@xxxxxxxxxxxxxxx>
---
drivers/soundwire/cadence_master.c | 31 ++++++++++++++++++------
drivers/soundwire/cadence_master.h | 3 +++
drivers/soundwire/intel_bus_common.c | 36 ++++++++++++++++++++--------
3 files changed, 53 insertions(+), 17 deletions(-)

diff --git a/drivers/soundwire/cadence_master.c b/drivers/soundwire/cadence_master.c
index 39502bc75712..58686ae50bbf 100644
--- a/drivers/soundwire/cadence_master.c
+++ b/drivers/soundwire/cadence_master.c
@@ -283,6 +283,29 @@ static int cdns_config_update(struct sdw_cdns *cdns)
return ret;
}

+/**
+ * sdw_cdns_config_update() - Update configurations
+ * @cdns: Cadence instance
+ */
+void sdw_cdns_config_update(struct sdw_cdns *cdns)
+{
+ /* commit changes */
+ cdns_writel(cdns, CDNS_MCP_CONFIG_UPDATE, CDNS_MCP_CONFIG_UPDATE_BIT);
+}
+EXPORT_SYMBOL(sdw_cdns_config_update);
+
+/**
+ * sdw_cdns_config_update_set_wait() - wait until configuration update bit is self-cleared
+ * @cdns: Cadence instance
+ */
+int sdw_cdns_config_update_set_wait(struct sdw_cdns *cdns)
+{
+ /* the hardware recommendation is to wait at least 300us */
+ return cdns_set_wait(cdns, CDNS_MCP_CONFIG_UPDATE,
+ CDNS_MCP_CONFIG_UPDATE_BIT, 0);
+}
+EXPORT_SYMBOL(sdw_cdns_config_update_set_wait);
+
/*
* debugfs
*/
@@ -1116,13 +1139,7 @@ int sdw_cdns_exit_reset(struct sdw_cdns *cdns)
CDNS_MCP_CONTROL_HW_RST);

/* commit changes */
- cdns_updatel(cdns, CDNS_MCP_CONFIG_UPDATE,
- CDNS_MCP_CONFIG_UPDATE_BIT,
- CDNS_MCP_CONFIG_UPDATE_BIT);
-
- /* don't wait here */
- return 0;
-
+ return cdns_config_update(cdns);
}
EXPORT_SYMBOL(sdw_cdns_exit_reset);

diff --git a/drivers/soundwire/cadence_master.h b/drivers/soundwire/cadence_master.h
index 27c56274217f..8e328ba01c0a 100644
--- a/drivers/soundwire/cadence_master.h
+++ b/drivers/soundwire/cadence_master.h
@@ -197,4 +197,7 @@ int cdns_set_sdw_stream(struct snd_soc_dai *dai,
void sdw_cdns_check_self_clearing_bits(struct sdw_cdns *cdns, const char *string,
bool initial_delay, int reset_iterations);

+void sdw_cdns_config_update(struct sdw_cdns *cdns);
+int sdw_cdns_config_update_set_wait(struct sdw_cdns *cdns);
+
#endif /* __SDW_CADENCE_H */
diff --git a/drivers/soundwire/intel_bus_common.c b/drivers/soundwire/intel_bus_common.c
index 901d4f5736c1..be0c93106fc8 100644
--- a/drivers/soundwire/intel_bus_common.c
+++ b/drivers/soundwire/intel_bus_common.c
@@ -29,11 +29,7 @@ int intel_start_bus(struct sdw_intel *sdw)
return ret;
}

- ret = sdw_cdns_exit_reset(cdns);
- if (ret < 0) {
- dev_err(dev, "%s: unable to exit bus reset sequence: %d\n", __func__, ret);
- return ret;
- }
+ sdw_cdns_config_update(cdns);

if (bus->multi_link) {
ret = sdw_intel_sync_go(sdw);
@@ -43,6 +39,18 @@ int intel_start_bus(struct sdw_intel *sdw)
}
}

+ ret = sdw_cdns_config_update_set_wait(cdns);
+ if (ret < 0) {
+ dev_err(dev, "%s: CONFIG_UPDATE BIT still set\n", __func__);
+ return ret;
+ }
+
+ ret = sdw_cdns_exit_reset(cdns);
+ if (ret < 0) {
+ dev_err(dev, "%s: unable to exit bus reset sequence: %d\n", __func__, ret);
+ return ret;
+ }
+
ret = sdw_cdns_enable_interrupt(cdns, true);
if (ret < 0) {
dev_err(dev, "%s: cannot enable interrupts: %d\n", __func__, ret);
@@ -112,11 +120,7 @@ int intel_start_bus_after_reset(struct sdw_intel *sdw)
}

if (!clock_stop0) {
- ret = sdw_cdns_exit_reset(cdns);
- if (ret < 0) {
- dev_err(dev, "unable to exit bus reset sequence during resume\n");
- return ret;
- }
+ sdw_cdns_config_update(cdns);

if (bus->multi_link) {
ret = sdw_intel_sync_go(sdw);
@@ -126,6 +130,18 @@ int intel_start_bus_after_reset(struct sdw_intel *sdw)
}
}

+ ret = sdw_cdns_config_update_set_wait(cdns);
+ if (ret < 0) {
+ dev_err(dev, "%s: CONFIG_UPDATE BIT still set\n", __func__);
+ return ret;
+ }
+
+ ret = sdw_cdns_exit_reset(cdns);
+ if (ret < 0) {
+ dev_err(dev, "unable to exit bus reset sequence during resume\n");
+ return ret;
+ }
+
ret = sdw_cdns_enable_interrupt(cdns, true);
if (ret < 0) {
dev_err(dev, "cannot enable interrupts during resume\n");
--
2.25.1