Commit 94327d00 authored by Frank Shew's avatar Frank Shew Committed by Ben Dooks

i2c: Blackfin TWI: fix transfer errors with repeat start

We have a custom BF537 board with an I2C RTC (MAX DS3231) running
uclinux 2007R1 for some time. Recently during migration to 2008R1.5-RC3
we losted access to the RTC. The RTC driver calls 'i2c_transfer()' which
in turns calls 'bfin_twi_master_xfer()' in i2c-bfin-twi.c.

Compared with 2007R1, it looks like the 2008R1.5 version of i2c-bin-twi.c
has a new mode 'TWI_I2C-MODE_REPEAT' which corresponds to the Repeat Start
Condition described in the HRM. However, according to the HRM, at XMIT or
RECV interrupt and when the data count is 0, not only is the RESTART bit
supposed to be set, but MDIR must also be set if the next operation is a
receive sequence, and cleared if not. Currently there is no code that looks
at the I2C_M_RD bit in the flag from the next cur_msg and set/clear the MDIR
flag accordingly at the same time that the RSTART bit is set. Instead, MDIR
is set or cleared (by OR'ing with 0?) after the RESTART bit has been cleared
during handling of MCOMP interrupt.

It appears that this is causing our failure with reading the RTC, as a
quick patch to set/clear MDIR when RESTART is set seem to solve our problem.
Signed-off-by: default avatarFrank Shew <fshew@geometrics.com>
Signed-off-by: default avatarSonic Zhang <sonic.zhang@analog.com>
Signed-off-by: default avatarMike Frysinger <vapier@gentoo.org>
Signed-off-by: default avatarBryan Wu <cooloney@kernel.org>
[ben-linux@fluff.org: shorted subject]
Signed-off-by: default avatarBen Dooks <ben-linux@fluff.org>
parent 57a8f32e
...@@ -104,9 +104,14 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface) ...@@ -104,9 +104,14 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
write_MASTER_CTL(iface, write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | STOP); read_MASTER_CTL(iface) | STOP);
else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
iface->cur_msg+1 < iface->msg_num) iface->cur_msg + 1 < iface->msg_num) {
write_MASTER_CTL(iface, if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
read_MASTER_CTL(iface) | RSTART); write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | RSTART | MDIR);
else
write_MASTER_CTL(iface,
(read_MASTER_CTL(iface) | RSTART) & ~MDIR);
}
SSYNC(); SSYNC();
/* Clear status */ /* Clear status */
write_INT_STAT(iface, XMTSERV); write_INT_STAT(iface, XMTSERV);
...@@ -134,9 +139,13 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface) ...@@ -134,9 +139,13 @@ static void bfin_twi_handle_interrupt(struct bfin_twi_iface *iface)
read_MASTER_CTL(iface) | STOP); read_MASTER_CTL(iface) | STOP);
SSYNC(); SSYNC();
} else if (iface->cur_mode == TWI_I2C_MODE_REPEAT && } else if (iface->cur_mode == TWI_I2C_MODE_REPEAT &&
iface->cur_msg+1 < iface->msg_num) { iface->cur_msg + 1 < iface->msg_num) {
write_MASTER_CTL(iface, if (iface->pmsg[iface->cur_msg + 1].flags & I2C_M_RD)
read_MASTER_CTL(iface) | RSTART); write_MASTER_CTL(iface,
read_MASTER_CTL(iface) | RSTART | MDIR);
else
write_MASTER_CTL(iface,
(read_MASTER_CTL(iface) | RSTART) & ~MDIR);
SSYNC(); SSYNC();
} }
/* Clear interrupt source */ /* Clear interrupt source */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment