diff options
author | Laxman Dewangan <ldewangan@nvidia.com> | 2010-04-09 22:24:25 +0530 |
---|---|---|
committer | Gary King <gking@nvidia.com> | 2010-04-13 15:57:22 -0700 |
commit | c31c451d72bc2c6e3973adf4d3356a872cc77539 (patch) | |
tree | 690fb78a2e02b0d35f2a823ee31de2994930385c | |
parent | e1a99bd90a73cd427aed02bb9bbee49c7cc09c8a (diff) |
tegra uart: Fixing the tx and rx dma path issue
Following issue has been fixed:
- Blocking write was returning immediatly if data request is multiple of 4.
- Blocking write was not able to complete if data length is nonmultiple of 4
and more than 4.
- The close was taking too much time because proper timeout and fifo size was
not configured.
- Tx dma path optimized to fill more data to dma buffer if there is more
pending chars in the buffer.
- Tx path is fixed to properly signal the wakup event to tty layer.
- RTS flow control is not getting set from second open even cflag is
requested for that.
- Rx dma was not receiving the correct data after second open. The multiple
request was
getting queued for the receive path at the time of closing.
- Rx dma was started before uart controller is configured and it is creating
to misbehave the dma.
- Transfer count was not getting calculated in the dma driver.
Pending issue:
- Loosing the data id more than 32K of data was sennt is single shot. Debugging
this.
Tested on harmony with different testcase developed for testing the linux serial
driver.
Change-Id: I6ed9095dd6340d2b5e7ef036823d2e4e5a61abcc
Reviewed-on: http://git-master/r/1065
Tested-by: Suresh Mangipudi <smangipudi@nvidia.com>
Reviewed-by: Udaykumar Rameshchan Raval <uraval@nvidia.com>
Tested-by: Udaykumar Rameshchan Raval <uraval@nvidia.com>
Reviewed-by: Gary King <gking@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/dma.c | 13 | ||||
-rw-r--r-- | drivers/serial/tegra_hsuart.c | 290 |
2 files changed, 143 insertions, 160 deletions
diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c index aae36b30664d..4ad9e19ff2d3 100644 --- a/arch/arm/mach-tegra/dma.c +++ b/arch/arm/mach-tegra/dma.c @@ -179,7 +179,16 @@ int tegra_dma_dequeue_req(int channel, struct tegra_dma_req *_req) req_transfer_count = NV_DRF_VAL(APBDMACHAN_CHANNEL_0, CSR, WCOUNT, ch->csr); - req->bytes_transferred = req_transfer_count - to_transfer; + if (status & NV_DRF_DEF(APBDMACHAN_CHANNEL_0, STA, BSY, ACTIVE)) { + if (to_transfer) + req->bytes_transferred = (req_transfer_count - + to_transfer); + else + req->bytes_transferred = (req_transfer_count); + } else { + req->bytes_transferred = (req_transfer_count + 1); + } + req->bytes_transferred *= 4; /* In continous transfer mode, DMA only tracks the count of the * half DMA buffer. So, if the DMA already finished half the DMA @@ -191,7 +200,7 @@ int tegra_dma_dequeue_req(int channel, struct tegra_dma_req *_req) */ if (ch->mode & TEGRA_DMA_MODE_CONTINOUS) if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL) { - req->bytes_transferred += 4 * req_transfer_count; + req->bytes_transferred += 4 * (req_transfer_count +1); } tegra_dma_stop(ch); diff --git a/drivers/serial/tegra_hsuart.c b/drivers/serial/tegra_hsuart.c index 8b69027ee6ed..667bb65f1eee 100644 --- a/drivers/serial/tegra_hsuart.c +++ b/drivers/serial/tegra_hsuart.c @@ -30,7 +30,6 @@ #include <linux/workqueue.h> #include <asm/io.h> #include <linux/dma-mapping.h> -#include <linux/dmapool.h> #include <linux/termios.h> #include <linux/irq.h> #include <linux/delay.h> @@ -49,13 +48,12 @@ #include "nvrm_power.h" +#define function_trace(i) printk(KERN_ERR "%s()%s \n",__func__, (i)?"++":"--"); #define TX_EMPTY_STATUS (NV_DRF_DEF(UART, LSR, TMTY, EMPTY) | \ NV_DRF_DEF(UART, LSR, THRE, EMPTY)) #define UART_RX_DMA_BUFFER_SIZE (4 * 1024 * 16) -#define UART_TX_DMA_BUFFERS_TOTAL 2 - static int tx_force_pio = 0; static int rx_force_pio = 0; @@ -86,11 +84,10 @@ struct tegra_uart_port { int tx_low_watermark; int rx_high_watermark; - /* Tx DMA Buffer info */ - struct dma_pool *tx_dma_pool; - void *tx_dma_virt[UART_TX_DMA_BUFFERS_TOTAL]; - dma_addr_t tx_dma_phys[UART_TX_DMA_BUFFERS_TOTAL]; - int tx_dma_size[UART_TX_DMA_BUFFERS_TOTAL]; + /* Tx DMA info */ + void *tx_dma_virt; + dma_addr_t tx_dma_phys; + int tx_dma_size; /* Rm DMA handles */ int tx_dma; @@ -99,8 +96,7 @@ struct tegra_uart_port { struct tegra_dma_req rx_dma_req; int rx_dma; - unsigned int tx_dma_id; - struct tegra_dma_req tx_dma_req[UART_TX_DMA_BUFFERS_TOTAL]; + struct tegra_dma_req tx_dma_req; /* Rx PIO buffers */ unsigned char *rx_pio_buffer; @@ -155,6 +151,10 @@ void tegra_rx_dma_threshold_callback(struct tegra_dma_req *req, int err) if (t->rts_active) set_rts(t, false); tegra_dma_dequeue(t->rx_dma); + + /* enqueue the request again */ + tegra_dma_enqueue_req(t->rx_dma, req); + if (t->rts_active) set_rts(t, true); @@ -197,8 +197,6 @@ void tegra_rx_dma_complete_callback(struct tegra_dma_req *req, int err) tty_flip_buffer_push(u->info->port.tty); spin_lock(&u->lock); - /* Enqueue the request again */ - tegra_dma_enqueue_req(t->rx_dma, req); } /* Lock already taken */ @@ -210,6 +208,10 @@ static void do_handle_rx_dma(struct uart_port *u) if (t->rts_active) set_rts(t, false); tegra_dma_dequeue(t->rx_dma); + + /* enqueue the request again */ + tegra_dma_enqueue_req(t->rx_dma, &t->rx_dma_req); + if (t->rts_active) set_rts(t, true); } @@ -283,9 +285,10 @@ static void tegra_tx_dma_workqueue(struct work_struct *w) struct uart_port *u; struct tegra_uart_port *t; struct circ_buf *xmit; - unsigned int to_send; + int to_end; unsigned long flags; - unsigned int i; + int pending_chars; + int fifo_timeout; t = container_of(w, struct tegra_uart_port, work); u = &t->uport; @@ -295,104 +298,74 @@ static void tegra_tx_dma_workqueue(struct work_struct *w) t = container_of(u, struct tegra_uart_port, uport); - /* Check next DMA if it is already queued, just return */ - if (tegra_dma_is_req_inflight(t->tx_dma, - &t->tx_dma_req[(t->tx_dma_id+1) % UART_TX_DMA_BUFFERS_TOTAL])) { + /* DMA request is already queued, just return */ + if (tegra_dma_is_req_inflight(t->tx_dma, &t->tx_dma_req)) { + spin_unlock_irqrestore(&u->lock, flags); + return; + } + + /* PIO is in flight. Just return */ + if (t->tx_pio_inflight == true) { spin_unlock_irqrestore(&u->lock, flags); return; } /* Update the DMA tail pointer */ - xmit->tail += t->tx_dma_req[t->tx_dma_id].size; + xmit->tail += t->tx_dma_req.size; xmit->tail &= UART_XMIT_SIZE - 1; - u->icount.tx += t->tx_dma_req[t->tx_dma_id].size; - t->tx_dma_req[t->tx_dma_id].size = 0; + u->icount.tx += t->tx_dma_req.size; + t->tx_dma_req.size = 0; if (uart_circ_empty(xmit)) { spin_unlock_irqrestore(&u->lock, flags); + uart_write_wakeup(u); return; } - to_send = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); - - /* if transfer size is lees than a Limit use polling method - * to send the data to FIFO otherwise use DMA/interrupt Mode. - */ - - if(to_send < t->tx_low_watermark) { - unsigned int timeout; - /* before writing to FIFO make sure all prev. DMAs finished */ - i = t->tx_dma_id; - while(tegra_dma_is_req_inflight(t->tx_dma, &t->tx_dma_req[i])){ - spin_unlock_irqrestore(&u->lock, flags); - msleep(1); /* sleep for some micro seconds*/ - spin_lock_irqsave(&u->lock, flags); - } - - /*Before writing to FIFO, Make sure FIFO is empty*/ - timeout = 10; - do { - unsigned char lsr; - lsr = readb(t->regs + UART_LSR_0); - if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS) - break; - spin_unlock_irqrestore(&u->lock, flags); - timeout--; - if(timeout == 0){ - spin_unlock_irqrestore(&u->lock, flags); - BUG(); - return; - } - msleep(1); /* sleep for some micro seconds*/ - spin_lock_irqsave(&u->lock, flags); - } while (timeout); - - /* because we waited for FIFO to be empty and tx_low_watermark - will always be less than FIFO depth, never overwrites FIFO*/ - while (to_send) { - writeb(xmit->buf[xmit->tail], - t->regs + UART_THR_DLAB_0_0); - xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); - u->icount.tx++; - to_send--; + pending_chars = uart_circ_chars_pending(xmit); + if (pending_chars > u->fifosize) { + int data_size_to_dma = (pending_chars < t->tx_dma_size)? + pending_chars : t->tx_dma_size; + data_size_to_dma &= ~3; + dev_dbg(u->dev, "Tx DMA starting 0x%x size %d\n", + xmit->tail, data_size_to_dma); + + to_end = CIRC_CNT_TO_END(xmit->head, xmit->tail, + UART_XMIT_SIZE); + if (to_end < data_size_to_dma) { + char *tx_dma_virt_buff = (char *)t->tx_dma_virt; + memcpy(tx_dma_virt_buff, xmit->buf + xmit->tail, to_end); + memcpy(tx_dma_virt_buff + to_end, xmit->buf, + data_size_to_dma - to_end); + } else { + memcpy(t->tx_dma_virt, xmit->buf + xmit->tail, + data_size_to_dma); } - spin_unlock_irqrestore(&u->lock, flags); - } - else - { - to_send &= ~0x3; - if (to_send) { - // Use and Fill the next DMA buffer and queue it. - i = (t->tx_dma_id + 1) % UART_TX_DMA_BUFFERS_TOTAL; - memcpy(t->tx_dma_virt[i], - xmit->buf + xmit->tail, to_send); - wmb(); - - t->fcr_shadow = NV_FLD_SET_DRF_DEF(UART, - IIR_FCR, TX_TRIG, FIFO_COUNT_GREATER_4, - t->fcr_shadow); - writeb(t->fcr_shadow, t->regs + UART_IIR_FCR_0); - t->tx_dma_req[i].source_addr = t->tx_dma_phys[i]; - t->tx_dma_req[i].size = to_send; - t->tx_pio_inflight = false; + t->fcr_shadow = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, TX_TRIG, + FIFO_COUNT_GREATER_4, t->fcr_shadow); + writeb(t->fcr_shadow, t->regs + UART_IIR_FCR_0); - tegra_dma_enqueue_req(t->tx_dma, &t->tx_dma_req[i]); - t->tx_dma_id = i; - spin_unlock_irqrestore(&u->lock, flags); + t->tx_dma_req.source_addr = t->tx_dma_phys; + t->tx_dma_req.size = data_size_to_dma; + t->tx_pio_inflight = false; - /* Wakeup tty layer if we are below threshold. - Unlock port lock before we wakeup */ - if(uart_circ_chars_pending(xmit) > 0) - uart_write_wakeup(u); + tegra_dma_enqueue_req(t->tx_dma, &t->tx_dma_req); + spin_unlock_irqrestore(&u->lock, flags); + } else { + /* Transfer in PIO mode */ + t->fcr_shadow = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, TX_TRIG, + FIFO_COUNT_GREATER_8, t->fcr_shadow); + writeb(t->fcr_shadow, t->regs + UART_IIR_FCR_0); - } else { - spin_unlock_irqrestore(&u->lock, flags); - // We should never be here - BUG(); + if (!t->tx_pio_inflight) { + t->tx_pio_inflight = true; + t->ier_shadow = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0, + IE_THR, ENABLE, t->ier_shadow); + writeb(t->ier_shadow, t->regs + UART_IER_DLAB_0_0); } + spin_unlock_irqrestore(&u->lock, flags); } - return; } @@ -624,13 +597,32 @@ static int tegra_uart_hw_init(struct tegra_uart_port *t) t->tx_low_watermark = 8; t->rx_high_watermark = 4; - if (t->use_rx_dma) + if (t->use_rx_dma) { + /* Initialize the uart for some default configi as it is + * going to start dma */ + t->lcr_shadow = 3; /* no parity,1 stop nit, 8 data bit */ + tegra_set_baudrate(t, 9600); t->fcr_shadow = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, DMA, CHANGE, t->fcr_shadow); - else + writeb(t->fcr_shadow, t->regs + UART_IIR_FCR_0); + if (tegra_dma_enqueue_req(t->rx_dma, &t->rx_dma_req)) { + dev_err(t->uport.dev, "Could not enqueue Rx DMA req\n"); + tegra_dma_free_channel(t->rx_dma); + if (t->rx_dma_req.dest_addr) { + dma_free_coherent(t->uport.dev, t->rx_dma_req.size, + t->rx_dma_req.virt_addr, + t->rx_dma_req.dest_addr); + } + t->use_rx_dma = false; + t->fcr_shadow = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, DMA, + NO_CHANGE, t->fcr_shadow); + writeb(t->fcr_shadow, t->regs + UART_IIR_FCR_0); + } + } else { t->fcr_shadow = NV_FLD_SET_DRF_DEF(UART, IIR_FCR, DMA, NO_CHANGE, t->fcr_shadow); - writeb(t->fcr_shadow, t->regs + UART_IIR_FCR_0); + writeb(t->fcr_shadow, t->regs + UART_IIR_FCR_0); + } /* * Enable IE_RXS for the receive status interrupts like line errros. @@ -701,11 +693,6 @@ static int tegra_uart_init_rx_dma(struct tegra_uart_port *t) t->rx_dma_req.threshold = tegra_rx_dma_threshold_callback; t->rx_dma_req.data = &t->uport; INIT_LIST_HEAD(&(t->rx_dma_req.list)); - if (tegra_dma_enqueue_req(t->rx_dma, &t->rx_dma_req)) { - dev_err(t->uport.dev, "Could not enqueue Rx DMA req\n"); - goto fail; - } - return 0; fail: tegra_dma_free_channel(t->rx_dma); @@ -720,7 +707,6 @@ static int tegra_startup(struct uart_port *u) struct tegra_uart_port *t = container_of(u, struct tegra_uart_port, uport); int ret = 0; - unsigned int i; t = container_of(u, struct tegra_uart_port, uport); sprintf(t->port_name, "tegra_uart_%d", u->line); @@ -741,37 +727,28 @@ static int tegra_startup(struct uart_port *u) t->use_tx_dma = true; } if (t->use_tx_dma) { - /* alloc resources */ - t->tx_dma_pool = dma_pool_create(t->port_name, t->uport.dev, - UART_XMIT_SIZE, 64, PAGE_SIZE); - - for(i=0; i<UART_TX_DMA_BUFFERS_TOTAL; i++) { - /* setup */ - t->tx_dma_virt[i] = dma_pool_alloc(t->tx_dma_pool, - GFP_KERNEL, &t->tx_dma_phys[i]); - - if (t->tx_dma_virt[i]) { - /* Setup the DMA Tx request structure - * which doesn't change */ - INIT_LIST_HEAD(&t->tx_dma_req[i].list); - t->tx_dma_size[i] = UART_XMIT_SIZE; - t->tx_dma_req[i].modid = NvRmModuleID_Uart; - t->tx_dma_req[i].instance = u->line; - t->tx_dma_req[i].complete = - tegra_tx_dma_complete_callback; - t->tx_dma_req[i].to_memory = 0; - - t->tx_dma_req[i].dest_addr = t->phys; - t->tx_dma_req[i].dest_wrap = 4; - t->tx_dma_req[i].source_wrap = 0; - t->tx_dma_req[i].data = u; - t->tx_dma_req[i].size = 0; - } else { - tegra_dma_free_channel(t->tx_dma); - t->use_tx_dma = false; - } + t->tx_dma_virt = dma_alloc_coherent(t->uport.dev, + UART_XMIT_SIZE, &t->tx_dma_phys, GFP_KERNEL); + if (t->tx_dma_virt) { + t->tx_dma_size = UART_XMIT_SIZE; + + /* Setup the DMA Tx request structure + * which doesn't change */ + INIT_LIST_HEAD(&t->tx_dma_req.list); + t->tx_dma_req.modid = NvRmModuleID_Uart; + t->tx_dma_req.instance = u->line; + t->tx_dma_req.complete = tegra_tx_dma_complete_callback; + t->tx_dma_req.to_memory = 0; + + t->tx_dma_req.dest_addr = t->phys; + t->tx_dma_req.dest_wrap = 4; + t->tx_dma_req.source_wrap = 0; + t->tx_dma_req.data = u; + t->tx_dma_req.size = 0; + } else { + tegra_dma_free_channel(t->tx_dma); + t->use_tx_dma = false; } - t->tx_dma_id = 0; } t->use_rx_dma = false; @@ -803,7 +780,6 @@ static void tegra_shutdown(struct uart_port *u) { struct tegra_uart_port *t; unsigned long flags; - unsigned int i; spin_lock_irqsave(&u->lock, flags); t = container_of(u, struct tegra_uart_port, uport); @@ -818,11 +794,8 @@ static void tegra_shutdown(struct uart_port *u) tegra_dma_free_channel(t->rx_dma); } if (t->use_tx_dma) { - for(i=0; i< UART_TX_DMA_BUFFERS_TOTAL; i++) { - dma_pool_free(t->tx_dma_pool, t->tx_dma_virt[i], - t->tx_dma_phys[i]); - } - dma_pool_destroy(t->tx_dma_pool); + dma_free_coherent(u->dev, t->tx_dma_size, t->tx_dma_virt, + t->tx_dma_phys); tegra_dma_free_channel(t->tx_dma); } @@ -932,10 +905,10 @@ static unsigned int tegra_tx_empty(struct uart_port *u) t = container_of(u, struct tegra_uart_port, uport); dev_vdbg(u->dev, "+tegra_tx_empty\n"); - if (t->use_tx_dma && - tegra_dma_is_req_inflight(t->tx_dma, &t->tx_dma_req[t->tx_dma_id])) - return 0; - + if (t->use_tx_dma) { + if (tegra_dma_is_req_inflight(t->tx_dma, &t->tx_dma_req)) + return 0; + } lsr = readb(t->regs + UART_LSR_0); if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS) ret = TIOCSER_TEMT; @@ -948,17 +921,11 @@ static unsigned int tegra_tx_empty(struct uart_port *u) static void tegra_stop_tx(struct uart_port *u) { struct tegra_uart_port *t; - unsigned int i; - t = container_of(u, struct tegra_uart_port, uport); if (t->use_tx_dma) { - i = (t->tx_dma_id+1) % UART_TX_DMA_BUFFERS_TOTAL; - while (i!= t->tx_dma_id) { - tegra_dma_dequeue_req(t->tx_dma, &t->tx_dma_req[i]); - t->tx_dma_req[i].size = 0; - i = (i+1) % UART_TX_DMA_BUFFERS_TOTAL; - } + tegra_dma_dequeue_req(t->tx_dma, &t->tx_dma_req); + t->tx_dma_req.size = 0; } return; } @@ -1060,6 +1027,10 @@ void tegra_set_termios(struct uart_port *u, struct ktermios *termios, spin_lock_irqsave(&u->lock, flags); + /* Changing configuration, it is safe to stop any rx now */ + if (t->rts_active) + set_rts(t, false); + /* Baud rate */ baud = uart_get_baud_rate(u, termios, oldtermios, 200, 4000000); tegra_set_baudrate(t, baud); @@ -1126,6 +1097,10 @@ void tegra_set_termios(struct uart_port *u, struct ktermios *termios, t->mcr_shadow = mcr; writeb(mcr, t->regs + UART_MCR_0); t->use_cts_control = true; + + /* if top layer have asked to set rts to active then do here */ + if (t->rts_active) + set_rts(t, true); } else { mcr = t->mcr_shadow; mcr = NV_FLD_SET_DRF_DEF(UART, MCR, CTS_EN, DISABLE, mcr); @@ -1135,6 +1110,9 @@ void tegra_set_termios(struct uart_port *u, struct ktermios *termios, t->use_cts_control = false; } + /* update the port timeout based on new settings */ + uart_update_timeout(u, termios->c_cflag, baud); + spin_unlock_irqrestore(&u->lock, flags); dev_info(u->dev, "%s\n", debug_string); dev_vdbg(t->uport.dev, "-tegra_set_termios\n"); @@ -1148,19 +1126,13 @@ void tegra_set_termios(struct uart_port *u, struct ktermios *termios, static void tegra_flush_buffer(struct uart_port *u) { struct tegra_uart_port *t; - unsigned int i; - dev_vdbg(u->dev, "tegra_flush_buffer called"); t = container_of(u, struct tegra_uart_port, uport); if (t->use_tx_dma) { - i = (t->tx_dma_id + 1) % UART_TX_DMA_BUFFERS_TOTAL; - while (i != t->tx_dma_id) { - tegra_dma_dequeue_req(t->tx_dma, &t->tx_dma_req[i]); - t->tx_dma_req[i].size = 0; - i = (i + 1) % UART_TX_DMA_BUFFERS_TOTAL; - } + tegra_dma_dequeue_req(t->tx_dma, &t->tx_dma_req); + t->tx_dma_req.size = 0; } return; } @@ -1296,7 +1268,9 @@ static int __init tegra_uart_probe(struct platform_device *pdev) u->line = pdev->id; u->ops = &tegra_uart_ops; u->type = ~PORT_UNKNOWN; + u->fifosize = 32; t->modid = NVRM_MODULE_ID(NvRmModuleID_Uart, u->line); + t->rts_active = false; snprintf(clk_name, MAX_CLK_NAME_CHARS, "mod=uart,inst=%d", pdev->id); t->clk = clk_get(&pdev->dev, clk_name); |