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.
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 <[email protected]>
Cc: Mike Turquette <[email protected]>
Cc: [email protected]
Acked-by: Gregory CLEMENT <[email protected]>
---
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;
+ 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
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 <[email protected]>
> Cc: Mike Turquette <[email protected]>
> Cc: [email protected]
> Acked-by: Gregory CLEMENT <[email protected]>
> ---
> 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
--
Thomas Petazzoni, CTO, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com