summaryrefslogtreecommitdiff
path: root/drivers/clk/clk-uclass.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/clk-uclass.c')
-rw-r--r--drivers/clk/clk-uclass.c68
1 files changed, 57 insertions, 11 deletions
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index 53e7be764d..14254212ca 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -160,7 +160,7 @@ int clk_get_by_index_nodev(ofnode node, int index, struct clk *clk)
int clk_get_bulk(struct udevice *dev, struct clk_bulk *bulk)
{
int i, ret, err, count;
-
+
bulk->count = 0;
count = dev_count_phandle_with_args(dev, "clocks", "#clock-cells", 0);
@@ -207,7 +207,8 @@ static struct clk *clk_set_default_get_by_id(struct clk *clk)
return c;
}
-static int clk_set_default_parents(struct udevice *dev, int stage)
+static int clk_set_default_parents(struct udevice *dev,
+ enum clk_defaults_stage stage)
{
struct clk clk, parent_clk, *c, *p;
int index;
@@ -241,6 +242,15 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
ret = clk_get_by_indexed_prop(dev, "assigned-clocks",
index, &clk);
+ /*
+ * If the clock provider is not ready yet, let it handle
+ * the re-programming later.
+ */
+ if (ret == -EPROBE_DEFER) {
+ ret = 0;
+ continue;
+ }
+
if (ret) {
debug("%s: could not get assigned clock %d for %s\n",
__func__, index, dev_read_name(dev));
@@ -251,10 +261,10 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
* It cannot be done right now but need to wait after the
* device is probed
*/
- if (stage == 0 && clk.dev == dev)
+ if (stage == CLK_DEFAULTS_PRE && clk.dev == dev)
continue;
- if (stage > 0 && clk.dev != dev)
+ if (stage != CLK_DEFAULTS_PRE && clk.dev != dev)
/* do not setup twice the parent clocks */
continue;
@@ -280,7 +290,8 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
return 0;
}
-static int clk_set_default_rates(struct udevice *dev, int stage)
+static int clk_set_default_rates(struct udevice *dev,
+ enum clk_defaults_stage stage)
{
struct clk clk, *c;
int index;
@@ -309,6 +320,15 @@ static int clk_set_default_rates(struct udevice *dev, int stage)
ret = clk_get_by_indexed_prop(dev, "assigned-clocks",
index, &clk);
+ /*
+ * If the clock provider is not ready yet, let it handle
+ * the re-programming later.
+ */
+ if (ret == -EPROBE_DEFER) {
+ ret = 0;
+ continue;
+ }
+
if (ret) {
dev_dbg(dev,
"could not get assigned clock %d (err = %d)\n",
@@ -320,10 +340,10 @@ static int clk_set_default_rates(struct udevice *dev, int stage)
* It cannot be done right now but need to wait after the
* device is probed
*/
- if (stage == 0 && clk.dev == dev)
+ if (stage == CLK_DEFAULTS_PRE && clk.dev == dev)
continue;
- if (stage > 0 && clk.dev != dev)
+ if (stage != CLK_DEFAULTS_PRE && clk.dev != dev)
/* do not setup twice the parent clocks */
continue;
@@ -346,16 +366,21 @@ fail:
return ret;
}
-int clk_set_defaults(struct udevice *dev, int stage)
+int clk_set_defaults(struct udevice *dev, enum clk_defaults_stage stage)
{
int ret;
if (!dev_has_ofnode(dev))
return 0;
- /* If this not in SPL and pre-reloc state, don't take any action. */
+ /*
+ * To avoid setting defaults twice, don't set them before relocation.
+ * However, still set them for SPL. And still set them if explicitly
+ * asked.
+ */
if (!(IS_ENABLED(CONFIG_SPL_BUILD) || (gd->flags & GD_FLG_RELOC)))
- return 0;
+ if (stage != CLK_DEFAULTS_POST_FORCE)
+ return 0;
debug("%s(%s)\n", __func__, dev_read_name(dev));
@@ -502,6 +527,8 @@ struct clk *clk_get_parent(struct clk *clk)
return NULL;
pdev = dev_get_parent(clk->dev);
+ if (!pdev)
+ return ERR_PTR(-ENODEV);
pclk = dev_get_clk_ptr(pdev);
if (!pclk)
return ERR_PTR(-ENODEV);
@@ -548,6 +575,22 @@ ulong clk_round_rate(struct clk *clk, ulong rate)
return ops->round_rate(clk, rate);
}
+static void clk_clean_rate_cache(struct clk *clk)
+{
+ struct udevice *child_dev;
+ struct clk *clkp;
+
+ if (!clk)
+ return;
+
+ clk->rate = 0;
+
+ list_for_each_entry(child_dev, &clk->dev->child_head, sibling_node) {
+ clkp = dev_get_clk_ptr(child_dev);
+ clk_clean_rate_cache(clkp);
+ }
+}
+
ulong clk_set_rate(struct clk *clk, ulong rate)
{
const struct clk_ops *ops;
@@ -560,6 +603,9 @@ ulong clk_set_rate(struct clk *clk, ulong rate)
if (!ops->set_rate)
return -ENOSYS;
+ /* Clean up cached rates for us and all child clocks */
+ clk_clean_rate_cache(clk);
+
return ops->set_rate(clk, rate);
}
@@ -805,7 +851,7 @@ int clk_uclass_post_probe(struct udevice *dev)
* where the DT is used to setup default parents and rates
* using assigned-clocks
*/
- clk_set_defaults(dev, 1);
+ clk_set_defaults(dev, CLK_DEFAULTS_POST);
return 0;
}