brintos

brintos / linux-shallow public Read only

0
0
Text · 20.1 KiB · 5292208 Raw
750 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * clk-flexgen.c4 *5 * Copyright (C) ST-Microelectronics SA 20136 * 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 long117clk_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 fdiv180	* should be used for div <= 64. The other way round can181	* 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	else271		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	} else693		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 unused723		 */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);750