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