This is an automated email from the ASF dual-hosted git repository. xiaoxiang 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 4995c836f3 arch/arm/stm32h5: Add DMA Support to STM32H5 Serial Driver 4995c836f3 is described below commit 4995c836f30fdfb338bef3a25680e2595b6ebc4e Author: kywwilson11 <kwil...@2g-eng.com> AuthorDate: Fri Jul 25 11:49:22 2025 -0500 arch/arm/stm32h5: Add DMA Support to STM32H5 Serial Driver Style fixes. --- arch/arm/src/stm32h5/Kconfig | 105 ++++++ .../arm/src/stm32h5/hardware/stm32h56x_dmasigmap.h | 8 +- arch/arm/src/stm32h5/stm32_dma.c | 29 +- arch/arm/src/stm32h5/stm32_serial.c | 375 +++++++++------------ arch/arm/src/stm32h5/stm32_uart.h | 3 +- 5 files changed, 301 insertions(+), 219 deletions(-) diff --git a/arch/arm/src/stm32h5/Kconfig b/arch/arm/src/stm32h5/Kconfig index 5dee7bc065..5899576daf 100644 --- a/arch/arm/src/stm32h5/Kconfig +++ b/arch/arm/src/stm32h5/Kconfig @@ -4397,6 +4397,13 @@ config LPUART1_RS485_DIR_POLARITY Polarity of DIR pin for RS-485 on LPUART1. Set to state on DIR pin which enables TX (0 - low / nTXEN, 1 - high / TXEN). +config LPUART1_RXDMA + bool "LPUART1 RX DMA" + default n + depends on STM32H5_LPUART1 && (STM32H5_DMA1 || STM32H5_DMA2) + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + endif # LPUART1_SERIALDRIVER choice @@ -4431,6 +4438,13 @@ config USART1_RS485_DIR_POLARITY Polarity of DIR pin for RS-485 on USART1. Set to state on DIR pin which enables TX (0 - low / nTXEN, 1 - high / TXEN). +config USART1_RXDMA + bool "USART1 RX DMA" + default n + depends on STM32H5_USART1 && (STM32H5_DMA1 || STM32H5_DMA2) + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + endif # USART1_SERIALDRIVER choice @@ -4465,6 +4479,13 @@ config USART2_RS485_DIR_POLARITY Polarity of DIR pin for RS-485 on USART2. Set to state on DIR pin which enables TX (0 - low / nTXEN, 1 - high / TXEN). +config USART2_RXDMA + bool "USART2 RX DMA" + default n + depends on STM32H5_USART2 && (STM32H5_DMA1 || STM32H5_DMA2) + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + endif # USART2_SERIALDRIVER choice @@ -4499,6 +4520,13 @@ config USART3_RS485_DIR_POLARITY Polarity of DIR pin for RS-485 on USART3. Set to state on DIR pin which enables TX (0 - low / nTXEN, 1 - high / TXEN). +config USART3_RXDMA + bool "USART3 RX DMA" + default n + depends on STM32H5_USART3 && (STM32H5_DMA1 || STM32H5_DMA2) + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + endif # USART3_SERIALDRIVER choice @@ -4533,6 +4561,13 @@ config UART4_RS485_DIR_POLARITY Polarity of DIR pin for RS-485 on UART4. Set to state on DIR pin which enables TX (0 - low / nTXEN, 1 - high / TXEN). +config UART4_RXDMA + bool "UART4 RX DMA" + default n + depends on STM32H5_UART4 && (STM32H5_DMA1 || STM32H5_DMA2) + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + endif # UART4_SERIALDRIVER choice @@ -4567,6 +4602,13 @@ config UART5_RS485_DIR_POLARITY Polarity of DIR pin for RS-485 on UART5. Set to state on DIR pin which enables TX (0 - low / nTXEN, 1 - high / TXEN). +config UART5_RXDMA + bool "UART5 RX DMA" + default n + depends on STM32H5_UART5 && (STM32H5_DMA1 || STM32H5_DMA2) + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + endif # UART5_SERIALDRIVER choice @@ -4601,6 +4643,13 @@ config USART6_RS485_DIR_POLARITY Polarity of DIR pin for RS-485 on USART6. Set to state on DIR pin which enables TX (0 - low / nTXEN, 1 - high / TXEN). +config USART6_RXDMA + bool "USART6 RX DMA" + default n + depends on STM32H5_USART6 && (STM32H5_DMA1 || STM32H5_DMA2) + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + endif # USART6_SERIALDRIVER if UART7_SERIALDRIVER @@ -4623,6 +4672,13 @@ config UART7_RS485_DIR_POLARITY Polarity of DIR pin for RS-485 on UART7. Set to state on DIR pin which enables TX (0 - low / nTXEN, 1 - high / TXEN). +config UART7_RXDMA + bool "UART7 RX DMA" + default n + depends on STM32H5_UART7 && (STM32H5_DMA1 || STM32H5_DMA2) + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + endif # UART7_SERIALDRIVER if UART8_SERIALDRIVER @@ -4645,6 +4701,13 @@ config UART8_RS485_DIR_POLARITY Polarity of DIR pin for RS-485 on UART8. Set to state on DIR pin which enables TX (0 - low / nTXEN, 1 - high / TXEN). +config UART8_RXDMA + bool "UART8 RX DMA" + default n + depends on STM32H5_UART8 && (STM32H5_DMA1 || STM32H5_DMA2) + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + endif # UART8_SERIALDRIVER if UART9_SERIALDRIVER @@ -4667,6 +4730,13 @@ config UART9_RS485_DIR_POLARITY Polarity of DIR pin for RS-485 on UART9. Set to state on DIR pin which enables TX (0 - low / nTXEN, 1 - high / TXEN). +config UART9_RXDMA + bool "UART9 RX DMA" + default n + depends on STM32H5_UART9 && (STM32H5_DMA1 || STM32H5_DMA2) + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + endif # UART9_SERIALDRIVER if USART10_SERIALDRIVER @@ -4689,6 +4759,13 @@ config USART10_RS485_DIR_POLARITY Polarity of DIR pin for RS-485 on USART10. Set to state on DIR pin which enables TX (0 - low / nTXEN, 1 - high / TXEN). +config USART10_RXDMA + bool "USART10 RX DMA" + default n + depends on STM32H5_USART10 && (STM32H5_DMA1 || STM32H5_DMA2) + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + endif # USART10_SERIALDRIVER if USART11_SERIALDRIVER @@ -4711,6 +4788,13 @@ config USART11_RS485_DIR_POLARITY Polarity of DIR pin for RS-485 on USART11. Set to state on DIR pin which enables TX (0 - low / nTXEN, 1 - high / TXEN). +config USART11_RXDMA + bool "USART11 RX DMA" + default n + depends on STM32H5_USART11 && (STM32H5_DMA1 || STM32H5_DMA2) + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + endif # USART11_SERIALDRIVER if UART12_SERIALDRIVER @@ -4733,12 +4817,33 @@ config UART12_RS485_DIR_POLARITY Polarity of DIR pin for RS-485 on UART12. Set to state on DIR pin which enables TX (0 - low / nTXEN, 1 - high / TXEN). +config UART12_RXDMA + bool "UART12 RX DMA" + default n + depends on STM32H5_UART12 && (STM32H5_DMA1 || STM32H5_DMA2) + ---help--- + In high data rate usage, Rx DMA may eliminate Rx overrun errors + endif # UART12_SERIALDRIVER if STM32H5_SERIALDRIVER comment "Serial Driver Configuration" +config STM32H5_SERIAL_RXDMA_BUFFER_SIZE + int "Rx DMA buffer size" + default 32 + depends on USART1_RXDMA || USART2_RXDMA || USART3_RXDMA || USART6_RXDMA || USART10_RXDMA || \ + USART11_RXDMA || UART4_RXDMA || UART5_RXDMA || UART7_RXDMA || UART8_RXDMA || \ + UART9_RXDMA || UART12_RXDMA || LPUART1_RXDMA + ---help--- + The DMA buffer size when using RX DMA to emulate a FIFO. + + When streaming data, the generic serial layer will be called + every time the FIFO receives half this number of bytes. + + Value given here will be rounded up to next multiple of 32 bytes. + config STM32H5_SERIAL_DISABLE_REORDERING bool "Disable reordering of ttySx devices." depends on STM32H5_USART1 || STM32H5_USART2 || STM32H5_USART3 || STM32H5_UART4 || STM32H5_UART5 diff --git a/arch/arm/src/stm32h5/hardware/stm32h56x_dmasigmap.h b/arch/arm/src/stm32h5/hardware/stm32h56x_dmasigmap.h index 9a65403226..3fc1c4fb9d 100644 --- a/arch/arm/src/stm32h5/hardware/stm32h56x_dmasigmap.h +++ b/arch/arm/src/stm32h5/hardware/stm32h56x_dmasigmap.h @@ -70,10 +70,10 @@ #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_USART10_RX (39) +#define GPDMA_REQ_USART10_TX (40) +#define GPDMA_REQ_USART11_RX (41) +#define GPDMA_REQ_USART11_TX (42) #define GPDMA_REQ_UART12_RX (43) #define GPDMA_REQ_UART12_TX (44) #define GPDMA_REQ_LPUART1_RX (45) diff --git a/arch/arm/src/stm32h5/stm32_dma.c b/arch/arm/src/stm32h5/stm32_dma.c index 2745c5b464..3d10262eb4 100644 --- a/arch/arm/src/stm32h5/stm32_dma.c +++ b/arch/arm/src/stm32h5/stm32_dma.c @@ -51,7 +51,7 @@ * off this in this file. */ -#define CH_BASE_OFFSET(ch) (0x80*(ch)) +#define CH_BASE_OFFSET(ch) (0x80*(ch)) #define CH_CXLBAR_OFFSET 0x50 #define CH_CXFCR_OFFSET 0x5C #define CH_CXSR_OFFSET 0x60 @@ -694,6 +694,31 @@ void stm32_dmastop(DMA_HANDLE handle) /* gpdma_ch_abort(chan); */ } +/**************************************************************************** + * Name: stm32_dmaresidual + * + * Description: + * Returns the number of data beats remaining to transfer in the current + * STM32H5 GPDMA block. This reads the BNDT[15:0] field from the + * GPDMA_CxBR1 register, which indicates how many beats are left in the + * programmed transfer. + * + * Assumptions: + * - DMA handle was allocated by stm32_dmachannel(). + * - The handle refers to a valid STM32H5 GPDMA channel. + * + ****************************************************************************/ + +size_t stm32_dmaresidual(DMA_HANDLE handle) +{ + struct gpdma_ch_s *chan = (struct gpdma_ch_s *)handle; + uint32_t br1 = getreg32(chan->base + CH_CXBR1_OFFSET); + + /* BNDT[15:0] = beats remaining in current block transfer */ + + return (size_t)(br1 & GPDMA_CXBR1_BNDT_MASK); +} + #ifdef CONFIG_STM32H5_DMACAPABLE /**************************************************************************** * Name: stm32_dmacapable @@ -760,4 +785,4 @@ void stm32_dmasample(DMA_HANDLE handle, struct stm32_dmaregs_s *regs) { #warning "stm32_dmasample() not implemented yet!" } -#endif \ No newline at end of file +#endif diff --git a/arch/arm/src/stm32h5/stm32_serial.c b/arch/arm/src/stm32h5/stm32_serial.c index c27d9b7567..5e032cc6ad 100644 --- a/arch/arm/src/stm32h5/stm32_serial.c +++ b/arch/arm/src/stm32h5/stm32_serial.c @@ -52,13 +52,7 @@ #include "chip.h" #include "stm32_gpio.h" #include "stm32_uart.h" - -/* DMA has not been implemented for H5 chip yet, so disable any serial DMA */ - -#ifdef SERIAL_HAVE_DMA -# error Serial DMA not implemented for STM32H5 chips. -#undef SERIAL_HAVE_DMA -#endif +#include "stm32_dma.h" #include "stm32_rcc.h" #include "arm_internal.h" @@ -81,22 +75,18 @@ * 5 X */ +#ifdef CONFIG_SERIAL_IFLOWCONTROL +# warning STM32H5 Serial IFLOWCONTROL is untested +#endif + #ifdef SERIAL_HAVE_DMA /* Verify that DMA has been enabled and the DMA channel has been defined. */ -# if defined(CONFIG_USART2_RXDMA) || defined(CONFIG_USART3_RXDMA) -# if !defined(CONFIG_STM32H5_DMA1) && !defined(CONFIG_STM32H5_DMAMUX) -# error STM32H5 USART2/3 receive DMA requires CONFIG_STM32H5_DMA1 -# endif -# endif - -# if defined(CONFIG_UART4_RXDMA) || defined(CONFIG_UART5_RXDMA) -# if !defined(CONFIG_STM32H5_DMA2) && !defined(CONFIG_STM32H5_DMAMUX) -# error STM32H5 UART4/5 receive DMA requires CONFIG_STM32H5_DMA2 -# endif -# endif +#if !defined(CONFIG_STM32H5_DMA1) && !defined(CONFIG_STM32H5_DMA2) +# error STM32H5 Serial DMA requires one of DMA1 or DMA2 to be enabled +#endif /* Currently RS-485 support cannot be enabled when RXDMA is in use due to * lack of testing - RS-485 support was developed on STM32F1x @@ -110,40 +100,6 @@ # error "RXDMA and RS-485 cannot be enabled at the same time for the same U[S]ART" # endif -/* For the L4, there are alternate DMA channels for USART1. - * Logic in the board.h file make the DMA channel selection by defining - * the following in the board.h file. - */ - -# if defined(CONFIG_USART1_RXDMA) && !defined(DMAMAP_USART1_RX) -# error "USART1 DMA channel not defined (DMAMAP_USART1_RX)" -# endif - -/* UART2-5 have no alternate channels without DMAMUX */ - -# ifndef CONFIG_STM32H5_HAVE_DMAMUX -# define DMAMAP_USART2_RX DMACHAN_USART2_RX -# define DMAMAP_USART3_RX DMACHAN_USART3_RX -# define DMAMAP_UART4_RX DMACHAN_UART4_RX -# define DMAMAP_UART5_RX DMACHAN_UART5_RX -# endif - -# if defined(CONFIG_USART2_RXDMA) && !defined(DMAMAP_USART2_RX) -# error "USART2 DMA channel not defined (DMAMAP_USART2_RX)" -# endif - -# if defined(CONFIG_USART3_RXDMA) && !defined(DMAMAP_USART3_RX) -# error "USART3 DMA channel not defined (DMAMAP_USART3_RX)" -# endif - -# if defined(CONFIG_UART4_RXDMA) && !defined(DMAMAP_UART4_RX) -# error "UART4 DMA channel not defined (DMAMAP_UART4_RX)" -# endif - -# if defined(CONFIG_UART5_RXDMA) && !defined(DMAMAP_UART5_RX) -# error "UART5 DMA channel not defined (DMAMAP_UART5_RX)" -# endif - /* The DMA buffer size when using RX DMA to emulate a FIFO. * * When streaming data, the generic serial layer will be called @@ -161,31 +117,6 @@ # define RXDMA_BUFFER_SIZE ((CONFIG_STM32H5_SERIAL_RXDMA_BUFFER_SIZE + 31) & ~31) # endif -/* DMA priority */ - -# ifndef CONFIG_USART_DMAPRIO -# define CONFIG_USART_DMAPRIO DMA_CCR_PRIMED -# endif -# if (CONFIG_USART_DMAPRIO & ~DMA_CCR_PL_MASK) != 0 -# error "Illegal value for CONFIG_USART_DMAPRIO" -# endif - -/* DMA control words */ - -# define SERIAL_DMA_CONTROL_WORD \ - (DMA_CCR_CIRC | \ - DMA_CCR_MINC | \ - DMA_CCR_PSIZE_8BITS | \ - DMA_CCR_MSIZE_8BITS | \ - CONFIG_USART_DMAPRIO) -# ifdef CONFIG_SERIAL_IFLOWCONTROL -# define SERIAL_DMA_IFLOW_CONTROL_WORD \ - (DMA_CCR_MINC | \ - DMA_CCR_PSIZE_8BITS | \ - DMA_CCR_MSIZE_8BITS | \ - CONFIG_USART_DMAPRIO) -# endif - #endif /* Power management definitions */ @@ -243,9 +174,6 @@ struct stm32_serial_s uint8_t parity; /* 0=none, 1=odd, 2=even */ uint8_t bits; /* Number of bits (7 or 8) */ bool stopbits2; /* True: Configure with 2 stop bits instead of 1 */ -#ifdef CONFIG_SERIAL_IFLOWCONTROL - bool iflow; /* input flow control (RTS) enabled */ -#endif #ifdef CONFIG_SERIAL_OFLOWCONTROL bool oflow; /* output flow control (CTS) enabled */ #endif @@ -254,15 +182,11 @@ struct stm32_serial_s const uint8_t parity; /* 0=none, 1=odd, 2=even */ const uint8_t bits; /* Number of bits (7 or 8) */ const bool stopbits2; /* True: Configure with 2 stop bits instead of 1 */ -#ifdef CONFIG_SERIAL_IFLOWCONTROL - const bool iflow; /* input flow control (RTS) enabled */ -#endif #ifdef CONFIG_SERIAL_OFLOWCONTROL const bool oflow; /* output flow control (CTS) enabled */ #endif const uint32_t baud; /* Configured baud */ #endif - const uint8_t irq; /* IRQ associated with this USART */ const uint32_t apbclock; /* PCLK 1 or 2 frequency */ const uint32_t usartbase; /* Base address of USART registers */ @@ -274,10 +198,7 @@ struct stm32_serial_s #ifdef CONFIG_SERIAL_OFLOWCONTROL const uint32_t cts_gpio; /* U[S]ART CTS GPIO pin configuration */ #endif - -#ifdef SERIAL_HAVE_DMA - const unsigned int rxdma_channel; /* DMA channel assigned */ -#endif + const bool iflow; /* input flow control (RTS) enabled */ /* RX DMA state */ @@ -288,7 +209,8 @@ struct stm32_serial_s bool rxdmasusp; /* Rx DMA suspended */ #endif uint32_t rxdmanext; /* Next byte in the DMA buffer to be read */ - char *const rxfifo; /* Receive DMA buffer */ + uint16_t rxdma_req; /* GPDMA Request number */ + char *const rxfifo; /* Receive DMA buffer */ #endif #ifdef HAVE_RS485 @@ -543,8 +465,8 @@ static struct stm32_serial_s g_lpuart1priv = .rts_gpio = GPIO_LPUART1_RTS, # endif # ifdef CONFIG_LPUART1_RXDMA - .rxdma_channel = DMAMAP_LPUSART_RX, .rxfifo = g_lpuart1rxfifo, + .rxdma_req = GPDMA_REQ_LPUART1_RX, # endif # ifdef CONFIG_USART1_RS485 @@ -604,8 +526,8 @@ static struct stm32_serial_s g_usart1priv = .rts_gpio = GPIO_USART1_RTS, # endif # ifdef CONFIG_USART1_RXDMA - .rxdma_channel = DMAMAP_USART1_RX, .rxfifo = g_usart1rxfifo, + .rxdma_req = GPDMA_REQ_USART1_RX, # endif # ifdef CONFIG_USART1_RS485 @@ -667,8 +589,8 @@ static struct stm32_serial_s g_usart2priv = .rts_gpio = GPIO_USART2_RTS, # endif # ifdef CONFIG_USART2_RXDMA - .rxdma_channel = DMAMAP_USART2_RX, .rxfifo = g_usart2rxfifo, + .rxdma_req = GPDMA_REQ_USART2_RX, # endif # ifdef CONFIG_USART2_RS485 @@ -730,8 +652,8 @@ static struct stm32_serial_s g_usart3priv = .rts_gpio = GPIO_USART3_RTS, # endif # ifdef CONFIG_USART3_RXDMA - .rxdma_channel = DMAMAP_USART3_RX, .rxfifo = g_usart3rxfifo, + .rxdma_req = GPDMA_REQ_USART3_RX, # endif # ifdef CONFIG_USART3_RS485 @@ -793,8 +715,8 @@ static struct stm32_serial_s g_uart4priv = .tx_gpio = GPIO_UART4_TX, .rx_gpio = GPIO_UART4_RX, # ifdef CONFIG_UART4_RXDMA - .rxdma_channel = DMAMAP_UART4_RX, .rxfifo = g_uart4rxfifo, + .rxdma_req = GPDMA_REQ_UART4_RX, # endif # ifdef CONFIG_UART4_RS485 @@ -856,8 +778,8 @@ static struct stm32_serial_s g_uart5priv = .tx_gpio = GPIO_UART5_TX, .rx_gpio = GPIO_UART5_RX, # ifdef CONFIG_UART5_RXDMA - .rxdma_channel = DMAMAP_UART5_RX, .rxfifo = g_uart5rxfifo, + .rxdma_req = GPDMA_REQ_UART5_RX, # endif # ifdef CONFIG_UART5_RS485 @@ -919,8 +841,8 @@ static struct stm32_serial_s g_usart6priv = .rts_gpio = GPIO_USART6_RTS, # endif # ifdef CONFIG_USART6_RXDMA - .rxdma_channel = DMAMAP_USART6_RX, .rxfifo = g_usart6rxfifo, + .rxdma_req = GPDMA_REQ_USART6_RX, # endif # ifdef CONFIG_USART6_RS485 @@ -982,8 +904,8 @@ static struct stm32_serial_s g_uart7priv = .tx_gpio = GPIO_UART7_TX, .rx_gpio = GPIO_UART7_RX, # ifdef CONFIG_UART7_RXDMA - .rxdma_channel = DMAMAP_UART7_RX, .rxfifo = g_uart7rxfifo, + .rxdma_req = GPDMA_REQ_UART7_RX, # endif # ifdef CONFIG_UART7_RS485 @@ -1045,8 +967,8 @@ static struct stm32_serial_s g_uart8priv = .tx_gpio = GPIO_UART8_TX, .rx_gpio = GPIO_UART8_RX, # ifdef CONFIG_UART8_RXDMA - .rxdma_channel = DMAMAP_UART8_RX, .rxfifo = g_uart8rxfifo, + .rxdma_req = GPDMA_REQ_UART8_RX, # endif # ifdef CONFIG_UART8_RS485 @@ -1108,8 +1030,8 @@ static struct stm32_serial_s g_uart9priv = .tx_gpio = GPIO_UART9_TX, .rx_gpio = GPIO_UART9_RX, # ifdef CONFIG_UART9_RXDMA - .rxdma_channel = DMAMAP_UART9_RX, .rxfifo = g_uart9rxfifo, + .rxdma_req = GPDMA_REQ_UART9_RX, # endif # ifdef CONFIG_UART9_RS485 @@ -1171,8 +1093,8 @@ static struct stm32_serial_s g_usart10priv = .rts_gpio = GPIO_USART10_RTS, # endif # ifdef CONFIG_USART10_RXDMA - .rxdma_channel = DMAMAP_USART10_RX, .rxfifo = g_usart10rxfifo, + .rxdma_req = GPDMA_REQ_USART10_RX, # endif # ifdef CONFIG_USART10_RS485 @@ -1234,8 +1156,8 @@ static struct stm32_serial_s g_usart11priv = .rts_gpio = GPIO_USART11_RTS, # endif # ifdef CONFIG_USART11_RXDMA - .rxdma_channel = DMAMAP_USART11_RX, .rxfifo = g_usart11rxfifo, + .rxdma_req = GPDMA_REQ_USART11_RX, # endif # ifdef CONFIG_USART11_RS485 @@ -1297,8 +1219,8 @@ static struct stm32_serial_s g_uart12priv = .tx_gpio = GPIO_UART12_TX, .rx_gpio = GPIO_UART12_RX, # ifdef CONFIG_UART12_RXDMA - .rxdma_channel = DMAMAP_UART12_RX, .rxfifo = g_uart12rxfifo, + .rxdma_req = GPDMA_REQ_UART12_RX, # endif # ifdef CONFIG_UART12_RS485 @@ -2166,69 +2088,88 @@ static int stm32serial_setup(struct uart_dev_s *dev) return OK; } +/**************************************************************************** + * Name: serial_rxdmacfg + * + * Description: + * Generate the required DMA configuration structure for oneshot mode based + * on the serial configuration. + * + * Input Parameters: + * priv - serial instance structure + * cfg - DMA configuration structure + * circular - 0 = oneshot, 1 = circular + * + * Returned Value: + * None + ****************************************************************************/ + + #ifdef SERIAL_HAVE_DMA +static void serial_rxdmacfg(struct stm32_serial_s *priv, + struct stm32_gpdma_cfg_s *cfg) +{ + cfg->src_addr = priv->usartbase + STM32_USART_RDR_OFFSET; + cfg->dest_addr = (uint32_t)priv->rxfifo; + + cfg->request = priv->rxdma_req; + + cfg->priority = GPMDACFG_PRIO_LH; + + cfg->mode = GPDMACFG_MODE_CIRC; + + cfg->ntransfers = RXDMA_BUFFER_SIZE; + + /* Write SDW and DDW to 0 for 8-bit beats */ + + cfg->tr1 = GPDMA_CXTR1_DINC; /* dest-inc, source fixed */ +} +#endif + /**************************************************************************** * Name: stm32serial_dmasetup * * Description: - * Configure the USART baud, bits, parity, etc. This method is called the - * first time that the serial port is opened. + * Configure and start circular RX DMA for USART: + * - Allocate a GPDMA channel + * - Set up source (USART RDR), destination (RX buffer), REQSEL, + * circular mode + * - Program DMA and reset read index + * - Enable USART CR3.DMAR + * - Start DMA with half‑ and full‑transfer callbacks * + * Returned Value: + * OK on success; negative errno on failure. ****************************************************************************/ #ifdef SERIAL_HAVE_DMA static int stm32serial_dmasetup(struct uart_dev_s *dev) { - struct stm32_serial_s *priv = - (struct stm32_serial_s *)dev->priv; - int result; - uint32_t regval; - - /* Do the basic UART setup first, unless we are the console */ + struct stm32_serial_s *priv = (struct stm32_serial_s *)dev->priv; + struct stm32_gpdma_cfg_s dmacfg; + uint32_t regval; + int ret; if (!dev->isconsole) { - result = stm32serial_setup(dev); - if (result != OK) + ret = stm32serial_setup(dev); + if (ret != OK) { - return result; + return ret; } } - /* Acquire the DMA channel. This should always succeed. */ - - priv->rxdma = stm32h5_dmachannel(priv->rxdma_channel); - -#ifdef CONFIG_SERIAL_IFLOWCONTROL - if (priv->iflow) + priv->rxdma = stm32_dmachannel(GPDMA_TTYPE_P2M); + if (!priv->rxdma) { - /* Configure for non-circular DMA reception into the RX FIFO */ - - stm32h5_dmasetup(priv->rxdma, - priv->usartbase + STM32_USART_RDR_OFFSET, - (uint32_t)priv->rxfifo, - RXDMA_BUFFER_SIZE, - SERIAL_DMA_IFLOW_CONTROL_WORD); + return -EBUSY; } - else -#endif - { - /* Configure for circular DMA reception into the RX FIFO */ - stm32h5_dmasetup(priv->rxdma, - priv->usartbase + STM32_USART_RDR_OFFSET, - (uint32_t)priv->rxfifo, - RXDMA_BUFFER_SIZE, - SERIAL_DMA_CONTROL_WORD); - } + serial_rxdmacfg(priv, &dmacfg); - /* Reset our DMA shadow pointer to match the address just - * programmed above. - */ + stm32_dmasetup(priv->rxdma, &dmacfg); priv->rxdmanext = 0; - /* Enable receive DMA for the UART */ - regval = stm32serial_getreg(priv, STM32_USART_CR3_OFFSET); regval |= USART_CR3_DMAR; stm32serial_putreg(priv, STM32_USART_CR3_OFFSET, regval); @@ -2241,8 +2182,8 @@ static int stm32serial_dmasetup(struct uart_dev_s *dev) * in and DMA transfer is stopped. */ - stm32h5_dmastart(priv->rxdma, stm32serial_dmarxcallback, - (void *)priv, false); + stm32_dmastart(priv->rxdma, stm32serial_dmarxcallback, + (void *)priv, false); } else #endif @@ -2252,8 +2193,8 @@ static int stm32serial_dmasetup(struct uart_dev_s *dev) * worth of time to claim bytes before they are overwritten. */ - stm32h5_dmastart(priv->rxdma, stm32serial_dmarxcallback, - (void *)priv, true); + stm32_dmastart(priv->rxdma, stm32serial_dmarxcallback, + (void *)priv, true); } return OK; @@ -2347,11 +2288,13 @@ static void stm32serial_dmashutdown(struct uart_dev_s *dev) /* Stop the DMA channel */ - stm32h5_dmastop(priv->rxdma); + stm32_dmastop(priv->rxdma); + + priv->rxenable = false; /* Release the DMA channel */ - stm32h5_dmafree(priv->rxdma); + stm32_dmafree(priv->rxdma); priv->rxdma = NULL; } #endif @@ -3131,43 +3074,64 @@ static bool stm32serial_rxflowcontrol(struct uart_dev_s *dev, * Name: stm32serial_dmareceive * * Description: - * Called (usually) from the interrupt level to receive one - * character from the USART. Error bits associated with the - * receipt are provided in the return 'status'. + * Retrieve one character from the RX FIFO filled by circular DMA. Also + * report any USART error flags in *status. * ****************************************************************************/ #ifdef SERIAL_HAVE_DMA static int stm32serial_dmareceive(struct uart_dev_s *dev, - unsigned int *status) + unsigned int *status) { struct stm32_serial_s *priv = (struct stm32_serial_s *)dev->priv; - int c = 0; + unsigned int next; + int ch = -1; + uint32_t sr; + + /* 1) Capture USART error flags */ - if (stm32serial_dmanextrx(priv) != priv->rxdmanext) + sr = getreg32(priv->usartbase + STM32_USART_ISR_OFFSET); + *status = sr & (USART_ISR_ORE | USART_ISR_NF | + USART_ISR_FE | USART_ISR_PE); + + /* 2) Where will DMA write the next byte? */ + + next = stm32serial_dmanextrx(priv); + + /* 3) Pull one byte if available */ + + if (next != priv->rxdmanext) { - c = priv->rxfifo[priv->rxdmanext]; + ch = priv->rxfifo[priv->rxdmanext++]; - priv->rxdmanext++; - if (priv->rxdmanext == RXDMA_BUFFER_SIZE) + /* 4) End‑of‑buffer wrap or flow‑control pause */ + + if (priv->rxdmanext >= RXDMA_BUFFER_SIZE) { #ifdef CONFIG_SERIAL_IFLOWCONTROL if (priv->iflow) { - /* RX DMA buffer full. RX paused, RTS line pulled up to prevent - * more input data from other end. - */ + /* Pause DMA callbacks */ + + stm32serial_dmarxint(&priv->dev, false); + + /* Assert RTS to halt sender */ + + (void)stm32serial_rxflowcontrol(&priv->dev, + RXDMA_BUFFER_SIZE, true); } else #endif { + /* Simply wrap to buffer start */ + priv->rxdmanext = 0; } } } - return c; + return ch; } #endif @@ -3182,28 +3146,10 @@ static int stm32serial_dmareceive(struct uart_dev_s *dev, #if defined(SERIAL_HAVE_DMA) static void stm32serial_dmareenable(struct stm32_serial_s *priv) { -#ifdef CONFIG_SERIAL_IFLOWCONTROL - if (priv->iflow) - { - /* Configure for non-circular DMA reception into the RX FIFO */ + struct stm32_gpdma_cfg_s dmacfg; - stm32h5_dmasetup(priv->rxdma, - priv->usartbase + STM32_USART_RDR_OFFSET, - (uint32_t)priv->rxfifo, - RXDMA_BUFFER_SIZE, - SERIAL_DMA_IFLOW_CONTROL_WORD); - } - else -#endif - { - /* Configure for circular DMA reception into the RX FIFO */ - - stm32h5_dmasetup(priv->rxdma, - priv->usartbase + STM32_USART_RDR_OFFSET, - (uint32_t)priv->rxfifo, - RXDMA_BUFFER_SIZE, - SERIAL_DMA_CONTROL_WORD); - } + serial_rxdmacfg(priv, &dmacfg); + stm32_dmasetup(priv->rxdma, &dmacfg); /* Reset our DMA shadow pointer to match the address just * programmed above. @@ -3219,7 +3165,7 @@ static void stm32serial_dmareenable(struct stm32_serial_s *priv) * in and DMA transfer is stopped. */ - stm32h5_dmastart(priv->rxdma, stm32serial_dmarxcallback, + stm32_dmastart(priv->rxdma, stm32serial_dmarxcallback, (void *)priv, false); } else @@ -3230,7 +3176,7 @@ static void stm32serial_dmareenable(struct stm32_serial_s *priv) * worth of time to claim bytes before they are overwritten. */ - stm32h5_dmastart(priv->rxdma, stm32serial_dmarxcallback, + stm32_dmastart(priv->rxdma, stm32serial_dmarxcallback, (void *)priv, true); } @@ -3461,50 +3407,57 @@ static bool stm32serial_txready(struct uart_dev_s *dev) * Name: stm32serial_dmarxcallback * * Description: - * This function checks the current DMA state and calls the generic - * serial stack when bytes appear to be available. + * DMA callback for STM32H5 USART RX. Called on half and full‐transfer + * events. Reads and clears the GPDMA status flags, notifies the NuttX + * serial core of newly arrived bytes, signals end‐of‐buffer when a + * full transfer completes, handles RTS flow control restart, and + * clears any lingering UART error flags to keep RX‐DMA running. + * + * Input Parameters: + * handle - DMA channel handle returned by stm32_dmachannel() + * status - Raw status byte passed by the DMA ISR (ignored here) + * arg - Pointer to the STM32 serial driver state + * (struct stm32_serial_s) + * + * Returned Value: + * None * ****************************************************************************/ -#ifdef SERIAL_HAVE_DMA -static void stm32serial_dmarxcallback(DMA_HANDLE handle, uint8_t status, - void *arg) +static void stm32serial_dmarxcallback(DMA_HANDLE handle, + uint8_t status, + void *arg) { - struct stm32_serial_s *priv = (struct stm32_serial_s *)arg; + struct stm32_serial_s *priv = arg; - if (priv->rxenable && stm32serial_dmarxavailable(&priv->dev)) - { - uart_recvchars(&priv->dev); + /* pull whatever is in the buffer now */ + + uart_recvchars(&priv->dev); + + /* If it really was a full‑buffer event, signal “done” so the + * serial core can rearm/restart the DMA behind the scenes: + */ #ifdef CONFIG_SERIAL_IFLOWCONTROL - if (priv->iflow) - { - /* Re-enable RX DMA. */ + /* If you had paused the DMA on RTS flow control, restart it now */ - stm32serial_dmaiflowrestart(priv); - } -#endif + if (priv->iflow) + { + stm32serial_dmaiflowrestart(priv); } +#endif - /* Get the masked USART status word to check and clear error flags. - * - * When wake-up from low power mode was not fast enough, UART is resumed - * too late and sometimes exactly when character was coming over UART, - * resulting to frame error. - * If error flag is not cleared, Rx DMA will be stuck. Clearing errors - * will release Rx DMA. + /* Clear any USART framing/overrun errors so RX‑DMA + * doesn’t get stuck waiting for the UART to clear them. */ - priv->sr = stm32serial_getreg(priv, STM32_USART_ISR_OFFSET); - - if ((priv->sr & (USART_ISR_ORE | USART_ISR_NF | USART_ISR_FE)) != 0) + priv->sr = getreg32(priv->usartbase + STM32_USART_ISR_OFFSET); + if (priv->sr & (USART_ISR_ORE | USART_ISR_NF | USART_ISR_FE)) { stm32serial_putreg(priv, STM32_USART_ICR_OFFSET, - (USART_ICR_NCF | USART_ICR_ORECF | - USART_ICR_FECF)); + USART_ICR_ORECF | USART_ICR_NCF | USART_ICR_FECF); } } -#endif /**************************************************************************** * Name: stm32serial_pmnotify diff --git a/arch/arm/src/stm32h5/stm32_uart.h b/arch/arm/src/stm32h5/stm32_uart.h index 297d978e61..93914500e7 100644 --- a/arch/arm/src/stm32h5/stm32_uart.h +++ b/arch/arm/src/stm32h5/stm32_uart.h @@ -443,8 +443,7 @@ defined(CONFIG_UART8_RXDMA) || defined(CONFIG_UART9_RXDMA) || \ defined(CONFIG_USART10_RXDMA) || defined(CONFIG_USART11_RXDMA) || \ defined(CONFIG_USART12_RXDMA) -# define SERIAL_HAVE_DMA 0 -# warning Serial DMA has not been implemented for STM32H5 chips. +# define SERIAL_HAVE_DMA 1 #endif /* Is DMA used on the console UART? */