[ARM] OMAP3 clock: avoid invalid FREQSEL values during DPLL rate rounding
authorPaul Walmsley <paul@pwsan.com>
Wed, 28 Jan 2009 19:08:44 +0000 (12:08 -0700)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Sun, 8 Feb 2009 17:50:35 +0000 (17:50 +0000)
The DPLL FREQSEL jitter correction bits are set based on a table in
the 34xx TRM, Table 4-38, according to the DPLL's internal clock
frequency "Fint."  Several Fint frequency ranges are missing from this
table.  Previously, we allowed these Fint frequency ranges to be
selected in the rate rounding code, but did not change the FREQSEL bits.
Correspondence with the OMAP hardware team indicates that Fint values
not in the table should not be used.  So, prevent them from being
selected during DPLL rate rounding.  This removes warnings and also
can prevent the chip from locking up.

The first pass through the rate rounding code will update the DPLL max
and min dividers appropriately, so later rate rounding passes will run
faster than the first.

Peter de Schrijver <peter.de-schrijver@nokia.com> put up with several
test cycles of this patch - thanks Peter.

linux-omap source commit is f9c1b82f55b60fc39eaa6e7aa1fbe380c0ffe2e9.

Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Peter de Schrijver <peter.de-schrijver@nokia.com>
Signed-off-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/mach-omap2/clock.c
arch/arm/mach-omap2/clock24xx.h
arch/arm/mach-omap2/clock34xx.h
arch/arm/plat-omap/include/mach/clock.h

index 76e20bcc4e8a5a86334bfcecd991371056c03b94..752e34787f215f3d5c23a975b32aeff4e4001de5 100644 (file)
 #define DPLL_ROUNDING_VAL              ((DPLL_SCALE_BASE / 2) * \
                                         (DPLL_SCALE_FACTOR / DPLL_SCALE_BASE))
 
+/* DPLL valid Fint frequency band limits - from 34xx TRM Section 4.7.6.2 */
+#define DPLL_FINT_BAND1_MIN            750000
+#define DPLL_FINT_BAND1_MAX            2100000
+#define DPLL_FINT_BAND2_MIN            7500000
+#define DPLL_FINT_BAND2_MAX            21000000
+
+/* _dpll_test_fint() return codes */
+#define DPLL_FINT_UNDERFLOW            -1
+#define DPLL_FINT_INVALID              -2
+
 u8 cpu_mask;
 
 /*-------------------------------------------------------------------------
  * OMAP2/3 specific clock functions
  *-------------------------------------------------------------------------*/
 
+/*
+ * _dpll_test_fint - test whether an Fint value is valid for the DPLL
+ * @clk: DPLL struct clk to test
+ * @n: divider value (N) to test
+ *
+ * Tests whether a particular divider @n will result in a valid DPLL
+ * internal clock frequency Fint. See the 34xx TRM 4.7.6.2 "DPLL Jitter
+ * Correction".  Returns 0 if OK, -1 if the enclosing loop can terminate
+ * (assuming that it is counting N upwards), or -2 if the enclosing loop
+ * should skip to the next iteration (again assuming N is increasing).
+ */
+static int _dpll_test_fint(struct clk *clk, u8 n)
+{
+       struct dpll_data *dd;
+       long fint;
+       int ret = 0;
+
+       dd = clk->dpll_data;
+
+       /* DPLL divider must result in a valid jitter correction val */
+       fint = clk->parent->rate / (n + 1);
+       if (fint < DPLL_FINT_BAND1_MIN) {
+
+               pr_debug("rejecting n=%d due to Fint failure, "
+                        "lowering max_divider\n", n);
+               dd->max_divider = n;
+               ret = DPLL_FINT_UNDERFLOW;
+
+       } else if (fint > DPLL_FINT_BAND1_MAX &&
+                  fint < DPLL_FINT_BAND2_MIN) {
+
+               pr_debug("rejecting n=%d due to Fint failure\n", n);
+               ret = DPLL_FINT_INVALID;
+
+       } else if (fint > DPLL_FINT_BAND2_MAX) {
+
+               pr_debug("rejecting n=%d due to Fint failure, "
+                        "boosting min_divider\n", n);
+               dd->min_divider = n;
+               ret = DPLL_FINT_INVALID;
+
+       }
+
+       return ret;
+}
+
 /**
  * omap2_init_clk_clkdm - look up a clockdomain name, store pointer in clk
  * @clk: OMAP clock struct ptr to use
@@ -892,7 +948,14 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
 
        dd->last_rounded_rate = 0;
 
-       for (n = DPLL_MIN_DIVIDER; n <= dd->max_divider; n++) {
+       for (n = dd->min_divider; n <= dd->max_divider; n++) {
+
+               /* Is the (input clk, divider) pair valid for the DPLL? */
+               r = _dpll_test_fint(clk, n);
+               if (r == DPLL_FINT_UNDERFLOW)
+                       break;
+               else if (r == DPLL_FINT_INVALID)
+                       continue;
 
                /* Compute the scaled DPLL multiplier, based on the divider */
                m = scaled_rt_rp * n;
@@ -926,7 +989,7 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
                        pr_debug("clock: found new least error %d\n", min_e);
 
                        /* We found good settings -- bail out now */
-                       if (min_e <= clk->dpll_data->rate_tolerance)
+                       if (min_e <= dd->rate_tolerance)
                                break;
                }
        }
index 32dd8573e56b05f65181a5126288a68417483a64..7731ab6acd18ea88ac7c79d297aa28b42c84213e 100644 (file)
@@ -666,6 +666,7 @@ static struct dpll_data dpll_dd = {
        .mult_mask              = OMAP24XX_DPLL_MULT_MASK,
        .div1_mask              = OMAP24XX_DPLL_DIV_MASK,
        .max_multiplier         = 1024,
+       .min_divider            = 1,
        .max_divider            = 16,
        .rate_tolerance         = DEFAULT_DPLL_RATE_TOLERANCE
 };
index 7ee131202625e5802b0bb1498fdf08e693b7bbf3..aadd296c05a2d230122192e399a0437491cca694 100644 (file)
@@ -268,6 +268,7 @@ static struct dpll_data dpll1_dd = {
        .idlest_reg     = OMAP_CM_REGADDR(MPU_MOD, OMAP3430_CM_IDLEST_PLL),
        .idlest_mask    = OMAP3430_ST_MPU_CLK_MASK,
        .max_multiplier = OMAP3_MAX_DPLL_MULT,
+       .min_divider    = 1,
        .max_divider    = OMAP3_MAX_DPLL_DIV,
        .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
 };
@@ -341,6 +342,7 @@ static struct dpll_data dpll2_dd = {
        .idlest_reg     = OMAP_CM_REGADDR(OMAP3430_IVA2_MOD, OMAP3430_CM_IDLEST_PLL),
        .idlest_mask    = OMAP3430_ST_IVA2_CLK_MASK,
        .max_multiplier = OMAP3_MAX_DPLL_MULT,
+       .min_divider    = 1,
        .max_divider    = OMAP3_MAX_DPLL_DIV,
        .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
 };
@@ -400,6 +402,7 @@ static struct dpll_data dpll3_dd = {
        .idlest_reg     = OMAP_CM_REGADDR(PLL_MOD, CM_IDLEST),
        .idlest_mask    = OMAP3430_ST_CORE_CLK_MASK,
        .max_multiplier = OMAP3_MAX_DPLL_MULT,
+       .min_divider    = 1,
        .max_divider    = OMAP3_MAX_DPLL_DIV,
        .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
 };
@@ -591,6 +594,7 @@ static struct dpll_data dpll4_dd = {
        .idlest_reg     = OMAP_CM_REGADDR(PLL_MOD, CM_IDLEST),
        .idlest_mask    = OMAP3430_ST_PERIPH_CLK_MASK,
        .max_multiplier = OMAP3_MAX_DPLL_MULT,
+       .min_divider    = 1,
        .max_divider    = OMAP3_MAX_DPLL_DIV,
        .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
 };
@@ -930,6 +934,7 @@ static struct dpll_data dpll5_dd = {
        .idlest_reg     = OMAP_CM_REGADDR(PLL_MOD, CM_IDLEST2),
        .idlest_mask    = OMAP3430ES2_ST_PERIPH2_CLK_MASK,
        .max_multiplier = OMAP3_MAX_DPLL_MULT,
+       .min_divider    = 1,
        .max_divider    = OMAP3_MAX_DPLL_DIV,
        .rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
 };
index 611df52a324284f1c19e6b9a7700f502c71d019d..cd69111cd33f194052d0d7c034b378c5de6208c0 100644 (file)
@@ -43,6 +43,7 @@ struct dpll_data {
        unsigned long           last_rounded_rate;
        u16                     last_rounded_m;
        u8                      last_rounded_n;
+       u8                      min_divider;
        u8                      max_divider;
        u32                     max_tolerance;
        u16                     max_multiplier;