[2/2] tty: serial: fsl_lpuart: Add runtime pm support
Commit Message
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
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.
> -----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)
> >
@@ -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)