summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorAlex Frid <afrid@nvidia.com>2012-06-23 23:50:54 -0700
committerRohan Somvanshi <rsomvanshi@nvidia.com>2012-07-13 05:48:41 -0700
commitc9ce3ff77d029d99ddae5577b8358258feff08a0 (patch)
tree536d68b03b7961e53813b4b93147f8562df3fd53 /arch
parent3eeffbc3d34d05b668234306a7de2e157db9b8db (diff)
ARM: tegra: clock: Allow Tegra3 PLLM rate change
Allowed Tegra3 memory PLLM rate change, provided it is disabled. Since PLLM can deviate from boot configuration now, and on Tegra3 it is controlled by PMC override registers (not CAR module registers): - Re-factored PLLM initialization, resume, and set rate operations accordingly (enable and disable ops already used PMC override). - Made sure that boot configuration is restored on entry to LP0 to match memory timing saved in scratch registers. Bug 1005576 Change-Id: Iac6297455bec709a8e12d71deccab62c18905ea7 Signed-off-by: Alex Frid <afrid@nvidia.com> Reviewed-on: http://git-master/r/110937 Reviewed-by: Rohan Somvanshi <rsomvanshi@nvidia.com> Tested-by: Rohan Somvanshi <rsomvanshi@nvidia.com> (cherry picked from commit b53f88c68543a2b0ddb4545bb3b389b42eeb95d8) Reviewed-on: http://git-master/r/114759 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Jihoon Bang <jbang@nvidia.com> Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-tegra/sleep-t3.S5
-rw-r--r--arch/arm/mach-tegra/sleep.h2
-rw-r--r--arch/arm/mach-tegra/tegra3_clocks.c66
3 files changed, 71 insertions, 2 deletions
diff --git a/arch/arm/mach-tegra/sleep-t3.S b/arch/arm/mach-tegra/sleep-t3.S
index 4417da33de38..23e96c605b96 100644
--- a/arch/arm/mach-tegra/sleep-t3.S
+++ b/arch/arm/mach-tegra/sleep-t3.S
@@ -92,6 +92,7 @@
#define CLK_RESET_PLLP_OUTB 0xa8
#define PMC_PLLP_WB0_OVERRIDE 0xf8
+#define PMC_PLLM_WB0_OVERRIDE 0x1dc
#define CLK_RESET_CLK_SOURCE_MSELECT 0x3b4
@@ -517,10 +518,12 @@ tegra3_cpu_clk32k:
tst r0, #PMC_CTRL_SIDE_EFFECT_LP0
beq lp1_clocks_prepare
- /* enable PLLM via PMC in LP0 */
+ /* enable PLLM auto-restart via PMC in LP0; restore override settings */
ldr r0, [r4, #PMC_PLLP_WB0_OVERRIDE]
orr r0, r0, #((1 << 12) | (1 << 11))
str r0, [r4, #PMC_PLLP_WB0_OVERRIDE]
+ ldr r0, [r4, #PMC_SCRATCH2]
+ str r0, [r4, #PMC_PLLM_WB0_OVERRIDE]
mov pc, lr
/* start by jumping to clkm to safely disable PLLs, then jump
diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h
index 7d086e00a835..c57399985ecd 100644
--- a/arch/arm/mach-tegra/sleep.h
+++ b/arch/arm/mach-tegra/sleep.h
@@ -50,6 +50,8 @@
#define TEGRA_IRAM_CODE_AREA (TEGRA_IRAM_BASE + SZ_4K)
+/* PMC_SCRATCH2 is used for PLLM boot state if PLLM auto-restart is enabled */
+#define PMC_SCRATCH2 0x58
/* PMC_SCRATCH37-39 and 41 are used for tegra_pen_lock in Tegra2 idle */
#define PMC_SCRATCH37 0x130
#define PMC_SCRATCH38 0x134
diff --git a/arch/arm/mach-tegra/tegra3_clocks.c b/arch/arm/mach-tegra/tegra3_clocks.c
index 3201a6e538c0..ca8cd5c449b9 100644
--- a/arch/arm/mach-tegra/tegra3_clocks.c
+++ b/arch/arm/mach-tegra/tegra3_clocks.c
@@ -228,6 +228,14 @@
#define PMC_PLLP_WB0_OVERRIDE 0xf8
#define PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE (1 << 12)
+#define PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE (1 << 11)
+#define PMC_PLLM_WB0_OVERRIDE 0x1dc
+#define PMC_PLLM_WB0_OVERRIDE_DIVP_MASK (0x7<<15)
+#define PMC_PLLM_WB0_OVERRIDE_DIVP_SHIFT 15
+#define PMC_PLLM_WB0_OVERRIDE_DIVN_MASK (0x3FF<<5)
+#define PMC_PLLM_WB0_OVERRIDE_DIVN_SHIFT 5
+#define PMC_PLLM_WB0_OVERRIDE_DIVM_MASK (0x1F)
+#define PMC_PLLM_WB0_OVERRIDE_DIVM_SHIFT 0
#define UTMIP_PLL_CFG2 0x488
#define UTMIP_PLL_CFG2_STABLE_COUNT(x) (((x) & 0xfff) << 6)
@@ -1457,6 +1465,30 @@ static void tegra3_utmi_param_configure(struct clk *c)
clk_writel(reg, UTMIP_PLL_CFG1);
}
+static void tegra3_pll_m_override_update(struct clk *c, bool init)
+{
+ u32 val = pmc_readl(PMC_PLLP_WB0_OVERRIDE);
+
+ if (!(val & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE))
+ return;
+
+ /* override PLLM state with PMC settings */
+ c->state = (val & PMC_PLLP_WB0_OVERRIDE_PLLM_ENABLE) ? ON : OFF;
+
+ val = pmc_readl(PMC_PLLM_WB0_OVERRIDE);
+ c->mul = (val & PMC_PLLM_WB0_OVERRIDE_DIVN_MASK) >>
+ PMC_PLLM_WB0_OVERRIDE_DIVN_SHIFT;
+ c->div = (val & PMC_PLLM_WB0_OVERRIDE_DIVM_MASK) >>
+ PMC_PLLM_WB0_OVERRIDE_DIVM_SHIFT;
+ c->div *= (0x1 << ((val & PMC_PLLM_WB0_OVERRIDE_DIVP_MASK) >>
+ PMC_PLLM_WB0_OVERRIDE_DIVP_SHIFT));
+
+ /* Save initial override settings in Scratch2 register; will be used by
+ LP0 entry code to restore PLLM boot configuration */
+ if (init)
+ pmc_writel(val, PMC_SCRATCH2);
+}
+
static void tegra3_pll_clk_init(struct clk *c)
{
u32 val = clk_readl(c->reg + PLL_BASE);
@@ -1498,6 +1530,9 @@ static void tegra3_pll_clk_init(struct clk *c)
if (c->flags & PLLU) {
tegra3_utmi_param_configure(c);
}
+
+ if (c->flags & PLLM)
+ tegra3_pll_m_override_update(c, true);
}
static int tegra3_pll_clk_enable(struct clk *c)
@@ -1542,6 +1577,27 @@ static void tegra3_pll_clk_disable(struct clk *c)
}
}
+static int tegra3_pllm_override_rate(
+ struct clk *c, const struct clk_pll_freq_table *sel, u32 p_div)
+{
+ u32 val, old_base;
+
+ old_base = val = pmc_readl(PMC_PLLM_WB0_OVERRIDE);
+
+ /* Keep default CPCON and DCCON in override configuration */
+ val &= ~(PMC_PLLM_WB0_OVERRIDE_DIVM_MASK |
+ PMC_PLLM_WB0_OVERRIDE_DIVN_MASK |
+ PMC_PLLM_WB0_OVERRIDE_DIVP_MASK);
+ val |= (sel->m << PMC_PLLM_WB0_OVERRIDE_DIVM_SHIFT) |
+ (sel->n << PMC_PLLM_WB0_OVERRIDE_DIVN_SHIFT) |
+ (p_div << PMC_PLLM_WB0_OVERRIDE_DIVP_SHIFT);
+
+ if (val != old_base)
+ pmc_writel(val, PMC_PLLM_WB0_OVERRIDE);
+
+ return 0;
+}
+
static int tegra3_pll_clk_set_rate(struct clk *c, unsigned long rate)
{
u32 val, p_div, old_base;
@@ -1561,7 +1617,7 @@ static int tegra3_pll_clk_set_rate(struct clk *c, unsigned long rate)
return ret;
}
- if (c->flags & PLLM) {
+ if ((c->flags & PLLM) && (c->state == ON)) {
if (rate != clk_get_rate_locked(c)) {
pr_err("%s: Can not change memory %s rate in flight\n",
__func__, c->name);
@@ -1643,6 +1699,13 @@ static int tegra3_pll_clk_set_rate(struct clk *c, unsigned long rate)
c->mul = sel->n;
c->div = sel->m * sel->p;
+ if (c->flags & PLLM) {
+ val = pmc_readl(PMC_PLLP_WB0_OVERRIDE);
+ if (val & PMC_PLLP_WB0_OVERRIDE_PLLM_OVERRIDE)
+ return tegra3_pllm_override_rate(
+ c, sel, p_div >> PLL_BASE_DIVP_SHIFT);
+ }
+
old_base = val = clk_readl(c->reg + PLL_BASE);
val &= ~(PLL_BASE_DIVM_MASK | PLL_BASE_DIVN_MASK |
((c->flags & PLLU) ? PLLU_BASE_POST_DIV : PLL_BASE_DIVP_MASK));
@@ -4989,6 +5052,7 @@ static void tegra_clk_resume(void)
/* Since EMC clock is not restored, and may not preserve parent across
suspend, update current state, and mark EMC DFS as out of sync */
+ tegra3_pll_m_override_update(&tegra_pll_m, false);
p = tegra_clk_emc.parent;
tegra3_periph_clk_init(&tegra_clk_emc);