Lines Matching +full:no +full:- +full:divider

1 // SPDX-License-Identifier: GPL-2.0-only
9 * Baikal-T1 CCU Dividers interface driver
12 #define pr_fmt(fmt) "bt1-ccu-div: " fmt
19 #include <linux/clk-provider.h>
27 #include "ccu-div.h"
35 GENMASK((_width) + CCU_DIV_CTL_CLKDIV_FLD - 1, CCU_DIV_CTL_CLKDIV_FLD)
48 * getter available with non-constant mask support.
78 unsigned long divider) in ccu_div_var_update_clkdiv() argument
85 nd = ccu_div_lock_delay_ns(parent_rate, divider); in ccu_div_var_update_clkdiv()
87 if (div->features & CCU_DIV_LOCK_SHIFTED) in ccu_div_var_update_clkdiv()
92 regmap_update_bits(div->sys_regs, div->reg_ctl, in ccu_div_var_update_clkdiv()
96 * Until there is nsec-version of readl_poll_timeout() is available in ccu_div_var_update_clkdiv()
102 regmap_read(div->sys_regs, div->reg_ctl, &val); in ccu_div_var_update_clkdiv()
105 } while (--count); in ccu_div_var_update_clkdiv()
107 return -ETIMEDOUT; in ccu_div_var_update_clkdiv()
119 pr_err("Can't enable '%s' with no parent", clk_hw_get_name(hw)); in ccu_div_var_enable()
120 return -EINVAL; in ccu_div_var_enable()
123 regmap_read(div->sys_regs, div->reg_ctl, &val); in ccu_div_var_enable()
127 spin_lock_irqsave(&div->lock, flags); in ccu_div_var_enable()
129 ccu_div_get(div->mask, val)); in ccu_div_var_enable()
131 regmap_update_bits(div->sys_regs, div->reg_ctl, in ccu_div_var_enable()
133 spin_unlock_irqrestore(&div->lock, flags); in ccu_div_var_enable()
135 pr_err("Divider '%s' lock timed out\n", clk_hw_get_name(hw)); in ccu_div_var_enable()
145 spin_lock_irqsave(&div->lock, flags); in ccu_div_gate_enable()
146 regmap_update_bits(div->sys_regs, div->reg_ctl, in ccu_div_gate_enable()
148 spin_unlock_irqrestore(&div->lock, flags); in ccu_div_gate_enable()
158 spin_lock_irqsave(&div->lock, flags); in ccu_div_gate_disable()
159 regmap_update_bits(div->sys_regs, div->reg_ctl, CCU_DIV_CTL_EN, 0); in ccu_div_gate_disable()
160 spin_unlock_irqrestore(&div->lock, flags); in ccu_div_gate_disable()
168 regmap_read(div->sys_regs, div->reg_ctl, &val); in ccu_div_gate_is_enabled()
178 spin_lock_irqsave(&div->lock, flags); in ccu_div_buf_enable()
179 regmap_update_bits(div->sys_regs, div->reg_ctl, in ccu_div_buf_enable()
181 spin_unlock_irqrestore(&div->lock, flags); in ccu_div_buf_enable()
191 spin_lock_irqsave(&div->lock, flags); in ccu_div_buf_disable()
192 regmap_update_bits(div->sys_regs, div->reg_ctl, in ccu_div_buf_disable()
194 spin_unlock_irqrestore(&div->lock, flags); in ccu_div_buf_disable()
202 regmap_read(div->sys_regs, div->reg_ctl, &val); in ccu_div_buf_is_enabled()
211 unsigned long divider; in ccu_div_var_recalc_rate() local
214 regmap_read(div->sys_regs, div->reg_ctl, &val); in ccu_div_var_recalc_rate()
215 divider = ccu_div_get(div->mask, val); in ccu_div_var_recalc_rate()
217 return ccu_div_calc_freq(parent_rate, divider); in ccu_div_var_recalc_rate()
224 unsigned long divider; in ccu_div_var_calc_divider() local
226 divider = parent_rate / rate; in ccu_div_var_calc_divider()
227 return clamp_t(unsigned long, divider, CCU_DIV_CLKDIV_MIN, in ccu_div_var_calc_divider()
235 unsigned long divider; in ccu_div_var_round_rate() local
237 divider = ccu_div_var_calc_divider(rate, *parent_rate, div->mask); in ccu_div_var_round_rate()
239 return ccu_div_calc_freq(*parent_rate, divider); in ccu_div_var_round_rate()
243 * This method is used for the clock divider blocks, which support the
244 * on-the-fly rate change. So due to lacking the EN bit functionality
251 unsigned long flags, divider; in ccu_div_var_set_rate_slow() local
255 divider = ccu_div_var_calc_divider(rate, parent_rate, div->mask); in ccu_div_var_set_rate_slow()
256 if (divider == 1 && div->features & CCU_DIV_SKIP_ONE) { in ccu_div_var_set_rate_slow()
257 divider = 0; in ccu_div_var_set_rate_slow()
258 } else if (div->features & CCU_DIV_SKIP_ONE_TO_THREE) { in ccu_div_var_set_rate_slow()
259 if (divider == 1 || divider == 2) in ccu_div_var_set_rate_slow()
260 divider = 0; in ccu_div_var_set_rate_slow()
261 else if (divider == 3) in ccu_div_var_set_rate_slow()
262 divider = 4; in ccu_div_var_set_rate_slow()
265 val = ccu_div_prep(div->mask, divider); in ccu_div_var_set_rate_slow()
267 spin_lock_irqsave(&div->lock, flags); in ccu_div_var_set_rate_slow()
268 regmap_update_bits(div->sys_regs, div->reg_ctl, div->mask, val); in ccu_div_var_set_rate_slow()
269 ret = ccu_div_var_update_clkdiv(div, parent_rate, divider); in ccu_div_var_set_rate_slow()
270 spin_unlock_irqrestore(&div->lock, flags); in ccu_div_var_set_rate_slow()
272 pr_err("Divider '%s' lock timed out\n", clk_hw_get_name(hw)); in ccu_div_var_set_rate_slow()
278 * This method is used for the clock divider blocks, which don't support
279 * the on-the-fly rate change.
285 unsigned long flags, divider; in ccu_div_var_set_rate_fast() local
288 divider = ccu_div_var_calc_divider(rate, parent_rate, div->mask); in ccu_div_var_set_rate_fast()
289 val = ccu_div_prep(div->mask, divider); in ccu_div_var_set_rate_fast()
292 * Also disable the clock divider block if it was enabled by default in ccu_div_var_set_rate_fast()
295 spin_lock_irqsave(&div->lock, flags); in ccu_div_var_set_rate_fast()
296 regmap_update_bits(div->sys_regs, div->reg_ctl, in ccu_div_var_set_rate_fast()
297 div->mask | CCU_DIV_CTL_EN, val); in ccu_div_var_set_rate_fast()
298 spin_unlock_irqrestore(&div->lock, flags); in ccu_div_var_set_rate_fast()
308 return ccu_div_calc_freq(parent_rate, div->divider); in ccu_div_fixed_recalc_rate()
316 return ccu_div_calc_freq(*parent_rate, div->divider); in ccu_div_fixed_round_rate()
349 * It can be dangerous to change the Divider settings behind clock framework
359 struct ccu_div *div = bit->div; in ccu_div_dbgfs_bit_set()
362 spin_lock_irqsave(&div->lock, flags); in ccu_div_dbgfs_bit_set()
363 regmap_update_bits(div->sys_regs, div->reg_ctl, in ccu_div_dbgfs_bit_set()
364 bit->mask, val ? bit->mask : 0); in ccu_div_dbgfs_bit_set()
365 spin_unlock_irqrestore(&div->lock, flags); in ccu_div_dbgfs_bit_set()
377 CCU_DIV_CLKDIV_MAX(div->mask)); in ccu_div_dbgfs_var_clkdiv_set()
378 data = ccu_div_prep(div->mask, val); in ccu_div_dbgfs_var_clkdiv_set()
380 spin_lock_irqsave(&div->lock, flags); in ccu_div_dbgfs_var_clkdiv_set()
381 regmap_update_bits(div->sys_regs, div->reg_ctl, div->mask, data); in ccu_div_dbgfs_var_clkdiv_set()
382 spin_unlock_irqrestore(&div->lock, flags); in ccu_div_dbgfs_var_clkdiv_set()
400 struct ccu_div *div = bit->div; in ccu_div_dbgfs_bit_get()
403 regmap_read(div->sys_regs, div->reg_ctl, &data); in ccu_div_dbgfs_bit_get()
404 *val = !!(data & bit->mask); in ccu_div_dbgfs_bit_get()
416 regmap_read(div->sys_regs, div->reg_ctl, &data); in ccu_div_dbgfs_var_clkdiv_get()
417 *val = ccu_div_get(div->mask, data); in ccu_div_dbgfs_var_clkdiv_get()
428 *val = div->divider; in ccu_div_dbgfs_fixed_clkdiv_get()
442 num += !!(div->flags & CLK_SET_RATE_GATE) + in ccu_div_var_debug_init()
443 !!(div->features & CCU_DIV_RESET_DOMAIN); in ccu_div_var_debug_init()
451 if (!(div->flags & CLK_SET_RATE_GATE) && in ccu_div_var_debug_init()
456 if (!(div->features & CCU_DIV_RESET_DOMAIN) && in ccu_div_var_debug_init()
467 if (div->features & CCU_DIV_LOCK_SHIFTED && in ccu_div_var_debug_init()
492 bit->div = div; in ccu_div_gate_debug_init()
493 debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit, in ccu_div_gate_debug_init()
510 bit->div = div; in ccu_div_buf_debug_init()
511 debugfs_create_file_unsafe(bit->name, ccu_div_dbgfs_mode, dentry, bit, in ccu_div_buf_debug_init()
581 return ERR_PTR(-EINVAL); in ccu_div_hw_register()
585 return ERR_PTR(-ENOMEM); in ccu_div_hw_register()
588 * Note since Baikal-T1 System Controller registers are MMIO-backed in ccu_div_hw_register()
592 div->hw.init = &hw_init; in ccu_div_hw_register()
593 div->id = div_init->id; in ccu_div_hw_register()
594 div->reg_ctl = div_init->base + CCU_DIV_CTL; in ccu_div_hw_register()
595 div->sys_regs = div_init->sys_regs; in ccu_div_hw_register()
596 div->flags = div_init->flags; in ccu_div_hw_register()
597 div->features = div_init->features; in ccu_div_hw_register()
598 spin_lock_init(&div->lock); in ccu_div_hw_register()
600 hw_init.name = div_init->name; in ccu_div_hw_register()
601 hw_init.flags = div_init->flags; in ccu_div_hw_register()
603 if (div_init->type == CCU_DIV_VAR) { in ccu_div_hw_register()
608 div->mask = CCU_DIV_CTL_CLKDIV_MASK(div_init->width); in ccu_div_hw_register()
609 } else if (div_init->type == CCU_DIV_GATE) { in ccu_div_hw_register()
611 div->divider = div_init->divider; in ccu_div_hw_register()
612 } else if (div_init->type == CCU_DIV_BUF) { in ccu_div_hw_register()
614 } else if (div_init->type == CCU_DIV_FIXED) { in ccu_div_hw_register()
616 div->divider = div_init->divider; in ccu_div_hw_register()
618 ret = -EINVAL; in ccu_div_hw_register()
622 if (!div_init->parent_name) { in ccu_div_hw_register()
623 ret = -EINVAL; in ccu_div_hw_register()
626 parent_data.fw_name = div_init->parent_name; in ccu_div_hw_register()
627 parent_data.name = div_init->parent_name; in ccu_div_hw_register()
631 ret = of_clk_hw_register(div_init->np, &div->hw); in ccu_div_hw_register()
645 clk_hw_unregister(&div->hw); in ccu_div_hw_unregister()