Message ID | 1415978496-9334-9-git-send-email-thomas.petazzoni@free-electrons.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Quoting Thomas Petazzoni (2014-11-14 07:21:28) > This commit adds suspend/resume support for the gatable clock driver > used on Marvell EBU platforms. When getting out of suspend, the > Marvell EBU platforms go through the bootloader, which re-enables all > gatable clocks. However, upon resume, the clock framework will not > disable again all gatable clocks that are not used. > > Therefore, if the clock driver does not save/restore the state of the > gatable clocks, all gatable clocks that are not claimed by any device > driver will remain enabled after a resume. This is why this driver > saves and restores the state of those clocks. It might be a good idea to call clk_disable_unused() from the clk core after resuming from suspend. > > Since clocks aren't real devices, we don't have the normal ->suspend() > and ->resume() of the device model, and have to use the ->suspend() > and ->resume() hooks of the syscore_ops mechanism. This mechanism has > the unfortunate idea of not providing a way of passing private data, > which requires us to change the driver to make the assumption that > there is only once instance of the gatable clock control structure. > > Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> > Cc: Mike Turquette <mturquette@linaro.org> > Cc: linux-kernel@vger.kernel.org > Acked-by: Gregory CLEMENT <gregory.clement@free-electrons.com> > --- > drivers/clk/mvebu/common.c | 30 ++++++++++++++++++++++++++++-- > 1 file changed, 28 insertions(+), 2 deletions(-) > > diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c > index b7fcb46..8799fb8 100644 > --- a/drivers/clk/mvebu/common.c > +++ b/drivers/clk/mvebu/common.c > @@ -19,6 +19,7 @@ > #include <linux/io.h> > #include <linux/of.h> > #include <linux/of_address.h> > +#include <linux/syscore_ops.h> > > #include "common.h" > > @@ -177,14 +178,18 @@ struct clk_gating_ctrl { > spinlock_t *lock; > struct clk **gates; > int num_gates; > + struct syscore_ops syscore_ops; You are registering suspend/resume ops per clock. Have you considered registering a single set of ops for your clock controller driver? See drivers/clk/samsung/clk-exynos5420.c for an example. Combined with a table of clocks registered by your driver, centralized suspend/resume methods might be a cleaner solution. Regards, Mike > + void __iomem *base; > + u32 saved_reg; > }; > > #define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) > > +static struct clk_gating_ctrl *ctrl; > + > static struct clk *clk_gating_get_src( > struct of_phandle_args *clkspec, void *data) > { > - struct clk_gating_ctrl *ctrl = (struct clk_gating_ctrl *)data; > int n; > > if (clkspec->args_count < 1) > @@ -199,15 +204,30 @@ static struct clk *clk_gating_get_src( > return ERR_PTR(-ENODEV); > } > > +static int mvebu_clk_gating_suspend(void) > +{ > + ctrl->saved_reg = readl(ctrl->base); > + return 0; > +} > + > +static void mvebu_clk_gating_resume(void) > +{ > + writel(ctrl->saved_reg, ctrl->base); > +} > + > void __init mvebu_clk_gating_setup(struct device_node *np, > const struct clk_gating_soc_desc *desc) > { > - struct clk_gating_ctrl *ctrl; > struct clk *clk; > void __iomem *base; > const char *default_parent = NULL; > int n; > > + if (ctrl) { > + pr_err("mvebu-clk-gating: cannot instantiate more than one gatable clock device\n"); > + return; > + } > + > base = of_iomap(np, 0); > if (WARN_ON(!base)) > return; > @@ -225,6 +245,10 @@ void __init mvebu_clk_gating_setup(struct device_node *np, > /* lock must already be initialized */ > ctrl->lock = &ctrl_gating_lock; > > + ctrl->base = base; > + ctrl->syscore_ops.suspend = mvebu_clk_gating_suspend; > + ctrl->syscore_ops.resume = mvebu_clk_gating_resume; > + > /* Count, allocate, and register clock gates */ > for (n = 0; desc[n].name;) > n++; > @@ -246,6 +270,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np, > > of_clk_add_provider(np, clk_gating_get_src, ctrl); > > + register_syscore_ops(&ctrl->syscore_ops); > + > return; > gates_out: > kfree(ctrl); > -- > 2.1.0 >
Dear Mike Turquette, On Mon, 17 Nov 2014 14:46:04 -0800, Mike Turquette wrote: > Quoting Thomas Petazzoni (2014-11-14 07:21:28) > > This commit adds suspend/resume support for the gatable clock driver > > used on Marvell EBU platforms. When getting out of suspend, the > > Marvell EBU platforms go through the bootloader, which re-enables all > > gatable clocks. However, upon resume, the clock framework will not > > disable again all gatable clocks that are not used. > > > > Therefore, if the clock driver does not save/restore the state of the > > gatable clocks, all gatable clocks that are not claimed by any device > > driver will remain enabled after a resume. This is why this driver > > saves and restores the state of those clocks. > > It might be a good idea to call clk_disable_unused() from the clk core > after resuming from suspend. Yes, this might be an interesting clk core improvement. > > @@ -177,14 +178,18 @@ struct clk_gating_ctrl { > > spinlock_t *lock; > > struct clk **gates; > > int num_gates; > > + struct syscore_ops syscore_ops; > > You are registering suspend/resume ops per clock. Have you considered > registering a single set of ops for your clock controller driver? See > drivers/clk/samsung/clk-exynos5420.c for an example. > > Combined with a table of clocks registered by your driver, centralized > suspend/resume methods might be a cleaner solution. Ok, I've changed. To be honest, I don't think it makes much change: if we had two instances of a gatable clock controller, then we would have two calls to mvebu_clk_gating_setup(), which would register twice the same syscore_ops. But we were anyway already assuming that we have already one instance of a gatable clock controller, since the syscore_ops operation implementation already used a global pointer to the gatable clock controller. Will be part of the upcoming v3. Thanks for the review! Thomas
diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c index b7fcb46..8799fb8 100644 --- a/drivers/clk/mvebu/common.c +++ b/drivers/clk/mvebu/common.c @@ -19,6 +19,7 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/of_address.h> +#include <linux/syscore_ops.h> #include "common.h" @@ -177,14 +178,18 @@ struct clk_gating_ctrl { spinlock_t *lock; struct clk **gates; int num_gates; + struct syscore_ops syscore_ops; + void __iomem *base; + u32 saved_reg; }; #define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) +static struct clk_gating_ctrl *ctrl; + static struct clk *clk_gating_get_src( struct of_phandle_args *clkspec, void *data) { - struct clk_gating_ctrl *ctrl = (struct clk_gating_ctrl *)data; int n; if (clkspec->args_count < 1) @@ -199,15 +204,30 @@ static struct clk *clk_gating_get_src( return ERR_PTR(-ENODEV); } +static int mvebu_clk_gating_suspend(void) +{ + ctrl->saved_reg = readl(ctrl->base); + return 0; +} + +static void mvebu_clk_gating_resume(void) +{ + writel(ctrl->saved_reg, ctrl->base); +} + void __init mvebu_clk_gating_setup(struct device_node *np, const struct clk_gating_soc_desc *desc) { - struct clk_gating_ctrl *ctrl; struct clk *clk; void __iomem *base; const char *default_parent = NULL; int n; + if (ctrl) { + pr_err("mvebu-clk-gating: cannot instantiate more than one gatable clock device\n"); + return; + } + base = of_iomap(np, 0); if (WARN_ON(!base)) return; @@ -225,6 +245,10 @@ void __init mvebu_clk_gating_setup(struct device_node *np, /* lock must already be initialized */ ctrl->lock = &ctrl_gating_lock; + ctrl->base = base; + ctrl->syscore_ops.suspend = mvebu_clk_gating_suspend; + ctrl->syscore_ops.resume = mvebu_clk_gating_resume; + /* Count, allocate, and register clock gates */ for (n = 0; desc[n].name;) n++; @@ -246,6 +270,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np, of_clk_add_provider(np, clk_gating_get_src, ctrl); + register_syscore_ops(&ctrl->syscore_ops); + return; gates_out: kfree(ctrl);