diff options
author | Mike Frysinger <michael.frysinger@analog.com> | 2007-12-24 19:48:04 +0800 |
---|---|---|
committer | Bryan Wu <cooloney@kernel.org> | 2007-12-24 19:48:04 +0800 |
commit | 8851c71eb97610f0f63121d62345c969f71201a2 (patch) | |
tree | 1c03ba69333fca895f061f3acfbe2618652c2890 | |
parent | 0bcfd70ea11a5d6f2362be463513a60245a62baf (diff) |
[Blackfin] serial driver: rework break flood anomaly handling to be more robust/realistic about what we can actually work around
Signed-off-by: Mike Frysinger <michael.frysinger@analog.com>
Signed-off-by: Bryan Wu <bryan.wu@analog.com>
-rw-r--r-- | drivers/serial/bfin_5xx.c | 66 | ||||
-rw-r--r-- | include/asm-blackfin/mach-bf533/bfin_serial_5xx.h | 3 | ||||
-rw-r--r-- | include/asm-blackfin/mach-bf561/bfin_serial_5xx.h | 3 |
3 files changed, 58 insertions, 14 deletions
diff --git a/drivers/serial/bfin_5xx.c b/drivers/serial/bfin_5xx.c index af84984df77..50aa3b2a19b 100644 --- a/drivers/serial/bfin_5xx.c +++ b/drivers/serial/bfin_5xx.c @@ -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)); diff --git a/include/asm-blackfin/mach-bf533/bfin_serial_5xx.h b/include/asm-blackfin/mach-bf533/bfin_serial_5xx.h index b619065ceeb..a1b4f4eebd0 100644 --- a/include/asm-blackfin/mach-bf533/bfin_serial_5xx.h +++ b/include/asm-blackfin/mach-bf533/bfin_serial_5xx.h @@ -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; diff --git a/include/asm-blackfin/mach-bf561/bfin_serial_5xx.h b/include/asm-blackfin/mach-bf561/bfin_serial_5xx.h index b619065ceeb..a1b4f4eebd0 100644 --- a/include/asm-blackfin/mach-bf561/bfin_serial_5xx.h +++ b/include/asm-blackfin/mach-bf561/bfin_serial_5xx.h @@ -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; |