Message ID | 20221109105659.17381-3-sherry.sun@nxp.com |
---|---|
State | New |
Headers | show |
Series | fsl_lpuart: add some new features for lpuart driver | expand |
> -----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) > >
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)
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(+)