diff --git a/drivers/spi/spi_ll_stm32.c b/drivers/spi/spi_ll_stm32.c index fa66b850d4c57ae..22d923534b1b68f 100644 --- a/drivers/spi/spi_ll_stm32.c +++ b/drivers/spi/spi_ll_stm32.c @@ -679,6 +679,33 @@ static int transceive(const struct device *dev, #ifdef CONFIG_SPI_STM32_DMA +#ifdef CONFIG_SOC_SERIES_STM32H7X + +static struct dma_mem_region stm32_mem_regions[] = { + DT_FOREACH_STATUS_OKAY(mmio_sram, DT_GET_MEM_REGION) +}; + +/* + * 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 (dma_buf_in_non_cacheable_sram( + buf->buf, buf->len, + stm32_mem_regions, + sizeof(stm32_mem_regions) / sizeof(*stm32_mem_regions))) { + 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 +750,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 +1049,7 @@ static void spi_stm32_irq_config_func_##id(const struct device *dev) \ + #define STM32_SPI_INIT(id) \ STM32_SPI_IRQ_HANDLER_DECL(id); \ \ diff --git a/include/zephyr/drivers/dma.h b/include/zephyr/drivers/dma.h index 0448543a80d313d..142348e29c94e2a 100644 --- a/include/zephyr/drivers/dma.h +++ b/include/zephyr/drivers/dma.h @@ -13,6 +13,8 @@ #ifndef ZEPHYR_INCLUDE_DRIVERS_DMA_H_ #define ZEPHYR_INCLUDE_DRIVERS_DMA_H_ +#include + #include #include @@ -256,6 +258,50 @@ struct dma_context { /* magic code to identify context content */ #define DMA_MAGIC 0x47494749 +/* Inidicates that a ram region is nocache. */ +/* TODO Obtained from zephyr,memory-region.yaml. This should be defined in + * just one place. + */ +#define MEM_NOCACHE_ATTR "RAM_NOCACHE" + +/** + * @brief Container for MMIO SRAM information specified in devicetree + * + * This type contains pointers to the begin and end of the memory region and + * a string that specifies such region's properties. + * + */ +struct dma_mem_region { + const uint8_t *start; + const uint8_t *end; + const char *region_mpu; +}; + +/** + * @brief Can be used to initialize a @see{struct dma_mem_region}. + * + * @param node_id Node ID of the memory region declared in device tree from + * which the information will be obtained. + * + * @example + * + * struct dma_mem_region mem_r = { + * DT_GET_MEM_REGION(DT_NODELABEL()) + * }; + * + */ +#define DT_GET_MEM_REGION(node_id) \ + { \ + .start = (const uint8_t *)DT_REG_ADDR(node_id), \ + .end = \ + (const uint8_t *)(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) \ + ) \ + }, + /** * @cond INTERNAL_HIDDEN * @@ -684,6 +730,36 @@ static inline uint32_t dma_burst_index(uint32_t burst) return find_msb_set(burst); } +/** + * Return true if buf is in a nocache memory region + * among mem_regions. + * + * @param buf Buffer to be checked + * @param size Size of the buffer above + * @param mem_regions Memory regions agaisnt which buf will be checked. + * @param mem_regions_cnt Number of memory regions + */ +static inline bool dma_buf_in_non_cacheable_sram( + const uint8_t *buf, + size_t size, + struct dma_mem_region mem_regions[], + size_t mem_regions_cnt) +{ + for (size_t i = 0; i < mem_regions_cnt; i++) { + const struct dma_mem_region *mem_reg = &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, MEM_NOCACHE_ATTR, sizeof(MEM_NOCACHE_ATTR)) == 0) { + return false; + } + } + return true; +} + /** * Get the device tree property describing the buffer address alignment *