From 310b4aa45898e17a1870870bb47d5b6771cd001b Mon Sep 17 00:00:00 2001 From: Philippe Thierry Date: Fri, 22 May 2026 10:54:21 +0200 Subject: [PATCH 01/11] dma: initial integration of STM32 DMA-v1 driver for STM32F4xx devices --- dts/examples/stm32f429i_disc1_autotest.dts | 23 +++++ kernel/src/arch/asm-cortex-m/Kconfig | 3 +- kernel/src/drivers/dma/meson.build | 21 +++- kernel/src/drivers/dma/stm32-dma-dt.c.in | 108 +++++++++++++++++++++ kernel/src/drivers/dma/stm32-dma-dt.h.in | 79 +++++++++++++++ 5 files changed, 231 insertions(+), 3 deletions(-) create mode 100644 kernel/src/drivers/dma/stm32-dma-dt.c.in create mode 100644 kernel/src/drivers/dma/stm32-dma-dt.h.in diff --git a/dts/examples/stm32f429i_disc1_autotest.dts b/dts/examples/stm32f429i_disc1_autotest.dts index 9a0ca63e..4fed4034 100644 --- a/dts/examples/stm32f429i_disc1_autotest.dts +++ b/dts/examples/stm32f429i_disc1_autotest.dts @@ -63,6 +63,29 @@ sentry,label = <0xf03>; sentry,owner = <0xbabe>; }; + dma-streams { + // device-to-memory DMA stream + stream1 { + compatible = "dma-stream"; + channel = <&dma1_1>; + streamid = <1>; // channel stream (af) identifier + prio = ; + source = <&shm_autotest_1>; + dest = <&shm_autotest_2>; + length = <42>; + circular = <1 0>; // circular source, linear dest + sentry,label = <0x1>; // task-level unique DMA identifier + }; + }; + }; +}; + +&dma1 { + status = "okay"; + // About channels that are used + dma1_1: dma-channel@1 { + status = "okay"; + sentry,owner = <0xbabe>; }; }; diff --git a/kernel/src/arch/asm-cortex-m/Kconfig b/kernel/src/arch/asm-cortex-m/Kconfig index c4c8cb9c..dcf917c3 100644 --- a/kernel/src/arch/asm-cortex-m/Kconfig +++ b/kernel/src/arch/asm-cortex-m/Kconfig @@ -97,6 +97,7 @@ config SOC_SUBFAMILY_STM32F4 select HAS_DCACHE select HAS_ICACHE select HAS_MPU_PMSA_V7 + select HAS_GPDMA help STM32F4 family is based on Cortex-M4 with FPU and PMSAv7 MPU. @@ -318,4 +319,4 @@ config RUSTC_TARGET default "thumbv8m.main-none-eabi" if ARCH_ARM_ARMV8MML && !FPU_HARDFP_ABI default "thumbv8m.main-none-eabihf" if ARCH_ARM_ARMV8MML && FPU_HARDFP_ABI -endmenu \ No newline at end of file +endmenu diff --git a/kernel/src/drivers/dma/meson.build b/kernel/src/drivers/dma/meson.build index 81476b89..359a90f2 100644 --- a/kernel/src/drivers/dma/meson.build +++ b/kernel/src/drivers/dma/meson.build @@ -1,6 +1,10 @@ # SPDX-FileCopyrightText: 2023 Ledger SAS # SPDX-License-Identifier: Apache-2.0 +# for U5A5 GPDMA implementation, the register definitions are generated from the SVD file using jinja templates. +# This allows us to have a single source of truth for the register definitions and avoid manual errors. +if kconfig_data.get('CONFIG_SOC_SUBFAMILY_STM32U5', 0) == 1 + gpdma_h = custom_target('gen_gpdma', input: peripheral_defs_in, output: '@0@_defs.h'.format('gpdma'), @@ -11,18 +15,31 @@ gpdma_h = custom_target('gen_gpdma', '@INPUT@' ], ) - bsp_private_gen_header_set.add(gpdma_h) +endif + + + stm32_gpdma_dts_template_c = files(['stm32-gpdma-dt.c.in']) stm32_gpdma_dts_template_h = files(['stm32-gpdma-dt.h.in']) stm32_gpdma_dtsgen_c = dtsgen.process(stm32_gpdma_dts_template_c) stm32_gpdma_dtsgen_h = dtsgen.process(stm32_gpdma_dts_template_h) +stm32_dma_dts_template_c = files(['stm32-dma-dt.c.in']) +stm32_dma_dts_template_h = files(['stm32-dma-dt.h.in']) + +stm32_dma_dtsgen_c = dtsgen.process(stm32_dma_dts_template_c) +stm32_dma_dtsgen_h = dtsgen.process(stm32_dma_dts_template_h) -bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32U5', if_true: [ stm32_gpdma_dtsgen_c, stm32_gpdma_dtsgen_h ]) +bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32U5', if_true: [ stm32_gpdma_dtsgen_c, stm32_gpdma_dtsgen_h ]) bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32U5', if_true: files('stm32u5-gpdma.c')) +bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32F4', if_true: [ stm32_dma_dtsgen_c, stm32_dma_dtsgen_h ]) +# driver implementation for STM32F4 dma to be added +# bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32F4', if_true: files('stm32f4-gpdma.c')) + + # driver source selection diff --git a/kernel/src/drivers/dma/stm32-dma-dt.c.in b/kernel/src/drivers/dma/stm32-dma-dt.c.in new file mode 100644 index 00000000..e5c06999 --- /dev/null +++ b/kernel/src/drivers/dma/stm32-dma-dt.c.in @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2023 Ledger SAS +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "stm32-dma-dt.h" + +{% set gpdma_ports = dts.get_compatible("stm32-dma-v1") -%} + +{% for port in gpdma_ports -%} +{% if port is owned or port is not enabled -%} +{% continue -%} +{% endif -%} + +/* channel ordered interrupts for each controller, controller-ordered */ +static const uint16_t stm32_{{ port.label }}_interrupts[] = { + /* {{ port['dma-channels']|int }} DMA channels */ + {% for chanirq in range((2*port['dma-channels'])) -%} + {% if port['interrupts'][chanirq]|int != 0 -%} + {{ port['interrupts'][chanirq] }}, /* channel {{ (chanirq / 2)|int }} interrupt identifier */ + {% endif -%} + {% endfor -%} + 0, /* sentinel */ +}; + +#define IRQ_IS_{{ port.label|upper }}_OWNED(x) ( \ +{% for chanirq in range((2*port['dma-channels'])) -%} +{% if port['interrupts'][chanirq]|int != 0 -%} + {{ port['interrupts'][chanirq] }} == x || \ +{% endif -%} +{% endfor -%} + 0 \ +) +{% endfor -%} + +bool stm32_gpdma_irq_is_dma_owned(int IRQn) +{ + return ( \ + {% for port in gpdma_ports -%} + {% if port is owned or port is not enabled -%} + {% continue -%} + {% endif -%} + IRQ_IS_{{ port.label|upper }}_OWNED(IRQn) || \ + {% endfor -%} + 0); +} + +/** + * \brief .rodata field: list of current platform GPDMA ports + */ +static const stm32_gpdma_desc_t stm32_gpdmas[] = { + {% for port in gpdma_ports -%} + {% if port is owned or port is not enabled -%} + {% continue -%} + {% endif -%} + {% set _, bus_id, clk_msk = port.clocks -%} + {%- set num_chan = port['dma-channels'] -%} + {%- set num_req = port['dma-requests'] -%} + /* {{ port.label }} port configuration */ + { + .base_addr = {{ "%#08xUL"|format(port.reg[0]) }}, + .size = {{ "%#08xUL"|format(port.reg[1]) }}, + .bus_id = {{ bus_id }}, + .clk_msk = {{ "%#08xUL"|format(clk_msk) }}, + .num_chan = {{ num_chan|int }}, + .num_req = {{ num_req|int }}, + .interrupts = &stm32_{{ port.label}}_interrupts[0], + }, + {% endfor -%} + {} /* sentinel */ +}; + + +/** + * @warning this is a private function, port id must be valid and checked by caller + */ +const stm32_gpdma_desc_t * stm32_gpdma_get_desc(uint8_t ctrl) +{ + const stm32_gpdma_desc_t *desc = NULL; +#if GPDMA_NUMER > 0 + if (unlikely(ctrl >= GPDMA_NUMBER)) { + goto end; + } + desc = &stm32_gpdmas[ctrl]; +end: +#endif + return desc; +} + +/** + * @warning this is a private function, controller id must be valid and checked by caller + */ +uint16_t const * stm32_gpdma_get_interrupts(uint8_t ctrl) +{ + uint16_t const * its = NULL; +#if GPDMA_NUMER > 0 + if (unlikely(ctrl >= GPDMA_NUMBER)) { + goto end; + } + /*@ assert \valid_read(stm32_gpdmas[ctrl].interrupts); */ + its = stm32_gpdmas[ctrl].interrupts; +end: +#endif + return its; +} + +bool gpdma_irq_is_dma_owned(int IRQn) __attribute__((alias("stm32_gpdma_irq_is_dma_owned"))); diff --git a/kernel/src/drivers/dma/stm32-dma-dt.h.in b/kernel/src/drivers/dma/stm32-dma-dt.h.in new file mode 100644 index 00000000..82ac0c73 --- /dev/null +++ b/kernel/src/drivers/dma/stm32-dma-dt.h.in @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2023 Ledger SAS +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __STM32_DMA_DT_GENERATED_H +#define __STM32_DMA_DT_GENERATED_H + +#include + +#include +#include + +{% set gpdma_ports = dts.get_compatible("stm32-dma-v1") -%} + +{% set ns = namespace() -%} +{% set ns.num_gpdma=0 -%} +{% set ns.num_req=0 -%} +{% set ns.num_chan=0 -%} + +{% for port in gpdma_ports -%} +{% if port is owned or port is not enabled -%} + {% continue -%} +{% endif -%} +{% set ns.num_chan = port['dma-channels']|int -%} +{% set ns.num_req = port['dma-requests']|int -%} +{% set ns.num_gpdma = ns.num_gpdma + 1 -%} +{% endfor -%} + +#define GPDMA_NUMBER {{ "%uUL"|format(ns.num_gpdma) }} + +/*@ + +// it is assumed here that when multiple GP DMA controlers +// exists, they have the same requests and chan numbers +// (like, for e.g. in STM32F4 (3 GPDMAs), and only the +// request type may vary +logic gpdma_request_is_valid(ℤ req) = req < {{ ns.num_req }}; + +// again we consider here that all GPDMA has the same channel +// number +logic gpdma_channel_is_valid(ℤ chan) = chan < {{ ns.num_chan }}; + +logic gpdma_controler_exists(ℤ ctrlid) = ctrlid < {{ "%u"|format(ns.num_gpdma) }}; + +*/ + +/** + * \brief STM32 GPDMA IP descriptor + * + * Descriptor is feed from device tree file + * + * \note Only node w/ compatible="st,stm32-usart" and status="okay" properties + * will be parsed + */ +typedef struct stm32_gpdma_desc { + uint32_t base_addr; /**< IP base address */ + size_t size; /**< IP base address */ + bus_id_t bus_id; /**< Bus on which the DMA controller is connected, needed for RCC config */ + uint32_t clk_msk; /**< IP clocks mask on the given bus */ + uint16_t num_chan; /***< IP number of channels */ + uint16_t num_req; /**< IP number of requests */ + uint16_t const *interrupts; /**< IP interrupts, hyp: 1 IRQ per channel */ +} stm32_gpdma_desc_t; + +const stm32_gpdma_desc_t * stm32_gpdma_get_desc(uint8_t ctrl); + +uint16_t const * stm32_gpdma_get_interrupts(uint8_t ctrl); + +static inline kstatus_t gpdma_map(uint8_t controller) +{ + stm32_gpdma_desc_t const * dma_desc = stm32_gpdma_get_desc(controller); + return mgr_mm_map_kdev(dma_desc->base_addr, dma_desc->size); +} + +/* for simplicity sake, but unmaping a kernel device is generic */ +static inline kstatus_t gpdma_unmap(void) { + return mgr_mm_unmap_kdev(); +} + +#endif /* __STM32_DMA_DT_GENERATED_H */ From e87a17eacf4fd316652db06e34842381d8999670 Mon Sep 17 00:00:00 2001 From: Philippe Thierry Date: Fri, 22 May 2026 11:13:34 +0200 Subject: [PATCH 02/11] sentry,dma: adding stub for STM32 dma-v1 driver for STM32F4 --- dts/examples/stm32f429i_disc1_autotest.dts | 2 + kernel/src/drivers/dma/meson.build | 3 +- kernel/src/drivers/dma/stm32f4-dma.c | 97 ++++++++++++++++++++++ kernel/src/managers/dma/dma-dt.c.in | 2 +- 4 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 kernel/src/drivers/dma/stm32f4-dma.c diff --git a/dts/examples/stm32f429i_disc1_autotest.dts b/dts/examples/stm32f429i_disc1_autotest.dts index 4fed4034..ef676769 100644 --- a/dts/examples/stm32f429i_disc1_autotest.dts +++ b/dts/examples/stm32f429i_disc1_autotest.dts @@ -82,6 +82,8 @@ &dma1 { status = "okay"; + // STM32F4 DMA has 8 channels + dma-channels = <8>; // About channels that are used dma1_1: dma-channel@1 { status = "okay"; diff --git a/kernel/src/drivers/dma/meson.build b/kernel/src/drivers/dma/meson.build index 359a90f2..4faf7cdc 100644 --- a/kernel/src/drivers/dma/meson.build +++ b/kernel/src/drivers/dma/meson.build @@ -38,8 +38,7 @@ bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32U5', if_true: [ bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32U5', if_true: files('stm32u5-gpdma.c')) bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32F4', if_true: [ stm32_dma_dtsgen_c, stm32_dma_dtsgen_h ]) -# driver implementation for STM32F4 dma to be added -# bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32F4', if_true: files('stm32f4-gpdma.c')) +bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32F4', if_true: files('stm32f4-dma.c')) # driver source selection diff --git a/kernel/src/drivers/dma/stm32f4-dma.c b/kernel/src/drivers/dma/stm32f4-dma.c new file mode 100644 index 00000000..82925dea --- /dev/null +++ b/kernel/src/drivers/dma/stm32f4-dma.c @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: 2023 Ledger SAS +// SPDX-License-Identifier: Apache-2.0 + +/** + * \file General purpose DMA driver for STM32F4xx (stm32-dma-v1 compatible) SoCs + */ + +#include +#include +#include +#include +#include +#include +#include "stm32-dma-dt.h" + + +/** + * @brief probe given GPDMA controller identifier + */ +kstatus_t gpdma_probe(uint8_t controller) +{ + return K_STATUS_OKAY; +} + +/** + * @brief clear given channel status flags + */ +kstatus_t gpdma_channel_clear_status(gpdma_stream_cfg_t const*const desc) +{ + return K_STATUS_OKAY; +} + +/** + * @brief get back current status of given DMA descriptor's stream + */ +kstatus_t gpdma_channel_get_status(gpdma_stream_cfg_t const*const desc, gpdma_chan_status_t * status) +{ + return K_STATUS_OKAY; +} + +/** + * @brief configure a DMA channel with given DMA descriptor + * + * @note this function do not enable the DMA channel, but only configure it + */ +kstatus_t gpdma_channel_configure(gpdma_stream_cfg_t const*const desc) +{ + return K_STATUS_OKAY; +} + +/** + * @brief enable a previously configured DMA channel + */ +kstatus_t gpdma_channel_enable(gpdma_stream_cfg_t const*const desc) +{ + return K_STATUS_OKAY; +} + +/** + * @brief given a stream, get back the associated IRQn + */ +kstatus_t gpdma_get_interrupt(gpdma_stream_cfg_t const * const desc, uint16_t * const IRQn) +{ + return K_STATUS_OKAY; +} + +/** + * @brief clear interrupt of corresponding stream at GPDMA level +*/ +kstatus_t gpdma_interrupt_clear(gpdma_stream_cfg_t const * const desc) +{ + return K_STATUS_OKAY; +} + +/** + * @brief suspend currently started stream + */ +kstatus_t gpdma_channel_suspend(gpdma_stream_cfg_t const*const desc) +{ + return K_STATUS_OKAY; +} + +/** + * @brief resume previously suspended stream + */ +kstatus_t gpdma_channel_resume(gpdma_stream_cfg_t const*const desc) +{ + return K_STATUS_OKAY; +} + +/** + * @brief reset currently suspended stream + */ +kstatus_t gpdma_channel_reset(gpdma_stream_cfg_t const*const desc) +{ + return K_STATUS_OKAY; +} diff --git a/kernel/src/managers/dma/dma-dt.c.in b/kernel/src/managers/dma/dma-dt.c.in index 8153f527..8ee85853 100644 --- a/kernel/src/managers/dma/dma-dt.c.in +++ b/kernel/src/managers/dma/dma-dt.c.in @@ -11,7 +11,7 @@ #include #include "dma-dt.h" -{% set gpdma_ports = dts.get_compatible("stm32u5-dma") -%} +{% set gpdma_ports = dts.get_compatible("stm32u5-dma") or dts.get_compatible("stm32-dma-v1") -%} {%- macro stream_get_transfer_type(node) -%} {% set source = node["source"] -%} From d4a1284427e9b3804587572f4b719f23ec6f559d Mon Sep 17 00:00:00 2001 From: Philippe Thierry Date: Fri, 22 May 2026 11:44:35 +0200 Subject: [PATCH 03/11] sentry,dma: add support for st-dma-v1 DMA controllers --- kernel/src/drivers/dma/meson.build | 2 +- kernel/src/drivers/dma/stm32-dmav1.c | 578 +++++++++++++++++++++++++++ kernel/src/drivers/dma/stm32f4-dma.c | 97 ----- 3 files changed, 579 insertions(+), 98 deletions(-) create mode 100644 kernel/src/drivers/dma/stm32-dmav1.c delete mode 100644 kernel/src/drivers/dma/stm32f4-dma.c diff --git a/kernel/src/drivers/dma/meson.build b/kernel/src/drivers/dma/meson.build index 4faf7cdc..7ce6a03a 100644 --- a/kernel/src/drivers/dma/meson.build +++ b/kernel/src/drivers/dma/meson.build @@ -38,7 +38,7 @@ bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32U5', if_true: [ bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32U5', if_true: files('stm32u5-gpdma.c')) bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32F4', if_true: [ stm32_dma_dtsgen_c, stm32_dma_dtsgen_h ]) -bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32F4', if_true: files('stm32f4-dma.c')) +bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32F4', if_true: files('stm32-dmav1.c')) # driver source selection diff --git a/kernel/src/drivers/dma/stm32-dmav1.c b/kernel/src/drivers/dma/stm32-dmav1.c new file mode 100644 index 00000000..cc96638f --- /dev/null +++ b/kernel/src/drivers/dma/stm32-dmav1.c @@ -0,0 +1,578 @@ +// SPDX-FileCopyrightText: 2023 Ledger SAS +// SPDX-License-Identifier: Apache-2.0 + +/** + * \file General purpose DMA driver for STM32F4xx (stm32-dmav1 compatible) SoCs + */ + +#include +#include +#include +#include +#include +#include +#include "stm32-dma-dt.h" + +#define STM32_DMA_LISR_REG 0x00UL +#define STM32_DMA_HISR_REG 0x04UL +#define STM32_DMA_LIFCR_REG 0x08UL +#define STM32_DMA_HIFCR_REG 0x0CUL + +#define STM32_DMA_SxCR(s) (0x10UL + (0x18UL * (s))) +#define STM32_DMA_SxNDTR(s) (0x14UL + (0x18UL * (s))) +#define STM32_DMA_SxPAR(s) (0x18UL + (0x18UL * (s))) +#define STM32_DMA_SxM0AR(s) (0x1CUL + (0x18UL * (s))) +#define STM32_DMA_SxM1AR(s) (0x20UL + (0x18UL * (s))) +#define STM32_DMA_SxFCR(s) (0x24UL + (0x18UL * (s))) + +#define STM32_DMA_SxCR_EN (1UL << 0) +#define STM32_DMA_SxCR_DMEIE (1UL << 1) +#define STM32_DMA_SxCR_TEIE (1UL << 2) +#define STM32_DMA_SxCR_HTIE (1UL << 3) +#define STM32_DMA_SxCR_TCIE (1UL << 4) +#define STM32_DMA_SxCR_DIR_SHIFT 6UL +#define STM32_DMA_SxCR_DIR_MASK (0x3UL << STM32_DMA_SxCR_DIR_SHIFT) +#define STM32_DMA_SxCR_CIRC (1UL << 8) +#define STM32_DMA_SxCR_PINC (1UL << 9) +#define STM32_DMA_SxCR_MINC (1UL << 10) +#define STM32_DMA_SxCR_PSIZE_SHIFT 11UL +#define STM32_DMA_SxCR_PSIZE_MASK (0x3UL << STM32_DMA_SxCR_PSIZE_SHIFT) +#define STM32_DMA_SxCR_MSIZE_SHIFT 13UL +#define STM32_DMA_SxCR_MSIZE_MASK (0x3UL << STM32_DMA_SxCR_MSIZE_SHIFT) +#define STM32_DMA_SxCR_PL_SHIFT 16UL +#define STM32_DMA_SxCR_PL_MASK (0x3UL << STM32_DMA_SxCR_PL_SHIFT) +#define STM32_DMA_SxCR_CHSEL_SHIFT 25UL +#define STM32_DMA_SxCR_CHSEL_MASK (0x7UL << STM32_DMA_SxCR_CHSEL_SHIFT) + +#define STM32_DMA_SxFCR_FEIE (1UL << 7) + +#define STM32_DMA_DIR_PERIPH_TO_MEM 0UL +#define STM32_DMA_DIR_MEM_TO_PERIPH 1UL +#define STM32_DMA_DIR_MEM_TO_MEM 2UL + +#define STM32_DMA_STATUS_FLAG_FEIF (1UL << 0) +#define STM32_DMA_STATUS_FLAG_DMEIF (1UL << 1) +#define STM32_DMA_STATUS_FLAG_TEIF (1UL << 2) +#define STM32_DMA_STATUS_FLAG_HTIF (1UL << 3) +#define STM32_DMA_STATUS_FLAG_TCIF (1UL << 4) + +static const uint32_t stm32_dma_stream_flag_shift[8] = { + 0UL, 6UL, 16UL, 22UL, 0UL, 6UL, 16UL, 22UL, +}; + +static inline bool stm32_dmav1_is_valid_desc(gpdma_stream_cfg_t const * const desc, + stm32_gpdma_desc_t const ** const ctrl) +{ + if (unlikely((desc == NULL) || (ctrl == NULL))) { + return false; + } + *ctrl = stm32_gpdma_get_desc(desc->controller); + if (unlikely(*ctrl == NULL)) { + return false; + } + if (unlikely(desc->channel >= (*ctrl)->num_chan)) { + return false; + } + return true; +} + +static inline uint32_t stm32_dmav1_get_stream_raw_flags(uint8_t stream, uint32_t isr) +{ + uint32_t const shift = stm32_dma_stream_flag_shift[stream]; + return (isr >> shift) & 0x1FUL; +} + +static inline uint32_t stm32_dmav1_get_stream_clr_flags(uint8_t stream) +{ + uint32_t const shift = stm32_dma_stream_flag_shift[stream]; + return (0x1FUL << shift); +} + +static inline uint32_t stm32_dmav1_get_beat_cfg(uint8_t beat) +{ + uint32_t cfg = 0; + + switch (beat) { + case GPDMA_BEAT_LEN_BYTE: + cfg = 0; + break; + case GPDMA_BEAT_LEN_HALFWORD: + cfg = 1; + break; + case GPDMA_BEAT_LEN_WORD: + cfg = 2; + break; + default: + cfg = 0xFFFFFFFFUL; + break; + } + return cfg; +} + +static inline uint32_t stm32_dmav1_get_beat_size(uint8_t beat) +{ + uint32_t sz = 0; + + switch (beat) { + case GPDMA_BEAT_LEN_BYTE: + sz = 1UL; + break; + case GPDMA_BEAT_LEN_HALFWORD: + sz = 2UL; + break; + case GPDMA_BEAT_LEN_WORD: + sz = 4UL; + break; + default: + sz = 0UL; + break; + } + return sz; +} + +static inline void stm32_dmav1_clear_stream_flags(stm32_gpdma_desc_t const * const ctrl, + uint8_t stream) +{ + uint32_t const clr = stm32_dmav1_get_stream_clr_flags(stream); + + if (stream < 4U) { + iowrite32(ctrl->base_addr + STM32_DMA_LIFCR_REG, clr); + } else { + iowrite32(ctrl->base_addr + STM32_DMA_HIFCR_REG, clr); + } +} + + +/** + * @brief probe given GPDMA controller identifier + */ +kstatus_t stm32_dmav1_probe(uint8_t controller) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * const ctrl_desc = stm32_gpdma_get_desc(controller); + uint8_t stream; + + if (unlikely(ctrl_desc == NULL)) { + goto end; + } + status = rcc_enable(ctrl_desc->bus_id, ctrl_desc->clk_msk, RCC_NOFLAG); + if (unlikely(status != K_STATUS_OKAY)) { + goto end; + } + + if (unlikely(gpdma_map(controller) != K_STATUS_OKAY)) { + status = K_ERROR_INVPARAM; + goto end; + } + + /* Clear all pending stream flags for a clean initial state. */ + iowrite32(ctrl_desc->base_addr + STM32_DMA_LIFCR_REG, 0x0F7D0F7DUL); + iowrite32(ctrl_desc->base_addr + STM32_DMA_HIFCR_REG, 0x0F7D0F7DUL); + + for (stream = 0; stream < ctrl_desc->num_chan; ++stream) { + iowrite32(ctrl_desc->base_addr + STM32_DMA_SxCR(stream), 0); + iowrite32(ctrl_desc->base_addr + STM32_DMA_SxNDTR(stream), 0); + iowrite32(ctrl_desc->base_addr + STM32_DMA_SxPAR(stream), 0); + iowrite32(ctrl_desc->base_addr + STM32_DMA_SxM0AR(stream), 0); + iowrite32(ctrl_desc->base_addr + STM32_DMA_SxM1AR(stream), 0); + iowrite32(ctrl_desc->base_addr + STM32_DMA_SxFCR(stream), 0); + } + + gpdma_unmap(); + status = K_STATUS_OKAY; +end: + return status; +} + +/** + * @brief clear given channel status flags + */ +kstatus_t stm32_dmav1_channel_clear_status(gpdma_stream_cfg_t const*const desc) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + + if (unlikely(!stm32_dmav1_is_valid_desc(desc, &ctrl))) { + goto end; + } + if (unlikely(gpdma_map(desc->controller) != K_STATUS_OKAY)) { + goto end; + } + + stm32_dmav1_clear_stream_flags(ctrl, desc->channel); + gpdma_unmap(); + status = K_STATUS_OKAY; +end: + return status; +} + +/** + * @brief get back current status of given DMA descriptor's stream + */ +kstatus_t stm32_dmav1_channel_get_status(gpdma_stream_cfg_t const*const desc, gpdma_chan_status_t * status) +{ + kstatus_t kret = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + uint32_t isr; + uint32_t flags; + uint32_t cr; + + if (unlikely((desc == NULL) || (status == NULL))) { + goto end; + } + if (unlikely(!stm32_dmav1_is_valid_desc(desc, &ctrl))) { + goto end; + } + if (unlikely(gpdma_map(desc->controller) != K_STATUS_OKAY)) { + goto end; + } + + memset(status, 0, sizeof(*status)); + if (desc->channel < 4U) { + isr = ioread32(ctrl->base_addr + STM32_DMA_LISR_REG); + } else { + isr = ioread32(ctrl->base_addr + STM32_DMA_HISR_REG); + } + flags = stm32_dmav1_get_stream_raw_flags(desc->channel, isr); + cr = ioread32(ctrl->base_addr + STM32_DMA_SxCR(desc->channel)); + + status->half_reached = !!(flags & STM32_DMA_STATUS_FLAG_HTIF); + status->completed = !!(flags & STM32_DMA_STATUS_FLAG_TCIF); + + if (flags & (STM32_DMA_STATUS_FLAG_TEIF | STM32_DMA_STATUS_FLAG_DMEIF | STM32_DMA_STATUS_FLAG_FEIF)) { + status->state = GPDMA_STATE_TRANSMISSION_FAILURE; + } else if (flags & STM32_DMA_STATUS_FLAG_TCIF) { + status->state = GPDMA_STATE_TRANSFER_COMPLETE; + } else if (flags & STM32_DMA_STATUS_FLAG_HTIF) { + status->state = GPDMA_STATE_HALF_TRANSFER; + } else if (cr & STM32_DMA_SxCR_EN) { + status->state = GPDMA_STATE_RUNNING; + } else { + status->state = GPDMA_STATE_IDLE; + } + + gpdma_unmap(); + kret = K_STATUS_OKAY; +end: + return kret; +} + +/** + * @brief configure a DMA channel with given DMA descriptor + * + * @note this function do not enable the DMA channel, but only configure it + */ +kstatus_t stm32_dmav1_channel_configure(gpdma_stream_cfg_t const*const desc) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + uint32_t cr = 0; + uint32_t fcr = 0; + uint32_t dir = 0; + uint32_t psize; + uint32_t msize; + uint32_t src_size; + uint32_t dst_size; + size_t periph_addr; + size_t mem_addr; + uint32_t ndtr; + + if (unlikely(!stm32_dmav1_is_valid_desc(desc, &ctrl))) { + goto end; + } + if (unlikely(desc->stream >= 8U)) { + goto end; + } + if (unlikely(desc->is_triggered)) { + goto end; + } + + psize = stm32_dmav1_get_beat_cfg(desc->src_beat_len); + msize = stm32_dmav1_get_beat_cfg(desc->dest_beat_len); + src_size = stm32_dmav1_get_beat_size(desc->src_beat_len); + dst_size = stm32_dmav1_get_beat_size(desc->dest_beat_len); + + if (unlikely((psize == 0xFFFFFFFFUL) || (msize == 0xFFFFFFFFUL) || (src_size == 0UL) || (dst_size == 0UL))) { + goto end; + } + if (unlikely(src_size != dst_size)) { + goto end; + } + if (unlikely((desc->transfer_len == 0UL) || ((desc->transfer_len % src_size) != 0UL))) { + goto end; + } + ndtr = (uint32_t)(desc->transfer_len / src_size); + if (unlikely((ndtr == 0UL) || (ndtr > 0xFFFFUL))) { + goto end; + } + + switch (desc->transfer_type) { + case GPDMA_TRANSFER_MEMORY_TO_DEVICE: + dir = STM32_DMA_DIR_MEM_TO_PERIPH; + psize = stm32_dmav1_get_beat_cfg(desc->dest_beat_len); + msize = stm32_dmav1_get_beat_cfg(desc->src_beat_len); + periph_addr = desc->dest; + mem_addr = desc->source; + break; + case GPDMA_TRANSFER_DEVICE_TO_MEMORY: + dir = STM32_DMA_DIR_PERIPH_TO_MEM; + psize = stm32_dmav1_get_beat_cfg(desc->src_beat_len); + msize = stm32_dmav1_get_beat_cfg(desc->dest_beat_len); + periph_addr = desc->source; + mem_addr = desc->dest; + break; + case GPDMA_TRANSFER_MEMORY_TO_MEMORY: + dir = STM32_DMA_DIR_MEM_TO_MEM; + psize = stm32_dmav1_get_beat_cfg(desc->src_beat_len); + msize = stm32_dmav1_get_beat_cfg(desc->dest_beat_len); + periph_addr = desc->source; + mem_addr = desc->dest; + break; + default: + goto end; + } + + if (unlikely((psize == 0xFFFFFFFFUL) || (msize == 0xFFFFFFFFUL))) { + goto end; + } + + if (unlikely(gpdma_map(desc->controller) != K_STATUS_OKAY)) { + goto end; + } + + cr = ioread32(ctrl->base_addr + STM32_DMA_SxCR(desc->channel)); + if (unlikely((cr & STM32_DMA_SxCR_EN) != 0UL)) { + status = K_ERROR_BADSTATE; + goto unmap_end; + } + + stm32_dmav1_clear_stream_flags(ctrl, desc->channel); + + cr = 0; + cr |= ((desc->stream & 0x7UL) << STM32_DMA_SxCR_CHSEL_SHIFT); + cr |= ((dir & 0x3UL) << STM32_DMA_SxCR_DIR_SHIFT); + cr |= ((psize & 0x3UL) << STM32_DMA_SxCR_PSIZE_SHIFT); + cr |= ((msize & 0x3UL) << STM32_DMA_SxCR_MSIZE_SHIFT); + cr |= ((desc->priority & 0x3UL) << STM32_DMA_SxCR_PL_SHIFT); + + if (desc->circular_source || desc->circular_dest) { + cr |= STM32_DMA_SxCR_CIRC; + } + + if (desc->transfer_type == GPDMA_TRANSFER_MEMORY_TO_DEVICE) { + if ((desc->transfer_mode & GPDMA_TRANSFER_MODE_INCREMENT_DEST) != 0U) { + cr |= STM32_DMA_SxCR_PINC; + } + if ((desc->transfer_mode & GPDMA_TRANSFER_MODE_INCREMENT_SRC) != 0U) { + cr |= STM32_DMA_SxCR_MINC; + } + } else { + if ((desc->transfer_mode & GPDMA_TRANSFER_MODE_INCREMENT_SRC) != 0U) { + cr |= STM32_DMA_SxCR_PINC; + } + if ((desc->transfer_mode & GPDMA_TRANSFER_MODE_INCREMENT_DEST) != 0U) { + cr |= STM32_DMA_SxCR_MINC; + } + } + + if ((desc->interrupts & GPDMA_INT_TC) != 0U) { + cr |= STM32_DMA_SxCR_TCIE; + } + if ((desc->interrupts & GPDMA_INT_HT) != 0U) { + cr |= STM32_DMA_SxCR_HTIE; + } + if ((desc->interrupts & GPDMA_INT_ERROR) != 0U) { + cr |= STM32_DMA_SxCR_TEIE | STM32_DMA_SxCR_DMEIE; + fcr |= STM32_DMA_SxFCR_FEIE; + } + + iowrite32(ctrl->base_addr + STM32_DMA_SxCR(desc->channel), cr); + iowrite32(ctrl->base_addr + STM32_DMA_SxNDTR(desc->channel), ndtr); + iowrite32(ctrl->base_addr + STM32_DMA_SxPAR(desc->channel), (uint32_t)periph_addr); + iowrite32(ctrl->base_addr + STM32_DMA_SxM0AR(desc->channel), (uint32_t)mem_addr); + iowrite32(ctrl->base_addr + STM32_DMA_SxM1AR(desc->channel), 0); + iowrite32(ctrl->base_addr + STM32_DMA_SxFCR(desc->channel), fcr); + + status = K_STATUS_OKAY; +unmap_end: + gpdma_unmap(); +end: + return status; +} + +/** + * @brief enable a previously configured DMA channel + */ +kstatus_t stm32_dmav1_channel_enable(gpdma_stream_cfg_t const*const desc) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + uint32_t cr; + + if (unlikely(!stm32_dmav1_is_valid_desc(desc, &ctrl))) { + goto end; + } + if (unlikely(gpdma_map(desc->controller) != K_STATUS_OKAY)) { + goto end; + } + + cr = ioread32(ctrl->base_addr + STM32_DMA_SxCR(desc->channel)); + cr |= STM32_DMA_SxCR_EN; + iowrite32(ctrl->base_addr + STM32_DMA_SxCR(desc->channel), cr); + + gpdma_unmap(); + status = K_STATUS_OKAY; +end: + return status; +} + +/** + * @brief given a stream, get back the associated IRQn + */ +kstatus_t stm32_dmav1_get_interrupt(gpdma_stream_cfg_t const * const desc, uint16_t * const IRQn) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + + if (unlikely((desc == NULL) || (IRQn == NULL))) { + goto end; + } + if (unlikely(!stm32_dmav1_is_valid_desc(desc, &ctrl))) { + goto end; + } + *IRQn = ctrl->interrupts[desc->channel]; + status = K_STATUS_OKAY; +end: + return status; +} + +/** + * @brief clear interrupt of corresponding stream at GPDMA level +*/ +kstatus_t stm32_dmav1_interrupt_clear(gpdma_stream_cfg_t const * const desc) +{ + return stm32_dmav1_channel_clear_status(desc); +} + +/** + * @brief suspend currently started stream + */ +kstatus_t stm32_dmav1_channel_suspend(gpdma_stream_cfg_t const*const desc) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + uint32_t cr; + uint32_t retry = 100000UL; + + if (unlikely(!stm32_dmav1_is_valid_desc(desc, &ctrl))) { + goto end; + } + if (unlikely(gpdma_map(desc->controller) != K_STATUS_OKAY)) { + goto end; + } + + cr = ioread32(ctrl->base_addr + STM32_DMA_SxCR(desc->channel)); + if ((cr & STM32_DMA_SxCR_EN) == 0UL) { + status = K_ERROR_BADSTATE; + goto unmap_end; + } + + cr &= ~STM32_DMA_SxCR_EN; + iowrite32(ctrl->base_addr + STM32_DMA_SxCR(desc->channel), cr); + + do { + cr = ioread32(ctrl->base_addr + STM32_DMA_SxCR(desc->channel)); + if ((cr & STM32_DMA_SxCR_EN) == 0UL) { + status = K_STATUS_OKAY; + goto unmap_end; + } + --retry; + } while (retry > 0UL); + + status = K_ERROR_BADSTATE; +unmap_end: + gpdma_unmap(); +end: + return status; +} + +/** + * @brief resume previously suspended stream + */ +kstatus_t stm32_dmav1_channel_resume(gpdma_stream_cfg_t const*const desc) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + uint32_t cr; + + if (unlikely(!stm32_dmav1_is_valid_desc(desc, &ctrl))) { + goto end; + } + if (unlikely(gpdma_map(desc->controller) != K_STATUS_OKAY)) { + goto end; + } + + cr = ioread32(ctrl->base_addr + STM32_DMA_SxCR(desc->channel)); + if ((cr & STM32_DMA_SxCR_EN) != 0UL) { + status = K_ERROR_BADSTATE; + goto unmap_end; + } + + cr |= STM32_DMA_SxCR_EN; + iowrite32(ctrl->base_addr + STM32_DMA_SxCR(desc->channel), cr); + status = K_STATUS_OKAY; + +unmap_end: + gpdma_unmap(); +end: + return status; +} + +/** + * @brief reset currently suspended stream + */ +kstatus_t stm32_dmav1_channel_reset(gpdma_stream_cfg_t const*const desc) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + uint32_t cr; + + if (unlikely(!stm32_dmav1_is_valid_desc(desc, &ctrl))) { + goto end; + } + if (unlikely(gpdma_map(desc->controller) != K_STATUS_OKAY)) { + goto end; + } + + cr = ioread32(ctrl->base_addr + STM32_DMA_SxCR(desc->channel)); + if ((cr & STM32_DMA_SxCR_EN) != 0UL) { + status = K_ERROR_BADSTATE; + goto unmap_end; + } + + stm32_dmav1_clear_stream_flags(ctrl, desc->channel); + iowrite32(ctrl->base_addr + STM32_DMA_SxCR(desc->channel), 0); + iowrite32(ctrl->base_addr + STM32_DMA_SxNDTR(desc->channel), 0); + iowrite32(ctrl->base_addr + STM32_DMA_SxPAR(desc->channel), 0); + iowrite32(ctrl->base_addr + STM32_DMA_SxM0AR(desc->channel), 0); + iowrite32(ctrl->base_addr + STM32_DMA_SxM1AR(desc->channel), 0); + iowrite32(ctrl->base_addr + STM32_DMA_SxFCR(desc->channel), 0); + + status = K_STATUS_OKAY; +unmap_end: + gpdma_unmap(); +end: + return status; +} + +/* aliasing functions to generic API */ +kstatus_t gpdma_probe(uint8_t controller) __attribute__((alias("stm32_dmav1_probe"))); +kstatus_t gpdma_channel_clear_status(gpdma_stream_cfg_t const*const desc) __attribute__((alias("stm32_dmav1_channel_clear_status"))); +kstatus_t gpdma_channel_get_status(gpdma_stream_cfg_t const*const desc, gpdma_chan_status_t * status) __attribute__((alias("stm32_dmav1_channel_get_status"))); +kstatus_t gpdma_channel_configure(gpdma_stream_cfg_t const*const desc) __attribute__((alias("stm32_dmav1_channel_configure"))); +kstatus_t gpdma_channel_enable(gpdma_stream_cfg_t const*const desc) __attribute__((alias("stm32_dmav1_channel_enable"))); +kstatus_t gpdma_get_interrupt(gpdma_stream_cfg_t const * const desc, uint16_t * const IRQn) __attribute__((alias("stm32_dmav1_get_interrupt"))); +kstatus_t gpdma_interrupt_clear(gpdma_stream_cfg_t const * const desc) __attribute__((alias("stm32_dmav1_interrupt_clear"))); +kstatus_t gpdma_channel_suspend(gpdma_stream_cfg_t const*const desc) __attribute__((alias("stm32_dmav1_channel_suspend"))); +kstatus_t gpdma_channel_resume(gpdma_stream_cfg_t const*const desc) __attribute__((alias("stm32_dmav1_channel_resume"))); +kstatus_t gpdma_channel_reset(gpdma_stream_cfg_t const*const desc) __attribute__((alias("stm32_dmav1_channel_reset"))); diff --git a/kernel/src/drivers/dma/stm32f4-dma.c b/kernel/src/drivers/dma/stm32f4-dma.c deleted file mode 100644 index 82925dea..00000000 --- a/kernel/src/drivers/dma/stm32f4-dma.c +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Ledger SAS -// SPDX-License-Identifier: Apache-2.0 - -/** - * \file General purpose DMA driver for STM32F4xx (stm32-dma-v1 compatible) SoCs - */ - -#include -#include -#include -#include -#include -#include -#include "stm32-dma-dt.h" - - -/** - * @brief probe given GPDMA controller identifier - */ -kstatus_t gpdma_probe(uint8_t controller) -{ - return K_STATUS_OKAY; -} - -/** - * @brief clear given channel status flags - */ -kstatus_t gpdma_channel_clear_status(gpdma_stream_cfg_t const*const desc) -{ - return K_STATUS_OKAY; -} - -/** - * @brief get back current status of given DMA descriptor's stream - */ -kstatus_t gpdma_channel_get_status(gpdma_stream_cfg_t const*const desc, gpdma_chan_status_t * status) -{ - return K_STATUS_OKAY; -} - -/** - * @brief configure a DMA channel with given DMA descriptor - * - * @note this function do not enable the DMA channel, but only configure it - */ -kstatus_t gpdma_channel_configure(gpdma_stream_cfg_t const*const desc) -{ - return K_STATUS_OKAY; -} - -/** - * @brief enable a previously configured DMA channel - */ -kstatus_t gpdma_channel_enable(gpdma_stream_cfg_t const*const desc) -{ - return K_STATUS_OKAY; -} - -/** - * @brief given a stream, get back the associated IRQn - */ -kstatus_t gpdma_get_interrupt(gpdma_stream_cfg_t const * const desc, uint16_t * const IRQn) -{ - return K_STATUS_OKAY; -} - -/** - * @brief clear interrupt of corresponding stream at GPDMA level -*/ -kstatus_t gpdma_interrupt_clear(gpdma_stream_cfg_t const * const desc) -{ - return K_STATUS_OKAY; -} - -/** - * @brief suspend currently started stream - */ -kstatus_t gpdma_channel_suspend(gpdma_stream_cfg_t const*const desc) -{ - return K_STATUS_OKAY; -} - -/** - * @brief resume previously suspended stream - */ -kstatus_t gpdma_channel_resume(gpdma_stream_cfg_t const*const desc) -{ - return K_STATUS_OKAY; -} - -/** - * @brief reset currently suspended stream - */ -kstatus_t gpdma_channel_reset(gpdma_stream_cfg_t const*const desc) -{ - return K_STATUS_OKAY; -} From 0ce579f3010d120b370899c3790bcf3556519add Mon Sep 17 00:00:00 2001 From: Philippe Thierry Date: Fri, 22 May 2026 11:56:54 +0200 Subject: [PATCH 04/11] sentry,dma: using proper naming for st-dma-v1 controller referense --- kernel/src/arch/asm-cortex-m/Kconfig | 1 + kernel/src/arch/asm-generic/Kconfig | 11 ++++++++++- kernel/src/drivers/dma/meson.build | 10 +++++++--- kernel/src/managers/dma/dma-dt.c.in | 2 +- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/kernel/src/arch/asm-cortex-m/Kconfig b/kernel/src/arch/asm-cortex-m/Kconfig index dcf917c3..bfbaa14a 100644 --- a/kernel/src/arch/asm-cortex-m/Kconfig +++ b/kernel/src/arch/asm-cortex-m/Kconfig @@ -98,6 +98,7 @@ config SOC_SUBFAMILY_STM32F4 select HAS_ICACHE select HAS_MPU_PMSA_V7 select HAS_GPDMA + select HAS_GPDMA_ST_DMAV1 help STM32F4 family is based on Cortex-M4 with FPU and PMSAv7 MPU. diff --git a/kernel/src/arch/asm-generic/Kconfig b/kernel/src/arch/asm-generic/Kconfig index 6246b6e6..4eff4d91 100644 --- a/kernel/src/arch/asm-generic/Kconfig +++ b/kernel/src/arch/asm-generic/Kconfig @@ -42,9 +42,18 @@ config HAS_RNG config HAS_GPDMA bool +config HAS_GPDMA_ST_DMAV1 + bool + depends on HAS_GPDMA + +config HAS_GPDMA_ST_DMAV2 + bool + depends on HAS_GPDMA + + config SYSTICK_HZ int "systick initial period configuration" range 0 10000 default 1000 -endmenu \ No newline at end of file +endmenu diff --git a/kernel/src/drivers/dma/meson.build b/kernel/src/drivers/dma/meson.build index 7ce6a03a..216b1867 100644 --- a/kernel/src/drivers/dma/meson.build +++ b/kernel/src/drivers/dma/meson.build @@ -33,12 +33,16 @@ stm32_dma_dts_template_h = files(['stm32-dma-dt.h.in']) stm32_dma_dtsgen_c = dtsgen.process(stm32_dma_dts_template_c) stm32_dma_dtsgen_h = dtsgen.process(stm32_dma_dts_template_h) - +# it seems that only the STM32u5 has the enhanced GPDMA bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32U5', if_true: [ stm32_gpdma_dtsgen_c, stm32_gpdma_dtsgen_h ]) bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32U5', if_true: files('stm32u5-gpdma.c')) -bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32F4', if_true: [ stm32_dma_dtsgen_c, stm32_dma_dtsgen_h ]) -bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32F4', if_true: files('stm32-dmav1.c')) +# Other STM32 families have older GPDMA implementations, being st-dma-v1 or st-dma-v2 depending on the family +# the selection is made at soc subfamily level but the config name is unified for a more generic inclusion here +bsp_private_gen_source_set.add(when: 'CONFIG_HAS_GPDMA_ST_DMAV1', if_true: [ stm32_dma_dtsgen_c, stm32_dma_dtsgen_h ]) +bsp_private_gen_source_set.add(when: 'CONFIG_HAS_GPDMA_ST_DMAV1', if_true: files('stm32-dmav1.c')) +#bsp_private_gen_source_set.add(when: 'CONFIG_HAS_GPDMA_ST_DMAV2', if_true: [ stm32_dma_dtsgen_c, stm32_dma_dtsgen_h ]) +#bsp_private_gen_source_set.add(when: 'CONFIG_HAS_GPDMA_ST_DMAV2', if_true: files('stm32-dmav2.c')) # driver source selection diff --git a/kernel/src/managers/dma/dma-dt.c.in b/kernel/src/managers/dma/dma-dt.c.in index 8ee85853..b56ddc71 100644 --- a/kernel/src/managers/dma/dma-dt.c.in +++ b/kernel/src/managers/dma/dma-dt.c.in @@ -11,7 +11,7 @@ #include #include "dma-dt.h" -{% set gpdma_ports = dts.get_compatible("stm32u5-dma") or dts.get_compatible("stm32-dma-v1") -%} +{% set gpdma_ports = dts.get_compatible("stm32u5-dma") or dts.get_compatible("stm32-dma-v1") or dts.get_compatible("stm32-dma-v2") -%} {%- macro stream_get_transfer_type(node) -%} {% set source = node["source"] -%} From a066d5927797ce138b41d953f6c6fe591e95d87f Mon Sep 17 00:00:00 2001 From: Philippe Thierry Date: Fri, 22 May 2026 12:00:07 +0200 Subject: [PATCH 05/11] sentry,dma: mkproper for generic st-dmav1 inclusion --- kernel/src/drivers/dma/meson.build | 10 +++++----- kernel/src/drivers/dma/stm32-dmav1.c | 2 +- .../dma/{stm32-dma-dt.c.in => stm32-st-dmav1-dt.c.in} | 2 +- .../dma/{stm32-dma-dt.h.in => stm32-st-dmav1-dt.h.in} | 0 4 files changed, 7 insertions(+), 7 deletions(-) rename kernel/src/drivers/dma/{stm32-dma-dt.c.in => stm32-st-dmav1-dt.c.in} (98%) rename kernel/src/drivers/dma/{stm32-dma-dt.h.in => stm32-st-dmav1-dt.h.in} (100%) diff --git a/kernel/src/drivers/dma/meson.build b/kernel/src/drivers/dma/meson.build index 216b1867..aee09e27 100644 --- a/kernel/src/drivers/dma/meson.build +++ b/kernel/src/drivers/dma/meson.build @@ -27,11 +27,11 @@ stm32_gpdma_dts_template_h = files(['stm32-gpdma-dt.h.in']) stm32_gpdma_dtsgen_c = dtsgen.process(stm32_gpdma_dts_template_c) stm32_gpdma_dtsgen_h = dtsgen.process(stm32_gpdma_dts_template_h) -stm32_dma_dts_template_c = files(['stm32-dma-dt.c.in']) -stm32_dma_dts_template_h = files(['stm32-dma-dt.h.in']) +stm32_dma_v1_dts_template_c = files(['stm32-st-dmav1-dt.c.in']) +stm32_dma_v1_dts_template_h = files(['stm32-st-dmav1-dt.h.in']) -stm32_dma_dtsgen_c = dtsgen.process(stm32_dma_dts_template_c) -stm32_dma_dtsgen_h = dtsgen.process(stm32_dma_dts_template_h) +stm32_dma_v1_dtsgen_c = dtsgen.process(stm32_dma_v1_dts_template_c) +stm32_dma_v1_dtsgen_h = dtsgen.process(stm32_dma_v1_dts_template_h) # it seems that only the STM32u5 has the enhanced GPDMA bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32U5', if_true: [ stm32_gpdma_dtsgen_c, stm32_gpdma_dtsgen_h ]) @@ -39,7 +39,7 @@ bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32U5', if_true: fi # Other STM32 families have older GPDMA implementations, being st-dma-v1 or st-dma-v2 depending on the family # the selection is made at soc subfamily level but the config name is unified for a more generic inclusion here -bsp_private_gen_source_set.add(when: 'CONFIG_HAS_GPDMA_ST_DMAV1', if_true: [ stm32_dma_dtsgen_c, stm32_dma_dtsgen_h ]) +bsp_private_gen_source_set.add(when: 'CONFIG_HAS_GPDMA_ST_DMAV1', if_true: [ stm32_dma_v1_dtsgen_c, stm32_dma_v1_dtsgen_h ]) bsp_private_gen_source_set.add(when: 'CONFIG_HAS_GPDMA_ST_DMAV1', if_true: files('stm32-dmav1.c')) #bsp_private_gen_source_set.add(when: 'CONFIG_HAS_GPDMA_ST_DMAV2', if_true: [ stm32_dma_dtsgen_c, stm32_dma_dtsgen_h ]) diff --git a/kernel/src/drivers/dma/stm32-dmav1.c b/kernel/src/drivers/dma/stm32-dmav1.c index cc96638f..45622881 100644 --- a/kernel/src/drivers/dma/stm32-dmav1.c +++ b/kernel/src/drivers/dma/stm32-dmav1.c @@ -11,7 +11,7 @@ #include #include #include -#include "stm32-dma-dt.h" +#include "stm32-st-dmav1-dt.h" #define STM32_DMA_LISR_REG 0x00UL #define STM32_DMA_HISR_REG 0x04UL diff --git a/kernel/src/drivers/dma/stm32-dma-dt.c.in b/kernel/src/drivers/dma/stm32-st-dmav1-dt.c.in similarity index 98% rename from kernel/src/drivers/dma/stm32-dma-dt.c.in rename to kernel/src/drivers/dma/stm32-st-dmav1-dt.c.in index e5c06999..832a49e3 100644 --- a/kernel/src/drivers/dma/stm32-dma-dt.c.in +++ b/kernel/src/drivers/dma/stm32-st-dmav1-dt.c.in @@ -4,7 +4,7 @@ #include #include -#include "stm32-dma-dt.h" +#include "stm32-st-dmav1-dt.h" {% set gpdma_ports = dts.get_compatible("stm32-dma-v1") -%} diff --git a/kernel/src/drivers/dma/stm32-dma-dt.h.in b/kernel/src/drivers/dma/stm32-st-dmav1-dt.h.in similarity index 100% rename from kernel/src/drivers/dma/stm32-dma-dt.h.in rename to kernel/src/drivers/dma/stm32-st-dmav1-dt.h.in From c9abd80a856b18cc3b6a1a855c22427413e1afb2 Mon Sep 17 00:00:00 2001 From: Philippe Thierry Date: Fri, 22 May 2026 12:20:43 +0200 Subject: [PATCH 06/11] sentry,dma: adding proper doxygen description for dmav1 driver --- kernel/src/drivers/dma/stm32-dmav1.c | 146 +++++++++++++++++++++++++-- 1 file changed, 135 insertions(+), 11 deletions(-) diff --git a/kernel/src/drivers/dma/stm32-dmav1.c b/kernel/src/drivers/dma/stm32-dmav1.c index 45622881..71b17eec 100644 --- a/kernel/src/drivers/dma/stm32-dmav1.c +++ b/kernel/src/drivers/dma/stm32-dmav1.c @@ -13,18 +13,26 @@ #include #include "stm32-st-dmav1-dt.h" +/** @name STM32 DMA v1 global register offsets */ +/** @{ */ #define STM32_DMA_LISR_REG 0x00UL #define STM32_DMA_HISR_REG 0x04UL #define STM32_DMA_LIFCR_REG 0x08UL #define STM32_DMA_HIFCR_REG 0x0CUL +/** @} */ +/** @name STM32 DMA v1 stream register offsets */ +/** @{ */ #define STM32_DMA_SxCR(s) (0x10UL + (0x18UL * (s))) #define STM32_DMA_SxNDTR(s) (0x14UL + (0x18UL * (s))) #define STM32_DMA_SxPAR(s) (0x18UL + (0x18UL * (s))) #define STM32_DMA_SxM0AR(s) (0x1CUL + (0x18UL * (s))) #define STM32_DMA_SxM1AR(s) (0x20UL + (0x18UL * (s))) #define STM32_DMA_SxFCR(s) (0x24UL + (0x18UL * (s))) +/** @} */ +/** @name STM32 DMA v1 stream control bits */ +/** @{ */ #define STM32_DMA_SxCR_EN (1UL << 0) #define STM32_DMA_SxCR_DMEIE (1UL << 1) #define STM32_DMA_SxCR_TEIE (1UL << 2) @@ -43,23 +51,42 @@ #define STM32_DMA_SxCR_PL_MASK (0x3UL << STM32_DMA_SxCR_PL_SHIFT) #define STM32_DMA_SxCR_CHSEL_SHIFT 25UL #define STM32_DMA_SxCR_CHSEL_MASK (0x7UL << STM32_DMA_SxCR_CHSEL_SHIFT) +/** @} */ #define STM32_DMA_SxFCR_FEIE (1UL << 7) +/** @name DMA transfer direction encoding for SxCR.DIR */ +/** @{ */ #define STM32_DMA_DIR_PERIPH_TO_MEM 0UL #define STM32_DMA_DIR_MEM_TO_PERIPH 1UL #define STM32_DMA_DIR_MEM_TO_MEM 2UL +/** @} */ +/** @name Per-stream status bits extracted from LISR/HISR */ +/** @{ */ #define STM32_DMA_STATUS_FLAG_FEIF (1UL << 0) #define STM32_DMA_STATUS_FLAG_DMEIF (1UL << 1) #define STM32_DMA_STATUS_FLAG_TEIF (1UL << 2) #define STM32_DMA_STATUS_FLAG_HTIF (1UL << 3) #define STM32_DMA_STATUS_FLAG_TCIF (1UL << 4) +/** @} */ +/** + * @brief Mapping table from stream index to flag bit-shift in LISR/HISR. + */ static const uint32_t stm32_dma_stream_flag_shift[8] = { 0UL, 6UL, 16UL, 22UL, 0UL, 6UL, 16UL, 22UL, }; +/** + * @brief Validate a DMA descriptor and resolve its controller descriptor. + * + * @param[in] desc DMA stream configuration descriptor. + * @param[out] ctrl Resolved controller descriptor on success. + * + * @retval true Descriptor is valid and @p ctrl is initialized. + * @retval false Invalid argument, unknown controller, or channel out of range. + */ static inline bool stm32_dmav1_is_valid_desc(gpdma_stream_cfg_t const * const desc, stm32_gpdma_desc_t const ** const ctrl) { @@ -76,18 +103,40 @@ static inline bool stm32_dmav1_is_valid_desc(gpdma_stream_cfg_t const * const de return true; } +/** + * @brief Extract the 5 status bits associated with one stream from ISR. + * + * @param[in] stream Stream index (0..7). + * @param[in] isr Raw ISR register value (LISR or HISR). + * + * @return Right-aligned stream status flags. + */ static inline uint32_t stm32_dmav1_get_stream_raw_flags(uint8_t stream, uint32_t isr) { uint32_t const shift = stm32_dma_stream_flag_shift[stream]; return (isr >> shift) & 0x1FUL; } +/** + * @brief Build IFCR clear mask for one stream. + * + * @param[in] stream Stream index (0..7). + * + * @return Bitmask to write in LIFCR/HIFCR for this stream. + */ static inline uint32_t stm32_dmav1_get_stream_clr_flags(uint8_t stream) { uint32_t const shift = stm32_dma_stream_flag_shift[stream]; return (0x1FUL << shift); } +/** + * @brief Convert generic beat length to STM32 DMA v1 register encoding. + * + * @param[in] beat Generic beat length value. + * + * @return Encoded beat value (0..2), or 0xFFFFFFFFUL if unsupported. + */ static inline uint32_t stm32_dmav1_get_beat_cfg(uint8_t beat) { uint32_t cfg = 0; @@ -109,6 +158,13 @@ static inline uint32_t stm32_dmav1_get_beat_cfg(uint8_t beat) return cfg; } +/** + * @brief Convert generic beat length to byte size. + * + * @param[in] beat Generic beat length value. + * + * @return Beat size in bytes, or 0 if unsupported. + */ static inline uint32_t stm32_dmav1_get_beat_size(uint8_t beat) { uint32_t sz = 0; @@ -130,6 +186,12 @@ static inline uint32_t stm32_dmav1_get_beat_size(uint8_t beat) return sz; } +/** + * @brief Clear all status flags associated with one stream. + * + * @param[in] ctrl DMA controller descriptor. + * @param[in] stream Stream index. + */ static inline void stm32_dmav1_clear_stream_flags(stm32_gpdma_desc_t const * const ctrl, uint8_t stream) { @@ -144,7 +206,16 @@ static inline void stm32_dmav1_clear_stream_flags(stm32_gpdma_desc_t const * con /** - * @brief probe given GPDMA controller identifier + * @brief Probe and initialize one STM32 DMA v1 controller. + * + * Enables the RCC clock, maps the controller, clears pending flags, + * and resets all stream registers to a known state. + * + * @param[in] controller DMA controller identifier. + * + * @retval K_STATUS_OKAY Controller initialized. + * @retval K_ERROR_INVPARAM Invalid controller or mapping failure. + * @retval Any error returned by RCC API on clock enable failure. */ kstatus_t stm32_dmav1_probe(uint8_t controller) { @@ -185,7 +256,12 @@ kstatus_t stm32_dmav1_probe(uint8_t controller) } /** - * @brief clear given channel status flags + * @brief Clear status flags for one configured stream. + * + * @param[in] desc DMA stream descriptor. + * + * @retval K_STATUS_OKAY Flags cleared. + * @retval K_ERROR_INVPARAM Invalid descriptor or map failure. */ kstatus_t stm32_dmav1_channel_clear_status(gpdma_stream_cfg_t const*const desc) { @@ -207,7 +283,13 @@ kstatus_t stm32_dmav1_channel_clear_status(gpdma_stream_cfg_t const*const desc) } /** - * @brief get back current status of given DMA descriptor's stream + * @brief Read runtime status for one DMA stream. + * + * @param[in] desc DMA stream descriptor. + * @param[out] status Output status snapshot. + * + * @retval K_STATUS_OKAY Status returned in @p status. + * @retval K_ERROR_INVPARAM Invalid argument or map failure. */ kstatus_t stm32_dmav1_channel_get_status(gpdma_stream_cfg_t const*const desc, gpdma_chan_status_t * status) { @@ -258,9 +340,16 @@ kstatus_t stm32_dmav1_channel_get_status(gpdma_stream_cfg_t const*const desc, gp } /** - * @brief configure a DMA channel with given DMA descriptor + * @brief Configure one DMA stream from a generic descriptor. + * + * The stream remains disabled after this call. Use + * stm32_dmav1_channel_enable() to start the transfer. * - * @note this function do not enable the DMA channel, but only configure it + * @param[in] desc DMA stream descriptor. + * + * @retval K_STATUS_OKAY Stream configured. + * @retval K_ERROR_BADSTATE Stream currently enabled. + * @retval K_ERROR_INVPARAM Invalid parameters or unsupported mode. */ kstatus_t stm32_dmav1_channel_configure(gpdma_stream_cfg_t const*const desc) { @@ -401,7 +490,12 @@ kstatus_t stm32_dmav1_channel_configure(gpdma_stream_cfg_t const*const desc) } /** - * @brief enable a previously configured DMA channel + * @brief Enable a previously configured DMA stream. + * + * @param[in] desc DMA stream descriptor. + * + * @retval K_STATUS_OKAY Stream enabled. + * @retval K_ERROR_INVPARAM Invalid descriptor or map failure. */ kstatus_t stm32_dmav1_channel_enable(gpdma_stream_cfg_t const*const desc) { @@ -427,7 +521,13 @@ kstatus_t stm32_dmav1_channel_enable(gpdma_stream_cfg_t const*const desc) } /** - * @brief given a stream, get back the associated IRQn + * @brief Get IRQ number associated with a DMA stream. + * + * @param[in] desc DMA stream descriptor. + * @param[out] IRQn Interrupt number associated with this stream. + * + * @retval K_STATUS_OKAY IRQ number returned. + * @retval K_ERROR_INVPARAM Invalid argument. */ kstatus_t stm32_dmav1_get_interrupt(gpdma_stream_cfg_t const * const desc, uint16_t * const IRQn) { @@ -447,7 +547,11 @@ kstatus_t stm32_dmav1_get_interrupt(gpdma_stream_cfg_t const * const desc, uint1 } /** - * @brief clear interrupt of corresponding stream at GPDMA level + * @brief Clear DMA interrupt source for one stream. + * + * @param[in] desc DMA stream descriptor. + * + * @return Same status as stm32_dmav1_channel_clear_status(). */ kstatus_t stm32_dmav1_interrupt_clear(gpdma_stream_cfg_t const * const desc) { @@ -455,7 +559,13 @@ kstatus_t stm32_dmav1_interrupt_clear(gpdma_stream_cfg_t const * const desc) } /** - * @brief suspend currently started stream + * @brief Suspend a running stream by clearing its EN bit. + * + * @param[in] desc DMA stream descriptor. + * + * @retval K_STATUS_OKAY Stream suspended. + * @retval K_ERROR_BADSTATE Stream not running or hardware did not stop. + * @retval K_ERROR_INVPARAM Invalid descriptor or map failure. */ kstatus_t stm32_dmav1_channel_suspend(gpdma_stream_cfg_t const*const desc) { @@ -497,7 +607,13 @@ kstatus_t stm32_dmav1_channel_suspend(gpdma_stream_cfg_t const*const desc) } /** - * @brief resume previously suspended stream + * @brief Resume a previously suspended stream. + * + * @param[in] desc DMA stream descriptor. + * + * @retval K_STATUS_OKAY Stream resumed. + * @retval K_ERROR_BADSTATE Stream already enabled. + * @retval K_ERROR_INVPARAM Invalid descriptor or map failure. */ kstatus_t stm32_dmav1_channel_resume(gpdma_stream_cfg_t const*const desc) { @@ -529,7 +645,15 @@ kstatus_t stm32_dmav1_channel_resume(gpdma_stream_cfg_t const*const desc) } /** - * @brief reset currently suspended stream + * @brief Reset stream registers and clear its status flags. + * + * The stream must be disabled before reset. + * + * @param[in] desc DMA stream descriptor. + * + * @retval K_STATUS_OKAY Stream reset. + * @retval K_ERROR_BADSTATE Stream still enabled. + * @retval K_ERROR_INVPARAM Invalid descriptor or map failure. */ kstatus_t stm32_dmav1_channel_reset(gpdma_stream_cfg_t const*const desc) { From 37dedf01bb83bf037dbdb2e059004152f85615b1 Mon Sep 17 00:00:00 2001 From: Philippe Thierry Date: Fri, 22 May 2026 12:21:23 +0200 Subject: [PATCH 07/11] sentry,dma: adding st-dmav2 driver support for wb55 and l476 SoCs --- kernel/src/arch/asm-cortex-m/Kconfig | 4 + kernel/src/drivers/dma/meson.build | 13 +- kernel/src/drivers/dma/stm32-dmav2.c | 673 ++++++++++++++++++ kernel/src/drivers/dma/stm32-st-dmav2-dt.c.in | 108 +++ kernel/src/drivers/dma/stm32-st-dmav2-dt.h.in | 79 ++ 5 files changed, 875 insertions(+), 2 deletions(-) create mode 100644 kernel/src/drivers/dma/stm32-dmav2.c create mode 100644 kernel/src/drivers/dma/stm32-st-dmav2-dt.c.in create mode 100644 kernel/src/drivers/dma/stm32-st-dmav2-dt.h.in diff --git a/kernel/src/arch/asm-cortex-m/Kconfig b/kernel/src/arch/asm-cortex-m/Kconfig index bfbaa14a..8a193852 100644 --- a/kernel/src/arch/asm-cortex-m/Kconfig +++ b/kernel/src/arch/asm-cortex-m/Kconfig @@ -113,6 +113,8 @@ config SOC_SUBFAMILY_STM32L4 select HAS_DCACHE select HAS_ICACHE select HAS_RNG + select HAS_GPDMA + select HAS_GPDMA_ST_DMAV2 help STM32L4 family is based on Cortex-M4 with FPU and PMSAv7 MPU with low power features @@ -138,6 +140,8 @@ config SOC_SUBFAMILY_STM32WB select HAS_MPU select HAS_RNG select HAS_MPU_PMSA_V7 + select HAS_GPDMA + select HAS_GPDMA_ST_DMAV2 # Cache support ? help STM32WB family is based on Cortex-M4 (main core and a Cortex-M0+ for wireless core) diff --git a/kernel/src/drivers/dma/meson.build b/kernel/src/drivers/dma/meson.build index aee09e27..e1983b4b 100644 --- a/kernel/src/drivers/dma/meson.build +++ b/kernel/src/drivers/dma/meson.build @@ -27,12 +27,21 @@ stm32_gpdma_dts_template_h = files(['stm32-gpdma-dt.h.in']) stm32_gpdma_dtsgen_c = dtsgen.process(stm32_gpdma_dts_template_c) stm32_gpdma_dtsgen_h = dtsgen.process(stm32_gpdma_dts_template_h) +# st-dma-v1 DTS-based controller definitions generation stm32_dma_v1_dts_template_c = files(['stm32-st-dmav1-dt.c.in']) stm32_dma_v1_dts_template_h = files(['stm32-st-dmav1-dt.h.in']) stm32_dma_v1_dtsgen_c = dtsgen.process(stm32_dma_v1_dts_template_c) stm32_dma_v1_dtsgen_h = dtsgen.process(stm32_dma_v1_dts_template_h) +# st-dma-v2 DTS-based controller definitions generation +stm32_dma_v2_dts_template_c = files(['stm32-st-dmav2-dt.c.in']) +stm32_dma_v2_dts_template_h = files(['stm32-st-dmav2-dt.h.in']) + +stm32_dma_v2_dtsgen_c = dtsgen.process(stm32_dma_v2_dts_template_c) +stm32_dma_v2_dtsgen_h = dtsgen.process(stm32_dma_v2_dts_template_h) + + # it seems that only the STM32u5 has the enhanced GPDMA bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32U5', if_true: [ stm32_gpdma_dtsgen_c, stm32_gpdma_dtsgen_h ]) bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32U5', if_true: files('stm32u5-gpdma.c')) @@ -42,7 +51,7 @@ bsp_private_gen_source_set.add(when: 'CONFIG_SOC_SUBFAMILY_STM32U5', if_true: fi bsp_private_gen_source_set.add(when: 'CONFIG_HAS_GPDMA_ST_DMAV1', if_true: [ stm32_dma_v1_dtsgen_c, stm32_dma_v1_dtsgen_h ]) bsp_private_gen_source_set.add(when: 'CONFIG_HAS_GPDMA_ST_DMAV1', if_true: files('stm32-dmav1.c')) -#bsp_private_gen_source_set.add(when: 'CONFIG_HAS_GPDMA_ST_DMAV2', if_true: [ stm32_dma_dtsgen_c, stm32_dma_dtsgen_h ]) -#bsp_private_gen_source_set.add(when: 'CONFIG_HAS_GPDMA_ST_DMAV2', if_true: files('stm32-dmav2.c')) +bsp_private_gen_source_set.add(when: 'CONFIG_HAS_GPDMA_ST_DMAV2', if_true: [ stm32_dma_v2_dtsgen_c, stm32_dma_v2_dtsgen_h ]) +bsp_private_gen_source_set.add(when: 'CONFIG_HAS_GPDMA_ST_DMAV2', if_true: files('stm32-dmav2.c')) # driver source selection diff --git a/kernel/src/drivers/dma/stm32-dmav2.c b/kernel/src/drivers/dma/stm32-dmav2.c new file mode 100644 index 00000000..b65f7917 --- /dev/null +++ b/kernel/src/drivers/dma/stm32-dmav2.c @@ -0,0 +1,673 @@ +// SPDX-FileCopyrightText: 2026 Ledger SAS +// SPDX-License-Identifier: Apache-2.0 + +/** + * \file General purpose DMA driver for STM32 (stm32-dmav2 compatible) SoCs + */ + +#include +#include +#include +#include +#include +#include +#include "stm32-st-dmav2-dt.h" + +/** @name STM32 DMA v2 global register offsets */ +/** @{ */ +#define STM32_DMA_ISR_REG 0x00UL +#define STM32_DMA_IFCR_REG 0x04UL +/** @} */ + +/** @name STM32 DMA v2 channel register offsets */ +/** @{ */ +#define STM32_DMA_CCR(c) (0x08UL + (0x14UL * (c))) +#define STM32_DMA_CNDTR(c) (0x0CUL + (0x14UL * (c))) +#define STM32_DMA_CPAR(c) (0x10UL + (0x14UL * (c))) +#define STM32_DMA_CMAR(c) (0x14UL + (0x14UL * (c))) +#define STM32_DMA_CSELR_REG 0xA8UL +/** @} */ + +/** @name STM32 DMA v2 CCR bit definitions */ +/** @{ */ +#define STM32_DMA_CCR_EN (1UL << 0) +#define STM32_DMA_CCR_TCIE (1UL << 1) +#define STM32_DMA_CCR_HTIE (1UL << 2) +#define STM32_DMA_CCR_TEIE (1UL << 3) +#define STM32_DMA_CCR_DIR (1UL << 4) +#define STM32_DMA_CCR_CIRC (1UL << 5) +#define STM32_DMA_CCR_PINC (1UL << 6) +#define STM32_DMA_CCR_MINC (1UL << 7) +#define STM32_DMA_CCR_PSIZE_SHIFT 8UL +#define STM32_DMA_CCR_MSIZE_SHIFT 10UL +#define STM32_DMA_CCR_PL_SHIFT 12UL +#define STM32_DMA_CCR_MEM2MEM (1UL << 14) +/** @} */ + +/** @name Per-channel status bits (4-bit field in ISR/IFCR) */ +/** @{ */ +#define STM32_DMA_STATUS_FLAG_GIF (1UL << 0) +#define STM32_DMA_STATUS_FLAG_TCIF (1UL << 1) +#define STM32_DMA_STATUS_FLAG_HTIF (1UL << 2) +#define STM32_DMA_STATUS_FLAG_TEIF (1UL << 3) +/** @} */ + +/** @name STM32 DMA v2 request multiplexer helpers */ +/** @{ */ +#define STM32_DMA_CSELR_CxS_SHIFT(c) (4UL * (c)) +#define STM32_DMA_CSELR_CxS_MASK(c) (0xFUL << STM32_DMA_CSELR_CxS_SHIFT(c)) +/** @} */ + +/** + * @brief Validate a DMA descriptor and resolve its controller descriptor. + * + * @param[in] desc DMA stream configuration descriptor. + * @param[out] ctrl Resolved controller descriptor on success. + * + * @retval true Descriptor is valid and @p ctrl is initialized. + * @retval false Invalid argument, unknown controller, or channel out of range. + */ +static inline bool stm32_dmav2_is_valid_desc(gpdma_stream_cfg_t const * const desc, + stm32_gpdma_desc_t const ** const ctrl) +{ + if (unlikely((desc == NULL) || (ctrl == NULL))) { + return false; + } + *ctrl = stm32_gpdma_get_desc(desc->controller); + if (unlikely(*ctrl == NULL)) { + return false; + } + if (unlikely(desc->channel >= (*ctrl)->num_chan)) { + return false; + } + return true; +} + +/** + * @brief Extract one channel status nibble from ISR value. + * + * @param[in] channel DMA channel index. + * @param[in] isr Raw ISR value. + * + * @return Right-aligned 4-bit status flags for the channel. + */ +static inline uint32_t stm32_dmav2_get_chan_raw_flags(uint8_t channel, uint32_t isr) +{ + uint32_t const shift = 4UL * channel; + return (isr >> shift) & 0xFUL; +} + +/** + * @brief Build the IFCR clear mask for one channel. + * + * @param[in] channel DMA channel index. + * + * @return Channel clear mask positioned in the global IFCR register. + */ +static inline uint32_t stm32_dmav2_get_chan_clr_flags(uint8_t channel) +{ + uint32_t const shift = 4UL * channel; + return (0xFUL << shift); +} + +/** + * @brief Convert generic beat length to STM32 DMA v2 register encoding. + * + * @param[in] beat Generic beat length value. + * + * @return Encoded beat value (0..2), or 0xFFFFFFFFUL if unsupported. + */ +static inline uint32_t stm32_dmav2_get_beat_cfg(uint8_t beat) +{ + uint32_t cfg = 0; + + switch (beat) { + case GPDMA_BEAT_LEN_BYTE: + cfg = 0; + break; + case GPDMA_BEAT_LEN_HALFWORD: + cfg = 1; + break; + case GPDMA_BEAT_LEN_WORD: + cfg = 2; + break; + default: + cfg = 0xFFFFFFFFUL; + break; + } + return cfg; +} + +/** + * @brief Convert generic beat length to byte size. + * + * @param[in] beat Generic beat length value. + * + * @return Beat size in bytes, or 0 if unsupported. + */ +static inline uint32_t stm32_dmav2_get_beat_size(uint8_t beat) +{ + uint32_t sz = 0; + + switch (beat) { + case GPDMA_BEAT_LEN_BYTE: + sz = 1UL; + break; + case GPDMA_BEAT_LEN_HALFWORD: + sz = 2UL; + break; + case GPDMA_BEAT_LEN_WORD: + sz = 4UL; + break; + default: + sz = 0UL; + break; + } + return sz; +} + +/** + * @brief Clear all status flags for one channel. + * + * @param[in] ctrl DMA controller descriptor. + * @param[in] channel DMA channel index. + */ +static inline void stm32_dmav2_clear_channel_flags(stm32_gpdma_desc_t const * const ctrl, + uint8_t channel) +{ + iowrite32(ctrl->base_addr + STM32_DMA_IFCR_REG, + stm32_dmav2_get_chan_clr_flags(channel)); +} + +/** + * @brief Probe and initialize one STM32 DMA v2 controller. + * + * Enables the RCC clock, maps controller registers, clears pending flags, + * resets all channel registers, and resets request mux mapping. + * + * @param[in] controller DMA controller identifier. + * + * @retval K_STATUS_OKAY Controller initialized. + * @retval K_ERROR_INVPARAM Invalid controller or map failure. + * @retval Any error returned by RCC API on clock enable failure. + */ +kstatus_t stm32_dmav2_probe(uint8_t controller) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * const ctrl_desc = stm32_gpdma_get_desc(controller); + uint8_t channel; + + if (unlikely(ctrl_desc == NULL)) { + goto end; + } + status = rcc_enable(ctrl_desc->bus_id, ctrl_desc->clk_msk, RCC_NOFLAG); + if (unlikely(status != K_STATUS_OKAY)) { + goto end; + } + + if (unlikely(gpdma_map(controller) != K_STATUS_OKAY)) { + status = K_ERROR_INVPARAM; + goto end; + } + + /* Clear all pending flags then reset channels and request mappings. */ + for (channel = 0; channel < ctrl_desc->num_chan; ++channel) { + stm32_dmav2_clear_channel_flags(ctrl_desc, channel); + iowrite32(ctrl_desc->base_addr + STM32_DMA_CCR(channel), 0); + iowrite32(ctrl_desc->base_addr + STM32_DMA_CNDTR(channel), 0); + iowrite32(ctrl_desc->base_addr + STM32_DMA_CPAR(channel), 0); + iowrite32(ctrl_desc->base_addr + STM32_DMA_CMAR(channel), 0); + } + iowrite32(ctrl_desc->base_addr + STM32_DMA_CSELR_REG, 0); + + gpdma_unmap(); + status = K_STATUS_OKAY; +end: + return status; +} + +/** + * @brief Clear status flags for one configured channel. + * + * @param[in] desc DMA stream descriptor. + * + * @retval K_STATUS_OKAY Flags cleared. + * @retval K_ERROR_INVPARAM Invalid descriptor or map failure. + */ +kstatus_t stm32_dmav2_channel_clear_status(gpdma_stream_cfg_t const*const desc) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + + if (unlikely(!stm32_dmav2_is_valid_desc(desc, &ctrl))) { + goto end; + } + if (unlikely(gpdma_map(desc->controller) != K_STATUS_OKAY)) { + goto end; + } + + stm32_dmav2_clear_channel_flags(ctrl, desc->channel); + gpdma_unmap(); + status = K_STATUS_OKAY; +end: + return status; +} + +/** + * @brief Read runtime status for one DMA channel. + * + * @param[in] desc DMA stream descriptor. + * @param[out] status Output status snapshot. + * + * @retval K_STATUS_OKAY Status returned in @p status. + * @retval K_ERROR_INVPARAM Invalid argument or map failure. + */ +kstatus_t stm32_dmav2_channel_get_status(gpdma_stream_cfg_t const*const desc, gpdma_chan_status_t * status) +{ + kstatus_t kret = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + uint32_t isr; + uint32_t flags; + uint32_t ccr; + + if (unlikely((desc == NULL) || (status == NULL))) { + goto end; + } + if (unlikely(!stm32_dmav2_is_valid_desc(desc, &ctrl))) { + goto end; + } + if (unlikely(gpdma_map(desc->controller) != K_STATUS_OKAY)) { + goto end; + } + + memset(status, 0, sizeof(*status)); + isr = ioread32(ctrl->base_addr + STM32_DMA_ISR_REG); + flags = stm32_dmav2_get_chan_raw_flags(desc->channel, isr); + ccr = ioread32(ctrl->base_addr + STM32_DMA_CCR(desc->channel)); + + status->half_reached = !!(flags & STM32_DMA_STATUS_FLAG_HTIF); + status->completed = !!(flags & STM32_DMA_STATUS_FLAG_TCIF); + + if (flags & STM32_DMA_STATUS_FLAG_TEIF) { + status->state = GPDMA_STATE_TRANSMISSION_FAILURE; + } else if (flags & STM32_DMA_STATUS_FLAG_TCIF) { + status->state = GPDMA_STATE_TRANSFER_COMPLETE; + } else if (flags & STM32_DMA_STATUS_FLAG_HTIF) { + status->state = GPDMA_STATE_HALF_TRANSFER; + } else if (ccr & STM32_DMA_CCR_EN) { + status->state = GPDMA_STATE_RUNNING; + } else { + status->state = GPDMA_STATE_IDLE; + } + + gpdma_unmap(); + kret = K_STATUS_OKAY; +end: + return kret; +} + +/** + * @brief Configure one DMA channel from a generic descriptor. + * + * The channel remains disabled after this call. Use + * stm32_dmav2_channel_enable() to start the transfer. + * + * @param[in] desc DMA stream descriptor. + * + * @retval K_STATUS_OKAY Channel configured. + * @retval K_ERROR_BADSTATE Channel currently enabled. + * @retval K_ERROR_INVPARAM Invalid parameters or unsupported mode. + */ +kstatus_t stm32_dmav2_channel_configure(gpdma_stream_cfg_t const*const desc) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + uint32_t ccr = 0; + uint32_t cselr; + uint32_t psize; + uint32_t msize; + uint32_t src_size; + uint32_t dst_size; + size_t periph_addr; + size_t mem_addr; + uint32_t ndtr; + + if (unlikely(!stm32_dmav2_is_valid_desc(desc, &ctrl))) { + goto end; + } + if (unlikely(desc->is_triggered)) { + goto end; + } + if (unlikely(desc->transfer_type == GPDMA_TRANSFER_DEVICE_TO_DEVICE)) { + goto end; + } + + psize = stm32_dmav2_get_beat_cfg(desc->src_beat_len); + msize = stm32_dmav2_get_beat_cfg(desc->dest_beat_len); + src_size = stm32_dmav2_get_beat_size(desc->src_beat_len); + dst_size = stm32_dmav2_get_beat_size(desc->dest_beat_len); + + if (unlikely((psize == 0xFFFFFFFFUL) || (msize == 0xFFFFFFFFUL) || + (src_size == 0UL) || (dst_size == 0UL))) { + goto end; + } + if (unlikely(src_size != dst_size)) { + goto end; + } + if (unlikely((desc->transfer_len == 0UL) || ((desc->transfer_len % src_size) != 0UL))) { + goto end; + } + ndtr = (uint32_t)(desc->transfer_len / src_size); + if (unlikely((ndtr == 0UL) || (ndtr > 0xFFFFUL))) { + goto end; + } + + switch (desc->transfer_type) { + case GPDMA_TRANSFER_MEMORY_TO_DEVICE: + psize = stm32_dmav2_get_beat_cfg(desc->dest_beat_len); + msize = stm32_dmav2_get_beat_cfg(desc->src_beat_len); + periph_addr = desc->dest; + mem_addr = desc->source; + ccr |= STM32_DMA_CCR_DIR; + break; + case GPDMA_TRANSFER_DEVICE_TO_MEMORY: + psize = stm32_dmav2_get_beat_cfg(desc->src_beat_len); + msize = stm32_dmav2_get_beat_cfg(desc->dest_beat_len); + periph_addr = desc->source; + mem_addr = desc->dest; + break; + case GPDMA_TRANSFER_MEMORY_TO_MEMORY: + psize = stm32_dmav2_get_beat_cfg(desc->src_beat_len); + msize = stm32_dmav2_get_beat_cfg(desc->dest_beat_len); + periph_addr = desc->source; + mem_addr = desc->dest; + ccr |= STM32_DMA_CCR_MEM2MEM; + break; + default: + goto end; + } + + if (unlikely((psize == 0xFFFFFFFFUL) || (msize == 0xFFFFFFFFUL))) { + goto end; + } + if (unlikely(desc->stream >= ctrl->num_req)) { + goto end; + } + + if (unlikely(gpdma_map(desc->controller) != K_STATUS_OKAY)) { + goto end; + } + + if (unlikely((ioread32(ctrl->base_addr + STM32_DMA_CCR(desc->channel)) & STM32_DMA_CCR_EN) != 0UL)) { + status = K_ERROR_BADSTATE; + goto unmap_end; + } + + stm32_dmav2_clear_channel_flags(ctrl, desc->channel); + + ccr |= ((psize & 0x3UL) << STM32_DMA_CCR_PSIZE_SHIFT); + ccr |= ((msize & 0x3UL) << STM32_DMA_CCR_MSIZE_SHIFT); + ccr |= ((desc->priority & 0x3UL) << STM32_DMA_CCR_PL_SHIFT); + + if (desc->circular_source || desc->circular_dest) { + ccr |= STM32_DMA_CCR_CIRC; + } + + if (desc->transfer_type == GPDMA_TRANSFER_MEMORY_TO_DEVICE) { + if ((desc->transfer_mode & GPDMA_TRANSFER_MODE_INCREMENT_DEST) != 0U) { + ccr |= STM32_DMA_CCR_PINC; + } + if ((desc->transfer_mode & GPDMA_TRANSFER_MODE_INCREMENT_SRC) != 0U) { + ccr |= STM32_DMA_CCR_MINC; + } + } else { + if ((desc->transfer_mode & GPDMA_TRANSFER_MODE_INCREMENT_SRC) != 0U) { + ccr |= STM32_DMA_CCR_PINC; + } + if ((desc->transfer_mode & GPDMA_TRANSFER_MODE_INCREMENT_DEST) != 0U) { + ccr |= STM32_DMA_CCR_MINC; + } + } + + if ((desc->interrupts & GPDMA_INT_TC) != 0U) { + ccr |= STM32_DMA_CCR_TCIE; + } + if ((desc->interrupts & GPDMA_INT_HT) != 0U) { + ccr |= STM32_DMA_CCR_HTIE; + } + if ((desc->interrupts & GPDMA_INT_ERROR) != 0U) { + ccr |= STM32_DMA_CCR_TEIE; + } + + /* Program request multiplexer (4-bit request per channel). */ + cselr = ioread32(ctrl->base_addr + STM32_DMA_CSELR_REG); + cselr &= ~STM32_DMA_CSELR_CxS_MASK(desc->channel); + cselr |= (((uint32_t)desc->stream & 0xFUL) << STM32_DMA_CSELR_CxS_SHIFT(desc->channel)); + iowrite32(ctrl->base_addr + STM32_DMA_CSELR_REG, cselr); + + iowrite32(ctrl->base_addr + STM32_DMA_CCR(desc->channel), 0); + iowrite32(ctrl->base_addr + STM32_DMA_CNDTR(desc->channel), ndtr); + iowrite32(ctrl->base_addr + STM32_DMA_CPAR(desc->channel), (uint32_t)periph_addr); + iowrite32(ctrl->base_addr + STM32_DMA_CMAR(desc->channel), (uint32_t)mem_addr); + iowrite32(ctrl->base_addr + STM32_DMA_CCR(desc->channel), ccr); + + status = K_STATUS_OKAY; +unmap_end: + gpdma_unmap(); +end: + return status; +} + +/** + * @brief Enable a previously configured DMA channel. + * + * @param[in] desc DMA stream descriptor. + * + * @retval K_STATUS_OKAY Channel enabled. + * @retval K_ERROR_INVPARAM Invalid descriptor or map failure. + */ +kstatus_t stm32_dmav2_channel_enable(gpdma_stream_cfg_t const*const desc) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + uint32_t ccr; + + if (unlikely(!stm32_dmav2_is_valid_desc(desc, &ctrl))) { + goto end; + } + if (unlikely(gpdma_map(desc->controller) != K_STATUS_OKAY)) { + goto end; + } + + ccr = ioread32(ctrl->base_addr + STM32_DMA_CCR(desc->channel)); + ccr |= STM32_DMA_CCR_EN; + iowrite32(ctrl->base_addr + STM32_DMA_CCR(desc->channel), ccr); + + gpdma_unmap(); + status = K_STATUS_OKAY; +end: + return status; +} + +/** + * @brief Get IRQ number associated with one DMA channel. + * + * @param[in] desc DMA stream descriptor. + * @param[out] IRQn Interrupt number associated with this channel. + * + * @retval K_STATUS_OKAY IRQ number returned. + * @retval K_ERROR_INVPARAM Invalid argument. + */ +kstatus_t stm32_dmav2_get_interrupt(gpdma_stream_cfg_t const * const desc, uint16_t * const IRQn) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + + if (unlikely((desc == NULL) || (IRQn == NULL))) { + goto end; + } + if (unlikely(!stm32_dmav2_is_valid_desc(desc, &ctrl))) { + goto end; + } + *IRQn = ctrl->interrupts[desc->channel]; + status = K_STATUS_OKAY; +end: + return status; +} + +/** + * @brief Clear DMA interrupt source for one channel. + * + * @param[in] desc DMA stream descriptor. + * + * @return Same status as stm32_dmav2_channel_clear_status(). + */ +kstatus_t stm32_dmav2_interrupt_clear(gpdma_stream_cfg_t const * const desc) +{ + return stm32_dmav2_channel_clear_status(desc); +} + +/** + * @brief Suspend a running channel by clearing its EN bit. + * + * @param[in] desc DMA stream descriptor. + * + * @retval K_STATUS_OKAY Channel suspended. + * @retval K_ERROR_BADSTATE Channel not running or hardware did not stop. + * @retval K_ERROR_INVPARAM Invalid descriptor or map failure. + */ +kstatus_t stm32_dmav2_channel_suspend(gpdma_stream_cfg_t const*const desc) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + uint32_t ccr; + uint32_t retry = 100000UL; + + if (unlikely(!stm32_dmav2_is_valid_desc(desc, &ctrl))) { + goto end; + } + if (unlikely(gpdma_map(desc->controller) != K_STATUS_OKAY)) { + goto end; + } + + ccr = ioread32(ctrl->base_addr + STM32_DMA_CCR(desc->channel)); + if ((ccr & STM32_DMA_CCR_EN) == 0UL) { + status = K_ERROR_BADSTATE; + goto unmap_end; + } + + ccr &= ~STM32_DMA_CCR_EN; + iowrite32(ctrl->base_addr + STM32_DMA_CCR(desc->channel), ccr); + + do { + ccr = ioread32(ctrl->base_addr + STM32_DMA_CCR(desc->channel)); + if ((ccr & STM32_DMA_CCR_EN) == 0UL) { + status = K_STATUS_OKAY; + goto unmap_end; + } + --retry; + } while (retry > 0UL); + + status = K_ERROR_BADSTATE; +unmap_end: + gpdma_unmap(); +end: + return status; +} + +/** + * @brief Resume a previously suspended DMA channel. + * + * @param[in] desc DMA stream descriptor. + * + * @retval K_STATUS_OKAY Channel resumed. + * @retval K_ERROR_BADSTATE Channel already enabled. + * @retval K_ERROR_INVPARAM Invalid descriptor or map failure. + */ +kstatus_t stm32_dmav2_channel_resume(gpdma_stream_cfg_t const*const desc) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + uint32_t ccr; + + if (unlikely(!stm32_dmav2_is_valid_desc(desc, &ctrl))) { + goto end; + } + if (unlikely(gpdma_map(desc->controller) != K_STATUS_OKAY)) { + goto end; + } + + ccr = ioread32(ctrl->base_addr + STM32_DMA_CCR(desc->channel)); + if ((ccr & STM32_DMA_CCR_EN) != 0UL) { + status = K_ERROR_BADSTATE; + goto unmap_end; + } + + ccr |= STM32_DMA_CCR_EN; + iowrite32(ctrl->base_addr + STM32_DMA_CCR(desc->channel), ccr); + status = K_STATUS_OKAY; +unmap_end: + gpdma_unmap(); +end: + return status; +} + +/** + * @brief Reset channel registers and clear its status flags. + * + * The channel must be disabled before reset. + * + * @param[in] desc DMA stream descriptor. + * + * @retval K_STATUS_OKAY Channel reset. + * @retval K_ERROR_BADSTATE Channel still enabled. + * @retval K_ERROR_INVPARAM Invalid descriptor or map failure. + */ +kstatus_t stm32_dmav2_channel_reset(gpdma_stream_cfg_t const*const desc) +{ + kstatus_t status = K_ERROR_INVPARAM; + stm32_gpdma_desc_t const * ctrl = NULL; + uint32_t ccr; + uint32_t cselr; + + if (unlikely(!stm32_dmav2_is_valid_desc(desc, &ctrl))) { + goto end; + } + if (unlikely(gpdma_map(desc->controller) != K_STATUS_OKAY)) { + goto end; + } + + ccr = ioread32(ctrl->base_addr + STM32_DMA_CCR(desc->channel)); + if ((ccr & STM32_DMA_CCR_EN) != 0UL) { + status = K_ERROR_BADSTATE; + goto unmap_end; + } + + stm32_dmav2_clear_channel_flags(ctrl, desc->channel); + iowrite32(ctrl->base_addr + STM32_DMA_CCR(desc->channel), 0); + iowrite32(ctrl->base_addr + STM32_DMA_CNDTR(desc->channel), 0); + iowrite32(ctrl->base_addr + STM32_DMA_CPAR(desc->channel), 0); + iowrite32(ctrl->base_addr + STM32_DMA_CMAR(desc->channel), 0); + + cselr = ioread32(ctrl->base_addr + STM32_DMA_CSELR_REG); + cselr &= ~STM32_DMA_CSELR_CxS_MASK(desc->channel); + iowrite32(ctrl->base_addr + STM32_DMA_CSELR_REG, cselr); + + status = K_STATUS_OKAY; +unmap_end: + gpdma_unmap(); +end: + return status; +} + +/* aliasing functions to generic API */ +kstatus_t gpdma_probe(uint8_t controller) __attribute__((alias("stm32_dmav2_probe"))); +kstatus_t gpdma_channel_clear_status(gpdma_stream_cfg_t const*const desc) __attribute__((alias("stm32_dmav2_channel_clear_status"))); +kstatus_t gpdma_channel_get_status(gpdma_stream_cfg_t const*const desc, gpdma_chan_status_t * status) __attribute__((alias("stm32_dmav2_channel_get_status"))); +kstatus_t gpdma_channel_configure(gpdma_stream_cfg_t const*const desc) __attribute__((alias("stm32_dmav2_channel_configure"))); +kstatus_t gpdma_channel_enable(gpdma_stream_cfg_t const*const desc) __attribute__((alias("stm32_dmav2_channel_enable"))); +kstatus_t gpdma_get_interrupt(gpdma_stream_cfg_t const * const desc, uint16_t * const IRQn) __attribute__((alias("stm32_dmav2_get_interrupt"))); +kstatus_t gpdma_interrupt_clear(gpdma_stream_cfg_t const * const desc) __attribute__((alias("stm32_dmav2_interrupt_clear"))); +kstatus_t gpdma_channel_suspend(gpdma_stream_cfg_t const*const desc) __attribute__((alias("stm32_dmav2_channel_suspend"))); +kstatus_t gpdma_channel_resume(gpdma_stream_cfg_t const*const desc) __attribute__((alias("stm32_dmav2_channel_resume"))); +kstatus_t gpdma_channel_reset(gpdma_stream_cfg_t const*const desc) __attribute__((alias("stm32_dmav2_channel_reset"))); diff --git a/kernel/src/drivers/dma/stm32-st-dmav2-dt.c.in b/kernel/src/drivers/dma/stm32-st-dmav2-dt.c.in new file mode 100644 index 00000000..e30dfca1 --- /dev/null +++ b/kernel/src/drivers/dma/stm32-st-dmav2-dt.c.in @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: 2023 Ledger SAS +// SPDX-License-Identifier: Apache-2.0 + +#include +#include + +#include "stm32-st-dmav2-dt.h" + +{% set gpdma_ports = dts.get_compatible("stm32-dma-v2") -%} + +{% for port in gpdma_ports -%} +{% if port is owned or port is not enabled -%} +{% continue -%} +{% endif -%} + +/* channel ordered interrupts for each controller, controller-ordered */ +static const uint16_t stm32_{{ port.label }}_interrupts[] = { + /* {{ port['dma-channels']|int }} DMA channels */ + {% for chanirq in range((2*port['dma-channels'])) -%} + {% if port['interrupts'][chanirq]|int != 0 -%} + {{ port['interrupts'][chanirq] }}, /* channel {{ (chanirq / 2)|int }} interrupt identifier */ + {% endif -%} + {% endfor -%} + 0, /* sentinel */ +}; + +#define IRQ_IS_{{ port.label|upper }}_OWNED(x) ( \ +{% for chanirq in range((2*port['dma-channels'])) -%} +{% if port['interrupts'][chanirq]|int != 0 -%} + {{ port['interrupts'][chanirq] }} == x || \ +{% endif -%} +{% endfor -%} + 0 \ +) +{% endfor -%} + +bool stm32_gpdma_irq_is_dma_owned(int IRQn) +{ + return ( \ + {% for port in gpdma_ports -%} + {% if port is owned or port is not enabled -%} + {% continue -%} + {% endif -%} + IRQ_IS_{{ port.label|upper }}_OWNED(IRQn) || \ + {% endfor -%} + 0); +} + +/** + * \brief .rodata field: list of current platform GPDMA ports + */ +static const stm32_gpdma_desc_t stm32_gpdmas[] = { + {% for port in gpdma_ports -%} + {% if port is owned or port is not enabled -%} + {% continue -%} + {% endif -%} + {% set _, bus_id, clk_msk = port.clocks -%} + {%- set num_chan = port['dma-channels'] -%} + {%- set num_req = port['dma-requests'] -%} + /* {{ port.label }} port configuration */ + { + .base_addr = {{ "%#08xUL"|format(port.reg[0]) }}, + .size = {{ "%#08xUL"|format(port.reg[1]) }}, + .bus_id = {{ bus_id }}, + .clk_msk = {{ "%#08xUL"|format(clk_msk) }}, + .num_chan = {{ num_chan|int }}, + .num_req = {{ num_req|int }}, + .interrupts = &stm32_{{ port.label}}_interrupts[0], + }, + {% endfor -%} + {} /* sentinel */ +}; + + +/** + * @warning this is a private function, port id must be valid and checked by caller + */ +const stm32_gpdma_desc_t * stm32_gpdma_get_desc(uint8_t ctrl) +{ + const stm32_gpdma_desc_t *desc = NULL; +#if GPDMA_NUMER > 0 + if (unlikely(ctrl >= GPDMA_NUMBER)) { + goto end; + } + desc = &stm32_gpdmas[ctrl]; +end: +#endif + return desc; +} + +/** + * @warning this is a private function, controller id must be valid and checked by caller + */ +uint16_t const * stm32_gpdma_get_interrupts(uint8_t ctrl) +{ + uint16_t const * its = NULL; +#if GPDMA_NUMER > 0 + if (unlikely(ctrl >= GPDMA_NUMBER)) { + goto end; + } + /*@ assert \valid_read(stm32_gpdmas[ctrl].interrupts); */ + its = stm32_gpdmas[ctrl].interrupts; +end: +#endif + return its; +} + +bool gpdma_irq_is_dma_owned(int IRQn) __attribute__((alias("stm32_gpdma_irq_is_dma_owned"))); diff --git a/kernel/src/drivers/dma/stm32-st-dmav2-dt.h.in b/kernel/src/drivers/dma/stm32-st-dmav2-dt.h.in new file mode 100644 index 00000000..0a5e6cab --- /dev/null +++ b/kernel/src/drivers/dma/stm32-st-dmav2-dt.h.in @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: 2023 Ledger SAS +// SPDX-License-Identifier: Apache-2.0 + +#ifndef __STM32_DMA_DT_GENERATED_H +#define __STM32_DMA_DT_GENERATED_H + +#include + +#include +#include + +{% set gpdma_ports = dts.get_compatible("stm32-dma-v2") -%} + +{% set ns = namespace() -%} +{% set ns.num_gpdma=0 -%} +{% set ns.num_req=0 -%} +{% set ns.num_chan=0 -%} + +{% for port in gpdma_ports -%} +{% if port is owned or port is not enabled -%} + {% continue -%} +{% endif -%} +{% set ns.num_chan = port['dma-channels']|int -%} +{% set ns.num_req = port['dma-requests']|int -%} +{% set ns.num_gpdma = ns.num_gpdma + 1 -%} +{% endfor -%} + +#define GPDMA_NUMBER {{ "%uUL"|format(ns.num_gpdma) }} + +/*@ + +// it is assumed here that when multiple GP DMA controlers +// exists, they have the same requests and chan numbers +// (like, for e.g. in STM32F4 (3 GPDMAs), and only the +// request type may vary +logic gpdma_request_is_valid(ℤ req) = req < {{ ns.num_req }}; + +// again we consider here that all GPDMA has the same channel +// number +logic gpdma_channel_is_valid(ℤ chan) = chan < {{ ns.num_chan }}; + +logic gpdma_controler_exists(ℤ ctrlid) = ctrlid < {{ "%u"|format(ns.num_gpdma) }}; + +*/ + +/** + * \brief STM32 GPDMA IP descriptor + * + * Descriptor is feed from device tree file + * + * \note Only node w/ compatible="st,stm32-usart" and status="okay" properties + * will be parsed + */ +typedef struct stm32_gpdma_desc { + uint32_t base_addr; /**< IP base address */ + size_t size; /**< IP base address */ + bus_id_t bus_id; /**< Bus on which the DMA controller is connected, needed for RCC config */ + uint32_t clk_msk; /**< IP clocks mask on the given bus */ + uint16_t num_chan; /***< IP number of channels */ + uint16_t num_req; /**< IP number of requests */ + uint16_t const *interrupts; /**< IP interrupts, hyp: 1 IRQ per channel */ +} stm32_gpdma_desc_t; + +const stm32_gpdma_desc_t * stm32_gpdma_get_desc(uint8_t ctrl); + +uint16_t const * stm32_gpdma_get_interrupts(uint8_t ctrl); + +static inline kstatus_t gpdma_map(uint8_t controller) +{ + stm32_gpdma_desc_t const * dma_desc = stm32_gpdma_get_desc(controller); + return mgr_mm_map_kdev(dma_desc->base_addr, dma_desc->size); +} + +/* for simplicity sake, but unmaping a kernel device is generic */ +static inline kstatus_t gpdma_unmap(void) { + return mgr_mm_unmap_kdev(); +} + +#endif /* __STM32_DMA_DT_GENERATED_H */ From d032858448563ff776ba0c0c903284dd4aeb5486 Mon Sep 17 00:00:00 2001 From: Philippe Thierry Date: Fri, 22 May 2026 14:50:56 +0200 Subject: [PATCH 08/11] sentry,dma: fixing copyright with uptodate values --- kernel/src/drivers/dma/meson.build | 1 + kernel/src/drivers/dma/stm32-dmav1.c | 2 +- kernel/src/drivers/dma/stm32-dmav2.c | 2 +- kernel/src/drivers/dma/stm32-st-dmav1-dt.c.in | 2 +- kernel/src/drivers/dma/stm32-st-dmav1-dt.h.in | 2 +- kernel/src/drivers/dma/stm32-st-dmav2-dt.c.in | 2 +- kernel/src/drivers/dma/stm32-st-dmav2-dt.h.in | 2 +- kernel/src/managers/dma/dma-dt.c.in | 1 + 8 files changed, 8 insertions(+), 6 deletions(-) diff --git a/kernel/src/drivers/dma/meson.build b/kernel/src/drivers/dma/meson.build index e1983b4b..aa61a23b 100644 --- a/kernel/src/drivers/dma/meson.build +++ b/kernel/src/drivers/dma/meson.build @@ -1,4 +1,5 @@ # SPDX-FileCopyrightText: 2023 Ledger SAS +# SPDX-FileCopyrightText: 2026 H2Lab Development Team # SPDX-License-Identifier: Apache-2.0 # for U5A5 GPDMA implementation, the register definitions are generated from the SVD file using jinja templates. diff --git a/kernel/src/drivers/dma/stm32-dmav1.c b/kernel/src/drivers/dma/stm32-dmav1.c index 71b17eec..cf052b4b 100644 --- a/kernel/src/drivers/dma/stm32-dmav1.c +++ b/kernel/src/drivers/dma/stm32-dmav1.c @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 Ledger SAS +// SPDX-FileCopyrightText: 2026 H2Lab Development Team // SPDX-License-Identifier: Apache-2.0 /** diff --git a/kernel/src/drivers/dma/stm32-dmav2.c b/kernel/src/drivers/dma/stm32-dmav2.c index b65f7917..5142732e 100644 --- a/kernel/src/drivers/dma/stm32-dmav2.c +++ b/kernel/src/drivers/dma/stm32-dmav2.c @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2026 Ledger SAS +// SPDX-FileCopyrightText: 2026 H2Lab Development Team // SPDX-License-Identifier: Apache-2.0 /** diff --git a/kernel/src/drivers/dma/stm32-st-dmav1-dt.c.in b/kernel/src/drivers/dma/stm32-st-dmav1-dt.c.in index 832a49e3..b7dfc993 100644 --- a/kernel/src/drivers/dma/stm32-st-dmav1-dt.c.in +++ b/kernel/src/drivers/dma/stm32-st-dmav1-dt.c.in @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 Ledger SAS +// SPDX-FileCopyrightText: 2026 H2Lab Development Team // SPDX-License-Identifier: Apache-2.0 #include diff --git a/kernel/src/drivers/dma/stm32-st-dmav1-dt.h.in b/kernel/src/drivers/dma/stm32-st-dmav1-dt.h.in index 82ac0c73..a852e24c 100644 --- a/kernel/src/drivers/dma/stm32-st-dmav1-dt.h.in +++ b/kernel/src/drivers/dma/stm32-st-dmav1-dt.h.in @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 Ledger SAS +// SPDX-FileCopyrightText: 2026 H2Lab Development Team // SPDX-License-Identifier: Apache-2.0 #ifndef __STM32_DMA_DT_GENERATED_H diff --git a/kernel/src/drivers/dma/stm32-st-dmav2-dt.c.in b/kernel/src/drivers/dma/stm32-st-dmav2-dt.c.in index e30dfca1..906a6a7d 100644 --- a/kernel/src/drivers/dma/stm32-st-dmav2-dt.c.in +++ b/kernel/src/drivers/dma/stm32-st-dmav2-dt.c.in @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 Ledger SAS +// SPDX-FileCopyrightText: 2026 H2Lab Development Team // SPDX-License-Identifier: Apache-2.0 #include diff --git a/kernel/src/drivers/dma/stm32-st-dmav2-dt.h.in b/kernel/src/drivers/dma/stm32-st-dmav2-dt.h.in index 0a5e6cab..fb381d16 100644 --- a/kernel/src/drivers/dma/stm32-st-dmav2-dt.h.in +++ b/kernel/src/drivers/dma/stm32-st-dmav2-dt.h.in @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2023 Ledger SAS +// SPDX-FileCopyrightText: 2026 H2Lab Development Team // SPDX-License-Identifier: Apache-2.0 #ifndef __STM32_DMA_DT_GENERATED_H diff --git a/kernel/src/managers/dma/dma-dt.c.in b/kernel/src/managers/dma/dma-dt.c.in index b56ddc71..b2ed9674 100644 --- a/kernel/src/managers/dma/dma-dt.c.in +++ b/kernel/src/managers/dma/dma-dt.c.in @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2023 Ledger SAS +// SPDX-FileCopyrightText: 2026 H2Lab Development Team // SPDX-License-Identifier: Apache-2.0 /** From a676f1c7ffd983865cd85a717152c85e8c7450fa Mon Sep 17 00:00:00 2001 From: Philippe Thierry Date: Fri, 22 May 2026 16:07:51 +0200 Subject: [PATCH 09/11] dts,armv7m: fixing invalid power-of-two MPU region alignment --- dts/sentry.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dts/sentry.dtsi b/dts/sentry.dtsi index 04d45aa4..d847a880 100644 --- a/dts/sentry.dtsi +++ b/dts/sentry.dtsi @@ -17,7 +17,7 @@ #size-cells = <1>; kernel_code: kernel_code@8000000 { - reg = <0x8000000 0xb800>; + reg = <0x8000000 0xc000>; compatible = "sentry,memory-pool"; }; From 12701b01119709b89f8332a9ac888593f9887b17 Mon Sep 17 00:00:00 2001 From: Philippe Thierry Date: Fri, 22 May 2026 16:18:43 +0200 Subject: [PATCH 10/11] sentry,dma: fixing typo --- kernel/src/drivers/dma/stm32-st-dmav1-dt.c.in | 4 ++-- kernel/src/drivers/dma/stm32-st-dmav2-dt.c.in | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/src/drivers/dma/stm32-st-dmav1-dt.c.in b/kernel/src/drivers/dma/stm32-st-dmav1-dt.c.in index b7dfc993..de18b047 100644 --- a/kernel/src/drivers/dma/stm32-st-dmav1-dt.c.in +++ b/kernel/src/drivers/dma/stm32-st-dmav1-dt.c.in @@ -78,7 +78,7 @@ static const stm32_gpdma_desc_t stm32_gpdmas[] = { const stm32_gpdma_desc_t * stm32_gpdma_get_desc(uint8_t ctrl) { const stm32_gpdma_desc_t *desc = NULL; -#if GPDMA_NUMER > 0 +#if GPDMA_NUMBER > 0 if (unlikely(ctrl >= GPDMA_NUMBER)) { goto end; } @@ -94,7 +94,7 @@ end: uint16_t const * stm32_gpdma_get_interrupts(uint8_t ctrl) { uint16_t const * its = NULL; -#if GPDMA_NUMER > 0 +#if GPDMA_NUMBER > 0 if (unlikely(ctrl >= GPDMA_NUMBER)) { goto end; } diff --git a/kernel/src/drivers/dma/stm32-st-dmav2-dt.c.in b/kernel/src/drivers/dma/stm32-st-dmav2-dt.c.in index 906a6a7d..c2558bfb 100644 --- a/kernel/src/drivers/dma/stm32-st-dmav2-dt.c.in +++ b/kernel/src/drivers/dma/stm32-st-dmav2-dt.c.in @@ -78,7 +78,7 @@ static const stm32_gpdma_desc_t stm32_gpdmas[] = { const stm32_gpdma_desc_t * stm32_gpdma_get_desc(uint8_t ctrl) { const stm32_gpdma_desc_t *desc = NULL; -#if GPDMA_NUMER > 0 +#if GPDMA_NUMBER > 0 if (unlikely(ctrl >= GPDMA_NUMBER)) { goto end; } @@ -94,7 +94,7 @@ end: uint16_t const * stm32_gpdma_get_interrupts(uint8_t ctrl) { uint16_t const * its = NULL; -#if GPDMA_NUMER > 0 +#if GPDMA_NUMBER > 0 if (unlikely(ctrl >= GPDMA_NUMBER)) { goto end; } From 78d130bb580c39813f18d454b2a18bafbcd6a03b Mon Sep 17 00:00:00 2001 From: Philippe Thierry Date: Fri, 22 May 2026 16:25:48 +0200 Subject: [PATCH 11/11] autotest,dma: adding missing streams for f429 dma tests --- configs/stm32f429i_disc1_autotest_defconfig | 1 + dts/examples/stm32f429i_disc1_autotest.dts | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/configs/stm32f429i_disc1_autotest_defconfig b/configs/stm32f429i_disc1_autotest_defconfig index 9b51430e..28115b81 100644 --- a/configs/stm32f429i_disc1_autotest_defconfig +++ b/configs/stm32f429i_disc1_autotest_defconfig @@ -6,3 +6,4 @@ CONFIG_STANDALONE_MODE=y CONFIG_BUILD_TARGET_AUTOTEST=y CONFIG_TEST_IRQ=y CONFIG_TEST_CAPA=y +CONFIG_TEST_DMA=y diff --git a/dts/examples/stm32f429i_disc1_autotest.dts b/dts/examples/stm32f429i_disc1_autotest.dts index ef676769..04c1254d 100644 --- a/dts/examples/stm32f429i_disc1_autotest.dts +++ b/dts/examples/stm32f429i_disc1_autotest.dts @@ -18,7 +18,7 @@ }; reserved-memory { - autotest_code: autotest_code@800e000 { + autotest_code: autotest_code@8010000 { reg = <0x8010000 0x10000>; compatible = "sentry,memory-pool"; }; @@ -66,6 +66,18 @@ dma-streams { // device-to-memory DMA stream stream1 { + compatible = "dma-stream"; + channel = <&dma1_1>; + streamid = <3>; // channel stream (af) identifier + prio = ; + source = <&usart1>; + dest = <&shm_autotest_1>; + length = <42>; + circular = <1 0>; // circular source, linear dest + sentry,label = <0x1>; // task-level unique DMA identifier + }; + // memory-to-memory DMA stream + stream2 { compatible = "dma-stream"; channel = <&dma1_1>; streamid = <1>; // channel stream (af) identifier @@ -74,7 +86,7 @@ dest = <&shm_autotest_2>; length = <42>; circular = <1 0>; // circular source, linear dest - sentry,label = <0x1>; // task-level unique DMA identifier + sentry,label = <0x2>; // task-level unique DMA identifier }; }; };