summaryrefslogtreecommitdiff
path: root/drivers/tty
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/8250.c28
-rw-r--r--drivers/tty/serial/8250.h1
-rw-r--r--drivers/tty/serial/Kconfig8
-rw-r--r--drivers/tty/serial/Makefile2
-rw-r--r--drivers/tty/serial/serial_core.c3
-rw-r--r--drivers/tty/serial/tegra_hsuart.c1686
6 files changed, 1725 insertions, 3 deletions
diff --git a/drivers/tty/serial/8250.c b/drivers/tty/serial/8250.c
index 7f50999eebc2..4358847af756 100644
--- a/drivers/tty/serial/8250.c
+++ b/drivers/tty/serial/8250.c
@@ -307,7 +307,8 @@ static const struct serial8250_config uart_config[] = {
.tx_loadsz = 8,
.fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 |
UART_FCR_T_TRIG_01,
- .flags = UART_CAP_FIFO | UART_CAP_RTOIE,
+ .flags = UART_CAP_FIFO | UART_CAP_RTOIE |
+ UART_CAP_HW_CTSRTS,
},
};
@@ -1902,8 +1903,13 @@ static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
container_of(port, struct uart_8250_port, port);
unsigned char mcr = 0;
- if (mctrl & TIOCM_RTS)
- mcr |= UART_MCR_RTS;
+ if (up->port.type == PORT_TEGRA) {
+ if (mctrl & TIOCM_RTS)
+ mcr |= UART_MCR_HW_RTS;
+ } else {
+ if (mctrl & TIOCM_RTS)
+ mcr |= UART_MCR_RTS;
+ }
if (mctrl & TIOCM_DTR)
mcr |= UART_MCR_DTR;
if (mctrl & TIOCM_OUT1)
@@ -2461,6 +2467,19 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
serial_outp(up, UART_EFR, efr);
}
+ if (up->capabilities & UART_CAP_HW_CTSRTS) {
+ unsigned char mcr = serial_inp(up, UART_MCR);
+ /*
+ * TEGRA UART core support the auto control of the RTS and CTS
+ * flow control.
+ */
+ if (termios->c_cflag & CRTSCTS)
+ mcr |= UART_MCR_HW_CTS;
+ else
+ mcr &= ~UART_MCR_HW_CTS;
+ serial_outp(up, UART_MCR, mcr);
+ }
+
#ifdef CONFIG_ARCH_OMAP
/* Workaround to enable 115200 baud on OMAP1510 internal ports */
if (cpu_is_omap1510() && is_omap_port(up)) {
@@ -2716,6 +2735,9 @@ static void serial8250_config_port(struct uart_port *port, int flags)
if (up->port.type == PORT_16550A && up->port.iotype == UPIO_AU)
up->bugs |= UART_BUG_NOMSR;
+ if (up->port.type == PORT_TEGRA)
+ up->bugs |= UART_BUG_NOMSR;
+
if (up->port.type != PORT_UNKNOWN && flags & UART_CONFIG_IRQ)
autoconfig_irq(up);
diff --git a/drivers/tty/serial/8250.h b/drivers/tty/serial/8250.h
index 6edf4a6a22d4..66a0c936b937 100644
--- a/drivers/tty/serial/8250.h
+++ b/drivers/tty/serial/8250.h
@@ -43,6 +43,7 @@ struct serial8250_config {
#define UART_CAP_AFE (1 << 11) /* MCR-based hw flow control */
#define UART_CAP_UUE (1 << 12) /* UART needs IER bit 6 set (Xscale) */
#define UART_CAP_RTOIE (1 << 13) /* UART needs IER bit 4 set (Xscale, Tegra) */
+#define UART_CAP_HW_CTSRTS (1 << 14) /* UART core support hw control of RTS and CTS */
#define UART_BUG_QUOT (1 << 0) /* UART has buggy quot LSB */
#define UART_BUG_TXEN (1 << 1) /* UART has buggy TX IIR status */
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index abf6fa49e07f..5c9d9890c9a5 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -528,6 +528,14 @@ config SERIAL_S5PV210
help
Serial port support for Samsung's S5P Family of SoC's
+config SERIAL_TEGRA
+ boolean "High speed serial support for NVIDIA Tegra SoCs"
+ depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA
+ select SERIAL_CORE
+ help
+ Support for the on-chip UARTs on NVIDIA Tegra SoC, providing
+ /dev/ttyHSx, where x is determined by the number of UARTs on the
+ platform
config SERIAL_MAX3100
tristate "MAX3100 support"
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index 83b4da6a1062..438af89db55e 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -1,6 +1,7 @@
#
# Makefile for the kernel serial device drivers.
#
+GCOV_PROFILE_tegra_hsuart.o := y
obj-$(CONFIG_SERIAL_CORE) += serial_core.o
obj-$(CONFIG_SERIAL_21285) += 21285.o
@@ -43,6 +44,7 @@ obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o
obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o
obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o
obj-$(CONFIG_SERIAL_S5PV210) += s5pv210.o
+obj-$(CONFIG_SERIAL_TEGRA) += tegra_hsuart.o
obj-$(CONFIG_SERIAL_MAX3100) += max3100.o
obj-$(CONFIG_SERIAL_MAX3107) += max3107.o
obj-$(CONFIG_SERIAL_MAX3107_AAVA) += max3107-aava.o
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 25f3094ec743..23b1f46c9663 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -91,6 +91,9 @@ static void __uart_start(struct tty_struct *tty)
struct uart_state *state = tty->driver_data;
struct uart_port *port = state->uart_port;
+ if (port->ops->wake_peer)
+ port->ops->wake_peer(port);
+
if (!uart_circ_empty(&state->xmit) && state->xmit.buf &&
!tty->stopped && !tty->hw_stopped)
port->ops->start_tx(port);
diff --git a/drivers/tty/serial/tegra_hsuart.c b/drivers/tty/serial/tegra_hsuart.c
new file mode 100644
index 000000000000..fecc3cf46f4d
--- /dev/null
+++ b/drivers/tty/serial/tegra_hsuart.c
@@ -0,0 +1,1686 @@
+/*
+ * drivers/serial/tegra_hsuart.c
+ *
+ * High-speed serial driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (C) 2009-2011 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 <linux/workqueue.h>
+#include <linux/tegra_uart.h>
+
+#include <mach/dma.h>
+#include <mach/clk.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 (2048*8)
+
+#define UART_LSR_FIFOE 0x80
+#define UART_LSR_TXFIFO_FULL 0x100
+#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 8
+
+#define TEGRA_UART_CLOSED 0
+#define TEGRA_UART_OPENED 1
+#define TEGRA_UART_CLOCK_OFF 2
+#define TEGRA_UART_SUSPEND 3
+
+/* Tx fifo trigger level setting in tegra uart is in
+ * reverse way then conventional uart */
+#define TEGRA_UART_TX_TRIG_16B 0x00
+#define TEGRA_UART_TX_TRIG_8B 0x10
+#define TEGRA_UART_TX_TRIG_4B 0x20
+#define TEGRA_UART_TX_TRIG_1B 0x30
+
+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;
+
+ /* TX DMA */
+ struct tegra_dma_req tx_dma_req;
+ struct tegra_dma_channel *tx_dma;
+
+ /* RX DMA */
+ struct tegra_dma_req rx_dma_req;
+ struct tegra_dma_channel *rx_dma;
+
+ bool use_rx_dma;
+ bool use_tx_dma;
+ int uart_state;
+ bool rx_timeout;
+ int rx_in_progress;
+};
+
+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 u32 uart_readl(struct tegra_uart_port *t, unsigned long reg)
+{
+ u32 val = readl(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 void 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);
+ writeb(val, t->uport.membase + (reg << t->uport.regshift));
+}
+
+static inline void 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);
+ 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 do_handle_rx_dma(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;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ unsigned long lsr;
+#endif
+
+ for (i = 0; i < max_bytes; i++) {
+ BUG_ON(uart_circ_empty(xmit));
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ lsr = uart_readl(t, UART_LSR);
+ if ((lsr & UART_LSR_TXFIFO_FULL))
+ break;
+#endif
+ 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 |= TEGRA_UART_TX_TRIG_8B;
+ 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 long bytes)
+{
+ struct circ_buf *xmit;
+ xmit = &t->uport.state->xmit;
+
+ 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 |= TEGRA_UART_TX_TRIG_4B;
+ uart_writeb(t, t->fcr_shadow, UART_FCR);
+
+ t->tx_bytes = bytes & ~(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 with u->lock taken */
+static void tegra_start_next_tx(struct tegra_uart_port *t)
+{
+ unsigned long tail;
+ unsigned long count;
+
+ struct circ_buf *xmit;
+
+ xmit = &t->uport.state->xmit;
+ tail = (unsigned long)&xmit->buf[xmit->tail];
+ count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE);
+
+
+ dev_vdbg(t->uport.dev, "+%s %lu %d\n", __func__, count,
+ t->tx_in_progress);
+
+ if (count == 0)
+ goto out;
+
+ if (!t->use_tx_dma || count < TEGRA_UART_MIN_DMA)
+ tegra_start_pio_tx(t, count);
+ else if (BYTES_TO_ALIGN(tail) > 0)
+ tegra_start_pio_tx(t, BYTES_TO_ALIGN(tail));
+ else
+ tegra_start_dma_tx(t, count);
+
+out:
+ dev_vdbg(t->uport.dev, "-%s", __func__);
+}
+
+/* Called by serial core driver with u->lock taken. */
+static void tegra_start_tx(struct uart_port *u)
+{
+ struct tegra_uart_port *t;
+ struct circ_buf *xmit;
+
+ t = container_of(u, struct tegra_uart_port, uport);
+ xmit = &u->state->xmit;
+
+ if (!uart_circ_empty(xmit) && !t->tx_in_progress)
+ tegra_start_next_tx(t);
+}
+
+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)
+{
+ struct tegra_uart_port *t = req->dev;
+ struct uart_port *u = &t->uport;
+ unsigned long flags;
+
+ spin_lock_irqsave(&u->lock, flags);
+
+ do_handle_rx_dma(t);
+
+ 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)
+{
+ struct tegra_uart_port *t = req->dev;
+ struct uart_port *u = &t->uport;
+ struct tty_struct *tty = u->state->port.tty;
+ int copied;
+
+ /* If we are here, DMA is stopped */
+
+ dev_dbg(t->uport.dev, "%s: %d %d\n", __func__, req->bytes_transferred,
+ req->status);
+ if (req->bytes_transferred) {
+ t->uport.icount.rx += req->bytes_transferred;
+ copied = tty_insert_flip_string(tty,
+ ((unsigned char *)(req->virt_addr)),
+ req->bytes_transferred);
+ if (copied != req->bytes_transferred) {
+ WARN_ON(1);
+ dev_err(t->uport.dev, "Not able to copy uart data "
+ "to tty layer Req %d and coped %d\n",
+ req->bytes_transferred, copied);
+ }
+ }
+
+ do_handle_rx_pio(t);
+
+ /* Push the read data later in caller place. */
+ if (req->status == -TEGRA_DMA_REQ_ERROR_ABORTED)
+ return;
+
+ spin_unlock(&u->lock);
+ tty_flip_buffer_push(u->state->port.tty);
+ spin_lock(&u->lock);
+}
+
+/* Lock already taken */
+static void do_handle_rx_dma(struct tegra_uart_port *t)
+{
+ struct uart_port *u = &t->uport;
+ if (t->rts_active)
+ set_rts(t, false);
+ tegra_dma_dequeue(t->rx_dma);
+ tty_flip_buffer_push(u->state->port.tty);
+ /* enqueue the request again */
+ tegra_start_dma_rx(t);
+ if (t->rts_active)
+ set_rts(t, true);
+}
+
+/* Wait for a symbol-time. */
+static void wait_sym_time(struct tegra_uart_port *t, unsigned int syms)
+{
+
+ /* Definitely have a start bit. */
+ unsigned int bits = 1;
+ switch (t->lcr_shadow & 3) {
+ case UART_LCR_WLEN5:
+ bits += 5;
+ break;
+ case UART_LCR_WLEN6:
+ bits += 6;
+ break;
+ case UART_LCR_WLEN7:
+ bits += 7;
+ break;
+ default:
+ bits += 8;
+ break;
+ }
+
+ /* Technically 5 bits gets 1.5 bits of stop... */
+ if (t->lcr_shadow & UART_LCR_STOP) {
+ bits += 2;
+ } else {
+ bits++;
+ }
+
+ if (t->lcr_shadow & UART_LCR_PARITY)
+ bits++;
+
+ if (likely(t->baud))
+ udelay(DIV_ROUND_UP(syms * bits * 1000000, t->baud));
+}
+
+/* Flush desired FIFO. */
+static void tegra_fifo_reset(struct tegra_uart_port *t, u8 fcr_bits)
+{
+ unsigned char fcr = t->fcr_shadow;
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ uart_writeb(t, fcr, UART_FCR);
+#else
+ /*Hw issue: Resetting tx fifo with non-fifo
+ mode to avoid any extra character to be sent*/
+ fcr &= ~UART_FCR_ENABLE_FIFO;
+ uart_writeb(t, fcr, UART_FCR);
+ udelay(60);
+ fcr |= fcr_bits & (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+ uart_writeb(t, fcr, UART_FCR);
+ fcr |= UART_FCR_ENABLE_FIFO;
+ uart_writeb(t, fcr, UART_FCR);
+#endif
+ uart_readb(t, UART_SCR); /* Dummy read to ensure the write is posted */
+ wait_sym_time(t, 1); /* Wait for the flush to propagate. */
+}
+
+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))
+ tegra_fifo_reset(t, UART_FCR_CLEAR_RCVR);
+ }
+ }
+ 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_next_tx(t);
+ return;
+}
+
+static void tegra_tx_dma_complete_callback(struct tegra_dma_req *req)
+{
+ struct tegra_uart_port *t = req->dev;
+ struct circ_buf *xmit = &t->uport.state->xmit;
+ int count = req->bytes_transferred;
+ unsigned long flags;
+
+ dev_vdbg(t->uport.dev, "%s: %d\n", __func__, count);
+
+ /* Update xmit pointers without lock if dma aborted. */
+ if (req->status == -TEGRA_DMA_REQ_ERROR_ABORTED) {
+ xmit->tail = (xmit->tail + count) & (UART_XMIT_SIZE - 1);
+ t->tx_in_progress = 0;
+ return;
+ }
+
+ 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_next_tx(t);
+
+ 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;
+ bool is_rx_int = false;
+ 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) {
+ if (likely(t->use_rx_dma) && is_rx_int) {
+ do_handle_rx_dma(t);
+
+ if (t->rx_in_progress) {
+ ier = t->ier_shadow;
+ ier |= (UART_IER_RLSI | UART_IER_RTOIE | UART_IER_EORD);
+ t->ier_shadow = ier;
+ uart_writeb(t, ier, UART_IER);
+ }
+ }
+ 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 */
+ case 6: /* Rx timeout */
+ case 2: /* Receive */
+ if (likely(t->use_rx_dma)) {
+ if (!is_rx_int) {
+ is_rx_int = true;
+ /* Disable interrups */
+ ier = t->ier_shadow;
+ ier |= UART_IER_RDI;
+ uart_writeb(t, ier, UART_IER);
+ ier &= ~(UART_IER_RDI | UART_IER_RLSI | UART_IER_RTOIE | UART_IER_EORD);
+ t->ier_shadow = ier;
+ uart_writeb(t, ier, UART_IER);
+ }
+ } 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;
+ unsigned char ier;
+
+ t = container_of(u, struct tegra_uart_port, uport);
+
+ if (t->rts_active)
+ set_rts(t, false);
+
+ if (t->rx_in_progress) {
+ wait_sym_time(t, 1); /* wait a character interval */
+
+ ier = t->ier_shadow;
+ ier &= ~(UART_IER_RDI | UART_IER_RLSI | UART_IER_RTOIE | UART_IER_EORD);
+ t->ier_shadow = ier;
+ uart_writeb(t, ier, UART_IER);
+ t->rx_in_progress = 0;
+
+ if (t->use_rx_dma && t->rx_dma)
+ tegra_dma_dequeue(t->rx_dma);
+ else
+ do_handle_rx_pio(t);
+
+ tty_flip_buffer_push(u->state->port.tty);
+ }
+
+ return;
+}
+
+static void tegra_uart_hw_deinit(struct tegra_uart_port *t)
+{
+ unsigned long flags;
+ int retry = 0;
+ unsigned long char_time = DIV_ROUND_UP(10000000, t->baud);
+ unsigned long fifo_empty_time = t->uport.fifosize * char_time;
+ unsigned long wait_time;
+ unsigned char lsr;
+ unsigned char msr;
+ unsigned char mcr;
+
+ /* Disable interrupts */
+ uart_writeb(t, 0, UART_IER);
+
+ lsr = uart_readb(t, UART_LSR);
+ if ((lsr & UART_LSR_TEMT) != UART_LSR_TEMT) {
+ msr = uart_readb(t, UART_MSR);
+ mcr = uart_readb(t, UART_MCR);
+ if ((mcr & UART_MCR_CTS_EN) && (msr & UART_MSR_CTS))
+ dev_err(t->uport.dev, "%s: Tx fifo not empty and "
+ "slave disabled CTS, Waiting for slave to"
+ " be ready\n", __func__);
+
+ /* Wait for Tx fifo to be empty */
+ while ((lsr & UART_LSR_TEMT) != UART_LSR_TEMT) {
+ wait_time = min(fifo_empty_time, 100);
+ udelay(wait_time);
+ fifo_empty_time -= wait_time;
+ if (!fifo_empty_time) {
+ msr = uart_readb(t, UART_MSR);
+ mcr = uart_readb(t, UART_MCR);
+ if ((mcr & UART_MCR_CTS_EN) &&
+ (msr & UART_MSR_CTS))
+ dev_err(t->uport.dev, "%s: Slave is "
+ "still not ready!\n", __func__);
+ break;
+ }
+ lsr = uart_readb(t, UART_LSR);
+ }
+ }
+
+ spin_lock_irqsave(&t->uport.lock, flags);
+
+ /* Reset the Rx and Tx FIFOs */
+ tegra_fifo_reset(t, UART_FCR_CLEAR_XMIT | UART_FCR_CLEAR_RCVR);
+
+ t->baud = 0;
+ t->uart_state = TEGRA_UART_CLOSED;
+
+ spin_unlock_irqrestore(&t->uport.lock, flags);
+
+ clk_disable(t->clk);
+}
+
+static void tegra_uart_free_rx_dma_buffer(struct tegra_uart_port *t)
+{
+ if (likely(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->rx_dma_req.dest_addr = 0;
+ t->rx_dma_req.virt_addr = NULL;
+}
+
+static void tegra_uart_free_rx_dma(struct tegra_uart_port *t)
+{
+ if (!t->use_rx_dma)
+ return;
+
+ tegra_dma_free_channel(t->rx_dma);
+ t->rx_dma = NULL;
+ t->use_rx_dma = false;
+}
+
+static int tegra_uart_hw_init(struct tegra_uart_port *t)
+{
+ unsigned char ier;
+
+ dev_vdbg(t->uport.dev, "+tegra_uart_hw_init\n");
+
+ t->fcr_shadow = 0;
+ t->mcr_shadow = 0;
+ t->lcr_shadow = 0;
+ t->ier_shadow = 0;
+ t->baud = 0;
+
+ clk_enable(t->clk);
+
+ /* Reset the UART controller to clear all previous status.*/
+ tegra_periph_reset_assert(t->clk);
+ udelay(100);
+ tegra_periph_reset_deassert(t->clk);
+ udelay(100);
+
+ t->rx_in_progress = 0;
+
+ /* 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_ENABLE_FIFO;
+ t->fcr_shadow |= UART_FCR_R_TRIG_01;
+ t->fcr_shadow |= TEGRA_UART_TX_TRIG_8B;
+ uart_writeb(t, t->fcr_shadow, UART_FCR);
+
+ if (t->use_rx_dma) {
+ /* initialize the UART for a simple default configuration
+ * so that the receive DMA buffer may be enqueued */
+ t->lcr_shadow = 3; /* no parity, stop, 8 data bits */
+ tegra_set_baudrate(t, 115200);
+ t->fcr_shadow |= UART_FCR_DMA_SELECT;
+ uart_writeb(t, t->fcr_shadow, UART_FCR);
+ if (tegra_start_dma_rx(t)) {
+ dev_err(t->uport.dev, "Rx DMA enqueue failed\n");
+ tegra_uart_free_rx_dma(t);
+ t->fcr_shadow &= ~UART_FCR_DMA_SELECT;
+ uart_writeb(t, t->fcr_shadow, UART_FCR);
+ }
+ }
+ else
+ uart_writeb(t, t->fcr_shadow, UART_FCR);
+
+ t->rx_in_progress = 1;
+
+ /*
+ * 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);
+
+ t->uart_state = TEGRA_UART_OPENED;
+ dev_vdbg(t->uport.dev, "-tegra_uart_hw_init\n");
+ return 0;
+}
+
+static int tegra_uart_init_rx_dma_buffer(struct tegra_uart_port *t)
+{
+ dma_addr_t rx_dma_phys;
+ void *rx_dma_virt;
+
+ 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");
+ return -ENOMEM;
+ }
+ 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;
+}
+
+static int tegra_uart_init_rx_dma(struct tegra_uart_port *t)
+{
+ dma_addr_t rx_dma_phys;
+
+ t->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_CONTINUOUS,
+ "uart_rx_%d", t->uport.line);
+ if (!t->rx_dma) {
+ dev_err(t->uport.dev, "%s: failed to allocate RX DMA.\n",
+ __func__);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int tegra_startup(struct uart_port *u)
+{
+ struct tegra_uart_port *t = container_of(u,
+ struct tegra_uart_port, uport);
+ int ret = 0;
+ struct tegra_uart_platform_data *pdata;
+
+ 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,
+ "uart_tx_%d", u->line);
+ if (t->tx_dma)
+ t->use_tx_dma = true;
+ else
+ pr_err("%s: failed to allocate TX DMA.\n", __func__);
+ }
+ 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 (!RX_FORCE_PIO && t->rx_dma_req.virt_addr) {
+ if (!tegra_uart_init_rx_dma(t))
+ t->use_rx_dma = true;
+ }
+
+ ret = tegra_uart_hw_init(t);
+ if (ret)
+ goto fail;
+
+ pdata = u->dev->platform_data;
+ if (pdata->is_loopback)
+ t->mcr_shadow |= UART_MCR_LOOP;
+ 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;
+
+ t = container_of(u, struct tegra_uart_port, uport);
+ dev_vdbg(u->dev, "+tegra_shutdown\n");
+
+ tegra_uart_hw_deinit(t);
+
+ t->rx_in_progress = 0;
+ t->tx_in_progress = 0;
+
+ tegra_uart_free_rx_dma(t);
+ if (t->use_tx_dma) {
+ tegra_dma_free_channel(t->tx_dma);
+ t->tx_dma = NULL;
+ t->use_tx_dma = false;
+ dma_unmap_single(t->uport.dev, t->xmit_dma_addr, UART_XMIT_SIZE,
+ DMA_TO_DEVICE);
+ t->xmit_dma_addr = 0;
+ }
+
+ free_irq(u->irq, t);
+ dev_vdbg(u->dev, "-tegra_shutdown\n");
+}
+
+static void tegra_wake_peer(struct uart_port *u)
+{
+ struct tegra_uart_platform_data *pdata = u->dev->platform_data;
+
+ if (pdata && pdata->wake_peer)
+ pdata->wake_peer(u);
+}
+
+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_EN;
+ else
+ mcr &= ~UART_MCR_RTS_EN;
+ 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;
+ unsigned char lsr;
+
+ 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) {
+ lsr = uart_readb(t, UART_LSR);
+ if ((lsr & TX_EMPTY_STATUS) == TX_EMPTY_STATUS)
+ 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)
+{
+}
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+static int clk_div71_get_divider(unsigned long parent_rate,
+ unsigned long rate)
+{
+ s64 divider_u71 = parent_rate;
+ if (!rate)
+ return -EINVAL;
+
+ divider_u71 *= 2;
+ divider_u71 += rate - 1;
+ do_div(divider_u71, rate);
+
+ if ((divider_u71 - 2) < 0)
+ return 0;
+
+ if ((divider_u71 - 2) > 255)
+ return -EINVAL;
+
+ return divider_u71 - 2;
+}
+#endif
+
+static int clk_div16_get_divider(unsigned long parent_rate, unsigned long rate)
+{
+ s64 divider_u16;
+
+ divider_u16 = parent_rate;
+ if (!rate)
+ return -EINVAL;
+ divider_u16 += rate - 1;
+ do_div(divider_u16, rate);
+
+ if (divider_u16 > 0xFFFF)
+ return -EINVAL;
+
+ return divider_u16;
+}
+
+static unsigned long find_best_clock_source(struct tegra_uart_port *t,
+ unsigned long rate)
+{
+ struct uart_port *u = &t->uport;
+ struct tegra_uart_platform_data *pdata;
+ int i;
+ int divider;
+ unsigned long parent_rate;
+ unsigned long new_rate;
+ unsigned long err_rate;
+ unsigned int fin_err = rate;
+ unsigned long fin_rate = rate;
+ int final_index = -1;
+ int count;
+ unsigned long error_2perc;
+
+ pdata = u->dev->platform_data;
+ if (!pdata || !pdata->parent_clk_count)
+ return fin_rate;
+
+ error_2perc = (rate / 50);
+
+ for (count = 0; count < pdata->parent_clk_count; ++count) {
+ parent_rate = pdata->parent_clk_list[count].fixed_clk_rate;
+
+ if (parent_rate < rate)
+ continue;
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ divider = clk_div71_get_divider(parent_rate, rate);
+
+ /* Get the best divider around calculated value */
+ if (divider > 2) {
+ for (i = divider - 2; i < (divider + 2); ++i) {
+ new_rate = ((parent_rate << 1) + i + 1) /
+ (i + 2);
+ err_rate = abs(new_rate - rate);
+ if (err_rate < fin_err) {
+ final_index = count;
+ fin_err = err_rate;
+ fin_rate = new_rate;
+ if (fin_err < error_2perc)
+ break;
+ }
+ }
+ if (fin_err < error_2perc)
+ break;
+ }
+#endif
+ /* Get the divisor by uart controller dll/dlm */
+ divider = clk_div16_get_divider(parent_rate, rate);
+
+ /* Get the best divider around calculated value */
+ if (divider > 2) {
+ for (i = divider - 2; i < (divider + 2); ++i) {
+ new_rate = parent_rate/i;
+ err_rate = abs(new_rate - rate);
+ if (err_rate < fin_err) {
+ final_index = count;
+ fin_err = err_rate;
+ fin_rate = parent_rate;
+ if (fin_err < error_2perc)
+ break;
+ }
+ }
+ if (fin_err < error_2perc)
+ break;
+ }
+ }
+
+ if (final_index >= 0) {
+ dev_info(t->uport.dev, "Setting clk_src %s\n",
+ pdata->parent_clk_list[final_index].name);
+ clk_set_parent(t->clk,
+ pdata->parent_clk_list[final_index].parent_clk);
+ }
+ return fin_rate;
+}
+
+#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;
+ unsigned int baud_actual;
+ unsigned int baud_delta;
+ unsigned long best_rate;
+
+ if (t->baud == baud)
+ return;
+
+ rate = baud * 16;
+ best_rate = find_best_clock_source(t, rate);
+ clk_set_rate(t->clk, best_rate);
+
+ rate = clk_get_rate(t->clk);
+
+ divisor = rate;
+ do_div(divisor, 16);
+ divisor += baud/2;
+ do_div(divisor, baud);
+
+ /* The allowable baudrate error from desired baudrate is 5% */
+ baud_actual = divisor ? rate / (16 * divisor) : 0;
+ baud_delta = abs(baud_actual - baud);
+ if (WARN_ON(baud_delta * 20 > baud)) {
+ dev_err(t->uport.dev, "requested baud %u, actual %u\n",
+ baud, baud_actual);
+ }
+
+ 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);
+ uart_readb(t, UART_SCR); /* Dummy read to ensure the write is posted */
+
+ t->baud = baud;
+ wait_sym_time(t, 2); /* wait two character intervals at new rate */
+ 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);
+
+ /* Changing configuration, it is safe to stop any rx now */
+ if (t->rts_active)
+ set_rts(t, false);
+
+ /* 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;
+
+ /* Baud rate. */
+ baud = uart_get_baud_rate(u, termios, oldtermios, 200, 4000000);
+ spin_unlock_irqrestore(&u->lock, flags);
+ tegra_set_baudrate(t, baud);
+ spin_lock_irqsave(&u->lock, flags);
+
+ /* 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;
+ /* if top layer has asked to set rts active then do so here */
+ if (t->rts_active)
+ set_rts(t, 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;
+ }
+
+ /* update the port timeout based on new settings */
+ uart_update_timeout(u, termios->c_cflag, baud);
+
+ spin_unlock_irqrestore(&u->lock, flags);
+ dev_vdbg(t->uport.dev, "-tegra_set_termios\n");
+ return;
+}
+
+/*
+ * Flush any TX data submitted for DMA and PIO. 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, "%s called", __func__);
+
+ t = container_of(u, struct tegra_uart_port, uport);
+
+ t->tx_bytes = 0;
+
+ 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,
+ .wake_peer = tegra_wake_peer,
+ .set_termios = tegra_set_termios,
+ .pm = tegra_pm,
+ .type = tegra_type,
+ .request_port = tegra_request_port,
+ .release_port = tegra_release_port,
+};
+
+static int 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);
+
+ u = &t->uport;
+ dev_dbg(t->uport.dev, "tegra_uart_suspend called\n");
+
+ /* enable clock before calling suspend so that controller
+ register can be accessible */
+ if (t->uart_state == TEGRA_UART_CLOCK_OFF) {
+ clk_enable(t->clk);
+ t->uart_state = TEGRA_UART_OPENED;
+ }
+
+ uart_suspend_port(&tegra_uart_driver, u);
+ t->uart_state = TEGRA_UART_SUSPEND;
+
+ 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_dbg(t->uport.dev, "tegra_uart_resume called\n");
+
+ if (t->uart_state == TEGRA_UART_SUSPEND) {
+ 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);
+
+ tegra_uart_free_rx_dma_buffer(t);
+
+ 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 tegra_uart_probe(struct platform_device *pdev)
+{
+ struct tegra_uart_port *t;
+ struct uart_port *u;
+ struct resource *resource;
+ 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->fifosize = 32;
+
+ resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (unlikely(!resource)) {
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ u->mapbase = resource->start;
+ u->membase = IO_ADDRESS(u->mapbase);
+ if (unlikely(!u->membase)) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ u->irq = platform_get_irq(pdev, 0);
+ if (unlikely(u->irq < 0)) {
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ u->regshift = 2;
+
+ t->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR_OR_NULL(t->clk)) {
+ dev_err(&pdev->dev, "Couldn't get the clock\n");
+ ret = -ENODEV;
+ 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);
+ goto fail;
+ }
+
+ snprintf(name, sizeof(name), "tegra_hsuart_%d", u->line);
+ pr_info("Registered UART port %s%d\n",
+ tegra_uart_driver.dev_name, u->line);
+ t->uart_state = TEGRA_UART_CLOSED;
+
+ if (!RX_FORCE_PIO) {
+ ret = tegra_uart_init_rx_dma_buffer(t);
+ if (ret < 0) {
+ pr_err("%s: Failed(%d) to allocate rx dma buffer "
+ "%s%d\n", __func__, ret,
+ tegra_uart_driver.dev_name, u->line);
+ goto rx_dma_buff_fail;
+ }
+ }
+ return ret;
+
+rx_dma_buff_fail:
+ uart_remove_one_port(&tegra_uart_driver, u);
+fail:
+ if (t->clk)
+ clk_put(t->clk);
+ platform_set_drvdata(pdev, NULL);
+ kfree(t);
+ return ret;
+}
+
+/* Switch off the clock of the uart controller. */
+void tegra_uart_request_clock_off(struct uart_port *uport)
+{
+ unsigned long flags;
+ struct tegra_uart_port *t;
+ bool is_clk_disable = false;
+
+ if (IS_ERR_OR_NULL(uport))
+ BUG();
+
+ dev_vdbg(uport->dev, "tegra_uart_request_clock_off");
+
+ t = container_of(uport, struct tegra_uart_port, uport);
+ spin_lock_irqsave(&uport->lock, flags);
+ if (t->uart_state == TEGRA_UART_OPENED) {
+ is_clk_disable = true;
+ t->uart_state = TEGRA_UART_CLOCK_OFF;
+ }
+ spin_unlock_irqrestore(&uport->lock, flags);
+
+ if (is_clk_disable)
+ clk_disable(t->clk);
+
+ return;
+}
+
+/* Switch on the clock of the uart controller */
+void tegra_uart_request_clock_on(struct uart_port *uport)
+{
+ unsigned long flags;
+ struct tegra_uart_port *t;
+ bool is_clk_enable = false;
+
+ if (IS_ERR_OR_NULL(uport))
+ BUG();
+
+ t = container_of(uport, struct tegra_uart_port, uport);
+ spin_lock_irqsave(&uport->lock, flags);
+ if (t->uart_state == TEGRA_UART_CLOCK_OFF) {
+ is_clk_enable = true;
+ t->uart_state = TEGRA_UART_OPENED;
+ }
+ spin_unlock_irqrestore(&uport->lock, flags);
+
+ if (is_clk_enable)
+ clk_enable(t->clk);
+
+ return;
+}
+
+/* Set the modem control signals state of uart controller. */
+void tegra_uart_set_mctrl(struct uart_port *uport, unsigned int mctrl)
+{
+ unsigned long flags;
+ struct tegra_uart_port *t;
+
+ t = container_of(uport, struct tegra_uart_port, uport);
+ if (t->uart_state != TEGRA_UART_OPENED) {
+ dev_err(t->uport.dev, "Uart is in invalid state\n");
+ return;
+ }
+
+ spin_lock_irqsave(&uport->lock, flags);
+ 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);
+ spin_unlock_irqrestore(&uport->lock, flags);
+ return;
+}
+
+/* Return the status of the transmit fifo whether empty or not.
+ * Return 0 if tx fifo is not empty.
+ * Return TIOCSER_TEMT if tx fifo is empty.
+ */
+int tegra_uart_is_tx_empty(struct uart_port *uport)
+{
+ return tegra_tx_empty(uport);
+}
+
+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");