clk: axi-clkgen: Correctly handle nocount bit in recalc_rate()
authorLars-Peter Clausen <lars@metafoo.de>
Tue, 5 Sep 2017 09:32:40 +0000 (11:32 +0200)
committerStephen Boyd <sboyd@codeaurora.org>
Fri, 22 Dec 2017 02:07:11 +0000 (18:07 -0800)
If the nocount bit is set the divider is bypassed and the settings for the
divider count should be ignored and a divider value of 1 should be assumed.
Handle this correctly in the driver recalc_rate() callback.

While the driver sets up the part so that the read back dividers values
yield the correct result the power-on reset settings of the part might not
reflect this and hence calling e.g. clk_get_rate() without prior calls to
clk_set_rate() will yield the wrong result.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
drivers/clk/clk-axi-clkgen.c

index 5e918e7afaba8d82b2f823c3c07602f8fb68f0d7..95a6e98343920a5f08137f46bbaf9583af846c49 100644 (file)
 #define MMCM_REG_FILTER1       0x4e
 #define MMCM_REG_FILTER2       0x4f
 
+#define MMCM_CLKOUT_NOCOUNT    BIT(6)
+
+#define MMCM_CLK_DIV_NOCOUNT   BIT(12)
+
 struct axi_clkgen {
        void __iomem *base;
        struct clk_hw clk_hw;
@@ -315,12 +319,27 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
        unsigned int reg;
        unsigned long long tmp;
 
-       axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, &reg);
-       dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+       axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_2, &reg);
+       if (reg & MMCM_CLKOUT_NOCOUNT) {
+               dout = 1;
+       } else {
+               axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLKOUT0_1, &reg);
+               dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+       }
+
        axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &reg);
-       d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
-       axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, &reg);
-       m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+       if (reg & MMCM_CLK_DIV_NOCOUNT)
+               d = 1;
+       else
+               d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+
+       axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB2, &reg);
+       if (reg & MMCM_CLKOUT_NOCOUNT) {
+               m = 1;
+       } else {
+               axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_FB1, &reg);
+               m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
+       }
 
        if (d == 0 || dout == 0)
                return 0;