Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Elaine Zhang and committed by
Heiko Stuebner
5d890c2d ec52e462

+36
+36
drivers/clk/rockchip/clk.c
··· 29 29 #include <linux/mfd/syscon.h> 30 30 #include <linux/regmap.h> 31 31 #include <linux/reboot.h> 32 + #include <linux/rational.h> 32 33 #include "clk.h" 33 34 34 35 /** ··· 165 164 return notifier_from_errno(ret); 166 165 } 167 166 167 + /** 168 + * fractional divider must set that denominator is 20 times larger than 169 + * numerator to generate precise clock frequency. 170 + */ 171 + void rockchip_fractional_approximation(struct clk_hw *hw, 172 + unsigned long rate, unsigned long *parent_rate, 173 + unsigned long *m, unsigned long *n) 174 + { 175 + struct clk_fractional_divider *fd = to_clk_fd(hw); 176 + unsigned long p_rate, p_parent_rate; 177 + struct clk_hw *p_parent; 178 + unsigned long scale; 179 + 180 + p_rate = clk_hw_get_rate(clk_hw_get_parent(hw)); 181 + if ((rate * 20 > p_rate) && (p_rate % rate != 0)) { 182 + p_parent = clk_hw_get_parent(clk_hw_get_parent(hw)); 183 + p_parent_rate = clk_hw_get_rate(p_parent); 184 + *parent_rate = p_parent_rate; 185 + } 186 + 187 + /* 188 + * Get rate closer to *parent_rate to guarantee there is no overflow 189 + * for m and n. In the result it will be the nearest rate left shifted 190 + * by (scale - fd->nwidth) bits. 191 + */ 192 + scale = fls_long(*parent_rate / rate - 1); 193 + if (scale > fd->nwidth) 194 + rate <<= scale - fd->nwidth; 195 + 196 + rational_best_approximation(rate, *parent_rate, 197 + GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0), 198 + m, n); 199 + } 200 + 168 201 static struct clk *rockchip_clk_register_frac_branch( 169 202 struct rockchip_clk_provider *ctx, const char *name, 170 203 const char *const *parent_names, u8 num_parents, ··· 245 210 div->nwidth = 16; 246 211 div->nmask = GENMASK(div->nwidth - 1, 0) << div->nshift; 247 212 div->lock = lock; 213 + div->approximation = rockchip_fractional_approximation; 248 214 div_ops = &clk_fractional_divider_ops; 249 215 250 216 clk = clk_register_composite(NULL, name, parent_names, num_parents,