11eb03c37SClément Léger // SPDX-License-Identifier: GPL-2.0+ or BSD-3-Clause
21eb03c37SClément Léger /*
31eb03c37SClément Léger * Copyright (C) 2015 - 2021 Atmel Corporation,
41eb03c37SClément Léger * Nicolas Ferre <nicolas.ferre@atmel.com>
51eb03c37SClément Léger *
61eb03c37SClément Léger * Based on clk-programmable & clk-peripheral drivers by Boris BREZILLON.
71eb03c37SClément Léger */
81eb03c37SClément Léger
91eb03c37SClément Léger #include <io.h>
101eb03c37SClément Léger #include <kernel/delay.h>
111eb03c37SClément Léger #include <kernel/panic.h>
121eb03c37SClément Léger #include <mm/core_memprot.h>
131eb03c37SClément Léger #include <string.h>
141eb03c37SClément Léger #include <types_ext.h>
151eb03c37SClément Léger
161eb03c37SClément Léger #include "at91_clk.h"
171eb03c37SClément Léger
181eb03c37SClément Léger #define GENERATED_MAX_DIV 255
191eb03c37SClément Léger
201eb03c37SClément Léger struct clk_generated {
211eb03c37SClément Léger vaddr_t base;
221eb03c37SClément Léger struct clk_range range;
231eb03c37SClément Léger uint32_t *mux_table;
241eb03c37SClément Léger uint32_t id;
251eb03c37SClément Léger uint32_t gckdiv;
261eb03c37SClément Léger const struct clk_pcr_layout *layout;
271eb03c37SClément Léger uint8_t parent_id;
281eb03c37SClément Léger int chg_pid;
291eb03c37SClément Léger };
301eb03c37SClément Léger
clk_generated_enable(struct clk * clk)311eb03c37SClément Léger static TEE_Result clk_generated_enable(struct clk *clk)
321eb03c37SClément Léger {
331eb03c37SClément Léger struct clk_generated *gck = clk->priv;
341eb03c37SClément Léger
351eb03c37SClément Léger io_write32(gck->base + gck->layout->offset,
361eb03c37SClément Léger (gck->id & gck->layout->pid_mask));
371eb03c37SClément Léger io_clrsetbits32(gck->base + gck->layout->offset,
381eb03c37SClément Léger AT91_PMC_PCR_GCKDIV_MASK | gck->layout->gckcss_mask |
391eb03c37SClément Léger gck->layout->cmd | AT91_PMC_PCR_GCKEN,
401eb03c37SClément Léger field_prep(gck->layout->gckcss_mask, gck->parent_id) |
411eb03c37SClément Léger gck->layout->cmd |
421eb03c37SClément Léger ((gck->gckdiv << AT91_PMC_PCR_GCKDIV_SHIFT) &
431eb03c37SClément Léger AT91_PMC_PCR_GCKDIV_MASK) |
441eb03c37SClément Léger AT91_PMC_PCR_GCKEN);
451eb03c37SClément Léger
461eb03c37SClément Léger return TEE_SUCCESS;
471eb03c37SClément Léger }
481eb03c37SClément Léger
clk_generated_disable(struct clk * clk)491eb03c37SClément Léger static void clk_generated_disable(struct clk *clk)
501eb03c37SClément Léger {
511eb03c37SClément Léger struct clk_generated *gck = clk->priv;
521eb03c37SClément Léger
531eb03c37SClément Léger io_write32(gck->base + gck->layout->offset,
541eb03c37SClément Léger gck->id & gck->layout->pid_mask);
551eb03c37SClément Léger io_clrsetbits32(gck->base + gck->layout->offset, AT91_PMC_PCR_GCKEN,
561eb03c37SClément Léger gck->layout->cmd);
571eb03c37SClément Léger }
581eb03c37SClément Léger
591eb03c37SClément Léger static unsigned long
clk_generated_get_rate(struct clk * clk,unsigned long parent_rate)601eb03c37SClément Léger clk_generated_get_rate(struct clk *clk, unsigned long parent_rate)
611eb03c37SClément Léger {
621eb03c37SClément Léger struct clk_generated *gck = clk->priv;
631eb03c37SClément Léger
641eb03c37SClément Léger return UDIV_ROUND_NEAREST(parent_rate, gck->gckdiv + 1);
651eb03c37SClément Léger }
661eb03c37SClément Léger
671eb03c37SClément Léger /* No modification of hardware as we have the flag CLK_SET_PARENT_GATE set */
clk_generated_set_parent(struct clk * clk,size_t index)681eb03c37SClément Léger static TEE_Result clk_generated_set_parent(struct clk *clk, size_t index)
691eb03c37SClément Léger {
701eb03c37SClément Léger struct clk_generated *gck = clk->priv;
711eb03c37SClément Léger
721eb03c37SClément Léger if (index >= clk_get_num_parents(clk))
731eb03c37SClément Léger return TEE_ERROR_BAD_PARAMETERS;
741eb03c37SClément Léger
75*9aab6fb2STony Han if (gck->mux_table)
76*9aab6fb2STony Han gck->parent_id = gck->mux_table[index];
77*9aab6fb2STony Han else
781eb03c37SClément Léger gck->parent_id = index;
791eb03c37SClément Léger
801eb03c37SClément Léger return TEE_SUCCESS;
811eb03c37SClément Léger }
821eb03c37SClément Léger
clk_generated_get_parent(struct clk * clk)831eb03c37SClément Léger static size_t clk_generated_get_parent(struct clk *clk)
841eb03c37SClément Léger {
851eb03c37SClément Léger struct clk_generated *gck = clk->priv;
86*9aab6fb2STony Han unsigned int i = 0;
871eb03c37SClément Léger
88*9aab6fb2STony Han if (gck->mux_table) {
89*9aab6fb2STony Han for (i = 0; i < clk_get_num_parents(clk); i++)
90*9aab6fb2STony Han if (gck->mux_table[i] == gck->parent_id)
91*9aab6fb2STony Han return i;
92*9aab6fb2STony Han panic("Can't get correct parent of clock");
93*9aab6fb2STony Han } else {
941eb03c37SClément Léger return gck->parent_id;
951eb03c37SClément Léger }
96*9aab6fb2STony Han }
971eb03c37SClément Léger
981eb03c37SClément Léger /* No modification of hardware as we have the flag CLK_SET_RATE_GATE set */
clk_generated_set_rate(struct clk * clk,unsigned long rate,unsigned long parent_rate)991eb03c37SClément Léger static TEE_Result clk_generated_set_rate(struct clk *clk, unsigned long rate,
1001eb03c37SClément Léger unsigned long parent_rate)
1011eb03c37SClément Léger {
1021eb03c37SClément Léger struct clk_generated *gck = clk->priv;
1031eb03c37SClément Léger uint32_t div = 1;
1041eb03c37SClément Léger
1051eb03c37SClément Léger if (!rate)
1061eb03c37SClément Léger return TEE_ERROR_BAD_PARAMETERS;
1071eb03c37SClément Léger
1081eb03c37SClément Léger if (gck->range.max && rate > gck->range.max)
1091eb03c37SClément Léger return TEE_ERROR_BAD_PARAMETERS;
1101eb03c37SClément Léger
1111eb03c37SClément Léger div = UDIV_ROUND_NEAREST(parent_rate, rate);
1121eb03c37SClément Léger if (div > GENERATED_MAX_DIV + 1 || !div)
1131eb03c37SClément Léger return TEE_ERROR_GENERIC;
1141eb03c37SClément Léger
1151eb03c37SClément Léger gck->gckdiv = div - 1;
116*9aab6fb2STony Han
1171eb03c37SClément Léger return TEE_SUCCESS;
1181eb03c37SClément Léger }
1191eb03c37SClément Léger
1201eb03c37SClément Léger static const struct clk_ops generated_ops = {
1211eb03c37SClément Léger .enable = clk_generated_enable,
1221eb03c37SClément Léger .disable = clk_generated_disable,
1231eb03c37SClément Léger .get_rate = clk_generated_get_rate,
1241eb03c37SClément Léger .get_parent = clk_generated_get_parent,
1251eb03c37SClément Léger .set_parent = clk_generated_set_parent,
1261eb03c37SClément Léger .set_rate = clk_generated_set_rate,
1271eb03c37SClément Léger };
1281eb03c37SClément Léger
1291eb03c37SClément Léger /**
1301eb03c37SClément Léger * clk_generated_startup - Initialize a given clock to its default parent and
1311eb03c37SClément Léger * divisor parameter.
1321eb03c37SClément Léger *
1331eb03c37SClément Léger * @gck: Generated clock to set the startup parameters for.
1341eb03c37SClément Léger *
1351eb03c37SClément Léger * Take parameters from the hardware and update local clock configuration
1361eb03c37SClément Léger * accordingly.
1371eb03c37SClément Léger */
clk_generated_startup(struct clk_generated * gck)1381eb03c37SClément Léger static void clk_generated_startup(struct clk_generated *gck)
1391eb03c37SClément Léger {
1401eb03c37SClément Léger uint32_t tmp = 0;
1411eb03c37SClément Léger
1421eb03c37SClément Léger io_write32(gck->base + gck->layout->offset,
1431eb03c37SClément Léger (gck->id & gck->layout->pid_mask));
1441eb03c37SClément Léger tmp = io_read32(gck->base + gck->layout->offset);
1451eb03c37SClément Léger
1461eb03c37SClément Léger gck->parent_id = field_get(gck->layout->gckcss_mask, tmp);
1471eb03c37SClément Léger gck->gckdiv = (tmp & AT91_PMC_PCR_GCKDIV_MASK) >>
1481eb03c37SClément Léger AT91_PMC_PCR_GCKDIV_SHIFT;
1491eb03c37SClément Léger }
1501eb03c37SClément Léger
1511eb03c37SClément Léger struct clk *
at91_clk_register_generated(struct pmc_data * pmc,const struct clk_pcr_layout * layout,const char * name,struct clk ** parents,uint32_t * mux_table,uint8_t num_parents,uint8_t id,const struct clk_range * range,int chg_pid)1521eb03c37SClément Léger at91_clk_register_generated(struct pmc_data *pmc,
1531eb03c37SClément Léger const struct clk_pcr_layout *layout,
1541eb03c37SClément Léger const char *name, struct clk **parents,
155*9aab6fb2STony Han uint32_t *mux_table,
1561eb03c37SClément Léger uint8_t num_parents, uint8_t id,
1571eb03c37SClément Léger const struct clk_range *range,
1581eb03c37SClément Léger int chg_pid)
1591eb03c37SClément Léger {
1601eb03c37SClément Léger struct clk_generated *gck = NULL;
1611eb03c37SClément Léger struct clk *clk = NULL;
1621eb03c37SClément Léger
1631eb03c37SClément Léger clk = clk_alloc(name, &generated_ops, parents, num_parents);
1641eb03c37SClément Léger if (!clk)
1651eb03c37SClément Léger return NULL;
1661eb03c37SClément Léger
1671eb03c37SClément Léger gck = calloc(1, sizeof(*gck));
1681eb03c37SClément Léger if (!gck) {
1691eb03c37SClément Léger clk_free(clk);
1701eb03c37SClément Léger return NULL;
1711eb03c37SClément Léger }
1721eb03c37SClément Léger
1731eb03c37SClément Léger clk->flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
1741eb03c37SClément Léger
1751eb03c37SClément Léger gck->id = id;
1761eb03c37SClément Léger gck->base = pmc->base;
1771eb03c37SClément Léger memcpy(&gck->range, range, sizeof(gck->range));
1781eb03c37SClément Léger gck->chg_pid = chg_pid;
1791eb03c37SClément Léger gck->layout = layout;
180*9aab6fb2STony Han gck->mux_table = mux_table;
1811eb03c37SClément Léger
1821eb03c37SClément Léger clk->priv = gck;
1831eb03c37SClément Léger
1841eb03c37SClément Léger clk_generated_startup(gck);
1851eb03c37SClément Léger
1861eb03c37SClément Léger if (clk_register(clk)) {
1871eb03c37SClément Léger clk_free(clk);
1881eb03c37SClément Léger free(gck);
1891eb03c37SClément Léger return NULL;
1901eb03c37SClément Léger }
1915e6f824bSClément Léger pmc_register_id(id);
1921eb03c37SClément Léger
1931eb03c37SClément Léger return clk;
1941eb03c37SClément Léger }
195