Re: [PATCH] i2c: iproc: handle invalid slave state

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Roman,

On 8/24/2023 2:23 PM, roman.bacik@xxxxxxxxxxxx wrote:
> From: Roman Bacik <roman.bacik@xxxxxxxxxxxx>
> 
> Add the code to handle an invalid state when both bits S_RX_EVENT
> (indicating a transaction) and S_START_BUSY (indicating the end
> of transaction - transition of START_BUSY from 1 to 0) are set in
> the interrupt status register during a slave read.
> 
> Signed-off-by: Roman Bacik <roman.bacik@xxxxxxxxxxxx>
> Fixes: 1ca1b4516088 ("i2c: iproc: handle Master aborted error")
> ---
>  drivers/i2c/busses/i2c-bcm-iproc.c | 133 ++++++++++++++++-------------
>  1 file changed, 75 insertions(+), 58 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c
> index 05c80680dff4..68438d4e5d73 100644
> --- a/drivers/i2c/busses/i2c-bcm-iproc.c
> +++ b/drivers/i2c/busses/i2c-bcm-iproc.c
> @@ -316,26 +316,44 @@ static void bcm_iproc_i2c_slave_init(
>  	iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
>  }
>  
> -static void bcm_iproc_i2c_check_slave_status(
> -	struct bcm_iproc_i2c_dev *iproc_i2c)
> +static bool bcm_iproc_i2c_check_slave_status
> +	(struct bcm_iproc_i2c_dev *iproc_i2c, u32 status)
>  {
>  	u32 val;
> +	bool recover = false;
>  
> -	val = iproc_i2c_rd_reg(iproc_i2c, S_CMD_OFFSET);
> -	/* status is valid only when START_BUSY is cleared after it was set */
> -	if (val & BIT(S_CMD_START_BUSY_SHIFT))
> -		return;
> +	/* check slave transmit status only if slave is transmitting */
> +	if (!iproc_i2c->slave_rx_only) {
> +		val = iproc_i2c_rd_reg(iproc_i2c, S_CMD_OFFSET);
> +		/* status is valid only when START_BUSY is cleared */
> +		if (!(val & BIT(S_CMD_START_BUSY_SHIFT))) {
> +			val = (val >> S_CMD_STATUS_SHIFT) & S_CMD_STATUS_MASK;
> +			if (val == S_CMD_STATUS_TIMEOUT ||
> +			    val == S_CMD_STATUS_MASTER_ABORT) {
> +				dev_warn(iproc_i2c->device,
> +					 (val == S_CMD_STATUS_TIMEOUT) ?
> +					 "slave random stretch time timeout\n" :
> +					 "Master aborted read transaction\n");
> +				recover = true;
> +			}
> +		}
> +	}
> +
> +	/* RX_EVENT is not valid when START_BUSY is set */
> +	if ((status & BIT(IS_S_RX_EVENT_SHIFT)) &&
> +	    (status & BIT(IS_S_START_BUSY_SHIFT))) {
> +		dev_warn(iproc_i2c->device, "Slave aborted read transaction\n");
> +		recover = true;
> +	}
>  
> -	val = (val >> S_CMD_STATUS_SHIFT) & S_CMD_STATUS_MASK;
> -	if (val == S_CMD_STATUS_TIMEOUT || val == S_CMD_STATUS_MASTER_ABORT) {
> -		dev_err(iproc_i2c->device, (val == S_CMD_STATUS_TIMEOUT) ?
> -			"slave random stretch time timeout\n" :
> -			"Master aborted read transaction\n");
> +	if (recover) {
>  		/* re-initialize i2c for recovery */
>  		bcm_iproc_i2c_enable_disable(iproc_i2c, false);
>  		bcm_iproc_i2c_slave_init(iproc_i2c, true);
>  		bcm_iproc_i2c_enable_disable(iproc_i2c, true);
>  	}
> +
> +	return recover;
>  }
>  
>  static void bcm_iproc_i2c_slave_read(struct bcm_iproc_i2c_dev *iproc_i2c)
> @@ -420,48 +438,6 @@ static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c,
>  	u32 val;
>  	u8 value;
>  
> -	/*
> -	 * Slave events in case of master-write, master-write-read and,
> -	 * master-read
> -	 *
> -	 * Master-write     : only IS_S_RX_EVENT_SHIFT event
> -	 * Master-write-read: both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT
> -	 *                    events
> -	 * Master-read      : both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT
> -	 *                    events or only IS_S_RD_EVENT_SHIFT
> -	 *
> -	 * iproc has a slave rx fifo size of 64 bytes. Rx fifo full interrupt
> -	 * (IS_S_RX_FIFO_FULL_SHIFT) will be generated when RX fifo becomes
> -	 * full. This can happen if Master issues write requests of more than
> -	 * 64 bytes.
> -	 */
> -	if (status & BIT(IS_S_RX_EVENT_SHIFT) ||
> -	    status & BIT(IS_S_RD_EVENT_SHIFT) ||
> -	    status & BIT(IS_S_RX_FIFO_FULL_SHIFT)) {
> -		/* disable slave interrupts */
> -		val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
> -		val &= ~iproc_i2c->slave_int_mask;
> -		iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
> -
> -		if (status & BIT(IS_S_RD_EVENT_SHIFT))
> -			/* Master-write-read request */
> -			iproc_i2c->slave_rx_only = false;
> -		else
> -			/* Master-write request only */
> -			iproc_i2c->slave_rx_only = true;
> -
> -		/* schedule tasklet to read data later */
> -		tasklet_schedule(&iproc_i2c->slave_rx_tasklet);
> -
> -		/*
> -		 * clear only IS_S_RX_EVENT_SHIFT and
> -		 * IS_S_RX_FIFO_FULL_SHIFT interrupt.
> -		 */
> -		val = BIT(IS_S_RX_EVENT_SHIFT);
> -		if (status & BIT(IS_S_RX_FIFO_FULL_SHIFT))
> -			val |= BIT(IS_S_RX_FIFO_FULL_SHIFT);
> -		iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, val);
> -	}
>  
>  	if (status & BIT(IS_S_TX_UNDERRUN_SHIFT)) {
>  		iproc_i2c->tx_underrun++;
> @@ -493,8 +469,9 @@ static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c,
>  		 * less than PKT_LENGTH bytes were output on the SMBUS
>  		 */
>  		iproc_i2c->slave_int_mask &= ~BIT(IE_S_TX_UNDERRUN_SHIFT);
> -		iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET,
> -				 iproc_i2c->slave_int_mask);
> +		val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
> +		val &= ~BIT(IE_S_TX_UNDERRUN_SHIFT);
> +		iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
>  
>  		/* End of SMBUS for Master Read */
>  		val = BIT(S_TX_WR_STATUS_SHIFT);
> @@ -515,9 +492,49 @@ static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c,
>  				 BIT(IS_S_START_BUSY_SHIFT));
>  	}
>  
> -	/* check slave transmit status only if slave is transmitting */
> -	if (!iproc_i2c->slave_rx_only)
> -		bcm_iproc_i2c_check_slave_status(iproc_i2c);
> +	/* if the controller has been reset, immediately return from the ISR */
> +	if (bcm_iproc_i2c_check_slave_status(iproc_i2c, status))
> +		return true;
> +
> +	/*
> +	 * Slave events in case of master-write, master-write-read and,
> +	 * master-read
> +	 *
> +	 * Master-write     : only IS_S_RX_EVENT_SHIFT event
> +	 * Master-write-read: both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT
> +	 *                    events
> +	 * Master-read      : both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT
> +	 *                    events or only IS_S_RD_EVENT_SHIFT
> +	 *
> +	 * iproc has a slave rx fifo size of 64 bytes. Rx fifo full interrupt
> +	 * (IS_S_RX_FIFO_FULL_SHIFT) will be generated when RX fifo becomes
> +	 * full. This can happen if Master issues write requests of more than
> +	 * 64 bytes.
> +	 */
> +	if (status & BIT(IS_S_RX_EVENT_SHIFT) ||
> +	    status & BIT(IS_S_RD_EVENT_SHIFT) ||
> +	    status & BIT(IS_S_RX_FIFO_FULL_SHIFT)) {
> +		/* disable slave interrupts */
> +		val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
> +		val &= ~iproc_i2c->slave_int_mask;
> +		iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
> +
> +		if (status & BIT(IS_S_RD_EVENT_SHIFT))
> +			/* Master-write-read request */
> +			iproc_i2c->slave_rx_only = false;
> +		else
> +			/* Master-write request only */
> +			iproc_i2c->slave_rx_only = true;
> +
> +		/* schedule tasklet to read data later */
> +		tasklet_schedule(&iproc_i2c->slave_rx_tasklet);
> +
> +		/* clear IS_S_RX_FIFO_FULL_SHIFT interrupt */
> +		if (status & BIT(IS_S_RX_FIFO_FULL_SHIFT)) {
> +			val = BIT(IS_S_RX_FIFO_FULL_SHIFT);
> +			iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, val);
> +		}
> +	}
>  
>  	return true;
>  }

This fix looks good to me, and we have reviewed and thoroughly tested it
internally. Thanks.

Acked-by: Ray Jui <ray.jui@xxxxxxxxxxxx>

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature


[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux