]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00221440 MX6x-Fix race-condition in checking bus_freq variables
authorRanjani Vaidyanathan <ra5478@freescale.com>
Sat, 25 Aug 2012 05:14:51 +0000 (00:14 -0500)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:35:19 +0000 (08:35 +0200)
Checking of the bus_freq variables and changing of the
bus/ddr frequency should be done under one mutex.
Else there is a race-condition that the variable changed
just after it was checked.
Also ensure that the bus freq is always increased before
the cpu freq is set to anything other than the lowest setpoint.
Else there is a possibility that the ARM is set to run from
PLL1 at higher frequency when bus/DDR are still at 24MHz.
This is dangerous since when system enters WAIT mode in
low bus freq state, PLL1 is set to bypass when ARM is being
sourced from it.

Signed-off-by: Ranjani Vaidyanathan <ra5478@freescale.com>
arch/arm/mach-mx6/bus_freq.c
arch/arm/mach-mx6/clock.c
arch/arm/mach-mx6/clock_mx6sl.c
arch/arm/plat-mxc/cpufreq.c

index bde499dd02a9c6c8e3ecd2b64962efc9e4710d04..53a65fb893f6fdd7204f4cb7d157d0e6e73f51f2 100644 (file)
@@ -69,6 +69,7 @@ int bus_freq_scaling_is_active;
 int lp_high_freq;
 int lp_med_freq;
 int lp_audio_freq;
+int high_cpu_freq;
 unsigned int ddr_low_rate;
 unsigned int ddr_med_rate;
 unsigned int ddr_normal_rate;
@@ -194,7 +195,6 @@ static void reduce_bus_freq_handler(struct work_struct *work)
 
                spin_unlock_irqrestore(&freq_lock, flags);
        }
-
        high_bus_freq_mode = 0;
        mutex_unlock(&bus_freq_mutex);
 }
@@ -225,40 +225,34 @@ int set_high_bus_freq(int high_bus_freq)
 {
        if (bus_freq_scaling_initialized && bus_freq_scaling_is_active)
                cancel_delayed_work_sync(&low_bus_freq_handler);
-       mutex_lock(&bus_freq_mutex);
-       if (busfreq_suspended) {
-               mutex_unlock(&bus_freq_mutex);
+
+       if (busfreq_suspended)
                return 0;
-       }
 
-       if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active) {
-               mutex_unlock(&bus_freq_mutex);
+
+       if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active)
                return 0;
-       }
 
-       if (high_bus_freq_mode && high_bus_freq) {
-               mutex_unlock(&bus_freq_mutex);
+
+       if (high_bus_freq_mode && high_bus_freq)
                return 0;
-       }
 
-       if (med_bus_freq_mode && !high_bus_freq) {
-               mutex_unlock(&bus_freq_mutex);
+
+       /* medium bus freq is only supported for MX6DQ */
+       if (cpu_is_mx6q() && med_bus_freq_mode && !high_bus_freq)
                return 0;
-       }
 
        if (cpu_is_mx6dl() && high_bus_freq)
                high_bus_freq = 0;
 
-       if (cpu_is_mx6dl() && med_bus_freq_mode) {
-               mutex_unlock(&bus_freq_mutex);
+       if (cpu_is_mx6dl() && med_bus_freq_mode)
                return 0;
-       }
+
        if ((high_bus_freq_mode && (high_bus_freq || lp_high_freq)) ||
            (med_bus_freq_mode && !high_bus_freq && lp_med_freq &&
-            !lp_high_freq)) {
-               mutex_unlock(&bus_freq_mutex);
+            !lp_high_freq))
                return 0;
-       }
+
        if (cpu_is_mx6sl()) {
                u32 reg;
                unsigned long flags;
@@ -321,8 +315,6 @@ int set_high_bus_freq(int high_bus_freq)
 
                clk_disable(pll3);
        }
-
-       mutex_unlock(&bus_freq_mutex);
        return 0;
 }
 
@@ -334,11 +326,8 @@ int low_freq_bus_used(void)
        /* We only go the lowest setpoint if ARM is also
         * at the lowest setpoint.
         */
-       if ((clk_get_rate(cpu_clk) >
-                       cpu_op_tbl[cpu_op_nr - 1].cpu_rate)
-               || (cpu_op_nr == 1)) {
+       if (high_cpu_freq)
                return 0;
-       }
 
        if ((lp_high_freq == 0)
            && (lp_med_freq == 0))
@@ -350,62 +339,80 @@ int low_freq_bus_used(void)
 void bus_freq_update(struct clk *clk, bool flag)
 {
        mutex_lock(&bus_freq_mutex);
+
        if (flag) {
-               /* Update count */
-               if (clk->flags & AHB_HIGH_SET_POINT)
-                       lp_high_freq++;
-               else if (clk->flags & AHB_MED_SET_POINT)
-                       lp_med_freq++;
-               else if (clk->flags & AHB_AUDIO_SET_POINT)
-                       lp_audio_freq++;
-               /* Update bus freq */
-               if ((clk->flags & CPU_FREQ_TRIG_UPDATE)
-                       && (clk_get_usecount(clk) == 0)) {
-                       if (!(clk->flags &
-                               (AHB_HIGH_SET_POINT | AHB_MED_SET_POINT)))  {
-                       if (low_freq_bus_used()) {
-                               if ((clk->flags & AHB_AUDIO_SET_POINT) & !audio_bus_freq_mode)
-                                       set_low_bus_freq();
-                               else if (!low_bus_freq_mode)
-                                       set_low_bus_freq();
-                       }
-               } else {
-                       if ((clk->flags & AHB_MED_SET_POINT)
-                               && !med_bus_freq_mode) {
-                               /* Set to Medium setpoint */
-                               mutex_unlock(&bus_freq_mutex);
+               if (clk == cpu_clk) {
+                       /* The CPU freq is being increased.
+                         * check if we need to increase the bus freq
+                         */
+                       high_cpu_freq = 1;
+                       if (low_bus_freq_mode || audio_bus_freq_mode)
                                set_high_bus_freq(0);
-                               return;
-                       }
-                       else if ((clk->flags & AHB_HIGH_SET_POINT)
-                               && !high_bus_freq_mode) {
-                               /* Currently at low or medium set point,
-                               * need to set to high setpoint
-                               */
-                               mutex_unlock(&bus_freq_mutex);
-                               set_high_bus_freq(1);
-                               return;
-                       }
+               } else {
+                       /* Update count */
+                       if (clk->flags & AHB_HIGH_SET_POINT)
+                               lp_high_freq++;
+                       else if (clk->flags & AHB_MED_SET_POINT)
+                               lp_med_freq++;
+                       else if (clk->flags & AHB_AUDIO_SET_POINT)
+                               lp_audio_freq++;
+                       /* Update bus freq */
+                       if ((clk->flags & CPU_FREQ_TRIG_UPDATE)
+                               && (clk_get_usecount(clk) == 0)) {
+                               if (!(clk->flags &
+                                       (AHB_HIGH_SET_POINT | AHB_MED_SET_POINT))) {
+                                       if (low_freq_bus_used()) {
+                                               if ((clk->flags & AHB_AUDIO_SET_POINT) &
+                                                               !audio_bus_freq_mode)
+                                                       set_low_bus_freq();
+                                               else if (!low_bus_freq_mode)
+                                                       set_low_bus_freq();
+                                       }
+                               } else {
+                                       if ((clk->flags & AHB_MED_SET_POINT)
+                                               && !med_bus_freq_mode) {
+                                               /* Set to Medium setpoint */
+                                               set_high_bus_freq(0);
+                                       } else if ((clk->flags & AHB_HIGH_SET_POINT)
+                                               && !high_bus_freq_mode) {
+                                               /* Currently at low or medium
+                                                 * set point, need to set to
+                                                 * high setpoint
+                                                 */
+                                               set_high_bus_freq(1);
+                                       }
+                               }
                        }
                }
        } else {
-               /* Update count */
-               if (clk->flags & AHB_HIGH_SET_POINT)
-                       lp_high_freq--;
-               else if (clk->flags & AHB_MED_SET_POINT)
-                       lp_med_freq--;
-               else if (clk->flags & AHB_AUDIO_SET_POINT)
-                       lp_audio_freq--;
-               /* Update bus freq */
-               if ((clk->flags & CPU_FREQ_TRIG_UPDATE)
-                       && (clk_get_usecount(clk) == 0)) {
-                       if (low_freq_bus_used() && !low_bus_freq_mode)
+               if (clk == cpu_clk) {
+                       /* CPU freq is dropped, check if we can
+                         * lower the bus freq.
+                         */
+                       high_cpu_freq = 0;
+
+                       if (low_freq_bus_used() &&
+                               !(low_bus_freq_mode || audio_bus_freq_mode))
                                set_low_bus_freq();
-                       else {
-                               /* Set to either high or medium setpoint. */
-                               mutex_unlock(&bus_freq_mutex);
-                               set_high_bus_freq(0);
-                               return;
+               } else {
+                       /* Update count */
+                       if (clk->flags & AHB_HIGH_SET_POINT)
+                               lp_high_freq--;
+                       else if (clk->flags & AHB_MED_SET_POINT)
+                               lp_med_freq--;
+                       else if (clk->flags & AHB_AUDIO_SET_POINT)
+                               lp_audio_freq--;
+                       /* Update bus freq */
+                       if ((clk->flags & CPU_FREQ_TRIG_UPDATE)
+                               && (clk_get_usecount(clk) == 0)) {
+                               if (low_freq_bus_used())
+                                       set_low_bus_freq();
+                               else {
+                                       /* Set to either high or
+                                         * medium setpoint.
+                                         */
+                                       set_high_bus_freq(0);
+                               }
                        }
                }
        }
index 8835c20ad34b06945645205420f9b565636be9c1..e0348e4f2e33c956e4dcaaaf7c8cfa34f8574c8a 100644 (file)
@@ -472,7 +472,6 @@ static int _clk_pll_enable(struct clk *clk)
        pllbase = _get_pll_base(clk);
 
        reg = __raw_readl(pllbase);
-       reg &= ~ANADIG_PLL_BYPASS;
        reg &= ~ANADIG_PLL_POWER_DOWN;
 
        /* The 480MHz PLLs have the opposite definition for power bit. */
@@ -492,6 +491,7 @@ static int _clk_pll_enable(struct clk *clk)
 
        /* Enable the PLL output now*/
        reg = __raw_readl(pllbase);
+       reg &= ~ANADIG_PLL_BYPASS;
        reg |= ANADIG_PLL_ENABLE;
        __raw_writel(reg, pllbase);
 
@@ -1262,14 +1262,17 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate)
                  */
                if (pll1_sw_clk.parent != &pll2_pfd_400M) {
                        pll2_pfd_400M.enable(&pll2_pfd_400M);
+                       pll2_pfd_400M.usecount++;
                        arm_needs_pll2_400 = true;
                        pll1_sw_clk.set_parent(&pll1_sw_clk, &pll2_pfd_400M);
                        pll1_sw_clk.parent = &pll2_pfd_400M;
                }
        } else {
                /* Make sure PLL1 is enabled */
-               if (!pll1_enabled)
+               if (!pll1_enabled) {
                        pll1_sys_main_clk.enable(&pll1_sys_main_clk);
+                       pll1_sys_main_clk.usecount = 1;
+               }
                /* Make sure PLL1 rate is what we want */
                if (cpu_op_tbl[i].pll_rate != clk_get_rate(&pll1_sys_main_clk)) {
                        /* If pll1_sw_clk is from pll1_sys_main_clk, switch it */
@@ -1285,6 +1288,8 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate)
                /* Make sure pll1_sw_clk is from pll1_sys_main_clk */
                pll1_sw_clk.set_parent(&pll1_sw_clk, &pll1_sys_main_clk);
                pll1_sw_clk.parent = &pll1_sys_main_clk;
+               if (arm_needs_pll2_400)
+                       pll2_pfd_400M.usecount--;
                arm_needs_pll2_400 = false;
                if (pll2_pfd_400M.usecount == 0)
                        pll2_pfd_400M.disable(&pll2_pfd_400M);
@@ -1323,8 +1328,10 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate)
        while (__raw_readl(MXC_CCM_CDHIPR))
                ;
 
-       if (pll1_sys_main_clk.usecount == 1 && arm_needs_pll2_400)
+       if (pll1_sys_main_clk.usecount == 1 && arm_needs_pll2_400) {
                pll1_sys_main_clk.disable(&pll1_sys_main_clk);
+               pll1_sys_main_clk.usecount = 0;
+       }
 
        spin_unlock_irqrestore(&clk_lock, flags);
 
index 9c79f744de991f9b49bcf4337c7363d59cfd1ebd..d93320d59dd809171d9fa92be254385944deffa0 100755 (executable)
@@ -67,6 +67,7 @@ static struct clk pll7_usb_host_main_clk;
 static struct clk usdhc3_clk;
 static struct clk ipg_clk;
 static struct clk gpt_clk[];
+static struct clk ahb_clk;
 
 static struct cpu_op *cpu_op_tbl;
 static int cpu_op_nr;
@@ -412,6 +413,7 @@ static int _clk_pfd_enable(struct clk *clk)
        __raw_writel((1 << (clk->enable_shift + 7)),
                        (int)clk->enable_reg + 8);
 
+       udelay(3);
        return 0;
 }
 
@@ -430,7 +432,6 @@ static int _clk_pll_enable(struct clk *clk)
        pllbase = _get_pll_base(clk);
 
        reg = __raw_readl(pllbase);
-       reg &= ~ANADIG_PLL_BYPASS;
        reg &= ~ANADIG_PLL_POWER_DOWN;
 
        /* The 480MHz PLLs have the opposite definition for power bit. */
@@ -450,6 +451,7 @@ static int _clk_pll_enable(struct clk *clk)
 
        /* Enable the PLL output now*/
        reg = __raw_readl(pllbase);
+       reg &= ~ANADIG_PLL_BYPASS;
        reg |= ANADIG_PLL_ENABLE;
        __raw_writel(reg, pllbase);
 
@@ -1153,6 +1155,11 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate)
        if (i >= cpu_op_nr)
                return -EINVAL;
 
+       if (clk_get_rate(&ahb_clk) == 24000000) {
+               printk(KERN_INFO "we should not be here!!!!! AHB is at 24MHz....cpu_rate requested = %ld\n", rate);
+               dump_stack();
+               BUG();
+       }
        spin_lock_irqsave(&mx6sl_clk_lock, flags);
 
        if (rate <= clk_get_rate(&pll2_pfd2_400M)) {
@@ -1161,14 +1168,17 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate)
                  */
                if (pll1_sw_clk.parent != &pll2_pfd2_400M) {
                        pll2_pfd2_400M.enable(&pll2_pfd2_400M);
+                       pll2_pfd2_400M.usecount++;
                        arm_needs_pll2_400 = true;
                        pll1_sw_clk.set_parent(&pll1_sw_clk, &pll2_pfd2_400M);
                        pll1_sw_clk.parent = &pll2_pfd2_400M;
                }
        } else {
                /* Make sure PLL1 is enabled */
-               if (!pll1_enabled)
+               if (!pll1_enabled) {
                        pll1_sys_main_clk.enable(&pll1_sys_main_clk);
+                       pll1_sys_main_clk.usecount = 1;
+               }
                if (cpu_op_tbl[i].pll_rate != clk_get_rate(&pll1_sys_main_clk)) {
                        if (pll1_sw_clk.parent == &pll1_sys_main_clk) {
                                /* Change the PLL1 rate. */
@@ -1177,15 +1187,13 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate)
                                else
                                        pll1_sw_clk.set_parent(&pll1_sw_clk, &osc_clk);
                        }
-                       if (cpu_op_tbl[i].cpu_podf) {
-                               __raw_writel(cpu_op_tbl[i].cpu_podf, MXC_CCM_CACRR);
-                               while (__raw_readl(MXC_CCM_CDHIPR))
-                                                       ;
-                       }
                        pll1_sys_main_clk.set_rate(&pll1_sys_main_clk, cpu_op_tbl[i].pll_rate);
                }
                pll1_sw_clk.set_parent(&pll1_sw_clk, &pll1_sys_main_clk);
                pll1_sw_clk.parent = &pll1_sys_main_clk;
+
+               if (arm_needs_pll2_400)
+                       pll2_pfd2_400M.usecount--;
                arm_needs_pll2_400 = false;
                if (pll2_pfd2_400M.usecount == 0)
                        pll2_pfd2_400M.disable(&pll2_pfd2_400M);
@@ -1224,9 +1232,10 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate)
        while (__raw_readl(MXC_CCM_CDHIPR))
                ;
 
-       if (pll1_sys_main_clk.usecount == 1 && arm_needs_pll2_400)
+       if (pll1_sys_main_clk.usecount == 1 && arm_needs_pll2_400) {
                pll1_sys_main_clk.disable(&pll1_sys_main_clk);
-
+               pll1_sys_main_clk.usecount = 0;
+       }
        spin_unlock_irqrestore(&mx6sl_clk_lock, flags);
 
        return 0;
index ee041507038ead93746a48a5f1338cfd642c44a5..b6c1e28e4aca46f36c74f461148e49936f6cd01e 100755 (executable)
@@ -58,13 +58,7 @@ extern struct regulator *soc_regulator;
 extern struct regulator *pu_regulator;
 extern int dvfs_core_is_active;
 extern struct cpu_op *(*get_cpu_op)(int *op);
-extern int low_bus_freq_mode;
-extern int audio_bus_freq_mode;
-extern int high_bus_freq_mode;
-extern int set_low_bus_freq(void);
-extern int set_high_bus_freq(int high_bus_speed);
-extern int low_freq_bus_used(void);
-extern struct mutex bus_freq_mutex;
+extern void bus_freq_update(struct clk *clk, bool flag);
 
 int set_cpu_freq(int freq)
 {
@@ -95,12 +89,9 @@ int set_cpu_freq(int freq)
 #endif
        /*Set the voltage for the GP domain. */
        if (freq > org_cpu_rate) {
-               mutex_lock(&bus_freq_mutex);
-               if (low_bus_freq_mode || audio_bus_freq_mode) {
-                       mutex_unlock(&bus_freq_mutex);
-                       set_high_bus_freq(0);
-               } else
-                       mutex_unlock(&bus_freq_mutex);
+               /* Check if the bus freq needs to be increased first */
+               bus_freq_update(cpu_clk, true);
+
                if (freq == cpu_op_tbl[0].cpu_rate && !IS_ERR(soc_regulator) && !IS_ERR(pu_regulator)) {
                        ret = regulator_set_voltage(soc_regulator, soc_volt,
                                                        soc_volt);
@@ -152,11 +143,8 @@ int set_cpu_freq(int freq)
                        }
                        soc_regulator_set = 0;
                }
-               mutex_lock(&bus_freq_mutex);
-               if (low_freq_bus_used() &&
-                       !(low_bus_freq_mode || audio_bus_freq_mode))
-                       set_low_bus_freq();
-               mutex_unlock(&bus_freq_mutex);
+               /* Check if the bus freq can be decreased.*/
+               bus_freq_update(cpu_clk, false);
        }
 
        return ret;