diff --git a/drivers/adc/adc_stm32.c b/drivers/adc/adc_stm32.c index 34d871509ba..9e2e6464072 100644 --- a/drivers/adc/adc_stm32.c +++ b/drivers/adc/adc_stm32.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -268,36 +269,6 @@ static int adc_stm32_dma_start(const struct device *dev, } #endif /* CONFIG_ADC_STM32_DMA */ -#if defined(CONFIG_ADC_STM32_DMA) && defined(CONFIG_SOC_SERIES_STM32H7X) -/* Returns true if given buffer is in a non-cacheable SRAM region. - * This is determined using the device tree, meaning the .nocache region won't work. - * The entire buffer must be in a single region. - * An example of how the SRAM region can be defined in the DTS: - * &sram4 { - * zephyr,memory-attr = <( DT_MEM_ARM(ATTR_MPU_RAM_NOCACHE) | ... )>; - * }; - */ -static bool buf_in_nocache(uintptr_t buf __maybe_unused, size_t len_bytes __maybe_unused) -{ - bool buf_within_nocache = false; - -#ifdef CONFIG_NOCACHE_MEMORY - buf_within_nocache = (buf >= ((uintptr_t)_nocache_ram_start)) && - ((buf + len_bytes - 1) <= ((uintptr_t)_nocache_ram_end)); - if (buf_within_nocache) { - return true; - } -#endif /* CONFIG_NOCACHE_MEMORY */ - -#ifdef CONFIG_MEM_ATTR - buf_within_nocache = mem_attr_check_buf( - (void *)buf, len_bytes, DT_MEM_ARM(ATTR_MPU_RAM_NOCACHE)) == 0; -#endif /* CONFIG_MEM_ATTR */ - - return buf_within_nocache; -} -#endif /* defined(CONFIG_ADC_STM32_DMA) && defined(CONFIG_SOC_SERIES_STM32H7X) */ - static int check_buffer(const struct adc_sequence *sequence, uint8_t active_channels) { @@ -315,13 +286,13 @@ static int check_buffer(const struct adc_sequence *sequence, return -ENOMEM; } -#if defined(CONFIG_ADC_STM32_DMA) && defined(CONFIG_SOC_SERIES_STM32H7X) +#if defined(CONFIG_ADC_STM32_DMA) /* Buffer is forced to be in non-cacheable SRAM region to avoid cache maintenance */ - if (!buf_in_nocache((uintptr_t)sequence->buffer, needed_buffer_size)) { - LOG_ERR("Supplied buffer is not in a non-cacheable region according to DTS."); + if (!stm32_buf_in_nocache((uintptr_t)sequence->buffer, needed_buffer_size)) { + LOG_ERR("Supplied buffer is not in a non-cacheable region."); return -EINVAL; } -#endif +#endif /* CONFIG_ADC_STM32_DMA */ return 0; } diff --git a/drivers/i2c/i2c_ll_stm32_v2.c b/drivers/i2c/i2c_ll_stm32_v2.c index 380f9bf02bc..872383e310f 100644 --- a/drivers/i2c/i2c_ll_stm32_v2.c +++ b/drivers/i2c/i2c_ll_stm32_v2.c @@ -659,37 +659,6 @@ end: return -EIO; } -#if defined(CONFIG_DCACHE) && defined(CONFIG_I2C_STM32_V2_DMA) -static bool buf_in_nocache(uintptr_t buf, size_t len_bytes) -{ - bool buf_within_nocache = false; - -#ifdef CONFIG_NOCACHE_MEMORY - /* Check if buffer is in nocache region defined by the linker */ - buf_within_nocache = (buf >= ((uintptr_t)_nocache_ram_start)) && - ((buf + len_bytes - 1) <= ((uintptr_t)_nocache_ram_end)); - if (buf_within_nocache) { - return true; - } -#endif /* CONFIG_NOCACHE_MEMORY */ - -#ifdef CONFIG_MEM_ATTR - /* Check if buffer is in nocache memory region defined in DT */ - buf_within_nocache = mem_attr_check_buf( - (void *)buf, len_bytes, DT_MEM_ARM(ATTR_MPU_RAM_NOCACHE)) == 0; - if (buf_within_nocache) { - return true; - } -#endif /* CONFIG_MEM_ATTR */ - - /* Check if buffer is in RO region (Flash..) */ - buf_within_nocache = (buf >= ((uintptr_t)__rodata_region_start)) && - ((buf + len_bytes - 1) <= ((uintptr_t)__rodata_region_end)); - - return buf_within_nocache; -} -#endif /* CONFIG_DCACHE && CONFIG_I2C_STM32_V2_DMA */ - static int i2c_stm32_msg_write(const struct device *dev, struct i2c_msg *msg, uint8_t *next_msg_flags, uint16_t slave) { @@ -705,13 +674,13 @@ static int i2c_stm32_msg_write(const struct device *dev, struct i2c_msg *msg, data->current.is_err = 0U; data->current.msg = msg; -#if defined(CONFIG_DCACHE) && defined(CONFIG_I2C_STM32_V2_DMA) - if (!buf_in_nocache((uintptr_t)msg->buf, msg->len)) { +#if defined(CONFIG_I2C_STM32_V2_DMA) + if (!stm32_buf_in_nocache((uintptr_t)msg->buf, msg->len)) { LOG_DBG("Tx buffer at %p (len %zu) is in cached memory; cleaning cache", msg->buf, msg->len); sys_cache_data_flush_range((void *)msg->buf, msg->len); } -#endif /* CONFIG_DCACHE && CONFIG_I2C_STM32_V2_DMA*/ +#endif /* CONFIG_I2C_STM32_V2_DMA */ msg_init(dev, msg, next_msg_flags, slave, LL_I2C_REQUEST_WRITE); @@ -783,14 +752,13 @@ static int i2c_stm32_msg_read(const struct device *dev, struct i2c_msg *msg, k_sem_take(&data->device_sync_sem, K_FOREVER); is_timeout = true; } - -#if defined(CONFIG_DCACHE) && defined(CONFIG_I2C_STM32_V2_DMA) - if (!buf_in_nocache((uintptr_t)msg->buf, msg->len)) { +#if defined(CONFIG_I2C_STM32_V2_DMA) + if (!stm32_buf_in_nocache((uintptr_t)msg->buf, msg->len)) { LOG_DBG("Rx buffer at %p (len %zu) is in cached memory; invalidating cache", msg->buf, msg->len); sys_cache_data_invd_range((void *)msg->buf, msg->len); } -#endif /* CONFIG_DCACHE && CONFIG_I2C_STM32_V2_DMA */ +#endif /* CONFIG_I2C_STM32_V2_DMA */ if (data->current.is_nack || data->current.is_err || data->current.is_arlo || is_timeout) { diff --git a/drivers/serial/uart_stm32.c b/drivers/serial/uart_stm32.c index 57c1ab134bd..d67513cae8b 100644 --- a/drivers/serial/uart_stm32.c +++ b/drivers/serial/uart_stm32.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1378,34 +1379,6 @@ static void uart_stm32_isr(const struct device *dev) #ifdef CONFIG_UART_ASYNC_API -#ifdef CONFIG_DCACHE -static bool buf_in_nocache(uintptr_t buf, size_t len_bytes) -{ - bool buf_within_nocache = false; - -#ifdef CONFIG_NOCACHE_MEMORY - buf_within_nocache = (buf >= ((uintptr_t)_nocache_ram_start)) && - ((buf + len_bytes - 1) <= ((uintptr_t)_nocache_ram_end)); - if (buf_within_nocache) { - return true; - } -#endif /* CONFIG_NOCACHE_MEMORY */ - -#ifdef CONFIG_MEM_ATTR - buf_within_nocache = mem_attr_check_buf( - (void *)buf, len_bytes, DT_MEM_ARM_MPU_RAM_NOCACHE) == 0; - if (buf_within_nocache) { - return true; - } -#endif /* CONFIG_MEM_ATTR */ - - buf_within_nocache = (buf >= ((uintptr_t)__rodata_region_start)) && - ((buf + len_bytes - 1) <= ((uintptr_t)__rodata_region_end)); - - return buf_within_nocache; -} -#endif /* CONFIG_DCACHE */ - static int uart_stm32_async_callback_set(const struct device *dev, uart_callback_t callback, void *user_data) @@ -1631,12 +1604,10 @@ static int uart_stm32_async_tx(const struct device *dev, return -EBUSY; } -#ifdef CONFIG_DCACHE - if (!buf_in_nocache((uintptr_t)tx_data, buf_size)) { + if (!stm32_buf_in_nocache((uintptr_t)tx_data, buf_size)) { LOG_ERR("Tx buffer should be placed in a nocache memory region"); return -EFAULT; } -#endif /* CONFIG_DCACHE */ #ifdef CONFIG_PM data->tx_poll_stream_on = false; @@ -1743,12 +1714,10 @@ static int uart_stm32_async_rx_enable(const struct device *dev, return -EBUSY; } -#ifdef CONFIG_DCACHE - if (!buf_in_nocache((uintptr_t)rx_buf, buf_size)) { + if (!stm32_buf_in_nocache((uintptr_t)rx_buf, buf_size)) { LOG_ERR("Rx buffer should be placed in a nocache memory region"); return -EFAULT; } -#endif /* CONFIG_DCACHE */ data->dma_rx.offset = 0; data->dma_rx.buffer = rx_buf; @@ -1874,12 +1843,10 @@ static int uart_stm32_async_rx_buf_rsp(const struct device *dev, uint8_t *buf, } else if (!data->dma_rx.enabled) { err = -EACCES; } else { -#ifdef CONFIG_DCACHE - if (!buf_in_nocache((uintptr_t)buf, len)) { + if (!stm32_buf_in_nocache((uintptr_t)buf, len)) { LOG_ERR("Rx buffer should be placed in a nocache memory region"); return -EFAULT; } -#endif /* CONFIG_DCACHE */ data->rx_next_buffer = buf; data->rx_next_buffer_len = len; } diff --git a/drivers/spi/spi_ll_stm32.c b/drivers/spi/spi_ll_stm32.c index df41f344aeb..366b59a9b00 100644 --- a/drivers/spi/spi_ll_stm32.c +++ b/drivers/spi/spi_ll_stm32.c @@ -13,6 +13,7 @@ LOG_MODULE_REGISTER(spi_ll_stm32); #include #include #include +#include #include #include #include @@ -1076,28 +1077,6 @@ static int wait_dma_rx_tx_done(const struct device *dev) } #ifdef CONFIG_DCACHE -static bool buf_in_nocache(uintptr_t buf __maybe_unused, size_t len_bytes __maybe_unused) -{ - bool buf_within_nocache = false; - -#ifdef CONFIG_NOCACHE_MEMORY - /* Check if buffer is in nocache region defined by the linker */ - buf_within_nocache = (buf >= ((uintptr_t)_nocache_ram_start)) && - ((buf + len_bytes - 1) <= ((uintptr_t)_nocache_ram_end)); - if (buf_within_nocache) { - return true; - } -#endif /* CONFIG_NOCACHE_MEMORY */ - -#ifdef CONFIG_MEM_ATTR - /* Check if buffer is in nocache memory region defined in DT */ - buf_within_nocache = mem_attr_check_buf( - (void *)buf, len_bytes, DT_MEM_ARM(ATTR_MPU_RAM_NOCACHE)) == 0; -#endif /* CONFIG_MEM_ATTR */ - - return buf_within_nocache; -} - static bool is_dummy_buffer(const struct spi_buf *buf) { return buf->buf == NULL; @@ -1109,7 +1088,7 @@ static bool spi_buf_set_in_nocache(const struct spi_buf_set *bufs) const struct spi_buf *buf = &bufs->buffers[i]; if (!is_dummy_buffer(buf) && - !buf_in_nocache((uintptr_t)buf->buf, buf->len)) { + !stm32_buf_in_nocache((uintptr_t)buf->buf, buf->len)) { return false; } } diff --git a/soc/st/stm32/common/CMakeLists.txt b/soc/st/stm32/common/CMakeLists.txt index 6d97ebd9ac0..e532e17b7ba 100644 --- a/soc/st/stm32/common/CMakeLists.txt +++ b/soc/st/stm32/common/CMakeLists.txt @@ -33,3 +33,5 @@ SoC Power Management (CONFIG_PM) enabled but the DBGMCU is still enabled \ (CONFIG_STM32_ENABLE_DEBUG_SLEEP_STOP). The SoC will use more power than expected \ in STOP modes due to internal oscillators that remain active.") endif() + +zephyr_sources_ifdef(CONFIG_DCACHE stm32_cache.c) diff --git a/soc/st/stm32/common/stm32_cache.c b/soc/st/stm32/common/stm32_cache.c new file mode 100644 index 00000000000..ac8c2878e4c --- /dev/null +++ b/soc/st/stm32/common/stm32_cache.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025 Henrik Lindblom + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "stm32_cache.h" +#include +#include +#include +#include + +bool stm32_buf_in_nocache(uintptr_t buf, size_t len_bytes) +{ + uintptr_t buf_end; + + /* Note: (len_bytes - 1) is safe because a length of 0 would overflow to 4 billion. */ + if (u32_add_overflow(buf, len_bytes - 1, (uint32_t *)&buf_end)) { + return false; + } + +#ifdef CONFIG_EXTERNAL_CACHE + /* STM32 DCACHE is enabled but it does not cover internal SRAM. If the buffer is in SRAM it + * is not cached by DCACHE. + * + * NOTE: This incorrectly returns true if Zephyr image RAM resides in external memory. + */ + if (((uintptr_t)_image_ram_start) <= buf && buf_end < ((uintptr_t)_image_ram_end)) { + return true; + } +#endif /* CONFIG_EXTERNAL_CACHE */ + +#ifdef CONFIG_NOCACHE_MEMORY + /* Check if buffer is in NOCACHE region defined by the linker. */ + if ((uintptr_t)_nocache_ram_start <= buf && buf_end <= (uintptr_t)_nocache_ram_end) { + return true; + } +#endif /* CONFIG_NOCACHE_MEMORY */ + +#ifdef CONFIG_MEM_ATTR + /* Check if buffer is in a region marked NOCACHE in DT. */ + if (mem_attr_check_buf((void *)buf, len_bytes, DT_MEM_ARM_MPU_RAM_NOCACHE) == 0) { + return true; + } +#endif /* CONFIG_MEM_ATTR */ + + /* Check if buffer is in read-only region, which cannot be stale due to DCACHE because it is + * not writeable. + */ + if ((uintptr_t)__rodata_region_start <= buf && buf_end <= (uintptr_t)__rodata_region_end) { + return true; + } + + /* Not in any region known to be NOCACHE */ + return false; +} diff --git a/soc/st/stm32/common/stm32_cache.h b/soc/st/stm32/common/stm32_cache.h new file mode 100644 index 00000000000..a789f474fd0 --- /dev/null +++ b/soc/st/stm32/common/stm32_cache.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025 Henrik Lindblom + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef STM32_CACHE_H_ +#define STM32_CACHE_H_ + +#include +#include +#include +#include + +#if defined(CONFIG_DCACHE) +/** + * @brief Check if the given buffer resides in nocache memory + * + * Always returns true if CONFIG_DCACHE is not set. + * + * @param buf Buffer + * @param len_bytes Buffer size in bytes + * @return true if buf resides completely in nocache memory + */ +bool stm32_buf_in_nocache(uintptr_t buf, size_t len_bytes); + +#else /* !CONFIG_DCACHE */ + +static inline bool stm32_buf_in_nocache(uintptr_t buf, size_t len_bytes) +{ + ARG_UNUSED(buf); + ARG_UNUSED(len_bytes); + + /* Whole memory is nocache if DCACHE is disabled */ + return true; +} + +#endif /* CONFIG_DCACHE */ + +#endif /* STM32_CACHE_H_ */