diff mbox series

[V4,11/11] clk: imx: lpcg: add suspend/resume support

Message ID 1566299605-15641-12-git-send-email-aisheng.dong@nxp.com (mailing list archive)
State Changes Requested, archived
Headers show
Series clk: imx8: add new clock binding for better pm support | expand

Commit Message

Aisheng Dong Aug. 20, 2019, 11:13 a.m. UTC
LPCG clock state may be lost when it's power domain is completely
off during system suspend/resume and we need save and restore the
state properly.

Signed-off-by: Dong Aisheng <aisheng.dong@nxp.com>
---
ChangeLog:
v3: new patch
---
 drivers/clk/imx/clk-imx8qxp-lpcg.c |  1 +
 drivers/clk/imx/clk-lpcg-scu.c     | 33 +++++++++++++++++++++++++++++++++
 drivers/clk/imx/clk-scu.h          |  1 +
 3 files changed, 35 insertions(+)

Comments

Stephen Boyd Sept. 6, 2019, 5:14 p.m. UTC | #1
Quoting Dong Aisheng (2019-08-20 04:13:25)
> diff --git a/drivers/clk/imx/clk-lpcg-scu.c b/drivers/clk/imx/clk-lpcg-scu.c
> index 3c092a0..4df0818 100644
> --- a/drivers/clk/imx/clk-lpcg-scu.c
> +++ b/drivers/clk/imx/clk-lpcg-scu.c
> @@ -33,6 +33,9 @@ struct clk_lpcg_scu {
>         void __iomem *reg;
>         u8 bit_idx;
>         bool hw_gate;
> +
> +       /* for state save&restore */
> +       u32 state;
>  };
>  
>  #define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw)
> @@ -112,5 +115,35 @@ struct clk_hw *__imx_clk_lpcg_scu(struct device *dev, const char *name,
>                 hw = ERR_PTR(ret);
>         }
>  
> +       if (dev)
> +               dev_set_drvdata(dev, clk);
> +
>         return hw;
>  }
> +
> +int __maybe_unused imx_clk_lpcg_scu_suspend(struct device *dev)

static?

> +{
> +       struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
> +
> +       clk->state = readl_relaxed(clk->reg);
> +       dev_dbg(dev, "save lpcg state 0x%x\n", clk->state);
> +
> +       return 0;
> +}
> +
> +int __maybe_unused imx_clk_lpcg_scu_resume(struct device *dev)

static?

> +{
> +       struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
> +
> +       /* FIXME: double write in case a failure */
 
What does this mean? Sometimes writes don't work unless the CPU issues
them twice?

> +       writel(clk->state, clk->reg);
> +       writel(clk->state, clk->reg);
> +       dev_dbg(dev, "restore lpcg state 0x%x\n", clk->state);
> +
> +       return 0;
> +}
> +
> +const struct dev_pm_ops imx_clk_lpcg_scu_pm_ops = {
> +       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_lpcg_scu_suspend,
> +                                     imx_clk_lpcg_scu_resume)
> +};
Dong Aisheng Sept. 9, 2019, 11:39 a.m. UTC | #2
On Sat, Sep 7, 2019 at 9:22 PM Stephen Boyd <sboyd@kernel.org> wrote:
>
> Quoting Dong Aisheng (2019-08-20 04:13:25)
> > diff --git a/drivers/clk/imx/clk-lpcg-scu.c b/drivers/clk/imx/clk-lpcg-scu.c
> > index 3c092a0..4df0818 100644
> > --- a/drivers/clk/imx/clk-lpcg-scu.c
> > +++ b/drivers/clk/imx/clk-lpcg-scu.c
> > @@ -33,6 +33,9 @@ struct clk_lpcg_scu {
> >         void __iomem *reg;
> >         u8 bit_idx;
> >         bool hw_gate;
> > +
> > +       /* for state save&restore */
> > +       u32 state;
> >  };
> >
> >  #define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw)
> > @@ -112,5 +115,35 @@ struct clk_hw *__imx_clk_lpcg_scu(struct device *dev, const char *name,
> >                 hw = ERR_PTR(ret);
> >         }
> >
> > +       if (dev)
> > +               dev_set_drvdata(dev, clk);
> > +
> >         return hw;
> >  }
> > +
> > +int __maybe_unused imx_clk_lpcg_scu_suspend(struct device *dev)
>
> static?
>
> > +{
> > +       struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
> > +
> > +       clk->state = readl_relaxed(clk->reg);
> > +       dev_dbg(dev, "save lpcg state 0x%x\n", clk->state);
> > +
> > +       return 0;
> > +}
> > +
> > +int __maybe_unused imx_clk_lpcg_scu_resume(struct device *dev)
>
> static?
>

Will fix.

> > +{
> > +       struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
> > +
> > +       /* FIXME: double write in case a failure */
>
> What does this mean? Sometimes writes don't work unless the CPU issues
> them twice?
>

Yes, it's a hardware timing issues.

Regards
Aisheng

> > +       writel(clk->state, clk->reg);
> > +       writel(clk->state, clk->reg);
> > +       dev_dbg(dev, "restore lpcg state 0x%x\n", clk->state);
> > +
> > +       return 0;
> > +}
> > +
> > +const struct dev_pm_ops imx_clk_lpcg_scu_pm_ops = {
> > +       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_lpcg_scu_suspend,
> > +                                     imx_clk_lpcg_scu_resume)
> > +};
diff mbox series

Patch

diff --git a/drivers/clk/imx/clk-imx8qxp-lpcg.c b/drivers/clk/imx/clk-imx8qxp-lpcg.c
index ca9bd58..885498f 100644
--- a/drivers/clk/imx/clk-imx8qxp-lpcg.c
+++ b/drivers/clk/imx/clk-imx8qxp-lpcg.c
@@ -338,6 +338,7 @@  static struct platform_driver imx8qxp_lpcg_clk_driver = {
 	.driver = {
 		.name = "imx8qxp-lpcg-clk",
 		.of_match_table = imx8qxp_lpcg_match,
+		.pm = &imx_clk_lpcg_scu_pm_ops,
 		.suppress_bind_attrs = true,
 	},
 	.probe = imx8qxp_lpcg_clk_probe,
diff --git a/drivers/clk/imx/clk-lpcg-scu.c b/drivers/clk/imx/clk-lpcg-scu.c
index 3c092a0..4df0818 100644
--- a/drivers/clk/imx/clk-lpcg-scu.c
+++ b/drivers/clk/imx/clk-lpcg-scu.c
@@ -33,6 +33,9 @@  struct clk_lpcg_scu {
 	void __iomem *reg;
 	u8 bit_idx;
 	bool hw_gate;
+
+	/* for state save&restore */
+	u32 state;
 };
 
 #define to_clk_lpcg_scu(_hw) container_of(_hw, struct clk_lpcg_scu, hw)
@@ -112,5 +115,35 @@  struct clk_hw *__imx_clk_lpcg_scu(struct device *dev, const char *name,
 		hw = ERR_PTR(ret);
 	}
 
+	if (dev)
+		dev_set_drvdata(dev, clk);
+
 	return hw;
 }
+
+int __maybe_unused imx_clk_lpcg_scu_suspend(struct device *dev)
+{
+	struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
+
+	clk->state = readl_relaxed(clk->reg);
+	dev_dbg(dev, "save lpcg state 0x%x\n", clk->state);
+
+	return 0;
+}
+
+int __maybe_unused imx_clk_lpcg_scu_resume(struct device *dev)
+{
+	struct clk_lpcg_scu *clk = dev_get_drvdata(dev);
+
+	/* FIXME: double write in case a failure */
+	writel(clk->state, clk->reg);
+	writel(clk->state, clk->reg);
+	dev_dbg(dev, "restore lpcg state 0x%x\n", clk->state);
+
+	return 0;
+}
+
+const struct dev_pm_ops imx_clk_lpcg_scu_pm_ops = {
+	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(imx_clk_lpcg_scu_suspend,
+				      imx_clk_lpcg_scu_resume)
+};
diff --git a/drivers/clk/imx/clk-scu.h b/drivers/clk/imx/clk-scu.h
index 84efda3..6d4b6e2 100644
--- a/drivers/clk/imx/clk-scu.h
+++ b/drivers/clk/imx/clk-scu.h
@@ -12,6 +12,7 @@ 
 
 extern u32 clock_cells;
 extern struct list_head imx_scu_clks[];
+extern const struct dev_pm_ops imx_clk_lpcg_scu_pm_ops;
 
 int imx_clk_scu_init(struct device_node *np);
 struct clk_hw *imx_scu_of_clk_src_get(struct of_phandle_args *clkspec,