Skip to content

cache: stm32: add cortex-m33 peripheral driver #88585

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/cache/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ zephyr_library_sources_ifdef(CONFIG_CACHE_ANDES cache_andes.c)
zephyr_library_sources_ifdef(CONFIG_USERSPACE cache_handlers.c)
zephyr_library_sources_ifdef(CONFIG_CACHE_NRF_CACHE cache_nrf.c)
zephyr_library_sources_ifdef(CONFIG_CACHE_NXP_XCACHE cache_nxp_xcache.c)
zephyr_library_sources_ifdef(CONFIG_CACHE_STM32 cache_stm32.c)
1 change: 1 addition & 0 deletions drivers/cache/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ source "drivers/cache/Kconfig.aspeed"
source "drivers/cache/Kconfig.nrf"
source "drivers/cache/Kconfig.andes"
source "drivers/cache/Kconfig.nxp_xcache"
source "drivers/cache/Kconfig.stm32"

endif # CACHE
24 changes: 24 additions & 0 deletions drivers/cache/Kconfig.stm32
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright (c) 2025 Henrik Lindblom <[email protected]>
# SPDX-License-Identifier: Apache-2.0

menuconfig CACHE_STM32
bool "STM32 cache driver"
select CACHE_HAS_DRIVER
depends on CACHE_MANAGEMENT
help
Enable support for the STM32 ICACHE / DCACHE peripheral present in some STM32 chips.

if CACHE_STM32

# "default n" for L5 is legacy - could be removed?
config CACHE_STM32_ICACHE_DIRECT_MAPPING
bool "Use 1-way associative mapping for ICACHE"
default n if SOC_SERIES_STM32L5X
Comment on lines +13 to +16
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be ok to move L5 to 1-way associated mapping, but we should document the change in migration guide and mention how to revert.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it's probably fine, but I'd suggest doing that in a separate PR.

default y
help
Use ICACHE in direct mapping (1-way associative) mode instead of the default n-way
associative cache mode.

This option reduces power consumption but slightly reduces cache's performance.

endif # CACHE_STM32
182 changes: 182 additions & 0 deletions drivers/cache/cache_stm32.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/*
* Copyright (c) 2025 Henrik Lindblom <[email protected]>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/drivers/cache.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/math_extras.h>
#include <stm32_ll_dcache.h>
#include <stm32_ll_icache.h>

LOG_MODULE_REGISTER(cache_stm32, CONFIG_CACHE_LOG_LEVEL);

#ifdef CONFIG_DCACHE

void cache_data_enable(void)
{
LL_DCACHE_Enable(DCACHE1);
#if defined(DCACHE2)
LL_DCACHE_Enable(DCACHE2);
#endif
}

void cache_data_disable(void)
{
cache_data_flush_all();

while (LL_DCACHE_IsActiveFlag_BUSYCMD(DCACHE1)) {
}

LL_DCACHE_Disable(DCACHE1);
LL_DCACHE_ClearFlag_BSYEND(DCACHE1);

#if defined(DCACHE2)
while (LL_DCACHE_IsActiveFlag_BUSYCMD(DCACHE2)) {
}

LL_DCACHE_Disable(DCACHE2);
LL_DCACHE_ClearFlag_BSYEND(DCACHE2);
#endif
}

static int cache_data_manage_range(void *addr, size_t size, uint32_t command)
{
/*
* This is a simple approach to invalidate the range. The address might be in either DCACHE1
* or DCACHE2 (if present). The cache invalidation algorithm checks the TAG memory for the
* specified address range so there's little harm in just checking both caches.
*/
uint32_t start = (uint32_t)addr;
uint32_t end;

if (u32_add_overflow(start, size, &end)) {
return -EOVERFLOW;
}

LL_DCACHE_SetStartAddress(DCACHE1, start);
LL_DCACHE_SetEndAddress(DCACHE1, end);
LL_DCACHE_SetCommand(DCACHE1, command);
LL_DCACHE_StartCommand(DCACHE1);
#if defined(DCACHE2)
LL_DCACHE_SetStartAddress(DCACHE2, start);
LL_DCACHE_SetEndAddress(DCACHE2, end);
LL_DCACHE_SetCommand(DCACHE2, command);
LL_DCACHE_StartCommand(DCACHE2);
#endif
return 0;
}

int cache_data_flush_range(void *addr, size_t size)
{
return cache_data_manage_range(addr, size, LL_DCACHE_COMMAND_CLEAN_BY_ADDR);
}

int cache_data_invd_range(void *addr, size_t size)
{
return cache_data_manage_range(addr, size, LL_DCACHE_COMMAND_INVALIDATE_BY_ADDR);
}

int cache_data_flush_and_invd_range(void *addr, size_t size)
{
return cache_data_manage_range(addr, size, LL_DCACHE_COMMAND_CLEAN_INVALIDATE_BY_ADDR);
}

int cache_data_flush_all(void)
{
return cache_data_flush_range(0, UINT32_MAX);
}

int cache_data_invd_all(void)
{
LL_DCACHE_Invalidate(DCACHE1);
#if defined(DCACHE2)
LL_DCACHE_Invalidate(DCACHE2);
#endif
return 0;
}

int cache_data_flush_and_invd_all(void)
{
return cache_data_flush_and_invd_range(0, UINT32_MAX);
}

#endif /* CONFIG_DCACHE */

static inline void wait_for_icache(void)
{
while (LL_ICACHE_IsActiveFlag_BUSY()) {
}

/* Clear BSYEND to avoid an extra interrupt if somebody enables them. */
LL_ICACHE_ClearFlag_BSYEND();
}

void cache_instr_enable(void)
{
if (IS_ENABLED(CONFIG_CACHE_STM32_ICACHE_DIRECT_MAPPING)) {
LL_ICACHE_SetMode(LL_ICACHE_1WAY);
}

/*
* Need to wait until any pending cache invalidation operations finish. This is recommended
* in the reference manual to ensure execution timing determinism.
*/
wait_for_icache();
LL_ICACHE_Enable();
}

void cache_instr_disable(void)
{
LL_ICACHE_Disable();

while (LL_ICACHE_IsEnabled()) {
/**
* Wait until the ICACHE is disabled (CR.EN=0), at which point
* all requests bypass the cache and are forwarded directly
* from the ICACHE slave port to the ICACHE master port(s).
*
* The cache invalidation will start once disabled, but we allow
* it to proceed in the background since it doesn't need to be
* complete for requests to bypass the ICACHE.
*/
}
}

int cache_instr_flush_all(void)
{
return -ENOTSUP;
}

int cache_instr_invd_all(void)
{
LL_ICACHE_Invalidate();
return 0;
}

int cache_instr_flush_and_invd_all(void)
{
return -ENOTSUP;
}

int cache_instr_flush_range(void *addr, size_t size)
{
ARG_UNUSED(addr);
ARG_UNUSED(size);
return -ENOTSUP;
}

int cache_instr_invd_range(void *addr, size_t size)
{
ARG_UNUSED(addr);
ARG_UNUSED(size);
return -ENOTSUP;
}

int cache_instr_flush_and_invd_range(void *addr, size_t size)
{
ARG_UNUSED(addr);
ARG_UNUSED(size);
return -ENOTSUP;
}
Loading