diff options
author | Biju Das <biju.das.jz@bp.renesas.com> | 2021-09-22 17:51:44 +0200 |
---|---|---|
committer | Geert Uytterhoeven <geert+renesas@glider.be> | 2021-09-24 15:11:05 +0200 |
commit | 32897e6fff196a5de4981030466ae391dfe56c7b (patch) | |
tree | eb20f75de0e391ec47e7d16143910ea6f56e1ff5 /drivers/clk/renesas/rzg2l-cpg.c | |
parent | clk: renesas: r9a07g044: Add ethernet clock sources (diff) | |
download | linux-32897e6fff196a5de4981030466ae391dfe56c7b.tar.xz linux-32897e6fff196a5de4981030466ae391dfe56c7b.zip |
clk: renesas: rzg2l: Add support to handle coupled clocks
The AXI and CHI clocks use the same register bit for controlling clock
output. Add a new clock type for coupled clocks, which sets the
CPG_CLKON_ETH.CLK[01]_ON bit when at least one clock is enabled, and
clears the bit only when both clocks are disabled.
Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
Reviewed-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Link: https://lore.kernel.org/r/20210922155145.28156-4-biju.das.jz@bp.renesas.com
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Diffstat (limited to 'drivers/clk/renesas/rzg2l-cpg.c')
-rw-r--r-- | drivers/clk/renesas/rzg2l-cpg.c | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index 69eba5a66490..1501547a11a3 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -333,13 +333,17 @@ fail: * @hw: handle between common and hardware-specific interfaces * @off: register offset * @bit: ON/MON bit + * @enabled: soft state of the clock, if it is coupled with another clock * @priv: CPG/MSTP private data + * @sibling: pointer to the other coupled clock */ struct mstp_clock { struct clk_hw hw; u16 off; u8 bit; + bool enabled; struct rzg2l_cpg_priv *priv; + struct mstp_clock *sibling; }; #define to_mod_clock(_hw) container_of(_hw, struct mstp_clock, hw) @@ -392,11 +396,41 @@ static int rzg2l_mod_clock_endisable(struct clk_hw *hw, bool enable) static int rzg2l_mod_clock_enable(struct clk_hw *hw) { + struct mstp_clock *clock = to_mod_clock(hw); + + if (clock->sibling) { + struct rzg2l_cpg_priv *priv = clock->priv; + unsigned long flags; + bool enabled; + + spin_lock_irqsave(&priv->rmw_lock, flags); + enabled = clock->sibling->enabled; + clock->enabled = true; + spin_unlock_irqrestore(&priv->rmw_lock, flags); + if (enabled) + return 0; + } + return rzg2l_mod_clock_endisable(hw, true); } static void rzg2l_mod_clock_disable(struct clk_hw *hw) { + struct mstp_clock *clock = to_mod_clock(hw); + + if (clock->sibling) { + struct rzg2l_cpg_priv *priv = clock->priv; + unsigned long flags; + bool enabled; + + spin_lock_irqsave(&priv->rmw_lock, flags); + enabled = clock->sibling->enabled; + clock->enabled = false; + spin_unlock_irqrestore(&priv->rmw_lock, flags); + if (enabled) + return; + } + rzg2l_mod_clock_endisable(hw, false); } @@ -412,6 +446,9 @@ static int rzg2l_mod_clock_is_enabled(struct clk_hw *hw) return 1; } + if (clock->sibling) + return clock->enabled; + value = readl(priv->base + CLK_MON_R(clock->off)); return value & bitmask; @@ -423,6 +460,28 @@ static const struct clk_ops rzg2l_mod_clock_ops = { .is_enabled = rzg2l_mod_clock_is_enabled, }; +static struct mstp_clock +*rzg2l_mod_clock__get_sibling(struct mstp_clock *clock, + struct rzg2l_cpg_priv *priv) +{ + struct clk_hw *hw; + unsigned int i; + + for (i = 0; i < priv->num_mod_clks; i++) { + struct mstp_clock *clk; + + if (priv->clks[priv->num_core_clks + i] == ERR_PTR(-ENOENT)) + continue; + + hw = __clk_get_hw(priv->clks[priv->num_core_clks + i]); + clk = to_mod_clock(hw); + if (clock->off == clk->off && clock->bit == clk->bit) + return clk; + } + + return NULL; +} + static void __init rzg2l_cpg_register_mod_clk(const struct rzg2l_mod_clk *mod, const struct rzg2l_cpg_info *info, @@ -484,6 +543,18 @@ rzg2l_cpg_register_mod_clk(const struct rzg2l_mod_clk *mod, dev_dbg(dev, "Module clock %pC at %lu Hz\n", clk, clk_get_rate(clk)); priv->clks[id] = clk; + + if (mod->is_coupled) { + struct mstp_clock *sibling; + + clock->enabled = rzg2l_mod_clock_is_enabled(&clock->hw); + sibling = rzg2l_mod_clock__get_sibling(clock, priv); + if (sibling) { + clock->sibling = sibling; + sibling->sibling = clock; + } + } + return; fail: |