From 5d0c1dec07c65f8de13c9f60a0465060ec2e5e24 Mon Sep 17 00:00:00 2001
From: Francesco Virlinzi <francesco.virlinzi@st.com>
Date: Mon, 20 Apr 2009 14:26:58 +0200
Subject: [PATCH] sh: clkfwk: Add resume from standby support.
Add the resume from standby support to the clock framework.
Each clock in the flags field specifies how it has to be managed
during a standby operation.
Signed-off-by: Francesco Virlinzi <francesco.virlinzi@st.com>
---
arch/sh/include/asm/clock.h | 5 +++
arch/sh/kernel/cpu/clock.c | 76 +++++++++++++++++++++++++++++++++---------
2 files changed, 64 insertions(+), 17 deletions(-)
@@ -38,6 +38,11 @@ struct clk {
#define CLK_ALWAYS_ENABLED (1 << 0)
#define CLK_RATE_PROPAGATES (1 << 1)
+#define CLK_PM_MASK (0xff)
+#define CLK_PM_EXP_SHIFT (24)
+#define CLK_PM_RATIO_SHIFT (16)
+#define CLK_PM_TURNOFF (CLK_PM_MASK << CLK_PM_EXP_SHIFT)
+
/* Should be defined by processor-specific code */
void arch_init_clk_ops(struct clk_ops **, int type);
int __init arch_clk_init(void);
@@ -365,30 +365,72 @@ static int clks_sysdev_suspend(struct sys_device *dev, pm_message_t state)
{
static pm_message_t prev_state;
struct clk *clkp;
+ unsigned long rate, ratio;
switch (state.event) {
- case PM_EVENT_ON:
- /* Resumeing from hibernation */
- if (prev_state.event == PM_EVENT_FREEZE) {
- list_for_each_entry(clkp, &clock_list, node)
- if (likely(clkp->ops)) {
- unsigned long rate = clkp->rate;
-
- if (likely(clkp->ops->set_parent))
- clkp->ops->set_parent(clkp,
+ case PM_EVENT_ON: /* Resume from: */
+ switch (prev_state.event) {
+ case PM_EVENT_FREEZE: /* Hibernation */
+ list_for_each_entry(clkp, &clock_list, node) {
+ if (!clkp->ops)
+ continue;
+ rate = clkp->rate;
+
+ if (likely(clkp->ops->set_parent))
+ clkp->ops->set_parent(clkp,
clkp->parent);
- if (likely(clkp->ops->set_rate))
- clkp->ops->set_rate(clkp,
+ if (likely(clkp->ops->set_rate))
+ clkp->ops->set_rate(clkp,
rate, NO_CHANGE);
- else if (likely(clkp->ops->recalc))
- clkp->ops->recalc(clkp);
- }
- }
+ else if (likely(clkp->ops->recalc))
+ clkp->ops->recalc(clkp);
+ }
break;
- case PM_EVENT_FREEZE:
+ case PM_EVENT_SUSPEND: /* Suspend/Standby */
+ list_for_each_entry(clkp, &clock_list, node) {
+ if (!likely(clkp->ops))
+ continue;
+ /* turn-on */
+ if ((clkp->flags & CLK_PM_TURNOFF) == CLK_PM_TURNOFF &&
+ clkp->ops->enable)
+ clkp->ops->enable(clkp);
+ else
+ if (likely(clkp->ops->set_rate)) {
+ ratio = ((clkp->flags >> CLK_PM_RATIO_SHIFT) & CLK_PM_MASK)
+ << ((clkp->flags >> CLK_PM_EXP_SHIFT) & CLK_PM_MASK);
+ if (ratio)
+ clkp->ops->set_rate(clkp,
+ clkp->rate * ratio,
+ NO_CHANGE);
+ }
+ }
break;
+ }
+ break;
case PM_EVENT_SUSPEND:
- break;
+ /* reduces/turns-off the frequency based
+ * on the flags directive
+ */
+ list_for_each_entry_reverse(clkp, &clock_list, node) {
+ if (!clkp->ops)
+ continue;
+ /* turn-off */
+ if ((clkp->flags & CLK_PM_TURNOFF) == CLK_PM_TURNOFF &&
+ clkp->ops->disable)
+ clkp->ops->disable(clkp);
+ else /* reduce */
+ if (likely(clkp->ops->set_rate)) {
+ ratio = ((clkp->flags >> CLK_PM_RATIO_SHIFT) & CLK_PM_MASK)
+ << ((clkp->flags >> CLK_PM_EXP_SHIFT) & CLK_PM_MASK);
+ if (ratio)
+ clkp->ops->set_rate(clkp,
+ clkp->rate / ratio,
+ NO_CHANGE);
+ }
+ }
+ break;
+ case PM_EVENT_FREEZE:
+ break;
}
prev_state = state;
--
1.6.0.6