From 3ff51ed8287d3d2c7899f18f0487348e3448afb5 Mon Sep 17 00:00:00 2001
From: Jon Hunter <jon-hunter@ti.com>
Date: Sat, 15 Dec 2012 01:35:46 -0700
Subject: ARM: OMAP4: Enhance support for DPLLs with 4X multiplier

On OMAP4 devices, the ABE DPLL has an internal 4X multiplier that can
be enabled or disabled in addition to the standard configurable
multiplier (M) for OMAP DPLLs. When configuring the ABE DPLL the 4X
multiplier is accounted for by checking to see whether it is enabled or
not. However, when calculating a new rate we only check to see if the
rate can be achieved with the current setting for the 4X multiplier.
Enhance the round_rate() function for such DPLLs to see if the rate
can be achieved with the 4X multiplier if it cannot be achieved without
the 4X multiplier.

This change is necessary, because when using the 32kHz clock as the
source clock for the ABE DPLL, the default DPLL frequency for the ABE
DPLL cannot be achieved without enabling the 4X multiplier.

When using the 32kHz clock as the source clock for the ABE DPLL and
attempting to lock the DPLL to 98.304MHz (default frequency), it was
found that the DPLL would fail to lock if the low-power mode for the DPLL
was not enabled. From reviewing boot-loader settings that configure the
ABE DPLL it was found that the low-power mode is enabled when using the
32kHz clock source, however, the documentation for OMAP does not state
that this is a requirement. Therefore, introduce a new function for
OMAP4 devices to see if low-power mode can be enabled when calculating a
new rate to ensure the DPLL will lock.

New variables for the last calculated 4X multiplier and low-power
setting have been added to the dpll data structure as well as variables
defining the bit mask for enabling these features via the DPLL's
control_reg. It is possible that we could eliminate these bit masks from
the dpll data structure as these bit masks are not unique to OMAP4, if
it is preferred.

The function omap3_noncore_program_dpll() has been updated to avoid
passing the calculated values for the multiplier (M) and divider (N) as
these are stored in the clk structure.

Signed-off-by: Jon Hunter <jon-hunter@ti.com>
Signed-off-by: Paul Walmsley <paul@pwsan.com>
---
 arch/arm/mach-omap2/dpll44xx.c | 64 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 55 insertions(+), 9 deletions(-)

(limited to 'arch/arm/mach-omap2/dpll44xx.c')

diff --git a/arch/arm/mach-omap2/dpll44xx.c b/arch/arm/mach-omap2/dpll44xx.c
index d3326c4..d28b0f7 100644
--- a/arch/arm/mach-omap2/dpll44xx.c
+++ b/arch/arm/mach-omap2/dpll44xx.c
@@ -20,6 +20,15 @@
 #include "clock44xx.h"
 #include "cm-regbits-44xx.h"
 
+/*
+ * Maximum DPLL input frequency (FINT) and output frequency (FOUT) that
+ * can supported when using the DPLL low-power mode. Frequencies are
+ * defined in OMAP4430/60 Public TRM section 3.6.3.3.2 "Enable Control,
+ * Status, and Low-Power Operation Mode".
+ */
+#define OMAP4_DPLL_LP_FINT_MAX	1000000
+#define OMAP4_DPLL_LP_FOUT_MAX	100000000
+
 /* Supported only on OMAP4 */
 int omap4_dpllmx_gatectrl_read(struct clk_hw_omap *clk)
 {
@@ -82,6 +91,31 @@ const struct clk_hw_omap_ops clkhwops_omap4_dpllmx = {
 };
 
 /**
+ * omap4_dpll_lpmode_recalc - compute DPLL low-power setting
+ * @dd: pointer to the dpll data structure
+ *
+ * Calculates if low-power mode can be enabled based upon the last
+ * multiplier and divider values calculated. If low-power mode can be
+ * enabled, then the bit to enable low-power mode is stored in the
+ * last_rounded_lpmode variable. This implementation is based upon the
+ * criteria for enabling low-power mode as described in the OMAP4430/60
+ * Public TRM section 3.6.3.3.2 "Enable Control, Status, and Low-Power
+ * Operation Mode".
+ */
+static void omap4_dpll_lpmode_recalc(struct dpll_data *dd)
+{
+	long fint, fout;
+
+	fint = __clk_get_rate(dd->clk_ref) / (dd->last_rounded_n + 1);
+	fout = fint * dd->last_rounded_m;
+
+	if ((fint < OMAP4_DPLL_LP_FINT_MAX) && (fout < OMAP4_DPLL_LP_FOUT_MAX))
+		dd->last_rounded_lpmode = 1;
+	else
+		dd->last_rounded_lpmode = 0;
+}
+
+/**
  * omap4_dpll_regm4xen_recalc - compute DPLL rate, considering REGM4XEN bit
  * @clk: struct clk * of the DPLL to compute the rate for
  *
@@ -130,7 +164,6 @@ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,
 				    unsigned long *parent_rate)
 {
 	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
-	u32 v;
 	struct dpll_data *dd;
 	long r;
 
@@ -139,18 +172,31 @@ long omap4_dpll_regm4xen_round_rate(struct clk_hw *hw,
 
 	dd = clk->dpll_data;
 
-	/* regm4xen adds a multiplier of 4 to DPLL calculations */
-	v = __raw_readl(dd->control_reg) & OMAP4430_DPLL_REGM4XEN_MASK;
-
-	if (v)
-		target_rate = target_rate / OMAP4430_REGM4XEN_MULT;
+	dd->last_rounded_m4xen = 0;
 
+	/*
+	 * First try to compute the DPLL configuration for
+	 * target rate without using the 4X multiplier.
+	 */
 	r = omap2_dpll_round_rate(hw, target_rate, NULL);
+	if (r != ~0)
+		goto out;
+
+	/*
+	 * If we did not find a valid DPLL configuration, try again, but
+	 * this time see if using the 4X multiplier can help. Enabling the
+	 * 4X multiplier is equivalent to dividing the target rate by 4.
+	 */
+	r = omap2_dpll_round_rate(hw, target_rate / OMAP4430_REGM4XEN_MULT,
+				  NULL);
 	if (r == ~0)
 		return r;
 
-	if (v)
-		clk->dpll_data->last_rounded_rate *= OMAP4430_REGM4XEN_MULT;
+	dd->last_rounded_rate *= OMAP4430_REGM4XEN_MULT;
+	dd->last_rounded_m4xen = 1;
+
+out:
+	omap4_dpll_lpmode_recalc(dd);
 
-	return clk->dpll_data->last_rounded_rate;
+	return dd->last_rounded_rate;
 }
-- 
cgit v1.1