diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-tegra/sleep-t3.S | 5 | ||||
-rw-r--r-- | arch/arm/mach-tegra/sleep.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra3_clocks.c | 66 |
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); |