From patchwork Thu Jul 3 14:38:42 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tomeu Vizoso X-Patchwork-Id: 4474561 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 8669FBEEAA for ; Thu, 3 Jul 2014 14:42:37 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 4E002203AA for ; Thu, 3 Jul 2014 14:42:36 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id AADE120384 for ; Thu, 3 Jul 2014 14:42:34 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1X2iBo-00031q-TD; Thu, 03 Jul 2014 14:40:44 +0000 Received: from mail-wg0-x22c.google.com ([2a00:1450:400c:c00::22c]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1X2iAs-0001Ck-78 for linux-arm-kernel@lists.infradead.org; Thu, 03 Jul 2014 14:39:56 +0000 Received: by mail-wg0-f44.google.com with SMTP id k14so341795wgh.15 for ; Thu, 03 Jul 2014 07:39:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=CMv0/NrpMgJcWqcl9rc6Qk5CczkRGKkt0sArqVQXmy0=; b=s7oW49VWoyMS9MFWGKjVoruHLbBk4pgfE0ivSDBM6to2oHEvvvaDIHGJIY2jl5uC9i ULKgHdRv0FU9oKOdE2PBJh1iZ0f0eTx3eCK9qbiafe5YVGuFAn0FweyZUAckoXdfGZTB Tf1JL2iJWeN80breNMgvyvTJTO0Z2urs0QMPv3y9YsMuvn4aZ/zZkSz2Aroi5AV5Qavu +1KETs3uMmTMJ81kn2o/LdeI+hiYqKYO9lwqkUezqA64Sf243ZEZVcIsx9fz0qFJyCmw nnEnKCAIJqMzFzyiPNk1Pmak3hPzmu9BqjZaYJ13PHDATAjp2ykxtoreJHIhLtKuq/jv mgSQ== X-Received: by 10.194.62.5 with SMTP id u5mr5309615wjr.46.1404398363384; Thu, 03 Jul 2014 07:39:23 -0700 (PDT) Received: from localhost.localdomain (37-48-39-127.tmcz.cz. [37.48.39.127]) by mx.google.com with ESMTPSA id w9sm43159694wif.12.2014.07.03.07.39.20 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 03 Jul 2014 07:39:22 -0700 (PDT) From: Tomeu Vizoso To: Stephen Warren , Thierry Reding , Mike Turquette , rabin@rab.in, linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Subject: [RFC v2 4/5] clk: per-user clock accounting for debug Date: Thu, 3 Jul 2014 16:38:42 +0200 Message-Id: <1404398323-18934-5-git-send-email-tomeu.vizoso@collabora.com> X-Mailer: git-send-email 1.9.3 In-Reply-To: <1404398323-18934-1-git-send-email-tomeu.vizoso@collabora.com> References: <1404398323-18934-1-git-send-email-tomeu.vizoso@collabora.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140703_073946_666489_E0154AE1 X-CRM114-Status: GOOD ( 20.74 ) X-Spam-Score: 0.0 (/) Cc: Rabin Vincent , Javier Martinez Canillas , Tomeu Vizoso X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-1.8 required=5.0 tests=BAYES_00,DKIM_SIGNED, T_DKIM_INVALID, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=no version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Rabin Vincent When a clock has multiple users, the WARNING on imbalance of enable/disable may not show the guilty party since although they may have commited the error earlier, the warning is emitted later when some other user, presumably innocent, disables the clock. Provide per-user clock enable/disable accounting and disabler tracking in order to help debug these problems. NOTE: with this patch, clk_get_parent() behaves like clk_get(), i.e. it needs to be matched with a clk_put(). Otherwise, memory will leak. Signed-off-by: Tomeu Vizoso --- drivers/clk/clk-devres.c | 3 ++- drivers/clk/clk-highbank.c | 2 +- drivers/clk/clk.c | 52 ++++++++++++++++++++++++++++++++++++++--- drivers/clk/clk.h | 2 +- drivers/clk/clkdev.c | 26 +++++++++++++++++---- drivers/clk/qcom/mmcc-msm8960.c | 9 ++++--- drivers/clk/sunxi/clk-sunxi.c | 2 +- drivers/clk/tegra/clk.c | 2 +- drivers/clk/zynq/clkc.c | 12 +++++----- include/linux/clk-private.h | 4 ++++ include/linux/clk-provider.h | 3 ++- 11 files changed, 94 insertions(+), 23 deletions(-) diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c index 5f2aba9..d6a5e59 100644 --- a/drivers/clk/clk-devres.c +++ b/drivers/clk/clk-devres.c @@ -37,7 +37,8 @@ EXPORT_SYMBOL(devm_clk_provider_get); struct clk *devm_clk_get(struct device *dev, const char *id) { - return clk_core_to_clk(devm_clk_provider_get(dev, id)); + const char *dev_id = dev ? dev_name(dev) : NULL; + return clk_core_to_clk(devm_clk_provider_get(dev, id), dev_id, id); } EXPORT_SYMBOL(devm_clk_get); diff --git a/drivers/clk/clk-highbank.c b/drivers/clk/clk-highbank.c index cc1bae1..4e38856 100644 --- a/drivers/clk/clk-highbank.c +++ b/drivers/clk/clk-highbank.c @@ -331,7 +331,7 @@ CLK_OF_DECLARE(hb_a9periph, "calxeda,hb-a9periph-clock", hb_a9periph_init); static void __init hb_a9bus_init(struct device_node *node) { struct clk_core *clk = hb_clk_init(node, &a9bclk_ops); - clk_prepare_enable(clk_core_to_clk(clk)); + clk_prepare_enable(clk_core_to_clk(clk, node->full_name, NULL)); } CLK_OF_DECLARE(hb_a9bus, "calxeda,hb-a9bus-clock", hb_a9bus_init); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 644a824..b887c69 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -568,6 +568,28 @@ static int clk_disable_unused(void) } late_initcall_sync(clk_disable_unused); +struct clk *clk_core_to_clk(struct clk_core *clk_core, const char *dev, + const char *con) +{ + struct clk *clk; + + clk = kzalloc(sizeof(*clk), GFP_KERNEL); + if (!clk) + return ERR_PTR(-ENOMEM); + + clk->core = clk_core; + clk->dev_id = dev; + clk->con_id = con; + + return clk; +} +EXPORT_SYMBOL_GPL(clk_core_to_clk); + +struct clk_core *clk_to_clk_core(struct clk *clk) +{ + return clk->core; +} + /*** helper functions ***/ const char *__clk_get_name(struct clk_core *clk) @@ -935,7 +957,20 @@ static void __clk_disable_internal(struct clk_core *clk) */ void clk_disable(struct clk *clk_user) { - __clk_disable_internal(clk_to_clk_core(clk_user)); + struct clk_core *clk = clk_to_clk_core(clk_user); + unsigned long flags; + + flags = clk_enable_lock(); + if (!WARN(clk_user->enable_count == 0, + "incorrect disable clk dev %s con %s last disabler %pF\n", + clk_user->dev_id, clk_user->con_id, clk_user->last_disable)) { + + clk_user->last_disable = __builtin_return_address(0); + clk_user->enable_count--; + + __clk_disable(clk); + } + clk_enable_unlock(flags); } EXPORT_SYMBOL_GPL(clk_disable); @@ -995,7 +1030,17 @@ static int __clk_enable_internal(struct clk_core *clk) */ int clk_enable(struct clk *clk_user) { - return __clk_enable_internal(clk_to_clk_core(clk_user)); + struct clk_core *clk = clk_to_clk_core(clk_user); + unsigned long flags; + int ret; + + flags = clk_enable_lock(); + ret = __clk_enable(clk); + if (!ret) + clk_user->enable_count++; + clk_enable_unlock(flags); + + return ret; } EXPORT_SYMBOL_GPL(clk_enable); @@ -1671,8 +1716,9 @@ EXPORT_SYMBOL_GPL(clk_provider_get_parent); struct clk *clk_get_parent(struct clk *clk_user) { struct clk_core *clk = clk_to_clk_core(clk_user); + struct clk_core *parent = clk_provider_get_parent(clk); - return clk_core_to_clk(clk_provider_get_parent(clk)); + return clk_core_to_clk(parent, clk_user->dev_id, clk_user->con_id); } EXPORT_SYMBOL_GPL(clk_get_parent); diff --git a/drivers/clk/clk.h b/drivers/clk/clk.h index c69f4fe..2f682bf 100644 --- a/drivers/clk/clk.h +++ b/drivers/clk/clk.h @@ -17,5 +17,5 @@ void of_clk_unlock(void); #endif #if defined(CONFIG_COMMON_CLK) -#define clk_to_clk_core(clk) (clk->core) +struct clk_core *clk_to_clk_core(struct clk *clk); #endif diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c index 48811c0..5be6bb1 100644 --- a/drivers/clk/clkdev.c +++ b/drivers/clk/clkdev.c @@ -76,7 +76,9 @@ struct clk_core *of_clk_provider_get(struct device_node *np, int index) struct clk *of_clk_get(struct device_node *np, int index) { - return clk_core_to_clk(of_clk_provider_get(np, index)); + struct clk_core *clk = of_clk_provider_get(np, index); + + return clk_core_to_clk(clk, np->full_name, NULL); } EXPORT_SYMBOL(of_clk_get); @@ -128,7 +130,9 @@ struct clk_core *of_clk_provider_get_by_name(struct device_node *np, const char */ struct clk *of_clk_get_by_name(struct device_node *np, const char *name) { - return clk_core_to_clk(of_clk_provider_get_by_name(np, name)); + struct clk_core *clk = of_clk_provider_get_by_name(np, name); + + return clk_core_to_clk(clk, np->full_name, NULL); } EXPORT_SYMBOL(of_clk_get_by_name); #endif @@ -195,7 +199,9 @@ EXPORT_SYMBOL_GPL(clk_provider_get_sys); struct clk *clk_get_sys(const char *dev_id, const char *con_id) { - return clk_core_to_clk(clk_provider_get_sys(dev_id, con_id)); + struct clk_core *clk = clk_provider_get_sys(dev_id, con_id); + + return clk_core_to_clk(clk, dev_id, con_id); } EXPORT_SYMBOL(clk_get_sys); @@ -218,13 +224,23 @@ EXPORT_SYMBOL(clk_provider_get); struct clk *clk_get(struct device *dev, const char *con_id) { - return clk_core_to_clk(clk_provider_get(dev, con_id)); + const char *dev_id = dev ? dev_name(dev) : NULL; + + return clk_core_to_clk(clk_provider_get(dev, con_id), dev_id, con_id); } EXPORT_SYMBOL(clk_get); +static void clk_free_clk(struct clk *clk) +{ + kfree(clk); +} + void clk_put(struct clk *clk) { - __clk_put(clk_to_clk_core(clk)); + clk_core_t *core = clk_to_clk_core(clk); + + clk_free_clk(clk); + __clk_put(core); } EXPORT_SYMBOL(clk_put); diff --git a/drivers/clk/qcom/mmcc-msm8960.c b/drivers/clk/qcom/mmcc-msm8960.c index 28a5d30..47f21ea 100644 --- a/drivers/clk/qcom/mmcc-msm8960.c +++ b/drivers/clk/qcom/mmcc-msm8960.c @@ -470,7 +470,8 @@ static int pix_rdi_set_parent(struct clk_hw *hw, u8 index) * needs to be on at what time. */ for (i = 0; i < num_parents; i++) { - ret = clk_prepare_enable(clk_core_to_clk(clk_get_parent_by_index(clk, i))); + struct clk_core *parent = clk_get_parent_by_index(clk, i); + ret = clk_prepare_enable(clk_core_to_clk(parent, NULL, __clk_get_name(parent))); if (ret) goto err; } @@ -498,8 +499,10 @@ static int pix_rdi_set_parent(struct clk_hw *hw, u8 index) udelay(1); err: - for (i--; i >= 0; i--) - clk_disable_unprepare(clk_core_to_clk(clk_get_parent_by_index(clk, i))); + for (i--; i >= 0; i--) { + struct clk_core *parent = clk_get_parent_by_index(clk, i); + clk_disable_unprepare(clk_core_to_clk(parent, NULL, __clk_get_name(parent))); + } return ret; } diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 5de44c1..550a691 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -1196,7 +1196,7 @@ static void __init sunxi_init_clocks(const char *clocks[], int nclocks) struct clk_core *clk = clk_provider_get(NULL, clocks[i]); if (!IS_ERR(clk)) - clk_prepare_enable(clk_core_to_clk(clk)); + clk_prepare_enable(clk_core_to_clk(clk, NULL, clocks[i])); } } diff --git a/drivers/clk/tegra/clk.c b/drivers/clk/tegra/clk.c index 385d101..2a03d47 100644 --- a/drivers/clk/tegra/clk.c +++ b/drivers/clk/tegra/clk.c @@ -228,7 +228,7 @@ void __init tegra_init_from_table(struct tegra_clk_init_table *tbl, } if (tbl->state) - if (clk_prepare_enable(clk_core_to_clk(clk))) { + if (clk_prepare_enable(clk_core_to_clk(clk, NULL, __clk_get_name(clk)))) { pr_err("%s: Failed to enable %s\n", __func__, __clk_get_name(clk)); WARN_ON(1); diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index c58c540..ed3e1cba 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -154,7 +154,7 @@ static void __init zynq_clk_register_fclk(enum zynq_clk fclk, 0, CLK_GATE_SET_TO_DISABLE, fclk_gate_lock); enable_reg = clk_readl(fclk_gate_reg) & 1; if (enable && !enable_reg) { - if (clk_prepare_enable(clk_core_to_clk(clks[fclk]))) + if (clk_prepare_enable(clk_core_to_clk(clks[fclk], NULL, clk_name))) pr_warn("%s: FCLK%u enable failed\n", __func__, fclk - fclk0); } @@ -333,13 +333,13 @@ static void __init zynq_clk_setup(struct device_node *np) CLK_DIVIDER_ALLOW_ZERO, &ddrclk_lock); clks[ddr2x] = clk_register_gate(NULL, clk_output_name[ddr2x], "ddr2x_div", 0, SLCR_DDR_CLK_CTRL, 1, 0, &ddrclk_lock); - clk_prepare_enable(clk_core_to_clk(clks[ddr2x])); + clk_prepare_enable(clk_core_to_clk(clks[ddr2x], NULL, clk_output_name[ddr2x])); clk = clk_register_divider(NULL, "ddr3x_div", "ddrpll", 0, SLCR_DDR_CLK_CTRL, 20, 6, CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, &ddrclk_lock); clks[ddr3x] = clk_register_gate(NULL, clk_output_name[ddr3x], "ddr3x_div", 0, SLCR_DDR_CLK_CTRL, 0, 0, &ddrclk_lock); - clk_prepare_enable(clk_core_to_clk(clks[ddr3x])); + clk_prepare_enable(clk_core_to_clk(clks[ddr3x], NULL, clk_output_name[ddr3x])); clk = clk_register_divider(NULL, "dci_div0", "ddrpll", 0, SLCR_DCI_CLK_CTRL, 8, 6, CLK_DIVIDER_ONE_BASED | @@ -351,7 +351,7 @@ static void __init zynq_clk_setup(struct device_node *np) clks[dci] = clk_register_gate(NULL, clk_output_name[dci], "dci_div1", CLK_SET_RATE_PARENT, SLCR_DCI_CLK_CTRL, 0, 0, &dciclk_lock); - clk_prepare_enable(clk_core_to_clk(clks[dci])); + clk_prepare_enable(clk_core_to_clk(clks[dci], NULL, clk_output_name[dci])); /* Peripheral clocks */ for (i = fclk0; i <= fclk3; i++) { @@ -505,10 +505,10 @@ static void __init zynq_clk_setup(struct device_node *np) /* leave debug clocks in the state the bootloader set them up to */ tmp = clk_readl(SLCR_DBG_CLK_CTRL); if (tmp & DBG_CLK_CTRL_CLKACT_TRC) - if (clk_prepare_enable(clk_core_to_clk(clks[dbg_trc]))) + if (clk_prepare_enable(clk_core_to_clk(clks[dbg_trc], NULL, clk_output_name[dbg_trc]))) pr_warn("%s: trace clk enable failed\n", __func__); if (tmp & DBG_CLK_CTRL_CPU_1XCLKACT) - if (clk_prepare_enable(clk_core_to_clk(clks[dbg_apb]))) + if (clk_prepare_enable(clk_core_to_clk(clks[dbg_apb], NULL, clk_output_name[dbg_apb]))) pr_warn("%s: debug APB clk enable failed\n", __func__); /* One gated clock for all APER clocks. */ diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h index 2c1ece9..9657fc8 100644 --- a/include/linux/clk-private.h +++ b/include/linux/clk-private.h @@ -57,6 +57,10 @@ struct clk_core { struct clk { struct clk_core *core; + unsigned int enable_count; + const char *dev_id; + const char *con_id; + void *last_disable; }; /* diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 87de32c..abfc31a 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -533,7 +533,8 @@ struct clk_core *clk_provider_get_sys(const char *dev_id, const char *con_id); struct clk_core *clk_provider_get(struct device *dev, const char *con_id); struct clk_core *devm_clk_provider_get(struct device *dev, const char *id); -#define clk_core_to_clk(core) ((struct clk *)(core)) +struct clk *clk_core_to_clk(struct clk_core *clk_core, const char *dev, + const char *con); /* * FIXME clock api without lock protection