clk: fractional-divider: allow overriding of approximation
authorElaine Zhang <zhangqing@rock-chips.com>
Tue, 1 Aug 2017 16:21:22 +0000 (18:21 +0200)
committerHeiko Stuebner <heiko@sntech.de>
Tue, 8 Aug 2017 15:39:48 +0000 (17:39 +0200)
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.

Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
Acked-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
drivers/clk/clk-fractional-divider.c
include/linux/clk-provider.h

index aab904618eb636f9f926a6b7b46d7b03b780b10c..fdf625fb10faa03fc8c394555612fdcdf855ac09 100644 (file)
@@ -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);
index c59c62571e4fe6a8c3ec5a9f48759b84c67e6a4a..1fc113fbf955b4f84d4f619827f19aeadd280640 100644 (file)
@@ -565,6 +565,9 @@ 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;
 };