Commit fbbc73a2 authored by Simon Trimmer's avatar Simon Trimmer Committed by Vinod Koul
Browse files

soundwire: cadence: fix updating slave status when a bus has multiple peripherals



The cadence IP explicitly reports slave status changes with bits for
each possible change. The function cdns_update_slave_status() attempts
to translate this into the current status of each of the slaves.

However when there are multiple peripherals on a bus any slave that did
not have a status change when the work function ran would not have it's
status updated - the array is initialised to a value that equates to
UNATTACHED and this can cause spurious reports that slaves had dropped
off the bus.

In the case where a slave has no status change or has multiple status
changes the value from the last PING command is used.

Signed-off-by: default avatarSimon Trimmer <simont@opensource.cirrus.com>
Signed-off-by: default avatarRichard Fitzgerald <rf@opensource.cirrus.com>
Reviewed-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20220914160248.1047627-2-rf@opensource.cirrus.com


Signed-off-by: default avatarVinod Koul <vkoul@kernel.org>
parent 279e46bc
Loading
Loading
Loading
Loading
+25 −32
Original line number Diff line number Diff line
@@ -782,6 +782,7 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
	enum sdw_slave_status status[SDW_MAX_DEVICES + 1];
	bool is_slave = false;
	u32 mask;
	u32 val;
	int i, set_status;

	memset(status, 0, sizeof(status));
@@ -789,12 +790,12 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
	for (i = 0; i <= SDW_MAX_DEVICES; i++) {
		mask = (slave_intstat >> (i * CDNS_MCP_SLAVE_STATUS_NUM)) &
			CDNS_MCP_SLAVE_STATUS_BITS;
		if (!mask)
			continue;

		is_slave = true;
		set_status = 0;

		if (mask) {
			is_slave = true;

			if (mask & CDNS_MCP_SLAVE_INTSTAT_RESERVED) {
				status[i] = SDW_SLAVE_RESERVED;
				set_status++;
@@ -814,16 +815,13 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
				status[i] = SDW_SLAVE_UNATTACHED;
				set_status++;
			}
		}

		/* first check if Slave reported multiple status */
		if (set_status > 1) {
			u32 val;

			dev_warn_ratelimited(cdns->dev,
					     "Slave %d reported multiple Status: %d\n",
					     i, mask);

			/* check latest status extracted from PING commands */
		/*
		 * check that there was a single reported Slave status and when
		 * there is not use the latest status extracted from PING commands
		 */
		if (set_status != 1) {
			val = cdns_readl(cdns, CDNS_MCP_SLAVE_STAT);
			val >>= (i * 2);

@@ -842,11 +840,6 @@ static int cdns_update_slave_status(struct sdw_cdns *cdns,
				status[i] = SDW_SLAVE_RESERVED;
				break;
			}

			dev_warn_ratelimited(cdns->dev,
					     "Slave %d status updated to %d\n",
					     i, status[i]);

		}
	}