On Thu, Feb 19, 2015 at 12:06:49PM -0600, Felipe Balbi wrote: > If either SCL or SDA are stuck low, we need to > recover the bus using the procedure described > on section 3.1.16 of the I2C specification. > > Note that we're trying to implement the procedure > exactly as described by that section. First we > check which line is stuck low, then implement > one or the other procedure. If SDA recovery procedure > fails, we reset our IP in an attempt to make it work. > > Signed-off-by: Felipe Balbi <balbi@xxxxxx> > --- > > Tested with AM437x IDK, AM437x SK, BeagleBoneBlack and Beagle X15 with > 1000 iterations of i2cdetect on all available buses. > > That said, I couldn't get any device to hold the bus busy so I could > see this working. If anybody has any good way of forcing a condition > so that we need bus recovery, I'd be glad to look at. > > cheers > > drivers/i2c/busses/i2c-omap.c | 71 +++++++++++++++++++++++++++++++++++++++++-- > 1 file changed, 69 insertions(+), 2 deletions(-) > > diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c > index 0e894193accf..c3e4da751adf 100644 > --- a/drivers/i2c/busses/i2c-omap.c > +++ b/drivers/i2c/busses/i2c-omap.c > @@ -472,6 +472,73 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) > return 0; > } > > +static void omap_i2c_clock_pulse(struct omap_i2c_dev *dev) > +{ > + u32 reg; > + int i; > + > + /* Enable testmode */ > + reg = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG); > + reg |= OMAP_I2C_SYSTEST_ST_EN; > + omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg); > + > + for (i = 0; i < 9; i++) { > + reg |= OMAP_I2C_SYSTEST_SCL_O; > + omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg); > + mdelay(100); > + reg &= ~OMAP_I2C_SYSTEST_SCL_O; > + omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg); > + mdelay(100); > + } > + > + /* Disable testmode */ > + reg &= ~OMAP_I2C_SYSTEST_ST_EN; > + omap_i2c_write_reg(dev, OMAP_I2C_SYSTEST_REG, reg); > +} > + > +static void omap_i2c_bus_recover(struct omap_i2c_dev *dev) > +{ > + u32 reg1; > + u32 reg2; > + > + /* > + * First differentiate SCL stuck low from SDA stuck low using our > + * SYSTEST register. Depending on which line is stuck low, we will > + * either Reset our I2C IP (SCL stuck low) or drive 9 clock pulses on > + * SCL (SDA stuck low) to tell the device to release the bus. > + * > + * If, after 9 clock pulses on SCL device still doesn't release the > + * bus, there's nothing more we can do; we will still try to Reset > + * our I2C IP anyway. > + */ > + > + reg1 = omap_i2c_read_reg(dev, OMAP_I2C_SYSTEST_REG); > + msleep(1); Hmm... I wonder if this msleep() should be scaled based on i2c bus frequency. -- balbi
Attachment:
signature.asc
Description: Digital signature