From d50e29662f78be5e0843ce1e0c5d818b6a74cc98 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marek=20Beh=C3=BAn?= Date: Thu, 2 May 2019 16:53:38 +0200 Subject: [PATCH] i2c: mvtwsi: fix reading status register after interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The twsi_wait function reads the control register for interrupt flag, and if interrupt flag is present, it immediately reads status register. On our device this sometimes causes bad value being read from status register, as if the value was not yet updated. My theory is that the controller does approximately this: 1. sets interrupt flag in control register, 2. sets the value of status register, 3. causes an interrupt In U-Boot we do not use interrupts, so I think that it is possible that sometimes the status register in the twsi_wait function is read between points 1 and 2. The bug does not appear if I add a small delay before reading status register. Wait 100ns (which in U-Boot currently means 1 us, because ndelay(i) function calls udelay(DIV_ROUND_UP(i, 1000))) before reading the status register. Signed-off-by: Marek Behún Reviewed-by: Heiko Schocher Reviewed-by: Stefan Roese Cc: Mario Six Cc: Baruch Siach Signed-off-by: Stefan Roese --- drivers/i2c/mvtwsi.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/i2c/mvtwsi.c b/drivers/i2c/mvtwsi.c index 74ac0a4aa7..0a2dafcec6 100644 --- a/drivers/i2c/mvtwsi.c +++ b/drivers/i2c/mvtwsi.c @@ -271,6 +271,17 @@ static int twsi_wait(struct mvtwsi_registers *twsi, int expected_status, do { control = readl(&twsi->control); if (control & MVTWSI_CONTROL_IFLG) { + /* + * On Armada 38x it seems that the controller works as + * if it first set the MVTWSI_CONTROL_IFLAG in the + * control register and only after that it changed the + * status register. + * This sometimes caused weird bugs which only appeared + * on selected I2C speeds and even then only sometimes. + * We therefore add here a simple ndealy(100), which + * seems to fix this weird bug. + */ + ndelay(100); status = readl(&twsi->status); if (status == expected_status) return 0; -- 2.25.1