From 32086c6d8d563e1e87a18821d4612bf887928ecd Mon Sep 17 00:00:00 2001 From: raiden00pl Date: Sun, 3 Mar 2019 12:22:37 +0000 Subject: [PATCH] Merged in raiden00/nuttx_h7 (pull request #836) Initial DMA support for STM32H7 arch/arm/src/stm32h7: SPI DMA mode arch/arm/src/stm32h7/chip/stm32h7x3xx_memorymap.h: add base addresses for D1, D2 and D3 domain; use 2 bytes to specify memory region arch/arm/src/stm32h7/stm32h7x3xx_rcc.c: add D1 domain core prescaler configuration Approved-by: Gregory Nutt --- arch/arm/src/stm32h7/Kconfig | 24 + .../src/stm32h7/chip/stm32h7x3xx_memorymap.h | 23 +- arch/arm/src/stm32h7/stm32_dma.c | 2333 +++++++++++++++++ arch/arm/src/stm32h7/stm32_dma.h | 231 ++ arch/arm/src/stm32h7/stm32_spi.c | 55 +- arch/arm/src/stm32h7/stm32h7x3xx_rcc.c | 7 +- configs/nucleo-h743zi/include/board.h | 10 +- 7 files changed, 2655 insertions(+), 28 deletions(-) create mode 100644 arch/arm/src/stm32h7/stm32_dma.c create mode 100644 arch/arm/src/stm32h7/stm32_dma.h diff --git a/arch/arm/src/stm32h7/Kconfig b/arch/arm/src/stm32h7/Kconfig index 7fd28f7435..dfefca7eb4 100644 --- a/arch/arm/src/stm32h7/Kconfig +++ b/arch/arm/src/stm32h7/Kconfig @@ -147,6 +147,20 @@ config STM32H7_DMA2 select STM32H7_DMA select ARCH_DMA +config STM32H7_MDMA + bool "MDMA" + default n + depends on EXPERIMENTAL + select STM32H7_DMA + select ARCH_DMA + +config STM32H7_BDMA + bool "BDMA" + default n + depends on EXPERIMENTAL + select STM32H7_DMA + select ARCH_DMA + menu "STM32H7 I2C Selection" config STM32H7_I2C1 @@ -332,6 +346,16 @@ config STM32H7_SPI_DMA endmenu # "SPI Configuration" +config STM32H7_DMACAPABLE + bool "Workaround non-DMA capable memory" + depends on ARCH_DMA + default n + ---help--- + This option enables the DMA interface stm32_dmacapable that can be + used to check if it is possible to do DMA from the selected address. + Drivers then may use this information to determine if they should + attempt the DMA or fall back to a different transfer method. + menu "U[S]ART Configuration" depends on STM32H7_USART diff --git a/arch/arm/src/stm32h7/chip/stm32h7x3xx_memorymap.h b/arch/arm/src/stm32h7/chip/stm32h7x3xx_memorymap.h index 03568604f7..59a992293e 100644 --- a/arch/arm/src/stm32h7/chip/stm32h7x3xx_memorymap.h +++ b/arch/arm/src/stm32h7/chip/stm32h7x3xx_memorymap.h @@ -59,11 +59,10 @@ #define STM32_FMC_BANK6 0xd0000000 /* 0xd0000000-0xdfffffff: 256Mb FMC SDRAM Bank 2 */ #define STM32_CORTEX_BASE 0xe0000000 /* 0xe0000000-0xffffffff: 512Mb Cortex-M7 block */ -#define STM32_REGION_MASK 0xf0000000 +#define STM32_REGION_MASK 0xff000000 #define STM32_IS_SRAM(a) ((((uint32_t)(a)) & STM32_REGION_MASK) == STM32_SRAM_BASE) #define STM32_IS_EXTSRAM(a) ((((uint32_t)(a)) & STM32_REGION_MASK) == STM32_FMC_BANK1) - /* Code Base Addresses **************************************************************/ #define STM32_ITCM_BASE 0x00000000 /* 0x00000000-0x0000ffff: ITCM */ @@ -85,14 +84,18 @@ /* Peripheral Base Addresses ********************************************************/ -#define STM32_APB1_BASE 0x40000000 /* 0x40000000-0x40007fff: APB1 */ -#define STM32_APB2_BASE 0x40010000 /* 0x40010000-0x40016bff: APB2 */ -#define STM32_AHB1_BASE 0x40020000 /* 0x40020000-0x4007ffff: APB1 */ -#define STM32_AHB2_BASE 0x48020000 /* 0x50000000-0x48022bff: AHB2 */ -#define STM32_APB3_BASE 0x50000000 /* 0x60000000-0x50003fff: APB3 */ -#define STM32_AHB3_BASE 0x51000000 /* 0x51000000-0x52008fff: AHB3 */ -#define STM32_APB4_BASE 0x58000000 /* 0x60000000-0x58006bff: APB4 */ -#define STM32_AHB4_BASE 0x58020000 /* 0x58020000-0x580267ff: AHB4 */ +#define STM32_PREGION_MASK 0xff000000 +#define STM32_D2_BASE 0x40000000 /* 0x40000000-0x48022800: D2 domain */ +# define STM32_APB1_BASE 0x40000000 /* 0x40000000-0x40007fff: APB1 */ +# define STM32_APB2_BASE 0x40010000 /* 0x40010000-0x40016bff: APB2 */ +# define STM32_AHB1_BASE 0x40020000 /* 0x40020000-0x4007ffff: APB1 */ +# define STM32_AHB2_BASE 0x48020000 /* 0x50000000-0x48022bff: AHB2 */ +#define STM32_D1_BASE 0x50000000 /* 0x50000000-0x50003fff: D1 domain */ +# define STM32_APB3_BASE 0x50000000 /* 0x60000000-0x50003fff: APB3 */ +# define STM32_AHB3_BASE 0x51000000 /* 0x51000000-0x52008fff: AHB3 */ +#define STM32_D3_BASE 0x58000000 /* 0x58000000-0x580267ff: D3 domain */ +# define STM32_APB4_BASE 0x58000000 /* 0x60000000-0x58006bff: APB4 */ +# define STM32_AHB4_BASE 0x58020000 /* 0x58020000-0x580267ff: AHB4 */ /* APB1 Base Addresses **************************************************************/ diff --git a/arch/arm/src/stm32h7/stm32_dma.c b/arch/arm/src/stm32h7/stm32_dma.c new file mode 100644 index 0000000000..8fba91912e --- /dev/null +++ b/arch/arm/src/stm32h7/stm32_dma.c @@ -0,0 +1,2333 @@ +/**************************************************************************** + * arch/arm/src/stm32h7/stm32_dma.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Authors: Mateusz Szafoni + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "up_arch.h" + +#include "up_internal.h" +#include "sched/sched.h" +#include "stm32_dma.h" +#include "chip/stm32_bdma.h" +#include "chip/stm32_mdma.h" +#include "chip/stm32_dmamux.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define DMAMUX_NUM 2 +#define DMA_CONTROLLERS 4 + +#ifdef CONFIG_STM32H7_MDMA +# define MDMA_NCHAN 16 +#else +# define MDMA_NCHAN 0 +#endif +#ifdef CONFIG_STM32H7_DMA1 +# define DMA1_NSTREAMS 8 +#else +# define DMA1_NSTREAMS 0 +#endif +#ifdef CONFIG_STM32H7_DMA2 +# define DMA2_NSTREAMS 8 +#else +# define DMA2_NSTREAMS 0 +#endif +#ifdef CONFIG_STM32H7_BDMA +# define BDMA_NCHAN 8 +#else +# define BDMA_NCHAN 0 +#endif + +#define MDMA_FIRST (0) +#define MDMA_LAST (MDMA_FIRST+MDMA_NCHAN) +#define DMA1_FIRST (MDMA_LAST) +#define DMA1_LAST (DMA1_FIRST+DMA1_NSTREAMS) +#define DMA2_FIRST (DMA1_LAST) +#define DMA2_LAST (DMA2_FIRST+DMA2_NSTREAMS) +#define BDMA_FIRST (DMA2_LAST) +#define BDMA_LAST (BDMA_FIRST+BDMA_NCHAN) + +/* All availalbe DMA channels (streams from standard DMA and + * channels from BDMA and MDMA) + */ + +#define DMA_NCHANNELS (DMA1_NSTREAMS+DMA2_NSTREAMS+MDMA_NCHAN+BDMA_NCHAN) + +/* Default DMA, MDMA and BDMA priorities */ + +#ifndef CONFIG_DMA_PRI +# define CONFIG_DMA_PRI NVIC_SYSH_PRIORITY_DEFAULT +#endif + +#ifndef CONFIG_MDMA_PRI +# define CONFIG_MDMA_PRI NVIC_SYSH_PRIORITY_DEFAULT +#endif + +#ifndef CONFIG_BDMA_PRI +# define CONFIG_BDMA_PRI NVIC_SYSH_PRIORITY_DEFAULT +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure described one DMAMUX device */ + +struct stm32_dmamux_s +{ + uint8_t id; /* DMAMUX id */ + uint8_t nchan; /* DMAMUX channels */ + uint32_t base; /* DMAMUX base address */ +}; + +typedef FAR struct stm32_dmamux_s *DMA_MUX; + +/* This structure describes one DMA controller */ + +struct stm32_dma_s +{ + uint8_t first; /* Offset in stm32_dmach_s array */ + uint8_t nchan; /* Number of channels */ + uint8_t dmamux_offset; /* DMAMUX channel offset */ + uint32_t base; /* Base address */ + DMA_MUX dmamux; /* DMAMUX associated with controller */ +}; + +/* This structure describes one DMA channel (DMA12, MDMA or BDMA) */ + +struct stm32_dmach_s +{ + bool used; /* Channel in use */ + uint8_t ctrl:3; /* DMA controller */ + uint8_t chan:5; /* DMA stream/channel channel id*/ + uint8_t irq; /* DMA stream IRQ number */ + uint8_t shift; /* ISR/IFCR bit shift value */ + uint32_t base; /* DMA register channel base address */ + dma_callback_t callback; /* Callback invoked when the DMA completes */ + void *arg; /* Argument passed to callback function */ +}; + +typedef FAR struct stm32_dmach_s *DMA_CHANNEL; + +/* DMA opertions */ + +struct stm32_dma_ops_s +{ + /* Start the DMA transfer */ + + CODE void (*dma_disable)(DMA_CHANNEL dmachan); + + /* DMA interrupt */ + + CODE int (*dma_interrupt)(int irq, void *context, FAR void *arg); + + /* Setup the DMA */ + + CODE void (*dma_setup)(DMA_HANDLE handle, FAR stm32_dmacfg_t *cfg); + + /* Start the DMA */ + + CODE void (*dma_start)(DMA_HANDLE handle, dma_callback_t callback, + void *arg, bool half); + + /* Read remaining DMA bytes */ + + CODE size_t (*dma_residual)(DMA_HANDLE handle); + + /* Check the DMA configuration */ + + CODE bool (*dma_capable)(FAR stm32_dmacfg_t *cfg); + + /* Dump the DMA registers */ + + CODE void (*dma_dump)(DMA_HANDLE handle, const char *msg); +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_MDMA +static void stm32_mdma_disable(DMA_CHANNEL dmachan); +static int stm32_mdma_interrupt(int irq, void *context, FAR void *arg); +static void stm32_mdma_setup(DMA_HANDLE handle, FAR stm32_dmacfg_t *cfg); +static void stm32_mdma_start(DMA_HANDLE handle, dma_callback_t callback, + void *arg, bool half); +static size_t stm32_mdma_residual(DMA_HANDLE handle); +#ifdef CONFIG_STM32H7_DMACAPABLE +static bool stm32_mdma_capable(FAR stm32_dmacfg_t *cfg); +#endif +#ifdef CONFIG_DEBUG_DMA_INFO +static void stm32_mdma_dump(DMA_HANDLE handle, const char *msg); +#endif +#endif + +#if defined(CONFIG_STM32H7_DMA1) || defined(CONFIG_STM32H7_DMA2) +static void stm32_sdma_disable(DMA_CHANNEL dmachan); +static int stm32_sdma_interrupt(int irq, void *context, FAR void *arg); +static void stm32_sdma_setup(DMA_HANDLE handle, FAR stm32_dmacfg_t *cfg); +static void stm32_sdma_start(DMA_HANDLE handle, dma_callback_t callback, + void *arg, bool half); +static size_t stm32_sdma_residual(DMA_HANDLE handle); +#ifdef CONFIG_STM32H7_DMACAPABLE +static bool stm32_sdma_capable(FAR stm32_dmacfg_t *cfg); +#endif +#ifdef CONFIG_DEBUG_DMA_INFO +static void stm32_sdma_dump(DMA_HANDLE handle, const char *msg); +#endif +#endif + +#ifdef CONFIG_STM32H7_BDMA +static void stm32_bdma_disable(DMA_CHANNEL dmachan); +static int stm32_bdma_interrupt(int irq, void *context, FAR void *arg); +static void stm32_bdma_setup(DMA_HANDLE handle, FAR stm32_dmacfg_t *cfg); +static void stm32_bdma_start(DMA_HANDLE handle, dma_callback_t callback, + void *arg, bool half); +static size_t stm32_bdma_residual(DMA_HANDLE handle); +#ifdef CONFIG_STM32H7_DMACAPABLE +static bool stm32_bdma_capable(FAR stm32_dmacfg_t *cfg); +#endif +#ifdef CONFIG_DEBUG_DMA_INFO +static void stm32_bdma_dump(DMA_HANDLE handle, const char *msg); +#endif +#endif + +static uint32_t dmachan_getbase(DMA_CHANNEL dmachan); +static uint32_t dmabase_getreg(DMA_CHANNEL dmachan, uint32_t offset); +static void dmabase_putreg(DMA_CHANNEL dmachan, uint32_t offset, uint32_t value); +static uint32_t dmachan_getreg(DMA_CHANNEL dmachan, uint32_t offset); +static void dmachan_putreg(DMA_CHANNEL dmachan, uint32_t offset, uint32_t value); +static void dmamux_putreg(DMA_MUX dmamux, uint32_t offset, uint32_t value); +#ifdef CONFIG_DEBUG_DMA_INFO +static uint32_t dmamux_getreg(DMA_MUX dmamux, uint32_t offset); +static void stm32_dmamux_dump(DMA_MUX dmamux, uint8_t chan); +#endif +static DMA_CHANNEL stm32_dma_channel_get(uint8_t channel, uint8_t controller); +static void stm32_gdma_limits_get(uint8_t controller, FAR uint8_t *first, + FAR uint8_t *last); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Operations specific to DMA controller */ + +struct stm32_dma_ops_s g_dma_ops[DMA_CONTROLLERS] = +{ +#ifdef CONFIG_STM32H7_MDMA + /* 0 - MDMA */ + + { + .dma_disable = stm32_mdma_disable, + .dma_interrupt = stm32_mdma_interrupt, + .dma_setup = stm32_mdma_setup, + .dma_start = stm32_mdma_start, + .dma_residual = stm32_mdma_residual, +#ifdef CONFIG_STM32H7_DMACAPABLE + .dma_capable = stm32_mdma_capable, +#endif +#ifdef CONFIG_DEBUG_DMA_INFO + .dma_dump = stm32_mdma_dump, +#endif + }, +#else + { NULL }, +#endif + +#ifdef CONFIG_STM32H7_DMA1 + /* 1 - DMA1 */ + + { + .dma_disable = stm32_sdma_disable, + .dma_interrupt = stm32_sdma_interrupt, + .dma_setup = stm32_sdma_setup, + .dma_start = stm32_sdma_start, + .dma_residual = stm32_sdma_residual, +#ifdef CONFIG_STM32H7_DMACAPABLE + .dma_capable = stm32_sdma_capable, +#endif +#ifdef CONFIG_DEBUG_DMA_INFO + .dma_dump = stm32_sdma_dump, +#endif + }, +#else + { NULL }, +#endif + +#ifdef CONFIG_STM32H7_DMA2 + /* 2 - DMA2 */ + + { + .dma_disable = stm32_sdma_disable, + .dma_interrupt = stm32_sdma_interrupt, + .dma_setup = stm32_sdma_setup, + .dma_start = stm32_sdma_start, + .dma_residual = stm32_sdma_residual, +#ifdef CONFIG_STM32H7_DMACAPABLE + .dma_capable = stm32_sdma_capable, +#endif +#ifdef CONFIG_DEBUG_DMA_INFO + .dma_dump = stm32_sdma_dump, +#endif + }, +#else + { NULL }, +#endif + +#ifdef CONFIG_STM32H7_BDMA + /* 3 - BDMA */ + + { + .dma_disable = stm32_bdma_disable, + .dma_interrupt = stm32_bdma_interrupt, + .dma_setup = stm32_bdma_setup, + .dma_start = stm32_bdma_start, + .dma_residual = stm32_bdma_residual, +#ifdef CONFIG_STM32H7_DMACAPABLE + .dma_capable = stm32_bdma_capable, +#endif +#ifdef CONFIG_DEBUG_DMA_INFO + .dma_dump = stm32_bdma_dump, +#endif + } +#else + { NULL } +#endif +}; + +/* This array describes the state of DMAMUX controller */ + +struct stm32_dmamux_s g_dmamux[DMAMUX_NUM] = +{ + { + .id = 1, + .nchan = 16, /* 0-7 - DMA1, 8-15 - DMA2 */ + .base = STM32_DMAMUX1_BASE + }, + { + .id = 2, + .nchan = 8, /* 0-7 - BDMA */ + .base = STM32_DMAMUX2_BASE + } +}; + +/* This array describes the state of each controller */ + +struct stm32_dma_s g_dma[DMA_NCHANNELS] = +{ + /* 0 - MDMA */ + { + .base = STM32_MDMA_BASE, + .first = MDMA_FIRST, + .nchan = MDMA_NCHAN, + .dmamux = NULL, /* No DMAMUX */ + .dmamux_offset = 0 + }, + + /* 1 - DMA1 */ + { + .base = STM32_DMA1_BASE, + .first = DMA1_FIRST, + .nchan = DMA1_NSTREAMS, + .dmamux = &g_dmamux[DMAMUX1], /* DMAMUX1 channels 0-7 */ + .dmamux_offset = 0 + }, + + /* 2 - DMA2 */ + { + .base = STM32_DMA2_BASE, + .first = DMA2_FIRST, + .nchan = DMA2_NSTREAMS, + .dmamux = &g_dmamux[DMAMUX1], /* DMAMUX1 channels 8-15 */ + .dmamux_offset = 8 + }, + + /* 3 - BDMA */ + { + .base = STM32_BDMA_BASE, + .first = BDMA_FIRST, + .nchan = BDMA_NCHAN, + .dmamux = &g_dmamux[DMAMUX2], /* DMAMUX2 channels 0-7 */ + .dmamux_offset = 0 + } +}; + +/* This array describes the state of each DMA channel. + * Note that we keep here standard DMA streams, BDMA channels and MDMA channels. + */ + +static struct stm32_dmach_s g_dmach[DMA_NCHANNELS] = +{ +#ifdef CONFIG_STM32H7_MDMA + /* MDMA */ + + { + .ctrl = MDMA, + .chan = 0, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(0), + }, + + { + .ctrl = MDMA, + .chan = 1, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(1), + }, + + { + .ctrl = MDMA, + .chan = 2, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(2), + }, + + { + .ctrl = MDMA, + .chan = 3, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(3), + }, + + { + .ctrl = MDMA, + .chan = 4, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(4), + }, + + { + .ctrl = MDMA, + .chan = 5, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(5), + }, + + { + .ctrl = MDMA, + .chan = 6, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(6), + }, + + { + .ctrl = MDMA, + .chan = 7, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(7), + }, + + { + .ctrl = MDMA, + .chan = 8, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(8), + }, + + { + .ctrl = MDMA, + .chan = 9, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(9), + }, + + { + .ctrl = MDMA, + .chan = 10, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(10), + }, + + { + .ctrl = MDMA, + .chan = 11, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(11), + }, + + { + .ctrl = MDMA, + .chan = 12, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(12), + }, + + { + .ctrl = MDMA, + .chan = 13, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(13), + }, + + { + .ctrl = MDMA, + .chan = 14, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(15), + }, + + { + .ctrl = MDMA, + .chan = 15, + .irq = STM32_IRQ_MDMA, + .shift = 0, + .base = STM32_MDMA_BASE + STM32_MDMA_OFFSET(15), + }, +#endif + +#ifdef CONFIG_STM32H7_DMA1 + /* DMA1 */ + + { + .ctrl = DMA1, + .chan = 0, + .irq = STM32_IRQ_DMA1S0, + .shift = DMA_INT_STREAM0_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(0), + }, + { + .ctrl = DMA1, + .chan = 1, + .irq = STM32_IRQ_DMA1S1, + .shift = DMA_INT_STREAM1_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(1), + }, + { + .ctrl = DMA1, + .chan = 2, + .irq = STM32_IRQ_DMA1S2, + .shift = DMA_INT_STREAM2_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(2), + }, + { + .ctrl = DMA1, + .chan = 3, + .irq = STM32_IRQ_DMA1S3, + .shift = DMA_INT_STREAM3_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(3), + }, + { + .ctrl = DMA1, + .chan = 4, + .irq = STM32_IRQ_DMA1S4, + .shift = DMA_INT_STREAM4_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(4), + }, + { + .ctrl = DMA1, + .chan = 5, + .irq = STM32_IRQ_DMA1S5, + .shift = DMA_INT_STREAM5_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(5), + }, + { + .ctrl = DMA1, + .chan = 6, + .irq = STM32_IRQ_DMA1S6, + .shift = DMA_INT_STREAM6_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(6), + }, + { + .ctrl = DMA1, + .chan = 7, + .irq = STM32_IRQ_DMA1S7, + .shift = DMA_INT_STREAM7_SHIFT, + .base = STM32_DMA1_BASE + STM32_DMA_OFFSET(7), + }, +#endif + +#ifdef CONFIG_STM32H7_DMA2 + /* DMA2 */ + + { + .ctrl = DMA2, + .chan = 0, + .irq = STM32_IRQ_DMA2S0, + .shift = DMA_INT_STREAM0_SHIFT, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(0), + }, + { + .ctrl = DMA2, + .chan = 1, + .irq = STM32_IRQ_DMA2S1, + .shift = DMA_INT_STREAM1_SHIFT, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(1), + }, + { + .ctrl = DMA2, + .chan = 2, + .irq = STM32_IRQ_DMA2S2, + .shift = DMA_INT_STREAM2_SHIFT, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(2), + }, + { + .ctrl = DMA2, + .chan = 3, + .irq = STM32_IRQ_DMA2S3, + .shift = DMA_INT_STREAM3_SHIFT, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(3), + }, + { + .ctrl = DMA2, + .chan = 4, + .irq = STM32_IRQ_DMA2S4, + .shift = DMA_INT_STREAM4_SHIFT, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(4), + }, + { + .ctrl = DMA2, + .chan = 5, + .irq = STM32_IRQ_DMA2S5, + .shift = DMA_INT_STREAM5_SHIFT, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(5), + }, + { + .ctrl = DMA2, + .chan = 6, + .irq = STM32_IRQ_DMA2S6, + .shift = DMA_INT_STREAM6_SHIFT, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(6), + }, + { + .ctrl = DMA2, + .chan = 7, + .irq = STM32_IRQ_DMA2S7, + .shift = DMA_INT_STREAM7_SHIFT, + .base = STM32_DMA2_BASE + STM32_DMA_OFFSET(7), + }, +#endif + +#ifdef CONFIG_STM32H7_BDMA + /* BDMA */ + + { + .ctrl = BDMA, + .chan = 0, + .irq = STM32_IRQ_BDMACH1, + .shift = BDMA_CHAN_SHIFT(0), + .base = STM32_BDMA_BASE + STM32_BDMA_OFFSET(0), + }, + + { + .ctrl = BDMA, + .chan = 1, + .irq = STM32_IRQ_BDMACH2, + .shift = BDMA_CHAN_SHIFT(1), + .base = STM32_BDMA_BASE + STM32_BDMA_OFFSET(1), + }, + + { + .ctrl = BDMA, + .chan = 2, + .irq = STM32_IRQ_BDMACH3, + .shift = BDMA_CHAN_SHIFT(2), + .base = STM32_BDMA_BASE + STM32_BDMA_OFFSET(2), + }, + + { + .ctrl = BDMA, + .chan = 3, + .irq = STM32_IRQ_BDMACH4, + .shift = BDMA_CHAN_SHIFT(3), + .base = STM32_BDMA_BASE + STM32_BDMA_OFFSET(3), + }, + + { + .ctrl = BDMA, + .chan = 4, + .irq = STM32_IRQ_BDMACH5, + .shift = BDMA_CHAN_SHIFT(4), + .base = STM32_BDMA_BASE + STM32_BDMA_OFFSET(4), + }, + + { + .ctrl = BDMA, + .chan = 5, + .irq = STM32_IRQ_BDMACH6, + .shift = BDMA_CHAN_SHIFT(5), + .base = STM32_BDMA_BASE + STM32_BDMA_OFFSET(5), + }, + + { + .ctrl = BDMA, + .chan = 6, + .irq = STM32_IRQ_BDMACH7, + .shift = BDMA_CHAN_SHIFT(6), + .base = STM32_BDMA_BASE + STM32_BDMA_OFFSET(6), + }, + + { + .ctrl = BDMA, + .chan = 7, + .irq = STM32_IRQ_BDMACH8, + .shift = BDMA_CHAN_SHIFT(7), + .base = STM32_BDMA_BASE + STM32_BDMA_OFFSET(7), + }, +#endif +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * DMA register access functions + ****************************************************************************/ + +/**************************************************************************** + * Name: dmachan_getbase + * + * Description: + * Get base DMA address for dmachan + * + ****************************************************************************/ + +static uint32_t dmachan_getbase(DMA_CHANNEL dmachan) +{ + uint8_t controller = dmachan->ctrl; + + return g_dma[controller].base; +} + +/********************************************************************** + * Name: dmabase_getreg + * + * Description: + * Get non-channel register from DMA controller + * + ****************************************************************************/ + +static uint32_t dmabase_getreg(DMA_CHANNEL dmachan, uint32_t offset) +{ + uint32_t dmabase = dmachan_getbase(dmachan); + + return getreg32(dmabase + offset); +} + +/**************************************************************************** + * Name: dmabase_putreg + * + * Description: + * Write to non-channel register in DMA controller + * + ****************************************************************************/ + +static void dmabase_putreg(DMA_CHANNEL dmachan, uint32_t offset, uint32_t value) +{ + uint32_t dmabase = dmachan_getbase(dmachan); + + putreg32(value, dmabase + offset); +} + +/**************************************************************************** + * Name: dmachan_getreg + * + * Description: + * Get channel register. + * + ****************************************************************************/ + +static uint32_t dmachan_getreg(DMA_CHANNEL dmachan, uint32_t offset) +{ + return getreg32(dmachan->base + offset); +} + +/**************************************************************************** + * Name: dmachan_putreg + * + * Description: + * Write to channel register. + * + ****************************************************************************/ + +static void dmachan_putreg(DMA_CHANNEL dmachan, uint32_t offset, uint32_t value) +{ + putreg32(value, dmachan->base + offset); +} + +/**************************************************************************** + * Name: dmamux_getreg + * + * Description: + * Write to DMAMUX + * + ****************************************************************************/ + +static void dmamux_putreg(DMA_MUX dmamux, uint32_t offset, uint32_t value) +{ + putreg32(value, dmamux->base + offset); +} + +/**************************************************************************** + * Name: dmamux_getreg + * + * Description: + * Get DMAMUX register. + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA_INFO +static uint32_t dmamux_getreg(DMA_MUX dmamux, uint32_t offset) +{ + return getreg32(dmamux->base + offset); +} +#endif + +/**************************************************************************** + * Name: stm32_dma_channel_get + * + * Description: + * Get the g_dmach table entry associated with a given DMA controller + * and channel number. + * + ****************************************************************************/ + +static DMA_CHANNEL stm32_dma_channel_get(uint8_t channel, uint8_t controller) +{ + uint8_t first = 0; + uint8_t nchan = 0; + + /* Get limits for g_dma array */ + + stm32_gdma_limits_get(controller, &first, &nchan); + + DEBUGASSERT(channel <= nchan); + + return &g_dmach[first + channel]; +} + +/**************************************************************************** + * Name: stm32_gdma_limits_get + * + * Description: + * Get g_dma array limits for a given DMA controller. + * + ****************************************************************************/ + +static void stm32_gdma_limits_get(uint8_t controller, FAR uint8_t *first, + FAR uint8_t *nchan) +{ + DEBUGASSERT(first != NULL); + DEBUGASSERT(nchan != NULL); + + DEBUGASSERT(controller >= MDMA && controller <= BDMA); + + *first = g_dma[controller].first; + *nchan = g_dma[controller].nchan; +} + +/**************************************************************************** + * Master DMA functions + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_MDMA + +/**************************************************************************** + * Name: stm32_mdma_disable + * + * Description: + * Disable the master DMA + * + ****************************************************************************/ + +static void stm32_mdma_disable(DMA_CHANNEL dmachan) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + + DEBUGASSERT(controller == MDMA); + +#warning stm32_mdma_disable +} + +/**************************************************************************** + * Name: stm32_mdma_interrupt + * + * Description: + * Master DMA interrupt handler + * + ****************************************************************************/ + +static int stm32_mdma_interrupt(int irq, void *context, FAR void *arg) +{ +#warning stm32_mdma_interrupt +} + +/**************************************************************************** + * Name: stm32_mdma_setup + * + * Description: + * Configure master DMA before using + * + ****************************************************************************/ + +static void stm32_mdma_setup(DMA_HANDLE handle, FAR stm32_dmacfg_t *cfg) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + + DEBUGASSERT(controller == MDMA); + +#warning stm32_mdma_setup not implemented +} + +/**************************************************************************** + * Name: stm32_mdma_start + * + * Description: + * Start the master DMA transfer + ****************************************************************************/ + +static void stm32_mdma_start(DMA_HANDLE handle, dma_callback_t callback, + void *arg, bool half) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + + DEBUGASSERT(controller == MDMA); + +#warning stm32_mdma_start not implemented +} + +/**************************************************************************** + * Name: stm32_mdma_residual + ****************************************************************************/ + +static size_t stm32_mdma_residual(DMA_HANDLE handle) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + uint32_t residual = 0; + + DEBUGASSERT(controller == MDMA); + + /* REVISIT */ + + /* Fetch the count of blocks remaining to be transferred */ + + residual = dmachan_getreg(dmachan, STM32_MDMACH_CBNDTR_OFFSET); + + return (size_t)(residual & MDMA_CBNDTR_BNDT_MASK); +} + +/**************************************************************************** + * Name: stm32_mdma_capable + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_DMACAPABLE +static bool stm32_mdma_capable(FAR stm32_dmacfg_t *cfg) +{ + uint32_t transfer_size; + uint32_t mend; + uint32_t ccr = cfg->cfg1; + uint32_t ctcr = cfg->cfg2; + + dmainfo("0x%08x/%u 0x%08x 0x%08x\n", cfg->maddr, cfg->ndata, ccr, + ctcr); + +#warning stm32_mdma_capable not implemented + + return true; +} +#endif + +/**************************************************************************** + * Name: stm32_mdma_dump + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA_INFO +static void stm32_mdma_dump(DMA_HANDLE handle, const char *msg) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + + DEBUGASSERT(controller == MDMA); + + dmainfo(" CISR: %08x\n", + dmachan_getreg(dmachan, STM32_MDMACH_CISR_OFFSET)); + dmainfo(" CESR: %08x\n", + dmachan_getreg(dmachan, STM32_MDMACH_CESR_OFFSET)); + dmainfo(" CCR: %08x\n", + dmachan_getreg(dmachan, STM32_MDMACH_CCR_OFFSET)); + dmainfo(" CTCR: %08x\n", + dmachan_getreg(dmachan, STM32_MDMACH_CTCR_OFFSET)); + dmainfo(" CBNDTR: %08x\n", + dmachan_getreg(dmachan, STM32_MDMACH_CBNDTR_OFFSET)); + dmainfo(" CSAR: %08x\n", + dmachan_getreg(dmachan, STM32_MDMACH_CSAR_OFFSET)); + dmainfo(" CDAR: %08x\n", + dmachan_getreg(dmachan, STM32_MDMACH_CDAR_OFFSET)); + dmainfo(" CBRUR: %08x\n", + dmachan_getreg(dmachan, STM32_MDMACH_CBRUR_OFFSET)); + dmainfo(" CLAR: %08x\n", + dmachan_getreg(dmachan, STM32_MDMACH_CLAR_OFFSET)); + dmainfo(" CTBR: %08x\n", + dmachan_getreg(dmachan, STM32_MDMACH_CTBR_OFFSET)); + dmainfo(" CMAR: %08x\n", + dmachan_getreg(dmachan, STM32_MDMACH_CMAR_OFFSET)); + dmainfo(" CMDR: %08x\n", + dmachan_getreg(dmachan, STM32_MDMACH_CMDR_OFFSET)); +} +#endif + +#endif /* CONFIG_STM32H7_MDMA */ + +/**************************************************************************** + * Standard DMA functions + ****************************************************************************/ + +#if defined(CONFIG_STM32H7_DMA1) || defined(CONFIG_STM32H7_DMA2) + +/**************************************************************************** + * Name: stm32_sdma_disable + * + * Description: + * Disable standard DMA stream (DMA1/DMA2) + * + ****************************************************************************/ + +static void stm32_sdma_disable(DMA_CHANNEL dmachan) +{ + uint32_t regoffset = 0; + uint32_t regval = 0; + uint8_t stream = 0; + + DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2); + + /* Get DMA stream */ + + stream = dmachan->chan; + + DEBUGASSERT(stream < 8); + + /* Disable all interrupts at the DMA controller */ + + regval = dmachan_getreg(dmachan, STM32_DMA_SCR_OFFSET); + regval &= ~DMA_SCR_ALLINTS; + + /* Disable the DMA stream */ + + regval &= ~DMA_SCR_EN; + dmachan_putreg(dmachan, STM32_DMA_SCR_OFFSET, regval); + + /* Clear pending stream interrupts by setting bits in the upper or lower IFCR + * register + */ + + if (stream < 4) + { + regoffset = STM32_DMA_LIFCR_OFFSET; + } + else + { + regoffset = STM32_DMA_HIFCR_OFFSET; + } + + dmabase_putreg(dmachan, regoffset, (DMA_STREAM_MASK << dmachan->shift)); +} + +/**************************************************************************** + * Name: stm32_sdma_interrupt + * + * Description: + * Standard DMA interrupt handler + * + ****************************************************************************/ + +static int stm32_sdma_interrupt(int irq, void *context, FAR void *arg) +{ + DMA_CHANNEL dmachan = NULL; + uint32_t status = 0; + uint32_t regoffset = 0; + uint8_t stream = 0; + uint8_t controller = 0; + + /* Get the stream and the controller that generated the interrupt */ + +#ifdef CONFIG_STM32H7_DMA1 + if (irq >= STM32_IRQ_DMA1S0 && irq <= STM32_IRQ_DMA1S6) + { + stream = irq - STM32_IRQ_DMA1S0; + controller = DMA1; + } + else if (irq == STM32_IRQ_DMA1S7) + { + stream = 7; + controller = DMA1; + } + else +#endif +#ifdef CONFIG_STM32H7_DMA2 + if (irq >= STM32_IRQ_DMA2S0 && irq <= STM32_IRQ_DMA2S4) + { + stream = irq - STM32_IRQ_DMA2S0; + controller = DMA2; + } + else if (irq >= STM32_IRQ_DMA2S5 && irq <= STM32_IRQ_DMA2S7) + { + stream = irq - STM32_IRQ_DMA2S5 + 5; + controller = DMA2; + } + else +#endif + { + DEBUGPANIC(); + } + + /* Get the channel structure from the stream and controller numbers */ + + dmachan = stm32_dma_channel_get(stream, controller); + + /* Select the interrupt status register (either the LISR or HISR) + * based on the stream number that caused the interrupt. + */ + + if (stream < 4) + { + regoffset = STM32_DMA_LISR_OFFSET; + } + else + { + regoffset = STM32_DMA_HISR_OFFSET; + } + + /* Get the interrupt status for this stream */ + + status = (dmabase_getreg(dmachan, regoffset) >> dmachan->shift) & DMA_STREAM_MASK; + + /* Clear fetched stream interrupts by setting bits in the upper or lower IFCR + * register + */ + + if (stream < 4) + { + regoffset = STM32_DMA_LIFCR_OFFSET; + } + else + { + regoffset = STM32_DMA_HIFCR_OFFSET; + } + + dmabase_putreg(dmachan, regoffset, (status << dmachan->shift)); + + /* Invoke the callback */ + + if (dmachan->callback) + { + dmachan->callback(dmachan, status, dmachan->arg); + } + + return OK; +} + +/**************************************************************************** + * Name: stm32_sdma_setup + * + * Description: + * Configure standard DMA before using + * + ****************************************************************************/ + +static void stm32_sdma_setup(DMA_HANDLE handle, FAR stm32_dmacfg_t *cfg) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint32_t regoffset = 0; + uint32_t regval = 0; + uint8_t stream = 0; + uint32_t scr = cfg->cfg1; + + DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2); + + dmainfo("paddr: %08x maddr: %08x ndata: %d scr: %08x\n", + cfg->paddr, cfg->maddr, cfg->ndata, cfg->cfg1); + +#ifdef CONFIG_STM32H7_DMACAPABLE + DEBUGASSERT(g_dma_ops[controller].dma_capable(cfg)); +#endif + + /* "If the stream is enabled, disable it by resetting the EN bit in the + * DMA_SxCR register, then read this bit in order to confirm that there is no + * ongoing stream operation. Writing this bit to 0 is not immediately + * effective since it is actually written to 0 once all the current transfers + * have finished. When the EN bit is read as 0, this means that the stream is + * ready to be configured. It is therefore necessary to wait for the EN bit + * to be cleared before starting any stream configuration. ..." + */ + + while ((dmachan_getreg(dmachan, STM32_DMA_SCR_OFFSET) & DMA_SCR_EN) != 0); + + /* "... All the stream dedicated bits set in the status register (DMA_LISR + * and DMA_HISR) from the previous data block DMA transfer should be cleared + * before the stream can be re-enabled." + * + * Clear pending stream interrupts by setting bits in the upper or lower IFCR + * register + */ + + stream = dmachan->chan; + + if (stream < 4) + { + regoffset = STM32_DMA_LIFCR_OFFSET; + } + else + { + regoffset = STM32_DMA_HIFCR_OFFSET; + } + + dmabase_putreg(dmachan, regoffset, (DMA_STREAM_MASK << dmachan->shift)); + + /* "Set the peripheral register address in the DMA_SPARx register. The data + * will be moved from/to this address to/from the memory after the + * peripheral event. + */ + + dmachan_putreg(dmachan, STM32_DMA_SPAR_OFFSET, cfg->paddr); + + /* "Set the memory address in the DMA_SM0ARx ... register. The data will be + * written to or read from this memory after the peripheral event." + * + * Note that in double-buffered mode it is explicitly assumed that the second + * buffer immediately follows the first. + */ + + dmachan_putreg(dmachan, STM32_DMA_SM0AR_OFFSET, cfg->maddr); + if (scr & DMA_SCR_DBM) + { + dmachan_putreg(dmachan, STM32_DMA_SM1AR_OFFSET, cfg->maddr + cfg->ndata); + } + + /* "Configure the total number of data items to be transferred in the + * DMA_SNDTRx register. After each peripheral event, this value will be + * decremented." + * + * "When the peripheral flow controller is used for a given stream, the value + * written into the DMA_SxNDTR has no effect on the DMA transfer. Actually, + * whatever the value written, it will be forced by hardware to 0xFFFF as soon + * as the stream is enabled..." + */ + + dmachan_putreg(dmachan, STM32_DMA_SNDTR_OFFSET, cfg->ndata); + + /* "Configure the stream priority using the PL[1:0] bits in the DMA_SCRx" + * register." + */ + + regval = dmachan_getreg(dmachan, STM32_DMA_SCR_OFFSET); + regval &= ~(DMA_SCR_PL_MASK); + regval |= scr & DMA_SCR_PL_MASK; + dmachan_putreg(dmachan, STM32_DMA_SCR_OFFSET, regval); + + /* "Configure the FIFO usage (enable or disable, threshold in transmission and + * reception)" + * + * "Caution is required when choosing the FIFO threshold (bits FTH[1:0] of the + * DMA_SxFCR register) and the size of the memory burst (MBURST[1:0] of the + * DMA_SxCR register): The content pointed by the FIFO threshold must exactly + * match to an integer number of memory burst transfers. If this is not in the + * case, a FIFO error (flag FEIFx of the DMA_HISR or DMA_LISR register) will be + * generated when the stream is enabled, then the stream will be automatically + * disabled." + * + * The FIFO is disabled in circular mode when transferring data from a + * peripheral to memory, as in this case it is usually desirable to know that + * every byte from the peripheral is transferred immediately to memory. It is + * not practical to flush the DMA FIFO, as this requires disabling the channel + * which triggers the transfer-complete interrupt. + * + * NOTE: The FEIFx error interrupt is not enabled because the FEIFx seems to + * be reported spuriously causing good transfers to be marked as failures. + */ + + regval = dmachan_getreg(dmachan, STM32_DMA_SFCR_OFFSET); + regval &= ~(DMA_SFCR_FTH_MASK | DMA_SFCR_FS_MASK | DMA_SFCR_FEIE); + if (!((scr & (DMA_SCR_CIRC | DMA_SCR_DIR_MASK)) == (DMA_SCR_CIRC | DMA_SCR_DIR_P2M))) + { + regval |= (DMA_SFCR_FTH_FULL | DMA_SFCR_DMDIS); + } + + dmachan_putreg(dmachan, STM32_DMA_SFCR_OFFSET, regval); + + /* "Configure data transfer direction, circular mode, peripheral & memory + * incremented mode, peripheral & memory data size, and interrupt after + * half and/or full transfer in the DMA_CCRx register." + * + * Note: The CT bit is always reset. + */ + + regval = dmachan_getreg(dmachan, STM32_DMA_SCR_OFFSET); + regval &= ~(DMA_SCR_PFCTRL | DMA_SCR_DIR_MASK | DMA_SCR_PINC | DMA_SCR_MINC | + DMA_SCR_PSIZE_MASK | DMA_SCR_MSIZE_MASK | DMA_SCR_PINCOS | + DMA_SCR_CIRC | DMA_SCR_DBM | DMA_SCR_CT | + DMA_SCR_PBURST_MASK | DMA_SCR_MBURST_MASK); + scr &= (DMA_SCR_PFCTRL | DMA_SCR_DIR_MASK | DMA_SCR_PINC | DMA_SCR_MINC | + DMA_SCR_PSIZE_MASK | DMA_SCR_MSIZE_MASK | DMA_SCR_PINCOS | + DMA_SCR_DBM | DMA_SCR_CIRC | + DMA_SCR_PBURST_MASK | DMA_SCR_MBURST_MASK); + regval |= scr; + dmachan_putreg(dmachan, STM32_DMA_SCR_OFFSET, regval); +} + +/**************************************************************************** + * Name: stm32_sdma_start + * + * Description: + * Start the standard DMA transfer + ****************************************************************************/ + +static void stm32_sdma_start(DMA_HANDLE handle, dma_callback_t callback, + void *arg, bool half) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint32_t scr = 0; + + DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2); + DEBUGASSERT(handle != NULL); + + /* Save the callback info. This will be invoked when the DMA completes */ + + dmachan->callback = callback; + dmachan->arg = arg; + + /* Activate the stream by setting the ENABLE bit in the DMA_SCRx register. + * As soon as the stream is enabled, it can serve any DMA request from the + * peripheral connected on the stream. + */ + + scr = dmachan_getreg(dmachan, STM32_DMA_SCR_OFFSET); + scr |= DMA_SCR_EN; + + /* In normal mode, interrupt at either half or full completion. In circular + * and double-buffered modes, always interrupt on buffer wrap, and optionally + * interrupt at the halfway point. + */ + + if ((scr & (DMA_SCR_DBM | DMA_SCR_CIRC)) == 0) + { + /* Once half of the bytes are transferred, the half-transfer flag (HTIF) is + * set and an interrupt is generated if the Half-Transfer Interrupt Enable + * bit (HTIE) is set. At the end of the transfer, the Transfer Complete Flag + * (TCIF) is set and an interrupt is generated if the Transfer Complete + * Interrupt Enable bit (TCIE) is set. + */ + + scr |= (half ? (DMA_SCR_HTIE | DMA_SCR_TEIE) : (DMA_SCR_TCIE | DMA_SCR_TEIE)); + } + else + { + /* In non-stop modes, when the transfer completes it immediately resets + * and starts again. The transfer-complete interrupt is thus always + * enabled, and the half-complete interrupt can be used in circular + * mode to determine when the buffer is half-full, or in double-buffered + * mode to determine when one of the two buffers is full. + */ + + scr |= (half ? DMA_SCR_HTIE : 0) | DMA_SCR_TCIE | DMA_SCR_TEIE; + } + + dmachan_putreg(dmachan, STM32_DMA_SCR_OFFSET, scr); + + stm32_dmadump(handle, "DMA after start"); +} + +/**************************************************************************** + * Name: stm32_sdma_residual + ****************************************************************************/ + +static size_t stm32_sdma_residual(DMA_HANDLE handle) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint32_t residual = 0; + + DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2); + + /* Fetch the count of bytes remaining to be transferred. + * + * If the FIFO is enabled, this count may be inaccurate. ST don't + * appear to document whether this counts the peripheral or the memory + * side of the channel, and they don't make the memory pointer + * available either. + * + * For reception in circular mode the FIFO is disabled in order that + * this value can be useful. + */ + + residual = dmachan_getreg(dmachan, STM32_DMA_SNDTR_OFFSET); + + return (size_t)(residual & DMA_SNDTR_NDT_MASK); +} + +/**************************************************************************** + * Name: stm32_sdma_capable + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_DMACAPABLE +static bool stm32_sdma_capable(FAR stm32_dmacfg_t *cfg) +{ + uint32_t transfer_size; + uint32_t burst_length; + uint32_t mend; + uint32_t ccr = cfg->cfg1; + + dmainfo("0x%08x/%u 0x%08x\n", cfg->maddr, cfg->ndata, cfg->cfg1); + + /* Verify that the address conforms to the memory transfer size. + * Transfers to/from memory performed by the DMA controller are + * required to be aligned to their size. + * + * See ST RM0410 DocID028270 Rev 2, section 8.3.11 Single and burst + * transfers + * + * Compute mend inline to avoid a possible non-constant integer + * multiply. + */ + + switch (ccr & DMA_SCR_MSIZE_MASK) + { + case DMA_SCR_MSIZE_8BITS: + { + transfer_size = 1; + mend = cfg->maddr + cfg->ndata - 1; + break; + } + + case DMA_SCR_MSIZE_16BITS: + { + transfer_size = 2; + mend = cfg->maddr + (cfg->ndata << 1) - 1; + break; + } + + case DMA_SCR_MSIZE_32BITS: + { + transfer_size = 4; + mend = cfg->maddr + (cfg->ndata << 2) - 1; + break; + } + + default: + { + dmainfo("stm32_dmacapable: bad transfer size in CCR\n"); + return false; + } + } + + if ((cfg->maddr & (transfer_size - 1)) != 0) + { + dmainfo("stm32_dmacapable: transfer unaligned\n"); + return false; + } + +# if defined(CONFIG_ARMV7M_DCACHE) && !defined(CONFIG_ARMV7M_DCACHE_WRITETHROUGH) + /* buffer alignment is required for DMA transfers with dcache in buffered + * mode (not write-through) because a) arch_invalidate_dcache could lose + * buffered writes and b) arch_flush_dcache could corrupt adjacent memory if + * the maddr and the mend+1, the next next address are not on + * ARMV7M_DCACHE_LINESIZE boundaries. + */ + + if ((cfg->maddr & (ARMV7M_DCACHE_LINESIZE-1)) != 0 || + ((mend + 1) & (ARMV7M_DCACHE_LINESIZE-1)) != 0) + { + dmainfo("stm32_dmacapable: dcache unaligned maddr:0x%08x mend:0x%08x\n", + cfg->maddr, mend); + return false; + } +# endif + + /* Verify that burst transfers do not cross a 1KiB boundary. */ + + if ((cfg->maddr / 1024) != (mend / 1024)) + { + /* The transfer as a whole crosses a 1KiB boundary. + * Verify that no burst does by asserting that the address + * is aligned to the burst length. + */ + + switch (ccr & DMA_SCR_MBURST_MASK) + { + case DMA_SCR_MBURST_SINGLE: + { + burst_length = transfer_size; + break; + } + + case DMA_SCR_MBURST_INCR4: + { + burst_length = transfer_size << 2; + break; + } + + case DMA_SCR_MBURST_INCR8: + { + burst_length = transfer_size << 3; + break; + } + + case DMA_SCR_MBURST_INCR16: + { + burst_length = transfer_size << 4; + break; + } + + default: + { + dmainfo("stm32_dmacapable: bad burst size in CCR\n"); + return false; + } + } + + if ((cfg->maddr & (burst_length - 1)) != 0) + { + dmainfo("stm32_dmacapable: burst crosses 1KiB\n"); + return false; + } + } + + /* Verify that transfer is froma a supported memory region */ + + if (cfg->paddr & STM32_PREGION_MASK != STM32_D2_BASE) + { + /* DMA1/DMA2 support only D2 domain */ + + dmainfo("transfer from unknown/unsupported region\n"); + return false; + } + + /* Verify that the transfer is to a memory region that supports DMA. */ + + if ((cfg->maddr & STM32_REGION_MASK) != (mend & STM32_REGION_MASK)) + { + dmainfo("stm32_dmacapable: transfer crosses memory region\n"); + return false; + } + + switch (cfg->maddr & STM32_REGION_MASK) + { + case STM32_FMC_BANK1: + case STM32_FMC_BANK2: + case STM32_FMC_BANK3: + case STM32_FMC_BANK4: + case STM32_FMC_BANK5: + case STM32_FMC_BANK6: + { + /* All RAM and FMC is supported */ + + break; + } + + case STM32_SRAM_BASE: + { + /* DTCM not supported for standard DMA (DMA1/DMA2) */ + + if (cfg->maddr >= STM32_DTCRAM_BASE + && (cfg->maddr - STM32_DTCRAM_BASE) < 0x1ffff) + { + dmainfo("transfer targets DTCRAM\n"); + return false; + } + break; + } + + case STM32_CODE_BASE: + { + /* ITCM not supported for standard DMA (DMA1/DMA2) */ + + if (cfg->maddr >= STM32_ITCM_BASE + && (cfg->maddr - STM32_ITCM_BASE) < 0xffff) + { + dmainfo("transfer targets ITCM RAM\n"); + return false; + } + break; + } + + default: + { + /* Everything else is unsupported by DMA */ + + dmainfo("transfer targets unknown/unsupported region\n"); + return false; + } + } + + dmainfo("transfer OK\n"); + return true; +} +#endif + +/**************************************************************************** + * Name: stm32_sdma_dump + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA_INFO +static void stm32_sdma_dump(DMA_HANDLE handle, const char *msg) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + + DEBUGASSERT(dmachan->ctrl == DMA1 || dmachan->ctrl == DMA2); + + dmainfo(" LISR: %08x\n", + dmabase_getreg(dmachan, STM32_DMA_LISR_OFFSET)); + dmainfo(" HISR: %08x\n", + dmabase_getreg(dmachan, STM32_DMA_HISR_OFFSET)); + dmainfo(" SCR: %08x\n", + dmachan_getreg(dmachan, STM32_DMA_SCR_OFFSET)); + dmainfo(" SNDTR: %08x\n", + dmachan_getreg(dmachan, STM32_DMA_SNDTR_OFFSET)); + dmainfo(" SPAR: %08x\n", + dmachan_getreg(dmachan, STM32_DMA_SPAR_OFFSET)); + dmainfo(" SM0AR: %08x\n", + dmachan_getreg(dmachan, STM32_DMA_SM0AR_OFFSET)); + dmainfo(" SM1AR: %08x\n", + dmachan_getreg(dmachan, STM32_DMA_SM1AR_OFFSET)); + dmainfo(" SFCR: %08x\n", + dmachan_getreg(dmachan, STM32_DMA_SFCR_OFFSET)); + + stm32_dmamux_dump(g_dma[dmachan->ctrl].dmamux, + dmachan->chan + g_dma[dmachan->ctrl].dmamux_offset); +} +#endif + +#endif /* CONFIG_STM32H7_DMA1 || CONFIG_STM32H7_DMA2 */ + +/**************************************************************************** + * Basic DMA functions + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_BDMA + +/**************************************************************************** + * Name: stm32_bdma_channel_disable + * + * Description: + * Disable the Basic DMA + * + ****************************************************************************/ + +static void stm32_bdma_disable(DMA_CHANNEL dmachan) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + + DEBUGASSERT(controller == BDMA); + +#warning stm32_bdma_disable not implemented +} + +/**************************************************************************** + * Name: stm32_bdma_interrupt + * + * Description: + * Basic DMA interrupt handler + * + ****************************************************************************/ + +static int stm32_bdma_interrupt(int irq, void *context, FAR void *arg) +{ +#warning stm32_bdma_interrupt not implemented +} + +/**************************************************************************** + * Name: stm32_bdma_setup + * + * Description: + * Configure basic DMA before using + * + ****************************************************************************/ + +static void stm32_bdma_setup(DMA_HANDLE handle, FAR stm32_dmacfg_t *cfg) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + + DEBUGASSERT(controller == BDMA); + +#warning stm32_bdma_setup not implemented +} + +/**************************************************************************** + * Name: stm32_bdma_start + * + * Description: + * Start the basic DMA transfer + * + ****************************************************************************/ + +static void stm32_bdma_start(DMA_HANDLE handle, dma_callback_t callback, + void *arg, bool half) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + + DEBUGASSERT(controller == BDMA); + +#warning stm32_bdma_start not implemented +} + +/**************************************************************************** + * Name: stm32_bdma_residual + ****************************************************************************/ + +static size_t stm32_bdma_residual(DMA_HANDLE handle) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + uint32_t residual = 0 + + DEBUGASSERT(controller == BDMA); + + /* Fetch the count of bytes remaining to be transferred */ + + residual = dmachan_getreg(dmachan, STM32_BDMACH_CNDTR_OFFSET); + + return (size_t)(residual & BDMA_CNDTR_NDT_MASK); +} + +/**************************************************************************** + * Name: stm32_bdma_capable + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_DMACAPABLE +static bool stm32_bdma_capable(FAR stm32_dmacfg_t *cfg) +{ + uint32_t transfer_size; + uint32_t mend; + uint32_t ccr = cfg->cfg1; + + dmainfo("0x%08x/%u 0x%08x\n", cfg->maddr, cfg->ndata, cfg->cfg1); + +#warning REVISIT + + /* Verify that the address conforms to the memory transfer size. + * Transfers to/from memory performed by the DMA controller are + * required to be aligned to their size. + * + * See ST RM0090 rev4, section 9.3.11 + * + * Compute mend inline to avoid a possible non-constant integer + * multiply. + */ + + switch (ccr & BDMA_CCR_MSIZE_MASK) + { + case BDMA_CCR_MSIZE_8BITS: + { + transfer_size = 1; + mend = maddr + count - 1; + break; + } + + case BDMA_CCR_MSIZE_16BITS: + { + transfer_size = 2; + mend = maddr + (count << 1) - 1; + break; + } + + case BDMA_CCR_MSIZE_32BITS: + { + transfer_size = 4; + mend = maddr + (count << 2) - 1; + break; + } + + default: + { + return false; + } + } + + if ((maddr & (transfer_size - 1)) != 0) + { + return false; + } + +# if defined(CONFIG_ARMV7M_DCACHE) && !defined(CONFIG_ARMV7M_DCACHE_WRITETHROUGH) + /* buffer alignment is required for DMA transfers with dcache in buffered + * mode (not write-through) because a) arch_invalidate_dcache could lose + * buffered writes and b) arch_flush_dcache could corrupt adjacent memory if + * the maddr and the mend+1, the next next address are not on + * ARMV7M_DCACHE_LINESIZE boundaries. + */ + + if ((cfg->maddr & (ARMV7M_DCACHE_LINESIZE-1)) != 0 || + ((mend + 1) & (ARMV7M_DCACHE_LINESIZE-1)) != 0) + { + dmainfo("stm32_dmacapable: dcache unaligned maddr:0x%08x mend:0x%08x\n", + cfg->maddr, mend); + return false; + } +# endif + + /* Verify that transfer is froma a supported memory region */ + + if (cfg->paddr & STM32_PREGION_MASK != STM32_D3_BASE) + { + /* BDMA support only D3 domain */ + + dmainfo("transfer from unknown/unsupported region\n"); + return false; + } + + /* Verify that the transfer is to a memory region that supports DMA. */ + + if ((maddr & STM32_REGION_MASK) != (mend & STM32_REGION_MASK)) + { + return false; + } + + switch (maddr & STM32_REGION_MASK) + { + case STM32_SRAM4_BASE: + { + /* BDMA transfers are limited to D3 domain resources. + * NOTE: STM32_SRAM4_BASE region mask cover STM32_BBRAM_BASE mask + */ + + return true; + } + + default: + { + /* Everything else is unsupported by DMA */ + + return false; + } + } + + dmainfo("transfer OK\n"); + return true; +} +#endif + +/**************************************************************************** + * Name: stm32_bdma_dump + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA_INFO +static void stm32_bdma_dump(DMA_HANDLE handle, const char *msg) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + + DEBUGASSERT(controller == BDMA); + + dmainfo(" ISR: %08x\n", + dmabase_getreg(dmachan, STM32_BDMA_ISR_OFFSET)); + dmainfo(" CCR: %08x\n", + dmachan_getreg(dmachan, STM32_BDMACH_CCR_OFFSET)); + dmainfo(" CNDTR: %08x\n", + dmachan_getreg(dmachan, STM32_BDMACH_CNDTR_OFFSET)); + dmainfo(" CPAR: %08x\n", + dmachan_getreg(dmachan, STM32_BDMACH_CPAR_OFFSET)); + dmainfo(" CM0AR: %08x\n", + dmachan_getreg(dmachan, STM32_BDMACH_CM0AR_OFFSET)); + dmainfo(" CM1AR: %08x\n", + dmachan_getreg(dmachan, STM32_BDMACH_CM1AR_OFFSET)); + + stm32_dmamux_dump(g_dma[dmachan->ctrl].dmamux, controller); +} +#endif + +#endif /* CONFIG_STM32H7_BDMA */ + +/**************************************************************************** + * Name: stm32_dmamux_dump + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA_INFO +static void stm32_dmamux_dump(DMA_MUX dmamux, uint8_t chan) +{ + dmainfo("DMAMUX%d CH=%d\n", dmamux->id, chan); + dmainfo(" CCR: %08x\n", + dmamux_getreg(dmamux, STM32_DMAMUX_CXCR_OFFSET(chan))); + dmainfo(" CSR: %08x\n", + dmamux_getreg(dmamux, STM32_DMAMUX_CSR_OFFSET)); + dmainfo(" RG0CR: %08x\n", + dmamux_getreg(dmamux, STM32_DMAMUX_RG0CR_OFFSET)); + dmainfo(" RG1CR: %08x\n", + dmamux_getreg(dmamux, STM32_DMAMUX_RG1CR_OFFSET)); + dmainfo(" RG2CR: %08x\n", + dmamux_getreg(dmamux, STM32_DMAMUX_RG2CR_OFFSET)); + dmainfo(" RG3CR: %08x\n", + dmamux_getreg(dmamux, STM32_DMAMUX_RG3CR_OFFSET)); + dmainfo(" RGSR: %08x\n", + dmamux_getreg(dmamux, STM32_DMAMUX_RGSR_OFFSET)); + dmainfo(" RGCFR: %08x\n", + dmamux_getreg(dmamux, STM32_DMAMUX_RGCFR_OFFSET)); +}; +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_dmainitialize + * + * Description: + * Initialize the DMA subsystem (DMA1, DMA2, MDMA and BDMA) + * + * Returned Value: + * None + * + ****************************************************************************/ + +void weak_function up_dma_initialize(void) +{ + DMA_CHANNEL dmachan = NULL; + uint8_t controller = 0; + int channel = 0; + + dmainfo("Initialize DMA\n"); + + /* Initialize DMA channels */ + + for (channel = 0; channel < DMA_NCHANNELS; channel++) + { + dmachan = &g_dmach[channel]; + + /* Initialize flag */ + + dmachan->used = false; + + /* Get DMA controller associated with channel */ + + controller = dmachan->ctrl; + + DEBUGASSERT(controller >= MDMA && controller <= BDMA); + + /* Attach standard DMA interrupt vectors */ + + (void)irq_attach(dmachan->irq, g_dma_ops[controller].dma_interrupt, dmachan); + + /* Disable the DMA stream */ + + g_dma_ops[controller].dma_disable(dmachan); + + /* Enable the IRQ at the NVIC (still disabled at the DMA controller) */ + + up_enable_irq(dmachan->irq); + + /* Set the interrupt priority + * TODO: refactor + */ + +#ifdef CONFIG_ARCH_IRQPRIO + switch (controller) + { +#if defined(CONFIG_STM32H7_DMA1) || defined(CONFIG_STM32H7_DMA2) + case DMA1: + case DMA2: + { + up_prioritize_irq(dmachan->irq, CONFIG_DMA_PRI); + break; + } +#endif /* CONFIG_STM32H7_DMA1 && CONFIG_STM32H7_DMA2 */ + +#ifdef CONFIG_STM32H7_MDMA + case MDMA: + { + up_prioritize_irq(dmachan->irq, CONFIG_MDMA_PRI); + break; + } +#endif /* CONFIG_STM32H7_MDMA */ + +#ifdef CONFIG_STM32H7_BDMA + case BDMA: + { + up_prioritize_irq(dmachan->irq, CONFIG_BDMA_PRI); + break; + } +#endif /* CONFIG_STM32H7_BDMA */ + + default: + { + ASSERT(0); + break; + } + } +#endif + } +} + +/**************************************************************************** + * Name: stm32_dmachannel + * + * Description: + * Allocate a DMA channel. This function gives the caller mutually + * exclusive access to the DMA channel specified by the 'dmamap' argument. + * It is common for standard DMA (DMA1, DMA2), master DMA (MDMA) and + * basic DMA (BDMA) controllers. + * + * Input Parameters: + * dmamap - Identifies the stream/channel resource. For the STM32 H7, this + * is a bit-encoded value as provided by the DMAMAP_* definitions + * in chip/stm32h7xxxxxxx_dmamux.h + * + * Returned Value: + * One success, this function returns a non-NULL, void* DMA channel + * handle. NULL is returned on any failure. This function can fail only + * if no DMA channel is available. + * + * Assumptions: + * - The caller does not hold he DMA channel. + * - The caller can wait for the DMA channel to be freed if it is no + * available. + * + ****************************************************************************/ + +DMA_HANDLE stm32_dmachannel(unsigned int dmamap) +{ + DMA_CHANNEL dmachan = NULL; + DMA_MUX dmamux = NULL; + irqstate_t flags; + uint8_t controller = 0; + uint8_t dmamux_req = 0; + uint32_t regval = 0; + uint8_t first = 0; + uint8_t nchan = 0; + int item = -1; + int i = 0; + + /* Get DMA controller from encoded DMAMAP value */ + + controller = DMAMAP_CONTROLLER(dmamap); + DEBUGASSERT(controller >= MDMA && controller <= BDMA); + + /* Get DMAMUX channel from encoded DMAMAP value */ + + dmamux_req = DMAMAP_REQUEST(dmamap); + + /* Get g_dma array limits for given controller */ + + stm32_gdma_limits_get(controller, &first, &nchan); + + /* Find available channel for given controller */ + + flags = enter_critical_section(); + for (i = first; i < first+nchan; i+=1) + { + if (g_dmach[i].used == false) + { + item = i; + g_dmach[i].used = true; + break; + } + } + leave_critical_section(flags); + + dmainfo("ctrl=%d item=%d\n", controller, item); + + if (item == -1) + { + dmainfo("No available DMA chan for CTRL=%d\n", + controller); + + /* No available channel */ + + goto errout; + } + + /* Assign DMA item */ + + dmachan = &g_dmach[item]; + + dmainfo("Get g_dmach[%d] CTRL=%d CH=%d\n", i, controller, dmachan->chan); + + /* Be sure that we have proper DMA controller */ + + DEBUGASSERT(dmachan->ctrl == controller); + + /* Get DMAMUX associated with DMA controller */ + + dmamux = g_dma[controller].dmamux; + + /* No DMAMUX for Master DMA */ + + if (dmamux != NULL) + { + uint8_t dmamux_chan = dmachan->chan + g_dma[controller].dmamux_offset; + + dmainfo("Get DMAMUX%d CH %d\n", dmamux->id, dmamux_chan); + + /* DMAMUX Set DMA channel source */ + + regval = dmamux_req << DMAMUX_CCR_DMAREQID_SHIFT; + dmamux_putreg(dmamux, STM32_DMAMUX_CXCR_OFFSET(dmamux_chan), regval); + + /* DMAMUX Set RGCR register */ + + regval = 0; + dmamux_putreg(dmamux, STM32_DMAMUX_RGXCR_OFFSET(dmamux_chan), regval); + } + +errout: + return (DMA_HANDLE)dmachan; +} + +/**************************************************************************** + * Name: stm32_dmafree + * + * Description: + * Release a DMA channel and unmap DMAMUX if required. + * + * NOTE: The 'handle' used in this argument must NEVER be used again + * until stm32_dmachannel() is called again to re-gain access to the channel. + * + * Returned Value: + * None + * + * Assumptions: + * - The caller holds the DMA channel. + * - There is no DMA in progress + * + ****************************************************************************/ + +void stm32_dmafree(DMA_HANDLE handle) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + DMA_MUX dmamux = NULL; + irqstate_t flags; + + DEBUGASSERT(handle != NULL); + + /* Get DMA controller */ + + controller = dmachan->ctrl; + DEBUGASSERT(controller >= MDMA && controller <= BDMA); + + /* Get DMAMUX associated with DMA controller */ + + dmamux = g_dma[controller].dmamux; + + /* No DMAMUX for Master DMA */ + + if (dmamux != NULL) + { + uint8_t dmamux_chan = dmachan->chan + g_dma[controller].dmamux_offset; + + dmainfo("Free DMAMUX%d CH %d\n", dmamux->id, dmamux_chan); + + /* Clear DMAMUX CCR register associated with channel */ + + dmamux_putreg(dmamux, STM32_DMAMUX_CXCR_OFFSET(dmamux_chan), 0); + + /* Clear DMAMUX RGCR register associated with channel */ + + dmamux_putreg(dmamux, STM32_DMAMUX_RGXCR_OFFSET(dmamux_chan), 0); + } + + /* Release the channel */ + + flags = enter_critical_section(); + dmachan->used = false; + leave_critical_section(flags); + + dmainfo("Unmapping DMAMUX(%d)\n", dmachan->chan); +} + +/**************************************************************************** + * Name: stm32_dmasetup + * + * Description: + * Configure DMA before using + * + ****************************************************************************/ + +void stm32_dmasetup(DMA_HANDLE handle, FAR stm32_dmacfg_t *cfg) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + + /* Get DMA controller */ + + DEBUGASSERT(controller >= MDMA && controller <= BDMA); + + g_dma_ops[controller].dma_setup(handle, cfg); +} + +/**************************************************************************** + * Name: stm32_dmastart + * + * Description: + * Start the DMA transfer + * + * Assumptions: + * - DMA handle allocated by stm32_dmachannel() + * - No DMA in progress + * + ****************************************************************************/ + +void stm32_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg, + bool half) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + + DEBUGASSERT(controller >= MDMA && controller <= BDMA); + + g_dma_ops[controller].dma_start(handle, callback, arg, half); +} + +/**************************************************************************** + * Name: stm32_dmastop + * + * Description: + * Cancel the DMA. After stm32_dmastop() is called, the DMA channel is + * reset and stm32_dmasetup() must be called before stm32_dmastart() can be + * called again + * + * Assumptions: + * - DMA handle allocated by stm32_dmachannel() + * + ****************************************************************************/ + +void stm32_dmastop(DMA_HANDLE handle) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + + DEBUGASSERT(controller >= MDMA && controller <= BDMA); + + g_dma_ops[controller].dma_disable(dmachan); +} + +/**************************************************************************** + * Name: stm32_dmaresidual + * + * Description: + * Read the DMA bytes-remaining register. + * + * Assumptions: + * - DMA handle allocated by stm32_dmachannel() + * + ****************************************************************************/ + +size_t stm32_dmaresidual(DMA_HANDLE handle) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + + DEBUGASSERT(controller >= MDMA && controller <= BDMA); + + return g_dma_ops[controller].dma_residual(handle); +} + +/**************************************************************************** + * Name: stm32_dmacapable + * + * Description: + * Check if the DMA controller can transfer data to/from given memory + * address. This depends on the internal connections in the ARM bus matrix + * of the processor. Note that this only applies to memory addresses, it + * will return false for any peripheral address. + * + * Input Parameters: + * cfg - DMA transfer configuration + * + * Returned Value: + * True, if transfer is possible. + * + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_DMACAPABLE +bool stm32_dmacapable(DMA_HANDLE handle, FAR stm32_dmacfg_t *cfg) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + + DEBUGASSERT(controller >= MDMA && controller <= BDMA); + + return g_dma_ops[controller].dma_capable(cfg); +} +#endif + +/**************************************************************************** + * Name: stm32_dmadump + * + * Description: + * Dump previously sampled DMA register contents + * + * Assumptions: + * - DMA handle allocated by stm32_dmachannel() + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA_INFO +void stm32_dmadump(DMA_HANDLE handle, const char *msg) +{ + DMA_CHANNEL dmachan = (DMA_CHANNEL)handle; + uint8_t controller = dmachan->ctrl; + + DEBUGASSERT(controller >= MDMA && controller <= BDMA); + + dmainfo("DMA %d CH%d Registers: %s\n", dmachan->ctrl, dmachan->ctrl, msg); + + g_dma_ops[controller].dma_dump(handle, msg); +} +#endif diff --git a/arch/arm/src/stm32h7/stm32_dma.h b/arch/arm/src/stm32h7/stm32_dma.h new file mode 100644 index 0000000000..f6376461da --- /dev/null +++ b/arch/arm/src/stm32h7/stm32_dma.h @@ -0,0 +1,231 @@ +/************************************************************************************ + * arch/arm/src/stm32h7/stm32_dma.h + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32H7_STM32_DMA_H +#define __ARCH_ARM_SRC_STM32H7_STM32_DMA_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include +#include + +#include "chip/stm32_dma.h" +#include "chip/stm32_dmamux.h" + +/* These definitions provide the bit encoding of the 'status' parameter passed to the + * DMA callback function (see dma_callback_t). + */ + +#define DMA_STATUS_FEIF 0 /* Stream FIFO error (ignored) */ +#define DMA_STATUS_DMEIF DMA_STREAM_DMEIF_BIT /* Stream direct mode error */ +#define DMA_STATUS_TEIF DMA_STREAM_TEIF_BIT /* Stream Transfer Error */ +#define DMA_STATUS_HTIF DMA_STREAM_HTIF_BIT /* Stream Half Transfer */ +#define DMA_STATUS_TCIF DMA_STREAM_TCIF_BIT /* Stream Transfer Complete */ + +#define DMA_STATUS_ERROR (DMA_STATUS_FEIF|DMA_STATUS_DMEIF|DMA_STATUS_TEIF) +#define DMA_STATUS_SUCCESS (DMA_STATUS_TCIF|DMA_STATUS_HTIF) + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +/* DMA channel configuration - common for DMA1 DMA2 MDMA and BDMA */ + +struct stm32_dma_config_s +{ + uint32_t paddr; /* Peripheral address */ + uint32_t maddr; /* Memory address */ + uint32_t cfg1; /* DMA transfer configuration 1. + * Its function depends on DMA controller: + * - DMA1 and DMA2 - SCR register configuration + * - BDMA - CCR register configuration + * - MDMA - CCR register configuration + */ + uint32_t cfg2; /* DMA transfer configuration 2. + * Its function depends on DMA controller: + * - MDMA - CTCR register configuration + */ + uint32_t ndata; /* Number of data to transfer */ +}; + +typedef struct stm32_dma_config_s stm32_dmacfg_t; + +/* DMA_HANDLE Provides an opaque are reference that can be used to represent a DMA + * a DMA stream. + */ + +typedef FAR void *DMA_HANDLE; + +/* Description: + * This is the type of the callback that is used to inform the user of the + * completion of the DMA. NOTE: The DMA module does *NOT* perform any cache + * operations. It is the responsibility of the DMA client to invalidate DMA + * buffers after completion of the DMA RX operations. + * + * Input Parameters: + * handle - Refers tot he DMA channel or stream + * status - A bit encoded value that provides the completion status. See the + * DMASTATUS_* definitions above. + * arg - A user-provided value that was provided when stm32_dmastart() was + * called. + */ + +typedef void (*dma_callback_t)(DMA_HANDLE handle, uint8_t status, void *arg); + +/************************************************************************************ + * Public Data + ************************************************************************************/ + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/************************************************************************************ + * Public Functions + ************************************************************************************/ + +/**************************************************************************** + * Name: stm32_dmachannel + * + * Description: + * Allocate a DMA channel. This function gives the caller mutually + * exclusive access to the DMA channel specified by the 'dmamap' argument. + * It is common for standard DMA (DMA1, DMA2), master DMA (MDMA) and + * basic DMA (BDMA) controllers. + * + * Input Parameters: + * dmamap - Identifies the stream/channel resource. For the STM32 H7, this + * is a bit-encoded value as provided by the DMAMAP_* definitions + * in chip/stm32h7xxxxxxx_dmamux.h + * + * Returned Value: + * One success, this function returns a non-NULL, void* DMA channel + * handle. NULL is returned on any failure. This function can fail only + * if no DMA channel is available. + * + * Assumptions: + * - The caller does not hold he DMA channel. + * - The caller can wait for the DMA channel to be freed if it is no + * available. + * + ****************************************************************************/ + +DMA_HANDLE stm32_dmachannel(unsigned int dmamap); + +/**************************************************************************** + * Name: stm32_dmafree + * + * Description: + * Release a DMA channel and unmap DMAMUX if required. + * + * NOTE: The 'handle' used in this argument must NEVER be used again + * until stm32_dmachannel() is called again to re-gain access to the channel. + * + * Returned Value: + * None + * + * Assumptions: + * - The caller holds the DMA channel. + * - There is no DMA in progress + * + ****************************************************************************/ + +void stm32_dmafree(DMA_HANDLE handle); + +/**************************************************************************** + * Name: stm32_dmasetup + * + * Description: + * Configure DMA before using + * + ****************************************************************************/ + +void stm32_dmasetup(DMA_HANDLE handle, stm32_dmacfg_t *cfg); + +/**************************************************************************** + * Name: stm32_dmastart + ****************************************************************************/ + +void stm32_dmastart(DMA_HANDLE handle, dma_callback_t callback, void *arg, + bool half); + +/**************************************************************************** + * Name: stm32_dmastop + ****************************************************************************/ + +void stm32_dmastop(DMA_HANDLE handle); + +/**************************************************************************** + * Name: stm32_dmaresidual + ****************************************************************************/ + +size_t stm32_dmaresidual(DMA_HANDLE handle); + +/**************************************************************************** + * Name: stm32_dmacapable + ****************************************************************************/ + +#ifdef CONFIG_STM32H7_DMACAPABLE +bool stm32_dmacapable(DMA_HANDLE handle, stm32_dmacfg_t *cfg); +#else +# define stm32_dmacapable(handle, cfg) (true) +#endif + +/**************************************************************************** + * Name: stm32_dmadump + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA_INFO +void stm32_dmadump(DMA_HANDLE handle, const char *msg); +#else +# define stm32_dmadump(handle,msg) +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_STM32H7_STM32_DMA_H */ diff --git a/arch/arm/src/stm32h7/stm32_spi.c b/arch/arm/src/stm32h7/stm32_spi.c index 413933ee19..4a4b4c9455 100644 --- a/arch/arm/src/stm32h7/stm32_spi.c +++ b/arch/arm/src/stm32h7/stm32_spi.c @@ -88,6 +88,7 @@ #include "stm32_rcc.h" #include "stm32_gpio.h" #include "stm32_spi.h" +#include "stm32_dma.h" #if defined(CONFIG_STM32H7_SPI1) || defined(CONFIG_STM32H7_SPI2) || \ defined(CONFIG_STM32H7_SPI3) || defined(CONFIG_STM32H7_SPI4) || \ @@ -104,10 +105,6 @@ # error "Interrupt driven SPI not yet supported" #endif -#ifdef CONFIG_STM32H7_SPI_DMA -# error "SPI DMA not supported yet" -#endif - /* Can't have both interrupt driven SPI and SPI DMA */ #if defined(CONFIG_STM32H7_SPI_INTERRUPTS) && defined(CONFIG_STM32H7_SPI_DMA) @@ -200,8 +197,8 @@ struct stm32_spidev_s bool defertrig; /* Flag indicating that trigger should be deferred */ bool trigarmed; /* Flag indicating that the trigger is armed */ #endif - uint8_t rxch; /* The RX DMA channel number */ - uint8_t txch; /* The TX DMA channel number */ + uint32_t rxch; /* The RX DMA channel number */ + uint32_t txch; /* The TX DMA channel number */ DMA_HANDLE rxdma; /* DMA channel handle for RX transfers */ DMA_HANDLE txdma; /* DMA channel handle for TX transfers */ sem_t rxsem; /* Wait for RX DMA to complete */ @@ -961,6 +958,8 @@ static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg) static void spi_dmarxsetup(FAR struct stm32_spidev_s *priv, FAR void *rxbuffer, FAR void *rxdummy, size_t nwords) { + stm32_dmacfg_t dmacfg; + /* 8- or 16-bit mode? */ if (spi_9to16bitmode(priv)) @@ -994,8 +993,13 @@ static void spi_dmarxsetup(FAR struct stm32_spidev_s *priv, FAR void *rxbuffer, /* Configure the RX DMA */ - stm32_dmasetup(priv->rxdma, priv->spibase + STM32_SPI_RXDR_OFFSET, - (uint32_t)rxbuffer, nwords, priv->rxccr); + dmacfg.paddr = priv->spibase + STM32_SPI_RXDR_OFFSET; + dmacfg.maddr = (uint32_t)rxbuffer; + dmacfg.ndata = nwords; + dmacfg.cfg1 = priv->rxccr; + dmacfg.cfg2 = 0; + + stm32_dmasetup(priv->rxdma, &dmacfg); } #endif @@ -1011,6 +1015,8 @@ static void spi_dmarxsetup(FAR struct stm32_spidev_s *priv, FAR void *rxbuffer, static void spi_dmatxsetup(FAR struct stm32_spidev_s *priv, FAR const void *txbuffer, FAR const void *txdummy, size_t nwords) { + stm32_dmacfg_t dmacfg; + /* 8- or 16-bit mode? */ if (spi_9to16bitmode(priv)) @@ -1042,10 +1048,15 @@ static void spi_dmatxsetup(FAR struct stm32_spidev_s *priv, FAR const void *txbu } } + dmacfg.paddr = priv->spibase + STM32_SPI_TXDR_OFFSET; + dmacfg.maddr = (uint32_t)txbuffer; + dmacfg.ndata = nwords; + dmacfg.cfg1 = priv->txccr; + dmacfg.cfg2 = 0; + /* Setup the TX DMA */ - stm32_dmasetup(priv->txdma, priv->spibase + STM32_SPI_TXDR_OFFSET, - (uint32_t)txbuffer, nwords, priv->txccr); + stm32_dmasetup(priv->txdma, &dmacfg); } #endif @@ -1358,6 +1369,7 @@ static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode) spi_enable(priv, false); spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, 0, SPI_CFG1_RXDMAEN | SPI_CFG1_TXDMAEN); + spi_enable(priv, true); #endif /* Save the mode so that subsequent re-configurations will be faster */ @@ -1704,8 +1716,23 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, DEBUGASSERT(priv != NULL); #ifdef CONFIG_STM32H7_DMACAPABLE - if ((txbuffer && !stm32_dmacapable((uint32_t)txbuffer, nwords, priv->txccr)) || - (rxbuffer && !stm32_dmacapable((uint32_t)rxbuffer, nwords, priv->rxccr))) + stm32_dmacfg_t dmacfg1; + stm32_dmacfg_t dmacfg2; + + /* TX transfer configuration */ + + dmacfg1.maddr = (uint32_t)txbuffer; + dmacfg1.ndata = nwords; + dmacfg1.cfg1 = priv->txccr; + + /* RX transfer configuration */ + + dmacfg2.maddr = (uint32_t)rxbuffer; + dmacfg2.ndata = nwords; + dmacfg2.cfg1 = priv->rxccr; + + if ((txbuffer && !stm32_dmacapable(priv->txdma, &dmacfg1)) || + (rxbuffer && !stm32_dmacapable(priv->rxdma, &dmacfg2))) { /* Unsupported memory region, fall back to non-DMA method. */ @@ -1739,6 +1766,10 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, arch_flush_dcache((uintptr_t)txbuffer, (uintptr_t)txbuffer + buflen); } + /* REVISIT: Master transfer start */ + + spi_modifyreg(priv, STM32_SPI_CR1_OFFSET, 0, SPI_CR1_CSTART); + #ifdef CONFIG_SPI_TRIGGER /* Is deferred triggering in effect? */ diff --git a/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c b/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c index a0a2aaa000..11cb84c144 100644 --- a/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c +++ b/arch/arm/src/stm32h7/stm32h7x3xx_rcc.c @@ -516,12 +516,11 @@ static void stm32_stdclockconfig(void) if (timeout > 0) { - - /* Set the HCLK source/divider */ + /* Set the D1 domain Core prescaler and the HCLK source/divider */ regval = getreg32(STM32_RCC_D1CFGR); - regval &= ~RCC_D1CFGR_HPRE_MASK; - regval |= STM32_RCC_D1CFGR_HPRE; + regval &= ~(RCC_D1CFGR_HPRE_MASK | RCC_D1CFGR_D1CPRE_MASK); + regval |= (STM32_RCC_D1CFGR_HPRE | STM32_RCC_D1CFGR_D1CPRE); putreg32(regval, STM32_RCC_D1CFGR); /* Set PCLK1 */ diff --git a/configs/nucleo-h743zi/include/board.h b/configs/nucleo-h743zi/include/board.h index d2833152ec..b6dc36d970 100644 --- a/configs/nucleo-h743zi/include/board.h +++ b/configs/nucleo-h743zi/include/board.h @@ -55,7 +55,7 @@ /* Clocking *************************************************************************/ /* The Nucleo-144 board provides the following clock sources: * - * MCO: 8 MHz from MCO output of ST-LINK is used as input clock + * MCO: 8 MHz from MCO output of ST-LINK is used as input clock (default) * X2: 32.768 KHz crystal for LSE * X3: HSE crystal oscillator (not provided) * @@ -67,7 +67,7 @@ * LSE: 32.768 kHz */ -#define STM32_BOARD_XTAL 8000000ul +#define STM32_BOARD_XTAL 8000000ul /* ST-LINK MCO */ #define STM32_HSI_FREQUENCY 16000000ul #define STM32_LSI_FREQUENCY 32000 @@ -164,6 +164,7 @@ * CPUCLK = SYSCLK / 1 = 400 MHz */ +#define STM32_RCC_D1CFGR_D1CPRE (RCC_D1CFGR_D1CPRE_SYSCLK) #define STM32_SYSCLK_FREQUENCY (STM32_PLL1P_FREQUENCY) #define STM32_CPUCLK_FREQUENCY (STM32_SYSCLK_FREQUENCY / 1) @@ -328,6 +329,11 @@ #define GPIO_SPI3_SCK GPIO_SPI3_SCK_1 /* PB3 */ #define GPIO_SPI3_NSS GPIO_SPI3_NSS_2 /* PA4 */ +/* DMA ******************************************************************************/ + +#define DMAMAP_SPI3_RX DMAMAP_DMA12_SPI3RX_0 /* DMA1 */ +#define DMAMAP_SPI3_TX DMAMAP_DMA12_SPI3TX_0 /* DMA1 */ + /************************************************************************************ * Public Data ************************************************************************************/