]>
git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/blob - drivers/clk/st/clk-flexgen.c
4 * Copyright (C) ST-Microelectronics SA 2013
5 * Author: Maxime Coquelin <maxime.coquelin@st.com> for ST-Microelectronics.
6 * License terms: GNU General Public License (GPL), version 2 */
9 #include <linux/clk-provider.h>
10 #include <linux/module.h>
11 #include <linux/slab.h>
13 #include <linux/err.h>
14 #include <linux/string.h>
16 #include <linux/of_address.h>
23 /* Pre-divisor's gate */
24 struct clk_gate pgate
;
26 struct clk_divider pdiv
;
27 /* Final divisor's gate */
28 struct clk_gate fgate
;
30 struct clk_divider fdiv
;
33 #define to_flexgen(_hw) container_of(_hw, struct flexgen, hw)
35 static int flexgen_enable(struct clk_hw
*hw
)
37 struct flexgen
*flexgen
= to_flexgen(hw
);
38 struct clk_hw
*pgate_hw
= &flexgen
->pgate
.hw
;
39 struct clk_hw
*fgate_hw
= &flexgen
->fgate
.hw
;
41 __clk_hw_set_clk(pgate_hw
, hw
);
42 __clk_hw_set_clk(fgate_hw
, hw
);
44 clk_gate_ops
.enable(pgate_hw
);
46 clk_gate_ops
.enable(fgate_hw
);
48 pr_debug("%s: flexgen output enabled\n", clk_hw_get_name(hw
));
52 static void flexgen_disable(struct clk_hw
*hw
)
54 struct flexgen
*flexgen
= to_flexgen(hw
);
55 struct clk_hw
*fgate_hw
= &flexgen
->fgate
.hw
;
57 /* disable only the final gate */
58 __clk_hw_set_clk(fgate_hw
, hw
);
60 clk_gate_ops
.disable(fgate_hw
);
62 pr_debug("%s: flexgen output disabled\n", clk_hw_get_name(hw
));
65 static int flexgen_is_enabled(struct clk_hw
*hw
)
67 struct flexgen
*flexgen
= to_flexgen(hw
);
68 struct clk_hw
*fgate_hw
= &flexgen
->fgate
.hw
;
70 __clk_hw_set_clk(fgate_hw
, hw
);
72 if (!clk_gate_ops
.is_enabled(fgate_hw
))
78 static u8
flexgen_get_parent(struct clk_hw
*hw
)
80 struct flexgen
*flexgen
= to_flexgen(hw
);
81 struct clk_hw
*mux_hw
= &flexgen
->mux
.hw
;
83 __clk_hw_set_clk(mux_hw
, hw
);
85 return clk_mux_ops
.get_parent(mux_hw
);
88 static int flexgen_set_parent(struct clk_hw
*hw
, u8 index
)
90 struct flexgen
*flexgen
= to_flexgen(hw
);
91 struct clk_hw
*mux_hw
= &flexgen
->mux
.hw
;
93 __clk_hw_set_clk(mux_hw
, hw
);
95 return clk_mux_ops
.set_parent(mux_hw
, index
);
98 static inline unsigned long
99 clk_best_div(unsigned long parent_rate
, unsigned long rate
)
101 return parent_rate
/ rate
+ ((rate
> (2*(parent_rate
% rate
))) ? 0 : 1);
104 static long flexgen_round_rate(struct clk_hw
*hw
, unsigned long rate
,
105 unsigned long *prate
)
109 /* Round div according to exact prate and wished rate */
110 div
= clk_best_div(*prate
, rate
);
112 if (clk_hw_get_flags(hw
) & CLK_SET_RATE_PARENT
) {
120 static unsigned long flexgen_recalc_rate(struct clk_hw
*hw
,
121 unsigned long parent_rate
)
123 struct flexgen
*flexgen
= to_flexgen(hw
);
124 struct clk_hw
*pdiv_hw
= &flexgen
->pdiv
.hw
;
125 struct clk_hw
*fdiv_hw
= &flexgen
->fdiv
.hw
;
126 unsigned long mid_rate
;
128 __clk_hw_set_clk(pdiv_hw
, hw
);
129 __clk_hw_set_clk(fdiv_hw
, hw
);
131 mid_rate
= clk_divider_ops
.recalc_rate(pdiv_hw
, parent_rate
);
133 return clk_divider_ops
.recalc_rate(fdiv_hw
, mid_rate
);
136 static int flexgen_set_rate(struct clk_hw
*hw
, unsigned long rate
,
137 unsigned long parent_rate
)
139 struct flexgen
*flexgen
= to_flexgen(hw
);
140 struct clk_hw
*pdiv_hw
= &flexgen
->pdiv
.hw
;
141 struct clk_hw
*fdiv_hw
= &flexgen
->fdiv
.hw
;
142 unsigned long div
= 0;
145 __clk_hw_set_clk(pdiv_hw
, hw
);
146 __clk_hw_set_clk(fdiv_hw
, hw
);
148 div
= clk_best_div(parent_rate
, rate
);
151 * pdiv is mainly targeted for low freq results, while fdiv
152 * should be used for div <= 64. The other way round can
153 * lead to 'duty cycle' issues.
157 clk_divider_ops
.set_rate(pdiv_hw
, parent_rate
, parent_rate
);
158 ret
= clk_divider_ops
.set_rate(fdiv_hw
, rate
, rate
* div
);
160 clk_divider_ops
.set_rate(fdiv_hw
, parent_rate
, parent_rate
);
161 ret
= clk_divider_ops
.set_rate(pdiv_hw
, rate
, rate
* div
);
167 static const struct clk_ops flexgen_ops
= {
168 .enable
= flexgen_enable
,
169 .disable
= flexgen_disable
,
170 .is_enabled
= flexgen_is_enabled
,
171 .get_parent
= flexgen_get_parent
,
172 .set_parent
= flexgen_set_parent
,
173 .round_rate
= flexgen_round_rate
,
174 .recalc_rate
= flexgen_recalc_rate
,
175 .set_rate
= flexgen_set_rate
,
178 static struct clk
*clk_register_flexgen(const char *name
,
179 const char **parent_names
, u8 num_parents
,
180 void __iomem
*reg
, spinlock_t
*lock
, u32 idx
,
181 unsigned long flexgen_flags
) {
182 struct flexgen
*fgxbar
;
184 struct clk_init_data init
;
186 void __iomem
*xbar_reg
, *fdiv_reg
;
188 fgxbar
= kzalloc(sizeof(struct flexgen
), GFP_KERNEL
);
190 return ERR_PTR(-ENOMEM
);
193 init
.ops
= &flexgen_ops
;
194 init
.flags
= CLK_IS_BASIC
| CLK_GET_RATE_NOCACHE
| flexgen_flags
;
195 init
.parent_names
= parent_names
;
196 init
.num_parents
= num_parents
;
198 xbar_reg
= reg
+ 0x18 + (idx
& ~0x3);
199 xbar_shift
= (idx
% 4) * 0x8;
200 fdiv_reg
= reg
+ 0x164 + idx
* 4;
202 /* Crossbar element config */
203 fgxbar
->mux
.lock
= lock
;
204 fgxbar
->mux
.mask
= BIT(6) - 1;
205 fgxbar
->mux
.reg
= xbar_reg
;
206 fgxbar
->mux
.shift
= xbar_shift
;
207 fgxbar
->mux
.table
= NULL
;
210 /* Pre-divider's gate config (in xbar register)*/
211 fgxbar
->pgate
.lock
= lock
;
212 fgxbar
->pgate
.reg
= xbar_reg
;
213 fgxbar
->pgate
.bit_idx
= xbar_shift
+ 6;
215 /* Pre-divider config */
216 fgxbar
->pdiv
.lock
= lock
;
217 fgxbar
->pdiv
.reg
= reg
+ 0x58 + idx
* 4;
218 fgxbar
->pdiv
.width
= 10;
220 /* Final divider's gate config */
221 fgxbar
->fgate
.lock
= lock
;
222 fgxbar
->fgate
.reg
= fdiv_reg
;
223 fgxbar
->fgate
.bit_idx
= 6;
225 /* Final divider config */
226 fgxbar
->fdiv
.lock
= lock
;
227 fgxbar
->fdiv
.reg
= fdiv_reg
;
228 fgxbar
->fdiv
.width
= 6;
230 fgxbar
->hw
.init
= &init
;
232 clk
= clk_register(NULL
, &fgxbar
->hw
);
236 pr_debug("%s: parent %s rate %u\n",
238 __clk_get_name(clk_get_parent(clk
)),
239 (unsigned int)clk_get_rate(clk
));
243 static const char ** __init
flexgen_get_parents(struct device_node
*np
,
246 const char **parents
;
249 nparents
= of_clk_get_parent_count(np
);
250 if (WARN_ON(nparents
<= 0))
253 parents
= kcalloc(nparents
, sizeof(const char *), GFP_KERNEL
);
257 *num_parents
= of_clk_parent_fill(np
, parents
, nparents
);
262 static void __init
st_of_flexgen_setup(struct device_node
*np
)
264 struct device_node
*pnode
;
266 struct clk_onecell_data
*clk_data
;
267 const char **parents
;
269 spinlock_t
*rlock
= NULL
;
270 unsigned long flex_flags
= 0;
272 pnode
= of_get_parent(np
);
276 reg
= of_iomap(pnode
, 0);
280 parents
= flexgen_get_parents(np
, &num_parents
);
284 clk_data
= kzalloc(sizeof(*clk_data
), GFP_KERNEL
);
288 clk_data
->clk_num
= of_property_count_strings(np
,
289 "clock-output-names");
290 if (clk_data
->clk_num
<= 0) {
291 pr_err("%s: Failed to get number of output clocks (%d)",
292 __func__
, clk_data
->clk_num
);
296 clk_data
->clks
= kcalloc(clk_data
->clk_num
, sizeof(struct clk
*),
301 rlock
= kzalloc(sizeof(spinlock_t
), GFP_KERNEL
);
305 spin_lock_init(rlock
);
307 for (i
= 0; i
< clk_data
->clk_num
; i
++) {
309 const char *clk_name
;
311 if (of_property_read_string_index(np
, "clock-output-names",
317 * If we read an empty clock name then the output is unused
319 if (*clk_name
== '\0')
322 clk
= clk_register_flexgen(clk_name
, parents
, num_parents
,
323 reg
, rlock
, i
, flex_flags
);
328 clk_data
->clks
[i
] = clk
;
332 of_clk_add_provider(np
, of_clk_src_onecell_get
, clk_data
);
338 kfree(clk_data
->clks
);
343 CLK_OF_DECLARE(flexgen
, "st,flexgen", st_of_flexgen_setup
);