xref: /optee_os/core/drivers/clk/sam/at91_master.c (revision 5110b3e7ade5d465022e30eb0c2830060e1e1070)
16ea4e740SClément Léger // SPDX-License-Identifier: GPL-2.0+ or BSD-3-Clause
26ea4e740SClément Léger /*
36ea4e740SClément Léger  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
46ea4e740SClément Léger  *  Copyright (C) 2021 Microchip
56ea4e740SClément Léger  */
66ea4e740SClément Léger 
76ea4e740SClément Léger #include <io.h>
86ea4e740SClément Léger #include <kernel/delay.h>
96ea4e740SClément Léger #include <mm/core_memprot.h>
106ea4e740SClément Léger #include <types_ext.h>
116ea4e740SClément Léger 
126ea4e740SClément Léger #include "at91_clk.h"
136ea4e740SClément Léger 
146ea4e740SClément Léger #define MASTER_PRES_MASK	0x7
156ea4e740SClément Léger #define MASTER_PRES_MAX		MASTER_PRES_MASK
166ea4e740SClément Léger #define MASTER_DIV_SHIFT	8
176ea4e740SClément Léger #define MASTER_DIV_MASK		0x7
18*5110b3e7STony Han #define MASTER_MAX_ID		4 /* Total 5 MCK clocks for SAMA7G5 */
196ea4e740SClément Léger 
206ea4e740SClément Léger struct clk_master {
216ea4e740SClément Léger 	vaddr_t base;
226ea4e740SClément Léger 	const struct clk_master_layout *layout;
236ea4e740SClément Léger 	const struct clk_master_charac *charac;
246ea4e740SClément Léger 	uint32_t *mux_table;
256ea4e740SClément Léger 	uint32_t mckr;
266ea4e740SClément Léger 	int chg_pid;
276ea4e740SClément Léger 	uint8_t div;
28*5110b3e7STony Han 	uint8_t id; /* ID of MCK clocks for SAMA7G5, MCK0 ~ MCK4 */
29*5110b3e7STony Han 	uint8_t parent; /* the source clock for SAMA7G5 MCKx */
306ea4e740SClément Léger };
316ea4e740SClément Léger 
clk_master_ready(struct clk_master * master)326ea4e740SClément Léger static bool clk_master_ready(struct clk_master *master)
336ea4e740SClément Léger {
346ea4e740SClément Léger 	uint32_t status = io_read32(master->base + AT91_PMC_SR);
356ea4e740SClément Léger 
366ea4e740SClément Léger 	return status & AT91_PMC_MCKRDY;
376ea4e740SClément Léger }
386ea4e740SClément Léger 
clk_master_enable(struct clk * clk)396ea4e740SClément Léger static TEE_Result clk_master_enable(struct clk *clk)
406ea4e740SClément Léger {
416ea4e740SClément Léger 	struct clk_master *master = clk->priv;
426ea4e740SClément Léger 
436ea4e740SClément Léger 	while (!clk_master_ready(master))
446ea4e740SClément Léger 		;
456ea4e740SClément Léger 
466ea4e740SClément Léger 	return TEE_SUCCESS;
476ea4e740SClément Léger }
486ea4e740SClément Léger 
clk_master_div_get_rate(struct clk * clk,unsigned long parent_rate)496ea4e740SClément Léger static unsigned long clk_master_div_get_rate(struct clk *clk,
506ea4e740SClément Léger 					     unsigned long parent_rate)
516ea4e740SClément Léger {
526ea4e740SClément Léger 	uint8_t div = 1;
536ea4e740SClément Léger 	uint32_t mckr = 0;
546ea4e740SClément Léger 	unsigned long rate = parent_rate;
556ea4e740SClément Léger 	struct clk_master *master = clk->priv;
566ea4e740SClément Léger 	const struct clk_master_layout *layout = master->layout;
576ea4e740SClément Léger 	const struct clk_master_charac *charac = master->charac;
586ea4e740SClément Léger 
596ea4e740SClément Léger 	mckr = io_read32(master->base + master->layout->offset);
606ea4e740SClément Léger 
616ea4e740SClément Léger 	mckr &= layout->mask;
626ea4e740SClément Léger 
636ea4e740SClément Léger 	div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
646ea4e740SClément Léger 
656ea4e740SClément Léger 	rate /= charac->divisors[div];
666ea4e740SClément Léger 
676ea4e740SClément Léger 	if (rate < charac->output.min)
686ea4e740SClément Léger 		IMSG("master clk div is underclocked");
696ea4e740SClément Léger 	else if (rate > charac->output.max)
706ea4e740SClément Léger 		IMSG("master clk div is overclocked");
716ea4e740SClément Léger 
726ea4e740SClément Léger 	return rate;
736ea4e740SClément Léger }
746ea4e740SClément Léger 
756ea4e740SClément Léger static const struct clk_ops master_div_ops = {
766ea4e740SClément Léger 	.enable = clk_master_enable,
776ea4e740SClément Léger 	.get_rate = clk_master_div_get_rate,
786ea4e740SClément Léger };
796ea4e740SClément Léger 
clk_master_pres_get_rate(struct clk * clk,unsigned long parent_rate)806ea4e740SClément Léger static unsigned long clk_master_pres_get_rate(struct clk *clk,
816ea4e740SClément Léger 					      unsigned long parent_rate)
826ea4e740SClément Léger {
836ea4e740SClément Léger 	struct clk_master *master = clk->priv;
846ea4e740SClément Léger 	const struct clk_master_charac *charac = master->charac;
856ea4e740SClément Léger 	uint32_t val = 0;
866ea4e740SClément Léger 	unsigned int pres = 0;
876ea4e740SClément Léger 
886ea4e740SClément Léger 	val = io_read32(master->base + master->layout->offset);
896ea4e740SClément Léger 
906ea4e740SClément Léger 	pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK;
916ea4e740SClément Léger 	if (pres != 3 || !charac->have_div3_pres)
926ea4e740SClément Léger 		pres = BIT(pres);
936ea4e740SClément Léger 
946ea4e740SClément Léger 	return UDIV_ROUND_NEAREST(parent_rate, pres);
956ea4e740SClément Léger }
966ea4e740SClément Léger 
clk_master_pres_get_parent(struct clk * clk)976ea4e740SClément Léger static size_t clk_master_pres_get_parent(struct clk *clk)
986ea4e740SClément Léger {
996ea4e740SClément Léger 	struct clk_master *master = clk->priv;
1006ea4e740SClément Léger 	uint32_t mckr = 0;
1016ea4e740SClément Léger 
1026ea4e740SClément Léger 	mckr = io_read32(master->base + master->layout->offset);
1036ea4e740SClément Léger 
1046ea4e740SClément Léger 	return mckr & AT91_PMC_CSS;
1056ea4e740SClément Léger }
1066ea4e740SClément Léger 
1076ea4e740SClément Léger static const struct clk_ops master_pres_ops = {
1086ea4e740SClément Léger 	.enable = clk_master_enable,
1096ea4e740SClément Léger 	.get_rate = clk_master_pres_get_rate,
1106ea4e740SClément Léger 	.get_parent = clk_master_pres_get_parent,
1116ea4e740SClément Léger };
1126ea4e740SClément Léger 
1136ea4e740SClément Léger static struct clk *
at91_clk_register_master_internal(struct pmc_data * pmc,const char * name,int num_parents,struct clk ** parents,const struct clk_master_layout * layout,const struct clk_master_charac * charac,const struct clk_ops * ops,int chg_pid)1146ea4e740SClément Léger at91_clk_register_master_internal(struct pmc_data *pmc,
1156ea4e740SClément Léger 				  const char *name, int num_parents,
1166ea4e740SClément Léger 				  struct clk **parents,
1176ea4e740SClément Léger 				  const struct clk_master_layout *layout,
1186ea4e740SClément Léger 				  const struct clk_master_charac *charac,
1196ea4e740SClément Léger 				  const struct clk_ops *ops, int chg_pid)
1206ea4e740SClément Léger {
1216ea4e740SClément Léger 	struct clk_master *master = NULL;
1226ea4e740SClément Léger 	struct clk *clk = NULL;
1236ea4e740SClément Léger 
1246ea4e740SClément Léger 	if (!name || !num_parents || !parents)
1256ea4e740SClément Léger 		return NULL;
1266ea4e740SClément Léger 
1276ea4e740SClément Léger 	clk = clk_alloc(name, ops, parents, num_parents);
1286ea4e740SClément Léger 	if (!clk)
1296ea4e740SClément Léger 		return NULL;
1306ea4e740SClément Léger 
1316ea4e740SClément Léger 	master = calloc(1, sizeof(*master));
1326ea4e740SClément Léger 	if (!master) {
1336ea4e740SClément Léger 		clk_free(clk);
1346ea4e740SClément Léger 		return NULL;
1356ea4e740SClément Léger 	}
1366ea4e740SClément Léger 
1376ea4e740SClément Léger 	master->layout = layout;
1386ea4e740SClément Léger 	master->charac = charac;
1396ea4e740SClément Léger 	master->base = pmc->base;
1406ea4e740SClément Léger 	master->chg_pid = chg_pid;
1416ea4e740SClément Léger 
1426ea4e740SClément Léger 	clk->priv = master;
1436ea4e740SClément Léger 	clk->flags = CLK_SET_RATE_GATE;
1446ea4e740SClément Léger 
1456ea4e740SClément Léger 	if (clk_register(clk)) {
1466ea4e740SClément Léger 		clk_free(clk);
1476ea4e740SClément Léger 		free(master);
1486ea4e740SClément Léger 		return NULL;
1496ea4e740SClément Léger 	}
1506ea4e740SClément Léger 
1516ea4e740SClément Léger 	return clk;
1526ea4e740SClément Léger }
1536ea4e740SClément Léger 
1546ea4e740SClément Léger struct clk *
at91_clk_register_master_pres(struct pmc_data * pmc,const char * name,int num_parents,struct clk ** parents,const struct clk_master_layout * layout,const struct clk_master_charac * charac,int chg_pid)1556ea4e740SClément Léger at91_clk_register_master_pres(struct pmc_data *pmc,
1566ea4e740SClément Léger 			      const char *name, int num_parents,
1576ea4e740SClément Léger 			      struct clk **parents,
1586ea4e740SClément Léger 			      const struct clk_master_layout *layout,
1596ea4e740SClément Léger 			      const struct clk_master_charac *charac,
1606ea4e740SClément Léger 			      int chg_pid)
1616ea4e740SClément Léger {
1626ea4e740SClément Léger 	return at91_clk_register_master_internal(pmc, name, num_parents,
1636ea4e740SClément Léger 						 parents, layout,
1646ea4e740SClément Léger 						 charac,
1656ea4e740SClément Léger 						 &master_pres_ops, chg_pid);
1666ea4e740SClément Léger }
1676ea4e740SClément Léger 
1686ea4e740SClément Léger struct clk *
at91_clk_register_master_div(struct pmc_data * pmc,const char * name,struct clk * parent,const struct clk_master_layout * layout,const struct clk_master_charac * charac)1696ea4e740SClément Léger at91_clk_register_master_div(struct pmc_data *pmc,
1706ea4e740SClément Léger 			     const char *name, struct clk *parent,
1716ea4e740SClément Léger 			     const struct clk_master_layout *layout,
1726ea4e740SClément Léger 			     const struct clk_master_charac *charac)
1736ea4e740SClément Léger {
1746ea4e740SClément Léger 	return at91_clk_register_master_internal(pmc, name, 1,
1756ea4e740SClément Léger 						 &parent, layout,
1766ea4e740SClément Léger 						 charac,
1776ea4e740SClément Léger 						 &master_div_ops, -1);
1786ea4e740SClément Léger }
1796ea4e740SClément Léger 
1806ea4e740SClément Léger const struct clk_master_layout at91sam9x5_master_layout = {
1816ea4e740SClément Léger 	.mask = 0x373,
1826ea4e740SClément Léger 	.pres_shift = 4,
1836ea4e740SClément Léger 	.offset = AT91_PMC_MCKR,
1846ea4e740SClément Léger };
185*5110b3e7STony Han 
clk_sama7g5_master_get_parent(struct clk * hw)186*5110b3e7STony Han static size_t clk_sama7g5_master_get_parent(struct clk *hw)
187*5110b3e7STony Han {
188*5110b3e7STony Han 	struct clk_master *master = hw->priv;
189*5110b3e7STony Han 	size_t i = 0;
190*5110b3e7STony Han 
191*5110b3e7STony Han 	for (i = 0; i < hw->num_parents; i++)
192*5110b3e7STony Han 		if (master->mux_table[i] == master->parent)
193*5110b3e7STony Han 			return i;
194*5110b3e7STony Han 
195*5110b3e7STony Han 	panic("Can't get correct parent of clock");
196*5110b3e7STony Han }
197*5110b3e7STony Han 
clk_sama7g5_master_set_parent(struct clk * hw,size_t index)198*5110b3e7STony Han static TEE_Result clk_sama7g5_master_set_parent(struct clk *hw, size_t index)
199*5110b3e7STony Han {
200*5110b3e7STony Han 	struct clk_master *master = hw->priv;
201*5110b3e7STony Han 
202*5110b3e7STony Han 	if (index >= hw->num_parents)
203*5110b3e7STony Han 		return TEE_ERROR_BAD_PARAMETERS;
204*5110b3e7STony Han 
205*5110b3e7STony Han 	master->parent = master->mux_table[index];
206*5110b3e7STony Han 
207*5110b3e7STony Han 	return TEE_SUCCESS;
208*5110b3e7STony Han }
209*5110b3e7STony Han 
clk_sama7g5_master_set_rate(struct clk * hw,unsigned long rate,unsigned long parent_rate)210*5110b3e7STony Han static TEE_Result clk_sama7g5_master_set_rate(struct clk *hw,
211*5110b3e7STony Han 					      unsigned long rate,
212*5110b3e7STony Han 					      unsigned long parent_rate)
213*5110b3e7STony Han {
214*5110b3e7STony Han 	struct clk_master *master = hw->priv;
215*5110b3e7STony Han 	unsigned long div = 0;
216*5110b3e7STony Han 
217*5110b3e7STony Han 	div = UDIV_ROUND_NEAREST(parent_rate, rate);
218*5110b3e7STony Han 	if (div > (1 << (MASTER_PRES_MAX - 1)) ||
219*5110b3e7STony Han 	    (!IS_POWER_OF_TWO(div) && div != 3))
220*5110b3e7STony Han 		return TEE_ERROR_BAD_PARAMETERS;
221*5110b3e7STony Han 
222*5110b3e7STony Han 	/*
223*5110b3e7STony Han 	 * Divisor Value: Select the division ratio to be applied to the
224*5110b3e7STony Han 	 * selected clock to generate the corresponding MCKx.
225*5110b3e7STony Han 	 *  Value  |    Description
226*5110b3e7STony Han 	 *    0    | Selected clock divided by 1
227*5110b3e7STony Han 	 *    1    | Selected clock divided by 2
228*5110b3e7STony Han 	 *    2    | Selected clock divided by 4
229*5110b3e7STony Han 	 *    3    | Selected clock divided by 8
230*5110b3e7STony Han 	 *    4    | Selected clock divided by 16
231*5110b3e7STony Han 	 *    5    | Selected clock divided by 32
232*5110b3e7STony Han 	 *    6    | Selected clock divided by 64
233*5110b3e7STony Han 	 *    7    | Selected clock divided by 3
234*5110b3e7STony Han 	 */
235*5110b3e7STony Han 	if (div == 3)
236*5110b3e7STony Han 		master->div = MASTER_PRES_MAX;
237*5110b3e7STony Han 	else
238*5110b3e7STony Han 		master->div = ffs(div) - 1;
239*5110b3e7STony Han 
240*5110b3e7STony Han 	return TEE_SUCCESS;
241*5110b3e7STony Han }
242*5110b3e7STony Han 
clk_sama7g5_master_get_rate(struct clk * hw,unsigned long parent_rate)243*5110b3e7STony Han static unsigned long clk_sama7g5_master_get_rate(struct clk *hw,
244*5110b3e7STony Han 						 unsigned long parent_rate)
245*5110b3e7STony Han {
246*5110b3e7STony Han 	struct clk_master *master = hw->priv;
247*5110b3e7STony Han 	unsigned long rate = parent_rate >> master->div;
248*5110b3e7STony Han 
249*5110b3e7STony Han 	if (master->div == 7)
250*5110b3e7STony Han 		rate = parent_rate / 3;
251*5110b3e7STony Han 
252*5110b3e7STony Han 	return rate;
253*5110b3e7STony Han }
254*5110b3e7STony Han 
255*5110b3e7STony Han static const struct clk_ops sama7g5_master_ops = {
256*5110b3e7STony Han 	.set_rate = clk_sama7g5_master_set_rate,
257*5110b3e7STony Han 	.get_rate = clk_sama7g5_master_get_rate,
258*5110b3e7STony Han 	.get_parent = clk_sama7g5_master_get_parent,
259*5110b3e7STony Han 	.set_parent = clk_sama7g5_master_set_parent,
260*5110b3e7STony Han };
261*5110b3e7STony Han 
at91_clk_sama7g5_register_master(struct pmc_data * pmc,const char * name,int num_parents,struct clk ** parent,uint32_t * mux_table,uint8_t id,int chg_pid)262*5110b3e7STony Han struct clk *at91_clk_sama7g5_register_master(struct pmc_data *pmc,
263*5110b3e7STony Han 					     const char *name,
264*5110b3e7STony Han 					     int num_parents,
265*5110b3e7STony Han 					     struct clk **parent,
266*5110b3e7STony Han 					     uint32_t *mux_table,
267*5110b3e7STony Han 					     uint8_t id,
268*5110b3e7STony Han 					     int chg_pid)
269*5110b3e7STony Han {
270*5110b3e7STony Han 	struct clk_master *master = NULL;
271*5110b3e7STony Han 	struct clk *hw = NULL;
272*5110b3e7STony Han 	unsigned int val = 0;
273*5110b3e7STony Han 
274*5110b3e7STony Han 	if (!name || !num_parents || !parent || !mux_table ||
275*5110b3e7STony Han 	    id > MASTER_MAX_ID)
276*5110b3e7STony Han 		return NULL;
277*5110b3e7STony Han 
278*5110b3e7STony Han 	master = calloc(1, sizeof(*master));
279*5110b3e7STony Han 	if (!master)
280*5110b3e7STony Han 		return NULL;
281*5110b3e7STony Han 
282*5110b3e7STony Han 	hw = clk_alloc(name, &sama7g5_master_ops, parent, num_parents);
283*5110b3e7STony Han 	if (!hw) {
284*5110b3e7STony Han 		free(master);
285*5110b3e7STony Han 		return NULL;
286*5110b3e7STony Han 	}
287*5110b3e7STony Han 
288*5110b3e7STony Han 	hw->priv = master;
289*5110b3e7STony Han 	master->base = pmc->base;
290*5110b3e7STony Han 	master->id = id;
291*5110b3e7STony Han 	master->chg_pid = chg_pid;
292*5110b3e7STony Han 	master->mux_table = mux_table;
293*5110b3e7STony Han 
294*5110b3e7STony Han 	io_write32(master->base + AT91_PMC_MCR_V2, master->id);
295*5110b3e7STony Han 	val = io_read32(master->base + AT91_PMC_MCR_V2);
296*5110b3e7STony Han 	master->parent = (val & AT91_PMC_MCR_V2_CSS_MASK) >>
297*5110b3e7STony Han 			 AT91_PMC_MCR_V2_CSS_SHIFT;
298*5110b3e7STony Han 	master->div = (val & AT91_PMC_MCR_V2_DIV_MASK) >> MASTER_DIV_SHIFT;
299*5110b3e7STony Han 
300*5110b3e7STony Han 	if (clk_register(hw)) {
301*5110b3e7STony Han 		clk_free(hw);
302*5110b3e7STony Han 		free(master);
303*5110b3e7STony Han 		return NULL;
304*5110b3e7STony Han 	}
305*5110b3e7STony Han 
306*5110b3e7STony Han 	return hw;
307*5110b3e7STony Han }
308