[PATCH] ARM: OMAP2+: clockdomain: disabling unused clks
From: Mike Turquette <hidden>
Date: 2012-11-09 19:40:23
Also in:
linux-omap
Quoting Paul Walmsley (2012-11-09 11:08:00)
On Fri, 9 Nov 2012, Paul Walmsley wrote:quoted
On Thu, 8 Nov 2012, Mike Turquette wrote:quoted
You're right. In my rush I glossed over the clkdm decrement part. In light of the suspend/resume issues I'm not sure this approach is really valid. I think getting to the bottom of those issues will give the final word.What do you think about something like this? It's still under test and review here, but seems to avoid the warnings on 3530ES3 Beagle at least. The usage of __clk_get_enable_count() in this code still seems like a hack to me. It would be better for the CCF to call a different clk_hw_ops function pointer for the disable-unused-clocks case. But if you agree, and plan to fix this, or have some other cleaner fix in mind for the near future, then something like this seems reasonable for the short term to me. What do you think?
For avoiding the WARNs, this seems fine to me. And I agree that a new clk_ops function pointer is needed. Maybe something like, void (*unused_disable)(struct clk_hw *hw); But I'm OK with the below patch in the short term. I just have one question: did you observe any PM regressions by skipping the clkdm programming? Thanks, Mike
quoted hunk ↗ jump to hunk
Here's the patch. The changes to clkdm_clk_disable() are the important ones, the rest can be ignored for the purposes of this review. - Paul From: Mike Turquette <redacted> Date: Fri, 9 Nov 2012 11:28:42 -0700 Subject: [PATCH] ARM: OMAP2+: clockdomain: bypass clockdomain handling when disabling unused clks The OMAP port to the common clk framework[1] resulted in spurious WARNs while disable unused clocks. This is due to _clkdm_clk_hwmod_disable catching clkdm->usecount's with a value of zero. Even less desirable it would not allow the clkdm_clk_disable function pointer to get called due to an early return of -ERANGE. This patch adds a check for such a corner case by skipping the WARN and early return in the event that clkdm->usecount and clk->enable_usecount are both zero. Presumably this could only happen during the check for unused clocks at boot-time. [1] http://article.gmane.org/gmane.linux.ports.arm.omap/88824 Signed-off-by: Mike Turquette <redacted> [paul at pwsan.com: split the hwmod and clock disable cases; modified the code to skip the clockdomain handling during the disable-unused-clocks phase] --- arch/arm/mach-omap2/clockdomain.c | 91 ++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 37 deletions(-)diff --git a/arch/arm/mach-omap2/clockdomain.c b/arch/arm/mach-omap2/clockdomain.c index 64e5046..1bd0ff0 100644 --- a/arch/arm/mach-omap2/clockdomain.c +++ b/arch/arm/mach-omap2/clockdomain.c@@ -22,6 +22,7 @@ #include <linux/clk.h> #include <linux/limits.h> #include <linux/err.h> +#include <linux/clk-private.h> #include <linux/io.h>@@ -947,35 +948,6 @@ static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm) return 0; } -static int _clkdm_clk_hwmod_disable(struct clockdomain *clkdm) -{ - unsigned long flags; - - if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_disable) - return -EINVAL; - - spin_lock_irqsave(&clkdm->lock, flags); - - if (atomic_read(&clkdm->usecount) == 0) { - spin_unlock_irqrestore(&clkdm->lock, flags); - WARN_ON(1); /* underflow */ - return -ERANGE; - } - - if (atomic_dec_return(&clkdm->usecount) > 0) { - spin_unlock_irqrestore(&clkdm->lock, flags); - return 0; - } - - arch_clkdm->clkdm_clk_disable(clkdm); - pwrdm_state_switch(clkdm->pwrdm.ptr); - spin_unlock_irqrestore(&clkdm->lock, flags); - - pr_debug("clockdomain: %s: disabled\n", clkdm->name); - - return 0; -} - /** * clkdm_clk_enable - add an enabled downstream clock to this clkdm * @clkdm: struct clockdomain *@@ -1018,15 +990,39 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) */ int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) { - /* - * XXX Rewrite this code to maintain a list of enabled - * downstream clocks for debugging purposes? - */ + unsigned long flags; + int force_disable = 0; - if (!clk) + if (!clkdm || !clk || !arch_clkdm || !arch_clkdm->clkdm_clk_disable) return -EINVAL; - return _clkdm_clk_hwmod_disable(clkdm); + spin_lock_irqsave(&clkdm->lock, flags); + + /* corner case: disabling unused clocks */ + force_disable = (__clk_get_enable_count(clk) == 0) ? 1 : 0; + if (force_disable) + goto ccd_exit; + + if (atomic_read(&clkdm->usecount) == 0) { + spin_unlock_irqrestore(&clkdm->lock, flags); + WARN_ON(1); /* underflow */ + return -ERANGE; + } + + if (atomic_dec_return(&clkdm->usecount) > 0) { + spin_unlock_irqrestore(&clkdm->lock, flags); + return 0; + } + + arch_clkdm->clkdm_clk_disable(clkdm); + pwrdm_state_switch(clkdm->pwrdm.ptr); + + pr_debug("clockdomain: %s: disabled\n", clkdm->name); + +ccd_exit: + spin_unlock_irqrestore(&clkdm->lock, flags); + + return 0; } /**@@ -1077,6 +1073,8 @@ int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh) */ int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh) { + unsigned long flags; + /* The clkdm attribute does not exist yet prior OMAP4 */ if (cpu_is_omap24xx() || cpu_is_omap34xx()) return 0;@@ -1086,9 +1084,28 @@ int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh) * downstream hwmods for debugging purposes? */ - if (!oh) + if (!clkdm || !oh || !arch_clkdm || !arch_clkdm->clkdm_clk_disable) return -EINVAL; - return _clkdm_clk_hwmod_disable(clkdm); + spin_lock_irqsave(&clkdm->lock, flags); + + if (atomic_read(&clkdm->usecount) == 0) { + spin_unlock_irqrestore(&clkdm->lock, flags); + WARN_ON(1); /* underflow */ + return -ERANGE; + } + + if (atomic_dec_return(&clkdm->usecount) > 0) { + spin_unlock_irqrestore(&clkdm->lock, flags); + return 0; + } + + arch_clkdm->clkdm_clk_disable(clkdm); + pwrdm_state_switch(clkdm->pwrdm.ptr); + spin_unlock_irqrestore(&clkdm->lock, flags); + + pr_debug("clockdomain: %s: disabled\n", clkdm->name); + + return 0; }-- 1.7.10.4