Re: [PATCH v3] gpio: mxc: add runtime pm support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Jul 6, 2023 at 5:56 PM Shenwei Wang <shenwei.wang@xxxxxxx> wrote:
>
> Add runtime PM support and allow the GPIO controller to enter
> into runtime suspend automatically when not in use to save power.
> However, it will automatically resume and enable clocks when a
> GPIO or IRQ is requested.
>
> While putting the GPIO module itself into power saving mode may not
> have an obvious impact on current dissipation, the function is necessary
> because the GPIO module disables its clock when idle. This enables the
> system an opportunity to power off the parent subsystem, and this conserves
> more power. The typical i.MX8 SoC features up to 8 GPIO controllers, but
> most of the controllers often remain unused.

Looks better, thank you.
Reviewed-by: Andy Shevchenko <andy.shevchenko@xxxxxxxxx>

> Signed-off-by: Shenwei Wang <shenwei.wang@xxxxxxx>
> ---
> v3:
>   - Improved the error handling logic in the probe() function. Thanks a
>     lot to AndyS for reviewing and pointing out the issue.
>
> v2:
>   - improved the patch comments
>   - using pm_runtime_resume_and_get to avoid reference count disbalance
>     when errors
>   - removed the __maybe_unused identifier
>
>  drivers/gpio/gpio-mxc.c | 61 +++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 59 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
> index 9d0cec4b82a3..a9fb6bd9aa6f 100644
> --- a/drivers/gpio/gpio-mxc.c
> +++ b/drivers/gpio/gpio-mxc.c
> @@ -17,6 +17,7 @@
>  #include <linux/irqchip/chained_irq.h>
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
>  #include <linux/slab.h>
>  #include <linux/spinlock.h>
>  #include <linux/syscore_ops.h>
> @@ -382,6 +383,23 @@ static int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
>         return irq_find_mapping(port->domain, offset);
>  }
>
> +static int mxc_gpio_request(struct gpio_chip *chip, unsigned int offset)
> +{
> +       int ret;
> +
> +       ret = gpiochip_generic_request(chip, offset);
> +       if (ret)
> +               return ret;
> +
> +       return pm_runtime_resume_and_get(chip->parent);
> +}
> +
> +static void mxc_gpio_free(struct gpio_chip *chip, unsigned int offset)
> +{
> +       gpiochip_generic_free(chip, offset);
> +       pm_runtime_put(chip->parent);
> +}
> +
>  static int mxc_gpio_probe(struct platform_device *pdev)
>  {
>         struct device_node *np = pdev->dev.of_node;
> @@ -429,6 +447,10 @@ static int mxc_gpio_probe(struct platform_device *pdev)
>         if (of_device_is_compatible(np, "fsl,imx7d-gpio"))
>                 port->power_off = true;
>
> +       pm_runtime_get_noresume(&pdev->dev);
> +       pm_runtime_set_active(&pdev->dev);
> +       pm_runtime_enable(&pdev->dev);
> +
>         /* disable the interrupt and clear the status */
>         writel(0, port->base + GPIO_IMR);
>         writel(~0, port->base + GPIO_ISR);
> @@ -459,8 +481,8 @@ static int mxc_gpio_probe(struct platform_device *pdev)
>         if (err)
>                 goto out_bgio;
>
> -       port->gc.request = gpiochip_generic_request;
> -       port->gc.free = gpiochip_generic_free;
> +       port->gc.request = mxc_gpio_request;
> +       port->gc.free = mxc_gpio_free;
>         port->gc.to_irq = mxc_gpio_to_irq;
>         port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
>                                              pdev->id * 32;
> @@ -482,6 +504,8 @@ static int mxc_gpio_probe(struct platform_device *pdev)
>                 goto out_bgio;
>         }
>
> +       irq_domain_set_pm_device(port->domain, &pdev->dev);
> +
>         /* gpio-mxc can be a generic irq chip */
>         err = mxc_gpio_init_gc(port, irq_base);
>         if (err < 0)
> @@ -490,12 +514,15 @@ static int mxc_gpio_probe(struct platform_device *pdev)
>         list_add_tail(&port->node, &mxc_gpio_ports);
>
>         platform_set_drvdata(pdev, port);
> +       pm_runtime_put_autosuspend(&pdev->dev);
>
>         return 0;
>
>  out_irqdomain_remove:
>         irq_domain_remove(port->domain);
>  out_bgio:
> +       pm_runtime_disable(&pdev->dev);
> +       pm_runtime_put_noidle(&pdev->dev);
>         clk_disable_unprepare(port->clk);
>         dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err);
>         return err;
> @@ -572,6 +599,30 @@ static bool mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable)
>         return ret;
>  }
>
> +static int mxc_gpio_runtime_suspend(struct device *dev)
> +{
> +       struct mxc_gpio_port *port = dev_get_drvdata(dev);
> +
> +       mxc_gpio_save_regs(port);
> +       clk_disable_unprepare(port->clk);
> +
> +       return 0;
> +}
> +
> +static int mxc_gpio_runtime_resume(struct device *dev)
> +{
> +       struct mxc_gpio_port *port = dev_get_drvdata(dev);
> +       int ret;
> +
> +       ret = clk_prepare_enable(port->clk);
> +       if (ret)
> +               return ret;
> +
> +       mxc_gpio_restore_regs(port);
> +
> +       return 0;
> +}
> +
>  static int __maybe_unused mxc_gpio_noirq_suspend(struct device *dev)
>  {
>         struct platform_device *pdev = to_platform_device(dev);
> @@ -597,14 +648,19 @@ static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev)
>
>  static const struct dev_pm_ops mxc_gpio_dev_pm_ops = {
>         SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume)
> +       SET_RUNTIME_PM_OPS(mxc_gpio_runtime_suspend, mxc_gpio_runtime_resume, NULL)
>  };
>
>  static int mxc_gpio_syscore_suspend(void)
>  {
>         struct mxc_gpio_port *port;
> +       int ret;
>
>         /* walk through all ports */
>         list_for_each_entry(port, &mxc_gpio_ports, node) {
> +               ret = clk_prepare_enable(port->clk);
> +               if (ret)
> +                       return ret;
>                 mxc_gpio_save_regs(port);
>                 clk_disable_unprepare(port->clk);
>         }
> @@ -625,6 +681,7 @@ static void mxc_gpio_syscore_resume(void)
>                         return;
>                 }
>                 mxc_gpio_restore_regs(port);
> +               clk_disable_unprepare(port->clk);
>         }
>  }
>
> --
> 2.34.1
>


-- 
With Best Regards,
Andy Shevchenko




[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux