Linux-Clk Archive on lore.kernel.org
 help / Atom feed
* [PATCH] clk: imx: Refactor entire sccg pll clk
@ 2019-01-29 14:33 Abel Vesa
  2019-02-11 10:54 ` Lucas Stach
  0 siblings, 1 reply; 2+ messages in thread
From: Abel Vesa @ 2019-01-29 14:33 UTC (permalink / raw)
  To: Stephen Boyd, Mike Turquette, Shawn Guo, Sascha Hauer,
	Fabio Estevam, Fabio Estevam, Anson Huang, Lucas Stach,
	Jacky Bai, Aisheng Dong
  Cc: dl-linux-imx, linux-clk, linux-arm-kernel,
	Linux Kernel Mailing List, Abel Vesa

Make the entire combination of plls to be one single clock. The parents used
for bypasses are specified each as an index in the parents list.
The determine_rate does a lookup throughout all the possible combinations
for all the divs and returns the best possible 'setup' which in turn is used
by set_rate later to set up all the divs and bypasses.

Signed-off-by: Abel Vesa <abel.vesa@nxp.com>
---
 drivers/clk/imx/clk-imx8mq.c   |  58 ++---
 drivers/clk/imx/clk-sccg-pll.c | 514 ++++++++++++++++++++++++++++++++---------
 drivers/clk/imx/clk.h          |   9 +-
 3 files changed, 423 insertions(+), 158 deletions(-)

diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c
index 398ab0b..7326dce 100644
--- a/drivers/clk/imx/clk-imx8mq.c
+++ b/drivers/clk/imx/clk-imx8mq.c
@@ -34,15 +34,10 @@ static const char * const audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_
 static const char * const audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", };
 static const char * const video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", };
 
-static const char * const sys1_pll1_out_sels[] = {"sys1_pll1", "sys1_pll1_ref_sel", };
-static const char * const sys2_pll1_out_sels[] = {"sys2_pll1", "sys1_pll1_ref_sel", };
-static const char * const sys3_pll1_out_sels[] = {"sys3_pll1", "sys3_pll1_ref_sel", };
-static const char * const dram_pll1_out_sels[] = {"dram_pll1", "dram_pll1_ref_sel", };
-
-static const char * const sys1_pll2_out_sels[] = {"sys1_pll2_div", "sys1_pll1_ref_sel", };
-static const char * const sys2_pll2_out_sels[] = {"sys2_pll2_div", "sys2_pll1_ref_sel", };
-static const char * const sys3_pll2_out_sels[] = {"sys3_pll2_div", "sys2_pll1_ref_sel", };
-static const char * const dram_pll2_out_sels[] = {"dram_pll2_div", "dram_pll1_ref_sel", };
+static const char * const sys1_pll_out_sels[] = {"sys1_pll1_ref_sel", };
+static const char * const sys2_pll_out_sels[] = {"sys1_pll1_ref_sel", "sys2_pll1_ref_sel", };
+static const char * const sys3_pll_out_sels[] = {"sys3_pll1_ref_sel", "sys2_pll1_ref_sel", };
+static const char * const dram_pll_out_sels[] = {"dram_pll1_ref_sel", };
 
 /* CCM ROOT */
 static const char * const imx8mq_a53_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m",
@@ -308,10 +303,6 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
 	clks[IMX8MQ_AUDIO_PLL1_REF_DIV] = imx_clk_divider("audio_pll1_ref_div", "audio_pll1_ref_sel", base + 0x0, 5, 6);
 	clks[IMX8MQ_AUDIO_PLL2_REF_DIV] = imx_clk_divider("audio_pll2_ref_div", "audio_pll2_ref_sel", base + 0x8, 5, 6);
 	clks[IMX8MQ_VIDEO_PLL1_REF_DIV] = imx_clk_divider("video_pll1_ref_div", "video_pll1_ref_sel", base + 0x10, 5, 6);
-	clks[IMX8MQ_SYS1_PLL1_REF_DIV]	= imx_clk_divider("sys1_pll1_ref_div", "sys1_pll1_ref_sel", base + 0x38, 25, 3);
-	clks[IMX8MQ_SYS2_PLL1_REF_DIV]	= imx_clk_divider("sys2_pll1_ref_div", "sys2_pll1_ref_sel", base + 0x44, 25, 3);
-	clks[IMX8MQ_SYS3_PLL1_REF_DIV]	= imx_clk_divider("sys3_pll1_ref_div", "sys3_pll1_ref_sel", base + 0x50, 25, 3);
-	clks[IMX8MQ_DRAM_PLL1_REF_DIV]	= imx_clk_divider("dram_pll1_ref_div", "dram_pll1_ref_sel", base + 0x68, 25, 3);
 
 	clks[IMX8MQ_ARM_PLL] = imx_clk_frac_pll("arm_pll", "arm_pll_ref_div", base + 0x28);
 	clks[IMX8MQ_GPU_PLL] = imx_clk_frac_pll("gpu_pll", "gpu_pll_ref_div", base + 0x18);
@@ -319,25 +310,6 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
 	clks[IMX8MQ_AUDIO_PLL1] = imx_clk_frac_pll("audio_pll1", "audio_pll1_ref_div", base + 0x0);
 	clks[IMX8MQ_AUDIO_PLL2] = imx_clk_frac_pll("audio_pll2", "audio_pll2_ref_div", base + 0x8);
 	clks[IMX8MQ_VIDEO_PLL1] = imx_clk_frac_pll("video_pll1", "video_pll1_ref_div", base + 0x10);
-	clks[IMX8MQ_SYS1_PLL1] = imx_clk_sccg_pll("sys1_pll1", "sys1_pll1_ref_div", base + 0x30, SCCG_PLL1);
-	clks[IMX8MQ_SYS2_PLL1] = imx_clk_sccg_pll("sys2_pll1", "sys2_pll1_ref_div", base + 0x3c, SCCG_PLL1);
-	clks[IMX8MQ_SYS3_PLL1] = imx_clk_sccg_pll("sys3_pll1", "sys3_pll1_ref_div", base + 0x48, SCCG_PLL1);
-	clks[IMX8MQ_DRAM_PLL1] = imx_clk_sccg_pll("dram_pll1", "dram_pll1_ref_div", base + 0x60, SCCG_PLL1);
-
-	clks[IMX8MQ_SYS1_PLL2] = imx_clk_sccg_pll("sys1_pll2", "sys1_pll1_out_div", base + 0x30, SCCG_PLL2);
-	clks[IMX8MQ_SYS2_PLL2] = imx_clk_sccg_pll("sys2_pll2", "sys2_pll1_out_div", base + 0x3c, SCCG_PLL2);
-	clks[IMX8MQ_SYS3_PLL2] = imx_clk_sccg_pll("sys3_pll2", "sys3_pll1_out_div", base + 0x48, SCCG_PLL2);
-	clks[IMX8MQ_DRAM_PLL2] = imx_clk_sccg_pll("dram_pll2", "dram_pll1_out_div", base + 0x60, SCCG_PLL2);
-
-	/* PLL divs */
-	clks[IMX8MQ_SYS1_PLL1_OUT_DIV] = imx_clk_divider("sys1_pll1_out_div", "sys1_pll1_out", base + 0x38, 19, 6);
-	clks[IMX8MQ_SYS2_PLL1_OUT_DIV] = imx_clk_divider("sys2_pll1_out_div", "sys2_pll1_out", base + 0x44, 19, 6);
-	clks[IMX8MQ_SYS3_PLL1_OUT_DIV] = imx_clk_divider("sys3_pll1_out_div", "sys3_pll1_out", base + 0x50, 19, 6);
-	clks[IMX8MQ_DRAM_PLL1_OUT_DIV] = imx_clk_divider("dram_pll1_out_div", "dram_pll1_out", base + 0x68, 19, 6);
-	clks[IMX8MQ_SYS1_PLL2_DIV] = imx_clk_divider("sys1_pll2_div", "sys1_pll2", base + 0x38, 1, 6);
-	clks[IMX8MQ_SYS2_PLL2_DIV] = imx_clk_divider("sys2_pll2_div", "sys2_pll2", base + 0x44, 1, 6);
-	clks[IMX8MQ_SYS3_PLL2_DIV] = imx_clk_divider("sys3_pll2_div", "sys3_pll2", base + 0x50, 1, 6);
-	clks[IMX8MQ_DRAM_PLL2_DIV] = imx_clk_divider("dram_pll2_div", "dram_pll2", base + 0x68, 1, 6);
 
 	/* PLL bypass out */
 	clks[IMX8MQ_ARM_PLL_BYPASS] = imx_clk_mux("arm_pll_bypass", base + 0x28, 14, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels));
@@ -347,14 +319,12 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
 	clks[IMX8MQ_AUDIO_PLL2_BYPASS] = imx_clk_mux("audio_pll2_bypass", base + 0x8, 14, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels));
 	clks[IMX8MQ_VIDEO_PLL1_BYPASS] = imx_clk_mux("video_pll1_bypass", base + 0x10, 14, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels));
 
-	clks[IMX8MQ_SYS1_PLL1_OUT] = imx_clk_mux("sys1_pll1_out", base + 0x30, 5, 1, sys1_pll1_out_sels, ARRAY_SIZE(sys1_pll1_out_sels));
-	clks[IMX8MQ_SYS2_PLL1_OUT] = imx_clk_mux("sys2_pll1_out", base + 0x3c, 5, 1, sys2_pll1_out_sels, ARRAY_SIZE(sys2_pll1_out_sels));
-	clks[IMX8MQ_SYS3_PLL1_OUT] = imx_clk_mux("sys3_pll1_out", base + 0x48, 5, 1, sys3_pll1_out_sels, ARRAY_SIZE(sys3_pll1_out_sels));
-	clks[IMX8MQ_DRAM_PLL1_OUT] = imx_clk_mux("dram_pll1_out", base + 0x60, 5, 1, dram_pll1_out_sels, ARRAY_SIZE(dram_pll1_out_sels));
-	clks[IMX8MQ_SYS1_PLL2_OUT] = imx_clk_mux("sys1_pll2_out", base + 0x30, 4, 1, sys1_pll2_out_sels, ARRAY_SIZE(sys1_pll2_out_sels));
-	clks[IMX8MQ_SYS2_PLL2_OUT] = imx_clk_mux("sys2_pll2_out", base + 0x3c, 4, 1, sys2_pll2_out_sels, ARRAY_SIZE(sys2_pll2_out_sels));
-	clks[IMX8MQ_SYS3_PLL2_OUT] = imx_clk_mux("sys3_pll2_out", base + 0x48, 4, 1, sys3_pll2_out_sels, ARRAY_SIZE(sys3_pll2_out_sels));
-	clks[IMX8MQ_DRAM_PLL2_OUT] = imx_clk_mux("dram_pll2_out", base + 0x60, 4, 1, dram_pll2_out_sels, ARRAY_SIZE(dram_pll2_out_sels));
+	/* unbypass all the plls */
+	clk_set_parent(clks[IMX8MQ_GPU_PLL_BYPASS], clks[IMX8MQ_GPU_PLL]);
+	clk_set_parent(clks[IMX8MQ_VPU_PLL_BYPASS], clks[IMX8MQ_VPU_PLL]);
+	clk_set_parent(clks[IMX8MQ_AUDIO_PLL1_BYPASS], clks[IMX8MQ_AUDIO_PLL1]);
+	clk_set_parent(clks[IMX8MQ_AUDIO_PLL2_BYPASS], clks[IMX8MQ_AUDIO_PLL2]);
+	clk_set_parent(clks[IMX8MQ_VIDEO_PLL1_BYPASS], clks[IMX8MQ_VIDEO_PLL1]);
 
 	/* PLL OUT GATE */
 	clks[IMX8MQ_ARM_PLL_OUT] = imx_clk_gate("arm_pll_out", "arm_pll_bypass", base + 0x28, 21);
@@ -363,11 +333,11 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
 	clks[IMX8MQ_AUDIO_PLL1_OUT] = imx_clk_gate("audio_pll1_out", "audio_pll1_bypass", base + 0x0, 21);
 	clks[IMX8MQ_AUDIO_PLL2_OUT] = imx_clk_gate("audio_pll2_out", "audio_pll2_bypass", base + 0x8, 21);
 	clks[IMX8MQ_VIDEO_PLL1_OUT] = imx_clk_gate("video_pll1_out", "video_pll1_bypass", base + 0x10, 21);
-	clks[IMX8MQ_SYS1_PLL_OUT] = imx_clk_gate("sys1_pll_out", "sys1_pll2_out", base + 0x30, 9);
-	clks[IMX8MQ_SYS2_PLL_OUT] = imx_clk_gate("sys2_pll_out", "sys2_pll2_out", base + 0x3c, 9);
-	clks[IMX8MQ_SYS3_PLL_OUT] = imx_clk_gate("sys3_pll_out", "sys3_pll2_out", base + 0x48, 9);
-	clks[IMX8MQ_DRAM_PLL_OUT] = imx_clk_gate("dram_pll_out", "dram_pll2_out", base + 0x60, 9);
 
+	clks[IMX8MQ_SYS1_PLL_OUT] = imx_clk_sccg_pll("sys1_pll_out", sys1_pll_out_sels, ARRAY_SIZE(sys1_pll_out_sels), 0, 0, 0, base + 0x30, CLK_IS_CRITICAL);
+	clks[IMX8MQ_SYS2_PLL_OUT] = imx_clk_sccg_pll("sys2_pll_out", sys2_pll_out_sels, ARRAY_SIZE(sys2_pll_out_sels), 0, 0, 1, base + 0x3c, CLK_IS_CRITICAL);
+	clks[IMX8MQ_SYS3_PLL_OUT] = imx_clk_sccg_pll("sys3_pll_out", sys3_pll_out_sels, ARRAY_SIZE(sys3_pll_out_sels), 0, 0, 1, base + 0x48, CLK_IS_CRITICAL);
+	clks[IMX8MQ_DRAM_PLL_OUT] = imx_clk_sccg_pll("dram_pll_out", dram_pll_out_sels, ARRAY_SIZE(dram_pll_out_sels), 0, 0, 0, base + 0x60, CLK_IS_CRITICAL);
 	/* SYS PLL fixed output */
 	clks[IMX8MQ_SYS1_PLL_40M] = imx_clk_fixed_factor("sys1_pll_40m", "sys1_pll_out", 1, 20);
 	clks[IMX8MQ_SYS1_PLL_80M] = imx_clk_fixed_factor("sys1_pll_80m", "sys1_pll_out", 1, 10);
diff --git a/drivers/clk/imx/clk-sccg-pll.c b/drivers/clk/imx/clk-sccg-pll.c
index ee7752b..f480af3 100644
--- a/drivers/clk/imx/clk-sccg-pll.c
+++ b/drivers/clk/imx/clk-sccg-pll.c
@@ -25,87 +25,292 @@
 #define PLL_DIVF2_MASK		GENMASK(12, 7)
 #define PLL_DIVR1_MASK		GENMASK(27, 25)
 #define PLL_DIVR2_MASK		GENMASK(24, 19)
+#define PLL_DIVQ_MASK           GENMASK(6, 1)
 #define PLL_REF_MASK		GENMASK(2, 0)
 
 #define PLL_LOCK_MASK		BIT(31)
 #define PLL_PD_MASK		BIT(7)
 
-#define OSC_25M			25000000
-#define OSC_27M			27000000
+/* These are the specification limits for the SSCG PLL */
+#define PLL_REF_MIN_FREQ		25000000
+#define PLL_REF_MAX_FREQ		235000000
 
-#define PLL_SCCG_LOCK_TIMEOUT	70
+#define PLL_STAGE1_MIN_FREQ		1600000000
+#define PLL_STAGE1_MAX_FREQ		2400000000
+
+#define PLL_STAGE1_REF_MIN_FREQ		25000000
+#define PLL_STAGE1_REF_MAX_FREQ		54000000
+
+#define PLL_STAGE2_MIN_FREQ		1200000000
+#define PLL_STAGE2_MAX_FREQ		2400000000
+
+#define PLL_STAGE2_REF_MIN_FREQ		54000000
+#define PLL_STAGE2_REF_MAX_FREQ		75000000
+
+#define PLL_OUT_MIN_FREQ		20000000
+#define PLL_OUT_MAX_FREQ		1200000000
+
+#define PLL_DIVR1_MAX			7
+#define PLL_DIVR2_MAX			63
+#define PLL_DIVF1_MAX			63
+#define PLL_DIVF2_MAX			63
+#define PLL_DIVQ_MAX			63
+
+#define PLL_BYPASS_NONE			0x0
+#define PLL_BYPASS1			0x2
+#define PLL_BYPASS2			0x1
+
+#define SSCG_PLL_BYPASS1_MASK           BIT(5)
+#define SSCG_PLL_BYPASS2_MASK           BIT(4)
+#define SSCG_PLL_BYPASS_MASK		GENMASK(5, 4)
+
+#define PLL_SCCG_LOCK_TIMEOUT		70
+
+struct clk_sccg_pll_setup {
+	int divr1, divf1;
+	int divr2, divf2;
+	int divq;
+	int bypass;
+
+	unsigned long vco1;
+	unsigned long vco2;
+	unsigned long fout;
+	unsigned long ref;
+	unsigned long ref_div1;
+	unsigned long ref_div2;
+	unsigned long fout_request;
+	int fout_error;
+};
 
 struct clk_sccg_pll {
 	struct clk_hw	hw;
-	void __iomem	*base;
+	const struct clk_ops  ops;
+
+	void __iomem *base;
+
+	struct clk_sccg_pll_setup setup;
+
+	u8 parent;
+	u8 bypass1;
+	u8 bypass2;
 };
 
 #define to_clk_sccg_pll(_hw) container_of(_hw, struct clk_sccg_pll, hw)
 
-static int clk_pll_wait_lock(struct clk_sccg_pll *pll)
+static int clk_sccg_pll_wait_lock(struct clk_sccg_pll *pll)
 {
 	u32 val;
 
-	return readl_poll_timeout(pll->base, val, val & PLL_LOCK_MASK, 0,
-					PLL_SCCG_LOCK_TIMEOUT);
+	val = readl_relaxed(pll->base + PLL_CFG0);
+
+	/* don't wait for lock if all plls are bypassed */
+	if (!(val & SSCG_PLL_BYPASS2_MASK))
+		return readl_poll_timeout(pll->base, val, val & PLL_LOCK_MASK,
+						0, PLL_SCCG_LOCK_TIMEOUT);
+
+	return 0;
 }
 
-static int clk_pll1_is_prepared(struct clk_hw *hw)
+static int clk_sccg_pll2_check_match(struct clk_sccg_pll_setup *setup,
+					struct clk_sccg_pll_setup *temp_setup)
 {
-	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
-	u32 val;
+	int new_diff = temp_setup->fout - temp_setup->fout_request;
+	int diff = temp_setup->fout_error;
 
-	val = readl_relaxed(pll->base + PLL_CFG0);
-	return (val & PLL_PD_MASK) ? 0 : 1;
+	if (abs(diff) > abs(new_diff)) {
+		temp_setup->fout_error = new_diff;
+		memcpy(setup, temp_setup, sizeof(struct clk_sccg_pll_setup));
+
+		if (temp_setup->fout_request == temp_setup->fout)
+			return 0;
+	}
+	return -1;
 }
 
-static unsigned long clk_pll1_recalc_rate(struct clk_hw *hw,
-					 unsigned long parent_rate)
+static int clk_sccg_divq_lookup(struct clk_sccg_pll_setup *setup,
+				struct clk_sccg_pll_setup *temp_setup)
 {
-	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
-	u32 val, divf;
+	int ret = -EINVAL;
+
+	for (temp_setup->divq = 0; temp_setup->divq <= PLL_DIVQ_MAX;
+	     temp_setup->divq++) {
+		temp_setup->vco2 = temp_setup->vco1;
+		do_div(temp_setup->vco2, temp_setup->divr2 + 1);
+		temp_setup->vco2 *= 2;
+		temp_setup->vco2 *= temp_setup->divf2 + 1;
+		if (temp_setup->vco2 >= PLL_STAGE2_MIN_FREQ &&
+				temp_setup->vco2 <= PLL_STAGE2_MAX_FREQ) {
+			temp_setup->fout = temp_setup->vco2;
+			do_div(temp_setup->fout, 2 * (temp_setup->divq + 1));
+
+			ret = clk_sccg_pll2_check_match(setup, temp_setup);
+			if (!ret) {
+				temp_setup->bypass = PLL_BYPASS1;
+				return ret;
+			}
+		}
+	}
 
-	val = readl_relaxed(pll->base + PLL_CFG2);
-	divf = FIELD_GET(PLL_DIVF1_MASK, val);
+	return ret;
+}
+
+static int clk_sccg_divf2_lookup(struct clk_sccg_pll_setup *setup,
+					struct clk_sccg_pll_setup *temp_setup)
+{
+	int ret = -EINVAL;
+
+	for (temp_setup->divf2 = 0; temp_setup->divf2 <= PLL_DIVF2_MAX;
+	     temp_setup->divf2++) {
+		ret = clk_sccg_divq_lookup(setup, temp_setup);
+		if (!ret)
+			return ret;
+	}
 
-	return parent_rate * 2 * (divf + 1);
+	return ret;
 }
 
-static long clk_pll1_round_rate(struct clk_hw *hw, unsigned long rate,
-			       unsigned long *prate)
+static int clk_sccg_divr2_lookup(struct clk_sccg_pll_setup *setup,
+				struct clk_sccg_pll_setup *temp_setup)
 {
-	unsigned long parent_rate = *prate;
-	u32 div;
+	int ret = -EINVAL;
+
+	for (temp_setup->divr2 = 0; temp_setup->divr2 <= PLL_DIVR2_MAX;
+	     temp_setup->divr2++) {
+		temp_setup->ref_div2 = temp_setup->vco1;
+		do_div(temp_setup->ref_div2, temp_setup->divr2 + 1);
+		if (temp_setup->ref_div2 >= PLL_STAGE2_REF_MIN_FREQ &&
+		    temp_setup->ref_div2 <= PLL_STAGE2_REF_MAX_FREQ) {
+			ret = clk_sccg_divf2_lookup(setup, temp_setup);
+			if (!ret)
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int clk_sccg_pll2_find_setup(struct clk_sccg_pll_setup *setup,
+					struct clk_sccg_pll_setup *temp_setup,
+					long ref)
+{
+
+	int ret = -EINVAL;
 
-	if (!parent_rate)
-		return 0;
+	if (ref < PLL_STAGE1_MIN_FREQ || ref > PLL_STAGE1_MAX_FREQ)
+		return ret;
 
-	div = rate / (parent_rate * 2);
+	temp_setup->vco1 = ref;
 
-	return parent_rate * div * 2;
+	ret = clk_sccg_divr2_lookup(setup, temp_setup);
+	return ret;
 }
 
-static int clk_pll1_set_rate(struct clk_hw *hw, unsigned long rate,
-			    unsigned long parent_rate)
+static int clk_sccg_divf1_lookup(struct clk_sccg_pll_setup *setup,
+				struct clk_sccg_pll_setup *temp_setup)
 {
-	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
-	u32 val;
-	u32 divf;
+	int ret = -EINVAL;
 
-	if (!parent_rate)
-		return -EINVAL;
+	for (temp_setup->divf1 = 0; temp_setup->divf1 <= PLL_DIVF1_MAX;
+	     temp_setup->divf1++) {
+		long vco1 = temp_setup->ref;
 
-	divf = rate / (parent_rate * 2);
+		do_div(vco1, temp_setup->divr1 + 1);
+		vco1 *= 2;
+		vco1 *= temp_setup->divf1 + 1;
 
-	val = readl_relaxed(pll->base + PLL_CFG2);
-	val &= ~PLL_DIVF1_MASK;
-	val |= FIELD_PREP(PLL_DIVF1_MASK, divf - 1);
-	writel_relaxed(val, pll->base + PLL_CFG2);
+		ret = clk_sccg_pll2_find_setup(setup, temp_setup, vco1);
+		if (!ret) {
+			temp_setup->bypass = PLL_BYPASS_NONE;
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int clk_sccg_divr1_lookup(struct clk_sccg_pll_setup *setup,
+				struct clk_sccg_pll_setup *temp_setup)
+{
+	int ret = -EINVAL;
+
+	for (temp_setup->divr1 = 0; temp_setup->divr1 <= PLL_DIVR1_MAX;
+	     temp_setup->divr1++) {
+		temp_setup->ref_div1 = temp_setup->ref;
+		do_div(temp_setup->ref_div1, temp_setup->divr1 + 1);
+		if (temp_setup->ref_div1 >= PLL_STAGE1_REF_MIN_FREQ &&
+		    temp_setup->ref_div1 <= PLL_STAGE1_REF_MAX_FREQ) {
+			ret = clk_sccg_divf1_lookup(setup, temp_setup);
+			if (!ret)
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int clk_sccg_pll1_find_setup(struct clk_sccg_pll_setup *setup,
+					struct clk_sccg_pll_setup *temp_setup,
+					long ref)
+{
+
+	int ret = -EINVAL;
+
+	if (ref < PLL_REF_MIN_FREQ || ref > PLL_REF_MAX_FREQ)
+		return ret;
+
+	temp_setup->ref = ref;
+
+	ret = clk_sccg_divr1_lookup(setup, temp_setup);
+
+	return ret;
+}
+
+static int clk_sccg_pll_find_setup(struct clk_sccg_pll_setup *setup,
+					unsigned long prate,
+					unsigned long rate, int try_bypass)
+{
+	struct clk_sccg_pll_setup temp_setup;
+	int ret = -EINVAL;
+
+	memset(&temp_setup, 0, sizeof(struct clk_sccg_pll_setup));
+	memset(setup, 0, sizeof(struct clk_sccg_pll_setup));
+
+	temp_setup.fout_error = PLL_OUT_MAX_FREQ;
+	temp_setup.fout_request = rate;
 
-	return clk_pll_wait_lock(pll);
+	switch (try_bypass) {
+
+	case PLL_BYPASS2:
+		if (prate == rate) {
+			setup->bypass = PLL_BYPASS2;
+			setup->fout = rate;
+			ret = 0;
+		}
+		break;
+
+	case PLL_BYPASS1:
+		ret = clk_sccg_pll2_find_setup(setup, &temp_setup, prate);
+		break;
+
+	case PLL_BYPASS_NONE:
+		ret = clk_sccg_pll1_find_setup(setup, &temp_setup, prate);
+		break;
+	}
+
+	return ret;
+}
+
+
+static int clk_sccg_pll_is_prepared(struct clk_hw *hw)
+{
+	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+
+	u32 val = readl_relaxed(pll->base + PLL_CFG0);
+
+	return (val & PLL_PD_MASK) ? 0 : 1;
 }
 
-static int clk_pll1_prepare(struct clk_hw *hw)
+static int clk_sccg_pll_prepare(struct clk_hw *hw)
 {
 	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
 	u32 val;
@@ -114,10 +319,10 @@ static int clk_pll1_prepare(struct clk_hw *hw)
 	val &= ~PLL_PD_MASK;
 	writel_relaxed(val, pll->base + PLL_CFG0);
 
-	return clk_pll_wait_lock(pll);
+	return clk_sccg_pll_wait_lock(pll);
 }
 
-static void clk_pll1_unprepare(struct clk_hw *hw)
+static void clk_sccg_pll_unprepare(struct clk_hw *hw)
 {
 	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
 	u32 val;
@@ -125,121 +330,208 @@ static void clk_pll1_unprepare(struct clk_hw *hw)
 	val = readl_relaxed(pll->base + PLL_CFG0);
 	val |= PLL_PD_MASK;
 	writel_relaxed(val, pll->base + PLL_CFG0);
-
 }
 
-static unsigned long clk_pll2_recalc_rate(struct clk_hw *hw,
+static unsigned long clk_sccg_pll_recalc_rate(struct clk_hw *hw,
 					 unsigned long parent_rate)
 {
 	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
-	u32 val, ref, divr1, divf1, divr2, divf2;
+	u32 val, divr1, divf1, divr2, divf2, divq;
 	u64 temp64;
 
-	val = readl_relaxed(pll->base + PLL_CFG0);
-	switch (FIELD_GET(PLL_REF_MASK, val)) {
-	case 0:
-		ref = OSC_25M;
-		break;
-	case 1:
-		ref = OSC_27M;
-		break;
-	default:
-		ref = OSC_25M;
-		break;
-	}
-
 	val = readl_relaxed(pll->base + PLL_CFG2);
 	divr1 = FIELD_GET(PLL_DIVR1_MASK, val);
 	divr2 = FIELD_GET(PLL_DIVR2_MASK, val);
 	divf1 = FIELD_GET(PLL_DIVF1_MASK, val);
 	divf2 = FIELD_GET(PLL_DIVF2_MASK, val);
+	divq = FIELD_GET(PLL_DIVQ_MASK, val);
+
+	temp64 = parent_rate;
+
+	val = clk_readl(pll->base + PLL_CFG0);
+	if (val & SSCG_PLL_BYPASS2_MASK) {
+		temp64 = parent_rate;
+	} else if (val & SSCG_PLL_BYPASS1_MASK) {
+		temp64 *= divf2;
+		do_div(temp64, (divr2 + 1) * (divq + 1));
+	} else {
+		temp64 *= 2;
+		temp64 *= (divf1 + 1) * (divf2 + 1);
+		do_div(temp64, (divr1 + 1) * (divr2 + 1) * (divq + 1));
+	}
+
+	return (unsigned long)temp64;
+}
 
-	temp64 = ref * 2;
-	temp64 *= (divf1 + 1) * (divf2 + 1);
+static int clk_sccg_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			    unsigned long parent_rate)
+{
+	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+	struct clk_sccg_pll_setup *setup = &pll->setup;
+	u32 val;
 
-	do_div(temp64, (divr1 + 1) * (divr2 + 1));
+	/* set bypass here too since the parent might be the same */
+	val = clk_readl(pll->base + PLL_CFG0);
+	val &= ~SSCG_PLL_BYPASS_MASK;
+	val |= FIELD_PREP(SSCG_PLL_BYPASS_MASK, setup->bypass);
+	clk_writel(val, pll->base + PLL_CFG0);
 
-	return temp64;
+	val = readl_relaxed(pll->base + PLL_CFG2);
+	val &= ~(PLL_DIVF1_MASK | PLL_DIVF2_MASK);
+	val &= ~(PLL_DIVR1_MASK | PLL_DIVR2_MASK | PLL_DIVQ_MASK);
+	val |= FIELD_PREP(PLL_DIVF1_MASK, setup->divf1);
+	val |= FIELD_PREP(PLL_DIVF2_MASK, setup->divf2);
+	val |= FIELD_PREP(PLL_DIVR1_MASK, setup->divr1);
+	val |= FIELD_PREP(PLL_DIVR2_MASK, setup->divr2);
+	val |= FIELD_PREP(PLL_DIVQ_MASK, setup->divq);
+	writel_relaxed(val, pll->base + PLL_CFG2);
+
+	return clk_sccg_pll_wait_lock(pll);
 }
 
-static long clk_pll2_round_rate(struct clk_hw *hw, unsigned long rate,
-			       unsigned long *prate)
+static u8 clk_sccg_pll_get_parent(struct clk_hw *hw)
 {
-	u32 div;
-	unsigned long parent_rate = *prate;
+	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+	u32 val;
+	u8 ret = pll->parent;
+
+	val = clk_readl(pll->base + PLL_CFG0);
+	if (val & SSCG_PLL_BYPASS2_MASK)
+		ret = pll->bypass2;
+	else if (val & SSCG_PLL_BYPASS1_MASK)
+		ret = pll->bypass1;
+	return ret;
+}
 
-	if (!parent_rate)
-		return 0;
+static int clk_sccg_pll_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+	u32 val;
 
-	div = rate / parent_rate;
+	val = clk_readl(pll->base + PLL_CFG0);
+	val &= ~SSCG_PLL_BYPASS_MASK;
+	val |= FIELD_PREP(SSCG_PLL_BYPASS_MASK, pll->setup.bypass);
+	clk_writel(val, pll->base + PLL_CFG0);
 
-	return parent_rate * div;
+	return clk_sccg_pll_wait_lock(pll);
 }
 
-static int clk_pll2_set_rate(struct clk_hw *hw, unsigned long rate,
-			    unsigned long parent_rate)
+static int __clk_sccg_pll_determine_rate(struct clk_hw *hw,
+					struct clk_rate_request *req,
+					unsigned long min,
+					unsigned long max,
+					unsigned long rate,
+					int bypass)
 {
-	u32 val;
-	u32 divf;
 	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+	struct clk_sccg_pll_setup *setup = &pll->setup;
+	struct clk_hw *parent_hw = NULL;
+	int bypass_parent_index;
+	int ret = -EINVAL;
 
-	if (!parent_rate)
-		return -EINVAL;
+	req->max_rate = max;
+	req->min_rate = min;
 
-	divf = rate / parent_rate;
+	switch (bypass) {
+	case PLL_BYPASS2:
+		bypass_parent_index = pll->bypass2;
+		break;
+	case PLL_BYPASS1:
+		bypass_parent_index = pll->bypass1;
+		break;
+	default:
+		bypass_parent_index = pll->parent;
+		break;
+	}
 
-	val = readl_relaxed(pll->base + PLL_CFG2);
-	val &= ~PLL_DIVF2_MASK;
-	val |= FIELD_PREP(PLL_DIVF2_MASK, divf - 1);
-	writel_relaxed(val, pll->base + PLL_CFG2);
+	parent_hw = clk_hw_get_parent_by_index(hw, bypass_parent_index);
+	ret = __clk_determine_rate(parent_hw, req);
+	if (!ret) {
+		ret = clk_sccg_pll_find_setup(setup, req->rate,
+						rate, bypass);
+	}
 
-	return clk_pll_wait_lock(pll);
+	req->best_parent_hw = parent_hw;
+	req->best_parent_rate = req->rate;
+	req->rate = setup->fout;
+
+	return ret;
 }
 
-static const struct clk_ops clk_sccg_pll1_ops = {
-	.is_prepared	= clk_pll1_is_prepared,
-	.recalc_rate	= clk_pll1_recalc_rate,
-	.round_rate	= clk_pll1_round_rate,
-	.set_rate	= clk_pll1_set_rate,
-};
+static int clk_sccg_pll_determine_rate(struct clk_hw *hw,
+				       struct clk_rate_request *req)
+{
+	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
+	struct clk_sccg_pll_setup *setup = &pll->setup;
+	unsigned long rate = req->rate;
+	unsigned long min = req->min_rate;
+	unsigned long max = req->max_rate;
+	int ret = -EINVAL;
+
+	if (rate < PLL_OUT_MIN_FREQ || rate > PLL_OUT_MAX_FREQ)
+		return ret;
+
+	ret = __clk_sccg_pll_determine_rate(hw, req, req->rate, req->rate,
+						rate, PLL_BYPASS2);
+	if (!ret)
+		return ret;
+
+	ret = __clk_sccg_pll_determine_rate(hw, req, PLL_STAGE1_REF_MIN_FREQ,
+						PLL_STAGE1_REF_MAX_FREQ, rate,
+						PLL_BYPASS1);
+	if (!ret)
+		return ret;
+
+	ret = __clk_sccg_pll_determine_rate(hw, req, PLL_REF_MIN_FREQ,
+						PLL_REF_MAX_FREQ, rate,
+						PLL_BYPASS_NONE);
+	if (!ret)
+		return ret;
+
+	if (setup->fout >= min && setup->fout <= max)
+		ret = 0;
+
+	return ret;
+}
 
-static const struct clk_ops clk_sccg_pll2_ops = {
-	.prepare	= clk_pll1_prepare,
-	.unprepare	= clk_pll1_unprepare,
-	.recalc_rate	= clk_pll2_recalc_rate,
-	.round_rate	= clk_pll2_round_rate,
-	.set_rate	= clk_pll2_set_rate,
+static const struct clk_ops clk_sccg_pll_ops = {
+	.prepare	= clk_sccg_pll_prepare,
+	.unprepare	= clk_sccg_pll_unprepare,
+	.is_prepared	= clk_sccg_pll_is_prepared,
+	.recalc_rate	= clk_sccg_pll_recalc_rate,
+	.set_rate	= clk_sccg_pll_set_rate,
+	.set_parent	= clk_sccg_pll_set_parent,
+	.get_parent	= clk_sccg_pll_get_parent,
+	.determine_rate	= clk_sccg_pll_determine_rate,
 };
 
 struct clk *imx_clk_sccg_pll(const char *name,
-				const char *parent_name,
+				const char * const *parent_names,
+				u8 num_parents,
+				u8 parent, u8 bypass1, u8 bypass2,
 				void __iomem *base,
-				enum imx_sccg_pll_type pll_type)
+				unsigned long flags)
 {
 	struct clk_sccg_pll *pll;
 	struct clk_init_data init;
 	struct clk_hw *hw;
 	int ret;
 
-	switch (pll_type) {
-	case SCCG_PLL1:
-		init.ops = &clk_sccg_pll1_ops;
-		break;
-	case SCCG_PLL2:
-		init.ops = &clk_sccg_pll2_ops;
-		break;
-	default:
-		return ERR_PTR(-EINVAL);
-	}
-
 	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
 	if (!pll)
 		return ERR_PTR(-ENOMEM);
 
+	pll->parent = parent;
+	pll->bypass1 = bypass1;
+	pll->bypass2 = bypass2;
+
+	pll->base = base;
 	init.name = name;
-	init.flags = 0;
-	init.parent_names = &parent_name;
-	init.num_parents = 1;
+	init.ops = &clk_sccg_pll_ops;
+
+	init.flags = flags;
+	init.parent_names = parent_names;
+	init.num_parents = num_parents;
 
 	pll->base = base;
 	pll->hw.init = &init;
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
index 2e442d8..dc7ee99 100644
--- a/drivers/clk/imx/clk.h
+++ b/drivers/clk/imx/clk.h
@@ -36,9 +36,12 @@ struct clk *imx_clk_pllv2(const char *name, const char *parent,
 struct clk *imx_clk_frac_pll(const char *name, const char *parent_name,
 			     void __iomem *base);
 
-struct clk *imx_clk_sccg_pll(const char *name, const char *parent_name,
-			     void __iomem *base,
-			     enum imx_sccg_pll_type pll_type);
+struct clk *imx_clk_sccg_pll(const char *name,
+				const char * const *parent_names,
+				u8 num_parents,
+				u8 parent, u8 bypass1, u8 bypass2,
+				void __iomem *base,
+				unsigned long flags);
 
 enum imx_pllv3_type {
 	IMX_PLLV3_GENERIC,
-- 
2.7.4


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] clk: imx: Refactor entire sccg pll clk
  2019-01-29 14:33 [PATCH] clk: imx: Refactor entire sccg pll clk Abel Vesa
@ 2019-02-11 10:54 ` Lucas Stach
  0 siblings, 0 replies; 2+ messages in thread
From: Lucas Stach @ 2019-02-11 10:54 UTC (permalink / raw)
  To: Abel Vesa, Stephen Boyd, Mike Turquette, Shawn Guo, Sascha Hauer,
	Fabio Estevam, Fabio Estevam, Anson Huang, Jacky Bai,
	Aisheng Dong
  Cc: dl-linux-imx, linux-clk, linux-arm-kernel, Linux Kernel Mailing List

Am Dienstag, den 29.01.2019, 14:33 +0000 schrieb Abel Vesa:
> Make the entire combination of plls to be one single clock. The parents used
> for bypasses are specified each as an index in the parents list.
> The determine_rate does a lookup throughout all the possible combinations
> for all the divs and returns the best possible 'setup' which in turn is used
> by set_rate later to set up all the divs and bypasses.

I can't claim to have reviewed the change, but I've tested it in my
setup and I like it quite a lot, as it simplifies the clock setup.

Tested-by: Lucas Stach <l.stach@pengutronix.de>
Acked-by: Lucas Stach <l.stach@pengutronix.de>

Regards,
Lucas

> Signed-off-by: Abel Vesa <abel.vesa@nxp.com>
> ---
>  drivers/clk/imx/clk-imx8mq.c   |  58 ++---
>  drivers/clk/imx/clk-sccg-pll.c | 514 ++++++++++++++++++++++++++++++++---------
>  drivers/clk/imx/clk.h          |   9 +-
>  3 files changed, 423 insertions(+), 158 deletions(-)
> 
> diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c
> index 398ab0b..7326dce 100644
> --- a/drivers/clk/imx/clk-imx8mq.c
> +++ b/drivers/clk/imx/clk-imx8mq.c
> @@ -34,15 +34,10 @@ static const char * const audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_
>  static const char * const audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", };
>  static const char * const video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", };
>  
> -static const char * const sys1_pll1_out_sels[] = {"sys1_pll1", "sys1_pll1_ref_sel", };
> -static const char * const sys2_pll1_out_sels[] = {"sys2_pll1", "sys1_pll1_ref_sel", };
> -static const char * const sys3_pll1_out_sels[] = {"sys3_pll1", "sys3_pll1_ref_sel", };
> -static const char * const dram_pll1_out_sels[] = {"dram_pll1", "dram_pll1_ref_sel", };
> -
> -static const char * const sys1_pll2_out_sels[] = {"sys1_pll2_div", "sys1_pll1_ref_sel", };
> -static const char * const sys2_pll2_out_sels[] = {"sys2_pll2_div", "sys2_pll1_ref_sel", };
> -static const char * const sys3_pll2_out_sels[] = {"sys3_pll2_div", "sys2_pll1_ref_sel", };
> -static const char * const dram_pll2_out_sels[] = {"dram_pll2_div", "dram_pll1_ref_sel", };
> +static const char * const sys1_pll_out_sels[] = {"sys1_pll1_ref_sel", };
> +static const char * const sys2_pll_out_sels[] = {"sys1_pll1_ref_sel", "sys2_pll1_ref_sel", };
> +static const char * const sys3_pll_out_sels[] = {"sys3_pll1_ref_sel", "sys2_pll1_ref_sel", };
> +static const char * const dram_pll_out_sels[] = {"dram_pll1_ref_sel", };
>  
>  /* CCM ROOT */
>  static const char * const imx8mq_a53_sels[] = {"osc_25m", "arm_pll_out", "sys2_pll_500m", "sys2_pll_1000m",
> @@ -308,10 +303,6 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
> >  	clks[IMX8MQ_AUDIO_PLL1_REF_DIV] = imx_clk_divider("audio_pll1_ref_div", "audio_pll1_ref_sel", base + 0x0, 5, 6);
> >  	clks[IMX8MQ_AUDIO_PLL2_REF_DIV] = imx_clk_divider("audio_pll2_ref_div", "audio_pll2_ref_sel", base + 0x8, 5, 6);
> >  	clks[IMX8MQ_VIDEO_PLL1_REF_DIV] = imx_clk_divider("video_pll1_ref_div", "video_pll1_ref_sel", base + 0x10, 5, 6);
> > > -	clks[IMX8MQ_SYS1_PLL1_REF_DIV]	= imx_clk_divider("sys1_pll1_ref_div", "sys1_pll1_ref_sel", base + 0x38, 25, 3);
> > > -	clks[IMX8MQ_SYS2_PLL1_REF_DIV]	= imx_clk_divider("sys2_pll1_ref_div", "sys2_pll1_ref_sel", base + 0x44, 25, 3);
> > > -	clks[IMX8MQ_SYS3_PLL1_REF_DIV]	= imx_clk_divider("sys3_pll1_ref_div", "sys3_pll1_ref_sel", base + 0x50, 25, 3);
> > > -	clks[IMX8MQ_DRAM_PLL1_REF_DIV]	= imx_clk_divider("dram_pll1_ref_div", "dram_pll1_ref_sel", base + 0x68, 25, 3);
>  
> >  	clks[IMX8MQ_ARM_PLL] = imx_clk_frac_pll("arm_pll", "arm_pll_ref_div", base + 0x28);
> >  	clks[IMX8MQ_GPU_PLL] = imx_clk_frac_pll("gpu_pll", "gpu_pll_ref_div", base + 0x18);
> @@ -319,25 +310,6 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
> >  	clks[IMX8MQ_AUDIO_PLL1] = imx_clk_frac_pll("audio_pll1", "audio_pll1_ref_div", base + 0x0);
> >  	clks[IMX8MQ_AUDIO_PLL2] = imx_clk_frac_pll("audio_pll2", "audio_pll2_ref_div", base + 0x8);
> >  	clks[IMX8MQ_VIDEO_PLL1] = imx_clk_frac_pll("video_pll1", "video_pll1_ref_div", base + 0x10);
> > -	clks[IMX8MQ_SYS1_PLL1] = imx_clk_sccg_pll("sys1_pll1", "sys1_pll1_ref_div", base + 0x30, SCCG_PLL1);
> > -	clks[IMX8MQ_SYS2_PLL1] = imx_clk_sccg_pll("sys2_pll1", "sys2_pll1_ref_div", base + 0x3c, SCCG_PLL1);
> > -	clks[IMX8MQ_SYS3_PLL1] = imx_clk_sccg_pll("sys3_pll1", "sys3_pll1_ref_div", base + 0x48, SCCG_PLL1);
> > -	clks[IMX8MQ_DRAM_PLL1] = imx_clk_sccg_pll("dram_pll1", "dram_pll1_ref_div", base + 0x60, SCCG_PLL1);
> -
> > -	clks[IMX8MQ_SYS1_PLL2] = imx_clk_sccg_pll("sys1_pll2", "sys1_pll1_out_div", base + 0x30, SCCG_PLL2);
> > -	clks[IMX8MQ_SYS2_PLL2] = imx_clk_sccg_pll("sys2_pll2", "sys2_pll1_out_div", base + 0x3c, SCCG_PLL2);
> > -	clks[IMX8MQ_SYS3_PLL2] = imx_clk_sccg_pll("sys3_pll2", "sys3_pll1_out_div", base + 0x48, SCCG_PLL2);
> > -	clks[IMX8MQ_DRAM_PLL2] = imx_clk_sccg_pll("dram_pll2", "dram_pll1_out_div", base + 0x60, SCCG_PLL2);
> -
> > -	/* PLL divs */
> > -	clks[IMX8MQ_SYS1_PLL1_OUT_DIV] = imx_clk_divider("sys1_pll1_out_div", "sys1_pll1_out", base + 0x38, 19, 6);
> > -	clks[IMX8MQ_SYS2_PLL1_OUT_DIV] = imx_clk_divider("sys2_pll1_out_div", "sys2_pll1_out", base + 0x44, 19, 6);
> > -	clks[IMX8MQ_SYS3_PLL1_OUT_DIV] = imx_clk_divider("sys3_pll1_out_div", "sys3_pll1_out", base + 0x50, 19, 6);
> > -	clks[IMX8MQ_DRAM_PLL1_OUT_DIV] = imx_clk_divider("dram_pll1_out_div", "dram_pll1_out", base + 0x68, 19, 6);
> > -	clks[IMX8MQ_SYS1_PLL2_DIV] = imx_clk_divider("sys1_pll2_div", "sys1_pll2", base + 0x38, 1, 6);
> > -	clks[IMX8MQ_SYS2_PLL2_DIV] = imx_clk_divider("sys2_pll2_div", "sys2_pll2", base + 0x44, 1, 6);
> > -	clks[IMX8MQ_SYS3_PLL2_DIV] = imx_clk_divider("sys3_pll2_div", "sys3_pll2", base + 0x50, 1, 6);
> > -	clks[IMX8MQ_DRAM_PLL2_DIV] = imx_clk_divider("dram_pll2_div", "dram_pll2", base + 0x68, 1, 6);
>  
> >  	/* PLL bypass out */
> >  	clks[IMX8MQ_ARM_PLL_BYPASS] = imx_clk_mux("arm_pll_bypass", base + 0x28, 14, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels));
> @@ -347,14 +319,12 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
> >  	clks[IMX8MQ_AUDIO_PLL2_BYPASS] = imx_clk_mux("audio_pll2_bypass", base + 0x8, 14, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels));
> >  	clks[IMX8MQ_VIDEO_PLL1_BYPASS] = imx_clk_mux("video_pll1_bypass", base + 0x10, 14, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels));
>  
> > -	clks[IMX8MQ_SYS1_PLL1_OUT] = imx_clk_mux("sys1_pll1_out", base + 0x30, 5, 1, sys1_pll1_out_sels, ARRAY_SIZE(sys1_pll1_out_sels));
> > -	clks[IMX8MQ_SYS2_PLL1_OUT] = imx_clk_mux("sys2_pll1_out", base + 0x3c, 5, 1, sys2_pll1_out_sels, ARRAY_SIZE(sys2_pll1_out_sels));
> > -	clks[IMX8MQ_SYS3_PLL1_OUT] = imx_clk_mux("sys3_pll1_out", base + 0x48, 5, 1, sys3_pll1_out_sels, ARRAY_SIZE(sys3_pll1_out_sels));
> > -	clks[IMX8MQ_DRAM_PLL1_OUT] = imx_clk_mux("dram_pll1_out", base + 0x60, 5, 1, dram_pll1_out_sels, ARRAY_SIZE(dram_pll1_out_sels));
> > -	clks[IMX8MQ_SYS1_PLL2_OUT] = imx_clk_mux("sys1_pll2_out", base + 0x30, 4, 1, sys1_pll2_out_sels, ARRAY_SIZE(sys1_pll2_out_sels));
> > -	clks[IMX8MQ_SYS2_PLL2_OUT] = imx_clk_mux("sys2_pll2_out", base + 0x3c, 4, 1, sys2_pll2_out_sels, ARRAY_SIZE(sys2_pll2_out_sels));
> > -	clks[IMX8MQ_SYS3_PLL2_OUT] = imx_clk_mux("sys3_pll2_out", base + 0x48, 4, 1, sys3_pll2_out_sels, ARRAY_SIZE(sys3_pll2_out_sels));
> > -	clks[IMX8MQ_DRAM_PLL2_OUT] = imx_clk_mux("dram_pll2_out", base + 0x60, 4, 1, dram_pll2_out_sels, ARRAY_SIZE(dram_pll2_out_sels));
> > +	/* unbypass all the plls */
> > +	clk_set_parent(clks[IMX8MQ_GPU_PLL_BYPASS], clks[IMX8MQ_GPU_PLL]);
> > +	clk_set_parent(clks[IMX8MQ_VPU_PLL_BYPASS], clks[IMX8MQ_VPU_PLL]);
> > +	clk_set_parent(clks[IMX8MQ_AUDIO_PLL1_BYPASS], clks[IMX8MQ_AUDIO_PLL1]);
> > +	clk_set_parent(clks[IMX8MQ_AUDIO_PLL2_BYPASS], clks[IMX8MQ_AUDIO_PLL2]);
> > +	clk_set_parent(clks[IMX8MQ_VIDEO_PLL1_BYPASS], clks[IMX8MQ_VIDEO_PLL1]);
>  
> >  	/* PLL OUT GATE */
> >  	clks[IMX8MQ_ARM_PLL_OUT] = imx_clk_gate("arm_pll_out", "arm_pll_bypass", base + 0x28, 21);
> @@ -363,11 +333,11 @@ static int imx8mq_clocks_probe(struct platform_device *pdev)
> >  	clks[IMX8MQ_AUDIO_PLL1_OUT] = imx_clk_gate("audio_pll1_out", "audio_pll1_bypass", base + 0x0, 21);
> >  	clks[IMX8MQ_AUDIO_PLL2_OUT] = imx_clk_gate("audio_pll2_out", "audio_pll2_bypass", base + 0x8, 21);
> >  	clks[IMX8MQ_VIDEO_PLL1_OUT] = imx_clk_gate("video_pll1_out", "video_pll1_bypass", base + 0x10, 21);
> > -	clks[IMX8MQ_SYS1_PLL_OUT] = imx_clk_gate("sys1_pll_out", "sys1_pll2_out", base + 0x30, 9);
> > -	clks[IMX8MQ_SYS2_PLL_OUT] = imx_clk_gate("sys2_pll_out", "sys2_pll2_out", base + 0x3c, 9);
> > -	clks[IMX8MQ_SYS3_PLL_OUT] = imx_clk_gate("sys3_pll_out", "sys3_pll2_out", base + 0x48, 9);
> > -	clks[IMX8MQ_DRAM_PLL_OUT] = imx_clk_gate("dram_pll_out", "dram_pll2_out", base + 0x60, 9);
>  
> > +	clks[IMX8MQ_SYS1_PLL_OUT] = imx_clk_sccg_pll("sys1_pll_out", sys1_pll_out_sels, ARRAY_SIZE(sys1_pll_out_sels), 0, 0, 0, base + 0x30, CLK_IS_CRITICAL);
> > +	clks[IMX8MQ_SYS2_PLL_OUT] = imx_clk_sccg_pll("sys2_pll_out", sys2_pll_out_sels, ARRAY_SIZE(sys2_pll_out_sels), 0, 0, 1, base + 0x3c, CLK_IS_CRITICAL);
> > +	clks[IMX8MQ_SYS3_PLL_OUT] = imx_clk_sccg_pll("sys3_pll_out", sys3_pll_out_sels, ARRAY_SIZE(sys3_pll_out_sels), 0, 0, 1, base + 0x48, CLK_IS_CRITICAL);
> > +	clks[IMX8MQ_DRAM_PLL_OUT] = imx_clk_sccg_pll("dram_pll_out", dram_pll_out_sels, ARRAY_SIZE(dram_pll_out_sels), 0, 0, 0, base + 0x60, CLK_IS_CRITICAL);
> >  	/* SYS PLL fixed output */
> >  	clks[IMX8MQ_SYS1_PLL_40M] = imx_clk_fixed_factor("sys1_pll_40m", "sys1_pll_out", 1, 20);
> >  	clks[IMX8MQ_SYS1_PLL_80M] = imx_clk_fixed_factor("sys1_pll_80m", "sys1_pll_out", 1, 10);
> diff --git a/drivers/clk/imx/clk-sccg-pll.c b/drivers/clk/imx/clk-sccg-pll.c
> index ee7752b..f480af3 100644
> --- a/drivers/clk/imx/clk-sccg-pll.c
> +++ b/drivers/clk/imx/clk-sccg-pll.c
> @@ -25,87 +25,292 @@
> >  #define PLL_DIVF2_MASK		GENMASK(12, 7)
> >  #define PLL_DIVR1_MASK		GENMASK(27, 25)
> >  #define PLL_DIVR2_MASK		GENMASK(24, 19)
> +#define PLL_DIVQ_MASK           GENMASK(6, 1)
> >  #define PLL_REF_MASK		GENMASK(2, 0)
>  
> >  #define PLL_LOCK_MASK		BIT(31)
> >  #define PLL_PD_MASK		BIT(7)
>  
> > -#define OSC_25M			25000000
> > -#define OSC_27M			27000000
> +/* These are the specification limits for the SSCG PLL */
> > +#define PLL_REF_MIN_FREQ		25000000
> > +#define PLL_REF_MAX_FREQ		235000000
>  
> > -#define PLL_SCCG_LOCK_TIMEOUT	70
> > +#define PLL_STAGE1_MIN_FREQ		1600000000
> > +#define PLL_STAGE1_MAX_FREQ		2400000000
> +
> > +#define PLL_STAGE1_REF_MIN_FREQ		25000000
> > +#define PLL_STAGE1_REF_MAX_FREQ		54000000
> +
> > +#define PLL_STAGE2_MIN_FREQ		1200000000
> > +#define PLL_STAGE2_MAX_FREQ		2400000000
> +
> > +#define PLL_STAGE2_REF_MIN_FREQ		54000000
> > +#define PLL_STAGE2_REF_MAX_FREQ		75000000
> +
> > +#define PLL_OUT_MIN_FREQ		20000000
> > +#define PLL_OUT_MAX_FREQ		1200000000
> +
> > +#define PLL_DIVR1_MAX			7
> > +#define PLL_DIVR2_MAX			63
> > +#define PLL_DIVF1_MAX			63
> > +#define PLL_DIVF2_MAX			63
> > +#define PLL_DIVQ_MAX			63
> +
> > +#define PLL_BYPASS_NONE			0x0
> > +#define PLL_BYPASS1			0x2
> > +#define PLL_BYPASS2			0x1
> +
> +#define SSCG_PLL_BYPASS1_MASK           BIT(5)
> +#define SSCG_PLL_BYPASS2_MASK           BIT(4)
> > +#define SSCG_PLL_BYPASS_MASK		GENMASK(5, 4)
> +
> > +#define PLL_SCCG_LOCK_TIMEOUT		70
> +
> +struct clk_sccg_pll_setup {
> > +	int divr1, divf1;
> > +	int divr2, divf2;
> > +	int divq;
> > +	int bypass;
> +
> > +	unsigned long vco1;
> > +	unsigned long vco2;
> > +	unsigned long fout;
> > +	unsigned long ref;
> > +	unsigned long ref_div1;
> > +	unsigned long ref_div2;
> > +	unsigned long fout_request;
> > +	int fout_error;
> +};
>  
>  struct clk_sccg_pll {
> > >  	struct clk_hw	hw;
> > > -	void __iomem	*base;
> > +	const struct clk_ops  ops;
> +
> > +	void __iomem *base;
> +
> > +	struct clk_sccg_pll_setup setup;
> +
> > +	u8 parent;
> > +	u8 bypass1;
> > +	u8 bypass2;
>  };
>  
>  #define to_clk_sccg_pll(_hw) container_of(_hw, struct clk_sccg_pll, hw)
>  
> -static int clk_pll_wait_lock(struct clk_sccg_pll *pll)
> +static int clk_sccg_pll_wait_lock(struct clk_sccg_pll *pll)
>  {
> >  	u32 val;
>  
> > -	return readl_poll_timeout(pll->base, val, val & PLL_LOCK_MASK, 0,
> > -					PLL_SCCG_LOCK_TIMEOUT);
> > +	val = readl_relaxed(pll->base + PLL_CFG0);
> +
> > +	/* don't wait for lock if all plls are bypassed */
> > +	if (!(val & SSCG_PLL_BYPASS2_MASK))
> > +		return readl_poll_timeout(pll->base, val, val & PLL_LOCK_MASK,
> > +						0, PLL_SCCG_LOCK_TIMEOUT);
> +
> > +	return 0;
>  }
>  
> -static int clk_pll1_is_prepared(struct clk_hw *hw)
> +static int clk_sccg_pll2_check_match(struct clk_sccg_pll_setup *setup,
> > +					struct clk_sccg_pll_setup *temp_setup)
>  {
> > -	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
> > -	u32 val;
> > +	int new_diff = temp_setup->fout - temp_setup->fout_request;
> > +	int diff = temp_setup->fout_error;
>  
> > -	val = readl_relaxed(pll->base + PLL_CFG0);
> > -	return (val & PLL_PD_MASK) ? 0 : 1;
> > +	if (abs(diff) > abs(new_diff)) {
> > +		temp_setup->fout_error = new_diff;
> > +		memcpy(setup, temp_setup, sizeof(struct clk_sccg_pll_setup));
> +
> > +		if (temp_setup->fout_request == temp_setup->fout)
> > +			return 0;
> > +	}
> > +	return -1;
>  }
>  
> -static unsigned long clk_pll1_recalc_rate(struct clk_hw *hw,
> > -					 unsigned long parent_rate)
> +static int clk_sccg_divq_lookup(struct clk_sccg_pll_setup *setup,
> > +				struct clk_sccg_pll_setup *temp_setup)
>  {
> > -	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
> > -	u32 val, divf;
> > +	int ret = -EINVAL;
> +
> > +	for (temp_setup->divq = 0; temp_setup->divq <= PLL_DIVQ_MAX;
> > +	     temp_setup->divq++) {
> > +		temp_setup->vco2 = temp_setup->vco1;
> > +		do_div(temp_setup->vco2, temp_setup->divr2 + 1);
> > +		temp_setup->vco2 *= 2;
> > +		temp_setup->vco2 *= temp_setup->divf2 + 1;
> > +		if (temp_setup->vco2 >= PLL_STAGE2_MIN_FREQ &&
> > +				temp_setup->vco2 <= PLL_STAGE2_MAX_FREQ) {
> > +			temp_setup->fout = temp_setup->vco2;
> > +			do_div(temp_setup->fout, 2 * (temp_setup->divq + 1));
> +
> > +			ret = clk_sccg_pll2_check_match(setup, temp_setup);
> > +			if (!ret) {
> > +				temp_setup->bypass = PLL_BYPASS1;
> > +				return ret;
> > +			}
> > +		}
> > +	}
>  
> > -	val = readl_relaxed(pll->base + PLL_CFG2);
> > -	divf = FIELD_GET(PLL_DIVF1_MASK, val);
> > +	return ret;
> +}
> +
> +static int clk_sccg_divf2_lookup(struct clk_sccg_pll_setup *setup,
> > +					struct clk_sccg_pll_setup *temp_setup)
> +{
> > +	int ret = -EINVAL;
> +
> > +	for (temp_setup->divf2 = 0; temp_setup->divf2 <= PLL_DIVF2_MAX;
> > +	     temp_setup->divf2++) {
> > +		ret = clk_sccg_divq_lookup(setup, temp_setup);
> > +		if (!ret)
> > +			return ret;
> > +	}
>  
> > -	return parent_rate * 2 * (divf + 1);
> > +	return ret;
>  }
>  
> -static long clk_pll1_round_rate(struct clk_hw *hw, unsigned long rate,
> > -			       unsigned long *prate)
> +static int clk_sccg_divr2_lookup(struct clk_sccg_pll_setup *setup,
> > +				struct clk_sccg_pll_setup *temp_setup)
>  {
> > -	unsigned long parent_rate = *prate;
> > -	u32 div;
> > +	int ret = -EINVAL;
> +
> > +	for (temp_setup->divr2 = 0; temp_setup->divr2 <= PLL_DIVR2_MAX;
> > +	     temp_setup->divr2++) {
> > +		temp_setup->ref_div2 = temp_setup->vco1;
> > +		do_div(temp_setup->ref_div2, temp_setup->divr2 + 1);
> > +		if (temp_setup->ref_div2 >= PLL_STAGE2_REF_MIN_FREQ &&
> > +		    temp_setup->ref_div2 <= PLL_STAGE2_REF_MAX_FREQ) {
> > +			ret = clk_sccg_divf2_lookup(setup, temp_setup);
> > +			if (!ret)
> > +				return ret;
> > +		}
> > +	}
> +
> > +	return ret;
> +}
> +
> +static int clk_sccg_pll2_find_setup(struct clk_sccg_pll_setup *setup,
> > +					struct clk_sccg_pll_setup *temp_setup,
> > +					long ref)
> +{
> +
> > +	int ret = -EINVAL;
>  
> > -	if (!parent_rate)
> > -		return 0;
> > +	if (ref < PLL_STAGE1_MIN_FREQ || ref > PLL_STAGE1_MAX_FREQ)
> > +		return ret;
>  
> > -	div = rate / (parent_rate * 2);
> > +	temp_setup->vco1 = ref;
>  
> > -	return parent_rate * div * 2;
> > +	ret = clk_sccg_divr2_lookup(setup, temp_setup);
> > +	return ret;
>  }
>  
> -static int clk_pll1_set_rate(struct clk_hw *hw, unsigned long rate,
> > -			    unsigned long parent_rate)
> +static int clk_sccg_divf1_lookup(struct clk_sccg_pll_setup *setup,
> > +				struct clk_sccg_pll_setup *temp_setup)
>  {
> > -	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
> > -	u32 val;
> > -	u32 divf;
> > +	int ret = -EINVAL;
>  
> > -	if (!parent_rate)
> > -		return -EINVAL;
> > +	for (temp_setup->divf1 = 0; temp_setup->divf1 <= PLL_DIVF1_MAX;
> > +	     temp_setup->divf1++) {
> > +		long vco1 = temp_setup->ref;
>  
> > -	divf = rate / (parent_rate * 2);
> > +		do_div(vco1, temp_setup->divr1 + 1);
> > +		vco1 *= 2;
> > +		vco1 *= temp_setup->divf1 + 1;
>  
> > -	val = readl_relaxed(pll->base + PLL_CFG2);
> > -	val &= ~PLL_DIVF1_MASK;
> > -	val |= FIELD_PREP(PLL_DIVF1_MASK, divf - 1);
> > -	writel_relaxed(val, pll->base + PLL_CFG2);
> > +		ret = clk_sccg_pll2_find_setup(setup, temp_setup, vco1);
> > +		if (!ret) {
> > +			temp_setup->bypass = PLL_BYPASS_NONE;
> > +			return ret;
> > +		}
> > +	}
> +
> > +	return ret;
> +}
> +
> +static int clk_sccg_divr1_lookup(struct clk_sccg_pll_setup *setup,
> > +				struct clk_sccg_pll_setup *temp_setup)
> +{
> > +	int ret = -EINVAL;
> +
> > +	for (temp_setup->divr1 = 0; temp_setup->divr1 <= PLL_DIVR1_MAX;
> > +	     temp_setup->divr1++) {
> > +		temp_setup->ref_div1 = temp_setup->ref;
> > +		do_div(temp_setup->ref_div1, temp_setup->divr1 + 1);
> > +		if (temp_setup->ref_div1 >= PLL_STAGE1_REF_MIN_FREQ &&
> > +		    temp_setup->ref_div1 <= PLL_STAGE1_REF_MAX_FREQ) {
> > +			ret = clk_sccg_divf1_lookup(setup, temp_setup);
> > +			if (!ret)
> > +				return ret;
> > +		}
> > +	}
> +
> > +	return ret;
> +}
> +
> +static int clk_sccg_pll1_find_setup(struct clk_sccg_pll_setup *setup,
> > +					struct clk_sccg_pll_setup *temp_setup,
> > +					long ref)
> +{
> +
> > +	int ret = -EINVAL;
> +
> > +	if (ref < PLL_REF_MIN_FREQ || ref > PLL_REF_MAX_FREQ)
> > +		return ret;
> +
> > +	temp_setup->ref = ref;
> +
> > +	ret = clk_sccg_divr1_lookup(setup, temp_setup);
> +
> > +	return ret;
> +}
> +
> +static int clk_sccg_pll_find_setup(struct clk_sccg_pll_setup *setup,
> > +					unsigned long prate,
> > +					unsigned long rate, int try_bypass)
> +{
> > +	struct clk_sccg_pll_setup temp_setup;
> > +	int ret = -EINVAL;
> +
> > +	memset(&temp_setup, 0, sizeof(struct clk_sccg_pll_setup));
> > +	memset(setup, 0, sizeof(struct clk_sccg_pll_setup));
> +
> > +	temp_setup.fout_error = PLL_OUT_MAX_FREQ;
> > +	temp_setup.fout_request = rate;
>  
> > -	return clk_pll_wait_lock(pll);
> > +	switch (try_bypass) {
> +
> > +	case PLL_BYPASS2:
> > +		if (prate == rate) {
> > +			setup->bypass = PLL_BYPASS2;
> > +			setup->fout = rate;
> > +			ret = 0;
> > +		}
> > +		break;
> +
> > +	case PLL_BYPASS1:
> > +		ret = clk_sccg_pll2_find_setup(setup, &temp_setup, prate);
> > +		break;
> +
> > +	case PLL_BYPASS_NONE:
> > +		ret = clk_sccg_pll1_find_setup(setup, &temp_setup, prate);
> > +		break;
> > +	}
> +
> > +	return ret;
> +}
> +
> +
> +static int clk_sccg_pll_is_prepared(struct clk_hw *hw)
> +{
> > +	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
> +
> > +	u32 val = readl_relaxed(pll->base + PLL_CFG0);
> +
> > +	return (val & PLL_PD_MASK) ? 0 : 1;
>  }
>  
> -static int clk_pll1_prepare(struct clk_hw *hw)
> +static int clk_sccg_pll_prepare(struct clk_hw *hw)
>  {
> >  	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
> >  	u32 val;
> @@ -114,10 +319,10 @@ static int clk_pll1_prepare(struct clk_hw *hw)
> >  	val &= ~PLL_PD_MASK;
> >  	writel_relaxed(val, pll->base + PLL_CFG0);
>  
> > -	return clk_pll_wait_lock(pll);
> > +	return clk_sccg_pll_wait_lock(pll);
>  }
>  
> -static void clk_pll1_unprepare(struct clk_hw *hw)
> +static void clk_sccg_pll_unprepare(struct clk_hw *hw)
>  {
> >  	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
> >  	u32 val;
> @@ -125,121 +330,208 @@ static void clk_pll1_unprepare(struct clk_hw *hw)
> >  	val = readl_relaxed(pll->base + PLL_CFG0);
> >  	val |= PLL_PD_MASK;
> >  	writel_relaxed(val, pll->base + PLL_CFG0);
> -
>  }
>  
> -static unsigned long clk_pll2_recalc_rate(struct clk_hw *hw,
> +static unsigned long clk_sccg_pll_recalc_rate(struct clk_hw *hw,
> >  					 unsigned long parent_rate)
>  {
> >  	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
> > -	u32 val, ref, divr1, divf1, divr2, divf2;
> > +	u32 val, divr1, divf1, divr2, divf2, divq;
> >  	u64 temp64;
>  
> > -	val = readl_relaxed(pll->base + PLL_CFG0);
> > -	switch (FIELD_GET(PLL_REF_MASK, val)) {
> > -	case 0:
> > -		ref = OSC_25M;
> > -		break;
> > -	case 1:
> > -		ref = OSC_27M;
> > -		break;
> > -	default:
> > -		ref = OSC_25M;
> > -		break;
> > -	}
> -
> >  	val = readl_relaxed(pll->base + PLL_CFG2);
> >  	divr1 = FIELD_GET(PLL_DIVR1_MASK, val);
> >  	divr2 = FIELD_GET(PLL_DIVR2_MASK, val);
> >  	divf1 = FIELD_GET(PLL_DIVF1_MASK, val);
> >  	divf2 = FIELD_GET(PLL_DIVF2_MASK, val);
> > +	divq = FIELD_GET(PLL_DIVQ_MASK, val);
> +
> > +	temp64 = parent_rate;
> +
> > +	val = clk_readl(pll->base + PLL_CFG0);
> > +	if (val & SSCG_PLL_BYPASS2_MASK) {
> > +		temp64 = parent_rate;
> > +	} else if (val & SSCG_PLL_BYPASS1_MASK) {
> > +		temp64 *= divf2;
> > +		do_div(temp64, (divr2 + 1) * (divq + 1));
> > +	} else {
> > +		temp64 *= 2;
> > +		temp64 *= (divf1 + 1) * (divf2 + 1);
> > +		do_div(temp64, (divr1 + 1) * (divr2 + 1) * (divq + 1));
> > +	}
> +
> > +	return (unsigned long)temp64;
> +}
>  
> > -	temp64 = ref * 2;
> > -	temp64 *= (divf1 + 1) * (divf2 + 1);
> +static int clk_sccg_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> > +			    unsigned long parent_rate)
> +{
> > +	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
> > +	struct clk_sccg_pll_setup *setup = &pll->setup;
> > +	u32 val;
>  
> > -	do_div(temp64, (divr1 + 1) * (divr2 + 1));
> > +	/* set bypass here too since the parent might be the same */
> > +	val = clk_readl(pll->base + PLL_CFG0);
> > +	val &= ~SSCG_PLL_BYPASS_MASK;
> > +	val |= FIELD_PREP(SSCG_PLL_BYPASS_MASK, setup->bypass);
> > +	clk_writel(val, pll->base + PLL_CFG0);
>  
> > -	return temp64;
> > +	val = readl_relaxed(pll->base + PLL_CFG2);
> > +	val &= ~(PLL_DIVF1_MASK | PLL_DIVF2_MASK);
> > +	val &= ~(PLL_DIVR1_MASK | PLL_DIVR2_MASK | PLL_DIVQ_MASK);
> > +	val |= FIELD_PREP(PLL_DIVF1_MASK, setup->divf1);
> > +	val |= FIELD_PREP(PLL_DIVF2_MASK, setup->divf2);
> > +	val |= FIELD_PREP(PLL_DIVR1_MASK, setup->divr1);
> > +	val |= FIELD_PREP(PLL_DIVR2_MASK, setup->divr2);
> > +	val |= FIELD_PREP(PLL_DIVQ_MASK, setup->divq);
> > +	writel_relaxed(val, pll->base + PLL_CFG2);
> +
> > +	return clk_sccg_pll_wait_lock(pll);
>  }
>  
> -static long clk_pll2_round_rate(struct clk_hw *hw, unsigned long rate,
> > -			       unsigned long *prate)
> +static u8 clk_sccg_pll_get_parent(struct clk_hw *hw)
>  {
> > -	u32 div;
> > -	unsigned long parent_rate = *prate;
> > +	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
> > +	u32 val;
> > +	u8 ret = pll->parent;
> +
> > +	val = clk_readl(pll->base + PLL_CFG0);
> > +	if (val & SSCG_PLL_BYPASS2_MASK)
> > +		ret = pll->bypass2;
> > +	else if (val & SSCG_PLL_BYPASS1_MASK)
> > +		ret = pll->bypass1;
> > +	return ret;
> +}
>  
> > -	if (!parent_rate)
> > -		return 0;
> +static int clk_sccg_pll_set_parent(struct clk_hw *hw, u8 index)
> +{
> > +	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
> > +	u32 val;
>  
> > -	div = rate / parent_rate;
> > +	val = clk_readl(pll->base + PLL_CFG0);
> > +	val &= ~SSCG_PLL_BYPASS_MASK;
> > +	val |= FIELD_PREP(SSCG_PLL_BYPASS_MASK, pll->setup.bypass);
> > +	clk_writel(val, pll->base + PLL_CFG0);
>  
> > -	return parent_rate * div;
> > +	return clk_sccg_pll_wait_lock(pll);
>  }
>  
> -static int clk_pll2_set_rate(struct clk_hw *hw, unsigned long rate,
> > -			    unsigned long parent_rate)
> +static int __clk_sccg_pll_determine_rate(struct clk_hw *hw,
> > +					struct clk_rate_request *req,
> > +					unsigned long min,
> > +					unsigned long max,
> > +					unsigned long rate,
> > +					int bypass)
>  {
> > -	u32 val;
> > -	u32 divf;
> >  	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
> > +	struct clk_sccg_pll_setup *setup = &pll->setup;
> > +	struct clk_hw *parent_hw = NULL;
> > +	int bypass_parent_index;
> > +	int ret = -EINVAL;
>  
> > -	if (!parent_rate)
> > -		return -EINVAL;
> > +	req->max_rate = max;
> > +	req->min_rate = min;
>  
> > -	divf = rate / parent_rate;
> > +	switch (bypass) {
> > +	case PLL_BYPASS2:
> > +		bypass_parent_index = pll->bypass2;
> > +		break;
> > +	case PLL_BYPASS1:
> > +		bypass_parent_index = pll->bypass1;
> > +		break;
> > +	default:
> > +		bypass_parent_index = pll->parent;
> > +		break;
> > +	}
>  
> > -	val = readl_relaxed(pll->base + PLL_CFG2);
> > -	val &= ~PLL_DIVF2_MASK;
> > -	val |= FIELD_PREP(PLL_DIVF2_MASK, divf - 1);
> > -	writel_relaxed(val, pll->base + PLL_CFG2);
> > +	parent_hw = clk_hw_get_parent_by_index(hw, bypass_parent_index);
> > +	ret = __clk_determine_rate(parent_hw, req);
> > +	if (!ret) {
> > +		ret = clk_sccg_pll_find_setup(setup, req->rate,
> > +						rate, bypass);
> > +	}
>  
> > -	return clk_pll_wait_lock(pll);
> > +	req->best_parent_hw = parent_hw;
> > +	req->best_parent_rate = req->rate;
> > +	req->rate = setup->fout;
> +
> > +	return ret;
>  }
>  
> -static const struct clk_ops clk_sccg_pll1_ops = {
> > > -	.is_prepared	= clk_pll1_is_prepared,
> > > -	.recalc_rate	= clk_pll1_recalc_rate,
> > > -	.round_rate	= clk_pll1_round_rate,
> > > -	.set_rate	= clk_pll1_set_rate,
> -};
> +static int clk_sccg_pll_determine_rate(struct clk_hw *hw,
> > +				       struct clk_rate_request *req)
> +{
> > +	struct clk_sccg_pll *pll = to_clk_sccg_pll(hw);
> > +	struct clk_sccg_pll_setup *setup = &pll->setup;
> > +	unsigned long rate = req->rate;
> > +	unsigned long min = req->min_rate;
> > +	unsigned long max = req->max_rate;
> > +	int ret = -EINVAL;
> +
> > +	if (rate < PLL_OUT_MIN_FREQ || rate > PLL_OUT_MAX_FREQ)
> > +		return ret;
> +
> > +	ret = __clk_sccg_pll_determine_rate(hw, req, req->rate, req->rate,
> > +						rate, PLL_BYPASS2);
> > +	if (!ret)
> > +		return ret;
> +
> > +	ret = __clk_sccg_pll_determine_rate(hw, req, PLL_STAGE1_REF_MIN_FREQ,
> > +						PLL_STAGE1_REF_MAX_FREQ, rate,
> > +						PLL_BYPASS1);
> > +	if (!ret)
> > +		return ret;
> +
> > +	ret = __clk_sccg_pll_determine_rate(hw, req, PLL_REF_MIN_FREQ,
> > +						PLL_REF_MAX_FREQ, rate,
> > +						PLL_BYPASS_NONE);
> > +	if (!ret)
> > +		return ret;
> +
> > +	if (setup->fout >= min && setup->fout <= max)
> > +		ret = 0;
> +
> > +	return ret;
> +}
>  
> -static const struct clk_ops clk_sccg_pll2_ops = {
> > > -	.prepare	= clk_pll1_prepare,
> > > -	.unprepare	= clk_pll1_unprepare,
> > > -	.recalc_rate	= clk_pll2_recalc_rate,
> > > -	.round_rate	= clk_pll2_round_rate,
> > > -	.set_rate	= clk_pll2_set_rate,
> +static const struct clk_ops clk_sccg_pll_ops = {
> > > +	.prepare	= clk_sccg_pll_prepare,
> > > +	.unprepare	= clk_sccg_pll_unprepare,
> > > +	.is_prepared	= clk_sccg_pll_is_prepared,
> > > +	.recalc_rate	= clk_sccg_pll_recalc_rate,
> > > +	.set_rate	= clk_sccg_pll_set_rate,
> > > +	.set_parent	= clk_sccg_pll_set_parent,
> > > +	.get_parent	= clk_sccg_pll_get_parent,
> > > +	.determine_rate	= clk_sccg_pll_determine_rate,
>  };
>  
>  struct clk *imx_clk_sccg_pll(const char *name,
> > -				const char *parent_name,
> > +				const char * const *parent_names,
> > +				u8 num_parents,
> > +				u8 parent, u8 bypass1, u8 bypass2,
> >  				void __iomem *base,
> > -				enum imx_sccg_pll_type pll_type)
> > +				unsigned long flags)
>  {
> >  	struct clk_sccg_pll *pll;
> >  	struct clk_init_data init;
> >  	struct clk_hw *hw;
> >  	int ret;
>  
> > -	switch (pll_type) {
> > -	case SCCG_PLL1:
> > -		init.ops = &clk_sccg_pll1_ops;
> > -		break;
> > -	case SCCG_PLL2:
> > -		init.ops = &clk_sccg_pll2_ops;
> > -		break;
> > -	default:
> > -		return ERR_PTR(-EINVAL);
> > -	}
> -
> >  	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
> >  	if (!pll)
> >  		return ERR_PTR(-ENOMEM);
>  
> > +	pll->parent = parent;
> > +	pll->bypass1 = bypass1;
> > +	pll->bypass2 = bypass2;
> +
> > +	pll->base = base;
> >  	init.name = name;
> > -	init.flags = 0;
> > -	init.parent_names = &parent_name;
> > -	init.num_parents = 1;
> > +	init.ops = &clk_sccg_pll_ops;
> +
> > +	init.flags = flags;
> > +	init.parent_names = parent_names;
> > +	init.num_parents = num_parents;
>  
> >  	pll->base = base;
> >  	pll->hw.init = &init;
> diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
> index 2e442d8..dc7ee99 100644
> --- a/drivers/clk/imx/clk.h
> +++ b/drivers/clk/imx/clk.h
> @@ -36,9 +36,12 @@ struct clk *imx_clk_pllv2(const char *name, const char *parent,
>  struct clk *imx_clk_frac_pll(const char *name, const char *parent_name,
> >  			     void __iomem *base);
>  
> -struct clk *imx_clk_sccg_pll(const char *name, const char *parent_name,
> > -			     void __iomem *base,
> > -			     enum imx_sccg_pll_type pll_type);
> +struct clk *imx_clk_sccg_pll(const char *name,
> > +				const char * const *parent_names,
> > +				u8 num_parents,
> > +				u8 parent, u8 bypass1, u8 bypass2,
> > +				void __iomem *base,
> > +				unsigned long flags);
>  
>  enum imx_pllv3_type {
> >  	IMX_PLLV3_GENERIC,

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, back to index

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-29 14:33 [PATCH] clk: imx: Refactor entire sccg pll clk Abel Vesa
2019-02-11 10:54 ` Lucas Stach

Linux-Clk Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-clk/0 linux-clk/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-clk linux-clk/ https://lore.kernel.org/linux-clk \
		linux-clk@vger.kernel.org linux-clk@archiver.kernel.org
	public-inbox-index linux-clk


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-clk


AGPL code for this site: git clone https://public-inbox.org/ public-inbox