1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Master clock support for AT91 architectures.
4  *
5  * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
6  *
7  * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
8  *
9  * Based on drivers/clk/at91/clk-master.c from Linux.
10  */
11 
12 #include <asm/processor.h>
13 #include <clk-uclass.h>
14 #include <common.h>
15 #include <dm.h>
16 #include <linux/clk-provider.h>
17 #include <linux/clk/at91_pmc.h>
18 
19 #include "pmc.h"
20 
21 #define UBOOT_DM_CLK_AT91_MASTER		"at91-master-clk"
22 #define UBOOT_DM_CLK_AT91_SAMA7G5_MASTER	"at91-sama7g5-master-clk"
23 
24 #define MASTER_PRES_MASK	0x7
25 #define MASTER_PRES_MAX		MASTER_PRES_MASK
26 #define MASTER_DIV_SHIFT	8
27 #define MASTER_DIV_MASK		0x7
28 
29 #define PMC_MCR			0x30
30 #define PMC_MCR_ID_MSK		GENMASK(3, 0)
31 #define PMC_MCR_CMD		BIT(7)
32 #define PMC_MCR_DIV		GENMASK(10, 8)
33 #define PMC_MCR_CSS		GENMASK(20, 16)
34 #define PMC_MCR_CSS_SHIFT	(16)
35 #define PMC_MCR_EN		BIT(28)
36 
37 #define PMC_MCR_ID(x)		((x) & PMC_MCR_ID_MSK)
38 
39 #define MASTER_MAX_ID		4
40 
41 struct clk_master {
42 	void __iomem *base;
43 	const struct clk_master_layout *layout;
44 	const struct clk_master_characteristics *characteristics;
45 	const u32 *mux_table;
46 	const u32 *clk_mux_table;
47 	u32 num_parents;
48 	struct clk clk;
49 	u8 id;
50 };
51 
52 #define to_clk_master(_clk) container_of(_clk, struct clk_master, clk)
53 
clk_master_ready(struct clk_master * master)54 static inline bool clk_master_ready(struct clk_master *master)
55 {
56 	unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
57 	unsigned int status;
58 
59 	pmc_read(master->base, AT91_PMC_SR, &status);
60 
61 	return !!(status & bit);
62 }
63 
clk_master_enable(struct clk * clk)64 static int clk_master_enable(struct clk *clk)
65 {
66 	struct clk_master *master = to_clk_master(clk);
67 
68 	while (!clk_master_ready(master)) {
69 		debug("waiting for mck %d\n", master->id);
70 		cpu_relax();
71 	}
72 
73 	return 0;
74 }
75 
clk_master_get_rate(struct clk * clk)76 static ulong clk_master_get_rate(struct clk *clk)
77 {
78 	struct clk_master *master = to_clk_master(clk);
79 	const struct clk_master_layout *layout = master->layout;
80 	const struct clk_master_characteristics *characteristics =
81 						master->characteristics;
82 	ulong rate = clk_get_parent_rate(clk);
83 	unsigned int mckr;
84 	u8 pres, div;
85 
86 	if (!rate)
87 		return 0;
88 
89 	pmc_read(master->base, master->layout->offset, &mckr);
90 	mckr &= layout->mask;
91 
92 	pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
93 	div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
94 
95 	if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
96 		rate /= 3;
97 	else
98 		rate >>= pres;
99 
100 	rate /= characteristics->divisors[div];
101 
102 	if (rate < characteristics->output.min)
103 		pr_warn("master clk is underclocked");
104 	else if (rate > characteristics->output.max)
105 		pr_warn("master clk is overclocked");
106 
107 	return rate;
108 }
109 
110 static const struct clk_ops master_ops = {
111 	.enable = clk_master_enable,
112 	.get_rate = clk_master_get_rate,
113 };
114 
at91_clk_register_master(void __iomem * base,const char * name,const char * const * parent_names,int num_parents,const struct clk_master_layout * layout,const struct clk_master_characteristics * characteristics,const u32 * mux_table)115 struct clk *at91_clk_register_master(void __iomem *base,
116 		const char *name, const char * const *parent_names,
117 		int num_parents, const struct clk_master_layout *layout,
118 		const struct clk_master_characteristics *characteristics,
119 		const u32 *mux_table)
120 {
121 	struct clk_master *master;
122 	struct clk *clk;
123 	unsigned int val;
124 	int ret;
125 
126 	if (!base || !name || !num_parents || !parent_names ||
127 	    !layout || !characteristics || !mux_table)
128 		return ERR_PTR(-EINVAL);
129 
130 	master = kzalloc(sizeof(*master), GFP_KERNEL);
131 	if (!master)
132 		return ERR_PTR(-ENOMEM);
133 
134 	master->layout = layout;
135 	master->characteristics = characteristics;
136 	master->base = base;
137 	master->num_parents = num_parents;
138 	master->mux_table = mux_table;
139 
140 	pmc_read(master->base, master->layout->offset, &val);
141 	clk = &master->clk;
142 	clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL;
143 	ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER, name,
144 			   parent_names[val & AT91_PMC_CSS]);
145 	if (ret) {
146 		kfree(master);
147 		clk = ERR_PTR(ret);
148 	}
149 
150 	return clk;
151 }
152 
153 U_BOOT_DRIVER(at91_master_clk) = {
154 	.name = UBOOT_DM_CLK_AT91_MASTER,
155 	.id = UCLASS_CLK,
156 	.ops = &master_ops,
157 	.flags = DM_FLAG_PRE_RELOC,
158 };
159 
clk_sama7g5_master_set_parent(struct clk * clk,struct clk * parent)160 static int clk_sama7g5_master_set_parent(struct clk *clk, struct clk *parent)
161 {
162 	struct clk_master *master = to_clk_master(clk);
163 	int index;
164 
165 	index = at91_clk_mux_val_to_index(master->clk_mux_table,
166 					  master->num_parents, parent->id);
167 	if (index < 0)
168 		return index;
169 
170 	index = at91_clk_mux_index_to_val(master->mux_table,
171 					  master->num_parents, index);
172 	if (index < 0)
173 		return index;
174 
175 	pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
176 	pmc_update_bits(master->base, PMC_MCR,
177 			PMC_MCR_CSS | PMC_MCR_CMD | PMC_MCR_ID_MSK,
178 			(index << PMC_MCR_CSS_SHIFT) | PMC_MCR_CMD |
179 			PMC_MCR_ID(master->id));
180 	return 0;
181 }
182 
clk_sama7g5_master_enable(struct clk * clk)183 static int clk_sama7g5_master_enable(struct clk *clk)
184 {
185 	struct clk_master *master = to_clk_master(clk);
186 
187 	pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
188 	pmc_update_bits(master->base, PMC_MCR,
189 			PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
190 			PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID(master->id));
191 
192 	return 0;
193 }
194 
clk_sama7g5_master_disable(struct clk * clk)195 static int clk_sama7g5_master_disable(struct clk *clk)
196 {
197 	struct clk_master *master = to_clk_master(clk);
198 
199 	pmc_write(master->base, PMC_MCR, master->id);
200 	pmc_update_bits(master->base, PMC_MCR,
201 			PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
202 			PMC_MCR_CMD | PMC_MCR_ID(master->id));
203 
204 	return 0;
205 }
206 
clk_sama7g5_master_set_rate(struct clk * clk,ulong rate)207 static ulong clk_sama7g5_master_set_rate(struct clk *clk, ulong rate)
208 {
209 	struct clk_master *master = to_clk_master(clk);
210 	ulong parent_rate = clk_get_parent_rate(clk);
211 	ulong div, rrate;
212 
213 	if (!parent_rate)
214 		return 0;
215 
216 	div = DIV_ROUND_CLOSEST(parent_rate, rate);
217 	if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1))) {
218 		return 0;
219 	} else if (div == 3) {
220 		rrate = DIV_ROUND_CLOSEST(parent_rate, MASTER_PRES_MAX);
221 		div = MASTER_PRES_MAX;
222 	} else {
223 		rrate = DIV_ROUND_CLOSEST(parent_rate, div);
224 		div = ffs(div) - 1;
225 	}
226 
227 	pmc_write(master->base, PMC_MCR, master->id);
228 	pmc_update_bits(master->base, PMC_MCR,
229 			PMC_MCR_DIV | PMC_MCR_CMD | PMC_MCR_ID_MSK,
230 			(div << MASTER_DIV_SHIFT) | PMC_MCR_CMD |
231 			PMC_MCR_ID(master->id));
232 
233 	return rrate;
234 }
235 
clk_sama7g5_master_get_rate(struct clk * clk)236 static ulong clk_sama7g5_master_get_rate(struct clk *clk)
237 {
238 	struct clk_master *master = to_clk_master(clk);
239 	ulong parent_rate = clk_get_parent_rate(clk);
240 	unsigned int val;
241 	ulong div;
242 
243 	if (!parent_rate)
244 		return 0;
245 
246 	pmc_write(master->base, PMC_MCR, master->id);
247 	pmc_read(master->base, PMC_MCR, &val);
248 
249 	div = (val >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
250 
251 	if (div == MASTER_PRES_MAX)
252 		div = 3;
253 	else
254 		div = 1 << div;
255 
256 	return DIV_ROUND_CLOSEST(parent_rate, div);
257 }
258 
259 static const struct clk_ops sama7g5_master_ops = {
260 	.enable = clk_sama7g5_master_enable,
261 	.disable = clk_sama7g5_master_disable,
262 	.set_rate = clk_sama7g5_master_set_rate,
263 	.get_rate = clk_sama7g5_master_get_rate,
264 	.set_parent = clk_sama7g5_master_set_parent,
265 };
266 
at91_clk_sama7g5_register_master(void __iomem * base,const char * name,const char * const * parent_names,int num_parents,const u32 * mux_table,const u32 * clk_mux_table,bool critical,u8 id)267 struct clk *at91_clk_sama7g5_register_master(void __iomem *base,
268 		const char *name, const char * const *parent_names,
269 		int num_parents, const u32 *mux_table, const u32 *clk_mux_table,
270 		bool critical, u8 id)
271 {
272 	struct clk_master *master;
273 	struct clk *clk;
274 	u32 val, index;
275 	int ret;
276 
277 	if (!base || !name || !num_parents || !parent_names ||
278 	    !mux_table || !clk_mux_table || id > MASTER_MAX_ID)
279 		return ERR_PTR(-EINVAL);
280 
281 	master = kzalloc(sizeof(*master), GFP_KERNEL);
282 	if (!master)
283 		return ERR_PTR(-ENOMEM);
284 
285 	master->base = base;
286 	master->id = id;
287 	master->mux_table = mux_table;
288 	master->clk_mux_table = clk_mux_table;
289 	master->num_parents = num_parents;
290 
291 	pmc_write(master->base, PMC_MCR, master->id);
292 	pmc_read(master->base, PMC_MCR, &val);
293 
294 	index = at91_clk_mux_val_to_index(master->mux_table,
295 				master->num_parents,
296 				(val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT);
297 	if (index < 0) {
298 		kfree(master);
299 		return ERR_PTR(index);
300 	}
301 
302 	clk = &master->clk;
303 	clk->flags = CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0);
304 
305 	ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_MASTER, name,
306 			   parent_names[index]);
307 	if (ret) {
308 		kfree(master);
309 		clk = ERR_PTR(ret);
310 	}
311 
312 	return clk;
313 }
314 
315 U_BOOT_DRIVER(at91_sama7g5_master_clk) = {
316 	.name = UBOOT_DM_CLK_AT91_SAMA7G5_MASTER,
317 	.id = UCLASS_CLK,
318 	.ops = &sama7g5_master_ops,
319 	.flags = DM_FLAG_PRE_RELOC,
320 };
321 
322 const struct clk_master_layout at91rm9200_master_layout = {
323 	.mask = 0x31F,
324 	.pres_shift = 2,
325 	.offset = AT91_PMC_MCKR,
326 };
327 
328 const struct clk_master_layout at91sam9x5_master_layout = {
329 	.mask = 0x373,
330 	.pres_shift = 4,
331 	.offset = AT91_PMC_MCKR,
332 };
333