diff options
author | Alex Frid <afrid@nvidia.com> | 2011-10-13 19:12:55 -0700 |
---|---|---|
committer | Varun Colbert <vcolbert@nvidia.com> | 2011-11-15 11:52:05 -0800 |
commit | b30bf0b313131037baffed7b6467eb1e0f021d19 (patch) | |
tree | 85dac8ff3cd087ebbff2f036838316fa6413e004 | |
parent | a213668b4f54b8ea7603a6d1e71f8b4ab1998bf7 (diff) |
ARM: tegra: clock: Add Tegra3 emergency throttling
Add Tegra3 emergency throttling API to directly control G-CPU super
clock skipper underneath clock framework, dvfs, and cpufreq driver
s/w layers. To be used by system power supply over-current ISR.
(cherry picked from commit fca2a12e90684526b2b7aeeb3af31de4254ad939)
Change-Id: I8de8f4889d0f6cf6a7cc19a3cc11c6bd9b4fc526
Signed-off-by: Alex Frid <afrid@nvidia.com>
Reviewed-on: http://git-master/r/63976
Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/include/mach/edp.h | 7 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra3_clocks.c | 67 |
2 files changed, 68 insertions, 6 deletions
diff --git a/arch/arm/mach-tegra/include/mach/edp.h b/arch/arm/mach-tegra/include/mach/edp.h index 8cba6da8b1e9..92d2e4f3f196 100644 --- a/arch/arm/mach-tegra/include/mach/edp.h +++ b/arch/arm/mach-tegra/include/mach/edp.h @@ -56,4 +56,11 @@ static inline unsigned int tegra_get_edp_limit(void) { return -1; } #endif +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +static inline void tegra_edp_throttle_cpu_now(u8 factor) +{} +#else +void tegra_edp_throttle_cpu_now(u8 factor); +#endif + #endif /* __MACH_EDP_H */ diff --git a/arch/arm/mach-tegra/tegra3_clocks.c b/arch/arm/mach-tegra/tegra3_clocks.c index ef9dc1c29f1b..ee2d25d8dc41 100644 --- a/arch/arm/mach-tegra/tegra3_clocks.c +++ b/arch/arm/mach-tegra/tegra3_clocks.c @@ -206,10 +206,14 @@ #define SUPER_IDLE_SOURCE_SHIFT 0 #define SUPER_CLK_DIVIDER 0x04 +#define SUPER_CLOCK_SKIP_ENABLE (0x1 << 31) #define SUPER_CLOCK_DIV_U71_SHIFT 16 #define SUPER_CLOCK_DIV_U71_MASK (0xff << SUPER_CLOCK_DIV_U71_SHIFT) /* guarantees safe cpu backup */ #define SUPER_CLOCK_DIV_U71_MIN 0x2 +#define SUPER_CLOCK_SKIP_NOMIN_SHIFT 8 +#define SUPER_CLOCK_SKIP_DENOM_SHIFT 0 +#define SUPER_CLOCK_SKIP_MASK (0xffff << SUPER_CLOCK_SKIP_DENOM_SHIFT) #define BUS_CLK_DISABLE (1<<3) #define BUS_CLK_DIV_MASK 0x3 @@ -567,6 +571,7 @@ static void tegra3_super_clk_init(struct clk *c) int source; int shift; const struct clk_mux_sel *sel; + val = clk_readl(c->reg + SUPER_CLK_MUX); c->state = ON; BUG_ON(((val & SUPER_STATE_MASK) != SUPER_STATE_RUN) && @@ -584,9 +589,12 @@ static void tegra3_super_clk_init(struct clk *c) c->parent = sel->input; if (c->flags & DIV_U71) { - /* Init safe 7.1 divider value (does not affect PLLX path) */ - clk_writel(SUPER_CLOCK_DIV_U71_MIN << SUPER_CLOCK_DIV_U71_SHIFT, - c->reg + SUPER_CLK_DIVIDER); + /* Init safe 7.1 divider value (does not affect PLLX path). + Super skipper is enabled to be ready for emergency throttle, + but set 1:1 */ + val = SUPER_CLOCK_SKIP_ENABLE | + (SUPER_CLOCK_DIV_U71_MIN << SUPER_CLOCK_DIV_U71_SHIFT); + clk_writel(val, c->reg + SUPER_CLK_DIVIDER); c->mul = 2; c->div = 2; if (!(c->parent->flags & PLLX)) @@ -663,6 +671,36 @@ static int tegra3_super_clk_set_parent(struct clk *c, struct clk *p) return -EINVAL; } +static DEFINE_SPINLOCK(super_divider_lock); + +static void tegra3_super_clk_divider_update(struct clk *c, u8 div) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&super_divider_lock, flags); + val = clk_readl(c->reg + SUPER_CLK_DIVIDER); + val &= ~SUPER_CLOCK_DIV_U71_MASK; + val |= div << SUPER_CLOCK_DIV_U71_SHIFT; + clk_writel(val, c->reg + SUPER_CLK_DIVIDER); + spin_unlock_irqrestore(&super_divider_lock, flags); + udelay(2); +} + +static void tegra3_super_clk_skipper_update(struct clk *c, u8 nomin, u8 denom) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&super_divider_lock, flags); + val = clk_readl(c->reg + SUPER_CLK_DIVIDER); + val &= ~SUPER_CLOCK_SKIP_MASK; + val |= (nomin << SUPER_CLOCK_SKIP_NOMIN_SHIFT) | + (denom << SUPER_CLOCK_SKIP_DENOM_SHIFT); + clk_writel(val, c->reg + SUPER_CLK_DIVIDER); + spin_unlock_irqrestore(&super_divider_lock, flags); +} + /* * Do not use super clocks "skippers", since dividing using a clock skipper * does not allow the voltage to be scaled down. Instead adjust the rate of @@ -678,9 +716,7 @@ static int tegra3_super_clk_set_rate(struct clk *c, unsigned long rate) int div = clk_div71_get_divider(c->parent->u.pll.fixed_rate, rate, c->flags, ROUND_DIVIDER_DOWN); div = max(div, SUPER_CLOCK_DIV_U71_MIN); - - clk_writel(div << SUPER_CLOCK_DIV_U71_SHIFT, - c->reg + SUPER_CLK_DIVIDER); + tegra3_super_clk_divider_update(c, div); c->div = div + 2; c->mul = 2; return 0; @@ -4144,6 +4180,25 @@ void __init tegra_soc_init_clocks(void) tegra_init_cpu_edp_limits(0); } +/* + * Emergency throttle of G-CPU by setting G-super clock skipper underneath + * clock framework, dvfs, and cpufreq driver s/w layers. Can be called in + * ISR context for EDP events. When releasing throttle, LP-divider is cleared + * just in case it was set as a result of save/restore operations across + * cluster switch (should not happen) + */ +void tegra_edp_throttle_cpu_now(u8 factor) +{ + if (factor > 1) { + if (!is_lp_cluster()) + tegra3_super_clk_skipper_update( + &tegra_clk_cclk_g, 0, factor - 1); + } else { + tegra3_super_clk_skipper_update(&tegra_clk_cclk_g, 0, 0); + tegra3_super_clk_skipper_update(&tegra_clk_cclk_lp, 0, 0); + } +} + #ifdef CONFIG_CPU_FREQ /* |