[05/29] clk: clk-uclass: Implement CLK_OPS_PARENT_ENABLE

Message ID 20240903153100.918077-5-dario.binacchi@amarulasolutions.com
State New
Headers show
Series
  • [01/29] clk: Propagate clk_set_rate() if CLK_SET_PARENT_RATE present for gate and mux
Related show

Commit Message

Dario Binacchi Sept. 3, 2024, 3:30 p.m. UTC
From: Michael Trimarchi <michael@amarulasolutions.com>

There are scenario that we need to enable the new parent clock
before reparent, or we need to do the same with clk_set_rate. The
patch cover those scenario

Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
---
 drivers/clk/clk-uclass.c | 47 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 46 insertions(+), 1 deletion(-)

Patch

diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index 4c2ca9909469..fdce69cb48e0 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -582,7 +582,9 @@  static void clk_clean_rate_cache(struct clk *clk)
 ulong clk_set_rate(struct clk *clk, ulong rate)
 {
 	const struct clk_ops *ops;
+	struct clk *pclk;
 	struct clk *clkp;
+	ulong ret;
 
 	debug("%s(clk=%p, rate=%lu)\n", __func__, clk, rate);
 	if (!clk_valid(clk))
@@ -597,11 +599,37 @@  ulong clk_set_rate(struct clk *clk, ulong rate)
 	/* Clean up cached rates for us and all child clocks */
 	clk_clean_rate_cache(clkp);
 
-	return ops->set_rate(clk, rate);
+	if (clk->flags & CLK_SET_RATE_UNGATE) {
+		ret = clk_enable(clk);
+		if (ret)
+			return ret;
+	}
+
+	pclk = clk_get_parent(clk);
+	if (pclk) {
+		if (clk->flags & CLK_OPS_PARENT_ENABLE) {
+			ret = clk_enable(pclk);
+			if (ret)
+				goto out;
+		}
+	}
+
+	ret = ops->set_rate(clk, rate);
+
+	if (pclk && clk->flags & CLK_OPS_PARENT_ENABLE)
+		clk_disable(pclk);
+
+out:
+	if (clk->flags & CLK_SET_RATE_UNGATE)
+		clk_disable(clk);
+
+	return ret;
 }
 
 int clk_set_parent(struct clk *clk, struct clk *parent)
 {
+	struct clk *old_parent;
+
 	const struct clk_ops *ops;
 	int ret;
 
@@ -613,6 +641,15 @@  int clk_set_parent(struct clk *clk, struct clk *parent)
 	if (!ops->set_parent)
 		return -ENOSYS;
 
+	if (clk->enable_count)
+		clk_enable(parent);
+
+	old_parent = clk_get_parent(clk);
+	if (clk->flags & CLK_OPS_PARENT_ENABLE) {
+		clk_enable(old_parent);
+		clk_enable(parent);
+	}
+
 	ret = ops->set_parent(clk, parent);
 	if (ret)
 		return ret;
@@ -620,6 +657,14 @@  int clk_set_parent(struct clk *clk, struct clk *parent)
 	if (CONFIG_IS_ENABLED(CLK_CCF))
 		ret = device_reparent(clk->dev, parent->dev);
 
+	if (clk->flags & CLK_OPS_PARENT_ENABLE) {
+		clk_disable(parent);
+		clk_disable(old_parent);
+	}
+
+	if (clk->enable_count)
+		clk_disable(old_parent);
+
 	return ret;
 }