Merge branch 'for-rmk' of git://git.pengutronix.de/git/imx/linux-2.6 into devel-stable
[safe/jmp/linux-2.6] / drivers / serial / imx.c
index db3a2df..b801f5a 100644 (file)
@@ -8,6 +8,9 @@
  *  Author: Sascha Hauer <sascha@saschahauer.de>
  *  Copyright (C) 2004 Pengutronix
  *
+ *  Copyright (C) 2009 emlix GmbH
+ *  Author: Fabian Godehardt (added IrDA support for iMX)
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
 #include <linux/serial_core.h>
 #include <linux/serial.h>
 #include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/rational.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
-#include <asm/arch/hardware.h>
-#include <asm/arch/imx-uart.h>
+#include <mach/hardware.h>
+#include <mach/imx-uart.h>
 
 /* Register definitions */
 #define URXD0 0x0  /* Receiver Register */
 #define UBIR  0xa4 /* BRM Incremental Register */
 #define UBMR  0xa8 /* BRM Modulator Register */
 #define UBRC  0xac /* Baud Rate Count Register */
-#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2
-#define ONEMS 0xb0 /* One Millisecond register */
-#define UTS   0xb4 /* UART Test Register */
-#endif
-#ifdef CONFIG_ARCH_IMX
-#define BIPR1 0xb0 /* Incremental Preset Register 1 */
-#define BIPR2 0xb4 /* Incremental Preset Register 2 */
-#define BIPR3 0xb8 /* Incremental Preset Register 3 */
-#define BIPR4 0xbc /* Incremental Preset Register 4 */
-#define BMPR1 0xc0 /* BRM Modulator Register 1 */
-#define BMPR2 0xc4 /* BRM Modulator Register 2 */
-#define BMPR3 0xc8 /* BRM Modulator Register 3 */
-#define BMPR4 0xcc /* BRM Modulator Register 4 */
-#define UTS   0xd0 /* UART Test Register */
-#endif
+#define MX2_ONEMS 0xb0 /* One Millisecond register */
+#define UTS (cpu_is_mx1() ? 0xd0 : 0xb4) /* UART Test Register */
 
 /* UART Control Register Bit Fields.*/
 #define  URXD_CHARRDY    (1<<15)
 #define  UCR1_RTSDEN     (1<<5)         /* RTS delta interrupt enable */
 #define  UCR1_SNDBRK     (1<<4)         /* Send break */
 #define  UCR1_TDMAEN     (1<<3)         /* Transmitter ready DMA enable */
-#ifdef CONFIG_ARCH_IMX
-#define  UCR1_UARTCLKEN  (1<<2)         /* UART clock enabled */
-#endif
-#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2
-#define  UCR1_UARTCLKEN  (0)    /* not present on mx2/mx3 */
-#endif
+#define  MX1_UCR1_UARTCLKEN  (1<<2)     /* UART clock enabled, mx1 only */
 #define  UCR1_DOZE       (1<<1)         /* Doze */
 #define  UCR1_UARTEN     (1<<0)         /* UART enabled */
 #define  UCR2_ESCI              (1<<15) /* Escape seq interrupt enable */
 #define  UCR3_RXDSEN    (1<<6)  /* Receive status interrupt enable */
 #define  UCR3_AIRINTEN   (1<<5)  /* Async IR wake interrupt enable */
 #define  UCR3_AWAKEN    (1<<4)  /* Async wake interrupt enable */
-#define  UCR3_REF25     (1<<3)  /* Ref freq 25 MHz */
-#define  UCR3_REF30     (1<<2)  /* Ref Freq 30 MHz */
+#define  MX1_UCR3_REF25         (1<<3)  /* Ref freq 25 MHz, only on mx1 */
+#define  MX1_UCR3_REF30         (1<<2)  /* Ref Freq 30 MHz, only on mx1 */
+#define  MX2_UCR3_RXDMUXSEL     (1<<2)  /* RXD Muxed Input Select, on mx2/mx3 */
 #define  UCR3_INVT      (1<<1)  /* Inverted Infrared transmission */
 #define  UCR3_BPEN      (1<<0)  /* Preset registers enable */
 #define  UCR4_CTSTL_32   (32<<10) /* CTS trigger level (32 chars) */
 #define  UCR4_DREN      (1<<0)  /* Recv data ready interrupt enable */
 #define  UFCR_RXTL_SHF   0       /* Receiver trigger level shift */
 #define  UFCR_RFDIV      (7<<7)  /* Reference freq divider mask */
+#define  UFCR_RFDIV_REG(x)     (((x) < 7 ? 6 - (x) : 6) << 7)
 #define  UFCR_TXTL_SHF   10      /* Transmitter trigger level shift */
 #define  USR1_PARITYERR  (1<<15) /* Parity error interrupt flag */
 #define  USR1_RTSS      (1<<14) /* RTS pin status */
 #define  UTS_SOFTRST    (1<<0)  /* Software reset */
 
 /* We've been assigned a range on the "Low-density serial ports" major */
-#ifdef CONFIG_ARCH_IMX
-#define SERIAL_IMX_MAJOR       204
-#define MINOR_START            41
-#define DEV_NAME               "ttySMX"
-#define MAX_INTERNAL_IRQ       IMX_IRQS
-#endif
-
-#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2
 #define SERIAL_IMX_MAJOR        207
 #define MINOR_START            16
 #define DEV_NAME               "ttymxc"
-#define MAX_INTERNAL_IRQ       MXC_MAX_INT_LINES
-#endif
+#define MAX_INTERNAL_IRQ       MXC_INTERNAL_IRQS
 
 /*
  * This determines how often we check the modem status signals
@@ -206,10 +186,20 @@ struct imx_port {
        struct timer_list       timer;
        unsigned int            old_status;
        int                     txirq,rxirq,rtsirq;
-       int                     have_rtscts:1;
+       unsigned int            have_rtscts:1;
+       unsigned int            use_irda:1;
+       unsigned int            irda_inv_rx:1;
+       unsigned int            irda_inv_tx:1;
+       unsigned short          trcv_delay; /* transceiver delay */
        struct clk              *clk;
 };
 
+#ifdef CONFIG_IRDA
+#define USE_IRDA(sport)        ((sport)->use_irda)
+#else
+#define USE_IRDA(sport)        (0)
+#endif
+
 /*
  * Handle any change of modem status signal since we were last called.
  */
@@ -234,7 +224,7 @@ static void imx_mctrl_check(struct imx_port *sport)
        if (changed & TIOCM_CTS)
                uart_handle_cts_change(&sport->port, status & TIOCM_CTS);
 
-       wake_up_interruptible(&sport->port.info->delta_msr_wait);
+       wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
 }
 
 /*
@@ -246,7 +236,7 @@ static void imx_timeout(unsigned long data)
        struct imx_port *sport = (struct imx_port *)data;
        unsigned long flags;
 
-       if (sport->port.info) {
+       if (sport->port.state) {
                spin_lock_irqsave(&sport->port.lock, flags);
                imx_mctrl_check(sport);
                spin_unlock_irqrestore(&sport->port.lock, flags);
@@ -263,6 +253,48 @@ static void imx_stop_tx(struct uart_port *port)
        struct imx_port *sport = (struct imx_port *)port;
        unsigned long temp;
 
+       if (USE_IRDA(sport)) {
+               /* half duplex - wait for end of transmission */
+               int n = 256;
+               while ((--n > 0) &&
+                     !(readl(sport->port.membase + USR2) & USR2_TXDC)) {
+                       udelay(5);
+                       barrier();
+               }
+               /*
+                * irda transceiver - wait a bit more to avoid
+                * cutoff, hardware dependent
+                */
+               udelay(sport->trcv_delay);
+
+               /*
+                * half duplex - reactivate receive mode,
+                * flush receive pipe echo crap
+                */
+               if (readl(sport->port.membase + USR2) & USR2_TXDC) {
+                       temp = readl(sport->port.membase + UCR1);
+                       temp &= ~(UCR1_TXMPTYEN | UCR1_TRDYEN);
+                       writel(temp, sport->port.membase + UCR1);
+
+                       temp = readl(sport->port.membase + UCR4);
+                       temp &= ~(UCR4_TCEN);
+                       writel(temp, sport->port.membase + UCR4);
+
+                       while (readl(sport->port.membase + URXD0) &
+                              URXD_CHARRDY)
+                               barrier();
+
+                       temp = readl(sport->port.membase + UCR1);
+                       temp |= UCR1_RRDYEN;
+                       writel(temp, sport->port.membase + UCR1);
+
+                       temp = readl(sport->port.membase + UCR4);
+                       temp |= UCR4_DREN;
+                       writel(temp, sport->port.membase + UCR4);
+               }
+               return;
+       }
+
        temp = readl(sport->port.membase + UCR1);
        writel(temp & ~UCR1_TXMPTYEN, sport->port.membase + UCR1);
 }
@@ -291,19 +323,21 @@ static void imx_enable_ms(struct uart_port *port)
 
 static inline void imx_transmit_buffer(struct imx_port *sport)
 {
-       struct circ_buf *xmit = &sport->port.info->xmit;
+       struct circ_buf *xmit = &sport->port.state->xmit;
 
        while (!(readl(sport->port.membase + UTS) & UTS_TXFULL)) {
                /* 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);
 }
@@ -316,9 +350,30 @@ static void imx_start_tx(struct uart_port *port)
        struct imx_port *sport = (struct imx_port *)port;
        unsigned long temp;
 
+       if (USE_IRDA(sport)) {
+               /* half duplex in IrDA mode; have to disable receive mode */
+               temp = readl(sport->port.membase + UCR4);
+               temp &= ~(UCR4_DREN);
+               writel(temp, sport->port.membase + UCR4);
+
+               temp = readl(sport->port.membase + UCR1);
+               temp &= ~(UCR1_RRDYEN);
+               writel(temp, sport->port.membase + UCR1);
+       }
+
        temp = readl(sport->port.membase + UCR1);
        writel(temp | UCR1_TXMPTYEN, sport->port.membase + UCR1);
 
+       if (USE_IRDA(sport)) {
+               temp = readl(sport->port.membase + UCR1);
+               temp |= UCR1_TRDYEN;
+               writel(temp, sport->port.membase + UCR1);
+
+               temp = readl(sport->port.membase + UCR4);
+               temp |= UCR4_TCEN;
+               writel(temp, sport->port.membase + UCR4);
+       }
+
        if (readl(sport->port.membase + UTS) & UTS_TXEMPTY)
                imx_transmit_buffer(sport);
 }
@@ -333,7 +388,7 @@ static irqreturn_t imx_rtsint(int irq, void *dev_id)
 
        writel(USR1_RTSD, sport->port.membase + USR1);
        uart_handle_cts_change(&sport->port, !!val);
-       wake_up_interruptible(&sport->port.info->delta_msr_wait);
+       wake_up_interruptible(&sport->port.state->port.delta_msr_wait);
 
        spin_unlock_irqrestore(&sport->port.lock, flags);
        return IRQ_HANDLED;
@@ -342,7 +397,7 @@ static irqreturn_t imx_rtsint(int irq, void *dev_id)
 static irqreturn_t imx_txint(int irq, void *dev_id)
 {
        struct imx_port *sport = dev_id;
-       struct circ_buf *xmit = &sport->port.info->xmit;
+       struct circ_buf *xmit = &sport->port.state->xmit;
        unsigned long flags;
 
        spin_lock_irqsave(&sport->port.lock,flags);
@@ -372,7 +427,7 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
 {
        struct imx_port *sport = dev_id;
        unsigned int rx,flg,ignored = 0;
-       struct tty_struct *tty = sport->port.info->port.tty;
+       struct tty_struct *tty = sport->port.state->port.tty;
        unsigned long flags, temp;
 
        spin_lock_irqsave(&sport->port.lock,flags);
@@ -385,13 +440,12 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
 
                temp = readl(sport->port.membase + USR2);
                if (temp & USR2_BRCD) {
-                       writel(temp | USR2_BRCD, sport->port.membase + USR2);
+                       writel(USR2_BRCD, sport->port.membase + USR2);
                        if (uart_handle_break(&sport->port))
                                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) ) {
@@ -445,7 +499,7 @@ static irqreturn_t imx_int(int irq, void *dev_id)
                        readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
                imx_txint(irq, dev_id);
 
-       if (sts & USR1_RTSS)
+       if (sts & USR1_RTSD)
                imx_rtsint(irq, dev_id);
 
        return IRQ_HANDLED;
@@ -466,26 +520,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);
@@ -529,12 +583,7 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
        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);
+       val |= UFCR_RFDIV_REG(ufcr_rfdiv);
 
        writel(val, sport->port.membase + UFCR);
 
@@ -553,8 +602,24 @@ static int imx_startup(struct uart_port *port)
         * requesting IRQs
         */
        temp = readl(sport->port.membase + UCR4);
+
+       if (USE_IRDA(sport))
+               temp |= UCR4_IRSC;
+
        writel(temp & ~UCR4_DREN, sport->port.membase + UCR4);
 
+       if (USE_IRDA(sport)) {
+               /* reset fifo's and state machines */
+               int i = 100;
+               temp = readl(sport->port.membase + UCR2);
+               temp &= ~UCR2_SRST;
+               writel(temp, sport->port.membase + UCR2);
+               while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) &&
+                   (--i > 0)) {
+                       udelay(1);
+               }
+       }
+
        /*
         * Allocate the IRQ(s) i.MX1 has three interrupts whereas later
         * chips only have one interrupt.
@@ -570,12 +635,16 @@ static int imx_startup(struct uart_port *port)
                if (retval)
                        goto error_out2;
 
-               retval = request_irq(sport->rtsirq, imx_rtsint,
-                            (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 :
-                              IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
-                               DRIVER_NAME, sport);
-               if (retval)
-                       goto error_out3;
+               /* do not use RTS IRQ on IrDA */
+               if (!USE_IRDA(sport)) {
+                       retval = request_irq(sport->rtsirq, imx_rtsint,
+                                    (sport->rtsirq < MAX_INTERNAL_IRQ) ? 0 :
+                                      IRQF_TRIGGER_FALLING |
+                                      IRQF_TRIGGER_RISING,
+                                       DRIVER_NAME, sport);
+                       if (retval)
+                               goto error_out3;
+               }
        } else {
                retval = request_irq(sport->port.irq, imx_int, 0,
                                DRIVER_NAME, sport);
@@ -592,12 +661,49 @@ static int imx_startup(struct uart_port *port)
 
        temp = readl(sport->port.membase + UCR1);
        temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
+
+       if (USE_IRDA(sport)) {
+               temp |= UCR1_IREN;
+               temp &= ~(UCR1_RTSDEN);
+       }
+
        writel(temp, sport->port.membase + UCR1);
 
        temp = readl(sport->port.membase + UCR2);
        temp |= (UCR2_RXEN | UCR2_TXEN);
        writel(temp, sport->port.membase + UCR2);
 
+       if (USE_IRDA(sport)) {
+               /* clear RX-FIFO */
+               int i = 64;
+               while ((--i > 0) &&
+                       (readl(sport->port.membase + URXD0) & URXD_CHARRDY)) {
+                       barrier();
+               }
+       }
+
+       if (!cpu_is_mx1()) {
+               temp = readl(sport->port.membase + UCR3);
+               temp |= MX2_UCR3_RXDMUXSEL;
+               writel(temp, sport->port.membase + UCR3);
+       }
+
+       if (USE_IRDA(sport)) {
+               temp = readl(sport->port.membase + UCR4);
+               if (sport->irda_inv_rx)
+                       temp |= UCR4_INVR;
+               else
+                       temp &= ~(UCR4_INVR);
+               writel(temp | UCR4_DREN, sport->port.membase + UCR4);
+
+               temp = readl(sport->port.membase + UCR3);
+               if (sport->irda_inv_tx)
+                       temp |= UCR3_INVT;
+               else
+                       temp &= ~(UCR3_INVT);
+               writel(temp, sport->port.membase + UCR3);
+       }
+
        /*
         * Enable modem status interrupts
         */
@@ -605,6 +711,16 @@ static int imx_startup(struct uart_port *port)
        imx_enable_ms(&sport->port);
        spin_unlock_irqrestore(&sport->port.lock,flags);
 
+       if (USE_IRDA(sport)) {
+               struct imxuart_platform_data *pdata;
+               pdata = sport->port.dev->platform_data;
+               sport->irda_inv_rx = pdata->irda_inv_rx;
+               sport->irda_inv_tx = pdata->irda_inv_tx;
+               sport->trcv_delay = pdata->transceiver_delay;
+               if (pdata->irda_enable)
+                       pdata->irda_enable(1);
+       }
+
        return 0;
 
 error_out3:
@@ -622,6 +738,17 @@ 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);
+
+       if (USE_IRDA(sport)) {
+               struct imxuart_platform_data *pdata;
+               pdata = sport->port.dev->platform_data;
+               if (pdata->irda_enable)
+                       pdata->irda_enable(0);
+       }
+
        /*
         * Stop our timer.
         */
@@ -631,7 +758,8 @@ static void imx_shutdown(struct uart_port *port)
         * Free the interrupts
         */
        if (sport->txirq > 0) {
-               free_irq(sport->rtsirq, sport);
+               if (!USE_IRDA(sport))
+                       free_irq(sport->rtsirq, sport);
                free_irq(sport->txirq, sport);
                free_irq(sport->rxirq, sport);
        } else
@@ -643,6 +771,9 @@ static void imx_shutdown(struct uart_port *port)
 
        temp = readl(sport->port.membase + UCR1);
        temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
+       if (USE_IRDA(sport))
+               temp &= ~(UCR1_IREN);
+
        writel(temp, sport->port.membase + UCR1);
 }
 
@@ -654,7 +785,9 @@ 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;
+       uint64_t tdiv64;
 
        /*
         * If we don't support modem control lines, don't allow
@@ -750,41 +883,44 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
                        sport->port.membase + UCR2);
        old_txrxen &= (UCR2_TXEN | UCR2_RXEN);
 
-       div = sport->port.uartclk / (baud * 16);
-       if (div > 7)
-               div = 7;
-       if (!div)
+       if (USE_IRDA(sport)) {
+               /*
+                * use maximum available submodule frequency to
+                * avoid missing short pulses due to low sampling rate
+                */
                div = 1;
+       } else {
+               div = sport->port.uartclk / (baud * 16);
+               if (div > 7)
+                       div = 7;
+               if (!div)
+                       div = 1;
+       }
 
-       num = baud;
-       denom = port->uartclk / div / 16;
+       rational_best_approximation(16 * div * baud, sport->port.uartclk,
+               1 << 16, 1 << 16, &num, &denom);
 
-       /* shift num and denom right until they fit into 16 bits */
-       while (num > 0x10000 || denom > 0x10000) {
-               num >>= 1;
-               denom >>= 1;
+       if (port->state && port->state->port.tty) {
+               tdiv64 = sport->port.uartclk;
+               tdiv64 *= num;
+               do_div(tdiv64, denom * 16 * div);
+               tty_encode_baud_rate(sport->port.state->port.tty,
+                               (speed_t)tdiv64, (speed_t)tdiv64);
        }
-       if (num > 0)
-               num -= 1;
-       if (denom > 0)
-               denom -= 1;
-
-       writel(num, sport->port.membase + UBIR);
-       writel(denom, sport->port.membase + UBMR);
 
-       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);
+       ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
        writel(ufcr, sport->port.membase + UFCR);
 
-#ifdef ONEMS
-       writel(sport->port.uartclk / div / 1000, sport->port.membase + ONEMS);
-#endif
+       writel(num, sport->port.membase + UBIR);
+       writel(denom, sport->port.membase + UBMR);
+
+       if (!cpu_is_mx1())
+               writel(sport->port.uartclk / div / 1000,
+                               sport->port.membase + MX2_ONEMS);
 
        writel(old_ucr1, sport->port.membase + UCR1);
 
@@ -914,17 +1050,20 @@ static void
 imx_console_write(struct console *co, const char *s, unsigned int count)
 {
        struct imx_port *sport = imx_ports[co->index];
-       unsigned int old_ucr1, old_ucr2;
+       unsigned int old_ucr1, old_ucr2, ucr1;
 
        /*
         *      First, save UCR1/2 and then disable interrupts
         */
-       old_ucr1 = readl(sport->port.membase + UCR1);
+       ucr1 = old_ucr1 = readl(sport->port.membase + UCR1);
        old_ucr2 = readl(sport->port.membase + UCR2);
 
-       writel((old_ucr1 | UCR1_UARTCLKEN | UCR1_UARTEN) &
-               ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
-               sport->port.membase + UCR1);
+       if (cpu_is_mx1())
+               ucr1 |= MX1_UCR1_UARTCLKEN;
+       ucr1 |= UCR1_UARTEN;
+       ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
+
+       writel(ucr1, sport->port.membase + UCR1);
 
        writel(old_ucr2 | UCR2_TXEN, sport->port.membase + UCR2);
 
@@ -949,7 +1088,7 @@ imx_console_get_options(struct imx_port *sport, int *baud,
                           int *parity, int *bits)
 {
 
-       if ( readl(sport->port.membase + UCR1) | UCR1_UARTEN ) {
+       if (readl(sport->port.membase + UCR1) & UCR1_UARTEN) {
                /* ok, the port was enabled */
                unsigned int ucr2, ubir,ubmr, uartclk;
                unsigned int baud_raw;
@@ -1020,6 +1159,8 @@ imx_console_setup(struct console *co, char *options)
        if (co->index == -1 || co->index >= ARRAY_SIZE(imx_ports))
                co->index = 0;
        sport = imx_ports[co->index];
+       if(sport == NULL)
+               return -ENODEV;
 
        if (options)
                uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -1059,22 +1200,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)
@@ -1118,7 +1259,7 @@ static int serial_imx_probe(struct platform_device *pdev)
        sport->timer.function = imx_timeout;
        sport->timer.data     = (unsigned long)sport;
 
-       sport->clk = clk_get(&pdev->dev, "uart_clk");
+       sport->clk = clk_get(&pdev->dev, "uart");
        if (IS_ERR(sport->clk)) {
                ret = PTR_ERR(sport->clk);
                goto unmap;
@@ -1130,16 +1271,32 @@ 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)
-               pdata->init(pdev);
+#ifdef CONFIG_IRDA
+       if (pdata && (pdata->flags & IMXUART_IRDA))
+               sport->use_irda = 1;
+#endif
+
+       if (pdata->init) {
+               ret = pdata->init(pdev);
+               if (ret)
+                       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);
 unmap:
        iounmap(sport->port.membase);
 free:
@@ -1174,13 +1331,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,
        },
 };