summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Kconfig12
-rw-r--r--drivers/clk/clk-uclass.c72
-rw-r--r--drivers/clk/clk.c14
-rw-r--r--drivers/clk/clk_sandbox_ccf.c10
-rw-r--r--drivers/core/device.c8
5 files changed, 102 insertions, 14 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 c33f415917e..009255cbdee 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -496,6 +496,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;
@@ -506,8 +532,22 @@ struct clk *clk_get_parent(struct clk *clk)
return NULL;
pdev = dev_get_parent(clk->dev);
- if (!pdev)
+ 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);
+
pclk = dev_get_clk_ptr(pdev);
if (!pclk)
return ERR_PTR(-ENODEV);
@@ -627,6 +667,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)
@@ -660,7 +704,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
int clk_enable(struct clk *clk)
{
const struct clk_ops *ops;
- struct clk *clkp = NULL;
+ struct clk *clkp = NULL, *clk_parent;
int ret;
debug("%s(clk=%p name=%s)\n", __func__, clk, clk ? clk->dev->name : "NULL");
@@ -676,9 +720,10 @@ int clk_enable(struct clk *clk)
clkp->enable_count++;
return 0;
}
- if (clkp->dev->parent &&
- device_get_uclass_id(clkp->dev->parent) == UCLASS_CLK) {
- ret = clk_enable(dev_get_clk_ptr(clkp->dev->parent));
+
+ clk_parent = clk_get_parent(clkp);
+ if (!IS_ERR_OR_NULL(clk_parent)) {
+ ret = clk_enable(clk_parent);
if (ret) {
printf("Enable %s failed\n",
clkp->dev->parent->name);
@@ -751,13 +796,16 @@ int clk_disable(struct clk *clk)
return ret;
}
- if (clkp && clkp->dev->parent &&
- device_get_uclass_id(clkp->dev->parent) == UCLASS_CLK) {
- ret = clk_disable(dev_get_clk_ptr(clkp->dev->parent));
- if (ret) {
- printf("Disable %s failed\n",
- clkp->dev->parent->name);
- return ret;
+ if (clkp) {
+ struct clk *clk_parent = clk_get_parent(clkp);
+
+ if (!IS_ERR_OR_NULL(clk_parent)) {
+ ret = clk_disable(clk_parent);
+ if (ret) {
+ printf("Disable %s failed\n",
+ clkp->dev->parent->name);
+ return ret;
+ }
}
}
} else {
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/drivers/clk/clk_sandbox_ccf.c b/drivers/clk/clk_sandbox_ccf.c
index 9b8036d41aa..86f75132d54 100644
--- a/drivers/clk/clk_sandbox_ccf.c
+++ b/drivers/clk/clk_sandbox_ccf.c
@@ -229,6 +229,7 @@ static const struct udevice_id sandbox_clk_ccf_test_ids[] = {
static const char *const usdhc_sels[] = { "pll3_60m", "pll3_80m", };
static const char *const i2c_sels[] = { "pll3_60m", "pll3_80m", };
+static const char *const i2s_sels[] = { "pll3_60m", "pll3_80m", };
static int sandbox_clk_ccf_probe(struct udevice *dev)
{
@@ -277,6 +278,15 @@ static int sandbox_clk_ccf_probe(struct udevice *dev)
dev_clk_dm(dev, SANDBOX_CLK_I2C_ROOT,
sandbox_clk_gate2("i2c_root", "i2c", base + 0x7c, 0));
+ /* Register i2s_root(child) and i2s(parent) in reverse order to test CLK_LAZY_REPARENT */
+ dev_clk_dm(dev, SANDBOX_CLK_I2S_ROOT,
+ sandbox_clk_gate2("i2s_root", "i2s", base + 0x80, 0));
+
+ reg = BIT(29) | BIT(25) | BIT(17);
+ dev_clk_dm(dev, SANDBOX_CLK_I2S,
+ sandbox_clk_composite("i2s", i2s_sels, ARRAY_SIZE(i2s_sels),
+ &reg, CLK_SET_RATE_UNGATE));
+
return 0;
}
diff --git a/drivers/core/device.c b/drivers/core/device.c
index 779f371b9d5..0ae09f5a4e3 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -285,6 +285,14 @@ int device_reparent(struct udevice *dev, struct udevice *new_parent)
assert(dev);
assert(new_parent);
+ if (!dev->parent) {
+ assert(list_empty(&dev->sibling_node));
+
+ list_add_tail(&dev->sibling_node, &new_parent->child_head);
+ dev->parent = new_parent;
+ return 0;
+ }
+
device_foreach_child_safe(pos, n, dev->parent) {
if (pos->driver != dev->driver)
continue;