summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarek Vasut <marex@denx.de>2016-10-20 16:48:28 +0200
committerHeiko Schocher <hs@denx.de>2016-10-24 18:15:47 +0200
commitb03380805b5a184b7017dc428a53c8e1e9c9f99c (patch)
tree797bf98c04716ed0719ee71f63f396633e39ce63
parent5ac5861c4ba851b473e6a24940b412b397627d8d (diff)
downloadu-boot-b03380805b5a184b7017dc428a53c8e1e9c9f99c.tar.gz
u-boot-b03380805b5a184b7017dc428a53c8e1e9c9f99c.tar.xz
u-boot-b03380805b5a184b7017dc428a53c8e1e9c9f99c.zip
i2c: designware: Avoid overwriting the cmd_data register
Make sure the driver writes the cmd_data register only once per read transfer instead of doing so potentially repeatedly. In case the read transfer didn't finish quickly enough, the loop in the driver code would spin fast enough to write the same value into the cmd_data register again before re-checking whether the transfer completed, which would cause another spurious read transfer on the bus. Signed-off-by: Marek Vasut <marex@denx.de> Cc: Stefan Roese <sr@denx.de> Cc: Alexey Brodkin <abrodkin@synopsys.com> Cc: Heiko Schocher <hs@denx.de> Cc: Dinh Nguyen <dinguyen@opensource.altera.com> Cc: Chin Liang See <clsee@altera.com>
-rw-r--r--drivers/i2c/designware_i2c.c23
1 files changed, 17 insertions, 6 deletions
diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c
index e60fd0a419..c68ff6420b 100644
--- a/drivers/i2c/designware_i2c.c
+++ b/drivers/i2c/designware_i2c.c
@@ -249,6 +249,7 @@ static int __dw_i2c_read(struct i2c_regs *i2c_base, u8 dev, uint addr,
int alen, u8 *buffer, int len)
{
unsigned long start_time_rx;
+ unsigned int active = 0;
#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
/*
@@ -274,18 +275,28 @@ static int __dw_i2c_read(struct i2c_regs *i2c_base, u8 dev, uint addr,
start_time_rx = get_timer(0);
while (len) {
- if (len == 1)
- writel(IC_CMD | IC_STOP, &i2c_base->ic_cmd_data);
- else
- writel(IC_CMD, &i2c_base->ic_cmd_data);
+ if (!active) {
+ /*
+ * Avoid writing to ic_cmd_data multiple times
+ * in case this loop spins too quickly and the
+ * ic_status RFNE bit isn't set after the first
+ * write. Subsequent writes to ic_cmd_data can
+ * trigger spurious i2c transfer.
+ */
+ if (len == 1)
+ writel(IC_CMD | IC_STOP, &i2c_base->ic_cmd_data);
+ else
+ writel(IC_CMD, &i2c_base->ic_cmd_data);
+ active = 1;
+ }
if (readl(&i2c_base->ic_status) & IC_STATUS_RFNE) {
*buffer++ = (uchar)readl(&i2c_base->ic_cmd_data);
len--;
start_time_rx = get_timer(0);
-
+ active = 0;
} else if (get_timer(start_time_rx) > I2C_BYTE_TO) {
- return 1;
+ return 1;
}
}