[PATCH v2 1/9] clk: renesas: rzg2l: Add DSI divider clock support for RZ/G3L
Biju <[email protected]>
| Newsgroups | org.kernel.vger.linux-renesas-soc,org.kernel.vger.linux-clk,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
From: Biju Das <[email protected]> Add a new DSI divider clock type (CLK_TYPE_G3L_PLLDSI_DIV) for the RZ/G3L SoC, which requires a different divider implementation than the existing RZ/G2L DSI divider clock. The RZ/G3L DSI divider uses two cascaded dividers, DIV_DSI_A and DIV_DSI_B, where the effective divider is: rate = parent_rate / ((1 << div_a) * (div_b + 1)) DIV_DSI_A is a power-of-two divider with values in the range [0, 5], and DIV_DSI_B is a linear divider with values in the range [1, 16]. Introduce the g3l_dsi_div_hw_data structure, rzg3l_cpg_dsi_div_ops, and rzg3l_cpg_dsi_div_clk_register() to implement the new clock type, and add the DEF_G3L_PLLDSI_DIV() macro for use in clock table definitions. Signed-off-by: Biju Das <[email protected]> --- v1->v2: * Introduced the macro DIV_DSI_{A,B}_SET and used inside rzg3l_cpg_dsi_div_recalc_rate() and rzg3l_cpg_dsi_div_set_rate() * Simplified the divider to "(div_b + 1) << div_a" in rzg3l_cpg_dsi_div_recalc_rate(). * Replaced the data type u32->unsigned int for the variable divider in rzg3l_cpg_dsi_div_determine_rate() * Moved the declarations of the loop counters inside the for()-statements * Updated the comment sections in rzg3l_cpg_dsi_div_determine_rate(). * Updated loop logic in rzg3l_cpg_dsi_div_determine_rate(). --- drivers/clk/renesas/rzg2l-cpg.c | 141 ++++++++++++++++++++++++++++++++ drivers/clk/renesas/rzg2l-cpg.h | 5 ++ 2 files changed, 146 insertions(+) diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index 975b705d3a2b..1660508d523a 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -67,6 +67,12 @@ #define CPG_PLL_MON_LOCK BIT(4) #define CPG_PLL_MON_RESETB BIT(0) +#define DIV_DSI_A_SET GENMASK(2, 0) +#define DIV_DSI_B_SET GENMASK(7, 4) + +#define RZG3L_SDIV_DIV_DSI_A_WEN BIT(16) +#define RZG3L_SDIV_DIV_DSI_B_WEN BIT(20) + #define CLK_ON_R(reg) (reg) #define CLK_MON_R(reg) (0x180 + (reg)) #define CLK_RST_R(reg) (reg) @@ -833,6 +839,138 @@ rzg2l_cpg_dsi_div_clk_register(const struct cpg_core_clk *core, return clk_hw->clk; } +struct g3l_dsi_div_hw_data { + struct clk_hw hw; + struct rzg2l_cpg_priv *priv; + unsigned long rate; + u32 off; + u8 div_a; + u8 div_b; +}; + +#define to_g3l_dsi_div_hw_data(_hw) container_of(_hw, struct g3l_dsi_div_hw_data, hw) + +static unsigned long rzg3l_cpg_dsi_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct g3l_dsi_div_hw_data *dsi_div = to_g3l_dsi_div_hw_data(hw); + struct rzg2l_cpg_priv *priv = dsi_div->priv; + int div_a, div_b, val; + + val = readl(priv->base + dsi_div->off); + div_a = FIELD_GET(DIV_DSI_A_SET, val); + div_b = FIELD_GET(DIV_DSI_B_SET, val); + + return DIV_ROUND_CLOSEST_ULL((u64)parent_rate, (div_b + 1) << div_a); +} + +static int rzg3l_cpg_dsi_div_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct g3l_dsi_div_hw_data *dsi_div = to_g3l_dsi_div_hw_data(hw); + struct rzg2l_cpg_priv *priv = dsi_div->priv; + unsigned int divider = dsi_div_ab_desired; + bool divider_found = false; + + if (dsi_div_target) { + /* Calculate DIV_DSI_A and DIV_DSI_B */ + for (unsigned int div_a = 0; div_a <= 5 && !divider_found; div_a++) { + unsigned int div_a_tmp = 5 - div_a; + + for (unsigned int div_b = 0; div_b < 16; div_b++) { + divider = (div_b + 1) << div_a_tmp; + if (divider == dsi_div_ab_desired) { + dsi_div->div_a = div_a_tmp; + dsi_div->div_b = div_b; + divider_found = true; + break; + } + } + } + } else { + dsi_div->div_b = 0; + /* Calculate DIV_DSI_A */ + for (unsigned int div_a = 0; div_a <= 5 && !divider_found; div_a++) { + unsigned int div_a_tmp = 5 - div_a; + + divider = 1 << div_a_tmp; + if (divider == dsi_div_ab_desired) { + dsi_div->div_a = div_a_tmp; + divider_found = true; + break; + } + } + } + + if (!divider_found) { + dev_err(priv->dev, "failed dsi div for: %u\n", divider); + return -EINVAL; + } + + req->best_parent_rate = req->rate * divider; + + return 0; +} + +static int rzg3l_cpg_dsi_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct g3l_dsi_div_hw_data *dsi_div = to_g3l_dsi_div_hw_data(hw); + struct rzg2l_cpg_priv *priv = dsi_div->priv; + + writel(RZG3L_SDIV_DIV_DSI_A_WEN | RZG3L_SDIV_DIV_DSI_B_WEN | + FIELD_PREP(DIV_DSI_A_SET, dsi_div->div_a) | + FIELD_PREP(DIV_DSI_B_SET, dsi_div->div_b), + priv->base + dsi_div->off); + + return 0; +} + +static const struct clk_ops rzg3l_cpg_dsi_div_ops = { + .recalc_rate = rzg3l_cpg_dsi_div_recalc_rate, + .determine_rate = rzg3l_cpg_dsi_div_determine_rate, + .set_rate = rzg3l_cpg_dsi_div_set_rate, +}; + +static struct clk * __init +rzg3l_cpg_dsi_div_clk_register(const struct cpg_core_clk *core, + struct rzg2l_cpg_priv *priv) +{ + struct g3l_dsi_div_hw_data *clk_hw_data; + const struct clk *parent; + const char *parent_name; + struct clk_init_data init; + struct clk_hw *clk_hw; + int ret; + + parent = priv->clks[core->parent]; + if (IS_ERR(parent)) + return ERR_CAST(parent); + + 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; + clk_hw_data->off = core->conf; + + parent_name = __clk_get_name(parent); + init.name = core->name; + init.ops = &rzg3l_cpg_dsi_div_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk_hw = &clk_hw_data->hw; + clk_hw->init = &init; + + ret = devm_clk_hw_register(priv->dev, clk_hw); + if (ret) + return ERR_PTR(ret); + + return clk_hw->clk; +} + struct pll5_mux_hw_data { struct clk_hw hw; u32 conf; @@ -1346,6 +1484,9 @@ rzg2l_cpg_register_core_clk(const struct cpg_core_clk *core, case CLK_TYPE_DSI_DIV: clk = rzg2l_cpg_dsi_div_clk_register(core, priv); break; + case CLK_TYPE_G3L_DSI_DIV: + clk = rzg3l_cpg_dsi_div_clk_register(core, priv); + break; default: goto fail; } diff --git a/drivers/clk/renesas/rzg2l-cpg.h b/drivers/clk/renesas/rzg2l-cpg.h index bd6169f62538..24642a089b6c 100644 --- a/drivers/clk/renesas/rzg2l-cpg.h +++ b/drivers/clk/renesas/rzg2l-cpg.h @@ -142,6 +142,8 @@ enum clk_types { /* Clock for DSI divider */ CLK_TYPE_DSI_DIV, + /* Clock for G3L DSI divider */ + CLK_TYPE_G3L_DSI_DIV, }; #define DEF_TYPE(_name, _id, _type...) \ @@ -201,6 +203,9 @@ enum clk_types { .num_parents = ARRAY_SIZE(_parent_names)) #define DEF_DSI_DIV(_name, _id, _parent, _flag) \ DEF_TYPE(_name, _id, CLK_TYPE_DSI_DIV, .parent = _parent, .flag = _flag) +#define DEF_G3L_DSI_DIV(_name, _id, _parent, _conf) \ + DEF_TYPE(_name, _id, CLK_TYPE_G3L_DSI_DIV, .parent = _parent, .conf = _conf, \ + .flag = CLK_SET_RATE_PARENT) /** * struct rzg2l_mod_clk - Module Clocks definitions -- 2.43.0