Add Station and AdHoc mode support to libertas_tf
[safe/jmp/linux-2.6] / drivers / serial / bfin_5xx.c
index fd9bb77..50abb7e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Blackfin On-Chip Serial Driver
  *
- * Copyright 2006-2007 Analog Devices Inc.
+ * Copyright 2006-2008 Analog Devices Inc.
  *
  * Enter bugs at http://blackfin.uclinux.org/
  *
 #include <linux/tty_flip.h>
 #include <linux/serial_core.h>
 
-#ifdef CONFIG_KGDB_UART
+#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
+       defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
 #include <linux/kgdb.h>
 #include <asm/irq_regs.h>
 #endif
 
 #include <asm/gpio.h>
-#include <asm/mach/bfin_serial_5xx.h>
+#include <mach/bfin_serial_5xx.h>
 
 #ifdef CONFIG_SERIAL_BFIN_DMA
 #include <linux/dma-mapping.h>
 #include <asm/cacheflush.h>
 #endif
 
+#ifdef CONFIG_SERIAL_BFIN_MODULE
+# undef CONFIG_EARLY_PRINTK
+#endif
+
+#ifdef CONFIG_SERIAL_BFIN_MODULE
+# undef CONFIG_EARLY_PRINTK
+#endif
+
 /* UART name and device definitions */
 #define BFIN_SERIAL_NAME       "ttyBF"
 #define BFIN_SERIAL_MAJOR      204
 #define BFIN_SERIAL_MINOR      64
 
+static struct bfin_serial_port bfin_serial_ports[BFIN_UART_NR_PORTS];
+static int nr_active_ports = ARRAY_SIZE(bfin_serial_resource);
+
+#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
+       defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
+
+# ifndef CONFIG_SERIAL_BFIN_PIO
+#  error KGDB only support UART in PIO mode.
+# endif
+
+static int kgdboc_port_line;
+static int kgdboc_break_enabled;
+#endif
 /*
  * Setup for console. Argument comes from the menuconfig
  */
@@ -49,7 +71,6 @@
 #define DMA_RX_YCOUNT          (PAGE_SIZE / DMA_RX_XCOUNT)
 
 #define DMA_RX_FLUSH_JIFFIES   (HZ / 50)
-#define CTS_CHECK_JIFFIES      (HZ / 50)
 
 #ifdef CONFIG_SERIAL_BFIN_DMA
 static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart);
@@ -57,7 +78,64 @@ static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart);
 static void bfin_serial_tx_chars(struct bfin_serial_port *uart);
 #endif
 
-static void bfin_serial_mctrl_check(struct bfin_serial_port *uart);
+static void bfin_serial_reset_irda(struct uart_port *port);
+
+#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \
+       defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS)
+static unsigned int bfin_serial_get_mctrl(struct uart_port *port)
+{
+       struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+       if (uart->cts_pin < 0)
+               return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+
+       /* CTS PIN is negative assertive. */
+       if (UART_GET_CTS(uart))
+               return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+       else
+               return TIOCM_DSR | TIOCM_CAR;
+}
+
+static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+       struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+       if (uart->rts_pin < 0)
+               return;
+
+       /* RTS PIN is negative assertive. */
+       if (mctrl & TIOCM_RTS)
+               UART_ENABLE_RTS(uart);
+       else
+               UART_DISABLE_RTS(uart);
+}
+
+/*
+ * Handle any change of modem status signal.
+ */
+static irqreturn_t bfin_serial_mctrl_cts_int(int irq, void *dev_id)
+{
+       struct bfin_serial_port *uart = dev_id;
+       unsigned int status;
+
+       status = bfin_serial_get_mctrl(&uart->port);
+       uart_handle_cts_change(&uart->port, status & TIOCM_CTS);
+#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
+       uart->scts = 1;
+       UART_CLEAR_SCTS(uart);
+       UART_CLEAR_IER(uart, EDSSI);
+#endif
+
+       return IRQ_HANDLED;
+}
+#else
+static unsigned int bfin_serial_get_mctrl(struct uart_port *port)
+{
+       return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
+}
+
+static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
+{
+}
+#endif
 
 /*
  * interrupts are disabled on entry
@@ -65,7 +143,9 @@ static void bfin_serial_mctrl_check(struct bfin_serial_port *uart);
 static void bfin_serial_stop_tx(struct uart_port *port)
 {
        struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
-       struct circ_buf *xmit = &uart->port.info->xmit;
+#ifdef CONFIG_SERIAL_BFIN_DMA
+       struct circ_buf *xmit = &uart->port.state->xmit;
+#endif
 
        while (!(UART_GET_LSR(uart) & TEMT))
                cpu_relax();
@@ -91,6 +171,21 @@ static void bfin_serial_stop_tx(struct uart_port *port)
 static void bfin_serial_start_tx(struct uart_port *port)
 {
        struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+       struct tty_struct *tty = uart->port.state->port.tty;
+
+#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
+       if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) {
+               uart->scts = 0;
+               uart_handle_cts_change(&uart->port, uart->scts);
+       }
+#endif
+
+       /*
+        * To avoid losting RX interrupt, we reset IR function
+        * before sending data.
+        */
+       if (tty->termios->c_line == N_IRDA)
+               bfin_serial_reset_irda(port);
 
 #ifdef CONFIG_SERIAL_BFIN_DMA
        if (uart->tx_done)
@@ -107,9 +202,7 @@ static void bfin_serial_start_tx(struct uart_port *port)
 static void bfin_serial_stop_rx(struct uart_port *port)
 {
        struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
-#ifdef CONFIG_KGDB_UART
-       if (uart->port.line != CONFIG_KGDB_UART_PORT)
-#endif
+
        UART_CLEAR_IER(uart, ERBFI);
 }
 
@@ -120,49 +213,6 @@ static void bfin_serial_enable_ms(struct uart_port *port)
 {
 }
 
-#ifdef CONFIG_KGDB_UART
-static int kgdb_entry_state;
-
-void kgdb_put_debug_char(int chr)
-{
-       struct bfin_serial_port *uart;
-       
-       if (CONFIG_KGDB_UART_PORT < 0
-               || CONFIG_KGDB_UART_PORT >= BFIN_UART_NR_PORTS)
-               uart = &bfin_serial_ports[0];
-       else
-               uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];
-       
-       while (!(UART_GET_LSR(uart) & THRE)) {
-               SSYNC();
-       }
-
-       UART_CLEAR_DLAB(uart);
-       UART_PUT_CHAR(uart, (unsigned char)chr);
-       SSYNC();
-}
-
-int kgdb_get_debug_char(void)
-{
-       struct bfin_serial_port *uart;
-       unsigned char chr;
-
-       if (CONFIG_KGDB_UART_PORT < 0
-               || CONFIG_KGDB_UART_PORT >= BFIN_UART_NR_PORTS)
-               uart = &bfin_serial_ports[0];
-       else
-               uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];
-       
-       while(!(UART_GET_LSR(uart) & DR)) {
-               SSYNC();
-       }
-       UART_CLEAR_DLAB(uart);
-       chr = UART_GET_CHAR(uart);
-       SSYNC();
-
-       return chr;
-}
-#endif
 
 #if ANOMALY_05000363 && defined(CONFIG_SERIAL_BFIN_PIO)
 # define UART_GET_ANOMALY_THRESHOLD(uart)    ((uart)->anomaly_threshold)
@@ -175,7 +225,7 @@ int kgdb_get_debug_char(void)
 #ifdef CONFIG_SERIAL_BFIN_PIO
 static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
 {
-       struct tty_struct *tty = uart->port.info->tty;
+       struct tty_struct *tty = NULL;
        unsigned int status, ch, flg;
        static struct timeval anomaly_start = { .tv_sec = 0 };
 
@@ -185,27 +235,18 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
        ch = UART_GET_CHAR(uart);
        uart->port.icount.rx++;
 
-#ifdef CONFIG_KGDB_UART
-       if (uart->port.line == CONFIG_KGDB_UART_PORT) {
-               struct pt_regs *regs = get_irq_regs();
-               if (uart->port.cons->index == CONFIG_KGDB_UART_PORT && ch == 0x1) { /* Ctrl + A */
-                       kgdb_breakkey_pressed(regs);
-                       return;
-               } else if (kgdb_entry_state == 0 && ch == '$') {/* connection from KGDB */
-                       kgdb_entry_state = 1;
-               } else if (kgdb_entry_state == 1 && ch == 'q') {
-                       kgdb_entry_state = 0;
-                       kgdb_breakkey_pressed(regs);
+#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
+       defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
+       if (kgdb_connected && kgdboc_port_line == uart->port.line)
+               if (ch == 0x3) {/* Ctrl + C */
+                       kgdb_breakpoint();
                        return;
-               } else if (ch == 0x3) {/* Ctrl + C */
-                       kgdb_entry_state = 0;
-                       kgdb_breakkey_pressed(regs);
-                       return;
-               } else {
-                       kgdb_entry_state = 0;
                }
-       }
+
+       if (!uart->port.state || !uart->port.state->port.tty)
+               return;
 #endif
+       tty = uart->port.state->port.tty;
 
        if (ANOMALY_05000363) {
                /* The BF533 (and BF561) family of processors have a nice anomaly
@@ -247,6 +288,7 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
                        return;
 
  known_good_char:
+                       status &= ~BI;
                        anomaly_start.tv_sec = 0;
                }
        }
@@ -289,16 +331,19 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
 
 static void bfin_serial_tx_chars(struct bfin_serial_port *uart)
 {
-       struct circ_buf *xmit = &uart->port.info->xmit;
-
-       /*
-        * Check the modem control lines before
-        * transmitting anything.
-        */
-       bfin_serial_mctrl_check(uart);
+       struct circ_buf *xmit = &uart->port.state->xmit;
 
        if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) {
-               bfin_serial_stop_tx(&uart->port);
+#ifdef CONFIG_BF54x
+               /* Clear TFI bit */
+               UART_PUT_LSR(uart, TFI);
+#endif
+               /* Anomaly notes:
+                *  05000215 -  we always clear ETBEI within last UART TX
+                *              interrupt to end a string. It is always set
+                *              when start a new tx.
+                */
+               UART_CLEAR_IER(uart, ETBEI);
                return;
        }
 
@@ -317,9 +362,6 @@ static void bfin_serial_tx_chars(struct bfin_serial_port *uart)
 
        if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
                uart_write_wakeup(&uart->port);
-
-       if (uart_circ_empty(xmit))
-               bfin_serial_stop_tx(&uart->port);
 }
 
 static irqreturn_t bfin_serial_rx_int(int irq, void *dev_id)
@@ -338,6 +380,12 @@ static irqreturn_t bfin_serial_tx_int(int irq, void *dev_id)
 {
        struct bfin_serial_port *uart = dev_id;
 
+#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
+       if (uart->scts && !(bfin_serial_get_mctrl(&uart->port) & TIOCM_CTS)) {
+               uart->scts = 0;
+               uart_handle_cts_change(&uart->port, uart->scts);
+       }
+#endif
        spin_lock(&uart->port.lock);
        if (UART_GET_LSR(uart) & THRE)
                bfin_serial_tx_chars(uart);
@@ -350,16 +398,10 @@ static irqreturn_t bfin_serial_tx_int(int irq, void *dev_id)
 #ifdef CONFIG_SERIAL_BFIN_DMA
 static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart)
 {
-       struct circ_buf *xmit = &uart->port.info->xmit;
+       struct circ_buf *xmit = &uart->port.state->xmit;
 
        uart->tx_done = 0;
 
-       /*
-        * Check the modem control lines before
-        * transmitting anything.
-        */
-       bfin_serial_mctrl_check(uart);
-
        if (uart_circ_empty(xmit) || uart_tx_stopped(&uart->port)) {
                uart->tx_count = 0;
                uart->tx_done = 1;
@@ -386,6 +428,7 @@ static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart)
        set_dma_start_addr(uart->tx_dma_channel, (unsigned long)(xmit->buf+xmit->tail));
        set_dma_x_count(uart->tx_dma_channel, uart->tx_count);
        set_dma_x_modify(uart->tx_dma_channel, 1);
+       SSYNC();
        enable_dma(uart->tx_dma_channel);
 
        UART_SET_IER(uart, ETBEI);
@@ -393,7 +436,7 @@ static void bfin_serial_dma_tx_chars(struct bfin_serial_port *uart)
 
 static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart)
 {
-       struct tty_struct *tty = uart->port.info->tty;
+       struct tty_struct *tty = uart->port.state->port.tty;
        int i, flg, status;
 
        status = UART_GET_LSR(uart);
@@ -427,9 +470,11 @@ static void bfin_serial_dma_rx_chars(struct bfin_serial_port *uart)
        else
                flg = TTY_NORMAL;
 
-       for (i = uart->rx_dma_buf.tail; i != uart->rx_dma_buf.head; i++) {
+       for (i = uart->rx_dma_buf.tail; ; i++) {
                if (i >= UART_XMIT_SIZE)
                        i = 0;
+               if (i == uart->rx_dma_buf.head)
+                       break;
                if (!uart_handle_sysrq_char(&uart->port, uart->rx_dma_buf.buf[i]))
                        uart_insert_char(&uart->port, status, OE,
                                uart->rx_dma_buf.buf[i], flg);
@@ -443,34 +488,65 @@ void bfin_serial_rx_dma_timeout(struct bfin_serial_port *uart)
 {
        int x_pos, pos;
 
+       dma_disable_irq(uart->rx_dma_channel);
+       spin_lock_bh(&uart->port.lock);
+
+       /* 2D DMA RX buffer ring is used. Because curr_y_count and
+        * curr_x_count can't be read as an atomic operation,
+        * curr_y_count should be read before curr_x_count. When
+        * curr_x_count is read, curr_y_count may already indicate
+        * next buffer line. But, the position calculated here is
+        * still indicate the old line. The wrong position data may
+        * be smaller than current buffer tail, which cause garbages
+        * are received if it is not prohibit.
+        */
        uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel);
        x_pos = get_dma_curr_xcount(uart->rx_dma_channel);
        uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows;
-       if (uart->rx_dma_nrows == DMA_RX_YCOUNT)
+       if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0)
                uart->rx_dma_nrows = 0;
        x_pos = DMA_RX_XCOUNT - x_pos;
        if (x_pos == DMA_RX_XCOUNT)
                x_pos = 0;
 
        pos = uart->rx_dma_nrows * DMA_RX_XCOUNT + x_pos;
-       if (pos != uart->rx_dma_buf.tail) {
+       /* Ignore receiving data if new position is in the same line of
+        * current buffer tail and small.
+        */
+       if (pos > uart->rx_dma_buf.tail ||
+               uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) {
                uart->rx_dma_buf.head = pos;
                bfin_serial_dma_rx_chars(uart);
                uart->rx_dma_buf.tail = uart->rx_dma_buf.head;
        }
 
+       spin_unlock_bh(&uart->port.lock);
+       dma_enable_irq(uart->rx_dma_channel);
+
        mod_timer(&(uart->rx_dma_timer), jiffies + DMA_RX_FLUSH_JIFFIES);
 }
 
 static irqreturn_t bfin_serial_dma_tx_int(int irq, void *dev_id)
 {
        struct bfin_serial_port *uart = dev_id;
-       struct circ_buf *xmit = &uart->port.info->xmit;
+       struct circ_buf *xmit = &uart->port.state->xmit;
+
+#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
+       if (uart->scts && !(bfin_serial_get_mctrl(&uart->port)&TIOCM_CTS)) {
+               uart->scts = 0;
+               uart_handle_cts_change(&uart->port, uart->scts);
+       }
+#endif
 
        spin_lock(&uart->port.lock);
        if (!(get_dma_curr_irqstat(uart->tx_dma_channel)&DMA_RUN)) {
                disable_dma(uart->tx_dma_channel);
                clear_dma_irqstat(uart->tx_dma_channel);
+               /* Anomaly notes:
+                *  05000215 -  we always clear ETBEI within last UART TX
+                *              interrupt to end a string. It is always set
+                *              when start a new tx.
+                */
                UART_CLEAR_IER(uart, ETBEI);
                xmit->tail = (xmit->tail + uart->tx_count) & (UART_XMIT_SIZE - 1);
                uart->port.icount.tx += uart->tx_count;
@@ -489,13 +565,27 @@ static irqreturn_t bfin_serial_dma_rx_int(int irq, void *dev_id)
 {
        struct bfin_serial_port *uart = dev_id;
        unsigned short irqstat;
+       int x_pos, pos;
 
        spin_lock(&uart->port.lock);
        irqstat = get_dma_curr_irqstat(uart->rx_dma_channel);
        clear_dma_irqstat(uart->rx_dma_channel);
-       spin_unlock(&uart->port.lock);
 
-       mod_timer(&(uart->rx_dma_timer), jiffies);
+       uart->rx_dma_nrows = get_dma_curr_ycount(uart->rx_dma_channel);
+       x_pos = get_dma_curr_xcount(uart->rx_dma_channel);
+       uart->rx_dma_nrows = DMA_RX_YCOUNT - uart->rx_dma_nrows;
+       if (uart->rx_dma_nrows == DMA_RX_YCOUNT || x_pos == 0)
+               uart->rx_dma_nrows = 0;
+
+       pos = uart->rx_dma_nrows * DMA_RX_XCOUNT;
+       if (pos > uart->rx_dma_buf.tail ||
+               uart->rx_dma_nrows < (uart->rx_dma_buf.tail/DMA_RX_XCOUNT)) {
+               uart->rx_dma_buf.head = pos;
+               bfin_serial_dma_rx_chars(uart);
+               uart->rx_dma_buf.tail = uart->rx_dma_buf.head;
+       }
+
+       spin_unlock(&uart->port.lock);
 
        return IRQ_HANDLED;
 }
@@ -516,61 +606,6 @@ static unsigned int bfin_serial_tx_empty(struct uart_port *port)
                return 0;
 }
 
-static unsigned int bfin_serial_get_mctrl(struct uart_port *port)
-{
-#ifdef CONFIG_SERIAL_BFIN_CTSRTS
-       struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
-       if (uart->cts_pin < 0)
-               return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
-
-       if (UART_GET_CTS(uart))
-               return TIOCM_DSR | TIOCM_CAR;
-       else
-#endif
-               return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
-}
-
-static void bfin_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
-{
-#ifdef CONFIG_SERIAL_BFIN_CTSRTS
-       struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
-       if (uart->rts_pin < 0)
-               return;
-
-       if (mctrl & TIOCM_RTS)
-               UART_CLEAR_RTS(uart);
-       else
-               UART_SET_RTS(uart);
-#endif
-}
-
-/*
- * Handle any change of modem status signal since we were last called.
- */
-static void bfin_serial_mctrl_check(struct bfin_serial_port *uart)
-{
-#ifdef CONFIG_SERIAL_BFIN_CTSRTS
-       unsigned int status;
-       struct uart_info *info = uart->port.info;
-       struct tty_struct *tty = info->tty;
-
-       status = bfin_serial_get_mctrl(&uart->port);
-       uart_handle_cts_change(&uart->port, status & TIOCM_CTS);
-       if (!(status & TIOCM_CTS)) {
-               tty->hw_stopped = 1;
-               uart->cts_timer.data = (unsigned long)(uart);
-               uart->cts_timer.function = (void *)bfin_serial_mctrl_check;
-               uart->cts_timer.expires = jiffies + CTS_CHECK_JIFFIES;
-               add_timer(&(uart->cts_timer));
-       } else {
-               tty->hw_stopped = 0;
-       }
-#endif
-}
-
-/*
- * Interrupts are always disabled.
- */
 static void bfin_serial_break_ctl(struct uart_port *port, int break_state)
 {
        struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
@@ -626,16 +661,16 @@ static int bfin_serial_startup(struct uart_port *port)
        uart->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
        add_timer(&(uart->rx_dma_timer));
 #else
+# if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
+       defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
+       if (kgdboc_port_line == uart->port.line && kgdboc_break_enabled)
+               kgdboc_break_enabled = 0;
+       else {
+# endif
        if (request_irq(uart->port.irq, bfin_serial_rx_int, IRQF_DISABLED,
             "BFIN_UART_RX", uart)) {
-# ifdef        CONFIG_KGDB_UART
-               if (uart->port.line != CONFIG_KGDB_UART_PORT) {
-# endif
                printk(KERN_NOTICE "Unable to attach BlackFin UART RX interrupt\n");
                return -EBUSY;
-# ifdef        CONFIG_KGDB_UART
-               }
-# endif
        }
 
        if (request_irq
@@ -645,7 +680,86 @@ static int bfin_serial_startup(struct uart_port *port)
                free_irq(uart->port.irq, uart);
                return -EBUSY;
        }
+
+# ifdef CONFIG_BF54x
+       {
+               unsigned uart_dma_ch_rx, uart_dma_ch_tx;
+
+               switch (uart->port.irq) {
+               case IRQ_UART3_RX:
+                       uart_dma_ch_rx = CH_UART3_RX;
+                       uart_dma_ch_tx = CH_UART3_TX;
+                       break;
+               case IRQ_UART2_RX:
+                       uart_dma_ch_rx = CH_UART2_RX;
+                       uart_dma_ch_tx = CH_UART2_TX;
+                       break;
+               default:
+                       uart_dma_ch_rx = uart_dma_ch_tx = 0;
+                       break;
+               };
+
+               if (uart_dma_ch_rx &&
+                       request_dma(uart_dma_ch_rx, "BFIN_UART_RX") < 0) {
+                       printk(KERN_NOTICE"Fail to attach UART interrupt\n");
+                       free_irq(uart->port.irq, uart);
+                       free_irq(uart->port.irq + 1, uart);
+                       return -EBUSY;
+               }
+               if (uart_dma_ch_tx &&
+                       request_dma(uart_dma_ch_tx, "BFIN_UART_TX") < 0) {
+                       printk(KERN_NOTICE "Fail to attach UART interrupt\n");
+                       free_dma(uart_dma_ch_rx);
+                       free_irq(uart->port.irq, uart);
+                       free_irq(uart->port.irq + 1, uart);
+                       return -EBUSY;
+               }
+       }
+# endif
+# if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
+       defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
+       }
+# endif
 #endif
+
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+       if (uart->cts_pin >= 0) {
+               if (request_irq(gpio_to_irq(uart->cts_pin),
+                       bfin_serial_mctrl_cts_int,
+                       IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+                       IRQF_DISABLED, "BFIN_UART_CTS", uart)) {
+                       uart->cts_pin = -1;
+                       pr_info("Unable to attach BlackFin UART CTS interrupt.\
+                                So, disable it.\n");
+               }
+       }
+       if (uart->rts_pin >= 0) {
+               gpio_request(uart->rts_pin, DRIVER_NAME);
+               gpio_direction_output(uart->rts_pin, 0);
+       }
+#endif
+#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
+       if (request_irq(uart->status_irq,
+               bfin_serial_mctrl_cts_int,
+               IRQF_DISABLED, "BFIN_UART_MODEM_STATUS", uart)) {
+               pr_info("Unable to attach BlackFin UART Modem \
+                       Status interrupt.\n");
+       }
+
+       if (uart->cts_pin >= 0) {
+               gpio_request(uart->cts_pin, DRIVER_NAME);
+               gpio_direction_output(uart->cts_pin, 1);
+       }
+       if (uart->rts_pin >= 0) {
+               gpio_request(uart->rts_pin, DRIVER_NAME);
+               gpio_direction_output(uart->rts_pin, 0);
+       }
+
+       /* CTS RTS PINs are negative assertive. */
+       UART_PUT_MCR(uart, ACTS);
+       UART_SET_IER(uart, EDSSI);
+#endif
+
        UART_SET_IER(uart, ERBFI);
        return 0;
 }
@@ -662,12 +776,38 @@ static void bfin_serial_shutdown(struct uart_port *port)
        del_timer(&(uart->rx_dma_timer));
        dma_free_coherent(NULL, PAGE_SIZE, uart->rx_dma_buf.buf, 0);
 #else
-#ifdef CONFIG_KGDB_UART
-       if (uart->port.line != CONFIG_KGDB_UART_PORT)
+#ifdef CONFIG_BF54x
+       switch (uart->port.irq) {
+       case IRQ_UART3_RX:
+               free_dma(CH_UART3_RX);
+               free_dma(CH_UART3_TX);
+               break;
+       case IRQ_UART2_RX:
+               free_dma(CH_UART2_RX);
+               free_dma(CH_UART2_TX);
+               break;
+       default:
+               break;
+       };
 #endif
        free_irq(uart->port.irq, uart);
        free_irq(uart->port.irq+1, uart);
 #endif
+
+#ifdef CONFIG_SERIAL_BFIN_CTSRTS
+       if (uart->cts_pin >= 0)
+               free_irq(gpio_to_irq(uart->cts_pin), uart);
+       if (uart->rts_pin >= 0)
+               gpio_free(uart->rts_pin);
+#endif
+#ifdef CONFIG_SERIAL_BFIN_HARD_CTSRTS
+       if (uart->cts_pin >= 0)
+               gpio_free(uart->cts_pin);
+       if (uart->rts_pin >= 0)
+               gpio_free(uart->rts_pin);
+       if (UART_GET_IER(uart) && EDSSI)
+               free_irq(uart->status_irq, uart);
+#endif
 }
 
 static void
@@ -697,8 +837,16 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios,
                        __func__);
        }
 
-       if (termios->c_cflag & CSTOPB)
-               lcr |= STB;
+       /* Anomaly notes:
+        *  05000231 -  STOP bit is always set to 1 whatever the user is set.
+        */
+       if (termios->c_cflag & CSTOPB) {
+               if (ANOMALY_05000231)
+                       printk(KERN_WARNING "STOP bits other than 1 is not "
+                               "supported in case of anomaly 05000231.\n");
+               else
+                       lcr |= STB;
+       }
        if (termios->c_cflag & PARENB)
                lcr |= PEN;
        if (!(termios->c_cflag & PARODD))
@@ -729,7 +877,7 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios,
        }
 
        baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
-       quot = uart_get_divisor(port, baud);
+       quot = uart_get_divisor(port, baud) - ANOMALY_05000230;
        spin_lock_irqsave(&uart->port.lock, flags);
 
        UART_SET_ANOMALY_THRESHOLD(uart, USEC_PER_SEC / baud * 15);
@@ -757,6 +905,9 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios,
        val |= UCEN;
        UART_PUT_GCTL(uart, val);
 
+       /* Port speed changed, update the per-port timeout. */
+       uart_update_timeout(port, termios->c_cflag, baud);
+
        spin_unlock_irqrestore(&uart->port.lock, flags);
 }
 
@@ -814,10 +965,10 @@ static void bfin_serial_set_ldisc(struct uart_port *port)
        int line = port->line;
        unsigned short val;
 
-       if (line >= port->info->tty->driver->num)
+       if (line >= port->state->port.tty->driver->num)
                return;
 
-       switch (port->info->tty->ldisc.num) {
+       switch (port->state->port.tty->termios->c_line) {
        case N_IRDA:
                val = UART_GET_GCTL(&bfin_serial_ports[line]);
                val |= (IREN | RPOLC);
@@ -830,6 +981,69 @@ static void bfin_serial_set_ldisc(struct uart_port *port)
        }
 }
 
+static void bfin_serial_reset_irda(struct uart_port *port)
+{
+       int line = port->line;
+       unsigned short val;
+
+       val = UART_GET_GCTL(&bfin_serial_ports[line]);
+       val &= ~(IREN | RPOLC);
+       UART_PUT_GCTL(&bfin_serial_ports[line], val);
+       SSYNC();
+       val |= (IREN | RPOLC);
+       UART_PUT_GCTL(&bfin_serial_ports[line], val);
+       SSYNC();
+}
+
+#ifdef CONFIG_CONSOLE_POLL
+/* Anomaly notes:
+ *  05000099 -  Because we only use THRE in poll_put and DR in poll_get,
+ *             losing other bits of UART_LSR is not a problem here.
+ */
+static void bfin_serial_poll_put_char(struct uart_port *port, unsigned char chr)
+{
+       struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+
+       while (!(UART_GET_LSR(uart) & THRE))
+               cpu_relax();
+
+       UART_CLEAR_DLAB(uart);
+       UART_PUT_CHAR(uart, (unsigned char)chr);
+}
+
+static int bfin_serial_poll_get_char(struct uart_port *port)
+{
+       struct bfin_serial_port *uart = (struct bfin_serial_port *)port;
+       unsigned char chr;
+
+       while (!(UART_GET_LSR(uart) & DR))
+               cpu_relax();
+
+       UART_CLEAR_DLAB(uart);
+       chr = UART_GET_CHAR(uart);
+
+       return chr;
+}
+#endif
+
+#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
+       defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
+static void bfin_kgdboc_port_shutdown(struct uart_port *port)
+{
+       if (kgdboc_break_enabled) {
+               kgdboc_break_enabled = 0;
+               bfin_serial_shutdown(port);
+       }
+}
+
+static int bfin_kgdboc_port_startup(struct uart_port *port)
+{
+       kgdboc_port_line = port->line;
+       kgdboc_break_enabled = !bfin_serial_startup(port);
+       return 0;
+}
+#endif
+
 static struct uart_ops bfin_serial_pops = {
        .tx_empty       = bfin_serial_tx_empty,
        .set_mctrl      = bfin_serial_set_mctrl,
@@ -848,8 +1062,50 @@ static struct uart_ops bfin_serial_pops = {
        .request_port   = bfin_serial_request_port,
        .config_port    = bfin_serial_config_port,
        .verify_port    = bfin_serial_verify_port,
+#if defined(CONFIG_KGDB_SERIAL_CONSOLE) || \
+       defined(CONFIG_KGDB_SERIAL_CONSOLE_MODULE)
+       .kgdboc_port_startup    = bfin_kgdboc_port_startup,
+       .kgdboc_port_shutdown   = bfin_kgdboc_port_shutdown,
+#endif
+#ifdef CONFIG_CONSOLE_POLL
+       .poll_put_char  = bfin_serial_poll_put_char,
+       .poll_get_char  = bfin_serial_poll_get_char,
+#endif
 };
 
+static void __init bfin_serial_hw_init(void)
+{
+#ifdef CONFIG_SERIAL_BFIN_UART0
+       peripheral_request(P_UART0_TX, DRIVER_NAME);
+       peripheral_request(P_UART0_RX, DRIVER_NAME);
+#endif
+
+#ifdef CONFIG_SERIAL_BFIN_UART1
+       peripheral_request(P_UART1_TX, DRIVER_NAME);
+       peripheral_request(P_UART1_RX, DRIVER_NAME);
+
+# if defined(CONFIG_BFIN_UART1_CTSRTS) && defined(CONFIG_BF54x)
+       peripheral_request(P_UART1_RTS, DRIVER_NAME);
+       peripheral_request(P_UART1_CTS, DRIVER_NAME);
+# endif
+#endif
+
+#ifdef CONFIG_SERIAL_BFIN_UART2
+       peripheral_request(P_UART2_TX, DRIVER_NAME);
+       peripheral_request(P_UART2_RX, DRIVER_NAME);
+#endif
+
+#ifdef CONFIG_SERIAL_BFIN_UART3
+       peripheral_request(P_UART3_TX, DRIVER_NAME);
+       peripheral_request(P_UART3_RX, DRIVER_NAME);
+
+# if defined(CONFIG_BFIN_UART3_CTSRTS) && defined(CONFIG_BF54x)
+       peripheral_request(P_UART3_RTS, DRIVER_NAME);
+       peripheral_request(P_UART3_CTS, DRIVER_NAME);
+# endif
+#endif
+}
+
 static void __init bfin_serial_init_ports(void)
 {
        static int first = 1;
@@ -859,8 +1115,12 @@ static void __init bfin_serial_init_ports(void)
                return;
        first = 0;
 
-       for (i = 0; i < nr_ports; i++) {
+       bfin_serial_hw_init();
+
+       for (i = 0; i < nr_active_ports; i++) {
+               spin_lock_init(&bfin_serial_ports[i].port.lock);
                bfin_serial_ports[i].port.uartclk   = get_sclk();
+               bfin_serial_ports[i].port.fifosize  = BFIN_UART_TX_FIFO_SIZE;
                bfin_serial_ports[i].port.ops       = &bfin_serial_pops;
                bfin_serial_ports[i].port.line      = i;
                bfin_serial_ports[i].port.iotype    = UPIO_MEM;
@@ -870,6 +1130,8 @@ static void __init bfin_serial_init_ports(void)
                        bfin_serial_resource[i].uart_base_addr;
                bfin_serial_ports[i].port.irq       =
                        bfin_serial_resource[i].uart_irq;
+               bfin_serial_ports[i].status_irq     =
+                       bfin_serial_resource[i].uart_status_irq;
                bfin_serial_ports[i].port.flags     = UPF_BOOT_AUTOCONF;
 #ifdef CONFIG_SERIAL_BFIN_DMA
                bfin_serial_ports[i].tx_done        = 1;
@@ -880,19 +1142,17 @@ static void __init bfin_serial_init_ports(void)
                        bfin_serial_resource[i].uart_rx_dma_channel;
                init_timer(&(bfin_serial_ports[i].rx_dma_timer));
 #endif
-#ifdef CONFIG_SERIAL_BFIN_CTSRTS
-               init_timer(&(bfin_serial_ports[i].cts_timer));
+#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \
+       defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS)
                bfin_serial_ports[i].cts_pin        =
                        bfin_serial_resource[i].uart_cts_pin;
                bfin_serial_ports[i].rts_pin        =
                        bfin_serial_resource[i].uart_rts_pin;
 #endif
-               bfin_serial_hw_init(&bfin_serial_ports[i]);
        }
-
 }
 
-#ifdef CONFIG_SERIAL_BFIN_CONSOLE
+#if defined(CONFIG_SERIAL_BFIN_CONSOLE) || defined(CONFIG_EARLY_PRINTK)
 /*
  * If the port was already initialised (eg, by a boot loader),
  * try to determine the current setup.
@@ -936,24 +1196,21 @@ bfin_serial_console_get_options(struct bfin_serial_port *uart, int *baud,
        }
        pr_debug("%s:baud = %d, parity = %c, bits= %d\n", __func__, *baud, *parity, *bits);
 }
-#endif
 
-#if defined(CONFIG_SERIAL_BFIN_CONSOLE) || defined(CONFIG_EARLY_PRINTK)
 static struct uart_driver bfin_serial_reg;
 
 static int __init
 bfin_serial_console_setup(struct console *co, char *options)
 {
        struct bfin_serial_port *uart;
-# ifdef CONFIG_SERIAL_BFIN_CONSOLE
        int baud = 57600;
        int bits = 8;
        int parity = 'n';
-#  ifdef CONFIG_SERIAL_BFIN_CTSRTS
+# if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \
+       defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS)
        int flow = 'r';
-#  else
+# else
        int flow = 'n';
-#  endif
 # endif
 
        /*
@@ -961,20 +1218,16 @@ bfin_serial_console_setup(struct console *co, char *options)
         * if so, search for the first available port that does have
         * console support.
         */
-       if (co->index == -1 || co->index >= nr_ports)
+       if (co->index == -1 || co->index >= nr_active_ports)
                co->index = 0;
        uart = &bfin_serial_ports[co->index];
 
-# ifdef CONFIG_SERIAL_BFIN_CONSOLE
        if (options)
                uart_parse_options(options, &baud, &parity, &bits, &flow);
        else
                bfin_serial_console_get_options(uart, &baud, &parity, &bits);
 
        return uart_set_options(&uart->port, co, baud, parity, bits, flow);
-# else
-       return 0;
-# endif
 }
 #endif /* defined (CONFIG_SERIAL_BFIN_CONSOLE) ||
                                 defined (CONFIG_EARLY_PRINTK) */
@@ -996,7 +1249,7 @@ static void
 bfin_serial_console_write(struct console *co, const char *s, unsigned int count)
 {
        struct bfin_serial_port *uart = &bfin_serial_ports[co->index];
-       int flags = 0;
+       unsigned long flags;
 
        spin_lock_irqsave(&uart->port.lock, flags);
        uart_console_write(&uart->port, s, count, bfin_serial_console_putchar);
@@ -1018,10 +1271,7 @@ static int __init bfin_serial_rs_console_init(void)
 {
        bfin_serial_init_ports();
        register_console(&bfin_serial_console);
-#ifdef CONFIG_KGDB_UART
-       kgdb_entry_state = 0;
-       init_kgdb_uart();
-#endif
+
        return 0;
 }
 console_initcall(bfin_serial_rs_console_init);
@@ -1056,12 +1306,17 @@ static __init void early_serial_write(struct console *con, const char *s,
        }
 }
 
-static struct __init console bfin_early_serial_console = {
+/*
+ * This should have a .setup or .early_setup in it, but then things get called
+ * without the command line options, and the baud rate gets messed up - so
+ * don't let the common infrastructure play with things. (see calls to setup
+ * & earlysetup in ./kernel/printk.c:register_console()
+ */
+static struct __initdata console bfin_early_serial_console = {
        .name = "early_BFuart",
        .write = early_serial_write,
        .device = uart_console_device,
        .flags = CON_PRINTBUFFER,
-       .setup = bfin_serial_console_setup,
        .index = -1,
        .data  = &bfin_serial_reg,
 };
@@ -1072,7 +1327,7 @@ struct console __init *bfin_earlyserial_init(unsigned int port,
        struct bfin_serial_port *uart;
        struct ktermios t;
 
-       if (port == -1 || port >= nr_ports)
+       if (port == -1 || port >= nr_active_ports)
                port = 0;
        bfin_serial_init_ports();
        bfin_early_serial_console.index = port;
@@ -1086,7 +1341,7 @@ struct console __init *bfin_earlyserial_init(unsigned int port,
        return &bfin_early_serial_console;
 }
 
-#endif /* CONFIG_SERIAL_BFIN_CONSOLE */
+#endif /* CONFIG_EARLY_PRINTK */
 
 static struct uart_driver bfin_serial_reg = {
        .owner                  = THIS_MODULE,
@@ -1100,20 +1355,26 @@ static struct uart_driver bfin_serial_reg = {
 
 static int bfin_serial_suspend(struct platform_device *dev, pm_message_t state)
 {
-       struct bfin_serial_port *uart = platform_get_drvdata(dev);
+       int i;
 
-       if (uart)
-               uart_suspend_port(&bfin_serial_reg, &uart->port);
+       for (i = 0; i < nr_active_ports; i++) {
+               if (bfin_serial_ports[i].port.dev != &dev->dev)
+                       continue;
+               uart_suspend_port(&bfin_serial_reg, &bfin_serial_ports[i].port);
+       }
 
        return 0;
 }
 
 static int bfin_serial_resume(struct platform_device *dev)
 {
-       struct bfin_serial_port *uart = platform_get_drvdata(dev);
+       int i;
 
-       if (uart)
-               uart_resume_port(&bfin_serial_reg, &uart->port);
+       for (i = 0; i < nr_active_ports; i++) {
+               if (bfin_serial_ports[i].port.dev != &dev->dev)
+                       continue;
+               uart_resume_port(&bfin_serial_reg, &bfin_serial_ports[i].port);
+       }
 
        return 0;
 }
@@ -1128,32 +1389,32 @@ static int bfin_serial_probe(struct platform_device *dev)
                        break;
 
        if (i < dev->num_resources) {
-               for (i = 0; i < nr_ports; i++, res++) {
+               for (i = 0; i < nr_active_ports; i++, res++) {
                        if (bfin_serial_ports[i].port.mapbase != res->start)
                                continue;
                        bfin_serial_ports[i].port.dev = &dev->dev;
                        uart_add_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port);
-                       platform_set_drvdata(dev, &bfin_serial_ports[i]);
                }
        }
 
        return 0;
 }
 
-static int bfin_serial_remove(struct platform_device *pdev)
+static int bfin_serial_remove(struct platform_device *dev)
 {
-       struct bfin_serial_port *uart = platform_get_drvdata(pdev);
-
+       int i;
 
-#ifdef CONFIG_SERIAL_BFIN_CTSRTS
-       gpio_free(uart->cts_pin);
-       gpio_free(uart->rts_pin);
+       for (i = 0; i < nr_active_ports; i++) {
+               if (bfin_serial_ports[i].port.dev != &dev->dev)
+                       continue;
+               uart_remove_one_port(&bfin_serial_reg, &bfin_serial_ports[i].port);
+               bfin_serial_ports[i].port.dev = NULL;
+#if defined(CONFIG_SERIAL_BFIN_CTSRTS) || \
+       defined(CONFIG_SERIAL_BFIN_HARD_CTSRTS)
+               gpio_free(bfin_serial_ports[i].cts_pin);
+               gpio_free(bfin_serial_ports[i].rts_pin);
 #endif
-
-       platform_set_drvdata(pdev, NULL);
-
-       if (uart)
-               uart_remove_one_port(&bfin_serial_reg, &uart->port);
+       }
 
        return 0;
 }
@@ -1172,10 +1433,6 @@ static struct platform_driver bfin_serial_driver = {
 static int __init bfin_serial_init(void)
 {
        int ret;
-#ifdef CONFIG_KGDB_UART
-       struct bfin_serial_port *uart = &bfin_serial_ports[CONFIG_KGDB_UART_PORT];
-       struct ktermios t;
-#endif
 
        pr_info("Serial: Blackfin serial driver\n");
 
@@ -1189,21 +1446,6 @@ static int __init bfin_serial_init(void)
                        uart_unregister_driver(&bfin_serial_reg);
                }
        }
-#ifdef CONFIG_KGDB_UART
-       if (uart->port.cons->index != CONFIG_KGDB_UART_PORT) {
-               request_irq(uart->port.irq, bfin_serial_rx_int,
-                       IRQF_DISABLED, "BFIN_UART_RX", uart);
-               pr_info("Request irq for kgdb uart port\n");
-               UART_SET_IER(uart, ERBFI);
-               SSYNC();
-               t.c_cflag = CS8|B57600;
-               t.c_iflag = 0;
-               t.c_oflag = 0;
-               t.c_lflag = ICANON;
-               t.c_line = CONFIG_KGDB_UART_PORT;
-               bfin_serial_set_termios(&uart->port, &t, &t);
-       }
-#endif
        return ret;
 }
 
@@ -1213,6 +1455,7 @@ static void __exit bfin_serial_exit(void)
        uart_unregister_driver(&bfin_serial_reg);
 }
 
+
 module_init(bfin_serial_init);
 module_exit(bfin_serial_exit);