This is an automated email from the ASF dual-hosted git repository. raiden00 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
commit c801de4201f7be3004e85078c2783f6765582a59 Author: David Sidrane <[email protected]> AuthorDate: Fri Apr 9 09:26:03 2021 -0700 stm32h7:Serial Add RX and TX DMA stm32h7:Serial Use Idel to poll RX DMA stm32h7:Serial Do not loop in an ISR! stm32h7:Serial signal txdma completion with semaphore stm32h7:Serial Apply formatting suggestions from code review Co-authored-by: Mateusz Szafoni <[email protected]> stm32h7: Serail Add Power Managment (Untested) --- arch/arm/src/stm32h7/Kconfig | 12 + arch/arm/src/stm32h7/stm32_serial.c | 1899 ++++++++++++++++++++++++++++++++--- arch/arm/src/stm32h7/stm32_uart.h | 281 +++++- 3 files changed, 2053 insertions(+), 139 deletions(-) diff --git a/arch/arm/src/stm32h7/Kconfig b/arch/arm/src/stm32h7/Kconfig index 00e5b69..64d518f 100644 --- a/arch/arm/src/stm32h7/Kconfig +++ b/arch/arm/src/stm32h7/Kconfig @@ -1310,6 +1310,18 @@ config UART8_RXFIFO_THRES endif # STM32H7_UART8 +config STM32H7_SERIAL_RXDMA_BUFFER_SIZE + int "Rx DMA buffer size" + default 32 + depends on USART1_RXDMA || USART2_RXDMA || USART3_RXDMA || UART4_RXDMA || UART5_RXDMA || USART6_RXDMA || UART7_RXDMA || UART8_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 STM32H7_SERIAL_DISABLE_REORDERING bool "Disable reordering of ttySx devices." default n diff --git a/arch/arm/src/stm32h7/stm32_serial.c b/arch/arm/src/stm32h7/stm32_serial.c index 005077e..f747879 100644 --- a/arch/arm/src/stm32h7/stm32_serial.c +++ b/arch/arm/src/stm32h7/stm32_serial.c @@ -36,6 +36,7 @@ #include <nuttx/arch.h> #include <nuttx/fs/ioctl.h> #include <nuttx/serial/serial.h> +#include <nuttx/semaphore.h> #include <nuttx/power/pm.h> #ifdef CONFIG_SERIAL_TERMIOS @@ -48,6 +49,7 @@ #include "chip.h" #include "stm32_gpio.h" #include "hardware/stm32_pinmap.h" +#include "stm32_dma.h" #include "stm32_rcc.h" #include "stm32_uart.h" @@ -63,15 +65,429 @@ #define STM32_NSERIAL (STM32H7_NUSART + STM32H7_NUART) +/* DMA configuration */ + +/* If DMA is enabled on any USART, then verify that other pre-requisites + * have also been selected. + */ + +#ifdef SERIAL_HAVE_RXDMA + +/* Verify that RX DMA configuration. */ + +# if defined(CONFIG_USART1_RXDMA) +# if !defined(DMAMAP_USART1_RX) +# error "USART1 DMA map not defined (DMAMAP_USART1_RX)" +# endif +# if DMAMAP_USART1_RX == DMAMAP_DMA12_USART1RX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 USART1 using DMAMAP_DMA12_USART1RX_0 for receive DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_USART1_RX == DMAMAP_DMA12_USART1RX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 USART1 using DMAMAP_DMA12_USART1RX_1 for receive DMA requires CONFIG_STM32H7_DMA2 +# endif +# endif + +# if defined(CONFIG_USART2_RXDMA) +# if !defined(DMAMAP_USART2_RX) +# error "USART2 DMA map not defined (DMAMAP_USART2_RX)" +# endif +# if DMAMAP_USART2_RX == DMAMAP_DMA12_USART2RX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 USART2 using DMAMAP_DMA12_USART2RX_0 for receive DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_USART2_RX == DMAMAP_DMA12_USART2RX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 USART2 using DMAMAP_DMA12_USART2RX_1 for receive DMA requires CONFIG_STM32H7_DMA2 +# endif +# endif + +# if defined(CONFIG_USART3_RXDMA) +# if !defined(DMAMAP_USART3_RX) +# error "USART3 DMA map not defined (DMAMAP_USART3_RX)" +# endif +# if DMAMAP_USART3_RX == DMAMAP_DMA12_USART3RX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 USART3 using DMAMAP_DMA12_USART3RX_0 for receive DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_USART3_RX == DMAMAP_DMA12_USART3RX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 USART3 using DMAMAP_DMA12_USART3RX_1 for receive DMA requires CONFIG_STM32H7_DMA2 +# endif +# endif + +# if defined(CONFIG_UART4_RXDMA) +# if !defined(DMAMAP_UART4_RX) +# error "UART4 DMA map not defined (DMAMAP_UART4_RX)" +# endif +# if DMAMAP_UART4_RX == DMAMAP_DMA12_UART4RX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 UART4 using DMAMAP_DMA12_UART4RX_0 for receive DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_UART4_RX == DMAMAP_DMA12_UART4RX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 UART4 using DMAMAP_DMA12_UART4RX_1 for receive DMA requires CONFIG_STM32H7_DMA2 +# endif +# endif + +# if defined(CONFIG_UART5_RXDMA) +# if !defined(DMAMAP_UART5_RX) +# error "UART5 DMA map not defined (DMAMAP_UART5_RX)" +# endif +# if DMAMAP_UART5_RX == DMAMAP_DMA12_UART5RX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 UART5 using DMAMAP_DMA12_UART5RX_0 for receive DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_UART5_RX == DMAMAP_DMA12_UART5RX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 UART5 using DMAMAP_DMA12_UART5RX_1 for receive DMA requires CONFIG_STM32H7_DMA2 +# endif +# endif + +# if defined(CONFIG_USART6_RXDMA) +# if !defined(DMAMAP_USART6_RX) +# error "USART6 DMA map not defined (DMAMAP_USART6_RX)" +# endif +# if DMAMAP_USART6_RX == DMAMAP_DMA12_USART6RX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 USART6 using DMAMAP_DMA12_USART6RX_0 for receive DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_USART6_RX == DMAMAP_DMA12_USART6RX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 USART6 using DMAMAP_DMA12_USART6RX_1 for receive DMA requires CONFIG_STM32H7_DMA2 +# endif +# endif + +# if defined(CONFIG_UART7_RXDMA) +# if !defined(DMAMAP_UART7_RX) +# error "UART7 DMA map not defined (DMAMAP_UART7_RX)" +# endif +# if DMAMAP_UART7_RX == DMAMAP_DMA12_UART7RX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 UART7 using DMAMAP_DMA12_UART7RX_0 for receive DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_UART7_RX == DMAMAP_DMA12_UART7RX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 UART7 using DMAMAP_DMA12_UART7RX_1 for receive DMA requires CONFIG_STM32H7_DMA2 +# endif +# endif + +# if defined(CONFIG_UART8_RXDMA) +# if !defined(DMAMAP_UART8_RX) +# error "UART8 DMA map not defined (DMAMAP_UART8_RX)" +# endif +# if DMAMAP_UART8_RX == DMAMAP_DMA12_UART8RX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 UART8 using DMAMAP_DMA12_UART8RX_0 for receive DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_UART8_RX == DMAMAP_DMA12_UART8RX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 UART8 using DMAMAP_DMA12_UART8RX_1 for receive DMA requires CONFIG_STM32H7_DMA2 +# endif +# 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 + */ + +# if (defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_RS485)) || \ + (defined(CONFIG_USART2_RXDMA) && defined(CONFIG_USART2_RS485)) || \ + (defined(CONFIG_USART3_RXDMA) && defined(CONFIG_USART3_RS485)) || \ + (defined(CONFIG_UART4_RXDMA) && defined(CONFIG_UART4_RS485)) || \ + (defined(CONFIG_UART5_RXDMA) && defined(CONFIG_UART5_RS485)) || \ + (defined(CONFIG_USART6_RXDMA) && defined(CONFIG_USART6_RS485)) || \ + (defined(CONFIG_UART7_RXDMA) && defined(CONFIG_UART7_RS485)) || \ + (defined(CONFIG_UART8_RXDMA) && defined(CONFIG_UART8_RS485)) +# error "RXDMA and RS-485 cannot be enabled at the same time for the same U[S]ART" +# endif + +/* 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. + * + * This buffer size should be an even multiple of the Cortex-M7 D-Cache line + * size, ARMV7M_DCACHE_LINESIZE, so that it can be individually invalidated. + * + * Should there be a Cortex-M7 without a D-Cache, ARMV7M_DCACHE_LINESIZE + * would be zero! + */ + +# if !defined(ARMV7M_DCACHE_LINESIZE) || ARMV7M_DCACHE_LINESIZE == 0 +# undef ARMV7M_DCACHE_LINESIZE +# define ARMV7M_DCACHE_LINESIZE 32 +# endif + +# if !defined(CONFIG_STM32H7_SERIAL_RXDMA_BUFFER_SIZE) || \ + (CONFIG_STM32H7_SERIAL_RXDMA_BUFFER_SIZE < ARMV7M_DCACHE_LINESIZE) +# undef CONFIG_STM32H7_SERIAL_RXDMA_BUFFER_SIZE +# define CONFIG_STM32H7_SERIAL_RXDMA_BUFFER_SIZE ARMV7M_DCACHE_LINESIZE +# endif + +# define RXDMA_BUFFER_MASK (ARMV7M_DCACHE_LINESIZE - 1) +# define RXDMA_BUFFER_SIZE ((CONFIG_STM32H7_SERIAL_RXDMA_BUFFER_SIZE \ + + RXDMA_BUFFER_MASK) & ~RXDMA_BUFFER_MASK) + +/* DMA priority */ + +# ifndef CONFIG_USART_RXDMAPRIO +# define CONFIG_USART_RXDMAPRIO DMA_SCR_PRIMED +# endif + +# if (CONFIG_USART_RXDMAPRIO & ~DMA_SCR_PL_MASK) != 0 +# error "Illegal value for CONFIG_USART_RXDMAPRIO" +# endif + +/* DMA control words */ + +# define SERIAL_RXDMA_CONTROL_WORD \ + (DMA_SCR_DIR_P2M | \ + DMA_SCR_CIRC | \ + DMA_SCR_MINC | \ + DMA_SCR_PSIZE_8BITS | \ + DMA_SCR_MSIZE_8BITS | \ + CONFIG_USART_RXDMAPRIO | \ + DMA_SCR_PBURST_SINGLE | \ + DMA_SCR_MBURST_SINGLE) + +#endif /* SERIAL_HAVE_RXDMA */ + +#ifdef SERIAL_HAVE_TXDMA + +/* Verify that TX DMA configuration. */ + +# if defined(CONFIG_USART1_TXDMA) +# if !defined(DMAMAP_USART1_TX) +# error "USART1 DMA map not defined (DMAMAP_USART1_TX)" +# endif +# if DMAMAP_USART1_TX == DMAMAP_DMA12_USART1TX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 USART1 using DMAMAP_DMA12_USART1TX_0 for transmit DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_USART1_TX == DMAMAP_DMA12_USART1TX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 USART1 using DMAMAP_DMA12_USART1TX_1 for transmit DMA requires CONFIG_STM32H7_DMA2 +# endif +# endif + +# if defined(CONFIG_USART2_TXDMA) +# if !defined(DMAMAP_USART2_TX) +# error "USART2 DMA map not defined (DMAMAP_USART2_TX)" +# endif +# if DMAMAP_USART2_TX == DMAMAP_DMA12_USART2TX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 USART2 using DMAMAP_DMA12_USART2TX_0 for transmit DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_USART2_TX == DMAMAP_DMA12_USART2TX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 USART2 using DMAMAP_DMA12_USART2TX_1 for transmit DMA requires CONFIG_STM32H7_DMA2 +# endif +# endif + +# if defined(CONFIG_USART3_TXDMA) +# if !defined(DMAMAP_USART3_TX) +# error "USART3 DMA map not defined (DMAMAP_USART3_TX)" +# endif +# if DMAMAP_USART3_TX == DMAMAP_DMA12_USART3TX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 USART3 using DMAMAP_DMA12_USART3TX_0 for transmit DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_USART3_TX == DMAMAP_DMA12_USART3TX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 USART3 using DMAMAP_DMA12_USART3TX_1 for transmit DMA requires CONFIG_STM32H7_DMA2 +# endif +# endif + +# if defined(CONFIG_UART4_TXDMA) +# if !defined(DMAMAP_UART4_TX) +# error "UART4 DMA map not defined (DMAMAP_UART4_TX)" +# endif +# if DMAMAP_UART4_TX == DMAMAP_DMA12_UART4TX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 UART4 using DMAMAP_DMA12_UART4TX_0 for transmit DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_UART4_TX == DMAMAP_DMA12_UART4TX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 UART4 using DMAMAP_DMA12_UART4TX_1 for transmit DMA requires CONFIG_STM32H7_DMA2 +# endif +# endif + +# if defined(CONFIG_UART5_TXDMA) +# if !defined(DMAMAP_UART5_TX) +# error "UART5 DMA map not defined (DMAMAP_UART5_TX)" +# endif +# if DMAMAP_UART5_TX == DMAMAP_DMA12_UART5TX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 UART5 using DMAMAP_DMA12_UART5TX_0 for transmit DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_UART5_TX == DMAMAP_DMA12_UART5TX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 UART5 using DMAMAP_DMA12_UART5TX_1 for transmit DMA requires CONFIG_STM32H7_DMA2 +# endif +# endif + +# if defined(CONFIG_USART6_TXDMA) +# if !defined(DMAMAP_USART6_TX) +# error "USART6 DMA map not defined (DMAMAP_USART6_TX)" +# endif +# if DMAMAP_USART6_TX == DMAMAP_DMA12_USART6TX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 USART6 using DMAMAP_DMA12_USART6TX_0 for transmit DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_USART6_TX == DMAMAP_DMA12_USART6TX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 USART6 using DMAMAP_DMA12_USART6TX_1 for transmit DMA requires CONFIG_STM32H7_DMA2 +# endif +# endif + +# if defined(CONFIG_UART7_TXDMA) +# if !defined(DMAMAP_UART7_TX) +# error "UART7 DMA map not defined (DMAMAP_UART7_TX)" +# endif +# if DMAMAP_UART7_TX == DMAMAP_DMA12_UART7TX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 UART7 using DMAMAP_DMA12_UART7TX_0 for transmit DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_UART7_TX == DMAMAP_DMA12_UART7TX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 UART7 using DMAMAP_DMA12_UART7TX_1 for transmit DMA requires CONFIG_STM32H7_DMA2 +# endif +# endif + +# if defined(CONFIG_UART8_TXDMA) +# if !defined(DMAMAP_UART8_TX) +# error "UART8 DMA map not defined (DMAMAP_UART8_TX)" +# endif +# if DMAMAP_UART8_TX == DMAMAP_DMA12_UART8TX_0 && !defined(CONFIG_STM32H7_DMA1) +# error STM32 UART8 using DMAMAP_DMA12_UART8TX_0 for transmit DMA requires CONFIG_STM32H7_DMA1 +# endif +# if DMAMAP_UART8_TX == DMAMAP_DMA12_UART8TX_1 && !defined(CONFIG_STM32H7_DMA2) +# error STM32 UART8 using DMAMAP_DMA12_UART8TX_1 for transmit DMA requires CONFIG_STM32H7_DMA2 +# endif +# endif +#endif + +/* Currently RS-485 support cannot be enabled when TXDMA is in use due to + * lack of testing - RS-485 support was developed on STM32F1x + */ + +#if (defined(CONFIG_USART1_TXDMA) && defined(CONFIG_USART1_RS485)) || \ + (defined(CONFIG_USART2_TXDMA) && defined(CONFIG_USART2_RS485)) || \ + (defined(CONFIG_USART3_TXDMA) && defined(CONFIG_USART3_RS485)) || \ + (defined(CONFIG_UART4_TXDMA) && defined(CONFIG_UART4_RS485)) || \ + (defined(CONFIG_UART5_TXDMA) && defined(CONFIG_UART5_RS485)) || \ + (defined(CONFIG_USART6_TXDMA) && defined(CONFIG_USART6_RS485)) || \ + (defined(CONFIG_UART7_TXDMA) && defined(CONFIG_UART7_RS485)) || \ + (defined(CONFIG_UART8_TXDMA) && defined(CONFIG_UART8_RS485)) +# error "TXDMA and RS-485 cannot be enabled at the same time for the same U[S]ART" +#endif + +/* The DMA buffer size when using TX DMA. + * + * This TX buffer size should be an even multiple of the Cortex-M7 D-Cache + * line size, ARMV7M_DCACHE_LINESIZE, so that it can be individually + * invalidated. + * + * Should there be a Cortex-M7 without a D-Cache, ARMV7M_DCACHE_LINESIZE + * would be zero! + */ + +#if !defined(ARMV7M_DCACHE_LINESIZE) || ARMV7M_DCACHE_LINESIZE == 0 +# undef ARMV7M_DCACHE_LINESIZE +# define ARMV7M_DCACHE_LINESIZE 32 +#endif + +#define TXDMA_BUFFER_MASK (ARMV7M_DCACHE_LINESIZE - 1) +#define TXDMA_BUFFER_SIZE ((CONFIG_STM32H7_SERIAL_RXDMA_BUFFER_SIZE \ + + RXDMA_BUFFER_MASK) & ~RXDMA_BUFFER_MASK) + +/* If built with CONFIG_ARMV7M_DCACHE Buffers need to be aligned and + * multiples of ARMV7M_DCACHE_LINESIZE + */ + +#if defined(CONFIG_ARMV7M_DCACHE) +# define TXDMA_BUF_SIZE(b) (((b) + TXDMA_BUFFER_MASK) & ~TXDMA_BUFFER_MASK) +# define TXDMA_BUF_ALIGN aligned_data(ARMV7M_DCACHE_LINESIZE); +#else +# define TXDMA_BUF_SIZE(b) (b) +# define TXDMA_BUF_ALIGN +#endif + +#if !defined(CONFIG_USART1_TXDMA) +# define USART1_TXBUFSIZE_ADJUSTED CONFIG_USART1_TXBUFSIZE +# define USART1_TXBUFSIZE_ALGN +#else +# define USART1_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_USART1_TXBUFSIZE) +# define USART1_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +#if !defined(CONFIG_USART2_TXDMA) +# define USART2_TXBUFSIZE_ADJUSTED CONFIG_USART2_TXBUFSIZE +# define USART2_TXBUFSIZE_ALGN +#else +# define USART2_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_USART2_TXBUFSIZE) +# define USART2_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +#if !defined(CONFIG_USART3_TXDMA) +# define USART3_TXBUFSIZE_ADJUSTED CONFIG_USART3_TXBUFSIZE +# define USART3_TXBUFSIZE_ALGN +#else +# define USART3_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_USART3_TXBUFSIZE) +# define USART3_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +#if !defined(CONFIG_UART4_TXDMA) +# define UART4_TXBUFSIZE_ADJUSTED CONFIG_UART4_TXBUFSIZE +# define UART4_TXBUFSIZE_ALGN +#else +# define UART4_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_UART4_TXBUFSIZE) +# define UART4_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +#if !defined(CONFIG_UART5_TXDMA) +# define UART5_TXBUFSIZE_ADJUSTED CONFIG_UART5_TXBUFSIZE +# define UART5_TXBUFSIZE_ALGN +#else +# define UART5_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_UART5_TXBUFSIZE) +# define UART5_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +#if !defined(CONFIG_USART6_TXDMA) +# define USART6_TXBUFSIZE_ADJUSTED CONFIG_USART6_TXBUFSIZE +# define USART6_TXBUFSIZE_ALGN +#else +# define USART6_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_USART6_TXBUFSIZE) +# define USART6_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +#if !defined(CONFIG_UART7_TXDMA) +# define UART7_TXBUFSIZE_ADJUSTED CONFIG_UART7_TXBUFSIZE +# define UART7_TXBUFSIZE_ALGN +#else +# define UART7_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_UART7_TXBUFSIZE) +# define UART7_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +#if !defined(CONFIG_UART8_TXDMA) +# define UART8_TXBUFSIZE_ADJUSTED CONFIG_UART8_TXBUFSIZE +# define UART8_TXBUFSIZE_ALGN +#else +# define UART8_TXBUFSIZE_ADJUSTED TXDMA_BUF_SIZE(CONFIG_UART8_TXBUFSIZE) +# define UART8_TXBUFSIZE_ALGN TXDMA_BUF_ALIGN +#endif + +#ifdef SERIAL_HAVE_TXDMA +/* DMA priority */ + +# ifndef CONFIG_USART_TXDMAPRIO +# define CONFIG_USART_TXDMAPRIO DMA_SCR_PRIMED +# endif + +# if (CONFIG_USART_TXDMAPRIO & ~DMA_SCR_PL_MASK) != 0 +# error "Illegal value for CONFIG_USART_TXDMAPRIO" +# endif + +# define SERIAL_TXDMA_CONTROL_WORD \ + (DMA_SCR_DIR_M2P | \ + DMA_SCR_MINC | \ + DMA_SCR_PSIZE_8BITS | \ + DMA_SCR_MSIZE_8BITS | \ + DMA_SCR_PBURST_SINGLE | \ + DMA_SCR_MBURST_SINGLE | \ + CONFIG_USART_TXDMAPRIO) + +#endif /* SERIAL_HAVE_TXDMA */ + /* Power management definitions */ #if defined(CONFIG_PM) && !defined(CONFIG_STM32H7_PM_SERIAL_ACTIVITY) # define CONFIG_STM32H7_PM_SERIAL_ACTIVITY 10 #endif #if defined(CONFIG_PM) +# warning stm32h7 serial power managemnt was taken from stm32f7 and is untested! # define PM_IDLE_DOMAIN 0 /* Revisit */ #endif +/* Since RX DMA or TX DMA or both may be enabled for a given U[S]ART. + * We need runtime detection in up_dma_setup and up_dma_shutdown + * We use the default struct default init value of 0 which does not map + * to a valid DMA MAPS. + */ + +#define INVALID_SERIAL_DMA_CHANNEL 0 + /* Keep track if a Break was set * * Note: @@ -99,6 +515,51 @@ CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS to be enabled." #endif +#ifndef CONFIG_STM32H7_FLOWCONTROL_BROKEN +/* Combination of RXDMA + IFLOWCONTROL does not work as one might expect. + * Since RXDMA uses circular DMA-buffer, DMA will always keep reading new + * data from USART peripheral even if DMA buffer underruns. Thus this + * combination only does following: RTS is asserted on USART setup and + * deasserted on shutdown and does not perform actual RTS flow-control. + * + * With SW flow-control, RTS is asserted before UART receive buffer fully + * fills, thus preventing data loss if application is slow to process data + * from serial device node. However, if RxDMA interrupt is blocked for too + * long, data loss is still possible as SW flow-control would also be + * blocked. + */ + +# if defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_IFLOWCONTROL) +# warning "RXDMA and IFLOWCONTROL both enabled for USART1. \ + This combination can lead to data loss." +# endif + +# if defined(CONFIG_USART2_RXDMA) && defined(CONFIG_USART2_IFLOWCONTROL) +# warning "RXDMA and IFLOWCONTROL both enabled for USART2. \ + This combination can lead to data loss." +# endif + +# if defined(CONFIG_USART3_RXDMA) && defined(CONFIG_USART3_IFLOWCONTROL) +# warning "RXDMA and IFLOWCONTROL both enabled for USART3. \ + This combination can lead to data loss." +# endif + +# if defined(CONFIG_USART6_RXDMA) && defined(CONFIG_USART6_IFLOWCONTROL) +# warning "RXDMA and IFLOWCONTROL both enabled for USART6. \ + This combination can lead to data loss." +# endif + +# if defined(CONFIG_UART7_RXDMA) && defined(CONFIG_UART7_IFLOWCONTROL) +# warning "RXDMA and IFLOWCONTROL both enabled for UART7. \ + This combination can lead to data loss." +# endif + +# if defined(CONFIG_UART8_RXDMA) && defined(CONFIG_UART8_IFLOWCONTROL) +# warning "RXDMA and IFLOWCONTROL both enabled for UART8. \ + This combination can lead to data loss." +# endif +#endif /* CONFIG_STM32H7_FLOWCONTROL_BROKEN */ + /**************************************************************************** * Private Types ****************************************************************************/ @@ -113,6 +574,14 @@ struct up_dev_s bool initialized; +#ifdef CONFIG_PM + bool suspended; /* UART device has been suspended. */ + + /* Interrupt mask value stored before suspending for stop mode. */ + + uint16_t suspended_ie; +#endif + /* If termios are supported, then the following fields may vary at * runtime. */ @@ -155,12 +624,45 @@ struct up_dev_s const uint32_t cts_gpio; /* U[S]ART CTS GPIO pin configuration */ #endif + /* TX DMA state */ + +#ifdef SERIAL_HAVE_TXDMA + const unsigned int txdma_channel; /* DMA channel assigned */ + DMA_HANDLE txdma; /* currently-open trasnmit DMA stream */ + sem_t txdmasem; /* Indicate TX DMA completion */ +#endif + + /* RX DMA state */ + +#ifdef SERIAL_HAVE_RXDMA + const unsigned int rxdma_channel; /* DMA channel assigned */ + DMA_HANDLE rxdma; /* currently-open receive DMA stream */ + bool rxenable; /* DMA-based reception en/disable */ +#ifdef CONFIG_PM + bool rxdmasusp; /* Rx DMA suspended */ +#endif + uint32_t rxdmanext; /* Next byte in the DMA buffer to be read */ +#ifdef CONFIG_ARMV7M_DCACHE + uint32_t rxdmaavail; /* Number of bytes available without need to + * to invalidate the data cache */ +#endif + char *const rxfifo; /* Receive DMA buffer */ +#endif + #ifdef HAVE_RS485 const uint32_t rs485_dir_gpio; /* U[S]ART RS-485 DIR GPIO pin configuration */ const bool rs485_dir_polarity; /* U[S]ART RS-485 DIR pin state for TX enabled */ #endif }; +#ifdef CONFIG_PM +struct pm_config_s +{ + struct pm_callback_s pm_cb; + bool serial_suspended; +}; +#endif + /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -172,9 +674,11 @@ static int up_attach(struct uart_dev_s *dev); static void up_detach(struct uart_dev_s *dev); static int up_interrupt(int irq, void *context, FAR void *arg); static int up_ioctl(struct file *filep, int cmd, unsigned long arg); +#if defined(SERIAL_HAVE_TXDMA_OPS) || defined(SERIAL_HAVE_NODMA_OPS) static int up_receive(struct uart_dev_s *dev, unsigned int *status); static void up_rxint(struct uart_dev_s *dev, bool enable); static bool up_rxavailable(struct uart_dev_s *dev); +#endif #ifdef CONFIG_SERIAL_IFLOWCONTROL static bool up_rxflowcontrol(struct uart_dev_s *dev, unsigned int nbuffered, bool upper); @@ -183,7 +687,32 @@ static void up_send(struct uart_dev_s *dev, int ch); static void up_txint(struct uart_dev_s *dev, bool enable); static bool up_txready(struct uart_dev_s *dev); +#ifdef SERIAL_HAVE_TXDMA +static void up_dma_send(struct uart_dev_s *dev); +static void up_dma_txint(struct uart_dev_s *dev, bool enable); +static void up_dma_txavailable(struct uart_dev_s *dev); +static void up_dma_txcallback(DMA_HANDLE handle, uint8_t status, void *arg); +#endif + +#if defined(SERIAL_HAVE_RXDMA) || defined(SERIAL_HAVE_TXDMA) +static int up_dma_setup(struct uart_dev_s *dev); +static void up_dma_shutdown(struct uart_dev_s *dev); +#endif + +#ifdef SERIAL_HAVE_RXDMA +static int up_dma_receive(struct uart_dev_s *dev, unsigned int *status); +#ifdef CONFIG_PM +static void up_dma_reenable(struct up_dev_s *priv); +#endif +static void up_dma_rxint(struct uart_dev_s *dev, bool enable); +static bool up_dma_rxavailable(struct uart_dev_s *dev); + +static void up_dma_rxcallback(DMA_HANDLE handle, uint8_t status, void *arg); +#endif + #ifdef CONFIG_PM +static void up_setsuspend(struct uart_dev_s *dev, bool suspend); +static void up_pm_setsuspend(bool suspend); static void up_pm_notify(struct pm_callback_s *cb, int domain, enum pm_state_e pmstate); static int up_pm_prepare(struct pm_callback_s *cb, int domain, @@ -194,6 +723,7 @@ static int up_pm_prepare(struct pm_callback_s *cb, int domain, * Private Data ****************************************************************************/ +#ifdef SERIAL_HAVE_NODMA_OPS static const struct uart_ops_s g_uart_ops = { .setup = up_setup, @@ -212,47 +742,174 @@ static const struct uart_ops_s g_uart_ops = .txready = up_txready, .txempty = up_txready, }; +#endif + +#ifdef SERIAL_HAVE_RXTXDMA_OPS +static const struct uart_ops_s g_uart_rxtxdma_ops = +{ + .setup = up_dma_setup, + .shutdown = up_dma_shutdown, + .attach = up_attach, + .detach = up_detach, + .ioctl = up_ioctl, + .receive = up_dma_receive, + .rxint = up_dma_rxint, + .rxavailable = up_dma_rxavailable, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rxflowcontrol = up_rxflowcontrol, +#endif + .send = up_send, + .txint = up_dma_txint, + .txready = up_txready, + .txempty = up_txready, + .dmatxavail = up_dma_txavailable, + .dmasend = up_dma_send, +}; +#endif + +#ifdef SERIAL_HAVE_RXDMA_OPS +static const struct uart_ops_s g_uart_rxdma_ops = +{ + .setup = up_dma_setup, + .shutdown = up_dma_shutdown, + .attach = up_attach, + .detach = up_detach, + .ioctl = up_ioctl, + .receive = up_dma_receive, + .rxint = up_dma_rxint, + .rxavailable = up_dma_rxavailable, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rxflowcontrol = up_rxflowcontrol, +#endif + .send = up_send, + .txint = up_txint, + .txready = up_txready, + .txempty = up_txready, +}; +#endif + +#ifdef SERIAL_HAVE_TXDMA_OPS +static const struct uart_ops_s g_uart_txdma_ops = +{ + .setup = up_dma_setup, + .shutdown = up_dma_shutdown, + .attach = up_attach, + .detach = up_detach, + .ioctl = up_ioctl, + .receive = up_receive, + .rxint = up_rxint, + .rxavailable = up_rxavailable, +#ifdef CONFIG_SERIAL_IFLOWCONTROL + .rxflowcontrol = up_rxflowcontrol, +#endif + .send = up_send, + .txint = up_dma_txint, + .txready = up_txready, + .txempty = up_txready, + .dmatxavail = up_dma_txavailable, + .dmasend = up_dma_send, +}; +#endif + +/* DMA buffers. RX DMA buffers must: + * + * 1. Be a multiple of the D-Cache line size. This requirement is assured + * by the definition of RXDMA buffer size above. + * 2. Be aligned a D-Cache line boundaries, and + * 3. Be positioned in DMA-able memory. + * + * These DMA buffers are defined sequentially here to best assure optimal + * packing of the buffers. + */ + +#ifdef CONFIG_USART1_RXDMA +static char g_usart1rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV7M_DCACHE_LINESIZE); +#endif + +# ifdef CONFIG_USART2_RXDMA +static char g_usart2rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV7M_DCACHE_LINESIZE); +#endif + +#ifdef CONFIG_USART3_RXDMA +static char g_usart3rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV7M_DCACHE_LINESIZE); +#endif + +#ifdef CONFIG_UART4_RXDMA +static char g_uart4rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV7M_DCACHE_LINESIZE); +#endif + +#ifdef CONFIG_UART5_RXDMA +static char g_uart5rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV7M_DCACHE_LINESIZE); +#endif + +#ifdef CONFIG_USART6_RXDMA +static char g_usart6rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV7M_DCACHE_LINESIZE); +#endif + +#ifdef CONFIG_UART7_RXDMA +static char g_uart7rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV7M_DCACHE_LINESIZE); +#endif + +#ifdef CONFIG_UART8_RXDMA +static char g_uart8rxfifo[RXDMA_BUFFER_SIZE] + aligned_data(ARMV7M_DCACHE_LINESIZE); +#endif /* Receive/Transmit buffers */ #ifdef CONFIG_STM32H7_USART1 static char g_usart1rxbuffer[CONFIG_USART1_RXBUFSIZE]; -static char g_usart1txbuffer[CONFIG_USART1_TXBUFSIZE]; +static char g_usart1txbuffer[USART1_TXBUFSIZE_ADJUSTED] \ + USART1_TXBUFSIZE_ALGN; #endif #ifdef CONFIG_STM32H7_USART2 static char g_usart2rxbuffer[CONFIG_USART2_RXBUFSIZE]; -static char g_usart2txbuffer[CONFIG_USART2_TXBUFSIZE]; +static char g_usart2txbuffer[USART2_TXBUFSIZE_ADJUSTED] \ + USART2_TXBUFSIZE_ALGN; #endif #ifdef CONFIG_STM32H7_USART3 static char g_usart3rxbuffer[CONFIG_USART3_RXBUFSIZE]; -static char g_usart3txbuffer[CONFIG_USART3_TXBUFSIZE]; +static char g_usart3txbuffer[USART3_TXBUFSIZE_ADJUSTED] \ + USART3_TXBUFSIZE_ALGN; #endif #ifdef CONFIG_STM32H7_UART4 static char g_uart4rxbuffer[CONFIG_UART4_RXBUFSIZE]; -static char g_uart4txbuffer[CONFIG_UART4_TXBUFSIZE]; +static char g_uart4txbuffer[UART4_TXBUFSIZE_ADJUSTED] \ + UART4_TXBUFSIZE_ALGN; #endif #ifdef CONFIG_STM32H7_UART5 static char g_uart5rxbuffer[CONFIG_UART5_RXBUFSIZE]; -static char g_uart5txbuffer[CONFIG_UART5_TXBUFSIZE]; +static char g_uart5txbuffer[UART5_TXBUFSIZE_ADJUSTED] \ + UART5_TXBUFSIZE_ALGN; #endif #ifdef CONFIG_STM32H7_USART6 static char g_usart6rxbuffer[CONFIG_USART6_RXBUFSIZE]; -static char g_usart6txbuffer[CONFIG_USART6_TXBUFSIZE]; +static char g_usart6txbuffer[USART6_TXBUFSIZE_ADJUSTED] \ + USART6_TXBUFSIZE_ALGN; #endif #ifdef CONFIG_STM32H7_UART7 static char g_uart7rxbuffer[CONFIG_UART7_RXBUFSIZE]; -static char g_uart7txbuffer[CONFIG_UART7_TXBUFSIZE]; +static char g_uart7txbuffer[UART7_TXBUFSIZE_ADJUSTED] \ + UART7_TXBUFSIZE_ALGN; #endif #ifdef CONFIG_STM32H7_UART8 static char g_uart8rxbuffer[CONFIG_UART8_RXBUFSIZE]; -static char g_uart8txbuffer[CONFIG_UART8_TXBUFSIZE]; +static char g_uart8txbuffer[UART8_TXBUFSIZE_ADJUSTED] \ + UART8_TXBUFSIZE_ALGN; #endif /* This describes the state of the STM32 USART1 ports. */ @@ -267,15 +924,23 @@ static struct up_dev_s g_usart1priv = #endif .recv = { - .size = CONFIG_USART1_RXBUFSIZE, + .size = sizeof(g_usart1rxbuffer), .buffer = g_usart1rxbuffer, }, .xmit = { - .size = CONFIG_USART1_TXBUFSIZE, + .size = sizeof(g_usart1txbuffer), .buffer = g_usart1txbuffer, }, +#if defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_TXDMA) + .ops = &g_uart_rxtxdma_ops, +#elif defined(CONFIG_USART1_RXDMA) && !defined(CONFIG_USART1_TXDMA) + .ops = &g_uart_rxdma_ops, +#elif !defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_TXDMA) + .ops = &g_uart_txdma_ops, +#else .ops = &g_uart_ops, +#endif .priv = &g_usart1priv, }, @@ -297,6 +962,13 @@ static struct up_dev_s g_usart1priv = .iflow = true, .rts_gpio = GPIO_USART1_RTS, #endif +#ifdef CONFIG_USART1_TXDMA + .txdma_channel = DMAMAP_USART1_TX, +#endif +#ifdef CONFIG_USART1_RXDMA + .rxdma_channel = DMAMAP_USART1_RX, + .rxfifo = g_usart1rxfifo, +#endif #ifdef CONFIG_USART1_RS485 .rs485_dir_gpio = GPIO_USART1_RS485_DIR, @@ -321,15 +993,23 @@ static struct up_dev_s g_usart2priv = #endif .recv = { - .size = CONFIG_USART2_RXBUFSIZE, + .size = sizeof(g_usart2rxbuffer), .buffer = g_usart2rxbuffer, }, .xmit = { - .size = CONFIG_USART2_TXBUFSIZE, + .size = sizeof(g_usart2txbuffer), .buffer = g_usart2txbuffer, }, +#if defined(CONFIG_USART2_RXDMA) && defined(CONFIG_USART2_TXDMA) + .ops = &g_uart_rxtxdma_ops, +#elif defined(CONFIG_USART2_RXDMA) && !defined(CONFIG_USART2_TXDMA) + .ops = &g_uart_rxdma_ops, +#elif !defined(CONFIG_USART2_RXDMA) && defined(CONFIG_USART2_TXDMA) + .ops = &g_uart_txdma_ops, +#else .ops = &g_uart_ops, +#endif .priv = &g_usart2priv, }, @@ -351,6 +1031,13 @@ static struct up_dev_s g_usart2priv = .iflow = true, .rts_gpio = GPIO_USART2_RTS, #endif +#ifdef CONFIG_USART2_TXDMA + .txdma_channel = DMAMAP_USART2_TX, +#endif +#ifdef CONFIG_USART2_RXDMA + .rxdma_channel = DMAMAP_USART2_RX, + .rxfifo = g_usart2rxfifo, +#endif #ifdef CONFIG_USART2_RS485 .rs485_dir_gpio = GPIO_USART2_RS485_DIR, @@ -375,15 +1062,23 @@ static struct up_dev_s g_usart3priv = #endif .recv = { - .size = CONFIG_USART3_RXBUFSIZE, + .size = sizeof(g_usart3rxbuffer), .buffer = g_usart3rxbuffer, }, .xmit = { - .size = CONFIG_USART3_TXBUFSIZE, + .size = sizeof(g_usart3txbuffer), .buffer = g_usart3txbuffer, }, +#if defined(CONFIG_USART3_RXDMA) && defined(CONFIG_USART3_TXDMA) + .ops = &g_uart_rxtxdma_ops, +#elif defined(CONFIG_USART3_RXDMA) && !defined(CONFIG_USART3_TXDMA) + .ops = &g_uart_rxdma_ops, +#elif !defined(CONFIG_USART3_RXDMA) && defined(CONFIG_USART3_TXDMA) + .ops = &g_uart_txdma_ops, +#else .ops = &g_uart_ops, +#endif .priv = &g_usart3priv, }, @@ -405,6 +1100,13 @@ static struct up_dev_s g_usart3priv = .iflow = true, .rts_gpio = GPIO_USART3_RTS, #endif +#ifdef CONFIG_USART3_TXDMA + .txdma_channel = DMAMAP_USART3_TX, +#endif +#ifdef CONFIG_USART3_RXDMA + .rxdma_channel = DMAMAP_USART3_RX, + .rxfifo = g_usart3rxfifo, +#endif #ifdef CONFIG_USART3_RS485 .rs485_dir_gpio = GPIO_USART3_RS485_DIR, @@ -429,15 +1131,23 @@ static struct up_dev_s g_uart4priv = #endif .recv = { - .size = CONFIG_UART4_RXBUFSIZE, + .size = sizeof(g_uart4rxbuffer), .buffer = g_uart4rxbuffer, }, .xmit = { - .size = CONFIG_UART4_TXBUFSIZE, + .size = sizeof(g_uart4txbuffer), .buffer = g_uart4txbuffer, }, +#if defined(CONFIG_UART4_RXDMA) && defined(CONFIG_UART4_TXDMA) + .ops = &g_uart_rxtxdma_ops, +#elif defined(CONFIG_UART4_RXDMA) && !defined(CONFIG_UART4_TXDMA) + .ops = &g_uart_rxdma_ops, +#elif !defined(CONFIG_UART4_RXDMA) && defined(CONFIG_UART4_TXDMA) + .ops = &g_uart_txdma_ops, +#else .ops = &g_uart_ops, +#endif .priv = &g_uart4priv, }, @@ -459,6 +1169,13 @@ static struct up_dev_s g_uart4priv = .usartbase = STM32_UART4_BASE, .tx_gpio = GPIO_UART4_TX, .rx_gpio = GPIO_UART4_RX, +#ifdef CONFIG_UART4_TXDMA + .txdma_channel = DMAMAP_UART4_TX, +#endif +#ifdef CONFIG_UART4_RXDMA + .rxdma_channel = DMAMAP_UART4_RX, + .rxfifo = g_uart4rxfifo, +#endif #ifdef CONFIG_UART4_RS485 .rs485_dir_gpio = GPIO_UART4_RS485_DIR, @@ -483,15 +1200,23 @@ static struct up_dev_s g_uart5priv = #endif .recv = { - .size = CONFIG_UART5_RXBUFSIZE, + .size = sizeof(g_uart5rxbuffer), .buffer = g_uart5rxbuffer, }, .xmit = { - .size = CONFIG_UART5_TXBUFSIZE, + .size = sizeof(g_uart5txbuffer), .buffer = g_uart5txbuffer, }, +#if defined(CONFIG_UART5_RXDMA) && defined(CONFIG_UART5_TXDMA) + .ops = &g_uart_rxtxdma_ops, +#elif defined(CONFIG_UART5_RXDMA) && !defined(CONFIG_UART5_TXDMA) + .ops = &g_uart_rxdma_ops, +#elif !defined(CONFIG_UART5_RXDMA) && defined(CONFIG_UART5_TXDMA) + .ops = &g_uart_txdma_ops, +#else .ops = &g_uart_ops, +#endif .priv = &g_uart5priv, }, @@ -513,6 +1238,13 @@ static struct up_dev_s g_uart5priv = .usartbase = STM32_UART5_BASE, .tx_gpio = GPIO_UART5_TX, .rx_gpio = GPIO_UART5_RX, +#ifdef CONFIG_UART5_TXDMA + .txdma_channel = DMAMAP_UART5_TX, +#endif +#ifdef CONFIG_UART5_RXDMA + .rxdma_channel = DMAMAP_UART5_RX, + .rxfifo = g_uart5rxfifo, +#endif #ifdef CONFIG_UART5_RS485 .rs485_dir_gpio = GPIO_UART5_RS485_DIR, @@ -537,15 +1269,23 @@ static struct up_dev_s g_usart6priv = #endif .recv = { - .size = CONFIG_USART6_RXBUFSIZE, + .size = sizeof(g_usart6rxbuffer), .buffer = g_usart6rxbuffer, }, .xmit = { - .size = CONFIG_USART6_TXBUFSIZE, + .size = sizeof(g_usart6txbuffer), .buffer = g_usart6txbuffer, }, +#if defined(CONFIG_USART6_RXDMA) && defined(CONFIG_USART6_TXDMA) + .ops = &g_uart_rxtxdma_ops, +#elif defined(CONFIG_USART6_RXDMA) && !defined(CONFIG_USART6_TXDMA) + .ops = &g_uart_rxdma_ops, +#elif !defined(CONFIG_USART6_RXDMA) && defined(CONFIG_USART6_TXDMA) + .ops = &g_uart_txdma_ops, +#else .ops = &g_uart_ops, +#endif .priv = &g_usart6priv, }, @@ -567,6 +1307,13 @@ static struct up_dev_s g_usart6priv = .iflow = true, .rts_gpio = GPIO_USART6_RTS, #endif +#ifdef CONFIG_USART6_TXDMA + .txdma_channel = DMAMAP_USART6_TX, +#endif +#ifdef CONFIG_USART6_RXDMA + .rxdma_channel = DMAMAP_USART6_RX, + .rxfifo = g_usart6rxfifo, +#endif #ifdef CONFIG_USART6_RS485 .rs485_dir_gpio = GPIO_USART6_RS485_DIR, @@ -591,15 +1338,23 @@ static struct up_dev_s g_uart7priv = #endif .recv = { - .size = CONFIG_UART7_RXBUFSIZE, + .size = sizeof(g_uart7rxbuffer), .buffer = g_uart7rxbuffer, }, .xmit = { - .size = CONFIG_UART7_TXBUFSIZE, + .size = sizeof(g_uart7txbuffer), .buffer = g_uart7txbuffer, }, +#if defined(CONFIG_UART7_RXDMA) && defined(CONFIG_UART7_TXDMA) + .ops = &g_uart_rxtxdma_ops, +#elif defined(CONFIG_UART7_RXDMA) && !defined(CONFIG_UART7_TXDMA) + .ops = &g_uart_rxdma_ops, +#elif !defined(CONFIG_UART7_RXDMA) && defined(CONFIG_UART7_TXDMA) + .ops = &g_uart_txdma_ops, +#else .ops = &g_uart_ops, +#endif .priv = &g_uart7priv, }, @@ -621,6 +1376,13 @@ static struct up_dev_s g_uart7priv = .iflow = true, .rts_gpio = GPIO_UART7_RTS, #endif +#ifdef CONFIG_UART7_TXDMA + .txdma_channel = DMAMAP_UART7_TX, +#endif +#ifdef CONFIG_UART7_RXDMA + .rxdma_channel = DMAMAP_UART7_RX, + .rxfifo = g_uart7rxfifo, +#endif #ifdef CONFIG_UART7_RS485 .rs485_dir_gpio = GPIO_UART7_RS485_DIR, @@ -645,15 +1407,23 @@ static struct up_dev_s g_uart8priv = #endif .recv = { - .size = CONFIG_UART8_RXBUFSIZE, + .size = sizeof(g_uart8rxbuffer), .buffer = g_uart8rxbuffer, }, .xmit = { - .size = CONFIG_UART8_TXBUFSIZE, + .size = sizeof(g_uart8txbuffer), .buffer = g_uart8txbuffer, }, +#if defined(CONFIG_UART8_RXDMA) && defined(CONFIG_UART8_TXDMA) + .ops = &g_uart_rxtxdma_ops, +#elif defined(CONFIG_UART8_RXDMA) && !defined(CONFIG_UART8_TXDMA) + .ops = &g_uart_rxdma_ops, +#elif !defined(CONFIG_UART8_RXDMA) && defined(CONFIG_UART8_TXDMA) + .ops = &g_uart_txdma_ops, +#else .ops = &g_uart_ops, +#endif .priv = &g_uart8priv, }, @@ -675,6 +1445,13 @@ static struct up_dev_s g_uart8priv = .iflow = true, .rts_gpio = GPIO_UART8_RTS, #endif +#ifdef CONFIG_UART8_TXDMA + .txdma_channel = DMAMAP_UART8_TX, +#endif +#ifdef CONFIG_UART8_RXDMA + .rxdma_channel = DMAMAP_UART8_RX, + .rxfifo = g_uart8rxfifo, +#endif #ifdef CONFIG_UART8_RS485 .rs485_dir_gpio = GPIO_UART8_RS485_DIR, @@ -718,10 +1495,11 @@ static struct up_dev_s * const g_uart_devs[STM32_NSERIAL] = }; #ifdef CONFIG_PM -static struct pm_callback_s g_serialcb = +static struct pm_config_s g_serialpm = { - .notify = up_pm_notify, - .prepare = up_pm_prepare, + .pm_cb.notify = up_pm_notify, + .pm_cb.prepare = up_pm_prepare, + .serial_suspended = false }; #endif @@ -779,6 +1557,9 @@ static inline void up_setusartint(struct up_dev_s *priv, uint16_t ie) cr = up_serialin(priv, STM32_USART_CR1_OFFSET); cr &= ~(USART_CR1_USED_INTS); cr |= (ie & (USART_CR1_USED_INTS)); +#ifdef SERIAL_HAVE_RXDMA + cr |= USART_CR1_IDLEIE; +#endif up_serialout(priv, STM32_USART_CR1_OFFSET, cr); cr = up_serialin(priv, STM32_USART_CR3_OFFSET); @@ -821,7 +1602,7 @@ static void up_disableusartint(struct up_dev_s *priv, uint16_t *ie) * * Enable Status Meaning Usage * ---------------- -------------- ----------------------- ---------- - * USART_CR1_IDLEIE USART_ISR_IDLE Idle Line Detected (not used) + * USART_CR1_IDLEIE USART_ISR_IDLE Idle Line Detected * USART_CR1_RXNEIE USART_ISR_RXNE Received Data Ready * to be Read * " " USART_ISR_ORE Overrun Error Detected @@ -858,20 +1639,40 @@ static void up_disableusartint(struct up_dev_s *priv, uint16_t *ie) } /**************************************************************************** - * Name: up_set_format + * Name: up_dma_nextrx * * Description: - * Set the serial line format and speed. + * Returns the index into the RX FIFO where the DMA will place the next + * byte that it receives. * ****************************************************************************/ -#ifndef CONFIG_SUPPRESS_UART_CONFIG -static void up_set_format(struct uart_dev_s *dev) +#ifdef SERIAL_HAVE_RXDMA +static int up_dma_nextrx(struct up_dev_s *priv) { - struct up_dev_s *priv = (struct up_dev_s *)dev->priv; - uint32_t regval; - uint32_t usartdiv8; - uint32_t cr1; + size_t dmaresidual; + + dmaresidual = stm32_dmaresidual(priv->rxdma); + + return (RXDMA_BUFFER_SIZE - (int)dmaresidual); +} +#endif + +/**************************************************************************** + * Name: up_set_format + * + * Description: + * Set the serial line format and speed. + * + ****************************************************************************/ + +#ifndef CONFIG_SUPPRESS_UART_CONFIG +static void up_set_format(struct uart_dev_s *dev) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + uint32_t regval; + uint32_t usartdiv8; + uint32_t cr1; uint32_t cr1_ue; uint32_t brr; irqstate_t flags; @@ -1014,6 +1815,155 @@ static void up_set_format(struct uart_dev_s *dev) #endif /* CONFIG_SUPPRESS_UART_CONFIG */ /**************************************************************************** + * Name: up_setsuspend + * + * Description: + * Suspend or resume serial peripheral. + * + ****************************************************************************/ + +#ifdef CONFIG_PM +static void up_setsuspend(struct uart_dev_s *dev, bool suspend) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + int passes; +#ifdef SERIAL_HAVE_RXDMA + bool dmarestored = false; +#endif + + if (priv->suspended == suspend) + { + return; + } + + priv->suspended = suspend; + + if (suspend) + { +#ifdef CONFIG_SERIAL_IFLOWCONTROL + if (priv->iflow) + { + /* Force RTS high to prevent further Rx. */ + + stm32_configgpio((priv->rts_gpio & ~GPIO_MODE_MASK) + | (GPIO_OUTPUT | GPIO_OUTPUT_SET)); + } +#endif + + /* Disable interrupts to prevent Tx. */ + + up_disableusartint(priv, &priv->suspended_ie); + + /* Loop until last Tx has completed or, + * until we have been looping for a long time. + */ + + for (passes = 0; passes < 256; passes++) + { + if ((up_serialin(priv, + STM32_USART_ISR_OFFSET) & USART_ISR_TC) != 0) + { + break; + } + } + +#ifdef SERIAL_HAVE_RXDMA + if (priv->rxdma != NULL && !priv->rxdmasusp) + { + /* Suspend Rx DMA. */ + + stm32_dmastop(priv->rxdma); + priv->rxdmasusp = true; + } +#endif + } + else + { +#ifdef SERIAL_HAVE_RXDMA + if (priv->rxdma != NULL && priv->rxdmasusp) + { + /* Re-enable DMA. */ + + up_dma_reenable(priv); + priv->rxdmasusp = false; + + dmarestored = true; + } +#endif + + /* Re-enable interrupts to resume Tx. */ + + up_restoreusartint(priv, priv->suspended_ie); + +#ifdef CONFIG_SERIAL_IFLOWCONTROL + if (priv->iflow) + { + /* Restore peripheral RTS control. */ + + stm32_configgpio(priv->rts_gpio); + } +#endif + } + +#ifdef SERIAL_HAVE_RXDMA + if (dmarestored) + { + irqstate_t flags; + + flags = enter_critical_section(); + + /* Perform initial Rx DMA buffer fetch to wake-up serial device + * activity. + */ + + if (priv->rxdma != NULL) + { + up_dma_rxcallback(priv->rxdma, 0, priv); + } + + leave_critical_section(flags); + } +#endif +} +#endif + +/**************************************************************************** + * Name: up_pm_setsuspend + * + * Description: + * Suspend or resume serial peripherals for/from deep-sleep/stop modes. + * + ****************************************************************************/ + +#ifdef CONFIG_PM +static void up_pm_setsuspend(bool suspend) +{ + int n; + + /* Already in desired state? */ + + if (suspend == g_serialpm.serial_suspended) + { + return; + } + + g_serialpm.serial_suspended = suspend; + + for (n = 0; n < STM32H7_NUSART + STM32H7_NUART; n++) + { + struct up_dev_s *priv = g_uart_devs[n]; + + if (!priv || !priv->initialized) + { + continue; + } + + up_setsuspend(&priv->dev, suspend); + } +} +#endif + +/**************************************************************************** * Name: up_set_apb_clock * * Description: @@ -1214,6 +2164,10 @@ static int up_setup(struct uart_dev_s *dev) regval = up_serialin(priv, STM32_USART_CR1_OFFSET); regval |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_RE); +#ifdef SERIAL_HAVE_RXDMA + regval |= USART_CR1_IDLEIE; +#endif + regval |= USART_CR1_FIFOEN; up_serialout(priv, STM32_USART_CR1_OFFSET, regval); @@ -1232,6 +2186,94 @@ static int up_setup(struct uart_dev_s *dev) } /**************************************************************************** + * Name: up_dma_setup + * + * Description: + * Configure the USART baud, bits, parity, etc. This method is called the + * first time that the serial port is opened. + * + ****************************************************************************/ + +#if defined(SERIAL_HAVE_RXDMA) || defined(SERIAL_HAVE_TXDMA) +static int up_dma_setup(struct uart_dev_s *dev) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + int result; +#if defined(SERIAL_HAVE_RXDMA) + struct stm32_dma_config_s rxdmacfg; +#endif + /* Do the basic UART setup first, unless we are the console */ + + if (!dev->isconsole) + { + result = up_setup(dev); + if (result != OK) + { + return result; + } + } + +#if defined(SERIAL_HAVE_TXDMA) + /* Acquire the Tx DMA channel. This should always succeed. */ + + if (priv->txdma_channel != INVALID_SERIAL_DMA_CHANNEL) + { + priv->txdma = stm32_dmachannel(priv->txdma_channel); + + nxsem_init(&priv->txdmasem, 0, 0); + nxsem_set_protocol(&priv->txdmasem, SEM_PRIO_NONE); + + /* Enable receive Tx DMA for the UART */ + + modifyreg32(priv->usartbase + STM32_USART_CR3_OFFSET, + 0, USART_CR3_DMAT); + } +#endif + +#if defined(SERIAL_HAVE_RXDMA) + /* Acquire the Rx DMA channel. This should always succeed. */ + + if (priv->rxdma_channel != INVALID_SERIAL_DMA_CHANNEL) + { + priv->rxdma = stm32_dmachannel(priv->rxdma_channel); + + /* Configure for circular DMA reception into the RX FIFO */ + + rxdmacfg.paddr = priv->usartbase + STM32_USART_RDR_OFFSET; + rxdmacfg.maddr = (uint32_t)priv->rxfifo; + rxdmacfg.ndata = RXDMA_BUFFER_SIZE; + rxdmacfg.cfg1 = SERIAL_RXDMA_CONTROL_WORD; + rxdmacfg.cfg2 = 0; + stm32_dmasetup(priv->rxdma, &rxdmacfg); + + /* Reset our DMA shadow pointer and Rx data availability count to + * match the address just programmed above. + */ + + priv->rxdmanext = 0; +#ifdef CONFIG_ARMV7M_DCACHE + priv->rxdmaavail = 0; +#endif + + /* Enable receive Rx DMA for the UART */ + + modifyreg32(priv->usartbase + STM32_USART_CR3_OFFSET, + 0, USART_CR3_DMAR); + + /* Start the DMA channel, and arrange for callbacks at the half and + * full points in the FIFO. This ensures that we have half a FIFO + * worth of time to claim bytes before they are overwritten. + */ + + stm32_dmastart(priv->rxdma, up_dma_rxcallback, (void *)priv, true); + } +#endif + + return OK; +} +#endif + +/**************************************************************************** * Name: up_shutdown * * Description: @@ -1298,6 +2340,54 @@ static void up_shutdown(struct uart_dev_s *dev) } /**************************************************************************** + * Name: up_dma_shutdown + * + * Description: + * Disable the USART. This method is called when the serial + * port is closed + * + ****************************************************************************/ + +#if defined(SERIAL_HAVE_RXDMA) || defined(SERIAL_HAVE_TXDMA) +static void up_dma_shutdown(struct uart_dev_s *dev) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + + /* Perform the normal UART shutdown */ + + up_shutdown(dev); + +#if defined(SERIAL_HAVE_RXDMA) + /* Stop the RX DMA channel */ + + if (priv->rxdma_channel != INVALID_SERIAL_DMA_CHANNEL) + { + stm32_dmastop(priv->rxdma); + + /* Release the RX DMA channel */ + + stm32_dmafree(priv->rxdma); + priv->rxdma = NULL; + } +#endif + +#if defined(SERIAL_HAVE_TXDMA) + /* Stop the TX DMA channel */ + + if (priv->txdma_channel != INVALID_SERIAL_DMA_CHANNEL) + { + stm32_dmastop(priv->txdma); + + /* Release the TX DMA channel */ + + stm32_dmafree(priv->txdma); + priv->txdma = NULL; + } +#endif +} +#endif + +/**************************************************************************** * Name: up_attach * * Description: @@ -1365,8 +2455,6 @@ static void up_detach(struct uart_dev_s *dev) static int up_interrupt(int irq, void *context, FAR void *arg) { struct up_dev_s *priv = (struct up_dev_s *)arg; - int passes; - bool handled; DEBUGASSERT(priv != NULL); @@ -1376,100 +2464,101 @@ static int up_interrupt(int irq, void *context, FAR void *arg) pm_activity(PM_IDLE_DOMAIN, CONFIG_STM32H7_PM_SERIAL_ACTIVITY); #endif - /* Loop until there are no characters to be transferred or, - * until we have been looping for a long time. - */ + /* Get the masked USART status word. */ - handled = true; - for (passes = 0; passes < 256 && handled; passes++) - { - handled = false; + priv->sr = up_serialin(priv, STM32_USART_ISR_OFFSET); - /* Get the masked USART status word. */ + /* USART interrupts: + * + * Enable Status Meaning Usage + * ---------------- -------------- ------------------------ ---------- + * USART_CR1_IDLEIE USART_ISR_IDLE Idle Line Detected + * USART_CR1_RXNEIE USART_ISR_RXNE Received Data Ready + * to be Read + * " " USART_ISR_ORE Overrun Error Detected + * USART_CR1_TCIE USART_ISR_TC Transmission Complete (used only + * for RS-485) + * USART_CR1_TXEIE USART_ISR_TXE Transmit Data Register + * Empty + * USART_CR1_PEIE USART_ISR_PE Parity Error + * + * USART_CR2_LBDIE USART_ISR_LBD Break Flag (not used) + * USART_CR3_EIE USART_ISR_FE Framing Error + * " " USART_ISR_NF Noise Error + * " " USART_ISR_ORE Overrun Error Detected + * USART_CR3_CTSIE USART_ISR_CTS CTS flag (not used) + * + * NOTE: + * Some of these status bits must be cleared by explicitly writing zero + * to the SR register: USART_ISR_CTS, USART_ISR_LBD. Note of those are + * currently being used. + */ - priv->sr = up_serialin(priv, STM32_USART_ISR_OFFSET); +#ifdef HAVE_RS485 + /* Transmission of whole buffer is over - TC is set, TXEIE is cleared. + * Note - this should be first, to have the most recent TC bit value + * from SR register - sending data affects TC, but without refresh we + * will not know that... + */ - /* USART interrupts: - * - * Enable Status Meaning Usage - * ---------------- -------------- ------------------------ ---------- - * USART_CR1_IDLEIE USART_ISR_IDLE Idle Line Detected (not used) - * USART_CR1_RXNEIE USART_ISR_RXNE Received Data Ready - * to be Read - * " " USART_ISR_ORE Overrun Error Detected - * USART_CR1_TCIE USART_ISR_TC Transmission Complete (used only - * for RS-485) - * USART_CR1_TXEIE USART_ISR_TXE Transmit Data Register - * Empty - * USART_CR1_PEIE USART_ISR_PE Parity Error - * - * USART_CR2_LBDIE USART_ISR_LBD Break Flag (not used) - * USART_CR3_EIE USART_ISR_FE Framing Error - * " " USART_ISR_NF Noise Error - * " " USART_ISR_ORE Overrun Error Detected - * USART_CR3_CTSIE USART_ISR_CTS CTS flag (not used) - * - * NOTE: - * Some of these status bits must be cleared by explicitly writing zero - * to the SR register: USART_ISR_CTS, USART_ISR_LBD. Note of those are - * currently being used. - */ + if ((priv->sr & USART_ISR_TC) != 0 && + (priv->ie & USART_CR1_TCIE) != 0 && + (priv->ie & USART_CR1_TXEIE) == 0) + { + stm32_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity); + up_restoreusartint(priv, priv->ie & ~USART_CR1_TCIE); + } +#endif -#ifdef HAVE_RS485 - /* Transmission of whole buffer is over - TC is set, TXEIE is cleared. - * Note - this should be first, to have the most recent TC bit value - * from SR register - sending data affects TC, but without refresh we - * will not know that... - */ +#ifdef SERIAL_HAVE_RXDMA + /* The line going to idle, deliver any fractions of RX data */ - if ((priv->sr & USART_ISR_TC) != 0 && - (priv->ie & USART_CR1_TCIE) != 0 && - (priv->ie & USART_CR1_TXEIE) == 0) + if ((priv->sr & USART_ISR_IDLE) != 0) + { + up_serialout(priv, STM32_USART_ICR_OFFSET, USART_ICR_IDLECF); + if (priv->rxdma != 0) { - stm32_gpiowrite(priv->rs485_dir_gpio, !priv->rs485_dir_polarity); - up_restoreusartint(priv, priv->ie & ~USART_CR1_TCIE); + up_dma_rxcallback(priv->rxdma, 0, priv); } + } #endif - /* Handle incoming, receive bytes. */ + /* Handle incoming, receive bytes. */ - if ((priv->sr & USART_ISR_RXNE) != 0 && - (priv->ie & USART_CR1_RXNEIE) != 0) - { - /* Received data ready... process incoming bytes. - * NOTE the check for RXNEIE: We cannot call uart_recvchards of - * RX interrupts are disabled. - */ + if ((priv->sr & USART_ISR_RXNE) != 0 && + (priv->ie & USART_CR1_RXNEIE) != 0) + { + /* Received data ready... process incoming bytes. + * NOTE the check for RXNEIE: We cannot call uart_recvchards of + * RX interrupts are disabled. + */ - uart_recvchars(&priv->dev); - handled = true; - } + uart_recvchars(&priv->dev); + } - /* We may still have to read from the DR register to clear any pending - * error conditions. - */ + /* We may still have to read from the DR register to clear any pending + * error conditions. + */ - else if ((priv->sr & - (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE)) != 0) - { - /* These errors are cleared by writing the corresponding bit to the - * interrupt clear register (ICR). - */ + else if ((priv->sr & + (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE)) != 0) + { + /* These errors are cleared by writing the corresponding bit to the + * interrupt clear register (ICR). + */ - up_serialout(priv, STM32_USART_ICR_OFFSET, - (USART_ICR_NCF | USART_ICR_ORECF | USART_ICR_FECF)); - } + up_serialout(priv, STM32_USART_ICR_OFFSET, + (USART_ICR_NCF | USART_ICR_ORECF | USART_ICR_FECF)); + } - /* Handle outgoing, transmit bytes */ + /* Handle outgoing, transmit bytes */ - if ((priv->sr & USART_ISR_TXE) != 0 && - (priv->ie & USART_CR1_TXEIE) != 0) - { - /* Transmit data register empty ... process outgoing bytes */ + if ((priv->sr & USART_ISR_TXE) != 0 && + (priv->ie & USART_CR1_TXEIE) != 0) + { + /* Transmit data register empty ... process outgoing bytes */ - uart_xmitchars(&priv->dev); - handled = true; - } + uart_xmitchars(&priv->dev); } return OK; @@ -1842,6 +2931,7 @@ static int up_ioctl(struct file *filep, int cmd, unsigned long arg) * ****************************************************************************/ +#ifndef SERIAL_HAVE_ONLY_RXDMA static int up_receive(struct uart_dev_s *dev, unsigned int *status) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; @@ -1860,6 +2950,7 @@ static int up_receive(struct uart_dev_s *dev, unsigned int *status) return rdr & 0xff; } +#endif /**************************************************************************** * Name: up_rxint @@ -1869,6 +2960,7 @@ static int up_receive(struct uart_dev_s *dev, unsigned int *status) * ****************************************************************************/ +#ifndef SERIAL_HAVE_ONLY_RXDMA static void up_rxint(struct uart_dev_s *dev, bool enable) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; @@ -1917,6 +3009,7 @@ static void up_rxint(struct uart_dev_s *dev, bool enable) up_restoreusartint(priv, ie); leave_critical_section(flags); } +#endif /**************************************************************************** * Name: up_rxavailable @@ -1926,11 +3019,13 @@ static void up_rxint(struct uart_dev_s *dev, bool enable) * ****************************************************************************/ +#ifndef SERIAL_HAVE_ONLY_RXDMA static bool up_rxavailable(struct uart_dev_s *dev) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; return ((up_serialin(priv, STM32_USART_ISR_OFFSET) & USART_ISR_RXNE) != 0); } +#endif /**************************************************************************** * Name: up_rxflowcontrol @@ -2029,30 +3124,368 @@ static bool up_rxflowcontrol(struct uart_dev_s *dev, #endif /**************************************************************************** - * Name: up_send + * Name: up_dma_receive * * Description: - * This method will send one byte on the USART + * 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'. * ****************************************************************************/ -static void up_send(struct uart_dev_s *dev, int ch) +#ifdef SERIAL_HAVE_RXDMA +static int up_dma_receive(struct uart_dev_s *dev, unsigned int *status) { struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + uint32_t nextrx = up_dma_nextrx(priv); + int c = 0; -#ifdef HAVE_RS485 - if (priv->rs485_dir_gpio != 0) + /* Check if more data is available */ + + if (nextrx != priv->rxdmanext) { - stm32_gpiowrite(priv->rs485_dir_gpio, priv->rs485_dir_polarity); - } -#endif +#ifdef CONFIG_ARMV7M_DCACHE + /* If the data cache is enabled, then we will also need to manage + * cache coherency. Are any bytes available in the currently coherent + * region of the data cache? + */ - up_serialout(priv, STM32_USART_TDR_OFFSET, (uint32_t)ch); -} + if (priv->rxdmaavail == 0) + { + uint32_t rxdmaavail; + uintptr_t addr; -/**************************************************************************** - * Name: up_txint - * + /* No.. then we will have to invalidate additional space in the Rx + * DMA buffer. + */ + + if (nextrx > priv->rxdmanext) + { + /* Number of available bytes */ + + rxdmaavail = nextrx - priv->rxdmanext; + } + else + { + /* Number of available bytes up to the end of RXDMA buffer */ + + rxdmaavail = RXDMA_BUFFER_SIZE - priv->rxdmanext; + } + + /* Invalidate the DMA buffer range */ + + addr = (uintptr_t)&priv->rxfifo[priv->rxdmanext]; + up_invalidate_dcache(addr, addr + rxdmaavail); + + /* We don't need to invalidate the data cache for the next + * rxdmaavail number of next bytes. + */ + + priv->rxdmaavail = rxdmaavail; + } + + priv->rxdmaavail--; +#endif + + /* Now read from the DMA buffer */ + + c = priv->rxfifo[priv->rxdmanext]; + + priv->rxdmanext++; + if (priv->rxdmanext == RXDMA_BUFFER_SIZE) + { + priv->rxdmanext = 0; + } + } + + /* NOTE: If no data is available, then we would return NULL which is, + * of course, valid binary data. The protocol is that the upper half + * driver must call up_dma_rxavailable prior to calling this function to + * assure that this never happens. + */ + + return c; +} +#endif + +/**************************************************************************** + * Name: up_dma_reenable + * + * Description: + * Call to re-enable RX DMA. + * + ****************************************************************************/ + +#if defined(SERIAL_HAVE_RXDMA) && defined(CONFIG_PM) +static void up_dma_reenable(struct up_dev_s *priv) +{ + struct stm32_dma_config_s rxdmacfg; + + /* Configure for circular DMA reception into the RX FIFO */ + + rxdmacfg.paddr = priv->usartbase + STM32_USART_RDR_OFFSET; + rxdmacfg.maddr = (uint32_t)priv->rxfifo; + rxdmacfg.ndata = RXDMA_BUFFER_SIZE; + rxdmacfg.cfg1 = SERIAL_RXDMA_CONTROL_WORD; + rxdmacfg.cfg2 = 0; + stm32_dmasetup(priv->rxdma, &rxdmacfg); + + /* Reset our DMA shadow pointer and Rx data availability count to match + * the address just programmed above. + */ + + priv->rxdmanext = 0; +#ifdef CONFIG_ARMV7M_DCACHE + priv->rxdmaavail = 0; +#endif + + /* Start the DMA channel, and arrange for callbacks at the half and + * full points in the FIFO. This ensures that we have half a FIFO + * worth of time to claim bytes before they are overwritten. + */ + + stm32_dmastart(priv->rxdma, up_dma_rxcallback, (void *)priv, true); + + /* Clear DMA suspended flag. */ + + priv->rxdmasusp = false; +} +#endif + +/**************************************************************************** + * Name: up_dma_rxint + * + * Description: + * Call to enable or disable RX interrupts + * + ****************************************************************************/ + +#ifdef SERIAL_HAVE_RXDMA +static void up_dma_rxint(struct uart_dev_s *dev, bool enable) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + + /* Enable/disable DMA reception. + * + * Note that it is not safe to check for available bytes and immediately + * pass them to uart_recvchars as that could potentially recurse back + * to us again. Instead, bytes must wait until the next up_dma_poll or + * DMA event. + */ + + priv->rxenable = enable; +} +#endif + +/**************************************************************************** + * Name: up_dma_rxavailable + * + * Description: + * Return true if the receive register is not empty + * + ****************************************************************************/ + +#ifdef SERIAL_HAVE_RXDMA +static bool up_dma_rxavailable(struct uart_dev_s *dev) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + + /* Compare our receive pointer to the current DMA pointer, if they + * do not match, then there are bytes to be received. + */ + + return (up_dma_nextrx(priv) != priv->rxdmanext); +} +#endif + +/**************************************************************************** + * Name: up_dma_txcallback + * + * Description: + * This function clears dma buffer at complete of DMA transfer and wakes up + * threads waiting for space in buffer. + * + ****************************************************************************/ + +#ifdef SERIAL_HAVE_TXDMA +static void up_dma_txcallback(DMA_HANDLE handle, uint8_t status, void *arg) +{ + struct stm32_dma_config_s txdmacfg; + struct up_dev_s *priv = (struct up_dev_s *)arg; + + /* Update 'nbytes' indicating number of bytes actually transferred by DMA. + * This is important to free TX buffer space by 'uart_xmitchars_done'. + */ + + if (status & DMA_SCR_HTIE) + { + priv->dev.dmatx.nbytes += priv->dev.dmatx.length / 2; + } + else if (status & DMA_SCR_TCIE) + { + priv->dev.dmatx.nbytes += priv->dev.dmatx.length; + if (priv->dev.dmatx.nlength) + { + /* Set up DMA on next buffer */ + + txdmacfg.paddr = priv->usartbase + STM32_USART_TDR_OFFSET; + txdmacfg.maddr = (uint32_t) priv->dev.dmatx.nbuffer; + txdmacfg.ndata = (size_t) priv->dev.dmatx.nlength; + txdmacfg.cfg1 = SERIAL_TXDMA_CONTROL_WORD; + txdmacfg.cfg2 = 0; + stm32_dmasetup(priv->txdma, &txdmacfg); + + /* Set length for next completion */ + + priv->dev.dmatx.length = priv->dev.dmatx.nlength; + priv->dev.dmatx.nlength = 0; + + /* Start transmission with the callback on DMA completion */ + + stm32_dmastart(priv->txdma, up_dma_txcallback, + (void *)priv, false); + + return; + } + } + + nxsem_post(&priv->txdmasem); + + /* Adjust the pointers */ + + uart_xmitchars_done(&priv->dev); +} +#endif + +/**************************************************************************** + * Name: up_dma_txavailable + * + * Description: + * Informs DMA that Tx data is available and is ready for transfer. + * + ****************************************************************************/ + +#ifdef SERIAL_HAVE_TXDMA +static void up_dma_txavailable(struct uart_dev_s *dev) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + size_t resid = 0; + + resid = stm32_dmaresidual(priv->txdma); + + /* Only send when the DMA is idle */ + + if (resid != 0) + { + nxsem_wait(&priv->txdmasem); + } + + uart_xmitchars_dma(dev); +} +#endif + +/**************************************************************************** + * Name: up_dma_send + * + * Description: + * Called (usually) from the interrupt level to start DMA transfer. + * (Re-)Configures DMA Stream updating buffer and buffer length. + * + ****************************************************************************/ + +#ifdef SERIAL_HAVE_TXDMA +static void up_dma_send(struct uart_dev_s *dev) +{ + struct stm32_dma_config_s txdmacfg; + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + + /* We need to stop DMA before reconfiguration */ + + stm32_dmastop(priv->txdma); + + /* Reset the number sent */ + + dev->dmatx.nbytes = 0; + + /* Flush the contents of the TX buffer into physical memory */ + + up_clean_dcache((uintptr_t)dev->dmatx.buffer, + (uintptr_t)dev->dmatx.buffer + dev->dmatx.length); + + /* Is this a split transfer */ + + if (dev->dmatx.nbuffer) + { + /* Flush the contents of the next TX buffer into physical memory */ + + up_clean_dcache((uintptr_t)dev->dmatx.nbuffer, + (uintptr_t)dev->dmatx.nbuffer + dev->dmatx.nlength); + } + + /* Make use of setup function to update buffer and its length for next + * transfer + */ + + txdmacfg.paddr = priv->usartbase + STM32_USART_TDR_OFFSET; + txdmacfg.maddr = (uint32_t) dev->dmatx.buffer; + txdmacfg.ndata = (size_t) dev->dmatx.length; + txdmacfg.cfg1 = SERIAL_TXDMA_CONTROL_WORD; + txdmacfg.cfg2 = 0; + stm32_dmasetup(priv->txdma, &txdmacfg); + + /* Start transmission with the callback on DMA completion */ + + stm32_dmastart(priv->txdma, up_dma_txcallback, (void *)priv, false); +} +#endif + +/**************************************************************************** + * Name: up_send + * + * Description: + * This method will send one byte on the USART + * + ****************************************************************************/ + +static void up_send(struct uart_dev_s *dev, int ch) +{ + struct up_dev_s *priv = (struct up_dev_s *)dev->priv; + +#ifdef HAVE_RS485 + if (priv->rs485_dir_gpio != 0) + { + stm32_gpiowrite(priv->rs485_dir_gpio, priv->rs485_dir_polarity); + } +#endif + + up_serialout(priv, STM32_USART_TDR_OFFSET, (uint32_t)ch); +} + +/**************************************************************************** + * Name: up_dma_txint + * + * Description: + * Call to enable or disable TX interrupts from the UART. + * + ****************************************************************************/ + +#ifdef SERIAL_HAVE_TXDMA +static void up_dma_txint(struct uart_dev_s *dev, bool enable) +{ + /* Nothing to do. */ + + /* In case of DMA transfer we do not want to make use of UART interrupts. + * Instead, we use DMA interrupts that are activated once during boot + * sequence. Furthermore we can use up_dma_txcallback() to handle staff at + * half DMA transfer or after transfer completion (depending configuration, + * see stm32_dmastart(...) ). + */ +} +#endif + +/**************************************************************************** + * Name: up_txint + * * Description: * Call to enable or disable TX interrupts * @@ -2134,6 +3567,44 @@ static bool up_txready(struct uart_dev_s *dev) } /**************************************************************************** + * Name: up_dma_rxcallback + * + * Description: + * This function checks the current DMA state and calls the generic + * serial stack when bytes appear to be available. + * + ****************************************************************************/ + +#ifdef SERIAL_HAVE_RXDMA +static void up_dma_rxcallback(DMA_HANDLE handle, uint8_t status, void *arg) +{ + struct up_dev_s *priv = (struct up_dev_s *)arg; + + if (priv->rxenable && up_dma_rxavailable(&priv->dev)) + { + uart_recvchars(&priv->dev); + } + + /* 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. + */ + + priv->sr = up_serialin(priv, STM32_USART_ISR_OFFSET); + + if ((priv->sr & (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE)) != 0) + { + up_serialout(priv, STM32_USART_ICR_OFFSET, + (USART_ICR_NCF | USART_ICR_ORECF | USART_ICR_FECF)); + } +} +#endif + +/**************************************************************************** * Name: up_pm_notify * * Description: @@ -2161,27 +3632,27 @@ static void up_pm_notify(struct pm_callback_s *cb, int domain, { switch (pmstate) { - case(PM_NORMAL): + case PM_NORMAL: { - /* Logic for PM_NORMAL goes here */ + up_pm_setsuspend(false); } break; - case(PM_IDLE): + case PM_IDLE: { - /* Logic for PM_IDLE goes here */ + up_pm_setsuspend(false); } break; - case(PM_STANDBY): + case PM_STANDBY: { - /* Logic for PM_STANDBY goes here */ + up_pm_setsuspend(true); } break; - case(PM_SLEEP): + case PM_SLEEP: { - /* Logic for PM_SLEEP goes here */ + up_pm_setsuspend(true); } break; @@ -2231,8 +3702,77 @@ static void up_pm_notify(struct pm_callback_s *cb, int domain, static int up_pm_prepare(struct pm_callback_s *cb, int domain, enum pm_state_e pmstate) { + int n; + /* Logic to prepare for a reduced power state goes here. */ + switch (pmstate) + { + case PM_NORMAL: + case PM_IDLE: + break; + + case PM_STANDBY: + case PM_SLEEP: + +#ifdef SERIAL_HAVE_RXDMA + /* Flush Rx DMA buffers before checking state of serial device + * buffers. + */ + + stm32_serial_dma_poll(); +#endif + + /* Check if any of the active ports have data pending on Tx/Rx + * buffers. + */ + + for (n = 0; n < STM32H7_NUSART + STM32H7_NUART; n++) + { + struct up_dev_s *priv = g_uart_devs[n]; + + if (!priv || !priv->initialized) + { + /* Not active, skip. */ + + continue; + } + + if (priv->suspended) + { + /* Port already suspended, skip. */ + + continue; + } + + if (priv->dev.isconsole) + { + /* Allow losing some debug traces. */ + + continue; + } + + /* Check if port has data pending (Rx & Tx). */ + + if (priv->dev.xmit.head != priv->dev.xmit.tail) + { + return ERROR; + } + + if (priv->dev.recv.head != priv->dev.recv.tail) + { + return ERROR; + } + } + break; + + default: + + /* Should not get here */ + + break; + } + return OK; } #endif @@ -2324,10 +3864,10 @@ void arm_serialinit(void) int ret; #endif +#ifdef CONFIG_PM /* Register to receive power management callbacks */ -#ifdef CONFIG_PM - ret = pm_register(&g_serialcb); + ret = pm_register(&g_serialpm.pm_cb); DEBUGASSERT(ret == OK); UNUSED(ret); #endif @@ -2346,6 +3886,11 @@ void arm_serialinit(void) minor = 1; #endif +#if defined(SERIAL_HAVE_CONSOLE_RXDMA) || defined(SERIAL_HAVE_CONSOLE_TXDMA) + /* If we need to re-initialise the console to enable DMA do that here. */ + + up_dma_setup(&g_uart_devs[CONSOLE_UART - 1]->dev); +#endif #endif /* CONSOLE_UART > 0 */ /* Register all remaining USARTs */ @@ -2379,10 +3924,88 @@ void arm_serialinit(void) } /**************************************************************************** + * Name: stm32_serial_dma_poll + * + * Description: + * Checks receive DMA buffers for received bytes that have not accumulated + * to the point where the DMA half/full interrupt has triggered. + * + * This function should be called from a timer or other periodic context. + * + ****************************************************************************/ + +#ifdef SERIAL_HAVE_RXDMA +void stm32_serial_dma_poll(void) +{ + irqstate_t flags; + + flags = enter_critical_section(); + +#ifdef CONFIG_USART1_RXDMA + if (g_usart1priv.rxdma != NULL) + { + up_dma_rxcallback(g_usart1priv.rxdma, 0, &g_usart1priv); + } +#endif + +#ifdef CONFIG_USART2_RXDMA + if (g_usart2priv.rxdma != NULL) + { + up_dma_rxcallback(g_usart2priv.rxdma, 0, &g_usart2priv); + } +#endif + +#ifdef CONFIG_USART3_RXDMA + if (g_usart3priv.rxdma != NULL) + { + up_dma_rxcallback(g_usart3priv.rxdma, 0, &g_usart3priv); + } +#endif + +#ifdef CONFIG_UART4_RXDMA + if (g_uart4priv.rxdma != NULL) + { + up_dma_rxcallback(g_uart4priv.rxdma, 0, &g_uart4priv); + } +#endif + +#ifdef CONFIG_UART5_RXDMA + if (g_uart5priv.rxdma != NULL) + { + up_dma_rxcallback(g_uart5priv.rxdma, 0, &g_uart5priv); + } +#endif + +#ifdef CONFIG_USART6_RXDMA + if (g_usart6priv.rxdma != NULL) + { + up_dma_rxcallback(g_usart6priv.rxdma, 0, &g_usart6priv); + } +#endif + +#ifdef CONFIG_UART7_RXDMA + if (g_uart7priv.rxdma != NULL) + { + up_dma_rxcallback(g_uart7priv.rxdma, 0, &g_uart7priv); + } +#endif + +#ifdef CONFIG_UART8_RXDMA + if (g_uart8priv.rxdma != NULL) + { + up_dma_rxcallback(g_uart8priv.rxdma, 0, &g_uart8priv); + } +#endif + + leave_critical_section(flags); +} +#endif + +/**************************************************************************** * Name: up_putc * * Description: - * Provide priority, low-level access to support OS debug writes + * Provide priority, low-level access to support OS debug writes * ****************************************************************************/ diff --git a/arch/arm/src/stm32h7/stm32_uart.h b/arch/arm/src/stm32h7/stm32_uart.h index d4e48c6..8a53fa8 100644 --- a/arch/arm/src/stm32h7/stm32_uart.h +++ b/arch/arm/src/stm32h7/stm32_uart.h @@ -168,6 +168,268 @@ # undef HAVE_CONSOLE #endif +#if !defined(CONFIG_ARCH_DMA) +# undef CONFIG_USART1_RXDMA +# undef CONFIG_USART1_TXDMA +# undef CONFIG_USART2_RXDMA +# undef CONFIG_USART2_TXDMA +# undef CONFIG_USART3_RXDMA +# undef CONFIG_USART3_TXDMA +# undef CONFIG_UART4_RXDMA +# undef CONFIG_UART4_TXDMA +# undef CONFIG_UART5_RXDMA +# undef CONFIG_UART5_TXDMA +# undef CONFIG_USART6_RXDMA +# undef CONFIG_USART6_TXDMA +# undef CONFIG_UART7_RXDMA +# undef CONFIG_UART7_TXDMA +# undef CONFIG_UART8_RXDMA +# undef CONFIG_UART8_TXDMA +#endif + +/* Disable the DMA configuration on all unused USARTs */ + +#ifndef CONFIG_USART1_SERIALDRIVER +# undef CONFIG_USART1_RXDMA +# undef CONFIG_USART1_TXDMA +#endif + +#ifndef CONFIG_USART2_SERIALDRIVER +# undef CONFIG_USART2_RXDMA +# undef CONFIG_USART2_TXDMA +#endif + +#ifndef CONFIG_USART3_SERIALDRIVER +# undef CONFIG_USART3_RXDMA +# undef CONFIG_USART3_TXDMA +#endif + +#ifndef CONFIG_UART4_SERIALDRIVER +# undef CONFIG_UART4_RXDMA +# undef CONFIG_UART4_TXDMA +#endif + +#ifndef CONFIG_UART5_SERIALDRIVER +# undef CONFIG_UART5_RXDMA +# undef CONFIG_UART5_TXDMA +#endif + +#ifndef CONFIG_USART6_SERIALDRIVER +# undef CONFIG_USART6_RXDMA +# undef CONFIG_USART6_TXDMA +#endif + +#ifndef CONFIG_UART7_SERIALDRIVER +# undef CONFIG_UART7_RXDMA +# undef CONFIG_UART7_TXDMA +#endif + +#ifndef CONFIG_UART8_SERIALDRIVER +# undef CONFIG_UART8_RXDMA +# undef CONFIG_UART8_TXDMA +#endif + +/* Is DMA available on any (enabled) U[S]ART? */ + +#undef SERIAL_HAVE_RXDMA +#if defined(CONFIG_USART1_RXDMA) || defined(CONFIG_USART2_RXDMA) || \ + defined(CONFIG_USART3_RXDMA) || defined(CONFIG_UART4_RXDMA) || \ + defined(CONFIG_UART5_RXDMA) || defined(CONFIG_USART6_RXDMA) || \ + defined(CONFIG_UART7_RXDMA) || defined(CONFIG_UART8_RXDMA) +# define SERIAL_HAVE_RXDMA 1 +#endif + +/* Is TX DMA available on any (enabled) U[S]ART? */ + +#undef SERIAL_HAVE_TXDMA +#if defined(CONFIG_USART1_TXDMA) || defined(CONFIG_USART2_TXDMA) || \ + defined(CONFIG_USART3_TXDMA) || defined(CONFIG_UART4_TXDMA) || \ + defined(CONFIG_UART5_TXDMA) || defined(CONFIG_USART6_TXDMA) || \ + defined(CONFIG_UART7_TXDMA) || defined(CONFIG_UART8_TXDMA) +# define SERIAL_HAVE_TXDMA 1 +#endif + +/* Is RX DMA used on the console UART? */ + +#undef SERIAL_HAVE_CONSOLE_RXDMA +#if defined(CONFIG_USART1_SERIAL_CONSOLE) && defined(CONFIG_USART1_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +#elif defined(CONFIG_USART2_SERIAL_CONSOLE) && defined(CONFIG_USART2_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +#elif defined(CONFIG_USART3_SERIAL_CONSOLE) && defined(CONFIG_USART3_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +#elif defined(CONFIG_UART4_SERIAL_CONSOLE) && defined(CONFIG_UART4_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +#elif defined(CONFIG_UART5_SERIAL_CONSOLE) && defined(CONFIG_UART5_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +#elif defined(CONFIG_USART6_SERIAL_CONSOLE) && defined(CONFIG_USART6_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +#elif defined(CONFIG_UART7_SERIAL_CONSOLE) && defined(CONFIG_UART7_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +#elif defined(CONFIG_UART8_SERIAL_CONSOLE) && defined(CONFIG_UART8_RXDMA) +# define SERIAL_HAVE_CONSOLE_RXDMA 1 +#endif + +/* Is TX DMA used on the console UART? */ + +#undef SERIAL_HAVE_CONSOLE_TXDMA +#if defined(CONFIG_USART1_SERIAL_CONSOLE) && defined(CONFIG_USART1_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +#elif defined(CONFIG_USART2_SERIAL_CONSOLE) && defined(CONFIG_USART2_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +#elif defined(CONFIG_USART3_SERIAL_CONSOLE) && defined(CONFIG_USART3_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +#elif defined(CONFIG_UART4_SERIAL_CONSOLE) && defined(CONFIG_UART4_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +#elif defined(CONFIG_UART5_SERIAL_CONSOLE) && defined(CONFIG_UART5_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +#elif defined(CONFIG_USART6_SERIAL_CONSOLE) && defined(CONFIG_USART6_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +#elif defined(CONFIG_UART7_SERIAL_CONSOLE) && defined(CONFIG_UART7_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +#elif defined(CONFIG_UART8_SERIAL_CONSOLE) && defined(CONFIG_UART8_TXDMA) +# define SERIAL_HAVE_CONSOLE_TXDMA 1 +#endif + +/* Is RX DMA used on all (enabled) USARTs */ + +#define SERIAL_HAVE_ONLY_RXDMA 1 +#if defined(CONFIG_STM32H7_USART1) && !defined(CONFIG_USART1_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#elif defined(CONFIG_STM32H7_USART2) && !defined(CONFIG_USART2_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#elif defined(CONFIG_STM32H7_USART3) && !defined(CONFIG_USART3_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#elif defined(CONFIG_STM32H7_UART4) && !defined(CONFIG_UART4_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#elif defined(CONFIG_STM32H7_UART5) && !defined(CONFIG_UART5_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#elif defined(CONFIG_STM32H7_USART6) && !defined(CONFIG_USART6_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#elif defined(CONFIG_STM32H7_UART7) && !defined(CONFIG_UART7_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#elif defined(CONFIG_STM32H7_UART8) && !defined(CONFIG_UART8_RXDMA) +# undef SERIAL_HAVE_ONLY_RXDMA +#endif + +/* Is TX DMA used on all (enabled) USARTs */ + +#define SERIAL_HAVE_ONLY_TXDMA 1 +#if defined(CONFIG_STM32H7_USART1) && !defined(CONFIG_USART1_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#elif defined(CONFIG_STM32H7_USART2) && !defined(CONFIG_USART2_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#elif defined(CONFIG_STM32H7_USART3) && !defined(CONFIG_USART3_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#elif defined(CONFIG_STM32H7_UART4) && !defined(CONFIG_UART4_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#elif defined(CONFIG_STM32H7_UART5) && !defined(CONFIG_UART5_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#elif defined(CONFIG_STM32H7_USART6) && !defined(CONFIG_USART6_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#elif defined(CONFIG_STM32H7_UART7) && !defined(CONFIG_UART7_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#elif defined(CONFIG_STM32H7_UART8) && !defined(CONFIG_UART8_TXDMA) +# undef SERIAL_HAVE_ONLY_TXDMA +#endif + +#undef SERIAL_HAVE_ONLY_DMA +#if defined(SERIAL_HAVE_ONLY_RXDMA) && defined(SERIAL_HAVE_ONLY_TXDMA) +# define SERIAL_HAVE_ONLY_DMA 1 +#endif + +/* No DMA ops */ + +#undef SERIAL_HAVE_NODMA_OPS +#if defined(CONFIG_STM32H7_USART1) && !defined(CONFIG_USART1_RXDMA) && \ + !defined(CONFIG_USART1_TXDMA) +# define SERIAL_HAVE_NODMA_OPS +#elif defined(CONFIG_STM32H7_USART2) && !defined(CONFIG_USART2_RXDMA) && \ + !defined(CONFIG_USART2_TXDMA) +# define SERIAL_HAVE_NODMA_OPS +#elif defined(CONFIG_STM32H7_USART3) && !defined(CONFIG_USART3_RXDMA) && \ + !defined(CONFIG_USART3_TXDMA) +# define SERIAL_HAVE_NODMA_OPS +#elif defined(CONFIG_STM32H7_UART4) && !defined(CONFIG_UART4_RXDMA) && \ + !defined(CONFIG_UART4_TXDMA) +# define SERIAL_HAVE_NODMA_OPS +#elif defined(CONFIG_STM32H7_UART5) && !defined(CONFIG_UART5_RXDMA) && \ + !defined(CONFIG_UART5_TXDMA) +# define SERIAL_HAVE_NODMA_OPS +#elif defined(CONFIG_STM32H7_USART6) && !defined(CONFIG_USART6_RXDMA) && \ + !defined(CONFIG_USART6_TXDMA) +# define SERIAL_HAVE_NODMA_OPS +#elif defined(CONFIG_STM32H7_UART7) && !defined(CONFIG_UART7_RXDMA) && \ + !defined(CONFIG_UART7_TXDMA) +# define SERIAL_HAVE_NODMA_OPS +#elif defined(CONFIG_STM32H7_UART8) && !defined(CONFIG_UART8_RXDMA) && \ + !defined(CONFIG_UART8_TXDMA) +# define SERIAL_HAVE_NODMA_OPS +#endif + +/* RX+TX DMA ops */ + +#undef SERIAL_HAVE_RXTXDMA_OPS +#if defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_TXDMA) +# define SERIAL_HAVE_RXTXDMA_OPS +#elif defined(CONFIG_USART2_RXDMA) && defined(CONFIG_USART2_TXDMA) +# define SERIAL_HAVE_RXTXDMA_OPS +#elif defined(CONFIG_USART3_RXDMA) && defined(CONFIG_USART3_TXDMA) +# define SERIAL_HAVE_RXTXDMA_OPS +#elif defined(CONFIG_UART4_RXDMA) && defined(CONFIG_UART4_TXDMA) +# define SERIAL_HAVE_RXTXDMA_OPS +#elif defined(CONFIG_UART5_RXDMA) && defined(CONFIG_UART5_TXDMA) +# define SERIAL_HAVE_RXTXDMA_OPS +#elif defined(CONFIG_USART6_RXDMA) && defined(CONFIG_USART6_TXDMA) +# define SERIAL_HAVE_RXTXDMA_OPS +#elif defined(CONFIG_UART7_RXDMA) && defined(CONFIG_UART7_TXDMA) +# define SERIAL_HAVE_RXTXDMA_OPS +#elif defined(CONFIG_UART8_RXDMA) && defined(CONFIG_UART8_TXDMA) +# define SERIAL_HAVE_RXTXDMA_OPS +#endif + +/* TX DMA ops */ + +#undef SERIAL_HAVE_TXDMA_OPS +#if !defined(CONFIG_USART1_RXDMA) && defined(CONFIG_USART1_TXDMA) +# define SERIAL_HAVE_TXDMA_OPS +#elif !defined(CONFIG_USART2_RXDMA) && defined(CONFIG_USART2_TXDMA) +# define SERIAL_HAVE_TXDMA_OPS +#elif !defined(CONFIG_USART3_RXDMA) && defined(CONFIG_USART3_TXDMA) +# define SERIAL_HAVE_TXDMA_OPS +#elif !defined(CONFIG_UART4_RXDMA) && defined(CONFIG_UART4_TXDMA) +# define SERIAL_HAVE_TXDMA_OPS +#elif !defined(CONFIG_UART5_RXDMA) && defined(CONFIG_UART5_TXDMA) +# define SERIAL_HAVE_TXDMA_OPS +#elif !defined(CONFIG_USART6_RXDMA) && defined(CONFIG_USART6_TXDMA) +# define SERIAL_HAVE_TXDMA_OPS +#elif !defined(CONFIG_UART7_RXDMA) && defined(CONFIG_UART7_TXDMA) +# define SERIAL_HAVE_TXDMA_OPS +#elif !defined(CONFIG_UART8_RXDMA) && defined(CONFIG_UART8_TXDMA) +# define SERIAL_HAVE_TXDMA_OPS +#endif + +/* RX DMA ops */ + +#undef SERIAL_HAVE_RXDMA_OPS +#if defined(CONFIG_USART1_RXDMA) && !defined(CONFIG_USART1_TXDMA) +# define SERIAL_HAVE_RXDMA_OPS +#elif defined(CONFIG_USART2_RXDMA) && !defined(CONFIG_USART2_TXDMA) +# define SERIAL_HAVE_RXDMA_OPS +#elif defined(CONFIG_USART3_RXDMA) && !defined(CONFIG_USART3_TXDMA) +# define SERIAL_HAVE_RXDMA_OPS +#elif defined(CONFIG_UART4_RXDMA) && !defined(CONFIG_UART4_TXDMA) +# define SERIAL_HAVE_RXDMA_OPS +#elif defined(CONFIG_UART5_RXDMA) && !defined(CONFIG_UART5_TXDMA) +# define SERIAL_HAVE_RXDMA_OPS +#elif defined(CONFIG_USART6_RXDMA) && !defined(CONFIG_USART6_TXDMA) +# define SERIAL_HAVE_RXDMA_OPS +#elif defined(CONFIG_UART7_RXDMA) && !defined(CONFIG_UART7_TXDMA) +# define SERIAL_HAVE_RXDMA_OPS +#elif defined(CONFIG_UART8_RXDMA) && !defined(CONFIG_UART8_TXDMA) +# define SERIAL_HAVE_RXDMA_OPS +#endif + /* Is RS-485 used? */ #if defined(CONFIG_USART1_RS485) || defined(CONFIG_USART2_RS485) || \ @@ -180,7 +442,7 @@ #ifdef HAVE_RS485 # define USART_CR1_USED_INTS (USART_CR1_RXNEIE | USART_CR1_TXEIE | USART_CR1_PEIE | USART_CR1_TCIE) #else -# define USART_CR1_USED_INTS (USART_CR1_RXNEIE | USART_CR1_TXEIE | USART_CR1_PEIE) +# define USART_CR1_USED_INTS (USART_CR1_RXNEIE | USART_CR1_TXEIE | USART_CR1_PEIE | USART_CR1_IDLEIE) #endif /**************************************************************************** @@ -207,6 +469,23 @@ extern "C" ****************************************************************************/ /**************************************************************************** + * Name: stm32_serial_dma_poll + * + * Description: + * Must be called periodically if any STM32 UART is configured for DMA. + * The DMA callback is triggered for each fifo size/2 bytes, but this can + * result in some bytes being transferred but not collected if the incoming + * data is not a whole multiple of half the FIFO size. + * + * May be safely called from either interrupt or thread context. + * + ****************************************************************************/ + +#ifdef SERIAL_HAVE_RXDMA +void stm32_serial_dma_poll(void); +#endif + +/**************************************************************************** * Name: stm32_serial_get_uart * * Description:
