linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
To: Vinod Koul <vinod.koul@intel.com>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: LKML <linux-kernel@vger.kernel.org>,
	ALSA <alsa-devel@alsa-project.org>, Mark <broonie@kernel.org>,
	Takashi <tiwai@suse.de>,
	patches.audio@intel.com, alan@linux.intel.com,
	Charles Keepax <ckeepax@opensource.cirrus.com>,
	Sagar Dharia <sdharia@codeaurora.org>,
	srinivas.kandagatla@linaro.org, plai@codeaurora.org,
	Sudheer Papothi <spapothi@codeaurora.org>
Subject: Re: [alsa-devel] [PATCH v4 09/15] soundwire: Add slave status handling
Date: Fri, 1 Dec 2017 17:52:03 -0600	[thread overview]
Message-ID: <693f15b3-3751-2416-5d81-abd362464309@linux.intel.com> (raw)
In-Reply-To: <1512122177-2889-10-git-send-email-vinod.koul@intel.com>

On 12/1/17 3:56 AM, Vinod Koul wrote:
> Add status handling API sdw_handle_slave_status() to handle
> Slave status changes.
> 
> Signed-off-by: Hardik T Shah <hardik.t.shah@intel.com>
> Signed-off-by: Sanyog Kale <sanyog.r.kale@intel.com>
> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
> ---
>   drivers/soundwire/bus.c       | 351 ++++++++++++++++++++++++++++++++++++++++++
>   drivers/soundwire/bus.h       |   2 +
>   include/linux/soundwire/sdw.h |  20 +++
>   3 files changed, 373 insertions(+)
> 
> diff --git a/drivers/soundwire/bus.c b/drivers/soundwire/bus.c
> index 09126ddd3cdd..c6a59a7a1306 100644
> --- a/drivers/soundwire/bus.c
> +++ b/drivers/soundwire/bus.c
> @@ -602,3 +602,354 @@ static int sdw_initialize_slave(struct sdw_slave *slave)
>   
>   	return 0;
>   }
> +
> +static int sdw_handle_dp0_interrupt(struct sdw_slave *slave, u8 *slave_status)
> +{
> +	u8 clear = 0, impl_int_mask;
> +	int status, ret, count = 0;
> +
> +	status = sdw_read(slave, SDW_DP0_INT);
> +	if (status < 0) {
> +		dev_err(slave->bus->dev,
> +				"SDW_DP0_INT read failed:%d", status);
> +		return status;
> +	}
> +
> +	do {
> +
> +		if (status & SDW_DP0_INT_TEST_FAIL) {
> +			dev_err(&slave->dev, "Test fail for port 0");
> +			clear |= SDW_DP0_INT_TEST_FAIL;
> +		}
> +
> +		/*
> +		 * Assumption: PORT_READY interrupt will be received only for
> +		 * ports implementing Channel Prepare state machine (CP_SM)
> +		 */
> +
> +		if (status & SDW_DP0_INT_PORT_READY) {
> +			complete(&slave->port_ready[0]);
> +			clear |= SDW_DP0_INT_PORT_READY;
> +		}
> +
> +		if (status & SDW_DP0_INT_BRA_FAILURE) {
> +			dev_err(&slave->dev, "BRA failed");
> +			clear |= SDW_DP0_INT_BRA_FAILURE;
> +		}
> +
> +		impl_int_mask = SDW_DP0_INT_IMPDEF1 |
> +			SDW_DP0_INT_IMPDEF2 | SDW_DP0_INT_IMPDEF3;
> +
> +		if (status & impl_int_mask) {
> +			clear |= impl_int_mask;
> +			*slave_status = clear;
> +		}
> +
> +		/* clear the interrupt */
> +		ret = sdw_write(slave, SDW_DP0_INT, clear);
> +		if (ret < 0) {
> +			dev_err(slave->bus->dev,
> +				"SDW_DP0_INT write failed:%d", ret);
> +			return ret;
> +		}
> +
> +		/* Read DP0 interrupt again */
> +		status = sdw_read(slave, SDW_DP0_INT);
> +		if (status < 0) {
> +			dev_err(slave->bus->dev,
> +				"SDW_DP0_INT read failed:%d", status);
> +			return status;
> +		}
> +
> +		count++;
> +
> +		/* we can get alerts while processing so keep retrying */

This is not incorrect, but this goes beyond what the spec requires.

The additional read is to make sure some interrupts are not lost due to 
a known race condition. It would be enough to mask the status read the 
second time to only check if the interrupts sources which were cleared 
are still signaling something.

With the code as it is, you may catch *new* interrupt sources, which 
could impact the arbitration/priority/policy in handling interrupts. 
It's not necessarily bad, but you'd need to document whether you want to 
deal with the race condition described in the MIPI spec or try to be 
smarter.


> +	} while (status != 0 && count < SDW_READ_INTR_CLEAR_RETRY);
> +
> +	if (count == SDW_READ_INTR_CLEAR_RETRY)
> +		dev_warn(slave->bus->dev, "Reached MAX_RETRY on DP0 read");
> +
> +	return ret;
> +}
> +
> +static int sdw_handle_port_interrupt(struct sdw_slave *slave,
> +		int port, u8 *slave_status)
> +{
> +	u8 clear = 0, impl_int_mask;
> +	int status, ret, count = 0;
> +	u32 addr;
> +
> +	if (port == 0)
> +		return sdw_handle_dp0_interrupt(slave, slave_status);
> +
> +	addr = SDW_DPN_INT(port);
> +	status = sdw_read(slave, addr);
> +	if (status < 0) {
> +		dev_err(slave->bus->dev,
> +				"SDW_DPN_INT read failed:%d", status);
> +
> +		return status;
> +	}
> +
> +	do {
> +
> +		if (status & SDW_DPN_INT_TEST_FAIL) {
> +			dev_err(&slave->dev, "Test fail for port:%d", port);
> +			clear |= SDW_DPN_INT_TEST_FAIL;
> +		}
> +
> +		/*
> +		 * Assumption: PORT_READY interrupt will be received only
> +		 * for ports implementing CP_SM.
> +		 */
> +		if (status & SDW_DPN_INT_PORT_READY) {
> +			complete(&slave->port_ready[port]);
> +			clear |= SDW_DPN_INT_PORT_READY;
> +		}
> +
> +		impl_int_mask = SDW_DPN_INT_IMPDEF1 |
> +			SDW_DPN_INT_IMPDEF2 | SDW_DPN_INT_IMPDEF3;
> +
> +
> +		if (status & impl_int_mask) {
> +			clear |= impl_int_mask;
> +			*slave_status = clear;
> +		}
> +
> +		/* clear the interrupt */
> +		ret = sdw_write(slave, addr, clear);
> +		if (ret < 0) {
> +			dev_err(slave->bus->dev,
> +					"SDW_DPN_INT write failed:%d", ret);
> +			return ret;
> +		}
> +
> +		/* Read DPN interrupt again */
> +		status = sdw_read(slave, addr);
> +		if (status < 0) {
> +			dev_err(slave->bus->dev,
> +					"SDW_DPN_INT read failed:%d", status);
> +			return status;
> +		}
> +
> +		count++;
> +
> +		/* we can get alerts while processing so keep retrying */
> +	} while (status != 0 && count < SDW_READ_INTR_CLEAR_RETRY);
> +
> +	if (count == SDW_READ_INTR_CLEAR_RETRY)
> +		dev_warn(slave->bus->dev, "Reached MAX_RETRY on port read");
> +
> +	return ret;
> +}
> +
> +static int sdw_handle_slave_alerts(struct sdw_slave *slave)
> +{
> +	u8 clear = 0, bit, port_status[15];
> +	int port_num, stat, ret, count = 0;
> +	unsigned long port;
> +	bool slave_notify = false;
> +	u8 buf[3];
> +
> +	sdw_modify_slave_status(slave, SDW_SLAVE_ALERT);
> +
> +	/* Read Instat 1, Instat 2 and Instat 3 registers */
> +	ret = sdw_nread(slave, SDW_SCP_INT1, 3, buf);
> +	if (ret < 0) {
> +		dev_err(slave->bus->dev,
> +					"SDW_SCP_INT1 read failed:%d", ret);
> +		return ret;
> +	}
> +
> +	do {
> +		/*
> +		 * Check parity, bus clash and Slave (impl defined)
> +		 * interrupt
> +		 */
> +		if (buf[0] & SDW_SCP_INT1_PARITY) {
> +			dev_err(&slave->dev, "Parity error detected");
> +			clear |= SDW_SCP_INT1_PARITY;
> +		}
> +
> +		if (buf[0] & SDW_SCP_INT1_BUS_CLASH) {
> +			dev_err(&slave->dev, "Bus clash error detected");
> +			clear |= SDW_SCP_INT1_BUS_CLASH;
> +		}
> +
> +		/*
> +		 * When bus clash or parity errors are detected, such errors
> +		 * are unlikely to be recoverable errors.
> +		 * TODO: In such scenario, reset bus. Make this configurable
> +		 * via sysfs property with bus reset being the default.
> +		 */
> +
> +		if (buf[0] & SDW_SCP_INT1_IMPL_DEF) {
> +			dev_dbg(&slave->dev, "Slave impl defined interrupt\n");
> +			clear |= SDW_SCP_INT1_IMPL_DEF;
> +			slave_notify = true;
> +		}
> +
> +		/* Check port 0 - 4 interrupts */
> +		port = buf[0] & SDW_SCP_INT1_PORT0_3;
> +
> +		/* To get port number corresponding to bits, shift it */
> +		port = port >> SDW_REG_SHIFT(SDW_SCP_INT1_PORT0_3);
> +		for_each_set_bit(bit, &port, 8) {
> +			sdw_handle_port_interrupt(slave, bit,
> +						&port_status[bit]);
> +
> +		}
> +
> +		/* Check if cascade 2 interrupt is present */
> +		if (buf[0] & SDW_SCP_INT1_SCP2_CASCADE) {
> +			port = buf[1] & SDW_SCP_INTSTAT2_PORT4_10;
> +			for_each_set_bit(bit, &port, 8) {
> +				/* scp2 ports start from 4 */
> +				port_num = bit + 3;
> +				sdw_handle_port_interrupt(slave,
> +						port_num,
> +						&port_status[port_num]);
> +			}
> +		}
> +
> +		/* now check last cascade */
> +		if (buf[1] & SDW_SCP_INTSTAT2_SCP3_CASCADE) {
> +			port = buf[2] & SDW_SCP_INTSTAT3_PORT11_14;
> +			for_each_set_bit(bit, &port, 8) {
> +				/* scp3 ports start from 11 */
> +				port_num = bit + 10;
> +				sdw_handle_port_interrupt(slave,
> +						port_num,
> +						&port_status[port_num]);
> +			}
> +		}
> +
> +		/* Update the Slave driver */
> +		if (slave_notify && (slave->ops) &&
> +					(slave->ops->interrupt_callback)) {
> +			struct sdw_slave_intr_status slave_intr;
> +
> +			slave_intr.control_port = clear;
> +			memcpy(slave_intr.port, &port_status,
> +						sizeof(slave_intr.port));
> +
> +			slave->ops->interrupt_callback(slave, &slave_intr);
> +		}
> +
> +		/* Ack interrupt */
> +		ret = sdw_write(slave, SDW_SCP_INT1, clear);
> +		if (ret < 0) {
> +			dev_err(slave->bus->dev,
> +					"SDW_SCP_INT1 write failed:%d", ret);
> +			return ret;
> +		}
> +
> +		/*
> +		 * Read status again to ensure no new interrupts arrived
> +		 * while servicing interrupts
> +		 */
> +		ret = sdw_nread(slave, SDW_SCP_INT1, 3, buf);
> +		if (ret < 0) {
> +			dev_err(slave->bus->dev,
> +					"SDW_SCP_INT1 read failed:%d", ret);
> +			return ret;
> +		}
> +
> +		/* Make sure no interrupts are pending */
> +		stat = buf[0] || buf[1] || buf[2];
> +
> +		/*
> +		 * Exit loop if Slave is continuously in ALERT state even
> +		 * after servicing the interrupt multiple times.
> +		 */
> +		count++;
> +
> +		/* we can get alerts while processing so keep retrying */
> +	} while (stat != 0 && count < SDW_READ_INTR_CLEAR_RETRY);

so that's a different case from what is done above, here you are making 
a conscious decision to re-read the interrupt status, it's got nothing 
to do with the spec requirements and you'd probably want a different 
RETRY value.

> +
> +	if (count == SDW_READ_INTR_CLEAR_RETRY)
> +		dev_warn(slave->bus->dev, "Reached MAX_RETRY on alert read");
> +
> +	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);
> +
> +	return 0;
> +}
> +
> +/**
> + * sdw_handle_slave_status() - Handle Slave status
> + * @bus: SDW bus instance
> + * @status: Status for all Slave(s)
> + */
> +int sdw_handle_slave_status(struct sdw_bus *bus,
> +			enum sdw_slave_status status[])
> +{
> +	struct sdw_slave *slave;
> +	int i, ret = 0;
> +
> +	if (status[0] == SDW_SLAVE_ATTACHED) {
> +		ret = sdw_program_device_num(bus);
> +		if (ret)
> +			dev_err(bus->dev, "Slave attach failed: %d", ret);
> +	}
> +
> +	/* Continue to check other slave statuses */
> +	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;
> +
> +		switch (status[i]) {
> +		case SDW_SLAVE_UNATTACHED:
> +			if (slave->status == SDW_SLAVE_UNATTACHED)
> +				break;
> +
> +			sdw_modify_slave_status(slave, SDW_SLAVE_UNATTACHED);
> +			break;
> +
> +		case SDW_SLAVE_ALERT:
> +			ret = sdw_handle_slave_alerts(slave);
> +			if (ret)
> +				dev_err(bus->dev,
> +					"Slave %d alert handling failed: %d",
> +					i, ret);
> +			break;
> +
> +		case SDW_SLAVE_ATTACHED:
> +			if (slave->status == SDW_SLAVE_ATTACHED)
> +				break;
> +
> +			sdw_initialize_slave(slave);
> +			sdw_modify_slave_status(slave, SDW_SLAVE_ATTACHED);
> +
> +			break;
> +
> +		default:
> +			dev_err(bus->dev, "Invalid slave %d status:%d",
> +							i, status[i]);
> +			break;
> +		}
> +
> +		ret = sdw_update_slave_status(slave, status[i]);
> +		if (ret)
> +			dev_err(slave->bus->dev,
> +				"Update Slave status failed:%d", ret);
> +
> +	}
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL(sdw_handle_slave_status);
> diff --git a/drivers/soundwire/bus.h b/drivers/soundwire/bus.h
> index b491e86f2c4c..463fecb02563 100644
> --- a/drivers/soundwire/bus.h
> +++ b/drivers/soundwire/bus.h
> @@ -45,6 +45,8 @@ struct sdw_msg {
>   	bool page;
>   };
>   
> +#define SDW_READ_INTR_CLEAR_RETRY	10
> +
>   int sdw_transfer(struct sdw_bus *bus, struct sdw_slave *slave,
>   			struct sdw_msg *msg);
>   int sdw_transfer_defer(struct sdw_bus *bus, struct sdw_slave *slave,
> diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h
> index 09619d042909..861a910911b6 100644
> --- a/include/linux/soundwire/sdw.h
> +++ b/include/linux/soundwire/sdw.h
> @@ -323,11 +323,28 @@ struct sdw_slave_id {
>   };
>   
>   /**
> + * struct sdw_slave_intr_status - Slave interrupt status
> + * @control_port: control port status
> + * @port: data port status
> + */
> +struct sdw_slave_intr_status {
> +	u8 control_port;
> +	u8 port[14];
> +};
> +
> +/**
>    * struct sdw_slave_ops - Slave driver callback ops
>    * @read_prop: Read Slave properties
> + * @interrupt_callback: Device interrupt notification (invoked in thread
> + * context)
> + * @update_status: Update Slave status
>    */
>   struct sdw_slave_ops {
>   	int (*read_prop)(struct sdw_slave *sdw);
> +	int (*interrupt_callback)(struct sdw_slave *slave,
> +			struct sdw_slave_intr_status *status);
> +	int (*update_status)(struct sdw_slave *slave,
> +			enum sdw_slave_status status);
>   };
>   
>   /**
> @@ -377,6 +394,9 @@ struct sdw_driver {
>   int sdw_handle_slave_status(struct sdw_bus *bus,
>   			enum sdw_slave_status status[]);
>   
> +int sdw_handle_slave_status(struct sdw_bus *bus,
> +			enum sdw_slave_status status[]);
> +
>   /*
>    * SDW master structures and APIs
>    */
> 

  reply	other threads:[~2017-12-01 23:52 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-12-01  9:56 [PATCH v4 00/15] soundwire: Add a new SoundWire subsystem Vinod Koul
2017-12-01  9:56 ` [PATCH v4 01/15] Documentation: Add SoundWire summary Vinod Koul
2017-12-01  9:56 ` [PATCH v4 02/15] soundwire: Add SoundWire bus type Vinod Koul
2017-12-01  9:56 ` [PATCH v4 03/15] soundwire: Add Master registration Vinod Koul
2017-12-01 22:10   ` [alsa-devel] " Pierre-Louis Bossart
2017-12-03 16:41     ` Vinod Koul
2017-12-04  2:44       ` Pierre-Louis Bossart
2017-12-04  2:59         ` Vinod Koul
2017-12-01  9:56 ` [PATCH v4 04/15] soundwire: Add MIPI DisCo property helpers Vinod Koul
2017-12-01 22:49   ` [alsa-devel] " Pierre-Louis Bossart
2017-12-03 16:52     ` Vinod Koul
2017-12-04  2:50       ` Pierre-Louis Bossart
2017-12-01  9:56 ` [PATCH v4 05/15] soundwire: Add SoundWire MIPI defined registers Vinod Koul
2017-12-01  9:56 ` [PATCH v4 06/15] soundwire: Add IO transfer Vinod Koul
2017-12-01 23:27   ` [alsa-devel] " Pierre-Louis Bossart
2017-12-03 17:04     ` Vinod Koul
2017-12-04  3:01       ` Pierre-Louis Bossart
2017-12-05  6:31         ` Vinod Koul
2017-12-05 13:43           ` Pierre-Louis Bossart
2017-12-05 14:48             ` Pierre-Louis Bossart
2017-12-06  5:58               ` Vinod Koul
2017-12-06 13:32                 ` Pierre-Louis Bossart
2017-12-06 14:44                   ` Vinod Koul
2017-12-01  9:56 ` [PATCH v4 07/15] regmap: Add SoundWire bus support Vinod Koul
2017-12-01  9:56 ` [PATCH v4 08/15] soundwire: Add Slave status handling helpers Vinod Koul
2017-12-01 23:36   ` [alsa-devel] " Pierre-Louis Bossart
2017-12-03 17:08     ` Vinod Koul
2017-12-04  3:07       ` Pierre-Louis Bossart
2017-12-04  3:13         ` Vinod Koul
2017-12-01  9:56 ` [PATCH v4 09/15] soundwire: Add slave status handling Vinod Koul
2017-12-01 23:52   ` Pierre-Louis Bossart [this message]
2017-12-03 17:11     ` [alsa-devel] " Vinod Koul
2017-12-04  3:11       ` Pierre-Louis Bossart
2017-12-04  3:21         ` Vinod Koul
2017-12-04  3:52           ` Pierre-Louis Bossart
2017-12-06  9:44             ` Vinod Koul
2017-12-01  9:56 ` [PATCH v4 10/15] soundwire: Add sysfs for SoundWire DisCo properties Vinod Koul
2017-12-01  9:56 ` [PATCH v4 11/15] soundwire: cdns: Add cadence library Vinod Koul
2017-12-01  9:56 ` [PATCH v4 12/15] soundwire: cdns: Add sdw_master_ops and IO transfer support Vinod Koul
2017-12-02  0:02   ` [alsa-devel] " Pierre-Louis Bossart
2017-12-03 17:10     ` Vinod Koul
2017-12-01  9:56 ` [PATCH v4 13/15] soundwire: intel: Add Intel Master driver Vinod Koul
2017-12-01  9:56 ` [PATCH v4 14/15] soundwire: intel: Add Intel init module Vinod Koul
2017-12-01  9:56 ` [PATCH v4 15/15] MAINTAINERS: Add SoundWire entry Vinod Koul
2017-12-02  0:24 ` [alsa-devel] [PATCH v4 00/15] soundwire: Add a new SoundWire subsystem Pierre-Louis Bossart
2017-12-03 17:12   ` Vinod Koul

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=693f15b3-3751-2416-5d81-abd362464309@linux.intel.com \
    --to=pierre-louis.bossart@linux.intel.com \
    --cc=alan@linux.intel.com \
    --cc=alsa-devel@alsa-project.org \
    --cc=broonie@kernel.org \
    --cc=ckeepax@opensource.cirrus.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=patches.audio@intel.com \
    --cc=plai@codeaurora.org \
    --cc=sdharia@codeaurora.org \
    --cc=spapothi@codeaurora.org \
    --cc=srinivas.kandagatla@linaro.org \
    --cc=tiwai@suse.de \
    --cc=vinod.koul@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).