b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame^] | 1 | From ddf55be668a5e5a22d4b7729e7ba92d928983168 Mon Sep 17 00:00:00 2001 |
| 2 | From: Zhang Ying-22455 <ying.zhang22455@nxp.com> |
| 3 | Date: Fri, 1 Dec 2017 15:59:28 +0800 |
| 4 | Subject: [PATCH] i2c: imx: implement bus recovery with gpio for Layerscape |
| 5 | |
| 6 | Based on the I2C specification, if the data line (SDA) is stuck low, |
| 7 | the master should send nine clock pulses. The I2C slave device that |
| 8 | held the bus low should release it sometime within those nine clocks. |
| 9 | |
| 10 | Because pinctrl is not supported on Layerscape, current bus recovery |
| 11 | is not avalible for Layerscape. This patch uses an open drain GPIO |
| 12 | pin to connect to the IICx_SCL to drive nine clock pulses to unlock |
| 13 | the I2C bus. |
| 14 | |
| 15 | Signed-off-by: Zhang Ying-22455 <ying.zhang22455@nxp.com> |
| 16 | --- |
| 17 | drivers/i2c/busses/i2c-imx.c | 190 ++++++++++++++++++++++++++++++++++++++++++- |
| 18 | 1 file changed, 188 insertions(+), 2 deletions(-) |
| 19 | |
| 20 | --- a/drivers/i2c/busses/i2c-imx.c |
| 21 | +++ b/drivers/i2c/busses/i2c-imx.c |
| 22 | @@ -39,12 +39,18 @@ |
| 23 | #include <linux/of.h> |
| 24 | #include <linux/of_device.h> |
| 25 | #include <linux/of_dma.h> |
| 26 | +#include <linux/of_gpio.h> |
| 27 | #include <linux/pinctrl/consumer.h> |
| 28 | #include <linux/platform_data/i2c-imx.h> |
| 29 | #include <linux/platform_device.h> |
| 30 | #include <linux/pm_runtime.h> |
| 31 | #include <linux/sched.h> |
| 32 | #include <linux/slab.h> |
| 33 | +#include <linux/gpio.h> |
| 34 | +#include <linux/of_address.h> |
| 35 | +#include <linux/of.h> |
| 36 | +#include <linux/of_device.h> |
| 37 | +#include <linux/libata.h> |
| 38 | |
| 39 | /* This will be the driver name the kernel reports */ |
| 40 | #define DRIVER_NAME "imx-i2c" |
| 41 | @@ -110,6 +116,54 @@ |
| 42 | |
| 43 | #define I2C_PM_TIMEOUT 10 /* ms */ |
| 44 | |
| 45 | +enum pinmux_endian_type { |
| 46 | + BIG_ENDIAN, |
| 47 | + LITTLE_ENDIAN, |
| 48 | +}; |
| 49 | + |
| 50 | +struct pinmux_cfg { |
| 51 | + enum pinmux_endian_type endian; /* endian of RCWPMUXCR0 */ |
| 52 | + u32 pmuxcr_offset; |
| 53 | + u32 pmuxcr_set_bit; /* pin mux of RCWPMUXCR0 */ |
| 54 | +}; |
| 55 | + |
| 56 | +static struct pinmux_cfg ls1012a_pinmux_cfg = { |
| 57 | + .endian = BIG_ENDIAN, |
| 58 | + .pmuxcr_offset = 0x430, |
| 59 | + .pmuxcr_set_bit = 0x10, |
| 60 | +}; |
| 61 | + |
| 62 | +static struct pinmux_cfg ls1043a_pinmux_cfg = { |
| 63 | + .endian = BIG_ENDIAN, |
| 64 | + .pmuxcr_offset = 0x40C, |
| 65 | + .pmuxcr_set_bit = 0x10, |
| 66 | +}; |
| 67 | + |
| 68 | +static struct pinmux_cfg ls1046a_pinmux_cfg = { |
| 69 | + .endian = BIG_ENDIAN, |
| 70 | + .pmuxcr_offset = 0x40C, |
| 71 | + .pmuxcr_set_bit = 0x80000000, |
| 72 | +}; |
| 73 | + |
| 74 | +static const struct of_device_id pinmux_of_match[] = { |
| 75 | + { .compatible = "fsl,ls1012a-vf610-i2c", .data = &ls1012a_pinmux_cfg}, |
| 76 | + { .compatible = "fsl,ls1043a-vf610-i2c", .data = &ls1043a_pinmux_cfg}, |
| 77 | + { .compatible = "fsl,ls1046a-vf610-i2c", .data = &ls1046a_pinmux_cfg}, |
| 78 | + {}, |
| 79 | +}; |
| 80 | +MODULE_DEVICE_TABLE(of, pinmux_of_match); |
| 81 | + |
| 82 | +/* The SCFG, Supplemental Configuration Unit, provides SoC specific |
| 83 | + * configuration and status registers for the device. There is a |
| 84 | + * SDHC IO VSEL control register on SCFG for some platforms. It's |
| 85 | + * used to support SDHC IO voltage switching. |
| 86 | + */ |
| 87 | +static const struct of_device_id scfg_device_ids[] = { |
| 88 | + { .compatible = "fsl,ls1012a-scfg", }, |
| 89 | + { .compatible = "fsl,ls1043a-scfg", }, |
| 90 | + { .compatible = "fsl,ls1046a-scfg", }, |
| 91 | + {} |
| 92 | +}; |
| 93 | /* |
| 94 | * sorted list of clock divider, register value pairs |
| 95 | * taken from table 26-5, p.26-9, Freescale i.MX |
| 96 | @@ -205,6 +259,12 @@ struct imx_i2c_struct { |
| 97 | struct pinctrl_state *pinctrl_pins_gpio; |
| 98 | |
| 99 | struct imx_i2c_dma *dma; |
| 100 | + int layerscape_bus_recover; |
| 101 | + int gpio; |
| 102 | + int need_set_pmuxcr; |
| 103 | + int pmuxcr_set; |
| 104 | + int pmuxcr_endian; |
| 105 | + void __iomem *pmuxcr_addr; |
| 106 | }; |
| 107 | |
| 108 | static const struct imx_i2c_hwdata imx1_i2c_hwdata = { |
| 109 | @@ -951,6 +1011,78 @@ static int i2c_imx_read(struct imx_i2c_s |
| 110 | return 0; |
| 111 | } |
| 112 | |
| 113 | +/* |
| 114 | + * Based on the I2C specification, if the data line (SDA) is |
| 115 | + * stuck low, the master should send nine * clock pulses. |
| 116 | + * The I2C slave device that held the bus low should release it |
| 117 | + * sometime within * those nine clocks. Due to this erratum, |
| 118 | + * the I2C controller cannot generate nine clock pulses. |
| 119 | + */ |
| 120 | +static int i2c_imx_recovery_for_layerscape(struct imx_i2c_struct *i2c_imx) |
| 121 | +{ |
| 122 | + u32 pmuxcr = 0; |
| 123 | + int ret; |
| 124 | + unsigned int i, temp; |
| 125 | + |
| 126 | + /* configure IICx_SCL/GPIO pin as a GPIO */ |
| 127 | + if (i2c_imx->need_set_pmuxcr == 1) { |
| 128 | + pmuxcr = ioread32be(i2c_imx->pmuxcr_addr); |
| 129 | + if (i2c_imx->pmuxcr_endian == BIG_ENDIAN) |
| 130 | + iowrite32be(i2c_imx->pmuxcr_set|pmuxcr, |
| 131 | + i2c_imx->pmuxcr_addr); |
| 132 | + else |
| 133 | + iowrite32(i2c_imx->pmuxcr_set|pmuxcr, |
| 134 | + i2c_imx->pmuxcr_addr); |
| 135 | + } |
| 136 | + |
| 137 | + ret = gpio_request(i2c_imx->gpio, i2c_imx->adapter.name); |
| 138 | + if (ret) { |
| 139 | + dev_err(&i2c_imx->adapter.dev, |
| 140 | + "can't get gpio: %d\n", ret); |
| 141 | + return ret; |
| 142 | + } |
| 143 | + |
| 144 | + /* Configure GPIO pin as an output and open drain. */ |
| 145 | + gpio_direction_output(i2c_imx->gpio, 1); |
| 146 | + udelay(10); |
| 147 | + |
| 148 | + /* Write data to generate 9 pulses */ |
| 149 | + for (i = 0; i < 9; i++) { |
| 150 | + gpio_set_value(i2c_imx->gpio, 1); |
| 151 | + udelay(10); |
| 152 | + gpio_set_value(i2c_imx->gpio, 0); |
| 153 | + udelay(10); |
| 154 | + } |
| 155 | + /* ensure that the last level sent is always high */ |
| 156 | + gpio_set_value(i2c_imx->gpio, 1); |
| 157 | + |
| 158 | + /* |
| 159 | + * Set I2Cx_IBCR = 0h00 to generate a STOP and then |
| 160 | + * set I2Cx_IBCR = 0h80 to reset |
| 161 | + */ |
| 162 | + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); |
| 163 | + temp &= ~(I2CR_MSTA | I2CR_MTX); |
| 164 | + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); |
| 165 | + |
| 166 | + /* Restore the saved value of the register SCFG_RCWPMUXCR0 */ |
| 167 | + if (i2c_imx->need_set_pmuxcr == 1) { |
| 168 | + if (i2c_imx->pmuxcr_endian == BIG_ENDIAN) |
| 169 | + iowrite32be(pmuxcr, i2c_imx->pmuxcr_addr); |
| 170 | + else |
| 171 | + iowrite32(pmuxcr, i2c_imx->pmuxcr_addr); |
| 172 | + } |
| 173 | + /* |
| 174 | + * Set I2C_IBSR[IBAL] to clear the IBAL bit if- |
| 175 | + * I2C_IBSR[IBAL] = 1 |
| 176 | + */ |
| 177 | + temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR); |
| 178 | + if (temp & I2SR_IAL) { |
| 179 | + temp &= ~I2SR_IAL; |
| 180 | + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR); |
| 181 | + } |
| 182 | + return 0; |
| 183 | +} |
| 184 | + |
| 185 | static int i2c_imx_xfer(struct i2c_adapter *adapter, |
| 186 | struct i2c_msg *msgs, int num) |
| 187 | { |
| 188 | @@ -973,8 +1105,13 @@ static int i2c_imx_xfer(struct i2c_adapt |
| 189 | * before switching to master mode and attempting a Start cycle |
| 190 | */ |
| 191 | result = i2c_imx_bus_busy(i2c_imx, 0); |
| 192 | - if (result) |
| 193 | - goto out; |
| 194 | + if (result) { |
| 195 | + /* timeout */ |
| 196 | + if ((result == -ETIMEDOUT) && (i2c_imx->layerscape_bus_recover == 1)) |
| 197 | + i2c_imx_recovery_for_layerscape(i2c_imx); |
| 198 | + else |
| 199 | + goto out; |
| 200 | + } |
| 201 | |
| 202 | result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent); |
| 203 | if (result < 0) |
| 204 | @@ -1121,6 +1258,50 @@ static int i2c_imx_init_recovery_info(st |
| 205 | return 0; |
| 206 | } |
| 207 | |
| 208 | +/* |
| 209 | + * switch SCL and SDA to their GPIO function and do some bitbanging |
| 210 | + * for bus recovery. |
| 211 | + * There are platforms such as Layerscape that don't support pinctrl, so add |
| 212 | + * workaround for layerscape, it has no effect for other platforms. |
| 213 | + */ |
| 214 | +static int i2c_imx_init_recovery_for_layerscape( |
| 215 | + struct imx_i2c_struct *i2c_imx, |
| 216 | + struct platform_device *pdev) |
| 217 | +{ |
| 218 | + const struct of_device_id *of_id; |
| 219 | + struct device_node *np = pdev->dev.of_node; |
| 220 | + struct pinmux_cfg *pinmux_cfg; |
| 221 | + struct device_node *scfg_node; |
| 222 | + void __iomem *scfg_base = NULL; |
| 223 | + |
| 224 | + i2c_imx->gpio = of_get_named_gpio(np, "scl-gpios", 0); |
| 225 | + if (!gpio_is_valid(i2c_imx->gpio)) { |
| 226 | + dev_info(&pdev->dev, "scl-gpios not found\n"); |
| 227 | + return 0; |
| 228 | + } |
| 229 | + pinmux_cfg = devm_kzalloc(&pdev->dev, sizeof(*pinmux_cfg), GFP_KERNEL); |
| 230 | + if (!pinmux_cfg) |
| 231 | + return -ENOMEM; |
| 232 | + |
| 233 | + i2c_imx->need_set_pmuxcr = 0; |
| 234 | + of_id = of_match_node(pinmux_of_match, np); |
| 235 | + if (of_id) { |
| 236 | + pinmux_cfg = (struct pinmux_cfg *)of_id->data; |
| 237 | + i2c_imx->pmuxcr_endian = pinmux_cfg->endian; |
| 238 | + i2c_imx->pmuxcr_set = pinmux_cfg->pmuxcr_set_bit; |
| 239 | + scfg_node = of_find_matching_node(NULL, scfg_device_ids); |
| 240 | + if (scfg_node) { |
| 241 | + scfg_base = of_iomap(scfg_node, 0); |
| 242 | + if (scfg_base) { |
| 243 | + i2c_imx->pmuxcr_addr = scfg_base + pinmux_cfg->pmuxcr_offset; |
| 244 | + i2c_imx->need_set_pmuxcr = 1; |
| 245 | + } |
| 246 | + } |
| 247 | + } |
| 248 | + i2c_imx->layerscape_bus_recover = 1; |
| 249 | + return 0; |
| 250 | +} |
| 251 | + |
| 252 | static u32 i2c_imx_func(struct i2c_adapter *adapter) |
| 253 | { |
| 254 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
| 255 | @@ -1245,8 +1426,13 @@ static int i2c_imx_probe(struct platform |
| 256 | i2c_imx, IMX_I2C_I2CR); |
| 257 | imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR); |
| 258 | |
| 259 | +#ifdef CONFIG_ARCH_LAYERSCAPE |
| 260 | + /* Init optional bus recovery for layerscape */ |
| 261 | + ret = i2c_imx_init_recovery_for_layerscape(i2c_imx, pdev); |
| 262 | +#else |
| 263 | /* Init optional bus recovery function */ |
| 264 | ret = i2c_imx_init_recovery_info(i2c_imx, pdev); |
| 265 | +#endif |
| 266 | /* Give it another chance if pinctrl used is not ready yet */ |
| 267 | if (ret == -EPROBE_DEFER) |
| 268 | goto clk_notifier_unregister; |