From patchwork Thu Mar 22 08:18:17 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Lothar_Wa=C3=9Fmann?= X-Patchwork-Id: 148185 Return-Path: X-Original-To: incoming-imx@patchwork.ozlabs.org Delivered-To: patchwork-incoming-imx@bilbo.ozlabs.org Received: from merlin.infradead.org (merlin.infradead.org [IPv6:2001:4978:20e::2]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 9D2B1B6F98 for ; Thu, 22 Mar 2012 19:22:51 +1100 (EST) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1SAdG3-0004io-4D; Thu, 22 Mar 2012 08:20:31 +0000 Received: from mail.karo-electronics.de ([81.173.242.67]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1SAdFy-0004iV-Fu for linux-arm-kernel@lists.infradead.org; Thu, 22 Mar 2012 08:20:28 +0000 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= To: Subject: [PATCH] ARM: mxs: enforce semantics of clk_prepare()/clk_unprepare() and clk_enable()/clk_disable() Date: Thu, 22 Mar 2012 09:18:17 +0100 Message-Id: <1332404298-18891-1-git-send-email-LW@KARO-electronics.de> X-Mailer: git-send-email 1.7.2.5 To: Shawn Guo MIME-Version: 1.0 X-Spam-Note: CRM114 invocation failed X-Spam-Score: -1.9 (-) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-1.9 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 T_RP_MATCHES_RCVD Envelope sender domain matches handover relay domain -1.9 BAYES_00 BODY: Bayes spam probability is 0 to 1% [score: 0.0000] 0.0 TO_EQ_FM_DIRECT_MX To == From and direct-to-MX Cc: linux-kernel@vger.kernel.org, Russell King , linux-arm-kernel@lists.infradead.org, Sascha Hauer , =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.14 Precedence: list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+incoming-imx=patchwork.ozlabs.org@lists.infradead.org List-Id: linux-imx-kernel.lists.patchwork.ozlabs.org After introducing clk_prepare()/clk_unprepare() there may still be drivers out there that don't use the new functions. This patch warns about drivers calling clk_enable() without a preceding clk_prepare() and various other invalid sequences of calls to clk_enable()/clk_disable() and clk_prepare()/clk_unprepare(). To not make a system unusable due to excessive warning messages the warnings are limited to 1 per clk by a flag in the 'flags' member of struct clk. Signed-off-by: Lothar Waßmann --- arch/arm/mach-mxs/clock.c | 98 ++++++++++++++++++++++++++++---- arch/arm/mach-mxs/include/mach/clock.h | 6 ++ 2 files changed, 93 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-mxs/clock.c b/arch/arm/mach-mxs/clock.c index 97a6f4a..4c23351 100644 --- a/arch/arm/mach-mxs/clock.c +++ b/arch/arm/mach-mxs/clock.c @@ -56,24 +56,61 @@ static void __clk_disable(struct clk *clk) if (!(--clk->usecount)) { if (clk->disable) clk->disable(clk); - __clk_disable(clk->parent); } } static int __clk_enable(struct clk *clk) { - if (clk == NULL || IS_ERR(clk)) - return -EINVAL; + if (clk == NULL) + return 0; - if (clk->usecount++ == 0) { - __clk_enable(clk->parent); + if (IS_ERR(clk)) + return -EINVAL; - if (clk->enable) - clk->enable(clk); + if (clk->usecount == 0) { + if (clk->enable) { + int ret = clk->enable(clk); + if (ret) + return ret; + } } + clk->usecount++; + return 0; } +static void __clk_unprepare(struct clk *clk) +{ + if (clk == NULL || IS_ERR(clk)) + return; + + if (clk->parent) + __clk_unprepare(clk->parent); + + __clk_disable(clk); +} + +static int __clk_prepare(struct clk *clk) +{ + int ret; + + if (clk == NULL) + return 0; + + if (IS_ERR(clk)) + return -EINVAL; + + ret = __clk_prepare(clk->parent); + if (ret) + return ret; + + ret = __clk_enable(clk); + if (ret) + __clk_unprepare(clk->parent); + + return ret; +} + /* * The clk_enable/clk_disable could be called by drivers in atomic context, * so they should not really hold mutex. Instead, clk_prepare/clk_unprepare @@ -86,11 +123,18 @@ int clk_prepare(struct clk *clk) { int ret = 0; - if (clk == NULL || IS_ERR(clk)) + if (clk == NULL) + return 0; + + if (IS_ERR(clk)) return -EINVAL; mutex_lock(&clocks_mutex); - ret = __clk_enable(clk); + if (clk->prepared++ == 0) { + ret = __clk_prepare(clk); + if (ret) + clk->prepared--; + } mutex_unlock(&clocks_mutex); return ret; @@ -103,20 +147,52 @@ void clk_unprepare(struct clk *clk) return; mutex_lock(&clocks_mutex); - __clk_disable(clk); + if (WARN_ON(!clk->prepared && !(clk->flags & CLK_WARNED))) { + pr_err("clk_unprepare() called without clk_prepare()\n"); + clk->flags |= CLK_WARNED; + } + if (WARN_ON(clk->prepared == 1 && clk->usecount > 1)) { + pr_err("unbalanced calls (%d) to clk_enable()/clk_disable() before clk_unprepare()\n", + clk->usecount); + clk->flags |= CLK_WARNED; + } + if (!(--clk->prepared)) + __clk_unprepare(clk); mutex_unlock(&clocks_mutex); } EXPORT_SYMBOL(clk_unprepare); int clk_enable(struct clk *clk) { + if (clk == NULL) + return 0; + + if (IS_ERR(clk)) + return -EINVAL; + + if (WARN_ON(!clk->prepared && !(clk->flags & CLK_WARNED))) { + pr_err("clk_enable() called without clk_prepare()\n"); + clk->flags |= CLK_WARNED; + } + clk->usecount++; return 0; } EXPORT_SYMBOL(clk_enable); void clk_disable(struct clk *clk) { - /* nothing to do */ + if (clk == NULL || IS_ERR(clk)) + return; + + if (WARN_ON(!clk->prepared && !(clk->flags & CLK_WARNED))) { + pr_err("clk_disable() called without clk_prepare()\n"); + clk->flags |= CLK_WARNED; + } + if (WARN_ON(clk->usecount <= 1 && !(clk->flags & CLK_WARNED))) { + pr_err("unbalanced clk_disable()\n"); + clk->flags |= CLK_WARNED; + } + clk->usecount--; } EXPORT_SYMBOL(clk_disable); diff --git a/arch/arm/mach-mxs/include/mach/clock.h b/arch/arm/mach-mxs/include/mach/clock.h index 592c9ab..285ca6b 100644 --- a/arch/arm/mach-mxs/include/mach/clock.h +++ b/arch/arm/mach-mxs/include/mach/clock.h @@ -25,12 +25,18 @@ struct module; +enum clk_flags { + CLK_WARNED = 0x01, +}; + struct clk { int id; /* Source clock this clk depends on */ struct clk *parent; /* Reference count of clock enable/disable */ __s8 usecount; + /* Reference count of clock prepare/unprepare */ + __s8 prepared; /* Register bit position for clock's enable/disable control. */ u8 enable_shift; /* Register address for clock's enable/disable control. */