From d6cb5b5fe7c6049a0099b1a722211110f9cdf5da Mon Sep 17 00:00:00 2001
From: Teknoman117 <linux.robotdude@gmail.com>
Date: Wed, 24 Jul 2013 17:25:46 -0700
Subject: [PATCH] hack: omap: clockk: dpll5: apply sprz319e 2.1 erratum
 (compatible with v3.11-rc2)

Signed-off-by: Teknoman117 <linux.robotdude@gmail.com>
---
 arch/arm/mach-omap2/cclock3xxx_data.c | 19 +++++++++-
 arch/arm/mach-omap2/clkt_dpll.c       | 68 +++++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/clock.h           |  2 ++
 arch/arm/mach-omap2/clock3xxx.c       |  9 ++++-
 4 files changed, 96 insertions(+), 2 deletions(-)

diff --git a/arch/arm/mach-omap2/cclock3xxx_data.c b/arch/arm/mach-omap2/cclock3xxx_data.c
index 334b767..60fa6bd 100644
--- a/arch/arm/mach-omap2/cclock3xxx_data.c
+++ b/arch/arm/mach-omap2/cclock3xxx_data.c
@@ -250,7 +250,9 @@ static struct dpll_data dpll1_dd = {
 
 static struct clk dpll1_ck;
 
-static const struct clk_ops dpll1_ck_ops = {
+static struct clk_ops dpll1_ck_ops;
+
+static struct clk_ops dpll1_ck_ops_34xx __initdata = {
 	.init		= &omap2_init_clk_clkdm,
 	.enable		= &omap3_noncore_dpll_enable,
 	.disable	= &omap3_noncore_dpll_disable,
@@ -260,6 +262,16 @@ static const struct clk_ops dpll1_ck_ops = {
 	.round_rate	= &omap2_dpll_round_rate,
 };
 
+static struct clk_ops dpll5_ck_ops_3630 __initdata = {
+	.init		= &omap2_init_clk_clkdm,
+	.enable		= &omap3_noncore_dpll_enable,
+	.disable	= &omap3_noncore_dpll_disable,
+	.get_parent	= &omap2_init_dpll_parent,
+	.recalc_rate	= &omap3_dpll_recalc,
+	.set_rate	= &omap3_noncore_dpll_set_rate,
+	.round_rate	= &omap2_dpll5_round_rate,
+};
+
 static struct clk_hw_omap dpll1_ck_hw = {
 	.hw = {
 		.clk = &dpll1_ck,
@@ -3560,6 +3572,11 @@ int __init omap3xxx_clk_init(void)
 	else
 		dpll4_dd = dpll4_dd_34xx;
 
+	if (cpu_is_omap3630())
+		dpll1_ck_ops = dpll5_ck_ops_3630;
+	else
+		dpll1_ck_ops = dpll1_ck_ops_34xx;
+
 
 	/*
 	 * 3505 must be tested before 3517, since 3517 returns true
diff --git a/arch/arm/mach-omap2/clkt_dpll.c b/arch/arm/mach-omap2/clkt_dpll.c
index 924c230..5100d46 100644
--- a/arch/arm/mach-omap2/clkt_dpll.c
+++ b/arch/arm/mach-omap2/clkt_dpll.c
@@ -63,6 +63,15 @@
 #define DPLL_FINT_UNDERFLOW		-1
 #define DPLL_FINT_INVALID		-2
 
+/* copied from clock3xxx.c */
+/*
+ * DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks
+ * that are sourced by DPLL5, and both of these require this clock
+ * to be at 120 MHz for proper operation.
+ */
+#define DPLL5_FREQ_FOR_USBHOST		120000000
+
+
 /* Private functions */
 
 /*
@@ -362,3 +371,62 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
 	return target_rate;
 }
 
+struct sprz319e_2_1_values {
+	unsigned long sys_clk_rate;
+	int m, n, div120m;
+};
+
+/**
+ * omap2_dpll5_round_rate - round a target rate for OMAP DPLL5
+ * according to DM37xx sprz319e 2.1 erratum
+ *
+ * @clk: struct clk * for a DPLL (presumably DPLL5)
+ * @target_rate: desired DPLL clock rate
+ *
+ * The erratum applies only for DM37xx, desired clock rates of
+ * div120m times 120 MHz and specified sys_clks.
+ */
+long omap2_dpll5_round_rate(struct clk_hw *hw, unsigned long target_rate,
+		unsigned long *parent_rate)
+{
+	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
+	struct dpll_data *dd;
+	const char *clk_name;
+	int i;
+
+	/* erratum tables */
+	const struct sprz319e_2_1_values sprz319e_2_1_table[] = {
+		/* table 35 */
+		{ 12000000,  80,  0, 8 },
+		{ 19200000,  50,  0, 8 },
+		{ 38400000,  25,  0, 8 },
+		/* table 36 */
+		{ 13000000, 443,  5, 8 },
+		{ 26000000, 443, 11, 8 },
+	};
+
+	if (!clk || !clk->dpll_data)
+		return ~0;
+
+	dd = clk->dpll_data;
+
+	clk_name = __clk_get_name(hw->clk);
+
+	for (i = 0; i < (sizeof(sprz319e_2_1_table)/sizeof(struct sprz319e_2_1_values)); i++) {
+		const struct sprz319e_2_1_values *v = &sprz319e_2_1_table[i];
+
+		if (*parent_rate == v->sys_clk_rate &&
+			target_rate == DPLL5_FREQ_FOR_USBHOST * v->div120m) {
+
+			pr_info("clock: dpll5: Applying SPRZ319E 2.1: %8lu, %3d, %3d, %3d\n",
+				v->sys_clk_rate, v->m, v->n, v->div120m);
+
+			dd->last_rounded_m = v->m;
+			dd->last_rounded_n = v->n + 1;
+			dd->last_rounded_rate = v->sys_clk_rate * v->m / (v->n + 1);
+
+			return dd->last_rounded_rate;
+		}
+	}
+	return target_rate;
+}
diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h
index 7aa32cd..56a50a7 100644
--- a/arch/arm/mach-omap2/clock.h
+++ b/arch/arm/mach-omap2/clock.h
@@ -358,6 +358,8 @@ unsigned long omap_fixed_divisor_recalc(struct clk_hw *hw,
 
 long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate,
 			unsigned long *parent_rate);
+long omap2_dpll5_round_rate(struct clk_hw *hw, unsigned long target_rate,
+			unsigned long *parent_rate);
 unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate);
 int omap3_noncore_dpll_enable(struct clk_hw *hw);
 void omap3_noncore_dpll_disable(struct clk_hw *hw);
diff --git a/arch/arm/mach-omap2/clock3xxx.c b/arch/arm/mach-omap2/clock3xxx.c
index 0b02b41..8ccf7c6 100644
--- a/arch/arm/mach-omap2/clock3xxx.c
+++ b/arch/arm/mach-omap2/clock3xxx.c
@@ -60,7 +60,14 @@ void __init omap3_clk_lock_dpll5(void)
 	struct clk *dpll5_m2_clk;
 
 	dpll5_clk = clk_get(NULL, "dpll5_ck");
-	clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST);
+
+	if (cpu_is_omap3630()) {
+		pr_info("clock: dpll5: dpll5_clk omap3630\n");
+		clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST * 8);
+	} else {
+		clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST);
+	}
+
 	clk_prepare_enable(dpll5_clk);
 
 	/* Program dpll5_m2_clk divider for no division */
-- 
1.8.4.rc3