diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c index 76e20bcc4e8a5a86334bfcecd991371056c03b94..752e34787f215f3d5c23a975b32aeff4e4001de5 100644 --- a/arch/arm/mach-omap2/clock.c +++ b/arch/arm/mach-omap2/clock.c @@ -58,12 +58,68 @@ #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; } } diff --git a/arch/arm/mach-omap2/clock24xx.h b/arch/arm/mach-omap2/clock24xx.h index 32dd8573e56b05f65181a5126288a68417483a64..7731ab6acd18ea88ac7c79d297aa28b42c84213e 100644 --- a/arch/arm/mach-omap2/clock24xx.h +++ b/arch/arm/mach-omap2/clock24xx.h @@ -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 }; diff --git a/arch/arm/mach-omap2/clock34xx.h b/arch/arm/mach-omap2/clock34xx.h index 7ee131202625e5802b0bb1498fdf08e693b7bbf3..aadd296c05a2d230122192e399a0437491cca694 100644 --- a/arch/arm/mach-omap2/clock34xx.h +++ b/arch/arm/mach-omap2/clock34xx.h @@ -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 }; diff --git a/arch/arm/plat-omap/include/mach/clock.h b/arch/arm/plat-omap/include/mach/clock.h index 611df52a324284f1c19e6b9a7700f502c71d019d..cd69111cd33f194052d0d7c034b378c5de6208c0 100644 --- a/arch/arm/plat-omap/include/mach/clock.h +++ b/arch/arm/plat-omap/include/mach/clock.h @@ -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;