drivers: i2c: add bus recovery

Added bus recovery support for ambiq i2c

Signed-off-by: Hao Luo <hluo@ambiq.com>
This commit is contained in:
Hao Luo 2024-10-15 12:00:44 +08:00 committed by Henrik Brix Andersen
parent 97a2f30757
commit 8b107ab5f1
8 changed files with 149 additions and 3 deletions

View file

@ -116,6 +116,8 @@
pinctrl-0 = <&i2c3_default>; pinctrl-0 = <&i2c3_default>;
pinctrl-names = "default"; pinctrl-names = "default";
clock-frequency = <I2C_BITRATE_STANDARD>; clock-frequency = <I2C_BITRATE_STANDARD>;
scl-gpios = <&gpio32_63 10 (GPIO_OPEN_DRAIN | GPIO_PULL_UP)>;
sda-gpios = <&gpio32_63 11 (GPIO_OPEN_DRAIN | GPIO_PULL_UP)>;
status = "okay"; status = "okay";
}; };

View file

@ -116,6 +116,8 @@
pinctrl-0 = <&i2c3_default>; pinctrl-0 = <&i2c3_default>;
pinctrl-names = "default"; pinctrl-names = "default";
clock-frequency = <I2C_BITRATE_STANDARD>; clock-frequency = <I2C_BITRATE_STANDARD>;
scl-gpios = <&gpio32_63 10 (GPIO_OPEN_DRAIN | GPIO_PULL_UP)>;
sda-gpios = <&gpio32_63 11 (GPIO_OPEN_DRAIN | GPIO_PULL_UP)>;
status = "okay"; status = "okay";
}; };

View file

@ -76,6 +76,8 @@
pinctrl-0 = <&i2c0_default>; pinctrl-0 = <&i2c0_default>;
pinctrl-names = "default"; pinctrl-names = "default";
clock-frequency = <I2C_BITRATE_STANDARD>; clock-frequency = <I2C_BITRATE_STANDARD>;
scl-gpios = <&gpio0_31 5 (GPIO_OPEN_DRAIN | GPIO_PULL_UP)>;
sda-gpios = <&gpio0_31 6 (GPIO_OPEN_DRAIN | GPIO_PULL_UP)>;
status = "okay"; status = "okay";
}; };

View file

@ -88,6 +88,8 @@ compatible = "ambiq,adc";
pinctrl-0 = <&i2c0_default>; pinctrl-0 = <&i2c0_default>;
pinctrl-names = "default"; pinctrl-names = "default";
clock-frequency = <I2C_BITRATE_STANDARD>; clock-frequency = <I2C_BITRATE_STANDARD>;
scl-gpios = <&gpio0_31 5 (GPIO_OPEN_DRAIN | GPIO_PULL_UP)>;
sda-gpios = <&gpio0_31 6 (GPIO_OPEN_DRAIN | GPIO_PULL_UP)>;
status = "okay"; status = "okay";
}; };

View file

@ -55,6 +55,11 @@ static int ambiq_gpio_pin_configure(const struct device *dev, gpio_pin_t pin, gp
if (flags & GPIO_SINGLE_ENDED) { if (flags & GPIO_SINGLE_ENDED) {
if (flags & GPIO_LINE_OPEN_DRAIN) { if (flags & GPIO_LINE_OPEN_DRAIN) {
pincfg.eGPOutcfg = AM_HAL_GPIO_PIN_OUTCFG_OPENDRAIN; pincfg.eGPOutcfg = AM_HAL_GPIO_PIN_OUTCFG_OPENDRAIN;
if (flags & GPIO_PULL_UP) {
pincfg.ePullup = AM_HAL_GPIO_PIN_PULLUP_1_5K;
} else if (flags & GPIO_PULL_DOWN) {
pincfg.ePullup = AM_HAL_GPIO_PIN_PULLDOWN;
}
} }
} else { } else {
pincfg.eGPOutcfg = AM_HAL_GPIO_PIN_OUTCFG_PUSHPULL; pincfg.eGPOutcfg = AM_HAL_GPIO_PIN_OUTCFG_PUSHPULL;
@ -89,6 +94,11 @@ static int ambiq_gpio_pin_configure(const struct device *dev, gpio_pin_t pin, gp
if (flags & GPIO_SINGLE_ENDED) { if (flags & GPIO_SINGLE_ENDED) {
if (flags & GPIO_LINE_OPEN_DRAIN) { if (flags & GPIO_LINE_OPEN_DRAIN) {
pincfg.GP.cfg_b.eGPOutCfg = AM_HAL_GPIO_PIN_OUTCFG_OPENDRAIN; pincfg.GP.cfg_b.eGPOutCfg = AM_HAL_GPIO_PIN_OUTCFG_OPENDRAIN;
if (flags & GPIO_PULL_UP) {
pincfg.GP.cfg_b.ePullup = AM_HAL_GPIO_PIN_PULLUP_50K;
} else if (flags & GPIO_PULL_DOWN) {
pincfg.GP.cfg_b.ePullup = AM_HAL_GPIO_PIN_PULLDOWN_50K;
}
} }
} else { } else {
pincfg.GP.cfg_b.eGPOutCfg = AM_HAL_GPIO_PIN_OUTCFG_PUSHPULL; pincfg.GP.cfg_b.eGPOutCfg = AM_HAL_GPIO_PIN_OUTCFG_PUSHPULL;
@ -260,12 +270,23 @@ static int ambiq_gpio_port_get_direction(const struct device *dev, gpio_port_pin
static int ambiq_gpio_port_get_raw(const struct device *dev, gpio_port_value_t *value) static int ambiq_gpio_port_get_raw(const struct device *dev, gpio_port_value_t *value)
{ {
const struct ambiq_gpio_config *const dev_cfg = dev->config; const struct ambiq_gpio_config *const dev_cfg = dev->config;
am_hal_gpio_pincfg_t pincfg;
uint32_t pin_offset;
#if defined(CONFIG_SOC_SERIES_APOLLO3X) #if defined(CONFIG_SOC_SERIES_APOLLO3X)
*value = (*AM_HAL_GPIO_RDn(dev_cfg->offset)); pin_offset = dev_cfg->offset;
am_hal_gpio_pinconfig_get(pin_offset, &pincfg);
if (pincfg.eGPInput == AM_HAL_GPIO_PIN_INPUT_ENABLE) {
#else #else
*value = (*AM_HAL_GPIO_RDn(dev_cfg->offset >> 2)); pin_offset = dev_cfg->offset >> 2;
am_hal_gpio_pinconfig_get(pin_offset, &pincfg);
if (pincfg.GP.cfg_b.eGPInput == AM_HAL_GPIO_PIN_INPUT_ENABLE) {
#endif #endif
*value = (*AM_HAL_GPIO_RDn(pin_offset));
} else {
*value = (*AM_HAL_GPIO_WTn(pin_offset));
}
return 0; return 0;
} }

View file

@ -27,4 +27,10 @@ config I2C_DMA_TCB_BUFFER_SIZE
help help
DMA Transfer Control Buffer size in words DMA Transfer Control Buffer size in words
config I2C_AMBIQ_BUS_RECOVERY
bool "Bus recovery support"
select I2C_BITBANG
help
Enable AMBIQ driver bus recovery support via GPIO bitbanging.
endif # I2C_AMBIQ endif # I2C_AMBIQ

View file

@ -15,6 +15,11 @@
#include <am_mcu_apollo.h> #include <am_mcu_apollo.h>
#ifdef CONFIG_I2C_AMBIQ_BUS_RECOVERY
#include <zephyr/drivers/gpio.h>
#include "i2c_bitbang.h"
#endif /* CONFIG_I2C_AMBIQ_BUS_RECOVERY */
#include <zephyr/logging/log.h> #include <zephyr/logging/log.h>
#include <zephyr/drivers/pinctrl.h> #include <zephyr/drivers/pinctrl.h>
@ -28,6 +33,10 @@ typedef int (*ambiq_i2c_pwr_func_t)(void);
#include "i2c-priv.h" #include "i2c-priv.h"
struct i2c_ambiq_config { struct i2c_ambiq_config {
#ifdef CONFIG_I2C_AMBIQ_BUS_RECOVERY
struct gpio_dt_spec scl;
struct gpio_dt_spec sda;
#endif /* CONFIG_I2C_AMBIQ_BUS_RECOVERY */
uint32_t base; uint32_t base;
int size; int size;
uint32_t bitrate; uint32_t bitrate;
@ -241,6 +250,90 @@ static int i2c_ambiq_transfer(const struct device *dev, struct i2c_msg *msgs, ui
return ret; return ret;
} }
#if CONFIG_I2C_AMBIQ_BUS_RECOVERY
static void i2c_ambiq_bitbang_set_scl(void *io_context, int state)
{
const struct i2c_ambiq_config *config = io_context;
gpio_pin_set_dt(&config->scl, state);
}
static void i2c_ambiq_bitbang_set_sda(void *io_context, int state)
{
const struct i2c_ambiq_config *config = io_context;
gpio_pin_set_dt(&config->sda, state);
}
static int i2c_ambiq_bitbang_get_sda(void *io_context)
{
const struct i2c_ambiq_config *config = io_context;
return gpio_pin_get_dt(&config->sda) == 0 ? 0 : 1;
}
static int i2c_ambiq_recover_bus(const struct device *dev)
{
const struct i2c_ambiq_config *config = dev->config;
struct i2c_ambiq_data *data = dev->data;
struct i2c_bitbang bitbang_ctx;
struct i2c_bitbang_io bitbang_io = {
.set_scl = i2c_ambiq_bitbang_set_scl,
.set_sda = i2c_ambiq_bitbang_set_sda,
.get_sda = i2c_ambiq_bitbang_get_sda,
};
uint32_t bitrate_cfg;
int error = 0;
LOG_ERR("attempting to recover bus");
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->bus_sem, 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);
}
restore:
(void)pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
k_sem_give(&data->bus_sem);
return error;
}
#endif /* CONFIG_I2C_AMBIQ_BUS_RECOVERY */
static int i2c_ambiq_init(const struct device *dev) static int i2c_ambiq_init(const struct device *dev)
{ {
struct i2c_ambiq_data *data = dev->data; struct i2c_ambiq_data *data = dev->data;
@ -291,6 +384,9 @@ end:
static const struct i2c_driver_api i2c_ambiq_driver_api = { static const struct i2c_driver_api i2c_ambiq_driver_api = {
.configure = i2c_ambiq_configure, .configure = i2c_ambiq_configure,
.transfer = i2c_ambiq_transfer, .transfer = i2c_ambiq_transfer,
#if CONFIG_I2C_AMBIQ_BUS_RECOVERY
.recover_bus = i2c_ambiq_recover_bus,
#endif /* CONFIG_I2C_AMBIQ_BUS_RECOVERY */
#ifdef CONFIG_I2C_RTIO #ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback, .iodev_submit = i2c_iodev_submit_fallback,
#endif #endif
@ -351,7 +447,10 @@ static int i2c_ambiq_pm_action(const struct device *dev, enum pm_device_action a
.bitrate = DT_INST_PROP(n, clock_frequency), \ .bitrate = DT_INST_PROP(n, clock_frequency), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
.irq_config_func = i2c_irq_config_func_##n, \ .irq_config_func = i2c_irq_config_func_##n, \
.pwr_func = pwr_on_ambiq_i2c_##n}; \ .pwr_func = pwr_on_ambiq_i2c_##n, \
IF_ENABLED(CONFIG_I2C_AMBIQ_BUS_RECOVERY, \
(.scl = GPIO_DT_SPEC_INST_GET_OR(n, scl_gpios, {0}),\
.sda = GPIO_DT_SPEC_INST_GET_OR(n, sda_gpios, {0}),)) }; \
PM_DEVICE_DT_INST_DEFINE(n, i2c_ambiq_pm_action); \ PM_DEVICE_DT_INST_DEFINE(n, i2c_ambiq_pm_action); \
I2C_DEVICE_DT_INST_DEFINE(n, i2c_ambiq_init, PM_DEVICE_DT_INST_GET(n), &i2c_ambiq_data##n, \ I2C_DEVICE_DT_INST_DEFINE(n, i2c_ambiq_init, PM_DEVICE_DT_INST_GET(n), &i2c_ambiq_data##n, \
&i2c_ambiq_config##n, POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, \ &i2c_ambiq_config##n, POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, \

View file

@ -16,3 +16,15 @@ properties:
ambiq,pwrcfg: ambiq,pwrcfg:
required: true required: true
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.