Skip to content

Commit ff56094

Browse files
plbossartvinodkoul
authored andcommitted
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 <[email protected]> Signed-off-by: Pierre-Louis Bossart <[email protected]> Signed-off-by: Bard Liao <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Vinod Koul <[email protected]>
1 parent caa15c8 commit ff56094

File tree

3 files changed

+65
-0
lines changed

3 files changed

+65
-0
lines changed

drivers/soundwire/cadence_master.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,49 @@ static void cdns_update_slave_status_work(struct work_struct *work)
935935

936936
}
937937

938+
/* paranoia check to make sure self-cleared bits are indeed cleared */
939+
void sdw_cdns_check_self_clearing_bits(struct sdw_cdns *cdns, const char *string,
940+
bool initial_delay, int reset_iterations)
941+
{
942+
u32 mcp_control;
943+
u32 mcp_config_update;
944+
int i;
945+
946+
if (initial_delay)
947+
usleep_range(1000, 1500);
948+
949+
mcp_control = cdns_readl(cdns, CDNS_MCP_CONTROL);
950+
951+
/* the following bits should be cleared immediately */
952+
if (mcp_control & CDNS_MCP_CONTROL_CMD_RST)
953+
dev_err(cdns->dev, "%s failed: MCP_CONTROL_CMD_RST is not cleared\n", string);
954+
if (mcp_control & CDNS_MCP_CONTROL_SOFT_RST)
955+
dev_err(cdns->dev, "%s failed: MCP_CONTROL_SOFT_RST is not cleared\n", string);
956+
if (mcp_control & CDNS_MCP_CONTROL_SW_RST)
957+
dev_err(cdns->dev, "%s failed: MCP_CONTROL_SW_RST is not cleared\n", string);
958+
if (mcp_control & CDNS_MCP_CONTROL_CLK_STOP_CLR)
959+
dev_err(cdns->dev, "%s failed: MCP_CONTROL_CLK_STOP_CLR is not cleared\n", string);
960+
mcp_config_update = cdns_readl(cdns, CDNS_MCP_CONFIG_UPDATE);
961+
if (mcp_config_update & CDNS_MCP_CONFIG_UPDATE_BIT)
962+
dev_err(cdns->dev, "%s failed: MCP_CONFIG_UPDATE_BIT is not cleared\n", string);
963+
964+
i = 0;
965+
while (mcp_control & CDNS_MCP_CONTROL_HW_RST) {
966+
if (i == reset_iterations) {
967+
dev_err(cdns->dev, "%s failed: MCP_CONTROL_HW_RST is not cleared\n", string);
968+
break;
969+
}
970+
971+
dev_dbg(cdns->dev, "%s: MCP_CONTROL_HW_RST is not cleared at iteration %d\n", string, i);
972+
i++;
973+
974+
usleep_range(1000, 1500);
975+
mcp_control = cdns_readl(cdns, CDNS_MCP_CONTROL);
976+
}
977+
978+
}
979+
EXPORT_SYMBOL(sdw_cdns_check_self_clearing_bits);
980+
938981
/*
939982
* init routines
940983
*/
@@ -1212,6 +1255,8 @@ int sdw_cdns_init(struct sdw_cdns *cdns)
12121255

12131256
cdns_init_clock_ctrl(cdns);
12141257

1258+
sdw_cdns_check_self_clearing_bits(cdns, __func__, false, 0);
1259+
12151260
/* reset msg_count to default value of FIFOLEVEL */
12161261
cdns->msg_count = cdns_readl(cdns, CDNS_MCP_FIFOLEVEL);
12171262

@@ -1396,6 +1441,8 @@ int sdw_cdns_clock_stop(struct sdw_cdns *cdns, bool block_wake)
13961441
struct sdw_slave *slave;
13971442
int ret;
13981443

1444+
sdw_cdns_check_self_clearing_bits(cdns, __func__, false, 0);
1445+
13991446
/* Check suspend status */
14001447
if (sdw_cdns_is_clock_stop(cdns)) {
14011448
dev_dbg(cdns->dev, "Clock is already stopped\n");

drivers/soundwire/cadence_master.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,8 @@ int cdns_bus_conf(struct sdw_bus *bus, struct sdw_bus_params *params);
184184

185185
int cdns_set_sdw_stream(struct snd_soc_dai *dai,
186186
void *stream, bool pcm, int direction);
187+
188+
void sdw_cdns_check_self_clearing_bits(struct sdw_cdns *cdns, const char *string,
189+
bool initial_delay, int reset_iterations);
190+
187191
#endif /* __SDW_CADENCE_H */

drivers/soundwire/intel.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "intel.h"
2424

2525
#define INTEL_MASTER_SUSPEND_DELAY_MS 3000
26+
#define INTEL_MASTER_RESET_ITERATIONS 10
2627

2728
/*
2829
* debug/config flags for the Intel SoundWire Master.
@@ -1467,6 +1468,8 @@ int intel_link_startup(struct auxiliary_device *auxdev)
14671468
goto err_interrupt;
14681469
}
14691470
}
1471+
sdw_cdns_check_self_clearing_bits(cdns, __func__,
1472+
true, INTEL_MASTER_RESET_ITERATIONS);
14701473

14711474
/* Register DAIs */
14721475
ret = intel_register_dai(sdw);
@@ -1783,6 +1786,8 @@ static int __maybe_unused intel_resume(struct device *dev)
17831786
return ret;
17841787
}
17851788
}
1789+
sdw_cdns_check_self_clearing_bits(cdns, __func__,
1790+
true, INTEL_MASTER_RESET_ITERATIONS);
17861791

17871792
/*
17881793
* after system resume, the pm_runtime suspend() may kick in
@@ -1867,6 +1872,9 @@ static int __maybe_unused intel_resume_runtime(struct device *dev)
18671872
return ret;
18681873
}
18691874
}
1875+
sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime TEARDOWN",
1876+
true, INTEL_MASTER_RESET_ITERATIONS);
1877+
18701878
} else if (clock_stop_quirks & SDW_INTEL_CLK_STOP_BUS_RESET) {
18711879
ret = intel_init(sdw);
18721880
if (ret) {
@@ -1940,6 +1948,9 @@ static int __maybe_unused intel_resume_runtime(struct device *dev)
19401948
}
19411949
}
19421950
}
1951+
sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime BUS_RESET",
1952+
true, INTEL_MASTER_RESET_ITERATIONS);
1953+
19431954
} else if (!clock_stop_quirks) {
19441955

19451956
clock_stop0 = sdw_cdns_is_clock_stop(&sdw->cdns);
@@ -1963,6 +1974,9 @@ static int __maybe_unused intel_resume_runtime(struct device *dev)
19631974
dev_err(dev, "unable to resume master during resume\n");
19641975
return ret;
19651976
}
1977+
1978+
sdw_cdns_check_self_clearing_bits(cdns, "intel_resume_runtime no_quirks",
1979+
true, INTEL_MASTER_RESET_ITERATIONS);
19661980
} else {
19671981
dev_err(dev, "%s clock_stop_quirks %x unsupported\n",
19681982
__func__, clock_stop_quirks);

0 commit comments

Comments
 (0)