diff --git a/drivers/i2c/i2c_ll_stm32_v2.c b/drivers/i2c/i2c_ll_stm32_v2.c index 8e97fffbf09b8b2..0dbd6ed1873d83a 100644 --- a/drivers/i2c/i2c_ll_stm32_v2.c +++ b/drivers/i2c/i2c_ll_stm32_v2.c @@ -117,6 +117,42 @@ static struct stm32_i2c_timings_t i2c_valid_timing[STM32_I2C_VALID_TIMING_NBR]; static uint32_t i2c_valid_timing_nbr; #endif /* CONFIG_I2C_STM32_V2_TIMING */ +#ifdef CONFIG_I2C_DMA +static int configure_dma(const struct device *dma_dev, uint32_t dma_channel, + struct dma_config *dma_cfg, struct dma_block_config *blk_cfg, + uint32_t source_address, uint32_t dest_address, struct i2c_msg *msg, + struct k_sem *dma_sem, const char *direction) +{ + if (!device_is_ready(dma_dev)) { + LOG_ERR("DMA device not ready for %s", direction); + return -ENODEV; + } + + k_sem_take(dma_sem, K_FOREVER); + + blk_cfg->source_address = source_address; + blk_cfg->dest_address = dest_address; + blk_cfg->block_size = msg->len; + + dma_cfg->head_block = blk_cfg; + dma_cfg->block_count = 1; + + int ret = dma_config(dma_dev, dma_channel, dma_cfg); + if (ret != 0) { + LOG_ERR("Problem setting up %s DMA: %d", direction, ret); + return ret; + } + + ret = dma_start(dma_dev, dma_channel); + if (ret != 0) { + LOG_ERR("Problem starting %s DMA: %d", direction, ret); + return ret; + } + + return 0; +} +#endif + static inline void msg_init(const struct device *dev, struct i2c_msg *msg, uint8_t *next_msg_flags, uint16_t slave, uint32_t transfer) @@ -151,6 +187,48 @@ static inline void msg_init(const struct device *dev, struct i2c_msg *msg, #if defined(CONFIG_I2C_TARGET) data->master_active = true; #endif + +#ifdef CONFIG_I2C_DMA + if (msg->len) { + if (msg->flags & I2C_MSG_READ) { + // Configure RX DMA + if (configure_dma(cfg->dev_dma_rx, cfg->dma_rx_channel, + &data->dma_rx_cfg, &data->dma_rx_blk_cfg, + LL_I2C_DMA_GetRegAddr( + cfg->i2c, LL_I2C_DMA_REG_DATA_RECEIVE), + (uint32_t)msg->buf, msg, &data->dma_rx_sem, + "RX") != 0) { + return; + } + data->current.buf += msg->len; + data->current.len -= msg->len; + LL_I2C_EnableDMAReq_RX(i2c); + } else { + // Preload the TX register for the first byte + + LL_I2C_TransmitData8(i2c, *data->current.buf); + data->current.buf++; + data->current.len--; + + if (msg->len) { + // Configure TX DMA + if (configure_dma( + cfg->dev_dma_tx, cfg->dma_tx_channel, + &data->dma_tx_cfg, &data->dma_tx_blk_cfg, + (uint32_t)msg->buf, + LL_I2C_DMA_GetRegAddr( + cfg->i2c, LL_I2C_DMA_REG_DATA_TRANSMIT), + msg, &data->dma_tx_sem, "TX") != 0) { + return; + } + data->current.buf += msg->len; + data->current.len -= msg->len; + LL_I2C_EnableDMAReq_TX(i2c); + } + } + } +#endif + LL_I2C_Enable(i2c); LL_I2C_GenerateStartCondition(i2c); @@ -209,6 +287,19 @@ static void stm32_i2c_master_mode_end(const struct device *dev) LL_I2C_Disable(i2c); } #endif + +#ifdef CONFIG_I2C_DMA + if (data->current.msg->flags & I2C_MSG_READ) { + dma_stop(cfg->dev_dma_rx, cfg->dma_rx_channel); + k_sem_give(&data->dma_rx_sem); + LL_I2C_DisableDMAReq_RX(i2c); + } else { + dma_stop(cfg->dev_dma_tx, cfg->dma_tx_channel); + k_sem_give(&data->dma_tx_sem); + LL_I2C_DisableDMAReq_TX(i2c); + } +#endif + k_sem_give(&data->device_sync_sem); } @@ -508,6 +599,19 @@ static void stm32_i2c_event(const struct device *dev) LL_I2C_GenerateStopCondition(i2c); } else { stm32_i2c_disable_transfer_interrupts(dev); + +#ifdef CONFIG_I2C_DMA + if (data->current.msg->flags & I2C_MSG_READ) { + dma_stop(cfg->dev_dma_rx, cfg->dma_rx_channel); + k_sem_give(&data->dma_rx_sem); + LL_I2C_DisableDMAReq_RX(i2c); + } else { + dma_stop(cfg->dev_dma_tx, cfg->dma_tx_channel); + k_sem_give(&data->dma_tx_sem); + LL_I2C_DisableDMAReq_TX(i2c); + } +#endif + k_sem_give(&data->device_sync_sem); } }