summaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial/sirfsoc_uart.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/sirfsoc_uart.c')
-rw-r--r--drivers/tty/serial/sirfsoc_uart.c126
1 files changed, 78 insertions, 48 deletions
diff --git a/drivers/tty/serial/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c
index 653cdd5fb508..c6657de78997 100644
--- a/drivers/tty/serial/sirfsoc_uart.c
+++ b/drivers/tty/serial/sirfsoc_uart.c
@@ -413,7 +413,6 @@ sirfsoc_uart_pio_rx_chars(struct uart_port *port, unsigned int max_rx_count)
break;
}
- sirfport->rx_io_count += rx_count;
port->icount.rx += rx_count;
return rx_count;
@@ -600,7 +599,6 @@ static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port)
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
- sirfport->rx_io_count = 0;
wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl,
rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) &
~SIRFUART_IO_MODE);
@@ -632,31 +630,6 @@ static void sirfsoc_uart_start_next_rx_dma(struct uart_port *port)
sirfport->uart_reg->uart_type));
}
-static void sirfsoc_uart_start_rx(struct uart_port *port)
-{
- struct sirfsoc_uart_port *sirfport = to_sirfport(port);
- struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
- struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
-
- sirfport->rx_io_count = 0;
- wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET);
- wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0);
- wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_START);
- if (sirfport->rx_dma_chan)
- sirfsoc_uart_start_next_rx_dma(port);
- else {
- if (!sirfport->is_atlas7)
- wr_regl(port, ureg->sirfsoc_int_en_reg,
- rd_regl(port, ureg->sirfsoc_int_en_reg) |
- SIRFUART_RX_IO_INT_EN(uint_en,
- sirfport->uart_reg->uart_type));
- else
- wr_regl(port, ureg->sirfsoc_int_en_reg,
- SIRFUART_RX_IO_INT_EN(uint_en,
- sirfport->uart_reg->uart_type));
- }
-}
-
static unsigned int
sirfsoc_usp_calc_sample_div(unsigned long set_rate,
unsigned long ioclk_rate, unsigned long *sample_reg)
@@ -850,7 +823,6 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
rx_time_out = SIRFSOC_UART_RX_TIMEOUT(set_baud, 20000);
rx_time_out = SIRFUART_RECV_TIMEOUT_VALUE(rx_time_out);
txfifo_op_reg = rd_regl(port, ureg->sirfsoc_tx_fifo_op);
- wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_STOP);
wr_regl(port, ureg->sirfsoc_tx_fifo_op,
(txfifo_op_reg & ~SIRFUART_FIFO_START));
if (sirfport->uart_reg->uart_type == SIRF_REAL_UART) {
@@ -886,9 +858,13 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
else
wr_regl(port, ureg->sirfsoc_tx_dma_io_ctrl, SIRFUART_IO_MODE);
if (sirfport->rx_dma_chan)
- wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, SIRFUART_DMA_MODE);
+ wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl,
+ rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) &
+ ~SIRFUART_IO_MODE);
else
- wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl, SIRFUART_IO_MODE);
+ wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl,
+ rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) |
+ SIRFUART_IO_MODE);
sirfport->rx_period_time = 20000000;
/* Reset Rx/Tx FIFO Threshold level for proper baudrate */
if (set_baud < 1000000)
@@ -902,7 +878,6 @@ static void sirfsoc_uart_set_termios(struct uart_port *port,
txfifo_op_reg |= SIRFUART_FIFO_START;
wr_regl(port, ureg->sirfsoc_tx_fifo_op, txfifo_op_reg);
uart_update_timeout(port, termios->c_cflag, set_baud);
- sirfsoc_uart_start_rx(port);
wr_regl(port, ureg->sirfsoc_tx_rx_en, SIRFUART_TX_EN | SIRFUART_RX_EN);
spin_unlock_irqrestore(&port->lock, flags);
}
@@ -921,6 +896,7 @@ static int sirfsoc_uart_startup(struct uart_port *port)
{
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct sirfsoc_int_en *uint_en = &sirfport->uart_reg->uart_int_en;
unsigned int index = port->line;
int ret;
irq_modify_status(port->irq, IRQ_NOREQUEST, IRQ_NOAUTOEN);
@@ -958,9 +934,9 @@ static int sirfsoc_uart_startup(struct uart_port *port)
wr_regl(port, ureg->sirfsoc_rx_fifo_ctrl, SIRFUART_FIFO_THD(port));
if (sirfport->rx_dma_chan)
wr_regl(port, ureg->sirfsoc_rx_fifo_level_chk,
- SIRFUART_RX_FIFO_CHK_SC(port->line, 0x4) |
- SIRFUART_RX_FIFO_CHK_LC(port->line, 0xe) |
- SIRFUART_RX_FIFO_CHK_HC(port->line, 0x1b));
+ SIRFUART_RX_FIFO_CHK_SC(port->line, 0x1) |
+ SIRFUART_RX_FIFO_CHK_LC(port->line, 0x2) |
+ SIRFUART_RX_FIFO_CHK_HC(port->line, 0x4));
if (sirfport->tx_dma_chan) {
sirfport->tx_dma_state = TX_DMA_IDLE;
wr_regl(port, ureg->sirfsoc_tx_fifo_level_chk,
@@ -981,16 +957,41 @@ static int sirfsoc_uart_startup(struct uart_port *port)
goto init_rx_err;
}
}
- enable_irq(port->irq);
+ if (sirfport->uart_reg->uart_type == SIRF_REAL_UART &&
+ sirfport->rx_dma_chan)
+ wr_regl(port, ureg->sirfsoc_swh_dma_io,
+ SIRFUART_CLEAR_RX_ADDR_EN);
+ if (sirfport->uart_reg->uart_type == SIRF_USP_UART &&
+ sirfport->rx_dma_chan)
+ wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl,
+ rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) |
+ SIRFSOC_USP_FRADDR_CLR_EN);
if (sirfport->rx_dma_chan && !sirfport->is_hrt_enabled) {
sirfport->is_hrt_enabled = true;
sirfport->rx_period_time = 20000000;
+ sirfport->rx_last_pos = -1;
+ sirfport->pio_fetch_cnt = 0;
sirfport->rx_dma_items.xmit.tail =
sirfport->rx_dma_items.xmit.head = 0;
hrtimer_start(&sirfport->hrt,
ns_to_ktime(sirfport->rx_period_time),
HRTIMER_MODE_REL);
}
+ wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_START);
+ if (sirfport->rx_dma_chan)
+ sirfsoc_uart_start_next_rx_dma(port);
+ else {
+ if (!sirfport->is_atlas7)
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ rd_regl(port, ureg->sirfsoc_int_en_reg) |
+ SIRFUART_RX_IO_INT_EN(uint_en,
+ sirfport->uart_reg->uart_type));
+ else
+ wr_regl(port, ureg->sirfsoc_int_en_reg,
+ SIRFUART_RX_IO_INT_EN(uint_en,
+ sirfport->uart_reg->uart_type));
+ }
+ enable_irq(port->irq);
return 0;
init_rx_err:
@@ -1003,6 +1004,9 @@ static void sirfsoc_uart_shutdown(struct uart_port *port)
{
struct sirfsoc_uart_port *sirfport = to_sirfport(port);
struct sirfsoc_register *ureg = &sirfport->uart_reg->uart_reg;
+ struct circ_buf *xmit;
+
+ xmit = &sirfport->rx_dma_items.xmit;
if (!sirfport->is_atlas7)
wr_regl(port, ureg->sirfsoc_int_en_reg, 0);
else
@@ -1019,8 +1023,10 @@ static void sirfsoc_uart_shutdown(struct uart_port *port)
if (sirfport->tx_dma_chan)
sirfport->tx_dma_state = TX_DMA_IDLE;
if (sirfport->rx_dma_chan && sirfport->is_hrt_enabled) {
- while ((rd_regl(port, ureg->sirfsoc_rx_fifo_status) &
- SIRFUART_RX_FIFO_MASK) > 0)
+ while (((rd_regl(port, ureg->sirfsoc_rx_fifo_status) &
+ SIRFUART_RX_FIFO_MASK) > sirfport->pio_fetch_cnt) &&
+ !CIRC_CNT(xmit->head, xmit->tail,
+ SIRFSOC_RX_DMA_BUF_SIZE))
;
sirfport->is_hrt_enabled = false;
hrtimer_cancel(&sirfport->hrt);
@@ -1169,6 +1175,8 @@ static enum hrtimer_restart
struct tty_struct *tty;
struct sirfsoc_register *ureg;
struct circ_buf *xmit;
+ struct sirfsoc_fifo_status *ufifo_st;
+ int max_pio_cnt;
sirfport = container_of(hrt, struct sirfsoc_uart_port, hrt);
port = &sirfport->port;
@@ -1176,9 +1184,16 @@ static enum hrtimer_restart
tty = port->state->port.tty;
ureg = &sirfport->uart_reg->uart_reg;
xmit = &sirfport->rx_dma_items.xmit;
+ ufifo_st = &sirfport->uart_reg->fifo_status;
+
dmaengine_tx_status(sirfport->rx_dma_chan,
- sirfport->rx_dma_items.cookie, &tx_state);
- xmit->head = SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue;
+ sirfport->rx_dma_items.cookie, &tx_state);
+ if (SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue !=
+ sirfport->rx_last_pos) {
+ xmit->head = SIRFSOC_RX_DMA_BUF_SIZE - tx_state.residue;
+ sirfport->rx_last_pos = xmit->head;
+ sirfport->pio_fetch_cnt = 0;
+ }
count = CIRC_CNT_TO_END(xmit->head, xmit->tail,
SIRFSOC_RX_DMA_BUF_SIZE);
while (count > 0) {
@@ -1200,23 +1215,38 @@ static enum hrtimer_restart
*/
if (!inserted && !count &&
((rd_regl(port, ureg->sirfsoc_rx_fifo_status) &
- SIRFUART_RX_FIFO_MASK) > 0)) {
+ SIRFUART_RX_FIFO_MASK) > sirfport->pio_fetch_cnt)) {
+ dmaengine_pause(sirfport->rx_dma_chan);
/* switch to pio mode */
wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl,
rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) |
SIRFUART_IO_MODE);
- while ((rd_regl(port, ureg->sirfsoc_rx_fifo_status) &
- SIRFUART_RX_FIFO_MASK) > 0) {
- if (sirfsoc_uart_pio_rx_chars(port, 16) > 0)
- tty_flip_buffer_push(tty->port);
+ /*
+ * UART controller SWH_DMA_IO register have CLEAR_RX_ADDR_EN
+ * When found changing I/O to DMA mode, it clears
+ * two low bits of read point;
+ * USP have similar FRADDR_CLR_EN bit in USP_RX_DMA_IO_CTRL.
+ * Fetch data out from rxfifo into DMA buffer in PIO mode,
+ * while switch back to DMA mode, the data fetched will override
+ * by DMA, as hardware have a strange behaviour:
+ * after switch back to DMA mode, check rxfifo status it will
+ * be the number PIO fetched, so record the fetched data count
+ * to avoid the repeated fetch
+ */
+ max_pio_cnt = 3;
+ while (!(rd_regl(port, ureg->sirfsoc_rx_fifo_status) &
+ ufifo_st->ff_empty(port)) && max_pio_cnt--) {
+ xmit->buf[xmit->head] =
+ rd_regl(port, ureg->sirfsoc_rx_fifo_data);
+ xmit->head = (xmit->head + 1) &
+ (SIRFSOC_RX_DMA_BUF_SIZE - 1);
+ sirfport->pio_fetch_cnt++;
}
- wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_RESET);
- wr_regl(port, ureg->sirfsoc_rx_fifo_op, 0);
- wr_regl(port, ureg->sirfsoc_rx_fifo_op, SIRFUART_FIFO_START);
/* switch back to dma mode */
wr_regl(port, ureg->sirfsoc_rx_dma_io_ctrl,
rd_regl(port, ureg->sirfsoc_rx_dma_io_ctrl) &
~SIRFUART_IO_MODE);
+ dmaengine_resume(sirfport->rx_dma_chan);
}
next_hrt:
hrtimer_forward_now(hrt, ns_to_ktime(sirfport->rx_period_time));
@@ -1239,7 +1269,7 @@ static int sirfsoc_uart_probe(struct platform_device *pdev)
struct resource *res;
int ret;
struct dma_slave_config slv_cfg = {
- .src_maxburst = 2,
+ .src_maxburst = 1,
};
struct dma_slave_config tx_slv_cfg = {
.dst_maxburst = 2,