]> git.hungrycats.org Git - linux/commitdiff
[ARM PATCH] 1670/1: PXA serial driver
authorHolger Schurig <h.schurig@de.rmk.(none)>
Fri, 20 Feb 2004 16:52:31 +0000 (16:52 +0000)
committerRussell King <rmk@flint.arm.linux.org.uk>
Fri, 20 Feb 2004 16:52:31 +0000 (16:52 +0000)
Patch from Holger Schurig

PXA Serial port (FFUART, BTUART, STUART) and console support

This patch works perfectly here with "console=ttyS0,115200" in the
kernel command line. It contains Jean-frederic Clere fix to detect
STUART and BTUART.

Nico said that I should post it to your patch tracker. It replaces patch 1662/1

drivers/serial/Kconfig
drivers/serial/Makefile
drivers/serial/pxa.c [new file with mode: 0644]
include/linux/serial_core.h
include/linux/serial_reg.h

index aae579ce6a80aa19fed7074a9f44ba71cbee070e..33dc331cf5dca3316715571dfc58a918e8a99d72 100644 (file)
@@ -324,6 +324,29 @@ config SERIAL_UART00_CONSOLE
          your boot loader (lilo or loadlin) about how to pass options to the
          kernel at boot time.)
 
+config SERIAL_PXA
+       bool "PXA serial port support"
+       depends on ARM && ARCH_PXA
+       select SERIAL_CORE
+       help
+         If you have a machine based on an Intel XScale PXA2xx CPU you
+         can enable its onboard serial ports by enabling this option.
+
+config SERIAL_PXA_CONSOLE
+       bool "Console on PXA serial port"
+       depends on SERIAL_PXA
+       select SERIAL_CORE_CONSOLE
+       help
+         If you have enabled the serial port on the Intel XScale PXA
+         CPU you can make it the console by answering Y to this option.
+
+         Even if you say Y here, the currently visible virtual console
+         (/dev/tty0) will still be used as the system console by default, but
+         you can alter that using a kernel command line option such as
+         "console=ttySA0". (Try "man bootparam" or see the documentation of
+         your boot loader (lilo or loadlin) about how to pass options to the
+         kernel at boot time.)
+
 config SERIAL_SA1100
        bool "SA1100 serial port support"
        depends on ARM && ARCH_SA1100
index 551f8d82918406febc70c493910495d6759ca0ec..713c3265adf6c33dfcf235f3e5285f2ec668dbf9 100644 (file)
@@ -19,6 +19,7 @@ obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o
 obj-$(CONFIG_SERIAL_ANAKIN) += anakin.o
 obj-$(CONFIG_SERIAL_AMBA) += amba.o
 obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
+obj-$(CONFIG_SERIAL_PXA) += pxa.o
 obj-$(CONFIG_SERIAL_SA1100) += sa1100.o
 obj-$(CONFIG_SERIAL_UART00) += uart00.o
 obj-$(CONFIG_SERIAL_SUNCORE) += suncore.o
diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c
new file mode 100644 (file)
index 0000000..42e83fd
--- /dev/null
@@ -0,0 +1,840 @@
+/*
+ *  linux/drivers/serial/pxa.c
+ *
+ *  Based on drivers/serial/8250.c by Russell King.
+ *
+ *  Author:    Nicolas Pitre
+ *  Created:   Feb 20, 2003
+ *  Copyright: (C) 2003 Monta Vista Software, Inc.
+ *
+ * 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.
+ *
+ * Note 1: This driver is made separate from the already too overloaded
+ * 8250.c because it needs some kirks of its own and that'll make it
+ * easier to add DMA support.
+ *
+ * Note 2: I'm too sick of device allocation policies for serial ports.
+ * If someone else wants to request an "official" allocation of major/minor
+ * for this driver please be my guest.  And don't forget that new hardware
+ * to come from Intel might have more than 3 or 4 of those UARTs.  Let's
+ * hope for a better port registration and dynamic device allocation scheme
+ * with the serial core maintainer satisfaction to appear soon.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/sysrq.h>
+#include <linux/serial_reg.h>
+#include <linux/circ_buf.h>
+#include <linux/serial.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+
+#if defined(CONFIG_SERIAL_PXA_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
+#include <linux/serial_core.h>
+
+
+struct uart_pxa_port {
+       struct uart_port        port;
+       unsigned char           ier;
+       unsigned char           lcr;
+       unsigned char           mcr;
+       unsigned int            lsr_break_flag;
+       unsigned int            cken;
+       char                    *name;
+};
+
+static inline unsigned int serial_in(struct uart_pxa_port *up, int offset)
+{
+       offset <<= 2;
+       return readl(up->port.membase + offset);
+}
+
+static inline void serial_out(struct uart_pxa_port *up, int offset, int value)
+{
+       offset <<= 2;
+       writel(value, up->port.membase + offset);
+}
+
+static void serial_pxa_enable_ms(struct uart_port *port)
+{
+       struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+       up->ier |= UART_IER_MSI;
+       serial_out(up, UART_IER, up->ier);
+}
+
+static void serial_pxa_stop_tx(struct uart_port *port, unsigned int tty_stop)
+{
+       struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+       if (up->ier & UART_IER_THRI) {
+               up->ier &= ~UART_IER_THRI;
+               serial_out(up, UART_IER, up->ier);
+       }
+}
+
+static void serial_pxa_stop_rx(struct uart_port *port)
+{
+       struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+       up->ier &= ~UART_IER_RLSI;
+       up->port.read_status_mask &= ~UART_LSR_DR;
+       serial_out(up, UART_IER, up->ier);
+}
+
+static inline void
+receive_chars(struct uart_pxa_port *up, int *status, struct pt_regs *regs)
+{
+       struct tty_struct *tty = up->port.info->tty;
+       unsigned char ch;
+       int max_count = 256;
+
+       do {
+               if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
+                       /*
+                        * FIXME: Deadlock can happen here if we're a
+                        * low-latency port.  We're holding the per-port
+                        * spinlock, and we call flush_to_ldisc->
+                        * n_tty_receive_buf->n_tty_receive_char->
+                        * opost->uart_put_char.
+                        */
+                       tty->flip.work.func((void *)tty);
+                       if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+                               return; // if TTY_DONT_FLIP is set
+               }
+               ch = serial_in(up, UART_RX);
+               *tty->flip.char_buf_ptr = ch;
+               *tty->flip.flag_buf_ptr = TTY_NORMAL;
+               up->port.icount.rx++;
+
+               if (unlikely(*status & (UART_LSR_BI | UART_LSR_PE |
+                                      UART_LSR_FE | UART_LSR_OE))) {
+                       /*
+                        * For statistics only
+                        */
+                       if (*status & UART_LSR_BI) {
+                               *status &= ~(UART_LSR_FE | UART_LSR_PE);
+                               up->port.icount.brk++;
+                               /*
+                                * We do the SysRQ and SAK checking
+                                * here because otherwise the break
+                                * may get masked by ignore_status_mask
+                                * or read_status_mask.
+                                */
+                               if (uart_handle_break(&up->port))
+                                       goto ignore_char;
+                       } else if (*status & UART_LSR_PE)
+                               up->port.icount.parity++;
+                       else if (*status & UART_LSR_FE)
+                               up->port.icount.frame++;
+                       if (*status & UART_LSR_OE)
+                               up->port.icount.overrun++;
+
+                       /*
+                        * Mask off conditions which should be ignored.
+                        */
+                       *status &= up->port.read_status_mask;
+
+#ifdef CONFIG_SERIAL_PXA_CONSOLE
+                       if (up->port.line == up->port.cons->index) {
+                               /* Recover the break flag from console xmit */
+                               *status |= up->lsr_break_flag;
+                               up->lsr_break_flag = 0;
+                       }
+#endif
+                       if (*status & UART_LSR_BI) {
+                               *tty->flip.flag_buf_ptr = TTY_BREAK;
+                       } else if (*status & UART_LSR_PE)
+                               *tty->flip.flag_buf_ptr = TTY_PARITY;
+                       else if (*status & UART_LSR_FE)
+                               *tty->flip.flag_buf_ptr = TTY_FRAME;
+               }
+               if (uart_handle_sysrq_char(&up->port, ch, regs))
+                       goto ignore_char;
+               if ((*status & up->port.ignore_status_mask) == 0) {
+                       tty->flip.flag_buf_ptr++;
+                       tty->flip.char_buf_ptr++;
+                       tty->flip.count++;
+               }
+               if ((*status & UART_LSR_OE) &&
+                   tty->flip.count < TTY_FLIPBUF_SIZE) {
+                       /*
+                        * Overrun is special, since it's reported
+                        * immediately, and doesn't affect the current
+                        * character.
+                        */
+                       *tty->flip.flag_buf_ptr = TTY_OVERRUN;
+                       tty->flip.flag_buf_ptr++;
+                       tty->flip.char_buf_ptr++;
+                       tty->flip.count++;
+               }
+       ignore_char:
+               *status = serial_in(up, UART_LSR);
+       } while ((*status & UART_LSR_DR) && (max_count-- > 0));
+       tty_flip_buffer_push(tty);
+}
+
+static void transmit_chars(struct uart_pxa_port *up)
+{
+       struct circ_buf *xmit = &up->port.info->xmit;
+       int count;
+
+       if (up->port.x_char) {
+               serial_out(up, UART_TX, up->port.x_char);
+               up->port.icount.tx++;
+               up->port.x_char = 0;
+               return;
+       }
+       if (uart_circ_empty(xmit) || uart_tx_stopped(&up->port)) {
+               serial_pxa_stop_tx(&up->port, 0);
+               return;
+       }
+
+       count = up->port.fifosize / 2;
+       do {
+               serial_out(up, UART_TX, xmit->buf[xmit->tail]);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
+               up->port.icount.tx++;
+               if (uart_circ_empty(xmit))
+                       break;
+       } while (--count > 0);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(&up->port);
+
+
+       if (uart_circ_empty(xmit))
+               serial_pxa_stop_tx(&up->port, 0);
+}
+
+static void serial_pxa_start_tx(struct uart_port *port, unsigned int tty_start)
+{
+       struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+
+       if (!(up->ier & UART_IER_THRI)) {
+               up->ier |= UART_IER_THRI;
+               serial_out(up, UART_IER, up->ier);
+       }
+}
+
+static inline void check_modem_status(struct uart_pxa_port *up)
+{
+       int status;
+
+       status = serial_in(up, UART_MSR);
+
+       if ((status & UART_MSR_ANY_DELTA) == 0)
+               return;
+
+       if (status & UART_MSR_TERI)
+               up->port.icount.rng++;
+       if (status & UART_MSR_DDSR)
+               up->port.icount.dsr++;
+       if (status & UART_MSR_DDCD)
+               uart_handle_dcd_change(&up->port, status & UART_MSR_DCD);
+       if (status & UART_MSR_DCTS)
+               uart_handle_cts_change(&up->port, status & UART_MSR_CTS);
+
+       wake_up_interruptible(&up->port.info->delta_msr_wait);
+}
+
+/*
+ * This handles the interrupt from one port.
+ */
+static inline irqreturn_t
+serial_pxa_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct uart_pxa_port *up = (struct uart_pxa_port *)dev_id;
+       unsigned int iir, lsr;
+
+       iir = serial_in(up, UART_IIR);
+       if (iir & UART_IIR_NO_INT)
+               return IRQ_NONE;
+       lsr = serial_in(up, UART_LSR);
+       if (lsr & UART_LSR_DR)
+               receive_chars(up, &lsr, regs);
+       check_modem_status(up);
+       if (lsr & UART_LSR_THRE)
+               transmit_chars(up);
+       return IRQ_HANDLED;
+}
+
+static unsigned int serial_pxa_tx_empty(struct uart_port *port)
+{
+       struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+       unsigned long flags;
+       unsigned int ret;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       ret = serial_in(up, UART_LSR) & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       return ret;
+}
+
+static unsigned int serial_pxa_get_mctrl(struct uart_port *port)
+{
+       struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+       unsigned long flags;
+       unsigned char status;
+       unsigned int ret;
+
+return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+       spin_lock_irqsave(&up->port.lock, flags);
+       status = serial_in(up, UART_MSR);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       ret = 0;
+       if (status & UART_MSR_DCD)
+               ret |= TIOCM_CAR;
+       if (status & UART_MSR_RI)
+               ret |= TIOCM_RNG;
+       if (status & UART_MSR_DSR)
+               ret |= TIOCM_DSR;
+       if (status & UART_MSR_CTS)
+               ret |= TIOCM_CTS;
+       return ret;
+}
+
+static void serial_pxa_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+       unsigned char mcr = 0;
+
+       if (mctrl & TIOCM_RTS)
+               mcr |= UART_MCR_RTS;
+       if (mctrl & TIOCM_DTR)
+               mcr |= UART_MCR_DTR;
+       if (mctrl & TIOCM_OUT1)
+               mcr |= UART_MCR_OUT1;
+       if (mctrl & TIOCM_OUT2)
+               mcr |= UART_MCR_OUT2;
+       if (mctrl & TIOCM_LOOP)
+               mcr |= UART_MCR_LOOP;
+
+       mcr |= up->mcr;
+
+       serial_out(up, UART_MCR, mcr);
+}
+
+static void serial_pxa_break_ctl(struct uart_port *port, int break_state)
+{
+       struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+       unsigned long flags;
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       if (break_state == -1)
+               up->lcr |= UART_LCR_SBC;
+       else
+               up->lcr &= ~UART_LCR_SBC;
+       serial_out(up, UART_LCR, up->lcr);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+#if 0
+static void serial_pxa_dma_init(struct pxa_uart *up)
+{
+       up->rxdma =
+               pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_receive_dma, up);
+       if (up->rxdma < 0)
+               goto out;
+       up->txdma =
+               pxa_request_dma(up->name, DMA_PRIO_LOW, pxa_transmit_dma, up);
+       if (up->txdma < 0)
+               goto err_txdma;
+       up->dmadesc = kmalloc(4 * sizeof(pxa_dma_desc), GFP_KERNEL);
+       if (!up->dmadesc)
+               goto err_alloc;
+
+       /* ... */
+err_alloc:
+       pxa_free_dma(up->txdma);
+err_rxdma:
+       pxa_free_dma(up->rxdma);
+out:
+       return;
+}
+#endif
+
+static int serial_pxa_startup(struct uart_port *port)
+{
+       struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+       unsigned long flags;
+       int retval;
+
+       up->mcr = 0;
+
+       /*
+        * Allocate the IRQ
+        */
+       retval = request_irq(up->port.irq, serial_pxa_irq, 0, up->name, up);
+       if (retval)
+               return retval;
+
+       CKEN |= up->cken;
+       udelay(1);
+
+       /*
+        * Clear the FIFO buffers and disable them.
+        * (they will be reenabled in set_termios())
+        */
+       serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
+       serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+                       UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
+       serial_out(up, UART_FCR, 0);
+
+       /*
+        * Clear the interrupt registers.
+        */
+       (void) serial_in(up, UART_LSR);
+       (void) serial_in(up, UART_RX);
+       (void) serial_in(up, UART_IIR);
+       (void) serial_in(up, UART_MSR);
+
+       /*
+        * Now, initialize the UART
+        */
+       serial_out(up, UART_LCR, UART_LCR_WLEN8);
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       up->port.mctrl |= TIOCM_OUT2;
+       serial_pxa_set_mctrl(&up->port, up->port.mctrl);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       /*
+        * Finally, enable interrupts.  Note: Modem status interrupts
+        * are set via set_termios(), which will be occuring imminently
+        * anyway, so we don't enable them here.
+        */
+       up->ier = UART_IER_RLSI | UART_IER_RDI | UART_IER_RTOIE | UART_IER_UUE;
+       serial_out(up, UART_IER, up->ier);
+
+       /*
+        * And clear the interrupt registers again for luck.
+        */
+       (void) serial_in(up, UART_LSR);
+       (void) serial_in(up, UART_RX);
+       (void) serial_in(up, UART_IIR);
+       (void) serial_in(up, UART_MSR);
+
+       return 0;
+}
+
+static void serial_pxa_shutdown(struct uart_port *port)
+{
+       struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+       unsigned long flags;
+
+       free_irq(up->port.irq, up);
+
+       /*
+        * Disable interrupts from this port
+        */
+       up->ier = 0;
+       serial_out(up, UART_IER, 0);
+
+       spin_lock_irqsave(&up->port.lock, flags);
+       up->port.mctrl &= ~TIOCM_OUT2;
+       serial_pxa_set_mctrl(&up->port, up->port.mctrl);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+
+       /*
+        * Disable break condition and FIFOs
+        */
+       serial_out(up, UART_LCR, serial_in(up, UART_LCR) & ~UART_LCR_SBC);
+       serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO |
+                                 UART_FCR_CLEAR_RCVR |
+                                 UART_FCR_CLEAR_XMIT);
+       serial_out(up, UART_FCR, 0);
+
+       CKEN &= ~up->cken;
+}
+
+static void
+serial_pxa_set_termios(struct uart_port *port, struct termios *termios,
+                      struct termios *old)
+{
+       struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+       unsigned char cval, fcr = 0;
+       unsigned long flags;
+       unsigned int baud, quot;
+
+       switch (termios->c_cflag & CSIZE) {
+       case CS5:
+               cval = 0x00;
+               break;
+       case CS6:
+               cval = 0x01;
+               break;
+       case CS7:
+               cval = 0x02;
+               break;
+       default:
+       case CS8:
+               cval = 0x03;
+               break;
+       }
+
+       if (termios->c_cflag & CSTOPB)
+               cval |= 0x04;
+       if (termios->c_cflag & PARENB)
+               cval |= UART_LCR_PARITY;
+       if (!(termios->c_cflag & PARODD))
+               cval |= UART_LCR_EPAR;
+
+       /*
+        * Ask the core to calculate the divisor for us.
+        */
+       baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+       quot = uart_get_divisor(port, baud);
+
+       if ((up->port.uartclk / quot) < (2400 * 16))
+               fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR1;
+       else
+               fcr = UART_FCR_ENABLE_FIFO | UART_FCR_PXAR8;
+
+       /*
+        * Ok, we're now changing the port state.  Do it with
+        * interrupts disabled.
+        */
+       spin_lock_irqsave(&up->port.lock, flags);
+
+       /*
+        * Ensure the port will be enabled.
+        * This is required especially for serial console.
+        */
+       up->ier |= IER_UUE;
+
+       /*
+        * Update the per-port timeout.
+        */
+       uart_update_timeout(port, termios->c_cflag, quot);
+
+       up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
+       if (termios->c_iflag & INPCK)
+               up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
+       if (termios->c_iflag & (BRKINT | PARMRK))
+               up->port.read_status_mask |= UART_LSR_BI;
+
+       /*
+        * Characters to ignore
+        */
+       up->port.ignore_status_mask = 0;
+       if (termios->c_iflag & IGNPAR)
+               up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
+       if (termios->c_iflag & IGNBRK) {
+               up->port.ignore_status_mask |= UART_LSR_BI;
+               /*
+                * If we're ignoring parity and break indicators,
+                * ignore overruns too (for real raw support).
+                */
+               if (termios->c_iflag & IGNPAR)
+                       up->port.ignore_status_mask |= UART_LSR_OE;
+       }
+
+       /*
+        * ignore all characters if CREAD is not set
+        */
+       if ((termios->c_cflag & CREAD) == 0)
+               up->port.ignore_status_mask |= UART_LSR_DR;
+
+       /*
+        * CTS flow control flag and modem status interrupts
+        */
+       up->ier &= ~UART_IER_MSI;
+       if (UART_ENABLE_MS(&up->port, termios->c_cflag))
+               up->ier |= UART_IER_MSI;
+
+       serial_out(up, UART_IER, up->ier);
+
+       serial_out(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
+       serial_out(up, UART_DLL, quot & 0xff);          /* LS of divisor */
+       serial_out(up, UART_DLM, quot >> 8);            /* MS of divisor */
+       serial_out(up, UART_LCR, cval);         /* reset DLAB */
+       up->lcr = cval;                                 /* Save LCR */
+       serial_pxa_set_mctrl(&up->port, up->port.mctrl);
+       serial_out(up, UART_FCR, fcr);
+       spin_unlock_irqrestore(&up->port.lock, flags);
+}
+
+static void
+serial_pxa_pm(struct uart_port *port, unsigned int state,
+             unsigned int oldstate)
+{
+       if (state) {
+               /* sleep */
+       } else {
+               /* wake */
+       }
+}
+
+static void serial_pxa_release_port(struct uart_port *port)
+{
+}
+
+static int serial_pxa_request_port(struct uart_port *port)
+{
+       return 0;
+}
+
+static void serial_pxa_config_port(struct uart_port *port, int flags)
+{
+       struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+       up->port.type = PORT_PXA;
+}
+
+static int
+serial_pxa_verify_port(struct uart_port *port, struct serial_struct *ser)
+{
+       /* we don't want the core code to modify any port params */
+       return -EINVAL;
+}
+
+static const char *
+serial_pxa_type(struct uart_port *port)
+{
+       struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+       return up->name;
+}
+
+#ifdef CONFIG_SERIAL_PXA_CONSOLE
+
+extern struct uart_pxa_port serial_pxa_ports[];
+extern struct uart_driver serial_pxa_reg;
+
+#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
+
+/*
+ *     Wait for transmitter & holding register to empty
+ */
+static inline void wait_for_xmitr(struct uart_pxa_port *up)
+{
+       unsigned int status, tmout = 10000;
+
+       /* Wait up to 10ms for the character(s) to be sent. */
+       do {
+               status = serial_in(up, UART_LSR);
+
+               if (status & UART_LSR_BI)
+                       up->lsr_break_flag = UART_LSR_BI;
+
+               if (--tmout == 0)
+                       break;
+               udelay(1);
+       } while ((status & BOTH_EMPTY) != BOTH_EMPTY);
+
+       /* Wait up to 1s for flow control if necessary */
+       if (up->port.flags & UPF_CONS_FLOW) {
+               tmout = 1000000;
+               while (--tmout &&
+                      ((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
+                       udelay(1);
+       }
+}
+
+/*
+ * Print a string to the serial port trying not to disturb
+ * any possible real use of the port...
+ *
+ *     The console_lock must be held when we get here.
+ */
+static void
+serial_pxa_console_write(struct console *co, const char *s, unsigned int count)
+{
+       struct uart_pxa_port *up = &serial_pxa_ports[co->index];
+       unsigned int ier;
+       int i;
+
+       /*
+        *      First save the UER then disable the interrupts
+        */
+       ier = serial_in(up, UART_IER);
+       serial_out(up, UART_IER, UART_IER_UUE);
+
+       /*
+        *      Now, do each character
+        */
+       for (i = 0; i < count; i++, s++) {
+               wait_for_xmitr(up);
+
+               /*
+                *      Send the character out.
+                *      If a LF, also do CR...
+                */
+               serial_out(up, UART_TX, *s);
+               if (*s == 10) {
+                       wait_for_xmitr(up);
+                       serial_out(up, UART_TX, 13);
+               }
+       }
+
+       /*
+        *      Finally, wait for transmitter to become empty
+        *      and restore the IER
+        */
+       wait_for_xmitr(up);
+       serial_out(up, UART_IER, ier);
+}
+
+static int __init
+serial_pxa_console_setup(struct console *co, char *options)
+{
+       struct uart_pxa_port *up;
+       int baud = 9600;
+       int bits = 8;
+       int parity = 'n';
+       int flow = 'n';
+
+       if (co->index == -1 || co->index >= serial_pxa_reg.nr)
+               co->index = 0;
+               up = &serial_pxa_ports[co->index];
+
+       if (options)
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+
+       return uart_set_options(&up->port, co, baud, parity, bits, flow);
+}
+
+static struct console serial_pxa_console = {
+       .name           = "ttyS",
+       .write          = serial_pxa_console_write,
+       .device         = uart_console_device,
+       .setup          = serial_pxa_console_setup,
+       .flags          = CON_PRINTBUFFER,
+       .index          = -1,
+       .data           = &serial_pxa_reg,
+};
+
+static int __init
+serial_pxa_console_init(void)
+{
+       register_console(&serial_pxa_console);
+       return 0;
+}
+
+console_initcall(serial_pxa_console_init);
+
+#define PXA_CONSOLE    &serial_pxa_console
+#else
+#define PXA_CONSOLE    NULL
+#endif
+
+struct uart_ops serial_pxa_pops = {
+       .tx_empty       = serial_pxa_tx_empty,
+       .set_mctrl      = serial_pxa_set_mctrl,
+       .get_mctrl      = serial_pxa_get_mctrl,
+       .stop_tx        = serial_pxa_stop_tx,
+       .start_tx       = serial_pxa_start_tx,
+       .stop_rx        = serial_pxa_stop_rx,
+       .enable_ms      = serial_pxa_enable_ms,
+       .break_ctl      = serial_pxa_break_ctl,
+       .startup        = serial_pxa_startup,
+       .shutdown       = serial_pxa_shutdown,
+       .set_termios    = serial_pxa_set_termios,
+       .pm             = serial_pxa_pm,
+       .type           = serial_pxa_type,
+       .release_port   = serial_pxa_release_port,
+       .request_port   = serial_pxa_request_port,
+       .config_port    = serial_pxa_config_port,
+       .verify_port    = serial_pxa_verify_port,
+};
+
+static struct uart_pxa_port serial_pxa_ports[] = {
+     { /* FFUART */
+       .name   = "FFUART",
+       .cken   = CKEN6_FFUART,
+       .port   = {
+               .type           = PORT_PXA,
+               .iotype         = SERIAL_IO_MEM,
+               .membase        = (void *)&FFUART,
+               .mapbase        = __PREG(FFUART),
+               .irq            = IRQ_FFUART,
+               .uartclk        = 921600 * 16,
+               .fifosize       = 64,
+               .flags          = ASYNC_SKIP_TEST,
+               .ops            = &serial_pxa_pops,
+               .line           = 0,
+       },
+  }, { /* BTUART */
+       .name   = "BTUART",
+       .cken   = CKEN7_BTUART,
+       .port   = {
+               .type           = PORT_PXA,
+               .iotype         = SERIAL_IO_MEM,
+               .membase        = (void *)&BTUART,
+               .mapbase        = __PREG(BTUART),
+               .irq            = IRQ_BTUART,
+               .uartclk        = 921600 * 16,
+               .fifosize       = 64,
+               .flags          = ASYNC_SKIP_TEST,
+               .ops            = &serial_pxa_pops,
+               .line           = 1,
+       },
+  }, { /* STUART */
+       .name   = "STUART",
+       .cken   = CKEN5_STUART,
+       .port   = {
+               .type           = PORT_PXA,
+               .iotype         = SERIAL_IO_MEM,
+               .membase        = (void *)&STUART,
+               .mapbase        = __PREG(STUART),
+               .irq            = IRQ_STUART,
+               .uartclk        = 921600 * 16,
+               .fifosize       = 64,
+               .flags          = ASYNC_SKIP_TEST,
+               .ops            = &serial_pxa_pops,
+               .line           = 2,
+       },
+  }
+};
+
+static struct uart_driver serial_pxa_reg = {
+       .owner          = THIS_MODULE,
+       .driver_name    = "PXA serial",
+       .devfs_name     = "tts/",
+       .dev_name       = "ttyS",
+       .major          = TTY_MAJOR,
+       .minor          = 64,
+       .nr             = ARRAY_SIZE(serial_pxa_ports),
+       .cons           = PXA_CONSOLE,
+};
+
+static int __init serial_pxa_init(void)
+{
+       int i, ret;
+
+       ret = uart_register_driver(&serial_pxa_reg);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < ARRAY_SIZE(serial_pxa_ports); i++)
+               uart_add_one_port(&serial_pxa_reg, &serial_pxa_ports[i].port);
+
+       return 0;
+}
+
+static void __exit serial_pxa_exit(void)
+{
+       uart_unregister_driver(&serial_pxa_reg);
+}
+
+module_init(serial_pxa_init);
+module_exit(serial_pxa_exit);
+
+MODULE_LICENSE("GPL");
+
index 8e09c20be41363926d892f706f601657f47ad686..da998dc9389d90b132aa8e9d5b1da2226b6027b6 100644 (file)
@@ -46,6 +46,7 @@
  * separate so any additions to the old serial.c that occur before
  * we are merged can be easily merged here.
  */
+#define PORT_PXA       31
 #define PORT_AMBA      32
 #define PORT_CLPS711X  33
 #define PORT_SA1100    34
index 947da230a189929006fd08cc15677bff8041f950..f2fee651085f94ab6db0dea059d46d7adf9e7955 100644 (file)
 #define UART_MSR_DCTS  0x01    /* Delta CTS */
 #define UART_MSR_ANY_DELTA 0x0F        /* Any of the delta bits! */
 
+/*
+ * The Intel PXA2xx chip defines those bits
+ */
+#define UART_IER_DMAE  0x80    /* DMA Requests Enable */
+#define UART_IER_UUE   0x40    /* UART Unit Enable */
+#define UART_IER_NRZE  0x20    /* NRZ coding Enable */
+#define UART_IER_RTOIE 0x10    /* Receiver Time Out Interrupt Enable */
+
+#define UART_IIR_TOD   0x08    /* Character Timeout Indication Detected */
+
+#define UART_FCR_PXAR1 0x00    /* receive FIFO treshold = 1 */
+#define UART_FCR_PXAR8 0x40    /* receive FIFO treshold = 8 */
+#define UART_FCR_PXAR16        0x80    /* receive FIFO treshold = 16 */
+#define UART_FCR_PXAR32        0xc0    /* receive FIFO treshold = 32 */
+
 /*
  * These are the definitions for the Extended Features Register
  * (StarTech 16C660 only, when DLAB=1)