summaryrefslogtreecommitdiff
path: root/drivers/serial/mxs-auart.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/serial/mxs-auart.c')
-rw-r--r--drivers/serial/mxs-auart.c1108
1 files changed, 1108 insertions, 0 deletions
diff --git a/drivers/serial/mxs-auart.c b/drivers/serial/mxs-auart.c
new file mode 100644
index 000000000000..be79f0e8bf11
--- /dev/null
+++ b/drivers/serial/mxs-auart.c
@@ -0,0 +1,1108 @@
+/*
+ * Freescale STMP37XX/STMP378X Application UART driver
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/cpufreq.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+
+#include <asm/cacheflush.h>
+
+#include <mach/hardware.h>
+#include <mach/device.h>
+#include <mach/dmaengine.h>
+
+#include "regs-uartapp.h"
+
+#define MXS_AUART_MAJOR 242
+#define MXS_AUART_RX_THRESHOLD 16
+
+static struct uart_driver auart_driver;
+
+struct mxs_auart_port {
+ struct uart_port port;
+
+ unsigned int flags;
+#define MXS_AUART_PORT_OPEN 0x80000000
+#define MXS_AUART_PORT_DMA_MODE 0x80000000
+ unsigned int ctrl;
+
+ unsigned int irq[3];
+
+ struct clk *clk;
+ struct device *dev;
+ unsigned int dma_rx_chan;
+ unsigned int dma_tx_chan;
+ unsigned int dma_rx_buffer_size;
+ struct list_head rx_done;
+ struct list_head free;
+ struct mxs_dma_desc *tx;
+ struct tasklet_struct rx_task;
+};
+
+static void mxs_auart_stop_tx(struct uart_port *u);
+static void mxs_auart_submit_tx(struct mxs_auart_port *s, int size);
+static void mxs_auart_submit_rx(struct mxs_auart_port *s);
+
+static inline struct mxs_auart_port *to_auart_port(struct uart_port *u)
+{
+ return container_of(u, struct mxs_auart_port, port);
+}
+
+static inline void mxs_auart_tx_chars(struct mxs_auart_port *s)
+{
+ struct circ_buf *xmit = &s->port.state->xmit;
+
+ if (s->flags & MXS_AUART_PORT_DMA_MODE) {
+ int i = 0, size;
+ char *buffer = s->tx->buffer;
+
+ if (mxs_dma_desc_pending(s->tx))
+ return;
+ while (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) {
+ if (i >= PAGE_SIZE)
+ break;
+ if (s->port.x_char) {
+ buffer[i++] = s->port.x_char;
+ s->port.x_char = 0;
+ continue;
+ }
+ size = min_t(u32, PAGE_SIZE - i,
+ CIRC_CNT_TO_END(xmit->head,
+ xmit->tail,
+ UART_XMIT_SIZE));
+ memcpy(buffer + i, xmit->buf + xmit->tail, size);
+ xmit->tail = (xmit->tail + size) & (UART_XMIT_SIZE - 1);
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&s->port);
+ i += size;
+ }
+ if (i)
+ mxs_auart_submit_tx(s, i);
+ else {
+ if (uart_tx_stopped(&s->port))
+ mxs_auart_stop_tx(&s->port);
+ }
+ return;
+ }
+
+ while (!(__raw_readl(s->port.membase + HW_UARTAPP_STAT) &
+ BM_UARTAPP_STAT_TXFF)) {
+ if (s->port.x_char) {
+ __raw_writel(s->port.x_char,
+ s->port.membase + HW_UARTAPP_DATA);
+ s->port.x_char = 0;
+ continue;
+ }
+ if (!uart_circ_empty(xmit) && !uart_tx_stopped(&s->port)) {
+ __raw_writel(xmit->buf[xmit->tail],
+ s->port.membase + HW_UARTAPP_DATA);
+ xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+ if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+ uart_write_wakeup(&s->port);
+ } else
+ break;
+ }
+ if (uart_circ_empty(&(s->port.state->xmit)))
+ __raw_writel(BM_UARTAPP_INTR_TXIEN,
+ s->port.membase + HW_UARTAPP_INTR_CLR);
+ else
+ __raw_writel(BM_UARTAPP_INTR_TXIEN,
+ s->port.membase + HW_UARTAPP_INTR_SET);
+
+ if (uart_tx_stopped(&s->port))
+ mxs_auart_stop_tx(&s->port);
+}
+
+static inline unsigned int
+mxs_auart_rx_char(struct mxs_auart_port *s, unsigned int stat, u8 c)
+{
+ int flag;
+
+ flag = TTY_NORMAL;
+ if (stat & BM_UARTAPP_STAT_BERR) {
+ stat &= ~BM_UARTAPP_STAT_BERR;
+ s->port.icount.brk++;
+ if (uart_handle_break(&s->port))
+ return stat;
+ flag = TTY_BREAK;
+ } else if (stat & BM_UARTAPP_STAT_PERR) {
+ stat &= ~BM_UARTAPP_STAT_PERR;
+ s->port.icount.parity++;
+ flag = TTY_PARITY;
+ } else if (stat & BM_UARTAPP_STAT_FERR) {
+ stat &= ~BM_UARTAPP_STAT_FERR;
+ s->port.icount.frame++;
+ flag = TTY_FRAME;
+ }
+
+ if (stat & BM_UARTAPP_STAT_OERR)
+ s->port.icount.overrun++;
+
+ if (uart_handle_sysrq_char(&s->port, c))
+ return stat;
+
+ uart_insert_char(&s->port, stat, BM_UARTAPP_STAT_OERR, c, flag);
+ return stat;
+}
+
+static void mxs_auart_rx_chars(struct mxs_auart_port *s)
+{
+ u8 c;
+ struct tty_struct *tty = s->port.state->port.tty;
+ u32 stat = 0;
+
+ if (s->flags & MXS_AUART_PORT_DMA_MODE) {
+ int i, count;
+ struct list_head *p, *q;
+ LIST_HEAD(list);
+ struct mxs_dma_desc *pdesc;
+ mxs_dma_cooked(s->dma_rx_chan, &list);
+ stat = __raw_readl(s->port.membase + HW_UARTAPP_STAT);
+ list_for_each_safe(p, q, &list) {
+ u8 *buffer;
+ list_del(p);
+ pdesc = list_entry(p, struct mxs_dma_desc, node);
+ count = stat & BM_UARTAPP_STAT_RXCOUNT;
+ buffer = pdesc->buffer;
+ for (i = 0; i < count; i++)
+ stat = mxs_auart_rx_char(s, stat, buffer[i]);
+ list_add(p, &s->free);
+ stat = __raw_readl(s->port.membase + HW_UARTAPP_STAT);
+ }
+ mxs_auart_submit_rx(s);
+ goto out;
+ }
+ for (;;) {
+ stat = __raw_readl(s->port.membase + HW_UARTAPP_STAT);
+ if (stat & BM_UARTAPP_STAT_RXFE)
+ break;
+ c = __raw_readl(s->port.membase + HW_UARTAPP_DATA);
+ stat = mxs_auart_rx_char(s, stat, c);
+ __raw_writel(stat, s->port.membase + HW_UARTAPP_STAT);
+ }
+out:
+ __raw_writel(stat, s->port.membase + HW_UARTAPP_STAT);
+ tty_flip_buffer_push(tty);
+}
+
+/* Allocate and initialize rx and tx DMA chains */
+static int mxs_auart_dma_init(struct mxs_auart_port *s)
+{
+ int ret, i;
+ struct list_head *p, *n;
+ struct mxs_dma_desc *pdesc;
+
+ ret = mxs_dma_request(s->dma_rx_chan, s->dev, dev_name(s->dev));
+ if (ret)
+ goto fail_get_dma_rx;
+ ret = mxs_dma_request(s->dma_tx_chan, s->dev, dev_name(s->dev));
+ if (ret)
+ goto fail_get_dma_tx;
+ ret = -ENOMEM;
+ INIT_LIST_HEAD(&s->rx_done);
+ INIT_LIST_HEAD(&s->free);
+ s->tx = NULL;
+
+
+ for (i = 0; i < 5; i++) {
+ pdesc = mxs_dma_alloc_desc();
+ if (pdesc == NULL || IS_ERR(pdesc))
+ goto fail_alloc_desc;
+
+ if (s->tx == NULL) {
+ pdesc->buffer = dma_alloc_coherent(s->dev, PAGE_SIZE,
+ &pdesc->cmd.address,
+ GFP_DMA);
+ if (pdesc->buffer == NULL)
+ goto fail_alloc_desc;
+ s->tx = pdesc;
+ } else {
+ pdesc->buffer = dma_alloc_coherent(s->dev,
+ s->dma_rx_buffer_size,
+ &pdesc->cmd.address,
+ GFP_DMA);
+ if (pdesc->buffer == NULL)
+ goto fail_alloc_desc;
+ list_add_tail(&pdesc->node, &s->free);
+ }
+ }
+ /*
+ Tell DMA to select UART.
+ Both DMA channels are shared between app UART and IrDA.
+ Target id of 0 means UART, 1 means IrDA
+ */
+ mxs_dma_set_target(s->dma_rx_chan, 0);
+ mxs_dma_set_target(s->dma_tx_chan, 0);
+
+ mxs_dma_enable_irq(s->dma_rx_chan, 1);
+ mxs_dma_enable_irq(s->dma_tx_chan, 1);
+
+ return 0;
+fail_alloc_desc:
+ if (s->tx) {
+ if (s->tx->buffer)
+ dma_free_coherent(s->dev,
+ PAGE_SIZE,
+ s->tx->buffer,
+ s->tx->cmd.address);
+ s->tx->buffer = NULL;
+ mxs_dma_free_desc(s->tx);
+ s->tx = NULL;
+ }
+ list_for_each_safe(p, n, &s->free) {
+ list_del(p);
+ pdesc = list_entry(p, struct mxs_dma_desc, node);
+ if (pdesc->buffer)
+ dma_free_coherent(s->dev,
+ s->dma_rx_buffer_size,
+ pdesc->buffer,
+ pdesc->cmd.address);
+ pdesc->buffer = NULL;
+ mxs_dma_free_desc(pdesc);
+ }
+ mxs_dma_release(s->dma_tx_chan, s->dev);
+fail_get_dma_tx:
+ mxs_dma_release(s->dma_rx_chan, s->dev);
+fail_get_dma_rx:
+ WARN_ON(ret);
+ return ret;
+}
+
+static void mxs_auart_dma_exit(struct mxs_auart_port *s)
+{
+ struct list_head *p, *n;
+ LIST_HEAD(list);
+ struct mxs_dma_desc *pdesc;
+
+ mxs_dma_enable_irq(s->dma_rx_chan, 0);
+ mxs_dma_enable_irq(s->dma_tx_chan, 0);
+
+ mxs_dma_disable(s->dma_tx_chan);
+ mxs_dma_disable(s->dma_rx_chan);
+
+ mxs_dma_get_cooked(s->dma_tx_chan, &list);
+ mxs_dma_get_cooked(s->dma_rx_chan, &s->free);
+
+ mxs_dma_release(s->dma_tx_chan, s->dev);
+ mxs_dma_release(s->dma_rx_chan, s->dev);
+
+ if (s->tx) {
+ if (s->tx->buffer)
+ dma_free_coherent(s->dev,
+ PAGE_SIZE,
+ s->tx->buffer,
+ s->tx->cmd.address);
+ s->tx->buffer = NULL;
+ mxs_dma_free_desc(s->tx);
+ s->tx = NULL;
+ }
+ list_for_each_safe(p, n, &s->free) {
+ list_del(p);
+ pdesc = list_entry(p, struct mxs_dma_desc, node);
+ if (pdesc->buffer)
+ dma_free_coherent(s->dev,
+ s->dma_rx_buffer_size,
+ pdesc->buffer,
+ pdesc->cmd.address);
+ pdesc->buffer = NULL;
+ mxs_dma_free_desc(pdesc);
+ }
+}
+
+static void mxs_auart_submit_rx(struct mxs_auart_port *s)
+{
+ int ret;
+ unsigned int pio_value;
+ struct list_head *p, *n;
+ struct mxs_dma_desc *pdesc;
+
+ pio_value = BM_UARTAPP_CTRL0_RXTO_ENABLE |
+ BF_UARTAPP_CTRL0_RXTIMEOUT(0x80) |
+ BF_UARTAPP_CTRL0_XFER_COUNT(s->dma_rx_buffer_size);
+
+ list_for_each_safe(p, n, &s->free) {
+ list_del(p);
+ pdesc = list_entry(p, struct mxs_dma_desc, node);
+ pdesc->cmd.cmd.bits.bytes = s->dma_rx_buffer_size;
+ pdesc->cmd.cmd.bits.terminate_flush = 1;
+ pdesc->cmd.cmd.bits.pio_words = 1;
+ pdesc->cmd.cmd.bits.wait4end = 1;
+ pdesc->cmd.cmd.bits.dec_sem = 1;
+ pdesc->cmd.cmd.bits.irq = 1;
+ pdesc->cmd.cmd.bits.chain = 1;
+ pdesc->cmd.cmd.bits.command = DMA_WRITE;
+ pdesc->cmd.pio_words[0] = pio_value;
+ ret = mxs_dma_desc_append(s->dma_rx_chan, pdesc);
+ if (ret)
+ pr_info("%s append dma desc, %d\n", __func__, ret);
+ }
+ ret = mxs_dma_enable(s->dma_rx_chan);
+ if (ret)
+ pr_info("%s enable dma desc, %d\n", __func__, ret);
+}
+
+static irqreturn_t mxs_auart_irq_dma_rx(int irq, void *context)
+{
+ struct mxs_auart_port *s = context;
+
+ mxs_dma_ack_irq(s->dma_rx_chan);
+ mxs_auart_rx_chars(s);
+ return IRQ_HANDLED;
+}
+
+static void mxs_auart_submit_tx(struct mxs_auart_port *s, int size)
+{
+ int ret;
+ struct mxs_dma_desc *d = s->tx;
+
+ d->cmd.pio_words[0] = BF_UARTAPP_CTRL1_XFER_COUNT(size);
+ d->cmd.cmd.bits.bytes = size;
+ d->cmd.cmd.bits.pio_words = 1;
+ d->cmd.cmd.bits.wait4end = 1;
+ d->cmd.cmd.bits.dec_sem = 1;
+ d->cmd.cmd.bits.irq = 1;
+ d->cmd.cmd.bits.command = DMA_READ;
+ ret = mxs_dma_desc_append(s->dma_tx_chan, s->tx);
+ if (ret)
+ pr_info("append dma desc, %d\n", ret);
+
+ ret = mxs_dma_enable(s->dma_tx_chan);
+ if (ret)
+ pr_info("enable dma desc, %d\n", ret);
+}
+
+static irqreturn_t mxs_auart_irq_dma_tx(int irq, void *context)
+{
+ struct mxs_auart_port *s = context;
+
+ LIST_HEAD(list);
+ mxs_dma_ack_irq(s->dma_tx_chan);
+ mxs_dma_cooked(s->dma_tx_chan, &list);
+ mxs_auart_tx_chars(s);
+ return IRQ_HANDLED;
+}
+
+static int mxs_auart_request_port(struct uart_port *u)
+{
+ struct mxs_auart_port *s = to_auart_port(u);
+
+ if (!request_mem_region((u32)u->mapbase, SZ_4K, dev_name(s->dev)))
+ return -EBUSY;
+ return 0;
+
+}
+
+static int mxs_auart_verify_port(struct uart_port *u,
+ struct serial_struct *ser)
+{
+ if (u->type != PORT_UNKNOWN && u->type != PORT_IMX)
+ return -EINVAL;
+ return 0;
+}
+
+static void mxs_auart_config_port(struct uart_port *u, int flags)
+{
+}
+
+static const char *mxs_auart_type(struct uart_port *u)
+{
+ struct mxs_auart_port *s = to_auart_port(u);
+
+ return dev_name(s->dev);
+}
+
+static void mxs_auart_release_port(struct uart_port *u)
+{
+ release_mem_region(u->mapbase, SZ_4K);
+}
+
+static void mxs_auart_set_mctrl(struct uart_port *u, unsigned mctrl)
+{
+ struct mxs_auart_port *s = to_auart_port(u);
+
+ u32 ctrl = __raw_readl(u->membase + HW_UARTAPP_CTRL2);
+
+ ctrl &= ~BM_UARTAPP_CTRL2_RTS;
+ if (mctrl & TIOCM_RTS)
+ ctrl |= BM_UARTAPP_CTRL2_RTS;
+ s->ctrl = mctrl;
+ __raw_writel(ctrl, u->membase + HW_UARTAPP_CTRL2);
+}
+
+static u32 mxs_auart_get_mctrl(struct uart_port *u)
+{
+ struct mxs_auart_port *s = to_auart_port(u);
+ u32 stat = __raw_readl(u->membase + HW_UARTAPP_STAT);
+ int ctrl2 = __raw_readl(u->membase + HW_UARTAPP_CTRL2);
+ u32 mctrl = s->ctrl;
+
+ mctrl &= ~TIOCM_CTS;
+ if (stat & BM_UARTAPP_STAT_CTS)
+ mctrl |= TIOCM_CTS;
+
+ if (ctrl2 & BM_UARTAPP_CTRL2_RTS)
+ mctrl |= TIOCM_RTS;
+
+ return mctrl;
+}
+
+static void mxs_auart_settermios(struct uart_port *u,
+ struct ktermios *termios,
+ struct ktermios *old)
+{
+ u32 bm, ctrl, ctrl2, div;
+ unsigned int cflag, baud;
+
+ if (termios == NULL) {
+ printk(KERN_ERR "Empty ktermios setting:!\n");
+ return;
+ }
+
+ cflag = termios->c_cflag;
+
+ ctrl = BM_UARTAPP_LINECTRL_FEN;
+ ctrl2 = __raw_readl(u->membase + HW_UARTAPP_CTRL2);
+
+ /* byte size */
+ switch (cflag & CSIZE) {
+ case CS5:
+ bm = 0;
+ break;
+ case CS6:
+ bm = 1;
+ break;
+ case CS7:
+ bm = 2;
+ break;
+ case CS8:
+ bm = 3;
+ break;
+ default:
+ return;
+ }
+
+ ctrl |= BF_UARTAPP_LINECTRL_WLEN(bm);
+
+ /* parity */
+ if (cflag & PARENB) {
+ ctrl |= BM_UARTAPP_LINECTRL_PEN;
+ if ((cflag & PARODD) == 0)
+ ctrl |= BM_UARTAPP_LINECTRL_EPS;
+ }
+
+ /* figure out the stop bits requested */
+ if (cflag & CSTOPB)
+ ctrl |= BM_UARTAPP_LINECTRL_STP2;
+
+ /* figure out the hardware flow control settings */
+ if (cflag & CRTSCTS)
+ ctrl2 |= BM_UARTAPP_CTRL2_CTSEN /* | BM_UARTAPP_CTRL2_RTSEN */ ;
+ else
+ ctrl2 &= ~BM_UARTAPP_CTRL2_CTSEN;
+
+ /* set baud rate */
+ baud = uart_get_baud_rate(u, termios, old, 0, u->uartclk);
+ div = u->uartclk * 32 / baud;
+ ctrl |= BF_UARTAPP_LINECTRL_BAUD_DIVFRAC(div & 0x3F);
+ ctrl |= BF_UARTAPP_LINECTRL_BAUD_DIVINT(div >> 6);
+
+ if ((cflag & CREAD) != 0)
+ ctrl2 |= BM_UARTAPP_CTRL2_RXE;
+
+ __raw_writel(ctrl, u->membase + HW_UARTAPP_LINECTRL);
+ __raw_writel(ctrl2, u->membase + HW_UARTAPP_CTRL2);
+}
+
+static irqreturn_t mxs_auart_irq_handle(int irq, void *context)
+{
+ u32 istatus, istat;
+ struct mxs_auart_port *s = context;
+ u32 stat = __raw_readl(s->port.membase + HW_UARTAPP_STAT);
+
+ istatus = istat = __raw_readl(s->port.membase + HW_UARTAPP_INTR);
+
+ if (istat & BM_UARTAPP_INTR_CTSMIS) {
+ uart_handle_cts_change(&s->port, stat & BM_UARTAPP_STAT_CTS);
+ __raw_writel(BM_UARTAPP_INTR_CTSMIS,
+ s->port.membase + HW_UARTAPP_INTR_CLR);
+ istat &= ~BM_UARTAPP_INTR_CTSMIS;
+ }
+ if (istat & (BM_UARTAPP_INTR_RTIS | BM_UARTAPP_INTR_RXIS)) {
+ mxs_auart_rx_chars(s);
+ istat &= ~(BM_UARTAPP_INTR_RTIS | BM_UARTAPP_INTR_RXIS);
+ }
+
+ if (istat & BM_UARTAPP_INTR_TXIS) {
+ mxs_auart_tx_chars(s);
+ istat &= ~BM_UARTAPP_INTR_TXIS;
+ }
+ /* modem status interrupt bits are undefined
+ after reset,and the hardware do not support
+ DSRMIS,DCDMIS and RIMIS bit,so we should ingore
+ them when they are pending. */
+ if (istat & (BM_UARTAPP_INTR_ABDIS
+ | BM_UARTAPP_INTR_OEIS
+ | BM_UARTAPP_INTR_BEIS
+ | BM_UARTAPP_INTR_PEIS
+ | BM_UARTAPP_INTR_FEIS
+ | BM_UARTAPP_INTR_RTIS
+ | BM_UARTAPP_INTR_TXIS
+ | BM_UARTAPP_INTR_RXIS
+ | BM_UARTAPP_INTR_CTSMIS)) {
+ dev_info(s->dev, "Unhandled status %x\n", istat);
+ }
+ __raw_writel(istatus & (BM_UARTAPP_INTR_ABDIS
+ | BM_UARTAPP_INTR_OEIS
+ | BM_UARTAPP_INTR_BEIS
+ | BM_UARTAPP_INTR_PEIS
+ | BM_UARTAPP_INTR_FEIS
+ | BM_UARTAPP_INTR_RTIS
+ | BM_UARTAPP_INTR_TXIS
+ | BM_UARTAPP_INTR_RXIS
+ | BM_UARTAPP_INTR_DSRMIS
+ | BM_UARTAPP_INTR_DCDMIS
+ | BM_UARTAPP_INTR_CTSMIS
+ | BM_UARTAPP_INTR_RIMIS),
+ s->port.membase + HW_UARTAPP_INTR_CLR);
+
+ return IRQ_HANDLED;
+}
+
+static int mxs_auart_free_irqs(struct mxs_auart_port *s)
+{
+ int irqn = 0;
+
+ for (irqn = 0; irqn < ARRAY_SIZE(s->irq); irqn++)
+ free_irq(s->irq[irqn], s);
+ return 0;
+}
+
+static int mxs_auart_request_irqs(struct mxs_auart_port *s)
+{
+ int err = 0;
+
+ /*
+ * order counts. resources should be listed in the same order
+ */
+ irq_handler_t handlers[] = {
+ mxs_auart_irq_handle,
+ mxs_auart_irq_dma_rx,
+ mxs_auart_irq_dma_tx,
+ };
+ char *handlers_names[] = {
+ "auart internal",
+ "auart dma rx",
+ "auart dma tx",
+ };
+ int irqn;
+
+ for (irqn = 0; irqn < ARRAY_SIZE(handlers); irqn++) {
+ err = request_irq(s->irq[irqn], handlers[irqn],
+ 0, handlers_names[irqn], s);
+ if (err)
+ goto out;
+ }
+ return 0;
+out:
+ mxs_auart_free_irqs(s);
+ return err;
+}
+
+static inline void mxs_auart_reset(struct uart_port *u)
+{
+ int i;
+ unsigned int reg;
+
+ __raw_writel(BM_UARTAPP_CTRL0_SFTRST,
+ u->membase + HW_UARTAPP_CTRL0_CLR);
+
+ for (i = 0; i < 10000; i++) {
+ reg = __raw_readl(u->membase + HW_UARTAPP_CTRL0);
+ if (!(reg & BM_UARTAPP_CTRL0_SFTRST))
+ break;
+ udelay(3);
+ }
+
+ __raw_writel(BM_UARTAPP_CTRL0_CLKGATE,
+ u->membase + HW_UARTAPP_CTRL0_CLR);
+}
+
+static int mxs_auart_startup(struct uart_port *u)
+{
+ struct mxs_auart_port *s = to_auart_port(u);
+
+ mxs_auart_reset(u);
+
+ __raw_writel(BM_UARTAPP_CTRL2_UARTEN,
+ s->port.membase + HW_UARTAPP_CTRL2_SET);
+
+ /* Enable the Application UART DMA bits. */
+ if (s->flags & MXS_AUART_PORT_DMA_MODE) {
+ int ret;
+ ret = mxs_auart_dma_init(s);
+ if (ret) {
+ __raw_writel(BM_UARTAPP_CTRL2_UARTEN,
+ s->port.membase + HW_UARTAPP_CTRL2_CLR);
+ return ret;
+ }
+ __raw_writel(BM_UARTAPP_CTRL2_TXDMAE | BM_UARTAPP_CTRL2_RXDMAE
+ | BM_UARTAPP_CTRL2_DMAONERR,
+ s->port.membase + HW_UARTAPP_CTRL2_SET);
+ /* clear any pending interrupts */
+ __raw_writel(0, s->port.membase + HW_UARTAPP_INTR);
+
+ /* reset all dma channels */
+ mxs_dma_reset(s->dma_tx_chan);
+ mxs_dma_reset(s->dma_rx_chan);
+ } else
+ __raw_writel(BM_UARTAPP_INTR_RXIEN | BM_UARTAPP_INTR_RTIEN,
+ s->port.membase + HW_UARTAPP_INTR);
+
+ __raw_writel(BM_UARTAPP_INTR_CTSMIEN,
+ s->port.membase + HW_UARTAPP_INTR_SET);
+
+ /*
+ * Enable fifo so all four bytes of a DMA word are written to
+ * output (otherwise, only the LSB is written, ie. 1 in 4 bytes)
+ */
+ __raw_writel(BM_UARTAPP_LINECTRL_FEN,
+ s->port.membase + HW_UARTAPP_LINECTRL_SET);
+
+ if (s->flags & MXS_AUART_PORT_DMA_MODE)
+ mxs_auart_submit_rx(s);
+ return mxs_auart_request_irqs(s);
+}
+
+static void mxs_auart_shutdown(struct uart_port *u)
+{
+ struct mxs_auart_port *s = to_auart_port(u);
+
+ __raw_writel(BM_UARTAPP_CTRL0_SFTRST,
+ s->port.membase + HW_UARTAPP_CTRL0_SET);
+
+ if (s->flags & MXS_AUART_PORT_DMA_MODE)
+ mxs_auart_dma_exit(s);
+ else
+ __raw_writel(BM_UARTAPP_INTR_RXIEN | BM_UARTAPP_INTR_RTIEN |
+ BM_UARTAPP_INTR_CTSMIEN,
+ s->port.membase + HW_UARTAPP_INTR_CLR);
+ mxs_auart_free_irqs(s);
+}
+
+static unsigned int mxs_auart_tx_empty(struct uart_port *u)
+{
+ struct mxs_auart_port *s = to_auart_port(u);
+
+ if (s->flags & MXS_AUART_PORT_DMA_MODE)
+ return mxs_dma_desc_pending(s->tx) ? 0 : TIOCSER_TEMT;
+
+ if (__raw_readl(u->membase + HW_UARTAPP_STAT) &
+ BM_UARTAPP_STAT_TXFE)
+ return TIOCSER_TEMT;
+ else
+ return 0;
+}
+
+static void mxs_auart_start_tx(struct uart_port *u)
+{
+ struct mxs_auart_port *s = to_auart_port(u);
+
+ /* enable transmitter */
+ __raw_writel(BM_UARTAPP_CTRL2_TXE, u->membase + HW_UARTAPP_CTRL2_SET);
+
+ mxs_auart_tx_chars(s);
+}
+
+static void mxs_auart_stop_tx(struct uart_port *u)
+{
+ __raw_writel(BM_UARTAPP_CTRL2_TXE, u->membase + HW_UARTAPP_CTRL2_CLR);
+}
+
+static void mxs_auart_stop_rx(struct uart_port *u)
+{
+ __raw_writel(BM_UARTAPP_CTRL2_RXE, u->membase + HW_UARTAPP_CTRL2_CLR);
+}
+
+static void mxs_auart_break_ctl(struct uart_port *u, int ctl)
+{
+ if (ctl)
+ __raw_writel(BM_UARTAPP_LINECTRL_BRK,
+ u->membase + HW_UARTAPP_LINECTRL_SET);
+ else
+ __raw_writel(BM_UARTAPP_LINECTRL_BRK,
+ u->membase + HW_UARTAPP_LINECTRL_CLR);
+}
+
+static void mxs_auart_enable_ms(struct uart_port *port)
+{
+ /* just empty */
+}
+
+static struct uart_ops mxs_auart_ops = {
+ .tx_empty = mxs_auart_tx_empty,
+ .start_tx = mxs_auart_start_tx,
+ .stop_tx = mxs_auart_stop_tx,
+ .stop_rx = mxs_auart_stop_rx,
+ .enable_ms = mxs_auart_enable_ms,
+ .break_ctl = mxs_auart_break_ctl,
+ .set_mctrl = mxs_auart_set_mctrl,
+ .get_mctrl = mxs_auart_get_mctrl,
+ .startup = mxs_auart_startup,
+ .shutdown = mxs_auart_shutdown,
+ .set_termios = mxs_auart_settermios,
+ .type = mxs_auart_type,
+ .release_port = mxs_auart_release_port,
+ .request_port = mxs_auart_request_port,
+ .config_port = mxs_auart_config_port,
+ .verify_port = mxs_auart_verify_port,
+};
+#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE
+static struct mxs_auart_port auart_port[CONFIG_MXS_AUART_PORTS] = {};
+
+static void
+auart_console_write(struct console *co, const char *s, unsigned int count)
+{
+ struct uart_port *port;
+ unsigned int status, old_cr;
+ int i;
+
+ if (co->index > CONFIG_MXS_AUART_PORTS || co->index < 0)
+ return;
+
+ port = &auart_port[co->index].port;
+
+ /* First save the CR then disable the interrupts */
+ old_cr = __raw_readl(port->membase + HW_UARTAPP_CTRL2);
+ __raw_writel(BM_UARTAPP_CTRL2_UARTEN | BM_UARTAPP_CTRL2_TXE,
+ port->membase + HW_UARTAPP_CTRL2_SET);
+
+ /* Now, do each character */
+ for (i = 0; i < count; i++) {
+ do {
+ status = __raw_readl(port->membase + HW_UARTAPP_STAT);
+ } while (status & BM_UARTAPP_STAT_TXFF);
+
+ __raw_writel(s[i], port->membase + HW_UARTAPP_DATA);
+ if (s[i] == '\n') {
+ do {
+ status = __raw_readl(port->membase +
+ HW_UARTAPP_STAT);
+ } while (status & BM_UARTAPP_STAT_TXFF);
+ __raw_writel('\r', port->membase + HW_UARTAPP_DATA);
+ }
+ }
+
+ /*
+ * Finally, wait for transmitter to become empty
+ * and restore the TCR
+ */
+ do {
+ status = __raw_readl(port->membase + HW_UARTAPP_STAT);
+ } while (status & BM_UARTAPP_STAT_BUSY);
+ __raw_writel(old_cr, port->membase + HW_UARTAPP_CTRL2);
+}
+
+static void __init
+auart_console_get_options(struct uart_port *port, int *baud,
+ int *parity, int *bits)
+{
+ if (__raw_readl(port->membase + HW_UARTAPP_CTRL2)
+ & BM_UARTAPP_CTRL2_UARTEN) {
+ unsigned int lcr_h, quot;
+ lcr_h = __raw_readl(port->membase + HW_UARTAPP_LINECTRL);
+
+ *parity = 'n';
+ if (lcr_h & BM_UARTAPP_LINECTRL_PEN) {
+ if (lcr_h & BM_UARTAPP_LINECTRL_EPS)
+ *parity = 'e';
+ else
+ *parity = 'o';
+ }
+
+ if ((lcr_h & BM_UARTAPP_LINECTRL_WLEN)
+ == BF_UARTAPP_LINECTRL_WLEN(2))
+ *bits = 7;
+ else
+ *bits = 8;
+
+ quot = (((__raw_readl(port->membase + HW_UARTAPP_LINECTRL)
+ & BM_UARTAPP_LINECTRL_BAUD_DIVINT))
+ >> (BP_UARTAPP_LINECTRL_BAUD_DIVINT - 6))
+ | (((__raw_readl(port->membase + HW_UARTAPP_LINECTRL)
+ & BM_UARTAPP_LINECTRL_BAUD_DIVFRAC))
+ >> BP_UARTAPP_LINECTRL_BAUD_DIVFRAC);
+ if (quot == 0)
+ quot = 1;
+ *baud = (port->uartclk << 2) / quot;
+ }
+}
+
+static int __init auart_console_setup(struct console *co, char *options)
+{
+ struct mxs_auart_port *port;
+ int baud = 115200;
+ int bits = 8;
+ int parity = 'n';
+ int flow = 'n';
+ /*
+ * Check whether an invalid uart number has been specified, and
+ * if so, search for the first available port that does have
+ * console support.
+ */
+ if (co->index > CONFIG_MXS_AUART_PORTS || co->index < 0)
+ return -EINVAL;
+
+ port = &auart_port[co->index].port;
+
+ if (port->port.membase == 0) {
+ if (cpu_is_mx23()) {
+ if (co->index == 1) {
+ port->port.membase = IO_ADDRESS(0x8006C000);
+ port->port.mapbase = 0x8006C000;
+ } else {
+ port->port.membase = IO_ADDRESS(0x8006E000);
+ port->port.mapbase = 0x8006E000;
+ }
+ }
+
+ port->port.fifosize = 16;
+ port->port.ops = &mxs_auart_ops;
+ port->port.flags = ASYNC_BOOT_AUTOCONF;
+ port->port.line = 0;
+ }
+ mxs_auart_reset(port);
+
+ __raw_writel(BM_UARTAPP_CTRL2_UARTEN,
+ port->port.membase + HW_UARTAPP_CTRL2_SET);
+
+ if (port->clk == NULL || IS_ERR(port->clk)) {
+ port->clk = clk_get(NULL, "uart");
+ if (port->clk == NULL || IS_ERR(port->clk))
+ return -ENODEV;
+ port->port.uartclk = clk_get_rate(port->clk);
+ }
+
+ if (options)
+ uart_parse_options(options, &baud, &parity, &bits, &flow);
+ else
+ auart_console_get_options(port, &baud, &parity, &bits);
+ return uart_set_options(port, co, baud, parity, bits, flow);
+}
+
+static struct console auart_console = {
+ .name = "ttySP",
+ .write = auart_console_write,
+ .device = uart_console_device,
+ .setup = auart_console_setup,
+ .flags = CON_PRINTBUFFER,
+ .index = -1,
+ .data = &auart_driver,
+};
+
+#ifdef CONFIG_MXS_EARLY_CONSOLE
+static int __init auart_console_init(void)
+{
+ register_console(&auart_console);
+ return 0;
+}
+
+console_initcall(auart_console_init);
+#endif
+
+#endif
+static struct uart_driver auart_driver = {
+ .owner = THIS_MODULE,
+ .driver_name = "auart",
+ .dev_name = "ttySP",
+ .major = MXS_AUART_MAJOR,
+ .minor = 0,
+ .nr = CONFIG_MXS_AUART_PORTS,
+#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE
+ .cons = &auart_console,
+#endif
+};
+
+static int __devinit mxs_auart_probe(struct platform_device *pdev)
+{
+ struct mxs_auart_plat_data *plat;
+ struct mxs_auart_port *s;
+ u32 version;
+ int i, ret = 0;
+ struct resource *r;
+
+ s = kzalloc(sizeof(struct mxs_auart_port), GFP_KERNEL);
+ if (!s) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ plat = pdev->dev.platform_data;
+ if (plat == NULL) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ if (plat && plat->clk)
+ s->clk = clk_get(NULL, plat->clk);
+ else
+ s->clk = clk_get(NULL, "uart");
+ if (IS_ERR(s->clk)) {
+ ret = PTR_ERR(s->clk);
+ goto out_free;
+ }
+
+ clk_enable(s->clk);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!r) {
+ ret = -ENXIO;
+ goto out_free_clk;
+ }
+ s->port.mapbase = r->start;
+ s->port.membase = (void __iomem *)IO_ADDRESS(r->start);
+ s->port.ops = &mxs_auart_ops;
+ s->port.iotype = UPIO_MEM;
+ s->port.line = pdev->id < 0 ? 0 : pdev->id;
+ s->port.fifosize = plat->fifo_size;
+ s->port.timeout = plat->timeout ? plat->timeout : (HZ / 10);
+ s->port.uartclk = clk_get_rate(s->clk);
+ s->port.type = PORT_IMX;
+ s->port.dev = s->dev = get_device(&pdev->dev);
+
+ s->flags = plat->dma_mode ? MXS_AUART_PORT_DMA_MODE : 0;
+ s->ctrl = 0;
+ s->dma_rx_buffer_size = plat->dma_rx_buffer_size;
+
+ for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
+ s->irq[i] = platform_get_irq(pdev, i);
+ if (s->irq[i] < 0) {
+ ret = s->irq[i];
+ goto out_free_clk;
+ }
+ }
+ s->port.irq = s->irq[0];
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (!r) {
+ ret = -ENXIO;
+ goto out_free_clk;
+ }
+ s->dma_rx_chan = r->start;
+
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (!r) {
+ ret = -ENXIO;
+ goto out_free_clk;
+ }
+ s->dma_tx_chan = r->start;
+
+ platform_set_drvdata(pdev, s);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+#ifdef CONFIG_SERIAL_MXS_AUART_CONSOLE
+ memcpy(&auart_port[pdev->id], s, sizeof(struct mxs_auart_port));
+#endif
+
+ ret = uart_add_one_port(&auart_driver, &s->port);
+ if (ret)
+ goto out_free_clk;
+
+ version = __raw_readl(s->port.membase + HW_UARTAPP_VERSION);
+ printk(KERN_INFO "Found APPUART %d.%d.%d\n",
+ (version >> 24) & 0xFF,
+ (version >> 16) & 0xFF, version & 0xFFFF);
+ return 0;
+
+out_free_clk:
+ if (!IS_ERR(s->clk))
+ clk_put(s->clk);
+out_free:
+ kfree(s);
+out:
+ return ret;
+}
+
+static int __devexit mxs_auart_remove(struct platform_device *pdev)
+{
+ struct mxs_auart_port *s;
+
+ s = platform_get_drvdata(pdev);
+ if (s) {
+ put_device(s->dev);
+ clk_disable(s->clk);
+ clk_put(s->clk);
+ uart_remove_one_port(&auart_driver, &s->port);
+ kfree(s);
+ }
+ return 0;
+}
+
+static struct platform_driver mxs_auart_driver = {
+ .probe = mxs_auart_probe,
+ .remove = __devexit_p(mxs_auart_remove),
+ .driver = {
+ .name = "mxs-auart",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init mxs_auart_init(void)
+{
+ int r;
+
+ r = uart_register_driver(&auart_driver);
+ if (r)
+ goto out;
+ r = platform_driver_register(&mxs_auart_driver);
+ if (r)
+ goto out_err;
+ return 0;
+out_err:
+ uart_unregister_driver(&auart_driver);
+out:
+ return r;
+}
+
+static void __exit mxs_auart_exit(void)
+{
+ platform_driver_unregister(&mxs_auart_driver);
+ uart_unregister_driver(&auart_driver);
+}
+
+module_init(mxs_auart_init)
+module_exit(mxs_auart_exit)
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Freescale MXS application uart driver");