diff options
author | Gabriel Fernandez <gabriel.fernandez@foss.st.com> | 2022-05-16 09:05:52 +0200 |
---|---|---|
committer | Stephen Boyd <sboyd@kernel.org> | 2022-05-20 21:07:49 -0700 |
commit | 5f0d47213f52c0623b3ce662c924575b2ba718ae (patch) | |
tree | c56056749d249ea460484c6dd9e9c439a9c803da /drivers/clk/stm32/clk-stm32-core.c | |
parent | 720e34ab3e57e19dba70c384e30494f047999c43 (diff) |
clk: stm32mp13: add composite clock
Just to introduce management of stm32 composite clock.
Signed-off-by: Gabriel Fernandez <gabriel.fernandez@foss.st.com>
Link: https://lore.kernel.org/r/20220516070600.7692-7-gabriel.fernandez@foss.st.com
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
Diffstat (limited to 'drivers/clk/stm32/clk-stm32-core.c')
-rw-r--r-- | drivers/clk/stm32/clk-stm32-core.c | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/drivers/clk/stm32/clk-stm32-core.c b/drivers/clk/stm32/clk-stm32-core.c index da38eca2b8c5..69e40c152d2f 100644 --- a/drivers/clk/stm32/clk-stm32-core.c +++ b/drivers/clk/stm32/clk-stm32-core.c @@ -389,6 +389,159 @@ const struct clk_ops clk_stm32_divider_ops = { .set_rate = clk_stm32_divider_set_rate, }; +static int clk_stm32_composite_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); + unsigned long flags = 0; + int ret; + + if (composite->div_id == NO_STM32_DIV) + return rate; + + spin_lock_irqsave(composite->lock, flags); + + ret = stm32_divider_set_rate(composite->base, composite->clock_data, + composite->div_id, rate, parent_rate); + + spin_unlock_irqrestore(composite->lock, flags); + + return ret; +} + +static unsigned long clk_stm32_composite_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); + + if (composite->div_id == NO_STM32_DIV) + return parent_rate; + + return stm32_divider_get_rate(composite->base, composite->clock_data, + composite->div_id, parent_rate); +} + +static long clk_stm32_composite_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); + + const struct stm32_div_cfg *divider; + + if (composite->div_id == NO_STM32_DIV) + return rate; + + divider = &composite->clock_data->dividers[composite->div_id]; + + /* if read only, just return current value */ + if (divider->flags & CLK_DIVIDER_READ_ONLY) { + u32 val; + + val = readl(composite->base + divider->offset) >> divider->shift; + val &= clk_div_mask(divider->width); + + return divider_ro_round_rate(hw, rate, prate, divider->table, + divider->width, divider->flags, + val); + } + + return divider_round_rate_parent(hw, clk_hw_get_parent(hw), + rate, prate, divider->table, + divider->width, divider->flags); +} + +static u8 clk_stm32_composite_get_parent(struct clk_hw *hw) +{ + struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); + + return stm32_mux_get_parent(composite->base, composite->clock_data, composite->mux_id); +} + +static int clk_stm32_composite_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); + unsigned long flags = 0; + + spin_lock_irqsave(composite->lock, flags); + + stm32_mux_set_parent(composite->base, composite->clock_data, composite->mux_id, index); + + spin_unlock_irqrestore(composite->lock, flags); + + return 0; +} + +static int clk_stm32_composite_is_enabled(struct clk_hw *hw) +{ + struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); + + if (composite->gate_id == NO_STM32_GATE) + return (__clk_get_enable_count(hw->clk) > 0); + + return stm32_gate_is_enabled(composite->base, composite->clock_data, composite->gate_id); +} + +static void clk_stm32_composite_gate_endisable(struct clk_hw *hw, int enable) +{ + struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); + unsigned long flags = 0; + + spin_lock_irqsave(composite->lock, flags); + + stm32_gate_endisable(composite->base, composite->clock_data, composite->gate_id, enable); + + spin_unlock_irqrestore(composite->lock, flags); +} + +static int clk_stm32_composite_gate_enable(struct clk_hw *hw) +{ + struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); + + if (composite->gate_id == NO_STM32_GATE) + return 0; + + clk_stm32_composite_gate_endisable(hw, 1); + + return 0; +} + +static void clk_stm32_composite_gate_disable(struct clk_hw *hw) +{ + struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); + + if (composite->gate_id == NO_STM32_GATE) + return; + + clk_stm32_composite_gate_endisable(hw, 0); +} + +static void clk_stm32_composite_disable_unused(struct clk_hw *hw) +{ + struct clk_stm32_composite *composite = to_clk_stm32_composite(hw); + unsigned long flags = 0; + + if (composite->gate_id == NO_STM32_GATE) + return; + + spin_lock_irqsave(composite->lock, flags); + + stm32_gate_disable_unused(composite->base, composite->clock_data, composite->gate_id); + + spin_unlock_irqrestore(composite->lock, flags); +} + +const struct clk_ops clk_stm32_composite_ops = { + .set_rate = clk_stm32_composite_set_rate, + .recalc_rate = clk_stm32_composite_recalc_rate, + .round_rate = clk_stm32_composite_round_rate, + .get_parent = clk_stm32_composite_get_parent, + .set_parent = clk_stm32_composite_set_parent, + .enable = clk_stm32_composite_gate_enable, + .disable = clk_stm32_composite_gate_disable, + .is_enabled = clk_stm32_composite_is_enabled, + .disable_unused = clk_stm32_composite_disable_unused, +}; + struct clk_hw *clk_stm32_mux_register(struct device *dev, const struct stm32_rcc_match_data *data, void __iomem *base, @@ -451,3 +604,24 @@ struct clk_hw *clk_stm32_div_register(struct device *dev, return hw; } + +struct clk_hw *clk_stm32_composite_register(struct device *dev, + const struct stm32_rcc_match_data *data, + void __iomem *base, + spinlock_t *lock, + const struct clock_config *cfg) +{ + struct clk_stm32_composite *composite = cfg->clock_cfg; + struct clk_hw *hw = &composite->hw; + int err; + + composite->base = base; + composite->lock = lock; + composite->clock_data = data->clock_data; + + err = clk_hw_register(dev, hw); + if (err) + return ERR_PTR(err); + + return hw; +} |