clk: imx: fracn-gppll: support integer pll
authorPeng Fan <peng.fan@nxp.com>
Mon, 3 Apr 2023 09:52:56 +0000 (17:52 +0800)
committerAbel Vesa <abel.vesa@linaro.org>
Sun, 9 Apr 2023 13:48:54 +0000 (16:48 +0300)
The fracn gppll could be configured in FRAC or INTEGER mode during
hardware design. The current driver only support FRAC mode, while
this patch introduces INTEGER support. When the PLL is INTEGER pll,
there is no mfn, mfd, the calculation is as below:
 Fvco_clk = (Fref / DIV[RDIV] ) * DIV[MFI]
 Fclko_odiv = Fvco_clk / DIV[ODIV]

In this patch, we reuse the FRAC pll logic with some condition check to
simplify the driver

Signed-off-by: Peng Fan <peng.fan@nxp.com>
Reviewed-by: Abel Vesa <abel.vesa@linaro.org>
Link: https://lore.kernel.org/r/20230403095300.3386988-4-peng.fan@oss.nxp.com
Signed-off-by: Abel Vesa <abel.vesa@linaro.org>
drivers/clk/imx/clk-fracn-gppll.c
drivers/clk/imx/clk.h

index f6674110a88e03ecc40b4033e2b2ef775833ec7a..e2633ad94640f0626b4444418ebe436f9fc6948e 100644 (file)
                .odiv   =       (_odiv),                        \
        }
 
+#define PLL_FRACN_GP_INTEGER(_rate, _mfi, _rdiv, _odiv)                \
+       {                                                       \
+               .rate   =       (_rate),                        \
+               .mfi    =       (_mfi),                         \
+               .mfn    =       0,                              \
+               .mfd    =       0,                              \
+               .rdiv   =       (_rdiv),                        \
+               .odiv   =       (_odiv),                        \
+       }
+
 struct clk_fracn_gppll {
        struct clk_hw                   hw;
        void __iomem                    *base;
        const struct imx_fracn_gppll_rate_table *rate_table;
        int rate_count;
+       u32 flags;
 };
 
 /*
@@ -83,6 +94,24 @@ struct imx_fracn_gppll_clk imx_fracn_gppll = {
 };
 EXPORT_SYMBOL_GPL(imx_fracn_gppll);
 
+/*
+ * Fvco = (Fref / rdiv) * MFI
+ * Fout = Fvco / odiv
+ * The (Fref / rdiv) should be in range 20MHz to 40MHz
+ * The Fvco should be in range 2.5Ghz to 5Ghz
+ */
+static const struct imx_fracn_gppll_rate_table int_tbl[] = {
+       PLL_FRACN_GP_INTEGER(1700000000U, 141, 1, 2),
+       PLL_FRACN_GP_INTEGER(1400000000U, 175, 1, 3),
+       PLL_FRACN_GP_INTEGER(900000000U, 150, 1, 4),
+};
+
+struct imx_fracn_gppll_clk imx_fracn_gppll_integer = {
+       .rate_table = int_tbl,
+       .rate_count = ARRAY_SIZE(int_tbl),
+};
+EXPORT_SYMBOL_GPL(imx_fracn_gppll_integer);
+
 static inline struct clk_fracn_gppll *to_clk_fracn_gppll(struct clk_hw *hw)
 {
        return container_of(hw, struct clk_fracn_gppll, hw);
@@ -169,9 +198,15 @@ static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned lon
                break;
        }
 
-       /* Fvco = Fref * (MFI + MFN / MFD) */
-       fvco = fvco * mfi * mfd + fvco * mfn;
-       do_div(fvco, mfd * rdiv * odiv);
+       if (pll->flags & CLK_FRACN_GPPLL_INTEGER) {
+               /* Fvco = (Fref / rdiv) * MFI */
+               fvco = fvco * mfi;
+               do_div(fvco, rdiv * odiv);
+       } else {
+               /* Fvco = (Fref / rdiv) * (MFI + MFN / MFD) */
+               fvco = fvco * mfi * mfd + fvco * mfn;
+               do_div(fvco, mfd * rdiv * odiv);
+       }
 
        return (unsigned long)fvco;
 }
@@ -215,8 +250,10 @@ static int clk_fracn_gppll_set_rate(struct clk_hw *hw, unsigned long drate,
        pll_div = FIELD_PREP(PLL_RDIV_MASK, rate->rdiv) | rate->odiv |
                FIELD_PREP(PLL_MFI_MASK, rate->mfi);
        writel_relaxed(pll_div, pll->base + PLL_DIV);
-       writel_relaxed(rate->mfd, pll->base + PLL_DENOMINATOR);
-       writel_relaxed(FIELD_PREP(PLL_MFN_MASK, rate->mfn), pll->base + PLL_NUMERATOR);
+       if (pll->flags & CLK_FRACN_GPPLL_FRACN) {
+               writel_relaxed(rate->mfd, pll->base + PLL_DENOMINATOR);
+               writel_relaxed(FIELD_PREP(PLL_MFN_MASK, rate->mfn), pll->base + PLL_NUMERATOR);
+       }
 
        /* Wait for 5us according to fracn mode pll doc */
        udelay(5);
@@ -300,8 +337,10 @@ static const struct clk_ops clk_fracn_gppll_ops = {
        .set_rate       = clk_fracn_gppll_set_rate,
 };
 
-struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base,
-                                  const struct imx_fracn_gppll_clk *pll_clk)
+static struct clk_hw *_imx_clk_fracn_gppll(const char *name, const char *parent_name,
+                                          void __iomem *base,
+                                          const struct imx_fracn_gppll_clk *pll_clk,
+                                          u32 pll_flags)
 {
        struct clk_fracn_gppll *pll;
        struct clk_hw *hw;
@@ -322,6 +361,7 @@ struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, vo
        pll->hw.init = &init;
        pll->rate_table = pll_clk->rate_table;
        pll->rate_count = pll_clk->rate_count;
+       pll->flags = pll_flags;
 
        hw = &pll->hw;
 
@@ -334,4 +374,18 @@ struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, vo
 
        return hw;
 }
+
+struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base,
+                                  const struct imx_fracn_gppll_clk *pll_clk)
+{
+       return _imx_clk_fracn_gppll(name, parent_name, base, pll_clk, CLK_FRACN_GPPLL_FRACN);
+}
 EXPORT_SYMBOL_GPL(imx_clk_fracn_gppll);
+
+struct clk_hw *imx_clk_fracn_gppll_integer(const char *name, const char *parent_name,
+                                          void __iomem *base,
+                                          const struct imx_fracn_gppll_clk *pll_clk)
+{
+       return _imx_clk_fracn_gppll(name, parent_name, base, pll_clk, CLK_FRACN_GPPLL_INTEGER);
+}
+EXPORT_SYMBOL_GPL(imx_clk_fracn_gppll_integer);
index ecce45b0dd70dc0ed2d83383d21434d45922eb34..c8db0c8d4c5dab56dce56d163514870709400197 100644 (file)
@@ -73,6 +73,9 @@ extern struct imx_pll14xx_clk imx_1416x_pll;
 extern struct imx_pll14xx_clk imx_1443x_pll;
 extern struct imx_pll14xx_clk imx_1443x_dram_pll;
 
+#define CLK_FRACN_GPPLL_INTEGER        BIT(0)
+#define CLK_FRACN_GPPLL_FRACN  BIT(1)
+
 /* NOTE: Rate table should be kept sorted in descending order. */
 struct imx_fracn_gppll_rate_table {
        unsigned int rate;
@@ -91,8 +94,12 @@ struct imx_fracn_gppll_clk {
 
 struct clk_hw *imx_clk_fracn_gppll(const char *name, const char *parent_name, void __iomem *base,
                                   const struct imx_fracn_gppll_clk *pll_clk);
+struct clk_hw *imx_clk_fracn_gppll_integer(const char *name, const char *parent_name,
+                                          void __iomem *base,
+                                          const struct imx_fracn_gppll_clk *pll_clk);
 
 extern struct imx_fracn_gppll_clk imx_fracn_gppll;
+extern struct imx_fracn_gppll_clk imx_fracn_gppll_integer;
 
 #define imx_clk_cpu(name, parent_name, div, mux, pll, step) \
        to_clk(imx_clk_hw_cpu(name, parent_name, div, mux, pll, step))