]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
clk: rockchip: Implement rockchip_clk_register_armclk_multi_pll()
authorElaine Zhang <zhangqing@rock-chips.com>
Tue, 11 Nov 2025 02:57:34 +0000 (10:57 +0800)
committerHeiko Stuebner <heiko@sntech.de>
Thu, 20 Nov 2025 19:50:23 +0000 (20:50 +0100)
The current path will have an independent PLL(LPLL\BPLL)
exclusively for the CPU to use.
As follows:

            |-\
    --lpll--|  \
            |mux|--[gate]--[div]--clk_core--
    --gpll--|  /
            |-/

The new chip does not have a dedicated PLL for the cpu;
it is distributed nearby from the common PLL.
If there are special frequency requirements that require the
use of pvtpll, explanations will be submitted later.

The clock path of new soc CPU simplified as follows:

    --gpll--|--\
            |   \
            |    \
            |     \
   --v0pll--| mux |--[gate]--[div]--clk_core--
            |     /
            |    /
   --v1pll--|   /
            |--/

Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
Link: https://patch.msgid.link/20251111025738.869847-2-zhangqing@rock-chips.com
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
drivers/clk/rockchip/clk-cpu.c
drivers/clk/rockchip/clk.c
drivers/clk/rockchip/clk.h

index dcc9dcb597ae11d59900a46591826fe82a5ea132..6e91a3041a03c55c19cd223783ade6307d599446 100644 (file)
@@ -396,3 +396,168 @@ free_cpuclk:
        kfree(cpuclk);
        return ERR_PTR(ret);
 }
+
+static int rockchip_cpuclk_multi_pll_pre_rate_change(struct rockchip_cpuclk *cpuclk,
+                                                    struct clk_notifier_data *ndata)
+{
+       unsigned long new_rate = roundup(ndata->new_rate, 1000);
+       const struct rockchip_cpuclk_rate_table *rate;
+       unsigned long flags;
+
+       rate = rockchip_get_cpuclk_settings(cpuclk, new_rate);
+       if (!rate) {
+               pr_err("%s: Invalid rate : %lu for cpuclk\n",
+                      __func__, new_rate);
+               return -EINVAL;
+       }
+
+       if (new_rate > ndata->old_rate) {
+               spin_lock_irqsave(cpuclk->lock, flags);
+               rockchip_cpuclk_set_dividers(cpuclk, rate);
+               spin_unlock_irqrestore(cpuclk->lock, flags);
+       }
+
+       return 0;
+}
+
+static int rockchip_cpuclk_multi_pll_post_rate_change(struct rockchip_cpuclk *cpuclk,
+                                                     struct clk_notifier_data *ndata)
+{
+       unsigned long new_rate = roundup(ndata->new_rate, 1000);
+       const struct rockchip_cpuclk_rate_table *rate;
+       unsigned long flags;
+
+       rate = rockchip_get_cpuclk_settings(cpuclk, new_rate);
+       if (!rate) {
+               pr_err("%s: Invalid rate : %lu for cpuclk\n",
+                      __func__, new_rate);
+               return -EINVAL;
+       }
+
+       if (new_rate < ndata->old_rate) {
+               spin_lock_irqsave(cpuclk->lock, flags);
+               rockchip_cpuclk_set_dividers(cpuclk, rate);
+               spin_unlock_irqrestore(cpuclk->lock, flags);
+       }
+
+       return 0;
+}
+
+static int rockchip_cpuclk_multi_pll_notifier_cb(struct notifier_block *nb,
+                                                unsigned long event, void *data)
+{
+       struct clk_notifier_data *ndata = data;
+       struct rockchip_cpuclk *cpuclk = to_rockchip_cpuclk_nb(nb);
+       int ret = 0;
+
+       pr_debug("%s: event %lu, old_rate %lu, new_rate: %lu\n",
+                __func__, event, ndata->old_rate, ndata->new_rate);
+       if (event == PRE_RATE_CHANGE)
+               ret = rockchip_cpuclk_multi_pll_pre_rate_change(cpuclk, ndata);
+       else if (event == POST_RATE_CHANGE)
+               ret = rockchip_cpuclk_multi_pll_post_rate_change(cpuclk, ndata);
+
+       return notifier_from_errno(ret);
+}
+
+struct clk *rockchip_clk_register_cpuclk_multi_pll(const char *name,
+                                                  const char *const *parent_names,
+                                                  u8 num_parents, void __iomem *base,
+                                                  int muxdiv_offset, u8 mux_shift,
+                                                  u8 mux_width, u8 mux_flags,
+                                                  int div_offset, u8 div_shift,
+                                                  u8 div_width, u8 div_flags,
+                                                  unsigned long flags, spinlock_t *lock,
+                                                  const struct rockchip_cpuclk_rate_table *rates,
+                                                  int nrates)
+{
+       struct rockchip_cpuclk *cpuclk;
+       struct clk_hw *hw;
+       struct clk_mux *mux = NULL;
+       struct clk_divider *div = NULL;
+       const struct clk_ops *mux_ops = NULL, *div_ops = NULL;
+       int ret;
+
+       if (num_parents > 1) {
+               mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+               if (!mux)
+                       return ERR_PTR(-ENOMEM);
+
+               mux->reg = base + muxdiv_offset;
+               mux->shift = mux_shift;
+               mux->mask = BIT(mux_width) - 1;
+               mux->flags = mux_flags;
+               mux->lock = lock;
+               mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
+                                                       : &clk_mux_ops;
+       }
+
+       if (div_width > 0) {
+               div = kzalloc(sizeof(*div), GFP_KERNEL);
+               if (!div) {
+                       ret = -ENOMEM;
+                       goto free_mux;
+               }
+
+               div->flags = div_flags;
+               if (div_offset)
+                       div->reg = base + div_offset;
+               else
+                       div->reg = base + muxdiv_offset;
+               div->shift = div_shift;
+               div->width = div_width;
+               div->lock = lock;
+               div_ops = (div_flags & CLK_DIVIDER_READ_ONLY)
+                                               ? &clk_divider_ro_ops
+                                               : &clk_divider_ops;
+       }
+
+       hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
+                                      mux ? &mux->hw : NULL, mux_ops,
+                                      div ? &div->hw : NULL, div_ops,
+                                      NULL, NULL, flags);
+       if (IS_ERR(hw)) {
+               ret = PTR_ERR(hw);
+               goto free_div;
+       }
+
+       cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
+       if (!cpuclk) {
+               ret = -ENOMEM;
+               goto unregister_clk;
+       }
+
+       cpuclk->reg_base = base;
+       cpuclk->lock = lock;
+       cpuclk->clk_nb.notifier_call = rockchip_cpuclk_multi_pll_notifier_cb;
+       ret = clk_notifier_register(hw->clk, &cpuclk->clk_nb);
+       if (ret) {
+               pr_err("%s: failed to register clock notifier for %s\n",
+                      __func__, name);
+               goto free_cpuclk;
+       }
+
+       if (nrates > 0) {
+               cpuclk->rate_count = nrates;
+               cpuclk->rate_table = kmemdup(rates,
+                                            sizeof(*rates) * nrates,
+                                            GFP_KERNEL);
+               if (!cpuclk->rate_table) {
+                       ret = -ENOMEM;
+                       goto free_cpuclk;
+               }
+       }
+
+       return hw->clk;
+
+free_cpuclk:
+       kfree(cpuclk);
+unregister_clk:
+       clk_hw_unregister_composite(hw);
+free_div:
+       kfree(div);
+free_mux:
+       kfree(mux);
+
+       return ERR_PTR(ret);
+}
index 19caf26c991bd5bf1b1c4bc469ec15ee57eea050..2601df3b1066b19648678960bd9b53e7fd5a8fa6 100644 (file)
@@ -722,6 +722,30 @@ void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
 }
 EXPORT_SYMBOL_GPL(rockchip_clk_register_armclk);
 
+void rockchip_clk_register_armclk_multi_pll(struct rockchip_clk_provider *ctx,
+                                           struct rockchip_clk_branch *list,
+                                           const struct rockchip_cpuclk_rate_table *rates,
+                                           int nrates)
+{
+       struct clk *clk;
+
+       clk = rockchip_clk_register_cpuclk_multi_pll(list->name, list->parent_names,
+                                                    list->num_parents, ctx->reg_base,
+                                                    list->muxdiv_offset, list->mux_shift,
+                                                    list->mux_width, list->mux_flags,
+                                                    list->div_offset, list->div_shift,
+                                                    list->div_width, list->div_flags,
+                                                    list->flags, &ctx->lock, rates, nrates);
+       if (IS_ERR(clk)) {
+               pr_err("%s: failed to register clock %s: %ld\n",
+                      __func__, list->name, PTR_ERR(clk));
+               return;
+       }
+
+       rockchip_clk_set_lookup(ctx, clk, list->id);
+}
+EXPORT_SYMBOL_GPL(rockchip_clk_register_armclk_multi_pll);
+
 void rockchip_clk_protect_critical(const char *const clocks[],
                                   int nclocks)
 {
index 7c5e74c7a2e210970a5a48b736e390c77e47399d..23653a942403afb3b56378700a875ff979a64cd9 100644 (file)
@@ -622,6 +622,17 @@ struct clk *rockchip_clk_register_cpuclk(const char *name,
                        const struct rockchip_cpuclk_rate_table *rates,
                        int nrates, void __iomem *reg_base, spinlock_t *lock);
 
+struct clk *rockchip_clk_register_cpuclk_multi_pll(const char *name,
+                                                  const char *const *parent_names,
+                                                  u8 num_parents, void __iomem *base,
+                                                  int muxdiv_offset, u8 mux_shift,
+                                                  u8 mux_width, u8 mux_flags,
+                                                  int div_offset, u8 div_shift,
+                                                  u8 div_width, u8 div_flags,
+                                                  unsigned long flags, spinlock_t *lock,
+                                                  const struct rockchip_cpuclk_rate_table *rates,
+                                                  int nrates);
+
 struct clk *rockchip_clk_register_mmc(const char *name,
                                const char *const *parent_names, u8 num_parents,
                                void __iomem *reg,
@@ -1208,6 +1219,10 @@ void rockchip_clk_register_armclk(struct rockchip_clk_provider *ctx,
                        const struct rockchip_cpuclk_reg_data *reg_data,
                        const struct rockchip_cpuclk_rate_table *rates,
                        int nrates);
+void rockchip_clk_register_armclk_multi_pll(struct rockchip_clk_provider *ctx,
+                                           struct rockchip_clk_branch *list,
+                                           const struct rockchip_cpuclk_rate_table *rates,
+                                           int nrates);
 void rockchip_clk_protect_critical(const char *const clocks[], int nclocks);
 void rockchip_register_restart_notifier(struct rockchip_clk_provider *ctx,
                                        unsigned int reg, void (*cb)(void));