[U-Boot] [PATCH 19/31] i2c, mpc83xx: add CONFIG_SYS_I2C_INIT_BOARD for fsl_i2c

Joakim Tjernlund Joakim.Tjernlund at transmode.se
Wed Jan 28 12:28:36 CET 2009


Heiko Schocher <hs at denx.de> wrote on 28/01/2009 11:54:22:

> Hello Joakim,
> 
> Joakim Tjernlund wrote:
> >> This patch adds the possibility to call a board specific
> >> i2c bus reset routine for the fsl_i2c bus driver, and adds
> >> this option for the keymile kmeter1 board.
> >>
> [...]
> >> @@ -478,6 +480,17 @@ static int i2c_make_abort (void)
> >>   */
> >>  void i2c_init_board(void)
> >>  {
> >> +#if defined(CONFIG_KMETER1)
> >> +   struct fsl_i2c *dev;
> >> +   dev = (struct fsl_i2c *) (CONFIG_SYS_IMMR + 
CONFIG_SYS_I2C_OFFSET);
> >> +   uchar   dummy;
> >> +
> >> +   out_8 (&dev->cr, (I2C_CR_MSTA));
> >> +   out_8 (&dev->cr, (I2C_CR_MEN | I2C_CR_MSTA));
> >> +   dummy = in_8(&dev->dr);
> >> +   out_8 (&dev->cr, (I2C_CR_MEN));
> > 
> > Are you sure this will generate a proper I2C reset sequence? We also
> > use this controller and I tried to do it too but didn't find a way. I 
then 
> > asked
> > Freescale and they could not come up with a solution either.
> 
> ?
> This routine is decribed in the MPC8260ERM.pdf §15.5.7 on page 15-23
> from Freescale!
> 
> 15.5.7 Generation of SCLn when SDAn is Negated
> It is sometimes necessary to force the I2C module to become the I2C bus 
master out of reset and drive
> SCLn (even though SDAn 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, 
SDAn can be negated low by another
> I2C device while this I2C module is coming out of reset and will stay 
low indefinitely. The following
> procedure can be used to force this I2C module to generate SCLn so that 
the device driving SDAn can
> finish its transaction:
> 1. Disable the I2C module and set the master bit by setting I2CnCR to 
0x20.
> 2. Enable the I2C module by setting I2CnCR to 0xA0.
> 3. Read I2CnDR.
> 4. Return the I2C module to slave mode by setting I2CnCR to 0x80.
> 
> And this worked fine on our Hardware ...

Ahh, memory slowly returns. The problem is that this does not generate a 
reset
sequence that will work in all cases. Consider the case when the
CPU is reset half-way through writing or reading a byte from the device.

I once researched this(can't remember the exact details now) but the
only reset sequence that works in all cases is:

static void send_start(void)
{
        I2C_DELAY;
        I2C_TRISTATE;
        I2C_SDA(1);
        I2C_DELAY;
        I2C_SCL(1);
        I2C_DELAY;
        I2C_SDA(0);
        I2C_ACTIVE;
        I2C_DELAY;
}

static void send_stop(void)
{
        I2C_SCL(0);
        I2C_DELAY;
        I2C_SDA(0);
        I2C_ACTIVE;
        I2C_DELAY;
        I2C_SCL(1);
        I2C_DELAY;
        I2C_TRISTATE;
        I2C_SDA(1);
        I2C_DELAY;
}

/*-----------------------------------------------------------------------
 * Send a reset sequence consisting of 9 clocks with the data signal high
 * to clock any confused device back into an idle state.  Also send a
 * <stop> at the end of the sequence for belts & suspenders.
 */
void tm_i2c_reset(int bus)
{
        int j;

        I2C_INIT;
        I2C_TRISTATE;
        for(j = 0; j < 9; j++) {
                if(I2C_READ)
                        send_start();
                I2C_SCL(0);
                I2C_DELAY;
                I2C_TRISTATE;
                I2C_SDA(1);
                I2C_DELAY;
                I2C_SCL(1);
                I2C_DELAY;
                I2C_DELAY;
        }
        send_stop();
        if(!I2C_READ)
                printf("I2C SDA is low! I2C bus:%d is stuck!!!\n", bus);
}



More information about the U-Boot mailing list