diff options
| author | Yang Xiwen <forbidden405@outlook.com> | 2026-01-20 03:07:22 +0800 |
|---|---|---|
| committer | Tom Rini <trini@konsulko.com> | 2026-02-02 13:40:41 -0600 |
| commit | 8f230323e42fa7dd69bcfda1e7810b3327f4b4f6 (patch) | |
| tree | 40edc9e915f33c2cb9960271dbaabf68be91553f | |
| parent | 5fc1388141de3660c271cf99e5fc4036fb03ae84 (diff) | |
clk: allow assigning parent lazily
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.
Reviewed-by: Simon Glass <simon.glass@canonical.com>
Signed-off-by: Yang Xiwen <forbidden405@outlook.com>
| -rw-r--r-- | drivers/clk/Kconfig | 12 | ||||
| -rw-r--r-- | drivers/clk/clk-uclass.c | 44 | ||||
| -rw-r--r-- | drivers/clk/clk.c | 14 | ||||
| -rw-r--r-- | include/clk.h | 2 |
4 files changed, 68 insertions, 4 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index ae783254008..5a57adef3cc 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -51,6 +51,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 297d4d63a57..a7f02d339a5 100644 --- a/drivers/clk/clk-uclass.c +++ b/drivers/clk/clk-uclass.c @@ -495,6 +495,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; @@ -505,8 +531,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); @@ -630,6 +666,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 b8c2e8d531b..32b3c03ab09 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 90b42a61867..88db75c56d4 100644 --- a/include/clk.h +++ b/include/clk.h @@ -47,6 +47,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 @@ -72,6 +73,7 @@ struct udevice; */ struct clk { struct udevice *dev; + char *parent_name; long long rate; /* in HZ */ u32 flags; int enable_count; |
