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>