summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaxman Dewangan <ldewangan@nvidia.com>2010-04-09 22:24:25 +0530
committerGary King <gking@nvidia.com>2010-04-13 15:57:22 -0700
commitc31c451d72bc2c6e3973adf4d3356a872cc77539 (patch)
tree690fb78a2e02b0d35f2a823ee31de2994930385c
parente1a99bd90a73cd427aed02bb9bbee49c7cc09c8a (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.c13
-rw-r--r--drivers/serial/tegra_hsuart.c290
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);