From b8ce3343b64434b95b9a25dea0534bf9f1593e8c Mon Sep 17 00:00:00 2001 From: Chunhe Lan Date: Fri, 16 Aug 2013 15:10:36 +0800 Subject: 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 Signed-off-by: Chunhe Lan Cc: Scott Wood Cc: Heiko Schocher 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); } -- cgit v0.10.2 From 9c3f77eb3bc0e1e24fc66bd41655fecddb6403ec Mon Sep 17 00:00:00 2001 From: Chunhe Lan Date: Fri, 16 Aug 2013 15:10:37 +0800 Subject: fsl_i2c: add workaround for the erratum I2C A004447 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 Signed-off-by: Chunhe Lan Cc: Scott Wood Cc: Heiko Schocher 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 e1fc0f7..cf1bb02 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); -- cgit v0.10.2 From ea0f73abb1afbf5bb4bf34f5a5eeb7896111df97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Majewski?= Date: Fri, 16 Aug 2013 15:31:45 +0200 Subject: i2c:multibus:fix: Correct I2C_MULTI_BUS value when support for many buses is enabled The I2C_MULTI_BUS needs to be defined for correct I2C operation with many software emulated I2C controllers. This fix restores correct value of the I2C_MULTI_BUS changed by following commit: SHA1: 385c9ef5a7215b2b0c22836fee6c692dfc8559d7 i2c: add i2c_core and prepare for new multibus support Signed-off-by: Lukasz Majewski Cc: Heiko Schocher diff --git a/include/i2c.h b/include/i2c.h index d8674ef..8fd17d1 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -390,7 +390,7 @@ unsigned int i2c_get_bus_speed(void); # if !defined(CONFIG_SYS_MAX_I2C_BUS) # define CONFIG_SYS_MAX_I2C_BUS 2 # endif -# define I2C_MULTI_BUS 0 +# define I2C_MULTI_BUS 1 #else # define CONFIG_SYS_MAX_I2C_BUS 1 # define I2C_MULTI_BUS 0 -- cgit v0.10.2 From 2936df1f1183f0bb884489e993a0381c3f179ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Majewski?= Date: Fri, 16 Aug 2013 15:33:33 +0200 Subject: i2c:samsung: Adjust Trats, GONI and Universal_c210 boards to work with new I2C framework New I2C framework, introduced after v2013.07 final release, imposed I2C code adjustment for some Samsung boards - namely Trats, GONI and Universal_c210. Those boards were using schematic based I2C enumeration (I2C_5, I2C_9). However, new I2C framework imposes usage of logical I2C adapters numbering (e.g. I2C_0, I2C_1, etc). Additionally, I2C_GET_* macros were replaced with i2c_*_bus_num() functions. Trats board gained definition of second soft I2C adapter. Signed-off-by: Lukasz Majewski Cc: Minkyu Kang Cc: Heiko Schocher diff --git a/board/samsung/common/multi_i2c.c b/board/samsung/common/multi_i2c.c index 4fce987..084858d 100644 --- a/board/samsung/common/multi_i2c.c +++ b/board/samsung/common/multi_i2c.c @@ -11,13 +11,12 @@ /* Handle multiple I2C buses instances */ int get_multi_scl_pin(void) { - unsigned int bus = I2C_GET_BUS(); + unsigned int bus = i2c_get_bus_num(); switch (bus) { - case I2C_0: /* I2C_0 definition - compatibility layer */ - case I2C_5: + case I2C_0: return CONFIG_SOFT_I2C_I2C5_SCL; - case I2C_9: + case I2C_1: return CONFIG_SOFT_I2C_I2C9_SCL; default: printf("I2C_%d not supported!\n", bus); @@ -28,13 +27,12 @@ int get_multi_scl_pin(void) int get_multi_sda_pin(void) { - unsigned int bus = I2C_GET_BUS(); + unsigned int bus = i2c_get_bus_num(); switch (bus) { - case I2C_0: /* I2C_0 definition - compatibility layer */ - case I2C_5: + case I2C_0: return CONFIG_SOFT_I2C_I2C5_SDA; - case I2C_9: + case I2C_1: return CONFIG_SOFT_I2C_I2C9_SDA; default: printf("I2C_%d not supported!\n", bus); diff --git a/board/samsung/goni/goni.c b/board/samsung/goni/goni.c index c05801d..5b3d6ef 100644 --- a/board/samsung/goni/goni.c +++ b/board/samsung/goni/goni.c @@ -32,7 +32,11 @@ int power_init_board(void) { int ret; - ret = pmic_init(I2C_5); + /* + * For PMIC the I2C bus is named as I2C5, but it is connected + * to logical I2C adapter 0 + */ + ret = pmic_init(I2C_0); if (ret) return ret; diff --git a/board/samsung/trats/trats.c b/board/samsung/trats/trats.c index c8698f3..7f61d17 100644 --- a/board/samsung/trats/trats.c +++ b/board/samsung/trats/trats.c @@ -61,10 +61,10 @@ void i2c_init_board(void) struct exynos4_gpio_part2 *gpio2 = (struct exynos4_gpio_part2 *)samsung_get_base_gpio_part2(); - /* I2C_5 -> PMIC */ + /* I2C_5 -> PMIC -> Adapter 0 */ s5p_gpio_direction_output(&gpio1->b, 7, 1); s5p_gpio_direction_output(&gpio1->b, 6, 1); - /* I2C_9 -> FG */ + /* I2C_9 -> FG -> Adapter 1 */ s5p_gpio_direction_output(&gpio2->y4, 0, 1); s5p_gpio_direction_output(&gpio2->y4, 1, 1); } @@ -282,10 +282,17 @@ int power_init_board(void) struct power_battery *pb; struct pmic *p_fg, *p_chrg, *p_muic, *p_bat; - ret = pmic_init(I2C_5); + /* + * For PMIC/MUIC the I2C bus is named as I2C5, but it is connected + * to logical I2C adapter 0 + * + * The FUEL_GAUGE is marked as I2C9 on the schematic, but connected + * to logical I2C adapter 1 + */ + ret = pmic_init(I2C_0); ret |= pmic_init_max8997(); - ret |= power_fg_init(I2C_9); - ret |= power_muic_init(I2C_5); + ret |= power_fg_init(I2C_1); + ret |= power_muic_init(I2C_0); ret |= power_bat_init(0); if (ret) return ret; diff --git a/board/samsung/universal_c210/universal.c b/board/samsung/universal_c210/universal.c index 2e1dba6..54d0e1e 100644 --- a/board/samsung/universal_c210/universal.c +++ b/board/samsung/universal_c210/universal.c @@ -45,6 +45,10 @@ int power_init_board(void) { int ret; + /* + * For PMIC the I2C bus is named as I2C5, but it is connected + * to logical I2C adapter 0 + */ ret = pmic_init(I2C_5); if (ret) return ret; diff --git a/include/configs/trats.h b/include/configs/trats.h index 9b6aac9..24ea06b 100644 --- a/include/configs/trats.h +++ b/include/configs/trats.h @@ -263,6 +263,9 @@ #define CONFIG_SYS_I2C_SOFT /* I2C bit-banged */ #define CONFIG_SYS_I2C_SOFT_SPEED 50000 #define CONFIG_SYS_I2C_SOFT_SLAVE 0xFE +#define I2C_SOFT_DECLARATIONS2 +#define CONFIG_SYS_I2C_SOFT_SPEED_2 50000 +#define CONFIG_SYS_I2C_SOFT_SLAVE_2 0x7F #define CONFIG_SOFT_I2C_READ_REPEATED_START #define CONFIG_SYS_I2C_INIT_BOARD #define CONFIG_I2C_MULTI_BUS -- cgit v0.10.2 From 3fbb517f30fe68e29640bee63ab366569914d898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Majewski?= Date: Fri, 16 Aug 2013 15:35:26 +0200 Subject: pmic:i2c: Replace legacy I2C_SET_BUS macro with i2c_set_bus_num() After introduction of unified i2c model, the I2C_SET_BUS() macro is regarded as obsolete. Hence it is replaced with i2c_set_bus_num() function call. Signed-off-by: Lukasz Majewski Cc: Heiko Schocher Cc: Tom Rini diff --git a/drivers/power/power_i2c.c b/drivers/power/power_i2c.c index 66b3bab..ac76870 100644 --- a/drivers/power/power_i2c.c +++ b/drivers/power/power_i2c.c @@ -98,7 +98,7 @@ int pmic_reg_read(struct pmic *p, u32 reg, u32 *val) int pmic_probe(struct pmic *p) { - I2C_SET_BUS(p->bus); + i2c_set_bus_num(p->bus); debug("Bus: %d PMIC:%s probed!\n", p->bus, p->name); if (i2c_probe(pmic_i2c_addr)) { printf("Can't find PMIC:%s\n", p->name); -- cgit v0.10.2