Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i2c: mpc: Wait for STOP to hit the bus

mpc_i2c_stop() only initiates STOP but does not wait for it to
hit the I2C bus. This is a problem when using I2C devices which
uses fairly long clock stretching just before STOP if you also
have an i2c-mux which may switch to another bus before STOP has
been processed.

Signed-off-by: Joakim Tjernlund <Joakim.Tjernlund@transmode.se>
Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>

authored by

Joakim Tjernlund and committed by
Wolfram Sang
0c25aefa 2bdbfa9c

+17 -1
+17 -1
drivers/i2c/busses/i2c-mpc.c
··· 576 576 mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i); 577 577 } 578 578 } 579 - mpc_i2c_stop(i2c); 579 + mpc_i2c_stop(i2c); /* Initiate STOP */ 580 + orig_jiffies = jiffies; 581 + /* Wait until STOP is seen, allow up to 1 s */ 582 + while (readb(i2c->base + MPC_I2C_SR) & CSR_MBB) { 583 + if (time_after(jiffies, orig_jiffies + HZ)) { 584 + u8 status = readb(i2c->base + MPC_I2C_SR); 585 + 586 + dev_dbg(i2c->dev, "timeout\n"); 587 + if ((status & (CSR_MCF | CSR_MBB | CSR_RXAK)) != 0) { 588 + writeb(status & ~CSR_MAL, 589 + i2c->base + MPC_I2C_SR); 590 + mpc_i2c_fixup(i2c); 591 + } 592 + return -EIO; 593 + } 594 + cond_resched(); 595 + } 580 596 return (ret < 0) ? ret : num; 581 597 } 582 598