[i2c] [PATCH] i2c-au1550: do i2c stop on errors

Jean Delvare khali at linux-fr.org
Sat Oct 27 18:01:23 CEST 2007


Hi Manuel,

Sorry for the late answer.

On Sat, 6 Oct 2007 18:14:34 +0200, Manuel Lauss wrote:
> From c1d94ed0881608ea1b2bcd4dcf7002d8d551437a Mon Sep 17 00:00:00 2001
> From: Manuel Lauss <mano at roarinelk.homelinux.net>
> Date: Sat, 6 Oct 2007 17:10:21 +0200
> Subject: [PATCH] i2c-au1550: do i2c stop on errors
> 
> The i2c-au1550.c driver does not do a stop condition in case of NAKs
> from the bus (non-existant slave/slave sent NAKs), which led to the
> RTC minute register on board being overwritten on subsequent transfers.
> 
> This fix however is suboptimal due to hardware constraints: The I2C
> hardware can only send a stop after it has sent/received 8 databits;
> this fix essentially does an extra byte transfer before doing the
> actual stop; however I have not yet seen any errors due to this.
> 
> Signed-off-by: Manuel Lauss <mano at roarinelk.homelinux.net>
> ---
>  drivers/i2c/busses/i2c-au1550.c |   37 ++++++++++++++++++++++++++++++-------
>  1 files changed, 30 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c
> index d7e7c35..df778eb 100644
> --- a/drivers/i2c/busses/i2c-au1550.c
> +++ b/drivers/i2c/busses/i2c-au1550.c
> @@ -180,6 +180,19 @@ wait_for_rx_byte(struct i2c_au1550_data *adap, u32 *ret_data)
>  	return 0;
>  }
>  
> +/* send a stop over I2C in case the transfer aborted abnormally.
> + * NOTE: This is very suboptimal: This function here will cause the hw
> + *	 to read a byte from / write zero to the bus and do the stop
> + *	 right after it;  the hw won't let us do it any other way.
> + */
> +static void i2c_au1550_stop(struct i2c_au1550_data *adap)
> +{
> +	volatile psc_smb_t *sp = (volatile psc_smb_t *)(adap->psc_base);
> +	sp->psc_smbtxrx = PSC_SMBTXRX_STP;
> +	au_sync();
> +	wait_master_done(adap);
> +}
> +
>  static int
>  i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
>  		    unsigned int len)
> @@ -203,7 +216,7 @@ i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
>  		sp->psc_smbtxrx = 0;
>  		au_sync();
>  		if (wait_for_rx_byte(adap, &data))
> -			return -EIO;
> +			goto out_err;
>  
>  		buf[i] = data;
>  		i++;
> @@ -214,12 +227,16 @@ i2c_read(struct i2c_au1550_data *adap, unsigned char *buf,
>  	sp->psc_smbtxrx = PSC_SMBTXRX_STP;
>  	au_sync();
>  	if (wait_master_done(adap))
> -		return -EIO;
> +		goto out_err;

I'm a bit skeptical about this one...

>  
>  	data = sp->psc_smbtxrx;
>  	au_sync();
>  	buf[i] = data;
>  	return 0;
> +
> +out_err:
> +	i2c_au1550_stop(adap);
> +	return -EIO;
>  }
>  
>  static int
> @@ -241,7 +258,7 @@ i2c_write(struct i2c_au1550_data *adap, unsigned char *buf,
>  		sp->psc_smbtxrx = data;
>  		au_sync();
>  		if (wait_ack(adap))
> -			return -EIO;
> +			goto out_err;
>  		i++;
>  	}
>  
> @@ -251,9 +268,12 @@ i2c_write(struct i2c_au1550_data *adap, unsigned char *buf,
>  	data |= PSC_SMBTXRX_STP;
>  	sp->psc_smbtxrx = data;
>  	au_sync();
> -	if (wait_master_done(adap))
> -		return -EIO;
> -	return 0;
> +	if (wait_master_done(adap) == 0)
> +		return 0;

... and this one. In both cases, you already sent the STOP condition as
part of the last byte read resp. written. A failure suggests that the
slave rejected the read or write, but does it also mean that the STOP
condition wasn't issued? I doubt it. If it were the case, then why
would a subsequent call to i2c_au1550_stop() succeed, while it's doing
exactly the same?

> +
> +out_err:
> +	i2c_au1550_stop(adap);
> +	return -EIO;
>  }
>  
>  static int
> @@ -266,8 +286,11 @@ au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
>  	for (i = 0; !err && i < num; i++) {
>  		p = &msgs[i];
>  		err = do_address(adap, p->addr, p->flags & I2C_M_RD);
> -		if (err || !p->len)
> +		if (err || ((!p->len) && (i == (num - 1)))) {
> +			/* slave NAK or no more data; need to do i2c stop */
> +			i2c_au1550_stop(adap);
>  			continue;
> +		}

Doesn't look correct. If you issue a STOP at this point, then you want
to break, NOT continue. It doesn't make a difference for the second
condition ((!p->len) && (i == (num - 1))) as this is the last
iteration, but it does make a big difference for the first condition
(err).

>  		if (p->flags & I2C_M_RD)
>  			err = i2c_read(adap, p->buf, p->len);
>  		else

BTW... We know that the AU1550 I2C module can't issue a STOP condition
without a data byte being read or written and this is a problem for
0-byte transactions. Did you try issuing the STOP condition directly in
do_address() in this case? I wonder if it would work. If it does, it
would be cleaner than your approach, I think.

-- 
Jean Delvare



More information about the i2c mailing list