[2/2] tty: serial: fsl_lpuart: Add runtime pm support

Message ID 20221109105659.17381-3-sherry.sun@nxp.com
State New
Headers
Series fsl_lpuart: add some new features for lpuart driver |

Commit Message

Sherry Sun Nov. 9, 2022, 10:56 a.m. UTC
  Add runtime pm support to manage the lpuart clock.

Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
---
 drivers/tty/serial/fsl_lpuart.c | 60 +++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)
  

Comments

Ilpo Järvinen Nov. 9, 2022, 12:38 p.m. UTC | #1
On Wed, 9 Nov 2022, Sherry Sun wrote:

> Add runtime pm support to manage the lpuart clock.
> 
> Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
> ---
>  drivers/tty/serial/fsl_lpuart.c | 60 +++++++++++++++++++++++++++++++++
>  1 file changed, 60 insertions(+)
> 
> diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
> index 474943cb06b2..e678a7aaf7e4 100644
> --- a/drivers/tty/serial/fsl_lpuart.c
> +++ b/drivers/tty/serial/fsl_lpuart.c
> @@ -19,6 +19,7 @@
>  #include <linux/of_device.h>
>  #include <linux/of_dma.h>
>  #include <linux/pinctrl/consumer.h>
> +#include <linux/pm_runtime.h>
>  #include <linux/serial_core.h>
>  #include <linux/slab.h>
>  #include <linux/tty_flip.h>
> @@ -235,6 +236,7 @@
>  
>  /* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
>  #define DMA_RX_TIMEOUT		(10)
> +#define UART_AUTOSUSPEND_TIMEOUT	3000
>  
>  #define DRIVER_NAME	"fsl-lpuart"
>  #define DEV_NAME	"ttyLP"
> @@ -795,6 +797,20 @@ static void lpuart32_start_tx(struct uart_port *port)
>  	}
>  }
>  
> +static void
> +lpuart_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
> +{
> +	switch (state) {
> +	case UART_PM_STATE_OFF:
> +		pm_runtime_mark_last_busy(port->dev);
> +		pm_runtime_put_autosuspend(port->dev);
> +		break;
> +	default:
> +		pm_runtime_get_sync(port->dev);
> +		break;
> +	}
> +}
> +
>  /* return TIOCSER_TEMT when transmitter is not busy */
>  static unsigned int lpuart_tx_empty(struct uart_port *port)
>  {
> @@ -2243,6 +2259,7 @@ static const struct uart_ops lpuart_pops = {
>  	.startup	= lpuart_startup,
>  	.shutdown	= lpuart_shutdown,
>  	.set_termios	= lpuart_set_termios,
> +	.pm		= lpuart_uart_pm,
>  	.type		= lpuart_type,
>  	.request_port	= lpuart_request_port,
>  	.release_port	= lpuart_release_port,
> @@ -2267,6 +2284,7 @@ static const struct uart_ops lpuart32_pops = {
>  	.startup	= lpuart32_startup,
>  	.shutdown	= lpuart32_shutdown,
>  	.set_termios	= lpuart32_set_termios,
> +	.pm		= lpuart_uart_pm,
>  	.type		= lpuart_type,
>  	.request_port	= lpuart_request_port,
>  	.release_port	= lpuart_release_port,
> @@ -2747,6 +2765,11 @@ static int lpuart_probe(struct platform_device *pdev)
>  		handler = lpuart_int;
>  	}
>  
> +	pm_runtime_use_autosuspend(&pdev->dev);
> +	pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT);
> +	pm_runtime_set_active(&pdev->dev);
> +	pm_runtime_enable(&pdev->dev);
> +
>  	ret = lpuart_global_reset(sport);
>  	if (ret)
>  		goto failed_reset;
> @@ -2771,6 +2794,9 @@ static int lpuart_probe(struct platform_device *pdev)
>  failed_attach_port:
>  failed_get_rs485:
>  failed_reset:
> +	pm_runtime_disable(&pdev->dev);
> +	pm_runtime_set_suspended(&pdev->dev);
> +	pm_runtime_dont_use_autosuspend(&pdev->dev);
>  	lpuart_disable_clks(sport);
>  	return ret;
>  }
> @@ -2789,9 +2815,30 @@ static int lpuart_remove(struct platform_device *pdev)
>  	if (sport->dma_rx_chan)
>  		dma_release_channel(sport->dma_rx_chan);
>  
> +	pm_runtime_disable(&pdev->dev);
> +	pm_runtime_set_suspended(&pdev->dev);
> +	pm_runtime_dont_use_autosuspend(&pdev->dev);
>  	return 0;
>  }
>  
> +static int __maybe_unused lpuart_runtime_suspend(struct device *dev)

IIRC, the PM_OPS macros contain special logic to make __maybe_unused 
unnecessary for these functions.
  
Sherry Sun Nov. 10, 2022, 11:04 a.m. UTC | #2
> -----Original Message-----
> From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
> Sent: 2022年11月9日 20:39
> To: Sherry Sun <sherry.sun@nxp.com>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>; Jiri Slaby
> <jirislaby@kernel.org>; linux-serial <linux-serial@vger.kernel.org>; LKML
> <linux-kernel@vger.kernel.org>; dl-linux-imx <linux-imx@nxp.com>
> Subject: Re: [PATCH 2/2] tty: serial: fsl_lpuart: Add runtime pm support
> 
> On Wed, 9 Nov 2022, Sherry Sun wrote:
> 
> > Add runtime pm support to manage the lpuart clock.
> >
> > Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
> > ---
> >  drivers/tty/serial/fsl_lpuart.c | 60
> > +++++++++++++++++++++++++++++++++
> >  1 file changed, 60 insertions(+)
> >
> > diff --git a/drivers/tty/serial/fsl_lpuart.c
> > b/drivers/tty/serial/fsl_lpuart.c index 474943cb06b2..e678a7aaf7e4
> > 100644
> > --- a/drivers/tty/serial/fsl_lpuart.c
> > +++ b/drivers/tty/serial/fsl_lpuart.c
> > @@ -19,6 +19,7 @@
> >  #include <linux/of_device.h>
> >  #include <linux/of_dma.h>
> >  #include <linux/pinctrl/consumer.h>
> > +#include <linux/pm_runtime.h>
> >  #include <linux/serial_core.h>
> >  #include <linux/slab.h>
> >  #include <linux/tty_flip.h>
> > @@ -235,6 +236,7 @@
> >
> >  /* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
> >  #define DMA_RX_TIMEOUT		(10)
> > +#define UART_AUTOSUSPEND_TIMEOUT	3000
> >
> >  #define DRIVER_NAME	"fsl-lpuart"
> >  #define DEV_NAME	"ttyLP"
> > @@ -795,6 +797,20 @@ static void lpuart32_start_tx(struct uart_port
> *port)
> >  	}
> >  }
> >
> > +static void
> > +lpuart_uart_pm(struct uart_port *port, unsigned int state, unsigned
> > +int oldstate) {
> > +	switch (state) {
> > +	case UART_PM_STATE_OFF:
> > +		pm_runtime_mark_last_busy(port->dev);
> > +		pm_runtime_put_autosuspend(port->dev);
> > +		break;
> > +	default:
> > +		pm_runtime_get_sync(port->dev);
> > +		break;
> > +	}
> > +}
> > +
> >  /* return TIOCSER_TEMT when transmitter is not busy */  static
> > unsigned int lpuart_tx_empty(struct uart_port *port)  { @@ -2243,6
> > +2259,7 @@ static const struct uart_ops lpuart_pops = {
> >  	.startup	= lpuart_startup,
> >  	.shutdown	= lpuart_shutdown,
> >  	.set_termios	= lpuart_set_termios,
> > +	.pm		= lpuart_uart_pm,
> >  	.type		= lpuart_type,
> >  	.request_port	= lpuart_request_port,
> >  	.release_port	= lpuart_release_port,
> > @@ -2267,6 +2284,7 @@ static const struct uart_ops lpuart32_pops = {
> >  	.startup	= lpuart32_startup,
> >  	.shutdown	= lpuart32_shutdown,
> >  	.set_termios	= lpuart32_set_termios,
> > +	.pm		= lpuart_uart_pm,
> >  	.type		= lpuart_type,
> >  	.request_port	= lpuart_request_port,
> >  	.release_port	= lpuart_release_port,
> > @@ -2747,6 +2765,11 @@ static int lpuart_probe(struct platform_device
> *pdev)
> >  		handler = lpuart_int;
> >  	}
> >
> > +	pm_runtime_use_autosuspend(&pdev->dev);
> > +	pm_runtime_set_autosuspend_delay(&pdev->dev,
> UART_AUTOSUSPEND_TIMEOUT);
> > +	pm_runtime_set_active(&pdev->dev);
> > +	pm_runtime_enable(&pdev->dev);
> > +
> >  	ret = lpuart_global_reset(sport);
> >  	if (ret)
> >  		goto failed_reset;
> > @@ -2771,6 +2794,9 @@ static int lpuart_probe(struct platform_device
> > *pdev)
> >  failed_attach_port:
> >  failed_get_rs485:
> >  failed_reset:
> > +	pm_runtime_disable(&pdev->dev);
> > +	pm_runtime_set_suspended(&pdev->dev);
> > +	pm_runtime_dont_use_autosuspend(&pdev->dev);
> >  	lpuart_disable_clks(sport);
> >  	return ret;
> >  }
> > @@ -2789,9 +2815,30 @@ static int lpuart_remove(struct platform_device
> *pdev)
> >  	if (sport->dma_rx_chan)
> >  		dma_release_channel(sport->dma_rx_chan);
> >
> > +	pm_runtime_disable(&pdev->dev);
> > +	pm_runtime_set_suspended(&pdev->dev);
> > +	pm_runtime_dont_use_autosuspend(&pdev->dev);
> >  	return 0;
> >  }
> >
> > +static int __maybe_unused lpuart_runtime_suspend(struct device *dev)
> 
> IIRC, the PM_OPS macros contain special logic to make __maybe_unused
> unnecessary for these functions.
> 

HI Ilpo, you are right, seems use pm_ptr() can remove the need to mark the pm functions as __maybe_unused.
I will change this in V2 patch.

Best Regards
Sherry


> --
>  i.
> 
> 
> > +{
> > +	struct platform_device *pdev = to_platform_device(dev);
> > +	struct lpuart_port *sport = platform_get_drvdata(pdev);
> > +
> > +	lpuart_disable_clks(sport);
> > +
> > +	return 0;
> > +};
> > +
> > +static int __maybe_unused lpuart_runtime_resume(struct device *dev)
> > +{
> > +	struct platform_device *pdev = to_platform_device(dev);
> > +	struct lpuart_port *sport = platform_get_drvdata(pdev);
> > +
> > +	return lpuart_enable_clks(sport);
> > +};
> > +
> >  static void serial_lpuart_enable_wakeup(struct lpuart_port *sport, bool on)
> >  {
> >  	unsigned int val, baud;
> > @@ -2937,6 +2984,10 @@ static int __maybe_unused
> lpuart_suspend(struct device *dev)
> >  			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);
> >  	}
> >
> >  	return 0;
> > @@ -2971,12 +3022,19 @@ static void lpuart_console_fixup(struct
> lpuart_port *sport)
> >  static int __maybe_unused 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);
> > @@ -2986,6 +3044,8 @@ static int __maybe_unused lpuart_resume(struct
> device *dev)
> >  }
> >
> >  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)
> >
  

Patch

diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 474943cb06b2..e678a7aaf7e4 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -19,6 +19,7 @@ 
 #include <linux/of_device.h>
 #include <linux/of_dma.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
 #include <linux/serial_core.h>
 #include <linux/slab.h>
 #include <linux/tty_flip.h>
@@ -235,6 +236,7 @@ 
 
 /* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
 #define DMA_RX_TIMEOUT		(10)
+#define UART_AUTOSUSPEND_TIMEOUT	3000
 
 #define DRIVER_NAME	"fsl-lpuart"
 #define DEV_NAME	"ttyLP"
@@ -795,6 +797,20 @@  static void lpuart32_start_tx(struct uart_port *port)
 	}
 }
 
+static void
+lpuart_uart_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
+{
+	switch (state) {
+	case UART_PM_STATE_OFF:
+		pm_runtime_mark_last_busy(port->dev);
+		pm_runtime_put_autosuspend(port->dev);
+		break;
+	default:
+		pm_runtime_get_sync(port->dev);
+		break;
+	}
+}
+
 /* return TIOCSER_TEMT when transmitter is not busy */
 static unsigned int lpuart_tx_empty(struct uart_port *port)
 {
@@ -2243,6 +2259,7 @@  static const struct uart_ops lpuart_pops = {
 	.startup	= lpuart_startup,
 	.shutdown	= lpuart_shutdown,
 	.set_termios	= lpuart_set_termios,
+	.pm		= lpuart_uart_pm,
 	.type		= lpuart_type,
 	.request_port	= lpuart_request_port,
 	.release_port	= lpuart_release_port,
@@ -2267,6 +2284,7 @@  static const struct uart_ops lpuart32_pops = {
 	.startup	= lpuart32_startup,
 	.shutdown	= lpuart32_shutdown,
 	.set_termios	= lpuart32_set_termios,
+	.pm		= lpuart_uart_pm,
 	.type		= lpuart_type,
 	.request_port	= lpuart_request_port,
 	.release_port	= lpuart_release_port,
@@ -2747,6 +2765,11 @@  static int lpuart_probe(struct platform_device *pdev)
 		handler = lpuart_int;
 	}
 
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_set_autosuspend_delay(&pdev->dev, UART_AUTOSUSPEND_TIMEOUT);
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
 	ret = lpuart_global_reset(sport);
 	if (ret)
 		goto failed_reset;
@@ -2771,6 +2794,9 @@  static int lpuart_probe(struct platform_device *pdev)
 failed_attach_port:
 failed_get_rs485:
 failed_reset:
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+	pm_runtime_dont_use_autosuspend(&pdev->dev);
 	lpuart_disable_clks(sport);
 	return ret;
 }
@@ -2789,9 +2815,30 @@  static int lpuart_remove(struct platform_device *pdev)
 	if (sport->dma_rx_chan)
 		dma_release_channel(sport->dma_rx_chan);
 
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+	pm_runtime_dont_use_autosuspend(&pdev->dev);
 	return 0;
 }
 
+static int __maybe_unused lpuart_runtime_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct lpuart_port *sport = platform_get_drvdata(pdev);
+
+	lpuart_disable_clks(sport);
+
+	return 0;
+};
+
+static int __maybe_unused lpuart_runtime_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct lpuart_port *sport = platform_get_drvdata(pdev);
+
+	return lpuart_enable_clks(sport);
+};
+
 static void serial_lpuart_enable_wakeup(struct lpuart_port *sport, bool on)
 {
 	unsigned int val, baud;
@@ -2937,6 +2984,10 @@  static int __maybe_unused lpuart_suspend(struct device *dev)
 			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);
 	}
 
 	return 0;
@@ -2971,12 +3022,19 @@  static void lpuart_console_fixup(struct lpuart_port *sport)
 static int __maybe_unused 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);
@@ -2986,6 +3044,8 @@  static int __maybe_unused lpuart_resume(struct device *dev)
 }
 
 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)