Skip to contentSkip to conversation

OMAP3 clock: skip reserved FREQSEL settings

PATCH In review Paul Walmsley 2 participants 5 messages Nov 12, 2008 – Nov 21, 2008

Subsystems (4)
lore
Share & tools
lore

Paul Walmsley

author maintainer
#0 lore raw

cover OMAP3 clock: skip reserved FREQSEL settings

Hello,

this series fixes a bug in the OMAP3 DPLL rate rounding code that will attempt to program the DPLL to use an internal clock frequency that does not exist in the FREQSEL table in 34xx TRM 4.7.6.2. This mostly seems to generate warnings, but can potentially hang the system.

As part of this process, improve the DPLL rate rounding algorithm to start from a low divider (N) and count up; this should result in fewer passes through the loop.

Tested on 3430SDP ES2 and a custom ES3 board. Thanks to Peter de Schrijver <[email protected]> for help with these patches.

- Paul

---

text data bss dec hex filename

3574155  189440  108880 3872475  3b16db vmlinux.3430sdp.orig
3574251  189440  108880 3872571  3b173b vmlinux.3430sdp
 arch/arm/mach-omap2/clock.c             |  118 ++++++++++++++++++++++++-------
 arch/arm/mach-omap2/clock24xx.h         |    1 
 arch/arm/mach-omap2/clock34xx.h         |    5 +
 arch/arm/plat-omap/include/mach/clock.h |    1 
 4 files changed, 99 insertions(+), 26 deletions(-)
author maintainer
#1 lore raw

PATCH 1/3 OMAP3 clock: remove unnecessary dpll_data dereferences

Remove some clutter from omap2_dpll_round_rate().

---
 arch/arm/mach-omap2/clock.c |   23 +++++++++++++----------
 1 files changed, 13 insertions(+), 10 deletions(-)
arch/arm/mach-omap2/clock.c +13 -10
index daea105..7aefabb 100644
@@ -928,19 +928,22 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
 	int m, n, r, e, scaled_max_m;
 	unsigned long scaled_rt_rp, new_rate;
 	int min_e = -1, min_e_m = -1, min_e_n = -1;
+	struct dpll_data *dd;
 
 	if (!clk || !clk->dpll_data)
 		return ~0;
 
+	dd = clk->dpll_data;
+
 	pr_debug("clock: starting DPLL round_rate for clock %s, target rate "
 		 "%ld\n", clk->name, target_rate);
 
 	scaled_rt_rp = target_rate / (clk->parent->rate / DPLL_SCALE_FACTOR);
-	scaled_max_m = clk->dpll_data->max_multiplier * DPLL_SCALE_FACTOR;
+	scaled_max_m = dd->max_multiplier * DPLL_SCALE_FACTOR;
 
-	clk->dpll_data->last_rounded_rate = 0;
+	dd->last_rounded_rate = 0;
 
-	for (n = clk->dpll_data->max_divider; n >= DPLL_MIN_DIVIDER; n--) {
+	for (n = dd->max_divider; n >= DPLL_MIN_DIVIDER; n--) {
 
 		/* Compute the scaled DPLL multiplier, based on the divider */
 		m = scaled_rt_rp * n;
@@ -960,7 +963,7 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
 			 "(new_rate = %ld)\n", n, m, e, new_rate);
 
 		if (min_e == -1 ||
-		    min_e >= (int)(abs(e) - clk->dpll_data->rate_tolerance)) {
+		    min_e >= (int)(abs(e) - dd->rate_tolerance)) {
 			min_e = e;
 			min_e_m = m;
 			min_e_n = n;
@@ -983,17 +986,17 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
 		return ~0;
 	}
 
-	clk->dpll_data->last_rounded_m = min_e_m;
-	clk->dpll_data->last_rounded_n = min_e_n;
-	clk->dpll_data->last_rounded_rate =
-		_dpll_compute_new_rate(clk->parent->rate, min_e_m,  min_e_n);
+	dd->last_rounded_m = min_e_m;
+	dd->last_rounded_n = min_e_n;
+	dd->last_rounded_rate = _dpll_compute_new_rate(clk->parent->rate,
+						       min_e_m,  min_e_n);
 
 	pr_debug("clock: final least error: e = %d, m = %d, n = %d\n",
 		 min_e, min_e_m, min_e_n);
 	pr_debug("clock: final rate: %ld  (target rate: %ld)\n",
-		 clk->dpll_data->last_rounded_rate, target_rate);
+		 dd->last_rounded_rate, target_rate);
 
-	return clk->dpll_data->last_rounded_rate;
+	return dd->last_rounded_rate;
 }
 
 /*-------------------------------------------------------------------------
author maintainer
#2 lore raw

PATCH 2/3 OMAP3 clock: optimize DPLL rate rounding algorithm

In reply to Paul Walmsley

The previous DPLL rate rounding algorithm counted the divider (N) down from the maximum to 1. Since we currently use a broad DPLL rate tolerance, and lower N values are more power-efficient, we can often bypass several iterations through the loop by counting N upwards from 1.

Peter de Schrijver <[email protected]> put up with several test cycles of this patch - thanks Peter.

Cc Peter de Schrijver <[email protected]>
---
 arch/arm/mach-omap2/clock.c |   35 ++++++++++++++++++-----------------
 1 files changed, 18 insertions(+), 17 deletions(-)
arch/arm/mach-omap2/clock.c +18 -17
index 7aefabb..bd3be45 100644
@@ -48,7 +48,7 @@
 #define DPLL_MIN_DIVIDER		1
 
 /* Possible error results from _dpll_test_mult */
-#define DPLL_MULT_UNDERFLOW		(1 << 0)
+#define DPLL_MULT_UNDERFLOW		-1
 
 /*
  * Scale factor to mitigate roundoff errors in DPLL rate rounding.
@@ -877,7 +877,7 @@ static int _dpll_test_mult(int *m, int n, unsigned long *new_rate,
 			   unsigned long target_rate,
 			   unsigned long parent_rate)
 {
-	int flags = 0, carry = 0;
+	int r = 0, carry = 0;
 
 	/* Unscale m and round if necessary */
 	if (*m % DPLL_SCALE_FACTOR >= DPLL_ROUNDING_VAL)
@@ -898,13 +898,13 @@ static int _dpll_test_mult(int *m, int n, unsigned long *new_rate,
 	if (*m < DPLL_MIN_MULTIPLIER) {
 		*m = DPLL_MIN_MULTIPLIER;
 		*new_rate = 0;
-		flags = DPLL_MULT_UNDERFLOW;
+		r = DPLL_MULT_UNDERFLOW;
 	}
 
 	if (*new_rate == 0)
 		*new_rate = _dpll_compute_new_rate(parent_rate, *m, n);
 
-	return flags;
+	return r;
 }
 
 /**
@@ -943,21 +943,27 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
 
 	dd->last_rounded_rate = 0;
 
-	for (n = dd->max_divider; n >= DPLL_MIN_DIVIDER; n--) {
+	for (n = DPLL_MIN_DIVIDER; n <= dd->max_divider; n++) {
 
 		/* Compute the scaled DPLL multiplier, based on the divider */
 		m = scaled_rt_rp * n;
 
 		/*
-		 * Since we're counting n down, a m overflow means we can
-		 * can immediately skip to the next n
+		 * Since we're counting n up, a m overflow means we
+		 * can bail out completely (since as n increases in
+		 * the next iteration, there's no way that m can
+		 * increase beyond the current m)
 		 */
 		if (m > scaled_max_m)
-			continue;
+			break;
 
 		r = _dpll_test_mult(&m, n, &new_rate, target_rate,
 				    clk->parent->rate);
 
+		/* m can't be set low enough for this n - try with a larger n */
+		if (r == DPLL_MULT_UNDERFLOW)
+			continue;
+
 		e = target_rate - new_rate;
 		pr_debug("clock: n = %d: m = %d: rate error is %d "
 			 "(new_rate = %ld)\n", n, m, e, new_rate);
@@ -969,16 +975,11 @@ long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
 			min_e_n = n;
 
 			pr_debug("clock: found new least error %d\n", min_e);
-		}
 
-		/*
-		 * Since we're counting n down, a m underflow means we
-		 * can bail out completely (since as n decreases in
-		 * the next iteration, there's no way that m can
-		 * increase beyond the current m)
-		 */
-		if (r & DPLL_MULT_UNDERFLOW)
-			break;
+			/* We found good settings -- bail out now */
+			if (min_e <= clk->dpll_data->rate_tolerance)
+				break;
+		}
 	}
 
 	if (min_e < 0) {
author maintainer
#3 lore raw

PATCH 3/3 OMAP3 clock: avoid invalid FREQSEL values during DPLL rate rounding

In reply to Paul Walmsley

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 <[email protected]> put up with several test cycles of this patch - thanks Peter.

Cc Peter de Schrijver <[email protected]>
---
 arch/arm/mach-omap2/clock.c             |   66 ++++++++++++++++++++++++++++++-
 arch/arm/mach-omap2/clock24xx.h         |    1 
 arch/arm/mach-omap2/clock34xx.h         |    5 ++
 arch/arm/plat-omap/include/mach/clock.h |    1 
 4 files changed, 71 insertions(+), 2 deletions(-)
arch/arm/mach-omap2/clock.c +64 -2
index bd3be45..42af286 100644
@@ -61,6 +61,16 @@
 #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
+
 /* Some OMAP2xxx CM_CLKSEL_PLL.ST_CORE_CLK bits - for omap2_get_dpll_rate() */
 #define ST_CORE_CLK_REF			0x1
 #define ST_CORE_CLK_32K			0x3
@@ -114,6 +124,51 @@ static void _omap2_clk_write_reg(u32 v, u16 reg_offset, struct clk *clk)
 		cm_write_mod_reg(v, clk->prcm_mod, reg_offset);
 }
 
+/*
+ * _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
@@ -943,7 +998,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;
@@ -977,7 +1039,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;
 		}
 	}
arch/arm/mach-omap2/clock24xx.h +1 -0
index 8bf70cf..1fc1249 100644
@@ -676,6 +676,7 @@ static struct dpll_data dpll_dd = {
 	.idlest_reg		= CM_IDLEST,
 	.idlest_mask		= OMAP24XX_ST_CORE_CLK_MASK,
 	.max_multiplier		= 1024,
+	.min_divider		= 1,
 	.max_divider		= 16,
 	.rate_tolerance		= DEFAULT_DPLL_RATE_TOLERANCE
 };
arch/arm/mach-omap2/clock34xx.h +5 -0
index 08789c8..0b95fcb 100644
@@ -292,6 +292,7 @@ static struct dpll_data dpll1_dd = {
 	.idlest_mask	= OMAP3430_ST_MPU_CLK_MASK,
 	.bypass_clk	= &dpll1_fck,
 	.max_multiplier = OMAP3_MAX_DPLL_MULT,
+	.min_divider	= 1,
 	.max_divider	= OMAP3_MAX_DPLL_DIV,
 	.rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
 };
@@ -367,6 +368,7 @@ static struct dpll_data dpll2_dd = {
 	.idlest_mask	= OMAP3430_ST_IVA2_CLK_MASK,
 	.bypass_clk	= &dpll2_fck,
 	.max_multiplier = OMAP3_MAX_DPLL_MULT,
+	.min_divider	= 1,
 	.max_divider	= OMAP3_MAX_DPLL_DIV,
 	.rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
 };
@@ -429,6 +431,7 @@ static struct dpll_data dpll3_dd = {
 	.idlest_mask	= OMAP3430_ST_CORE_CLK_MASK,
 	.bypass_clk	= &sys_ck,
 	.max_multiplier = OMAP3_MAX_DPLL_MULT,
+	.min_divider	= 1,
 	.max_divider	= OMAP3_MAX_DPLL_DIV,
 	.rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
 };
@@ -594,6 +597,7 @@ static struct dpll_data dpll4_dd = {
 	.idlest_mask	= OMAP3430_ST_PERIPH_CLK_MASK,
 	.bypass_clk	= &sys_ck,
 	.max_multiplier = OMAP3_MAX_DPLL_MULT,
+	.min_divider	= 1,
 	.max_divider	= OMAP3_MAX_DPLL_DIV,
 	.rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
 };
@@ -920,6 +924,7 @@ static struct dpll_data dpll5_dd = {
 	.idlest_mask	= OMAP3430ES2_ST_PERIPH2_CLK_MASK,
 	.bypass_clk	= &sys_ck,
 	.max_multiplier = OMAP3_MAX_DPLL_MULT,
+	.min_divider	= 1,
 	.max_divider	= OMAP3_MAX_DPLL_DIV,
 	.rate_tolerance = DEFAULT_DPLL_RATE_TOLERANCE
 };
arch/arm/plat-omap/include/mach/clock.h +1 -0
index 9088925..4eef580 100644
@@ -39,6 +39,7 @@ struct dpll_data {
 	unsigned long		last_rounded_rate;
 	unsigned int		rate_tolerance;
 	u16			max_multiplier;
+	u8			min_divider;
 	u8			max_divider;
 	u32			max_tolerance;
 	u16			idlest_reg;
maintainer
#4 lore raw
In reply to Paul Walmsley
Quoted 7 lines · Paul Walmsley

Hello,

this series fixes a bug in the OMAP3 DPLL rate rounding code that will attempt to program the DPLL to use an internal clock frequency that does not exist in the FREQSEL table in 34xx TRM 4.7.6.2. This mostly seems to generate warnings, but can potentially hang the system.

Quoted 6 lines

As part of this process, improve the DPLL rate rounding algorithm to start from a low divider (N) and count up; this should result in fewer passes through the loop.

Tested on 3430SDP ES2 and a custom ES3 board. Thanks to Peter de Schrijver <[email protected]> for help with these patches.

Pushing this series today to l-o.

Tony

Quoted 20 lines

- Paul

---

text data bss dec hex filename

3574155  189440  108880 3872475  3b16db vmlinux.3430sdp.orig
3574251  189440  108880 3872571  3b173b vmlinux.3430sdp
 arch/arm/mach-omap2/clock.c             |  118 ++++++++++++++++++++++++-------
 arch/arm/mach-omap2/clock24xx.h         |    1 
 arch/arm/mach-omap2/clock34xx.h         |    5 +
 arch/arm/plat-omap/include/mach/clock.h |    1 
 4 files changed, 99 insertions(+), 26 deletions(-)

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html