fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / serial / amba-pl011.c
index f902474..44639e7 100644 (file)
  * required, these have to be supplied via some other means (eg, GPIO)
  * and hooked into this driver.
  */
-#include <linux/config.h>
+
+#if defined(CONFIG_SERIAL_AMBA_PL011_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
+#define SUPPORT_SYSRQ
+#endif
+
 #include <linux/module.h>
-#include <linux/tty.h>
 #include <linux/ioport.h>
 #include <linux/init.h>
-#include <linux/serial.h>
 #include <linux/console.h>
 #include <linux/sysrq.h>
 #include <linux/device.h>
-
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/hardware/amba.h>
-
-#if defined(CONFIG_SERIAL_AMBA_PL011_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
-#define SUPPORT_SYSRQ
-#endif
-
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
 #include <linux/serial_core.h>
+#include <linux/serial.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/serial.h>
+#include <linux/clk.h>
 
-#include <asm/hardware/amba_serial.h>
+#include <asm/io.h>
+#include <asm/sizes.h>
 
 #define UART_NR                        14
 
 
 #define AMBA_ISR_PASS_LIMIT    256
 
-#define UART_DUMMY_RSR_RX      256
+#define UART_DR_ERROR          (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE)
+#define UART_DUMMY_DR_RX       (1 << 16)
 
 /*
  * We wrap our port structure around the generic uart_port.
  */
 struct uart_amba_port {
        struct uart_port        port;
+       struct clk              *clk;
        unsigned int            im;     /* interrupt mask */
        unsigned int            old_status;
 };
 
-static void pl011_stop_tx(struct uart_port *port, unsigned int tty_stop)
+static void pl011_stop_tx(struct uart_port *port)
 {
        struct uart_amba_port *uap = (struct uart_amba_port *)port;
 
@@ -80,7 +82,7 @@ static void pl011_stop_tx(struct uart_port *port, unsigned int tty_stop)
        writew(uap->im, uap->port.membase + UART011_IMSC);
 }
 
-static void pl011_start_tx(struct uart_port *port, unsigned int tty_start)
+static void pl011_start_tx(struct uart_port *port)
 {
        struct uart_amba_port *uap = (struct uart_amba_port *)port;
 
@@ -105,79 +107,49 @@ static void pl011_enable_ms(struct uart_port *port)
        writew(uap->im, uap->port.membase + UART011_IMSC);
 }
 
-static void
-#ifdef SUPPORT_SYSRQ
-pl011_rx_chars(struct uart_amba_port *uap, struct pt_regs *regs)
-#else
-pl011_rx_chars(struct uart_amba_port *uap)
-#endif
+static void pl011_rx_chars(struct uart_amba_port *uap)
 {
        struct tty_struct *tty = uap->port.info->tty;
-       unsigned int status, ch, rsr, max_count = 256;
+       unsigned int status, ch, flag, max_count = 256;
 
        status = readw(uap->port.membase + UART01x_FR);
        while ((status & UART01x_FR_RXFE) == 0 && max_count--) {
-               if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
-                       tty->flip.work.func((void *)tty);
-                       if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
-                               printk(KERN_WARNING "TTY_DONT_FLIP set\n");
-                               return;
-                       }
-               }
-
-               ch = readw(uap->port.membase + UART01x_DR);
-
-               *tty->flip.char_buf_ptr = ch;
-               *tty->flip.flag_buf_ptr = TTY_NORMAL;
+               ch = readw(uap->port.membase + UART01x_DR) | UART_DUMMY_DR_RX;
+               flag = TTY_NORMAL;
                uap->port.icount.rx++;
 
                /*
                 * Note that the error handling code is
                 * out of the main execution path
                 */
-               rsr = readw(uap->port.membase + UART01x_RSR) | UART_DUMMY_RSR_RX;
-               if (rsr & UART01x_RSR_ANY) {
-                       if (rsr & UART01x_RSR_BE) {
-                               rsr &= ~(UART01x_RSR_FE | UART01x_RSR_PE);
+               if (unlikely(ch & UART_DR_ERROR)) {
+                       if (ch & UART011_DR_BE) {
+                               ch &= ~(UART011_DR_FE | UART011_DR_PE);
                                uap->port.icount.brk++;
                                if (uart_handle_break(&uap->port))
                                        goto ignore_char;
-                       } else if (rsr & UART01x_RSR_PE)
+                       } else if (ch & UART011_DR_PE)
                                uap->port.icount.parity++;
-                       else if (rsr & UART01x_RSR_FE)
+                       else if (ch & UART011_DR_FE)
                                uap->port.icount.frame++;
-                       if (rsr & UART01x_RSR_OE)
+                       if (ch & UART011_DR_OE)
                                uap->port.icount.overrun++;
 
-                       rsr &= uap->port.read_status_mask;
+                       ch &= uap->port.read_status_mask;
 
-                       if (rsr & UART01x_RSR_BE)
-                               *tty->flip.flag_buf_ptr = TTY_BREAK;
-                       else if (rsr & UART01x_RSR_PE)
-                               *tty->flip.flag_buf_ptr = TTY_PARITY;
-                       else if (rsr & UART01x_RSR_FE)
-                               *tty->flip.flag_buf_ptr = TTY_FRAME;
+                       if (ch & UART011_DR_BE)
+                               flag = TTY_BREAK;
+                       else if (ch & UART011_DR_PE)
+                               flag = TTY_PARITY;
+                       else if (ch & UART011_DR_FE)
+                               flag = TTY_FRAME;
                }
 
-               if (uart_handle_sysrq_char(&uap->port, ch, regs))
+               if (uart_handle_sysrq_char(&uap->port, ch & 255))
                        goto ignore_char;
 
-               if ((rsr & uap->port.ignore_status_mask) == 0) {
-                       tty->flip.flag_buf_ptr++;
-                       tty->flip.char_buf_ptr++;
-                       tty->flip.count++;
-               }
-               if ((rsr & UART01x_RSR_OE) &&
-                   tty->flip.count < TTY_FLIPBUF_SIZE) {
-                       /*
-                        * Overrun is special, since it's reported
-                        * immediately, and doesn't affect the current
-                        * character
-                        */
-                       *tty->flip.char_buf_ptr++ = 0;
-                       *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
-                       tty->flip.count++;
-               }
+               uart_insert_char(&uap->port, ch, UART011_DR_OE, ch, flag);
+
        ignore_char:
                status = readw(uap->port.membase + UART01x_FR);
        }
@@ -197,7 +169,7 @@ static void pl011_tx_chars(struct uart_amba_port *uap)
                return;
        }
        if (uart_circ_empty(xmit) || uart_tx_stopped(&uap->port)) {
-               pl011_stop_tx(&uap->port, 0);
+               pl011_stop_tx(&uap->port);
                return;
        }
 
@@ -214,7 +186,7 @@ static void pl011_tx_chars(struct uart_amba_port *uap)
                uart_write_wakeup(&uap->port);
 
        if (uart_circ_empty(xmit))
-               pl011_stop_tx(&uap->port, 0);
+               pl011_stop_tx(&uap->port);
 }
 
 static void pl011_modem_status(struct uart_amba_port *uap)
@@ -241,7 +213,7 @@ static void pl011_modem_status(struct uart_amba_port *uap)
        wake_up_interruptible(&uap->port.info->delta_msr_wait);
 }
 
-static irqreturn_t pl011_int(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t pl011_int(int irq, void *dev_id)
 {
        struct uart_amba_port *uap = dev_id;
        unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT;
@@ -257,11 +229,7 @@ static irqreturn_t pl011_int(int irq, void *dev_id, struct pt_regs *regs)
                               uap->port.membase + UART011_ICR);
 
                        if (status & (UART011_RTIS|UART011_RXIS))
-#ifdef SUPPORT_SYSRQ
-                               pl011_rx_chars(uap, regs);
-#else
                                pl011_rx_chars(uap);
-#endif
                        if (status & (UART011_DSRMIS|UART011_DCDMIS|
                                      UART011_CTSMIS|UART011_RIMIS))
                                pl011_modem_status(uap);
@@ -351,12 +319,21 @@ static int pl011_startup(struct uart_port *port)
        unsigned int cr;
        int retval;
 
+       /*
+        * Try to enable the clock producer.
+        */
+       retval = clk_enable(uap->clk);
+       if (retval)
+               goto out;
+
+       uap->port.uartclk = clk_get_rate(uap->clk);
+
        /*
         * Allocate the IRQ
         */
        retval = request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap);
        if (retval)
-               goto out;
+               goto clk_dis;
 
        writew(UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
               uap->port.membase + UART011_IFLS);
@@ -391,6 +368,8 @@ static int pl011_startup(struct uart_port *port)
 
        return 0;
 
+ clk_dis:
+       clk_disable(uap->clk);
  out:
        return retval;
 }
@@ -425,11 +404,16 @@ static void pl011_shutdown(struct uart_port *port)
        val = readw(uap->port.membase + UART011_LCRH);
        val &= ~(UART01x_LCRH_BRK | UART01x_LCRH_FEN);
        writew(val, uap->port.membase + UART011_LCRH);
+
+       /*
+        * Shut down the clock producer
+        */
+       clk_disable(uap->clk);
 }
 
 static void
-pl011_set_termios(struct uart_port *port, struct termios *termios,
-                    struct termios *old)
+pl011_set_termios(struct uart_port *port, struct ktermios *termios,
+                    struct ktermios *old)
 {
        unsigned int lcr_h, old_cr;
        unsigned long flags;
@@ -472,33 +456,33 @@ pl011_set_termios(struct uart_port *port, struct termios *termios,
         */
        uart_update_timeout(port, termios->c_cflag, baud);
 
-       port->read_status_mask = UART01x_RSR_OE;
+       port->read_status_mask = UART011_DR_OE | 255;
        if (termios->c_iflag & INPCK)
-               port->read_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE;
+               port->read_status_mask |= UART011_DR_FE | UART011_DR_PE;
        if (termios->c_iflag & (BRKINT | PARMRK))
-               port->read_status_mask |= UART01x_RSR_BE;
+               port->read_status_mask |= UART011_DR_BE;
 
        /*
         * Characters to ignore
         */
        port->ignore_status_mask = 0;
        if (termios->c_iflag & IGNPAR)
-               port->ignore_status_mask |= UART01x_RSR_FE | UART01x_RSR_PE;
+               port->ignore_status_mask |= UART011_DR_FE | UART011_DR_PE;
        if (termios->c_iflag & IGNBRK) {
-               port->ignore_status_mask |= UART01x_RSR_BE;
+               port->ignore_status_mask |= UART011_DR_BE;
                /*
                 * If we're ignoring parity and break indicators,
                 * ignore overruns too (for real raw support).
                 */
                if (termios->c_iflag & IGNPAR)
-                       port->ignore_status_mask |= UART01x_RSR_OE;
+                       port->ignore_status_mask |= UART011_DR_OE;
        }
 
        /*
         * Ignore all characters if CREAD is not set.
         */
        if ((termios->c_cflag & CREAD) == 0)
-               port->ignore_status_mask |= UART_DUMMY_RSR_RX;
+               port->ignore_status_mask |= UART_DUMMY_DR_RX;
 
        if (UART_ENABLE_MS(port, termios->c_cflag))
                pl011_enable_ms(port);
@@ -593,59 +577,53 @@ static struct uart_amba_port *amba_ports[UART_NR];
 
 #ifdef CONFIG_SERIAL_AMBA_PL011_CONSOLE
 
-static inline void
-pl011_console_write_char(struct uart_port *port, char ch)
+static void pl011_console_putchar(struct uart_port *port, int ch)
 {
-       unsigned int status;
+       struct uart_amba_port *uap = (struct uart_amba_port *)port;
 
-       do {
-               status = readw(port->membase + UART01x_FR);
-       } while (status & UART01x_FR_TXFF);
-       writew(ch, port->membase + UART01x_DR);
+       while (readw(uap->port.membase + UART01x_FR) & UART01x_FR_TXFF)
+               barrier();
+       writew(ch, uap->port.membase + UART01x_DR);
 }
 
 static void
 pl011_console_write(struct console *co, const char *s, unsigned int count)
 {
-       struct uart_port *port = &amba_ports[co->index]->port;
+       struct uart_amba_port *uap = amba_ports[co->index];
        unsigned int status, old_cr, new_cr;
-       int i;
+
+       clk_enable(uap->clk);
 
        /*
         *      First save the CR then disable the interrupts
         */
-       old_cr = readw(port->membase + UART011_CR);
+       old_cr = readw(uap->port.membase + UART011_CR);
        new_cr = old_cr & ~UART011_CR_CTSEN;
        new_cr |= UART01x_CR_UARTEN | UART011_CR_TXE;
-       writew(new_cr, port->membase + UART011_CR);
+       writew(new_cr, uap->port.membase + UART011_CR);
 
-       /*
-        *      Now, do each character
-        */
-       for (i = 0; i < count; i++) {
-               pl011_console_write_char(port, s[i]);
-               if (s[i] == '\n')
-                       pl011_console_write_char(port, '\r');
-       }
+       uart_console_write(&uap->port, s, count, pl011_console_putchar);
 
        /*
         *      Finally, wait for transmitter to become empty
         *      and restore the TCR
         */
        do {
-               status = readw(port->membase + UART01x_FR);
+               status = readw(uap->port.membase + UART01x_FR);
        } while (status & UART01x_FR_BUSY);
-       writew(old_cr, port->membase + UART011_CR);
+       writew(old_cr, uap->port.membase + UART011_CR);
+
+       clk_disable(uap->clk);
 }
 
 static void __init
-pl011_console_get_options(struct uart_port *port, int *baud,
+pl011_console_get_options(struct uart_amba_port *uap, int *baud,
                             int *parity, int *bits)
 {
-       if (readw(port->membase + UART011_CR) & UART01x_CR_UARTEN) {
+       if (readw(uap->port.membase + UART011_CR) & UART01x_CR_UARTEN) {
                unsigned int lcr_h, ibrd, fbrd;
 
-               lcr_h = readw(port->membase + UART011_LCRH);
+               lcr_h = readw(uap->port.membase + UART011_LCRH);
 
                *parity = 'n';
                if (lcr_h & UART01x_LCRH_PEN) {
@@ -660,10 +638,10 @@ pl011_console_get_options(struct uart_port *port, int *baud,
                else
                        *bits = 8;
 
-               ibrd = readw(port->membase + UART011_IBRD);
-               fbrd = readw(port->membase + UART011_FBRD);
+               ibrd = readw(uap->port.membase + UART011_IBRD);
+               fbrd = readw(uap->port.membase + UART011_FBRD);
 
-               *baud = port->uartclk * 4 / (64 * ibrd + fbrd);
+               *baud = uap->port.uartclk * 4 / (64 * ibrd + fbrd);
        }
 }
 
@@ -674,7 +652,6 @@ static int __init pl011_console_setup(struct console *co, char *options)
        int bits = 8;
        int parity = 'n';
        int flow = 'n';
-       int ret;
 
        /*
         * Check whether an invalid uart number has been specified, and
@@ -684,16 +661,20 @@ static int __init pl011_console_setup(struct console *co, char *options)
        if (co->index >= UART_NR)
                co->index = 0;
        uap = amba_ports[co->index];
+       if (!uap)
+               return -ENODEV;
+
+       uap->port.uartclk = clk_get_rate(uap->clk);
 
        if (options)
                uart_parse_options(options, &baud, &parity, &bits, &flow);
        else
-               pl011_console_get_options(&uap->port, &baud, &parity, &bits);
+               pl011_console_get_options(uap, &baud, &parity, &bits);
 
        return uart_set_options(&uap->port, co, baud, parity, bits, flow);
 }
 
-extern struct uart_driver amba_reg;
+static struct uart_driver amba_reg;
 static struct console amba_console = {
        .name           = "ttyAMA",
        .write          = pl011_console_write,
@@ -722,7 +703,7 @@ static struct uart_driver amba_reg = {
 static int pl011_probe(struct amba_device *dev, void *id)
 {
        struct uart_amba_port *uap;
-       void *base;
+       void __iomem *base;
        int i, ret;
 
        for (i = 0; i < ARRAY_SIZE(amba_ports); i++)
@@ -747,16 +728,17 @@ static int pl011_probe(struct amba_device *dev, void *id)
        }
 
        memset(uap, 0, sizeof(struct uart_amba_port));
+       uap->clk = clk_get(&dev->dev, "UARTCLK");
+       if (IS_ERR(uap->clk)) {
+               ret = PTR_ERR(uap->clk);
+               goto unmap;
+       }
+
        uap->port.dev = &dev->dev;
        uap->port.mapbase = dev->res.start;
        uap->port.membase = base;
        uap->port.iotype = UPIO_MEM;
        uap->port.irq = dev->irq[0];
-#if 0 /* FIXME */
-       uap->port.uartclk = 14745600;
-#else
-       uap->port.uartclk = 24000000;
-#endif
        uap->port.fifosize = 16;
        uap->port.ops = &amba_pl011_pops;
        uap->port.flags = UPF_BOOT_AUTOCONF;
@@ -769,6 +751,8 @@ static int pl011_probe(struct amba_device *dev, void *id)
        if (ret) {
                amba_set_drvdata(dev, NULL);
                amba_ports[i] = NULL;
+               clk_put(uap->clk);
+ unmap:
                iounmap(base);
  free:
                kfree(uap);
@@ -791,6 +775,7 @@ static int pl011_remove(struct amba_device *dev)
                        amba_ports[i] = NULL;
 
        iounmap(uap->port.membase);
+       clk_put(uap->clk);
        kfree(uap);
        return 0;
 }