[U-Boot] [PATCH 2/2] Powerpc/i2c: Force i2c to become bus master out of reset

Joakim Tjernlund joakim.tjernlund at transmode.se
Thu Oct 27 09:25:36 CEST 2011


> From: <Chang-Ming.Huang at freescale.com>
>
> From: Jerry Huang <Chang-Ming.Huang at freescale.com>
>
> It is sometimes necessary to force the I2C module to become the I2C bus master
> out of reset and drive SCL(even though SDA may already be driven,
> which indicates that the bus is busy). This can occur when a system reset
> does not cause all I2C devices to be reset. Thus, SDA can be driven low
> by another I2C device while this I2C module is coming out of reset
> and stays low indefinitely. The following procedure can be used to
> force this I2C module to generate SCL so that the device driving SDA
> can finish its transaction.
>
> Signed-off-by: Jerry Huang <Chang-Ming.Huang at freescale.com>
> ---
>  drivers/i2c/fsl_i2c.c |   18 ++++++++++++++++++
>  1 files changed, 18 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c
> index 258be0a..007db70 100644
> --- a/drivers/i2c/fsl_i2c.c
> +++ b/drivers/i2c/fsl_i2c.c
> @@ -252,6 +252,24 @@ i2c_init(int speed, int slaveadd)
>        writeb(slaveadd << 1, &dev->adr);/* write slave address */
>        writeb(0x0, &dev->sr);      /* clear status register */
>        writeb(I2C_CR_MEN, &dev->cr);   /* start I2C controller */
> +
> +      /* Force I2C module to become bus master which can occure when
> +       * a system reset does not cause all I2C devices to be reset */
> +      udelay(5);
> +      if (readb(&dev->sr) & I2C_SR_MBB) {

You need to the sequence unconditionally, the slave can be stuck without
driving SCL.

> +         writeb(I2C_CR_MSTA, &dev->cr);
> +         udelay(5);
> +         writeb(I2C_CR_MEN | I2C_CR_MSTA, &dev->cr);
> +         udelay(5);
> +         readb(&dev->dr);
> +         udelay(5);
> +         writeb(I2C_CR_MEN, &dev->cr);
> +         udelay(5);
> +         if (readb(&dev->sr) & I2C_SR_MBB)
> +            debug("I2C%d: Drive SCL failed\n", i + 1);
> +         else
> +            debug("I2C%d: Drive SCL succeed\n", i + 1);
> +      }

The above sequence is different than the kernel version, why?

Kernel version below:
static void mpc_i2c_fixup(struct mpc_i2c *i2c)
 {
         int k;
         u32 delay_val = 1000000 / i2c->real_clk + 1;

         if (delay_val < 2)
                 delay_val = 2;

         for (k = 9; k; k--) {
                 writeccr(i2c, 0);
                 writeccr(i2c, CCR_MSTA | CCR_MTX | CCR_MEN);
                 udelay(delay_val);
                 writeccr(i2c, CCR_MEN);
                 udelay(delay_val << 1);
         }
 }





More information about the U-Boot mailing list