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>
This commit is contained in:
Yang Xiwen
2026-01-20 03:07:22 +08:00
committed by Tom Rini
parent 5fc1388141
commit 8f230323e4
4 changed files with 68 additions and 4 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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);

View File

@@ -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;