]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/arm/mach-omap2/clock34xx.c
OMAP3 clock: fix non-CORE DPLL rate assignment bugs
[linux-2.6-omap-h63xx.git] / arch / arm / mach-omap2 / clock34xx.c
index 8fdf8f3c09fb0fb62911346d0a47088c2d4674f0..cc43f4f2a547e7e6d965df4cb1d37d821f8c5991 100644 (file)
 #include <linux/io.h>
 #include <linux/limits.h>
 
-#include <asm/arch/clock.h>
-#include <asm/arch/sram.h>
+#include <mach/clock.h>
+#include <mach/sram.h>
 #include <asm/div64.h>
 #include <asm/bitops.h>
 
-#include "memory.h"
+#include <mach/sdrc.h>
 #include "clock.h"
 #include "clock34xx.h"
 #include "prm.h"
@@ -66,10 +66,10 @@ static void _omap3_dpll_write_clken(struct clk *clk, u8 clken_bits)
 
        dd = clk->dpll_data;
 
-       v = __raw_readl(dd->control_reg);
+       v = cm_read_mod_reg(clk->prcm_mod, dd->control_reg);
        v &= ~dd->enable_mask;
        v |= clken_bits << __ffs(dd->enable_mask);
-       __raw_writel(v, dd->control_reg);
+       cm_write_mod_reg(v, clk->prcm_mod, dd->control_reg);
 }
 
 /* _omap3_wait_dpll_status: wait for a DPLL to enter a specific state */
@@ -78,14 +78,13 @@ static int _omap3_wait_dpll_status(struct clk *clk, u8 state)
        const struct dpll_data *dd;
        int i = 0;
        int ret = -EINVAL;
-       u32 idlest_mask;
 
        dd = clk->dpll_data;
 
-       state <<= dd->idlest_bit;
-       idlest_mask = 1 << dd->idlest_bit;
+       state <<= __ffs(dd->idlest_mask);
 
-       while (((__raw_readl(dd->idlest_reg) & idlest_mask) != state) &&
+       while (((cm_read_mod_reg(clk->prcm_mod, dd->idlest_reg)
+                & dd->idlest_mask) != state) &&
               i < MAX_DPLL_WAIT_TRIES) {
                i++;
                udelay(1);
@@ -182,7 +181,7 @@ static int _omap3_noncore_dpll_lock(struct clk *clk)
 }
 
 /*
- * omap3_noncore_dpll_bypass - instruct a DPLL to bypass and wait for readiness
+ * _omap3_noncore_dpll_bypass - instruct a DPLL to bypass and wait for readiness
  * @clk: pointer to a DPLL struct clk
  *
  * Instructs a non-CORE DPLL to enter low-power bypass mode.  In
@@ -272,15 +271,23 @@ static int _omap3_noncore_dpll_stop(struct clk *clk)
 static int omap3_noncore_dpll_enable(struct clk *clk)
 {
        int r;
+       struct dpll_data *dd;
 
        if (clk == &dpll3_ck)
                return -EINVAL;
 
-       if (clk->parent->rate == omap2_get_dpll_rate(clk))
+       dd = clk->dpll_data;
+       if (!dd)
+               return -EINVAL;
+
+       if (clk->rate == dd->bypass_clk->rate)
                r = _omap3_noncore_dpll_bypass(clk);
        else
                r = _omap3_noncore_dpll_lock(clk);
 
+       if (!r)
+               clk->rate = omap2_get_dpll_rate(clk);
+
        return r;
 }
 
@@ -336,7 +343,7 @@ static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel)
         * on 3430ES1 prevents us from changing DPLL multipliers or dividers
         * on DPLL4.
         */
-       if (is_sil_rev_equal_to(OMAP3430_REV_ES1_0) &&
+       if (system_rev == OMAP3430_REV_ES1_0 &&
            !strcmp("dpll4_ck", clk->name)) {
                printk(KERN_ERR "clock: DPLL4 cannot change rate due to "
                       "silicon 'Limitation 2.5' on 3430ES1.\n");
@@ -347,17 +354,17 @@ static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel)
        _omap3_noncore_dpll_bypass(clk);
 
        /* Set jitter correction */
-       v = __raw_readl(dd->control_reg);
+       v = cm_read_mod_reg(clk->prcm_mod, dd->control_reg);
        v &= ~dd->freqsel_mask;
        v |= freqsel << __ffs(dd->freqsel_mask);
-       __raw_writel(v, dd->control_reg);
+       cm_write_mod_reg(v, clk->prcm_mod, dd->control_reg);
 
        /* Set DPLL multiplier, divider */
-       v = __raw_readl(dd->mult_div1_reg);
+       v = cm_read_mod_reg(clk->prcm_mod, dd->mult_div1_reg);
        v &= ~(dd->mult_mask | dd->div1_mask);
        v |= m << __ffs(dd->mult_mask);
        v |= (n - 1) << __ffs(dd->div1_mask);
-       __raw_writel(v, dd->mult_div1_reg);
+       cm_write_mod_reg(v, clk->prcm_mod, dd->mult_div1_reg);
 
        /* We let the clock framework set the other output dividers later */
 
@@ -373,13 +380,17 @@ static int omap3_noncore_dpll_program(struct clk *clk, u16 m, u8 n, u16 freqsel)
  * @clk: struct clk * of DPLL to set
  * @rate: rounded target rate
  *
- * Program the DPLL with the rounded target rate.  Returns -EINVAL upon
- * error, or 0 upon success.
+ * Set the DPLL CLKOUT to the target rate.  If the DPLL can enter
+ * low-power bypass, and the target rate is the bypass source clock
+ * rate, then configure the DPLL for bypass.  Otherwise, round the
+ * target rate if it hasn't been done already, then program and lock
+ * the DPLL.  Returns -EINVAL upon error, or 0 upon success.
  */
 static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
 {
        u16 freqsel;
        struct dpll_data *dd;
+       int ret;
 
        if (!clk || !rate)
                return -EINVAL;
@@ -391,24 +402,108 @@ static int omap3_noncore_dpll_set_rate(struct clk *clk, unsigned long rate)
        if (rate == omap2_get_dpll_rate(clk))
                return 0;
 
-       if (dd->last_rounded_rate != rate)
-               omap2_dpll_round_rate(clk, rate);
+       if (dd->bypass_clk->rate == rate &&
+           (clk->dpll_data->modes & (1 << DPLL_LOW_POWER_BYPASS))) {
 
-       if (dd->last_rounded_rate == 0)
-               return -EINVAL;
+               pr_debug("clock: %s: set rate: entering bypass.\n", clk->name);
+
+               ret = _omap3_noncore_dpll_bypass(clk);
+               if (!ret)
+                       clk->rate = rate;
+
+       } else {
+
+               if (dd->last_rounded_rate != rate)
+                       omap2_dpll_round_rate(clk, rate);
+
+               if (dd->last_rounded_rate == 0)
+                       return -EINVAL;
+
+               freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n);
+               if (!freqsel)
+                       WARN_ON(1);
 
-       freqsel = _omap3_dpll_compute_freqsel(clk, dd->last_rounded_n);
-       if (!freqsel)
-               WARN_ON(1);
+               pr_debug("clock: %s: set rate: locking rate to %lu.\n",
+                        clk->name, rate);
 
-       omap3_noncore_dpll_program(clk, dd->last_rounded_m, dd->last_rounded_n,
-                                  freqsel);
+               ret = omap3_noncore_dpll_program(clk, dd->last_rounded_m,
+                                                dd->last_rounded_n, freqsel);
+
+               if (!ret)
+                       clk->rate = rate;
+
+       }
 
        omap3_dpll_recalc(clk);
 
        return 0;
 }
 
+
+/*
+ * CORE DPLL (DPLL3) rate programming functions
+ *
+ * These call into SRAM code to do the actual CM writes, since the SDRAM
+ * is clocked from DPLL3.
+ */
+
+/**
+ * omap3_core_dpll_m2_set_rate - set CORE DPLL M2 divider
+ * @clk: struct clk * of DPLL to set
+ * @rate: rounded target rate
+ *
+ * Program the DPLL M2 divider with the rounded target rate.  Returns
+ * -EINVAL upon error, or 0 upon success.
+ */
+static int omap3_core_dpll_m2_set_rate(struct clk *clk, unsigned long rate)
+{
+       u32 new_div = 0;
+       unsigned long validrate, sdrcrate;
+       struct omap_sdrc_params *sp;
+
+       if (!clk || !rate)
+               return -EINVAL;
+
+       if (clk != &dpll3_m2_ck)
+               return -EINVAL;
+
+       if (rate == clk->rate)
+               return 0;
+
+       validrate = omap2_clksel_round_rate_div(clk, rate, &new_div);
+       if (validrate != rate)
+               return -EINVAL;
+
+       sdrcrate = sdrc_ick.rate;
+       if (rate > clk->rate)
+               sdrcrate <<= ((rate / clk->rate) - 1);
+       else
+               sdrcrate >>= ((clk->rate / rate) - 1);
+
+       sp = omap2_sdrc_get_params(sdrcrate);
+       if (!sp)
+               return -EINVAL;
+
+       pr_info("clock: changing CORE DPLL rate from %lu to %lu\n", clk->rate,
+               validrate);
+       pr_info("clock: SDRC timing params used: %08x %08x %08x\n",
+               sp->rfr_ctrl, sp->actim_ctrla, sp->actim_ctrlb);
+
+       /* REVISIT: SRAM code doesn't support other M2 divisors yet */
+       WARN_ON(new_div != 1 && new_div != 2);
+
+       /* REVISIT: Add SDRC_MR changing to this code also */
+       local_irq_disable();
+       omap3_configure_core_dpll(sp->rfr_ctrl, sp->actim_ctrla,
+                                 sp->actim_ctrlb, new_div);
+       local_irq_enable();
+
+       omap2_clksel_recalc(clk);
+
+       return 0;
+}
+
+
 /* DPLL autoidle read/set code */
 
 
@@ -430,7 +525,7 @@ static u32 omap3_dpll_autoidle_read(struct clk *clk)
 
        dd = clk->dpll_data;
 
-       v = __raw_readl(dd->autoidle_reg);
+       v = cm_read_mod_reg(clk->prcm_mod, dd->autoidle_reg);
        v &= dd->autoidle_mask;
        v >>= __ffs(dd->autoidle_mask);
 
@@ -461,10 +556,10 @@ static void omap3_dpll_allow_idle(struct clk *clk)
         * by writing 0x5 instead of 0x1.  Add some mechanism to
         * optionally enter this mode.
         */
-       v = __raw_readl(dd->autoidle_reg);
+       v = cm_read_mod_reg(clk->prcm_mod, dd->autoidle_reg);
        v &= ~dd->autoidle_mask;
        v |= DPLL_AUTOIDLE_LOW_POWER_STOP << __ffs(dd->autoidle_mask);
-       __raw_writel(v, dd->autoidle_reg);
+       cm_write_mod_reg(v, clk->prcm_mod, dd->autoidle_reg);
 }
 
 /**
@@ -483,10 +578,10 @@ static void omap3_dpll_deny_idle(struct clk *clk)
 
        dd = clk->dpll_data;
 
-       v = __raw_readl(dd->autoidle_reg);
+       v = cm_read_mod_reg(clk->prcm_mod, dd->autoidle_reg);
        v &= ~dd->autoidle_mask;
        v |= DPLL_AUTOIDLE_DISABLE << __ffs(dd->autoidle_mask);
-       __raw_writel(v, dd->autoidle_reg);
+       cm_write_mod_reg(v, clk->prcm_mod, dd->autoidle_reg);
 }
 
 /* Clock control for DPLL outputs */
@@ -514,11 +609,10 @@ static void omap3_clkoutx2_recalc(struct clk *clk)
 
        dd = pclk->dpll_data;
 
-       WARN_ON(!dd->control_reg || !dd->enable_mask);
+       WARN_ON(!dd->idlest_reg || !dd->idlest_mask);
 
-       v = __raw_readl(dd->control_reg) & dd->enable_mask;
-       v >>= __ffs(dd->enable_mask);
-       if (v != DPLL_LOCKED)
+       v = cm_read_mod_reg(pclk->prcm_mod, dd->idlest_reg) & dd->idlest_mask;
+       if (!v)
                clk->rate = clk->parent->rate;
        else
                clk->rate = clk->parent->rate * 2;
@@ -617,7 +711,7 @@ int __init omap2_clk_init(void)
                 * Update this if there are further clock changes between ES2
                 * and production parts
                 */
-               if (is_sil_rev_equal_to(OMAP3430_REV_ES1_0)) {
+               if (system_rev == OMAP3430_REV_ES1_0) {
                        /* No 3430ES1-only rates exist, so no RATE_IN_3430ES1 */
                        cpu_clkflg |= CLOCK_IN_OMAP3430ES1;
                } else {