diff options
Diffstat (limited to 'drivers/clk/clk.c')
-rw-r--r-- | drivers/clk/clk.c | 63 |
1 files changed, 54 insertions, 9 deletions
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index af011974d4ec..d2477a5058ac 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com> * Copyright (C) 2011-2012 Linaro Ltd <mturquette@linaro.org> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * Standard functionality for the common clock API. See Documentation/driver-api/clk.rst */ @@ -1516,10 +1513,20 @@ static int clk_fetch_parent_index(struct clk_core *core, if (!parent) return -EINVAL; - for (i = 0; i < core->num_parents; i++) - if (clk_core_get_parent_by_index(core, i) == parent) + for (i = 0; i < core->num_parents; i++) { + if (core->parents[i] == parent) return i; + if (core->parents[i]) + continue; + + /* Fallback to comparing globally unique names */ + if (!strcmp(parent->name, core->parent_names[i])) { + core->parents[i] = parent; + return i; + } + } + return -EINVAL; } @@ -2782,7 +2789,7 @@ static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level) seq_printf(s, "\"protect_count\": %d,", c->protect_count); seq_printf(s, "\"rate\": %lu,", clk_core_get_rate(c)); seq_printf(s, "\"accuracy\": %lu,", clk_core_get_accuracy(c)); - seq_printf(s, "\"phase\": %d", clk_core_get_phase(c)); + seq_printf(s, "\"phase\": %d,", clk_core_get_phase(c)); seq_printf(s, "\"duty_cycle\": %u", clk_core_get_scaled_duty_cycle(c, 100000)); } @@ -3893,6 +3900,39 @@ static void devm_of_clk_release_provider(struct device *dev, void *res) of_clk_del_provider(*(struct device_node **)res); } +/* + * We allow a child device to use its parent device as the clock provider node + * for cases like MFD sub-devices where the child device driver wants to use + * devm_*() APIs but not list the device in DT as a sub-node. + */ +static struct device_node *get_clk_provider_node(struct device *dev) +{ + struct device_node *np, *parent_np; + + np = dev->of_node; + parent_np = dev->parent ? dev->parent->of_node : NULL; + + if (!of_find_property(np, "#clock-cells", NULL)) + if (of_find_property(parent_np, "#clock-cells", NULL)) + np = parent_np; + + return np; +} + +/** + * devm_of_clk_add_hw_provider() - Managed clk provider node registration + * @dev: Device acting as the clock provider (used for DT node and lifetime) + * @get: callback for decoding clk_hw + * @data: context pointer for @get callback + * + * Registers clock provider for given device's node. If the device has no DT + * node or if the device node lacks of clock provider information (#clock-cells) + * then the parent device's node is scanned for this information. If parent node + * has the #clock-cells then it is used in registration. Provider is + * automatically released at device exit. + * + * Return: 0 on success or an errno on failure. + */ int devm_of_clk_add_hw_provider(struct device *dev, struct clk_hw *(*get)(struct of_phandle_args *clkspec, void *data), @@ -3906,7 +3946,7 @@ int devm_of_clk_add_hw_provider(struct device *dev, if (!ptr) return -ENOMEM; - np = dev->of_node; + np = get_clk_provider_node(dev); ret = of_clk_add_hw_provider(np, get, data); if (!ret) { *ptr = np; @@ -3950,12 +3990,17 @@ static int devm_clk_provider_match(struct device *dev, void *res, void *data) return *np == data; } +/** + * devm_of_clk_del_provider() - Remove clock provider registered using devm + * @dev: Device to whose lifetime the clock provider was bound + */ void devm_of_clk_del_provider(struct device *dev) { int ret; + struct device_node *np = get_clk_provider_node(dev); ret = devres_release(dev, devm_of_clk_release_provider, - devm_clk_provider_match, dev->of_node); + devm_clk_provider_match, np); WARN_ON(ret); } |