From f177f4250c729727b1629fa8d8d6556c999e9b8c Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Wed, 9 Apr 2008 02:02:07 -0400 Subject: Blackfin: fix up UART status bit handling Some Blackfin UARTs are read-to-clear while others are write-to-clear. This can cause problems when we poll the LSR and then later try and handle any errors detected. Signed-off-by: Mike Frysinger --- cpu/blackfin/serial.c | 72 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 60 insertions(+), 12 deletions(-) diff --git a/cpu/blackfin/serial.c b/cpu/blackfin/serial.c index 406d9d023..0d6f377c0 100644 --- a/cpu/blackfin/serial.c +++ b/cpu/blackfin/serial.c @@ -35,6 +35,32 @@ #include "serial.h" +#ifdef CONFIG_DEBUG_SERIAL +uint16_t cached_lsr[256]; +uint16_t cached_rbr[256]; +size_t cache_count; + +/* The LSR is read-to-clear on some parts, so we have to make sure status + * bits aren't inadvertently lost when doing various tests. + */ +static uint16_t uart_lsr_save; +static uint16_t uart_lsr_read(void) +{ + uint16_t lsr = *pUART_LSR; + uart_lsr_save |= (lsr & (OE|PE|FE|BI)); + return lsr | uart_lsr_save; +} +/* Just do the clear for everyone since it can't hurt. */ +static void uart_lsr_clear(void) +{ + uart_lsr_save = 0; + *pUART_LSR |= -1; +} +#else +static inline uint16_t uart_lsr_read(void) { return *pUART_LSR; } +static inline void uart_lsr_clear(void) { *pUART_LSR = -1; } +#endif + /* Symbol for our assembly to call. */ void serial_set_baud(uint32_t baud) { @@ -61,6 +87,12 @@ int serial_init(void) { serial_initialize(); serial_setbrg(); + uart_lsr_clear(); +#ifdef CONFIG_DEBUG_SERIAL + cache_count = 0; + memset(cached_lsr, 0x00, sizeof(cached_lsr)); + memset(cached_rbr, 0x00, sizeof(cached_rbr)); +#endif return 0; } @@ -73,7 +105,7 @@ void serial_putc(const char c) WATCHDOG_RESET(); /* wait for the hardware fifo to clear up */ - while (!(*pUART_LSR & THRE)) + while (!(uart_lsr_read() & THRE)) continue; /* queue the character for transmission */ @@ -83,38 +115,54 @@ void serial_putc(const char c) WATCHDOG_RESET(); /* wait for the byte to be shifted over the line */ - while (!(*pUART_LSR & TEMT)) + while (!(uart_lsr_read() & TEMT)) continue; } int serial_tstc(void) { WATCHDOG_RESET(); - return (*pUART_LSR & DR) ? 1 : 0; + return (uart_lsr_read() & DR) ? 1 : 0; } int serial_getc(void) { - uint16_t uart_lsr_val, uart_rbr_val; + uint16_t uart_rbr_val; /* wait for data ! */ while (!serial_tstc()) continue; - /* clear the status and grab the new byte */ - uart_lsr_val = *pUART_LSR; + /* grab the new byte */ uart_rbr_val = *pUART_RBR; +#ifdef CONFIG_DEBUG_SERIAL + /* grab & clear the LSR */ + uint16_t uart_lsr_val = uart_lsr_read(); + + cached_lsr[cache_count] = uart_lsr_val; + cached_rbr[cache_count] = uart_rbr_val; + cache_count = (cache_count + 1) % ARRAY_SIZE(cached_lsr); + if (uart_lsr_val & (OE|PE|FE|BI)) { - /* Some parts are read-to-clear while others are - * write-to-clear. Just do the write for everyone - * since it cant hurt (other than code size). - */ - *pUART_LSR = (OE|PE|FE|BI); + uint16_t dll, dlh; + printf("\n[SERIAL ERROR]\n"); + ACCESS_LATCH(); + dll = *pUART_DLL; + dlh = *pUART_DLH; + ACCESS_PORT_IER(); + printf("\tDLL=0x%x DLH=0x%x\n", dll, dlh); + do { + --cache_count; + printf("\t%3i: RBR=0x%02x LSR=0x%02x\n", cache_count, + cached_rbr[cache_count], cached_lsr[cache_count]); + } while (cache_count > 0); return -1; } +#endif + uart_lsr_clear(); - return uart_rbr_val & 0xFF; + return uart_rbr_val; } void serial_puts(const char *s) -- cgit v1.2.3