162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2014 Samsung Electronics Co., Ltd.
462306a36Sopenharmony_ci * Sylwester Nawrocki <s.nawrocki@samsung.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/clk.h>
862306a36Sopenharmony_ci#include <linux/clk-provider.h>
962306a36Sopenharmony_ci#include <linux/clk/clk-conf.h>
1062306a36Sopenharmony_ci#include <linux/device.h>
1162306a36Sopenharmony_ci#include <linux/of.h>
1262306a36Sopenharmony_ci#include <linux/printk.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic int __set_clk_parents(struct device_node *node, bool clk_supplier)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	struct of_phandle_args clkspec;
1762306a36Sopenharmony_ci	int index, rc, num_parents;
1862306a36Sopenharmony_ci	struct clk *clk, *pclk;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	num_parents = of_count_phandle_with_args(node, "assigned-clock-parents",
2162306a36Sopenharmony_ci						 "#clock-cells");
2262306a36Sopenharmony_ci	if (num_parents == -EINVAL)
2362306a36Sopenharmony_ci		pr_err("clk: invalid value of clock-parents property at %pOF\n",
2462306a36Sopenharmony_ci		       node);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	for (index = 0; index < num_parents; index++) {
2762306a36Sopenharmony_ci		rc = of_parse_phandle_with_args(node, "assigned-clock-parents",
2862306a36Sopenharmony_ci					"#clock-cells",	index, &clkspec);
2962306a36Sopenharmony_ci		if (rc < 0) {
3062306a36Sopenharmony_ci			/* skip empty (null) phandles */
3162306a36Sopenharmony_ci			if (rc == -ENOENT)
3262306a36Sopenharmony_ci				continue;
3362306a36Sopenharmony_ci			else
3462306a36Sopenharmony_ci				return rc;
3562306a36Sopenharmony_ci		}
3662306a36Sopenharmony_ci		if (clkspec.np == node && !clk_supplier) {
3762306a36Sopenharmony_ci			of_node_put(clkspec.np);
3862306a36Sopenharmony_ci			return 0;
3962306a36Sopenharmony_ci		}
4062306a36Sopenharmony_ci		pclk = of_clk_get_from_provider(&clkspec);
4162306a36Sopenharmony_ci		of_node_put(clkspec.np);
4262306a36Sopenharmony_ci		if (IS_ERR(pclk)) {
4362306a36Sopenharmony_ci			if (PTR_ERR(pclk) != -EPROBE_DEFER)
4462306a36Sopenharmony_ci				pr_warn("clk: couldn't get parent clock %d for %pOF\n",
4562306a36Sopenharmony_ci					index, node);
4662306a36Sopenharmony_ci			return PTR_ERR(pclk);
4762306a36Sopenharmony_ci		}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci		rc = of_parse_phandle_with_args(node, "assigned-clocks",
5062306a36Sopenharmony_ci					"#clock-cells", index, &clkspec);
5162306a36Sopenharmony_ci		if (rc < 0)
5262306a36Sopenharmony_ci			goto err;
5362306a36Sopenharmony_ci		if (clkspec.np == node && !clk_supplier) {
5462306a36Sopenharmony_ci			of_node_put(clkspec.np);
5562306a36Sopenharmony_ci			rc = 0;
5662306a36Sopenharmony_ci			goto err;
5762306a36Sopenharmony_ci		}
5862306a36Sopenharmony_ci		clk = of_clk_get_from_provider(&clkspec);
5962306a36Sopenharmony_ci		of_node_put(clkspec.np);
6062306a36Sopenharmony_ci		if (IS_ERR(clk)) {
6162306a36Sopenharmony_ci			if (PTR_ERR(clk) != -EPROBE_DEFER)
6262306a36Sopenharmony_ci				pr_warn("clk: couldn't get assigned clock %d for %pOF\n",
6362306a36Sopenharmony_ci					index, node);
6462306a36Sopenharmony_ci			rc = PTR_ERR(clk);
6562306a36Sopenharmony_ci			goto err;
6662306a36Sopenharmony_ci		}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci		rc = clk_set_parent(clk, pclk);
6962306a36Sopenharmony_ci		if (rc < 0)
7062306a36Sopenharmony_ci			pr_err("clk: failed to reparent %s to %s: %d\n",
7162306a36Sopenharmony_ci			       __clk_get_name(clk), __clk_get_name(pclk), rc);
7262306a36Sopenharmony_ci		clk_put(clk);
7362306a36Sopenharmony_ci		clk_put(pclk);
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci	return 0;
7662306a36Sopenharmony_cierr:
7762306a36Sopenharmony_ci	clk_put(pclk);
7862306a36Sopenharmony_ci	return rc;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic int __set_clk_rates(struct device_node *node, bool clk_supplier)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	struct of_phandle_args clkspec;
8462306a36Sopenharmony_ci	struct property	*prop;
8562306a36Sopenharmony_ci	const __be32 *cur;
8662306a36Sopenharmony_ci	int rc, index = 0;
8762306a36Sopenharmony_ci	struct clk *clk;
8862306a36Sopenharmony_ci	u32 rate;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	of_property_for_each_u32(node, "assigned-clock-rates", prop, cur, rate) {
9162306a36Sopenharmony_ci		if (rate) {
9262306a36Sopenharmony_ci			rc = of_parse_phandle_with_args(node, "assigned-clocks",
9362306a36Sopenharmony_ci					"#clock-cells",	index, &clkspec);
9462306a36Sopenharmony_ci			if (rc < 0) {
9562306a36Sopenharmony_ci				/* skip empty (null) phandles */
9662306a36Sopenharmony_ci				if (rc == -ENOENT)
9762306a36Sopenharmony_ci					continue;
9862306a36Sopenharmony_ci				else
9962306a36Sopenharmony_ci					return rc;
10062306a36Sopenharmony_ci			}
10162306a36Sopenharmony_ci			if (clkspec.np == node && !clk_supplier) {
10262306a36Sopenharmony_ci				of_node_put(clkspec.np);
10362306a36Sopenharmony_ci				return 0;
10462306a36Sopenharmony_ci			}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci			clk = of_clk_get_from_provider(&clkspec);
10762306a36Sopenharmony_ci			of_node_put(clkspec.np);
10862306a36Sopenharmony_ci			if (IS_ERR(clk)) {
10962306a36Sopenharmony_ci				if (PTR_ERR(clk) != -EPROBE_DEFER)
11062306a36Sopenharmony_ci					pr_warn("clk: couldn't get clock %d for %pOF\n",
11162306a36Sopenharmony_ci						index, node);
11262306a36Sopenharmony_ci				return PTR_ERR(clk);
11362306a36Sopenharmony_ci			}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci			rc = clk_set_rate(clk, rate);
11662306a36Sopenharmony_ci			if (rc < 0)
11762306a36Sopenharmony_ci				pr_err("clk: couldn't set %s clk rate to %u (%d), current rate: %lu\n",
11862306a36Sopenharmony_ci				       __clk_get_name(clk), rate, rc,
11962306a36Sopenharmony_ci				       clk_get_rate(clk));
12062306a36Sopenharmony_ci			clk_put(clk);
12162306a36Sopenharmony_ci		}
12262306a36Sopenharmony_ci		index++;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci	return 0;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/**
12862306a36Sopenharmony_ci * of_clk_set_defaults() - parse and set assigned clocks configuration
12962306a36Sopenharmony_ci * @node: device node to apply clock settings for
13062306a36Sopenharmony_ci * @clk_supplier: true if clocks supplied by @node should also be considered
13162306a36Sopenharmony_ci *
13262306a36Sopenharmony_ci * This function parses 'assigned-{clocks/clock-parents/clock-rates}' properties
13362306a36Sopenharmony_ci * and sets any specified clock parents and rates. The @clk_supplier argument
13462306a36Sopenharmony_ci * should be set to true if @node may be also a clock supplier of any clock
13562306a36Sopenharmony_ci * listed in its 'assigned-clocks' or 'assigned-clock-parents' properties.
13662306a36Sopenharmony_ci * If @clk_supplier is false the function exits returning 0 as soon as it
13762306a36Sopenharmony_ci * determines the @node is also a supplier of any of the clocks.
13862306a36Sopenharmony_ci */
13962306a36Sopenharmony_ciint of_clk_set_defaults(struct device_node *node, bool clk_supplier)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	int rc;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (!node)
14462306a36Sopenharmony_ci		return 0;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	rc = __set_clk_parents(node, clk_supplier);
14762306a36Sopenharmony_ci	if (rc < 0)
14862306a36Sopenharmony_ci		return rc;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return __set_clk_rates(node, clk_supplier);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(of_clk_set_defaults);
153