diff options
Diffstat (limited to 'drivers/clk/clk.c')
-rw-r--r-- | drivers/clk/clk.c | 660 |
1 files changed, 580 insertions, 80 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index ea3304bc7c..bd36878280 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1,18 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * clk.c - generic barebox clock support. Based on Linux clk support * * Copyright (c) 2012 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix - * - * 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; either version 2 of - * the License, or (at your option) any later version. - * - * 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 <common.h> #include <errno.h> @@ -46,6 +36,7 @@ static void clk_parent_disable(struct clk *clk) int clk_enable(struct clk *clk) { + struct clk_hw *hw; int ret; if (!clk) @@ -54,13 +45,15 @@ int clk_enable(struct clk *clk) if (IS_ERR(clk)) return PTR_ERR(clk); + hw = clk_to_clk_hw(clk); + if (!clk->enable_count) { ret = clk_parent_enable(clk); if (ret) return ret; if (clk->ops->enable) { - ret = clk->ops->enable(clk); + ret = clk->ops->enable(hw); if (ret) { clk_parent_disable(clk); return ret; @@ -75,6 +68,8 @@ int clk_enable(struct clk *clk) void clk_disable(struct clk *clk) { + struct clk_hw *hw; + if (!clk) return; @@ -84,14 +79,18 @@ void clk_disable(struct clk *clk) if (!clk->enable_count) return; + if (clk->enable_count == 1 && clk->flags & CLK_IS_CRITICAL) { + pr_warn("Disabling critical clock %s\n", clk->name); + return; + } + clk->enable_count--; - if (!clk->enable_count) { - if (clk->flags & CLK_IS_CRITICAL) - return; + hw = clk_to_clk_hw(clk); + if (!clk->enable_count) { if (clk->ops->disable) - clk->ops->disable(clk); + clk->ops->disable(hw); clk_parent_disable(clk); } @@ -99,6 +98,7 @@ void clk_disable(struct clk *clk) unsigned long clk_get_rate(struct clk *clk) { + struct clk_hw *hw; struct clk *parent; unsigned long parent_rate = 0; @@ -114,14 +114,22 @@ unsigned long clk_get_rate(struct clk *clk) if (!IS_ERR_OR_NULL(parent)) parent_rate = clk_get_rate(parent); + hw = clk_to_clk_hw(clk); + if (clk->ops->recalc_rate) - return clk->ops->recalc_rate(clk, parent_rate); + return clk->ops->recalc_rate(hw, parent_rate); return parent_rate; } +unsigned long clk_hw_get_rate(struct clk_hw *hw) +{ + return clk_get_rate(clk_hw_to_clk(hw)); +} + long clk_round_rate(struct clk *clk, unsigned long rate) { + struct clk_hw *hw; unsigned long parent_rate = 0; struct clk *parent; @@ -135,14 +143,22 @@ long clk_round_rate(struct clk *clk, unsigned long rate) if (parent) parent_rate = clk_get_rate(parent); + hw = clk_to_clk_hw(clk); + if (clk->ops->round_rate) - return clk->ops->round_rate(clk, rate, &parent_rate); + return clk->ops->round_rate(hw, rate, &parent_rate); return clk_get_rate(clk); } +long clk_hw_round_rate(struct clk_hw *hw, unsigned long rate) +{ + return clk_round_rate(&hw->clk, rate); +} + int clk_set_rate(struct clk *clk, unsigned long rate) { + struct clk_hw *hw; struct clk *parent; unsigned long parent_rate = 0; int ret; @@ -153,9 +169,18 @@ int clk_set_rate(struct clk *clk, unsigned long rate) if (IS_ERR(clk)) return PTR_ERR(clk); + if (clk_get_rate(clk) == clk_round_rate(clk, rate)) + return 0; + if (!clk->ops->set_rate) return -ENOSYS; + if (clk->flags & CLK_SET_RATE_UNGATE) { + ret = clk_enable(clk); + if (ret) + return ret; + } + parent = clk_get_parent(clk); if (parent) { parent_rate = clk_get_rate(parent); @@ -163,18 +188,70 @@ int clk_set_rate(struct clk *clk, unsigned long rate) if (clk->flags & CLK_OPS_PARENT_ENABLE) { ret = clk_enable(parent); if (ret) - return ret; + goto out; } } - ret = clk->ops->set_rate(clk, rate, parent_rate); + hw = clk_to_clk_hw(clk); + + ret = clk->ops->set_rate(hw, rate, parent_rate); if (parent && clk->flags & CLK_OPS_PARENT_ENABLE) clk_disable(parent); +out: + if (clk->flags & CLK_SET_RATE_UNGATE) + clk_disable(clk); + return ret; } +int clk_hw_set_rate(struct clk_hw *hw, unsigned long rate) +{ + return clk_set_rate(&hw->clk, rate); +} + +static int clk_fetch_parent_index(struct clk *clk, + struct clk *parent) +{ + int i; + + if (!parent) + return -EINVAL; + + for (i = 0; i < clk->num_parents; i++) { + if (IS_ERR_OR_NULL(clk->parents[i])) + clk->parents[i] = clk_lookup(clk->parent_names[i]); + + if (!IS_ERR_OR_NULL(clk->parents[i])) + if (clk->parents[i] == parent) + break; + } + + if (i == clk->num_parents) + return -EINVAL; + + return i; +} + +/** + * clk_hw_get_parent_index - return the index of the parent clock + * @hw: clk_hw associated with the clk being consumed + * + * Fetches and returns the index of parent clock. Returns -EINVAL if the given + * clock does not have a current parent. + */ +int clk_hw_get_parent_index(struct clk_hw *hw) +{ + struct clk_hw *parent = clk_hw_get_parent(hw); + + if (WARN_ON(parent == NULL)) + return -EINVAL; + + return clk_fetch_parent_index(clk_hw_to_clk(hw), clk_hw_to_clk(parent)); +} +EXPORT_SYMBOL_GPL(clk_hw_get_parent_index); + struct clk *clk_lookup(const char *name) { struct clk *c; @@ -192,9 +269,13 @@ struct clk *clk_lookup(const char *name) int clk_set_parent(struct clk *clk, struct clk *newparent) { + struct clk_hw *hw; int i, ret; struct clk *curparent = clk_get_parent(clk); + if (!clk || !newparent) + return 0; + if (IS_ERR(clk)) return PTR_ERR(clk); if (IS_ERR(newparent)) @@ -205,17 +286,9 @@ int clk_set_parent(struct clk *clk, struct clk *newparent) if (!clk->ops->set_parent) return -EINVAL; - for (i = 0; i < clk->num_parents; i++) { - if (IS_ERR_OR_NULL(clk->parents[i])) - clk->parents[i] = clk_lookup(clk->parent_names[i]); - - if (!IS_ERR_OR_NULL(clk->parents[i])) - if (clk->parents[i] == newparent) - break; - } - - if (i == clk->num_parents) - return -EINVAL; + i = clk_fetch_parent_index(clk, newparent); + if (i < 0) + return i; if (clk->enable_count) clk_enable(newparent); @@ -225,7 +298,9 @@ int clk_set_parent(struct clk *clk, struct clk *newparent) clk_enable(newparent); } - ret = clk->ops->set_parent(clk, i); + hw = clk_to_clk_hw(clk); + + ret = clk->ops->set_parent(hw, i); if (clk->flags & CLK_OPS_PARENT_ENABLE) { clk_disable(curparent); @@ -238,21 +313,49 @@ int clk_set_parent(struct clk *clk, struct clk *newparent) return ret; } +int clk_hw_set_parent(struct clk_hw *hw, struct clk_hw *newparent) +{ + return clk_set_parent(&hw->clk, &newparent->clk); +} + +static struct clk *clk_get_parent_by_index(struct clk *clk, u8 idx) +{ + if (IS_ERR_OR_NULL(clk->parents[idx])) + clk->parents[idx] = clk_lookup(clk->parent_names[idx]); + + return clk->parents[idx]; +} + +struct clk_hw * +clk_hw_get_parent_by_index(const struct clk_hw *hw, unsigned int idx) +{ + struct clk *clk = clk_hw_to_clk(hw); + + if (!clk || idx >= clk->num_parents || !clk->parents) + return NULL; + + return clk_to_clk_hw(clk_get_parent_by_index(clk, idx)); +} +EXPORT_SYMBOL_GPL(clk_hw_get_parent_by_index); + struct clk *clk_get_parent(struct clk *clk) { + struct clk_hw *hw; int idx; - if (IS_ERR(clk)) + if (IS_ERR_OR_NULL(clk)) return clk; if (!clk->num_parents) return ERR_PTR(-ENODEV); + hw = clk_to_clk_hw(clk); + if (clk->num_parents != 1) { if (!clk->ops->get_parent) return ERR_PTR(-EINVAL); - idx = clk->ops->get_parent(clk); + idx = clk->ops->get_parent(hw); if (idx >= clk->num_parents) return ERR_PTR(-ENODEV); @@ -260,15 +363,79 @@ struct clk *clk_get_parent(struct clk *clk) idx = 0; } - if (IS_ERR_OR_NULL(clk->parents[idx])) - clk->parents[idx] = clk_lookup(clk->parent_names[idx]); + return clk_get_parent_by_index(clk, idx); +} - return clk->parents[idx]; +struct clk_hw *clk_hw_get_parent(struct clk_hw *hw) +{ + struct clk *clk = clk_get_parent(clk_hw_to_clk(hw)); + + if (IS_ERR(clk)) + return ERR_CAST(clk); + + return clk_to_clk_hw(clk); +} + +/** + * 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. + */ +int clk_set_phase(struct clk *clk, int degrees) +{ + if (!clk) + return 0; + + /* sanity check degrees */ + degrees %= 360; + if (degrees < 0) + degrees += 360; + + if (!clk->ops->set_phase) + return -EINVAL; + + return clk->ops->set_phase(clk_to_clk_hw(clk), degrees); +} + +/** + * 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) +{ + int ret; + + if (!clk->ops->get_phase) + return 0; + + ret = clk->ops->get_phase(clk_to_clk_hw(clk)); + + return ret; } -int clk_register(struct clk *clk) +static int __bclk_register(struct clk *clk) { + struct clk_hw *hw = clk_to_clk_hw(clk); struct clk *c; + int ret; list_for_each_entry(c, &clks, list) { if (!strcmp(c->name, clk->name)) { @@ -278,16 +445,90 @@ int clk_register(struct clk *clk) } } - clk->parents = xzalloc(sizeof(struct clk *) * clk->num_parents); - list_add_tail(&clk->list, &clks); + if (clk->ops->init) { + ret = clk->ops->init(hw); + if (ret) + goto out; + } + + if (clk->flags & CLK_IS_CRITICAL) + clk_enable(clk); + return 0; +out: + list_del(&clk->list); + + return ret; +} + +int bclk_register(struct clk *clk) +{ + int ret; + + clk->parents = xzalloc(sizeof(struct clk *) * clk->num_parents); + + ret = __bclk_register(clk); + if (ret) + free(clk->parents); + + return ret; +} + +struct clk *clk_register(struct device *dev, struct clk_hw *hw) +{ + struct clk *clk; + const struct clk_init_data *init = hw->init; + char **parent_names = NULL; + int i, ret; + + if (!hw->init) + return ERR_PTR(-EINVAL); + + clk = clk_hw_to_clk(hw); + + memset(clk, 0, sizeof(*clk)); + + clk->name = xstrdup(init->name); + clk->ops = init->ops; + clk->num_parents = init->num_parents; + + clk->parents = xzalloc(sizeof(struct clk *) * clk->num_parents); + + if (init->parent_names) { + parent_names = xzalloc(init->num_parents * sizeof(char *)); + + for (i = 0; i < init->num_parents; i++) + parent_names[i] = xstrdup(init->parent_names[i]); + + clk->parent_names = (const char *const*)parent_names; + + } else { + for (i = 0; i < init->num_parents; i++) + clk->parents[i] = clk_hw_to_clk(init->parent_hws[i]); + } + + clk->flags = init->flags; + + ret = __bclk_register(clk); + if (ret) { + if (parent_names) { + for (i = 0; i < init->num_parents; i++) + free(parent_names[i]); + free(parent_names); + } + free(clk->parents); + return ERR_PTR(ret); + } + + return clk; } int clk_is_enabled(struct clk *clk) { int enabled; + struct clk_hw *hw = clk_to_clk_hw(clk); if (IS_ERR(clk)) return 0; @@ -296,7 +537,7 @@ int clk_is_enabled(struct clk *clk) /* * If we can ask a clk, do it */ - enabled = clk->ops->is_enabled(clk); + enabled = clk->ops->is_enabled(hw); } else { if (clk->ops->enable) { /* @@ -325,31 +566,62 @@ int clk_is_enabled(struct clk *clk) return clk_is_enabled(clk); } +int clk_hw_is_enabled(struct clk_hw *hw) +{ + return clk_is_enabled(&hw->clk); +} + /* * Generic struct clk_ops callbacks */ -int clk_is_enabled_always(struct clk *clk) +int clk_is_enabled_always(struct clk_hw *hw) { return 1; } -long clk_parent_round_rate(struct clk *clk, unsigned long rate, +long clk_parent_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *prate) { + struct clk *clk = clk_hw_to_clk(hw); + if (!(clk->flags & CLK_SET_RATE_PARENT)) return *prate; return clk_round_rate(clk_get_parent(clk), rate); } -int clk_parent_set_rate(struct clk *clk, unsigned long rate, +int clk_parent_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { + struct clk *clk = clk_hw_to_clk(hw); + if (!(clk->flags & CLK_SET_RATE_PARENT)) return 0; return clk_set_rate(clk_get_parent(clk), rate); } +int clk_name_set_parent(const char *clkname, const char *clkparentname) +{ + struct clk *clk = clk_lookup(clkname); + struct clk *parent = clk_lookup(clkparentname); + + if (IS_ERR(clk)) + return -ENOENT; + if (IS_ERR(parent)) + return -ENOENT; + return clk_set_parent(clk, parent); +} + +int clk_name_set_rate(const char *clkname, unsigned long rate) +{ + struct clk *clk = clk_lookup(clkname); + + if (IS_ERR(clk)) + return -ENOENT; + + return clk_set_rate(clk, rate); +} + #if defined(CONFIG_COMMON_CLK_OF_PROVIDER) /** * struct of_clk_provider - Clock provider registration structure @@ -364,12 +636,12 @@ struct of_clk_provider { struct device_node *node; struct clk *(*get)(struct of_phandle_args *clkspec, void *data); + struct clk_hw *(*get_hw)(struct of_phandle_args *clkspec, void *data); void *data; }; extern struct of_device_id __clk_of_table_start[]; -const struct of_device_id __clk_of_table_sentinel - __attribute__ ((unused,section (".__clk_of_table_end"))); +const struct of_device_id __clk_of_table_sentinel __ll_elem(.__clk_of_table_end); static LIST_HEAD(of_clk_providers); @@ -380,6 +652,12 @@ struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, } EXPORT_SYMBOL_GPL(of_clk_src_simple_get); +struct clk_hw *of_clk_hw_simple_get(struct of_phandle_args *clkspec, void *data) +{ + return data; +} +EXPORT_SYMBOL_GPL(of_clk_hw_simple_get); + struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data) { struct clk_onecell_data *clk_data = data; @@ -394,15 +672,27 @@ struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data) } EXPORT_SYMBOL_GPL(of_clk_src_onecell_get); -/** - * of_clk_add_provider() - Register a clock provider for a node - * @np: Device node pointer associated with clock provider - * @clk_src_get: callback for decoding clock - * @data: context pointer for @clk_src_get callback. - */ -int of_clk_add_provider(struct device_node *np, +struct clk_hw * +of_clk_hw_onecell_get(struct of_phandle_args *clkspec, void *data) +{ + struct clk_hw_onecell_data *hw_data = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= hw_data->num) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return hw_data->hws[idx]; +} +EXPORT_SYMBOL_GPL(of_clk_hw_onecell_get); + + +static int __of_clk_add_provider(struct device_node *np, struct clk *(*clk_src_get)(struct of_phandle_args *clkspec, void *data), + struct clk_hw *(*clk_hw_src_get)(struct of_phandle_args *clkspec, + void *data), void *data) { struct of_clk_provider *cp; @@ -414,14 +704,40 @@ int of_clk_add_provider(struct device_node *np, cp->node = np; cp->data = data; cp->get = clk_src_get; + cp->get_hw = clk_hw_src_get; list_add(&cp->link, &of_clk_providers); - pr_debug("Added clock from %s\n", np ? np->full_name : "<none>"); + pr_debug("Added clock from %pOF\n", np); + + of_clk_set_defaults(np, true); return 0; } + +/** + * of_clk_add_provider() - Register a clock provider for a node + * @np: Device node pointer associated with clock provider + * @clk_src_get: callback for decoding clock + * @data: context pointer for @clk_src_get callback. + */ +int of_clk_add_provider(struct device_node *np, + struct clk *(*clk_src_get)(struct of_phandle_args *clkspec, + void *data), + void *data) +{ + return __of_clk_add_provider(np, clk_src_get, NULL, data); +} EXPORT_SYMBOL_GPL(of_clk_add_provider); +int of_clk_add_hw_provider(struct device_node *np, + struct clk_hw *(*clk_hw_src_get)(struct of_phandle_args *clkspec, + void *data), + void *data) +{ + return __of_clk_add_provider(np, NULL, clk_hw_src_get, data); +} +EXPORT_SYMBOL_GPL(of_clk_add_hw_provider); + /** * of_clk_del_provider() - Remove a previously registered clock provider * @np: Device node pointer associated with clock provider @@ -445,10 +761,17 @@ struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec) struct of_clk_provider *provider; struct clk *clk = ERR_PTR(-EPROBE_DEFER); + /* Ignore error, as CLK_OF_DECLARE clocks have no proper driver. */ + of_device_ensure_probed(clkspec->np); + /* Check if we have such a provider in our array */ list_for_each_entry(provider, &of_clk_providers, link) { - if (provider->node == clkspec->np) - clk = provider->get(clkspec, provider->data); + if (provider->node == clkspec->np) { + if (provider->get) + clk = provider->get(clkspec, provider->data); + else + clk = clk_hw_to_clk(provider->get_hw(clkspec, provider->data)); + } if (!IS_ERR(clk)) break; } @@ -474,21 +797,60 @@ unsigned int of_clk_get_parent_count(struct device_node *np) } EXPORT_SYMBOL_GPL(of_clk_get_parent_count); -char *of_clk_get_parent_name(struct device_node *np, unsigned int index) +char *of_clk_get_parent_name(const struct device_node *np, int index) { struct of_phandle_args clkspec; + struct property *prop; const char *clk_name; + const __be32 *vp; + u32 pv; int rc; + int count; + struct clk *clk; rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index, - &clkspec); + &clkspec); if (rc) return NULL; + index = clkspec.args_count ? clkspec.args[0] : 0; + count = 0; + + /* if there is an indices property, use it to transfer the index + * specified into an array offset for the clock-output-names property. + */ + of_property_for_each_u32(clkspec.np, "clock-indices", prop, vp, pv) { + if (index == pv) { + index = count; + break; + } + count++; + } + /* We went off the end of 'clock-indices' without finding it */ + if (prop && !vp) + return NULL; + if (of_property_read_string_index(clkspec.np, "clock-output-names", - clkspec.args_count ? clkspec.args[0] : 0, - &clk_name) < 0) - clk_name = clkspec.np->name; + index, + &clk_name) < 0) { + /* + * Best effort to get the name if the clock has been + * registered with the framework. If the clock isn't + * registered, we return the node name as the name of + * the clock as long as #clock-cells = 0. + */ + clk = of_clk_get_from_provider(&clkspec); + if (IS_ERR(clk)) { + if (clkspec.args_count == 0) + clk_name = clkspec.np->name; + else + clk_name = NULL; + } else { + clk_name = __clk_get_name(clk); + clk_put(clk); + } + } + return xstrdup(clk_name); } @@ -556,18 +918,30 @@ static int parent_ready(struct device_node *np) } } +static LIST_HEAD(probed_clks); + +static bool of_clk_probed(struct device_node *np) +{ + struct clock_provider *clk_provider; + + list_for_each_entry(clk_provider, &probed_clks, node) + if (clk_provider->np == np) + return true; + return false; +} + /** * of_clk_init() - Scan and init clock providers from the DT - * @root: parent of the first level to probe or NULL for the root of the tree - * @matches: array of compatible values and init functions for providers. * * This function scans the device tree for matching clock providers and * calls their initialization functions * * Returns 0 on success, < 0 on failure. */ -int of_clk_init(struct device_node *root, const struct of_device_id *matches) +int of_clk_init(void) { + struct device_node *root = of_get_root_node(); + const struct of_device_id *matches = __clk_of_table_start; struct clock_provider *clk_provider, *next; bool is_init_done; bool force = false; @@ -575,11 +949,7 @@ int of_clk_init(struct device_node *root, const struct of_device_id *matches) const struct of_device_id *match; if (!root) - root = of_find_node_by_path("/"); - if (!root) return -EINVAL; - if (!matches) - matches = __clk_of_table_start; /* First prepare the list of the clocks providers */ for_each_matching_node_and_match(root, matches, &match) { @@ -588,6 +958,11 @@ int of_clk_init(struct device_node *root, const struct of_device_id *matches) if (!of_device_is_available(root)) continue; + if (of_clk_probed(root)) { + pr_debug("%s: already probed: %pOF\n", __func__, root); + continue; + } + parent = xzalloc(sizeof(*parent)); parent->clk_init_cb = match->data; @@ -607,8 +982,7 @@ int of_clk_init(struct device_node *root, const struct of_device_id *matches) clk_provider->clk_init_cb(np); of_clk_set_defaults(np, true); - list_del(&clk_provider->node); - free(clk_provider); + list_move_tail(&clk_provider->node, &probed_clks); is_init_done = true; } } @@ -627,42 +1001,168 @@ int of_clk_init(struct device_node *root, const struct of_device_id *matches) } #endif -static void dump_one(struct clk *clk, int verbose, int indent) +static const char *clk_hw_stat(struct clk *clk) { - struct clk *c; + struct clk_hw *hw = clk_to_clk_hw(clk); + + if (clk->ops->is_enabled) { + if (clk->ops->is_enabled(hw)) + return "enabled"; + else + return "disabled"; + } - printf("%*s%s (rate %lu, %sabled)\n", indent * 4, "", clk->name, clk_get_rate(clk), - clk_is_enabled(clk) ? "en" : "dis"); - if (verbose) { + if (!clk->ops->enable) + return "always enabled"; + + return "unknown"; +} + +static const char *clk_parent_name_by_index(struct clk *clk, u8 idx) +{ + if (clk->parent_names) + return clk->parent_names[idx]; + if (clk->parents[idx]) + return clk->parents[idx]->name; + return "unknown"; +} + +static void dump_one_summary(struct clk *clk, int flags, int indent) +{ + int enabled = clk_is_enabled(clk); + const char *hwstat, *stat; + + hwstat = clk_hw_stat(clk); + + if (enabled == 0) + stat = "disabled"; + else + stat = "enabled"; + + printf("%*s%s (rate %lu, enable_count: %d, %s)\n", indent * 4, "", + clk->name, + clk_get_rate(clk), + clk->enable_count, + hwstat); + + if (flags & CLK_DUMP_VERBOSE) { if (clk->num_parents > 1) { int i; printf("%*s`---- possible parents: ", indent * 4, ""); for (i = 0; i < clk->num_parents; i++) - printf("%s ", clk->parent_names[i]); + printf("%s ", clk_parent_name_by_index(clk, i)); printf("\n"); } } +} + +static void dump_one_json(struct clk *clk, int flags, int indent) +{ + printf("\"%s\": { \"rate\": %lu,\"enable_count\": %d", + clk->name, + clk_get_rate(clk), + clk->enable_count); +} + +static void dump_one(struct clk *clk, int flags, int indent) +{ + if (flags & CLK_DUMP_JSON) + dump_one_json(clk, flags, indent); + else + dump_one_summary(clk, flags, indent); +} + +static inline bool json_puts(const char *str, int flags) +{ + if (flags & CLK_DUMP_JSON) { + puts(str); + return true; + } + + return false; +} + +static void dump_subtree(struct clk *clk, int flags, int indent) +{ + struct clk *c; + + dump_one(clk, flags, indent); list_for_each_entry(c, &clks, list) { struct clk *parent = clk_get_parent(c); if (parent == clk) { - dump_one(c, verbose, indent + 1); + json_puts(",", flags); + dump_subtree(c, flags, indent + 1); } } + + json_puts("}", flags); } -void clk_dump(int verbose) +void clk_dump(int flags) { + bool first_node = true; struct clk *c; + json_puts("{", flags); + list_for_each_entry(c, &clks, list) { struct clk *parent = clk_get_parent(c); - if (IS_ERR_OR_NULL(parent)) - dump_one(c, verbose, 0); + if (IS_ERR_OR_NULL(parent)) { + if (!first_node) + json_puts(",", flags); + first_node = false; + dump_subtree(c, flags, 0); + } } + + json_puts("}\n", flags); +} + +static int clk_print_parent(struct clk *clk, int flags) +{ + struct clk *c; + int indent; + + c = clk_get_parent(clk); + if (IS_ERR_OR_NULL(c)) + return 0; + + indent = clk_print_parent(c, flags); + + dump_one(c, flags, indent); + + return indent + 1; +} + +void clk_dump_one(struct clk *clk, int flags) +{ + int indent = 0; + struct clk *c; + + if (json_puts("{", flags)) { + dump_one(clk, flags, indent); + } else { + indent = clk_print_parent(clk, flags); + + printf("\033[1m"); + dump_one(clk, flags, indent); + printf("\033[0m"); + } + + list_for_each_entry(c, &clks, list) { + struct clk *parent = clk_get_parent(c); + + if (parent == clk) { + json_puts(",", flags); + dump_subtree(c, flags, indent + 1); + } + } + + json_puts("}}\n", flags); } int clk_name_complete(struct string_list *sl, char *instr) |