From: hhb <hhb@rock-chips.com>
Date: Fri, 5 Aug 2011 09:23:46 +0000 (+0800)
Subject: a22:UART:fix uart receive fifo error bug
X-Git-Tag: firefly_0821_release~9772^2~18^2~23^2~7
X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=a452030b6a6a61b2bc7767f11adca854c84cfde2;p=firefly-linux-kernel-4.4.55.git

a22:UART:fix uart receive fifo error bug
---

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