omap4460 fix lack of mpu dpll bypass From: Andy Green Signed-off-by: Andy Green --- arch/arm/mach-omap2/dpll44xx.c | 136 +++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 71 deletions(-) diff --git a/arch/arm/mach-omap2/dpll44xx.c b/arch/arm/mach-omap2/dpll44xx.c index 9042c8c..da63a3c 100644 --- a/arch/arm/mach-omap2/dpll44xx.c +++ b/arch/arm/mach-omap2/dpll44xx.c @@ -92,42 +92,40 @@ const struct clkops clkops_omap4_dpllmx_ops = { .deny_idle = omap4_dpllmx_deny_gatectrl, }; -static int omap4460_dcc(struct clk *clk, unsigned long rate) + +static void omap4460_mpu_dpll_update_children(unsigned long rate) { - struct dpll_data *dd; - u32 v; - - if (!clk || !rate || !clk->parent) - return -EINVAL; - - dd = clk->parent->dpll_data; - - if (!dd) - return -EINVAL; - - v = __raw_readl(dd->mult_div1_reg); - if (rate < OMAP_1GHz) { - /* If DCC is enabled, disable it */ - if (v & OMAP4460_DCC_EN_MASK) { - v &= ~OMAP4460_DCC_EN_MASK; - __raw_writel(v, dd->mult_div1_reg); - } - } else { - v &= ~OMAP4460_DCC_COUNT_MAX_MASK; - v |= (5 << OMAP4460_DCC_COUNT_MAX_SHIFT); - v |= OMAP4460_DCC_EN_MASK; - __raw_writel(v, dd->mult_div1_reg); - } - - return 0; -} + u32 v; + + /* + * The interconnect frequency to EMIF should + * be switched between MPU clk divide by 4 (for + * frequencies higher than 920Mhz) and MPU clk divide + * by 2 (for frequencies lower than or equal to 920Mhz) + * Also the async bridge to ABE must be MPU clk divide + * by 8 for MPU clk > 748Mhz and MPU clk divide by 4 + * for lower frequencies. + */ + v = __raw_readl(OMAP4430_CM_MPU_MPU_CLKCTRL); + if (rate > OMAP_920MHz) + v |= OMAP4460_CLKSEL_EMIF_DIV_MODE_MASK; + else + v &= ~OMAP4460_CLKSEL_EMIF_DIV_MODE_MASK; + if (rate > OMAP_748MHz) + v |= OMAP4460_CLKSEL_ABE_DIV_MODE_MASK; + else + v &= ~OMAP4460_CLKSEL_ABE_DIV_MODE_MASK; + __raw_writel(v, OMAP4430_CM_MPU_MPU_CLKCTRL); +} int omap4460_mpu_dpll_set_rate(struct clk *clk, unsigned long rate) { struct dpll_data *dd; u32 v; unsigned long dpll_rate; + unsigned long eff_rate = rate; + int ret = 0; if (!clk || !rate || !clk->parent) return -EINVAL; @@ -152,56 +150,52 @@ int omap4460_mpu_dpll_set_rate(struct clk *clk, unsigned long rate) if (v & OMAP4460_DCC_EN_MASK) dpll_rate *= 2; -// pr_err("omap4460_mpu_dpll_set_rate: old rate %ld, new rate %ld\n", -// dpll_rate, rate); - - if (rate < OMAP_1GHz) { - omap4460_dcc(clk, rate); - if (clk->parent->set_rate(clk->parent, rate)) { - omap4460_dcc(clk, dpll_rate); - return -EINVAL; - } - } else { - /* - * On 4460, the MPU clk for frequencies higher than 1Ghz - * is sourced from CLKOUTX2_M3, instead of CLKOUT_M2, while - * value of M3 is fixed to 1. Hence for frequencies higher - * than 1 Ghz, lock the DPLL at half the rate so the - * CLKOUTX2_M3 then matches the requested rate. - */ - if (clk->parent->set_rate(clk->parent, rate/2)) { - omap4460_dcc(clk, dpll_rate); - return -EINVAL; - } - omap4460_dcc(clk, rate); - } + /* + * bypass the DPLL - out update action including dcc and + * bus rate adapation is far from atomic. We need to get a stable + * clock coming nicely while we rearrange the timing furniture + */ + + v = __raw_readl(dd->control_reg); + v &= ~OMAP4430_DPLL_EN_MASK; + v |= 5 << OMAP4430_DPLL_EN_SHIFT; + __raw_writel(v, dd->control_reg); + + pr_debug("omap4460_mpu_dpll_set_rate: %ld -> %ld\n", dpll_rate, rate); + + if (rate > OMAP_1GHz) + eff_rate /= 2; + + ret = clk->parent->set_rate(clk->parent, eff_rate); + if (ret) + goto bail; + + v = __raw_readl(dd->mult_div1_reg); + if (rate > OMAP_1GHz) + v &= ~OMAP4460_DCC_EN_MASK; + else + v |= ~OMAP4460_DCC_EN_MASK; + __raw_writel(v, dd->mult_div1_reg); - clk->rate = rate; +bail: + omap4460_mpu_dpll_update_children(rate); - /* - * The interconnect frequency to EMIF should - * be switched between MPU clk divide by 4 (for - * frequencies higher than 920Mhz) and MPU clk divide - * by 2 (for frequencies lower than or equal to 920Mhz) - * Also the async bridge to ABE must be MPU clk divide - * by 8 for MPU clk > 748Mhz and MPU clk divide by 4 - * for lower frequencies. + /* + * lock the DPLL + * now we took care of everything, ask the dpll to resume giving + * appropriate output when it locks */ - v = __raw_readl(OMAP4430_CM_MPU_MPU_CLKCTRL); - if (rate > OMAP_920MHz) - v |= OMAP4460_CLKSEL_EMIF_DIV_MODE_MASK; - else - v &= ~OMAP4460_CLKSEL_EMIF_DIV_MODE_MASK; - if (rate > OMAP_748MHz) - v |= OMAP4460_CLKSEL_ABE_DIV_MODE_MASK; - else - v &= ~OMAP4460_CLKSEL_ABE_DIV_MODE_MASK; - __raw_writel(v, OMAP4430_CM_MPU_MPU_CLKCTRL); + v = __raw_readl(dd->control_reg); + v |= 7 << OMAP4430_DPLL_EN_SHIFT; + __raw_writel(v, dd->control_reg); - return 0; + clk->rate = rate; + + return ret; } + long omap4460_mpu_dpll_round_rate(struct clk *clk, unsigned long rate) { if (!clk || !rate || !clk->parent)