diff --git a/ChangeLog b/ChangeLog index 7a21826d7a0..6cc87c42c99 100755 --- a/ChangeLog +++ b/ChangeLog @@ -11084,4 +11084,7 @@ * drivers/mtd/mtd_progmem.c: Add an upper half MTD device that can use the interfaces defined in included/nuttx/progmem.h to provide a standard MTD interface (2015-11-12). + * drivers/serial/serial.c, serialirq.c and include/nuttx/serial/serial.h: + Implement high level DMA infrastructure for serial devices. From + Max Neklyudov (2015-11-12). diff --git a/configs b/configs index 758aa6d2364..4d23ce39bf6 160000 --- a/configs +++ b/configs @@ -1 +1 @@ -Subproject commit 758aa6d23647d98fb1a6064175a8abd6c9cc28b2 +Subproject commit 4d23ce39bf6bbf3ca0161ff6247fc9fe1610890f diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 6e7d7865e55..ec333bc0d81 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -540,6 +540,10 @@ config SERIAL_OFLOWCONTROL bool default n +config SERIAL_DMA + bool + default n + config SERIAL_IFLOWCONTROL_WATERMARKS bool "RX flow control watermarks" default n @@ -762,6 +766,13 @@ config UART_OFLOWCONTROL ---help--- Enable UART CTS flow control +config UART_DMA + bool "UART DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on UART + endmenu menu "UART0 Configuration" @@ -820,6 +831,13 @@ config UART0_OFLOWCONTROL ---help--- Enable UART0 CTS flow control +config UART0_DMA + bool "UART0 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on UART0 + endmenu menu "USART0 Configuration" @@ -878,6 +896,13 @@ config USART0_OFLOWCONTROL ---help--- Enable USART0 CTS flow control +config USART0_DMA + bool "USART0 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on USART0 + endmenu menu "UART1 Configuration" @@ -936,6 +961,13 @@ config UART1_OFLOWCONTROL ---help--- Enable UART1 CTS flow control +config UART1_DMA + bool "UART1 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on UART1 + endmenu menu "USART1 Configuration" @@ -994,6 +1026,13 @@ config USART1_OFLOWCONTROL ---help--- Enable USART1 CTS flow control +config USART1_DMA + bool "USART1 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on USART1 + endmenu menu "UART2 Configuration" @@ -1052,6 +1091,13 @@ config UART2_OFLOWCONTROL ---help--- Enable UART2 CTS flow control +config UART2_DMA + bool "UART2 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on UART2 + endmenu menu "USART2 Configuration" @@ -1110,6 +1156,12 @@ config USART2_OFLOWCONTROL ---help--- Enable USART2 CTS flow control +config USART2_DMA + bool "USART2 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on USART2 endmenu menu "UART3 Configuration" @@ -1168,6 +1220,13 @@ config UART3_OFLOWCONTROL ---help--- Enable UART3 CTS flow control +config UART3_DMA + bool "UART3 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on UART3 + endmenu menu "USART3 Configuration" @@ -1226,6 +1285,13 @@ config USART3_OFLOWCONTROL ---help--- Enable USART3 CTS flow control +config USART3_DMA + bool "USART3 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on USART3 + endmenu menu "UART4 Configuration" @@ -1284,6 +1350,13 @@ config UART4_OFLOWCONTROL ---help--- Enable UART4 CTS flow control +config UART4_DMA + bool "UART4 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on UART4 + endmenu menu "USART4 Configuration" @@ -1342,6 +1415,13 @@ config USART4_OFLOWCONTROL ---help--- Enable USART4 CTS flow control +config USART4_DMA + bool "USART4 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on USART4 + endmenu menu "UART5 Configuration" @@ -1400,6 +1480,13 @@ config UART5_OFLOWCONTROL ---help--- Enable UART5 CTS flow control +config UART5_DMA + bool "UART5 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on UART5 + endmenu menu "USART5 Configuration" @@ -1458,6 +1545,13 @@ config USART5_OFLOWCONTROL ---help--- Enable USART5 CTS flow control +config USART5_DMA + bool "USART5 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on USART5 + endmenu menu "USART6 Configuration" @@ -1516,6 +1610,13 @@ config USART6_OFLOWCONTROL ---help--- Enable USART6 CTS flow control +config USART6_DMA + bool "USART6 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on USART6 + endmenu menu "UART6 Configuration" @@ -1574,6 +1675,13 @@ config UART6_OFLOWCONTROL ---help--- Enable UART6 CTS flow control +config UART6_DMA + bool "UART6 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on UART6 + endmenu menu "USART7 Configuration" @@ -1632,6 +1740,13 @@ config USART7_OFLOWCONTROL ---help--- Enable USART7 CTS flow control +config USART7_DMA + bool "USART7 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on USART7 + endmenu menu "UART7 Configuration" @@ -1690,6 +1805,13 @@ config UART7_OFLOWCONTROL ---help--- Enable UART7 CTS flow control +config UART7_DMA + bool "UART7 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on UART7 + endmenu menu "USART8 Configuration" @@ -1748,6 +1870,13 @@ config USART8_OFLOWCONTROL ---help--- Enable USART8 CTS flow control +config USART8_DMA + bool "USART8 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on USART8 + endmenu menu "UART8 Configuration" @@ -1806,6 +1935,13 @@ config UART8_OFLOWCONTROL ---help--- Enable UART8 CTS flow control +config UART8_DMA + bool "UART8 DMA support" + default n + select SERIAL_DMA + ---help--- + Enable DMA transfers on UART8 + endmenu menu "SCI0 Configuration" diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index 9930810fc76..378a696b4c4 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -249,6 +249,9 @@ static int uart_putxmitchar(FAR uart_dev_t *dev, int ch, bool oktoblock) */ dev->xmitwaiting = true; +#ifdef CONFIG_SERIAL_DMA + uart_dmatxavail(dev); +#endif uart_enabletxint(dev); ret = uart_takesem(&dev->xmitsem, true); uart_disabletxint(dev); @@ -507,6 +510,9 @@ static ssize_t uart_write(FAR struct file *filep, FAR const char *buffer, if (dev->xmit.head != dev->xmit.tail) { +#ifdef CONFIG_SERIAL_DMA + uart_dmatxavail(dev); +#endif uart_enabletxint(dev); } @@ -707,6 +713,20 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen */ flags = irqsave(); + +#ifdef CONFIG_SERIAL_DMA + /* If RX buffer is empty move tail and head to zero position */ + + if (rxbuf->head == rxbuf->tail) + { + rxbuf->head = rxbuf->tail = 0; + } + + /* Notify DMA that there is free space in the RX buffer */ + + uart_dmarxfree(dev); +#endif + uart_enablerxint(dev); #ifdef CONFIG_SERIAL_REMOVABLE @@ -778,6 +798,27 @@ static ssize_t uart_read(FAR struct file *filep, FAR char *buffer, size_t buflen } } +#ifdef CONFIG_SERIAL_DMA + flags = irqsave(); + + /* If RX buffer is empty move tail and head to zero position */ + + if (rxbuf->head == rxbuf->tail) + { + rxbuf->head = rxbuf->tail = 0; + } + + irqrestore(flags); + + /* Notify DMA that there is free space in the RX buffer */ + + uart_dmarxfree(dev); + + /* RX interrupt could be disabled by RX buffer overflow. Enable it now. */ + + uart_enablerxint(dev); +#endif + #ifdef CONFIG_SERIAL_IFLOWCONTROL #ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS /* How many bytes are now buffered */ @@ -1281,6 +1322,12 @@ static int uart_open(FAR struct file *filep) } #endif +#ifdef CONFIG_SERIAL_DMA + /* Notify DMA that there is free space in the RX buffer */ + + uart_dmarxfree(dev); +#endif + /* Enable the RX interrupt */ uart_enablerxint(dev); diff --git a/drivers/serial/serialirq.c b/drivers/serial/serialirq.c index 3962d985cfe..b58867f71de 100644 --- a/drivers/serial/serialirq.c +++ b/drivers/serial/serialirq.c @@ -1,7 +1,7 @@ /************************************************************************************ * drivers/serial/serialirq.c * - * Copyright (C) 2007-2009, 2011 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2009, 2011, 2015 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -65,10 +65,269 @@ * Private Functions ************************************************************************************/ +/************************************************************************************ + * Name: uart_dorxflowcontrol + * + * Description: + * Handle RX flow control using watermark levels or not + * + ************************************************************************************/ + +#ifdef CONFIG_SERIAL_IFLOWCONTROL +#ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS +static inline bool uart_dorxflowcontrol(FAR uart_dev_t *dev, + FAR struct uart_buffer_s *rxbuf, + unsigned int watermark) +{ + unsigned int nbuffered; + + /* How many bytes are buffered */ + + if (rxbuf->head >= rxbuf->tail) + { + nbuffered = rxbuf->head - rxbuf->tail; + } + else + { + nbuffered = rxbuf->size - rxbuf->tail + rxbuf->head; + } + + /* Is the level now above the watermark level that we need to report? */ + + if (nbuffered >= watermark) + { + /* Let the lower level driver know that the watermark level has been + * crossed. It will probably activate RX flow control. + */ + + if (uart_rxflowcontrol(dev, nbuffered, true)) + { + /* Low-level driver activated RX flow control, exit loop now. */ + + return true; + } + } + + return false; +} +#else +static inline bool uart_dorxflowcontrol(FAR uart_dev_t *dev, + FAR struct uart_buffer_s *rxbuf, + bool is_full) +{ + /* Check if RX buffer is full and allow serial low-level driver to pause + * processing. This allows proper utilization of hardware flow control. + */ + + if (is_full) + { + if (uart_rxflowcontrol(dev, rxbuf->size, true)) + { + /* Low-level driver activated RX flow control, exit loop now. */ + + return true; + } + } + + return false; +} +#endif +#endif + /************************************************************************************ * Public Functions ************************************************************************************/ +/************************************************************************************ + * Name: uart_xmitchars_dma + * + * Description: + * Set up to transfer bytes from the TX circular buffer using DMA + * + ************************************************************************************/ + +#ifdef CONFIG_SERIAL_DMA +void uart_xmitchars_dma(FAR uart_dev_t *dev) +{ + FAR struct uart_dmaxfer_s *xfer = &dev->dmatx; + + if (dev->xmit.head == dev->xmit.tail) + { + /* No data to transfer. */ + + return; + } + + if (dev->xmit.tail < dev->xmit.head) + { + xfer->buffer = &dev->xmit.buffer[dev->xmit.tail]; + xfer->length = dev->xmit.head - dev->xmit.tail; + xfer->nbuffer = NULL; + xfer->nlength = 0; + } + else + { + xfer->buffer = &dev->xmit.buffer[dev->xmit.tail]; + xfer->length = dev->xmit.size - dev->xmit.tail; + xfer->nbuffer = dev->xmit.buffer; + xfer->nlength = dev->xmit.head; + } + + uart_dmasend(dev); +} +#endif /* CONFIG_SERIAL_DMA */ + +/************************************************************************************ + * Name: uart_xmitchars_done + * + * Description: + * Perform operations necessary at the complete of DMA including adjusting the + * TX circular buffer indices and waking up of any threads that may have been + * waiting for space to become available in the TX circular buffer. + * + ************************************************************************************/ + +#ifdef CONFIG_SERIAL_DMA +void uart_xmitchars_done(FAR uart_dev_t *dev) +{ + FAR struct uart_dmaxfer_s *xfer = &dev->dmatx; + size_t nbytes = xfer->nbytes; + struct uart_buffer_s *txbuf = &dev->xmit; + + /* Move tail for nbytes. */ + + txbuf->tail = (txbuf->tail + nbytes) % txbuf->size; + xfer->nbytes = 0; + xfer->length = xfer->nlength = 0; + + /* If any bytes were removed from the buffer, inform any waiters there there is + * space available. + */ + + if (nbytes) + { + uart_datasent(dev); + } +} +#endif /* CONFIG_SERIAL_DMA */ + +/************************************************************************************ + * Name: uart_recvchars_dma + * + * Description: + * Set up to receive bytes into the RX circular buffer using DMA + * + ************************************************************************************/ + +#ifdef CONFIG_SERIAL_DMA +void uart_recvchars_dma(FAR uart_dev_t *dev) +{ + FAR struct uart_dmaxfer_s *xfer = &dev->dmarx; + FAR struct uart_buffer_s *rxbuf = &dev->recv; +#ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS + unsigned int watermark; +#endif + bool is_full; + int nexthead = rxbuf->head + 1; + + if (nexthead >= rxbuf->size) + { + nexthead = 0; + } + + is_full = nexthead == rxbuf->tail; + +#ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS + /* Pre-calcuate the watermark level that we will need to test against. */ + + watermark = (CONFIG_SERIAL_IFLOWCONTROL_UPPER_WATERMARK * rxbuf->size) / 100; +#endif + +#ifdef CONFIG_SERIAL_IFLOWCONTROL +#ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS + if (uart_dorxflowcontrol(dev, rxbuf, watermark)) + { + return; + } +#else + if (uart_dorxflowcontrol(dev, rxbuf, is_full)) + { + return; + } +#endif +#endif + + if (is_full) + { + /* If there is no free space in receive buffer we cannot start DMA + * transfer. + */ + + return; + } + + if (rxbuf->tail <= rxbuf->head) + { + xfer->buffer = &rxbuf->buffer[rxbuf->head]; + xfer->nbuffer = rxbuf->buffer; + + if (rxbuf->tail > 0) + { + xfer->length = rxbuf->size - rxbuf->head; + xfer->nlength = rxbuf->tail - 1; + } + else + { + xfer->length = rxbuf->size - rxbuf->head - 1; + xfer->nlength = 0; + } + } + else + { + xfer->buffer = &rxbuf->buffer[rxbuf->head]; + xfer->length = rxbuf->tail - rxbuf->head - 1; + xfer->nbuffer = NULL; + xfer->nlength = 0; + } + + uart_dmareceive(dev); +} +#endif /* CONFIG_SERIAL_DMA */ + +/************************************************************************************ + * Name: uart_recvchars_done + * + * Description: + * Perform operations necessary at the complete of DMA including adjusting the + * RX circular buffer indices and waking up of any threads that may have been + * waiting for new data to become available in the RX circular buffer. + * + ************************************************************************************/ + +#ifdef CONFIG_SERIAL_DMA +void uart_recvchars_done(FAR uart_dev_t *dev) +{ + FAR struct uart_dmaxfer_s *xfer = &dev->dmarx; + FAR struct uart_buffer_s *rxbuf = &dev->recv; + size_t nbytes = xfer->nbytes; + + /* Move head for nbytes. */ + + rxbuf->head = (rxbuf->head + nbytes) % rxbuf->size; + xfer->nbytes = 0; + xfer->length = xfer->nlength = 0; + + /* If any bytes were added to the buffer, inform any waiters there is new + * incoming data available. + */ + + if (nbytes) + { + uart_datareceived(dev); + } +} +#endif /* CONFIG_SERIAL_DMA */ + /************************************************************************************ * Name: uart_xmitchars * @@ -145,6 +404,7 @@ void uart_recvchars(FAR uart_dev_t *dev) unsigned int status; int nexthead = rxbuf->head + 1; uint16_t nbytes = 0; + bool is_full; if (nexthead >= rxbuf->size) { @@ -163,52 +423,19 @@ void uart_recvchars(FAR uart_dev_t *dev) while (uart_rxavailable(dev)) { - bool is_full = (nexthead == rxbuf->tail); + is_full = (nexthead == rxbuf->tail); char ch; #ifdef CONFIG_SERIAL_IFLOWCONTROL #ifdef CONFIG_SERIAL_IFLOWCONTROL_WATERMARKS - unsigned int nbuffered; - - /* How many bytes are buffered */ - - if (rxbuf->head >= rxbuf->tail) + if (uart_dorxflowcontrol(dev, rxbuf, watermark)) { - nbuffered = rxbuf->head - rxbuf->tail; - } - else - { - nbuffered = rxbuf->size - rxbuf->tail + rxbuf->head; - } - - /* Is the level now above the watermark level that we need to report? */ - - if (nbuffered >= watermark) - { - /* Let the lower level driver know that the watermark level has been - * crossed. It will probably activate RX flow control. - */ - - if (uart_rxflowcontrol(dev, nbuffered, true)) - { - /* Low-level driver activated RX flow control, exit loop now. */ - - break; - } + break; } #else - /* Check if RX buffer is full and allow serial low-level driver to pause - * processing. This allows proper utilization of hardware flow control. - */ - - if (is_full) + if (uart_dorxflowcontrol(dev, rxbuf, is_full)) { - if (uart_rxflowcontrol(dev, rxbuf->size, true)) - { - /* Low-level driver activated RX flow control, exit loop now. */ - - break; - } + break; } #endif #endif @@ -240,7 +467,7 @@ void uart_recvchars(FAR uart_dev_t *dev) } } - /* If any bytes were added to the buffer, inform any waiters there there is new + /* If any bytes were added to the buffer, inform any waiters there is new * incoming data available. */ diff --git a/include/nuttx/serial/serial.h b/include/nuttx/serial/serial.h index 710345511f2..493c0d53bad 100644 --- a/include/nuttx/serial/serial.h +++ b/include/nuttx/serial/serial.h @@ -1,7 +1,7 @@ /************************************************************************************ * include/nuttx/serial/serial.h * - * Copyright (C) 2007-2008, 2012-2014 Gregory Nutt. All rights reserved. + * Copyright (C) 2007-2008, 2012-2015 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -104,9 +104,16 @@ #define uart_send(dev,ch) dev->ops->send(dev,ch) #define uart_receive(dev,s) dev->ops->receive(dev,s) +#ifdef CONFIG_SERIAL_DMA +# define uart_dmasend(dev) dev->ops->dmasend(dev) +# define uart_dmareceive(dev) dev->ops->dmareceive(dev) +# define uart_dmarxfree(dev) dev->ops->dmarxfree(dev) +# define uart_dmatxavail(dev) dev->ops->dmatxavail(dev) +#endif + #ifdef CONFIG_SERIAL_IFLOWCONTROL -#define uart_rxflowcontrol(dev,n,u) \ - (dev->ops->rxflowcontrol && dev->ops->rxflowcontrol(dev,n,u)) +# define uart_rxflowcontrol(dev,n,u) \ + (dev->ops->rxflowcontrol && dev->ops->rxflowcontrol(dev,n,u)) #endif /************************************************************************************ @@ -127,6 +134,17 @@ struct uart_buffer_s FAR char *buffer; /* Pointer to the allocated buffer memory */ }; +#ifdef CONFIG_SERIAL_DMA +struct uart_dmaxfer_s +{ + FAR char *buffer; /* First DMA buffer */ + FAR char *nbuffer; /* Next DMA buffer */ + size_t length; /* Length of first DMA buffer */ + size_t nlength; /* Length of next DMA buffer */ + size_t nbytes; /* Bytes actually transferred by DMA from both buffers */ +}; +#endif /* CONFIG_SERIAL_DMA */ + /* This structure defines all of the operations providd by the architecture specific * logic. All fields must be provided with non-NULL function pointers by the * caller of uart_register(). @@ -199,6 +217,24 @@ struct uart_ops_s unsigned int nbuffered, bool upper); #endif +#ifdef CONFIG_SERIAL_DMA + /* Start transfer bytes from the TX circular buffer using DMA */ + + CODE void (*dmasend)(FAR struct uart_dev_s *dev); + + /* Start transfer bytes from the TX circular buffer using DMA */ + + CODE void (*dmareceive)(FAR struct uart_dev_s *dev); + + /* Notify DMA that there is free space in the RX buffer */ + + CODE void (*dmarxfree)(FAR struct uart_dev_s *dev); + + /* Notify DMA that there is data to be transferred in the TX buffer */ + + CODE void (*dmatxavail)(FAR struct uart_dev_s *dev); +#endif + /* This method will send one byte on the UART */ CODE void (*send)(FAR struct uart_dev_s *dev, int ch); @@ -266,6 +302,14 @@ struct uart_dev_s struct uart_buffer_s xmit; /* Describes transmit buffer */ struct uart_buffer_s recv; /* Describes receive buffer */ +#ifdef CONFIG_SERIAL_DMA + + /* DMA transfers */ + + struct uart_dmaxfer_s dmatx; /* Describes transmit DMA transfer */ + struct uart_dmaxfer_s dmarx; /* Describes receive DMA transfer */ +#endif + /* Driver interface */ FAR const struct uart_ops_s *ops; /* Arch-specific operations */ @@ -385,6 +429,58 @@ void uart_datasent(FAR uart_dev_t *dev); void uart_connected(FAR uart_dev_t *dev, bool connected); #endif +/************************************************************************************ + * Name: uart_xmitchars_dma + * + * Description: + * Set up to transfer bytes from the TX circular buffer using DMA + * + ************************************************************************************/ + +#ifdef CONFIG_SERIAL_DMA +void uart_xmitchars_dma(FAR uart_dev_t *dev); +#endif + +/************************************************************************************ + * Name: uart_xmitchars_done + * + * Description: + * Perform operations necessary at the complete of DMA including adjusting the + * TX circular buffer indices and waking up of any threads that may have been + * waiting for space to become available in the TX circular buffer. + * + ************************************************************************************/ + +#ifdef CONFIG_SERIAL_DMA +void uart_xmitchars_done(FAR uart_dev_t *dev); +#endif + +/************************************************************************************ + * Name: uart_recvchars_dma + * + * Description: + * Set up to receive bytes into the RX circular buffer using DMA + * + ************************************************************************************/ + +#ifdef CONFIG_SERIAL_DMA +void uart_recvchars_dma(FAR uart_dev_t *dev); +#endif + +/************************************************************************************ + * Name: uart_recvchars_done + * + * Description: + * Perform operations necessary at the complete of DMA including adjusting the + * RX circular buffer indices and waking up of any threads that may have been + * waiting for new data to become available in the RX circular buffer. + * + ************************************************************************************/ + +#ifdef CONFIG_SERIAL_DMA +void uart_recvchars_done(FAR uart_dev_t *dev); +#endif + #undef EXTERN #if defined(__cplusplus) }