]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00221975 Fix race condition in clock code.
authorRanjani Vaidyanathan <ra5478@freescale.com>
Wed, 29 Aug 2012 22:05:11 +0000 (17:05 -0500)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:35:20 +0000 (08:35 +0200)
Need to ensure that check for usecount in clk_set_parent
occurs within the protection of the clock mutex. Else
there is a chance that the usecount can be decremented
(and the clock disabled) after the check.
Also add back the code to maintain the correct usecount
for pll2_pfd_400.

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

index 81b63a55c52936ad8a0c73591e0593286a45cb57..c2bf201779096b28c5b0e1535d070d520cd948dc 100644 (file)
@@ -1262,6 +1262,7 @@ 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;
@@ -1287,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);
index 7a66d10c3aabba95357c0b073cf30552041802c7..43b2bd1baac20e2031c74c5417545050d6d0d6ed 100755 (executable)
@@ -1169,6 +1169,7 @@ 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);
                        arm_needs_pll2_400 = true;
+                       pll2_pfd2_400M.usecount++;
                        pll1_sw_clk.set_parent(&pll1_sw_clk, &pll2_pfd2_400M);
                        pll1_sw_clk.parent = &pll2_pfd2_400M;
                }
@@ -1191,6 +1192,8 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long 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);
index ee4ea63d9fdceef65f0b6b99fc85fe1fcdef1533..93347eb14c6970423eb7a128fd8ecf640d51d887 100755 (executable)
@@ -225,10 +225,18 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
            IS_ERR(parent) || clk->set_parent == NULL)
                return ret;
 
-       if (clk->usecount)
-               clk_enable(parent);
-
        mutex_lock(&clocks_mutex);
+
+       if (clk->usecount) {
+               if (in_interrupt()) {
+                       printk(KERN_ERR " clk_enable cannot be called in an interrupt context\n");
+                       dump_stack();
+                       mutex_unlock(&clocks_mutex);
+                       BUG();
+               }
+               __clk_enable(parent);
+       }
+
        ret = clk->set_parent(clk, parent);
        if (ret == 0) {
                old = clk->parent;
@@ -236,10 +244,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
        } else {
                old = parent;
        }
-       mutex_unlock(&clocks_mutex);
-
        if (clk->usecount)
-               clk_disable(old);
+               __clk_disable(old);
+
+       mutex_unlock(&clocks_mutex);
 
        return ret;
 }