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