summaryrefslogtreecommitdiff
path: root/drivers/clk/clk.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/clk.c')
-rw-r--r--drivers/clk/clk.c76
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