Loading drivers/clk/qcom/clock.c +170 −3 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ static LIST_HEAD(handoff_vdd_list); static DEFINE_MUTEX(msm_clock_init_lock); LIST_HEAD(orphan_clk_list); static LIST_HEAD(clk_notifier_list); /* Find the voltage level required for a given rate. */ int find_vdd_level(struct clk *clk, unsigned long rate) Loading Loading @@ -474,6 +475,163 @@ int clk_reset(struct clk *clk, enum clk_reset_action action) } EXPORT_SYMBOL(clk_reset); /** * __clk_notify - call clk notifier chain * @clk: struct clk * that is changing rate * @msg: clk notifier type (see include/linux/clk.h) * @old_rate: old clk rate * @new_rate: new clk rate * * Triggers a notifier call chain on the clk rate-change notification * for 'clk'. Passes a pointer to the struct clk and the previous * and current rates to the notifier callback. Intended to be called by * internal clock code only. Returns NOTIFY_DONE from the last driver * called if all went well, or NOTIFY_STOP or NOTIFY_BAD immediately if * a driver returns that. */ static int __clk_notify(struct clk *clk, unsigned long msg, unsigned long old_rate, unsigned long new_rate) { struct msm_clk_notifier *cn; struct msm_clk_notifier_data cnd; int ret = NOTIFY_DONE; cnd.clk = clk; cnd.old_rate = old_rate; cnd.new_rate = new_rate; list_for_each_entry(cn, &clk_notifier_list, node) { if (cn->clk == clk) { ret = srcu_notifier_call_chain(&cn->notifier_head, msg, &cnd); break; } } return ret; } /* * clk rate change notifiers * * Note - The following notifier functionality is a verbatim copy * of the implementation in the common clock framework, copied here * until MSM switches to the common clock framework. */ /** * msm_clk_notif_register - add a clk rate change notifier * @clk: struct clk * to watch * @nb: struct notifier_block * with callback info * * Request notification when clk's rate changes. This uses an SRCU * notifier because we want it to block and notifier unregistrations are * uncommon. The callbacks associated with the notifier must not * re-enter into the clk framework by calling any top-level clk APIs; * this will cause a nested prepare_lock mutex. * * Pre-change notifier callbacks will be passed the current, pre-change * rate of the clk via struct msm_clk_notifier_data.old_rate. The new, * post-change rate of the clk is passed via struct * msm_clk_notifier_data.new_rate. * * Post-change notifiers will pass the now-current, post-change rate of * the clk in both struct msm_clk_notifier_data.old_rate and struct * msm_clk_notifier_data.new_rate. * * Abort-change notifiers are effectively the opposite of pre-change * notifiers: the original pre-change clk rate is passed in via struct * msm_clk_notifier_data.new_rate and the failed post-change rate is passed * in via struct msm_clk_notifier_data.old_rate. * * msm_clk_notif_register() must be called from non-atomic context. * Returns -EINVAL if called with null arguments, -ENOMEM upon * allocation failure; otherwise, passes along the return value of * srcu_notifier_chain_register(). */ int msm_clk_notif_register(struct clk *clk, struct notifier_block *nb) { struct msm_clk_notifier *cn; int ret = -ENOMEM; if (!clk || !nb) return -EINVAL; mutex_lock(&clk->prepare_lock); /* search the list of notifiers for this clk */ list_for_each_entry(cn, &clk_notifier_list, node) if (cn->clk == clk) break; /* if clk wasn't in the notifier list, allocate new clk_notifier */ if (cn->clk != clk) { cn = kzalloc(sizeof(struct msm_clk_notifier), GFP_KERNEL); if (!cn) goto out; cn->clk = clk; srcu_init_notifier_head(&cn->notifier_head); list_add(&cn->node, &clk_notifier_list); } ret = srcu_notifier_chain_register(&cn->notifier_head, nb); clk->notifier_count++; out: mutex_unlock(&clk->prepare_lock); return ret; } /** * msm_clk_notif_unregister - remove a clk rate change notifier * @clk: struct clk * * @nb: struct notifier_block * with callback info * * Request no further notification for changes to 'clk' and frees memory * allocated in msm_clk_notifier_register. * * Returns -EINVAL if called with null arguments; otherwise, passes * along the return value of srcu_notifier_chain_unregister(). */ int msm_clk_notif_unregister(struct clk *clk, struct notifier_block *nb) { struct msm_clk_notifier *cn = NULL; int ret = -EINVAL; if (!clk || !nb) return -EINVAL; mutex_lock(&clk->prepare_lock); list_for_each_entry(cn, &clk_notifier_list, node) if (cn->clk == clk) break; if (cn->clk == clk) { ret = srcu_notifier_chain_unregister(&cn->notifier_head, nb); clk->notifier_count--; /* XXX the notifier code should handle this better */ if (!cn->notifier_head.head) { srcu_cleanup_notifier_head(&cn->notifier_head); list_del(&cn->node); kfree(cn); } } else { ret = -ENOENT; } mutex_unlock(&clk->prepare_lock); return ret; } unsigned long clk_get_rate(struct clk *clk) { if (IS_ERR_OR_NULL(clk)) Loading Loading @@ -514,10 +672,14 @@ int clk_set_rate(struct clk *clk, unsigned long rate) start_rate = clk->rate; if (clk->ops->pre_set_rate) if (clk->notifier_count) __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, rate); if (clk->ops->pre_set_rate) { rc = clk->ops->pre_set_rate(clk, rate); if (rc) goto out; goto abort_set_rate; } /* Enforce vdd requirements for target frequency. */ if (clk->prepare_count) { Loading @@ -538,10 +700,15 @@ int clk_set_rate(struct clk *clk, unsigned long rate) if (clk->ops->post_set_rate) clk->ops->post_set_rate(clk, start_rate); if (clk->notifier_count) __clk_notify(clk, POST_RATE_CHANGE, start_rate, clk->rate); out: mutex_unlock(&clk->prepare_lock); return rc; abort_set_rate: __clk_notify(clk, ABORT_RATE_CHANGE, clk->rate, rate); err_set_rate: if (clk->prepare_count) unvote_rate_vdd(clk, rate); Loading include/linux/clk/msm-clk-provider.h +1 −0 Original line number Diff line number Diff line Loading @@ -185,6 +185,7 @@ struct clk { struct list_head list; unsigned count; unsigned notifier_count; spinlock_t lock; unsigned prepare_count; struct mutex prepare_lock; Loading include/linux/clk/msm-clk.h +63 −1 Original line number Diff line number Diff line /* Copyright (c) 2009, 2012-2013 The Linux Foundation. All rights reserved. /* Copyright (c) 2009, 2012-2014 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -12,6 +12,8 @@ #ifndef __MACH_CLK_H #define __MACH_CLK_H #include <linux/notifier.h> #define CLKFLAG_INVERT 0x00000001 #define CLKFLAG_NOINVERT 0x00000002 #define CLKFLAG_NONEST 0x00000004 Loading Loading @@ -57,4 +59,64 @@ int parent_to_src_sel(struct clk_src *parents, int num_parents, struct clk *p); /* returns the mux selection index associated with a particular parent */ int clk_get_parent_sel(struct clk *c, struct clk *parent); /** * DOC: clk notifier callback types * * PRE_RATE_CHANGE - called immediately before the clk 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 may either return NOTIFY_DONE, NOTIFY_OK, * NOTIFY_STOP or NOTIFY_BAD. * * ABORT_RATE_CHANGE: called if the rate change failed for some reason * after PRE_RATE_CHANGE. In this case, all registered notifiers on * the clk will be called with ABORT_RATE_CHANGE. Callbacks must * always return NOTIFY_DONE or NOTIFY_OK. * * POST_RATE_CHANGE - called after the clk rate change has successfully * completed. Callbacks must always return NOTIFY_DONE or NOTIFY_OK. * */ #define PRE_RATE_CHANGE BIT(0) #define POST_RATE_CHANGE BIT(1) #define ABORT_RATE_CHANGE BIT(2) /** * struct msm_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 msm_clk_notifier { struct clk *clk; struct srcu_notifier_head notifier_head; struct list_head node; }; /** * struct msm_clk_notifier_data - rate data to pass to the notifier callback * @clk: struct clk * being changed * @old_rate: previous rate of this clk * @new_rate: new rate of this clk * * For a pre-notifier, old_rate is the clk'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 clk's * current rate (this was done to optimize the implementation). */ struct msm_clk_notifier_data { struct clk *clk; unsigned long old_rate; unsigned long new_rate; }; int msm_clk_notif_register(struct clk *clk, struct notifier_block *nb); int msm_clk_notif_unregister(struct clk *clk, struct notifier_block *nb); #endif Loading
drivers/clk/qcom/clock.c +170 −3 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ static LIST_HEAD(handoff_vdd_list); static DEFINE_MUTEX(msm_clock_init_lock); LIST_HEAD(orphan_clk_list); static LIST_HEAD(clk_notifier_list); /* Find the voltage level required for a given rate. */ int find_vdd_level(struct clk *clk, unsigned long rate) Loading Loading @@ -474,6 +475,163 @@ int clk_reset(struct clk *clk, enum clk_reset_action action) } EXPORT_SYMBOL(clk_reset); /** * __clk_notify - call clk notifier chain * @clk: struct clk * that is changing rate * @msg: clk notifier type (see include/linux/clk.h) * @old_rate: old clk rate * @new_rate: new clk rate * * Triggers a notifier call chain on the clk rate-change notification * for 'clk'. Passes a pointer to the struct clk and the previous * and current rates to the notifier callback. Intended to be called by * internal clock code only. Returns NOTIFY_DONE from the last driver * called if all went well, or NOTIFY_STOP or NOTIFY_BAD immediately if * a driver returns that. */ static int __clk_notify(struct clk *clk, unsigned long msg, unsigned long old_rate, unsigned long new_rate) { struct msm_clk_notifier *cn; struct msm_clk_notifier_data cnd; int ret = NOTIFY_DONE; cnd.clk = clk; cnd.old_rate = old_rate; cnd.new_rate = new_rate; list_for_each_entry(cn, &clk_notifier_list, node) { if (cn->clk == clk) { ret = srcu_notifier_call_chain(&cn->notifier_head, msg, &cnd); break; } } return ret; } /* * clk rate change notifiers * * Note - The following notifier functionality is a verbatim copy * of the implementation in the common clock framework, copied here * until MSM switches to the common clock framework. */ /** * msm_clk_notif_register - add a clk rate change notifier * @clk: struct clk * to watch * @nb: struct notifier_block * with callback info * * Request notification when clk's rate changes. This uses an SRCU * notifier because we want it to block and notifier unregistrations are * uncommon. The callbacks associated with the notifier must not * re-enter into the clk framework by calling any top-level clk APIs; * this will cause a nested prepare_lock mutex. * * Pre-change notifier callbacks will be passed the current, pre-change * rate of the clk via struct msm_clk_notifier_data.old_rate. The new, * post-change rate of the clk is passed via struct * msm_clk_notifier_data.new_rate. * * Post-change notifiers will pass the now-current, post-change rate of * the clk in both struct msm_clk_notifier_data.old_rate and struct * msm_clk_notifier_data.new_rate. * * Abort-change notifiers are effectively the opposite of pre-change * notifiers: the original pre-change clk rate is passed in via struct * msm_clk_notifier_data.new_rate and the failed post-change rate is passed * in via struct msm_clk_notifier_data.old_rate. * * msm_clk_notif_register() must be called from non-atomic context. * Returns -EINVAL if called with null arguments, -ENOMEM upon * allocation failure; otherwise, passes along the return value of * srcu_notifier_chain_register(). */ int msm_clk_notif_register(struct clk *clk, struct notifier_block *nb) { struct msm_clk_notifier *cn; int ret = -ENOMEM; if (!clk || !nb) return -EINVAL; mutex_lock(&clk->prepare_lock); /* search the list of notifiers for this clk */ list_for_each_entry(cn, &clk_notifier_list, node) if (cn->clk == clk) break; /* if clk wasn't in the notifier list, allocate new clk_notifier */ if (cn->clk != clk) { cn = kzalloc(sizeof(struct msm_clk_notifier), GFP_KERNEL); if (!cn) goto out; cn->clk = clk; srcu_init_notifier_head(&cn->notifier_head); list_add(&cn->node, &clk_notifier_list); } ret = srcu_notifier_chain_register(&cn->notifier_head, nb); clk->notifier_count++; out: mutex_unlock(&clk->prepare_lock); return ret; } /** * msm_clk_notif_unregister - remove a clk rate change notifier * @clk: struct clk * * @nb: struct notifier_block * with callback info * * Request no further notification for changes to 'clk' and frees memory * allocated in msm_clk_notifier_register. * * Returns -EINVAL if called with null arguments; otherwise, passes * along the return value of srcu_notifier_chain_unregister(). */ int msm_clk_notif_unregister(struct clk *clk, struct notifier_block *nb) { struct msm_clk_notifier *cn = NULL; int ret = -EINVAL; if (!clk || !nb) return -EINVAL; mutex_lock(&clk->prepare_lock); list_for_each_entry(cn, &clk_notifier_list, node) if (cn->clk == clk) break; if (cn->clk == clk) { ret = srcu_notifier_chain_unregister(&cn->notifier_head, nb); clk->notifier_count--; /* XXX the notifier code should handle this better */ if (!cn->notifier_head.head) { srcu_cleanup_notifier_head(&cn->notifier_head); list_del(&cn->node); kfree(cn); } } else { ret = -ENOENT; } mutex_unlock(&clk->prepare_lock); return ret; } unsigned long clk_get_rate(struct clk *clk) { if (IS_ERR_OR_NULL(clk)) Loading Loading @@ -514,10 +672,14 @@ int clk_set_rate(struct clk *clk, unsigned long rate) start_rate = clk->rate; if (clk->ops->pre_set_rate) if (clk->notifier_count) __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, rate); if (clk->ops->pre_set_rate) { rc = clk->ops->pre_set_rate(clk, rate); if (rc) goto out; goto abort_set_rate; } /* Enforce vdd requirements for target frequency. */ if (clk->prepare_count) { Loading @@ -538,10 +700,15 @@ int clk_set_rate(struct clk *clk, unsigned long rate) if (clk->ops->post_set_rate) clk->ops->post_set_rate(clk, start_rate); if (clk->notifier_count) __clk_notify(clk, POST_RATE_CHANGE, start_rate, clk->rate); out: mutex_unlock(&clk->prepare_lock); return rc; abort_set_rate: __clk_notify(clk, ABORT_RATE_CHANGE, clk->rate, rate); err_set_rate: if (clk->prepare_count) unvote_rate_vdd(clk, rate); Loading
include/linux/clk/msm-clk-provider.h +1 −0 Original line number Diff line number Diff line Loading @@ -185,6 +185,7 @@ struct clk { struct list_head list; unsigned count; unsigned notifier_count; spinlock_t lock; unsigned prepare_count; struct mutex prepare_lock; Loading
include/linux/clk/msm-clk.h +63 −1 Original line number Diff line number Diff line /* Copyright (c) 2009, 2012-2013 The Linux Foundation. All rights reserved. /* Copyright (c) 2009, 2012-2014 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -12,6 +12,8 @@ #ifndef __MACH_CLK_H #define __MACH_CLK_H #include <linux/notifier.h> #define CLKFLAG_INVERT 0x00000001 #define CLKFLAG_NOINVERT 0x00000002 #define CLKFLAG_NONEST 0x00000004 Loading Loading @@ -57,4 +59,64 @@ int parent_to_src_sel(struct clk_src *parents, int num_parents, struct clk *p); /* returns the mux selection index associated with a particular parent */ int clk_get_parent_sel(struct clk *c, struct clk *parent); /** * DOC: clk notifier callback types * * PRE_RATE_CHANGE - called immediately before the clk 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 may either return NOTIFY_DONE, NOTIFY_OK, * NOTIFY_STOP or NOTIFY_BAD. * * ABORT_RATE_CHANGE: called if the rate change failed for some reason * after PRE_RATE_CHANGE. In this case, all registered notifiers on * the clk will be called with ABORT_RATE_CHANGE. Callbacks must * always return NOTIFY_DONE or NOTIFY_OK. * * POST_RATE_CHANGE - called after the clk rate change has successfully * completed. Callbacks must always return NOTIFY_DONE or NOTIFY_OK. * */ #define PRE_RATE_CHANGE BIT(0) #define POST_RATE_CHANGE BIT(1) #define ABORT_RATE_CHANGE BIT(2) /** * struct msm_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 msm_clk_notifier { struct clk *clk; struct srcu_notifier_head notifier_head; struct list_head node; }; /** * struct msm_clk_notifier_data - rate data to pass to the notifier callback * @clk: struct clk * being changed * @old_rate: previous rate of this clk * @new_rate: new rate of this clk * * For a pre-notifier, old_rate is the clk'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 clk's * current rate (this was done to optimize the implementation). */ struct msm_clk_notifier_data { struct clk *clk; unsigned long old_rate; unsigned long new_rate; }; int msm_clk_notif_register(struct clk *clk, struct notifier_block *nb); int msm_clk_notif_unregister(struct clk *clk, struct notifier_block *nb); #endif