ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/target/linux/layerscape/patches-5.4/819-uart-0010-tty-serial-lpuart-enable-wakeup-source-for-lpuart.patch b/target/linux/layerscape/patches-5.4/819-uart-0010-tty-serial-lpuart-enable-wakeup-source-for-lpuart.patch
new file mode 100644
index 0000000..be1a567
--- /dev/null
+++ b/target/linux/layerscape/patches-5.4/819-uart-0010-tty-serial-lpuart-enable-wakeup-source-for-lpuart.patch
@@ -0,0 +1,392 @@
+From 4db59ee0d7224e0c8008534c9247480a83889034 Mon Sep 17 00:00:00 2001
+From: Fugang Duan <fugang.duan@nxp.com>
+Date: Wed, 11 Sep 2019 17:01:45 +0800
+Subject: [PATCH] tty: serial: lpuart: enable wakeup source for lpuart
+
+When use lpuart with DMA mode as wake up source, it still switch to
+cpu mode in .suspend() that enable cpu interrupts RIE and ILIE as
+wakeup source. Enable the wakeup irq bits in .suspend_noirq() and
+disable the wakeup irq bits in .resume_noirq().
+
+For DMA mode, after system resume back, it needs to setup DMA again,
+if DMA setup is failed, it switchs to CPU mode. .resume() will share
+the HW setup code with .startup(), so abstract the same code to the
+api like lpuartx_hw_setup().
+
+Signed-off-by: Fugang Duan <fugang.duan@nxp.com>
+---
+ drivers/tty/serial/fsl_lpuart.c | 285 ++++++++++++++++++++++++++++------------
+ 1 file changed, 198 insertions(+), 87 deletions(-)
+
+--- a/drivers/tty/serial/fsl_lpuart.c
++++ b/drivers/tty/serial/fsl_lpuart.c
+@@ -21,6 +21,7 @@
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+ #include <linux/of_dma.h>
++#include <linux/pinctrl/consumer.h>
+ #include <linux/pm_domain.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/reset.h>
+@@ -1739,10 +1740,23 @@ static void lpuart_rx_dma_startup(struct
+ 	}
+ }
+ 
++static void lpuart_hw_setup(struct lpuart_port *sport)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&sport->port.lock, flags);
++
++	lpuart_setup_watermark_enable(sport);
++
++	lpuart_rx_dma_startup(sport);
++	lpuart_tx_dma_startup(sport);
++
++	spin_unlock_irqrestore(&sport->port.lock, flags);
++}
++
+ static int lpuart_startup(struct uart_port *port)
+ {
+ 	struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+-	unsigned long flags;
+ 	unsigned char temp;
+ 
+ 	/* determine FIFO size and enable FIFO mode */
+@@ -1755,14 +1769,7 @@ static int lpuart_startup(struct uart_po
+ 	sport->rxfifo_size = UARTFIFO_DEPTH((temp >> UARTPFIFO_RXSIZE_OFF) &
+ 					    UARTPFIFO_FIFOSIZE_MASK);
+ 
+-	spin_lock_irqsave(&sport->port.lock, flags);
+-
+-	lpuart_setup_watermark_enable(sport);
+-
+-	lpuart_rx_dma_startup(sport);
+-	lpuart_tx_dma_startup(sport);
+-
+-	spin_unlock_irqrestore(&sport->port.lock, flags);
++	lpuart_hw_setup(sport);
+ 
+ 	return 0;
+ }
+@@ -1789,11 +1796,27 @@ static void lpuart32_configure(struct lp
+ 	lpuart32_write(&sport->port, temp, UARTCTRL);
+ }
+ 
++static void lpuart32_hw_setup(struct lpuart_port *sport)
++{
++	unsigned long flags;
++
++	spin_lock_irqsave(&sport->port.lock, flags);
++
++	lpuart32_hw_disable(sport);
++
++	lpuart_rx_dma_startup(sport);
++	lpuart_tx_dma_startup(sport);
++
++	lpuart32_setup_watermark_enable(sport);
++	lpuart32_configure(sport);
++
++	spin_unlock_irqrestore(&sport->port.lock, flags);
++}
++
+ static int lpuart32_startup(struct uart_port *port)
+ {
+ 	struct lpuart_port *sport = container_of(port, struct lpuart_port, port);
+ 	struct tty_port *tty_port = &sport->port.state->port;
+-	unsigned long flags;
+ 	unsigned long temp;
+ 	int ret;
+ 
+@@ -1825,17 +1848,8 @@ static int lpuart32_startup(struct uart_
+ 		sport->port.fifosize = sport->txfifo_size;
+ 	}
+ 
+-	spin_lock_irqsave(&sport->port.lock, flags);
+-
+-	lpuart32_hw_disable(sport);
+-
+-	lpuart_rx_dma_startup(sport);
+-	lpuart_tx_dma_startup(sport);
++	lpuart32_hw_setup(sport);
+ 
+-	lpuart32_setup_watermark_enable(sport);
+-	lpuart32_configure(sport);
+-
+-	spin_unlock_irqrestore(&sport->port.lock, flags);
+ 	return 0;
+ }
+ 
+@@ -2893,108 +2907,205 @@ static int lpuart_runtime_resume(struct
+ 	return lpuart_enable_clks(sport);
+ };
+ 
+-static int lpuart_suspend(struct device *dev)
++static void serial_lpuart_enable_wakeup(struct lpuart_port *sport, bool on)
+ {
+-	struct lpuart_port *sport = dev_get_drvdata(dev);
+-	unsigned long temp;
+-	bool irq_wake;
+-	int ret;
+-
+-	ret = clk_prepare_enable(sport->ipg_clk);
+-	if (ret)
+-		return ret;
++	unsigned int val;
+ 
+ 	if (lpuart_is_32(sport)) {
+-		/* disable Rx/Tx and interrupts */
+-		temp = lpuart32_read(&sport->port, UARTCTRL);
+-		temp &= ~(UARTCTRL_TE | UARTCTRL_TIE | UARTCTRL_TCIE);
+-		lpuart32_write(&sport->port, temp, UARTCTRL);
++		val = lpuart32_read(&sport->port, UARTCTRL);
++		if (on)
++			val |= (UARTCTRL_RIE | UARTCTRL_ILIE);
++		else
++			val &= ~(UARTCTRL_RIE | UARTCTRL_ILIE);
++		lpuart32_write(&sport->port, val, UARTCTRL);
+ 	} else {
+-		/* disable Rx/Tx and interrupts */
+-		temp = readb(sport->port.membase + UARTCR2);
+-		temp &= ~(UARTCR2_TE | UARTCR2_TIE | UARTCR2_TCIE);
+-		writeb(temp, sport->port.membase + UARTCR2);
++		val = readb(sport->port.membase + UARTCR2);
++		if (on)
++			val |= UARTCR2_RIE;
++		else
++			val &= ~UARTCR2_RIE;
++		writeb(val, sport->port.membase + UARTCR2);
+ 	}
++}
+ 
+-	clk_disable_unprepare(sport->ipg_clk);
++static bool lpuart_uport_is_active(struct lpuart_port *sport)
++{
++	struct tty_port *port = &sport->port.state->port;
++	struct tty_struct *tty;
++	struct device *tty_dev;
++	int may_wake = 0;
+ 
+-	uart_suspend_port(&lpuart_reg, &sport->port);
++	tty = tty_port_tty_get(port);
++	if (tty) {
++		tty_dev = tty->dev;
++		may_wake = device_may_wakeup(tty_dev);
++		tty_kref_put(tty);
++	}
+ 
+-	/* uart_suspend_port() might set wakeup flag */
+-	irq_wake = irqd_is_wakeup_set(irq_get_irq_data(sport->port.irq));
+-	if (sport->port.suspended && !irq_wake)
+-		return 0;
++	if ((tty_port_initialized(port) && may_wake) ||
++	    (!console_suspend_enabled && uart_console(&sport->port)))
++		return true;
+ 
+-	if (sport->lpuart_dma_rx_use) {
+-		/*
+-		 * EDMA driver during suspend will forcefully release any
+-		 * non-idle DMA channels. If port wakeup is enabled or if port
+-		 * is console port or 'no_console_suspend' is set the Rx DMA
+-		 * cannot resume as as expected, hence gracefully release the
+-		 * Rx DMA path before suspend and start Rx DMA path on resume.
+-		 */
+-		if (irq_wake) {
+-			lpuart_del_timer_sync(sport);
+-			lpuart_dma_rx_free(&sport->port);
+-		}
++	return false;
++}
++
++static int lpuart_suspend_noirq(struct device *dev)
++{
++	struct lpuart_port *sport = dev_get_drvdata(dev);
++	bool irq_wake = irqd_is_wakeup_set(irq_get_irq_data(sport->port.irq));
++
++	if (lpuart_uport_is_active(sport))
++		serial_lpuart_enable_wakeup(sport, !!irq_wake);
++
++	pinctrl_pm_select_sleep_state(dev);
+ 
+-		/* Disable Rx DMA to use UART port as wakeup source */
++	return 0;
++}
++
++static int lpuart_resume_noirq(struct device *dev)
++{
++	struct lpuart_port *sport = dev_get_drvdata(dev);
++	unsigned int val;
++
++	pinctrl_pm_select_default_state(dev);
++
++	if (lpuart_uport_is_active(sport)) {
++		serial_lpuart_enable_wakeup(sport, false);
++
++		/* clear the wakeup flags */
+ 		if (lpuart_is_32(sport)) {
+-			temp = lpuart32_read(&sport->port, UARTBAUD);
+-			lpuart32_write(&sport->port, temp & ~UARTBAUD_RDMAE,
+-				       UARTBAUD);
+-		} else {
+-			writeb(readb(sport->port.membase + UARTCR5) &
+-			       ~UARTCR5_RDMAS, sport->port.membase + UARTCR5);
++			val = lpuart32_read(&sport->port, UARTSTAT);
++			lpuart32_write(&sport->port, val, UARTSTAT);
+ 		}
+ 	}
+ 
+-	if (sport->lpuart_dma_tx_use) {
+-		sport->dma_tx_in_progress = false;
+-		dmaengine_terminate_all(sport->dma_tx_chan);
+-	}
+-
+ 	return 0;
+ }
+ 
+-static int lpuart_resume(struct device *dev)
++static int lpuart_suspend(struct device *dev)
+ {
+ 	struct lpuart_port *sport = dev_get_drvdata(dev);
+-	bool irq_wake = irqd_is_wakeup_set(irq_get_irq_data(sport->port.irq));
+-	int ret;
++	unsigned long temp;
++	unsigned long flags;
+ 
+-	ret = clk_prepare_enable(sport->ipg_clk);
+-	if (ret)
+-		return ret;
++	uart_suspend_port(&lpuart_reg, &sport->port);
+ 
+-	if (lpuart_is_32(sport))
+-		lpuart32_setup_watermark_enable(sport);
+-	else
+-		lpuart_setup_watermark_enable(sport);
++	if (lpuart_uport_is_active(sport)) {
++		spin_lock_irqsave(&sport->port.lock, flags);
++		if (lpuart_is_32(sport)) {
++			temp = lpuart32_read(&sport->port, UARTCTRL);
++			temp &= ~(UARTCTRL_TE | UARTCTRL_TIE | UARTCTRL_TCIE);
++			lpuart32_write(&sport->port, temp, UARTCTRL);
++		} else {
++			temp = readb(sport->port.membase + UARTCR2);
++			temp &= ~(UARTCR2_TE | UARTCR2_TIE | UARTCR2_TCIE);
++			writeb(temp, sport->port.membase + UARTCR2);
++		}
++		spin_unlock_irqrestore(&sport->port.lock, flags);
+ 
+-	if (sport->lpuart_dma_rx_use) {
+-		if (irq_wake) {
+-			if (!lpuart_start_rx_dma(sport))
+-				rx_dma_timer_init(sport);
+-			else
+-				sport->lpuart_dma_rx_use = false;
++		if (sport->lpuart_dma_rx_use) {
++			/*
++			 * EDMA driver during suspend will forcefully release any
++			 * non-idle DMA channels. If port wakeup is enabled or if port
++			 * is console port or 'no_console_suspend' is set the Rx DMA
++			 * cannot resume as as expected, hence gracefully release the
++			 * Rx DMA path before suspend and start Rx DMA path on resume.
++			 */
++			lpuart_del_timer_sync(sport);
++			lpuart_dma_rx_free(&sport->port);
++
++			/* Disable Rx DMA to use UART port as wakeup source */
++			spin_lock_irqsave(&sport->port.lock, flags);
++			if (lpuart_is_32(sport)) {
++				temp = lpuart32_read(&sport->port, UARTBAUD);
++				lpuart32_write(&sport->port, temp & ~UARTBAUD_RDMAE,
++					       UARTBAUD);
++			} else {
++				writeb(readb(sport->port.membase + UARTCR5) &
++				       ~UARTCR5_RDMAS, sport->port.membase + UARTCR5);
++			}
++			spin_unlock_irqrestore(&sport->port.lock, flags);
+ 		}
++
++		if (sport->lpuart_dma_tx_use) {
++			spin_lock_irqsave(&sport->port.lock, flags);
++			if (lpuart_is_32(sport)) {
++				temp = lpuart32_read(&sport->port, UARTBAUD);
++				temp &= ~UARTBAUD_TDMAE;
++				lpuart32_write(&sport->port, temp, UARTBAUD);
++			} else {
++				temp = readb(sport->port.membase + UARTCR5);
++				temp &= ~UARTCR5_TDMAS;
++				writeb(temp, sport->port.membase + UARTCR5);
++			}
++			spin_unlock_irqrestore(&sport->port.lock, flags);
++			sport->dma_tx_in_progress = false;
++			dmaengine_terminate_all(sport->dma_tx_chan);
++		}
++	} else if (pm_runtime_active(sport->port.dev)) {
++		lpuart_disable_clks(sport);
++		pm_runtime_disable(sport->port.dev);
++		pm_runtime_set_suspended(sport->port.dev);
+ 	}
+ 
+-	lpuart_tx_dma_startup(sport);
++	return 0;
++}
+ 
+-	if (lpuart_is_32(sport))
+-		lpuart32_configure(sport);
++static void lpuart_console_fixup(struct lpuart_port *sport)
++{
++	struct tty_port *port = &sport->port.state->port;
++	struct uart_port *uport = &sport->port;
++	struct ktermios termios;
+ 
+-	clk_disable_unprepare(sport->ipg_clk);
++	/* i.MX7ULP enter VLLS mode that lpuart module power off and registers
++	 * all lost no matter the port is wakeup source.
++	 * For console port, console baud rate setting lost and print messy
++	 * log when enable the console port as wakeup source. To avoid the
++	 * issue happen, user should not enable uart port as wakeup source
++	 * in VLLS mode, or restore console setting here.
++	 */
++	if (is_imx7ulp_lpuart(sport) && lpuart_uport_is_active(sport) &&
++	    console_suspend_enabled && uart_console(&sport->port)) {
++
++		mutex_lock(&port->mutex);
++		memset(&termios, 0, sizeof(struct ktermios));
++		termios.c_cflag = uport->cons->cflag;
++		if (port->tty && termios.c_cflag == 0)
++			termios = port->tty->termios;
++		uport->ops->set_termios(uport, &termios, NULL);
++		mutex_unlock(&port->mutex);
++	}
++}
++
++static int lpuart_resume(struct device *dev)
++{
++	struct lpuart_port *sport = dev_get_drvdata(dev);
++	int ret;
+ 
++	if (lpuart_uport_is_active(sport)) {
++		if (lpuart_is_32(sport))
++			lpuart32_hw_setup(sport);
++		else
++			lpuart_hw_setup(sport);
++	} else if (pm_runtime_active(sport->port.dev)) {
++		ret = lpuart_enable_clks(sport);
++		if (ret)
++			return ret;
++		pm_runtime_set_active(sport->port.dev);
++		pm_runtime_enable(sport->port.dev);
++	}
++
++	lpuart_console_fixup(sport);
+ 	uart_resume_port(&lpuart_reg, &sport->port);
+ 
+ 	return 0;
+ }
++
+ static const struct dev_pm_ops lpuart_pm_ops = {
+ 	SET_RUNTIME_PM_OPS(lpuart_runtime_suspend,
+ 			   lpuart_runtime_resume, NULL)
++	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(lpuart_suspend_noirq,
++				      lpuart_resume_noirq)
+ 	SET_SYSTEM_SLEEP_PM_OPS(lpuart_suspend, lpuart_resume)
+ };
+ #define SERIAL_LPUART_PM_OPS	(&lpuart_pm_ops)