Merge branch 'topic/core-cleanup' into for-linus
[safe/jmp/linux-2.6] / drivers / serial / imx.c
index e216dcf..eacb588 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/tty_flip.h>
 #include <linux/serial_core.h>
 #include <linux/serial.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/rational.h>
+#include <linux/slab.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
-#include <asm/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 URTX0 0x40 /* Transmitter Register */
+#define UCR1  0x80 /* Control Register 1 */
+#define UCR2  0x84 /* Control Register 2 */
+#define UCR3  0x88 /* Control Register 3 */
+#define UCR4  0x8c /* Control Register 4 */
+#define UFCR  0x90 /* FIFO Control Register */
+#define USR1  0x94 /* Status Register 1 */
+#define USR2  0x98 /* Status Register 2 */
+#define UESC  0x9c /* Escape Character Register */
+#define UTIM  0xa0 /* Escape Timer Register */
+#define UBIR  0xa4 /* BRM Incremental Register */
+#define UBMR  0xa8 /* BRM Modulator Register */
+#define UBRC  0xac /* Baud Rate Count Register */
+#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  URXD_ERR        (1<<14)
+#define  URXD_OVRRUN     (1<<13)
+#define  URXD_FRMERR     (1<<12)
+#define  URXD_BRK        (1<<11)
+#define  URXD_PRERR      (1<<10)
+#define  UCR1_ADEN       (1<<15) /* Auto dectect interrupt */
+#define  UCR1_ADBR       (1<<14) /* Auto detect baud rate */
+#define  UCR1_TRDYEN     (1<<13) /* Transmitter ready interrupt enable */
+#define  UCR1_IDEN       (1<<12) /* Idle condition interrupt */
+#define  UCR1_RRDYEN     (1<<9)         /* Recv ready interrupt enable */
+#define  UCR1_RDMAEN     (1<<8)         /* Recv ready DMA enable */
+#define  UCR1_IREN       (1<<7)         /* Infrared interface enable */
+#define  UCR1_TXMPTYEN   (1<<6)         /* Transimitter empty interrupt enable */
+#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 */
+#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  UCR2_IRTS      (1<<14) /* Ignore RTS pin */
+#define  UCR2_CTSC      (1<<13) /* CTS pin control */
+#define  UCR2_CTS        (1<<12) /* Clear to send */
+#define  UCR2_ESCEN      (1<<11) /* Escape enable */
+#define  UCR2_PREN       (1<<8)  /* Parity enable */
+#define  UCR2_PROE       (1<<7)  /* Parity odd/even */
+#define  UCR2_STPB       (1<<6)         /* Stop */
+#define  UCR2_WS         (1<<5)         /* Word size */
+#define  UCR2_RTSEN      (1<<4)         /* Request to send interrupt enable */
+#define  UCR2_TXEN       (1<<2)         /* Transmitter enabled */
+#define  UCR2_RXEN       (1<<1)         /* Receiver enabled */
+#define  UCR2_SRST      (1<<0)  /* SW reset */
+#define  UCR3_DTREN     (1<<13) /* DTR interrupt enable */
+#define  UCR3_PARERREN   (1<<12) /* Parity enable */
+#define  UCR3_FRAERREN   (1<<11) /* Frame error interrupt enable */
+#define  UCR3_DSR        (1<<10) /* Data set ready */
+#define  UCR3_DCD        (1<<9)  /* Data carrier detect */
+#define  UCR3_RI         (1<<8)  /* Ring indicator */
+#define  UCR3_TIMEOUTEN  (1<<7)  /* Timeout 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  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_SHF  10      /* CTS trigger level shift */
+#define  UCR4_CTSTL_MASK 0x3F    /* CTS trigger is 6 bits wide */
+#define  UCR4_INVR      (1<<9)  /* Inverted infrared reception */
+#define  UCR4_ENIRI     (1<<8)  /* Serial infrared interrupt enable */
+#define  UCR4_WKEN      (1<<7)  /* Wake interrupt enable */
+#define  UCR4_REF16     (1<<6)  /* Ref freq 16 MHz */
+#define  UCR4_IRSC      (1<<5)  /* IR special case */
+#define  UCR4_TCEN      (1<<3)  /* Transmit complete interrupt enable */
+#define  UCR4_BKEN      (1<<2)  /* Break condition interrupt enable */
+#define  UCR4_OREN      (1<<1)  /* Receiver overrun interrupt enable */
+#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  USR1_TRDY      (1<<13) /* Transmitter ready interrupt/dma flag */
+#define  USR1_RTSD      (1<<12) /* RTS delta */
+#define  USR1_ESCF      (1<<11) /* Escape seq interrupt flag */
+#define  USR1_FRAMERR    (1<<10) /* Frame error interrupt flag */
+#define  USR1_RRDY       (1<<9)         /* Receiver ready interrupt/dma flag */
+#define  USR1_TIMEOUT    (1<<7)         /* Receive timeout interrupt status */
+#define  USR1_RXDS      (1<<6)  /* Receiver idle interrupt flag */
+#define  USR1_AIRINT    (1<<5)  /* Async IR wake interrupt flag */
+#define  USR1_AWAKE     (1<<4)  /* Aysnc wake interrupt flag */
+#define  USR2_ADET      (1<<15) /* Auto baud rate detect complete */
+#define  USR2_TXFE      (1<<14) /* Transmit buffer FIFO empty */
+#define  USR2_DTRF      (1<<13) /* DTR edge interrupt flag */
+#define  USR2_IDLE      (1<<12) /* Idle condition */
+#define  USR2_IRINT     (1<<8)  /* Serial infrared interrupt flag */
+#define  USR2_WAKE      (1<<7)  /* Wake */
+#define  USR2_RTSF      (1<<4)  /* RTS edge interrupt flag */
+#define  USR2_TXDC      (1<<3)  /* Transmitter complete */
+#define  USR2_BRCD      (1<<2)  /* Break condition */
+#define  USR2_ORE        (1<<1)         /* Overrun error */
+#define  USR2_RDR        (1<<0)         /* Recv data ready */
+#define  UTS_FRCPERR    (1<<13) /* Force parity error */
+#define  UTS_LOOP        (1<<12) /* Loop tx and rx */
+#define  UTS_TXEMPTY    (1<<6)  /* TxFIFO empty */
+#define  UTS_RXEMPTY    (1<<5)  /* RxFIFO empty */
+#define  UTS_TXFULL     (1<<4)  /* TxFIFO full */
+#define  UTS_RXFULL     (1<<3)  /* RxFIFO full */
+#define  UTS_SOFTRST    (1<<0)  /* Software reset */
 
 /* We've been assigned a range on the "Low-density serial ports" major */
-#define SERIAL_IMX_MAJOR       204
-#define MINOR_START            41
-
-#define NR_PORTS               2
-
-#define IMX_ISR_PASS_LIMIT     256
-
-/*
- * This is the size of our serial port register set.
- */
-#define UART_PORT_SIZE 0x100
+#define SERIAL_IMX_MAJOR        207
+#define MINOR_START            16
+#define DEV_NAME               "ttymxc"
+#define MAX_INTERNAL_IRQ       MXC_INTERNAL_IRQS
 
 /*
  * This determines how often we check the modem status signals
 
 #define DRIVER_NAME "IMX-uart"
 
+#define UART_NR 8
+
 struct imx_port {
        struct uart_port        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.
  */
@@ -101,7 +226,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);
 }
 
 /*
@@ -113,7 +238,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);
@@ -128,7 +253,52 @@ static void imx_timeout(unsigned long data)
 static void imx_stop_tx(struct uart_port *port)
 {
        struct imx_port *sport = (struct imx_port *)port;
-       UCR1((u32)sport->port.membase) &= ~UCR1_TXMPTYEN;
+       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);
 }
 
 /*
@@ -137,7 +307,10 @@ static void imx_stop_tx(struct uart_port *port)
 static void imx_stop_rx(struct uart_port *port)
 {
        struct imx_port *sport = (struct imx_port *)port;
-       UCR2((u32)sport->port.membase) &= ~UCR2_RXEN;
+       unsigned long temp;
+
+       temp = readl(sport->port.membase + UCR2);
+       writel(temp &~ UCR2_RXEN, sport->port.membase + UCR2);
 }
 
 /*
@@ -152,18 +325,20 @@ 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;
 
-       do {
+       while (!(readl(sport->port.membase + UTS) & UTS_TXFULL)) {
                /* send xmit->buf[xmit->tail]
                 * out the port here */
-               URTX0((u32)sport->port.membase) = xmit->buf[xmit->tail];
-               xmit->tail = (xmit->tail + 1) &
-                        (UART_XMIT_SIZE - 1);
+               writel(xmit->buf[xmit->tail], sport->port.membase + URTX0);
+               xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
                sport->port.icount.tx++;
                if (uart_circ_empty(xmit))
                        break;
-       } while (!(UTS((u32)sport->port.membase) & UTS_TXFULL));
+       }
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(&sport->port);
 
        if (uart_circ_empty(xmit))
                imx_stop_tx(&sport->port);
@@ -175,24 +350,47 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
 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);
 
-       UCR1((u32)sport->port.membase) |= UCR1_TXMPTYEN;
+               temp = readl(sport->port.membase + UCR1);
+               temp &= ~(UCR1_RRDYEN);
+               writel(temp, sport->port.membase + UCR1);
+       }
 
-       if(UTS((u32)sport->port.membase) & UTS_TXEMPTY)
+       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);
 }
 
 static irqreturn_t imx_rtsint(int irq, void *dev_id)
 {
-       struct imx_port *sport = (struct imx_port *)dev_id;
-       unsigned int val = USR1((u32)sport->port.membase)&USR1_RTSS;
+       struct imx_port *sport = dev_id;
+       unsigned int val = readl(sport->port.membase + USR1) & USR1_RTSS;
        unsigned long flags;
 
        spin_lock_irqsave(&sport->port.lock, flags);
 
-       USR1((u32)sport->port.membase) = USR1_RTSD;
+       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;
@@ -200,15 +398,15 @@ static irqreturn_t imx_rtsint(int irq, void *dev_id)
 
 static irqreturn_t imx_txint(int irq, void *dev_id)
 {
-       struct imx_port *sport = (struct imx_port *)dev_id;
-       struct circ_buf *xmit = &sport->port.info->xmit;
+       struct imx_port *sport = dev_id;
+       struct circ_buf *xmit = &sport->port.state->xmit;
        unsigned long flags;
 
        spin_lock_irqsave(&sport->port.lock,flags);
        if (sport->port.x_char)
        {
                /* Send next char */
-               URTX0((u32)sport->port.membase) = sport->port.x_char;
+               writel(sport->port.x_char, sport->port.membase + URTX0);
                goto out;
        }
 
@@ -231,68 +429,82 @@ 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->tty;
-       unsigned long flags;
+       struct tty_struct *tty = sport->port.state->port.tty;
+       unsigned long flags, temp;
 
-       rx = URXD0((u32)sport->port.membase);
        spin_lock_irqsave(&sport->port.lock,flags);
 
-       do {
+       while (readl(sport->port.membase + USR2) & USR2_RDR) {
                flg = TTY_NORMAL;
                sport->port.icount.rx++;
 
-               if( USR2((u32)sport->port.membase) & USR2_BRCD ) {
-                       USR2((u32)sport->port.membase) |= USR2_BRCD;
-                       if(uart_handle_break(&sport->port))
-                               goto ignore_char;
+               rx = readl(sport->port.membase + URXD0);
+
+               temp = readl(sport->port.membase + USR2);
+               if (temp & USR2_BRCD) {
+                       writel(USR2_BRCD, sport->port.membase + USR2);
+                       if (uart_handle_break(&sport->port))
+                               continue;
                }
 
-               if (uart_handle_sysrq_char
-                           (&sport->port, (unsigned char)rx))
-                       goto ignore_char;
+               if (uart_handle_sysrq_char(&sport->port, (unsigned char)rx))
+                       continue;
 
-               if( rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR) )
-                       goto handle_error;
+               if (rx & (URXD_PRERR | URXD_OVRRUN | URXD_FRMERR) ) {
+                       if (rx & URXD_PRERR)
+                               sport->port.icount.parity++;
+                       else if (rx & URXD_FRMERR)
+                               sport->port.icount.frame++;
+                       if (rx & URXD_OVRRUN)
+                               sport->port.icount.overrun++;
 
-       error_return:
-               tty_insert_flip_char(tty, rx, flg);
+                       if (rx & sport->port.ignore_status_mask) {
+                               if (++ignored > 100)
+                                       goto out;
+                               continue;
+                       }
+
+                       rx &= sport->port.read_status_mask;
+
+                       if (rx & URXD_PRERR)
+                               flg = TTY_PARITY;
+                       else if (rx & URXD_FRMERR)
+                               flg = TTY_FRAME;
+                       if (rx & URXD_OVRRUN)
+                               flg = TTY_OVERRUN;
+
+#ifdef SUPPORT_SYSRQ
+                       sport->port.sysrq = 0;
+#endif
+               }
 
-       ignore_char:
-               rx = URXD0((u32)sport->port.membase);
-       } while(rx & URXD_CHARRDY);
+               tty_insert_flip_char(tty, rx, flg);
+       }
 
 out:
        spin_unlock_irqrestore(&sport->port.lock,flags);
        tty_flip_buffer_push(tty);
        return IRQ_HANDLED;
+}
 
-handle_error:
-       if (rx & URXD_PRERR)
-               sport->port.icount.parity++;
-       else if (rx & URXD_FRMERR)
-               sport->port.icount.frame++;
-       if (rx & URXD_OVRRUN)
-               sport->port.icount.overrun++;
-
-       if (rx & sport->port.ignore_status_mask) {
-               if (++ignored > 100)
-                       goto out;
-               goto ignore_char;
-       }
+static irqreturn_t imx_int(int irq, void *dev_id)
+{
+       struct imx_port *sport = dev_id;
+       unsigned int sts;
 
-       rx &= sport->port.read_status_mask;
+       sts = readl(sport->port.membase + USR1);
 
-       if (rx & URXD_PRERR)
-               flg = TTY_PARITY;
-       else if (rx & URXD_FRMERR)
-               flg = TTY_FRAME;
-       if (rx & URXD_OVRRUN)
-               flg = TTY_OVERRUN;
+       if (sts & USR1_RRDY)
+               imx_rxint(irq, dev_id);
 
-#ifdef SUPPORT_SYSRQ
-       sport->port.sysrq = 0;
-#endif
-       goto error_return;
+       if (sts & USR1_TRDY &&
+                       readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN)
+               imx_txint(irq, dev_id);
+
+       if (sts & USR1_RTSD)
+               imx_rtsint(irq, dev_id);
+
+       return IRQ_HANDLED;
 }
 
 /*
@@ -302,7 +514,7 @@ static unsigned int imx_tx_empty(struct uart_port *port)
 {
        struct imx_port *sport = (struct imx_port *)port;
 
-       return USR2((u32)sport->port.membase) & USR2_TXDC ?  TIOCSER_TEMT : 0;
+       return (readl(sport->port.membase + USR2) & USR2_TXDC) ?  TIOCSER_TEMT : 0;
 }
 
 /*
@@ -310,26 +522,29 @@ 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 (USR1((u32)sport->port.membase) & USR1_RTSS)
-                tmp |= TIOCM_CTS;
+       if (readl(sport->port.membase + USR1) & USR1_RTSS)
+               tmp |= TIOCM_CTS;
 
-        if (UCR2((u32)sport->port.membase) & 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)
-                UCR2((u32)sport->port.membase) |= UCR2_CTS;
-        else
-                UCR2((u32)sport->port.membase) &= ~UCR2_CTS;
+       if (mctrl & TIOCM_RTS)
+               temp |= UCR2_CTS;
+
+       writel(temp, sport->port.membase + UCR2);
 }
 
 /*
@@ -338,14 +553,16 @@ static void imx_set_mctrl(struct uart_port *port, unsigned int mctrl)
 static void imx_break_ctl(struct uart_port *port, int break_state)
 {
        struct imx_port *sport = (struct imx_port *)port;
-       unsigned long flags;
+       unsigned long flags, temp;
 
        spin_lock_irqsave(&sport->port.lock, flags);
 
+       temp = readl(sport->port.membase + UCR1) & ~UCR1_SNDBRK;
+
        if ( break_state != 0 )
-               UCR1((u32)sport->port.membase) |= UCR1_SNDBRK;
-       else
-               UCR1((u32)sport->port.membase) &= ~UCR1_SNDBRK;
+               temp |= UCR1_SNDBRK;
+
+       writel(temp, sport->port.membase + UCR1);
 
        spin_unlock_irqrestore(&sport->port.lock, flags);
 }
@@ -361,62 +578,141 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
        /* 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;
+       val = TXTL << 10 | RXTL;
+       ufcr_rfdiv = (clk_get_rate(sport->clk) + 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);
+       val |= UFCR_RFDIV_REG(ufcr_rfdiv);
 
-       UFCR((u32)sport->port.membase) = val;
+       writel(val, sport->port.membase + UFCR);
 
        return 0;
 }
 
+/* half the RX buffer size */
+#define CTSTL 16
+
 static int imx_startup(struct uart_port *port)
 {
        struct imx_port *sport = (struct imx_port *)port;
        int retval;
-       unsigned long flags;
+       unsigned long flags, temp;
 
        imx_setup_ufcr(sport, 0);
 
        /* disable the DREN bit (Data Ready interrupt enable) before
         * requesting IRQs
         */
-       UCR4((u32)sport->port.membase) &= ~UCR4_DREN;
+       temp = readl(sport->port.membase + UCR4);
+
+       if (USE_IRDA(sport))
+               temp |= UCR4_IRSC;
+
+       /* set the trigger level for CTS */
+       temp &= ~(UCR4_CTSTL_MASK<<  UCR4_CTSTL_SHF);
+       temp |= CTSTL<<  UCR4_CTSTL_SHF;
+
+       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
+        * Allocate the IRQ(s) i.MX1 has three interrupts whereas later
+        * chips only have one interrupt.
         */
-       retval = request_irq(sport->rxirq, imx_rxint, 0,
-                            DRIVER_NAME, sport);
-       if (retval) goto error_out1;
-
-       retval = request_irq(sport->txirq, imx_txint, 0,
-                            DRIVER_NAME, sport);
-       if (retval) goto error_out2;
-
-       retval = request_irq(sport->rtsirq, imx_rtsint,
-                            IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
-                            DRIVER_NAME, sport);
-       if (retval) goto error_out3;
+       if (sport->txirq > 0) {
+               retval = request_irq(sport->rxirq, imx_rxint, 0,
+                               DRIVER_NAME, sport);
+               if (retval)
+                       goto error_out1;
+
+               retval = request_irq(sport->txirq, imx_txint, 0,
+                               DRIVER_NAME, sport);
+               if (retval)
+                       goto error_out2;
+
+               /* 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);
+               if (retval) {
+                       free_irq(sport->port.irq, sport);
+                       goto error_out1;
+               }
+       }
 
        /*
         * Finally, clear and enable interrupts
         */
+       writel(USR1_RTSD, sport->port.membase + USR1);
+
+       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);
 
-       USR1((u32)sport->port.membase) = USR1_RTSD;
-       UCR1((u32)sport->port.membase) |=
-                        (UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
+               temp = readl(sport->port.membase + UCR3);
+               if (sport->irda_inv_tx)
+                       temp |= UCR3_INVT;
+               else
+                       temp &= ~(UCR3_INVT);
+               writel(temp, sport->port.membase + UCR3);
+       }
 
-       UCR2((u32)sport->port.membase) |= (UCR2_RXEN | UCR2_TXEN);
        /*
         * Enable modem status interrupts
         */
@@ -424,12 +720,24 @@ 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:
-       free_irq(sport->txirq, sport);
+       if (sport->txirq)
+               free_irq(sport->txirq, sport);
 error_out2:
-       free_irq(sport->rxirq, sport);
+       if (sport->rxirq)
+               free_irq(sport->rxirq, sport);
 error_out1:
        return retval;
 }
@@ -437,6 +745,18 @@ error_out1:
 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.
@@ -446,16 +766,24 @@ 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);
+       if (sport->txirq > 0) {
+               if (!USE_IRDA(sport))
+                       free_irq(sport->rtsirq, sport);
+               free_irq(sport->txirq, sport);
+               free_irq(sport->rxirq, sport);
+       } else
+               free_irq(sport->port.irq, sport);
 
        /*
         * Disable all interrupts, port and break condition.
         */
 
-       UCR1((u32)sport->port.membase) &=
-                        ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN);
+       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);
 }
 
 static void
@@ -466,6 +794,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, ufcr;
+       unsigned long num, denom;
+       uint64_t tdiv64;
 
        /*
         * If we don't support modem control lines, don't allow
@@ -511,7 +842,7 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
        /*
         * Ask the core to calculate the divisor for us.
         */
-       baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+       baud = uart_get_baud_rate(port, termios, old, 50, port->uartclk / 16);
        quot = uart_get_divisor(port, baud);
 
        spin_lock_irqsave(&sport->port.lock, flags);
@@ -548,30 +879,62 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
        /*
         * disable interrupts and drain transmitter
         */
-       old_ucr1 = UCR1((u32)sport->port.membase);
-       UCR1((u32)sport->port.membase) &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
+       old_ucr1 = readl(sport->port.membase + UCR1);
+       writel(old_ucr1 & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
+                       sport->port.membase + UCR1);
 
-       while ( !(USR2((u32)sport->port.membase) & USR2_TXDC))
+       while ( !(readl(sport->port.membase + USR2) & USR2_TXDC))
                barrier();
 
        /* then, disable everything */
-       old_txrxen = UCR2((u32)sport->port.membase) & ( UCR2_TXEN | UCR2_RXEN );
-       UCR2((u32)sport->port.membase) &= ~( UCR2_TXEN | UCR2_RXEN);
+       old_txrxen = readl(sport->port.membase + UCR2);
+       writel(old_txrxen & ~( UCR2_TXEN | UCR2_RXEN),
+                       sport->port.membase + UCR2);
+       old_txrxen &= (UCR2_TXEN | UCR2_RXEN);
 
-       /* set the parity, stop bits and data size */
-       UCR2((u32)sport->port.membase) = ucr2;
+       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;
+       }
 
-       /* set the baud rate. We assume uartclk = 16 MHz
-        *
-        * baud * 16   UBIR - 1
-        * --------- = --------
-        *  uartclk    UBMR - 1
-        */
-       UBIR((u32)sport->port.membase) = (baud / 100) - 1;
-       UBMR((u32)sport->port.membase) = 10000 - 1;
+       rational_best_approximation(16 * div * baud, sport->port.uartclk,
+               1 << 16, 1 << 16, &num, &denom);
+
+       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);
+       }
+
+       num -= 1;
+       denom -= 1;
 
-       UCR1((u32)sport->port.membase) = old_ucr1;
-       UCR2((u32)sport->port.membase) |= old_txrxen;
+       ufcr = readl(sport->port.membase + UFCR);
+       ufcr = (ufcr & (~UFCR_RFDIV)) | UFCR_RFDIV_REG(div);
+       writel(ufcr, sport->port.membase + UFCR);
+
+       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);
+
+       /* set the parity, stop bits and data size */
+       writel(ucr2 | old_txrxen, sport->port.membase + UCR2);
 
        if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
                imx_enable_ms(&sport->port);
@@ -591,9 +954,11 @@ static const char *imx_type(struct uart_port *port)
  */
 static void imx_release_port(struct uart_port *port)
 {
-       struct imx_port *sport = (struct imx_port *)port;
+       struct platform_device *pdev = to_platform_device(port->dev);
+       struct resource *mmres;
 
-       release_mem_region(sport->port.mapbase, UART_PORT_SIZE);
+       mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(mmres->start, mmres->end - mmres->start + 1);
 }
 
 /*
@@ -601,10 +966,18 @@ static void imx_release_port(struct uart_port *port)
  */
 static int imx_request_port(struct uart_port *port)
 {
-       struct imx_port *sport = (struct imx_port *)port;
+       struct platform_device *pdev = to_platform_device(port->dev);
+       struct resource *mmres;
+       void *ret;
+
+       mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mmres)
+               return -ENODEV;
 
-       return request_mem_region(sport->port.mapbase, UART_PORT_SIZE,
-                       "imx-uart") != NULL ? 0 : -EBUSY;
+       ret = request_mem_region(mmres->start, mmres->end - mmres->start + 1,
+                       "imx-uart");
+
+       return  ret ? 0 : -EBUSY;
 }
 
 /*
@@ -666,73 +1039,17 @@ static struct uart_ops imx_pops = {
        .verify_port    = imx_verify_port,
 };
 
-static struct imx_port imx_ports[] = {
-       {
-       .txirq  = UART1_MINT_TX,
-       .rxirq  = UART1_MINT_RX,
-       .rtsirq = UART1_MINT_RTS,
-       .port   = {
-               .type           = PORT_IMX,
-               .iotype         = UPIO_MEM,
-               .membase        = (void *)IMX_UART1_BASE,
-               .mapbase        = IMX_UART1_BASE, /* FIXME */
-               .irq            = UART1_MINT_RX,
-               .uartclk        = 16000000,
-               .fifosize       = 8,
-               .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         = UPIO_MEM,
-               .membase        = (void *)IMX_UART2_BASE,
-               .mapbase        = IMX_UART2_BASE, /* FIXME */
-               .irq            = UART2_MINT_RX,
-               .uartclk        = 16000000,
-               .fifosize       = 8,
-               .flags          = UPF_BOOT_AUTOCONF,
-               .ops            = &imx_pops,
-               .line           = 1,
-       },
-       }
-};
-
-/*
- * Setup the IMX serial ports.
- * Note also that we support "console=ttySMXx" where "x" is either 0 or 1.
- * Which serial port this ends up being depends on the machine you're
- * running this kernel on.  I'm not convinced that this is a good idea,
- * but that's the way it traditionally works.
- *
- */
-static void __init imx_init_ports(void)
-{
-       static int first = 1;
-       int i;
-
-       if (!first)
-               return;
-       first = 0;
-
-       for (i = 0; i < ARRAY_SIZE(imx_ports); i++) {
-               init_timer(&imx_ports[i].timer);
-               imx_ports[i].timer.function = imx_timeout;
-               imx_ports[i].timer.data     = (unsigned long)&imx_ports[i];
-       }
-}
+static struct imx_port *imx_ports[UART_NR];
 
 #ifdef CONFIG_SERIAL_IMX_CONSOLE
 static void imx_console_putchar(struct uart_port *port, int ch)
 {
        struct imx_port *sport = (struct imx_port *)port;
-       while ((UTS((u32)sport->port.membase) & UTS_TXFULL))
+
+       while (readl(sport->port.membase + UTS) & UTS_TXFULL)
                barrier();
-       URTX0((u32)sport->port.membase) = ch;
+
+       writel(ch, sport->port.membase + URTX0);
 }
 
 /*
@@ -741,19 +1058,23 @@ static void imx_console_putchar(struct uart_port *port, int ch)
 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;
+       struct imx_port *sport = imx_ports[co->index];
+       unsigned int old_ucr1, old_ucr2, ucr1;
 
        /*
         *      First, save UCR1/2 and then disable interrupts
         */
-       old_ucr1 = UCR1((u32)sport->port.membase);
-       old_ucr2 = UCR2((u32)sport->port.membase);
+       ucr1 = old_ucr1 = readl(sport->port.membase + UCR1);
+       old_ucr2 = readl(sport->port.membase + UCR2);
 
-       UCR1((u32)sport->port.membase) =
-                          (old_ucr1 | UCR1_UARTCLKEN | UCR1_UARTEN)
-                          & ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
-       UCR2((u32)sport->port.membase) = old_ucr2 | UCR2_TXEN;
+       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);
 
        uart_console_write(&sport->port, s, count, imx_console_putchar);
 
@@ -761,10 +1082,10 @@ imx_console_write(struct console *co, const char *s, unsigned int count)
         *      Finally, wait for transmitter to become empty
         *      and restore UCR1/2
         */
-       while (!(USR2((u32)sport->port.membase) & USR2_TXDC));
+       while (!(readl(sport->port.membase + USR2) & USR2_TXDC));
 
-       UCR1((u32)sport->port.membase) = old_ucr1;
-       UCR2((u32)sport->port.membase) = old_ucr2;
+       writel(old_ucr1, sport->port.membase + UCR1);
+       writel(old_ucr2, sport->port.membase + UCR2);
 }
 
 /*
@@ -776,13 +1097,13 @@ imx_console_get_options(struct imx_port *sport, int *baud,
                           int *parity, int *bits)
 {
 
-       if ( UCR1((u32)sport->port.membase) | 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;
                unsigned int ucfr_rfdiv;
 
-               ucr2 = UCR2((u32)sport->port.membase);
+               ucr2 = readl(sport->port.membase + UCR2);
 
                *parity = 'n';
                if (ucr2 & UCR2_PREN) {
@@ -797,17 +1118,16 @@ imx_console_get_options(struct imx_port *sport, int *baud,
                else
                        *bits = 7;
 
-               ubir = UBIR((u32)sport->port.membase) & 0xffff;
-               ubmr = UBMR((u32)sport->port.membase) & 0xffff;
-
+               ubir = readl(sport->port.membase + UBIR) & 0xffff;
+               ubmr = readl(sport->port.membase + UBMR) & 0xffff;
 
-               ucfr_rfdiv = (UFCR((u32)sport->port.membase) & UFCR_RFDIV) >> 7;
+               ucfr_rfdiv = (readl(sport->port.membase + UFCR) & UFCR_RFDIV) >> 7;
                if (ucfr_rfdiv == 6)
                        ucfr_rfdiv = 7;
                else
                        ucfr_rfdiv = 6 - ucfr_rfdiv;
 
-               uartclk = imx_get_perclk1();
+               uartclk = clk_get_rate(sport->clk);
                uartclk /= ucfr_rfdiv;
 
                {       /*
@@ -847,7 +1167,9 @@ 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];
+       sport = imx_ports[co->index];
+       if(sport == NULL)
+               return -ENODEV;
 
        if (options)
                uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -861,7 +1183,7 @@ imx_console_setup(struct console *co, char *options)
 
 static struct uart_driver imx_reg;
 static struct console imx_console = {
-       .name           = "ttySMX",
+       .name           = DEV_NAME,
        .write          = imx_console_write,
        .device         = uart_console_device,
        .setup          = imx_console_setup,
@@ -870,14 +1192,6 @@ static struct console imx_console = {
        .data           = &imx_reg,
 };
 
-static int __init imx_rs_console_init(void)
-{
-       imx_init_ports();
-       register_console(&imx_console);
-       return 0;
-}
-console_initcall(imx_rs_console_init);
-
 #define IMX_CONSOLE    &imx_console
 #else
 #define IMX_CONSOLE    NULL
@@ -886,7 +1200,7 @@ console_initcall(imx_rs_console_init);
 static struct uart_driver imx_reg = {
        .owner          = THIS_MODULE,
        .driver_name    = DRIVER_NAME,
-       .dev_name       = "ttySMX",
+       .dev_name       = DEV_NAME,
        .major          = SERIAL_IMX_MAJOR,
        .minor          = MINOR_START,
        .nr             = ARRAY_SIZE(imx_ports),
@@ -895,59 +1209,145 @@ 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 *dev)
+static int serial_imx_probe(struct platform_device *pdev)
 {
+       struct imx_port *sport;
        struct imxuart_platform_data *pdata;
+       void __iomem *base;
+       int ret = 0;
+       struct resource *res;
+
+       sport = kzalloc(sizeof(*sport), GFP_KERNEL);
+       if (!sport)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               ret = -ENODEV;
+               goto free;
+       }
+
+       base = ioremap(res->start, PAGE_SIZE);
+       if (!base) {
+               ret = -ENOMEM;
+               goto free;
+       }
+
+       sport->port.dev = &pdev->dev;
+       sport->port.mapbase = res->start;
+       sport->port.membase = base;
+       sport->port.type = PORT_IMX,
+       sport->port.iotype = UPIO_MEM;
+       sport->port.irq = platform_get_irq(pdev, 0);
+       sport->rxirq = platform_get_irq(pdev, 0);
+       sport->txirq = platform_get_irq(pdev, 1);
+       sport->rtsirq = platform_get_irq(pdev, 2);
+       sport->port.fifosize = 32;
+       sport->port.ops = &imx_pops;
+       sport->port.flags = UPF_BOOT_AUTOCONF;
+       sport->port.line = pdev->id;
+       init_timer(&sport->timer);
+       sport->timer.function = imx_timeout;
+       sport->timer.data     = (unsigned long)sport;
+
+       sport->clk = clk_get(&pdev->dev, "uart");
+       if (IS_ERR(sport->clk)) {
+               ret = PTR_ERR(sport->clk);
+               goto unmap;
+       }
+       clk_enable(sport->clk);
+
+       sport->port.uartclk = clk_get_rate(sport->clk);
+
+       imx_ports[pdev->id] = sport;
 
-       imx_ports[dev->id].port.dev = &dev->dev;
+       pdata = pdev->dev.platform_data;
+       if (pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
+               sport->have_rtscts = 1;
 
-       pdata = (struct imxuart_platform_data *)dev->dev.platform_data;
-       if(pdata && (pdata->flags & IMXUART_HAVE_RTSCTS))
-               imx_ports[dev->id].have_rtscts = 1;
+#ifdef CONFIG_IRDA
+       if (pdata && (pdata->flags & IMXUART_IRDA))
+               sport->use_irda = 1;
+#endif
+
+       if (pdata && pdata->init) {
+               ret = pdata->init(pdev);
+               if (ret)
+                       goto clkput;
+       }
+
+       ret = uart_add_one_port(&imx_reg, &sport->port);
+       if (ret)
+               goto deinit;
+       platform_set_drvdata(pdev, &sport->port);
 
-       uart_add_one_port(&imx_reg, &imx_ports[dev->id].port);
-       platform_set_drvdata(dev, &imx_ports[dev->id]);
        return 0;
+deinit:
+       if (pdata && pdata->exit)
+               pdata->exit(pdev);
+clkput:
+       clk_put(sport->clk);
+       clk_disable(sport->clk);
+unmap:
+       iounmap(sport->port.membase);
+free:
+       kfree(sport);
+
+       return ret;
 }
 
-static int serial_imx_remove(struct platform_device *dev)
+static int serial_imx_remove(struct platform_device *pdev)
 {
-       struct imx_port *sport = platform_get_drvdata(dev);
+       struct imxuart_platform_data *pdata;
+       struct imx_port *sport = platform_get_drvdata(pdev);
 
-       platform_set_drvdata(dev, NULL);
+       pdata = pdev->dev.platform_data;
 
-       if (sport)
+       platform_set_drvdata(pdev, NULL);
+
+       if (sport) {
                uart_remove_one_port(&imx_reg, &sport->port);
+               clk_put(sport->clk);
+       }
+
+       clk_disable(sport->clk);
+
+       if (pdata && pdata->exit)
+               pdata->exit(pdev);
+
+       iounmap(sport->port.membase);
+       kfree(sport);
 
        return 0;
 }
 
 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,
        },
 };
 
@@ -957,8 +1357,6 @@ static int __init imx_serial_init(void)
 
        printk(KERN_INFO "Serial: IMX driver\n");
 
-       imx_init_ports();
-
        ret = uart_register_driver(&imx_reg);
        if (ret)
                return ret;
@@ -972,8 +1370,8 @@ 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);
+       uart_unregister_driver(&imx_reg);
 }
 
 module_init(imx_serial_init);
@@ -982,3 +1380,4 @@ module_exit(imx_serial_exit);
 MODULE_AUTHOR("Sascha Hauer");
 MODULE_DESCRIPTION("IMX generic serial port driver");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:imx-uart");