
Heiko Schocher hs@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:
- Disable the I2C module and set the master bit by setting I2CnCR to
0x20.
- Enable the I2C module by setting I2CnCR to 0xA0.
- Read I2CnDR.
- 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); }