Skip to content

Commit 171e23e

Browse files
jhnikulaWolfram Sang
authored andcommitted
i2c: designware: Avoid aborted transfers with fast reacting I2C slaves
I2C DesignWare may abort transfer with arbitration lost if I2C slave pulls SDA down quickly after falling edge of SCL. Reason for this is unknown but after trial and error it was found this can be avoided by enabling non-zero SDA RX hold time for the receiver. By the specification SDA RX hold time extends incoming SDA low to high transition by n * ic_clk cycles but only when SCL is high. However it seems to help avoid above faulty arbitration lost error. Bits 23:16 in IC_SDA_HOLD register define the SDA RX hold time for the receiver. Be conservative and enable 1 ic_clk cycle long hold time in case boot firmware hasn't set it up. Reported-by: Jukka Laitinen <jukka.laitinen@intel.com> Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Tested-by: Jukka Laitinen <jukka.laitinen@intel.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
1 parent ba9ad2a commit 171e23e

1 file changed

Lines changed: 14 additions & 3 deletions

File tree

drivers/i2c/busses/i2c-designware-core.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@
9595
#define DW_IC_STATUS_TFE BIT(2)
9696
#define DW_IC_STATUS_MST_ACTIVITY BIT(5)
9797

98+
#define DW_IC_SDA_HOLD_RX_SHIFT 16
99+
#define DW_IC_SDA_HOLD_RX_MASK GENMASK(23, DW_IC_SDA_HOLD_RX_SHIFT)
100+
98101
#define DW_IC_ERR_TX_ABRT 0x1
99102

100103
#define DW_IC_TAR_10BITADDR_MASTER BIT(12)
@@ -420,12 +423,20 @@ int i2c_dw_init(struct dw_i2c_dev *dev)
420423
/* Configure SDA Hold Time if required */
421424
reg = dw_readl(dev, DW_IC_COMP_VERSION);
422425
if (reg >= DW_IC_SDA_HOLD_MIN_VERS) {
423-
if (dev->sda_hold_time) {
424-
dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
425-
} else {
426+
if (!dev->sda_hold_time) {
426427
/* Keep previous hold time setting if no one set it */
427428
dev->sda_hold_time = dw_readl(dev, DW_IC_SDA_HOLD);
428429
}
430+
/*
431+
* Workaround for avoiding TX arbitration lost in case I2C
432+
* slave pulls SDA down "too quickly" after falling egde of
433+
* SCL by enabling non-zero SDA RX hold. Specification says it
434+
* extends incoming SDA low to high transition while SCL is
435+
* high but it apprears to help also above issue.
436+
*/
437+
if (!(dev->sda_hold_time & DW_IC_SDA_HOLD_RX_MASK))
438+
dev->sda_hold_time |= 1 << DW_IC_SDA_HOLD_RX_SHIFT;
439+
dw_writel(dev, dev->sda_hold_time, DW_IC_SDA_HOLD);
429440
} else {
430441
dev_warn(dev->dev,
431442
"Hardware too old to adjust SDA hold time.\n");

0 commit comments

Comments
 (0)