Fix sprz319 erratum 2.1
[pandora-kernel.git] / arch / arm / mach-omap2 / clock3xxx.c
index 952c3e0..d5be086 100644 (file)
 /* needed by omap3_core_dpll_m2_set_rate() */
 struct clk *sdrc_ick_p, *arm_fck_p;
 
+struct dpll_settings {
+       int rate, m, n, f;
+};
+
+
+static int omap3_dpll5_apply_erratum21(struct clk *clk, struct clk *dpll5_m2)
+{
+       struct clk *sys_clk;
+       int i, rv;
+       static const struct dpll_settings precomputed[] = {
+               /* From DM3730 errata (sprz319e), table 36
+               * +1 is because the values in the table are register values;
+               * dpll_program() will subtract one from what we give it,
+               * so ...
+               */
+               { 13000000, 443+1, 5+1, 8 },
+               { 26000000, 443+1, 11+1, 8 }
+       };
+
+       sys_clk = clk_get(NULL, "sys_ck");
+
+       for (i = 0 ; i < (sizeof(precomputed)/sizeof(struct dpll_settings)) ;
+               ++i) {
+               const struct dpll_settings *d = &precomputed[i];
+               if (sys_clk->rate == d->rate) {
+                       rv =  omap3_noncore_dpll_program(clk, d->m , d->n, 0);
+                       if (rv)
+                               return 1;
+                       rv =  omap2_clksel_force_divisor(dpll5_m2 , d->f);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+int omap3_dpll5_set_rate(struct clk *clk, unsigned long rate)
+{
+       struct clk *dpll5_m2;
+       int rv;
+       dpll5_m2 = clk_get(NULL, "dpll5_m2_ck");
+
+       if (cpu_is_omap3630() && rate == DPLL5_FREQ_FOR_USBHOST &&
+               omap3_dpll5_apply_erratum21(clk, dpll5_m2)) {
+               return 1;
+       }
+       rv = omap3_noncore_dpll_set_rate(clk, rate);
+       if (rv)
+               goto out;
+       rv = clk_set_rate(dpll5_m2, rate);
+
+out:
+       return rv;
+}
+
 int omap3_dpll4_set_rate(struct clk *clk, unsigned long rate)
 {
        /*
@@ -59,19 +113,14 @@ int omap3_dpll4_set_rate(struct clk *clk, unsigned long rate)
 void __init omap3_clk_lock_dpll5(void)
 {
        struct clk *dpll5_clk;
-       struct clk *dpll5_m2_clk;
 
        dpll5_clk = clk_get(NULL, "dpll5_ck");
        clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST);
-       clk_enable(dpll5_clk);
 
-       /* Program dpll5_m2_clk divider for no division */
-       dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck");
-       clk_enable(dpll5_m2_clk);
-       clk_set_rate(dpll5_m2_clk, DPLL5_FREQ_FOR_USBHOST);
+       /* dpll5_m2_ck is now (grottily!) handled by dpll5_clk's set routine,
+        * to cope with an erratum on DM3730
+        */
 
-       clk_disable(dpll5_m2_clk);
-       clk_disable(dpll5_clk);
        return;
 }