From patchwork Fri Mar 20 13:26:35 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Rajendra Nayak X-Patchwork-Id: 13319 X-Patchwork-Delegate: khilman@deeprootsystems.com Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n2KDQeCl029350 for ; Fri, 20 Mar 2009 13:26:46 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753059AbZCTN0r (ORCPT ); Fri, 20 Mar 2009 09:26:47 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1753184AbZCTN0q (ORCPT ); Fri, 20 Mar 2009 09:26:46 -0400 Received: from arroyo.ext.ti.com ([192.94.94.40]:41103 "EHLO arroyo.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753059AbZCTN0q convert rfc822-to-8bit (ORCPT ); Fri, 20 Mar 2009 09:26:46 -0400 Received: from dbdp20.itg.ti.com ([172.24.170.38]) by arroyo.ext.ti.com (8.13.7/8.13.7) with ESMTP id n2KDQbn6021597 for ; Fri, 20 Mar 2009 08:26:43 -0500 Received: from dbde71.ent.ti.com (localhost [127.0.0.1]) by dbdp20.itg.ti.com (8.13.8/8.13.8) with ESMTP id n2KDQa0Q003108 for ; Fri, 20 Mar 2009 18:56:36 +0530 (IST) Received: from dbde02.ent.ti.com ([172.24.170.145]) by dbde71.ent.ti.com ([172.24.170.149]) with mapi; Fri, 20 Mar 2009 18:56:36 +0530 From: "Nayak, Rajendra" To: "linux-omap@vger.kernel.org" CC: "Nayak, Rajendra" Date: Fri, 20 Mar 2009 18:56:35 +0530 Subject: [PATCH 08/09] OMAP2/3 clock: implement clock notifier registration infrastructure Thread-Topic: [PATCH 08/09] OMAP2/3 clock: implement clock notifier registration infrastructure Thread-Index: AcmpX32LLUnMotJ+TC2E47pe+LCdDQ== Message-ID: <5A47E75E594F054BAF48C5E4FC4B92AB02FAF6EF26@dbde02.ent.ti.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: acceptlanguage: en-US MIME-Version: 1.0 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org From: Paul Walmsley This patch implements notifier registration and unregistration functions for clock rate changes. The implementation is via a blocking notifier, called with the clockfw spinlock held. Callback functions must not re-enter the clock framework. There are likely to be few users of these notifiers, compared to the total number of clocks. So, rather than storing one notifier per struct clk, notifiers are stored in a separate, dynamically allocated list, effectively trading execution speed (in terms of a sequential scan of the notifier list) for memory savings. The implementation is completely hidden from the callbacks and can be easily changed. Until prototypes for these functions are made available in include/linux/clk.h, drivers should pass function pointers to clk_notifier_register() and clk_notifier_unregister() via their platform_data struct. Signed-off-by: Paul Walmsley --- arch/arm/plat-omap/clock.c | 118 +++++++++++++++++++++++++++++++ arch/arm/plat-omap/include/mach/clock.h | 66 +++++++++++++++++ 2 files changed, 184 insertions(+), 0 deletions(-) diff --git a/arch/arm/plat-omap/clock.c b/arch/arm/plat-omap/clock.c index bdf2cd4..d76964a 100644 --- a/arch/arm/plat-omap/clock.c +++ b/arch/arm/plat-omap/clock.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,8 @@ static DEFINE_SPINLOCK(clockfw_lock); static struct clk_functions *arch_clock; +static LIST_HEAD(clk_notifier_list); + /** * omap_clk_for_each_child - call callback on each child clock of clk * @clk: struct clk * to use as the "parent" @@ -535,6 +538,121 @@ void clk_init_cpufreq_table(struct cpufreq_frequency_table **table) EXPORT_SYMBOL(clk_init_cpufreq_table); #endif +/** + * clk_notifier_register - add a clock parameter change notifier + * @clk: struct clk * to watch + * @nb: struct notifier_block * with callback info + * + * Request notification for changes to the clock 'clk'. This uses a + * blocking notifier. Callback code must not call back into the clock + * framework. Pre-notifier callbacks will be passed the previous and + * new rate of the clock. + * + * clk_notifier_register() must be called from process + * context. Returns -EINVAL if called with null arguments, -ENOMEM + * upon allocation failure; otherwise, passes along the return value + * of blocking_notifier_chain_register(). + */ +int clk_notifier_register(struct clk *clk, struct notifier_block *nb) +{ + struct clk_notifier *cn = NULL, *cn_new = NULL; + int r; + unsigned long flags; + struct clk *clkp; + + if (!clk || !nb) + return -EINVAL; + + /* Allocate this here speculatively to avoid GFP_ATOMIC */ + cn_new = kzalloc(sizeof(struct clk_notifier), GFP_KERNEL); + if (!cn_new) + return -ENOMEM; + + spin_lock_irqsave(&clockfw_lock, flags); + + list_for_each_entry(cn, &clk_notifier_list, node) + if (cn->clk == clk) + break; + + if (cn->clk != clk) { + cn_new->clk = clk; + BLOCKING_INIT_NOTIFIER_HEAD(&cn_new->notifier_head); + + list_add(&cn_new->node, &clk_notifier_list); + cn = cn_new; + } else { + kfree(cn_new); /* didn't need it after all */ + } + + r = blocking_notifier_chain_register(&cn->notifier_head, nb); + if (!r) { + clkp = clk; + do { + clkp->notifier_count++; + } while ((clkp = clkp->parent)); + } + + spin_unlock_irqrestore(&clockfw_lock, flags); + + return r; +} + +/** + * clk_notifier_unregister - remove a clock change notifier + * @clk: struct clk * + * @nb: struct notifier_block * with callback info + * + * Request no further notification for changes to clock 'clk'. This + * function presently does not release memory allocated by its + * corresponding _register function; see inline comments for more + * information. Returns -EINVAL if called with null arguments; + * otherwise, passes along the return value of + * blocking_notifier_chain_unregister(). + */ +int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb) +{ + struct clk_notifier *cn = NULL; + struct clk *clkp; + int r = -EINVAL; + unsigned long flags; + + if (!clk || !nb) + return -EINVAL; + + spin_lock_irqsave(&clockfw_lock, flags); + + list_for_each_entry(cn, &clk_notifier_list, node) + if (cn->clk == clk) + break; + + if (cn->clk == clk) { + r = blocking_notifier_chain_unregister(&cn->notifier_head, nb); + + if (!r) { + clkp = clk; + do { + clkp->notifier_count--; + } while ((clkp = clkp->parent)); + } + + /* + * XXX ugh, layering violation. there should be some + * support in the notifier code for this. + */ + if (!cn->notifier_head.head) + kfree(cn); + + } else { + r = -ENOENT; + } + + spin_unlock_irqrestore(&clockfw_lock, flags); + + return r; +} + + + /*-------------------------------------------------------------------------*/ #ifdef CONFIG_OMAP_RESET_CLOCKS diff --git a/arch/arm/plat-omap/include/mach/clock.h b/arch/arm/plat-omap/include/mach/clock.h index db57b71..41faba8 100644 --- a/arch/arm/plat-omap/include/mach/clock.h +++ b/arch/arm/plat-omap/include/mach/clock.h @@ -10,6 +10,8 @@ * published by the Free Software Foundation. */ +#include + #ifndef __ARCH_ARM_OMAP_CLOCK_H #define __ARCH_ARM_OMAP_CLOCK_H @@ -75,6 +77,40 @@ struct clk_child { u8 flags; }; +/** + * struct clk_notifier - associate a clk with a notifier + * @clk: struct clk * to associate the notifier with + * @notifier_head: a blocking_notifier_head for this clk + * @node: linked list pointers + * + * A list of struct clk_notifier is maintained by the notifier code. + * An entry is created whenever code registers the first notifier on a + * particular @clk. Future notifiers on that @clk are added to the + * @notifier_head. + */ +struct clk_notifier { + struct clk *clk; + struct blocking_notifier_head notifier_head; + struct list_head node; +}; + +/** + * struct clk_notifier_data - rate data to pass to the notifier callback + * @clk: struct clk * being changed + * @old_rate: previous rate of this clock + * @new_rate: new rate of this clock + * + * For a pre-notifier, old_rate is the clock's rate before this rate + * change, and new_rate is what the rate will be in the future. For a + * post-notifier, old_rate and new_rate are both set to the clock's + * current rate (this was done to optimize the implementation). + */ +struct clk_notifier_data { + struct clk *clk; + unsigned long old_rate; + unsigned long new_rate; +}; + struct clk { struct list_head node; const char *name; @@ -91,6 +127,7 @@ struct clk { void (*init)(struct clk *); int (*enable)(struct clk *); void (*disable)(struct clk *); + u16 notifier_count; __u8 enable_bit; __s8 usecount; u8 idlest_bit; @@ -144,6 +181,8 @@ extern void followparent_recalc(struct clk *clk, unsigned long parent_rate, extern void clk_allow_idle(struct clk *clk); extern void clk_deny_idle(struct clk *clk); extern void clk_enable_init_clocks(void); +extern int clk_notifier_register(struct clk *clk, struct notifier_block *nb); +extern int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb); #ifdef CONFIG_CPU_FREQ extern void clk_init_cpufreq_table(struct cpufreq_frequency_table **table); #endif @@ -201,4 +240,31 @@ void omap_clk_del_child(struct clk *clk, struct clk *clk2); #define CLK_REG_IN_PRM (1 << 0) #define CLK_REG_IN_SCM (1 << 1) +/* + * Clk notifier callback types + * + * Since the notifier is called with interrupts disabled, any actions + * taken by callbacks must be extremely fast and lightweight. + * + * CLK_PRE_RATE_CHANGE - called after all callbacks have approved the + * rate change, immediately before the clock rate is changed, to + * indicate that the rate change will proceed. Drivers must + * immediately terminate any operations that will be affected by + * the rate change. Callbacks must always return NOTIFY_DONE. + * + * CLK_ABORT_RATE_CHANGE: called if the rate change failed for some + * reason after CLK_PRE_RATE_CHANGE. In this case, all registered + * notifiers on the clock will be called with + * CLK_ABORT_RATE_CHANGE. Callbacks must always return + * NOTIFY_DONE. + * + * CLK_POST_RATE_CHANGE - called after the clock rate change has + * successfully completed. Callbacks must always return + * NOTIFY_DONE. + * + */ +#define CLK_ABORT_RATE_CHANGE 2 +#define CLK_PRE_RATE_CHANGE 3 +#define CLK_POST_RATE_CHANGE 4 + #endif