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
43struct 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
56static 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
66static 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
78static 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
104static const struct clk_ops master_pres_ops = {
105	.enable = clk_master_enable,
106	.get_rate = clk_master_pres_get_rate,
107};
108
109struct 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
147U_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
154static 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
180static const struct clk_ops master_div_ops = {
181	.enable = clk_master_enable,
182	.get_rate = clk_master_div_get_rate,
183};
184
185struct 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
218U_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
225static 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
248static 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
260static 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
272static 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
301static 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
324static 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
332struct 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
380U_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
387const struct clk_master_layout at91rm9200_master_layout = {
388	.mask = 0x31F,
389	.pres_shift = 2,
390	.offset = AT91_PMC_MCKR,
391};
392
393const struct clk_master_layout at91sam9x5_master_layout = {
394	.mask = 0x373,
395	.pres_shift = 4,
396	.offset = AT91_PMC_MCKR,
397};
398