Commit 6220bb6fb43aa485a10f96fb6ade864b3371e4ca
1 parent
db7ae71814
Exists in
smarc_8mm-imx_v2018.03_4.14.98_2.0.0_ga
and in
5 other branches
ENGR00328312 i2c: imx: Optimize the i2c device recovery solution
From i2c spec, if device pull down the SDA line that causes i2c bus dead, host can send out 9 clock to let device release SDA. But for some special device like pfuze100, it pull down SDA line and the solution cannot take effort. The patch just add NACK and STOP signal after 8 dummy clock, and pmic can release SDA line after the recovery. Test case catch 375 times of i2c hang, and all are recovered. Signed-off-by: Fugang Duan <B38611@freescale.com> Signed-off-by: Peng Fan <Peng.Fan@freescale.com> Signed-off-by: Ye Li <ye.li@nxp.com>
Showing 1 changed file with 27 additions and 2 deletions Inline Diff
arch/arm/mach-imx/i2c-mxv7.c
1 | /* | 1 | /* |
2 | * Copyright (C) 2012 Boundary Devices Inc. | 2 | * Copyright (C) 2012 Boundary Devices Inc. |
3 | * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. | ||
4 | * Copyright 2018 NXP | ||
3 | * | 5 | * |
4 | * SPDX-License-Identifier: GPL-2.0+ | 6 | * SPDX-License-Identifier: GPL-2.0+ |
5 | */ | 7 | */ |
6 | #include <common.h> | 8 | #include <common.h> |
7 | #include <malloc.h> | 9 | #include <malloc.h> |
8 | #include <asm/arch/clock.h> | 10 | #include <asm/arch/clock.h> |
9 | #include <asm/arch/imx-regs.h> | 11 | #include <asm/arch/imx-regs.h> |
10 | #include <linux/errno.h> | 12 | #include <linux/errno.h> |
11 | #include <asm/gpio.h> | 13 | #include <asm/gpio.h> |
12 | #include <asm/mach-imx/mxc_i2c.h> | 14 | #include <asm/mach-imx/mxc_i2c.h> |
13 | #include <watchdog.h> | 15 | #include <watchdog.h> |
14 | 16 | ||
15 | int force_idle_bus(void *priv) | 17 | int force_idle_bus(void *priv) |
16 | { | 18 | { |
17 | int i; | 19 | int i; |
18 | int sda, scl; | 20 | int sda, scl; |
19 | ulong elapsed, start_time; | 21 | ulong elapsed, start_time; |
20 | struct i2c_pads_info *p = (struct i2c_pads_info *)priv; | 22 | struct i2c_pads_info *p = (struct i2c_pads_info *)priv; |
21 | int ret = 0; | 23 | int ret = 0; |
22 | 24 | ||
23 | gpio_direction_input(p->sda.gp); | 25 | gpio_direction_input(p->sda.gp); |
24 | gpio_direction_input(p->scl.gp); | 26 | gpio_direction_input(p->scl.gp); |
25 | 27 | ||
26 | imx_iomux_v3_setup_pad(p->sda.gpio_mode); | 28 | imx_iomux_v3_setup_pad(p->sda.gpio_mode); |
27 | imx_iomux_v3_setup_pad(p->scl.gpio_mode); | 29 | imx_iomux_v3_setup_pad(p->scl.gpio_mode); |
28 | 30 | ||
29 | sda = gpio_get_value(p->sda.gp); | 31 | sda = gpio_get_value(p->sda.gp); |
30 | scl = gpio_get_value(p->scl.gp); | 32 | scl = gpio_get_value(p->scl.gp); |
31 | if ((sda & scl) == 1) | 33 | if ((sda & scl) == 1) |
32 | goto exit; /* Bus is idle already */ | 34 | goto exit; /* Bus is idle already */ |
33 | 35 | ||
34 | printf("%s: sda=%d scl=%d sda.gp=0x%x scl.gp=0x%x\n", __func__, | 36 | printf("%s: sda=%d scl=%d sda.gp=0x%x scl.gp=0x%x\n", __func__, |
35 | sda, scl, p->sda.gp, p->scl.gp); | 37 | sda, scl, p->sda.gp, p->scl.gp); |
38 | gpio_direction_output(p->scl.gp, 1); | ||
39 | udelay(1000); | ||
36 | /* Send high and low on the SCL line */ | 40 | /* Send high and low on the SCL line */ |
37 | for (i = 0; i < 9; i++) { | 41 | for (i = 0; i < 9; i++) { |
42 | gpio_direction_output(p->scl.gp, 1); | ||
43 | udelay(50); | ||
38 | gpio_direction_output(p->scl.gp, 0); | 44 | gpio_direction_output(p->scl.gp, 0); |
39 | udelay(50); | 45 | udelay(50); |
40 | gpio_direction_input(p->scl.gp); | ||
41 | udelay(50); | ||
42 | } | 46 | } |
47 | |||
48 | /* Simulate the NACK */ | ||
49 | gpio_direction_output(p->sda.gp, 1); | ||
50 | udelay(50); | ||
51 | gpio_direction_output(p->scl.gp, 1); | ||
52 | udelay(50); | ||
53 | gpio_direction_output(p->scl.gp, 0); | ||
54 | udelay(50); | ||
55 | |||
56 | /* Simulate the STOP signal */ | ||
57 | gpio_direction_output(p->sda.gp, 0); | ||
58 | udelay(50); | ||
59 | gpio_direction_output(p->scl.gp, 1); | ||
60 | udelay(50); | ||
61 | gpio_direction_output(p->sda.gp, 1); | ||
62 | udelay(50); | ||
63 | |||
64 | /* Get the bus status */ | ||
65 | gpio_direction_input(p->sda.gp); | ||
66 | gpio_direction_input(p->scl.gp); | ||
67 | |||
43 | start_time = get_timer(0); | 68 | start_time = get_timer(0); |
44 | for (;;) { | 69 | for (;;) { |
45 | sda = gpio_get_value(p->sda.gp); | 70 | sda = gpio_get_value(p->sda.gp); |
46 | scl = gpio_get_value(p->scl.gp); | 71 | scl = gpio_get_value(p->scl.gp); |
47 | if ((sda & scl) == 1) | 72 | if ((sda & scl) == 1) |
48 | break; | 73 | break; |
49 | WATCHDOG_RESET(); | 74 | WATCHDOG_RESET(); |
50 | elapsed = get_timer(start_time); | 75 | elapsed = get_timer(start_time); |
51 | if (elapsed > (CONFIG_SYS_HZ / 5)) { /* .2 seconds */ | 76 | if (elapsed > (CONFIG_SYS_HZ / 5)) { /* .2 seconds */ |
52 | ret = -EBUSY; | 77 | ret = -EBUSY; |
53 | printf("%s: failed to clear bus, sda=%d scl=%d\n", | 78 | printf("%s: failed to clear bus, sda=%d scl=%d\n", |
54 | __func__, sda, scl); | 79 | __func__, sda, scl); |
55 | break; | 80 | break; |
56 | } | 81 | } |
57 | } | 82 | } |
58 | exit: | 83 | exit: |
59 | imx_iomux_v3_setup_pad(p->sda.i2c_mode); | 84 | imx_iomux_v3_setup_pad(p->sda.i2c_mode); |
60 | imx_iomux_v3_setup_pad(p->scl.i2c_mode); | 85 | imx_iomux_v3_setup_pad(p->scl.i2c_mode); |
61 | return ret; | 86 | return ret; |
62 | } | 87 | } |
63 | 88 | ||
64 | static void * const i2c_bases[] = { | 89 | static void * const i2c_bases[] = { |
65 | (void *)I2C1_BASE_ADDR, | 90 | (void *)I2C1_BASE_ADDR, |
66 | (void *)I2C2_BASE_ADDR, | 91 | (void *)I2C2_BASE_ADDR, |
67 | #ifdef I2C3_BASE_ADDR | 92 | #ifdef I2C3_BASE_ADDR |
68 | (void *)I2C3_BASE_ADDR, | 93 | (void *)I2C3_BASE_ADDR, |
69 | #endif | 94 | #endif |
70 | #ifdef I2C4_BASE_ADDR | 95 | #ifdef I2C4_BASE_ADDR |
71 | (void *)I2C4_BASE_ADDR, | 96 | (void *)I2C4_BASE_ADDR, |
72 | #endif | 97 | #endif |
73 | }; | 98 | }; |
74 | 99 | ||
75 | /* i2c_index can be from 0 - 3 */ | 100 | /* i2c_index can be from 0 - 3 */ |
76 | int setup_i2c(unsigned i2c_index, int speed, int slave_addr, | 101 | int setup_i2c(unsigned i2c_index, int speed, int slave_addr, |
77 | struct i2c_pads_info *p) | 102 | struct i2c_pads_info *p) |
78 | { | 103 | { |
79 | char name[9]; | 104 | char name[9]; |
80 | int ret; | 105 | int ret; |
81 | 106 | ||
82 | if (i2c_index >= ARRAY_SIZE(i2c_bases)) | 107 | if (i2c_index >= ARRAY_SIZE(i2c_bases)) |
83 | return -EINVAL; | 108 | return -EINVAL; |
84 | 109 | ||
85 | snprintf(name, sizeof(name), "i2c_sda%01d", i2c_index); | 110 | snprintf(name, sizeof(name), "i2c_sda%01d", i2c_index); |
86 | ret = gpio_request(p->sda.gp, name); | 111 | ret = gpio_request(p->sda.gp, name); |
87 | if (ret) | 112 | if (ret) |
88 | return ret; | 113 | return ret; |
89 | 114 | ||
90 | snprintf(name, sizeof(name), "i2c_scl%01d", i2c_index); | 115 | snprintf(name, sizeof(name), "i2c_scl%01d", i2c_index); |
91 | ret = gpio_request(p->scl.gp, name); | 116 | ret = gpio_request(p->scl.gp, name); |
92 | if (ret) | 117 | if (ret) |
93 | goto err_req; | 118 | goto err_req; |
94 | 119 | ||
95 | /* Enable i2c clock */ | 120 | /* Enable i2c clock */ |
96 | ret = enable_i2c_clk(1, i2c_index); | 121 | ret = enable_i2c_clk(1, i2c_index); |
97 | if (ret) | 122 | if (ret) |
98 | goto err_clk; | 123 | goto err_clk; |
99 | 124 | ||
100 | /* Make sure bus is idle */ | 125 | /* Make sure bus is idle */ |
101 | ret = force_idle_bus(p); | 126 | ret = force_idle_bus(p); |
102 | if (ret) | 127 | if (ret) |
103 | goto err_idle; | 128 | goto err_idle; |
104 | 129 | ||
105 | #ifndef CONFIG_DM_I2C | 130 | #ifndef CONFIG_DM_I2C |
106 | bus_i2c_init(i2c_index, speed, slave_addr, force_idle_bus, p); | 131 | bus_i2c_init(i2c_index, speed, slave_addr, force_idle_bus, p); |
107 | #endif | 132 | #endif |
108 | 133 | ||
109 | return 0; | 134 | return 0; |
110 | 135 | ||
111 | err_idle: | 136 | err_idle: |
112 | err_clk: | 137 | err_clk: |
113 | gpio_free(p->scl.gp); | 138 | gpio_free(p->scl.gp); |
114 | err_req: | 139 | err_req: |
115 | gpio_free(p->sda.gp); | 140 | gpio_free(p->sda.gp); |
116 | 141 | ||
117 | return ret; | 142 | return ret; |
-
mentioned in commit 8353e2
-
mentioned in commit 8353e2
-
mentioned in commit cb4ac4
-
mentioned in commit cb4ac4
-
mentioned in commit cb4ac4
-
mentioned in commit cb4ac4
-
mentioned in commit 8c14a4
-
mentioned in commit 8c14a4
-
mentioned in commit cb4ac4
-
mentioned in commit 8c14a4
-
mentioned in commit 8c14a4
-
mentioned in commit 8c14a4
-
mentioned in commit 8c14a4
-
mentioned in commit ed1a7d
-
mentioned in commit 340a12