All of lore.kernel.org
 help / color / mirror / Atom feed
From: Anson Huang <anson.huang@nxp.com>
To: "mturquette@baylibre.com" <mturquette@baylibre.com>,
	"sboyd@kernel.org" <sboyd@kernel.org>,
	"shawnguo@kernel.org" <shawnguo@kernel.org>,
	"s.hauer@pengutronix.de" <s.hauer@pengutronix.de>,
	"kernel@pengutronix.de" <kernel@pengutronix.de>,
	"festevam@gmail.com" <festevam@gmail.com>,
	Aisheng Dong <aisheng.dong@nxp.com>,
	"linux-clk@vger.kernel.org" <linux-clk@vger.kernel.org>,
	"linux-arm-kernel@lists.infradead.org" 
	<linux-arm-kernel@lists.infradead.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>
Cc: dl-linux-imx <linux-imx@nxp.com>
Subject: [PATCH V2] clk: imx: pllv4: add fractional-N pll support
Date: Tue, 30 Apr 2019 00:57:22 +0000	[thread overview]
Message-ID: <1556585557-28795-1-git-send-email-Anson.Huang@nxp.com> (raw)

The pllv4 supports fractional-N function, the formula is:

PLL output freq = input * (mult + num/denom),

This patch adds fractional-N function support, including
clock round rate, calculate rate and set rate, with this
patch, the clock rate of APLL in clock tree is more accurate
than before:

Without fraction:
apll_pre_sel                      1        1        1    24000000          0     0  50000
   apll_pre_div                   1        1        2    24000000          0     0  50000
      apll                        1        1        2   528000000          0     0  50000
         apll_pfd3                0        0        0   792000000          0     0  50000
         apll_pfd2                0        0        0   339428571          0     0  50000
         apll_pfd1                0        0        0   352000000          0     0  50000
            usdhc0                0        0        0   352000000          0     0  50000
         apll_pfd0                1        1        1   352000000          0     0  50000

With fraction:
apll_pre_sel                      1        1        1    24000000          0     0  50000
   apll_pre_div                   1        1        2    24000000          0     0  50000
      apll                        1        1        2   529200000          0     0  50000
         apll_pfd3                0        0        0   793800000          0     0  50000
         apll_pfd2                0        0        0   340200000          0     0  50000
         apll_pfd1                0        0        0   352800000          0     0  50000
            usdhc0                0        0        0   352800000          0     0  50000
         apll_pfd0                1        1        1   352800000          0     0  50000

Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
Reviewed-by: Dong Aisheng <aisheng.dong@nxp.com>
---
 drivers/clk/imx/clk-pllv4.c | 72 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 63 insertions(+), 9 deletions(-)

diff --git a/drivers/clk/imx/clk-pllv4.c b/drivers/clk/imx/clk-pllv4.c
index d38bc9f..d7e62c3 100644
--- a/drivers/clk/imx/clk-pllv4.c
+++ b/drivers/clk/imx/clk-pllv4.c
@@ -30,6 +30,9 @@
 /* PLL Denominator Register (xPLLDENOM) */
 #define PLL_DENOM_OFFSET	0x14
 
+#define MAX_MFD			0x3fffffff
+#define DEFAULT_MFD		1000000
+
 struct clk_pllv4 {
 	struct clk_hw	hw;
 	void __iomem	*base;
@@ -64,13 +67,20 @@ static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw,
 					   unsigned long parent_rate)
 {
 	struct clk_pllv4 *pll = to_clk_pllv4(hw);
-	u32 div;
+	u32 mult, mfn, mfd;
+	u64 temp64;
+
+	mult = readl_relaxed(pll->base + PLL_CFG_OFFSET);
+	mult &= BM_PLL_MULT;
+	mult >>= BP_PLL_MULT;
 
-	div = readl_relaxed(pll->base + PLL_CFG_OFFSET);
-	div &= BM_PLL_MULT;
-	div >>= BP_PLL_MULT;
+	mfn = readl_relaxed(pll->base + PLL_NUM_OFFSET);
+	mfd = readl_relaxed(pll->base + PLL_DENOM_OFFSET);
+	temp64 = parent_rate;
+	temp64 *= mfn;
+	do_div(temp64, mfd);
 
-	return parent_rate * div;
+	return (parent_rate * mult) + (u32)temp64;
 }
 
 static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
@@ -78,14 +88,46 @@ static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
 {
 	unsigned long parent_rate = *prate;
 	unsigned long round_rate, i;
+	u32 mfn, mfd = DEFAULT_MFD;
+	bool found = false;
+	u64 temp64;
 
 	for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
 		round_rate = parent_rate * pllv4_mult_table[i];
-		if (rate >= round_rate)
-			return round_rate;
+		if (rate >= round_rate) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		pr_warn("%s: unable to round rate %lu, parent rate %lu\n",
+			clk_hw_get_name(hw), rate, parent_rate);
+		return 0;
 	}
 
-	return round_rate;
+	if (parent_rate <= MAX_MFD)
+		mfd = parent_rate;
+
+	temp64 = (u64)(rate - round_rate);
+	temp64 *= mfd;
+	do_div(temp64, parent_rate);
+	mfn = temp64;
+
+	/*
+	 * NOTE: The value of numerator must always be configured to be
+	 * less than the value of the denominator. If we can't get a proper
+	 * pair of mfn/mfd, we simply return the round_rate without using
+	 * the frac part.
+	 */
+	if (mfn >= mfd)
+		return round_rate;
+
+	temp64 = (u64)parent_rate;
+	temp64 *= mfn;
+	do_div(temp64, mfd);
+
+	return round_rate + (u32)temp64;
 }
 
 static bool clk_pllv4_is_valid_mult(unsigned int mult)
@@ -105,18 +147,30 @@ static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate,
 			      unsigned long parent_rate)
 {
 	struct clk_pllv4 *pll = to_clk_pllv4(hw);
-	u32 val, mult;
+	u32 val, mult, mfn, mfd = DEFAULT_MFD;
+	u64 temp64;
 
 	mult = rate / parent_rate;
 
 	if (!clk_pllv4_is_valid_mult(mult))
 		return -EINVAL;
 
+	if (parent_rate <= MAX_MFD)
+		mfd = parent_rate;
+
+	temp64 = (u64)(rate - mult * parent_rate);
+	temp64 *= mfd;
+	do_div(temp64, parent_rate);
+	mfn = temp64;
+
 	val = readl_relaxed(pll->base + PLL_CFG_OFFSET);
 	val &= ~BM_PLL_MULT;
 	val |= mult << BP_PLL_MULT;
 	writel_relaxed(val, pll->base + PLL_CFG_OFFSET);
 
+	writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET);
+	writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET);
+
 	return 0;
 }
 
-- 
2.7.4


WARNING: multiple messages have this Message-ID (diff)
From: Anson Huang <anson.huang@nxp.com>
To: "mturquette@baylibre.com" <mturquette@baylibre.com>,
	"sboyd@kernel.org" <sboyd@kernel.org>,
	"shawnguo@kernel.org" <shawnguo@kernel.org>,
	"s.hauer@pengutronix.de" <s.hauer@pengutronix.de>,
	"kernel@pengutronix.de" <kernel@pengutronix.de>,
	"festevam@gmail.com" <festevam@gmail.com>,
	Aisheng Dong <aisheng.dong@nxp.com>,
	"linux-clk@vger.kernel.org" <linux-clk@vger.kernel.org>,
	"linux-arm-kernel@lists.infradead.org"
	<linux-arm-kernel@lists.infradead.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>
Cc: dl-linux-imx <linux-imx@nxp.com>
Subject: [PATCH V2] clk: imx: pllv4: add fractional-N pll support
Date: Tue, 30 Apr 2019 00:57:22 +0000	[thread overview]
Message-ID: <1556585557-28795-1-git-send-email-Anson.Huang@nxp.com> (raw)

The pllv4 supports fractional-N function, the formula is:

PLL output freq = input * (mult + num/denom),

This patch adds fractional-N function support, including
clock round rate, calculate rate and set rate, with this
patch, the clock rate of APLL in clock tree is more accurate
than before:

Without fraction:
apll_pre_sel                      1        1        1    24000000          0     0  50000
   apll_pre_div                   1        1        2    24000000          0     0  50000
      apll                        1        1        2   528000000          0     0  50000
         apll_pfd3                0        0        0   792000000          0     0  50000
         apll_pfd2                0        0        0   339428571          0     0  50000
         apll_pfd1                0        0        0   352000000          0     0  50000
            usdhc0                0        0        0   352000000          0     0  50000
         apll_pfd0                1        1        1   352000000          0     0  50000

With fraction:
apll_pre_sel                      1        1        1    24000000          0     0  50000
   apll_pre_div                   1        1        2    24000000          0     0  50000
      apll                        1        1        2   529200000          0     0  50000
         apll_pfd3                0        0        0   793800000          0     0  50000
         apll_pfd2                0        0        0   340200000          0     0  50000
         apll_pfd1                0        0        0   352800000          0     0  50000
            usdhc0                0        0        0   352800000          0     0  50000
         apll_pfd0                1        1        1   352800000          0     0  50000

Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
Reviewed-by: Dong Aisheng <aisheng.dong@nxp.com>
---
 drivers/clk/imx/clk-pllv4.c | 72 +++++++++++++++++++++++++++++++++++++++------
 1 file changed, 63 insertions(+), 9 deletions(-)

diff --git a/drivers/clk/imx/clk-pllv4.c b/drivers/clk/imx/clk-pllv4.c
index d38bc9f..d7e62c3 100644
--- a/drivers/clk/imx/clk-pllv4.c
+++ b/drivers/clk/imx/clk-pllv4.c
@@ -30,6 +30,9 @@
 /* PLL Denominator Register (xPLLDENOM) */
 #define PLL_DENOM_OFFSET	0x14
 
+#define MAX_MFD			0x3fffffff
+#define DEFAULT_MFD		1000000
+
 struct clk_pllv4 {
 	struct clk_hw	hw;
 	void __iomem	*base;
@@ -64,13 +67,20 @@ static unsigned long clk_pllv4_recalc_rate(struct clk_hw *hw,
 					   unsigned long parent_rate)
 {
 	struct clk_pllv4 *pll = to_clk_pllv4(hw);
-	u32 div;
+	u32 mult, mfn, mfd;
+	u64 temp64;
+
+	mult = readl_relaxed(pll->base + PLL_CFG_OFFSET);
+	mult &= BM_PLL_MULT;
+	mult >>= BP_PLL_MULT;
 
-	div = readl_relaxed(pll->base + PLL_CFG_OFFSET);
-	div &= BM_PLL_MULT;
-	div >>= BP_PLL_MULT;
+	mfn = readl_relaxed(pll->base + PLL_NUM_OFFSET);
+	mfd = readl_relaxed(pll->base + PLL_DENOM_OFFSET);
+	temp64 = parent_rate;
+	temp64 *= mfn;
+	do_div(temp64, mfd);
 
-	return parent_rate * div;
+	return (parent_rate * mult) + (u32)temp64;
 }
 
 static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
@@ -78,14 +88,46 @@ static long clk_pllv4_round_rate(struct clk_hw *hw, unsigned long rate,
 {
 	unsigned long parent_rate = *prate;
 	unsigned long round_rate, i;
+	u32 mfn, mfd = DEFAULT_MFD;
+	bool found = false;
+	u64 temp64;
 
 	for (i = 0; i < ARRAY_SIZE(pllv4_mult_table); i++) {
 		round_rate = parent_rate * pllv4_mult_table[i];
-		if (rate >= round_rate)
-			return round_rate;
+		if (rate >= round_rate) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		pr_warn("%s: unable to round rate %lu, parent rate %lu\n",
+			clk_hw_get_name(hw), rate, parent_rate);
+		return 0;
 	}
 
-	return round_rate;
+	if (parent_rate <= MAX_MFD)
+		mfd = parent_rate;
+
+	temp64 = (u64)(rate - round_rate);
+	temp64 *= mfd;
+	do_div(temp64, parent_rate);
+	mfn = temp64;
+
+	/*
+	 * NOTE: The value of numerator must always be configured to be
+	 * less than the value of the denominator. If we can't get a proper
+	 * pair of mfn/mfd, we simply return the round_rate without using
+	 * the frac part.
+	 */
+	if (mfn >= mfd)
+		return round_rate;
+
+	temp64 = (u64)parent_rate;
+	temp64 *= mfn;
+	do_div(temp64, mfd);
+
+	return round_rate + (u32)temp64;
 }
 
 static bool clk_pllv4_is_valid_mult(unsigned int mult)
@@ -105,18 +147,30 @@ static int clk_pllv4_set_rate(struct clk_hw *hw, unsigned long rate,
 			      unsigned long parent_rate)
 {
 	struct clk_pllv4 *pll = to_clk_pllv4(hw);
-	u32 val, mult;
+	u32 val, mult, mfn, mfd = DEFAULT_MFD;
+	u64 temp64;
 
 	mult = rate / parent_rate;
 
 	if (!clk_pllv4_is_valid_mult(mult))
 		return -EINVAL;
 
+	if (parent_rate <= MAX_MFD)
+		mfd = parent_rate;
+
+	temp64 = (u64)(rate - mult * parent_rate);
+	temp64 *= mfd;
+	do_div(temp64, parent_rate);
+	mfn = temp64;
+
 	val = readl_relaxed(pll->base + PLL_CFG_OFFSET);
 	val &= ~BM_PLL_MULT;
 	val |= mult << BP_PLL_MULT;
 	writel_relaxed(val, pll->base + PLL_CFG_OFFSET);
 
+	writel_relaxed(mfn, pll->base + PLL_NUM_OFFSET);
+	writel_relaxed(mfd, pll->base + PLL_DENOM_OFFSET);
+
 	return 0;
 }
 
-- 
2.7.4

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

             reply	other threads:[~2019-04-30  0:57 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-04-30  0:57 Anson Huang [this message]
2019-04-30  0:57 ` [PATCH V2] clk: imx: pllv4: add fractional-N pll support Anson Huang
2019-05-01 21:00 ` Stephen Boyd
2019-05-01 21:00   ` Stephen Boyd
2019-05-03  2:38   ` Aisheng Dong
2019-05-03  2:38     ` Aisheng Dong
2019-05-03 15:57     ` Stephen Boyd
2019-05-03 15:57       ` Stephen Boyd
2019-05-03 16:00     ` Stephen Boyd
2019-05-03 16:00       ` Stephen Boyd
2019-05-05  6:18       ` Aisheng Dong
2019-05-05  6:18         ` Aisheng Dong
2019-05-03 13:00   ` Anson Huang
2019-05-03 13:00     ` Anson Huang

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=1556585557-28795-1-git-send-email-Anson.Huang@nxp.com \
    --to=anson.huang@nxp.com \
    --cc=aisheng.dong@nxp.com \
    --cc=festevam@gmail.com \
    --cc=kernel@pengutronix.de \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=linux-imx@nxp.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mturquette@baylibre.com \
    --cc=s.hauer@pengutronix.de \
    --cc=sboyd@kernel.org \
    --cc=shawnguo@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.