diff options
author | Anton <antone@marvell.com> | 2017-01-20 18:49:30 -0800 |
---|---|---|
committer | Kostya Porotchkin <kostap@marvell.com> | 2017-06-18 15:06:18 +0300 |
commit | 2538698f34b41caa85e74304a793da8c55c067a6 (patch) | |
tree | 4b55c630849ba9558e39f341b7118530b9691cc5 | |
parent | 44114c6c36b85cba804301cc8b90034f55324c0d (diff) |
i2c: a8k: unstuck when start clear bit timeouts
When performing back-to-back resets (~1s apart)
SPD I2C fails as the bus gets stuck:
ERROR: Start clear bit timeout
Release the bus using the "unstuck" register.
This requires to soft-reset to complete successfully
(otherwise I2C drives to finish the requested start/stop
and interferes with unstuck FSM).
On successful completion of unstuck process reset i2c again.
The solution assumes failure occurs on the first access,
therefore -EAGAIN returned triggers a re-try and
SPD access is completed successfully:
NOTICE: Gathering DRAM information
ERROR: Start clear bit timeout
Trying to "unstuck i2c"... ok
mv_ddr: version 16.12.1
mv_ddr: scrubbing memory...
Change-Id: I6a3a5ccb45edd0472de770ba55b747e6b6d357ff
Signed-off-by: Anton <antone@marvell.com>
Reviewed-on: http://vgitil04.il.marvell.com:8080/40573
Tested-by: iSoC Platform CI <ykjenk@marvell.com>
Reviewed-by: Kostya Porotchkin <kostap@marvell.com>
-rw-r--r-- | drivers/marvell/i2c/a8k_i2c.c | 35 |
1 files changed, 33 insertions, 2 deletions
diff --git a/drivers/marvell/i2c/a8k_i2c.c b/drivers/marvell/i2c/a8k_i2c.c index c4ef1441..7fe4d261 100644 --- a/drivers/marvell/i2c/a8k_i2c.c +++ b/drivers/marvell/i2c/a8k_i2c.c @@ -73,6 +73,10 @@ #define I2C_STATUS_LOST_ARB_GENERAL_CALL 0x78 #define I2C_STATUS_IDLE 0xF8 +#define I2C_UNSTUCK_TRIGGER 0x1 +#define I2C_UNSTUCK_ONGOING 0x2 +#define I2C_UNSTUCK_ERROR 0x4 + #define EPERM 1 /* Operation not permitted */ #define EAGAIN 11 /* Try again */ #define ETIMEDOUT 110 /* Connection timed out */ @@ -88,6 +92,8 @@ struct marvell_i2c_regs { uint32_t xtnd_slave_addr; uint32_t reserved[2]; uint32_t soft_reset; + uint8_t reserved2[0xa0 - 0x20]; + uint32_t unstuck; }; static struct marvell_i2c_regs *base; @@ -416,6 +422,27 @@ static int marvell_i2c_target_offset_set(uint8_t chip, uint32_t addr, int alen) return marvell_i2c_data_transmit(off_block, off_size); } +static int marvell_i2c_unstuck(int ret) +{ + uint32_t v; + + if (ret != -ETIMEDOUT) + return ret; + INFO("Trying to \"unstuck i2c\"... "); + i2c_init(base); + mmio_write_32((uintptr_t)&base->unstuck, I2C_UNSTUCK_TRIGGER); + do { + v = mmio_read_32((uintptr_t)&base->unstuck); + } while (v & I2C_UNSTUCK_ONGOING); + if (v & I2C_UNSTUCK_ERROR) { + INFO("failed - soft reset i2c\n"); + } else { + INFO("ok\n"); + i2c_init(base); + ret = -EAGAIN; + } + return ret; +} /* ------------------------------------------------------------------------ */ /* API Functions */ /* ------------------------------------------------------------------------ */ @@ -482,8 +509,10 @@ int i2c_read(uint8_t chip, uint32_t addr, int alen, uint8_t *buffer, int len) counter++; ret = marvell_i2c_start_bit_set(); - if (ret) + if (ret) { + ret = marvell_i2c_unstuck(ret); continue; + } /* if EEPROM device */ if (alen != 0) { @@ -548,8 +577,10 @@ int i2c_write(uint8_t chip, uint32_t addr, int alen, uint8_t *buffer, int len) counter++; ret = marvell_i2c_start_bit_set(); - if (ret) + if (ret) { + ret = marvell_i2c_unstuck(ret); continue; + } ret = marvell_i2c_address_set(chip, I2C_CMD_WRITE); if (ret) |