summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton <antone@marvell.com>2017-01-20 18:49:30 -0800
committerKostya Porotchkin <kostap@marvell.com>2017-06-18 15:06:18 +0300
commit2538698f34b41caa85e74304a793da8c55c067a6 (patch)
tree4b55c630849ba9558e39f341b7118530b9691cc5
parent44114c6c36b85cba804301cc8b90034f55324c0d (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.c35
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)