123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- From 4db59ee0d7224e0c8008534c9247480a83889034 Mon Sep 17 00:00:00 2001
- From: Fugang Duan <[email protected]>
- 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 <[email protected]>
- ---
- 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>
- @@ -1722,10 +1723,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 */
- @@ -1738,14 +1752,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;
- }
- @@ -1772,11 +1779,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;
-
- @@ -1808,17 +1831,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_setup_watermark_enable(sport);
- - lpuart32_configure(sport);
- + lpuart32_hw_setup(sport);
-
- - spin_unlock_irqrestore(&sport->port.lock, flags);
- return 0;
- }
-
- @@ -2876,108 +2890,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;
- +}
-
- - /* Disable Rx DMA to use UART port as wakeup source */
- +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);
- +
- + 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)
|