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-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";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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, \
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue