1e3f05d3bSChunyan Zhang // SPDX-License-Identifier: GPL-2.0
2e3f05d3bSChunyan Zhang //
3e3f05d3bSChunyan Zhang // Spreadtrum divider clock driver
4e3f05d3bSChunyan Zhang //
5e3f05d3bSChunyan Zhang // Copyright (C) 2017 Spreadtrum, Inc.
6e3f05d3bSChunyan Zhang // Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
7e3f05d3bSChunyan Zhang
8e3f05d3bSChunyan Zhang #include <linux/clk-provider.h>
9e3f05d3bSChunyan Zhang
10e3f05d3bSChunyan Zhang #include "div.h"
11e3f05d3bSChunyan Zhang
sprd_div_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)12e3f05d3bSChunyan Zhang static long sprd_div_round_rate(struct clk_hw *hw, unsigned long rate,
13e3f05d3bSChunyan Zhang unsigned long *parent_rate)
14e3f05d3bSChunyan Zhang {
15e3f05d3bSChunyan Zhang struct sprd_div *cd = hw_to_sprd_div(hw);
16e3f05d3bSChunyan Zhang
17587dd448SStephen Boyd return divider_round_rate(&cd->common.hw, rate, parent_rate, NULL,
18587dd448SStephen Boyd cd->div.width, 0);
19e3f05d3bSChunyan Zhang }
20e3f05d3bSChunyan Zhang
sprd_div_helper_recalc_rate(struct sprd_clk_common * common,const struct sprd_div_internal * div,unsigned long parent_rate)21e3f05d3bSChunyan Zhang unsigned long sprd_div_helper_recalc_rate(struct sprd_clk_common *common,
22e3f05d3bSChunyan Zhang const struct sprd_div_internal *div,
23e3f05d3bSChunyan Zhang unsigned long parent_rate)
24e3f05d3bSChunyan Zhang {
25e3f05d3bSChunyan Zhang unsigned long val;
26e3f05d3bSChunyan Zhang unsigned int reg;
27e3f05d3bSChunyan Zhang
28*48a8748fSZhifeng Tang regmap_read(common->regmap, common->reg + div->offset, ®);
29e3f05d3bSChunyan Zhang val = reg >> div->shift;
30e3f05d3bSChunyan Zhang val &= (1 << div->width) - 1;
31e3f05d3bSChunyan Zhang
32716d9b1dSStephen Boyd return divider_recalc_rate(&common->hw, parent_rate, val, NULL, 0,
33716d9b1dSStephen Boyd div->width);
34e3f05d3bSChunyan Zhang }
35e3f05d3bSChunyan Zhang EXPORT_SYMBOL_GPL(sprd_div_helper_recalc_rate);
36e3f05d3bSChunyan Zhang
sprd_div_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)37e3f05d3bSChunyan Zhang static unsigned long sprd_div_recalc_rate(struct clk_hw *hw,
38e3f05d3bSChunyan Zhang unsigned long parent_rate)
39e3f05d3bSChunyan Zhang {
40e3f05d3bSChunyan Zhang struct sprd_div *cd = hw_to_sprd_div(hw);
41e3f05d3bSChunyan Zhang
42e3f05d3bSChunyan Zhang return sprd_div_helper_recalc_rate(&cd->common, &cd->div, parent_rate);
43e3f05d3bSChunyan Zhang }
44e3f05d3bSChunyan Zhang
sprd_div_helper_set_rate(const struct sprd_clk_common * common,const struct sprd_div_internal * div,unsigned long rate,unsigned long parent_rate)45e3f05d3bSChunyan Zhang int sprd_div_helper_set_rate(const struct sprd_clk_common *common,
46e3f05d3bSChunyan Zhang const struct sprd_div_internal *div,
47e3f05d3bSChunyan Zhang unsigned long rate,
48e3f05d3bSChunyan Zhang unsigned long parent_rate)
49e3f05d3bSChunyan Zhang {
50e3f05d3bSChunyan Zhang unsigned long val;
51e3f05d3bSChunyan Zhang unsigned int reg;
52e3f05d3bSChunyan Zhang
53e3f05d3bSChunyan Zhang val = divider_get_val(rate, parent_rate, NULL,
54e3f05d3bSChunyan Zhang div->width, 0);
55e3f05d3bSChunyan Zhang
56*48a8748fSZhifeng Tang regmap_read(common->regmap, common->reg + div->offset, ®);
57e3f05d3bSChunyan Zhang reg &= ~GENMASK(div->width + div->shift - 1, div->shift);
58e3f05d3bSChunyan Zhang
59*48a8748fSZhifeng Tang regmap_write(common->regmap, common->reg + div->offset,
60e3f05d3bSChunyan Zhang reg | (val << div->shift));
61e3f05d3bSChunyan Zhang
62e3f05d3bSChunyan Zhang return 0;
63e3f05d3bSChunyan Zhang
64e3f05d3bSChunyan Zhang }
65e3f05d3bSChunyan Zhang EXPORT_SYMBOL_GPL(sprd_div_helper_set_rate);
66e3f05d3bSChunyan Zhang
sprd_div_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)67e3f05d3bSChunyan Zhang static int sprd_div_set_rate(struct clk_hw *hw, unsigned long rate,
68e3f05d3bSChunyan Zhang unsigned long parent_rate)
69e3f05d3bSChunyan Zhang {
70e3f05d3bSChunyan Zhang struct sprd_div *cd = hw_to_sprd_div(hw);
71e3f05d3bSChunyan Zhang
72e3f05d3bSChunyan Zhang return sprd_div_helper_set_rate(&cd->common, &cd->div,
73e3f05d3bSChunyan Zhang rate, parent_rate);
74e3f05d3bSChunyan Zhang }
75e3f05d3bSChunyan Zhang
76e3f05d3bSChunyan Zhang const struct clk_ops sprd_div_ops = {
77e3f05d3bSChunyan Zhang .recalc_rate = sprd_div_recalc_rate,
78e3f05d3bSChunyan Zhang .round_rate = sprd_div_round_rate,
79e3f05d3bSChunyan Zhang .set_rate = sprd_div_set_rate,
80e3f05d3bSChunyan Zhang };
81e3f05d3bSChunyan Zhang EXPORT_SYMBOL_GPL(sprd_div_ops);
82