summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2011-10-13 19:12:55 -0700
committerVarun Colbert <vcolbert@nvidia.com>2011-11-15 11:52:05 -0800
commitb30bf0b313131037baffed7b6467eb1e0f021d19 (patch)
tree85dac8ff3cd087ebbff2f036838316fa6413e004
parenta213668b4f54b8ea7603a6d1e71f8b4ab1998bf7 (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.h7
-rw-r--r--arch/arm/mach-tegra/tegra3_clocks.c67
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
/*