diff --git a/drivers/spi/spi_ll_stm32.c b/drivers/spi/spi_ll_stm32.c index fa66b850d4c57ae..78e4565d17f4441 100644 --- a/drivers/spi/spi_ll_stm32.c +++ b/drivers/spi/spi_ll_stm32.c @@ -679,6 +679,82 @@ static int transceive(const struct device *dev, #ifdef CONFIG_SPI_STM32_DMA +#ifdef CONFIG_SOC_SERIES_STM32H7X +#define STM32_SPI_GET_MEM_REGION(node_id) \ + { \ + .start = (const void *)DT_REG_ADDR(node_id), \ + .end = \ + (const void *)(DT_REG_ADDR(node_id) + DT_REG_SIZE(node_id)), \ + .region_mpu = COND_CODE_1( \ + DT_NODE_HAS_PROP(node_id, zephyr_memory_region_mpu), \ + (DT_PROP(node_id, zephyr_memory_region_mpu)), \ + (NULL) \ + ) \ + }, + +#define NOCACHE_ID "RAM_NOCACHE" +#define NOCACHE_ID_LEN sizeof(NOCACHE_ID) + +struct spi_stm32_mem_region { + const uint8_t *start; + const uint8_t *end; + const char *region_mpu; +}; + +static struct spi_stm32_mem_region stm32_mem_regions[] = { + DT_FOREACH_STATUS_OKAY(mmio_sram, STM32_SPI_GET_MEM_REGION) +}; + +/* + * DMA in H7 must only access non-cacheable RAM regions or it won't work + * properly. Use this function to check if the SPI buffers are in a nocache + * region. + * + * Return true if @param{buf} is not within nocache RAM bounds, false + * otherwise. + * + * TODO In H7, DMA can only access certain memory areas regardless of if they + * are nocache or not (e.g. SRAM1, SRAM2). It would be good to check this as + * well, but different H7 SoCs can access different areas. + */ +static bool buf_in_non_cacheable_sram(const uint8_t *buf, size_t size) +{ + const size_t mem_regions_cnt = + sizeof(stm32_mem_regions) / sizeof(*stm32_mem_regions); + + for (size_t i = 0; i < mem_regions_cnt; i++) { + const struct spi_stm32_mem_region *mem_reg = &stm32_mem_regions[i]; + const char *reg_mpu = mem_reg->region_mpu; + + const bool buf_within_bounds = + (buf >= mem_reg->start) && ((buf + size) <= mem_reg->end); + if (buf_within_bounds && + reg_mpu != NULL && + strncmp(reg_mpu, NOCACHE_ID, NOCACHE_ID_LEN) == 0) { + return false; + } + } + return true; +} + +/* + * Checks that all buffers in `bufs` are in a valid area to be used + * with DMA. + * + */ +static bool all_bufs_in_valid_dma_ram(const struct spi_buf_set *bufs) +{ + for (size_t i = 0; i < bufs->count; i++) { + const struct spi_buf *buf = &bufs->buffers[i]; + + if (buf_in_non_cacheable_sram(buf->buf, buf->len)) { + return false; + } + } + return true; +} +#endif /* CONFIG_SOC_SERIES_STM32H7X */ + static int wait_dma_rx_tx_done(const struct device *dev) { struct spi_stm32_data *data = dev->data; @@ -723,6 +799,13 @@ static int transceive_dma(const struct device *dev, return -ENOTSUP; } +#ifdef CONFIG_SOC_SERIES_STM32H7X + if ((tx_bufs != NULL && !all_bufs_in_valid_dma_ram(tx_bufs)) || + (rx_bufs != NULL && !all_bufs_in_valid_dma_ram(rx_bufs))) { + return -EFAULT; + } +#endif /* CONFIG_SOC_SERIES_STM32H7X */ + spi_context_lock(&data->ctx, asynchronous, cb, userdata, config); k_sem_reset(&data->status_sem); @@ -1015,6 +1098,7 @@ static void spi_stm32_irq_config_func_##id(const struct device *dev) \ + #define STM32_SPI_INIT(id) \ STM32_SPI_IRQ_HANDLER_DECL(id); \ \