From: Alexander Shiyan <eagle.alexander923@gmail.com>
To: barebox@lists.infradead.org
Cc: Alexander Shiyan <eagle.alexander923@gmail.com>
Subject: [PATCH 1/3] clk: Update clk_fractional_divider driver
Date: Wed, 9 Apr 2025 12:01:15 +0300 [thread overview]
Message-ID: <20250409090117.32359-1-eagle.alexander923@gmail.com> (raw)
This patch updates the clk_fractional_divider driver code from
the Linux kernel repository and updates affected drivers that
use this code.
Signed-off-by: Alexander Shiyan <eagle.alexander923@gmail.com>
---
drivers/clk/clk-fractional-divider.c | 186 ++++++++++++++-------------
drivers/clk/clk-fractional-divider.h | 15 +++
drivers/clk/rockchip/clk.c | 22 +---
include/linux/clk.h | 41 +++---
4 files changed, 132 insertions(+), 132 deletions(-)
create mode 100644 drivers/clk/clk-fractional-divider.h
diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c
index d175921f64..2e6c391614 100644
--- a/drivers/clk/clk-fractional-divider.c
+++ b/drivers/clk/clk-fractional-divider.c
@@ -1,22 +1,47 @@
-// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2014 Intel Corporation
*
* Adjustable fractional divider clock implementation.
- * Output rate = (m / n) * parent_rate.
* Uses rational best approximation algorithm.
+ *
+ * Output is calculated as
+ *
+ * rate = (m / n) * parent_rate (1)
+ *
+ * This is useful when we have a prescaler block which asks for
+ * m (numerator) and n (denominator) values to be provided to satisfy
+ * the (1) as much as possible.
+ *
+ * Since m and n have the limitation by a range, e.g.
+ *
+ * n >= 1, n < N_width, where N_width = 2^nwidth (2)
+ *
+ * for some cases the output may be saturated. Hence, from (1) and (2),
+ * assuming the worst case when m = 1, the inequality
+ *
+ * floor(log2(parent_rate / rate)) <= nwidth (3)
+ *
+ * may be derived. Thus, in cases when
+ *
+ * (parent_rate / rate) >> N_width (4)
+ *
+ * we might scale up the rate by 2^scale (see the description of
+ * CLK_FRAC_DIVIDER_POWER_OF_TWO_PS for additional information), where
+ *
+ * scale = floor(log2(parent_rate / rate)) - nwidth (5)
+ *
+ * and assume that the IP, that needs m and n, has also its own
+ * prescaler, which is capable to divide by 2^scale. In this way
+ * we get the denominator to satisfy the desired range (2) and
+ * at the same time a much better result of m and n than simple
+ * saturated values.
*/
#include <common.h>
-#include <io.h>
-#include <malloc.h>
-#include <linux/clk.h>
-#include <linux/spinlock.h>
-#include <linux/err.h>
-#include <linux/gcd.h>
-#include <linux/math64.h>
#include <linux/rational.h>
-#include <linux/barebox-wrapper.h>
+
+#include "clk-fractional-divider.h"
static inline u32 clk_fd_readl(struct clk_fractional_divider *fd)
{
@@ -34,53 +59,79 @@ static inline void clk_fd_writel(struct clk_fractional_divider *fd, u32 val)
writel(val, fd->reg);
}
-static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
+static void clk_fd_get_div(struct clk_hw *hw, struct u32_fract *fract)
{
struct clk_fractional_divider *fd = to_clk_fd(hw);
unsigned long m, n;
+ u32 mmask, nmask;
u32 val;
- u64 ret;
val = clk_fd_readl(fd);
- m = (val & fd->mmask) >> fd->mshift;
- n = (val & fd->nmask) >> fd->nshift;
+ mmask = GENMASK(fd->mwidth - 1, 0) << fd->mshift;
+ nmask = GENMASK(fd->nwidth - 1, 0) << fd->nshift;
+
+ m = (val & mmask) >> fd->mshift;
+ n = (val & nmask) >> fd->nshift;
if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) {
m++;
n++;
}
- if (!n || !m)
+ fract->numerator = m;
+ fract->denominator = n;
+}
+
+static unsigned long clk_fd_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct u32_fract fract;
+ u64 ret;
+
+ clk_fd_get_div(hw, &fract);
+
+ if (!fract.numerator || !fract.denominator)
return parent_rate;
- ret = (u64)parent_rate * m;
- do_div(ret, n);
+ ret = (u64)parent_rate * fract.numerator;
+ do_div(ret, fract.denominator);
return ret;
}
-static void clk_fd_general_approximation(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate,
- unsigned long *m, unsigned long *n)
+void clk_fractional_divider_general_approximation(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *parent_rate,
+ unsigned long *m, unsigned long *n)
{
struct clk_fractional_divider *fd = to_clk_fd(hw);
- unsigned long scale;
+ unsigned long max_m, max_n;
/*
* Get rate closer to *parent_rate to guarantee there is no overflow
* for m and n. In the result it will be the nearest rate left shifted
* by (scale - fd->nwidth) bits.
+ *
+ * For the detailed explanation see the top comment in this file.
*/
- scale = fls_long(*parent_rate / rate - 1);
- if (scale > fd->nwidth)
- rate <<= scale - fd->nwidth;
+ if (fd->flags & CLK_FRAC_DIVIDER_POWER_OF_TWO_PS) {
+ unsigned long scale = fls_long(*parent_rate / rate - 1);
+
+ if (scale > fd->nwidth)
+ rate <<= scale - fd->nwidth;
+ }
+
+ if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) {
+ max_m = BIT(fd->mwidth);
+ max_n = BIT(fd->nwidth);
+ } else {
+ max_m = GENMASK(fd->mwidth - 1, 0);
+ max_n = GENMASK(fd->nwidth - 1, 0);
+ }
- rational_best_approximation(rate, *parent_rate,
- GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0),
- m, n);
+ rational_best_approximation(rate, *parent_rate, max_m, max_n, m, n);
}
+EXPORT_SYMBOL_GPL(clk_fractional_divider_general_approximation);
static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
@@ -96,7 +147,7 @@ static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate,
if (fd->approximation)
fd->approximation(hw, rate, parent_rate, &m, &n);
else
- clk_fd_general_approximation(hw, rate, parent_rate, &m, &n);
+ clk_fractional_divider_general_approximation(hw, rate, parent_rate, &m, &n);
ret = (u64)*parent_rate * m;
do_div(ret, n);
@@ -108,20 +159,29 @@ static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_fractional_divider *fd = to_clk_fd(hw);
- unsigned long m, n;
+ unsigned long m, n, max_m, max_n;
+ u32 mmask, nmask;
u32 val;
- rational_best_approximation(rate, parent_rate,
- GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0),
- &m, &n);
+ if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) {
+ max_m = BIT(fd->mwidth);
+ max_n = BIT(fd->nwidth);
+ } else {
+ max_m = GENMASK(fd->mwidth - 1, 0);
+ max_n = GENMASK(fd->nwidth - 1, 0);
+ }
+ rational_best_approximation(rate, parent_rate, max_m, max_n, &m, &n);
if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) {
m--;
n--;
}
+ mmask = GENMASK(fd->mwidth - 1, 0) << fd->mshift;
+ nmask = GENMASK(fd->nwidth - 1, 0) << fd->nshift;
+
val = clk_fd_readl(fd);
- val &= ~(fd->mmask | fd->nmask);
+ val &= ~(mmask | nmask);
val |= (m << fd->mshift) | (n << fd->nshift);
clk_fd_writel(fd, val);
@@ -134,61 +194,3 @@ const struct clk_ops clk_fractional_divider_ops = {
.set_rate = clk_fd_set_rate,
};
EXPORT_SYMBOL_GPL(clk_fractional_divider_ops);
-
-struct clk *clk_fractional_divider_alloc(
- const char *name, const char *parent_name, unsigned long flags,
- void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
- u8 clk_divider_flags)
-{
- struct clk_fractional_divider *fd;
-
- fd = xzalloc(sizeof(*fd));
-
- fd->reg = reg;
- fd->mshift = mshift;
- fd->mwidth = mwidth;
- fd->mmask = GENMASK(mwidth - 1, 0) << mshift;
- fd->nshift = nshift;
- fd->nwidth = nwidth;
- fd->nmask = GENMASK(nwidth - 1, 0) << nshift;
- fd->flags = clk_divider_flags;
- fd->hw.clk.name = name;
- fd->hw.clk.ops = &clk_fractional_divider_ops;
- fd->hw.clk.flags = flags;
- fd->hw.clk.parent_names = parent_name ? &parent_name : NULL;
- fd->hw.clk.num_parents = parent_name ? 1 : 0;
-
- return &fd->hw.clk;
-}
-
-void clk_fractional_divider_free(struct clk *clk_fd)
-{
- struct clk_fractional_divider *fd = to_clk_fd(clk_to_clk_hw(clk_fd));
-
- free(fd);
-}
-
-struct clk *clk_fractional_divider(
- const char *name, const char *parent_name, unsigned long flags,
- void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
- u8 clk_divider_flags)
-{
- struct clk *fd;
- int ret;
-
- fd = clk_fractional_divider_alloc(name, parent_name, flags,
- reg, mshift, mwidth, nshift, nwidth,
- clk_divider_flags);
-
- if (IS_ERR(fd))
- return fd;
-
- ret = bclk_register(fd);
- if (ret) {
- clk_fractional_divider_free(fd);
- return ERR_PTR(ret);
- }
-
- return fd;
-}
-EXPORT_SYMBOL_GPL(clk_fractional_divider);
diff --git a/drivers/clk/clk-fractional-divider.h b/drivers/clk/clk-fractional-divider.h
new file mode 100644
index 0000000000..f0f71d2379
--- /dev/null
+++ b/drivers/clk/clk-fractional-divider.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _CLK_FRACTIONAL_DIV_H
+#define _CLK_FRACTIONAL_DIV_H
+
+struct clk_hw;
+
+extern const struct clk_ops clk_fractional_divider_ops;
+
+void clk_fractional_divider_general_approximation(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *parent_rate,
+ unsigned long *m,
+ unsigned long *n);
+
+#endif
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index aedb02a8d3..d5ecf3fc13 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -15,15 +15,15 @@
*/
#include <common.h>
-#include <malloc.h>
#include <linux/clk.h>
#include <linux/regmap.h>
#include <mfd/syscon.h>
-#include <linux/spinlock.h>
#include <linux/rational.h>
#include <restart.h>
#include "clk.h"
+#include "../clk-fractional-divider.h"
+
/*
* Register a clock branch.
* Most clock branches have a form like
@@ -143,7 +143,6 @@ static void rockchip_fractional_approximation(struct clk_hw *hw,
struct clk_fractional_divider *fd = to_clk_fd(hw);
unsigned long p_rate, p_parent_rate;
struct clk_hw *p_parent;
- unsigned long scale;
p_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
if ((rate * 20 > p_rate) && (p_rate % rate != 0)) {
@@ -152,18 +151,9 @@ static void rockchip_fractional_approximation(struct clk_hw *hw,
*parent_rate = p_parent_rate;
}
- /*
- * Get rate closer to *parent_rate to guarantee there is no overflow
- * for m and n. In the result it will be the nearest rate left shifted
- * by (scale - fd->nwidth) bits.
- */
- scale = fls_long(*parent_rate / rate - 1);
- if (scale > fd->nwidth)
- rate <<= scale - fd->nwidth;
-
- rational_best_approximation(rate, *parent_rate,
- GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0),
- m, n);
+ fd->flags |= CLK_FRAC_DIVIDER_POWER_OF_TWO_PS;
+
+ clk_fractional_divider_general_approximation(hw, rate, parent_rate, m, n);
}
static struct clk *rockchip_clk_register_frac_branch(
@@ -206,10 +196,8 @@ static struct clk *rockchip_clk_register_frac_branch(
div->reg = base + muxdiv_offset;
div->mshift = 16;
div->mwidth = 16;
- div->mmask = GENMASK(div->mwidth - 1, 0) << div->mshift;
div->nshift = 0;
div->nwidth = 16;
- div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift;
div->lock = lock;
div->approximation = rockchip_fractional_approximation;
div->hw.clk.ops = &clk_fractional_divider_ops;
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 733ba356dd..b10af93af4 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -547,50 +547,45 @@ struct clk_hw *clk_hw_register_fixed_factor(struct device *dev,
* @mwidth: width of the numerator bit field
* @nshift: shift to the denominator bit field
* @nwidth: width of the denominator bit field
+ * @approximation: clk driver's callback for calculating the divider clock
+ * @lock: register lock
*
* Clock with adjustable fractional divider affecting its output frequency.
*
- * Flags:
+ * @flags:
* CLK_FRAC_DIVIDER_ZERO_BASED - by default the numerator and denominator
- * is the value read from the register. If CLK_FRAC_DIVIDER_ZERO_BASED
- * is set then the numerator and denominator are both the value read
- * plus one.
+ * is the value read from the register. If CLK_FRAC_DIVIDER_ZERO_BASED
+ * is set then the numerator and denominator are both the value read
+ * plus one.
* CLK_FRAC_DIVIDER_BIG_ENDIAN - By default little endian register accesses are
- * used for the divider register. Setting this flag makes the register
- * accesses big endian.
+ * used for the divider register. Setting this flag makes the register
+ * accesses big endian.
+ * CLK_FRAC_DIVIDER_POWER_OF_TWO_PS - By default the resulting fraction might
+ * be saturated and the caller will get quite far from the good enough
+ * approximation. Instead the caller may require, by setting this flag,
+ * to shift left by a few bits in case, when the asked one is quite small
+ * to satisfy the desired range of denominator. It assumes that on the
+ * caller's side the power-of-two capable prescaler exists.
*/
struct clk_fractional_divider {
struct clk_hw hw;
void __iomem *reg;
u8 mshift;
u8 mwidth;
- u32 mmask;
u8 nshift;
u8 nwidth;
- u32 nmask;
u8 flags;
void (*approximation)(struct clk_hw *hw,
unsigned long rate, unsigned long *parent_rate,
unsigned long *m, unsigned long *n);
- spinlock_t *lock;
+ spinlock_t *lock;
};
-#define CLK_FRAC_DIVIDER_ZERO_BASED BIT(0)
-#define CLK_FRAC_DIVIDER_BIG_ENDIAN BIT(1)
-
-struct clk *clk_fractional_divider_alloc(
- const char *name, const char *parent_name, unsigned long flags,
- void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
- u8 clk_divider_flags);
-struct clk *clk_fractional_divider(
- const char *name, const char *parent_name, unsigned long flags,
- void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
- u8 clk_divider_flags);
-void clk_fractional_divider_free(struct clk *clk_fd);
-
#define to_clk_fd(_hw) container_of(_hw, struct clk_fractional_divider, hw)
-extern const struct clk_ops clk_fractional_divider_ops;
+#define CLK_FRAC_DIVIDER_ZERO_BASED BIT(0)
+#define CLK_FRAC_DIVIDER_BIG_ENDIAN BIT(1)
+#define CLK_FRAC_DIVIDER_POWER_OF_TWO_PS BIT(2)
struct clk_mux {
struct clk_hw hw;
--
2.39.1
next reply other threads:[~2025-04-09 10:13 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-09 9:01 Alexander Shiyan [this message]
2025-04-09 9:01 ` [PATCH 2/3] clk: rockchip: fix finding of maximum clock ID Alexander Shiyan
2025-04-09 9:01 ` [PATCH 3/3] ARM: rockchip: rk3568: Remove hardcoded clock initialization Alexander Shiyan
2025-04-10 7:21 ` [PATCH 1/3] clk: Update clk_fractional_divider driver Sascha Hauer
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=20250409090117.32359-1-eagle.alexander923@gmail.com \
--to=eagle.alexander923@gmail.com \
--cc=barebox@lists.infradead.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox