aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElaine Zhang <zhangqing@rock-chips.com>2017-08-01 12:22:24 -0400
committerHeiko Stuebner <heiko@sntech.de>2017-08-08 11:45:42 -0400
commit5d890c2df900db0197d46ec75383d7633ef41c82 (patch)
tree2e9e50736f9d15cddd389b1f968655651d9a15db
parentec52e462564b9c5bfdf1f79638c537c7103e1d2b (diff)
clk: rockchip: add special approximation to fix up fractional clk's jitter
>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> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
-rw-r--r--drivers/clk/rockchip/clk.c36
1 files changed, 36 insertions, 0 deletions
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 @@
29#include <linux/mfd/syscon.h> 29#include <linux/mfd/syscon.h>
30#include <linux/regmap.h> 30#include <linux/regmap.h>
31#include <linux/reboot.h> 31#include <linux/reboot.h>
32#include <linux/rational.h>
32#include "clk.h" 33#include "clk.h"
33 34
34/** 35/**
@@ -164,6 +165,40 @@ static int rockchip_clk_frac_notifier_cb(struct notifier_block *nb,
164 return notifier_from_errno(ret); 165 return notifier_from_errno(ret);
165} 166}
166 167
168/**
169 * fractional divider must set that denominator is 20 times larger than
170 * numerator to generate precise clock frequency.
171 */
172void rockchip_fractional_approximation(struct clk_hw *hw,
173 unsigned long rate, unsigned long *parent_rate,
174 unsigned long *m, unsigned long *n)
175{
176 struct clk_fractional_divider *fd = to_clk_fd(hw);
177 unsigned long p_rate, p_parent_rate;
178 struct clk_hw *p_parent;
179 unsigned long scale;
180
181 p_rate = clk_hw_get_rate(clk_hw_get_parent(hw));
182 if ((rate * 20 > p_rate) && (p_rate % rate != 0)) {
183 p_parent = clk_hw_get_parent(clk_hw_get_parent(hw));
184 p_parent_rate = clk_hw_get_rate(p_parent);
185 *parent_rate = p_parent_rate;
186 }
187
188 /*
189 * Get rate closer to *parent_rate to guarantee there is no overflow
190 * for m and n. In the result it will be the nearest rate left shifted
191 * by (scale - fd->nwidth) bits.
192 */
193 scale = fls_long(*parent_rate / rate - 1);
194 if (scale > fd->nwidth)
195 rate <<= scale - fd->nwidth;
196
197 rational_best_approximation(rate, *parent_rate,
198 GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0),
199 m, n);
200}
201
167static struct clk *rockchip_clk_register_frac_branch( 202static struct clk *rockchip_clk_register_frac_branch(
168 struct rockchip_clk_provider *ctx, const char *name, 203 struct rockchip_clk_provider *ctx, const char *name,
169 const char *const *parent_names, u8 num_parents, 204 const char *const *parent_names, u8 num_parents,
@@ -210,6 +245,7 @@ static struct clk *rockchip_clk_register_frac_branch(
210 div->nwidth = 16; 245 div->nwidth = 16;
211 div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift; 246 div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift;
212 div->lock = lock; 247 div->lock = lock;
248 div->approximation = rockchip_fractional_approximation;
213 div_ops = &clk_fractional_divider_ops; 249 div_ops = &clk_fractional_divider_ops;
214 250
215 clk = clk_register_composite(NULL, name, parent_names, num_parents, 251 clk = clk_register_composite(NULL, name, parent_names, num_parents,