[PATCH] soundwire: bus: conditionally recheck UNATTACHED status

From: Bard Liao
Date: Tue Aug 30 2022 - 03:38:11 EST


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

In configurations with two amplifiers on the same link, the Intel CI
reports occasional enumeration/initialization timeouts during
suspend-resume stress tests, where one of the two amplifiers becomes
UNATTACHED immediately after being enumerated. This problem was
reported both with Maxim and Realtek codecs, which pointed initially
to a problem with status handling on the Intel side.

The Cadence IP integrated on Intel platforms throws an interrupt when
the status changes, and the information is kept with sticky bits until
cleared. We initially added more checks to make sure the edge
detection did not miss any transition, but that did not improve the
results significantly.

With the recent addition of the read_ping_status() callback, we were
able to show that the status in sticky bits is shown as UNATTACHED
even though the PING frames show the problematic device as
ATTACHED. That completely breaks the entire logic where we assumed
that a peripheral would always re-attach as device0. The resume
timeouts make sense in that in those cases, the
enumeration/initialization never happens a second time.

One possible explanation is that this problem typically happens when a
bus clash is reported, so it could very well be that the detection is
fooled by a transient electrical issue or conflict between two
peripherals.

This patch conditionally double-checks the status reported in the
sticky bits with the actual PING frame status. If the peripheral
reports as attached in PING frames, the early detection based on
sticky bits is discarded.

Note that this patch only corrects issues of false positives on the
manager side.

If the peripheral lost and regain sync, then it would report as
attached on Device0. A peripheral that would not reset its dev_num
would not be compliant with the MIPI specification.

BugLink: https://github.com/thesofproject/linux/issues/3638
BugLink: https://github.com/thesofproject/linux/issues/3325
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>
---
Hi Vinod,

You will need the "ASoC/soundwire: log actual PING status on resume issues"
series which is applied at ASoC tree before appling this patch.

---
drivers/soundwire/bus.c | 19 +++++++++++++++++++
drivers/soundwire/intel.c | 1 +
include/linux/soundwire/sdw.h | 3 +++
3 files changed, 23 insertions(+)

diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
index 2772973eebb1..d0d486f07673 100644
--- a/drivers/soundwire/bus.c
+++ b/drivers/soundwire/bus.c
@@ -1767,6 +1767,25 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
slave->status != SDW_SLAVE_UNATTACHED) {
dev_warn(&slave->dev, "Slave %d state check1: UNATTACHED, status was %d\n",
i, slave->status);
+
+ if (bus->recheck_unattached && bus->ops->read_ping_status) {
+ u32 ping_status;
+
+ mutex_lock(&bus->msg_lock);
+
+ ping_status = bus->ops->read_ping_status(bus);
+
+ mutex_unlock(&bus->msg_lock);
+
+ ping_status >>= (i * 2);
+ ping_status &= 0x3;
+
+ if (ping_status != 0) {
+ dev_warn(&slave->dev, "Slave %d state in PING frame is %d, ignoring earlier detection\n",
+ i, ping_status);
+ continue;
+ }
+ }
sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED);
}
}
diff --git a/drivers/soundwire/intel.c b/drivers/soundwire/intel.c
index 25ec9c272239..0c6e674dbf85 100644
--- a/drivers/soundwire/intel.c
+++ b/drivers/soundwire/intel.c
@@ -1311,6 +1311,7 @@ static int intel_link_probe(struct auxiliary_device *auxdev,

bus->link_id = auxdev->id;
bus->dev_num_ida_min = INTEL_DEV_NUM_IDA_MIN;
+ bus->recheck_unattached = true;

sdw_cdns_probe(cdns);

diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
index a2b31d25ea27..51ac71984260 100644
--- a/include/linux/soundwire/sdw.h
+++ b/include/linux/soundwire/sdw.h
@@ -892,6 +892,8 @@ struct sdw_master_ops {
* @dev_num_ida_min: if set, defines the minimum values for the IDA
* used to allocate system-unique device numbers. This value needs to be
* identical across all SoundWire bus in the system.
+ * @recheck_unattached: if set, double-check UNATTACHED status changes
+ * by reading PING frame status.
*/
struct sdw_bus {
struct device *dev;
@@ -917,6 +919,7 @@ struct sdw_bus {
bool multi_link;
int hw_sync_min_links;
int dev_num_ida_min;
+ bool recheck_unattached;
};

int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
--
2.25.1