summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDomen Puncer <domen.puncer@telargo.com>2007-07-12 14:12:31 +0200
committerJean Delvare <khali@hyperion.delvare>2007-07-12 14:12:31 +0200
commit254db9b5e7b1b0d38a4f177c2c23a5685c78221a (patch)
tree10dffafbbf287d61194fbe885a7e977de2d88242
parent1b144df1d7d69d6dd3394205933c8951dd8b6784 (diff)
i2c-mpc: work around missing-9th-clock-pulse bug
Work around a problem reported on: http://ozlabs.org/pipermail/linuxppc-embedded/2005-July/019038.html Without this patch I2C on mpc5200 becomes unusable after a while. Tested on mpc5200 boards by Matthias Fechner and me. Signed-off-by: Domen Puncer <domen.puncer@telargo.com> Signed-off-by: Jean Delvare <khali@linux-fr.org>
-rw-r--r--drivers/i2c/busses/i2c-mpc.c23
1 files changed, 23 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index a769efc7f408..851c3ed513d0 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -74,6 +74,25 @@ static irqreturn_t mpc_i2c_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
+/* Sometimes 9th clock pulse isn't generated, and slave doesn't release
+ * the bus, because it wants to send ACK.
+ * Following sequence of enabling/disabling and sending start/stop generates
+ * the pulse, so it's all OK.
+ */
+static void mpc_i2c_fixup(struct mpc_i2c *i2c)
+{
+ writeccr(i2c, 0);
+ udelay(30);
+ writeccr(i2c, CCR_MEN);
+ udelay(30);
+ writeccr(i2c, CCR_MSTA | CCR_MTX);
+ udelay(30);
+ writeccr(i2c, CCR_MSTA | CCR_MTX | CCR_MEN);
+ udelay(30);
+ writeccr(i2c, CCR_MEN);
+ udelay(30);
+}
+
static int i2c_wait(struct mpc_i2c *i2c, unsigned timeout, int writing)
{
unsigned long orig_jiffies = jiffies;
@@ -153,6 +172,7 @@ static void mpc_i2c_start(struct mpc_i2c *i2c)
static void mpc_i2c_stop(struct mpc_i2c *i2c)
{
writeccr(i2c, CCR_MEN);
+ writeccr(i2c, 0);
}
static int mpc_write(struct mpc_i2c *i2c, int target,
@@ -245,6 +265,9 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
}
if (time_after(jiffies, orig_jiffies + HZ)) {
pr_debug("I2C: timeout\n");
+ if (readb(i2c->base + MPC_I2C_SR) ==
+ (CSR_MCF | CSR_MBB | CSR_RXAK))
+ mpc_i2c_fixup(i2c);
return -EIO;
}
schedule();