Add PLLDSI clk mux support to select PLLDSI clock from different clock
sources.

Introduce the DEF_PLLDSI_SMUX() macro to define these muxes and register
them in the clock driver.

Extend the determine_rate callback to calculate and propagate PLL
parameters via rzv2h_get_pll_dtable_pars() when LVDS output is selected,
using a new helper function rzv2h_cpg_plldsi_smux_lvds_determine_rate().

Signed-off-by: Tommaso Merciai <[email protected]>
---
 drivers/clk/renesas/rzv2h-cpg.c | 131 ++++++++++++++++++++++++++++++++
 drivers/clk/renesas/rzv2h-cpg.h |   8 ++
 2 files changed, 139 insertions(+)

diff --git a/drivers/clk/renesas/rzv2h-cpg.c b/drivers/clk/renesas/rzv2h-cpg.c
index 3f6299b9fec0..dd782fa269d7 100644
--- a/drivers/clk/renesas/rzv2h-cpg.c
+++ b/drivers/clk/renesas/rzv2h-cpg.c
@@ -418,6 +418,20 @@ bool rzv2h_get_pll_divs_pars(const struct rzv2h_pll_limits 
*limits,
 }
 EXPORT_SYMBOL_NS_GPL(rzv2h_get_pll_divs_pars, "RZV2H_CPG");
 
+/**
+ * struct rzv2h_plldsi_mux_clk - PLL DSI MUX clock
+ *
+ * @priv: CPG private data
+ * @mux: mux clk
+ */
+struct rzv2h_plldsi_mux_clk {
+       struct rzv2h_cpg_priv *priv;
+       struct clk_mux mux;
+};
+
+#define to_plldsi_clk_mux(_mux) \
+       container_of(_mux, struct rzv2h_plldsi_mux_clk, mux)
+
 static unsigned long rzv2h_cpg_plldsi_div_recalc_rate(struct clk_hw *hw,
                                                      unsigned long parent_rate)
 {
@@ -649,6 +663,120 @@ static int rzv2h_cpg_plldsi_set_rate(struct clk_hw *hw, 
unsigned long rate,
        return rzv2h_cpg_pll_set_rate(pll_clk, 
&dsi_info->pll_dsi_parameters.pll, true);
 }
 
+static u8 rzv2h_cpg_plldsi_smux_get_parent(struct clk_hw *hw)
+{
+       return clk_mux_ops.get_parent(hw);
+}
+
+static int rzv2h_cpg_plldsi_smux_set_parent(struct clk_hw *hw, u8 index)
+{
+       return clk_mux_ops.set_parent(hw, index);
+}
+
+static int rzv2h_cpg_plldsi_smux_lvds_determine_rate(struct rzv2h_cpg_priv 
*priv,
+                                                    struct pll_clk *pll_clk,
+                                                    struct clk_rate_request 
*req)
+{
+       struct rzv2h_pll_div_pars *dsi_params;
+       struct rzv2h_pll_dsi_info *dsi_info;
+       u8 lvds_table[] = { 7 };
+       u64 rate_millihz;
+
+       dsi_info = &priv->pll_dsi_info[pll_clk->pll.instance];
+       dsi_params = &dsi_info->pll_dsi_parameters;
+
+       rate_millihz = mul_u32_u32(req->rate, MILLI);
+       if (!rzv2h_get_pll_divs_pars(dsi_info->pll_dsi_limits, dsi_params,
+                                    lvds_table, 1, rate_millihz)) {
+               dev_err(priv->dev, "failed to determine rate for req->rate: 
%lu\n",
+                       req->rate);
+               return -EINVAL;
+       }
+
+       req->rate = DIV_ROUND_CLOSEST_ULL(dsi_params->div.freq_millihz, MILLI);
+       req->best_parent_rate = req->rate;
+       dsi_info->req_pll_dsi_rate = req->best_parent_rate * 
dsi_params->div.divider_value;
+
+       return 0;
+}
+
+static int rzv2h_cpg_plldsi_smux_determine_rate(struct clk_hw *hw,
+                                               struct clk_rate_request *req)
+{
+       struct clk_mux *mux = to_clk_mux(hw);
+       struct rzv2h_plldsi_mux_clk *dsi_mux = to_plldsi_clk_mux(mux);
+       struct pll_clk *pll_clk = to_pll(clk_hw_get_parent(hw));
+       struct rzv2h_cpg_priv *priv = dsi_mux->priv;
+       int ret;
+
+       /*
+        * For LVDS output (parent_idx == 0), calculate PLL parameters with
+        * fixed divider value of 7. For DSI/RGB output (parent_idx == 1) skip
+        * PLL calculation here as it's handled by determine_rate of the
+        * divider (up one level).
+        */
+       if (!clk_mux_ops.get_parent(hw))
+               ret = rzv2h_cpg_plldsi_smux_lvds_determine_rate(priv, pll_clk, 
req);
+       else
+               ret = clk_mux_determine_rate_flags(hw, req, mux->flags);
+
+       return ret;
+}
+
+static const struct clk_ops rzv2h_cpg_plldsi_smux_ops = {
+       .determine_rate = rzv2h_cpg_plldsi_smux_determine_rate,
+       .get_parent = rzv2h_cpg_plldsi_smux_get_parent,
+       .set_parent = rzv2h_cpg_plldsi_smux_set_parent,
+};
+
+static struct clk * __init
+rzv2h_cpg_plldsi_smux_clk_register(const struct cpg_core_clk *core,
+                                  struct rzv2h_cpg_priv *priv)
+{
+       struct rzv2h_plldsi_mux_clk *clk_hw_data;
+       struct clk_init_data init;
+       struct clk_hw *clk_hw;
+       struct smuxed smux;
+       u8 width;
+       int ret;
+
+       smux = core->cfg.smux;
+       width = fls(smux.width) - ffs(smux.width) + 1;
+
+       if (width + smux.width > 16) {
+               dev_err(priv->dev, "mux value exceeds LOWORD field\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       clk_hw_data = devm_kzalloc(priv->dev, sizeof(*clk_hw_data), GFP_KERNEL);
+       if (!clk_hw_data)
+               return ERR_PTR(-ENOMEM);
+
+       clk_hw_data->priv = priv;
+
+       init.name = core->name;
+       init.ops = &rzv2h_cpg_plldsi_smux_ops;
+       init.flags = core->flag;
+       init.parent_names = core->parent_names;
+       init.num_parents = core->num_parents;
+
+       clk_hw_data->mux.reg = priv->base + smux.offset;
+
+       clk_hw_data->mux.shift = smux.shift;
+       clk_hw_data->mux.mask = smux.width;
+       clk_hw_data->mux.flags = core->mux_flags;
+       clk_hw_data->mux.lock = &priv->rmw_lock;
+
+       clk_hw = &clk_hw_data->mux.hw;
+       clk_hw->init = &init;
+
+       ret = devm_clk_hw_register(priv->dev, clk_hw);
+       if (ret)
+               return ERR_PTR(ret);
+
+       return clk_hw->clk;
+}
+
 static int rzv2h_cpg_pll_clk_is_enabled(struct clk_hw *hw)
 {
        struct pll_clk *pll_clk = to_pll(hw);
@@ -1085,6 +1213,9 @@ rzv2h_cpg_register_core_clk(const struct cpg_core_clk 
*core,
        case CLK_TYPE_PLLDSI_DIV:
                clk = rzv2h_cpg_plldsi_div_clk_register(core, priv);
                break;
+       case CLK_TYPE_PLLDSI_SMUX:
+               clk = rzv2h_cpg_plldsi_smux_clk_register(core, priv);
+               break;
        default:
                goto fail;
        }
diff --git a/drivers/clk/renesas/rzv2h-cpg.h b/drivers/clk/renesas/rzv2h-cpg.h
index dc957bdaf5e9..5f6e775612e7 100644
--- a/drivers/clk/renesas/rzv2h-cpg.h
+++ b/drivers/clk/renesas/rzv2h-cpg.h
@@ -203,6 +203,7 @@ enum clk_types {
        CLK_TYPE_SMUX,          /* Static Mux */
        CLK_TYPE_PLLDSI,        /* PLLDSI */
        CLK_TYPE_PLLDSI_DIV,    /* PLLDSI divider */
+       CLK_TYPE_PLLDSI_SMUX,   /* PLLDSI Static Mux */
 };
 
 #define DEF_TYPE(_name, _id, _type...) \
@@ -241,6 +242,13 @@ enum clk_types {
                 .dtable = _dtable, \
                 .parent = _parent, \
                 .flag = CLK_SET_RATE_PARENT)
+#define DEF_PLLDSI_SMUX(_name, _id, _smux_packed, _parent_names) \
+       DEF_TYPE(_name, _id, CLK_TYPE_PLLDSI_SMUX, \
+                .cfg.smux = _smux_packed, \
+                .parent_names = _parent_names, \
+                .num_parents = ARRAY_SIZE(_parent_names), \
+                .flag = CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, \
+                .mux_flags = CLK_MUX_HIWORD_MASK)
 
 /**
  * struct rzv2h_mod_clk - Module Clocks definitions
-- 
2.43.0

Reply via email to