[Blackfin] serial driver: rework break flood anomaly handling to be more robust/reali...
authorMike Frysinger <michael.frysinger@analog.com>
Mon, 24 Dec 2007 11:48:04 +0000 (19:48 +0800)
committerBryan Wu <cooloney@kernel.org>
Mon, 24 Dec 2007 11:48:04 +0000 (19:48 +0800)
Signed-off-by: Mike Frysinger <michael.frysinger@analog.com>
Signed-off-by: Bryan Wu <bryan.wu@analog.com>
drivers/serial/bfin_5xx.c
include/asm-blackfin/mach-bf533/bfin_serial_5xx.h
include/asm-blackfin/mach-bf561/bfin_serial_5xx.h

index af84984df775eb15bfd2c20171f75bac2ddf0ad0..50aa3b2a19b827fb5460605097c7f346758e78af 100644 (file)
@@ -206,12 +206,20 @@ int kgdb_get_debug_char(void)
 }
 #endif
 
+#if ANOMALY_05000230 && defined(CONFIG_SERIAL_BFIN_PIO)
+# define UART_GET_ANOMALY_THRESHOLD(uart)    ((uart)->anomaly_threshold)
+# define UART_SET_ANOMALY_THRESHOLD(uart, v) ((uart)->anomaly_threshold = (v))
+#else
+# define UART_GET_ANOMALY_THRESHOLD(uart)    0
+# define UART_SET_ANOMALY_THRESHOLD(uart, v)
+#endif
+
 #ifdef CONFIG_SERIAL_BFIN_PIO
 static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
 {
        struct tty_struct *tty = uart->port.info->tty;
        unsigned int status, ch, flg;
-       static int in_break = 0;
+       static struct timeval anomaly_start = { .tv_sec = 0 };
 #ifdef CONFIG_KGDB_UART
        struct pt_regs *regs = get_irq_regs();
 #endif
@@ -244,28 +252,56 @@ static void bfin_serial_rx_chars(struct bfin_serial_port *uart)
 #endif
 
        if (ANOMALY_05000230) {
-               /* The BF533 family of processors have a nice misbehavior where
-                * they continuously generate characters for a "single" break.
+               /* The BF533 (and BF561) family of processors have a nice anomaly
+                * where they continuously generate characters for a "single" break.
                 * We have to basically ignore this flood until the "next" valid
-                * character comes across.  All other Blackfin families operate
-                * properly though.
+                * character comes across.  Due to the nature of the flood, it is
+                * not possible to reliably catch bytes that are sent too quickly
+                * after this break.  So application code talking to the Blackfin
+                * which sends a break signal must allow at least 1.5 character
+                * times after the end of the break for things to stabilize.  This
+                * timeout was picked as it must absolutely be larger than 1
+                * character time +/- some percent.  So 1.5 sounds good.  All other
+                * Blackfin families operate properly.  Woo.
                 * Note: While Anomaly 05000230 does not directly address this,
                 *       the changes that went in for it also fixed this issue.
+                *       That anomaly was fixed in 0.5+ silicon.  I like bunnies.
                 */
-               if (in_break) {
-                       if (ch != 0) {
-                               in_break = 0;
-                               ch = UART_GET_CHAR(uart);
-                               if (bfin_revid() < 5)
-                                       return;
-                       } else
-                               return;
+               if (anomaly_start.tv_sec) {
+                       struct timeval curr;
+                       suseconds_t usecs;
+
+                       if ((~ch & (~ch + 1)) & 0xff)
+                               goto known_good_char;
+
+                       do_gettimeofday(&curr);
+                       if (curr.tv_sec - anomaly_start.tv_sec > 1)
+                               goto known_good_char;
+
+                       usecs = 0;
+                       if (curr.tv_sec != anomaly_start.tv_sec)
+                               usecs += USEC_PER_SEC;
+                       usecs += curr.tv_usec - anomaly_start.tv_usec;
+
+                       if (usecs > UART_GET_ANOMALY_THRESHOLD(uart))
+                               goto known_good_char;
+
+                       if (ch)
+                               anomaly_start.tv_sec = 0;
+                       else
+                               anomaly_start = curr;
+
+                       return;
+
+ known_good_char:
+                       anomaly_start.tv_sec = 0;
                }
        }
 
        if (status & BI) {
                if (ANOMALY_05000230)
-                       in_break = 1;
+                       if (bfin_revid() < 5)
+                               do_gettimeofday(&anomaly_start);
                uart->port.icount.brk++;
                if (uart_handle_break(&uart->port))
                        goto ignore_char;
@@ -778,6 +814,8 @@ bfin_serial_set_termios(struct uart_port *port, struct ktermios *termios,
        quot = uart_get_divisor(port, baud);
        spin_lock_irqsave(&uart->port.lock, flags);
 
+       UART_SET_ANOMALY_THRESHOLD(uart, USEC_PER_SEC / baud * 15);
+
        do {
                lsr = UART_GET_LSR(uart);
        } while (!(lsr & TEMT));
index b619065ceeb0291cc05f65aad038ed4a9d82d732..a1b4f4eebd069e28b426b1ae31da8adf11ffbcf3 100644 (file)
@@ -57,6 +57,9 @@ struct bfin_serial_port {
        struct work_struct      tx_dma_workqueue;
 #else
        struct work_struct      cts_workqueue;
+# if ANOMALY_05000230
+       unsigned int anomaly_threshold;
+# endif
 #endif
 #ifdef CONFIG_SERIAL_BFIN_CTSRTS
        int                     cts_pin;
index b619065ceeb0291cc05f65aad038ed4a9d82d732..a1b4f4eebd069e28b426b1ae31da8adf11ffbcf3 100644 (file)
@@ -57,6 +57,9 @@ struct bfin_serial_port {
        struct work_struct      tx_dma_workqueue;
 #else
        struct work_struct      cts_workqueue;
+# if ANOMALY_05000230
+       unsigned int anomaly_threshold;
+# endif
 #endif
 #ifdef CONFIG_SERIAL_BFIN_CTSRTS
        int                     cts_pin;