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

Joakim Tjernlund
2009-01-28T13:14:14Z

> Hello Joakim,
>
> Joakim Tjernlund wrote:
> > Heiko Schocher <hs@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