summaryrefslogtreecommitdiff
path: root/drivers/serial
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2010-04-05 13:30:14 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:35:20 -0800
commitf7ca01fdcfb723167f2439a8c2a494319907668c (patch)
tree6c490d326c611134331f4cb9b2de4edf55fd8b56 /drivers/serial
parent4badc811d4a1682c6c14ddb17f01f1ccab2a19db (diff)
serial: Add tegra_hsuart driver
Change-Id: Ic2d0a43ac3597c8a270758af6ea3090acdd0fe7f Signed-off-by: Colin Cross <ccross@android.com>
Diffstat (limited to 'drivers/serial')
-rw-r--r--drivers/serial/tegra_hsuart.c1182
1 files changed, 1182 insertions, 0 deletions
diff --git a/drivers/serial/tegra_hsuart.c b/drivers/serial/tegra_hsuart.c
new file mode 100644
index 000000000000..dadf2ed64070
--- /dev/null
+++ b/drivers/serial/tegra_hsuart.c
@@ -0,0 +1,1182 @@
+/*
+ * drivers/serial/tegra_hsuart.c
+ *
+ * High-speed serial driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (C) 2009 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/*#define DEBUG 1*/
+/*#define VERBOSE_DEBUG 1*/
+
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/termios.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/string.h>
+#include <linux/pagemap.h>
+#include <linux/serial_reg.h>
+#include <linux/serial_8250.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <mach/dma.h>
+
+#define TX_EMPTY_STATUS (UART_LSR_TEMT | UART_LSR_THRE)
+
+#define BYTES_TO_ALIGN(x) ((unsigned long)(ALIGN((x), sizeof(u32))) - \
+ (unsigned long)(x))
+
+#define UART_RX_DMA_BUFFER_SIZE (4 * 1024 * 16)
+
+#define UART_TX_DMA_BUFFERS_TOTAL 2
+
+#define UART_LSR_FIFOE 0x80
+#define UART_IER_EORD 0x20
+#define UART_MCR_RTS_EN 0x40
+#define UART_MCR_CTS_EN 0x20
+#define UART_LSR_ANY (UART_LSR_OE | UART_LSR_BI | \
+ UART_LSR_PE | UART_LSR_FE)
+
+#define TX_FORCE_PIO 0
+#define RX_FORCE_PIO 0
+
+const int dma_req_sel[] = {
+ TEGRA_DMA_REQ_SEL_UARTA,
+ TEGRA_DMA_REQ_SEL_UARTB,
+ TEGRA_DMA_REQ_SEL_UARTC,
+ TEGRA_DMA_REQ_SEL_UARTD,
+ TEGRA_DMA_REQ_SEL_UARTE,
+};
+
+#define TEGRA_TX_PIO 1
+#define TEGRA_TX_DMA 2
+
+#define TEGRA_UART_MIN_DMA 16
+#define TEGRA_UART_FIFO_SIZE 4
+
+struct tegra_uart_port {
+ struct uart_port uport;
+ char port_name[32];
+
+ /* Module info */
+ unsigned long size;
+ struct clk *clk;
+ unsigned int baud;
+
+ /* Register shadow */
+ unsigned char fcr_shadow;
+ unsigned char mcr_shadow;
+ unsigned char lcr_shadow;
+ unsigned char ier_shadow;
+ bool use_cts_control;
+ bool rts_active;
+
+ int tx_in_progress;
+ unsigned int tx_bytes;
+
+ dma_addr_t xmit_dma_addr;
+
+ /* Rm DMA handles */
+ struct tegra_dma_req tx_dma_req;
+ struct tegra_dma_channel *tx_dma;
+
+ /* DMA requests */
+ struct tegra_dma_req rx_dma_req;
+ struct tegra_dma_channel *rx_dma;
+
+ bool use_rx_dma;
+ bool use_tx_dma;
+};
+
+static inline u8 uart_readb(struct tegra_uart_port *t, unsigned long reg)
+{
+ u8 val = readb(t->uport.membase + (reg << t->uport.regshift));
+ dev_vdbg(t->uport.dev, "%s: %p %03lx = %02x\n", __func__,
+ t->uport.membase, reg << t->uport.regshift, val);
+ return val;
+}
+
+static inline u8 uart_writeb(struct tegra_uart_port *t, u8 val,
+ unsigned long reg)
+{
+ dev_vdbg(t->uport.dev, "%s: %p %03lx %02x\n",
+ __func__, t->uport.membase, reg << t->uport.regshift, val);
+ return writeb(val, t->uport.membase + (reg << t->uport.regshift));
+}
+
+static inline u8 uart_writel(struct tegra_uart_port *t, u32 val,
+ unsigned long reg)
+{
+ dev_vdbg(t->uport.dev, "%s: %p %03lx %08x\n",
+ __func__, t->uport.membase, reg << t->uport.regshift, val);
+ return writel(val, t->uport.membase + (reg << t->uport.regshift));
+}
+
+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 tegra_uart_port *t);
+static void set_rts(struct tegra_uart_port *t, bool active);
+static void set_dtr(struct tegra_uart_port *t, bool active);
+
+static void fill_tx_fifo(struct tegra_uart_port *t, int max_bytes)
+{
+ int i;
+ struct circ_buf *xmit = &t->uport.state->xmit;
+
+ for (i = 0; i < max_bytes; i++) {
+ BUG_ON(uart_circ_empty(xmit));
+ uart_writeb(t, xmit->buf[xmit->tail], UART_TX);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ t->uport.icount.tx++;
+ }
+}
+
+static void tegra_start_pio_tx(struct tegra_uart_port *t, unsigned int bytes)
+{
+ if (bytes > TEGRA_UART_FIFO_SIZE)
+ bytes = TEGRA_UART_FIFO_SIZE;
+
+ t->fcr_shadow &= ~UART_FCR_T_TRIG_11;
+ t->fcr_shadow |= UART_FCR_T_TRIG_00;
+ uart_writeb(t, t->fcr_shadow, UART_FCR);
+ t->tx_in_progress = TEGRA_TX_PIO;
+ t->tx_bytes = bytes;
+ t->ier_shadow |= UART_IER_THRI;
+ uart_writeb(t, t->ier_shadow, UART_IER);
+}
+
+static void tegra_start_dma_tx(struct tegra_uart_port *t)
+{
+ unsigned int count;
+ struct circ_buf *xmit;
+ xmit = &t->uport.state->xmit;
+ count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+
+ dma_sync_single_for_device(t->uport.dev, t->xmit_dma_addr,
+ UART_XMIT_SIZE, DMA_TO_DEVICE);
+
+ t->fcr_shadow &= ~UART_FCR_T_TRIG_11;
+ t->fcr_shadow |= UART_FCR_T_TRIG_01;
+ uart_writeb(t, t->fcr_shadow, UART_FCR);
+
+ t->tx_bytes = count & ~(sizeof(u32)-1);
+ t->tx_dma_req.source_addr = t->xmit_dma_addr + xmit->tail;
+ t->tx_dma_req.size = t->tx_bytes;
+
+ t->tx_in_progress = TEGRA_TX_DMA;
+
+ tegra_dma_enqueue_req(t->tx_dma, &t->tx_dma_req);
+}
+
+/* Called by serial core driver, or in interrupt context when a PIO or DMA
+ * tranmission is complete. Called with u->lock taken.
+ */
+static void tegra_start_tx(struct uart_port *u)
+{
+ struct tegra_uart_port *t;
+ struct circ_buf *xmit;
+ unsigned long tail;
+ int pending;
+
+ t = container_of(u, struct tegra_uart_port, uport);
+ xmit = &u->state->xmit;
+
+ dev_vdbg(t->uport.dev, "+tegra_start_tx\n");
+ while (!uart_circ_empty(xmit) && !t->tx_in_progress) {
+ tail = (unsigned long)&xmit->buf[xmit->tail];
+ pending = uart_circ_chars_pending(xmit);
+ if (TX_FORCE_PIO || pending < TEGRA_UART_MIN_DMA)
+ tegra_start_pio_tx(t, pending);
+ else if (BYTES_TO_ALIGN(tail) > 0)
+ tegra_start_pio_tx(t, BYTES_TO_ALIGN(tail));
+ else
+ tegra_start_dma_tx(t);
+ }
+ dev_vdbg(t->uport.dev, "-tegra_start_tx\n");
+}
+
+static int tegra_start_dma_rx(struct tegra_uart_port *t)
+{
+ wmb();
+ if (tegra_dma_enqueue_req(t->rx_dma, &t->rx_dma_req)) {
+ dev_err(t->uport.dev, "Could not enqueue Rx DMA req\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void tegra_rx_dma_threshold_callback(struct tegra_dma_req *req, int err)
+{
+ struct tegra_uart_port *t = req->dev;
+ struct uart_port *u = &t->uport;
+ unsigned long flags;
+
+ spin_lock_irqsave(&u->lock, flags);
+
+ if (t->rts_active)
+ set_rts(t, false);
+ tegra_dma_dequeue(t->rx_dma);
+ if (t->rts_active)
+ set_rts(t, true);
+
+ spin_unlock_irqrestore(&u->lock, flags);
+}
+
+/* It is expected that the callers take the UART lock when this API is called.
+ *
+ * There are 2 contexts when this function is called:
+ *
+ * 1. DMA ISR - DMA ISR triggers the threshold complete calback, which calls the
+ * dequue API which in-turn calls this callback. UART lock is taken during
+ * the call to the threshold callback.
+ *
+ * 2. UART ISR - UART calls the dequue API which in-turn will call this API.
+ * In this case, UART ISR takes the UART lock.
+ * */
+static void tegra_rx_dma_complete_callback(struct tegra_dma_req *req, int err)
+{
+ struct tegra_uart_port *t = req->dev;
+ struct uart_port *u = &t->uport;
+ struct tty_struct *tty = u->state->port.tty;
+
+ /* If we are here, DMA is stopped */
+
+ dev_dbg(t->uport.dev, "%s: %d\n", __func__, req->bytes_transferred);
+ if (req->bytes_transferred) {
+ t->uport.icount.rx += req->bytes_transferred;
+ tty_insert_flip_string(tty,
+ ((unsigned char *)(req->virt_addr)),
+ req->bytes_transferred);
+ }
+
+ if (req->status == -TEGRA_DMA_REQ_ERROR_ABORTED)
+ do_handle_rx_pio(t);
+
+ spin_unlock(&u->lock);
+ tty_flip_buffer_push(u->state->port.tty);
+ spin_lock(&u->lock);
+
+ tegra_start_dma_rx(t);
+}
+
+/* Lock already taken */
+static void do_handle_rx_dma(struct tegra_uart_port *t)
+{
+ if (t->rts_active)
+ set_rts(t, false);
+ tegra_dma_dequeue(t->rx_dma);
+ if (t->rts_active)
+ set_rts(t, true);
+}
+
+static char do_decode_rx_error(struct tegra_uart_port *t, u8 lsr)
+{
+ char flag = TTY_NORMAL;
+
+ if (unlikely(lsr & UART_LSR_ANY)) {
+ if (lsr & UART_LSR_OE) {
+ /* Overrrun error */
+ flag |= TTY_OVERRUN;
+ t->uport.icount.overrun++;
+ dev_err(t->uport.dev, "Got overrun errors\n");
+ } else if (lsr & UART_LSR_PE) {
+ /* Parity error */
+ flag |= TTY_PARITY;
+ t->uport.icount.parity++;
+ dev_err(t->uport.dev, "Got Parity errors\n");
+ } else if (lsr & UART_LSR_FE) {
+ flag |= TTY_FRAME;
+ t->uport.icount.frame++;
+ dev_err(t->uport.dev, "Got frame errors\n");
+ } else if (lsr & UART_LSR_BI) {
+ dev_err(t->uport.dev, "Got Break\n");
+ t->uport.icount.brk++;
+ /* If FIFO read error without any data, reset Rx FIFO */
+ if (!(lsr & UART_LSR_DR) && (lsr & UART_LSR_FIFOE)) {
+ unsigned char fcr = t->fcr_shadow;
+ fcr |= UART_FCR_CLEAR_RCVR;
+ uart_writeb(t, fcr, UART_FCR);
+ }
+ }
+ }
+ return flag;
+}
+
+static void do_handle_rx_pio(struct tegra_uart_port *t)
+{
+ int count = 0;
+ do {
+ char flag = TTY_NORMAL;
+ unsigned char lsr = 0;
+ unsigned char ch;
+
+
+ lsr = uart_readb(t, UART_LSR);
+ if (!(lsr & UART_LSR_DR))
+ break;
+
+ flag = do_decode_rx_error(t, lsr);
+ ch = uart_readb(t, UART_RX);
+ t->uport.icount.rx++;
+ count++;
+
+ if (!uart_handle_sysrq_char(&t->uport, c))
+ uart_insert_char(&t->uport, lsr, UART_LSR_OE, ch, flag);
+ } while (1);
+
+ dev_dbg(t->uport.dev, "PIO received %d bytes\n", count);
+
+ return;
+}
+
+static void do_handle_modem_signal(struct uart_port *u)
+{
+ unsigned char msr;
+ struct tegra_uart_port *t;
+
+ t = container_of(u, struct tegra_uart_port, uport);
+ msr = uart_readb(t, UART_MSR);
+ if (msr & UART_MSR_CTS)
+ dev_dbg(u->dev, "CTS triggered\n");
+ if (msr & UART_MSR_DSR)
+ dev_dbg(u->dev, "DSR enabled\n");
+ if (msr & UART_MSR_DCD)
+ dev_dbg(u->dev, "CD enabled\n");
+ if (msr & UART_MSR_RI)
+ dev_dbg(u->dev, "RI enabled\n");
+ return;
+}
+
+static void do_handle_tx_pio(struct tegra_uart_port *t)
+{
+ struct circ_buf *xmit = &t->uport.state->xmit;
+
+ fill_tx_fifo(t, t->tx_bytes);
+
+ t->tx_in_progress = 0;
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&t->uport);
+
+ tegra_start_tx(&t->uport);
+ return;
+}
+
+static void tegra_tx_dma_complete_callback(struct tegra_dma_req *req, int err)
+{
+ struct tegra_uart_port *t = req->dev;
+ struct circ_buf *xmit = &t->uport.state->xmit;
+ int count = req->bytes_transferred;
+ unsigned long flags;
+ int timeout = 20;
+ if (err == -TEGRA_DMA_REQ_ERROR_ABORTED)
+ BUG();
+
+ dev_vdbg(t->uport.dev, "%s: %d\n", __func__, count);
+
+ while ((uart_readb(t, UART_LSR) & TX_EMPTY_STATUS) != TX_EMPTY_STATUS) {
+ timeout--;
+ if (timeout-- == 0) {
+ dev_err(t->uport.dev,
+ "timed out waiting for TX FIFO to empty");
+ return;
+ }
+ msleep(1);
+ }
+
+ spin_lock_irqsave(&t->uport.lock, flags);
+ xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+ t->tx_in_progress = 0;
+
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&t->uport);
+
+ tegra_start_tx(&t->uport);
+ spin_unlock_irqrestore(&t->uport.lock, flags);
+}
+
+static irqreturn_t tegra_uart_isr(int irq, void *data)
+{
+ struct tegra_uart_port *t = data;
+ struct uart_port *u = &t->uport;
+ unsigned char iir;
+ unsigned char ier;
+ unsigned long flags;
+
+ spin_lock_irqsave(&u->lock, flags);
+ t = container_of(u, struct tegra_uart_port, uport);
+ while (1) {
+ iir = uart_readb(t, UART_IIR);
+ if (iir & UART_IIR_NO_INT) {
+ spin_unlock_irqrestore(&u->lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ dev_dbg(u->dev, "tegra_uart_isr iir = 0x%x (%d)\n", iir,
+ (iir >> 1) & 0x7);
+ switch ((iir >> 1) & 0x7) {
+ case 0: /* Modem signal change interrupt */
+ do_handle_modem_signal(u);
+ break;
+ case 1: /* Transmit interrupt only triggered when using PIO */
+ t->ier_shadow &= ~UART_IER_THRI;
+ uart_writeb(t, t->ier_shadow, UART_IER);
+ do_handle_tx_pio(t);
+ break;
+ case 4: /* End of data */
+ /* As per hw spec, to clear EORD interrupt, we need
+ * to disable and then re-enable the interrupt.
+ */
+ ier = t->ier_shadow;
+ ier &= ~UART_IER_EORD;
+ uart_writeb(t, ier, UART_IER);
+ ier |= UART_IER_EORD;
+ uart_writeb(t, ier, UART_IER);
+ /* fallthrough */
+ case 2: /* Receive */
+ case 6: /* Rx timeout */
+ if (likely(t->use_rx_dma))
+ do_handle_rx_dma(t);
+ else
+ do_handle_rx_pio(t);
+
+ spin_unlock_irqrestore(&u->lock, flags);
+ tty_flip_buffer_push(u->state->port.tty);
+ spin_lock_irqsave(&u->lock, flags);
+ break;
+ case 3: /* Receive error */
+ /* FIXME how to handle this? Why do we get here */
+ do_decode_rx_error(t, uart_readb(t, UART_LSR));
+ break;
+ case 5: /* break nothing to handle */
+ case 7: /* break nothing to handle */
+ break;
+ }
+ }
+}
+
+static void tegra_stop_rx(struct uart_port *u)
+{
+ struct tegra_uart_port *t;
+
+ t = container_of(u, struct tegra_uart_port, uport);
+
+ if (t->rts_active)
+ set_rts(t, false);
+ if (t->rx_dma)
+ tegra_dma_dequeue(t->rx_dma);
+
+ return;
+}
+
+static void tegra_uart_hw_deinit(struct tegra_uart_port *t)
+{
+ unsigned char fcr;
+
+ /* Disable interrupts */
+ uart_writeb(t, 0, UART_IER);
+
+ /* Reset the Rx and Tx FIFOs */
+ fcr = t->fcr_shadow;
+ fcr |= UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR;
+ uart_writeb(t, fcr, UART_FCR);
+
+ clk_disable(t->clk);
+ t->baud = 0;
+}
+
+static int tegra_uart_hw_init(struct tegra_uart_port *t)
+{
+ unsigned char fcr;
+ unsigned char ier;
+
+ dev_vdbg(t->uport.dev, "+tegra_uart_hw_init\n");
+
+ t->fcr_shadow = 0;
+ t->mcr_shadow = UART_MCR_LOOP;
+ t->lcr_shadow = 0;
+ t->ier_shadow = 0;
+ t->baud = 0;
+
+ clk_enable(t->clk);
+ msleep(10);
+
+ /* Reset the FIFO twice with some delay to make sure that the FIFOs are
+ * really flushed. Wait is needed as the clearing needs to cross
+ * multiple clock domains.
+ * */
+ t->fcr_shadow = UART_FCR_ENABLE_FIFO;
+
+ fcr = t->fcr_shadow;
+ fcr |= UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR;
+ uart_writeb(t, fcr, UART_FCR);
+
+ udelay(100);
+ uart_writeb(t, fcr, UART_FCR);
+ udelay(100);
+
+ /* Set the trigger level
+ *
+ * For PIO mode:
+ *
+ * For receive, this will interrupt the CPU after that many number of
+ * bytes are received, for the remaining bytes the receive timeout
+ * interrupt is received.
+ *
+ * Rx high watermark is set to 4.
+ *
+ * For transmit, if the trasnmit interrupt is enabled, this will
+ * interrupt the CPU when the number of entries in the FIFO reaches the
+ * low watermark.
+ *
+ * Tx low watermark is set to 8.
+ *
+ * For DMA mode:
+ *
+ * Set the Tx trigger to 4. This should match the DMA burst size that
+ * programmed in the DMA registers.
+ * */
+ t->fcr_shadow |= UART_FCR_R_TRIG_01;
+ t->fcr_shadow |= UART_FCR_T_TRIG_01;
+ uart_writeb(t, t->fcr_shadow, UART_FCR);
+
+ if (t->use_rx_dma)
+ t->fcr_shadow |= UART_FCR_DMA_SELECT;
+ uart_writeb(t, t->fcr_shadow, UART_FCR);
+
+ /*
+ * Enable IE_RXS for the receive status interrupts like line errros.
+ * Enable IE_RX_TIMEOUT to get the bytes which cannot be DMA'd.
+ *
+ * If using DMA mode, enable EORD instead of receive interrupt which
+ * will interrupt after the UART is done with the receive instead of
+ * the interrupt when the FIFO "threshold" is reached.
+ *
+ * EORD is different interrupt than RX_TIMEOUT - RX_TIMEOUT occurs when
+ * the DATA is sitting in the FIFO and couldn't be transferred to the
+ * DMA as the DMA size alignment(4 bytes) is not met. EORD will be
+ * triggered when there is a pause of the incomming data stream for 4
+ * characters long.
+ *
+ * For pauses in the data which is not aligned to 4 bytes, we get
+ * both the EORD as well as RX_TIMEOUT - SW sees RX_TIMEOUT first
+ * then the EORD.
+ *
+ * Don't get confused, believe in the magic of nvidia hw...:-)
+ */
+ ier = 0;
+ ier |= UART_IER_RLSI | UART_IER_RTOIE;
+ if (t->use_rx_dma)
+ ier |= UART_IER_EORD;
+ else
+ ier |= UART_IER_RDI;
+ t->ier_shadow = ier;
+ uart_writeb(t, ier, UART_IER);
+
+ dev_vdbg(t->uport.dev, "-tegra_uart_hw_init\n");
+ return 0;
+}
+
+static int tegra_uart_init_rx_dma(struct tegra_uart_port *t)
+{
+ dma_addr_t rx_dma_phys;
+ void *rx_dma_virt;
+
+ t->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_CONTINOUS);
+ if (IS_ERR_OR_NULL(t->rx_dma))
+ return -ENODEV;
+
+ memset(&t->rx_dma_req, 0, sizeof(t->rx_dma_req));
+
+ t->rx_dma_req.size = UART_RX_DMA_BUFFER_SIZE;
+ rx_dma_virt = dma_alloc_coherent(t->uport.dev,
+ t->rx_dma_req.size, &rx_dma_phys, GFP_KERNEL);
+ if (!rx_dma_virt) {
+ dev_err(t->uport.dev, "DMA buffers allocate failed\n");
+ goto fail;
+ }
+ t->rx_dma_req.dest_addr = rx_dma_phys;
+ t->rx_dma_req.virt_addr = rx_dma_virt;
+
+ t->rx_dma_req.source_addr = (unsigned long)t->uport.mapbase;
+ t->rx_dma_req.source_wrap = 4;
+ t->rx_dma_req.dest_wrap = 0;
+ t->rx_dma_req.to_memory = 1;
+ t->rx_dma_req.source_bus_width = 8;
+ t->rx_dma_req.dest_bus_width = 32;
+ t->rx_dma_req.req_sel = dma_req_sel[t->uport.line];
+ t->rx_dma_req.complete = tegra_rx_dma_complete_callback;
+ t->rx_dma_req.threshold = tegra_rx_dma_threshold_callback;
+ t->rx_dma_req.dev = t;
+
+ return 0;
+fail:
+ 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);
+ return -ENODEV;
+}
+
+static int tegra_startup(struct uart_port *u)
+{
+ struct tegra_uart_port *t = container_of(u,
+ struct tegra_uart_port, uport);
+ int ret = 0;
+
+ t = container_of(u, struct tegra_uart_port, uport);
+ sprintf(t->port_name, "tegra_uart_%d", u->line);
+
+ t->use_tx_dma = false;
+ if (!TX_FORCE_PIO) {
+ t->tx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
+ if (!IS_ERR_OR_NULL(t->tx_dma))
+ t->use_tx_dma = true;
+ }
+ if (t->use_tx_dma) {
+ 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 = (unsigned long)t->uport.mapbase;
+ t->tx_dma_req.dest_wrap = 4;
+ t->tx_dma_req.source_wrap = 0;
+ t->tx_dma_req.source_bus_width = 32;
+ t->tx_dma_req.dest_bus_width = 8;
+ t->tx_dma_req.req_sel = dma_req_sel[t->uport.line];
+ t->tx_dma_req.dev = t;
+ t->tx_dma_req.size = 0;
+ t->xmit_dma_addr = dma_map_single(t->uport.dev,
+ t->uport.state->xmit.buf, UART_XMIT_SIZE,
+ DMA_TO_DEVICE);
+ }
+ t->tx_in_progress = 0;
+
+ t->use_rx_dma = false;
+ if (!TX_FORCE_PIO) {
+ if (!tegra_uart_init_rx_dma(t))
+ t->use_rx_dma = true;
+ }
+
+ ret = tegra_uart_hw_init(t);
+ if (ret)
+ goto fail;
+
+ if (t->use_rx_dma)
+ tegra_start_dma_rx(t);
+
+ dev_dbg(u->dev, "Requesting IRQ %d\n", u->irq);
+ msleep(1);
+
+ ret = request_irq(u->irq, tegra_uart_isr, IRQF_DISABLED,
+ t->port_name, t);
+ if (ret) {
+ dev_err(u->dev, "Failed to register ISR for IRQ %d\n", u->irq);
+ goto fail;
+ }
+ dev_dbg(u->dev,"Started UART port %d\n", u->line);
+
+ return 0;
+fail:
+ dev_err(u->dev, "Tegra UART startup failed\n");
+ return ret;
+}
+
+static void tegra_shutdown(struct uart_port *u)
+{
+ struct tegra_uart_port *t;
+ unsigned long flags;
+
+ spin_lock_irqsave(&u->lock, flags);
+ t = container_of(u, struct tegra_uart_port, uport);
+ dev_vdbg(u->dev, "+tegra_shutdown\n");
+
+ tegra_uart_hw_deinit(t);
+ spin_unlock_irqrestore(&u->lock, flags);
+ if (t->use_rx_dma) {
+ tegra_dma_free_channel(t->rx_dma);
+ dma_free_coherent(u->dev, t->rx_dma_req.size,
+ t->rx_dma_req.virt_addr, t->rx_dma_req.dest_addr);
+ }
+
+ if (t->use_tx_dma)
+ tegra_dma_free_channel(t->tx_dma);
+
+ dma_unmap_single(t->uport.dev, t->xmit_dma_addr, UART_XMIT_SIZE,
+ DMA_TO_DEVICE);
+ free_irq(u->irq, t);
+ dev_vdbg(u->dev, "-tegra_shutdown\n");
+}
+
+static unsigned int tegra_get_mctrl(struct uart_port *u)
+{
+ /* RI - Ring detector is active
+ * CD/DCD/CAR - Carrier detect is always active. For some reason
+ * linux has different names for carrier detect.
+ * DSR - Data Set ready is active as the hardware doesn't support it.
+ * Don't know if the linux support this yet?
+ * CTS - Clear to send. Always set to active, as the hardware handles
+ * CTS automatically.
+ * */
+ return TIOCM_RI | TIOCM_CD | TIOCM_DSR | TIOCM_CTS;
+}
+
+static void set_rts(struct tegra_uart_port *t, bool active)
+{
+ unsigned char mcr;
+ mcr = t->mcr_shadow;
+ if (active)
+ mcr |= UART_MCR_RTS;
+ else
+ mcr &= ~UART_MCR_RTS;
+ if (mcr != t->mcr_shadow) {
+ uart_writeb(t, mcr, UART_MCR);
+ t->mcr_shadow = mcr;
+ }
+ return;
+}
+
+static void set_dtr(struct tegra_uart_port *t, bool active)
+{
+ unsigned char mcr;
+ mcr = t->mcr_shadow;
+ if (active)
+ mcr |= UART_MCR_DTR;
+ else
+ mcr &= UART_MCR_DTR;
+ if (mcr != t->mcr_shadow) {
+ uart_writeb(t, mcr, UART_MCR);
+ t->mcr_shadow = mcr;
+ }
+ return;
+}
+
+static void tegra_set_mctrl(struct uart_port *u, unsigned int mctrl)
+{
+ unsigned char mcr;
+ struct tegra_uart_port *t;
+
+ dev_dbg(u->dev, "tegra_set_mctrl called with %d\n", mctrl);
+ t = container_of(u, struct tegra_uart_port, uport);
+
+ mcr = t->mcr_shadow;
+ if (mctrl & TIOCM_RTS) {
+ t->rts_active = true;
+ set_rts(t, true);
+ } else {
+ t->rts_active = false;
+ set_rts(t, false);
+ }
+
+ if (mctrl & TIOCM_DTR)
+ set_dtr(t, true);
+ else
+ set_dtr(t, false);
+ return;
+}
+
+static void tegra_break_ctl(struct uart_port *u, int break_ctl)
+{
+ struct tegra_uart_port *t;
+ unsigned char lcr;
+
+ t = container_of(u, struct tegra_uart_port, uport);
+ lcr = t->lcr_shadow;
+ if (break_ctl)
+ lcr |= UART_LCR_SBC;
+ else
+ lcr &= ~UART_LCR_SBC;
+ uart_writeb(t, lcr, UART_LCR);
+ t->lcr_shadow = lcr;
+}
+
+static int tegra_request_port(struct uart_port *u)
+{
+ return 0;
+}
+
+static void tegra_release_port(struct uart_port *u)
+{
+
+}
+
+static unsigned int tegra_tx_empty(struct uart_port *u)
+{
+ struct tegra_uart_port *t;
+ unsigned int ret = 0;
+ unsigned long flags;
+
+ t = container_of(u, struct tegra_uart_port, uport);
+ dev_vdbg(u->dev, "+tegra_tx_empty\n");
+
+ spin_lock_irqsave(&u->lock, flags);
+ if (!t->tx_in_progress)
+ ret = TIOCSER_TEMT;
+ spin_unlock_irqrestore(&u->lock, flags);
+
+ dev_vdbg(u->dev, "-tegra_tx_empty\n");
+ return ret;
+}
+
+static void tegra_stop_tx(struct uart_port *u)
+{
+ struct tegra_uart_port *t;
+
+ t = container_of(u, struct tegra_uart_port, uport);
+
+ if (t->use_tx_dma)
+ tegra_dma_dequeue_req(t->tx_dma, &t->tx_dma_req);
+ return;
+}
+
+static void tegra_enable_ms(struct uart_port *u)
+{
+}
+
+#define UART_CLOCK_ACCURACY 5
+
+static void tegra_set_baudrate(struct tegra_uart_port *t, unsigned int baud)
+{
+ unsigned long rate;
+ unsigned int divisor;
+ unsigned char lcr;
+
+ if (t->baud == baud)
+ return;
+
+ rate = clk_get_rate(t->clk);
+
+ divisor = rate;
+ do_div(divisor, 16);
+ divisor += baud/2;
+ do_div(divisor, baud);
+
+ lcr = t->lcr_shadow;
+ lcr |= UART_LCR_DLAB;
+ uart_writeb(t, lcr, UART_LCR);
+
+ uart_writel(t, divisor & 0xFF, UART_TX);
+ uart_writel(t, ((divisor >> 8) & 0xFF), UART_IER);
+
+ lcr &= ~UART_LCR_DLAB;
+ uart_writeb(t, lcr, UART_LCR);
+
+ t->baud = baud;
+ dev_dbg(t->uport.dev, "Baud %u clock freq %lu and divisor of %u\n",
+ baud, rate, divisor);
+}
+
+static void tegra_set_termios(struct uart_port *u, struct ktermios *termios,
+ struct ktermios *oldtermios)
+{
+ struct tegra_uart_port *t;
+ unsigned int baud;
+ unsigned long flags;
+ unsigned int lcr;
+ unsigned int c_cflag = termios->c_cflag;
+ unsigned char mcr;
+
+ t = container_of(u, struct tegra_uart_port, uport);
+ dev_vdbg(t->uport.dev, "+tegra_set_termios\n");
+
+ spin_lock_irqsave(&u->lock, flags);
+
+ /* Baud rate */
+ baud = uart_get_baud_rate(u, termios, oldtermios, 200, 4000000);
+ tegra_set_baudrate(t, baud);
+
+ /* Parity */
+ lcr = t->lcr_shadow;
+ lcr &= ~UART_LCR_PARITY;
+ if (PARENB == (c_cflag & PARENB)) {
+ if (CMSPAR == (c_cflag & CMSPAR)) {
+ /* FIXME What is space parity? */
+ /* data |= SPACE_PARITY; */
+ } else if (c_cflag & PARODD) {
+ lcr |= UART_LCR_PARITY;
+ lcr &= ~UART_LCR_EPAR;
+ lcr &= ~UART_LCR_SPAR;
+ } else {
+ lcr |= UART_LCR_PARITY;
+ lcr |= UART_LCR_EPAR;
+ lcr &= ~UART_LCR_SPAR;
+ }
+ }
+
+ lcr &= UART_LCR_WLEN8;
+ switch (c_cflag & CSIZE) {
+ case CS5:
+ lcr |= UART_LCR_WLEN5;
+ break;
+ case CS6:
+ lcr |= UART_LCR_WLEN6;
+ break;
+ case CS7:
+ lcr |= UART_LCR_WLEN7;
+ break;
+ default:
+ lcr |= UART_LCR_WLEN8;
+ break;
+ }
+
+ /* Stop bits */
+ if (termios->c_cflag & CSTOPB)
+ lcr |= UART_LCR_STOP;
+ else
+ lcr &= ~UART_LCR_STOP;
+
+ uart_writeb(t, lcr, UART_LCR);
+ t->lcr_shadow = lcr;
+
+ /* Flow control */
+ if (termios->c_cflag & CRTSCTS) {
+ mcr = t->mcr_shadow;
+ mcr |= UART_MCR_CTS_EN;
+ mcr &= ~UART_MCR_RTS_EN;
+ t->mcr_shadow = mcr;
+ uart_writeb(t, mcr, UART_MCR);
+ t->use_cts_control = true;
+ } else {
+ mcr = t->mcr_shadow;
+ mcr &= ~UART_MCR_CTS_EN;
+ mcr &= ~UART_MCR_RTS_EN;
+ t->mcr_shadow = mcr;
+ uart_writeb(t, mcr, UART_MCR);
+ t->use_cts_control = false;
+ }
+
+ spin_unlock_irqrestore(&u->lock, flags);
+ dev_vdbg(t->uport.dev, "-tegra_set_termios\n");
+ return;
+}
+
+/*
+ * Flush any TX data submitted for DMA. Called when the TX circular
+ * buffer is reset.
+ */
+static void tegra_flush_buffer(struct uart_port *u)
+{
+ struct tegra_uart_port *t;
+
+ dev_vdbg(u->dev, "tegra_flush_buffer called");
+
+ t = container_of(u, struct tegra_uart_port, uport);
+
+ if (t->use_tx_dma) {
+ tegra_dma_dequeue_req(t->tx_dma, &t->tx_dma_req);
+ t->tx_dma_req.size = 0;
+ }
+ return;
+}
+
+
+static void tegra_pm(struct uart_port *u, unsigned int state,
+ unsigned int oldstate)
+{
+
+}
+
+static const char *tegra_type(struct uart_port *u)
+{
+ return 0;
+}
+
+static struct uart_ops tegra_uart_ops = {
+ .tx_empty = tegra_tx_empty,
+ .set_mctrl = tegra_set_mctrl,
+ .get_mctrl = tegra_get_mctrl,
+ .stop_tx = tegra_stop_tx,
+ .start_tx = tegra_start_tx,
+ .stop_rx = tegra_stop_rx,
+ .flush_buffer = tegra_flush_buffer,
+ .enable_ms = tegra_enable_ms,
+ .break_ctl = tegra_break_ctl,
+ .startup = tegra_startup,
+ .shutdown = tegra_shutdown,
+ .set_termios = tegra_set_termios,
+ .pm = tegra_pm,
+ .type = tegra_type,
+ .request_port = tegra_request_port,
+ .release_port = tegra_release_port,
+};
+
+static int __init tegra_uart_probe(struct platform_device *pdev);
+static int __devexit tegra_uart_remove(struct platform_device *pdev);
+static int tegra_uart_suspend(struct platform_device *pdev, pm_message_t state);
+static int tegra_uart_resume(struct platform_device *pdev);
+
+static struct platform_driver tegra_uart_platform_driver = {
+ .remove = tegra_uart_remove,
+ .probe = tegra_uart_probe,
+ .suspend = tegra_uart_suspend,
+ .resume = tegra_uart_resume,
+ .driver = {
+ .name = "tegra_uart"
+ }
+};
+
+static struct uart_driver tegra_uart_driver =
+{
+ .owner = THIS_MODULE,
+ .driver_name = "tegra_uart",
+ .dev_name = "ttyHS",
+ .cons = 0,
+ .nr = 5,
+};
+
+static int tegra_uart_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct tegra_uart_port *t = platform_get_drvdata(pdev);
+ struct uart_port *u;
+
+ if (pdev->id < 0 || pdev->id > tegra_uart_driver.nr)
+ pr_err("Invalid Uart instance (%d)\n", pdev->id);
+
+ dev_err(t->uport.dev, "tegra_uart_suspend called\n");
+ u = &t->uport;
+ uart_suspend_port(&tegra_uart_driver, u);
+ return 0;
+}
+
+static int tegra_uart_resume(struct platform_device *pdev)
+{
+ struct tegra_uart_port *t = platform_get_drvdata(pdev);
+ struct uart_port *u;
+
+ if (pdev->id < 0 || pdev->id > tegra_uart_driver.nr)
+ pr_err("Invalid Uart instance (%d)\n", pdev->id);
+
+ u = &t->uport;
+ dev_err(t->uport.dev, "tegra_uart_resume called\n");
+ uart_resume_port(&tegra_uart_driver, u);
+ return 0;
+}
+
+
+
+static int __devexit tegra_uart_remove(struct platform_device *pdev)
+{
+ struct tegra_uart_port *t = platform_get_drvdata(pdev);
+ struct uart_port *u;
+
+ if (pdev->id < 0 || pdev->id > tegra_uart_driver.nr)
+ pr_err("Invalid Uart instance (%d)\n", pdev->id);
+
+ u = &t->uport;
+ uart_remove_one_port(&tegra_uart_driver, u);
+
+ platform_set_drvdata(pdev, NULL);
+
+ pr_info("Unregistered UART port %s%d\n",
+ tegra_uart_driver.dev_name, u->line);
+ kfree(t);
+ return 0;
+}
+
+static int __init tegra_uart_probe(struct platform_device *pdev)
+{
+ struct tegra_uart_port *t;
+ struct plat_serial8250_port *pdata = pdev->dev.platform_data;
+ struct uart_port *u;
+ int ret;
+ char name[64];
+ if (pdev->id < 0 || pdev->id > tegra_uart_driver.nr) {
+ pr_err("Invalid Uart instance (%d)\n", pdev->id);
+ return -ENODEV;
+ }
+
+ t = kzalloc(sizeof(struct tegra_uart_port), GFP_KERNEL);
+ if (!t) {
+ pr_err("%s: Failed to allocate memory\n", __func__);
+ return -ENOMEM;
+ }
+ u = &t->uport;
+ u->dev = &pdev->dev;
+ platform_set_drvdata(pdev, u);
+ u->line = pdev->id;
+ u->ops = &tegra_uart_ops;
+ u->type = ~PORT_UNKNOWN;
+ u->mapbase = pdata->mapbase;
+ u->membase = pdata->membase;
+ u->irq = pdata->irq;
+ u->regshift = 2;
+
+ t->clk = clk_get(&pdev->dev, NULL);
+ if (!t->clk) {
+ dev_err(&pdev->dev, "Couldn't get the clock\n");
+ goto fail;
+ }
+
+ ret = uart_add_one_port(&tegra_uart_driver, u);
+ if (ret) {
+ pr_err("%s: Failed(%d) to add uart port %s%d\n",
+ __func__, ret, tegra_uart_driver.dev_name, u->line);
+ kfree(t);
+ platform_set_drvdata(pdev, NULL);
+ return ret;
+ }
+
+ snprintf(name, sizeof(name), "tegra_hsuart_%d", u->line);
+ pr_info("Registered UART port %s%d\n",
+ tegra_uart_driver.dev_name, u->line);
+
+ return ret;
+fail:
+ kfree(t);
+ return -ENODEV;
+}
+
+static int __init tegra_uart_init(void)
+{
+ int ret;
+
+ ret = uart_register_driver(&tegra_uart_driver);
+ if (unlikely(ret)) {
+ pr_err("Could not register %s driver\n",
+ tegra_uart_driver.driver_name);
+ return ret;
+ }
+
+ ret = platform_driver_register(&tegra_uart_platform_driver);
+ if (unlikely(ret)) {
+ pr_err("Could not register the UART platfrom "
+ "driver\n");
+ uart_unregister_driver(&tegra_uart_driver);
+ return ret;
+ }
+
+ pr_info("Initialized tegra uart driver\n");
+ return 0;
+}
+
+static void __exit tegra_uart_exit(void)
+{
+ pr_info("Unloading tegra uart driver\n");
+ platform_driver_unregister(&tegra_uart_platform_driver);
+ uart_unregister_driver(&tegra_uart_driver);
+}
+
+module_init(tegra_uart_init);
+module_exit(tegra_uart_exit);
+MODULE_DESCRIPTION("High speed UART driver for tegra chipset");