From patchwork Tue Sep 3 11:34:29 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chander Kashyap X-Patchwork-Id: 2853192 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 0F3B8C0AB5 for ; Tue, 3 Sep 2013 11:35:50 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id A5C752035E for ; Tue, 3 Sep 2013 11:35:48 +0000 (UTC) Received: from casper.infradead.org (casper.infradead.org [85.118.1.10]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 1A50A20353 for ; Tue, 3 Sep 2013 11:35:47 +0000 (UTC) Received: from merlin.infradead.org ([2001:4978:20e::2]) by casper.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VGotK-00076V-JL; Tue, 03 Sep 2013 11:35:26 +0000 Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1VGotE-0004gb-Mi; Tue, 03 Sep 2013 11:35:20 +0000 Received: from mail-pd0-f172.google.com ([209.85.192.172]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1VGot5-0004eI-0p for linux-arm-kernel@lists.infradead.org; Tue, 03 Sep 2013 11:35:12 +0000 Received: by mail-pd0-f172.google.com with SMTP id z10so5902142pdj.17 for ; Tue, 03 Sep 2013 04:34:49 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=Giik67804fHxDwc19ybBzim9FW/oZiUFdKoRKcxr878=; b=eNj0NlTR5AbQUnt7HtNXrIfUyM2L26ywaWYe4m5HfWQ2eMK6hAnLeT3Z9q5Ycog7Rs P/537h4BwDGtce7YqHeSfPprDhrV9gliHBgAhj8a3g+3PNRbSKdJ4vh/A7FygOt0oAKp xY9G9fGmRIh8eKJgEQw1iKFcykTrhEUKGaxITUjfcnlREzeJx2/TBhNNmERYMQ52EsaQ XrlcNl8yOZNWXSUPQhGP6DgvP5pscn8mxQOfonZAdeGUnQrRZ/P8ULgno82EeDiRDzOX q87OheXvba7rLwGUlaOZhP1TR6B/u3nBKlO1qIWcLDN69uWar0PMmcqlwMddwUDLaA38 9JDg== X-Gm-Message-State: ALoCoQlE8NRYWU1SH9sBUfv0Bzh1AeBCLEOQF+j0hAMqHMGp9Vc7spgQPYPk+n7XCiUl4Su/8oTK X-Received: by 10.66.251.1 with SMTP id zg1mr7646567pac.160.1378208089572; Tue, 03 Sep 2013 04:34:49 -0700 (PDT) Received: from localhost.localdomain ([115.113.119.130]) by mx.google.com with ESMTPSA id ts6sm21918962pbc.12.1969.12.31.16.00.00 (version=TLSv1.1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 03 Sep 2013 04:34:48 -0700 (PDT) From: Chander Kashyap To: linux-arm-kernel@lists.infradead.org Subject: [RFC Patch v2 1/3] clk: add support for temporary parent clock migration Date: Tue, 3 Sep 2013 17:04:29 +0530 Message-Id: <1378208072-10173-2-git-send-email-chander.kashyap@linaro.org> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1378208072-10173-1-git-send-email-chander.kashyap@linaro.org> References: <1378208072-10173-1-git-send-email-chander.kashyap@linaro.org> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130903_073511_222743_22C18832 X-CRM114-Status: GOOD ( 20.77 ) X-Spam-Score: -1.9 (-) Cc: thomas.abraham@linaro.org, linux-samsung-soc@vger.kernel.org, mturquette@linaro.org, Chander Kashyap X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 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=-6.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable 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 Some platforms use to migrate temporarily to another parent during cpu frequency scaling, e.g. Exynos and Tegra. Once the frequency is changed the latch on to original parent. The generic cpufreq-cpu0 driver use clk_set_rate API to scale cpu frequency. This patch is an attempt to address the above mentioned requirement. This is achieved as follows: Add a clk flag "CLK_SET_RATE_TEMP_PARENT" for clocks which need to migrate to another parent during set_rate operation on them. Add "temp_parent_name" and "tmp_parent" fields to clk structure i.e the name of temp_parent_clock and reference to temp_parent_clock. Hence in clk_set_rate API check for the "CLK_SET_RATE_TEMP_PARENT" flag, then latch on to alternate parent clock temporarily. Once the requested rate is set on the clk, re-parent back to original parent clock. Signed-off-by: Chander Kashyap --- drivers/clk/clk-mux.c | 13 +++++++------ drivers/clk/clk.c | 43 ++++++++++++++++++++++++++++++++++++++++-- include/linux/clk-private.h | 19 +++++++++++-------- include/linux/clk-provider.h | 10 ++++++---- 4 files changed, 65 insertions(+), 20 deletions(-) diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 4f96ff3..854b3ac 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -115,8 +115,8 @@ EXPORT_SYMBOL_GPL(clk_mux_ro_ops); struct clk *clk_register_mux_table(struct device *dev, const char *name, const char **parent_names, u8 num_parents, unsigned long flags, - void __iomem *reg, u8 shift, u32 mask, - u8 clk_mux_flags, u32 *table, spinlock_t *lock) + const char *temp_parent_name, void __iomem *reg, u8 shift, + u32 mask, u8 clk_mux_flags, u32 *table, spinlock_t *lock) { struct clk_mux *mux; struct clk *clk; @@ -146,6 +146,7 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, init.flags = flags | CLK_IS_BASIC; init.parent_names = parent_names; init.num_parents = num_parents; + init.temp_parent_name = temp_parent_name; /* struct clk_mux assignments */ mux->reg = reg; @@ -167,13 +168,13 @@ EXPORT_SYMBOL_GPL(clk_register_mux_table); struct clk *clk_register_mux(struct device *dev, const char *name, const char **parent_names, u8 num_parents, unsigned long flags, - void __iomem *reg, u8 shift, u8 width, - u8 clk_mux_flags, spinlock_t *lock) + const char *temp_parent_name, void __iomem *reg, u8 shift, + u8 width, u8 clk_mux_flags, spinlock_t *lock) { u32 mask = BIT(width) - 1; return clk_register_mux_table(dev, name, parent_names, num_parents, - flags, reg, shift, mask, clk_mux_flags, - NULL, lock); + flags, temp_parent_name, reg, shift, + mask, clk_mux_flags, NULL, lock); } EXPORT_SYMBOL_GPL(clk_register_mux); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 2db08c0..0e29a5b 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1425,8 +1425,8 @@ static void clk_change_rate(struct clk *clk) */ int clk_set_rate(struct clk *clk, unsigned long rate) { - struct clk *top, *fail_clk; - int ret = 0; + struct clk *top, *fail_clk, *parent = NULL; + int ret = 0, index; if (!clk) return 0; @@ -1450,6 +1450,35 @@ int clk_set_rate(struct clk *clk, unsigned long rate) goto out; } + /* Latch on to alternate parent temporarily if needed */ + if ((clk->flags & CLK_SET_RATE_TEMP_PARENT) && clk->temp_parent_name) { + /* Save current parent before latching on to alternate parent */ + parent = clk->parent; + + if (!clk->temp_parent) { + for (index = 0; index < clk->num_parents; index++) { + if (!strcmp(clk->parent_names[index], + clk->temp_parent_name)) + clk->temp_parent = + __clk_lookup(clk->temp_parent_name); + } + + if (!clk->temp_parent) { + pr_warn("%s: wrong temp_parent_name %s", + __func__, clk->temp_parent_name); + ret = -EINVAL; + goto out; + } + } + + ret = clk_set_parent(clk, clk->temp_parent); + if (ret) { + pr_warn("%s: failed to set %s parent\n", + __func__, clk->temp_parent->name); + goto out; + } + } + /* notify that we are about to change rates */ fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE); if (fail_clk) { @@ -1464,6 +1493,14 @@ int clk_set_rate(struct clk *clk, unsigned long rate) clk_change_rate(top); out: + /* Reparent back to original parent */ + if (parent) { + ret = clk_set_parent(clk, parent); + if (ret) + pr_warn("%s: failed to set %s parent\n", + __func__, parent->name); + } + clk_prepare_unlock(); return ret; @@ -1803,6 +1840,7 @@ struct clk *__clk_register(struct device *dev, struct clk_hw *hw) clk->flags = hw->init->flags; clk->parent_names = hw->init->parent_names; clk->num_parents = hw->init->num_parents; + clk->temp_parent_name = hw->init->temp_parent_name; ret = __clk_init(dev, clk); if (ret) @@ -1826,6 +1864,7 @@ static int _clk_register(struct device *dev, struct clk_hw *hw, struct clk *clk) clk->hw = hw; clk->flags = hw->init->flags; clk->num_parents = hw->init->num_parents; + clk->temp_parent_name = hw->init->temp_parent_name; hw->clk = clk; /* allocate local copy in case parent_names is __initdata */ diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h index 8138c94..b70ba4d 100644 --- a/include/linux/clk-private.h +++ b/include/linux/clk-private.h @@ -47,6 +47,8 @@ struct clk { #ifdef CONFIG_COMMON_CLK_DEBUG struct dentry *dentry; #endif + const char *temp_parent_name; + struct clk *temp_parent; }; /* @@ -59,7 +61,7 @@ struct clk { */ #define DEFINE_CLK(_name, _ops, _flags, _parent_names, \ - _parents) \ + _parents, _temp_parent_name) \ static struct clk _name = { \ .name = #_name, \ .ops = &_ops, \ @@ -68,6 +70,7 @@ struct clk { .num_parents = ARRAY_SIZE(_parent_names), \ .parents = _parents, \ .flags = _flags | CLK_IS_BASIC, \ + .temp_parent_name = _temp_parent_name, \ } #define DEFINE_CLK_FIXED_RATE(_name, _flags, _rate, \ @@ -82,7 +85,7 @@ struct clk { .flags = _fixed_rate_flags, \ }; \ DEFINE_CLK(_name, clk_fixed_rate_ops, _flags, \ - _name##_parent_names, NULL); + _name##_parent_names, NULL, NULL); #define DEFINE_CLK_GATE(_name, _parent_name, _parent_ptr, \ _flags, _reg, _bit_idx, \ @@ -104,7 +107,7 @@ struct clk { .lock = _lock, \ }; \ DEFINE_CLK(_name, clk_gate_ops, _flags, \ - _name##_parent_names, _name##_parents); + _name##_parent_names, _name##_parents, NULL); #define _DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \ _flags, _reg, _shift, _width, \ @@ -128,7 +131,7 @@ struct clk { .lock = _lock, \ }; \ DEFINE_CLK(_name, clk_divider_ops, _flags, \ - _name##_parent_names, _name##_parents); + _name##_parent_names, _name##_parents, NULL); #define DEFINE_CLK_DIVIDER(_name, _parent_name, _parent_ptr, \ _flags, _reg, _shift, _width, \ @@ -146,8 +149,8 @@ struct clk { _divider_flags, _table, _lock) \ #define DEFINE_CLK_MUX(_name, _parent_names, _parents, _flags, \ - _reg, _shift, _width, \ - _mux_flags, _lock) \ + _temp_parent_name, _reg, _shift,\ + _width, _mux_flags, _lock) \ static struct clk _name; \ static struct clk_mux _name##_hw = { \ .hw = { \ @@ -160,7 +163,7 @@ struct clk { .lock = _lock, \ }; \ DEFINE_CLK(_name, clk_mux_ops, _flags, _parent_names, \ - _parents); + _parents, _temp_parent_name); #define DEFINE_CLK_FIXED_FACTOR(_name, _parent_name, \ _parent_ptr, _flags, \ @@ -180,7 +183,7 @@ struct clk { .div = _div, \ }; \ DEFINE_CLK(_name, clk_fixed_factor_ops, _flags, \ - _name##_parent_names, _name##_parents); + _name##_parent_names, _name##_parents, NULL); /** * __clk_init - initialize the data structures in a struct clk diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 73bdb69..6d7e2aa 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -29,6 +29,7 @@ #define CLK_IS_BASIC BIT(5) /* Basic clk, can't do a to_clk_foo() */ #define CLK_GET_RATE_NOCACHE BIT(6) /* do not use the cached clk rate */ #define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */ +#define CLK_SET_RATE_TEMP_PARENT BIT(8) /* migrate to alternate parent */ struct clk_hw; @@ -158,6 +159,7 @@ struct clk_init_data { const char **parent_names; u8 num_parents; unsigned long flags; + const char *temp_parent_name; }; /** @@ -343,13 +345,13 @@ extern const struct clk_ops clk_mux_ro_ops; struct clk *clk_register_mux(struct device *dev, const char *name, const char **parent_names, u8 num_parents, unsigned long flags, - void __iomem *reg, u8 shift, u8 width, - u8 clk_mux_flags, spinlock_t *lock); + const char *temp_parent_name, void __iomem *reg, u8 shift, + u8 width, u8 clk_mux_flags, spinlock_t *lock); struct clk *clk_register_mux_table(struct device *dev, const char *name, const char **parent_names, u8 num_parents, unsigned long flags, - void __iomem *reg, u8 shift, u32 mask, - u8 clk_mux_flags, u32 *table, spinlock_t *lock); + const char *temp_parent_name, void __iomem *reg, u8 shift, + u32 mask, u8 clk_mux_flags, u32 *table, spinlock_t *lock); void of_fixed_factor_clk_setup(struct device_node *node);