]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
clk: spacemit: ccu_pll: add plla type clock
authorYixun Lan <dlan@gentoo.org>
Mon, 27 Oct 2025 13:41:24 +0000 (21:41 +0800)
committerYixun Lan <dlan@gentoo.org>
Fri, 9 Jan 2026 02:29:10 +0000 (10:29 +0800)
Introduce a new clock PLLA for SpacemiT's K3 SoC which has a different
register layout comparing to previous PPL type. And, It is configured
by swcr1, swcr3 and swcr2 BIT[15:8].

Link: https://lore.kernel.org/r/20260108-k3-clk-v5-3-42a11b74ad58@gentoo.org
Signed-off-by: Yixun Lan <dlan@gentoo.org>
drivers/clk/spacemit/ccu_common.h
drivers/clk/spacemit/ccu_pll.c
drivers/clk/spacemit/ccu_pll.h

index 7ae244b5eaceadba5b5e91a5b3ab5f259231e78a..8691698e007d976a2ad5122a255a88649b3d02f8 100644 (file)
@@ -26,6 +26,7 @@ struct ccu_common {
                /* For PLL */
                struct {
                        u32 reg_swcr1;
+                       u32 reg_swcr2;
                        u32 reg_swcr3;
                };
        };
index 76d0244873d87c320d5e9675c0a440a421479a76..d4066a0ed452634eb572a5c84eeab901ed14faf4 100644 (file)
@@ -17,6 +17,9 @@
 #define PLL_SWCR3_EN           ((u32)BIT(31))
 #define PLL_SWCR3_MASK         GENMASK(30, 0)
 
+#define PLLA_SWCR2_EN          ((u32)BIT(16))
+#define PLLA_SWCR2_MASK                GENMASK(15, 8)
+
 static const struct ccu_pll_rate_tbl *ccu_pll_lookup_best_rate(struct ccu_pll *pll,
                                                               unsigned long rate)
 {
@@ -148,6 +151,110 @@ static int ccu_pll_init(struct clk_hw *hw)
        return 0;
 }
 
+static const struct ccu_pll_rate_tbl *ccu_plla_lookup_matched_entry(struct ccu_pll *pll)
+{
+       struct ccu_pll_config *config = &pll->config;
+       const struct ccu_pll_rate_tbl *entry;
+       u32 i, swcr1, swcr2, swcr3;
+
+       swcr1 = ccu_read(&pll->common, swcr1);
+       swcr2 = ccu_read(&pll->common, swcr2);
+       swcr2 &= PLLA_SWCR2_MASK;
+       swcr3 = ccu_read(&pll->common, swcr3);
+
+       for (i = 0; i < config->tbl_num; i++) {
+               entry = &config->rate_tbl[i];
+
+               if (swcr1 == entry->swcr1 &&
+                   swcr2 == entry->swcr2 &&
+                   swcr3 == entry->swcr3)
+                       return entry;
+       }
+
+       return NULL;
+}
+
+static void ccu_plla_update_param(struct ccu_pll *pll, const struct ccu_pll_rate_tbl *entry)
+{
+       struct ccu_common *common = &pll->common;
+
+       regmap_write(common->regmap, common->reg_swcr1, entry->swcr1);
+       regmap_write(common->regmap, common->reg_swcr3, entry->swcr3);
+       ccu_update(common, swcr2, PLLA_SWCR2_MASK, entry->swcr2);
+}
+
+static int ccu_plla_is_enabled(struct clk_hw *hw)
+{
+       struct ccu_common *common = hw_to_ccu_common(hw);
+
+       return ccu_read(common, swcr2) & PLLA_SWCR2_EN;
+}
+
+static int ccu_plla_enable(struct clk_hw *hw)
+{
+       struct ccu_pll *pll = hw_to_ccu_pll(hw);
+       struct ccu_common *common = &pll->common;
+       unsigned int tmp;
+
+       ccu_update(common, swcr2, PLLA_SWCR2_EN, PLLA_SWCR2_EN);
+
+       /* check lock status */
+       return regmap_read_poll_timeout_atomic(common->lock_regmap,
+                                              pll->config.reg_lock,
+                                              tmp,
+                                              tmp & pll->config.mask_lock,
+                                              PLL_DELAY_US, PLL_TIMEOUT_US);
+}
+
+static void ccu_plla_disable(struct clk_hw *hw)
+{
+       struct ccu_common *common = hw_to_ccu_common(hw);
+
+       ccu_update(common, swcr2, PLLA_SWCR2_EN, 0);
+}
+
+/*
+ * PLLAs must be gated before changing rate, which is ensured by
+ * flag CLK_SET_RATE_GATE.
+ */
+static int ccu_plla_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_rate_tbl *entry;
+
+       entry = ccu_pll_lookup_best_rate(pll, rate);
+       ccu_plla_update_param(pll, entry);
+
+       return 0;
+}
+
+static unsigned long ccu_plla_recalc_rate(struct clk_hw *hw,
+                                         unsigned long parent_rate)
+{
+       struct ccu_pll *pll = hw_to_ccu_pll(hw);
+       const struct ccu_pll_rate_tbl *entry;
+
+       entry = ccu_plla_lookup_matched_entry(pll);
+
+       WARN_ON_ONCE(!entry);
+
+       return entry ? entry->rate : 0;
+}
+
+static int ccu_plla_init(struct clk_hw *hw)
+{
+       struct ccu_pll *pll = hw_to_ccu_pll(hw);
+
+       if (ccu_plla_lookup_matched_entry(pll))
+               return 0;
+
+       ccu_plla_disable(hw);
+       ccu_plla_update_param(pll, &pll->config.rate_tbl[0]);
+
+       return 0;
+}
+
 const struct clk_ops spacemit_ccu_pll_ops = {
        .init           = ccu_pll_init,
        .enable         = ccu_pll_enable,
@@ -158,3 +265,14 @@ const struct clk_ops spacemit_ccu_pll_ops = {
        .is_enabled     = ccu_pll_is_enabled,
 };
 EXPORT_SYMBOL_NS_GPL(spacemit_ccu_pll_ops, "CLK_SPACEMIT");
+
+const struct clk_ops spacemit_ccu_plla_ops = {
+       .init           = ccu_plla_init,
+       .enable         = ccu_plla_enable,
+       .disable        = ccu_plla_disable,
+       .set_rate       = ccu_plla_set_rate,
+       .recalc_rate    = ccu_plla_recalc_rate,
+       .determine_rate = ccu_pll_determine_rate,
+       .is_enabled     = ccu_plla_is_enabled,
+};
+EXPORT_SYMBOL_NS_GPL(spacemit_ccu_plla_ops, "CLK_SPACEMIT");
index 0592f4c3068c718f3383636aafd1a42731793ba0..e41db5c97c1a815fa55e917d4339a1a038b08c03 100644 (file)
  * configuration.
  *
  * @rate:      PLL rate
- * @swcr1:     Register value of PLLX_SW1_CTRL (PLLx_SWCR1).
- * @swcr3:     Register value of the PLLx_SW3_CTRL's lowest 31 bits of
- *             PLLx_SW3_CTRL (PLLx_SWCR3). This highest bit is for enabling
- *             the PLL and not contained in this field.
+ * @swcr1:     Value of register PLLx_SW1_CTRL.
+ * @swcr2:     Value of register PLLAx_SW2_CTRL.
+ * @swcr3:     value of register PLLx_SW3_CTRL.
+ *
+ * See below tables for the register used in PPL/PPLA clocks
+ *
+ * Regular PLL type
+ *  | Enable | swcr3 | PLLx_SW3_CTRL - BIT[31]    |
+ *  -----------------------------------------------
+ *  | Config | swcr1 | PLLx_SW1_CTRL - BIT[31:0]  |
+ *  |        | swcr2 | Not used                   |
+ *  |        | swcr3 | PLLx_SW3_CTRL - BIT[30:0]  |
+ *
+ * Special PLL type A
+ *  | Enable | swcr2 | PLLAx_SW2_CTRL - BIT[16]   |
+ *  -----------------------------------------------
+ *  | Config | swcr1 | PLLAx_SW1_CTRL - BIT[31:0] |
+ *  |        | swcr2 | PLLAx_SW2_CTRL - BIT[15:8] |
+ *  |        | swcr3 | PLLAx_SW3_CTRL - BIT[31:0] |
+ *
  */
 struct ccu_pll_rate_tbl {
        unsigned long rate;
        u32 swcr1;
+       u32 swcr2;
        u32 swcr3;
 };
 
@@ -36,11 +53,19 @@ struct ccu_pll_config {
 
 #define CCU_PLL_RATE(_rate, _swcr1, _swcr3) \
        {                                                                       \
-               .rate   = _rate,                                                        \
+               .rate   = _rate,                                                \
                .swcr1  = _swcr1,                                               \
                .swcr3  = _swcr3,                                               \
        }
 
+#define CCU_PLLA_RATE(_rate, _swcr1, _swcr2, _swcr3) \
+       {                                                                       \
+               .rate   = _rate,                                                \
+               .swcr1  = _swcr1,                                               \
+               .swcr2  = _swcr2,                                               \
+               .swcr3  = _swcr3,                                               \
+       }
+
 struct ccu_pll {
        struct ccu_common       common;
        struct ccu_pll_config   config;
@@ -54,26 +79,37 @@ struct ccu_pll {
                .mask_lock      = (_mask_lock),                                 \
        }
 
-#define CCU_PLL_HWINIT(_name, _flags)                                          \
+#define CCU_PLL_COMMON_HWINIT(_name, _ops, _flags)                             \
        (&(struct clk_init_data) {                                              \
                .name           = #_name,                                       \
-               .ops            = &spacemit_ccu_pll_ops,                        \
+               .ops            = _ops,                                         \
                .parent_data    = &(struct clk_parent_data) { .index = 0 },     \
                .num_parents    = 1,                                            \
                .flags          = _flags,                                       \
        })
 
-#define CCU_PLL_DEFINE(_name, _table, _reg_swcr1, _reg_swcr3, _reg_lock,       \
-                      _mask_lock, _flags)                                      \
+#define CCU_PLL_X_DEFINE(_name, _table, _reg_swcr1, _reg_swcr2, _reg_swcr3,    \
+                      _reg_lock, _mask_lock, _ops, _flags)                     \
 static struct ccu_pll _name = {                                                        \
        .config = CCU_PLL_CONFIG(_table, _reg_lock, _mask_lock),                \
        .common = {                                                             \
                .reg_swcr1      = _reg_swcr1,                                   \
+               .reg_swcr2      = _reg_swcr2,                                   \
                .reg_swcr3      = _reg_swcr3,                                   \
-               .hw.init        = CCU_PLL_HWINIT(_name, _flags)                 \
+               .hw.init        = CCU_PLL_COMMON_HWINIT(_name, _ops, _flags)    \
        }                                                                       \
 }
 
+#define CCU_PLL_DEFINE(_name, _table, _reg_swcr1, _reg_swcr3, _reg_lock,       \
+                      _mask_lock, _flags)                                      \
+       CCU_PLL_X_DEFINE(_name, _table, _reg_swcr1, 0, _reg_swcr3,              \
+                      _reg_lock, _mask_lock, &spacemit_ccu_pll_ops, _flags)
+
+#define CCU_PLLA_DEFINE(_name, _table, _reg_swcr1, _reg_swcr2, _reg_swcr3,     \
+                      _reg_lock, _mask_lock, _flags)                           \
+       CCU_PLL_X_DEFINE(_name, _table, _reg_swcr1, _reg_swcr2, _reg_swcr3,     \
+                      _reg_lock, _mask_lock, &spacemit_ccu_plla_ops, _flags)
+
 static inline struct ccu_pll *hw_to_ccu_pll(struct clk_hw *hw)
 {
        struct ccu_common *common = hw_to_ccu_common(hw);
@@ -82,5 +118,6 @@ static inline struct ccu_pll *hw_to_ccu_pll(struct clk_hw *hw)
 }
 
 extern const struct clk_ops spacemit_ccu_pll_ops;
+extern const struct clk_ops spacemit_ccu_plla_ops;
 
 #endif