diff options
author | Octav Zlatior <octav.zlatior@theobroma-systems.com> | 2015-06-08 15:47:42 +0200 |
---|---|---|
committer | Klaus Goger <klaus.goger@theobroma-systems.com> | 2015-07-30 18:53:02 +0200 |
commit | 726941494fa59b0a106d21fcad6770bb0ed79f66 (patch) | |
tree | 80fa82ef82616a24e750fd361dd2f3c44ce62c3a | |
parent | 403a58de190ff1322270ee571a4b670793a28fdf (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.c | 52 | ||||
-rw-r--r-- | drivers/clk/clkdev.c | 20 | ||||
-rw-r--r-- | include/linux/clk.h | 27 |
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 |