Skip to content

Commit 24b4ce1

Browse files
Henrik Lindblomkartben
authored andcommitted
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 a2dd232 ("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 <[email protected]>
1 parent 811302e commit 24b4ce1

File tree

7 files changed

+113
-132
lines changed

7 files changed

+113
-132
lines changed

drivers/adc/adc_stm32.c

Lines changed: 5 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <zephyr/init.h>
2121
#include <zephyr/toolchain.h>
2222
#include <soc.h>
23+
#include <stm32_cache.h>
2324
#include <zephyr/pm/device.h>
2425
#include <zephyr/pm/policy.h>
2526
#include <stm32_ll_adc.h>
@@ -268,36 +269,6 @@ static int adc_stm32_dma_start(const struct device *dev,
268269
}
269270
#endif /* CONFIG_ADC_STM32_DMA */
270271

271-
#if defined(CONFIG_ADC_STM32_DMA) && defined(CONFIG_SOC_SERIES_STM32H7X)
272-
/* Returns true if given buffer is in a non-cacheable SRAM region.
273-
* This is determined using the device tree, meaning the .nocache region won't work.
274-
* The entire buffer must be in a single region.
275-
* An example of how the SRAM region can be defined in the DTS:
276-
* &sram4 {
277-
* zephyr,memory-attr = <( DT_MEM_ARM(ATTR_MPU_RAM_NOCACHE) | ... )>;
278-
* };
279-
*/
280-
static bool buf_in_nocache(uintptr_t buf __maybe_unused, size_t len_bytes __maybe_unused)
281-
{
282-
bool buf_within_nocache = false;
283-
284-
#ifdef CONFIG_NOCACHE_MEMORY
285-
buf_within_nocache = (buf >= ((uintptr_t)_nocache_ram_start)) &&
286-
((buf + len_bytes - 1) <= ((uintptr_t)_nocache_ram_end));
287-
if (buf_within_nocache) {
288-
return true;
289-
}
290-
#endif /* CONFIG_NOCACHE_MEMORY */
291-
292-
#ifdef CONFIG_MEM_ATTR
293-
buf_within_nocache = mem_attr_check_buf(
294-
(void *)buf, len_bytes, DT_MEM_ARM(ATTR_MPU_RAM_NOCACHE)) == 0;
295-
#endif /* CONFIG_MEM_ATTR */
296-
297-
return buf_within_nocache;
298-
}
299-
#endif /* defined(CONFIG_ADC_STM32_DMA) && defined(CONFIG_SOC_SERIES_STM32H7X) */
300-
301272
static int check_buffer(const struct adc_sequence *sequence,
302273
uint8_t active_channels)
303274
{
@@ -315,13 +286,13 @@ static int check_buffer(const struct adc_sequence *sequence,
315286
return -ENOMEM;
316287
}
317288

318-
#if defined(CONFIG_ADC_STM32_DMA) && defined(CONFIG_SOC_SERIES_STM32H7X)
289+
#if defined(CONFIG_ADC_STM32_DMA)
319290
/* Buffer is forced to be in non-cacheable SRAM region to avoid cache maintenance */
320-
if (!buf_in_nocache((uintptr_t)sequence->buffer, needed_buffer_size)) {
321-
LOG_ERR("Supplied buffer is not in a non-cacheable region according to DTS.");
291+
if (!stm32_buf_in_nocache((uintptr_t)sequence->buffer, needed_buffer_size)) {
292+
LOG_ERR("Supplied buffer is not in a non-cacheable region.");
322293
return -EINVAL;
323294
}
324-
#endif
295+
#endif /* CONFIG_ADC_STM32_DMA */
325296

326297
return 0;
327298
}

drivers/i2c/i2c_ll_stm32_v2.c

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -659,37 +659,6 @@ int i2c_stm32_error(const struct device *dev)
659659
return -EIO;
660660
}
661661

662-
#if defined(CONFIG_DCACHE) && defined(CONFIG_I2C_STM32_V2_DMA)
663-
static bool buf_in_nocache(uintptr_t buf, size_t len_bytes)
664-
{
665-
bool buf_within_nocache = false;
666-
667-
#ifdef CONFIG_NOCACHE_MEMORY
668-
/* Check if buffer is in nocache region defined by the linker */
669-
buf_within_nocache = (buf >= ((uintptr_t)_nocache_ram_start)) &&
670-
((buf + len_bytes - 1) <= ((uintptr_t)_nocache_ram_end));
671-
if (buf_within_nocache) {
672-
return true;
673-
}
674-
#endif /* CONFIG_NOCACHE_MEMORY */
675-
676-
#ifdef CONFIG_MEM_ATTR
677-
/* Check if buffer is in nocache memory region defined in DT */
678-
buf_within_nocache = mem_attr_check_buf(
679-
(void *)buf, len_bytes, DT_MEM_ARM(ATTR_MPU_RAM_NOCACHE)) == 0;
680-
if (buf_within_nocache) {
681-
return true;
682-
}
683-
#endif /* CONFIG_MEM_ATTR */
684-
685-
/* Check if buffer is in RO region (Flash..) */
686-
buf_within_nocache = (buf >= ((uintptr_t)__rodata_region_start)) &&
687-
((buf + len_bytes - 1) <= ((uintptr_t)__rodata_region_end));
688-
689-
return buf_within_nocache;
690-
}
691-
#endif /* CONFIG_DCACHE && CONFIG_I2C_STM32_V2_DMA */
692-
693662
static int i2c_stm32_msg_write(const struct device *dev, struct i2c_msg *msg,
694663
uint8_t *next_msg_flags, uint16_t slave)
695664
{
@@ -705,13 +674,13 @@ static int i2c_stm32_msg_write(const struct device *dev, struct i2c_msg *msg,
705674
data->current.is_err = 0U;
706675
data->current.msg = msg;
707676

708-
#if defined(CONFIG_DCACHE) && defined(CONFIG_I2C_STM32_V2_DMA)
709-
if (!buf_in_nocache((uintptr_t)msg->buf, msg->len)) {
677+
#if defined(CONFIG_I2C_STM32_V2_DMA)
678+
if (!stm32_buf_in_nocache((uintptr_t)msg->buf, msg->len)) {
710679
LOG_DBG("Tx buffer at %p (len %zu) is in cached memory; cleaning cache", msg->buf,
711680
msg->len);
712681
sys_cache_data_flush_range((void *)msg->buf, msg->len);
713682
}
714-
#endif /* CONFIG_DCACHE && CONFIG_I2C_STM32_V2_DMA*/
683+
#endif /* CONFIG_I2C_STM32_V2_DMA */
715684

716685
msg_init(dev, msg, next_msg_flags, slave, LL_I2C_REQUEST_WRITE);
717686

@@ -783,14 +752,13 @@ static int i2c_stm32_msg_read(const struct device *dev, struct i2c_msg *msg,
783752
k_sem_take(&data->device_sync_sem, K_FOREVER);
784753
is_timeout = true;
785754
}
786-
787-
#if defined(CONFIG_DCACHE) && defined(CONFIG_I2C_STM32_V2_DMA)
788-
if (!buf_in_nocache((uintptr_t)msg->buf, msg->len)) {
755+
#if defined(CONFIG_I2C_STM32_V2_DMA)
756+
if (!stm32_buf_in_nocache((uintptr_t)msg->buf, msg->len)) {
789757
LOG_DBG("Rx buffer at %p (len %zu) is in cached memory; invalidating cache",
790758
msg->buf, msg->len);
791759
sys_cache_data_invd_range((void *)msg->buf, msg->len);
792760
}
793-
#endif /* CONFIG_DCACHE && CONFIG_I2C_STM32_V2_DMA */
761+
#endif /* CONFIG_I2C_STM32_V2_DMA */
794762

795763
if (data->current.is_nack || data->current.is_err ||
796764
data->current.is_arlo || is_timeout) {

drivers/serial/uart_stm32.c

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <zephyr/arch/cpu.h>
2020
#include <zephyr/sys/__assert.h>
2121
#include <soc.h>
22+
#include <stm32_cache.h>
2223
#include <zephyr/init.h>
2324
#include <zephyr/drivers/clock_control.h>
2425
#include <zephyr/pm/policy.h>
@@ -1378,34 +1379,6 @@ static void uart_stm32_isr(const struct device *dev)
13781379

13791380
#ifdef CONFIG_UART_ASYNC_API
13801381

1381-
#ifdef CONFIG_DCACHE
1382-
static bool buf_in_nocache(uintptr_t buf, size_t len_bytes)
1383-
{
1384-
bool buf_within_nocache = false;
1385-
1386-
#ifdef CONFIG_NOCACHE_MEMORY
1387-
buf_within_nocache = (buf >= ((uintptr_t)_nocache_ram_start)) &&
1388-
((buf + len_bytes - 1) <= ((uintptr_t)_nocache_ram_end));
1389-
if (buf_within_nocache) {
1390-
return true;
1391-
}
1392-
#endif /* CONFIG_NOCACHE_MEMORY */
1393-
1394-
#ifdef CONFIG_MEM_ATTR
1395-
buf_within_nocache = mem_attr_check_buf(
1396-
(void *)buf, len_bytes, DT_MEM_ARM_MPU_RAM_NOCACHE) == 0;
1397-
if (buf_within_nocache) {
1398-
return true;
1399-
}
1400-
#endif /* CONFIG_MEM_ATTR */
1401-
1402-
buf_within_nocache = (buf >= ((uintptr_t)__rodata_region_start)) &&
1403-
((buf + len_bytes - 1) <= ((uintptr_t)__rodata_region_end));
1404-
1405-
return buf_within_nocache;
1406-
}
1407-
#endif /* CONFIG_DCACHE */
1408-
14091382
static int uart_stm32_async_callback_set(const struct device *dev,
14101383
uart_callback_t callback,
14111384
void *user_data)
@@ -1631,12 +1604,10 @@ static int uart_stm32_async_tx(const struct device *dev,
16311604
return -EBUSY;
16321605
}
16331606

1634-
#ifdef CONFIG_DCACHE
1635-
if (!buf_in_nocache((uintptr_t)tx_data, buf_size)) {
1607+
if (!stm32_buf_in_nocache((uintptr_t)tx_data, buf_size)) {
16361608
LOG_ERR("Tx buffer should be placed in a nocache memory region");
16371609
return -EFAULT;
16381610
}
1639-
#endif /* CONFIG_DCACHE */
16401611

16411612
#ifdef CONFIG_PM
16421613
data->tx_poll_stream_on = false;
@@ -1743,12 +1714,10 @@ static int uart_stm32_async_rx_enable(const struct device *dev,
17431714
return -EBUSY;
17441715
}
17451716

1746-
#ifdef CONFIG_DCACHE
1747-
if (!buf_in_nocache((uintptr_t)rx_buf, buf_size)) {
1717+
if (!stm32_buf_in_nocache((uintptr_t)rx_buf, buf_size)) {
17481718
LOG_ERR("Rx buffer should be placed in a nocache memory region");
17491719
return -EFAULT;
17501720
}
1751-
#endif /* CONFIG_DCACHE */
17521721

17531722
data->dma_rx.offset = 0;
17541723
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,
18741843
} else if (!data->dma_rx.enabled) {
18751844
err = -EACCES;
18761845
} else {
1877-
#ifdef CONFIG_DCACHE
1878-
if (!buf_in_nocache((uintptr_t)buf, len)) {
1846+
if (!stm32_buf_in_nocache((uintptr_t)buf, len)) {
18791847
LOG_ERR("Rx buffer should be placed in a nocache memory region");
18801848
return -EFAULT;
18811849
}
1882-
#endif /* CONFIG_DCACHE */
18831850
data->rx_next_buffer = buf;
18841851
data->rx_next_buffer_len = len;
18851852
}

drivers/spi/spi_ll_stm32.c

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ LOG_MODULE_REGISTER(spi_ll_stm32);
1313
#include <zephyr/sys/util.h>
1414
#include <zephyr/kernel.h>
1515
#include <soc.h>
16+
#include <stm32_cache.h>
1617
#include <stm32_ll_spi.h>
1718
#include <errno.h>
1819
#include <zephyr/cache.h>
@@ -1076,28 +1077,6 @@ static int wait_dma_rx_tx_done(const struct device *dev)
10761077
}
10771078

10781079
#ifdef CONFIG_DCACHE
1079-
static bool buf_in_nocache(uintptr_t buf __maybe_unused, size_t len_bytes __maybe_unused)
1080-
{
1081-
bool buf_within_nocache = false;
1082-
1083-
#ifdef CONFIG_NOCACHE_MEMORY
1084-
/* Check if buffer is in nocache region defined by the linker */
1085-
buf_within_nocache = (buf >= ((uintptr_t)_nocache_ram_start)) &&
1086-
((buf + len_bytes - 1) <= ((uintptr_t)_nocache_ram_end));
1087-
if (buf_within_nocache) {
1088-
return true;
1089-
}
1090-
#endif /* CONFIG_NOCACHE_MEMORY */
1091-
1092-
#ifdef CONFIG_MEM_ATTR
1093-
/* Check if buffer is in nocache memory region defined in DT */
1094-
buf_within_nocache = mem_attr_check_buf(
1095-
(void *)buf, len_bytes, DT_MEM_ARM(ATTR_MPU_RAM_NOCACHE)) == 0;
1096-
#endif /* CONFIG_MEM_ATTR */
1097-
1098-
return buf_within_nocache;
1099-
}
1100-
11011080
static bool is_dummy_buffer(const struct spi_buf *buf)
11021081
{
11031082
return buf->buf == NULL;
@@ -1109,7 +1088,7 @@ static bool spi_buf_set_in_nocache(const struct spi_buf_set *bufs)
11091088
const struct spi_buf *buf = &bufs->buffers[i];
11101089

11111090
if (!is_dummy_buffer(buf) &&
1112-
!buf_in_nocache((uintptr_t)buf->buf, buf->len)) {
1091+
!stm32_buf_in_nocache((uintptr_t)buf->buf, buf->len)) {
11131092
return false;
11141093
}
11151094
}

soc/st/stm32/common/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ SoC Power Management (CONFIG_PM) enabled but the DBGMCU is still enabled \
3333
(CONFIG_STM32_ENABLE_DEBUG_SLEEP_STOP). The SoC will use more power than expected \
3434
in STOP modes due to internal oscillators that remain active.")
3535
endif()
36+
37+
zephyr_sources_ifdef(CONFIG_DCACHE stm32_cache.c)

soc/st/stm32/common/stm32_cache.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright (c) 2025 Henrik Lindblom <[email protected]>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#include "stm32_cache.h"
7+
#include <zephyr/linker/linker-defs.h>
8+
#include <zephyr/mem_mgmt/mem_attr.h>
9+
#include <zephyr/dt-bindings/memory-attr/memory-attr-arm.h>
10+
#include <zephyr/sys/math_extras.h>
11+
12+
bool stm32_buf_in_nocache(uintptr_t buf, size_t len_bytes)
13+
{
14+
uintptr_t buf_end;
15+
16+
/* Note: (len_bytes - 1) is safe because a length of 0 would overflow to 4 billion. */
17+
if (u32_add_overflow(buf, len_bytes - 1, (uint32_t *)&buf_end)) {
18+
return false;
19+
}
20+
21+
#ifdef CONFIG_EXTERNAL_CACHE
22+
/* STM32 DCACHE is enabled but it does not cover internal SRAM. If the buffer is in SRAM it
23+
* is not cached by DCACHE.
24+
*
25+
* NOTE: This incorrectly returns true if Zephyr image RAM resides in external memory.
26+
*/
27+
if (((uintptr_t)_image_ram_start) <= buf && buf_end < ((uintptr_t)_image_ram_end)) {
28+
return true;
29+
}
30+
#endif /* CONFIG_EXTERNAL_CACHE */
31+
32+
#ifdef CONFIG_NOCACHE_MEMORY
33+
/* Check if buffer is in NOCACHE region defined by the linker. */
34+
if ((uintptr_t)_nocache_ram_start <= buf && buf_end <= (uintptr_t)_nocache_ram_end) {
35+
return true;
36+
}
37+
#endif /* CONFIG_NOCACHE_MEMORY */
38+
39+
#ifdef CONFIG_MEM_ATTR
40+
/* Check if buffer is in a region marked NOCACHE in DT. */
41+
if (mem_attr_check_buf((void *)buf, len_bytes, DT_MEM_ARM_MPU_RAM_NOCACHE) == 0) {
42+
return true;
43+
}
44+
#endif /* CONFIG_MEM_ATTR */
45+
46+
/* Check if buffer is in read-only region, which cannot be stale due to DCACHE because it is
47+
* not writeable.
48+
*/
49+
if ((uintptr_t)__rodata_region_start <= buf && buf_end <= (uintptr_t)__rodata_region_end) {
50+
return true;
51+
}
52+
53+
/* Not in any region known to be NOCACHE */
54+
return false;
55+
}

soc/st/stm32/common/stm32_cache.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (c) 2025 Henrik Lindblom <[email protected]>
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#ifndef STM32_CACHE_H_
7+
#define STM32_CACHE_H_
8+
9+
#include <stdint.h>
10+
#include <stddef.h>
11+
#include <stdbool.h>
12+
#include <zephyr/toolchain.h>
13+
14+
#if defined(CONFIG_DCACHE)
15+
/**
16+
* @brief Check if the given buffer resides in nocache memory
17+
*
18+
* Always returns true if CONFIG_DCACHE is not set.
19+
*
20+
* @param buf Buffer
21+
* @param len_bytes Buffer size in bytes
22+
* @return true if buf resides completely in nocache memory
23+
*/
24+
bool stm32_buf_in_nocache(uintptr_t buf, size_t len_bytes);
25+
26+
#else /* !CONFIG_DCACHE */
27+
28+
static inline bool stm32_buf_in_nocache(uintptr_t buf, size_t len_bytes)
29+
{
30+
ARG_UNUSED(buf);
31+
ARG_UNUSED(len_bytes);
32+
33+
/* Whole memory is nocache if DCACHE is disabled */
34+
return true;
35+
}
36+
37+
#endif /* CONFIG_DCACHE */
38+
39+
#endif /* STM32_CACHE_H_ */

0 commit comments

Comments
 (0)