summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/include/asm/ppc4xx-i2c.h2
-rw-r--r--drivers/i2c/fsl_i2c.c2
-rw-r--r--drivers/i2c/ppc4xx_i2c.c30
-rw-r--r--drivers/i2c/rcar_i2c.c4
4 files changed, 31 insertions, 7 deletions
diff --git a/arch/powerpc/include/asm/ppc4xx-i2c.h b/arch/powerpc/include/asm/ppc4xx-i2c.h
index 09189cf..df97f17 100644
--- a/arch/powerpc/include/asm/ppc4xx-i2c.h
+++ b/arch/powerpc/include/asm/ppc4xx-i2c.h
@@ -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
diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c
index 811033b..7bb1702 100644
--- a/drivers/i2c/fsl_i2c.c
+++ b/drivers/i2c/fsl_i2c.c
@@ -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
diff --git a/drivers/i2c/ppc4xx_i2c.c b/drivers/i2c/ppc4xx_i2c.c
index e7a15ba..df88885 100644
--- a/drivers/i2c/ppc4xx_i2c.c
+++ b/drivers/i2c/ppc4xx_i2c.c
@@ -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;
diff --git a/drivers/i2c/rcar_i2c.c b/drivers/i2c/rcar_i2c.c
index 50cebd6..90ad116 100644
--- a/drivers/i2c/rcar_i2c.c
+++ b/drivers/i2c/rcar_i2c.c
@@ -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))