diff mbox series

clk: imx: imx8mp: Add pm_runtime support for power saving

Message ID 1710743811-1698-1-git-send-email-shengjiu.wang@nxp.com (mailing list archive)
State New, archived
Headers show
Series clk: imx: imx8mp: Add pm_runtime support for power saving | expand

Commit Message

Shengjiu Wang March 18, 2024, 6:36 a.m. UTC
Add pm_runtime support for power saving. In pm runtime suspend
state the registers will be reseted, so add registers save
in pm runtime suspend and restore them in pm runtime resume.

Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
 drivers/clk/imx/clk-imx8mp-audiomix.c | 99 ++++++++++++++++++++++++++-
 1 file changed, 96 insertions(+), 3 deletions(-)

Comments

Peng Fan March 18, 2024, 11:16 p.m. UTC | #1
> Subject: [PATCH] clk: imx: imx8mp: Add pm_runtime support for power
> saving
> 
> Add pm_runtime support for power saving. In pm runtime suspend state the
> registers will be reseted, so add registers save in pm runtime suspend and
> restore them in pm runtime resume.
> 
> Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>

Reviewed-by: Peng Fan <peng.fan@nxp.com>
> ---
>  drivers/clk/imx/clk-imx8mp-audiomix.c | 99 ++++++++++++++++++++++++++-
>  1 file changed, 96 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/clk/imx/clk-imx8mp-audiomix.c b/drivers/clk/imx/clk-
> imx8mp-audiomix.c
> index 55ed211a5e0b..d2bf53e2aacf 100644
> --- a/drivers/clk/imx/clk-imx8mp-audiomix.c
> +++ b/drivers/clk/imx/clk-imx8mp-audiomix.c
> @@ -7,10 +7,12 @@
> 
>  #include <linux/clk-provider.h>
>  #include <linux/device.h>
> +#include <linux/io.h>
>  #include <linux/mod_devicetable.h>
>  #include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> 
>  #include <dt-bindings/clock/imx8mp-clock.h>
> 
> @@ -18,6 +20,7 @@
> 
>  #define CLKEN0			0x000
>  #define CLKEN1			0x004
> +#define EARC			0x200
>  #define SAI1_MCLK_SEL		0x300
>  #define SAI2_MCLK_SEL		0x304
>  #define SAI3_MCLK_SEL		0x308
> @@ -26,6 +29,12 @@
>  #define SAI7_MCLK_SEL		0x314
>  #define PDM_SEL			0x318
>  #define SAI_PLL_GNRL_CTL	0x400
> +#define SAI_PLL_FDIVL_CTL0	0x404
> +#define SAI_PLL_FDIVL_CTL1	0x408
> +#define SAI_PLL_SSCG_CTL	0x40C
> +#define SAI_PLL_MNIT_CTL	0x410
> +#define IPG_LP_CTRL		0x504
> +#define REGS_NUM		16
> 
>  #define SAIn_MCLK1_PARENT(n)						\
>  static const struct clk_parent_data					\
> @@ -182,13 +191,65 @@ static struct clk_imx8mp_audiomix_sel sels[] = {
>  	CLK_SAIn(7)
>  };
> 
> +struct clk_imx8mp_audiomix_regs {
> +	u32 regs_num;
> +	u32 regs_off[];
> +};
> +
> +static const struct clk_imx8mp_audiomix_regs audiomix_regs = {
> +	.regs_num = REGS_NUM,
> +	.regs_off = {
> +		CLKEN0,
> +		CLKEN1,
> +		EARC,
> +		SAI1_MCLK_SEL,
> +		SAI2_MCLK_SEL,
> +		SAI3_MCLK_SEL,
> +		SAI5_MCLK_SEL,
> +		SAI6_MCLK_SEL,
> +		SAI7_MCLK_SEL,
> +		PDM_SEL,
> +		SAI_PLL_GNRL_CTL,
> +		SAI_PLL_FDIVL_CTL0,
> +		SAI_PLL_FDIVL_CTL1,
> +		SAI_PLL_SSCG_CTL,
> +		SAI_PLL_MNIT_CTL,
> +		IPG_LP_CTRL
> +	},
> +};
> +
> +struct clk_imx8mp_audiomix_drvdata {
> +	void __iomem *base;
> +	u32 regs_save[REGS_NUM];
> +};
> +
> +static void clk_imx8mp_audiomix_save_restore(struct device *dev, bool
> +save) {
> +	struct clk_imx8mp_audiomix_drvdata *drvdata =
> dev_get_drvdata(dev);
> +	void __iomem *base = drvdata->base;
> +	int i;
> +
> +	if (save) {
> +		for (i = 0; i < audiomix_regs.regs_num; i++)
> +			drvdata->regs_save[i] = readl(base +
> audiomix_regs.regs_off[i]);
> +	} else {
> +		for (i = 0; i < audiomix_regs.regs_num; i++)
> +			writel(drvdata->regs_save[i], base +
> audiomix_regs.regs_off[i]);
> +	}
> +}
> +
>  static int clk_imx8mp_audiomix_probe(struct platform_device *pdev)  {
> +	struct clk_imx8mp_audiomix_drvdata *drvdata;
>  	struct clk_hw_onecell_data *priv;
>  	struct device *dev = &pdev->dev;
>  	void __iomem *base;
>  	struct clk_hw *hw;
> -	int i;
> +	int i, ret;
> +
> +	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> +	if (!drvdata)
> +		return -ENOMEM;
> 
>  	priv = devm_kzalloc(dev,
>  			    struct_size(priv, hws,
> IMX8MP_CLK_AUDIOMIX_END), @@ -202,6 +263,9 @@ static int
> clk_imx8mp_audiomix_probe(struct platform_device *pdev)
>  	if (IS_ERR(base))
>  		return PTR_ERR(base);
> 
> +	drvdata->base = base;
> +	dev_set_drvdata(dev, drvdata);
> +
>  	for (i = 0; i < ARRAY_SIZE(sels); i++) {
>  		if (sels[i].num_parents == 1) {
>  			hw = devm_clk_hw_register_gate_parent_data(dev,
> @@ -257,10 +321,38 @@ static int clk_imx8mp_audiomix_probe(struct
> platform_device *pdev)
>  	if (IS_ERR(hw))
>  		return PTR_ERR(hw);
> 
> -	return devm_of_clk_add_hw_provider(&pdev->dev,
> of_clk_hw_onecell_get,
> -					   priv);
> +	ret = devm_of_clk_add_hw_provider(&pdev->dev,
> of_clk_hw_onecell_get,
> +					  priv);
> +	if (ret)
> +		return ret;
> +
> +	pm_runtime_enable(&pdev->dev);
> +	clk_imx8mp_audiomix_save_restore(&pdev->dev, true);
> +
> +	return 0;
>  }
> 
> +static int clk_imx8mp_audiomix_runtime_suspend(struct device *dev) {
> +	clk_imx8mp_audiomix_save_restore(dev, true);
> +
> +	return 0;
> +}
> +
> +static int clk_imx8mp_audiomix_runtime_resume(struct device *dev) {
> +	clk_imx8mp_audiomix_save_restore(dev, false);
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops clk_imx8mp_audiomix_pm_ops = {
> +	SET_RUNTIME_PM_OPS(clk_imx8mp_audiomix_runtime_suspend,
> +			   clk_imx8mp_audiomix_runtime_resume, NULL)
> +	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> +				      pm_runtime_force_resume)
> +};
> +
>  static const struct of_device_id clk_imx8mp_audiomix_of_match[] = {
>  	{ .compatible = "fsl,imx8mp-audio-blk-ctrl" },
>  	{ /* sentinel */ }
> @@ -272,6 +364,7 @@ static struct platform_driver
> clk_imx8mp_audiomix_driver = {
>  	.driver = {
>  		.name = "imx8mp-audio-blk-ctrl",
>  		.of_match_table = clk_imx8mp_audiomix_of_match,
> +		.pm = &clk_imx8mp_audiomix_pm_ops,
>  	},
>  };
> 
> --
> 2.34.1
Marco Felsch March 19, 2024, 7:35 a.m. UTC | #2
On 24-03-18, Peng Fan wrote:
> > Subject: [PATCH] clk: imx: imx8mp: Add pm_runtime support for power
> > saving
> > 
> > Add pm_runtime support for power saving. In pm runtime suspend state the
> > registers will be reseted, so add registers save in pm runtime suspend and
> > restore them in pm runtime resume.

We had similar patches in our downstream repo but didn't upstream yet
since there was an clk-handing issue. IIRC the issue was regarding the
global clock-prepare lock and a circular dependency on it. Is this
resolved now?

Regards,
  Marco

> > Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
> 
> Reviewed-by: Peng Fan <peng.fan@nxp.com>
> > ---
> >  drivers/clk/imx/clk-imx8mp-audiomix.c | 99 ++++++++++++++++++++++++++-
> >  1 file changed, 96 insertions(+), 3 deletions(-)
> > 
> > diff --git a/drivers/clk/imx/clk-imx8mp-audiomix.c b/drivers/clk/imx/clk-
> > imx8mp-audiomix.c
> > index 55ed211a5e0b..d2bf53e2aacf 100644
> > --- a/drivers/clk/imx/clk-imx8mp-audiomix.c
> > +++ b/drivers/clk/imx/clk-imx8mp-audiomix.c
> > @@ -7,10 +7,12 @@
> > 
> >  #include <linux/clk-provider.h>
> >  #include <linux/device.h>
> > +#include <linux/io.h>
> >  #include <linux/mod_devicetable.h>
> >  #include <linux/module.h>
> >  #include <linux/of.h>
> >  #include <linux/platform_device.h>
> > +#include <linux/pm_runtime.h>
> > 
> >  #include <dt-bindings/clock/imx8mp-clock.h>
> > 
> > @@ -18,6 +20,7 @@
> > 
> >  #define CLKEN0			0x000
> >  #define CLKEN1			0x004
> > +#define EARC			0x200
> >  #define SAI1_MCLK_SEL		0x300
> >  #define SAI2_MCLK_SEL		0x304
> >  #define SAI3_MCLK_SEL		0x308
> > @@ -26,6 +29,12 @@
> >  #define SAI7_MCLK_SEL		0x314
> >  #define PDM_SEL			0x318
> >  #define SAI_PLL_GNRL_CTL	0x400
> > +#define SAI_PLL_FDIVL_CTL0	0x404
> > +#define SAI_PLL_FDIVL_CTL1	0x408
> > +#define SAI_PLL_SSCG_CTL	0x40C
> > +#define SAI_PLL_MNIT_CTL	0x410
> > +#define IPG_LP_CTRL		0x504
> > +#define REGS_NUM		16
> > 
> >  #define SAIn_MCLK1_PARENT(n)						\
> >  static const struct clk_parent_data					\
> > @@ -182,13 +191,65 @@ static struct clk_imx8mp_audiomix_sel sels[] = {
> >  	CLK_SAIn(7)
> >  };
> > 
> > +struct clk_imx8mp_audiomix_regs {
> > +	u32 regs_num;
> > +	u32 regs_off[];
> > +};
> > +
> > +static const struct clk_imx8mp_audiomix_regs audiomix_regs = {
> > +	.regs_num = REGS_NUM,
> > +	.regs_off = {
> > +		CLKEN0,
> > +		CLKEN1,
> > +		EARC,
> > +		SAI1_MCLK_SEL,
> > +		SAI2_MCLK_SEL,
> > +		SAI3_MCLK_SEL,
> > +		SAI5_MCLK_SEL,
> > +		SAI6_MCLK_SEL,
> > +		SAI7_MCLK_SEL,
> > +		PDM_SEL,
> > +		SAI_PLL_GNRL_CTL,
> > +		SAI_PLL_FDIVL_CTL0,
> > +		SAI_PLL_FDIVL_CTL1,
> > +		SAI_PLL_SSCG_CTL,
> > +		SAI_PLL_MNIT_CTL,
> > +		IPG_LP_CTRL
> > +	},
> > +};
> > +
> > +struct clk_imx8mp_audiomix_drvdata {
> > +	void __iomem *base;
> > +	u32 regs_save[REGS_NUM];
> > +};
> > +
> > +static void clk_imx8mp_audiomix_save_restore(struct device *dev, bool
> > +save) {
> > +	struct clk_imx8mp_audiomix_drvdata *drvdata =
> > dev_get_drvdata(dev);
> > +	void __iomem *base = drvdata->base;
> > +	int i;
> > +
> > +	if (save) {
> > +		for (i = 0; i < audiomix_regs.regs_num; i++)
> > +			drvdata->regs_save[i] = readl(base +
> > audiomix_regs.regs_off[i]);
> > +	} else {
> > +		for (i = 0; i < audiomix_regs.regs_num; i++)
> > +			writel(drvdata->regs_save[i], base +
> > audiomix_regs.regs_off[i]);
> > +	}
> > +}
> > +
> >  static int clk_imx8mp_audiomix_probe(struct platform_device *pdev)  {
> > +	struct clk_imx8mp_audiomix_drvdata *drvdata;
> >  	struct clk_hw_onecell_data *priv;
> >  	struct device *dev = &pdev->dev;
> >  	void __iomem *base;
> >  	struct clk_hw *hw;
> > -	int i;
> > +	int i, ret;
> > +
> > +	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> > +	if (!drvdata)
> > +		return -ENOMEM;
> > 
> >  	priv = devm_kzalloc(dev,
> >  			    struct_size(priv, hws,
> > IMX8MP_CLK_AUDIOMIX_END), @@ -202,6 +263,9 @@ static int
> > clk_imx8mp_audiomix_probe(struct platform_device *pdev)
> >  	if (IS_ERR(base))
> >  		return PTR_ERR(base);
> > 
> > +	drvdata->base = base;
> > +	dev_set_drvdata(dev, drvdata);
> > +
> >  	for (i = 0; i < ARRAY_SIZE(sels); i++) {
> >  		if (sels[i].num_parents == 1) {
> >  			hw = devm_clk_hw_register_gate_parent_data(dev,
> > @@ -257,10 +321,38 @@ static int clk_imx8mp_audiomix_probe(struct
> > platform_device *pdev)
> >  	if (IS_ERR(hw))
> >  		return PTR_ERR(hw);
> > 
> > -	return devm_of_clk_add_hw_provider(&pdev->dev,
> > of_clk_hw_onecell_get,
> > -					   priv);
> > +	ret = devm_of_clk_add_hw_provider(&pdev->dev,
> > of_clk_hw_onecell_get,
> > +					  priv);
> > +	if (ret)
> > +		return ret;
> > +
> > +	pm_runtime_enable(&pdev->dev);
> > +	clk_imx8mp_audiomix_save_restore(&pdev->dev, true);
> > +
> > +	return 0;
> >  }
> > 
> > +static int clk_imx8mp_audiomix_runtime_suspend(struct device *dev) {
> > +	clk_imx8mp_audiomix_save_restore(dev, true);
> > +
> > +	return 0;
> > +}
> > +
> > +static int clk_imx8mp_audiomix_runtime_resume(struct device *dev) {
> > +	clk_imx8mp_audiomix_save_restore(dev, false);
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct dev_pm_ops clk_imx8mp_audiomix_pm_ops = {
> > +	SET_RUNTIME_PM_OPS(clk_imx8mp_audiomix_runtime_suspend,
> > +			   clk_imx8mp_audiomix_runtime_resume, NULL)
> > +	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> > +				      pm_runtime_force_resume)
> > +};
> > +
> >  static const struct of_device_id clk_imx8mp_audiomix_of_match[] = {
> >  	{ .compatible = "fsl,imx8mp-audio-blk-ctrl" },
> >  	{ /* sentinel */ }
> > @@ -272,6 +364,7 @@ static struct platform_driver
> > clk_imx8mp_audiomix_driver = {
> >  	.driver = {
> >  		.name = "imx8mp-audio-blk-ctrl",
> >  		.of_match_table = clk_imx8mp_audiomix_of_match,
> > +		.pm = &clk_imx8mp_audiomix_pm_ops,
> >  	},
> >  };
> > 
> > --
> > 2.34.1
> 
> 
>
Shengjiu Wang March 20, 2024, 2:11 a.m. UTC | #3
On Tue, Mar 19, 2024 at 3:35 PM Marco Felsch <m.felsch@pengutronix.de> wrote:
>
> On 24-03-18, Peng Fan wrote:
> > > Subject: [PATCH] clk: imx: imx8mp: Add pm_runtime support for power
> > > saving
> > >
> > > Add pm_runtime support for power saving. In pm runtime suspend state the
> > > registers will be reseted, so add registers save in pm runtime suspend and
> > > restore them in pm runtime resume.
>
> We had similar patches in our downstream repo but didn't upstream yet
> since there was an clk-handing issue. IIRC the issue was regarding the
> global clock-prepare lock and a circular dependency on it. Is this
> resolved now?

Seems I didn't meet the issue you mentioned.

But I found another issue with my implementation that the pm_runtime_enable
need to move before the clk registered, Otherwise the core->rpm_enabled
is not ture at clk registering.

I will update in next version.

Best regards
Shengjiu Wang
>
> Regards,
>   Marco
>
> > > Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
> >
> > Reviewed-by: Peng Fan <peng.fan@nxp.com>
> > > ---
> > >  drivers/clk/imx/clk-imx8mp-audiomix.c | 99 ++++++++++++++++++++++++++-
> > >  1 file changed, 96 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/drivers/clk/imx/clk-imx8mp-audiomix.c b/drivers/clk/imx/clk-
> > > imx8mp-audiomix.c
> > > index 55ed211a5e0b..d2bf53e2aacf 100644
> > > --- a/drivers/clk/imx/clk-imx8mp-audiomix.c
> > > +++ b/drivers/clk/imx/clk-imx8mp-audiomix.c
> > > @@ -7,10 +7,12 @@
> > >
> > >  #include <linux/clk-provider.h>
> > >  #include <linux/device.h>
> > > +#include <linux/io.h>
> > >  #include <linux/mod_devicetable.h>
> > >  #include <linux/module.h>
> > >  #include <linux/of.h>
> > >  #include <linux/platform_device.h>
> > > +#include <linux/pm_runtime.h>
> > >
> > >  #include <dt-bindings/clock/imx8mp-clock.h>
> > >
> > > @@ -18,6 +20,7 @@
> > >
> > >  #define CLKEN0                     0x000
> > >  #define CLKEN1                     0x004
> > > +#define EARC                       0x200
> > >  #define SAI1_MCLK_SEL              0x300
> > >  #define SAI2_MCLK_SEL              0x304
> > >  #define SAI3_MCLK_SEL              0x308
> > > @@ -26,6 +29,12 @@
> > >  #define SAI7_MCLK_SEL              0x314
> > >  #define PDM_SEL                    0x318
> > >  #define SAI_PLL_GNRL_CTL   0x400
> > > +#define SAI_PLL_FDIVL_CTL0 0x404
> > > +#define SAI_PLL_FDIVL_CTL1 0x408
> > > +#define SAI_PLL_SSCG_CTL   0x40C
> > > +#define SAI_PLL_MNIT_CTL   0x410
> > > +#define IPG_LP_CTRL                0x504
> > > +#define REGS_NUM           16
> > >
> > >  #define SAIn_MCLK1_PARENT(n)                                               \
> > >  static const struct clk_parent_data                                        \
> > > @@ -182,13 +191,65 @@ static struct clk_imx8mp_audiomix_sel sels[] = {
> > >     CLK_SAIn(7)
> > >  };
> > >
> > > +struct clk_imx8mp_audiomix_regs {
> > > +   u32 regs_num;
> > > +   u32 regs_off[];
> > > +};
> > > +
> > > +static const struct clk_imx8mp_audiomix_regs audiomix_regs = {
> > > +   .regs_num = REGS_NUM,
> > > +   .regs_off = {
> > > +           CLKEN0,
> > > +           CLKEN1,
> > > +           EARC,
> > > +           SAI1_MCLK_SEL,
> > > +           SAI2_MCLK_SEL,
> > > +           SAI3_MCLK_SEL,
> > > +           SAI5_MCLK_SEL,
> > > +           SAI6_MCLK_SEL,
> > > +           SAI7_MCLK_SEL,
> > > +           PDM_SEL,
> > > +           SAI_PLL_GNRL_CTL,
> > > +           SAI_PLL_FDIVL_CTL0,
> > > +           SAI_PLL_FDIVL_CTL1,
> > > +           SAI_PLL_SSCG_CTL,
> > > +           SAI_PLL_MNIT_CTL,
> > > +           IPG_LP_CTRL
> > > +   },
> > > +};
> > > +
> > > +struct clk_imx8mp_audiomix_drvdata {
> > > +   void __iomem *base;
> > > +   u32 regs_save[REGS_NUM];
> > > +};
> > > +
> > > +static void clk_imx8mp_audiomix_save_restore(struct device *dev, bool
> > > +save) {
> > > +   struct clk_imx8mp_audiomix_drvdata *drvdata =
> > > dev_get_drvdata(dev);
> > > +   void __iomem *base = drvdata->base;
> > > +   int i;
> > > +
> > > +   if (save) {
> > > +           for (i = 0; i < audiomix_regs.regs_num; i++)
> > > +                   drvdata->regs_save[i] = readl(base +
> > > audiomix_regs.regs_off[i]);
> > > +   } else {
> > > +           for (i = 0; i < audiomix_regs.regs_num; i++)
> > > +                   writel(drvdata->regs_save[i], base +
> > > audiomix_regs.regs_off[i]);
> > > +   }
> > > +}
> > > +
> > >  static int clk_imx8mp_audiomix_probe(struct platform_device *pdev)  {
> > > +   struct clk_imx8mp_audiomix_drvdata *drvdata;
> > >     struct clk_hw_onecell_data *priv;
> > >     struct device *dev = &pdev->dev;
> > >     void __iomem *base;
> > >     struct clk_hw *hw;
> > > -   int i;
> > > +   int i, ret;
> > > +
> > > +   drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> > > +   if (!drvdata)
> > > +           return -ENOMEM;
> > >
> > >     priv = devm_kzalloc(dev,
> > >                         struct_size(priv, hws,
> > > IMX8MP_CLK_AUDIOMIX_END), @@ -202,6 +263,9 @@ static int
> > > clk_imx8mp_audiomix_probe(struct platform_device *pdev)
> > >     if (IS_ERR(base))
> > >             return PTR_ERR(base);
> > >
> > > +   drvdata->base = base;
> > > +   dev_set_drvdata(dev, drvdata);
> > > +
> > >     for (i = 0; i < ARRAY_SIZE(sels); i++) {
> > >             if (sels[i].num_parents == 1) {
> > >                     hw = devm_clk_hw_register_gate_parent_data(dev,
> > > @@ -257,10 +321,38 @@ static int clk_imx8mp_audiomix_probe(struct
> > > platform_device *pdev)
> > >     if (IS_ERR(hw))
> > >             return PTR_ERR(hw);
> > >
> > > -   return devm_of_clk_add_hw_provider(&pdev->dev,
> > > of_clk_hw_onecell_get,
> > > -                                      priv);
> > > +   ret = devm_of_clk_add_hw_provider(&pdev->dev,
> > > of_clk_hw_onecell_get,
> > > +                                     priv);
> > > +   if (ret)
> > > +           return ret;
> > > +
> > > +   pm_runtime_enable(&pdev->dev);
> > > +   clk_imx8mp_audiomix_save_restore(&pdev->dev, true);
> > > +
> > > +   return 0;
> > >  }
> > >
> > > +static int clk_imx8mp_audiomix_runtime_suspend(struct device *dev) {
> > > +   clk_imx8mp_audiomix_save_restore(dev, true);
> > > +
> > > +   return 0;
> > > +}
> > > +
> > > +static int clk_imx8mp_audiomix_runtime_resume(struct device *dev) {
> > > +   clk_imx8mp_audiomix_save_restore(dev, false);
> > > +
> > > +   return 0;
> > > +}
> > > +
> > > +static const struct dev_pm_ops clk_imx8mp_audiomix_pm_ops = {
> > > +   SET_RUNTIME_PM_OPS(clk_imx8mp_audiomix_runtime_suspend,
> > > +                      clk_imx8mp_audiomix_runtime_resume, NULL)
> > > +   SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> > > +                                 pm_runtime_force_resume)
> > > +};
> > > +
> > >  static const struct of_device_id clk_imx8mp_audiomix_of_match[] = {
> > >     { .compatible = "fsl,imx8mp-audio-blk-ctrl" },
> > >     { /* sentinel */ }
> > > @@ -272,6 +364,7 @@ static struct platform_driver
> > > clk_imx8mp_audiomix_driver = {
> > >     .driver = {
> > >             .name = "imx8mp-audio-blk-ctrl",
> > >             .of_match_table = clk_imx8mp_audiomix_of_match,
> > > +           .pm = &clk_imx8mp_audiomix_pm_ops,
> > >     },
> > >  };
> > >
> > > --
> > > 2.34.1
> >
> >
> >
diff mbox series

Patch

diff --git a/drivers/clk/imx/clk-imx8mp-audiomix.c b/drivers/clk/imx/clk-imx8mp-audiomix.c
index 55ed211a5e0b..d2bf53e2aacf 100644
--- a/drivers/clk/imx/clk-imx8mp-audiomix.c
+++ b/drivers/clk/imx/clk-imx8mp-audiomix.c
@@ -7,10 +7,12 @@ 
 
 #include <linux/clk-provider.h>
 #include <linux/device.h>
+#include <linux/io.h>
 #include <linux/mod_devicetable.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 
 #include <dt-bindings/clock/imx8mp-clock.h>
 
@@ -18,6 +20,7 @@ 
 
 #define CLKEN0			0x000
 #define CLKEN1			0x004
+#define EARC			0x200
 #define SAI1_MCLK_SEL		0x300
 #define SAI2_MCLK_SEL		0x304
 #define SAI3_MCLK_SEL		0x308
@@ -26,6 +29,12 @@ 
 #define SAI7_MCLK_SEL		0x314
 #define PDM_SEL			0x318
 #define SAI_PLL_GNRL_CTL	0x400
+#define SAI_PLL_FDIVL_CTL0	0x404
+#define SAI_PLL_FDIVL_CTL1	0x408
+#define SAI_PLL_SSCG_CTL	0x40C
+#define SAI_PLL_MNIT_CTL	0x410
+#define IPG_LP_CTRL		0x504
+#define REGS_NUM		16
 
 #define SAIn_MCLK1_PARENT(n)						\
 static const struct clk_parent_data					\
@@ -182,13 +191,65 @@  static struct clk_imx8mp_audiomix_sel sels[] = {
 	CLK_SAIn(7)
 };
 
+struct clk_imx8mp_audiomix_regs {
+	u32 regs_num;
+	u32 regs_off[];
+};
+
+static const struct clk_imx8mp_audiomix_regs audiomix_regs = {
+	.regs_num = REGS_NUM,
+	.regs_off = {
+		CLKEN0,
+		CLKEN1,
+		EARC,
+		SAI1_MCLK_SEL,
+		SAI2_MCLK_SEL,
+		SAI3_MCLK_SEL,
+		SAI5_MCLK_SEL,
+		SAI6_MCLK_SEL,
+		SAI7_MCLK_SEL,
+		PDM_SEL,
+		SAI_PLL_GNRL_CTL,
+		SAI_PLL_FDIVL_CTL0,
+		SAI_PLL_FDIVL_CTL1,
+		SAI_PLL_SSCG_CTL,
+		SAI_PLL_MNIT_CTL,
+		IPG_LP_CTRL
+	},
+};
+
+struct clk_imx8mp_audiomix_drvdata {
+	void __iomem *base;
+	u32 regs_save[REGS_NUM];
+};
+
+static void clk_imx8mp_audiomix_save_restore(struct device *dev, bool save)
+{
+	struct clk_imx8mp_audiomix_drvdata *drvdata = dev_get_drvdata(dev);
+	void __iomem *base = drvdata->base;
+	int i;
+
+	if (save) {
+		for (i = 0; i < audiomix_regs.regs_num; i++)
+			drvdata->regs_save[i] = readl(base + audiomix_regs.regs_off[i]);
+	} else {
+		for (i = 0; i < audiomix_regs.regs_num; i++)
+			writel(drvdata->regs_save[i], base + audiomix_regs.regs_off[i]);
+	}
+}
+
 static int clk_imx8mp_audiomix_probe(struct platform_device *pdev)
 {
+	struct clk_imx8mp_audiomix_drvdata *drvdata;
 	struct clk_hw_onecell_data *priv;
 	struct device *dev = &pdev->dev;
 	void __iomem *base;
 	struct clk_hw *hw;
-	int i;
+	int i, ret;
+
+	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
 
 	priv = devm_kzalloc(dev,
 			    struct_size(priv, hws, IMX8MP_CLK_AUDIOMIX_END),
@@ -202,6 +263,9 @@  static int clk_imx8mp_audiomix_probe(struct platform_device *pdev)
 	if (IS_ERR(base))
 		return PTR_ERR(base);
 
+	drvdata->base = base;
+	dev_set_drvdata(dev, drvdata);
+
 	for (i = 0; i < ARRAY_SIZE(sels); i++) {
 		if (sels[i].num_parents == 1) {
 			hw = devm_clk_hw_register_gate_parent_data(dev,
@@ -257,10 +321,38 @@  static int clk_imx8mp_audiomix_probe(struct platform_device *pdev)
 	if (IS_ERR(hw))
 		return PTR_ERR(hw);
 
-	return devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
-					   priv);
+	ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
+					  priv);
+	if (ret)
+		return ret;
+
+	pm_runtime_enable(&pdev->dev);
+	clk_imx8mp_audiomix_save_restore(&pdev->dev, true);
+
+	return 0;
 }
 
+static int clk_imx8mp_audiomix_runtime_suspend(struct device *dev)
+{
+	clk_imx8mp_audiomix_save_restore(dev, true);
+
+	return 0;
+}
+
+static int clk_imx8mp_audiomix_runtime_resume(struct device *dev)
+{
+	clk_imx8mp_audiomix_save_restore(dev, false);
+
+	return 0;
+}
+
+static const struct dev_pm_ops clk_imx8mp_audiomix_pm_ops = {
+	SET_RUNTIME_PM_OPS(clk_imx8mp_audiomix_runtime_suspend,
+			   clk_imx8mp_audiomix_runtime_resume, NULL)
+	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				      pm_runtime_force_resume)
+};
+
 static const struct of_device_id clk_imx8mp_audiomix_of_match[] = {
 	{ .compatible = "fsl,imx8mp-audio-blk-ctrl" },
 	{ /* sentinel */ }
@@ -272,6 +364,7 @@  static struct platform_driver clk_imx8mp_audiomix_driver = {
 	.driver = {
 		.name = "imx8mp-audio-blk-ctrl",
 		.of_match_table = clk_imx8mp_audiomix_of_match,
+		.pm = &clk_imx8mp_audiomix_pm_ops,
 	},
 };