[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 14:14:14 CET 2009


> Hello Joakim,
> 
> Joakim Tjernlund wrote:
> > Heiko Schocher <hs at denx.de> wrote on 28/01/2009 11:54:22:
> >> 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);
> > }
> 
> I dont know if it is possible to make this with the 8360 ...
> 
> Maybe we can do the following:
> - make the reset Sequence suggested from Freescale
> - checking the Status Register, if the Bus is now free (MBB Bit = 0)
>   If not, do again the reset Sequence. And this for max. 9 times.
> 
> What do you think?

It is better, but not complete I think.
Looks similar to our old reset procedure that didn't
handle the case when a slave was stuck writing a byte
but without the STOP condition:
        I2C_INIT;
        I2C_SCL(1);
        I2C_SDA(1);
        I2C_INIT;
        I2C_TRISTATE;
        for(j = 0; j < 9; j++) {
                I2C_SCL(0);
                I2C_DELAY;
                I2C_DELAY;
                I2C_SCL(1);
                I2C_DELAY;
                I2C_DELAY;
        }
        send_stop();
        if(!I2C_READ)
                printf("I2C SDA is low! I2C bus is stuck!!!\n");

You need to experiment and you probably need some help from
Freescale.

    Jocke


More information about the U-Boot mailing list