Commit 33e12f6e authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman
Browse files

Merge tag 'soundwire-5.7-rc1' of...

Merge tag 'soundwire-5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire into char-misc-next

Vinod writes:

soundwire updates for v5.7-rc1

This contains updates to stream and pm handling in the core as well as
updates to Intel drivers for hw sequencing and multi-link.

Details:
Core:
  - Updates to stream handling for state machine checks
  - Changes to handle potential races for probe/enumeration and init of the bus
  - Add no pm version of read and writes
  - Support for multiple Slave on same link
  - Add read_only_wordlength for simple/reduced ports

Intel:
  - Updates to cadence lib to handle hw sequencing
  - Support for audio dai calls in intel driver
  - Multi link support for cadence lib

Qualcomm:
  - Support for get_sdw_stream()

* tag 'soundwire-5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire: (43 commits)
  soundwire: qcom: add support for get_sdw_stream()
  soundwire: stream: Add read_only_wordlength flag to port properties
  soundwire: cadence: clear FIFO to avoid pop noise issue on playback start
  soundwire: cadence: multi-link support
  soundwire: cadence: commit changes in the exit_reset() sequence
  soundwire: cadence: remove automatic command retries
  soundwire: cadence: remove PREQ_DELAY assignment
  soundwire: cadence: enable NORMAL operation in cdns_init()
  soundwire: cadence: reorder MCP_CONFIG settings
  soundwire: cadence: make SSP interval programmable
  soundwire: cadence: move clock/SSP related inits to dedicated function
  soundwire: cadence: merge routines to clear/set bits
  soundwire: cadence: mask Slave interrupt before stopping clock
  soundwire: cadence: fix a io timeout issue in S3 test
  soundwire: cadence: add clock_stop/restart routines
  soundwire: cadence: handle error cases with CONFIG_UPDATE
  soundwire: cadence: add interface to check clock status
  soundwire: cadence: simplifiy cdns_init()
  soundwire: cadence: s/update_config/config_update
  soundwire: stream: use sdw_write instead of update
  ...
parents baca54d9 39ec6f99
Loading
Loading
Loading
Loading
+44 −17
Original line number Diff line number Diff line
@@ -156,22 +156,27 @@ Below shows the SoundWire stream states and state transition diagram. ::
	+-----------+     +------------+     +----------+     +----------+
	| ALLOCATED +---->| CONFIGURED +---->| PREPARED +---->| ENABLED  |
	|   STATE   |     |    STATE   |     |  STATE   |     |  STATE   |
	+-----------+     +------------+     +----------+     +----+-----+
	                                                           ^
	                                                           |
	                                                           |
	                                                           v
	         +----------+           +------------+        +----+-----+
	+-----------+     +------------+     +---+--+---+     +----+-----+
	                                         ^  ^              ^
				                 |  |              |
				               __|  |___________   |
				              |                 |  |
	                                      v                 |  v
	         +----------+           +-----+------+        +-+--+-----+
	         | RELEASED |<----------+ DEPREPARED |<-------+ DISABLED |
	         |  STATE   |           |   STATE    |        |  STATE   |
	         +----------+           +------------+        +----------+

NOTE: State transition between prepare and deprepare is supported in Spec
but not in the software (subsystem)
NOTE: State transitions between ``SDW_STREAM_ENABLED`` and
``SDW_STREAM_DISABLED`` are only relevant when then INFO_PAUSE flag is
supported at the ALSA/ASoC level. Likewise the transition between
``SDW_DISABLED_STATE`` and ``SDW_PREPARED_STATE`` depends on the
INFO_RESUME flag.

NOTE2: Stream state transition checks need to be handled by caller
framework, for example ALSA/ASoC. No checks for stream transition exist in
SoundWire subsystem.
NOTE2: The framework implements basic state transition checks, but
does not e.g. check if a transition from DISABLED to ENABLED is valid
on a specific platform. Such tests need to be added at the ALSA/ASoC
level.

Stream State Operations
-----------------------
@@ -246,6 +251,9 @@ SDW_STREAM_PREPARED

Prepare state of stream. Operations performed before entering in this state:

  (0) Steps 1 and 2 are omitted in the case of a resume operation,
      where the bus bandwidth is known.

  (1) Bus parameters such as bandwidth, frame shape, clock frequency,
      are computed based on current stream as well as already active
      stream(s) on Bus. Re-computation is required to accommodate current
@@ -270,9 +278,11 @@ Prepare state of stream. Operations performed before entering in this state:
After all above operations are successful, stream state is set to
``SDW_STREAM_PREPARED``.

Bus implements below API for PREPARE state which needs to be called once per
stream. From ASoC DPCM framework, this stream state is linked to
.prepare() operation.
Bus implements below API for PREPARE state which needs to be called
once per stream. From ASoC DPCM framework, this stream state is linked
to .prepare() operation. Since the .trigger() operations may not
follow the .prepare(), a direct transition from
``SDW_STREAM_PREPARED`` to ``SDW_STREAM_DEPREPARED`` is allowed.

.. code-block:: c

@@ -332,6 +342,14 @@ Bus implements below API for DISABLED state which needs to be called once
per stream. From ASoC DPCM framework, this stream state is linked to
.trigger() stop operation.

When the INFO_PAUSE flag is supported, a direct transition to
``SDW_STREAM_ENABLED`` is allowed.

For resume operations where ASoC will use the .prepare() callback, the
stream can transition from ``SDW_STREAM_DISABLED`` to
``SDW_STREAM_PREPARED``, with all required settings restored but
without updating the bandwidth and bit allocation.

.. code-block:: c

  int sdw_disable_stream(struct sdw_stream_runtime * stream);
@@ -353,9 +371,18 @@ state:
After all above operations are successful, stream state is set to
``SDW_STREAM_DEPREPARED``.

Bus implements below API for DEPREPARED state which needs to be called once
per stream. From ASoC DPCM framework, this stream state is linked to
.trigger() stop operation.
Bus implements below API for DEPREPARED state which needs to be called
once per stream. ALSA/ASoC do not have a concept of 'deprepare', and
the mapping from this stream state to ALSA/ASoC operation may be
implementation specific.

When the INFO_PAUSE flag is supported, the stream state is linked to
the .hw_free() operation - the stream is not deprepared on a
TRIGGER_STOP.

Other implementations may transition to the ``SDW_STREAM_DEPREPARED``
state on TRIGGER_STOP, should they require a transition through the
``SDW_STREAM_PREPARED`` state.

.. code-block:: c

+491 −46
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
// Copyright(c) 2015-17 Intel Corporation.

#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw_registers.h>
@@ -113,6 +114,8 @@ static int sdw_delete_slave(struct device *dev, void *data)
	struct sdw_slave *slave = dev_to_sdw_dev(dev);
	struct sdw_bus *bus = slave->bus;

	pm_runtime_disable(dev);

	sdw_slave_debugfs_exit(slave);

	mutex_lock(&bus->bus_lock);
@@ -317,6 +320,92 @@ int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave,
	return 0;
}

/*
 * Read/Write IO functions.
 * no_pm versions can only be called by the bus, e.g. while enumerating or
 * handling suspend-resume sequences.
 * all clients need to use the pm versions
 */

static int
sdw_nread_no_pm(struct sdw_slave *slave, u32 addr, size_t count, u8 *val)
{
	struct sdw_msg msg;
	int ret;

	ret = sdw_fill_msg(&msg, slave, addr, count,
			   slave->dev_num, SDW_MSG_FLAG_READ, val);
	if (ret < 0)
		return ret;

	return sdw_transfer(slave->bus, &msg);
}

static int
sdw_nwrite_no_pm(struct sdw_slave *slave, u32 addr, size_t count, u8 *val)
{
	struct sdw_msg msg;
	int ret;

	ret = sdw_fill_msg(&msg, slave, addr, count,
			   slave->dev_num, SDW_MSG_FLAG_WRITE, val);
	if (ret < 0)
		return ret;

	return sdw_transfer(slave->bus, &msg);
}

static int sdw_write_no_pm(struct sdw_slave *slave, u32 addr, u8 value)
{
	return sdw_nwrite_no_pm(slave, addr, 1, &value);
}

static int
sdw_bread_no_pm(struct sdw_bus *bus, u16 dev_num, u32 addr)
{
	struct sdw_msg msg;
	u8 buf;
	int ret;

	ret = sdw_fill_msg(&msg, NULL, addr, 1, dev_num,
			   SDW_MSG_FLAG_READ, &buf);
	if (ret)
		return ret;

	ret = sdw_transfer(bus, &msg);
	if (ret < 0)
		return ret;
	else
		return buf;
}

static int
sdw_bwrite_no_pm(struct sdw_bus *bus, u16 dev_num, u32 addr, u8 value)
{
	struct sdw_msg msg;
	int ret;

	ret = sdw_fill_msg(&msg, NULL, addr, 1, dev_num,
			   SDW_MSG_FLAG_WRITE, &value);
	if (ret)
		return ret;

	return sdw_transfer(bus, &msg);
}

static int
sdw_read_no_pm(struct sdw_slave *slave, u32 addr)
{
	u8 buf;
	int ret;

	ret = sdw_nread_no_pm(slave, addr, 1, &buf);
	if (ret < 0)
		return ret;
	else
		return buf;
}

/**
 * sdw_nread() - Read "n" contiguous SDW Slave registers
 * @slave: SDW Slave
@@ -326,19 +415,17 @@ int sdw_fill_msg(struct sdw_msg *msg, struct sdw_slave *slave,
 */
int sdw_nread(struct sdw_slave *slave, u32 addr, size_t count, u8 *val)
{
	struct sdw_msg msg;
	int ret;

	ret = sdw_fill_msg(&msg, slave, addr, count,
			   slave->dev_num, SDW_MSG_FLAG_READ, val);
	if (ret < 0)
		return ret;

	ret = pm_runtime_get_sync(slave->bus->dev);
	if (ret < 0)
	if (ret < 0 && ret != -EACCES) {
		pm_runtime_put_noidle(slave->bus->dev);
		return ret;
	}

	ret = sdw_nread_no_pm(slave, addr, count, val);

	ret = sdw_transfer(slave->bus, &msg);
	pm_runtime_mark_last_busy(slave->bus->dev);
	pm_runtime_put(slave->bus->dev);

	return ret;
@@ -354,19 +441,17 @@ EXPORT_SYMBOL(sdw_nread);
 */
int sdw_nwrite(struct sdw_slave *slave, u32 addr, size_t count, u8 *val)
{
	struct sdw_msg msg;
	int ret;

	ret = sdw_fill_msg(&msg, slave, addr, count,
			   slave->dev_num, SDW_MSG_FLAG_WRITE, val);
	if (ret < 0)
		return ret;

	ret = pm_runtime_get_sync(slave->bus->dev);
	if (ret < 0)
	if (ret < 0 && ret != -EACCES) {
		pm_runtime_put_noidle(slave->bus->dev);
		return ret;
	}

	ret = sdw_nwrite_no_pm(slave, addr, count, val);

	ret = sdw_transfer(slave->bus, &msg);
	pm_runtime_mark_last_busy(slave->bus->dev);
	pm_runtime_put(slave->bus->dev);

	return ret;
@@ -486,7 +571,7 @@ static int sdw_assign_device_num(struct sdw_slave *slave)
	dev_num = slave->dev_num;
	slave->dev_num = 0;

	ret = sdw_write(slave, SDW_SCP_DEVNUMBER, dev_num);
	ret = sdw_write_no_pm(slave, SDW_SCP_DEVNUMBER, dev_num);
	if (ret < 0) {
		dev_err(&slave->dev, "Program device_num %d failed: %d\n",
			dev_num, ret);
@@ -504,22 +589,11 @@ void sdw_extract_slave_id(struct sdw_bus *bus,
{
	dev_dbg(bus->dev, "SDW Slave Addr: %llx\n", addr);

	/*
	 * Spec definition
	 *   Register		Bit	Contents
	 *   DevId_0 [7:4]	47:44	sdw_version
	 *   DevId_0 [3:0]	43:40	unique_id
	 *   DevId_1		39:32	mfg_id [15:8]
	 *   DevId_2		31:24	mfg_id [7:0]
	 *   DevId_3		23:16	part_id [15:8]
	 *   DevId_4		15:08	part_id [7:0]
	 *   DevId_5		07:00	class_id
	 */
	id->sdw_version = (addr >> 44) & GENMASK(3, 0);
	id->unique_id = (addr >> 40) & GENMASK(3, 0);
	id->mfg_id = (addr >> 24) & GENMASK(15, 0);
	id->part_id = (addr >> 8) & GENMASK(15, 0);
	id->class_id = addr & GENMASK(7, 0);
	id->sdw_version = SDW_VERSION(addr);
	id->unique_id = SDW_UNIQUE_ID(addr);
	id->mfg_id = SDW_MFG_ID(addr);
	id->part_id = SDW_PART_ID(addr);
	id->class_id = SDW_CLASS_ID(addr);

	dev_dbg(bus->dev,
		"SDW Slave class_id %x, part_id %x, mfg_id %x, unique_id %x, version %x\n",
@@ -610,10 +684,320 @@ static void sdw_modify_slave_status(struct sdw_slave *slave,
				    enum sdw_slave_status status)
{
	mutex_lock(&slave->bus->bus_lock);

	dev_vdbg(&slave->dev,
		 "%s: changing status slave %d status %d new status %d\n",
		 __func__, slave->dev_num, slave->status, status);

	if (status == SDW_SLAVE_UNATTACHED) {
		dev_dbg(&slave->dev,
			"%s: initializing completion for Slave %d\n",
			__func__, slave->dev_num);

		init_completion(&slave->enumeration_complete);
		init_completion(&slave->initialization_complete);

	} else if ((status == SDW_SLAVE_ATTACHED) &&
		   (slave->status == SDW_SLAVE_UNATTACHED)) {
		dev_dbg(&slave->dev,
			"%s: signaling completion for Slave %d\n",
			__func__, slave->dev_num);

		complete(&slave->enumeration_complete);
	}
	slave->status = status;
	mutex_unlock(&slave->bus->bus_lock);
}

static enum sdw_clk_stop_mode sdw_get_clk_stop_mode(struct sdw_slave *slave)
{
	enum sdw_clk_stop_mode mode;

	/*
	 * Query for clock stop mode if Slave implements
	 * ops->get_clk_stop_mode, else read from property.
	 */
	if (slave->ops && slave->ops->get_clk_stop_mode) {
		mode = slave->ops->get_clk_stop_mode(slave);
	} else {
		if (slave->prop.clk_stop_mode1)
			mode = SDW_CLK_STOP_MODE1;
		else
			mode = SDW_CLK_STOP_MODE0;
	}

	return mode;
}

static int sdw_slave_clk_stop_callback(struct sdw_slave *slave,
				       enum sdw_clk_stop_mode mode,
				       enum sdw_clk_stop_type type)
{
	int ret;

	if (slave->ops && slave->ops->clk_stop) {
		ret = slave->ops->clk_stop(slave, mode, type);
		if (ret < 0) {
			dev_err(&slave->dev,
				"Clk Stop type =%d failed: %d\n", type, ret);
			return ret;
		}
	}

	return 0;
}

static int sdw_slave_clk_stop_prepare(struct sdw_slave *slave,
				      enum sdw_clk_stop_mode mode,
				      bool prepare)
{
	bool wake_en;
	u32 val = 0;
	int ret;

	wake_en = slave->prop.wake_capable;

	if (prepare) {
		val = SDW_SCP_SYSTEMCTRL_CLK_STP_PREP;

		if (mode == SDW_CLK_STOP_MODE1)
			val |= SDW_SCP_SYSTEMCTRL_CLK_STP_MODE1;

		if (wake_en)
			val |= SDW_SCP_SYSTEMCTRL_WAKE_UP_EN;
	} else {
		val = sdw_read_no_pm(slave, SDW_SCP_SYSTEMCTRL);

		val &= ~(SDW_SCP_SYSTEMCTRL_CLK_STP_PREP);
	}

	ret = sdw_write_no_pm(slave, SDW_SCP_SYSTEMCTRL, val);

	if (ret != 0)
		dev_err(&slave->dev,
			"Clock Stop prepare failed for slave: %d", ret);

	return ret;
}

static int sdw_bus_wait_for_clk_prep_deprep(struct sdw_bus *bus, u16 dev_num)
{
	int retry = bus->clk_stop_timeout;
	int val;

	do {
		val = sdw_bread_no_pm(bus, dev_num, SDW_SCP_STAT) &
			SDW_SCP_STAT_CLK_STP_NF;
		if (!val) {
			dev_info(bus->dev, "clock stop prep/de-prep done slave:%d",
				 dev_num);
			return 0;
		}

		usleep_range(1000, 1500);
		retry--;
	} while (retry);

	dev_err(bus->dev, "clock stop prep/de-prep failed slave:%d",
		dev_num);

	return -ETIMEDOUT;
}

/**
 * sdw_bus_prep_clk_stop: prepare Slave(s) for clock stop
 *
 * @bus: SDW bus instance
 *
 * Query Slave for clock stop mode and prepare for that mode.
 */
int sdw_bus_prep_clk_stop(struct sdw_bus *bus)
{
	enum sdw_clk_stop_mode slave_mode;
	bool simple_clk_stop = true;
	struct sdw_slave *slave;
	bool is_slave = false;
	int ret = 0;

	/*
	 * In order to save on transition time, prepare
	 * each Slave and then wait for all Slave(s) to be
	 * prepared for clock stop.
	 */
	list_for_each_entry(slave, &bus->slaves, node) {
		if (!slave->dev_num)
			continue;

		/* Identify if Slave(s) are available on Bus */
		is_slave = true;

		if (slave->status != SDW_SLAVE_ATTACHED &&
		    slave->status != SDW_SLAVE_ALERT)
			continue;

		slave_mode = sdw_get_clk_stop_mode(slave);
		slave->curr_clk_stop_mode = slave_mode;

		ret = sdw_slave_clk_stop_callback(slave, slave_mode,
						  SDW_CLK_PRE_PREPARE);
		if (ret < 0) {
			dev_err(&slave->dev,
				"pre-prepare failed:%d", ret);
			return ret;
		}

		ret = sdw_slave_clk_stop_prepare(slave,
						 slave_mode, true);
		if (ret < 0) {
			dev_err(&slave->dev,
				"pre-prepare failed:%d", ret);
			return ret;
		}

		if (slave_mode == SDW_CLK_STOP_MODE1)
			simple_clk_stop = false;
	}

	if (is_slave && !simple_clk_stop) {
		ret = sdw_bus_wait_for_clk_prep_deprep(bus,
						       SDW_BROADCAST_DEV_NUM);
		if (ret < 0)
			return ret;
	}

	/* Inform slaves that prep is done */
	list_for_each_entry(slave, &bus->slaves, node) {
		if (!slave->dev_num)
			continue;

		if (slave->status != SDW_SLAVE_ATTACHED &&
		    slave->status != SDW_SLAVE_ALERT)
			continue;

		slave_mode = slave->curr_clk_stop_mode;

		if (slave_mode == SDW_CLK_STOP_MODE1) {
			ret = sdw_slave_clk_stop_callback(slave,
							  slave_mode,
							  SDW_CLK_POST_PREPARE);

			if (ret < 0) {
				dev_err(&slave->dev,
					"post-prepare failed:%d", ret);
			}
		}
	}

	return ret;
}
EXPORT_SYMBOL(sdw_bus_prep_clk_stop);

/**
 * sdw_bus_clk_stop: stop bus clock
 *
 * @bus: SDW bus instance
 *
 * After preparing the Slaves for clock stop, stop the clock by broadcasting
 * write to SCP_CTRL register.
 */
int sdw_bus_clk_stop(struct sdw_bus *bus)
{
	int ret;

	/*
	 * broadcast clock stop now, attached Slaves will ACK this,
	 * unattached will ignore
	 */
	ret = sdw_bwrite_no_pm(bus, SDW_BROADCAST_DEV_NUM,
			       SDW_SCP_CTRL, SDW_SCP_CTRL_CLK_STP_NOW);
	if (ret < 0) {
		if (ret == -ENODATA)
			dev_dbg(bus->dev,
				"ClockStopNow Broadcast msg ignored %d", ret);
		else
			dev_err(bus->dev,
				"ClockStopNow Broadcast msg failed %d", ret);
		return ret;
	}

	return 0;
}
EXPORT_SYMBOL(sdw_bus_clk_stop);

/**
 * sdw_bus_exit_clk_stop: Exit clock stop mode
 *
 * @bus: SDW bus instance
 *
 * This De-prepares the Slaves by exiting Clock Stop Mode 0. For the Slaves
 * exiting Clock Stop Mode 1, they will be de-prepared after they enumerate
 * back.
 */
int sdw_bus_exit_clk_stop(struct sdw_bus *bus)
{
	enum sdw_clk_stop_mode mode;
	bool simple_clk_stop = true;
	struct sdw_slave *slave;
	bool is_slave = false;
	int ret;

	/*
	 * In order to save on transition time, de-prepare
	 * each Slave and then wait for all Slave(s) to be
	 * de-prepared after clock resume.
	 */
	list_for_each_entry(slave, &bus->slaves, node) {
		if (!slave->dev_num)
			continue;

		/* Identify if Slave(s) are available on Bus */
		is_slave = true;

		if (slave->status != SDW_SLAVE_ATTACHED &&
		    slave->status != SDW_SLAVE_ALERT)
			continue;

		mode = slave->curr_clk_stop_mode;

		if (mode == SDW_CLK_STOP_MODE1) {
			simple_clk_stop = false;
			continue;
		}

		ret = sdw_slave_clk_stop_callback(slave, mode,
						  SDW_CLK_PRE_DEPREPARE);
		if (ret < 0)
			dev_warn(&slave->dev,
				 "clk stop deprep failed:%d", ret);

		ret = sdw_slave_clk_stop_prepare(slave, mode,
						 false);

		if (ret < 0)
			dev_warn(&slave->dev,
				 "clk stop deprep failed:%d", ret);
	}

	if (is_slave && !simple_clk_stop)
		sdw_bus_wait_for_clk_prep_deprep(bus, SDW_BROADCAST_DEV_NUM);

	list_for_each_entry(slave, &bus->slaves, node) {
		if (!slave->dev_num)
			continue;

		if (slave->status != SDW_SLAVE_ATTACHED &&
		    slave->status != SDW_SLAVE_ALERT)
			continue;

		mode = slave->curr_clk_stop_mode;
		sdw_slave_clk_stop_callback(slave, mode,
					    SDW_CLK_POST_DEPREPARE);
	}

	return 0;
}
EXPORT_SYMBOL(sdw_bus_exit_clk_stop);

int sdw_configure_dpn_intr(struct sdw_slave *slave,
			   int port, bool enable, int mask)
{
@@ -672,13 +1056,10 @@ static int sdw_initialize_slave(struct sdw_slave *slave)
	val |= SDW_DP0_INT_PORT_READY | SDW_DP0_INT_BRA_FAILURE;

	ret = sdw_update(slave, SDW_DP0_INTMASK, val, val);
	if (ret < 0) {
	if (ret < 0)
		dev_err(slave->bus->dev,
			"SDW_DP0_INTMASK read failed:%d\n", ret);
		return val;
	}

	return 0;
	return ret;
}

static int sdw_handle_dp0_interrupt(struct sdw_slave *slave, u8 *slave_status)
@@ -831,12 +1212,19 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)

	sdw_modify_slave_status(slave, SDW_SLAVE_ALERT);

	ret = pm_runtime_get_sync(&slave->dev);
	if (ret < 0 && ret != -EACCES) {
		dev_err(&slave->dev, "Failed to resume device: %d\n", ret);
		pm_runtime_put_noidle(slave->bus->dev);
		return ret;
	}

	/* Read Instat 1, Instat 2 and Instat 3 registers */
	ret = sdw_read(slave, SDW_SCP_INT1);
	if (ret < 0) {
		dev_err(slave->bus->dev,
			"SDW_SCP_INT1 read failed:%d\n", ret);
		return ret;
		goto io_err;
	}
	buf = ret;

@@ -844,7 +1232,7 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
	if (ret < 0) {
		dev_err(slave->bus->dev,
			"SDW_SCP_INT2/3 read failed:%d\n", ret);
		return ret;
		goto io_err;
	}

	do {
@@ -924,7 +1312,7 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
		if (ret < 0) {
			dev_err(slave->bus->dev,
				"SDW_SCP_INT1 write failed:%d\n", ret);
			return ret;
			goto io_err;
		}

		/*
@@ -935,7 +1323,7 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
		if (ret < 0) {
			dev_err(slave->bus->dev,
				"SDW_SCP_INT1 read failed:%d\n", ret);
			return ret;
			goto io_err;
		}
		_buf = ret;

@@ -943,7 +1331,7 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
		if (ret < 0) {
			dev_err(slave->bus->dev,
				"SDW_SCP_INT2/3 read failed:%d\n", ret);
			return ret;
			goto io_err;
		}

		/* Make sure no interrupts are pending */
@@ -964,16 +1352,39 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
	if (count == SDW_READ_INTR_CLEAR_RETRY)
		dev_warn(slave->bus->dev, "Reached MAX_RETRY on alert read\n");

io_err:
	pm_runtime_mark_last_busy(&slave->dev);
	pm_runtime_put_autosuspend(&slave->dev);

	return ret;
}

static int sdw_update_slave_status(struct sdw_slave *slave,
				   enum sdw_slave_status status)
{
	if (slave->ops && slave->ops->update_status)
		return slave->ops->update_status(slave, status);
	unsigned long time;

	if (!slave->probed) {
		/*
		 * the slave status update is typically handled in an
		 * interrupt thread, which can race with the driver
		 * probe, e.g. when a module needs to be loaded.
		 *
		 * make sure the probe is complete before updating
		 * status.
		 */
		time = wait_for_completion_timeout(&slave->probe_complete,
				msecs_to_jiffies(DEFAULT_PROBE_TIMEOUT));
		if (!time) {
			dev_err(&slave->dev, "Probe not complete, timed out\n");
			return -ETIMEDOUT;
		}
	}

	if (!slave->ops || !slave->ops->update_status)
		return 0;

	return slave->ops->update_status(slave, status);
}

/**
@@ -986,6 +1397,7 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
{
	enum sdw_slave_status prev_status;
	struct sdw_slave *slave;
	bool attached_initializing;
	int i, ret = 0;

	/* first check if any Slaves fell off the bus */
@@ -1031,6 +1443,8 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
		if (!slave)
			continue;

		attached_initializing = false;

		switch (status[i]) {
		case SDW_SLAVE_UNATTACHED:
			if (slave->status == SDW_SLAVE_UNATTACHED)
@@ -1057,6 +1471,8 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
			if (prev_status == SDW_SLAVE_ALERT)
				break;

			attached_initializing = true;

			ret = sdw_initialize_slave(slave);
			if (ret)
				dev_err(bus->dev,
@@ -1075,8 +1491,37 @@ int sdw_handle_slave_status(struct sdw_bus *bus,
		if (ret)
			dev_err(slave->bus->dev,
				"Update Slave status failed:%d\n", ret);
		if (attached_initializing)
			complete(&slave->initialization_complete);
	}

	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);
+9 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
#define __SDW_BUS_H

#define DEFAULT_BANK_SWITCH_TIMEOUT 3000
#define DEFAULT_PROBE_TIMEOUT       2000

#if IS_ENABLED(CONFIG_ACPI)
int sdw_acpi_find_slaves(struct sdw_bus *bus);
@@ -164,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 */
+5 −0
Original line number Diff line number Diff line
@@ -110,6 +110,11 @@ static int sdw_drv_probe(struct device *dev)
	slave->bus->clk_stop_timeout = max_t(u32, slave->bus->clk_stop_timeout,
					     slave->prop.clk_stop_timeout);

	slave->probed = true;
	complete(&slave->probe_complete);

	dev_dbg(dev, "probe complete\n");

	return 0;
}

+240 −42

File changed.

Preview size limit exceeded, changes collapsed.

Loading