1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
4 */
5
6#include <linux/clk-provider.h>
7#include <linux/clkdev.h>
8#include <linux/clk/at91_pmc.h>
9#include <linux/of.h>
10#include <linux/mfd/syscon.h>
11#include <linux/regmap.h>
12
13#include "pmc.h"
14
15#define MASTER_PRES_MASK	0x7
16#define MASTER_PRES_MAX		MASTER_PRES_MASK
17#define MASTER_DIV_SHIFT	8
18#define MASTER_DIV_MASK		0x3
19
20#define PMC_MCR			0x30
21#define PMC_MCR_ID_MSK		GENMASK(3, 0)
22#define PMC_MCR_CMD		BIT(7)
23#define PMC_MCR_DIV		GENMASK(10, 8)
24#define PMC_MCR_CSS		GENMASK(20, 16)
25#define PMC_MCR_CSS_SHIFT	(16)
26#define PMC_MCR_EN		BIT(28)
27
28#define PMC_MCR_ID(x)		((x) & PMC_MCR_ID_MSK)
29
30#define MASTER_MAX_ID		4
31
32#define to_clk_master(hw) container_of(hw, struct clk_master, hw)
33
34struct clk_master {
35	struct clk_hw hw;
36	struct regmap *regmap;
37	spinlock_t *lock;
38	const struct clk_master_layout *layout;
39	const struct clk_master_characteristics *characteristics;
40	u32 *mux_table;
41	u32 mckr;
42	int chg_pid;
43	u8 id;
44	u8 parent;
45	u8 div;
46};
47
48static inline bool clk_master_ready(struct clk_master *master)
49{
50	unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
51	unsigned int status;
52
53	regmap_read(master->regmap, AT91_PMC_SR, &status);
54
55	return !!(status & bit);
56}
57
58static int clk_master_prepare(struct clk_hw *hw)
59{
60	struct clk_master *master = to_clk_master(hw);
61
62	while (!clk_master_ready(master))
63		cpu_relax();
64
65	return 0;
66}
67
68static int clk_master_is_prepared(struct clk_hw *hw)
69{
70	struct clk_master *master = to_clk_master(hw);
71
72	return clk_master_ready(master);
73}
74
75static unsigned long clk_master_recalc_rate(struct clk_hw *hw,
76					    unsigned long parent_rate)
77{
78	u8 pres;
79	u8 div;
80	unsigned long rate = parent_rate;
81	struct clk_master *master = to_clk_master(hw);
82	const struct clk_master_layout *layout = master->layout;
83	const struct clk_master_characteristics *characteristics =
84						master->characteristics;
85	unsigned int mckr;
86
87	regmap_read(master->regmap, master->layout->offset, &mckr);
88	mckr &= layout->mask;
89
90	pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
91	div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
92
93	if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
94		rate /= 3;
95	else
96		rate >>= pres;
97
98	rate /= characteristics->divisors[div];
99
100	if (rate < characteristics->output.min)
101		pr_warn("master clk is underclocked");
102	else if (rate > characteristics->output.max)
103		pr_warn("master clk is overclocked");
104
105	return rate;
106}
107
108static u8 clk_master_get_parent(struct clk_hw *hw)
109{
110	struct clk_master *master = to_clk_master(hw);
111	unsigned int mckr;
112
113	regmap_read(master->regmap, master->layout->offset, &mckr);
114
115	return mckr & AT91_PMC_CSS;
116}
117
118static const struct clk_ops master_ops = {
119	.prepare = clk_master_prepare,
120	.is_prepared = clk_master_is_prepared,
121	.recalc_rate = clk_master_recalc_rate,
122	.get_parent = clk_master_get_parent,
123};
124
125struct clk_hw * __init
126at91_clk_register_master(struct regmap *regmap,
127		const char *name, int num_parents,
128		const char **parent_names,
129		const struct clk_master_layout *layout,
130		const struct clk_master_characteristics *characteristics)
131{
132	struct clk_master *master;
133	struct clk_init_data init;
134	struct clk_hw *hw;
135	int ret;
136
137	if (!name || !num_parents || !parent_names)
138		return ERR_PTR(-EINVAL);
139
140	master = kzalloc(sizeof(*master), GFP_KERNEL);
141	if (!master)
142		return ERR_PTR(-ENOMEM);
143
144	init.name = name;
145	init.ops = &master_ops;
146	init.parent_names = parent_names;
147	init.num_parents = num_parents;
148	init.flags = 0;
149
150	master->hw.init = &init;
151	master->layout = layout;
152	master->characteristics = characteristics;
153	master->regmap = regmap;
154
155	hw = &master->hw;
156	ret = clk_hw_register(NULL, &master->hw);
157	if (ret) {
158		kfree(master);
159		hw = ERR_PTR(ret);
160	}
161
162	return hw;
163}
164
165static unsigned long
166clk_sama7g5_master_recalc_rate(struct clk_hw *hw,
167			       unsigned long parent_rate)
168{
169	struct clk_master *master = to_clk_master(hw);
170
171	return DIV_ROUND_CLOSEST_ULL(parent_rate, (1 << master->div));
172}
173
174static void clk_sama7g5_master_best_diff(struct clk_rate_request *req,
175					 struct clk_hw *parent,
176					 unsigned long parent_rate,
177					 long *best_rate,
178					 long *best_diff,
179					 u32 div)
180{
181	unsigned long tmp_rate, tmp_diff;
182
183	if (div == MASTER_PRES_MAX)
184		tmp_rate = parent_rate / 3;
185	else
186		tmp_rate = parent_rate >> div;
187
188	tmp_diff = abs(req->rate - tmp_rate);
189
190	if (*best_diff < 0 || *best_diff >= tmp_diff) {
191		*best_rate = tmp_rate;
192		*best_diff = tmp_diff;
193		req->best_parent_rate = parent_rate;
194		req->best_parent_hw = parent;
195	}
196}
197
198static int clk_sama7g5_master_determine_rate(struct clk_hw *hw,
199					     struct clk_rate_request *req)
200{
201	struct clk_master *master = to_clk_master(hw);
202	struct clk_rate_request req_parent = *req;
203	struct clk_hw *parent;
204	long best_rate = LONG_MIN, best_diff = LONG_MIN;
205	unsigned long parent_rate;
206	unsigned int div, i;
207
208	/* First: check the dividers of MCR. */
209	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
210		parent = clk_hw_get_parent_by_index(hw, i);
211		if (!parent)
212			continue;
213
214		parent_rate = clk_hw_get_rate(parent);
215		if (!parent_rate)
216			continue;
217
218		for (div = 0; div < MASTER_PRES_MAX + 1; div++) {
219			clk_sama7g5_master_best_diff(req, parent, parent_rate,
220						     &best_rate, &best_diff,
221						     div);
222			if (!best_diff)
223				break;
224		}
225
226		if (!best_diff)
227			break;
228	}
229
230	/* Second: try to request rate form changeable parent. */
231	if (master->chg_pid < 0)
232		goto end;
233
234	parent = clk_hw_get_parent_by_index(hw, master->chg_pid);
235	if (!parent)
236		goto end;
237
238	for (div = 0; div < MASTER_PRES_MAX + 1; div++) {
239		if (div == MASTER_PRES_MAX)
240			req_parent.rate = req->rate * 3;
241		else
242			req_parent.rate = req->rate << div;
243
244		if (__clk_determine_rate(parent, &req_parent))
245			continue;
246
247		clk_sama7g5_master_best_diff(req, parent, req_parent.rate,
248					     &best_rate, &best_diff, div);
249
250		if (!best_diff)
251			break;
252	}
253
254end:
255	pr_debug("MCK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
256		 __func__, best_rate,
257		 __clk_get_name((req->best_parent_hw)->clk),
258		req->best_parent_rate);
259
260	if (best_rate < 0)
261		return -EINVAL;
262
263	req->rate = best_rate;
264
265	return 0;
266}
267
268static u8 clk_sama7g5_master_get_parent(struct clk_hw *hw)
269{
270	struct clk_master *master = to_clk_master(hw);
271	unsigned long flags;
272	u8 index;
273
274	spin_lock_irqsave(master->lock, flags);
275	index = clk_mux_val_to_index(&master->hw, master->mux_table, 0,
276				     master->parent);
277	spin_unlock_irqrestore(master->lock, flags);
278
279	return index;
280}
281
282static int clk_sama7g5_master_set_parent(struct clk_hw *hw, u8 index)
283{
284	struct clk_master *master = to_clk_master(hw);
285	unsigned long flags;
286
287	if (index >= clk_hw_get_num_parents(hw))
288		return -EINVAL;
289
290	spin_lock_irqsave(master->lock, flags);
291	master->parent = clk_mux_index_to_val(master->mux_table, 0, index);
292	spin_unlock_irqrestore(master->lock, flags);
293
294	return 0;
295}
296
297static int clk_sama7g5_master_enable(struct clk_hw *hw)
298{
299	struct clk_master *master = to_clk_master(hw);
300	unsigned long flags;
301	unsigned int val, cparent;
302
303	spin_lock_irqsave(master->lock, flags);
304
305	regmap_write(master->regmap, PMC_MCR, PMC_MCR_ID(master->id));
306	regmap_read(master->regmap, PMC_MCR, &val);
307	regmap_update_bits(master->regmap, PMC_MCR,
308			   PMC_MCR_EN | PMC_MCR_CSS | PMC_MCR_DIV |
309			   PMC_MCR_CMD | PMC_MCR_ID_MSK,
310			   PMC_MCR_EN | (master->parent << PMC_MCR_CSS_SHIFT) |
311			   (master->div << MASTER_DIV_SHIFT) |
312			   PMC_MCR_CMD | PMC_MCR_ID(master->id));
313
314	cparent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT;
315
316	/* Wait here only if parent is being changed. */
317	while ((cparent != master->parent) && !clk_master_ready(master))
318		cpu_relax();
319
320	spin_unlock_irqrestore(master->lock, flags);
321
322	return 0;
323}
324
325static void clk_sama7g5_master_disable(struct clk_hw *hw)
326{
327	struct clk_master *master = to_clk_master(hw);
328	unsigned long flags;
329
330	spin_lock_irqsave(master->lock, flags);
331
332	regmap_write(master->regmap, PMC_MCR, master->id);
333	regmap_update_bits(master->regmap, PMC_MCR,
334			   PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
335			   PMC_MCR_CMD | PMC_MCR_ID(master->id));
336
337	spin_unlock_irqrestore(master->lock, flags);
338}
339
340static int clk_sama7g5_master_is_enabled(struct clk_hw *hw)
341{
342	struct clk_master *master = to_clk_master(hw);
343	unsigned long flags;
344	unsigned int val;
345
346	spin_lock_irqsave(master->lock, flags);
347
348	regmap_write(master->regmap, PMC_MCR, master->id);
349	regmap_read(master->regmap, PMC_MCR, &val);
350
351	spin_unlock_irqrestore(master->lock, flags);
352
353	return !!(val & PMC_MCR_EN);
354}
355
356static int clk_sama7g5_master_set_rate(struct clk_hw *hw, unsigned long rate,
357				       unsigned long parent_rate)
358{
359	struct clk_master *master = to_clk_master(hw);
360	unsigned long div, flags;
361
362	div = DIV_ROUND_CLOSEST(parent_rate, rate);
363	if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1)))
364		return -EINVAL;
365
366	if (div == 3)
367		div = MASTER_PRES_MAX;
368	else
369		div = ffs(div) - 1;
370
371	spin_lock_irqsave(master->lock, flags);
372	master->div = div;
373	spin_unlock_irqrestore(master->lock, flags);
374
375	return 0;
376}
377
378static const struct clk_ops sama7g5_master_ops = {
379	.enable = clk_sama7g5_master_enable,
380	.disable = clk_sama7g5_master_disable,
381	.is_enabled = clk_sama7g5_master_is_enabled,
382	.recalc_rate = clk_sama7g5_master_recalc_rate,
383	.determine_rate = clk_sama7g5_master_determine_rate,
384	.set_rate = clk_sama7g5_master_set_rate,
385	.get_parent = clk_sama7g5_master_get_parent,
386	.set_parent = clk_sama7g5_master_set_parent,
387};
388
389struct clk_hw * __init
390at91_clk_sama7g5_register_master(struct regmap *regmap,
391				 const char *name, int num_parents,
392				 const char **parent_names,
393				 u32 *mux_table,
394				 spinlock_t *lock, u8 id,
395				 bool critical, int chg_pid)
396{
397	struct clk_master *master;
398	struct clk_hw *hw;
399	struct clk_init_data init;
400	unsigned long flags;
401	unsigned int val;
402	int ret;
403
404	if (!name || !num_parents || !parent_names || !mux_table ||
405	    !lock || id > MASTER_MAX_ID)
406		return ERR_PTR(-EINVAL);
407
408	master = kzalloc(sizeof(*master), GFP_KERNEL);
409	if (!master)
410		return ERR_PTR(-ENOMEM);
411
412	init.name = name;
413	init.ops = &sama7g5_master_ops;
414	init.parent_names = parent_names;
415	init.num_parents = num_parents;
416	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
417	if (chg_pid >= 0)
418		init.flags |= CLK_SET_RATE_PARENT;
419	if (critical)
420		init.flags |= CLK_IS_CRITICAL;
421
422	master->hw.init = &init;
423	master->regmap = regmap;
424	master->id = id;
425	master->chg_pid = chg_pid;
426	master->lock = lock;
427	master->mux_table = mux_table;
428
429	spin_lock_irqsave(master->lock, flags);
430	regmap_write(master->regmap, PMC_MCR, master->id);
431	regmap_read(master->regmap, PMC_MCR, &val);
432	master->parent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT;
433	master->div = (val & PMC_MCR_DIV) >> MASTER_DIV_SHIFT;
434	spin_unlock_irqrestore(master->lock, flags);
435
436	hw = &master->hw;
437	ret = clk_hw_register(NULL, &master->hw);
438	if (ret) {
439		kfree(master);
440		hw = ERR_PTR(ret);
441	}
442
443	return hw;
444}
445
446const struct clk_master_layout at91rm9200_master_layout = {
447	.mask = 0x31F,
448	.pres_shift = 2,
449	.offset = AT91_PMC_MCKR,
450};
451
452const struct clk_master_layout at91sam9x5_master_layout = {
453	.mask = 0x373,
454	.pres_shift = 4,
455	.offset = AT91_PMC_MCKR,
456};
457