msm: clock-8960: Set memory retention modes with clk_set_flags()
Some branch clocks have a configuration bit associated with them
that determines whether or not SRAM memories in their clock domain
will lose or retain state when the clock is halted.
By default (out of reset) these bits are set so that, even when the
clocks are halted, memory state is retained. This is useful for
drivers that wish to take advantage of temporary clock gating as an
inexpensive way to save power, without needing to re-initialize the
hardware they control again after a clock is re-enabled.
On the other hand, some additional power can be saved by clearing
these bits and allowing the core's memory to collapse. To take
advantage of this, a means of setting this bit is supported through
the clk_set_flags() API.
Change-Id: I80bf17b721b36861e1de3a0267be1788982c57e7
Signed-off-by: Matt Wagantall <mattw@codeaurora.org>
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index ff81923..eb3cb13 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -596,6 +596,7 @@
.reset = rcg_clk_reset,
.is_local = local_clk_is_local,
.get_parent = rcg_clk_get_parent,
+ .set_flags = rcg_clk_set_flags,
};
static struct clk_ops clk_ops_branch = {
@@ -611,6 +612,7 @@
.get_parent = branch_clk_get_parent,
.set_parent = branch_clk_set_parent,
.handoff = branch_clk_handoff,
+ .set_flags = branch_clk_set_flags,
};
static struct clk_ops clk_ops_reset = {
@@ -625,6 +627,8 @@
.en_mask = BIT(24),
.halt_reg = DBG_BUS_VEC_E_REG,
.halt_bit = 6,
+ .retain_reg = MAXI_EN2_REG,
+ .retain_mask = BIT(21),
},
.c = {
.dbg_name = "gmem_axi_clk",
@@ -661,6 +665,8 @@
.reset_mask = BIT(10),
.halt_reg = DBG_BUS_VEC_E_REG,
.halt_bit = 7,
+ .retain_reg = MAXI_EN2_REG,
+ .retain_mask = BIT(10),
},
.c = {
.dbg_name = "imem_axi_clk",
@@ -691,6 +697,8 @@
.hwcg_mask = BIT(22),
.halt_reg = DBG_BUS_VEC_I_REG,
.halt_bit = 25,
+ .retain_reg = MAXI_EN4_REG,
+ .retain_mask = BIT(21),
},
.c = {
.dbg_name = "vcodec_axi_b_clk",
@@ -707,6 +715,8 @@
.hwcg_mask = BIT(24),
.halt_reg = DBG_BUS_VEC_I_REG,
.halt_bit = 26,
+ .retain_reg = MAXI_EN4_REG,
+ .retain_mask = BIT(10),
},
.c = {
.dbg_name = "vcodec_axi_a_clk",
@@ -726,6 +736,8 @@
.reset_mask = BIT(4)|BIT(5)|BIT(7),
.halt_reg = DBG_BUS_VEC_E_REG,
.halt_bit = 3,
+ .retain_reg = MAXI_EN2_REG,
+ .retain_mask = BIT(28),
},
.c = {
.dbg_name = "vcodec_axi_clk",
@@ -761,6 +773,8 @@
.reset_mask = BIT(13),
.halt_reg = DBG_BUS_VEC_E_REG,
.halt_bit = 8,
+ .retain_reg = MAXI_EN_REG,
+ .retain_mask = BIT(0),
},
.c = {
.dbg_name = "mdp_axi_clk",
@@ -779,6 +793,8 @@
.reset_mask = BIT(6),
.halt_reg = DBG_BUS_VEC_E_REG,
.halt_bit = 2,
+ .retain_reg = MAXI_EN3_REG,
+ .retain_mask = BIT(10),
},
.c = {
.dbg_name = "rot_axi_clk",
@@ -797,6 +813,9 @@
.reset_mask = BIT(15),
.halt_reg = DBG_BUS_VEC_E_REG,
.halt_bit = 1,
+ .retain_reg = MAXI_EN3_REG,
+ .retain_mask = BIT(21),
+
},
.c = {
.dbg_name = "vpe_axi_clk",
@@ -1167,6 +1186,8 @@
.reset_mask = BIT(0),
.halt_reg = DBG_BUS_VEC_F_REG,
.halt_bit = 14,
+ .retain_reg = AHB_EN2_REG,
+ .retain_mask = BIT(0),
},
.c = {
.dbg_name = "vfe_p_clk",
@@ -3082,6 +3103,8 @@
.reset_mask = BIT(7),
.halt_reg = DBG_BUS_VEC_B_REG,
.halt_bit = 21,
+ .retain_reg = DSI1_BYTE_CC_REG,
+ .retain_mask = BIT(31),
},
.ns_reg = DSI1_BYTE_NS_REG,
.root_en_mask = BIT(2),
@@ -3104,6 +3127,8 @@
.reset_mask = BIT(25),
.halt_reg = DBG_BUS_VEC_B_REG,
.halt_bit = 20,
+ .retain_reg = DSI2_BYTE_CC_REG,
+ .retain_mask = BIT(31),
},
.ns_reg = DSI2_BYTE_NS_REG,
.root_en_mask = BIT(2),
@@ -3211,6 +3236,8 @@
.reset_mask = BIT(14),
.halt_reg = DBG_BUS_VEC_A_REG,
.halt_bit = 9,
+ .retain_reg = GFX2D0_CC_REG,
+ .retain_mask = BIT(31),
},
.ns_reg = GFX2D0_NS_REG,
.root_en_mask = BIT(2),
@@ -3253,6 +3280,8 @@
.reset_mask = BIT(13),
.halt_reg = DBG_BUS_VEC_A_REG,
.halt_bit = 14,
+ .retain_reg = GFX2D1_CC_REG,
+ .retain_mask = BIT(31),
},
.ns_reg = GFX2D1_NS_REG,
.root_en_mask = BIT(2),
@@ -3377,6 +3406,8 @@
.reset_mask = BIT(12),
.halt_reg = DBG_BUS_VEC_A_REG,
.halt_bit = 4,
+ .retain_reg = GFX3D_CC_REG,
+ .retain_mask = BIT(31),
},
.ns_reg = GFX3D_NS_REG,
.root_en_mask = BIT(2),
@@ -3517,6 +3548,8 @@
.reset_mask = BIT(9),
.halt_reg = DBG_BUS_VEC_A_REG,
.halt_bit = 24,
+ .retain_reg = IJPEG_CC_REG,
+ .retain_mask = BIT(31),
},
.ns_reg = IJPEG_NS_REG,
.md_reg = IJPEG_MD_REG,
@@ -3559,6 +3592,8 @@
.reset_mask = BIT(19),
.halt_reg = DBG_BUS_VEC_A_REG,
.halt_bit = 19,
+ .retain_reg = JPEGD_CC_REG,
+ .retain_mask = BIT(31),
},
.ns_reg = JPEGD_NS_REG,
.root_en_mask = BIT(2),
@@ -3635,6 +3670,8 @@
.reset_mask = BIT(21),
.halt_reg = DBG_BUS_VEC_C_REG,
.halt_bit = 10,
+ .retain_reg = MDP_CC_REG,
+ .retain_mask = BIT(31),
},
.ns_reg = MDP_NS_REG,
.root_en_mask = BIT(2),
@@ -3657,6 +3694,8 @@
.en_mask = BIT(0),
.halt_reg = DBG_BUS_VEC_I_REG,
.halt_bit = 13,
+ .retain_reg = MDP_LUT_CC_REG,
+ .retain_mask = BIT(31),
},
.parent = &mdp_clk.c,
.c = {
@@ -3743,6 +3782,8 @@
.reset_mask = BIT(2),
.halt_reg = DBG_BUS_VEC_C_REG,
.halt_bit = 15,
+ .retain_reg = ROT_CC_REG,
+ .retain_mask = BIT(31),
},
.ns_reg = ROT_NS_REG,
.root_en_mask = BIT(2),
@@ -3853,6 +3894,8 @@
.b = {
.ctl_reg = TV_CC_REG,
.halt_check = NOCHECK,
+ .retain_reg = TV_CC_REG,
+ .retain_mask = BIT(31),
},
.md_reg = TV_MD_REG,
.root_en_mask = BIT(2),
@@ -3909,6 +3952,8 @@
.reset_mask = BIT(4),
.halt_reg = DBG_BUS_VEC_D_REG,
.halt_bit = 12,
+ .retain_reg = TV_CC2_REG,
+ .retain_mask = BIT(10),
},
.parent = &tv_src_clk.c,
.c = {
@@ -3998,6 +4043,8 @@
.reset_mask = BIT(6),
.halt_reg = DBG_BUS_VEC_C_REG,
.halt_bit = 29,
+ .retain_reg = VCODEC_CC_REG,
+ .retain_mask = BIT(31),
},
.ns_reg = VCODEC_NS_REG,
.root_en_mask = BIT(2),
@@ -4042,6 +4089,8 @@
.reset_mask = BIT(17),
.halt_reg = DBG_BUS_VEC_A_REG,
.halt_bit = 28,
+ .retain_reg = VPE_CC_REG,
+ .retain_mask = BIT(31),
},
.ns_reg = VPE_NS_REG,
.root_en_mask = BIT(2),
@@ -4110,6 +4159,8 @@
.halt_reg = DBG_BUS_VEC_B_REG,
.halt_bit = 6,
.en_mask = BIT(0),
+ .retain_reg = VFE_CC2_REG,
+ .retain_mask = BIT(31),
},
.ns_reg = VFE_NS_REG,
.md_reg = VFE_MD_REG,
diff --git a/arch/arm/mach-msm/clock-local.c b/arch/arm/mach-msm/clock-local.c
index 1bb9c86..c02c6f3 100644
--- a/arch/arm/mach-msm/clock-local.c
+++ b/arch/arm/mach-msm/clock-local.c
@@ -924,6 +924,38 @@
branch_disable_hwcg(&branch->b);
}
+static int branch_set_flags(struct branch *b, unsigned flags)
+{
+ unsigned long irq_flags;
+ u32 reg_val;
+ int ret = 0;
+
+ if (!b->retain_reg)
+ return -EPERM;
+
+ spin_lock_irqsave(&local_clock_reg_lock, irq_flags);
+ reg_val = readl_relaxed(b->retain_reg);
+ switch (flags) {
+ case CLKFLAG_RETAIN:
+ reg_val |= b->retain_mask;
+ break;
+ case CLKFLAG_NORETAIN:
+ reg_val &= ~b->retain_mask;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ writel_relaxed(reg_val, b->retain_reg);
+ spin_unlock_irqrestore(&local_clock_reg_lock, irq_flags);
+
+ return ret;
+}
+
+int branch_clk_set_flags(struct clk *clk, unsigned flags)
+{
+ return branch_set_flags(&to_branch_clk(clk)->b, flags);
+}
+
int branch_clk_in_hwcg_mode(struct clk *c)
{
struct branch_clk *clk = to_branch_clk(c);
@@ -948,6 +980,11 @@
return branch_in_hwcg_mode(&clk->b);
}
+int rcg_clk_set_flags(struct clk *clk, unsigned flags)
+{
+ return branch_set_flags(&to_rcg_clk(clk)->b, flags);
+}
+
int branch_reset(struct branch *b, enum clk_reset_action action)
{
int ret = 0;
diff --git a/arch/arm/mach-msm/clock-local.h b/arch/arm/mach-msm/clock-local.h
index 9292e5d..8ea344b 100644
--- a/arch/arm/mach-msm/clock-local.h
+++ b/arch/arm/mach-msm/clock-local.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -164,12 +164,16 @@
void __iomem *const reset_reg;
const u32 reset_mask;
+
+ void __iomem *const retain_reg;
+ const u32 retain_mask;
};
int branch_reset(struct branch *b, enum clk_reset_action action);
void __branch_clk_enable_reg(const struct branch *clk, const char *name);
u32 __branch_clk_disable_reg(const struct branch *clk, const char *name);
int branch_clk_handoff(struct clk *c);
+int branch_clk_set_flags(struct clk *clk, unsigned flags);
/*
* Generic clock-definition struct and macros
@@ -213,6 +217,7 @@
void rcg_clk_enable_hwcg(struct clk *clk);
void rcg_clk_disable_hwcg(struct clk *clk);
int rcg_clk_in_hwcg_mode(struct clk *c);
+int rcg_clk_set_flags(struct clk *clk, unsigned flags);
/**
* struct cdiv_clk - integer divider clock with external source selection
diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h
index 5a9b199..4210bd9 100644
--- a/arch/arm/mach-msm/clock.h
+++ b/arch/arm/mach-msm/clock.h
@@ -30,6 +30,8 @@
#define CLKFLAG_NORESET 0x00000008
#define CLKFLAG_HANDOFF_RATE 0x00000010
#define CLKFLAG_HWCG 0x00000020
+#define CLKFLAG_RETAIN 0x00000040
+#define CLKFLAG_NORETAIN 0x00000080
#define CLKFLAG_SKIP_AUTO_OFF 0x00000200
#define CLKFLAG_MIN 0x00000400
#define CLKFLAG_MAX 0x00000800