All of lore.kernel.org
 help / color / mirror / Atom feed
From: Daniel Mack <daniel@zonque.org>
To: linux-clk@vger.kernel.org, devicetree@vger.kernel.org
Cc: robh+dt@kernel.org, kuninori.morimoto.gx@renesas.com,
	mturquette@baylibre.com, sboyd@kernel.org,
	Daniel Mack <daniel@zonque.org>
Subject: [PATCH v4 6/9] clk: cs2000-cp: add support for dynamic mode
Date: Fri, 27 Aug 2021 13:54:17 +0200	[thread overview]
Message-ID: <20210827115420.3052019-7-daniel@zonque.org> (raw)
In-Reply-To: <20210827115420.3052019-1-daniel@zonque.org>

The CS2000 chip features two input clocks, REF_CLK and CLK_IN.

In static mode, the output clock (CLK_OUT) is directly derived from
REF_CLK, and CLK_IN is ignored. In dynamic mode, CLK_IN is used by the
digital PLL.

In dynamic mode, a low-frequency ratio configuration that uses a higher
multiplier factor.

Until now, only the static mode and high-frequency divider rations of
the hardware was supported by the driver. This patch adds support for
dynamic mode and both ratios:

 * Parse a new OF property 'cirrus,dynamic-mode' to determine the mode
 * In dynamic mode, present CLK_IN as parent clock, else use REF_CLK
 * The low-frequency ratio mode is automatically selected, depending
   on the mode of operation and the given input and output rates

Signed-off-by: Daniel Mack <daniel@zonque.org>
---
 drivers/clk/clk-cs2000-cp.c | 114 ++++++++++++++++++++++++------------
 1 file changed, 77 insertions(+), 37 deletions(-)

diff --git a/drivers/clk/clk-cs2000-cp.c b/drivers/clk/clk-cs2000-cp.c
index db7290621cef..c869bc692dfb 100644
--- a/drivers/clk/clk-cs2000-cp.c
+++ b/drivers/clk/clk-cs2000-cp.c
@@ -49,7 +49,7 @@
 #define LOCKCLK_MASK	LOCKCLK(0x3)
 #define FRACNSRC_MASK	(1 << 0)
 #define FRACNSRC_STATIC		(0 << 0)
-#define FRACNSRC_DYNAMIC	(1 << 1)
+#define FRACNSRC_DYNAMIC	(1 << 0)
 
 /* GLOBAL_CFG */
 #define ENDEV2		(0x1)
@@ -79,6 +79,9 @@ struct cs2000_priv {
 	struct clk *clk_in;
 	struct clk *ref_clk;
 
+	bool dynamic_mode;
+	bool lf_ratio;
+
 	/* suspend/resume */
 	unsigned long saved_rate;
 	unsigned long saved_parent_rate;
@@ -134,17 +137,11 @@ static int cs2000_enable_dev_config(struct cs2000_priv *priv, bool enable)
 	if (ret < 0)
 		return ret;
 
-	/* FIXME: for Static ratio mode */
-	ret = cs2000_bset(priv, FUNC_CFG2, LFRATIO_MASK,
-			  LFRATIO_12_20);
-	if (ret < 0)
-		return ret;
-
 	return 0;
 }
 
-static int cs2000_clk_in_bound_rate(struct cs2000_priv *priv,
-				    u32 rate_in)
+static int cs2000_ref_clk_bound_rate(struct cs2000_priv *priv,
+				     u32 rate_in)
 {
 	u32 val;
 
@@ -191,35 +188,37 @@ static int cs2000_clk_out_enable(struct cs2000_priv *priv, bool enable)
 			   (AUXOUTDIS | CLKOUTDIS));
 }
 
-static u32 cs2000_rate_to_ratio(u32 rate_in, u32 rate_out)
+static u32 cs2000_rate_to_ratio(u32 rate_in, u32 rate_out, bool lf_ratio)
 {
 	u64 ratio;
+	u32 multiplier = lf_ratio ? 12 : 20;
 
 	/*
-	 * ratio = rate_out / rate_in * 2^20
+	 * ratio = rate_out / rate_in * 2^multiplier
 	 *
 	 * To avoid over flow, rate_out is u64.
 	 * The result should be u32.
 	 */
-	ratio = (u64)rate_out << 20;
+	ratio = (u64)rate_out << multiplier;
 	do_div(ratio, rate_in);
 
 	return ratio;
 }
 
-static unsigned long cs2000_ratio_to_rate(u32 ratio, u32 rate_in)
+static unsigned long cs2000_ratio_to_rate(u32 ratio, u32 rate_in, bool lf_ratio)
 {
 	u64 rate_out;
+	u32 multiplier = lf_ratio ? 12 : 20;
 
 	/*
-	 * ratio = rate_out / rate_in * 2^20
+	 * ratio = rate_out / rate_in * 2^multiplier
 	 *
 	 * To avoid over flow, rate_out is u64.
 	 * The result should be u32 or unsigned long.
 	 */
 
 	rate_out = (u64)ratio * rate_in;
-	return rate_out >> 20;
+	return rate_out >> multiplier;
 }
 
 static int cs2000_ratio_set(struct cs2000_priv *priv,
@@ -232,7 +231,7 @@ static int cs2000_ratio_set(struct cs2000_priv *priv,
 	if (CH_SIZE_ERR(ch))
 		return -EINVAL;
 
-	val = cs2000_rate_to_ratio(rate_in, rate_out);
+	val = cs2000_rate_to_ratio(rate_in, rate_out, priv->lf_ratio);
 	for (i = 0; i < RATIO_REG_SIZE; i++) {
 		ret = cs2000_write(priv,
 				   Ratio_Add(ch, i),
@@ -265,22 +264,21 @@ static u32 cs2000_ratio_get(struct cs2000_priv *priv, int ch)
 static int cs2000_ratio_select(struct cs2000_priv *priv, int ch)
 {
 	int ret;
+	u8 fracnsrc;
 
 	if (CH_SIZE_ERR(ch))
 		return -EINVAL;
 
-	/*
-	 * FIXME
-	 *
-	 * this driver supports static ratio mode only at this point.
-	 */
 	ret = cs2000_bset(priv, DEVICE_CFG1, RSEL_MASK, RSEL(ch));
 	if (ret < 0)
 		return ret;
 
+	/* Always use dynamic mode when CLK_IN is present */
+	fracnsrc = priv->dynamic_mode ? FRACNSRC_DYNAMIC : FRACNSRC_STATIC;
+
 	ret = cs2000_bset(priv, DEVICE_CFG2,
-			  (AUTORMOD | LOCKCLK_MASK | FRACNSRC_MASK),
-			  (LOCKCLK(ch) | FRACNSRC_STATIC));
+			  AUTORMOD | LOCKCLK_MASK | FRACNSRC_MASK,
+			  LOCKCLK(ch) | fracnsrc);
 	if (ret < 0)
 		return ret;
 
@@ -296,17 +294,40 @@ static unsigned long cs2000_recalc_rate(struct clk_hw *hw,
 
 	ratio = cs2000_ratio_get(priv, ch);
 
-	return cs2000_ratio_to_rate(ratio, parent_rate);
+	return cs2000_ratio_to_rate(ratio, parent_rate, priv->lf_ratio);
 }
 
 static long cs2000_round_rate(struct clk_hw *hw, unsigned long rate,
 			      unsigned long *parent_rate)
 {
+	struct cs2000_priv *priv = hw_to_priv(hw);
 	u32 ratio;
 
-	ratio = cs2000_rate_to_ratio(*parent_rate, rate);
+	ratio = cs2000_rate_to_ratio(*parent_rate, rate, priv->lf_ratio);
 
-	return cs2000_ratio_to_rate(ratio, *parent_rate);
+	return cs2000_ratio_to_rate(ratio, *parent_rate, priv->lf_ratio);
+}
+
+static int cs2000_select_ratio_mode(struct cs2000_priv *priv,
+				    unsigned long rate,
+				    unsigned long parent_rate)
+{
+	/*
+	 * From the datasheet:
+	 *
+	 * | It is recommended that the 12.20 High-Resolution format be
+	 * | utilized whenever the desired ratio is less than 4096 since
+	 * | the output frequency accuracy of the PLL is directly proportional
+	 * | to the accuracy of the timing reference clock and the resolution
+	 * | of the R_UD.
+	 *
+	 * This mode is only available in dynamic mode (ie, with a CLK_IN input
+	 * present).
+	 */
+	priv->lf_ratio = priv->dynamic_mode && ((rate / parent_rate) > 4096);
+
+	return cs2000_bset(priv, FUNC_CFG2, LFRATIO_MASK,
+			   priv->lf_ratio ? LFRATIO_20_12 : LFRATIO_12_20);
 }
 
 static int __cs2000_set_rate(struct cs2000_priv *priv, int ch,
@@ -315,7 +336,7 @@ static int __cs2000_set_rate(struct cs2000_priv *priv, int ch,
 {
 	int ret;
 
-	ret = cs2000_clk_in_bound_rate(priv, parent_rate);
+	ret = cs2000_select_ratio_mode(priv, rate, parent_rate);
 	if (ret < 0)
 		return ret;
 
@@ -382,8 +403,14 @@ static void cs2000_disable(struct clk_hw *hw)
 
 static u8 cs2000_get_parent(struct clk_hw *hw)
 {
-	/* always return REF_CLK */
-	return REF_CLK;
+	struct cs2000_priv *priv = hw_to_priv(hw);
+
+	/*
+	 * If CLK_IN was provided, we're operating in dynamic mode,
+	 * hence output clock rates are derived from that.
+	 */
+
+	return priv->dynamic_mode ? CLK_IN : REF_CLK;
 }
 
 static const struct clk_ops cs2000_ops = {
@@ -424,28 +451,41 @@ static int cs2000_clk_register(struct cs2000_priv *priv)
 	const char *name = np->name;
 	static const char *parent_names[CLK_MAX];
 	u32 aux_out = 0;
+	int ref_clk_rate;
 	int ch = 0; /* it uses ch0 only at this point */
-	int rate;
 	int ret;
 
 	of_property_read_string(np, "clock-output-names", &name);
 
+	priv->dynamic_mode = of_property_read_bool(np, "cirrus,dynamic-mode");
+	dev_info(dev, "operating in %s mode\n",
+		 priv->dynamic_mode ? "dynamic" : "static");
+
 	of_property_read_u32(np, "cirrus,aux-output-source", &aux_out);
 	ret = cs2000_bset(priv, DEVICE_CFG1,
 			  AUXOUTSRC_MASK, AUXOUTSRC(aux_out));
 	if (ret < 0)
 		return ret;
 
-	/*
-	 * set default rate as 1/1.
-	 * otherwise .set_rate which setup ratio
-	 * is never called if user requests 1/1 rate
-	 */
-	rate = clk_get_rate(priv->ref_clk);
-	ret = __cs2000_set_rate(priv, ch, rate, rate);
+	ref_clk_rate = clk_get_rate(priv->ref_clk);
+	ret = cs2000_ref_clk_bound_rate(priv, ref_clk_rate);
 	if (ret < 0)
 		return ret;
 
+	if (priv->clk_in) {
+		/* Default to low-frequency mode to allow for large ratios */
+		priv->lf_ratio = true;
+	} else {
+		/*
+		 * set default rate as 1/1.
+		 * otherwise .set_rate which setup ratio
+		 * is never called if user requests 1/1 rate
+		 */
+		ret = __cs2000_set_rate(priv, ch, ref_clk_rate, ref_clk_rate);
+		if (ret < 0)
+			return ret;
+	}
+
 	parent_names[CLK_IN]	= __clk_get_name(priv->clk_in);
 	parent_names[REF_CLK]	= __clk_get_name(priv->ref_clk);
 
-- 
2.31.1


  parent reply	other threads:[~2021-08-27 12:02 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-27 11:54 [PATCH v4 0/9] clk: cs2000-cp: add dynamic mode and more features Daniel Mack
2021-08-27 11:54 ` [PATCH v4 1/9] dt-bindings: clock: convert cs2000-cp bindings to yaml Daniel Mack
2021-08-27 11:54 ` [PATCH v4 2/9] dt-bindings: clock: cs2000-cp: document aux-output-source Daniel Mack
2021-08-31 20:35   ` Rob Herring
2021-08-27 11:54 ` [PATCH v4 3/9] dt-bindings: clock: cs2000-cp: document cirrus,clock-skip flag Daniel Mack
2021-08-31 20:36   ` Rob Herring
2021-08-27 11:54 ` [PATCH v4 4/9] dt-bindings: clock: cs2000-cp: document cirrus,dynamic-mode Daniel Mack
2021-08-31 20:36   ` Rob Herring
2021-08-27 11:54 ` [PATCH v4 5/9] clk: cs2000-cp: Make aux output function controllable Daniel Mack
2021-08-27 11:54 ` Daniel Mack [this message]
2021-08-27 11:54 ` [PATCH v4 7/9] clk: cs2000-cp: make clock skip setting configurable Daniel Mack
2021-08-27 11:54 ` [PATCH v4 8/9] clk: cs2000-cp: freeze config during register fiddling Daniel Mack
2021-08-27 11:54 ` [PATCH v4 9/9] clk: cs2000-cp: convert driver to regmap Daniel Mack

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210827115420.3052019-7-daniel@zonque.org \
    --to=daniel@zonque.org \
    --cc=devicetree@vger.kernel.org \
    --cc=kuninori.morimoto.gx@renesas.com \
    --cc=linux-clk@vger.kernel.org \
    --cc=mturquette@baylibre.com \
    --cc=robh+dt@kernel.org \
    --cc=sboyd@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.