[U-Boot] [PATCH v3 1/2] fsl_i2c: generate nine pulses on SCL if the I2C bus is hung

When the code detected that the bus is hung (e.g. SDA stuck low), send 9 pulses on SCL to try to fixup the bus.
Signed-off-by: Zhao Chenhui chenhui.zhao@freescale.com Signed-off-by: Chunhe Lan Chunhe.Lan@freescale.com Cc: Scott Wood scottwood@freescale.com Cc: Heiko Schocher hs@denx.de --- Changes for v2: - No change Changes for v3: - Rework codes for the newest mainline
drivers/i2c/fsl_i2c.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 57 insertions(+), 2 deletions(-)
diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c index 38455e1..44b08f7 100644 --- a/drivers/i2c/fsl_i2c.c +++ b/drivers/i2c/fsl_i2c.c @@ -206,9 +206,50 @@ static unsigned int get_i2c_clock(int bus) return gd->arch.i2c1_clk; /* I2C1 clock */ }
+static int fsl_i2c_fixup(const struct fsl_i2c *dev) +{ + const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT); + unsigned long long timeval = 0; + int ret = -1; + + writeb(I2C_CR_MEN | I2C_CR_MSTA, &dev->cr); + + timeval = get_ticks(); + while (!(readb(&dev->sr) & I2C_SR_MBB)) { + if ((get_ticks() - timeval) > timeout) + goto err; + } + + if (readb(&dev->sr) & I2C_SR_MAL) { + /* SDA is stuck low */ + writeb(0, &dev->cr); + udelay(100); + writeb(I2C_CR_MSTA, &dev->cr); + writeb(I2C_CR_MEN | I2C_CR_MSTA, &dev->cr); + } + + readb(&dev->dr); + + timeval = get_ticks(); + while (!(readb(&dev->sr) & I2C_SR_MIF)) { + if ((get_ticks() - timeval) > timeout) + goto err; + } + ret = 0; + +err: + writeb(I2C_CR_MEN, &dev->cr); + writeb(0, &dev->sr); + udelay(100); + + return ret; +} + static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) { const struct fsl_i2c *dev; + const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT); + unsigned long long timeval;
#ifdef CONFIG_SYS_I2C_INIT_BOARD /* Call board specific i2c bus reset routine before accessing the @@ -226,6 +267,18 @@ static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) writeb(0x0, &dev->sr); /* clear status register */ writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */
+ timeval = get_ticks(); + while (readb(&dev->sr) & I2C_SR_MBB) { + if ((get_ticks() - timeval) < timeout) + continue; + + if (fsl_i2c_fixup(dev)) + debug("i2c_init: BUS#%d failed to init\n", + adap->hwadapnr); + + break; + } + #ifdef CONFIG_SYS_I2C_BOARD_LATE_INIT /* Call board specific i2c bus reset routine AFTER the bus has been * initialized. Use either this callpoint or i2c_init_board; @@ -394,8 +447,10 @@ fsl_i2c_write(struct i2c_adapter *adap, u8 dev, uint addr, int alen, int i = -1; /* signal error */ u8 *a = (u8*)&addr;
- if (i2c_wait4bus(adap) >= 0 && - i2c_write_addr(adap, dev, I2C_WRITE_BIT, 0) != 0 && + if (i2c_wait4bus(adap) < 0) + return -1; + + if (i2c_write_addr(adap, dev, I2C_WRITE_BIT, 0) != 0 && __i2c_write(adap, &a[4 - alen], alen) == alen) { i = __i2c_write(adap, data, length); }

This workaround is for the erratum I2C A004447. Device reference manual provides a scheme that allows the I2C master controller to generate nine SCL pulses, which enable an I2C slave device that held SDA low to release SDA. However, due to this erratum, this scheme no longer works. In addition, when I2C is used as a source of the PBL, the state machine is not able to recover.
At the same time, delete the reduplicative definition of SVR_VER and SVR_REV. The SVR_REV is the low 8 bits rather than the low 16 bits of svr. And we use the CONFIG_SYS_FSL_A004447_SVR_REV macro instead of hard-code value 0x10, 0x11 and 0x20.
The CONFIG_SYS_FSL_A004447_SVR_REV = 0x00 represents that one version of platform has this I2C errata. So enable this errata by IS_SVR_REV(svr, maj, min) function.
Signed-off-by: Zhao Chenhui chenhui.zhao@freescale.com Signed-off-by: Chunhe Lan Chunhe.Lan@freescale.com Cc: Scott Wood scottwood@freescale.com Cc: Heiko Schocher hs@denx.de --- Changes for v2: - Caller directly uses CONFIG_SYS_FSL_A004447_SVR_REV to judge whether SVR_REV is less than or equal CONFIG_SYS_FSL_A004447_SVR_REV - SVR_REV is the low 8 bits rather than the low 16 bits of svr Changes for v3: - Rework codes for the newest mainline
arch/powerpc/cpu/mpc85xx/cmd_errata.c | 5 +++++ arch/powerpc/include/asm/config_mpc85xx.h | 16 ++++++++++++++++ arch/powerpc/include/asm/fsl_i2c.h | 1 + arch/powerpc/include/asm/processor.h | 5 +---- drivers/i2c/fsl_i2c.c | 14 +++++++++++--- 5 files changed, 34 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/cpu/mpc85xx/cmd_errata.c b/arch/powerpc/cpu/mpc85xx/cmd_errata.c index cbb443f..05436f9 100644 --- a/arch/powerpc/cpu/mpc85xx/cmd_errata.c +++ b/arch/powerpc/cpu/mpc85xx/cmd_errata.c @@ -248,6 +248,11 @@ static int do_errata(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) #ifdef CONFIG_SYS_FSL_ERRATUM_A005812 puts("Work-around for Erratum A-005812 enabled\n"); #endif +#ifdef CONFIG_SYS_FSL_ERRATUM_I2C_A004447 + if ((SVR_SOC_VER(svr) == SVR_8548 && IS_SVR_REV(svr, 3, 1)) || + (SVR_REV(svr) <= CONFIG_SYS_FSL_A004447_SVR_REV)) + puts("Work-around for Erratum I2C-A004447 enabled\n"); +#endif return 0; }
diff --git a/arch/powerpc/include/asm/config_mpc85xx.h b/arch/powerpc/include/asm/config_mpc85xx.h index ce1bf05..3d1de4a 100644 --- a/arch/powerpc/include/asm/config_mpc85xx.h +++ b/arch/powerpc/include/asm/config_mpc85xx.h @@ -67,6 +67,8 @@ #define CONFIG_SYS_FSL_SRIO_IB_WIN_NUM 5 #define CONFIG_SYS_FSL_RMU #define CONFIG_SYS_FSL_SRIO_MSG_UNIT_NUM 2 +#define CONFIG_SYS_FSL_ERRATUM_I2C_A004447 +#define CONFIG_SYS_FSL_A004447_SVR_REV 0x00
#elif defined(CONFIG_MPC8555) #define CONFIG_MAX_CPUS 1 @@ -132,6 +134,8 @@ #define CONFIG_SYS_FSL_ERRATUM_IFC_A002769 #define CONFIG_SYS_FSL_ERRATUM_P1010_A003549 #define CONFIG_SYS_FSL_ERRATUM_IFC_A003399 +#define CONFIG_SYS_FSL_ERRATUM_I2C_A004447 +#define CONFIG_SYS_FSL_A004447_SVR_REV 0x10
/* P1011 is single core version of P1020 */ #elif defined(CONFIG_P1011) @@ -249,6 +253,8 @@ #define CONFIG_SYS_FM_MURAM_SIZE 0x10000 #define CONFIG_SYS_FSL_PCIE_COMPAT "fsl,qoriq-pcie-v2.2" #define CONFIG_SYS_CCSRBAR_DEFAULT 0xff600000 +#define CONFIG_SYS_FSL_ERRATUM_I2C_A004447 +#define CONFIG_SYS_FSL_A004447_SVR_REV 0x11
/* P1024 is lower end variant of P1020 */ #elif defined(CONFIG_P1024) @@ -334,6 +340,8 @@ #define CONFIG_SYS_FSL_CORENET_SNOOPVEC_COREONLY 0xf0000000 #define CONFIG_SYS_FSL_ERRATUM_SRIO_A004034 #define CONFIG_SYS_FSL_ERRATUM_A004849 +#define CONFIG_SYS_FSL_ERRATUM_I2C_A004447 +#define CONFIG_SYS_FSL_A004447_SVR_REV 0x11
#elif defined(CONFIG_PPC_P3041) #define CONFIG_SYS_FSL_QORIQ_CHASSIS1 @@ -369,6 +377,8 @@ #define CONFIG_SYS_FSL_ERRATUM_SRIO_A004034 #define CONFIG_SYS_FSL_ERRATUM_A004849 #define CONFIG_SYS_FSL_ERRATUM_A005812 +#define CONFIG_SYS_FSL_ERRATUM_I2C_A004447 +#define CONFIG_SYS_FSL_A004447_SVR_REV 0x20
#elif defined(CONFIG_PPC_P4080) /* also supports P4040 */ #define CONFIG_SYS_FSL_QORIQ_CHASSIS1 @@ -415,6 +425,8 @@ #define CONFIG_SYS_FSL_ERRATUM_A004580 #define CONFIG_SYS_P4080_ERRATUM_PCIE_A003 #define CONFIG_SYS_FSL_ERRATUM_A005812 +#define CONFIG_SYS_FSL_ERRATUM_I2C_A004447 +#define CONFIG_SYS_FSL_A004447_SVR_REV 0x20
#elif defined(CONFIG_PPC_P5020) /* also supports P5010 */ #define CONFIG_SYS_PPC64 /* 64-bit core */ @@ -446,6 +458,8 @@ #define CONFIG_SYS_FSL_ERRATUM_A004510_SVR_REV 0x10 #define CONFIG_SYS_FSL_CORENET_SNOOPVEC_COREONLY 0xc0000000 #define CONFIG_SYS_FSL_ERRATUM_SRIO_A004034 +#define CONFIG_SYS_FSL_ERRATUM_I2C_A004447 +#define CONFIG_SYS_FSL_A004447_SVR_REV 0x20
#elif defined(CONFIG_PPC_P5040) #define CONFIG_SYS_PPC64 @@ -510,6 +524,8 @@ #define CONFIG_SYS_FSL_ERRATUM_ESDHC111 #define CONFIG_SYS_FSL_ESDHC_P1010_BROKEN_SDCLK #define CONFIG_SYS_FSL_PCIE_COMPAT "fsl,qoriq-pcie-v2.2" +#define CONFIG_SYS_FSL_ERRATUM_I2C_A004447 +#define CONFIG_SYS_FSL_A004447_SVR_REV 0x11
#elif defined(CONFIG_PPC_T4240) || defined(CONFIG_PPC_T4160) #define CONFIG_E6500 diff --git a/arch/powerpc/include/asm/fsl_i2c.h b/arch/powerpc/include/asm/fsl_i2c.h index 4f71341..d6537fd 100644 --- a/arch/powerpc/include/asm/fsl_i2c.h +++ b/arch/powerpc/include/asm/fsl_i2c.h @@ -54,6 +54,7 @@ typedef struct fsl_i2c { #define I2C_CR_MTX 0x10 #define I2C_CR_TXAK 0x08 #define I2C_CR_RSTA 0x04 +#define I2C_CR_BIT6 0x02 /* required for workaround A004447 */ #define I2C_CR_BCST 0x01
u8 sr; /* I2C status register */ diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 64a6f9c..bcf6cfb 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -847,7 +847,7 @@ /* System-On-Chip Version Register (SVR) field extraction */
#define SVR_VER(svr) (((svr) >> 16) & 0xFFFF) /* Version field */ -#define SVR_REV(svr) (((svr) >> 0) & 0xFFFF) /* Revision field */ +#define SVR_REV(svr) (((svr) >> 0) & 0xFF) /* Revision field */
#define SVR_CID(svr) (((svr) >> 28) & 0x0F) /* Company or manufacturer ID */ #define SVR_SOCOP(svr) (((svr) >> 22) & 0x3F) /* SOC integration options */ @@ -1043,9 +1043,6 @@
/* System Version Register (SVR) field extraction */
-#define SVR_VER(svr) (((svr) >> 16) & 0xFFFF) /* Version field */ -#define SVR_REV(svr) (((svr) >> 0) & 0xFFFF) /* Revison field */ - #define SVR_SUBVER(svr) (((svr) >> 8) & 0xFF) /* Process/MFG sub-version */
#define SVR_FAM(svr) (((svr) >> 20) & 0xFFF) /* Family field */ diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c index 44b08f7..291ad94 100644 --- a/drivers/i2c/fsl_i2c.c +++ b/drivers/i2c/fsl_i2c.c @@ -211,6 +211,14 @@ static int fsl_i2c_fixup(const struct fsl_i2c *dev) const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT); unsigned long long timeval = 0; int ret = -1; + unsigned int flags = 0; + +#ifdef CONFIG_SYS_FSL_ERRATUM_I2C_A004447 + unsigned int svr = get_svr(); + if ((SVR_SOC_VER(svr) == SVR_8548 && IS_SVR_REV(svr, 3, 1)) || + (SVR_REV(svr) <= CONFIG_SYS_FSL_A004447_SVR_REV)) + flags = I2C_CR_BIT6; +#endif
writeb(I2C_CR_MEN | I2C_CR_MSTA, &dev->cr);
@@ -224,8 +232,8 @@ static int fsl_i2c_fixup(const struct fsl_i2c *dev) /* SDA is stuck low */ writeb(0, &dev->cr); udelay(100); - writeb(I2C_CR_MSTA, &dev->cr); - writeb(I2C_CR_MEN | I2C_CR_MSTA, &dev->cr); + writeb(I2C_CR_MSTA | flags, &dev->cr); + writeb(I2C_CR_MEN | I2C_CR_MSTA | flags, &dev->cr); }
readb(&dev->dr); @@ -238,7 +246,7 @@ static int fsl_i2c_fixup(const struct fsl_i2c *dev) ret = 0;
err: - writeb(I2C_CR_MEN, &dev->cr); + writeb(I2C_CR_MEN | flags, &dev->cr); writeb(0, &dev->sr); udelay(100);

Hello Chunhe,
Am 16.08.2013 09:10, schrieb Chunhe Lan:
This workaround is for the erratum I2C A004447. Device reference manual provides a scheme that allows the I2C master controller to generate nine SCL pulses, which enable an I2C slave device that held SDA low to release SDA. However, due to this erratum, this scheme no longer works. In addition, when I2C is used as a source of the PBL, the state machine is not able to recover.
At the same time, delete the reduplicative definition of SVR_VER and SVR_REV. The SVR_REV is the low 8 bits rather than the low 16 bits of svr. And we use the CONFIG_SYS_FSL_A004447_SVR_REV macro instead of hard-code value 0x10, 0x11 and 0x20.
The CONFIG_SYS_FSL_A004447_SVR_REV = 0x00 represents that one version of platform has this I2C errata. So enable this errata by IS_SVR_REV(svr, maj, min) function.
Signed-off-by: Zhao Chenhuichenhui.zhao@freescale.com Signed-off-by: Chunhe LanChunhe.Lan@freescale.com Cc: Scott Woodscottwood@freescale.com Cc: Heiko Schocherhs@denx.de
Changes for v2:
- Caller directly uses CONFIG_SYS_FSL_A004447_SVR_REV to judge whether SVR_REV is less than or equal CONFIG_SYS_FSL_A004447_SVR_REV
- SVR_REV is the low 8 bits rather than the low 16 bits of svr
Changes for v3:
- Rework codes for the newest mainline
arch/powerpc/cpu/mpc85xx/cmd_errata.c | 5 +++++ arch/powerpc/include/asm/config_mpc85xx.h | 16 ++++++++++++++++ arch/powerpc/include/asm/fsl_i2c.h | 1 + arch/powerpc/include/asm/processor.h | 5 +---- drivers/i2c/fsl_i2c.c | 14 +++++++++++--- 5 files changed, 34 insertions(+), 7 deletions(-)
Applied to u-boot-i2c.git, thanks.
bye, Heiko

Hello Chunhe,
Am 16.08.2013 09:10, schrieb Chunhe Lan:
When the code detected that the bus is hung (e.g. SDA stuck low), send 9 pulses on SCL to try to fixup the bus.
Signed-off-by: Zhao Chenhuichenhui.zhao@freescale.com Signed-off-by: Chunhe LanChunhe.Lan@freescale.com Cc: Scott Woodscottwood@freescale.com Cc: Heiko Schocherhs@denx.de
Changes for v2:
- No change
Changes for v3:
- Rework codes for the newest mainline
drivers/i2c/fsl_i2c.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 57 insertions(+), 2 deletions(-)
Applied to u-boot-i2c.git, thanks.
bye, Heiko
participants (2)
-
Chunhe Lan
-
Heiko Schocher