diff options
author | Qipan Li <Qipan.Li@csr.com> | 2015-07-14 03:52:22 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-07-24 01:32:04 +0300 |
commit | 1d26c9ff420f647df4a7a3e9a28736b9cff6359a (patch) | |
tree | a5c9695dc19810e0e6857f559f5a10e0a8712a39 /drivers/tty/serial/sirfsoc_uart.h | |
parent | e9bb4b510046d41e3e5a4adc56c76ec8d6812962 (diff) | |
download | linux-1d26c9ff420f647df4a7a3e9a28736b9cff6359a.tar.xz |
serial: sirf: workaround rx process to avoid possible data loss
when UART works in DMA mode and left bytes in rx fifo less than
a dma transfer unit, DMA engine can't transfer the bytes out
to rx DMA buffer. so it need a way to fetch them out and
flush them into tty buffer in time.
in the above case, we want UART switch from DMA mode to PIO mode and
fetch && flush bytes into tty layer buffer until rxfifo become empty,
after that done let UART switch from PIO mode back to DMA mode.
(record as method1)
method1 result in the next receive result wrong. for example in PIO part
of method1, we fetched && pushed X1...X3 bytes, when UART rxfifo newly
received Y1...Y4 bytes, UART trigger a DMA unit transfer, the DMA unit's
content is X1...X3Y1 and rxfifo fifo status is empty, so X1X2X3 pushed
twice by PIO way and DMA way also the bytes Y2Y3Y4 missed. add rxfifo
reset operation before UART switch back to DMA mode would resolve the
issue. ([method1 + do fifo reset] record as method2)
before the commit, UART driver use method2. but methd2 have a risk of
data loss, as if UART's shift register receive a complete byte and
transfer it into rxfifo before rxfifo reset operation the byte will
loss.
UART and USP have the similar bits CLEAR_RX_ADDR_EN(uart)/FRADDR_CLR_EN(usp),
When found UART controller changing I/O to DMA mode, UART controller
clears the two low bits of read point (rx_fifo_addr[1:0]).
when enable the bit + method1(record as method3), in above example
the DMA unit's content is X1...X3Y1 and there are Y2Y3Y4 in rxfifo by
experiment, we just push bytes in rx DMA buffer.
BTW, the workaround works only for UART receive DMA channel use SINGLE
DMA mode.
Signed-off-by: Qipan Li <Qipan.Li@csr.com>
Signed-off-by: Barry Song <Baohua.Song@csr.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/serial/sirfsoc_uart.h')
-rw-r--r-- | drivers/tty/serial/sirfsoc_uart.h | 4 |
1 files changed, 4 insertions, 0 deletions
diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h index eb162b012eec..671a08dc5497 100644 --- a/drivers/tty/serial/sirfsoc_uart.h +++ b/drivers/tty/serial/sirfsoc_uart.h @@ -296,6 +296,7 @@ struct sirfsoc_uart_register sirfsoc_uart = { #define SIRFUART_DMA_MODE 0x0 #define SIRFUART_RX_DMA_FLUSH 0x4 +#define SIRFUART_CLEAR_RX_ADDR_EN 0x2 /* Baud Rate Calculation */ #define SIRF_USP_MIN_SAMPLE_DIV 0x1 #define SIRF_MIN_SAMPLE_DIV 0xf @@ -325,6 +326,7 @@ struct sirfsoc_uart_register sirfsoc_uart = { #define SIRFSOC_USP_ASYNC_DIV2_MASK 0x3f #define SIRFSOC_USP_ASYNC_DIV2_OFFSET 16 #define SIRFSOC_USP_LOOP_BACK_CTRL BIT(2) +#define SIRFSOC_USP_FRADDR_CLR_EN BIT(1) /* USP-UART Common */ #define SIRFSOC_UART_RX_TIMEOUT(br, to) (((br) * (((to) + 999) / 1000)) / 1000) #define SIRFUART_RECV_TIMEOUT_VALUE(x) \ @@ -431,6 +433,8 @@ struct sirfsoc_uart_port { struct hrtimer hrt; bool is_hrt_enabled; unsigned long rx_period_time; + unsigned long rx_last_pos; + unsigned long pio_fetch_cnt; }; /* Register Access Control */ |