xref: /rk3399_ARM-atf/drivers/nxp/clk/s32cc/s32cc_clk_drv.c (revision 64e0c2260fa385bdf91d7e3471e10ab251c96644)
13a580e9eSGhennadi Procopciuc /*
23a580e9eSGhennadi Procopciuc  * Copyright 2024 NXP
33a580e9eSGhennadi Procopciuc  *
43a580e9eSGhennadi Procopciuc  * SPDX-License-Identifier: BSD-3-Clause
53a580e9eSGhennadi Procopciuc  */
63a580e9eSGhennadi Procopciuc #include <errno.h>
73a580e9eSGhennadi Procopciuc 
88ab34357SGhennadi Procopciuc #include <s32cc-clk-regs.h>
98ab34357SGhennadi Procopciuc 
10d9373519SGhennadi Procopciuc #include <common/debug.h>
113a580e9eSGhennadi Procopciuc #include <drivers/clk.h>
128ab34357SGhennadi Procopciuc #include <lib/mmio.h>
13d9373519SGhennadi Procopciuc #include <s32cc-clk-modules.h>
14d9373519SGhennadi Procopciuc #include <s32cc-clk-utils.h>
15d9373519SGhennadi Procopciuc 
16d9373519SGhennadi Procopciuc #define MAX_STACK_DEPTH		(15U)
17d9373519SGhennadi Procopciuc 
188ab34357SGhennadi Procopciuc struct s32cc_clk_drv {
198ab34357SGhennadi Procopciuc 	uintptr_t fxosc_base;
208ab34357SGhennadi Procopciuc };
218ab34357SGhennadi Procopciuc 
22d9373519SGhennadi Procopciuc static int update_stack_depth(unsigned int *depth)
23d9373519SGhennadi Procopciuc {
24d9373519SGhennadi Procopciuc 	if (*depth == 0U) {
25d9373519SGhennadi Procopciuc 		return -ENOMEM;
26d9373519SGhennadi Procopciuc 	}
27d9373519SGhennadi Procopciuc 
28d9373519SGhennadi Procopciuc 	(*depth)--;
29d9373519SGhennadi Procopciuc 	return 0;
30d9373519SGhennadi Procopciuc }
313a580e9eSGhennadi Procopciuc 
328ab34357SGhennadi Procopciuc static struct s32cc_clk_drv *get_drv(void)
338ab34357SGhennadi Procopciuc {
348ab34357SGhennadi Procopciuc 	static struct s32cc_clk_drv driver = {
358ab34357SGhennadi Procopciuc 		.fxosc_base = FXOSC_BASE_ADDR,
368ab34357SGhennadi Procopciuc 	};
378ab34357SGhennadi Procopciuc 
388ab34357SGhennadi Procopciuc 	return &driver;
398ab34357SGhennadi Procopciuc }
408ab34357SGhennadi Procopciuc 
418ab34357SGhennadi Procopciuc static int enable_module(const struct s32cc_clk_obj *module, unsigned int *depth);
428ab34357SGhennadi Procopciuc 
438ab34357SGhennadi Procopciuc static int enable_clk_module(const struct s32cc_clk_obj *module,
448ab34357SGhennadi Procopciuc 			     const struct s32cc_clk_drv *drv,
458ab34357SGhennadi Procopciuc 			     unsigned int *depth)
468ab34357SGhennadi Procopciuc {
478ab34357SGhennadi Procopciuc 	const struct s32cc_clk *clk = s32cc_obj2clk(module);
488ab34357SGhennadi Procopciuc 	int ret;
498ab34357SGhennadi Procopciuc 
508ab34357SGhennadi Procopciuc 	ret = update_stack_depth(depth);
518ab34357SGhennadi Procopciuc 	if (ret != 0) {
528ab34357SGhennadi Procopciuc 		return ret;
538ab34357SGhennadi Procopciuc 	}
548ab34357SGhennadi Procopciuc 
558ab34357SGhennadi Procopciuc 	if (clk == NULL) {
568ab34357SGhennadi Procopciuc 		return -EINVAL;
578ab34357SGhennadi Procopciuc 	}
588ab34357SGhennadi Procopciuc 
598ab34357SGhennadi Procopciuc 	if (clk->module != NULL) {
608ab34357SGhennadi Procopciuc 		return enable_module(clk->module, depth);
618ab34357SGhennadi Procopciuc 	}
628ab34357SGhennadi Procopciuc 
638ab34357SGhennadi Procopciuc 	if (clk->pclock != NULL) {
648ab34357SGhennadi Procopciuc 		return enable_clk_module(&clk->pclock->desc, drv, depth);
658ab34357SGhennadi Procopciuc 	}
668ab34357SGhennadi Procopciuc 
678ab34357SGhennadi Procopciuc 	return -EINVAL;
688ab34357SGhennadi Procopciuc }
698ab34357SGhennadi Procopciuc 
708ab34357SGhennadi Procopciuc static void enable_fxosc(const struct s32cc_clk_drv *drv)
718ab34357SGhennadi Procopciuc {
728ab34357SGhennadi Procopciuc 	uintptr_t fxosc_base = drv->fxosc_base;
738ab34357SGhennadi Procopciuc 	uint32_t ctrl;
748ab34357SGhennadi Procopciuc 
758ab34357SGhennadi Procopciuc 	ctrl = mmio_read_32(FXOSC_CTRL(fxosc_base));
768ab34357SGhennadi Procopciuc 	if ((ctrl & FXOSC_CTRL_OSCON) != U(0)) {
778ab34357SGhennadi Procopciuc 		return;
788ab34357SGhennadi Procopciuc 	}
798ab34357SGhennadi Procopciuc 
808ab34357SGhennadi Procopciuc 	ctrl = FXOSC_CTRL_COMP_EN;
818ab34357SGhennadi Procopciuc 	ctrl &= ~FXOSC_CTRL_OSC_BYP;
828ab34357SGhennadi Procopciuc 	ctrl |= FXOSC_CTRL_EOCV(0x1);
838ab34357SGhennadi Procopciuc 	ctrl |= FXOSC_CTRL_GM_SEL(0x7);
848ab34357SGhennadi Procopciuc 	mmio_write_32(FXOSC_CTRL(fxosc_base), ctrl);
858ab34357SGhennadi Procopciuc 
868ab34357SGhennadi Procopciuc 	/* Switch ON the crystal oscillator. */
878ab34357SGhennadi Procopciuc 	mmio_setbits_32(FXOSC_CTRL(fxosc_base), FXOSC_CTRL_OSCON);
888ab34357SGhennadi Procopciuc 
898ab34357SGhennadi Procopciuc 	/* Wait until the clock is stable. */
908ab34357SGhennadi Procopciuc 	while ((mmio_read_32(FXOSC_STAT(fxosc_base)) & FXOSC_STAT_OSC_STAT) == U(0)) {
918ab34357SGhennadi Procopciuc 	}
928ab34357SGhennadi Procopciuc }
938ab34357SGhennadi Procopciuc 
948ab34357SGhennadi Procopciuc static int enable_osc(const struct s32cc_clk_obj *module,
958ab34357SGhennadi Procopciuc 		      const struct s32cc_clk_drv *drv,
968ab34357SGhennadi Procopciuc 		      unsigned int *depth)
978ab34357SGhennadi Procopciuc {
988ab34357SGhennadi Procopciuc 	const struct s32cc_osc *osc = s32cc_obj2osc(module);
998ab34357SGhennadi Procopciuc 	int ret = 0;
1008ab34357SGhennadi Procopciuc 
1018ab34357SGhennadi Procopciuc 	ret = update_stack_depth(depth);
1028ab34357SGhennadi Procopciuc 	if (ret != 0) {
1038ab34357SGhennadi Procopciuc 		return ret;
1048ab34357SGhennadi Procopciuc 	}
1058ab34357SGhennadi Procopciuc 
1068ab34357SGhennadi Procopciuc 	switch (osc->source) {
1078ab34357SGhennadi Procopciuc 	case S32CC_FXOSC:
1088ab34357SGhennadi Procopciuc 		enable_fxosc(drv);
1098ab34357SGhennadi Procopciuc 		break;
1108ab34357SGhennadi Procopciuc 	/* FIRC and SIRC oscillators are enabled by default */
1118ab34357SGhennadi Procopciuc 	case S32CC_FIRC:
1128ab34357SGhennadi Procopciuc 		break;
1138ab34357SGhennadi Procopciuc 	case S32CC_SIRC:
1148ab34357SGhennadi Procopciuc 		break;
1158ab34357SGhennadi Procopciuc 	default:
1168ab34357SGhennadi Procopciuc 		ERROR("Invalid oscillator %d\n", osc->source);
1178ab34357SGhennadi Procopciuc 		ret = -EINVAL;
1188ab34357SGhennadi Procopciuc 		break;
1198ab34357SGhennadi Procopciuc 	};
1208ab34357SGhennadi Procopciuc 
1218ab34357SGhennadi Procopciuc 	return ret;
1228ab34357SGhennadi Procopciuc }
1238ab34357SGhennadi Procopciuc 
1248ab34357SGhennadi Procopciuc static int enable_module(const struct s32cc_clk_obj *module, unsigned int *depth)
1258ab34357SGhennadi Procopciuc {
1268ab34357SGhennadi Procopciuc 	const struct s32cc_clk_drv *drv = get_drv();
1278ab34357SGhennadi Procopciuc 	int ret = 0;
1288ab34357SGhennadi Procopciuc 
1298ab34357SGhennadi Procopciuc 	ret = update_stack_depth(depth);
1308ab34357SGhennadi Procopciuc 	if (ret != 0) {
1318ab34357SGhennadi Procopciuc 		return ret;
1328ab34357SGhennadi Procopciuc 	}
1338ab34357SGhennadi Procopciuc 
1348ab34357SGhennadi Procopciuc 	if (drv == NULL) {
1358ab34357SGhennadi Procopciuc 		return -EINVAL;
1368ab34357SGhennadi Procopciuc 	}
1378ab34357SGhennadi Procopciuc 
1388ab34357SGhennadi Procopciuc 	switch (module->type) {
1398ab34357SGhennadi Procopciuc 	case s32cc_osc_t:
1408ab34357SGhennadi Procopciuc 		ret = enable_osc(module, drv, depth);
1418ab34357SGhennadi Procopciuc 		break;
1428ab34357SGhennadi Procopciuc 	case s32cc_clk_t:
1438ab34357SGhennadi Procopciuc 		ret = enable_clk_module(module, drv, depth);
1448ab34357SGhennadi Procopciuc 		break;
145a8be748aSGhennadi Procopciuc 	case s32cc_clkmux_t:
146a8be748aSGhennadi Procopciuc 		ret = -ENOTSUP;
147a8be748aSGhennadi Procopciuc 		break;
1483fa91a94SGhennadi Procopciuc 	case s32cc_shared_clkmux_t:
1493fa91a94SGhennadi Procopciuc 		ret = -ENOTSUP;
1503fa91a94SGhennadi Procopciuc 		break;
151a8be748aSGhennadi Procopciuc 	case s32cc_pll_t:
152a8be748aSGhennadi Procopciuc 		ret = -ENOTSUP;
153a8be748aSGhennadi Procopciuc 		break;
154a8be748aSGhennadi Procopciuc 	case s32cc_pll_out_div_t:
15544e2130aSGhennadi Procopciuc 	case s32cc_fixed_div_t:
156a8be748aSGhennadi Procopciuc 		ret = -ENOTSUP;
157a8be748aSGhennadi Procopciuc 		break;
1588ab34357SGhennadi Procopciuc 	default:
1598ab34357SGhennadi Procopciuc 		ret = -EINVAL;
1608ab34357SGhennadi Procopciuc 		break;
1618ab34357SGhennadi Procopciuc 	}
1628ab34357SGhennadi Procopciuc 
1638ab34357SGhennadi Procopciuc 	return ret;
1648ab34357SGhennadi Procopciuc }
1658ab34357SGhennadi Procopciuc 
1663a580e9eSGhennadi Procopciuc static int s32cc_clk_enable(unsigned long id)
1673a580e9eSGhennadi Procopciuc {
1688ab34357SGhennadi Procopciuc 	unsigned int depth = MAX_STACK_DEPTH;
1698ab34357SGhennadi Procopciuc 	const struct s32cc_clk *clk;
1708ab34357SGhennadi Procopciuc 
1718ab34357SGhennadi Procopciuc 	clk = s32cc_get_arch_clk(id);
1728ab34357SGhennadi Procopciuc 	if (clk == NULL) {
1738ab34357SGhennadi Procopciuc 		return -EINVAL;
1748ab34357SGhennadi Procopciuc 	}
1758ab34357SGhennadi Procopciuc 
1768ab34357SGhennadi Procopciuc 	return enable_module(&clk->desc, &depth);
1773a580e9eSGhennadi Procopciuc }
1783a580e9eSGhennadi Procopciuc 
1793a580e9eSGhennadi Procopciuc static void s32cc_clk_disable(unsigned long id)
1803a580e9eSGhennadi Procopciuc {
1813a580e9eSGhennadi Procopciuc }
1823a580e9eSGhennadi Procopciuc 
1833a580e9eSGhennadi Procopciuc static bool s32cc_clk_is_enabled(unsigned long id)
1843a580e9eSGhennadi Procopciuc {
1853a580e9eSGhennadi Procopciuc 	return false;
1863a580e9eSGhennadi Procopciuc }
1873a580e9eSGhennadi Procopciuc 
1883a580e9eSGhennadi Procopciuc static unsigned long s32cc_clk_get_rate(unsigned long id)
1893a580e9eSGhennadi Procopciuc {
1903a580e9eSGhennadi Procopciuc 	return 0;
1913a580e9eSGhennadi Procopciuc }
1923a580e9eSGhennadi Procopciuc 
193d9373519SGhennadi Procopciuc static int set_module_rate(const struct s32cc_clk_obj *module,
194d9373519SGhennadi Procopciuc 			   unsigned long rate, unsigned long *orate,
195d9373519SGhennadi Procopciuc 			   unsigned int *depth);
196d9373519SGhennadi Procopciuc 
197d9373519SGhennadi Procopciuc static int set_osc_freq(const struct s32cc_clk_obj *module, unsigned long rate,
198d9373519SGhennadi Procopciuc 			unsigned long *orate, unsigned int *depth)
199d9373519SGhennadi Procopciuc {
200d9373519SGhennadi Procopciuc 	struct s32cc_osc *osc = s32cc_obj2osc(module);
201d9373519SGhennadi Procopciuc 	int ret;
202d9373519SGhennadi Procopciuc 
203d9373519SGhennadi Procopciuc 	ret = update_stack_depth(depth);
204d9373519SGhennadi Procopciuc 	if (ret != 0) {
205d9373519SGhennadi Procopciuc 		return ret;
206d9373519SGhennadi Procopciuc 	}
207d9373519SGhennadi Procopciuc 
208d9373519SGhennadi Procopciuc 	if ((osc->freq != 0UL) && (rate != osc->freq)) {
209d9373519SGhennadi Procopciuc 		ERROR("Already initialized oscillator. freq = %lu\n",
210d9373519SGhennadi Procopciuc 		      osc->freq);
211d9373519SGhennadi Procopciuc 		return -EINVAL;
212d9373519SGhennadi Procopciuc 	}
213d9373519SGhennadi Procopciuc 
214d9373519SGhennadi Procopciuc 	osc->freq = rate;
215d9373519SGhennadi Procopciuc 	*orate = osc->freq;
216d9373519SGhennadi Procopciuc 
217d9373519SGhennadi Procopciuc 	return 0;
218d9373519SGhennadi Procopciuc }
219d9373519SGhennadi Procopciuc 
220d9373519SGhennadi Procopciuc static int set_clk_freq(const struct s32cc_clk_obj *module, unsigned long rate,
221d9373519SGhennadi Procopciuc 			unsigned long *orate, unsigned int *depth)
222d9373519SGhennadi Procopciuc {
223d9373519SGhennadi Procopciuc 	const struct s32cc_clk *clk = s32cc_obj2clk(module);
224d9373519SGhennadi Procopciuc 	int ret;
225d9373519SGhennadi Procopciuc 
226d9373519SGhennadi Procopciuc 	ret = update_stack_depth(depth);
227d9373519SGhennadi Procopciuc 	if (ret != 0) {
228d9373519SGhennadi Procopciuc 		return ret;
229d9373519SGhennadi Procopciuc 	}
230d9373519SGhennadi Procopciuc 
231d9373519SGhennadi Procopciuc 	if ((clk->min_freq != 0UL) && (clk->max_freq != 0UL) &&
232d9373519SGhennadi Procopciuc 	    ((rate < clk->min_freq) || (rate > clk->max_freq))) {
233d9373519SGhennadi Procopciuc 		ERROR("%lu frequency is out of the allowed range: [%lu:%lu]\n",
234d9373519SGhennadi Procopciuc 		      rate, clk->min_freq, clk->max_freq);
235d9373519SGhennadi Procopciuc 		return -EINVAL;
236d9373519SGhennadi Procopciuc 	}
237d9373519SGhennadi Procopciuc 
238d9373519SGhennadi Procopciuc 	if (clk->module != NULL) {
239d9373519SGhennadi Procopciuc 		return set_module_rate(clk->module, rate, orate, depth);
240d9373519SGhennadi Procopciuc 	}
241d9373519SGhennadi Procopciuc 
242d9373519SGhennadi Procopciuc 	if (clk->pclock != NULL) {
243d9373519SGhennadi Procopciuc 		return set_clk_freq(&clk->pclock->desc, rate, orate, depth);
244d9373519SGhennadi Procopciuc 	}
245d9373519SGhennadi Procopciuc 
246d9373519SGhennadi Procopciuc 	return -EINVAL;
247d9373519SGhennadi Procopciuc }
248d9373519SGhennadi Procopciuc 
2497ad4e231SGhennadi Procopciuc static int set_pll_freq(const struct s32cc_clk_obj *module, unsigned long rate,
2507ad4e231SGhennadi Procopciuc 			unsigned long *orate, unsigned int *depth)
2517ad4e231SGhennadi Procopciuc {
2527ad4e231SGhennadi Procopciuc 	struct s32cc_pll *pll = s32cc_obj2pll(module);
2537ad4e231SGhennadi Procopciuc 	int ret;
2547ad4e231SGhennadi Procopciuc 
2557ad4e231SGhennadi Procopciuc 	ret = update_stack_depth(depth);
2567ad4e231SGhennadi Procopciuc 	if (ret != 0) {
2577ad4e231SGhennadi Procopciuc 		return ret;
2587ad4e231SGhennadi Procopciuc 	}
2597ad4e231SGhennadi Procopciuc 
2607ad4e231SGhennadi Procopciuc 	if ((pll->vco_freq != 0UL) && (pll->vco_freq != rate)) {
2617ad4e231SGhennadi Procopciuc 		ERROR("PLL frequency was already set\n");
2627ad4e231SGhennadi Procopciuc 		return -EINVAL;
2637ad4e231SGhennadi Procopciuc 	}
2647ad4e231SGhennadi Procopciuc 
2657ad4e231SGhennadi Procopciuc 	pll->vco_freq = rate;
2667ad4e231SGhennadi Procopciuc 	*orate = pll->vco_freq;
2677ad4e231SGhennadi Procopciuc 
2687ad4e231SGhennadi Procopciuc 	return 0;
2697ad4e231SGhennadi Procopciuc }
2707ad4e231SGhennadi Procopciuc 
271de950ef0SGhennadi Procopciuc static int set_pll_div_freq(const struct s32cc_clk_obj *module, unsigned long rate,
272de950ef0SGhennadi Procopciuc 			    unsigned long *orate, unsigned int *depth)
273de950ef0SGhennadi Procopciuc {
274de950ef0SGhennadi Procopciuc 	struct s32cc_pll_out_div *pdiv = s32cc_obj2plldiv(module);
275de950ef0SGhennadi Procopciuc 	const struct s32cc_pll *pll;
276de950ef0SGhennadi Procopciuc 	unsigned long prate, dc;
277de950ef0SGhennadi Procopciuc 	int ret;
278de950ef0SGhennadi Procopciuc 
279de950ef0SGhennadi Procopciuc 	ret = update_stack_depth(depth);
280de950ef0SGhennadi Procopciuc 	if (ret != 0) {
281de950ef0SGhennadi Procopciuc 		return ret;
282de950ef0SGhennadi Procopciuc 	}
283de950ef0SGhennadi Procopciuc 
284de950ef0SGhennadi Procopciuc 	if (pdiv->parent == NULL) {
285de950ef0SGhennadi Procopciuc 		ERROR("Failed to identify PLL divider's parent\n");
286de950ef0SGhennadi Procopciuc 		return -EINVAL;
287de950ef0SGhennadi Procopciuc 	}
288de950ef0SGhennadi Procopciuc 
289de950ef0SGhennadi Procopciuc 	pll = s32cc_obj2pll(pdiv->parent);
290de950ef0SGhennadi Procopciuc 	if (pll == NULL) {
291de950ef0SGhennadi Procopciuc 		ERROR("The parent of the PLL DIV is invalid\n");
292de950ef0SGhennadi Procopciuc 		return -EINVAL;
293de950ef0SGhennadi Procopciuc 	}
294de950ef0SGhennadi Procopciuc 
295de950ef0SGhennadi Procopciuc 	prate = pll->vco_freq;
296de950ef0SGhennadi Procopciuc 
297de950ef0SGhennadi Procopciuc 	/**
298de950ef0SGhennadi Procopciuc 	 * The PLL is not initialized yet, so let's take a risk
299de950ef0SGhennadi Procopciuc 	 * and accept the proposed rate.
300de950ef0SGhennadi Procopciuc 	 */
301de950ef0SGhennadi Procopciuc 	if (prate == 0UL) {
302de950ef0SGhennadi Procopciuc 		pdiv->freq = rate;
303de950ef0SGhennadi Procopciuc 		*orate = rate;
304de950ef0SGhennadi Procopciuc 		return 0;
305de950ef0SGhennadi Procopciuc 	}
306de950ef0SGhennadi Procopciuc 
307de950ef0SGhennadi Procopciuc 	/* Decline in case the rate cannot fit PLL's requirements. */
308de950ef0SGhennadi Procopciuc 	dc = prate / rate;
309de950ef0SGhennadi Procopciuc 	if ((prate / dc) != rate) {
310de950ef0SGhennadi Procopciuc 		return -EINVAL;
311de950ef0SGhennadi Procopciuc 	}
312de950ef0SGhennadi Procopciuc 
313de950ef0SGhennadi Procopciuc 	pdiv->freq = rate;
314de950ef0SGhennadi Procopciuc 	*orate = pdiv->freq;
315de950ef0SGhennadi Procopciuc 
316de950ef0SGhennadi Procopciuc 	return 0;
317de950ef0SGhennadi Procopciuc }
318de950ef0SGhennadi Procopciuc 
31965739db2SGhennadi Procopciuc static int set_fixed_div_freq(const struct s32cc_clk_obj *module, unsigned long rate,
32065739db2SGhennadi Procopciuc 			      unsigned long *orate, unsigned int *depth)
32165739db2SGhennadi Procopciuc {
32265739db2SGhennadi Procopciuc 	const struct s32cc_fixed_div *fdiv = s32cc_obj2fixeddiv(module);
32365739db2SGhennadi Procopciuc 	int ret;
32465739db2SGhennadi Procopciuc 
32565739db2SGhennadi Procopciuc 	ret = update_stack_depth(depth);
32665739db2SGhennadi Procopciuc 	if (ret != 0) {
32765739db2SGhennadi Procopciuc 		return ret;
32865739db2SGhennadi Procopciuc 	}
32965739db2SGhennadi Procopciuc 
33065739db2SGhennadi Procopciuc 	if (fdiv->parent == NULL) {
33165739db2SGhennadi Procopciuc 		ERROR("The divider doesn't have a valid parent\b");
33265739db2SGhennadi Procopciuc 		return -EINVAL;
33365739db2SGhennadi Procopciuc 	}
33465739db2SGhennadi Procopciuc 
33565739db2SGhennadi Procopciuc 	ret = set_module_rate(fdiv->parent, rate * fdiv->rate_div, orate, depth);
33665739db2SGhennadi Procopciuc 
33765739db2SGhennadi Procopciuc 	/* Update the output rate based on the parent's rate */
33865739db2SGhennadi Procopciuc 	*orate /= fdiv->rate_div;
33965739db2SGhennadi Procopciuc 
34065739db2SGhennadi Procopciuc 	return ret;
34165739db2SGhennadi Procopciuc }
34265739db2SGhennadi Procopciuc 
343*64e0c226SGhennadi Procopciuc static int set_mux_freq(const struct s32cc_clk_obj *module, unsigned long rate,
344*64e0c226SGhennadi Procopciuc 			unsigned long *orate, unsigned int *depth)
345*64e0c226SGhennadi Procopciuc {
346*64e0c226SGhennadi Procopciuc 	const struct s32cc_clkmux *mux = s32cc_obj2clkmux(module);
347*64e0c226SGhennadi Procopciuc 	const struct s32cc_clk *clk = s32cc_get_arch_clk(mux->source_id);
348*64e0c226SGhennadi Procopciuc 	int ret;
349*64e0c226SGhennadi Procopciuc 
350*64e0c226SGhennadi Procopciuc 	ret = update_stack_depth(depth);
351*64e0c226SGhennadi Procopciuc 	if (ret != 0) {
352*64e0c226SGhennadi Procopciuc 		return ret;
353*64e0c226SGhennadi Procopciuc 	}
354*64e0c226SGhennadi Procopciuc 
355*64e0c226SGhennadi Procopciuc 	if (clk == NULL) {
356*64e0c226SGhennadi Procopciuc 		ERROR("Mux (id:%" PRIu8 ") without a valid source (%lu)\n",
357*64e0c226SGhennadi Procopciuc 		      mux->index, mux->source_id);
358*64e0c226SGhennadi Procopciuc 		return -EINVAL;
359*64e0c226SGhennadi Procopciuc 	}
360*64e0c226SGhennadi Procopciuc 
361*64e0c226SGhennadi Procopciuc 	return set_module_rate(&clk->desc, rate, orate, depth);
362*64e0c226SGhennadi Procopciuc }
363*64e0c226SGhennadi Procopciuc 
364d9373519SGhennadi Procopciuc static int set_module_rate(const struct s32cc_clk_obj *module,
365d9373519SGhennadi Procopciuc 			   unsigned long rate, unsigned long *orate,
366d9373519SGhennadi Procopciuc 			   unsigned int *depth)
367d9373519SGhennadi Procopciuc {
368d9373519SGhennadi Procopciuc 	int ret = 0;
369d9373519SGhennadi Procopciuc 
370d9373519SGhennadi Procopciuc 	ret = update_stack_depth(depth);
371d9373519SGhennadi Procopciuc 	if (ret != 0) {
372d9373519SGhennadi Procopciuc 		return ret;
373d9373519SGhennadi Procopciuc 	}
374d9373519SGhennadi Procopciuc 
375d9373519SGhennadi Procopciuc 	switch (module->type) {
376d9373519SGhennadi Procopciuc 	case s32cc_clk_t:
377d9373519SGhennadi Procopciuc 		ret = set_clk_freq(module, rate, orate, depth);
378d9373519SGhennadi Procopciuc 		break;
379d9373519SGhennadi Procopciuc 	case s32cc_osc_t:
380d9373519SGhennadi Procopciuc 		ret = set_osc_freq(module, rate, orate, depth);
381d9373519SGhennadi Procopciuc 		break;
3827ad4e231SGhennadi Procopciuc 	case s32cc_pll_t:
3837ad4e231SGhennadi Procopciuc 		ret = set_pll_freq(module, rate, orate, depth);
3847ad4e231SGhennadi Procopciuc 		break;
385de950ef0SGhennadi Procopciuc 	case s32cc_pll_out_div_t:
386de950ef0SGhennadi Procopciuc 		ret = set_pll_div_freq(module, rate, orate, depth);
387de950ef0SGhennadi Procopciuc 		break;
38865739db2SGhennadi Procopciuc 	case s32cc_fixed_div_t:
38965739db2SGhennadi Procopciuc 		ret = set_fixed_div_freq(module, rate, orate, depth);
39065739db2SGhennadi Procopciuc 		break;
391a8be748aSGhennadi Procopciuc 	case s32cc_clkmux_t:
392*64e0c226SGhennadi Procopciuc 		ret = set_mux_freq(module, rate, orate, depth);
393*64e0c226SGhennadi Procopciuc 		break;
3943fa91a94SGhennadi Procopciuc 	case s32cc_shared_clkmux_t:
395*64e0c226SGhennadi Procopciuc 		ret = set_mux_freq(module, rate, orate, depth);
396a8be748aSGhennadi Procopciuc 		break;
397d9373519SGhennadi Procopciuc 	default:
398d9373519SGhennadi Procopciuc 		ret = -EINVAL;
399d9373519SGhennadi Procopciuc 		break;
400d9373519SGhennadi Procopciuc 	}
401d9373519SGhennadi Procopciuc 
402d9373519SGhennadi Procopciuc 	return ret;
403d9373519SGhennadi Procopciuc }
404d9373519SGhennadi Procopciuc 
4053a580e9eSGhennadi Procopciuc static int s32cc_clk_set_rate(unsigned long id, unsigned long rate,
4063a580e9eSGhennadi Procopciuc 			      unsigned long *orate)
4073a580e9eSGhennadi Procopciuc {
408d9373519SGhennadi Procopciuc 	unsigned int depth = MAX_STACK_DEPTH;
409d9373519SGhennadi Procopciuc 	const struct s32cc_clk *clk;
410d9373519SGhennadi Procopciuc 	int ret;
411d9373519SGhennadi Procopciuc 
412d9373519SGhennadi Procopciuc 	clk = s32cc_get_arch_clk(id);
413d9373519SGhennadi Procopciuc 	if (clk == NULL) {
414d9373519SGhennadi Procopciuc 		return -EINVAL;
415d9373519SGhennadi Procopciuc 	}
416d9373519SGhennadi Procopciuc 
417d9373519SGhennadi Procopciuc 	ret = set_module_rate(&clk->desc, rate, orate, &depth);
418d9373519SGhennadi Procopciuc 	if (ret != 0) {
419d9373519SGhennadi Procopciuc 		ERROR("Failed to set frequency (%lu MHz) for clock %lu\n",
420d9373519SGhennadi Procopciuc 		      rate, id);
421d9373519SGhennadi Procopciuc 	}
422d9373519SGhennadi Procopciuc 
423d9373519SGhennadi Procopciuc 	return ret;
4243a580e9eSGhennadi Procopciuc }
4253a580e9eSGhennadi Procopciuc 
4263a580e9eSGhennadi Procopciuc static int s32cc_clk_get_parent(unsigned long id)
4273a580e9eSGhennadi Procopciuc {
4283a580e9eSGhennadi Procopciuc 	return -ENOTSUP;
4293a580e9eSGhennadi Procopciuc }
4303a580e9eSGhennadi Procopciuc 
4313a580e9eSGhennadi Procopciuc static int s32cc_clk_set_parent(unsigned long id, unsigned long parent_id)
4323a580e9eSGhennadi Procopciuc {
43312e7a2cdSGhennadi Procopciuc 	const struct s32cc_clk *parent;
43412e7a2cdSGhennadi Procopciuc 	const struct s32cc_clk *clk;
43512e7a2cdSGhennadi Procopciuc 	bool valid_source = false;
43612e7a2cdSGhennadi Procopciuc 	struct s32cc_clkmux *mux;
43712e7a2cdSGhennadi Procopciuc 	uint8_t i;
43812e7a2cdSGhennadi Procopciuc 
43912e7a2cdSGhennadi Procopciuc 	clk = s32cc_get_arch_clk(id);
44012e7a2cdSGhennadi Procopciuc 	if (clk == NULL) {
44112e7a2cdSGhennadi Procopciuc 		return -EINVAL;
44212e7a2cdSGhennadi Procopciuc 	}
44312e7a2cdSGhennadi Procopciuc 
44412e7a2cdSGhennadi Procopciuc 	parent = s32cc_get_arch_clk(parent_id);
44512e7a2cdSGhennadi Procopciuc 	if (parent == NULL) {
44612e7a2cdSGhennadi Procopciuc 		return -EINVAL;
44712e7a2cdSGhennadi Procopciuc 	}
44812e7a2cdSGhennadi Procopciuc 
44912e7a2cdSGhennadi Procopciuc 	if (!is_s32cc_clk_mux(clk)) {
45012e7a2cdSGhennadi Procopciuc 		ERROR("Clock %lu is not a mux\n", id);
45112e7a2cdSGhennadi Procopciuc 		return -EINVAL;
45212e7a2cdSGhennadi Procopciuc 	}
45312e7a2cdSGhennadi Procopciuc 
45412e7a2cdSGhennadi Procopciuc 	mux = s32cc_clk2mux(clk);
45512e7a2cdSGhennadi Procopciuc 	if (mux == NULL) {
45612e7a2cdSGhennadi Procopciuc 		ERROR("Failed to cast clock %lu to clock mux\n", id);
45712e7a2cdSGhennadi Procopciuc 		return -EINVAL;
45812e7a2cdSGhennadi Procopciuc 	}
45912e7a2cdSGhennadi Procopciuc 
46012e7a2cdSGhennadi Procopciuc 	for (i = 0; i < mux->nclks; i++) {
46112e7a2cdSGhennadi Procopciuc 		if (mux->clkids[i] == parent_id) {
46212e7a2cdSGhennadi Procopciuc 			valid_source = true;
46312e7a2cdSGhennadi Procopciuc 			break;
46412e7a2cdSGhennadi Procopciuc 		}
46512e7a2cdSGhennadi Procopciuc 	}
46612e7a2cdSGhennadi Procopciuc 
46712e7a2cdSGhennadi Procopciuc 	if (!valid_source) {
46812e7a2cdSGhennadi Procopciuc 		ERROR("Clock %lu is not a valid clock for mux %lu\n",
46912e7a2cdSGhennadi Procopciuc 		      parent_id, id);
47012e7a2cdSGhennadi Procopciuc 		return -EINVAL;
47112e7a2cdSGhennadi Procopciuc 	}
47212e7a2cdSGhennadi Procopciuc 
47312e7a2cdSGhennadi Procopciuc 	mux->source_id = parent_id;
47412e7a2cdSGhennadi Procopciuc 
47512e7a2cdSGhennadi Procopciuc 	return 0;
4763a580e9eSGhennadi Procopciuc }
4773a580e9eSGhennadi Procopciuc 
4783a580e9eSGhennadi Procopciuc void s32cc_clk_register_drv(void)
4793a580e9eSGhennadi Procopciuc {
4803a580e9eSGhennadi Procopciuc 	static const struct clk_ops s32cc_clk_ops = {
4813a580e9eSGhennadi Procopciuc 		.enable		= s32cc_clk_enable,
4823a580e9eSGhennadi Procopciuc 		.disable	= s32cc_clk_disable,
4833a580e9eSGhennadi Procopciuc 		.is_enabled	= s32cc_clk_is_enabled,
4843a580e9eSGhennadi Procopciuc 		.get_rate	= s32cc_clk_get_rate,
4853a580e9eSGhennadi Procopciuc 		.set_rate	= s32cc_clk_set_rate,
4863a580e9eSGhennadi Procopciuc 		.get_parent	= s32cc_clk_get_parent,
4873a580e9eSGhennadi Procopciuc 		.set_parent	= s32cc_clk_set_parent,
4883a580e9eSGhennadi Procopciuc 	};
4893a580e9eSGhennadi Procopciuc 
4903a580e9eSGhennadi Procopciuc 	clk_register(&s32cc_clk_ops);
4913a580e9eSGhennadi Procopciuc }
4923a580e9eSGhennadi Procopciuc 
493