Message ID | 3942563.TMEkxTS0ar@phil (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 08/02/2017 12:22 AM, Heiko Stuebner wrote: > From: Elaine Zhang <zhangqing@rock-chips.com> > >>From Rockchips fractional divider description: > 3.1.9 Fractional divider usage > To get specific frequency, clocks of I2S, SPDIF, UARTcan be generated by > fractional divider. Generally you must set that denominator is 20 times > larger than numerator to generate precise clock frequency. So the > fractional divider applies only to generate low frequency clock like > I2S, UART. > > Therefore add a special approximation function that handles this > special requirement. > > Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com> > --- > drivers/clk/rockchip/clk.c | 36 ++++++++++++++++++++++++++++++++++++ > 1 file changed, 36 insertions(+) > > diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c > index fe1d393cf678..b6db79a00602 100644 > --- a/drivers/clk/rockchip/clk.c > +++ b/drivers/clk/rockchip/clk.c > @@ -29,6 +29,7 @@ > #include <linux/mfd/syscon.h> > #include <linux/regmap.h> > #include <linux/reboot.h> > +#include <linux/rational.h> > #include "clk.h" > > /** > @@ -164,6 +165,40 @@ static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb, > return notifier_from_errno(ret); > } > > +/** > + * fractional divider must set that denominator is 20 times larger than > + * numerator to generate precise clock frequency. > + */ > +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)) { > + p_parent = clk_hw_get_parent(clk_hw_get_parent(hw)); > + p_parent_rate = clk_hw_get_rate(p_parent); > + *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); > +} > + > static struct clk *rockchip_clk_register_frac_branch( > struct rockchip_clk_provider *ctx, const char *name, > const char *const *parent_names, u8 num_parents, > @@ -210,6 +245,7 @@ static struct clk *rockchip_clk_register_frac_branch( > div->nwidth = 16; > div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift; > div->lock = lock; > + div->approximation = rockchip_fractional_approximation; > div_ops = &clk_fractional_divider_ops; > > clk = clk_register_composite(NULL, name, parent_names, num_parents, > Tested-by: Elaine Zhang <zhangqing@rock-chips.com>
Am Dienstag, 1. August 2017, 18:22:24 CEST schrieb Heiko Stuebner: > From: Elaine Zhang <zhangqing@rock-chips.com> > > From Rockchips fractional divider description: > 3.1.9 Fractional divider usage > To get specific frequency, clocks of I2S, SPDIF, UARTcan be generated by > fractional divider. Generally you must set that denominator is 20 times > larger than numerator to generate precise clock frequency. So the > fractional divider applies only to generate low frequency clock like > I2S, UART. > > Therefore add a special approximation function that handles this > special requirement. > > Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com> applied for 4.14 Heiko
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index fe1d393cf678..b6db79a00602 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -29,6 +29,7 @@ #include <linux/mfd/syscon.h> #include <linux/regmap.h> #include <linux/reboot.h> +#include <linux/rational.h> #include "clk.h" /** @@ -164,6 +165,40 @@ static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb, return notifier_from_errno(ret); } +/** + * fractional divider must set that denominator is 20 times larger than + * numerator to generate precise clock frequency. + */ +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)) { + p_parent = clk_hw_get_parent(clk_hw_get_parent(hw)); + p_parent_rate = clk_hw_get_rate(p_parent); + *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); +} + static struct clk *rockchip_clk_register_frac_branch( struct rockchip_clk_provider *ctx, const char *name, const char *const *parent_names, u8 num_parents, @@ -210,6 +245,7 @@ static struct clk *rockchip_clk_register_frac_branch( div->nwidth = 16; div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift; div->lock = lock; + div->approximation = rockchip_fractional_approximation; div_ops = &clk_fractional_divider_ops; clk = clk_register_composite(NULL, name, parent_names, num_parents,