drivers: i2c_mcux_flexcomm: add support for bus recovery.

use the bit bang driver to recover the i2c bus using gpio.

Signed-off-by: Johan Carlsson <johan.carlsson@teenage.engineering>
This commit is contained in:
Johan Carlsson 2024-11-07 13:33:06 +01:00 committed by Benjamin Cabé
parent 2bf61e51fe
commit 86de2d69ad
3 changed files with 125 additions and 0 deletions

View file

@ -12,6 +12,13 @@ menuconfig I2C_MCUX_FLEXCOMM
help
Enable the mcux flexcomm i2c driver.
config I2C_MCUX_FLEXCOMM_BUS_RECOVERY
bool "Bus recovery support"
depends on I2C_MCUX_FLEXCOMM && PINCTRL
select I2C_BITBANG
help
Enable flexcomm i2c driver bus recovery support via GPIO bitbanging.
config I2C_NXP_TRANSFER_TIMEOUT
int "Transfer timeout [ms]"
default 0

View file

@ -14,6 +14,11 @@
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/reset.h>
#ifdef CONFIG_I2C_MCUX_FLEXCOMM_BUS_RECOVERY
#include "i2c_bitbang.h"
#include <zephyr/drivers/gpio.h>
#endif /* CONFIG_I2C_MCUX_FLEXCOMM_BUS_RECOVERY */
#include <zephyr/logging/log.h>
#include <zephyr/irq.h>
LOG_MODULE_REGISTER(mcux_flexcomm);
@ -34,6 +39,10 @@ struct mcux_flexcomm_config {
uint32_t bitrate;
const struct pinctrl_dev_config *pincfg;
const struct reset_dt_spec reset;
#ifdef CONFIG_I2C_MCUX_FLEXCOMM_BUS_RECOVERY
struct gpio_dt_spec scl;
struct gpio_dt_spec sda;
#endif /* CONFIG_I2C_MCUX_FLEXCOMM_BUS_RECOVERY */
};
#ifdef CONFIG_I2C_TARGET
@ -204,6 +213,89 @@ static int mcux_flexcomm_transfer(const struct device *dev,
return ret;
}
#if CONFIG_I2C_MCUX_FLEXCOMM_BUS_RECOVERY
static void mcux_flexcomm_bitbang_set_scl(void *io_context, int state)
{
const struct mcux_flexcomm_config *config = io_context;
gpio_pin_set_dt(&config->scl, state);
}
static void mcux_flexcomm_bitbang_set_sda(void *io_context, int state)
{
const struct mcux_flexcomm_config *config = io_context;
gpio_pin_set_dt(&config->sda, state);
}
static int mcux_flexcomm_bitbang_get_sda(void *io_context)
{
const struct mcux_flexcomm_config *config = io_context;
return gpio_pin_get_dt(&config->sda) == 0 ? 0 : 1;
}
static int mcux_flexcomm_recover_bus(const struct device *dev)
{
const struct mcux_flexcomm_config *config = dev->config;
struct mcux_flexcomm_data *data = dev->data;
struct i2c_bitbang bitbang_ctx;
struct i2c_bitbang_io bitbang_io = {
.set_scl = mcux_flexcomm_bitbang_set_scl,
.set_sda = mcux_flexcomm_bitbang_set_sda,
.get_sda = mcux_flexcomm_bitbang_get_sda,
};
uint32_t bitrate_cfg;
int error = 0;
if (!gpio_is_ready_dt(&config->scl)) {
LOG_ERR("SCL GPIO device not ready");
return -EIO;
}
if (!gpio_is_ready_dt(&config->sda)) {
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_CONTROLLER;
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_FLEXCOMM_BUS_RECOVERY */
#if defined(CONFIG_I2C_TARGET)
static struct mcux_flexcomm_target_data *mcux_flexcomm_find_free_target(
@ -522,6 +614,9 @@ static int mcux_flexcomm_init(const struct device *dev)
static DEVICE_API(i2c, mcux_flexcomm_driver_api) = {
.configure = mcux_flexcomm_configure,
.transfer = mcux_flexcomm_transfer,
#if CONFIG_I2C_MCUX_FLEXCOMM_BUS_RECOVERY
.recover_bus = mcux_flexcomm_recover_bus,
#endif /* CONFIG_I2C_MCUX_FLEXCOMM_BUS_RECOVERY */
#if defined(CONFIG_I2C_TARGET)
.target_register = mcux_flexcomm_target_register,
.target_unregister = mcux_flexcomm_target_unregister,
@ -531,6 +626,14 @@ static DEVICE_API(i2c, mcux_flexcomm_driver_api) = {
#endif
};
#if CONFIG_I2C_MCUX_FLEXCOMM_BUS_RECOVERY
#define I2C_MCUX_FLEXCOMM_SCL_INIT(n) .scl = GPIO_DT_SPEC_INST_GET_OR(n, scl_gpios, {0}),
#define I2C_MCUX_FLEXCOMM_SDA_INIT(n) .sda = GPIO_DT_SPEC_INST_GET_OR(n, sda_gpios, {0}),
#else
#define I2C_MCUX_FLEXCOMM_SCL_INIT(n)
#define I2C_MCUX_FLEXCOMM_SDA_INIT(n)
#endif /* CONFIG_I2C_MCUX_FLEXCOMM_BUS_RECOVERY */
#define I2C_MCUX_FLEXCOMM_DEVICE(id) \
PINCTRL_DT_INST_DEFINE(id); \
static void mcux_flexcomm_config_func_##id(const struct device *dev); \
@ -542,6 +645,8 @@ static DEVICE_API(i2c, mcux_flexcomm_driver_api) = {
.irq_config_func = mcux_flexcomm_config_func_##id, \
.bitrate = DT_INST_PROP(id, clock_frequency), \
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(id), \
I2C_MCUX_FLEXCOMM_SCL_INIT(id) \
I2C_MCUX_FLEXCOMM_SDA_INIT(id) \
.reset = RESET_DT_SPEC_INST_GET(id), \
}; \
static struct mcux_flexcomm_data mcux_flexcomm_data_##id; \

View file

@ -6,3 +6,16 @@ description: LPC I2C node
compatible: "nxp,lpc-i2c"
include: [i2c-controller.yaml, "nxp,lpc-flexcomm.yaml"]
properties:
scl-gpios:
type: phandle-array
description: |
GPIO to which the I2C SCL signal is routed. This is only needed for I2C bus recovery
support.
sda-gpios:
type: phandle-array
description: |
GPIO to which the I2C SDA signal is routed. This is only needed for I2C bus recovery
support.