drivers: i2c: stm32: add target mode in RTIO drivers

Implement I2C target mode in STM32 I2C RTIO drivers. The implementation
in respectively i2c_ll_stm32_v1_rtio.c and i2c_ll_stm32_v2_rtio.c is
based on the implementation of the non-RTIO drivers, respectively
i2c_ll_stm32_v1.c and i2c_ll_stm32_v2_rtio.c.

Signed-off-by: Etienne Carriere <etienne.carriere@st.com>
This commit is contained in:
Etienne Carriere 2025-06-19 12:45:08 +02:00 committed by Dan Kalowsky
parent e16c9bb41d
commit 79d1803efe
4 changed files with 448 additions and 13 deletions

View File

@ -114,14 +114,6 @@ struct i2c_stm32_data {
unsigned int len;
uint8_t *buf;
} current;
#ifdef CONFIG_I2C_TARGET
bool master_active;
struct i2c_target_config *slave_cfg;
#ifdef CONFIG_I2C_STM32_V2
struct i2c_target_config *slave2_cfg;
#endif /* CONFIG_I2C_STM32_V2 */
bool slave_attached;
#endif /* CONFIG_I2C_TARGET */
bool is_configured;
bool smbalert_active;
enum i2c_stm32_mode mode;
@ -135,6 +127,15 @@ struct i2c_stm32_data {
struct dma_block_config dma_blk_cfg;
#endif /* CONFIG_I2C_STM32_V2_DMA */
#endif /* CONFIG_I2C_RTIO */
#ifdef CONFIG_I2C_TARGET
bool master_active;
bool slave_attached;
struct i2c_target_config *slave_cfg;
#ifdef CONFIG_I2C_STM32_V2
struct i2c_target_config *slave2_cfg;
#endif /* CONFIG_I2C_STM32_V2 */
#endif /* CONFIG_I2C_TARGET */
};
#ifdef CONFIG_I2C_RTIO
@ -145,13 +146,14 @@ int i2c_stm32_msg_start(const struct device *dev, uint8_t flags,
int i2c_stm32_transaction(const struct device *dev,
struct i2c_msg msg, uint8_t *next_msg_flags,
uint16_t periph);
#endif /* CONFIG_I2C_RTIO */
int i2c_stm32_runtime_configure(const struct device *dev, uint32_t config);
#ifdef CONFIG_I2C_TARGET
int i2c_stm32_target_register(const struct device *dev, struct i2c_target_config *config);
int i2c_stm32_target_unregister(const struct device *dev, struct i2c_target_config *config);
#endif /* CONFIG_I2C_TARGET */
#endif /* CONFIG_I2C_RTIO */
int i2c_stm32_activate(const struct device *dev);
int i2c_stm32_configure_timing(const struct device *dev, uint32_t clk);

View File

@ -40,7 +40,7 @@ LOG_MODULE_REGISTER(i2c_ll_stm32_rtio);
#define I2C_STM32_DOMAIN_CLOCK_SUPPORT 0
#endif
static int i2c_stm32_do_configure(const struct device *dev, uint32_t config)
int i2c_stm32_runtime_configure(const struct device *dev, uint32_t config)
{
const struct i2c_stm32_config *cfg = dev->config;
struct i2c_stm32_data *data = dev->data;
@ -121,7 +121,7 @@ bool i2c_stm32_start(const struct device *dev)
return i2c_stm32_msg_start(dev, flags, (uint8_t *)sqe->tx.buf,
sqe->tx.buf_len, dt_spec->addr);
case RTIO_OP_I2C_CONFIGURE:
res = i2c_stm32_do_configure(dev, sqe->i2c_config);
res = i2c_stm32_runtime_configure(dev, sqe->i2c_config);
return i2c_rtio_complete(data->ctx, res);
default:
LOG_ERR("Invalid op code %d for submission %p\n", sqe->op, (void *)sqe);
@ -214,6 +214,10 @@ static const struct i2c_driver_api api_funcs = {
.transfer = i2c_stm32_transfer,
.get_config = i2c_stm32_get_config,
.iodev_submit = i2c_stm32_submit,
#if defined(CONFIG_I2C_TARGET)
.target_register = i2c_stm32_target_register,
.target_unregister = i2c_stm32_target_unregister,
#endif
};
static int i2c_stm32_init(const struct device *dev)
@ -259,7 +263,7 @@ static int i2c_stm32_init(const struct device *dev)
bitrate_cfg = i2c_map_dt_bitrate(cfg->bitrate);
ret = i2c_stm32_do_configure(dev, I2C_MODE_CONTROLLER | bitrate_cfg);
ret = i2c_stm32_runtime_configure(dev, I2C_MODE_CONTROLLER | bitrate_cfg);
if (ret < 0) {
LOG_ERR("i2c: failure initializing");
return ret;

View File

@ -71,8 +71,17 @@ static void i2c_stm32_master_mode_end(const struct device *dev, int status)
I2C_TypeDef *i2c = cfg->i2c;
i2c_stm32_disable_transfer_interrupts(dev);
LL_I2C_Disable(i2c);
#if defined(CONFIG_I2C_TARGET)
data->master_active = false;
if (data->slave_attached) {
i2c_stm32_enable_transfer_interrupts(dev);
LL_I2C_AcknowledgeNextData(i2c, LL_I2C_ACK);
return;
}
#endif
LL_I2C_Disable(i2c);
if ((data->xfer_len == 0U) && i2c_rtio_complete(ctx, status)) {
i2c_stm32_start(dev);
}
@ -277,12 +286,150 @@ static void handle_btf(const struct device *dev)
}
}
#if defined(CONFIG_I2C_TARGET)
static void i2c_stm32_target_event(const struct device *dev)
{
const struct i2c_stm32_config *cfg = dev->config;
struct i2c_stm32_data *data = dev->data;
I2C_TypeDef *i2c = cfg->i2c;
const struct i2c_target_callbacks *target_cb =
data->slave_cfg->callbacks;
if (LL_I2C_IsActiveFlag_TXE(i2c) && LL_I2C_IsActiveFlag_BTF(i2c)) {
uint8_t val;
target_cb->read_processed(data->slave_cfg, &val);
LL_I2C_TransmitData8(i2c, val);
return;
}
if (LL_I2C_IsActiveFlag_RXNE(i2c)) {
uint8_t val = LL_I2C_ReceiveData8(i2c);
if (target_cb->write_received(data->slave_cfg, val)) {
LL_I2C_AcknowledgeNextData(i2c, LL_I2C_NACK);
}
return;
}
if (LL_I2C_IsActiveFlag_AF(i2c)) {
LL_I2C_ClearFlag_AF(i2c);
}
if (LL_I2C_IsActiveFlag_STOP(i2c)) {
LL_I2C_ClearFlag_STOP(i2c);
target_cb->stop(data->slave_cfg);
/* Prepare to ACK next transmissions address byte */
LL_I2C_AcknowledgeNextData(i2c, LL_I2C_ACK);
}
if (LL_I2C_IsActiveFlag_ADDR(i2c)) {
uint32_t dir = LL_I2C_GetTransferDirection(i2c);
if (dir == LL_I2C_DIRECTION_READ) {
target_cb->write_requested(data->slave_cfg);
LL_I2C_EnableIT_RX(i2c);
} else {
uint8_t val;
target_cb->read_requested(data->slave_cfg, &val);
LL_I2C_TransmitData8(i2c, val);
LL_I2C_EnableIT_TX(i2c);
}
i2c_stm32_enable_transfer_interrupts(dev);
}
}
/* Attach and start I2C as target */
int i2c_stm32_target_register(const struct device *dev, struct i2c_target_config *config)
{
const struct i2c_stm32_config *cfg = dev->config;
struct i2c_stm32_data *data = dev->data;
I2C_TypeDef *i2c = cfg->i2c;
uint32_t bitrate_cfg;
int ret;
if (config == NULL) {
return -EINVAL;
}
if (data->slave_attached) {
return -EBUSY;
}
if (data->master_active) {
return -EBUSY;
}
bitrate_cfg = i2c_map_dt_bitrate(cfg->bitrate);
ret = i2c_stm32_runtime_configure(dev, bitrate_cfg);
if (ret < 0) {
LOG_ERR("i2c: failure initializing");
return ret;
}
data->slave_cfg = config;
LL_I2C_Enable(i2c);
if (data->slave_cfg->flags == I2C_TARGET_FLAGS_ADDR_10_BITS) {
return -ENOTSUP;
}
LL_I2C_SetOwnAddress1(i2c, config->address << 1U, LL_I2C_OWNADDRESS1_7BIT);
data->slave_attached = true;
LOG_DBG("i2c: target registered");
i2c_stm32_enable_transfer_interrupts(dev);
LL_I2C_AcknowledgeNextData(i2c, LL_I2C_ACK);
return 0;
}
int i2c_stm32_target_unregister(const struct device *dev, struct i2c_target_config *config)
{
const struct i2c_stm32_config *cfg = dev->config;
struct i2c_stm32_data *data = dev->data;
I2C_TypeDef *i2c = cfg->i2c;
if (!data->slave_attached) {
return -EINVAL;
}
if (data->master_active) {
return -EBUSY;
}
i2c_stm32_disable_transfer_interrupts(dev);
LL_I2C_ClearFlag_AF(i2c);
LL_I2C_ClearFlag_STOP(i2c);
LL_I2C_ClearFlag_ADDR(i2c);
LL_I2C_Disable(i2c);
data->slave_attached = false;
LOG_DBG("i2c: target unregistered");
return 0;
}
#endif /* defined(CONFIG_I2C_TARGET) */
void i2c_stm32_event(const struct device *dev)
{
const struct i2c_stm32_config *cfg = dev->config;
struct i2c_stm32_data *data = dev->data;
I2C_TypeDef *i2c = cfg->i2c;
#if defined(CONFIG_I2C_TARGET)
if (data->slave_attached && !data->master_active) {
i2c_stm32_target_event(dev);
return;
}
#endif
if (LL_I2C_IsActiveFlag_SB(i2c)) {
handle_sb(dev);
} else if (LL_I2C_IsActiveFlag_ADD10(i2c)) {
@ -303,6 +450,15 @@ int i2c_stm32_error(const struct device *dev)
const struct i2c_stm32_config *cfg = dev->config;
I2C_TypeDef *i2c = cfg->i2c;
#if defined(CONFIG_I2C_TARGET)
struct i2c_stm32_data *data = dev->data;
if (data->slave_attached && !data->master_active) {
/* No need for a target error function right now. */
return 0;
}
#endif
if (LL_I2C_IsActiveFlag_AF(i2c)) {
LL_I2C_ClearFlag_AF(i2c);
LL_I2C_GenerateStopCondition(i2c);
@ -338,6 +494,9 @@ int i2c_stm32_msg_start(const struct device *dev, uint8_t flags,
data->msg_len = buf_len;
data->is_restart = 0;
data->slave_address = i2c_addr;
#if defined(CONFIG_I2C_TARGET)
data->master_active = true;
#endif
LL_I2C_Enable(i2c);

View File

@ -61,8 +61,260 @@ static void i2c_stm32_master_mode_end(const struct device *dev)
if (LL_I2C_IsEnabledReloadMode(i2c)) {
LL_I2C_DisableReloadMode(i2c);
}
#if defined(CONFIG_I2C_TARGET)
struct i2c_stm32_data *data = dev->data;
data->master_active = false;
if (!data->slave_attached) {
LL_I2C_Disable(i2c);
}
#else
LL_I2C_Disable(i2c);
#endif
}
#if defined(CONFIG_I2C_TARGET)
static void i2c_stm32_target_event(const struct device *dev)
{
const struct i2c_stm32_config *cfg = dev->config;
struct i2c_stm32_data *data = dev->data;
I2C_TypeDef *i2c = cfg->i2c;
const struct i2c_target_callbacks *target_cb;
struct i2c_target_config *target_cfg;
if (data->slave_cfg->flags != I2C_TARGET_FLAGS_ADDR_10_BITS) {
uint8_t target_address;
/* Choose the right target from the address match code */
target_address = LL_I2C_GetAddressMatchCode(i2c) >> 1;
if (data->slave_cfg != NULL &&
target_address == data->slave_cfg->address) {
target_cfg = data->slave_cfg;
} else if (data->slave2_cfg != NULL &&
target_address == data->slave2_cfg->address) {
target_cfg = data->slave2_cfg;
} else {
__ASSERT_NO_MSG(0);
return;
}
} else {
/* On STM32 the LL_I2C_GetAddressMatchCode & (ISR register) returns
* only 7bits of address match so 10 bit dual addressing is broken.
* Revert to assuming single address match.
*/
if (data->slave_cfg != NULL) {
target_cfg = data->slave_cfg;
} else {
__ASSERT_NO_MSG(0);
return;
}
}
target_cb = target_cfg->callbacks;
if (LL_I2C_IsActiveFlag_TXIS(i2c)) {
uint8_t val;
if (target_cb->read_processed(target_cfg, &val) < 0) {
LOG_ERR("Error continuing reading");
} else {
LL_I2C_TransmitData8(i2c, val);
}
return;
}
if (LL_I2C_IsActiveFlag_RXNE(i2c)) {
uint8_t val = LL_I2C_ReceiveData8(i2c);
if (target_cb->write_received(target_cfg, val)) {
LL_I2C_AcknowledgeNextData(i2c, LL_I2C_NACK);
}
return;
}
if (LL_I2C_IsActiveFlag_NACK(i2c)) {
LL_I2C_ClearFlag_NACK(i2c);
}
if (LL_I2C_IsActiveFlag_STOP(i2c)) {
i2c_stm32_disable_transfer_interrupts(dev);
/* Flush remaining TX byte before clearing Stop Flag */
LL_I2C_ClearFlag_TXE(i2c);
LL_I2C_ClearFlag_STOP(i2c);
target_cb->stop(target_cfg);
/* Prepare to ACK next transmissions address byte */
LL_I2C_AcknowledgeNextData(i2c, LL_I2C_ACK);
}
if (LL_I2C_IsActiveFlag_ADDR(i2c)) {
uint32_t dir;
LL_I2C_ClearFlag_ADDR(i2c);
dir = LL_I2C_GetTransferDirection(i2c);
if (dir == LL_I2C_DIRECTION_WRITE) {
if (target_cb->write_requested(target_cfg) < 0) {
LOG_ERR("Error initiating writing");
} else {
LL_I2C_EnableIT_RX(i2c);
}
} else {
uint8_t val;
if (target_cb->read_requested(target_cfg, &val) < 0) {
LOG_ERR("Error initiating reading");
} else {
LL_I2C_TransmitData8(i2c, val);
LL_I2C_EnableIT_TX(i2c);
}
}
i2c_stm32_enable_transfer_interrupts(dev);
}
}
/* Attach and start I2C as target */
int i2c_stm32_target_register(const struct device *dev,
struct i2c_target_config *config)
{
const struct i2c_stm32_config *cfg = dev->config;
struct i2c_stm32_data *data = dev->data;
I2C_TypeDef *i2c = cfg->i2c;
uint32_t bitrate_cfg;
int ret;
if (config == NULL) {
return -EINVAL;
}
if (data->slave_cfg && data->slave2_cfg) {
return -EBUSY;
}
if (data->master_active) {
return -EBUSY;
}
bitrate_cfg = i2c_map_dt_bitrate(cfg->bitrate);
ret = i2c_stm32_runtime_configure(dev, bitrate_cfg);
if (ret < 0) {
LOG_ERR("i2c: failure initializing");
return ret;
}
#if defined(CONFIG_PM_DEVICE_RUNTIME)
if (pm_device_wakeup_is_capable(dev)) {
/* Mark device as active */
(void)pm_device_runtime_get(dev);
/* Enable wake-up from stop */
LOG_DBG("i2c: enabling wakeup from stop");
LL_I2C_EnableWakeUpFromStop(cfg->i2c);
}
#endif /* defined(CONFIG_PM_DEVICE_RUNTIME) */
LL_I2C_Enable(i2c);
if (!data->slave_cfg) {
data->slave_cfg = config;
if (data->slave_cfg->flags == I2C_TARGET_FLAGS_ADDR_10_BITS) {
LL_I2C_SetOwnAddress1(i2c, config->address, LL_I2C_OWNADDRESS1_10BIT);
LOG_DBG("i2c: target #1 registered with 10-bit address");
} else {
LL_I2C_SetOwnAddress1(i2c, config->address << 1U, LL_I2C_OWNADDRESS1_7BIT);
LOG_DBG("i2c: target #1 registered with 7-bit address");
}
LL_I2C_EnableOwnAddress1(i2c);
LOG_DBG("i2c: target #1 registered");
} else {
data->slave2_cfg = config;
if (data->slave2_cfg->flags == I2C_TARGET_FLAGS_ADDR_10_BITS) {
return -EINVAL;
}
LL_I2C_SetOwnAddress2(i2c, config->address << 1U,
LL_I2C_OWNADDRESS2_NOMASK);
LL_I2C_EnableOwnAddress2(i2c);
LOG_DBG("i2c: target #2 registered");
}
data->slave_attached = true;
LL_I2C_EnableIT_ADDR(i2c);
return 0;
}
int i2c_stm32_target_unregister(const struct device *dev,
struct i2c_target_config *config)
{
const struct i2c_stm32_config *cfg = dev->config;
struct i2c_stm32_data *data = dev->data;
I2C_TypeDef *i2c = cfg->i2c;
if (!data->slave_attached) {
return -EINVAL;
}
if (data->master_active) {
return -EBUSY;
}
if (config == data->slave_cfg) {
LL_I2C_DisableOwnAddress1(i2c);
data->slave_cfg = NULL;
LOG_DBG("i2c: target #1 unregistered");
} else if (config == data->slave2_cfg) {
LL_I2C_DisableOwnAddress2(i2c);
data->slave2_cfg = NULL;
LOG_DBG("i2c: target #2 unregistered");
} else {
return -EINVAL;
}
/* Return if there is a target remaining */
if (data->slave_cfg || data->slave2_cfg) {
LOG_DBG("i2c: target#%c still registered", data->slave_cfg?'1':'2');
return 0;
}
/* Otherwise disable I2C */
LL_I2C_DisableIT_ADDR(i2c);
i2c_stm32_disable_transfer_interrupts(dev);
LL_I2C_ClearFlag_NACK(i2c);
LL_I2C_ClearFlag_STOP(i2c);
LL_I2C_ClearFlag_ADDR(i2c);
LL_I2C_Disable(i2c);
#if defined(CONFIG_PM_DEVICE_RUNTIME)
if (pm_device_wakeup_is_capable(dev)) {
/* Disable wake-up from STOP */
LOG_DBG("i2c: disabling wakeup from stop");
LL_I2C_DisableWakeUpFromStop(i2c);
/* Release the device */
(void)pm_device_runtime_put(dev);
}
#endif /* defined(CONFIG_PM_DEVICE_RUNTIME) */
data->slave_attached = false;
return 0;
}
#endif /* defined(CONFIG_I2C_TARGET) */
static void i2c_stm32_reload_burst(const struct device *dev)
{
const struct i2c_stm32_config *cfg = dev->config;
@ -98,6 +350,13 @@ void i2c_stm32_event(const struct device *dev)
I2C_TypeDef *i2c = cfg->i2c;
int ret = 0;
#if defined(CONFIG_I2C_TARGET)
if (data->slave_attached && !data->master_active) {
i2c_stm32_target_event(dev);
return;
}
#endif
if (data->burst_len != 0U) {
/* Send next byte */
if (LL_I2C_IsActiveFlag_TXIS(i2c)) {
@ -166,6 +425,13 @@ int i2c_stm32_error(const struct device *dev)
I2C_TypeDef *i2c = cfg->i2c;
int ret = 0;
#if defined(CONFIG_I2C_TARGET)
if (data->slave_attached && !data->master_active) {
/* No need for a target error function right now. */
return 0;
}
#endif
if (LL_I2C_IsActiveFlag_ARLO(i2c)) {
LL_I2C_ClearFlag_ARLO(i2c);
ret = -EIO;
@ -233,6 +499,10 @@ int i2c_stm32_msg_start(const struct device *dev, uint8_t flags,
LL_I2C_SetTransferRequest(i2c, transfer);
LL_I2C_SetTransferSize(i2c, data->burst_len);
#if defined(CONFIG_I2C_TARGET)
data->master_active = true;
#endif
LL_I2C_Enable(i2c);
LL_I2C_GenerateStartCondition(i2c);