Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * clk-flexgen.c
  4 *
  5 * Copyright (C) ST-Microelectronics SA 2013
  6 * Author:  Maxime Coquelin <maxime.coquelin@st.com> for ST-Microelectronics.
  7 */
  8
  9#include <linux/clk.h>
 10#include <linux/clk-provider.h>
 11#include <linux/module.h>
 12#include <linux/slab.h>
 13#include <linux/io.h>
 14#include <linux/err.h>
 15#include <linux/string.h>
 16#include <linux/of.h>
 17#include <linux/of_address.h>
 18
 19struct clkgen_clk_out {
 20	const char *name;
 21	unsigned long flags;
 22};
 23
 24struct clkgen_data {
 25	unsigned long flags;
 26	bool mode;
 27	const struct clkgen_clk_out *outputs;
 28	const unsigned int outputs_nb;
 29};
 30
 31struct flexgen {
 32	struct clk_hw hw;
 33
 34	/* Crossbar */
 35	struct clk_mux mux;
 36	/* Pre-divisor's gate */
 37	struct clk_gate pgate;
 38	/* Pre-divisor */
 39	struct clk_divider pdiv;
 40	/* Final divisor's gate */
 41	struct clk_gate fgate;
 42	/* Final divisor */
 43	struct clk_divider fdiv;
 44	/* Asynchronous mode control */
 45	struct clk_gate sync;
 46	/* hw control flags */
 47	bool control_mode;
 48};
 49
 50#define to_flexgen(_hw) container_of(_hw, struct flexgen, hw)
 51#define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
 52
 53static int flexgen_enable(struct clk_hw *hw)
 54{
 55	struct flexgen *flexgen = to_flexgen(hw);
 56	struct clk_hw *pgate_hw = &flexgen->pgate.hw;
 57	struct clk_hw *fgate_hw = &flexgen->fgate.hw;
 58
 59	__clk_hw_set_clk(pgate_hw, hw);
 60	__clk_hw_set_clk(fgate_hw, hw);
 61
 62	clk_gate_ops.enable(pgate_hw);
 63
 64	clk_gate_ops.enable(fgate_hw);
 65
 66	pr_debug("%s: flexgen output enabled\n", clk_hw_get_name(hw));
 67	return 0;
 68}
 69
 70static void flexgen_disable(struct clk_hw *hw)
 71{
 72	struct flexgen *flexgen = to_flexgen(hw);
 73	struct clk_hw *fgate_hw = &flexgen->fgate.hw;
 74
 75	/* disable only the final gate */
 76	__clk_hw_set_clk(fgate_hw, hw);
 77
 78	clk_gate_ops.disable(fgate_hw);
 79
 80	pr_debug("%s: flexgen output disabled\n", clk_hw_get_name(hw));
 81}
 82
 83static int flexgen_is_enabled(struct clk_hw *hw)
 84{
 85	struct flexgen *flexgen = to_flexgen(hw);
 86	struct clk_hw *fgate_hw = &flexgen->fgate.hw;
 87
 88	__clk_hw_set_clk(fgate_hw, hw);
 89
 90	if (!clk_gate_ops.is_enabled(fgate_hw))
 91		return 0;
 92
 93	return 1;
 94}
 95
 96static u8 flexgen_get_parent(struct clk_hw *hw)
 97{
 98	struct flexgen *flexgen = to_flexgen(hw);
 99	struct clk_hw *mux_hw = &flexgen->mux.hw;
100
101	__clk_hw_set_clk(mux_hw, hw);
102
103	return clk_mux_ops.get_parent(mux_hw);
104}
105
106static int flexgen_set_parent(struct clk_hw *hw, u8 index)
107{
108	struct flexgen *flexgen = to_flexgen(hw);
109	struct clk_hw *mux_hw = &flexgen->mux.hw;
110
111	__clk_hw_set_clk(mux_hw, hw);
112
113	return clk_mux_ops.set_parent(mux_hw, index);
114}
115
116static inline unsigned long
117clk_best_div(unsigned long parent_rate, unsigned long rate)
118{
119	return parent_rate / rate + ((rate > (2*(parent_rate % rate))) ? 0 : 1);
120}
121
122static int flexgen_determine_rate(struct clk_hw *hw,
123				  struct clk_rate_request *req)
124{
125	unsigned long div;
126
127	/* Round div according to exact prate and wished rate */
128	div = clk_best_div(req->best_parent_rate, req->rate);
129
130	if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
131		req->best_parent_rate = req->rate * div;
132		return 0;
133	}
134
135	req->rate = req->best_parent_rate / div;
136	return 0;
137}
138
139static unsigned long flexgen_recalc_rate(struct clk_hw *hw,
140		unsigned long parent_rate)
141{
142	struct flexgen *flexgen = to_flexgen(hw);
143	struct clk_hw *pdiv_hw = &flexgen->pdiv.hw;
144	struct clk_hw *fdiv_hw = &flexgen->fdiv.hw;
145	unsigned long mid_rate;
146
147	__clk_hw_set_clk(pdiv_hw, hw);
148	__clk_hw_set_clk(fdiv_hw, hw);
149
150	mid_rate = clk_divider_ops.recalc_rate(pdiv_hw, parent_rate);
151
152	return clk_divider_ops.recalc_rate(fdiv_hw, mid_rate);
153}
154
155static int flexgen_set_rate(struct clk_hw *hw, unsigned long rate,
156				unsigned long parent_rate)
157{
158	struct flexgen *flexgen = to_flexgen(hw);
159	struct clk_hw *pdiv_hw = &flexgen->pdiv.hw;
160	struct clk_hw *fdiv_hw = &flexgen->fdiv.hw;
161	struct clk_hw *sync_hw = &flexgen->sync.hw;
162	struct clk_gate *config = to_clk_gate(sync_hw);
163	unsigned long div = 0;
164	int ret = 0;
165	u32 reg;
166
167	__clk_hw_set_clk(pdiv_hw, hw);
168	__clk_hw_set_clk(fdiv_hw, hw);
169
170	if (flexgen->control_mode) {
171		reg = readl(config->reg);
172		reg &= ~BIT(config->bit_idx);
173		writel(reg, config->reg);
174	}
175
176	div = clk_best_div(parent_rate, rate);
177
178	/*
179	* pdiv is mainly targeted for low freq results, while fdiv
180	* should be used for div <= 64. The other way round can
181	* lead to 'duty cycle' issues.
182	*/
183
184	if (div <= 64) {
185		clk_divider_ops.set_rate(pdiv_hw, parent_rate, parent_rate);
186		ret = clk_divider_ops.set_rate(fdiv_hw, rate, rate * div);
187	} else {
188		clk_divider_ops.set_rate(fdiv_hw, parent_rate, parent_rate);
189		ret = clk_divider_ops.set_rate(pdiv_hw, rate, rate * div);
190	}
191
192	return ret;
193}
194
195static const struct clk_ops flexgen_ops = {
196	.enable = flexgen_enable,
197	.disable = flexgen_disable,
198	.is_enabled = flexgen_is_enabled,
199	.get_parent = flexgen_get_parent,
200	.set_parent = flexgen_set_parent,
201	.determine_rate = flexgen_determine_rate,
202	.recalc_rate = flexgen_recalc_rate,
203	.set_rate = flexgen_set_rate,
204};
205
206static struct clk *clk_register_flexgen(const char *name,
207				const char **parent_names, u8 num_parents,
208				void __iomem *reg, spinlock_t *lock, u32 idx,
209				unsigned long flexgen_flags, bool mode) {
210	struct flexgen *fgxbar;
211	struct clk *clk;
212	struct clk_init_data init;
213	u32  xbar_shift;
214	void __iomem *xbar_reg, *fdiv_reg;
215
216	fgxbar = kzalloc(sizeof(struct flexgen), GFP_KERNEL);
217	if (!fgxbar)
218		return ERR_PTR(-ENOMEM);
219
220	init.name = name;
221	init.ops = &flexgen_ops;
222	init.flags = CLK_GET_RATE_NOCACHE | flexgen_flags;
223	init.parent_names = parent_names;
224	init.num_parents = num_parents;
225
226	xbar_reg = reg + 0x18 + (idx & ~0x3);
227	xbar_shift = (idx % 4) * 0x8;
228	fdiv_reg = reg + 0x164 + idx * 4;
229
230	/* Crossbar element config */
231	fgxbar->mux.lock = lock;
232	fgxbar->mux.mask = BIT(6) - 1;
233	fgxbar->mux.reg = xbar_reg;
234	fgxbar->mux.shift = xbar_shift;
235	fgxbar->mux.table = NULL;
236
237
238	/* Pre-divider's gate config (in xbar register)*/
239	fgxbar->pgate.lock = lock;
240	fgxbar->pgate.reg = xbar_reg;
241	fgxbar->pgate.bit_idx = xbar_shift + 6;
242
243	/* Pre-divider config */
244	fgxbar->pdiv.lock = lock;
245	fgxbar->pdiv.reg = reg + 0x58 + idx * 4;
246	fgxbar->pdiv.width = 10;
247
248	/* Final divider's gate config */
249	fgxbar->fgate.lock = lock;
250	fgxbar->fgate.reg = fdiv_reg;
251	fgxbar->fgate.bit_idx = 6;
252
253	/* Final divider config */
254	fgxbar->fdiv.lock = lock;
255	fgxbar->fdiv.reg = fdiv_reg;
256	fgxbar->fdiv.width = 6;
257
258	/* Final divider sync config */
259	fgxbar->sync.lock = lock;
260	fgxbar->sync.reg = fdiv_reg;
261	fgxbar->sync.bit_idx = 7;
262
263	fgxbar->control_mode = mode;
264
265	fgxbar->hw.init = &init;
266
267	clk = clk_register(NULL, &fgxbar->hw);
268	if (IS_ERR(clk))
269		kfree(fgxbar);
270	else
271		pr_debug("%s: parent %s rate %u\n",
272			__clk_get_name(clk),
273			__clk_get_name(clk_get_parent(clk)),
274			(unsigned int)clk_get_rate(clk));
275	return clk;
276}
277
278static const char ** __init flexgen_get_parents(struct device_node *np,
279						       int *num_parents)
280{
281	const char **parents;
282	unsigned int nparents;
283
284	nparents = of_clk_get_parent_count(np);
285	if (WARN_ON(!nparents))
286		return NULL;
287
288	parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL);
289	if (!parents)
290		return NULL;
291
292	*num_parents = of_clk_parent_fill(np, parents, nparents);
293
294	return parents;
295}
296
297static const struct clkgen_data clkgen_audio = {
298	.flags = CLK_SET_RATE_PARENT,
299};
300
301static const struct clkgen_data clkgen_video = {
302	.flags = CLK_SET_RATE_PARENT,
303	.mode = 1,
304};
305
306static const struct clkgen_clk_out clkgen_stih407_a0_clk_out[] = {
307	/* This clk needs to be on so that memory interface is accessible */
308	{ .name = "clk-ic-lmi0", .flags = CLK_IS_CRITICAL },
309};
310
311static const struct clkgen_data clkgen_stih407_a0 = {
312	.outputs = clkgen_stih407_a0_clk_out,
313	.outputs_nb = ARRAY_SIZE(clkgen_stih407_a0_clk_out),
314};
315
316static const struct clkgen_clk_out clkgen_stih410_a0_clk_out[] = {
317	/* Those clks need to be on so that memory interface is accessible */
318	{ .name = "clk-ic-lmi0", .flags = CLK_IS_CRITICAL },
319	{ .name = "clk-ic-lmi1", .flags = CLK_IS_CRITICAL },
320};
321
322static const struct clkgen_data clkgen_stih410_a0 = {
323	.outputs = clkgen_stih410_a0_clk_out,
324	.outputs_nb = ARRAY_SIZE(clkgen_stih410_a0_clk_out),
325};
326
327static const struct clkgen_clk_out clkgen_stih407_c0_clk_out[] = {
328	{ .name = "clk-icn-gpu", },
329	{ .name = "clk-fdma", },
330	{ .name = "clk-nand", },
331	{ .name = "clk-hva", },
332	{ .name = "clk-proc-stfe", },
333	{ .name = "clk-proc-tp", },
334	{ .name = "clk-rx-icn-dmu", },
335	{ .name = "clk-rx-icn-hva", },
336	/* This clk needs to be on to keep bus interconnect alive */
337	{ .name = "clk-icn-cpu", .flags = CLK_IS_CRITICAL },
338	/* This clk needs to be on to keep bus interconnect alive */
339	{ .name = "clk-tx-icn-dmu", .flags = CLK_IS_CRITICAL },
340	{ .name = "clk-mmc-0", },
341	{ .name = "clk-mmc-1", },
342	{ .name = "clk-jpegdec", },
343	/* This clk needs to be on to keep A9 running */
344	{ .name = "clk-ext2fa9", .flags = CLK_IS_CRITICAL },
345	{ .name = "clk-ic-bdisp-0", },
346	{ .name = "clk-ic-bdisp-1", },
347	{ .name = "clk-pp-dmu", },
348	{ .name = "clk-vid-dmu", },
349	{ .name = "clk-dss-lpc", },
350	{ .name = "clk-st231-aud-0", },
351	{ .name = "clk-st231-gp-1", },
352	{ .name = "clk-st231-dmu", },
353	/* This clk needs to be on to keep bus interconnect alive */
354	{ .name = "clk-icn-lmi", .flags = CLK_IS_CRITICAL },
355	{ .name = "clk-tx-icn-disp-1", },
356	/* This clk needs to be on to keep bus interconnect alive */
357	{ .name = "clk-icn-sbc", .flags = CLK_IS_CRITICAL },
358	{ .name = "clk-stfe-frc2", },
359	{ .name = "clk-eth-phy", },
360	{ .name = "clk-eth-ref-phyclk", },
361	{ .name = "clk-flash-promip", },
362	{ .name = "clk-main-disp", },
363	{ .name = "clk-aux-disp", },
364	{ .name = "clk-compo-dvp", },
365};
366
367static const struct clkgen_data clkgen_stih407_c0 = {
368	.outputs = clkgen_stih407_c0_clk_out,
369	.outputs_nb = ARRAY_SIZE(clkgen_stih407_c0_clk_out),
370};
371
372static const struct clkgen_clk_out clkgen_stih410_c0_clk_out[] = {
373	{ .name = "clk-icn-gpu", },
374	{ .name = "clk-fdma", },
375	{ .name = "clk-nand", },
376	{ .name = "clk-hva", },
377	{ .name = "clk-proc-stfe", },
378	{ .name = "clk-proc-tp", },
379	{ .name = "clk-rx-icn-dmu", },
380	{ .name = "clk-rx-icn-hva", },
381	/* This clk needs to be on to keep bus interconnect alive */
382	{ .name = "clk-icn-cpu", .flags = CLK_IS_CRITICAL },
383	/* This clk needs to be on to keep bus interconnect alive */
384	{ .name = "clk-tx-icn-dmu", .flags = CLK_IS_CRITICAL },
385	{ .name = "clk-mmc-0", },
386	{ .name = "clk-mmc-1", },
387	{ .name = "clk-jpegdec", },
388	/* This clk needs to be on to keep A9 running */
389	{ .name = "clk-ext2fa9", .flags = CLK_IS_CRITICAL },
390	{ .name = "clk-ic-bdisp-0", },
391	{ .name = "clk-ic-bdisp-1", },
392	{ .name = "clk-pp-dmu", },
393	{ .name = "clk-vid-dmu", },
394	{ .name = "clk-dss-lpc", },
395	{ .name = "clk-st231-aud-0", },
396	{ .name = "clk-st231-gp-1", },
397	{ .name = "clk-st231-dmu", },
398	/* This clk needs to be on to keep bus interconnect alive */
399	{ .name = "clk-icn-lmi", .flags = CLK_IS_CRITICAL },
400	{ .name = "clk-tx-icn-disp-1", },
401	/* This clk needs to be on to keep bus interconnect alive */
402	{ .name = "clk-icn-sbc", .flags = CLK_IS_CRITICAL },
403	{ .name = "clk-stfe-frc2", },
404	{ .name = "clk-eth-phy", },
405	{ .name = "clk-eth-ref-phyclk", },
406	{ .name = "clk-flash-promip", },
407	{ .name = "clk-main-disp", },
408	{ .name = "clk-aux-disp", },
409	{ .name = "clk-compo-dvp", },
410	{ .name = "clk-tx-icn-hades", },
411	{ .name = "clk-rx-icn-hades", },
412	/* This clk needs to be on to keep bus interconnect alive */
413	{ .name = "clk-icn-reg-16", .flags = CLK_IS_CRITICAL },
414	{ .name = "clk-pp-hades", },
415	{ .name = "clk-clust-hades", },
416	{ .name = "clk-hwpe-hades", },
417	{ .name = "clk-fc-hades", },
418};
419
420static const struct clkgen_data clkgen_stih410_c0 = {
421	.outputs = clkgen_stih410_c0_clk_out,
422	.outputs_nb = ARRAY_SIZE(clkgen_stih410_c0_clk_out),
423};
424
425static const struct clkgen_clk_out clkgen_stih418_c0_clk_out[] = {
426	{ .name = "clk-icn-gpu", },
427	{ .name = "clk-fdma", },
428	{ .name = "clk-nand", },
429	{ .name = "clk-hva", },
430	{ .name = "clk-proc-stfe", },
431	{ .name = "clk-tp", },
432	/* This clk needs to be on to keep bus interconnect alive */
433	{ .name = "clk-rx-icn-dmu", .flags = CLK_IS_CRITICAL },
434	/* This clk needs to be on to keep bus interconnect alive */
435	{ .name = "clk-rx-icn-hva", .flags = CLK_IS_CRITICAL },
436	{ .name = "clk-icn-cpu", .flags = CLK_IS_CRITICAL },
437	/* This clk needs to be on to keep bus interconnect alive */
438	{ .name = "clk-tx-icn-dmu", .flags = CLK_IS_CRITICAL },
439	{ .name = "clk-mmc-0", },
440	{ .name = "clk-mmc-1", },
441	{ .name = "clk-jpegdec", },
442	/* This clk needs to be on to keep bus interconnect alive */
443	{ .name = "clk-icn-reg", .flags = CLK_IS_CRITICAL },
444	{ .name = "clk-proc-bdisp-0", },
445	{ .name = "clk-proc-bdisp-1", },
446	{ .name = "clk-pp-dmu", },
447	{ .name = "clk-vid-dmu", },
448	{ .name = "clk-dss-lpc", },
449	{ .name = "clk-st231-aud-0", },
450	{ .name = "clk-st231-gp-1", },
451	{ .name = "clk-st231-dmu", },
452	/* This clk needs to be on to keep bus interconnect alive */
453	{ .name = "clk-icn-lmi", .flags = CLK_IS_CRITICAL },
454	/* This clk needs to be on to keep bus interconnect alive */
455	{ .name = "clk-tx-icn-1", .flags = CLK_IS_CRITICAL },
456	/* This clk needs to be on to keep bus interconnect alive */
457	{ .name = "clk-icn-sbc", .flags = CLK_IS_CRITICAL },
458	{ .name = "clk-stfe-frc2", },
459	{ .name = "clk-eth-phyref", },
460	{ .name = "clk-eth-ref-phyclk", },
461	{ .name = "clk-flash-promip", },
462	{ .name = "clk-main-disp", },
463	{ .name = "clk-aux-disp", },
464	{ .name = "clk-compo-dvp", },
465	/* This clk needs to be on to keep bus interconnect alive */
466	{ .name = "clk-tx-icn-hades", .flags = CLK_IS_CRITICAL },
467	/* This clk needs to be on to keep bus interconnect alive */
468	{ .name = "clk-rx-icn-hades", .flags = CLK_IS_CRITICAL },
469	/* This clk needs to be on to keep bus interconnect alive */
470	{ .name = "clk-icn-reg-16", .flags = CLK_IS_CRITICAL },
471	{ .name = "clk-pp-hevc", },
472	{ .name = "clk-clust-hevc", },
473	{ .name = "clk-hwpe-hevc", },
474	{ .name = "clk-fc-hevc", },
475	{ .name = "clk-proc-mixer", },
476	{ .name = "clk-proc-sc", },
477	{ .name = "clk-avsp-hevc", },
478};
479
480static const struct clkgen_data clkgen_stih418_c0 = {
481	.outputs = clkgen_stih418_c0_clk_out,
482	.outputs_nb = ARRAY_SIZE(clkgen_stih418_c0_clk_out),
483};
484
485static const struct clkgen_clk_out clkgen_stih407_d0_clk_out[] = {
486	{ .name = "clk-pcm-0", },
487	{ .name = "clk-pcm-1", },
488	{ .name = "clk-pcm-2", },
489	{ .name = "clk-spdiff", },
490};
491
492static const struct clkgen_data clkgen_stih407_d0 = {
493	.flags = CLK_SET_RATE_PARENT,
494	.outputs = clkgen_stih407_d0_clk_out,
495	.outputs_nb = ARRAY_SIZE(clkgen_stih407_d0_clk_out),
496};
497
498static const struct clkgen_clk_out clkgen_stih410_d0_clk_out[] = {
499	{ .name = "clk-pcm-0", },
500	{ .name = "clk-pcm-1", },
501	{ .name = "clk-pcm-2", },
502	{ .name = "clk-spdiff", },
503	{ .name = "clk-pcmr10-master", },
504	{ .name = "clk-usb2-phy", },
505};
506
507static const struct clkgen_data clkgen_stih410_d0 = {
508	.flags = CLK_SET_RATE_PARENT,
509	.outputs = clkgen_stih410_d0_clk_out,
510	.outputs_nb = ARRAY_SIZE(clkgen_stih410_d0_clk_out),
511};
512
513static const struct clkgen_clk_out clkgen_stih407_d2_clk_out[] = {
514	{ .name = "clk-pix-main-disp", },
515	{ .name = "clk-pix-pip", },
516	{ .name = "clk-pix-gdp1", },
517	{ .name = "clk-pix-gdp2", },
518	{ .name = "clk-pix-gdp3", },
519	{ .name = "clk-pix-gdp4", },
520	{ .name = "clk-pix-aux-disp", },
521	{ .name = "clk-denc", },
522	{ .name = "clk-pix-hddac", },
523	{ .name = "clk-hddac", },
524	{ .name = "clk-sddac", },
525	{ .name = "clk-pix-dvo", },
526	{ .name = "clk-dvo", },
527	{ .name = "clk-pix-hdmi", },
528	{ .name = "clk-tmds-hdmi", },
529	{ .name = "clk-ref-hdmiphy", },
530};
531
532static const struct clkgen_data clkgen_stih407_d2 = {
533	.outputs = clkgen_stih407_d2_clk_out,
534	.outputs_nb = ARRAY_SIZE(clkgen_stih407_d2_clk_out),
535	.flags = CLK_SET_RATE_PARENT,
536	.mode = 1,
537};
538
539static const struct clkgen_clk_out clkgen_stih418_d2_clk_out[] = {
540	{ .name = "clk-pix-main-disp", },
541	{ .name = "", },
542	{ .name = "", },
543	{ .name = "", },
544	{ .name = "", },
545	{ .name = "clk-tmds-hdmi-div2", },
546	{ .name = "clk-pix-aux-disp", },
547	{ .name = "clk-denc", },
548	{ .name = "clk-pix-hddac", },
549	{ .name = "clk-hddac", },
550	{ .name = "clk-sddac", },
551	{ .name = "clk-pix-dvo", },
552	{ .name = "clk-dvo", },
553	{ .name = "clk-pix-hdmi", },
554	{ .name = "clk-tmds-hdmi", },
555	{ .name = "clk-ref-hdmiphy", },
556	{ .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
557	{ .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
558	{ .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
559	{ .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
560	{ .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
561	{ .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
562	{ .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
563	{ .name = "", }, { .name = "", }, { .name = "", },
564	{ .name = "clk-vp9", },
565};
566
567static const struct clkgen_data clkgen_stih418_d2 = {
568	.outputs = clkgen_stih418_d2_clk_out,
569	.outputs_nb = ARRAY_SIZE(clkgen_stih418_d2_clk_out),
570	.flags = CLK_SET_RATE_PARENT,
571	.mode = 1,
572};
573
574static const struct clkgen_clk_out clkgen_stih407_d3_clk_out[] = {
575	{ .name = "clk-stfe-frc1", },
576	{ .name = "clk-tsout-0", },
577	{ .name = "clk-tsout-1", },
578	{ .name = "clk-mchi", },
579	{ .name = "clk-vsens-compo", },
580	{ .name = "clk-frc1-remote", },
581	{ .name = "clk-lpc-0", },
582	{ .name = "clk-lpc-1", },
583};
584
585static const struct clkgen_data clkgen_stih407_d3 = {
586	.outputs = clkgen_stih407_d3_clk_out,
587	.outputs_nb = ARRAY_SIZE(clkgen_stih407_d3_clk_out),
588};
589
590static const struct of_device_id flexgen_of_match[] = {
591	{
592		.compatible = "st,flexgen-audio",
593		.data = &clkgen_audio,
594	},
595	{
596		.compatible = "st,flexgen-video",
597		.data = &clkgen_video,
598	},
599	{
600		.compatible = "st,flexgen-stih407-a0",
601		.data = &clkgen_stih407_a0,
602	},
603	{
604		.compatible = "st,flexgen-stih410-a0",
605		.data = &clkgen_stih410_a0,
606	},
607	{
608		.compatible = "st,flexgen-stih407-c0",
609		.data = &clkgen_stih407_c0,
610	},
611	{
612		.compatible = "st,flexgen-stih410-c0",
613		.data = &clkgen_stih410_c0,
614	},
615	{
616		.compatible = "st,flexgen-stih418-c0",
617		.data = &clkgen_stih418_c0,
618	},
619	{
620		.compatible = "st,flexgen-stih407-d0",
621		.data = &clkgen_stih407_d0,
622	},
623	{
624		.compatible = "st,flexgen-stih410-d0",
625		.data = &clkgen_stih410_d0,
626	},
627	{
628		.compatible = "st,flexgen-stih407-d2",
629		.data = &clkgen_stih407_d2,
630	},
631	{
632		.compatible = "st,flexgen-stih418-d2",
633		.data = &clkgen_stih418_d2,
634	},
635	{
636		.compatible = "st,flexgen-stih407-d3",
637		.data = &clkgen_stih407_d3,
638	},
639	{}
640};
641
642static void __init st_of_flexgen_setup(struct device_node *np)
643{
644	struct device_node *pnode;
645	void __iomem *reg;
646	struct clk_onecell_data *clk_data;
647	const char **parents;
648	int num_parents, i;
649	spinlock_t *rlock = NULL;
650	const struct of_device_id *match;
651	struct clkgen_data *data = NULL;
652	unsigned long flex_flags = 0;
653	int ret;
654	bool clk_mode = 0;
655	const char *clk_name;
656
657	pnode = of_get_parent(np);
658	if (!pnode)
659		return;
660
661	reg = of_iomap(pnode, 0);
662	of_node_put(pnode);
663	if (!reg)
664		return;
665
666	parents = flexgen_get_parents(np, &num_parents);
667	if (!parents) {
668		iounmap(reg);
669		return;
670	}
671
672	match = of_match_node(flexgen_of_match, np);
673	if (match) {
674		data = (struct clkgen_data *)match->data;
675		flex_flags = data->flags;
676		clk_mode = data->mode;
677	}
678
679	clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
680	if (!clk_data)
681		goto err;
682
683	/* First try to get output information from the compatible data */
684	if (!data || !data->outputs_nb || !data->outputs) {
685		ret = of_property_count_strings(np, "clock-output-names");
686		if (ret <= 0) {
687			pr_err("%s: Failed to get number of output clocks (%d)",
688					__func__, clk_data->clk_num);
689			goto err;
690		}
691		clk_data->clk_num = ret;
692	} else
693		clk_data->clk_num = data->outputs_nb;
694
695	clk_data->clks = kcalloc(clk_data->clk_num, sizeof(struct clk *),
696			GFP_KERNEL);
697	if (!clk_data->clks)
698		goto err;
699
700	rlock = kzalloc(sizeof(spinlock_t), GFP_KERNEL);
701	if (!rlock)
702		goto err;
703
704	spin_lock_init(rlock);
705
706	for (i = 0; i < clk_data->clk_num; i++) {
707		struct clk *clk;
708
709		if (!data || !data->outputs_nb || !data->outputs) {
710			if (of_property_read_string_index(np,
711							  "clock-output-names",
712							  i, &clk_name))
713				break;
714			flex_flags &= ~CLK_IS_CRITICAL;
715			of_clk_detect_critical(np, i, &flex_flags);
716		} else {
717			clk_name = data->outputs[i].name;
718			flex_flags = data->flags | data->outputs[i].flags;
719		}
720
721		/*
722		 * If we read an empty clock name then the output is unused
723		 */
724		if (*clk_name == '\0')
725			continue;
726
727		clk = clk_register_flexgen(clk_name, parents, num_parents,
728					   reg, rlock, i, flex_flags, clk_mode);
729
730		if (IS_ERR(clk))
731			goto err;
732
733		clk_data->clks[i] = clk;
734	}
735
736	kfree(parents);
737	of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
738
739	return;
740
741err:
742	iounmap(reg);
743	if (clk_data)
744		kfree(clk_data->clks);
745	kfree(clk_data);
746	kfree(parents);
747	kfree(rlock);
748}
749CLK_OF_DECLARE(flexgen, "st,flexgen", st_of_flexgen_setup);