Merge branch 'master' of git://git.denx.de/u-boot-i2c
authorTom Rini <trini@ti.com>
Mon, 10 Nov 2014 14:20:52 +0000 (09:20 -0500)
committerTom Rini <trini@ti.com>
Mon, 10 Nov 2014 21:25:29 +0000 (16:25 -0500)
arch/powerpc/include/asm/ppc4xx-i2c.h
drivers/i2c/fsl_i2c.c
drivers/i2c/ppc4xx_i2c.c
drivers/i2c/rcar_i2c.c

index 09189cf..df97f17 100644 (file)
@@ -72,6 +72,8 @@ struct ppc4xx_i2c {
 #define IIC_EXTSTS_XFRA                0x01
 #define IIC_EXTSTS_ICT         0x02
 #define IIC_EXTSTS_LA          0x04
+#define IIC_EXTSTS_BCS_MASK    0x70
+#define IIC_EXTSTS_BCS_FREE    0x40
 
 /* XTCNTLSS Register Bit definition */
 #define IIC_XTCNTLSS_SRST      0x01
index 811033b..7bb1702 100644 (file)
@@ -38,7 +38,7 @@
  * generic value.
  */
 #ifndef CONFIG_I2C_TIMEOUT
-#define CONFIG_I2C_TIMEOUT     10000
+#define CONFIG_I2C_TIMEOUT     100000
 #endif
 
 #define I2C_READ_BIT  1
index e7a15ba..df88885 100644 (file)
@@ -158,8 +158,7 @@ static void ppc4xx_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
  *
  * Typical case is a Write of an addr followd by a Read. The
  * IBM FAQ does not cover this. On the last byte of the write
- * we don't set the creg CHT bit, and on the first bytes of the
- * read we set the RPST bit.
+ * we don't set the creg CHT bit but the RPST bit.
  *
  * It does not support address only transfers, there must be
  * a data part. If you want to write the address yourself, put
@@ -247,6 +246,10 @@ static int _i2c_transfer(struct i2c_adapter *adap,
                if ((!cmd_type && (ptr == addr)) || ((tran + bc) != cnt))
                        creg |= IIC_CNTL_CHT;
 
+               /* last part of address, prepare for repeated start on read */
+               if (cmd_type && (ptr == addr) && ((tran + bc) == cnt))
+                       creg |= IIC_CNTL_RPST;
+
                if (reading) {
                        creg |= IIC_CNTL_READ;
                } else {
@@ -286,6 +289,27 @@ static int _i2c_transfer(struct i2c_adapter *adap,
                        /* Transfer aborted? */
                        if (status & IIC_EXTSTS_XFRA)
                                result = IIC_NOK_XFRA;
+                       /* Is bus free?
+                        * If error happened during combined xfer
+                        * IIC interface is usually stuck in some strange
+                        * state without a valid stop condition.
+                        * Brute, but working: generate stop, then soft reset.
+                        */
+                       if ((status & IIC_EXTSTS_BCS_MASK)
+                           != IIC_EXTSTS_BCS_FREE){
+                               u8 mdcntl = in_8(&i2c->mdcntl);
+
+                               /* Generate valid stop condition */
+                               out_8(&i2c->xtcntlss, IIC_XTCNTLSS_SRST);
+                               out_8(&i2c->directcntl, IIC_DIRCNTL_SCC);
+                               udelay(10);
+                               out_8(&i2c->directcntl,
+                                     IIC_DIRCNTL_SCC | IIC_DIRCNTL_SDAC);
+                               out_8(&i2c->xtcntlss, 0);
+
+                               ppc4xx_i2c_init(adap, (mdcntl & IIC_MDCNTL_FSM)
+                                               ? 400000 : 100000, 0);
+                       }
                } else if ( status & IIC_STS_PT) {
                        result = IIC_NOK_TOUT;
                }
@@ -314,8 +338,6 @@ static int _i2c_transfer(struct i2c_adapter *adap,
                        cnt = data_len;
                        tran = 0;
                        reading = cmd_type;
-                       if (reading)
-                               creg = IIC_CNTL_RPST;
                }
        }
        return result;
index 50cebd6..90ad116 100644 (file)
@@ -119,10 +119,10 @@ rcar_i2c_raw_read(struct rcar_i2c *dev, u8 chip, uint addr)
 
        /* set slave address, receive */
        writel((chip << 1) | 1, &dev->icmar);
-       /* clear status */
-       writel(0, &dev->icmsr);
        /* start master receive */
        writel(MCR_MDBS | MCR_MIE | MCR_ESG, &dev->icmcr);
+       /* clear status */
+       writel(0, &dev->icmsr);
 
        while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDR))
                != (MSR_MAT | MSR_MDR))