imx: serial: use rational library function
[pandora-kernel.git] / drivers / serial / imx.c
index 5f0be40..0de81f7 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/serial_core.h>
 #include <linux/serial.h>
 #include <linux/clk.h>
+#include <linux/rational.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -211,7 +212,7 @@ struct imx_port {
        struct timer_list       timer;
        unsigned int            old_status;
        int                     txirq,rxirq,rtsirq;
-       int                     have_rtscts:1;
+       unsigned int            have_rtscts:1;
        struct clk              *clk;
 };
 
@@ -302,13 +303,15 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
                /* send xmit->buf[xmit->tail]
                 * out the port here */
                writel(xmit->buf[xmit->tail], sport->port.membase + URTX0);
-               xmit->tail = (xmit->tail + 1) &
-                        (UART_XMIT_SIZE - 1);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
                sport->port.icount.tx++;
                if (uart_circ_empty(xmit))
                        break;
        }
 
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(&sport->port);
+
        if (uart_circ_empty(xmit))
                imx_stop_tx(&sport->port);
 }
@@ -395,8 +398,7 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
                                continue;
                }
 
-               if (uart_handle_sysrq_char
-                           (&sport->port, (unsigned char)rx))
+               if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
                        continue;
 
                if (rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR) ) {
@@ -471,26 +473,26 @@ static unsigned int imx_tx_empty(struct uart_port *port)
  */
 static unsigned int imx_get_mctrl(struct uart_port *port)
 {
-        struct imx_port *sport = (struct imx_port *)port;
-        unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
+       struct imx_port *sport = (struct imx_port *)port;
+       unsigned int tmp = TIOCM_DSR | TIOCM_CAR;
 
-        if (readl(sport->port.membase + USR1) & USR1_RTSS)
-                tmp |= TIOCM_CTS;
+       if (readl(sport->port.membase + USR1) & USR1_RTSS)
+               tmp |= TIOCM_CTS;
 
-        if (readl(sport->port.membase + UCR2) & UCR2_CTS)
-                tmp |= TIOCM_RTS;
+       if (readl(sport->port.membase + UCR2) & UCR2_CTS)
+               tmp |= TIOCM_RTS;
 
-        return tmp;
+       return tmp;
 }
 
 static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
 {
-        struct imx_port *sport = (struct imx_port *)port;
+       struct imx_port *sport = (struct imx_port *)port;
        unsigned long temp;
 
        temp = readl(sport->port.membase + UCR2) & ~UCR2_CTS;
 
-        if (mctrl & TIOCM_RTS)
+       if (mctrl & TIOCM_RTS)
                temp |= UCR2_CTS;
 
        writel(temp, sport->port.membase + UCR2);
@@ -633,6 +635,10 @@ static void imx_shutdown(struct uart_port *port)
        struct imx_port *sport = (struct imx_port *)port;
        unsigned long temp;
 
+       temp = readl(sport->port.membase + UCR2);
+       temp &= ~(UCR2_TXEN);
+       writel(temp, sport->port.membase + UCR2);
+
        /*
         * Stop our timer.
         */
@@ -665,7 +671,8 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
        unsigned long flags;
        unsigned int ucr2, old_ucr1, old_txrxen, baud, quot;
        unsigned int old_csize = old ? old->c_cflag & CSIZE : CS8;
-       unsigned int div, num, denom, ufcr;
+       unsigned int div, ufcr;
+       unsigned long num, denom;
 
        /*
         * If we don't support modem control lines, don't allow
@@ -767,32 +774,20 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
        if (!div)
                div = 1;
 
-       num = baud;
-       denom = port->uartclk / div / 16;
-
-       /* shift num and denom right until they fit into 16 bits */
-       while (num > 0x10000 || denom > 0x10000) {
-               num >>= 1;
-               denom >>= 1;
-       }
-       if (num > 0)
-               num -= 1;
-       if (denom > 0)
-               denom -= 1;
-
-       writel(num, sport->port.membase + UBIR);
-       writel(denom, sport->port.membase + UBMR);
+       rational_best_approximation(16 * div * baud, sport->port.uartclk,
+               1 << 16, 1 << 16, &num, &denom);
 
-       if (div == 7)
-               div = 6; /* 6 in RFDIV means divide by 7 */
-       else
-               div = 6 - div;
+       num -= 1;
+       denom -= 1;
 
        ufcr = readl(sport->port.membase + UFCR);
        ufcr = (ufcr & (~UFCR_RFDIV)) |
            (div << 7);
        writel(ufcr, sport->port.membase + UFCR);
 
+       writel(num, sport->port.membase + UBIR);
+       writel(denom, sport->port.membase + UBMR);
+
 #ifdef ONEMS
        writel(sport->port.uartclk / div / 1000, sport->port.membase + ONEMS);
 #endif
@@ -1072,22 +1067,22 @@ static struct uart_driver imx_reg = {
 
 static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
 {
-        struct imx_port *sport = platform_get_drvdata(dev);
+       struct imx_port *sport = platform_get_drvdata(dev);
 
-        if (sport)
-                uart_suspend_port(&imx_reg, &sport->port);
+       if (sport)
+               uart_suspend_port(&imx_reg, &sport->port);
 
-        return 0;
+       return 0;
 }
 
 static int serial_imx_resume(struct platform_device *dev)
 {
-        struct imx_port *sport = platform_get_drvdata(dev);
+       struct imx_port *sport = platform_get_drvdata(dev);
 
-        if (sport)
-                uart_resume_port(&imx_reg, &sport->port);
+       if (sport)
+               uart_resume_port(&imx_reg, &sport->port);
 
-        return 0;
+       return 0;
 }
 
 static int serial_imx_probe(struct platform_device *pdev)
@@ -1143,7 +1138,7 @@ static int serial_imx_probe(struct platform_device *pdev)
        imx_ports[pdev->id] = sport;
 
        pdata = pdev->dev.platform_data;
-       if(pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
+       if (pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
                sport->have_rtscts = 1;
 
        if (pdata->init) {
@@ -1152,10 +1147,15 @@ static int serial_imx_probe(struct platform_device *pdev)
                        goto clkput;
        }
 
-       uart_add_one_port(&imx_reg, &sport->port);
+       ret = uart_add_one_port(&imx_reg, &sport->port);
+       if (ret)
+               goto deinit;
        platform_set_drvdata(pdev, &sport->port);
 
        return 0;
+deinit:
+       if (pdata->exit)
+               pdata->exit(pdev);
 clkput:
        clk_put(sport->clk);
        clk_disable(sport->clk);
@@ -1193,13 +1193,13 @@ static int serial_imx_remove(struct platform_device *pdev)
 }
 
 static struct platform_driver serial_imx_driver = {
-        .probe          = serial_imx_probe,
-        .remove         = serial_imx_remove,
+       .probe          = serial_imx_probe,
+       .remove         = serial_imx_remove,
 
        .suspend        = serial_imx_suspend,
        .resume         = serial_imx_resume,
        .driver         = {
-               .name   = "imx-uart",
+               .name   = "imx-uart",
                .owner  = THIS_MODULE,
        },
 };