perf/trace/scripting: rw-by-pid script cleanup
[safe/jmp/linux-2.6] / drivers / serial / amba-pl011.c
index 8b5aa0b..743ebf5 100644 (file)
@@ -47,6 +47,7 @@
 #include <linux/amba/bus.h>
 #include <linux/amba/serial.h>
 #include <linux/clk.h>
+#include <linux/slab.h>
 
 #include <asm/io.h>
 #include <asm/sizes.h>
@@ -70,6 +71,24 @@ struct uart_amba_port {
        struct clk              *clk;
        unsigned int            im;     /* interrupt mask */
        unsigned int            old_status;
+       unsigned int            ifls;   /* vendor-specific */
+       bool                    autorts;
+};
+
+/* There is by now at least one vendor with differing details, so handle it */
+struct vendor_data {
+       unsigned int            ifls;
+       unsigned int            fifosize;
+};
+
+static struct vendor_data vendor_arm = {
+       .ifls                   = UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
+       .fifosize               = 16,
+};
+
+static struct vendor_data vendor_st = {
+       .ifls                   = UART011_IFLS_RX_HALF|UART011_IFLS_TX_HALF,
+       .fifosize               = 64,
 };
 
 static void pl011_stop_tx(struct uart_port *port)
@@ -107,7 +126,7 @@ static void pl011_enable_ms(struct uart_port *port)
 
 static void pl011_rx_chars(struct uart_amba_port *uap)
 {
-       struct tty_struct *tty = uap->port.info->tty;
+       struct tty_struct *tty = uap->port.state->port.tty;
        unsigned int status, ch, flag, max_count = 256;
 
        status = readw(uap->port.membase + UART01x_FR);
@@ -158,7 +177,7 @@ static void pl011_rx_chars(struct uart_amba_port *uap)
 
 static void pl011_tx_chars(struct uart_amba_port *uap)
 {
-       struct circ_buf *xmit = &uap->port.info->xmit;
+       struct circ_buf *xmit = &uap->port.state->xmit;
        int count;
 
        if (uap->port.x_char) {
@@ -209,7 +228,7 @@ static void pl011_modem_status(struct uart_amba_port *uap)
        if (delta & UART01x_FR_CTS)
                uart_handle_cts_change(&uap->port, status & UART01x_FR_CTS);
 
-       wake_up_interruptible(&uap->port.info->delta_msr_wait);
+       wake_up_interruptible(&uap->port.state->port.delta_msr_wait);
 }
 
 static irqreturn_t pl011_int(int irq, void *dev_id)
@@ -291,6 +310,11 @@ static void pl011_set_mctrl(struct uart_port *port, unsigned int mctrl)
        TIOCMBIT(TIOCM_OUT1, UART011_CR_OUT1);
        TIOCMBIT(TIOCM_OUT2, UART011_CR_OUT2);
        TIOCMBIT(TIOCM_LOOP, UART011_CR_LBE);
+
+       if (uap->autorts) {
+               /* We need to disable auto-RTS if we want to turn RTS off */
+               TIOCMBIT(TIOCM_RTS, UART011_CR_RTSEN);
+       }
 #undef TIOCMBIT
 
        writew(cr, uap->port.membase + UART011_CR);
@@ -360,8 +384,7 @@ static int pl011_startup(struct uart_port *port)
        if (retval)
                goto clk_dis;
 
-       writew(UART011_IFLS_RX4_8|UART011_IFLS_TX4_8,
-              uap->port.membase + UART011_IFLS);
+       writew(uap->ifls, uap->port.membase + UART011_IFLS);
 
        /*
         * Provoke TX FIFO interrupt into asserting.
@@ -421,6 +444,7 @@ static void pl011_shutdown(struct uart_port *port)
        /*
         * disable the port
         */
+       uap->autorts = false;
        writew(UART01x_CR_UARTEN | UART011_CR_TXE, uap->port.membase + UART011_CR);
 
        /*
@@ -440,6 +464,7 @@ static void
 pl011_set_termios(struct uart_port *port, struct ktermios *termios,
                     struct ktermios *old)
 {
+       struct uart_amba_port *uap = (struct uart_amba_port *)port;
        unsigned int lcr_h, old_cr;
        unsigned long flags;
        unsigned int baud, quot;
@@ -516,6 +541,17 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
        old_cr = readw(port->membase + UART011_CR);
        writew(0, port->membase + UART011_CR);
 
+       if (termios->c_cflag & CRTSCTS) {
+               if (old_cr & UART011_CR_RTS)
+                       old_cr |= UART011_CR_RTSEN;
+
+               old_cr |= UART011_CR_CTSEN;
+               uap->autorts = true;
+       } else {
+               old_cr &= ~(UART011_CR_CTSEN | UART011_CR_RTSEN);
+               uap->autorts = false;
+       }
+
        /* Set baud rate */
        writew(quot & 0x3f, port->membase + UART011_FBRD);
        writew(quot >> 6, port->membase + UART011_IBRD);
@@ -572,7 +608,7 @@ static int pl010_verify_port(struct uart_port *port, struct serial_struct *ser)
        int ret = 0;
        if (ser->type != PORT_UNKNOWN && ser->type != PORT_AMBA)
                ret = -EINVAL;
-       if (ser->irq < 0 || ser->irq >= NR_IRQS)
+       if (ser->irq < 0 || ser->irq >= nr_irqs)
                ret = -EINVAL;
        if (ser->baud_base < 9600)
                ret = -EINVAL;
@@ -729,9 +765,10 @@ static struct uart_driver amba_reg = {
        .cons                   = AMBA_CONSOLE,
 };
 
-static int pl011_probe(struct amba_device *dev, void *id)
+static int pl011_probe(struct amba_device *dev, struct amba_id *id)
 {
        struct uart_amba_port *uap;
+       struct vendor_data *vendor = id->data;
        void __iomem *base;
        int i, ret;
 
@@ -750,24 +787,25 @@ static int pl011_probe(struct amba_device *dev, void *id)
                goto out;
        }
 
-       base = ioremap(dev->res.start, PAGE_SIZE);
+       base = ioremap(dev->res.start, resource_size(&dev->res));
        if (!base) {
                ret = -ENOMEM;
                goto free;
        }
 
-       uap->clk = clk_get(&dev->dev, "UARTCLK");
+       uap->clk = clk_get(&dev->dev, NULL);
        if (IS_ERR(uap->clk)) {
                ret = PTR_ERR(uap->clk);
                goto unmap;
        }
 
+       uap->ifls = vendor->ifls;
        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];
-       uap->port.fifosize = 16;
+       uap->port.fifosize = vendor->fifosize;
        uap->port.ops = &amba_pl011_pops;
        uap->port.flags = UPF_BOOT_AUTOCONF;
        uap->port.line = i;
@@ -808,10 +846,38 @@ static int pl011_remove(struct amba_device *dev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int pl011_suspend(struct amba_device *dev, pm_message_t state)
+{
+       struct uart_amba_port *uap = amba_get_drvdata(dev);
+
+       if (!uap)
+               return -EINVAL;
+
+       return uart_suspend_port(&amba_reg, &uap->port);
+}
+
+static int pl011_resume(struct amba_device *dev)
+{
+       struct uart_amba_port *uap = amba_get_drvdata(dev);
+
+       if (!uap)
+               return -EINVAL;
+
+       return uart_resume_port(&amba_reg, &uap->port);
+}
+#endif
+
 static struct amba_id pl011_ids[] __initdata = {
        {
                .id     = 0x00041011,
                .mask   = 0x000fffff,
+               .data   = &vendor_arm,
+       },
+       {
+               .id     = 0x00380802,
+               .mask   = 0x00ffffff,
+               .data   = &vendor_st,
        },
        { 0, 0 },
 };
@@ -823,6 +889,10 @@ static struct amba_driver pl011_driver = {
        .id_table       = pl011_ids,
        .probe          = pl011_probe,
        .remove         = pl011_remove,
+#ifdef CONFIG_PM
+       .suspend        = pl011_suspend,
+       .resume         = pl011_resume,
+#endif
 };
 
 static int __init pl011_init(void)
@@ -845,7 +915,11 @@ static void __exit pl011_exit(void)
        uart_unregister_driver(&amba_reg);
 }
 
-module_init(pl011_init);
+/*
+ * While this can be a module, if builtin it's most likely the console
+ * So let's leave module_exit but move module_init to an earlier place
+ */
+arch_initcall(pl011_init);
 module_exit(pl011_exit);
 
 MODULE_AUTHOR("ARM Ltd/Deep Blue Solutions Ltd");