a22:UART:fix uart receive fifo error bug
authorhhb <hhb@rock-chips.com>
Fri, 5 Aug 2011 09:23:46 +0000 (17:23 +0800)
committerroot <root@lw.(none)>
Tue, 9 Aug 2011 06:11:00 +0000 (14:11 +0800)
drivers/serial/rk_serial.c

index 93847bf5ae0fea19fd631296a0e18e648fd17bce..a1e1c5af6082ff14486c9c94b7229d5cdf71d4e2 100644 (file)
@@ -56,6 +56,9 @@
 #define UART_USR       0x1F    /* UART Status Register */\r
 #define UART_USR_BUSY (1)\r
 #define UART_IER_PTIME 0x80    /* Programmable THRE Interrupt Mode Enable */\r
+#define UART_LSR_RFE   0x80    /* receive fifo error */\r
+\r
+\r
 #define RX_TIMEOUT             (3000*10)  //uint ms\r
 \r
 #define BOTH_EMPTY     (UART_LSR_TEMT | UART_LSR_THRE)\r
@@ -103,9 +106,9 @@ static struct uart_driver serial_rk_reg;
 #define uart_console(port)     (0)\r
 #endif\r
 \r
-#ifdef DEBUG\r
-extern void printascii(const char *);\r
+#define DEBUG 0\r
 \r
+extern void printascii(const char *);\r
 static void dbg(const char *fmt, ...)\r
 {\r
        va_list va;\r
@@ -118,6 +121,7 @@ static void dbg(const char *fmt, ...)
        printascii(buff);\r
 }\r
 \r
+#if DEBUG\r
 #define DEBUG_INTR(fmt...)     if (!uart_console(&up->port)) dbg(fmt)\r
 #else\r
 #define DEBUG_INTR(fmt...)     do { } while (0)\r
@@ -232,26 +236,28 @@ static inline void serial_dl_write(struct uart_rk_port *up, unsigned int value)
        serial_out(up, UART_DLM, value >> 8 & 0xff);\r
 }\r
 \r
-static void serial_lcr_write(struct uart_rk_port *up, unsigned char value)\r
+static int serial_lcr_write(struct uart_rk_port *up, unsigned char value)\r
 {\r
        unsigned int tmout = 10000;\r
+       unsigned char lcr;\r
+\r
+       lcr = serial_in(up, UART_LCR);\r
+       if (lcr == value)\r
+               return -1;\r
+\r
+       while(serial_in(up, UART_USR) & UART_USR_BUSY){\r
 \r
-       for (;;) {\r
-               unsigned char lcr;\r
-               serial_out(up, UART_LCR, value);\r
-               lcr = serial_in(up, UART_LCR);\r
-               if (lcr == value)\r
-                       break;\r
-               /* Read the USR to clear any busy interrupts */\r
-               serial_in(up, UART_USR);\r
-               serial_in(up, UART_RX);\r
                if (--tmout == 0){\r
-                       dev_info(up->port.dev, "set lcr timeout\n");\r
-                       break;\r
+                       dbg("set serial.%d lcr timeout\n", up->port.line);\r
+                       return -1;\r
                }\r
-                       \r
                udelay(1);\r
        }\r
+\r
+       if (tmout != 0){\r
+               serial_out(up, UART_LCR, value);\r
+       }\r
+       return 0;\r
 }\r
 \r
 static inline void serial_rk_enable_ier_thri(struct uart_rk_port *up)\r
@@ -271,6 +277,26 @@ static inline void serial_rk_disable_ier_thri(struct uart_rk_port *up)
        }\r
 }\r
 \r
+static int rk29_uart_dump_register(struct uart_rk_port *up){\r
+\r
+       unsigned int reg_value = 0;\r
+\r
+       reg_value = serial_in(up, UART_IER);\r
+       dbg("UART_IER = 0x%0x\n", reg_value);\r
+       reg_value = serial_in(up, UART_IIR);\r
+       dbg("UART_IIR = 0x%0x\n", reg_value);\r
+//     reg_value = serial_in(up, 0X25);\r
+//     dbg("DMA_MODE = 0x%0x\n", reg_value);\r
+    reg_value = serial_in(up, UART_LSR);\r
+    dbg("UART_LSR = 0x%0x\n", reg_value);\r
+    reg_value = serial_in(up, 0x21);\r
+    dbg("UART_RFL = 0x%0x\n", reg_value);\r
+       return 0;\r
+\r
+}\r
+\r
+\r
+\r
 /*\r
  * FIFO support.\r
  */\r
@@ -729,8 +755,9 @@ receive_chars(struct uart_rk_port *up, unsigned int *status)
        char flag;\r
 \r
        do {\r
-               if (likely(lsr & UART_LSR_DR))\r
+               if (likely(lsr & UART_LSR_DR)){\r
                        ch = serial_in(up, UART_RX);\r
+               }\r
                else\r
                        /*\r
                         * Intel 82571 has a Serial Over Lan device that will\r
@@ -870,14 +897,14 @@ static void serial_rk_handle_port(struct uart_rk_port *up)
        unsigned long flags;\r
        spin_lock_irqsave(&up->port.lock, flags);\r
 \r
+       /* reading UART_LSR can automatically clears PE FE OE bits, except receive fifo error bit*/\r
        status = serial_in(up, UART_LSR);\r
-\r
+       \r
        DEBUG_INTR("status = %x...", status);\r
-\r
+       /* DMA mode enable */\r
        if(up->prk29_uart_dma_t->use_dma == 1) {\r
 \r
                if(up->iir & UART_IIR_RLSI){\r
-\r
                        if (status & (UART_LSR_DR | UART_LSR_BI)) {\r
                                up->port_activity = jiffies;\r
                                up->ier &= ~UART_IER_RLSI;\r
@@ -885,21 +912,28 @@ static void serial_rk_handle_port(struct uart_rk_port *up)
                                serial_out(up, UART_IER, up->ier);\r
                                //receive_chars(up, &status);\r
                                //mod_timer(&up->prk29_uart_dma_t->rx_timer, jiffies +\r
-                                                               //msecs_to_jiffies(up->prk29_uart_dma_t->rx_timeout));\r
+                               //msecs_to_jiffies(up->prk29_uart_dma_t->rx_timeout));\r
                                if(serial_rk_start_rx_dma(&up->port) == -1){\r
                                        receive_chars(up, &status);\r
                                }\r
                        }\r
                }\r
 \r
-/*\r
-               if (status & UART_LSR_THRE) {\r
-                       transmit_chars(up);\r
-               }\r
-*/\r
-\r
        }else {   //dma mode disable\r
 \r
+               /*\r
+                * when uart receive a serial of data which doesn't have stop bit and so on, that causes frame error,and\r
+                * set UART_LSR_RFE to one,what is worse,we couldn't read the data in the receive fifo. So if\r
+                * wo don't clear this bit and reset the receive fifo, the received data available interrupt would\r
+                * occur continuously.  added by hhb@rock-chips.com 2011-08-05\r
+                */\r
+               if(status & UART_LSR_RFE){\r
+                       dbg("serial.%d LSR = 0x%02x...\n", up->port.line, status);\r
+                       serial_out(up, UART_FCR, serial_in(up, UART_FCR) | UART_FCR_CLEAR_RCVR);\r
+                       //dsb();   //dsb is an instruction that make sure write the UART_FCR in time\r
+                       serial_in(up, UART_RX);\r
+                       rk29_uart_dump_register(up);\r
+               }\r
                if (status & (UART_LSR_DR | UART_LSR_BI)) {\r
                        receive_chars(up, &status);\r
                }\r
@@ -924,12 +958,14 @@ static irqreturn_t serial_rk_interrupt(int irq, void *dev_id)
 \r
        iir = serial_in(up, UART_IIR);\r
        DEBUG_INTR("%s(%d) iir = 0x%02x ", __func__, irq, iir);\r
+\r
        up->iir = iir;\r
 \r
        if (!(iir & UART_IIR_NO_INT)) {\r
                serial_rk_handle_port(up);\r
                handled = 1;\r
        } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {\r
+\r
                /* The DesignWare APB UART has an Busy Detect (0x07)\r
                 * interrupt meaning an LCR write attempt occured while the\r
                 * UART was busy. The interrupt must be cleared by reading\r
@@ -938,9 +974,8 @@ static irqreturn_t serial_rk_interrupt(int irq, void *dev_id)
                if(!(serial_in(up, UART_USR) & UART_USR_BUSY)){\r
                        serial_out(up, UART_LCR, up->lcr);\r
                }\r
-               \r
                handled = 1;\r
-               dev_info(up->port.dev, "the serial is busy\n");\r
+               dbg("the serial.%d is busy\n", up->port.line);\r
        }\r
        DEBUG_INTR("end(%d).\n", handled);\r
 \r
@@ -1132,6 +1167,7 @@ static int serial_rk_startup(struct uart_port *port)
        (void) serial_in(up, UART_RX);\r
        (void) serial_in(up, UART_IIR);\r
        (void) serial_in(up, UART_MSR);\r
+       (void) serial_in(up, UART_USR);\r
 \r
        /*\r
         * Now, initialize the UART\r
@@ -1154,10 +1190,11 @@ static int serial_rk_startup(struct uart_port *port)
         * saved flags to avoid getting false values from polling\r
         * routines or the previous session.\r
         */\r
-       serial_in(up, UART_LSR);\r
-       serial_in(up, UART_RX);\r
-       serial_in(up, UART_IIR);\r
-       serial_in(up, UART_MSR);\r
+       (void) serial_in(up, UART_LSR);\r
+       (void) serial_in(up, UART_RX);\r
+       (void) serial_in(up, UART_IIR);\r
+       (void) serial_in(up, UART_MSR);\r
+       (void) serial_in(up, UART_USR);\r
        up->lsr_saved_flags = 0;\r
 #if 0\r
        up->msr_saved_flags = 0;\r
@@ -1181,8 +1218,7 @@ static int serial_rk_startup(struct uart_port *port)
                up->port_activity = jiffies;\r
 \r
        }else{\r
-               up->ier |= UART_IER_RDI;\r
-               up->ier |= UART_IER_RLSI;\r
+               up->ier = 0;\r
                serial_out(up, UART_IER, up->ier);\r
        }\r
 \r
@@ -1320,7 +1356,6 @@ serial_rk_set_termios(struct uart_port *port, struct ktermios *termios,
                up->mcr |= UART_MCR_AFE;\r
        }\r
 \r
-\r
        /*\r
         * Ok, we're now changing the port state.  Do it with\r
         * interrupts disabled.\r
@@ -1369,8 +1404,6 @@ serial_rk_set_termios(struct uart_port *port, struct ktermios *termios,
                up->ier |= UART_IER_MSI;\r
 #endif\r
 \r
-       serial_out(up, UART_IER, up->ier);\r
-\r
        serial_lcr_write(up, cval | UART_LCR_DLAB);/* set DLAB */\r
 \r
        serial_dl_write(up, quot);\r
@@ -1381,7 +1414,12 @@ serial_rk_set_termios(struct uart_port *port, struct ktermios *termios,
        serial_out(up, UART_FCR, fcr);          /* set fcr */\r
 \r
        serial_rk_set_mctrl(&up->port, up->port.mctrl);\r
-\r
+       \r
+       /* enable the uart interrupt last */\r
+       up->ier |= UART_IER_RDI;\r
+       up->ier |= UART_IER_RLSI;\r
+       serial_out(up, UART_IER, up->ier);\r
+       \r
        spin_unlock_irqrestore(&up->port.lock, flags);\r
        /* Don't rewrite B0 */\r
        if (tty_termios_baud_rate(termios))\r
@@ -1658,7 +1696,7 @@ static int __devinit serial_rk_probe(struct platform_device *pdev)
                goto do_put_clk;\r
        }\r
        up->port.mapbase = mem->start;\r
-       up->port.irqflags = 0;\r
+       up->port.irqflags = IRQF_DISABLED;\r
        up->port.uartclk = clk_get_rate(up->clk);\r
 \r
        /* set dma config */\r
@@ -1708,7 +1746,6 @@ static int __devinit serial_rk_probe(struct platform_device *pdev)
                up->ier |= THRE_MODE;                   // enable THRE interrupt mode\r
                serial_out(up, UART_IER, up->ier);\r
        }\r
-       clk_enable(up->clk);  // enable the config uart clock\r
 \r
        serial_rk_add_console_port(up);\r
        ret = uart_add_one_port(&serial_rk_reg, &up->port);\r