From d6968fca789700afd723347d9d583795b19224b8 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 30 Apr 2015 13:54:13 -0700 Subject: clk: s/clk/core/ for struct clk_core While introducing struct clk_core we tried to minimize the diff by changing the type of 'clk' variables from struct clk to struct clk_core without changing the names of the variables. Now that the split is complete, the code is slightly confusing when it mixes variables called 'clk' and variables called 'core' that are of the same type struct clk_core. Let's be consistent and use 'core' everywhere we have a struct clk_core pointer and 'clk' when we have a struct clk pointer. Cc: Tomeu Vizoso Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 824 +++++++++++++++++++++++++++--------------------------- 1 file changed, 412 insertions(+), 412 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 459ce9da13e0..0b3c914db1ca 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -37,11 +37,11 @@ static HLIST_HEAD(clk_root_list); static HLIST_HEAD(clk_orphan_list); static LIST_HEAD(clk_notifier_list); -static long clk_core_get_accuracy(struct clk_core *clk); -static unsigned long clk_core_get_rate(struct clk_core *clk); -static int clk_core_get_phase(struct clk_core *clk); -static bool clk_core_is_prepared(struct clk_core *clk); -static bool clk_core_is_enabled(struct clk_core *clk); +static long clk_core_get_accuracy(struct clk_core *core); +static unsigned long clk_core_get_rate(struct clk_core *core); +static int clk_core_get_phase(struct clk_core *core); +static bool clk_core_is_prepared(struct clk_core *core); +static bool clk_core_is_enabled(struct clk_core *core); static struct clk_core *clk_core_lookup(const char *name); /*** private data structures ***/ @@ -293,59 +293,59 @@ static const struct file_operations clk_dump_fops = { .release = single_release, }; -static int clk_debug_create_one(struct clk_core *clk, struct dentry *pdentry) +static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) { struct dentry *d; int ret = -ENOMEM; - if (!clk || !pdentry) { + if (!core || !pdentry) { ret = -EINVAL; goto out; } - d = debugfs_create_dir(clk->name, pdentry); + d = debugfs_create_dir(core->name, pdentry); if (!d) goto out; - clk->dentry = d; + core->dentry = d; - d = debugfs_create_u32("clk_rate", S_IRUGO, clk->dentry, - (u32 *)&clk->rate); + d = debugfs_create_u32("clk_rate", S_IRUGO, core->dentry, + (u32 *)&core->rate); if (!d) goto err_out; - d = debugfs_create_u32("clk_accuracy", S_IRUGO, clk->dentry, - (u32 *)&clk->accuracy); + d = debugfs_create_u32("clk_accuracy", S_IRUGO, core->dentry, + (u32 *)&core->accuracy); if (!d) goto err_out; - d = debugfs_create_u32("clk_phase", S_IRUGO, clk->dentry, - (u32 *)&clk->phase); + d = debugfs_create_u32("clk_phase", S_IRUGO, core->dentry, + (u32 *)&core->phase); if (!d) goto err_out; - d = debugfs_create_x32("clk_flags", S_IRUGO, clk->dentry, - (u32 *)&clk->flags); + d = debugfs_create_x32("clk_flags", S_IRUGO, core->dentry, + (u32 *)&core->flags); if (!d) goto err_out; - d = debugfs_create_u32("clk_prepare_count", S_IRUGO, clk->dentry, - (u32 *)&clk->prepare_count); + d = debugfs_create_u32("clk_prepare_count", S_IRUGO, core->dentry, + (u32 *)&core->prepare_count); if (!d) goto err_out; - d = debugfs_create_u32("clk_enable_count", S_IRUGO, clk->dentry, - (u32 *)&clk->enable_count); + d = debugfs_create_u32("clk_enable_count", S_IRUGO, core->dentry, + (u32 *)&core->enable_count); if (!d) goto err_out; - d = debugfs_create_u32("clk_notifier_count", S_IRUGO, clk->dentry, - (u32 *)&clk->notifier_count); + d = debugfs_create_u32("clk_notifier_count", S_IRUGO, core->dentry, + (u32 *)&core->notifier_count); if (!d) goto err_out; - if (clk->ops->debug_init) { - ret = clk->ops->debug_init(clk->hw, clk->dentry); + if (core->ops->debug_init) { + ret = core->ops->debug_init(core->hw, core->dentry); if (ret) goto err_out; } @@ -354,31 +354,31 @@ static int clk_debug_create_one(struct clk_core *clk, struct dentry *pdentry) goto out; err_out: - debugfs_remove_recursive(clk->dentry); - clk->dentry = NULL; + debugfs_remove_recursive(core->dentry); + core->dentry = NULL; out: return ret; } /** * clk_debug_register - add a clk node to the debugfs clk tree - * @clk: the clk being added to the debugfs clk tree + * @core: the clk being added to the debugfs clk tree * * Dynamically adds a clk to the debugfs clk tree if debugfs has been * initialized. Otherwise it bails out early since the debugfs clk tree * will be created lazily by clk_debug_init as part of a late_initcall. */ -static int clk_debug_register(struct clk_core *clk) +static int clk_debug_register(struct clk_core *core) { int ret = 0; mutex_lock(&clk_debug_lock); - hlist_add_head(&clk->debug_node, &clk_debug_list); + hlist_add_head(&core->debug_node, &clk_debug_list); if (!inited) goto unlock; - ret = clk_debug_create_one(clk, rootdir); + ret = clk_debug_create_one(core, rootdir); unlock: mutex_unlock(&clk_debug_lock); @@ -387,18 +387,18 @@ unlock: /** * clk_debug_unregister - remove a clk node from the debugfs clk tree - * @clk: the clk being removed from the debugfs clk tree + * @core: the clk being removed from the debugfs clk tree * * Dynamically removes a clk and all it's children clk nodes from the * debugfs clk tree if clk->dentry points to debugfs created by * clk_debug_register in __clk_init. */ -static void clk_debug_unregister(struct clk_core *clk) +static void clk_debug_unregister(struct clk_core *core) { mutex_lock(&clk_debug_lock); - hlist_del_init(&clk->debug_node); - debugfs_remove_recursive(clk->dentry); - clk->dentry = NULL; + hlist_del_init(&core->debug_node); + debugfs_remove_recursive(core->dentry); + core->dentry = NULL; mutex_unlock(&clk_debug_lock); } @@ -429,7 +429,7 @@ EXPORT_SYMBOL_GPL(clk_debugfs_add_file); */ static int __init clk_debug_init(void) { - struct clk_core *clk; + struct clk_core *core; struct dentry *d; rootdir = debugfs_create_dir("clk", NULL); @@ -458,8 +458,8 @@ static int __init clk_debug_init(void) return -ENOMEM; mutex_lock(&clk_debug_lock); - hlist_for_each_entry(clk, &clk_debug_list, debug_node) - clk_debug_create_one(clk, rootdir); + hlist_for_each_entry(core, &clk_debug_list, debug_node) + clk_debug_create_one(core, rootdir); inited = 1; mutex_unlock(&clk_debug_lock); @@ -468,59 +468,59 @@ static int __init clk_debug_init(void) } late_initcall(clk_debug_init); #else -static inline int clk_debug_register(struct clk_core *clk) { return 0; } -static inline void clk_debug_reparent(struct clk_core *clk, +static inline int clk_debug_register(struct clk_core *core) { return 0; } +static inline void clk_debug_reparent(struct clk_core *core, struct clk_core *new_parent) { } -static inline void clk_debug_unregister(struct clk_core *clk) +static inline void clk_debug_unregister(struct clk_core *core) { } #endif /* caller must hold prepare_lock */ -static void clk_unprepare_unused_subtree(struct clk_core *clk) +static void clk_unprepare_unused_subtree(struct clk_core *core) { struct clk_core *child; lockdep_assert_held(&prepare_lock); - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry(child, &core->children, child_node) clk_unprepare_unused_subtree(child); - if (clk->prepare_count) + if (core->prepare_count) return; - if (clk->flags & CLK_IGNORE_UNUSED) + if (core->flags & CLK_IGNORE_UNUSED) return; - if (clk_core_is_prepared(clk)) { - trace_clk_unprepare(clk); - if (clk->ops->unprepare_unused) - clk->ops->unprepare_unused(clk->hw); - else if (clk->ops->unprepare) - clk->ops->unprepare(clk->hw); - trace_clk_unprepare_complete(clk); + if (clk_core_is_prepared(core)) { + trace_clk_unprepare(core); + if (core->ops->unprepare_unused) + core->ops->unprepare_unused(core->hw); + else if (core->ops->unprepare) + core->ops->unprepare(core->hw); + trace_clk_unprepare_complete(core); } } /* caller must hold prepare_lock */ -static void clk_disable_unused_subtree(struct clk_core *clk) +static void clk_disable_unused_subtree(struct clk_core *core) { struct clk_core *child; unsigned long flags; lockdep_assert_held(&prepare_lock); - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry(child, &core->children, child_node) clk_disable_unused_subtree(child); flags = clk_enable_lock(); - if (clk->enable_count) + if (core->enable_count) goto unlock_out; - if (clk->flags & CLK_IGNORE_UNUSED) + if (core->flags & CLK_IGNORE_UNUSED) goto unlock_out; /* @@ -528,13 +528,13 @@ static void clk_disable_unused_subtree(struct clk_core *clk) * sequence. call .disable_unused if available, otherwise fall * back to .disable */ - if (clk_core_is_enabled(clk)) { - trace_clk_disable(clk); - if (clk->ops->disable_unused) - clk->ops->disable_unused(clk->hw); - else if (clk->ops->disable) - clk->ops->disable(clk->hw); - trace_clk_disable_complete(clk); + if (clk_core_is_enabled(core)) { + trace_clk_disable(core); + if (core->ops->disable_unused) + core->ops->disable_unused(core->hw); + else if (core->ops->disable) + core->ops->disable(core->hw); + trace_clk_disable_complete(core); } unlock_out: @@ -551,7 +551,7 @@ __setup("clk_ignore_unused", clk_ignore_unused_setup); static int clk_disable_unused(void) { - struct clk_core *clk; + struct clk_core *core; if (clk_ignore_unused) { pr_warn("clk: Not disabling unused clocks\n"); @@ -560,17 +560,17 @@ static int clk_disable_unused(void) clk_prepare_lock(); - hlist_for_each_entry(clk, &clk_root_list, child_node) - clk_disable_unused_subtree(clk); + hlist_for_each_entry(core, &clk_root_list, child_node) + clk_disable_unused_subtree(core); - hlist_for_each_entry(clk, &clk_orphan_list, child_node) - clk_disable_unused_subtree(clk); + hlist_for_each_entry(core, &clk_orphan_list, child_node) + clk_disable_unused_subtree(core); - hlist_for_each_entry(clk, &clk_root_list, child_node) - clk_unprepare_unused_subtree(clk); + hlist_for_each_entry(core, &clk_root_list, child_node) + clk_unprepare_unused_subtree(core); - hlist_for_each_entry(clk, &clk_orphan_list, child_node) - clk_unprepare_unused_subtree(clk); + hlist_for_each_entry(core, &clk_orphan_list, child_node) + clk_unprepare_unused_subtree(core); clk_prepare_unlock(); @@ -608,18 +608,18 @@ struct clk *__clk_get_parent(struct clk *clk) } EXPORT_SYMBOL_GPL(__clk_get_parent); -static struct clk_core *clk_core_get_parent_by_index(struct clk_core *clk, +static struct clk_core *clk_core_get_parent_by_index(struct clk_core *core, u8 index) { - if (!clk || index >= clk->num_parents) + if (!core || index >= core->num_parents) return NULL; - else if (!clk->parents) - return clk_core_lookup(clk->parent_names[index]); - else if (!clk->parents[index]) - return clk->parents[index] = - clk_core_lookup(clk->parent_names[index]); + else if (!core->parents) + return clk_core_lookup(core->parent_names[index]); + else if (!core->parents[index]) + return core->parents[index] = + clk_core_lookup(core->parent_names[index]); else - return clk->parents[index]; + return core->parents[index]; } struct clk *clk_get_parent_by_index(struct clk *clk, u8 index) @@ -640,21 +640,21 @@ unsigned int __clk_get_enable_count(struct clk *clk) return !clk ? 0 : clk->core->enable_count; } -static unsigned long clk_core_get_rate_nolock(struct clk_core *clk) +static unsigned long clk_core_get_rate_nolock(struct clk_core *core) { unsigned long ret; - if (!clk) { + if (!core) { ret = 0; goto out; } - ret = clk->rate; + ret = core->rate; - if (clk->flags & CLK_IS_ROOT) + if (core->flags & CLK_IS_ROOT) goto out; - if (!clk->parent) + if (!core->parent) ret = 0; out: @@ -670,12 +670,12 @@ unsigned long __clk_get_rate(struct clk *clk) } EXPORT_SYMBOL_GPL(__clk_get_rate); -static unsigned long __clk_get_accuracy(struct clk_core *clk) +static unsigned long __clk_get_accuracy(struct clk_core *core) { - if (!clk) + if (!core) return 0; - return clk->accuracy; + return core->accuracy; } unsigned long __clk_get_flags(struct clk *clk) @@ -684,23 +684,23 @@ unsigned long __clk_get_flags(struct clk *clk) } EXPORT_SYMBOL_GPL(__clk_get_flags); -static bool clk_core_is_prepared(struct clk_core *clk) +static bool clk_core_is_prepared(struct clk_core *core) { int ret; - if (!clk) + if (!core) return false; /* * .is_prepared is optional for clocks that can prepare * fall back to software usage counter if it is missing */ - if (!clk->ops->is_prepared) { - ret = clk->prepare_count ? 1 : 0; + if (!core->ops->is_prepared) { + ret = core->prepare_count ? 1 : 0; goto out; } - ret = clk->ops->is_prepared(clk->hw); + ret = core->ops->is_prepared(core->hw); out: return !!ret; } @@ -713,23 +713,23 @@ bool __clk_is_prepared(struct clk *clk) return clk_core_is_prepared(clk->core); } -static bool clk_core_is_enabled(struct clk_core *clk) +static bool clk_core_is_enabled(struct clk_core *core) { int ret; - if (!clk) + if (!core) return false; /* * .is_enabled is only mandatory for clocks that gate * fall back to software usage counter if .is_enabled is missing */ - if (!clk->ops->is_enabled) { - ret = clk->enable_count ? 1 : 0; + if (!core->ops->is_enabled) { + ret = core->enable_count ? 1 : 0; goto out; } - ret = clk->ops->is_enabled(clk->hw); + ret = core->ops->is_enabled(core->hw); out: return !!ret; } @@ -744,15 +744,15 @@ bool __clk_is_enabled(struct clk *clk) EXPORT_SYMBOL_GPL(__clk_is_enabled); static struct clk_core *__clk_lookup_subtree(const char *name, - struct clk_core *clk) + struct clk_core *core) { struct clk_core *child; struct clk_core *ret; - if (!strcmp(clk->name, name)) - return clk; + if (!strcmp(core->name, name)) + return core; - hlist_for_each_entry(child, &clk->children, child_node) { + hlist_for_each_entry(child, &core->children, child_node) { ret = __clk_lookup_subtree(name, child); if (ret) return ret; @@ -853,7 +853,7 @@ struct clk *__clk_lookup(const char *name) return !core ? NULL : core->hw->clk; } -static void clk_core_get_boundaries(struct clk_core *clk, +static void clk_core_get_boundaries(struct clk_core *core, unsigned long *min_rate, unsigned long *max_rate) { @@ -862,10 +862,10 @@ static void clk_core_get_boundaries(struct clk_core *clk, *min_rate = 0; *max_rate = ULONG_MAX; - hlist_for_each_entry(clk_user, &clk->clks, clks_node) + hlist_for_each_entry(clk_user, &core->clks, clks_node) *min_rate = max(*min_rate, clk_user->min_rate); - hlist_for_each_entry(clk_user, &clk->clks, clks_node) + hlist_for_each_entry(clk_user, &core->clks, clks_node) *max_rate = min(*max_rate, clk_user->max_rate); } @@ -901,26 +901,26 @@ EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest); /*** clk api ***/ -static void clk_core_unprepare(struct clk_core *clk) +static void clk_core_unprepare(struct clk_core *core) { - if (!clk) + if (!core) return; - if (WARN_ON(clk->prepare_count == 0)) + if (WARN_ON(core->prepare_count == 0)) return; - if (--clk->prepare_count > 0) + if (--core->prepare_count > 0) return; - WARN_ON(clk->enable_count > 0); + WARN_ON(core->enable_count > 0); - trace_clk_unprepare(clk); + trace_clk_unprepare(core); - if (clk->ops->unprepare) - clk->ops->unprepare(clk->hw); + if (core->ops->unprepare) + core->ops->unprepare(core->hw); - trace_clk_unprepare_complete(clk); - clk_core_unprepare(clk->parent); + trace_clk_unprepare_complete(core); + clk_core_unprepare(core->parent); } /** @@ -945,32 +945,32 @@ void clk_unprepare(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_unprepare); -static int clk_core_prepare(struct clk_core *clk) +static int clk_core_prepare(struct clk_core *core) { int ret = 0; - if (!clk) + if (!core) return 0; - if (clk->prepare_count == 0) { - ret = clk_core_prepare(clk->parent); + if (core->prepare_count == 0) { + ret = clk_core_prepare(core->parent); if (ret) return ret; - trace_clk_prepare(clk); + trace_clk_prepare(core); - if (clk->ops->prepare) - ret = clk->ops->prepare(clk->hw); + if (core->ops->prepare) + ret = core->ops->prepare(core->hw); - trace_clk_prepare_complete(clk); + trace_clk_prepare_complete(core); if (ret) { - clk_core_unprepare(clk->parent); + clk_core_unprepare(core->parent); return ret; } } - clk->prepare_count++; + core->prepare_count++; return 0; } @@ -1002,25 +1002,25 @@ int clk_prepare(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_prepare); -static void clk_core_disable(struct clk_core *clk) +static void clk_core_disable(struct clk_core *core) { - if (!clk) + if (!core) return; - if (WARN_ON(clk->enable_count == 0)) + if (WARN_ON(core->enable_count == 0)) return; - if (--clk->enable_count > 0) + if (--core->enable_count > 0) return; - trace_clk_disable(clk); + trace_clk_disable(core); - if (clk->ops->disable) - clk->ops->disable(clk->hw); + if (core->ops->disable) + core->ops->disable(core->hw); - trace_clk_disable_complete(clk); + trace_clk_disable_complete(core); - clk_core_disable(clk->parent); + clk_core_disable(core->parent); } static void __clk_disable(struct clk *clk) @@ -1056,36 +1056,36 @@ void clk_disable(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_disable); -static int clk_core_enable(struct clk_core *clk) +static int clk_core_enable(struct clk_core *core) { int ret = 0; - if (!clk) + if (!core) return 0; - if (WARN_ON(clk->prepare_count == 0)) + if (WARN_ON(core->prepare_count == 0)) return -ESHUTDOWN; - if (clk->enable_count == 0) { - ret = clk_core_enable(clk->parent); + if (core->enable_count == 0) { + ret = clk_core_enable(core->parent); if (ret) return ret; - trace_clk_enable(clk); + trace_clk_enable(core); - if (clk->ops->enable) - ret = clk->ops->enable(clk->hw); + if (core->ops->enable) + ret = core->ops->enable(core->hw); - trace_clk_enable_complete(clk); + trace_clk_enable_complete(core); if (ret) { - clk_core_disable(clk->parent); + clk_core_disable(core->parent); return ret; } } - clk->enable_count++; + core->enable_count++; return 0; } @@ -1123,7 +1123,7 @@ int clk_enable(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_enable); -static unsigned long clk_core_round_rate_nolock(struct clk_core *clk, +static unsigned long clk_core_round_rate_nolock(struct clk_core *core, unsigned long rate, unsigned long min_rate, unsigned long max_rate) @@ -1134,25 +1134,25 @@ static unsigned long clk_core_round_rate_nolock(struct clk_core *clk, lockdep_assert_held(&prepare_lock); - if (!clk) + if (!core) return 0; - parent = clk->parent; + parent = core->parent; if (parent) parent_rate = parent->rate; - if (clk->ops->determine_rate) { + if (core->ops->determine_rate) { parent_hw = parent ? parent->hw : NULL; - return clk->ops->determine_rate(clk->hw, rate, + return core->ops->determine_rate(core->hw, rate, min_rate, max_rate, &parent_rate, &parent_hw); - } else if (clk->ops->round_rate) - return clk->ops->round_rate(clk->hw, rate, &parent_rate); - else if (clk->flags & CLK_SET_RATE_PARENT) - return clk_core_round_rate_nolock(clk->parent, rate, min_rate, + } else if (core->ops->round_rate) + return core->ops->round_rate(core->hw, rate, &parent_rate); + else if (core->flags & CLK_SET_RATE_PARENT) + return clk_core_round_rate_nolock(core->parent, rate, min_rate, max_rate); else - return clk->rate; + return core->rate; } /** @@ -1224,7 +1224,7 @@ EXPORT_SYMBOL_GPL(clk_round_rate); /** * __clk_notify - call clk notifier chain - * @clk: struct clk * that is changing rate + * @core: clk that is changing rate * @msg: clk notifier type (see include/linux/clk.h) * @old_rate: old clk rate * @new_rate: new clk rate @@ -1236,7 +1236,7 @@ EXPORT_SYMBOL_GPL(clk_round_rate); * called if all went well, or NOTIFY_STOP or NOTIFY_BAD immediately if * a driver returns that. */ -static int __clk_notify(struct clk_core *clk, unsigned long msg, +static int __clk_notify(struct clk_core *core, unsigned long msg, unsigned long old_rate, unsigned long new_rate) { struct clk_notifier *cn; @@ -1247,7 +1247,7 @@ static int __clk_notify(struct clk_core *clk, unsigned long msg, cnd.new_rate = new_rate; list_for_each_entry(cn, &clk_notifier_list, node) { - if (cn->clk->core == clk) { + if (cn->clk->core == core) { cnd.clk = cn->clk; ret = srcu_notifier_call_chain(&cn->notifier_head, msg, &cnd); @@ -1259,7 +1259,7 @@ static int __clk_notify(struct clk_core *clk, unsigned long msg, /** * __clk_recalc_accuracies - * @clk: first clk in the subtree + * @core: first clk in the subtree * * Walks the subtree of clks starting with clk and recalculates accuracies as * it goes. Note that if a clk does not implement the .recalc_accuracy @@ -1268,35 +1268,35 @@ static int __clk_notify(struct clk_core *clk, unsigned long msg, * * Caller must hold prepare_lock. */ -static void __clk_recalc_accuracies(struct clk_core *clk) +static void __clk_recalc_accuracies(struct clk_core *core) { unsigned long parent_accuracy = 0; struct clk_core *child; lockdep_assert_held(&prepare_lock); - if (clk->parent) - parent_accuracy = clk->parent->accuracy; + if (core->parent) + parent_accuracy = core->parent->accuracy; - if (clk->ops->recalc_accuracy) - clk->accuracy = clk->ops->recalc_accuracy(clk->hw, + if (core->ops->recalc_accuracy) + core->accuracy = core->ops->recalc_accuracy(core->hw, parent_accuracy); else - clk->accuracy = parent_accuracy; + core->accuracy = parent_accuracy; - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry(child, &core->children, child_node) __clk_recalc_accuracies(child); } -static long clk_core_get_accuracy(struct clk_core *clk) +static long clk_core_get_accuracy(struct clk_core *core) { unsigned long accuracy; clk_prepare_lock(); - if (clk && (clk->flags & CLK_GET_ACCURACY_NOCACHE)) - __clk_recalc_accuracies(clk); + if (core && (core->flags & CLK_GET_ACCURACY_NOCACHE)) + __clk_recalc_accuracies(core); - accuracy = __clk_get_accuracy(clk); + accuracy = __clk_get_accuracy(core); clk_prepare_unlock(); return accuracy; @@ -1320,17 +1320,17 @@ long clk_get_accuracy(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_get_accuracy); -static unsigned long clk_recalc(struct clk_core *clk, +static unsigned long clk_recalc(struct clk_core *core, unsigned long parent_rate) { - if (clk->ops->recalc_rate) - return clk->ops->recalc_rate(clk->hw, parent_rate); + if (core->ops->recalc_rate) + return core->ops->recalc_rate(core->hw, parent_rate); return parent_rate; } /** * __clk_recalc_rates - * @clk: first clk in the subtree + * @core: first clk in the subtree * @msg: notification type (see include/linux/clk.h) * * Walks the subtree of clks starting with clk and recalculates rates as it @@ -1342,7 +1342,7 @@ static unsigned long clk_recalc(struct clk_core *clk, * * Caller must hold prepare_lock. */ -static void __clk_recalc_rates(struct clk_core *clk, unsigned long msg) +static void __clk_recalc_rates(struct clk_core *core, unsigned long msg) { unsigned long old_rate; unsigned long parent_rate = 0; @@ -1350,34 +1350,34 @@ static void __clk_recalc_rates(struct clk_core *clk, unsigned long msg) lockdep_assert_held(&prepare_lock); - old_rate = clk->rate; + old_rate = core->rate; - if (clk->parent) - parent_rate = clk->parent->rate; + if (core->parent) + parent_rate = core->parent->rate; - clk->rate = clk_recalc(clk, parent_rate); + core->rate = clk_recalc(core, parent_rate); /* * ignore NOTIFY_STOP and NOTIFY_BAD return values for POST_RATE_CHANGE * & ABORT_RATE_CHANGE notifiers */ - if (clk->notifier_count && msg) - __clk_notify(clk, msg, old_rate, clk->rate); + if (core->notifier_count && msg) + __clk_notify(core, msg, old_rate, core->rate); - hlist_for_each_entry(child, &clk->children, child_node) + hlist_for_each_entry(child, &core->children, child_node) __clk_recalc_rates(child, msg); } -static unsigned long clk_core_get_rate(struct clk_core *clk) +static unsigned long clk_core_get_rate(struct clk_core *core) { unsigned long rate; clk_prepare_lock(); - if (clk && (clk->flags & CLK_GET_RATE_NOCACHE)) - __clk_recalc_rates(clk, 0); + if (core && (core->flags & CLK_GET_RATE_NOCACHE)) + __clk_recalc_rates(core, 0); - rate = clk_core_get_rate_nolock(clk); + rate = clk_core_get_rate_nolock(core); clk_prepare_unlock(); return rate; @@ -1400,15 +1400,15 @@ unsigned long clk_get_rate(struct clk *clk) } EXPORT_SYMBOL_GPL(clk_get_rate); -static int clk_fetch_parent_index(struct clk_core *clk, +static int clk_fetch_parent_index(struct clk_core *core, struct clk_core *parent) { int i; - if (!clk->parents) { - clk->parents = kcalloc(clk->num_parents, + if (!core->parents) { + core->parents = kcalloc(core->num_parents, sizeof(struct clk *), GFP_KERNEL); - if (!clk->parents) + if (!core->parents) return -ENOMEM; } @@ -1417,15 +1417,15 @@ static int clk_fetch_parent_index(struct clk_core *clk, * or if not yet cached, use string name comparison and cache * them now to avoid future calls to clk_core_lookup. */ - for (i = 0; i < clk->num_parents; i++) { - if (clk->parents[i] == parent) + for (i = 0; i < core->num_parents; i++) { + if (core->parents[i] == parent) return i; - if (clk->parents[i]) + if (core->parents[i]) continue; - if (!strcmp(clk->parent_names[i], parent->name)) { - clk->parents[i] = clk_core_lookup(parent->name); + if (!strcmp(core->parent_names[i], parent->name)) { + core->parents[i] = clk_core_lookup(parent->name); return i; } } @@ -1433,28 +1433,28 @@ static int clk_fetch_parent_index(struct clk_core *clk, return -EINVAL; } -static void clk_reparent(struct clk_core *clk, struct clk_core *new_parent) +static void clk_reparent(struct clk_core *core, struct clk_core *new_parent) { - hlist_del(&clk->child_node); + hlist_del(&core->child_node); if (new_parent) { /* avoid duplicate POST_RATE_CHANGE notifications */ - if (new_parent->new_child == clk) + if (new_parent->new_child == core) new_parent->new_child = NULL; - hlist_add_head(&clk->child_node, &new_parent->children); + hlist_add_head(&core->child_node, &new_parent->children); } else { - hlist_add_head(&clk->child_node, &clk_orphan_list); + hlist_add_head(&core->child_node, &clk_orphan_list); } - clk->parent = new_parent; + core->parent = new_parent; } -static struct clk_core *__clk_set_parent_before(struct clk_core *clk, +static struct clk_core *__clk_set_parent_before(struct clk_core *core, struct clk_core *parent) { unsigned long flags; - struct clk_core *old_parent = clk->parent; + struct clk_core *old_parent = core->parent; /* * Migrate prepare state between parents and prevent race with @@ -1473,15 +1473,15 @@ static struct clk_core *__clk_set_parent_before(struct clk_core *clk, * * See also: Comment for clk_set_parent() below. */ - if (clk->prepare_count) { + if (core->prepare_count) { clk_core_prepare(parent); clk_core_enable(parent); - clk_core_enable(clk); + clk_core_enable(core); } /* update the clk tree topology */ flags = clk_enable_lock(); - clk_reparent(clk, parent); + clk_reparent(core, parent); clk_enable_unlock(flags); return old_parent; @@ -1502,44 +1502,44 @@ static void __clk_set_parent_after(struct clk_core *core, } } -static int __clk_set_parent(struct clk_core *clk, struct clk_core *parent, +static int __clk_set_parent(struct clk_core *core, struct clk_core *parent, u8 p_index) { unsigned long flags; int ret = 0; struct clk_core *old_parent; - old_parent = __clk_set_parent_before(clk, parent); + old_parent = __clk_set_parent_before(core, parent); - trace_clk_set_parent(clk, parent); + trace_clk_set_parent(core, parent); /* change clock input source */ - if (parent && clk->ops->set_parent) - ret = clk->ops->set_parent(clk->hw, p_index); + if (parent && core->ops->set_parent) + ret = core->ops->set_parent(core->hw, p_index); - trace_clk_set_parent_complete(clk, parent); + trace_clk_set_parent_complete(core, parent); if (ret) { flags = clk_enable_lock(); - clk_reparent(clk, old_parent); + clk_reparent(core, old_parent); clk_enable_unlock(flags); - if (clk->prepare_count) { - clk_core_disable(clk); + if (core->prepare_count) { + clk_core_disable(core); clk_core_disable(parent); clk_core_unprepare(parent); } return ret; } - __clk_set_parent_after(clk, parent, old_parent); + __clk_set_parent_after(core, parent, old_parent); return 0; } /** * __clk_speculate_rates - * @clk: first clk in the subtree + * @core: first clk in the subtree * @parent_rate: the "future" rate of clk's parent * * Walks the subtree of clks starting with clk, speculating rates as it @@ -1553,7 +1553,7 @@ static int __clk_set_parent(struct clk_core *clk, struct clk_core *parent, * * Caller must hold prepare_lock. */ -static int __clk_speculate_rates(struct clk_core *clk, +static int __clk_speculate_rates(struct clk_core *core, unsigned long parent_rate) { struct clk_core *child; @@ -1562,19 +1562,19 @@ static int __clk_speculate_rates(struct clk_core *clk, lockdep_assert_held(&prepare_lock); - new_rate = clk_recalc(clk, parent_rate); + new_rate = clk_recalc(core, parent_rate); /* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */ - if (clk->notifier_count) - ret = __clk_notify(clk, PRE_RATE_CHANGE, clk->rate, new_rate); + if (core->notifier_count) + ret = __clk_notify(core, PRE_RATE_CHANGE, core->rate, new_rate); if (ret & NOTIFY_STOP_MASK) { pr_debug("%s: clk notifier callback for clock %s aborted with error %d\n", - __func__, clk->name, ret); + __func__, core->name, ret); goto out; } - hlist_for_each_entry(child, &clk->children, child_node) { + hlist_for_each_entry(child, &core->children, child_node) { ret = __clk_speculate_rates(child, new_rate); if (ret & NOTIFY_STOP_MASK) break; @@ -1584,20 +1584,20 @@ out: return ret; } -static void clk_calc_subtree(struct clk_core *clk, unsigned long new_rate, +static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate, struct clk_core *new_parent, u8 p_index) { struct clk_core *child; - clk->new_rate = new_rate; - clk->new_parent = new_parent; - clk->new_parent_index = p_index; + core->new_rate = new_rate; + core->new_parent = new_parent; + core->new_parent_index = p_index; /* include clk in new parent's PRE_RATE_CHANGE notifications */ - clk->new_child = NULL; - if (new_parent && new_parent != clk->parent) - new_parent->new_child = clk; + core->new_child = NULL; + if (new_parent && new_parent != core->parent) + new_parent->new_child = core; - hlist_for_each_entry(child, &clk->children, child_node) { + hlist_for_each_entry(child, &core->children, child_node) { child->new_rate = clk_recalc(child, new_rate); clk_calc_subtree(child, child->new_rate, NULL, 0); } @@ -1607,10 +1607,10 @@ static void clk_calc_subtree(struct clk_core *clk, unsigned long new_rate, * calculate the new rates returning the topmost clock that has to be * changed. */ -static struct clk_core *clk_calc_new_rates(struct clk_core *clk, +static struct clk_core *clk_calc_new_rates(struct clk_core *core, unsigned long rate) { - struct clk_core *top = clk; + struct clk_core *top = core; struct clk_core *old_parent, *parent; struct clk_hw *parent_hw; unsigned long best_parent_rate = 0; @@ -1621,20 +1621,20 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk, long ret; /* sanity */ - if (IS_ERR_OR_NULL(clk)) + if (IS_ERR_OR_NULL(core)) return NULL; /* save parent rate, if it exists */ - parent = old_parent = clk->parent; + parent = old_parent = core->parent; if (parent) best_parent_rate = parent->rate; - clk_core_get_boundaries(clk, &min_rate, &max_rate); + clk_core_get_boundaries(core, &min_rate, &max_rate); /* find the closest rate and parent clk/rate */ - if (clk->ops->determine_rate) { + if (core->ops->determine_rate) { parent_hw = parent ? parent->hw : NULL; - ret = clk->ops->determine_rate(clk->hw, rate, + ret = core->ops->determine_rate(core->hw, rate, min_rate, max_rate, &best_parent_rate, @@ -1644,8 +1644,8 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk, new_rate = ret; parent = parent_hw ? parent_hw->core : NULL; - } else if (clk->ops->round_rate) { - ret = clk->ops->round_rate(clk->hw, rate, + } else if (core->ops->round_rate) { + ret = core->ops->round_rate(core->hw, rate, &best_parent_rate); if (ret < 0) return NULL; @@ -1653,9 +1653,9 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk, new_rate = ret; if (new_rate < min_rate || new_rate > max_rate) return NULL; - } else if (!parent || !(clk->flags & CLK_SET_RATE_PARENT)) { + } else if (!parent || !(core->flags & CLK_SET_RATE_PARENT)) { /* pass-through clock without adjustable parent */ - clk->new_rate = clk->rate; + core->new_rate = core->rate; return NULL; } else { /* pass-through clock with adjustable parent */ @@ -1666,28 +1666,28 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *clk, /* some clocks must be gated to change parent */ if (parent != old_parent && - (clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) { + (core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) { pr_debug("%s: %s not gated but wants to reparent\n", - __func__, clk->name); + __func__, core->name); return NULL; } /* try finding the new parent index */ - if (parent && clk->num_parents > 1) { - p_index = clk_fetch_parent_index(clk, parent); + if (parent && core->num_parents > 1) { + p_index = clk_fetch_parent_index(core, parent); if (p_index < 0) { pr_debug("%s: clk %s can not be parent of clk %s\n", - __func__, parent->name, clk->name); + __func__, parent->name, core->name); return NULL; } } - if ((clk->flags & CLK_SET_RATE_PARENT) && parent && + if ((core->flags & CLK_SET_RATE_PARENT) && parent && best_parent_rate != parent->rate) top = clk_calc_new_rates(parent, best_parent_rate); out: - clk_calc_subtree(clk, new_rate, parent, p_index); + clk_calc_subtree(core, new_rate, parent, p_index); return top; } @@ -1697,33 +1697,33 @@ out: * so that in case of an error we can walk down the whole tree again and * abort the change. */ -static struct clk_core *clk_propagate_rate_change(struct clk_core *clk, +static struct clk_core *clk_propagate_rate_change(struct clk_core *core, unsigned long event) { struct clk_core *child, *tmp_clk, *fail_clk = NULL; int ret = NOTIFY_DONE; - if (clk->rate == clk->new_rate) + if (core->rate == core->new_rate) return NULL; - if (clk->notifier_count) { - ret = __clk_notify(clk, event, clk->rate, clk->new_rate); + if (core->notifier_count) { + ret = __clk_notify(core, event, core->rate, core->new_rate); if (ret & NOTIFY_STOP_MASK) - fail_clk = clk; + fail_clk = core; } - hlist_for_each_entry(child, &clk->children, child_node) { + hlist_for_each_entry(child, &core->children, child_node) { /* Skip children who will be reparented to another clock */ - if (child->new_parent && child->new_parent != clk) + if (child->new_parent && child->new_parent != core) continue; tmp_clk = clk_propagate_rate_change(child, event); if (tmp_clk) fail_clk = tmp_clk; } - /* handle the new child who might not be in clk->children yet */ - if (clk->new_child) { - tmp_clk = clk_propagate_rate_change(clk->new_child, event); + /* handle the new child who might not be in core->children yet */ + if (core->new_child) { + tmp_clk = clk_propagate_rate_change(core->new_child, event); if (tmp_clk) fail_clk = tmp_clk; } @@ -1735,7 +1735,7 @@ static struct clk_core *clk_propagate_rate_change(struct clk_core *clk, * walk down a subtree and set the new rates notifying the rate * change on the way */ -static void clk_change_rate(struct clk_core *clk) +static void clk_change_rate(struct clk_core *core) { struct clk_core *child; struct hlist_node *tmp; @@ -1744,77 +1744,77 @@ static void clk_change_rate(struct clk_core *clk) bool skip_set_rate = false; struct clk_core *old_parent; - old_rate = clk->rate; + old_rate = core->rate; - if (clk->new_parent) - best_parent_rate = clk->new_parent->rate; - else if (clk->parent) - best_parent_rate = clk->parent->rate; + if (core->new_parent) + best_parent_rate = core->new_parent->rate; + else if (core->parent) + best_parent_rate = core->parent->rate; - if (clk->new_parent && clk->new_parent != clk->parent) { - old_parent = __clk_set_parent_before(clk, clk->new_parent); - trace_clk_set_parent(clk, clk->new_parent); + if (core->new_parent && core->new_parent != core->parent) { + old_parent = __clk_set_parent_before(core, core->new_parent); + trace_clk_set_parent(core, core->new_parent); - if (clk->ops->set_rate_and_parent) { + if (core->ops->set_rate_and_parent) { skip_set_rate = true; - clk->ops->set_rate_and_parent(clk->hw, clk->new_rate, + core->ops->set_rate_and_parent(core->hw, core->new_rate, best_parent_rate, - clk->new_parent_index); - } else if (clk->ops->set_parent) { - clk->ops->set_parent(clk->hw, clk->new_parent_index); + core->new_parent_index); + } else if (core->ops->set_parent) { + core->ops->set_parent(core->hw, core->new_parent_index); } - trace_clk_set_parent_complete(clk, clk->new_parent); - __clk_set_parent_after(clk, clk->new_parent, old_parent); + trace_clk_set_parent_complete(core, core->new_parent); + __clk_set_parent_after(core, core->new_parent, old_parent); } - trace_clk_set_rate(clk, clk->new_rate); + trace_clk_set_rate(core, core->new_rate); - if (!skip_set_rate && clk->ops->set_rate) - clk->ops->set_rate(clk->hw, clk->new_rate, best_parent_rate); + if (!skip_set_rate && core->ops->set_rate) + core->ops->set_rate(core->hw, core->new_rate, best_parent_rate); - trace_clk_set_rate_complete(clk, clk->new_rate); + trace_clk_set_rate_complete(core, core->new_rate); - clk->rate = clk_recalc(clk, best_parent_rate); + core->rate = clk_recalc(core, best_parent_rate); - if (clk->notifier_count && old_rate != clk->rate) - __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); + if (core->notifier_count && old_rate != core->rate) + __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate); /* * Use safe iteration, as change_rate can actually swap parents * for certain clock types. */ - hlist_for_each_entry_safe(child, tmp, &clk->children, child_node) { + hlist_for_each_entry_safe(child, tmp, &core->children, child_node) { /* Skip children who will be reparented to another clock */ - if (child->new_parent && child->new_parent != clk) + if (child->new_parent && child->new_parent != core) continue; clk_change_rate(child); } - /* handle the new child who might not be in clk->children yet */ - if (clk->new_child) - clk_change_rate(clk->new_child); + /* handle the new child who might not be in core->children yet */ + if (core->new_child) + clk_change_rate(core->new_child); } -static int clk_core_set_rate_nolock(struct clk_core *clk, +static int clk_core_set_rate_nolock(struct clk_core *core, unsigned long req_rate) { struct clk_core *top, *fail_clk; unsigned long rate = req_rate; int ret = 0; - if (!clk) + if (!core) return 0; /* bail early if nothing to do */ - if (rate == clk_core_get_rate_nolock(clk)) + if (rate == clk_core_get_rate_nolock(core)) return 0; - if ((clk->flags & CLK_SET_RATE_GATE) && clk->prepare_count) + if ((core->flags & CLK_SET_RATE_GATE) && core->prepare_count) return -EBUSY; /* calculate new rates and get the topmost changed clock */ - top = clk_calc_new_rates(clk, rate); + top = clk_calc_new_rates(core, rate); if (!top) return -EINVAL; @@ -1830,7 +1830,7 @@ static int clk_core_set_rate_nolock(struct clk_core *clk, /* change the rates */ clk_change_rate(top); - clk->req_rate = req_rate; + core->req_rate = req_rate; return ret; } @@ -1969,55 +1969,55 @@ EXPORT_SYMBOL_GPL(clk_get_parent); * .parents array exists, and if so use it to avoid an expensive tree * traversal. If .parents does not exist then walk the tree. */ -static struct clk_core *__clk_init_parent(struct clk_core *clk) +static struct clk_core *__clk_init_parent(struct clk_core *core) { struct clk_core *ret = NULL; u8 index; /* handle the trivial cases */ - if (!clk->num_parents) + if (!core->num_parents) goto out; - if (clk->num_parents == 1) { - if (IS_ERR_OR_NULL(clk->parent)) - clk->parent = clk_core_lookup(clk->parent_names[0]); - ret = clk->parent; + if (core->num_parents == 1) { + if (IS_ERR_OR_NULL(core->parent)) + core->parent = clk_core_lookup(core->parent_names[0]); + ret = core->parent; goto out; } - if (!clk->ops->get_parent) { - WARN(!clk->ops->get_parent, + if (!core->ops->get_parent) { + WARN(!core->ops->get_parent, "%s: multi-parent clocks must implement .get_parent\n", __func__); goto out; }; /* - * Do our best to cache parent clocks in clk->parents. This prevents - * unnecessary and expensive lookups. We don't set clk->parent here; + * Do our best to cache parent clocks in core->parents. This prevents + * unnecessary and expensive lookups. We don't set core->parent here; * that is done by the calling function. */ - index = clk->ops->get_parent(clk->hw); + index = core->ops->get_parent(core->hw); - if (!clk->parents) - clk->parents = - kcalloc(clk->num_parents, sizeof(struct clk *), + if (!core->parents) + core->parents = + kcalloc(core->num_parents, sizeof(struct clk *), GFP_KERNEL); - ret = clk_core_get_parent_by_index(clk, index); + ret = clk_core_get_parent_by_index(core, index); out: return ret; } -static void clk_core_reparent(struct clk_core *clk, +static void clk_core_reparent(struct clk_core *core, struct clk_core *new_parent) { - clk_reparent(clk, new_parent); - __clk_recalc_accuracies(clk); - __clk_recalc_rates(clk, POST_RATE_CHANGE); + clk_reparent(core, new_parent); + __clk_recalc_accuracies(core); + __clk_recalc_rates(core, POST_RATE_CHANGE); } /** @@ -2054,61 +2054,61 @@ bool clk_has_parent(struct clk *clk, struct clk *parent) } EXPORT_SYMBOL_GPL(clk_has_parent); -static int clk_core_set_parent(struct clk_core *clk, struct clk_core *parent) +static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent) { int ret = 0; int p_index = 0; unsigned long p_rate = 0; - if (!clk) + if (!core) return 0; /* prevent racing with updates to the clock topology */ clk_prepare_lock(); - if (clk->parent == parent) + if (core->parent == parent) goto out; /* verify ops for for multi-parent clks */ - if ((clk->num_parents > 1) && (!clk->ops->set_parent)) { + if ((core->num_parents > 1) && (!core->ops->set_parent)) { ret = -ENOSYS; goto out; } /* check that we are allowed to re-parent if the clock is in use */ - if ((clk->flags & CLK_SET_PARENT_GATE) && clk->prepare_count) { + if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) { ret = -EBUSY; goto out; } /* try finding the new parent index */ if (parent) { - p_index = clk_fetch_parent_index(clk, parent); + p_index = clk_fetch_parent_index(core, parent); p_rate = parent->rate; if (p_index < 0) { pr_debug("%s: clk %s can not be parent of clk %s\n", - __func__, parent->name, clk->name); + __func__, parent->name, core->name); ret = p_index; goto out; } } /* propagate PRE_RATE_CHANGE notifications */ - ret = __clk_speculate_rates(clk, p_rate); + ret = __clk_speculate_rates(core, p_rate); /* abort if a driver objects */ if (ret & NOTIFY_STOP_MASK) goto out; /* do the re-parent */ - ret = __clk_set_parent(clk, parent, p_index); + ret = __clk_set_parent(core, parent, p_index); /* propagate rate an accuracy recalculation accordingly */ if (ret) { - __clk_recalc_rates(clk, ABORT_RATE_CHANGE); + __clk_recalc_rates(core, ABORT_RATE_CHANGE); } else { - __clk_recalc_rates(clk, POST_RATE_CHANGE); - __clk_recalc_accuracies(clk); + __clk_recalc_rates(core, POST_RATE_CHANGE); + __clk_recalc_accuracies(core); } out: @@ -2193,15 +2193,15 @@ int clk_set_phase(struct clk *clk, int degrees) } EXPORT_SYMBOL_GPL(clk_set_phase); -static int clk_core_get_phase(struct clk_core *clk) +static int clk_core_get_phase(struct clk_core *core) { int ret = 0; - if (!clk) + if (!core) goto out; clk_prepare_lock(); - ret = clk->phase; + ret = core->phase; clk_prepare_unlock(); out: @@ -2263,67 +2263,67 @@ static int __clk_init(struct device *dev, struct clk *clk_user) int i, ret = 0; struct clk_core *orphan; struct hlist_node *tmp2; - struct clk_core *clk; + struct clk_core *core; unsigned long rate; if (!clk_user) return -EINVAL; - clk = clk_user->core; + core = clk_user->core; clk_prepare_lock(); /* check to see if a clock with this name is already registered */ - if (clk_core_lookup(clk->name)) { + if (clk_core_lookup(core->name)) { pr_debug("%s: clk %s already initialized\n", - __func__, clk->name); + __func__, core->name); ret = -EEXIST; goto out; } /* check that clk_ops are sane. See Documentation/clk.txt */ - if (clk->ops->set_rate && - !((clk->ops->round_rate || clk->ops->determine_rate) && - clk->ops->recalc_rate)) { + if (core->ops->set_rate && + !((core->ops->round_rate || core->ops->determine_rate) && + core->ops->recalc_rate)) { pr_warning("%s: %s must implement .round_rate or .determine_rate in addition to .recalc_rate\n", - __func__, clk->name); + __func__, core->name); ret = -EINVAL; goto out; } - if (clk->ops->set_parent && !clk->ops->get_parent) { + if (core->ops->set_parent && !core->ops->get_parent) { pr_warning("%s: %s must implement .get_parent & .set_parent\n", - __func__, clk->name); + __func__, core->name); ret = -EINVAL; goto out; } - if (clk->ops->set_rate_and_parent && - !(clk->ops->set_parent && clk->ops->set_rate)) { + if (core->ops->set_rate_and_parent && + !(core->ops->set_parent && core->ops->set_rate)) { pr_warn("%s: %s must implement .set_parent & .set_rate\n", - __func__, clk->name); + __func__, core->name); ret = -EINVAL; goto out; } /* throw a WARN if any entries in parent_names are NULL */ - for (i = 0; i < clk->num_parents; i++) - WARN(!clk->parent_names[i], + for (i = 0; i < core->num_parents; i++) + WARN(!core->parent_names[i], "%s: invalid NULL in %s's .parent_names\n", - __func__, clk->name); + __func__, core->name); /* * Allocate an array of struct clk *'s to avoid unnecessary string * look-ups of clk's possible parents. This can fail for clocks passed - * in to clk_init during early boot; thus any access to clk->parents[] + * in to clk_init during early boot; thus any access to core->parents[] * must always check for a NULL pointer and try to populate it if * necessary. * - * If clk->parents is not NULL we skip this entire block. This allows - * for clock drivers to statically initialize clk->parents. + * If core->parents is not NULL we skip this entire block. This allows + * for clock drivers to statically initialize core->parents. */ - if (clk->num_parents > 1 && !clk->parents) { - clk->parents = kcalloc(clk->num_parents, sizeof(struct clk *), + if (core->num_parents > 1 && !core->parents) { + core->parents = kcalloc(core->num_parents, sizeof(struct clk *), GFP_KERNEL); /* * clk_core_lookup returns NULL for parents that have not been @@ -2331,16 +2331,16 @@ static int __clk_init(struct device *dev, struct clk *clk_user) * for a NULL pointer. We can always perform lazy lookups for * missing parents later on. */ - if (clk->parents) - for (i = 0; i < clk->num_parents; i++) - clk->parents[i] = - clk_core_lookup(clk->parent_names[i]); + if (core->parents) + for (i = 0; i < core->num_parents; i++) + core->parents[i] = + clk_core_lookup(core->parent_names[i]); } - clk->parent = __clk_init_parent(clk); + core->parent = __clk_init_parent(core); /* - * Populate clk->parent if parent has already been __clk_init'd. If + * Populate core->parent if parent has already been __clk_init'd. If * parent has not yet been __clk_init'd then place clk in the orphan * list. If clk has set the CLK_IS_ROOT flag then place it in the root * clk list. @@ -2349,13 +2349,13 @@ static int __clk_init(struct device *dev, struct clk *clk_user) * clocks and re-parent any that are children of the clock currently * being clk_init'd. */ - if (clk->parent) - hlist_add_head(&clk->child_node, - &clk->parent->children); - else if (clk->flags & CLK_IS_ROOT) - hlist_add_head(&clk->child_node, &clk_root_list); + if (core->parent) + hlist_add_head(&core->child_node, + &core->parent->children); + else if (core->flags & CLK_IS_ROOT) + hlist_add_head(&core->child_node, &clk_root_list); else - hlist_add_head(&clk->child_node, &clk_orphan_list); + hlist_add_head(&core->child_node, &clk_orphan_list); /* * Set clk's accuracy. The preferred method is to use @@ -2364,23 +2364,23 @@ static int __clk_init(struct device *dev, struct clk *clk_user) * parent (or is orphaned) then accuracy is set to zero (perfect * clock). */ - if (clk->ops->recalc_accuracy) - clk->accuracy = clk->ops->recalc_accuracy(clk->hw, - __clk_get_accuracy(clk->parent)); - else if (clk->parent) - clk->accuracy = clk->parent->accuracy; + if (core->ops->recalc_accuracy) + core->accuracy = core->ops->recalc_accuracy(core->hw, + __clk_get_accuracy(core->parent)); + else if (core->parent) + core->accuracy = core->parent->accuracy; else - clk->accuracy = 0; + core->accuracy = 0; /* * Set clk's phase. * Since a phase is by definition relative to its parent, just * query the current clock phase, or just assume it's in phase. */ - if (clk->ops->get_phase) - clk->phase = clk->ops->get_phase(clk->hw); + if (core->ops->get_phase) + core->phase = core->ops->get_phase(core->hw); else - clk->phase = 0; + core->phase = 0; /* * Set clk's rate. The preferred method is to use .recalc_rate. For @@ -2388,14 +2388,14 @@ static int __clk_init(struct device *dev, struct clk *clk_user) * parent's rate. If a clock doesn't have a parent (or is orphaned) * then rate is set to zero. */ - if (clk->ops->recalc_rate) - rate = clk->ops->recalc_rate(clk->hw, - clk_core_get_rate_nolock(clk->parent)); - else if (clk->parent) - rate = clk->parent->rate; + if (core->ops->recalc_rate) + rate = core->ops->recalc_rate(core->hw, + clk_core_get_rate_nolock(core->parent)); + else if (core->parent) + rate = core->parent->rate; else rate = 0; - clk->rate = clk->req_rate = rate; + core->rate = core->req_rate = rate; /* * walk the list of orphan clocks and reparent any that are children of @@ -2404,14 +2404,14 @@ static int __clk_init(struct device *dev, struct clk *clk_user) hlist_for_each_entry_safe(orphan, tmp2, &clk_orphan_list, child_node) { if (orphan->num_parents && orphan->ops->get_parent) { i = orphan->ops->get_parent(orphan->hw); - if (!strcmp(clk->name, orphan->parent_names[i])) - clk_core_reparent(orphan, clk); + if (!strcmp(core->name, orphan->parent_names[i])) + clk_core_reparent(orphan, core); continue; } for (i = 0; i < orphan->num_parents; i++) - if (!strcmp(clk->name, orphan->parent_names[i])) { - clk_core_reparent(orphan, clk); + if (!strcmp(core->name, orphan->parent_names[i])) { + clk_core_reparent(orphan, core); break; } } @@ -2424,15 +2424,15 @@ static int __clk_init(struct device *dev, struct clk *clk_user) * Please consider other ways of solving initialization problems before * using this callback, as its use is discouraged. */ - if (clk->ops->init) - clk->ops->init(clk->hw); + if (core->ops->init) + core->ops->init(core->hw); - kref_init(&clk->ref); + kref_init(&core->ref); out: clk_prepare_unlock(); if (!ret) - clk_debug_register(clk); + clk_debug_register(core); return ret; } @@ -2485,34 +2485,34 @@ void __clk_free_clk(struct clk *clk) struct clk *clk_register(struct device *dev, struct clk_hw *hw) { int i, ret; - struct clk_core *clk; + struct clk_core *core; - clk = kzalloc(sizeof(*clk), GFP_KERNEL); - if (!clk) { + core = kzalloc(sizeof(*core), GFP_KERNEL); + if (!core) { pr_err("%s: could not allocate clk\n", __func__); ret = -ENOMEM; goto fail_out; } - clk->name = kstrdup_const(hw->init->name, GFP_KERNEL); - if (!clk->name) { + core->name = kstrdup_const(hw->init->name, GFP_KERNEL); + if (!core->name) { pr_err("%s: could not allocate clk->name\n", __func__); ret = -ENOMEM; goto fail_name; } - clk->ops = hw->init->ops; + core->ops = hw->init->ops; if (dev && dev->driver) - clk->owner = dev->driver->owner; - clk->hw = hw; - clk->flags = hw->init->flags; - clk->num_parents = hw->init->num_parents; - hw->core = clk; + core->owner = dev->driver->owner; + core->hw = hw; + core->flags = hw->init->flags; + core->num_parents = hw->init->num_parents; + hw->core = core; /* allocate local copy in case parent_names is __initdata */ - clk->parent_names = kcalloc(clk->num_parents, sizeof(char *), + core->parent_names = kcalloc(core->num_parents, sizeof(char *), GFP_KERNEL); - if (!clk->parent_names) { + if (!core->parent_names) { pr_err("%s: could not allocate clk->parent_names\n", __func__); ret = -ENOMEM; goto fail_parent_names; @@ -2520,17 +2520,17 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) /* copy each string name in case parent_names is __initdata */ - for (i = 0; i < clk->num_parents; i++) { - clk->parent_names[i] = kstrdup_const(hw->init->parent_names[i], + for (i = 0; i < core->num_parents; i++) { + core->parent_names[i] = kstrdup_const(hw->init->parent_names[i], GFP_KERNEL); - if (!clk->parent_names[i]) { + if (!core->parent_names[i]) { pr_err("%s: could not copy parent_names\n", __func__); ret = -ENOMEM; goto fail_parent_names_copy; } } - INIT_HLIST_HEAD(&clk->clks); + INIT_HLIST_HEAD(&core->clks); hw->clk = __clk_create_clk(hw, NULL, NULL); if (IS_ERR(hw->clk)) { @@ -2548,12 +2548,12 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) fail_parent_names_copy: while (--i >= 0) - kfree_const(clk->parent_names[i]); - kfree(clk->parent_names); + kfree_const(core->parent_names[i]); + kfree(core->parent_names); fail_parent_names: - kfree_const(clk->name); + kfree_const(core->name); fail_name: - kfree(clk); + kfree(core); fail_out: return ERR_PTR(ret); } @@ -2565,18 +2565,18 @@ EXPORT_SYMBOL_GPL(clk_register); */ static void __clk_release(struct kref *ref) { - struct clk_core *clk = container_of(ref, struct clk_core, ref); - int i = clk->num_parents; + struct clk_core *core = container_of(ref, struct clk_core, ref); + int i = core->num_parents; lockdep_assert_held(&prepare_lock); - kfree(clk->parents); + kfree(core->parents); while (--i >= 0) - kfree_const(clk->parent_names[i]); + kfree_const(core->parent_names[i]); - kfree(clk->parent_names); - kfree_const(clk->name); - kfree(clk); + kfree(core->parent_names); + kfree_const(core->name); + kfree(core); } /* -- cgit v1.2.3-58-ga151 From 864e160ae510f5223e7bd9aa4335d7e092173446 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Thu, 30 Apr 2015 14:02:19 -0700 Subject: clk: Squash __clk_{enable,disable}() into callers These functions are only used in one place. Let's squash them into their respective callers to save some lines. Signed-off-by: Dong Aisheng [sboyd@codeaurora.org: Redo commit text, add NULL check in clk_enable()] Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 0b3c914db1ca..88439af5d032 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1023,14 +1023,6 @@ static void clk_core_disable(struct clk_core *core) clk_core_disable(core->parent); } -static void __clk_disable(struct clk *clk) -{ - if (!clk) - return; - - clk_core_disable(clk->core); -} - /** * clk_disable - gate a clock * @clk: the clk being gated @@ -1051,7 +1043,7 @@ void clk_disable(struct clk *clk) return; flags = clk_enable_lock(); - __clk_disable(clk); + clk_core_disable(clk->core); clk_enable_unlock(flags); } EXPORT_SYMBOL_GPL(clk_disable); @@ -1089,14 +1081,6 @@ static int clk_core_enable(struct clk_core *core) return 0; } -static int __clk_enable(struct clk *clk) -{ - if (!clk) - return 0; - - return clk_core_enable(clk->core); -} - /** * clk_enable - ungate a clock * @clk: the clk being ungated @@ -1115,8 +1099,11 @@ int clk_enable(struct clk *clk) unsigned long flags; int ret; + if (!clk) + return 0; + flags = clk_enable_lock(); - ret = __clk_enable(clk); + ret = clk_core_enable(clk->core); clk_enable_unlock(flags); return ret; -- cgit v1.2.3-58-ga151 From d7d131512b474c902b83a314d69a012e70c55431 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 30 Apr 2015 14:04:53 -0700 Subject: clk: Drop unnecessary OOM prints We don't need to print error messages when allocations fail. We'll get a nice backtrace in such situations anyway. Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 88439af5d032..8d36100c00ce 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2476,14 +2476,12 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) core = kzalloc(sizeof(*core), GFP_KERNEL); if (!core) { - pr_err("%s: could not allocate clk\n", __func__); ret = -ENOMEM; goto fail_out; } core->name = kstrdup_const(hw->init->name, GFP_KERNEL); if (!core->name) { - pr_err("%s: could not allocate clk->name\n", __func__); ret = -ENOMEM; goto fail_name; } @@ -2500,7 +2498,6 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) GFP_KERNEL); if (!core->parent_names) { - pr_err("%s: could not allocate clk->parent_names\n", __func__); ret = -ENOMEM; goto fail_parent_names; } @@ -2511,7 +2508,6 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) core->parent_names[i] = kstrdup_const(hw->init->parent_names[i], GFP_KERNEL); if (!core->parent_names[i]) { - pr_err("%s: could not copy parent_names\n", __func__); ret = -ENOMEM; goto fail_parent_names_copy; } @@ -2521,7 +2517,6 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw) hw->clk = __clk_create_clk(hw, NULL, NULL); if (IS_ERR(hw->clk)) { - pr_err("%s: could not allocate per-user clk\n", __func__); ret = PTR_ERR(hw->clk); goto fail_parent_names_copy; } -- cgit v1.2.3-58-ga151 From 1f3e1983429d31ceada9a09197d79445c92a2901 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 30 Apr 2015 14:21:56 -0700 Subject: clk: Remove impossible if condition in clk_core_get_phase() This condition can't ever be true because this function is static and it's always called with a non-NULL pointer. Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 8d36100c00ce..3e58b7453076 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2182,16 +2182,12 @@ EXPORT_SYMBOL_GPL(clk_set_phase); static int clk_core_get_phase(struct clk_core *core) { - int ret = 0; - - if (!core) - goto out; + int ret; clk_prepare_lock(); ret = core->phase; clk_prepare_unlock(); -out: return ret; } EXPORT_SYMBOL_GPL(clk_get_phase); -- cgit v1.2.3-58-ga151 From 4dff95dc9477a34de77d24c59dcf1dc593687fcf Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 30 Apr 2015 14:43:22 -0700 Subject: clk: Remove forward declared function prototypes Move the code around so that we don't need to declare function prototypes at the start of the file. Simplify clk_core_is_prepared() and clk_core_is_enabled() too to make the diff easier to read. Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 3117 ++++++++++++++++++++++++++--------------------------- 1 file changed, 1546 insertions(+), 1571 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 3e58b7453076..0001b91f2b6e 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -37,13 +37,6 @@ static HLIST_HEAD(clk_root_list); static HLIST_HEAD(clk_orphan_list); static LIST_HEAD(clk_notifier_list); -static long clk_core_get_accuracy(struct clk_core *core); -static unsigned long clk_core_get_rate(struct clk_core *core); -static int clk_core_get_phase(struct clk_core *core); -static bool clk_core_is_prepared(struct clk_core *core); -static bool clk_core_is_enabled(struct clk_core *core); -static struct clk_core *clk_core_lookup(const char *name); - /*** private data structures ***/ struct clk_core { @@ -145,2093 +138,2075 @@ static void clk_enable_unlock(unsigned long flags) spin_unlock_irqrestore(&enable_lock, flags); } -/*** debugfs support ***/ - -#ifdef CONFIG_DEBUG_FS -#include +static bool clk_core_is_prepared(struct clk_core *core) +{ + /* + * .is_prepared is optional for clocks that can prepare + * fall back to software usage counter if it is missing + */ + if (!core->ops->is_prepared) + return core->prepare_count; -static struct dentry *rootdir; -static int inited = 0; -static DEFINE_MUTEX(clk_debug_lock); -static HLIST_HEAD(clk_debug_list); + return core->ops->is_prepared(core->hw); +} -static struct hlist_head *all_lists[] = { - &clk_root_list, - &clk_orphan_list, - NULL, -}; +static bool clk_core_is_enabled(struct clk_core *core) +{ + /* + * .is_enabled is only mandatory for clocks that gate + * fall back to software usage counter if .is_enabled is missing + */ + if (!core->ops->is_enabled) + return core->enable_count; -static struct hlist_head *orphan_list[] = { - &clk_orphan_list, - NULL, -}; + return core->ops->is_enabled(core->hw); +} -static void clk_summary_show_one(struct seq_file *s, struct clk_core *c, - int level) +/* caller must hold prepare_lock */ +static void clk_unprepare_unused_subtree(struct clk_core *core) { - if (!c) + struct clk_core *child; + + lockdep_assert_held(&prepare_lock); + + hlist_for_each_entry(child, &core->children, child_node) + clk_unprepare_unused_subtree(child); + + if (core->prepare_count) return; - seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu %-3d\n", - level * 3 + 1, "", - 30 - level * 3, c->name, - c->enable_count, c->prepare_count, clk_core_get_rate(c), - clk_core_get_accuracy(c), clk_core_get_phase(c)); + if (core->flags & CLK_IGNORE_UNUSED) + return; + + if (clk_core_is_prepared(core)) { + trace_clk_unprepare(core); + if (core->ops->unprepare_unused) + core->ops->unprepare_unused(core->hw); + else if (core->ops->unprepare) + core->ops->unprepare(core->hw); + trace_clk_unprepare_complete(core); + } } -static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c, - int level) +/* caller must hold prepare_lock */ +static void clk_disable_unused_subtree(struct clk_core *core) { struct clk_core *child; + unsigned long flags; - if (!c) - return; + lockdep_assert_held(&prepare_lock); - clk_summary_show_one(s, c, level); + hlist_for_each_entry(child, &core->children, child_node) + clk_disable_unused_subtree(child); - hlist_for_each_entry(child, &c->children, child_node) - clk_summary_show_subtree(s, child, level + 1); + flags = clk_enable_lock(); + + if (core->enable_count) + goto unlock_out; + + if (core->flags & CLK_IGNORE_UNUSED) + goto unlock_out; + + /* + * some gate clocks have special needs during the disable-unused + * sequence. call .disable_unused if available, otherwise fall + * back to .disable + */ + if (clk_core_is_enabled(core)) { + trace_clk_disable(core); + if (core->ops->disable_unused) + core->ops->disable_unused(core->hw); + else if (core->ops->disable) + core->ops->disable(core->hw); + trace_clk_disable_complete(core); + } + +unlock_out: + clk_enable_unlock(flags); } -static int clk_summary_show(struct seq_file *s, void *data) +static bool clk_ignore_unused; +static int __init clk_ignore_unused_setup(char *__unused) { - struct clk_core *c; - struct hlist_head **lists = (struct hlist_head **)s->private; + clk_ignore_unused = true; + return 1; +} +__setup("clk_ignore_unused", clk_ignore_unused_setup); - seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n"); - seq_puts(s, "----------------------------------------------------------------------------------------\n"); +static int clk_disable_unused(void) +{ + struct clk_core *core; + + if (clk_ignore_unused) { + pr_warn("clk: Not disabling unused clocks\n"); + return 0; + } clk_prepare_lock(); - for (; *lists; lists++) - hlist_for_each_entry(c, *lists, child_node) - clk_summary_show_subtree(s, c, 0); + hlist_for_each_entry(core, &clk_root_list, child_node) + clk_disable_unused_subtree(core); + + hlist_for_each_entry(core, &clk_orphan_list, child_node) + clk_disable_unused_subtree(core); + + hlist_for_each_entry(core, &clk_root_list, child_node) + clk_unprepare_unused_subtree(core); + + hlist_for_each_entry(core, &clk_orphan_list, child_node) + clk_unprepare_unused_subtree(core); clk_prepare_unlock(); return 0; } +late_initcall_sync(clk_disable_unused); +/*** helper functions ***/ -static int clk_summary_open(struct inode *inode, struct file *file) +const char *__clk_get_name(struct clk *clk) { - return single_open(file, clk_summary_show, inode->i_private); + return !clk ? NULL : clk->core->name; } +EXPORT_SYMBOL_GPL(__clk_get_name); -static const struct file_operations clk_summary_fops = { - .open = clk_summary_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +struct clk_hw *__clk_get_hw(struct clk *clk) +{ + return !clk ? NULL : clk->core->hw; +} +EXPORT_SYMBOL_GPL(__clk_get_hw); -static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level) +u8 __clk_get_num_parents(struct clk *clk) { - if (!c) - return; + return !clk ? 0 : clk->core->num_parents; +} +EXPORT_SYMBOL_GPL(__clk_get_num_parents); - seq_printf(s, "\"%s\": { ", c->name); - seq_printf(s, "\"enable_count\": %d,", c->enable_count); - seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); - seq_printf(s, "\"rate\": %lu", clk_core_get_rate(c)); - seq_printf(s, "\"accuracy\": %lu", clk_core_get_accuracy(c)); - seq_printf(s, "\"phase\": %d", clk_core_get_phase(c)); +struct clk *__clk_get_parent(struct clk *clk) +{ + if (!clk) + return NULL; + + /* TODO: Create a per-user clk and change callers to call clk_put */ + return !clk->core->parent ? NULL : clk->core->parent->hw->clk; } +EXPORT_SYMBOL_GPL(__clk_get_parent); -static void clk_dump_subtree(struct seq_file *s, struct clk_core *c, int level) +static struct clk_core *__clk_lookup_subtree(const char *name, + struct clk_core *core) { struct clk_core *child; + struct clk_core *ret; - if (!c) - return; - - clk_dump_one(s, c, level); + if (!strcmp(core->name, name)) + return core; - hlist_for_each_entry(child, &c->children, child_node) { - seq_printf(s, ","); - clk_dump_subtree(s, child, level + 1); + hlist_for_each_entry(child, &core->children, child_node) { + ret = __clk_lookup_subtree(name, child); + if (ret) + return ret; } - seq_printf(s, "}"); + return NULL; } -static int clk_dump(struct seq_file *s, void *data) +static struct clk_core *clk_core_lookup(const char *name) { - struct clk_core *c; - bool first_node = true; - struct hlist_head **lists = (struct hlist_head **)s->private; - - seq_printf(s, "{"); + struct clk_core *root_clk; + struct clk_core *ret; - clk_prepare_lock(); + if (!name) + return NULL; - for (; *lists; lists++) { - hlist_for_each_entry(c, *lists, child_node) { - if (!first_node) - seq_puts(s, ","); - first_node = false; - clk_dump_subtree(s, c, 0); - } + /* search the 'proper' clk tree first */ + hlist_for_each_entry(root_clk, &clk_root_list, child_node) { + ret = __clk_lookup_subtree(name, root_clk); + if (ret) + return ret; } - clk_prepare_unlock(); + /* if not found, then search the orphan tree */ + hlist_for_each_entry(root_clk, &clk_orphan_list, child_node) { + ret = __clk_lookup_subtree(name, root_clk); + if (ret) + return ret; + } - seq_printf(s, "}"); - return 0; + return NULL; } - -static int clk_dump_open(struct inode *inode, struct file *file) +static struct clk_core *clk_core_get_parent_by_index(struct clk_core *core, + u8 index) { - return single_open(file, clk_dump, inode->i_private); + if (!core || index >= core->num_parents) + return NULL; + else if (!core->parents) + return clk_core_lookup(core->parent_names[index]); + else if (!core->parents[index]) + return core->parents[index] = + clk_core_lookup(core->parent_names[index]); + else + return core->parents[index]; } -static const struct file_operations clk_dump_fops = { - .open = clk_dump_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) +struct clk *clk_get_parent_by_index(struct clk *clk, u8 index) { - struct dentry *d; - int ret = -ENOMEM; + struct clk_core *parent; - if (!core || !pdentry) { - ret = -EINVAL; - goto out; - } + if (!clk) + return NULL; - d = debugfs_create_dir(core->name, pdentry); - if (!d) - goto out; - - core->dentry = d; - - d = debugfs_create_u32("clk_rate", S_IRUGO, core->dentry, - (u32 *)&core->rate); - if (!d) - goto err_out; - - d = debugfs_create_u32("clk_accuracy", S_IRUGO, core->dentry, - (u32 *)&core->accuracy); - if (!d) - goto err_out; + parent = clk_core_get_parent_by_index(clk->core, index); - d = debugfs_create_u32("clk_phase", S_IRUGO, core->dentry, - (u32 *)&core->phase); - if (!d) - goto err_out; + return !parent ? NULL : parent->hw->clk; +} +EXPORT_SYMBOL_GPL(clk_get_parent_by_index); - d = debugfs_create_x32("clk_flags", S_IRUGO, core->dentry, - (u32 *)&core->flags); - if (!d) - goto err_out; +unsigned int __clk_get_enable_count(struct clk *clk) +{ + return !clk ? 0 : clk->core->enable_count; +} - d = debugfs_create_u32("clk_prepare_count", S_IRUGO, core->dentry, - (u32 *)&core->prepare_count); - if (!d) - goto err_out; +static unsigned long clk_core_get_rate_nolock(struct clk_core *core) +{ + unsigned long ret; - d = debugfs_create_u32("clk_enable_count", S_IRUGO, core->dentry, - (u32 *)&core->enable_count); - if (!d) - goto err_out; + if (!core) { + ret = 0; + goto out; + } - d = debugfs_create_u32("clk_notifier_count", S_IRUGO, core->dentry, - (u32 *)&core->notifier_count); - if (!d) - goto err_out; + ret = core->rate; - if (core->ops->debug_init) { - ret = core->ops->debug_init(core->hw, core->dentry); - if (ret) - goto err_out; - } + if (core->flags & CLK_IS_ROOT) + goto out; - ret = 0; - goto out; + if (!core->parent) + ret = 0; -err_out: - debugfs_remove_recursive(core->dentry); - core->dentry = NULL; out: return ret; } -/** - * clk_debug_register - add a clk node to the debugfs clk tree - * @core: the clk being added to the debugfs clk tree - * - * Dynamically adds a clk to the debugfs clk tree if debugfs has been - * initialized. Otherwise it bails out early since the debugfs clk tree - * will be created lazily by clk_debug_init as part of a late_initcall. - */ -static int clk_debug_register(struct clk_core *core) +unsigned long __clk_get_rate(struct clk *clk) { - int ret = 0; - - mutex_lock(&clk_debug_lock); - hlist_add_head(&core->debug_node, &clk_debug_list); + if (!clk) + return 0; - if (!inited) - goto unlock; + return clk_core_get_rate_nolock(clk->core); +} +EXPORT_SYMBOL_GPL(__clk_get_rate); - ret = clk_debug_create_one(core, rootdir); -unlock: - mutex_unlock(&clk_debug_lock); +static unsigned long __clk_get_accuracy(struct clk_core *core) +{ + if (!core) + return 0; - return ret; + return core->accuracy; } - /** - * clk_debug_unregister - remove a clk node from the debugfs clk tree - * @core: the clk being removed from the debugfs clk tree - * - * Dynamically removes a clk and all it's children clk nodes from the - * debugfs clk tree if clk->dentry points to debugfs created by - * clk_debug_register in __clk_init. - */ -static void clk_debug_unregister(struct clk_core *core) +unsigned long __clk_get_flags(struct clk *clk) { - mutex_lock(&clk_debug_lock); - hlist_del_init(&core->debug_node); - debugfs_remove_recursive(core->dentry); - core->dentry = NULL; - mutex_unlock(&clk_debug_lock); + return !clk ? 0 : clk->core->flags; } +EXPORT_SYMBOL_GPL(__clk_get_flags); -struct dentry *clk_debugfs_add_file(struct clk_hw *hw, char *name, umode_t mode, - void *data, const struct file_operations *fops) +bool __clk_is_prepared(struct clk *clk) { - struct dentry *d = NULL; - - if (hw->core->dentry) - d = debugfs_create_file(name, mode, hw->core->dentry, data, - fops); + if (!clk) + return false; - return d; + return clk_core_is_prepared(clk->core); } -EXPORT_SYMBOL_GPL(clk_debugfs_add_file); -/** - * clk_debug_init - lazily create the debugfs clk tree visualization - * - * clks are often initialized very early during boot before memory can - * be dynamically allocated and well before debugfs is setup. - * clk_debug_init walks the clk tree hierarchy while holding - * prepare_lock and creates the topology as part of a late_initcall, - * thus insuring that clks initialized very early will still be - * represented in the debugfs clk tree. This function should only be - * called once at boot-time, and all other clks added dynamically will - * be done so with clk_debug_register. - */ -static int __init clk_debug_init(void) +bool __clk_is_enabled(struct clk *clk) { - struct clk_core *core; - struct dentry *d; - - rootdir = debugfs_create_dir("clk", NULL); + if (!clk) + return false; - if (!rootdir) - return -ENOMEM; + return clk_core_is_enabled(clk->core); +} +EXPORT_SYMBOL_GPL(__clk_is_enabled); - d = debugfs_create_file("clk_summary", S_IRUGO, rootdir, &all_lists, - &clk_summary_fops); - if (!d) - return -ENOMEM; +static bool mux_is_better_rate(unsigned long rate, unsigned long now, + unsigned long best, unsigned long flags) +{ + if (flags & CLK_MUX_ROUND_CLOSEST) + return abs(now - rate) < abs(best - rate); - d = debugfs_create_file("clk_dump", S_IRUGO, rootdir, &all_lists, - &clk_dump_fops); - if (!d) - return -ENOMEM; + return now <= rate && now > best; +} - d = debugfs_create_file("clk_orphan_summary", S_IRUGO, rootdir, - &orphan_list, &clk_summary_fops); - if (!d) - return -ENOMEM; +static long +clk_mux_determine_rate_flags(struct clk_hw *hw, unsigned long rate, + unsigned long min_rate, + unsigned long max_rate, + unsigned long *best_parent_rate, + struct clk_hw **best_parent_p, + unsigned long flags) +{ + struct clk_core *core = hw->core, *parent, *best_parent = NULL; + int i, num_parents; + unsigned long parent_rate, best = 0; - d = debugfs_create_file("clk_orphan_dump", S_IRUGO, rootdir, - &orphan_list, &clk_dump_fops); - if (!d) - return -ENOMEM; + /* if NO_REPARENT flag set, pass through to current parent */ + if (core->flags & CLK_SET_RATE_NO_REPARENT) { + parent = core->parent; + if (core->flags & CLK_SET_RATE_PARENT) + best = __clk_determine_rate(parent ? parent->hw : NULL, + rate, min_rate, max_rate); + else if (parent) + best = clk_core_get_rate_nolock(parent); + else + best = clk_core_get_rate_nolock(core); + goto out; + } - mutex_lock(&clk_debug_lock); - hlist_for_each_entry(core, &clk_debug_list, debug_node) - clk_debug_create_one(core, rootdir); + /* find the parent that can provide the fastest rate <= rate */ + num_parents = core->num_parents; + for (i = 0; i < num_parents; i++) { + parent = clk_core_get_parent_by_index(core, i); + if (!parent) + continue; + if (core->flags & CLK_SET_RATE_PARENT) + parent_rate = __clk_determine_rate(parent->hw, rate, + min_rate, + max_rate); + else + parent_rate = clk_core_get_rate_nolock(parent); + if (mux_is_better_rate(rate, parent_rate, best, flags)) { + best_parent = parent; + best = parent_rate; + } + } - inited = 1; - mutex_unlock(&clk_debug_lock); +out: + if (best_parent) + *best_parent_p = best_parent->hw; + *best_parent_rate = best; - return 0; -} -late_initcall(clk_debug_init); -#else -static inline int clk_debug_register(struct clk_core *core) { return 0; } -static inline void clk_debug_reparent(struct clk_core *core, - struct clk_core *new_parent) -{ + return best; } -static inline void clk_debug_unregister(struct clk_core *core) + +struct clk *__clk_lookup(const char *name) { + struct clk_core *core = clk_core_lookup(name); + + return !core ? NULL : core->hw->clk; } -#endif -/* caller must hold prepare_lock */ -static void clk_unprepare_unused_subtree(struct clk_core *core) +static void clk_core_get_boundaries(struct clk_core *core, + unsigned long *min_rate, + unsigned long *max_rate) { - struct clk_core *child; + struct clk *clk_user; - lockdep_assert_held(&prepare_lock); + *min_rate = 0; + *max_rate = ULONG_MAX; - hlist_for_each_entry(child, &core->children, child_node) - clk_unprepare_unused_subtree(child); + hlist_for_each_entry(clk_user, &core->clks, clks_node) + *min_rate = max(*min_rate, clk_user->min_rate); - if (core->prepare_count) - return; + hlist_for_each_entry(clk_user, &core->clks, clks_node) + *max_rate = min(*max_rate, clk_user->max_rate); +} - if (core->flags & CLK_IGNORE_UNUSED) - return; - - if (clk_core_is_prepared(core)) { - trace_clk_unprepare(core); - if (core->ops->unprepare_unused) - core->ops->unprepare_unused(core->hw); - else if (core->ops->unprepare) - core->ops->unprepare(core->hw); - trace_clk_unprepare_complete(core); - } +/* + * Helper for finding best parent to provide a given frequency. This can be used + * directly as a determine_rate callback (e.g. for a mux), or from a more + * complex clock that may combine a mux with other operations. + */ +long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long min_rate, + unsigned long max_rate, + unsigned long *best_parent_rate, + struct clk_hw **best_parent_p) +{ + return clk_mux_determine_rate_flags(hw, rate, min_rate, max_rate, + best_parent_rate, + best_parent_p, 0); } +EXPORT_SYMBOL_GPL(__clk_mux_determine_rate); -/* caller must hold prepare_lock */ -static void clk_disable_unused_subtree(struct clk_core *core) +long __clk_mux_determine_rate_closest(struct clk_hw *hw, unsigned long rate, + unsigned long min_rate, + unsigned long max_rate, + unsigned long *best_parent_rate, + struct clk_hw **best_parent_p) { - struct clk_core *child; - unsigned long flags; + return clk_mux_determine_rate_flags(hw, rate, min_rate, max_rate, + best_parent_rate, + best_parent_p, + CLK_MUX_ROUND_CLOSEST); +} +EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest); - lockdep_assert_held(&prepare_lock); +/*** clk api ***/ - hlist_for_each_entry(child, &core->children, child_node) - clk_disable_unused_subtree(child); +static void clk_core_unprepare(struct clk_core *core) +{ + if (!core) + return; - flags = clk_enable_lock(); + if (WARN_ON(core->prepare_count == 0)) + return; - if (core->enable_count) - goto unlock_out; + if (--core->prepare_count > 0) + return; - if (core->flags & CLK_IGNORE_UNUSED) - goto unlock_out; + WARN_ON(core->enable_count > 0); - /* - * some gate clocks have special needs during the disable-unused - * sequence. call .disable_unused if available, otherwise fall - * back to .disable - */ - if (clk_core_is_enabled(core)) { - trace_clk_disable(core); - if (core->ops->disable_unused) - core->ops->disable_unused(core->hw); - else if (core->ops->disable) - core->ops->disable(core->hw); - trace_clk_disable_complete(core); - } + trace_clk_unprepare(core); -unlock_out: - clk_enable_unlock(flags); + if (core->ops->unprepare) + core->ops->unprepare(core->hw); + + trace_clk_unprepare_complete(core); + clk_core_unprepare(core->parent); } -static bool clk_ignore_unused; -static int __init clk_ignore_unused_setup(char *__unused) +/** + * clk_unprepare - undo preparation of a clock source + * @clk: the clk being unprepared + * + * clk_unprepare may sleep, which differentiates it from clk_disable. In a + * simple case, clk_unprepare can be used instead of clk_disable to gate a clk + * if the operation may sleep. One example is a clk which is accessed over + * I2c. In the complex case a clk gate operation may require a fast and a slow + * part. It is this reason that clk_unprepare and clk_disable are not mutually + * exclusive. In fact clk_disable must be called before clk_unprepare. + */ +void clk_unprepare(struct clk *clk) { - clk_ignore_unused = true; - return 1; + if (IS_ERR_OR_NULL(clk)) + return; + + clk_prepare_lock(); + clk_core_unprepare(clk->core); + clk_prepare_unlock(); } -__setup("clk_ignore_unused", clk_ignore_unused_setup); +EXPORT_SYMBOL_GPL(clk_unprepare); -static int clk_disable_unused(void) +static int clk_core_prepare(struct clk_core *core) { - struct clk_core *core; + int ret = 0; - if (clk_ignore_unused) { - pr_warn("clk: Not disabling unused clocks\n"); + if (!core) return 0; - } - clk_prepare_lock(); + if (core->prepare_count == 0) { + ret = clk_core_prepare(core->parent); + if (ret) + return ret; - hlist_for_each_entry(core, &clk_root_list, child_node) - clk_disable_unused_subtree(core); + trace_clk_prepare(core); - hlist_for_each_entry(core, &clk_orphan_list, child_node) - clk_disable_unused_subtree(core); + if (core->ops->prepare) + ret = core->ops->prepare(core->hw); - hlist_for_each_entry(core, &clk_root_list, child_node) - clk_unprepare_unused_subtree(core); + trace_clk_prepare_complete(core); - hlist_for_each_entry(core, &clk_orphan_list, child_node) - clk_unprepare_unused_subtree(core); + if (ret) { + clk_core_unprepare(core->parent); + return ret; + } + } - clk_prepare_unlock(); + core->prepare_count++; return 0; } -late_initcall_sync(clk_disable_unused); - -/*** helper functions ***/ -const char *__clk_get_name(struct clk *clk) +/** + * clk_prepare - prepare a clock source + * @clk: the clk being prepared + * + * clk_prepare may sleep, which differentiates it from clk_enable. In a simple + * case, clk_prepare can be used instead of clk_enable to ungate a clk if the + * operation may sleep. One example is a clk which is accessed over I2c. In + * the complex case a clk ungate operation may require a fast and a slow part. + * It is this reason that clk_prepare and clk_enable are not mutually + * exclusive. In fact clk_prepare must be called before clk_enable. + * Returns 0 on success, -EERROR otherwise. + */ +int clk_prepare(struct clk *clk) { - return !clk ? NULL : clk->core->name; -} -EXPORT_SYMBOL_GPL(__clk_get_name); + int ret; -struct clk_hw *__clk_get_hw(struct clk *clk) -{ - return !clk ? NULL : clk->core->hw; -} -EXPORT_SYMBOL_GPL(__clk_get_hw); + if (!clk) + return 0; -u8 __clk_get_num_parents(struct clk *clk) -{ - return !clk ? 0 : clk->core->num_parents; + clk_prepare_lock(); + ret = clk_core_prepare(clk->core); + clk_prepare_unlock(); + + return ret; } -EXPORT_SYMBOL_GPL(__clk_get_num_parents); +EXPORT_SYMBOL_GPL(clk_prepare); -struct clk *__clk_get_parent(struct clk *clk) +static void clk_core_disable(struct clk_core *core) { - if (!clk) - return NULL; + if (!core) + return; - /* TODO: Create a per-user clk and change callers to call clk_put */ - return !clk->core->parent ? NULL : clk->core->parent->hw->clk; -} -EXPORT_SYMBOL_GPL(__clk_get_parent); + if (WARN_ON(core->enable_count == 0)) + return; -static struct clk_core *clk_core_get_parent_by_index(struct clk_core *core, - u8 index) -{ - if (!core || index >= core->num_parents) - return NULL; - else if (!core->parents) - return clk_core_lookup(core->parent_names[index]); - else if (!core->parents[index]) - return core->parents[index] = - clk_core_lookup(core->parent_names[index]); - else - return core->parents[index]; -} + if (--core->enable_count > 0) + return; -struct clk *clk_get_parent_by_index(struct clk *clk, u8 index) -{ - struct clk_core *parent; + trace_clk_disable(core); - if (!clk) - return NULL; + if (core->ops->disable) + core->ops->disable(core->hw); - parent = clk_core_get_parent_by_index(clk->core, index); + trace_clk_disable_complete(core); - return !parent ? NULL : parent->hw->clk; + clk_core_disable(core->parent); } -EXPORT_SYMBOL_GPL(clk_get_parent_by_index); -unsigned int __clk_get_enable_count(struct clk *clk) +/** + * clk_disable - gate a clock + * @clk: the clk being gated + * + * clk_disable must not sleep, which differentiates it from clk_unprepare. In + * a simple case, clk_disable can be used instead of clk_unprepare to gate a + * clk if the operation is fast and will never sleep. One example is a + * SoC-internal clk which is controlled via simple register writes. In the + * complex case a clk gate operation may require a fast and a slow part. It is + * this reason that clk_unprepare and clk_disable are not mutually exclusive. + * In fact clk_disable must be called before clk_unprepare. + */ +void clk_disable(struct clk *clk) { - return !clk ? 0 : clk->core->enable_count; + unsigned long flags; + + if (IS_ERR_OR_NULL(clk)) + return; + + flags = clk_enable_lock(); + clk_core_disable(clk->core); + clk_enable_unlock(flags); } +EXPORT_SYMBOL_GPL(clk_disable); -static unsigned long clk_core_get_rate_nolock(struct clk_core *core) +static int clk_core_enable(struct clk_core *core) { - unsigned long ret; + int ret = 0; - if (!core) { - ret = 0; - goto out; - } + if (!core) + return 0; - ret = core->rate; + if (WARN_ON(core->prepare_count == 0)) + return -ESHUTDOWN; - if (core->flags & CLK_IS_ROOT) - goto out; + if (core->enable_count == 0) { + ret = clk_core_enable(core->parent); - if (!core->parent) - ret = 0; + if (ret) + return ret; -out: - return ret; -} + trace_clk_enable(core); -unsigned long __clk_get_rate(struct clk *clk) -{ - if (!clk) - return 0; + if (core->ops->enable) + ret = core->ops->enable(core->hw); - return clk_core_get_rate_nolock(clk->core); + trace_clk_enable_complete(core); + + if (ret) { + clk_core_disable(core->parent); + return ret; + } + } + + core->enable_count++; + return 0; } -EXPORT_SYMBOL_GPL(__clk_get_rate); -static unsigned long __clk_get_accuracy(struct clk_core *core) +/** + * clk_enable - ungate a clock + * @clk: the clk being ungated + * + * clk_enable must not sleep, which differentiates it from clk_prepare. In a + * simple case, clk_enable can be used instead of clk_prepare to ungate a clk + * if the operation will never sleep. One example is a SoC-internal clk which + * is controlled via simple register writes. In the complex case a clk ungate + * operation may require a fast and a slow part. It is this reason that + * clk_enable and clk_prepare are not mutually exclusive. In fact clk_prepare + * must be called before clk_enable. Returns 0 on success, -EERROR + * otherwise. + */ +int clk_enable(struct clk *clk) { - if (!core) + unsigned long flags; + int ret; + + if (!clk) return 0; - return core->accuracy; -} + flags = clk_enable_lock(); + ret = clk_core_enable(clk->core); + clk_enable_unlock(flags); -unsigned long __clk_get_flags(struct clk *clk) -{ - return !clk ? 0 : clk->core->flags; + return ret; } -EXPORT_SYMBOL_GPL(__clk_get_flags); +EXPORT_SYMBOL_GPL(clk_enable); -static bool clk_core_is_prepared(struct clk_core *core) +static unsigned long clk_core_round_rate_nolock(struct clk_core *core, + unsigned long rate, + unsigned long min_rate, + unsigned long max_rate) { - int ret; + unsigned long parent_rate = 0; + struct clk_core *parent; + struct clk_hw *parent_hw; + + lockdep_assert_held(&prepare_lock); if (!core) - return false; + return 0; - /* - * .is_prepared is optional for clocks that can prepare - * fall back to software usage counter if it is missing - */ - if (!core->ops->is_prepared) { - ret = core->prepare_count ? 1 : 0; - goto out; - } + parent = core->parent; + if (parent) + parent_rate = parent->rate; - ret = core->ops->is_prepared(core->hw); -out: - return !!ret; + if (core->ops->determine_rate) { + parent_hw = parent ? parent->hw : NULL; + return core->ops->determine_rate(core->hw, rate, + min_rate, max_rate, + &parent_rate, &parent_hw); + } else if (core->ops->round_rate) + return core->ops->round_rate(core->hw, rate, &parent_rate); + else if (core->flags & CLK_SET_RATE_PARENT) + return clk_core_round_rate_nolock(core->parent, rate, min_rate, + max_rate); + else + return core->rate; } -bool __clk_is_prepared(struct clk *clk) +/** + * __clk_determine_rate - get the closest rate actually supported by a clock + * @hw: determine the rate of this clock + * @rate: target rate + * @min_rate: returned rate must be greater than this rate + * @max_rate: returned rate must be less than this rate + * + * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate and + * .determine_rate. + */ +unsigned long __clk_determine_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long min_rate, + unsigned long max_rate) { - if (!clk) - return false; + if (!hw) + return 0; - return clk_core_is_prepared(clk->core); + return clk_core_round_rate_nolock(hw->core, rate, min_rate, max_rate); } +EXPORT_SYMBOL_GPL(__clk_determine_rate); -static bool clk_core_is_enabled(struct clk_core *core) +/** + * __clk_round_rate - round the given rate for a clk + * @clk: round the rate of this clock + * @rate: the rate which is to be rounded + * + * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate + */ +unsigned long __clk_round_rate(struct clk *clk, unsigned long rate) { - int ret; + unsigned long min_rate; + unsigned long max_rate; - if (!core) - return false; + if (!clk) + return 0; - /* - * .is_enabled is only mandatory for clocks that gate - * fall back to software usage counter if .is_enabled is missing - */ - if (!core->ops->is_enabled) { - ret = core->enable_count ? 1 : 0; - goto out; - } + clk_core_get_boundaries(clk->core, &min_rate, &max_rate); - ret = core->ops->is_enabled(core->hw); -out: - return !!ret; + return clk_core_round_rate_nolock(clk->core, rate, min_rate, max_rate); } +EXPORT_SYMBOL_GPL(__clk_round_rate); -bool __clk_is_enabled(struct clk *clk) +/** + * clk_round_rate - round the given rate for a clk + * @clk: the clk for which we are rounding a rate + * @rate: the rate which is to be rounded + * + * Takes in a rate as input and rounds it to a rate that the clk can actually + * use which is then returned. If clk doesn't support round_rate operation + * then the parent rate is returned. + */ +long clk_round_rate(struct clk *clk, unsigned long rate) { + unsigned long ret; + if (!clk) - return false; + return 0; - return clk_core_is_enabled(clk->core); + clk_prepare_lock(); + ret = __clk_round_rate(clk, rate); + clk_prepare_unlock(); + + return ret; } -EXPORT_SYMBOL_GPL(__clk_is_enabled); +EXPORT_SYMBOL_GPL(clk_round_rate); -static struct clk_core *__clk_lookup_subtree(const char *name, - struct clk_core *core) +/** + * __clk_notify - call clk notifier chain + * @core: 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_core *core, unsigned long msg, + unsigned long old_rate, unsigned long new_rate) { - struct clk_core *child; - struct clk_core *ret; + struct clk_notifier *cn; + struct clk_notifier_data cnd; + int ret = NOTIFY_DONE; - if (!strcmp(core->name, name)) - return core; + cnd.old_rate = old_rate; + cnd.new_rate = new_rate; - hlist_for_each_entry(child, &core->children, child_node) { - ret = __clk_lookup_subtree(name, child); - if (ret) - return ret; + list_for_each_entry(cn, &clk_notifier_list, node) { + if (cn->clk->core == core) { + cnd.clk = cn->clk; + ret = srcu_notifier_call_chain(&cn->notifier_head, msg, + &cnd); + } } - return NULL; + return ret; } -static struct clk_core *clk_core_lookup(const char *name) +/** + * __clk_recalc_accuracies + * @core: first clk in the subtree + * + * Walks the subtree of clks starting with clk and recalculates accuracies as + * it goes. Note that if a clk does not implement the .recalc_accuracy + * callback then it is assumed that the clock will take on the accuracy of it's + * parent. + * + * Caller must hold prepare_lock. + */ +static void __clk_recalc_accuracies(struct clk_core *core) { - struct clk_core *root_clk; - struct clk_core *ret; + unsigned long parent_accuracy = 0; + struct clk_core *child; - if (!name) - return NULL; + lockdep_assert_held(&prepare_lock); - /* search the 'proper' clk tree first */ - hlist_for_each_entry(root_clk, &clk_root_list, child_node) { - ret = __clk_lookup_subtree(name, root_clk); - if (ret) - return ret; - } + if (core->parent) + parent_accuracy = core->parent->accuracy; - /* if not found, then search the orphan tree */ - hlist_for_each_entry(root_clk, &clk_orphan_list, child_node) { - ret = __clk_lookup_subtree(name, root_clk); - if (ret) - return ret; - } + if (core->ops->recalc_accuracy) + core->accuracy = core->ops->recalc_accuracy(core->hw, + parent_accuracy); + else + core->accuracy = parent_accuracy; - return NULL; + hlist_for_each_entry(child, &core->children, child_node) + __clk_recalc_accuracies(child); } -static bool mux_is_better_rate(unsigned long rate, unsigned long now, - unsigned long best, unsigned long flags) +static long clk_core_get_accuracy(struct clk_core *core) { - if (flags & CLK_MUX_ROUND_CLOSEST) - return abs(now - rate) < abs(best - rate); + unsigned long accuracy; - return now <= rate && now > best; -} + clk_prepare_lock(); + if (core && (core->flags & CLK_GET_ACCURACY_NOCACHE)) + __clk_recalc_accuracies(core); -static long -clk_mux_determine_rate_flags(struct clk_hw *hw, unsigned long rate, - unsigned long min_rate, - unsigned long max_rate, - unsigned long *best_parent_rate, - struct clk_hw **best_parent_p, - unsigned long flags) -{ - struct clk_core *core = hw->core, *parent, *best_parent = NULL; - int i, num_parents; - unsigned long parent_rate, best = 0; - - /* if NO_REPARENT flag set, pass through to current parent */ - if (core->flags & CLK_SET_RATE_NO_REPARENT) { - parent = core->parent; - if (core->flags & CLK_SET_RATE_PARENT) - best = __clk_determine_rate(parent ? parent->hw : NULL, - rate, min_rate, max_rate); - else if (parent) - best = clk_core_get_rate_nolock(parent); - else - best = clk_core_get_rate_nolock(core); - goto out; - } - - /* find the parent that can provide the fastest rate <= rate */ - num_parents = core->num_parents; - for (i = 0; i < num_parents; i++) { - parent = clk_core_get_parent_by_index(core, i); - if (!parent) - continue; - if (core->flags & CLK_SET_RATE_PARENT) - parent_rate = __clk_determine_rate(parent->hw, rate, - min_rate, - max_rate); - else - parent_rate = clk_core_get_rate_nolock(parent); - if (mux_is_better_rate(rate, parent_rate, best, flags)) { - best_parent = parent; - best = parent_rate; - } - } - -out: - if (best_parent) - *best_parent_p = best_parent->hw; - *best_parent_rate = best; + accuracy = __clk_get_accuracy(core); + clk_prepare_unlock(); - return best; + return accuracy; } -struct clk *__clk_lookup(const char *name) +/** + * clk_get_accuracy - return the accuracy of clk + * @clk: the clk whose accuracy is being returned + * + * Simply returns the cached accuracy of the clk, unless + * CLK_GET_ACCURACY_NOCACHE flag is set, which means a recalc_rate will be + * issued. + * If clk is NULL then returns 0. + */ +long clk_get_accuracy(struct clk *clk) { - struct clk_core *core = clk_core_lookup(name); + if (!clk) + return 0; - return !core ? NULL : core->hw->clk; + return clk_core_get_accuracy(clk->core); } +EXPORT_SYMBOL_GPL(clk_get_accuracy); -static void clk_core_get_boundaries(struct clk_core *core, - unsigned long *min_rate, - unsigned long *max_rate) +static unsigned long clk_recalc(struct clk_core *core, + unsigned long parent_rate) { - struct clk *clk_user; - - *min_rate = 0; - *max_rate = ULONG_MAX; - - hlist_for_each_entry(clk_user, &core->clks, clks_node) - *min_rate = max(*min_rate, clk_user->min_rate); - - hlist_for_each_entry(clk_user, &core->clks, clks_node) - *max_rate = min(*max_rate, clk_user->max_rate); + if (core->ops->recalc_rate) + return core->ops->recalc_rate(core->hw, parent_rate); + return parent_rate; } -/* - * Helper for finding best parent to provide a given frequency. This can be used - * directly as a determine_rate callback (e.g. for a mux), or from a more - * complex clock that may combine a mux with other operations. +/** + * __clk_recalc_rates + * @core: first clk in the subtree + * @msg: notification type (see include/linux/clk.h) + * + * Walks the subtree of clks starting with clk and recalculates rates as it + * goes. Note that if a clk does not implement the .recalc_rate callback then + * it is assumed that the clock will take on the rate of its parent. + * + * clk_recalc_rates also propagates the POST_RATE_CHANGE notification, + * if necessary. + * + * Caller must hold prepare_lock. */ -long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate, - unsigned long min_rate, - unsigned long max_rate, - unsigned long *best_parent_rate, - struct clk_hw **best_parent_p) +static void __clk_recalc_rates(struct clk_core *core, unsigned long msg) { - return clk_mux_determine_rate_flags(hw, rate, min_rate, max_rate, - best_parent_rate, - best_parent_p, 0); -} -EXPORT_SYMBOL_GPL(__clk_mux_determine_rate); + unsigned long old_rate; + unsigned long parent_rate = 0; + struct clk_core *child; -long __clk_mux_determine_rate_closest(struct clk_hw *hw, unsigned long rate, - unsigned long min_rate, - unsigned long max_rate, - unsigned long *best_parent_rate, - struct clk_hw **best_parent_p) -{ - return clk_mux_determine_rate_flags(hw, rate, min_rate, max_rate, - best_parent_rate, - best_parent_p, - CLK_MUX_ROUND_CLOSEST); -} -EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest); + lockdep_assert_held(&prepare_lock); -/*** clk api ***/ + old_rate = core->rate; -static void clk_core_unprepare(struct clk_core *core) -{ - if (!core) - return; + if (core->parent) + parent_rate = core->parent->rate; - if (WARN_ON(core->prepare_count == 0)) - return; + core->rate = clk_recalc(core, parent_rate); - if (--core->prepare_count > 0) - return; + /* + * ignore NOTIFY_STOP and NOTIFY_BAD return values for POST_RATE_CHANGE + * & ABORT_RATE_CHANGE notifiers + */ + if (core->notifier_count && msg) + __clk_notify(core, msg, old_rate, core->rate); - WARN_ON(core->enable_count > 0); + hlist_for_each_entry(child, &core->children, child_node) + __clk_recalc_rates(child, msg); +} - trace_clk_unprepare(core); +static unsigned long clk_core_get_rate(struct clk_core *core) +{ + unsigned long rate; - if (core->ops->unprepare) - core->ops->unprepare(core->hw); + clk_prepare_lock(); - trace_clk_unprepare_complete(core); - clk_core_unprepare(core->parent); + if (core && (core->flags & CLK_GET_RATE_NOCACHE)) + __clk_recalc_rates(core, 0); + + rate = clk_core_get_rate_nolock(core); + clk_prepare_unlock(); + + return rate; } /** - * clk_unprepare - undo preparation of a clock source - * @clk: the clk being unprepared + * clk_get_rate - return the rate of clk + * @clk: the clk whose rate is being returned * - * clk_unprepare may sleep, which differentiates it from clk_disable. In a - * simple case, clk_unprepare can be used instead of clk_disable to gate a clk - * if the operation may sleep. One example is a clk which is accessed over - * I2c. In the complex case a clk gate operation may require a fast and a slow - * part. It is this reason that clk_unprepare and clk_disable are not mutually - * exclusive. In fact clk_disable must be called before clk_unprepare. + * Simply returns the cached rate of the clk, unless CLK_GET_RATE_NOCACHE flag + * is set, which means a recalc_rate will be issued. + * If clk is NULL then returns 0. */ -void clk_unprepare(struct clk *clk) +unsigned long clk_get_rate(struct clk *clk) { - if (IS_ERR_OR_NULL(clk)) - return; + if (!clk) + return 0; - clk_prepare_lock(); - clk_core_unprepare(clk->core); - clk_prepare_unlock(); + return clk_core_get_rate(clk->core); } -EXPORT_SYMBOL_GPL(clk_unprepare); +EXPORT_SYMBOL_GPL(clk_get_rate); -static int clk_core_prepare(struct clk_core *core) +static int clk_fetch_parent_index(struct clk_core *core, + struct clk_core *parent) { - int ret = 0; - - if (!core) - return 0; - - if (core->prepare_count == 0) { - ret = clk_core_prepare(core->parent); - if (ret) - return ret; + int i; - trace_clk_prepare(core); + if (!core->parents) { + core->parents = kcalloc(core->num_parents, + sizeof(struct clk *), GFP_KERNEL); + if (!core->parents) + return -ENOMEM; + } - if (core->ops->prepare) - ret = core->ops->prepare(core->hw); + /* + * find index of new parent clock using cached parent ptrs, + * or if not yet cached, use string name comparison and cache + * them now to avoid future calls to clk_core_lookup. + */ + for (i = 0; i < core->num_parents; i++) { + if (core->parents[i] == parent) + return i; - trace_clk_prepare_complete(core); + if (core->parents[i]) + continue; - if (ret) { - clk_core_unprepare(core->parent); - return ret; + if (!strcmp(core->parent_names[i], parent->name)) { + core->parents[i] = clk_core_lookup(parent->name); + return i; } } - core->prepare_count++; - - return 0; + return -EINVAL; } -/** - * clk_prepare - prepare a clock source - * @clk: the clk being prepared - * - * clk_prepare may sleep, which differentiates it from clk_enable. In a simple - * case, clk_prepare can be used instead of clk_enable to ungate a clk if the - * operation may sleep. One example is a clk which is accessed over I2c. In - * the complex case a clk ungate operation may require a fast and a slow part. - * It is this reason that clk_prepare and clk_enable are not mutually - * exclusive. In fact clk_prepare must be called before clk_enable. - * Returns 0 on success, -EERROR otherwise. - */ -int clk_prepare(struct clk *clk) +static void clk_reparent(struct clk_core *core, struct clk_core *new_parent) { - int ret; - - if (!clk) - return 0; + hlist_del(&core->child_node); - clk_prepare_lock(); - ret = clk_core_prepare(clk->core); - clk_prepare_unlock(); - - return ret; -} -EXPORT_SYMBOL_GPL(clk_prepare); - -static void clk_core_disable(struct clk_core *core) -{ - if (!core) - return; - - if (WARN_ON(core->enable_count == 0)) - return; - - if (--core->enable_count > 0) - return; - - trace_clk_disable(core); - - if (core->ops->disable) - core->ops->disable(core->hw); + if (new_parent) { + /* avoid duplicate POST_RATE_CHANGE notifications */ + if (new_parent->new_child == core) + new_parent->new_child = NULL; - trace_clk_disable_complete(core); + hlist_add_head(&core->child_node, &new_parent->children); + } else { + hlist_add_head(&core->child_node, &clk_orphan_list); + } - clk_core_disable(core->parent); + core->parent = new_parent; } -/** - * clk_disable - gate a clock - * @clk: the clk being gated - * - * clk_disable must not sleep, which differentiates it from clk_unprepare. In - * a simple case, clk_disable can be used instead of clk_unprepare to gate a - * clk if the operation is fast and will never sleep. One example is a - * SoC-internal clk which is controlled via simple register writes. In the - * complex case a clk gate operation may require a fast and a slow part. It is - * this reason that clk_unprepare and clk_disable are not mutually exclusive. - * In fact clk_disable must be called before clk_unprepare. - */ -void clk_disable(struct clk *clk) +static struct clk_core *__clk_set_parent_before(struct clk_core *core, + struct clk_core *parent) { unsigned long flags; + struct clk_core *old_parent = core->parent; - if (IS_ERR_OR_NULL(clk)) - return; + /* + * Migrate prepare state between parents and prevent race with + * clk_enable(). + * + * If the clock is not prepared, then a race with + * clk_enable/disable() is impossible since we already have the + * prepare lock (future calls to clk_enable() need to be preceded by + * a clk_prepare()). + * + * If the clock is prepared, migrate the prepared state to the new + * parent and also protect against a race with clk_enable() by + * forcing the clock and the new parent on. This ensures that all + * future calls to clk_enable() are practically NOPs with respect to + * hardware and software states. + * + * See also: Comment for clk_set_parent() below. + */ + if (core->prepare_count) { + clk_core_prepare(parent); + clk_core_enable(parent); + clk_core_enable(core); + } + /* update the clk tree topology */ flags = clk_enable_lock(); - clk_core_disable(clk->core); + clk_reparent(core, parent); clk_enable_unlock(flags); + + return old_parent; } -EXPORT_SYMBOL_GPL(clk_disable); -static int clk_core_enable(struct clk_core *core) +static void __clk_set_parent_after(struct clk_core *core, + struct clk_core *parent, + struct clk_core *old_parent) { - int ret = 0; - - if (!core) - return 0; + /* + * Finish the migration of prepare state and undo the changes done + * for preventing a race with clk_enable(). + */ + if (core->prepare_count) { + clk_core_disable(core); + clk_core_disable(old_parent); + clk_core_unprepare(old_parent); + } +} - if (WARN_ON(core->prepare_count == 0)) - return -ESHUTDOWN; +static int __clk_set_parent(struct clk_core *core, struct clk_core *parent, + u8 p_index) +{ + unsigned long flags; + int ret = 0; + struct clk_core *old_parent; - if (core->enable_count == 0) { - ret = clk_core_enable(core->parent); + old_parent = __clk_set_parent_before(core, parent); - if (ret) - return ret; + trace_clk_set_parent(core, parent); - trace_clk_enable(core); + /* change clock input source */ + if (parent && core->ops->set_parent) + ret = core->ops->set_parent(core->hw, p_index); - if (core->ops->enable) - ret = core->ops->enable(core->hw); + trace_clk_set_parent_complete(core, parent); - trace_clk_enable_complete(core); + if (ret) { + flags = clk_enable_lock(); + clk_reparent(core, old_parent); + clk_enable_unlock(flags); - if (ret) { - clk_core_disable(core->parent); - return ret; + if (core->prepare_count) { + clk_core_disable(core); + clk_core_disable(parent); + clk_core_unprepare(parent); } + return ret; } - core->enable_count++; + __clk_set_parent_after(core, parent, old_parent); + return 0; } /** - * clk_enable - ungate a clock - * @clk: the clk being ungated + * __clk_speculate_rates + * @core: first clk in the subtree + * @parent_rate: the "future" rate of clk's parent * - * clk_enable must not sleep, which differentiates it from clk_prepare. In a - * simple case, clk_enable can be used instead of clk_prepare to ungate a clk - * if the operation will never sleep. One example is a SoC-internal clk which - * is controlled via simple register writes. In the complex case a clk ungate - * operation may require a fast and a slow part. It is this reason that - * clk_enable and clk_prepare are not mutually exclusive. In fact clk_prepare - * must be called before clk_enable. Returns 0 on success, -EERROR - * otherwise. + * Walks the subtree of clks starting with clk, speculating rates as it + * goes and firing off PRE_RATE_CHANGE notifications as necessary. + * + * Unlike clk_recalc_rates, clk_speculate_rates exists only for sending + * pre-rate change notifications and returns early if no clks in the + * subtree have subscribed to the notifications. Note that if a clk does not + * implement the .recalc_rate callback then it is assumed that the clock will + * take on the rate of its parent. + * + * Caller must hold prepare_lock. */ -int clk_enable(struct clk *clk) +static int __clk_speculate_rates(struct clk_core *core, + unsigned long parent_rate) { - unsigned long flags; - int ret; + struct clk_core *child; + unsigned long new_rate; + int ret = NOTIFY_DONE; - if (!clk) - return 0; + lockdep_assert_held(&prepare_lock); - flags = clk_enable_lock(); - ret = clk_core_enable(clk->core); - clk_enable_unlock(flags); + new_rate = clk_recalc(core, parent_rate); + + /* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */ + if (core->notifier_count) + ret = __clk_notify(core, PRE_RATE_CHANGE, core->rate, new_rate); + + if (ret & NOTIFY_STOP_MASK) { + pr_debug("%s: clk notifier callback for clock %s aborted with error %d\n", + __func__, core->name, ret); + goto out; + } + + hlist_for_each_entry(child, &core->children, child_node) { + ret = __clk_speculate_rates(child, new_rate); + if (ret & NOTIFY_STOP_MASK) + break; + } +out: return ret; } -EXPORT_SYMBOL_GPL(clk_enable); -static unsigned long clk_core_round_rate_nolock(struct clk_core *core, - unsigned long rate, - unsigned long min_rate, - unsigned long max_rate) +static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate, + struct clk_core *new_parent, u8 p_index) { - unsigned long parent_rate = 0; - struct clk_core *parent; - struct clk_hw *parent_hw; + struct clk_core *child; - lockdep_assert_held(&prepare_lock); + core->new_rate = new_rate; + core->new_parent = new_parent; + core->new_parent_index = p_index; + /* include clk in new parent's PRE_RATE_CHANGE notifications */ + core->new_child = NULL; + if (new_parent && new_parent != core->parent) + new_parent->new_child = core; - if (!core) - return 0; + hlist_for_each_entry(child, &core->children, child_node) { + child->new_rate = clk_recalc(child, new_rate); + clk_calc_subtree(child, child->new_rate, NULL, 0); + } +} - parent = core->parent; +/* + * calculate the new rates returning the topmost clock that has to be + * changed. + */ +static struct clk_core *clk_calc_new_rates(struct clk_core *core, + unsigned long rate) +{ + struct clk_core *top = core; + struct clk_core *old_parent, *parent; + struct clk_hw *parent_hw; + unsigned long best_parent_rate = 0; + unsigned long new_rate; + unsigned long min_rate; + unsigned long max_rate; + int p_index = 0; + long ret; + + /* sanity */ + if (IS_ERR_OR_NULL(core)) + return NULL; + + /* save parent rate, if it exists */ + parent = old_parent = core->parent; if (parent) - parent_rate = parent->rate; + best_parent_rate = parent->rate; + clk_core_get_boundaries(core, &min_rate, &max_rate); + + /* find the closest rate and parent clk/rate */ if (core->ops->determine_rate) { parent_hw = parent ? parent->hw : NULL; - return core->ops->determine_rate(core->hw, rate, - min_rate, max_rate, - &parent_rate, &parent_hw); - } else if (core->ops->round_rate) - return core->ops->round_rate(core->hw, rate, &parent_rate); - else if (core->flags & CLK_SET_RATE_PARENT) - return clk_core_round_rate_nolock(core->parent, rate, min_rate, - max_rate); - else - return core->rate; -} - -/** - * __clk_determine_rate - get the closest rate actually supported by a clock - * @hw: determine the rate of this clock - * @rate: target rate - * @min_rate: returned rate must be greater than this rate - * @max_rate: returned rate must be less than this rate - * - * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate and - * .determine_rate. - */ -unsigned long __clk_determine_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long min_rate, - unsigned long max_rate) -{ - if (!hw) - return 0; - - return clk_core_round_rate_nolock(hw->core, rate, min_rate, max_rate); -} -EXPORT_SYMBOL_GPL(__clk_determine_rate); - -/** - * __clk_round_rate - round the given rate for a clk - * @clk: round the rate of this clock - * @rate: the rate which is to be rounded - * - * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate - */ -unsigned long __clk_round_rate(struct clk *clk, unsigned long rate) -{ - unsigned long min_rate; - unsigned long max_rate; + ret = core->ops->determine_rate(core->hw, rate, + min_rate, + max_rate, + &best_parent_rate, + &parent_hw); + if (ret < 0) + return NULL; - if (!clk) - return 0; + new_rate = ret; + parent = parent_hw ? parent_hw->core : NULL; + } else if (core->ops->round_rate) { + ret = core->ops->round_rate(core->hw, rate, + &best_parent_rate); + if (ret < 0) + return NULL; - clk_core_get_boundaries(clk->core, &min_rate, &max_rate); + new_rate = ret; + if (new_rate < min_rate || new_rate > max_rate) + return NULL; + } else if (!parent || !(core->flags & CLK_SET_RATE_PARENT)) { + /* pass-through clock without adjustable parent */ + core->new_rate = core->rate; + return NULL; + } else { + /* pass-through clock with adjustable parent */ + top = clk_calc_new_rates(parent, rate); + new_rate = parent->new_rate; + goto out; + } - return clk_core_round_rate_nolock(clk->core, rate, min_rate, max_rate); -} -EXPORT_SYMBOL_GPL(__clk_round_rate); + /* some clocks must be gated to change parent */ + if (parent != old_parent && + (core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) { + pr_debug("%s: %s not gated but wants to reparent\n", + __func__, core->name); + return NULL; + } -/** - * clk_round_rate - round the given rate for a clk - * @clk: the clk for which we are rounding a rate - * @rate: the rate which is to be rounded - * - * Takes in a rate as input and rounds it to a rate that the clk can actually - * use which is then returned. If clk doesn't support round_rate operation - * then the parent rate is returned. - */ -long clk_round_rate(struct clk *clk, unsigned long rate) -{ - unsigned long ret; + /* try finding the new parent index */ + if (parent && core->num_parents > 1) { + p_index = clk_fetch_parent_index(core, parent); + if (p_index < 0) { + pr_debug("%s: clk %s can not be parent of clk %s\n", + __func__, parent->name, core->name); + return NULL; + } + } - if (!clk) - return 0; + if ((core->flags & CLK_SET_RATE_PARENT) && parent && + best_parent_rate != parent->rate) + top = clk_calc_new_rates(parent, best_parent_rate); - clk_prepare_lock(); - ret = __clk_round_rate(clk, rate); - clk_prepare_unlock(); +out: + clk_calc_subtree(core, new_rate, parent, p_index); - return ret; + return top; } -EXPORT_SYMBOL_GPL(clk_round_rate); -/** - * __clk_notify - call clk notifier chain - * @core: 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. +/* + * Notify about rate changes in a subtree. Always walk down the whole tree + * so that in case of an error we can walk down the whole tree again and + * abort the change. */ -static int __clk_notify(struct clk_core *core, unsigned long msg, - unsigned long old_rate, unsigned long new_rate) +static struct clk_core *clk_propagate_rate_change(struct clk_core *core, + unsigned long event) { - struct clk_notifier *cn; - struct clk_notifier_data cnd; + struct clk_core *child, *tmp_clk, *fail_clk = NULL; int ret = NOTIFY_DONE; - cnd.old_rate = old_rate; - cnd.new_rate = new_rate; + if (core->rate == core->new_rate) + return NULL; - list_for_each_entry(cn, &clk_notifier_list, node) { - if (cn->clk->core == core) { - cnd.clk = cn->clk; - ret = srcu_notifier_call_chain(&cn->notifier_head, msg, - &cnd); - } + if (core->notifier_count) { + ret = __clk_notify(core, event, core->rate, core->new_rate); + if (ret & NOTIFY_STOP_MASK) + fail_clk = core; } - return ret; -} - -/** - * __clk_recalc_accuracies - * @core: first clk in the subtree - * - * Walks the subtree of clks starting with clk and recalculates accuracies as - * it goes. Note that if a clk does not implement the .recalc_accuracy - * callback then it is assumed that the clock will take on the accuracy of it's - * parent. - * - * Caller must hold prepare_lock. - */ -static void __clk_recalc_accuracies(struct clk_core *core) -{ - unsigned long parent_accuracy = 0; - struct clk_core *child; - - lockdep_assert_held(&prepare_lock); - - if (core->parent) - parent_accuracy = core->parent->accuracy; + hlist_for_each_entry(child, &core->children, child_node) { + /* Skip children who will be reparented to another clock */ + if (child->new_parent && child->new_parent != core) + continue; + tmp_clk = clk_propagate_rate_change(child, event); + if (tmp_clk) + fail_clk = tmp_clk; + } - if (core->ops->recalc_accuracy) - core->accuracy = core->ops->recalc_accuracy(core->hw, - parent_accuracy); - else - core->accuracy = parent_accuracy; + /* handle the new child who might not be in core->children yet */ + if (core->new_child) { + tmp_clk = clk_propagate_rate_change(core->new_child, event); + if (tmp_clk) + fail_clk = tmp_clk; + } - hlist_for_each_entry(child, &core->children, child_node) - __clk_recalc_accuracies(child); + return fail_clk; } -static long clk_core_get_accuracy(struct clk_core *core) +/* + * walk down a subtree and set the new rates notifying the rate + * change on the way + */ +static void clk_change_rate(struct clk_core *core) { - unsigned long accuracy; - - clk_prepare_lock(); - if (core && (core->flags & CLK_GET_ACCURACY_NOCACHE)) - __clk_recalc_accuracies(core); + struct clk_core *child; + struct hlist_node *tmp; + unsigned long old_rate; + unsigned long best_parent_rate = 0; + bool skip_set_rate = false; + struct clk_core *old_parent; - accuracy = __clk_get_accuracy(core); - clk_prepare_unlock(); + old_rate = core->rate; - return accuracy; -} + if (core->new_parent) + best_parent_rate = core->new_parent->rate; + else if (core->parent) + best_parent_rate = core->parent->rate; -/** - * clk_get_accuracy - return the accuracy of clk - * @clk: the clk whose accuracy is being returned - * - * Simply returns the cached accuracy of the clk, unless - * CLK_GET_ACCURACY_NOCACHE flag is set, which means a recalc_rate will be - * issued. - * If clk is NULL then returns 0. - */ -long clk_get_accuracy(struct clk *clk) -{ - if (!clk) - return 0; + if (core->new_parent && core->new_parent != core->parent) { + old_parent = __clk_set_parent_before(core, core->new_parent); + trace_clk_set_parent(core, core->new_parent); - return clk_core_get_accuracy(clk->core); -} -EXPORT_SYMBOL_GPL(clk_get_accuracy); + if (core->ops->set_rate_and_parent) { + skip_set_rate = true; + core->ops->set_rate_and_parent(core->hw, core->new_rate, + best_parent_rate, + core->new_parent_index); + } else if (core->ops->set_parent) { + core->ops->set_parent(core->hw, core->new_parent_index); + } -static unsigned long clk_recalc(struct clk_core *core, - unsigned long parent_rate) -{ - if (core->ops->recalc_rate) - return core->ops->recalc_rate(core->hw, parent_rate); - return parent_rate; -} + trace_clk_set_parent_complete(core, core->new_parent); + __clk_set_parent_after(core, core->new_parent, old_parent); + } -/** - * __clk_recalc_rates - * @core: first clk in the subtree - * @msg: notification type (see include/linux/clk.h) - * - * Walks the subtree of clks starting with clk and recalculates rates as it - * goes. Note that if a clk does not implement the .recalc_rate callback then - * it is assumed that the clock will take on the rate of its parent. - * - * clk_recalc_rates also propagates the POST_RATE_CHANGE notification, - * if necessary. - * - * Caller must hold prepare_lock. - */ -static void __clk_recalc_rates(struct clk_core *core, unsigned long msg) -{ - unsigned long old_rate; - unsigned long parent_rate = 0; - struct clk_core *child; + trace_clk_set_rate(core, core->new_rate); - lockdep_assert_held(&prepare_lock); + if (!skip_set_rate && core->ops->set_rate) + core->ops->set_rate(core->hw, core->new_rate, best_parent_rate); - old_rate = core->rate; + trace_clk_set_rate_complete(core, core->new_rate); - if (core->parent) - parent_rate = core->parent->rate; + core->rate = clk_recalc(core, best_parent_rate); - core->rate = clk_recalc(core, parent_rate); + if (core->notifier_count && old_rate != core->rate) + __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate); /* - * ignore NOTIFY_STOP and NOTIFY_BAD return values for POST_RATE_CHANGE - * & ABORT_RATE_CHANGE notifiers + * Use safe iteration, as change_rate can actually swap parents + * for certain clock types. */ - if (core->notifier_count && msg) - __clk_notify(core, msg, old_rate, core->rate); + hlist_for_each_entry_safe(child, tmp, &core->children, child_node) { + /* Skip children who will be reparented to another clock */ + if (child->new_parent && child->new_parent != core) + continue; + clk_change_rate(child); + } - hlist_for_each_entry(child, &core->children, child_node) - __clk_recalc_rates(child, msg); + /* handle the new child who might not be in core->children yet */ + if (core->new_child) + clk_change_rate(core->new_child); } -static unsigned long clk_core_get_rate(struct clk_core *core) +static int clk_core_set_rate_nolock(struct clk_core *core, + unsigned long req_rate) { - unsigned long rate; + struct clk_core *top, *fail_clk; + unsigned long rate = req_rate; + int ret = 0; - clk_prepare_lock(); + if (!core) + return 0; - if (core && (core->flags & CLK_GET_RATE_NOCACHE)) - __clk_recalc_rates(core, 0); + /* bail early if nothing to do */ + if (rate == clk_core_get_rate_nolock(core)) + return 0; - rate = clk_core_get_rate_nolock(core); - clk_prepare_unlock(); + if ((core->flags & CLK_SET_RATE_GATE) && core->prepare_count) + return -EBUSY; - return rate; + /* calculate new rates and get the topmost changed clock */ + top = clk_calc_new_rates(core, rate); + if (!top) + return -EINVAL; + + /* notify that we are about to change rates */ + fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE); + if (fail_clk) { + pr_debug("%s: failed to set %s rate\n", __func__, + fail_clk->name); + clk_propagate_rate_change(top, ABORT_RATE_CHANGE); + return -EBUSY; + } + + /* change the rates */ + clk_change_rate(top); + + core->req_rate = req_rate; + + return ret; } /** - * clk_get_rate - return the rate of clk - * @clk: the clk whose rate is being returned + * clk_set_rate - specify a new rate for clk + * @clk: the clk whose rate is being changed + * @rate: the new rate for clk * - * Simply returns the cached rate of the clk, unless CLK_GET_RATE_NOCACHE flag - * is set, which means a recalc_rate will be issued. - * If clk is NULL then returns 0. + * In the simplest case clk_set_rate will only adjust the rate of clk. + * + * Setting the CLK_SET_RATE_PARENT flag allows the rate change operation to + * propagate up to clk's parent; whether or not this happens depends on the + * outcome of clk's .round_rate implementation. If *parent_rate is unchanged + * after calling .round_rate then upstream parent propagation is ignored. If + * *parent_rate comes back with a new rate for clk's parent then we propagate + * up to clk's parent and set its rate. Upward propagation will continue + * until either a clk does not support the CLK_SET_RATE_PARENT flag or + * .round_rate stops requesting changes to clk's parent_rate. + * + * Rate changes are accomplished via tree traversal that also recalculates the + * rates for the clocks and fires off POST_RATE_CHANGE notifiers. + * + * Returns 0 on success, -EERROR otherwise. */ -unsigned long clk_get_rate(struct clk *clk) +int clk_set_rate(struct clk *clk, unsigned long rate) { + int ret; + if (!clk) return 0; - return clk_core_get_rate(clk->core); -} -EXPORT_SYMBOL_GPL(clk_get_rate); - -static int clk_fetch_parent_index(struct clk_core *core, - struct clk_core *parent) -{ - int i; - - if (!core->parents) { - core->parents = kcalloc(core->num_parents, - sizeof(struct clk *), GFP_KERNEL); - if (!core->parents) - return -ENOMEM; - } - - /* - * find index of new parent clock using cached parent ptrs, - * or if not yet cached, use string name comparison and cache - * them now to avoid future calls to clk_core_lookup. - */ - for (i = 0; i < core->num_parents; i++) { - if (core->parents[i] == parent) - return i; + /* prevent racing with updates to the clock topology */ + clk_prepare_lock(); - if (core->parents[i]) - continue; + ret = clk_core_set_rate_nolock(clk->core, rate); - if (!strcmp(core->parent_names[i], parent->name)) { - core->parents[i] = clk_core_lookup(parent->name); - return i; - } - } + clk_prepare_unlock(); - return -EINVAL; + return ret; } +EXPORT_SYMBOL_GPL(clk_set_rate); -static void clk_reparent(struct clk_core *core, struct clk_core *new_parent) +/** + * clk_set_rate_range - set a rate range for a clock source + * @clk: clock source + * @min: desired minimum clock rate in Hz, inclusive + * @max: desired maximum clock rate in Hz, inclusive + * + * Returns success (0) or negative errno. + */ +int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max) { - hlist_del(&core->child_node); + int ret = 0; - if (new_parent) { - /* avoid duplicate POST_RATE_CHANGE notifications */ - if (new_parent->new_child == core) - new_parent->new_child = NULL; + if (!clk) + return 0; - hlist_add_head(&core->child_node, &new_parent->children); - } else { - hlist_add_head(&core->child_node, &clk_orphan_list); + if (min > max) { + pr_err("%s: clk %s dev %s con %s: invalid range [%lu, %lu]\n", + __func__, clk->core->name, clk->dev_id, clk->con_id, + min, max); + return -EINVAL; } - core->parent = new_parent; -} - -static struct clk_core *__clk_set_parent_before(struct clk_core *core, - struct clk_core *parent) -{ - unsigned long flags; - struct clk_core *old_parent = core->parent; + clk_prepare_lock(); - /* - * Migrate prepare state between parents and prevent race with - * clk_enable(). - * - * If the clock is not prepared, then a race with - * clk_enable/disable() is impossible since we already have the - * prepare lock (future calls to clk_enable() need to be preceded by - * a clk_prepare()). - * - * If the clock is prepared, migrate the prepared state to the new - * parent and also protect against a race with clk_enable() by - * forcing the clock and the new parent on. This ensures that all - * future calls to clk_enable() are practically NOPs with respect to - * hardware and software states. - * - * See also: Comment for clk_set_parent() below. - */ - if (core->prepare_count) { - clk_core_prepare(parent); - clk_core_enable(parent); - clk_core_enable(core); + if (min != clk->min_rate || max != clk->max_rate) { + clk->min_rate = min; + clk->max_rate = max; + ret = clk_core_set_rate_nolock(clk->core, clk->core->req_rate); } - /* update the clk tree topology */ - flags = clk_enable_lock(); - clk_reparent(core, parent); - clk_enable_unlock(flags); + clk_prepare_unlock(); - return old_parent; + return ret; } +EXPORT_SYMBOL_GPL(clk_set_rate_range); -static void __clk_set_parent_after(struct clk_core *core, - struct clk_core *parent, - struct clk_core *old_parent) +/** + * clk_set_min_rate - set a minimum clock rate for a clock source + * @clk: clock source + * @rate: desired minimum clock rate in Hz, inclusive + * + * Returns success (0) or negative errno. + */ +int clk_set_min_rate(struct clk *clk, unsigned long rate) { - /* - * Finish the migration of prepare state and undo the changes done - * for preventing a race with clk_enable(). - */ - if (core->prepare_count) { - clk_core_disable(core); - clk_core_disable(old_parent); - clk_core_unprepare(old_parent); - } + if (!clk) + return 0; + + return clk_set_rate_range(clk, rate, clk->max_rate); } +EXPORT_SYMBOL_GPL(clk_set_min_rate); -static int __clk_set_parent(struct clk_core *core, struct clk_core *parent, - u8 p_index) +/** + * clk_set_max_rate - set a maximum clock rate for a clock source + * @clk: clock source + * @rate: desired maximum clock rate in Hz, inclusive + * + * Returns success (0) or negative errno. + */ +int clk_set_max_rate(struct clk *clk, unsigned long rate) { - unsigned long flags; - int ret = 0; - struct clk_core *old_parent; - - old_parent = __clk_set_parent_before(core, parent); - - trace_clk_set_parent(core, parent); - - /* change clock input source */ - if (parent && core->ops->set_parent) - ret = core->ops->set_parent(core->hw, p_index); - - trace_clk_set_parent_complete(core, parent); - - if (ret) { - flags = clk_enable_lock(); - clk_reparent(core, old_parent); - clk_enable_unlock(flags); - - if (core->prepare_count) { - clk_core_disable(core); - clk_core_disable(parent); - clk_core_unprepare(parent); - } - return ret; - } - - __clk_set_parent_after(core, parent, old_parent); + if (!clk) + return 0; - return 0; + return clk_set_rate_range(clk, clk->min_rate, rate); } +EXPORT_SYMBOL_GPL(clk_set_max_rate); /** - * __clk_speculate_rates - * @core: first clk in the subtree - * @parent_rate: the "future" rate of clk's parent - * - * Walks the subtree of clks starting with clk, speculating rates as it - * goes and firing off PRE_RATE_CHANGE notifications as necessary. - * - * Unlike clk_recalc_rates, clk_speculate_rates exists only for sending - * pre-rate change notifications and returns early if no clks in the - * subtree have subscribed to the notifications. Note that if a clk does not - * implement the .recalc_rate callback then it is assumed that the clock will - * take on the rate of its parent. + * clk_get_parent - return the parent of a clk + * @clk: the clk whose parent gets returned * - * Caller must hold prepare_lock. + * Simply returns clk->parent. Returns NULL if clk is NULL. */ -static int __clk_speculate_rates(struct clk_core *core, - unsigned long parent_rate) +struct clk *clk_get_parent(struct clk *clk) { - struct clk_core *child; - unsigned long new_rate; - int ret = NOTIFY_DONE; + struct clk *parent; - lockdep_assert_held(&prepare_lock); + clk_prepare_lock(); + parent = __clk_get_parent(clk); + clk_prepare_unlock(); - new_rate = clk_recalc(core, parent_rate); + return parent; +} +EXPORT_SYMBOL_GPL(clk_get_parent); - /* abort rate change if a driver returns NOTIFY_BAD or NOTIFY_STOP */ - if (core->notifier_count) - ret = __clk_notify(core, PRE_RATE_CHANGE, core->rate, new_rate); +/* + * .get_parent is mandatory for clocks with multiple possible parents. It is + * optional for single-parent clocks. Always call .get_parent if it is + * available and WARN if it is missing for multi-parent clocks. + * + * For single-parent clocks without .get_parent, first check to see if the + * .parents array exists, and if so use it to avoid an expensive tree + * traversal. If .parents does not exist then walk the tree. + */ +static struct clk_core *__clk_init_parent(struct clk_core *core) +{ + struct clk_core *ret = NULL; + u8 index; - if (ret & NOTIFY_STOP_MASK) { - pr_debug("%s: clk notifier callback for clock %s aborted with error %d\n", - __func__, core->name, ret); + /* handle the trivial cases */ + + if (!core->num_parents) goto out; - } - hlist_for_each_entry(child, &core->children, child_node) { - ret = __clk_speculate_rates(child, new_rate); - if (ret & NOTIFY_STOP_MASK) - break; + if (core->num_parents == 1) { + if (IS_ERR_OR_NULL(core->parent)) + core->parent = clk_core_lookup(core->parent_names[0]); + ret = core->parent; + goto out; } + if (!core->ops->get_parent) { + WARN(!core->ops->get_parent, + "%s: multi-parent clocks must implement .get_parent\n", + __func__); + goto out; + }; + + /* + * Do our best to cache parent clocks in core->parents. This prevents + * unnecessary and expensive lookups. We don't set core->parent here; + * that is done by the calling function. + */ + + index = core->ops->get_parent(core->hw); + + if (!core->parents) + core->parents = + kcalloc(core->num_parents, sizeof(struct clk *), + GFP_KERNEL); + + ret = clk_core_get_parent_by_index(core, index); + out: return ret; } -static void clk_calc_subtree(struct clk_core *core, unsigned long new_rate, - struct clk_core *new_parent, u8 p_index) +static void clk_core_reparent(struct clk_core *core, + struct clk_core *new_parent) { - struct clk_core *child; - - core->new_rate = new_rate; - core->new_parent = new_parent; - core->new_parent_index = p_index; - /* include clk in new parent's PRE_RATE_CHANGE notifications */ - core->new_child = NULL; - if (new_parent && new_parent != core->parent) - new_parent->new_child = core; - - hlist_for_each_entry(child, &core->children, child_node) { - child->new_rate = clk_recalc(child, new_rate); - clk_calc_subtree(child, child->new_rate, NULL, 0); - } + clk_reparent(core, new_parent); + __clk_recalc_accuracies(core); + __clk_recalc_rates(core, POST_RATE_CHANGE); } -/* - * calculate the new rates returning the topmost clock that has to be - * changed. +/** + * clk_has_parent - check if a clock is a possible parent for another + * @clk: clock source + * @parent: parent clock source + * + * This function can be used in drivers that need to check that a clock can be + * the parent of another without actually changing the parent. + * + * Returns true if @parent is a possible parent for @clk, false otherwise. */ -static struct clk_core *clk_calc_new_rates(struct clk_core *core, - unsigned long rate) +bool clk_has_parent(struct clk *clk, struct clk *parent) { - struct clk_core *top = core; - struct clk_core *old_parent, *parent; - struct clk_hw *parent_hw; - unsigned long best_parent_rate = 0; - unsigned long new_rate; - unsigned long min_rate; - unsigned long max_rate; - int p_index = 0; - long ret; + struct clk_core *core, *parent_core; + unsigned int i; - /* sanity */ - if (IS_ERR_OR_NULL(core)) - return NULL; + /* NULL clocks should be nops, so return success if either is NULL. */ + if (!clk || !parent) + return true; - /* save parent rate, if it exists */ - parent = old_parent = core->parent; - if (parent) - best_parent_rate = parent->rate; + core = clk->core; + parent_core = parent->core; - clk_core_get_boundaries(core, &min_rate, &max_rate); + /* Optimize for the case where the parent is already the parent. */ + if (core->parent == parent_core) + return true; - /* find the closest rate and parent clk/rate */ - if (core->ops->determine_rate) { - parent_hw = parent ? parent->hw : NULL; - ret = core->ops->determine_rate(core->hw, rate, - min_rate, - max_rate, - &best_parent_rate, - &parent_hw); - if (ret < 0) - return NULL; + for (i = 0; i < core->num_parents; i++) + if (strcmp(core->parent_names[i], parent_core->name) == 0) + return true; - new_rate = ret; - parent = parent_hw ? parent_hw->core : NULL; - } else if (core->ops->round_rate) { - ret = core->ops->round_rate(core->hw, rate, - &best_parent_rate); - if (ret < 0) - return NULL; + return false; +} +EXPORT_SYMBOL_GPL(clk_has_parent); - new_rate = ret; - if (new_rate < min_rate || new_rate > max_rate) - return NULL; - } else if (!parent || !(core->flags & CLK_SET_RATE_PARENT)) { - /* pass-through clock without adjustable parent */ - core->new_rate = core->rate; - return NULL; - } else { - /* pass-through clock with adjustable parent */ - top = clk_calc_new_rates(parent, rate); - new_rate = parent->new_rate; +static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent) +{ + int ret = 0; + int p_index = 0; + unsigned long p_rate = 0; + + if (!core) + return 0; + + /* prevent racing with updates to the clock topology */ + clk_prepare_lock(); + + if (core->parent == parent) + goto out; + + /* verify ops for for multi-parent clks */ + if ((core->num_parents > 1) && (!core->ops->set_parent)) { + ret = -ENOSYS; goto out; } - /* some clocks must be gated to change parent */ - if (parent != old_parent && - (core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) { - pr_debug("%s: %s not gated but wants to reparent\n", - __func__, core->name); - return NULL; + /* check that we are allowed to re-parent if the clock is in use */ + if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) { + ret = -EBUSY; + goto out; } /* try finding the new parent index */ - if (parent && core->num_parents > 1) { + if (parent) { p_index = clk_fetch_parent_index(core, parent); + p_rate = parent->rate; if (p_index < 0) { pr_debug("%s: clk %s can not be parent of clk %s\n", - __func__, parent->name, core->name); - return NULL; + __func__, parent->name, core->name); + ret = p_index; + goto out; } } - if ((core->flags & CLK_SET_RATE_PARENT) && parent && - best_parent_rate != parent->rate) - top = clk_calc_new_rates(parent, best_parent_rate); - -out: - clk_calc_subtree(core, new_rate, parent, p_index); - - return top; -} + /* propagate PRE_RATE_CHANGE notifications */ + ret = __clk_speculate_rates(core, p_rate); -/* - * Notify about rate changes in a subtree. Always walk down the whole tree - * so that in case of an error we can walk down the whole tree again and - * abort the change. - */ -static struct clk_core *clk_propagate_rate_change(struct clk_core *core, - unsigned long event) -{ - struct clk_core *child, *tmp_clk, *fail_clk = NULL; - int ret = NOTIFY_DONE; + /* abort if a driver objects */ + if (ret & NOTIFY_STOP_MASK) + goto out; - if (core->rate == core->new_rate) - return NULL; + /* do the re-parent */ + ret = __clk_set_parent(core, parent, p_index); - if (core->notifier_count) { - ret = __clk_notify(core, event, core->rate, core->new_rate); - if (ret & NOTIFY_STOP_MASK) - fail_clk = core; + /* propagate rate an accuracy recalculation accordingly */ + if (ret) { + __clk_recalc_rates(core, ABORT_RATE_CHANGE); + } else { + __clk_recalc_rates(core, POST_RATE_CHANGE); + __clk_recalc_accuracies(core); } - hlist_for_each_entry(child, &core->children, child_node) { - /* Skip children who will be reparented to another clock */ - if (child->new_parent && child->new_parent != core) - continue; - tmp_clk = clk_propagate_rate_change(child, event); - if (tmp_clk) - fail_clk = tmp_clk; - } +out: + clk_prepare_unlock(); - /* handle the new child who might not be in core->children yet */ - if (core->new_child) { - tmp_clk = clk_propagate_rate_change(core->new_child, event); - if (tmp_clk) - fail_clk = tmp_clk; - } + return ret; +} - return fail_clk; +/** + * 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 its new input source. If clk is in + * prepared state, the clk will get enabled for the duration of this call. If + * that's not acceptable for a specific clk (Eg: the consumer can't handle + * that, the reparenting is glitchy in hardware, etc), use the + * CLK_SET_PARENT_GATE flag to allow reparenting only when clk is unprepared. + * + * 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) +{ + if (!clk) + return 0; + + return clk_core_set_parent(clk->core, parent ? parent->core : NULL); } +EXPORT_SYMBOL_GPL(clk_set_parent); -/* - * walk down a subtree and set the new rates notifying the rate - * change on the way +/** + * clk_set_phase - adjust the phase shift of a clock signal + * @clk: clock signal source + * @degrees: number of degrees the signal is shifted + * + * Shifts the phase of a clock signal by the specified + * degrees. Returns 0 on success, -EERROR otherwise. + * + * This function makes no distinction about the input or reference + * signal that we adjust the clock signal phase against. For example + * phase locked-loop clock signal generators we may shift phase with + * respect to feedback clock signal input, but for other cases the + * clock phase may be shifted with respect to some other, unspecified + * signal. + * + * Additionally the concept of phase shift does not propagate through + * the clock tree hierarchy, which sets it apart from clock rates and + * clock accuracy. A parent clock phase attribute does not have an + * impact on the phase attribute of a child clock. */ -static void clk_change_rate(struct clk_core *core) +int clk_set_phase(struct clk *clk, int degrees) { - struct clk_core *child; - struct hlist_node *tmp; - unsigned long old_rate; - unsigned long best_parent_rate = 0; - bool skip_set_rate = false; - struct clk_core *old_parent; + int ret = -EINVAL; - old_rate = core->rate; + if (!clk) + return 0; - if (core->new_parent) - best_parent_rate = core->new_parent->rate; - else if (core->parent) - best_parent_rate = core->parent->rate; + /* sanity check degrees */ + degrees %= 360; + if (degrees < 0) + degrees += 360; - if (core->new_parent && core->new_parent != core->parent) { - old_parent = __clk_set_parent_before(core, core->new_parent); - trace_clk_set_parent(core, core->new_parent); + clk_prepare_lock(); - if (core->ops->set_rate_and_parent) { - skip_set_rate = true; - core->ops->set_rate_and_parent(core->hw, core->new_rate, - best_parent_rate, - core->new_parent_index); - } else if (core->ops->set_parent) { - core->ops->set_parent(core->hw, core->new_parent_index); - } + trace_clk_set_phase(clk->core, degrees); - trace_clk_set_parent_complete(core, core->new_parent); - __clk_set_parent_after(core, core->new_parent, old_parent); - } + if (clk->core->ops->set_phase) + ret = clk->core->ops->set_phase(clk->core->hw, degrees); - trace_clk_set_rate(core, core->new_rate); + trace_clk_set_phase_complete(clk->core, degrees); - if (!skip_set_rate && core->ops->set_rate) - core->ops->set_rate(core->hw, core->new_rate, best_parent_rate); + if (!ret) + clk->core->phase = degrees; - trace_clk_set_rate_complete(core, core->new_rate); + clk_prepare_unlock(); - core->rate = clk_recalc(core, best_parent_rate); + return ret; +} +EXPORT_SYMBOL_GPL(clk_set_phase); - if (core->notifier_count && old_rate != core->rate) - __clk_notify(core, POST_RATE_CHANGE, old_rate, core->rate); +static int clk_core_get_phase(struct clk_core *core) +{ + int ret; - /* - * Use safe iteration, as change_rate can actually swap parents - * for certain clock types. - */ - hlist_for_each_entry_safe(child, tmp, &core->children, child_node) { - /* Skip children who will be reparented to another clock */ - if (child->new_parent && child->new_parent != core) - continue; - clk_change_rate(child); - } + clk_prepare_lock(); + ret = core->phase; + clk_prepare_unlock(); - /* handle the new child who might not be in core->children yet */ - if (core->new_child) - clk_change_rate(core->new_child); + return ret; } -static int clk_core_set_rate_nolock(struct clk_core *core, - unsigned long req_rate) +/** + * clk_get_phase - return the phase shift of a clock signal + * @clk: clock signal source + * + * Returns the phase shift of a clock node in degrees, otherwise returns + * -EERROR. + */ +int clk_get_phase(struct clk *clk) { - struct clk_core *top, *fail_clk; - unsigned long rate = req_rate; - int ret = 0; - - if (!core) + if (!clk) return 0; - /* bail early if nothing to do */ - if (rate == clk_core_get_rate_nolock(core)) - return 0; + return clk_core_get_phase(clk->core); +} +EXPORT_SYMBOL_GPL(clk_get_phase); - if ((core->flags & CLK_SET_RATE_GATE) && core->prepare_count) - return -EBUSY; +/** + * clk_is_match - check if two clk's point to the same hardware clock + * @p: clk compared against q + * @q: clk compared against p + * + * Returns true if the two struct clk pointers both point to the same hardware + * clock node. Put differently, returns true if struct clk *p and struct clk *q + * share the same struct clk_core object. + * + * Returns false otherwise. Note that two NULL clks are treated as matching. + */ +bool clk_is_match(const struct clk *p, const struct clk *q) +{ + /* trivial case: identical struct clk's or both NULL */ + if (p == q) + return true; - /* calculate new rates and get the topmost changed clock */ - top = clk_calc_new_rates(core, rate); - if (!top) - return -EINVAL; + /* true if clk->core pointers match. Avoid derefing garbage */ + if (!IS_ERR_OR_NULL(p) && !IS_ERR_OR_NULL(q)) + if (p->core == q->core) + return true; - /* notify that we are about to change rates */ - fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE); - if (fail_clk) { - pr_debug("%s: failed to set %s rate\n", __func__, - fail_clk->name); - clk_propagate_rate_change(top, ABORT_RATE_CHANGE); - return -EBUSY; - } + return false; +} +EXPORT_SYMBOL_GPL(clk_is_match); - /* change the rates */ - clk_change_rate(top); +/*** debugfs support ***/ - core->req_rate = req_rate; +#ifdef CONFIG_DEBUG_FS +#include - return ret; -} +static struct dentry *rootdir; +static int inited = 0; +static DEFINE_MUTEX(clk_debug_lock); +static HLIST_HEAD(clk_debug_list); -/** - * clk_set_rate - specify a new rate for clk - * @clk: the clk whose rate is being changed - * @rate: the new rate for clk - * - * In the simplest case clk_set_rate will only adjust the rate of clk. - * - * Setting the CLK_SET_RATE_PARENT flag allows the rate change operation to - * propagate up to clk's parent; whether or not this happens depends on the - * outcome of clk's .round_rate implementation. If *parent_rate is unchanged - * after calling .round_rate then upstream parent propagation is ignored. If - * *parent_rate comes back with a new rate for clk's parent then we propagate - * up to clk's parent and set its rate. Upward propagation will continue - * until either a clk does not support the CLK_SET_RATE_PARENT flag or - * .round_rate stops requesting changes to clk's parent_rate. - * - * Rate changes are accomplished via tree traversal that also recalculates the - * rates for the clocks and fires off POST_RATE_CHANGE notifiers. - * - * Returns 0 on success, -EERROR otherwise. - */ -int clk_set_rate(struct clk *clk, unsigned long rate) +static struct hlist_head *all_lists[] = { + &clk_root_list, + &clk_orphan_list, + NULL, +}; + +static struct hlist_head *orphan_list[] = { + &clk_orphan_list, + NULL, +}; + +static void clk_summary_show_one(struct seq_file *s, struct clk_core *c, + int level) { - int ret; + if (!c) + return; - if (!clk) - return 0; + seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu %-3d\n", + level * 3 + 1, "", + 30 - level * 3, c->name, + c->enable_count, c->prepare_count, clk_core_get_rate(c), + clk_core_get_accuracy(c), clk_core_get_phase(c)); +} - /* prevent racing with updates to the clock topology */ - clk_prepare_lock(); +static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c, + int level) +{ + struct clk_core *child; - ret = clk_core_set_rate_nolock(clk->core, rate); + if (!c) + return; - clk_prepare_unlock(); + clk_summary_show_one(s, c, level); - return ret; + hlist_for_each_entry(child, &c->children, child_node) + clk_summary_show_subtree(s, child, level + 1); } -EXPORT_SYMBOL_GPL(clk_set_rate); -/** - * clk_set_rate_range - set a rate range for a clock source - * @clk: clock source - * @min: desired minimum clock rate in Hz, inclusive - * @max: desired maximum clock rate in Hz, inclusive - * - * Returns success (0) or negative errno. - */ -int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max) +static int clk_summary_show(struct seq_file *s, void *data) { - int ret = 0; - - if (!clk) - return 0; + struct clk_core *c; + struct hlist_head **lists = (struct hlist_head **)s->private; - if (min > max) { - pr_err("%s: clk %s dev %s con %s: invalid range [%lu, %lu]\n", - __func__, clk->core->name, clk->dev_id, clk->con_id, - min, max); - return -EINVAL; - } + seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n"); + seq_puts(s, "----------------------------------------------------------------------------------------\n"); clk_prepare_lock(); - if (min != clk->min_rate || max != clk->max_rate) { - clk->min_rate = min; - clk->max_rate = max; - ret = clk_core_set_rate_nolock(clk->core, clk->core->req_rate); - } + for (; *lists; lists++) + hlist_for_each_entry(c, *lists, child_node) + clk_summary_show_subtree(s, c, 0); clk_prepare_unlock(); - return ret; + return 0; } -EXPORT_SYMBOL_GPL(clk_set_rate_range); - -/** - * clk_set_min_rate - set a minimum clock rate for a clock source - * @clk: clock source - * @rate: desired minimum clock rate in Hz, inclusive - * - * Returns success (0) or negative errno. - */ -int clk_set_min_rate(struct clk *clk, unsigned long rate) -{ - if (!clk) - return 0; - return clk_set_rate_range(clk, rate, clk->max_rate); -} -EXPORT_SYMBOL_GPL(clk_set_min_rate); -/** - * clk_set_max_rate - set a maximum clock rate for a clock source - * @clk: clock source - * @rate: desired maximum clock rate in Hz, inclusive - * - * Returns success (0) or negative errno. - */ -int clk_set_max_rate(struct clk *clk, unsigned long rate) +static int clk_summary_open(struct inode *inode, struct file *file) { - if (!clk) - return 0; - - return clk_set_rate_range(clk, clk->min_rate, rate); + return single_open(file, clk_summary_show, inode->i_private); } -EXPORT_SYMBOL_GPL(clk_set_max_rate); -/** - * clk_get_parent - return the parent of a clk - * @clk: the clk whose parent gets returned - * - * Simply returns clk->parent. Returns NULL if clk is NULL. - */ -struct clk *clk_get_parent(struct clk *clk) -{ - struct clk *parent; +static const struct file_operations clk_summary_fops = { + .open = clk_summary_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - clk_prepare_lock(); - parent = __clk_get_parent(clk); - clk_prepare_unlock(); +static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level) +{ + if (!c) + return; - return parent; + seq_printf(s, "\"%s\": { ", c->name); + seq_printf(s, "\"enable_count\": %d,", c->enable_count); + seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); + seq_printf(s, "\"rate\": %lu", clk_core_get_rate(c)); + seq_printf(s, "\"accuracy\": %lu", clk_core_get_accuracy(c)); + seq_printf(s, "\"phase\": %d", clk_core_get_phase(c)); } -EXPORT_SYMBOL_GPL(clk_get_parent); -/* - * .get_parent is mandatory for clocks with multiple possible parents. It is - * optional for single-parent clocks. Always call .get_parent if it is - * available and WARN if it is missing for multi-parent clocks. - * - * For single-parent clocks without .get_parent, first check to see if the - * .parents array exists, and if so use it to avoid an expensive tree - * traversal. If .parents does not exist then walk the tree. - */ -static struct clk_core *__clk_init_parent(struct clk_core *core) +static void clk_dump_subtree(struct seq_file *s, struct clk_core *c, int level) { - struct clk_core *ret = NULL; - u8 index; + struct clk_core *child; - /* handle the trivial cases */ + if (!c) + return; - if (!core->num_parents) - goto out; + clk_dump_one(s, c, level); - if (core->num_parents == 1) { - if (IS_ERR_OR_NULL(core->parent)) - core->parent = clk_core_lookup(core->parent_names[0]); - ret = core->parent; - goto out; + hlist_for_each_entry(child, &c->children, child_node) { + seq_printf(s, ","); + clk_dump_subtree(s, child, level + 1); } - if (!core->ops->get_parent) { - WARN(!core->ops->get_parent, - "%s: multi-parent clocks must implement .get_parent\n", - __func__); - goto out; - }; - - /* - * Do our best to cache parent clocks in core->parents. This prevents - * unnecessary and expensive lookups. We don't set core->parent here; - * that is done by the calling function. - */ - - index = core->ops->get_parent(core->hw); - - if (!core->parents) - core->parents = - kcalloc(core->num_parents, sizeof(struct clk *), - GFP_KERNEL); - - ret = clk_core_get_parent_by_index(core, index); - -out: - return ret; -} - -static void clk_core_reparent(struct clk_core *core, - struct clk_core *new_parent) -{ - clk_reparent(core, new_parent); - __clk_recalc_accuracies(core); - __clk_recalc_rates(core, POST_RATE_CHANGE); + seq_printf(s, "}"); } -/** - * clk_has_parent - check if a clock is a possible parent for another - * @clk: clock source - * @parent: parent clock source - * - * This function can be used in drivers that need to check that a clock can be - * the parent of another without actually changing the parent. - * - * Returns true if @parent is a possible parent for @clk, false otherwise. - */ -bool clk_has_parent(struct clk *clk, struct clk *parent) +static int clk_dump(struct seq_file *s, void *data) { - struct clk_core *core, *parent_core; - unsigned int i; + struct clk_core *c; + bool first_node = true; + struct hlist_head **lists = (struct hlist_head **)s->private; - /* NULL clocks should be nops, so return success if either is NULL. */ - if (!clk || !parent) - return true; + seq_printf(s, "{"); - core = clk->core; - parent_core = parent->core; + clk_prepare_lock(); - /* Optimize for the case where the parent is already the parent. */ - if (core->parent == parent_core) - return true; + for (; *lists; lists++) { + hlist_for_each_entry(c, *lists, child_node) { + if (!first_node) + seq_puts(s, ","); + first_node = false; + clk_dump_subtree(s, c, 0); + } + } - for (i = 0; i < core->num_parents; i++) - if (strcmp(core->parent_names[i], parent_core->name) == 0) - return true; + clk_prepare_unlock(); - return false; + seq_printf(s, "}"); + return 0; } -EXPORT_SYMBOL_GPL(clk_has_parent); -static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent) + +static int clk_dump_open(struct inode *inode, struct file *file) { - int ret = 0; - int p_index = 0; - unsigned long p_rate = 0; + return single_open(file, clk_dump, inode->i_private); +} - if (!core) - return 0; +static const struct file_operations clk_dump_fops = { + .open = clk_dump_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; - /* prevent racing with updates to the clock topology */ - clk_prepare_lock(); +static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) +{ + struct dentry *d; + int ret = -ENOMEM; - if (core->parent == parent) + if (!core || !pdentry) { + ret = -EINVAL; goto out; + } - /* verify ops for for multi-parent clks */ - if ((core->num_parents > 1) && (!core->ops->set_parent)) { - ret = -ENOSYS; + d = debugfs_create_dir(core->name, pdentry); + if (!d) goto out; - } - /* check that we are allowed to re-parent if the clock is in use */ - if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) { - ret = -EBUSY; - goto out; - } + core->dentry = d; + + d = debugfs_create_u32("clk_rate", S_IRUGO, core->dentry, + (u32 *)&core->rate); + if (!d) + goto err_out; + + d = debugfs_create_u32("clk_accuracy", S_IRUGO, core->dentry, + (u32 *)&core->accuracy); + if (!d) + goto err_out; + + d = debugfs_create_u32("clk_phase", S_IRUGO, core->dentry, + (u32 *)&core->phase); + if (!d) + goto err_out; - /* try finding the new parent index */ - if (parent) { - p_index = clk_fetch_parent_index(core, parent); - p_rate = parent->rate; - if (p_index < 0) { - pr_debug("%s: clk %s can not be parent of clk %s\n", - __func__, parent->name, core->name); - ret = p_index; - goto out; - } - } + d = debugfs_create_x32("clk_flags", S_IRUGO, core->dentry, + (u32 *)&core->flags); + if (!d) + goto err_out; - /* propagate PRE_RATE_CHANGE notifications */ - ret = __clk_speculate_rates(core, p_rate); + d = debugfs_create_u32("clk_prepare_count", S_IRUGO, core->dentry, + (u32 *)&core->prepare_count); + if (!d) + goto err_out; - /* abort if a driver objects */ - if (ret & NOTIFY_STOP_MASK) - goto out; + d = debugfs_create_u32("clk_enable_count", S_IRUGO, core->dentry, + (u32 *)&core->enable_count); + if (!d) + goto err_out; - /* do the re-parent */ - ret = __clk_set_parent(core, parent, p_index); + d = debugfs_create_u32("clk_notifier_count", S_IRUGO, core->dentry, + (u32 *)&core->notifier_count); + if (!d) + goto err_out; - /* propagate rate an accuracy recalculation accordingly */ - if (ret) { - __clk_recalc_rates(core, ABORT_RATE_CHANGE); - } else { - __clk_recalc_rates(core, POST_RATE_CHANGE); - __clk_recalc_accuracies(core); + if (core->ops->debug_init) { + ret = core->ops->debug_init(core->hw, core->dentry); + if (ret) + goto err_out; } -out: - clk_prepare_unlock(); + ret = 0; + goto out; +err_out: + debugfs_remove_recursive(core->dentry); + core->dentry = NULL; +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 its new input source. If clk is in - * prepared state, the clk will get enabled for the duration of this call. If - * that's not acceptable for a specific clk (Eg: the consumer can't handle - * that, the reparenting is glitchy in hardware, etc), use the - * CLK_SET_PARENT_GATE flag to allow reparenting only when clk is unprepared. - * - * After successfully changing clk's parent clk_set_parent will update the - * clk topology, sysfs topology and propagate rate recalculation via - * __clk_recalc_rates. + * clk_debug_register - add a clk node to the debugfs clk tree + * @core: the clk being added to the debugfs clk tree * - * Returns 0 on success, -EERROR otherwise. + * Dynamically adds a clk to the debugfs clk tree if debugfs has been + * initialized. Otherwise it bails out early since the debugfs clk tree + * will be created lazily by clk_debug_init as part of a late_initcall. */ -int clk_set_parent(struct clk *clk, struct clk *parent) +static int clk_debug_register(struct clk_core *core) { - if (!clk) - return 0; + int ret = 0; - return clk_core_set_parent(clk->core, parent ? parent->core : NULL); + mutex_lock(&clk_debug_lock); + hlist_add_head(&core->debug_node, &clk_debug_list); + + if (!inited) + goto unlock; + + ret = clk_debug_create_one(core, rootdir); +unlock: + mutex_unlock(&clk_debug_lock); + + return ret; } -EXPORT_SYMBOL_GPL(clk_set_parent); -/** - * clk_set_phase - adjust the phase shift of a clock signal - * @clk: clock signal source - * @degrees: number of degrees the signal is shifted - * - * Shifts the phase of a clock signal by the specified - * degrees. Returns 0 on success, -EERROR otherwise. - * - * This function makes no distinction about the input or reference - * signal that we adjust the clock signal phase against. For example - * phase locked-loop clock signal generators we may shift phase with - * respect to feedback clock signal input, but for other cases the - * clock phase may be shifted with respect to some other, unspecified - * signal. + /** + * clk_debug_unregister - remove a clk node from the debugfs clk tree + * @core: the clk being removed from the debugfs clk tree * - * Additionally the concept of phase shift does not propagate through - * the clock tree hierarchy, which sets it apart from clock rates and - * clock accuracy. A parent clock phase attribute does not have an - * impact on the phase attribute of a child clock. + * Dynamically removes a clk and all it's children clk nodes from the + * debugfs clk tree if clk->dentry points to debugfs created by + * clk_debug_register in __clk_init. */ -int clk_set_phase(struct clk *clk, int degrees) +static void clk_debug_unregister(struct clk_core *core) { - int ret = -EINVAL; + mutex_lock(&clk_debug_lock); + hlist_del_init(&core->debug_node); + debugfs_remove_recursive(core->dentry); + core->dentry = NULL; + mutex_unlock(&clk_debug_lock); +} - if (!clk) - return 0; +struct dentry *clk_debugfs_add_file(struct clk_hw *hw, char *name, umode_t mode, + void *data, const struct file_operations *fops) +{ + struct dentry *d = NULL; - /* sanity check degrees */ - degrees %= 360; - if (degrees < 0) - degrees += 360; + if (hw->core->dentry) + d = debugfs_create_file(name, mode, hw->core->dentry, data, + fops); - clk_prepare_lock(); + return d; +} +EXPORT_SYMBOL_GPL(clk_debugfs_add_file); - trace_clk_set_phase(clk->core, degrees); +/** + * clk_debug_init - lazily create the debugfs clk tree visualization + * + * clks are often initialized very early during boot before memory can + * be dynamically allocated and well before debugfs is setup. + * clk_debug_init walks the clk tree hierarchy while holding + * prepare_lock and creates the topology as part of a late_initcall, + * thus insuring that clks initialized very early will still be + * represented in the debugfs clk tree. This function should only be + * called once at boot-time, and all other clks added dynamically will + * be done so with clk_debug_register. + */ +static int __init clk_debug_init(void) +{ + struct clk_core *core; + struct dentry *d; - if (clk->core->ops->set_phase) - ret = clk->core->ops->set_phase(clk->core->hw, degrees); + rootdir = debugfs_create_dir("clk", NULL); - trace_clk_set_phase_complete(clk->core, degrees); + if (!rootdir) + return -ENOMEM; - if (!ret) - clk->core->phase = degrees; + d = debugfs_create_file("clk_summary", S_IRUGO, rootdir, &all_lists, + &clk_summary_fops); + if (!d) + return -ENOMEM; - clk_prepare_unlock(); + d = debugfs_create_file("clk_dump", S_IRUGO, rootdir, &all_lists, + &clk_dump_fops); + if (!d) + return -ENOMEM; - return ret; -} -EXPORT_SYMBOL_GPL(clk_set_phase); + d = debugfs_create_file("clk_orphan_summary", S_IRUGO, rootdir, + &orphan_list, &clk_summary_fops); + if (!d) + return -ENOMEM; -static int clk_core_get_phase(struct clk_core *core) -{ - int ret; + d = debugfs_create_file("clk_orphan_dump", S_IRUGO, rootdir, + &orphan_list, &clk_dump_fops); + if (!d) + return -ENOMEM; - clk_prepare_lock(); - ret = core->phase; - clk_prepare_unlock(); + mutex_lock(&clk_debug_lock); + hlist_for_each_entry(core, &clk_debug_list, debug_node) + clk_debug_create_one(core, rootdir); - return ret; -} -EXPORT_SYMBOL_GPL(clk_get_phase); + inited = 1; + mutex_unlock(&clk_debug_lock); -/** - * clk_get_phase - return the phase shift of a clock signal - * @clk: clock signal source - * - * Returns the phase shift of a clock node in degrees, otherwise returns - * -EERROR. - */ -int clk_get_phase(struct clk *clk) + return 0; +} +late_initcall(clk_debug_init); +#else +static inline int clk_debug_register(struct clk_core *core) { return 0; } +static inline void clk_debug_reparent(struct clk_core *core, + struct clk_core *new_parent) { - if (!clk) - return 0; - - return clk_core_get_phase(clk->core); } - -/** - * clk_is_match - check if two clk's point to the same hardware clock - * @p: clk compared against q - * @q: clk compared against p - * - * Returns true if the two struct clk pointers both point to the same hardware - * clock node. Put differently, returns true if struct clk *p and struct clk *q - * share the same struct clk_core object. - * - * Returns false otherwise. Note that two NULL clks are treated as matching. - */ -bool clk_is_match(const struct clk *p, const struct clk *q) +static inline void clk_debug_unregister(struct clk_core *core) { - /* trivial case: identical struct clk's or both NULL */ - if (p == q) - return true; - - /* true if clk->core pointers match. Avoid derefing garbage */ - if (!IS_ERR_OR_NULL(p) && !IS_ERR_OR_NULL(q)) - if (p->core == q->core) - return true; - - return false; } -EXPORT_SYMBOL_GPL(clk_is_match); +#endif /** * __clk_init - initialize the data structures in a struct clk -- cgit v1.2.3-58-ga151 From 6e5ab41b1064eee6e3477f573370b2dda7d82bb7 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 30 Apr 2015 15:11:31 -0700 Subject: clk: Update some comments to reflect reality The debugfs clk directory no longer expresses a clk tree. Update the comments around that apporiately. Also drop comments about prepare locks needing to be held as we have the proper annotations with lockdep_assert_held(). Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 51 ++++++++++++++++++--------------------------------- 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 0001b91f2b6e..43b7e7227056 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -162,7 +162,6 @@ static bool clk_core_is_enabled(struct clk_core *core) return core->ops->is_enabled(core->hw); } -/* caller must hold prepare_lock */ static void clk_unprepare_unused_subtree(struct clk_core *core) { struct clk_core *child; @@ -188,7 +187,6 @@ static void clk_unprepare_unused_subtree(struct clk_core *core) } } -/* caller must hold prepare_lock */ static void clk_disable_unused_subtree(struct clk_core *core) { struct clk_core *child; @@ -791,8 +789,7 @@ static unsigned long clk_core_round_rate_nolock(struct clk_core *core, * @min_rate: returned rate must be greater than this rate * @max_rate: returned rate must be less than this rate * - * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate and - * .determine_rate. + * Useful for clk_ops such as .set_rate and .determine_rate. */ unsigned long __clk_determine_rate(struct clk_hw *hw, unsigned long rate, @@ -811,7 +808,7 @@ EXPORT_SYMBOL_GPL(__clk_determine_rate); * @clk: round the rate of this clock * @rate: the rate which is to be rounded * - * Caller must hold prepare_lock. Useful for clk_ops such as .set_rate + * Useful for clk_ops such as .set_rate */ unsigned long __clk_round_rate(struct clk *clk, unsigned long rate) { @@ -892,10 +889,8 @@ static int __clk_notify(struct clk_core *core, unsigned long msg, * * Walks the subtree of clks starting with clk and recalculates accuracies as * it goes. Note that if a clk does not implement the .recalc_accuracy - * callback then it is assumed that the clock will take on the accuracy of it's + * callback then it is assumed that the clock will take on the accuracy of its * parent. - * - * Caller must hold prepare_lock. */ static void __clk_recalc_accuracies(struct clk_core *core) { @@ -968,8 +963,6 @@ static unsigned long clk_recalc(struct clk_core *core, * * clk_recalc_rates also propagates the POST_RATE_CHANGE notification, * if necessary. - * - * Caller must hold prepare_lock. */ static void __clk_recalc_rates(struct clk_core *core, unsigned long msg) { @@ -1179,8 +1172,6 @@ static int __clk_set_parent(struct clk_core *core, struct clk_core *parent, * subtree have subscribed to the notifications. Note that if a clk does not * implement the .recalc_rate callback then it is assumed that the clock will * take on the rate of its parent. - * - * Caller must hold prepare_lock. */ static int __clk_speculate_rates(struct clk_core *core, unsigned long parent_rate) @@ -2091,11 +2082,11 @@ out: } /** - * clk_debug_register - add a clk node to the debugfs clk tree - * @core: the clk being added to the debugfs clk tree + * clk_debug_register - add a clk node to the debugfs clk directory + * @core: the clk being added to the debugfs clk directory * - * Dynamically adds a clk to the debugfs clk tree if debugfs has been - * initialized. Otherwise it bails out early since the debugfs clk tree + * Dynamically adds a clk to the debugfs clk directory if debugfs has been + * initialized. Otherwise it bails out early since the debugfs clk directory * will be created lazily by clk_debug_init as part of a late_initcall. */ static int clk_debug_register(struct clk_core *core) @@ -2116,11 +2107,11 @@ unlock: } /** - * clk_debug_unregister - remove a clk node from the debugfs clk tree - * @core: the clk being removed from the debugfs clk tree + * clk_debug_unregister - remove a clk node from the debugfs clk directory + * @core: the clk being removed from the debugfs clk directory * - * Dynamically removes a clk and all it's children clk nodes from the - * debugfs clk tree if clk->dentry points to debugfs created by + * Dynamically removes a clk and all its child nodes from the + * debugfs clk directory if clk->dentry points to debugfs created by * clk_debug_register in __clk_init. */ static void clk_debug_unregister(struct clk_core *core) @@ -2146,16 +2137,13 @@ struct dentry *clk_debugfs_add_file(struct clk_hw *hw, char *name, umode_t mode, EXPORT_SYMBOL_GPL(clk_debugfs_add_file); /** - * clk_debug_init - lazily create the debugfs clk tree visualization + * clk_debug_init - lazily populate the debugfs clk directory * - * clks are often initialized very early during boot before memory can - * be dynamically allocated and well before debugfs is setup. - * clk_debug_init walks the clk tree hierarchy while holding - * prepare_lock and creates the topology as part of a late_initcall, - * thus insuring that clks initialized very early will still be - * represented in the debugfs clk tree. This function should only be - * called once at boot-time, and all other clks added dynamically will - * be done so with clk_debug_register. + * clks are often initialized very early during boot before memory can be + * dynamically allocated and well before debugfs is setup. This function + * populates the debugfs clk directory once at boot-time when we know that + * debugfs is setup. It should only be called once at boot-time, all other clks + * added dynamically will be done so with clk_debug_register. */ static int __init clk_debug_init(void) { @@ -2512,10 +2500,7 @@ fail_out: } EXPORT_SYMBOL_GPL(clk_register); -/* - * Free memory allocated for a clock. - * Caller must hold prepare_lock. - */ +/* Free memory allocated for a clock. */ static void __clk_release(struct kref *ref) { struct clk_core *core = container_of(ref, struct clk_core, ref); -- cgit v1.2.3-58-ga151 From 7cb81136d2efe0f5ed9d965857f4756a15e6c338 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Wed, 29 Apr 2015 16:36:43 +0000 Subject: clk: Fix JSON output in debugfs key/value pairs in a JSON object must be separated by a comma. After adding the properties "accuracy" and "phase" the JSON output of /sys/kernel/debug/clk/clk_dump is invalid. So add the missing commas to fix it. Fixes: 5279fc402ae5 ("clk: add clk accuracy retrieval support") Signed-off-by: Stefan Wahren [sboyd@codeaurora.org: Added comment in function] Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 43b7e7227056..ea7e8695a32b 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1951,11 +1951,12 @@ static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level) if (!c) return; + /* This should be JSON format, i.e. elements separated with a comma */ seq_printf(s, "\"%s\": { ", c->name); seq_printf(s, "\"enable_count\": %d,", c->enable_count); seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); - seq_printf(s, "\"rate\": %lu", clk_core_get_rate(c)); - seq_printf(s, "\"accuracy\": %lu", clk_core_get_accuracy(c)); + seq_printf(s, "\"rate\": %lu,", clk_core_get_rate(c)); + seq_printf(s, "\"accuracy\": %lu,", clk_core_get_accuracy(c)); seq_printf(s, "\"phase\": %d", clk_core_get_phase(c)); } -- cgit v1.2.3-58-ga151 From d6c27678676abd13f5457490a30995524e1046fb Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Apr 2015 21:52:38 +0900 Subject: clk: at91: Constify irq_domain_ops The irq_domain_ops are not modified by the driver and the irqdomain core code accepts pointer to a const data. Signed-off-by: Krzysztof Kozlowski Acked-by: Boris Brezillon Signed-off-by: Stephen Boyd --- drivers/clk/at91/pmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c index 3f27d21fb729..39be2be82b0a 100644 --- a/drivers/clk/at91/pmc.c +++ b/drivers/clk/at91/pmc.c @@ -153,7 +153,7 @@ static int pmc_irq_domain_xlate(struct irq_domain *d, return 0; } -static struct irq_domain_ops pmc_irq_ops = { +static const struct irq_domain_ops pmc_irq_ops = { .map = pmc_irq_map, .xlate = pmc_irq_domain_xlate, }; -- cgit v1.2.3-58-ga151 From 5ac382c311fc12ccfb74832afe545ce8c38cc459 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Sat, 2 May 2015 17:03:22 +0200 Subject: clk: sunxi: Fix of_io_request_and_map error check of_io_request_and map returns an error pointer, but the current code assumes that on error the returned pointer will be NULL. Obviously, that makes the check completely useless. Change the test to actually check for the proper error code. Signed-off-by: Maxime Ripard Cc: Mike Turquette Cc: Stephen Boyd Cc: linux-clk@vger.kernel.org --- drivers/clk/sunxi/clk-sun9i-core.c | 10 +++++----- drivers/clk/sunxi/clk-sunxi.c | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/clk/sunxi/clk-sun9i-core.c b/drivers/clk/sunxi/clk-sun9i-core.c index d8da77d72861..887f4ea161bb 100644 --- a/drivers/clk/sunxi/clk-sun9i-core.c +++ b/drivers/clk/sunxi/clk-sun9i-core.c @@ -93,7 +93,7 @@ static void __init sun9i_a80_pll4_setup(struct device_node *node) void __iomem *reg; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (!reg) { + if (IS_ERR(reg)) { pr_err("Could not get registers for a80-pll4-clk: %s\n", node->name); return; @@ -154,7 +154,7 @@ static void __init sun9i_a80_gt_setup(struct device_node *node) struct clk *gt; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (!reg) { + if (IS_ERR(reg)) { pr_err("Could not get registers for a80-gt-clk: %s\n", node->name); return; @@ -218,7 +218,7 @@ static void __init sun9i_a80_ahb_setup(struct device_node *node) void __iomem *reg; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (!reg) { + if (IS_ERR(reg)) { pr_err("Could not get registers for a80-ahb-clk: %s\n", node->name); return; @@ -244,7 +244,7 @@ static void __init sun9i_a80_apb0_setup(struct device_node *node) void __iomem *reg; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (!reg) { + if (IS_ERR(reg)) { pr_err("Could not get registers for a80-apb0-clk: %s\n", node->name); return; @@ -310,7 +310,7 @@ static void __init sun9i_a80_apb1_setup(struct device_node *node) void __iomem *reg; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); - if (!reg) { + if (IS_ERR(reg)) { pr_err("Could not get registers for a80-apb1-clk: %s\n", node->name); return; diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 7e1e2bd189b6..9a82f17d2d73 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -198,6 +198,8 @@ static void __init sun6i_ahb1_clk_setup(struct device_node *node) int i = 0; reg = of_io_request_and_map(node, 0, of_node_full_name(node)); + if (IS_ERR(reg)) + return; /* we have a mux, we will have >1 parents */ while (i < SUN6I_AHB1_MAX_PARENTS && -- cgit v1.2.3-58-ga151 From 2893c379461a208b3059f55dfe4dafa06b4aa46a Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Tue, 31 Mar 2015 20:16:52 +0200 Subject: clk: make strings in parent name arrays const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The clk functions and structs declare the parent_name arrays as 'const char **parent_names' which means the parent name strings are const, but the array itself is not. Use 'const char * const * parent_names' instead which also makes the array const. This allows us to put the parent_name arrays into the __initconst section. Signed-off-by: Sascha Hauer Reviewed-by: Krzysztof Kozlowski Tested-by: Krzysztof Kozlowski Acked-by: Uwe Kleine-König [sboyd@codeaurora.org: Squelch 80-character checkpatch warnings] Signed-off-by: Stephen Boyd --- drivers/clk/clk-composite.c | 2 +- drivers/clk/clk-mux.c | 6 ++++-- include/linux/clk-provider.h | 10 ++++++---- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 956b7e54fa1c..077f4c7148f1 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -188,7 +188,7 @@ static void clk_composite_disable(struct clk_hw *hw) } struct clk *clk_register_composite(struct device *dev, const char *name, - const char **parent_names, int num_parents, + const char * const *parent_names, int num_parents, struct clk_hw *mux_hw, const struct clk_ops *mux_ops, struct clk_hw *rate_hw, const struct clk_ops *rate_ops, struct clk_hw *gate_hw, const struct clk_ops *gate_ops, diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 69a094c3783d..6066a01b20ea 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -114,7 +114,8 @@ const struct clk_ops clk_mux_ro_ops = { 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, + const char * const *parent_names, u8 num_parents, + unsigned long flags, void __iomem *reg, u8 shift, u32 mask, u8 clk_mux_flags, u32 *table, spinlock_t *lock) { @@ -166,7 +167,8 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, 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, + const char * const *parent_names, u8 num_parents, + unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_mux_flags, spinlock_t *lock) { diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index df695313f975..5378c2aba4d2 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -209,7 +209,7 @@ struct clk_ops { struct clk_init_data { const char *name; const struct clk_ops *ops; - const char **parent_names; + const char * const *parent_names; u8 num_parents; unsigned long flags; }; @@ -426,12 +426,14 @@ extern const struct clk_ops clk_mux_ops; 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, + const char * const *parent_names, u8 num_parents, + unsigned long flags, 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, + const char * const *parent_names, u8 num_parents, + unsigned long flags, void __iomem *reg, u8 shift, u32 mask, u8 clk_mux_flags, u32 *table, spinlock_t *lock); @@ -518,7 +520,7 @@ struct clk_composite { }; struct clk *clk_register_composite(struct device *dev, const char *name, - const char **parent_names, int num_parents, + const char * const *parent_names, int num_parents, struct clk_hw *mux_hw, const struct clk_ops *mux_ops, struct clk_hw *rate_hw, const struct clk_ops *rate_ops, struct clk_hw *gate_hw, const struct clk_ops *gate_ops, -- cgit v1.2.3-58-ga151 From 9741b1a68035b541005db1a4d7623bd9b3522ab4 Mon Sep 17 00:00:00 2001 From: James Liao Date: Thu, 23 Apr 2015 10:35:39 +0200 Subject: clk: mediatek: Add initial common clock support for Mediatek SoCs. This patch adds common clock support for Mediatek SoCs, including plls, muxes and clock gates. Signed-off-by: James Liao Signed-off-by: Henry Chen Signed-off-by: Sascha Hauer [sboyd@codeaurora.org: Squelch checkpatch warning in clk-mtk.h] Signed-off-by: Stephen Boyd --- drivers/clk/Makefile | 1 + drivers/clk/mediatek/Makefile | 1 + drivers/clk/mediatek/clk-gate.c | 137 +++++++++++++++++ drivers/clk/mediatek/clk-gate.h | 49 ++++++ drivers/clk/mediatek/clk-mtk.c | 220 ++++++++++++++++++++++++++ drivers/clk/mediatek/clk-mtk.h | 159 +++++++++++++++++++ drivers/clk/mediatek/clk-pll.c | 332 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 899 insertions(+) create mode 100644 drivers/clk/mediatek/Makefile create mode 100644 drivers/clk/mediatek/clk-gate.c create mode 100644 drivers/clk/mediatek/clk-gate.h create mode 100644 drivers/clk/mediatek/clk-mtk.c create mode 100644 drivers/clk/mediatek/clk-mtk.h create mode 100644 drivers/clk/mediatek/clk-pll.c diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 3d00c25382c5..d965b3f6a372 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ obj-$(CONFIG_ARCH_HIP04) += hisilicon/ obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ +obj-$(CONFIG_ARCH_MEDIATEK) += mediatek/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile new file mode 100644 index 000000000000..c384e9778d1a --- /dev/null +++ b/drivers/clk/mediatek/Makefile @@ -0,0 +1 @@ +obj-y += clk-mtk.o clk-pll.o clk-gate.o diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c new file mode 100644 index 000000000000..9d77ee3256f2 --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +static int mtk_cg_bit_is_cleared(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val == 0; +} + +static int mtk_cg_bit_is_set(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + u32 val; + + regmap_read(cg->regmap, cg->sta_ofs, &val); + + val &= BIT(cg->bit); + + return val != 0; +} + +static void mtk_cg_set_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit)); +} + +static void mtk_cg_clr_bit(struct clk_hw *hw) +{ + struct mtk_clk_gate *cg = to_clk_gate(hw); + + regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit)); +} + +static int mtk_cg_enable(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); + + return 0; +} + +static void mtk_cg_disable(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); +} + +static int mtk_cg_enable_inv(struct clk_hw *hw) +{ + mtk_cg_set_bit(hw); + + return 0; +} + +static void mtk_cg_disable_inv(struct clk_hw *hw) +{ + mtk_cg_clr_bit(hw); +} + +const struct clk_ops mtk_clk_gate_ops_setclr = { + .is_enabled = mtk_cg_bit_is_cleared, + .enable = mtk_cg_enable, + .disable = mtk_cg_disable, +}; + +const struct clk_ops mtk_clk_gate_ops_setclr_inv = { + .is_enabled = mtk_cg_bit_is_set, + .enable = mtk_cg_enable_inv, + .disable = mtk_cg_disable_inv, +}; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops) +{ + struct mtk_clk_gate *cg; + struct clk *clk; + struct clk_init_data init; + + cg = kzalloc(sizeof(*cg), GFP_KERNEL); + if (!cg) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + init.ops = ops; + + cg->regmap = regmap; + cg->set_ofs = set_ofs; + cg->clr_ofs = clr_ofs; + cg->sta_ofs = sta_ofs; + cg->bit = bit; + + cg->hw.init = &init; + + clk = clk_register(NULL, &cg->hw); + if (IS_ERR(clk)) + kfree(cg); + + return clk; +} diff --git a/drivers/clk/mediatek/clk-gate.h b/drivers/clk/mediatek/clk-gate.h new file mode 100644 index 000000000000..6b6780b1e9c5 --- /dev/null +++ b/drivers/clk/mediatek/clk-gate.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __DRV_CLK_GATE_H +#define __DRV_CLK_GATE_H + +#include +#include +#include + +struct mtk_clk_gate { + struct clk_hw hw; + struct regmap *regmap; + int set_ofs; + int clr_ofs; + int sta_ofs; + u8 bit; +}; + +static inline struct mtk_clk_gate *to_clk_gate(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_gate, hw); +} + +extern const struct clk_ops mtk_clk_gate_ops_setclr; +extern const struct clk_ops mtk_clk_gate_ops_setclr_inv; + +struct clk *mtk_clk_register_gate( + const char *name, + const char *parent_name, + struct regmap *regmap, + int set_ofs, + int clr_ofs, + int sta_ofs, + u8 bit, + const struct clk_ops *ops); + +#endif /* __DRV_CLK_GATE_H */ diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c new file mode 100644 index 000000000000..18444aea63c9 --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num) +{ + int i; + struct clk_onecell_data *clk_data; + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return NULL; + + clk_data->clks = kcalloc(clk_num, sizeof(*clk_data->clks), GFP_KERNEL); + if (!clk_data->clks) + goto err_out; + + clk_data->clk_num = clk_num; + + for (i = 0; i < clk_num; i++) + clk_data->clks[i] = ERR_PTR(-ENOENT); + + return clk_data; +err_out: + kfree(clk_data); + + return NULL; +} + +void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, int num, + struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + + for (i = 0; i < num; i++) { + const struct mtk_fixed_factor *ff = &clks[i]; + + clk = clk_register_fixed_factor(NULL, ff->name, ff->parent_name, + CLK_SET_RATE_PARENT, ff->mult, ff->div); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + ff->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[ff->id] = clk; + } +} + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data) +{ + int i; + struct clk *clk; + struct regmap *regmap; + + if (!clk_data) + return -ENOMEM; + + regmap = syscon_node_to_regmap(node); + if (IS_ERR(regmap)) { + pr_err("Cannot find regmap for %s: %ld\n", node->full_name, + PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + + for (i = 0; i < num; i++) { + const struct mtk_gate *gate = &clks[i]; + + clk = mtk_clk_register_gate(gate->name, gate->parent_name, + regmap, + gate->regs->set_ofs, + gate->regs->clr_ofs, + gate->regs->sta_ofs, + gate->shift, gate->ops); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + gate->name, PTR_ERR(clk)); + continue; + } + + clk_data->clks[gate->id] = clk; + } + + return 0; +} + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock) +{ + struct clk *clk; + struct clk_mux *mux = NULL; + struct clk_gate *gate = NULL; + struct clk_divider *div = NULL; + struct clk_hw *mux_hw = NULL, *gate_hw = NULL, *div_hw = NULL; + const struct clk_ops *mux_ops = NULL, *gate_ops = NULL, *div_ops = NULL; + const char * const *parent_names; + const char *parent; + int num_parents; + int ret; + + if (mc->mux_shift >= 0) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = base + mc->mux_reg; + mux->mask = BIT(mc->mux_width) - 1; + mux->shift = mc->mux_shift; + mux->lock = lock; + + mux_hw = &mux->hw; + mux_ops = &clk_mux_ops; + + parent_names = mc->parent_names; + num_parents = mc->num_parents; + } else { + parent = mc->parent; + parent_names = &parent; + num_parents = 1; + } + + if (mc->gate_shift >= 0) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + ret = -ENOMEM; + goto err_out; + } + + gate->reg = base + mc->gate_reg; + gate->bit_idx = mc->gate_shift; + gate->flags = CLK_GATE_SET_TO_DISABLE; + gate->lock = lock; + + gate_hw = &gate->hw; + gate_ops = &clk_gate_ops; + } + + if (mc->divider_shift >= 0) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) { + ret = -ENOMEM; + goto err_out; + } + + div->reg = base + mc->divider_reg; + div->shift = mc->divider_shift; + div->width = mc->divider_width; + div->lock = lock; + + div_hw = &div->hw; + div_ops = &clk_divider_ops; + } + + clk = clk_register_composite(NULL, mc->name, parent_names, num_parents, + mux_hw, mux_ops, + div_hw, div_ops, + gate_hw, gate_ops, + mc->flags); + + if (IS_ERR(clk)) { + kfree(gate); + kfree(mux); + } + + return clk; +err_out: + kfree(mux); + + return ERR_PTR(ret); +} + +void mtk_clk_register_composites(const struct mtk_composite *mcs, + int num, void __iomem *base, spinlock_t *lock, + struct clk_onecell_data *clk_data) +{ + struct clk *clk; + int i; + + for (i = 0; i < num; i++) { + const struct mtk_composite *mc = &mcs[i]; + + clk = mtk_clk_register_composite(mc, base, lock); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + mc->name, PTR_ERR(clk)); + continue; + } + + if (clk_data) + clk_data->clks[mc->id] = clk; + } +} diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h new file mode 100644 index 000000000000..38f8ceb0470b --- /dev/null +++ b/drivers/clk/mediatek/clk-mtk.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __DRV_CLK_MTK_H +#define __DRV_CLK_MTK_H + +#include +#include +#include +#include + +#define MAX_MUX_GATE_BIT 31 +#define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1) + +#define MHZ (1000 * 1000) + +struct mtk_fixed_factor { + int id; + const char *name; + const char *parent_name; + int mult; + int div; +}; + +#define FACTOR(_id, _name, _parent, _mult, _div) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .mult = _mult, \ + .div = _div, \ + } + +extern void mtk_clk_register_factors(const struct mtk_fixed_factor *clks, + int num, struct clk_onecell_data *clk_data); + +struct mtk_composite { + int id; + const char *name; + const char * const *parent_names; + const char *parent; + unsigned flags; + + uint32_t mux_reg; + uint32_t divider_reg; + uint32_t gate_reg; + + signed char mux_shift; + signed char mux_width; + signed char gate_shift; + + signed char divider_shift; + signed char divider_width; + + signed char num_parents; +}; + +#define MUX_GATE(_id, _name, _parents, _reg, _shift, _width, _gate) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_reg = _reg, \ + .gate_shift = _gate, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define MUX(_id, _name, _parents, _reg, _shift, _width) { \ + .id = _id, \ + .name = _name, \ + .mux_reg = _reg, \ + .mux_shift = _shift, \ + .mux_width = _width, \ + .gate_shift = -1, \ + .divider_shift = -1, \ + .parent_names = _parents, \ + .num_parents = ARRAY_SIZE(_parents), \ + .flags = CLK_SET_RATE_PARENT, \ + } + +#define DIV_GATE(_id, _name, _parent, _gate_reg, _gate_shift, _div_reg, _div_width, _div_shift) { \ + .id = _id, \ + .parent = _parent, \ + .name = _name, \ + .divider_reg = _div_reg, \ + .divider_shift = _div_shift, \ + .divider_width = _div_width, \ + .gate_reg = _gate_reg, \ + .gate_shift = _gate_shift, \ + .mux_shift = -1, \ + .flags = 0, \ + } + +struct clk *mtk_clk_register_composite(const struct mtk_composite *mc, + void __iomem *base, spinlock_t *lock); + +void mtk_clk_register_composites(const struct mtk_composite *mcs, + int num, void __iomem *base, spinlock_t *lock, + struct clk_onecell_data *clk_data); + +struct mtk_gate_regs { + u32 sta_ofs; + u32 clr_ofs; + u32 set_ofs; +}; + +struct mtk_gate { + int id; + const char *name; + const char *parent_name; + const struct mtk_gate_regs *regs; + int shift; + const struct clk_ops *ops; +}; + +int mtk_clk_register_gates(struct device_node *node, const struct mtk_gate *clks, + int num, struct clk_onecell_data *clk_data); + +struct clk_onecell_data *mtk_alloc_clk_data(unsigned int clk_num); + +#define HAVE_RST_BAR BIT(0) + +struct mtk_pll_data { + int id; + const char *name; + uint32_t reg; + uint32_t pwr_reg; + uint32_t en_mask; + uint32_t pd_reg; + uint32_t tuner_reg; + int pd_shift; + unsigned int flags; + const struct clk_ops *ops; + u32 rst_bar_mask; + unsigned long fmax; + int pcwbits; + uint32_t pcw_reg; + int pcw_shift; +}; + +void __init mtk_clk_register_plls(struct device_node *node, + const struct mtk_pll_data *plls, int num_plls, + struct clk_onecell_data *clk_data); + +#endif /* __DRV_CLK_MTK_H */ diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c new file mode 100644 index 000000000000..66154caf992a --- /dev/null +++ b/drivers/clk/mediatek/clk-pll.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include + +#include "clk-mtk.h" + +#define REG_CON0 0 +#define REG_CON1 4 + +#define CON0_BASE_EN BIT(0) +#define CON0_PWR_ON BIT(0) +#define CON0_ISO_EN BIT(1) +#define CON0_PCW_CHG BIT(31) + +#define AUDPLL_TUNER_EN BIT(31) + +#define POSTDIV_MASK 0x7 +#define INTEGER_BITS 7 + +/* + * MediaTek PLLs are configured through their pcw value. The pcw value describes + * a divider in the PLL feedback loop which consists of 7 bits for the integer + * part and the remaining bits (if present) for the fractional part. Also they + * have a 3 bit power-of-two post divider. + */ + +struct mtk_clk_pll { + struct clk_hw hw; + void __iomem *base_addr; + void __iomem *pd_addr; + void __iomem *pwr_addr; + void __iomem *tuner_addr; + void __iomem *pcw_addr; + const struct mtk_pll_data *data; +}; + +static inline struct mtk_clk_pll *to_mtk_clk_pll(struct clk_hw *hw) +{ + return container_of(hw, struct mtk_clk_pll, hw); +} + +static int mtk_pll_is_prepared(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + + return (readl(pll->base_addr + REG_CON0) & CON0_BASE_EN) != 0; +} + +static unsigned long __mtk_pll_recalc_rate(struct mtk_clk_pll *pll, u32 fin, + u32 pcw, int postdiv) +{ + int pcwbits = pll->data->pcwbits; + int pcwfbits; + u64 vco; + u8 c = 0; + + /* The fractional part of the PLL divider. */ + pcwfbits = pcwbits > INTEGER_BITS ? pcwbits - INTEGER_BITS : 0; + + vco = (u64)fin * pcw; + + if (pcwfbits && (vco & GENMASK(pcwfbits - 1, 0))) + c = 1; + + vco >>= pcwfbits; + + if (c) + vco++; + + return ((unsigned long)vco + postdiv - 1) / postdiv; +} + +static void mtk_pll_set_rate_regs(struct mtk_clk_pll *pll, u32 pcw, + int postdiv) +{ + u32 con1, pd, val; + int pll_en; + + /* set postdiv */ + pd = readl(pll->pd_addr); + pd &= ~(POSTDIV_MASK << pll->data->pd_shift); + pd |= (ffs(postdiv) - 1) << pll->data->pd_shift; + writel(pd, pll->pd_addr); + + pll_en = readl(pll->base_addr + REG_CON0) & CON0_BASE_EN; + + /* set pcw */ + val = readl(pll->pcw_addr); + + val &= ~GENMASK(pll->data->pcw_shift + pll->data->pcwbits - 1, + pll->data->pcw_shift); + val |= pcw << pll->data->pcw_shift; + writel(val, pll->pcw_addr); + + con1 = readl(pll->base_addr + REG_CON1); + + if (pll_en) + con1 |= CON0_PCW_CHG; + + writel(con1, pll->base_addr + REG_CON1); + if (pll->tuner_addr) + writel(con1 + 1, pll->tuner_addr); + + if (pll_en) + udelay(20); +} + +/* + * mtk_pll_calc_values - calculate good values for a given input frequency. + * @pll: The pll + * @pcw: The pcw value (output) + * @postdiv: The post divider (output) + * @freq: The desired target frequency + * @fin: The input frequency + * + */ +static void mtk_pll_calc_values(struct mtk_clk_pll *pll, u32 *pcw, u32 *postdiv, + u32 freq, u32 fin) +{ + unsigned long fmin = 1000 * MHZ; + u64 _pcw; + u32 val; + + if (freq > pll->data->fmax) + freq = pll->data->fmax; + + for (val = 0; val < 4; val++) { + *postdiv = 1 << val; + if (freq * *postdiv >= fmin) + break; + } + + /* _pcw = freq * postdiv / fin * 2^pcwfbits */ + _pcw = ((u64)freq << val) << (pll->data->pcwbits - INTEGER_BITS); + do_div(_pcw, fin); + + *pcw = (u32)_pcw; +} + +static int mtk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + u32 postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, parent_rate); + mtk_pll_set_rate_regs(pll, pcw, postdiv); + + return 0; +} + +static unsigned long mtk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 postdiv; + u32 pcw; + + postdiv = (readl(pll->pd_addr) >> pll->data->pd_shift) & POSTDIV_MASK; + postdiv = 1 << postdiv; + + pcw = readl(pll->pcw_addr) >> pll->data->pcw_shift; + pcw &= GENMASK(pll->data->pcwbits - 1, 0); + + return __mtk_pll_recalc_rate(pll, parent_rate, pcw, postdiv); +} + +static long mtk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 pcw = 0; + int postdiv; + + mtk_pll_calc_values(pll, &pcw, &postdiv, rate, *prate); + + return __mtk_pll_recalc_rate(pll, *prate, pcw, postdiv); +} + +static int mtk_pll_prepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + r = readl(pll->pwr_addr) | CON0_PWR_ON; + writel(r, pll->pwr_addr); + udelay(1); + + r = readl(pll->pwr_addr) & ~CON0_ISO_EN; + writel(r, pll->pwr_addr); + udelay(1); + + r = readl(pll->base_addr + REG_CON0); + r |= pll->data->en_mask; + writel(r, pll->base_addr + REG_CON0); + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) | AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + udelay(20); + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr + REG_CON0); + r |= pll->data->rst_bar_mask; + writel(r, pll->base_addr + REG_CON0); + } + + return 0; +} + +static void mtk_pll_unprepare(struct clk_hw *hw) +{ + struct mtk_clk_pll *pll = to_mtk_clk_pll(hw); + u32 r; + + if (pll->data->flags & HAVE_RST_BAR) { + r = readl(pll->base_addr + REG_CON0); + r &= ~pll->data->rst_bar_mask; + writel(r, pll->base_addr + REG_CON0); + } + + if (pll->tuner_addr) { + r = readl(pll->tuner_addr) & ~AUDPLL_TUNER_EN; + writel(r, pll->tuner_addr); + } + + r = readl(pll->base_addr + REG_CON0); + r &= ~CON0_BASE_EN; + writel(r, pll->base_addr + REG_CON0); + + r = readl(pll->pwr_addr) | CON0_ISO_EN; + writel(r, pll->pwr_addr); + + r = readl(pll->pwr_addr) & ~CON0_PWR_ON; + writel(r, pll->pwr_addr); +} + +static const struct clk_ops mtk_pll_ops = { + .is_prepared = mtk_pll_is_prepared, + .prepare = mtk_pll_prepare, + .unprepare = mtk_pll_unprepare, + .recalc_rate = mtk_pll_recalc_rate, + .round_rate = mtk_pll_round_rate, + .set_rate = mtk_pll_set_rate, +}; + +static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data, + void __iomem *base) +{ + struct mtk_clk_pll *pll; + struct clk_init_data init; + struct clk *clk; + const char *parent_name = "clk26m"; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (!pll) + return ERR_PTR(-ENOMEM); + + pll->base_addr = base + data->reg; + pll->pwr_addr = base + data->pwr_reg; + pll->pd_addr = base + data->pd_reg; + pll->pcw_addr = base + data->pcw_reg; + if (data->tuner_reg) + pll->tuner_addr = base + data->tuner_reg; + pll->hw.init = &init; + pll->data = data; + + init.name = data->name; + init.ops = &mtk_pll_ops; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &pll->hw); + + if (IS_ERR(clk)) + kfree(pll); + + return clk; +} + +void __init mtk_clk_register_plls(struct device_node *node, + const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data) +{ + void __iomem *base; + int r, i; + struct clk *clk; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + for (i = 0; i < num_plls; i++) { + const struct mtk_pll_data *pll = &plls[i]; + + clk = mtk_clk_register_pll(pll, base); + + if (IS_ERR(clk)) { + pr_err("Failed to register clk %s: %ld\n", + pll->name, PTR_ERR(clk)); + continue; + } + + clk_data->clks[pll->id] = clk; + } + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} -- cgit v1.2.3-58-ga151 From d633fb7ac1e6abc39270edebbfa23131c673b5b9 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 23 Apr 2015 10:35:40 +0200 Subject: clk: mediatek: Add reset controller support The pericfg and infracfg units also provide reset lines to several other SoC internal units. This adds a function which can be called from the pericfg and infracfg initialization functions which will register the reset controller using reset_controller_register. The reset controller will provide support for resetting the units connected to the pericfg and infracfg controller. The units resetted by this controller can use the standard reset device tree binding to gain access to the reset lines. Signed-off-by: Sascha Hauer Acked-by: Philipp Zabel Signed-off-by: Stephen Boyd --- drivers/clk/mediatek/Makefile | 1 + drivers/clk/mediatek/clk-mtk.h | 10 +++++ drivers/clk/mediatek/reset.c | 97 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 drivers/clk/mediatek/reset.c diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index c384e9778d1a..0b6f1c3c9c69 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -1 +1,2 @@ obj-y += clk-mtk.o clk-pll.o clk-gate.o +obj-$(CONFIG_RESET_CONTROLLER) += reset.o diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h index 38f8ceb0470b..9dda9d8ad10b 100644 --- a/drivers/clk/mediatek/clk-mtk.h +++ b/drivers/clk/mediatek/clk-mtk.h @@ -156,4 +156,14 @@ void __init mtk_clk_register_plls(struct device_node *node, const struct mtk_pll_data *plls, int num_plls, struct clk_onecell_data *clk_data); +#ifdef CONFIG_RESET_CONTROLLER +void mtk_register_reset_controller(struct device_node *np, + unsigned int num_regs, int regofs); +#else +static inline void mtk_register_reset_controller(struct device_node *np, + unsigned int num_regs, int regofs) +{ +} +#endif + #endif /* __DRV_CLK_MTK_H */ diff --git a/drivers/clk/mediatek/reset.c b/drivers/clk/mediatek/reset.c new file mode 100644 index 000000000000..9e9fe4b19ac4 --- /dev/null +++ b/drivers/clk/mediatek/reset.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "clk-mtk.h" + +struct mtk_reset { + struct regmap *regmap; + int regofs; + struct reset_controller_dev rcdev; +}; + +static int mtk_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); + + return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2), + BIT(id % 32), ~0); +} + +static int mtk_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); + + return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2), + BIT(id % 32), 0); +} + +static int mtk_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + int ret; + + ret = mtk_reset_assert(rcdev, id); + if (ret) + return ret; + + return mtk_reset_deassert(rcdev, id); +} + +static struct reset_control_ops mtk_reset_ops = { + .assert = mtk_reset_assert, + .deassert = mtk_reset_deassert, + .reset = mtk_reset, +}; + +void mtk_register_reset_controller(struct device_node *np, + unsigned int num_regs, int regofs) +{ + struct mtk_reset *data; + int ret; + struct regmap *regmap; + + regmap = syscon_node_to_regmap(np); + if (IS_ERR(regmap)) { + pr_err("Cannot find regmap for %s: %ld\n", np->full_name, + PTR_ERR(regmap)); + return; + } + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return; + + data->regmap = regmap; + data->regofs = regofs; + data->rcdev.owner = THIS_MODULE; + data->rcdev.nr_resets = num_regs * 32; + data->rcdev.ops = &mtk_reset_ops; + data->rcdev.of_node = np; + + ret = reset_controller_register(&data->rcdev); + if (ret) { + pr_err("could not register reset controller: %d\n", ret); + kfree(data); + return; + } +} -- cgit v1.2.3-58-ga151 From a8aede7948438f3a9c830a5a865ae6a78c5fc4ca Mon Sep 17 00:00:00 2001 From: James Liao Date: Thu, 23 Apr 2015 10:35:41 +0200 Subject: clk: mediatek: Add basic clocks for Mediatek MT8135. This patch adds basic clocks for MT8135, including TOPCKGEN, PLLs, INFRA and PERI clocks. Signed-off-by: James Liao Signed-off-by: Henry Chen Signed-off-by: Sascha Hauer Signed-off-by: Stephen Boyd --- drivers/clk/mediatek/Makefile | 1 + drivers/clk/mediatek/clk-mt8135.c | 644 +++++++++++++++++++++ include/dt-bindings/clock/mt8135-clk.h | 194 +++++++ .../dt-bindings/reset-controller/mt8135-resets.h | 64 ++ 4 files changed, 903 insertions(+) create mode 100644 drivers/clk/mediatek/clk-mt8135.c create mode 100644 include/dt-bindings/clock/mt8135-clk.h create mode 100644 include/dt-bindings/reset-controller/mt8135-resets.h diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index 0b6f1c3c9c69..12ce576e628a 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -1,2 +1,3 @@ obj-y += clk-mtk.o clk-pll.o clk-gate.o obj-$(CONFIG_RESET_CONTROLLER) += reset.o +obj-y += clk-mt8135.o diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c new file mode 100644 index 000000000000..a63435b95822 --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8135.c @@ -0,0 +1,644 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +static DEFINE_SPINLOCK(mt8135_clk_lock); + +static const struct mtk_fixed_factor root_clk_alias[] __initconst = { + FACTOR(CLK_TOP_DSI0_LNTC_DSICLK, "dsi0_lntc_dsiclk", "clk_null", 1, 1), + FACTOR(CLK_TOP_HDMITX_CLKDIG_CTS, "hdmitx_clkdig_cts", "clk_null", 1, 1), + FACTOR(CLK_TOP_CLKPH_MCK, "clkph_mck", "clk_null", 1, 1), + FACTOR(CLK_TOP_CPUM_TCK_IN, "cpum_tck_in", "clk_null", 1, 1), +}; + +static const struct mtk_fixed_factor top_divs[] __initconst = { + FACTOR(CLK_TOP_MAINPLL_806M, "mainpll_806m", "mainpll", 1, 2), + FACTOR(CLK_TOP_MAINPLL_537P3M, "mainpll_537p3m", "mainpll", 1, 3), + FACTOR(CLK_TOP_MAINPLL_322P4M, "mainpll_322p4m", "mainpll", 1, 5), + FACTOR(CLK_TOP_MAINPLL_230P3M, "mainpll_230p3m", "mainpll", 1, 7), + + FACTOR(CLK_TOP_UNIVPLL_624M, "univpll_624m", "univpll", 1, 2), + FACTOR(CLK_TOP_UNIVPLL_416M, "univpll_416m", "univpll", 1, 3), + FACTOR(CLK_TOP_UNIVPLL_249P6M, "univpll_249p6m", "univpll", 1, 5), + FACTOR(CLK_TOP_UNIVPLL_178P3M, "univpll_178p3m", "univpll", 1, 7), + FACTOR(CLK_TOP_UNIVPLL_48M, "univpll_48m", "univpll", 1, 26), + + FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2), + FACTOR(CLK_TOP_MMPLL_D3, "mmpll_d3", "mmpll", 1, 3), + FACTOR(CLK_TOP_MMPLL_D5, "mmpll_d5", "mmpll", 1, 5), + FACTOR(CLK_TOP_MMPLL_D7, "mmpll_d7", "mmpll", 1, 7), + FACTOR(CLK_TOP_MMPLL_D4, "mmpll_d4", "mmpll_d2", 1, 2), + FACTOR(CLK_TOP_MMPLL_D6, "mmpll_d6", "mmpll_d3", 1, 2), + + FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "mainpll_806m", 1, 1), + FACTOR(CLK_TOP_SYSPLL_D4, "syspll_d4", "mainpll_806m", 1, 2), + FACTOR(CLK_TOP_SYSPLL_D6, "syspll_d6", "mainpll_806m", 1, 3), + FACTOR(CLK_TOP_SYSPLL_D8, "syspll_d8", "mainpll_806m", 1, 4), + FACTOR(CLK_TOP_SYSPLL_D10, "syspll_d10", "mainpll_806m", 1, 5), + FACTOR(CLK_TOP_SYSPLL_D12, "syspll_d12", "mainpll_806m", 1, 6), + FACTOR(CLK_TOP_SYSPLL_D16, "syspll_d16", "mainpll_806m", 1, 8), + FACTOR(CLK_TOP_SYSPLL_D24, "syspll_d24", "mainpll_806m", 1, 12), + + FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "mainpll_537p3m", 1, 1), + + FACTOR(CLK_TOP_SYSPLL_D2P5, "syspll_d2p5", "mainpll_322p4m", 2, 1), + FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "mainpll_322p4m", 1, 1), + + FACTOR(CLK_TOP_SYSPLL_D3P5, "syspll_d3p5", "mainpll_230p3m", 2, 1), + + FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univpll_624m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univpll_624m", 1, 4), + FACTOR(CLK_TOP_UNIVPLL1_D6, "univpll1_d6", "univpll_624m", 1, 6), + FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univpll_624m", 1, 8), + FACTOR(CLK_TOP_UNIVPLL1_D10, "univpll1_d10", "univpll_624m", 1, 10), + + FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univpll_416m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univpll_416m", 1, 4), + FACTOR(CLK_TOP_UNIVPLL2_D6, "univpll2_d6", "univpll_416m", 1, 6), + FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univpll_416m", 1, 8), + + FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univpll_416m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univpll_249p6m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univpll_178p3m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D10, "univpll_d10", "univpll_249p6m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univpll_48m", 1, 1), + + FACTOR(CLK_TOP_APLL, "apll_ck", "audpll", 1, 1), + FACTOR(CLK_TOP_APLL_D4, "apll_d4", "audpll", 1, 4), + FACTOR(CLK_TOP_APLL_D8, "apll_d8", "audpll", 1, 8), + FACTOR(CLK_TOP_APLL_D16, "apll_d16", "audpll", 1, 16), + FACTOR(CLK_TOP_APLL_D24, "apll_d24", "audpll", 1, 24), + + FACTOR(CLK_TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll", 1, 2), + FACTOR(CLK_TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll", 1, 4), + FACTOR(CLK_TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll", 1, 8), + + FACTOR(CLK_TOP_LVDSTX_CLKDIG_CT, "lvdstx_clkdig_cts", "lvdspll", 1, 1), + FACTOR(CLK_TOP_VPLL_DPIX, "vpll_dpix_ck", "lvdspll", 1, 1), + + FACTOR(CLK_TOP_TVHDMI_H, "tvhdmi_h_ck", "tvdpll", 1, 1), + + FACTOR(CLK_TOP_HDMITX_CLKDIG_D2, "hdmitx_clkdig_d2", "hdmitx_clkdig_cts", 1, 2), + FACTOR(CLK_TOP_HDMITX_CLKDIG_D3, "hdmitx_clkdig_d3", "hdmitx_clkdig_cts", 1, 3), + + FACTOR(CLK_TOP_TVHDMI_D2, "tvhdmi_d2", "tvhdmi_h_ck", 1, 2), + FACTOR(CLK_TOP_TVHDMI_D4, "tvhdmi_d4", "tvhdmi_h_ck", 1, 4), + + FACTOR(CLK_TOP_MEMPLL_MCK_D4, "mempll_mck_d4", "clkph_mck", 1, 4), +}; + +static const char * const axi_parents[] __initconst = { + "clk26m", + "syspll_d3", + "syspll_d4", + "syspll_d6", + "univpll_d5", + "univpll2_d2", + "syspll_d3p5" +}; + +static const char * const smi_parents[] __initconst = { + "clk26m", + "clkph_mck", + "syspll_d2p5", + "syspll_d3", + "syspll_d8", + "univpll_d5", + "univpll1_d2", + "univpll1_d6", + "mmpll_d3", + "mmpll_d4", + "mmpll_d5", + "mmpll_d6", + "mmpll_d7", + "vdecpll", + "lvdspll" +}; + +static const char * const mfg_parents[] __initconst = { + "clk26m", + "univpll1_d4", + "syspll_d2", + "syspll_d2p5", + "syspll_d3", + "univpll_d5", + "univpll1_d2", + "mmpll_d2", + "mmpll_d3", + "mmpll_d4", + "mmpll_d5", + "mmpll_d6", + "mmpll_d7" +}; + +static const char * const irda_parents[] __initconst = { + "clk26m", + "univpll2_d8", + "univpll1_d6" +}; + +static const char * const cam_parents[] __initconst = { + "clk26m", + "syspll_d3", + "syspll_d3p5", + "syspll_d4", + "univpll_d5", + "univpll2_d2", + "univpll_d7", + "univpll1_d4" +}; + +static const char * const aud_intbus_parents[] __initconst = { + "clk26m", + "syspll_d6", + "univpll_d10" +}; + +static const char * const jpg_parents[] __initconst = { + "clk26m", + "syspll_d5", + "syspll_d4", + "syspll_d3", + "univpll_d7", + "univpll2_d2", + "univpll_d5" +}; + +static const char * const disp_parents[] __initconst = { + "clk26m", + "syspll_d3p5", + "syspll_d3", + "univpll2_d2", + "univpll_d5", + "univpll1_d2", + "lvdspll", + "vdecpll" +}; + +static const char * const msdc30_parents[] __initconst = { + "clk26m", + "syspll_d6", + "syspll_d5", + "univpll1_d4", + "univpll2_d4", + "msdcpll" +}; + +static const char * const usb20_parents[] __initconst = { + "clk26m", + "univpll2_d6", + "univpll1_d10" +}; + +static const char * const venc_parents[] __initconst = { + "clk26m", + "syspll_d3", + "syspll_d8", + "univpll_d5", + "univpll1_d6", + "mmpll_d4", + "mmpll_d5", + "mmpll_d6" +}; + +static const char * const spi_parents[] __initconst = { + "clk26m", + "syspll_d6", + "syspll_d8", + "syspll_d10", + "univpll1_d6", + "univpll1_d8" +}; + +static const char * const uart_parents[] __initconst = { + "clk26m", + "univpll2_d8" +}; + +static const char * const mem_parents[] __initconst = { + "clk26m", + "clkph_mck" +}; + +static const char * const camtg_parents[] __initconst = { + "clk26m", + "univpll_d26", + "univpll1_d6", + "syspll_d16", + "syspll_d8" +}; + +static const char * const audio_parents[] __initconst = { + "clk26m", + "syspll_d24" +}; + +static const char * const fix_parents[] __initconst = { + "rtc32k", + "clk26m", + "univpll_d5", + "univpll_d7", + "univpll1_d2", + "univpll1_d4", + "univpll1_d6", + "univpll1_d8" +}; + +static const char * const vdec_parents[] __initconst = { + "clk26m", + "vdecpll", + "clkph_mck", + "syspll_d2p5", + "syspll_d3", + "syspll_d3p5", + "syspll_d4", + "syspll_d5", + "syspll_d6", + "syspll_d8", + "univpll1_d2", + "univpll2_d2", + "univpll_d7", + "univpll_d10", + "univpll2_d4", + "lvdspll" +}; + +static const char * const ddrphycfg_parents[] __initconst = { + "clk26m", + "axi_sel", + "syspll_d12" +}; + +static const char * const dpilvds_parents[] __initconst = { + "clk26m", + "lvdspll", + "lvdspll_d2", + "lvdspll_d4", + "lvdspll_d8" +}; + +static const char * const pmicspi_parents[] __initconst = { + "clk26m", + "univpll2_d6", + "syspll_d8", + "syspll_d10", + "univpll1_d10", + "mempll_mck_d4", + "univpll_d26", + "syspll_d24" +}; + +static const char * const smi_mfg_as_parents[] __initconst = { + "clk26m", + "smi_sel", + "mfg_sel", + "mem_sel" +}; + +static const char * const gcpu_parents[] __initconst = { + "clk26m", + "syspll_d4", + "univpll_d7", + "syspll_d5", + "syspll_d6" +}; + +static const char * const dpi1_parents[] __initconst = { + "clk26m", + "tvhdmi_h_ck", + "tvhdmi_d2", + "tvhdmi_d4" +}; + +static const char * const cci_parents[] __initconst = { + "clk26m", + "mainpll_537p3m", + "univpll_d3", + "syspll_d2p5", + "syspll_d3", + "syspll_d5" +}; + +static const char * const apll_parents[] __initconst = { + "clk26m", + "apll_ck", + "apll_d4", + "apll_d8", + "apll_d16", + "apll_d24" +}; + +static const char * const hdmipll_parents[] __initconst = { + "clk26m", + "hdmitx_clkdig_cts", + "hdmitx_clkdig_d2", + "hdmitx_clkdig_d3" +}; + +static const struct mtk_composite top_muxes[] __initconst = { + /* CLK_CFG_0 */ + MUX_GATE(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, + 0x0140, 0, 3, INVALID_MUX_GATE_BIT), + MUX_GATE(CLK_TOP_SMI_SEL, "smi_sel", smi_parents, 0x0140, 8, 4, 15), + MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x0140, 16, 4, 23), + MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", irda_parents, 0x0140, 24, 2, 31), + /* CLK_CFG_1 */ + MUX_GATE(CLK_TOP_CAM_SEL, "cam_sel", cam_parents, 0x0144, 0, 3, 7), + MUX_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel", aud_intbus_parents, + 0x0144, 8, 2, 15), + MUX_GATE(CLK_TOP_JPG_SEL, "jpg_sel", jpg_parents, 0x0144, 16, 3, 23), + MUX_GATE(CLK_TOP_DISP_SEL, "disp_sel", disp_parents, 0x0144, 24, 3, 31), + /* CLK_CFG_2 */ + MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_parents, 0x0148, 0, 3, 7), + MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_parents, 0x0148, 8, 3, 15), + MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_parents, 0x0148, 16, 3, 23), + MUX_GATE(CLK_TOP_MSDC30_4_SEL, "msdc30_4_sel", msdc30_parents, 0x0148, 24, 3, 31), + /* CLK_CFG_3 */ + MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", usb20_parents, 0x014c, 0, 2, 7), + /* CLK_CFG_4 */ + MUX_GATE(CLK_TOP_VENC_SEL, "venc_sel", venc_parents, 0x0150, 8, 3, 15), + MUX_GATE(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, 0x0150, 16, 3, 23), + MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents, 0x0150, 24, 2, 31), + /* CLK_CFG_6 */ + MUX_GATE(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, 0x0158, 0, 2, 7), + MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x0158, 8, 3, 15), + MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents, 0x0158, 24, 2, 31), + /* CLK_CFG_7 */ + MUX_GATE(CLK_TOP_FIX_SEL, "fix_sel", fix_parents, 0x015c, 0, 3, 7), + MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_parents, 0x015c, 8, 4, 15), + MUX_GATE(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents, + 0x015c, 16, 2, 23), + MUX_GATE(CLK_TOP_DPILVDS_SEL, "dpilvds_sel", dpilvds_parents, 0x015c, 24, 3, 31), + /* CLK_CFG_8 */ + MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, 0x0164, 0, 3, 7), + MUX_GATE(CLK_TOP_MSDC30_0_SEL, "msdc30_0_sel", msdc30_parents, 0x0164, 8, 3, 15), + MUX_GATE(CLK_TOP_SMI_MFG_AS_SEL, "smi_mfg_as_sel", smi_mfg_as_parents, + 0x0164, 16, 2, 23), + MUX_GATE(CLK_TOP_GCPU_SEL, "gcpu_sel", gcpu_parents, 0x0164, 24, 3, 31), + /* CLK_CFG_9 */ + MUX_GATE(CLK_TOP_DPI1_SEL, "dpi1_sel", dpi1_parents, 0x0168, 0, 2, 7), + MUX_GATE(CLK_TOP_CCI_SEL, "cci_sel", cci_parents, 0x0168, 8, 3, 15), + MUX_GATE(CLK_TOP_APLL_SEL, "apll_sel", apll_parents, 0x0168, 16, 3, 23), + MUX_GATE(CLK_TOP_HDMIPLL_SEL, "hdmipll_sel", hdmipll_parents, 0x0168, 24, 2, 31), +}; + +static const struct mtk_gate_regs infra_cg_regs = { + .set_ofs = 0x0040, + .clr_ofs = 0x0044, + .sta_ofs = 0x0048, +}; + +#define GATE_ICG(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate infra_clks[] __initconst = { + GATE_ICG(CLK_INFRA_PMIC_WRAP, "pmic_wrap_ck", "axi_sel", 23), + GATE_ICG(CLK_INFRA_PMICSPI, "pmicspi_ck", "pmicspi_sel", 22), + GATE_ICG(CLK_INFRA_CCIF1_AP_CTRL, "ccif1_ap_ctrl", "axi_sel", 21), + GATE_ICG(CLK_INFRA_CCIF0_AP_CTRL, "ccif0_ap_ctrl", "axi_sel", 20), + GATE_ICG(CLK_INFRA_KP, "kp_ck", "axi_sel", 16), + GATE_ICG(CLK_INFRA_CPUM, "cpum_ck", "cpum_tck_in", 15), + GATE_ICG(CLK_INFRA_M4U, "m4u_ck", "mem_sel", 8), + GATE_ICG(CLK_INFRA_MFGAXI, "mfgaxi_ck", "axi_sel", 7), + GATE_ICG(CLK_INFRA_DEVAPC, "devapc_ck", "axi_sel", 6), + GATE_ICG(CLK_INFRA_AUDIO, "audio_ck", "aud_intbus_sel", 5), + GATE_ICG(CLK_INFRA_MFG_BUS, "mfg_bus_ck", "axi_sel", 2), + GATE_ICG(CLK_INFRA_SMI, "smi_ck", "smi_sel", 1), + GATE_ICG(CLK_INFRA_DBGCLK, "dbgclk_ck", "axi_sel", 0), +}; + +static const struct mtk_gate_regs peri0_cg_regs = { + .set_ofs = 0x0008, + .clr_ofs = 0x0010, + .sta_ofs = 0x0018, +}; + +static const struct mtk_gate_regs peri1_cg_regs = { + .set_ofs = 0x000c, + .clr_ofs = 0x0014, + .sta_ofs = 0x001c, +}; + +#define GATE_PERI0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_PERI1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate peri_gates[] __initconst = { + /* PERI0 */ + GATE_PERI0(CLK_PERI_I2C5, "i2c5_ck", "axi_sel", 31), + GATE_PERI0(CLK_PERI_I2C4, "i2c4_ck", "axi_sel", 30), + GATE_PERI0(CLK_PERI_I2C3, "i2c3_ck", "axi_sel", 29), + GATE_PERI0(CLK_PERI_I2C2, "i2c2_ck", "axi_sel", 28), + GATE_PERI0(CLK_PERI_I2C1, "i2c1_ck", "axi_sel", 27), + GATE_PERI0(CLK_PERI_I2C0, "i2c0_ck", "axi_sel", 26), + GATE_PERI0(CLK_PERI_UART3, "uart3_ck", "axi_sel", 25), + GATE_PERI0(CLK_PERI_UART2, "uart2_ck", "axi_sel", 24), + GATE_PERI0(CLK_PERI_UART1, "uart1_ck", "axi_sel", 23), + GATE_PERI0(CLK_PERI_UART0, "uart0_ck", "axi_sel", 22), + GATE_PERI0(CLK_PERI_IRDA, "irda_ck", "irda_sel", 21), + GATE_PERI0(CLK_PERI_NLI, "nli_ck", "axi_sel", 20), + GATE_PERI0(CLK_PERI_MD_HIF, "md_hif_ck", "axi_sel", 19), + GATE_PERI0(CLK_PERI_AP_HIF, "ap_hif_ck", "axi_sel", 18), + GATE_PERI0(CLK_PERI_MSDC30_3, "msdc30_3_ck", "msdc30_4_sel", 17), + GATE_PERI0(CLK_PERI_MSDC30_2, "msdc30_2_ck", "msdc30_3_sel", 16), + GATE_PERI0(CLK_PERI_MSDC30_1, "msdc30_1_ck", "msdc30_2_sel", 15), + GATE_PERI0(CLK_PERI_MSDC20_2, "msdc20_2_ck", "msdc30_1_sel", 14), + GATE_PERI0(CLK_PERI_MSDC20_1, "msdc20_1_ck", "msdc30_0_sel", 13), + GATE_PERI0(CLK_PERI_AP_DMA, "ap_dma_ck", "axi_sel", 12), + GATE_PERI0(CLK_PERI_USB1, "usb1_ck", "usb20_sel", 11), + GATE_PERI0(CLK_PERI_USB0, "usb0_ck", "usb20_sel", 10), + GATE_PERI0(CLK_PERI_PWM, "pwm_ck", "axi_sel", 9), + GATE_PERI0(CLK_PERI_PWM7, "pwm7_ck", "axi_sel", 8), + GATE_PERI0(CLK_PERI_PWM6, "pwm6_ck", "axi_sel", 7), + GATE_PERI0(CLK_PERI_PWM5, "pwm5_ck", "axi_sel", 6), + GATE_PERI0(CLK_PERI_PWM4, "pwm4_ck", "axi_sel", 5), + GATE_PERI0(CLK_PERI_PWM3, "pwm3_ck", "axi_sel", 4), + GATE_PERI0(CLK_PERI_PWM2, "pwm2_ck", "axi_sel", 3), + GATE_PERI0(CLK_PERI_PWM1, "pwm1_ck", "axi_sel", 2), + GATE_PERI0(CLK_PERI_THERM, "therm_ck", "axi_sel", 1), + GATE_PERI0(CLK_PERI_NFI, "nfi_ck", "axi_sel", 0), + /* PERI1 */ + GATE_PERI1(CLK_PERI_USBSLV, "usbslv_ck", "axi_sel", 8), + GATE_PERI1(CLK_PERI_USB1_MCU, "usb1_mcu_ck", "axi_sel", 7), + GATE_PERI1(CLK_PERI_USB0_MCU, "usb0_mcu_ck", "axi_sel", 6), + GATE_PERI1(CLK_PERI_GCPU, "gcpu_ck", "gcpu_sel", 5), + GATE_PERI1(CLK_PERI_FHCTL, "fhctl_ck", "clk26m", 4), + GATE_PERI1(CLK_PERI_SPI1, "spi1_ck", "spi_sel", 3), + GATE_PERI1(CLK_PERI_AUXADC, "auxadc_ck", "clk26m", 2), + GATE_PERI1(CLK_PERI_PERI_PWRAP, "peri_pwrap_ck", "axi_sel", 1), + GATE_PERI1(CLK_PERI_I2C6, "i2c6_ck", "axi_sel", 0), +}; + +static const char * const uart_ck_sel_parents[] __initconst = { + "clk26m", + "uart_sel", +}; + +static const struct mtk_composite peri_clks[] __initconst = { + MUX(CLK_PERI_UART0_SEL, "uart0_ck_sel", uart_ck_sel_parents, 0x40c, 0, 1), + MUX(CLK_PERI_UART1_SEL, "uart1_ck_sel", uart_ck_sel_parents, 0x40c, 1, 1), + MUX(CLK_PERI_UART2_SEL, "uart2_ck_sel", uart_ck_sel_parents, 0x40c, 2, 1), + MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents, 0x40c, 3, 1), +}; + +static void __init mtk_topckgen_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK); + + mtk_clk_register_factors(root_clk_alias, ARRAY_SIZE(root_clk_alias), clk_data); + mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data); + mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base, + &mt8135_clk_lock, clk_data); + + clk_prepare_enable(clk_data->clks[CLK_TOP_CCI_SEL]); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8135-topckgen", mtk_topckgen_init); + +static void __init mtk_infrasys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK); + + mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), + clk_data); + + clk_prepare_enable(clk_data->clks[CLK_INFRA_M4U]); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_register_reset_controller(node, 2, 0x30); +} +CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8135-infracfg", mtk_infrasys_init); + +static void __init mtk_pericfg_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + void __iomem *base; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK); + + mtk_clk_register_gates(node, peri_gates, ARRAY_SIZE(peri_gates), + clk_data); + mtk_clk_register_composites(peri_clks, ARRAY_SIZE(peri_clks), base, + &mt8135_clk_lock, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_register_reset_controller(node, 2, 0); +} +CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8135-pericfg", mtk_pericfg_init); + +#define MT8135_PLL_FMAX (2000 * MHZ) +#define CON0_MT8135_RST_BAR BIT(27) + +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift) { \ + .id = _id, \ + .name = _name, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .flags = _flags, \ + .rst_bar_mask = CON0_MT8135_RST_BAR, \ + .fmax = MT8135_PLL_FMAX, \ + .pcwbits = _pcwbits, \ + .pd_reg = _pd_reg, \ + .pd_shift = _pd_shift, \ + .tuner_reg = _tuner_reg, \ + .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, \ + } + +static const struct mtk_pll_data plls[] = { + PLL(CLK_APMIXED_ARMPLL1, "armpll1", 0x200, 0x218, 0x80000001, 0, 21, 0x204, 24, 0x0, 0x204, 0), + PLL(CLK_APMIXED_ARMPLL2, "armpll2", 0x2cc, 0x2e4, 0x80000001, 0, 21, 0x2d0, 24, 0x0, 0x2d0, 0), + PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x21c, 0x234, 0xf0000001, HAVE_RST_BAR, 21, 0x21c, 6, 0x0, 0x220, 0), + PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x238, 0x250, 0xf3000001, HAVE_RST_BAR, 7, 0x238, 6, 0x0, 0x238, 9), + PLL(CLK_APMIXED_MMPLL, "mmpll", 0x254, 0x26c, 0xf0000001, HAVE_RST_BAR, 21, 0x254, 6, 0x0, 0x258, 0), + PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x278, 0x290, 0x80000001, 0, 21, 0x278, 6, 0x0, 0x27c, 0), + PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x294, 0x2ac, 0x80000001, 0, 31, 0x294, 6, 0x0, 0x298, 0), + PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x2b0, 0x2c8, 0x80000001, 0, 21, 0x2b0, 6, 0x0, 0x2b4, 0), + PLL(CLK_APMIXED_AUDPLL, "audpll", 0x2e8, 0x300, 0x80000001, 0, 31, 0x2e8, 6, 0x2f8, 0x2ec, 0), + PLL(CLK_APMIXED_VDECPLL, "vdecpll", 0x304, 0x31c, 0x80000001, 0, 21, 0x2b0, 6, 0x0, 0x308, 0), +}; + +static void __init mtk_apmixedsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + + clk_data = mtk_alloc_clk_data(ARRAY_SIZE(plls)); + if (!clk_data) + return; + + mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data); +} +CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8135-apmixedsys", + mtk_apmixedsys_init); diff --git a/include/dt-bindings/clock/mt8135-clk.h b/include/dt-bindings/clock/mt8135-clk.h new file mode 100644 index 000000000000..6dac6c091dd2 --- /dev/null +++ b/include/dt-bindings/clock/mt8135-clk.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DT_BINDINGS_CLK_MT8135_H +#define _DT_BINDINGS_CLK_MT8135_H + +/* TOPCKGEN */ + +#define CLK_TOP_DSI0_LNTC_DSICLK 1 +#define CLK_TOP_HDMITX_CLKDIG_CTS 2 +#define CLK_TOP_CLKPH_MCK 3 +#define CLK_TOP_CPUM_TCK_IN 4 +#define CLK_TOP_MAINPLL_806M 5 +#define CLK_TOP_MAINPLL_537P3M 6 +#define CLK_TOP_MAINPLL_322P4M 7 +#define CLK_TOP_MAINPLL_230P3M 8 +#define CLK_TOP_UNIVPLL_624M 9 +#define CLK_TOP_UNIVPLL_416M 10 +#define CLK_TOP_UNIVPLL_249P6M 11 +#define CLK_TOP_UNIVPLL_178P3M 12 +#define CLK_TOP_UNIVPLL_48M 13 +#define CLK_TOP_MMPLL_D2 14 +#define CLK_TOP_MMPLL_D3 15 +#define CLK_TOP_MMPLL_D5 16 +#define CLK_TOP_MMPLL_D7 17 +#define CLK_TOP_MMPLL_D4 18 +#define CLK_TOP_MMPLL_D6 19 +#define CLK_TOP_SYSPLL_D2 20 +#define CLK_TOP_SYSPLL_D4 21 +#define CLK_TOP_SYSPLL_D6 22 +#define CLK_TOP_SYSPLL_D8 23 +#define CLK_TOP_SYSPLL_D10 24 +#define CLK_TOP_SYSPLL_D12 25 +#define CLK_TOP_SYSPLL_D16 26 +#define CLK_TOP_SYSPLL_D24 27 +#define CLK_TOP_SYSPLL_D3 28 +#define CLK_TOP_SYSPLL_D2P5 29 +#define CLK_TOP_SYSPLL_D5 30 +#define CLK_TOP_SYSPLL_D3P5 31 +#define CLK_TOP_UNIVPLL1_D2 32 +#define CLK_TOP_UNIVPLL1_D4 33 +#define CLK_TOP_UNIVPLL1_D6 34 +#define CLK_TOP_UNIVPLL1_D8 35 +#define CLK_TOP_UNIVPLL1_D10 36 +#define CLK_TOP_UNIVPLL2_D2 37 +#define CLK_TOP_UNIVPLL2_D4 38 +#define CLK_TOP_UNIVPLL2_D6 39 +#define CLK_TOP_UNIVPLL2_D8 40 +#define CLK_TOP_UNIVPLL_D3 41 +#define CLK_TOP_UNIVPLL_D5 42 +#define CLK_TOP_UNIVPLL_D7 43 +#define CLK_TOP_UNIVPLL_D10 44 +#define CLK_TOP_UNIVPLL_D26 45 +#define CLK_TOP_APLL 46 +#define CLK_TOP_APLL_D4 47 +#define CLK_TOP_APLL_D8 48 +#define CLK_TOP_APLL_D16 49 +#define CLK_TOP_APLL_D24 50 +#define CLK_TOP_LVDSPLL_D2 51 +#define CLK_TOP_LVDSPLL_D4 52 +#define CLK_TOP_LVDSPLL_D8 53 +#define CLK_TOP_LVDSTX_CLKDIG_CT 54 +#define CLK_TOP_VPLL_DPIX 55 +#define CLK_TOP_TVHDMI_H 56 +#define CLK_TOP_HDMITX_CLKDIG_D2 57 +#define CLK_TOP_HDMITX_CLKDIG_D3 58 +#define CLK_TOP_TVHDMI_D2 59 +#define CLK_TOP_TVHDMI_D4 60 +#define CLK_TOP_MEMPLL_MCK_D4 61 +#define CLK_TOP_AXI_SEL 62 +#define CLK_TOP_SMI_SEL 63 +#define CLK_TOP_MFG_SEL 64 +#define CLK_TOP_IRDA_SEL 65 +#define CLK_TOP_CAM_SEL 66 +#define CLK_TOP_AUD_INTBUS_SEL 67 +#define CLK_TOP_JPG_SEL 68 +#define CLK_TOP_DISP_SEL 69 +#define CLK_TOP_MSDC30_1_SEL 70 +#define CLK_TOP_MSDC30_2_SEL 71 +#define CLK_TOP_MSDC30_3_SEL 72 +#define CLK_TOP_MSDC30_4_SEL 73 +#define CLK_TOP_USB20_SEL 74 +#define CLK_TOP_VENC_SEL 75 +#define CLK_TOP_SPI_SEL 76 +#define CLK_TOP_UART_SEL 77 +#define CLK_TOP_MEM_SEL 78 +#define CLK_TOP_CAMTG_SEL 79 +#define CLK_TOP_AUDIO_SEL 80 +#define CLK_TOP_FIX_SEL 81 +#define CLK_TOP_VDEC_SEL 82 +#define CLK_TOP_DDRPHYCFG_SEL 83 +#define CLK_TOP_DPILVDS_SEL 84 +#define CLK_TOP_PMICSPI_SEL 85 +#define CLK_TOP_MSDC30_0_SEL 86 +#define CLK_TOP_SMI_MFG_AS_SEL 87 +#define CLK_TOP_GCPU_SEL 88 +#define CLK_TOP_DPI1_SEL 89 +#define CLK_TOP_CCI_SEL 90 +#define CLK_TOP_APLL_SEL 91 +#define CLK_TOP_HDMIPLL_SEL 92 +#define CLK_TOP_NR_CLK 93 + +/* APMIXED_SYS */ + +#define CLK_APMIXED_ARMPLL1 1 +#define CLK_APMIXED_ARMPLL2 2 +#define CLK_APMIXED_MAINPLL 3 +#define CLK_APMIXED_UNIVPLL 4 +#define CLK_APMIXED_MMPLL 5 +#define CLK_APMIXED_MSDCPLL 6 +#define CLK_APMIXED_TVDPLL 7 +#define CLK_APMIXED_LVDSPLL 8 +#define CLK_APMIXED_AUDPLL 9 +#define CLK_APMIXED_VDECPLL 10 +#define CLK_APMIXED_NR_CLK 11 + +/* INFRA_SYS */ + +#define CLK_INFRA_PMIC_WRAP 1 +#define CLK_INFRA_PMICSPI 2 +#define CLK_INFRA_CCIF1_AP_CTRL 3 +#define CLK_INFRA_CCIF0_AP_CTRL 4 +#define CLK_INFRA_KP 5 +#define CLK_INFRA_CPUM 6 +#define CLK_INFRA_M4U 7 +#define CLK_INFRA_MFGAXI 8 +#define CLK_INFRA_DEVAPC 9 +#define CLK_INFRA_AUDIO 10 +#define CLK_INFRA_MFG_BUS 11 +#define CLK_INFRA_SMI 12 +#define CLK_INFRA_DBGCLK 13 +#define CLK_INFRA_NR_CLK 14 + +/* PERI_SYS */ + +#define CLK_PERI_I2C5 1 +#define CLK_PERI_I2C4 2 +#define CLK_PERI_I2C3 3 +#define CLK_PERI_I2C2 4 +#define CLK_PERI_I2C1 5 +#define CLK_PERI_I2C0 6 +#define CLK_PERI_UART3 7 +#define CLK_PERI_UART2 8 +#define CLK_PERI_UART1 9 +#define CLK_PERI_UART0 10 +#define CLK_PERI_IRDA 11 +#define CLK_PERI_NLI 12 +#define CLK_PERI_MD_HIF 13 +#define CLK_PERI_AP_HIF 14 +#define CLK_PERI_MSDC30_3 15 +#define CLK_PERI_MSDC30_2 16 +#define CLK_PERI_MSDC30_1 17 +#define CLK_PERI_MSDC20_2 18 +#define CLK_PERI_MSDC20_1 19 +#define CLK_PERI_AP_DMA 20 +#define CLK_PERI_USB1 21 +#define CLK_PERI_USB0 22 +#define CLK_PERI_PWM 23 +#define CLK_PERI_PWM7 24 +#define CLK_PERI_PWM6 25 +#define CLK_PERI_PWM5 26 +#define CLK_PERI_PWM4 27 +#define CLK_PERI_PWM3 28 +#define CLK_PERI_PWM2 29 +#define CLK_PERI_PWM1 30 +#define CLK_PERI_THERM 31 +#define CLK_PERI_NFI 32 +#define CLK_PERI_USBSLV 33 +#define CLK_PERI_USB1_MCU 34 +#define CLK_PERI_USB0_MCU 35 +#define CLK_PERI_GCPU 36 +#define CLK_PERI_FHCTL 37 +#define CLK_PERI_SPI1 38 +#define CLK_PERI_AUXADC 39 +#define CLK_PERI_PERI_PWRAP 40 +#define CLK_PERI_I2C6 41 +#define CLK_PERI_UART0_SEL 42 +#define CLK_PERI_UART1_SEL 43 +#define CLK_PERI_UART2_SEL 44 +#define CLK_PERI_UART3_SEL 45 +#define CLK_PERI_NR_CLK 46 + +#endif /* _DT_BINDINGS_CLK_MT8135_H */ diff --git a/include/dt-bindings/reset-controller/mt8135-resets.h b/include/dt-bindings/reset-controller/mt8135-resets.h new file mode 100644 index 000000000000..1fb629508db2 --- /dev/null +++ b/include/dt-bindings/reset-controller/mt8135-resets.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Flora Fu, MediaTek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT8135 +#define _DT_BINDINGS_RESET_CONTROLLER_MT8135 + +/* INFRACFG resets */ +#define MT8135_INFRA_EMI_REG_RST 0 +#define MT8135_INFRA_DRAMC0_A0_RST 1 +#define MT8135_INFRA_CCIF0_RST 2 +#define MT8135_INFRA_APCIRQ_EINT_RST 3 +#define MT8135_INFRA_APXGPT_RST 4 +#define MT8135_INFRA_SCPSYS_RST 5 +#define MT8135_INFRA_CCIF1_RST 6 +#define MT8135_INFRA_PMIC_WRAP_RST 7 +#define MT8135_INFRA_KP_RST 8 +#define MT8135_INFRA_EMI_RST 32 +#define MT8135_INFRA_DRAMC0_RST 34 +#define MT8135_INFRA_SMI_RST 35 +#define MT8135_INFRA_M4U_RST 36 + +/* PERICFG resets */ +#define MT8135_PERI_UART0_SW_RST 0 +#define MT8135_PERI_UART1_SW_RST 1 +#define MT8135_PERI_UART2_SW_RST 2 +#define MT8135_PERI_UART3_SW_RST 3 +#define MT8135_PERI_IRDA_SW_RST 4 +#define MT8135_PERI_PTP_SW_RST 5 +#define MT8135_PERI_AP_HIF_SW_RST 6 +#define MT8135_PERI_GPCU_SW_RST 7 +#define MT8135_PERI_MD_HIF_SW_RST 8 +#define MT8135_PERI_NLI_SW_RST 9 +#define MT8135_PERI_AUXADC_SW_RST 10 +#define MT8135_PERI_DMA_SW_RST 11 +#define MT8135_PERI_NFI_SW_RST 14 +#define MT8135_PERI_PWM_SW_RST 15 +#define MT8135_PERI_THERM_SW_RST 16 +#define MT8135_PERI_MSDC0_SW_RST 17 +#define MT8135_PERI_MSDC1_SW_RST 18 +#define MT8135_PERI_MSDC2_SW_RST 19 +#define MT8135_PERI_MSDC3_SW_RST 20 +#define MT8135_PERI_I2C0_SW_RST 22 +#define MT8135_PERI_I2C1_SW_RST 23 +#define MT8135_PERI_I2C2_SW_RST 24 +#define MT8135_PERI_I2C3_SW_RST 25 +#define MT8135_PERI_I2C4_SW_RST 26 +#define MT8135_PERI_I2C5_SW_RST 27 +#define MT8135_PERI_I2C6_SW_RST 28 +#define MT8135_PERI_USB_SW_RST 29 +#define MT8135_PERI_SPI1_SW_RST 33 +#define MT8135_PERI_PWRAP_BRIDGE_SW_RST 34 + +#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT8135 */ -- cgit v1.2.3-58-ga151 From c1e81a3bef36cb046c079480948fa3e0eca590d6 Mon Sep 17 00:00:00 2001 From: James Liao Date: Thu, 23 Apr 2015 10:35:42 +0200 Subject: clk: mediatek: Add basic clocks for Mediatek MT8173. This patch adds basic clocks for MT8173, including TOPCKGEN, PLLs, INFRA and PERI clocks. Signed-off-by: James Liao Signed-off-by: Henry Chen Signed-off-by: Sascha Hauer Signed-off-by: Stephen Boyd --- drivers/clk/mediatek/Makefile | 1 + drivers/clk/mediatek/clk-mt8173.c | 830 +++++++++++++++++++++ include/dt-bindings/clock/mt8173-clk.h | 235 ++++++ .../dt-bindings/reset-controller/mt8173-resets.h | 63 ++ 4 files changed, 1129 insertions(+) create mode 100644 drivers/clk/mediatek/clk-mt8173.c create mode 100644 include/dt-bindings/clock/mt8173-clk.h create mode 100644 include/dt-bindings/reset-controller/mt8173-resets.h diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile index 12ce576e628a..8e4b2a4635b9 100644 --- a/drivers/clk/mediatek/Makefile +++ b/drivers/clk/mediatek/Makefile @@ -1,3 +1,4 @@ obj-y += clk-mtk.o clk-pll.o clk-gate.o obj-$(CONFIG_RESET_CONTROLLER) += reset.o obj-y += clk-mt8135.o +obj-y += clk-mt8173.o diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c new file mode 100644 index 000000000000..357b080eb04f --- /dev/null +++ b/drivers/clk/mediatek/clk-mt8173.c @@ -0,0 +1,830 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include + +static DEFINE_SPINLOCK(mt8173_clk_lock); + +static const struct mtk_fixed_factor root_clk_alias[] __initconst = { + FACTOR(CLK_TOP_CLKPH_MCK_O, "clkph_mck_o", "clk_null", 1, 1), + FACTOR(CLK_TOP_DPI, "dpi_ck", "clk_null", 1, 1), + FACTOR(CLK_TOP_USB_SYSPLL_125M, "usb_syspll_125m", "clk_null", 1, 1), + FACTOR(CLK_TOP_HDMITX_DIG_CTS, "hdmitx_dig_cts", "clk_null", 1, 1), +}; + +static const struct mtk_fixed_factor top_divs[] __initconst = { + FACTOR(CLK_TOP_ARMCA7PLL_754M, "armca7pll_754m", "armca7pll", 1, 2), + FACTOR(CLK_TOP_ARMCA7PLL_502M, "armca7pll_502m", "armca7pll", 1, 3), + + FACTOR(CLK_TOP_MAIN_H546M, "main_h546m", "mainpll", 1, 2), + FACTOR(CLK_TOP_MAIN_H364M, "main_h364m", "mainpll", 1, 3), + FACTOR(CLK_TOP_MAIN_H218P4M, "main_h218p4m", "mainpll", 1, 5), + FACTOR(CLK_TOP_MAIN_H156M, "main_h156m", "mainpll", 1, 7), + + FACTOR(CLK_TOP_TVDPLL_445P5M, "tvdpll_445p5m", "tvdpll", 1, 4), + FACTOR(CLK_TOP_TVDPLL_594M, "tvdpll_594m", "tvdpll", 1, 3), + + FACTOR(CLK_TOP_UNIV_624M, "univ_624m", "univpll", 1, 2), + FACTOR(CLK_TOP_UNIV_416M, "univ_416m", "univpll", 1, 3), + FACTOR(CLK_TOP_UNIV_249P6M, "univ_249p6m", "univpll", 1, 5), + FACTOR(CLK_TOP_UNIV_178P3M, "univ_178p3m", "univpll", 1, 7), + FACTOR(CLK_TOP_UNIV_48M, "univ_48m", "univpll", 1, 26), + + FACTOR(CLK_TOP_CLKRTC_EXT, "clkrtc_ext", "clk32k", 1, 1), + FACTOR(CLK_TOP_CLKRTC_INT, "clkrtc_int", "clk26m", 1, 793), + FACTOR(CLK_TOP_FPC, "fpc_ck", "clk26m", 1, 1), + + FACTOR(CLK_TOP_HDMITXPLL_D2, "hdmitxpll_d2", "hdmitx_dig_cts", 1, 2), + FACTOR(CLK_TOP_HDMITXPLL_D3, "hdmitxpll_d3", "hdmitx_dig_cts", 1, 3), + + FACTOR(CLK_TOP_ARMCA7PLL_D2, "armca7pll_d2", "armca7pll_754m", 1, 1), + FACTOR(CLK_TOP_ARMCA7PLL_D3, "armca7pll_d3", "armca7pll_502m", 1, 1), + + FACTOR(CLK_TOP_APLL1, "apll1_ck", "apll1", 1, 1), + FACTOR(CLK_TOP_APLL2, "apll2_ck", "apll2", 1, 1), + + FACTOR(CLK_TOP_DMPLL, "dmpll_ck", "clkph_mck_o", 1, 1), + FACTOR(CLK_TOP_DMPLL_D2, "dmpll_d2", "clkph_mck_o", 1, 2), + FACTOR(CLK_TOP_DMPLL_D4, "dmpll_d4", "clkph_mck_o", 1, 4), + FACTOR(CLK_TOP_DMPLL_D8, "dmpll_d8", "clkph_mck_o", 1, 8), + FACTOR(CLK_TOP_DMPLL_D16, "dmpll_d16", "clkph_mck_o", 1, 16), + + FACTOR(CLK_TOP_LVDSPLL_D2, "lvdspll_d2", "lvdspll", 1, 2), + FACTOR(CLK_TOP_LVDSPLL_D4, "lvdspll_d4", "lvdspll", 1, 4), + FACTOR(CLK_TOP_LVDSPLL_D8, "lvdspll_d8", "lvdspll", 1, 8), + + FACTOR(CLK_TOP_MMPLL, "mmpll_ck", "mmpll", 1, 1), + FACTOR(CLK_TOP_MMPLL_D2, "mmpll_d2", "mmpll", 1, 2), + + FACTOR(CLK_TOP_MSDCPLL, "msdcpll_ck", "msdcpll", 1, 1), + FACTOR(CLK_TOP_MSDCPLL_D2, "msdcpll_d2", "msdcpll", 1, 2), + FACTOR(CLK_TOP_MSDCPLL_D4, "msdcpll_d4", "msdcpll", 1, 4), + FACTOR(CLK_TOP_MSDCPLL2, "msdcpll2_ck", "msdcpll2", 1, 1), + FACTOR(CLK_TOP_MSDCPLL2_D2, "msdcpll2_d2", "msdcpll2", 1, 2), + FACTOR(CLK_TOP_MSDCPLL2_D4, "msdcpll2_d4", "msdcpll2", 1, 4), + + FACTOR(CLK_TOP_SYSPLL_D2, "syspll_d2", "main_h546m", 1, 1), + FACTOR(CLK_TOP_SYSPLL1_D2, "syspll1_d2", "main_h546m", 1, 2), + FACTOR(CLK_TOP_SYSPLL1_D4, "syspll1_d4", "main_h546m", 1, 4), + FACTOR(CLK_TOP_SYSPLL1_D8, "syspll1_d8", "main_h546m", 1, 8), + FACTOR(CLK_TOP_SYSPLL1_D16, "syspll1_d16", "main_h546m", 1, 16), + FACTOR(CLK_TOP_SYSPLL_D3, "syspll_d3", "main_h364m", 1, 1), + FACTOR(CLK_TOP_SYSPLL2_D2, "syspll2_d2", "main_h364m", 1, 2), + FACTOR(CLK_TOP_SYSPLL2_D4, "syspll2_d4", "main_h364m", 1, 4), + FACTOR(CLK_TOP_SYSPLL_D5, "syspll_d5", "main_h218p4m", 1, 1), + FACTOR(CLK_TOP_SYSPLL3_D2, "syspll3_d2", "main_h218p4m", 1, 2), + FACTOR(CLK_TOP_SYSPLL3_D4, "syspll3_d4", "main_h218p4m", 1, 4), + FACTOR(CLK_TOP_SYSPLL_D7, "syspll_d7", "main_h156m", 1, 1), + FACTOR(CLK_TOP_SYSPLL4_D2, "syspll4_d2", "main_h156m", 1, 2), + FACTOR(CLK_TOP_SYSPLL4_D4, "syspll4_d4", "main_h156m", 1, 4), + + FACTOR(CLK_TOP_TVDPLL, "tvdpll_ck", "tvdpll_594m", 1, 1), + FACTOR(CLK_TOP_TVDPLL_D2, "tvdpll_d2", "tvdpll_594m", 1, 2), + FACTOR(CLK_TOP_TVDPLL_D4, "tvdpll_d4", "tvdpll_594m", 1, 4), + FACTOR(CLK_TOP_TVDPLL_D8, "tvdpll_d8", "tvdpll_594m", 1, 8), + FACTOR(CLK_TOP_TVDPLL_D16, "tvdpll_d16", "tvdpll_594m", 1, 16), + + FACTOR(CLK_TOP_UNIVPLL_D2, "univpll_d2", "univ_624m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL1_D2, "univpll1_d2", "univ_624m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL1_D4, "univpll1_d4", "univ_624m", 1, 4), + FACTOR(CLK_TOP_UNIVPLL1_D8, "univpll1_d8", "univ_624m", 1, 8), + FACTOR(CLK_TOP_UNIVPLL_D3, "univpll_d3", "univ_416m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL2_D2, "univpll2_d2", "univ_416m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL2_D4, "univpll2_d4", "univ_416m", 1, 4), + FACTOR(CLK_TOP_UNIVPLL2_D8, "univpll2_d8", "univ_416m", 1, 8), + FACTOR(CLK_TOP_UNIVPLL_D5, "univpll_d5", "univ_249p6m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL3_D2, "univpll3_d2", "univ_249p6m", 1, 2), + FACTOR(CLK_TOP_UNIVPLL3_D4, "univpll3_d4", "univ_249p6m", 1, 4), + FACTOR(CLK_TOP_UNIVPLL3_D8, "univpll3_d8", "univ_249p6m", 1, 8), + FACTOR(CLK_TOP_UNIVPLL_D7, "univpll_d7", "univ_178p3m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D26, "univpll_d26", "univ_48m", 1, 1), + FACTOR(CLK_TOP_UNIVPLL_D52, "univpll_d52", "univ_48m", 1, 2), + + FACTOR(CLK_TOP_VCODECPLL, "vcodecpll_ck", "vcodecpll", 1, 3), + FACTOR(CLK_TOP_VCODECPLL_370P5, "vcodecpll_370p5", "vcodecpll", 1, 4), + + FACTOR(CLK_TOP_VENCPLL, "vencpll_ck", "vencpll", 1, 1), + FACTOR(CLK_TOP_VENCPLL_D2, "vencpll_d2", "vencpll", 1, 2), + FACTOR(CLK_TOP_VENCPLL_D4, "vencpll_d4", "vencpll", 1, 4), +}; + +static const char * const axi_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "syspll_d5", + "syspll1_d4", + "univpll_d5", + "univpll2_d2", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const mem_parents[] __initconst = { + "clk26m", + "dmpll_ck" +}; + +static const char * const ddrphycfg_parents[] __initconst = { + "clk26m", + "syspll1_d8" +}; + +static const char * const mm_parents[] __initconst = { + "clk26m", + "vencpll_d2", + "main_h364m", + "syspll1_d2", + "syspll_d5", + "syspll1_d4", + "univpll1_d2", + "univpll2_d2", + "dmpll_d2" +}; + +static const char * const pwm_parents[] __initconst = { + "clk26m", + "univpll2_d4", + "univpll3_d2", + "univpll1_d4" +}; + +static const char * const vdec_parents[] __initconst = { + "clk26m", + "vcodecpll_ck", + "tvdpll_445p5m", + "univpll_d3", + "vencpll_d2", + "syspll_d3", + "univpll1_d2", + "mmpll_d2", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const venc_parents[] __initconst = { + "clk26m", + "vcodecpll_ck", + "tvdpll_445p5m", + "univpll_d3", + "vencpll_d2", + "syspll_d3", + "univpll1_d2", + "univpll2_d2", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const mfg_parents[] __initconst = { + "clk26m", + "mmpll_ck", + "dmpll_ck", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "clk26m", + "syspll_d3", + "syspll1_d2", + "syspll_d5", + "univpll_d3", + "univpll1_d2", + "univpll_d5", + "univpll2_d2" +}; + +static const char * const camtg_parents[] __initconst = { + "clk26m", + "univpll_d26", + "univpll2_d2", + "syspll3_d2", + "syspll3_d4", + "univpll1_d4" +}; + +static const char * const uart_parents[] __initconst = { + "clk26m", + "univpll2_d8" +}; + +static const char * const spi_parents[] __initconst = { + "clk26m", + "syspll3_d2", + "syspll1_d4", + "syspll4_d2", + "univpll3_d2", + "univpll2_d4", + "univpll1_d8" +}; + +static const char * const usb20_parents[] __initconst = { + "clk26m", + "univpll1_d8", + "univpll3_d4" +}; + +static const char * const usb30_parents[] __initconst = { + "clk26m", + "univpll3_d2", + "usb_syspll_125m", + "univpll2_d4" +}; + +static const char * const msdc50_0_h_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "syspll2_d2", + "syspll4_d2", + "univpll_d5", + "univpll1_d4" +}; + +static const char * const msdc50_0_parents[] __initconst = { + "clk26m", + "msdcpll_ck", + "msdcpll_d2", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "msdcpll_d4", + "vencpll_d4", + "tvdpll_ck", + "univpll_d2", + "univpll1_d2", + "mmpll_ck", + "msdcpll2_ck", + "msdcpll2_d2", + "msdcpll2_d4" +}; + +static const char * const msdc30_1_parents[] __initconst = { + "clk26m", + "univpll2_d2", + "msdcpll_d4", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "univpll_d7", + "vencpll_d4" +}; + +static const char * const msdc30_2_parents[] __initconst = { + "clk26m", + "univpll2_d2", + "msdcpll_d4", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "univpll_d7", + "vencpll_d2" +}; + +static const char * const msdc30_3_parents[] __initconst = { + "clk26m", + "msdcpll2_ck", + "msdcpll2_d2", + "univpll2_d2", + "msdcpll2_d4", + "msdcpll_d4", + "univpll1_d4", + "syspll2_d2", + "syspll_d7", + "univpll_d7", + "vencpll_d4", + "msdcpll_ck", + "msdcpll_d2", + "msdcpll_d4" +}; + +static const char * const audio_parents[] __initconst = { + "clk26m", + "syspll3_d4", + "syspll4_d4", + "syspll1_d16" +}; + +static const char * const aud_intbus_parents[] __initconst = { + "clk26m", + "syspll1_d4", + "syspll4_d2", + "univpll3_d2", + "univpll2_d8", + "dmpll_d4", + "dmpll_d8" +}; + +static const char * const pmicspi_parents[] __initconst = { + "clk26m", + "syspll1_d8", + "syspll3_d4", + "syspll1_d16", + "univpll3_d4", + "univpll_d26", + "dmpll_d8", + "dmpll_d16" +}; + +static const char * const scp_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "univpll_d5", + "syspll_d5", + "dmpll_d2", + "dmpll_d4" +}; + +static const char * const atb_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "univpll_d5", + "dmpll_d2" +}; + +static const char * const venc_lt_parents[] __initconst = { + "clk26m", + "univpll_d3", + "vcodecpll_ck", + "tvdpll_445p5m", + "vencpll_d2", + "syspll_d3", + "univpll1_d2", + "univpll2_d2", + "syspll1_d2", + "univpll_d5", + "vcodecpll_370p5", + "dmpll_ck" +}; + +static const char * const dpi0_parents[] __initconst = { + "clk26m", + "tvdpll_d2", + "tvdpll_d4", + "clk26m", + "clk26m", + "tvdpll_d8", + "tvdpll_d16" +}; + +static const char * const irda_parents[] __initconst = { + "clk26m", + "univpll2_d4", + "syspll2_d4" +}; + +static const char * const cci400_parents[] __initconst = { + "clk26m", + "vencpll_ck", + "armca7pll_754m", + "armca7pll_502m", + "univpll_d2", + "syspll_d2", + "msdcpll_ck", + "dmpll_ck" +}; + +static const char * const aud_1_parents[] __initconst = { + "clk26m", + "apll1_ck", + "univpll2_d4", + "univpll2_d8" +}; + +static const char * const aud_2_parents[] __initconst = { + "clk26m", + "apll2_ck", + "univpll2_d4", + "univpll2_d8" +}; + +static const char * const mem_mfg_in_parents[] __initconst = { + "clk26m", + "mmpll_ck", + "dmpll_ck", + "clk26m" +}; + +static const char * const axi_mfg_in_parents[] __initconst = { + "clk26m", + "axi_sel", + "dmpll_d2" +}; + +static const char * const scam_parents[] __initconst = { + "clk26m", + "syspll3_d2", + "univpll2_d4", + "dmpll_d4" +}; + +static const char * const spinfi_ifr_parents[] __initconst = { + "clk26m", + "univpll2_d8", + "univpll3_d4", + "syspll4_d2", + "univpll2_d4", + "univpll3_d2", + "syspll1_d4", + "univpll1_d4" +}; + +static const char * const hdmi_parents[] __initconst = { + "clk26m", + "hdmitx_dig_cts", + "hdmitxpll_d2", + "hdmitxpll_d3" +}; + +static const char * const dpilvds_parents[] __initconst = { + "clk26m", + "lvdspll", + "lvdspll_d2", + "lvdspll_d4", + "lvdspll_d8", + "fpc_ck" +}; + +static const char * const msdc50_2_h_parents[] __initconst = { + "clk26m", + "syspll1_d2", + "syspll2_d2", + "syspll4_d2", + "univpll_d5", + "univpll1_d4" +}; + +static const char * const hdcp_parents[] __initconst = { + "clk26m", + "syspll4_d2", + "syspll3_d4", + "univpll2_d4" +}; + +static const char * const hdcp_24m_parents[] __initconst = { + "clk26m", + "univpll_d26", + "univpll_d52", + "univpll2_d8" +}; + +static const char * const rtc_parents[] __initconst = { + "clkrtc_int", + "clkrtc_ext", + "clk26m", + "univpll3_d8" +}; + +static const char * const i2s0_m_ck_parents[] __initconst = { + "apll1_div1", + "apll2_div1" +}; + +static const char * const i2s1_m_ck_parents[] __initconst = { + "apll1_div2", + "apll2_div2" +}; + +static const char * const i2s2_m_ck_parents[] __initconst = { + "apll1_div3", + "apll2_div3" +}; + +static const char * const i2s3_m_ck_parents[] __initconst = { + "apll1_div4", + "apll2_div4" +}; + +static const char * const i2s3_b_ck_parents[] __initconst = { + "apll1_div5", + "apll2_div5" +}; + +static const struct mtk_composite top_muxes[] __initconst = { + /* CLK_CFG_0 */ + MUX(CLK_TOP_AXI_SEL, "axi_sel", axi_parents, 0x0040, 0, 3), + MUX(CLK_TOP_MEM_SEL, "mem_sel", mem_parents, 0x0040, 8, 1), + MUX_GATE(CLK_TOP_DDRPHYCFG_SEL, "ddrphycfg_sel", ddrphycfg_parents, 0x0040, 16, 1, 23), + MUX_GATE(CLK_TOP_MM_SEL, "mm_sel", mm_parents, 0x0040, 24, 4, 31), + /* CLK_CFG_1 */ + MUX_GATE(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents, 0x0050, 0, 2, 7), + MUX_GATE(CLK_TOP_VDEC_SEL, "vdec_sel", vdec_parents, 0x0050, 8, 4, 15), + MUX_GATE(CLK_TOP_VENC_SEL, "venc_sel", venc_parents, 0x0050, 16, 4, 23), + MUX_GATE(CLK_TOP_MFG_SEL, "mfg_sel", mfg_parents, 0x0050, 24, 4, 31), + /* CLK_CFG_2 */ + MUX_GATE(CLK_TOP_CAMTG_SEL, "camtg_sel", camtg_parents, 0x0060, 0, 3, 7), + MUX_GATE(CLK_TOP_UART_SEL, "uart_sel", uart_parents, 0x0060, 8, 1, 15), + MUX_GATE(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, 0x0060, 16, 3, 23), + MUX_GATE(CLK_TOP_USB20_SEL, "usb20_sel", usb20_parents, 0x0060, 24, 2, 31), + /* CLK_CFG_3 */ + MUX_GATE(CLK_TOP_USB30_SEL, "usb30_sel", usb30_parents, 0x0070, 0, 2, 7), + MUX_GATE(CLK_TOP_MSDC50_0_H_SEL, "msdc50_0_h_sel", msdc50_0_h_parents, 0x0070, 8, 3, 15), + MUX_GATE(CLK_TOP_MSDC50_0_SEL, "msdc50_0_sel", msdc50_0_parents, 0x0070, 16, 4, 23), + MUX_GATE(CLK_TOP_MSDC30_1_SEL, "msdc30_1_sel", msdc30_1_parents, 0x0070, 24, 3, 31), + /* CLK_CFG_4 */ + MUX_GATE(CLK_TOP_MSDC30_2_SEL, "msdc30_2_sel", msdc30_2_parents, 0x0080, 0, 3, 7), + MUX_GATE(CLK_TOP_MSDC30_3_SEL, "msdc30_3_sel", msdc30_3_parents, 0x0080, 8, 4, 15), + MUX_GATE(CLK_TOP_AUDIO_SEL, "audio_sel", audio_parents, 0x0080, 16, 2, 23), + MUX_GATE(CLK_TOP_AUD_INTBUS_SEL, "aud_intbus_sel", aud_intbus_parents, 0x0080, 24, 3, 31), + /* CLK_CFG_5 */ + MUX_GATE(CLK_TOP_PMICSPI_SEL, "pmicspi_sel", pmicspi_parents, 0x0090, 0, 3, 7 /* 7:5 */), + MUX_GATE(CLK_TOP_SCP_SEL, "scp_sel", scp_parents, 0x0090, 8, 3, 15), + MUX_GATE(CLK_TOP_ATB_SEL, "atb_sel", atb_parents, 0x0090, 16, 2, 23), + MUX_GATE(CLK_TOP_VENC_LT_SEL, "venclt_sel", venc_lt_parents, 0x0090, 24, 4, 31), + /* CLK_CFG_6 */ + MUX_GATE(CLK_TOP_DPI0_SEL, "dpi0_sel", dpi0_parents, 0x00a0, 0, 3, 7), + MUX_GATE(CLK_TOP_IRDA_SEL, "irda_sel", irda_parents, 0x00a0, 8, 2, 15), + MUX_GATE(CLK_TOP_CCI400_SEL, "cci400_sel", cci400_parents, 0x00a0, 16, 3, 23), + MUX_GATE(CLK_TOP_AUD_1_SEL, "aud_1_sel", aud_1_parents, 0x00a0, 24, 2, 31), + /* CLK_CFG_7 */ + MUX_GATE(CLK_TOP_AUD_2_SEL, "aud_2_sel", aud_2_parents, 0x00b0, 0, 2, 7), + MUX_GATE(CLK_TOP_MEM_MFG_IN_SEL, "mem_mfg_in_sel", mem_mfg_in_parents, 0x00b0, 8, 2, 15), + MUX_GATE(CLK_TOP_AXI_MFG_IN_SEL, "axi_mfg_in_sel", axi_mfg_in_parents, 0x00b0, 16, 2, 23), + MUX_GATE(CLK_TOP_SCAM_SEL, "scam_sel", scam_parents, 0x00b0, 24, 2, 31), + /* CLK_CFG_12 */ + MUX_GATE(CLK_TOP_SPINFI_IFR_SEL, "spinfi_ifr_sel", spinfi_ifr_parents, 0x00c0, 0, 3, 7), + MUX_GATE(CLK_TOP_HDMI_SEL, "hdmi_sel", hdmi_parents, 0x00c0, 8, 2, 15), + MUX_GATE(CLK_TOP_DPILVDS_SEL, "dpilvds_sel", dpilvds_parents, 0x00c0, 24, 3, 31), + /* CLK_CFG_13 */ + MUX_GATE(CLK_TOP_MSDC50_2_H_SEL, "msdc50_2_h_sel", msdc50_2_h_parents, 0x00d0, 0, 3, 7), + MUX_GATE(CLK_TOP_HDCP_SEL, "hdcp_sel", hdcp_parents, 0x00d0, 8, 2, 15), + MUX_GATE(CLK_TOP_HDCP_24M_SEL, "hdcp_24m_sel", hdcp_24m_parents, 0x00d0, 16, 2, 23), + MUX(CLK_TOP_RTC_SEL, "rtc_sel", rtc_parents, 0x00d0, 24, 2), + + DIV_GATE(CLK_TOP_APLL1_DIV0, "apll1_div0", "aud_1_sel", 0x12c, 8, 0x120, 4, 24), + DIV_GATE(CLK_TOP_APLL1_DIV1, "apll1_div1", "aud_1_sel", 0x12c, 9, 0x124, 8, 0), + DIV_GATE(CLK_TOP_APLL1_DIV2, "apll1_div2", "aud_1_sel", 0x12c, 10, 0x124, 8, 8), + DIV_GATE(CLK_TOP_APLL1_DIV3, "apll1_div3", "aud_1_sel", 0x12c, 11, 0x124, 8, 16), + DIV_GATE(CLK_TOP_APLL1_DIV4, "apll1_div4", "aud_1_sel", 0x12c, 12, 0x124, 8, 24), + DIV_GATE(CLK_TOP_APLL1_DIV5, "apll1_div5", "apll1_div4", 0x12c, 13, 0x12c, 4, 0), + + DIV_GATE(CLK_TOP_APLL2_DIV0, "apll2_div0", "aud_2_sel", 0x12c, 16, 0x120, 4, 28), + DIV_GATE(CLK_TOP_APLL2_DIV1, "apll2_div1", "aud_2_sel", 0x12c, 17, 0x128, 8, 0), + DIV_GATE(CLK_TOP_APLL2_DIV2, "apll2_div2", "aud_2_sel", 0x12c, 18, 0x128, 8, 8), + DIV_GATE(CLK_TOP_APLL2_DIV3, "apll2_div3", "aud_2_sel", 0x12c, 19, 0x128, 8, 16), + DIV_GATE(CLK_TOP_APLL2_DIV4, "apll2_div4", "aud_2_sel", 0x12c, 20, 0x128, 8, 24), + DIV_GATE(CLK_TOP_APLL2_DIV5, "apll2_div5", "apll2_div4", 0x12c, 21, 0x12c, 4, 4), + + MUX(CLK_TOP_I2S0_M_SEL, "i2s0_m_ck_sel", i2s0_m_ck_parents, 0x120, 4, 1), + MUX(CLK_TOP_I2S1_M_SEL, "i2s1_m_ck_sel", i2s1_m_ck_parents, 0x120, 5, 1), + MUX(CLK_TOP_I2S2_M_SEL, "i2s2_m_ck_sel", i2s2_m_ck_parents, 0x120, 6, 1), + MUX(CLK_TOP_I2S3_M_SEL, "i2s3_m_ck_sel", i2s3_m_ck_parents, 0x120, 7, 1), + MUX(CLK_TOP_I2S3_B_SEL, "i2s3_b_ck_sel", i2s3_b_ck_parents, 0x120, 8, 1), +}; + +static const struct mtk_gate_regs infra_cg_regs = { + .set_ofs = 0x0040, + .clr_ofs = 0x0044, + .sta_ofs = 0x0048, +}; + +#define GATE_ICG(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &infra_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate infra_clks[] __initconst = { + GATE_ICG(CLK_INFRA_DBGCLK, "infra_dbgclk", "axi_sel", 0), + GATE_ICG(CLK_INFRA_SMI, "infra_smi", "mm_sel", 1), + GATE_ICG(CLK_INFRA_AUDIO, "infra_audio", "aud_intbus_sel", 5), + GATE_ICG(CLK_INFRA_GCE, "infra_gce", "axi_sel", 6), + GATE_ICG(CLK_INFRA_L2C_SRAM, "infra_l2c_sram", "axi_sel", 7), + GATE_ICG(CLK_INFRA_M4U, "infra_m4u", "mem_sel", 8), + GATE_ICG(CLK_INFRA_CPUM, "infra_cpum", "clk_null", 15), + GATE_ICG(CLK_INFRA_KP, "infra_kp", "axi_sel", 16), + GATE_ICG(CLK_INFRA_CEC, "infra_cec", "clk26m", 18), + GATE_ICG(CLK_INFRA_PMICSPI, "infra_pmicspi", "pmicspi_sel", 22), + GATE_ICG(CLK_INFRA_PMICWRAP, "infra_pmicwrap", "axi_sel", 23), +}; + +static const struct mtk_gate_regs peri0_cg_regs = { + .set_ofs = 0x0008, + .clr_ofs = 0x0010, + .sta_ofs = 0x0018, +}; + +static const struct mtk_gate_regs peri1_cg_regs = { + .set_ofs = 0x000c, + .clr_ofs = 0x0014, + .sta_ofs = 0x001c, +}; + +#define GATE_PERI0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_PERI1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &peri1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate peri_gates[] __initconst = { + /* PERI0 */ + GATE_PERI0(CLK_PERI_NFI, "peri_nfi", "axi_sel", 0), + GATE_PERI0(CLK_PERI_THERM, "peri_therm", "axi_sel", 1), + GATE_PERI0(CLK_PERI_PWM1, "peri_pwm1", "axi_sel", 2), + GATE_PERI0(CLK_PERI_PWM2, "peri_pwm2", "axi_sel", 3), + GATE_PERI0(CLK_PERI_PWM3, "peri_pwm3", "axi_sel", 4), + GATE_PERI0(CLK_PERI_PWM4, "peri_pwm4", "axi_sel", 5), + GATE_PERI0(CLK_PERI_PWM5, "peri_pwm5", "axi_sel", 6), + GATE_PERI0(CLK_PERI_PWM6, "peri_pwm6", "axi_sel", 7), + GATE_PERI0(CLK_PERI_PWM7, "peri_pwm7", "axi_sel", 8), + GATE_PERI0(CLK_PERI_PWM, "peri_pwm", "axi_sel", 9), + GATE_PERI0(CLK_PERI_USB0, "peri_usb0", "usb20_sel", 10), + GATE_PERI0(CLK_PERI_USB1, "peri_usb1", "usb20_sel", 11), + GATE_PERI0(CLK_PERI_AP_DMA, "peri_ap_dma", "axi_sel", 12), + GATE_PERI0(CLK_PERI_MSDC30_0, "peri_msdc30_0", "msdc50_0_sel", 13), + GATE_PERI0(CLK_PERI_MSDC30_1, "peri_msdc30_1", "msdc30_1_sel", 14), + GATE_PERI0(CLK_PERI_MSDC30_2, "peri_msdc30_2", "msdc30_2_sel", 15), + GATE_PERI0(CLK_PERI_MSDC30_3, "peri_msdc30_3", "msdc30_3_sel", 16), + GATE_PERI0(CLK_PERI_NLI_ARB, "peri_nli_arb", "axi_sel", 17), + GATE_PERI0(CLK_PERI_IRDA, "peri_irda", "irda_sel", 18), + GATE_PERI0(CLK_PERI_UART0, "peri_uart0", "axi_sel", 19), + GATE_PERI0(CLK_PERI_UART1, "peri_uart1", "axi_sel", 20), + GATE_PERI0(CLK_PERI_UART2, "peri_uart2", "axi_sel", 21), + GATE_PERI0(CLK_PERI_UART3, "peri_uart3", "axi_sel", 22), + GATE_PERI0(CLK_PERI_I2C0, "peri_i2c0", "axi_sel", 23), + GATE_PERI0(CLK_PERI_I2C1, "peri_i2c1", "axi_sel", 24), + GATE_PERI0(CLK_PERI_I2C2, "peri_i2c2", "axi_sel", 25), + GATE_PERI0(CLK_PERI_I2C3, "peri_i2c3", "axi_sel", 26), + GATE_PERI0(CLK_PERI_I2C4, "peri_i2c4", "axi_sel", 27), + GATE_PERI0(CLK_PERI_AUXADC, "peri_auxadc", "clk26m", 28), + GATE_PERI0(CLK_PERI_SPI0, "peri_spi0", "spi_sel", 29), + GATE_PERI0(CLK_PERI_I2C5, "peri_i2c5", "axi_sel", 30), + GATE_PERI0(CLK_PERI_NFIECC, "peri_nfiecc", "axi_sel", 31), + /* PERI1 */ + GATE_PERI1(CLK_PERI_SPI, "peri_spi", "spi_sel", 0), + GATE_PERI1(CLK_PERI_IRRX, "peri_irrx", "spi_sel", 1), + GATE_PERI1(CLK_PERI_I2C6, "peri_i2c6", "axi_sel", 2), +}; + +static const char * const uart_ck_sel_parents[] __initconst = { + "clk26m", + "uart_sel", +}; + +static const struct mtk_composite peri_clks[] __initconst = { + MUX(CLK_PERI_UART0_SEL, "uart0_ck_sel", uart_ck_sel_parents, 0x40c, 0, 1), + MUX(CLK_PERI_UART1_SEL, "uart1_ck_sel", uart_ck_sel_parents, 0x40c, 1, 1), + MUX(CLK_PERI_UART2_SEL, "uart2_ck_sel", uart_ck_sel_parents, 0x40c, 2, 1), + MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents, 0x40c, 3, 1), +}; + +static void __init mtk_topckgen_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + void __iomem *base; + int r; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + clk_data = mtk_alloc_clk_data(CLK_TOP_NR_CLK); + + mtk_clk_register_factors(root_clk_alias, ARRAY_SIZE(root_clk_alias), clk_data); + mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data); + mtk_clk_register_composites(top_muxes, ARRAY_SIZE(top_muxes), base, + &mt8173_clk_lock, clk_data); + + clk_prepare_enable(clk_data->clks[CLK_TOP_CCI400_SEL]); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_topckgen, "mediatek,mt8173-topckgen", mtk_topckgen_init); + +static void __init mtk_infrasys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(CLK_INFRA_NR_CLK); + + mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_register_reset_controller(node, 2, 0x30); +} +CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8173-infracfg", mtk_infrasys_init); + +static void __init mtk_pericfg_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + void __iomem *base; + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return; + } + + clk_data = mtk_alloc_clk_data(CLK_PERI_NR_CLK); + + mtk_clk_register_gates(node, peri_gates, ARRAY_SIZE(peri_gates), + clk_data); + mtk_clk_register_composites(peri_clks, ARRAY_SIZE(peri_clks), base, + &mt8173_clk_lock, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + + mtk_register_reset_controller(node, 2, 0); +} +CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8173-pericfg", mtk_pericfg_init); + +#define MT8173_PLL_FMAX (3000UL * MHZ) + +#define CON0_MT8173_RST_BAR BIT(24) + +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, _pd_shift, \ + _tuner_reg, _pcw_reg, _pcw_shift) { \ + .id = _id, \ + .name = _name, \ + .reg = _reg, \ + .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, \ + .flags = _flags, \ + .rst_bar_mask = CON0_MT8173_RST_BAR, \ + .fmax = MT8173_PLL_FMAX, \ + .pcwbits = _pcwbits, \ + .pd_reg = _pd_reg, \ + .pd_shift = _pd_shift, \ + .tuner_reg = _tuner_reg, \ + .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, \ + } + +static const struct mtk_pll_data plls[] = { + PLL(CLK_APMIXED_ARMCA15PLL, "armca15pll", 0x200, 0x20c, 0x00000001, 0, 21, 0x204, 24, 0x0, 0x204, 0), + PLL(CLK_APMIXED_ARMCA7PLL, "armca7pll", 0x210, 0x21c, 0x00000001, 0, 21, 0x214, 24, 0x0, 0x214, 0), + PLL(CLK_APMIXED_MAINPLL, "mainpll", 0x220, 0x22c, 0xf0000101, HAVE_RST_BAR, 21, 0x220, 4, 0x0, 0x224, 0), + PLL(CLK_APMIXED_UNIVPLL, "univpll", 0x230, 0x23c, 0xfe000001, HAVE_RST_BAR, 7, 0x230, 4, 0x0, 0x234, 14), + PLL(CLK_APMIXED_MMPLL, "mmpll", 0x240, 0x24c, 0x00000001, 0, 21, 0x244, 24, 0x0, 0x244, 0), + PLL(CLK_APMIXED_MSDCPLL, "msdcpll", 0x250, 0x25c, 0x00000001, 0, 21, 0x250, 4, 0x0, 0x254, 0), + PLL(CLK_APMIXED_VENCPLL, "vencpll", 0x260, 0x26c, 0x00000001, 0, 21, 0x260, 4, 0x0, 0x264, 0), + PLL(CLK_APMIXED_TVDPLL, "tvdpll", 0x270, 0x27c, 0x00000001, 0, 21, 0x270, 4, 0x0, 0x274, 0), + PLL(CLK_APMIXED_MPLL, "mpll", 0x280, 0x28c, 0x00000001, 0, 21, 0x280, 4, 0x0, 0x284, 0), + PLL(CLK_APMIXED_VCODECPLL, "vcodecpll", 0x290, 0x29c, 0x00000001, 0, 21, 0x290, 4, 0x0, 0x294, 0), + PLL(CLK_APMIXED_APLL1, "apll1", 0x2a0, 0x2b0, 0x00000001, 0, 31, 0x2a0, 4, 0x2a4, 0x2a4, 0), + PLL(CLK_APMIXED_APLL2, "apll2", 0x2b4, 0x2c4, 0x00000001, 0, 31, 0x2b4, 4, 0x2b8, 0x2b8, 0), + PLL(CLK_APMIXED_LVDSPLL, "lvdspll", 0x2d0, 0x2dc, 0x00000001, 0, 21, 0x2d0, 4, 0x0, 0x2d4, 0), + PLL(CLK_APMIXED_MSDCPLL2, "msdcpll2", 0x2f0, 0x2fc, 0x00000001, 0, 21, 0x2f0, 4, 0x0, 0x2f4, 0), +}; + +static void __init mtk_apmixedsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + + clk_data = mtk_alloc_clk_data(ARRAY_SIZE(plls)); + if (!clk_data) + return; + + mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data); + + clk_prepare_enable(clk_data->clks[CLK_APMIXED_ARMCA15PLL]); +} +CLK_OF_DECLARE(mtk_apmixedsys, "mediatek,mt8173-apmixedsys", + mtk_apmixedsys_init); diff --git a/include/dt-bindings/clock/mt8173-clk.h b/include/dt-bindings/clock/mt8173-clk.h new file mode 100644 index 000000000000..4ad76ed882ad --- /dev/null +++ b/include/dt-bindings/clock/mt8173-clk.h @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: James Liao + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DT_BINDINGS_CLK_MT8173_H +#define _DT_BINDINGS_CLK_MT8173_H + +/* TOPCKGEN */ + +#define CLK_TOP_CLKPH_MCK_O 1 +#define CLK_TOP_DPI 2 +#define CLK_TOP_USB_SYSPLL_125M 3 +#define CLK_TOP_HDMITX_DIG_CTS 4 +#define CLK_TOP_ARMCA7PLL_754M 5 +#define CLK_TOP_ARMCA7PLL_502M 6 +#define CLK_TOP_MAIN_H546M 7 +#define CLK_TOP_MAIN_H364M 8 +#define CLK_TOP_MAIN_H218P4M 9 +#define CLK_TOP_MAIN_H156M 10 +#define CLK_TOP_TVDPLL_445P5M 11 +#define CLK_TOP_TVDPLL_594M 12 +#define CLK_TOP_UNIV_624M 13 +#define CLK_TOP_UNIV_416M 14 +#define CLK_TOP_UNIV_249P6M 15 +#define CLK_TOP_UNIV_178P3M 16 +#define CLK_TOP_UNIV_48M 17 +#define CLK_TOP_CLKRTC_EXT 18 +#define CLK_TOP_CLKRTC_INT 19 +#define CLK_TOP_FPC 20 +#define CLK_TOP_HDMITXPLL_D2 21 +#define CLK_TOP_HDMITXPLL_D3 22 +#define CLK_TOP_ARMCA7PLL_D2 23 +#define CLK_TOP_ARMCA7PLL_D3 24 +#define CLK_TOP_APLL1 25 +#define CLK_TOP_APLL2 26 +#define CLK_TOP_DMPLL 27 +#define CLK_TOP_DMPLL_D2 28 +#define CLK_TOP_DMPLL_D4 29 +#define CLK_TOP_DMPLL_D8 30 +#define CLK_TOP_DMPLL_D16 31 +#define CLK_TOP_LVDSPLL_D2 32 +#define CLK_TOP_LVDSPLL_D4 33 +#define CLK_TOP_LVDSPLL_D8 34 +#define CLK_TOP_MMPLL 35 +#define CLK_TOP_MMPLL_D2 36 +#define CLK_TOP_MSDCPLL 37 +#define CLK_TOP_MSDCPLL_D2 38 +#define CLK_TOP_MSDCPLL_D4 39 +#define CLK_TOP_MSDCPLL2 40 +#define CLK_TOP_MSDCPLL2_D2 41 +#define CLK_TOP_MSDCPLL2_D4 42 +#define CLK_TOP_SYSPLL_D2 43 +#define CLK_TOP_SYSPLL1_D2 44 +#define CLK_TOP_SYSPLL1_D4 45 +#define CLK_TOP_SYSPLL1_D8 46 +#define CLK_TOP_SYSPLL1_D16 47 +#define CLK_TOP_SYSPLL_D3 48 +#define CLK_TOP_SYSPLL2_D2 49 +#define CLK_TOP_SYSPLL2_D4 50 +#define CLK_TOP_SYSPLL_D5 51 +#define CLK_TOP_SYSPLL3_D2 52 +#define CLK_TOP_SYSPLL3_D4 53 +#define CLK_TOP_SYSPLL_D7 54 +#define CLK_TOP_SYSPLL4_D2 55 +#define CLK_TOP_SYSPLL4_D4 56 +#define CLK_TOP_TVDPLL 57 +#define CLK_TOP_TVDPLL_D2 58 +#define CLK_TOP_TVDPLL_D4 59 +#define CLK_TOP_TVDPLL_D8 60 +#define CLK_TOP_TVDPLL_D16 61 +#define CLK_TOP_UNIVPLL_D2 62 +#define CLK_TOP_UNIVPLL1_D2 63 +#define CLK_TOP_UNIVPLL1_D4 64 +#define CLK_TOP_UNIVPLL1_D8 65 +#define CLK_TOP_UNIVPLL_D3 66 +#define CLK_TOP_UNIVPLL2_D2 67 +#define CLK_TOP_UNIVPLL2_D4 68 +#define CLK_TOP_UNIVPLL2_D8 69 +#define CLK_TOP_UNIVPLL_D5 70 +#define CLK_TOP_UNIVPLL3_D2 71 +#define CLK_TOP_UNIVPLL3_D4 72 +#define CLK_TOP_UNIVPLL3_D8 73 +#define CLK_TOP_UNIVPLL_D7 74 +#define CLK_TOP_UNIVPLL_D26 75 +#define CLK_TOP_UNIVPLL_D52 76 +#define CLK_TOP_VCODECPLL 77 +#define CLK_TOP_VCODECPLL_370P5 78 +#define CLK_TOP_VENCPLL 79 +#define CLK_TOP_VENCPLL_D2 80 +#define CLK_TOP_VENCPLL_D4 81 +#define CLK_TOP_AXI_SEL 82 +#define CLK_TOP_MEM_SEL 83 +#define CLK_TOP_DDRPHYCFG_SEL 84 +#define CLK_TOP_MM_SEL 85 +#define CLK_TOP_PWM_SEL 86 +#define CLK_TOP_VDEC_SEL 87 +#define CLK_TOP_VENC_SEL 88 +#define CLK_TOP_MFG_SEL 89 +#define CLK_TOP_CAMTG_SEL 90 +#define CLK_TOP_UART_SEL 91 +#define CLK_TOP_SPI_SEL 92 +#define CLK_TOP_USB20_SEL 93 +#define CLK_TOP_USB30_SEL 94 +#define CLK_TOP_MSDC50_0_H_SEL 95 +#define CLK_TOP_MSDC50_0_SEL 96 +#define CLK_TOP_MSDC30_1_SEL 97 +#define CLK_TOP_MSDC30_2_SEL 98 +#define CLK_TOP_MSDC30_3_SEL 99 +#define CLK_TOP_AUDIO_SEL 100 +#define CLK_TOP_AUD_INTBUS_SEL 101 +#define CLK_TOP_PMICSPI_SEL 102 +#define CLK_TOP_SCP_SEL 103 +#define CLK_TOP_ATB_SEL 104 +#define CLK_TOP_VENC_LT_SEL 105 +#define CLK_TOP_DPI0_SEL 106 +#define CLK_TOP_IRDA_SEL 107 +#define CLK_TOP_CCI400_SEL 108 +#define CLK_TOP_AUD_1_SEL 109 +#define CLK_TOP_AUD_2_SEL 110 +#define CLK_TOP_MEM_MFG_IN_SEL 111 +#define CLK_TOP_AXI_MFG_IN_SEL 112 +#define CLK_TOP_SCAM_SEL 113 +#define CLK_TOP_SPINFI_IFR_SEL 114 +#define CLK_TOP_HDMI_SEL 115 +#define CLK_TOP_DPILVDS_SEL 116 +#define CLK_TOP_MSDC50_2_H_SEL 117 +#define CLK_TOP_HDCP_SEL 118 +#define CLK_TOP_HDCP_24M_SEL 119 +#define CLK_TOP_RTC_SEL 120 +#define CLK_TOP_APLL1_DIV0 121 +#define CLK_TOP_APLL1_DIV1 122 +#define CLK_TOP_APLL1_DIV2 123 +#define CLK_TOP_APLL1_DIV3 124 +#define CLK_TOP_APLL1_DIV4 125 +#define CLK_TOP_APLL1_DIV5 126 +#define CLK_TOP_APLL2_DIV0 127 +#define CLK_TOP_APLL2_DIV1 128 +#define CLK_TOP_APLL2_DIV2 129 +#define CLK_TOP_APLL2_DIV3 130 +#define CLK_TOP_APLL2_DIV4 131 +#define CLK_TOP_APLL2_DIV5 132 +#define CLK_TOP_I2S0_M_SEL 133 +#define CLK_TOP_I2S1_M_SEL 134 +#define CLK_TOP_I2S2_M_SEL 135 +#define CLK_TOP_I2S3_M_SEL 136 +#define CLK_TOP_I2S3_B_SEL 137 +#define CLK_TOP_NR_CLK 138 + +/* APMIXED_SYS */ + +#define CLK_APMIXED_ARMCA15PLL 1 +#define CLK_APMIXED_ARMCA7PLL 2 +#define CLK_APMIXED_MAINPLL 3 +#define CLK_APMIXED_UNIVPLL 4 +#define CLK_APMIXED_MMPLL 5 +#define CLK_APMIXED_MSDCPLL 6 +#define CLK_APMIXED_VENCPLL 7 +#define CLK_APMIXED_TVDPLL 8 +#define CLK_APMIXED_MPLL 9 +#define CLK_APMIXED_VCODECPLL 10 +#define CLK_APMIXED_APLL1 11 +#define CLK_APMIXED_APLL2 12 +#define CLK_APMIXED_LVDSPLL 13 +#define CLK_APMIXED_MSDCPLL2 14 +#define CLK_APMIXED_NR_CLK 15 + +/* INFRA_SYS */ + +#define CLK_INFRA_DBGCLK 1 +#define CLK_INFRA_SMI 2 +#define CLK_INFRA_AUDIO 3 +#define CLK_INFRA_GCE 4 +#define CLK_INFRA_L2C_SRAM 5 +#define CLK_INFRA_M4U 6 +#define CLK_INFRA_CPUM 7 +#define CLK_INFRA_KP 8 +#define CLK_INFRA_CEC 9 +#define CLK_INFRA_PMICSPI 10 +#define CLK_INFRA_PMICWRAP 11 +#define CLK_INFRA_NR_CLK 12 + +/* PERI_SYS */ + +#define CLK_PERI_NFI 1 +#define CLK_PERI_THERM 2 +#define CLK_PERI_PWM1 3 +#define CLK_PERI_PWM2 4 +#define CLK_PERI_PWM3 5 +#define CLK_PERI_PWM4 6 +#define CLK_PERI_PWM5 7 +#define CLK_PERI_PWM6 8 +#define CLK_PERI_PWM7 9 +#define CLK_PERI_PWM 10 +#define CLK_PERI_USB0 11 +#define CLK_PERI_USB1 12 +#define CLK_PERI_AP_DMA 13 +#define CLK_PERI_MSDC30_0 14 +#define CLK_PERI_MSDC30_1 15 +#define CLK_PERI_MSDC30_2 16 +#define CLK_PERI_MSDC30_3 17 +#define CLK_PERI_NLI_ARB 18 +#define CLK_PERI_IRDA 19 +#define CLK_PERI_UART0 20 +#define CLK_PERI_UART1 21 +#define CLK_PERI_UART2 22 +#define CLK_PERI_UART3 23 +#define CLK_PERI_I2C0 24 +#define CLK_PERI_I2C1 25 +#define CLK_PERI_I2C2 26 +#define CLK_PERI_I2C3 27 +#define CLK_PERI_I2C4 28 +#define CLK_PERI_AUXADC 29 +#define CLK_PERI_SPI0 30 +#define CLK_PERI_I2C5 31 +#define CLK_PERI_NFIECC 32 +#define CLK_PERI_SPI 33 +#define CLK_PERI_IRRX 34 +#define CLK_PERI_I2C6 35 +#define CLK_PERI_UART0_SEL 36 +#define CLK_PERI_UART1_SEL 37 +#define CLK_PERI_UART2_SEL 38 +#define CLK_PERI_UART3_SEL 39 +#define CLK_PERI_NR_CLK 40 + +#endif /* _DT_BINDINGS_CLK_MT8173_H */ diff --git a/include/dt-bindings/reset-controller/mt8173-resets.h b/include/dt-bindings/reset-controller/mt8173-resets.h new file mode 100644 index 000000000000..9464b37cf68c --- /dev/null +++ b/include/dt-bindings/reset-controller/mt8173-resets.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Flora Fu, MediaTek + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _DT_BINDINGS_RESET_CONTROLLER_MT8173 +#define _DT_BINDINGS_RESET_CONTROLLER_MT8173 + +/* INFRACFG resets */ +#define MT8173_INFRA_EMI_REG_RST 0 +#define MT8173_INFRA_DRAMC0_A0_RST 1 +#define MT8173_INFRA_APCIRQ_EINT_RST 3 +#define MT8173_INFRA_APXGPT_RST 4 +#define MT8173_INFRA_SCPSYS_RST 5 +#define MT8173_INFRA_KP_RST 6 +#define MT8173_INFRA_PMIC_WRAP_RST 7 +#define MT8173_INFRA_MPIP_RST 8 +#define MT8173_INFRA_CEC_RST 9 +#define MT8173_INFRA_EMI_RST 32 +#define MT8173_INFRA_DRAMC0_RST 34 +#define MT8173_INFRA_APMIXEDSYS_RST 35 +#define MT8173_INFRA_MIPI_DSI_RST 36 +#define MT8173_INFRA_TRNG_RST 37 +#define MT8173_INFRA_SYSIRQ_RST 38 +#define MT8173_INFRA_MIPI_CSI_RST 39 +#define MT8173_INFRA_GCE_FAXI_RST 40 +#define MT8173_INFRA_MMIOMMURST 47 + + +/* PERICFG resets */ +#define MT8173_PERI_UART0_SW_RST 0 +#define MT8173_PERI_UART1_SW_RST 1 +#define MT8173_PERI_UART2_SW_RST 2 +#define MT8173_PERI_UART3_SW_RST 3 +#define MT8173_PERI_IRRX_SW_RST 4 +#define MT8173_PERI_PWM_SW_RST 8 +#define MT8173_PERI_AUXADC_SW_RST 10 +#define MT8173_PERI_DMA_SW_RST 11 +#define MT8173_PERI_I2C6_SW_RST 13 +#define MT8173_PERI_NFI_SW_RST 14 +#define MT8173_PERI_THERM_SW_RST 16 +#define MT8173_PERI_MSDC2_SW_RST 17 +#define MT8173_PERI_MSDC3_SW_RST 18 +#define MT8173_PERI_MSDC0_SW_RST 19 +#define MT8173_PERI_MSDC1_SW_RST 20 +#define MT8173_PERI_I2C0_SW_RST 22 +#define MT8173_PERI_I2C1_SW_RST 23 +#define MT8173_PERI_I2C2_SW_RST 24 +#define MT8173_PERI_I2C3_SW_RST 25 +#define MT8173_PERI_I2C4_SW_RST 26 +#define MT8173_PERI_HDMI_SW_RST 29 +#define MT8173_PERI_SPI0_SW_RST 33 + +#endif /* _DT_BINDINGS_RESET_CONTROLLER_MT8173 */ -- cgit v1.2.3-58-ga151 From 9eb67f1093fdaefdffe56861e373f50d117352e9 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 23 Apr 2015 10:35:43 +0200 Subject: dt-bindings: ARM: Mediatek: Document devicetree bindings for clock/reset controllers This adds the binding documentation for the apmixedsys, perisys and infracfg controllers found on Mediatek SoCs. Signed-off-by: Sascha Hauer Signed-off-by: Stephen Boyd --- .../bindings/arm/mediatek/mediatek,apmixedsys.txt | 23 +++++++++++++++++ .../bindings/arm/mediatek/mediatek,infracfg.txt | 30 ++++++++++++++++++++++ .../bindings/arm/mediatek/mediatek,pericfg.txt | 30 ++++++++++++++++++++++ .../bindings/arm/mediatek/mediatek,topckgen.txt | 23 +++++++++++++++++ 4 files changed, 106 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt create mode 100644 Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt new file mode 100644 index 000000000000..5af6d7394608 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt @@ -0,0 +1,23 @@ +Mediatek apmixedsys controller +============================== + +The Mediatek apmixedsys controller provides the PLLs to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt8135-apmixedsys" + - "mediatek,mt8173-apmixedsys" +- #clock-cells: Must be 1 + +The apmixedsys controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. + +Example: + +apmixedsys: apmixedsys@10209000 { + compatible = "mediatek,mt8173-apmixedsys"; + reg = <0 0x10209000 0 0x1000>; + #clock-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt new file mode 100644 index 000000000000..684da473b3e8 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt @@ -0,0 +1,30 @@ +Mediatek infracfg controller +============================ + +The Mediatek infracfg controller provides various clocks and reset +outputs to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt8135-infracfg", "syscon" + - "mediatek,mt8173-infracfg", "syscon" +- #clock-cells: Must be 1 +- #reset-cells: Must be 1 + +The infracfg controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. +Also it uses the common reset controller binding from +Documentation/devicetree/bindings/reset/reset.txt. +The available reset outputs are defined in +dt-bindings/reset-controller/mt*-resets.h + +Example: + +infracfg: infracfg@10001000 { + compatible = "mediatek,mt8173-infracfg", "syscon"; + reg = <0 0x10001000 0 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt new file mode 100644 index 000000000000..fdb45c6b63de --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt @@ -0,0 +1,30 @@ +Mediatek pericfg controller +=========================== + +The Mediatek pericfg controller provides various clocks and reset +outputs to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt8135-pericfg", "syscon" + - "mediatek,mt8173-pericfg", "syscon" +- #clock-cells: Must be 1 +- #reset-cells: Must be 1 + +The pericfg controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. +Also it uses the common reset controller binding from +Documentation/devicetree/bindings/reset/reset.txt. +The available reset outputs are defined in +dt-bindings/reset-controller/mt*-resets.h + +Example: + +pericfg: pericfg@10003000 { + compatible = "mediatek,mt8173-pericfg", "syscon"; + reg = <0 0x10003000 0 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; +}; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt new file mode 100644 index 000000000000..a4252489860d --- /dev/null +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt @@ -0,0 +1,23 @@ +Mediatek topckgen controller +============================ + +The Mediatek topckgen controller provides various clocks to the system. + +Required Properties: + +- compatible: Should be: + - "mediatek,mt8135-topckgen" + - "mediatek,mt8173-topckgen" +- #clock-cells: Must be 1 + +The topckgen controller uses the common clk binding from +Documentation/devicetree/bindings/clock/clock-bindings.txt +The available clocks are defined in dt-bindings/clock/mt*-clk.h. + +Example: + +topckgen: topckgen@10000000 { + compatible = "mediatek,mt8173-topckgen"; + reg = <0 0x10000000 0 0x1000>; + #clock-cells = <1>; +}; -- cgit v1.2.3-58-ga151 From 9b030bc92999853bae91262908554f168fa74965 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 28 Apr 2015 13:46:16 +0900 Subject: clk: rockchip: Staticize file-scope declarations Add missing static to local (file-scope only) symbols. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Stephen Boyd --- drivers/clk/rockchip/clk-rk3188.c | 2 +- drivers/clk/rockchip/clk-rk3288.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index 556ce041d371..e4f9d472f1ff 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -26,7 +26,7 @@ enum rk3188_plls { apll, cpll, dpll, gpll, }; -struct rockchip_pll_rate_table rk3188_pll_rates[] = { +static struct rockchip_pll_rate_table rk3188_pll_rates[] = { RK3066_PLL_RATE(2208000000, 1, 92, 1), RK3066_PLL_RATE(2184000000, 1, 91, 1), RK3066_PLL_RATE(2160000000, 1, 90, 1), diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index d17eb4528a28..4f817ed9e6ee 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -27,7 +27,7 @@ enum rk3288_plls { apll, dpll, cpll, gpll, npll, }; -struct rockchip_pll_rate_table rk3288_pll_rates[] = { +static struct rockchip_pll_rate_table rk3288_pll_rates[] = { RK3066_PLL_RATE(2208000000, 1, 92, 1), RK3066_PLL_RATE(2184000000, 1, 91, 1), RK3066_PLL_RATE(2160000000, 1, 90, 1), -- cgit v1.2.3-58-ga151 From 6b8f9eabec1c77ab72194b1bacff5d5c6e16dbf8 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 28 Apr 2015 13:46:17 +0900 Subject: clk: exynos: Staticize file-scope declarations Add missing static to local (file-scope only) symbols. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Stephen Boyd --- drivers/clk/samsung/clk-exynos5260.c | 74 ++++++++++++++++++------------------ drivers/clk/samsung/clk-exynos5420.c | 10 ++--- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos5260.c b/drivers/clk/samsung/clk-exynos5260.c index e2e5193d1049..df1d83c45554 100644 --- a/drivers/clk/samsung/clk-exynos5260.c +++ b/drivers/clk/samsung/clk-exynos5260.c @@ -94,7 +94,7 @@ PNAME(mout_aud_pll_user_p) = {"fin_pll", "fout_aud_pll"}; PNAME(mout_sclk_aud_i2s_p) = {"mout_aud_pll_user", "ioclk_i2s_cdclk"}; PNAME(mout_sclk_aud_pcm_p) = {"mout_aud_pll_user", "ioclk_pcm_extclk"}; -struct samsung_mux_clock aud_mux_clks[] __initdata = { +static struct samsung_mux_clock aud_mux_clks[] __initdata = { MUX(AUD_MOUT_AUD_PLL_USER, "mout_aud_pll_user", mout_aud_pll_user_p, MUX_SEL_AUD, 0, 1), MUX(AUD_MOUT_SCLK_AUD_I2S, "mout_sclk_aud_i2s", mout_sclk_aud_i2s_p, @@ -103,7 +103,7 @@ struct samsung_mux_clock aud_mux_clks[] __initdata = { MUX_SEL_AUD, 8, 1), }; -struct samsung_div_clock aud_div_clks[] __initdata = { +static struct samsung_div_clock aud_div_clks[] __initdata = { DIV(AUD_DOUT_ACLK_AUD_131, "dout_aclk_aud_131", "mout_aud_pll_user", DIV_AUD0, 0, 4), @@ -115,7 +115,7 @@ struct samsung_div_clock aud_div_clks[] __initdata = { DIV_AUD1, 12, 4), }; -struct samsung_gate_clock aud_gate_clks[] __initdata = { +static struct samsung_gate_clock aud_gate_clks[] __initdata = { GATE(AUD_SCLK_I2S, "sclk_aud_i2s", "dout_sclk_aud_i2s", EN_SCLK_AUD, 0, CLK_SET_RATE_PARENT, 0), GATE(AUD_SCLK_PCM, "sclk_aud_pcm", "dout_sclk_aud_pcm", @@ -203,7 +203,7 @@ PNAME(mout_phyclk_mipi_dphy_4lmrxclk_esc0_user_p) = {"fin_pll", PNAME(mout_sclk_hdmi_spdif_p) = {"fin_pll", "ioclk_spdif_extclk", "dout_aclk_peri_aud", "phyclk_hdmi_phy_ref_cko"}; -struct samsung_mux_clock disp_mux_clks[] __initdata = { +static struct samsung_mux_clock disp_mux_clks[] __initdata = { MUX(DISP_MOUT_ACLK_DISP_333_USER, "mout_aclk_disp_333_user", mout_aclk_disp_333_user_p, MUX_SEL_DISP0, 0, 1), @@ -272,7 +272,7 @@ struct samsung_mux_clock disp_mux_clks[] __initdata = { MUX_SEL_DISP4, 4, 2), }; -struct samsung_div_clock disp_div_clks[] __initdata = { +static struct samsung_div_clock disp_div_clks[] __initdata = { DIV(DISP_DOUT_PCLK_DISP_111, "dout_pclk_disp_111", "mout_aclk_disp_222_user", DIV_DISP, 8, 4), @@ -285,7 +285,7 @@ struct samsung_div_clock disp_div_clks[] __initdata = { DIV_DISP, 16, 4), }; -struct samsung_gate_clock disp_gate_clks[] __initdata = { +static struct samsung_gate_clock disp_gate_clks[] __initdata = { GATE(DISP_MOUT_HDMI_PHY_PIXEL_USER, "sclk_hdmi_link_i_pixel", "mout_phyclk_hdmi_phy_pixel_clko_user", EN_SCLK_DISP0, 26, CLK_SET_RATE_PARENT, 0), @@ -363,13 +363,13 @@ static unsigned long egl_clk_regs[] __initdata = { PNAME(mout_egl_b_p) = {"mout_egl_pll", "dout_bus_pll"}; PNAME(mout_egl_pll_p) = {"fin_pll", "fout_egl_pll"}; -struct samsung_mux_clock egl_mux_clks[] __initdata = { +static struct samsung_mux_clock egl_mux_clks[] __initdata = { MUX(EGL_MOUT_EGL_PLL, "mout_egl_pll", mout_egl_pll_p, MUX_SEL_EGL, 4, 1), MUX(EGL_MOUT_EGL_B, "mout_egl_b", mout_egl_b_p, MUX_SEL_EGL, 16, 1), }; -struct samsung_div_clock egl_div_clks[] __initdata = { +static struct samsung_div_clock egl_div_clks[] __initdata = { DIV(EGL_DOUT_EGL1, "dout_egl1", "mout_egl_b", DIV_EGL, 0, 3), DIV(EGL_DOUT_EGL2, "dout_egl2", "dout_egl1", DIV_EGL, 4, 3), DIV(EGL_DOUT_ACLK_EGL, "dout_aclk_egl", "dout_egl2", DIV_EGL, 8, 3), @@ -433,7 +433,7 @@ PNAME(mout_phyclk_usbdrd30_pipe_pclk_user_p) = {"fin_pll", PNAME(mout_phyclk_usbdrd30_phyclock_user_p) = {"fin_pll", "phyclk_usbdrd30_udrd30_phyclock"}; -struct samsung_mux_clock fsys_mux_clks[] __initdata = { +static struct samsung_mux_clock fsys_mux_clks[] __initdata = { MUX(FSYS_MOUT_PHYCLK_USBDRD30_PHYCLOCK_USER, "mout_phyclk_usbdrd30_phyclock_user", mout_phyclk_usbdrd30_phyclock_user_p, @@ -456,7 +456,7 @@ struct samsung_mux_clock fsys_mux_clks[] __initdata = { MUX_SEL_FSYS1, 16, 1), }; -struct samsung_gate_clock fsys_gate_clks[] __initdata = { +static struct samsung_gate_clock fsys_gate_clks[] __initdata = { GATE(FSYS_PHYCLK_USBHOST20, "phyclk_usbhost20_phyclock", "mout_phyclk_usbdrd30_phyclock_user", EN_SCLK_FSYS, 1, 0, 0), @@ -537,18 +537,18 @@ static unsigned long g2d_clk_regs[] __initdata = { PNAME(mout_aclk_g2d_333_user_p) = {"fin_pll", "dout_aclk_g2d_333"}; -struct samsung_mux_clock g2d_mux_clks[] __initdata = { +static struct samsung_mux_clock g2d_mux_clks[] __initdata = { MUX(G2D_MOUT_ACLK_G2D_333_USER, "mout_aclk_g2d_333_user", mout_aclk_g2d_333_user_p, MUX_SEL_G2D, 0, 1), }; -struct samsung_div_clock g2d_div_clks[] __initdata = { +static struct samsung_div_clock g2d_div_clks[] __initdata = { DIV(G2D_DOUT_PCLK_G2D_83, "dout_pclk_g2d_83", "mout_aclk_g2d_333_user", DIV_G2D, 0, 3), }; -struct samsung_gate_clock g2d_gate_clks[] __initdata = { +static struct samsung_gate_clock g2d_gate_clks[] __initdata = { GATE(G2D_CLK_G2D, "clk_g2d", "mout_aclk_g2d_333_user", EN_IP_G2D, 4, 0, 0), GATE(G2D_CLK_JPEG, "clk_jpeg", "mout_aclk_g2d_333_user", @@ -617,17 +617,17 @@ static unsigned long g3d_clk_regs[] __initdata = { PNAME(mout_g3d_pll_p) = {"fin_pll", "fout_g3d_pll"}; -struct samsung_mux_clock g3d_mux_clks[] __initdata = { +static struct samsung_mux_clock g3d_mux_clks[] __initdata = { MUX(G3D_MOUT_G3D_PLL, "mout_g3d_pll", mout_g3d_pll_p, MUX_SEL_G3D, 0, 1), }; -struct samsung_div_clock g3d_div_clks[] __initdata = { +static struct samsung_div_clock g3d_div_clks[] __initdata = { DIV(G3D_DOUT_PCLK_G3D, "dout_pclk_g3d", "dout_aclk_g3d", DIV_G3D, 0, 3), DIV(G3D_DOUT_ACLK_G3D, "dout_aclk_g3d", "mout_g3d_pll", DIV_G3D, 4, 3), }; -struct samsung_gate_clock g3d_gate_clks[] __initdata = { +static struct samsung_gate_clock g3d_gate_clks[] __initdata = { GATE(G3D_CLK_G3D, "clk_g3d", "dout_aclk_g3d", EN_IP_G3D, 2, 0, 0), GATE(G3D_CLK_G3D_HPM, "clk_g3d_hpm", "dout_aclk_g3d", EN_IP_G3D, 3, 0, 0), @@ -694,7 +694,7 @@ PNAME(mout_aclk_m2m_400_user_p) = {"fin_pll", "dout_aclk_gscl_400"}; PNAME(mout_aclk_gscl_fimc_user_p) = {"fin_pll", "dout_aclk_gscl_400"}; PNAME(mout_aclk_csis_p) = {"dout_aclk_csis_200", "mout_aclk_gscl_fimc_user"}; -struct samsung_mux_clock gscl_mux_clks[] __initdata = { +static struct samsung_mux_clock gscl_mux_clks[] __initdata = { MUX(GSCL_MOUT_ACLK_GSCL_333_USER, "mout_aclk_gscl_333_user", mout_aclk_gscl_333_user_p, MUX_SEL_GSCL, 0, 1), @@ -708,7 +708,7 @@ struct samsung_mux_clock gscl_mux_clks[] __initdata = { MUX_SEL_GSCL, 24, 1), }; -struct samsung_div_clock gscl_div_clks[] __initdata = { +static struct samsung_div_clock gscl_div_clks[] __initdata = { DIV(GSCL_DOUT_PCLK_M2M_100, "dout_pclk_m2m_100", "mout_aclk_m2m_400_user", DIV_GSCL, 0, 3), @@ -717,7 +717,7 @@ struct samsung_div_clock gscl_div_clks[] __initdata = { DIV_GSCL, 4, 3), }; -struct samsung_gate_clock gscl_gate_clks[] __initdata = { +static struct samsung_gate_clock gscl_gate_clks[] __initdata = { GATE(GSCL_SCLK_CSIS0_WRAP, "sclk_csis0_wrap", "dout_aclk_csis_200", EN_SCLK_GSCL_FIMC, 0, CLK_SET_RATE_PARENT, 0), GATE(GSCL_SCLK_CSIS1_WRAP, "sclk_csis1_wrap", "dout_aclk_csis_200", @@ -813,14 +813,14 @@ static unsigned long isp_clk_regs[] __initdata = { PNAME(mout_isp_400_user_p) = {"fin_pll", "dout_aclk_isp1_400"}; PNAME(mout_isp_266_user_p) = {"fin_pll", "dout_aclk_isp1_266"}; -struct samsung_mux_clock isp_mux_clks[] __initdata = { +static struct samsung_mux_clock isp_mux_clks[] __initdata = { MUX(ISP_MOUT_ISP_266_USER, "mout_isp_266_user", mout_isp_266_user_p, MUX_SEL_ISP0, 0, 1), MUX(ISP_MOUT_ISP_400_USER, "mout_isp_400_user", mout_isp_400_user_p, MUX_SEL_ISP0, 4, 1), }; -struct samsung_div_clock isp_div_clks[] __initdata = { +static struct samsung_div_clock isp_div_clks[] __initdata = { DIV(ISP_DOUT_PCLK_ISP_66, "dout_pclk_isp_66", "mout_kfc", DIV_ISP, 0, 3), DIV(ISP_DOUT_PCLK_ISP_133, "dout_pclk_isp_133", "mout_kfc", @@ -832,7 +832,7 @@ struct samsung_div_clock isp_div_clks[] __initdata = { DIV(ISP_DOUT_SCLK_MPWM, "dout_sclk_mpwm", "mout_kfc", DIV_ISP, 20, 2), }; -struct samsung_gate_clock isp_gate_clks[] __initdata = { +static struct samsung_gate_clock isp_gate_clks[] __initdata = { GATE(ISP_CLK_GIC, "clk_isp_gic", "mout_aclk_isp1_266", EN_IP_ISP0, 15, 0, 0), @@ -934,13 +934,13 @@ static unsigned long kfc_clk_regs[] __initdata = { PNAME(mout_kfc_pll_p) = {"fin_pll", "fout_kfc_pll"}; PNAME(mout_kfc_p) = {"mout_kfc_pll", "dout_media_pll"}; -struct samsung_mux_clock kfc_mux_clks[] __initdata = { +static struct samsung_mux_clock kfc_mux_clks[] __initdata = { MUX(KFC_MOUT_KFC_PLL, "mout_kfc_pll", mout_kfc_pll_p, MUX_SEL_KFC0, 0, 1), MUX(KFC_MOUT_KFC, "mout_kfc", mout_kfc_p, MUX_SEL_KFC2, 0, 1), }; -struct samsung_div_clock kfc_div_clks[] __initdata = { +static struct samsung_div_clock kfc_div_clks[] __initdata = { DIV(KFC_DOUT_KFC1, "dout_kfc1", "mout_kfc", DIV_KFC, 0, 3), DIV(KFC_DOUT_KFC2, "dout_kfc2", "dout_kfc1", DIV_KFC, 4, 3), DIV(KFC_DOUT_KFC_ATCLK, "dout_kfc_atclk", "dout_kfc2", DIV_KFC, 8, 3), @@ -993,18 +993,18 @@ static unsigned long mfc_clk_regs[] __initdata = { PNAME(mout_aclk_mfc_333_user_p) = {"fin_pll", "dout_aclk_mfc_333"}; -struct samsung_mux_clock mfc_mux_clks[] __initdata = { +static struct samsung_mux_clock mfc_mux_clks[] __initdata = { MUX(MFC_MOUT_ACLK_MFC_333_USER, "mout_aclk_mfc_333_user", mout_aclk_mfc_333_user_p, MUX_SEL_MFC, 0, 1), }; -struct samsung_div_clock mfc_div_clks[] __initdata = { +static struct samsung_div_clock mfc_div_clks[] __initdata = { DIV(MFC_DOUT_PCLK_MFC_83, "dout_pclk_mfc_83", "mout_aclk_mfc_333_user", DIV_MFC, 0, 3), }; -struct samsung_gate_clock mfc_gate_clks[] __initdata = { +static struct samsung_gate_clock mfc_gate_clks[] __initdata = { GATE(MFC_CLK_MFC, "clk_mfc", "mout_aclk_mfc_333_user", EN_IP_MFC, 1, 0, 0), GATE(MFC_CLK_SMMU2_MFCM0, "clk_smmu2_mfcm0", "mout_aclk_mfc_333_user", @@ -1078,7 +1078,7 @@ PNAME(mout_mif_drex2x_p) = {"dout_mem_pll", "dout_bus_pll"}; PNAME(mout_clkm_phy_p) = {"mout_mif_drex", "dout_media_pll"}; PNAME(mout_clk2x_phy_p) = {"mout_mif_drex2x", "dout_media_pll"}; -struct samsung_mux_clock mif_mux_clks[] __initdata = { +static struct samsung_mux_clock mif_mux_clks[] __initdata = { MUX(MIF_MOUT_MEM_PLL, "mout_mem_pll", mout_mem_pll_p, MUX_SEL_MIF, 0, 1), MUX(MIF_MOUT_BUS_PLL, "mout_bus_pll", mout_bus_pll_p, @@ -1095,7 +1095,7 @@ struct samsung_mux_clock mif_mux_clks[] __initdata = { MUX_SEL_MIF, 24, 1), }; -struct samsung_div_clock mif_div_clks[] __initdata = { +static struct samsung_div_clock mif_div_clks[] __initdata = { DIV(MIF_DOUT_MEDIA_PLL, "dout_media_pll", "mout_media_pll", DIV_MIF, 0, 3), DIV(MIF_DOUT_MEM_PLL, "dout_mem_pll", "mout_mem_pll", @@ -1114,7 +1114,7 @@ struct samsung_div_clock mif_div_clks[] __initdata = { DIV_MIF, 28, 4), }; -struct samsung_gate_clock mif_gate_clks[] __initdata = { +static struct samsung_gate_clock mif_gate_clks[] __initdata = { GATE(MIF_CLK_LPDDR3PHY_WRAP0, "clk_lpddr3phy_wrap0", "dout_clk2x_phy", EN_IP_MIF, 12, CLK_IGNORE_UNUSED, 0), GATE(MIF_CLK_LPDDR3PHY_WRAP1, "clk_lpddr3phy_wrap1", "dout_clk2x_phy", @@ -1221,7 +1221,7 @@ PNAME(mout_sclk_i2scod_p) = {"ioclk_i2s_cdclk", "fin_pll", "dout_aclk_peri_aud", PNAME(mout_sclk_spdif_p) = {"ioclk_spdif_extclk", "fin_pll", "dout_aclk_peri_aud", "phyclk_hdmi_phy_ref_cko"}; -struct samsung_mux_clock peri_mux_clks[] __initdata = { +static struct samsung_mux_clock peri_mux_clks[] __initdata = { MUX(PERI_MOUT_SCLK_PCM, "mout_sclk_pcm", mout_sclk_pcm_p, MUX_SEL_PERI1, 4, 2), MUX(PERI_MOUT_SCLK_I2SCOD, "mout_sclk_i2scod", mout_sclk_i2scod_p, @@ -1230,12 +1230,12 @@ struct samsung_mux_clock peri_mux_clks[] __initdata = { MUX_SEL_PERI1, 20, 2), }; -struct samsung_div_clock peri_div_clks[] __initdata = { +static struct samsung_div_clock peri_div_clks[] __initdata = { DIV(PERI_DOUT_PCM, "dout_pcm", "mout_sclk_pcm", DIV_PERI, 0, 8), DIV(PERI_DOUT_I2S, "dout_i2s", "mout_sclk_i2scod", DIV_PERI, 8, 6), }; -struct samsung_gate_clock peri_gate_clks[] __initdata = { +static struct samsung_gate_clock peri_gate_clks[] __initdata = { GATE(PERI_SCLK_PCM1, "sclk_pcm1", "dout_pcm", EN_SCLK_PERI, 0, CLK_SET_RATE_PARENT, 0), GATE(PERI_SCLK_I2S, "sclk_i2s", "dout_i2s", EN_SCLK_PERI, 1, @@ -1432,7 +1432,7 @@ static unsigned long top_clk_regs[] __initdata = { }; /* fixed rate clocks generated inside the soc */ -struct samsung_fixed_rate_clock fixed_rate_clks[] __initdata = { +static struct samsung_fixed_rate_clock fixed_rate_clks[] __initdata = { FRATE(PHYCLK_DPTX_PHY_CH3_TXD_CLK, "phyclk_dptx_phy_ch3_txd_clk", NULL, CLK_IS_ROOT, 270000000), FRATE(PHYCLK_DPTX_PHY_CH2_TXD_CLK, "phyclk_dptx_phy_ch2_txd_clk", NULL, @@ -1519,7 +1519,7 @@ PNAME(mout_sclk_fsys_mmc1_sdclkin_b_p) = {"mout_sclk_fsys_mmc1_sdclkin_a", PNAME(mout_sclk_fsys_mmc2_sdclkin_b_p) = {"mout_sclk_fsys_mmc2_sdclkin_a", "mout_mediatop_pll_user"}; -struct samsung_mux_clock top_mux_clks[] __initdata = { +static struct samsung_mux_clock top_mux_clks[] __initdata = { MUX(TOP_MOUT_MEDIATOP_PLL_USER, "mout_mediatop_pll_user", mout_mediatop_pll_user_p, MUX_SEL_TOP_PLL0, 0, 1), @@ -1679,7 +1679,7 @@ struct samsung_mux_clock top_mux_clks[] __initdata = { MUX_SEL_TOP_GSCL, 20, 1), }; -struct samsung_div_clock top_div_clks[] __initdata = { +static struct samsung_div_clock top_div_clks[] __initdata = { DIV(TOP_DOUT_ACLK_G2D_333, "dout_aclk_g2d_333", "mout_aclk_g2d_333", DIV_TOP_G2D_MFC, 0, 3), DIV(TOP_DOUT_ACLK_MFC_333, "dout_aclk_mfc_333", "mout_aclk_mfc_333", @@ -1800,7 +1800,7 @@ struct samsung_div_clock top_div_clks[] __initdata = { }; -struct samsung_gate_clock top_gate_clks[] __initdata = { +static struct samsung_gate_clock top_gate_clks[] __initdata = { GATE(TOP_SCLK_MMC0, "sclk_fsys_mmc0_sdclkin", "dout_sclk_fsys_mmc0_sdclkin_b", EN_SCLK_TOP, 7, CLK_SET_RATE_PARENT, 0), diff --git a/drivers/clk/samsung/clk-exynos5420.c b/drivers/clk/samsung/clk-exynos5420.c index 07d666cc6a29..af9979001b95 100644 --- a/drivers/clk/samsung/clk-exynos5420.c +++ b/drivers/clk/samsung/clk-exynos5420.c @@ -503,7 +503,7 @@ static struct samsung_fixed_factor_clock FFACTOR(0, "ff_dout_spll2", "mout_sclk_spll", 1, 2, 0), }; -struct samsung_mux_clock exynos5800_mux_clks[] __initdata = { +static struct samsung_mux_clock exynos5800_mux_clks[] __initdata = { MUX(0, "mout_aclk400_isp", mout_group3_5800_p, SRC_TOP0, 0, 3), MUX(0, "mout_aclk400_mscl", mout_group3_5800_p, SRC_TOP0, 4, 3), MUX(0, "mout_aclk400_wcore", mout_group2_5800_p, SRC_TOP0, 16, 3), @@ -552,7 +552,7 @@ struct samsung_mux_clock exynos5800_mux_clks[] __initdata = { MUX(0, "mout_fimd1", mout_group2_p, SRC_DISP10, 4, 3), }; -struct samsung_div_clock exynos5800_div_clks[] __initdata = { +static struct samsung_div_clock exynos5800_div_clks[] __initdata = { DIV(0, "dout_aclk400_wcore", "mout_aclk400_wcore", DIV_TOP0, 16, 3), DIV(0, "dout_aclk550_cam", "mout_aclk550_cam", @@ -568,14 +568,14 @@ struct samsung_div_clock exynos5800_div_clks[] __initdata = { DIV(0, "dout_sclk_sw", "sclk_spll", DIV_TOP9, 24, 6), }; -struct samsung_gate_clock exynos5800_gate_clks[] __initdata = { +static struct samsung_gate_clock exynos5800_gate_clks[] __initdata = { GATE(CLK_ACLK550_CAM, "aclk550_cam", "mout_user_aclk550_cam", GATE_BUS_TOP, 24, 0, 0), GATE(CLK_ACLK432_SCALER, "aclk432_scaler", "mout_user_aclk432_scaler", GATE_BUS_TOP, 27, 0, 0), }; -struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { +static struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "sclk_bpll", mout_bpll_p, TOP_SPARE2, 0, 1), MUX(0, "mout_aclk400_wcore_bpll", mout_aclk400_wcore_bpll_p, TOP_SPARE2, 4, 1), @@ -605,7 +605,7 @@ struct samsung_mux_clock exynos5420_mux_clks[] __initdata = { MUX(0, "mout_fimd1", mout_group3_p, SRC_DISP10, 4, 1), }; -struct samsung_div_clock exynos5420_div_clks[] __initdata = { +static struct samsung_div_clock exynos5420_div_clks[] __initdata = { DIV(0, "dout_aclk400_wcore", "mout_aclk400_wcore_bpll", DIV_TOP0, 16, 3), }; -- cgit v1.2.3-58-ga151 From f3db6f16bdfda536c15aefd4d47515231b3009f4 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 28 Apr 2015 13:46:20 +0900 Subject: clk: cdce706: Constify parent names in clock init data The array of parent names can be made as array of const pointers to const strings. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Stephen Boyd --- drivers/clk/clk-cdce706.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk-cdce706.c b/drivers/clk/clk-cdce706.c index b8e4f8a822e9..8a2dfd0012f3 100644 --- a/drivers/clk/clk-cdce706.c +++ b/drivers/clk/clk-cdce706.c @@ -94,7 +94,7 @@ static const char * const cdce706_source_name[] = { "clk_in0", "clk_in1", }; -static const char *cdce706_clkin_name[] = { +static const char * const cdce706_clkin_name[] = { "clk_in", }; @@ -102,7 +102,7 @@ static const char * const cdce706_pll_name[] = { "pll1", "pll2", "pll3", }; -static const char *cdce706_divider_parent_name[] = { +static const char * const cdce706_divider_parent_name[] = { "clk_in", "pll1", "pll2", "pll2", "pll3", }; -- cgit v1.2.3-58-ga151 From 45195b8f8402bcb8e519b2b2c8521d47c6c5d96c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 28 Apr 2015 13:46:21 +0900 Subject: clk: sirf: Constify parent names in clock init data The array of parent names can be made as array of const pointers to const strings. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Stephen Boyd --- drivers/clk/sirf/clk-common.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/clk/sirf/clk-common.c b/drivers/clk/sirf/clk-common.c index 37af51c5f213..e9cf5730effe 100644 --- a/drivers/clk/sirf/clk-common.c +++ b/drivers/clk/sirf/clk-common.c @@ -188,7 +188,7 @@ static struct clk_ops std_pll_ops = { .set_rate = pll_clk_set_rate, }; -static const char *pll_clk_parents[] = { +static const char * const pll_clk_parents[] = { "osc", }; @@ -284,7 +284,7 @@ static struct clk_hw usb_pll_clk_hw = { * clock domains - cpu, mem, sys/io, dsp, gfx */ -static const char *dmn_clk_parents[] = { +static const char * const dmn_clk_parents[] = { "rtc", "osc", "pll1", @@ -673,7 +673,7 @@ static void std_clk_disable(struct clk_hw *hw) clkc_writel(val, reg); } -static const char *std_clk_io_parents[] = { +static const char * const std_clk_io_parents[] = { "io", }; @@ -949,7 +949,7 @@ static struct clk_std clk_pulse = { }, }; -static const char *std_clk_dsp_parents[] = { +static const char * const std_clk_dsp_parents[] = { "dsp", }; @@ -981,7 +981,7 @@ static struct clk_std clk_mf = { }, }; -static const char *std_clk_sys_parents[] = { +static const char * const std_clk_sys_parents[] = { "sys", }; @@ -999,7 +999,7 @@ static struct clk_std clk_security = { }, }; -static const char *std_clk_usb_parents[] = { +static const char * const std_clk_usb_parents[] = { "usb_pll", }; -- cgit v1.2.3-58-ga151 From 2ad8b3fcf5b43f269ab463ff27c41f4637a4967f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 28 Apr 2015 13:46:22 +0900 Subject: clk: ls1x: Fix duplicate const for parent names Replace duplicated const keyword with proper array of const pointers to const strings. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Stephen Boyd --- drivers/clk/clk-ls1x.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/clk/clk-ls1x.c b/drivers/clk/clk-ls1x.c index ca80103ac188..d4c61985f448 100644 --- a/drivers/clk/clk-ls1x.c +++ b/drivers/clk/clk-ls1x.c @@ -80,9 +80,9 @@ static struct clk *__init clk_register_pll(struct device *dev, return clk; } -static const char const *cpu_parents[] = { "cpu_clk_div", "osc_33m_clk", }; -static const char const *ahb_parents[] = { "ahb_clk_div", "osc_33m_clk", }; -static const char const *dc_parents[] = { "dc_clk_div", "osc_33m_clk", }; +static const char * const cpu_parents[] = { "cpu_clk_div", "osc_33m_clk", }; +static const char * const ahb_parents[] = { "ahb_clk_div", "osc_33m_clk", }; +static const char * const dc_parents[] = { "dc_clk_div", "osc_33m_clk", }; void __init ls1x_clk_init(void) { -- cgit v1.2.3-58-ga151 From 1237dc44300c429c4dcfc81e038ee049dad883b1 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 01:02:47 +0900 Subject: clk: s3c2410: Staticize local symbols Staticize symbols not exported and not used outside of file. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Stephen Boyd --- drivers/clk/samsung/clk-s3c2410-dclk.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/samsung/clk-s3c2410-dclk.c b/drivers/clk/samsung/clk-s3c2410-dclk.c index f4f29ed6bd25..db6fddb211d4 100644 --- a/drivers/clk/samsung/clk-s3c2410-dclk.c +++ b/drivers/clk/samsung/clk-s3c2410-dclk.c @@ -81,13 +81,13 @@ static int s3c24xx_clkout_set_parent(struct clk_hw *hw, u8 index) return ret; } -const struct clk_ops s3c24xx_clkout_ops = { +static const struct clk_ops s3c24xx_clkout_ops = { .get_parent = s3c24xx_clkout_get_parent, .set_parent = s3c24xx_clkout_set_parent, .determine_rate = __clk_mux_determine_rate, }; -struct clk *s3c24xx_register_clkout(struct device *dev, const char *name, +static struct clk *s3c24xx_register_clkout(struct device *dev, const char *name, const char **parent_names, u8 num_parents, u8 shift, u32 mask) { -- cgit v1.2.3-58-ga151 From d5255ec362f67586392c368fd21c8a2746e84e77 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 2 May 2015 01:02:48 +0900 Subject: clk: s3c2410: Constify platform_device_id The platform_device_id is not modified by the driver and core uses it as const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Stephen Boyd --- drivers/clk/samsung/clk-s3c2410-dclk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/samsung/clk-s3c2410-dclk.c b/drivers/clk/samsung/clk-s3c2410-dclk.c index db6fddb211d4..e56df5064889 100644 --- a/drivers/clk/samsung/clk-s3c2410-dclk.c +++ b/drivers/clk/samsung/clk-s3c2410-dclk.c @@ -404,7 +404,7 @@ static struct s3c24xx_dclk_drv_data dclk_variants[] = { }, }; -static struct platform_device_id s3c24xx_dclk_driver_ids[] = { +static const struct platform_device_id s3c24xx_dclk_driver_ids[] = { { .name = "s3c2410-dclk", .driver_data = (kernel_ulong_t)&dclk_variants[S3C2410], -- cgit v1.2.3-58-ga151 From 70e9f4dde00182600989ca57f08d51be12f1523f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 1 May 2015 09:48:37 -0500 Subject: clk: add newline character after dumping all clocks clk_dump() will dump data about all clocks in JSON format, but it misses a newline character at the end of the JSON string. This patch adds that missing newline character. Signed-off-by: Felipe Balbi [sboyd@codeaurora.org: Squelch checkpatch with seq_puts()] Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index ea7e8695a32b..4a2e9478edbd 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1998,7 +1998,7 @@ static int clk_dump(struct seq_file *s, void *data) clk_prepare_unlock(); - seq_printf(s, "}"); + seq_puts(s, "}\n"); return 0; } -- cgit v1.2.3-58-ga151 From a6ae41b54cb077270456acdf090c9db1663c75b5 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 20 Apr 2015 15:06:28 +0200 Subject: clk: ux500: ape[ate|trace]clk are scaleable The APEATCLK and APETRACECLK are actually scaleable so register them as scaleable clocks. Acked-by: Ulf Hansson Signed-off-by: Linus Walleij Signed-off-by: Stephen Boyd --- drivers/clk/ux500/u8500_clk.c | 7 ++++--- drivers/clk/ux500/u8500_of_clk.c | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/clk/ux500/u8500_clk.c b/drivers/clk/ux500/u8500_clk.c index 80069c370a47..4626b97b7d83 100644 --- a/drivers/clk/ux500/u8500_clk.c +++ b/drivers/clk/ux500/u8500_clk.c @@ -116,11 +116,12 @@ void u8500_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, clk_register_clkdev(clk, NULL, "hdmi"); clk_register_clkdev(clk, "hdmi", "mcde"); - clk = clk_reg_prcmu_gate("apeatclk", NULL, PRCMU_APEATCLK, CLK_IS_ROOT); + clk = clk_reg_prcmu_scalable("apeatclk", NULL, PRCMU_APEATCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "apeat"); - clk = clk_reg_prcmu_gate("apetraceclk", NULL, PRCMU_APETRACECLK, - CLK_IS_ROOT); + clk = clk_reg_prcmu_scalable("apetraceclk", NULL, PRCMU_APETRACECLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); clk_register_clkdev(clk, NULL, "apetrace"); clk = clk_reg_prcmu_gate("mcdeclk", NULL, PRCMU_MCDECLK, CLK_IS_ROOT); diff --git a/drivers/clk/ux500/u8500_of_clk.c b/drivers/clk/ux500/u8500_of_clk.c index 7b55ef89baa5..e319ef912dc6 100644 --- a/drivers/clk/ux500/u8500_of_clk.c +++ b/drivers/clk/ux500/u8500_of_clk.c @@ -166,8 +166,8 @@ void u8500_of_clk_init(u32 clkrst1_base, u32 clkrst2_base, u32 clkrst3_base, clk = clk_reg_prcmu_gate("apeatclk", NULL, PRCMU_APEATCLK, CLK_IS_ROOT); prcmu_clk[PRCMU_APEATCLK] = clk; - clk = clk_reg_prcmu_gate("apetraceclk", NULL, PRCMU_APETRACECLK, - CLK_IS_ROOT); + clk = clk_reg_prcmu_scalable("apetraceclk", NULL, PRCMU_APETRACECLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); prcmu_clk[PRCMU_APETRACECLK] = clk; clk = clk_reg_prcmu_gate("mcdeclk", NULL, PRCMU_MCDECLK, CLK_IS_ROOT); -- cgit v1.2.3-58-ga151 From c542a54fcb5591e7637f71fa400065853ce54ee0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 27 Apr 2015 14:51:33 +0200 Subject: clk: emev2: Use generic names for device nodes uart -> serial Signed-off-by: Geert Uytterhoeven Acked-by: Simon Horman Signed-off-by: Stephen Boyd --- Documentation/devicetree/bindings/clock/emev2-clock.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/clock/emev2-clock.txt b/Documentation/devicetree/bindings/clock/emev2-clock.txt index 60bbb1a8c69a..268ca615459e 100644 --- a/Documentation/devicetree/bindings/clock/emev2-clock.txt +++ b/Documentation/devicetree/bindings/clock/emev2-clock.txt @@ -52,7 +52,7 @@ usia_u0_sclk: usia_u0_sclk { Example of consumer: -uart@e1020000 { +serial@e1020000 { compatible = "renesas,em-uart"; reg = <0xe1020000 0x38>; interrupts = <0 8 0>; -- cgit v1.2.3-58-ga151 From a63347251907d7f9fc2fc02e3b9898efda573c05 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 6 May 2015 17:00:54 -0700 Subject: clk: Add some more lockdep assertions We don't check to make sure the enable_lock is held across enable/disable and we don't check if the prepare_lock is held across prepare/unprepare. Add some asserts to catch any future locking problems. Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 9e9d18cadeee..659f2b036cf5 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -543,6 +543,8 @@ EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest); static void clk_core_unprepare(struct clk_core *core) { + lockdep_assert_held(&prepare_lock); + if (!core) return; @@ -589,6 +591,8 @@ static int clk_core_prepare(struct clk_core *core) { int ret = 0; + lockdep_assert_held(&prepare_lock); + if (!core) return 0; @@ -644,6 +648,8 @@ EXPORT_SYMBOL_GPL(clk_prepare); static void clk_core_disable(struct clk_core *core) { + lockdep_assert_held(&enable_lock); + if (!core) return; @@ -692,6 +698,8 @@ static int clk_core_enable(struct clk_core *core) { int ret = 0; + lockdep_assert_held(&enable_lock); + if (!core) return 0; -- cgit v1.2.3-58-ga151 From c4b6c26e828f4df30fe3289f35c100088f2ff71d Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 7 May 2015 10:14:58 +0200 Subject: dt-bindings: ARM: Mediatek: use more generic node name in examples Use 'clock-controller' and 'power-controller' as node names in the examples rather than the specific names of the units. Signed-off-by: Sascha Hauer Signed-off-by: Stephen Boyd --- Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt | 2 +- Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt | 2 +- Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt | 2 +- Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt index 5af6d7394608..936166fbee09 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,apmixedsys.txt @@ -16,7 +16,7 @@ The available clocks are defined in dt-bindings/clock/mt*-clk.h. Example: -apmixedsys: apmixedsys@10209000 { +apmixedsys: clock-controller@10209000 { compatible = "mediatek,mt8173-apmixedsys"; reg = <0 0x10209000 0 0x1000>; #clock-cells = <1>; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt index 684da473b3e8..f6cd3e4192ff 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,infracfg.txt @@ -22,7 +22,7 @@ dt-bindings/reset-controller/mt*-resets.h Example: -infracfg: infracfg@10001000 { +infracfg: power-controller@10001000 { compatible = "mediatek,mt8173-infracfg", "syscon"; reg = <0 0x10001000 0 0x1000>; #clock-cells = <1>; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt index fdb45c6b63de..f25b85499a6f 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,pericfg.txt @@ -22,7 +22,7 @@ dt-bindings/reset-controller/mt*-resets.h Example: -pericfg: pericfg@10003000 { +pericfg: power-controller@10003000 { compatible = "mediatek,mt8173-pericfg", "syscon"; reg = <0 0x10003000 0 0x1000>; #clock-cells = <1>; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt index a4252489860d..f9e917994ced 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,topckgen.txt @@ -16,7 +16,7 @@ The available clocks are defined in dt-bindings/clock/mt*-clk.h. Example: -topckgen: topckgen@10000000 { +topckgen: power-controller@10000000 { compatible = "mediatek,mt8173-topckgen"; reg = <0 0x10000000 0 0x1000>; #clock-cells = <1>; -- cgit v1.2.3-58-ga151 From 42c86547f4e5c2e81616c76ce9a2badce515c41f Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Wed, 11 Mar 2015 11:34:25 +0100 Subject: clk: Expose clk_hw_reparent() to providers To be used by clock implementations for switching to a new parent during rate change. Signed-off-by: Tomeu Vizoso Signed-off-by: Thierry Reding --- drivers/clk/clk.c | 8 ++++++++ include/linux/clk-provider.h | 1 + 2 files changed, 9 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 459ce9da13e0..5315a273eae9 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2020,6 +2020,14 @@ static void clk_core_reparent(struct clk_core *clk, __clk_recalc_rates(clk, POST_RATE_CHANGE); } +void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent) +{ + if (!hw) + return; + + clk_core_reparent(hw->core, !new_parent ? NULL : new_parent->core); +} + /** * clk_has_parent - check if a clock is a possible parent for another * @clk: clock source diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index df695313f975..51efb9ec3e37 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -589,6 +589,7 @@ long __clk_mux_determine_rate_closest(struct clk_hw *hw, unsigned long rate, unsigned long max_rate, unsigned long *best_parent_rate, struct clk_hw **best_parent_p); +void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent); static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw *src) { -- cgit v1.2.3-58-ga151 From e34812c5bac8de641a2551b5ccb601627c10e772 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Thu, 12 Mar 2015 15:47:56 +0100 Subject: of: document new emc-timings subnode in nvidia,tegra124-car The EMC clock needs some extra information for changing its rate. Signed-off-by: Tomeu Vizoso Signed-off-by: Thierry Reding --- .../bindings/clock/nvidia,tegra124-car.txt | 42 +++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.txt index c6620bc96703..c3891cece73f 100644 --- a/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.txt +++ b/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.txt @@ -21,10 +21,31 @@ Required properties : In clock consumers, this cell represents the bit number in the CAR's array of CLK_RST_CONTROLLER_RST_DEVICES_* registers. +The node should contain a "emc-timings" subnode for each supported RAM type (see +field RAM_CODE in register PMC_STRAPPING_OPT_A). + +Required properties for "emc-timings" nodes : +- nvidia,ram-code : Should contain the value of RAM_CODE this timing set + is used for. + +Each "emc-timings" node should contain a "timing" subnode for every supported +EMC clock rate. + +Required properties for "timing" nodes : +- clock-frequency : Should contain the memory clock rate to which this timing +relates. +- nvidia,parent-clock-frequency : Should contain the rate at which the current +parent of the EMC clock should be running at this timing. +- clocks : Must contain an entry for each entry in clock-names. + See ../clocks/clock-bindings.txt for details. +- clock-names : Must include the following entries: + - emc-parent : the clock that should be the parent of the EMC clock at this +timing. + Example SoC include file: / { - tegra_car: clock { + tegra_car: clock@60006000 { compatible = "nvidia,tegra124-car"; reg = <0x60006000 0x1000>; #clock-cells = <1>; @@ -62,4 +83,23 @@ Example board file: &tegra_car { clocks = <&clk_32k> <&osc>; }; + + clock@60006000 { + emc-timings-3 { + nvidia,ram-code = <3>; + + timing-12750000 { + clock-frequency = <12750000>; + nvidia,parent-clock-frequency = <408000000>; + clocks = <&tegra_car TEGRA124_CLK_PLL_P>; + clock-names = "emc-parent"; + }; + timing-20400000 { + clock-frequency = <20400000>; + nvidia,parent-clock-frequency = <408000000>; + clocks = <&tegra_car TEGRA124_CLK_PLL_P>; + clock-names = "emc-parent"; + }; + }; + }; }; -- cgit v1.2.3-58-ga151 From ef03b35a9a2cadd6b3abc758dab4c580a454760c Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Thu, 12 Mar 2015 15:47:59 +0100 Subject: of: document external-memory-controller property in tegra124-car This property contains a phandle to the EMC driver that is needed by the EMC clock to request the EMC driver to do its part of the clock change sequence. Signed-off-by: Tomeu Vizoso Signed-off-by: Thierry Reding --- Documentation/devicetree/bindings/clock/nvidia,tegra124-car.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.txt b/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.txt index c3891cece73f..7f02fb4ca4ad 100644 --- a/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.txt +++ b/Documentation/devicetree/bindings/clock/nvidia,tegra124-car.txt @@ -20,6 +20,7 @@ Required properties : - #reset-cells : Should be 1. In clock consumers, this cell represents the bit number in the CAR's array of CLK_RST_CONTROLLER_RST_DEVICES_* registers. +- nvidia,external-memory-controller : phandle of the EMC driver. The node should contain a "emc-timings" subnode for each supported RAM type (see field RAM_CODE in register PMC_STRAPPING_OPT_A). @@ -50,6 +51,7 @@ Example SoC include file: reg = <0x60006000 0x1000>; #clock-cells = <1>; #reset-cells = <1>; + nvidia,external-memory-controller = <&emc>; }; usb@c5004000 { -- cgit v1.2.3-58-ga151 From 374ffadaf3abd4bf77073bd8f1acf0ada7dc797e Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Thu, 12 Mar 2015 15:47:53 +0100 Subject: clk: tegra: Remove old Tegra124 EMC clock This clock has never been able to do anything. Signed-off-by: Mikko Perttunen Signed-off-by: Tomeu Vizoso Signed-off-by: Thierry Reding --- drivers/clk/tegra/clk-tegra124.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index 11f857cd5f6a..62fe3cf7fcb5 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -791,7 +791,6 @@ static struct tegra_clk tegra124_clks[tegra_clk_max] __initdata = { [tegra_clk_i2c2] = { .dt_id = TEGRA124_CLK_I2C2, .present = true }, [tegra_clk_uartc] = { .dt_id = TEGRA124_CLK_UARTC, .present = true }, [tegra_clk_mipi_cal] = { .dt_id = TEGRA124_CLK_MIPI_CAL, .present = true }, - [tegra_clk_emc] = { .dt_id = TEGRA124_CLK_EMC, .present = true }, [tegra_clk_usb2] = { .dt_id = TEGRA124_CLK_USB2, .present = true }, [tegra_clk_usb3] = { .dt_id = TEGRA124_CLK_USB3, .present = true }, [tegra_clk_vde_8] = { .dt_id = TEGRA124_CLK_VDE, .present = true }, -- cgit v1.2.3-58-ga151 From 2db04f16b589c6c96bd07df3f1ef8558bfdb6810 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Thu, 12 Mar 2015 15:48:05 +0100 Subject: clk: tegra: Add EMC clock driver The driver is currently only tested on Tegra124 Jetson TK1, but should work with other Tegra124 boards, provided that correct EMC tables are provided through the device tree. Older chip models have differing timing change sequences, so they are not currently supported. Signed-off-by: Mikko Perttunen Signed-off-by: Tomeu Vizoso [treding@nvidia.com: use more consistent function names] Signed-off-by: Thierry Reding --- drivers/clk/tegra/Makefile | 2 +- drivers/clk/tegra/clk-emc.c | 527 +++++++++++++++++++++++++++++++++++++++ drivers/clk/tegra/clk-tegra124.c | 5 +- drivers/clk/tegra/clk.h | 3 + 4 files changed, 535 insertions(+), 2 deletions(-) create mode 100644 drivers/clk/tegra/clk-emc.c diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index edb8358fa6ce..18c28d1ae400 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -14,5 +14,5 @@ obj-y += clk-tegra-super-gen4.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o -obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o +obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o clk-emc.o obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c new file mode 100644 index 000000000000..32e5563711ac --- /dev/null +++ b/drivers/clk/tegra/clk-emc.c @@ -0,0 +1,527 @@ +/* + * drivers/clk/tegra/clk-emc.c + * + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved. + * + * Author: + * Mikko Perttunen + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "clk.h" + +#define CLK_SOURCE_EMC 0x19c + +#define CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_SHIFT 0 +#define CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_MASK 0xff +#define CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR(x) (((x) & CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_MASK) << \ + CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_SHIFT) + +#define CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT 29 +#define CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK 0x7 +#define CLK_SOURCE_EMC_EMC_2X_CLK_SRC(x) (((x) & CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK) << \ + CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT) + +static const char * const emc_parent_clk_names[] = { + "pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud", + "pll_c2", "pll_c3", "pll_c_ud" +}; + +/* + * List of clock sources for various parents the EMC clock can have. + * When we change the timing to a timing with a parent that has the same + * clock source as the current parent, we must first change to a backup + * timing that has a different clock source. + */ + +#define EMC_SRC_PLL_M 0 +#define EMC_SRC_PLL_C 1 +#define EMC_SRC_PLL_P 2 +#define EMC_SRC_CLK_M 3 +#define EMC_SRC_PLL_C2 4 +#define EMC_SRC_PLL_C3 5 + +static const char emc_parent_clk_sources[] = { + EMC_SRC_PLL_M, EMC_SRC_PLL_C, EMC_SRC_PLL_P, EMC_SRC_CLK_M, + EMC_SRC_PLL_M, EMC_SRC_PLL_C2, EMC_SRC_PLL_C3, EMC_SRC_PLL_C +}; + +struct emc_timing { + unsigned long rate, parent_rate; + u8 parent_index; + struct clk *parent; + u32 ram_code; +}; + +struct tegra_clk_emc { + struct clk_hw hw; + void __iomem *clk_regs; + struct clk *prev_parent; + bool changing_timing; + + struct device_node *emc_node; + struct tegra_emc *emc; + + int num_timings; + struct emc_timing *timings; + spinlock_t *lock; +}; + +/* Common clock framework callback implementations */ + +static unsigned long emc_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct tegra_clk_emc *tegra; + u32 val, div; + + tegra = container_of(hw, struct tegra_clk_emc, hw); + + /* + * CCF wrongly assumes that the parent won't change during set_rate, + * so get the parent rate explicitly. + */ + parent_rate = __clk_get_rate(__clk_get_parent(hw->clk)); + + val = readl(tegra->clk_regs + CLK_SOURCE_EMC); + div = val & CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR_MASK; + + return parent_rate / (div + 2) * 2; +} + +/* + * Rounds up unless no higher rate exists, in which case down. This way is + * safer since things have EMC rate floors. Also don't touch parent_rate + * since we don't want the CCF to play with our parent clocks. + */ +static long emc_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct tegra_clk_emc *tegra; + u8 ram_code = tegra_read_ram_code(); + struct emc_timing *timing = NULL; + int i; + + tegra = container_of(hw, struct tegra_clk_emc, hw); + + for (i = 0; i < tegra->num_timings; i++) { + if (tegra->timings[i].ram_code != ram_code) + continue; + + timing = tegra->timings + i; + + if (timing->rate >= rate) + return timing->rate; + } + + if (timing) + return timing->rate; + + return __clk_get_rate(hw->clk); +} + +static u8 emc_get_parent(struct clk_hw *hw) +{ + struct tegra_clk_emc *tegra; + u32 val; + + tegra = container_of(hw, struct tegra_clk_emc, hw); + + val = readl(tegra->clk_regs + CLK_SOURCE_EMC); + + return (val >> CLK_SOURCE_EMC_EMC_2X_CLK_SRC_SHIFT) + & CLK_SOURCE_EMC_EMC_2X_CLK_SRC_MASK; +} + +static struct tegra_emc *emc_ensure_emc_driver(struct tegra_clk_emc *tegra) +{ + struct platform_device *pdev; + + if (tegra->emc) + return tegra->emc; + + if (!tegra->emc_node) + return NULL; + + pdev = of_find_device_by_node(tegra->emc_node); + if (!pdev) { + pr_err("%s: could not get external memory controller\n", + __func__); + return NULL; + } + + of_node_put(tegra->emc_node); + tegra->emc_node = NULL; + + tegra->emc = platform_get_drvdata(pdev); + if (!tegra->emc) { + pr_err("%s: cannot find EMC driver\n", __func__); + return NULL; + } + + return tegra->emc; +} + +static int emc_set_timing(struct tegra_clk_emc *tegra, + struct emc_timing *timing) +{ + int err; + u8 div; + u32 car_value; + unsigned long flags = 0; + struct tegra_emc *emc = emc_ensure_emc_driver(tegra); + + if (!emc) + return -ENOENT; + + pr_debug("going to rate %ld prate %ld p %s\n", timing->rate, + timing->parent_rate, __clk_get_name(timing->parent)); + + if (emc_get_parent(&tegra->hw) == timing->parent_index && + clk_get_rate(timing->parent) != timing->parent_rate) { + BUG(); + return -EINVAL; + } + + tegra->changing_timing = true; + + err = clk_set_rate(timing->parent, timing->parent_rate); + if (err) { + pr_err("cannot change parent %s rate to %ld: %d\n", + __clk_get_name(timing->parent), timing->parent_rate, + err); + + return err; + } + + err = clk_prepare_enable(timing->parent); + if (err) { + pr_err("cannot enable parent clock: %d\n", err); + return err; + } + + div = timing->parent_rate / (timing->rate / 2) - 2; + + err = tegra_emc_prepare_timing_change(emc, timing->rate); + if (err) + return err; + + spin_lock_irqsave(tegra->lock, flags); + + car_value = readl(tegra->clk_regs + CLK_SOURCE_EMC); + + car_value &= ~CLK_SOURCE_EMC_EMC_2X_CLK_SRC(~0); + car_value |= CLK_SOURCE_EMC_EMC_2X_CLK_SRC(timing->parent_index); + + car_value &= ~CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR(~0); + car_value |= CLK_SOURCE_EMC_EMC_2X_CLK_DIVISOR(div); + + writel(car_value, tegra->clk_regs + CLK_SOURCE_EMC); + + spin_unlock_irqrestore(tegra->lock, flags); + + tegra_emc_complete_timing_change(emc, timing->rate); + + clk_hw_reparent(&tegra->hw, __clk_get_hw(timing->parent)); + clk_disable_unprepare(tegra->prev_parent); + + tegra->prev_parent = timing->parent; + tegra->changing_timing = false; + + return 0; +} + +/* + * Get backup timing to use as an intermediate step when a change between + * two timings with the same clock source has been requested. First try to + * find a timing with a higher clock rate to avoid a rate below any set rate + * floors. If that is not possible, find a lower rate. + */ +static struct emc_timing *get_backup_timing(struct tegra_clk_emc *tegra, + int timing_index) +{ + int i; + u32 ram_code = tegra_read_ram_code(); + struct emc_timing *timing; + + for (i = timing_index+1; i < tegra->num_timings; i++) { + timing = tegra->timings + i; + if (timing->ram_code != ram_code) + continue; + + if (emc_parent_clk_sources[timing->parent_index] != + emc_parent_clk_sources[ + tegra->timings[timing_index].parent_index]) + return timing; + } + + for (i = timing_index-1; i >= 0; --i) { + timing = tegra->timings + i; + if (timing->ram_code != ram_code) + continue; + + if (emc_parent_clk_sources[timing->parent_index] != + emc_parent_clk_sources[ + tegra->timings[timing_index].parent_index]) + return timing; + } + + return NULL; +} + +static int emc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct tegra_clk_emc *tegra; + struct emc_timing *timing = NULL; + int i, err; + u32 ram_code = tegra_read_ram_code(); + + tegra = container_of(hw, struct tegra_clk_emc, hw); + + if (__clk_get_rate(hw->clk) == rate) + return 0; + + /* + * When emc_set_timing changes the parent rate, CCF will propagate + * that downward to us, so ignore any set_rate calls while a rate + * change is already going on. + */ + if (tegra->changing_timing) + return 0; + + for (i = 0; i < tegra->num_timings; i++) { + if (tegra->timings[i].rate == rate && + tegra->timings[i].ram_code == ram_code) { + timing = tegra->timings + i; + break; + } + } + + if (!timing) { + pr_err("cannot switch to rate %ld without emc table\n", rate); + return -EINVAL; + } + + if (emc_parent_clk_sources[emc_get_parent(hw)] == + emc_parent_clk_sources[timing->parent_index] && + clk_get_rate(timing->parent) != timing->parent_rate) { + /* + * Parent clock source not changed but parent rate has changed, + * need to temporarily switch to another parent + */ + + struct emc_timing *backup_timing; + + backup_timing = get_backup_timing(tegra, i); + if (!backup_timing) { + pr_err("cannot find backup timing\n"); + return -EINVAL; + } + + pr_debug("using %ld as backup rate when going to %ld\n", + backup_timing->rate, rate); + + err = emc_set_timing(tegra, backup_timing); + if (err) { + pr_err("cannot set backup timing: %d\n", err); + return err; + } + } + + return emc_set_timing(tegra, timing); +} + +/* Initialization and deinitialization */ + +static int load_one_timing_from_dt(struct tegra_clk_emc *tegra, + struct emc_timing *timing, + struct device_node *node) +{ + int err, i; + u32 tmp; + + err = of_property_read_u32(node, "clock-frequency", &tmp); + if (err) { + pr_err("timing %s: failed to read rate\n", node->full_name); + return err; + } + + timing->rate = tmp; + + err = of_property_read_u32(node, "nvidia,parent-clock-frequency", &tmp); + if (err) { + pr_err("timing %s: failed to read parent rate\n", + node->full_name); + return err; + } + + timing->parent_rate = tmp; + + timing->parent = of_clk_get_by_name(node, "emc-parent"); + if (IS_ERR(timing->parent)) { + pr_err("timing %s: failed to get parent clock\n", + node->full_name); + return PTR_ERR(timing->parent); + } + + timing->parent_index = 0xff; + for (i = 0; i < ARRAY_SIZE(emc_parent_clk_names); i++) { + if (!strcmp(emc_parent_clk_names[i], + __clk_get_name(timing->parent))) { + timing->parent_index = i; + break; + } + } + if (timing->parent_index == 0xff) { + pr_err("timing %s: %s is not a valid parent\n", + node->full_name, __clk_get_name(timing->parent)); + clk_put(timing->parent); + return -EINVAL; + } + + return 0; +} + +static int cmp_timings(const void *_a, const void *_b) +{ + const struct emc_timing *a = _a; + const struct emc_timing *b = _b; + + if (a->rate < b->rate) + return -1; + else if (a->rate == b->rate) + return 0; + else + return 1; +} + +static int load_timings_from_dt(struct tegra_clk_emc *tegra, + struct device_node *node, + u32 ram_code) +{ + struct device_node *child; + int child_count = of_get_child_count(node); + int i = 0, err; + + tegra->timings = kcalloc(child_count, sizeof(struct emc_timing), + GFP_KERNEL); + if (!tegra->timings) + return -ENOMEM; + + tegra->num_timings = child_count; + + for_each_child_of_node(node, child) { + struct emc_timing *timing = tegra->timings + (i++); + + err = load_one_timing_from_dt(tegra, timing, child); + if (err) + return err; + + timing->ram_code = ram_code; + } + + sort(tegra->timings, tegra->num_timings, sizeof(struct emc_timing), + cmp_timings, NULL); + + return 0; +} + +static const struct clk_ops tegra_clk_emc_ops = { + .recalc_rate = emc_recalc_rate, + .round_rate = emc_round_rate, + .set_rate = emc_set_rate, + .get_parent = emc_get_parent, +}; + +struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, + spinlock_t *lock) +{ + struct tegra_clk_emc *tegra; + struct clk_init_data init; + struct device_node *node; + u32 node_ram_code; + struct clk *clk; + int err; + + tegra = kcalloc(1, sizeof(*tegra), GFP_KERNEL); + if (!tegra) + return ERR_PTR(-ENOMEM); + + tegra->clk_regs = base; + tegra->lock = lock; + + tegra->num_timings = 0; + + for_each_child_of_node(np, node) { + err = of_property_read_u32(node, "nvidia,ram-code", + &node_ram_code); + if (err) { + of_node_put(node); + continue; + } + + /* + * Store timings for all ram codes as we cannot read the + * fuses until the apbmisc driver is loaded. + */ + err = load_timings_from_dt(tegra, node, node_ram_code); + if (err) + return ERR_PTR(err); + of_node_put(node); + break; + } + + if (tegra->num_timings == 0) + pr_warn("%s: no memory timings registered\n", __func__); + + tegra->emc_node = of_parse_phandle(np, + "nvidia,external-memory-controller", 0); + if (!tegra->emc_node) + pr_warn("%s: couldn't find node for EMC driver\n", __func__); + + init.name = "emc"; + init.ops = &tegra_clk_emc_ops; + init.flags = 0; + init.parent_names = emc_parent_clk_names; + init.num_parents = ARRAY_SIZE(emc_parent_clk_names); + + tegra->hw.init = &init; + + clk = clk_register(NULL, &tegra->hw); + if (IS_ERR(clk)) + return clk; + + tegra->prev_parent = clk_get_parent_by_index( + tegra->hw.clk, emc_get_parent(&tegra->hw)); + tegra->changing_timing = false; + + /* Allow debugging tools to see the EMC clock */ + clk_register_clkdev(clk, "emc", "tegra-clk-debug"); + + clk_prepare_enable(clk); + + return clk; +}; diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index 62fe3cf7fcb5..096261d0a44e 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -1388,7 +1388,6 @@ static struct tegra_clk_init_table common_init_table[] __initdata = { {TEGRA124_CLK_XUSB_HOST_SRC, TEGRA124_CLK_PLL_RE_OUT, 112000000, 0}, {TEGRA124_CLK_SATA, TEGRA124_CLK_PLL_P, 104000000, 0}, {TEGRA124_CLK_SATA_OOB, TEGRA124_CLK_PLL_P, 204000000, 0}, - {TEGRA124_CLK_EMC, TEGRA124_CLK_CLK_MAX, 0, 1}, {TEGRA124_CLK_MSELECT, TEGRA124_CLK_CLK_MAX, 0, 1}, {TEGRA124_CLK_CSITE, TEGRA124_CLK_CLK_MAX, 0, 1}, {TEGRA124_CLK_TSENSOR, TEGRA124_CLK_CLK_M, 400000, 0}, @@ -1512,6 +1511,10 @@ static void __init tegra124_132_clock_init_post(struct device_node *np) tegra_super_clk_gen4_init(clk_base, pmc_base, tegra124_clks, &pll_x_params); tegra_add_of_provider(np); + + clks[TEGRA124_CLK_EMC] = tegra_clk_register_emc(clk_base, np, + &emc_lock); + tegra_register_devclks(devclks, ARRAY_SIZE(devclks)); tegra_cpu_car_ops = &tegra124_cpu_car_ops; diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index d6ac00647faf..e69e98a7bc80 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -623,6 +623,9 @@ void tegra_super_clk_gen4_init(void __iomem *clk_base, void __iomem *pmc_base, struct tegra_clk *tegra_clks, struct tegra_clk_pll_params *pll_params); +struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, + spinlock_t *lock); + void tegra114_clock_tune_cpu_trimmers_high(void); void tegra114_clock_tune_cpu_trimmers_low(void); void tegra114_clock_tune_cpu_trimmers_init(void); -- cgit v1.2.3-58-ga151 From ac67477f8f4163a6e7678f252030051f4eef2d5f Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Thu, 12 Mar 2015 15:48:07 +0100 Subject: clk: tegra: Set the EMC clock as the parent of the MC clock On Tegra124, as we now have a proper driver for the EMC. Signed-off-by: Tomeu Vizoso Signed-off-by: Thierry Reding --- drivers/clk/tegra/clk-tegra124.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/clk/tegra/clk-tegra124.c b/drivers/clk/tegra/clk-tegra124.c index 096261d0a44e..e8cca3eac007 100644 --- a/drivers/clk/tegra/clk-tegra124.c +++ b/drivers/clk/tegra/clk-tegra124.c @@ -152,11 +152,6 @@ static unsigned long tegra124_input_freq[] = { [12] = 260000000, }; -static const char *mux_pllmcp_clkm[] = { - "pll_m", "pll_c", "pll_p", "clk_m", "pll_m_ud", "pll_c2", "pll_c3", -}; -#define mux_pllmcp_clkm_idx NULL - static struct div_nmp pllxc_nmp = { .divm_shift = 0, .divm_width = 8, @@ -1126,13 +1121,7 @@ static __init void tegra124_periph_clk_init(void __iomem *clk_base, periph_clk_enb_refcnt); clks[TEGRA124_CLK_DSIB] = clk; - /* emc mux */ - clk = clk_register_mux(NULL, "emc_mux", mux_pllmcp_clkm, - ARRAY_SIZE(mux_pllmcp_clkm), 0, - clk_base + CLK_SOURCE_EMC, - 29, 3, 0, &emc_lock); - - clk = tegra_clk_register_mc("mc", "emc_mux", clk_base + CLK_SOURCE_EMC, + clk = tegra_clk_register_mc("mc", "emc", clk_base + CLK_SOURCE_EMC, &emc_lock); clks[TEGRA124_CLK_MC] = clk; -- cgit v1.2.3-58-ga151 From 890d6a54ead9dafdfdeebe65cbb10056e14c835a Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Tue, 17 Mar 2015 10:36:13 +0100 Subject: clk: tegra: Have EMC clock implement determine_rate() As opposed to round_rate(), determine_rate() can take rate constraints into account when choosing the best rate. Signed-off-by: Tomeu Vizoso Signed-off-by: Thierry Reding --- drivers/clk/tegra/clk-emc.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/clk/tegra/clk-emc.c b/drivers/clk/tegra/clk-emc.c index 32e5563711ac..7649685c86bc 100644 --- a/drivers/clk/tegra/clk-emc.c +++ b/drivers/clk/tegra/clk-emc.c @@ -116,8 +116,11 @@ static unsigned long emc_recalc_rate(struct clk_hw *hw, * safer since things have EMC rate floors. Also don't touch parent_rate * since we don't want the CCF to play with our parent clocks. */ -static long emc_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static long emc_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long min_rate, + unsigned long max_rate, + unsigned long *best_parent_rate, + struct clk_hw **best_parent_hw) { struct tegra_clk_emc *tegra; u8 ram_code = tegra_read_ram_code(); @@ -126,18 +129,26 @@ static long emc_round_rate(struct clk_hw *hw, unsigned long rate, tegra = container_of(hw, struct tegra_clk_emc, hw); - for (i = 0; i < tegra->num_timings; i++) { - if (tegra->timings[i].ram_code != ram_code) - continue; + for (i = 0; i < tegra->num_timings; i++) { + if (tegra->timings[i].ram_code != ram_code) + continue; + + timing = tegra->timings + i; - timing = tegra->timings + i; + if (timing->rate > max_rate) { + i = min(i, 1); + return tegra->timings[i - 1].rate; + } + + if (timing->rate < min_rate) + continue; - if (timing->rate >= rate) - return timing->rate; - } + if (timing->rate >= rate) + return timing->rate; + } - if (timing) - return timing->rate; + if (timing) + return timing->rate; return __clk_get_rate(hw->clk); } @@ -451,7 +462,7 @@ static int load_timings_from_dt(struct tegra_clk_emc *tegra, static const struct clk_ops tegra_clk_emc_ops = { .recalc_rate = emc_recalc_rate, - .round_rate = emc_round_rate, + .determine_rate = emc_determine_rate, .set_rate = emc_set_rate, .get_parent = emc_get_parent, }; -- cgit v1.2.3-58-ga151 From 31b52ba42d71e9ed8ee544e64c340ec6c571272f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 1 Apr 2015 09:10:58 +0200 Subject: clk: tegra: EMC clock driver depends on EMC driver The EMC clock driver uses symbols exported by the EMC driver, so it needs the corresponding dependency to avoid build breakage. Signed-off-by: Thierry Reding --- drivers/clk/Kconfig | 1 + drivers/clk/tegra/Kconfig | 3 +++ drivers/clk/tegra/Makefile | 3 ++- drivers/clk/tegra/clk.h | 9 +++++++++ 4 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/tegra/Kconfig diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 9897f353bf1a..ff20c485418a 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -158,3 +158,4 @@ source "drivers/clk/bcm/Kconfig" source "drivers/clk/mvebu/Kconfig" source "drivers/clk/samsung/Kconfig" +source "drivers/clk/tegra/Kconfig" diff --git a/drivers/clk/tegra/Kconfig b/drivers/clk/tegra/Kconfig new file mode 100644 index 000000000000..1ba30d1e14f2 --- /dev/null +++ b/drivers/clk/tegra/Kconfig @@ -0,0 +1,3 @@ +config TEGRA_CLK_EMC + def_bool y + depends on TEGRA124_EMC diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile index 18c28d1ae400..aec862ba7a17 100644 --- a/drivers/clk/tegra/Makefile +++ b/drivers/clk/tegra/Makefile @@ -11,8 +11,9 @@ obj-y += clk-tegra-periph.o obj-y += clk-tegra-pmc.o obj-y += clk-tegra-fixed.o obj-y += clk-tegra-super-gen4.o +obj-$(CONFIG_TEGRA_CLK_EMC) += clk-emc.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o -obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o clk-emc.o +obj-$(CONFIG_ARCH_TEGRA_124_SOC) += clk-tegra124.o obj-$(CONFIG_ARCH_TEGRA_132_SOC) += clk-tegra124.o diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index e69e98a7bc80..75ddc8ff8bd4 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -623,8 +623,17 @@ void tegra_super_clk_gen4_init(void __iomem *clk_base, void __iomem *pmc_base, struct tegra_clk *tegra_clks, struct tegra_clk_pll_params *pll_params); +#ifdef CONFIG_TEGRA_CLK_EMC struct clk *tegra_clk_register_emc(void __iomem *base, struct device_node *np, spinlock_t *lock); +#else +static inline struct clk *tegra_clk_register_emc(void __iomem *base, + struct device_node *np, + spinlock_t *lock) +{ + return NULL; +} +#endif void tegra114_clock_tune_cpu_trimmers_high(void); void tegra114_clock_tune_cpu_trimmers_low(void); -- cgit v1.2.3-58-ga151 From 36b7be6d3ea8f434f1e0723f3fb0e85c3e00ebc2 Mon Sep 17 00:00:00 2001 From: Marcel Ziswiler Date: Fri, 10 Apr 2015 23:35:57 +0200 Subject: clk: tegra: Fix hda2codec_2x clock name for Tegra30 The HDA to codec clock is named hda2codec_2x, so use the proper name in the clock table. Signed-off-by: Marcel Ziswiler Signed-off-by: Thierry Reding --- drivers/clk/tegra/clk-tegra30.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/tegra/clk-tegra30.c b/drivers/clk/tegra/clk-tegra30.c index 4b26509fc218..0af3e834dd24 100644 --- a/drivers/clk/tegra/clk-tegra30.c +++ b/drivers/clk/tegra/clk-tegra30.c @@ -679,7 +679,7 @@ static struct tegra_devclk devclks[] __initdata = { { .dev_id = "tegra30-dam.1", .dt_id = TEGRA30_CLK_DAM1 }, { .dev_id = "tegra30-dam.2", .dt_id = TEGRA30_CLK_DAM2 }, { .con_id = "hda", .dev_id = "tegra30-hda", .dt_id = TEGRA30_CLK_HDA }, - { .con_id = "hda2codec", .dev_id = "tegra30-hda", .dt_id = TEGRA30_CLK_HDA2CODEC_2X }, + { .con_id = "hda2codec_2x", .dev_id = "tegra30-hda", .dt_id = TEGRA30_CLK_HDA2CODEC_2X }, { .dev_id = "spi_tegra.0", .dt_id = TEGRA30_CLK_SBC1 }, { .dev_id = "spi_tegra.1", .dt_id = TEGRA30_CLK_SBC2 }, { .dev_id = "spi_tegra.2", .dt_id = TEGRA30_CLK_SBC3 }, -- cgit v1.2.3-58-ga151 From 2073b5e909b96f8fbc9b8a40b89c1fa316fb092c Mon Sep 17 00:00:00 2001 From: Sergej Sawazki Date: Mon, 11 May 2015 10:44:50 +0200 Subject: clk: si5351: fix .round_rate for multisynth 6-7 The divider calculation for multisynth 6 and 7 differs from the calculation for multisynth 0-5. For MS6 and MS7, set MSx_P1 directly, MSx_P1=divide value [AN619, p. 6]. Referenced document: [AN619] Manually Generating an Si5351 Register Map, Rev. 0.4 Signed-off-by: Sergej Sawazki Signed-off-by: Michael Turquette --- drivers/clk/clk-si5351.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 30335d3b99af..48c5298b6bb1 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -552,7 +552,8 @@ static const struct clk_ops si5351_pll_ops = { * MSx_P2[19:0] = 128 * b - c * floor(128 * b/c) = (128*b) mod c * MSx_P3[19:0] = c * - * MS[6,7] are integer (P1) divide only, P2 = 0, P3 = 0 + * MS[6,7] are integer (P1) divide only, P1 = divide value, + * P2 and P3 are not applicable * * for 150MHz < fOUT <= 160MHz: * @@ -679,6 +680,16 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate, c = 1; *parent_rate = a * rate; + } else if (hwdata->num >= 6) { + /* determine the closest integer divider */ + a = DIV_ROUND_CLOSEST(*parent_rate, rate); + if (a < SI5351_MULTISYNTH_A_MIN) + a = SI5351_MULTISYNTH_A_MIN; + if (a > SI5351_MULTISYNTH67_A_MAX) + a = SI5351_MULTISYNTH67_A_MAX; + + b = 0; + c = 1; } else { unsigned long rfrac, denom; @@ -692,9 +703,7 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate, a = *parent_rate / rate; if (a < SI5351_MULTISYNTH_A_MIN) a = SI5351_MULTISYNTH_A_MIN; - if (hwdata->num >= 6 && a > SI5351_MULTISYNTH67_A_MAX) - a = SI5351_MULTISYNTH67_A_MAX; - else if (a > SI5351_MULTISYNTH_A_MAX) + if (a > SI5351_MULTISYNTH_A_MAX) a = SI5351_MULTISYNTH_A_MAX; /* find best approximation for b/c = fVCO mod fOUT */ @@ -723,6 +732,10 @@ static long si5351_msynth_round_rate(struct clk_hw *hw, unsigned long rate, hwdata->params.p3 = 1; hwdata->params.p2 = 0; hwdata->params.p1 = 0; + } else if (hwdata->num >= 6) { + hwdata->params.p3 = 0; + hwdata->params.p2 = 0; + hwdata->params.p1 = a; } else { hwdata->params.p3 = c; hwdata->params.p2 = (128 * b) % c; -- cgit v1.2.3-58-ga151 From 45b03d3e88c2068644e377cf08fc480208700b85 Mon Sep 17 00:00:00 2001 From: Sergej Sawazki Date: Mon, 11 May 2015 10:44:59 +0200 Subject: clk: si5351: fix .recalc_rate for multisynth 6-7 MS6 and MS7 do not have the MSx_P3 field. Do the 'params.p3 == 0' check for MS0-M5 only. See [AN619, p. 6] for details. Referenced document: [AN619] Manually Generating an Si5351 Register Map, Rev. 0.4 Signed-off-by: Sergej Sawazki Signed-off-by: Michael Turquette --- drivers/clk/clk-si5351.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/clk/clk-si5351.c b/drivers/clk/clk-si5351.c index 48c5298b6bb1..e39e1e680b3c 100644 --- a/drivers/clk/clk-si5351.c +++ b/drivers/clk/clk-si5351.c @@ -607,9 +607,6 @@ static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw, if (!hwdata->params.valid) si5351_read_parameters(hwdata->drvdata, reg, &hwdata->params); - if (hwdata->params.p3 == 0) - return parent_rate; - /* * multisync0-5: fOUT = (128 * P3 * fIN) / (P1*P3 + P2 + 512*P3) * multisync6-7: fOUT = fIN / P1 @@ -617,6 +614,8 @@ static unsigned long si5351_msynth_recalc_rate(struct clk_hw *hw, rate = parent_rate; if (hwdata->num > 5) { m = hwdata->params.p1; + } else if (hwdata->params.p3 == 0) { + return parent_rate; } else if ((si5351_reg_read(hwdata->drvdata, reg + 2) & SI5351_OUTPUT_CLK_DIVBY4) == SI5351_OUTPUT_CLK_DIVBY4) { m = 4; -- cgit v1.2.3-58-ga151 From dbc3976d9123a00925e3c9893b3f40f58f82636c Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 7 May 2015 23:43:26 -0700 Subject: clk: bindings: Fix assigned-clock-rates description The binding uses assigned-clock-parents when it should use assigned-clock-rates. Furthermore, the part that describes how they relate to the assigned-clocks property is not clear about what is related. Correct and clarify this part of the binding. Reported-by: Krzysztof Kozlowski Signed-off-by: Stephen Boyd --- Documentation/devicetree/bindings/clock/clock-bindings.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/clock-bindings.txt b/Documentation/devicetree/bindings/clock/clock-bindings.txt index 06fc6d541c89..2ec489eebe72 100644 --- a/Documentation/devicetree/bindings/clock/clock-bindings.txt +++ b/Documentation/devicetree/bindings/clock/clock-bindings.txt @@ -138,9 +138,10 @@ Some platforms may require initial configuration of default parent clocks and clock frequencies. Such a configuration can be specified in a device tree node through assigned-clocks, assigned-clock-parents and assigned-clock-rates properties. The assigned-clock-parents property should contain a list of parent -clocks in form of phandle and clock specifier pairs, the assigned-clock-parents -property the list of assigned clock frequency values - corresponding to clocks -listed in the assigned-clocks property. +clocks in the form of a phandle and clock specifier pair and the +assigned-clock-rates property should contain a list of frequencies in Hz. Both +these properties should correspond to the clocks listed in the assigned-clocks +property. To skip setting parent or rate of a clock its corresponding entry should be set to 0, or can be omitted if it is not followed by any non-zero entry. -- cgit v1.2.3-58-ga151 From 42801ca46719b189d4090b76268e6db90fc01e3c Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Mon, 11 May 2015 11:20:06 +0100 Subject: clk: Update docs after removal of clk-private.h Currently Documentation/clk.txt describes an obsolete techinique to statically define struct clk objects. This capability was removed by b09d6d991025("clk: remove clk-private.h") and is no longer supported. The documentation describing the feature should be removed. Signed-off-by: Daniel Thompson Cc: Jonathan Corbet Cc: Michael Turquette Cc: Stephen Boyd Signed-off-by: Stephen Boyd --- Documentation/clk.txt | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/Documentation/clk.txt b/Documentation/clk.txt index 0e4f90aa1c13..f463bdc37f88 100644 --- a/Documentation/clk.txt +++ b/Documentation/clk.txt @@ -230,30 +230,7 @@ clk_register(...) See the basic clock types in drivers/clk/clk-*.c for examples. - Part 5 - static initialization of clock data - -For platforms with many clocks (often numbering into the hundreds) it -may be desirable to statically initialize some clock data. This -presents a problem since the definition of struct clk should be hidden -from everyone except for the clock core in drivers/clk/clk.c. - -To get around this problem struct clk's definition is exposed in -include/linux/clk-private.h along with some macros for more easily -initializing instances of the basic clock types. These clocks must -still be initialized with the common clock framework via a call to -__clk_init. - -clk-private.h must NEVER be included by code which implements struct -clk_ops callbacks, nor must it be included by any logic which pokes -around inside of struct clk at run-time. To do so is a layering -violation. - -To better enforce this policy, always follow this simple rule: any -statically initialized clock data MUST be defined in a separate file -from the logic that implements its ops. Basically separate the logic -from the data and all is well. - - Part 6 - Disabling clock gating of unused clocks + Part 5 - Disabling clock gating of unused clocks Sometimes during development it can be useful to be able to bypass the default disabling of unused clocks. For example, if drivers aren't enabling @@ -264,7 +241,7 @@ are sorted out. To bypass this disabling, include "clk_ignore_unused" in the bootargs to the kernel. - Part 7 - Locking + Part 6 - Locking The common clock framework uses two global locks, the prepare lock and the enable lock. -- cgit v1.2.3-58-ga151 From b1ed311baf66d7e826f07f456b9f7945fa300aaf Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Sat, 2 May 2015 17:03:21 +0200 Subject: clk: asm9260: Fix of_io_request_and_map error check of_io_request_and map returns an error pointer, but the current code assumes that on error the returned pointer will be NULL. Obviously, that makes the check completely useless. Change the test to actually check for the proper error code. Signed-off-by: Maxime Ripard Cc: Mike Turquette Cc: Stephen Boyd Cc: linux-clk@vger.kernel.org Signed-off-by: Stephen Boyd --- drivers/clk/clk-asm9260.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk-asm9260.c b/drivers/clk/clk-asm9260.c index 88f4ff6916fe..90897af8d9f7 100644 --- a/drivers/clk/clk-asm9260.c +++ b/drivers/clk/clk-asm9260.c @@ -274,7 +274,7 @@ static void __init asm9260_acc_init(struct device_node *np) u32 accuracy = 0; base = of_io_request_and_map(np, 0, np->name); - if (!base) + if (IS_ERR(base)) panic("%s: unable to map resource", np->name); /* register pll */ -- cgit v1.2.3-58-ga151 From beb7a2a97103ce31df639b35b6f3a07ca7f77188 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Wed, 13 May 2015 18:27:40 +0200 Subject: clk: axm55xx: Use %zu in pr_info for size_t Fix the following compiler warning: drivers/clk/clk-axm5516.c: In function 'axmclk_probe': drivers/clk/clk-axm5516.c:559:2: warning: format '%u' expects argument of type 'unsigned int', but argument 2 has type 'size_t' [-Wformat=] pr_info("axmclk: supporting %u clocks\n", num_clks); ^ Signed-off-by: Alexander Sverdlin Signed-off-by: Stephen Boyd --- drivers/clk/clk-axm5516.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk-axm5516.c b/drivers/clk/clk-axm5516.c index 0f6368ceec4c..c7c91a5ecf8b 100644 --- a/drivers/clk/clk-axm5516.c +++ b/drivers/clk/clk-axm5516.c @@ -556,7 +556,7 @@ static int axmclk_probe(struct platform_device *pdev) return PTR_ERR(regmap); num_clks = ARRAY_SIZE(axmclk_clocks); - pr_info("axmclk: supporting %u clocks\n", num_clks); + pr_info("axmclk: supporting %zu clocks\n", num_clks); priv = devm_kzalloc(dev, sizeof(*priv) + sizeof(*priv->clks) * num_clks, GFP_KERNEL); if (!priv) -- cgit v1.2.3-58-ga151 From e0cdcda508f110b7ec190dc7c5eb2869ba73a535 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 13 May 2015 15:54:40 +0900 Subject: clk: ti: dra7-atl-clock: Fix possible ERR_PTR dereference of_clk_get_from_provider() returns ERR_PTR on failure. The dra7-atl-clock driver was not checking its return value and immediately used it in __clk_get_hw(). __clk_get_hw() dereferences supplied clock, if it is not NULL, so in that case it would dereference an ERR_PTR. Fixes: 9ac33b0ce81f ("CLK: TI: Driver for DRA7 ATL (Audio Tracking Logic)") Signed-off-by: Krzysztof Kozlowski Signed-off-by: Stephen Boyd --- drivers/clk/ti/clk-dra7-atl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c index d86bc46b93bd..0a1df821860f 100644 --- a/drivers/clk/ti/clk-dra7-atl.c +++ b/drivers/clk/ti/clk-dra7-atl.c @@ -252,6 +252,11 @@ static int of_dra7_atl_clk_probe(struct platform_device *pdev) } clk = of_clk_get_from_provider(&clkspec); + if (IS_ERR(clk)) { + pr_err("%s: failed to get atl clock %d from provider\n", + __func__, i); + return PTR_ERR(clk); + } cdesc = to_atl_desc(__clk_get_hw(clk)); cdesc->cinfo = cinfo; -- cgit v1.2.3-58-ga151 From d122db7e86669244759226bfdd2b9d623d5c6ae8 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 14 May 2015 16:47:10 -0700 Subject: clk: basic-types: Remove useless allocation failure printks Printing an error on kmalloc() failures is unnecessary. Remove the print and use *ptr in sizeof() for future-proof code. Signed-off-by: Stephen Boyd --- drivers/clk/clk-composite.c | 4 +--- drivers/clk/clk-divider.c | 6 ++---- drivers/clk/clk-fixed-factor.c | 4 +--- drivers/clk/clk-fixed-rate.c | 6 ++---- drivers/clk/clk-fractional-divider.c | 4 +--- drivers/clk/clk-gate.c | 6 ++---- 6 files changed, 9 insertions(+), 21 deletions(-) diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c index 077f4c7148f1..616f5aef3c26 100644 --- a/drivers/clk/clk-composite.c +++ b/drivers/clk/clk-composite.c @@ -200,10 +200,8 @@ struct clk *clk_register_composite(struct device *dev, const char *name, struct clk_ops *clk_composite_ops; composite = kzalloc(sizeof(*composite), GFP_KERNEL); - if (!composite) { - pr_err("%s: could not allocate composite clk\n", __func__); + if (!composite) return ERR_PTR(-ENOMEM); - } init.name = name; init.flags = flags | CLK_IS_BASIC; diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 25006a8bb8e6..706b5783c360 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -430,11 +430,9 @@ static struct clk *_register_divider(struct device *dev, const char *name, } /* allocate the divider */ - div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL); - if (!div) { - pr_err("%s: could not allocate divider clk\n", __func__); + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &clk_divider_ops; diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index d9e3f671c2ea..e186db263d5e 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -74,10 +74,8 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name, struct clk *clk; fix = kmalloc(sizeof(*fix), GFP_KERNEL); - if (!fix) { - pr_err("%s: could not allocate fixed factor clk\n", __func__); + if (!fix) return ERR_PTR(-ENOMEM); - } /* struct clk_fixed_factor assignments */ fix->mult = mult; diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c index 0fc56ab6e844..f85ec8d1711f 100644 --- a/drivers/clk/clk-fixed-rate.c +++ b/drivers/clk/clk-fixed-rate.c @@ -65,11 +65,9 @@ struct clk *clk_register_fixed_rate_with_accuracy(struct device *dev, struct clk_init_data init; /* allocate fixed-rate clock */ - fixed = kzalloc(sizeof(struct clk_fixed_rate), GFP_KERNEL); - if (!fixed) { - pr_err("%s: could not allocate fixed clk\n", __func__); + fixed = kzalloc(sizeof(*fixed), GFP_KERNEL); + if (!fixed) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &clk_fixed_rate_ops; diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index 6aa72d9d79ba..140eb5844dc4 100644 --- a/drivers/clk/clk-fractional-divider.c +++ b/drivers/clk/clk-fractional-divider.c @@ -109,10 +109,8 @@ struct clk *clk_register_fractional_divider(struct device *dev, struct clk *clk; fd = kzalloc(sizeof(*fd), GFP_KERNEL); - if (!fd) { - dev_err(dev, "could not allocate fractional divider clk\n"); + if (!fd) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &clk_fractional_divider_ops; diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c index 3f0e4200cb5d..551dd0672794 100644 --- a/drivers/clk/clk-gate.c +++ b/drivers/clk/clk-gate.c @@ -135,11 +135,9 @@ struct clk *clk_register_gate(struct device *dev, const char *name, } /* allocate the gate */ - gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL); - if (!gate) { - pr_err("%s: could not allocate gated clk\n", __func__); + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) return ERR_PTR(-ENOMEM); - } init.name = name; init.ops = &clk_gate_ops; -- cgit v1.2.3-58-ga151 From 2bb5d1b88bb5c8dc929d3a24d5ad327c0281fdbf Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 12:14:24 -0700 Subject: clk: gpio-gate: Don't export __init functions This function is marked as __init, so exposing it to modules doesn't make any sense and it isn't used by modules anyway. drivers/clk/clk-gpio-gate.c:192:13: warning: symbol 'of_gpio_gate_clk_setup' was not declared. Should it be static? Cc: Jyri Sarha Signed-off-by: Stephen Boyd --- drivers/clk/clk-gpio-gate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/clk/clk-gpio-gate.c b/drivers/clk/clk-gpio-gate.c index a71cabedda93..f564e624fb93 100644 --- a/drivers/clk/clk-gpio-gate.c +++ b/drivers/clk/clk-gpio-gate.c @@ -189,7 +189,7 @@ static struct clk *of_clk_gpio_gate_delayed_register_get( /** * of_gpio_gate_clk_setup() - Setup function for gpio controlled clock */ -void __init of_gpio_gate_clk_setup(struct device_node *node) +static void __init of_gpio_gate_clk_setup(struct device_node *node) { struct clk_gpio_gate_delayed_register_data *data; @@ -203,6 +203,5 @@ void __init of_gpio_gate_clk_setup(struct device_node *node) of_clk_add_provider(node, of_clk_gpio_gate_delayed_register_get, data); } -EXPORT_SYMBOL_GPL(of_gpio_gate_clk_setup); CLK_OF_DECLARE(gpio_gate_clk, "gpio-gate-clock", of_gpio_gate_clk_setup); #endif -- cgit v1.2.3-58-ga151 From 562ef0b098552970fd25e9c960691e6c2bcb1181 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 12:16:14 -0700 Subject: clk: Silence sparse warnings about __clk_{get,put}() drivers/clk/clk.c:2700:5: warning: symbol '__clk_get' was not declared. Should it be static? drivers/clk/clk.c:2713:6: warning: symbol '__clk_put' was not declared. Should it be static? Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 659f2b036cf5..c44623fe9c48 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "clk.h" -- cgit v1.2.3-58-ga151 From 9a39222db60cca13c78effbff745801c7b3c17a9 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 12:17:37 -0700 Subject: clk: max-gen: Silence sparse warnings drivers/clk/clk-max-gen.c:82:16: warning: symbol 'max_gen_clk_ops' was not declared. Should it be static? drivers/clk/clk-max-gen.c:109:5: warning: symbol 'max_gen_clk_probe' was not declared. Should it be static? drivers/clk/clk-max-gen.c:183:5: warning: symbol 'max_gen_clk_remove' was not declared. Should it be static? Acked-by: Javier Martinez Canillas Signed-off-by: Stephen Boyd --- drivers/clk/clk-max-gen.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/clk/clk-max-gen.c b/drivers/clk/clk-max-gen.c index 6505049d50f1..35af9cb6da4f 100644 --- a/drivers/clk/clk-max-gen.c +++ b/drivers/clk/clk-max-gen.c @@ -31,6 +31,8 @@ #include #include +#include "clk-max-gen.h" + struct max_gen_clk { struct regmap *regmap; u32 mask; -- cgit v1.2.3-58-ga151 From 4cca1be4bca4b21657e2d99c43710dec14658673 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 12:21:53 -0700 Subject: clk: bcm/kona: Silence sparse warnings drivers/clk/bcm/clk-kona.c:1243:16: warning: odd constant _Bool cast (ffffffffffffffea becomes 1) Reviewed-by: Alex Elder Cc: Tim Kryger Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-kona.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index a0ef4f75d457..79a98506c433 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -1240,7 +1240,7 @@ static bool __kona_clk_init(struct kona_clk *bcm_clk) default: BUG(); } - return -EINVAL; + return false; } /* Set a CCU and all its clocks into their desired initial state */ -- cgit v1.2.3-58-ga151 From 7926b3f8c62628f51a85bbc5fd806087d3fdfbac Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 12:23:08 -0700 Subject: clk: bcm/kona: Remove ccu_list This list doesn't look to be used. Let's remove it and any associated code that would be manipulating this list. This also silences this error: drivers/clk/bcm/clk-kona-setup.c:24:1: warning: symbol 'ccu_list' was not declared. Should it be static? Reviewed-by: Alex Elder Cc: Tim Kryger Signed-off-by: Stephen Boyd --- drivers/clk/bcm/clk-kona-setup.c | 4 ---- drivers/clk/bcm/clk-kona.h | 2 -- 2 files changed, 6 deletions(-) diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c index e5aededdd322..deaa7f962b84 100644 --- a/drivers/clk/bcm/clk-kona-setup.c +++ b/drivers/clk/bcm/clk-kona-setup.c @@ -21,8 +21,6 @@ #define selector_clear_exists(sel) ((sel)->width = 0) #define trigger_clear_exists(trig) FLAG_CLEAR(trig, TRIG, EXISTS) -LIST_HEAD(ccu_list); /* The list of set up CCUs */ - /* Validity checking */ static bool ccu_data_offsets_valid(struct ccu_data *ccu) @@ -773,7 +771,6 @@ static void kona_ccu_teardown(struct ccu_data *ccu) of_clk_del_provider(ccu->node); /* safe if never added */ ccu_clks_teardown(ccu); - list_del(&ccu->links); of_node_put(ccu->node); ccu->node = NULL; iounmap(ccu->base); @@ -847,7 +844,6 @@ void __init kona_dt_ccu_setup(struct ccu_data *ccu, goto out_err; } ccu->node = of_node_get(node); - list_add_tail(&ccu->links, &ccu_list); /* * Set up each defined kona clock and save the result in diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h index 6849a64baf6d..906576ec97b6 100644 --- a/drivers/clk/bcm/clk-kona.h +++ b/drivers/clk/bcm/clk-kona.h @@ -480,7 +480,6 @@ struct ccu_data { spinlock_t lock; /* serialization lock */ bool write_enabled; /* write access is currently enabled */ struct ccu_policy policy; - struct list_head links; /* for ccu_list */ struct device_node *node; struct clk_onecell_data clk_data; const char *name; @@ -492,7 +491,6 @@ struct ccu_data { #define KONA_CCU_COMMON(_prefix, _name, _ccuname) \ .name = #_name "_ccu", \ .lock = __SPIN_LOCK_UNLOCKED(_name ## _ccu_data.lock), \ - .links = LIST_HEAD_INIT(_name ## _ccu_data.links), \ .clk_data = { \ .clk_num = _prefix ## _ ## _ccuname ## _CCU_CLOCK_COUNT, \ } -- cgit v1.2.3-58-ga151 From 47c18e4cd99463241fb8dfd9ac37a59cc4b65c82 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 12:24:44 -0700 Subject: clk: berlin: Silence sparse warning drivers/clk/berlin/berlin2-pll.c:94:12: warning: symbol 'berlin2_pll_register' was not declared. Should it be static? Cc: Alexandre Belloni Acked-by: Sebastian Hesselbarth Signed-off-by: Stephen Boyd --- drivers/clk/berlin/berlin2-pll.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/clk/berlin/berlin2-pll.c b/drivers/clk/berlin/berlin2-pll.c index bdc506b03824..f4b8d324b083 100644 --- a/drivers/clk/berlin/berlin2-pll.c +++ b/drivers/clk/berlin/berlin2-pll.c @@ -25,14 +25,7 @@ #include #include "berlin2-div.h" - -struct berlin2_pll_map { - const u8 vcodiv[16]; - u8 mult; - u8 fbdiv_shift; - u8 rfdiv_shift; - u8 divsel_shift; -}; +#include "berlin2-pll.h" struct berlin2_pll { struct clk_hw hw; -- cgit v1.2.3-58-ga151 From 4a77f817b32031aa13992710dfd5c2ec4f3949be Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 12:25:53 -0700 Subject: clk: hix5hd2: Silence sparse warnings drivers/clk/hisilicon/clk-hix5hd2.c:255:13: warning: symbol 'hix5hd2_clk_register_complex' was not declared. Should it be static? Acked-by: Zhangfei Gao Signed-off-by: Stephen Boyd --- drivers/clk/hisilicon/clk-hix5hd2.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/clk/hisilicon/clk-hix5hd2.c b/drivers/clk/hisilicon/clk-hix5hd2.c index f1d239435826..78bd95b6fc7b 100644 --- a/drivers/clk/hisilicon/clk-hix5hd2.c +++ b/drivers/clk/hisilicon/clk-hix5hd2.c @@ -252,8 +252,9 @@ static struct clk_ops clk_complex_ops = { .disable = clk_complex_disable, }; -void __init hix5hd2_clk_register_complex(struct hix5hd2_complex_clock *clks, - int nums, struct hisi_clock_data *data) +static void __init +hix5hd2_clk_register_complex(struct hix5hd2_complex_clock *clks, int nums, + struct hisi_clock_data *data) { void __iomem *base = data->base; int i; -- cgit v1.2.3-58-ga151 From f6704f9e1995c862d5281fc9efc0b4e95ee9f9b9 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 12:32:17 -0700 Subject: clk: samsung: Silence sparse warnings drivers/clk/samsung/clk-exynos5260.c:138:40: warning: Using plain integer as NULL pointer drivers/clk/samsung/clk-exynos5260.c:328:40: warning: Using plain integer as NULL pointer drivers/clk/samsung/clk-exynos5260.c:392:40: warning: Using plain integer as NULL pointer drivers/clk/samsung/clk-exynos5260.c:494:40: warning: Using plain integer as NULL pointer drivers/clk/samsung/clk-exynos5260.c:583:40: warning: Using plain integer as NULL pointer drivers/clk/samsung/clk-exynos5260.c:644:40: warning: Using plain integer as NULL pointer drivers/clk/samsung/clk-exynos5260.c:779:40: warning: Using plain integer as NULL pointer drivers/clk/samsung/clk-exynos5260.c:898:40: warning: Using plain integer as NULL pointer drivers/clk/samsung/clk-exynos5260.c:962:40: warning: Using plain integer as NULL pointer drivers/clk/samsung/clk-exynos5260.c:1018:40: warning: Using plain integer as NULL pointer drivers/clk/samsung/clk-exynos5260.c:1165:40: warning: Using plain integer as NULL pointer drivers/clk/samsung/clk-exynos5260.c:1373:40: warning: Using plain integer as NULL pointer drivers/clk/samsung/clk-exynos5260.c:1829:40: warning: Using plain integer as NULL pointer Acked-by: Sylwester Nawrocki Signed-off-by: Stephen Boyd --- drivers/clk/samsung/clk-exynos5260.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos5260.c b/drivers/clk/samsung/clk-exynos5260.c index df1d83c45554..06f96eb7cf93 100644 --- a/drivers/clk/samsung/clk-exynos5260.c +++ b/drivers/clk/samsung/clk-exynos5260.c @@ -135,7 +135,7 @@ static struct samsung_gate_clock aud_gate_clks[] __initdata = { static void __init exynos5260_clk_aud_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = aud_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(aud_mux_clks); @@ -325,7 +325,7 @@ static struct samsung_gate_clock disp_gate_clks[] __initdata = { static void __init exynos5260_clk_disp_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = disp_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(disp_mux_clks); @@ -389,7 +389,7 @@ static struct samsung_pll_clock egl_pll_clks[] __initdata = { static void __init exynos5260_clk_egl_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.pll_clks = egl_pll_clks; cmu.nr_pll_clks = ARRAY_SIZE(egl_pll_clks); @@ -491,7 +491,7 @@ static struct samsung_gate_clock fsys_gate_clks[] __initdata = { static void __init exynos5260_clk_fsys_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = fsys_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(fsys_mux_clks); @@ -580,7 +580,7 @@ static struct samsung_gate_clock g2d_gate_clks[] __initdata = { static void __init exynos5260_clk_g2d_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = g2d_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(g2d_mux_clks); @@ -641,7 +641,7 @@ static struct samsung_pll_clock g3d_pll_clks[] __initdata = { static void __init exynos5260_clk_g3d_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.pll_clks = g3d_pll_clks; cmu.nr_pll_clks = ARRAY_SIZE(g3d_pll_clks); @@ -776,7 +776,7 @@ static struct samsung_gate_clock gscl_gate_clks[] __initdata = { static void __init exynos5260_clk_gscl_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = gscl_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(gscl_mux_clks); @@ -895,7 +895,7 @@ static struct samsung_gate_clock isp_gate_clks[] __initdata = { static void __init exynos5260_clk_isp_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = isp_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(isp_mux_clks); @@ -959,7 +959,7 @@ static struct samsung_pll_clock kfc_pll_clks[] __initdata = { static void __init exynos5260_clk_kfc_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.pll_clks = kfc_pll_clks; cmu.nr_pll_clks = ARRAY_SIZE(kfc_pll_clks); @@ -1015,7 +1015,7 @@ static struct samsung_gate_clock mfc_gate_clks[] __initdata = { static void __init exynos5260_clk_mfc_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = mfc_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(mfc_mux_clks); @@ -1162,7 +1162,7 @@ static struct samsung_pll_clock mif_pll_clks[] __initdata = { static void __init exynos5260_clk_mif_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.pll_clks = mif_pll_clks; cmu.nr_pll_clks = ARRAY_SIZE(mif_pll_clks); @@ -1370,7 +1370,7 @@ static struct samsung_gate_clock peri_gate_clks[] __initdata = { static void __init exynos5260_clk_peri_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.mux_clks = peri_mux_clks; cmu.nr_mux_clks = ARRAY_SIZE(peri_mux_clks); @@ -1826,7 +1826,7 @@ static struct samsung_pll_clock top_pll_clks[] __initdata = { static void __init exynos5260_clk_top_init(struct device_node *np) { - struct samsung_cmu_info cmu = {0}; + struct samsung_cmu_info cmu = { NULL }; cmu.pll_clks = top_pll_clks; cmu.nr_pll_clks = ARRAY_SIZE(top_pll_clks); -- cgit v1.2.3-58-ga151 From 43f535702bb2c7b96091c0267fc105d44c18348c Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 12:33:49 -0700 Subject: clk: emev2: Silence sparse warnings drivers/clk/shmobile/clk-emev2.c:37:14: warning: symbol 'smu_base' was not declared. Should it be static? Cc: Takashi Yoshii Cc: Magnus Damm Acked-by: Simon Horman Signed-off-by: Stephen Boyd --- drivers/clk/shmobile/clk-emev2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/shmobile/clk-emev2.c b/drivers/clk/shmobile/clk-emev2.c index 6c7c929c7765..5b60beb7d0eb 100644 --- a/drivers/clk/shmobile/clk-emev2.c +++ b/drivers/clk/shmobile/clk-emev2.c @@ -34,7 +34,7 @@ static DEFINE_SPINLOCK(lock); /* not pretty, but hey */ -void __iomem *smu_base; +static void __iomem *smu_base; static void __init emev2_smu_write(unsigned long value, int offs) { -- cgit v1.2.3-58-ga151 From f53f551b11d112f6cdd6ab9ca320bfd890a9b021 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 12:36:24 -0700 Subject: clk: sirf: Silence sparse warnings These are __iomem pointers. Mark them appropriately so we don't get sparse errors like drivers/clk/sirf/clk-common.c:60:16: warning: incorrect type in argument 1 (different address spaces) drivers/clk/sirf/clk-common.c:60:16: expected void const volatile [noderef] *addr drivers/clk/sirf/clk-common.c:60:16: got void * Cc: Barry Song Signed-off-by: Stephen Boyd --- drivers/clk/sirf/clk-common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/sirf/clk-common.c b/drivers/clk/sirf/clk-common.c index e9cf5730effe..9fc285d784d3 100644 --- a/drivers/clk/sirf/clk-common.c +++ b/drivers/clk/sirf/clk-common.c @@ -10,8 +10,8 @@ #define KHZ 1000 #define MHZ (KHZ * KHZ) -static void *sirfsoc_clk_vbase; -static void *sirfsoc_rsc_vbase; +static void __iomem *sirfsoc_clk_vbase; +static void __iomem *sirfsoc_rsc_vbase; static struct clk_onecell_data clk_data; /* -- cgit v1.2.3-58-ga151 From b011d386e9efdef6f5776435aad97a5bf302c041 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 12:39:52 -0700 Subject: clk: socfpga: Silence sparse warning drivers/clk/socfpga/clk-gate.c:227:40: warning: Using plain integer as NULL pointer Cc: Dinh Nguyen Signed-off-by: Stephen Boyd --- drivers/clk/socfpga/clk-gate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c index dd3a78c64795..0d5dc84372dc 100644 --- a/drivers/clk/socfpga/clk-gate.c +++ b/drivers/clk/socfpga/clk-gate.c @@ -224,7 +224,7 @@ static void __init __socfpga_gate_init(struct device_node *node, socfpga_clk->shift = div_reg[1]; socfpga_clk->width = div_reg[2]; } else { - socfpga_clk->div_reg = 0; + socfpga_clk->div_reg = NULL; } rc = of_property_read_u32_array(node, "clk-phase", clk_phase, 2); -- cgit v1.2.3-58-ga151 From 8e6dd77ccc324a57d5f37c7f8b1e37319fb2e2b3 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 12:45:53 -0700 Subject: clk: st: Silence sparse warnings drivers/clk/st/clkgen-mux.c:134:4: warning: symbol 'clkgena_divmux_get_parent' was not declared. Should it be static? drivers/clk/st/clkgen-mux.c:171:15: warning: symbol 'clkgena_divmux_recalc_rate' was not declared. Should it be static? drivers/clk/st/clkgen-mux.c:218:12: warning: symbol 'clk_register_genamux' was not declared. Should it be static? drivers/clk/st/clkgen-mux.c:388:13: warning: symbol 'st_of_clkgena_divmux_setup' was not declared. Should it be static? drivers/clk/st/clkgen-mux.c:488:13: warning: symbol 'st_of_clkgena_prediv_setup' was not declared. Should it be static? drivers/clk/st/clkgen-mux.c:625:13: warning: symbol 'st_of_clkgen_mux_setup' was not declared. Should it be static? drivers/clk/st/clkgen-mux.c:702:13: warning: symbol 'st_of_clkgen_vcc_setup' was not declared. Should it be static? drivers/clk/st/clkgen-pll.c:273:15: warning: symbol 'recalc_stm_pll800c65' was not declared. Should it be static? drivers/clk/st/clkgen-pll.c:300:15: warning: symbol 'recalc_stm_pll1600c65' was not declared. Should it be static? drivers/clk/st/clkgen-pll.c:324:15: warning: symbol 'recalc_stm_pll3200c32' was not declared. Should it be static? drivers/clk/st/clkgen-pll.c:346:15: warning: symbol 'recalc_stm_pll1200c32' was not declared. Should it be static? drivers/clk/st/clkgen-pll.c:565:19: warning: incorrect type in assignment (different address spaces) drivers/clk/st/clkgen-pll.c:565:19: expected void [noderef] *reg drivers/clk/st/clkgen-pll.c:565:19: got void * drivers/clk/st/clkgen-pll.c:576:18: warning: incorrect type in assignment (different address spaces) drivers/clk/st/clkgen-pll.c:576:18: expected void [noderef] *reg drivers/clk/st/clkgen-pll.c:576:18: got void * drivers/clk/st/clkgen-pll.c:693:53: warning: incorrect type in argument 2 (different address spaces) drivers/clk/st/clkgen-pll.c:693:53: expected void *[noderef] reg drivers/clk/st/clkgen-pll.c:693:53: got void [noderef] *[assigned] pll_base drivers/clk/st/clkgen-fsyn.c:495:5: warning: symbol 'clk_fs660c32_vco_get_rate' was not declared. Should it be static? drivers/clk/st/clkgen-fsyn.c:522:5: warning: symbol 'clk_fs660c32_vco_get_params' was not declared. Should it be static? drivers/clk/st/clk-flexgen.c:119:15: warning: symbol 'flexgen_recalc_rate' was not declared. Should it be static? drivers/clk/st/clk-flexgen.c:177:12: warning: symbol 'clk_register_flexgen' was not declared. Should it be static? drivers/clk/st/clk-flexgen.c:263:13: warning: symbol 'st_of_flexgen_setup' was not declared. Should it be static? Signed-off-by: Stephen Boyd --- drivers/clk/st/clk-flexgen.c | 6 +++--- drivers/clk/st/clkgen-fsyn.c | 4 ++-- drivers/clk/st/clkgen-mux.c | 14 +++++++------- drivers/clk/st/clkgen-pll.c | 10 +++++----- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/clk/st/clk-flexgen.c b/drivers/clk/st/clk-flexgen.c index bf12a25eb3a2..409ed4b0b569 100644 --- a/drivers/clk/st/clk-flexgen.c +++ b/drivers/clk/st/clk-flexgen.c @@ -116,7 +116,7 @@ static long flexgen_round_rate(struct clk_hw *hw, unsigned long rate, return *prate / div; } -unsigned long flexgen_recalc_rate(struct clk_hw *hw, +static unsigned long flexgen_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct flexgen *flexgen = to_flexgen(hw); @@ -174,7 +174,7 @@ static const struct clk_ops flexgen_ops = { .set_rate = flexgen_set_rate, }; -struct clk *clk_register_flexgen(const char *name, +static struct clk *clk_register_flexgen(const char *name, const char **parent_names, u8 num_parents, void __iomem *reg, spinlock_t *lock, u32 idx, unsigned long flexgen_flags) { @@ -260,7 +260,7 @@ static const char ** __init flexgen_get_parents(struct device_node *np, return parents; } -void __init st_of_flexgen_setup(struct device_node *np) +static void __init st_of_flexgen_setup(struct device_node *np) { struct device_node *pnode; void __iomem *reg; diff --git a/drivers/clk/st/clkgen-fsyn.c b/drivers/clk/st/clkgen-fsyn.c index a917c4c7eaa9..e94197f04b0b 100644 --- a/drivers/clk/st/clkgen-fsyn.c +++ b/drivers/clk/st/clkgen-fsyn.c @@ -492,7 +492,7 @@ static int quadfs_pll_is_enabled(struct clk_hw *hw) return !!npda; } -int clk_fs660c32_vco_get_rate(unsigned long input, struct stm_fs *fs, +static int clk_fs660c32_vco_get_rate(unsigned long input, struct stm_fs *fs, unsigned long *rate) { unsigned long nd = fs->ndiv + 16; /* ndiv value */ @@ -519,7 +519,7 @@ static unsigned long quadfs_pll_fs660c32_recalc_rate(struct clk_hw *hw, return rate; } -int clk_fs660c32_vco_get_params(unsigned long input, +static int clk_fs660c32_vco_get_params(unsigned long input, unsigned long output, struct stm_fs *fs) { /* Formula diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c index fdcff10f6d30..80aa77dbf660 100644 --- a/drivers/clk/st/clkgen-mux.c +++ b/drivers/clk/st/clkgen-mux.c @@ -131,7 +131,7 @@ static int clkgena_divmux_is_enabled(struct clk_hw *hw) return (s8)clk_mux_ops.get_parent(mux_hw) > 0; } -u8 clkgena_divmux_get_parent(struct clk_hw *hw) +static u8 clkgena_divmux_get_parent(struct clk_hw *hw) { struct clkgena_divmux *genamux = to_clkgena_divmux(hw); struct clk_hw *mux_hw = &genamux->mux.hw; @@ -168,7 +168,7 @@ static int clkgena_divmux_set_parent(struct clk_hw *hw, u8 index) return 0; } -unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw, +static unsigned long clkgena_divmux_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { struct clkgena_divmux *genamux = to_clkgena_divmux(hw); @@ -215,7 +215,7 @@ static const struct clk_ops clkgena_divmux_ops = { /** * clk_register_genamux - register a genamux clock with the clock framework */ -struct clk *clk_register_genamux(const char *name, +static struct clk *clk_register_genamux(const char *name, const char **parent_names, u8 num_parents, void __iomem *reg, const struct clkgena_divmux_data *muxdata, @@ -385,7 +385,7 @@ static void __iomem * __init clkgen_get_register_base( return reg; } -void __init st_of_clkgena_divmux_setup(struct device_node *np) +static void __init st_of_clkgena_divmux_setup(struct device_node *np) { const struct of_device_id *match; const struct clkgena_divmux_data *data; @@ -485,7 +485,7 @@ static const struct of_device_id clkgena_prediv_of_match[] = { {} }; -void __init st_of_clkgena_prediv_setup(struct device_node *np) +static void __init st_of_clkgena_prediv_setup(struct device_node *np) { const struct of_device_id *match; void __iomem *reg; @@ -622,7 +622,7 @@ static const struct of_device_id mux_of_match[] = { {} }; -void __init st_of_clkgen_mux_setup(struct device_node *np) +static void __init st_of_clkgen_mux_setup(struct device_node *np) { const struct of_device_id *match; struct clk *clk; @@ -699,7 +699,7 @@ static const struct of_device_id vcc_of_match[] = { {} }; -void __init st_of_clkgen_vcc_setup(struct device_node *np) +static void __init st_of_clkgen_vcc_setup(struct device_node *np) { const struct of_device_id *match; void __iomem *reg; diff --git a/drivers/clk/st/clkgen-pll.c b/drivers/clk/st/clkgen-pll.c index d204ba85db3a..106532207213 100644 --- a/drivers/clk/st/clkgen-pll.c +++ b/drivers/clk/st/clkgen-pll.c @@ -270,7 +270,7 @@ static int clkgen_pll_is_enabled(struct clk_hw *hw) return !poweroff; } -unsigned long recalc_stm_pll800c65(struct clk_hw *hw, +static unsigned long recalc_stm_pll800c65(struct clk_hw *hw, unsigned long parent_rate) { struct clkgen_pll *pll = to_clkgen_pll(hw); @@ -297,7 +297,7 @@ unsigned long recalc_stm_pll800c65(struct clk_hw *hw, } -unsigned long recalc_stm_pll1600c65(struct clk_hw *hw, +static unsigned long recalc_stm_pll1600c65(struct clk_hw *hw, unsigned long parent_rate) { struct clkgen_pll *pll = to_clkgen_pll(hw); @@ -321,7 +321,7 @@ unsigned long recalc_stm_pll1600c65(struct clk_hw *hw, return rate; } -unsigned long recalc_stm_pll3200c32(struct clk_hw *hw, +static unsigned long recalc_stm_pll3200c32(struct clk_hw *hw, unsigned long parent_rate) { struct clkgen_pll *pll = to_clkgen_pll(hw); @@ -343,7 +343,7 @@ unsigned long recalc_stm_pll3200c32(struct clk_hw *hw, return rate; } -unsigned long recalc_stm_pll1200c32(struct clk_hw *hw, +static unsigned long recalc_stm_pll1200c32(struct clk_hw *hw, unsigned long parent_rate) { struct clkgen_pll *pll = to_clkgen_pll(hw); @@ -544,7 +544,7 @@ CLK_OF_DECLARE(clkgena_c65_plls, "st,clkgena-plls-c65", clkgena_c65_pll_setup); static struct clk * __init clkgen_odf_register(const char *parent_name, - void * __iomem reg, + void __iomem *reg, struct clkgen_pll_data *pll_data, int odf, spinlock_t *odf_lock, -- cgit v1.2.3-58-ga151 From 412d6b47cc6e33a04fe03be1db725202417f4d72 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 12:59:32 -0700 Subject: clk: ti: Silence sparse warnings drivers/clk/ti/clk.c:125:31: warning: incorrect type in return expression (different address spaces) drivers/clk/ti/clk.c:125:31: expected void [noderef] * drivers/clk/ti/clk.c:125:31: got void * drivers/clk/ti/clk.c:132:31: warning: incorrect type in return expression (different address spaces) drivers/clk/ti/clk.c:132:31: expected void [noderef] * drivers/clk/ti/clk.c:132:31: got void * drivers/clk/ti/dpll.c:180:14: warning: symbol '_get_reg' was not declared. Should it be static? drivers/clk/ti/fapll.c:624:32: warning: Using plain integer as NULL pointer drivers/clk/ti/fapll.c:625:31: warning: Using plain integer as NULL pointer drivers/clk/ti/fapll.c:630:40: warning: Using plain integer as NULL pointer drivers/clk/ti/clk-dra7-atl.c:158:22: warning: symbol 'atl_clk_ops' was not declared. Should it be static? drivers/clk/ti/clk-dra7-atl.c:170:39: warning: Using plain integer as NULL pointer Acked-by: Peter Ujfalusi Acked-by: Tero Kristo Signed-off-by: Stephen Boyd --- drivers/clk/ti/clk-dra7-atl.c | 4 ++-- drivers/clk/ti/clk.c | 4 ++-- drivers/clk/ti/dpll.c | 2 +- drivers/clk/ti/fapll.c | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c index 0a1df821860f..19e543a32e2b 100644 --- a/drivers/clk/ti/clk-dra7-atl.c +++ b/drivers/clk/ti/clk-dra7-atl.c @@ -155,7 +155,7 @@ static int atl_clk_set_rate(struct clk_hw *hw, unsigned long rate, return 0; } -const struct clk_ops atl_clk_ops = { +static const struct clk_ops atl_clk_ops = { .enable = atl_clk_enable, .disable = atl_clk_disable, .is_enabled = atl_clk_is_enabled, @@ -167,7 +167,7 @@ const struct clk_ops atl_clk_ops = { static void __init of_dra7_atl_clock_setup(struct device_node *node) { struct dra7_atl_desc *clk_hw = NULL; - struct clk_init_data init = { 0 }; + struct clk_init_data init = { NULL }; const char **parent_names = NULL; struct clk *clk; diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index 0ebe5c51062b..64bb5e8a3b8c 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -122,14 +122,14 @@ void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index) if (i == CLK_MAX_MEMMAPS) { pr_err("clk-provider not found for %s!\n", node->name); - return ERR_PTR(-ENOENT); + return IOMEM_ERR_PTR(-ENOENT); } reg->index = i; if (of_property_read_u32_index(node, "reg", index, &val)) { pr_err("%s must have reg[%d]!\n", node->name, index); - return ERR_PTR(-EINVAL); + return IOMEM_ERR_PTR(-EINVAL); } reg->offset = val; diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c index 11478a501c30..2aacf7a3bcae 100644 --- a/drivers/clk/ti/dpll.c +++ b/drivers/clk/ti/dpll.c @@ -177,7 +177,7 @@ cleanup: } #if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_ATAGS) -void __iomem *_get_reg(u8 module, u16 offset) +static void __iomem *_get_reg(u8 module, u16 offset) { u32 reg; struct clk_omap_reg *reg_setup; diff --git a/drivers/clk/ti/fapll.c b/drivers/clk/ti/fapll.c index ffcd8e09e85b..730aa62454a2 100644 --- a/drivers/clk/ti/fapll.c +++ b/drivers/clk/ti/fapll.c @@ -621,13 +621,13 @@ static void __init ti_fapll_setup(struct device_node *node) /* Check for hardwired audio_pll_clk1 */ if (is_audio_pll_clk1(freq)) { - freq = 0; - div = 0; + freq = NULL; + div = NULL; } else { /* Does the synthesizer have a FREQ register? */ v = readl_relaxed(freq); if (!v) - freq = 0; + freq = NULL; } synth_clk = ti_fapll_synth_setup(fd, freq, div, output_instance, output_name, node->name, -- cgit v1.2.3-58-ga151 From 11bee5e19afd11a42a03df9025f82a1bab4d5595 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 13:02:26 -0700 Subject: clk: versatile: Silence sparse warnings drivers/clk/versatile/clk-sp810.c:159:29: error: incompatible types for operation (<=) drivers/clk/versatile/clk-sp810.c:159:29: left side has type char const * drivers/clk/versatile/clk-sp810.c:159:29: right side has type int drivers/clk/versatile/clk-sp810.c:159:53: error: incompatible types for operation (<=) drivers/clk/versatile/clk-sp810.c:159:53: left side has type char const * drivers/clk/versatile/clk-sp810.c:159:53: right side has type int drivers/clk/versatile/clk-sp810.c:138:13: warning: symbol 'clk_sp810_of_setup' was not declared. Should it be static? Acked: Pawel Moll Signed-off-by: Stephen Boyd --- drivers/clk/versatile/clk-sp810.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/versatile/clk-sp810.c b/drivers/clk/versatile/clk-sp810.c index c6e86a9a2aa3..a96dd8e53fdb 100644 --- a/drivers/clk/versatile/clk-sp810.c +++ b/drivers/clk/versatile/clk-sp810.c @@ -135,7 +135,7 @@ static struct clk *clk_sp810_timerclken_of_get(struct of_phandle_args *clkspec, return sp810->timerclken[clkspec->args[0]].clk; } -void __init clk_sp810_of_setup(struct device_node *node) +static void __init clk_sp810_of_setup(struct device_node *node) { struct clk_sp810 *sp810 = kzalloc(sizeof(*sp810), GFP_KERNEL); const char *parent_names[2]; @@ -156,7 +156,7 @@ void __init clk_sp810_of_setup(struct device_node *node) "timclk"); parent_names[1] = of_clk_get_parent_name(node, sp810->timclk_index); - if (parent_names[0] <= 0 || parent_names[1] <= 0) { + if (!parent_names[0] || !parent_names[1]) { pr_warn("Failed to obtain parent clocks for SP810!\n"); return; } -- cgit v1.2.3-58-ga151 From e45310bfcd21d797f9d36bd4883e600bab645723 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 13:11:31 -0700 Subject: clk: socfpga: Silence sparse warning drivers/clk/socfpga/clk-periph.c:79:39: warning: Using plain integer as NULL pointer Cc: Dinh Nguyen Signed-off-by: Stephen Boyd --- drivers/clk/socfpga/clk-periph.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/socfpga/clk-periph.c b/drivers/clk/socfpga/clk-periph.c index 46531c34ec9b..83aeaa219d14 100644 --- a/drivers/clk/socfpga/clk-periph.c +++ b/drivers/clk/socfpga/clk-periph.c @@ -76,7 +76,7 @@ static __init void __socfpga_periph_init(struct device_node *node, periph_clk->shift = div_reg[1]; periph_clk->width = div_reg[2]; } else { - periph_clk->div_reg = 0; + periph_clk->div_reg = NULL; } rc = of_property_read_u32(node, "fixed-divider", &fixed_div); -- cgit v1.2.3-58-ga151 From 52127755fe6bc5a7a6d28540938ccdfa2a7b5f90 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 14:03:08 -0700 Subject: clk: mmp: Silence sparse warnings drivers/clk/mmp/clk-apbc.c:118:16: warning: symbol 'clk_apbc_ops' was not declared. Should it be static? drivers/clk/mmp/clk-apmu.c:64:16: warning: symbol 'clk_apmu_ops' was not declared. Should it be static? Cc: Chao Xie Signed-off-by: Stephen Boyd --- drivers/clk/mmp/clk-apbc.c | 2 +- drivers/clk/mmp/clk-apmu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/mmp/clk-apbc.c b/drivers/clk/mmp/clk-apbc.c index d14120eaa71f..09d41c717c52 100644 --- a/drivers/clk/mmp/clk-apbc.c +++ b/drivers/clk/mmp/clk-apbc.c @@ -115,7 +115,7 @@ static void clk_apbc_unprepare(struct clk_hw *hw) spin_unlock_irqrestore(apbc->lock, flags); } -struct clk_ops clk_apbc_ops = { +static struct clk_ops clk_apbc_ops = { .prepare = clk_apbc_prepare, .unprepare = clk_apbc_unprepare, }; diff --git a/drivers/clk/mmp/clk-apmu.c b/drivers/clk/mmp/clk-apmu.c index abe182b2377f..cdcf2d7f321e 100644 --- a/drivers/clk/mmp/clk-apmu.c +++ b/drivers/clk/mmp/clk-apmu.c @@ -61,7 +61,7 @@ static void clk_apmu_disable(struct clk_hw *hw) spin_unlock_irqrestore(apmu->lock, flags); } -struct clk_ops clk_apmu_ops = { +static struct clk_ops clk_apmu_ops = { .enable = clk_apmu_enable, .disable = clk_apmu_disable, }; -- cgit v1.2.3-58-ga151 From 6ae5fd381251993a650a7fad51bd9318c19a6c80 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 14:14:57 -0700 Subject: clk: xgene: Silence sparse warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/clk/clk-xgene.c:77:43: warning: incorrect type in argument 1 (different address spaces) drivers/clk/clk-xgene.c:77:43: expected void *csr drivers/clk/clk-xgene.c:77:43: got void [noderef] * ... drivers/clk/clk-xgene.c: In function ‘xgene_clk_enable’: drivers/clk/clk-xgene.c:237:3: warning: format ‘%LX’ expects argument of type ‘long long unsigned int’, but argument 4 has type ‘phys_addr_t’ [-Wformat] drivers/clk/clk-xgene.c:248:3: warning: format ‘%LX’ expects argument of type ‘long long unsigned int’, but argument 4 has type ‘phys_addr_t’ [-Wformat] Signed-off-by: Stephen Boyd --- drivers/clk/clk-xgene.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index dd8a62d8f11f..f26b3ac36b27 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -42,12 +42,12 @@ static DEFINE_SPINLOCK(clk_lock); -static inline u32 xgene_clk_read(void *csr) +static inline u32 xgene_clk_read(void __iomem *csr) { return readl_relaxed(csr); } -static inline void xgene_clk_write(u32 data, void *csr) +static inline void xgene_clk_write(u32 data, void __iomem *csr) { return writel_relaxed(data, csr); } @@ -119,7 +119,7 @@ static unsigned long xgene_clk_pll_recalc_rate(struct clk_hw *hw, return fvco / nout; } -const struct clk_ops xgene_clk_pll_ops = { +static const struct clk_ops xgene_clk_pll_ops = { .is_enabled = xgene_clk_pll_is_enabled, .recalc_rate = xgene_clk_pll_recalc_rate, }; @@ -167,7 +167,7 @@ static void xgene_pllclk_init(struct device_node *np, enum xgene_pll_type pll_ty { const char *clk_name = np->full_name; struct clk *clk; - void *reg; + void __iomem *reg; reg = of_iomap(np, 0); if (reg == NULL) { @@ -222,20 +222,22 @@ static int xgene_clk_enable(struct clk_hw *hw) struct xgene_clk *pclk = to_xgene_clk(hw); unsigned long flags = 0; u32 data; + phys_addr_t reg; if (pclk->lock) spin_lock_irqsave(pclk->lock, flags); if (pclk->param.csr_reg != NULL) { pr_debug("%s clock enabled\n", pclk->name); + reg = __pa(pclk->param.csr_reg); /* First enable the clock */ data = xgene_clk_read(pclk->param.csr_reg + pclk->param.reg_clk_offset); data |= pclk->param.reg_clk_mask; xgene_clk_write(data, pclk->param.csr_reg + pclk->param.reg_clk_offset); - pr_debug("%s clock PADDR base 0x%016LX clk offset 0x%08X mask 0x%08X value 0x%08X\n", - pclk->name, __pa(pclk->param.csr_reg), + pr_debug("%s clock PADDR base %pa clk offset 0x%08X mask 0x%08X value 0x%08X\n", + pclk->name, ®, pclk->param.reg_clk_offset, pclk->param.reg_clk_mask, data); @@ -245,8 +247,8 @@ static int xgene_clk_enable(struct clk_hw *hw) data &= ~pclk->param.reg_csr_mask; xgene_clk_write(data, pclk->param.csr_reg + pclk->param.reg_csr_offset); - pr_debug("%s CSR RESET PADDR base 0x%016LX csr offset 0x%08X mask 0x%08X value 0x%08X\n", - pclk->name, __pa(pclk->param.csr_reg), + pr_debug("%s CSR RESET PADDR base %pa csr offset 0x%08X mask 0x%08X value 0x%08X\n", + pclk->name, ®, pclk->param.reg_csr_offset, pclk->param.reg_csr_mask, data); } @@ -386,7 +388,7 @@ static long xgene_clk_round_rate(struct clk_hw *hw, unsigned long rate, return parent_rate / divider; } -const struct clk_ops xgene_clk_ops = { +static const struct clk_ops xgene_clk_ops = { .enable = xgene_clk_enable, .disable = xgene_clk_disable, .is_enabled = xgene_clk_is_enabled, @@ -456,7 +458,7 @@ static void __init xgene_devclk_init(struct device_node *np) parameters.csr_reg = NULL; parameters.divider_reg = NULL; for (i = 0; i < 2; i++) { - void *map_res; + void __iomem *map_res; rc = of_address_to_resource(np, i, &res); if (rc != 0) { if (i == 0) { -- cgit v1.2.3-58-ga151 From 61ad23a14ac11002addd2b7f9235e15c033f6e3b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 16:09:33 -0700 Subject: clk: moxart: Silence sparse warnings drivers/clk/clk-moxart.c:18:13: warning: symbol 'moxart_of_pll_clk_init' was not declared. Should it be static? drivers/clk/clk-moxart.c:56:13: warning: symbol 'moxart_of_apb_clk_init' was not declared. Should it be static? Cc: Jonas Jensen Signed-off-by: Stephen Boyd --- drivers/clk/clk-moxart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c index 30a3b6999e10..5181b89c3cb2 100644 --- a/drivers/clk/clk-moxart.c +++ b/drivers/clk/clk-moxart.c @@ -15,7 +15,7 @@ #include #include -void __init moxart_of_pll_clk_init(struct device_node *node) +static void __init moxart_of_pll_clk_init(struct device_node *node) { static void __iomem *base; struct clk *clk, *ref_clk; @@ -53,7 +53,7 @@ void __init moxart_of_pll_clk_init(struct device_node *node) CLK_OF_DECLARE(moxart_pll_clock, "moxa,moxart-pll-clock", moxart_of_pll_clk_init); -void __init moxart_of_apb_clk_init(struct device_node *node) +static void __init moxart_of_apb_clk_init(struct device_node *node) { static void __iomem *base; struct clk *clk, *pll_clk; -- cgit v1.2.3-58-ga151 From 6c4178163a872cbeef70fd3354222ee4b238e7a2 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Fri, 1 May 2015 16:10:28 -0700 Subject: clk: u300: Silence sparse warnings drivers/clk/clk-u300.c:1175:13: warning: symbol 'u300_clk_init' was not declared. Should it be static? Acked-by: Linus Walleij Signed-off-by: Stephen Boyd --- drivers/clk/clk-u300.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/clk-u300.c b/drivers/clk/clk-u300.c index 406bfc1375b2..18bf5e576b93 100644 --- a/drivers/clk/clk-u300.c +++ b/drivers/clk/clk-u300.c @@ -12,6 +12,7 @@ #include #include #include +#include /* APP side SYSCON registers */ /* CLK Control Register 16bit (R/W) */ -- cgit v1.2.3-58-ga151 From 64a12c56addcb2d15ff752874abbe553a5f2a45b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 14 May 2015 17:38:21 -0700 Subject: clk: Kconfig: Move bcm Kconfig into clk menu Having this Kconfig sourced outside the clk menu means the option is under the "Device Drivers" menu instead of the "Common Clock Framework" menu. Move it so that the bcm clock config options are in the right place. Cc: Alex Elder Signed-off-by: Stephen Boyd --- drivers/clk/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 9897f353bf1a..67e3a84d2805 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -150,11 +150,11 @@ config COMMON_CLK_CDCE706 ---help--- This driver supports TI CDCE706 programmable 3-PLL clock synthesizer. +source "drivers/clk/bcm/Kconfig" source "drivers/clk/qcom/Kconfig" endmenu -source "drivers/clk/bcm/Kconfig" source "drivers/clk/mvebu/Kconfig" source "drivers/clk/samsung/Kconfig" -- cgit v1.2.3-58-ga151 From 2885c3b2a3da7902314fa1d0a5b603eeea7c7302 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 27 Apr 2015 18:29:52 +0900 Subject: clk: Show correct information when fail to set clock rate This patch shows the correct information for debugging when fail to set clock rate because original error message shows the error value instead of current clock rate. Cc: Mike Turquette Signed-off-by: Chanwoo Choi Acked-by: Sylwester Nawrocki Signed-off-by: Stephen Boyd --- drivers/clk/clk-conf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c index 48a65b2b4027..35a396bc9672 100644 --- a/drivers/clk/clk-conf.c +++ b/drivers/clk/clk-conf.c @@ -106,8 +106,9 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier) rc = clk_set_rate(clk, rate); if (rc < 0) - pr_err("clk: couldn't set %s clock rate: %d\n", - __clk_get_name(clk), rc); + pr_err("clk: couldn't set %s clk rate to %d (%d), current rate: %ld\n", + __clk_get_name(clk), rate, rc, + clk_get_rate(clk)); clk_put(clk); } index++; -- cgit v1.2.3-58-ga151 From 8a3d9c164152e461e5f80db65aaaa0fbfdb2eed3 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 11 May 2015 17:25:19 -0500 Subject: dt-bindings: Add pxa1928 clock binding This adds the clock binding documentation for the Marvell PXA1928 SOC. The PXA1928 has 3 clock control blocks for different subsystems of the chip. Signed-off-by: Rob Herring Cc: Pawel Moll Cc: Mark Rutland Cc: Ian Campbell Cc: Kumar Gala Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/marvell,pxa1928.txt | 21 ++++++++ include/dt-bindings/clock/marvell,pxa1928.h | 57 ++++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/marvell,pxa1928.txt create mode 100644 include/dt-bindings/clock/marvell,pxa1928.h diff --git a/Documentation/devicetree/bindings/clock/marvell,pxa1928.txt b/Documentation/devicetree/bindings/clock/marvell,pxa1928.txt new file mode 100644 index 000000000000..809c5a2d8d9d --- /dev/null +++ b/Documentation/devicetree/bindings/clock/marvell,pxa1928.txt @@ -0,0 +1,21 @@ +* Marvell PXA1928 Clock Controllers + +The PXA1928 clock subsystem generates and supplies clock to various +controllers within the PXA1928 SoC. The PXA1928 contains 3 clock controller +blocks called APMU, MPMU, and APBC roughly corresponding to internal buses. + +Required Properties: + +- compatible: should be one of the following. + - "marvell,pxa1928-apmu" - APMU controller compatible + - "marvell,pxa1928-mpmu" - MPMU controller compatible + - "marvell,pxa1928-apbc" - APBC controller compatible +- reg: physical base address of the clock controller and length of memory mapped + region. +- #clock-cells: should be 1. +- #reset-cells: should be 1. + +Each clock is assigned an identifier and client nodes use the clock controller +phandle and this identifier to specify the clock which they consume. + +All these identifiers can be found in . diff --git a/include/dt-bindings/clock/marvell,pxa1928.h b/include/dt-bindings/clock/marvell,pxa1928.h new file mode 100644 index 000000000000..d4f2e18919ff --- /dev/null +++ b/include/dt-bindings/clock/marvell,pxa1928.h @@ -0,0 +1,57 @@ +#ifndef __DTS_MARVELL_PXA1928_CLOCK_H +#define __DTS_MARVELL_PXA1928_CLOCK_H + +/* + * Clock ID values here correspond to the control register offset/4. + */ + +/* apb peripherals */ +#define PXA1928_CLK_RTC 0x00 +#define PXA1928_CLK_TWSI0 0x01 +#define PXA1928_CLK_TWSI1 0x02 +#define PXA1928_CLK_TWSI2 0x03 +#define PXA1928_CLK_TWSI3 0x04 +#define PXA1928_CLK_OWIRE 0x05 +#define PXA1928_CLK_KPC 0x06 +#define PXA1928_CLK_TB_ROTARY 0x07 +#define PXA1928_CLK_SW_JTAG 0x08 +#define PXA1928_CLK_TIMER1 0x09 +#define PXA1928_CLK_UART0 0x0b +#define PXA1928_CLK_UART1 0x0c +#define PXA1928_CLK_UART2 0x0d +#define PXA1928_CLK_GPIO 0x0e +#define PXA1928_CLK_PWM0 0x0f +#define PXA1928_CLK_PWM1 0x10 +#define PXA1928_CLK_PWM2 0x11 +#define PXA1928_CLK_PWM3 0x12 +#define PXA1928_CLK_SSP0 0x13 +#define PXA1928_CLK_SSP1 0x14 +#define PXA1928_CLK_SSP2 0x15 + +#define PXA1928_CLK_TWSI4 0x1f +#define PXA1928_CLK_TWSI5 0x20 +#define PXA1928_CLK_UART3 0x22 +#define PXA1928_CLK_THSENS_GLOB 0x24 +#define PXA1928_CLK_THSENS_CPU 0x26 +#define PXA1928_CLK_THSENS_VPU 0x27 +#define PXA1928_CLK_THSENS_GC 0x28 +#define PXA1928_APBC_NR_CLKS 0x30 + + +/* axi peripherals */ +#define PXA1928_CLK_SDH0 0x15 +#define PXA1928_CLK_SDH1 0x16 +#define PXA1928_CLK_USB 0x17 +#define PXA1928_CLK_NAND 0x18 +#define PXA1928_CLK_DMA 0x19 + +#define PXA1928_CLK_SDH2 0x3a +#define PXA1928_CLK_SDH3 0x3b +#define PXA1928_CLK_HSIC 0x3e +#define PXA1928_CLK_SDH4 0x57 +#define PXA1928_CLK_GC3D 0x5d +#define PXA1928_CLK_GC2D 0x5f + +#define PXA1928_APMU_NR_CLKS 0x60 + +#endif -- cgit v1.2.3-58-ga151 From df5338d9fe5d607cdbc68d8432cbfdc9a942a679 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 11 May 2015 17:25:20 -0500 Subject: clk: mmp: add PXA1928 clock support Add initial clock support for Marvell PXA1928. The PXA1928 is a mobile SOC and is similar to other MMP/PXA series of SOCs, so a lot of the existing infrastructure is reused here. Currently the PLLs are just fixed clocks, and not all leaf clocks are implemented. Signed-off-by: Rob Herring Cc: Mike Turquette Signed-off-by: Stephen Boyd --- drivers/clk/mmp/Makefile | 2 + drivers/clk/mmp/clk-of-pxa1928.c | 265 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 267 insertions(+) create mode 100644 drivers/clk/mmp/clk-of-pxa1928.c diff --git a/drivers/clk/mmp/Makefile b/drivers/clk/mmp/Makefile index 3caaf7cc169c..9d4bc41e4239 100644 --- a/drivers/clk/mmp/Makefile +++ b/drivers/clk/mmp/Makefile @@ -12,3 +12,5 @@ obj-$(CONFIG_MACH_MMP2_DT) += clk-of-mmp2.o obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o obj-$(CONFIG_CPU_MMP2) += clk-mmp2.o + +obj-y += clk-of-pxa1928.o diff --git a/drivers/clk/mmp/clk-of-pxa1928.c b/drivers/clk/mmp/clk-of-pxa1928.c new file mode 100644 index 000000000000..433a5ae1eae0 --- /dev/null +++ b/drivers/clk/mmp/clk-of-pxa1928.c @@ -0,0 +1,265 @@ +/* + * pxa1928 clock framework source file + * + * Copyright (C) 2015 Linaro, Ltd. + * Rob Herring + * + * Based on drivers/clk/mmp/clk-of-mmp2.c: + * Copyright (C) 2012 Marvell + * Chao Xie + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ +#include +#include +#include +#include +#include + +#include + +#include "clk.h" +#include "reset.h" + +#define MPMU_UART_PLL 0x14 + +struct pxa1928_clk_unit { + struct mmp_clk_unit unit; + void __iomem *mpmu_base; + void __iomem *apmu_base; + void __iomem *apbc_base; + void __iomem *apbcp_base; +}; + +static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { + {0, "clk32", NULL, CLK_IS_ROOT, 32768}, + {0, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, + {0, "pll1_624", NULL, CLK_IS_ROOT, 624000000}, + {0, "pll5p", NULL, CLK_IS_ROOT, 832000000}, + {0, "pll5", NULL, CLK_IS_ROOT, 1248000000}, + {0, "usb_pll", NULL, CLK_IS_ROOT, 480000000}, +}; + +static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { + {0, "pll1_d2", "pll1_624", 1, 2, 0}, + {0, "pll1_d9", "pll1_624", 1, 9, 0}, + {0, "pll1_d12", "pll1_624", 1, 12, 0}, + {0, "pll1_d16", "pll1_624", 1, 16, 0}, + {0, "pll1_d20", "pll1_624", 1, 20, 0}, + {0, "pll1_416", "pll1_624", 2, 3, 0}, + {0, "vctcxo_d2", "vctcxo", 1, 2, 0}, + {0, "vctcxo_d4", "vctcxo", 1, 4, 0}, +}; + +static struct mmp_clk_factor_masks uart_factor_masks = { + .factor = 2, + .num_mask = 0x1fff, + .den_mask = 0x1fff, + .num_shift = 16, + .den_shift = 0, +}; + +static struct mmp_clk_factor_tbl uart_factor_tbl[] = { + {.num = 832, .den = 234}, /*58.5MHZ */ + {.num = 1, .den = 1}, /*26MHZ */ +}; + +static void pxa1928_pll_init(struct pxa1928_clk_unit *pxa_unit) +{ + struct clk *clk; + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_fixed_rate_clks(unit, fixed_rate_clks, + ARRAY_SIZE(fixed_rate_clks)); + + mmp_register_fixed_factor_clks(unit, fixed_factor_clks, + ARRAY_SIZE(fixed_factor_clks)); + + clk = mmp_clk_register_factor("uart_pll", "pll1_416", + CLK_SET_RATE_PARENT, + pxa_unit->mpmu_base + MPMU_UART_PLL, + &uart_factor_masks, uart_factor_tbl, + ARRAY_SIZE(uart_factor_tbl), NULL); +} + +static DEFINE_SPINLOCK(uart0_lock); +static DEFINE_SPINLOCK(uart1_lock); +static DEFINE_SPINLOCK(uart2_lock); +static DEFINE_SPINLOCK(uart3_lock); +static const char *uart_parent_names[] = {"uart_pll", "vctcxo"}; + +static DEFINE_SPINLOCK(ssp0_lock); +static DEFINE_SPINLOCK(ssp1_lock); +static const char *ssp_parent_names[] = {"vctcxo_d4", "vctcxo_d2", "vctcxo", "pll1_d12"}; + +static DEFINE_SPINLOCK(reset_lock); + +static struct mmp_param_mux_clk apbc_mux_clks[] = { + {0, "uart0_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART0 * 4, 4, 3, 0, &uart0_lock}, + {0, "uart1_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART1 * 4, 4, 3, 0, &uart1_lock}, + {0, "uart2_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART2 * 4, 4, 3, 0, &uart2_lock}, + {0, "uart3_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_UART3 * 4, 4, 3, 0, &uart3_lock}, + {0, "ssp0_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_SSP0 * 4, 4, 3, 0, &ssp0_lock}, + {0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_SSP1 * 4, 4, 3, 0, &ssp1_lock}, +}; + +static struct mmp_param_gate_clk apbc_gate_clks[] = { + {PXA1928_CLK_TWSI0, "twsi0_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI0 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_TWSI1, "twsi1_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI1 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_TWSI2, "twsi2_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI2 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_TWSI3, "twsi3_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI3 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_TWSI4, "twsi4_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI4 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_TWSI5, "twsi5_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_TWSI5 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_GPIO, "gpio_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_GPIO * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_KPC, "kpc_clk", "clk32", CLK_SET_RATE_PARENT, PXA1928_CLK_KPC * 4, 0x3, 0x3, 0x0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA1928_CLK_RTC, "rtc_clk", "clk32", CLK_SET_RATE_PARENT, PXA1928_CLK_RTC * 4, 0x83, 0x83, 0x0, MMP_CLK_GATE_NEED_DELAY, NULL}, + {PXA1928_CLK_PWM0, "pwm0_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM0 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_PWM1, "pwm1_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM1 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_PWM2, "pwm2_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM2 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + {PXA1928_CLK_PWM3, "pwm3_clk", "vctcxo", CLK_SET_RATE_PARENT, PXA1928_CLK_PWM3 * 4, 0x3, 0x3, 0x0, 0, &reset_lock}, + /* The gate clocks has mux parent. */ + {PXA1928_CLK_UART0, "uart0_clk", "uart0_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART0 * 4, 0x3, 0x3, 0x0, 0, &uart0_lock}, + {PXA1928_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART1 * 4, 0x3, 0x3, 0x0, 0, &uart1_lock}, + {PXA1928_CLK_UART2, "uart2_clk", "uart2_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART2 * 4, 0x3, 0x3, 0x0, 0, &uart2_lock}, + {PXA1928_CLK_UART3, "uart3_clk", "uart3_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_UART3 * 4, 0x3, 0x3, 0x0, 0, &uart3_lock}, + {PXA1928_CLK_SSP0, "ssp0_clk", "ssp0_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_SSP0 * 4, 0x3, 0x3, 0x0, 0, &ssp0_lock}, + {PXA1928_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, PXA1928_CLK_SSP1 * 4, 0x3, 0x3, 0x0, 0, &ssp1_lock}, +}; + +static void pxa1928_apb_periph_clk_init(struct pxa1928_clk_unit *pxa_unit) +{ + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_mux_clks(unit, apbc_mux_clks, pxa_unit->apbc_base, + ARRAY_SIZE(apbc_mux_clks)); + + mmp_register_gate_clks(unit, apbc_gate_clks, pxa_unit->apbc_base, + ARRAY_SIZE(apbc_gate_clks)); +} + +static DEFINE_SPINLOCK(sdh0_lock); +static DEFINE_SPINLOCK(sdh1_lock); +static DEFINE_SPINLOCK(sdh2_lock); +static DEFINE_SPINLOCK(sdh3_lock); +static DEFINE_SPINLOCK(sdh4_lock); +static const char *sdh_parent_names[] = {"pll1_624", "pll5p", "pll5", "pll1_416"}; + +static DEFINE_SPINLOCK(usb_lock); + +static struct mmp_param_mux_clk apmu_mux_clks[] = { + {0, "sdh_mux", sdh_parent_names, ARRAY_SIZE(sdh_parent_names), CLK_SET_RATE_PARENT, PXA1928_CLK_SDH0 * 4, 8, 2, 0, &sdh0_lock}, +}; + +static struct mmp_param_div_clk apmu_div_clks[] = { + {0, "sdh_div", "sdh_mux", 0, PXA1928_CLK_SDH0 * 4, 10, 4, CLK_DIVIDER_ONE_BASED, &sdh0_lock}, +}; + +static struct mmp_param_gate_clk apmu_gate_clks[] = { + {PXA1928_CLK_USB, "usb_clk", "usb_pll", 0, PXA1928_CLK_USB * 4, 0x9, 0x9, 0x0, 0, &usb_lock}, + {PXA1928_CLK_HSIC, "hsic_clk", "usb_pll", 0, PXA1928_CLK_HSIC * 4, 0x9, 0x9, 0x0, 0, &usb_lock}, + /* The gate clocks has mux parent. */ + {PXA1928_CLK_SDH0, "sdh0_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH0 * 4, 0x1b, 0x1b, 0x0, 0, &sdh0_lock}, + {PXA1928_CLK_SDH1, "sdh1_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH1 * 4, 0x1b, 0x1b, 0x0, 0, &sdh1_lock}, + {PXA1928_CLK_SDH2, "sdh2_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH2 * 4, 0x1b, 0x1b, 0x0, 0, &sdh2_lock}, + {PXA1928_CLK_SDH3, "sdh3_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH3 * 4, 0x1b, 0x1b, 0x0, 0, &sdh3_lock}, + {PXA1928_CLK_SDH4, "sdh4_clk", "sdh_div", CLK_SET_RATE_PARENT, PXA1928_CLK_SDH4 * 4, 0x1b, 0x1b, 0x0, 0, &sdh4_lock}, +}; + +static void pxa1928_axi_periph_clk_init(struct pxa1928_clk_unit *pxa_unit) +{ + struct mmp_clk_unit *unit = &pxa_unit->unit; + + mmp_register_mux_clks(unit, apmu_mux_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_mux_clks)); + + mmp_register_div_clks(unit, apmu_div_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_div_clks)); + + mmp_register_gate_clks(unit, apmu_gate_clks, pxa_unit->apmu_base, + ARRAY_SIZE(apmu_gate_clks)); +} + +static void pxa1928_clk_reset_init(struct device_node *np, + struct pxa1928_clk_unit *pxa_unit) +{ + struct mmp_clk_reset_cell *cells; + int i, base, nr_resets; + + nr_resets = ARRAY_SIZE(apbc_gate_clks); + cells = kcalloc(nr_resets, sizeof(*cells), GFP_KERNEL); + if (!cells) + return; + + base = 0; + for (i = 0; i < nr_resets; i++) { + cells[base + i].clk_id = apbc_gate_clks[i].id; + cells[base + i].reg = + pxa_unit->apbc_base + apbc_gate_clks[i].offset; + cells[base + i].flags = 0; + cells[base + i].lock = apbc_gate_clks[i].lock; + cells[base + i].bits = 0x4; + } + + mmp_clk_reset_register(np, cells, nr_resets); +} + +static void __init pxa1928_mpmu_clk_init(struct device_node *np) +{ + struct pxa1928_clk_unit *pxa_unit; + + pxa_unit = kzalloc(sizeof(*pxa_unit), GFP_KERNEL); + if (!pxa_unit) + return; + + pxa_unit->mpmu_base = of_iomap(np, 0); + if (!pxa_unit->mpmu_base) { + pr_err("failed to map mpmu registers\n"); + return; + } + + pxa1928_pll_init(pxa_unit); +} +CLK_OF_DECLARE(pxa1928_mpmu_clk, "marvell,pxa1928-mpmu", pxa1928_mpmu_clk_init); + +static void __init pxa1928_apmu_clk_init(struct device_node *np) +{ + struct pxa1928_clk_unit *pxa_unit; + + pxa_unit = kzalloc(sizeof(*pxa_unit), GFP_KERNEL); + if (!pxa_unit) + return; + + pxa_unit->apmu_base = of_iomap(np, 0); + if (!pxa_unit->apmu_base) { + pr_err("failed to map apmu registers\n"); + return; + } + + mmp_clk_init(np, &pxa_unit->unit, PXA1928_APMU_NR_CLKS); + + pxa1928_axi_periph_clk_init(pxa_unit); +} +CLK_OF_DECLARE(pxa1928_apmu_clk, "marvell,pxa1928-apmu", pxa1928_apmu_clk_init); + +static void __init pxa1928_apbc_clk_init(struct device_node *np) +{ + struct pxa1928_clk_unit *pxa_unit; + + pxa_unit = kzalloc(sizeof(*pxa_unit), GFP_KERNEL); + if (!pxa_unit) + return; + + pxa_unit->apbc_base = of_iomap(np, 0); + if (!pxa_unit->apbc_base) { + pr_err("failed to map apbc registers\n"); + return; + } + + mmp_clk_init(np, &pxa_unit->unit, PXA1928_APBC_NR_CLKS); + + pxa1928_apb_periph_clk_init(pxa_unit); + pxa1928_clk_reset_init(np, pxa_unit); +} +CLK_OF_DECLARE(pxa1928_apbc_clk, "marvell,pxa1928-apbc", pxa1928_apbc_clk_init); -- cgit v1.2.3-58-ga151 From 95f589814c8af85fa673320bf319ffe8d721dd74 Mon Sep 17 00:00:00 2001 From: Ricky Liang Date: Mon, 18 May 2015 22:00:26 +0800 Subject: clk: mediatek: Initialize clk_init_data The variable init (struct clk_init_data) is allocated on the stack. We weren't initializing the .flags field, so it contains random junk, which can cause all kinds of interesting issues when the flags are parsed by clk_register. Signed-off-by: Ricky Liang Acked-by: Sascha Hauer Signed-off-by: Stephen Boyd --- drivers/clk/mediatek/clk-gate.c | 2 +- drivers/clk/mediatek/clk-pll.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/mediatek/clk-gate.c b/drivers/clk/mediatek/clk-gate.c index 9d77ee3256f2..57020368a693 100644 --- a/drivers/clk/mediatek/clk-gate.c +++ b/drivers/clk/mediatek/clk-gate.c @@ -109,7 +109,7 @@ struct clk *mtk_clk_register_gate( { struct mtk_clk_gate *cg; struct clk *clk; - struct clk_init_data init; + struct clk_init_data init = {}; cg = kzalloc(sizeof(*cg), GFP_KERNEL); if (!cg) diff --git a/drivers/clk/mediatek/clk-pll.c b/drivers/clk/mediatek/clk-pll.c index 66154caf992a..44409e98c52f 100644 --- a/drivers/clk/mediatek/clk-pll.c +++ b/drivers/clk/mediatek/clk-pll.c @@ -268,7 +268,7 @@ static struct clk *mtk_clk_register_pll(const struct mtk_pll_data *data, void __iomem *base) { struct mtk_clk_pll *pll; - struct clk_init_data init; + struct clk_init_data init = {}; struct clk *clk; const char *parent_name = "clk26m"; -- cgit v1.2.3-58-ga151 From 301c5d29402e590ea893fe7ddc83a00a71994cba Mon Sep 17 00:00:00 2001 From: Zhiwu Song Date: Wed, 20 May 2015 08:50:33 +0000 Subject: clk: sirf: add CSR atlas7 clk and reset support the hardware node includes both clock and reset support, so it is named as "car". this patch implements Flexible clocks(mux, divider, gate), Selectable clock(mux, divider, gate), root clock(gate),leaf clock(gate), others. it also implements the reset controller functionality. Signed-off-by: Zhiwu Song Signed-off-by: Guo Zeng Signed-off-by: Barry Song Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/csr,atlas7-car.txt | 55 + drivers/clk/sirf/Makefile | 2 +- drivers/clk/sirf/clk-atlas7.c | 1632 ++++++++++++++++++++ 3 files changed, 1688 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/clock/csr,atlas7-car.txt create mode 100644 drivers/clk/sirf/clk-atlas7.c diff --git a/Documentation/devicetree/bindings/clock/csr,atlas7-car.txt b/Documentation/devicetree/bindings/clock/csr,atlas7-car.txt new file mode 100644 index 000000000000..54d6d1358339 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/csr,atlas7-car.txt @@ -0,0 +1,55 @@ +* Clock and reset bindings for CSR atlas7 + +Required properties: +- compatible: Should be "sirf,atlas7-car" +- reg: Address and length of the register set +- #clock-cells: Should be <1> +- #reset-cells: Should be <1> + +The clock consumer should specify the desired clock by having the clock +ID in its "clocks" phandle cell. +The ID list atlas7_clks defined in drivers/clk/sirf/clk-atlas7.c + +The reset consumer should specify the desired reset by having the reset +ID in its "reset" phandle cell. +The ID list atlas7_reset_unit defined in drivers/clk/sirf/clk-atlas7.c + +Examples: Clock and reset controller node: + +car: clock-controller@18620000 { + compatible = "sirf,atlas7-car"; + reg = <0x18620000 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; +}; + +Examples: Consumers using clock or reset: + +timer@10dc0000 { + compatible = "sirf,macro-tick"; + reg = <0x10dc0000 0x1000>; + clocks = <&car 54>; + interrupts = <0 0 0>, + <0 1 0>, + <0 2 0>, + <0 49 0>, + <0 50 0>, + <0 51 0>; +}; + +uart1: uart@18020000 { + cell-index = <1>; + compatible = "sirf,macro-uart"; + reg = <0x18020000 0x1000>; + clocks = <&clks 95>; + interrupts = <0 18 0>; + fifosize = <32>; +}; + +vpp@13110000 { + compatible = "sirf,prima2-vpp"; + reg = <0x13110000 0x10000>; + interrupts = <0 31 0>; + clocks = <&car 85>; + resets = <&car 29>; +}; diff --git a/drivers/clk/sirf/Makefile b/drivers/clk/sirf/Makefile index 36b8e203f6e7..09b4210d9124 100644 --- a/drivers/clk/sirf/Makefile +++ b/drivers/clk/sirf/Makefile @@ -2,4 +2,4 @@ # Makefile for sirf specific clk # -obj-$(CONFIG_ARCH_SIRF) += clk-prima2.o clk-atlas6.o +obj-$(CONFIG_ARCH_SIRF) += clk-prima2.o clk-atlas6.o clk-atlas7.o diff --git a/drivers/clk/sirf/clk-atlas7.c b/drivers/clk/sirf/clk-atlas7.c new file mode 100644 index 000000000000..db8ab691dbf6 --- /dev/null +++ b/drivers/clk/sirf/clk-atlas7.c @@ -0,0 +1,1632 @@ +/* + * Clock tree for CSR SiRFAtlas7 + * + * Copyright (c) 2014 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define SIRFSOC_CLKC_MEMPLL_AB_FREQ 0x0000 +#define SIRFSOC_CLKC_MEMPLL_AB_SSC 0x0004 +#define SIRFSOC_CLKC_MEMPLL_AB_CTRL0 0x0008 +#define SIRFSOC_CLKC_MEMPLL_AB_CTRL1 0x000c +#define SIRFSOC_CLKC_MEMPLL_AB_STATUS 0x0010 +#define SIRFSOC_CLKC_MEMPLL_AB_SSRAM_ADDR 0x0014 +#define SIRFSOC_CLKC_MEMPLL_AB_SSRAM_DATA 0x0018 + +#define SIRFSOC_CLKC_CPUPLL_AB_FREQ 0x001c +#define SIRFSOC_CLKC_CPUPLL_AB_SSC 0x0020 +#define SIRFSOC_CLKC_CPUPLL_AB_CTRL0 0x0024 +#define SIRFSOC_CLKC_CPUPLL_AB_CTRL1 0x0028 +#define SIRFSOC_CLKC_CPUPLL_AB_STATUS 0x002c + +#define SIRFSOC_CLKC_SYS0PLL_AB_FREQ 0x0030 +#define SIRFSOC_CLKC_SYS0PLL_AB_SSC 0x0034 +#define SIRFSOC_CLKC_SYS0PLL_AB_CTRL0 0x0038 +#define SIRFSOC_CLKC_SYS0PLL_AB_CTRL1 0x003c +#define SIRFSOC_CLKC_SYS0PLL_AB_STATUS 0x0040 + +#define SIRFSOC_CLKC_SYS1PLL_AB_FREQ 0x0044 +#define SIRFSOC_CLKC_SYS1PLL_AB_SSC 0x0048 +#define SIRFSOC_CLKC_SYS1PLL_AB_CTRL0 0x004c +#define SIRFSOC_CLKC_SYS1PLL_AB_CTRL1 0x0050 +#define SIRFSOC_CLKC_SYS1PLL_AB_STATUS 0x0054 + +#define SIRFSOC_CLKC_SYS2PLL_AB_FREQ 0x0058 +#define SIRFSOC_CLKC_SYS2PLL_AB_SSC 0x005c +#define SIRFSOC_CLKC_SYS2PLL_AB_CTRL0 0x0060 +#define SIRFSOC_CLKC_SYS2PLL_AB_CTRL1 0x0064 +#define SIRFSOC_CLKC_SYS2PLL_AB_STATUS 0x0068 + +#define SIRFSOC_CLKC_SYS3PLL_AB_FREQ 0x006c +#define SIRFSOC_CLKC_SYS3PLL_AB_SSC 0x0070 +#define SIRFSOC_CLKC_SYS3PLL_AB_CTRL0 0x0074 +#define SIRFSOC_CLKC_SYS3PLL_AB_CTRL1 0x0078 +#define SIRFSOC_CLKC_SYS3PLL_AB_STATUS 0x007c + +#define SIRFSOC_ABPLL_CTRL0_SSEN 0x00001000 +#define SIRFSOC_ABPLL_CTRL0_BYPASS 0x00000010 +#define SIRFSOC_ABPLL_CTRL0_RESET 0x00000001 + +#define SIRFSOC_CLKC_AUDIO_DTO_INC 0x0088 +#define SIRFSOC_CLKC_DISP0_DTO_INC 0x008c +#define SIRFSOC_CLKC_DISP1_DTO_INC 0x0090 + +#define SIRFSOC_CLKC_AUDIO_DTO_SRC 0x0094 +#define SIRFSOC_CLKC_AUDIO_DTO_ENA 0x0098 +#define SIRFSOC_CLKC_AUDIO_DTO_DROFF 0x009c + +#define SIRFSOC_CLKC_DISP0_DTO_SRC 0x00a0 +#define SIRFSOC_CLKC_DISP0_DTO_ENA 0x00a4 +#define SIRFSOC_CLKC_DISP0_DTO_DROFF 0x00a8 + +#define SIRFSOC_CLKC_DISP1_DTO_SRC 0x00ac +#define SIRFSOC_CLKC_DISP1_DTO_ENA 0x00b0 +#define SIRFSOC_CLKC_DISP1_DTO_DROFF 0x00b4 + +#define SIRFSOC_CLKC_I2S_CLK_SEL 0x00b8 +#define SIRFSOC_CLKC_I2S_SEL_STAT 0x00bc + +#define SIRFSOC_CLKC_USBPHY_CLKDIV_CFG 0x00c0 +#define SIRFSOC_CLKC_USBPHY_CLKDIV_ENA 0x00c4 +#define SIRFSOC_CLKC_USBPHY_CLK_SEL 0x00c8 +#define SIRFSOC_CLKC_USBPHY_CLK_SEL_STAT 0x00cc + +#define SIRFSOC_CLKC_BTSS_CLKDIV_CFG 0x00d0 +#define SIRFSOC_CLKC_BTSS_CLKDIV_ENA 0x00d4 +#define SIRFSOC_CLKC_BTSS_CLK_SEL 0x00d8 +#define SIRFSOC_CLKC_BTSS_CLK_SEL_STAT 0x00dc + +#define SIRFSOC_CLKC_RGMII_CLKDIV_CFG 0x00e0 +#define SIRFSOC_CLKC_RGMII_CLKDIV_ENA 0x00e4 +#define SIRFSOC_CLKC_RGMII_CLK_SEL 0x00e8 +#define SIRFSOC_CLKC_RGMII_CLK_SEL_STAT 0x00ec + +#define SIRFSOC_CLKC_CPU_CLKDIV_CFG 0x00f0 +#define SIRFSOC_CLKC_CPU_CLKDIV_ENA 0x00f4 +#define SIRFSOC_CLKC_CPU_CLK_SEL 0x00f8 +#define SIRFSOC_CLKC_CPU_CLK_SEL_STAT 0x00fc + +#define SIRFSOC_CLKC_SDPHY01_CLKDIV_CFG 0x0100 +#define SIRFSOC_CLKC_SDPHY01_CLKDIV_ENA 0x0104 +#define SIRFSOC_CLKC_SDPHY01_CLK_SEL 0x0108 +#define SIRFSOC_CLKC_SDPHY01_CLK_SEL_STAT 0x010c + +#define SIRFSOC_CLKC_SDPHY23_CLKDIV_CFG 0x0110 +#define SIRFSOC_CLKC_SDPHY23_CLKDIV_ENA 0x0114 +#define SIRFSOC_CLKC_SDPHY23_CLK_SEL 0x0118 +#define SIRFSOC_CLKC_SDPHY23_CLK_SEL_STAT 0x011c + +#define SIRFSOC_CLKC_SDPHY45_CLKDIV_CFG 0x0120 +#define SIRFSOC_CLKC_SDPHY45_CLKDIV_ENA 0x0124 +#define SIRFSOC_CLKC_SDPHY45_CLK_SEL 0x0128 +#define SIRFSOC_CLKC_SDPHY45_CLK_SEL_STAT 0x012c + +#define SIRFSOC_CLKC_SDPHY67_CLKDIV_CFG 0x0130 +#define SIRFSOC_CLKC_SDPHY67_CLKDIV_ENA 0x0134 +#define SIRFSOC_CLKC_SDPHY67_CLK_SEL 0x0138 +#define SIRFSOC_CLKC_SDPHY67_CLK_SEL_STAT 0x013c + +#define SIRFSOC_CLKC_CAN_CLKDIV_CFG 0x0140 +#define SIRFSOC_CLKC_CAN_CLKDIV_ENA 0x0144 +#define SIRFSOC_CLKC_CAN_CLK_SEL 0x0148 +#define SIRFSOC_CLKC_CAN_CLK_SEL_STAT 0x014c + +#define SIRFSOC_CLKC_DEINT_CLKDIV_CFG 0x0150 +#define SIRFSOC_CLKC_DEINT_CLKDIV_ENA 0x0154 +#define SIRFSOC_CLKC_DEINT_CLK_SEL 0x0158 +#define SIRFSOC_CLKC_DEINT_CLK_SEL_STAT 0x015c + +#define SIRFSOC_CLKC_NAND_CLKDIV_CFG 0x0160 +#define SIRFSOC_CLKC_NAND_CLKDIV_ENA 0x0164 +#define SIRFSOC_CLKC_NAND_CLK_SEL 0x0168 +#define SIRFSOC_CLKC_NAND_CLK_SEL_STAT 0x016c + +#define SIRFSOC_CLKC_DISP0_CLKDIV_CFG 0x0170 +#define SIRFSOC_CLKC_DISP0_CLKDIV_ENA 0x0174 +#define SIRFSOC_CLKC_DISP0_CLK_SEL 0x0178 +#define SIRFSOC_CLKC_DISP0_CLK_SEL_STAT 0x017c + +#define SIRFSOC_CLKC_DISP1_CLKDIV_CFG 0x0180 +#define SIRFSOC_CLKC_DISP1_CLKDIV_ENA 0x0184 +#define SIRFSOC_CLKC_DISP1_CLK_SEL 0x0188 +#define SIRFSOC_CLKC_DISP1_CLK_SEL_STAT 0x018c + +#define SIRFSOC_CLKC_GPU_CLKDIV_CFG 0x0190 +#define SIRFSOC_CLKC_GPU_CLKDIV_ENA 0x0194 +#define SIRFSOC_CLKC_GPU_CLK_SEL 0x0198 +#define SIRFSOC_CLKC_GPU_CLK_SEL_STAT 0x019c + +#define SIRFSOC_CLKC_GNSS_CLKDIV_CFG 0x01a0 +#define SIRFSOC_CLKC_GNSS_CLKDIV_ENA 0x01a4 +#define SIRFSOC_CLKC_GNSS_CLK_SEL 0x01a8 +#define SIRFSOC_CLKC_GNSS_CLK_SEL_STAT 0x01ac + +#define SIRFSOC_CLKC_SHARED_DIVIDER_CFG0 0x01b0 +#define SIRFSOC_CLKC_SHARED_DIVIDER_CFG1 0x01b4 +#define SIRFSOC_CLKC_SHARED_DIVIDER_ENA 0x01b8 + +#define SIRFSOC_CLKC_SYS_CLK_SEL 0x01bc +#define SIRFSOC_CLKC_SYS_CLK_SEL_STAT 0x01c0 +#define SIRFSOC_CLKC_IO_CLK_SEL 0x01c4 +#define SIRFSOC_CLKC_IO_CLK_SEL_STAT 0x01c8 +#define SIRFSOC_CLKC_G2D_CLK_SEL 0x01cc +#define SIRFSOC_CLKC_G2D_CLK_SEL_STAT 0x01d0 +#define SIRFSOC_CLKC_JPENC_CLK_SEL 0x01d4 +#define SIRFSOC_CLKC_JPENC_CLK_SEL_STAT 0x01d8 +#define SIRFSOC_CLKC_VDEC_CLK_SEL 0x01dc +#define SIRFSOC_CLKC_VDEC_CLK_SEL_STAT 0x01e0 +#define SIRFSOC_CLKC_GMAC_CLK_SEL 0x01e4 +#define SIRFSOC_CLKC_GMAC_CLK_SEL_STAT 0x01e8 +#define SIRFSOC_CLKC_USB_CLK_SEL 0x01ec +#define SIRFSOC_CLKC_USB_CLK_SEL_STAT 0x01f0 +#define SIRFSOC_CLKC_KAS_CLK_SEL 0x01f4 +#define SIRFSOC_CLKC_KAS_CLK_SEL_STAT 0x01f8 +#define SIRFSOC_CLKC_SEC_CLK_SEL 0x01fc +#define SIRFSOC_CLKC_SEC_CLK_SEL_STAT 0x0200 +#define SIRFSOC_CLKC_SDR_CLK_SEL 0x0204 +#define SIRFSOC_CLKC_SDR_CLK_SEL_STAT 0x0208 +#define SIRFSOC_CLKC_VIP_CLK_SEL 0x020c +#define SIRFSOC_CLKC_VIP_CLK_SEL_STAT 0x0210 +#define SIRFSOC_CLKC_NOCD_CLK_SEL 0x0214 +#define SIRFSOC_CLKC_NOCD_CLK_SEL_STAT 0x0218 +#define SIRFSOC_CLKC_NOCR_CLK_SEL 0x021c +#define SIRFSOC_CLKC_NOCR_CLK_SEL_STAT 0x0220 +#define SIRFSOC_CLKC_TPIU_CLK_SEL 0x0224 +#define SIRFSOC_CLKC_TPIU_CLK_SEL_STAT 0x0228 + +#define SIRFSOC_CLKC_ROOT_CLK_EN0_SET 0x022c +#define SIRFSOC_CLKC_ROOT_CLK_EN0_CLR 0x0230 +#define SIRFSOC_CLKC_ROOT_CLK_EN0_STAT 0x0234 +#define SIRFSOC_CLKC_ROOT_CLK_EN1_SET 0x0238 +#define SIRFSOC_CLKC_ROOT_CLK_EN1_CLR 0x023c +#define SIRFSOC_CLKC_ROOT_CLK_EN1_STAT 0x0240 + +#define SIRFSOC_CLKC_LEAF_CLK_EN0_SET 0x0244 +#define SIRFSOC_CLKC_LEAF_CLK_EN0_CLR 0x0248 +#define SIRFSOC_CLKC_LEAF_CLK_EN0_STAT 0x024c + +#define SIRFSOC_CLKC_RSTC_A7_SW_RST 0x0308 + +#define SIRFSOC_CLKC_LEAF_CLK_EN1_SET 0x04a0 +#define SIRFSOC_CLKC_LEAF_CLK_EN2_SET 0x04b8 +#define SIRFSOC_CLKC_LEAF_CLK_EN3_SET 0x04d0 +#define SIRFSOC_CLKC_LEAF_CLK_EN4_SET 0x04e8 +#define SIRFSOC_CLKC_LEAF_CLK_EN5_SET 0x0500 +#define SIRFSOC_CLKC_LEAF_CLK_EN6_SET 0x0518 +#define SIRFSOC_CLKC_LEAF_CLK_EN7_SET 0x0530 +#define SIRFSOC_CLKC_LEAF_CLK_EN8_SET 0x0548 + + +static void __iomem *sirfsoc_clk_vbase; +static struct clk_onecell_data clk_data; + +static const struct clk_div_table pll_div_table[] = { + { .val = 0, .div = 1 }, + { .val = 1, .div = 2 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 8 }, + { .val = 4, .div = 16 }, + { .val = 5, .div = 32 }, +}; + +struct clk_pll { + struct clk_hw hw; + u16 regofs; /* register offset */ +}; +#define to_pllclk(_hw) container_of(_hw, struct clk_pll, hw) + +struct clk_dto { + struct clk_hw hw; + u16 inc_offset; /* dto increment offset */ + u16 src_offset; /* dto src offset */ +}; +#define to_dtoclk(_hw) container_of(_hw, struct clk_dto, hw) + +struct clk_unit { + struct clk_hw hw; + u16 regofs; + u16 bit; + spinlock_t *lock; +}; +#define to_unitclk(_hw) container_of(_hw, struct clk_unit, hw) + +struct atlas7_div_init_data { + const char *div_name; + const char *parent_name; + const char *gate_name; + unsigned long flags; + u8 divider_flags; + u8 gate_flags; + u32 div_offset; + u8 shift; + u8 width; + u32 gate_offset; + u8 gate_bit; + spinlock_t *lock; +}; + +struct atlas7_mux_init_data { + const char *mux_name; + const char * const *parent_names; + u8 parent_num; + unsigned long flags; + u8 mux_flags; + u32 mux_offset; + u8 shift; + u8 width; +}; + +struct atlas7_unit_init_data { + u32 index; + const char *unit_name; + const char *parent_name; + unsigned long flags; + u32 regofs; + u8 bit; + spinlock_t *lock; +}; + +struct atlas7_reset_desc { + const char *name; + u32 clk_ofs; + u8 clk_bit; + u32 rst_ofs; + u8 rst_bit; + spinlock_t *lock; +}; + +static DEFINE_SPINLOCK(cpupll_ctrl1_lock); +static DEFINE_SPINLOCK(mempll_ctrl1_lock); +static DEFINE_SPINLOCK(sys0pll_ctrl1_lock); +static DEFINE_SPINLOCK(sys1pll_ctrl1_lock); +static DEFINE_SPINLOCK(sys2pll_ctrl1_lock); +static DEFINE_SPINLOCK(sys3pll_ctrl1_lock); +static DEFINE_SPINLOCK(usbphy_div_lock); +static DEFINE_SPINLOCK(btss_div_lock); +static DEFINE_SPINLOCK(rgmii_div_lock); +static DEFINE_SPINLOCK(cpu_div_lock); +static DEFINE_SPINLOCK(sdphy01_div_lock); +static DEFINE_SPINLOCK(sdphy23_div_lock); +static DEFINE_SPINLOCK(sdphy45_div_lock); +static DEFINE_SPINLOCK(sdphy67_div_lock); +static DEFINE_SPINLOCK(can_div_lock); +static DEFINE_SPINLOCK(deint_div_lock); +static DEFINE_SPINLOCK(nand_div_lock); +static DEFINE_SPINLOCK(disp0_div_lock); +static DEFINE_SPINLOCK(disp1_div_lock); +static DEFINE_SPINLOCK(gpu_div_lock); +static DEFINE_SPINLOCK(gnss_div_lock); +/* gate register shared */ +static DEFINE_SPINLOCK(share_div_lock); +static DEFINE_SPINLOCK(root0_gate_lock); +static DEFINE_SPINLOCK(root1_gate_lock); +static DEFINE_SPINLOCK(leaf0_gate_lock); +static DEFINE_SPINLOCK(leaf1_gate_lock); +static DEFINE_SPINLOCK(leaf2_gate_lock); +static DEFINE_SPINLOCK(leaf3_gate_lock); +static DEFINE_SPINLOCK(leaf4_gate_lock); +static DEFINE_SPINLOCK(leaf5_gate_lock); +static DEFINE_SPINLOCK(leaf6_gate_lock); +static DEFINE_SPINLOCK(leaf7_gate_lock); +static DEFINE_SPINLOCK(leaf8_gate_lock); + +static inline unsigned long clkc_readl(unsigned reg) +{ + return readl(sirfsoc_clk_vbase + reg); +} + +static inline void clkc_writel(u32 val, unsigned reg) +{ + writel(val, sirfsoc_clk_vbase + reg); +} + +/* +* ABPLL +* integer mode: Fvco = Fin * 2 * NF / NR +* Spread Spectrum mode: Fvco = Fin * SSN / NR +* SSN = 2^24 / (256 * ((ssdiv >> ssdepth) << ssdepth) + (ssmod << ssdepth)) +*/ +static unsigned long pll_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned long fin = parent_rate; + struct clk_pll *clk = to_pllclk(hw); + u64 rate; + u32 regctrl0 = clkc_readl(clk->regofs + SIRFSOC_CLKC_MEMPLL_AB_CTRL0 - + SIRFSOC_CLKC_MEMPLL_AB_FREQ); + u32 regfreq = clkc_readl(clk->regofs); + u32 regssc = clkc_readl(clk->regofs + SIRFSOC_CLKC_MEMPLL_AB_SSC - + SIRFSOC_CLKC_MEMPLL_AB_FREQ); + u32 nr = (regfreq >> 16 & (BIT(3) - 1)) + 1; + u32 nf = (regfreq & (BIT(9) - 1)) + 1; + u32 ssdiv = regssc >> 8 & (BIT(12) - 1); + u32 ssdepth = regssc >> 20 & (BIT(2) - 1); + u32 ssmod = regssc & (BIT(8) - 1); + + if (regctrl0 & SIRFSOC_ABPLL_CTRL0_BYPASS) + return fin; + + if (regctrl0 & SIRFSOC_ABPLL_CTRL0_SSEN) { + rate = fin; + rate *= 1 << 24; + do_div(rate, (256 * ((ssdiv >> ssdepth) << ssdepth) + + (ssmod << ssdepth))); + } else { + rate = 2 * fin; + rate *= nf; + do_div(rate, nr); + } + return rate; +} + +static const struct clk_ops ab_pll_ops = { + .recalc_rate = pll_clk_recalc_rate, +}; + +static const char * const pll_clk_parents[] = { + "xin", +}; + +static struct clk_init_data clk_cpupll_init = { + .name = "cpupll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_cpupll = { + .regofs = SIRFSOC_CLKC_CPUPLL_AB_FREQ, + .hw = { + .init = &clk_cpupll_init, + }, +}; + +static struct clk_init_data clk_mempll_init = { + .name = "mempll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_mempll = { + .regofs = SIRFSOC_CLKC_MEMPLL_AB_FREQ, + .hw = { + .init = &clk_mempll_init, + }, +}; + +static struct clk_init_data clk_sys0pll_init = { + .name = "sys0pll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_sys0pll = { + .regofs = SIRFSOC_CLKC_SYS0PLL_AB_FREQ, + .hw = { + .init = &clk_sys0pll_init, + }, +}; + +static struct clk_init_data clk_sys1pll_init = { + .name = "sys1pll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_sys1pll = { + .regofs = SIRFSOC_CLKC_SYS1PLL_AB_FREQ, + .hw = { + .init = &clk_sys1pll_init, + }, +}; + +static struct clk_init_data clk_sys2pll_init = { + .name = "sys2pll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_sys2pll = { + .regofs = SIRFSOC_CLKC_SYS2PLL_AB_FREQ, + .hw = { + .init = &clk_sys2pll_init, + }, +}; + +static struct clk_init_data clk_sys3pll_init = { + .name = "sys3pll_vco", + .ops = &ab_pll_ops, + .parent_names = pll_clk_parents, + .num_parents = ARRAY_SIZE(pll_clk_parents), +}; + +static struct clk_pll clk_sys3pll = { + .regofs = SIRFSOC_CLKC_SYS3PLL_AB_FREQ, + .hw = { + .init = &clk_sys3pll_init, + }, +}; + +/* + * DTO in clkc, default enable double resolution mode + * double resolution mode:fout = fin * finc / 2^29 + * normal mode:fout = fin * finc / 2^28 + */ +static int dto_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_dto *clk = to_dtoclk(hw); + int reg; + + reg = clk->src_offset + SIRFSOC_CLKC_AUDIO_DTO_ENA - SIRFSOC_CLKC_AUDIO_DTO_SRC; + + return !!(clkc_readl(reg) & BIT(0)); +} + +static int dto_clk_enable(struct clk_hw *hw) +{ + u32 val, reg; + struct clk_dto *clk = to_dtoclk(hw); + + reg = clk->src_offset + SIRFSOC_CLKC_AUDIO_DTO_ENA - SIRFSOC_CLKC_AUDIO_DTO_SRC; + + val = clkc_readl(reg) | BIT(0); + clkc_writel(val, reg); + return 0; +} + +static void dto_clk_disable(struct clk_hw *hw) +{ + u32 val, reg; + struct clk_dto *clk = to_dtoclk(hw); + + reg = clk->src_offset + SIRFSOC_CLKC_AUDIO_DTO_ENA - SIRFSOC_CLKC_AUDIO_DTO_SRC; + + val = clkc_readl(reg) & ~BIT(0); + clkc_writel(val, reg); +} + +static unsigned long dto_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u64 rate = parent_rate; + struct clk_dto *clk = to_dtoclk(hw); + u32 finc = clkc_readl(clk->inc_offset); + u32 droff = clkc_readl(clk->src_offset + SIRFSOC_CLKC_AUDIO_DTO_DROFF - SIRFSOC_CLKC_AUDIO_DTO_SRC); + + rate *= finc; + if (droff & BIT(0)) + /* Double resolution off */ + do_div(rate, 1 << 28); + else + do_div(rate, 1 << 29); + + return rate; +} + +static long dto_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u64 dividend = rate * (1 << 29); + + do_div(dividend, *parent_rate); + dividend *= *parent_rate; + do_div(dividend, 1 << 29); + + return dividend; +} + +static int dto_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u64 dividend = rate * (1 << 29); + struct clk_dto *clk = to_dtoclk(hw); + + do_div(dividend, parent_rate); + clkc_writel(0, clk->src_offset + SIRFSOC_CLKC_AUDIO_DTO_DROFF - SIRFSOC_CLKC_AUDIO_DTO_SRC); + clkc_writel(dividend, clk->inc_offset); + + return 0; +} + +static u8 dto_clk_get_parent(struct clk_hw *hw) +{ + struct clk_dto *clk = to_dtoclk(hw); + + return clkc_readl(clk->src_offset); +} + +/* + * dto need CLK_SET_PARENT_GATE + */ +static int dto_clk_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_dto *clk = to_dtoclk(hw); + + clkc_writel(index, clk->src_offset); + return 0; +} + +static const struct clk_ops dto_ops = { + .is_enabled = dto_clk_is_enabled, + .enable = dto_clk_enable, + .disable = dto_clk_disable, + .recalc_rate = dto_clk_recalc_rate, + .round_rate = dto_clk_round_rate, + .set_rate = dto_clk_set_rate, + .get_parent = dto_clk_get_parent, + .set_parent = dto_clk_set_parent, +}; + +/* dto parent clock as syspllvco/clk1 */ +static const char * const audiodto_clk_parents[] = { + "sys0pll_clk1", + "sys1pll_clk1", + "sys3pll_clk1", +}; + +static struct clk_init_data clk_audiodto_init = { + .name = "audio_dto", + .ops = &dto_ops, + .parent_names = audiodto_clk_parents, + .num_parents = ARRAY_SIZE(audiodto_clk_parents), +}; + +static struct clk_dto clk_audio_dto = { + .inc_offset = SIRFSOC_CLKC_AUDIO_DTO_INC, + .src_offset = SIRFSOC_CLKC_AUDIO_DTO_SRC, + .hw = { + .init = &clk_audiodto_init, + }, +}; + +static const char * const disp0dto_clk_parents[] = { + "sys0pll_clk1", + "sys1pll_clk1", + "sys3pll_clk1", +}; + +static struct clk_init_data clk_disp0dto_init = { + .name = "disp0_dto", + .ops = &dto_ops, + .parent_names = disp0dto_clk_parents, + .num_parents = ARRAY_SIZE(disp0dto_clk_parents), +}; + +static struct clk_dto clk_disp0_dto = { + .inc_offset = SIRFSOC_CLKC_DISP0_DTO_INC, + .src_offset = SIRFSOC_CLKC_DISP0_DTO_SRC, + .hw = { + .init = &clk_disp0dto_init, + }, +}; + +static const char * const disp1dto_clk_parents[] = { + "sys0pll_clk1", + "sys1pll_clk1", + "sys3pll_clk1", +}; + +static struct clk_init_data clk_disp1dto_init = { + .name = "disp1_dto", + .ops = &dto_ops, + .parent_names = disp1dto_clk_parents, + .num_parents = ARRAY_SIZE(disp1dto_clk_parents), +}; + +static struct clk_dto clk_disp1_dto = { + .inc_offset = SIRFSOC_CLKC_DISP1_DTO_INC, + .src_offset = SIRFSOC_CLKC_DISP1_DTO_SRC, + .hw = { + .init = &clk_disp1dto_init, + }, +}; + +static struct atlas7_div_init_data divider_list[] __initdata = { + /* div_name, parent_name, gate_name, clk_flag, divider_flag, gate_flag, div_offset, shift, wdith, gate_offset, bit_enable, lock */ + { "sys0pll_qa1", "sys0pll_fixdiv", "sys0pll_a1", 0, 0, 0, SIRFSOC_CLKC_USBPHY_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_USBPHY_CLKDIV_ENA, 0, &usbphy_div_lock }, + { "sys1pll_qa1", "sys1pll_fixdiv", "sys1pll_a1", 0, 0, 0, SIRFSOC_CLKC_USBPHY_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_USBPHY_CLKDIV_ENA, 4, &usbphy_div_lock }, + { "sys2pll_qa1", "sys2pll_fixdiv", "sys2pll_a1", 0, 0, 0, SIRFSOC_CLKC_USBPHY_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_USBPHY_CLKDIV_ENA, 8, &usbphy_div_lock }, + { "sys3pll_qa1", "sys3pll_fixdiv", "sys3pll_a1", 0, 0, 0, SIRFSOC_CLKC_USBPHY_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_USBPHY_CLKDIV_ENA, 12, &usbphy_div_lock }, + { "sys0pll_qa2", "sys0pll_fixdiv", "sys0pll_a2", 0, 0, 0, SIRFSOC_CLKC_BTSS_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_BTSS_CLKDIV_ENA, 0, &btss_div_lock }, + { "sys1pll_qa2", "sys1pll_fixdiv", "sys1pll_a2", 0, 0, 0, SIRFSOC_CLKC_BTSS_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_BTSS_CLKDIV_ENA, 4, &btss_div_lock }, + { "sys2pll_qa2", "sys2pll_fixdiv", "sys2pll_a2", 0, 0, 0, SIRFSOC_CLKC_BTSS_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_BTSS_CLKDIV_ENA, 8, &btss_div_lock }, + { "sys3pll_qa2", "sys3pll_fixdiv", "sys3pll_a2", 0, 0, 0, SIRFSOC_CLKC_BTSS_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_BTSS_CLKDIV_ENA, 12, &btss_div_lock }, + { "sys0pll_qa3", "sys0pll_fixdiv", "sys0pll_a3", 0, 0, 0, SIRFSOC_CLKC_RGMII_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_RGMII_CLKDIV_ENA, 0, &rgmii_div_lock }, + { "sys1pll_qa3", "sys1pll_fixdiv", "sys1pll_a3", 0, 0, 0, SIRFSOC_CLKC_RGMII_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_RGMII_CLKDIV_ENA, 4, &rgmii_div_lock }, + { "sys2pll_qa3", "sys2pll_fixdiv", "sys2pll_a3", 0, 0, 0, SIRFSOC_CLKC_RGMII_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_RGMII_CLKDIV_ENA, 8, &rgmii_div_lock }, + { "sys3pll_qa3", "sys3pll_fixdiv", "sys3pll_a3", 0, 0, 0, SIRFSOC_CLKC_RGMII_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_RGMII_CLKDIV_ENA, 12, &rgmii_div_lock }, + { "sys0pll_qa4", "sys0pll_fixdiv", "sys0pll_a4", 0, 0, 0, SIRFSOC_CLKC_CPU_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_CPU_CLKDIV_ENA, 0, &cpu_div_lock }, + { "sys1pll_qa4", "sys1pll_fixdiv", "sys1pll_a4", 0, 0, CLK_IGNORE_UNUSED, SIRFSOC_CLKC_CPU_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_CPU_CLKDIV_ENA, 4, &cpu_div_lock }, + { "sys0pll_qa5", "sys0pll_fixdiv", "sys0pll_a5", 0, 0, 0, SIRFSOC_CLKC_SDPHY01_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_SDPHY01_CLKDIV_ENA, 0, &sdphy01_div_lock }, + { "sys1pll_qa5", "sys1pll_fixdiv", "sys1pll_a5", 0, 0, 0, SIRFSOC_CLKC_SDPHY01_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_SDPHY01_CLKDIV_ENA, 4, &sdphy01_div_lock }, + { "sys2pll_qa5", "sys2pll_fixdiv", "sys2pll_a5", 0, 0, 0, SIRFSOC_CLKC_SDPHY01_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_SDPHY01_CLKDIV_ENA, 8, &sdphy01_div_lock }, + { "sys3pll_qa5", "sys3pll_fixdiv", "sys3pll_a5", 0, 0, 0, SIRFSOC_CLKC_SDPHY01_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_SDPHY01_CLKDIV_ENA, 12, &sdphy01_div_lock }, + { "sys0pll_qa6", "sys0pll_fixdiv", "sys0pll_a6", 0, 0, 0, SIRFSOC_CLKC_SDPHY23_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_SDPHY23_CLKDIV_ENA, 0, &sdphy23_div_lock }, + { "sys1pll_qa6", "sys1pll_fixdiv", "sys1pll_a6", 0, 0, 0, SIRFSOC_CLKC_SDPHY23_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_SDPHY23_CLKDIV_ENA, 4, &sdphy23_div_lock }, + { "sys2pll_qa6", "sys2pll_fixdiv", "sys2pll_a6", 0, 0, 0, SIRFSOC_CLKC_SDPHY23_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_SDPHY23_CLKDIV_ENA, 8, &sdphy23_div_lock }, + { "sys3pll_qa6", "sys3pll_fixdiv", "sys3pll_a6", 0, 0, 0, SIRFSOC_CLKC_SDPHY23_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_SDPHY23_CLKDIV_ENA, 12, &sdphy23_div_lock }, + { "sys0pll_qa7", "sys0pll_fixdiv", "sys0pll_a7", 0, 0, 0, SIRFSOC_CLKC_SDPHY45_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_SDPHY45_CLKDIV_ENA, 0, &sdphy45_div_lock }, + { "sys1pll_qa7", "sys1pll_fixdiv", "sys1pll_a7", 0, 0, 0, SIRFSOC_CLKC_SDPHY45_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_SDPHY45_CLKDIV_ENA, 4, &sdphy45_div_lock }, + { "sys2pll_qa7", "sys2pll_fixdiv", "sys2pll_a7", 0, 0, 0, SIRFSOC_CLKC_SDPHY45_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_SDPHY45_CLKDIV_ENA, 8, &sdphy45_div_lock }, + { "sys3pll_qa7", "sys3pll_fixdiv", "sys3pll_a7", 0, 0, 0, SIRFSOC_CLKC_SDPHY45_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_SDPHY45_CLKDIV_ENA, 12, &sdphy45_div_lock }, + { "sys0pll_qa8", "sys0pll_fixdiv", "sys0pll_a8", 0, 0, 0, SIRFSOC_CLKC_SDPHY67_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_SDPHY67_CLKDIV_ENA, 0, &sdphy67_div_lock }, + { "sys1pll_qa8", "sys1pll_fixdiv", "sys1pll_a8", 0, 0, 0, SIRFSOC_CLKC_SDPHY67_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_SDPHY67_CLKDIV_ENA, 4, &sdphy67_div_lock }, + { "sys2pll_qa8", "sys2pll_fixdiv", "sys2pll_a8", 0, 0, 0, SIRFSOC_CLKC_SDPHY67_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_SDPHY67_CLKDIV_ENA, 8, &sdphy67_div_lock }, + { "sys3pll_qa8", "sys3pll_fixdiv", "sys3pll_a8", 0, 0, 0, SIRFSOC_CLKC_SDPHY67_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_SDPHY67_CLKDIV_ENA, 12, &sdphy67_div_lock }, + { "sys0pll_qa9", "sys0pll_fixdiv", "sys0pll_a9", 0, 0, 0, SIRFSOC_CLKC_CAN_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_CAN_CLKDIV_ENA, 0, &can_div_lock }, + { "sys1pll_qa9", "sys1pll_fixdiv", "sys1pll_a9", 0, 0, 0, SIRFSOC_CLKC_CAN_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_CAN_CLKDIV_ENA, 4, &can_div_lock }, + { "sys2pll_qa9", "sys2pll_fixdiv", "sys2pll_a9", 0, 0, 0, SIRFSOC_CLKC_CAN_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_CAN_CLKDIV_ENA, 8, &can_div_lock }, + { "sys3pll_qa9", "sys3pll_fixdiv", "sys3pll_a9", 0, 0, 0, SIRFSOC_CLKC_CAN_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_CAN_CLKDIV_ENA, 12, &can_div_lock }, + { "sys0pll_qa10", "sys0pll_fixdiv", "sys0pll_a10", 0, 0, 0, SIRFSOC_CLKC_DEINT_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_DEINT_CLKDIV_ENA, 0, &deint_div_lock }, + { "sys1pll_qa10", "sys1pll_fixdiv", "sys1pll_a10", 0, 0, 0, SIRFSOC_CLKC_DEINT_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_DEINT_CLKDIV_ENA, 4, &deint_div_lock }, + { "sys2pll_qa10", "sys2pll_fixdiv", "sys2pll_a10", 0, 0, 0, SIRFSOC_CLKC_DEINT_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_DEINT_CLKDIV_ENA, 8, &deint_div_lock }, + { "sys3pll_qa10", "sys3pll_fixdiv", "sys3pll_a10", 0, 0, 0, SIRFSOC_CLKC_DEINT_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_DEINT_CLKDIV_ENA, 12, &deint_div_lock }, + { "sys0pll_qa11", "sys0pll_fixdiv", "sys0pll_a11", 0, 0, 0, SIRFSOC_CLKC_NAND_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_NAND_CLKDIV_ENA, 0, &nand_div_lock }, + { "sys1pll_qa11", "sys1pll_fixdiv", "sys1pll_a11", 0, 0, 0, SIRFSOC_CLKC_NAND_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_NAND_CLKDIV_ENA, 4, &nand_div_lock }, + { "sys2pll_qa11", "sys2pll_fixdiv", "sys2pll_a11", 0, 0, 0, SIRFSOC_CLKC_NAND_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_NAND_CLKDIV_ENA, 8, &nand_div_lock }, + { "sys3pll_qa11", "sys3pll_fixdiv", "sys3pll_a11", 0, 0, 0, SIRFSOC_CLKC_NAND_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_NAND_CLKDIV_ENA, 12, &nand_div_lock }, + { "sys0pll_qa12", "sys0pll_fixdiv", "sys0pll_a12", 0, 0, 0, SIRFSOC_CLKC_DISP0_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_DISP0_CLKDIV_ENA, 0, &disp0_div_lock }, + { "sys1pll_qa12", "sys1pll_fixdiv", "sys1pll_a12", 0, 0, 0, SIRFSOC_CLKC_DISP0_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_DISP0_CLKDIV_ENA, 4, &disp0_div_lock }, + { "sys2pll_qa12", "sys2pll_fixdiv", "sys2pll_a12", 0, 0, 0, SIRFSOC_CLKC_DISP0_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_DISP0_CLKDIV_ENA, 8, &disp0_div_lock }, + { "sys3pll_qa12", "sys3pll_fixdiv", "sys3pll_a12", 0, 0, 0, SIRFSOC_CLKC_DISP0_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_DISP0_CLKDIV_ENA, 12, &disp0_div_lock }, + { "sys0pll_qa13", "sys0pll_fixdiv", "sys0pll_a13", 0, 0, 0, SIRFSOC_CLKC_DISP1_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_DISP1_CLKDIV_ENA, 0, &disp1_div_lock }, + { "sys1pll_qa13", "sys1pll_fixdiv", "sys1pll_a13", 0, 0, 0, SIRFSOC_CLKC_DISP1_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_DISP1_CLKDIV_ENA, 4, &disp1_div_lock }, + { "sys2pll_qa13", "sys2pll_fixdiv", "sys2pll_a13", 0, 0, 0, SIRFSOC_CLKC_DISP1_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_DISP1_CLKDIV_ENA, 8, &disp1_div_lock }, + { "sys3pll_qa13", "sys3pll_fixdiv", "sys3pll_a13", 0, 0, 0, SIRFSOC_CLKC_DISP1_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_DISP1_CLKDIV_ENA, 12, &disp1_div_lock }, + { "sys0pll_qa14", "sys0pll_fixdiv", "sys0pll_a14", 0, 0, 0, SIRFSOC_CLKC_GPU_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_GPU_CLKDIV_ENA, 0, &gpu_div_lock }, + { "sys1pll_qa14", "sys1pll_fixdiv", "sys1pll_a14", 0, 0, 0, SIRFSOC_CLKC_GPU_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_GPU_CLKDIV_ENA, 4, &gpu_div_lock }, + { "sys2pll_qa14", "sys2pll_fixdiv", "sys2pll_a14", 0, 0, 0, SIRFSOC_CLKC_GPU_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_GPU_CLKDIV_ENA, 8, &gpu_div_lock }, + { "sys3pll_qa14", "sys3pll_fixdiv", "sys3pll_a14", 0, 0, 0, SIRFSOC_CLKC_GPU_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_GPU_CLKDIV_ENA, 12, &gpu_div_lock }, + { "sys0pll_qa15", "sys0pll_fixdiv", "sys0pll_a15", 0, 0, 0, SIRFSOC_CLKC_GNSS_CLKDIV_CFG, 0, 6, SIRFSOC_CLKC_GNSS_CLKDIV_ENA, 0, &gnss_div_lock }, + { "sys1pll_qa15", "sys1pll_fixdiv", "sys1pll_a15", 0, 0, 0, SIRFSOC_CLKC_GNSS_CLKDIV_CFG, 8, 6, SIRFSOC_CLKC_GNSS_CLKDIV_ENA, 4, &gnss_div_lock }, + { "sys2pll_qa15", "sys2pll_fixdiv", "sys2pll_a15", 0, 0, 0, SIRFSOC_CLKC_GNSS_CLKDIV_CFG, 16, 6, SIRFSOC_CLKC_GNSS_CLKDIV_ENA, 8, &gnss_div_lock }, + { "sys3pll_qa15", "sys3pll_fixdiv", "sys3pll_a15", 0, 0, 0, SIRFSOC_CLKC_GNSS_CLKDIV_CFG, 24, 6, SIRFSOC_CLKC_GNSS_CLKDIV_ENA, 12, &gnss_div_lock }, + { "sys1pll_qa18", "sys1pll_fixdiv", "sys1pll_a18", 0, 0, 0, SIRFSOC_CLKC_SHARED_DIVIDER_CFG0, 24, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 12, &share_div_lock }, + { "sys1pll_qa19", "sys1pll_fixdiv", "sys1pll_a19", 0, 0, CLK_IGNORE_UNUSED, SIRFSOC_CLKC_SHARED_DIVIDER_CFG0, 16, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 8, &share_div_lock }, + { "sys1pll_qa20", "sys1pll_fixdiv", "sys1pll_a20", 0, 0, 0, SIRFSOC_CLKC_SHARED_DIVIDER_CFG0, 8, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 4, &share_div_lock }, + { "sys2pll_qa20", "sys2pll_fixdiv", "sys2pll_a20", 0, 0, 0, SIRFSOC_CLKC_SHARED_DIVIDER_CFG0, 0, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 0, &share_div_lock }, + { "sys1pll_qa17", "sys1pll_fixdiv", "sys1pll_a17", 0, 0, CLK_IGNORE_UNUSED, SIRFSOC_CLKC_SHARED_DIVIDER_CFG1, 8, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 20, &share_div_lock }, + { "sys0pll_qa20", "sys0pll_fixdiv", "sys0pll_a20", 0, 0, 0, SIRFSOC_CLKC_SHARED_DIVIDER_CFG1, 0, 6, SIRFSOC_CLKC_SHARED_DIVIDER_ENA, 16, &share_div_lock }, +}; + +static const char * const i2s_clk_parents[] = { + "xin", + "xinw", + "audio_dto", + /* "pwm_i2s01" */ +}; + +static const char * const usbphy_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a1", + "sys1pll_a1", + "sys2pll_a1", + "sys3pll_a1", +}; + +static const char * const btss_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a2", + "sys1pll_a2", + "sys2pll_a2", + "sys3pll_a2", +}; + +static const char * const rgmii_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a3", + "sys1pll_a3", + "sys2pll_a3", + "sys3pll_a3", +}; + +static const char * const cpu_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a4", + "sys1pll_a4", + "cpupll_clk1", +}; + +static const char * const sdphy01_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a5", + "sys1pll_a5", + "sys2pll_a5", + "sys3pll_a5", +}; + +static const char * const sdphy23_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a6", + "sys1pll_a6", + "sys2pll_a6", + "sys3pll_a6", +}; + +static const char * const sdphy45_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a7", + "sys1pll_a7", + "sys2pll_a7", + "sys3pll_a7", +}; + +static const char * const sdphy67_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a8", + "sys1pll_a8", + "sys2pll_a8", + "sys3pll_a8", +}; + +static const char * const can_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a9", + "sys1pll_a9", + "sys2pll_a9", + "sys3pll_a9", +}; + +static const char * const deint_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a10", + "sys1pll_a10", + "sys2pll_a10", + "sys3pll_a10", +}; + +static const char * const nand_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a11", + "sys1pll_a11", + "sys2pll_a11", + "sys3pll_a11", +}; + +static const char * const disp0_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a12", + "sys1pll_a12", + "sys2pll_a12", + "sys3pll_a12", + "disp0_dto", +}; + +static const char * const disp1_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a13", + "sys1pll_a13", + "sys2pll_a13", + "sys3pll_a13", + "disp1_dto", +}; + +static const char * const gpu_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a14", + "sys1pll_a14", + "sys2pll_a14", + "sys3pll_a14", +}; + +static const char * const gnss_clk_parents[] = { + "xin", + "xinw", + "sys0pll_a15", + "sys1pll_a15", + "sys2pll_a15", + "sys3pll_a15", +}; + +static const char * const sys_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const io_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const g2d_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const jpenc_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const vdec_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const gmac_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const usb_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const kas_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const sec_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const sdr_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const vip_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const nocd_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const nocr_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static const char * const tpiu_clk_parents[] = { + "xin", + "xinw", + "sys2pll_a20", + "sys1pll_a20", + "sys1pll_a19", + "sys1pll_a18", + "sys0pll_a20", + "sys1pll_a17", +}; + +static struct atlas7_mux_init_data mux_list[] __initdata = { + /* mux_name, parent_names, parent_num, flags, mux_flags, mux_offset, shift, width */ + { "i2s_mux", i2s_clk_parents, ARRAY_SIZE(i2s_clk_parents), 0, 0, SIRFSOC_CLKC_I2S_CLK_SEL, 0, 2 }, + { "usbphy_mux", usbphy_clk_parents, ARRAY_SIZE(usbphy_clk_parents), 0, 0, SIRFSOC_CLKC_I2S_CLK_SEL, 0, 3 }, + { "btss_mux", btss_clk_parents, ARRAY_SIZE(btss_clk_parents), 0, 0, SIRFSOC_CLKC_BTSS_CLK_SEL, 0, 3 }, + { "rgmii_mux", rgmii_clk_parents, ARRAY_SIZE(rgmii_clk_parents), 0, 0, SIRFSOC_CLKC_RGMII_CLK_SEL, 0, 3 }, + { "cpu_mux", cpu_clk_parents, ARRAY_SIZE(cpu_clk_parents), 0, 0, SIRFSOC_CLKC_CPU_CLK_SEL, 0, 3 }, + { "sdphy01_mux", sdphy01_clk_parents, ARRAY_SIZE(sdphy01_clk_parents), 0, 0, SIRFSOC_CLKC_SDPHY01_CLK_SEL, 0, 3 }, + { "sdphy23_mux", sdphy23_clk_parents, ARRAY_SIZE(sdphy23_clk_parents), 0, 0, SIRFSOC_CLKC_SDPHY23_CLK_SEL, 0, 3 }, + { "sdphy45_mux", sdphy45_clk_parents, ARRAY_SIZE(sdphy45_clk_parents), 0, 0, SIRFSOC_CLKC_SDPHY45_CLK_SEL, 0, 3 }, + { "sdphy67_mux", sdphy67_clk_parents, ARRAY_SIZE(sdphy67_clk_parents), 0, 0, SIRFSOC_CLKC_SDPHY67_CLK_SEL, 0, 3 }, + { "can_mux", can_clk_parents, ARRAY_SIZE(can_clk_parents), 0, 0, SIRFSOC_CLKC_CAN_CLK_SEL, 0, 3 }, + { "deint_mux", deint_clk_parents, ARRAY_SIZE(deint_clk_parents), 0, 0, SIRFSOC_CLKC_DEINT_CLK_SEL, 0, 3 }, + { "nand_mux", nand_clk_parents, ARRAY_SIZE(nand_clk_parents), 0, 0, SIRFSOC_CLKC_NAND_CLK_SEL, 0, 3 }, + { "disp0_mux", disp0_clk_parents, ARRAY_SIZE(disp0_clk_parents), 0, 0, SIRFSOC_CLKC_DISP0_CLK_SEL, 0, 3 }, + { "disp1_mux", disp1_clk_parents, ARRAY_SIZE(disp1_clk_parents), 0, 0, SIRFSOC_CLKC_DISP1_CLK_SEL, 0, 3 }, + { "gpu_mux", gpu_clk_parents, ARRAY_SIZE(gpu_clk_parents), 0, 0, SIRFSOC_CLKC_GPU_CLK_SEL, 0, 3 }, + { "gnss_mux", gnss_clk_parents, ARRAY_SIZE(gnss_clk_parents), 0, 0, SIRFSOC_CLKC_GNSS_CLK_SEL, 0, 3 }, + { "sys_mux", sys_clk_parents, ARRAY_SIZE(sys_clk_parents), 0, 0, SIRFSOC_CLKC_SYS_CLK_SEL, 0, 3 }, + { "io_mux", io_clk_parents, ARRAY_SIZE(io_clk_parents), 0, 0, SIRFSOC_CLKC_IO_CLK_SEL, 0, 3 }, + { "g2d_mux", g2d_clk_parents, ARRAY_SIZE(g2d_clk_parents), 0, 0, SIRFSOC_CLKC_G2D_CLK_SEL, 0, 3 }, + { "jpenc_mux", jpenc_clk_parents, ARRAY_SIZE(jpenc_clk_parents), 0, 0, SIRFSOC_CLKC_JPENC_CLK_SEL, 0, 3 }, + { "vdec_mux", vdec_clk_parents, ARRAY_SIZE(vdec_clk_parents), 0, 0, SIRFSOC_CLKC_VDEC_CLK_SEL, 0, 3 }, + { "gmac_mux", gmac_clk_parents, ARRAY_SIZE(gmac_clk_parents), 0, 0, SIRFSOC_CLKC_GMAC_CLK_SEL, 0, 3 }, + { "usb_mux", usb_clk_parents, ARRAY_SIZE(usb_clk_parents), 0, 0, SIRFSOC_CLKC_USB_CLK_SEL, 0, 3 }, + { "kas_mux", kas_clk_parents, ARRAY_SIZE(kas_clk_parents), 0, 0, SIRFSOC_CLKC_KAS_CLK_SEL, 0, 3 }, + { "sec_mux", sec_clk_parents, ARRAY_SIZE(sec_clk_parents), 0, 0, SIRFSOC_CLKC_SEC_CLK_SEL, 0, 3 }, + { "sdr_mux", sdr_clk_parents, ARRAY_SIZE(sdr_clk_parents), 0, 0, SIRFSOC_CLKC_SDR_CLK_SEL, 0, 3 }, + { "vip_mux", vip_clk_parents, ARRAY_SIZE(vip_clk_parents), 0, 0, SIRFSOC_CLKC_VIP_CLK_SEL, 0, 3 }, + { "nocd_mux", nocd_clk_parents, ARRAY_SIZE(nocd_clk_parents), 0, 0, SIRFSOC_CLKC_NOCD_CLK_SEL, 0, 3 }, + { "nocr_mux", nocr_clk_parents, ARRAY_SIZE(nocr_clk_parents), 0, 0, SIRFSOC_CLKC_NOCR_CLK_SEL, 0, 3 }, + { "tpiu_mux", tpiu_clk_parents, ARRAY_SIZE(tpiu_clk_parents), 0, 0, SIRFSOC_CLKC_TPIU_CLK_SEL, 0, 3 }, +}; + + /* new unit should add start from the tail of list */ +static struct atlas7_unit_init_data unit_list[] __initdata = { + /* unit_name, parent_name, flags, regofs, bit, lock */ + { 0, "audmscm_kas", "kas_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 0, &root0_gate_lock }, + { 1, "gnssm_gnss", "gnss_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 1, &root0_gate_lock }, + { 2, "gpum_gpu", "gpu_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 2, &root0_gate_lock }, + { 3, "mediam_g2d", "g2d_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 3, &root0_gate_lock }, + { 4, "mediam_jpenc", "jpenc_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 4, &root0_gate_lock }, + { 5, "vdifm_disp0", "disp0_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 5, &root0_gate_lock }, + { 6, "vdifm_disp1", "disp1_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 6, &root0_gate_lock }, + { 7, "audmscm_i2s", "i2s_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 8, &root0_gate_lock }, + { 8, "audmscm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 11, &root0_gate_lock }, + { 9, "vdifm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 12, &root0_gate_lock }, + { 10, "gnssm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 13, &root0_gate_lock }, + { 11, "mediam_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 14, &root0_gate_lock }, + { 12, "btm_io", "io_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 17, &root0_gate_lock }, + { 13, "mediam_sdphy01", "sdphy01_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 18, &root0_gate_lock }, + { 14, "vdifm_sdphy23", "sdphy23_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 19, &root0_gate_lock }, + { 15, "vdifm_sdphy45", "sdphy45_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 20, &root0_gate_lock }, + { 16, "vdifm_sdphy67", "sdphy67_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 21, &root0_gate_lock }, + { 17, "audmscm_xin", "xin", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 22, &root0_gate_lock }, + { 18, "mediam_nand", "nand_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 27, &root0_gate_lock }, + { 19, "gnssm_sec", "sec_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 28, &root0_gate_lock }, + { 20, "cpum_cpu", "cpu_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 29, &root0_gate_lock }, + { 21, "gnssm_xin", "xin", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 30, &root0_gate_lock }, + { 22, "vdifm_vip", "vip_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN0_SET, 31, &root0_gate_lock }, + { 23, "btm_btss", "btss_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 0, &root1_gate_lock }, + { 24, "mediam_usbphy", "usbphy_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 1, &root1_gate_lock }, + { 25, "rtcm_kas", "kas_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 2, &root1_gate_lock }, + { 26, "audmscm_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 3, &root1_gate_lock }, + { 27, "vdifm_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 4, &root1_gate_lock }, + { 28, "gnssm_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 5, &root1_gate_lock }, + { 29, "mediam_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 6, &root1_gate_lock }, + { 30, "cpum_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 8, &root1_gate_lock }, + { 31, "gpum_nocd", "nocd_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 9, &root1_gate_lock }, + { 32, "audmscm_nocr", "nocr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 11, &root1_gate_lock }, + { 33, "vdifm_nocr", "nocr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 12, &root1_gate_lock }, + { 34, "gnssm_nocr", "nocr_mux", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 13, &root1_gate_lock }, + { 35, "mediam_nocr", "nocr_mux", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 14, &root1_gate_lock }, + { 36, "ddrm_nocr", "nocr_mux", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 15, &root1_gate_lock }, + { 37, "cpum_tpiu", "tpiu_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 16, &root1_gate_lock }, + { 38, "gpum_nocr", "nocr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 17, &root1_gate_lock }, + { 39, "gnssm_rgmii", "rgmii_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 20, &root1_gate_lock }, + { 40, "mediam_vdec", "vdec_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 21, &root1_gate_lock }, + { 41, "gpum_sdr", "sdr_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 22, &root1_gate_lock }, + { 42, "vdifm_deint", "deint_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 23, &root1_gate_lock }, + { 43, "gnssm_can", "can_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 26, &root1_gate_lock }, + { 44, "mediam_usb", "usb_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 28, &root1_gate_lock }, + { 45, "gnssm_gmac", "gmac_mux", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 29, &root1_gate_lock }, + { 46, "cvd_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 0, &leaf1_gate_lock }, + { 47, "timer_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 1, &leaf1_gate_lock }, + { 48, "pulse_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 2, &leaf1_gate_lock }, + { 49, "tsc_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 3, &leaf1_gate_lock }, + { 50, "tsc_xin", "audmscm_xin", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 21, &leaf1_gate_lock }, + { 51, "ioctop_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 4, &leaf1_gate_lock }, + { 52, "rsc_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 5, &leaf1_gate_lock }, + { 53, "dvm_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 6, &leaf1_gate_lock }, + { 54, "lvds_xin", "audmscm_xin", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 7, &leaf1_gate_lock }, + { 55, "kas_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 8, &leaf1_gate_lock }, + { 56, "ac97_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 9, &leaf1_gate_lock }, + { 57, "usp0_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 10, &leaf1_gate_lock }, + { 58, "usp1_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 11, &leaf1_gate_lock }, + { 59, "usp2_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 12, &leaf1_gate_lock }, + { 60, "dmac2_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 13, &leaf1_gate_lock }, + { 61, "dmac3_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 14, &leaf1_gate_lock }, + { 62, "audioif_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 15, &leaf1_gate_lock }, + { 63, "i2s1_kas", "audmscm_kas", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 17, &leaf1_gate_lock }, + { 64, "thaudmscm_io", "audmscm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 22, &leaf1_gate_lock }, + { 65, "analogtest_xin", "audmscm_xin", 0, SIRFSOC_CLKC_LEAF_CLK_EN1_SET, 23, &leaf1_gate_lock }, + { 66, "sys2pci_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 0, &leaf2_gate_lock }, + { 67, "pciarb_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 1, &leaf2_gate_lock }, + { 68, "pcicopy_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 2, &leaf2_gate_lock }, + { 69, "rom_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 3, &leaf2_gate_lock }, + { 70, "sdio23_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 4, &leaf2_gate_lock }, + { 71, "sdio45_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 5, &leaf2_gate_lock }, + { 72, "sdio67_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 6, &leaf2_gate_lock }, + { 73, "vip1_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 7, &leaf2_gate_lock }, + { 74, "vip1_vip", "vdifm_vip", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 16, &leaf2_gate_lock }, + { 75, "sdio23_sdphy23", "vdifm_sdphy23", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 8, &leaf2_gate_lock }, + { 76, "sdio45_sdphy45", "vdifm_sdphy45", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 9, &leaf2_gate_lock }, + { 77, "sdio67_sdphy67", "vdifm_sdphy67", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 10, &leaf2_gate_lock }, + { 78, "vpp0_disp0", "vdifm_disp0", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 11, &leaf2_gate_lock }, + { 79, "lcd0_disp0", "vdifm_disp0", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 12, &leaf2_gate_lock }, + { 80, "vpp1_disp1", "vdifm_disp1", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 13, &leaf2_gate_lock }, + { 81, "lcd1_disp1", "vdifm_disp1", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 14, &leaf2_gate_lock }, + { 82, "dcu_deint", "vdifm_deint", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 15, &leaf2_gate_lock }, + { 83, "vdifm_dapa_r_nocr", "vdifm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 17, &leaf2_gate_lock }, + { 84, "gpio1_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 18, &leaf2_gate_lock }, + { 85, "thvdifm_io", "vdifm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN2_SET, 19, &leaf2_gate_lock }, + { 86, "gmac_rgmii", "gnssm_rgmii", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 0, &leaf3_gate_lock }, + { 87, "gmac_gmac", "gnssm_gmac", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 1, &leaf3_gate_lock }, + { 88, "uart1_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 2, &leaf3_gate_lock }, + { 89, "dmac0_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 3, &leaf3_gate_lock }, + { 90, "uart0_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 4, &leaf3_gate_lock }, + { 91, "uart2_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 5, &leaf3_gate_lock }, + { 92, "uart3_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 6, &leaf3_gate_lock }, + { 93, "uart4_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 7, &leaf3_gate_lock }, + { 94, "uart5_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 8, &leaf3_gate_lock }, + { 95, "spi1_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 9, &leaf3_gate_lock }, + { 96, "gnss_gnss", "gnssm_gnss", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 10, &leaf3_gate_lock }, + { 97, "canbus1_can", "gnssm_can", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 12, &leaf3_gate_lock }, + { 98, "ccsec_sec", "gnssm_sec", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 15, &leaf3_gate_lock }, + { 99, "ccpub_sec", "gnssm_sec", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 16, &leaf3_gate_lock }, + { 100, "gnssm_dapa_r_nocr", "gnssm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 13, &leaf3_gate_lock }, + { 101, "thgnssm_io", "gnssm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN3_SET, 14, &leaf3_gate_lock }, + { 102, "media_vdec", "mediam_vdec", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 0, &leaf4_gate_lock }, + { 103, "media_jpenc", "mediam_jpenc", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 1, &leaf4_gate_lock }, + { 104, "g2d_g2d", "mediam_g2d", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 2, &leaf4_gate_lock }, + { 105, "i2c0_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 3, &leaf4_gate_lock }, + { 106, "i2c1_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 4, &leaf4_gate_lock }, + { 107, "gpio0_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 5, &leaf4_gate_lock }, + { 108, "nand_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 6, &leaf4_gate_lock }, + { 109, "sdio01_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 7, &leaf4_gate_lock }, + { 110, "sys2pci2_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 8, &leaf4_gate_lock }, + { 111, "sdio01_sdphy01", "mediam_sdphy01", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 9, &leaf4_gate_lock }, + { 112, "nand_nand", "mediam_nand", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 10, &leaf4_gate_lock }, + { 113, "usb0_usb", "mediam_usb", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 11, &leaf4_gate_lock }, + { 114, "usb1_usb", "mediam_usb", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 12, &leaf4_gate_lock }, + { 115, "usbphy0_usbphy", "mediam_usbphy", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 13, &leaf4_gate_lock }, + { 116, "usbphy1_usbphy", "mediam_usbphy", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 14, &leaf4_gate_lock }, + { 117, "thmediam_io", "mediam_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN4_SET, 15, &leaf4_gate_lock }, + { 118, "memc_mem", "mempll_clk1", CLK_IGNORE_UNUSED, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 0, &leaf5_gate_lock }, + { 119, "dapa_mem", "mempll_clk1", 0, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 1, &leaf5_gate_lock }, + { 120, "nocddrm_nocr", "ddrm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 2, &leaf5_gate_lock }, + { 121, "thddrm_nocr", "ddrm_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN5_SET, 3, &leaf5_gate_lock }, + { 122, "spram1_cpudiv2", "cpum_cpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 0, &leaf6_gate_lock }, + { 123, "spram2_cpudiv2", "cpum_cpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 1, &leaf6_gate_lock }, + { 124, "coresight_cpudiv2", "cpum_cpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 2, &leaf6_gate_lock }, + { 125, "thcpum_cpudiv4", "cpum_cpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN6_SET, 3, &leaf6_gate_lock }, + { 126, "graphic_gpu", "gpum_gpu", 0, SIRFSOC_CLKC_LEAF_CLK_EN7_SET, 0, &leaf7_gate_lock }, + { 127, "vss_sdr", "gpum_sdr", 0, SIRFSOC_CLKC_LEAF_CLK_EN7_SET, 1, &leaf7_gate_lock }, + { 128, "thgpum_nocr", "gpum_nocr", 0, SIRFSOC_CLKC_LEAF_CLK_EN7_SET, 2, &leaf7_gate_lock }, + { 129, "a7ca_btss", "btm_btss", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 1, &leaf8_gate_lock }, + { 130, "dmac4_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 2, &leaf8_gate_lock }, + { 131, "uart6_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 3, &leaf8_gate_lock }, + { 132, "usp3_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 4, &leaf8_gate_lock }, + { 133, "a7ca_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 5, &leaf8_gate_lock }, + { 134, "noc_btm_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 6, &leaf8_gate_lock }, + { 135, "thbtm_io", "btm_io", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 7, &leaf8_gate_lock }, + { 136, "btslow", "xinw_fixdiv_btslow", 0, SIRFSOC_CLKC_ROOT_CLK_EN1_SET, 25, &root1_gate_lock }, + { 137, "a7ca_btslow", "btslow", 0, SIRFSOC_CLKC_LEAF_CLK_EN8_SET, 0, &leaf8_gate_lock }, +}; + +static struct clk *atlas7_clks[ARRAY_SIZE(unit_list)]; + +static int unit_clk_is_enabled(struct clk_hw *hw) +{ + struct clk_unit *clk = to_unitclk(hw); + u32 reg; + + reg = clk->regofs + SIRFSOC_CLKC_ROOT_CLK_EN0_STAT - SIRFSOC_CLKC_ROOT_CLK_EN0_SET; + + return !!(clkc_readl(reg) & BIT(clk->bit)); +} + +static int unit_clk_enable(struct clk_hw *hw) +{ + u32 reg; + struct clk_unit *clk = to_unitclk(hw); + unsigned long flags; + + reg = clk->regofs; + + spin_lock_irqsave(clk->lock, flags); + clkc_writel(BIT(clk->bit), reg); + spin_unlock_irqrestore(clk->lock, flags); + return 0; +} + +static void unit_clk_disable(struct clk_hw *hw) +{ + u32 reg; + struct clk_unit *clk = to_unitclk(hw); + unsigned long flags; + + reg = clk->regofs + SIRFSOC_CLKC_ROOT_CLK_EN0_CLR - SIRFSOC_CLKC_ROOT_CLK_EN0_SET; + + spin_lock_irqsave(clk->lock, flags); + clkc_writel(BIT(clk->bit), reg); + spin_unlock_irqrestore(clk->lock, flags); +} + +static const struct clk_ops unit_clk_ops = { + .is_enabled = unit_clk_is_enabled, + .enable = unit_clk_enable, + .disable = unit_clk_disable, +}; + +static struct clk * __init +atlas7_unit_clk_register(struct device *dev, const char *name, + const char * const parent_name, unsigned long flags, + u32 regofs, u8 bit, spinlock_t *lock) +{ + struct clk *clk; + struct clk_unit *unit; + struct clk_init_data init; + + unit = kzalloc(sizeof(*unit), GFP_KERNEL); + if (!unit) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.parent_names = &parent_name; + init.num_parents = 1; + init.ops = &unit_clk_ops; + init.flags = flags; + + unit->hw.init = &init; + unit->regofs = regofs; + unit->bit = bit; + unit->lock = lock; + + clk = clk_register(dev, &unit->hw); + if (IS_ERR(clk)) + kfree(unit); + + return clk; +} + +static struct atlas7_reset_desc atlas7_reset_unit[] = { + { "PWM", 0x0244, 0, 0x0320, 0, &leaf0_gate_lock }, /* 0-5 */ + { "THCGUM", 0x0244, 3, 0x0320, 1, &leaf0_gate_lock }, + { "CVD", 0x04A0, 0, 0x032C, 0, &leaf1_gate_lock }, + { "TIMER", 0x04A0, 1, 0x032C, 1, &leaf1_gate_lock }, + { "PULSEC", 0x04A0, 2, 0x032C, 2, &leaf1_gate_lock }, + { "TSC", 0x04A0, 3, 0x032C, 3, &leaf1_gate_lock }, + { "IOCTOP", 0x04A0, 4, 0x032C, 4, &leaf1_gate_lock }, /* 6-10 */ + { "RSC", 0x04A0, 5, 0x032C, 5, &leaf1_gate_lock }, + { "DVM", 0x04A0, 6, 0x032C, 6, &leaf1_gate_lock }, + { "LVDS", 0x04A0, 7, 0x032C, 7, &leaf1_gate_lock }, + { "KAS", 0x04A0, 8, 0x032C, 8, &leaf1_gate_lock }, + { "AC97", 0x04A0, 9, 0x032C, 9, &leaf1_gate_lock }, /* 11-15 */ + { "USP0", 0x04A0, 10, 0x032C, 10, &leaf1_gate_lock }, + { "USP1", 0x04A0, 11, 0x032C, 11, &leaf1_gate_lock }, + { "USP2", 0x04A0, 12, 0x032C, 12, &leaf1_gate_lock }, + { "DMAC2", 0x04A0, 13, 0x032C, 13, &leaf1_gate_lock }, + { "DMAC3", 0x04A0, 14, 0x032C, 14, &leaf1_gate_lock }, /* 16-20 */ + { "AUDIO", 0x04A0, 15, 0x032C, 15, &leaf1_gate_lock }, + { "I2S1", 0x04A0, 17, 0x032C, 16, &leaf1_gate_lock }, + { "PMU_AUDIO", 0x04A0, 22, 0x032C, 17, &leaf1_gate_lock }, + { "THAUDMSCM", 0x04A0, 23, 0x032C, 18, &leaf1_gate_lock }, + { "SYS2PCI", 0x04B8, 0, 0x0338, 0, &leaf2_gate_lock }, /* 21-25 */ + { "PCIARB", 0x04B8, 1, 0x0338, 1, &leaf2_gate_lock }, + { "PCICOPY", 0x04B8, 2, 0x0338, 2, &leaf2_gate_lock }, + { "ROM", 0x04B8, 3, 0x0338, 3, &leaf2_gate_lock }, + { "SDIO23", 0x04B8, 4, 0x0338, 4, &leaf2_gate_lock }, + { "SDIO45", 0x04B8, 5, 0x0338, 5, &leaf2_gate_lock }, /* 26-30 */ + { "SDIO67", 0x04B8, 6, 0x0338, 6, &leaf2_gate_lock }, + { "VIP1", 0x04B8, 7, 0x0338, 7, &leaf2_gate_lock }, + { "VPP0", 0x04B8, 11, 0x0338, 8, &leaf2_gate_lock }, + { "LCD0", 0x04B8, 12, 0x0338, 9, &leaf2_gate_lock }, + { "VPP1", 0x04B8, 13, 0x0338, 10, &leaf2_gate_lock }, /* 31-35 */ + { "LCD1", 0x04B8, 14, 0x0338, 11, &leaf2_gate_lock }, + { "DCU", 0x04B8, 15, 0x0338, 12, &leaf2_gate_lock }, + { "GPIO", 0x04B8, 18, 0x0338, 13, &leaf2_gate_lock }, + { "DAPA_VDIFM", 0x04B8, 17, 0x0338, 15, &leaf2_gate_lock }, + { "THVDIFM", 0x04B8, 19, 0x0338, 16, &leaf2_gate_lock }, /* 36-40 */ + { "RGMII", 0x04D0, 0, 0x0344, 0, &leaf3_gate_lock }, + { "GMAC", 0x04D0, 1, 0x0344, 1, &leaf3_gate_lock }, + { "UART1", 0x04D0, 2, 0x0344, 2, &leaf3_gate_lock }, + { "DMAC0", 0x04D0, 3, 0x0344, 3, &leaf3_gate_lock }, + { "UART0", 0x04D0, 4, 0x0344, 4, &leaf3_gate_lock }, /* 41-45 */ + { "UART2", 0x04D0, 5, 0x0344, 5, &leaf3_gate_lock }, + { "UART3", 0x04D0, 6, 0x0344, 6, &leaf3_gate_lock }, + { "UART4", 0x04D0, 7, 0x0344, 7, &leaf3_gate_lock }, + { "UART5", 0x04D0, 8, 0x0344, 8, &leaf3_gate_lock }, + { "SPI1", 0x04D0, 9, 0x0344, 9, &leaf3_gate_lock }, /* 46-50 */ + { "GNSS_SYS_M0", 0x04D0, 10, 0x0344, 10, &leaf3_gate_lock }, + { "CANBUS1", 0x04D0, 12, 0x0344, 11, &leaf3_gate_lock }, + { "CCSEC", 0x04D0, 15, 0x0344, 12, &leaf3_gate_lock }, + { "CCPUB", 0x04D0, 16, 0x0344, 13, &leaf3_gate_lock }, + { "DAPA_GNSSM", 0x04D0, 13, 0x0344, 14, &leaf3_gate_lock }, /* 51-55 */ + { "THGNSSM", 0x04D0, 14, 0x0344, 15, &leaf3_gate_lock }, + { "VDEC", 0x04E8, 0, 0x0350, 0, &leaf4_gate_lock }, + { "JPENC", 0x04E8, 1, 0x0350, 1, &leaf4_gate_lock }, + { "G2D", 0x04E8, 2, 0x0350, 2, &leaf4_gate_lock }, + { "I2C0", 0x04E8, 3, 0x0350, 3, &leaf4_gate_lock }, /* 56-60 */ + { "I2C1", 0x04E8, 4, 0x0350, 4, &leaf4_gate_lock }, + { "GPIO0", 0x04E8, 5, 0x0350, 5, &leaf4_gate_lock }, + { "NAND", 0x04E8, 6, 0x0350, 6, &leaf4_gate_lock }, + { "SDIO01", 0x04E8, 7, 0x0350, 7, &leaf4_gate_lock }, + { "SYS2PCI2", 0x04E8, 8, 0x0350, 8, &leaf4_gate_lock }, /* 61-65 */ + { "USB0", 0x04E8, 11, 0x0350, 9, &leaf4_gate_lock }, + { "USB1", 0x04E8, 12, 0x0350, 10, &leaf4_gate_lock }, + { "THMEDIAM", 0x04E8, 15, 0x0350, 11, &leaf4_gate_lock }, + { "MEMC_DDRPHY", 0x0500, 0, 0x035C, 0, &leaf5_gate_lock }, + { "MEMC_UPCTL", 0x0500, 0, 0x035C, 1, &leaf5_gate_lock }, /* 66-70 */ + { "DAPA_MEM", 0x0500, 1, 0x035C, 2, &leaf5_gate_lock }, + { "MEMC_MEMDIV", 0x0500, 0, 0x035C, 3, &leaf5_gate_lock }, + { "THDDRM", 0x0500, 3, 0x035C, 4, &leaf5_gate_lock }, + { "CORESIGHT", 0x0518, 3, 0x0368, 13, &leaf6_gate_lock }, + { "THCPUM", 0x0518, 4, 0x0368, 17, &leaf6_gate_lock }, /* 71-75 */ + { "GRAPHIC", 0x0530, 0, 0x0374, 0, &leaf7_gate_lock }, + { "VSS_SDR", 0x0530, 1, 0x0374, 1, &leaf7_gate_lock }, + { "THGPUM", 0x0530, 2, 0x0374, 2, &leaf7_gate_lock }, + { "DMAC4", 0x0548, 2, 0x0380, 1, &leaf8_gate_lock }, + { "UART6", 0x0548, 3, 0x0380, 2, &leaf8_gate_lock }, /* 76- */ + { "USP3", 0x0548, 4, 0x0380, 3, &leaf8_gate_lock }, + { "THBTM", 0x0548, 5, 0x0380, 5, &leaf8_gate_lock }, + { "A7CA", 0x0548, 1, 0x0380, 0, &leaf8_gate_lock }, + { "A7CA_APB", 0x0548, 5, 0x0380, 4, &leaf8_gate_lock }, +}; + +static int atlas7_reset_module(struct reset_controller_dev *rcdev, + unsigned long reset_idx) +{ + struct atlas7_reset_desc *reset = &atlas7_reset_unit[reset_idx]; + unsigned long flags; + + /* + * HW suggest unit reset sequence: + * assert sw reset (0) + * setting sw clk_en to if the clock was disabled before reset + * delay 16 clocks + * disable clock (sw clk_en = 0) + * de-assert reset (1) + * after this sequence, restore clock or not is decided by SW + */ + + spin_lock_irqsave(reset->lock, flags); + /* clock enable or not */ + if (clkc_readl(reset->clk_ofs + 8) & (1 << reset->clk_bit)) { + clkc_writel(1 << reset->rst_bit, reset->rst_ofs + 4); + udelay(2); + clkc_writel(1 << reset->clk_bit, reset->clk_ofs + 4); + clkc_writel(1 << reset->rst_bit, reset->rst_ofs); + /* restore clock enable */ + clkc_writel(1 << reset->clk_bit, reset->clk_ofs); + } else { + clkc_writel(1 << reset->rst_bit, reset->rst_ofs + 4); + clkc_writel(1 << reset->clk_bit, reset->clk_ofs); + udelay(2); + clkc_writel(1 << reset->clk_bit, reset->clk_ofs + 4); + clkc_writel(1 << reset->rst_bit, reset->rst_ofs); + } + spin_unlock_irqrestore(reset->lock, flags); + + return 0; +} + +static struct reset_control_ops atlas7_rst_ops = { + .reset = atlas7_reset_module, +}; + +static struct reset_controller_dev atlas7_rst_ctlr = { + .ops = &atlas7_rst_ops, + .owner = THIS_MODULE, + .of_reset_n_cells = 1, +}; + +static void __init atlas7_clk_init(struct device_node *np) +{ + struct clk *clk; + struct atlas7_div_init_data *div; + struct atlas7_mux_init_data *mux; + struct atlas7_unit_init_data *unit; + int i; + int ret; + + sirfsoc_clk_vbase = of_iomap(np, 0); + if (!sirfsoc_clk_vbase) + panic("unable to map clkc registers\n"); + + of_node_put(np); + + clk = clk_register(NULL, &clk_cpupll.hw); + BUG_ON(!clk); + clk = clk_register(NULL, &clk_mempll.hw); + BUG_ON(!clk); + clk = clk_register(NULL, &clk_sys0pll.hw); + BUG_ON(!clk); + clk = clk_register(NULL, &clk_sys1pll.hw); + BUG_ON(!clk); + clk = clk_register(NULL, &clk_sys2pll.hw); + BUG_ON(!clk); + clk = clk_register(NULL, &clk_sys3pll.hw); + BUG_ON(!clk); + + clk = clk_register_divider_table(NULL, "cpupll_div1", "cpupll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &cpupll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "cpupll_div2", "cpupll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &cpupll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "cpupll_div3", "cpupll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &cpupll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_divider_table(NULL, "mempll_div1", "mempll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &mempll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "mempll_div2", "mempll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &mempll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "mempll_div3", "mempll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &mempll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_divider_table(NULL, "sys0pll_div1", "sys0pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys0pll_div2", "sys0pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys0pll_div3", "sys0pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_fixed_factor(NULL, "sys0pll_fixdiv", "sys0pll_vco", + CLK_SET_RATE_PARENT, 1, 2); + + clk = clk_register_divider_table(NULL, "sys1pll_div1", "sys1pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys1pll_div2", "sys1pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys1pll_div3", "sys1pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_fixed_factor(NULL, "sys1pll_fixdiv", "sys1pll_vco", + CLK_SET_RATE_PARENT, 1, 2); + + clk = clk_register_divider_table(NULL, "sys2pll_div1", "sys2pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys2pll_div2", "sys2pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys2pll_div3", "sys2pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_fixed_factor(NULL, "sys2pll_fixdiv", "sys2pll_vco", + CLK_SET_RATE_PARENT, 1, 2); + + clk = clk_register_divider_table(NULL, "sys3pll_div1", "sys3pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, 0, 3, 0, + pll_div_table, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys3pll_div2", "sys3pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, 4, 3, 0, + pll_div_table, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_divider_table(NULL, "sys3pll_div3", "sys3pll_vco", 0, + sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, 8, 3, 0, + pll_div_table, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_fixed_factor(NULL, "sys3pll_fixdiv", "sys3pll_vco", + CLK_SET_RATE_PARENT, 1, 2); + + BUG_ON(!clk); + clk = clk_register_fixed_factor(NULL, "xinw_fixdiv_btslow", "xinw", + CLK_SET_RATE_PARENT, 1, 4); + + BUG_ON(!clk); + clk = clk_register_gate(NULL, "cpupll_clk1", "cpupll_div1", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, + 12, 0, &cpupll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "cpupll_clk2", "cpupll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, + 13, 0, &cpupll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "cpupll_clk3", "cpupll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_CPUPLL_AB_CTRL1, + 14, 0, &cpupll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_gate(NULL, "mempll_clk1", "mempll_div1", + CLK_SET_RATE_PARENT | CLK_IGNORE_UNUSED, + sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, + 12, 0, &mempll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "mempll_clk2", "mempll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, + 13, 0, &mempll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "mempll_clk3", "mempll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_MEMPLL_AB_CTRL1, + 14, 0, &mempll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_gate(NULL, "sys0pll_clk1", "sys0pll_div1", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, + 12, 0, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys0pll_clk2", "sys0pll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, + 13, 0, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys0pll_clk3", "sys0pll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS0PLL_AB_CTRL1, + 14, 0, &sys0pll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_gate(NULL, "sys1pll_clk1", "sys1pll_div1", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, + 12, 0, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys1pll_clk2", "sys1pll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, + 13, 0, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys1pll_clk3", "sys1pll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS1PLL_AB_CTRL1, + 14, 0, &sys1pll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_gate(NULL, "sys2pll_clk1", "sys2pll_div1", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, + 12, 0, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys2pll_clk2", "sys2pll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, + 13, 0, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys2pll_clk3", "sys2pll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS2PLL_AB_CTRL1, + 14, 0, &sys2pll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register_gate(NULL, "sys3pll_clk1", "sys3pll_div1", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, + 12, 0, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys3pll_clk2", "sys3pll_div2", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, + 13, 0, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, "sys3pll_clk3", "sys3pll_div3", + CLK_SET_RATE_PARENT, sirfsoc_clk_vbase + SIRFSOC_CLKC_SYS3PLL_AB_CTRL1, + 14, 0, &sys3pll_ctrl1_lock); + BUG_ON(!clk); + + clk = clk_register(NULL, &clk_audio_dto.hw); + BUG_ON(!clk); + + clk = clk_register(NULL, &clk_disp0_dto.hw); + BUG_ON(!clk); + + clk = clk_register(NULL, &clk_disp1_dto.hw); + BUG_ON(!clk); + + for (i = 0; i < ARRAY_SIZE(divider_list); i++) { + div = ÷r_list[i]; + clk = clk_register_divider(NULL, div->div_name, + div->parent_name, div->divider_flags, sirfsoc_clk_vbase + div->div_offset, + div->shift, div->width, 0, div->lock); + BUG_ON(!clk); + clk = clk_register_gate(NULL, div->gate_name, div->div_name, + div->gate_flags, sirfsoc_clk_vbase + div->gate_offset, + div->gate_bit, 0, div->lock); + BUG_ON(!clk); + } + /* ignore selector status register check */ + for (i = 0; i < ARRAY_SIZE(mux_list); i++) { + mux = &mux_list[i]; + clk = clk_register_mux(NULL, mux->mux_name, mux->parent_names, + mux->parent_num, mux->flags, + sirfsoc_clk_vbase + mux->mux_offset, + mux->shift, mux->width, + mux->mux_flags, NULL); + BUG_ON(!clk); + } + + for (i = 0; i < ARRAY_SIZE(unit_list); i++) { + unit = &unit_list[i]; + atlas7_clks[i] = atlas7_unit_clk_register(NULL, unit->unit_name, unit->parent_name, + unit->flags, unit->regofs, unit->bit, unit->lock); + BUG_ON(!atlas7_clks[i]); + } + + clk_data.clks = atlas7_clks; + clk_data.clk_num = ARRAY_SIZE(unit_list); + + ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + BUG_ON(ret); + + atlas7_rst_ctlr.of_node = np; + atlas7_rst_ctlr.nr_resets = ARRAY_SIZE(atlas7_reset_unit); + reset_controller_register(&atlas7_rst_ctlr); +} +CLK_OF_DECLARE(atlas7_clk, "sirf,atlas7-car", atlas7_clk_init); -- cgit v1.2.3-58-ga151 From 9c684e290d1741e068cc226418a26a39663db010 Mon Sep 17 00:00:00 2001 From: Guo Zeng Date: Wed, 20 May 2015 08:50:34 +0000 Subject: ARM: dts: atlas7: add fixed frequency clocks in car node This patch adds the fixed clocks of external crystal oscillators. Signed-off-by: Guo Zeng Signed-off-by: Barry Song [sboyd@codeaurora.org: Remove size-cells/address-cells] Signed-off-by: Stephen Boyd --- arch/arm/boot/dts/atlas7.dtsi | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/arm/boot/dts/atlas7.dtsi b/arch/arm/boot/dts/atlas7.dtsi index a753178abc85..5dfd3a44bf82 100644 --- a/arch/arm/boot/dts/atlas7.dtsi +++ b/arch/arm/boot/dts/atlas7.dtsi @@ -38,6 +38,21 @@ }; }; + clocks { + xinw { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + clock-output-names = "xinw"; + }; + xin { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <26000000>; + clock-output-names = "xin"; + }; + }; + noc { compatible = "simple-bus"; #address-cells = <1>; -- cgit v1.2.3-58-ga151 From a59a516398750dc3477a81df34e655b596e352b3 Mon Sep 17 00:00:00 2001 From: Shailendra Verma Date: Thu, 21 May 2015 00:06:48 +0530 Subject: clk: Fix typo in clk_register() comment Signed-off-by: Shailendra Verma Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index c44623fe9c48..a522dbbc01b9 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2442,7 +2442,7 @@ void __clk_free_clk(struct clk *clk) * * clk_register is the primary interface for populating the clock tree with new * clock nodes. It returns a pointer to the newly allocated struct clk which - * cannot be dereferenced by driver code but may be used in conjuction with the + * cannot be dereferenced by driver code but may be used in conjunction with the * rest of the clock API. In the event of an error clk_register will return an * error code; drivers must test for an error code after calling clk_register. */ -- cgit v1.2.3-58-ga151 From 8cdea5022dade063a10ffb0f768a18bfabab40e3 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 21 May 2015 22:26:13 +0800 Subject: clk: cdce706: Add missing of_clk_del_provider call in cdce706_remove Remove a previously registered clock provider when unload the module. Signed-off-by: Axel Lin Acked-by: Max Filippov Signed-off-by: Stephen Boyd --- drivers/clk/clk-cdce706.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/clk-cdce706.c b/drivers/clk/clk-cdce706.c index 8a2dfd0012f3..f01164fada5d 100644 --- a/drivers/clk/clk-cdce706.c +++ b/drivers/clk/clk-cdce706.c @@ -666,6 +666,7 @@ static int cdce706_probe(struct i2c_client *client, static int cdce706_remove(struct i2c_client *client) { + of_clk_del_provider(client->dev.of_node); return 0; } -- cgit v1.2.3-58-ga151 From c306317a0e285670d8cceab067d5aceff4593ec8 Mon Sep 17 00:00:00 2001 From: Shailendra Verma Date: Thu, 21 May 2015 23:26:03 +0530 Subject: clk: samsung: Fix typo in panic log Signed-off-by: Shailendra Verma Signed-off-by: Stephen Boyd --- drivers/clk/samsung/clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index 9e1f88c04fd4..76491629d1e5 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -389,7 +389,7 @@ struct samsung_clk_provider * __init samsung_cmu_register_one( ctx = samsung_clk_init(np, reg_base, cmu->nr_clk_ids); if (!ctx) { - panic("%s: unable to alllocate ctx\n", __func__); + panic("%s: unable to allocate ctx\n", __func__); return ctx; } -- cgit v1.2.3-58-ga151 From 004cbb475fb0164665d66c68e509a4af64f7f5f1 Mon Sep 17 00:00:00 2001 From: Shailendra Verma Date: Thu, 21 May 2015 23:32:56 +0530 Subject: clk: clk-conf: Fix typo in comment Signed-off-by: Shailendra Verma Signed-off-by: Stephen Boyd --- drivers/clk/clk-conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk-conf.c b/drivers/clk/clk-conf.c index 35a396bc9672..43a218f35b19 100644 --- a/drivers/clk/clk-conf.c +++ b/drivers/clk/clk-conf.c @@ -125,7 +125,7 @@ static int __set_clk_rates(struct device_node *node, bool clk_supplier) * and sets any specified clock parents and rates. The @clk_supplier argument * should be set to true if @node may be also a clock supplier of any clock * listed in its 'assigned-clocks' or 'assigned-clock-parents' properties. - * If @clk_supplier is false the function exits returnning 0 as soon as it + * If @clk_supplier is false the function exits returning 0 as soon as it * determines the @node is also a supplier of any of the clocks. */ int of_clk_set_defaults(struct device_node *node, bool clk_supplier) -- cgit v1.2.3-58-ga151 From 5611a5ba8e5435740df99235b262b553f687b13b Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 19 May 2015 22:22:41 -0500 Subject: clk: socfpga: update clk.h so for Arria10 platform to use There are 5 possible parent clocks for the SoCFPGA Arria10. Move the define SYSMGR_SDMMC_CTRL_SET and streq() to clk.h so that the Arria clock driver can use. Signed-off-by: Dinh Nguyen Signed-off-by: Stephen Boyd --- drivers/clk/socfpga/clk-gate.c | 4 ---- drivers/clk/socfpga/clk.h | 6 +++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c index 0d5dc84372dc..0add360525a0 100644 --- a/drivers/clk/socfpga/clk-gate.c +++ b/drivers/clk/socfpga/clk-gate.c @@ -32,14 +32,10 @@ #define SOCFPGA_MMC_CLK "sdmmc_clk" #define SOCFPGA_GPIO_DB_CLK_OFFSET 0xA8 -#define streq(a, b) (strcmp((a), (b)) == 0) - #define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw) /* SDMMC Group for System Manager defines */ #define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108 -#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \ - ((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0)) static u8 socfpga_clk_get_parent(struct clk_hw *hwclk) { diff --git a/drivers/clk/socfpga/clk.h b/drivers/clk/socfpga/clk.h index d291f60c46e1..b09a5d50547e 100644 --- a/drivers/clk/socfpga/clk.h +++ b/drivers/clk/socfpga/clk.h @@ -26,9 +26,13 @@ #define CLKMGR_L4SRC 0x70 #define CLKMGR_PERPLL_SRC 0xAC -#define SOCFPGA_MAX_PARENTS 3 +#define SOCFPGA_MAX_PARENTS 5 #define div_mask(width) ((1 << (width)) - 1) +#define streq(a, b) (strcmp((a), (b)) == 0) +#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \ + ((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0)) + extern void __iomem *clk_mgr_base_addr; void __init socfpga_pll_init(struct device_node *node); -- cgit v1.2.3-58-ga151 From 5343325ff3dd299f459fa9dacbd95dca5c9bf215 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Tue, 19 May 2015 22:22:42 -0500 Subject: clk: socfpga: add a clock driver for the Arria 10 platform The clocks on the Arria 10 platform is a bit different than the Cyclone/Arria 5 platform that it should just have it's own driver. Signed-off-by: Dinh Nguyen Signed-off-by: Stephen Boyd --- drivers/clk/socfpga/Makefile | 1 + drivers/clk/socfpga/clk-gate-a10.c | 190 +++++++++++++++++++++++++++++++++++ drivers/clk/socfpga/clk-periph-a10.c | 138 +++++++++++++++++++++++++ drivers/clk/socfpga/clk-pll-a10.c | 129 ++++++++++++++++++++++++ drivers/clk/socfpga/clk.c | 7 +- drivers/clk/socfpga/clk.h | 5 + 6 files changed, 469 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/socfpga/clk-gate-a10.c create mode 100644 drivers/clk/socfpga/clk-periph-a10.c create mode 100644 drivers/clk/socfpga/clk-pll-a10.c diff --git a/drivers/clk/socfpga/Makefile b/drivers/clk/socfpga/Makefile index 7e2d15a0c7b8..d8bb239753a4 100644 --- a/drivers/clk/socfpga/Makefile +++ b/drivers/clk/socfpga/Makefile @@ -2,3 +2,4 @@ obj-y += clk.o obj-y += clk-gate.o obj-y += clk-pll.o obj-y += clk-periph.o +obj-y += clk-pll-a10.o clk-periph-a10.o clk-gate-a10.o diff --git a/drivers/clk/socfpga/clk-gate-a10.c b/drivers/clk/socfpga/clk-gate-a10.c new file mode 100644 index 000000000000..be3e998b85d0 --- /dev/null +++ b/drivers/clk/socfpga/clk-gate-a10.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2015 Altera Corporation. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#include +#include +#include +#include +#include + +#include "clk.h" + +#define streq(a, b) (strcmp((a), (b)) == 0) + +#define to_socfpga_gate_clk(p) container_of(p, struct socfpga_gate_clk, hw.hw) + +/* SDMMC Group for System Manager defines */ +#define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x28 + +static unsigned long socfpga_gate_clk_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); + u32 div = 1, val; + + if (socfpgaclk->fixed_div) + div = socfpgaclk->fixed_div; + else if (socfpgaclk->div_reg) { + val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; + val &= div_mask(socfpgaclk->width); + div = (1 << val); + } + + return parent_rate / div; +} + +static int socfpga_clk_prepare(struct clk_hw *hwclk) +{ + struct socfpga_gate_clk *socfpgaclk = to_socfpga_gate_clk(hwclk); + int i; + u32 hs_timing; + u32 clk_phase[2]; + + if (socfpgaclk->clk_phase[0] || socfpgaclk->clk_phase[1]) { + for (i = 0; i < ARRAY_SIZE(clk_phase); i++) { + switch (socfpgaclk->clk_phase[i]) { + case 0: + clk_phase[i] = 0; + break; + case 45: + clk_phase[i] = 1; + break; + case 90: + clk_phase[i] = 2; + break; + case 135: + clk_phase[i] = 3; + break; + case 180: + clk_phase[i] = 4; + break; + case 225: + clk_phase[i] = 5; + break; + case 270: + clk_phase[i] = 6; + break; + case 315: + clk_phase[i] = 7; + break; + default: + clk_phase[i] = 0; + break; + } + } + + hs_timing = SYSMGR_SDMMC_CTRL_SET(clk_phase[0], clk_phase[1]); + if (!IS_ERR(socfpgaclk->sys_mgr_base_addr)) + regmap_write(socfpgaclk->sys_mgr_base_addr, + SYSMGR_SDMMCGRP_CTRL_OFFSET, hs_timing); + else + pr_err("%s: cannot set clk_phase because sys_mgr_base_addr is not available!\n", + __func__); + } + return 0; +} + +static struct clk_ops gateclk_ops = { + .prepare = socfpga_clk_prepare, + .recalc_rate = socfpga_gate_clk_recalc_rate, +}; + +static void __init __socfpga_gate_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 clk_gate[2]; + u32 div_reg[3]; + u32 clk_phase[2]; + u32 fixed_div; + struct clk *clk; + struct socfpga_gate_clk *socfpga_clk; + const char *clk_name = node->name; + const char *parent_name[SOCFPGA_MAX_PARENTS]; + struct clk_init_data init; + int rc; + int i = 0; + + socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); + if (WARN_ON(!socfpga_clk)) + return; + + rc = of_property_read_u32_array(node, "clk-gate", clk_gate, 2); + if (rc) + clk_gate[0] = 0; + + if (clk_gate[0]) { + socfpga_clk->hw.reg = clk_mgr_a10_base_addr + clk_gate[0]; + socfpga_clk->hw.bit_idx = clk_gate[1]; + + gateclk_ops.enable = clk_gate_ops.enable; + gateclk_ops.disable = clk_gate_ops.disable; + } + + rc = of_property_read_u32(node, "fixed-divider", &fixed_div); + if (rc) + socfpga_clk->fixed_div = 0; + else + socfpga_clk->fixed_div = fixed_div; + + rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); + if (!rc) { + socfpga_clk->div_reg = clk_mgr_a10_base_addr + div_reg[0]; + socfpga_clk->shift = div_reg[1]; + socfpga_clk->width = div_reg[2]; + } else { + socfpga_clk->div_reg = NULL; + } + + rc = of_property_read_u32_array(node, "clk-phase", clk_phase, 2); + if (!rc) { + socfpga_clk->clk_phase[0] = clk_phase[0]; + socfpga_clk->clk_phase[1] = clk_phase[1]; + + socfpga_clk->sys_mgr_base_addr = + syscon_regmap_lookup_by_compatible("altr,sys-mgr"); + if (IS_ERR(socfpga_clk->sys_mgr_base_addr)) { + pr_err("%s: failed to find altr,sys-mgr regmap!\n", + __func__); + return; + } + } + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = + of_clk_get_parent_name(node, i)) != NULL) + i++; + + init.parent_names = parent_name; + init.num_parents = i; + socfpga_clk->hw.hw.init = &init; + + clk = clk_register(NULL, &socfpga_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(socfpga_clk); + return; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (WARN_ON(rc)) + return; +} + +void __init socfpga_a10_gate_init(struct device_node *node) +{ + __socfpga_gate_init(node, &gateclk_ops); +} diff --git a/drivers/clk/socfpga/clk-periph-a10.c b/drivers/clk/socfpga/clk-periph-a10.c new file mode 100644 index 000000000000..9d0181b5a6a4 --- /dev/null +++ b/drivers/clk/socfpga/clk-periph-a10.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2015 Altera Corporation. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#include +#include +#include + +#include "clk.h" + +#define CLK_MGR_FREE_SHIFT 16 +#define CLK_MGR_FREE_MASK 0x7 + +#define SOCFPGA_MPU_FREE_CLK "mpu_free_clk" +#define SOCFPGA_NOC_FREE_CLK "noc_free_clk" +#define SOCFPGA_SDMMC_FREE_CLK "sdmmc_free_clk" +#define to_socfpga_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw) + +static unsigned long clk_periclk_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk); + u32 div; + + if (socfpgaclk->fixed_div) { + div = socfpgaclk->fixed_div; + } else if (socfpgaclk->div_reg) { + div = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; + div &= div_mask(socfpgaclk->width); + div += 1; + } else { + div = ((readl(socfpgaclk->hw.reg) & 0x7ff) + 1); + } + + return parent_rate / div; +} + +static u8 clk_periclk_get_parent(struct clk_hw *hwclk) +{ + struct socfpga_periph_clk *socfpgaclk = to_socfpga_periph_clk(hwclk); + u32 clk_src; + + clk_src = readl(socfpgaclk->hw.reg); + if (streq(hwclk->init->name, SOCFPGA_MPU_FREE_CLK) || + streq(hwclk->init->name, SOCFPGA_NOC_FREE_CLK) || + streq(hwclk->init->name, SOCFPGA_SDMMC_FREE_CLK)) + return (clk_src >> CLK_MGR_FREE_SHIFT) & + CLK_MGR_FREE_MASK; + else + return 0; +} + +static const struct clk_ops periclk_ops = { + .recalc_rate = clk_periclk_recalc_rate, + .get_parent = clk_periclk_get_parent, +}; + +static __init void __socfpga_periph_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 reg; + struct clk *clk; + struct socfpga_periph_clk *periph_clk; + const char *clk_name = node->name; + const char *parent_name; + struct clk_init_data init; + int rc; + u32 fixed_div; + u32 div_reg[3]; + + of_property_read_u32(node, "reg", ®); + + periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL); + if (WARN_ON(!periph_clk)) + return; + + periph_clk->hw.reg = clk_mgr_a10_base_addr + reg; + + rc = of_property_read_u32_array(node, "div-reg", div_reg, 3); + if (!rc) { + periph_clk->div_reg = clk_mgr_a10_base_addr + div_reg[0]; + periph_clk->shift = div_reg[1]; + periph_clk->width = div_reg[2]; + } else { + periph_clk->div_reg = NULL; + } + + rc = of_property_read_u32(node, "fixed-divider", &fixed_div); + if (rc) + periph_clk->fixed_div = 0; + else + periph_clk->fixed_div = fixed_div; + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + + parent_name = of_clk_get_parent_name(node, 0); + init.num_parents = 1; + init.parent_names = &parent_name; + + periph_clk->hw.hw.init = &init; + + clk = clk_register(NULL, &periph_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(periph_clk); + return; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (rc < 0) { + pr_err("Could not register clock provider for node:%s\n", + clk_name); + goto err_clk; + } + + return; + +err_clk: + clk_unregister(clk); +} + +void __init socfpga_a10_periph_init(struct device_node *node) +{ + __socfpga_periph_init(node, &periclk_ops); +} diff --git a/drivers/clk/socfpga/clk-pll-a10.c b/drivers/clk/socfpga/clk-pll-a10.c new file mode 100644 index 000000000000..1178b11babca --- /dev/null +++ b/drivers/clk/socfpga/clk-pll-a10.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2015 Altera Corporation. All rights reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +#include +#include +#include +#include + +#include "clk.h" + +/* Clock Manager offsets */ +#define CLK_MGR_PLL_CLK_SRC_SHIFT 8 +#define CLK_MGR_PLL_CLK_SRC_MASK 0x3 + +/* Clock bypass bits */ +#define SOCFPGA_PLL_BG_PWRDWN 0 +#define SOCFPGA_PLL_PWR_DOWN 1 +#define SOCFPGA_PLL_EXT_ENA 2 +#define SOCFPGA_PLL_DIVF_MASK 0x00001FFF +#define SOCFPGA_PLL_DIVF_SHIFT 0 +#define SOCFPGA_PLL_DIVQ_MASK 0x003F0000 +#define SOCFPGA_PLL_DIVQ_SHIFT 16 +#define SOCFGPA_MAX_PARENTS 5 + +#define SOCFPGA_MAIN_PLL_CLK "main_pll" +#define SOCFPGA_PERIP_PLL_CLK "periph_pll" + +#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw) + +void __iomem *clk_mgr_a10_base_addr; + +static unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk, + unsigned long parent_rate) +{ + struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); + unsigned long divf, divq, reg; + unsigned long long vco_freq; + + /* read VCO1 reg for numerator and denominator */ + reg = readl(socfpgaclk->hw.reg + 0x4); + divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT; + divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT; + vco_freq = (unsigned long long)parent_rate * (divf + 1); + do_div(vco_freq, (1 + divq)); + return (unsigned long)vco_freq; +} + +static u8 clk_pll_get_parent(struct clk_hw *hwclk) +{ + struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk); + u32 pll_src; + + pll_src = readl(socfpgaclk->hw.reg); + + return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) & + CLK_MGR_PLL_CLK_SRC_MASK; +} + +static struct clk_ops clk_pll_ops = { + .recalc_rate = clk_pll_recalc_rate, + .get_parent = clk_pll_get_parent, +}; + +static struct __init clk * __socfpga_pll_init(struct device_node *node, + const struct clk_ops *ops) +{ + u32 reg; + struct clk *clk; + struct socfpga_pll *pll_clk; + const char *clk_name = node->name; + const char *parent_name[SOCFGPA_MAX_PARENTS]; + struct clk_init_data init; + struct device_node *clkmgr_np; + int rc; + int i = 0; + + of_property_read_u32(node, "reg", ®); + + pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL); + if (WARN_ON(!pll_clk)) + return NULL; + + clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr"); + clk_mgr_a10_base_addr = of_iomap(clkmgr_np, 0); + BUG_ON(!clk_mgr_a10_base_addr); + pll_clk->hw.reg = clk_mgr_a10_base_addr + reg; + + of_property_read_string(node, "clock-output-names", &clk_name); + + init.name = clk_name; + init.ops = ops; + init.flags = 0; + + while (i < SOCFGPA_MAX_PARENTS && (parent_name[i] = + of_clk_get_parent_name(node, i)) != NULL) + i++; + init.num_parents = i; + init.parent_names = parent_name; + pll_clk->hw.hw.init = &init; + + pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA; + clk_pll_ops.enable = clk_gate_ops.enable; + clk_pll_ops.disable = clk_gate_ops.disable; + + clk = clk_register(NULL, &pll_clk->hw.hw); + if (WARN_ON(IS_ERR(clk))) { + kfree(pll_clk); + return NULL; + } + rc = of_clk_add_provider(node, of_clk_src_simple_get, clk); + return clk; +} + +void __init socfpga_a10_pll_init(struct device_node *node) +{ + __socfpga_pll_init(node, &clk_pll_ops); +} diff --git a/drivers/clk/socfpga/clk.c b/drivers/clk/socfpga/clk.c index 43db947e5f0e..7564d2e35f32 100644 --- a/drivers/clk/socfpga/clk.c +++ b/drivers/clk/socfpga/clk.c @@ -24,4 +24,9 @@ CLK_OF_DECLARE(socfpga_pll_clk, "altr,socfpga-pll-clock", socfpga_pll_init); CLK_OF_DECLARE(socfpga_perip_clk, "altr,socfpga-perip-clk", socfpga_periph_init); CLK_OF_DECLARE(socfpga_gate_clk, "altr,socfpga-gate-clk", socfpga_gate_init); - +CLK_OF_DECLARE(socfpga_a10_pll_clk, "altr,socfpga-a10-pll-clock", + socfpga_a10_pll_init); +CLK_OF_DECLARE(socfpga_a10_perip_clk, "altr,socfpga-a10-perip-clk", + socfpga_a10_periph_init); +CLK_OF_DECLARE(socfpga_a10_gate_clk, "altr,socfpga-a10-gate-clk", + socfpga_a10_gate_init); diff --git a/drivers/clk/socfpga/clk.h b/drivers/clk/socfpga/clk.h index b09a5d50547e..603973ab7e29 100644 --- a/drivers/clk/socfpga/clk.h +++ b/drivers/clk/socfpga/clk.h @@ -34,10 +34,14 @@ ((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0)) extern void __iomem *clk_mgr_base_addr; +extern void __iomem *clk_mgr_a10_base_addr; void __init socfpga_pll_init(struct device_node *node); void __init socfpga_periph_init(struct device_node *node); void __init socfpga_gate_init(struct device_node *node); +void socfpga_a10_pll_init(struct device_node *node); +void socfpga_a10_periph_init(struct device_node *node); +void socfpga_a10_gate_init(struct device_node *node); struct socfpga_pll { struct clk_gate hw; @@ -48,6 +52,7 @@ struct socfpga_gate_clk { char *parent_name; u32 fixed_div; void __iomem *div_reg; + struct regmap *sys_mgr_base_addr; u32 width; /* only valid if div_reg != 0 */ u32 shift; /* only valid if div_reg != 0 */ u32 clk_phase[2]; -- cgit v1.2.3-58-ga151 From 31a0b5373c7562d5a24f973a2afec1785d72d9c4 Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Thu, 26 Feb 2015 17:42:06 +0100 Subject: clk: shmobile: Add r8a7778 SoC to MSTP bindings Signed-off-by: Ulrich Hecht Acked-by: Laurent Pinchart Signed-off-by: Geert Uytterhoeven --- Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt index 0a80fa70ca26..e163092348bf 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt @@ -13,6 +13,7 @@ Required Properties: - "renesas,r7s72100-mstp-clocks" for R7S72100 (RZ) MSTP gate clocks - "renesas,r8a73a4-mstp-clocks" for R8A73A4 (R-Mobile APE6) MSTP gate clocks - "renesas,r8a7740-mstp-clocks" for R8A7740 (R-Mobile A1) MSTP gate clocks + - "renesas,r8a7778-mstp-clocks" for R8A7778 (R-Car M1) MSTP gate clocks - "renesas,r8a7779-mstp-clocks" for R8A7779 (R-Car H1) MSTP gate clocks - "renesas,r8a7790-mstp-clocks" for R8A7790 (R-Car H2) MSTP gate clocks - "renesas,r8a7791-mstp-clocks" for R8A7791 (R-Car M2) MSTP gate clocks -- cgit v1.2.3-58-ga151 From b5405db9df8ea71435336bea8295cc4aa84ee3fa Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Thu, 28 May 2015 17:17:00 +0200 Subject: clk: shmobile: Add r8a7793 SoC to MSTP bindings Also replaces "R-Car M2" with "R-Car M2-W" to avoid confusion. Signed-off-by: Ulrich Hecht Acked-by: Simon Horman Cc: Michael Turquette Cc: devicetree@vger.kernel.org Signed-off-by: Geert Uytterhoeven --- Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt index e163092348bf..c95f7d2db6bd 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt @@ -16,7 +16,8 @@ Required Properties: - "renesas,r8a7778-mstp-clocks" for R8A7778 (R-Car M1) MSTP gate clocks - "renesas,r8a7779-mstp-clocks" for R8A7779 (R-Car H1) MSTP gate clocks - "renesas,r8a7790-mstp-clocks" for R8A7790 (R-Car H2) MSTP gate clocks - - "renesas,r8a7791-mstp-clocks" for R8A7791 (R-Car M2) MSTP gate clocks + - "renesas,r8a7791-mstp-clocks" for R8A7791 (R-Car M2-W) MSTP gate clocks + - "renesas,r8a7793-mstp-clocks" for R8A7793 (R-Car M2-N) MSTP gate clocks - "renesas,r8a7794-mstp-clocks" for R8A7794 (R-Car E2) MSTP gate clocks - "renesas,sh73a0-mstp-clocks" for SH73A0 (SH-MobileAG5) MSTP gate clocks - "renesas,cpg-mstp-clock" for generic MSTP gate clocks -- cgit v1.2.3-58-ga151 From 0e0d8b7050372e5b55f5067be8a15bb7577c9b00 Mon Sep 17 00:00:00 2001 From: Ulrich Hecht Date: Thu, 28 May 2015 17:17:01 +0200 Subject: clk: shmobile: r8a7793: Document DIV6 clock bindings Signed-off-by: Ulrich Hecht Acked-by: Simon Horman Cc: devicetree@vger.kernel.org Signed-off-by: Geert Uytterhoeven --- Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt index 054f65f9319c..b0a3a1147d62 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt @@ -10,7 +10,8 @@ Required Properties: - "renesas,r8a73a4-div6-clock" for R8A73A4 (R-Mobile APE6) DIV6 clocks - "renesas,r8a7740-div6-clock" for R8A7740 (R-Mobile A1) DIV6 clocks - "renesas,r8a7790-div6-clock" for R8A7790 (R-Car H2) DIV6 clocks - - "renesas,r8a7791-div6-clock" for R8A7791 (R-Car M2) DIV6 clocks + - "renesas,r8a7791-div6-clock" for R8A7791 (R-Car M2-W) DIV6 clocks + - "renesas,r8a7793-div6-clock" for R8A7793 (R-Car M2-N) DIV6 clocks - "renesas,sh73a0-div6-clock" for SH73A0 (SH-Mobile AG5) DIV6 clocks - "renesas,cpg-div6-clock" for generic DIV6 clocks - reg: Base address and length of the memory resource used by the DIV6 clock -- cgit v1.2.3-58-ga151 From 0c933f3a45be471160bcf3428c1ee8f75f9cd75d Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 1 Jun 2015 11:57:48 +0200 Subject: clk: shmobile: r8a7794: Document DIV6 clock bindings Signed-off-by: Geert Uytterhoeven --- Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt index b0a3a1147d62..3e40c41b0565 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt @@ -12,6 +12,7 @@ Required Properties: - "renesas,r8a7790-div6-clock" for R8A7790 (R-Car H2) DIV6 clocks - "renesas,r8a7791-div6-clock" for R8A7791 (R-Car M2-W) DIV6 clocks - "renesas,r8a7793-div6-clock" for R8A7793 (R-Car M2-N) DIV6 clocks + - "renesas,r8a7794-div6-clock" for R8A7794 (R-Car E2) DIV6 clocks - "renesas,sh73a0-div6-clock" for SH73A0 (SH-Mobile AG5) DIV6 clocks - "renesas,cpg-div6-clock" for generic DIV6 clocks - reg: Base address and length of the memory resource used by the DIV6 clock -- cgit v1.2.3-58-ga151 From 370626288f1a5992951abf825c4b662ba55774c0 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 1 Jun 2015 12:00:08 +0200 Subject: clk: shmobile: div6: Document mandatory compatible fallback The generic CPG DIV6 clock compatible value is mandatory, as the driver uses only this value for matching. Document that this is a fallback that must be present. Signed-off-by: Geert Uytterhoeven --- Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt index 3e40c41b0565..5ddb68418655 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-div6-clocks.txt @@ -14,7 +14,7 @@ Required Properties: - "renesas,r8a7793-div6-clock" for R8A7793 (R-Car M2-N) DIV6 clocks - "renesas,r8a7794-div6-clock" for R8A7794 (R-Car E2) DIV6 clocks - "renesas,sh73a0-div6-clock" for SH73A0 (SH-Mobile AG5) DIV6 clocks - - "renesas,cpg-div6-clock" for generic DIV6 clocks + and "renesas,cpg-div6-clock" as a fallback. - reg: Base address and length of the memory resource used by the DIV6 clock - clocks: Reference to the parent clock(s); either one, four, or eight clocks must be specified. For clocks with multiple parents, invalid -- cgit v1.2.3-58-ga151 From 17df1fb2e502371b63ea1b2110584e8a712d6c20 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 28 May 2015 11:28:10 +0200 Subject: clk: shmobile: mstp: Document mandatory compatible fallback The generic MSTP gate clocks compatible value is mandatory, as the driver uses only this value for matching. Document that this is a fallback that must be present. Also fix a typo (missing plural "s") in the compatible value. Signed-off-by: Geert Uytterhoeven --- Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt index c95f7d2db6bd..16ed18155160 100644 --- a/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,cpg-mstp-clocks.txt @@ -20,7 +20,7 @@ Required Properties: - "renesas,r8a7793-mstp-clocks" for R8A7793 (R-Car M2-N) MSTP gate clocks - "renesas,r8a7794-mstp-clocks" for R8A7794 (R-Car E2) MSTP gate clocks - "renesas,sh73a0-mstp-clocks" for SH73A0 (SH-MobileAG5) MSTP gate clocks - - "renesas,cpg-mstp-clock" for generic MSTP gate clocks + and "renesas,cpg-mstp-clocks" as a fallback. - reg: Base address and length of the I/O mapped registers used by the MSTP clocks. The first register is the clock control register and is mandatory. The second register is the clock status register and is optional when not -- cgit v1.2.3-58-ga151 From dd734a7e773c6b9d764d2879e715aaf579a99506 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 28 May 2015 11:31:17 +0200 Subject: clk: shmobile: rcar-gen2: Document mandatory compatible fallback The generic R-Car Gen2 CPG compatible value is mandatory, as the driver uses only this value for matching. Document that this is a fallback that must be present. Signed-off-by: Geert Uytterhoeven --- .../devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt index b02944fba9de..56f111bd3e45 100644 --- a/Documentation/devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,rcar-gen2-cpg-clocks.txt @@ -10,7 +10,7 @@ Required Properties: - "renesas,r8a7791-cpg-clocks" for the r8a7791 CPG - "renesas,r8a7793-cpg-clocks" for the r8a7793 CPG - "renesas,r8a7794-cpg-clocks" for the r8a7794 CPG - - "renesas,rcar-gen2-cpg-clocks" for the generic R-Car Gen2 CPG + and "renesas,rcar-gen2-cpg-clocks" as a fallback. - reg: Base address and length of the memory resource used by the CPG -- cgit v1.2.3-58-ga151 From a431c1fa01ff020ecb146c04fbbea974e3eb910f Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 28 May 2015 11:33:33 +0200 Subject: clk: shmobile: rz: Document mandatory compatible fallback The generic RZ CPG compatible value is mandatory, as the driver uses only this value for matching. Document that this is a fallback that must be present. Signed-off-by: Geert Uytterhoeven --- Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt b/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt index 98a257492522..b0f7ddb8cdb1 100644 --- a/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt +++ b/Documentation/devicetree/bindings/clock/renesas,rz-cpg-clocks.txt @@ -7,7 +7,7 @@ Required Properties: - compatible: Must be one of - "renesas,r7s72100-cpg-clocks" for the r7s72100 CPG - - "renesas,rz-cpg-clocks" for the generic RZ CPG + and "renesas,rz-cpg-clocks" as a fallback. - reg: Base address and length of the memory resource used by the CPG - clocks: References to possible parent clocks. Order must match clock modes in the datasheet. For the r7s72100, this is extal, usb_x1. -- cgit v1.2.3-58-ga151 From ec80749dfd859b560e8864d665d38d7a63e6a7f2 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 1 Jun 2015 20:23:27 +0200 Subject: clk: sunxi: Add support for the usb-clk on sun8i a23 and a33 SoCs The usb-clk on sun8i a23 and a33 SoCs is similar to the ones found on sun6i-a31 SoCs but instead of a 3th phy the a23 / a33 have a hsic interface which gets enabled by almost the same bits as used on the a31 for the 3rd phy, but not exactly the same bits so we need a new compatible for this. Signed-off-by: Hans de Goede Signed-off-by: Maxime Ripard Reviewed-by: Chen-Yu Tsai --- Documentation/devicetree/bindings/clock/sunxi.txt | 1 + drivers/clk/sunxi/clk-usb.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 4fa11af3d378..8a47b77abfca 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -67,6 +67,7 @@ Required properties: "allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20 "allwinner,sun5i-a13-usb-clk" - for usb gates + resets on A13 "allwinner,sun6i-a31-usb-clk" - for usb gates + resets on A31 + "allwinner,sun8i-a23-usb-clk" - for usb gates + resets on A23 "allwinner,sun9i-a80-usb-mod-clk" - for usb gates + resets on A80 "allwinner,sun9i-a80-usb-phy-clk" - for usb phy gates + resets on A80 diff --git a/drivers/clk/sunxi/clk-usb.c b/drivers/clk/sunxi/clk-usb.c index a86ed2f8d7af..3a25f9588e67 100644 --- a/drivers/clk/sunxi/clk-usb.c +++ b/drivers/clk/sunxi/clk-usb.c @@ -204,6 +204,17 @@ static void __init sun6i_a31_usb_setup(struct device_node *node) } CLK_OF_DECLARE(sun6i_a31_usb, "allwinner,sun6i-a31-usb-clk", sun6i_a31_usb_setup); +static const struct usb_clk_data sun8i_a23_usb_clk_data __initconst = { + .clk_mask = BIT(16) | BIT(11) | BIT(10) | BIT(9) | BIT(8), + .reset_mask = BIT(2) | BIT(1) | BIT(0), +}; + +static void __init sun8i_a23_usb_setup(struct device_node *node) +{ + sunxi_usb_clk_setup(node, &sun8i_a23_usb_clk_data, &sun4i_a10_usb_lock); +} +CLK_OF_DECLARE(sun8i_a23_usb, "allwinner,sun8i-a23-usb-clk", sun8i_a23_usb_setup); + static const struct usb_clk_data sun9i_a80_usb_mod_data __initconst = { .clk_mask = BIT(6) | BIT(5) | BIT(4) | BIT(3) | BIT(2) | BIT(1), .reset_mask = BIT(19) | BIT(18) | BIT(17), -- cgit v1.2.3-58-ga151 From 463cfb2c9b9516a710c6320d3f6c609d33331378 Mon Sep 17 00:00:00 2001 From: Bintian Wang Date: Fri, 29 May 2015 10:08:36 +0800 Subject: clk: hisilicon: Remove __init for marking function prototypes __init markings on function prototypes are useless, so remove them. Suggested-by: Stephen Boyd Signed-off-by: Bintian Wang Tested-by: Will Deacon Tested-by: Tyler Baker Tested-by: Kevin Hilman Signed-off-by: Michael Turquette --- drivers/clk/hisilicon/clk.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h index 31083ffc0650..6b6f99448e01 100644 --- a/drivers/clk/hisilicon/clk.h +++ b/drivers/clk/hisilicon/clk.h @@ -95,17 +95,17 @@ struct clk *hisi_register_clkgate_sep(struct device *, const char *, void __iomem *, u8, u8, spinlock_t *); -struct hisi_clock_data __init *hisi_clk_init(struct device_node *, int); -void __init hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *, - int, struct hisi_clock_data *); -void __init hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *, - int, struct hisi_clock_data *); -void __init hisi_clk_register_mux(struct hisi_mux_clock *, int, +struct hisi_clock_data *hisi_clk_init(struct device_node *, int); +void hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *, + int, struct hisi_clock_data *); +void hisi_clk_register_fixed_factor(struct hisi_fixed_factor_clock *, + int, struct hisi_clock_data *); +void hisi_clk_register_mux(struct hisi_mux_clock *, int, struct hisi_clock_data *); -void __init hisi_clk_register_divider(struct hisi_divider_clock *, +void hisi_clk_register_divider(struct hisi_divider_clock *, + int, struct hisi_clock_data *); +void hisi_clk_register_gate(struct hisi_gate_clock *, + int, struct hisi_clock_data *); +void hisi_clk_register_gate_sep(struct hisi_gate_clock *, int, struct hisi_clock_data *); -void __init hisi_clk_register_gate(struct hisi_gate_clock *, - int, struct hisi_clock_data *); -void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *, - int, struct hisi_clock_data *); #endif /* __HISI_CLK_H */ -- cgit v1.2.3-58-ga151 From 2a40a2ea109269def06f991f771c2bf2f6c0f396 Mon Sep 17 00:00:00 2001 From: Bintian Wang Date: Fri, 29 May 2015 10:08:37 +0800 Subject: dt-bindings: Add header file of hi6220 clock driver Add the header file "hi6220-clock.h" used by both hi6220 clock driver and hi6220 device tree file. Suggested-by: Stephen Boyd Signed-off-by: Bintian Wang Tested-by: Will Deacon Tested-by: Tyler Baker Tested-by: Kevin Hilman Signed-off-by: Michael Turquette --- include/dt-bindings/clock/hi6220-clock.h | 173 +++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 include/dt-bindings/clock/hi6220-clock.h diff --git a/include/dt-bindings/clock/hi6220-clock.h b/include/dt-bindings/clock/hi6220-clock.h new file mode 100644 index 000000000000..70ee3833a7a0 --- /dev/null +++ b/include/dt-bindings/clock/hi6220-clock.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2015 Hisilicon Limited. + * + * Author: Bintian Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DT_BINDINGS_CLOCK_HI6220_H +#define __DT_BINDINGS_CLOCK_HI6220_H + +/* clk in Hi6220 AO (always on) controller */ +#define HI6220_NONE_CLOCK 0 + +/* fixed rate clocks */ +#define HI6220_REF32K 1 +#define HI6220_CLK_TCXO 2 +#define HI6220_MMC1_PAD 3 +#define HI6220_MMC2_PAD 4 +#define HI6220_MMC0_PAD 5 +#define HI6220_PLL_BBP 6 +#define HI6220_PLL_GPU 7 +#define HI6220_PLL1_DDR 8 +#define HI6220_PLL_SYS 9 +#define HI6220_PLL_SYS_MEDIA 10 +#define HI6220_DDR_SRC 11 +#define HI6220_PLL_MEDIA 12 +#define HI6220_PLL_DDR 13 + +/* fixed factor clocks */ +#define HI6220_300M 14 +#define HI6220_150M 15 +#define HI6220_PICOPHY_SRC 16 +#define HI6220_MMC0_SRC_SEL 17 +#define HI6220_MMC1_SRC_SEL 18 +#define HI6220_MMC2_SRC_SEL 19 +#define HI6220_VPU_CODEC 20 +#define HI6220_MMC0_SMP 21 +#define HI6220_MMC1_SMP 22 +#define HI6220_MMC2_SMP 23 + +/* gate clocks */ +#define HI6220_WDT0_PCLK 24 +#define HI6220_WDT1_PCLK 25 +#define HI6220_WDT2_PCLK 26 +#define HI6220_TIMER0_PCLK 27 +#define HI6220_TIMER1_PCLK 28 +#define HI6220_TIMER2_PCLK 29 +#define HI6220_TIMER3_PCLK 30 +#define HI6220_TIMER4_PCLK 31 +#define HI6220_TIMER5_PCLK 32 +#define HI6220_TIMER6_PCLK 33 +#define HI6220_TIMER7_PCLK 34 +#define HI6220_TIMER8_PCLK 35 +#define HI6220_UART0_PCLK 36 + +#define HI6220_AO_NR_CLKS 37 + +/* clk in Hi6220 systrl */ +/* gate clock */ +#define HI6220_MMC0_CLK 1 +#define HI6220_MMC0_CIUCLK 2 +#define HI6220_MMC1_CLK 3 +#define HI6220_MMC1_CIUCLK 4 +#define HI6220_MMC2_CLK 5 +#define HI6220_MMC2_CIUCLK 6 +#define HI6220_USBOTG_HCLK 7 +#define HI6220_CLK_PICOPHY 8 +#define HI6220_HIFI 9 +#define HI6220_DACODEC_PCLK 10 +#define HI6220_EDMAC_ACLK 11 +#define HI6220_CS_ATB 12 +#define HI6220_I2C0_CLK 13 +#define HI6220_I2C1_CLK 14 +#define HI6220_I2C2_CLK 15 +#define HI6220_I2C3_CLK 16 +#define HI6220_UART1_PCLK 17 +#define HI6220_UART2_PCLK 18 +#define HI6220_UART3_PCLK 19 +#define HI6220_UART4_PCLK 20 +#define HI6220_SPI_CLK 21 +#define HI6220_TSENSOR_CLK 22 +#define HI6220_MMU_CLK 23 +#define HI6220_HIFI_SEL 24 +#define HI6220_MMC0_SYSPLL 25 +#define HI6220_MMC1_SYSPLL 26 +#define HI6220_MMC2_SYSPLL 27 +#define HI6220_MMC0_SEL 28 +#define HI6220_MMC1_SEL 29 +#define HI6220_BBPPLL_SEL 30 +#define HI6220_MEDIA_PLL_SRC 31 +#define HI6220_MMC2_SEL 32 +#define HI6220_CS_ATB_SYSPLL 33 + +/* mux clocks */ +#define HI6220_MMC0_SRC 34 +#define HI6220_MMC0_SMP_IN 35 +#define HI6220_MMC1_SRC 36 +#define HI6220_MMC1_SMP_IN 37 +#define HI6220_MMC2_SRC 38 +#define HI6220_MMC2_SMP_IN 39 +#define HI6220_HIFI_SRC 40 +#define HI6220_UART1_SRC 41 +#define HI6220_UART2_SRC 42 +#define HI6220_UART3_SRC 43 +#define HI6220_UART4_SRC 44 +#define HI6220_MMC0_MUX0 45 +#define HI6220_MMC1_MUX0 46 +#define HI6220_MMC2_MUX0 47 +#define HI6220_MMC0_MUX1 48 +#define HI6220_MMC1_MUX1 49 +#define HI6220_MMC2_MUX1 50 + +/* divider clocks */ +#define HI6220_CLK_BUS 51 +#define HI6220_MMC0_DIV 52 +#define HI6220_MMC1_DIV 53 +#define HI6220_MMC2_DIV 54 +#define HI6220_HIFI_DIV 55 +#define HI6220_BBPPLL0_DIV 56 +#define HI6220_CS_DAPB 57 +#define HI6220_CS_ATB_DIV 58 + +#define HI6220_SYS_NR_CLKS 59 + +/* clk in Hi6220 media controller */ +/* gate clocks */ +#define HI6220_DSI_PCLK 1 +#define HI6220_G3D_PCLK 2 +#define HI6220_ACLK_CODEC_VPU 3 +#define HI6220_ISP_SCLK 4 +#define HI6220_ADE_CORE 5 +#define HI6220_MED_MMU 6 +#define HI6220_CFG_CSI4PHY 7 +#define HI6220_CFG_CSI2PHY 8 +#define HI6220_ISP_SCLK_GATE 9 +#define HI6220_ISP_SCLK_GATE1 10 +#define HI6220_ADE_CORE_GATE 11 +#define HI6220_CODEC_VPU_GATE 12 +#define HI6220_MED_SYSPLL 13 + +/* mux clocks */ +#define HI6220_1440_1200 14 +#define HI6220_1000_1200 15 +#define HI6220_1000_1440 16 + +/* divider clocks */ +#define HI6220_CODEC_JPEG 17 +#define HI6220_ISP_SCLK_SRC 18 +#define HI6220_ISP_SCLK1 19 +#define HI6220_ADE_CORE_SRC 20 +#define HI6220_ADE_PIX_SRC 21 +#define HI6220_G3D_CLK 22 +#define HI6220_CODEC_VPU_SRC 23 + +#define HI6220_MEDIA_NR_CLKS 24 + +/* clk in Hi6220 power controller */ +/* gate clocks */ +#define HI6220_PLL_GPU_GATE 1 +#define HI6220_PLL1_DDR_GATE 2 +#define HI6220_PLL_DDR_GATE 3 +#define HI6220_PLL_MEDIA_GATE 4 +#define HI6220_PLL0_BBP_GATE 5 + +/* divider clocks */ +#define HI6220_DDRC_SRC 6 +#define HI6220_DDRC_AXI1 7 + +#define HI6220_POWER_NR_CLKS 8 +#endif -- cgit v1.2.3-58-ga151 From 72ea48610d43c59507d9ad39083d40085400ba12 Mon Sep 17 00:00:00 2001 From: Bintian Wang Date: Fri, 29 May 2015 10:08:38 +0800 Subject: clk: hi6220: Clock driver support for Hisilicon hi6220 SoC Add clock drivers for hi6220 SoC, this driver controls the SoC registers to supply different clocks to different IPs in the SoC. We add one divider clock for hi6220 because the divider in hi6220 also has a mask bit but it doesnot obey the rule defined by flag "CLK_DIVIDER_HIWORD_MASK", we can not get index of the mask bit by left shift fixed bits (e.g. 16 bits), so we add this divider clock to handle it. Signed-off-by: Jorge Ramirez-Ortiz Signed-off-by: Bintian Wang Acked-by: Haojian Zhuang Reviewed-by: Zhangfei Gao Tested-by: Will Deacon Tested-by: Tyler Baker Tested-by: Kevin Hilman Signed-off-by: Michael Turquette --- drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 4 +- drivers/clk/hisilicon/Kconfig | 6 + drivers/clk/hisilicon/Makefile | 3 +- drivers/clk/hisilicon/clk-hi6220.c | 284 ++++++++++++++++++++++++++++++ drivers/clk/hisilicon/clk.c | 29 +++ drivers/clk/hisilicon/clk.h | 17 ++ drivers/clk/hisilicon/clkdivider-hi6220.c | 156 ++++++++++++++++ 8 files changed, 496 insertions(+), 4 deletions(-) create mode 100644 drivers/clk/hisilicon/Kconfig create mode 100644 drivers/clk/hisilicon/clk-hi6220.c create mode 100644 drivers/clk/hisilicon/clkdivider-hi6220.c diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 9897f353bf1a..8ec04156717b 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -150,6 +150,7 @@ config COMMON_CLK_CDCE706 ---help--- This driver supports TI CDCE706 programmable 3-PLL clock synthesizer. +source "drivers/clk/hisilicon/Kconfig" source "drivers/clk/qcom/Kconfig" endmenu diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 3d00c25382c5..971995428861 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -47,9 +47,7 @@ obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o obj-$(CONFIG_COMMON_CLK_AT91) += at91/ obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm/ obj-$(CONFIG_ARCH_BERLIN) += berlin/ -obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/ -obj-$(CONFIG_ARCH_HIP04) += hisilicon/ -obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/ +obj-$(CONFIG_ARCH_HISI) += hisilicon/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ diff --git a/drivers/clk/hisilicon/Kconfig b/drivers/clk/hisilicon/Kconfig new file mode 100644 index 000000000000..b4165ba75d9f --- /dev/null +++ b/drivers/clk/hisilicon/Kconfig @@ -0,0 +1,6 @@ +config COMMON_CLK_HI6220 + bool "Hi6220 Clock Driver" + depends on ARCH_HISI || COMPILE_TEST + default ARCH_HISI + help + Build the Hisilicon Hi6220 clock driver based on the common clock framework. diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile index 038c02f4d0e7..48f0116a032a 100644 --- a/drivers/clk/hisilicon/Makefile +++ b/drivers/clk/hisilicon/Makefile @@ -2,8 +2,9 @@ # Hisilicon Clock specific Makefile # -obj-y += clk.o clkgate-separated.o +obj-y += clk.o clkgate-separated.o clkdivider-hi6220.o obj-$(CONFIG_ARCH_HI3xxx) += clk-hi3620.o obj-$(CONFIG_ARCH_HIP04) += clk-hip04.o obj-$(CONFIG_ARCH_HIX5HD2) += clk-hix5hd2.o +obj-$(CONFIG_COMMON_CLK_HI6220) += clk-hi6220.o diff --git a/drivers/clk/hisilicon/clk-hi6220.c b/drivers/clk/hisilicon/clk-hi6220.c new file mode 100644 index 000000000000..4563343b6420 --- /dev/null +++ b/drivers/clk/hisilicon/clk-hi6220.c @@ -0,0 +1,284 @@ +/* + * Hisilicon Hi6220 clock driver + * + * Copyright (c) 2015 Hisilicon Limited. + * + * Author: Bintian Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clk.h" + + +/* clocks in AO (always on) controller */ +static struct hisi_fixed_rate_clock hi6220_fixed_rate_clks[] __initdata = { + { HI6220_REF32K, "ref32k", NULL, CLK_IS_ROOT, 32764, }, + { HI6220_CLK_TCXO, "clk_tcxo", NULL, CLK_IS_ROOT, 19200000, }, + { HI6220_MMC1_PAD, "mmc1_pad", NULL, CLK_IS_ROOT, 100000000, }, + { HI6220_MMC2_PAD, "mmc2_pad", NULL, CLK_IS_ROOT, 100000000, }, + { HI6220_MMC0_PAD, "mmc0_pad", NULL, CLK_IS_ROOT, 200000000, }, + { HI6220_PLL_BBP, "bbppll0", NULL, CLK_IS_ROOT, 245760000, }, + { HI6220_PLL_GPU, "gpupll", NULL, CLK_IS_ROOT, 1000000000,}, + { HI6220_PLL1_DDR, "ddrpll1", NULL, CLK_IS_ROOT, 1066000000,}, + { HI6220_PLL_SYS, "syspll", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_PLL_SYS_MEDIA, "media_syspll", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_DDR_SRC, "ddr_sel_src", NULL, CLK_IS_ROOT, 1200000000,}, + { HI6220_PLL_MEDIA, "media_pll", NULL, CLK_IS_ROOT, 1440000000,}, + { HI6220_PLL_DDR, "ddrpll0", NULL, CLK_IS_ROOT, 1600000000,}, +}; + +static struct hisi_fixed_factor_clock hi6220_fixed_factor_clks[] __initdata = { + { HI6220_300M, "clk_300m", "syspll", 1, 4, 0, }, + { HI6220_150M, "clk_150m", "clk_300m", 1, 2, 0, }, + { HI6220_PICOPHY_SRC, "picophy_src", "clk_150m", 1, 4, 0, }, + { HI6220_MMC0_SRC_SEL, "mmc0srcsel", "mmc0_sel", 1, 8, 0, }, + { HI6220_MMC1_SRC_SEL, "mmc1srcsel", "mmc1_sel", 1, 8, 0, }, + { HI6220_MMC2_SRC_SEL, "mmc2srcsel", "mmc2_sel", 1, 8, 0, }, + { HI6220_VPU_CODEC, "vpucodec", "codec_jpeg_aclk", 1, 2, 0, }, + { HI6220_MMC0_SMP, "mmc0_sample", "mmc0_sel", 1, 8, 0, }, + { HI6220_MMC1_SMP, "mmc1_sample", "mmc1_sel", 1, 8, 0, }, + { HI6220_MMC2_SMP, "mmc2_sample", "mmc2_sel", 1, 8, 0, }, +}; + +static struct hisi_gate_clock hi6220_separated_gate_clks_ao[] __initdata = { + { HI6220_WDT0_PCLK, "wdt0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 12, 0, }, + { HI6220_WDT1_PCLK, "wdt1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 13, 0, }, + { HI6220_WDT2_PCLK, "wdt2_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 14, 0, }, + { HI6220_TIMER0_PCLK, "timer0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 15, 0, }, + { HI6220_TIMER1_PCLK, "timer1_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 16, 0, }, + { HI6220_TIMER2_PCLK, "timer2_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 17, 0, }, + { HI6220_TIMER3_PCLK, "timer3_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 18, 0, }, + { HI6220_TIMER4_PCLK, "timer4_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 19, 0, }, + { HI6220_TIMER5_PCLK, "timer5_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 20, 0, }, + { HI6220_TIMER6_PCLK, "timer6_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 21, 0, }, + { HI6220_TIMER7_PCLK, "timer7_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 22, 0, }, + { HI6220_TIMER8_PCLK, "timer8_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 23, 0, }, + { HI6220_UART0_PCLK, "uart0_pclk", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x630, 24, 0, }, +}; + +static void __init hi6220_clk_ao_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data_ao; + + clk_data_ao = hisi_clk_init(np, HI6220_AO_NR_CLKS); + if (!clk_data_ao) + return; + + hisi_clk_register_fixed_rate(hi6220_fixed_rate_clks, + ARRAY_SIZE(hi6220_fixed_rate_clks), clk_data_ao); + + hisi_clk_register_fixed_factor(hi6220_fixed_factor_clks, + ARRAY_SIZE(hi6220_fixed_factor_clks), clk_data_ao); + + hisi_clk_register_gate_sep(hi6220_separated_gate_clks_ao, + ARRAY_SIZE(hi6220_separated_gate_clks_ao), clk_data_ao); +} +CLK_OF_DECLARE(hi6220_clk_ao, "hisilicon,hi6220-aoctrl", hi6220_clk_ao_init); + + +/* clocks in sysctrl */ +static const char *mmc0_mux0_p[] __initdata = { "pll_ddr_gate", "syspll", }; +static const char *mmc0_mux1_p[] __initdata = { "mmc0_mux0", "pll_media_gate", }; +static const char *mmc0_src_p[] __initdata = { "mmc0srcsel", "mmc0_div", }; +static const char *mmc1_mux0_p[] __initdata = { "pll_ddr_gate", "syspll", }; +static const char *mmc1_mux1_p[] __initdata = { "mmc1_mux0", "pll_media_gate", }; +static const char *mmc1_src_p[] __initdata = { "mmc1srcsel", "mmc1_div", }; +static const char *mmc2_mux0_p[] __initdata = { "pll_ddr_gate", "syspll", }; +static const char *mmc2_mux1_p[] __initdata = { "mmc2_mux0", "pll_media_gate", }; +static const char *mmc2_src_p[] __initdata = { "mmc2srcsel", "mmc2_div", }; +static const char *mmc0_sample_in[] __initdata = { "mmc0_sample", "mmc0_pad", }; +static const char *mmc1_sample_in[] __initdata = { "mmc1_sample", "mmc1_pad", }; +static const char *mmc2_sample_in[] __initdata = { "mmc2_sample", "mmc2_pad", }; +static const char *uart1_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *uart2_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *uart3_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *uart4_src[] __initdata = { "clk_tcxo", "clk_150m", }; +static const char *hifi_src[] __initdata = { "syspll", "pll_media_gate", }; + +static struct hisi_gate_clock hi6220_separated_gate_clks_sys[] __initdata = { + { HI6220_MMC0_CLK, "mmc0_clk", "mmc0_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 0, 0, }, + { HI6220_MMC0_CIUCLK, "mmc0_ciuclk", "mmc0_smp_in", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 0, 0, }, + { HI6220_MMC1_CLK, "mmc1_clk", "mmc1_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 1, 0, }, + { HI6220_MMC1_CIUCLK, "mmc1_ciuclk", "mmc1_smp_in", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 1, 0, }, + { HI6220_MMC2_CLK, "mmc2_clk", "mmc2_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 2, 0, }, + { HI6220_MMC2_CIUCLK, "mmc2_ciuclk", "mmc2_smp_in", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 2, 0, }, + { HI6220_USBOTG_HCLK, "usbotg_hclk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 4, 0, }, + { HI6220_CLK_PICOPHY, "clk_picophy", "cs_dapb", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x200, 5, 0, }, + { HI6220_HIFI, "hifi_clk", "hifi_div", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x210, 0, 0, }, + { HI6220_DACODEC_PCLK, "dacodec_pclk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x210, 5, 0, }, + { HI6220_EDMAC_ACLK, "edmac_aclk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x220, 2, 0, }, + { HI6220_CS_ATB, "cs_atb", "cs_atb_div", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 0, 0, }, + { HI6220_I2C0_CLK, "i2c0_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 1, 0, }, + { HI6220_I2C1_CLK, "i2c1_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 2, 0, }, + { HI6220_I2C2_CLK, "i2c2_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 3, 0, }, + { HI6220_I2C3_CLK, "i2c3_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 4, 0, }, + { HI6220_UART1_PCLK, "uart1_pclk", "uart1_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 5, 0, }, + { HI6220_UART2_PCLK, "uart2_pclk", "uart2_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 6, 0, }, + { HI6220_UART3_PCLK, "uart3_pclk", "uart3_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 7, 0, }, + { HI6220_UART4_PCLK, "uart4_pclk", "uart4_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 8, 0, }, + { HI6220_SPI_CLK, "spi_clk", "clk_150m", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 9, 0, }, + { HI6220_TSENSOR_CLK, "tsensor_clk", "clk_bus", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x230, 12, 0, }, + { HI6220_MMU_CLK, "mmu_clk", "ddrc_axi1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x240, 11, 0, }, + { HI6220_HIFI_SEL, "hifi_sel", "hifi_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 0, 0, }, + { HI6220_MMC0_SYSPLL, "mmc0_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 1, 0, }, + { HI6220_MMC1_SYSPLL, "mmc1_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 2, 0, }, + { HI6220_MMC2_SYSPLL, "mmc2_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 3, 0, }, + { HI6220_MMC0_SEL, "mmc0_sel", "mmc0_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 6, 0, }, + { HI6220_MMC1_SEL, "mmc1_sel", "mmc1_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 7, 0, }, + { HI6220_BBPPLL_SEL, "bbppll_sel", "pll0_bbp_gate", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 9, 0, }, + { HI6220_MEDIA_PLL_SRC, "media_pll_src", "pll_media_gate", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 10, 0, }, + { HI6220_MMC2_SEL, "mmc2_sel", "mmc2_mux1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 11, 0, }, + { HI6220_CS_ATB_SYSPLL, "cs_atb_syspll", "syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x270, 12, 0, }, +}; + +static struct hisi_mux_clock hi6220_mux_clks_sys[] __initdata = { + { HI6220_MMC0_SRC, "mmc0_src", mmc0_src_p, ARRAY_SIZE(mmc0_src_p), CLK_SET_RATE_PARENT, 0x4, 0, 1, 0, }, + { HI6220_MMC0_SMP_IN, "mmc0_smp_in", mmc0_sample_in, ARRAY_SIZE(mmc0_sample_in), CLK_SET_RATE_PARENT, 0x4, 0, 1, 0, }, + { HI6220_MMC1_SRC, "mmc1_src", mmc1_src_p, ARRAY_SIZE(mmc1_src_p), CLK_SET_RATE_PARENT, 0x4, 2, 1, 0, }, + { HI6220_MMC1_SMP_IN, "mmc1_smp_in", mmc1_sample_in, ARRAY_SIZE(mmc1_sample_in), CLK_SET_RATE_PARENT, 0x4, 2, 1, 0, }, + { HI6220_MMC2_SRC, "mmc2_src", mmc2_src_p, ARRAY_SIZE(mmc2_src_p), CLK_SET_RATE_PARENT, 0x4, 4, 1, 0, }, + { HI6220_MMC2_SMP_IN, "mmc2_smp_in", mmc2_sample_in, ARRAY_SIZE(mmc2_sample_in), CLK_SET_RATE_PARENT, 0x4, 4, 1, 0, }, + { HI6220_HIFI_SRC, "hifi_src", hifi_src, ARRAY_SIZE(hifi_src), CLK_SET_RATE_PARENT, 0x400, 0, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART1_SRC, "uart1_src", uart1_src, ARRAY_SIZE(uart1_src), CLK_SET_RATE_PARENT, 0x400, 1, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART2_SRC, "uart2_src", uart2_src, ARRAY_SIZE(uart2_src), CLK_SET_RATE_PARENT, 0x400, 2, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART3_SRC, "uart3_src", uart3_src, ARRAY_SIZE(uart3_src), CLK_SET_RATE_PARENT, 0x400, 3, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_UART4_SRC, "uart4_src", uart4_src, ARRAY_SIZE(uart4_src), CLK_SET_RATE_PARENT, 0x400, 4, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC0_MUX0, "mmc0_mux0", mmc0_mux0_p, ARRAY_SIZE(mmc0_mux0_p), CLK_SET_RATE_PARENT, 0x400, 5, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC1_MUX0, "mmc1_mux0", mmc1_mux0_p, ARRAY_SIZE(mmc1_mux0_p), CLK_SET_RATE_PARENT, 0x400, 11, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC2_MUX0, "mmc2_mux0", mmc2_mux0_p, ARRAY_SIZE(mmc2_mux0_p), CLK_SET_RATE_PARENT, 0x400, 12, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC0_MUX1, "mmc0_mux1", mmc0_mux1_p, ARRAY_SIZE(mmc0_mux1_p), CLK_SET_RATE_PARENT, 0x400, 13, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC1_MUX1, "mmc1_mux1", mmc1_mux1_p, ARRAY_SIZE(mmc1_mux1_p), CLK_SET_RATE_PARENT, 0x400, 14, 1, CLK_MUX_HIWORD_MASK,}, + { HI6220_MMC2_MUX1, "mmc2_mux1", mmc2_mux1_p, ARRAY_SIZE(mmc2_mux1_p), CLK_SET_RATE_PARENT, 0x400, 15, 1, CLK_MUX_HIWORD_MASK,}, +}; + +static struct hi6220_divider_clock hi6220_div_clks_sys[] __initdata = { + { HI6220_CLK_BUS, "clk_bus", "clk_300m", CLK_SET_RATE_PARENT, 0x490, 0, 4, 7, }, + { HI6220_MMC0_DIV, "mmc0_div", "mmc0_syspll", CLK_SET_RATE_PARENT, 0x494, 0, 6, 7, }, + { HI6220_MMC1_DIV, "mmc1_div", "mmc1_syspll", CLK_SET_RATE_PARENT, 0x498, 0, 6, 7, }, + { HI6220_MMC2_DIV, "mmc2_div", "mmc2_syspll", CLK_SET_RATE_PARENT, 0x49c, 0, 6, 7, }, + { HI6220_HIFI_DIV, "hifi_div", "hifi_sel", CLK_SET_RATE_PARENT, 0x4a0, 0, 4, 7, }, + { HI6220_BBPPLL0_DIV, "bbppll0_div", "bbppll_sel", CLK_SET_RATE_PARENT, 0x4a0, 8, 6, 15,}, + { HI6220_CS_DAPB, "cs_dapb", "picophy_src", CLK_SET_RATE_PARENT, 0x4a0, 24, 2, 31,}, + { HI6220_CS_ATB_DIV, "cs_atb_div", "cs_atb_syspll", CLK_SET_RATE_PARENT, 0x4a4, 0, 4, 7, }, +}; + +static void __init hi6220_clk_sys_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HI6220_SYS_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_gate_sep(hi6220_separated_gate_clks_sys, + ARRAY_SIZE(hi6220_separated_gate_clks_sys), clk_data); + + hisi_clk_register_mux(hi6220_mux_clks_sys, + ARRAY_SIZE(hi6220_mux_clks_sys), clk_data); + + hi6220_clk_register_divider(hi6220_div_clks_sys, + ARRAY_SIZE(hi6220_div_clks_sys), clk_data); +} +CLK_OF_DECLARE(hi6220_clk_sys, "hisilicon,hi6220-sysctrl", hi6220_clk_sys_init); + + +/* clocks in media controller */ +static const char *clk_1000_1200_src[] __initdata = { "pll_gpu_gate", "media_syspll_src", }; +static const char *clk_1440_1200_src[] __initdata = { "media_syspll_src", "media_pll_src", }; +static const char *clk_1000_1440_src[] __initdata = { "pll_gpu_gate", "media_pll_src", }; + +static struct hisi_gate_clock hi6220_separated_gate_clks_media[] __initdata = { + { HI6220_DSI_PCLK, "dsi_pclk", "vpucodec", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 0, 0, }, + { HI6220_G3D_PCLK, "g3d_pclk", "vpucodec", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 1, 0, }, + { HI6220_ACLK_CODEC_VPU, "aclk_codec_vpu", "ade_core_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 3, 0, }, + { HI6220_ISP_SCLK, "isp_sclk", "isp_sclk_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 5, 0, }, + { HI6220_ADE_CORE, "ade_core", "ade_core_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 6, 0, }, + { HI6220_MED_MMU, "media_mmu", "mmu_clk", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 8, 0, }, + { HI6220_CFG_CSI4PHY, "cfg_csi4phy", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 9, 0, }, + { HI6220_CFG_CSI2PHY, "cfg_csi2phy", "clk_tcxo", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 10, 0, }, + { HI6220_ISP_SCLK_GATE, "isp_sclk_gate", "media_pll_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 11, 0, }, + { HI6220_ISP_SCLK_GATE1, "isp_sclk_gate1", "media_pll_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 12, 0, }, + { HI6220_ADE_CORE_GATE, "ade_core_gate", "media_pll_src", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 14, 0, }, + { HI6220_CODEC_VPU_GATE, "codec_vpu_gate", "clk_1000_1440", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 15, 0, }, + { HI6220_MED_SYSPLL, "media_syspll_src", "media_syspll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x520, 17, 0, }, +}; + +static struct hisi_mux_clock hi6220_mux_clks_media[] __initdata = { + { HI6220_1440_1200, "clk_1440_1200", clk_1440_1200_src, ARRAY_SIZE(clk_1440_1200_src), CLK_SET_RATE_PARENT, 0x51c, 0, 1, 0, }, + { HI6220_1000_1200, "clk_1000_1200", clk_1000_1200_src, ARRAY_SIZE(clk_1000_1200_src), CLK_SET_RATE_PARENT, 0x51c, 1, 1, 0, }, + { HI6220_1000_1440, "clk_1000_1440", clk_1000_1440_src, ARRAY_SIZE(clk_1000_1440_src), CLK_SET_RATE_PARENT, 0x51c, 6, 1, 0, }, +}; + +static struct hi6220_divider_clock hi6220_div_clks_media[] __initdata = { + { HI6220_CODEC_JPEG, "codec_jpeg_aclk", "media_pll_src", CLK_SET_RATE_PARENT, 0xcbc, 0, 4, 23, }, + { HI6220_ISP_SCLK_SRC, "isp_sclk_src", "isp_sclk_gate", CLK_SET_RATE_PARENT, 0xcbc, 8, 4, 15, }, + { HI6220_ISP_SCLK1, "isp_sclk1", "isp_sclk_gate1", CLK_SET_RATE_PARENT, 0xcbc, 24, 4, 31, }, + { HI6220_ADE_CORE_SRC, "ade_core_src", "ade_core_gate", CLK_SET_RATE_PARENT, 0xcc0, 16, 3, 23, }, + { HI6220_ADE_PIX_SRC, "ade_pix_src", "clk_1440_1200", CLK_SET_RATE_PARENT, 0xcc0, 24, 6, 31, }, + { HI6220_G3D_CLK, "g3d_clk", "clk_1000_1200", CLK_SET_RATE_PARENT, 0xcc4, 8, 4, 15, }, + { HI6220_CODEC_VPU_SRC, "codec_vpu_src", "codec_vpu_gate", CLK_SET_RATE_PARENT, 0xcc4, 24, 6, 31, }, +}; + +static void __init hi6220_clk_media_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HI6220_MEDIA_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_gate_sep(hi6220_separated_gate_clks_media, + ARRAY_SIZE(hi6220_separated_gate_clks_media), clk_data); + + hisi_clk_register_mux(hi6220_mux_clks_media, + ARRAY_SIZE(hi6220_mux_clks_media), clk_data); + + hi6220_clk_register_divider(hi6220_div_clks_media, + ARRAY_SIZE(hi6220_div_clks_media), clk_data); +} +CLK_OF_DECLARE(hi6220_clk_media, "hisilicon,hi6220-mediactrl", hi6220_clk_media_init); + + +/* clocks in pmctrl */ +static struct hisi_gate_clock hi6220_gate_clks_power[] __initdata = { + { HI6220_PLL_GPU_GATE, "pll_gpu_gate", "gpupll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x8, 0, 0, }, + { HI6220_PLL1_DDR_GATE, "pll1_ddr_gate", "ddrpll1", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x10, 0, 0, }, + { HI6220_PLL_DDR_GATE, "pll_ddr_gate", "ddrpll0", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x18, 0, 0, }, + { HI6220_PLL_MEDIA_GATE, "pll_media_gate", "media_pll", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x38, 0, 0, }, + { HI6220_PLL0_BBP_GATE, "pll0_bbp_gate", "bbppll0", CLK_SET_RATE_PARENT|CLK_IGNORE_UNUSED, 0x48, 0, 0, }, +}; + +static struct hi6220_divider_clock hi6220_div_clks_power[] __initdata = { + { HI6220_DDRC_SRC, "ddrc_src", "ddr_sel_src", CLK_SET_RATE_PARENT, 0x5a8, 0, 4, 0, }, + { HI6220_DDRC_AXI1, "ddrc_axi1", "ddrc_src", CLK_SET_RATE_PARENT, 0x5a8, 8, 2, 0, }, +}; + +static void __init hi6220_clk_power_init(struct device_node *np) +{ + struct hisi_clock_data *clk_data; + + clk_data = hisi_clk_init(np, HI6220_POWER_NR_CLKS); + if (!clk_data) + return; + + hisi_clk_register_gate(hi6220_gate_clks_power, + ARRAY_SIZE(hi6220_gate_clks_power), clk_data); + + hi6220_clk_register_divider(hi6220_div_clks_power, + ARRAY_SIZE(hi6220_div_clks_power), clk_data); +} +CLK_OF_DECLARE(hi6220_clk_power, "hisilicon,hi6220-pmctrl", hi6220_clk_power_init); diff --git a/drivers/clk/hisilicon/clk.c b/drivers/clk/hisilicon/clk.c index a078e84f7b05..c90a89739b03 100644 --- a/drivers/clk/hisilicon/clk.c +++ b/drivers/clk/hisilicon/clk.c @@ -232,3 +232,32 @@ void __init hisi_clk_register_gate_sep(struct hisi_gate_clock *clks, data->clk_data.clks[clks[i].id] = clk; } } + +void __init hi6220_clk_register_divider(struct hi6220_divider_clock *clks, + int nums, struct hisi_clock_data *data) +{ + struct clk *clk; + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + clk = hi6220_register_clkdiv(NULL, clks[i].name, + clks[i].parent_name, + clks[i].flags, + base + clks[i].offset, + clks[i].shift, + clks[i].width, + clks[i].mask_bit, + &hisi_clk_lock); + if (IS_ERR(clk)) { + pr_err("%s: failed to register clock %s\n", + __func__, clks[i].name); + continue; + } + + if (clks[i].alias) + clk_register_clkdev(clk, clks[i].alias, NULL); + + data->clk_data.clks[clks[i].id] = clk; + } +} diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h index 6b6f99448e01..21b403b3423a 100644 --- a/drivers/clk/hisilicon/clk.h +++ b/drivers/clk/hisilicon/clk.h @@ -79,6 +79,18 @@ struct hisi_divider_clock { const char *alias; }; +struct hi6220_divider_clock { + unsigned int id; + const char *name; + const char *parent_name; + unsigned long flags; + unsigned long offset; + u8 shift; + u8 width; + u32 mask_bit; + const char *alias; +}; + struct hisi_gate_clock { unsigned int id; const char *name; @@ -94,6 +106,9 @@ struct clk *hisi_register_clkgate_sep(struct device *, const char *, const char *, unsigned long, void __iomem *, u8, u8, spinlock_t *); +struct clk *hi6220_register_clkdiv(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, void __iomem *reg, + u8 shift, u8 width, u32 mask_bit, spinlock_t *lock); struct hisi_clock_data *hisi_clk_init(struct device_node *, int); void hisi_clk_register_fixed_rate(struct hisi_fixed_rate_clock *, @@ -108,4 +123,6 @@ void hisi_clk_register_gate(struct hisi_gate_clock *, int, struct hisi_clock_data *); void hisi_clk_register_gate_sep(struct hisi_gate_clock *, int, struct hisi_clock_data *); +void hi6220_clk_register_divider(struct hi6220_divider_clock *, + int, struct hisi_clock_data *); #endif /* __HISI_CLK_H */ diff --git a/drivers/clk/hisilicon/clkdivider-hi6220.c b/drivers/clk/hisilicon/clkdivider-hi6220.c new file mode 100644 index 000000000000..113eee8ed23a --- /dev/null +++ b/drivers/clk/hisilicon/clkdivider-hi6220.c @@ -0,0 +1,156 @@ +/* + * Hisilicon hi6220 SoC divider clock driver + * + * Copyright (c) 2015 Hisilicon Limited. + * + * Author: Bintian Wang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include + +#define div_mask(width) ((1 << (width)) - 1) + +/** + * struct hi6220_clk_divider - divider clock for hi6220 + * + * @hw: handle between common and hardware-specific interfaces + * @reg: register containing divider + * @shift: shift to the divider bit field + * @width: width of the divider bit field + * @mask: mask for setting divider rate + * @table: the div table that the divider supports + * @lock: register lock + */ +struct hi6220_clk_divider { + struct clk_hw hw; + void __iomem *reg; + u8 shift; + u8 width; + u32 mask; + const struct clk_div_table *table; + spinlock_t *lock; +}; + +#define to_hi6220_clk_divider(_hw) \ + container_of(_hw, struct hi6220_clk_divider, hw) + +static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + unsigned int val; + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + val = readl_relaxed(dclk->reg) >> dclk->shift; + val &= div_mask(dclk->width); + + return divider_recalc_rate(hw, parent_rate, val, dclk->table, + CLK_DIVIDER_ROUND_CLOSEST); +} + +static long hi6220_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + return divider_round_rate(hw, rate, prate, dclk->table, + dclk->width, CLK_DIVIDER_ROUND_CLOSEST); +} + +static int hi6220_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + int value; + unsigned long flags = 0; + u32 data; + struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw); + + value = divider_get_val(rate, parent_rate, dclk->table, + dclk->width, CLK_DIVIDER_ROUND_CLOSEST); + + if (dclk->lock) + spin_lock_irqsave(dclk->lock, flags); + + data = readl_relaxed(dclk->reg); + data &= ~(div_mask(dclk->width) << dclk->shift); + data |= value << dclk->shift; + data |= dclk->mask; + + writel_relaxed(data, dclk->reg); + + if (dclk->lock) + spin_unlock_irqrestore(dclk->lock, flags); + + return 0; +} + +static const struct clk_ops hi6220_clkdiv_ops = { + .recalc_rate = hi6220_clkdiv_recalc_rate, + .round_rate = hi6220_clkdiv_round_rate, + .set_rate = hi6220_clkdiv_set_rate, +}; + +struct clk *hi6220_register_clkdiv(struct device *dev, const char *name, + const char *parent_name, unsigned long flags, void __iomem *reg, + u8 shift, u8 width, u32 mask_bit, spinlock_t *lock) +{ + struct hi6220_clk_divider *div; + struct clk *clk; + struct clk_init_data init; + struct clk_div_table *table; + u32 max_div, min_div; + int i; + + /* allocate the divider */ + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return ERR_PTR(-ENOMEM); + + /* Init the divider table */ + max_div = div_mask(width) + 1; + min_div = 1; + + table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL); + if (!table) { + kfree(div); + return ERR_PTR(-ENOMEM); + } + + for (i = 0; i < max_div; i++) { + table[i].div = min_div + i; + table[i].val = table[i].div - 1; + } + + init.name = name; + init.ops = &hi6220_clkdiv_ops; + init.flags = flags; + init.parent_names = parent_name ? &parent_name : NULL; + init.num_parents = parent_name ? 1 : 0; + + /* struct hi6220_clk_divider assignments */ + div->reg = reg; + div->shift = shift; + div->width = width; + div->mask = mask_bit ? BIT(mask_bit) : 0; + div->lock = lock; + div->hw.init = &init; + div->table = table; + + /* register the clock */ + clk = clk_register(dev, &div->hw); + if (IS_ERR(clk)) { + kfree(table); + kfree(div); + } + + return clk; +} -- cgit v1.2.3-58-ga151 From 4d52b2acefdfceae0e47ed08324a96f511dc80b1 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 26 May 2015 14:42:57 +0200 Subject: clk: mvebu: add missing CESA gate clk Even if not documented in the datasheet, the Armada 370 SoC can actually gate the CESA (crypto engine) clock. Add an entry in the gating_desc table to be able to reference the CESA gateclk in the crypto node. Signed-off-by: Boris Brezillon Acked-by: Gregory CLEMENT Signed-off-by: Michael Turquette --- Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt | 1 + drivers/clk/mvebu/armada-370.c | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt index 31c7c0c1ce8f..660e64912cce 100644 --- a/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt +++ b/Documentation/devicetree/bindings/clock/mvebu-gated-clock.txt @@ -19,6 +19,7 @@ ID Clock Peripheral 9 pex1 PCIe Cntrl 1 15 sata0 SATA Host 0 17 sdio SDHCI Host +23 crypto CESA (crypto engine) 25 tdm Time Division Mplx 28 ddr DDR Cntrl 30 sata1 SATA Host 0 diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c index 756f0f39d6a3..c19fd77e6c27 100644 --- a/drivers/clk/mvebu/armada-370.c +++ b/drivers/clk/mvebu/armada-370.c @@ -163,6 +163,7 @@ static const struct clk_gating_soc_desc a370_gating_desc[] __initconst = { { "pex1", "pex1_en", 9, 0 }, { "sata0", NULL, 15, 0 }, { "sdio", NULL, 17, 0 }, + { "crypto", NULL, 23, 0 }, { "tdm", NULL, 25, 0 }, { "ddr", NULL, 28, CLK_IGNORE_UNUSED }, { "sata1", NULL, 30, 0 }, -- cgit v1.2.3-58-ga151 From 19fbbbbcd3a3a8e307a4768784166abf7b55b779 Mon Sep 17 00:00:00 2001 From: Mike Looijmans Date: Wed, 3 Jun 2015 07:25:19 +0200 Subject: Add TI CDCE925 I2C controlled clock synthesizer driver This driver supports the TI CDCE925 programmable clock synthesizer. The chip contains two PLLs with spread-spectrum clocking support and five output dividers. The driver only supports the following setup, and uses a fixed setting for the output muxes: Y1 is derived from the input clock Y2 and Y3 derive from PLL1 Y4 and Y5 derive from PLL2 Given a target output frequency, the driver will set the PLL and divider to best approximate the desired output. Signed-off-by: Mike Looijmans Signed-off-by: Michael Turquette --- .../devicetree/bindings/clock/ti,cdce925.txt | 42 ++ drivers/clk/Kconfig | 17 + drivers/clk/Makefile | 1 + drivers/clk/clk-cdce925.c | 749 +++++++++++++++++++++ 4 files changed, 809 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/ti,cdce925.txt create mode 100644 drivers/clk/clk-cdce925.c diff --git a/Documentation/devicetree/bindings/clock/ti,cdce925.txt b/Documentation/devicetree/bindings/clock/ti,cdce925.txt new file mode 100644 index 000000000000..4c7669ad681b --- /dev/null +++ b/Documentation/devicetree/bindings/clock/ti,cdce925.txt @@ -0,0 +1,42 @@ +Binding for TO CDCE925 programmable I2C clock synthesizers. + +Reference +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt +[2] http://www.ti.com/product/cdce925 + +The driver provides clock sources for each output Y1 through Y5. + +Required properties: + - compatible: Shall be "ti,cdce925" + - reg: I2C device address. + - clocks: Points to a fixed parent clock that provides the input frequency. + - #clock-cells: From common clock bindings: Shall be 1. + +Optional properties: + - xtal-load-pf: Crystal load-capacitor value to fine-tune performance on a + board, or to compensate for external influences. + +For both PLL1 and PLL2 an optional child node can be used to specify spread +spectrum clocking parameters for a board. + - spread-spectrum: SSC mode as defined in the data sheet. + - spread-spectrum-center: Use "centered" mode instead of "max" mode. When + present, the clock runs at the requested frequency on average. Otherwise + the requested frequency is the maximum value of the SCC range. + + +Example: + + clockgen: cdce925pw@64 { + compatible = "cdce925"; + reg = <0x64>; + clocks = <&xtal_27Mhz>; + #clock-cells = <1>; + xtal-load-pf = <5>; + /* PLL options to get SSC 1% centered */ + PLL2 { + spread-spectrum = <4>; + spread-spectrum-center; + }; + }; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 67e3a84d2805..e33ff4e3e18d 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -78,6 +78,23 @@ config COMMON_CLK_SI570 This driver supports Silicon Labs 570/571/598/599 programmable clock generators. +config COMMON_CLK_CDCE925 + tristate "Clock driver for TI CDCE925 devices" + depends on I2C + depends on OF + select REGMAP_I2C + help + ---help--- + This driver supports the TI CDCE925 programmable clock synthesizer. + The chip contains two PLLs with spread-spectrum clocking support and + five output dividers. The driver only supports the following setup, + and uses a fixed setting for the output muxes. + Y1 is derived from the input clock + Y2 and Y3 derive from PLL1 + Y4 and Y5 derive from PLL2 + Given a target output frequency, the driver will set the PLL and + divider to best approximate the desired output. + config COMMON_CLK_S2MPS11 tristate "Clock driver for S2MPS1X/S5M8767 MFD" depends on MFD_SEC_CORE diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d965b3f6a372..00ae2de0803a 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o +obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o obj-$(CONFIG_ARCH_U300) += clk-u300.o obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c new file mode 100644 index 000000000000..56b870d331a1 --- /dev/null +++ b/drivers/clk/clk-cdce925.c @@ -0,0 +1,749 @@ +/* + * Driver for TI Dual PLL CDCE925 clock synthesizer + * + * This driver always connects the Y1 to the input clock, Y2/Y3 to PLL1 + * and Y4/Y5 to PLL2. PLL frequency is set on a first-come-first-serve + * basis. Clients can directly request any frequency that the chip can + * deliver using the standard clk framework. In addition, the device can + * be configured and activated via the devicetree. + * + * Copyright (C) 2014, Topic Embedded Products + * Licenced under GPL + */ +#include +#include +#include +#include +#include +#include +#include + +/* The chip has 2 PLLs which can be routed through dividers to 5 outputs. + * Model this as 2 PLL clocks which are parents to the outputs. + */ +#define NUMBER_OF_PLLS 2 +#define NUMBER_OF_OUTPUTS 5 + +#define CDCE925_REG_GLOBAL1 0x01 +#define CDCE925_REG_Y1SPIPDIVH 0x02 +#define CDCE925_REG_PDIVL 0x03 +#define CDCE925_REG_XCSEL 0x05 +/* PLL parameters start at 0x10, steps of 0x10 */ +#define CDCE925_OFFSET_PLL 0x10 +/* Add CDCE925_OFFSET_PLL * (pll) to these registers before sending */ +#define CDCE925_PLL_MUX_OUTPUTS 0x14 +#define CDCE925_PLL_MULDIV 0x18 + +#define CDCE925_PLL_FREQUENCY_MIN 80000000ul +#define CDCE925_PLL_FREQUENCY_MAX 230000000ul +struct clk_cdce925_chip; + +struct clk_cdce925_output { + struct clk_hw hw; + struct clk_cdce925_chip *chip; + u8 index; + u16 pdiv; /* 1..127 for Y2-Y5; 1..1023 for Y1 */ +}; +#define to_clk_cdce925_output(_hw) \ + container_of(_hw, struct clk_cdce925_output, hw) + +struct clk_cdce925_pll { + struct clk_hw hw; + struct clk_cdce925_chip *chip; + u8 index; + u16 m; /* 1..511 */ + u16 n; /* 1..4095 */ +}; +#define to_clk_cdce925_pll(_hw) container_of(_hw, struct clk_cdce925_pll, hw) + +struct clk_cdce925_chip { + struct regmap *regmap; + struct i2c_client *i2c_client; + struct clk_cdce925_pll pll[NUMBER_OF_PLLS]; + struct clk_cdce925_output clk[NUMBER_OF_OUTPUTS]; + struct clk *dt_clk[NUMBER_OF_OUTPUTS]; + struct clk_onecell_data onecell; +}; + +/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ + +static unsigned long cdce925_pll_calculate_rate(unsigned long parent_rate, + u16 n, u16 m) +{ + if ((!m || !n) || (m == n)) + return parent_rate; /* In bypass mode runs at same frequency */ + return mult_frac(parent_rate, (unsigned long)n, (unsigned long)m); +} + +static unsigned long cdce925_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + /* Output frequency of PLL is Fout = (Fin/Pdiv)*(N/M) */ + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); + + return cdce925_pll_calculate_rate(parent_rate, data->n, data->m); +} + +static void cdce925_pll_find_rate(unsigned long rate, + unsigned long parent_rate, u16 *n, u16 *m) +{ + unsigned long un; + unsigned long um; + unsigned long g; + + if (rate <= parent_rate) { + /* Can always deliver parent_rate in bypass mode */ + rate = parent_rate; + *n = 0; + *m = 0; + } else { + /* In PLL mode, need to apply min/max range */ + if (rate < CDCE925_PLL_FREQUENCY_MIN) + rate = CDCE925_PLL_FREQUENCY_MIN; + else if (rate > CDCE925_PLL_FREQUENCY_MAX) + rate = CDCE925_PLL_FREQUENCY_MAX; + + g = gcd(rate, parent_rate); + um = parent_rate / g; + un = rate / g; + /* When outside hw range, reduce to fit (rounding errors) */ + while ((un > 4095) || (um > 511)) { + un >>= 1; + um >>= 1; + } + if (un == 0) + un = 1; + if (um == 0) + um = 1; + + *n = un; + *m = um; + } +} + +static long cdce925_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + u16 n, m; + + cdce925_pll_find_rate(rate, *parent_rate, &n, &m); + return (long)cdce925_pll_calculate_rate(*parent_rate, n, m); +} + +static int cdce925_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); + + if (!rate || (rate == parent_rate)) { + data->m = 0; /* Bypass mode */ + data->n = 0; + return 0; + } + + if ((rate < CDCE925_PLL_FREQUENCY_MIN) || + (rate > CDCE925_PLL_FREQUENCY_MAX)) { + pr_debug("%s: rate %lu outside PLL range.\n", __func__, rate); + return -EINVAL; + } + + if (rate < parent_rate) { + pr_debug("%s: rate %lu less than parent rate %lu.\n", __func__, + rate, parent_rate); + return -EINVAL; + } + + cdce925_pll_find_rate(rate, parent_rate, &data->n, &data->m); + return 0; +} + + +/* calculate p = max(0, 4 - int(log2 (n/m))) */ +static u8 cdce925_pll_calc_p(u16 n, u16 m) +{ + u8 p; + u16 r = n / m; + + if (r >= 16) + return 0; + p = 4; + while (r > 1) { + r >>= 1; + --p; + } + return p; +} + +/* Returns VCO range bits for VCO1_0_RANGE */ +static u8 cdce925_pll_calc_range_bits(struct clk_hw *hw, u16 n, u16 m) +{ + struct clk *parent = clk_get_parent(hw->clk); + unsigned long rate = clk_get_rate(parent); + + rate = mult_frac(rate, (unsigned long)n, (unsigned long)m); + if (rate >= 175000000) + return 0x3; + if (rate >= 150000000) + return 0x02; + if (rate >= 125000000) + return 0x01; + return 0x00; +} + +/* I2C clock, hence everything must happen in (un)prepare because this + * may sleep */ +static int cdce925_pll_prepare(struct clk_hw *hw) +{ + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); + u16 n = data->n; + u16 m = data->m; + u16 r; + u8 q; + u8 p; + u16 nn; + u8 pll[4]; /* Bits are spread out over 4 byte registers */ + u8 reg_ofs = data->index * CDCE925_OFFSET_PLL; + unsigned i; + + if ((!m || !n) || (m == n)) { + /* Set PLL mux to bypass mode, leave the rest as is */ + regmap_update_bits(data->chip->regmap, + reg_ofs + CDCE925_PLL_MUX_OUTPUTS, 0x80, 0x80); + } else { + /* According to data sheet: */ + /* p = max(0, 4 - int(log2 (n/m))) */ + p = cdce925_pll_calc_p(n, m); + /* nn = n * 2^p */ + nn = n * BIT(p); + /* q = int(nn/m) */ + q = nn / m; + if ((q < 16) || (1 > 64)) { + pr_debug("%s invalid q=%d\n", __func__, q); + return -EINVAL; + } + r = nn - (m*q); + if (r > 511) { + pr_debug("%s invalid r=%d\n", __func__, r); + return -EINVAL; + } + pr_debug("%s n=%d m=%d p=%d q=%d r=%d\n", __func__, + n, m, p, q, r); + /* encode into register bits */ + pll[0] = n >> 4; + pll[1] = ((n & 0x0F) << 4) | ((r >> 5) & 0x0F); + pll[2] = ((r & 0x1F) << 3) | ((q >> 3) & 0x07); + pll[3] = ((q & 0x07) << 5) | (p << 2) | + cdce925_pll_calc_range_bits(hw, n, m); + /* Write to registers */ + for (i = 0; i < ARRAY_SIZE(pll); ++i) + regmap_write(data->chip->regmap, + reg_ofs + CDCE925_PLL_MULDIV + i, pll[i]); + /* Enable PLL */ + regmap_update_bits(data->chip->regmap, + reg_ofs + CDCE925_PLL_MUX_OUTPUTS, 0x80, 0x00); + } + + return 0; +} + +static void cdce925_pll_unprepare(struct clk_hw *hw) +{ + struct clk_cdce925_pll *data = to_clk_cdce925_pll(hw); + u8 reg_ofs = data->index * CDCE925_OFFSET_PLL; + + regmap_update_bits(data->chip->regmap, + reg_ofs + CDCE925_PLL_MUX_OUTPUTS, 0x80, 0x80); +} + +static const struct clk_ops cdce925_pll_ops = { + .prepare = cdce925_pll_prepare, + .unprepare = cdce925_pll_unprepare, + .recalc_rate = cdce925_pll_recalc_rate, + .round_rate = cdce925_pll_round_rate, + .set_rate = cdce925_pll_set_rate, +}; + + +static void cdce925_clk_set_pdiv(struct clk_cdce925_output *data, u16 pdiv) +{ + switch (data->index) { + case 0: + regmap_update_bits(data->chip->regmap, + CDCE925_REG_Y1SPIPDIVH, + 0x03, (pdiv >> 8) & 0x03); + regmap_write(data->chip->regmap, 0x03, pdiv & 0xFF); + break; + case 1: + regmap_update_bits(data->chip->regmap, 0x16, 0x7F, pdiv); + break; + case 2: + regmap_update_bits(data->chip->regmap, 0x17, 0x7F, pdiv); + break; + case 3: + regmap_update_bits(data->chip->regmap, 0x26, 0x7F, pdiv); + break; + case 4: + regmap_update_bits(data->chip->regmap, 0x27, 0x7F, pdiv); + break; + } +} + +static void cdce925_clk_activate(struct clk_cdce925_output *data) +{ + switch (data->index) { + case 0: + regmap_update_bits(data->chip->regmap, + CDCE925_REG_Y1SPIPDIVH, 0x0c, 0x0c); + break; + case 1: + case 2: + regmap_update_bits(data->chip->regmap, 0x14, 0x03, 0x03); + break; + case 3: + case 4: + regmap_update_bits(data->chip->regmap, 0x24, 0x03, 0x03); + break; + } +} + +static int cdce925_clk_prepare(struct clk_hw *hw) +{ + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); + + cdce925_clk_set_pdiv(data, data->pdiv); + cdce925_clk_activate(data); + return 0; +} + +static void cdce925_clk_unprepare(struct clk_hw *hw) +{ + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); + + /* Disable clock by setting divider to "0" */ + cdce925_clk_set_pdiv(data, 0); +} + +static unsigned long cdce925_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); + + if (data->pdiv) + return parent_rate / data->pdiv; + return 0; +} + +static u16 cdce925_calc_divider(unsigned long rate, + unsigned long parent_rate) +{ + unsigned long divider; + + if (!rate) + return 0; + if (rate >= parent_rate) + return 1; + + divider = DIV_ROUND_CLOSEST(parent_rate, rate); + if (divider > 0x7F) + divider = 0x7F; + + return (u16)divider; +} + +static unsigned long cdce925_clk_best_parent_rate( + struct clk_hw *hw, unsigned long rate) +{ + struct clk *pll = clk_get_parent(hw->clk); + struct clk *root = clk_get_parent(pll); + unsigned long root_rate = clk_get_rate(root); + unsigned long best_rate_error = rate; + u16 pdiv_min; + u16 pdiv_max; + u16 pdiv_best; + u16 pdiv_now; + + if (root_rate % rate == 0) + return root_rate; /* Don't need the PLL, use bypass */ + + pdiv_min = (u16)max(1ul, DIV_ROUND_UP(CDCE925_PLL_FREQUENCY_MIN, rate)); + pdiv_max = (u16)min(127ul, CDCE925_PLL_FREQUENCY_MAX / rate); + + if (pdiv_min > pdiv_max) + return 0; /* No can do? */ + + pdiv_best = pdiv_min; + for (pdiv_now = pdiv_min; pdiv_now < pdiv_max; ++pdiv_now) { + unsigned long target_rate = rate * pdiv_now; + long pll_rate = clk_round_rate(pll, target_rate); + unsigned long actual_rate; + unsigned long rate_error; + + if (pll_rate <= 0) + continue; + actual_rate = pll_rate / pdiv_now; + rate_error = abs((long)actual_rate - (long)rate); + if (rate_error < best_rate_error) { + pdiv_best = pdiv_now; + best_rate_error = rate_error; + } + /* TODO: Consider PLL frequency based on smaller n/m values + * and pick the better one if the error is equal */ + } + + return rate * pdiv_best; +} + +static long cdce925_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long l_parent_rate = *parent_rate; + u16 divider = cdce925_calc_divider(rate, l_parent_rate); + + if (l_parent_rate / divider != rate) { + l_parent_rate = cdce925_clk_best_parent_rate(hw, rate); + divider = cdce925_calc_divider(rate, l_parent_rate); + *parent_rate = l_parent_rate; + } + + if (divider) + return (long)(l_parent_rate / divider); + return 0; +} + +static int cdce925_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); + + data->pdiv = cdce925_calc_divider(rate, parent_rate); + + return 0; +} + +static const struct clk_ops cdce925_clk_ops = { + .prepare = cdce925_clk_prepare, + .unprepare = cdce925_clk_unprepare, + .recalc_rate = cdce925_clk_recalc_rate, + .round_rate = cdce925_clk_round_rate, + .set_rate = cdce925_clk_set_rate, +}; + + +static u16 cdce925_y1_calc_divider(unsigned long rate, + unsigned long parent_rate) +{ + unsigned long divider; + + if (!rate) + return 0; + if (rate >= parent_rate) + return 1; + + divider = DIV_ROUND_CLOSEST(parent_rate, rate); + if (divider > 0x3FF) /* Y1 has 10-bit divider */ + divider = 0x3FF; + + return (u16)divider; +} + +static long cdce925_clk_y1_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned long l_parent_rate = *parent_rate; + u16 divider = cdce925_y1_calc_divider(rate, l_parent_rate); + + if (divider) + return (long)(l_parent_rate / divider); + return 0; +} + +static int cdce925_clk_y1_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_cdce925_output *data = to_clk_cdce925_output(hw); + + data->pdiv = cdce925_y1_calc_divider(rate, parent_rate); + + return 0; +} + +static const struct clk_ops cdce925_clk_y1_ops = { + .prepare = cdce925_clk_prepare, + .unprepare = cdce925_clk_unprepare, + .recalc_rate = cdce925_clk_recalc_rate, + .round_rate = cdce925_clk_y1_round_rate, + .set_rate = cdce925_clk_y1_set_rate, +}; + + +static struct regmap_config cdce925_regmap_config = { + .name = "configuration0", + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .max_register = 0x2F, +}; + +#define CDCE925_I2C_COMMAND_BLOCK_TRANSFER 0x00 +#define CDCE925_I2C_COMMAND_BYTE_TRANSFER 0x80 + +static int cdce925_regmap_i2c_write( + void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + int ret; + u8 reg_data[2]; + + if (count != 2) + return -ENOTSUPP; + + /* First byte is command code */ + reg_data[0] = CDCE925_I2C_COMMAND_BYTE_TRANSFER | ((u8 *)data)[0]; + reg_data[1] = ((u8 *)data)[1]; + + dev_dbg(&i2c->dev, "%s(%zu) %#x %#x\n", __func__, count, + reg_data[0], reg_data[1]); + + ret = i2c_master_send(i2c, reg_data, count); + if (likely(ret == count)) + return 0; + else if (ret < 0) + return ret; + else + return -EIO; +} + +static int cdce925_regmap_i2c_read(void *context, + const void *reg, size_t reg_size, void *val, size_t val_size) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + struct i2c_msg xfer[2]; + int ret; + u8 reg_data[2]; + + if (reg_size != 1) + return -ENOTSUPP; + + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].buf = reg_data; + if (val_size == 1) { + reg_data[0] = + CDCE925_I2C_COMMAND_BYTE_TRANSFER | ((u8 *)reg)[0]; + xfer[0].len = 1; + } else { + reg_data[0] = + CDCE925_I2C_COMMAND_BLOCK_TRANSFER | ((u8 *)reg)[0]; + reg_data[1] = val_size; + xfer[0].len = 2; + } + + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = val_size; + xfer[1].buf = val; + + ret = i2c_transfer(i2c->adapter, xfer, 2); + if (likely(ret == 2)) { + dev_dbg(&i2c->dev, "%s(%zu, %u) %#x %#x\n", __func__, + reg_size, val_size, reg_data[0], *((u8 *)val)); + return 0; + } else if (ret < 0) + return ret; + else + return -EIO; +} + +/* The CDCE925 uses a funky way to read/write registers. Bulk mode is + * just weird, so just use the single byte mode exclusively. */ +static struct regmap_bus regmap_cdce925_bus = { + .write = cdce925_regmap_i2c_write, + .read = cdce925_regmap_i2c_read, +}; + +static int cdce925_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct clk_cdce925_chip *data; + struct device_node *node = client->dev.of_node; + const char *parent_name; + const char *pll_clk_name[NUMBER_OF_PLLS] = {NULL,}; + struct clk_init_data init; + struct clk *clk; + u32 value; + int i; + int err; + struct device_node *np_output; + char child_name[6]; + + dev_dbg(&client->dev, "%s\n", __func__); + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->i2c_client = client; + data->regmap = devm_regmap_init(&client->dev, ®map_cdce925_bus, + &client->dev, &cdce925_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(&client->dev, "failed to allocate register map\n"); + return PTR_ERR(data->regmap); + } + i2c_set_clientdata(client, data); + + parent_name = of_clk_get_parent_name(node, 0); + if (!parent_name) { + dev_err(&client->dev, "missing parent clock\n"); + return -ENODEV; + } + dev_dbg(&client->dev, "parent is: %s\n", parent_name); + + if (of_property_read_u32(node, "xtal-load-pf", &value) == 0) + regmap_write(data->regmap, + CDCE925_REG_XCSEL, (value << 3) & 0xF8); + /* PWDN bit */ + regmap_update_bits(data->regmap, CDCE925_REG_GLOBAL1, BIT(4), 0); + + /* Set input source for Y1 to be the XTAL */ + regmap_update_bits(data->regmap, 0x02, BIT(7), 0); + + init.ops = &cdce925_pll_ops; + init.flags = 0; + init.parent_names = &parent_name; + init.num_parents = parent_name ? 1 : 0; + + /* Register PLL clocks */ + for (i = 0; i < NUMBER_OF_PLLS; ++i) { + pll_clk_name[i] = kasprintf(GFP_KERNEL, "%s.pll%d", + client->dev.of_node->name, i); + init.name = pll_clk_name[i]; + data->pll[i].chip = data; + data->pll[i].hw.init = &init; + data->pll[i].index = i; + clk = devm_clk_register(&client->dev, &data->pll[i].hw); + if (IS_ERR(clk)) { + dev_err(&client->dev, "Failed register PLL %d\n", i); + err = PTR_ERR(clk); + goto error; + } + sprintf(child_name, "PLL%d", i+1); + np_output = of_get_child_by_name(node, child_name); + if (!np_output) + continue; + if (!of_property_read_u32(np_output, + "clock-frequency", &value)) { + err = clk_set_rate(clk, value); + if (err) + dev_err(&client->dev, + "unable to set PLL frequency %ud\n", + value); + } + if (!of_property_read_u32(np_output, + "spread-spectrum", &value)) { + u8 flag = of_property_read_bool(np_output, + "spread-spectrum-center") ? 0x80 : 0x00; + regmap_update_bits(data->regmap, + 0x16 + (i*CDCE925_OFFSET_PLL), + 0x80, flag); + regmap_update_bits(data->regmap, + 0x12 + (i*CDCE925_OFFSET_PLL), + 0x07, value & 0x07); + } + } + + /* Register output clock Y1 */ + init.ops = &cdce925_clk_y1_ops; + init.flags = 0; + init.num_parents = 1; + init.parent_names = &parent_name; /* Mux Y1 to input */ + init.name = kasprintf(GFP_KERNEL, "%s.Y1", client->dev.of_node->name); + data->clk[0].chip = data; + data->clk[0].hw.init = &init; + data->clk[0].index = 0; + data->clk[0].pdiv = 1; + clk = devm_clk_register(&client->dev, &data->clk[0].hw); + kfree(init.name); /* clock framework made a copy of the name */ + if (IS_ERR(clk)) { + dev_err(&client->dev, "clock registration Y1 failed\n"); + err = PTR_ERR(clk); + goto error; + } + data->dt_clk[0] = clk; + + /* Register output clocks Y2 .. Y5*/ + init.ops = &cdce925_clk_ops; + init.flags = CLK_SET_RATE_PARENT; + init.num_parents = 1; + for (i = 1; i < NUMBER_OF_OUTPUTS; ++i) { + init.name = kasprintf(GFP_KERNEL, "%s.Y%d", + client->dev.of_node->name, i+1); + data->clk[i].chip = data; + data->clk[i].hw.init = &init; + data->clk[i].index = i; + data->clk[i].pdiv = 1; + switch (i) { + case 1: + case 2: + /* Mux Y2/3 to PLL1 */ + init.parent_names = &pll_clk_name[0]; + break; + case 3: + case 4: + /* Mux Y4/5 to PLL2 */ + init.parent_names = &pll_clk_name[1]; + break; + } + clk = devm_clk_register(&client->dev, &data->clk[i].hw); + kfree(init.name); /* clock framework made a copy of the name */ + if (IS_ERR(clk)) { + dev_err(&client->dev, "clock registration failed\n"); + err = PTR_ERR(clk); + goto error; + } + data->dt_clk[i] = clk; + } + + /* Register the output clocks */ + data->onecell.clk_num = NUMBER_OF_OUTPUTS; + data->onecell.clks = data->dt_clk; + err = of_clk_add_provider(client->dev.of_node, of_clk_src_onecell_get, + &data->onecell); + if (err) + dev_err(&client->dev, "unable to add OF clock provider\n"); + + err = 0; + +error: + for (i = 0; i < NUMBER_OF_PLLS; ++i) + /* clock framework made a copy of the name */ + kfree(pll_clk_name[i]); + + return err; +} + +static const struct i2c_device_id cdce925_id[] = { + { "cdce925", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cdce925_id); + +static const struct of_device_id clk_cdce925_of_match[] = { + { .compatible = "ti,cdce925" }, + { }, +}; +MODULE_DEVICE_TABLE(of, clk_cdce925_of_match); + +static struct i2c_driver cdce925_driver = { + .driver = { + .name = "cdce925", + .of_match_table = of_match_ptr(clk_cdce925_of_match), + }, + .probe = cdce925_probe, + .id_table = cdce925_id, +}; +module_i2c_driver(cdce925_driver); + +MODULE_AUTHOR("Mike Looijmans "); +MODULE_DESCRIPTION("cdce925 driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-58-ga151 From a35247c6ee7265b024d606f7503fef44e88d9d2d Mon Sep 17 00:00:00 2001 From: Chao Xie Date: Thu, 30 Apr 2015 09:53:40 +0800 Subject: clk: mmp: add fixed clock UBS_PLL for pxa910/pxa168 USB will drive clock from USB_PLL. Signed-off-by: Chao Xie Signed-off-by: Stephen Boyd --- drivers/clk/mmp/clk-of-pxa168.c | 1 + drivers/clk/mmp/clk-of-pxa910.c | 1 + include/dt-bindings/clock/marvell,pxa168.h | 1 + include/dt-bindings/clock/marvell,pxa910.h | 1 + 4 files changed, 4 insertions(+) diff --git a/drivers/clk/mmp/clk-of-pxa168.c b/drivers/clk/mmp/clk-of-pxa168.c index 5b1810dc4bd2..01a650e6f8a4 100644 --- a/drivers/clk/mmp/clk-of-pxa168.c +++ b/drivers/clk/mmp/clk-of-pxa168.c @@ -58,6 +58,7 @@ static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { {PXA168_CLK_CLK32, "clk32", NULL, CLK_IS_ROOT, 32768}, {PXA168_CLK_VCTCXO, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, {PXA168_CLK_PLL1, "pll1", NULL, CLK_IS_ROOT, 624000000}, + {PXA168_CLK_USB_PLL, "usb_pll", NULL, CLK_IS_ROOT, 480000000}, }; static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { diff --git a/drivers/clk/mmp/clk-of-pxa910.c b/drivers/clk/mmp/clk-of-pxa910.c index 5e3c80dad336..cca98eff641a 100644 --- a/drivers/clk/mmp/clk-of-pxa910.c +++ b/drivers/clk/mmp/clk-of-pxa910.c @@ -57,6 +57,7 @@ static struct mmp_param_fixed_rate_clk fixed_rate_clks[] = { {PXA910_CLK_CLK32, "clk32", NULL, CLK_IS_ROOT, 32768}, {PXA910_CLK_VCTCXO, "vctcxo", NULL, CLK_IS_ROOT, 26000000}, {PXA910_CLK_PLL1, "pll1", NULL, CLK_IS_ROOT, 624000000}, + {PXA910_CLK_USB_PLL, "usb_pll", NULL, CLK_IS_ROOT, 480000000}, }; static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { diff --git a/include/dt-bindings/clock/marvell,pxa168.h b/include/dt-bindings/clock/marvell,pxa168.h index 79630b9d74b8..84ce5de6c83d 100644 --- a/include/dt-bindings/clock/marvell,pxa168.h +++ b/include/dt-bindings/clock/marvell,pxa168.h @@ -19,6 +19,7 @@ #define PXA168_CLK_PLL1_2_1_5 19 #define PXA168_CLK_PLL1_3_16 20 #define PXA168_CLK_UART_PLL 27 +#define PXA168_CLK_USB_PLL 28 /* apb periphrals */ #define PXA168_CLK_TWSI0 60 diff --git a/include/dt-bindings/clock/marvell,pxa910.h b/include/dt-bindings/clock/marvell,pxa910.h index 719cffb2bea2..bea08b60298b 100644 --- a/include/dt-bindings/clock/marvell,pxa910.h +++ b/include/dt-bindings/clock/marvell,pxa910.h @@ -19,6 +19,7 @@ #define PXA910_CLK_PLL1_2_1_5 19 #define PXA910_CLK_PLL1_3_16 20 #define PXA910_CLK_UART_PLL 27 +#define PXA910_CLK_USB_PLL 28 /* apb periphrals */ #define PXA910_CLK_TWSI0 60 -- cgit v1.2.3-58-ga151 From 6644fddf062e1fa9bd56e74dfeaf864a14bf08cb Mon Sep 17 00:00:00 2001 From: Chao Xie Date: Thu, 30 Apr 2015 09:53:41 +0800 Subject: clk: mmp: Fix the wrong factor table for uart PLL The suggested value in the mmp2 manual is wrong. There are only 13 bits for numerator, but some suggested value has 14 bits. Fix the factor tabled and remove the unused items. Signed-off-by: Chao Xie Signed-off-by: Stephen Boyd --- drivers/clk/mmp/clk-mmp2.c | 4 +--- drivers/clk/mmp/clk-of-mmp2.c | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c index 5c90a4230fa3..09d2832fbd78 100644 --- a/drivers/clk/mmp/clk-mmp2.c +++ b/drivers/clk/mmp/clk-mmp2.c @@ -63,10 +63,8 @@ static struct mmp_clk_factor_masks uart_factor_masks = { }; static struct mmp_clk_factor_tbl uart_factor_tbl[] = { - {.num = 14634, .den = 2165}, /*14.745MHZ */ + {.num = 8125, .den = 1536}, /*14.745MHZ */ {.num = 3521, .den = 689}, /*19.23MHZ */ - {.num = 9679, .den = 5728}, /*58.9824MHZ */ - {.num = 15850, .den = 9451}, /*59.429MHZ */ }; static const char *uart_parent[] = {"uart_pll", "vctcxo"}; diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c index 2cbc2b43ae52..b7e0b89b2e3f 100644 --- a/drivers/clk/mmp/clk-of-mmp2.c +++ b/drivers/clk/mmp/clk-of-mmp2.c @@ -98,10 +98,8 @@ static struct mmp_clk_factor_masks uart_factor_masks = { }; static struct mmp_clk_factor_tbl uart_factor_tbl[] = { - {.num = 14634, .den = 2165}, /*14.745MHZ */ + {.num = 8125, .den = 1536}, /*14.745MHZ */ {.num = 3521, .den = 689}, /*19.23MHZ */ - {.num = 9679, .den = 5728}, /*58.9824MHZ */ - {.num = 15850, .den = 9451}, /*59.429MHZ */ }; static void mmp2_pll_init(struct mmp2_clk_unit *pxa_unit) -- cgit v1.2.3-58-ga151 From 24c65a02b23509284cce5185d81a150d699837b5 Mon Sep 17 00:00:00 2001 From: Chao Xie Date: Thu, 30 Apr 2015 09:53:42 +0800 Subject: clk: mmp: add timer clock for pxa168/mmp2/pxa910 Timer has external fast clock, and it is a mux clock. Add the timer clock type for timer driver. Signed-off-by: Chao Xie Signed-off-by: Stephen Boyd --- drivers/clk/mmp/clk-of-mmp2.c | 6 ++++++ drivers/clk/mmp/clk-of-pxa168.c | 7 +++++++ drivers/clk/mmp/clk-of-pxa910.c | 11 +++++++++++ include/dt-bindings/clock/marvell,mmp2.h | 1 + include/dt-bindings/clock/marvell,pxa168.h | 2 ++ include/dt-bindings/clock/marvell,pxa910.h | 3 +++ 6 files changed, 30 insertions(+) diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c index b7e0b89b2e3f..251533d87c65 100644 --- a/drivers/clk/mmp/clk-of-mmp2.c +++ b/drivers/clk/mmp/clk-of-mmp2.c @@ -30,6 +30,7 @@ #define APBC_TWSI4 0x7c #define APBC_TWSI5 0x80 #define APBC_KPC 0x18 +#define APBC_TIMER 0x24 #define APBC_UART0 0x2c #define APBC_UART1 0x30 #define APBC_UART2 0x34 @@ -132,6 +133,9 @@ static DEFINE_SPINLOCK(ssp2_lock); static DEFINE_SPINLOCK(ssp3_lock); static const char *ssp_parent_names[] = {"vctcxo_4", "vctcxo_2", "vctcxo", "pll1_16"}; +static DEFINE_SPINLOCK(timer_lock); +static const char *timer_parent_names[] = {"clk32", "vctcxo_2", "vctcxo_4", "vctcxo"}; + static DEFINE_SPINLOCK(reset_lock); static struct mmp_param_mux_clk apbc_mux_clks[] = { @@ -143,6 +147,7 @@ static struct mmp_param_mux_clk apbc_mux_clks[] = { {0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP1, 4, 3, 0, &ssp1_lock}, {0, "ssp2_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP2, 4, 3, 0, &ssp2_lock}, {0, "ssp3_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP3, 4, 3, 0, &ssp3_lock}, + {0, "timer_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER, 4, 3, 0, &timer_lock}, }; static struct mmp_param_gate_clk apbc_gate_clks[] = { @@ -168,6 +173,7 @@ static struct mmp_param_gate_clk apbc_gate_clks[] = { {MMP2_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, APBC_SSP1, 0x7, 0x3, 0x0, 0, &ssp1_lock}, {MMP2_CLK_SSP2, "ssp2_clk", "ssp2_mux", CLK_SET_RATE_PARENT, APBC_SSP2, 0x7, 0x3, 0x0, 0, &ssp2_lock}, {MMP2_CLK_SSP3, "ssp3_clk", "ssp3_mux", CLK_SET_RATE_PARENT, APBC_SSP3, 0x7, 0x3, 0x0, 0, &ssp3_lock}, + {MMP2_CLK_TIMER, "timer_clk", "timer_mux", CLK_SET_RATE_PARENT, APBC_TIMER, 0x7, 0x3, 0x0, 0, &timer_lock}, }; static void mmp2_apb_periph_clk_init(struct mmp2_clk_unit *pxa_unit) diff --git a/drivers/clk/mmp/clk-of-pxa168.c b/drivers/clk/mmp/clk-of-pxa168.c index 01a650e6f8a4..64eaf4141c69 100644 --- a/drivers/clk/mmp/clk-of-pxa168.c +++ b/drivers/clk/mmp/clk-of-pxa168.c @@ -32,6 +32,7 @@ #define APBC_PWM1 0x10 #define APBC_PWM2 0x14 #define APBC_PWM3 0x18 +#define APBC_TIMER 0x34 #define APBC_SSP0 0x81c #define APBC_SSP1 0x820 #define APBC_SSP2 0x84c @@ -71,6 +72,7 @@ static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { {PXA168_CLK_PLL1_24, "pll1_24", "pll1_12", 1, 2, 0}, {PXA168_CLK_PLL1_48, "pll1_48", "pll1_24", 1, 2, 0}, {PXA168_CLK_PLL1_96, "pll1_96", "pll1_48", 1, 2, 0}, + {PXA168_CLK_PLL1_192, "pll1_192", "pll1_96", 1, 2, 0}, {PXA168_CLK_PLL1_13, "pll1_13", "pll1", 1, 13, 0}, {PXA168_CLK_PLL1_13_1_5, "pll1_13_1_5", "pll1_13", 2, 3, 0}, {PXA168_CLK_PLL1_2_1_5, "pll1_2_1_5", "pll1_2", 2, 3, 0}, @@ -120,6 +122,9 @@ static DEFINE_SPINLOCK(ssp3_lock); static DEFINE_SPINLOCK(ssp4_lock); static const char *ssp_parent_names[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"}; +static DEFINE_SPINLOCK(timer_lock); +static const char *timer_parent_names[] = {"pll1_48", "clk32", "pll1_96", "pll1_192"}; + static DEFINE_SPINLOCK(reset_lock); static struct mmp_param_mux_clk apbc_mux_clks[] = { @@ -131,6 +136,7 @@ static struct mmp_param_mux_clk apbc_mux_clks[] = { {0, "ssp2_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP2, 4, 3, 0, &ssp2_lock}, {0, "ssp3_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP3, 4, 3, 0, &ssp3_lock}, {0, "ssp4_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP4, 4, 3, 0, &ssp4_lock}, + {0, "timer_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER, 4, 3, 0, &timer_lock}, }; static struct mmp_param_gate_clk apbc_gate_clks[] = { @@ -152,6 +158,7 @@ static struct mmp_param_gate_clk apbc_gate_clks[] = { {PXA168_CLK_SSP2, "ssp2_clk", "ssp2_mux", CLK_SET_RATE_PARENT, APBC_SSP2, 0x3, 0x3, 0x0, 0, &ssp2_lock}, {PXA168_CLK_SSP3, "ssp3_clk", "ssp3_mux", CLK_SET_RATE_PARENT, APBC_SSP3, 0x3, 0x3, 0x0, 0, &ssp3_lock}, {PXA168_CLK_SSP4, "ssp4_clk", "ssp4_mux", CLK_SET_RATE_PARENT, APBC_SSP4, 0x3, 0x3, 0x0, 0, &ssp4_lock}, + {PXA168_CLK_TIMER, "timer_clk", "timer_mux", CLK_SET_RATE_PARENT, APBC_TIMER, 0x3, 0x3, 0x0, 0, &timer_lock}, }; static void pxa168_apb_periph_clk_init(struct pxa168_clk_unit *pxa_unit) diff --git a/drivers/clk/mmp/clk-of-pxa910.c b/drivers/clk/mmp/clk-of-pxa910.c index cca98eff641a..13d6173326a4 100644 --- a/drivers/clk/mmp/clk-of-pxa910.c +++ b/drivers/clk/mmp/clk-of-pxa910.c @@ -35,6 +35,8 @@ #define APBC_SSP0 0x1c #define APBC_SSP1 0x20 #define APBC_SSP2 0x4c +#define APBC_TIMER0 0x30 +#define APBC_TIMER1 0x44 #define APBCP_TWSI1 0x28 #define APBCP_UART2 0x1c #define APMU_SDH0 0x54 @@ -70,6 +72,7 @@ static struct mmp_param_fixed_factor_clk fixed_factor_clks[] = { {PXA910_CLK_PLL1_24, "pll1_24", "pll1_12", 1, 2, 0}, {PXA910_CLK_PLL1_48, "pll1_48", "pll1_24", 1, 2, 0}, {PXA910_CLK_PLL1_96, "pll1_96", "pll1_48", 1, 2, 0}, + {PXA910_CLK_PLL1_192, "pll1_192", "pll1_96", 1, 2, 0}, {PXA910_CLK_PLL1_13, "pll1_13", "pll1", 1, 13, 0}, {PXA910_CLK_PLL1_13_1_5, "pll1_13_1_5", "pll1_13", 2, 3, 0}, {PXA910_CLK_PLL1_2_1_5, "pll1_2_1_5", "pll1_2", 2, 3, 0}, @@ -116,6 +119,10 @@ static DEFINE_SPINLOCK(ssp0_lock); static DEFINE_SPINLOCK(ssp1_lock); static const char *ssp_parent_names[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"}; +static DEFINE_SPINLOCK(timer0_lock); +static DEFINE_SPINLOCK(timer1_lock); +static const char *timer_parent_names[] = {"pll1_48", "clk32", "pll1_96"}; + static DEFINE_SPINLOCK(reset_lock); static struct mmp_param_mux_clk apbc_mux_clks[] = { @@ -123,6 +130,8 @@ static struct mmp_param_mux_clk apbc_mux_clks[] = { {0, "uart1_mux", uart_parent_names, ARRAY_SIZE(uart_parent_names), CLK_SET_RATE_PARENT, APBC_UART1, 4, 3, 0, &uart1_lock}, {0, "ssp0_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP0, 4, 3, 0, &ssp0_lock}, {0, "ssp1_mux", ssp_parent_names, ARRAY_SIZE(ssp_parent_names), CLK_SET_RATE_PARENT, APBC_SSP1, 4, 3, 0, &ssp1_lock}, + {0, "timer0_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER0, 4, 3, 0, &timer0_lock}, + {0, "timer1_mux", timer_parent_names, ARRAY_SIZE(timer_parent_names), CLK_SET_RATE_PARENT, APBC_TIMER1, 4, 3, 0, &timer1_lock}, }; static struct mmp_param_mux_clk apbcp_mux_clks[] = { @@ -143,6 +152,8 @@ static struct mmp_param_gate_clk apbc_gate_clks[] = { {PXA910_CLK_UART1, "uart1_clk", "uart1_mux", CLK_SET_RATE_PARENT, APBC_UART1, 0x3, 0x3, 0x0, 0, &uart1_lock}, {PXA910_CLK_SSP0, "ssp0_clk", "ssp0_mux", CLK_SET_RATE_PARENT, APBC_SSP0, 0x3, 0x3, 0x0, 0, &ssp0_lock}, {PXA910_CLK_SSP1, "ssp1_clk", "ssp1_mux", CLK_SET_RATE_PARENT, APBC_SSP1, 0x3, 0x3, 0x0, 0, &ssp1_lock}, + {PXA910_CLK_TIMER0, "timer0_clk", "timer0_mux", CLK_SET_RATE_PARENT, APBC_TIMER0, 0x3, 0x3, 0x0, 0, &timer0_lock}, + {PXA910_CLK_TIMER1, "timer1_clk", "timer1_mux", CLK_SET_RATE_PARENT, APBC_TIMER1, 0x3, 0x3, 0x0, 0, &timer1_lock}, }; static struct mmp_param_gate_clk apbcp_gate_clks[] = { diff --git a/include/dt-bindings/clock/marvell,mmp2.h b/include/dt-bindings/clock/marvell,mmp2.h index 591f7fba89e2..7a510384a82a 100644 --- a/include/dt-bindings/clock/marvell,mmp2.h +++ b/include/dt-bindings/clock/marvell,mmp2.h @@ -48,6 +48,7 @@ #define MMP2_CLK_SSP1 78 #define MMP2_CLK_SSP2 79 #define MMP2_CLK_SSP3 80 +#define MMP2_CLK_TIMER 81 /* axi periphrals */ #define MMP2_CLK_SDH0 101 diff --git a/include/dt-bindings/clock/marvell,pxa168.h b/include/dt-bindings/clock/marvell,pxa168.h index 84ce5de6c83d..3e45bdfe1aa4 100644 --- a/include/dt-bindings/clock/marvell,pxa168.h +++ b/include/dt-bindings/clock/marvell,pxa168.h @@ -18,6 +18,7 @@ #define PXA168_CLK_PLL1_13_1_5 18 #define PXA168_CLK_PLL1_2_1_5 19 #define PXA168_CLK_PLL1_3_16 20 +#define PXA168_CLK_PLL1_192 21 #define PXA168_CLK_UART_PLL 27 #define PXA168_CLK_USB_PLL 28 @@ -41,6 +42,7 @@ #define PXA168_CLK_SSP2 76 #define PXA168_CLK_SSP3 77 #define PXA168_CLK_SSP4 78 +#define PXA168_CLK_TIMER 79 /* axi periphrals */ #define PXA168_CLK_DFC 100 diff --git a/include/dt-bindings/clock/marvell,pxa910.h b/include/dt-bindings/clock/marvell,pxa910.h index bea08b60298b..135082a0b62f 100644 --- a/include/dt-bindings/clock/marvell,pxa910.h +++ b/include/dt-bindings/clock/marvell,pxa910.h @@ -18,6 +18,7 @@ #define PXA910_CLK_PLL1_13_1_5 18 #define PXA910_CLK_PLL1_2_1_5 19 #define PXA910_CLK_PLL1_3_16 20 +#define PXA910_CLK_PLL1_192 21 #define PXA910_CLK_UART_PLL 27 #define PXA910_CLK_USB_PLL 28 @@ -38,6 +39,8 @@ #define PXA910_CLK_UART2 73 #define PXA910_CLK_SSP0 74 #define PXA910_CLK_SSP1 75 +#define PXA910_CLK_TIMER0 76 +#define PXA910_CLK_TIMER1 77 /* axi periphrals */ #define PXA910_CLK_DFC 100 -- cgit v1.2.3-58-ga151 From 4f4adfbf8e655914bb80daa0252c90b62fd27d78 Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 26 May 2015 19:01:07 -0300 Subject: clk: pistachio: Add a pll_lock() helper for clarity This commit adds a pll_lock() helper making the code more readable. Cosmetic change only, no functionality changes. Signed-off-by: Andrew Bresticker Signed-off-by: Ezequiel Garcia Signed-off-by: Stephen Boyd --- drivers/clk/pistachio/clk-pll.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/clk/pistachio/clk-pll.c b/drivers/clk/pistachio/clk-pll.c index de537560bf70..9ce1be779c9c 100644 --- a/drivers/clk/pistachio/clk-pll.c +++ b/drivers/clk/pistachio/clk-pll.c @@ -67,6 +67,12 @@ static inline void pll_writel(struct pistachio_clk_pll *pll, u32 val, u32 reg) writel(val, pll->base + reg); } +static inline void pll_lock(struct pistachio_clk_pll *pll) +{ + while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK)) + cpu_relax(); +} + static inline u32 do_div_round_closest(u64 dividend, u32 divisor) { dividend += divisor / 2; @@ -178,8 +184,7 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, (params->postdiv2 << PLL_FRAC_CTRL2_POSTDIV2_SHIFT); pll_writel(pll, val, PLL_CTRL2); - while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK)) - cpu_relax(); + pll_lock(pll); if (!was_enabled) pll_gf40lp_frac_disable(hw); @@ -288,8 +293,7 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, (params->postdiv2 << PLL_INT_CTRL1_POSTDIV2_SHIFT); pll_writel(pll, val, PLL_CTRL1); - while (!(pll_readl(pll, PLL_STATUS) & PLL_STATUS_LOCK)) - cpu_relax(); + pll_lock(pll); if (!was_enabled) pll_gf40lp_laint_disable(hw); -- cgit v1.2.3-58-ga151 From e0b7a79524771ad368abefbbcbd73f130f8e500e Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Tue, 26 May 2015 19:01:08 -0300 Subject: clk: pistachio: Lock the PLL when enabled upon rate change Currently, when the rate is changed, the driver makes sure the PLL is enabled before doing so. This is done because the PLL cannot be locked while disabled. Once locked, the drivers returns the PLL to its previous enable/disable state. This is a bit cumbersome, and can be simplified. This commit reworks the .set_rate() functions for the integer and fractional PLLs. Upon rate change, the PLL is now locked only if it's already enabled. Also, the driver locks the PLL on .enable(). This makes sure the PLL is locked when enabled, and not locked when disabled. Signed-off-by: Andrew Bresticker Signed-off-by: Ezequiel Garcia Signed-off-by: Stephen Boyd --- drivers/clk/pistachio/clk-pll.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/drivers/clk/pistachio/clk-pll.c b/drivers/clk/pistachio/clk-pll.c index 9ce1be779c9c..f12d5207e478 100644 --- a/drivers/clk/pistachio/clk-pll.c +++ b/drivers/clk/pistachio/clk-pll.c @@ -130,6 +130,8 @@ static int pll_gf40lp_frac_enable(struct clk_hw *hw) val &= ~PLL_FRAC_CTRL4_BYPASS; pll_writel(pll, val, PLL_CTRL4); + pll_lock(pll); + return 0; } @@ -155,17 +157,13 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, { struct pistachio_clk_pll *pll = to_pistachio_pll(hw); struct pistachio_pll_rate_table *params; - bool was_enabled; + int enabled = pll_gf40lp_frac_is_enabled(hw); u32 val; params = pll_get_params(pll, parent_rate, rate); if (!params) return -EINVAL; - was_enabled = pll_gf40lp_frac_is_enabled(hw); - if (!was_enabled) - pll_gf40lp_frac_enable(hw); - val = pll_readl(pll, PLL_CTRL1); val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT)); @@ -184,10 +182,8 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, (params->postdiv2 << PLL_FRAC_CTRL2_POSTDIV2_SHIFT); pll_writel(pll, val, PLL_CTRL2); - pll_lock(pll); - - if (!was_enabled) - pll_gf40lp_frac_disable(hw); + if (enabled) + pll_lock(pll); return 0; } @@ -246,6 +242,8 @@ static int pll_gf40lp_laint_enable(struct clk_hw *hw) val &= ~PLL_INT_CTRL2_BYPASS; pll_writel(pll, val, PLL_CTRL2); + pll_lock(pll); + return 0; } @@ -271,17 +269,13 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, { struct pistachio_clk_pll *pll = to_pistachio_pll(hw); struct pistachio_pll_rate_table *params; - bool was_enabled; + int enabled = pll_gf40lp_laint_is_enabled(hw); u32 val; params = pll_get_params(pll, parent_rate, rate); if (!params) return -EINVAL; - was_enabled = pll_gf40lp_laint_is_enabled(hw); - if (!was_enabled) - pll_gf40lp_laint_enable(hw); - val = pll_readl(pll, PLL_CTRL1); val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT) | @@ -293,10 +287,8 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, (params->postdiv2 << PLL_INT_CTRL1_POSTDIV2_SHIFT); pll_writel(pll, val, PLL_CTRL1); - pll_lock(pll); - - if (!was_enabled) - pll_gf40lp_laint_disable(hw); + if (enabled) + pll_lock(pll); return 0; } -- cgit v1.2.3-58-ga151 From 17bfa3f7b3cd07e92b41ce7b5bea2dd8c8e2a8c3 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 26 May 2015 19:01:09 -0300 Subject: clk: pistachio: Add sanity checks on PLL configuration When setting the PLL rates, check that: - VCO is within range - PFD is within range - PLL is disabled when postdiv is changed - postdiv2 <= postdiv1 Reviewed-by: Andrew Bresticker Signed-off-by: Kevin Cernekee Signed-off-by: Ezequiel Garcia Signed-off-by: Stephen Boyd --- drivers/clk/pistachio/clk-pll.c | 83 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/drivers/clk/pistachio/clk-pll.c b/drivers/clk/pistachio/clk-pll.c index f12d5207e478..e17dada0dd21 100644 --- a/drivers/clk/pistachio/clk-pll.c +++ b/drivers/clk/pistachio/clk-pll.c @@ -6,9 +6,12 @@ * version 2, as published by the Free Software Foundation. */ +#define pr_fmt(fmt) "%s: " fmt, __func__ + #include #include #include +#include #include #include "clk.h" @@ -50,6 +53,18 @@ #define PLL_CTRL4 0x10 #define PLL_FRAC_CTRL4_BYPASS BIT(28) +#define MIN_PFD 9600000UL +#define MIN_VCO_LA 400000000UL +#define MAX_VCO_LA 1600000000UL +#define MIN_VCO_FRAC_INT 600000000UL +#define MAX_VCO_FRAC_INT 1600000000UL +#define MIN_VCO_FRAC_FRAC 600000000UL +#define MAX_VCO_FRAC_FRAC 2400000000UL +#define MIN_OUTPUT_LA 8000000UL +#define MAX_OUTPUT_LA 1600000000UL +#define MIN_OUTPUT_FRAC 12000000UL +#define MAX_OUTPUT_FRAC 1600000000UL + struct pistachio_clk_pll { struct clk_hw hw; void __iomem *base; @@ -158,12 +173,29 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, struct pistachio_clk_pll *pll = to_pistachio_pll(hw); struct pistachio_pll_rate_table *params; int enabled = pll_gf40lp_frac_is_enabled(hw); - u32 val; + u32 val, vco, old_postdiv1, old_postdiv2; + const char *name = __clk_get_name(hw->clk); + + if (rate < MIN_OUTPUT_FRAC || rate > MAX_OUTPUT_FRAC) + return -EINVAL; params = pll_get_params(pll, parent_rate, rate); - if (!params) + if (!params || !params->refdiv) return -EINVAL; + vco = params->fref * params->fbdiv / params->refdiv; + if (vco < MIN_VCO_FRAC_FRAC || vco > MAX_VCO_FRAC_FRAC) + pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, + MIN_VCO_FRAC_FRAC, MAX_VCO_FRAC_FRAC); + + val = params->fref / params->refdiv; + if (val < MIN_PFD) + pr_warn("%s: PFD %u is too low (min %lu)\n", + name, val, MIN_PFD); + if (val > vco / 16) + pr_warn("%s: PFD %u is too high (max %u)\n", + name, val, vco / 16); + val = pll_readl(pll, PLL_CTRL1); val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT)); @@ -172,6 +204,19 @@ static int pll_gf40lp_frac_set_rate(struct clk_hw *hw, unsigned long rate, pll_writel(pll, val, PLL_CTRL1); val = pll_readl(pll, PLL_CTRL2); + + old_postdiv1 = (val >> PLL_FRAC_CTRL2_POSTDIV1_SHIFT) & + PLL_FRAC_CTRL2_POSTDIV1_MASK; + old_postdiv2 = (val >> PLL_FRAC_CTRL2_POSTDIV2_SHIFT) & + PLL_FRAC_CTRL2_POSTDIV2_MASK; + if (enabled && + (params->postdiv1 != old_postdiv1 || + params->postdiv2 != old_postdiv2)) + pr_warn("%s: changing postdiv while PLL is enabled\n", name); + + if (params->postdiv2 > params->postdiv1) + pr_warn("%s: postdiv2 should not exceed postdiv1\n", name); + val &= ~((PLL_FRAC_CTRL2_FRAC_MASK << PLL_FRAC_CTRL2_FRAC_SHIFT) | (PLL_FRAC_CTRL2_POSTDIV1_MASK << PLL_FRAC_CTRL2_POSTDIV1_SHIFT) | @@ -270,13 +315,43 @@ static int pll_gf40lp_laint_set_rate(struct clk_hw *hw, unsigned long rate, struct pistachio_clk_pll *pll = to_pistachio_pll(hw); struct pistachio_pll_rate_table *params; int enabled = pll_gf40lp_laint_is_enabled(hw); - u32 val; + u32 val, vco, old_postdiv1, old_postdiv2; + const char *name = __clk_get_name(hw->clk); + + if (rate < MIN_OUTPUT_LA || rate > MAX_OUTPUT_LA) + return -EINVAL; params = pll_get_params(pll, parent_rate, rate); - if (!params) + if (!params || !params->refdiv) return -EINVAL; + vco = params->fref * params->fbdiv / params->refdiv; + if (vco < MIN_VCO_LA || vco > MAX_VCO_LA) + pr_warn("%s: VCO %u is out of range %lu..%lu\n", name, vco, + MIN_VCO_LA, MAX_VCO_LA); + + val = params->fref / params->refdiv; + if (val < MIN_PFD) + pr_warn("%s: PFD %u is too low (min %lu)\n", + name, val, MIN_PFD); + if (val > vco / 16) + pr_warn("%s: PFD %u is too high (max %u)\n", + name, val, vco / 16); + val = pll_readl(pll, PLL_CTRL1); + + old_postdiv1 = (val >> PLL_INT_CTRL1_POSTDIV1_SHIFT) & + PLL_INT_CTRL1_POSTDIV1_MASK; + old_postdiv2 = (val >> PLL_INT_CTRL1_POSTDIV2_SHIFT) & + PLL_INT_CTRL1_POSTDIV2_MASK; + if (enabled && + (params->postdiv1 != old_postdiv1 || + params->postdiv2 != old_postdiv2)) + pr_warn("%s: changing postdiv while PLL is enabled\n", name); + + if (params->postdiv2 > params->postdiv1) + pr_warn("%s: postdiv2 should not exceed postdiv1\n", name); + val &= ~((PLL_CTRL1_REFDIV_MASK << PLL_CTRL1_REFDIV_SHIFT) | (PLL_CTRL1_FBDIV_MASK << PLL_CTRL1_FBDIV_SHIFT) | (PLL_INT_CTRL1_POSTDIV1_MASK << PLL_INT_CTRL1_POSTDIV1_SHIFT) | -- cgit v1.2.3-58-ga151 From 51a43be9fa9016e02ef9c4214470c919223d138c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 29 May 2015 11:25:45 +0200 Subject: clk: at91: Use of_clk_get_parent_count() instead of open coding Signed-off-by: Geert Uytterhoeven Acked-by: Boris Brezillon Signed-off-by: Stephen Boyd --- drivers/clk/at91/clk-main.c | 2 +- drivers/clk/at91/clk-master.c | 2 +- drivers/clk/at91/clk-programmable.c | 2 +- drivers/clk/at91/clk-slow.c | 4 ++-- drivers/clk/at91/clk-smd.c | 2 +- drivers/clk/at91/clk-usb.c | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c index 59fa3cc96c9e..c2400456a044 100644 --- a/drivers/clk/at91/clk-main.c +++ b/drivers/clk/at91/clk-main.c @@ -614,7 +614,7 @@ void __init of_at91sam9x5_clk_main_setup(struct device_node *np, const char *name = np->name; int i; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > 2) return; diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c index c1af80bcdf20..f98eafe9b12d 100644 --- a/drivers/clk/at91/clk-master.c +++ b/drivers/clk/at91/clk-master.c @@ -224,7 +224,7 @@ of_at91_clk_master_setup(struct device_node *np, struct at91_pmc *pmc, const char *name = np->name; struct clk_master_characteristics *characteristics; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > MASTER_SOURCE_MAX) return; diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c index 86c8a073dcc3..8c86c0f7847a 100644 --- a/drivers/clk/at91/clk-programmable.c +++ b/drivers/clk/at91/clk-programmable.c @@ -237,7 +237,7 @@ of_at91_clk_prog_setup(struct device_node *np, struct at91_pmc *pmc, const char *name; struct device_node *progclknp; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > PROG_SOURCE_MAX) return; diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c index 2f13bd5246b5..98a84a865fe1 100644 --- a/drivers/clk/at91/clk-slow.c +++ b/drivers/clk/at91/clk-slow.c @@ -373,7 +373,7 @@ void __init of_at91sam9x5_clk_slow_setup(struct device_node *np, const char *name = np->name; int i; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > 2) return; @@ -451,7 +451,7 @@ void __init of_at91sam9260_clk_slow_setup(struct device_node *np, const char *name = np->name; int i; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents != 2) return; diff --git a/drivers/clk/at91/clk-smd.c b/drivers/clk/at91/clk-smd.c index 144d47ecfe63..3817ea865ca2 100644 --- a/drivers/clk/at91/clk-smd.c +++ b/drivers/clk/at91/clk-smd.c @@ -150,7 +150,7 @@ void __init of_at91sam9x5_clk_smd_setup(struct device_node *np, const char *parent_names[SMD_SOURCE_MAX]; const char *name = np->name; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > SMD_SOURCE_MAX) return; diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c index 0b7c3e8840ba..b0cbd2b1ff59 100644 --- a/drivers/clk/at91/clk-usb.c +++ b/drivers/clk/at91/clk-usb.c @@ -378,7 +378,7 @@ void __init of_at91sam9x5_clk_usb_setup(struct device_node *np, const char *parent_names[USB_SOURCE_MAX]; const char *name = np->name; - num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + num_parents = of_clk_get_parent_count(np); if (num_parents <= 0 || num_parents > USB_SOURCE_MAX) return; -- cgit v1.2.3-58-ga151 From 0a65239c280bb0d56d8d84c4a9cc78bc68144925 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 29 May 2015 11:25:46 +0200 Subject: clk: st: Use of_clk_get_parent_count() instead of open coding Signed-off-by: Geert Uytterhoeven Signed-off-by: Stephen Boyd --- drivers/clk/st/clk-flexgen.c | 2 +- drivers/clk/st/clkgen-mux.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/st/clk-flexgen.c b/drivers/clk/st/clk-flexgen.c index 409ed4b0b569..657ca14ba709 100644 --- a/drivers/clk/st/clk-flexgen.c +++ b/drivers/clk/st/clk-flexgen.c @@ -245,7 +245,7 @@ static const char ** __init flexgen_get_parents(struct device_node *np, const char **parents; int nparents, i; - nparents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + nparents = of_clk_get_parent_count(np); if (WARN_ON(nparents <= 0)) return NULL; diff --git a/drivers/clk/st/clkgen-mux.c b/drivers/clk/st/clkgen-mux.c index 80aa77dbf660..4fbe6e099587 100644 --- a/drivers/clk/st/clkgen-mux.c +++ b/drivers/clk/st/clkgen-mux.c @@ -26,7 +26,7 @@ static const char ** __init clkgen_mux_get_parents(struct device_node *np, const char **parents; int nparents, i; - nparents = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + nparents = of_clk_get_parent_count(np); if (WARN_ON(nparents <= 0)) return ERR_PTR(-EINVAL); -- cgit v1.2.3-58-ga151 From f71355b3d14421784f13c44b66fe0fbc7d3dbdb1 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 29 May 2015 11:25:47 +0200 Subject: clk: ti: Use of_clk_get_parent_count() instead of open coding Signed-off-by: Geert Uytterhoeven Signed-off-by: Stephen Boyd --- drivers/clk/ti/clockdomain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/ti/clockdomain.c b/drivers/clk/ti/clockdomain.c index 35fe1085480c..b82ef07f3403 100644 --- a/drivers/clk/ti/clockdomain.c +++ b/drivers/clk/ti/clockdomain.c @@ -32,7 +32,7 @@ static void __init of_ti_clockdomain_setup(struct device_node *node) int i; int num_clks; - num_clks = of_count_phandle_with_args(node, "clocks", "#clock-cells"); + num_clks = of_clk_get_parent_count(node); for (i = 0; i < num_clks; i++) { clk = of_clk_get(node, i); -- cgit v1.2.3-58-ga151 From 90acb40f1874f7b304b1d8d9b07c72aa83337e31 Mon Sep 17 00:00:00 2001 From: James Liao Date: Thu, 21 May 2015 15:12:52 +0800 Subject: clk: mediatek: Fix apmixedsys clock registration The size of clk_data should be the same as CLK_APMIXED_NR_CLK instead of ARRAY_SIZE(plls). CLK_APMIXED_* is numbered from 1, so CLK_APMIXED_NR_CLK will be greater than ARRAY_SIZE(plls). Signed-off-by: James Liao Acked-by: Sascha Hauer Signed-off-by: Stephen Boyd --- drivers/clk/mediatek/clk-mt8135.c | 2 +- drivers/clk/mediatek/clk-mt8173.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c index a63435b95822..08b4b849b491 100644 --- a/drivers/clk/mediatek/clk-mt8135.c +++ b/drivers/clk/mediatek/clk-mt8135.c @@ -634,7 +634,7 @@ static void __init mtk_apmixedsys_init(struct device_node *node) { struct clk_onecell_data *clk_data; - clk_data = mtk_alloc_clk_data(ARRAY_SIZE(plls)); + clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); if (!clk_data) return; diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c index 357b080eb04f..4b9e04cdf7e8 100644 --- a/drivers/clk/mediatek/clk-mt8173.c +++ b/drivers/clk/mediatek/clk-mt8173.c @@ -818,7 +818,7 @@ static void __init mtk_apmixedsys_init(struct device_node *node) { struct clk_onecell_data *clk_data; - clk_data = mtk_alloc_clk_data(ARRAY_SIZE(plls)); + clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); if (!clk_data) return; -- cgit v1.2.3-58-ga151 From 4a1caed3d0c2fbf8c9f18909bec69e2aa2638b97 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 28 May 2015 10:45:51 +0200 Subject: clk: make several parent names const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 2893c379461a ("clk: make strings in parent name arrays const") the name of parent clocks can be const. So add more const in several clock drivers. Signed-off-by: Uwe Kleine-König Acked-by: Heiko Stuebner Acked-by: Sylwester Nawrocki Signed-off-by: Stephen Boyd --- drivers/clk/hisilicon/clk-hi3620.c | 70 ++++++++++++++-------------- drivers/clk/hisilicon/clk-hix5hd2.c | 6 +-- drivers/clk/hisilicon/clk.h | 2 +- drivers/clk/mxs/clk-imx23.c | 12 ++--- drivers/clk/mxs/clk-imx28.c | 18 ++++---- drivers/clk/mxs/clk.h | 2 +- drivers/clk/pxa/clk-pxa.h | 2 +- drivers/clk/rockchip/clk-cpu.c | 2 +- drivers/clk/rockchip/clk-mmc-phase.c | 2 +- drivers/clk/rockchip/clk-pll.c | 8 ++-- drivers/clk/rockchip/clk.c | 8 ++-- drivers/clk/rockchip/clk.h | 20 ++++---- drivers/clk/samsung/clk-pll.c | 4 +- drivers/clk/samsung/clk-s5pv210.c | 88 ++++++++++++++++++------------------ drivers/clk/samsung/clk.c | 13 +++--- drivers/clk/samsung/clk.h | 18 ++++---- drivers/clk/zynq/clkc.c | 25 ++++++---- 17 files changed, 154 insertions(+), 146 deletions(-) diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c index 472dd2cb10b3..715d34a5ef9b 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -38,44 +38,44 @@ #include "clk.h" /* clock parent list */ -static const char *timer0_mux_p[] __initdata = { "osc32k", "timerclk01", }; -static const char *timer1_mux_p[] __initdata = { "osc32k", "timerclk01", }; -static const char *timer2_mux_p[] __initdata = { "osc32k", "timerclk23", }; -static const char *timer3_mux_p[] __initdata = { "osc32k", "timerclk23", }; -static const char *timer4_mux_p[] __initdata = { "osc32k", "timerclk45", }; -static const char *timer5_mux_p[] __initdata = { "osc32k", "timerclk45", }; -static const char *timer6_mux_p[] __initdata = { "osc32k", "timerclk67", }; -static const char *timer7_mux_p[] __initdata = { "osc32k", "timerclk67", }; -static const char *timer8_mux_p[] __initdata = { "osc32k", "timerclk89", }; -static const char *timer9_mux_p[] __initdata = { "osc32k", "timerclk89", }; -static const char *uart0_mux_p[] __initdata = { "osc26m", "pclk", }; -static const char *uart1_mux_p[] __initdata = { "osc26m", "pclk", }; -static const char *uart2_mux_p[] __initdata = { "osc26m", "pclk", }; -static const char *uart3_mux_p[] __initdata = { "osc26m", "pclk", }; -static const char *uart4_mux_p[] __initdata = { "osc26m", "pclk", }; -static const char *spi0_mux_p[] __initdata = { "osc26m", "rclk_cfgaxi", }; -static const char *spi1_mux_p[] __initdata = { "osc26m", "rclk_cfgaxi", }; -static const char *spi2_mux_p[] __initdata = { "osc26m", "rclk_cfgaxi", }; +static const char *const timer0_mux_p[] __initconst = { "osc32k", "timerclk01", }; +static const char *const timer1_mux_p[] __initconst = { "osc32k", "timerclk01", }; +static const char *const timer2_mux_p[] __initconst = { "osc32k", "timerclk23", }; +static const char *const timer3_mux_p[] __initconst = { "osc32k", "timerclk23", }; +static const char *const timer4_mux_p[] __initconst = { "osc32k", "timerclk45", }; +static const char *const timer5_mux_p[] __initconst = { "osc32k", "timerclk45", }; +static const char *const timer6_mux_p[] __initconst = { "osc32k", "timerclk67", }; +static const char *const timer7_mux_p[] __initconst = { "osc32k", "timerclk67", }; +static const char *const timer8_mux_p[] __initconst = { "osc32k", "timerclk89", }; +static const char *const timer9_mux_p[] __initconst = { "osc32k", "timerclk89", }; +static const char *const uart0_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart1_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart2_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart3_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const uart4_mux_p[] __initconst = { "osc26m", "pclk", }; +static const char *const spi0_mux_p[] __initconst = { "osc26m", "rclk_cfgaxi", }; +static const char *const spi1_mux_p[] __initconst = { "osc26m", "rclk_cfgaxi", }; +static const char *const spi2_mux_p[] __initconst = { "osc26m", "rclk_cfgaxi", }; /* share axi parent */ -static const char *saxi_mux_p[] __initdata = { "armpll3", "armpll2", }; -static const char *pwm0_mux_p[] __initdata = { "osc32k", "osc26m", }; -static const char *pwm1_mux_p[] __initdata = { "osc32k", "osc26m", }; -static const char *sd_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *mmc1_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *mmc1_mux2_p[] __initdata = { "osc26m", "mmc1_div", }; -static const char *g2d_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *venc_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *vdec_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *vpp_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *edc0_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *ldi0_mux_p[] __initdata = { "armpll2", "armpll4", +static const char *const saxi_mux_p[] __initconst = { "armpll3", "armpll2", }; +static const char *const pwm0_mux_p[] __initconst = { "osc32k", "osc26m", }; +static const char *const pwm1_mux_p[] __initconst = { "osc32k", "osc26m", }; +static const char *const sd_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const mmc1_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const mmc1_mux2_p[] __initconst = { "osc26m", "mmc1_div", }; +static const char *const g2d_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const venc_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const vdec_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const vpp_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const edc0_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const ldi0_mux_p[] __initconst = { "armpll2", "armpll4", "armpll3", "armpll5", }; -static const char *edc1_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *ldi1_mux_p[] __initdata = { "armpll2", "armpll4", +static const char *const edc1_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const ldi1_mux_p[] __initconst = { "armpll2", "armpll4", "armpll3", "armpll5", }; -static const char *rclk_hsic_p[] __initdata = { "armpll3", "armpll2", }; -static const char *mmc2_mux_p[] __initdata = { "armpll2", "armpll3", }; -static const char *mmc3_mux_p[] __initdata = { "armpll2", "armpll3", }; +static const char *const rclk_hsic_p[] __initconst = { "armpll3", "armpll2", }; +static const char *const mmc2_mux_p[] __initconst = { "armpll2", "armpll3", }; +static const char *const mmc3_mux_p[] __initconst = { "armpll2", "armpll3", }; /* fixed rate clocks */ diff --git a/drivers/clk/hisilicon/clk-hix5hd2.c b/drivers/clk/hisilicon/clk-hix5hd2.c index 78bd95b6fc7b..0aaf29da8491 100644 --- a/drivers/clk/hisilicon/clk-hix5hd2.c +++ b/drivers/clk/hisilicon/clk-hix5hd2.c @@ -46,15 +46,15 @@ static struct hisi_fixed_rate_clock hix5hd2_fixed_rate_clks[] __initdata = { { HIX5HD2_FIXED_83M, "83m", NULL, CLK_IS_ROOT, 83333333, }, }; -static const char *sfc_mux_p[] __initdata = { +static const char *const sfc_mux_p[] __initconst = { "24m", "150m", "200m", "100m", "75m", }; static u32 sfc_mux_table[] = {0, 4, 5, 6, 7}; -static const char *sdio_mux_p[] __initdata = { +static const char *const sdio_mux_p[] __initconst = { "75m", "100m", "50m", "15m", }; static u32 sdio_mux_table[] = {0, 1, 2, 3}; -static const char *fephy_mux_p[] __initdata = { "25m", "125m"}; +static const char *const fephy_mux_p[] __initconst = { "25m", "125m"}; static u32 fephy_mux_table[] = {0, 1}; diff --git a/drivers/clk/hisilicon/clk.h b/drivers/clk/hisilicon/clk.h index 21b403b3423a..b56fbc1c5f27 100644 --- a/drivers/clk/hisilicon/clk.h +++ b/drivers/clk/hisilicon/clk.h @@ -55,7 +55,7 @@ struct hisi_fixed_factor_clock { struct hisi_mux_clock { unsigned int id; const char *name; - const char **parent_names; + const char *const *parent_names; u8 num_parents; unsigned long flags; unsigned long offset; diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c index 22d136aa699f..32216f9b7f03 100644 --- a/drivers/clk/mxs/clk-imx23.c +++ b/drivers/clk/mxs/clk-imx23.c @@ -77,12 +77,12 @@ static void __init clk_misc_init(void) writel_relaxed(30 << BP_FRAC_IOFRAC, FRAC + SET); } -static const char *sel_pll[] __initdata = { "pll", "ref_xtal", }; -static const char *sel_cpu[] __initdata = { "ref_cpu", "ref_xtal", }; -static const char *sel_pix[] __initdata = { "ref_pix", "ref_xtal", }; -static const char *sel_io[] __initdata = { "ref_io", "ref_xtal", }; -static const char *cpu_sels[] __initdata = { "cpu_pll", "cpu_xtal", }; -static const char *emi_sels[] __initdata = { "emi_pll", "emi_xtal", }; +static const char *const sel_pll[] __initconst = { "pll", "ref_xtal", }; +static const char *const sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", }; +static const char *const sel_pix[] __initconst = { "ref_pix", "ref_xtal", }; +static const char *const sel_io[] __initconst = { "ref_io", "ref_xtal", }; +static const char *const cpu_sels[] __initconst = { "cpu_pll", "cpu_xtal", }; +static const char *const emi_sels[] __initconst = { "emi_pll", "emi_xtal", }; enum imx23_clk { ref_xtal, pll, ref_cpu, ref_emi, ref_pix, ref_io, saif_sel, diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index b1be3746ce95..a68670868baa 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -125,15 +125,15 @@ static void __init clk_misc_init(void) writel_relaxed(val, FRAC0); } -static const char *sel_cpu[] __initdata = { "ref_cpu", "ref_xtal", }; -static const char *sel_io0[] __initdata = { "ref_io0", "ref_xtal", }; -static const char *sel_io1[] __initdata = { "ref_io1", "ref_xtal", }; -static const char *sel_pix[] __initdata = { "ref_pix", "ref_xtal", }; -static const char *sel_gpmi[] __initdata = { "ref_gpmi", "ref_xtal", }; -static const char *sel_pll0[] __initdata = { "pll0", "ref_xtal", }; -static const char *cpu_sels[] __initdata = { "cpu_pll", "cpu_xtal", }; -static const char *emi_sels[] __initdata = { "emi_pll", "emi_xtal", }; -static const char *ptp_sels[] __initdata = { "ref_xtal", "pll0", }; +static const char *const sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", }; +static const char *const sel_io0[] __initconst = { "ref_io0", "ref_xtal", }; +static const char *const sel_io1[] __initconst = { "ref_io1", "ref_xtal", }; +static const char *const sel_pix[] __initconst = { "ref_pix", "ref_xtal", }; +static const char *const sel_gpmi[] __initconst = { "ref_gpmi", "ref_xtal", }; +static const char *const sel_pll0[] __initconst = { "pll0", "ref_xtal", }; +static const char *const cpu_sels[] __initconst = { "cpu_pll", "cpu_xtal", }; +static const char *const emi_sels[] __initconst = { "emi_pll", "emi_xtal", }; +static const char *const ptp_sels[] __initconst = { "ref_xtal", "pll0", }; enum imx28_clk { ref_xtal, pll0, pll1, pll2, ref_cpu, ref_emi, ref_io0, ref_io1, diff --git a/drivers/clk/mxs/clk.h b/drivers/clk/mxs/clk.h index ef10ad9b5daa..f07d821dd75d 100644 --- a/drivers/clk/mxs/clk.h +++ b/drivers/clk/mxs/clk.h @@ -49,7 +49,7 @@ static inline struct clk *mxs_clk_gate(const char *name, } static inline struct clk *mxs_clk_mux(const char *name, void __iomem *reg, - u8 shift, u8 width, const char **parent_names, int num_parents) + u8 shift, u8 width, const char *const *parent_names, int num_parents) { return clk_register_mux(NULL, name, parent_names, num_parents, CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, diff --git a/drivers/clk/pxa/clk-pxa.h b/drivers/clk/pxa/clk-pxa.h index b04c5b9c0ea8..7bf532dca709 100644 --- a/drivers/clk/pxa/clk-pxa.h +++ b/drivers/clk/pxa/clk-pxa.h @@ -14,7 +14,7 @@ #define _CLK_PXA_ #define PARENTS(name) \ - static const char *name ## _parents[] __initdata + static const char *const name ## _parents[] __initconst #define MUX_RO_RATE_RO_OPS(name, clk_name) \ static struct clk_hw name ## _mux_hw; \ static struct clk_hw name ## _rate_hw; \ diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c index 8539c4fd34cc..fb7721bd37e6 100644 --- a/drivers/clk/rockchip/clk-cpu.c +++ b/drivers/clk/rockchip/clk-cpu.c @@ -231,7 +231,7 @@ static int rockchip_cpuclk_notifier_cb(struct notifier_block *nb, } struct clk *rockchip_clk_register_cpuclk(const char *name, - const char **parent_names, u8 num_parents, + const char *const *parent_names, u8 num_parents, const struct rockchip_cpuclk_reg_data *reg_data, const struct rockchip_cpuclk_rate_table *rates, int nrates, void __iomem *reg_base, spinlock_t *lock) diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c index c842e3b60f21..e9f8df324e7c 100644 --- a/drivers/clk/rockchip/clk-mmc-phase.c +++ b/drivers/clk/rockchip/clk-mmc-phase.c @@ -120,7 +120,7 @@ static const struct clk_ops rockchip_mmc_clk_ops = { }; struct clk *rockchip_clk_register_mmc(const char *name, - const char **parent_names, u8 num_parents, + const char *const *parent_names, u8 num_parents, void __iomem *reg, int shift) { struct clk_init_data init; diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index f8d3baf275b2..76027261f7ed 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -329,10 +329,10 @@ static const struct clk_ops rockchip_rk3066_pll_clk_ops = { */ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, - const char *name, const char **parent_names, u8 num_parents, - void __iomem *base, int con_offset, int grf_lock_offset, - int lock_shift, int mode_offset, int mode_shift, - struct rockchip_pll_rate_table *rate_table, + const char *name, const char *const *parent_names, + u8 num_parents, void __iomem *base, int con_offset, + int grf_lock_offset, int lock_shift, int mode_offset, + int mode_shift, struct rockchip_pll_rate_table *rate_table, u8 clk_pll_flags, spinlock_t *lock) { const char *pll_parents[3]; diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index edb5d489ae61..052b94db0ff9 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -39,7 +39,7 @@ * sometimes without one of those components. */ static struct clk *rockchip_clk_register_branch(const char *name, - const char **parent_names, u8 num_parents, void __iomem *base, + const char *const *parent_names, u8 num_parents, void __iomem *base, int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags, u8 div_shift, u8 div_width, u8 div_flags, struct clk_div_table *div_table, int gate_offset, @@ -103,8 +103,8 @@ static struct clk *rockchip_clk_register_branch(const char *name, } static struct clk *rockchip_clk_register_frac_branch(const char *name, - const char **parent_names, u8 num_parents, void __iomem *base, - int muxdiv_offset, u8 div_flags, + const char *const *parent_names, u8 num_parents, + void __iomem *base, int muxdiv_offset, u8 div_flags, int gate_offset, u8 gate_shift, u8 gate_flags, unsigned long flags, spinlock_t *lock) { @@ -297,7 +297,7 @@ void __init rockchip_clk_register_branches( } void __init rockchip_clk_register_armclk(unsigned int lookup_id, - const char *name, const char **parent_names, + const char *name, const char *const *parent_names, u8 num_parents, const struct rockchip_cpuclk_reg_data *reg_data, const struct rockchip_cpuclk_rate_table *rates, diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index e63cafe893e1..6b092673048a 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -108,7 +108,7 @@ struct rockchip_pll_rate_table { struct rockchip_pll_clock { unsigned int id; const char *name; - const char **parent_names; + const char *const *parent_names; u8 num_parents; unsigned long flags; int con_offset; @@ -140,10 +140,10 @@ struct rockchip_pll_clock { } struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type, - const char *name, const char **parent_names, u8 num_parents, - void __iomem *base, int con_offset, int grf_lock_offset, - int lock_shift, int reg_mode, int mode_shift, - struct rockchip_pll_rate_table *rate_table, + const char *name, const char *const *parent_names, + u8 num_parents, void __iomem *base, int con_offset, + int grf_lock_offset, int lock_shift, int reg_mode, + int mode_shift, struct rockchip_pll_rate_table *rate_table, u8 clk_pll_flags, spinlock_t *lock); struct rockchip_cpuclk_clksel { @@ -173,16 +173,16 @@ struct rockchip_cpuclk_reg_data { }; struct clk *rockchip_clk_register_cpuclk(const char *name, - const char **parent_names, u8 num_parents, + const char *const *parent_names, u8 num_parents, const struct rockchip_cpuclk_reg_data *reg_data, const struct rockchip_cpuclk_rate_table *rates, int nrates, void __iomem *reg_base, spinlock_t *lock); struct clk *rockchip_clk_register_mmc(const char *name, - const char **parent_names, u8 num_parents, + const char *const *parent_names, u8 num_parents, void __iomem *reg, int shift); -#define PNAME(x) static const char *x[] __initdata +#define PNAME(x) static const char *const x[] __initconst enum rockchip_clk_branch_type { branch_composite, @@ -197,7 +197,7 @@ struct rockchip_clk_branch { unsigned int id; enum rockchip_clk_branch_type branch_type; const char *name; - const char **parent_names; + const char *const *parent_names; u8 num_parents; unsigned long flags; int muxdiv_offset; @@ -403,7 +403,7 @@ void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list, void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list, unsigned int nr_pll, int grf_lock_offset); void rockchip_clk_register_armclk(unsigned int lookup_id, const char *name, - const char **parent_names, u8 num_parents, + const char *const *parent_names, u8 num_parents, const struct rockchip_cpuclk_reg_data *reg_data, const struct rockchip_cpuclk_rate_table *rates, int nrates); diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 9d70e5c03804..bebc61b5fce1 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -1156,7 +1156,7 @@ static const struct clk_ops samsung_pll2650xx_clk_min_ops = { }; static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx, - struct samsung_pll_clock *pll_clk, + const struct samsung_pll_clock *pll_clk, void __iomem *base) { struct samsung_clk_pll *pll; @@ -1303,7 +1303,7 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx, } void __init samsung_clk_register_pll(struct samsung_clk_provider *ctx, - struct samsung_pll_clock *pll_list, + const struct samsung_pll_clock *pll_list, unsigned int nr_pll, void __iomem *base) { int cnt; diff --git a/drivers/clk/samsung/clk-s5pv210.c b/drivers/clk/samsung/clk-s5pv210.c index e668e479a697..cf7e8fa7b624 100644 --- a/drivers/clk/samsung/clk-s5pv210.c +++ b/drivers/clk/samsung/clk-s5pv210.c @@ -169,44 +169,44 @@ static inline void s5pv210_clk_sleep_init(void) { } #endif /* Mux parent lists. */ -static const char *fin_pll_p[] __initdata = { +static const char *const fin_pll_p[] __initconst = { "xxti", "xusbxti" }; -static const char *mout_apll_p[] __initdata = { +static const char *const mout_apll_p[] __initconst = { "fin_pll", "fout_apll" }; -static const char *mout_mpll_p[] __initdata = { +static const char *const mout_mpll_p[] __initconst = { "fin_pll", "fout_mpll" }; -static const char *mout_epll_p[] __initdata = { +static const char *const mout_epll_p[] __initconst = { "fin_pll", "fout_epll" }; -static const char *mout_vpllsrc_p[] __initdata = { +static const char *const mout_vpllsrc_p[] __initconst = { "fin_pll", "sclk_hdmi27m" }; -static const char *mout_vpll_p[] __initdata = { +static const char *const mout_vpll_p[] __initconst = { "mout_vpllsrc", "fout_vpll" }; -static const char *mout_group1_p[] __initdata = { +static const char *const mout_group1_p[] __initconst = { "dout_a2m", "mout_mpll", "mout_epll", "mout_vpll" }; -static const char *mout_group2_p[] __initdata = { +static const char *const mout_group2_p[] __initconst = { "xxti", "xusbxti", "sclk_hdmi27m", @@ -218,7 +218,7 @@ static const char *mout_group2_p[] __initdata = { "mout_vpll", }; -static const char *mout_audio0_p[] __initdata = { +static const char *const mout_audio0_p[] __initconst = { "xxti", "pcmcdclk0", "sclk_hdmi27m", @@ -230,7 +230,7 @@ static const char *mout_audio0_p[] __initdata = { "mout_vpll", }; -static const char *mout_audio1_p[] __initdata = { +static const char *const mout_audio1_p[] __initconst = { "i2scdclk1", "pcmcdclk1", "sclk_hdmi27m", @@ -242,7 +242,7 @@ static const char *mout_audio1_p[] __initdata = { "mout_vpll", }; -static const char *mout_audio2_p[] __initdata = { +static const char *const mout_audio2_p[] __initconst = { "i2scdclk2", "pcmcdclk2", "sclk_hdmi27m", @@ -254,63 +254,63 @@ static const char *mout_audio2_p[] __initdata = { "mout_vpll", }; -static const char *mout_spdif_p[] __initdata = { +static const char *const mout_spdif_p[] __initconst = { "dout_audio0", "dout_audio1", "dout_audio3", }; -static const char *mout_group3_p[] __initdata = { +static const char *const mout_group3_p[] __initconst = { "mout_apll", "mout_mpll" }; -static const char *mout_group4_p[] __initdata = { +static const char *const mout_group4_p[] __initconst = { "mout_mpll", "dout_a2m" }; -static const char *mout_flash_p[] __initdata = { +static const char *const mout_flash_p[] __initconst = { "dout_hclkd", "dout_hclkp" }; -static const char *mout_dac_p[] __initdata = { +static const char *const mout_dac_p[] __initconst = { "mout_vpll", "sclk_hdmiphy" }; -static const char *mout_hdmi_p[] __initdata = { +static const char *const mout_hdmi_p[] __initconst = { "sclk_hdmiphy", "dout_tblk" }; -static const char *mout_mixer_p[] __initdata = { +static const char *const mout_mixer_p[] __initconst = { "mout_dac", "mout_hdmi" }; -static const char *mout_vpll_6442_p[] __initdata = { +static const char *const mout_vpll_6442_p[] __initconst = { "fin_pll", "fout_vpll" }; -static const char *mout_mixer_6442_p[] __initdata = { +static const char *const mout_mixer_6442_p[] __initconst = { "mout_vpll", "dout_mixer" }; -static const char *mout_d0sync_6442_p[] __initdata = { +static const char *const mout_d0sync_6442_p[] __initconst = { "mout_dsys", "div_apll" }; -static const char *mout_d1sync_6442_p[] __initdata = { +static const char *const mout_d1sync_6442_p[] __initconst = { "mout_psys", "div_apll" }; -static const char *mout_group2_6442_p[] __initdata = { +static const char *const mout_group2_6442_p[] __initconst = { "fin_pll", "none", "none", @@ -322,7 +322,7 @@ static const char *mout_group2_6442_p[] __initdata = { "mout_vpll", }; -static const char *mout_audio0_6442_p[] __initdata = { +static const char *const mout_audio0_6442_p[] __initconst = { "fin_pll", "pcmcdclk0", "none", @@ -334,7 +334,7 @@ static const char *mout_audio0_6442_p[] __initdata = { "mout_vpll", }; -static const char *mout_audio1_6442_p[] __initdata = { +static const char *const mout_audio1_6442_p[] __initconst = { "i2scdclk1", "pcmcdclk1", "none", @@ -347,7 +347,7 @@ static const char *mout_audio1_6442_p[] __initdata = { "fin_pll", }; -static const char *mout_clksel_p[] __initdata = { +static const char *const mout_clksel_p[] __initconst = { "fout_apll_clkout", "fout_mpll_clkout", "fout_epll", @@ -370,7 +370,7 @@ static const char *mout_clksel_p[] __initdata = { "div_dclk" }; -static const char *mout_clksel_6442_p[] __initdata = { +static const char *const mout_clksel_6442_p[] __initconst = { "fout_apll_clkout", "fout_mpll_clkout", "fout_epll", @@ -393,7 +393,7 @@ static const char *mout_clksel_6442_p[] __initdata = { "div_dclk" }; -static const char *mout_clkout_p[] __initdata = { +static const char *const mout_clkout_p[] __initconst = { "dout_clkout", "none", "xxti", @@ -401,20 +401,20 @@ static const char *mout_clkout_p[] __initdata = { }; /* Common fixed factor clocks. */ -static struct samsung_fixed_factor_clock ffactor_clks[] __initdata = { +static const struct samsung_fixed_factor_clock ffactor_clks[] __initconst = { FFACTOR(FOUT_APLL_CLKOUT, "fout_apll_clkout", "fout_apll", 1, 4, 0), FFACTOR(FOUT_MPLL_CLKOUT, "fout_mpll_clkout", "fout_mpll", 1, 2, 0), FFACTOR(DOUT_APLL_CLKOUT, "dout_apll_clkout", "dout_apll", 1, 4, 0), }; /* PLL input mux (fin_pll), which needs to be registered before PLLs. */ -static struct samsung_mux_clock early_mux_clks[] __initdata = { +static const struct samsung_mux_clock early_mux_clks[] __initconst = { MUX_F(FIN_PLL, "fin_pll", fin_pll_p, OM_STAT, 0, 1, CLK_MUX_READ_ONLY, 0), }; /* Common clock muxes. */ -static struct samsung_mux_clock mux_clks[] __initdata = { +static const struct samsung_mux_clock mux_clks[] __initconst = { MUX(MOUT_FLASH, "mout_flash", mout_flash_p, CLK_SRC0, 28, 1), MUX(MOUT_PSYS, "mout_psys", mout_group4_p, CLK_SRC0, 24, 1), MUX(MOUT_DSYS, "mout_dsys", mout_group4_p, CLK_SRC0, 20, 1), @@ -427,7 +427,7 @@ static struct samsung_mux_clock mux_clks[] __initdata = { }; /* S5PV210-specific clock muxes. */ -static struct samsung_mux_clock s5pv210_mux_clks[] __initdata = { +static const struct samsung_mux_clock s5pv210_mux_clks[] __initconst = { MUX(MOUT_VPLL, "mout_vpll", mout_vpll_p, CLK_SRC0, 12, 1), MUX(MOUT_VPLLSRC, "mout_vpllsrc", mout_vpllsrc_p, CLK_SRC1, 28, 1), @@ -472,7 +472,7 @@ static struct samsung_mux_clock s5pv210_mux_clks[] __initdata = { }; /* S5P6442-specific clock muxes. */ -static struct samsung_mux_clock s5p6442_mux_clks[] __initdata = { +static const struct samsung_mux_clock s5p6442_mux_clks[] __initconst = { MUX(MOUT_VPLL, "mout_vpll", mout_vpll_6442_p, CLK_SRC0, 12, 1), MUX(MOUT_FIMD, "mout_fimd", mout_group2_6442_p, CLK_SRC1, 20, 4), @@ -504,7 +504,7 @@ static struct samsung_mux_clock s5p6442_mux_clks[] __initdata = { }; /* S5PV210-specific fixed rate clocks generated inside the SoC. */ -static struct samsung_fixed_rate_clock s5pv210_frate_clks[] __initdata = { +static const struct samsung_fixed_rate_clock s5pv210_frate_clks[] __initconst = { FRATE(SCLK_HDMI27M, "sclk_hdmi27m", NULL, CLK_IS_ROOT, 27000000), FRATE(SCLK_HDMIPHY, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 27000000), FRATE(SCLK_USBPHY0, "sclk_usbphy0", NULL, CLK_IS_ROOT, 48000000), @@ -512,12 +512,12 @@ static struct samsung_fixed_rate_clock s5pv210_frate_clks[] __initdata = { }; /* S5P6442-specific fixed rate clocks generated inside the SoC. */ -static struct samsung_fixed_rate_clock s5p6442_frate_clks[] __initdata = { +static const struct samsung_fixed_rate_clock s5p6442_frate_clks[] __initconst = { FRATE(SCLK_USBPHY0, "sclk_usbphy0", NULL, CLK_IS_ROOT, 30000000), }; /* Common clock dividers. */ -static struct samsung_div_clock div_clks[] __initdata = { +static const struct samsung_div_clock div_clks[] __initconst = { DIV(DOUT_PCLKP, "dout_pclkp", "dout_hclkp", CLK_DIV0, 28, 3), DIV(DOUT_PCLKD, "dout_pclkd", "dout_hclkd", CLK_DIV0, 20, 3), DIV(DOUT_A2M, "dout_a2m", "mout_apll", CLK_DIV0, 4, 3), @@ -549,7 +549,7 @@ static struct samsung_div_clock div_clks[] __initdata = { }; /* S5PV210-specific clock dividers. */ -static struct samsung_div_clock s5pv210_div_clks[] __initdata = { +static const struct samsung_div_clock s5pv210_div_clks[] __initconst = { DIV(DOUT_HCLKP, "dout_hclkp", "mout_psys", CLK_DIV0, 24, 4), DIV(DOUT_HCLKD, "dout_hclkd", "mout_dsys", CLK_DIV0, 16, 4), DIV(DOUT_PCLKM, "dout_pclkm", "dout_hclkm", CLK_DIV0, 12, 3), @@ -578,7 +578,7 @@ static struct samsung_div_clock s5pv210_div_clks[] __initdata = { }; /* S5P6442-specific clock dividers. */ -static struct samsung_div_clock s5p6442_div_clks[] __initdata = { +static const struct samsung_div_clock s5p6442_div_clks[] __initconst = { DIV(DOUT_HCLKP, "dout_hclkp", "mout_d1sync", CLK_DIV0, 24, 4), DIV(DOUT_HCLKD, "dout_hclkd", "mout_d0sync", CLK_DIV0, 16, 4), @@ -586,7 +586,7 @@ static struct samsung_div_clock s5p6442_div_clks[] __initdata = { }; /* Common clock gates. */ -static struct samsung_gate_clock gate_clks[] __initdata = { +static const struct samsung_gate_clock gate_clks[] __initconst = { GATE(CLK_ROTATOR, "rotator", "dout_hclkd", CLK_GATE_IP0, 29, 0, 0), GATE(CLK_FIMC2, "fimc2", "dout_hclkd", CLK_GATE_IP0, 26, 0, 0), GATE(CLK_FIMC1, "fimc1", "dout_hclkd", CLK_GATE_IP0, 25, 0, 0), @@ -666,7 +666,7 @@ static struct samsung_gate_clock gate_clks[] __initdata = { }; /* S5PV210-specific clock gates. */ -static struct samsung_gate_clock s5pv210_gate_clks[] __initdata = { +static const struct samsung_gate_clock s5pv210_gate_clks[] __initconst = { GATE(CLK_CSIS, "clk_csis", "dout_hclkd", CLK_GATE_IP0, 31, 0, 0), GATE(CLK_MFC, "mfc", "dout_hclkm", CLK_GATE_IP0, 16, 0, 0), GATE(CLK_G2D, "g2d", "dout_hclkd", CLK_GATE_IP0, 12, 0, 0), @@ -728,7 +728,7 @@ static struct samsung_gate_clock s5pv210_gate_clks[] __initdata = { }; /* S5P6442-specific clock gates. */ -static struct samsung_gate_clock s5p6442_gate_clks[] __initdata = { +static const struct samsung_gate_clock s5p6442_gate_clks[] __initconst = { GATE(CLK_JPEG, "jpeg", "dout_hclkd", CLK_GATE_IP0, 28, 0, 0), GATE(CLK_MFC, "mfc", "dout_hclkd", CLK_GATE_IP0, 16, 0, 0), GATE(CLK_G2D, "g2d", "dout_hclkd", CLK_GATE_IP0, 12, 0, 0), @@ -748,14 +748,14 @@ static struct samsung_gate_clock s5p6442_gate_clks[] __initdata = { * Clock aliases for legacy clkdev look-up. * NOTE: Needed only to support legacy board files. */ -static struct samsung_clock_alias s5pv210_aliases[] = { +static const struct samsung_clock_alias s5pv210_aliases[] __initconst = { ALIAS(DOUT_APLL, NULL, "armclk"), ALIAS(DOUT_HCLKM, NULL, "hclk_msys"), ALIAS(MOUT_DMC0, NULL, "sclk_dmc0"), }; /* S5PV210-specific PLLs. */ -static struct samsung_pll_clock s5pv210_pll_clks[] __initdata = { +static const struct samsung_pll_clock s5pv210_pll_clks[] __initconst = { [apll] = PLL(pll_4508, FOUT_APLL, "fout_apll", "fin_pll", APLL_LOCK, APLL_CON0, NULL), [mpll] = PLL(pll_4502, FOUT_MPLL, "fout_mpll", "fin_pll", @@ -767,7 +767,7 @@ static struct samsung_pll_clock s5pv210_pll_clks[] __initdata = { }; /* S5P6442-specific PLLs. */ -static struct samsung_pll_clock s5p6442_pll_clks[] __initdata = { +static const struct samsung_pll_clock s5p6442_pll_clks[] __initconst = { [apll] = PLL(pll_4502, FOUT_APLL, "fout_apll", "fin_pll", APLL_LOCK, APLL_CON0, NULL), [mpll] = PLL(pll_4502, FOUT_MPLL, "fout_mpll", "fin_pll", diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c index 76491629d1e5..0117238391d6 100644 --- a/drivers/clk/samsung/clk.c +++ b/drivers/clk/samsung/clk.c @@ -98,7 +98,7 @@ void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk, /* register a list of aliases */ void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx, - struct samsung_clock_alias *list, + const struct samsung_clock_alias *list, unsigned int nr_clk) { struct clk *clk; @@ -132,7 +132,8 @@ void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx, /* register a list of fixed clocks */ void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx, - struct samsung_fixed_rate_clock *list, unsigned int nr_clk) + const struct samsung_fixed_rate_clock *list, + unsigned int nr_clk) { struct clk *clk; unsigned int idx, ret; @@ -161,7 +162,7 @@ void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx, /* register a list of fixed factor clocks */ void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx, - struct samsung_fixed_factor_clock *list, unsigned int nr_clk) + const struct samsung_fixed_factor_clock *list, unsigned int nr_clk) { struct clk *clk; unsigned int idx; @@ -181,7 +182,7 @@ void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx, /* register a list of mux clocks */ void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx, - struct samsung_mux_clock *list, + const struct samsung_mux_clock *list, unsigned int nr_clk) { struct clk *clk; @@ -213,7 +214,7 @@ void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx, /* register a list of div clocks */ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, - struct samsung_div_clock *list, + const struct samsung_div_clock *list, unsigned int nr_clk) { struct clk *clk; @@ -252,7 +253,7 @@ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, /* register a list of gate clocks */ void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx, - struct samsung_gate_clock *list, + const struct samsung_gate_clock *list, unsigned int nr_clk) { struct clk *clk; diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h index e4c75383cea7..b775fc29caa5 100644 --- a/drivers/clk/samsung/clk.h +++ b/drivers/clk/samsung/clk.h @@ -121,7 +121,7 @@ struct samsung_mux_clock { unsigned int id; const char *dev_name; const char *name; - const char **parent_names; + const char *const *parent_names; u8 num_parents; unsigned long flags; unsigned long offset; @@ -368,28 +368,28 @@ extern void __init samsung_clk_of_register_fixed_ext( extern void samsung_clk_add_lookup(struct samsung_clk_provider *ctx, struct clk *clk, unsigned int id); -extern void samsung_clk_register_alias(struct samsung_clk_provider *ctx, - struct samsung_clock_alias *list, +extern void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx, + const struct samsung_clock_alias *list, unsigned int nr_clk); extern void __init samsung_clk_register_fixed_rate( struct samsung_clk_provider *ctx, - struct samsung_fixed_rate_clock *clk_list, + const struct samsung_fixed_rate_clock *clk_list, unsigned int nr_clk); extern void __init samsung_clk_register_fixed_factor( struct samsung_clk_provider *ctx, - struct samsung_fixed_factor_clock *list, + const struct samsung_fixed_factor_clock *list, unsigned int nr_clk); extern void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx, - struct samsung_mux_clock *clk_list, + const struct samsung_mux_clock *clk_list, unsigned int nr_clk); extern void __init samsung_clk_register_div(struct samsung_clk_provider *ctx, - struct samsung_div_clock *clk_list, + const struct samsung_div_clock *clk_list, unsigned int nr_clk); extern void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx, - struct samsung_gate_clock *clk_list, + const struct samsung_gate_clock *clk_list, unsigned int nr_clk); extern void __init samsung_clk_register_pll(struct samsung_clk_provider *ctx, - struct samsung_pll_clock *pll_list, + const struct samsung_pll_clock *pll_list, unsigned int nr_clk, void __iomem *base); extern struct samsung_clk_provider __init *samsung_cmu_register_one( diff --git a/drivers/clk/zynq/clkc.c b/drivers/clk/zynq/clkc.c index 40cb113be6af..de614384bb44 100644 --- a/drivers/clk/zynq/clkc.c +++ b/drivers/clk/zynq/clkc.c @@ -85,22 +85,29 @@ static DEFINE_SPINLOCK(canmioclk_lock); static DEFINE_SPINLOCK(dbgclk_lock); static DEFINE_SPINLOCK(aperclk_lock); -static const char *armpll_parents[] __initdata = {"armpll_int", "ps_clk"}; -static const char *ddrpll_parents[] __initdata = {"ddrpll_int", "ps_clk"}; -static const char *iopll_parents[] __initdata = {"iopll_int", "ps_clk"}; +static const char *const armpll_parents[] __initconst = {"armpll_int", + "ps_clk"}; +static const char *const ddrpll_parents[] __initconst = {"ddrpll_int", + "ps_clk"}; +static const char *const iopll_parents[] __initconst = {"iopll_int", + "ps_clk"}; static const char *gem0_mux_parents[] __initdata = {"gem0_div1", "dummy_name"}; static const char *gem1_mux_parents[] __initdata = {"gem1_div1", "dummy_name"}; -static const char *can0_mio_mux2_parents[] __initdata = {"can0_gate", +static const char *const can0_mio_mux2_parents[] __initconst = {"can0_gate", "can0_mio_mux"}; -static const char *can1_mio_mux2_parents[] __initdata = {"can1_gate", +static const char *const can1_mio_mux2_parents[] __initconst = {"can1_gate", "can1_mio_mux"}; static const char *dbg_emio_mux_parents[] __initdata = {"dbg_div", "dummy_name"}; -static const char *dbgtrc_emio_input_names[] __initdata = {"trace_emio_clk"}; -static const char *gem0_emio_input_names[] __initdata = {"gem0_emio_clk"}; -static const char *gem1_emio_input_names[] __initdata = {"gem1_emio_clk"}; -static const char *swdt_ext_clk_input_names[] __initdata = {"swdt_ext_clk"}; +static const char *const dbgtrc_emio_input_names[] __initconst = { + "trace_emio_clk"}; +static const char *const gem0_emio_input_names[] __initconst = { + "gem0_emio_clk"}; +static const char *const gem1_emio_input_names[] __initconst = { + "gem1_emio_clk"}; +static const char *const swdt_ext_clk_input_names[] __initconst = { + "swdt_ext_clk"}; static void __init zynq_clk_register_fclk(enum zynq_clk fclk, const char *clk_name, void __iomem *fclk_ctrl_reg, -- cgit v1.2.3-58-ga151 From 7a29a869434e8b7a71972227f0920e2f8ea777bd Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Mon, 1 Jun 2015 13:13:53 +0200 Subject: clk: meson: Add support for Meson clock controller This patchset adds the infrastructure for registering and managing the core clocks found on Amlogic MesonX SoCs. In particular: - PLLs - CPU clock - Fixed rate clocks, fixed factor clocks, ... Signed-off-by: Carlo Caione Signed-off-by: Stephen Boyd --- drivers/clk/Makefile | 1 + drivers/clk/meson/Makefile | 5 + drivers/clk/meson/clk-cpu.c | 234 +++++++++++++++++++++++++++++ drivers/clk/meson/clk-pll.c | 227 ++++++++++++++++++++++++++++ drivers/clk/meson/clkc.c | 250 +++++++++++++++++++++++++++++++ drivers/clk/meson/clkc.h | 187 +++++++++++++++++++++++ include/dt-bindings/clock/meson8b-clkc.h | 25 ++++ 7 files changed, 929 insertions(+) create mode 100644 drivers/clk/meson/Makefile create mode 100644 drivers/clk/meson/clk-cpu.c create mode 100644 drivers/clk/meson/clk-pll.c create mode 100644 drivers/clk/meson/clkc.c create mode 100644 drivers/clk/meson/clkc.h create mode 100644 include/dt-bindings/clock/meson8b-clkc.h diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 3d00c25382c5..9c4ddb1b0845 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -55,6 +55,7 @@ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif obj-$(CONFIG_PLAT_ORION) += mvebu/ +obj-$(CONFIG_ARCH_MESON) += meson/ obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_MACH_PISTACHIO) += pistachio/ obj-$(CONFIG_COMMON_CLK_PXA) += pxa/ diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile new file mode 100644 index 000000000000..66c6d8d23553 --- /dev/null +++ b/drivers/clk/meson/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for Meson specific clk +# + +obj-y += clkc.o clk-pll.o clk-cpu.o diff --git a/drivers/clk/meson/clk-cpu.c b/drivers/clk/meson/clk-cpu.c new file mode 100644 index 000000000000..148e99fc5970 --- /dev/null +++ b/drivers/clk/meson/clk-cpu.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/* + * CPU clock path: + * + * +-[/N]-----|3| + * MUX2 +--[/3]-+----------|2| MUX1 + * [sys_pll]---|1| |--[/2]------------|1|-|1| + * | |---+------------------|0| | |----- [a5_clk] + * +--|0| | | + * [xtal]---+-------------------------------|0| + * + * + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MESON_CPU_CLK_CNTL1 0x00 +#define MESON_CPU_CLK_CNTL 0x40 + +#define MESON_CPU_CLK_MUX1 BIT(7) +#define MESON_CPU_CLK_MUX2 BIT(0) + +#define MESON_N_WIDTH 9 +#define MESON_N_SHIFT 20 +#define MESON_SEL_WIDTH 2 +#define MESON_SEL_SHIFT 2 + +#include "clkc.h" + +struct meson_clk_cpu { + struct notifier_block clk_nb; + const struct clk_div_table *div_table; + struct clk_hw hw; + void __iomem *base; + u16 reg_off; +}; +#define to_meson_clk_cpu_hw(_hw) container_of(_hw, struct meson_clk_cpu, hw) +#define to_meson_clk_cpu_nb(_nb) container_of(_nb, struct meson_clk_cpu, clk_nb) + +static long meson_clk_cpu_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw); + + return divider_round_rate(hw, rate, prate, clk_cpu->div_table, + MESON_N_WIDTH, CLK_DIVIDER_ROUND_CLOSEST); +} + +static int meson_clk_cpu_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw); + unsigned int div, sel, N = 0; + u32 reg; + + div = DIV_ROUND_UP(parent_rate, rate); + + if (div <= 3) { + sel = div - 1; + } else { + sel = 3; + N = div / 2; + } + + reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1); + reg = PARM_SET(MESON_N_WIDTH, MESON_N_SHIFT, reg, N); + writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1); + + reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL); + reg = PARM_SET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg, sel); + writel(reg, clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL); + + return 0; +} + +static unsigned long meson_clk_cpu_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_hw(hw); + unsigned int N, sel; + unsigned int div = 1; + u32 reg; + + reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL1); + N = PARM_GET(MESON_N_WIDTH, MESON_N_SHIFT, reg); + + reg = readl(clk_cpu->base + clk_cpu->reg_off + MESON_CPU_CLK_CNTL); + sel = PARM_GET(MESON_SEL_WIDTH, MESON_SEL_SHIFT, reg); + + if (sel < 3) + div = sel + 1; + else + div = 2 * N; + + return parent_rate / div; +} + +static int meson_clk_cpu_pre_rate_change(struct meson_clk_cpu *clk_cpu, + struct clk_notifier_data *ndata) +{ + u32 cpu_clk_cntl; + + /* switch MUX1 to xtal */ + cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off + + MESON_CPU_CLK_CNTL); + cpu_clk_cntl &= ~MESON_CPU_CLK_MUX1; + writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off + + MESON_CPU_CLK_CNTL); + udelay(100); + + /* switch MUX2 to sys-pll */ + cpu_clk_cntl |= MESON_CPU_CLK_MUX2; + writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off + + MESON_CPU_CLK_CNTL); + + return 0; +} + +static int meson_clk_cpu_post_rate_change(struct meson_clk_cpu *clk_cpu, + struct clk_notifier_data *ndata) +{ + u32 cpu_clk_cntl; + + /* switch MUX1 to divisors' output */ + cpu_clk_cntl = readl(clk_cpu->base + clk_cpu->reg_off + + MESON_CPU_CLK_CNTL); + cpu_clk_cntl |= MESON_CPU_CLK_MUX1; + writel(cpu_clk_cntl, clk_cpu->base + clk_cpu->reg_off + + MESON_CPU_CLK_CNTL); + udelay(100); + + return 0; +} + +/* + * This clock notifier is called when the frequency of the of the parent + * PLL clock is to be changed. We use the xtal input as temporary parent + * while the PLL frequency is stabilized. + */ +static int meson_clk_cpu_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct meson_clk_cpu *clk_cpu = to_meson_clk_cpu_nb(nb); + int ret = 0; + + if (event == PRE_RATE_CHANGE) + ret = meson_clk_cpu_pre_rate_change(clk_cpu, ndata); + else if (event == POST_RATE_CHANGE) + ret = meson_clk_cpu_post_rate_change(clk_cpu, ndata); + + return notifier_from_errno(ret); +} + +static const struct clk_ops meson_clk_cpu_ops = { + .recalc_rate = meson_clk_cpu_recalc_rate, + .round_rate = meson_clk_cpu_round_rate, + .set_rate = meson_clk_cpu_set_rate, +}; + +struct clk *meson_clk_register_cpu(const struct clk_conf *clk_conf, + void __iomem *reg_base, + spinlock_t *lock) +{ + struct clk *clk; + struct clk *pclk; + struct meson_clk_cpu *clk_cpu; + struct clk_init_data init; + int ret; + + clk_cpu = kzalloc(sizeof(*clk_cpu), GFP_KERNEL); + if (!clk_cpu) + return ERR_PTR(-ENOMEM); + + clk_cpu->base = reg_base; + clk_cpu->reg_off = clk_conf->reg_off; + clk_cpu->div_table = clk_conf->conf.div_table; + clk_cpu->clk_nb.notifier_call = meson_clk_cpu_notifier_cb; + + init.name = clk_conf->clk_name; + init.ops = &meson_clk_cpu_ops; + init.flags = clk_conf->flags | CLK_GET_RATE_NOCACHE; + init.flags |= CLK_SET_RATE_PARENT; + init.parent_names = clk_conf->clks_parent; + init.num_parents = 1; + + clk_cpu->hw.init = &init; + + pclk = __clk_lookup(clk_conf->clks_parent[0]); + if (!pclk) { + pr_err("%s: could not lookup parent clock %s\n", + __func__, clk_conf->clks_parent[0]); + return ERR_PTR(-EINVAL); + } + + ret = clk_notifier_register(pclk, &clk_cpu->clk_nb); + if (ret) { + pr_err("%s: failed to register clock notifier for %s\n", + __func__, clk_conf->clk_name); + return ERR_PTR(-EINVAL); + } + + clk = clk_register(NULL, &clk_cpu->hw); + if (IS_ERR(clk)) { + clk_notifier_unregister(pclk, &clk_cpu->clk_nb); + kfree(clk_cpu); + } + + return clk; +} + diff --git a/drivers/clk/meson/clk-pll.c b/drivers/clk/meson/clk-pll.c new file mode 100644 index 000000000000..664edf0708ea --- /dev/null +++ b/drivers/clk/meson/clk-pll.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +/* + * In the most basic form, a Meson PLL is composed as follows: + * + * PLL + * +------------------------------+ + * | | + * in -----[ /N ]---[ *M ]---[ >>OD ]----->> out + * | ^ ^ | + * +------------------------------+ + * | | + * FREF VCO + * + * out = (in * M / N) >> OD + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clkc.h" + +#define MESON_PLL_RESET BIT(29) +#define MESON_PLL_LOCK BIT(31) + +struct meson_clk_pll { + struct clk_hw hw; + void __iomem *base; + struct pll_conf *conf; + unsigned int rate_count; + spinlock_t *lock; +}; +#define to_meson_clk_pll(_hw) container_of(_hw, struct meson_clk_pll, hw) + +static unsigned long meson_clk_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct meson_clk_pll *pll = to_meson_clk_pll(hw); + struct parm *p; + unsigned long parent_rate_mhz = parent_rate / 1000000; + unsigned long rate_mhz; + u16 n, m, od; + u32 reg; + + p = &pll->conf->n; + reg = readl(pll->base + p->reg_off); + n = PARM_GET(p->width, p->shift, reg); + + p = &pll->conf->m; + reg = readl(pll->base + p->reg_off); + m = PARM_GET(p->width, p->shift, reg); + + p = &pll->conf->od; + reg = readl(pll->base + p->reg_off); + od = PARM_GET(p->width, p->shift, reg); + + rate_mhz = (parent_rate_mhz * m / n) >> od; + + return rate_mhz * 1000000; +} + +static long meson_clk_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct meson_clk_pll *pll = to_meson_clk_pll(hw); + const struct pll_rate_table *rate_table = pll->conf->rate_table; + int i; + + for (i = 0; i < pll->rate_count; i++) { + if (rate <= rate_table[i].rate) + return rate_table[i].rate; + } + + /* else return the smallest value */ + return rate_table[0].rate; +} + +static const struct pll_rate_table *meson_clk_get_pll_settings(struct meson_clk_pll *pll, + unsigned long rate) +{ + const struct pll_rate_table *rate_table = pll->conf->rate_table; + int i; + + for (i = 0; i < pll->rate_count; i++) { + if (rate == rate_table[i].rate) + return &rate_table[i]; + } + return NULL; +} + +static int meson_clk_pll_wait_lock(struct meson_clk_pll *pll, + struct parm *p_n) +{ + int delay = 24000000; + u32 reg; + + while (delay > 0) { + reg = readl(pll->base + p_n->reg_off); + + if (reg & MESON_PLL_LOCK) + return 0; + delay--; + } + return -ETIMEDOUT; +} + +static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct meson_clk_pll *pll = to_meson_clk_pll(hw); + struct parm *p; + const struct pll_rate_table *rate_set; + unsigned long old_rate; + int ret = 0; + u32 reg; + + if (parent_rate == 0 || rate == 0) + return -EINVAL; + + old_rate = rate; + + rate_set = meson_clk_get_pll_settings(pll, rate); + if (!rate_set) + return -EINVAL; + + /* PLL reset */ + p = &pll->conf->n; + reg = readl(pll->base + p->reg_off); + writel(reg | MESON_PLL_RESET, pll->base + p->reg_off); + + reg = PARM_SET(p->width, p->shift, reg, rate_set->n); + writel(reg, pll->base + p->reg_off); + + p = &pll->conf->m; + reg = readl(pll->base + p->reg_off); + reg = PARM_SET(p->width, p->shift, reg, rate_set->m); + writel(reg, pll->base + p->reg_off); + + p = &pll->conf->od; + reg = readl(pll->base + p->reg_off); + reg = PARM_SET(p->width, p->shift, reg, rate_set->od); + writel(reg, pll->base + p->reg_off); + + p = &pll->conf->n; + ret = meson_clk_pll_wait_lock(pll, p); + if (ret) { + pr_warn("%s: pll did not lock, trying to restore old rate %lu\n", + __func__, old_rate); + meson_clk_pll_set_rate(hw, old_rate, parent_rate); + } + + return ret; +} + +static const struct clk_ops meson_clk_pll_ops = { + .recalc_rate = meson_clk_pll_recalc_rate, + .round_rate = meson_clk_pll_round_rate, + .set_rate = meson_clk_pll_set_rate, +}; + +static const struct clk_ops meson_clk_pll_ro_ops = { + .recalc_rate = meson_clk_pll_recalc_rate, +}; + +struct clk *meson_clk_register_pll(const struct clk_conf *clk_conf, + void __iomem *reg_base, + spinlock_t *lock) +{ + struct clk *clk; + struct meson_clk_pll *clk_pll; + struct clk_init_data init; + + clk_pll = kzalloc(sizeof(*clk_pll), GFP_KERNEL); + if (!clk_pll) + return ERR_PTR(-ENOMEM); + + clk_pll->base = reg_base + clk_conf->reg_off; + clk_pll->lock = lock; + clk_pll->conf = clk_conf->conf.pll; + + init.name = clk_conf->clk_name; + init.flags = clk_conf->flags | CLK_GET_RATE_NOCACHE; + + init.parent_names = &clk_conf->clks_parent[0]; + init.num_parents = 1; + init.ops = &meson_clk_pll_ro_ops; + + /* If no rate_table is specified we assume the PLL is read-only */ + if (clk_pll->conf->rate_table) { + int len; + + for (len = 0; clk_pll->conf->rate_table[len].rate != 0; ) + len++; + + clk_pll->rate_count = len; + init.ops = &meson_clk_pll_ops; + } + + clk_pll->hw.init = &init; + + clk = clk_register(NULL, &clk_pll->hw); + if (IS_ERR(clk)) + kfree(clk_pll); + + return clk; +} diff --git a/drivers/clk/meson/clkc.c b/drivers/clk/meson/clkc.c new file mode 100644 index 000000000000..b8c511c5e7a7 --- /dev/null +++ b/drivers/clk/meson/clkc.c @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include + +#include "clkc.h" + +static DEFINE_SPINLOCK(clk_lock); + +static struct clk **clks; +static struct clk_onecell_data clk_data; + +struct clk ** __init meson_clk_init(struct device_node *np, + unsigned long nr_clks) +{ + clks = kcalloc(nr_clks, sizeof(*clks), GFP_KERNEL); + if (!clks) + return ERR_PTR(-ENOMEM); + + clk_data.clks = clks; + clk_data.clk_num = nr_clks; + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + + return clks; +} + +static void meson_clk_add_lookup(struct clk *clk, unsigned int id) +{ + if (clks && id) + clks[id] = clk; +} + +static struct clk * __init +meson_clk_register_composite(const struct clk_conf *clk_conf, + void __iomem *clk_base) +{ + struct clk *clk; + struct clk_mux *mux = NULL; + struct clk_divider *div = NULL; + struct clk_gate *gate = NULL; + const struct clk_ops *mux_ops = NULL; + const struct composite_conf *composite_conf; + + composite_conf = clk_conf->conf.composite; + + if (clk_conf->num_parents > 1) { + mux = kzalloc(sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->reg = clk_base + clk_conf->reg_off + + composite_conf->mux_parm.reg_off; + mux->shift = composite_conf->mux_parm.shift; + mux->mask = BIT(composite_conf->mux_parm.width) - 1; + mux->flags = composite_conf->mux_flags; + mux->lock = &clk_lock; + mux->table = composite_conf->mux_table; + mux_ops = (composite_conf->mux_flags & CLK_MUX_READ_ONLY) ? + &clk_mux_ro_ops : &clk_mux_ops; + } + + if (MESON_PARM_APPLICABLE(&composite_conf->div_parm)) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) { + clk = ERR_PTR(-ENOMEM); + goto error; + } + + div->reg = clk_base + clk_conf->reg_off + + composite_conf->div_parm.reg_off; + div->shift = composite_conf->div_parm.shift; + div->width = composite_conf->div_parm.width; + div->lock = &clk_lock; + div->flags = composite_conf->div_flags; + div->table = composite_conf->div_table; + } + + if (MESON_PARM_APPLICABLE(&composite_conf->gate_parm)) { + gate = kzalloc(sizeof(*gate), GFP_KERNEL); + if (!gate) { + clk = ERR_PTR(-ENOMEM); + goto error; + } + + gate->reg = clk_base + clk_conf->reg_off + + composite_conf->div_parm.reg_off; + gate->bit_idx = composite_conf->gate_parm.shift; + gate->flags = composite_conf->gate_flags; + gate->lock = &clk_lock; + } + + clk = clk_register_composite(NULL, clk_conf->clk_name, + clk_conf->clks_parent, + clk_conf->num_parents, + mux ? &mux->hw : NULL, mux_ops, + div ? &div->hw : NULL, &clk_divider_ops, + gate ? &gate->hw : NULL, &clk_gate_ops, + clk_conf->flags); + if (IS_ERR(clk)) + goto error; + + return clk; + +error: + kfree(gate); + kfree(div); + kfree(mux); + + return clk; +} + +static struct clk * __init +meson_clk_register_fixed_factor(const struct clk_conf *clk_conf, + void __iomem *clk_base) +{ + struct clk *clk; + const struct fixed_fact_conf *fixed_fact_conf; + const struct parm *p; + unsigned int mult, div; + u32 reg; + + fixed_fact_conf = &clk_conf->conf.fixed_fact; + + mult = clk_conf->conf.fixed_fact.mult; + div = clk_conf->conf.fixed_fact.div; + + if (!mult) { + mult = 1; + p = &fixed_fact_conf->mult_parm; + if (MESON_PARM_APPLICABLE(p)) { + reg = readl(clk_base + clk_conf->reg_off + p->reg_off); + mult = PARM_GET(p->width, p->shift, reg); + } + } + + if (!div) { + div = 1; + p = &fixed_fact_conf->div_parm; + if (MESON_PARM_APPLICABLE(p)) { + reg = readl(clk_base + clk_conf->reg_off + p->reg_off); + mult = PARM_GET(p->width, p->shift, reg); + } + } + + clk = clk_register_fixed_factor(NULL, + clk_conf->clk_name, + clk_conf->clks_parent[0], + clk_conf->flags, + mult, div); + + return clk; +} + +static struct clk * __init +meson_clk_register_fixed_rate(const struct clk_conf *clk_conf, + void __iomem *clk_base) +{ + struct clk *clk; + const struct fixed_rate_conf *fixed_rate_conf; + const struct parm *r; + unsigned long rate; + u32 reg; + + fixed_rate_conf = &clk_conf->conf.fixed_rate; + rate = fixed_rate_conf->rate; + + if (!rate) { + r = &fixed_rate_conf->rate_parm; + reg = readl(clk_base + clk_conf->reg_off + r->reg_off); + rate = PARM_GET(r->width, r->shift, reg); + } + + rate *= 1000000; + + clk = clk_register_fixed_rate(NULL, + clk_conf->clk_name, + clk_conf->num_parents + ? clk_conf->clks_parent[0] : NULL, + clk_conf->flags, rate); + + return clk; +} + +void __init meson_clk_register_clks(const struct clk_conf *clk_confs, + size_t nr_confs, + void __iomem *clk_base) +{ + unsigned int i; + struct clk *clk = NULL; + + for (i = 0; i < nr_confs; i++) { + const struct clk_conf *clk_conf = &clk_confs[i]; + + switch (clk_conf->clk_type) { + case CLK_FIXED_RATE: + clk = meson_clk_register_fixed_rate(clk_conf, + clk_base); + break; + case CLK_FIXED_FACTOR: + clk = meson_clk_register_fixed_factor(clk_conf, + clk_base); + break; + case CLK_COMPOSITE: + clk = meson_clk_register_composite(clk_conf, + clk_base); + break; + case CLK_CPU: + clk = meson_clk_register_cpu(clk_conf, clk_base, + &clk_lock); + break; + case CLK_PLL: + clk = meson_clk_register_pll(clk_conf, clk_base, + &clk_lock); + break; + default: + clk = NULL; + } + + if (!clk) { + pr_err("%s: unknown clock type %d\n", __func__, + clk_conf->clk_type); + continue; + } + + if (IS_ERR(clk)) { + pr_warn("%s: Unable to create %s clock\n", __func__, + clk_conf->clk_name); + continue; + } + + meson_clk_add_lookup(clk, clk_conf->clk_id); + } +} diff --git a/drivers/clk/meson/clkc.h b/drivers/clk/meson/clkc.h new file mode 100644 index 000000000000..609ae92cc13f --- /dev/null +++ b/drivers/clk/meson/clkc.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#ifndef __CLKC_H +#define __CLKC_H + +#define PMASK(width) GENMASK(width - 1, 0) +#define SETPMASK(width, shift) GENMASK(shift + width - 1, shift) +#define CLRPMASK(width, shift) (~SETPMASK(width, shift)) + +#define PARM_GET(width, shift, reg) \ + (((reg) & SETPMASK(width, shift)) >> (shift)) +#define PARM_SET(width, shift, reg, val) \ + (((reg) & CLRPMASK(width, shift)) | (val << (shift))) + +#define MESON_PARM_APPLICABLE(p) (!!((p)->width)) + +struct parm { + u16 reg_off; + u8 shift; + u8 width; +}; +#define PARM(_r, _s, _w) \ + { \ + .reg_off = (_r), \ + .shift = (_s), \ + .width = (_w), \ + } \ + +struct pll_rate_table { + unsigned long rate; + u16 m; + u16 n; + u16 od; +}; +#define PLL_RATE(_r, _m, _n, _od) \ + { \ + .rate = (_r), \ + .m = (_m), \ + .n = (_n), \ + .od = (_od), \ + } \ + +struct pll_conf { + const struct pll_rate_table *rate_table; + struct parm m; + struct parm n; + struct parm od; +}; + +struct fixed_fact_conf { + unsigned int div; + unsigned int mult; + struct parm div_parm; + struct parm mult_parm; +}; + +struct fixed_rate_conf { + unsigned long rate; + struct parm rate_parm; +}; + +struct composite_conf { + struct parm mux_parm; + struct parm div_parm; + struct parm gate_parm; + struct clk_div_table *div_table; + u32 *mux_table; + u8 mux_flags; + u8 div_flags; + u8 gate_flags; +}; + +#define PNAME(x) static const char *x[] + +enum clk_type { + CLK_FIXED_FACTOR, + CLK_FIXED_RATE, + CLK_COMPOSITE, + CLK_CPU, + CLK_PLL, +}; + +struct clk_conf { + u16 reg_off; + enum clk_type clk_type; + unsigned int clk_id; + const char *clk_name; + const char **clks_parent; + int num_parents; + unsigned long flags; + union { + struct fixed_fact_conf fixed_fact; + struct fixed_rate_conf fixed_rate; + const struct composite_conf *composite; + struct pll_conf *pll; + const struct clk_div_table *div_table; + } conf; +}; + +#define FIXED_RATE_P(_ro, _ci, _cn, _f, _c) \ + { \ + .reg_off = (_ro), \ + .clk_type = CLK_FIXED_RATE, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .flags = (_f), \ + .conf.fixed_rate.rate_parm = _c, \ + } \ + +#define FIXED_RATE(_ci, _cn, _f, _r) \ + { \ + .clk_type = CLK_FIXED_RATE, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .flags = (_f), \ + .conf.fixed_rate.rate = (_r), \ + } \ + +#define PLL(_ro, _ci, _cn, _cp, _f, _c) \ + { \ + .reg_off = (_ro), \ + .clk_type = CLK_PLL, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .clks_parent = (_cp), \ + .num_parents = ARRAY_SIZE(_cp), \ + .flags = (_f), \ + .conf.pll = (_c), \ + } \ + +#define FIXED_FACTOR_DIV(_ci, _cn, _cp, _f, _d) \ + { \ + .clk_type = CLK_FIXED_FACTOR, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .clks_parent = (_cp), \ + .num_parents = ARRAY_SIZE(_cp), \ + .conf.fixed_fact.div = (_d), \ + } \ + +#define CPU(_ro, _ci, _cn, _cp, _dt) \ + { \ + .reg_off = (_ro), \ + .clk_type = CLK_CPU, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .clks_parent = (_cp), \ + .num_parents = ARRAY_SIZE(_cp), \ + .conf.div_table = (_dt), \ + } \ + +#define COMPOSITE(_ro, _ci, _cn, _cp, _f, _c) \ + { \ + .reg_off = (_ro), \ + .clk_type = CLK_COMPOSITE, \ + .clk_id = (_ci), \ + .clk_name = (_cn), \ + .clks_parent = (_cp), \ + .num_parents = ARRAY_SIZE(_cp), \ + .flags = (_f), \ + .conf.composite = (_c), \ + } \ + +struct clk **meson_clk_init(struct device_node *np, unsigned long nr_clks); +void meson_clk_register_clks(const struct clk_conf *clk_confs, + unsigned int nr_confs, void __iomem *clk_base); +struct clk *meson_clk_register_cpu(const struct clk_conf *clk_conf, + void __iomem *reg_base, spinlock_t *lock); +struct clk *meson_clk_register_pll(const struct clk_conf *clk_conf, + void __iomem *reg_base, spinlock_t *lock); + +#endif /* __CLKC_H */ diff --git a/include/dt-bindings/clock/meson8b-clkc.h b/include/dt-bindings/clock/meson8b-clkc.h new file mode 100644 index 000000000000..bd2720d58e0c --- /dev/null +++ b/include/dt-bindings/clock/meson8b-clkc.h @@ -0,0 +1,25 @@ +/* + * Meson8b clock tree IDs + */ + +#ifndef __MESON8B_CLKC_H +#define __MESON8B_CLKC_H + +#define CLKID_UNUSED 0 +#define CLKID_XTAL 1 +#define CLKID_PLL_FIXED 2 +#define CLKID_PLL_VID 3 +#define CLKID_PLL_SYS 4 +#define CLKID_FCLK_DIV2 5 +#define CLKID_FCLK_DIV3 6 +#define CLKID_FCLK_DIV4 7 +#define CLKID_FCLK_DIV5 8 +#define CLKID_FCLK_DIV7 9 +#define CLKID_CLK81 10 +#define CLKID_MALI 11 +#define CLKID_CPUCLK 12 +#define CLKID_ZERO 13 + +#define CLK_NR_CLKS (CLKID_ZERO + 1) + +#endif /* __MESON8B_CLKC_H */ -- cgit v1.2.3-58-ga151 From 12545fa33ad3363c639ec56bb84c8a58cfbcf65b Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Mon, 1 Jun 2015 13:13:54 +0200 Subject: clk: meson: Document bindings for Meson8b clock controller Add documentation for the clock controller. Signed-off-by: Carlo Caione Signed-off-by: Stephen Boyd --- .../bindings/clock/amlogic,meson8b-clkc.txt | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt diff --git a/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt b/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt new file mode 100644 index 000000000000..2b7b3fa588d7 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/amlogic,meson8b-clkc.txt @@ -0,0 +1,40 @@ +* Amlogic Meson8b Clock and Reset Unit + +The Amlogic Meson8b clock controller generates and supplies clock to various +controllers within the SoC. + +Required Properties: + +- compatible: should be "amlogic,meson8b-clkc" +- reg: it must be composed by two tuples: + 0) physical base address of the xtal register and length of memory + mapped region. + 1) physical base address of the clock controller and length of memory + mapped region. + +- #clock-cells: should be 1. + +Each clock is assigned an identifier and client nodes can use this identifier +to specify the clock which they consume. All available clocks are defined as +preprocessor macros in the dt-bindings/clock/meson8b-clkc.h header and can be +used in device tree sources. + +Example: Clock controller node: + + clkc: clock-controller@c1104000 { + #clock-cells = <1>; + compatible = "amlogic,meson8b-clkc"; + reg = <0xc1108000 0x4>, <0xc1104000 0x460>; + }; + + +Example: UART controller node that consumes the clock generated by the clock + controller: + + uart_AO: serial@c81004c0 { + compatible = "amlogic,meson-uart"; + reg = <0xc81004c0 0x14>; + interrupts = <0 90 1>; + clocks = <&clkc CLKID_CLK81>; + status = "disabled"; + }; -- cgit v1.2.3-58-ga151 From 28b9fcd016126e6495d486c169fddaf26091a04b Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Mon, 1 Jun 2015 13:13:55 +0200 Subject: clk: meson8b: Add support for Meson8b clocks This patch adds support for the basic clocks found on the Amlogic Meson8b SoCs. Signed-off-by: Carlo Caione Signed-off-by: Stephen Boyd --- drivers/clk/meson/Makefile | 1 + drivers/clk/meson/meson8b-clkc.c | 196 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 drivers/clk/meson/meson8b-clkc.c diff --git a/drivers/clk/meson/Makefile b/drivers/clk/meson/Makefile index 66c6d8d23553..6d45531df9ab 100644 --- a/drivers/clk/meson/Makefile +++ b/drivers/clk/meson/Makefile @@ -3,3 +3,4 @@ # obj-y += clkc.o clk-pll.o clk-cpu.o +obj-y += meson8b-clkc.o diff --git a/drivers/clk/meson/meson8b-clkc.c b/drivers/clk/meson/meson8b-clkc.c new file mode 100644 index 000000000000..61f6d55c4ac7 --- /dev/null +++ b/drivers/clk/meson/meson8b-clkc.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2015 Endless Mobile, Inc. + * Author: Carlo Caione + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include "clkc.h" + +#define MESON8B_REG_CTL0_ADDR 0x0000 +#define MESON8B_REG_SYS_CPU_CNTL1 0x015c +#define MESON8B_REG_HHI_MPEG 0x0174 +#define MESON8B_REG_MALI 0x01b0 +#define MESON8B_REG_PLL_FIXED 0x0280 +#define MESON8B_REG_PLL_SYS 0x0300 +#define MESON8B_REG_PLL_VID 0x0320 + +static const struct pll_rate_table sys_pll_rate_table[] = { + PLL_RATE(312000000, 52, 1, 2), + PLL_RATE(336000000, 56, 1, 2), + PLL_RATE(360000000, 60, 1, 2), + PLL_RATE(384000000, 64, 1, 2), + PLL_RATE(408000000, 68, 1, 2), + PLL_RATE(432000000, 72, 1, 2), + PLL_RATE(456000000, 76, 1, 2), + PLL_RATE(480000000, 80, 1, 2), + PLL_RATE(504000000, 84, 1, 2), + PLL_RATE(528000000, 88, 1, 2), + PLL_RATE(552000000, 92, 1, 2), + PLL_RATE(576000000, 96, 1, 2), + PLL_RATE(600000000, 50, 1, 1), + PLL_RATE(624000000, 52, 1, 1), + PLL_RATE(648000000, 54, 1, 1), + PLL_RATE(672000000, 56, 1, 1), + PLL_RATE(696000000, 58, 1, 1), + PLL_RATE(720000000, 60, 1, 1), + PLL_RATE(744000000, 62, 1, 1), + PLL_RATE(768000000, 64, 1, 1), + PLL_RATE(792000000, 66, 1, 1), + PLL_RATE(816000000, 68, 1, 1), + PLL_RATE(840000000, 70, 1, 1), + PLL_RATE(864000000, 72, 1, 1), + PLL_RATE(888000000, 74, 1, 1), + PLL_RATE(912000000, 76, 1, 1), + PLL_RATE(936000000, 78, 1, 1), + PLL_RATE(960000000, 80, 1, 1), + PLL_RATE(984000000, 82, 1, 1), + PLL_RATE(1008000000, 84, 1, 1), + PLL_RATE(1032000000, 86, 1, 1), + PLL_RATE(1056000000, 88, 1, 1), + PLL_RATE(1080000000, 90, 1, 1), + PLL_RATE(1104000000, 92, 1, 1), + PLL_RATE(1128000000, 94, 1, 1), + PLL_RATE(1152000000, 96, 1, 1), + PLL_RATE(1176000000, 98, 1, 1), + PLL_RATE(1200000000, 50, 1, 0), + PLL_RATE(1224000000, 51, 1, 0), + PLL_RATE(1248000000, 52, 1, 0), + PLL_RATE(1272000000, 53, 1, 0), + PLL_RATE(1296000000, 54, 1, 0), + PLL_RATE(1320000000, 55, 1, 0), + PLL_RATE(1344000000, 56, 1, 0), + PLL_RATE(1368000000, 57, 1, 0), + PLL_RATE(1392000000, 58, 1, 0), + PLL_RATE(1416000000, 59, 1, 0), + PLL_RATE(1440000000, 60, 1, 0), + PLL_RATE(1464000000, 61, 1, 0), + PLL_RATE(1488000000, 62, 1, 0), + PLL_RATE(1512000000, 63, 1, 0), + PLL_RATE(1536000000, 64, 1, 0), + { /* sentinel */ }, +}; + +static const struct clk_div_table cpu_div_table[] = { + { .val = 1, .div = 1 }, + { .val = 2, .div = 2 }, + { .val = 3, .div = 3 }, + { .val = 2, .div = 4 }, + { .val = 3, .div = 6 }, + { .val = 4, .div = 8 }, + { .val = 5, .div = 10 }, + { .val = 6, .div = 12 }, + { .val = 7, .div = 14 }, + { .val = 8, .div = 16 }, + { /* sentinel */ }, +}; + +PNAME(p_xtal) = { "xtal" }; +PNAME(p_fclk_div) = { "fixed_pll" }; +PNAME(p_cpu_clk) = { "sys_pll" }; +PNAME(p_clk81) = { "fclk_div3", "fclk_div4", "fclk_div5" }; +PNAME(p_mali) = { "fclk_div3", "fclk_div4", "fclk_div5", + "fclk_div7", "zero" }; + +static u32 mux_table_clk81[] = { 6, 5, 7 }; +static u32 mux_table_mali[] = { 6, 5, 7, 4, 0 }; + +static struct pll_conf pll_confs = { + .m = PARM(0x00, 0, 9), + .n = PARM(0x00, 9, 5), + .od = PARM(0x00, 16, 2), +}; + +static struct pll_conf sys_pll_conf = { + .m = PARM(0x00, 0, 9), + .n = PARM(0x00, 9, 5), + .od = PARM(0x00, 16, 2), + .rate_table = sys_pll_rate_table, +}; + +static const struct composite_conf clk81_conf __initconst = { + .mux_table = mux_table_clk81, + .mux_flags = CLK_MUX_READ_ONLY, + .mux_parm = PARM(0x00, 12, 3), + .div_parm = PARM(0x00, 0, 7), + .gate_parm = PARM(0x00, 7, 1), +}; + +static const struct composite_conf mali_conf __initconst = { + .mux_table = mux_table_mali, + .mux_parm = PARM(0x00, 9, 3), + .div_parm = PARM(0x00, 0, 7), + .gate_parm = PARM(0x00, 8, 1), +}; + +static const struct clk_conf meson8b_xtal_conf __initconst = + FIXED_RATE_P(MESON8B_REG_CTL0_ADDR, CLKID_XTAL, "xtal", + CLK_IS_ROOT, PARM(0x00, 4, 7)); + +static const struct clk_conf meson8b_clk_confs[] __initconst = { + FIXED_RATE(CLKID_ZERO, "zero", CLK_IS_ROOT, 0), + PLL(MESON8B_REG_PLL_FIXED, CLKID_PLL_FIXED, "fixed_pll", + p_xtal, 0, &pll_confs), + PLL(MESON8B_REG_PLL_VID, CLKID_PLL_VID, "vid_pll", + p_xtal, 0, &pll_confs), + PLL(MESON8B_REG_PLL_SYS, CLKID_PLL_SYS, "sys_pll", + p_xtal, 0, &sys_pll_conf), + FIXED_FACTOR_DIV(CLKID_FCLK_DIV2, "fclk_div2", p_fclk_div, 0, 2), + FIXED_FACTOR_DIV(CLKID_FCLK_DIV3, "fclk_div3", p_fclk_div, 0, 3), + FIXED_FACTOR_DIV(CLKID_FCLK_DIV4, "fclk_div4", p_fclk_div, 0, 4), + FIXED_FACTOR_DIV(CLKID_FCLK_DIV5, "fclk_div5", p_fclk_div, 0, 5), + FIXED_FACTOR_DIV(CLKID_FCLK_DIV7, "fclk_div7", p_fclk_div, 0, 7), + CPU(MESON8B_REG_SYS_CPU_CNTL1, CLKID_CPUCLK, "a5_clk", p_cpu_clk, + cpu_div_table), + COMPOSITE(MESON8B_REG_HHI_MPEG, CLKID_CLK81, "clk81", p_clk81, + CLK_SET_RATE_NO_REPARENT | CLK_IGNORE_UNUSED, &clk81_conf), + COMPOSITE(MESON8B_REG_MALI, CLKID_MALI, "mali", p_mali, + CLK_IGNORE_UNUSED, &mali_conf), +}; + +static void __init meson8b_clkc_init(struct device_node *np) +{ + void __iomem *clk_base; + + if (!meson_clk_init(np, CLK_NR_CLKS)) + return; + + /* XTAL */ + clk_base = of_iomap(np, 0); + if (!clk_base) { + pr_err("%s: Unable to map xtal base\n", __func__); + return; + } + + meson_clk_register_clks(&meson8b_xtal_conf, 1, clk_base); + iounmap(clk_base); + + /* Generic clocks and PLLs */ + clk_base = of_iomap(np, 1); + if (!clk_base) { + pr_err("%s: Unable to map clk base\n", __func__); + return; + } + + meson_clk_register_clks(meson8b_clk_confs, + ARRAY_SIZE(meson8b_clk_confs), + clk_base); +} +CLK_OF_DECLARE(meson8b_clock, "amlogic,meson8b-clkc", meson8b_clkc_init); -- cgit v1.2.3-58-ga151 From 2e61dfb3602b904966491f260f62c01b9895936a Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Fri, 5 Jun 2015 11:26:13 -0500 Subject: clk: of: helper for filling parent clock array and return num of parents Sprinkled all through the platform clock drivers are code like this to fill the clock parent array: for (i = 0; i < num_parents; ++i) parent_names[i] = of_clk_get_parent_name(np, i); The of_clk_parent_fill() will do the same as the code above, and while at it, return the number of parents as well since the logic of the function is to the walk the clock node to look for the parent. Signed-off-by: Dinh Nguyen [sboyd@codeaurora.org: Fixed kernel-doc] Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 21 +++++++++++++++++++++ include/linux/clk-provider.h | 2 ++ 2 files changed, 23 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index a522dbbc01b9..ee222e28bd97 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -3016,6 +3016,27 @@ const char *of_clk_get_parent_name(struct device_node *np, int index) } EXPORT_SYMBOL_GPL(of_clk_get_parent_name); +/** + * of_clk_parent_fill() - Fill @parents with names of @np's parents and return + * number of parents + * @np: Device node pointer associated with clock provider + * @parents: pointer to char array that hold the parents' names + * @size: size of the @parents array + * + * Return: number of parents for the clock node. + */ +int of_clk_parent_fill(struct device_node *np, const char **parents, + unsigned int size) +{ + unsigned int i = 0; + + while (i < size && (parents[i] = of_clk_get_parent_name(np, i)) != NULL) + i++; + + return i; +} +EXPORT_SYMBOL_GPL(of_clk_parent_fill); + struct clock_provider { of_clk_init_cb_t clk_init_cb; struct device_node *np; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 5378c2aba4d2..2e5df069ca34 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -626,6 +626,8 @@ struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, void *data); struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data); int of_clk_get_parent_count(struct device_node *np); +int of_clk_parent_fill(struct device_node *np, const char **parents, + unsigned int size); const char *of_clk_get_parent_name(struct device_node *np, int index); void of_clk_init(const struct of_device_id *matches); -- cgit v1.2.3-58-ga151 From 761d3e328c503c8428cec54e33196e49f5c46e55 Mon Sep 17 00:00:00 2001 From: Dinh Nguyen Date: Fri, 5 Jun 2015 11:26:14 -0500 Subject: clk: socfpga: make use of of_clk_parent_fill helper function Use of_clk_parent_fill to fill in the parent clock's array. Signed-off-by: Dinh Nguyen Signed-off-by: Stephen Boyd --- drivers/clk/socfpga/clk-gate.c | 6 +----- drivers/clk/socfpga/clk-pll.c | 7 +------ 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/drivers/clk/socfpga/clk-gate.c b/drivers/clk/socfpga/clk-gate.c index 0add360525a0..82449cd76fd7 100644 --- a/drivers/clk/socfpga/clk-gate.c +++ b/drivers/clk/socfpga/clk-gate.c @@ -190,7 +190,6 @@ static void __init __socfpga_gate_init(struct device_node *node, const char *parent_name[SOCFPGA_MAX_PARENTS]; struct clk_init_data init; int rc; - int i = 0; socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); if (WARN_ON(!socfpga_clk)) @@ -234,12 +233,9 @@ static void __init __socfpga_gate_init(struct device_node *node, init.name = clk_name; init.ops = ops; init.flags = 0; - while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = - of_clk_get_parent_name(node, i)) != NULL) - i++; + init.num_parents = of_clk_parent_fill(node, parent_name, SOCFPGA_MAX_PARENTS); init.parent_names = parent_name; - init.num_parents = i; socfpga_clk->hw.hw.init = &init; clk = clk_register(NULL, &socfpga_clk->hw.hw); diff --git a/drivers/clk/socfpga/clk-pll.c b/drivers/clk/socfpga/clk-pll.c index de6da957a09d..8f26b5234947 100644 --- a/drivers/clk/socfpga/clk-pll.c +++ b/drivers/clk/socfpga/clk-pll.c @@ -92,7 +92,6 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, struct clk_init_data init; struct device_node *clkmgr_np; int rc; - int i = 0; of_property_read_u32(node, "reg", ®); @@ -111,11 +110,7 @@ static __init struct clk *__socfpga_pll_init(struct device_node *node, init.ops = ops; init.flags = 0; - while (i < SOCFPGA_MAX_PARENTS && (parent_name[i] = - of_clk_get_parent_name(node, i)) != NULL) - i++; - - init.num_parents = i; + init.num_parents = of_clk_parent_fill(node, parent_name, SOCFPGA_MAX_PARENTS); init.parent_names = parent_name; pll_clk->hw.hw.init = &init; -- cgit v1.2.3-58-ga151 From f7c172b5c11ff91ce103aa94a284fcf635554df0 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Mon, 27 Apr 2015 20:36:33 +0900 Subject: clk: exynos5433: Add CLK_SET_RATE_PARENT to support DVFS for big.LITTLE core This patch adds CLK_SET_RATE_PARENT flag to support DVFS of Cortex-{A53|A57} core (big.LITTLE core) because 'sclk_{apollo|atlas}' leaf clock is used to change the CPU frequency of Cortex-{A53|A57} core in arm_big_little.c driver. - 'apollo' word means the LITTLE core (Cortex-A53 core) in Exynos5433 TRM. - 'atlas' word means the big core (Cortex-A57 core) in Exynos5433 TRM. Signed-off-by: Chanwoo Choi Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 9e04ae2bb4d7..e4a67717d9a9 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -3582,7 +3582,7 @@ static struct samsung_pll_clock apollo_pll_clks[] __initdata = { static struct samsung_mux_clock apollo_mux_clks[] __initdata = { /* MUX_SEL_APOLLO0 */ MUX_F(CLK_MOUT_APOLLO_PLL, "mout_apollo_pll", mout_apollo_pll_p, - MUX_SEL_APOLLO0, 0, 1, 0, CLK_MUX_READ_ONLY), + MUX_SEL_APOLLO0, 0, 1, CLK_SET_RATE_PARENT, 0), /* MUX_SEL_APOLLO1 */ MUX(CLK_MOUT_BUS_PLL_APOLLO_USER, "mout_bus_pll_apollo_user", @@ -3590,7 +3590,7 @@ static struct samsung_mux_clock apollo_mux_clks[] __initdata = { /* MUX_SEL_APOLLO2 */ MUX_F(CLK_MOUT_APOLLO, "mout_apollo", mout_apollo_p, MUX_SEL_APOLLO2, - 0, 1, 0, CLK_MUX_READ_ONLY), + 0, 1, CLK_SET_RATE_PARENT, 0), }; static struct samsung_div_clock apollo_div_clks[] __initdata = { @@ -3611,11 +3611,9 @@ static struct samsung_div_clock apollo_div_clks[] __initdata = { DIV_APOLLO0, 8, 3, CLK_GET_RATE_NOCACHE, CLK_DIVIDER_READ_ONLY), DIV_F(CLK_DIV_APOLLO2, "div_apollo2", "div_apollo1", - DIV_APOLLO0, 4, 3, CLK_GET_RATE_NOCACHE, - CLK_DIVIDER_READ_ONLY), + DIV_APOLLO0, 4, 3, CLK_SET_RATE_PARENT, 0), DIV_F(CLK_DIV_APOLLO1, "div_apollo1", "mout_apollo", - DIV_APOLLO0, 0, 3, CLK_GET_RATE_NOCACHE, - CLK_DIVIDER_READ_ONLY), + DIV_APOLLO0, 0, 3, CLK_SET_RATE_PARENT, 0), /* DIV_APOLLO1 */ DIV_F(CLK_DIV_SCLK_HPM_APOLLO, "div_sclk_hpm_apollo", "mout_apollo", @@ -3666,7 +3664,8 @@ static struct samsung_gate_clock apollo_gate_clks[] __initdata = { GATE(CLK_SCLK_HPM_APOLLO, "sclk_hpm_apollo", "div_sclk_hpm_apollo", ENABLE_SCLK_APOLLO, 1, CLK_IGNORE_UNUSED, 0), GATE(CLK_SCLK_APOLLO, "sclk_apollo", "div_apollo2", - ENABLE_SCLK_APOLLO, 0, CLK_IGNORE_UNUSED, 0), + ENABLE_SCLK_APOLLO, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), }; static struct samsung_cmu_info apollo_cmu_info __initdata = { @@ -3775,7 +3774,7 @@ static struct samsung_pll_clock atlas_pll_clks[] __initdata = { static struct samsung_mux_clock atlas_mux_clks[] __initdata = { /* MUX_SEL_ATLAS0 */ MUX_F(CLK_MOUT_ATLAS_PLL, "mout_atlas_pll", mout_atlas_pll_p, - MUX_SEL_ATLAS0, 0, 1, 0, CLK_MUX_READ_ONLY), + MUX_SEL_ATLAS0, 0, 1, CLK_SET_RATE_PARENT, 0), /* MUX_SEL_ATLAS1 */ MUX(CLK_MOUT_BUS_PLL_ATLAS_USER, "mout_bus_pll_atlas_user", @@ -3783,7 +3782,7 @@ static struct samsung_mux_clock atlas_mux_clks[] __initdata = { /* MUX_SEL_ATLAS2 */ MUX_F(CLK_MOUT_ATLAS, "mout_atlas", mout_atlas_p, MUX_SEL_ATLAS2, - 0, 1, 0, CLK_MUX_READ_ONLY), + 0, 1, CLK_SET_RATE_PARENT, 0), }; static struct samsung_div_clock atlas_div_clks[] __initdata = { @@ -3804,11 +3803,9 @@ static struct samsung_div_clock atlas_div_clks[] __initdata = { DIV_ATLAS0, 8, 3, CLK_GET_RATE_NOCACHE, CLK_DIVIDER_READ_ONLY), DIV_F(CLK_DIV_ATLAS2, "div_atlas2", "div_atlas1", - DIV_ATLAS0, 4, 3, CLK_GET_RATE_NOCACHE, - CLK_DIVIDER_READ_ONLY), + DIV_ATLAS0, 4, 3, CLK_SET_RATE_PARENT, 0), DIV_F(CLK_DIV_ATLAS1, "div_atlas1", "mout_atlas", - DIV_ATLAS0, 0, 3, CLK_GET_RATE_NOCACHE, - CLK_DIVIDER_READ_ONLY), + DIV_ATLAS0, 0, 3, CLK_SET_RATE_PARENT, 0), /* DIV_ATLAS1 */ DIV_F(CLK_DIV_SCLK_HPM_ATLAS, "div_sclk_hpm_atlas", "mout_atlas", @@ -3885,7 +3882,8 @@ static struct samsung_gate_clock atlas_gate_clks[] __initdata = { GATE(CLK_ATCLK, "atclk", "div_atclk_atlas", ENABLE_SCLK_ATLAS, 1, CLK_IGNORE_UNUSED, 0), GATE(CLK_SCLK_ATLAS, "sclk_atlas", "div_atlas2", - ENABLE_SCLK_ATLAS, 0, CLK_IGNORE_UNUSED, 0), + ENABLE_SCLK_ATLAS, 0, + CLK_IGNORE_UNUSED | CLK_SET_RATE_PARENT, 0), }; static struct samsung_cmu_info atlas_cmu_info __initdata = { -- cgit v1.2.3-58-ga151 From 2a9c67b2d3eb81b26493b5d2ee9d27eb4a806c53 Mon Sep 17 00:00:00 2001 From: Hyungwon Hwang Date: Mon, 27 Apr 2015 20:36:34 +0900 Subject: clk: exynos5433: Add DIV_CPIF to the list of stored registers on suspend This DIV_CPIF register must be stored when the system is suspended, and must be restored on system resume. This patch adds the register to the list of restored registers. Signed-off-by: Hyungwon Hwang Signed-off-by: Chanwoo Choi Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index e4a67717d9a9..5d8b4a82c98b 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -835,6 +835,7 @@ static unsigned long cpif_clk_regs[] __initdata = { MPHY_PLL_CON1, MPHY_PLL_FREQ_DET, MUX_SEL_CPIF0, + DIV_CPIF, ENABLE_SCLK_CPIF, }; -- cgit v1.2.3-58-ga151 From eceb7aaec07d17a0facb4236c27f7d331c3cdf46 Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Mon, 27 Apr 2015 20:36:35 +0900 Subject: clk: exynos5433: Add clock flag to support the DVFS of GPU This patch adds the CLK_SET_RATE_PARENT flag for 'aclk_g3d' clock and parent clocks becuase 'aclk_g3d' is used to change GPU frequency for DVFS (Dynamic Voltage Frequency Scaling) feature and adds CLK_IGNORE_UNUSED flags to 'aclk_asyncapbs_g3d'/'aclk_asyncapbm_g3d' clocks to access the SFR of the MALI device. Signed-off-by: Joonyoung Shim [cw00.choi: Add patch description and add CLK_SET_RATE_PARENT to 'aclk_g3d' clk] Signed-off-by: Chanwoo Choi Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 5d8b4a82c98b..256fec0f2d18 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -3287,10 +3287,10 @@ static struct samsung_pll_clock g3d_pll_clks[] __initdata = { static struct samsung_mux_clock g3d_mux_clks[] __initdata = { /* MUX_SEL_G3D */ - MUX(CLK_MOUT_ACLK_G3D_400, "mout_aclk_g3d_400", mout_aclk_g3d_400_p, - MUX_SEL_G3D, 8, 1), - MUX(CLK_MOUT_G3D_PLL, "mout_g3d_pll", mout_g3d_pll_p, - MUX_SEL_G3D, 0, 1), + MUX_F(CLK_MOUT_ACLK_G3D_400, "mout_aclk_g3d_400", mout_aclk_g3d_400_p, + MUX_SEL_G3D, 8, 1, CLK_SET_RATE_PARENT, 0), + MUX_F(CLK_MOUT_G3D_PLL, "mout_g3d_pll", mout_g3d_pll_p, + MUX_SEL_G3D, 0, 1, CLK_SET_RATE_PARENT, 0), }; static struct samsung_div_clock g3d_div_clks[] __initdata = { @@ -3299,8 +3299,8 @@ static struct samsung_div_clock g3d_div_clks[] __initdata = { 8, 2), DIV(CLK_DIV_PCLK_G3D, "div_pclk_g3d", "div_aclk_g3d", DIV_G3D, 4, 3), - DIV(CLK_DIV_ACLK_G3D, "div_aclk_g3d", "mout_aclk_g3d_400", DIV_G3D, - 0, 3), + DIV_F(CLK_DIV_ACLK_G3D, "div_aclk_g3d", "mout_aclk_g3d_400", DIV_G3D, + 0, 3, CLK_SET_RATE_PARENT, 0), }; static struct samsung_gate_clock g3d_gate_clks[] __initdata = { @@ -3310,9 +3310,9 @@ static struct samsung_gate_clock g3d_gate_clks[] __initdata = { GATE(CLK_ACLK_BTS_G3D0, "aclk_bts_g3d0", "div_aclk_g3d", ENABLE_ACLK_G3D, 6, 0, 0), GATE(CLK_ACLK_ASYNCAPBS_G3D, "aclk_asyncapbs_g3d", "div_pclk_g3d", - ENABLE_ACLK_G3D, 5, 0, 0), + ENABLE_ACLK_G3D, 5, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_ASYNCAPBM_G3D, "aclk_asyncapbm_g3d", "div_aclk_g3d", - ENABLE_ACLK_G3D, 4, 0, 0), + ENABLE_ACLK_G3D, 4, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_AHB2APB_G3DP, "aclk_ahb2apb_g3dp", "div_pclk_g3d", ENABLE_ACLK_G3D, 3, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_G3DNP_150, "aclk_g3dnp_150", "div_pclk_g3d", @@ -3320,7 +3320,7 @@ static struct samsung_gate_clock g3d_gate_clks[] __initdata = { GATE(CLK_ACLK_G3DND_600, "aclk_g3dnd_600", "div_aclk_g3d", ENABLE_ACLK_G3D, 1, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_G3D, "aclk_g3d", "div_aclk_g3d", - ENABLE_ACLK_G3D, 0, 0, 0), + ENABLE_ACLK_G3D, 0, CLK_SET_RATE_PARENT, 0), /* ENABLE_PCLK_G3D */ GATE(CLK_PCLK_BTS_G3D1, "pclk_bts_g3d1", "div_pclk_g3d", -- cgit v1.2.3-58-ga151 From 80e7264c8f1b9b2ca73bc8db327fb3b73e626b7b Mon Sep 17 00:00:00 2001 From: Jonghwa Lee Date: Mon, 27 Apr 2015 20:36:36 +0900 Subject: clk: exynos5433: Add CLK_IGNORE_UNUSED flag to clocks for SMC This patch adds 'CLK_IGNORE_UNUSED' flag to clocks which is required for operation of secure monitor call (smc). System will hang when it executes 'smc' with one of those clock is gated. All related clocks must be enabled. Signed-off-by: Jonghwa Lee Signed-off-by: Chanwoo Choi Signed-off-by: Sylwester Nawrocki --- drivers/clk/samsung/clk-exynos5433.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c index 256fec0f2d18..39c95649d3d0 100644 --- a/drivers/clk/samsung/clk-exynos5433.c +++ b/drivers/clk/samsung/clk-exynos5433.c @@ -1390,7 +1390,7 @@ static struct samsung_gate_clock mif_gate_clks[] __initdata = { /* ENABLE_ACLK_MIF2 */ GATE(CLK_ACLK_MIFND_266, "aclk_mifnd_266", "div_aclk_mif_266", - ENABLE_ACLK_MIF2, 20, 0, 0), + ENABLE_ACLK_MIF2, 20, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_PPMU_DREX1S3, "aclk_ppmu_drex1s3", "div_aclk_drex1", ENABLE_ACLK_MIF2, 17, CLK_IGNORE_UNUSED, 0), GATE(CLK_ACLK_PPMU_DREX1S1, "aclk_ppmu_drex1s1", "div_aclk_drex1", @@ -1833,39 +1833,39 @@ static struct samsung_gate_clock peris_gate_clks[] __initdata = { /* ENABLE_PCLK_PERIS_SECURE_TZPC */ GATE(CLK_PCLK_TZPC12, "pclk_tzpc12", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 12, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 12, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC11, "pclk_tzpc11", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 11, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 11, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC10, "pclk_tzpc10", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 10, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 10, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC9, "pclk_tzpc9", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 9, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 9, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC8, "pclk_tzpc8", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 8, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 8, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC7, "pclk_tzpc7", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 7, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 7, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC6, "pclk_tzpc6", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 6, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 6, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC5, "pclk_tzpc5", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 5, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 5, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC4, "pclk_tzpc4", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 4, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 4, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC3, "pclk_tzpc3", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 3, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 3, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC2, "pclk_tzpc2", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 2, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 2, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC1, "pclk_tzpc1", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 1, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 1, CLK_IGNORE_UNUSED, 0), GATE(CLK_PCLK_TZPC0, "pclk_tzpc0", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_TZPC, 0, 0, 0), + ENABLE_PCLK_PERIS_SECURE_TZPC, 0, CLK_IGNORE_UNUSED, 0), /* ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF */ GATE(CLK_PCLK_SECKEY_APBIF, "pclk_seckey_apbif", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF, 0, 0, 0), + ENABLE_PCLK_PERIS_SECURE_SECKEY_APBIF, 0, CLK_IGNORE_UNUSED, 0), /* ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF */ GATE(CLK_PCLK_CHIPID_APBIF, "pclk_chipid_apbif", "aclk_peris_66", - ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF, 0, 0, 0), + ENABLE_PCLK_PERIS_SECURE_CHIPID_APBIF, 0, CLK_IGNORE_UNUSED, 0), /* ENABLE_PCLK_PERIS_SECURE_TOPRTC */ GATE(CLK_PCLK_TOPRTC, "pclk_toprtc", "aclk_peris_66", @@ -1896,11 +1896,11 @@ static struct samsung_gate_clock peris_gate_clks[] __initdata = { /* ENABLE_SCLK_PERIS_SECURE_SECKEY */ GATE(CLK_SCLK_SECKEY, "sclk_seckey", "oscclk_efuse_common", - ENABLE_SCLK_PERIS_SECURE_SECKEY, 0, 0, 0), + ENABLE_SCLK_PERIS_SECURE_SECKEY, 0, CLK_IGNORE_UNUSED, 0), /* ENABLE_SCLK_PERIS_SECURE_CHIPID */ GATE(CLK_SCLK_CHIPID, "sclk_chipid", "oscclk_efuse_common", - ENABLE_SCLK_PERIS_SECURE_CHIPID, 0, 0, 0), + ENABLE_SCLK_PERIS_SECURE_CHIPID, 0, CLK_IGNORE_UNUSED, 0), /* ENABLE_SCLK_PERIS_SECURE_TOPRTC */ GATE(CLK_SCLK_TOPRTC, "sclk_toprtc", "oscclk_efuse_common", -- cgit v1.2.3-58-ga151 From 7eaf8b9f0b699732bab739ae47d4cad55e7ee4e3 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 27 May 2015 11:26:27 +0300 Subject: clk: socfpga: remove a stray tab This line was indented too far. Signed-off-by: Dan Carpenter Acked-by: Dinh Nguyen Signed-off-by: Michael Turquette --- drivers/clk/socfpga/clk-gate-a10.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/socfpga/clk-gate-a10.c b/drivers/clk/socfpga/clk-gate-a10.c index be3e998b85d0..83c6780ff4b2 100644 --- a/drivers/clk/socfpga/clk-gate-a10.c +++ b/drivers/clk/socfpga/clk-gate-a10.c @@ -39,7 +39,7 @@ static unsigned long socfpga_gate_clk_recalc_rate(struct clk_hw *hwclk, else if (socfpgaclk->div_reg) { val = readl(socfpgaclk->div_reg) >> socfpgaclk->shift; val &= div_mask(socfpgaclk->width); - div = (1 << val); + div = (1 << val); } return parent_rate / div; -- cgit v1.2.3-58-ga151 From b41c7bfa27f221a567e33b44aa5395adfe042229 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 10 Jun 2015 14:14:28 -0700 Subject: clk: cdce925: Fix printk size_t warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/clk/clk-cdce925.c:550: warning: format ‘%u’ expects type ‘unsigned int’, but argument 6 has type ‘size_t’ Cc: Mike Looijmans Reported-by: kbuild test robot Signed-off-by: Stephen Boyd --- drivers/clk/clk-cdce925.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk-cdce925.c b/drivers/clk/clk-cdce925.c index 56b870d331a1..85fafb41e6ca 100644 --- a/drivers/clk/clk-cdce925.c +++ b/drivers/clk/clk-cdce925.c @@ -547,7 +547,7 @@ static int cdce925_regmap_i2c_read(void *context, ret = i2c_transfer(i2c->adapter, xfer, 2); if (likely(ret == 2)) { - dev_dbg(&i2c->dev, "%s(%zu, %u) %#x %#x\n", __func__, + dev_dbg(&i2c->dev, "%s(%zu, %zu) %#x %#x\n", __func__, reg_size, val_size, reg_data[0], *((u8 *)val)); return 0; } else if (ret < 0) -- cgit v1.2.3-58-ga151 From 3037e9ea780027d41baaaabb68a749e49e7c8260 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Wed, 10 Jun 2015 21:04:54 +0100 Subject: clk: fixed: Add comment to clk_fixed_set_rate Currently it is not made explicit why clk_fixed_set_rate() can ignore its arguments and unconditionally return success. Add a comment to explain this. We also mark the clk_ops table const since it should never be modified at runtime. Suggested-by: Stephen Boyd Signed-off-by: Daniel Thompson Signed-off-by: Stephen Boyd --- drivers/clk/clk-fixed-factor.c | 8 +++++++- include/linux/clk-provider.h | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index e186db263d5e..fccabe497f6e 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -55,10 +55,16 @@ static long clk_factor_round_rate(struct clk_hw *hw, unsigned long rate, static int clk_factor_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { + /* + * We must report success but we can do so unconditionally because + * clk_factor_round_rate returns values that ensure this call is a + * nop. + */ + return 0; } -struct clk_ops clk_fixed_factor_ops = { +const struct clk_ops clk_fixed_factor_ops = { .round_rate = clk_factor_round_rate, .set_rate = clk_factor_set_rate, .recalc_rate = clk_factor_recalc_rate, diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 2e5df069ca34..4a943d13625b 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -459,7 +459,7 @@ struct clk_fixed_factor { unsigned int div; }; -extern struct clk_ops clk_fixed_factor_ops; +extern const struct clk_ops clk_fixed_factor_ops; struct clk *clk_register_fixed_factor(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div); -- cgit v1.2.3-58-ga151 From 8c9a8a8f71f43e56d35524fa17646ce45c2f7fe6 Mon Sep 17 00:00:00 2001 From: Maxime Coquelin Date: Wed, 10 Jun 2015 13:28:27 +0200 Subject: clk: Move debug_node field under DEBUG_FS flag in struct clk_core The debug_node field is only used when DEBUG_FS config is selected, so declare it only if DEBUG_FS is selected. Signed-off-by: Maxime Coquelin Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index ee222e28bd97..1cf479b9f3b4 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -62,11 +62,11 @@ struct clk_core { int phase; struct hlist_head children; struct hlist_node child_node; - struct hlist_node debug_node; struct hlist_head clks; unsigned int notifier_count; #ifdef CONFIG_DEBUG_FS struct dentry *dentry; + struct hlist_node debug_node; #endif struct kref ref; }; -- cgit v1.2.3-58-ga151 From 22109785163310666cf9913bafbcc11c5aebe68a Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 11 Jun 2015 16:08:51 -0700 Subject: clk: pxa: Fix const discarding warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A recent change to mark parent names as const missed this struct member so we get warnings like: drivers/clk/pxa/clk-pxa25x.c:122:2: warning: initialization discards 'const' qualifier from pointer target type Fix it. Reported-by: kbuild test robot Acked-by: Uwe Kleine-König Signed-off-by: Stephen Boyd --- drivers/clk/pxa/clk-pxa.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/pxa/clk-pxa.h b/drivers/clk/pxa/clk-pxa.h index 7bf532dca709..d1de805df867 100644 --- a/drivers/clk/pxa/clk-pxa.h +++ b/drivers/clk/pxa/clk-pxa.h @@ -72,7 +72,7 @@ struct desc_clk_cken { const char *name; const char *dev_id; const char *con_id; - const char **parent_names; + const char * const *parent_names; struct clk_fixed_factor lp; struct clk_fixed_factor hp; struct clk_gate gate; -- cgit v1.2.3-58-ga151 From 46965688acd0f9599a3c3ecce82109b3b24153a9 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 11 Jun 2015 18:20:46 +0300 Subject: clk: meson: add some error handling in meson_clk_register_cpu() This error handling hopefully isn't needed but it make the static checkers happy. Signed-off-by: Dan Carpenter Acked-by: Carlo Caione Signed-off-by: Stephen Boyd --- drivers/clk/meson/clk-cpu.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/clk/meson/clk-cpu.c b/drivers/clk/meson/clk-cpu.c index 148e99fc5970..71ad493b94df 100644 --- a/drivers/clk/meson/clk-cpu.c +++ b/drivers/clk/meson/clk-cpu.c @@ -213,22 +213,30 @@ struct clk *meson_clk_register_cpu(const struct clk_conf *clk_conf, if (!pclk) { pr_err("%s: could not lookup parent clock %s\n", __func__, clk_conf->clks_parent[0]); - return ERR_PTR(-EINVAL); + ret = -EINVAL; + goto free_clk; } ret = clk_notifier_register(pclk, &clk_cpu->clk_nb); if (ret) { pr_err("%s: failed to register clock notifier for %s\n", __func__, clk_conf->clk_name); - return ERR_PTR(-EINVAL); + goto free_clk; } clk = clk_register(NULL, &clk_cpu->hw); if (IS_ERR(clk)) { - clk_notifier_unregister(pclk, &clk_cpu->clk_nb); - kfree(clk_cpu); + ret = PTR_ERR(clk); + goto unregister_clk_nb; } return clk; + +unregister_clk_nb: + clk_notifier_unregister(pclk, &clk_cpu->clk_nb); +free_clk: + kfree(clk_cpu); + + return ERR_PTR(ret); } -- cgit v1.2.3-58-ga151 From a85fa007b772611fc97f3d68bce33e69a5ed8945 Mon Sep 17 00:00:00 2001 From: Michael Turquette Date: Wed, 17 Jun 2015 13:41:04 -0700 Subject: MAINTAINERS: update email for Michael Turquette Signed-off-by: Michael Turquette --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index d8afd2953678..15b6d710d222 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2642,7 +2642,7 @@ F: Documentation/devicetree/bindings/media/coda.txt F: drivers/media/platform/coda/ COMMON CLK FRAMEWORK -M: Mike Turquette +M: Michael Turquette M: Stephen Boyd L: linux-clk@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux.git -- cgit v1.2.3-58-ga151 From 476276d69d84edd4f5de9711f55c810dfc69c26c Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Tue, 5 May 2015 11:13:18 -0700 Subject: clk: iproc: define Broadcom iProc clock binding Document the device tree binding for Broadcom iProc architecture based clock controller Signed-off-by: Ray Jui Reviewed-by: Scott Branden Signed-off-by: Michael Turquette --- .../bindings/clock/brcm,iproc-clocks.txt | 132 +++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/brcm,iproc-clocks.txt diff --git a/Documentation/devicetree/bindings/clock/brcm,iproc-clocks.txt b/Documentation/devicetree/bindings/clock/brcm,iproc-clocks.txt new file mode 100644 index 000000000000..da8d9bb5751c --- /dev/null +++ b/Documentation/devicetree/bindings/clock/brcm,iproc-clocks.txt @@ -0,0 +1,132 @@ +Broadcom iProc Family Clocks + +This binding uses the common clock binding: + Documentation/devicetree/bindings/clock/clock-bindings.txt + +The iProc clock controller manages clocks that are common to the iProc family. +An SoC from the iProc family may have several PPLs, e.g., ARMPLL, GENPLL, +LCPLL0, MIPIPLL, and etc., all derived from an onboard crystal. Each PLL +comprises of several leaf clocks + +Required properties for a PLL and its leaf clocks: + +- compatible: + Should have a value of the form "brcm,-". For example, GENPLL on +Cygnus has a compatible string of "brcm,cygnus-genpll" + +- #clock-cells: + Have a value of <1> since there are more than 1 leaf clock of a given PLL + +- reg: + Define the base and range of the I/O address space that contain the iProc +clock control registers required for the PLL + +- clocks: + The input parent clock phandle for the PLL. For most iProc PLLs, this is an +onboard crystal with a fixed rate + +- clock-output-names: + An ordered list of strings defining the names of the clocks + +Example: + + osc: oscillator { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <25000000>; + }; + + genpll: genpll { + #clock-cells = <1>; + compatible = "brcm,cygnus-genpll"; + reg = <0x0301d000 0x2c>, <0x0301c020 0x4>; + clocks = <&osc>; + clock-output-names = "genpll", "axi21", "250mhz", "ihost_sys", + "enet_sw", "audio_125", "can"; + }; + +Required properties for ASIU clocks: + +ASIU clocks are a special case. These clocks are derived directly from the +reference clock of the onboard crystal + +- compatible: + Should have a value of the form "brcm,-asiu-clk". For example, ASIU +clocks for Cygnus have a compatible string of "brcm,cygnus-asiu-clk" + +- #clock-cells: + Have a value of <1> since there are more than 1 ASIU clocks + +- reg: + Define the base and range of the I/O address space that contain the iProc +clock control registers required for ASIU clocks + +- clocks: + The input parent clock phandle for the ASIU clock, i.e., the onboard +crystal + +- clock-output-names: + An ordered list of strings defining the names of the ASIU clocks + +Example: + + osc: oscillator { + #clock-cells = <0>; + compatible = "fixed-clock"; + clock-frequency = <25000000>; + }; + + asiu_clks: asiu_clks { + #clock-cells = <1>; + compatible = "brcm,cygnus-asiu-clk"; + reg = <0x0301d048 0xc>, <0x180aa024 0x4>; + clocks = <&osc>; + clock-output-names = "keypad", "adc/touch", "pwm"; + }; + +Cygnus +------ +PLL and leaf clock compatible strings for Cygnus are: + "brcm,cygnus-armpll" + "brcm,cygnus-genpll" + "brcm,cygnus-lcpll0" + "brcm,cygnus-mipipll" + "brcm,cygnus-asiu-clk" + +The following table defines the set of PLL/clock index and ID for Cygnus. +These clock IDs are defined in: + "include/dt-bindings/clock/bcm-cygnus.h" + + Clock Source (Parent) Index ID + --- ----- ----- --------- + crystal N/A N/A N/A + + armpll crystal N/A N/A + + keypad crystal (ASIU) 0 BCM_CYGNUS_ASIU_KEYPAD_CLK + adc/tsc crystal (ASIU) 1 BCM_CYGNUS_ASIU_ADC_CLK + pwm crystal (ASIU) 2 BCM_CYGNUS_ASIU_PWM_CLK + + genpll crystal 0 BCM_CYGNUS_GENPLL + axi21 genpll 1 BCM_CYGNUS_GENPLL_AXI21_CLK + 250mhz genpll 2 BCM_CYGNUS_GENPLL_250MHZ_CLK + ihost_sys genpll 3 BCM_CYGNUS_GENPLL_IHOST_SYS_CLK + enet_sw genpll 4 BCM_CYGNUS_GENPLL_ENET_SW_CLK + audio_125 genpll 5 BCM_CYGNUS_GENPLL_AUDIO_125_CLK + can genpll 6 BCM_CYGNUS_GENPLL_CAN_CLK + + lcpll0 crystal 0 BCM_CYGNUS_LCPLL0 + pcie_phy lcpll0 1 BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK + ddr_phy lcpll0 2 BCM_CYGNUS_LCPLL0_DDR_PHY_CLK + sdio lcpll0 3 BCM_CYGNUS_LCPLL0_SDIO_CLK + usb_phy lcpll0 4 BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK + smart_card lcpll0 5 BCM_CYGNUS_LCPLL0_SMART_CARD_CLK + ch5_unused lcpll0 6 BCM_CYGNUS_LCPLL0_CH5_UNUSED + + mipipll crystal 0 BCM_CYGNUS_MIPIPLL + ch0_unused mipipll 1 BCM_CYGNUS_MIPIPLL_CH0_UNUSED + ch1_lcd mipipll 2 BCM_CYGNUS_MIPIPLL_CH1_LCD + ch2_v3d mipipll 3 BCM_CYGNUS_MIPIPLL_CH2_V3D + ch3_unused mipipll 4 BCM_CYGNUS_MIPIPLL_CH3_UNUSED + ch4_unused mipipll 5 BCM_CYGNUS_MIPIPLL_CH4_UNUSED + ch5_unused mipipll 6 BCM_CYGNUS_MIPIPLL_CH5_UNUSED -- cgit v1.2.3-58-ga151 From 5fe225c105fd54debae1699ec0f6aef1e73376d0 Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Tue, 5 May 2015 11:13:19 -0700 Subject: clk: iproc: add initial common clock support This adds basic and generic support for various iProc PLLs and clocks including the ARMPLL, GENPLL, LCPLL, MIPIPLL, and ASIU clocks. SoCs under the iProc architecture can define their specific register offsets and clock parameters for their PLL and clock controllers. These parameters can be passed as arugments into the generic iProc PLL and clock setup functions Derived from code originally provided by Jonathan Richardson Signed-off-by: Ray Jui Reviewed-by: Scott Branden Signed-off-by: Michael Turquette --- drivers/clk/bcm/Kconfig | 9 + drivers/clk/bcm/Makefile | 1 + drivers/clk/bcm/clk-iproc-armpll.c | 282 +++++++++++++++ drivers/clk/bcm/clk-iproc-asiu.c | 276 ++++++++++++++ drivers/clk/bcm/clk-iproc-pll.c | 716 +++++++++++++++++++++++++++++++++++++ drivers/clk/bcm/clk-iproc.h | 178 +++++++++ 6 files changed, 1462 insertions(+) create mode 100644 drivers/clk/bcm/clk-iproc-armpll.c create mode 100644 drivers/clk/bcm/clk-iproc-asiu.c create mode 100644 drivers/clk/bcm/clk-iproc-pll.c create mode 100644 drivers/clk/bcm/clk-iproc.h diff --git a/drivers/clk/bcm/Kconfig b/drivers/clk/bcm/Kconfig index 75506e53075b..88febf53b276 100644 --- a/drivers/clk/bcm/Kconfig +++ b/drivers/clk/bcm/Kconfig @@ -7,3 +7,12 @@ config CLK_BCM_KONA Enable common clock framework support for Broadcom SoCs using "Kona" style clock control units, including those in the BCM281xx and BCM21664 families. + +config COMMON_CLK_IPROC + bool "Broadcom iProc clock support" + depends on ARCH_BCM_IPROC + depends on COMMON_CLK + default ARCH_BCM_IPROC + help + Enable common clock framework support for Broadcom SoCs + based on the iProc architecture diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile index 6297d05a9a10..0facbbc5652c 100644 --- a/drivers/clk/bcm/Makefile +++ b/drivers/clk/bcm/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona.o obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o +obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o diff --git a/drivers/clk/bcm/clk-iproc-armpll.c b/drivers/clk/bcm/clk-iproc-armpll.c new file mode 100644 index 000000000000..a196ee28a17a --- /dev/null +++ b/drivers/clk/bcm/clk-iproc-armpll.c @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPROC_CLK_MAX_FREQ_POLICY 0x3 +#define IPROC_CLK_POLICY_FREQ_OFFSET 0x008 +#define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT 8 +#define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK 0x7 + +#define IPROC_CLK_PLLARMA_OFFSET 0xc00 +#define IPROC_CLK_PLLARMA_LOCK_SHIFT 28 +#define IPROC_CLK_PLLARMA_PDIV_SHIFT 24 +#define IPROC_CLK_PLLARMA_PDIV_MASK 0xf +#define IPROC_CLK_PLLARMA_NDIV_INT_SHIFT 8 +#define IPROC_CLK_PLLARMA_NDIV_INT_MASK 0x3ff + +#define IPROC_CLK_PLLARMB_OFFSET 0xc04 +#define IPROC_CLK_PLLARMB_NDIV_FRAC_MASK 0xfffff + +#define IPROC_CLK_PLLARMC_OFFSET 0xc08 +#define IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT 8 +#define IPROC_CLK_PLLARMC_MDIV_MASK 0xff + +#define IPROC_CLK_PLLARMCTL5_OFFSET 0xc20 +#define IPROC_CLK_PLLARMCTL5_H_MDIV_MASK 0xff + +#define IPROC_CLK_PLLARM_OFFSET_OFFSET 0xc24 +#define IPROC_CLK_PLLARM_SW_CTL_SHIFT 29 +#define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT 20 +#define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK 0xff +#define IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK 0xfffff + +#define IPROC_CLK_ARM_DIV_OFFSET 0xe00 +#define IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT 4 +#define IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK 0xf + +#define IPROC_CLK_POLICY_DBG_OFFSET 0xec0 +#define IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT 12 +#define IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK 0x7 + +enum iproc_arm_pll_fid { + ARM_PLL_FID_CRYSTAL_CLK = 0, + ARM_PLL_FID_SYS_CLK = 2, + ARM_PLL_FID_CH0_SLOW_CLK = 6, + ARM_PLL_FID_CH1_FAST_CLK = 7 +}; + +struct iproc_arm_pll { + struct clk_hw hw; + void __iomem *base; + unsigned long rate; +}; + +#define to_iproc_arm_pll(hw) container_of(hw, struct iproc_arm_pll, hw) + +static unsigned int __get_fid(struct iproc_arm_pll *pll) +{ + u32 val; + unsigned int policy, fid, active_fid; + + val = readl(pll->base + IPROC_CLK_ARM_DIV_OFFSET); + if (val & (1 << IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT)) + policy = val & IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK; + else + policy = 0; + + /* something is seriously wrong */ + BUG_ON(policy > IPROC_CLK_MAX_FREQ_POLICY); + + val = readl(pll->base + IPROC_CLK_POLICY_FREQ_OFFSET); + fid = (val >> (IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT * policy)) & + IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK; + + val = readl(pll->base + IPROC_CLK_POLICY_DBG_OFFSET); + active_fid = IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK & + (val >> IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT); + if (fid != active_fid) { + pr_debug("%s: fid override %u->%u\n", __func__, fid, + active_fid); + fid = active_fid; + } + + pr_debug("%s: active fid: %u\n", __func__, fid); + + return fid; +} + +/* + * Determine the mdiv (post divider) based on the frequency ID being used. + * There are 4 sources that can be used to derive the output clock rate: + * - 25 MHz Crystal + * - System clock + * - PLL channel 0 (slow clock) + * - PLL channel 1 (fast clock) + */ +static int __get_mdiv(struct iproc_arm_pll *pll) +{ + unsigned int fid; + int mdiv; + u32 val; + + fid = __get_fid(pll); + + switch (fid) { + case ARM_PLL_FID_CRYSTAL_CLK: + case ARM_PLL_FID_SYS_CLK: + mdiv = 1; + break; + + case ARM_PLL_FID_CH0_SLOW_CLK: + val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET); + mdiv = val & IPROC_CLK_PLLARMC_MDIV_MASK; + if (mdiv == 0) + mdiv = 256; + break; + + case ARM_PLL_FID_CH1_FAST_CLK: + val = readl(pll->base + IPROC_CLK_PLLARMCTL5_OFFSET); + mdiv = val & IPROC_CLK_PLLARMCTL5_H_MDIV_MASK; + if (mdiv == 0) + mdiv = 256; + break; + + default: + mdiv = -EFAULT; + } + + return mdiv; +} + +static unsigned int __get_ndiv(struct iproc_arm_pll *pll) +{ + u32 val; + unsigned int ndiv_int, ndiv_frac, ndiv; + + val = readl(pll->base + IPROC_CLK_PLLARM_OFFSET_OFFSET); + if (val & (1 << IPROC_CLK_PLLARM_SW_CTL_SHIFT)) { + /* + * offset mode is active. Read the ndiv from the PLLARM OFFSET + * register + */ + ndiv_int = (val >> IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT) & + IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK; + if (ndiv_int == 0) + ndiv_int = 256; + + ndiv_frac = val & IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK; + } else { + /* offset mode not active */ + val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET); + ndiv_int = (val >> IPROC_CLK_PLLARMA_NDIV_INT_SHIFT) & + IPROC_CLK_PLLARMA_NDIV_INT_MASK; + if (ndiv_int == 0) + ndiv_int = 1024; + + val = readl(pll->base + IPROC_CLK_PLLARMB_OFFSET); + ndiv_frac = val & IPROC_CLK_PLLARMB_NDIV_FRAC_MASK; + } + + ndiv = (ndiv_int << 20) | ndiv_frac; + + return ndiv; +} + +/* + * The output frequency of the ARM PLL is calculated based on the ARM PLL + * divider values: + * pdiv = ARM PLL pre-divider + * ndiv = ARM PLL multiplier + * mdiv = ARM PLL post divider + * + * The frequency is calculated by: + * ((ndiv * parent clock rate) / pdiv) / mdiv + */ +static unsigned long iproc_arm_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct iproc_arm_pll *pll = to_iproc_arm_pll(hw); + u32 val; + int mdiv; + u64 ndiv; + unsigned int pdiv; + + /* in bypass mode, use parent rate */ + val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET); + if (val & (1 << IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT)) { + pll->rate = parent_rate; + return pll->rate; + } + + /* PLL needs to be locked */ + val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET); + if (!(val & (1 << IPROC_CLK_PLLARMA_LOCK_SHIFT))) { + pll->rate = 0; + return 0; + } + + pdiv = (val >> IPROC_CLK_PLLARMA_PDIV_SHIFT) & + IPROC_CLK_PLLARMA_PDIV_MASK; + if (pdiv == 0) + pdiv = 16; + + ndiv = __get_ndiv(pll); + mdiv = __get_mdiv(pll); + if (mdiv <= 0) { + pll->rate = 0; + return 0; + } + pll->rate = (ndiv * parent_rate) >> 20; + pll->rate = (pll->rate / pdiv) / mdiv; + + pr_debug("%s: ARM PLL rate: %lu. parent rate: %lu\n", __func__, + pll->rate, parent_rate); + pr_debug("%s: ndiv_int: %u, pdiv: %u, mdiv: %d\n", __func__, + (unsigned int)(ndiv >> 20), pdiv, mdiv); + + return pll->rate; +} + +static const struct clk_ops iproc_arm_pll_ops = { + .recalc_rate = iproc_arm_pll_recalc_rate, +}; + +void __init iproc_armpll_setup(struct device_node *node) +{ + int ret; + struct clk *clk; + struct iproc_arm_pll *pll; + struct clk_init_data init; + const char *parent_name; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (WARN_ON(!pll)) + return; + + pll->base = of_iomap(node, 0); + if (WARN_ON(!pll->base)) + goto err_free_pll; + + init.name = node->name; + init.ops = &iproc_arm_pll_ops; + init.flags = 0; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + pll->hw.init = &init; + + clk = clk_register(NULL, &pll->hw); + if (WARN_ON(IS_ERR(clk))) + goto err_iounmap; + + ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); + if (WARN_ON(ret)) + goto err_clk_unregister; + + return; + +err_clk_unregister: + clk_unregister(clk); +err_iounmap: + iounmap(pll->base); +err_free_pll: + kfree(pll); +} diff --git a/drivers/clk/bcm/clk-iproc-asiu.c b/drivers/clk/bcm/clk-iproc-asiu.c new file mode 100644 index 000000000000..e19c09cd9645 --- /dev/null +++ b/drivers/clk/bcm/clk-iproc-asiu.c @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk-iproc.h" + +struct iproc_asiu; + +struct iproc_asiu_clk { + struct clk_hw hw; + const char *name; + struct iproc_asiu *asiu; + unsigned long rate; + struct iproc_asiu_div div; + struct iproc_asiu_gate gate; +}; + +struct iproc_asiu { + void __iomem *div_base; + void __iomem *gate_base; + + struct clk_onecell_data clk_data; + struct iproc_asiu_clk *clks; +}; + +#define to_asiu_clk(hw) container_of(hw, struct iproc_asiu_clk, hw) + +static int iproc_asiu_clk_enable(struct clk_hw *hw) +{ + struct iproc_asiu_clk *clk = to_asiu_clk(hw); + struct iproc_asiu *asiu = clk->asiu; + u32 val; + + /* some clocks at the ASIU level are always enabled */ + if (clk->gate.offset == IPROC_CLK_INVALID_OFFSET) + return 0; + + val = readl(asiu->gate_base + clk->gate.offset); + val |= (1 << clk->gate.en_shift); + writel(val, asiu->gate_base + clk->gate.offset); + + return 0; +} + +static void iproc_asiu_clk_disable(struct clk_hw *hw) +{ + struct iproc_asiu_clk *clk = to_asiu_clk(hw); + struct iproc_asiu *asiu = clk->asiu; + u32 val; + + /* some clocks at the ASIU level are always enabled */ + if (clk->gate.offset == IPROC_CLK_INVALID_OFFSET) + return; + + val = readl(asiu->gate_base + clk->gate.offset); + val &= ~(1 << clk->gate.en_shift); + writel(val, asiu->gate_base + clk->gate.offset); +} + +static unsigned long iproc_asiu_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct iproc_asiu_clk *clk = to_asiu_clk(hw); + struct iproc_asiu *asiu = clk->asiu; + u32 val; + unsigned int div_h, div_l; + + if (parent_rate == 0) { + clk->rate = 0; + return 0; + } + + /* if clock divisor is not enabled, simply return parent rate */ + val = readl(asiu->div_base + clk->div.offset); + if ((val & (1 << clk->div.en_shift)) == 0) { + clk->rate = parent_rate; + return parent_rate; + } + + /* clock rate = parent rate / (high_div + 1) + (low_div + 1) */ + div_h = (val >> clk->div.high_shift) & bit_mask(clk->div.high_width); + div_h++; + div_l = (val >> clk->div.low_shift) & bit_mask(clk->div.low_width); + div_l++; + + clk->rate = parent_rate / (div_h + div_l); + pr_debug("%s: rate: %lu. parent rate: %lu div_h: %u div_l: %u\n", + __func__, clk->rate, parent_rate, div_h, div_l); + + return clk->rate; +} + +static long iproc_asiu_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned int div; + + if (rate == 0 || *parent_rate == 0) + return -EINVAL; + + if (rate == *parent_rate) + return *parent_rate; + + div = DIV_ROUND_UP(*parent_rate, rate); + if (div < 2) + return *parent_rate; + + return *parent_rate / div; +} + +static int iproc_asiu_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct iproc_asiu_clk *clk = to_asiu_clk(hw); + struct iproc_asiu *asiu = clk->asiu; + unsigned int div, div_h, div_l; + u32 val; + + if (rate == 0 || parent_rate == 0) + return -EINVAL; + + /* simply disable the divisor if one wants the same rate as parent */ + if (rate == parent_rate) { + val = readl(asiu->div_base + clk->div.offset); + val &= ~(1 << clk->div.en_shift); + writel(val, asiu->div_base + clk->div.offset); + return 0; + } + + div = DIV_ROUND_UP(parent_rate, rate); + if (div < 2) + return -EINVAL; + + div_h = div_l = div >> 1; + div_h--; + div_l--; + + val = readl(asiu->div_base + clk->div.offset); + val |= 1 << clk->div.en_shift; + if (div_h) { + val &= ~(bit_mask(clk->div.high_width) + << clk->div.high_shift); + val |= div_h << clk->div.high_shift; + } else { + val &= ~(bit_mask(clk->div.high_width) + << clk->div.high_shift); + } + if (div_l) { + val &= ~(bit_mask(clk->div.low_width) << clk->div.low_shift); + val |= div_l << clk->div.low_shift; + } else { + val &= ~(bit_mask(clk->div.low_width) << clk->div.low_shift); + } + writel(val, asiu->div_base + clk->div.offset); + + return 0; +} + +static const struct clk_ops iproc_asiu_ops = { + .enable = iproc_asiu_clk_enable, + .disable = iproc_asiu_clk_disable, + .recalc_rate = iproc_asiu_clk_recalc_rate, + .round_rate = iproc_asiu_clk_round_rate, + .set_rate = iproc_asiu_clk_set_rate, +}; + +void __init iproc_asiu_setup(struct device_node *node, + const struct iproc_asiu_div *div, + const struct iproc_asiu_gate *gate, + unsigned int num_clks) +{ + int i, ret; + struct iproc_asiu *asiu; + + if (WARN_ON(!gate || !div)) + return; + + asiu = kzalloc(sizeof(*asiu), GFP_KERNEL); + if (WARN_ON(!asiu)) + return; + + asiu->clk_data.clk_num = num_clks; + asiu->clk_data.clks = kcalloc(num_clks, sizeof(*asiu->clk_data.clks), + GFP_KERNEL); + if (WARN_ON(!asiu->clk_data.clks)) + goto err_clks; + + asiu->clks = kcalloc(num_clks, sizeof(*asiu->clks), GFP_KERNEL); + if (WARN_ON(!asiu->clks)) + goto err_asiu_clks; + + asiu->div_base = of_iomap(node, 0); + if (WARN_ON(!asiu->div_base)) + goto err_iomap_div; + + asiu->gate_base = of_iomap(node, 1); + if (WARN_ON(!asiu->gate_base)) + goto err_iomap_gate; + + for (i = 0; i < num_clks; i++) { + struct clk_init_data init; + struct clk *clk; + const char *parent_name; + struct iproc_asiu_clk *asiu_clk; + const char *clk_name; + + clk_name = kzalloc(IPROC_CLK_NAME_LEN, GFP_KERNEL); + if (WARN_ON(!clk_name)) + goto err_clk_register; + + ret = of_property_read_string_index(node, "clock-output-names", + i, &clk_name); + if (WARN_ON(ret)) + goto err_clk_register; + + asiu_clk = &asiu->clks[i]; + asiu_clk->name = clk_name; + asiu_clk->asiu = asiu; + asiu_clk->div = div[i]; + asiu_clk->gate = gate[i]; + init.name = clk_name; + init.ops = &iproc_asiu_ops; + init.flags = 0; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + asiu_clk->hw.init = &init; + + clk = clk_register(NULL, &asiu_clk->hw); + if (WARN_ON(IS_ERR(clk))) + goto err_clk_register; + asiu->clk_data.clks[i] = clk; + } + + ret = of_clk_add_provider(node, of_clk_src_onecell_get, + &asiu->clk_data); + if (WARN_ON(ret)) + goto err_clk_register; + + return; + +err_clk_register: + for (i = 0; i < num_clks; i++) + kfree(asiu->clks[i].name); + iounmap(asiu->gate_base); + +err_iomap_gate: + iounmap(asiu->div_base); + +err_iomap_div: + kfree(asiu->clks); + +err_asiu_clks: + kfree(asiu->clk_data.clks); + +err_clks: + kfree(asiu); +} diff --git a/drivers/clk/bcm/clk-iproc-pll.c b/drivers/clk/bcm/clk-iproc-pll.c new file mode 100644 index 000000000000..46fb84bc2674 --- /dev/null +++ b/drivers/clk/bcm/clk-iproc-pll.c @@ -0,0 +1,716 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk-iproc.h" + +#define PLL_VCO_HIGH_SHIFT 19 +#define PLL_VCO_LOW_SHIFT 30 + +/* number of delay loops waiting for PLL to lock */ +#define LOCK_DELAY 100 + +/* number of VCO frequency bands */ +#define NUM_FREQ_BANDS 8 + +#define NUM_KP_BANDS 3 +enum kp_band { + KP_BAND_MID = 0, + KP_BAND_HIGH, + KP_BAND_HIGH_HIGH +}; + +static const unsigned int kp_table[NUM_KP_BANDS][NUM_FREQ_BANDS] = { + { 5, 6, 6, 7, 7, 8, 9, 10 }, + { 4, 4, 5, 5, 6, 7, 8, 9 }, + { 4, 5, 5, 6, 7, 8, 9, 10 }, +}; + +static const unsigned long ref_freq_table[NUM_FREQ_BANDS][2] = { + { 10000000, 12500000 }, + { 12500000, 15000000 }, + { 15000000, 20000000 }, + { 20000000, 25000000 }, + { 25000000, 50000000 }, + { 50000000, 75000000 }, + { 75000000, 100000000 }, + { 100000000, 125000000 }, +}; + +enum vco_freq_range { + VCO_LOW = 700000000U, + VCO_MID = 1200000000U, + VCO_HIGH = 2200000000U, + VCO_HIGH_HIGH = 3100000000U, + VCO_MAX = 4000000000U, +}; + +struct iproc_pll; + +struct iproc_clk { + struct clk_hw hw; + const char *name; + struct iproc_pll *pll; + unsigned long rate; + const struct iproc_clk_ctrl *ctrl; +}; + +struct iproc_pll { + void __iomem *pll_base; + void __iomem *pwr_base; + void __iomem *asiu_base; + + const struct iproc_pll_ctrl *ctrl; + const struct iproc_pll_vco_param *vco_param; + unsigned int num_vco_entries; + + struct clk_onecell_data clk_data; + struct iproc_clk *clks; +}; + +#define to_iproc_clk(hw) container_of(hw, struct iproc_clk, hw) + +/* + * Based on the target frequency, find a match from the VCO frequency parameter + * table and return its index + */ +static int pll_get_rate_index(struct iproc_pll *pll, unsigned int target_rate) +{ + int i; + + for (i = 0; i < pll->num_vco_entries; i++) + if (target_rate == pll->vco_param[i].rate) + break; + + if (i >= pll->num_vco_entries) + return -EINVAL; + + return i; +} + +static int get_kp(unsigned long ref_freq, enum kp_band kp_index) +{ + int i; + + if (ref_freq < ref_freq_table[0][0]) + return -EINVAL; + + for (i = 0; i < NUM_FREQ_BANDS; i++) { + if (ref_freq >= ref_freq_table[i][0] && + ref_freq < ref_freq_table[i][1]) + return kp_table[kp_index][i]; + } + return -EINVAL; +} + +static int pll_wait_for_lock(struct iproc_pll *pll) +{ + int i; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + + for (i = 0; i < LOCK_DELAY; i++) { + u32 val = readl(pll->pll_base + ctrl->status.offset); + + if (val & (1 << ctrl->status.shift)) + return 0; + udelay(10); + } + + return -EIO; +} + +static void __pll_disable(struct iproc_pll *pll) +{ + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + u32 val; + + if (ctrl->flags & IPROC_CLK_PLL_ASIU) { + val = readl(pll->asiu_base + ctrl->asiu.offset); + val &= ~(1 << ctrl->asiu.en_shift); + writel(val, pll->asiu_base + ctrl->asiu.offset); + } + + /* latch input value so core power can be shut down */ + val = readl(pll->pwr_base + ctrl->aon.offset); + val |= (1 << ctrl->aon.iso_shift); + writel(val, pll->pwr_base + ctrl->aon.offset); + + /* power down the core */ + val &= ~(bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift); + writel(val, pll->pwr_base + ctrl->aon.offset); +} + +static int __pll_enable(struct iproc_pll *pll) +{ + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + u32 val; + + /* power up the PLL and make sure it's not latched */ + val = readl(pll->pwr_base + ctrl->aon.offset); + val |= bit_mask(ctrl->aon.pwr_width) << ctrl->aon.pwr_shift; + val &= ~(1 << ctrl->aon.iso_shift); + writel(val, pll->pwr_base + ctrl->aon.offset); + + /* certain PLLs also need to be ungated from the ASIU top level */ + if (ctrl->flags & IPROC_CLK_PLL_ASIU) { + val = readl(pll->asiu_base + ctrl->asiu.offset); + val |= (1 << ctrl->asiu.en_shift); + writel(val, pll->asiu_base + ctrl->asiu.offset); + } + + return 0; +} + +static void __pll_put_in_reset(struct iproc_pll *pll) +{ + u32 val; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + const struct iproc_pll_reset_ctrl *reset = &ctrl->reset; + + val = readl(pll->pll_base + reset->offset); + val &= ~(1 << reset->reset_shift | 1 << reset->p_reset_shift); + writel(val, pll->pll_base + reset->offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + reset->offset); +} + +static void __pll_bring_out_reset(struct iproc_pll *pll, unsigned int kp, + unsigned int ka, unsigned int ki) +{ + u32 val; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + const struct iproc_pll_reset_ctrl *reset = &ctrl->reset; + + val = readl(pll->pll_base + reset->offset); + val &= ~(bit_mask(reset->ki_width) << reset->ki_shift | + bit_mask(reset->kp_width) << reset->kp_shift | + bit_mask(reset->ka_width) << reset->ka_shift); + val |= ki << reset->ki_shift | kp << reset->kp_shift | + ka << reset->ka_shift; + val |= 1 << reset->reset_shift | 1 << reset->p_reset_shift; + writel(val, pll->pll_base + reset->offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + reset->offset); +} + +static int pll_set_rate(struct iproc_clk *clk, unsigned int rate_index, + unsigned long parent_rate) +{ + struct iproc_pll *pll = clk->pll; + const struct iproc_pll_vco_param *vco = &pll->vco_param[rate_index]; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + int ka = 0, ki, kp, ret; + unsigned long rate = vco->rate; + u32 val; + enum kp_band kp_index; + unsigned long ref_freq; + + /* + * reference frequency = parent frequency / PDIV + * If PDIV = 0, then it becomes a multiplier (x2) + */ + if (vco->pdiv == 0) + ref_freq = parent_rate * 2; + else + ref_freq = parent_rate / vco->pdiv; + + /* determine Ki and Kp index based on target VCO frequency */ + if (rate >= VCO_LOW && rate < VCO_HIGH) { + ki = 4; + kp_index = KP_BAND_MID; + } else if (rate >= VCO_HIGH && rate && rate < VCO_HIGH_HIGH) { + ki = 3; + kp_index = KP_BAND_HIGH; + } else if (rate >= VCO_HIGH_HIGH && rate < VCO_MAX) { + ki = 3; + kp_index = KP_BAND_HIGH_HIGH; + } else { + pr_err("%s: pll: %s has invalid rate: %lu\n", __func__, + clk->name, rate); + return -EINVAL; + } + + kp = get_kp(ref_freq, kp_index); + if (kp < 0) { + pr_err("%s: pll: %s has invalid kp\n", __func__, clk->name); + return kp; + } + + ret = __pll_enable(pll); + if (ret) { + pr_err("%s: pll: %s fails to enable\n", __func__, clk->name); + return ret; + } + + /* put PLL in reset */ + __pll_put_in_reset(pll); + + writel(0, pll->pll_base + ctrl->vco_ctrl.u_offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->vco_ctrl.u_offset); + val = readl(pll->pll_base + ctrl->vco_ctrl.l_offset); + + if (rate >= VCO_LOW && rate < VCO_MID) + val |= (1 << PLL_VCO_LOW_SHIFT); + + if (rate < VCO_HIGH) + val &= ~(1 << PLL_VCO_HIGH_SHIFT); + else + val |= (1 << PLL_VCO_HIGH_SHIFT); + + writel(val, pll->pll_base + ctrl->vco_ctrl.l_offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->vco_ctrl.l_offset); + + /* program integer part of NDIV */ + val = readl(pll->pll_base + ctrl->ndiv_int.offset); + val &= ~(bit_mask(ctrl->ndiv_int.width) << ctrl->ndiv_int.shift); + val |= vco->ndiv_int << ctrl->ndiv_int.shift; + writel(val, pll->pll_base + ctrl->ndiv_int.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->ndiv_int.offset); + + /* program fractional part of NDIV */ + if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) { + val = readl(pll->pll_base + ctrl->ndiv_frac.offset); + val &= ~(bit_mask(ctrl->ndiv_frac.width) << + ctrl->ndiv_frac.shift); + val |= vco->ndiv_frac << ctrl->ndiv_frac.shift; + writel(val, pll->pll_base + ctrl->ndiv_frac.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->ndiv_frac.offset); + } + + /* program PDIV */ + val = readl(pll->pll_base + ctrl->pdiv.offset); + val &= ~(bit_mask(ctrl->pdiv.width) << ctrl->pdiv.shift); + val |= vco->pdiv << ctrl->pdiv.shift; + writel(val, pll->pll_base + ctrl->pdiv.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->pdiv.offset); + + __pll_bring_out_reset(pll, kp, ka, ki); + + ret = pll_wait_for_lock(pll); + if (ret < 0) { + pr_err("%s: pll: %s failed to lock\n", __func__, clk->name); + return ret; + } + + return 0; +} + +static int iproc_pll_enable(struct clk_hw *hw) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + struct iproc_pll *pll = clk->pll; + + return __pll_enable(pll); +} + +static void iproc_pll_disable(struct clk_hw *hw) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + struct iproc_pll *pll = clk->pll; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + + if (ctrl->flags & IPROC_CLK_AON) + return; + + __pll_disable(pll); +} + +static unsigned long iproc_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + struct iproc_pll *pll = clk->pll; + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + u32 val; + u64 ndiv; + unsigned int ndiv_int, ndiv_frac, pdiv; + + if (parent_rate == 0) + return 0; + + /* PLL needs to be locked */ + val = readl(pll->pll_base + ctrl->status.offset); + if ((val & (1 << ctrl->status.shift)) == 0) { + clk->rate = 0; + return 0; + } + + /* + * PLL output frequency = + * + * ((ndiv_int + ndiv_frac / 2^20) * (parent clock rate / pdiv) + */ + val = readl(pll->pll_base + ctrl->ndiv_int.offset); + ndiv_int = (val >> ctrl->ndiv_int.shift) & + bit_mask(ctrl->ndiv_int.width); + ndiv = ndiv_int << ctrl->ndiv_int.shift; + + if (ctrl->flags & IPROC_CLK_PLL_HAS_NDIV_FRAC) { + val = readl(pll->pll_base + ctrl->ndiv_frac.offset); + ndiv_frac = (val >> ctrl->ndiv_frac.shift) & + bit_mask(ctrl->ndiv_frac.width); + + if (ndiv_frac != 0) + ndiv = (ndiv_int << ctrl->ndiv_int.shift) | ndiv_frac; + } + + val = readl(pll->pll_base + ctrl->pdiv.offset); + pdiv = (val >> ctrl->pdiv.shift) & bit_mask(ctrl->pdiv.width); + + clk->rate = (ndiv * parent_rate) >> ctrl->ndiv_int.shift; + + if (pdiv == 0) + clk->rate *= 2; + else + clk->rate /= pdiv; + + return clk->rate; +} + +static long iproc_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned i; + struct iproc_clk *clk = to_iproc_clk(hw); + struct iproc_pll *pll = clk->pll; + + if (rate == 0 || *parent_rate == 0 || !pll->vco_param) + return -EINVAL; + + for (i = 0; i < pll->num_vco_entries; i++) { + if (rate <= pll->vco_param[i].rate) + break; + } + + if (i == pll->num_vco_entries) + i--; + + return pll->vco_param[i].rate; +} + +static int iproc_pll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + struct iproc_pll *pll = clk->pll; + int rate_index, ret; + + rate_index = pll_get_rate_index(pll, rate); + if (rate_index < 0) + return rate_index; + + ret = pll_set_rate(clk, rate_index, parent_rate); + return ret; +} + +static const struct clk_ops iproc_pll_ops = { + .enable = iproc_pll_enable, + .disable = iproc_pll_disable, + .recalc_rate = iproc_pll_recalc_rate, + .round_rate = iproc_pll_round_rate, + .set_rate = iproc_pll_set_rate, +}; + +static int iproc_clk_enable(struct clk_hw *hw) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + const struct iproc_clk_ctrl *ctrl = clk->ctrl; + struct iproc_pll *pll = clk->pll; + u32 val; + + /* channel enable is active low */ + val = readl(pll->pll_base + ctrl->enable.offset); + val &= ~(1 << ctrl->enable.enable_shift); + writel(val, pll->pll_base + ctrl->enable.offset); + + /* also make sure channel is not held */ + val = readl(pll->pll_base + ctrl->enable.offset); + val &= ~(1 << ctrl->enable.hold_shift); + writel(val, pll->pll_base + ctrl->enable.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->enable.offset); + + return 0; +} + +static void iproc_clk_disable(struct clk_hw *hw) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + const struct iproc_clk_ctrl *ctrl = clk->ctrl; + struct iproc_pll *pll = clk->pll; + u32 val; + + if (ctrl->flags & IPROC_CLK_AON) + return; + + val = readl(pll->pll_base + ctrl->enable.offset); + val |= 1 << ctrl->enable.enable_shift; + writel(val, pll->pll_base + ctrl->enable.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->enable.offset); +} + +static unsigned long iproc_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + const struct iproc_clk_ctrl *ctrl = clk->ctrl; + struct iproc_pll *pll = clk->pll; + u32 val; + unsigned int mdiv; + + if (parent_rate == 0) + return 0; + + val = readl(pll->pll_base + ctrl->mdiv.offset); + mdiv = (val >> ctrl->mdiv.shift) & bit_mask(ctrl->mdiv.width); + if (mdiv == 0) + mdiv = 256; + + clk->rate = parent_rate / mdiv; + + return clk->rate; +} + +static long iproc_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned int div; + + if (rate == 0 || *parent_rate == 0) + return -EINVAL; + + if (rate == *parent_rate) + return *parent_rate; + + div = DIV_ROUND_UP(*parent_rate, rate); + if (div < 2) + return *parent_rate; + + if (div > 256) + div = 256; + + return *parent_rate / div; +} + +static int iproc_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct iproc_clk *clk = to_iproc_clk(hw); + const struct iproc_clk_ctrl *ctrl = clk->ctrl; + struct iproc_pll *pll = clk->pll; + u32 val; + unsigned int div; + + if (rate == 0 || parent_rate == 0) + return -EINVAL; + + div = DIV_ROUND_UP(parent_rate, rate); + if (div > 256) + return -EINVAL; + + val = readl(pll->pll_base + ctrl->mdiv.offset); + if (div == 256) { + val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift); + } else { + val &= ~(bit_mask(ctrl->mdiv.width) << ctrl->mdiv.shift); + val |= div << ctrl->mdiv.shift; + } + writel(val, pll->pll_base + ctrl->mdiv.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->mdiv.offset); + clk->rate = parent_rate / div; + + return 0; +} + +static const struct clk_ops iproc_clk_ops = { + .enable = iproc_clk_enable, + .disable = iproc_clk_disable, + .recalc_rate = iproc_clk_recalc_rate, + .round_rate = iproc_clk_round_rate, + .set_rate = iproc_clk_set_rate, +}; + +/** + * Some PLLs require the PLL SW override bit to be set before changes can be + * applied to the PLL + */ +static void iproc_pll_sw_cfg(struct iproc_pll *pll) +{ + const struct iproc_pll_ctrl *ctrl = pll->ctrl; + + if (ctrl->flags & IPROC_CLK_PLL_NEEDS_SW_CFG) { + u32 val; + + val = readl(pll->pll_base + ctrl->sw_ctrl.offset); + val |= BIT(ctrl->sw_ctrl.shift); + writel(val, pll->pll_base + ctrl->sw_ctrl.offset); + if (unlikely(ctrl->flags & IPROC_CLK_NEEDS_READ_BACK)) + readl(pll->pll_base + ctrl->sw_ctrl.offset); + } +} + +void __init iproc_pll_clk_setup(struct device_node *node, + const struct iproc_pll_ctrl *pll_ctrl, + const struct iproc_pll_vco_param *vco, + unsigned int num_vco_entries, + const struct iproc_clk_ctrl *clk_ctrl, + unsigned int num_clks) +{ + int i, ret; + struct clk *clk; + struct iproc_pll *pll; + struct iproc_clk *iclk; + struct clk_init_data init; + const char *parent_name; + + if (WARN_ON(!pll_ctrl) || WARN_ON(!clk_ctrl)) + return; + + pll = kzalloc(sizeof(*pll), GFP_KERNEL); + if (WARN_ON(!pll)) + return; + + pll->clk_data.clk_num = num_clks; + pll->clk_data.clks = kcalloc(num_clks, sizeof(*pll->clk_data.clks), + GFP_KERNEL); + if (WARN_ON(!pll->clk_data.clks)) + goto err_clk_data; + + pll->clks = kcalloc(num_clks, sizeof(*pll->clks), GFP_KERNEL); + if (WARN_ON(!pll->clks)) + goto err_clks; + + pll->pll_base = of_iomap(node, 0); + if (WARN_ON(!pll->pll_base)) + goto err_pll_iomap; + + pll->pwr_base = of_iomap(node, 1); + if (WARN_ON(!pll->pwr_base)) + goto err_pwr_iomap; + + /* some PLLs require gating control at the top ASIU level */ + if (pll_ctrl->flags & IPROC_CLK_PLL_ASIU) { + pll->asiu_base = of_iomap(node, 2); + if (WARN_ON(!pll->asiu_base)) + goto err_asiu_iomap; + } + + /* initialize and register the PLL itself */ + pll->ctrl = pll_ctrl; + + iclk = &pll->clks[0]; + iclk->pll = pll; + iclk->name = node->name; + + init.name = node->name; + init.ops = &iproc_pll_ops; + init.flags = 0; + parent_name = of_clk_get_parent_name(node, 0); + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + iclk->hw.init = &init; + + if (vco) { + pll->num_vco_entries = num_vco_entries; + pll->vco_param = vco; + } + + iproc_pll_sw_cfg(pll); + + clk = clk_register(NULL, &iclk->hw); + if (WARN_ON(IS_ERR(clk))) + goto err_pll_register; + + pll->clk_data.clks[0] = clk; + + /* now initialize and register all leaf clocks */ + for (i = 1; i < num_clks; i++) { + const char *clk_name; + + memset(&init, 0, sizeof(init)); + parent_name = node->name; + + clk_name = kzalloc(IPROC_CLK_NAME_LEN, GFP_KERNEL); + if (WARN_ON(!clk_name)) + goto err_clk_register; + + ret = of_property_read_string_index(node, "clock-output-names", + i, &clk_name); + if (WARN_ON(ret)) + goto err_clk_register; + + iclk = &pll->clks[i]; + iclk->name = clk_name; + iclk->pll = pll; + iclk->ctrl = &clk_ctrl[i]; + + init.name = clk_name; + init.ops = &iproc_clk_ops; + init.flags = 0; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + iclk->hw.init = &init; + + clk = clk_register(NULL, &iclk->hw); + if (WARN_ON(IS_ERR(clk))) + goto err_clk_register; + + pll->clk_data.clks[i] = clk; + } + + ret = of_clk_add_provider(node, of_clk_src_onecell_get, &pll->clk_data); + if (WARN_ON(ret)) + goto err_clk_register; + + return; + +err_clk_register: + for (i = 0; i < num_clks; i++) { + kfree(pll->clks[i].name); + clk_unregister(pll->clk_data.clks[i]); + } + +err_pll_register: + if (pll->asiu_base) + iounmap(pll->asiu_base); + +err_asiu_iomap: + iounmap(pll->pwr_base); + +err_pwr_iomap: + iounmap(pll->pll_base); + +err_pll_iomap: + kfree(pll->clks); + +err_clks: + kfree(pll->clk_data.clks); + +err_clk_data: + kfree(pll); +} diff --git a/drivers/clk/bcm/clk-iproc.h b/drivers/clk/bcm/clk-iproc.h new file mode 100644 index 000000000000..d834b7abd5c6 --- /dev/null +++ b/drivers/clk/bcm/clk-iproc.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _CLK_IPROC_H +#define _CLK_IPROC_H + +#include +#include +#include +#include +#include +#include +#include + +#define IPROC_CLK_NAME_LEN 25 +#define IPROC_CLK_INVALID_OFFSET 0xffffffff +#define bit_mask(width) ((1 << (width)) - 1) + +/* clocks that should not be disabled at runtime */ +#define IPROC_CLK_AON BIT(0) + +/* PLL that requires gating through ASIU */ +#define IPROC_CLK_PLL_ASIU BIT(1) + +/* PLL that has fractional part of the NDIV */ +#define IPROC_CLK_PLL_HAS_NDIV_FRAC BIT(2) + +/* + * Some of the iProc PLL/clocks may have an ASIC bug that requires read back + * of the same register following the write to flush the write transaction into + * the intended register + */ +#define IPROC_CLK_NEEDS_READ_BACK BIT(3) + +/* + * Some PLLs require the PLL SW override bit to be set before changes can be + * applied to the PLL + */ +#define IPROC_CLK_PLL_NEEDS_SW_CFG BIT(4) + +/* + * Parameters for VCO frequency configuration + * + * VCO frequency = + * ((ndiv_int + ndiv_frac / 2^20) * (ref freqeuncy / pdiv) + */ +struct iproc_pll_vco_param { + unsigned long rate; + unsigned int ndiv_int; + unsigned int ndiv_frac; + unsigned int pdiv; +}; + +struct iproc_clk_reg_op { + unsigned int offset; + unsigned int shift; + unsigned int width; +}; + +/* + * Clock gating control at the top ASIU level + */ +struct iproc_asiu_gate { + unsigned int offset; + unsigned int en_shift; +}; + +/* + * Control of powering on/off of a PLL + * + * Before powering off a PLL, input isolation (ISO) needs to be enabled + */ +struct iproc_pll_aon_pwr_ctrl { + unsigned int offset; + unsigned int pwr_width; + unsigned int pwr_shift; + unsigned int iso_shift; +}; + +/* + * Control of the PLL reset, with Ki, Kp, and Ka parameters + */ +struct iproc_pll_reset_ctrl { + unsigned int offset; + unsigned int reset_shift; + unsigned int p_reset_shift; + unsigned int ki_shift; + unsigned int ki_width; + unsigned int kp_shift; + unsigned int kp_width; + unsigned int ka_shift; + unsigned int ka_width; +}; + +/* + * To enable SW control of the PLL + */ +struct iproc_pll_sw_ctrl { + unsigned int offset; + unsigned int shift; +}; + +struct iproc_pll_vco_ctrl { + unsigned int u_offset; + unsigned int l_offset; +}; + +/* + * Main PLL control parameters + */ +struct iproc_pll_ctrl { + unsigned long flags; + struct iproc_pll_aon_pwr_ctrl aon; + struct iproc_asiu_gate asiu; + struct iproc_pll_reset_ctrl reset; + struct iproc_pll_sw_ctrl sw_ctrl; + struct iproc_clk_reg_op ndiv_int; + struct iproc_clk_reg_op ndiv_frac; + struct iproc_clk_reg_op pdiv; + struct iproc_pll_vco_ctrl vco_ctrl; + struct iproc_clk_reg_op status; +}; + +/* + * Controls enabling/disabling a PLL derived clock + */ +struct iproc_clk_enable_ctrl { + unsigned int offset; + unsigned int enable_shift; + unsigned int hold_shift; + unsigned int bypass_shift; +}; + +/* + * Main clock control parameters for clocks derived from the PLLs + */ +struct iproc_clk_ctrl { + unsigned int channel; + unsigned long flags; + struct iproc_clk_enable_ctrl enable; + struct iproc_clk_reg_op mdiv; +}; + +/* + * Divisor of the ASIU clocks + */ +struct iproc_asiu_div { + unsigned int offset; + unsigned int en_shift; + unsigned int high_shift; + unsigned int high_width; + unsigned int low_shift; + unsigned int low_width; +}; + +void __init iproc_armpll_setup(struct device_node *node); +void __init iproc_pll_clk_setup(struct device_node *node, + const struct iproc_pll_ctrl *pll_ctrl, + const struct iproc_pll_vco_param *vco, + unsigned int num_vco_entries, + const struct iproc_clk_ctrl *clk_ctrl, + unsigned int num_clks); +void __init iproc_asiu_setup(struct device_node *node, + const struct iproc_asiu_div *div, + const struct iproc_asiu_gate *gate, + unsigned int num_clks); + +#endif /* _CLK_IPROC_H */ -- cgit v1.2.3-58-ga151 From 69a0b2c559099a043662f8c96214d8eab32a7153 Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Tue, 5 May 2015 11:13:20 -0700 Subject: clk: Change bcm clocks build dependency The clock code under drivers/clk/bcm now contains code for both the Broadcom mobile SoCs and the iProc SoCs. Change the the makefile dependency to be under config flag CONFIG_ARCH_BCM that's enabled for both families of SoCs Signed-off-by: Ray Jui Signed-off-by: Michael Turquette --- drivers/clk/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 3233f0e8bf43..d4bc8ed29984 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -46,7 +46,7 @@ obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o obj-$(CONFIG_COMMON_CLK_AT91) += at91/ -obj-$(CONFIG_ARCH_BCM_MOBILE) += bcm/ +obj-$(CONFIG_ARCH_BCM) += bcm/ obj-$(CONFIG_ARCH_BERLIN) += berlin/ obj-$(CONFIG_ARCH_HISI) += hisilicon/ obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/ -- cgit v1.2.3-58-ga151 From 61ca7b0c7fffb968bd16394daf05b7e888e9541e Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Tue, 5 May 2015 11:13:21 -0700 Subject: clk: cygnus: add clock support for Broadcom Cygnus The Broadcom Cygnus SoC is architected under the iProc architecture. It has the following PLLs: ARMPLL, GENPLL, LCPLL0, MIPIPLL, all dervied from an onboard crystal. Cygnus also has various ASIU clocks that are derived directly from the onboard crystal. Signed-off-by: Ray Jui Reviewed-by: Scott Branden Signed-off-by: Michael Turquette --- drivers/clk/bcm/Makefile | 1 + drivers/clk/bcm/clk-cygnus.c | 265 +++++++++++++++++++++++++++++++++ include/dt-bindings/clock/bcm-cygnus.h | 68 +++++++++ 3 files changed, 334 insertions(+) create mode 100644 drivers/clk/bcm/clk-cygnus.c create mode 100644 include/dt-bindings/clock/bcm-cygnus.h diff --git a/drivers/clk/bcm/Makefile b/drivers/clk/bcm/Makefile index 0facbbc5652c..8a7a477862c7 100644 --- a/drivers/clk/bcm/Makefile +++ b/drivers/clk/bcm/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_CLK_BCM_KONA) += clk-kona-setup.o obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm281xx.o obj-$(CONFIG_CLK_BCM_KONA) += clk-bcm21664.o obj-$(CONFIG_COMMON_CLK_IPROC) += clk-iproc-armpll.o clk-iproc-pll.o clk-iproc-asiu.o +obj-$(CONFIG_ARCH_BCM_CYGNUS) += clk-cygnus.o diff --git a/drivers/clk/bcm/clk-cygnus.c b/drivers/clk/bcm/clk-cygnus.c new file mode 100644 index 000000000000..316c60337661 --- /dev/null +++ b/drivers/clk/bcm/clk-cygnus.c @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "clk-iproc.h" + +#define reg_val(o, s, w) { .offset = o, .shift = s, .width = w, } + +#define aon_val(o, pw, ps, is) { .offset = o, .pwr_width = pw, \ + .pwr_shift = ps, .iso_shift = is } + +#define sw_ctrl_val(o, s) { .offset = o, .shift = s, } + +#define asiu_div_val(o, es, hs, hw, ls, lw) \ + { .offset = o, .en_shift = es, .high_shift = hs, \ + .high_width = hw, .low_shift = ls, .low_width = lw } + +#define reset_val(o, rs, prs, kis, kiw, kps, kpw, kas, kaw) { .offset = o, \ + .reset_shift = rs, .p_reset_shift = prs, .ki_shift = kis, \ + .ki_width = kiw, .kp_shift = kps, .kp_width = kpw, .ka_shift = kas, \ + .ka_width = kaw } + +#define vco_ctrl_val(uo, lo) { .u_offset = uo, .l_offset = lo } + +#define enable_val(o, es, hs, bs) { .offset = o, .enable_shift = es, \ + .hold_shift = hs, .bypass_shift = bs } + +#define asiu_gate_val(o, es) { .offset = o, .en_shift = es } + +static void __init cygnus_armpll_init(struct device_node *node) +{ + iproc_armpll_setup(node); +} +CLK_OF_DECLARE(cygnus_armpll, "brcm,cygnus-armpll", cygnus_armpll_init); + +static const struct iproc_pll_ctrl genpll = { + .flags = IPROC_CLK_AON | IPROC_CLK_PLL_HAS_NDIV_FRAC | + IPROC_CLK_PLL_NEEDS_SW_CFG, + .aon = aon_val(0x0, 2, 1, 0), + .reset = reset_val(0x0, 11, 10, 4, 3, 0, 4, 7, 3), + .sw_ctrl = sw_ctrl_val(0x10, 31), + .ndiv_int = reg_val(0x10, 20, 10), + .ndiv_frac = reg_val(0x10, 0, 20), + .pdiv = reg_val(0x14, 0, 4), + .vco_ctrl = vco_ctrl_val(0x18, 0x1c), + .status = reg_val(0x28, 12, 1), +}; + +static const struct iproc_clk_ctrl genpll_clk[] = { + [BCM_CYGNUS_GENPLL_AXI21_CLK] = { + .channel = BCM_CYGNUS_GENPLL_AXI21_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 6, 0, 12), + .mdiv = reg_val(0x20, 0, 8), + }, + [BCM_CYGNUS_GENPLL_250MHZ_CLK] = { + .channel = BCM_CYGNUS_GENPLL_250MHZ_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 7, 1, 13), + .mdiv = reg_val(0x20, 10, 8), + }, + [BCM_CYGNUS_GENPLL_IHOST_SYS_CLK] = { + .channel = BCM_CYGNUS_GENPLL_IHOST_SYS_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 8, 2, 14), + .mdiv = reg_val(0x20, 20, 8), + }, + [BCM_CYGNUS_GENPLL_ENET_SW_CLK] = { + .channel = BCM_CYGNUS_GENPLL_ENET_SW_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 9, 3, 15), + .mdiv = reg_val(0x24, 0, 8), + }, + [BCM_CYGNUS_GENPLL_AUDIO_125_CLK] = { + .channel = BCM_CYGNUS_GENPLL_AUDIO_125_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 10, 4, 16), + .mdiv = reg_val(0x24, 10, 8), + }, + [BCM_CYGNUS_GENPLL_CAN_CLK] = { + .channel = BCM_CYGNUS_GENPLL_CAN_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x4, 11, 5, 17), + .mdiv = reg_val(0x24, 20, 8), + }, +}; + +static void __init cygnus_genpll_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &genpll, NULL, 0, genpll_clk, + ARRAY_SIZE(genpll_clk)); +} +CLK_OF_DECLARE(cygnus_genpll, "brcm,cygnus-genpll", cygnus_genpll_clk_init); + +static const struct iproc_pll_ctrl lcpll0 = { + .flags = IPROC_CLK_AON | IPROC_CLK_PLL_NEEDS_SW_CFG, + .aon = aon_val(0x0, 2, 5, 4), + .reset = reset_val(0x0, 31, 30, 27, 3, 23, 4, 19, 4), + .sw_ctrl = sw_ctrl_val(0x4, 31), + .ndiv_int = reg_val(0x4, 16, 10), + .pdiv = reg_val(0x4, 26, 4), + .vco_ctrl = vco_ctrl_val(0x10, 0x14), + .status = reg_val(0x18, 12, 1), +}; + +static const struct iproc_clk_ctrl lcpll0_clk[] = { + [BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK] = { + .channel = BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 7, 1, 13), + .mdiv = reg_val(0x8, 0, 8), + }, + [BCM_CYGNUS_LCPLL0_DDR_PHY_CLK] = { + .channel = BCM_CYGNUS_LCPLL0_DDR_PHY_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 8, 2, 14), + .mdiv = reg_val(0x8, 10, 8), + }, + [BCM_CYGNUS_LCPLL0_SDIO_CLK] = { + .channel = BCM_CYGNUS_LCPLL0_SDIO_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 9, 3, 15), + .mdiv = reg_val(0x8, 20, 8), + }, + [BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK] = { + .channel = BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 10, 4, 16), + .mdiv = reg_val(0xc, 0, 8), + }, + [BCM_CYGNUS_LCPLL0_SMART_CARD_CLK] = { + .channel = BCM_CYGNUS_LCPLL0_SMART_CARD_CLK, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 11, 5, 17), + .mdiv = reg_val(0xc, 10, 8), + }, + [BCM_CYGNUS_LCPLL0_CH5_UNUSED] = { + .channel = BCM_CYGNUS_LCPLL0_CH5_UNUSED, + .flags = IPROC_CLK_AON, + .enable = enable_val(0x0, 12, 6, 18), + .mdiv = reg_val(0xc, 20, 8), + }, +}; + +static void __init cygnus_lcpll0_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &lcpll0, NULL, 0, lcpll0_clk, + ARRAY_SIZE(lcpll0_clk)); +} +CLK_OF_DECLARE(cygnus_lcpll0, "brcm,cygnus-lcpll0", cygnus_lcpll0_clk_init); + +/* + * MIPI PLL VCO frequency parameter table + */ +static const struct iproc_pll_vco_param mipipll_vco_params[] = { + /* rate (Hz) ndiv_int ndiv_frac pdiv */ + { 750000000UL, 30, 0, 1 }, + { 1000000000UL, 40, 0, 1 }, + { 1350000000ul, 54, 0, 1 }, + { 2000000000UL, 80, 0, 1 }, + { 2100000000UL, 84, 0, 1 }, + { 2250000000UL, 90, 0, 1 }, + { 2500000000UL, 100, 0, 1 }, + { 2700000000UL, 54, 0, 0 }, + { 2975000000UL, 119, 0, 1 }, + { 3100000000UL, 124, 0, 1 }, + { 3150000000UL, 126, 0, 1 }, +}; + +static const struct iproc_pll_ctrl mipipll = { + .flags = IPROC_CLK_PLL_ASIU | IPROC_CLK_PLL_HAS_NDIV_FRAC | + IPROC_CLK_NEEDS_READ_BACK, + .aon = aon_val(0x0, 4, 17, 16), + .asiu = asiu_gate_val(0x0, 3), + .reset = reset_val(0x0, 11, 10, 4, 3, 0, 4, 7, 4), + .ndiv_int = reg_val(0x10, 20, 10), + .ndiv_frac = reg_val(0x10, 0, 20), + .pdiv = reg_val(0x14, 0, 4), + .vco_ctrl = vco_ctrl_val(0x18, 0x1c), + .status = reg_val(0x28, 12, 1), +}; + +static const struct iproc_clk_ctrl mipipll_clk[] = { + [BCM_CYGNUS_MIPIPLL_CH0_UNUSED] = { + .channel = BCM_CYGNUS_MIPIPLL_CH0_UNUSED, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 12, 6, 18), + .mdiv = reg_val(0x20, 0, 8), + }, + [BCM_CYGNUS_MIPIPLL_CH1_LCD] = { + .channel = BCM_CYGNUS_MIPIPLL_CH1_LCD, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 13, 7, 19), + .mdiv = reg_val(0x20, 10, 8), + }, + [BCM_CYGNUS_MIPIPLL_CH2_V3D] = { + .channel = BCM_CYGNUS_MIPIPLL_CH2_V3D, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 14, 8, 20), + .mdiv = reg_val(0x20, 20, 8), + }, + [BCM_CYGNUS_MIPIPLL_CH3_UNUSED] = { + .channel = BCM_CYGNUS_MIPIPLL_CH3_UNUSED, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 15, 9, 21), + .mdiv = reg_val(0x24, 0, 8), + }, + [BCM_CYGNUS_MIPIPLL_CH4_UNUSED] = { + .channel = BCM_CYGNUS_MIPIPLL_CH4_UNUSED, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 16, 10, 22), + .mdiv = reg_val(0x24, 10, 8), + }, + [BCM_CYGNUS_MIPIPLL_CH5_UNUSED] = { + .channel = BCM_CYGNUS_MIPIPLL_CH5_UNUSED, + .flags = IPROC_CLK_NEEDS_READ_BACK, + .enable = enable_val(0x4, 17, 11, 23), + .mdiv = reg_val(0x24, 20, 8), + }, +}; + +static void __init cygnus_mipipll_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &mipipll, mipipll_vco_params, + ARRAY_SIZE(mipipll_vco_params), mipipll_clk, + ARRAY_SIZE(mipipll_clk)); +} +CLK_OF_DECLARE(cygnus_mipipll, "brcm,cygnus-mipipll", cygnus_mipipll_clk_init); + +static const struct iproc_asiu_div asiu_div[] = { + [BCM_CYGNUS_ASIU_KEYPAD_CLK] = asiu_div_val(0x0, 31, 16, 10, 0, 10), + [BCM_CYGNUS_ASIU_ADC_CLK] = asiu_div_val(0x4, 31, 16, 10, 0, 10), + [BCM_CYGNUS_ASIU_PWM_CLK] = asiu_div_val(0x8, 31, 16, 10, 0, 10), +}; + +static const struct iproc_asiu_gate asiu_gate[] = { + [BCM_CYGNUS_ASIU_KEYPAD_CLK] = asiu_gate_val(0x0, 7), + [BCM_CYGNUS_ASIU_ADC_CLK] = asiu_gate_val(0x0, 9), + [BCM_CYGNUS_ASIU_PWM_CLK] = asiu_gate_val(IPROC_CLK_INVALID_OFFSET, 0), +}; + +static void __init cygnus_asiu_init(struct device_node *node) +{ + iproc_asiu_setup(node, asiu_div, asiu_gate, ARRAY_SIZE(asiu_div)); +} +CLK_OF_DECLARE(cygnus_asiu_clk, "brcm,cygnus-asiu-clk", cygnus_asiu_init); diff --git a/include/dt-bindings/clock/bcm-cygnus.h b/include/dt-bindings/clock/bcm-cygnus.h new file mode 100644 index 000000000000..32fbc475087a --- /dev/null +++ b/include/dt-bindings/clock/bcm-cygnus.h @@ -0,0 +1,68 @@ +/* + * BSD LICENSE + * + * Copyright(c) 2014 Broadcom Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Broadcom Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CLOCK_BCM_CYGNUS_H +#define _CLOCK_BCM_CYGNUS_H + +/* GENPLL clock ID */ +#define BCM_CYGNUS_GENPLL 0 +#define BCM_CYGNUS_GENPLL_AXI21_CLK 1 +#define BCM_CYGNUS_GENPLL_250MHZ_CLK 2 +#define BCM_CYGNUS_GENPLL_IHOST_SYS_CLK 3 +#define BCM_CYGNUS_GENPLL_ENET_SW_CLK 4 +#define BCM_CYGNUS_GENPLL_AUDIO_125_CLK 5 +#define BCM_CYGNUS_GENPLL_CAN_CLK 6 + +/* LCPLL0 clock ID */ +#define BCM_CYGNUS_LCPLL0 0 +#define BCM_CYGNUS_LCPLL0_PCIE_PHY_REF_CLK 1 +#define BCM_CYGNUS_LCPLL0_DDR_PHY_CLK 2 +#define BCM_CYGNUS_LCPLL0_SDIO_CLK 3 +#define BCM_CYGNUS_LCPLL0_USB_PHY_REF_CLK 4 +#define BCM_CYGNUS_LCPLL0_SMART_CARD_CLK 5 +#define BCM_CYGNUS_LCPLL0_CH5_UNUSED 6 + +/* MIPI PLL clock ID */ +#define BCM_CYGNUS_MIPIPLL 0 +#define BCM_CYGNUS_MIPIPLL_CH0_UNUSED 1 +#define BCM_CYGNUS_MIPIPLL_CH1_LCD 2 +#define BCM_CYGNUS_MIPIPLL_CH2_V3D 3 +#define BCM_CYGNUS_MIPIPLL_CH3_UNUSED 4 +#define BCM_CYGNUS_MIPIPLL_CH4_UNUSED 5 +#define BCM_CYGNUS_MIPIPLL_CH5_UNUSED 6 + +/* ASIU clock ID */ +#define BCM_CYGNUS_ASIU_KEYPAD_CLK 0 +#define BCM_CYGNUS_ASIU_ADC_CLK 1 +#define BCM_CYGNUS_ASIU_PWM_CLK 2 + +#endif /* _CLOCK_BCM_CYGNUS_H */ -- cgit v1.2.3-58-ga151 From d0b30c983f0e3793193a96697e59cfaaba594501 Mon Sep 17 00:00:00 2001 From: Ray Jui Date: Tue, 5 May 2015 11:13:23 -0700 Subject: clk: cygnus: remove Cygnus dummy clock binding Remove old Cygnus dummy clock binding document, as it's replaced by Documentation/devicetree/bindings/clock/brcm,iproc-clocks.txt Signed-off-by: Ray Jui Signed-off-by: Michael Turquette --- .../devicetree/bindings/clock/bcm-cygnus-clock.txt | 34 ---------------------- 1 file changed, 34 deletions(-) delete mode 100644 Documentation/devicetree/bindings/clock/bcm-cygnus-clock.txt diff --git a/Documentation/devicetree/bindings/clock/bcm-cygnus-clock.txt b/Documentation/devicetree/bindings/clock/bcm-cygnus-clock.txt deleted file mode 100644 index 00d26edec8bc..000000000000 --- a/Documentation/devicetree/bindings/clock/bcm-cygnus-clock.txt +++ /dev/null @@ -1,34 +0,0 @@ -Broadcom Cygnus Clocks - -This binding uses the common clock binding: -Documentation/devicetree/bindings/clock/clock-bindings.txt - -Currently various "fixed" clocks are declared for peripheral drivers that use -the common clock framework to reference their core clocks. Proper support of -these clocks will be added later - -Device tree example: - - clocks { - #address-cells = <1>; - #size-cells = <1>; - ranges; - - osc: oscillator { - compatible = "fixed-clock"; - #clock-cells = <1>; - clock-frequency = <25000000>; - }; - - apb_clk: apb_clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <1000000000>; - }; - - periph_clk: periph_clk { - compatible = "fixed-clock"; - #clock-cells = <0>; - clock-frequency = <500000000>; - }; - }; -- cgit v1.2.3-58-ga151 From dc627eea98b1e12f9ff2ecd241a34a2056fdddbd Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Tue, 16 Jun 2015 17:56:19 +0200 Subject: clk: mvebu: flag the crypto clk as CLK_IGNORE_UNUSED The crypto SRAM, used by the armada 370 cpuidle code to workaround a bug in the BootROM code, requires the crypto clk to be up and running. Flag the crypto clk as IGNORE_UNUSED until we add the proper infrastructure to define the crypto SRAM in the DT and reference the crypto clk in this SRAM node. Reported-by: Kevin Hilman Signed-off-by: Boris Brezillon Tested-by: Kevin Hilman Signed-off-by: Michael Turquette --- drivers/clk/mvebu/armada-370.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/mvebu/armada-370.c b/drivers/clk/mvebu/armada-370.c index c19fd77e6c27..2c7c1085f883 100644 --- a/drivers/clk/mvebu/armada-370.c +++ b/drivers/clk/mvebu/armada-370.c @@ -163,7 +163,7 @@ static const struct clk_gating_soc_desc a370_gating_desc[] __initconst = { { "pex1", "pex1_en", 9, 0 }, { "sata0", NULL, 15, 0 }, { "sdio", NULL, 17, 0 }, - { "crypto", NULL, 23, 0 }, + { "crypto", NULL, 23, CLK_IGNORE_UNUSED }, { "tdm", NULL, 25, 0 }, { "ddr", NULL, 28, CLK_IGNORE_UNUSED }, { "sata1", NULL, 30, 0 }, -- cgit v1.2.3-58-ga151 From 02fdfd708fd252a778709beb6c65d5e7360341ac Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Fri, 29 May 2015 12:04:12 -0400 Subject: clk: keystone: add support for post divider register for main pll Main PLL controller has post divider bits in a separate register in pll controller. Use the value from this register instead of fixed divider when available. Signed-off-by: Murali Karicheri Signed-off-by: Michael Turquette --- .../devicetree/bindings/clock/keystone-pll.txt | 8 ++++---- drivers/clk/keystone/pll.c | 20 ++++++++++++++++++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/keystone-pll.txt b/Documentation/devicetree/bindings/clock/keystone-pll.txt index 225990f79b7c..47570d207215 100644 --- a/Documentation/devicetree/bindings/clock/keystone-pll.txt +++ b/Documentation/devicetree/bindings/clock/keystone-pll.txt @@ -15,8 +15,8 @@ Required properties: - compatible : shall be "ti,keystone,main-pll-clock" or "ti,keystone,pll-clock" - clocks : parent clock phandle - reg - pll control0 and pll multipler registers -- reg-names : control and multiplier. The multiplier is applicable only for - main pll clock +- reg-names : control, multiplier and post-divider. The multiplier and + post-divider registers are applicable only for main pll clock - fixed-postdiv : fixed post divider value. If absent, use clkod register bits for postdiv @@ -25,8 +25,8 @@ Example: #clock-cells = <0>; compatible = "ti,keystone,main-pll-clock"; clocks = <&refclksys>; - reg = <0x02620350 4>, <0x02310110 4>; - reg-names = "control", "multiplier"; + reg = <0x02620350 4>, <0x02310110 4>, <0x02310108 4>; + reg-names = "control", "multiplier", "post-divider"; fixed-postdiv = <2>; }; diff --git a/drivers/clk/keystone/pll.c b/drivers/clk/keystone/pll.c index 0dd8a4b12747..4a375ead70e9 100644 --- a/drivers/clk/keystone/pll.c +++ b/drivers/clk/keystone/pll.c @@ -37,7 +37,8 @@ * Main PLL or any other PLLs in the device such as ARM PLL, DDR PLL * or PA PLL available on keystone2. These PLLs are controlled by * this register. Main PLL is controlled by a PLL controller. - * @pllm: PLL register map address + * @pllm: PLL register map address for multiplier bits + * @pllod: PLL register map address for post divider bits * @pll_ctl0: PLL controller map address * @pllm_lower_mask: multiplier lower mask * @pllm_upper_mask: multiplier upper mask @@ -53,6 +54,7 @@ struct clk_pll_data { u32 phy_pllm; u32 phy_pll_ctl0; void __iomem *pllm; + void __iomem *pllod; void __iomem *pll_ctl0; u32 pllm_lower_mask; u32 pllm_upper_mask; @@ -102,7 +104,11 @@ static unsigned long clk_pllclk_recalc(struct clk_hw *hw, /* read post divider from od bits*/ postdiv = ((val & pll_data->clkod_mask) >> pll_data->clkod_shift) + 1; - else + else if (pll_data->pllod) { + postdiv = readl(pll_data->pllod); + postdiv = ((postdiv & pll_data->clkod_mask) >> + pll_data->clkod_shift) + 1; + } else postdiv = pll_data->postdiv; rate /= (prediv + 1); @@ -172,12 +178,21 @@ static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl) /* assume the PLL has output divider register bits */ pll_data->clkod_mask = CLKOD_MASK; pll_data->clkod_shift = CLKOD_SHIFT; + + /* + * Check if there is an post-divider register. If not + * assume od bits are part of control register. + */ + i = of_property_match_string(node, "reg-names", + "post-divider"); + pll_data->pllod = of_iomap(node, i); } i = of_property_match_string(node, "reg-names", "control"); pll_data->pll_ctl0 = of_iomap(node, i); if (!pll_data->pll_ctl0) { pr_err("%s: ioremap failed\n", __func__); + iounmap(pll_data->pllod); goto out; } @@ -193,6 +208,7 @@ static void __init _of_pll_clk_init(struct device_node *node, bool pllctrl) pll_data->pllm = of_iomap(node, i); if (!pll_data->pllm) { iounmap(pll_data->pll_ctl0); + iounmap(pll_data->pllod); goto out; } } -- cgit v1.2.3-58-ga151 From b04e0b8fd5443b61c04e6103f16c5d1e1821e295 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 28 May 2015 22:31:43 +0200 Subject: clk: add lpc18xx cgu clk driver Add driver for NXP LPC18xx/43xx Clock Generation Unit (CGU). The CGU contains several clock generators and output stages that route the clocks either directly to peripherals or to a Clock Control Unit (CCU). Signed-off-by: Joachim Eastwood Signed-off-by: Michael Turquette --- drivers/clk/Makefile | 1 + drivers/clk/nxp/Makefile | 1 + drivers/clk/nxp/clk-lpc18xx-cgu.c | 635 ++++++++++++++++++++++++++++++++ include/dt-bindings/clock/lpc18xx-cgu.h | 41 +++ 4 files changed, 678 insertions(+) create mode 100644 drivers/clk/nxp/Makefile create mode 100644 drivers/clk/nxp/clk-lpc18xx-cgu.c create mode 100644 include/dt-bindings/clock/lpc18xx-cgu.h diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d4bc8ed29984..256977ed050a 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -57,6 +57,7 @@ endif obj-$(CONFIG_PLAT_ORION) += mvebu/ obj-$(CONFIG_ARCH_MESON) += meson/ obj-$(CONFIG_ARCH_MXS) += mxs/ +obj-$(CONFIG_ARCH_LPC18XX) += nxp/ obj-$(CONFIG_MACH_PISTACHIO) += pistachio/ obj-$(CONFIG_COMMON_CLK_PXA) += pxa/ obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/ diff --git a/drivers/clk/nxp/Makefile b/drivers/clk/nxp/Makefile new file mode 100644 index 000000000000..aca9d3e4f7fe --- /dev/null +++ b/drivers/clk/nxp/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ARCH_LPC18XX) += clk-lpc18xx-cgu.o diff --git a/drivers/clk/nxp/clk-lpc18xx-cgu.c b/drivers/clk/nxp/clk-lpc18xx-cgu.c new file mode 100644 index 000000000000..81e9e1c788f4 --- /dev/null +++ b/drivers/clk/nxp/clk-lpc18xx-cgu.c @@ -0,0 +1,635 @@ +/* + * Clk driver for NXP LPC18xx/LPC43xx Clock Generation Unit (CGU) + * + * Copyright (C) 2015 Joachim Eastwood + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include + +#include + +/* Clock Generation Unit (CGU) registers */ +#define LPC18XX_CGU_XTAL_OSC_CTRL 0x018 +#define LPC18XX_CGU_PLL0USB_STAT 0x01c +#define LPC18XX_CGU_PLL0USB_CTRL 0x020 +#define LPC18XX_CGU_PLL0USB_MDIV 0x024 +#define LPC18XX_CGU_PLL0USB_NP_DIV 0x028 +#define LPC18XX_CGU_PLL0AUDIO_STAT 0x02c +#define LPC18XX_CGU_PLL0AUDIO_CTRL 0x030 +#define LPC18XX_CGU_PLL0AUDIO_MDIV 0x034 +#define LPC18XX_CGU_PLL0AUDIO_NP_DIV 0x038 +#define LPC18XX_CGU_PLL0AUDIO_FRAC 0x03c +#define LPC18XX_CGU_PLL1_STAT 0x040 +#define LPC18XX_CGU_PLL1_CTRL 0x044 +#define LPC18XX_PLL1_CTRL_FBSEL BIT(6) +#define LPC18XX_PLL1_CTRL_DIRECT BIT(7) +#define LPC18XX_CGU_IDIV_CTRL(n) (0x048 + (n) * sizeof(u32)) +#define LPC18XX_CGU_BASE_CLK(id) (0x05c + (id) * sizeof(u32)) +#define LPC18XX_CGU_PLL_CTRL_OFFSET 0x4 + +/* PLL0 bits common to both audio and USB PLL */ +#define LPC18XX_PLL0_STAT_LOCK BIT(0) +#define LPC18XX_PLL0_CTRL_PD BIT(0) +#define LPC18XX_PLL0_CTRL_BYPASS BIT(1) +#define LPC18XX_PLL0_CTRL_DIRECTI BIT(2) +#define LPC18XX_PLL0_CTRL_DIRECTO BIT(3) +#define LPC18XX_PLL0_CTRL_CLKEN BIT(4) +#define LPC18XX_PLL0_MDIV_MDEC_MASK 0x1ffff +#define LPC18XX_PLL0_MDIV_SELP_SHIFT 17 +#define LPC18XX_PLL0_MDIV_SELI_SHIFT 22 +#define LPC18XX_PLL0_MSEL_MAX BIT(15) + +/* Register value that gives PLL0 post/pre dividers equal to 1 */ +#define LPC18XX_PLL0_NP_DIVS_1 0x00302062 + +enum { + CLK_SRC_OSC32, + CLK_SRC_IRC, + CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, + CLK_SRC_GP_CLKIN, + CLK_SRC_RESERVED1, + CLK_SRC_OSC, + CLK_SRC_PLL0USB, + CLK_SRC_PLL0AUDIO, + CLK_SRC_PLL1, + CLK_SRC_RESERVED2, + CLK_SRC_RESERVED3, + CLK_SRC_IDIVA, + CLK_SRC_IDIVB, + CLK_SRC_IDIVC, + CLK_SRC_IDIVD, + CLK_SRC_IDIVE, + CLK_SRC_MAX +}; + +static const char *clk_src_names[CLK_SRC_MAX] = { + [CLK_SRC_OSC32] = "osc32", + [CLK_SRC_IRC] = "irc", + [CLK_SRC_ENET_RX_CLK] = "enet_rx_clk", + [CLK_SRC_ENET_TX_CLK] = "enet_tx_clk", + [CLK_SRC_GP_CLKIN] = "gp_clkin", + [CLK_SRC_OSC] = "osc", + [CLK_SRC_PLL0USB] = "pll0usb", + [CLK_SRC_PLL0AUDIO] = "pll0audio", + [CLK_SRC_PLL1] = "pll1", + [CLK_SRC_IDIVA] = "idiva", + [CLK_SRC_IDIVB] = "idivb", + [CLK_SRC_IDIVC] = "idivc", + [CLK_SRC_IDIVD] = "idivd", + [CLK_SRC_IDIVE] = "idive", +}; + +static const char *clk_base_names[BASE_CLK_MAX] = { + [BASE_SAFE_CLK] = "base_safe_clk", + [BASE_USB0_CLK] = "base_usb0_clk", + [BASE_PERIPH_CLK] = "base_periph_clk", + [BASE_USB1_CLK] = "base_usb1_clk", + [BASE_CPU_CLK] = "base_cpu_clk", + [BASE_SPIFI_CLK] = "base_spifi_clk", + [BASE_SPI_CLK] = "base_spi_clk", + [BASE_PHY_RX_CLK] = "base_phy_rx_clk", + [BASE_PHY_TX_CLK] = "base_phy_tx_clk", + [BASE_APB1_CLK] = "base_apb1_clk", + [BASE_APB3_CLK] = "base_apb3_clk", + [BASE_LCD_CLK] = "base_lcd_clk", + [BASE_ADCHS_CLK] = "base_adchs_clk", + [BASE_SDIO_CLK] = "base_sdio_clk", + [BASE_SSP0_CLK] = "base_ssp0_clk", + [BASE_SSP1_CLK] = "base_ssp1_clk", + [BASE_UART0_CLK] = "base_uart0_clk", + [BASE_UART1_CLK] = "base_uart1_clk", + [BASE_UART2_CLK] = "base_uart2_clk", + [BASE_UART3_CLK] = "base_uart3_clk", + [BASE_OUT_CLK] = "base_out_clk", + [BASE_AUDIO_CLK] = "base_audio_clk", + [BASE_CGU_OUT0_CLK] = "base_cgu_out0_clk", + [BASE_CGU_OUT1_CLK] = "base_cgu_out1_clk", +}; + +static u32 lpc18xx_cgu_pll0_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL1, CLK_SRC_IDIVA, CLK_SRC_IDIVB, CLK_SRC_IDIVC, + CLK_SRC_IDIVD, CLK_SRC_IDIVE, +}; + +static u32 lpc18xx_cgu_pll1_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL0USB, CLK_SRC_PLL0AUDIO, CLK_SRC_IDIVA, + CLK_SRC_IDIVB, CLK_SRC_IDIVC, CLK_SRC_IDIVD, CLK_SRC_IDIVE, +}; + +static u32 lpc18xx_cgu_idiva_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL0USB, CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1 +}; + +static u32 lpc18xx_cgu_idivbcde_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1, CLK_SRC_IDIVA, +}; + +static u32 lpc18xx_cgu_base_irc_src_ids[] = {CLK_SRC_IRC}; + +static u32 lpc18xx_cgu_base_usb0_src_ids[] = {CLK_SRC_PLL0USB}; + +static u32 lpc18xx_cgu_base_common_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1, CLK_SRC_IDIVA, + CLK_SRC_IDIVB, CLK_SRC_IDIVC, CLK_SRC_IDIVD, CLK_SRC_IDIVE, +}; + +static u32 lpc18xx_cgu_base_all_src_ids[] = { + CLK_SRC_OSC32, CLK_SRC_IRC, CLK_SRC_ENET_RX_CLK, + CLK_SRC_ENET_TX_CLK, CLK_SRC_GP_CLKIN, CLK_SRC_OSC, + CLK_SRC_PLL0USB, CLK_SRC_PLL0AUDIO, CLK_SRC_PLL1, + CLK_SRC_IDIVA, CLK_SRC_IDIVB, CLK_SRC_IDIVC, + CLK_SRC_IDIVD, CLK_SRC_IDIVE, +}; + +struct lpc18xx_cgu_src_clk_div { + u8 clk_id; + u8 n_parents; + struct clk_divider div; + struct clk_mux mux; + struct clk_gate gate; +}; + +#define LPC1XX_CGU_SRC_CLK_DIV(_id, _width, _table) \ +{ \ + .clk_id = CLK_SRC_ ##_id, \ + .n_parents = ARRAY_SIZE(lpc18xx_cgu_ ##_table), \ + .div = { \ + .shift = 2, \ + .width = _width, \ + }, \ + .mux = { \ + .mask = 0x1f, \ + .shift = 24, \ + .table = lpc18xx_cgu_ ##_table, \ + }, \ + .gate = { \ + .bit_idx = 0, \ + .flags = CLK_GATE_SET_TO_DISABLE, \ + }, \ +} + +static struct lpc18xx_cgu_src_clk_div lpc18xx_cgu_src_clk_divs[] = { + LPC1XX_CGU_SRC_CLK_DIV(IDIVA, 2, idiva_src_ids), + LPC1XX_CGU_SRC_CLK_DIV(IDIVB, 4, idivbcde_src_ids), + LPC1XX_CGU_SRC_CLK_DIV(IDIVC, 4, idivbcde_src_ids), + LPC1XX_CGU_SRC_CLK_DIV(IDIVD, 4, idivbcde_src_ids), + LPC1XX_CGU_SRC_CLK_DIV(IDIVE, 8, idivbcde_src_ids), +}; + +struct lpc18xx_cgu_base_clk { + u8 clk_id; + u8 n_parents; + struct clk_mux mux; + struct clk_gate gate; +}; + +#define LPC1XX_CGU_BASE_CLK(_id, _table, _flags) \ +{ \ + .clk_id = BASE_ ##_id ##_CLK, \ + .n_parents = ARRAY_SIZE(lpc18xx_cgu_ ##_table), \ + .mux = { \ + .mask = 0x1f, \ + .shift = 24, \ + .table = lpc18xx_cgu_ ##_table, \ + .flags = _flags, \ + }, \ + .gate = { \ + .bit_idx = 0, \ + .flags = CLK_GATE_SET_TO_DISABLE, \ + }, \ +} + +static struct lpc18xx_cgu_base_clk lpc18xx_cgu_base_clks[] = { + LPC1XX_CGU_BASE_CLK(SAFE, base_irc_src_ids, CLK_MUX_READ_ONLY), + LPC1XX_CGU_BASE_CLK(USB0, base_usb0_src_ids, 0), + LPC1XX_CGU_BASE_CLK(PERIPH, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(USB1, base_all_src_ids, 0), + LPC1XX_CGU_BASE_CLK(CPU, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(SPIFI, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(SPI, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(PHY_RX, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(PHY_TX, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(APB1, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(APB3, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(LCD, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(ADCHS, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(SDIO, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(SSP0, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(SSP1, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(UART0, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(UART1, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(UART2, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(UART3, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(OUT, base_all_src_ids, 0), + { /* 21 reserved */ }, + { /* 22 reserved */ }, + { /* 23 reserved */ }, + { /* 24 reserved */ }, + LPC1XX_CGU_BASE_CLK(AUDIO, base_common_src_ids, 0), + LPC1XX_CGU_BASE_CLK(CGU_OUT0, base_all_src_ids, 0), + LPC1XX_CGU_BASE_CLK(CGU_OUT1, base_all_src_ids, 0), +}; + +struct lpc18xx_pll { + struct clk_hw hw; + void __iomem *reg; + spinlock_t *lock; + u8 flags; +}; + +#define to_lpc_pll(hw) container_of(hw, struct lpc18xx_pll, hw) + +struct lpc18xx_cgu_pll_clk { + u8 clk_id; + u8 n_parents; + u8 reg_offset; + struct clk_mux mux; + struct clk_gate gate; + struct lpc18xx_pll pll; + const struct clk_ops *pll_ops; +}; + +#define LPC1XX_CGU_CLK_PLL(_id, _table, _pll_ops) \ +{ \ + .clk_id = CLK_SRC_ ##_id, \ + .n_parents = ARRAY_SIZE(lpc18xx_cgu_ ##_table), \ + .reg_offset = LPC18XX_CGU_ ##_id ##_STAT, \ + .mux = { \ + .mask = 0x1f, \ + .shift = 24, \ + .table = lpc18xx_cgu_ ##_table, \ + }, \ + .gate = { \ + .bit_idx = 0, \ + .flags = CLK_GATE_SET_TO_DISABLE, \ + }, \ + .pll_ops = &lpc18xx_ ##_pll_ops, \ +} + +/* + * PLL0 uses a special register value encoding. The compute functions below + * are taken or derived from the LPC1850 user manual (section 12.6.3.3). + */ + +/* Compute PLL0 multiplier from decoded version */ +static u32 lpc18xx_pll0_mdec2msel(u32 x) +{ + int i; + + switch (x) { + case 0x18003: return 1; + case 0x10003: return 2; + default: + for (i = LPC18XX_PLL0_MSEL_MAX + 1; x != 0x4000 && i > 0; i--) + x = ((x ^ x >> 14) & 1) | (x << 1 & 0x7fff); + return i; + } +} +/* Compute PLL0 decoded multiplier from binary version */ +static u32 lpc18xx_pll0_msel2mdec(u32 msel) +{ + u32 i, x = 0x4000; + + switch (msel) { + case 0: return 0; + case 1: return 0x18003; + case 2: return 0x10003; + default: + for (i = msel; i <= LPC18XX_PLL0_MSEL_MAX; i++) + x = ((x ^ x >> 1) & 1) << 14 | (x >> 1 & 0xffff); + return x; + } +} + +/* Compute PLL0 bandwidth SELI reg from multiplier */ +static u32 lpc18xx_pll0_msel2seli(u32 msel) +{ + u32 tmp; + + if (msel > 16384) return 1; + if (msel > 8192) return 2; + if (msel > 2048) return 4; + if (msel >= 501) return 8; + if (msel >= 60) { + tmp = 1024 / (msel + 9); + return ((1024 == (tmp * (msel + 9))) == 0) ? tmp * 4 : (tmp + 1) * 4; + } + + return (msel & 0x3c) + 4; +} + +/* Compute PLL0 bandwidth SELP reg from multiplier */ +static u32 lpc18xx_pll0_msel2selp(u32 msel) +{ + if (msel < 60) + return (msel >> 1) + 1; + + return 31; +} + +static unsigned long lpc18xx_pll0_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct lpc18xx_pll *pll = to_lpc_pll(hw); + u32 ctrl, mdiv, msel, npdiv; + + ctrl = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_CTRL); + mdiv = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_MDIV); + npdiv = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_NP_DIV); + + if (ctrl & LPC18XX_PLL0_CTRL_BYPASS) + return parent_rate; + + if (npdiv != LPC18XX_PLL0_NP_DIVS_1) { + pr_warn("%s: pre/post dividers not supported\n", __func__); + return 0; + } + + msel = lpc18xx_pll0_mdec2msel(mdiv & LPC18XX_PLL0_MDIV_MDEC_MASK); + if (msel) + return 2 * msel * parent_rate; + + pr_warn("%s: unable to calculate rate\n", __func__); + + return 0; +} + +static long lpc18xx_pll0_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + unsigned long m; + + if (*prate < rate) { + pr_warn("%s: pll dividers not supported\n", __func__); + return -EINVAL; + } + + m = DIV_ROUND_UP_ULL(*prate, rate * 2); + if (m <= 0 && m > LPC18XX_PLL0_MSEL_MAX) { + pr_warn("%s: unable to support rate %lu\n", __func__, rate); + return -EINVAL; + } + + return 2 * *prate * m; +} + +static int lpc18xx_pll0_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct lpc18xx_pll *pll = to_lpc_pll(hw); + u32 ctrl, stat, m; + int retry = 3; + + if (parent_rate < rate) { + pr_warn("%s: pll dividers not supported\n", __func__); + return -EINVAL; + } + + m = DIV_ROUND_UP_ULL(parent_rate, rate * 2); + if (m <= 0 && m > LPC18XX_PLL0_MSEL_MAX) { + pr_warn("%s: unable to support rate %lu\n", __func__, rate); + return -EINVAL; + } + + m = lpc18xx_pll0_msel2mdec(m); + m |= lpc18xx_pll0_msel2selp(m) << LPC18XX_PLL0_MDIV_SELP_SHIFT; + m |= lpc18xx_pll0_msel2seli(m) << LPC18XX_PLL0_MDIV_SELI_SHIFT; + + /* Power down PLL, disable clk output and dividers */ + ctrl = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_CTRL); + ctrl |= LPC18XX_PLL0_CTRL_PD; + ctrl &= ~(LPC18XX_PLL0_CTRL_BYPASS | LPC18XX_PLL0_CTRL_DIRECTI | + LPC18XX_PLL0_CTRL_DIRECTO | LPC18XX_PLL0_CTRL_CLKEN); + clk_writel(ctrl, pll->reg + LPC18XX_CGU_PLL0USB_CTRL); + + /* Configure new PLL settings */ + clk_writel(m, pll->reg + LPC18XX_CGU_PLL0USB_MDIV); + clk_writel(LPC18XX_PLL0_NP_DIVS_1, pll->reg + LPC18XX_CGU_PLL0USB_NP_DIV); + + /* Power up PLL and wait for lock */ + ctrl &= ~LPC18XX_PLL0_CTRL_PD; + clk_writel(ctrl, pll->reg + LPC18XX_CGU_PLL0USB_CTRL); + do { + udelay(10); + stat = clk_readl(pll->reg + LPC18XX_CGU_PLL0USB_STAT); + if (stat & LPC18XX_PLL0_STAT_LOCK) { + ctrl |= LPC18XX_PLL0_CTRL_CLKEN; + clk_writel(ctrl, pll->reg + LPC18XX_CGU_PLL0USB_CTRL); + + return 0; + } + } while (retry--); + + pr_warn("%s: unable to lock pll\n", __func__); + + return -EINVAL; +} + +static const struct clk_ops lpc18xx_pll0_ops = { + .recalc_rate = lpc18xx_pll0_recalc_rate, + .round_rate = lpc18xx_pll0_round_rate, + .set_rate = lpc18xx_pll0_set_rate, +}; + +static unsigned long lpc18xx_pll1_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct lpc18xx_pll *pll = to_lpc_pll(hw); + u16 msel, nsel, psel; + bool direct, fbsel; + u32 stat, ctrl; + + stat = clk_readl(pll->reg + LPC18XX_CGU_PLL1_STAT); + ctrl = clk_readl(pll->reg + LPC18XX_CGU_PLL1_CTRL); + + direct = (ctrl & LPC18XX_PLL1_CTRL_DIRECT) ? true : false; + fbsel = (ctrl & LPC18XX_PLL1_CTRL_FBSEL) ? true : false; + + msel = ((ctrl >> 16) & 0xff) + 1; + nsel = ((ctrl >> 12) & 0x3) + 1; + + if (direct || fbsel) + return msel * (parent_rate / nsel); + + psel = (ctrl >> 8) & 0x3; + psel = 1 << psel; + + return (msel / (2 * psel)) * (parent_rate / nsel); +} + +static const struct clk_ops lpc18xx_pll1_ops = { + .recalc_rate = lpc18xx_pll1_recalc_rate, +}; + +static struct lpc18xx_cgu_pll_clk lpc18xx_cgu_src_clk_plls[] = { + LPC1XX_CGU_CLK_PLL(PLL0USB, pll0_src_ids, pll0_ops), + LPC1XX_CGU_CLK_PLL(PLL0AUDIO, pll0_src_ids, pll0_ops), + LPC1XX_CGU_CLK_PLL(PLL1, pll1_src_ids, pll1_ops), +}; + +static void lpc18xx_fill_parent_names(const char **parent, u32 *id, int size) +{ + int i; + + for (i = 0; i < size; i++) + parent[i] = clk_src_names[id[i]]; +} + +static struct clk *lpc18xx_cgu_register_div(struct lpc18xx_cgu_src_clk_div *clk, + void __iomem *base, int n) +{ + void __iomem *reg = base + LPC18XX_CGU_IDIV_CTRL(n); + const char *name = clk_src_names[clk->clk_id]; + const char *parents[CLK_SRC_MAX]; + + clk->div.reg = reg; + clk->mux.reg = reg; + clk->gate.reg = reg; + + lpc18xx_fill_parent_names(parents, clk->mux.table, clk->n_parents); + + return clk_register_composite(NULL, name, parents, clk->n_parents, + &clk->mux.hw, &clk_mux_ops, + &clk->div.hw, &clk_divider_ops, + &clk->gate.hw, &clk_gate_ops, 0); +} + + +static struct clk *lpc18xx_register_base_clk(struct lpc18xx_cgu_base_clk *clk, + void __iomem *reg_base, int n) +{ + void __iomem *reg = reg_base + LPC18XX_CGU_BASE_CLK(n); + const char *name = clk_base_names[clk->clk_id]; + const char *parents[CLK_SRC_MAX]; + + if (clk->n_parents == 0) + return ERR_PTR(-ENOENT); + + clk->mux.reg = reg; + clk->gate.reg = reg; + + lpc18xx_fill_parent_names(parents, clk->mux.table, clk->n_parents); + + /* SAFE_CLK can not be turned off */ + if (n == BASE_SAFE_CLK) + return clk_register_composite(NULL, name, parents, clk->n_parents, + &clk->mux.hw, &clk_mux_ops, + NULL, NULL, NULL, NULL, 0); + + return clk_register_composite(NULL, name, parents, clk->n_parents, + &clk->mux.hw, &clk_mux_ops, + NULL, NULL, + &clk->gate.hw, &clk_gate_ops, 0); +} + + +static struct clk *lpc18xx_cgu_register_pll(struct lpc18xx_cgu_pll_clk *clk, + void __iomem *base) +{ + const char *name = clk_src_names[clk->clk_id]; + const char *parents[CLK_SRC_MAX]; + + clk->pll.reg = base; + clk->mux.reg = base + clk->reg_offset + LPC18XX_CGU_PLL_CTRL_OFFSET; + clk->gate.reg = base + clk->reg_offset + LPC18XX_CGU_PLL_CTRL_OFFSET; + + lpc18xx_fill_parent_names(parents, clk->mux.table, clk->n_parents); + + return clk_register_composite(NULL, name, parents, clk->n_parents, + &clk->mux.hw, &clk_mux_ops, + &clk->pll.hw, clk->pll_ops, + &clk->gate.hw, &clk_gate_ops, 0); +} + +static void __init lpc18xx_cgu_register_source_clks(struct device_node *np, + void __iomem *base) +{ + const char *parents[CLK_SRC_MAX]; + struct clk *clk; + int i; + + /* Register the internal 12 MHz RC oscillator (IRC) */ + clk = clk_register_fixed_rate(NULL, clk_src_names[CLK_SRC_IRC], + NULL, CLK_IS_ROOT, 12000000); + if (IS_ERR(clk)) + pr_warn("%s: failed to register irc clk\n", __func__); + + /* Register crystal oscillator controlller */ + parents[0] = of_clk_get_parent_name(np, 0); + clk = clk_register_gate(NULL, clk_src_names[CLK_SRC_OSC], parents[0], + 0, base + LPC18XX_CGU_XTAL_OSC_CTRL, + 0, CLK_GATE_SET_TO_DISABLE, NULL); + if (IS_ERR(clk)) + pr_warn("%s: failed to register osc clk\n", __func__); + + /* Register all PLLs */ + for (i = 0; i < ARRAY_SIZE(lpc18xx_cgu_src_clk_plls); i++) { + clk = lpc18xx_cgu_register_pll(&lpc18xx_cgu_src_clk_plls[i], + base); + if (IS_ERR(clk)) + pr_warn("%s: failed to register pll (%d)\n", __func__, i); + } + + /* Register all clock dividers A-E */ + for (i = 0; i < ARRAY_SIZE(lpc18xx_cgu_src_clk_divs); i++) { + clk = lpc18xx_cgu_register_div(&lpc18xx_cgu_src_clk_divs[i], + base, i); + if (IS_ERR(clk)) + pr_warn("%s: failed to register div %d\n", __func__, i); + } +} + +static struct clk *clk_base[BASE_CLK_MAX]; +static struct clk_onecell_data clk_base_data = { + .clks = clk_base, + .clk_num = BASE_CLK_MAX, +}; + +static void __init lpc18xx_cgu_register_base_clks(void __iomem *reg_base) +{ + int i; + + for (i = BASE_SAFE_CLK; i < BASE_CLK_MAX; i++) { + clk_base[i] = lpc18xx_register_base_clk(&lpc18xx_cgu_base_clks[i], + reg_base, i); + if (IS_ERR(clk_base[i]) && PTR_ERR(clk_base[i]) != -ENOENT) + pr_warn("%s: register base clk %d failed\n", __func__, i); + } +} + +static void __init lpc18xx_cgu_init(struct device_node *np) +{ + void __iomem *reg_base; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_warn("%s: failed to map address range\n", __func__); + return; + } + + lpc18xx_cgu_register_source_clks(np, reg_base); + lpc18xx_cgu_register_base_clks(reg_base); + + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_base_data); +} +CLK_OF_DECLARE(lpc18xx_cgu, "nxp,lpc1850-cgu", lpc18xx_cgu_init); diff --git a/include/dt-bindings/clock/lpc18xx-cgu.h b/include/dt-bindings/clock/lpc18xx-cgu.h new file mode 100644 index 000000000000..6e57c6d2ca66 --- /dev/null +++ b/include/dt-bindings/clock/lpc18xx-cgu.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 Joachim Eastwood + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the licence that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + * + */ + +/* LPC18xx/43xx base clock ids */ +#define BASE_SAFE_CLK 0 +#define BASE_USB0_CLK 1 +#define BASE_PERIPH_CLK 2 +#define BASE_USB1_CLK 3 +#define BASE_CPU_CLK 4 +#define BASE_SPIFI_CLK 5 +#define BASE_SPI_CLK 6 +#define BASE_PHY_RX_CLK 7 +#define BASE_PHY_TX_CLK 8 +#define BASE_APB1_CLK 9 +#define BASE_APB3_CLK 10 +#define BASE_LCD_CLK 11 +#define BASE_ADCHS_CLK 12 +#define BASE_SDIO_CLK 13 +#define BASE_SSP0_CLK 14 +#define BASE_SSP1_CLK 15 +#define BASE_UART0_CLK 16 +#define BASE_UART1_CLK 17 +#define BASE_UART2_CLK 18 +#define BASE_UART3_CLK 19 +#define BASE_OUT_CLK 20 +#define BASE_RES1_CLK 21 +#define BASE_RES2_CLK 22 +#define BASE_RES3_CLK 23 +#define BASE_RES4_CLK 24 +#define BASE_AUDIO_CLK 25 +#define BASE_CGU_OUT0_CLK 26 +#define BASE_CGU_OUT1_CLK 27 +#define BASE_CLK_MAX (BASE_CGU_OUT1_CLK + 1) -- cgit v1.2.3-58-ga151 From 668c45df36939fbf0a3578ad97f547c8db86a7a0 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 28 May 2015 22:31:44 +0200 Subject: doc: dt: add documentation for lpc1850-cgu clk driver Add DT binding documentation for lpc1850-cgu driver. Signed-off-by: Joachim Eastwood Signed-off-by: Michael Turquette --- .../devicetree/bindings/clock/lpc1850-cgu.txt | 131 +++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/lpc1850-cgu.txt diff --git a/Documentation/devicetree/bindings/clock/lpc1850-cgu.txt b/Documentation/devicetree/bindings/clock/lpc1850-cgu.txt new file mode 100644 index 000000000000..2cc32a9a945a --- /dev/null +++ b/Documentation/devicetree/bindings/clock/lpc1850-cgu.txt @@ -0,0 +1,131 @@ +* NXP LPC1850 Clock Generation Unit (CGU) + +The CGU generates multiple independent clocks for the core and the +peripheral blocks of the LPC18xx. Each independent clock is called +a base clock and itself is one of the inputs to the two Clock +Control Units (CCUs) which control the branch clocks to the +individual peripherals. + +The CGU selects the inputs to the clock generators from multiple +clock sources, controls the clock generation, and routes the outputs +of the clock generators through the clock source bus to the output +stages. Each output stage provides an independent clock source and +corresponds to one of the base clocks for the LPC18xx. + + - Above text taken from NXP LPC1850 User Manual. + + +This binding uses the common clock binding: + Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible: + Should be "nxp,lpc1850-cgu" +- reg: + Shall define the base and range of the address space + containing clock control registers +- #clock-cells: + Shall have value <1>. The permitted clock-specifier values + are the base clock numbers defined below. +- clocks: + Shall contain a list of phandles for the external input + sources to the CGU. The list shall be in the following + order: xtal, 32khz, enet_rx_clk, enet_tx_clk, gp_clkin. +- clock-indices: + Shall be an ordered list of numbers defining the base clock + number provided by the CGU. +- clock-output-names: + Shall be an ordered list of strings defining the names of + the clocks provided by the CGU. + +Which base clocks that are available on the CGU depends on the +specific LPC part. Base clocks are numbered from 0 to 27. + +Number: Name: Description: + 0 BASE_SAFE_CLK Base safe clock (always on) for WWDT + 1 BASE_USB0_CLK Base clock for USB0 + 2 BASE_PERIPH_CLK Base clock for Cortex-M0SUB subsystem, + SPI, and SGPIO + 3 BASE_USB1_CLK Base clock for USB1 + 4 BASE_CPU_CLK System base clock for ARM Cortex-M core + and APB peripheral blocks #0 and #2 + 5 BASE_SPIFI_CLK Base clock for SPIFI + 6 BASE_SPI_CLK Base clock for SPI + 7 BASE_PHY_RX_CLK Base clock for Ethernet PHY Receive clock + 8 BASE_PHY_TX_CLK Base clock for Ethernet PHY Transmit clock + 9 BASE_APB1_CLK Base clock for APB peripheral block # 1 +10 BASE_APB3_CLK Base clock for APB peripheral block # 3 +11 BASE_LCD_CLK Base clock for LCD +12 BASE_ADCHS_CLK Base clock for ADCHS +13 BASE_SDIO_CLK Base clock for SD/MMC +14 BASE_SSP0_CLK Base clock for SSP0 +15 BASE_SSP1_CLK Base clock for SSP1 +16 BASE_UART0_CLK Base clock for UART0 +17 BASE_UART1_CLK Base clock for UART1 +18 BASE_UART2_CLK Base clock for UART2 +19 BASE_UART3_CLK Base clock for UART3 +20 BASE_OUT_CLK Base clock for CLKOUT pin +24-21 - Reserved +25 BASE_AUDIO_CLK Base clock for audio system (I2S) +26 BASE_CGU_OUT0_CLK Base clock for CGU_OUT0 clock output +27 BASE_CGU_OUT1_CLK Base clock for CGU_OUT1 clock output + +BASE_PERIPH_CLK and BASE_SPI_CLK is only available on LPC43xx. +BASE_ADCHS_CLK is only available on LPC4370. + + +Example board file: + +/ { + clocks { + xtal: xtal { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <12000000>; + }; + + xtal32: xtal32 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + }; + + enet_rx_clk: enet_rx_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "enet_rx_clk"; + }; + + enet_tx_clk: enet_tx_clk { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "enet_tx_clk"; + }; + + gp_clkin: gp_clkin { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <0>; + clock-output-names = "gp_clkin"; + }; + }; + + soc { + cgu: clock-controller@40050000 { + compatible = "nxp,lpc1850-cgu"; + reg = <0x40050000 0x1000>; + #clock-cells = <1>; + clocks = <&xtal>, <&creg_clk 1>, <&enet_rx_clk>, <&enet_tx_clk>, <&gp_clkin>; + }; + + /* A CGU and CCU clock consumer */ + lcdc: lcdc@40008000 { + ... + clocks = <&cgu BASE_LCD_CLK>, <&ccu1 CLK_CPU_LCD>; + clock-names = "clcdclk", "apb_pclk"; + ... + }; + }; +}; -- cgit v1.2.3-58-ga151 From 472cd304a36e0a1b21045ddc802ffb16a1cbac65 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 28 May 2015 22:31:45 +0200 Subject: clk: add lpc18xx ccu clk driver Add driver for NXP LPC18xx/43xx Clock Control Unit (CCU). The CCU provides fine grained gating of most clocks present in the SoC. Signed-off-by: Joachim Eastwood Signed-off-by: Michael Turquette --- drivers/clk/nxp/Makefile | 1 + drivers/clk/nxp/clk-lpc18xx-ccu.c | 293 ++++++++++++++++++++++++++++++++ include/dt-bindings/clock/lpc18xx-ccu.h | 74 ++++++++ 3 files changed, 368 insertions(+) create mode 100644 drivers/clk/nxp/clk-lpc18xx-ccu.c create mode 100644 include/dt-bindings/clock/lpc18xx-ccu.h diff --git a/drivers/clk/nxp/Makefile b/drivers/clk/nxp/Makefile index aca9d3e4f7fe..7f608b0ad7b4 100644 --- a/drivers/clk/nxp/Makefile +++ b/drivers/clk/nxp/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_ARCH_LPC18XX) += clk-lpc18xx-cgu.o +obj-$(CONFIG_ARCH_LPC18XX) += clk-lpc18xx-ccu.o diff --git a/drivers/clk/nxp/clk-lpc18xx-ccu.c b/drivers/clk/nxp/clk-lpc18xx-ccu.c new file mode 100644 index 000000000000..eeaee97da110 --- /dev/null +++ b/drivers/clk/nxp/clk-lpc18xx-ccu.c @@ -0,0 +1,293 @@ +/* + * Clk driver for NXP LPC18xx/LPC43xx Clock Control Unit (CCU) + * + * Copyright (C) 2015 Joachim Eastwood + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Bit defines for CCU branch configuration register */ +#define LPC18XX_CCU_RUN BIT(0) +#define LPC18XX_CCU_AUTO BIT(1) +#define LPC18XX_CCU_DIV BIT(5) +#define LPC18XX_CCU_DIVSTAT BIT(27) + +/* CCU branch feature bits */ +#define CCU_BRANCH_IS_BUS BIT(0) +#define CCU_BRANCH_HAVE_DIV2 BIT(1) + +#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw) + +struct lpc18xx_branch_clk_data { + const char **name; + int num; +}; + +struct lpc18xx_clk_branch { + const char *base_name; + const char *name; + u16 offset; + u16 flags; + struct clk *clk; + struct clk_gate gate; +}; + +static struct lpc18xx_clk_branch clk_branches[] = { + {"base_apb3_clk", "apb3_bus", CLK_APB3_BUS, CCU_BRANCH_IS_BUS}, + {"base_apb3_clk", "apb3_i2c1", CLK_APB3_I2C1, 0}, + {"base_apb3_clk", "apb3_dac", CLK_APB3_DAC, 0}, + {"base_apb3_clk", "apb3_adc0", CLK_APB3_ADC0, 0}, + {"base_apb3_clk", "apb3_adc1", CLK_APB3_ADC1, 0}, + {"base_apb3_clk", "apb3_can0", CLK_APB3_CAN0, 0}, + + {"base_apb1_clk", "apb1_bus", CLK_APB1_BUS, CCU_BRANCH_IS_BUS}, + {"base_apb1_clk", "apb1_mc_pwm", CLK_APB1_MOTOCON_PWM, 0}, + {"base_apb1_clk", "apb1_i2c0", CLK_APB1_I2C0, 0}, + {"base_apb1_clk", "apb1_i2s", CLK_APB1_I2S, 0}, + {"base_apb1_clk", "apb1_can1", CLK_APB1_CAN1, 0}, + + {"base_spifi_clk", "spifi", CLK_SPIFI, 0}, + + {"base_cpu_clk", "cpu_bus", CLK_CPU_BUS, CCU_BRANCH_IS_BUS}, + {"base_cpu_clk", "cpu_spifi", CLK_CPU_SPIFI, 0}, + {"base_cpu_clk", "cpu_gpio", CLK_CPU_GPIO, 0}, + {"base_cpu_clk", "cpu_lcd", CLK_CPU_LCD, 0}, + {"base_cpu_clk", "cpu_ethernet", CLK_CPU_ETHERNET, 0}, + {"base_cpu_clk", "cpu_usb0", CLK_CPU_USB0, 0}, + {"base_cpu_clk", "cpu_emc", CLK_CPU_EMC, 0}, + {"base_cpu_clk", "cpu_sdio", CLK_CPU_SDIO, 0}, + {"base_cpu_clk", "cpu_dma", CLK_CPU_DMA, 0}, + {"base_cpu_clk", "cpu_core", CLK_CPU_CORE, 0}, + {"base_cpu_clk", "cpu_sct", CLK_CPU_SCT, 0}, + {"base_cpu_clk", "cpu_usb1", CLK_CPU_USB1, 0}, + {"base_cpu_clk", "cpu_emcdiv", CLK_CPU_EMCDIV, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_flasha", CLK_CPU_FLASHA, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_flashb", CLK_CPU_FLASHB, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_m0app", CLK_CPU_M0APP, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_adchs", CLK_CPU_ADCHS, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_eeprom", CLK_CPU_EEPROM, CCU_BRANCH_HAVE_DIV2}, + {"base_cpu_clk", "cpu_wwdt", CLK_CPU_WWDT, 0}, + {"base_cpu_clk", "cpu_uart0", CLK_CPU_UART0, 0}, + {"base_cpu_clk", "cpu_uart1", CLK_CPU_UART1, 0}, + {"base_cpu_clk", "cpu_ssp0", CLK_CPU_SSP0, 0}, + {"base_cpu_clk", "cpu_timer0", CLK_CPU_TIMER0, 0}, + {"base_cpu_clk", "cpu_timer1", CLK_CPU_TIMER1, 0}, + {"base_cpu_clk", "cpu_scu", CLK_CPU_SCU, 0}, + {"base_cpu_clk", "cpu_creg", CLK_CPU_CREG, 0}, + {"base_cpu_clk", "cpu_ritimer", CLK_CPU_RITIMER, 0}, + {"base_cpu_clk", "cpu_uart2", CLK_CPU_UART2, 0}, + {"base_cpu_clk", "cpu_uart3", CLK_CPU_UART3, 0}, + {"base_cpu_clk", "cpu_timer2", CLK_CPU_TIMER2, 0}, + {"base_cpu_clk", "cpu_timer3", CLK_CPU_TIMER3, 0}, + {"base_cpu_clk", "cpu_ssp1", CLK_CPU_SSP1, 0}, + {"base_cpu_clk", "cpu_qei", CLK_CPU_QEI, 0}, + + {"base_periph_clk", "periph_bus", CLK_PERIPH_BUS, CCU_BRANCH_IS_BUS}, + {"base_periph_clk", "periph_core", CLK_PERIPH_CORE, 0}, + {"base_periph_clk", "periph_sgpio", CLK_PERIPH_SGPIO, 0}, + + {"base_usb0_clk", "usb0", CLK_USB0, 0}, + {"base_usb1_clk", "usb1", CLK_USB1, 0}, + {"base_spi_clk", "spi", CLK_SPI, 0}, + {"base_adchs_clk", "adchs", CLK_ADCHS, 0}, + + {"base_audio_clk", "audio", CLK_AUDIO, 0}, + {"base_uart3_clk", "apb2_uart3", CLK_APB2_UART3, 0}, + {"base_uart2_clk", "apb2_uart2", CLK_APB2_UART2, 0}, + {"base_uart1_clk", "apb0_uart1", CLK_APB0_UART1, 0}, + {"base_uart0_clk", "apb0_uart0", CLK_APB0_UART0, 0}, + {"base_ssp1_clk", "apb2_ssp1", CLK_APB2_SSP1, 0}, + {"base_ssp0_clk", "apb0_ssp0", CLK_APB0_SSP0, 0}, + {"base_sdio_clk", "sdio", CLK_SDIO, 0}, +}; + +static struct clk *lpc18xx_ccu_branch_clk_get(struct of_phandle_args *clkspec, + void *data) +{ + struct lpc18xx_branch_clk_data *clk_data = data; + unsigned int offset = clkspec->args[0]; + int i, j; + + for (i = 0; i < ARRAY_SIZE(clk_branches); i++) { + if (clk_branches[i].offset != offset) + continue; + + for (j = 0; j < clk_data->num; j++) { + if (!strcmp(clk_branches[i].base_name, clk_data->name[j])) + return clk_branches[i].clk; + } + } + + pr_err("%s: invalid clock offset %d\n", __func__, offset); + + return ERR_PTR(-EINVAL); +} + +static int lpc18xx_ccu_gate_endisable(struct clk_hw *hw, bool enable) +{ + struct clk_gate *gate = to_clk_gate(hw); + u32 val; + + /* + * Divider field is write only, so divider stat field must + * be read so divider field can be set accordingly. + */ + val = clk_readl(gate->reg); + if (val & LPC18XX_CCU_DIVSTAT) + val |= LPC18XX_CCU_DIV; + + if (enable) { + val |= LPC18XX_CCU_RUN; + } else { + /* + * To safely disable a branch clock a squence of two separate + * writes must be used. First write should set the AUTO bit + * and the next write should clear the RUN bit. + */ + val |= LPC18XX_CCU_AUTO; + clk_writel(val, gate->reg); + + val &= ~LPC18XX_CCU_RUN; + } + + clk_writel(val, gate->reg); + + return 0; +} + +static int lpc18xx_ccu_gate_enable(struct clk_hw *hw) +{ + return lpc18xx_ccu_gate_endisable(hw, true); +} + +static void lpc18xx_ccu_gate_disable(struct clk_hw *hw) +{ + lpc18xx_ccu_gate_endisable(hw, false); +} + +static int lpc18xx_ccu_gate_is_enabled(struct clk_hw *hw) +{ + struct clk_gate *gate = to_clk_gate(hw); + + return clk_readl(gate->reg) & LPC18XX_CCU_RUN; +} + +static const struct clk_ops lpc18xx_ccu_gate_ops = { + .enable = lpc18xx_ccu_gate_enable, + .disable = lpc18xx_ccu_gate_disable, + .is_enabled = lpc18xx_ccu_gate_is_enabled, +}; + +static void lpc18xx_ccu_register_branch_gate_div(struct lpc18xx_clk_branch *branch, + void __iomem *reg_base, + const char *parent) +{ + const struct clk_ops *div_ops = NULL; + struct clk_divider *div = NULL; + struct clk_hw *div_hw = NULL; + + if (branch->flags & CCU_BRANCH_HAVE_DIV2) { + div = kzalloc(sizeof(*div), GFP_KERNEL); + if (!div) + return; + + div->reg = branch->offset + reg_base; + div->flags = CLK_DIVIDER_READ_ONLY; + div->shift = 27; + div->width = 1; + + div_hw = &div->hw; + div_ops = &clk_divider_ops; + } + + branch->gate.reg = branch->offset + reg_base; + branch->gate.bit_idx = 0; + + branch->clk = clk_register_composite(NULL, branch->name, &parent, 1, + NULL, NULL, + div_hw, div_ops, + &branch->gate.hw, &lpc18xx_ccu_gate_ops, 0); + if (IS_ERR(branch->clk)) { + kfree(div); + pr_warn("%s: failed to register %s\n", __func__, branch->name); + return; + } + + /* Grab essential branch clocks for CPU and SDRAM */ + switch (branch->offset) { + case CLK_CPU_EMC: + case CLK_CPU_CORE: + case CLK_CPU_CREG: + case CLK_CPU_EMCDIV: + clk_prepare_enable(branch->clk); + } +} + +static void lpc18xx_ccu_register_branch_clks(void __iomem *reg_base, + const char *base_name) +{ + const char *parent = base_name; + int i; + + for (i = 0; i < ARRAY_SIZE(clk_branches); i++) { + if (strcmp(clk_branches[i].base_name, base_name)) + continue; + + lpc18xx_ccu_register_branch_gate_div(&clk_branches[i], reg_base, + parent); + + if (clk_branches[i].flags & CCU_BRANCH_IS_BUS) + parent = clk_branches[i].name; + } +} + +static void __init lpc18xx_ccu_init(struct device_node *np) +{ + struct lpc18xx_branch_clk_data *clk_data; + void __iomem *reg_base; + int i, ret; + + reg_base = of_iomap(np, 0); + if (!reg_base) { + pr_warn("%s: failed to map address range\n", __func__); + return; + } + + clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + return; + + clk_data->num = of_property_count_strings(np, "clock-names"); + clk_data->name = kcalloc(clk_data->num, sizeof(char *), GFP_KERNEL); + if (!clk_data->name) { + kfree(clk_data); + return; + } + + for (i = 0; i < clk_data->num; i++) { + ret = of_property_read_string_index(np, "clock-names", i, + &clk_data->name[i]); + if (ret) { + pr_warn("%s: failed to get clock name at idx %d\n", + __func__, i); + continue; + } + + lpc18xx_ccu_register_branch_clks(reg_base, clk_data->name[i]); + } + + of_clk_add_provider(np, lpc18xx_ccu_branch_clk_get, clk_data); +} +CLK_OF_DECLARE(lpc18xx_ccu, "nxp,lpc1850-ccu", lpc18xx_ccu_init); diff --git a/include/dt-bindings/clock/lpc18xx-ccu.h b/include/dt-bindings/clock/lpc18xx-ccu.h new file mode 100644 index 000000000000..bbfe00b6ab7d --- /dev/null +++ b/include/dt-bindings/clock/lpc18xx-ccu.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2015 Joachim Eastwood + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the licence that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + * + */ + +/* Clock Control Unit 1 (CCU1) clock offsets */ +#define CLK_APB3_BUS 0x100 +#define CLK_APB3_I2C1 0x108 +#define CLK_APB3_DAC 0x110 +#define CLK_APB3_ADC0 0x118 +#define CLK_APB3_ADC1 0x120 +#define CLK_APB3_CAN0 0x128 +#define CLK_APB1_BUS 0x200 +#define CLK_APB1_MOTOCON_PWM 0x208 +#define CLK_APB1_I2C0 0x210 +#define CLK_APB1_I2S 0x218 +#define CLK_APB1_CAN1 0x220 +#define CLK_SPIFI 0x300 +#define CLK_CPU_BUS 0x400 +#define CLK_CPU_SPIFI 0x408 +#define CLK_CPU_GPIO 0x410 +#define CLK_CPU_LCD 0x418 +#define CLK_CPU_ETHERNET 0x420 +#define CLK_CPU_USB0 0x428 +#define CLK_CPU_EMC 0x430 +#define CLK_CPU_SDIO 0x438 +#define CLK_CPU_DMA 0x440 +#define CLK_CPU_CORE 0x448 +#define CLK_CPU_SCT 0x468 +#define CLK_CPU_USB1 0x470 +#define CLK_CPU_EMCDIV 0x478 +#define CLK_CPU_FLASHA 0x480 +#define CLK_CPU_FLASHB 0x488 +#define CLK_CPU_M0APP 0x490 +#define CLK_CPU_ADCHS 0x498 +#define CLK_CPU_EEPROM 0x4a0 +#define CLK_CPU_WWDT 0x500 +#define CLK_CPU_UART0 0x508 +#define CLK_CPU_UART1 0x510 +#define CLK_CPU_SSP0 0x518 +#define CLK_CPU_TIMER0 0x520 +#define CLK_CPU_TIMER1 0x528 +#define CLK_CPU_SCU 0x530 +#define CLK_CPU_CREG 0x538 +#define CLK_CPU_RITIMER 0x600 +#define CLK_CPU_UART2 0x608 +#define CLK_CPU_UART3 0x610 +#define CLK_CPU_TIMER2 0x618 +#define CLK_CPU_TIMER3 0x620 +#define CLK_CPU_SSP1 0x628 +#define CLK_CPU_QEI 0x630 +#define CLK_PERIPH_BUS 0x700 +#define CLK_PERIPH_CORE 0x710 +#define CLK_PERIPH_SGPIO 0x718 +#define CLK_USB0 0x800 +#define CLK_USB1 0x900 +#define CLK_SPI 0xA00 +#define CLK_ADCHS 0xB00 + +/* Clock Control Unit 2 (CCU2) clock offsets */ +#define CLK_AUDIO 0x100 +#define CLK_APB2_UART3 0x200 +#define CLK_APB2_UART2 0x300 +#define CLK_APB0_UART1 0x400 +#define CLK_APB0_UART0 0x500 +#define CLK_APB2_SSP1 0x600 +#define CLK_APB0_SSP0 0x700 +#define CLK_SDIO 0x800 -- cgit v1.2.3-58-ga151 From ddfb157444e3cdb6da18ea759730b4b4af65d3d9 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Thu, 28 May 2015 22:31:46 +0200 Subject: doc: dt: add documentation for lpc1850-ccu clk driver Add DT binding documentation for lpc1850-ccu clk driver. Signed-off-by: Joachim Eastwood Signed-off-by: Michael Turquette --- .../devicetree/bindings/clock/lpc1850-ccu.txt | 77 ++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/lpc1850-ccu.txt diff --git a/Documentation/devicetree/bindings/clock/lpc1850-ccu.txt b/Documentation/devicetree/bindings/clock/lpc1850-ccu.txt new file mode 100644 index 000000000000..fa97c12014ac --- /dev/null +++ b/Documentation/devicetree/bindings/clock/lpc1850-ccu.txt @@ -0,0 +1,77 @@ +* NXP LPC1850 Clock Control Unit (CCU) + +Each CGU base clock has several clock branches which can be turned on +or off independently by the Clock Control Units CCU1 or CCU2. The +branch clocks are distributed between CCU1 and CCU2. + + - Above text taken from NXP LPC1850 User Manual. + +This binding uses the common clock binding: + Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible: + Should be "nxp,lpc1850-ccu" +- reg: + Shall define the base and range of the address space + containing clock control registers +- #clock-cells: + Shall have value <1>. The permitted clock-specifier values + are the branch clock names defined in table below. +- clocks: + Shall contain a list of phandles for the base clocks routed + from the CGU to the specific CCU. See mapping of base clocks + and CCU in table below. +- clock-names: + Shall contain a list of names for the base clock routed + from the CGU to the specific CCU. Valid CCU clock names: + "base_usb0_clk", "base_periph_clk", "base_usb1_clk", + "base_cpu_clk", "base_spifi_clk", "base_spi_clk", + "base_apb1_clk", "base_apb3_clk", "base_adchs_clk", + "base_sdio_clk", "base_ssp0_clk", "base_ssp1_clk", + "base_uart0_clk", "base_uart1_clk", "base_uart2_clk", + "base_uart3_clk", "base_audio_clk" + +Which branch clocks that are available on the CCU depends on the +specific LPC part. Check the user manual for your specific part. + +A list of CCU clocks can be found in dt-bindings/clock/lpc18xx-ccu.h. + +Example board file: + +soc { + ccu1: clock-controller@40051000 { + compatible = "nxp,lpc1850-ccu"; + reg = <0x40051000 0x1000>; + #clock-cells = <1>; + clocks = <&cgu BASE_APB3_CLK>, <&cgu BASE_APB1_CLK>, + <&cgu BASE_SPIFI_CLK>, <&cgu BASE_CPU_CLK>, + <&cgu BASE_PERIPH_CLK>, <&cgu BASE_USB0_CLK>, + <&cgu BASE_USB1_CLK>, <&cgu BASE_SPI_CLK>; + clock-names = "base_apb3_clk", "base_apb1_clk", + "base_spifi_clk", "base_cpu_clk", + "base_periph_clk", "base_usb0_clk", + "base_usb1_clk", "base_spi_clk"; + }; + + ccu2: clock-controller@40052000 { + compatible = "nxp,lpc1850-ccu"; + reg = <0x40052000 0x1000>; + #clock-cells = <1>; + clocks = <&cgu BASE_AUDIO_CLK>, <&cgu BASE_UART3_CLK>, + <&cgu BASE_UART2_CLK>, <&cgu BASE_UART1_CLK>, + <&cgu BASE_UART0_CLK>, <&cgu BASE_SSP1_CLK>, + <&cgu BASE_SSP0_CLK>, <&cgu BASE_SDIO_CLK>; + clock-names = "base_audio_clk", "base_uart3_clk", + "base_uart2_clk", "base_uart1_clk", + "base_uart0_clk", "base_ssp1_clk", + "base_ssp0_clk", "base_sdio_clk"; + }; + + /* A user of CCU brach clocks */ + uart1: serial@40082000 { + ... + clocks = <&ccu2 CLK_APB0_UART1>, <&ccu1 CLK_CPU_UART1>; + ... + }; +}; -- cgit v1.2.3-58-ga151 From d8d919879e9a645061a560a0a26abb9f3bca97df Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Fri, 3 Apr 2015 18:43:44 +0200 Subject: clk: add CLK_RECALC_NEW_RATES clock flag for Exynos cpu clock support This flag is needed to fix the issue with wrong dividers being setup by Common Clock Framework when using the new Exynos cpu clock support. The issue happens because clk_core_set_rate_nolock() calls clk_calc_new_rates(clk, rate) before both pre/post clock notifiers have a chance to run. In case of Exynos cpu clock support pre/post clock notifiers are registered for mout_apll clock which is a parent of armclk cpu clock and dividers are modified in both pre and post clock notifier. This results in wrong dividers values being later programmed by clk_change_rate(top). To workaround the problem CLK_RECALC_NEW_RATES flag is added and it is set for mout_apll clock later so the correct divider values are re-calculated after both pre and post clock notifiers had run. For example when using "performance" governor on Exynos4210 Origen board the cpufreq-dt driver requests to change the frequency from 1000MHz to 1200MHz and after the change state of the relevant clocks is following: Without use of CLK_GET_RATE_NOCACHE flag: fout_apll rate: 1200000000 fout_apll_div_2 rate: 600000000 mout_clkout_cpu rate: 600000000 div_clkout_cpu rate: 600000000 clkout_cpu rate: 600000000 mout_apll rate: 1200000000 armclk rate: 1200000000 mout_hpm rate: 1200000000 div_copy rate: 300000000 div_hpm rate: 300000000 mout_core rate: 1200000000 div_core rate: 1200000000 div_core2 rate: 1200000000 arm_clk_div_2 rate: 600000000 div_corem0 rate: 300000000 div_corem1 rate: 150000000 div_periph rate: 300000000 div_atb rate: 300000000 div_pclk_dbg rate: 150000000 sclk_apll rate: 1200000000 sclk_apll_div_2 rate: 600000000 With use of CLK_GET_RATE_NOCACHE flag: fout_apll rate: 1200000000 fout_apll_div_2 rate: 600000000 mout_clkout_cpu rate: 600000000 div_clkout_cpu rate: 600000000 clkout_cpu rate: 600000000 mout_apll rate: 1200000000 armclk rate: 1200000000 mout_hpm rate: 1200000000 div_copy rate: 200000000 div_hpm rate: 200000000 mout_core rate: 1200000000 div_core rate: 1200000000 div_core2 rate: 1200000000 arm_clk_div_2 rate: 600000000 div_corem0 rate: 300000000 div_corem1 rate: 150000000 div_periph rate: 300000000 div_atb rate: 240000000 div_pclk_dbg rate: 120000000 sclk_apll rate: 150000000 sclk_apll_div_2 rate: 75000000 Without this change cpufreq-dt driver showed ~10 mA larger energy consumption when compared to cpufreq-exynos one when "performance" cpufreq governor was used on Exynos4210 SoC based Origen board. This issue was probably meant to be workarounded by use of CLK_GET_RATE_NOCACHE and CLK_DIVIDER_READ_ONLY clock flags in the original Exynos cpu clock patchset (in "[PATCH v12 6/6] clk: samsung: remove unused clock aliases and update clock flags" patch) but usage of these flags is not sufficient to fix the issue observed. Cc: Thomas Abraham Cc: Tomasz Figa Cc: Mike Turquette Cc: Javier Martinez Canillas Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Michael Turquette --- drivers/clk/clk.c | 3 +++ include/linux/clk-provider.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 5b0f41868b42..0c0124c4eeeb 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1788,6 +1788,9 @@ static void clk_change_rate(struct clk_core *clk) if (clk->notifier_count && old_rate != clk->rate) __clk_notify(clk, POST_RATE_CHANGE, old_rate, clk->rate); + if (clk->flags & CLK_RECALC_NEW_RATES) + (void)clk_calc_new_rates(clk, clk->new_rate); + /* * Use safe iteration, as change_rate can actually swap parents * for certain clock types. diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index df695313f975..82f59ca8188a 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -31,6 +31,7 @@ #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_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */ +#define CLK_RECALC_NEW_RATES BIT(9) /* recalc rates after notifications */ struct clk_hw; struct clk_core; -- cgit v1.2.3-58-ga151 From ddeac8d968d41d13a52582d6e80395a329e9b1ff Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Fri, 3 Apr 2015 18:43:45 +0200 Subject: clk: samsung: add infrastructure to register cpu clocks The CPU clock provider supplies the clock to the CPU clock domain. The composition and organization of the CPU clock provider could vary among Exynos SoCs. A CPU clock provider can be composed of clock mux, dividers and gates. This patch defines a new clock type for CPU clock provider and adds infrastructure to register the CPU clock providers for Samsung platforms. Changes by Bartlomiej: - fixed issue with setting lower dividers before the parent clock speed was lowered (the issue resulted in lockup on Exynos4210 SoC based Origen board when "ondemand" cpufreq governor was stress tested) - fixed missing spin_unlock on error in exynos_cpuclk_post_rate_change() problem by moving cfg_data search outside of the spin locked area - removed leftover kfree() in exynos_register_cpu_clock() that could result in dereferencing the NULL pointer on error - moved spin_lock earlier in exynos_cpuclk_pre_rate_change() to cover reading of E4210_SRC_CPU and E4210_DIV_CPU1 registers - added missing "last chance" checks to wait_until_divider_stable() and wait_until_mux_stable() (needed in case that IRQ handling took long time to proceed and resulted in function printing incorrect error message about timeout) - moved E4210_CPU_DIV[0,1]() macros just before their only users, this resulted in moving them from patch #2 to patch #3/6 ("clk: samsung: exynos4: add cpu clock configuration data and instantiate cpu clock") - removed E5250_CPU_DIV[0,1](), E5420_EGL_DIV0() and E5420_KFC_DIV() macros for now - added my Copyrights to drivers/clk/samsung/clk-cpu.c Cc: Tomasz Figa Cc: Mike Turquette Cc: Javier Martinez Canillas Signed-off-by: Thomas Abraham Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Michael Turquette --- drivers/clk/samsung/Makefile | 2 +- drivers/clk/samsung/clk-cpu.c | 349 ++++++++++++++++++++++++++++++++++++++++++ drivers/clk/samsung/clk-cpu.h | 73 +++++++++ 3 files changed, 423 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/samsung/clk-cpu.c create mode 100644 drivers/clk/samsung/clk-cpu.h diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index a17683b2cf27..5f6833ea355d 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -2,7 +2,7 @@ # Samsung Clock specific Makefile # -obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o +obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o clk-cpu.o obj-$(CONFIG_SOC_EXYNOS3250) += clk-exynos3250.o obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o obj-$(CONFIG_SOC_EXYNOS4415) += clk-exynos4415.o diff --git a/drivers/clk/samsung/clk-cpu.c b/drivers/clk/samsung/clk-cpu.c new file mode 100644 index 000000000000..3a1fe07cfe9e --- /dev/null +++ b/drivers/clk/samsung/clk-cpu.c @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * Author: Thomas Abraham + * + * Copyright (c) 2015 Samsung Electronics Co., Ltd. + * Bartlomiej Zolnierkiewicz + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This file contains the utility function to register CPU clock for Samsung + * Exynos platforms. A CPU clock is defined as a clock supplied to a CPU or a + * group of CPUs. The CPU clock is typically derived from a hierarchy of clock + * blocks which includes mux and divider blocks. There are a number of other + * auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI + * clock for CPU domain. The rates of these auxiliary clocks are related to the + * CPU clock rate and this relation is usually specified in the hardware manual + * of the SoC or supplied after the SoC characterization. + * + * The below implementation of the CPU clock allows the rate changes of the CPU + * clock and the corresponding rate changes of the auxillary clocks of the CPU + * domain. The platform clock driver provides a clock register configuration + * for each configurable rate which is then used to program the clock hardware + * registers to acheive a fast co-oridinated rate change for all the CPU domain + * clocks. + * + * On a rate change request for the CPU clock, the rate change is propagated + * upto the PLL supplying the clock to the CPU domain clock blocks. While the + * CPU domain PLL is reconfigured, the CPU domain clocks are driven using an + * alternate clock source. If required, the alternate clock source is divided + * down in order to keep the output clock rate within the previous OPP limits. +*/ + +#include +#include "clk-cpu.h" + +#define E4210_SRC_CPU 0x0 +#define E4210_STAT_CPU 0x200 +#define E4210_DIV_CPU0 0x300 +#define E4210_DIV_CPU1 0x304 +#define E4210_DIV_STAT_CPU0 0x400 +#define E4210_DIV_STAT_CPU1 0x404 + +#define E4210_DIV0_RATIO0_MASK 0x7 +#define E4210_DIV1_HPM_MASK (0x7 << 4) +#define E4210_DIV1_COPY_MASK (0x7 << 0) +#define E4210_MUX_HPM_MASK (1 << 20) +#define E4210_DIV0_ATB_SHIFT 16 +#define E4210_DIV0_ATB_MASK (DIV_MASK << E4210_DIV0_ATB_SHIFT) + +#define MAX_DIV 8 +#define DIV_MASK 7 +#define DIV_MASK_ALL 0xffffffff +#define MUX_MASK 7 + +/* + * Helper function to wait until divider(s) have stabilized after the divider + * value has changed. + */ +static void wait_until_divider_stable(void __iomem *div_reg, unsigned long mask) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(10); + + do { + if (!(readl(div_reg) & mask)) + return; + } while (time_before(jiffies, timeout)); + + if (!(readl(div_reg) & mask)) + return; + + pr_err("%s: timeout in divider stablization\n", __func__); +} + +/* + * Helper function to wait until mux has stabilized after the mux selection + * value was changed. + */ +static void wait_until_mux_stable(void __iomem *mux_reg, u32 mux_pos, + unsigned long mux_value) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(10); + + do { + if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value) + return; + } while (time_before(jiffies, timeout)); + + if (((readl(mux_reg) >> mux_pos) & MUX_MASK) == mux_value) + return; + + pr_err("%s: re-parenting mux timed-out\n", __func__); +} + +/* common round rate callback useable for all types of CPU clocks */ +static long exynos_cpuclk_round_rate(struct clk_hw *hw, + unsigned long drate, unsigned long *prate) +{ + struct clk *parent = __clk_get_parent(hw->clk); + *prate = __clk_round_rate(parent, drate); + return *prate; +} + +/* common recalc rate callback useable for all types of CPU clocks */ +static unsigned long exynos_cpuclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + /* + * The CPU clock output (armclk) rate is the same as its parent + * rate. Although there exist certain dividers inside the CPU + * clock block that could be used to divide the parent clock, + * the driver does not make use of them currently, except during + * frequency transitions. + */ + return parent_rate; +} + +static const struct clk_ops exynos_cpuclk_clk_ops = { + .recalc_rate = exynos_cpuclk_recalc_rate, + .round_rate = exynos_cpuclk_round_rate, +}; + +/* + * Helper function to set the 'safe' dividers for the CPU clock. The parameters + * div and mask contain the divider value and the register bit mask of the + * dividers to be programmed. + */ +static void exynos_set_safe_div(void __iomem *base, unsigned long div, + unsigned long mask) +{ + unsigned long div0; + + div0 = readl(base + E4210_DIV_CPU0); + div0 = (div0 & ~mask) | (div & mask); + writel(div0, base + E4210_DIV_CPU0); + wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, mask); +} + +/* handler for pre-rate change notification from parent clock */ +static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata, + struct exynos_cpuclk *cpuclk, void __iomem *base) +{ + const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg; + unsigned long alt_prate = clk_get_rate(cpuclk->alt_parent); + unsigned long alt_div = 0, alt_div_mask = DIV_MASK; + unsigned long div0, div1 = 0, mux_reg; + + /* find out the divider values to use for clock data */ + while ((cfg_data->prate * 1000) != ndata->new_rate) { + if (cfg_data->prate == 0) + return -EINVAL; + cfg_data++; + } + + spin_lock(cpuclk->lock); + + /* + * For the selected PLL clock frequency, get the pre-defined divider + * values. If the clock for sclk_hpm is not sourced from apll, then + * the values for DIV_COPY and DIV_HPM dividers need not be set. + */ + div0 = cfg_data->div0; + if (test_bit(CLK_CPU_HAS_DIV1, &cpuclk->flags)) { + div1 = cfg_data->div1; + if (readl(base + E4210_SRC_CPU) & E4210_MUX_HPM_MASK) + div1 = readl(base + E4210_DIV_CPU1) & + (E4210_DIV1_HPM_MASK | E4210_DIV1_COPY_MASK); + } + + /* + * If the old parent clock speed is less than the clock speed of + * the alternate parent, then it should be ensured that at no point + * the armclk speed is more than the old_prate until the dividers are + * set. Also workaround the issue of the dividers being set to lower + * values before the parent clock speed is set to new lower speed + * (this can result in too high speed of armclk output clocks). + */ + if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) { + unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate); + + alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1; + WARN_ON(alt_div >= MAX_DIV); + + if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) { + /* + * In Exynos4210, ATB clock parent is also mout_core. So + * ATB clock also needs to be mantained at safe speed. + */ + alt_div |= E4210_DIV0_ATB_MASK; + alt_div_mask |= E4210_DIV0_ATB_MASK; + } + exynos_set_safe_div(base, alt_div, alt_div_mask); + div0 |= alt_div; + } + + /* select sclk_mpll as the alternate parent */ + mux_reg = readl(base + E4210_SRC_CPU); + writel(mux_reg | (1 << 16), base + E4210_SRC_CPU); + wait_until_mux_stable(base + E4210_STAT_CPU, 16, 2); + + /* alternate parent is active now. set the dividers */ + writel(div0, base + E4210_DIV_CPU0); + wait_until_divider_stable(base + E4210_DIV_STAT_CPU0, DIV_MASK_ALL); + + if (test_bit(CLK_CPU_HAS_DIV1, &cpuclk->flags)) { + writel(div1, base + E4210_DIV_CPU1); + wait_until_divider_stable(base + E4210_DIV_STAT_CPU1, + DIV_MASK_ALL); + } + + spin_unlock(cpuclk->lock); + return 0; +} + +/* handler for post-rate change notification from parent clock */ +static int exynos_cpuclk_post_rate_change(struct clk_notifier_data *ndata, + struct exynos_cpuclk *cpuclk, void __iomem *base) +{ + const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg; + unsigned long div = 0, div_mask = DIV_MASK; + unsigned long mux_reg; + + /* find out the divider values to use for clock data */ + if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) { + while ((cfg_data->prate * 1000) != ndata->new_rate) { + if (cfg_data->prate == 0) + return -EINVAL; + cfg_data++; + } + } + + spin_lock(cpuclk->lock); + + /* select mout_apll as the alternate parent */ + mux_reg = readl(base + E4210_SRC_CPU); + writel(mux_reg & ~(1 << 16), base + E4210_SRC_CPU); + wait_until_mux_stable(base + E4210_STAT_CPU, 16, 1); + + if (test_bit(CLK_CPU_NEEDS_DEBUG_ALT_DIV, &cpuclk->flags)) { + div |= (cfg_data->div0 & E4210_DIV0_ATB_MASK); + div_mask |= E4210_DIV0_ATB_MASK; + } + + exynos_set_safe_div(base, div, div_mask); + spin_unlock(cpuclk->lock); + return 0; +} + +/* + * This notifier function is called for the pre-rate and post-rate change + * notifications of the parent clock of cpuclk. + */ +static int exynos_cpuclk_notifier_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct clk_notifier_data *ndata = data; + struct exynos_cpuclk *cpuclk; + void __iomem *base; + int err = 0; + + cpuclk = container_of(nb, struct exynos_cpuclk, clk_nb); + base = cpuclk->ctrl_base; + + if (event == PRE_RATE_CHANGE) + err = exynos_cpuclk_pre_rate_change(ndata, cpuclk, base); + else if (event == POST_RATE_CHANGE) + err = exynos_cpuclk_post_rate_change(ndata, cpuclk, base); + + return notifier_from_errno(err); +} + +/* helper function to register a CPU clock */ +int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx, + unsigned int lookup_id, const char *name, const char *parent, + const char *alt_parent, unsigned long offset, + const struct exynos_cpuclk_cfg_data *cfg, + unsigned long num_cfgs, unsigned long flags) +{ + struct exynos_cpuclk *cpuclk; + struct clk_init_data init; + struct clk *clk; + int ret = 0; + + cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL); + if (!cpuclk) + return -ENOMEM; + + init.name = name; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = &parent; + init.num_parents = 1; + init.ops = &exynos_cpuclk_clk_ops; + + cpuclk->hw.init = &init; + cpuclk->ctrl_base = ctx->reg_base + offset; + cpuclk->lock = &ctx->lock; + cpuclk->flags = flags; + cpuclk->clk_nb.notifier_call = exynos_cpuclk_notifier_cb; + + cpuclk->alt_parent = __clk_lookup(alt_parent); + if (!cpuclk->alt_parent) { + pr_err("%s: could not lookup alternate parent %s\n", + __func__, alt_parent); + ret = -EINVAL; + goto free_cpuclk; + } + + clk = __clk_lookup(parent); + if (!clk) { + pr_err("%s: could not lookup parent clock %s\n", + __func__, parent); + ret = -EINVAL; + goto free_cpuclk; + } + + ret = clk_notifier_register(clk, &cpuclk->clk_nb); + if (ret) { + pr_err("%s: failed to register clock notifier for %s\n", + __func__, name); + goto free_cpuclk; + } + + cpuclk->cfg = kmemdup(cfg, sizeof(*cfg) * num_cfgs, GFP_KERNEL); + if (!cpuclk->cfg) { + pr_err("%s: could not allocate memory for cpuclk data\n", + __func__); + ret = -ENOMEM; + goto unregister_clk_nb; + } + + clk = clk_register(NULL, &cpuclk->hw); + if (IS_ERR(clk)) { + pr_err("%s: could not register cpuclk %s\n", __func__, name); + ret = PTR_ERR(clk); + goto free_cpuclk_data; + } + + samsung_clk_add_lookup(ctx, clk, lookup_id); + return 0; + +free_cpuclk_data: + kfree(cpuclk->cfg); +unregister_clk_nb: + clk_notifier_unregister(__clk_lookup(parent), &cpuclk->clk_nb); +free_cpuclk: + kfree(cpuclk); + return ret; +} diff --git a/drivers/clk/samsung/clk-cpu.h b/drivers/clk/samsung/clk-cpu.h new file mode 100644 index 000000000000..37874d3c3165 --- /dev/null +++ b/drivers/clk/samsung/clk-cpu.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Common Clock Framework support for all PLL's in Samsung platforms +*/ + +#ifndef __SAMSUNG_CLK_CPU_H +#define __SAMSUNG_CLK_CPU_H + +#include "clk.h" + +/** + * struct exynos_cpuclk_data: config data to setup cpu clocks. + * @prate: frequency of the primary parent clock (in KHz). + * @div0: value to be programmed in the div_cpu0 register. + * @div1: value to be programmed in the div_cpu1 register. + * + * This structure holds the divider configuration data for dividers in the CPU + * clock domain. The parent frequency at which these divider values are valid is + * specified in @prate. The @prate is the frequency of the primary parent clock. + * For CPU clock domains that do not have a DIV1 register, the @div1 member + * value is not used. + */ +struct exynos_cpuclk_cfg_data { + unsigned long prate; + unsigned long div0; + unsigned long div1; +}; + +/** + * struct exynos_cpuclk: information about clock supplied to a CPU core. + * @hw: handle between CCF and CPU clock. + * @alt_parent: alternate parent clock to use when switching the speed + * of the primary parent clock. + * @ctrl_base: base address of the clock controller. + * @lock: cpu clock domain register access lock. + * @cfg: cpu clock rate configuration data. + * @num_cfgs: number of array elements in @cfg array. + * @clk_nb: clock notifier registered for changes in clock speed of the + * primary parent clock. + * @flags: configuration flags for the CPU clock. + * + * This structure holds information required for programming the CPU clock for + * various clock speeds. + */ +struct exynos_cpuclk { + struct clk_hw hw; + struct clk *alt_parent; + void __iomem *ctrl_base; + spinlock_t *lock; + const struct exynos_cpuclk_cfg_data *cfg; + const unsigned long num_cfgs; + struct notifier_block clk_nb; + unsigned long flags; + +/* The CPU clock registers has DIV1 configuration register */ +#define CLK_CPU_HAS_DIV1 (1 << 0) +/* When ALT parent is active, debug clocks need safe divider values */ +#define CLK_CPU_NEEDS_DEBUG_ALT_DIV (1 << 1) +}; + +extern int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx, + unsigned int lookup_id, const char *name, + const char *parent, const char *alt_parent, + unsigned long offset, + const struct exynos_cpuclk_cfg_data *cfg, + unsigned long num_cfgs, unsigned long flags); + +#endif /* __SAMSUNG_CLK_CPU_H */ -- cgit v1.2.3-58-ga151 From 6ae5a0b46fd82055ad5950539e1b36e4922c233f Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Fri, 3 Apr 2015 18:43:46 +0200 Subject: clk: samsung: exynos4: add cpu clock configuration data and instantiate cpu clock With the addition of the new Samsung specific cpu-clock type, the arm clock can be represented as a cpu-clock type. Add the CPU clock configuration data and instantiate the CPU clock type for Exynos4210. Changes by Bartlomiej: - fixed issue with wrong dividers being setup by Common Clock Framework (by an addition of CLK_RECALC_NEW_RATES clock flag to mout_apll clock, without this change cpufreq-dt driver showed ~10 mA larger energy consumption when compared to cpufreq-exynos one when "performance" cpufreq governor was used on Exynos4210 SoC based Origen board), this was probably meant to be workarounded by use of CLK_GET_RATE_NOCACHE and CLK_DIVIDER_READ_ONLY clock flags in the original patchset (in "[PATCH v12 6/6] clk: samsung: remove unused clock aliases and update clock flags") but using these flags is not sufficient to fix the issue observed - removed Exynos5250 and Exynos5420 support for now Cc: Tomasz Figa Cc: Mike Turquette Cc: Javier Martinez Canillas Signed-off-by: Thomas Abraham Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Michael Turquette --- drivers/clk/samsung/clk-exynos4.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/clk/samsung/clk-exynos4.c b/drivers/clk/samsung/clk-exynos4.c index 714d6ba782c8..cae2c048488d 100644 --- a/drivers/clk/samsung/clk-exynos4.c +++ b/drivers/clk/samsung/clk-exynos4.c @@ -19,6 +19,7 @@ #include #include "clk.h" +#include "clk-cpu.h" /* Exynos4 clock controller register offsets */ #define SRC_LEFTBUS 0x4200 @@ -534,7 +535,8 @@ static struct samsung_fixed_factor_clock exynos4x12_fixed_factor_clks[] __initda /* list of mux clocks supported in all exynos4 soc's */ static struct samsung_mux_clock exynos4_mux_clks[] __initdata = { MUX_FA(CLK_MOUT_APLL, "mout_apll", mout_apll_p, SRC_CPU, 0, 1, - CLK_SET_RATE_PARENT, 0, "mout_apll"), + CLK_SET_RATE_PARENT | CLK_RECALC_NEW_RATES, 0, + "mout_apll"), MUX(CLK_MOUT_HDMI, "mout_hdmi", mout_hdmi_p, SRC_TV, 0, 1), MUX(0, "mout_mfc1", sclk_evpll_p, SRC_MFC, 4, 1), MUX(0, "mout_mfc", mout_mfc_p, SRC_MFC, 8, 1), @@ -1378,6 +1380,22 @@ static void __init exynos4x12_core_down_clock(void) __raw_writel(0x0, reg_base + E4X12_PWR_CTRL2); } +#define E4210_CPU_DIV0(apll, pclk_dbg, atb, periph, corem1, corem0) \ + (((apll) << 24) | ((pclk_dbg) << 20) | ((atb) << 16) | \ + ((periph) << 12) | ((corem1) << 8) | ((corem0) << 4)) +#define E4210_CPU_DIV1(hpm, copy) \ + (((hpm) << 4) | ((copy) << 0)) + +static const struct exynos_cpuclk_cfg_data e4210_armclk_d[] __initconst = { + { 1200000, E4210_CPU_DIV0(7, 1, 4, 3, 7, 3), E4210_CPU_DIV1(0, 5), }, + { 1000000, E4210_CPU_DIV0(7, 1, 4, 3, 7, 3), E4210_CPU_DIV1(0, 4), }, + { 800000, E4210_CPU_DIV0(7, 1, 3, 3, 7, 3), E4210_CPU_DIV1(0, 3), }, + { 500000, E4210_CPU_DIV0(7, 1, 3, 3, 7, 3), E4210_CPU_DIV1(0, 3), }, + { 400000, E4210_CPU_DIV0(7, 1, 3, 3, 7, 3), E4210_CPU_DIV1(0, 3), }, + { 200000, E4210_CPU_DIV0(0, 1, 1, 1, 3, 1), E4210_CPU_DIV1(0, 3), }, + { 0 }, +}; + /* register exynos4 clocks */ static void __init exynos4_clk_init(struct device_node *np, enum exynos4_soc soc) @@ -1455,6 +1473,10 @@ static void __init exynos4_clk_init(struct device_node *np, samsung_clk_register_fixed_factor(ctx, exynos4210_fixed_factor_clks, ARRAY_SIZE(exynos4210_fixed_factor_clks)); + exynos_register_cpu_clock(ctx, CLK_ARM_CLK, "armclk", + mout_core_p4210[0], mout_core_p4210[1], 0x14200, + e4210_armclk_d, ARRAY_SIZE(e4210_armclk_d), + CLK_CPU_NEEDS_DEBUG_ALT_DIV | CLK_CPU_HAS_DIV1); } else { samsung_clk_register_mux(ctx, exynos4x12_mux_clks, ARRAY_SIZE(exynos4x12_mux_clks)); -- cgit v1.2.3-58-ga151 From 131323cd16e563499caedc93a9c2f82fce4c966c Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Fri, 3 Apr 2015 18:43:48 +0200 Subject: ARM: Exynos: switch to using generic cpufreq driver for Exynos4210 The new CPU clock type allows the use of generic CPUfreq driver. Switch Exynos4210 to using generic cpufreq driver. Changes by Bartlomiej: - removed non-Exynos4210 support for now Cc: Tomasz Figa Cc: Kukjin Kim Cc: Javier Martinez Canillas Acked-by: Viresh Kumar Signed-off-by: Thomas Abraham Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Michael Turquette --- arch/arm/mach-exynos/exynos.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index 5917a30eee33..9cb17ff1e152 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -224,6 +224,25 @@ static void __init exynos_init_irq(void) exynos_map_pmu(); } +static const struct of_device_id exynos_cpufreq_matches[] = { + { .compatible = "samsung,exynos4210", .data = "cpufreq-dt" }, + { /* sentinel */ } +}; + +static void __init exynos_cpufreq_init(void) +{ + struct device_node *root = of_find_node_by_path("/"); + const struct of_device_id *match; + + match = of_match_node(exynos_cpufreq_matches, root); + if (!match) { + platform_device_register_simple("exynos-cpufreq", -1, NULL, 0); + return; + } + + platform_device_register_simple(match->data, -1, NULL, 0); +} + static void __init exynos_dt_machine_init(void) { /* @@ -245,7 +264,7 @@ static void __init exynos_dt_machine_init(void) of_machine_is_compatible("samsung,exynos5250")) platform_device_register(&exynos_cpuidle); - platform_device_register_simple("exynos-cpufreq", -1, NULL, 0); + exynos_cpufreq_init(); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } -- cgit v1.2.3-58-ga151 From 8eb92ab68f961bb9045d8d7882cceb2d6be0659d Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Fri, 3 Apr 2015 18:43:49 +0200 Subject: cpufreq: exynos: remove Exynos4210 specific cpufreq driver support Exynos4210 based platforms have switched over to use generic cpufreq driver for cpufreq functionality. So the Exynos specific cpufreq support for these platforms can be removed. Changes by Bartlomiej: - dropped Exynos5250 support removal for now - updated exynos-cpufreq.[c,h] Cc: Javier Martinez Canillas Acked-by: Viresh Kumar Signed-off-by: Thomas Abraham Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Michael Turquette --- drivers/cpufreq/Kconfig.arm | 11 --- drivers/cpufreq/Makefile | 1 - drivers/cpufreq/exynos-cpufreq.c | 5 +- drivers/cpufreq/exynos-cpufreq.h | 9 -- drivers/cpufreq/exynos4210-cpufreq.c | 184 ----------------------------------- 5 files changed, 1 insertion(+), 209 deletions(-) delete mode 100644 drivers/cpufreq/exynos4210-cpufreq.c diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 4f3dbc8cf729..a86a196e32f6 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -36,17 +36,6 @@ config ARM_EXYNOS_CPUFREQ If in doubt, say N. -config ARM_EXYNOS4210_CPUFREQ - bool "SAMSUNG EXYNOS4210" - depends on CPU_EXYNOS4210 - depends on ARM_EXYNOS_CPUFREQ - default y - help - This adds the CPUFreq driver for Samsung EXYNOS4210 - SoC (S5PV310 or S5PC210). - - If in doubt, say N. - config ARM_EXYNOS4X12_CPUFREQ bool "SAMSUNG EXYNOS4x12" depends on SOC_EXYNOS4212 || SOC_EXYNOS4412 diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index cdce92ae2e8b..2169bf792db7 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -54,7 +54,6 @@ obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-cpufreq.o obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += arm-exynos-cpufreq.o arm-exynos-cpufreq-y := exynos-cpufreq.o -arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index 82d2fbb20f7e..fb24aaf4adcf 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -168,10 +168,7 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) exynos_info->dev = &pdev->dev; - if (of_machine_is_compatible("samsung,exynos4210")) { - exynos_info->type = EXYNOS_SOC_4210; - ret = exynos4210_cpufreq_init(exynos_info); - } else if (of_machine_is_compatible("samsung,exynos4212")) { + if (of_machine_is_compatible("samsung,exynos4212")) { exynos_info->type = EXYNOS_SOC_4212; ret = exynos4x12_cpufreq_init(exynos_info); } else if (of_machine_is_compatible("samsung,exynos4412")) { diff --git a/drivers/cpufreq/exynos-cpufreq.h b/drivers/cpufreq/exynos-cpufreq.h index 9f2062a7cc02..a3855e4d913d 100644 --- a/drivers/cpufreq/exynos-cpufreq.h +++ b/drivers/cpufreq/exynos-cpufreq.h @@ -18,7 +18,6 @@ enum cpufreq_level_index { }; enum exynos_soc_type { - EXYNOS_SOC_4210, EXYNOS_SOC_4212, EXYNOS_SOC_4412, EXYNOS_SOC_5250, @@ -53,14 +52,6 @@ struct exynos_dvfs_info { void __iomem *cmu_regs; }; -#ifdef CONFIG_ARM_EXYNOS4210_CPUFREQ -extern int exynos4210_cpufreq_init(struct exynos_dvfs_info *); -#else -static inline int exynos4210_cpufreq_init(struct exynos_dvfs_info *info) -{ - return -EOPNOTSUPP; -} -#endif #ifdef CONFIG_ARM_EXYNOS4X12_CPUFREQ extern int exynos4x12_cpufreq_init(struct exynos_dvfs_info *); #else diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c deleted file mode 100644 index 843ec824fd91..000000000000 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * EXYNOS4210 - CPU frequency scaling support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "exynos-cpufreq.h" - -static struct clk *cpu_clk; -static struct clk *moutcore; -static struct clk *mout_mpll; -static struct clk *mout_apll; -static struct exynos_dvfs_info *cpufreq; - -static unsigned int exynos4210_volt_table[] = { - 1250000, 1150000, 1050000, 975000, 950000, -}; - -static struct cpufreq_frequency_table exynos4210_freq_table[] = { - {0, L0, 1200 * 1000}, - {0, L1, 1000 * 1000}, - {0, L2, 800 * 1000}, - {0, L3, 500 * 1000}, - {0, L4, 200 * 1000}, - {0, 0, CPUFREQ_TABLE_END}, -}; - -static struct apll_freq apll_freq_4210[] = { - /* - * values: - * freq - * clock divider for CORE, COREM0, COREM1, PERIPH, ATB, PCLK_DBG, APLL, RESERVED - * clock divider for COPY, HPM, RESERVED - * PLL M, P, S - */ - APLL_FREQ(1200, 0, 3, 7, 3, 4, 1, 7, 0, 5, 0, 0, 150, 3, 1), - APLL_FREQ(1000, 0, 3, 7, 3, 4, 1, 7, 0, 4, 0, 0, 250, 6, 1), - APLL_FREQ(800, 0, 3, 7, 3, 3, 1, 7, 0, 3, 0, 0, 200, 6, 1), - APLL_FREQ(500, 0, 3, 7, 3, 3, 1, 7, 0, 3, 0, 0, 250, 6, 2), - APLL_FREQ(200, 0, 1, 3, 1, 3, 1, 0, 0, 3, 0, 0, 200, 6, 3), -}; - -static void exynos4210_set_clkdiv(unsigned int div_index) -{ - unsigned int tmp; - - /* Change Divider - CPU0 */ - - tmp = apll_freq_4210[div_index].clk_div_cpu0; - - __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU); - - do { - tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU); - } while (tmp & 0x1111111); - - /* Change Divider - CPU1 */ - - tmp = apll_freq_4210[div_index].clk_div_cpu1; - - __raw_writel(tmp, cpufreq->cmu_regs + EXYNOS4_CLKDIV_CPU1); - - do { - tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKDIV_STATCPU1); - } while (tmp & 0x11); -} - -static void exynos4210_set_apll(unsigned int index) -{ - unsigned int tmp, freq = apll_freq_4210[index].freq; - - /* MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */ - clk_set_parent(moutcore, mout_mpll); - - do { - tmp = (__raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU) - >> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT); - tmp &= 0x7; - } while (tmp != 0x2); - - clk_set_rate(mout_apll, freq * 1000); - - /* MUX_CORE_SEL = APLL */ - clk_set_parent(moutcore, mout_apll); - - do { - tmp = __raw_readl(cpufreq->cmu_regs + EXYNOS4_CLKMUX_STATCPU); - tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK; - } while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT)); -} - -static void exynos4210_set_frequency(unsigned int old_index, - unsigned int new_index) -{ - if (old_index > new_index) { - exynos4210_set_clkdiv(new_index); - exynos4210_set_apll(new_index); - } else if (old_index < new_index) { - exynos4210_set_apll(new_index); - exynos4210_set_clkdiv(new_index); - } -} - -int exynos4210_cpufreq_init(struct exynos_dvfs_info *info) -{ - struct device_node *np; - unsigned long rate; - - /* - * HACK: This is a temporary workaround to get access to clock - * controller registers directly and remove static mappings and - * dependencies on platform headers. It is necessary to enable - * Exynos multi-platform support and will be removed together with - * this whole driver as soon as Exynos gets migrated to use - * cpufreq-dt driver. - */ - np = of_find_compatible_node(NULL, NULL, "samsung,exynos4210-clock"); - if (!np) { - pr_err("%s: failed to find clock controller DT node\n", - __func__); - return -ENODEV; - } - - info->cmu_regs = of_iomap(np, 0); - if (!info->cmu_regs) { - pr_err("%s: failed to map CMU registers\n", __func__); - return -EFAULT; - } - - cpu_clk = clk_get(NULL, "armclk"); - if (IS_ERR(cpu_clk)) - return PTR_ERR(cpu_clk); - - moutcore = clk_get(NULL, "moutcore"); - if (IS_ERR(moutcore)) - goto err_moutcore; - - mout_mpll = clk_get(NULL, "mout_mpll"); - if (IS_ERR(mout_mpll)) - goto err_mout_mpll; - - rate = clk_get_rate(mout_mpll) / 1000; - - mout_apll = clk_get(NULL, "mout_apll"); - if (IS_ERR(mout_apll)) - goto err_mout_apll; - - info->mpll_freq_khz = rate; - /* 800Mhz */ - info->pll_safe_idx = L2; - info->cpu_clk = cpu_clk; - info->volt_table = exynos4210_volt_table; - info->freq_table = exynos4210_freq_table; - info->set_freq = exynos4210_set_frequency; - - cpufreq = info; - - return 0; - -err_mout_apll: - clk_put(mout_mpll); -err_mout_mpll: - clk_put(moutcore); -err_moutcore: - clk_put(cpu_clk); - - pr_debug("%s: failed initialization\n", __func__); - return -EINVAL; -} -- cgit v1.2.3-58-ga151 From 41655239eaed741ac8da066bc43c2483c78e61ec Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Wed, 10 Jun 2015 21:09:36 +0100 Subject: dt-bindings: Document the STM32F4 clock bindings This adds documentation of device tree bindings for the clock related portions of the STM32 RCC block. Signed-off-by: Daniel Thompson Acked-by: Maxime Coquelin Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/st,stm32-rcc.txt | 65 ++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/st,stm32-rcc.txt diff --git a/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt b/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt new file mode 100644 index 000000000000..fee3205cdff9 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/st,stm32-rcc.txt @@ -0,0 +1,65 @@ +STMicroelectronics STM32 Reset and Clock Controller +=================================================== + +The RCC IP is both a reset and a clock controller. This documentation only +describes the clock part. + +Please also refer to clock-bindings.txt in this directory for common clock +controller binding usage. + +Required properties: +- compatible: Should be "st,stm32f42xx-rcc" +- reg: should be register base and length as documented in the + datasheet +- #clock-cells: 2, device nodes should specify the clock in their "clocks" + property, containing a phandle to the clock device node, an index selecting + between gated clocks and other clocks and an index specifying the clock to + use. + +Example: + + rcc: rcc@40023800 { + #clock-cells = <2> + compatible = "st,stm32f42xx-rcc", "st,stm32-rcc"; + reg = <0x40023800 0x400>; + }; + +Specifying gated clocks +======================= + +The primary index must be set to 0. + +The secondary index is the bit number within the RCC register bank, starting +from the first RCC clock enable register (RCC_AHB1ENR, address offset 0x30). + +It is calculated as: index = register_offset / 4 * 32 + bit_offset. +Where bit_offset is the bit offset within the register (LSB is 0, MSB is 31). + +Example: + + /* Gated clock, AHB1 bit 0 (GPIOA) */ + ... { + clocks = <&rcc 0 0> + }; + + /* Gated clock, AHB2 bit 4 (CRYP) */ + ... { + clocks = <&rcc 0 36> + }; + +Specifying other clocks +======================= + +The primary index must be set to 1. + +The secondary index is bound with the following magic numbers: + + 0 SYSTICK + 1 FCLK + +Example: + + /* Misc clock, FCLK */ + ... { + clocks = <&rcc 1 1> + }; -- cgit v1.2.3-58-ga151 From 358bdf892f6bfacf20884b54a35ab038321f06f9 Mon Sep 17 00:00:00 2001 From: Daniel Thompson Date: Wed, 10 Jun 2015 21:09:37 +0100 Subject: clk: stm32: Add clock driver for STM32F4[23]xxx devices The driver supports decoding and statically modelling PLL state (i.e. we inherit state from bootloader) and provides support for all peripherals that support simple one-bit gated clocks. The covers all peripherals whose clocks come from the AHB, APB1 or APB2 buses. It has been tested on an STM32F429I-Discovery board. The clock counts for TIM2, USART1 and SYSTICK are all set correctly and the wall clock looks OK when checked with a stopwatch. I have also tested a prototype driver for the RNG hardware. The RNG clock is correctly enabled by the framework (also did inverse test and proved that by changing DT to configure the wrong clock bit then we observe the RNG driver to fail). Signed-off-by: Daniel Thompson Reviewed-by: Maxime Coquelin [sboyd@codeaurora.org: Silence sparse warnings] Signed-off-by: Stephen Boyd --- drivers/clk/Makefile | 1 + drivers/clk/clk-stm32f4.c | 380 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 381 insertions(+) create mode 100644 drivers/clk/clk-stm32f4.c diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 256977ed050a..fc789a07cb5f 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o obj-$(CONFIG_COMMON_CLK_SI5351) += clk-si5351.o obj-$(CONFIG_COMMON_CLK_SI570) += clk-si570.o obj-$(CONFIG_COMMON_CLK_CDCE925) += clk-cdce925.o +obj-$(CONFIG_ARCH_STM32) += clk-stm32f4.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o obj-$(CONFIG_ARCH_U300) += clk-u300.o obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o diff --git a/drivers/clk/clk-stm32f4.c b/drivers/clk/clk-stm32f4.c new file mode 100644 index 000000000000..b9b12a742970 --- /dev/null +++ b/drivers/clk/clk-stm32f4.c @@ -0,0 +1,380 @@ +/* + * Author: Daniel Thompson + * + * Inspired by clk-asm9260.c . + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define STM32F4_RCC_PLLCFGR 0x04 +#define STM32F4_RCC_CFGR 0x08 +#define STM32F4_RCC_AHB1ENR 0x30 +#define STM32F4_RCC_AHB2ENR 0x34 +#define STM32F4_RCC_AHB3ENR 0x38 +#define STM32F4_RCC_APB1ENR 0x40 +#define STM32F4_RCC_APB2ENR 0x44 + +struct stm32f4_gate_data { + u8 offset; + u8 bit_idx; + const char *name; + const char *parent_name; + unsigned long flags; +}; + +static const struct stm32f4_gate_data stm32f4_gates[] __initconst = { + { STM32F4_RCC_AHB1ENR, 0, "gpioa", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 1, "gpiob", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 2, "gpioc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 3, "gpiod", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 4, "gpioe", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 5, "gpiof", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 6, "gpiog", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 7, "gpioh", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 8, "gpioi", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 9, "gpioj", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 10, "gpiok", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 12, "crc", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 18, "bkpsra", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 20, "ccmdatam", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 21, "dma1", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 22, "dma2", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 23, "dma2d", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 25, "ethmac", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 26, "ethmactx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 27, "ethmacrx", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 28, "ethmacptp", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 29, "otghs", "ahb_div" }, + { STM32F4_RCC_AHB1ENR, 30, "otghsulpi", "ahb_div" }, + + { STM32F4_RCC_AHB2ENR, 0, "dcmi", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 4, "cryp", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 5, "hash", "ahb_div" }, + { STM32F4_RCC_AHB2ENR, 6, "rng", "pll48" }, + { STM32F4_RCC_AHB2ENR, 7, "otgfs", "pll48" }, + + { STM32F4_RCC_AHB3ENR, 0, "fmc", "ahb_div", + CLK_IGNORE_UNUSED }, + + { STM32F4_RCC_APB1ENR, 0, "tim2", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 1, "tim3", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 2, "tim4", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 3, "tim5", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 4, "tim6", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 5, "tim7", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 6, "tim12", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 7, "tim13", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 8, "tim14", "apb1_mul" }, + { STM32F4_RCC_APB1ENR, 11, "wwdg", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 14, "spi2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 15, "spi3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 17, "uart2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 18, "uart3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 19, "uart4", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 20, "uart5", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 21, "i2c1", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 22, "i2c2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 23, "i2c3", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 25, "can1", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 26, "can2", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 28, "pwr", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 29, "dac", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 30, "uart7", "apb1_div" }, + { STM32F4_RCC_APB1ENR, 31, "uart8", "apb1_div" }, + + { STM32F4_RCC_APB2ENR, 0, "tim1", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 1, "tim8", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 4, "usart1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 5, "usart6", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 8, "adc1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 9, "adc2", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 10, "adc3", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 11, "sdio", "pll48" }, + { STM32F4_RCC_APB2ENR, 12, "spi1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 13, "spi4", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 14, "syscfg", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 16, "tim9", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 17, "tim10", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 18, "tim11", "apb2_mul" }, + { STM32F4_RCC_APB2ENR, 20, "spi5", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 21, "spi6", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 22, "sai1", "apb2_div" }, + { STM32F4_RCC_APB2ENR, 26, "ltdc", "apb2_div" }, +}; + +/* + * MAX_CLKS is the maximum value in the enumeration below plus the combined + * hweight of stm32f42xx_gate_map (plus one). + */ +#define MAX_CLKS 74 + +enum { SYSTICK, FCLK }; + +/* + * This bitmask tells us which bit offsets (0..192) on STM32F4[23]xxx + * have gate bits associated with them. Its combined hweight is 71. + */ +static const u64 stm32f42xx_gate_map[] = { 0x000000f17ef417ffull, + 0x0000000000000001ull, + 0x04777f33f6fec9ffull }; + +static struct clk *clks[MAX_CLKS]; +static DEFINE_SPINLOCK(stm32f4_clk_lock); +static void __iomem *base; + +/* + * "Multiplier" device for APBx clocks. + * + * The APBx dividers are power-of-two dividers and, if *not* running in 1:1 + * mode, they also tap out the one of the low order state bits to run the + * timers. ST datasheets represent this feature as a (conditional) clock + * multiplier. + */ +struct clk_apb_mul { + struct clk_hw hw; + u8 bit_idx; +}; + +#define to_clk_apb_mul(_hw) container_of(_hw, struct clk_apb_mul, hw) + +static unsigned long clk_apb_mul_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_apb_mul *am = to_clk_apb_mul(hw); + + if (readl(base + STM32F4_RCC_CFGR) & BIT(am->bit_idx)) + return parent_rate * 2; + + return parent_rate; +} + +static long clk_apb_mul_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_apb_mul *am = to_clk_apb_mul(hw); + unsigned long mult = 1; + + if (readl(base + STM32F4_RCC_CFGR) & BIT(am->bit_idx)) + mult = 2; + + if (__clk_get_flags(hw->clk) & CLK_SET_RATE_PARENT) { + unsigned long best_parent = rate / mult; + + *prate = + __clk_round_rate(__clk_get_parent(hw->clk), best_parent); + } + + return *prate * mult; +} + +static int clk_apb_mul_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + /* + * We must report success but we can do so unconditionally because + * clk_apb_mul_round_rate returns values that ensure this call is a + * nop. + */ + + return 0; +} + +static const struct clk_ops clk_apb_mul_factor_ops = { + .round_rate = clk_apb_mul_round_rate, + .set_rate = clk_apb_mul_set_rate, + .recalc_rate = clk_apb_mul_recalc_rate, +}; + +static struct clk *clk_register_apb_mul(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, u8 bit_idx) +{ + struct clk_apb_mul *am; + struct clk_init_data init; + struct clk *clk; + + am = kzalloc(sizeof(*am), GFP_KERNEL); + if (!am) + return ERR_PTR(-ENOMEM); + + am->bit_idx = bit_idx; + am->hw.init = &init; + + init.name = name; + init.ops = &clk_apb_mul_factor_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(dev, &am->hw); + + if (IS_ERR(clk)) + kfree(am); + + return clk; +} + +/* + * Decode current PLL state and (statically) model the state we inherit from + * the bootloader. + */ +static void stm32f4_rcc_register_pll(const char *hse_clk, const char *hsi_clk) +{ + unsigned long pllcfgr = readl(base + STM32F4_RCC_PLLCFGR); + + unsigned long pllm = pllcfgr & 0x3f; + unsigned long plln = (pllcfgr >> 6) & 0x1ff; + unsigned long pllp = BIT(((pllcfgr >> 16) & 3) + 1); + const char *pllsrc = pllcfgr & BIT(22) ? hse_clk : hsi_clk; + unsigned long pllq = (pllcfgr >> 24) & 0xf; + + clk_register_fixed_factor(NULL, "vco", pllsrc, 0, plln, pllm); + clk_register_fixed_factor(NULL, "pll", "vco", 0, 1, pllp); + clk_register_fixed_factor(NULL, "pll48", "vco", 0, 1, pllq); +} + +/* + * Converts the primary and secondary indices (as they appear in DT) to an + * offset into our struct clock array. + */ +static int stm32f4_rcc_lookup_clk_idx(u8 primary, u8 secondary) +{ + u64 table[ARRAY_SIZE(stm32f42xx_gate_map)]; + + if (primary == 1) { + if (WARN_ON(secondary > FCLK)) + return -EINVAL; + return secondary; + } + + memcpy(table, stm32f42xx_gate_map, sizeof(table)); + + /* only bits set in table can be used as indices */ + if (WARN_ON(secondary > 8 * sizeof(table) || + 0 == (table[BIT_ULL_WORD(secondary)] & + BIT_ULL_MASK(secondary)))) + return -EINVAL; + + /* mask out bits above our current index */ + table[BIT_ULL_WORD(secondary)] &= + GENMASK_ULL(secondary % BITS_PER_LONG_LONG, 0); + + return FCLK + hweight64(table[0]) + + (BIT_ULL_WORD(secondary) >= 1 ? hweight64(table[1]) : 0) + + (BIT_ULL_WORD(secondary) >= 2 ? hweight64(table[2]) : 0); +} + +static struct clk * +stm32f4_rcc_lookup_clk(struct of_phandle_args *clkspec, void *data) +{ + int i = stm32f4_rcc_lookup_clk_idx(clkspec->args[0], clkspec->args[1]); + + if (i < 0) + return ERR_PTR(-EINVAL); + + return clks[i]; +} + +static const char *sys_parents[] __initdata = { "hsi", NULL, "pll" }; + +static const struct clk_div_table ahb_div_table[] = { + { 0x0, 1 }, { 0x1, 1 }, { 0x2, 1 }, { 0x3, 1 }, + { 0x4, 1 }, { 0x5, 1 }, { 0x6, 1 }, { 0x7, 1 }, + { 0x8, 2 }, { 0x9, 4 }, { 0xa, 8 }, { 0xb, 16 }, + { 0xc, 64 }, { 0xd, 128 }, { 0xe, 256 }, { 0xf, 512 }, + { 0 }, +}; + +static const struct clk_div_table apb_div_table[] = { + { 0, 1 }, { 0, 1 }, { 0, 1 }, { 0, 1 }, + { 4, 2 }, { 5, 4 }, { 6, 8 }, { 7, 16 }, + { 0 }, +}; + +static void __init stm32f4_rcc_init(struct device_node *np) +{ + const char *hse_clk; + int n; + + base = of_iomap(np, 0); + if (!base) { + pr_err("%s: unable to map resource", np->name); + return; + } + + hse_clk = of_clk_get_parent_name(np, 0); + + clk_register_fixed_rate_with_accuracy(NULL, "hsi", NULL, 0, + 16000000, 160000); + stm32f4_rcc_register_pll(hse_clk, "hsi"); + + sys_parents[1] = hse_clk; + clk_register_mux_table( + NULL, "sys", sys_parents, ARRAY_SIZE(sys_parents), 0, + base + STM32F4_RCC_CFGR, 0, 3, 0, NULL, &stm32f4_clk_lock); + + clk_register_divider_table(NULL, "ahb_div", "sys", + CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR, + 4, 4, 0, ahb_div_table, &stm32f4_clk_lock); + + clk_register_divider_table(NULL, "apb1_div", "ahb_div", + CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR, + 10, 3, 0, apb_div_table, &stm32f4_clk_lock); + clk_register_apb_mul(NULL, "apb1_mul", "apb1_div", + CLK_SET_RATE_PARENT, 12); + + clk_register_divider_table(NULL, "apb2_div", "ahb_div", + CLK_SET_RATE_PARENT, base + STM32F4_RCC_CFGR, + 13, 3, 0, apb_div_table, &stm32f4_clk_lock); + clk_register_apb_mul(NULL, "apb2_mul", "apb2_div", + CLK_SET_RATE_PARENT, 15); + + clks[SYSTICK] = clk_register_fixed_factor(NULL, "systick", "ahb_div", + 0, 1, 8); + clks[FCLK] = clk_register_fixed_factor(NULL, "fclk", "ahb_div", + 0, 1, 1); + + for (n = 0; n < ARRAY_SIZE(stm32f4_gates); n++) { + const struct stm32f4_gate_data *gd = &stm32f4_gates[n]; + unsigned int secondary = + 8 * (gd->offset - STM32F4_RCC_AHB1ENR) + gd->bit_idx; + int idx = stm32f4_rcc_lookup_clk_idx(0, secondary); + + if (idx < 0) + goto fail; + + clks[idx] = clk_register_gate( + NULL, gd->name, gd->parent_name, gd->flags, + base + gd->offset, gd->bit_idx, 0, &stm32f4_clk_lock); + + if (IS_ERR(clks[n])) { + pr_err("%s: Unable to register leaf clock %s\n", + np->full_name, gd->name); + goto fail; + } + } + + of_clk_add_provider(np, stm32f4_rcc_lookup_clk, NULL); + return; +fail: + iounmap(base); +} +CLK_OF_DECLARE(stm32f4_rcc, "st,stm32f42xx-rcc", stm32f4_rcc_init); -- cgit v1.2.3-58-ga151