summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOctav Zlatior <octav.zlatior@theobroma-systems.com>2015-06-08 15:47:42 +0200
committerKlaus Goger <klaus.goger@theobroma-systems.com>2015-07-30 18:53:02 +0200
commit726941494fa59b0a106d21fcad6770bb0ed79f66 (patch)
tree80fa82ef82616a24e750fd361dd2f3c44ce62c3a
parent403a58de190ff1322270ee571a4b670793a28fdf (diff)
clk: adds support for transition-clock property
The transition-clock property defines a parent clock to be used by a clock consumer during frequency transitions in variable frequency clocks. It is specified by referencing a clock (must be a parent clock). The clock functions clk_set_transition_parent and clk_get_transition_parent are defined to work with the transition parent in clocks. The clock function clk_use_transition_mode sets the clock parent to the specified transition parent. Signed-off-by: Octav Zlatior <octav.zlatior@theobroma-systems.com>
-rw-r--r--drivers/clk/clk.c52
-rw-r--r--drivers/clk/clkdev.c20
-rw-r--r--include/linux/clk.h27
3 files changed, 96 insertions, 3 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index b59c1b4e80b1..421dfb4e8422 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -54,6 +54,7 @@ struct clk_core {
struct clk_core *parent;
const char **parent_names;
struct clk_core **parents;
+ struct clk_core *transition_parent;
u8 num_parents;
u8 new_parent_index;
unsigned long rate;
@@ -1501,6 +1502,23 @@ static int __clk_set_parent(struct clk_core *clk, struct clk_core *parent,
return 0;
}
+static struct clk *__clk_get_transition_parent(struct clk_core *clk) {
+ if (clk->transition_parent == NULL)
+ return NULL;
+ return clk->transition_parent->hw->clk;
+}
+
+static int __clk_set_transition_parent(struct clk_core *clk,
+ struct clk_core *parent)
+{
+ if (!clk)
+ return -EINVAL;
+
+ clk->transition_parent = parent;
+
+ return 0;
+}
+
/**
* __clk_speculate_rates
* @clk: first clk in the subtree
@@ -2204,6 +2222,34 @@ bool clk_is_match(const struct clk *p, const struct clk *q)
}
EXPORT_SYMBOL_GPL(clk_is_match);
+struct clk* clk_get_transition_parent(struct clk *clk) {
+ if (!clk)
+ return NULL;
+
+ return __clk_get_transition_parent(clk->core);
+}
+EXPORT_SYMBOL_GPL(clk_get_transition_parent);
+
+int clk_set_transition_parent(struct clk *clk, struct clk *parent) {
+ if (!clk)
+ return -EINVAL;
+ if (!clk->core)
+ return -EINVAL;
+
+ return __clk_set_transition_parent(clk->core, parent->core);
+}
+EXPORT_SYMBOL_GPL(clk_set_transition_parent);
+
+int clk_use_transition_mode(struct clk *clk) {
+ if (!clk || !clk->core)
+ return -EINVAL;
+ if (clk->core->transition_parent == NULL)
+ return -EINVAL;
+
+ return clk_core_set_parent(clk->core, clk->core->transition_parent);
+}
+EXPORT_SYMBOL_GPL(clk_use_transition_mode);
+
/**
* __clk_init - initialize the data structures in a struct clk
* @dev: device initializing this clk, placeholder for now
@@ -2312,6 +2358,11 @@ static int __clk_init(struct device *dev, struct clk *clk_user)
hlist_add_head(&clk->child_node, &clk_orphan_list);
/*
+ * Set the default transition parent to NULL (not used)
+ */
+ clk->transition_parent = NULL;
+
+ /*
* Set clk's accuracy. The preferred method is to use
* .recalc_accuracy. For simple clocks and lazy developers the default
* fallback is to use the parent's accuracy. If a clock doesn't have a
@@ -3093,7 +3144,6 @@ void __init of_clk_init(const struct of_device_id *matches)
list_for_each_entry_safe(clk_provider, next,
&clk_provider_list, node) {
if (force || parent_ready(clk_provider->np)) {
-
clk_provider->clk_init_cb(clk_provider->np);
of_clk_set_defaults(clk_provider->np, true);
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 043fd3633373..5feaed862d0a 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -59,8 +59,9 @@ struct clk *of_clk_get_by_clkspec(struct of_phandle_args *clkspec)
static struct clk *__of_clk_get(struct device_node *np, int index,
const char *dev_id, const char *con_id)
{
- struct of_phandle_args clkspec;
- struct clk *clk;
+ struct of_phandle_args clkspec, transition_clkspec;
+ struct clk *clk, *transition_clock;
+ struct device_node *clock_node;
int rc;
if (index < 0)
@@ -74,6 +75,21 @@ static struct clk *__of_clk_get(struct device_node *np, int index,
clk = __of_clk_get_by_clkspec(&clkspec, dev_id, con_id);
of_node_put(clkspec.np);
+ clock_node = of_parse_phandle(np, "clocks", 0);
+ of_node_put(clock_node);
+
+ if (clock_node != NULL) {
+ rc = of_parse_phandle_with_args(clock_node, "transition-clock", NULL,
+ 0, &transition_clkspec);
+ of_node_put(transition_clkspec.np);
+
+ if (!rc) {
+ transition_clock = __of_clk_get_by_clkspec(&transition_clkspec,
+ dev_id, con_id);
+ clk_set_transition_parent(clk, transition_clock);
+ }
+ }
+
return clk;
}
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 68c16a6bedb3..4c2add0b4819 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -378,6 +378,33 @@ int clk_set_parent(struct clk *clk, struct clk *parent);
struct clk *clk_get_parent(struct clk *clk);
/**
+ * clk_get_transition_parent - return the transition parent to be used by
+ * a clock
+ * @clk: clock to get transition parent for
+ *
+ * Returns a pointer to the transition parent or NULL if not set
+ */
+struct clk *clk_get_transition_parent(struct clk *clk);
+
+/**
+ * clk_set_transition_parent - set the transition parent to be used by
+ * a clock
+ * @clk: clock to set transition parent for
+ * @parent: clock parent to set as transition parent
+ *
+ * Returns 0 on success or an error code otherwise
+ */
+int clk_set_transition_parent(struct clk *clk, struct clk *parent);
+
+/**
+ * clk_use_transition_mode - set the clock to "transition_mode"
+ * In transition mode, the clock will use the transition parent as parent
+ *
+ * Returns 0 on success or an error code otherwise
+ */
+int clk_use_transition_mode(struct clk *clk);
+
+/**
* clk_get_sys - get a clock based upon the device name
* @dev_id: device name
* @con_id: connection ID