diff options
-rw-r--r-- | drivers/acpi/acpi_lpss.c | 4 | ||||
-rw-r--r-- | drivers/clk/clk-fractional-divider.c | 56 | ||||
-rw-r--r-- | drivers/clk/clk-fractional-divider.h | 15 | ||||
-rw-r--r-- | drivers/clk/imx/clk-composite-7ulp.c | 1 | ||||
-rw-r--r-- | drivers/clk/rockchip/clk.c | 17 | ||||
-rw-r--r-- | drivers/mfd/intel-lpss.c | 3 | ||||
-rw-r--r-- | include/linux/clk-provider.h | 12 |
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, |