diff options
Diffstat (limited to 'drivers/clk/clk.c')
-rw-r--r-- | drivers/clk/clk.c | 76 |
1 files changed, 72 insertions, 4 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index f13c3f4228d4..97a604755053 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -26,6 +26,8 @@ #include "clk.h" +#if defined(CONFIG_COMMON_CLK) + static DEFINE_SPINLOCK(enable_lock); static DEFINE_MUTEX(prepare_lock); @@ -545,6 +547,26 @@ void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate, EXPORT_SYMBOL_GPL(clk_hw_set_rate_range); /* + * Aggregate the rate of all child nodes which are enabled and exclude the + * child node which requests for clk_aggregate_rate. + */ +unsigned long clk_aggregate_rate(struct clk_hw *hw, + const struct clk_core *parent) +{ + struct clk_core *child; + unsigned long aggre_rate = 0; + + hlist_for_each_entry(child, &parent->children, child_node) { + if (child->enable_count && + strcmp(child->name, hw->init->name)) + aggre_rate = max(child->rate, aggre_rate); + } + + return aggre_rate; +} +EXPORT_SYMBOL_GPL(clk_aggregate_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. @@ -1427,7 +1449,7 @@ static struct clk_core *clk_propagate_rate_change(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) +static int clk_change_rate(struct clk_core *core) { struct clk_core *child; struct hlist_node *tmp; @@ -1435,6 +1457,7 @@ static void clk_change_rate(struct clk_core *core) unsigned long best_parent_rate = 0; bool skip_set_rate = false; struct clk_core *old_parent; + int rc = 0; old_rate = core->rate; @@ -1462,8 +1485,12 @@ static void clk_change_rate(struct clk_core *core) trace_clk_set_rate(core, core->new_rate); - if (!skip_set_rate && core->ops->set_rate) - core->ops->set_rate(core->hw, core->new_rate, best_parent_rate); + if (!skip_set_rate && core->ops->set_rate) { + rc = core->ops->set_rate(core->hw, core->new_rate, + best_parent_rate); + if (rc) + goto out; + } trace_clk_set_rate_complete(core, core->new_rate); @@ -1489,6 +1516,13 @@ static void clk_change_rate(struct clk_core *core) /* handle the new child who might not be in core->children yet */ if (core->new_child) clk_change_rate(core->new_child); + + return rc; + +out: + trace_clk_set_rate_complete(core, core->new_rate); + + return rc; } static int clk_core_set_rate_nolock(struct clk_core *core, @@ -1523,7 +1557,13 @@ static int clk_core_set_rate_nolock(struct clk_core *core, } /* change the rates */ - clk_change_rate(top); + ret = clk_change_rate(top); + if (ret) { + pr_err("%s: failed to set %s rate\n", __func__, + top->name); + clk_propagate_rate_change(top, ABORT_RATE_CHANGE); + return ret; + } core->req_rate = req_rate; @@ -1953,6 +1993,18 @@ bool clk_is_match(const struct clk *p, const struct clk *q) } EXPORT_SYMBOL_GPL(clk_is_match); +int clk_set_flags(struct clk *clk, unsigned long flags) +{ + if (!clk) + return 0; + + if (!clk->core->ops->set_flags) + return -EINVAL; + + return clk->core->ops->set_flags(clk->core->hw, flags); +} +EXPORT_SYMBOL_GPL(clk_set_flags); + /*** debugfs support ***/ #ifdef CONFIG_DEBUG_FS @@ -2901,6 +2953,8 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb) } EXPORT_SYMBOL_GPL(clk_notifier_unregister); +#endif /* CONFIG_COMMON_CLK */ + #ifdef CONFIG_OF /** * struct of_clk_provider - Clock provider registration structure @@ -2931,6 +2985,8 @@ struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, } EXPORT_SYMBOL_GPL(of_clk_src_simple_get); +#if defined(CONFIG_COMMON_CLK) + struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data) { struct clk_onecell_data *clk_data = data; @@ -2945,6 +3001,11 @@ struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data) } EXPORT_SYMBOL_GPL(of_clk_src_onecell_get); +#endif /* CONFIG_COMMON_CLK */ + +/* forward declaration */ +void of_clk_del_provider(struct device_node *np); + /** * of_clk_add_provider() - Register a clock provider for a node * @np: Device node pointer associated with clock provider @@ -3100,8 +3161,10 @@ const char *of_clk_get_parent_name(struct device_node *np, int index) else clk_name = NULL; } else { +#if defined(CONFIG_COMMON_CLK) clk_name = __clk_get_name(clk); clk_put(clk); +#endif } } @@ -3132,6 +3195,8 @@ int of_clk_parent_fill(struct device_node *np, const char **parents, } EXPORT_SYMBOL_GPL(of_clk_parent_fill); +#if defined(CONFIG_COMMON_CLK) + struct clock_provider { of_clk_init_cb_t clk_init_cb; struct device_node *np; @@ -3240,4 +3305,7 @@ void __init of_clk_init(const struct of_device_id *matches) force = true; } } + +#endif /* CONFIG_COMMON_CLK */ + #endif |