| // SPDX-License-Identifier: GPL-2.0+ |
| // |
| // OWL divider clock driver |
| // |
| // Copyright (c) 2014 Actions Semi Inc. |
| // Author: David Liu <liuwei@actions-semi.com> |
| // |
| // Copyright (c) 2018 Linaro Ltd. |
| // Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> |
| |
| #include <linux/clk-provider.h> |
| #include <linux/regmap.h> |
| |
| #include "owl-divider.h" |
| |
| long owl_divider_helper_round_rate(struct owl_clk_common *common, |
| const struct owl_divider_hw *div_hw, |
| unsigned long rate, |
| unsigned long *parent_rate) |
| { |
| return divider_round_rate(&common->hw, rate, parent_rate, |
| div_hw->table, div_hw->width, |
| div_hw->div_flags); |
| } |
| |
| static long owl_divider_round_rate(struct clk_hw *hw, unsigned long rate, |
| unsigned long *parent_rate) |
| { |
| struct owl_divider *div = hw_to_owl_divider(hw); |
| |
| return owl_divider_helper_round_rate(&div->common, &div->div_hw, |
| rate, parent_rate); |
| } |
| |
| unsigned long owl_divider_helper_recalc_rate(struct owl_clk_common *common, |
| const struct owl_divider_hw *div_hw, |
| unsigned long parent_rate) |
| { |
| unsigned long val; |
| unsigned int reg; |
| |
| regmap_read(common->regmap, div_hw->reg, ®); |
| val = reg >> div_hw->shift; |
| val &= (1 << div_hw->width) - 1; |
| |
| return divider_recalc_rate(&common->hw, parent_rate, |
| val, div_hw->table, |
| div_hw->div_flags, |
| div_hw->width); |
| } |
| |
| static unsigned long owl_divider_recalc_rate(struct clk_hw *hw, |
| unsigned long parent_rate) |
| { |
| struct owl_divider *div = hw_to_owl_divider(hw); |
| |
| return owl_divider_helper_recalc_rate(&div->common, |
| &div->div_hw, parent_rate); |
| } |
| |
| int owl_divider_helper_set_rate(const struct owl_clk_common *common, |
| const struct owl_divider_hw *div_hw, |
| unsigned long rate, |
| unsigned long parent_rate) |
| { |
| unsigned long val; |
| unsigned int reg; |
| |
| val = divider_get_val(rate, parent_rate, div_hw->table, |
| div_hw->width, 0); |
| |
| regmap_read(common->regmap, div_hw->reg, ®); |
| reg &= ~GENMASK(div_hw->width + div_hw->shift - 1, div_hw->shift); |
| |
| regmap_write(common->regmap, div_hw->reg, |
| reg | (val << div_hw->shift)); |
| |
| return 0; |
| } |
| |
| static int owl_divider_set_rate(struct clk_hw *hw, unsigned long rate, |
| unsigned long parent_rate) |
| { |
| struct owl_divider *div = hw_to_owl_divider(hw); |
| |
| return owl_divider_helper_set_rate(&div->common, &div->div_hw, |
| rate, parent_rate); |
| } |
| |
| const struct clk_ops owl_divider_ops = { |
| .recalc_rate = owl_divider_recalc_rate, |
| .round_rate = owl_divider_round_rate, |
| .set_rate = owl_divider_set_rate, |
| }; |