vserver 1.9.5.x5
[linux-2.6.git] / drivers / serial / amba-pl011.c
index f902474..ff658a8 100644 (file)
  * 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 <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial_core.h>
+#include <linux/serial.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/serial_core.h>
-
+#include <asm/hardware/clock.h>
 #include <asm/hardware/amba_serial.h>
 
 #define UART_NR                        14
@@ -68,6 +69,7 @@
  */
 struct uart_amba_port {
        struct uart_port        port;
+       struct clk              *clk;
        unsigned int            im;     /* interrupt mask */
        unsigned int            old_status;
 };
@@ -113,22 +115,21 @@ pl011_rx_chars(struct uart_amba_port *uap)
 #endif
 {
        struct tty_struct *tty = uap->port.info->tty;
-       unsigned int status, ch, rsr, max_count = 256;
+       unsigned int status, ch, flag, rsr, 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;
-                       }
+                       if (tty->low_latency)
+                               tty_flip_buffer_push(tty);
+                       /*
+                        * If this failed then we will throw away the
+                        * bytes but must do so to clear interrupts
+                        */
                }
 
                ch = readw(uap->port.membase + UART01x_DR);
-
-               *tty->flip.char_buf_ptr = ch;
-               *tty->flip.flag_buf_ptr = TTY_NORMAL;
+               flag = TTY_NORMAL;
                uap->port.icount.rx++;
 
                /*
@@ -152,20 +153,18 @@ pl011_rx_chars(struct uart_amba_port *uap)
                        rsr &= uap->port.read_status_mask;
 
                        if (rsr & UART01x_RSR_BE)
-                               *tty->flip.flag_buf_ptr = TTY_BREAK;
+                               flag = TTY_BREAK;
                        else if (rsr & UART01x_RSR_PE)
-                               *tty->flip.flag_buf_ptr = TTY_PARITY;
+                               flag = TTY_PARITY;
                        else if (rsr & UART01x_RSR_FE)
-                               *tty->flip.flag_buf_ptr = TTY_FRAME;
+                               flag = TTY_FRAME;
                }
 
                if (uart_handle_sysrq_char(&uap->port, ch, regs))
                        goto ignore_char;
 
                if ((rsr & uap->port.ignore_status_mask) == 0) {
-                       tty->flip.flag_buf_ptr++;
-                       tty->flip.char_buf_ptr++;
-                       tty->flip.count++;
+                       tty_insert_flip_char(tty, ch, flag);
                }
                if ((rsr & UART01x_RSR_OE) &&
                    tty->flip.count < TTY_FLIPBUF_SIZE) {
@@ -174,9 +173,7 @@ pl011_rx_chars(struct uart_amba_port *uap)
                         * immediately, and doesn't affect the current
                         * character
                         */
-                       *tty->flip.char_buf_ptr++ = 0;
-                       *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
-                       tty->flip.count++;
+                       tty_insert_flip_char(tty, 0, TTY_OVERRUN);
                }
        ignore_char:
                status = readw(uap->port.membase + UART01x_FR);
@@ -351,12 +348,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 +397,8 @@ static int pl011_startup(struct uart_port *port)
 
        return 0;
 
+ clk_dis:
+       clk_disable(uap->clk);
  out:
        return retval;
 }
@@ -425,6 +433,11 @@ 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
@@ -594,38 +607,40 @@ 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)
+pl011_console_write_char(struct uart_amba_port *uap, char ch)
 {
        unsigned int status;
 
        do {
-               status = readw(port->membase + UART01x_FR);
+               status = readw(uap->port.membase + UART01x_FR);
        } while (status & UART01x_FR_TXFF);
-       writew(ch, port->membase + UART01x_DR);
+       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]);
+               pl011_console_write_char(uap, s[i]);
                if (s[i] == '\n')
-                       pl011_console_write_char(port, '\r');
+                       pl011_console_write_char(uap, '\r');
        }
 
        /*
@@ -633,19 +648,21 @@ pl011_console_write(struct console *co, const char *s, unsigned int count)
         *      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 +677,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 +691,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
@@ -685,10 +701,12 @@ static int __init pl011_console_setup(struct console *co, char *options)
                co->index = 0;
        uap = amba_ports[co->index];
 
+       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);
 }
@@ -722,7 +740,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 +765,21 @@ 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;
+       }
+
+       ret = clk_use(uap->clk);
+       if (ret)
+               goto putclk;
+
        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 +792,10 @@ static int pl011_probe(struct amba_device *dev, void *id)
        if (ret) {
                amba_set_drvdata(dev, NULL);
                amba_ports[i] = NULL;
+               clk_unuse(uap->clk);
+ putclk:
+               clk_put(uap->clk);
+ unmap:
                iounmap(base);
  free:
                kfree(uap);
@@ -791,6 +818,8 @@ static int pl011_remove(struct amba_device *dev)
                        amba_ports[i] = NULL;
 
        iounmap(uap->port.membase);
+       clk_unuse(uap->clk);
+       clk_put(uap->clk);
        kfree(uap);
        return 0;
 }