1 // SPDX-License-Identifier: GPL-2.0-only
5 * Copyright (C) ST-Microelectronics SA 2013
6 * Author: Maxime Coquelin <maxime.coquelin@st.com> for ST-Microelectronics.
10 #include <linux/clk-provider.h>
11 #include <linux/module.h>
12 #include <linux/slab.h>
14 #include <linux/err.h>
15 #include <linux/string.h>
17 #include <linux/of_address.h>
29 /* Pre-divisor's gate */
30 struct clk_gate pgate
;
32 struct clk_divider pdiv
;
33 /* Final divisor's gate */
34 struct clk_gate fgate
;
36 struct clk_divider fdiv
;
37 /* Asynchronous mode control */
39 /* hw control flags */
43 #define to_flexgen(_hw) container_of(_hw, struct flexgen, hw)
44 #define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
46 static int flexgen_enable(struct clk_hw
*hw
)
48 struct flexgen
*flexgen
= to_flexgen(hw
);
49 struct clk_hw
*pgate_hw
= &flexgen
->pgate
.hw
;
50 struct clk_hw
*fgate_hw
= &flexgen
->fgate
.hw
;
52 __clk_hw_set_clk(pgate_hw
, hw
);
53 __clk_hw_set_clk(fgate_hw
, hw
);
55 clk_gate_ops
.enable(pgate_hw
);
57 clk_gate_ops
.enable(fgate_hw
);
59 pr_debug("%s: flexgen output enabled\n", clk_hw_get_name(hw
));
63 static void flexgen_disable(struct clk_hw
*hw
)
65 struct flexgen
*flexgen
= to_flexgen(hw
);
66 struct clk_hw
*fgate_hw
= &flexgen
->fgate
.hw
;
68 /* disable only the final gate */
69 __clk_hw_set_clk(fgate_hw
, hw
);
71 clk_gate_ops
.disable(fgate_hw
);
73 pr_debug("%s: flexgen output disabled\n", clk_hw_get_name(hw
));
76 static int flexgen_is_enabled(struct clk_hw
*hw
)
78 struct flexgen
*flexgen
= to_flexgen(hw
);
79 struct clk_hw
*fgate_hw
= &flexgen
->fgate
.hw
;
81 __clk_hw_set_clk(fgate_hw
, hw
);
83 if (!clk_gate_ops
.is_enabled(fgate_hw
))
89 static u8
flexgen_get_parent(struct clk_hw
*hw
)
91 struct flexgen
*flexgen
= to_flexgen(hw
);
92 struct clk_hw
*mux_hw
= &flexgen
->mux
.hw
;
94 __clk_hw_set_clk(mux_hw
, hw
);
96 return clk_mux_ops
.get_parent(mux_hw
);
99 static int flexgen_set_parent(struct clk_hw
*hw
, u8 index
)
101 struct flexgen
*flexgen
= to_flexgen(hw
);
102 struct clk_hw
*mux_hw
= &flexgen
->mux
.hw
;
104 __clk_hw_set_clk(mux_hw
, hw
);
106 return clk_mux_ops
.set_parent(mux_hw
, index
);
109 static inline unsigned long
110 clk_best_div(unsigned long parent_rate
, unsigned long rate
)
112 return parent_rate
/ rate
+ ((rate
> (2*(parent_rate
% rate
))) ? 0 : 1);
115 static long flexgen_round_rate(struct clk_hw
*hw
, unsigned long rate
,
116 unsigned long *prate
)
120 /* Round div according to exact prate and wished rate */
121 div
= clk_best_div(*prate
, rate
);
123 if (clk_hw_get_flags(hw
) & CLK_SET_RATE_PARENT
) {
131 static unsigned long flexgen_recalc_rate(struct clk_hw
*hw
,
132 unsigned long parent_rate
)
134 struct flexgen
*flexgen
= to_flexgen(hw
);
135 struct clk_hw
*pdiv_hw
= &flexgen
->pdiv
.hw
;
136 struct clk_hw
*fdiv_hw
= &flexgen
->fdiv
.hw
;
137 unsigned long mid_rate
;
139 __clk_hw_set_clk(pdiv_hw
, hw
);
140 __clk_hw_set_clk(fdiv_hw
, hw
);
142 mid_rate
= clk_divider_ops
.recalc_rate(pdiv_hw
, parent_rate
);
144 return clk_divider_ops
.recalc_rate(fdiv_hw
, mid_rate
);
147 static int flexgen_set_rate(struct clk_hw
*hw
, unsigned long rate
,
148 unsigned long parent_rate
)
150 struct flexgen
*flexgen
= to_flexgen(hw
);
151 struct clk_hw
*pdiv_hw
= &flexgen
->pdiv
.hw
;
152 struct clk_hw
*fdiv_hw
= &flexgen
->fdiv
.hw
;
153 struct clk_hw
*sync_hw
= &flexgen
->sync
.hw
;
154 struct clk_gate
*config
= to_clk_gate(sync_hw
);
155 unsigned long div
= 0;
159 __clk_hw_set_clk(pdiv_hw
, hw
);
160 __clk_hw_set_clk(fdiv_hw
, hw
);
162 if (flexgen
->control_mode
) {
163 reg
= readl(config
->reg
);
164 reg
&= ~BIT(config
->bit_idx
);
165 writel(reg
, config
->reg
);
168 div
= clk_best_div(parent_rate
, rate
);
171 * pdiv is mainly targeted for low freq results, while fdiv
172 * should be used for div <= 64. The other way round can
173 * lead to 'duty cycle' issues.
177 clk_divider_ops
.set_rate(pdiv_hw
, parent_rate
, parent_rate
);
178 ret
= clk_divider_ops
.set_rate(fdiv_hw
, rate
, rate
* div
);
180 clk_divider_ops
.set_rate(fdiv_hw
, parent_rate
, parent_rate
);
181 ret
= clk_divider_ops
.set_rate(pdiv_hw
, rate
, rate
* div
);
187 static const struct clk_ops flexgen_ops
= {
188 .enable
= flexgen_enable
,
189 .disable
= flexgen_disable
,
190 .is_enabled
= flexgen_is_enabled
,
191 .get_parent
= flexgen_get_parent
,
192 .set_parent
= flexgen_set_parent
,
193 .round_rate
= flexgen_round_rate
,
194 .recalc_rate
= flexgen_recalc_rate
,
195 .set_rate
= flexgen_set_rate
,
198 static struct clk
*clk_register_flexgen(const char *name
,
199 const char **parent_names
, u8 num_parents
,
200 void __iomem
*reg
, spinlock_t
*lock
, u32 idx
,
201 unsigned long flexgen_flags
, bool mode
) {
202 struct flexgen
*fgxbar
;
204 struct clk_init_data init
;
206 void __iomem
*xbar_reg
, *fdiv_reg
;
208 fgxbar
= kzalloc(sizeof(struct flexgen
), GFP_KERNEL
);
210 return ERR_PTR(-ENOMEM
);
213 init
.ops
= &flexgen_ops
;
214 init
.flags
= CLK_GET_RATE_NOCACHE
| flexgen_flags
;
215 init
.parent_names
= parent_names
;
216 init
.num_parents
= num_parents
;
218 xbar_reg
= reg
+ 0x18 + (idx
& ~0x3);
219 xbar_shift
= (idx
% 4) * 0x8;
220 fdiv_reg
= reg
+ 0x164 + idx
* 4;
222 /* Crossbar element config */
223 fgxbar
->mux
.lock
= lock
;
224 fgxbar
->mux
.mask
= BIT(6) - 1;
225 fgxbar
->mux
.reg
= xbar_reg
;
226 fgxbar
->mux
.shift
= xbar_shift
;
227 fgxbar
->mux
.table
= NULL
;
230 /* Pre-divider's gate config (in xbar register)*/
231 fgxbar
->pgate
.lock
= lock
;
232 fgxbar
->pgate
.reg
= xbar_reg
;
233 fgxbar
->pgate
.bit_idx
= xbar_shift
+ 6;
235 /* Pre-divider config */
236 fgxbar
->pdiv
.lock
= lock
;
237 fgxbar
->pdiv
.reg
= reg
+ 0x58 + idx
* 4;
238 fgxbar
->pdiv
.width
= 10;
240 /* Final divider's gate config */
241 fgxbar
->fgate
.lock
= lock
;
242 fgxbar
->fgate
.reg
= fdiv_reg
;
243 fgxbar
->fgate
.bit_idx
= 6;
245 /* Final divider config */
246 fgxbar
->fdiv
.lock
= lock
;
247 fgxbar
->fdiv
.reg
= fdiv_reg
;
248 fgxbar
->fdiv
.width
= 6;
250 /* Final divider sync config */
251 fgxbar
->sync
.lock
= lock
;
252 fgxbar
->sync
.reg
= fdiv_reg
;
253 fgxbar
->sync
.bit_idx
= 7;
255 fgxbar
->control_mode
= mode
;
257 fgxbar
->hw
.init
= &init
;
259 clk
= clk_register(NULL
, &fgxbar
->hw
);
263 pr_debug("%s: parent %s rate %u\n",
265 __clk_get_name(clk_get_parent(clk
)),
266 (unsigned int)clk_get_rate(clk
));
270 static const char ** __init
flexgen_get_parents(struct device_node
*np
,
273 const char **parents
;
274 unsigned int nparents
;
276 nparents
= of_clk_get_parent_count(np
);
277 if (WARN_ON(!nparents
))
280 parents
= kcalloc(nparents
, sizeof(const char *), GFP_KERNEL
);
284 *num_parents
= of_clk_parent_fill(np
, parents
, nparents
);
289 static const struct clkgen_data clkgen_audio
= {
290 .flags
= CLK_SET_RATE_PARENT
,
293 static const struct clkgen_data clkgen_video
= {
294 .flags
= CLK_SET_RATE_PARENT
,
298 static const struct of_device_id flexgen_of_match
[] = {
300 .compatible
= "st,flexgen-audio",
301 .data
= &clkgen_audio
,
304 .compatible
= "st,flexgen-video",
305 .data
= &clkgen_video
,
310 static void __init
st_of_flexgen_setup(struct device_node
*np
)
312 struct device_node
*pnode
;
314 struct clk_onecell_data
*clk_data
;
315 const char **parents
;
317 spinlock_t
*rlock
= NULL
;
318 const struct of_device_id
*match
;
319 struct clkgen_data
*data
= NULL
;
320 unsigned long flex_flags
= 0;
324 pnode
= of_get_parent(np
);
328 reg
= of_iomap(pnode
, 0);
333 parents
= flexgen_get_parents(np
, &num_parents
);
339 match
= of_match_node(flexgen_of_match
, np
);
341 data
= (struct clkgen_data
*)match
->data
;
342 flex_flags
= data
->flags
;
343 clk_mode
= data
->mode
;
346 clk_data
= kzalloc(sizeof(*clk_data
), GFP_KERNEL
);
350 ret
= of_property_count_strings(np
, "clock-output-names");
352 pr_err("%s: Failed to get number of output clocks (%d)",
353 __func__
, clk_data
->clk_num
);
356 clk_data
->clk_num
= ret
;
358 clk_data
->clks
= kcalloc(clk_data
->clk_num
, sizeof(struct clk
*),
363 rlock
= kzalloc(sizeof(spinlock_t
), GFP_KERNEL
);
367 spin_lock_init(rlock
);
369 for (i
= 0; i
< clk_data
->clk_num
; i
++) {
371 const char *clk_name
;
373 if (of_property_read_string_index(np
, "clock-output-names",
378 flex_flags
&= ~CLK_IS_CRITICAL
;
379 of_clk_detect_critical(np
, i
, &flex_flags
);
382 * If we read an empty clock name then the output is unused
384 if (*clk_name
== '\0')
387 clk
= clk_register_flexgen(clk_name
, parents
, num_parents
,
388 reg
, rlock
, i
, flex_flags
, clk_mode
);
393 clk_data
->clks
[i
] = clk
;
397 of_clk_add_provider(np
, of_clk_src_onecell_get
, clk_data
);
404 kfree(clk_data
->clks
);
409 CLK_OF_DECLARE(flexgen
, "st,flexgen", st_of_flexgen_setup
);