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 */
8 #include <linux/clk-provider.h>
9 #include <linux/module.h>
10 #include <linux/slab.h>
12 #include <linux/err.h>
13 #include <linux/string.h>
15 #include <linux/of_address.h>
22 /* Pre-divisor's gate */
23 struct clk_gate pgate
;
25 struct clk_divider pdiv
;
26 /* Final divisor's gate */
27 struct clk_gate fgate
;
29 struct clk_divider fdiv
;
32 #define to_flexgen(_hw) container_of(_hw, struct flexgen, hw)
34 static int flexgen_enable(struct clk_hw
*hw
)
36 struct flexgen
*flexgen
= to_flexgen(hw
);
37 struct clk_hw
*pgate_hw
= &flexgen
->pgate
.hw
;
38 struct clk_hw
*fgate_hw
= &flexgen
->fgate
.hw
;
40 pgate_hw
->clk
= hw
->clk
;
41 fgate_hw
->clk
= hw
->clk
;
43 clk_gate_ops
.enable(pgate_hw
);
45 clk_gate_ops
.enable(fgate_hw
);
47 pr_debug("%s: flexgen output enabled\n", __clk_get_name(hw
->clk
));
51 static void flexgen_disable(struct clk_hw
*hw
)
53 struct flexgen
*flexgen
= to_flexgen(hw
);
54 struct clk_hw
*fgate_hw
= &flexgen
->fgate
.hw
;
56 /* disable only the final gate */
57 fgate_hw
->clk
= hw
->clk
;
59 clk_gate_ops
.disable(fgate_hw
);
61 pr_debug("%s: flexgen output disabled\n", __clk_get_name(hw
->clk
));
64 static int flexgen_is_enabled(struct clk_hw
*hw
)
66 struct flexgen
*flexgen
= to_flexgen(hw
);
67 struct clk_hw
*fgate_hw
= &flexgen
->fgate
.hw
;
69 fgate_hw
->clk
= hw
->clk
;
71 if (!clk_gate_ops
.is_enabled(fgate_hw
))
77 static u8
flexgen_get_parent(struct clk_hw
*hw
)
79 struct flexgen
*flexgen
= to_flexgen(hw
);
80 struct clk_hw
*mux_hw
= &flexgen
->mux
.hw
;
82 mux_hw
->clk
= hw
->clk
;
84 return clk_mux_ops
.get_parent(mux_hw
);
87 static int flexgen_set_parent(struct clk_hw
*hw
, u8 index
)
89 struct flexgen
*flexgen
= to_flexgen(hw
);
90 struct clk_hw
*mux_hw
= &flexgen
->mux
.hw
;
92 mux_hw
->clk
= hw
->clk
;
94 return clk_mux_ops
.set_parent(mux_hw
, index
);
97 static inline unsigned long
98 clk_best_div(unsigned long parent_rate
, unsigned long rate
)
100 return parent_rate
/ rate
+ ((rate
> (2*(parent_rate
% rate
))) ? 0 : 1);
103 static long flexgen_round_rate(struct clk_hw
*hw
, unsigned long rate
,
104 unsigned long *prate
)
108 /* Round div according to exact prate and wished rate */
109 div
= clk_best_div(*prate
, rate
);
111 if (__clk_get_flags(hw
->clk
) & CLK_SET_RATE_PARENT
) {
119 unsigned long flexgen_recalc_rate(struct clk_hw
*hw
,
120 unsigned long parent_rate
)
122 struct flexgen
*flexgen
= to_flexgen(hw
);
123 struct clk_hw
*pdiv_hw
= &flexgen
->pdiv
.hw
;
124 struct clk_hw
*fdiv_hw
= &flexgen
->fdiv
.hw
;
125 unsigned long mid_rate
;
127 pdiv_hw
->clk
= hw
->clk
;
128 fdiv_hw
->clk
= hw
->clk
;
130 mid_rate
= clk_divider_ops
.recalc_rate(pdiv_hw
, parent_rate
);
132 return clk_divider_ops
.recalc_rate(fdiv_hw
, mid_rate
);
135 static int flexgen_set_rate(struct clk_hw
*hw
, unsigned long rate
,
136 unsigned long parent_rate
)
138 struct flexgen
*flexgen
= to_flexgen(hw
);
139 struct clk_hw
*pdiv_hw
= &flexgen
->pdiv
.hw
;
140 struct clk_hw
*fdiv_hw
= &flexgen
->fdiv
.hw
;
141 unsigned long primary_div
= 0;
144 pdiv_hw
->clk
= hw
->clk
;
145 fdiv_hw
->clk
= hw
->clk
;
147 primary_div
= clk_best_div(parent_rate
, rate
);
149 clk_divider_ops
.set_rate(fdiv_hw
, parent_rate
, parent_rate
);
150 ret
= clk_divider_ops
.set_rate(pdiv_hw
, rate
, rate
* primary_div
);
155 static const struct clk_ops flexgen_ops
= {
156 .enable
= flexgen_enable
,
157 .disable
= flexgen_disable
,
158 .is_enabled
= flexgen_is_enabled
,
159 .get_parent
= flexgen_get_parent
,
160 .set_parent
= flexgen_set_parent
,
161 .round_rate
= flexgen_round_rate
,
162 .recalc_rate
= flexgen_recalc_rate
,
163 .set_rate
= flexgen_set_rate
,
166 struct clk
*clk_register_flexgen(const char *name
,
167 const char **parent_names
, u8 num_parents
,
168 void __iomem
*reg
, spinlock_t
*lock
, u32 idx
,
169 unsigned long flexgen_flags
) {
170 struct flexgen
*fgxbar
;
172 struct clk_init_data init
;
174 void __iomem
*xbar_reg
, *fdiv_reg
;
176 fgxbar
= kzalloc(sizeof(struct flexgen
), GFP_KERNEL
);
178 return ERR_PTR(-ENOMEM
);
181 init
.ops
= &flexgen_ops
;
182 init
.flags
= CLK_IS_BASIC
| flexgen_flags
;
183 init
.parent_names
= parent_names
;
184 init
.num_parents
= num_parents
;
186 xbar_reg
= reg
+ 0x18 + (idx
& ~0x3);
187 xbar_shift
= (idx
% 4) * 0x8;
188 fdiv_reg
= reg
+ 0x164 + idx
* 4;
190 /* Crossbar element config */
191 fgxbar
->mux
.lock
= lock
;
192 fgxbar
->mux
.mask
= BIT(6) - 1;
193 fgxbar
->mux
.reg
= xbar_reg
;
194 fgxbar
->mux
.shift
= xbar_shift
;
195 fgxbar
->mux
.table
= NULL
;
198 /* Pre-divider's gate config (in xbar register)*/
199 fgxbar
->pgate
.lock
= lock
;
200 fgxbar
->pgate
.reg
= xbar_reg
;
201 fgxbar
->pgate
.bit_idx
= xbar_shift
+ 6;
203 /* Pre-divider config */
204 fgxbar
->pdiv
.lock
= lock
;
205 fgxbar
->pdiv
.reg
= reg
+ 0x58 + idx
* 4;
206 fgxbar
->pdiv
.width
= 10;
208 /* Final divider's gate config */
209 fgxbar
->fgate
.lock
= lock
;
210 fgxbar
->fgate
.reg
= fdiv_reg
;
211 fgxbar
->fgate
.bit_idx
= 6;
213 /* Final divider config */
214 fgxbar
->fdiv
.lock
= lock
;
215 fgxbar
->fdiv
.reg
= fdiv_reg
;
216 fgxbar
->fdiv
.width
= 6;
218 fgxbar
->hw
.init
= &init
;
220 clk
= clk_register(NULL
, &fgxbar
->hw
);
224 pr_debug("%s: parent %s rate %u\n",
226 __clk_get_name(clk_get_parent(clk
)),
227 (unsigned int)clk_get_rate(clk
));
231 static const char ** __init
flexgen_get_parents(struct device_node
*np
,
234 const char **parents
;
237 nparents
= of_count_phandle_with_args(np
, "clocks", "#clock-cells");
238 if (WARN_ON(nparents
<= 0))
241 parents
= kcalloc(nparents
, sizeof(const char *), GFP_KERNEL
);
245 for (i
= 0; i
< nparents
; i
++)
246 parents
[i
] = of_clk_get_parent_name(np
, i
);
248 *num_parents
= nparents
;
252 void __init
st_of_flexgen_setup(struct device_node
*np
)
254 struct device_node
*pnode
;
256 struct clk_onecell_data
*clk_data
;
257 const char **parents
;
259 spinlock_t
*rlock
= NULL
;
260 unsigned long flex_flags
= 0;
262 pnode
= of_get_parent(np
);
266 reg
= of_iomap(pnode
, 0);
270 parents
= flexgen_get_parents(np
, &num_parents
);
274 clk_data
= kzalloc(sizeof(*clk_data
), GFP_KERNEL
);
278 clk_data
->clk_num
= of_property_count_strings(np
,
279 "clock-output-names");
280 if (clk_data
->clk_num
<= 0) {
281 pr_err("%s: Failed to get number of output clocks (%d)",
282 __func__
, clk_data
->clk_num
);
286 clk_data
->clks
= kcalloc(clk_data
->clk_num
, sizeof(struct clk
*),
291 rlock
= kzalloc(sizeof(spinlock_t
), GFP_KERNEL
);
295 for (i
= 0; i
< clk_data
->clk_num
; i
++) {
297 const char *clk_name
;
299 if (of_property_read_string_index(np
, "clock-output-names",
305 * If we read an empty clock name then the output is unused
307 if (*clk_name
== '\0')
310 clk
= clk_register_flexgen(clk_name
, parents
, num_parents
,
311 reg
, rlock
, i
, flex_flags
);
316 clk_data
->clks
[i
] = clk
;
320 of_clk_add_provider(np
, of_clk_src_onecell_get
, clk_data
);
326 kfree(clk_data
->clks
);
331 CLK_OF_DECLARE(flexgen
, "st,flexgen", st_of_flexgen_setup
);