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