1*4882a593Smuzhiyun // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2018 BayLibre, SAS.
4*4882a593Smuzhiyun * Author: Jerome Brunet <jbrunet@baylibre.com>
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Sample clock generator divider:
7*4882a593Smuzhiyun * This HW divider gates with value 0 but is otherwise a zero based divider:
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * val >= 1
10*4882a593Smuzhiyun * divider = val + 1
11*4882a593Smuzhiyun *
12*4882a593Smuzhiyun * The duty cycle may also be set for the LR clock variant. The duty cycle
13*4882a593Smuzhiyun * ratio is:
14*4882a593Smuzhiyun *
15*4882a593Smuzhiyun * hi = [0 - val]
16*4882a593Smuzhiyun * duty_cycle = (1 + hi) / (1 + val)
17*4882a593Smuzhiyun */
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun #include <linux/clk-provider.h>
20*4882a593Smuzhiyun #include <linux/module.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #include "clk-regmap.h"
23*4882a593Smuzhiyun #include "sclk-div.h"
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun static inline struct meson_sclk_div_data *
meson_sclk_div_data(struct clk_regmap * clk)26*4882a593Smuzhiyun meson_sclk_div_data(struct clk_regmap *clk)
27*4882a593Smuzhiyun {
28*4882a593Smuzhiyun return (struct meson_sclk_div_data *)clk->data;
29*4882a593Smuzhiyun }
30*4882a593Smuzhiyun
sclk_div_maxval(struct meson_sclk_div_data * sclk)31*4882a593Smuzhiyun static int sclk_div_maxval(struct meson_sclk_div_data *sclk)
32*4882a593Smuzhiyun {
33*4882a593Smuzhiyun return (1 << sclk->div.width) - 1;
34*4882a593Smuzhiyun }
35*4882a593Smuzhiyun
sclk_div_maxdiv(struct meson_sclk_div_data * sclk)36*4882a593Smuzhiyun static int sclk_div_maxdiv(struct meson_sclk_div_data *sclk)
37*4882a593Smuzhiyun {
38*4882a593Smuzhiyun return sclk_div_maxval(sclk) + 1;
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun
sclk_div_getdiv(struct clk_hw * hw,unsigned long rate,unsigned long prate,int maxdiv)41*4882a593Smuzhiyun static int sclk_div_getdiv(struct clk_hw *hw, unsigned long rate,
42*4882a593Smuzhiyun unsigned long prate, int maxdiv)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun int div = DIV_ROUND_CLOSEST_ULL((u64)prate, rate);
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun return clamp(div, 2, maxdiv);
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun
sclk_div_bestdiv(struct clk_hw * hw,unsigned long rate,unsigned long * prate,struct meson_sclk_div_data * sclk)49*4882a593Smuzhiyun static int sclk_div_bestdiv(struct clk_hw *hw, unsigned long rate,
50*4882a593Smuzhiyun unsigned long *prate,
51*4882a593Smuzhiyun struct meson_sclk_div_data *sclk)
52*4882a593Smuzhiyun {
53*4882a593Smuzhiyun struct clk_hw *parent = clk_hw_get_parent(hw);
54*4882a593Smuzhiyun int bestdiv = 0, i;
55*4882a593Smuzhiyun unsigned long maxdiv, now, parent_now;
56*4882a593Smuzhiyun unsigned long best = 0, best_parent = 0;
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun if (!rate)
59*4882a593Smuzhiyun rate = 1;
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun maxdiv = sclk_div_maxdiv(sclk);
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT))
64*4882a593Smuzhiyun return sclk_div_getdiv(hw, rate, *prate, maxdiv);
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun /*
67*4882a593Smuzhiyun * The maximum divider we can use without overflowing
68*4882a593Smuzhiyun * unsigned long in rate * i below
69*4882a593Smuzhiyun */
70*4882a593Smuzhiyun maxdiv = min(ULONG_MAX / rate, maxdiv);
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun for (i = 2; i <= maxdiv; i++) {
73*4882a593Smuzhiyun /*
74*4882a593Smuzhiyun * It's the most ideal case if the requested rate can be
75*4882a593Smuzhiyun * divided from parent clock without needing to change
76*4882a593Smuzhiyun * parent rate, so return the divider immediately.
77*4882a593Smuzhiyun */
78*4882a593Smuzhiyun if (rate * i == *prate)
79*4882a593Smuzhiyun return i;
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun parent_now = clk_hw_round_rate(parent, rate * i);
82*4882a593Smuzhiyun now = DIV_ROUND_UP_ULL((u64)parent_now, i);
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun if (abs(rate - now) < abs(rate - best)) {
85*4882a593Smuzhiyun bestdiv = i;
86*4882a593Smuzhiyun best = now;
87*4882a593Smuzhiyun best_parent = parent_now;
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun if (!bestdiv)
92*4882a593Smuzhiyun bestdiv = sclk_div_maxdiv(sclk);
93*4882a593Smuzhiyun else
94*4882a593Smuzhiyun *prate = best_parent;
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun return bestdiv;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
sclk_div_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * prate)99*4882a593Smuzhiyun static long sclk_div_round_rate(struct clk_hw *hw, unsigned long rate,
100*4882a593Smuzhiyun unsigned long *prate)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun struct clk_regmap *clk = to_clk_regmap(hw);
103*4882a593Smuzhiyun struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
104*4882a593Smuzhiyun int div;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun div = sclk_div_bestdiv(hw, rate, prate, sclk);
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun return DIV_ROUND_UP_ULL((u64)*prate, div);
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun
sclk_apply_ratio(struct clk_regmap * clk,struct meson_sclk_div_data * sclk)111*4882a593Smuzhiyun static void sclk_apply_ratio(struct clk_regmap *clk,
112*4882a593Smuzhiyun struct meson_sclk_div_data *sclk)
113*4882a593Smuzhiyun {
114*4882a593Smuzhiyun unsigned int hi = DIV_ROUND_CLOSEST(sclk->cached_div *
115*4882a593Smuzhiyun sclk->cached_duty.num,
116*4882a593Smuzhiyun sclk->cached_duty.den);
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun if (hi)
119*4882a593Smuzhiyun hi -= 1;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun meson_parm_write(clk->map, &sclk->hi, hi);
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun
sclk_div_set_duty_cycle(struct clk_hw * hw,struct clk_duty * duty)124*4882a593Smuzhiyun static int sclk_div_set_duty_cycle(struct clk_hw *hw,
125*4882a593Smuzhiyun struct clk_duty *duty)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun struct clk_regmap *clk = to_clk_regmap(hw);
128*4882a593Smuzhiyun struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun if (MESON_PARM_APPLICABLE(&sclk->hi)) {
131*4882a593Smuzhiyun memcpy(&sclk->cached_duty, duty, sizeof(*duty));
132*4882a593Smuzhiyun sclk_apply_ratio(clk, sclk);
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun return 0;
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun
sclk_div_get_duty_cycle(struct clk_hw * hw,struct clk_duty * duty)138*4882a593Smuzhiyun static int sclk_div_get_duty_cycle(struct clk_hw *hw,
139*4882a593Smuzhiyun struct clk_duty *duty)
140*4882a593Smuzhiyun {
141*4882a593Smuzhiyun struct clk_regmap *clk = to_clk_regmap(hw);
142*4882a593Smuzhiyun struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
143*4882a593Smuzhiyun int hi;
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun if (!MESON_PARM_APPLICABLE(&sclk->hi)) {
146*4882a593Smuzhiyun duty->num = 1;
147*4882a593Smuzhiyun duty->den = 2;
148*4882a593Smuzhiyun return 0;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun hi = meson_parm_read(clk->map, &sclk->hi);
152*4882a593Smuzhiyun duty->num = hi + 1;
153*4882a593Smuzhiyun duty->den = sclk->cached_div;
154*4882a593Smuzhiyun return 0;
155*4882a593Smuzhiyun }
156*4882a593Smuzhiyun
sclk_apply_divider(struct clk_regmap * clk,struct meson_sclk_div_data * sclk)157*4882a593Smuzhiyun static void sclk_apply_divider(struct clk_regmap *clk,
158*4882a593Smuzhiyun struct meson_sclk_div_data *sclk)
159*4882a593Smuzhiyun {
160*4882a593Smuzhiyun if (MESON_PARM_APPLICABLE(&sclk->hi))
161*4882a593Smuzhiyun sclk_apply_ratio(clk, sclk);
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun meson_parm_write(clk->map, &sclk->div, sclk->cached_div - 1);
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun
sclk_div_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long prate)166*4882a593Smuzhiyun static int sclk_div_set_rate(struct clk_hw *hw, unsigned long rate,
167*4882a593Smuzhiyun unsigned long prate)
168*4882a593Smuzhiyun {
169*4882a593Smuzhiyun struct clk_regmap *clk = to_clk_regmap(hw);
170*4882a593Smuzhiyun struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
171*4882a593Smuzhiyun unsigned long maxdiv = sclk_div_maxdiv(sclk);
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun sclk->cached_div = sclk_div_getdiv(hw, rate, prate, maxdiv);
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun if (clk_hw_is_enabled(hw))
176*4882a593Smuzhiyun sclk_apply_divider(clk, sclk);
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun return 0;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
sclk_div_recalc_rate(struct clk_hw * hw,unsigned long prate)181*4882a593Smuzhiyun static unsigned long sclk_div_recalc_rate(struct clk_hw *hw,
182*4882a593Smuzhiyun unsigned long prate)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun struct clk_regmap *clk = to_clk_regmap(hw);
185*4882a593Smuzhiyun struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun return DIV_ROUND_UP_ULL((u64)prate, sclk->cached_div);
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun
sclk_div_enable(struct clk_hw * hw)190*4882a593Smuzhiyun static int sclk_div_enable(struct clk_hw *hw)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun struct clk_regmap *clk = to_clk_regmap(hw);
193*4882a593Smuzhiyun struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun sclk_apply_divider(clk, sclk);
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun return 0;
198*4882a593Smuzhiyun }
199*4882a593Smuzhiyun
sclk_div_disable(struct clk_hw * hw)200*4882a593Smuzhiyun static void sclk_div_disable(struct clk_hw *hw)
201*4882a593Smuzhiyun {
202*4882a593Smuzhiyun struct clk_regmap *clk = to_clk_regmap(hw);
203*4882a593Smuzhiyun struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun meson_parm_write(clk->map, &sclk->div, 0);
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun
sclk_div_is_enabled(struct clk_hw * hw)208*4882a593Smuzhiyun static int sclk_div_is_enabled(struct clk_hw *hw)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun struct clk_regmap *clk = to_clk_regmap(hw);
211*4882a593Smuzhiyun struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun if (meson_parm_read(clk->map, &sclk->div))
214*4882a593Smuzhiyun return 1;
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun return 0;
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun
sclk_div_init(struct clk_hw * hw)219*4882a593Smuzhiyun static int sclk_div_init(struct clk_hw *hw)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun struct clk_regmap *clk = to_clk_regmap(hw);
222*4882a593Smuzhiyun struct meson_sclk_div_data *sclk = meson_sclk_div_data(clk);
223*4882a593Smuzhiyun unsigned int val;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun val = meson_parm_read(clk->map, &sclk->div);
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun /* if the divider is initially disabled, assume max */
228*4882a593Smuzhiyun if (!val)
229*4882a593Smuzhiyun sclk->cached_div = sclk_div_maxdiv(sclk);
230*4882a593Smuzhiyun else
231*4882a593Smuzhiyun sclk->cached_div = val + 1;
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun sclk_div_get_duty_cycle(hw, &sclk->cached_duty);
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun return 0;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun const struct clk_ops meson_sclk_div_ops = {
239*4882a593Smuzhiyun .recalc_rate = sclk_div_recalc_rate,
240*4882a593Smuzhiyun .round_rate = sclk_div_round_rate,
241*4882a593Smuzhiyun .set_rate = sclk_div_set_rate,
242*4882a593Smuzhiyun .enable = sclk_div_enable,
243*4882a593Smuzhiyun .disable = sclk_div_disable,
244*4882a593Smuzhiyun .is_enabled = sclk_div_is_enabled,
245*4882a593Smuzhiyun .get_duty_cycle = sclk_div_get_duty_cycle,
246*4882a593Smuzhiyun .set_duty_cycle = sclk_div_set_duty_cycle,
247*4882a593Smuzhiyun .init = sclk_div_init,
248*4882a593Smuzhiyun };
249*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(meson_sclk_div_ops);
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun MODULE_DESCRIPTION("Amlogic Sample divider driver");
252*4882a593Smuzhiyun MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
253*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
254