Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / serial / cpm_uart / cpm_uart_core.c
index 0612e94..5cba59a 100644 (file)
@@ -7,11 +7,13 @@
  *  Based on ppc8xx.c by Thomas Gleixner
  *  Based on drivers/serial/amba.c by Russell King
  *
- *  Maintainer: Kumar Gala (kumar.gala@freescale.com) (CPM2)
+ *  Maintainer: Kumar Gala (galak@kernel.crashing.org) (CPM2)
  *              Pantelis Antoniou (panto@intracom.gr) (CPM1)
- * 
+ *
  *  Copyright (C) 2004 Freescale Semiconductor, Inc.
  *            (C) 2004 Intracom, S.A.
+ *            (C) 2005-2006 MontaVista Software, Inc.
+ *             Vitaly Bordug <vbordug@ru.mvista.com>
  *
  * 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
@@ -40,6 +42,7 @@
 #include <linux/device.h>
 #include <linux/bootmem.h>
 #include <linux/dma-mapping.h>
+#include <linux/fs_uart_pd.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
 /* Track which ports are configured as uarts */
 int cpm_uart_port_map[UART_NR];
 /* How many ports did we config as uarts */
-int cpm_uart_nr;
+int cpm_uart_nr = 0;
 
 /**************************************************************/
 
 static int  cpm_uart_tx_pump(struct uart_port *port);
-static void cpm_uart_init_smc(struct uart_cpm_port *pinfo, int bits, u16 cval);
-static void cpm_uart_init_scc(struct uart_cpm_port *pinfo, int sbits, u16 sval);
+static void cpm_uart_init_smc(struct uart_cpm_port *pinfo);
+static void cpm_uart_init_scc(struct uart_cpm_port *pinfo);
+static void cpm_uart_initbd(struct uart_cpm_port *pinfo);
 
 /**************************************************************/
 
+
+/* Place-holder for board-specific stuff */
+struct platform_device* __attribute__ ((weak)) __init
+early_uart_get_pdev(int index)
+{
+       return NULL;
+}
+
+
+static void cpm_uart_count(void)
+{
+       cpm_uart_nr = 0;
+#ifdef CONFIG_SERIAL_CPM_SMC1
+       cpm_uart_port_map[cpm_uart_nr++] = UART_SMC1;
+#endif
+#ifdef CONFIG_SERIAL_CPM_SMC2
+       cpm_uart_port_map[cpm_uart_nr++] = UART_SMC2;
+#endif
+#ifdef CONFIG_SERIAL_CPM_SCC1
+       cpm_uart_port_map[cpm_uart_nr++] = UART_SCC1;
+#endif
+#ifdef CONFIG_SERIAL_CPM_SCC2
+       cpm_uart_port_map[cpm_uart_nr++] = UART_SCC2;
+#endif
+#ifdef CONFIG_SERIAL_CPM_SCC3
+       cpm_uart_port_map[cpm_uart_nr++] = UART_SCC3;
+#endif
+#ifdef CONFIG_SERIAL_CPM_SCC4
+       cpm_uart_port_map[cpm_uart_nr++] = UART_SCC4;
+#endif
+}
+
+/* Get UART number by its id */
+static int cpm_uart_id2nr(int id)
+{
+       int i;
+       if (id < UART_NR) {
+               for (i=0; i<UART_NR; i++) {
+                       if (cpm_uart_port_map[i] == id)
+                               return i;
+               }
+       }
+
+       /* not found or invalid argument */
+       return -1;
+}
+
 /*
- * Check, if transmit buffers are processed            
+ * Check, if transmit buffers are processed
 */
 static unsigned int cpm_uart_tx_empty(struct uart_port *port)
 {
@@ -108,7 +159,7 @@ static unsigned int cpm_uart_get_mctrl(struct uart_port *port)
 /*
  * Stop transmitter
  */
-static void cpm_uart_stop_tx(struct uart_port *port, unsigned int tty_stop)
+static void cpm_uart_stop_tx(struct uart_port *port)
 {
        struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
        volatile smc_t *smcp = pinfo->smcp;
@@ -125,7 +176,7 @@ static void cpm_uart_stop_tx(struct uart_port *port, unsigned int tty_stop)
 /*
  * Start transmitter
  */
-static void cpm_uart_start_tx(struct uart_port *port, unsigned int tty_start)
+static void cpm_uart_start_tx(struct uart_port *port)
 {
        struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
        volatile smc_t *smcp = pinfo->smcp;
@@ -133,10 +184,6 @@ static void cpm_uart_start_tx(struct uart_port *port, unsigned int tty_start)
 
        pr_debug("CPM uart[%d]:start tx\n", port->line);
 
-       /* if in the middle of discarding return */
-       if (IS_DISCARDING(pinfo))
-               return;
-
        if (IS_SMC(pinfo)) {
                if (smcp->smc_smcm & SMCM_TX)
                        return;
@@ -146,15 +193,18 @@ static void cpm_uart_start_tx(struct uart_port *port, unsigned int tty_start)
        }
 
        if (cpm_uart_tx_pump(port) != 0) {
-               if (IS_SMC(pinfo))
+               if (IS_SMC(pinfo)) {
                        smcp->smc_smcm |= SMCM_TX;
-               else
+                       smcp->smc_smcmr |= SMCMR_TEN;
+               } else {
                        sccp->scc_sccm |= UART_SCCM_TX;
+                       pinfo->sccp->scc_gsmrl |= SCC_GSMRL_ENT;
+               }
        }
 }
 
 /*
- * Stop receiver 
+ * Stop receiver
  */
 static void cpm_uart_stop_rx(struct uart_port *port)
 {
@@ -179,7 +229,7 @@ static void cpm_uart_enable_ms(struct uart_port *port)
 }
 
 /*
- * Generate a break. 
+ * Generate a break.
  */
 static void cpm_uart_break_ctl(struct uart_port *port, int break_state)
 {
@@ -234,19 +284,16 @@ static void cpm_uart_int_rx(struct uart_port *port, struct pt_regs *regs)
                /* get number of characters, and check spce in flip-buffer */
                i = bdp->cbd_datlen;
 
-               /* If we have not enough room in tty flip buffer, then we try 
+               /* If we have not enough room in tty flip buffer, then we try
                 * later, which will be the next rx-interrupt or a timeout
                 */
-               if ((tty->flip.count + i) >= TTY_FLIPBUF_SIZE) {
-                       tty->flip.work.func((void *)tty);
-                       if ((tty->flip.count + i) >= TTY_FLIPBUF_SIZE) {
-                               printk(KERN_WARNING "TTY_DONT_FLIP set\n");
-                               return;
-                       }
+               if(tty_buffer_request_room(tty, i) < i) {
+                       printk(KERN_WARNING "No room in flip buffer\n");
+                       return;
                }
 
                /* get pointer */
-               cp = (unsigned char *)bus_to_virt(bdp->cbd_bufaddr);
+               cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo);
 
                /* loop through the buffer */
                while (i-- > 0) {
@@ -261,20 +308,19 @@ static void cpm_uart_int_rx(struct uart_port *port, struct pt_regs *regs)
                                continue;
 
                      error_return:
-                       *tty->flip.char_buf_ptr++ = ch;
-                       *tty->flip.flag_buf_ptr++ = flg;
-                       tty->flip.count++;
+                       tty_insert_flip_char(tty, ch, flg);
 
                }               /* End while (i--) */
 
                /* This BD is ready to be used again. Clear status. get next */
-               bdp->cbd_sc &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV);
+               bdp->cbd_sc &= ~(BD_SC_BR | BD_SC_FR | BD_SC_PR | BD_SC_OV | BD_SC_ID);
                bdp->cbd_sc |= BD_SC_EMPTY;
 
                if (bdp->cbd_sc & BD_SC_WRAP)
                        bdp = pinfo->rx_bd_base;
                else
                        bdp++;
+
        } /* End for (;;) */
 
        /* Write back buffer pointer */
@@ -339,22 +385,22 @@ static irqreturn_t cpm_uart_int(int irq, void *data, struct pt_regs *regs)
 
        if (IS_SMC(pinfo)) {
                events = smcp->smc_smce;
+               smcp->smc_smce = events;
                if (events & SMCM_BRKE)
                        uart_handle_break(port);
                if (events & SMCM_RX)
                        cpm_uart_int_rx(port, regs);
                if (events & SMCM_TX)
                        cpm_uart_int_tx(port, regs);
-               smcp->smc_smce = events;
        } else {
                events = sccp->scc_scce;
+               sccp->scc_scce = events;
                if (events & UART_SCCM_BRKE)
                        uart_handle_break(port);
                if (events & UART_SCCM_RX)
                        cpm_uart_int_rx(port, regs);
                if (events & UART_SCCM_TX)
                        cpm_uart_int_tx(port, regs);
-               sccp->scc_scce = events;
        }
        return (events) ? IRQ_HANDLED : IRQ_NONE;
 }
@@ -362,6 +408,8 @@ static irqreturn_t cpm_uart_int(int irq, void *data, struct pt_regs *regs)
 static int cpm_uart_startup(struct uart_port *port)
 {
        int retval;
+       struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
+       int line = pinfo - cpm_uart_ports;
 
        pr_debug("CPM uart[%d]:startup\n", port->line);
 
@@ -370,9 +418,25 @@ static int cpm_uart_startup(struct uart_port *port)
        if (retval)
                return retval;
 
+       /* Startup rx-int */
+       if (IS_SMC(pinfo)) {
+               pinfo->smcp->smc_smcm |= SMCM_RX;
+               pinfo->smcp->smc_smcmr |= SMCMR_REN;
+       } else {
+               pinfo->sccp->scc_sccm |= UART_SCCM_RX;
+       }
+
+       if (!(pinfo->flags & FLAG_CONSOLE))
+               cpm_line_cr_cmd(line,CPM_CR_INIT_TRX);
        return 0;
 }
 
+inline void cpm_uart_wait_until_send(struct uart_cpm_port *pinfo)
+{
+       set_current_state(TASK_UNINTERRUPTIBLE);
+       schedule_timeout(pinfo->wait_closing);
+}
+
 /*
  * Shutdown the uart
  */
@@ -388,6 +452,15 @@ static void cpm_uart_shutdown(struct uart_port *port)
 
        /* If the port is not the console, disable Rx and Tx. */
        if (!(pinfo->flags & FLAG_CONSOLE)) {
+               /* Wait for all the BDs marked sent */
+               while(!cpm_uart_tx_empty(port)) {
+                       set_current_state(TASK_UNINTERRUPTIBLE);
+                       schedule_timeout(2);
+               }
+
+               if (pinfo->wait_closing)
+                       cpm_uart_wait_until_send(pinfo);
+
                /* Stop uarts */
                if (IS_SMC(pinfo)) {
                        volatile smc_t *smcp = pinfo->smcp;
@@ -400,7 +473,12 @@ static void cpm_uart_shutdown(struct uart_port *port)
                }
 
                /* Shut them really down and reinit buffer descriptors */
-               cpm_line_cr_cmd(line, CPM_CR_INIT_TRX);
+               if (IS_SMC(pinfo))
+                       cpm_line_cr_cmd(line, CPM_CR_STOP_TX);
+               else
+                       cpm_line_cr_cmd(line, CPM_CR_GRA_STOP_TX);
+
+               cpm_uart_initbd(pinfo);
        }
 }
 
@@ -409,57 +487,14 @@ static void cpm_uart_set_termios(struct uart_port *port,
 {
        int baud;
        unsigned long flags;
-       u16 cval, scval;
+       u16 cval, scval, prev_mode;
        int bits, sbits;
        struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
-       int line = pinfo - cpm_uart_ports;
-       volatile cbd_t *bdp;
+       volatile smc_t *smcp = pinfo->smcp;
+       volatile scc_t *sccp = pinfo->sccp;
 
        pr_debug("CPM uart[%d]:set_termios\n", port->line);
 
-       spin_lock_irqsave(&port->lock, flags);
-       /* disable uart interrupts */
-       if (IS_SMC(pinfo))
-               pinfo->smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX);
-       else
-               pinfo->sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX);
-       pinfo->flags |= FLAG_DISCARDING;
-       spin_unlock_irqrestore(&port->lock, flags);
-
-       /* if previous configuration exists wait for tx to finish */
-       if (pinfo->baud != 0 && pinfo->bits != 0) {
-
-               /* point to the last txed bd */
-               bdp = pinfo->tx_cur;
-               if (bdp == pinfo->tx_bd_base)
-                       bdp = pinfo->tx_bd_base + (pinfo->tx_nrfifos - 1);
-               else
-                       bdp--;
-
-               /* wait for it to be transmitted */
-               while ((bdp->cbd_sc & BD_SC_READY) != 0)
-                       schedule();
-
-               /* and delay for the hw fifo to drain */
-               udelay((3 * 1000000 * pinfo->bits) / pinfo->baud);
-       }
-
-       spin_lock_irqsave(&port->lock, flags);
-
-       /* Send the CPM an initialize command. */
-       cpm_line_cr_cmd(line, CPM_CR_STOP_TX);
-
-       /* Stop uart */
-       if (IS_SMC(pinfo))
-               pinfo->smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
-       else
-               pinfo->sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
-
-       /* Send the CPM an initialize command. */
-       cpm_line_cr_cmd(line, CPM_CR_INIT_TRX);
-
-       spin_unlock_irqrestore(&port->lock, flags);
-
        baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
 
        /* Character length programmed into the mode register is the
@@ -538,27 +573,27 @@ static void cpm_uart_set_termios(struct uart_port *port,
         */
        if ((termios->c_cflag & CREAD) == 0)
                port->read_status_mask &= ~BD_SC_EMPTY;
-       
-       spin_lock_irqsave(&port->lock, flags);
 
-       cpm_set_brg(pinfo->brg - 1, baud);
+       spin_lock_irqsave(&port->lock, flags);
 
        /* Start bit has not been added (so don't, because we would just
         * subtract it later), and we need to add one for the number of
         * stops bits (there is always at least one).
         */
        bits++;
+       if (IS_SMC(pinfo)) {
+               /* Set the mode register.  We want to keep a copy of the
+                * enables, because we want to put them back if they were
+                * present.
+                */
+               prev_mode = smcp->smc_smcmr;
+               smcp->smc_smcmr = smcr_mk_clen(bits) | cval | SMCMR_SM_UART;
+               smcp->smc_smcmr |= (prev_mode & (SMCMR_REN | SMCMR_TEN));
+       } else {
+               sccp->scc_psmr = (sbits << 12) | scval;
+       }
 
-       /* re-init */
-       if (IS_SMC(pinfo))
-               cpm_uart_init_smc(pinfo, bits, cval);
-       else
-               cpm_uart_init_scc(pinfo, sbits, scval);
-
-       pinfo->baud = baud;
-       pinfo->bits = bits;
-
-       pinfo->flags &= ~FLAG_DISCARDING;
+       cpm_set_brg(pinfo->brg - 1, baud);
        spin_unlock_irqrestore(&port->lock, flags);
 
 }
@@ -605,8 +640,9 @@ static int cpm_uart_tx_pump(struct uart_port *port)
                /* Pick next descriptor and fill from buffer */
                bdp = pinfo->tx_cur;
 
-               p = bus_to_virt(bdp->cbd_bufaddr);
-               *p++ = xmit->buf[xmit->tail];
+               p = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo);
+
+               *p++ = port->x_char;
                bdp->cbd_datlen = 1;
                bdp->cbd_sc |= BD_SC_READY;
                /* Get next BD. */
@@ -622,7 +658,7 @@ static int cpm_uart_tx_pump(struct uart_port *port)
        }
 
        if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
-               cpm_uart_stop_tx(port, 0);
+               cpm_uart_stop_tx(port);
                return 0;
        }
 
@@ -631,7 +667,7 @@ static int cpm_uart_tx_pump(struct uart_port *port)
 
        while (!(bdp->cbd_sc & BD_SC_READY) && (xmit->tail != xmit->head)) {
                count = 0;
-               p = bus_to_virt(bdp->cbd_bufaddr);
+               p = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo);
                while (count < pinfo->tx_fifosize) {
                        *p++ = xmit->buf[xmit->tail];
                        xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
@@ -642,6 +678,7 @@ static int cpm_uart_tx_pump(struct uart_port *port)
                }
                bdp->cbd_datlen = count;
                bdp->cbd_sc |= BD_SC_READY;
+               __asm__("eieio");
                /* Get next BD. */
                if (bdp->cbd_sc & BD_SC_WRAP)
                        bdp = pinfo->tx_bd_base;
@@ -654,52 +691,66 @@ static int cpm_uart_tx_pump(struct uart_port *port)
                uart_write_wakeup(port);
 
        if (uart_circ_empty(xmit)) {
-               cpm_uart_stop_tx(port, 0);
+               cpm_uart_stop_tx(port);
                return 0;
        }
 
        return 1;
 }
 
-static void cpm_uart_init_scc(struct uart_cpm_port *pinfo, int bits, u16 scval)
+/*
+ * init buffer descriptors
+ */
+static void cpm_uart_initbd(struct uart_cpm_port *pinfo)
 {
-       int line = pinfo - cpm_uart_ports;
-       volatile scc_t *scp;
-       volatile scc_uart_t *sup;
+       int i;
        u8 *mem_addr;
        volatile cbd_t *bdp;
-       int i;
-
-       pr_debug("CPM uart[%d]:init_scc\n", pinfo->port.line);
 
-       scp = pinfo->sccp;
-       sup = pinfo->sccup;
+       pr_debug("CPM uart[%d]:initbd\n", pinfo->port.line);
 
        /* Set the physical address of the host memory
         * buffers in the buffer descriptors, and the
         * virtual address for us to work with.
         */
-       pinfo->rx_cur = pinfo->rx_bd_base;
        mem_addr = pinfo->mem_addr;
-       for (bdp = pinfo->rx_bd_base, i = 0; i < pinfo->rx_nrfifos; i++, bdp++) {
-               bdp->cbd_bufaddr = virt_to_bus(mem_addr);
-               bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT | (i < (pinfo->rx_nrfifos - 1) ? 0 : BD_SC_WRAP);
+       bdp = pinfo->rx_cur = pinfo->rx_bd_base;
+       for (i = 0; i < (pinfo->rx_nrfifos - 1); i++, bdp++) {
+               bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo);
+               bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT;
                mem_addr += pinfo->rx_fifosize;
        }
 
+       bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo);
+       bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT;
+
        /* Set the physical address of the host memory
         * buffers in the buffer descriptors, and the
         * virtual address for us to work with.
         */
        mem_addr = pinfo->mem_addr + L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize);
-       pinfo->tx_cur = pinfo->tx_bd_base;
-       for (bdp = pinfo->tx_bd_base, i = 0; i < pinfo->tx_nrfifos; i++, bdp++) {
-               bdp->cbd_bufaddr = virt_to_bus(mem_addr);
-               bdp->cbd_sc = BD_SC_INTRPT | (i < (pinfo->tx_nrfifos - 1) ? 0 : BD_SC_WRAP);
+       bdp = pinfo->tx_cur = pinfo->tx_bd_base;
+       for (i = 0; i < (pinfo->tx_nrfifos - 1); i++, bdp++) {
+               bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo);
+               bdp->cbd_sc = BD_SC_INTRPT;
                mem_addr += pinfo->tx_fifosize;
-               bdp++;
        }
 
+       bdp->cbd_bufaddr = cpu2cpm_addr(mem_addr, pinfo);
+       bdp->cbd_sc = BD_SC_WRAP | BD_SC_INTRPT;
+}
+
+static void cpm_uart_init_scc(struct uart_cpm_port *pinfo)
+{
+       int line = pinfo - cpm_uart_ports;
+       volatile scc_t *scp;
+       volatile scc_uart_t *sup;
+
+       pr_debug("CPM uart[%d]:init_scc\n", pinfo->port.line);
+
+       scp = pinfo->sccp;
+       sup = pinfo->sccup;
+
        /* Store address */
        pinfo->sccup->scc_genscc.scc_rbase = (unsigned char *)pinfo->rx_bd_base - DPRAM_BASE;
        pinfo->sccup->scc_genscc.scc_tbase = (unsigned char *)pinfo->tx_bd_base - DPRAM_BASE;
@@ -742,56 +793,41 @@ static void cpm_uart_init_scc(struct uart_cpm_port *pinfo, int bits, u16 scval)
            (SCC_GSMRL_MODE_UART | SCC_GSMRL_TDCR_16 | SCC_GSMRL_RDCR_16);
 
        /* Enable rx interrupts  and clear all pending events.  */
-       scp->scc_sccm = UART_SCCM_RX;
+       scp->scc_sccm = 0;
        scp->scc_scce = 0xffff;
        scp->scc_dsr = 0x7e7e;
-       scp->scc_psmr = (bits << 12) | scval;
+       scp->scc_psmr = 0x3000;
 
        scp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT);
 }
 
-static void cpm_uart_init_smc(struct uart_cpm_port *pinfo, int bits, u16 cval)
+static void cpm_uart_init_smc(struct uart_cpm_port *pinfo)
 {
        int line = pinfo - cpm_uart_ports;
        volatile smc_t *sp;
        volatile smc_uart_t *up;
-       volatile u8 *mem_addr;
-       volatile cbd_t *bdp;
-       int i;
 
        pr_debug("CPM uart[%d]:init_smc\n", pinfo->port.line);
 
        sp = pinfo->smcp;
        up = pinfo->smcup;
 
-       /* Set the physical address of the host memory
-        * buffers in the buffer descriptors, and the
-        * virtual address for us to work with.
-        */
-       mem_addr = pinfo->mem_addr;
-       pinfo->rx_cur = pinfo->rx_bd_base;
-       for (bdp = pinfo->rx_bd_base, i = 0; i < pinfo->rx_nrfifos; i++, bdp++) {
-               bdp->cbd_bufaddr = virt_to_bus(mem_addr);
-               bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT | (i < (pinfo->rx_nrfifos - 1) ? 0 : BD_SC_WRAP);
-               mem_addr += pinfo->rx_fifosize;
-       }
-
-       /* Set the physical address of the host memory
-        * buffers in the buffer descriptors, and the
-        * virtual address for us to work with.
-        */
-       mem_addr = pinfo->mem_addr + L1_CACHE_ALIGN(pinfo->rx_nrfifos * pinfo->rx_fifosize);
-       pinfo->tx_cur = pinfo->tx_bd_base;
-       for (bdp = pinfo->tx_bd_base, i = 0; i < pinfo->tx_nrfifos; i++, bdp++) {
-               bdp->cbd_bufaddr = virt_to_bus(mem_addr);
-               bdp->cbd_sc = BD_SC_INTRPT | (i < (pinfo->tx_nrfifos - 1) ? 0 : BD_SC_WRAP);
-               mem_addr += pinfo->tx_fifosize;
-       }
-
        /* Store address */
        pinfo->smcup->smc_rbase = (u_char *)pinfo->rx_bd_base - DPRAM_BASE;
        pinfo->smcup->smc_tbase = (u_char *)pinfo->tx_bd_base - DPRAM_BASE;
 
+/*
+ *  In case SMC1 is being relocated...
+ */
+#if defined (CONFIG_I2C_SPI_SMC1_UCODE_PATCH)
+       up->smc_rbptr = pinfo->smcup->smc_rbase;
+       up->smc_tbptr = pinfo->smcup->smc_tbase;
+       up->smc_rstate = 0;
+       up->smc_tstate = 0;
+       up->smc_brkcr = 1;              /* number of break chars */
+       up->smc_brkec = 0;
+#endif
+
        /* Set up the uart parameters in the
         * parameter ram.
         */
@@ -800,15 +836,19 @@ static void cpm_uart_init_smc(struct uart_cpm_port *pinfo, int bits, u16 cval)
        /* Using idle charater time requires some additional tuning.  */
        up->smc_mrblr = pinfo->rx_fifosize;
        up->smc_maxidl = pinfo->rx_fifosize;
+       up->smc_brklen = 0;
+       up->smc_brkec = 0;
        up->smc_brkcr = 1;
 
        cpm_line_cr_cmd(line, CPM_CR_INIT_TRX);
 
-       /* Set UART mode, according to the parameters */
-       sp->smc_smcmr = smcr_mk_clen(bits) | cval | SMCMR_SM_UART;
+       /* Set UART mode, 8 bit, no parity, one stop.
+        * Enable receive and transmit.
+        */
+       sp->smc_smcmr = smcr_mk_clen(9) | SMCMR_SM_UART;
 
        /* Enable only rx interrupts clear all pending events. */
-       sp->smc_smcm = SMCM_RX;
+       sp->smc_smcm = 0;
        sp->smc_smce = 0xff;
 
        sp->smc_smcmr |= (SMCMR_REN | SMCMR_TEN);
@@ -828,18 +868,25 @@ static int cpm_uart_request_port(struct uart_port *port)
        if (pinfo->flags & FLAG_CONSOLE)
                return 0;
 
-       /*
-        * Setup any port IO, connect any baud rate generators,
-        * etc.  This is expected to be handled by board
-        * dependant code 
-        */
-       if (pinfo->set_lineif)
-               pinfo->set_lineif(pinfo);
+       if (IS_SMC(pinfo)) {
+               pinfo->smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX);
+               pinfo->smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
+       } else {
+               pinfo->sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX);
+               pinfo->sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
+       }
 
        ret = cpm_uart_allocbuf(pinfo, 0);
+
        if (ret)
                return ret;
 
+       cpm_uart_initbd(pinfo);
+       if (IS_SMC(pinfo))
+               cpm_uart_init_smc(pinfo);
+       else
+               cpm_uart_init_scc(pinfo);
+
        return 0;
 }
 
@@ -887,12 +934,13 @@ struct uart_cpm_port cpm_uart_ports[UART_NR] = {
                .port = {
                        .irq            = SMC1_IRQ,
                        .ops            = &cpm_uart_pops,
-                       .iotype         = SERIAL_IO_MEM,
+                       .iotype         = UPIO_MEM,
+                       .lock           = SPIN_LOCK_UNLOCKED,
                },
                .flags = FLAG_SMC,
                .tx_nrfifos = TX_NUM_FIFO,
                .tx_fifosize = TX_BUF_SIZE,
-               .rx_nrfifos = RX_NUM_FIFO, 
+               .rx_nrfifos = RX_NUM_FIFO,
                .rx_fifosize = RX_BUF_SIZE,
                .set_lineif = smc1_lineif,
        },
@@ -900,65 +948,129 @@ struct uart_cpm_port cpm_uart_ports[UART_NR] = {
                .port = {
                        .irq            = SMC2_IRQ,
                        .ops            = &cpm_uart_pops,
-                       .iotype         = SERIAL_IO_MEM,
+                       .iotype         = UPIO_MEM,
+                       .lock           = SPIN_LOCK_UNLOCKED,
                },
                .flags = FLAG_SMC,
                .tx_nrfifos = TX_NUM_FIFO,
                .tx_fifosize = TX_BUF_SIZE,
-               .rx_nrfifos = RX_NUM_FIFO, 
+               .rx_nrfifos = RX_NUM_FIFO,
                .rx_fifosize = RX_BUF_SIZE,
                .set_lineif = smc2_lineif,
+#ifdef CONFIG_SERIAL_CPM_ALT_SMC2
+               .is_portb = 1,
+#endif
        },
        [UART_SCC1] = {
                .port = {
                        .irq            = SCC1_IRQ,
                        .ops            = &cpm_uart_pops,
-                       .iotype         = SERIAL_IO_MEM,
+                       .iotype         = UPIO_MEM,
+                       .lock           = SPIN_LOCK_UNLOCKED,
                },
                .tx_nrfifos = TX_NUM_FIFO,
                .tx_fifosize = TX_BUF_SIZE,
-               .rx_nrfifos = RX_NUM_FIFO, 
+               .rx_nrfifos = RX_NUM_FIFO,
                .rx_fifosize = RX_BUF_SIZE,
                .set_lineif = scc1_lineif,
+               .wait_closing = SCC_WAIT_CLOSING,
        },
        [UART_SCC2] = {
                .port = {
                        .irq            = SCC2_IRQ,
                        .ops            = &cpm_uart_pops,
-                       .iotype         = SERIAL_IO_MEM,
+                       .iotype         = UPIO_MEM,
+                       .lock           = SPIN_LOCK_UNLOCKED,
                },
                .tx_nrfifos = TX_NUM_FIFO,
                .tx_fifosize = TX_BUF_SIZE,
-               .rx_nrfifos = RX_NUM_FIFO, 
+               .rx_nrfifos = RX_NUM_FIFO,
                .rx_fifosize = RX_BUF_SIZE,
                .set_lineif = scc2_lineif,
+               .wait_closing = SCC_WAIT_CLOSING,
        },
        [UART_SCC3] = {
                .port = {
                        .irq            = SCC3_IRQ,
                        .ops            = &cpm_uart_pops,
-                       .iotype         = SERIAL_IO_MEM,
+                       .iotype         = UPIO_MEM,
+                       .lock           = SPIN_LOCK_UNLOCKED,
                },
                .tx_nrfifos = TX_NUM_FIFO,
                .tx_fifosize = TX_BUF_SIZE,
-               .rx_nrfifos = RX_NUM_FIFO, 
+               .rx_nrfifos = RX_NUM_FIFO,
                .rx_fifosize = RX_BUF_SIZE,
                .set_lineif = scc3_lineif,
+               .wait_closing = SCC_WAIT_CLOSING,
        },
        [UART_SCC4] = {
                .port = {
                        .irq            = SCC4_IRQ,
                        .ops            = &cpm_uart_pops,
-                       .iotype         = SERIAL_IO_MEM,
+                       .iotype         = UPIO_MEM,
+                       .lock           = SPIN_LOCK_UNLOCKED,
                },
                .tx_nrfifos = TX_NUM_FIFO,
                .tx_fifosize = TX_BUF_SIZE,
-               .rx_nrfifos = RX_NUM_FIFO, 
+               .rx_nrfifos = RX_NUM_FIFO,
                .rx_fifosize = RX_BUF_SIZE,
                .set_lineif = scc4_lineif,
+               .wait_closing = SCC_WAIT_CLOSING,
        },
 };
 
+int cpm_uart_drv_get_platform_data(struct platform_device *pdev, int is_con)
+{
+       struct resource *r;
+       struct fs_uart_platform_info *pdata = pdev->dev.platform_data;
+       int idx = pdata->fs_no; /* It is UART_SMCx or UART_SCCx index */
+       struct uart_cpm_port *pinfo;
+       int line;
+       u32 mem, pram;
+
+       line = cpm_uart_id2nr(idx);
+       if(line < 0) {
+               printk(KERN_ERR"%s(): port %d is not registered", __FUNCTION__, idx);
+               return -1;
+       }
+
+       pinfo = (struct uart_cpm_port *) &cpm_uart_ports[idx];
+
+       pinfo->brg = pdata->brg;
+
+       if (!is_con) {
+               pinfo->port.line = line;
+               pinfo->port.flags = UPF_BOOT_AUTOCONF;
+       }
+
+       if (!(r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs")))
+               return -EINVAL;
+       mem = r->start;
+
+       if (!(r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pram")))
+               return -EINVAL;
+       pram = r->start;
+
+       if(idx > fsid_smc2_uart) {
+               pinfo->sccp = (scc_t *)mem;
+               pinfo->sccup = (scc_uart_t *)pram;
+       } else {
+               pinfo->smcp = (smc_t *)mem;
+               pinfo->smcup = (smc_uart_t *)pram;
+       }
+       pinfo->tx_nrfifos = pdata->tx_num_fifo;
+       pinfo->tx_fifosize = pdata->tx_buf_size;
+
+       pinfo->rx_nrfifos = pdata->rx_num_fifo;
+       pinfo->rx_fifosize = pdata->rx_buf_size;
+
+       pinfo->port.uartclk = pdata->uart_clk;
+       pinfo->port.mapbase = (unsigned long)mem;
+       pinfo->port.irq = platform_get_irq(pdev, 0);
+
+       return 0;
+}
+
 #ifdef CONFIG_SERIAL_CPM_CONSOLE
 /*
  *     Print a string to the serial port trying not to disturb
@@ -975,9 +1087,6 @@ static void cpm_uart_console_write(struct console *co, const char *s,
        volatile cbd_t *bdp, *bdbase;
        volatile unsigned char *cp;
 
-       if (IS_DISCARDING(pinfo))
-               return;
-
        /* Get the address of the host memory buffer.
         */
        bdp = pinfo->tx_cur;
@@ -1001,11 +1110,8 @@ static void cpm_uart_console_write(struct console *co, const char *s,
                 * If the buffer address is in the CPM DPRAM, don't
                 * convert it.
                 */
-               if ((uint) (bdp->cbd_bufaddr) > (uint) CPM_ADDR)
-                       cp = (unsigned char *) (bdp->cbd_bufaddr);
-               else
-                       cp = bus_to_virt(bdp->cbd_bufaddr);
-               
+               cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo);
+
                *cp = *s;
 
                bdp->cbd_datlen = 1;
@@ -1021,10 +1127,7 @@ static void cpm_uart_console_write(struct console *co, const char *s,
                        while ((bdp->cbd_sc & BD_SC_READY) != 0)
                                ;
 
-                       if ((uint) (bdp->cbd_bufaddr) > (uint) CPM_ADDR)
-                               cp = (unsigned char *) (bdp->cbd_bufaddr);
-                       else
-                               cp = bus_to_virt(bdp->cbd_bufaddr);
+                       cp = cpm2cpu_addr(bdp->cbd_bufaddr, pinfo);
 
                        *cp = 13;
                        bdp->cbd_datlen = 1;
@@ -1047,9 +1150,7 @@ static void cpm_uart_console_write(struct console *co, const char *s,
        pinfo->tx_cur = (volatile cbd_t *) bdp;
 }
 
-/*
- * Setup console. Be careful is called early !
- */
+
 static int __init cpm_uart_console_setup(struct console *co, char *options)
 {
        struct uart_port *port;
@@ -1060,10 +1161,30 @@ static int __init cpm_uart_console_setup(struct console *co, char *options)
        int flow = 'n';
        int ret;
 
+       struct fs_uart_platform_info *pdata;
+       struct platform_device* pdev = early_uart_get_pdev(co->index);
+
+       if (!pdev) {
+               pr_info("cpm_uart: console: compat mode\n");
+               /* compatibility - will be cleaned up */
+               cpm_uart_init_portdesc();
+       }
+
        port =
            (struct uart_port *)&cpm_uart_ports[cpm_uart_port_map[co->index]];
        pinfo = (struct uart_cpm_port *)port;
-       
+       if (!pdev) {
+               if (pinfo->set_lineif)
+                       pinfo->set_lineif(pinfo);
+       } else {
+               pdata = pdev->dev.platform_data;
+               if (pdata)
+                       if (pdata->init_ioports)
+                               pdata->init_ioports();
+
+               cpm_uart_drv_get_platform_data(pdev, 1);
+       }
+
        pinfo->flags |= FLAG_CONSOLE;
 
        if (options) {
@@ -1077,41 +1198,46 @@ static int __init cpm_uart_console_setup(struct console *co, char *options)
                        baud = 9600;
        }
 
-       /*
-        * Setup any port IO, connect any baud rate generators,
-        * etc.  This is expected to be handled by board
-        * dependant code 
-        */
-       if (pinfo->set_lineif)
-               pinfo->set_lineif(pinfo);
+       if (IS_SMC(pinfo)) {
+               pinfo->smcp->smc_smcm &= ~(SMCM_RX | SMCM_TX);
+               pinfo->smcp->smc_smcmr &= ~(SMCMR_REN | SMCMR_TEN);
+       } else {
+               pinfo->sccp->scc_sccm &= ~(UART_SCCM_TX | UART_SCCM_RX);
+               pinfo->sccp->scc_gsmrl &= ~(SCC_GSMRL_ENR | SCC_GSMRL_ENT);
+       }
 
        ret = cpm_uart_allocbuf(pinfo, 1);
+
        if (ret)
                return ret;
 
+       cpm_uart_initbd(pinfo);
+
+       if (IS_SMC(pinfo))
+               cpm_uart_init_smc(pinfo);
+       else
+               cpm_uart_init_scc(pinfo);
+
        uart_set_options(port, co, baud, parity, bits, flow);
 
        return 0;
 }
 
-extern struct uart_driver cpm_reg;
+static struct uart_driver cpm_reg;
 static struct console cpm_scc_uart_console = {
-       .name           "ttyCPM",
-       .write          cpm_uart_console_write,
-       .device         uart_console_device,
-       .setup          cpm_uart_console_setup,
-       .flags          CON_PRINTBUFFER,
-       .index          -1,
+       .name           "ttyCPM",
+       .write          cpm_uart_console_write,
+       .device         uart_console_device,
+       .setup          cpm_uart_console_setup,
+       .flags          CON_PRINTBUFFER,
+       .index          -1,
        .data           = &cpm_reg,
 };
 
 int __init cpm_uart_console_init(void)
 {
-       int ret = cpm_uart_init_portdesc();
-
-       if (!ret)
-               register_console(&cpm_scc_uart_console);
-       return ret;
+       register_console(&cpm_scc_uart_console);
+       return 0;
 }
 
 console_initcall(cpm_uart_console_init);
@@ -1129,44 +1255,129 @@ static struct uart_driver cpm_reg = {
        .minor          = SERIAL_CPM_MINOR,
        .cons           = CPM_UART_CONSOLE,
 };
-
-static int __init cpm_uart_init(void)
+static int cpm_uart_drv_probe(struct device *dev)
 {
-       int ret, i;
+       struct platform_device  *pdev = to_platform_device(dev);
+       struct fs_uart_platform_info *pdata;
+       int ret = -ENODEV;
 
-       printk(KERN_INFO "Serial: CPM driver $Revision: 0.01 $\n");
-
-#ifndef CONFIG_SERIAL_CPM_CONSOLE
-       ret = cpm_uart_init_portdesc();
-       if (ret)
+       if(!pdev) {
+               printk(KERN_ERR"CPM UART: platform data missing!\n");
                return ret;
-#endif
+       }
 
-       cpm_reg.nr = cpm_uart_nr;
-       ret = uart_register_driver(&cpm_reg);
+       pdata = pdev->dev.platform_data;
+       pr_debug("cpm_uart_drv_probe: Adding CPM UART %d\n", cpm_uart_id2nr(pdata->fs_no));
 
-       if (ret)
+       if ((ret = cpm_uart_drv_get_platform_data(pdev, 0)))
                return ret;
 
-       for (i = 0; i < cpm_uart_nr; i++) {
-               int con = cpm_uart_port_map[i];
-               cpm_uart_ports[con].port.line = i;
-               cpm_uart_ports[con].port.flags = UPF_BOOT_AUTOCONF;
-               uart_add_one_port(&cpm_reg, &cpm_uart_ports[con].port);
-       }
+       if (pdata->init_ioports)
+                pdata->init_ioports();
 
-       return ret;
+       ret = uart_add_one_port(&cpm_reg, &cpm_uart_ports[pdata->fs_no].port);
+
+        return ret;
 }
 
-static void __exit cpm_uart_exit(void)
+static int cpm_uart_drv_remove(struct device *dev)
 {
+       struct platform_device  *pdev = to_platform_device(dev);
+       struct fs_uart_platform_info *pdata = pdev->dev.platform_data;
+
+       pr_debug("cpm_uart_drv_remove: Removing CPM UART %d\n",
+                       cpm_uart_id2nr(pdata->fs_no));
+
+        uart_remove_one_port(&cpm_reg, &cpm_uart_ports[pdata->fs_no].port);
+        return 0;
+}
+
+static struct device_driver cpm_smc_uart_driver = {
+        .name   = "fsl-cpm-smc:uart",
+        .bus    = &platform_bus_type,
+        .probe  = cpm_uart_drv_probe,
+        .remove = cpm_uart_drv_remove,
+};
+
+static struct device_driver cpm_scc_uart_driver = {
+        .name   = "fsl-cpm-scc:uart",
+        .bus    = &platform_bus_type,
+        .probe  = cpm_uart_drv_probe,
+        .remove = cpm_uart_drv_remove,
+};
+
+/*
+   This is supposed to match uart devices on platform bus,
+   */
+static int match_is_uart (struct device* dev, void* data)
+{
+       struct platform_device* pdev = container_of(dev, struct platform_device, dev);
+       int ret = 0;
+       /* this was setfunc as uart */
+       if(strstr(pdev->name,":uart")) {
+               ret = 1;
+       }
+       return ret;
+}
+
+
+static int cpm_uart_init(void) {
+
+       int ret;
        int i;
+       struct device *dev;
+       printk(KERN_INFO "Serial: CPM driver $Revision: 0.02 $\n");
+
+       /* lookup the bus for uart devices */
+       dev = bus_find_device(&platform_bus_type, NULL, 0, match_is_uart);
+
+       /* There are devices on the bus - all should be OK  */
+       if (dev) {
+               cpm_uart_count();
+               cpm_reg.nr = cpm_uart_nr;
+
+               if (!(ret = uart_register_driver(&cpm_reg))) {
+                       if ((ret = driver_register(&cpm_smc_uart_driver))) {
+                               uart_unregister_driver(&cpm_reg);
+                               return ret;
+                       }
+                       if ((ret = driver_register(&cpm_scc_uart_driver))) {
+                               driver_unregister(&cpm_scc_uart_driver);
+                               uart_unregister_driver(&cpm_reg);
+                       }
+               }
+       } else {
+       /* No capable platform devices found - falling back to legacy mode */
+               pr_info("cpm_uart: WARNING: no UART devices found on platform bus!\n");
+               pr_info(
+               "cpm_uart: the driver will guess configuration, but this mode is no longer supported.\n");
+#ifndef CONFIG_SERIAL_CPM_CONSOLE
+               ret = cpm_uart_init_portdesc();
+               if (ret)
+                       return ret;
+#endif
+
+               cpm_reg.nr = cpm_uart_nr;
+               ret = uart_register_driver(&cpm_reg);
+
+               if (ret)
+                       return ret;
+
+               for (i = 0; i < cpm_uart_nr; i++) {
+                       int con = cpm_uart_port_map[i];
+                       cpm_uart_ports[con].port.line = i;
+                       cpm_uart_ports[con].port.flags = UPF_BOOT_AUTOCONF;
+                       uart_add_one_port(&cpm_reg, &cpm_uart_ports[con].port);
+               }
 
-       for (i = 0; i < cpm_uart_nr; i++) {
-               int con = cpm_uart_port_map[i];
-               uart_remove_one_port(&cpm_reg, &cpm_uart_ports[con].port);
        }
+       return ret;
+}
 
+static void __exit cpm_uart_exit(void)
+{
+       driver_unregister(&cpm_scc_uart_driver);
+       driver_unregister(&cpm_smc_uart_driver);
        uart_unregister_driver(&cpm_reg);
 }