]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
clk: thead: th1520-ap: Support setting PLL rates
authorYao Zi <ziyao@disroot.org>
Thu, 20 Nov 2025 13:14:13 +0000 (13:14 +0000)
committerDrew Fustini <fustini@kernel.org>
Thu, 18 Dec 2025 19:15:35 +0000 (11:15 -0800)
TH1520 ships several PLLs that could operate in either integer or
fractional mode. However, the TRM only lists a few configuration whose
stability is considered guaranteed.

Add a table-lookup rate determination logic to support PLL rate setting,
and fill up frequency-configuration tables for AP-subsystem PLLs.

Reviewed-by: Drew Fustini <fustini@kernel.org>
Signed-off-by: Yao Zi <ziyao@disroot.org>
Signed-off-by: Drew Fustini <fustini@kernel.org>
drivers/clk/thead/clk-th1520-ap.c

index b820d47387bb94a73699fd1545fafeb50ac2c708..bf8e80c39a9ed21e526f5fc48234538e473d4bbf 100644 (file)
@@ -22,6 +22,7 @@
 #define TH1520_PLL_REFDIV      GENMASK(5, 0)
 #define TH1520_PLL_BYPASS      BIT(30)
 #define TH1520_PLL_VCO_RST     BIT(29)
+#define TH1520_PLL_DACPD       BIT(25)
 #define TH1520_PLL_DSMPD       BIT(24)
 #define TH1520_PLL_FRAC                GENMASK(23, 0)
 #define TH1520_PLL_FRAC_BITS    24
@@ -72,9 +73,19 @@ struct ccu_div {
        struct ccu_common       common;
 };
 
+struct ccu_pll_cfg {
+       unsigned long           freq;
+       u32                     fbdiv;
+       u32                     frac;
+       u32                     postdiv1;
+       u32                     postdiv2;
+};
+
 struct ccu_pll {
        struct ccu_common       common;
        u32                     lock_sts_mask;
+       int                     cfgnum;
+       const struct ccu_pll_cfg *cfgs;
 };
 
 #define TH_CCU_ARG(_shift, _width)                                     \
@@ -391,17 +402,102 @@ static unsigned long ccu_pll_recalc_rate(struct clk_hw *hw,
        return rate;
 }
 
+static const struct ccu_pll_cfg *ccu_pll_lookup_best_cfg(struct ccu_pll *pll,
+                                                        unsigned long rate)
+{
+       unsigned long best_delta = ULONG_MAX;
+       const struct ccu_pll_cfg *best_cfg;
+       int i;
+
+       for (i = 0; i < pll->cfgnum; i++) {
+               const struct ccu_pll_cfg *cfg = &pll->cfgs[i];
+               unsigned long delta;
+
+               delta = abs_diff(cfg->freq, rate);
+               if (delta < best_delta) {
+                       best_delta      = delta;
+                       best_cfg        = cfg;
+               }
+       }
+
+       return best_cfg;
+}
+
+static int ccu_pll_determine_rate(struct clk_hw *hw,
+                                 struct clk_rate_request *req)
+{
+       struct ccu_pll *pll = hw_to_ccu_pll(hw);
+
+       req->rate = ccu_pll_lookup_best_cfg(pll, req->rate)->freq;
+
+       return 0;
+}
+
+static int ccu_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+                           unsigned long parent_rate)
+{
+       struct ccu_pll *pll = hw_to_ccu_pll(hw);
+       const struct ccu_pll_cfg *cfg;
+
+       cfg = ccu_pll_lookup_best_cfg(pll, rate);
+
+       ccu_pll_disable(hw);
+
+       regmap_write(pll->common.map, pll->common.cfg0,
+                    FIELD_PREP(TH1520_PLL_REFDIV,      1)              |
+                    FIELD_PREP(TH1520_PLL_FBDIV,       cfg->fbdiv)     |
+                    FIELD_PREP(TH1520_PLL_POSTDIV1,    cfg->postdiv1)  |
+                    FIELD_PREP(TH1520_PLL_POSTDIV2,    cfg->postdiv2));
+
+       regmap_update_bits(pll->common.map, pll->common.cfg1,
+                          TH1520_PLL_DACPD | TH1520_PLL_DSMPD |
+                          TH1520_PLL_FRAC,
+                          cfg->frac ? cfg->frac :
+                               TH1520_PLL_DACPD | TH1520_PLL_DSMPD);
+
+       return ccu_pll_enable(hw);
+}
+
 static const struct clk_ops clk_pll_ops = {
        .disable        = ccu_pll_disable,
        .enable         = ccu_pll_enable,
        .is_enabled     = ccu_pll_is_enabled,
        .recalc_rate    = ccu_pll_recalc_rate,
+       .determine_rate = ccu_pll_determine_rate,
+       .set_rate       = ccu_pll_set_rate,
 };
 
 static const struct clk_parent_data osc_24m_clk[] = {
        { .index = 0 }
 };
 
+static const struct ccu_pll_cfg cpu_pll_cfgs[] = {
+       { 125000000,    125,    0, 6, 4 },
+       { 200000000,    125,    0, 5, 3 },
+       { 300000000,    125,    0, 5, 2 },
+       { 400000000,    100,    0, 3, 2 },
+       { 500000000,    125,    0, 6, 1 },
+       { 600000000,    125,    0, 5, 1 },
+       { 702000000,    117,    0, 4, 1 },
+       { 800000000,    100,    0, 3, 1 },
+       { 900000000,    75,     0, 2, 1 },
+       { 1000000000,   125,    0, 3, 1 },
+       { 1104000000,   92,     0, 2, 1 },
+       { 1200000000,   100,    0, 2, 1 },
+       { 1296000000,   108,    0, 2, 1 },
+       { 1404000000,   117,    0, 2, 1 },
+       { 1500000000,   125,    0, 2, 1 },
+       { 1608000000,   67,     0, 1, 1 },
+       { 1704000000,   71,     0, 1, 1 },
+       { 1800000000,   75,     0, 1, 1 },
+       { 1896000000,   79,     0, 1, 1 },
+       { 1992000000,   83,     0, 1, 1 },
+       { 2112000000,   88,     0, 1, 1 },
+       { 2208000000,   92,     0, 1, 1 },
+       { 2304000000,   96,     0, 1, 1 },
+       { 2400000000,   100,    0, 1, 1 },
+};
+
 static struct ccu_pll cpu_pll0_clk = {
        .common         = {
                .clkid          = CLK_CPU_PLL0,
@@ -413,6 +509,8 @@ static struct ccu_pll cpu_pll0_clk = {
                                              CLK_IS_CRITICAL),
        },
        .lock_sts_mask          = BIT(1),
+       .cfgnum                 = ARRAY_SIZE(cpu_pll_cfgs),
+       .cfgs                   = cpu_pll_cfgs,
 };
 
 static struct ccu_pll cpu_pll1_clk = {
@@ -426,6 +524,16 @@ static struct ccu_pll cpu_pll1_clk = {
                                              CLK_IS_CRITICAL),
        },
        .lock_sts_mask          = BIT(4),
+       .cfgnum                 = ARRAY_SIZE(cpu_pll_cfgs),
+       .cfgs                   = cpu_pll_cfgs,
+};
+
+static const struct ccu_pll_cfg gmac_pll_cfg = {
+       .freq           = 1000000000,
+       .fbdiv          = 125,
+       .frac           = 0,
+       .postdiv1       = 3,
+       .postdiv2       = 1,
 };
 
 static struct ccu_pll gmac_pll_clk = {
@@ -439,6 +547,8 @@ static struct ccu_pll gmac_pll_clk = {
                                              CLK_IS_CRITICAL),
        },
        .lock_sts_mask          = BIT(3),
+       .cfgnum                 = 1,
+       .cfgs                   = &gmac_pll_cfg,
 };
 
 static const struct clk_hw *gmac_pll_clk_parent[] = {
@@ -449,6 +559,14 @@ static const struct clk_parent_data gmac_pll_clk_pd[] = {
        { .hw = &gmac_pll_clk.common.hw }
 };
 
+static const struct ccu_pll_cfg video_pll_cfg = {
+       .freq           = 792000000,
+       .fbdiv          = 99,
+       .frac           = 0,
+       .postdiv1       = 3,
+       .postdiv2       = 1,
+};
+
 static struct ccu_pll video_pll_clk = {
        .common         = {
                .clkid          = CLK_VIDEO_PLL,
@@ -460,6 +578,8 @@ static struct ccu_pll video_pll_clk = {
                                              CLK_IS_CRITICAL),
        },
        .lock_sts_mask          = BIT(7),
+       .cfgnum                 = 1,
+       .cfgs                   = &video_pll_cfg,
 };
 
 static const struct clk_hw *video_pll_clk_parent[] = {
@@ -470,6 +590,14 @@ static const struct clk_parent_data video_pll_clk_pd[] = {
        { .hw = &video_pll_clk.common.hw }
 };
 
+static const struct ccu_pll_cfg dpu_pll_cfg = {
+       .freq           = 1188000000,
+       .fbdiv          = 99,
+       .frac           = 0,
+       .postdiv1       = 2,
+       .postdiv2       = 1,
+};
+
 static struct ccu_pll dpu0_pll_clk = {
        .common         = {
                .clkid          = CLK_DPU0_PLL,
@@ -481,6 +609,8 @@ static struct ccu_pll dpu0_pll_clk = {
                                              0),
        },
        .lock_sts_mask          = BIT(8),
+       .cfgnum                 = 1,
+       .cfgs                   = &dpu_pll_cfg,
 };
 
 static const struct clk_hw *dpu0_pll_clk_parent[] = {
@@ -498,12 +628,22 @@ static struct ccu_pll dpu1_pll_clk = {
                                              0),
        },
        .lock_sts_mask          = BIT(9),
+       .cfgnum                 = 1,
+       .cfgs                   = &dpu_pll_cfg,
 };
 
 static const struct clk_hw *dpu1_pll_clk_parent[] = {
        &dpu1_pll_clk.common.hw
 };
 
+static const struct ccu_pll_cfg tee_pll_cfg = {
+       .freq           = 792000000,
+       .fbdiv          = 99,
+       .frac           = 0,
+       .postdiv1       = 3,
+       .postdiv2       = 1,
+};
+
 static struct ccu_pll tee_pll_clk = {
        .common         = {
                .clkid          = CLK_TEE_PLL,
@@ -515,6 +655,8 @@ static struct ccu_pll tee_pll_clk = {
                                              CLK_IS_CRITICAL),
        },
        .lock_sts_mask          = BIT(10),
+       .cfgnum                 = 1,
+       .cfgs                   = &tee_pll_cfg,
 };
 
 static const struct clk_parent_data c910_i0_parents[] = {