summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerrit Code Review <gerrit2@git-master.nvidia.com>2010-01-22 20:30:05 +0200
committerGerrit Code Review <gerrit2@git-master.nvidia.com>2010-01-22 20:30:05 +0200
commit14da3c0dbb2ed251c2679900ba5d29f096c03c60 (patch)
tree0b56fc8b49bceebfb5ec2f7edae70d79a729a22d
parente39a2445044b48ff179b272d681d1278267f1e3d (diff)
parent5b340d629f24131fd5cffee7efa277ba5e1bd400 (diff)
Merge change I1502ad81 into android-tegra-2.6.29
* changes: tegra: Update the UART Tx data path and added new system DMA API.
-rw-r--r--arch/arm/mach-tegra/dma.c27
-rw-r--r--arch/arm/mach-tegra/include/mach/dma.h7
-rw-r--r--drivers/serial/tegra_hsuart.c196
3 files changed, 137 insertions, 93 deletions
diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c
index 4f337d06c5a7..bbd3691240ea 100644
--- a/arch/arm/mach-tegra/dma.c
+++ b/arch/arm/mach-tegra/dma.c
@@ -212,23 +212,40 @@ int tegra_dma_dequeue_req(int channel, struct tegra_dma_req *_req)
}
EXPORT_SYMBOL(tegra_dma_dequeue_req);
-int tegra_dma_is_empty(int channel)
+bool tegra_dma_is_empty(int channel)
{
unsigned long irq_flags;
struct tegra_dma_channel *ch = &dma_channels[channel];
- int is_empty;
+ bool is_empty;
spin_lock_irqsave(&ch->lock, irq_flags);
if (list_empty(&ch->list))
- is_empty = 1;
+ is_empty = true;
else
- is_empty = 0;
+ is_empty = false;
spin_unlock_irqrestore(&ch->lock, irq_flags);
-
return is_empty;
}
EXPORT_SYMBOL(tegra_dma_is_empty);
+bool tegra_dma_is_req_inflight(int channel, struct tegra_dma_req *_req)
+{
+ unsigned long irq_flags;
+ struct tegra_dma_channel *ch = &dma_channels[channel];
+ struct tegra_dma_req *req;
+
+ spin_lock_irqsave(&ch->lock, irq_flags);
+ list_for_each_entry (req, &ch->list, list) {
+ if (req == _req) {
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+ return true;
+ }
+ }
+ spin_unlock_irqrestore(&ch->lock, irq_flags);
+ return false;
+}
+EXPORT_SYMBOL(tegra_dma_is_req_inflight);
+
int tegra_dma_enqueue_req(int channel, struct tegra_dma_req *req)
{
unsigned long irq_flags;
diff --git a/arch/arm/mach-tegra/include/mach/dma.h b/arch/arm/mach-tegra/include/mach/dma.h
index c39be865bafc..508e2fcf8409 100644
--- a/arch/arm/mach-tegra/include/mach/dma.h
+++ b/arch/arm/mach-tegra/include/mach/dma.h
@@ -101,12 +101,11 @@ struct tegra_dma_req {
int tegra_dma_enqueue_req(int channel, struct tegra_dma_req *req);
int tegra_dma_dequeue_req(int channel, struct tegra_dma_req *req);
void tegra_dma_dequeue(int channel);
-
-/* Returns 1 if there are DMA is empty.
- */
-int tegra_dma_is_empty(int channel);
void tegra_dma_flush(int channel);
+bool tegra_dma_is_req_inflight(int channel, struct tegra_dma_req *req);
+bool tegra_dma_is_empty(int channel);
+
int tegra_dma_allocate_channel(int mode);
void tegra_dma_free_channel(int channel);
diff --git a/drivers/serial/tegra_hsuart.c b/drivers/serial/tegra_hsuart.c
index 8b726a3c73af..e58473a88fa5 100644
--- a/drivers/serial/tegra_hsuart.c
+++ b/drivers/serial/tegra_hsuart.c
@@ -56,6 +56,7 @@
static int tx_force_pio = 0;
static int rx_force_pio = 0;
+
struct tegra_uart_port {
struct uart_port uport;
char port_name[32];
@@ -102,12 +103,44 @@ struct tegra_uart_port {
bool use_rx_dma;
bool use_tx_dma;
- struct tasklet_struct tasklet;
+
+ bool tx_pio_inflight;
+
+ struct work_struct work;
+ struct workqueue_struct *work_queue;
+
};
static void tegra_set_baudrate(struct tegra_uart_port *t, unsigned int baud);
static void tegra_set_mctrl(struct uart_port *u, unsigned int mctrl);
static void do_handle_rx_pio(struct uart_port *u);
+static inline bool tegra_wait_for_tx_fifo_empty(struct uart_port *u);
+
+/*
+ * Attempts to wait for some time before to make sure that the data is drained.
+ * Assumes that the caller has taken the u->lock.
+ *
+ * */
+static inline bool tegra_wait_for_tx_fifo_empty(struct uart_port *u)
+{
+ /* FIXME compute the correct value based on the baud rate */
+ struct tegra_uart_port *t;
+ int timeout = 10;
+ unsigned char lsr;
+
+ t = container_of(u, struct tegra_uart_port, uport);
+ do {
+ lsr = readb(t->regs + UART_LSR_0);
+ if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS)
+ return true;
+ spin_unlock(&u->lock);
+ msleep(1);
+ spin_lock(&u->lock);
+ timeout--;
+ } while (timeout);
+
+ return false;
+}
static inline int tegra_uart_isbreak(struct uart_port *u)
{
@@ -274,37 +307,56 @@ static void do_handle_rx_pio(struct uart_port *u)
return;
}
-static void tegra_tx_dma_tasklet(unsigned long data)
+
+static void tegra_tx_dma_workqueue(struct work_struct *w)
{
- struct uart_port *u = (struct uart_port *)data;
+ struct uart_port *u;
struct tegra_uart_port *t;
- struct circ_buf *xmit = &u->info->xmit;
+ struct circ_buf *xmit;
unsigned int count;
unsigned int to_send;
unsigned long flags;
+ t = container_of(w, struct tegra_uart_port, work);
+ u = &t->uport;
+ xmit = &u->info->xmit;
+
spin_lock_irqsave(&u->lock, flags);
t = container_of(u, struct tegra_uart_port, uport);
- /* DMA in flight, just return */
- if (!tegra_dma_is_empty(t->tx_dma)) {
+ /* 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;
}
- /* Update the DMA tail */
+
+ /* DMA just finished. Wait for the FIFO to drain. */
+ if (t->tx_dma_req.size) {
+ bool empty;
+ empty = tegra_wait_for_tx_fifo_empty(u);
+ BUG_ON(empty != true);
+ }
+
+ /* Update the DMA tail pointer */
xmit->tail += t->tx_dma_req.size;
xmit->tail &= UART_XMIT_SIZE - 1;
u->icount.tx += t->tx_dma_req.size;
t->tx_dma_req.size = 0;
+ /* PIO is in flight. Just return */
+ if (t->tx_pio_inflight == true) {
+ spin_unlock_irqrestore(&u->lock, flags);
+ return;
+ }
+
if (uart_circ_empty(xmit)) {
spin_unlock_irqrestore(&u->lock, flags);
return;
}
dma_sync_single_for_device(u->dev, t->tx_dma_phys,
- t->tx_dma_size, DMA_BIDIRECTIONAL);
+ t->tx_dma_size, DMA_TO_DEVICE);
to_send = CIRC_CNT_TO_END(xmit->head, xmit->tail,
UART_XMIT_SIZE);
@@ -316,34 +368,38 @@ static void tegra_tx_dma_tasklet(unsigned long data)
dev_dbg(u->dev, "Tx DMA starting 0x%x size %d\n",
xmit->tail, count);
+ 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.source_addr = t->tx_dma_phys + xmit->tail;
t->tx_dma_req.size = count;
+ t->tx_pio_inflight = false;
tegra_dma_enqueue_req(t->tx_dma, &t->tx_dma_req);
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(u);
+ spin_unlock_irqrestore(&u->lock, flags);
+ return;
+
} else {
- /* FIXME: Optimize this path! */
- count = to_send & 0x3;
- while (count) {
- unsigned char lsr;
-
- lsr = readb(t->regs + UART_LSR_0);
- if (!(lsr & (NV_DRF_DEF(UART, LSR,
- TMTY, EMPTY)))) {
- if (uart_circ_chars_pending(xmit)
- < WAKEUP_CHARS)
- uart_write_wakeup(u);
- spin_unlock_irqrestore(&u->lock, flags);
- return;
- }
- writeb(xmit->buf[xmit->tail],
- t->regs + UART_THR_DLAB_0_0);
- xmit->tail = (xmit->tail + 1) &
- (UART_XMIT_SIZE - 1);
- u->icount.tx++;
- count--;
+ /* Trasnfer 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);
+
+ 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;
+
}
- spin_unlock_irqrestore(&u->lock, flags);
}
static void do_handle_modem_signal(struct uart_port *u)
@@ -382,6 +438,7 @@ static void do_handle_tx_pio(struct uart_port *u)
t->ier_shadow = NV_FLD_SET_DRF_DEF(UART, IER_DLAB_0,
IE_THR, DISABLE, t->ier_shadow);
writeb(t->ier_shadow, t->regs + UART_IER_DLAB_0_0);
+ t->tx_pio_inflight = false;
break;
}
@@ -397,9 +454,13 @@ static void do_handle_tx_pio(struct uart_port *u)
void tegra_tx_dma_complete_callback(struct tegra_dma_req *req, int err)
{
+ struct uart_port *u = (struct uart_port *)req->data;
+ struct tegra_uart_port *t;
if (err == -TEGRA_DMA_REQ_ERROR_ABOTRED)
return;
- tasklet_schedule(req->data);
+
+ t = container_of(u, struct tegra_uart_port, uport);
+ queue_work(t->work_queue, &t->work);
}
static irqreturn_t tegra_uart_isr(int irq, void *data)
@@ -688,7 +749,7 @@ static int tegra_startup(struct uart_port *u)
if (t->use_tx_dma) {
t->tx_dma_virt = xmit->buf;
t->tx_dma_phys = dma_map_single(u->dev, xmit->buf,
- UART_XMIT_SIZE, DMA_BIDIRECTIONAL);
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
t->tx_dma_size = UART_XMIT_SIZE;
t->tx_dma_offset = 0;
@@ -702,7 +763,7 @@ static int tegra_startup(struct uart_port *u)
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 = &t->tasklet;
+ t->tx_dma_req.data = u;
t->tx_dma_req.size = 0;
}
@@ -735,16 +796,11 @@ static void tegra_shutdown(struct uart_port *u)
{
struct tegra_uart_port *t;
unsigned long flags;
- int i;
spin_lock_irqsave(&u->lock, flags);
t = container_of(u, struct tegra_uart_port, uport);
dev_vdbg(u->dev, "+tegra_shutdown\n");
- for (i=0; i< 20; i++) {
- dev_err(u->dev, "+tegra_shutdown\n");
- }
-
if (t->use_tx_dma) {
/* FIXME: dequeue means abort. Should we need to abort
* or wait for the DMA to complete?
@@ -753,6 +809,7 @@ static void tegra_shutdown(struct uart_port *u)
t->tx_dma_req.size = 0;
}
tegra_uart_hw_deinit(t);
+ spin_unlock_irqrestore(&u->lock, flags);
if (t->use_rx_dma) {
dma_free_coherent(u->dev, t->rx_dma_req.size,
@@ -763,8 +820,6 @@ static void tegra_shutdown(struct uart_port *u)
tegra_dma_free_channel(t->tx_dma);
}
- spin_unlock_irqrestore(&u->lock, flags);
-
free_irq(t->irq, u);
dev_vdbg(u->dev, "-tegra_shutdown\n");
}
@@ -844,17 +899,14 @@ static unsigned int tegra_tx_empty(struct uart_port *u)
dev_vdbg(u->dev, "+tegra_tx_empty\n");
if (t->use_tx_dma) {
- if (tegra_dma_is_empty(t->tx_dma))
- return TIOCSER_TEMT;
- else
+ if (tegra_dma_is_req_inflight(t->tx_dma, &t->tx_dma_req))
return 0;
- } else {
- lsr = readb(t->regs + UART_LSR_0);
- if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS)
- ret = TIOCSER_TEMT;
- else
- ret = 0;
}
+ lsr = readb(t->regs + UART_LSR_0);
+ if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS)
+ ret = TIOCSER_TEMT;
+ else
+ ret = 0;
dev_vdbg(u->dev, "-tegra_tx_empty\n");
return 0;
}
@@ -874,16 +926,15 @@ static void tegra_start_tx_locked(struct uart_port *u)
if (!t->use_tx_dma) {
/* Enable interrupt on transmit FIFO empty, if it is disabled */
- if (!(t->ier_shadow & NV_DRF_DEF(UART, IER_DLAB_0, IE_THR,
- ENABLE))) {
+ 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);
}
} else {
- tasklet_schedule(&t->tasklet);
+ queue_work(t->work_queue, &t->work);
}
-
dev_vdbg(t->uport.dev, "-tegra_start_tx_locked\n");
}
@@ -953,38 +1004,12 @@ void tegra_set_termios(struct uart_port *u, struct ktermios *termios,
unsigned int lcr;
unsigned int c_cflag = termios->c_cflag;
char debug_string[50];
- int timeout;
- unsigned char lsr;
t = container_of(u, struct tegra_uart_port, uport);
dev_vdbg(t->uport.dev, "+tegra_set_termios\n");
debug_string[0] = 0;
strlcat(debug_string, "uart port setting: ", 50);
-
- /* Make sure that the Tx buffer is drained so that buad rate chenage
- * will not cause the data errors.
- */
- if (t->use_tx_dma) {
- timeout = 10;
- do {
- if (tegra_dma_is_empty(t->tx_dma))
- break;
- timeout--;
- msleep(1);
- } while (timeout);
- BUG_ON(!timeout);
- }
- timeout = 10;
- do {
- lsr = readb(t->regs + UART_LSR_0);
- if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS)
- break;
- timeout--;
- msleep(1);
- } while (timeout);
- BUG_ON(!timeout);
-
spin_lock_irqsave(&u->lock, flags);
/* Baud rate */
@@ -1131,7 +1156,7 @@ static int __devexit tegra_uart_remove(struct platform_device *pdev)
u = &t->uport;
uart_remove_one_port(&tegra_uart_driver, u);
- tasklet_kill(&t->tasklet);
+ destroy_workqueue(t->work_queue);
platform_set_drvdata(pdev, NULL);
@@ -1150,7 +1175,7 @@ static int __init tegra_uart_probe(struct platform_device *pdev)
struct uart_port *u;
int ret;
char clk_name[MAX_CLK_NAME_CHARS];
-
+ char name[64];
if (pdev->id < 0 || pdev->id > tegra_uart_driver.nr) {
printk(KERN_ERR "Invalid Uart instance (%d) \n", pdev->id);
return -ENODEV;
@@ -1193,13 +1218,16 @@ static int __init tegra_uart_probe(struct platform_device *pdev)
return ret;
}
- tasklet_init(&t->tasklet, tegra_tx_dma_tasklet, (unsigned long)u);
-
+ snprintf(name, sizeof(name), "tegra_hsuart_%d", u->line);
+ t->work_queue = create_singlethread_workqueue(name);
+ if (t->work_queue == NULL) {
+ dev_err(u->dev, "Failed to create work queue\n");
+ goto fail;
+ }
+ INIT_WORK(&t->work, tegra_tx_dma_workqueue);
printk(KERN_INFO "Registered UART port %s%d\n",
tegra_uart_driver.dev_name, u->line);
-
return ret;
-
fail:
kfree(t);
return -ENODEV;