1*71ffeafbSRoman Beranek // SPDX-License-Identifier: GPL-2.0-or-later
2*71ffeafbSRoman Beranek /*
3*71ffeafbSRoman Beranek * Copyright (C) 2016 Free Electrons
4*71ffeafbSRoman Beranek * Copyright (C) 2016 NextThing Co
5*71ffeafbSRoman Beranek *
6*71ffeafbSRoman Beranek * Maxime Ripard <maxime.ripard@free-electrons.com>
7*71ffeafbSRoman Beranek */
8*71ffeafbSRoman Beranek
9*71ffeafbSRoman Beranek #include <linux/clk-provider.h>
10*71ffeafbSRoman Beranek #include <linux/regmap.h>
11*71ffeafbSRoman Beranek
12*71ffeafbSRoman Beranek #include "sun4i_tcon.h"
13*71ffeafbSRoman Beranek #include "sun4i_tcon_dclk.h"
14*71ffeafbSRoman Beranek
15*71ffeafbSRoman Beranek struct sun4i_dclk {
16*71ffeafbSRoman Beranek struct clk_hw hw;
17*71ffeafbSRoman Beranek struct regmap *regmap;
18*71ffeafbSRoman Beranek struct sun4i_tcon *tcon;
19*71ffeafbSRoman Beranek };
20*71ffeafbSRoman Beranek
hw_to_dclk(struct clk_hw * hw)21*71ffeafbSRoman Beranek static inline struct sun4i_dclk *hw_to_dclk(struct clk_hw *hw)
22*71ffeafbSRoman Beranek {
23*71ffeafbSRoman Beranek return container_of(hw, struct sun4i_dclk, hw);
24*71ffeafbSRoman Beranek }
25*71ffeafbSRoman Beranek
sun4i_dclk_disable(struct clk_hw * hw)26*71ffeafbSRoman Beranek static void sun4i_dclk_disable(struct clk_hw *hw)
27*71ffeafbSRoman Beranek {
28*71ffeafbSRoman Beranek struct sun4i_dclk *dclk = hw_to_dclk(hw);
29*71ffeafbSRoman Beranek
30*71ffeafbSRoman Beranek regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
31*71ffeafbSRoman Beranek BIT(SUN4I_TCON0_DCLK_GATE_BIT), 0);
32*71ffeafbSRoman Beranek }
33*71ffeafbSRoman Beranek
sun4i_dclk_enable(struct clk_hw * hw)34*71ffeafbSRoman Beranek static int sun4i_dclk_enable(struct clk_hw *hw)
35*71ffeafbSRoman Beranek {
36*71ffeafbSRoman Beranek struct sun4i_dclk *dclk = hw_to_dclk(hw);
37*71ffeafbSRoman Beranek
38*71ffeafbSRoman Beranek return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
39*71ffeafbSRoman Beranek BIT(SUN4I_TCON0_DCLK_GATE_BIT),
40*71ffeafbSRoman Beranek BIT(SUN4I_TCON0_DCLK_GATE_BIT));
41*71ffeafbSRoman Beranek }
42*71ffeafbSRoman Beranek
sun4i_dclk_is_enabled(struct clk_hw * hw)43*71ffeafbSRoman Beranek static int sun4i_dclk_is_enabled(struct clk_hw *hw)
44*71ffeafbSRoman Beranek {
45*71ffeafbSRoman Beranek struct sun4i_dclk *dclk = hw_to_dclk(hw);
46*71ffeafbSRoman Beranek u32 val;
47*71ffeafbSRoman Beranek
48*71ffeafbSRoman Beranek regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val);
49*71ffeafbSRoman Beranek
50*71ffeafbSRoman Beranek return val & BIT(SUN4I_TCON0_DCLK_GATE_BIT);
51*71ffeafbSRoman Beranek }
52*71ffeafbSRoman Beranek
sun4i_dclk_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)53*71ffeafbSRoman Beranek static unsigned long sun4i_dclk_recalc_rate(struct clk_hw *hw,
54*71ffeafbSRoman Beranek unsigned long parent_rate)
55*71ffeafbSRoman Beranek {
56*71ffeafbSRoman Beranek struct sun4i_dclk *dclk = hw_to_dclk(hw);
57*71ffeafbSRoman Beranek u32 val;
58*71ffeafbSRoman Beranek
59*71ffeafbSRoman Beranek regmap_read(dclk->regmap, SUN4I_TCON0_DCLK_REG, &val);
60*71ffeafbSRoman Beranek
61*71ffeafbSRoman Beranek val >>= SUN4I_TCON0_DCLK_DIV_SHIFT;
62*71ffeafbSRoman Beranek val &= (1 << SUN4I_TCON0_DCLK_DIV_WIDTH) - 1;
63*71ffeafbSRoman Beranek
64*71ffeafbSRoman Beranek if (!val)
65*71ffeafbSRoman Beranek val = 1;
66*71ffeafbSRoman Beranek
67*71ffeafbSRoman Beranek return parent_rate / val;
68*71ffeafbSRoman Beranek }
69*71ffeafbSRoman Beranek
sun4i_dclk_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)70*71ffeafbSRoman Beranek static long sun4i_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
71*71ffeafbSRoman Beranek unsigned long *parent_rate)
72*71ffeafbSRoman Beranek {
73*71ffeafbSRoman Beranek struct sun4i_dclk *dclk = hw_to_dclk(hw);
74*71ffeafbSRoman Beranek struct sun4i_tcon *tcon = dclk->tcon;
75*71ffeafbSRoman Beranek unsigned long best_parent = 0;
76*71ffeafbSRoman Beranek u8 best_div = 1;
77*71ffeafbSRoman Beranek int i;
78*71ffeafbSRoman Beranek
79*71ffeafbSRoman Beranek for (i = tcon->dclk_min_div; i <= tcon->dclk_max_div; i++) {
80*71ffeafbSRoman Beranek u64 ideal = (u64)rate * i;
81*71ffeafbSRoman Beranek unsigned long rounded;
82*71ffeafbSRoman Beranek
83*71ffeafbSRoman Beranek /*
84*71ffeafbSRoman Beranek * ideal has overflowed the max value that can be stored in an
85*71ffeafbSRoman Beranek * unsigned long, and every clk operation we might do on a
86*71ffeafbSRoman Beranek * truncated u64 value will give us incorrect results.
87*71ffeafbSRoman Beranek * Let's just stop there since bigger dividers will result in
88*71ffeafbSRoman Beranek * the same overflow issue.
89*71ffeafbSRoman Beranek */
90*71ffeafbSRoman Beranek if (ideal > ULONG_MAX)
91*71ffeafbSRoman Beranek goto out;
92*71ffeafbSRoman Beranek
93*71ffeafbSRoman Beranek rounded = clk_hw_round_rate(clk_hw_get_parent(hw),
94*71ffeafbSRoman Beranek ideal);
95*71ffeafbSRoman Beranek
96*71ffeafbSRoman Beranek if (rounded == ideal) {
97*71ffeafbSRoman Beranek best_parent = rounded;
98*71ffeafbSRoman Beranek best_div = i;
99*71ffeafbSRoman Beranek goto out;
100*71ffeafbSRoman Beranek }
101*71ffeafbSRoman Beranek
102*71ffeafbSRoman Beranek if (abs(rate - rounded / i) <
103*71ffeafbSRoman Beranek abs(rate - best_parent / best_div)) {
104*71ffeafbSRoman Beranek best_parent = rounded;
105*71ffeafbSRoman Beranek best_div = i;
106*71ffeafbSRoman Beranek }
107*71ffeafbSRoman Beranek }
108*71ffeafbSRoman Beranek
109*71ffeafbSRoman Beranek out:
110*71ffeafbSRoman Beranek *parent_rate = best_parent;
111*71ffeafbSRoman Beranek
112*71ffeafbSRoman Beranek return best_parent / best_div;
113*71ffeafbSRoman Beranek }
114*71ffeafbSRoman Beranek
sun4i_dclk_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)115*71ffeafbSRoman Beranek static int sun4i_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
116*71ffeafbSRoman Beranek unsigned long parent_rate)
117*71ffeafbSRoman Beranek {
118*71ffeafbSRoman Beranek struct sun4i_dclk *dclk = hw_to_dclk(hw);
119*71ffeafbSRoman Beranek u8 div = parent_rate / rate;
120*71ffeafbSRoman Beranek
121*71ffeafbSRoman Beranek return regmap_update_bits(dclk->regmap, SUN4I_TCON0_DCLK_REG,
122*71ffeafbSRoman Beranek GENMASK(6, 0), div);
123*71ffeafbSRoman Beranek }
124*71ffeafbSRoman Beranek
sun4i_dclk_get_phase(struct clk_hw * hw)125*71ffeafbSRoman Beranek static int sun4i_dclk_get_phase(struct clk_hw *hw)
126*71ffeafbSRoman Beranek {
127*71ffeafbSRoman Beranek struct sun4i_dclk *dclk = hw_to_dclk(hw);
128*71ffeafbSRoman Beranek u32 val;
129*71ffeafbSRoman Beranek
130*71ffeafbSRoman Beranek regmap_read(dclk->regmap, SUN4I_TCON0_IO_POL_REG, &val);
131*71ffeafbSRoman Beranek
132*71ffeafbSRoman Beranek val >>= 28;
133*71ffeafbSRoman Beranek val &= 3;
134*71ffeafbSRoman Beranek
135*71ffeafbSRoman Beranek return val * 120;
136*71ffeafbSRoman Beranek }
137*71ffeafbSRoman Beranek
sun4i_dclk_set_phase(struct clk_hw * hw,int degrees)138*71ffeafbSRoman Beranek static int sun4i_dclk_set_phase(struct clk_hw *hw, int degrees)
139*71ffeafbSRoman Beranek {
140*71ffeafbSRoman Beranek struct sun4i_dclk *dclk = hw_to_dclk(hw);
141*71ffeafbSRoman Beranek u32 val = degrees / 120;
142*71ffeafbSRoman Beranek
143*71ffeafbSRoman Beranek val <<= 28;
144*71ffeafbSRoman Beranek
145*71ffeafbSRoman Beranek regmap_update_bits(dclk->regmap, SUN4I_TCON0_IO_POL_REG,
146*71ffeafbSRoman Beranek GENMASK(29, 28),
147*71ffeafbSRoman Beranek val);
148*71ffeafbSRoman Beranek
149*71ffeafbSRoman Beranek return 0;
150*71ffeafbSRoman Beranek }
151*71ffeafbSRoman Beranek
152*71ffeafbSRoman Beranek static const struct clk_ops sun4i_dclk_ops = {
153*71ffeafbSRoman Beranek .disable = sun4i_dclk_disable,
154*71ffeafbSRoman Beranek .enable = sun4i_dclk_enable,
155*71ffeafbSRoman Beranek .is_enabled = sun4i_dclk_is_enabled,
156*71ffeafbSRoman Beranek
157*71ffeafbSRoman Beranek .recalc_rate = sun4i_dclk_recalc_rate,
158*71ffeafbSRoman Beranek .round_rate = sun4i_dclk_round_rate,
159*71ffeafbSRoman Beranek .set_rate = sun4i_dclk_set_rate,
160*71ffeafbSRoman Beranek
161*71ffeafbSRoman Beranek .get_phase = sun4i_dclk_get_phase,
162*71ffeafbSRoman Beranek .set_phase = sun4i_dclk_set_phase,
163*71ffeafbSRoman Beranek };
164*71ffeafbSRoman Beranek
sun4i_dclk_create(struct device * dev,struct sun4i_tcon * tcon)165*71ffeafbSRoman Beranek int sun4i_dclk_create(struct device *dev, struct sun4i_tcon *tcon)
166*71ffeafbSRoman Beranek {
167*71ffeafbSRoman Beranek const char *clk_name, *parent_name;
168*71ffeafbSRoman Beranek struct clk_init_data init;
169*71ffeafbSRoman Beranek struct sun4i_dclk *dclk;
170*71ffeafbSRoman Beranek int ret;
171*71ffeafbSRoman Beranek
172*71ffeafbSRoman Beranek parent_name = __clk_get_name(tcon->sclk0);
173*71ffeafbSRoman Beranek ret = of_property_read_string_index(dev->of_node,
174*71ffeafbSRoman Beranek "clock-output-names", 0,
175*71ffeafbSRoman Beranek &clk_name);
176*71ffeafbSRoman Beranek if (ret)
177*71ffeafbSRoman Beranek return ret;
178*71ffeafbSRoman Beranek
179*71ffeafbSRoman Beranek dclk = devm_kzalloc(dev, sizeof(*dclk), GFP_KERNEL);
180*71ffeafbSRoman Beranek if (!dclk)
181*71ffeafbSRoman Beranek return -ENOMEM;
182*71ffeafbSRoman Beranek dclk->tcon = tcon;
183*71ffeafbSRoman Beranek
184*71ffeafbSRoman Beranek init.name = clk_name;
185*71ffeafbSRoman Beranek init.ops = &sun4i_dclk_ops;
186*71ffeafbSRoman Beranek init.parent_names = &parent_name;
187*71ffeafbSRoman Beranek init.num_parents = 1;
188*71ffeafbSRoman Beranek init.flags = CLK_SET_RATE_PARENT;
189*71ffeafbSRoman Beranek
190*71ffeafbSRoman Beranek dclk->regmap = tcon->regs;
191*71ffeafbSRoman Beranek dclk->hw.init = &init;
192*71ffeafbSRoman Beranek
193*71ffeafbSRoman Beranek tcon->dclk = clk_register(dev, &dclk->hw);
194*71ffeafbSRoman Beranek if (IS_ERR(tcon->dclk))
195*71ffeafbSRoman Beranek return PTR_ERR(tcon->dclk);
196*71ffeafbSRoman Beranek
197*71ffeafbSRoman Beranek return 0;
198*71ffeafbSRoman Beranek }
199*71ffeafbSRoman Beranek EXPORT_SYMBOL(sun4i_dclk_create);
200*71ffeafbSRoman Beranek
sun4i_dclk_free(struct sun4i_tcon * tcon)201*71ffeafbSRoman Beranek int sun4i_dclk_free(struct sun4i_tcon *tcon)
202*71ffeafbSRoman Beranek {
203*71ffeafbSRoman Beranek clk_unregister(tcon->dclk);
204*71ffeafbSRoman Beranek return 0;
205*71ffeafbSRoman Beranek }
206*71ffeafbSRoman Beranek EXPORT_SYMBOL(sun4i_dclk_free);
207