xref: /optee_os/core/drivers/clk/sam/at91_programmable.c (revision 5e6f824b0640ede149e5a4353b0ab47a7297fd18)
1162917ffSClément Léger // SPDX-License-Identifier: GPL-2.0+ or BSD-3-Clause
2162917ffSClément Léger /*
3162917ffSClément Léger  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
4162917ffSClément Léger  *  Copyright (C) 2021 Microchip
5162917ffSClément Léger  */
6162917ffSClément Léger 
7162917ffSClément Léger #include <io.h>
8162917ffSClément Léger #include <kernel/delay.h>
9162917ffSClément Léger #include <kernel/panic.h>
10162917ffSClément Léger #include <mm/core_memprot.h>
11162917ffSClément Léger #include <types_ext.h>
12162917ffSClément Léger 
13162917ffSClément Léger #include "at91_clk.h"
14162917ffSClément Léger 
15162917ffSClément Léger #define PROG_ID_MAX		7
16162917ffSClément Léger 
17162917ffSClément Léger #define PROG_STATUS_MASK(id)	(1 << ((id) + 8))
18162917ffSClément Léger #define PROG_PRES(layout, pckr)	\
19162917ffSClément Léger 	({ \
20162917ffSClément Léger 		typeof(layout) __layout = layout; \
21162917ffSClément Léger 		\
22162917ffSClément Léger 		(((pckr) >> (__layout)->pres_shift) & (__layout)->pres_mask); \
23162917ffSClément Léger 	})
24162917ffSClément Léger #define PROG_MAX_RM9200_CSS	3
25162917ffSClément Léger 
26162917ffSClément Léger struct clk_programmable {
27162917ffSClément Léger 	vaddr_t base;
28162917ffSClément Léger 	uint8_t id;
29162917ffSClément Léger 	const struct clk_programmable_layout *layout;
30162917ffSClément Léger };
31162917ffSClément Léger 
clk_programmable_get_rate(struct clk * clk,unsigned long parent_rate)32162917ffSClément Léger static unsigned long clk_programmable_get_rate(struct clk *clk,
33162917ffSClément Léger 					       unsigned long parent_rate)
34162917ffSClément Léger {
35162917ffSClément Léger 	struct clk_programmable *prog = clk->priv;
36162917ffSClément Léger 	const struct clk_programmable_layout *layout = prog->layout;
37162917ffSClément Léger 	unsigned int pckr = io_read32(prog->base + AT91_PMC_PCKR(prog->id));
38162917ffSClément Léger 	unsigned long rate = 0;
39162917ffSClément Léger 
40162917ffSClément Léger 	if (layout->is_pres_direct)
41162917ffSClément Léger 		rate = parent_rate / (PROG_PRES(layout, pckr) + 1);
42162917ffSClément Léger 	else
43162917ffSClément Léger 		rate = parent_rate >> PROG_PRES(layout, pckr);
44162917ffSClément Léger 
45162917ffSClément Léger 	return rate;
46162917ffSClément Léger }
47162917ffSClément Léger 
clk_programmable_set_parent(struct clk * clk,size_t index)48162917ffSClément Léger static TEE_Result clk_programmable_set_parent(struct clk *clk, size_t index)
49162917ffSClément Léger {
50162917ffSClément Léger 	struct clk_programmable *prog = clk->priv;
51162917ffSClément Léger 	const struct clk_programmable_layout *layout = prog->layout;
52162917ffSClément Léger 	unsigned int mask = layout->css_mask;
53162917ffSClément Léger 	unsigned int pckr = index;
54162917ffSClément Léger 
55162917ffSClément Léger 	if (layout->have_slck_mck)
56162917ffSClément Léger 		mask |= AT91_PMC_CSSMCK_MCK;
57162917ffSClément Léger 
58162917ffSClément Léger 	if (index > layout->css_mask) {
59162917ffSClément Léger 		if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck)
60162917ffSClément Léger 			return TEE_ERROR_BAD_PARAMETERS;
61162917ffSClément Léger 
62162917ffSClément Léger 		pckr |= AT91_PMC_CSSMCK_MCK;
63162917ffSClément Léger 	}
64162917ffSClément Léger 
65162917ffSClément Léger 	io_clrsetbits32(prog->base + AT91_PMC_PCKR(prog->id), mask, pckr);
66162917ffSClément Léger 
67162917ffSClément Léger 	return TEE_SUCCESS;
68162917ffSClément Léger }
69162917ffSClément Léger 
clk_programmable_get_parent(struct clk * clk)70162917ffSClément Léger static size_t clk_programmable_get_parent(struct clk *clk)
71162917ffSClément Léger {
72162917ffSClément Léger 	struct clk_programmable *prog = clk->priv;
73162917ffSClément Léger 	const struct clk_programmable_layout *layout = prog->layout;
74162917ffSClément Léger 	unsigned int pckr = io_read32(prog->base + AT91_PMC_PCKR(prog->id));
75162917ffSClément Léger 	size_t ret = 0;
76162917ffSClément Léger 
77162917ffSClément Léger 	ret = pckr & layout->css_mask;
78162917ffSClément Léger 
79162917ffSClément Léger 	if (layout->have_slck_mck && (pckr & AT91_PMC_CSSMCK_MCK) && !ret)
80162917ffSClément Léger 		ret = PROG_MAX_RM9200_CSS + 1;
81162917ffSClément Léger 
82162917ffSClément Léger 	return ret;
83162917ffSClément Léger }
84162917ffSClément Léger 
flsi(unsigned int val)85162917ffSClément Léger static unsigned int flsi(unsigned int val)
86162917ffSClément Léger {
87162917ffSClément Léger 	if (val == 0)
88162917ffSClément Léger 		return 0;
89162917ffSClément Léger 
90162917ffSClément Léger 	return sizeof(unsigned int) * 8 - __builtin_clz(val);
91162917ffSClément Léger }
92162917ffSClément Léger 
clk_programmable_set_rate(struct clk * clk,unsigned long rate,unsigned long parent_rate)93162917ffSClément Léger static TEE_Result clk_programmable_set_rate(struct clk *clk, unsigned long rate,
94162917ffSClément Léger 					    unsigned long parent_rate)
95162917ffSClément Léger {
96162917ffSClément Léger 	struct clk_programmable *prog = clk->priv;
97162917ffSClément Léger 	const struct clk_programmable_layout *layout = prog->layout;
98162917ffSClément Léger 	unsigned long div = parent_rate / rate;
99162917ffSClément Léger 	int shift = 0;
100162917ffSClément Léger 
101162917ffSClément Léger 	if (!div)
102162917ffSClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
103162917ffSClément Léger 
104162917ffSClément Léger 	if (layout->is_pres_direct) {
105162917ffSClément Léger 		shift = div - 1;
106162917ffSClément Léger 
107162917ffSClément Léger 		if (shift > layout->pres_mask)
108162917ffSClément Léger 			return TEE_ERROR_BAD_PARAMETERS;
109162917ffSClément Léger 	} else {
110162917ffSClément Léger 		shift = flsi(div) - 1;
111162917ffSClément Léger 
112162917ffSClément Léger 		if (div != (1ULL << shift))
113162917ffSClément Léger 			return TEE_ERROR_BAD_PARAMETERS;
114162917ffSClément Léger 
115162917ffSClément Léger 		if (shift >= layout->pres_mask)
116162917ffSClément Léger 			return TEE_ERROR_BAD_PARAMETERS;
117162917ffSClément Léger 	}
118162917ffSClément Léger 
119162917ffSClément Léger 	io_clrsetbits32(prog->base + AT91_PMC_PCKR(prog->id),
120162917ffSClément Léger 			layout->pres_mask << layout->pres_shift,
121162917ffSClément Léger 			shift << layout->pres_shift);
122162917ffSClément Léger 
123162917ffSClément Léger 	return TEE_SUCCESS;
124162917ffSClément Léger }
125162917ffSClément Léger 
126162917ffSClément Léger static const struct clk_ops programmable_ops = {
127162917ffSClément Léger 	.get_rate = clk_programmable_get_rate,
128162917ffSClément Léger 	.get_parent = clk_programmable_get_parent,
129162917ffSClément Léger 	.set_parent = clk_programmable_set_parent,
130162917ffSClément Léger 	.set_rate = clk_programmable_set_rate,
131162917ffSClément Léger };
132162917ffSClément Léger 
133162917ffSClément Léger struct clk *
at91_clk_register_programmable(struct pmc_data * pmc,const char * name,struct clk ** parents,uint8_t num_parents,uint8_t id,const struct clk_programmable_layout * layout)134162917ffSClément Léger at91_clk_register_programmable(struct pmc_data *pmc,
135162917ffSClément Léger 			       const char *name, struct clk **parents,
136162917ffSClément Léger 			       uint8_t num_parents, uint8_t id,
137162917ffSClément Léger 			       const struct clk_programmable_layout *layout)
138162917ffSClément Léger {
139162917ffSClément Léger 	struct clk_programmable *prog = NULL;
140162917ffSClément Léger 	struct clk *clk = NULL;
141162917ffSClément Léger 
142162917ffSClément Léger 	assert(id <= PROG_ID_MAX);
143162917ffSClément Léger 
144162917ffSClément Léger 	clk = clk_alloc(name, &programmable_ops, parents, num_parents);
145162917ffSClément Léger 	prog = calloc(1, sizeof(*prog));
146162917ffSClément Léger 	if (!prog || !clk)
147162917ffSClément Léger 		return NULL;
148162917ffSClément Léger 
149162917ffSClément Léger 	prog->id = id;
150162917ffSClément Léger 	prog->layout = layout;
151162917ffSClément Léger 	prog->base = pmc->base;
152162917ffSClément Léger 
153162917ffSClément Léger 	clk->priv = prog;
154162917ffSClément Léger 	clk->flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
155162917ffSClément Léger 
156162917ffSClément Léger 	if (clk_register(clk)) {
157162917ffSClément Léger 		clk_free(clk);
158162917ffSClément Léger 		free(prog);
159162917ffSClément Léger 		return NULL;
160162917ffSClément Léger 	}
161162917ffSClément Léger 
162*5e6f824bSClément Léger 	pmc_register_pck(id);
163*5e6f824bSClément Léger 
164162917ffSClément Léger 	return clk;
165162917ffSClément Léger }
166