From patchwork Fri Mar 22 15:43:50 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Hogan X-Patchwork-Id: 2321481 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by patchwork1.kernel.org (Postfix) with ESMTP id 122B83FD8C for ; Fri, 22 Mar 2013 15:49:10 +0000 (UTC) Received: from localhost ([::1] helo=merlin.infradead.org) by merlin.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1UJ4At-0007jB-6d; Fri, 22 Mar 2013 15:46:35 +0000 Received: from multi.imgtec.com ([194.200.65.239]) by merlin.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1UJ49N-0007GQ-76 for linux-arm-kernel@lists.infradead.org; Fri, 22 Mar 2013 15:45:03 +0000 From: James Hogan To: Mike Turquette , , Subject: [RFC PATCH v1 2/3] clk: add support for clock remuxing Date: Fri, 22 Mar 2013 15:43:50 +0000 Message-ID: <1363967031-22781-3-git-send-email-james.hogan@imgtec.com> X-Mailer: git-send-email 1.8.1.2 In-Reply-To: <1363967031-22781-1-git-send-email-james.hogan@imgtec.com> References: <1363967031-22781-1-git-send-email-james.hogan@imgtec.com> MIME-Version: 1.0 X-SEF-Processed: 7_3_0_01181__2013_03_22_15_44_55 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20130322_114501_511855_9E01CB0E X-CRM114-Status: GOOD ( 22.80 ) X-Spam-Score: -4.4 (----) X-Spam-Report: SpamAssassin version 3.3.2 on merlin.infradead.org summary: Content analysis details: (-4.4 points) pts rule name description ---- ---------------------- -------------------------------------------------- -2.5 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] Cc: James Hogan , Sascha Hauer , Chao Xie 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: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org Add support for automatic selection of the best parent for a mux, i.e. the one which can provide the closest clock rate to that requested. This is by way of adding a parameter to the round_rate clock op which allows the clock to optionally select a different parent index. This is used in clk_calc_new_rates to decide whether to initiate a set_parent operation. This splits out the mutex protected portion of clk_set_parent() into a separate __clk_set_parent_notify() which takes care of sending clock change notifications. A new clock flag is also added called CLK_SET_RATE_REMUX to indicate that the clock can have it's parent changed automatically in response to a set_rate. It isn't used yet, but will be used within clock mux drivers. Signed-off-by: James Hogan --- drivers/clk/clk.c | 94 +++++++++++++++++++++++++++++++------------- include/linux/clk-provider.h | 6 ++- 2 files changed, 71 insertions(+), 29 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 79d5deb..3886ccd 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -36,6 +36,8 @@ static struct dentry *rootdir; static struct dentry *orphandir; static int inited = 0; +static int __clk_set_parent_notify(struct clk *clk, struct clk *parent); + static void clk_summary_show_one(struct seq_file *s, struct clk *c, int level) { if (!c) @@ -737,7 +739,7 @@ unsigned long __clk_round_rate(struct clk *clk, unsigned long rate) if (clk->parent) parent_rate = clk->parent->rate; - return clk->ops->round_rate(clk->hw, rate, &parent_rate); + return clk->ops->round_rate(clk->hw, rate, &parent_rate, NULL); } /** @@ -928,8 +930,10 @@ static void clk_calc_subtree(struct clk *clk, unsigned long new_rate) static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) { struct clk *top = clk; + struct clk *parent; unsigned long best_parent_rate = 0; unsigned long new_rate; + u8 old_parent = 0, best_parent; /* sanity */ if (IS_ERR_OR_NULL(clk)) @@ -939,13 +943,21 @@ static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) if (clk->parent) best_parent_rate = clk->parent->rate; + /* by default don't change the parent */ + if (clk->ops->get_parent) + old_parent = clk->ops->get_parent(clk->hw); + best_parent = old_parent; + parent = clk->parent; + /* never propagate up to the parent */ if (!(clk->flags & CLK_SET_RATE_PARENT)) { if (!clk->ops->round_rate) { clk->new_rate = clk->rate; return NULL; } - new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); + new_rate = clk->ops->round_rate(clk->hw, rate, + &best_parent_rate, + &best_parent); goto out; } @@ -962,15 +974,36 @@ static struct clk *clk_calc_new_rates(struct clk *clk, unsigned long rate) goto out; } - new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate); + new_rate = clk->ops->round_rate(clk->hw, rate, &best_parent_rate, + &best_parent); - if (best_parent_rate != clk->parent->rate) { - top = clk_calc_new_rates(clk->parent, best_parent_rate); + if (best_parent != old_parent) { + parent = __clk_get_parent_by_index(clk, best_parent); + /* couldn't find requested parent */ + if (!parent) + return NULL; + } + + if (best_parent_rate != parent->rate) { + top = clk_calc_new_rates(parent, best_parent_rate); goto out; } out: + /* the parent may have changed */ + if (best_parent != old_parent) { + parent = __clk_get_parent_by_index(clk, best_parent); + /* couldn't find requested parent */ + if (!parent) { + parent = clk->parent; + best_parent = old_parent; + } + if (best_parent != old_parent) + if (__clk_set_parent_notify(clk, parent)) + return NULL; + } + clk_calc_subtree(clk, new_rate); return top; @@ -1270,31 +1303,10 @@ out: return ret; } -/** - * clk_set_parent - switch the parent of a mux clk - * @clk: the mux clk whose input we are switching - * @parent: the new input to clk - * - * Re-parent clk to use parent as it's new input source. If clk has the - * CLK_SET_PARENT_GATE flag set then clk must be gated for this - * operation to succeed. After successfully changing clk's parent - * clk_set_parent will update the clk topology, sysfs topology and - * propagate rate recalculation via __clk_recalc_rates. Returns 0 on - * success, -EERROR otherwise. - */ -int clk_set_parent(struct clk *clk, struct clk *parent) +static int __clk_set_parent_notify(struct clk *clk, struct clk *parent) { int ret = 0; - if (!clk || !clk->ops) - return -EINVAL; - - if (!clk->ops->set_parent) - return -ENOSYS; - - /* prevent racing with updates to the clock topology */ - mutex_lock(&prepare_lock); - if (clk->parent == parent) goto out; @@ -1322,6 +1334,34 @@ int clk_set_parent(struct clk *clk, struct clk *parent) __clk_reparent(clk, parent); out: + return ret; +} + +/** + * clk_set_parent - switch the parent of a mux clk + * @clk: the mux clk whose input we are switching + * @parent: the new input to clk + * + * Re-parent clk to use parent as it's new input source. If clk has the + * CLK_SET_PARENT_GATE flag set then clk must be gated for this + * operation to succeed. After successfully changing clk's parent + * clk_set_parent will update the clk topology, sysfs topology and + * propagate rate recalculation via __clk_recalc_rates. Returns 0 on + * success, -EERROR otherwise. + */ +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + int ret; + + if (!clk || !clk->ops) + return -EINVAL; + + if (!clk->ops->set_parent) + return -ENOSYS; + + /* prevent racing with updates to the clock topology */ + mutex_lock(&prepare_lock); + ret = __clk_set_parent_notify(clk, parent); mutex_unlock(&prepare_lock); return ret; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 4e0b634..cdad3ce 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -27,6 +27,7 @@ #define CLK_IS_ROOT BIT(4) /* root clk, has no parent */ #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_REMUX BIT(7) /* find best parent for rate change */ struct clk_hw; @@ -69,7 +70,8 @@ struct clk_hw; * this op is not set then clock rate will be initialized to 0. * * @round_rate: Given a target rate as input, returns the closest rate actually - * supported by the clock. + * supported by the clock, and optionally the index of the parent + * that should be used to provide the clock rate. * * @get_parent: Queries the hardware to determine the parent of a clock. The * return value is a u8 which specifies the index corresponding to @@ -115,7 +117,7 @@ struct clk_ops { unsigned long (*recalc_rate)(struct clk_hw *hw, unsigned long parent_rate); long (*round_rate)(struct clk_hw *hw, unsigned long, - unsigned long *); + unsigned long *, u8 *index); int (*set_parent)(struct clk_hw *hw, u8 index); u8 (*get_parent)(struct clk_hw *hw); int (*set_rate)(struct clk_hw *hw, unsigned long,