diff --git a/drivers/i2c/i2c_ll_stm32.h b/drivers/i2c/i2c_ll_stm32.h index bc7e28706ea..b194bc5e8f6 100644 --- a/drivers/i2c/i2c_ll_stm32.h +++ b/drivers/i2c/i2c_ll_stm32.h @@ -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); diff --git a/drivers/i2c/i2c_ll_stm32_rtio.c b/drivers/i2c/i2c_ll_stm32_rtio.c index 1340261a95b..6b150c1d561 100644 --- a/drivers/i2c/i2c_ll_stm32_rtio.c +++ b/drivers/i2c/i2c_ll_stm32_rtio.c @@ -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; diff --git a/drivers/i2c/i2c_ll_stm32_v1_rtio.c b/drivers/i2c/i2c_ll_stm32_v1_rtio.c index 7486152e4a9..d07c40bbcd4 100644 --- a/drivers/i2c/i2c_ll_stm32_v1_rtio.c +++ b/drivers/i2c/i2c_ll_stm32_v1_rtio.c @@ -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); diff --git a/drivers/i2c/i2c_ll_stm32_v2_rtio.c b/drivers/i2c/i2c_ll_stm32_v2_rtio.c index 83bd201f850..3bb4614b56d 100644 --- a/drivers/i2c/i2c_ll_stm32_v2_rtio.c +++ b/drivers/i2c/i2c_ll_stm32_v2_rtio.c @@ -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);