[PATCH v2 4/5] clk: allow assigning parent lazily

Yang Xiwen via B4 Relay devnull+forbidden405.outlook.com at kernel.org
Wed Dec 31 20:52:50 CET 2025


From: Yang Xiwen <forbidden405 at outlook.com>

Don't mandate the parent device exists when registering a clock.

Instead, cache the parent name in the core clk struct and resolve the
parent in clk_get_parent(), which is called lazily upon real use.

Disable this feature for xPLs by default to save size.

Signed-off-by: Yang Xiwen <forbidden405 at outlook.com>
---
 drivers/clk/Kconfig      | 12 ++++++++++++
 drivers/clk/clk-uclass.c | 44 ++++++++++++++++++++++++++++++++++++++++++--
 drivers/clk/clk.c        | 14 ++++++++++++--
 include/clk.h            |  2 ++
 4 files changed, 68 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 19aa2ffa5396..bb2a97a4a07a 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -40,6 +40,18 @@ config VPL_CLK
 	  setting up clocks within TPL, and allows the same drivers to be
 	  used as U-Boot proper.
 
+config CLK_LAZY_REPARENT
+	bool "Enable clock lazy reparenting feature"
+	depends on CLK_CCF
+	default n if SPL_CLK || TPL_CLK || VPL_CLK
+	default y
+	help
+	  This option allows registering clocks in a less strict order that
+	  Parent clocks can be registered before their children. The clock subsystem
+	  will cache the parent's name and resolve it to the real parent device "lazily".
+	  This is the default behavior in Linux clock subsystem. Enabling this feature
+	  should simplifies the porting of Linux clock drivers to U-Boot.
+
 config CLK_BCM6345
 	bool "Clock controller driver for BCM6345"
 	depends on CLK && ARCH_BMIPS
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index bfad71e7af70..cea79e05ea09 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -490,6 +490,32 @@ ulong clk_get_rate(struct clk *clk)
 	return ops->get_rate(clk);
 }
 
+static struct udevice *clk_reparent(struct clk *clk, const char *parent_name)
+{
+	struct udevice *pdev;
+	int ret;
+
+	if (!clk_valid(clk))
+		return NULL;
+
+	if (!parent_name)
+		return NULL;
+
+	debug("%s(clk=%p) reparenting to %s\n", __func__, clk, parent_name);
+
+	ret = uclass_get_device_by_name(UCLASS_CLK, parent_name, &pdev);
+	if (ret) {
+		log_err("%s(clk=%p) failed to find parent \"%s\"\n", __func__, clk, parent_name);
+		return NULL;
+	}
+
+	ret = device_reparent(clk->dev, pdev);
+	if (ret)
+		return NULL;
+
+	return pdev;
+}
+
 struct clk *clk_get_parent(struct clk *clk)
 {
 	struct udevice *pdev;
@@ -500,8 +526,18 @@ struct clk *clk_get_parent(struct clk *clk)
 		return NULL;
 
 	pdev = dev_get_parent(clk->dev);
-	if (!pdev)
-		return ERR_PTR(-ENODEV);
+	if (!pdev) {
+		if (CONFIG_IS_ENABLED(CLK_LAZY_REPARENT)) {
+			pdev = clk_reparent(clk, clk->parent_name);
+			free(clk->parent_name);
+			clk->parent_name = NULL;
+
+			if (!pdev)
+				return ERR_PTR(-ENODEV);
+		} else {
+			return ERR_PTR(-ENODEV);
+		}
+	}
 
 	if (device_get_uclass_id(pdev) != UCLASS_CLK)
 		return ERR_PTR(-ENODEV);
@@ -625,6 +661,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
 	debug("%s(clk=%p, parent=%p)\n", __func__, clk, parent);
 	if (!clk_valid(clk))
 		return 0;
+
+	free(clk->parent_name);
+	clk->parent_name = NULL;
+
 	ops = clk_dev_ops(clk->dev);
 
 	if (!ops->set_parent)
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index b8c2e8d531b9..32b3c03ab09a 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -24,8 +24,18 @@ int clk_register(struct clk *clk, const char *drv_name,
 	if (parent_name) {
 		ret = uclass_get_device_by_name(UCLASS_CLK, parent_name, &parent);
 		if (ret) {
-			log_err("%s: failed to get %s device (parent of %s)\n",
-				__func__, parent_name, name);
+			log_debug("%s: failed to get %s device (parent of %s)\n",
+				  __func__, parent_name, name);
+
+			if (CONFIG_IS_ENABLED(CLK_LAZY_REPARENT)) {
+				/*
+				 * The parent is not yet registered.
+				 * Cache the parent name and resolve it later.
+				 */
+				clk->parent_name = strdup(parent_name);
+				if (!clk->parent_name)
+					return -ENOMEM;
+			}
 		} else {
 			log_debug("%s: name: %s parent: %s [0x%p]\n", __func__, name,
 				  parent->name, parent);
diff --git a/include/clk.h b/include/clk.h
index a6ef4e026922..5f684dcbf9d2 100644
--- a/include/clk.h
+++ b/include/clk.h
@@ -37,6 +37,7 @@ struct udevice;
 /**
  * struct clk - A handle to (allowing control of) a single clock.
  * @dev: The device which implements the clock signal.
+ * @parent_name: The name of the parent.
  * @rate: The clock rate (in HZ).
  * @flags: Flags used across common clock structure (e.g. %CLK_)
  *         Clock IP blocks specific flags (i.e. mux, div, gate, etc) are defined
@@ -62,6 +63,7 @@ struct udevice;
  */
 struct clk {
 	struct udevice *dev;
+	char *parent_name;
 	long long rate;	/* in HZ */
 	u32 flags;
 	int enable_count;

-- 
2.43.0




More information about the U-Boot mailing list