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.
This commit is contained in:
kywwilson11 2025-07-08 15:55:05 -05:00 committed by Alan C. Assis
parent 156ef05205
commit 0c1f9d482d
9 changed files with 1589 additions and 178 deletions

View file

@ -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

View file

@ -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)

View file

@ -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 */

View file

@ -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 */

View file

@ -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;
if (priv->circular)
{
setbits |= ADC_CFGR_DMACFG;
setbits |= ADC_CFGR_CONT;
}
else
{
clrbits |= ADC_CFGR_DMACFG;
clrbits |= ADC_CFGR_CONT;
}
}
#endif
/* Disable continuous mode */
#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;

View file

@ -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

View file

@ -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

View file

@ -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)
}

View file

@ -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;