[U-Boot] [PATCH 1/2] i2c: i2c_cdns: Fix clearing of all interrupts

From: Siva Durga Prasad Paladugu siva.durga.paladugu@xilinx.com
The arbitration lost interrupt was not getting cleared while clearing interrupts. This patch fixes this by adding arbitration lost interrupt as well during clear. This patch also removes hardcoded value and defined a macro for it.
Signed-off-by: Siva Durga Prasad Paladugu siva.durga.paladugu@xilinx.com Signed-off-by: Michal Simek michal.simek@xilinx.com ---
drivers/i2c/i2c-cdns.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c index 4330d28c2b0d..b391c0ba49b8 100644 --- a/drivers/i2c/i2c-cdns.c +++ b/drivers/i2c/i2c-cdns.c @@ -64,6 +64,16 @@ struct cdns_i2c_regs { #define CDNS_I2C_INTERRUPT_RXUNF 0x00000080 #define CDNS_I2C_INTERRUPT_ARBLOST 0x00000200
+#define CDNS_I2C_INTERRUPTS_MASK (CDNS_I2C_INTERRUPT_COMP | \ + CDNS_I2C_INTERRUPT_DATA | \ + CDNS_I2C_INTERRUPT_NACK | \ + CDNS_I2C_INTERRUPT_TO | \ + CDNS_I2C_INTERRUPT_SLVRDY | \ + CDNS_I2C_INTERRUPT_RXOVF | \ + CDNS_I2C_INTERRUPT_TXOVF | \ + CDNS_I2C_INTERRUPT_RXUNF | \ + CDNS_I2C_INTERRUPT_ARBLOST) + #define CDNS_I2C_FIFO_DEPTH 16 #define CDNS_I2C_TRANSFER_SIZE_MAX 255 /* Controller transfer limit */ #define CDNS_I2C_TRANSFER_SIZE (CDNS_I2C_TRANSFER_SIZE_MAX - 3) @@ -241,7 +251,7 @@ static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data, setbits_le32(®s->control, CDNS_I2C_CONTROL_HOLD);
/* Clear the interrupts in status register */ - writel(0xFF, ®s->interrupt_status); + writel(CDNS_I2C_INTERRUPTS_MASK, ®s->interrupt_status);
writel(addr, ®s->address);

From: Siva Durga Prasad Paladugu siva.durga.paladugu@xilinx.com
This patch adds support for handling arbitration lost in case of multi master mode. When an arbitration lost is detected, it retries for 10 times before failing.
Signed-off-by: Siva Durga Prasad Paladugu siva.durga.paladugu@xilinx.com Signed-off-by: Michal Simek michal.simek@xilinx.com ---
drivers/i2c/i2c-cdns.c | 66 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 13 deletions(-)
diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c index b391c0ba49b8..1af94d176145 100644 --- a/drivers/i2c/i2c-cdns.c +++ b/drivers/i2c/i2c-cdns.c @@ -80,6 +80,8 @@ struct cdns_i2c_regs {
#define CDNS_I2C_BROKEN_HOLD_BIT BIT(0)
+#define CDNS_I2C_ARB_LOST_MAX_RETRIES 10 + #ifdef DEBUG static void cdns_i2c_debug_status(struct cdns_i2c_regs *cdns_i2c) { @@ -234,11 +236,17 @@ static int cdns_i2c_set_bus_speed(struct udevice *dev, unsigned int speed) return 0; }
+static inline u32 is_arbitration_lost(struct cdns_i2c_regs *regs) +{ + return (readl(®s->interrupt_status) & CDNS_I2C_INTERRUPT_ARBLOST); +} + static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data, u32 len) { u8 *cur_data = data; struct cdns_i2c_regs *regs = i2c_bus->regs; + u32 ret;
/* Set the controller in Master transmit mode and clear FIFO */ setbits_le32(®s->control, CDNS_I2C_CONTROL_CLR_FIFO); @@ -255,25 +263,38 @@ static int cdns_i2c_write_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
writel(addr, ®s->address);
- while (len--) { + while (len-- && !is_arbitration_lost(regs)) { writel(*(cur_data++), ®s->data); if (readl(®s->transfer_size) == CDNS_I2C_FIFO_DEPTH) { - if (!cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP)) { - /* Release the bus */ - clrbits_le32(®s->control, - CDNS_I2C_CONTROL_HOLD); - return -ETIMEDOUT; - } + ret = cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP | + CDNS_I2C_INTERRUPT_ARBLOST); + if (ret & CDNS_I2C_INTERRUPT_ARBLOST) + return -EAGAIN; + if (ret & CDNS_I2C_INTERRUPT_COMP) + continue; + /* Release the bus */ + clrbits_le32(®s->control, + CDNS_I2C_CONTROL_HOLD); + return -ETIMEDOUT; } }
+ if (len && is_arbitration_lost(regs)) + return -EAGAIN; + /* All done... release the bus */ if (!i2c_bus->hold_flag) clrbits_le32(®s->control, CDNS_I2C_CONTROL_HOLD);
/* Wait for the address and data to be sent */ - if (!cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP)) + ret = cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP | + CDNS_I2C_INTERRUPT_ARBLOST); + if (!(ret & (CDNS_I2C_INTERRUPT_ARBLOST | + CDNS_I2C_INTERRUPT_COMP))) return -ETIMEDOUT; + if (ret & CDNS_I2C_INTERRUPT_ARBLOST) + return -EAGAIN; + return 0; }
@@ -289,6 +310,7 @@ static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data, struct cdns_i2c_regs *regs = i2c_bus->regs; int curr_recv_count; int updatetx, hold_quirk; + u32 ret;
/* Check the hardware can handle the requested bytes */ if ((recv_count < 0)) @@ -317,7 +339,7 @@ static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data,
hold_quirk = (i2c_bus->quirks & CDNS_I2C_BROKEN_HOLD_BIT) && updatetx;
- while (recv_count) { + while (recv_count && !is_arbitration_lost(regs)) { while (readl(®s->status) & CDNS_I2C_STATUS_RXDV) { if (recv_count < CDNS_I2C_FIFO_DEPTH && !i2c_bus->hold_flag) { @@ -366,8 +388,13 @@ static int cdns_i2c_read_data(struct i2c_cdns_bus *i2c_bus, u32 addr, u8 *data, }
/* Wait for the address and data to be sent */ - if (!cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP)) + ret = cdns_i2c_wait(regs, CDNS_I2C_INTERRUPT_COMP | + CDNS_I2C_INTERRUPT_ARBLOST); + if (!(ret & (CDNS_I2C_INTERRUPT_ARBLOST | + CDNS_I2C_INTERRUPT_COMP))) return -ETIMEDOUT; + if (ret & CDNS_I2C_INTERRUPT_ARBLOST) + return -EAGAIN;
return 0; } @@ -376,8 +403,11 @@ static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs) { struct i2c_cdns_bus *i2c_bus = dev_get_priv(dev); - int ret, count; + int ret = 0; + int count; bool hold_quirk; + struct i2c_msg *message = msg; + int num_msgs = nmsgs;
hold_quirk = !!(i2c_bus->quirks & CDNS_I2C_BROKEN_HOLD_BIT);
@@ -403,7 +433,8 @@ static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, }
debug("i2c_xfer: %d messages\n", nmsgs); - for (; nmsgs > 0; nmsgs--, msg++) { + for (u8 retry = 0; retry < CDNS_I2C_ARB_LOST_MAX_RETRIES && + nmsgs > 0; nmsgs--, msg++) { debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); if (msg->flags & I2C_M_RD) { ret = cdns_i2c_read_data(i2c_bus, msg->addr, msg->buf, @@ -412,13 +443,22 @@ static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, ret = cdns_i2c_write_data(i2c_bus, msg->addr, msg->buf, msg->len); } + if (ret == -EAGAIN) { + msg = message; + nmsgs = num_msgs; + retry++; + printf("%s,arbitration lost, retrying:%d\n", __func__, + retry); + continue; + } + if (ret) { debug("i2c_write: error sending\n"); return -EREMOTEIO; } }
- return 0; + return ret; }
static int cdns_i2c_ofdata_to_platdata(struct udevice *dev)

Hello Michal,
Am 07.03.2019 um 11:52 schrieb Michal Simek:
From: Siva Durga Prasad Paladugu siva.durga.paladugu@xilinx.com
This patch adds support for handling arbitration lost in case of multi master mode. When an arbitration lost is detected, it retries for 10 times before failing.
Signed-off-by: Siva Durga Prasad Paladugu siva.durga.paladugu@xilinx.com Signed-off-by: Michal Simek michal.simek@xilinx.com
drivers/i2c/i2c-cdns.c | 66 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 53 insertions(+), 13 deletions(-)
Reviewed-by: Heiko Schocher hs@denx.de
bye, Heiko

Hello Michal,
Am 07.03.2019 um 11:52 schrieb Michal Simek:
From: Siva Durga Prasad Paladugu siva.durga.paladugu@xilinx.com
The arbitration lost interrupt was not getting cleared while clearing interrupts. This patch fixes this by adding arbitration lost interrupt as well during clear. This patch also removes hardcoded value and defined a macro for it.
Signed-off-by: Siva Durga Prasad Paladugu siva.durga.paladugu@xilinx.com Signed-off-by: Michal Simek michal.simek@xilinx.com
drivers/i2c/i2c-cdns.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-)
Reviewed-by: Heiko Schocher hs@denx.de
bye, Heiko
participants (2)
-
Heiko Schocher
-
Michal Simek