soundwire: bus: add helper to clear Slave status to UNATTACHED
authorPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Wed, 15 Jan 2020 00:08:40 +0000 (18:08 -0600)
committerVinod Koul <vkoul@kernel.org>
Tue, 25 Feb 2020 10:27:02 +0000 (15:57 +0530)
When resuming with a bus reset, we need to re-enumerate and restart
from UNATTACHED. The helper added in this patch helps implement a more
robust state machine avoiding race conditions on resume.

The unattach request is stored and will be used by Slave drivers, if
needed: Intel validation exposed a corner case where the Slave device
may transition to D3 when streaming stops, but streaming restarts
before the Master transitions to D3. In that case, the Slave status
was not cleared as UNATTACHED by the Master resuming, and the
wait_for_completion will time out.

When the slave resumes, it can check if a Master-initiated
re-enumeration and initialization took place and skip the
wait_for_completion() if there is no reason to wait.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20200115000844.14695-7-pierre-louis.bossart@linux.intel.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/soundwire/bus.c
drivers/soundwire/bus.h

index dfe27e3ca815b20f1fd52d486c1493d74dc81629..57dec61142e530168e9e2744e42bc06374fbf1bc 100644 (file)
@@ -1163,3 +1163,30 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
        return ret;
 }
 EXPORT_SYMBOL(sdw_handle_slave_status);
+
+void sdw_clear_slave_status(struct sdw_bus *bus, u32 request)
+{
+       struct sdw_slave *slave;
+       int i;
+
+       /* Check all non-zero devices */
+       for (i = 1; i <= SDW_MAX_DEVICES; i++) {
+               mutex_lock(&bus->bus_lock);
+               if (test_bit(i, bus->assigned) == false) {
+                       mutex_unlock(&bus->bus_lock);
+                       continue;
+               }
+               mutex_unlock(&bus->bus_lock);
+
+               slave = sdw_get_slave(bus, i);
+               if (!slave)
+                       continue;
+
+               if (slave->status != SDW_SLAVE_UNATTACHED)
+                       sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED);
+
+               /* keep track of request, used in pm_runtime resume */
+               slave->unattach_request = request;
+       }
+}
+EXPORT_SYMBOL(sdw_clear_slave_status);
index acb8d11a4c84fe11fe6d12945e8826e3fd1d1b3b..204204a26db8be1acfd8e18837f636fc99cb3df8 100644 (file)
@@ -165,4 +165,12 @@ sdw_update(struct sdw_slave *slave, u32 addr, u8 mask, u8 val)
        return sdw_write(slave, addr, tmp);
 }
 
+/*
+ * At the moment we only track Master-initiated hw_reset.
+ * Additional fields can be added as needed
+ */
+#define SDW_UNATTACH_REQUEST_MASTER_RESET      BIT(0)
+
+void sdw_clear_slave_status(struct sdw_bus *bus, u32 request);
+
 #endif /* __SDW_BUS_H */