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 <div64.h>
16 #include <dm.h>
17 #include <linux/clk-provider.h>
18 #include <linux/clk/at91_pmc.h>
19 
20 #include "pmc.h"
21 
22 #define UBOOT_DM_CLK_AT91_MASTER_PRES		"at91-master-clk-pres"
23 #define UBOOT_DM_CLK_AT91_MASTER_DIV		"at91-master-clk-div"
24 #define UBOOT_DM_CLK_AT91_SAMA7G5_MASTER	"at91-sama7g5-master-clk"
25 
26 #define MASTER_PRES_MASK	0x7
27 #define MASTER_PRES_MAX		MASTER_PRES_MASK
28 #define MASTER_DIV_SHIFT	8
29 #define MASTER_DIV_MASK		0x7
30 
31 #define PMC_MCR			0x30
32 #define PMC_MCR_ID_MSK		GENMASK(3, 0)
33 #define PMC_MCR_CMD		BIT(7)
34 #define PMC_MCR_DIV		GENMASK(10, 8)
35 #define PMC_MCR_CSS		GENMASK(20, 16)
36 #define PMC_MCR_CSS_SHIFT	(16)
37 #define PMC_MCR_EN		BIT(28)
38 
39 #define PMC_MCR_ID(x)		((x) & PMC_MCR_ID_MSK)
40 
41 #define MASTER_MAX_ID		4
42 
43 struct clk_master {
44 	void __iomem *base;
45 	const struct clk_master_layout *layout;
46 	const struct clk_master_characteristics *characteristics;
47 	const u32 *mux_table;
48 	const u32 *clk_mux_table;
49 	u32 num_parents;
50 	struct clk clk;
51 	u8 id;
52 };
53 
54 #define to_clk_master(_clk) container_of(_clk, struct clk_master, clk)
55 
clk_master_ready(struct clk_master * master)56 static inline bool clk_master_ready(struct clk_master *master)
57 {
58 	unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
59 	unsigned int status;
60 
61 	pmc_read(master->base, AT91_PMC_SR, &status);
62 
63 	return !!(status & bit);
64 }
65 
clk_master_enable(struct clk * clk)66 static int clk_master_enable(struct clk *clk)
67 {
68 	struct clk_master *master = to_clk_master(clk);
69 
70 	while (!clk_master_ready(master)) {
71 		debug("waiting for mck %d\n", master->id);
72 		cpu_relax();
73 	}
74 
75 	return 0;
76 }
77 
clk_master_pres_get_rate(struct clk * clk)78 static ulong clk_master_pres_get_rate(struct clk *clk)
79 {
80 	struct clk_master *master = to_clk_master(clk);
81 	const struct clk_master_layout *layout = master->layout;
82 	const struct clk_master_characteristics *characteristics =
83 						master->characteristics;
84 	ulong rate = clk_get_parent_rate(clk);
85 	unsigned int mckr;
86 	u8 pres;
87 
88 	if (!rate)
89 		return 0;
90 
91 	pmc_read(master->base, master->layout->offset, &mckr);
92 	mckr &= layout->mask;
93 
94 	pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
95 
96 	if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
97 		pres = 3;
98 	else
99 		pres = (1 << pres);
100 
101 	return DIV_ROUND_CLOSEST_ULL(rate, pres);
102 }
103 
104 static const struct clk_ops master_pres_ops = {
105 	.enable = clk_master_enable,
106 	.get_rate = clk_master_pres_get_rate,
107 };
108 
at91_clk_register_master_pres(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)109 struct clk *at91_clk_register_master_pres(void __iomem *base,
110 		const char *name, const char * const *parent_names,
111 		int num_parents, const struct clk_master_layout *layout,
112 		const struct clk_master_characteristics *characteristics,
113 		const u32 *mux_table)
114 {
115 	struct clk_master *master;
116 	struct clk *clk;
117 	unsigned int val;
118 	int ret;
119 
120 	if (!base || !name || !num_parents || !parent_names ||
121 	    !layout || !characteristics || !mux_table)
122 		return ERR_PTR(-EINVAL);
123 
124 	master = kzalloc(sizeof(*master), GFP_KERNEL);
125 	if (!master)
126 		return ERR_PTR(-ENOMEM);
127 
128 	master->layout = layout;
129 	master->characteristics = characteristics;
130 	master->base = base;
131 	master->num_parents = num_parents;
132 	master->mux_table = mux_table;
133 
134 	pmc_read(master->base, master->layout->offset, &val);
135 	clk = &master->clk;
136 	clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL;
137 	ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER_PRES, name,
138 			   parent_names[val & AT91_PMC_CSS]);
139 	if (ret) {
140 		kfree(master);
141 		clk = ERR_PTR(ret);
142 	}
143 
144 	return clk;
145 }
146 
147 U_BOOT_DRIVER(at91_master_pres_clk) = {
148 	.name = UBOOT_DM_CLK_AT91_MASTER_PRES,
149 	.id = UCLASS_CLK,
150 	.ops = &master_pres_ops,
151 	.flags = DM_FLAG_PRE_RELOC,
152 };
153 
clk_master_div_get_rate(struct clk * clk)154 static ulong clk_master_div_get_rate(struct clk *clk)
155 {
156 	struct clk_master *master = to_clk_master(clk);
157 	const struct clk_master_layout *layout = master->layout;
158 	const struct clk_master_characteristics *characteristics =
159 						master->characteristics;
160 	ulong rate = clk_get_parent_rate(clk);
161 	unsigned int mckr;
162 	u8 div;
163 
164 	if (!rate)
165 		return 0;
166 
167 	pmc_read(master->base, master->layout->offset, &mckr);
168 	mckr &= layout->mask;
169 	div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
170 
171 	rate = DIV_ROUND_CLOSEST_ULL(rate, characteristics->divisors[div]);
172 	if (rate < characteristics->output.min)
173 		pr_warn("master clk is underclocked");
174 	else if (rate > characteristics->output.max)
175 		pr_warn("master clk is overclocked");
176 
177 	return rate;
178 }
179 
180 static const struct clk_ops master_div_ops = {
181 	.enable = clk_master_enable,
182 	.get_rate = clk_master_div_get_rate,
183 };
184 
at91_clk_register_master_div(void __iomem * base,const char * name,const char * parent_name,const struct clk_master_layout * layout,const struct clk_master_characteristics * characteristics)185 struct clk *at91_clk_register_master_div(void __iomem *base,
186 		const char *name, const char *parent_name,
187 		const struct clk_master_layout *layout,
188 		const struct clk_master_characteristics *characteristics)
189 {
190 	struct clk_master *master;
191 	struct clk *clk;
192 	int ret;
193 
194 	if (!base || !name || !parent_name || !layout || !characteristics)
195 		return ERR_PTR(-EINVAL);
196 
197 	master = kzalloc(sizeof(*master), GFP_KERNEL);
198 	if (!master)
199 		return ERR_PTR(-ENOMEM);
200 
201 	master->layout = layout;
202 	master->characteristics = characteristics;
203 	master->base = base;
204 	master->num_parents = 1;
205 
206 	clk = &master->clk;
207 	clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL;
208 	ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER_DIV, name,
209 			   parent_name);
210 	if (ret) {
211 		kfree(master);
212 		clk = ERR_PTR(ret);
213 	}
214 
215 	return clk;
216 }
217 
218 U_BOOT_DRIVER(at91_master_div_clk) = {
219 	.name = UBOOT_DM_CLK_AT91_MASTER_DIV,
220 	.id = UCLASS_CLK,
221 	.ops = &master_div_ops,
222 	.flags = DM_FLAG_PRE_RELOC,
223 };
224 
clk_sama7g5_master_set_parent(struct clk * clk,struct clk * parent)225 static int clk_sama7g5_master_set_parent(struct clk *clk, struct clk *parent)
226 {
227 	struct clk_master *master = to_clk_master(clk);
228 	int index;
229 
230 	index = at91_clk_mux_val_to_index(master->clk_mux_table,
231 					  master->num_parents, parent->id);
232 	if (index < 0)
233 		return index;
234 
235 	index = at91_clk_mux_index_to_val(master->mux_table,
236 					  master->num_parents, index);
237 	if (index < 0)
238 		return index;
239 
240 	pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
241 	pmc_update_bits(master->base, PMC_MCR,
242 			PMC_MCR_CSS | PMC_MCR_CMD | PMC_MCR_ID_MSK,
243 			(index << PMC_MCR_CSS_SHIFT) | PMC_MCR_CMD |
244 			PMC_MCR_ID(master->id));
245 	return 0;
246 }
247 
clk_sama7g5_master_enable(struct clk * clk)248 static int clk_sama7g5_master_enable(struct clk *clk)
249 {
250 	struct clk_master *master = to_clk_master(clk);
251 
252 	pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
253 	pmc_update_bits(master->base, PMC_MCR,
254 			PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
255 			PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID(master->id));
256 
257 	return 0;
258 }
259 
clk_sama7g5_master_disable(struct clk * clk)260 static int clk_sama7g5_master_disable(struct clk *clk)
261 {
262 	struct clk_master *master = to_clk_master(clk);
263 
264 	pmc_write(master->base, PMC_MCR, master->id);
265 	pmc_update_bits(master->base, PMC_MCR,
266 			PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
267 			PMC_MCR_CMD | PMC_MCR_ID(master->id));
268 
269 	return 0;
270 }
271 
clk_sama7g5_master_set_rate(struct clk * clk,ulong rate)272 static ulong clk_sama7g5_master_set_rate(struct clk *clk, ulong rate)
273 {
274 	struct clk_master *master = to_clk_master(clk);
275 	ulong parent_rate = clk_get_parent_rate(clk);
276 	ulong div, rrate;
277 
278 	if (!parent_rate)
279 		return 0;
280 
281 	div = DIV_ROUND_CLOSEST(parent_rate, rate);
282 	if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1))) {
283 		return 0;
284 	} else if (div == 3) {
285 		rrate = DIV_ROUND_CLOSEST(parent_rate, MASTER_PRES_MAX);
286 		div = MASTER_PRES_MAX;
287 	} else {
288 		rrate = DIV_ROUND_CLOSEST(parent_rate, div);
289 		div = ffs(div) - 1;
290 	}
291 
292 	pmc_write(master->base, PMC_MCR, master->id);
293 	pmc_update_bits(master->base, PMC_MCR,
294 			PMC_MCR_DIV | PMC_MCR_CMD | PMC_MCR_ID_MSK,
295 			(div << MASTER_DIV_SHIFT) | PMC_MCR_CMD |
296 			PMC_MCR_ID(master->id));
297 
298 	return rrate;
299 }
300 
clk_sama7g5_master_get_rate(struct clk * clk)301 static ulong clk_sama7g5_master_get_rate(struct clk *clk)
302 {
303 	struct clk_master *master = to_clk_master(clk);
304 	ulong parent_rate = clk_get_parent_rate(clk);
305 	unsigned int val;
306 	ulong div;
307 
308 	if (!parent_rate)
309 		return 0;
310 
311 	pmc_write(master->base, PMC_MCR, master->id);
312 	pmc_read(master->base, PMC_MCR, &val);
313 
314 	div = (val >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
315 
316 	if (div == MASTER_PRES_MAX)
317 		div = 3;
318 	else
319 		div = 1 << div;
320 
321 	return DIV_ROUND_CLOSEST(parent_rate, div);
322 }
323 
324 static const struct clk_ops sama7g5_master_ops = {
325 	.enable = clk_sama7g5_master_enable,
326 	.disable = clk_sama7g5_master_disable,
327 	.set_rate = clk_sama7g5_master_set_rate,
328 	.get_rate = clk_sama7g5_master_get_rate,
329 	.set_parent = clk_sama7g5_master_set_parent,
330 };
331 
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)332 struct clk *at91_clk_sama7g5_register_master(void __iomem *base,
333 		const char *name, const char * const *parent_names,
334 		int num_parents, const u32 *mux_table, const u32 *clk_mux_table,
335 		bool critical, u8 id)
336 {
337 	struct clk_master *master;
338 	struct clk *clk;
339 	u32 val, index;
340 	int ret;
341 
342 	if (!base || !name || !num_parents || !parent_names ||
343 	    !mux_table || !clk_mux_table || id > MASTER_MAX_ID)
344 		return ERR_PTR(-EINVAL);
345 
346 	master = kzalloc(sizeof(*master), GFP_KERNEL);
347 	if (!master)
348 		return ERR_PTR(-ENOMEM);
349 
350 	master->base = base;
351 	master->id = id;
352 	master->mux_table = mux_table;
353 	master->clk_mux_table = clk_mux_table;
354 	master->num_parents = num_parents;
355 
356 	pmc_write(master->base, PMC_MCR, master->id);
357 	pmc_read(master->base, PMC_MCR, &val);
358 
359 	index = at91_clk_mux_val_to_index(master->mux_table,
360 				master->num_parents,
361 				(val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT);
362 	if (index < 0) {
363 		kfree(master);
364 		return ERR_PTR(index);
365 	}
366 
367 	clk = &master->clk;
368 	clk->flags = CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0);
369 
370 	ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_MASTER, name,
371 			   parent_names[index]);
372 	if (ret) {
373 		kfree(master);
374 		clk = ERR_PTR(ret);
375 	}
376 
377 	return clk;
378 }
379 
380 U_BOOT_DRIVER(at91_sama7g5_master_clk) = {
381 	.name = UBOOT_DM_CLK_AT91_SAMA7G5_MASTER,
382 	.id = UCLASS_CLK,
383 	.ops = &sama7g5_master_ops,
384 	.flags = DM_FLAG_PRE_RELOC,
385 };
386 
387 const struct clk_master_layout at91rm9200_master_layout = {
388 	.mask = 0x31F,
389 	.pres_shift = 2,
390 	.offset = AT91_PMC_MCKR,
391 };
392 
393 const struct clk_master_layout at91sam9x5_master_layout = {
394 	.mask = 0x373,
395 	.pres_shift = 4,
396 	.offset = AT91_PMC_MCKR,
397 };
398