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