diff mbox

Improve DPLL frequency rounding code

Message ID 1322024211.1796.24.camel@dave-Dell-System-XPS-L502X
State New
Headers show

Commit Message

David Long Nov. 23, 2011, 4:56 a.m. UTC
Maintain a list of successful frequency translations for any given DPLL.
Speed up frequency calculations by searching the list first.
Do a more exhaustive search for the best possible DPLL paramters for a
new frequency.
If an exact fit is not found, return the best possible match within jitter
constraints.
Change the worst case match criteria from +/-1MHZ to +/-0.01% (on the 4460
350MHz will be off by 0.003%).

Signed-off-by: David A. Long <dave.long@linaro.org>
---
 arch/arm/mach-omap2/clkt_dpll.c         |   75 ++++++++++++++++++++++++++-----
 arch/arm/plat-omap/include/plat/clock.h |   25 +++++++++-
 2 files changed, 86 insertions(+), 14 deletions(-)

Comments

warmcat Nov. 23, 2011, 5:02 a.m. UTC | #1
On 11/23/2011 12:56 PM, Somebody in the thread at some point said:
> +#ifdef DEBUG
> +		pr_err("clock: target=%ld %s: m = %d: n = %d: new_rate = %ld\n",
> +			 target_rate, clk->name, m, n, new_rate);
> +#endif

This should just be pr_debug()

+	if (bestdiff < (ULONG_MAX/10000) &&
+	    ((bestdiff*10000)/target_rate) < 1) {

checkpatch should have caught no spaces around operators

Otherwise it's looking way better with kernel lists and size limit.

Can you fix those and send me again... it might seem picky but you will 
be instantly judged on this stuff by the recipient inside TI.

-Andy
diff mbox

Patch

diff --git a/arch/arm/mach-omap2/clkt_dpll.c b/arch/arm/mach-omap2/clkt_dpll.c
index f95c2ef..e23cdf8 100644
--- a/arch/arm/mach-omap2/clkt_dpll.c
+++ b/arch/arm/mach-omap2/clkt_dpll.c
@@ -17,6 +17,8 @@ 
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/clk.h>
+#include <linux/list.h>
+#include <linux/slab.h>
 #include <linux/io.h>
 
 #include <asm/div64.h>
@@ -276,12 +278,28 @@  long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
 	unsigned long scaled_rt_rp;
 	unsigned long new_rate = 0;
 	struct dpll_data *dd;
+	unsigned long bestrate = 0, diff, bestdiff = ULONG_MAX;
+	int bestm = 0, bestn = 0;
+	struct list_head *lp;
+	struct dpll_rate_list *rs;
 
 	if (!clk || !clk->dpll_data)
 		return ~0;
 
 	dd = clk->dpll_data;
 
+	/* It is assumed locking is handled by the caller */
+	if (dd->rate_cache_len > 0)
+		list_for_each(lp, &dd->rate_cache) {
+			rs = container_of(lp, struct dpll_rate_list, list);
+			if (rs->target_rate == target_rate) {
+				dd->last_rounded_m = rs->m;
+				dd->last_rounded_n = rs->n;
+				dd->last_rounded_rate = rs->actual_rate;
+				return rs->actual_rate;
+			}
+		}
+
 	pr_debug("clock: %s: starting DPLL round_rate, target rate %ld\n",
 		 clk->name, target_rate);
 
@@ -318,19 +336,52 @@  long omap2_dpll_round_rate(struct clk *clk, unsigned long target_rate)
 		if (r == DPLL_MULT_UNDERFLOW)
 			continue;
 
-//		pr_err("    clock: target=%ld %s: m = %d: n = %d: new_rate = %ld\n",
-//			 target_rate, clk->name, m, n, new_rate);
-
-		if (target_rate == new_rate ||
-			((target_rate - new_rate) > 0 && (target_rate - new_rate) < 1000000)
-		) {
-			dd->last_rounded_m = m;
-			dd->last_rounded_n = n;
-			dd->last_rounded_rate = target_rate;
-			break;
+#ifdef DEBUG
+		pr_err("clock: target=%ld %s: m = %d: n = %d: new_rate = %ld\n",
+			 target_rate, clk->name, m, n, new_rate);
+#endif
+
+		if (target_rate > new_rate)
+			diff = target_rate - new_rate;
+		else
+			diff = new_rate - target_rate;
+		if (diff < bestdiff) {
+			bestm = m;
+			bestn = n;
+			bestrate = new_rate;
+			bestdiff = diff;
 		}
+		if (new_rate == target_rate)
+			break;
 	}
-
-	return new_rate;
+	/*
+	 * The following if verifies that the new frequency is within 0.01% of
+	 * the target frequency.
+	 */
+	if (bestdiff < (ULONG_MAX/10000) &&
+	    ((bestdiff*10000)/target_rate) < 1) {
+		dd->last_rounded_m = bestm;
+		dd->last_rounded_n = bestn;
+		dd->last_rounded_rate = bestrate;
+
+		if (dd->rate_cache_len >= DPLL_MAX_RATE_CACHE)
+			return bestrate;
+		/*
+		 * Add new rate to the cache
+		 */
+		rs = kzalloc(sizeof(struct dpll_rate_list), GFP_ATOMIC);
+		if (rs) {
+			rs->m = bestm;
+			rs->n = bestn;
+			rs->target_rate = target_rate;
+			rs->actual_rate = bestrate;
+			if (dd->rate_cache_len == 0)
+				INIT_LIST_HEAD(&dd->rate_cache);
+			list_add(&rs->list, &dd->rate_cache);
+			dd->rate_cache_len++;
+		}
+		return bestrate;
+	} else
+		return 0;
 }
 
diff --git a/arch/arm/plat-omap/include/plat/clock.h b/arch/arm/plat-omap/include/plat/clock.h
index 6e724d5..2278230 100644
--- a/arch/arm/plat-omap/include/plat/clock.h
+++ b/arch/arm/plat-omap/include/plat/clock.h
@@ -104,6 +104,23 @@  struct clksel {
 };
 
 /**
+ * struct dpll_rate_list - optional list of frequency translations
+ * @m: dpll multiplier value
+ * @n: dpll divisor value
+ * @target_rate: desired clock frequency
+ * @actual_frequency: rate caluclated from best multiplier/divisor combination
+ */
+struct dpll_rate_list {
+	struct list_head list;
+	int m;
+	int n;
+	unsigned long target_rate;
+	unsigned long actual_rate;
+};
+
+#define DPLL_MAX_RATE_CACHE	10
+
+/**
  * struct dpll_data - DPLL registers and integration data
  * @mult_div1_reg: register containing the DPLL M and N bitfields
  * @mult_mask: mask of the DPLL M bitfield in @mult_div1_reg
@@ -160,6 +177,8 @@  struct dpll_data {
 #if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_ARCH_OMAP4)
 	void __iomem		*autoidle_reg;
 	void __iomem		*idlest_reg;
+	struct list_head	rate_cache;
+	int			rate_cache_len;
 	u32			autoidle_mask;
 	u32			freqsel_mask;
 	u32			idlest_mask;
@@ -285,8 +304,10 @@  struct clk_functions {
 	void		(*clk_deny_idle)(struct clk *clk);
 	void		(*clk_disable_unused)(struct clk *clk);
 #ifdef CONFIG_CPU_FREQ
-	void		(*clk_init_cpufreq_table)(struct cpufreq_frequency_table **);
-	void		(*clk_exit_cpufreq_table)(struct cpufreq_frequency_table **);
+	void		(*clk_init_cpufreq_table)
+				(struct cpufreq_frequency_table **);
+	void		(*clk_exit_cpufreq_table)
+				(struct cpufreq_frequency_table **);
 #endif
 };