[U-Boot,v2,4/5] drivers: clk: Fix using assigned-clocks in the node of the clock it sets up
diff mbox series

Message ID 20191022120007.23288-5-jjhiblot@ti.com
State Accepted
Delegated to: Lukasz Majewski
Headers show
Series
  • clk: Add a managed API and fix clock self-assignment
Related show

Commit Message

Jean-Jacques Hiblot Oct. 22, 2019, noon UTC
This fixes the case where assigned-clocks is used to define a clock
defaults inside this same clock's node. This is used sometimes to setup a
default parents and/or rate for a clock.

example:
muxed_clock: muxed_clock {
	clocks = <&clk_provider 0>, <&clk_provider 1>;
	#clock-cells = <0>;
	assigned-clocks = <&muxed_clock>;
	assigned-clock-parents = <&clk_provider 1>;
};

It doesn't work in u-boot because the assigned-clocks are setup *before*
the clock is probed. (clk_set_parent() will likely crash or fail if called
before the device probe function)
Making it work by handling "assigned-clocks" in 2 steps: first before the
clk device is probed, and then after the clk device is probed.

Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>
---

Changes in v2: None

 drivers/clk/clk-uclass.c | 48 +++++++++++++++++++++++++++++++++++-----
 drivers/core/device.c    |  2 +-
 include/clk.h            |  7 ++++--
 3 files changed, 48 insertions(+), 9 deletions(-)

Patch
diff mbox series

diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index e7ec6347de..75d618a47b 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -178,7 +178,7 @@  bulk_get_err:
 	return ret;
 }
 
-static int clk_set_default_parents(struct udevice *dev)
+static int clk_set_default_parents(struct udevice *dev, int stage)
 {
 	struct clk clk, parent_clk;
 	int index;
@@ -214,8 +214,18 @@  static int clk_set_default_parents(struct udevice *dev)
 			return ret;
 		}
 
-		ret = clk_set_parent(&clk, &parent_clk);
+		/* This is clk provider device trying to reparent itself
+		 * It cannot be done right now but need to wait after the
+		 * device is probed
+		 */
+		if (stage == 0 && clk.dev == dev)
+			continue;
+
+		if (stage > 0 && clk.dev != dev)
+			/* do not setup twice the parent clocks */
+			continue;
 
+		ret = clk_set_parent(&clk, &parent_clk);
 		/*
 		 * Not all drivers may support clock-reparenting (as of now).
 		 * Ignore errors due to this.
@@ -233,7 +243,7 @@  static int clk_set_default_parents(struct udevice *dev)
 	return 0;
 }
 
-static int clk_set_default_rates(struct udevice *dev)
+static int clk_set_default_rates(struct udevice *dev, int stage)
 {
 	struct clk clk;
 	int index;
@@ -268,7 +278,19 @@  static int clk_set_default_rates(struct udevice *dev)
 			continue;
 		}
 
+		/* This is clk provider device trying to program itself
+		 * It cannot be done right now but need to wait after the
+		 * device is probed
+		 */
+		if (stage == 0 && clk.dev == dev)
+			continue;
+
+		if (stage > 0 && clk.dev != dev)
+			/* do not setup twice the parent clocks */
+			continue;
+
 		ret = clk_set_rate(&clk, rates[index]);
+
 		if (ret < 0) {
 			debug("%s: failed to set rate on clock index %d (%ld) for %s\n",
 			      __func__, index, clk.id, dev_read_name(dev));
@@ -281,7 +303,7 @@  fail:
 	return ret;
 }
 
-int clk_set_defaults(struct udevice *dev)
+int clk_set_defaults(struct udevice *dev, int stage)
 {
 	int ret;
 
@@ -294,11 +316,11 @@  int clk_set_defaults(struct udevice *dev)
 
 	debug("%s(%s)\n", __func__, dev_read_name(dev));
 
-	ret = clk_set_default_parents(dev);
+	ret = clk_set_default_parents(dev, stage);
 	if (ret)
 		return ret;
 
-	ret = clk_set_default_rates(dev);
+	ret = clk_set_default_rates(dev, stage);
 	if (ret < 0)
 		return ret;
 
@@ -673,7 +695,21 @@  void devm_clk_put(struct udevice *dev, struct clk *clk)
 	WARN_ON(rc);
 }
 
+int clk_uclass_post_probe(struct udevice *dev)
+{
+	/*
+	 * when a clock provider is probed. Call clk_set_defaults()
+	 * also after the device is probed. This takes care of cases
+	 * where the DT is used to setup default parents and rates
+	 * using assigned-clocks
+	 */
+	clk_set_defaults(dev, 1);
+
+	return 0;
+}
+
 UCLASS_DRIVER(clk) = {
 	.id		= UCLASS_CLK,
 	.name		= "clk",
+	.post_probe	= clk_uclass_post_probe,
 };
diff --git a/drivers/core/device.c b/drivers/core/device.c
index ce66c72e5e..854aa91bbf 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -417,7 +417,7 @@  int device_probe(struct udevice *dev)
 		 * Process 'assigned-{clocks/clock-parents/clock-rates}'
 		 * properties
 		 */
-		ret = clk_set_defaults(dev);
+		ret = clk_set_defaults(dev, 0);
 		if (ret)
 			goto fail;
 	}
diff --git a/include/clk.h b/include/clk.h
index 9be3264113..a5ee53d94a 100644
--- a/include/clk.h
+++ b/include/clk.h
@@ -244,10 +244,13 @@  static inline int clk_release_all(struct clk *clk, int count)
  *
  * @dev:        A device to process (the ofnode associated with this device
  *              will be processed).
+ * @stage:	A integer. 0 indicates that this is called before the device
+ *		is probed. 1 indicates that this is called just after the
+ *		device has been probed
  */
-int clk_set_defaults(struct udevice *dev);
+int clk_set_defaults(struct udevice *dev, int stage);
 #else
-static inline int clk_set_defaults(struct udevice *dev)
+static inline int clk_set_defaults(struct udevice *dev, int stage)
 {
 	return 0;
 }