diff options
author | Thomas Petazzoni <thomas.petazzoni@free-electrons.com> | 2014-11-21 11:00:05 -0500 |
---|---|---|
committer | Jason Cooper <jason@lakedaemon.net> | 2014-11-30 11:40:12 -0500 |
commit | f571053152f660769f9f39f150ac984bc4c6ac85 (patch) | |
tree | eb85fc93b3d6fe931c35683524f42a5b07dc46ce /drivers/clk | |
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>
Diffstat (limited to 'drivers/clk')
-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 @@ | |||
19 | #include <linux/io.h> | 19 | #include <linux/io.h> |
20 | #include <linux/of.h> | 20 | #include <linux/of.h> |
21 | #include <linux/of_address.h> | 21 | #include <linux/of_address.h> |
22 | #include <linux/syscore_ops.h> | ||
22 | 23 | ||
23 | #include "common.h" | 24 | #include "common.h" |
24 | 25 | ||
@@ -177,14 +178,17 @@ struct clk_gating_ctrl { | |||
177 | spinlock_t *lock; | 178 | spinlock_t *lock; |
178 | struct clk **gates; | 179 | struct clk **gates; |
179 | int num_gates; | 180 | int num_gates; |
181 | void __iomem *base; | ||
182 | u32 saved_reg; | ||
180 | }; | 183 | }; |
181 | 184 | ||
182 | #define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) | 185 | #define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) |
183 | 186 | ||
187 | static struct clk_gating_ctrl *ctrl; | ||
188 | |||
184 | static struct clk *clk_gating_get_src( | 189 | static struct clk *clk_gating_get_src( |
185 | struct of_phandle_args *clkspec, void *data) | 190 | struct of_phandle_args *clkspec, void *data) |
186 | { | 191 | { |
187 | struct clk_gating_ctrl *ctrl = (struct clk_gating_ctrl *)data; | ||
188 | int n; | 192 | int n; |
189 | 193 | ||
190 | if (clkspec->args_count < 1) | 194 | if (clkspec->args_count < 1) |
@@ -199,15 +203,35 @@ static struct clk *clk_gating_get_src( | |||
199 | return ERR_PTR(-ENODEV); | 203 | return ERR_PTR(-ENODEV); |
200 | } | 204 | } |
201 | 205 | ||
206 | static int mvebu_clk_gating_suspend(void) | ||
207 | { | ||
208 | ctrl->saved_reg = readl(ctrl->base); | ||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | static void mvebu_clk_gating_resume(void) | ||
213 | { | ||
214 | writel(ctrl->saved_reg, ctrl->base); | ||
215 | } | ||
216 | |||
217 | static struct syscore_ops clk_gate_syscore_ops = { | ||
218 | .suspend = mvebu_clk_gating_suspend, | ||
219 | .resume = mvebu_clk_gating_resume, | ||
220 | }; | ||
221 | |||
202 | void __init mvebu_clk_gating_setup(struct device_node *np, | 222 | void __init mvebu_clk_gating_setup(struct device_node *np, |
203 | const struct clk_gating_soc_desc *desc) | 223 | const struct clk_gating_soc_desc *desc) |
204 | { | 224 | { |
205 | struct clk_gating_ctrl *ctrl; | ||
206 | struct clk *clk; | 225 | struct clk *clk; |
207 | void __iomem *base; | 226 | void __iomem *base; |
208 | const char *default_parent = NULL; | 227 | const char *default_parent = NULL; |
209 | int n; | 228 | int n; |
210 | 229 | ||
230 | if (ctrl) { | ||
231 | pr_err("mvebu-clk-gating: cannot instantiate more than one gatable clock device\n"); | ||
232 | return; | ||
233 | } | ||
234 | |||
211 | base = of_iomap(np, 0); | 235 | base = of_iomap(np, 0); |
212 | if (WARN_ON(!base)) | 236 | if (WARN_ON(!base)) |
213 | return; | 237 | return; |
@@ -225,6 +249,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np, | |||
225 | /* lock must already be initialized */ | 249 | /* lock must already be initialized */ |
226 | ctrl->lock = &ctrl_gating_lock; | 250 | ctrl->lock = &ctrl_gating_lock; |
227 | 251 | ||
252 | ctrl->base = base; | ||
253 | |||
228 | /* Count, allocate, and register clock gates */ | 254 | /* Count, allocate, and register clock gates */ |
229 | for (n = 0; desc[n].name;) | 255 | for (n = 0; desc[n].name;) |
230 | n++; | 256 | n++; |
@@ -246,6 +272,8 @@ void __init mvebu_clk_gating_setup(struct device_node *np, | |||
246 | 272 | ||
247 | of_clk_add_provider(np, clk_gating_get_src, ctrl); | 273 | of_clk_add_provider(np, clk_gating_get_src, ctrl); |
248 | 274 | ||
275 | register_syscore_ops(&clk_gate_syscore_ops); | ||
276 | |||
249 | return; | 277 | return; |
250 | gates_out: | 278 | gates_out: |
251 | kfree(ctrl); | 279 | kfree(ctrl); |