diff mbox

[v2] Improve DPLL frequency rounding code

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

Commit Message

David Long Nov. 23, 2011, 5:36 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         |   73 ++++++++++++++++++++++++++-----
 arch/arm/plat-omap/include/plat/clock.h |   25 ++++++++++-
 2 files changed, 84 insertions(+), 14 deletions(-)
diff mbox

Patch

diff --git a/arch/arm/mach-omap2/clkt_dpll.c b/arch/arm/mach-omap2/clkt_dpll.c
index f95c2ef..57e0cb1 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,50 @@  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;
+		pr_debug("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)
+			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
 };