diff mbox

[RFC,OMAP3:I2C] Workaround for OMAP3430 I2C silicon errata 1.153

Message ID CD8CC2B65FEE304DA95744A5472698F2028A41187E@dlee06.ent.ti.com (mailing list archive)
State Awaiting Upstream, archived
Headers show

Commit Message

Sonasath, Moiz June 22, 2009, 4:49 p.m. UTC
This patch includes the workarround for I2C Errata 1.153: When an XRDY/XDR is hit, wait for XUDF before writing data to DATA_REG

Signed-off-by: Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com>
Signed-off by: Nishant Kamat <nskamat@ti.com>
Signed-off-by: Moiz Sonasath<m-sonasath@ti.com>
---
 drivers/i2c/busses/i2c-omap.c |   54 +++++++++++++++++++++++++++++++++++++---
 1 files changed, 50 insertions(+), 4 deletions(-)

Comments

Kevin Hilman June 22, 2009, 10:30 p.m. UTC | #1
"Sonasath, Moiz" <m-sonasath@ti.com> writes:

> This patch includes the workarround for I2C Errata 1.153: When an XRDY/XDR is hit, wait for XUDF before writing data to DATA_REG

How was this tested/validated, on what platforms etc.

> Signed-off-by: Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com>
> Signed-off by: Nishant Kamat <nskamat@ti.com>
> Signed-off-by: Moiz Sonasath<m-sonasath@ti.com>
> ---
>  drivers/i2c/busses/i2c-omap.c |   54 +++++++++++++++++++++++++++++++++++++---
>  1 files changed, 50 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
> index ece0125..e84836b 100644
> --- a/drivers/i2c/busses/i2c-omap.c
> +++ b/drivers/i2c/busses/i2c-omap.c
> @@ -632,6 +632,37 @@ omap_i2c_rev1_isr(int this_irq, void *dev_id)
>  #define omap_i2c_rev1_isr		NULL
>  #endif
>  
> +/* I2C Errata 1.153:
> + * When an XRDY/XDR is hit, wait for XUDF before writing data to DATA_REG.
> + * Otherwise some data bytes can be lost while transferring them from the
> + * memory to the I2C interface.
> + */
> +
> +static int omap_i2c_wait_for_xudf(struct omap_i2c_dev *dev)
> +{
> +	u16 xudf;
> +	int counter = 500;
> +
> +	/* We are in interrupt context. Wait for XUDF for max 7 msec */

What does being in interrupt context have to do with how long you
wait?  Threaded interrupts are now in mainline and will become the
default, so this ISR may run in thread context.


> +	xudf = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
> +	while (!(xudf & OMAP_I2C_STAT_XUDF) && counter--) {
> +		if (xudf & (OMAP_I2C_STAT_ROVR | OMAP_I2C_STAT_NACK |
> +			    OMAP_I2C_STAT_AL))
> +			return -EINVAL;
> +		udelay(10);
> +		xudf = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
> +	}
> +
> +	if (!counter) {
> +		/* Clear Tx FIFO */
> +		omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG,
> +				OMAP_I2C_BUF_TXFIF_CLR);
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +
>  static irqreturn_t
>  omap_i2c_isr(int this_irq, void *dev_id)
>  {
> @@ -639,6 +670,7 @@ omap_i2c_isr(int this_irq, void *dev_id)
>  	u16 bits;
>  	u16 stat, w;
>  	int err, count = 0;
> +	int error;
>  
>  	if (dev->idle)
>  		return IRQ_NONE;
> @@ -647,7 +679,7 @@ omap_i2c_isr(int this_irq, void *dev_id)
>  	while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) {
>  		dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat);
>  		if (count++ == 100) {
> -			dev_warn(dev->dev, "Too much work in one IRQ\n");
> +			dev_dbg(dev->dev, "Too much work in one IRQ\n");

Should stay as dev_warn I think.

>  			break;
>  		}
>  
> @@ -715,11 +747,22 @@ omap_i2c_isr(int this_irq, void *dev_id)
>  							OMAP_I2C_BUFSTAT_REG);
>  			}
>  			while (num_bytes) {
> -				num_bytes--;
>  				w = 0;
>  				if (dev->buf_len) {
> +					if (cpu_is_omap34xx()) {
> +						/* OMAP3430 Errata 1.153 */
> +						error = omap_i2c_wait_for_xudf(dev);
> +						if (error) {
> +							omap_i2c_ack_stat(dev, stat &
> +								(OMAP_I2C_STAT_XRDY |
> +								 OMAP_I2C_STAT_XDR));
> +							dev_err(dev->dev, "Transmit error\n");
> +							omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_XUDF);
> +
> +							return IRQ_HANDLED;
> +						}
> +					}

Yuck, too much wrapping here.  Running through checkpatch would've warned
you about this.  Looks like you need to break this out into a subroutine.


>  					w = *dev->buf++;
> -					dev->buf_len--;
>  					/* Data reg from  2430 is 8 bit wide */
>  					if (!cpu_is_omap2430() &&
>  							!cpu_is_omap34xx()) {
> @@ -728,6 +771,10 @@ omap_i2c_isr(int this_irq, void *dev_id)
>  							dev->buf_len--;
>  						}
>  					}
> +					omap_i2c_write_reg(dev,
> +						OMAP_I2C_DATA_REG, w);
> +					num_bytes--;
> +					dev->buf_len--;
>  				} else {
>  					if (stat & OMAP_I2C_STAT_XRDY)
>  						dev_err(dev->dev,
> @@ -739,7 +786,6 @@ omap_i2c_isr(int this_irq, void *dev_id)
>  							"data to send\n");
>  					break;
>  				}
> -				omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
>  			}
>  			omap_i2c_ack_stat(dev,
>  				stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
> -- 
> 1.5.6.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Paul Walmsley June 23, 2009, 2:17 a.m. UTC | #2
Moiz, Nishant, Jagadeesh,

Looks like this patch has some serious problems.

On Mon, 22 Jun 2009, Sonasath, Moiz wrote:

> This patch includes the workarround for I2C Errata 1.153: When an 
> XRDY/XDR is hit, wait for XUDF before writing data to DATA_REG
> 
> Signed-off-by: Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com>
> Signed-off by: Nishant Kamat <nskamat@ti.com>
> Signed-off-by: Moiz Sonasath<m-sonasath@ti.com>
> ---
>  drivers/i2c/busses/i2c-omap.c |   54 +++++++++++++++++++++++++++++++++++++---
>  1 files changed, 50 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
> index ece0125..e84836b 100644
> --- a/drivers/i2c/busses/i2c-omap.c
> +++ b/drivers/i2c/busses/i2c-omap.c
> @@ -632,6 +632,37 @@ omap_i2c_rev1_isr(int this_irq, void *dev_id)
>  #define omap_i2c_rev1_isr		NULL
>  #endif
>  
> +/* I2C Errata 1.153:
> + * When an XRDY/XDR is hit, wait for XUDF before writing data to DATA_REG.
> + * Otherwise some data bytes can be lost while transferring them from the
> + * memory to the I2C interface.
> + */
> +
> +static int omap_i2c_wait_for_xudf(struct omap_i2c_dev *dev)
> +{
> +	u16 xudf;
> +	int counter = 500;

Shouldn't the timeout threshold depend on the I2C bus speed and transmit 
FIFO threshold?

> +
> +	/* We are in interrupt context. Wait for XUDF for max 7 msec */

There's no way an interrupt service routine should loop for up to 7 
milliseconds.  Why does it need to wait this long?  Shouldn't this patch 
zero the transmit FIFO threshold to minimize the amount of time between 
XRDY/XDR and XUDF?

> +	xudf = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
> +	while (!(xudf & OMAP_I2C_STAT_XUDF) && counter--) {

Why is a separate I2C_STAT loop used here?  Shouldn't this patch just use 
the existing I2C_STAT loop in the ISR and use a state variable to indicate 
the current state of the ISR?

> +		if (xudf & (OMAP_I2C_STAT_ROVR | OMAP_I2C_STAT_NACK |
> +			    OMAP_I2C_STAT_AL))
> +			return -EINVAL;
> +		udelay(10);

udelay() in an ISR is a big red flag.  Why is this needed?

> +		xudf = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
> +	}
> +
> +	if (!counter) {
> +		/* Clear Tx FIFO */
> +		omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG,
> +				OMAP_I2C_BUF_TXFIF_CLR);
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}
> +
>  static irqreturn_t
>  omap_i2c_isr(int this_irq, void *dev_id)
>  {
> @@ -639,6 +670,7 @@ omap_i2c_isr(int this_irq, void *dev_id)
>  	u16 bits;
>  	u16 stat, w;
>  	int err, count = 0;
> +	int error;
>  
>  	if (dev->idle)
>  		return IRQ_NONE;
> @@ -647,7 +679,7 @@ omap_i2c_isr(int this_irq, void *dev_id)
>  	while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) {
>  		dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat);
>  		if (count++ == 100) {
> -			dev_warn(dev->dev, "Too much work in one IRQ\n");
> +			dev_dbg(dev->dev, "Too much work in one IRQ\n");
>  			break;
>  		}
>  
> @@ -715,11 +747,22 @@ omap_i2c_isr(int this_irq, void *dev_id)
>  							OMAP_I2C_BUFSTAT_REG);
>  			}
>  			while (num_bytes) {
> -				num_bytes--;
>  				w = 0;
>  				if (dev->buf_len) {
> +					if (cpu_is_omap34xx()) {
> +						/* OMAP3430 Errata 1.153 */

What about this I2C IP block on previous chip versions?  Is this problem 
also present on 2430, which also has FIFO transmit capability?

> +						error = omap_i2c_wait_for_xudf(dev);
> +						if (error) {
> +							omap_i2c_ack_stat(dev, stat &
> +								(OMAP_I2C_STAT_XRDY |
> +								 OMAP_I2C_STAT_XDR));
> +							dev_err(dev->dev, "Transmit error\n");
> +							omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_XUDF);
> +
> +							return IRQ_HANDLED;
> +						}
> +					}
>  					w = *dev->buf++;
> -					dev->buf_len--;
>  					/* Data reg from  2430 is 8 bit wide */
>  					if (!cpu_is_omap2430() &&
>  							!cpu_is_omap34xx()) {
> @@ -728,6 +771,10 @@ omap_i2c_isr(int this_irq, void *dev_id)
>  							dev->buf_len--;
>  						}
>  					}
> +					omap_i2c_write_reg(dev,
> +						OMAP_I2C_DATA_REG, w);
> +					num_bytes--;
> +					dev->buf_len--;
>  				} else {
>  					if (stat & OMAP_I2C_STAT_XRDY)
>  						dev_err(dev->dev,
> @@ -739,7 +786,6 @@ omap_i2c_isr(int this_irq, void *dev_id)
>  							"data to send\n");
>  					break;
>  				}
> -				omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
>  			}
>  			omap_i2c_ack_stat(dev,
>  				stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));


- Paul
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Nishanth Menon June 23, 2009, 4:20 a.m. UTC | #3
> -----Original Message-----
> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-
> owner@vger.kernel.org] On Behalf Of Sonasath, Moiz
> Sent: Monday, June 22, 2009 7:50 PM
> To: linux-omap@vger.kernel.org
> Cc: Pandita, Vikram
> Subject: [PATCH] [RFC][OMAP3:I2C]Workaround for OMAP3430 I2C silicon
> errata 1.153
> 
> This patch includes the workarround for I2C Errata 1.153: When an XRDY/XDR
> is hit, wait for XUDF before writing data to DATA_REG

Is this workaround valid for omap2430 also?

Regards,
Nishanth Menon
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Kamat, Nishant June 23, 2009, 9:55 a.m. UTC | #4
Kevin, 

> -----Original Message-----
> From: Kevin Hilman
>
> "Sonasath, Moiz" <m-sonasath@ti.com> writes:
> 
> > This patch includes the workarround for I2C Errata 1.153: 
[...]

> > +static int omap_i2c_wait_for_xudf(struct omap_i2c_dev *dev)
> > +{
> > +	u16 xudf;
> > +	int counter = 500;
> > +
> > +	/* We are in interrupt context. Wait for XUDF for max 7 msec */
> 
> What does being in interrupt context have to do with how long you
> wait?  Threaded interrupts are now in mainline and will become the
> default, so this ISR may run in thread context.

The interrupt context comment was meant to say that we can't sleep. Perhaps, with threaded interrupts that might not be true (I am not sure). We can remove the interrupt context comment.


> > @@ -647,7 +679,7 @@ omap_i2c_isr(int this_irq, void *dev_id)
> >  	while ((stat = (omap_i2c_read_reg(dev, 
> OMAP_I2C_STAT_REG))) & bits) {
> >  		dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat);
> >  		if (count++ == 100) {
> > -			dev_warn(dev->dev, "Too much work in 
> one IRQ\n");
> > +			dev_dbg(dev->dev, "Too much work in one IRQ\n");
> 
> Should stay as dev_warn I think.
> 

When I2C is used to transfer a large number of bytes continuously (e.g. during some camera sensor firmware update), we hit the max count more often now (because of the delay introduced by the workaround implementation). In this case, its undesirable to see the dev_warn messages fill up the console. Changing this to dev_dbg means that this message is not printed in the expected case.


Regards,
Nishant
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Nishanth Menon June 23, 2009, 10:05 a.m. UTC | #5
> -----Original Message-----
> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-
> owner@vger.kernel.org] On Behalf Of Kamat, Nishant
> Sent: Tuesday, June 23, 2009 12:55 PM
> 
> > > @@ -647,7 +679,7 @@ omap_i2c_isr(int this_irq, void *dev_id)
> > >  	while ((stat = (omap_i2c_read_reg(dev,
> > OMAP_I2C_STAT_REG))) & bits) {
> > >  		dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat);
> > >  		if (count++ == 100) {
> > > -			dev_warn(dev->dev, "Too much work in
> > one IRQ\n");
> > > +			dev_dbg(dev->dev, "Too much work in one IRQ\n");
> >
> > Should stay as dev_warn I think.
> >
> 
> When I2C is used to transfer a large number of bytes continuously (e.g.
> during some camera sensor firmware update), we hit the max count more
> often now (because of the delay introduced by the workaround
> implementation). In this case, its undesirable to see the dev_warn
> messages fill up the console. Changing this to dev_dbg means that this
> message is not printed in the expected case.
Just wondering on this -> cant I do a multibyte write upto 255 bytes? Is count==100 not enough given that we used buffered writes? The real question is this:
Are devices expected to trigger retry logic to the extent where the error condition is triggered?

I think this might be an indication of something else being at fault with the sensor /configuration of sensor - hiding the error messages by moving warn->dbg is not correct IMHO.

Regards,
Nishanth Menon
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Igor Mazanov June 23, 2009, 1:43 p.m. UTC | #6
Hello,

Menon, Nishanth wrote:
>> -----Original Message-----
>> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-
>> owner@vger.kernel.org] On Behalf Of Sonasath, Moiz
>> Sent: Monday, June 22, 2009 7:50 PM
>> To: linux-omap@vger.kernel.org
>> Cc: Pandita, Vikram
>> Subject: [PATCH] [RFC][OMAP3:I2C]Workaround for OMAP3430 I2C silicon
>> errata 1.153
>>
>> This patch includes the workarround for I2C Errata 1.153: When an XRDY/XDR
>> is hit, wait for XUDF before writing data to DATA_REG
> 
> Is this workaround valid for omap2430 also?

Some kind of such workaround needs to be applied and for OMAP1 ISR too. 
I had
the same problem on our OMAP5910 based custom made board. While writing 
a large contiguous amount of data constantly occurs a situation when 
dev->buf_len = 0, but I2C_CNT register != 0, and, as result, I2C 
controller doesn't generate ARDY IRQ, no complete_cmd occurs in ISR, and 
we get "controller timed out" issues then...

So, here is a part of modified OMAP1 ISR. It works for me at least.

/*
  * Is there a bug in the OMAP5910 I2C controller? It
  * generates a bunch of fake XRDY interrupts under high load.
  * As result, there is a very high chance to have a situation
  * when dev->buf_len = 0 already, but I2C_CNT != 0. So, there
  * is no ARDY irq then, no complete_cmd, controller timed out
  * issues...
  *
  * Workaround:
  *
  * When we get XRDY interrupt without transmit underflow flag
  * (XUDF bit in the I2C_STAT register), delay for 100 microsecs
  * and ignore it.
  *
  * We write data into I2C_DATA register in case of transmit
  * underflow condition ONLY.
  */
if (stat & OMAP_I2C_STAT_XRDY) {
	if (!(stat & OMAP_I2C_STAT_XUDF)) {
		udelay(100);
		continue;
	} else {
		w = 0;
		if (dev->buf_len) {
			w = *dev->buf++;
			dev->buf_len--;
			if (dev->buf_len) {
				w |= *dev->buf++ << 8;
				dev->buf_len--;
			}
			omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
		} else {
			dev_err(dev->dev, "XRDY IRQ while no "
						"data to send\n");
			break;
		}
		continue;
	}
}	

Regards,
Igor.

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Nishanth Menon June 23, 2009, 7:06 p.m. UTC | #7
> -----Original Message-----
> From: linux-omap-owner@vger.kernel.org [mailto:linux-omap-
> owner@vger.kernel.org] On Behalf Of Igor Mazanov
> Sent: Tuesday, June 23, 2009 4:43 PM
> To: linux-omap@vger.kernel.org
> Subject: Re: [PATCH] [RFC][OMAP3:I2C]Workaround for OMAP3430 I2C silicon
> errata 1.153
> 
> /*
>   * Is there a bug in the OMAP5910 I2C controller? It
>   * generates a bunch of fake XRDY interrupts under high load.
>   * As result, there is a very high chance to have a situation
>   * when dev->buf_len = 0 already, but I2C_CNT != 0. So, there
>   * is no ARDY irq then, no complete_cmd, controller timed out
>   * issues...
>   *
>   * Workaround:
>   *
>   * When we get XRDY interrupt without transmit underflow flag
>   * (XUDF bit in the I2C_STAT register), delay for 100 microsecs
>   * and ignore it.
>   *
>   * We write data into I2C_DATA register in case of transmit
>   * underflow condition ONLY.
>   */
> if (stat & OMAP_I2C_STAT_XRDY) {
> 	if (!(stat & OMAP_I2C_STAT_XUDF)) {
> 		udelay(100);
> 		continue;
> 	} else {
> 		w = 0;
> 		if (dev->buf_len) {
> 			w = *dev->buf++;
> 			dev->buf_len--;
> 			if (dev->buf_len) {
> 				w |= *dev->buf++ << 8;
> 				dev->buf_len--;
> 			}
> 			omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
> 		} else {
> 			dev_err(dev->dev, "XRDY IRQ while no "
> 						"data to send\n");
> 			break;
> 		}
> 		continue;
> 	}
> }
> 
Could you submit a patch please?


Regards,
Nishanth Menon
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Paul Walmsley July 8, 2009, 4:50 p.m. UTC | #8
Hello Moiz, Nishant, Jagadeesh,

Any plans to respond to these comments?  

- Paul

On Mon, 22 Jun 2009, Paul Walmsley wrote:

> Moiz, Nishant, Jagadeesh,
> 
> Looks like this patch has some serious problems.
> 
> On Mon, 22 Jun 2009, Sonasath, Moiz wrote:
> 
> > This patch includes the workarround for I2C Errata 1.153: When an 
> > XRDY/XDR is hit, wait for XUDF before writing data to DATA_REG
> > 
> > Signed-off-by: Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com>
> > Signed-off by: Nishant Kamat <nskamat@ti.com>
> > Signed-off-by: Moiz Sonasath<m-sonasath@ti.com>
> > ---
> >  drivers/i2c/busses/i2c-omap.c |   54 +++++++++++++++++++++++++++++++++++++---
> >  1 files changed, 50 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
> > index ece0125..e84836b 100644
> > --- a/drivers/i2c/busses/i2c-omap.c
> > +++ b/drivers/i2c/busses/i2c-omap.c
> > @@ -632,6 +632,37 @@ omap_i2c_rev1_isr(int this_irq, void *dev_id)
> >  #define omap_i2c_rev1_isr		NULL
> >  #endif
> >  
> > +/* I2C Errata 1.153:
> > + * When an XRDY/XDR is hit, wait for XUDF before writing data to DATA_REG.
> > + * Otherwise some data bytes can be lost while transferring them from the
> > + * memory to the I2C interface.
> > + */
> > +
> > +static int omap_i2c_wait_for_xudf(struct omap_i2c_dev *dev)
> > +{
> > +	u16 xudf;
> > +	int counter = 500;
> 
> Shouldn't the timeout threshold depend on the I2C bus speed and transmit 
> FIFO threshold?
> 
> > +
> > +	/* We are in interrupt context. Wait for XUDF for max 7 msec */
> 
> There's no way an interrupt service routine should loop for up to 7 
> milliseconds.  Why does it need to wait this long?  Shouldn't this patch 
> zero the transmit FIFO threshold to minimize the amount of time between 
> XRDY/XDR and XUDF?
> 
> > +	xudf = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
> > +	while (!(xudf & OMAP_I2C_STAT_XUDF) && counter--) {
> 
> Why is a separate I2C_STAT loop used here?  Shouldn't this patch just use 
> the existing I2C_STAT loop in the ISR and use a state variable to indicate 
> the current state of the ISR?
> 
> > +		if (xudf & (OMAP_I2C_STAT_ROVR | OMAP_I2C_STAT_NACK |
> > +			    OMAP_I2C_STAT_AL))
> > +			return -EINVAL;
> > +		udelay(10);
> 
> udelay() in an ISR is a big red flag.  Why is this needed?
> 
> > +		xudf = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
> > +	}
> > +
> > +	if (!counter) {
> > +		/* Clear Tx FIFO */
> > +		omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG,
> > +				OMAP_I2C_BUF_TXFIF_CLR);
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >  static irqreturn_t
> >  omap_i2c_isr(int this_irq, void *dev_id)
> >  {
> > @@ -639,6 +670,7 @@ omap_i2c_isr(int this_irq, void *dev_id)
> >  	u16 bits;
> >  	u16 stat, w;
> >  	int err, count = 0;
> > +	int error;
> >  
> >  	if (dev->idle)
> >  		return IRQ_NONE;
> > @@ -647,7 +679,7 @@ omap_i2c_isr(int this_irq, void *dev_id)
> >  	while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) {
> >  		dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat);
> >  		if (count++ == 100) {
> > -			dev_warn(dev->dev, "Too much work in one IRQ\n");
> > +			dev_dbg(dev->dev, "Too much work in one IRQ\n");
> >  			break;
> >  		}
> >  
> > @@ -715,11 +747,22 @@ omap_i2c_isr(int this_irq, void *dev_id)
> >  							OMAP_I2C_BUFSTAT_REG);
> >  			}
> >  			while (num_bytes) {
> > -				num_bytes--;
> >  				w = 0;
> >  				if (dev->buf_len) {
> > +					if (cpu_is_omap34xx()) {
> > +						/* OMAP3430 Errata 1.153 */
> 
> What about this I2C IP block on previous chip versions?  Is this problem 
> also present on 2430, which also has FIFO transmit capability?
> 
> > +						error = omap_i2c_wait_for_xudf(dev);
> > +						if (error) {
> > +							omap_i2c_ack_stat(dev, stat &
> > +								(OMAP_I2C_STAT_XRDY |
> > +								 OMAP_I2C_STAT_XDR));
> > +							dev_err(dev->dev, "Transmit error\n");
> > +							omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_XUDF);
> > +
> > +							return IRQ_HANDLED;
> > +						}
> > +					}
> >  					w = *dev->buf++;
> > -					dev->buf_len--;
> >  					/* Data reg from  2430 is 8 bit wide */
> >  					if (!cpu_is_omap2430() &&
> >  							!cpu_is_omap34xx()) {
> > @@ -728,6 +771,10 @@ omap_i2c_isr(int this_irq, void *dev_id)
> >  							dev->buf_len--;
> >  						}
> >  					}
> > +					omap_i2c_write_reg(dev,
> > +						OMAP_I2C_DATA_REG, w);
> > +					num_bytes--;
> > +					dev->buf_len--;
> >  				} else {
> >  					if (stat & OMAP_I2C_STAT_XRDY)
> >  						dev_err(dev->dev,
> > @@ -739,7 +786,6 @@ omap_i2c_isr(int this_irq, void *dev_id)
> >  							"data to send\n");
> >  					break;
> >  				}
> > -				omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
> >  			}
> >  			omap_i2c_ack_stat(dev,
> >  				stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
> 
> 
> - Paul
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sonasath, Moiz July 8, 2009, 6:21 p.m. UTC | #9
Hello Paul!

I am working on this issue and will get back soon.

Regards
Moiz Sonasath
Linux Baseport Team, Dallas
214-567-5514


-----Original Message-----
From: Paul Walmsley [mailto:paul@pwsan.com] 
Sent: Wednesday, July 08, 2009 11:51 AM
To: Sonasath, Moiz
Cc: linux-omap@vger.kernel.org; Pakaravoor, Jagadeesh; Kamat, Nishant; Pandita, Vikram
Subject: Re: [PATCH] [RFC][OMAP3:I2C]Workaround for OMAP3430 I2C silicon errata 1.153

Hello Moiz, Nishant, Jagadeesh,

Any plans to respond to these comments?  

- Paul

On Mon, 22 Jun 2009, Paul Walmsley wrote:

> Moiz, Nishant, Jagadeesh,
> 
> Looks like this patch has some serious problems.
> 
> On Mon, 22 Jun 2009, Sonasath, Moiz wrote:
> 
> > This patch includes the workarround for I2C Errata 1.153: When an 
> > XRDY/XDR is hit, wait for XUDF before writing data to DATA_REG
> > 
> > Signed-off-by: Jagadeesh Bhaskar Pakaravoor <j-pakaravoor@ti.com>
> > Signed-off by: Nishant Kamat <nskamat@ti.com>
> > Signed-off-by: Moiz Sonasath<m-sonasath@ti.com>
> > ---
> >  drivers/i2c/busses/i2c-omap.c |   54 +++++++++++++++++++++++++++++++++++++---
> >  1 files changed, 50 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
> > index ece0125..e84836b 100644
> > --- a/drivers/i2c/busses/i2c-omap.c
> > +++ b/drivers/i2c/busses/i2c-omap.c
> > @@ -632,6 +632,37 @@ omap_i2c_rev1_isr(int this_irq, void *dev_id)
> >  #define omap_i2c_rev1_isr		NULL
> >  #endif
> >  
> > +/* I2C Errata 1.153:
> > + * When an XRDY/XDR is hit, wait for XUDF before writing data to DATA_REG.
> > + * Otherwise some data bytes can be lost while transferring them from the
> > + * memory to the I2C interface.
> > + */
> > +
> > +static int omap_i2c_wait_for_xudf(struct omap_i2c_dev *dev)
> > +{
> > +	u16 xudf;
> > +	int counter = 500;
> 
> Shouldn't the timeout threshold depend on the I2C bus speed and transmit 
> FIFO threshold?
> 
> > +
> > +	/* We are in interrupt context. Wait for XUDF for max 7 msec */
> 
> There's no way an interrupt service routine should loop for up to 7 
> milliseconds.  Why does it need to wait this long?  Shouldn't this patch 
> zero the transmit FIFO threshold to minimize the amount of time between 
> XRDY/XDR and XUDF?
> 
> > +	xudf = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
> > +	while (!(xudf & OMAP_I2C_STAT_XUDF) && counter--) {
> 
> Why is a separate I2C_STAT loop used here?  Shouldn't this patch just use 
> the existing I2C_STAT loop in the ISR and use a state variable to indicate 
> the current state of the ISR?
> 
> > +		if (xudf & (OMAP_I2C_STAT_ROVR | OMAP_I2C_STAT_NACK |
> > +			    OMAP_I2C_STAT_AL))
> > +			return -EINVAL;
> > +		udelay(10);
> 
> udelay() in an ISR is a big red flag.  Why is this needed?
> 
> > +		xudf = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
> > +	}
> > +
> > +	if (!counter) {
> > +		/* Clear Tx FIFO */
> > +		omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG,
> > +				OMAP_I2C_BUF_TXFIF_CLR);
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >  static irqreturn_t
> >  omap_i2c_isr(int this_irq, void *dev_id)
> >  {
> > @@ -639,6 +670,7 @@ omap_i2c_isr(int this_irq, void *dev_id)
> >  	u16 bits;
> >  	u16 stat, w;
> >  	int err, count = 0;
> > +	int error;
> >  
> >  	if (dev->idle)
> >  		return IRQ_NONE;
> > @@ -647,7 +679,7 @@ omap_i2c_isr(int this_irq, void *dev_id)
> >  	while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) {
> >  		dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat);
> >  		if (count++ == 100) {
> > -			dev_warn(dev->dev, "Too much work in one IRQ\n");
> > +			dev_dbg(dev->dev, "Too much work in one IRQ\n");
> >  			break;
> >  		}
> >  
> > @@ -715,11 +747,22 @@ omap_i2c_isr(int this_irq, void *dev_id)
> >  							OMAP_I2C_BUFSTAT_REG);
> >  			}
> >  			while (num_bytes) {
> > -				num_bytes--;
> >  				w = 0;
> >  				if (dev->buf_len) {
> > +					if (cpu_is_omap34xx()) {
> > +						/* OMAP3430 Errata 1.153 */
> 
> What about this I2C IP block on previous chip versions?  Is this problem 
> also present on 2430, which also has FIFO transmit capability?
> 
> > +						error = omap_i2c_wait_for_xudf(dev);
> > +						if (error) {
> > +							omap_i2c_ack_stat(dev, stat &
> > +								(OMAP_I2C_STAT_XRDY |
> > +								 OMAP_I2C_STAT_XDR));
> > +							dev_err(dev->dev, "Transmit error\n");
> > +							omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_XUDF);
> > +
> > +							return IRQ_HANDLED;
> > +						}
> > +					}
> >  					w = *dev->buf++;
> > -					dev->buf_len--;
> >  					/* Data reg from  2430 is 8 bit wide */
> >  					if (!cpu_is_omap2430() &&
> >  							!cpu_is_omap34xx()) {
> > @@ -728,6 +771,10 @@ omap_i2c_isr(int this_irq, void *dev_id)
> >  							dev->buf_len--;
> >  						}
> >  					}
> > +					omap_i2c_write_reg(dev,
> > +						OMAP_I2C_DATA_REG, w);
> > +					num_bytes--;
> > +					dev->buf_len--;
> >  				} else {
> >  					if (stat & OMAP_I2C_STAT_XRDY)
> >  						dev_err(dev->dev,
> > @@ -739,7 +786,6 @@ omap_i2c_isr(int this_irq, void *dev_id)
> >  							"data to send\n");
> >  					break;
> >  				}
> > -				omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
> >  			}
> >  			omap_i2c_ack_stat(dev,
> >  				stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));
> 
> 
> - Paul
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index ece0125..e84836b 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -632,6 +632,37 @@  omap_i2c_rev1_isr(int this_irq, void *dev_id)
 #define omap_i2c_rev1_isr		NULL
 #endif
 
+/* I2C Errata 1.153:
+ * When an XRDY/XDR is hit, wait for XUDF before writing data to DATA_REG.
+ * Otherwise some data bytes can be lost while transferring them from the
+ * memory to the I2C interface.
+ */
+
+static int omap_i2c_wait_for_xudf(struct omap_i2c_dev *dev)
+{
+	u16 xudf;
+	int counter = 500;
+
+	/* We are in interrupt context. Wait for XUDF for max 7 msec */
+	xudf = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
+	while (!(xudf & OMAP_I2C_STAT_XUDF) && counter--) {
+		if (xudf & (OMAP_I2C_STAT_ROVR | OMAP_I2C_STAT_NACK |
+			    OMAP_I2C_STAT_AL))
+			return -EINVAL;
+		udelay(10);
+		xudf = omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG);
+	}
+
+	if (!counter) {
+		/* Clear Tx FIFO */
+		omap_i2c_write_reg(dev, OMAP_I2C_BUF_REG,
+				OMAP_I2C_BUF_TXFIF_CLR);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
 static irqreturn_t
 omap_i2c_isr(int this_irq, void *dev_id)
 {
@@ -639,6 +670,7 @@  omap_i2c_isr(int this_irq, void *dev_id)
 	u16 bits;
 	u16 stat, w;
 	int err, count = 0;
+	int error;
 
 	if (dev->idle)
 		return IRQ_NONE;
@@ -647,7 +679,7 @@  omap_i2c_isr(int this_irq, void *dev_id)
 	while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) {
 		dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat);
 		if (count++ == 100) {
-			dev_warn(dev->dev, "Too much work in one IRQ\n");
+			dev_dbg(dev->dev, "Too much work in one IRQ\n");
 			break;
 		}
 
@@ -715,11 +747,22 @@  omap_i2c_isr(int this_irq, void *dev_id)
 							OMAP_I2C_BUFSTAT_REG);
 			}
 			while (num_bytes) {
-				num_bytes--;
 				w = 0;
 				if (dev->buf_len) {
+					if (cpu_is_omap34xx()) {
+						/* OMAP3430 Errata 1.153 */
+						error = omap_i2c_wait_for_xudf(dev);
+						if (error) {
+							omap_i2c_ack_stat(dev, stat &
+								(OMAP_I2C_STAT_XRDY |
+								 OMAP_I2C_STAT_XDR));
+							dev_err(dev->dev, "Transmit error\n");
+							omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_XUDF);
+
+							return IRQ_HANDLED;
+						}
+					}
 					w = *dev->buf++;
-					dev->buf_len--;
 					/* Data reg from  2430 is 8 bit wide */
 					if (!cpu_is_omap2430() &&
 							!cpu_is_omap34xx()) {
@@ -728,6 +771,10 @@  omap_i2c_isr(int this_irq, void *dev_id)
 							dev->buf_len--;
 						}
 					}
+					omap_i2c_write_reg(dev,
+						OMAP_I2C_DATA_REG, w);
+					num_bytes--;
+					dev->buf_len--;
 				} else {
 					if (stat & OMAP_I2C_STAT_XRDY)
 						dev_err(dev->dev,
@@ -739,7 +786,6 @@  omap_i2c_isr(int this_irq, void *dev_id)
 							"data to send\n");
 					break;
 				}
-				omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
 			}
 			omap_i2c_ack_stat(dev,
 				stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR));