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:
parent
97a2f30757
commit
8b107ab5f1
8 changed files with 149 additions and 3 deletions
|
|
@ -116,6 +116,8 @@
|
|||
pinctrl-0 = <&i2c3_default>;
|
||||
pinctrl-names = "default";
|
||||
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";
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -116,6 +116,8 @@
|
|||
pinctrl-0 = <&i2c3_default>;
|
||||
pinctrl-names = "default";
|
||||
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";
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -76,6 +76,8 @@
|
|||
pinctrl-0 = <&i2c0_default>;
|
||||
pinctrl-names = "default";
|
||||
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";
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -88,6 +88,8 @@ compatible = "ambiq,adc";
|
|||
pinctrl-0 = <&i2c0_default>;
|
||||
pinctrl-names = "default";
|
||||
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";
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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_LINE_OPEN_DRAIN) {
|
||||
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 {
|
||||
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_LINE_OPEN_DRAIN) {
|
||||
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 {
|
||||
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)
|
||||
{
|
||||
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)
|
||||
*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
|
||||
*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
|
||||
*value = (*AM_HAL_GPIO_RDn(pin_offset));
|
||||
} else {
|
||||
*value = (*AM_HAL_GPIO_WTn(pin_offset));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,4 +27,10 @@ config I2C_DMA_TCB_BUFFER_SIZE
|
|||
help
|
||||
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
|
||||
|
|
|
|||
|
|
@ -15,6 +15,11 @@
|
|||
|
||||
#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/drivers/pinctrl.h>
|
||||
|
||||
|
|
@ -28,6 +33,10 @@ typedef int (*ambiq_i2c_pwr_func_t)(void);
|
|||
#include "i2c-priv.h"
|
||||
|
||||
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;
|
||||
int size;
|
||||
uint32_t bitrate;
|
||||
|
|
@ -241,6 +250,90 @@ static int i2c_ambiq_transfer(const struct device *dev, struct i2c_msg *msgs, ui
|
|||
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)
|
||||
{
|
||||
struct i2c_ambiq_data *data = dev->data;
|
||||
|
|
@ -291,6 +384,9 @@ end:
|
|||
static const struct i2c_driver_api i2c_ambiq_driver_api = {
|
||||
.configure = i2c_ambiq_configure,
|
||||
.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
|
||||
.iodev_submit = i2c_iodev_submit_fallback,
|
||||
#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), \
|
||||
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(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); \
|
||||
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, \
|
||||
|
|
|
|||
|
|
@ -16,3 +16,15 @@ properties:
|
|||
|
||||
ambiq,pwrcfg:
|
||||
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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue