uart:optimize uart driver about serial_rk_settermios
authorhhb <hhb@rock-chips.com>
Sat, 13 Aug 2011 01:38:55 +0000 (09:38 +0800)
committerhhb <hhb@rock-chips.com>
Sat, 13 Aug 2011 01:38:55 +0000 (09:38 +0800)
drivers/serial/rk_serial.c

index cc31fb048f93dd78e8cb34df929572803e5c2a11..c12e9db9bb4d2c5c9aa51ab3a6f3c2809a70f0e9 100644 (file)
 #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
+#define UART_SRR               0x22    /* software reset register */\r
+#define UART_RESET             0x01\r
 #define RX_TIMEOUT             (3000*10)  //uint ms\r
 \r
 #define BOTH_EMPTY     (UART_LSR_TEMT | UART_LSR_THRE)\r
 \r
 #define UART_NR        4   //uart port number\r
+#define POWER_MANEGEMENT 1\r
 \r
 /* configurate whether the port transmit-receive by DMA */\r
 #define OPEN_DMA      1\r
@@ -96,16 +100,17 @@ static struct uart_driver serial_rk_reg;
 /*\r
  * Debugging.\r
  */\r
-#define DBG_PORT 1\r
+#define DBG_PORT 1   //DBG_PORT which uart is used to print log message\r
+\r
 #ifdef CONFIG_SERIAL_CORE_CONSOLE\r
 #define uart_console(port)     ((port)->cons && (port)->cons->index == (port)->line)\r
 #else\r
 #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 +123,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
@@ -162,7 +168,7 @@ struct uart_rk_port {
        unsigned char           lcr;\r
        unsigned char           mcr;\r
        unsigned char           iir;\r
-\r
+       unsigned char           fcr;\r
        /*\r
         * Some bits in registers are cleared on a read, so they must\r
         * be saved whenever the register is read but the bits will not\r
@@ -226,32 +232,53 @@ static inline int serial_dl_read(struct uart_rk_port *up)
 }\r
 \r
 /* Uart divisor latch write */\r
-static inline void serial_dl_write(struct uart_rk_port *up, unsigned int value)\r
+static int serial_dl_write(struct uart_rk_port *up, unsigned int value)\r
 {\r
+       unsigned int tmout = 100;\r
+       if(up->port.line != DBG_PORT)\r
+       {\r
+               while(!(serial_in(up, UART_LCR) & UART_LCR_DLAB)){\r
+                       if (--tmout == 0){\r
+                               dbg("set serial.%d baudrate fail with DLAB not set\n", up->port.line);\r
+                               return -1;\r
+                       }\r
+               }\r
+\r
+               tmout = 15000;\r
+               while(serial_in(up, UART_USR) & UART_USR_BUSY){\r
+                       if (--tmout == 0){\r
+                               dbg("set serial.%d baudrate timeout\n", up->port.line);\r
+                               return -1;\r
+                       }\r
+               }\r
+       }\r
+\r
        serial_out(up, UART_DLL, value & 0xff);\r
        serial_out(up, UART_DLM, value >> 8 & 0xff);\r
+\r
+       return 0;\r
+\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 = 15000;\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
-               }\r
+       if(up->port.line != DBG_PORT)\r
+       {\r
+               while(serial_in(up, UART_USR) & UART_USR_BUSY){\r
 \r
-               udelay(1);\r
+                       if (--tmout == 0){\r
+                               dbg("set serial.%d lc r = 0x%02x timeout\n", up->port.line, value);\r
+                               return -1;\r
+                       }\r
+                       udelay(1);\r
+               }\r
        }\r
+\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
@@ -270,6 +297,29 @@ static inline void serial_rk_disable_ier_thri(struct uart_rk_port *up)
                serial_out(up, UART_IER, up->ier);\r
        }\r
 }\r
+#if 0\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, UART_LSR);\r
+    dbg("UART_LSR = 0x%0x\n", reg_value);\r
+    reg_value = serial_in(up, UART_MSR);\r
+    dbg("UART_MSR = 0x%0x\n", reg_value);\r
+    reg_value = serial_in(up, UART_MCR);\r
+    dbg("UART_MCR = 0x%0x\n", reg_value);\r
+    reg_value = serial_in(up, 0x21);\r
+    dbg("UART_RFL = 0x%0x\n", reg_value);\r
+    reg_value = serial_in(up, UART_LCR);\r
+    dbg("UART_LCR = 0x%0x\n", reg_value);\r
+       return 0;\r
+\r
+}\r
+#endif\r
 \r
 /*\r
  * FIFO support.\r
@@ -307,16 +357,7 @@ static void serial_rk_start_tx(struct uart_port *port)
 {\r
        struct uart_rk_port *up =\r
                container_of(port, struct uart_rk_port, port);\r
-/*\r
- *  struct circ_buf *xmit = &port->state->xmit;\r
-    int size = CIRC_CNT(xmit->head, xmit->tail, UART_XMIT_SIZE);\r
-       if(size > 64){\r
-               serial_rk_start_tx_dma(port);\r
-       }\r
-       else{\r
-               serial_rk_enable_ier_thri(up);\r
-       }\r
-*/\r
+\r
 \r
        if(0 == serial_rk_start_tx_dma(port)){\r
                serial_rk_enable_ier_thri(up);\r
@@ -729,8 +770,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 +912,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
        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 +927,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
+\r
+               if (status & UART_LSR_RFE) {\r
+                       status = serial_in(up, UART_LSR);\r
+                       dev_info(up->port.dev, "error:lsr=0x%x\n", status);\r
+               //      rk29_uart_dump_register(up);\r
+               }\r
+\r
                if (status & (UART_LSR_DR | UART_LSR_BI)) {\r
                        receive_chars(up, &status);\r
                }\r
@@ -923,24 +972,26 @@ static irqreturn_t serial_rk_interrupt(int irq, void *dev_id)
        unsigned int iir;\r
 \r
        iir = serial_in(up, UART_IIR);\r
-       DEBUG_INTR("%s(%d) iir = 0x%02x ", __func__, irq, iir);\r
+\r
+       DEBUG_INTR("%s(%d) iir = 0x%02x\n", __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
                 * the UART status register (USR) and the LCR re-written. */\r
-                                \r
+\r
                if(!(serial_in(up, UART_USR) & UART_USR_BUSY)){\r
-                       //serial_out(up, UART_LCR, up->lcr);\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
@@ -1006,7 +1057,7 @@ static void serial_rk_set_mctrl(struct uart_port *port, unsigned int mctrl)
        mcr |= up->mcr;\r
 \r
        serial_out(up, UART_MCR, mcr);\r
-       dev_dbg(port->dev, "-%s mcr: 0x%02x\n", __func__, mcr);\r
+       dev_dbg(port->dev, "-serial.%d %s mcr: 0x%02x\n", port->line, __func__, mcr);\r
 }\r
 \r
 static void serial_rk_break_ctl(struct uart_port *port, int break_state)\r
@@ -1139,14 +1190,14 @@ static int serial_rk_startup(struct uart_port *port)
        /*\r
         * Now, initialize the UART\r
         */\r
-       serial_lcr_write(up, UART_LCR_WLEN8);\r
+       serial_lcr_write(up, UART_LCR_WLEN8 | UART_LCR_EPAR);\r
 \r
        spin_lock_irqsave(&up->port.lock, flags);\r
 \r
        /*\r
         * Most PC uarts need OUT2 raised to enable interrupts.\r
         */\r
-       up->port.mctrl |= TIOCM_OUT2;\r
+//     up->port.mctrl |= TIOCM_OUT2;\r
 \r
        serial_rk_set_mctrl(&up->port, up->port.mctrl);\r
 \r
@@ -1157,17 +1208,16 @@ 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
-       serial_in(up, UART_USR);\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
 #endif\r
 \r
-\r
        if (1 == up->prk29_uart_dma_t->use_dma) {\r
 \r
                if(up->port.state->xmit.buf != up->prk29_uart_dma_t->tx_buffer){\r
@@ -1213,7 +1263,7 @@ static void serial_rk_shutdown(struct uart_port *port)
        serial_out(up, UART_IER, 0);\r
 \r
        spin_lock_irqsave(&up->port.lock, flags);\r
-       up->port.mctrl &= ~TIOCM_OUT2;\r
+//     up->port.mctrl &= ~TIOCM_OUT2;\r
        serial_rk_set_mctrl(&up->port, up->port.mctrl);\r
        spin_unlock_irqrestore(&up->port.lock, flags);\r
 \r
@@ -1237,43 +1287,33 @@ serial_rk_set_termios(struct uart_port *port, struct ktermios *termios,
 {\r
        struct uart_rk_port *up =\r
                container_of(port, struct uart_rk_port, port);\r
-       unsigned char cval=0, fcr = 0;\r
-       unsigned long flags, bits;\r
+       unsigned char cval, fcr = 0;\r
+       unsigned long flags;\r
        unsigned int baud, quot;\r
-\r
+       int timeout = 1000000;\r
        dev_dbg(port->dev, "+%s\n", __func__);\r
 \r
-       //start bit\r
-       bits += 1;\r
        switch (termios->c_cflag & CSIZE) {\r
        case CS5:\r
                cval = UART_LCR_WLEN5;\r
-               bits += 5;\r
                break;\r
        case CS6:\r
                cval = UART_LCR_WLEN6;\r
-               bits += 6;\r
                break;\r
        case CS7:\r
                cval = UART_LCR_WLEN7;\r
-               bits += 7;\r
                break;\r
+       case CS8:\r
        default:\r
                cval = UART_LCR_WLEN8;\r
-               bits += 8;\r
                break;\r
        }\r
 \r
        if (termios->c_cflag & CSTOPB){\r
                cval |= UART_LCR_STOP;\r
-               bits += 2;\r
-       }\r
-       else{\r
-               bits += 1;\r
        }\r
        if (termios->c_cflag & PARENB){\r
                cval |= UART_LCR_PARITY;\r
-               bits += 1;\r
        }\r
        if (!(termios->c_cflag & PARODD)){\r
                cval |= UART_LCR_EPAR;\r
@@ -1283,6 +1323,7 @@ serial_rk_set_termios(struct uart_port *port, struct ktermios *termios,
                cval |= UART_LCR_SPAR;\r
 #endif\r
 \r
+\r
        /*\r
         * Ask the core to calculate the divisor for us.\r
         */\r
@@ -1292,19 +1333,30 @@ serial_rk_set_termios(struct uart_port *port, struct ktermios *termios,
 \r
        quot = uart_get_divisor(port, baud);\r
 \r
+       dev_info(up->port.dev, "baud:%d\n", baud);\r
+//     dev_info(up->port.dev, "quot:%d\n", quot);\r
 \r
-       dev_info(up->port.dev, "*****baud:%d*******\n", baud);\r
-//     dev_info(up->port.dev, "*****quot:%d*******\n", quot);\r
+       /*\r
+        * To wait long enough to avoid writting lcr when the uart is busy\r
+        * because of data communication, so that we can set lcr and baud rate\r
+        * successfully. added by hhb@rock-chips.com\r
+        */\r
 \r
-       int timeout = 10000000;\r
-       while(serial_in(up, UART_USR)&UART_USR_BUSY){\r
-       if(timeout-- == 0){\r
-       printk("rk29_serial_set_termios uart timeout,uart%d,ret=0x%x\n",up->port.line,serial_in(up, UART_USR));\r
-       break;\r
-       }\r
-       cpu_relax();\r
+       while(serial_in(up, UART_USR) & UART_USR_BUSY){\r
+               if(--timeout == 0){\r
+                       if(port->line != DBG_PORT){\r
+                               serial_out(up, UART_SRR, UART_RESET);\r
+                       }\r
+                       dbg("rk_serial_set_termios uart.%d timeout,irq=%d,ret=0x%x  AND uart is reseted\n",\r
+                                       port->line, port->irq, serial_in(up, UART_USR));\r
+                       break;\r
+               }\r
+               cpu_relax();\r
        }\r
-       printk("%s:timeout=%d\n",__FUNCTION__,timeout);\r
+\r
+\r
+       printk("serial.%d timeout:%d\n", up->port.line,timeout);\r
+\r
 \r
        if (baud < 2400){\r
                fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;\r
@@ -1332,7 +1384,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
@@ -1381,25 +1432,49 @@ serial_rk_set_termios(struct uart_port *port, struct ktermios *termios,
                up->ier |= UART_IER_MSI;\r
 #endif\r
 \r
-       serial_lcr_write(up, cval | UART_LCR_DLAB);/* set DLAB */\r
-\r
-       serial_dl_write(up, quot);\r
-\r
-       serial_lcr_write(up, cval);             /* reset DLAB */\r
        up->lcr = cval;                         /* Save LCR */\r
+       /* set DLAB */\r
+       if(serial_lcr_write(up, cval | UART_LCR_DLAB)){\r
+               dbg("serial.%d set DLAB fail\n", up->port.line);\r
+               serial_out(up, UART_SRR, UART_RESET);\r
+               goto fail;\r
+       }\r
 \r
-       serial_rk_set_mctrl(&up->port, up->port.mctrl);\r
+       /* set uart baud rate */\r
+       if(serial_dl_write(up, quot)){\r
+               dbg("serial.%d set dll fail\n", up->port.line);\r
+               serial_out(up, UART_SRR, UART_RESET);\r
+               goto fail;\r
+       }\r
 \r
-       serial_out(up, UART_FCR, fcr);          /* set fcr */\r
-       up->ier |= UART_IER_RDI;\r
-       up->ier |= UART_IER_RLSI;\r
-       serial_out(up, UART_IER, up->ier);\r
+       /* reset DLAB */\r
+       if(serial_lcr_write(up, cval)){\r
+               dbg("serial.%d reset DLAB fail\n", up->port.line);\r
+               serial_out(up, UART_SRR, UART_RESET);\r
+               goto fail;\r
+       }\r
+       else{\r
+               serial_rk_set_mctrl(&up->port, up->port.mctrl);\r
+               serial_out(up, UART_FCR, fcr);          /* set fcr */\r
+               up->fcr = fcr;\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
 \r
        spin_unlock_irqrestore(&up->port.lock, flags);\r
+\r
        /* Don't rewrite B0 */\r
        if (tty_termios_baud_rate(termios))\r
                tty_termios_encode_baud_rate(termios, baud, baud);\r
        dev_dbg(port->dev, "-%s baud %d\n", __func__, baud);\r
+\r
+       return;\r
+\r
+fail:\r
+       spin_unlock_irqrestore(&up->port.lock, flags);\r
+\r
 }\r
 \r
 #if 0\r
@@ -1671,7 +1746,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
@@ -1768,8 +1843,13 @@ static int serial_rk_suspend(struct platform_device *dev, pm_message_t state)
 {\r
        struct uart_rk_port *up = platform_get_drvdata(dev);\r
 \r
-       if (up)\r
+       if (up && up->port.line != DBG_PORT && POWER_MANEGEMENT){\r
                uart_suspend_port(&serial_rk_reg, &up->port);\r
+       }\r
+       if(up->port.line == DBG_PORT && POWER_MANEGEMENT){\r
+               serial_rk_pm(&up->port, 1, 0);\r
+       }\r
+\r
        return 0;\r
 }\r
 \r
@@ -1777,8 +1857,12 @@ static int serial_rk_resume(struct platform_device *dev)
 {\r
        struct uart_rk_port *up = platform_get_drvdata(dev);\r
 \r
-       if (up)\r
+       if (up && up->port.line != DBG_PORT && POWER_MANEGEMENT){\r
                uart_resume_port(&serial_rk_reg, &up->port);\r
+       }\r
+       if(up->port.line == DBG_PORT && POWER_MANEGEMENT){\r
+               serial_rk_pm(&up->port, 0, 1);\r
+       }\r
        return 0;\r
 }\r
 \r