diff options
author | Cyrille Pitchen <cyrille.pitchen@atmel.com> | 2014-10-20 13:12:20 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-11-06 17:57:18 -0500 |
commit | 66f37aafd6a6177287334397c59d7a727d16cd24 (patch) | |
tree | 71e9e7240611ad2908229e3518202bb7bb184fe1 /drivers | |
parent | a7ae7f8243b15cd65b5811de031eb4f8413b3e6f (diff) |
tty/serial: at91: fix rx ring buffer management
This patch swaps the use "tail" and "head" to fit the semantic of the linux
circular buffer documentation:
- head: the point at which the producer (the DMA controller) inserts items.
- tail: the point at which the consumer (the serial framework) finds the next
item.
Besides the former code of the rx ring buffer didn't manage the case where
head < tail, which might lead to loss of data. To fix this bug the data are now
sent from the DMA buffer to the serial framework in two steps:
1 - First, we test if head < tail. If so, we copy the data from tail to the end
of the DMA buffer then reset tail to zero.
2 - Finally, we copy data from tail to head then set tail to head.
In addition, since tty_insert_flip_string() may now be called twice,
atmel_flip_buffer_rx_dma() becomes less efficient than moving the calls
dma_sync_sg_for_cpu(), dma_sync_sg_for_device(), tty_insert_flip_string() and
tty_flip_buffer_push() directly into atmel_rx_from_dma().
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Acked-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/tty/serial/atmel_serial.c | 94 |
1 files changed, 55 insertions, 39 deletions
diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index 8a84034d130c..9eb624a22431 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c | |||
@@ -880,32 +880,6 @@ chan_err: | |||
880 | return -EINVAL; | 880 | return -EINVAL; |
881 | } | 881 | } |
882 | 882 | ||
883 | static void atmel_flip_buffer_rx_dma(struct uart_port *port, | ||
884 | char *buf, size_t count) | ||
885 | { | ||
886 | struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); | ||
887 | struct tty_port *tport = &port->state->port; | ||
888 | |||
889 | dma_sync_sg_for_cpu(port->dev, | ||
890 | &atmel_port->sg_rx, | ||
891 | 1, | ||
892 | DMA_DEV_TO_MEM); | ||
893 | |||
894 | tty_insert_flip_string(tport, buf, count); | ||
895 | |||
896 | dma_sync_sg_for_device(port->dev, | ||
897 | &atmel_port->sg_rx, | ||
898 | 1, | ||
899 | DMA_DEV_TO_MEM); | ||
900 | /* | ||
901 | * Drop the lock here since it might end up calling | ||
902 | * uart_start(), which takes the lock. | ||
903 | */ | ||
904 | spin_unlock(&port->lock); | ||
905 | tty_flip_buffer_push(tport); | ||
906 | spin_lock(&port->lock); | ||
907 | } | ||
908 | |||
909 | static void atmel_complete_rx_dma(void *arg) | 883 | static void atmel_complete_rx_dma(void *arg) |
910 | { | 884 | { |
911 | struct uart_port *port = arg; | 885 | struct uart_port *port = arg; |
@@ -934,11 +908,12 @@ static void atmel_release_rx_dma(struct uart_port *port) | |||
934 | static void atmel_rx_from_dma(struct uart_port *port) | 908 | static void atmel_rx_from_dma(struct uart_port *port) |
935 | { | 909 | { |
936 | struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); | 910 | struct atmel_uart_port *atmel_port = to_atmel_uart_port(port); |
911 | struct tty_port *tport = &port->state->port; | ||
937 | struct circ_buf *ring = &atmel_port->rx_ring; | 912 | struct circ_buf *ring = &atmel_port->rx_ring; |
938 | struct dma_chan *chan = atmel_port->chan_rx; | 913 | struct dma_chan *chan = atmel_port->chan_rx; |
939 | struct dma_tx_state state; | 914 | struct dma_tx_state state; |
940 | enum dma_status dmastat; | 915 | enum dma_status dmastat; |
941 | size_t pending, count; | 916 | size_t count; |
942 | 917 | ||
943 | 918 | ||
944 | /* Reset the UART timeout early so that we don't miss one */ | 919 | /* Reset the UART timeout early so that we don't miss one */ |
@@ -953,27 +928,68 @@ static void atmel_rx_from_dma(struct uart_port *port) | |||
953 | tasklet_schedule(&atmel_port->tasklet); | 928 | tasklet_schedule(&atmel_port->tasklet); |
954 | return; | 929 | return; |
955 | } | 930 | } |
956 | /* current transfer size should no larger than dma buffer */ | ||
957 | pending = sg_dma_len(&atmel_port->sg_rx) - state.residue; | ||
958 | BUG_ON(pending > sg_dma_len(&atmel_port->sg_rx)); | ||
959 | 931 | ||
932 | /* CPU claims ownership of RX DMA buffer */ | ||
933 | dma_sync_sg_for_cpu(port->dev, | ||
934 | &atmel_port->sg_rx, | ||
935 | 1, | ||
936 | DMA_DEV_TO_MEM); | ||
937 | |||
938 | /* | ||
939 | * ring->head points to the end of data already written by the DMA. | ||
940 | * ring->tail points to the beginning of data to be read by the | ||
941 | * framework. | ||
942 | * The current transfer size should not be larger than the dma buffer | ||
943 | * length. | ||
944 | */ | ||
945 | ring->head = sg_dma_len(&atmel_port->sg_rx) - state.residue; | ||
946 | BUG_ON(ring->head > sg_dma_len(&atmel_port->sg_rx)); | ||
960 | /* | 947 | /* |
961 | * This will take the chars we have so far, | 948 | * At this point ring->head may point to the first byte right after the |
962 | * ring->head will record the transfer size, only new bytes come | 949 | * last byte of the dma buffer: |
963 | * will insert into the framework. | 950 | * 0 <= ring->head <= sg_dma_len(&atmel_port->sg_rx) |
951 | * | ||
952 | * However ring->tail must always points inside the dma buffer: | ||
953 | * 0 <= ring->tail <= sg_dma_len(&atmel_port->sg_rx) - 1 | ||
954 | * | ||
955 | * Since we use a ring buffer, we have to handle the case | ||
956 | * where head is lower than tail. In such a case, we first read from | ||
957 | * tail to the end of the buffer then reset tail. | ||
964 | */ | 958 | */ |
965 | if (pending > ring->head) { | 959 | if (ring->head < ring->tail) { |
966 | count = pending - ring->head; | 960 | count = sg_dma_len(&atmel_port->sg_rx) - ring->tail; |
967 | 961 | ||
968 | atmel_flip_buffer_rx_dma(port, ring->buf + ring->head, count); | 962 | tty_insert_flip_string(tport, ring->buf + ring->tail, count); |
963 | ring->tail = 0; | ||
964 | port->icount.rx += count; | ||
965 | } | ||
969 | 966 | ||
970 | ring->head += count; | 967 | /* Finally we read data from tail to head */ |
971 | if (ring->head == sg_dma_len(&atmel_port->sg_rx)) | 968 | if (ring->tail < ring->head) { |
972 | ring->head = 0; | 969 | count = ring->head - ring->tail; |
973 | 970 | ||
971 | tty_insert_flip_string(tport, ring->buf + ring->tail, count); | ||
972 | /* Wrap ring->head if needed */ | ||
973 | if (ring->head >= sg_dma_len(&atmel_port->sg_rx)) | ||
974 | ring->head = 0; | ||
975 | ring->tail = ring->head; | ||
974 | port->icount.rx += count; | 976 | port->icount.rx += count; |
975 | } | 977 | } |
976 | 978 | ||
979 | /* USART retreives ownership of RX DMA buffer */ | ||
980 | dma_sync_sg_for_device(port->dev, | ||
981 | &atmel_port->sg_rx, | ||
982 | 1, | ||
983 | DMA_DEV_TO_MEM); | ||
984 | |||
985 | /* | ||
986 | * Drop the lock here since it might end up calling | ||
987 | * uart_start(), which takes the lock. | ||
988 | */ | ||
989 | spin_unlock(&port->lock); | ||
990 | tty_flip_buffer_push(tport); | ||
991 | spin_lock(&port->lock); | ||
992 | |||
977 | UART_PUT_IER(port, ATMEL_US_TIMEOUT); | 993 | UART_PUT_IER(port, ATMEL_US_TIMEOUT); |
978 | } | 994 | } |
979 | 995 | ||