[PATCH 1/4] ARM: SAMSUNG SoC: Powerdomain/Block-gating Support
MyungJoo Ham
myungjoo.ham at samsung.com
Mon Jul 19 04:30:33 EDT 2010
Even when all the clocks of a domain are turned off, turning off the
power for the domain saves (leakage) current. Thus, we need to control
powerdomain accordingly to save even more power when we do clock gating.
Block-gating is a similar feature with powerdomain, but, it controls
"block", which is smaller than power-domain, and the saved current is
not so significant.
This patch enables powerdomain/block-gating support for Samsung SoC.
Signed-off-by: MyungJoo Ham <myungjoo.ham at samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park at samsung.com>
---
arch/arm/plat-samsung/Kconfig | 19 +++++++
arch/arm/plat-samsung/clock.c | 76 ++++++++++++++++++++++++++-
arch/arm/plat-samsung/include/plat/clock.h | 32 ++++++++++++
3 files changed, 124 insertions(+), 3 deletions(-)
diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig
index bd007e3..1fddb04 100644
--- a/arch/arm/plat-samsung/Kconfig
+++ b/arch/arm/plat-samsung/Kconfig
@@ -298,4 +298,23 @@ config SAMSUNG_WAKEMASK
and above. This code allows a set of interrupt to wakeup-mask
mappings. See <plat/wakeup-mask.h>
+config SAMSUNG_POWERDOMAIN
+ bool "Powerdomain Control Support"
+ depends on CPU_S5PV210
+ select S5PV210_POWERDOMAIN
+ help
+ Compile support for powerdomain controls. This code allows
+ enabling and diabling powerdomain according to its clocks,
+ which often saves significant amount of power.
+
+config SAMSUNG_BLOCKGATING
+ bool "Block Gating Control Support"
+ depends on SAMSUNG_POWERDOMAIN && CPU_S5PV210
+ select S5PV210_BLOCKGATING
+ help
+ Compile support for block gating controls. This code allows
+ enabling and diabling blocks according to its clocks.
+ However, at least in S5PV210, this is not as effective as
+ powerdomain control.
+
endif
diff --git a/arch/arm/plat-samsung/clock.c b/arch/arm/plat-samsung/clock.c
index 8bf79f3..f8a30f7 100644
--- a/arch/arm/plat-samsung/clock.c
+++ b/arch/arm/plat-samsung/clock.c
@@ -113,6 +113,10 @@ void clk_put(struct clk *clk)
int clk_enable(struct clk *clk)
{
+#if defined(CONFIG_SAMSUNG_BLOCKGATING) || defined(CONFIG_SAMSUNG_POWERDOMAIN)
+ struct clk *p;
+#endif
+
if (IS_ERR(clk) || clk == NULL)
return -EINVAL;
@@ -120,8 +124,38 @@ int clk_enable(struct clk *clk)
spin_lock(&clocks_lock);
- if ((clk->usage++) == 0)
- (clk->enable)(clk, 1);
+ if ((clk->usage++) == 0) {
+#ifdef CONFIG_SAMSUNG_BLOCKGATING
+ if (clk->bd && clk->bd->ref_count == 0) {
+ if (clk->bd->pd_set)
+ clk->bd->pd_set(clk->bd, 1);
+ clk->bd->ref_count++;
+ }
+#endif
+#ifdef CONFIG_SAMSUNG_POWERDOMAIN
+ if (clk->pd) {
+ if (clk->pd->ref_count == 0 &&
+ clk->pd->num_clks_boot_on <= 0) {
+ list_for_each_entry(p, &clk->pd->clocks,
+ powerdomain_list) {
+ if (p->enable)
+ p->enable(p, 1);
+ }
+
+ clk->pd->pd_set(clk->pd, 1);
+
+ list_for_each_entry(p, &clk->pd->clocks,
+ powerdomain_list) {
+ if (p->enable)
+ p->enable(p, 0);
+ }
+ }
+ clk->pd->ref_count++;
+ }
+#endif
+ if (clk->enable)
+ (clk->enable)(clk, 1);
+ }
spin_unlock(&clocks_lock);
return 0;
@@ -134,8 +168,29 @@ void clk_disable(struct clk *clk)
spin_lock(&clocks_lock);
- if ((--clk->usage) == 0)
+ if ((--clk->usage) == 0) {
(clk->enable)(clk, 0);
+#ifdef CONFIG_SAMSUNG_POWERDOMAIN
+ if (clk->pd) {
+ if (clk->pd->ref_count == 1 &&
+ clk->pd->num_clks_boot_on <= 0 &&
+ clk->pd->pd_set)
+ clk->pd->pd_set(clk->pd, 0);
+ if (clk->pd->ref_count > 0)
+ clk->pd->ref_count--;
+ }
+#endif
+#ifdef CONFIG_SAMSUNG_BLOCKGATING
+ if (clk->bd) {
+ if (clk->bd->ref_count == 1 &&
+ clk->bd->num_clks_boot_on <= 0 &&
+ clk->bd->pd_set)
+ clk->bd->pd_set(clk->bd, 0);
+ if (clk->bd->ref_count > 0)
+ clk->bd->ref_count--;
+ }
+#endif
+ }
spin_unlock(&clocks_lock);
clk_disable(clk->parent);
@@ -327,6 +382,21 @@ int s3c24xx_register_clock(struct clk *clk)
list_add(&clk->list, &clocks);
spin_unlock(&clocks_lock);
+#ifdef CONFIG_SAMSUNG_POWERDOMAIN
+ if (clk->pd) {
+ spin_lock(&clocks_lock);
+ list_add(&clk->powerdomain_list, &clk->pd->clocks);
+ spin_unlock(&clocks_lock);
+ }
+#endif
+#ifdef CONFIG_SAMSUNG_BLOCKGATING
+ if (clk->bd) {
+ spin_lock(&clocks_lock);
+ list_add(&clk->blockgating_list, &clk->bd->clocks);
+ spin_unlock(&clocks_lock);
+ }
+#endif
+
return 0;
}
diff --git a/arch/arm/plat-samsung/include/plat/clock.h b/arch/arm/plat-samsung/include/plat/clock.h
index 0fbcd0e..bf851e1 100644
--- a/arch/arm/plat-samsung/include/plat/clock.h
+++ b/arch/arm/plat-samsung/include/plat/clock.h
@@ -11,6 +11,30 @@
#include <linux/spinlock.h>
+#ifdef CONFIG_SAMSUNG_POWERDOMAIN
+struct register_save {
+ unsigned int addr;
+ unsigned int data;
+};
+
+struct powerdomain {
+ /* The number of clks that are never-disabled and "BOOT_ON" */
+ int num_clks_boot_on;
+ struct list_head clocks;
+
+ unsigned long *pd_reg;
+ unsigned long *pd_stable_reg;
+ unsigned long pd_ctrlbit;
+ unsigned long pd_stable_ctrlbit;
+ int ref_count;
+
+ int (*pd_set)(struct powerdomain *, int enable);
+ bool normal_ff_saved;
+ struct register_save *normal_ff;
+ unsigned int num_normal_ff;
+};
+#endif
+
struct clk;
/**
@@ -47,6 +71,14 @@ struct clk {
struct clk_ops *ops;
int (*enable)(struct clk *, int enable);
+#ifdef CONFIG_SAMSUNG_POWERDOMAIN
+ struct powerdomain *pd;
+ struct list_head powerdomain_list;
+#endif
+#ifdef CONFIG_SAMSUNG_BLOCKGATING
+ struct powerdomain *bd;
+ struct list_head blockgating_list;
+#endif
};
/* other clocks which may be registered by board support */
--
1.6.3.3
More information about the linux-arm-kernel
mailing list