aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk
diff options
context:
space:
mode:
authorThomas Petazzoni <thomas.petazzoni@free-electrons.com>2014-11-21 11:00:05 -0500
committerJason Cooper <jason@lakedaemon.net>2014-11-30 11:40:12 -0500
commitf571053152f660769f9f39f150ac984bc4c6ac85 (patch)
treeeb85fc93b3d6fe931c35683524f42a5b07dc46ce /drivers/clk
parent4749c02b8da6d8dbc29218652985bda844017e95 (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.c32
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
187static struct clk_gating_ctrl *ctrl;
188
184static struct clk *clk_gating_get_src( 189static 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
206static int mvebu_clk_gating_suspend(void)
207{
208 ctrl->saved_reg = readl(ctrl->base);
209 return 0;
210}
211
212static void mvebu_clk_gating_resume(void)
213{
214 writel(ctrl->saved_reg, ctrl->base);
215}
216
217static struct syscore_ops clk_gate_syscore_ops = {
218 .suspend = mvebu_clk_gating_suspend,
219 .resume = mvebu_clk_gating_resume,
220};
221
202void __init mvebu_clk_gating_setup(struct device_node *np, 222void __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;
250gates_out: 278gates_out:
251 kfree(ctrl); 279 kfree(ctrl);