drivers: stm32: dma: fix external dcache support
Several drivers checked for the presense and availability of data cache
through Kconfig symbol. This is supported according to the current
documentation, but the symbol DCACHE masks two types of cache devices: arch
and external caches. The latter is present on some Cortex-M33 chips, like
the STM32U5xx. The external dcache is bypassed when accessing internal
SRAM and only used for external memories.
In commit a2dd232410
("drivers: adc: stm32: dma support") the rationale
for gating dcache for adc_stm32 behind STM32H7X is only hinted at, but
reason seems to be that it was the only SOC the change was tested on. The
SOC configures DCACHE=y so it is most likely safe to swap the SOC gate for
DCACHE.
The DCACHE ifdefs are now hidden inside the shared stm32_buf_in_nocache()
implementation.
Signed-off-by: Henrik Lindblom <henrik.lindblom@vaisala.com>
This commit is contained in:
parent
811302e6d2
commit
24b4ce189f
7 changed files with 113 additions and 132 deletions
|
@ -20,6 +20,7 @@
|
|||
#include <zephyr/init.h>
|
||||
#include <zephyr/toolchain.h>
|
||||
#include <soc.h>
|
||||
#include <stm32_cache.h>
|
||||
#include <zephyr/pm/device.h>
|
||||
#include <zephyr/pm/policy.h>
|
||||
#include <stm32_ll_adc.h>
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <zephyr/arch/cpu.h>
|
||||
#include <zephyr/sys/__assert.h>
|
||||
#include <soc.h>
|
||||
#include <stm32_cache.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <zephyr/drivers/clock_control.h>
|
||||
#include <zephyr/pm/policy.h>
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ LOG_MODULE_REGISTER(spi_ll_stm32);
|
|||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <soc.h>
|
||||
#include <stm32_cache.h>
|
||||
#include <stm32_ll_spi.h>
|
||||
#include <errno.h>
|
||||
#include <zephyr/cache.h>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
55
soc/st/stm32/common/stm32_cache.c
Normal file
55
soc/st/stm32/common/stm32_cache.c
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Henrik Lindblom <henrik.lindblom@vaisala.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "stm32_cache.h"
|
||||
#include <zephyr/linker/linker-defs.h>
|
||||
#include <zephyr/mem_mgmt/mem_attr.h>
|
||||
#include <zephyr/dt-bindings/memory-attr/memory-attr-arm.h>
|
||||
#include <zephyr/sys/math_extras.h>
|
||||
|
||||
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;
|
||||
}
|
39
soc/st/stm32/common/stm32_cache.h
Normal file
39
soc/st/stm32/common/stm32_cache.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Henrik Lindblom <henrik.lindblom@vaisala.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef STM32_CACHE_H_
|
||||
#define STM32_CACHE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <zephyr/toolchain.h>
|
||||
|
||||
#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_ */
|
Loading…
Add table
Add a link
Reference in a new issue