diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index cbe32fe5686..fbda5d4dc3d 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -107,6 +107,13 @@ config I2C_MCUX_LPI2C help Enable the mcux LPI2C driver. +config I2C_MCUX_LPI2C_BUS_RECOVERY + bool "Bus recovery support" + depends on I2C_MCUX_LPI2C && PINCTRL + select I2C_BITBANG + help + Enable LPI2C driver bus recovery support via GPIO bitbanging. + config I2C_IMX bool "i.MX I2C driver" depends on HAS_IMX_I2C diff --git a/drivers/i2c/i2c_mcux_lpi2c.c b/drivers/i2c/i2c_mcux_lpi2c.c index 229f2511e9a..019f54cd20a 100644 --- a/drivers/i2c/i2c_mcux_lpi2c.c +++ b/drivers/i2c/i2c_mcux_lpi2c.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2016 Freescale Semiconductor, Inc. * Copyright (c) 2019, NXP + * Copyright (c) 2022 Vestas Wind Systems A/S * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,6 +17,11 @@ #include #endif /* CONFIG_PINCTRL */ +#ifdef CONFIG_I2C_MCUX_LPI2C_BUS_RECOVERY +#include "i2c_bitbang.h" +#include +#endif /* CONFIG_I2C_MCUX_LPI2C_BUS_RECOVERY */ + #include LOG_MODULE_REGISTER(mcux_lpi2c); @@ -36,6 +42,10 @@ struct mcux_lpi2c_config { #ifdef CONFIG_PINCTRL const struct pinctrl_dev_config *pincfg; #endif /* CONFIG_PINCTRL */ +#ifdef CONFIG_I2C_MCUX_LPI2C_BUS_RECOVERY + struct gpio_dt_spec scl; + struct gpio_dt_spec sda; +#endif /* CONFIG_I2C_MCUX_LPI2C_BUS_RECOVERY */ }; struct mcux_lpi2c_data { @@ -202,6 +212,89 @@ static int mcux_lpi2c_transfer(const struct device *dev, struct i2c_msg *msgs, return ret; } +#if CONFIG_I2C_MCUX_LPI2C_BUS_RECOVERY +static void mcux_lpi2c_bitbang_set_scl(void *io_context, int state) +{ + const struct mcux_lpi2c_config *config = io_context; + + gpio_pin_set_dt(&config->scl, state); +} + +static void mcux_lpi2c_bitbang_set_sda(void *io_context, int state) +{ + const struct mcux_lpi2c_config *config = io_context; + + gpio_pin_set_dt(&config->sda, state); +} + +static int mcux_lpi2c_bitbang_get_sda(void *io_context) +{ + const struct mcux_lpi2c_config *config = io_context; + + return gpio_pin_get_dt(&config->sda) == 0 ? 0 : 1; +} + +static int mcux_lpi2c_recover_bus(const struct device *dev) +{ + const struct mcux_lpi2c_config *config = dev->config; + struct mcux_lpi2c_data *data = dev->data; + struct i2c_bitbang bitbang_ctx; + struct i2c_bitbang_io bitbang_io = { + .set_scl = mcux_lpi2c_bitbang_set_scl, + .set_sda = mcux_lpi2c_bitbang_set_sda, + .get_sda = mcux_lpi2c_bitbang_get_sda, + }; + uint32_t bitrate_cfg; + int error = 0; + + if (!device_is_ready(config->scl.port)) { + LOG_ERR("SCL GPIO device not ready"); + return -EIO; + } + + if (!device_is_ready(config->sda.port)) { + LOG_ERR("SDA GPIO device not ready"); + return -EIO; + } + + k_sem_take(&data->lock, K_FOREVER); + + error = gpio_pin_configure_dt(&config->scl, GPIO_OUTPUT_HIGH); + if (error != 0) { + LOG_ERR("failed to configure SCL GPIO (err %d)", error); + goto restore; + } + + error = gpio_pin_configure_dt(&config->sda, GPIO_OUTPUT_HIGH); + if (error != 0) { + LOG_ERR("failed to configure SDA GPIO (err %d)", error); + goto restore; + } + + i2c_bitbang_init(&bitbang_ctx, &bitbang_io, (void *)config); + + bitrate_cfg = i2c_map_dt_bitrate(config->bitrate) | I2C_MODE_MASTER; + error = i2c_bitbang_configure(&bitbang_ctx, bitrate_cfg); + if (error != 0) { + LOG_ERR("failed to configure I2C bitbang (err %d)", error); + goto restore; + } + + error = i2c_bitbang_recover_bus(&bitbang_ctx); + if (error != 0) { + LOG_ERR("failed to recover bus (err %d)", error); + goto restore; + } + +restore: + (void)pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + + k_sem_give(&data->lock); + + return error; +} +#endif /* CONFIG_I2C_MCUX_LPI2C_BUS_RECOVERY */ + static void mcux_lpi2c_isr(const struct device *dev) { const struct mcux_lpi2c_config *config = dev->config; @@ -256,6 +349,9 @@ static int mcux_lpi2c_init(const struct device *dev) static const struct i2c_driver_api mcux_lpi2c_driver_api = { .configure = mcux_lpi2c_configure, .transfer = mcux_lpi2c_transfer, +#if CONFIG_I2C_MCUX_LPI2C_BUS_RECOVERY + .recover_bus = mcux_lpi2c_recover_bus, +#endif /* CONFIG_I2C_MCUX_LPI2C_BUS_RECOVERY */ }; #ifdef CONFIG_PINCTRL @@ -266,6 +362,14 @@ static const struct i2c_driver_api mcux_lpi2c_driver_api = { #define I2C_MCUX_LPI2C_PINCTRL_INIT(n) #endif /* CONFIG_PINCTRL */ +#if CONFIG_I2C_MCUX_LPI2C_BUS_RECOVERY +#define I2C_MCUX_LPI2C_SCL_INIT(n) .scl = GPIO_DT_SPEC_INST_GET_OR(n, scl_gpios, {0}), +#define I2C_MCUX_LPI2C_SDA_INIT(n) .sda = GPIO_DT_SPEC_INST_GET_OR(n, sda_gpios, {0}), +#else +#define I2C_MCUX_LPI2C_SCL_INIT(n) +#define I2C_MCUX_LPI2C_SDA_INIT(n) +#endif /* CONFIG_I2C_MCUX_LPI2C_BUS_RECOVERY */ + #define I2C_MCUX_LPI2C_INIT(n) \ I2C_MCUX_LPI2C_PINCTRL_DEFINE(n) \ \ @@ -279,6 +383,8 @@ static const struct i2c_driver_api mcux_lpi2c_driver_api = { .irq_config_func = mcux_lpi2c_config_func_##n, \ .bitrate = DT_INST_PROP(n, clock_frequency), \ I2C_MCUX_LPI2C_PINCTRL_INIT(n) \ + I2C_MCUX_LPI2C_SCL_INIT(n) \ + I2C_MCUX_LPI2C_SDA_INIT(n) \ .bus_idle_timeout_ns = \ UTIL_AND(DT_INST_NODE_HAS_PROP(n, bus_idle_timeout),\ DT_INST_PROP(n, bus_idle_timeout)), \ diff --git a/dts/bindings/i2c/nxp,imx-lpi2c.yaml b/dts/bindings/i2c/nxp,imx-lpi2c.yaml index 3f0e34ff665..572132f6be6 100644 --- a/dts/bindings/i2c/nxp,imx-lpi2c.yaml +++ b/dts/bindings/i2c/nxp,imx-lpi2c.yaml @@ -18,3 +18,15 @@ properties: required: false type: int description: Bus idle timeout in nanoseconds + + scl-gpios: + type: phandle-array + required: false + description: | + GPIO to which the I2C SCL signal is routed. This is only needed for I2C bus recovery support. + + sda-gpios: + type: phandle-array + required: false + description: | + GPIO to which the I2C SDA signal is routed. This is only needed for I2C bus recovery support.