From 18138af79bc015ab7ef97e107c1f02cfe738ca47 Mon Sep 17 00:00:00 2001 From: Elaine Zhang Date: Wed, 2 Aug 2017 14:52:33 +0800 Subject: [PATCH] clk: fractional-divider: allow overriding of approximation Fractional dividers may have special requirements concerning numerator and denominator selection that differ from just getting the best approximation. For example on Rockchip socs the denominator must be at least 20 times larger than the numerator to generate precise clock frequencies. Therefore add the ability to provide custom approximation functions. Change-Id: I656a5851a3e2581b7f1b44bd67681ac00172873d Signed-off-by: Elaine Zhang --- drivers/clk/clk-fractional-divider.c | 28 ++++++++++++++++++++-------- include/linux/clk-provider.h | 5 +++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c index 1abcd76b4993..a8cb68d508a6 100644 --- a/drivers/clk/clk-fractional-divider.c +++ b/drivers/clk/clk-fractional-divider.c @@ -49,16 +49,12 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw, return ret; } -static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static void clk_fd_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; - unsigned long m, n; - u64 ret; - - if (!rate || rate >= *parent_rate) - return *parent_rate; /* * Get rate closer to *parent_rate to guarantee there is no overflow @@ -71,7 +67,23 @@ static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, rational_best_approximation(rate, *parent_rate, GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0), - &m, &n); + m, n); +} + +static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_fractional_divider *fd = to_clk_fd(hw); + unsigned long m, n; + u64 ret; + + if (!rate || rate >= *parent_rate) + return *parent_rate; + + if (fd->approximation) + fd->approximation(hw, rate, parent_rate, &m, &n); + else + clk_fd_general_approximation(hw, rate, parent_rate, &m, &n); ret = (u64)*parent_rate * m; do_div(ret, n); diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 0d2396e2a5b1..b7f247851363 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -523,6 +523,11 @@ struct clk_fractional_divider { u8 nwidth; u32 nmask; u8 flags; + void (*approximation)(struct clk_hw *hw, + unsigned long rate, + unsigned long *parent_rate, + unsigned long *m, + unsigned long *n); spinlock_t *lock; };