]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/arm/mach-omap2/clock.c
ARM: OMAP: Add support for forcing osc_ck on
[linux-2.6-omap-h63xx.git] / arch / arm / mach-omap2 / clock.c
index 72eb4bf571acf11abb6cafe89e65fcfa2274c2e2..737aca4cff136f43711a64753f2ce72147e2ee4d 100644 (file)
@@ -15,7 +15,6 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
@@ -37,6 +36,8 @@
 
 static struct prcm_config *curr_prcm_set;
 static u32 curr_perf_level = PRCM_FULL_SPEED;
+static struct clk *vclk;
+static struct clk *sclk;
 
 /*-------------------------------------------------------------------------
  * Omap2 specific clock functions
@@ -80,6 +81,14 @@ static void omap2_propagate_rate(struct clk * clk)
        propagate_rate(clk);
 }
 
+static void omap2_set_osc_ck(int enable)
+{
+       if (enable)
+               PRCM_CLKSRC_CTRL &= ~(0x3 << 3);
+       else
+               PRCM_CLKSRC_CTRL |= 0x3 << 3;
+}
+
 /* Enable an APLL if off */
 static void omap2_clk_fixed_enable(struct clk *clk)
 {
@@ -102,7 +111,7 @@ static void omap2_clk_fixed_enable(struct clk *clk)
        else if (clk == &apll54_ck)
                cval = (1 << 6);
 
-       while (!CM_IDLEST_CKGEN & cval) {               /* Wait for lock */
+       while (!(CM_IDLEST_CKGEN & cval)) {             /* Wait for lock */
                ++i;
                udelay(1);
                if (i == 100000)
@@ -120,6 +129,11 @@ static int _omap2_clk_enable(struct clk * clk)
        if (clk->flags & ALWAYS_ENABLED)
                return 0;
 
+       if (unlikely(clk == &osc_ck)) {
+               omap2_set_osc_ck(1);
+               return 0;
+       }
+
        if (unlikely(clk->enable_reg == 0)) {
                printk(KERN_ERR "clock.c: Enable for %s without enable code\n",
                       clk->name);
@@ -134,6 +148,7 @@ static int _omap2_clk_enable(struct clk * clk)
        regval32 = __raw_readl(clk->enable_reg);
        regval32 |= (1 << clk->enable_bit);
        __raw_writel(regval32, clk->enable_reg);
+       wmb();
 
        return 0;
 }
@@ -156,6 +171,11 @@ static void _omap2_clk_disable(struct clk *clk)
 {
        u32 regval32;
 
+       if (unlikely(clk == &osc_ck)) {
+               omap2_set_osc_ck(0);
+               return;
+       }
+
        if (clk->enable_reg == 0)
                return;
 
@@ -167,6 +187,7 @@ static void _omap2_clk_disable(struct clk *clk)
        regval32 = __raw_readl(clk->enable_reg);
        regval32 &= ~(1 << clk->enable_bit);
        __raw_writel(regval32, clk->enable_reg);
+       wmb();
 }
 
 static int omap2_clk_enable(struct clk *clk)
@@ -660,26 +681,35 @@ static int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
 
                /* Isolate control register */
                div_sel = (SRC_RATE_SEL_MASK & clk->flags);
-               div_off = clk->src_offset;
+               div_off = clk->rate_offset;
 
                validrate = omap2_clksel_round_rate(clk, rate, &new_div);
-               if(validrate != rate)
+               if (validrate != rate)
                        return(ret);
 
                field_val = omap2_get_clksel(&div_sel, &field_mask, clk);
                if (div_sel == 0)
                        return ret;
 
-               if(clk->flags & CM_SYSCLKOUT_SEL1){
-                       switch(new_div){
-                       case 16: field_val = 4; break;
-                       case 8:  field_val = 3; break;
-                       case 4:  field_val = 2; break;
-                       case 2:  field_val = 1; break;
-                       case 1:  field_val = 0; break;
+               if (clk->flags & CM_SYSCLKOUT_SEL1) {
+                       switch (new_div) {
+                       case 16:
+                               field_val = 4;
+                               break;
+                       case 8:
+                               field_val = 3;
+                               break;
+                       case 4:
+                               field_val = 2;
+                               break;
+                       case 2:
+                               field_val = 1;
+                               break;
+                       case 1:
+                               field_val = 0;
+                               break;
                        }
-               }
-               else
+               } else
                        field_val = new_div;
 
                reg = (void __iomem *)div_sel;
@@ -687,12 +717,14 @@ static int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
                reg_val = __raw_readl(reg);
                reg_val &= ~(field_mask << div_off);
                reg_val |= (field_val << div_off);
-
                __raw_writel(reg_val, reg);
+               wmb();
                clk->rate = clk->parent->rate / field_val;
 
-               if (clk->flags & DELAYED_APP)
+               if (clk->flags & DELAYED_APP) {
                        __raw_writel(0x1, (void __iomem *)&PRCM_CLKCFG_CTRL);
+                       wmb();
+               }
                ret = 0;
        } else if (clk->set_rate != 0)
                ret = clk->set_rate(clk, rate);
@@ -744,7 +776,7 @@ static u32 omap2_get_src_field(u32 *type_to_addr, u32 reg_offset,
                        val = 0x2;
                break;
        case CM_WKUP_SEL1:
-               src_reg_addr = (u32)&CM_CLKSEL2_CORE;
+               src_reg_addr = (u32)&CM_CLKSEL_WKUP;
                mask = 0x3;
                if (src_clk == &func_32k_ck)
                        val = 0x0;
@@ -784,9 +816,9 @@ static u32 omap2_get_src_field(u32 *type_to_addr, u32 reg_offset,
                        val = 0;
                if (src_clk == &sys_ck)
                        val = 1;
-               if (src_clk == &func_54m_ck)
-                       val = 2;
                if (src_clk == &func_96m_ck)
+                       val = 2;
+               if (src_clk == &func_54m_ck)
                        val = 3;
                break;
        }
@@ -828,10 +860,12 @@ static int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)
                reg_val = __raw_readl(reg) & ~(field_mask << src_off);
                reg_val |= (field_val << src_off);
                __raw_writel(reg_val, reg);
+               wmb();
 
-               if (clk->flags & DELAYED_APP)
+               if (clk->flags & DELAYED_APP) {
                        __raw_writel(0x1, (void __iomem *)&PRCM_CLKCFG_CTRL);
-
+                       wmb();
+               }
                if (clk->usecount > 0)
                        _omap2_clk_enable(clk);
 
@@ -976,6 +1010,20 @@ static void __init omap2_get_crystal_rate(struct clk *osc, struct clk *sys)
        sys->rate = sclk;
 }
 
+/*
+ * Set clocks for bypass mode for reboot to work.
+ */
+void omap2_clk_prepare_for_reboot(void)
+{
+       u32 rate;
+
+       if (vclk == NULL || sclk == NULL)
+               return;
+
+       rate = clk_get_rate(sclk);
+       clk_set_rate(vclk, rate);
+}
+
 #ifdef CONFIG_OMAP_RESET_CLOCKS
 static void __init omap2_disable_unused_clocks(void)
 {
@@ -1072,5 +1120,9 @@ int __init omap2_clk_init(void)
        if (cpu_is_omap2430())
                clk_enable(&sdrc_ick);
 
+       /* Avoid sleeping sleeping during omap2_clk_prepare_for_reboot() */
+       vclk = clk_get(NULL, "virt_prcm_set");
+       sclk = clk_get(NULL, "sys_ck");
+
        return 0;
 }