From: Yang Xiwen <[email protected]>

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 <[email protected]>
---
 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


Reply via email to