Paul,<br><div class="gmail_extra"><br><div class="gmail_quote">On Sun, Dec 9, 2012 at 2:23 AM, Paul Walmsley <span dir="ltr"><<a href="mailto:paul@pwsan.com" target="_blank">paul@pwsan.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Add a per-powerdomain spinlock.  Use that instead of the clockdomain<br>
spinlock.  Add pwrdm_lock()/pwrdm_unlock() functions to allow other<br>
code to acquire or release the powerdomain spinlock without reaching<br>
directly into the struct powerdomain.<br></blockquote><div>Since clockdomains are part of powerdomains it seems weird for the clockdomain code to take a powerdoamin lock.<br>Is there a reason why the powerdomain could not take the lock before calling the clockdomain functions?<br>
<br>Also, are the lock and nolock version the clockdomain function needed?<br><br>Regards,<br>Jean<br> <br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
Signed-off-by: Paul Walmsley <<a href="mailto:paul@pwsan.com">paul@pwsan.com</a>><br>
Cc: Jean Pihet <<a href="mailto:jean.pihet@newoldbits.com">jean.pihet@newoldbits.com</a>><br>
---<br>
 arch/arm/mach-omap2/clockdomain-powerdomain.h |   22 +++<br>
 arch/arm/mach-omap2/clockdomain.c             |  161 +++++++++++++++++--------<br>
 arch/arm/mach-omap2/clockdomain.h             |    6 +<br>
 arch/arm/mach-omap2/powerdomain-clockdomain.h |   27 ++++<br>
 arch/arm/mach-omap2/powerdomain.c             |   56 ++++++++-<br>
 arch/arm/mach-omap2/powerdomain.h             |   11 +-<br>
 6 files changed, 219 insertions(+), 64 deletions(-)<br>
 create mode 100644 arch/arm/mach-omap2/clockdomain-powerdomain.h<br>
 create mode 100644 arch/arm/mach-omap2/powerdomain-clockdomain.h<br>
<br>
diff --git a/arch/arm/mach-omap2/clockdomain-powerdomain.h b/arch/arm/mach-omap2/clockdomain-powerdomain.h<br>
new file mode 100644<br>
index 0000000..8beee2d<br>
--- /dev/null<br>
+++ b/arch/arm/mach-omap2/clockdomain-powerdomain.h<br>
@@ -0,0 +1,22 @@<br>
+/*<br>
+ * OMAP clockdomain framework functions for use only by the powerdomain code<br>
+ *<br>
+ * Copyright (C) 2012 Texas Instruments, Inc.<br>
+ * Paul Walmsley<br>
+ *<br>
+ * This program is free software; you can redistribute it and/or modify<br>
+ * it under the terms of the GNU General Public License version 2 as<br>
+ * published by the Free Software Foundation.<br>
+ */<br>
+<br>
+#ifndef __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_POWERDOMAIN_H<br>
+#define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_POWERDOMAIN_H<br>
+<br>
+#include "clockdomain.h"<br>
+<br>
+extern void clkdm_allow_idle_nolock(struct clockdomain *clkdm);<br>
+extern void clkdm_deny_idle_nolock(struct clockdomain *clkdm);<br>
+extern int clkdm_wakeup_nolock(struct clockdomain *clkdm);<br>
+extern int clkdm_sleep_nolock(struct clockdomain *clkdm);<br>
+<br>
+#endif<br>
diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c<br>
index 18f65fd..2142dab 100644<br>
--- a/arch/arm/mach-omap2/clockdomain.c<br>
+++ b/arch/arm/mach-omap2/clockdomain.c<br>
@@ -30,6 +30,7 @@<br>
 #include "soc.h"<br>
 #include "clock.h"<br>
 #include "clockdomain.h"<br>
+#include "powerdomain-clockdomain.h"<br>
<br>
 /* clkdm_list contains all registered struct clockdomains */<br>
 static LIST_HEAD(clkdm_list);<br>
@@ -91,8 +92,6 @@ static int _clkdm_register(struct clockdomain *clkdm)<br>
<br>
        pwrdm_add_clkdm(pwrdm, clkdm);<br>
<br>
-       spin_lock_init(&clkdm->lock);<br>
-<br>
        pr_debug("clockdomain: registered %s\n", clkdm->name);<br>
<br>
        return 0;<br>
@@ -733,18 +732,17 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm)<br>
 }<br>
<br>
 /**<br>
- * clkdm_sleep - force clockdomain sleep transition<br>
+ * clkdm_sleep_nolock - force clockdomain sleep transition (lockless)<br>
  * @clkdm: struct clockdomain *<br>
  *<br>
  * Instruct the CM to force a sleep transition on the specified<br>
- * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if<br>
- * clockdomain does not support software-initiated sleep; 0 upon<br>
- * success.<br>
+ * clockdomain @clkdm.  Only for use by the powerdomain code.  Returns<br>
+ * -EINVAL if @clkdm is NULL or if clockdomain does not support<br>
+ * software-initiated sleep; 0 upon success.<br>
  */<br>
-int clkdm_sleep(struct clockdomain *clkdm)<br>
+int clkdm_sleep_nolock(struct clockdomain *clkdm)<br>
 {<br>
        int ret;<br>
-       unsigned long flags;<br>
<br>
        if (!clkdm)<br>
                return -EINVAL;<br>
@@ -760,27 +758,45 @@ int clkdm_sleep(struct clockdomain *clkdm)<br>
<br>
        pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);<br>
<br>
-       spin_lock_irqsave(&clkdm->lock, flags);<br>
        clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;<br>
        ret = arch_clkdm->clkdm_sleep(clkdm);<br>
-       ret |= pwrdm_state_switch(clkdm->pwrdm.ptr);<br>
-       spin_unlock_irqrestore(&clkdm->lock, flags);<br>
+       ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);<br>
+<br>
        return ret;<br>
 }<br>
<br>
 /**<br>
- * clkdm_wakeup - force clockdomain wakeup transition<br>
+ * clkdm_sleep - force clockdomain sleep transition<br>
  * @clkdm: struct clockdomain *<br>
  *<br>
- * Instruct the CM to force a wakeup transition on the specified<br>
- * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if the<br>
- * clockdomain does not support software-controlled wakeup; 0 upon<br>
+ * Instruct the CM to force a sleep transition on the specified<br>
+ * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if<br>
+ * clockdomain does not support software-initiated sleep; 0 upon<br>
  * success.<br>
  */<br>
-int clkdm_wakeup(struct clockdomain *clkdm)<br>
+int clkdm_sleep(struct clockdomain *clkdm)<br>
+{<br>
+       int ret;<br>
+<br>
+       pwrdm_lock(clkdm->pwrdm.ptr);<br>
+       ret = clkdm_sleep_nolock(clkdm);<br>
+       pwrdm_unlock(clkdm->pwrdm.ptr);<br>
+<br>
+       return ret;<br>
+}<br>
+<br>
+/**<br>
+ * clkdm_wakeup_nolock - force clockdomain wakeup transition (lockless)<br>
+ * @clkdm: struct clockdomain *<br>
+ *<br>
+ * Instruct the CM to force a wakeup transition on the specified<br>
+ * clockdomain @clkdm.  Only for use by the powerdomain code.  Returns<br>
+ * -EINVAL if @clkdm is NULL or if the clockdomain does not support<br>
+ * software-controlled wakeup; 0 upon success.<br>
+ */<br>
+int clkdm_wakeup_nolock(struct clockdomain *clkdm)<br>
 {<br>
        int ret;<br>
-       unsigned long flags;<br>
<br>
        if (!clkdm)<br>
                return -EINVAL;<br>
@@ -796,28 +812,46 @@ int clkdm_wakeup(struct clockdomain *clkdm)<br>
<br>
        pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);<br>
<br>
-       spin_lock_irqsave(&clkdm->lock, flags);<br>
        clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;<br>
        ret = arch_clkdm->clkdm_wakeup(clkdm);<br>
-       ret |= pwrdm_state_switch(clkdm->pwrdm.ptr);<br>
-       spin_unlock_irqrestore(&clkdm->lock, flags);<br>
+       ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);<br>
+<br>
        return ret;<br>
 }<br>
<br>
 /**<br>
- * clkdm_allow_idle - enable hwsup idle transitions for clkdm<br>
+ * clkdm_wakeup - force clockdomain wakeup transition<br>
  * @clkdm: struct clockdomain *<br>
  *<br>
- * Allow the hardware to automatically switch the clockdomain @clkdm into<br>
- * active or idle states, as needed by downstream clocks.  If the<br>
+ * Instruct the CM to force a wakeup transition on the specified<br>
+ * clockdomain @clkdm.  Returns -EINVAL if @clkdm is NULL or if the<br>
+ * clockdomain does not support software-controlled wakeup; 0 upon<br>
+ * success.<br>
+ */<br>
+int clkdm_wakeup(struct clockdomain *clkdm)<br>
+{<br>
+       int ret;<br>
+<br>
+       pwrdm_lock(clkdm->pwrdm.ptr);<br>
+       ret = clkdm_wakeup_nolock(clkdm);<br>
+       pwrdm_unlock(clkdm->pwrdm.ptr);<br>
+<br>
+       return ret;<br>
+}<br>
+<br>
+/**<br>
+ * clkdm_allow_idle_nolock - enable hwsup idle transitions for clkdm<br>
+ * @clkdm: struct clockdomain *<br>
+ *<br>
+ * Allow the hardware to automatically switch the clockdomain @clkdm<br>
+ * into active or idle states, as needed by downstream clocks.  If the<br>
  * clockdomain has any downstream clocks enabled in the clock<br>
  * framework, wkdep/sleepdep autodependencies are added; this is so<br>
- * device drivers can read and write to the device.  No return value.<br>
+ * device drivers can read and write to the device.  Only for use by<br>
+ * the powerdomain code.  No return value.<br>
  */<br>
-void clkdm_allow_idle(struct clockdomain *clkdm)<br>
+void clkdm_allow_idle_nolock(struct clockdomain *clkdm)<br>
 {<br>
-       unsigned long flags;<br>
-<br>
        if (!clkdm)<br>
                return;<br>
<br>
@@ -833,11 +867,26 @@ void clkdm_allow_idle(struct clockdomain *clkdm)<br>
        pr_debug("clockdomain: enabling automatic idle transitions for %s\n",<br>
                 clkdm->name);<br>
<br>
-       spin_lock_irqsave(&clkdm->lock, flags);<br>
        clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED;<br>
        arch_clkdm->clkdm_allow_idle(clkdm);<br>
-       pwrdm_state_switch(clkdm->pwrdm.ptr);<br>
-       spin_unlock_irqrestore(&clkdm->lock, flags);<br>
+       pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);<br>
+}<br>
+<br>
+/**<br>
+ * clkdm_allow_idle - enable hwsup idle transitions for clkdm<br>
+ * @clkdm: struct clockdomain *<br>
+ *<br>
+ * Allow the hardware to automatically switch the clockdomain @clkdm into<br>
+ * active or idle states, as needed by downstream clocks.  If the<br>
+ * clockdomain has any downstream clocks enabled in the clock<br>
+ * framework, wkdep/sleepdep autodependencies are added; this is so<br>
+ * device drivers can read and write to the device.  No return value.<br>
+ */<br>
+void clkdm_allow_idle(struct clockdomain *clkdm)<br>
+{<br>
+       pwrdm_lock(clkdm->pwrdm.ptr);<br>
+       clkdm_allow_idle_nolock(clkdm);<br>
+       pwrdm_unlock(clkdm->pwrdm.ptr);<br>
 }<br>
<br>
 /**<br>
@@ -847,12 +896,11 @@ void clkdm_allow_idle(struct clockdomain *clkdm)<br>
  * Prevent the hardware from automatically switching the clockdomain<br>
  * @clkdm into inactive or idle states.  If the clockdomain has<br>
  * downstream clocks enabled in the clock framework, wkdep/sleepdep<br>
- * autodependencies are removed.  No return value.<br>
+ * autodependencies are removed.  Only for use by the powerdomain<br>
+ * code.  No return value.<br>
  */<br>
-void clkdm_deny_idle(struct clockdomain *clkdm)<br>
+void clkdm_deny_idle_nolock(struct clockdomain *clkdm)<br>
 {<br>
-       unsigned long flags;<br>
-<br>
        if (!clkdm)<br>
                return;<br>
<br>
@@ -868,11 +916,25 @@ void clkdm_deny_idle(struct clockdomain *clkdm)<br>
        pr_debug("clockdomain: disabling automatic idle transitions for %s\n",<br>
                 clkdm->name);<br>
<br>
-       spin_lock_irqsave(&clkdm->lock, flags);<br>
        clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;<br>
        arch_clkdm->clkdm_deny_idle(clkdm);<br>
-       pwrdm_state_switch(clkdm->pwrdm.ptr);<br>
-       spin_unlock_irqrestore(&clkdm->lock, flags);<br>
+       pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);<br>
+}<br>
+<br>
+/**<br>
+ * clkdm_deny_idle - disable hwsup idle transitions for clkdm<br>
+ * @clkdm: struct clockdomain *<br>
+ *<br>
+ * Prevent the hardware from automatically switching the clockdomain<br>
+ * @clkdm into inactive or idle states.  If the clockdomain has<br>
+ * downstream clocks enabled in the clock framework, wkdep/sleepdep<br>
+ * autodependencies are removed.  No return value.<br>
+ */<br>
+void clkdm_deny_idle(struct clockdomain *clkdm)<br>
+{<br>
+       pwrdm_lock(clkdm->pwrdm.ptr);<br>
+       clkdm_deny_idle_nolock(clkdm);<br>
+       pwrdm_unlock(clkdm->pwrdm.ptr);<br>
 }<br>
<br>
 /**<br>
@@ -889,14 +951,11 @@ void clkdm_deny_idle(struct clockdomain *clkdm)<br>
 bool clkdm_in_hwsup(struct clockdomain *clkdm)<br>
 {<br>
        bool ret;<br>
-       unsigned long flags;<br>
<br>
        if (!clkdm)<br>
                return false;<br>
<br>
-       spin_lock_irqsave(&clkdm->lock, flags);<br>
        ret = (clkdm->_flags & _CLKDM_FLAG_HWSUP_ENABLED) ? true : false;<br>
-       spin_unlock_irqrestore(&clkdm->lock, flags);<br>
<br>
        return ret;<br>
 }<br>
@@ -922,12 +981,10 @@ bool clkdm_missing_idle_reporting(struct clockdomain *clkdm)<br>
<br>
 static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm)<br>
 {<br>
-       unsigned long flags;<br>
-<br>
        if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable)<br>
                return -EINVAL;<br>
<br>
-       spin_lock_irqsave(&clkdm->lock, flags);<br>
+       pwrdm_lock(clkdm->pwrdm.ptr);<br>
<br>
        /*<br>
         * For arch's with no autodeps, clkcm_clk_enable<br>
@@ -935,13 +992,13 @@ static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm)<br>
         * enabled, so the clkdm can be force woken up.<br>
         */<br>
        if ((atomic_inc_return(&clkdm->usecount) > 1) && autodeps) {<br>
-               spin_unlock_irqrestore(&clkdm->lock, flags);<br>
+               pwrdm_unlock(clkdm->pwrdm.ptr);<br>
                return 0;<br>
        }<br>
<br>
        arch_clkdm->clkdm_clk_enable(clkdm);<br>
-       pwrdm_state_switch(clkdm->pwrdm.ptr);<br>
-       spin_unlock_irqrestore(&clkdm->lock, flags);<br>
+       pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);<br>
+       pwrdm_unlock(clkdm->pwrdm.ptr);<br>
<br>
        pr_debug("clockdomain: %s: enabled\n", clkdm->name);<br>
<br>
@@ -950,27 +1007,25 @@ static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm)<br>
<br>
 static int _clkdm_clk_hwmod_disable(struct clockdomain *clkdm)<br>
 {<br>
-       unsigned long flags;<br>
-<br>
        if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_disable)<br>
                return -EINVAL;<br>
<br>
-       spin_lock_irqsave(&clkdm->lock, flags);<br>
+       pwrdm_lock(clkdm->pwrdm.ptr);<br>
<br>
        if (atomic_read(&clkdm->usecount) == 0) {<br>
-               spin_unlock_irqrestore(&clkdm->lock, flags);<br>
+               pwrdm_unlock(clkdm->pwrdm.ptr);<br>
                WARN_ON(1); /* underflow */<br>
                return -ERANGE;<br>
        }<br>
<br>
        if (atomic_dec_return(&clkdm->usecount) > 0) {<br>
-               spin_unlock_irqrestore(&clkdm->lock, flags);<br>
+               pwrdm_unlock(clkdm->pwrdm.ptr);<br>
                return 0;<br>
        }<br>
<br>
        arch_clkdm->clkdm_clk_disable(clkdm);<br>
-       pwrdm_state_switch(clkdm->pwrdm.ptr);<br>
-       spin_unlock_irqrestore(&clkdm->lock, flags);<br>
+       pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);<br>
+       pwrdm_unlock(clkdm->pwrdm.ptr);<br>
<br>
        pr_debug("clockdomain: %s: disabled\n", clkdm->name);<br>
<br>
diff --git a/arch/arm/mach-omap2/clockdomain.h b/arch/arm/mach-omap2/clockdomain.h<br>
index bc42446..e7f1b4b 100644<br>
--- a/arch/arm/mach-omap2/clockdomain.h<br>
+++ b/arch/arm/mach-omap2/clockdomain.h<br>
@@ -15,7 +15,6 @@<br>
 #define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_H<br>
<br>
 #include <linux/init.h><br>
-#include <linux/spinlock.h><br>
<br>
 #include "powerdomain.h"<br>
 #include "clock.h"<br>
@@ -139,7 +138,6 @@ struct clockdomain {<br>
        struct clkdm_dep *sleepdep_srcs;<br>
        atomic_t usecount;<br>
        struct list_head node;<br>
-       spinlock_t lock;<br>
 };<br>
<br>
 /**<br>
@@ -196,12 +194,16 @@ int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2);<br>
 int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2);<br>
 int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm);<br>
<br>
+void clkdm_allow_idle_nolock(struct clockdomain *clkdm);<br>
 void clkdm_allow_idle(struct clockdomain *clkdm);<br>
+void clkdm_deny_idle_nolock(struct clockdomain *clkdm);<br>
 void clkdm_deny_idle(struct clockdomain *clkdm);<br>
 bool clkdm_in_hwsup(struct clockdomain *clkdm);<br>
 bool clkdm_missing_idle_reporting(struct clockdomain *clkdm);<br>
<br>
+int clkdm_wakeup_nolock(struct clockdomain *clkdm);<br>
 int clkdm_wakeup(struct clockdomain *clkdm);<br>
+int clkdm_sleep_nolock(struct clockdomain *clkdm);<br>
 int clkdm_sleep(struct clockdomain *clkdm);<br>
<br>
 int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk);<br>
diff --git a/arch/arm/mach-omap2/powerdomain-clockdomain.h b/arch/arm/mach-omap2/powerdomain-clockdomain.h<br>
new file mode 100644<br>
index 0000000..e404eec<br>
--- /dev/null<br>
+++ b/arch/arm/mach-omap2/powerdomain-clockdomain.h<br>
@@ -0,0 +1,27 @@<br>
+/*<br>
+ * OMAP powerdomain functions only for use by the OMAP clockdomain code<br>
+ *<br>
+ * Copyright (C) 2012 Texas Instruments, Inc.<br>
+ * Paul Walmsley<br>
+ *<br>
+ * This program is free software; you can redistribute it and/or modify<br>
+ * it under the terms of the GNU General Public License version 2 as<br>
+ * published by the Free Software Foundation.<br>
+ *<br>
+ * The point of this file is to try to prevent other code outside of<br>
+ * clockdomain.c from calling the functions listed herein.  Yes, this<br>
+ * means you.<br>
+ */<br>
+<br>
+#ifndef __ARCH_ARM_MACH_OMAP2_POWERDOMAIN_CLOCKDOMAIN_H<br>
+#define __ARCH_ARM_MACH_OMAP2_POWERDOMAIN_CLOCKDOMAIN_H<br>
+<br>
+#include <linux/types.h><br>
+<br>
+#include "powerdomain.h"<br>
+<br>
+extern void pwrdm_lock(struct powerdomain *pwrdm);<br>
+extern void pwrdm_unlock(struct powerdomain *pwrdm);<br>
+extern int pwrdm_state_switch_nolock(struct powerdomain *pwrdm);<br>
+<br>
+#endif<br>
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c<br>
index 05f00660..2a5f15b 100644<br>
--- a/arch/arm/mach-omap2/powerdomain.c<br>
+++ b/arch/arm/mach-omap2/powerdomain.c<br>
@@ -19,6 +19,7 @@<br>
 #include <linux/list.h><br>
 #include <linux/errno.h><br>
 #include <linux/string.h><br>
+#include <linux/spinlock.h><br>
 #include <trace/events/power.h><br>
<br>
 #include "cm2xxx_3xxx.h"<br>
@@ -31,6 +32,7 @@<br>
<br>
 #include "powerdomain.h"<br>
 #include "clockdomain.h"<br>
+#include "clockdomain-powerdomain.h"<br>
<br>
 #include "soc.h"<br>
 #include "pm.h"<br>
@@ -101,6 +103,7 @@ static int _pwrdm_register(struct powerdomain *pwrdm)<br>
        pwrdm->voltdm.ptr = voltdm;<br>
        INIT_LIST_HEAD(&pwrdm->voltdm_node);<br>
        voltdm_add_pwrdm(voltdm, pwrdm);<br>
+       spin_lock_init(&pwrdm->_lock);<br>
<br>
        list_add(&pwrdm->node, &pwrdm_list);<br>
<br>
@@ -276,6 +279,30 @@ int pwrdm_complete_init(void)<br>
 }<br>
<br>
 /**<br>
+ * pwrdm_lock - acquire a Linux spinlock on a powerdomain<br>
+ * @pwrdm: struct powerdomain * to lock<br>
+ *<br>
+ * Acquire the powerdomain spinlock on @pwrdm.  No return value.<br>
+ */<br>
+void pwrdm_lock(struct powerdomain *pwrdm)<br>
+       __acquires(&pwrdm->_lock)<br>
+{<br>
+       spin_lock_irqsave(&pwrdm->_lock, pwrdm->_lock_flags);<br>
+}<br>
+<br>
+/**<br>
+ * pwrdm_unlock - release a Linux spinlock on a powerdomain<br>
+ * @pwrdm: struct powerdomain * to unlock<br>
+ *<br>
+ * Release the powerdomain spinlock on @pwrdm.  No return value.<br>
+ */<br>
+void pwrdm_unlock(struct powerdomain *pwrdm)<br>
+       __releases(&pwrdm->_lock)<br>
+{<br>
+       spin_unlock_irqrestore(&pwrdm->_lock, pwrdm->_lock_flags);<br>
+}<br>
+<br>
+/**<br>
  * pwrdm_lookup - look up a powerdomain by name, return a pointer<br>
  * @name: name of powerdomain<br>
  *<br>
@@ -921,7 +948,7 @@ bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm)<br>
        return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0;<br>
 }<br>
<br>
-int pwrdm_state_switch(struct powerdomain *pwrdm)<br>
+int pwrdm_state_switch_nolock(struct powerdomain *pwrdm)<br>
 {<br>
        int ret;<br>
<br>
@@ -935,6 +962,17 @@ int pwrdm_state_switch(struct powerdomain *pwrdm)<br>
        return ret;<br>
 }<br>
<br>
+int __deprecated pwrdm_state_switch(struct powerdomain *pwrdm)<br>
+{<br>
+       int ret;<br>
+<br>
+       pwrdm_lock(pwrdm);<br>
+       ret = pwrdm_state_switch_nolock(pwrdm);<br>
+       pwrdm_unlock(pwrdm);<br>
+<br>
+       return ret;<br>
+}<br>
+<br>
 int pwrdm_pre_transition(struct powerdomain *pwrdm)<br>
 {<br>
        if (pwrdm)<br>
@@ -973,7 +1011,7 @@ static u8 _pwrdm_save_clkdm_state_and_activate(struct powerdomain *pwrdm,<br>
                        sleep_switch = LOWPOWERSTATE_SWITCH;<br>
                } else {<br>
                        *hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]);<br>
-                       clkdm_wakeup(pwrdm->pwrdm_clkdms[0]);<br>
+                       clkdm_wakeup_nolock(pwrdm->pwrdm_clkdms[0]);<br>
                        sleep_switch = FORCEWAKEUP_SWITCH;<br>
                }<br>
        } else {<br>
@@ -989,15 +1027,15 @@ static void _pwrdm_restore_clkdm_state(struct powerdomain *pwrdm,<br>
        switch (sleep_switch) {<br>
        case FORCEWAKEUP_SWITCH:<br>
                if (hwsup)<br>
-                       clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]);<br>
+                       clkdm_allow_idle_nolock(pwrdm->pwrdm_clkdms[0]);<br>
                else<br>
-                       clkdm_sleep(pwrdm->pwrdm_clkdms[0]);<br>
+                       clkdm_sleep_nolock(pwrdm->pwrdm_clkdms[0]);<br>
                break;<br>
        case LOWPOWERSTATE_SWITCH:<br>
                if (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE &&<br>
                    arch_pwrdm->pwrdm_set_lowpwrstchange)<br>
                        arch_pwrdm->pwrdm_set_lowpwrstchange(pwrdm);<br>
-               pwrdm_state_switch(pwrdm);<br>
+               pwrdm_state_switch_nolock(pwrdm);<br>
                break;<br>
        }<br>
 }<br>
@@ -1021,9 +1059,13 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 pwrst)<br>
                pwrst--;<br>
        }<br>
<br>
+       pwrdm_lock(pwrdm);<br>
+<br>
        next_pwrst = pwrdm_read_next_pwrst(pwrdm);<br>
-       if (next_pwrst == pwrst)<br>
+       if (next_pwrst == pwrst) {<br>
+               pwrdm_unlock(pwrdm);<br>
                return ret;<br>
+       }<br>
<br>
        sleep_switch = _pwrdm_save_clkdm_state_and_activate(pwrdm, pwrst,<br>
                                                            &hwsup);<br>
@@ -1035,6 +1077,8 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 pwrst)<br>
<br>
        _pwrdm_restore_clkdm_state(pwrdm, sleep_switch, hwsup);<br>
<br>
+       pwrdm_unlock(pwrdm);<br>
+<br>
        return ret;<br>
 }<br>
<br>
diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-omap2/powerdomain.h<br>
index 1edb3b7..83b4892 100644<br>
--- a/arch/arm/mach-omap2/powerdomain.h<br>
+++ b/arch/arm/mach-omap2/powerdomain.h<br>
@@ -19,8 +19,7 @@<br>
<br>
 #include <linux/types.h><br>
 #include <linux/list.h><br>
-<br>
-#include <linux/atomic.h><br>
+#include <linux/spinlock.h><br>
<br>
 #include "voltage.h"<br>
<br>
@@ -103,6 +102,8 @@ struct powerdomain;<br>
  * @state_counter:<br>
  * @timer:<br>
  * @state_timer:<br>
+ * @_lock: spinlock used to serialize powerdomain and some clockdomain ops<br>
+ * @_lock_flags: stored flags when @_lock is taken<br>
  *<br>
  * @prcm_partition possible values are defined in mach-omap2/prcm44xx.h.<br>
  */<br>
@@ -127,7 +128,8 @@ struct powerdomain {<br>
        unsigned state_counter[PWRDM_MAX_PWRSTS];<br>
        unsigned ret_logic_off_counter;<br>
        unsigned ret_mem_off_counter[PWRDM_MAX_MEM_BANKS];<br>
-<br>
+       spinlock_t _lock;<br>
+       unsigned long _lock_flags;<br>
        const u8 pwrstctrl_offs;<br>
        const u8 pwrstst_offs;<br>
        const u32 logicretstate_mask;<br>
@@ -225,6 +227,7 @@ int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm);<br>
 int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm);<br>
 bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm);<br>
<br>
+int pwrdm_state_switch_nolock(struct powerdomain *pwrdm);<br>
 int pwrdm_state_switch(struct powerdomain *pwrdm);<br>
 int pwrdm_pre_transition(struct powerdomain *pwrdm);<br>
 int pwrdm_post_transition(struct powerdomain *pwrdm);<br>
@@ -252,5 +255,7 @@ extern u32 omap2_pwrdm_get_mem_bank_stst_mask(u8 bank);<br>
 extern struct powerdomain wkup_omap2_pwrdm;<br>
 extern struct powerdomain gfx_omap2_pwrdm;<br>
<br>
+extern void pwrdm_lock(struct powerdomain *pwrdm);<br>
+extern void pwrdm_unlock(struct powerdomain *pwrdm);<br>
<br>
 #endif<br>
<br>
<br>
</blockquote></div><br></div>