This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push: new 0c1f9d482db Added DMA support for H5. Also added ADC DMA support. 0c1f9d482db is described below commit 0c1f9d482dbb8a9d6247335d7484afaad24baec2 Author: kywwilson11 <kwil...@2g-eng.com> AuthorDate: Tue Jul 8 15:55:05 2025 -0500 Added DMA support for H5. Also added ADC DMA support. Added logic to set hasdma to false. This is needed to enable or not enable interrupts on a per ADC basis. Made other minor formatting changes. Fixed build issues with non ADC/DMA configurations. --- arch/arm/src/stm32h5/Kconfig | 123 ++-- arch/arm/src/stm32h5/Make.defs | 4 + arch/arm/src/stm32h5/hardware/stm32_gpdma.h | 10 +- .../arm/src/stm32h5/hardware/stm32h56x_dmasigmap.h | 218 ++++++ arch/arm/src/stm32h5/stm32_adc.c | 195 +++++- arch/arm/src/stm32h5/stm32_adc.h | 143 ++-- arch/arm/src/stm32h5/stm32_dma.c | 763 +++++++++++++++++++++ arch/arm/src/stm32h5/stm32_dma.h | 307 +++++++++ arch/arm/src/stm32h5/stm32h5xx_rcc.c | 4 +- 9 files changed, 1589 insertions(+), 178 deletions(-) diff --git a/arch/arm/src/stm32h5/Kconfig b/arch/arm/src/stm32h5/Kconfig index 0af61ae1b98..d480668e4f5 100644 --- a/arch/arm/src/stm32h5/Kconfig +++ b/arch/arm/src/stm32h5/Kconfig @@ -881,6 +881,75 @@ config ARCH_BOARD_STM32H5_CUSTOM_CLOCKCONFIG ---help--- Enables special, board-specific STM32 clock configuration. +menu "ADC Configuration" + depends on STM32H5_ADC + +config STM32H5_ADC_MAX_SAMPLES + int "The maximum number of channels that can be sampled" + default 16 + ---help--- + The maximum number of samples which can be handled without + overrun depends on various factors. This is the user's + responsibility to correctly select this value. + Since the interface to update the sampling time is available + for all supported devices, the user can change the default + values in the board initialization logic and avoid ADC overrun. + +config STM32H5_ADC1_DMA + bool "ADC1 DMA Enable" + depends on STM32H5_ADC1 && STM32H5_DMA + default n + ---help--- + If DMA is selected, then the ADC may be configured to support DMA + transfer, which is necessary if multiple channels are read or if + very high trigger frequencies are used. + +config STM32H5_ADC1_DMA_BATCH + int "ADC1 DMA number of conversions" + depends on STM32H5_ADC1 && STM32H5_ADC1_DMA + default 1 + ---help--- + This option allows you to select the number of regular group conversions + that will trigger a DMA callback transerring data to the upper-half driver. + By default, this value is 1, which means that data is transferred after + each group conversion. + +config STM32H5_ADC1_DMA_CFG + bool "ADC1 DMA configuration" + depends on STM32H5_ADC1 && STM32H5_ADC1_DMA + default n + ---help--- + 0 - ADC1 DMA in One Shot Mode, 1 - ADC1 DMA in Circular Mode + +config STM32H5_ADC2_DMA + bool "ADC2 DMA Enable" + depends on STM32H5_ADC2 && STM32H5_DMA + default n + ---help--- + If DMA is selected, then the ADC may be configured to support DMA + transfer, which is necessary if multiple channels are read or if + very high trigger frequencies are used. + +config STM32H5_ADC2_DMA_BATCH + int "ADC2 DMA number of conversions" + depends on STM32H5_ADC2 && STM32H5_ADC2_DMA + default 1 + ---help--- + This option allows you to select the number of regular group conversions + that will trigger a DMA callback transerring data to the upper-half driver. + By default, this value is 1, which means that data is transferred after + each group conversion. + +config STM32H5_ADC2_DMA_CFG + int "ADC2 DMA configuration" + depends on STM32H5_ADC2_DMA && STM32H5_DMA + range 0 1 + default 0 + ---help--- + 0 - ADC2 DMA in One Shot Mode, 1 - ADC2 DMA in Circular Mode + +endmenu # ADC Configuration + menu "SPI Configuration" depends on STM32H5_SPI @@ -4550,60 +4619,6 @@ endif # STM32H5_SERIALDRIVER endmenu # U[S]ART Configuration -menu "ADC Configuration" - depends on STM32H5_ADC - -config STM32H5_ADC_MAX_SAMPLES - int "The maximum number of channels that can be sampled" - default 16 - ---help--- - The maximum number of samples which can be handled without - overrun depends on various factors. This is the user's - responsibility to correctly select this value. - Since the interface to update the sampling time is available - for all supported devices, the user can change the default - values in the board initialization logic and avoid ADC overrun. - -config STM32H5_ADC1_DMA - bool "ADC1 DMA (not supported yet)" - depends on STM32H5_ADC1 && EXPERIMENTAL - default n - ---help--- - If DMA is selected, then the ADC may be configured to support - DMA transfer, which is necessary if multiple channels are read - or if very high trigger frequencies are used. - -config STM32H5_ADC1_DMA_BATCH - int "ADC1 DMA number of conversions" - depends on STM32H5_ADC1 && STM32H5_ADC1_DMA - default 1 - ---help--- - This option allows you to select the number of regular group conversions - that will trigger a DMA callback transerring data to the upper-half driver. - By default, this value is 1, which means that data is transferred after - each group conversion. - -config STM32H5_ADC2_DMA - bool "ADC2 DMA (not supported yet)" - depends on STM32H5_ADC2 && EXPERIMENTAL - default n - ---help--- - If DMA is selected, then the ADC may be configured to support - DMA transfer, which is necessary if multiple channels are read - or if very high trigger frequencies are used. - -config STM32H5_ADC2_DMA_BATCH - int "ADC2 DMA number of conversions" - depends on STM32H5_ADC2 && STM32H5_ADC2_DMA - default 1 - ---help--- - This option allows you to select the number of regular group conversions - that will trigger a DMA callback transerring data to the upper-half driver. - By default, this value is 1, which means that data is transferred after - each group conversion. - -endmenu - menu "Ethernet MAC Configuration" depends on STM32H5_ETHMAC diff --git a/arch/arm/src/stm32h5/Make.defs b/arch/arm/src/stm32h5/Make.defs index 61ca2130741..c2c5430145b 100644 --- a/arch/arm/src/stm32h5/Make.defs +++ b/arch/arm/src/stm32h5/Make.defs @@ -88,6 +88,10 @@ ifeq ($(CONFIG_STM32H5_ETHMAC),y) CHIP_CSRCS += stm32_ethernet.c endif +ifeq ($(CONFIG_STM32H5_DMA),y) +CHIP_CSRCS += stm32_dma.c +endif + # Required chip type specific files ifeq ($(CONFIG_STM32H5_STM32H5XXXX),y) diff --git a/arch/arm/src/stm32h5/hardware/stm32_gpdma.h b/arch/arm/src/stm32h5/hardware/stm32_gpdma.h index 1e4b2760085..47125c3173d 100644 --- a/arch/arm/src/stm32h5/hardware/stm32_gpdma.h +++ b/arch/arm/src/stm32h5/hardware/stm32_gpdma.h @@ -30,6 +30,12 @@ #include <nuttx/config.h> #include "chip.h" +#if defined(CONFIG_STM32H5_STM32H56XXX) || defined(CONFIG_STM32H5_STM32H57XXX) +# include "stm32h56x_dmasigmap.h" +#else +# error "Unsupported STM32 H5 DMA map" +#endif + /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ @@ -455,6 +461,8 @@ #define GPDMA_CXCR_PRIO_SHIFT (22) /* Bits 22-23: Priority level of ch x GPDMA transfer */ #define GPDMA_CXCR_PRIO_MASK (0b11 << GPDMA_CXCR_PRIO_SHIFT) +#define GPDMA_CXCR_ALLINTS (GPDMA_CXCR_TOIE|GPDMA_CXCR_SUSPEI|GPDMA_CXCR_USEIE|GPDMA_CXCR_ULEIE|GPDMA_CXCR_DTEIE|GPDMA_CXCR_HTIE|GPDMA_CXCR_TCIE) + /* Channel x transfer register 1 */ #define GPDMA_CXTR1_SDW_LOG2_SHIFT (0) @@ -485,7 +493,7 @@ #define GPDMA_CXTR1_DBL_1(l) ((l) - 1 << GPDMA_CXTR1_DBL_1_SHIFT) #define GPDMA_CXTR1_DBX (1 << 26) /* Destination byte exchange */ -#define GPDMA_CXTR1_DHX (1 << 17) /* Destination half-word exchange */ +#define GPDMA_CXTR1_DHX (1 << 27) /* Destination half-word exchange */ #define GPDMA_CXTR1_DAP (1 << 30) /* Destination allocated port */ #define GPDMA_CXTR1_DSEC (1 << 31) /* Security attribute of transfer to the destination */ diff --git a/arch/arm/src/stm32h5/hardware/stm32h56x_dmasigmap.h b/arch/arm/src/stm32h5/hardware/stm32h56x_dmasigmap.h new file mode 100644 index 00000000000..9a654032261 --- /dev/null +++ b/arch/arm/src/stm32h5/hardware/stm32h56x_dmasigmap.h @@ -0,0 +1,218 @@ +/**************************************************************************** + * arch/arm/src/stm32h5/hardware/stm32h56x_dmasigmap.h + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32H56X_DMASIGMAP_H +#define __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32H56X_DMASIGMAP_H + +/* This file is valid for STM32H562/563/573 devices */ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* GPDMA Request Number */ + +#define GPDMA_REQ_ADC1 (0) +#define GPDMA_REQ_ADC2 (1) +#define GPDMA_REQ_DAC1_CH1 (2) +#define GPDMA_REQ_DAC1_CH2 (3) +#define GPDMA_REQ_TIM6_UPD (4) +#define GPDMA_REQ_TIM7_UPD (5) +#define GPDMA_REQ_SPI1_RX (6) +#define GPDMA_REQ_SPI1_TX (7) +#define GPDMA_REQ_SPI2_RX (8) +#define GPDMA_REQ_SPI2_TX (9) +#define GPDMA_REQ_SPI3_RX (10) +#define GPDMA_REQ_SPI3_TX (11) +#define GPDMA_REQ_I2C1_RX (12) +#define GPDMA_REQ_I2C1_TX (13) + /* (14) RESERVED */ +#define GPDMA_REQ_I2C2_RX (15) +#define GPDMA_REQ_I2C2_TX (16) + /* (17) RESERVED */ +#define GPDMA_REQ_I2C3_RX (18) +#define GPDMA_REQ_I2C3_TX (19) + /* (20) RESERVED */ +#define GPDMA_REQ_USART1_RX (21) +#define GPDMA_REQ_USART1_TX (22) +#define GPDMA_REQ_USART2_RX (23) +#define GPDMA_REQ_USART2_TX (24) +#define GPDMA_REQ_USART3_RX (25) +#define GPDMA_REQ_USART3_TX (26) +#define GPDMA_REQ_UART4_RX (27) +#define GPDMA_REQ_UART4_TX (28) +#define GPDMA_REQ_UART5_RX (29) +#define GPDMA_REQ_UART5_TX (30) +#define GPDMA_REQ_USART6_RX (31) +#define GPDMA_REQ_USART6_TX (32) +#define GPDMA_REQ_UART7_RX (33) +#define GPDMA_REQ_UART7_TX (34) +#define GPDMA_REQ_UART8_RX (35) +#define GPDMA_REQ_UART8_TX (36) +#define GPDMA_REQ_UART9_RX (37) +#define GPDMA_REQ_UART9_TX (38) +#define GPDMA_REQ_UART10_RX (39) +#define GPDMA_REQ_UART10_TX (40) +#define GPDMA_REQ_UART11_RX (41) +#define GPDMA_REQ_UART11_TX (42) +#define GPDMA_REQ_UART12_RX (43) +#define GPDMA_REQ_UART12_TX (44) +#define GPDMA_REQ_LPUART1_RX (45) +#define GPDMA_REQ_LPUART1_TX (46) +#define GPDMA_REQ_SPI4_RX (47) +#define GPDMA_REQ_SPI4_TX (48) +#define GPDMA_REQ_SPI5_RX (49) +#define GPDMA_REQ_SPI5_TX (50) +#define GPDMA_REQ_SPI6_RX (51) +#define GPDMA_REQ_SPI6_TX (52) +#define GPDMA_REQ_SAI1_A (53) +#define GPDMA_REQ_SAI1_B (54) +#define GPDMA_REQ_SAI2_A (55) +#define GPDMA_REQ_SAI2_B (56) +#define GPDMA_REQ_OSPI1 (57) +#define GPDMA_REQ_TIM1_CC1 (58) +#define GPDMA_REQ_TIM1_CC2 (59) +#define GPDMA_REQ_TIM1_CC3 (60) +#define GPDMA_REQ_TIM1_CC4 (61) +#define GPDMA_REQ_TIM1_UPD (62) +#define GPDMA_REQ_TIM1_TRG (63) +#define GPDMA_REQ_TIM1_COM (64) +#define GPDMA_REQ_TIM8_CC1 (65) +#define GPDMA_REQ_TIM8_CC2 (66) +#define GPDMA_REQ_TIM8_CC3 (67) +#define GPDMA_REQ_TIM8_CC4 (68) +#define GPDMA_REQ_TIM8_UPD (69) +#define GPDMA_REQ_TIM8_TIG (70) +#define GPDMA_REQ_TIM8_COM (71) +#define GPDMA_REQ_TIM2_CC1 (72) +#define GPDMA_REQ_TIM2_CC2 (73) +#define GPDMA_REQ_TIM2_CC3 (74) +#define GPDMA_REQ_TIM2_CC4 (75) +#define GPDMA_REQ_TIM2_UPD (76) +#define GPDMA_REQ_TIM3_CC1 (77) +#define GPDMA_REQ_TIM3_CC2 (78) +#define GPDMA_REQ_TIM3_CC3 (79) +#define GPDMA_REQ_TIM3_CC4 (80) +#define GPDMA_REQ_TIM3_UPD (81) +#define GPDMA_REQ_TIM3_TRG (82) +#define GPDMA_REQ_TIM4_CC1 (83) +#define GPDMA_REQ_TIM4_CC2 (84) +#define GPDMA_REQ_TIM4_CC3 (85) +#define GPDMA_REQ_TIM4_CC4 (86) +#define GPDMA_REQ_TIM4_UPD (87) +#define GPDMA_REQ_TIM5_CC1 (88) +#define GPDMA_REQ_TIM5_CC2 (89) +#define GPDMA_REQ_TIM5_CC3 (90) +#define GPDMA_REQ_TIM5_CC4 (91) +#define GPDMA_REQ_TIM5_UPD (92) +#define GPDMA_REQ_TIM5_TRG (93) +#define GPDMA_REQ_TIM15_CC1 (94) +#define GPDMA_REQ_TIM15_UPD (95) +#define GPDMA_REQ_TIM15_TRG (96) +#define GPDMA_REQ_TIM15_COM (97) +#define GPDMA_REQ_TIM16_CC1 (98) +#define GPDMA_REQ_TIM16_UPD (99) +#define GPDMA_REQ_TIM17_CC1 (100) +#define GPDMA_REQ_TIM17_UPD (101) +#define GPDMA_REQ_LPTIM1_IC1 (102) +#define GPDMA_REQ_LPTIM1_IC2 (103) +#define GPDMA_REQ_LPTIM1_UE (104) +#define GPDMA_REQ_LPTIM2_IC1 (105) +#define GPDMA_REQ_LPTIM2_IC2 (106) +#define GPDMA_REQ_LPTIM2_UE (107) +#define GPDMA_REQ_DCMI_PSSI (108) +#define GPDMA_REQ_AES_OUT (109) +#define GPDMA_REQ_AES_IN (110) +#define GPDMA_REQ_HASH_IN (111) +#define GPDMA_REQ_UCPD1_RX (112) +#define GPDMA_REQ_UCPD1_TX (113) +#define GPDMA_REQ_CORDIC_R (114) +#define GPDMA_REQ_CORDIC_W (115) +#define GPDMA_REQ_FMAC_R (116) +#define GPDMA_REQ_FMAC_W (117) +#define GPDMA_REQ_SAES_OUT (118) +#define GPDMA_REQ_SAES_IN (119) +#define GPDMA_REQ_I3C1_RX (120) +#define GPDMA_REQ_I3C1_TX (121) +#define GPDMA_REQ_I3C1_TC (122) +#define GPDMA_REQ_I3C1_RS (123) +#define GPDMA_REQ_I2C4_RX (124) +#define GPDMA_REQ_I2C4_TX (125) + /* (126) RESERVED */ +#define GPDMA_REQ_LPTIM3_IC1 (127) +#define GPDMA_REQ_LPTIM3_IC2 (128) +#define GPDMA_REQ_LPTIM3_UE (129) +#define GPDMA_REQ_LPTIM5_IC1 (130) +#define GPDMA_REQ_LPTIM5_IC2 (131) +#define GPDMA_REQ_LPTIM5_UE (132) +#define GPDMA_REQ_LPTIM6_IC1 (133) +#define GPDMA_REQ_LPTIM6_IC2 (134) +#define GPDMA_REQ_LPTIM6_UE (135) + +/* GPDMA Trigger Number */ + +#define GPDMA_TRIG_EXTI0 (0) +#define GPDMA_TRIG_EXTI1 (1) +#define GPDMA_TRIG_EXTI2 (2) +#define GPDMA_TRIG_EXTI3 (3) +#define GPDMA_TRIG_EXTI4 (4) +#define GPDMA_TRIG_EXTI5 (5) +#define GPDMA_TRIG_EXTI6 (6) +#define GPDMA_TRIG_EXTI7 (7) +#define GPDMA_TRIG_TAMP_TRG1 (8) +#define GPDMA_TRIG_TAMP_TRG2 (9) +#define GPDMA_TRIG_TAMP_TRG4 (10) +#define GPDMA_TRIG_LPTIM1_CH1 (11) +#define GPDMA_TRIG_LPTIM1_CH2 (12) +#define GPDMA_TRIG_LPTIM2_CH1 (13) +#define GPDMA_TRIG_LPTIM2_CH2 (14) +#define GPDMA_TRIG_RTC_ALRA_TRG (15) +#define GPDMA_TRIG_RTC_ALRB_TRG (16) +#define GPDMA_TRIG_RTC_WUT_TRG (17) +#define GPDMA_TRIG_GPDMA1_CH0_TC (18) +#define GPDMA_TRIG_GPDMA1_CH1_TC (19) +#define GPDMA_TRIG_GPDMA1_CH2_TC (20) +#define GPDMA_TRIG_GPDMA1_CH3_TC (21) +#define GPDMA_TRIG_GPDMA1_CH4_TC (22) +#define GPDMA_TRIG_GPDMA1_CH5_TC (23) +#define GPDMA_TRIG_GPDMA1_CH6_TC (24) +#define GPDMA_TRIG_GPDMA1_CH7_TC (25) +#define GPDMA_TRIG_GPDMA2_CH0_TC (26) +#define GPDMA_TRIG_GPDMA2_CH1_TC (27) +#define GPDMA_TRIG_GPDMA2_CH2_TC (28) +#define GPDMA_TRIG_GPDMA2_CH3_TC (29) +#define GPDMA_TRIG_GPDMA2_CH4_TC (30) +#define GPDMA_TRIG_GPDMA2_CH5_TC (31) +#define GPDMA_TRIG_GPDMA2_CH6_TC (32) +#define GPDMA_TRIG_GPDMA2_CH7_TC (33) +#define GPDMA_TRIG_TIM2_TRGO (34) +#define GPDMA_TRIG_TIM15_TRG0 (35) +#define GPDMA_TRIG_TIM12_TRGO (36) +#define GPDMA_TRIG_LPTIM3_CH1 (37) +#define GPDMA_TRIG_LPTIM3_CH2 (38) +#define GPDMA_TRIG_LPTIM4_AIT (39) +#define GPDMA_TRIG_LPTIM5_CH1 (40) +#define GPDMA_TRIG_LPTIM5_CH2 (41) +#define GPDMA_TRIG_LPTIM6_CH1 (42) +#define GPDMA_TRIG_LPTIM6_CH2 (43) + +#endif /* __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32H56X_DMASIGMAP_H */ \ No newline at end of file diff --git a/arch/arm/src/stm32h5/stm32_adc.c b/arch/arm/src/stm32h5/stm32_adc.c index 2217ea7a197..391dcd6412d 100644 --- a/arch/arm/src/stm32h5/stm32_adc.c +++ b/arch/arm/src/stm32h5/stm32_adc.c @@ -3,8 +3,6 @@ * * SPDX-License-Identifier: Apache-2.0 * - * SPDX-License-Identifier: Apache-2.0 - * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The @@ -49,6 +47,9 @@ #include "stm32_adc.h" #include "stm32_tim.h" #include "stm32_rcc.h" +#include "stm32_dma.h" + +/* ADC "upper half" support must be enabled */ #ifdef CONFIG_ADC @@ -64,11 +65,6 @@ /* ADC Channels/DMA *********************************************************/ -#ifdef ADC_HAVE_DMA -# error "STM32H5 ADC does not have DMA support." -# undef ADC_HAVE_DMA -#endif - #define ADC_SMPR_DEFAULT ADC_SMPR_640p5 #define ADC_SMPR1_DEFAULT ((ADC_SMPR_DEFAULT << ADC_SMPR1_SMP0_SHIFT) | \ (ADC_SMPR_DEFAULT << ADC_SMPR1_SMP1_SHIFT) | \ @@ -105,10 +101,10 @@ struct stm32_dev_s uint8_t cchannels; /* Number of configured channels */ uint8_t intf; /* ADC interface number */ uint8_t current; /* Current ADC channel being converted */ + bool hasdma; /* True: This ADC supports DMA */ #ifdef ADC_HAVE_DMA - uint8_t dmachan; /* DMA channel needed by this ADC */ - bool hasdma; /* True: This ADC supports DMA */ uint16_t dmabatch; /* Number of conversions for DMA batch */ + bool circular; /* 0 = one-shot, 1 = circular */ #endif #ifdef ADC_HAVE_TIMER uint8_t trigger; /* Timer trigger channel: 0=CC1, 1=CC2, 2=CC3, @@ -187,6 +183,13 @@ static void adc_timstart(struct stm32_dev_s *priv, bool enable); static int adc_timinit(struct stm32_dev_s *priv); #endif +#ifdef ADC_HAVE_DMA +static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t status, + void *arg); +static void adc_dmacfg(struct stm32_dev_s *priv, + struct stm32_gpdma_cfg_s *cfg); +#endif + /* ADC Interrupt Handler */ static int adc_interrupt(struct adc_dev_s *dev, uint32_t regval); @@ -245,10 +248,16 @@ static struct stm32_dev_s g_adcpriv1 = .freq = CONFIG_STM32H5_ADC1_SAMPLE_FREQUENCY, #endif #ifdef ADC1_HAVE_DMA - .dmachan = ADC1_DMA_CHAN, .hasdma = true, .r_dmabuffer = g_adc1_dmabuffer, - .dmabatch = CONFIG_STM32H5_ADC1_DMA_BATCH + .dmabatch = CONFIG_STM32H5_ADC1_DMA_BATCH, +# ifdef CONFIG_STM32H5_ADC1_DMA_CFG + .circular = true, +# else + .circular = false, +# endif +#else + .hasdma = false, #endif }; @@ -262,6 +271,12 @@ static struct adc_dev_s g_adcdev1 = /* ADC2 state */ #ifdef CONFIG_STM32H5_ADC2 + +#ifdef ADC2_HAVE_DMA +static uint16_t g_adc2_dmabuffer[CONFIG_STM32H5_ADC_MAX_SAMPLES * + CONFIG_STM32H5_ADC2_DMA_BATCH]; +#endif + static struct stm32_dev_s g_adcpriv2 = { .irq = STM32_IRQ_ADC2, @@ -279,6 +294,18 @@ static struct stm32_dev_s g_adcpriv2 = .pclck = ADC2_TIMER_PCLK_FREQUENCY, .freq = CONFIG_STM32H5_ADC2_SAMPLE_FREQUENCY, #endif +#ifdef ADC2_HAVE_DMA + .hasdma = true, + .r_dmabuffer = g_adc2_dmabuffer, + .dmabatch = CONFIG_STM32H5_ADC2_DMA_BATCH, +# ifdef CONFIG_STM32H5_ADC2_DMA_CFG + .circular = true, +# else + .circular = false, +# endif +#else + .hasdma = false, +#endif }; static struct adc_dev_s g_adcdev2 = @@ -777,6 +804,104 @@ static void adc_reset(struct adc_dev_s *dev) adc_rccreset(priv, false); } +/**************************************************************************** + * Name: adc_dmaconvcallback + * + * Description: + * Callback for DMA. Called from the DMA transfer complete interrupt after + * all channels have been converted and transferred with DMA. + * + * Input Parameters: + * + * handle - handle to DMA + * status - + * arg - adc device + * + * Returned Value: + * None + * + ****************************************************************************/ + + #ifdef ADC_HAVE_DMA +static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t status, void *arg) +{ + struct adc_dev_s *dev = (struct adc_dev_s *)arg; + struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv; + struct stm32_gpdma_cfg_s dmacfg; + int i; + + /* Verify that the upper-half has bound its callback */ + + if (priv->cb != NULL) + { + DEBUGASSERT(priv->cb->au_receive != NULL); + + /* Deliver one sample per configured channel */ + + for (i = 0; i < priv->rnchannels * priv->dmabatch; i++) + { + priv->cb->au_receive(dev, + priv->chanlist[priv->current], + priv->r_dmabuffer[i]); + priv->current++; + if (priv->current >= priv->rnchannels) + { + priv->current = 0; + } + } + } + + /* Restart DMA for the next conversion series */ + + if (priv->circular == 0) + { + adc_dmacfg(priv, &dmacfg); + stm32_dmasetup(priv->dma, &dmacfg); + stm32_dmastart(priv->dma, adc_dmaconvcallback, dev, false); + adc_startconv(priv, true); + } +} + +/**************************************************************************** + * Name: adc_dmacfg + * + * Description: + * Generate the required DMA configuration structure for oneshot mode based + * on the ADC configuration. + * + * Input Parameters: + * priv - ADC instance structure + * cfg - DMA configuration structure + * circular - 0 = oneshot, 1 = circular + * + * Returned Value: + * None + ****************************************************************************/ + +static void adc_dmacfg(struct stm32_dev_s *priv, + struct stm32_gpdma_cfg_s *cfg) +{ + const uint32_t sdw_log2 = 1; /* Always 16-bit half-word for ADC_DR */ + + cfg->src_addr = priv->base + STM32_ADC_DR_OFFSET; + cfg->dest_addr = (uintptr_t)priv->r_dmabuffer; + + cfg->request = (priv->base == STM32_ADC1_BASE) + ? GPDMA_REQ_ADC1 + : GPDMA_REQ_ADC2; + + cfg->priority = GPMDACFG_PRIO_LH; + + cfg->mode = priv->circular ? GPDMACFG_MODE_CIRC : 0; + + cfg->ntransfers = priv->cchannels * priv->dmabatch * (1u << sdw_log2); + + cfg->tr1 = (sdw_log2 << GPDMA_CXTR1_SDW_LOG2_SHIFT) + | (sdw_log2 << GPDMA_CXTR1_DDW_LOG2_SHIFT) + | GPDMA_CXTR1_DINC; /* dest-inc, source fixed */ +} +#endif + /**************************************************************************** * Name: adc_setup * @@ -795,6 +920,9 @@ static void adc_reset(struct adc_dev_s *dev) static int adc_setup(struct adc_dev_s *dev) { struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv; +#ifdef ADC_HAVE_DMA + struct stm32_gpdma_cfg_s dmacfg; +#endif int ret; irqstate_t flags; uint32_t clrbits; @@ -842,15 +970,29 @@ static int adc_setup(struct adc_dev_s *dev) #ifdef ADC_HAVE_DMA if (priv->hasdma) { - /* Enable One shot DMA */ + /* Enable One-shot or Circular DMA. + * WARNING: This doesn't work in dual-ADC modes. [RM0481] ADC_CFGR + * register description (pg. 1122) - "In dual-ADC modes, this bit is + * not relevant and replaced by control bit DMACFG of the ADC_CCR + * register" + */ setbits |= ADC_CFGR_DMAEN; - } -#endif - - /* Disable continuous mode */ + if (priv->circular) + { + setbits |= ADC_CFGR_DMACFG; + setbits |= ADC_CFGR_CONT; + } + else + { + clrbits |= ADC_CFGR_DMACFG; + clrbits |= ADC_CFGR_CONT; + } + } +#else clrbits |= ADC_CFGR_CONT; +#endif /* Disable external trigger for regular channels */ @@ -903,17 +1045,14 @@ static int adc_setup(struct adc_dev_s *dev) stm32_dmafree(priv->dma); } - priv->dma = stm32_dmachannel(priv->dmachan); + priv->dma = stm32_dmachannel(GPDMA_TTYPE_P2M); - stm32_dmasetup(priv->dma, - priv->base + STM32_ADC_DR_OFFSET, - (uint32_t)priv->r_dmabuffer, - priv->rnchannels * priv->dmabatch, - ADC_DMA_CONTROL_WORD); + adc_dmacfg(priv, &dmacfg); + + stm32_dmasetup(priv->dma, &dmacfg); stm32_dmastart(priv->dma, adc_dmaconvcallback, dev, false); } - #endif /* Set ADEN to wake up the ADC from Power Down. */ @@ -949,10 +1088,14 @@ static int adc_setup(struct adc_dev_s *dev) adc_getreg(priv, STM32_ADC_SQR4_OFFSET)); ainfo("CCR: 0x%08" PRIx32 "\n", adc_getregm(priv, STM32_ADC_CCR_OFFSET)); - /* Enable the ADC interrupt */ + if (!priv->hasdma) + { + /* Enable the ADC interrupt */ - ainfo("Enable the ADC interrupt: irq=%d\n", priv->irq); - up_enable_irq(priv->irq); + ainfo("Enable the ADC interrupt: irq=%d\n", priv->irq); + + up_enable_irq(priv->irq); + } priv->initialized = true; diff --git a/arch/arm/src/stm32h5/stm32_adc.h b/arch/arm/src/stm32h5/stm32_adc.h index 646048c1fd1..eec3fc4e5fc 100644 --- a/arch/arm/src/stm32h5/stm32_adc.h +++ b/arch/arm/src/stm32h5/stm32_adc.h @@ -43,52 +43,58 @@ /* Timer devices may be used for different purposes. One special purpose is * to control periodic ADC sampling. If CONFIG_STM32H5_TIMn is defined then * CONFIG_STM32H5_TIMn_ADC must also be defined to indicate that timer "n" - * is intended to be used for that purpose. Timers 1,2,3,6 and 15 may be - * used on STM32H5X3, while STM32H5X6 adds support for timers 4 and 8 as - * well. + * is intended to be used for that purpose. */ #ifndef CONFIG_STM32H5_TIM1 # undef CONFIG_STM32H5_TIM1_ADC # undef CONFIG_STM32H5_TIM1_ADC1 # undef CONFIG_STM32H5_TIM1_ADC2 -# undef CONFIG_STM32H5_TIM1_ADC3 #endif #ifndef CONFIG_STM32H5_TIM2 # undef CONFIG_STM32H5_TIM2_ADC # undef CONFIG_STM32H5_TIM2_ADC1 # undef CONFIG_STM32H5_TIM2_ADC2 -# undef CONFIG_STM32H5_TIM2_ADC3 #endif #ifndef CONFIG_STM32H5_TIM3 # undef CONFIG_STM32H5_TIM3_ADC # undef CONFIG_STM32H5_TIM3_ADC1 # undef CONFIG_STM32H5_TIM3_ADC2 -# undef CONFIG_STM32H5_TIM3_ADC3 #endif #ifndef CONFIG_STM32H5_TIM4 # undef CONFIG_STM32H5_TIM4_ADC # undef CONFIG_STM32H5_TIM4_ADC1 # undef CONFIG_STM32H5_TIM4_ADC2 -# undef CONFIG_STM32H5_TIM4_ADC3 #endif #ifndef CONFIG_STM32H5_TIM6 # undef CONFIG_STM32H5_TIM6_ADC # undef CONFIG_STM32H5_TIM6_ADC1 # undef CONFIG_STM32H5_TIM6_ADC2 -# undef CONFIG_STM32H5_TIM6_ADC3 #endif #ifndef CONFIG_STM32H5_TIM8 # undef CONFIG_STM32H5_TIM8_ADC # undef CONFIG_STM32H5_TIM8_ADC1 # undef CONFIG_STM32H5_TIM8_ADC2 -# undef CONFIG_STM32H5_TIM8_ADC3 #endif #ifndef CONFIG_STM32H5_TIM15 # undef CONFIG_STM32H5_TIM15_ADC # undef CONFIG_STM32H5_TIM15_ADC1 # undef CONFIG_STM32H5_TIM15_ADC2 -# undef CONFIG_STM32H5_TIM15_ADC3 +#endif + +/* DMA support */ + +#undef ADC_HAVE_DMA +#if defined(CONFIG_STM32H5_ADC1_DMA) || defined(CONFIG_STM32H5_ADC2_DMA) +# define ADC_HAVE_DMA 1 +#endif + +#if defined(CONFIG_STM32H5_ADC1_DMA) +# define ADC1_HAVE_DMA 1 +#endif + +#if defined(CONFIG_STM32H5_ADC2_DMA) +# define ADC2_HAVE_DMA 1 #endif /* Timer configuration: If a timer trigger is specified, then get @@ -213,72 +219,19 @@ # undef ADC_HAVE_TIMER #endif -/* Timer 1 */ - -#define ADC1_EXTSEL_T1CC1 ADC_CFGR_EXTSEL_T1CC1 -#define ADC1_EXTSEL_T1CC2 ADC_CFGR_EXTSEL_T1CC2 -#define ADC1_EXTSEL_T1CC3 ADC_CFGR_EXTSEL_T1CC3 -#define ADC1_EXTSEL_T1CC4 ADC_CFGR_EXTSEL_T1CC4 -#define ADC1_EXTSEL_T1TRGO ADC_CFGR_EXTSEL_T1TRGO -#define ADC1_EXTSEL_T1TRGO2 ADC_CFGR_EXTSEL_T1TRGO2 -#define ADC2_EXTSEL_T1CC1 ADC_CFGR_EXTSEL_T1CC1 -#define ADC2_EXTSEL_T1CC2 ADC_CFGR_EXTSEL_T1CC2 -#define ADC2_EXTSEL_T1CC3 ADC_CFGR_EXTSEL_T1CC3 -#define ADC2_EXTSEL_T1CC4 ADC_CFGR_EXTSEL_T1CC4 -#define ADC2_EXTSEL_T1TRGO ADC_CFGR_EXTSEL_T1TRGO -#define ADC2_EXTSEL_T1TRGO2 ADC_CFGR_EXTSEL_T1TRGO2 - -/* Timer 2 */ - -#define ADC1_EXTSEL_T2CC2 ADC_CFGR_EXTSEL_T2CC2 -#define ADC1_EXTSEL_T2TRGO ADC_CFGR_EXTSEL_T2TRGO -#define ADC2_EXTSEL_T2CC2 ADC_CFGR_EXTSEL_T2CC2 -#define ADC2_EXTSEL_T2TRGO ADC_CFGR_EXTSEL_T2TRGO - -/* Timer 3 */ - -#define ADC1_EXTSEL_T3CC4 ADC_CFGR_EXTSEL_T3CC4 -#define ADC1_EXTSEL_T3TRGO ADC_CFGR_EXTSEL_T3TRGO -#define ADC2_EXTSEL_T3CC4 ADC_CFGR_EXTSEL_T3CC4 -#define ADC2_EXTSEL_T3TRGO ADC_CFGR_EXTSEL_T3TRGO - -/* Timer 4 */ - -#define ADC1_EXTSEL_T4CC4 ADC_CFGR_EXTSEL_T4CC4 -#define ADC1_EXTSEL_T4TRGO ADC_CFGR_EXTSEL_T4TRGO -#define ADC2_EXTSEL_T4CC4 ADC_CFGR_EXTSEL_T4CC4 -#define ADC2_EXTSEL_T4TRGO ADC_CFGR_EXTSEL_T4TRGO - -/* Timer 6 */ - -#define ADC1_EXTSEL_T6TRGO ADC_CFGR_EXTSEL_T6TRGO -#define ADC2_EXTSEL_T6TRGO ADC_CFGR_EXTSEL_T6TRGO - -/* Timer 8 */ - -#define ADC1_EXTSEL_T8TRGO ADC_CFGR_EXTSEL_T8TRGO -#define ADC1_EXTSEL_T8TRGO2 ADC_CFGR_EXTSEL_T8TRGO2 -#define ADC2_EXTSEL_T8TRGO ADC_CFGR_EXTSEL_T8TRGO -#define ADC2_EXTSEL_T8TRGO2 ADC_CFGR_EXTSEL_T8TRGO2 - -/* Timer 15 */ - -#define ADC1_EXTSEL_T15TRGO ADC_CFGR_EXTSEL_T15TRGO -#define ADC2_EXTSEL_T15TRGO ADC_CFGR_EXTSEL_T15TRGO - #if defined(CONFIG_STM32H5_TIM1_ADC1) # if CONFIG_STM32H5_ADC1_TIMTRIG == 0 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T1CC1 +# define ADC1_EXTSEL_VALUE ADC_CFGR_EXTSEL_T1CC1 # elif CONFIG_STM32H5_ADC1_TIMTRIG == 1 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T1CC2 +# define ADC1_EXTSEL_VALUE ADC_CFGR_EXTSEL_T1CC2 # elif CONFIG_STM32H5_ADC1_TIMTRIG == 2 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T1CC3 +# define ADC1_EXTSEL_VALUE ADC_CFGR__EXTSEL_T1CC3 # elif CONFIG_STM32H5_ADC1_TIMTRIG == 3 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T1CC4 +# define ADC1_EXTSEL_VALUE ADC_CFGR_EXTSEL_T1CC4 # elif CONFIG_STM32H5_ADC1_TIMTRIG == 4 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T1TRGO +# define ADC1_EXTSEL_VALUE ADC_CFGR_EXTSEL_T1TRGO # elif CONFIG_STM32H5_ADC1_TIMTRIG == 5 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T1TRGO2 +# define ADC1_EXTSEL_VALUE ADC_CFGR_EXTSEL_T1TRGO2 # else # error "CONFIG_STM32H5_ADC1_TIMTRIG is out of range (TIM1)" # endif @@ -286,13 +239,13 @@ # if CONFIG_STM32H5_ADC1_TIMTRIG == 0 # error "CONFIG_STM32H5_ADC1_TIMTRIG is invalid (TIM2)" # elif CONFIG_STM32H5_ADC1_TIMTRIG == 1 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T2CC2 +# define ADC1_EXTSEL_VALUE ADC_CFGR_EXTSEL_T2CC2 # elif CONFIG_STM32H5_ADC1_TIMTRIG == 2 # error "CONFIG_STM32H5_ADC1_TIMTRIG is invalid (TIM2)" # elif CONFIG_STM32H5_ADC1_TIMTRIG == 3 # error "CONFIG_STM32H5_ADC1_TIMTRIG is invalid (TIM2)" # elif CONFIG_STM32H5_ADC1_TIMTRIG == 4 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T2TRGO +# define ADC1_EXTSEL_VALUE ADC_CFGR_EXTSEL_T2TRGO # elif CONFIG_STM32H5_ADC1_TIMTRIG == 5 # error "CONFIG_STM32H5_ADC1_TIMTRIG is invalid (TIM2)" # else @@ -306,9 +259,9 @@ # elif CONFIG_STM32H5_ADC1_TIMTRIG == 2 # error "CONFIG_STM32H5_ADC1_TIMTRIG is invalid (TIM3)" # elif CONFIG_STM32H5_ADC1_TIMTRIG == 3 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T3CC4 +# define ADC1_EXTSEL_VALUE ADC_CFGR_EXTSEL_T3CC4 # elif CONFIG_STM32H5_ADC1_TIMTRIG == 4 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T3TRGO +# define ADC1_EXTSEL_VALUE ADC_CFGR_EXTSEL_T3TRGO # elif CONFIG_STM32H5_ADC1_TIMTRIG == 5 # error "CONFIG_STM32H5_ADC1_TIMTRIG is invalid (TIM3)" # else @@ -322,9 +275,9 @@ # elif CONFIG_STM32H5_ADC1_TIMTRIG == 2 # error "CONFIG_STM32H5_ADC1_TIMTRIG is invalid (TIM4)" # elif CONFIG_STM32H5_ADC1_TIMTRIG == 3 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T4CC4 +# define ADC1_EXTSEL_VALUE ADC_CFGR_EXTSEL_T4CC4 # elif CONFIG_STM32H5_ADC1_TIMTRIG == 4 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T4TRGO +# define ADC1_EXTSEL_VALUE ADC_CRFT_EXTSEL_T4TRGO # elif CONFIG_STM32H5_ADC1_TIMTRIG == 5 # error "CONFIG_STM32H5_ADC1_TIMTRIG is invalid (TIM4)" # else @@ -340,7 +293,7 @@ # elif CONFIG_STM32H5_ADC1_TIMTRIG == 3 # error "CONFIG_STM32H5_ADC1_TIMTRIG is invalid (TIM6)" # elif CONFIG_STM32H5_ADC1_TIMTRIG == 4 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T6TRGO +# define ADC1_EXTSEL_VALUE ADC_CFGR_EXTSEL_T6TRGO # elif CONFIG_STM32H5_ADC1_TIMTRIG == 5 # error "CONFIG_STM32H5_ADC1_TIMTRIG is invalid (TIM6)" # else @@ -356,9 +309,9 @@ # elif CONFIG_STM32H5_ADC1_TIMTRIG == 3 # error "CONFIG_STM32H5_ADC1_TIMTRIG is invalid (TIM8)" # elif CONFIG_STM32H5_ADC1_TIMTRIG == 4 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T8TRGO +# define ADC1_EXTSEL_VALUE ADC_CFGR_EXTSEL_T8TRGO # elif CONFIG_STM32H5_ADC1_TIMTRIG == 5 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T8TRGO2 +# define ADC1_EXTSEL_VALUE ADC_CFGR_EXTSEL_T8TRGO2 # else # error "CONFIG_STM32H5_ADC1_TIMTRIG is out of range (TIM8)" # endif @@ -372,7 +325,7 @@ # elif CONFIG_STM32H5_ADC1_TIMTRIG == 3 # error "CONFIG_STM32H5_ADC1_TIMTRIG is invalid (TIM15)" # elif CONFIG_STM32H5_ADC1_TIMTRIG == 4 -# define ADC1_EXTSEL_VALUE ADC1_EXTSEL_T15TRGO +# define ADC1_EXTSEL_VALUE ADC_CFGR_EXTSEL_T15TRGO # elif CONFIG_STM32H5_ADC1_TIMTRIG == 5 # error "CONFIG_STM32H5_ADC1_TIMTRIG is invalid (TIM15)" # else @@ -382,17 +335,17 @@ #if defined(CONFIG_STM32H5_TIM1_ADC2) # if CONFIG_STM32H5_ADC2_TIMTRIG == 0 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T1CC1 +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T1CC1 # elif CONFIG_STM32H5_ADC2_TIMTRIG == 1 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T1CC2 +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T1CC2 # elif CONFIG_STM32H5_ADC2_TIMTRIG == 2 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T1CC3 +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T1CC3 # elif CONFIG_STM32H5_ADC2_TIMTRIG == 3 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T1CC4 +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T1CC4 # elif CONFIG_STM32H5_ADC2_TIMTRIG == 4 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T1TRGO +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T1TRGO # elif CONFIG_STM32H5_ADC2_TIMTRIG == 5 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T1TRGO2 +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T1TRGO2 # else # error "CONFIG_STM32H5_ADC2_TIMTRIG is out of range (TIM1)" # endif @@ -400,13 +353,13 @@ # if CONFIG_STM32H5_ADC2_TIMTRIG == 0 # error "CONFIG_STM32H5_ADC2_TIMTRIG is invalid (TIM2)" # elif CONFIG_STM32H5_ADC2_TIMTRIG == 1 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T2CC2 +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T2CC2 # elif CONFIG_STM32H5_ADC2_TIMTRIG == 2 # error "CONFIG_STM32H5_ADC2_TIMTRIG is invalid (TIM2)" # elif CONFIG_STM32H5_ADC2_TIMTRIG == 3 # error "CONFIG_STM32H5_ADC2_TIMTRIG is invalid (TIM2)" # elif CONFIG_STM32H5_ADC2_TIMTRIG == 4 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T2TRGO +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T2TRGO # elif CONFIG_STM32H5_ADC2_TIMTRIG == 5 # error "CONFIG_STM32H5_ADC2_TIMTRIG is invalid (TIM2)" # else @@ -420,9 +373,9 @@ # elif CONFIG_STM32H5_ADC2_TIMTRIG == 2 # error "CONFIG_STM32H5_ADC2_TIMTRIG is invalid (TIM3)" # elif CONFIG_STM32H5_ADC2_TIMTRIG == 3 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T3CC4 +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T3CC4 # elif CONFIG_STM32H5_ADC2_TIMTRIG == 4 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T3TRGO +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T3TRGO # elif CONFIG_STM32H5_ADC2_TIMTRIG == 5 # error "CONFIG_STM32H5_ADC2_TIMTRIG is invalid (TIM3)" # else @@ -436,9 +389,9 @@ # elif CONFIG_STM32H5_ADC2_TIMTRIG == 2 # error "CONFIG_STM32H5_ADC2_TIMTRIG is invalid (TIM4)" # elif CONFIG_STM32H5_ADC2_TIMTRIG == 3 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T4CC4 +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T4CC4 # elif CONFIG_STM32H5_ADC2_TIMTRIG == 4 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T4TRGO +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T4TRGO # elif CONFIG_STM32H5_ADC2_TIMTRIG == 5 # error "CONFIG_STM32H5_ADC2_TIMTRIG is invalid (TIM4)" # else @@ -454,7 +407,7 @@ # elif CONFIG_STM32H5_ADC2_TIMTRIG == 3 # error "CONFIG_STM32H5_ADC2_TIMTRIG is invalid (TIM6)" # elif CONFIG_STM32H5_ADC2_TIMTRIG == 4 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T6TRGO +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T6TRGO # elif CONFIG_STM32H5_ADC2_TIMTRIG == 5 # error "CONFIG_STM32H5_ADC2_TIMTRIG is invalid (TIM6)" # else @@ -470,9 +423,9 @@ # elif CONFIG_STM32H5_ADC2_TIMTRIG == 3 # error "CONFIG_STM32H5_ADC2_TIMTRIG is invalid (TIM8)" # elif CONFIG_STM32H5_ADC2_TIMTRIG == 4 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T8TRGO +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T8TRGO # elif CONFIG_STM32H5_ADC2_TIMTRIG == 5 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T8TRGO2 +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T8TRGO2 # else # error "CONFIG_STM32H5_ADC2_TIMTRIG is out of range (TIM8)" # endif @@ -486,7 +439,7 @@ # elif CONFIG_STM32H5_ADC2_TIMTRIG == 3 # error "CONFIG_STM32H5_ADC2_TIMTRIG is invalid (TIM15)" # elif CONFIG_STM32H5_ADC2_TIMTRIG == 4 -# define ADC2_EXTSEL_VALUE ADC2_EXTSEL_T15TRGO +# define ADC2_EXTSEL_VALUE ADC_CFGR_EXTSEL_T15TRGO # elif CONFIG_STM32H5_ADC2_TIMTRIG == 5 # error "CONFIG_STM32H5_ADC2_TIMTRIG is invalid (TIM15)" # else diff --git a/arch/arm/src/stm32h5/stm32_dma.c b/arch/arm/src/stm32h5/stm32_dma.c new file mode 100644 index 00000000000..2745c5b464a --- /dev/null +++ b/arch/arm/src/stm32h5/stm32_dma.c @@ -0,0 +1,763 @@ +/**************************************************************************** + * arch/arm/src/stm32h5/stm32_dma.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <stdint.h> +#include <stdbool.h> +#include <assert.h> +#include <debug.h> +#include <errno.h> + +#include <nuttx/irq.h> +#include <nuttx/arch.h> +#include <nuttx/signal.h> + +#include "arm_internal.h" +#include "sched/sched.h" +#include "stm32_dma.h" +#include "hardware/stm32_gpdma.h" +#include "hardware/stm32_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* For GPDMA peripheral, each channel has channel specific addresses that are + * at a base offset based on channel. Channel reg references will be based + * off this in this file. + */ + +#define CH_BASE_OFFSET(ch) (0x80*(ch)) +#define CH_CXLBAR_OFFSET 0x50 +#define CH_CXFCR_OFFSET 0x5C +#define CH_CXSR_OFFSET 0x60 +#define CH_CXCR_OFFSET 0x64 +#define CH_CXTR1_OFFSET 0x90 +#define CH_CXTR2_OFFSET 0x94 +#define CH_CXBR1_OFFSET 0x98 +#define CH_CXSAR_OFFSET 0x9C +#define CH_CXDAR_OFFSET 0xA0 +#define CH_CXTR3_OFFSET 0xA4 +#define CH_CXBR2_OFFSET 0xA8 +#define CH_CXLLR_OFFSET 0xCC + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct stm32_gpdma_lli_s +{ + uint32_t tr1; /* GPDMA_CxTR1 value */ + uint32_t tr2; /* GPDMA_CxTR2 value */ + uint32_t br1; /* GPDMA_CxBR1 value (block size in bytes) */ + uint32_t sar; /* GPDMA_CxSAR (source address) */ + uint32_t dar; /* GPDMA_CxDAR (dest address) */ + uint32_t llr; /* GPDMA_CxLLR (pointer+update bits) */ +} +__attribute__ ((aligned(32))); + +struct gpdma_ch_s +{ + uint8_t dma_instance; /* GPDMA1 or GPDMA2 */ + uint8_t channel; + uint8_t irq; + enum gpdma_ttype_e type; + bool free; /* Is this channel free to use. */ + uint32_t base; /* Channel base address */ + dma_callback_t callback; + void *arg; + struct stm32_gpdma_cfg_s cfg; /* Configuration passed at channel setup */ + struct stm32_gpdma_lli_s lli[2]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline uint32_t gpdmach_getreg(struct gpdma_ch_s *chan, + uint32_t offset); +static inline void gpdmach_putreg(struct gpdma_ch_s *chan, uint32_t offset, + uint32_t value); +static inline void gpdmach_modifyreg32(struct gpdma_ch_s *chan, + uint32_t offset, uint32_t clrbits, + uint32_t setbits); +static void gpdma_ch_abort(struct gpdma_ch_s *chan); +static void gpdma_ch_disable(struct gpdma_ch_s *chan); + +static int gpdma_setup(struct gpdma_ch_s *chan, + struct stm32_gpdma_cfg_s *cfg); +static int gpdma_setup_circular(struct gpdma_ch_s *chan, + struct stm32_gpdma_cfg_s *cfg); +static int gpdma_dmainterrupt(int irq, void *context, void *arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_STM32H5_DMA1 +static struct gpdma_ch_s g_chan[] = +{ + { + .dma_instance = 1, + .channel = 0, + .irq = STM32_IRQ_GPDMA1_CH0, + .free = true, + .base = STM32_DMA1_BASE + CH_BASE_OFFSET(0) + }, + { + .dma_instance = 1, + .channel = 1, + .irq = STM32_IRQ_GPDMA1_CH1, + .free = true, + .base = STM32_DMA1_BASE + CH_BASE_OFFSET(1) + }, + { + .dma_instance = 1, + .channel = 2, + .irq = STM32_IRQ_GPDMA1_CH2, + .free = true, + .base = STM32_DMA1_BASE + CH_BASE_OFFSET(2) + }, + { + .dma_instance = 1, + .channel = 3, + .irq = STM32_IRQ_GPDMA1_CH3, + .free = true, + .base = STM32_DMA1_BASE + CH_BASE_OFFSET(3) + }, +#endif +#ifdef CONFIG_STM32H5_DMA2 + { + .dma_instance = 2, + .channel = 0, + .irq = STM32_IRQ_GPDMA2_CH0, + .free = true, + .base = STM32_DMA2_BASE + CH_BASE_OFFSET(0) + }, + { + .dma_instance = 2, + .channel = 1, + .irq = STM32_IRQ_GPDMA2_CH1, + .free = true, + .base = STM32_DMA2_BASE + CH_BASE_OFFSET(1) + }, + { + .dma_instance = 2, + .channel = 2, + .irq = STM32_IRQ_GPDMA2_CH2, + .free = true, + .base = STM32_DMA2_BASE + CH_BASE_OFFSET(2) + }, + { + .dma_instance = 2, + .channel = 3, + .irq = STM32_IRQ_GPDMA2_CH3, + .free = true, + .base = STM32_DMA2_BASE + CH_BASE_OFFSET(3) + } +#endif +}; + +static uint32_t circ_addr_1; + +static uint32_t circ_addr_1; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static inline uint32_t gpdmach_getreg(struct gpdma_ch_s *chan, + uint32_t offset) +{ + return getreg32(chan->base + offset); +} + +static inline void gpdmach_putreg(struct gpdma_ch_s *chan, uint32_t offset, + uint32_t value) +{ + putreg32(value, chan->base + offset); +} + +static inline void gpdmach_modifyreg32(struct gpdma_ch_s *chan, + uint32_t offset, uint32_t clrbits, + uint32_t setbits) +{ + modifyreg32(chan->base + offset, clrbits, setbits); +} + +/**************************************************************************** + * Name: gpdma_dmainterrupt + * + * Description: + * DMA interrupt handler. + * + ****************************************************************************/ + +static int gpdma_dmainterrupt(int irq, void *context, void *arg) +{ + struct gpdma_ch_s *chan = NULL; + uint32_t status; + + /* Get the channel that generated the interrupt */ + + if (irq >= STM32_IRQ_GPDMA1_CH0 && irq <= STM32_IRQ_GPDMA1_CH7) + { + chan = &g_chan[irq - STM32_IRQ_GPDMA1_CH0]; + } + else if (irq >= STM32_IRQ_GPDMA2_CH0 && irq <= STM32_IRQ_GPDMA2_CH7) + { + chan = &g_chan[irq - STM32_IRQ_GPDMA2_CH0 + 4]; + } + else + { + DEBUGPANIC(); + } + + /* Get the interrupt status for this channel */ + + status = (gpdmach_getreg(chan, CH_CXSR_OFFSET) >> 8) & 0x7f; + + /* Clear the fetched channel interrupts by setting bits in the flag + * clear register + */ + + gpdmach_putreg(chan, CH_CXFCR_OFFSET, ~0); + + /* Invoke the callback */ + + if (chan->callback) + { + chan->callback(chan, (uint8_t)status, chan->arg); + } + + return 0; +} + +/**************************************************************************** + * Name: gpdma_ch_abort + * + * Description: + * For the given channel, suspend and abort any ongoing channel transfers. + * Returns after the abort has complete and taken effect. + * + ****************************************************************************/ + +static void gpdma_ch_abort(struct gpdma_ch_s *chan) +{ + if ((gpdmach_getreg(chan, CH_CXCR_OFFSET) & GPDMA_CXCR_EN) == 0) + { + return; + } + + /* 1. Software writes 1 to the GPDMA_CxCR.SUSP bit */ + + gpdmach_putreg(chan, CH_CXCR_OFFSET, GPDMA_CXCR_SUSP); + + /* 2. Polls suspend flag GPDMA_CxSR.SUSPF until SUSPF = 1, or waits for an + * interrupt previously enabled by writing 1 to GPDMA_CxCR.SUSPIE. + */ + + while ((gpdmach_getreg(chan, CH_CXSR_OFFSET) & GPDMA_CXSR_SUSPF) == 0) + { + } + + /* 3. Reset chan by writing 1 to GPDMA_CxCR.RESET */ + + gpdmach_putreg(chan, CH_CXCR_OFFSET, GPDMA_CXCR_RESET); + + /* 4. Wait for GPDMA_CxCR.EN and GPDMA_CxCR.SUSP bits to be reset */ + + while ((gpdmach_getreg(chan, CH_CXCR_OFFSET) & + (GPDMA_CXCR_EN | GPDMA_CXCR_SUSP)) != 0) + { + } +} + +/**************************************************************************** + * Name: gpdma_ch_disable + * + * Description: + * Disable the DMA channel. + * + ****************************************************************************/ + +static void gpdma_ch_disable(struct gpdma_ch_s *chan) +{ + DEBUGASSERT(chan != NULL); + + gpdma_ch_abort(chan); + + /* Disable and clear all interrupts. */ + + gpdmach_modifyreg32(chan, CH_CXCR_OFFSET, GPDMA_CXCR_ALLINTS, 0); + gpdmach_modifyreg32(chan, CH_CXFCR_OFFSET, 0, ~0); +} + +/**************************************************************************** + * Name: gpdma_setup + * + * Assumptions: + * - EN bit not set. Channel must have been aborted before this is called. + * + ****************************************************************************/ + +static int gpdma_setup(struct gpdma_ch_s *chan, + struct stm32_gpdma_cfg_s *cfg) +{ + uint32_t reg; + + /* Make sure not to use linked list mode. */ + + gpdmach_modifyreg32(chan, CH_CXLLR_OFFSET, ~0, 0); + gpdmach_putreg(chan, CH_CXLBAR_OFFSET, 0); + + /* Set source and destination addresses. */ + + gpdmach_putreg(chan, CH_CXSAR_OFFSET, cfg->src_addr); + gpdmach_putreg(chan, CH_CXDAR_OFFSET, cfg->dest_addr); + + /* Set the channel priority according to configuration. */ + + gpdmach_modifyreg32(chan, CH_CXCR_OFFSET, GPDMA_CXCR_PRIO_MASK, + cfg->priority << GPDMA_CXCR_PRIO_SHIFT); + + /* Set channels TR1 register based on configuration provided. */ + + gpdmach_putreg(chan, CH_CXTR1_OFFSET, cfg->tr1); + + /* Assemble the required config for TR2 */ + + reg = (uint32_t)cfg->request & + (GPDMA_CXTR2_REQSEL_MASK | GPDMA_CXTR2_DREQ | GPDMA_CXTR2_SWREQ); + gpdmach_putreg(chan, CH_CXTR2_OFFSET, reg); + + /* Calculate block number of data bytes to transfer, update BR1 */ + + reg = cfg->ntransfers; + + if (cfg->mode & GPDMACFG_MODE_CIRC) + { + /* This only targets peripheral to memory, with memory increment */ + + circ_addr_1 = cfg->dest_addr; + gpdmach_putreg(chan, CH_CXLBAR_OFFSET, + (uint32_t)&circ_addr_1 & (0xffff << 16)); + + reg = GPDMA_CXLLR_UDA | ((uint32_t)&circ_addr_1 & GPDMA_CXLLR_LA_MASK); + gpdmach_putreg(chan, CH_CXLLR_OFFSET, reg); + } + + gpdmach_putreg(chan, CH_CXBR1_OFFSET, reg); + + return 0; +} + +/**************************************************************************** + * Name: gpdma_setup_circular + * + * Description: + * Circular DMA requires linked list items (LLI) to setup properly. This + * function handles LLI allocation and handling necessary to implement + * circular DMA. + * + ****************************************************************************/ + +static int gpdma_setup_circular(struct gpdma_ch_s *chan, + struct stm32_gpdma_cfg_s *cfg) +{ + struct stm32_gpdma_lli_s *lli = chan->lli; + + lli[0].tr1 = cfg->tr1; + lli[0].tr2 = (2U << GPDMA_CXTR2_TCEM_SHIFT) + | (cfg->request & GPDMA_CXTR2_REQSEL_MASK); + lli[0].br1 = cfg->ntransfers; + lli[0].sar = cfg->src_addr; + lli[0].dar = cfg->dest_addr; + lli[0].llr = (GPDMA_CXLLR_UT1 /* reload TR1 */ + | GPDMA_CXLLR_UT2 /* reload TR2 */ + | GPDMA_CXLLR_UB1 /* reload BR1 */ + | GPDMA_CXLLR_USA /* reload SAR */ + | GPDMA_CXLLR_UDA /* reload DAR */ + | GPDMA_CXLLR_ULL) /* reload LLR */ + | (((uint32_t)&lli[1]) & GPDMA_CXLLR_LA_MASK); + + lli[1].tr1 = lli[0].tr1; + lli[1].tr2 = lli[0].tr2; + lli[1].br1 = lli[0].br1; + lli[1].sar = lli[0].sar; + lli[1].dar = lli[0].dar; + lli[1].llr = (lli[0].llr & ~GPDMA_CXLLR_LA_MASK) + | (((uint32_t)&lli[0]) & GPDMA_CXLLR_LA_MASK); + + gpdmach_putreg(chan, CH_CXSAR_OFFSET, lli[0].sar); + gpdmach_putreg(chan, CH_CXDAR_OFFSET, lli[0].dar); + gpdmach_putreg(chan, CH_CXTR1_OFFSET, lli[0].tr1); + gpdmach_putreg(chan, CH_CXTR2_OFFSET, lli[0].tr2); + gpdmach_putreg(chan, CH_CXBR1_OFFSET, lli[0].br1); + gpdmach_putreg(chan, CH_CXLBAR_OFFSET, (uint32_t)&lli[0]); + gpdmach_putreg(chan, CH_CXLLR_OFFSET, lli[0].llr); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: arm_dma_initialize + * + * Description: + * Initialize the DMA subsystem + * + * Returned Value: + * None + * + ****************************************************************************/ + +void weak_function arm_dma_initialize(void) +{ + struct gpdma_ch_s *chan; + int i; + + /* Initialize each DMA stream */ + + for (i = 0; i < sizeof(g_chan) / sizeof(struct gpdma_ch_s); i++) + { + chan = &g_chan[i]; + + /* Attach DMA interrupt vectors */ + + irq_attach(chan->irq, gpdma_dmainterrupt, chan); + + /* Disable the DMA channel */ + + gpdma_ch_disable(chan); + + /* Enable the IRQ at the NVIC (still disabled at the DMA controller) */ + + up_enable_irq(chan->irq); + } +} + +/**************************************************************************** + * Name: stm32_dmachannel + * + ****************************************************************************/ + +DMA_HANDLE stm32_dmachannel(enum gpdma_ttype_e type) +{ + /* On the H5, peripherals can kind of be mapped to any number of different + * channels. + * + * P2M and M2P should be on channels with 8 byte FIFOs. GPDMA1/2 CH[0-3] + * + * Linear M2M should be on channels with 32 byte FIFOs. GPDMA1/2 CH[4-5] + * Some peripherals (e.g. OCTOSPI) can also use these for burst transfers. + * + * 2D addressed transfers should be on GPDMA1/2 CH[6-7] + * + * Note: Currently, only P2M and M2P modes are supported and implemented. + */ + + DMA_HANDLE handle = NULL; + irqstate_t flags; + int i; + + /* Currently no support for M2M or 2D addressing modes. + * TODO: Remove when support is added! + */ + + DEBUGASSERT(type != GPDMA_TTYPE_M2M_LINEAR); + DEBUGASSERT(type != GPDMA_TTYPE_2D); + + flags = enter_critical_section(); + + if (type == GPDMA_TTYPE_M2P || type == GPDMA_TTYPE_P2M) + { + for (i = 0; i < (sizeof(g_chan) / sizeof(struct gpdma_ch_s)); i++) + { + struct gpdma_ch_s *chan = &g_chan[i]; + + if (chan->free && chan->channel <= 3) + { + chan->free = false; + chan->type = type; + handle = (DMA_HANDLE)chan; + break; + } + } + } + + leave_critical_section(flags); + + if (handle == NULL) + { + /* Failed to allocate channel. */ + + dmainfo("No available DMA chan for transfer type=%" PRIu8 "\n", + type); + } + + return handle; +} + +/**************************************************************************** + * 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) +{ + struct gpdma_ch_s *chan = (struct gpdma_ch_s *)handle; + + DEBUGASSERT(handle != NULL); + + chan->free = true; +} + +/**************************************************************************** + * Name: stm32_dmasetup + * + * Description: + * Configure DMA before using + * + * Input Parameters: + * TODO: Figure out what the input parameter needs to be!!! H7 and F7 have + * very different implementations. + * + ****************************************************************************/ + +void stm32_dmasetup(DMA_HANDLE handle, struct stm32_gpdma_cfg_s *cfg) +{ + struct gpdma_ch_s *chan = (struct gpdma_ch_s *)handle; + + DEBUGASSERT(handle != NULL); + + /* Store the configuration so it can be referenced later by start. */ + + chan->cfg = *cfg; + + /* Drivers using DMA should manage channel usage. If a DMA request is not + * made on an error or an abort occurs, the driver should stop the DMA. If + * it fails to do so, we can not just hang waiting on the HW that will not + * change state. + * + * If at the end of waiting the HW is still not ready, there is a HW + * or software usage problem. + */ + + gpdma_ch_disable(chan); + + /* if ((gpdmach_getreg(chan, CH_CXCR_OFFSET) & GPDMA_CXCR_EN) != 0) + * { + * gpdma_ch_abort(chan); + * } + */ + + /* Clear any unhandled flags from previous transactions */ + + /* gpdmach_putreg(chan, CH_CXFCR_OFFSET, 0x7f << 8); */ + + if (cfg->mode & GPDMACFG_MODE_CIRC) + { + gpdma_setup_circular(chan, cfg); + } + else + { + gpdma_setup(chan, 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) +{ + struct gpdma_ch_s *chan = (struct gpdma_ch_s *)handle; + uint32_t cr; + + DEBUGASSERT(handle != NULL); + + /* Save the callback info. This will be invoked when the DMA completes */ + + chan->callback = callback; + chan->arg = arg; + + /* Activate channel by setting ENABLE bin in the GPDMA_CXCR register. + * As soon as the channel is enabled, it can serve any DMA request from the + * peripheral connected to the stream. + */ + + cr = gpdmach_getreg(chan, CH_CXCR_OFFSET); + cr |= GPDMA_CXCR_EN; + + /* In normal mode, interrupt at either half or full completion. In circular + * mode, always interrupt on wrap and optionally interrupt at halfway. + */ + + if (chan->cfg.mode & (GPDMACFG_MODE_CIRC)) + { + /* In circular mode, when transfer completes it resets and starts + * again. The transfer-complete interrupt is thus always enabled, and + * the half-complete interrupt can be used to determine when the buffer + * is half-full. + */ + + cr |= ((half ? GPDMA_CXCR_HTIE : 0) | + GPDMA_CXCR_TCIE | + GPDMA_CXCR_DTEIE); + } + else + { + /* 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. + */ + + cr |= (half ? (GPDMA_CXCR_HTIE | GPDMA_CXCR_DTEIE) : + (GPDMA_CXCR_TCIE | GPDMA_CXCR_DTEIE)); + } + + gpdmach_putreg(chan, CH_CXCR_OFFSET, cr); +} + +/**************************************************************************** + * 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) +{ + struct gpdma_ch_s *chan = (struct gpdma_ch_s *)handle; + gpdma_ch_disable(chan); + + /* gpdma_ch_abort(chan); */ +} + +#ifdef CONFIG_STM32H5_DMACAPABLE +/**************************************************************************** + * 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. + * + * NOTE: No implementation yet. + * + ****************************************************************************/ + +bool stm32_dmacapable(DMA_HANDLE handle, stm32_gpdma_cfg_s *cfg) +{ +# warning "stm32_dma_capable not implemented yet" + return false; +} +#endif + +#ifdef CONFIG_DEBUG_DMA_INFO +/**************************************************************************** + * Name: stm32_dmadump + * + * Description: + * Dump previously sampled DMA register contents + * + * Assumptions: + * - DMA handle allocated by stm32_dmachannel() + * + * NOTE: No implementation yet. + * + ****************************************************************************/ + +static void stm32_dmadump(DMA_HANDLE handle, const char *msg) +{ +# warning "stm32_dma_dump not implemented yet!" +} +#endif + +#ifdef CONFIG_DEBUG_DMA_INFO +/**************************************************************************** + * Name: stm32_dmasample + * + * Description: + * Sample DMA register contents + * + * Input Parameters: + * TODO: Figure these out!! Not sure if I need the struct like F7 or not? + * + * Assumptions: + * - DMA handle allocated by stm32_dmachannel() + * + ****************************************************************************/ + +void stm32_dmasample(DMA_HANDLE handle, struct stm32_dmaregs_s *regs) +{ + #warning "stm32_dmasample() not implemented yet!" +} +#endif \ No newline at end of file diff --git a/arch/arm/src/stm32h5/stm32_dma.h b/arch/arm/src/stm32h5/stm32_dma.h index abdaa2e81ef..63e554dca2b 100644 --- a/arch/arm/src/stm32h5/stm32_dma.h +++ b/arch/arm/src/stm32h5/stm32_dma.h @@ -30,12 +30,150 @@ #include <nuttx/config.h> #include <sys/types.h> +#include <stdint.h> + #include "hardware/stm32_gpdma.h" +#ifdef CONFIG_DEBUG_DMA_INFO +# error "CONFIG_DEBUG_DMA_INFO not yet implemented." +# undef CONFIG_DEBUG_DMA_INFO +#endif + +#ifdef CONFIG_STM32H5_DMACAPABLE +# error "CONFIG_STM32H5_DMACAPABLE not yet implemented." +# undef CONFIG_STM32H5_DMACAPABLE +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* These definitions provide the bit encoding of the 'status' parameter + * passed to the DMA callback function (see dma_callback_t). + */ + +#define DMA_STATUS_TCF (1 << 0) /* Transfer Complete */ +#define DMA_STATUS_HTF (1 << 1) /* Half Transfer */ +#define DMA_STATUS_DTEF (1 << 2) /* Data transfer error */ +#define DMA_STATUS_ULEF (1 << 3) /* Update link transfer error */ +#define DMA_STATUS_USEF (1 << 4) /* User setting error */ +#define DMA_STATUS_SUSPF (1 << 5) /* Completed suspension flag */ +#define DMA_STATUS_TOF (1 << 6) /* Trigger overrun flag */ + +#define DMA_STATUS_ERROR (DMA_STATUS_DTEF | DMA_STATUS_ULEF | DMA_STATUS_USEF | DMA_STATUS_TOF) +#define DMA_STATUS_SUCCESS (DMA_STATUS_TCF | DMA_STATUS_HTEF) + +/* GPDMA Mode Flags: WARNING!! NOT YET IMPLEMENTED! */ + +#define GPDMACFG_MODE_CIRC (1 << 0) /* Enable Circular mode */ +#define GPDMACFG_MODE_PFC (1 << 1) /* Enable Peripheral flow control */ +#define GPDMACFG_MODE_DB (1 << 2) /* Enable Double buffer mode */ + +/* Channel priority level + * Refer to PRIO field in GPDMA_CxCR register description (RM0481) + */ + +#define GPDMACFG_PRIO_LL (0) /* Low priority, low weight */ +#define GPDMACFG_PRIO_LM (1) /* Low priority, mid weight */ +#define GPMDACFG_PRIO_LH (2) /* Low priority, high weight */ +#define GPDMACFG_PRIO_H (3) /* High priority */ + /**************************************************************************** * Public Types ****************************************************************************/ +/* GPDMA transfer type enumeration */ + +enum gpdma_ttype_e +{ + /* Peripheral-to-memory transfer */ + + GPDMA_TTYPE_P2M = 0, + + /* Memory-to-peripheral transfer */ + + GPDMA_TTYPE_M2P, + + /* Memory-to-memory transfer, linear addressing */ + + GPDMA_TTYPE_M2M_LINEAR, + + /* 2D Addressing needed (NOT IMPLEMENTED YET) */ + + GPDMA_TTYPE_2D +}; + +#ifdef CONFIG_DEBUG_DMA_INFO +struct stm32_gpdma_reg_s +{ + uint32_t cxlbar; + uint32_t cxfcr; + uint32_t cxsr; + uint32_t cxcr; + uint32_t cxtr1; + uint32_t cxtr2; + uint32_t cxbr1; + uint32_t cxsar; + uint32_t cxdar; + uint32_t cxtr3; + uint32_t cxbr2; + uint32_t cxllr; +}; +#endif + +struct stm32_gpdma_cfg_s +{ + uint32_t src_addr; + uint32_t dest_addr; + + /* CxTR1 register for specified channel. */ + + uint32_t tr1; + + /* request: Accepts GPDMA_CXTR2_SWREQ, GPDMA_CXTR2_DREQ, and + * GPDMA_CXTR2_REQSEL(r) for r given by GPDMA_REQ_x + * macros defined in hardware/stm32h56x_dmasigmap.h + */ + + uint16_t request; + + /* Number of transfers, in units of the data width + * specified in tr1. + */ + + uint16_t ntransfers; + + /* Priority level: refer to GPDMACFG_PRIO defines above */ + + uint8_t priority; + + /* mode flags, refer to GPDMACFG_MODE_X defines above. */ + + uint8_t mode; +}; + +/* DMA_HANDLE Provides an opaque reference that can be used to represent a + * DMA stream. + */ + +typedef 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 to the DMA channel or stream + * status - A bit encoded value that provides the completion status. See + * the DMA_STATUS_* 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 ****************************************************************************/ @@ -55,6 +193,175 @@ extern "C" * Public Function Prototypes ****************************************************************************/ +/**************************************************************************** + * Name: stm32_dmachannel + * + * Description: + * Allocate a DMA channel based on provided transfer type. The driver will + * return the first available DMA channel compatible with the transfer type + * + * Input Parameters: + * type - Type of DMA transfer required + * + * Returned Value: + * On 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(enum gpdma_ttype_e type); + +/**************************************************************************** + * Name: stm32_dmafree + * + * Description: + * Release a DMA channel. + * + * 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 + * + * Input Parameters: + * TODO: Figure out what the input parameter needs to be!!! H7 and F7 have + * very different implementations. + * + ****************************************************************************/ + +void stm32_dmasetup(DMA_HANDLE handle, struct stm32_gpdma_cfg_s *cfg); + +/**************************************************************************** + * Name: stm32_dmastart + * + * Description: + * Start the DMA transfer. NOTE: The DMA module does *NOT* perform any + * cache operations. It is the responsibility of the DMA client to clean + * DMA buffers after starting of the DMA TX operations. + * + * 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); + +/**************************************************************************** + * 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); + +/**************************************************************************** + * Name: stm32_dmaresidual + * + * Description: + * Returns the number of bytes remaining to be transferred + * + * Assumptions: + * - DMA handle allocated by stm32_dmachannel() + * + ****************************************************************************/ + +size_t stm32_dmaresidual(DMA_HANDLE handle); + +/**************************************************************************** + * Name: stm32_dmacapable + * + * Description: + * Check if the DMA controller can transfer data to/from given memory + * address with the given configuration. 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: + * + * maddr - starting memory address + * count - number of unit8 or uint16 or uint32 items as defined by MSIZE + * of ccr. + * ccr - DMA stream configuration register + * + * Returned Value: + * True, if transfer is possible. + * + ****************************************************************************/ + +#ifdef CONFIG_STM32H5_DMACAPABLE +bool stm32_dmacapable(DMA_HANDLE handle, struct stm32_gpdma_cfg_s *cfg); +#else +# define stm32_dmacapable(handle, cfg) (true) +#endif + +/**************************************************************************** + * Name: stm32_dmasample + * + * Description: + * Sample DMA register contents + * + * Input Parameters: + * TODO: Figure these out!! Not sure if I need the struct like F7 or not? + * + * Assumptions: + * - DMA handle allocated by stm32_dmachannel() + * + ****************************************************************************/ + +#ifdef CONFIG_DEBUG_DMA_INFO +void stm32_dmasample(DMA_HANDLE handle, struct stm32_dmaregs_s *regs); +#else +# define stm32_dmasample(handle,regs) +#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); +#else +# define stm32_dmadump(handle,msg) +#endif + #undef EXTERN #if defined(__cplusplus) } diff --git a/arch/arm/src/stm32h5/stm32h5xx_rcc.c b/arch/arm/src/stm32h5/stm32h5xx_rcc.c index a72f2b89d31..7421c9f75e8 100644 --- a/arch/arm/src/stm32h5/stm32h5xx_rcc.c +++ b/arch/arm/src/stm32h5/stm32h5xx_rcc.c @@ -89,13 +89,13 @@ static inline void rcc_enableahb1(void) regval = getreg32(STM32_RCC_AHB1ENR); -#ifdef CONFIG_STM32H5_GPDMA1 +#ifdef CONFIG_STM32H5_DMA1 /* DMA 1 clock enable */ regval |= RCC_AHB1ENR_GPDMA1EN; #endif -#ifdef CONFIG_STM32H5_GPDMA2 +#ifdef CONFIG_STM32H5_DMA2 /* DMA 2 clock enable */ regval |= RCC_AHB1ENR_GPDMA2EN;