| // SPDX-License-Identifier: GPL-2.0 |
| // |
| // Spreadtrum divider clock driver |
| // |
| // Copyright (C) 2017 Spreadtrum, Inc. |
| // Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com> |
| |
| #include <linux/clk-provider.h> |
| |
| #include "div.h" |
| |
| static long sprd_div_round_rate(struct clk_hw *hw, unsigned long rate, |
| unsigned long *parent_rate) |
| { |
| struct sprd_div *cd = hw_to_sprd_div(hw); |
| |
| return divider_round_rate(&cd->common.hw, rate, parent_rate, NULL, |
| cd->div.width, 0); |
| } |
| |
| unsigned long sprd_div_helper_recalc_rate(struct sprd_clk_common *common, |
| const struct sprd_div_internal *div, |
| unsigned long parent_rate) |
| { |
| unsigned long val; |
| unsigned int reg; |
| |
| regmap_read(common->regmap, common->reg + div->offset, ®); |
| val = reg >> div->shift; |
| val &= (1 << div->width) - 1; |
| |
| return divider_recalc_rate(&common->hw, parent_rate, val, NULL, 0, |
| div->width); |
| } |
| EXPORT_SYMBOL_GPL(sprd_div_helper_recalc_rate); |
| |
| static unsigned long sprd_div_recalc_rate(struct clk_hw *hw, |
| unsigned long parent_rate) |
| { |
| struct sprd_div *cd = hw_to_sprd_div(hw); |
| |
| return sprd_div_helper_recalc_rate(&cd->common, &cd->div, parent_rate); |
| } |
| |
| int sprd_div_helper_set_rate(const struct sprd_clk_common *common, |
| const struct sprd_div_internal *div, |
| unsigned long rate, |
| unsigned long parent_rate) |
| { |
| unsigned long val; |
| unsigned int reg; |
| |
| val = divider_get_val(rate, parent_rate, NULL, |
| div->width, 0); |
| |
| regmap_read(common->regmap, common->reg + div->offset, ®); |
| reg &= ~GENMASK(div->width + div->shift - 1, div->shift); |
| |
| regmap_write(common->regmap, common->reg + div->offset, |
| reg | (val << div->shift)); |
| |
| return 0; |
| |
| } |
| EXPORT_SYMBOL_GPL(sprd_div_helper_set_rate); |
| |
| static int sprd_div_set_rate(struct clk_hw *hw, unsigned long rate, |
| unsigned long parent_rate) |
| { |
| struct sprd_div *cd = hw_to_sprd_div(hw); |
| |
| return sprd_div_helper_set_rate(&cd->common, &cd->div, |
| rate, parent_rate); |
| } |
| |
| const struct clk_ops sprd_div_ops = { |
| .recalc_rate = sprd_div_recalc_rate, |
| .round_rate = sprd_div_round_rate, |
| .set_rate = sprd_div_set_rate, |
| }; |
| EXPORT_SYMBOL_GPL(sprd_div_ops); |