diff options
author | Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | 2014-11-21 17:00:05 +0100 |
---|---|---|
committer | Jason Cooper <jason@lakedaemon.net> | 2014-11-30 16:40:12 +0000 |
commit | f571053152f660769f9f39f150ac984bc4c6ac85 (patch) | |
tree | eb85fc93b3d6fe931c35683524f42a5b07dc46ce | |
parent | 4749c02b8da6d8dbc29218652985bda844017e95 (diff) |
clk: mvebu: add suspend/resume for gatable clocks
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 <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>
Link: https://lkml.kernel.org/r/1416585613-2113-9-git-send-email-thomas.petazzoni@free-electrons.com
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
-rw-r--r-- | drivers/clk/mvebu/common.c | 32 |
1 files changed, 30 insertions, 2 deletions
diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c index b7fcb469c87a..0d4d1216f2dd 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,17 @@ struct clk_gating_ctrl { spinlock_t *lock; struct clk **gates; int num_gates; + 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 +203,35 @@ 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); +} + +static struct syscore_ops clk_gate_syscore_ops = { + .suspend = mvebu_clk_gating_suspend, + .resume = mvebu_clk_gating_resume, +}; + 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 +249,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np, /* lock must already be initialized */ ctrl->lock = &ctrl_gating_lock; + ctrl->base = base; + /* Count, allocate, and register clock gates */ for (n = 0; desc[n].name;) n++; @@ -246,6 +272,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np, of_clk_add_provider(np, clk_gating_get_src, ctrl); + register_syscore_ops(&clk_gate_syscore_ops); + return; gates_out: kfree(ctrl); |