linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / serial / imx.c
index fc2a8f0..4d53fb5 100644 (file)
@@ -22,6 +22,8 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
+ * [29-Mar-2005] Mike Lee
+ * Added hardware handshake
  */
 #include <linux/config.h>
 
@@ -34,7 +36,7 @@
 #include <linux/init.h>
 #include <linux/console.h>
 #include <linux/sysrq.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
 #include <linux/serial_core.h>
@@ -71,7 +73,7 @@ struct imx_port {
        struct uart_port        port;
        struct timer_list       timer;
        unsigned int            old_status;
-       int txirq,rxirq;
+       int txirq,rxirq,rtsirq;
 };
 
 /*
@@ -122,7 +124,7 @@ static void imx_timeout(unsigned long data)
 /*
  * interrupts disabled on entry
  */
-static void imx_stop_tx(struct uart_port *port, unsigned int tty_stop)
+static void imx_stop_tx(struct uart_port *port)
 {
        struct imx_port *sport = (struct imx_port *)port;
        UCR1((u32)sport->port.membase) &= ~UCR1_TXMPTYEN;
@@ -163,13 +165,13 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
        } while (!(UTS((u32)sport->port.membase) & UTS_TXFULL));
 
        if (uart_circ_empty(xmit))
-               imx_stop_tx(&sport->port, 0);
+               imx_stop_tx(&sport->port);
 }
 
 /*
  * interrupts disabled on entry
  */
-static void imx_start_tx(struct uart_port *port, unsigned int tty_start)
+static void imx_start_tx(struct uart_port *port)
 {
        struct imx_port *sport = (struct imx_port *)port;
 
@@ -179,6 +181,22 @@ static void imx_start_tx(struct uart_port *port, unsigned int tty_start)
                imx_transmit_buffer(sport);
 }
 
+static irqreturn_t imx_rtsint(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct imx_port *sport = (struct imx_port *)dev_id;
+       unsigned int val = USR1((u32)sport->port.membase)&USR1_RTSS;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sport->port.lock, flags);
+
+       USR1((u32)sport->port.membase) = USR1_RTSD;
+       uart_handle_cts_change(&sport->port, !!val);
+       wake_up_interruptible(&sport->port.info->delta_msr_wait);
+
+       spin_unlock_irqrestore(&sport->port.lock, flags);
+       return IRQ_HANDLED;
+}
+
 static irqreturn_t imx_txint(int irq, void *dev_id, struct pt_regs *regs)
 {
        struct imx_port *sport = (struct imx_port *)dev_id;
@@ -194,7 +212,7 @@ static irqreturn_t imx_txint(int irq, void *dev_id, struct pt_regs *regs)
        }
 
        if (uart_circ_empty(xmit) || uart_tx_stopped(&sport->port)) {
-               imx_stop_tx(&sport->port, 0);
+               imx_stop_tx(&sport->port);
                goto out;
        }
 
@@ -238,9 +256,6 @@ static irqreturn_t imx_rxint(int irq, void *dev_id, struct pt_regs *regs)
        error_return:
                tty_insert_flip_char(tty, rx, flg);
 
-               if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-                       goto out;
-
        ignore_char:
                rx = URXD0((u32)sport->port.membase);
        } while(rx & URXD_CHARRDY);
@@ -289,13 +304,31 @@ static unsigned int imx_tx_empty(struct uart_port *port)
        return USR2((u32)sport->port.membase) & USR2_TXDC ?  TIOCSER_TEMT : 0;
 }
 
+/*
+ * We have a modem side uart, so the meanings of RTS and CTS are inverted.
+ */
 static unsigned int imx_get_mctrl(struct uart_port *port)
 {
-       return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+        struct imx_port *sport = (struct imx_port *)port;
+        unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
+
+        if (USR1((u32)sport->port.membase) & USR1_RTSS)
+                tmp |= TIOCM_CTS;
+
+        if (UCR2((u32)sport->port.membase) & UCR2_CTS)
+                tmp |= TIOCM_RTS;
+
+        return tmp;
 }
 
 static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
+        struct imx_port *sport = (struct imx_port *)port;
+
+        if (mctrl & TIOCM_RTS)
+                UCR2((u32)sport->port.membase) |= UCR2_CTS;
+        else
+                UCR2((u32)sport->port.membase) &= ~UCR2_CTS;
 }
 
 /*
@@ -319,18 +352,39 @@ static void imx_break_ctl(struct uart_port *port, int break_state)
 #define TXTL 2 /* reset default */
 #define RXTL 1 /* reset default */
 
+static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
+{
+       unsigned int val;
+       unsigned int ufcr_rfdiv;
+
+       /* set receiver / transmitter trigger level.
+        * RFDIV is set such way to satisfy requested uartclk value
+        */
+       val = TXTL<<10 | RXTL;
+       ufcr_rfdiv = (imx_get_perclk1() + sport->port.uartclk / 2) / sport->port.uartclk;
+
+       if(!ufcr_rfdiv)
+               ufcr_rfdiv = 1;
+
+       if(ufcr_rfdiv >= 7)
+               ufcr_rfdiv = 6;
+       else
+               ufcr_rfdiv = 6 - ufcr_rfdiv;
+
+       val |= UFCR_RFDIV & (ufcr_rfdiv << 7);
+
+       UFCR((u32)sport->port.membase) = val;
+
+       return 0;
+}
+
 static int imx_startup(struct uart_port *port)
 {
        struct imx_port *sport = (struct imx_port *)port;
        int retval;
-       unsigned int val;
        unsigned long flags;
 
-       /* set receiver / transmitter trigger level. We assume
-        * that RFDIV has been set by the arch setup or by the bootloader.
-        */
-       val = (UFCR((u32)sport->port.membase) & UFCR_RFDIV)  | TXTL<<10 | RXTL;
-       UFCR((u32)sport->port.membase) = val;
+       imx_setup_ufcr(sport, 0);
 
        /* disable the DREN bit (Data Ready interrupt enable) before
         * requesting IRQs
@@ -342,18 +396,24 @@ static int imx_startup(struct uart_port *port)
         */
        retval = request_irq(sport->rxirq, imx_rxint, 0,
                             DRIVER_NAME, sport);
-       if (retval) goto error_out2;
+       if (retval) goto error_out1;
 
        retval = request_irq(sport->txirq, imx_txint, 0,
-                            "imx-uart", sport);
-       if (retval) goto error_out1;
+                            DRIVER_NAME, sport);
+       if (retval) goto error_out2;
+
+       retval = request_irq(sport->rtsirq, imx_rtsint,
+                            SA_TRIGGER_FALLING | SA_TRIGGER_RISING,
+                            DRIVER_NAME, sport);
+       if (retval) goto error_out3;
 
        /*
         * Finally, clear and enable interrupts
         */
 
+       USR1((u32)sport->port.membase) = USR1_RTSD;
        UCR1((u32)sport->port.membase) |=
-                        (UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_UARTEN);
+                        (UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
 
        UCR2((u32)sport->port.membase) |= (UCR2_RXEN | UCR2_TXEN);
        /*
@@ -365,10 +425,11 @@ static int imx_startup(struct uart_port *port)
 
        return 0;
 
-error_out1:
-       free_irq(sport->rxirq, sport);
-error_out2:
+error_out3:
        free_irq(sport->txirq, sport);
+error_out2:
+       free_irq(sport->rxirq, sport);
+error_out1:
        return retval;
 }
 
@@ -384,6 +445,7 @@ static void imx_shutdown(struct uart_port *port)
        /*
         * Free the interrupts
         */
+       free_irq(sport->rtsirq, sport);
        free_irq(sport->txirq, sport);
        free_irq(sport->rxirq, sport);
 
@@ -392,7 +454,7 @@ static void imx_shutdown(struct uart_port *port)
         */
 
        UCR1((u32)sport->port.membase) &=
-                        ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_UARTEN);
+                        ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
 }
 
 static void
@@ -428,11 +490,16 @@ imx_set_termios(struct uart_port *port, struct termios *termios,
        else
                ucr2 = UCR2_SRST | UCR2_IRTS;
 
+       if (termios->c_cflag & CRTSCTS) {
+               ucr2 &= ~UCR2_IRTS;
+               ucr2 |= UCR2_CTSC;
+       }
+
        if (termios->c_cflag & CSTOPB)
                ucr2 |= UCR2_STPB;
        if (termios->c_cflag & PARENB) {
                ucr2 |= UCR2_PREN;
-               if (!(termios->c_cflag & PARODD))
+               if (termios->c_cflag & PARODD)
                        ucr2 |= UCR2_PROE;
        }
 
@@ -477,7 +544,7 @@ imx_set_termios(struct uart_port *port, struct termios *termios,
         * disable interrupts and drain transmitter
         */
        old_ucr1 = UCR1((u32)sport->port.membase);
-       UCR1((u32)sport->port.membase) &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN);
+       UCR1((u32)sport->port.membase) &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
 
        while ( !(USR2((u32)sport->port.membase) & USR2_TXDC))
                barrier();
@@ -598,30 +665,32 @@ static struct imx_port imx_ports[] = {
        {
        .txirq  = UART1_MINT_TX,
        .rxirq  = UART1_MINT_RX,
+       .rtsirq = UART1_MINT_RTS,
        .port   = {
                .type           = PORT_IMX,
-               .iotype         = SERIAL_IO_MEM,
+               .iotype         = UPIO_MEM,
                .membase        = (void *)IMX_UART1_BASE,
                .mapbase        = IMX_UART1_BASE, /* FIXME */
                .irq            = UART1_MINT_RX,
                .uartclk        = 16000000,
                .fifosize       = 8,
-               .flags          = ASYNC_BOOT_AUTOCONF,
+               .flags          = UPF_BOOT_AUTOCONF,
                .ops            = &imx_pops,
                .line           = 0,
        },
        }, {
        .txirq  = UART2_MINT_TX,
        .rxirq  = UART2_MINT_RX,
+       .rtsirq = UART2_MINT_RTS,
        .port   = {
                .type           = PORT_IMX,
-               .iotype         = SERIAL_IO_MEM,
+               .iotype         = UPIO_MEM,
                .membase        = (void *)IMX_UART2_BASE,
                .mapbase        = IMX_UART2_BASE, /* FIXME */
                .irq            = UART2_MINT_RX,
                .uartclk        = 16000000,
                .fifosize       = 8,
-               .flags          = ASYNC_BOOT_AUTOCONF,
+               .flags          = UPF_BOOT_AUTOCONF,
                .ops            = &imx_pops,
                .line           = 1,
        },
@@ -692,7 +761,7 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
 
        UCR1((u32)sport->port.membase) =
                           (old_ucr1 | UCR1_UARTCLKEN | UCR1_UARTEN)
-                          & ~(UCR1_TXMPTYEN | UCR1_RRDYEN);
+                          & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
        UCR2((u32)sport->port.membase) = old_ucr2 | UCR2_TXEN;
 
        /*
@@ -730,9 +799,12 @@ static void __init
 imx_console_get_options(struct imx_port *sport, int *baud,
                           int *parity, int *bits)
 {
+
        if ( UCR1((u32)sport->port.membase) | UCR1_UARTEN ) {
                /* ok, the port was enabled */
                unsigned int ucr2, ubir,ubmr, uartclk;
+               unsigned int baud_raw;
+               unsigned int ucfr_rfdiv;
 
                ucr2 = UCR2((u32)sport->port.membase);
 
@@ -751,9 +823,35 @@ imx_console_get_options(struct imx_port *sport, int *baud,
 
                ubir = UBIR((u32)sport->port.membase) & 0xffff;
                ubmr = UBMR((u32)sport->port.membase) & 0xffff;
-               uartclk = sport->port.uartclk;
 
-               *baud = ((uartclk/16) * (ubir + 1)) / (ubmr + 1);
+
+               ucfr_rfdiv = (UFCR((u32)sport->port.membase) & UFCR_RFDIV) >> 7;
+               if (ucfr_rfdiv == 6)
+                       ucfr_rfdiv = 7;
+               else
+                       ucfr_rfdiv = 6 - ucfr_rfdiv;
+
+               uartclk = imx_get_perclk1();
+               uartclk /= ucfr_rfdiv;
+
+               {       /*
+                        * The next code provides exact computation of
+                        *   baud_raw = round(((uartclk/16) * (ubir + 1)) / (ubmr + 1))
+                        * without need of float support or long long division,
+                        * which would be required to prevent 32bit arithmetic overflow
+                        */
+                       unsigned int mul = ubir + 1;
+                       unsigned int div = 16 * (ubmr + 1);
+                       unsigned int rem = uartclk % div;
+
+                       baud_raw = (uartclk / div) * mul;
+                       baud_raw += (rem * mul + div / 2) / div;
+                       *baud = (baud_raw + 50) / 100 * 100;
+               }
+
+               if(*baud != baud_raw)
+                       printk(KERN_INFO "Serial: Console IMX rounded baud rate from %d to %d\n",
+                               baud_raw, *baud);
        }
 }
 
@@ -780,10 +878,12 @@ imx_console_setup(struct console *co, char *options)
        else
                imx_console_get_options(sport, &baud, &parity, &bits);
 
+       imx_setup_ufcr(sport, 0);
+
        return uart_set_options(&sport->port, co, baud, parity, bits, flow);
 }
 
-extern struct uart_driver imx_reg;
+static struct uart_driver imx_reg;
 static struct console imx_console = {
        .name           = "ttySMX",
        .write          = imx_console_write,
@@ -818,41 +918,39 @@ static struct uart_driver imx_reg = {
        .cons           = IMX_CONSOLE,
 };
 
-static int serial_imx_suspend(struct device *_dev, u32 state, u32 level)
+static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
 {
-        struct imx_port *sport = dev_get_drvdata(_dev);
+        struct imx_port *sport = platform_get_drvdata(dev);
 
-        if (sport && level == SUSPEND_DISABLE)
+        if (sport)
                 uart_suspend_port(&imx_reg, &sport->port);
 
         return 0;
 }
 
-static int serial_imx_resume(struct device *_dev, u32 level)
+static int serial_imx_resume(struct platform_device *dev)
 {
-        struct imx_port *sport = dev_get_drvdata(_dev);
+        struct imx_port *sport = platform_get_drvdata(dev);
 
-        if (sport && level == RESUME_ENABLE)
+        if (sport)
                 uart_resume_port(&imx_reg, &sport->port);
 
         return 0;
 }
 
-static int serial_imx_probe(struct device *_dev)
+static int serial_imx_probe(struct platform_device *dev)
 {
-       struct platform_device *dev = to_platform_device(_dev);
-
-       imx_ports[dev->id].port.dev = _dev;
+       imx_ports[dev->id].port.dev = &dev->dev;
        uart_add_one_port(&imx_reg, &imx_ports[dev->id].port);
-       dev_set_drvdata(_dev, &imx_ports[dev->id]);
+       platform_set_drvdata(dev, &imx_ports[dev->id]);
        return 0;
 }
 
-static int serial_imx_remove(struct device *_dev)
+static int serial_imx_remove(struct platform_device *dev)
 {
-       struct imx_port *sport = dev_get_drvdata(_dev);
+       struct imx_port *sport = platform_get_drvdata(dev);
 
-       dev_set_drvdata(_dev, NULL);
+       platform_set_drvdata(dev, NULL);
 
        if (sport)
                uart_remove_one_port(&imx_reg, &sport->port);
@@ -860,14 +958,15 @@ static int serial_imx_remove(struct device *_dev)
        return 0;
 }
 
-static struct device_driver serial_imx_driver = {
-        .name           = "imx-uart",
-        .bus            = &platform_bus_type,
+static struct platform_driver serial_imx_driver = {
         .probe          = serial_imx_probe,
         .remove         = serial_imx_remove,
 
        .suspend        = serial_imx_suspend,
        .resume         = serial_imx_resume,
+       .driver         = {
+               .name   = "imx-uart",
+       },
 };
 
 static int __init imx_serial_init(void)
@@ -882,7 +981,7 @@ static int __init imx_serial_init(void)
        if (ret)
                return ret;
 
-       ret = driver_register(&serial_imx_driver);
+       ret = platform_driver_register(&serial_imx_driver);
        if (ret != 0)
                uart_unregister_driver(&imx_reg);
 
@@ -892,6 +991,7 @@ static int __init imx_serial_init(void)
 static void __exit imx_serial_exit(void)
 {
        uart_unregister_driver(&imx_reg);
+       platform_driver_unregister(&serial_imx_driver);
 }
 
 module_init(imx_serial_init);