> -----Original Message----- > From: Ilpo Järvinen <ilpo.jarvinen@xxxxxxxxxxxxxxx> > Sent: 2022年11月9日 20:39 > To: Sherry Sun <sherry.sun@xxxxxxx> > Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>; Jiri Slaby > <jirislaby@xxxxxxxxxx>; linux-serial <linux-serial@xxxxxxxxxxxxxxx>; LKML > <linux-kernel@xxxxxxxxxxxxxxx>; dl-linux-imx <linux-imx@xxxxxxx> > 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@xxxxxxx> > > --- > > 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) > >