]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
clk: ingenic: Fix round_rate misbehaving with non-integer dividers
authorPaul Cercueil <paul@crapouillou.net>
Mon, 28 Jan 2019 02:09:20 +0000 (23:09 -0300)
committerKleber Sacilotto de Souza <kleber.souza@canonical.com>
Wed, 14 Aug 2019 09:18:49 +0000 (11:18 +0200)
BugLink: https://bugs.launchpad.net/bugs/1837952
commit bc5d922c93491878c44c9216e9d227c7eeb81d7f upstream.

Take a parent rate of 180 MHz, and a requested rate of 4.285715 MHz.
This results in a theorical divider of 41.999993 which is then rounded
up to 42. The .round_rate function would then return (180 MHz / 42) as
the clock, rounded down, so 4.285714 MHz.

Calling clk_set_rate on 4.285714 MHz would round the rate again, and
give a theorical divider of 42,0000028, now rounded up to 43, and the
rate returned would be (180 MHz / 43) which is 4.186046 MHz, aka. not
what we requested.

Fix this by rounding up the divisions.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Maarten ter Huurne <maarten@treewalker.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Kamal Mostafa <kamal@canonical.com>
Signed-off-by: Khalid Elmously <khalid.elmously@canonical.com>
drivers/clk/ingenic/cgu.c

index ab393637f7b0f80791657fa88f458df9b3e7b609..a6b4b90ff227766d31c44ee0d7b5f008d95a3ac4 100644 (file)
@@ -364,16 +364,16 @@ ingenic_clk_round_rate(struct clk_hw *hw, unsigned long req_rate,
        struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
        struct ingenic_cgu *cgu = ingenic_clk->cgu;
        const struct ingenic_cgu_clk_info *clk_info;
-       long rate = *parent_rate;
+       unsigned int div = 1;
 
        clk_info = &cgu->clock_info[ingenic_clk->idx];
 
        if (clk_info->type & CGU_CLK_DIV)
-               rate /= ingenic_clk_calc_div(clk_info, *parent_rate, req_rate);
+               div = ingenic_clk_calc_div(clk_info, *parent_rate, req_rate);
        else if (clk_info->type & CGU_CLK_FIXDIV)
-               rate /= clk_info->fixdiv.div;
+               div = clk_info->fixdiv.div;
 
-       return rate;
+       return DIV_ROUND_UP(*parent_rate, div);
 }
 
 static int
@@ -393,7 +393,7 @@ ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate,
 
        if (clk_info->type & CGU_CLK_DIV) {
                div = ingenic_clk_calc_div(clk_info, parent_rate, req_rate);
-               rate = parent_rate / div;
+               rate = DIV_ROUND_UP(parent_rate, div);
 
                if (rate != req_rate)
                        return -EINVAL;