summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Boyd <sboyd@kernel.org>2021-09-01 15:26:42 -0700
committerStephen Boyd <sboyd@kernel.org>2021-09-01 15:26:45 -0700
commit1faa7cb2b066a3f8966a1311d574832ccb93a72c (patch)
tree47e3299c00c2407417fdb957d182ad9f10832f32
parent7110569a096d820876f99543660741bd8a96af7c (diff)
parente81b917a78c7ed08007de5b502a14535daa106bf (diff)
Merge branch 'clk-frac-divider' into clk-next
- Add power of two flag to fractional divider clk type * clk-frac-divider: clk: fractional-divider: Document the arithmetics used behind the code clk: fractional-divider: Introduce POWER_OF_TWO_PS flag clk: fractional-divider: Hide clk_fractional_divider_ops from wide audience clk: fractional-divider: Export approximation algorithm to the CCF users
-rw-r--r--drivers/acpi/acpi_lpss.c4
-rw-r--r--drivers/clk/clk-fractional-divider.c56
-rw-r--r--drivers/clk/clk-fractional-divider.h15
-rw-r--r--drivers/clk/imx/clk-composite-7ulp.c1
-rw-r--r--drivers/clk/rockchip/clk.c17
-rw-r--r--drivers/mfd/intel-lpss.c3
-rw-r--r--include/linux/clk-provider.h12
7 files changed, 79 insertions, 29 deletions
diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c
index 7f163074e4e4..30b1f511c2af 100644
--- a/drivers/acpi/acpi_lpss.c
+++ b/drivers/acpi/acpi_lpss.c
@@ -436,8 +436,8 @@ static int register_device_clock(struct acpi_device *adev,
if (!clk_name)
return -ENOMEM;
clk = clk_register_fractional_divider(NULL, clk_name, parent,
- 0, prv_base,
- 1, 15, 16, 15, 0, NULL);
+ CLK_FRAC_DIVIDER_POWER_OF_TWO_PS,
+ prv_base, 1, 15, 16, 15, 0, NULL);
parent = clk_name;
clk_name = kasprintf(GFP_KERNEL, "%s-update", devname);
diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c
index b1e556f20911..4274540327ce 100644
--- a/drivers/clk/clk-fractional-divider.c
+++ b/drivers/clk/clk-fractional-divider.c
@@ -3,8 +3,39 @@
* 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 much much better result of m and n than simple
+ * saturated values.
*/
#include <linux/clk-provider.h>
@@ -14,6 +45,8 @@
#include <linux/slab.h>
#include <linux/rational.h>
+#include "clk-fractional-divider.h"
+
static inline u32 clk_fd_readl(struct clk_fractional_divider *fd)
{
if (fd->flags & CLK_FRAC_DIVIDER_BIG_ENDIAN)
@@ -68,21 +101,26 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
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;
/*
* 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;
+ }
rational_best_approximation(rate, *parent_rate,
GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0),
@@ -102,7 +140,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);
diff --git a/drivers/clk/clk-fractional-divider.h b/drivers/clk/clk-fractional-divider.h
new file mode 100644
index 000000000000..f0f71d23797b
--- /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/imx/clk-composite-7ulp.c b/drivers/clk/imx/clk-composite-7ulp.c
index 7c4f31b31eb0..d85ba78abbb1 100644
--- a/drivers/clk/imx/clk-composite-7ulp.c
+++ b/drivers/clk/imx/clk-composite-7ulp.c
@@ -10,6 +10,7 @@
#include <linux/err.h>
#include <linux/slab.h>
+#include "../clk-fractional-divider.h"
#include "clk.h"
#define PCG_PCS_SHIFT 24
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 049e5e0b64f6..b7be7e11b0df 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -22,6 +22,8 @@
#include <linux/regmap.h>
#include <linux/reboot.h>
#include <linux/rational.h>
+
+#include "../clk-fractional-divider.h"
#include "clk.h"
/*
@@ -178,10 +180,8 @@ static void rockchip_fractional_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 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)) {
@@ -190,18 +190,7 @@ 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);
+ clk_fractional_divider_general_approximation(hw, rate, parent_rate, m, n);
}
static struct clk *rockchip_clk_register_frac_branch(
diff --git a/drivers/mfd/intel-lpss.c b/drivers/mfd/intel-lpss.c
index a9bf10bee796..0e15afc39f54 100644
--- a/drivers/mfd/intel-lpss.c
+++ b/drivers/mfd/intel-lpss.c
@@ -301,7 +301,8 @@ static int intel_lpss_register_clock_divider(struct intel_lpss *lpss,
snprintf(name, sizeof(name), "%s-div", devname);
tmp = clk_register_fractional_divider(NULL, name, __clk_get_name(tmp),
- 0, lpss->priv, 1, 15, 16, 15, 0,
+ CLK_FRAC_DIVIDER_POWER_OF_TWO_PS,
+ lpss->priv, 1, 15, 16, 15, 0,
NULL);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 7be81d5fcf8c..f59c875271a0 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -1001,6 +1001,12 @@ struct clk_hw *devm_clk_hw_register_fixed_factor(struct device *dev,
* 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.
+ * 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;
@@ -1020,10 +1026,10 @@ struct clk_fractional_divider {
#define to_clk_fd(_hw) container_of(_hw, struct clk_fractional_divider, hw)
-#define CLK_FRAC_DIVIDER_ZERO_BASED BIT(0)
-#define CLK_FRAC_DIVIDER_BIG_ENDIAN BIT(1)
+#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)
-extern const struct clk_ops clk_fractional_divider_ops;
struct clk *clk_register_fractional_divider(struct device *dev,
const char *name, const char *parent_name, unsigned long flags,
void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,