
Linux uses the properties 'assigned-clocks', 'assigned-clock-parents' and 'assigned-clock-rates' to configure the clock subsystem for use with various peripheral nodes.
This implements clk_set_defaults() and hooks it up with the general device probibin in drivers/core/device.c: when a new device is probed, clk_set_defaults() will be called for it and will process the properties mentioned above.
Note that this functionality is designed to fail gracefully (i.e. if a clock-driver does not implement set_parent(), we simply accept this and ignore the error) as not to break existing board-support.
Signed-off-by: Philipp Tomsich philipp.tomsich@theobroma-systems.com Tested-by: David Wu david.wu@theobroma-systems.com
---
drivers/clk/clk-uclass.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/core/device.c | 6 +++ include/clk.h | 17 +++++++ 3 files changed, 141 insertions(+)
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c index 7fdf16d..ad76379 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -2,6 +2,7 @@ * Copyright (C) 2015 Google, Inc * Written by Simon Glass sjg@chromium.org * Copyright (c) 2016, NVIDIA CORPORATION. + * Copyright (c) 2018, Theobroma Systems Design und Consulting GmbH * * SPDX-License-Identifier: GPL-2.0+ */ @@ -10,6 +11,7 @@ #include <clk.h> #include <clk-uclass.h> #include <dm.h> +#include <dm/read.h> #include <dt-structs.h> #include <errno.h>
@@ -101,6 +103,122 @@ int clk_get_by_index(struct udevice *dev, int index, struct clk *clk) { return clk_get_by_indexed_prop(dev, "clocks", index, clk); } + +static int clk_set_default_parents(struct udevice *dev) +{ + struct clk clk, parent_clk; + int index; + int num_parents; + int ret; + + num_parents = dev_count_phandle_with_args(dev, "assigned-clock-parents", + "#clock-cells"); + if (num_parents < 0) { + debug("%s: could not read assigned-clock-parents for %p\n", + __func__, dev); + return 0; + } + + for (index = 0; index < num_parents; index++) { + ret = clk_get_by_indexed_prop(dev, "assigned-clock-parents", + index, &parent_clk); + if (ret) { + debug("%s: could not get parent clock %d for %s\n", + __func__, index, dev_read_name(dev)); + return ret; + } + + ret = clk_get_by_indexed_prop(dev, "assigned-clocks", + index, &clk); + if (ret) { + debug("%s: could not get assigned clock %d for %s\n", + __func__, index, dev_read_name(dev)); + return ret; + } + + ret = clk_set_parent(&clk, &parent_clk); + + /* + * Not all drivers may support clock-reparenting (as of now). + * Ignore errors due to this. + */ + if (ret == -ENOSYS) + continue; + + if (ret) { + debug("%s: failed to reparent clock %d for %s\n", + __func__, index, dev_read_name(dev)); + return ret; + } + } + + return 0; +} + +static int clk_set_default_rates(struct udevice *dev) +{ + struct clk clk; + int index; + int num_rates; + int size; + int ret = 0; + u32 *rates = NULL; + + size = dev_read_size(dev, "assigned-clock-rates"); + if (size < 0) + return 0; + + num_rates = size / sizeof(u32); + rates = calloc(num_rates, sizeof(u32)); + if (!rates) + return -ENOMEM; + + ret = dev_read_u32_array(dev, "assigned-clock-rates", rates, num_rates); + if (ret) + goto fail; + + for (index = 0; index < num_rates; index++) { + ret = clk_get_by_indexed_prop(dev, "assigned-clocks", + index, &clk); + if (ret) { + debug("%s: could not get assigned clock %d for %s\n", + __func__, index, dev_read_name(dev)); + continue; + } + + ret = clk_set_rate(&clk, rates[index]); + if (ret < 0) { + debug("%s: failed to set rate on clock %d for %s\n", + __func__, index, dev_read_name(dev)); + break; + } + } + +fail: + free(rates); + return ret; +} + +int clk_set_defaults(struct udevice *dev) +{ + int ret; + + /* If this is running pre-reloc state, don't take any action. */ + if (!(gd->flags & GD_FLG_RELOC)) + return 0; + + debug("%s(%s)\n", __func__, dev_read_name(dev)); + + ret = clk_set_default_parents(dev); + if (ret) + return ret; + + ret = clk_set_default_rates(dev); + if (ret < 0) + return ret; + + return 0; +} # endif /* OF_PLATDATA */
int clk_get_by_name(struct udevice *dev, const char *name, struct clk *clk) diff --git a/drivers/core/device.c b/drivers/core/device.c index 144ac2a..940a153 100644 --- a/drivers/core/device.c +++ b/drivers/core/device.c @@ -11,6 +11,7 @@
#include <common.h> #include <asm/io.h> +#include <clk.h> #include <fdtdec.h> #include <fdt_support.h> #include <malloc.h> @@ -391,6 +392,11 @@ int device_probe(struct udevice *dev) goto fail; }
+ /* Process 'assigned-{clocks/clock-parents/clock-rates}' properties */ + ret = clk_set_defaults(dev); + if (ret) + goto fail; + if (drv->probe) { ret = drv->probe(dev); if (ret) { diff --git a/include/clk.h b/include/clk.h index e463d8e..a7d95d3 100644 --- a/include/clk.h +++ b/include/clk.h @@ -133,6 +133,23 @@ static inline int clk_release_all(struct clk *clk, int count)
#endif
+#if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) && \ + CONFIG_IS_ENABLED(CLK) +/** + * clk_set_defaults - Process 'assigned-{clocks/clock-parents/clock-rates}' + * properties to configure clocks + * + * @dev: A device to process (the ofnode associated with this device + * will be processed). + */ +int clk_set_defaults(struct udevice *dev); +#else +static inline int clk_set_defaults(struct udevice *dev) +{ + return 0; +} +#endif + /** * clk_request - Request a clock by provider-specific ID. *