xref: /OK3568_Linux_fs/kernel/drivers/clk/at91/clk-peripheral.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/bitops.h>
7*4882a593Smuzhiyun #include <linux/clk-provider.h>
8*4882a593Smuzhiyun #include <linux/clkdev.h>
9*4882a593Smuzhiyun #include <linux/clk/at91_pmc.h>
10*4882a593Smuzhiyun #include <linux/of.h>
11*4882a593Smuzhiyun #include <linux/mfd/syscon.h>
12*4882a593Smuzhiyun #include <linux/regmap.h>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include "pmc.h"
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun DEFINE_SPINLOCK(pmc_pcr_lock);
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #define PERIPHERAL_ID_MIN	2
19*4882a593Smuzhiyun #define PERIPHERAL_ID_MAX	31
20*4882a593Smuzhiyun #define PERIPHERAL_MASK(id)	(1 << ((id) & PERIPHERAL_ID_MAX))
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun #define PERIPHERAL_MAX_SHIFT	3
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun struct clk_peripheral {
25*4882a593Smuzhiyun 	struct clk_hw hw;
26*4882a593Smuzhiyun 	struct regmap *regmap;
27*4882a593Smuzhiyun 	u32 id;
28*4882a593Smuzhiyun };
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun #define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw)
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun struct clk_sam9x5_peripheral {
33*4882a593Smuzhiyun 	struct clk_hw hw;
34*4882a593Smuzhiyun 	struct regmap *regmap;
35*4882a593Smuzhiyun 	struct clk_range range;
36*4882a593Smuzhiyun 	spinlock_t *lock;
37*4882a593Smuzhiyun 	u32 id;
38*4882a593Smuzhiyun 	u32 div;
39*4882a593Smuzhiyun 	const struct clk_pcr_layout *layout;
40*4882a593Smuzhiyun 	bool auto_div;
41*4882a593Smuzhiyun 	int chg_pid;
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun #define to_clk_sam9x5_peripheral(hw) \
45*4882a593Smuzhiyun 	container_of(hw, struct clk_sam9x5_peripheral, hw)
46*4882a593Smuzhiyun 
clk_peripheral_enable(struct clk_hw * hw)47*4882a593Smuzhiyun static int clk_peripheral_enable(struct clk_hw *hw)
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun 	struct clk_peripheral *periph = to_clk_peripheral(hw);
50*4882a593Smuzhiyun 	int offset = AT91_PMC_PCER;
51*4882a593Smuzhiyun 	u32 id = periph->id;
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	if (id < PERIPHERAL_ID_MIN)
54*4882a593Smuzhiyun 		return 0;
55*4882a593Smuzhiyun 	if (id > PERIPHERAL_ID_MAX)
56*4882a593Smuzhiyun 		offset = AT91_PMC_PCER1;
57*4882a593Smuzhiyun 	regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	return 0;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun 
clk_peripheral_disable(struct clk_hw * hw)62*4882a593Smuzhiyun static void clk_peripheral_disable(struct clk_hw *hw)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun 	struct clk_peripheral *periph = to_clk_peripheral(hw);
65*4882a593Smuzhiyun 	int offset = AT91_PMC_PCDR;
66*4882a593Smuzhiyun 	u32 id = periph->id;
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	if (id < PERIPHERAL_ID_MIN)
69*4882a593Smuzhiyun 		return;
70*4882a593Smuzhiyun 	if (id > PERIPHERAL_ID_MAX)
71*4882a593Smuzhiyun 		offset = AT91_PMC_PCDR1;
72*4882a593Smuzhiyun 	regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun 
clk_peripheral_is_enabled(struct clk_hw * hw)75*4882a593Smuzhiyun static int clk_peripheral_is_enabled(struct clk_hw *hw)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun 	struct clk_peripheral *periph = to_clk_peripheral(hw);
78*4882a593Smuzhiyun 	int offset = AT91_PMC_PCSR;
79*4882a593Smuzhiyun 	unsigned int status;
80*4882a593Smuzhiyun 	u32 id = periph->id;
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	if (id < PERIPHERAL_ID_MIN)
83*4882a593Smuzhiyun 		return 1;
84*4882a593Smuzhiyun 	if (id > PERIPHERAL_ID_MAX)
85*4882a593Smuzhiyun 		offset = AT91_PMC_PCSR1;
86*4882a593Smuzhiyun 	regmap_read(periph->regmap, offset, &status);
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	return status & PERIPHERAL_MASK(id) ? 1 : 0;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun static const struct clk_ops peripheral_ops = {
92*4882a593Smuzhiyun 	.enable = clk_peripheral_enable,
93*4882a593Smuzhiyun 	.disable = clk_peripheral_disable,
94*4882a593Smuzhiyun 	.is_enabled = clk_peripheral_is_enabled,
95*4882a593Smuzhiyun };
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun struct clk_hw * __init
at91_clk_register_peripheral(struct regmap * regmap,const char * name,const char * parent_name,u32 id)98*4882a593Smuzhiyun at91_clk_register_peripheral(struct regmap *regmap, const char *name,
99*4882a593Smuzhiyun 			     const char *parent_name, u32 id)
100*4882a593Smuzhiyun {
101*4882a593Smuzhiyun 	struct clk_peripheral *periph;
102*4882a593Smuzhiyun 	struct clk_init_data init;
103*4882a593Smuzhiyun 	struct clk_hw *hw;
104*4882a593Smuzhiyun 	int ret;
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
107*4882a593Smuzhiyun 		return ERR_PTR(-EINVAL);
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	periph = kzalloc(sizeof(*periph), GFP_KERNEL);
110*4882a593Smuzhiyun 	if (!periph)
111*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun 	init.name = name;
114*4882a593Smuzhiyun 	init.ops = &peripheral_ops;
115*4882a593Smuzhiyun 	init.parent_names = &parent_name;
116*4882a593Smuzhiyun 	init.num_parents = 1;
117*4882a593Smuzhiyun 	init.flags = 0;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	periph->id = id;
120*4882a593Smuzhiyun 	periph->hw.init = &init;
121*4882a593Smuzhiyun 	periph->regmap = regmap;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	hw = &periph->hw;
124*4882a593Smuzhiyun 	ret = clk_hw_register(NULL, &periph->hw);
125*4882a593Smuzhiyun 	if (ret) {
126*4882a593Smuzhiyun 		kfree(periph);
127*4882a593Smuzhiyun 		hw = ERR_PTR(ret);
128*4882a593Smuzhiyun 	}
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	return hw;
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun 
clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral * periph)133*4882a593Smuzhiyun static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun 	struct clk_hw *parent;
136*4882a593Smuzhiyun 	unsigned long parent_rate;
137*4882a593Smuzhiyun 	int shift = 0;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	if (!periph->auto_div)
140*4882a593Smuzhiyun 		return;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	if (periph->range.max) {
143*4882a593Smuzhiyun 		parent = clk_hw_get_parent_by_index(&periph->hw, 0);
144*4882a593Smuzhiyun 		parent_rate = clk_hw_get_rate(parent);
145*4882a593Smuzhiyun 		if (!parent_rate)
146*4882a593Smuzhiyun 			return;
147*4882a593Smuzhiyun 
148*4882a593Smuzhiyun 		for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
149*4882a593Smuzhiyun 			if (parent_rate >> shift <= periph->range.max)
150*4882a593Smuzhiyun 				break;
151*4882a593Smuzhiyun 		}
152*4882a593Smuzhiyun 	}
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 	periph->auto_div = false;
155*4882a593Smuzhiyun 	periph->div = shift;
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun 
clk_sam9x5_peripheral_enable(struct clk_hw * hw)158*4882a593Smuzhiyun static int clk_sam9x5_peripheral_enable(struct clk_hw *hw)
159*4882a593Smuzhiyun {
160*4882a593Smuzhiyun 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
161*4882a593Smuzhiyun 	unsigned long flags;
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	if (periph->id < PERIPHERAL_ID_MIN)
164*4882a593Smuzhiyun 		return 0;
165*4882a593Smuzhiyun 
166*4882a593Smuzhiyun 	spin_lock_irqsave(periph->lock, flags);
167*4882a593Smuzhiyun 	regmap_write(periph->regmap, periph->layout->offset,
168*4882a593Smuzhiyun 		     (periph->id & periph->layout->pid_mask));
169*4882a593Smuzhiyun 	regmap_update_bits(periph->regmap, periph->layout->offset,
170*4882a593Smuzhiyun 			   periph->layout->div_mask | periph->layout->cmd |
171*4882a593Smuzhiyun 			   AT91_PMC_PCR_EN,
172*4882a593Smuzhiyun 			   field_prep(periph->layout->div_mask, periph->div) |
173*4882a593Smuzhiyun 			   periph->layout->cmd |
174*4882a593Smuzhiyun 			   AT91_PMC_PCR_EN);
175*4882a593Smuzhiyun 	spin_unlock_irqrestore(periph->lock, flags);
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	return 0;
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun 
clk_sam9x5_peripheral_disable(struct clk_hw * hw)180*4882a593Smuzhiyun static void clk_sam9x5_peripheral_disable(struct clk_hw *hw)
181*4882a593Smuzhiyun {
182*4882a593Smuzhiyun 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
183*4882a593Smuzhiyun 	unsigned long flags;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	if (periph->id < PERIPHERAL_ID_MIN)
186*4882a593Smuzhiyun 		return;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	spin_lock_irqsave(periph->lock, flags);
189*4882a593Smuzhiyun 	regmap_write(periph->regmap, periph->layout->offset,
190*4882a593Smuzhiyun 		     (periph->id & periph->layout->pid_mask));
191*4882a593Smuzhiyun 	regmap_update_bits(periph->regmap, periph->layout->offset,
192*4882a593Smuzhiyun 			   AT91_PMC_PCR_EN | periph->layout->cmd,
193*4882a593Smuzhiyun 			   periph->layout->cmd);
194*4882a593Smuzhiyun 	spin_unlock_irqrestore(periph->lock, flags);
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun 
clk_sam9x5_peripheral_is_enabled(struct clk_hw * hw)197*4882a593Smuzhiyun static int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
200*4882a593Smuzhiyun 	unsigned long flags;
201*4882a593Smuzhiyun 	unsigned int status;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	if (periph->id < PERIPHERAL_ID_MIN)
204*4882a593Smuzhiyun 		return 1;
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun 	spin_lock_irqsave(periph->lock, flags);
207*4882a593Smuzhiyun 	regmap_write(periph->regmap, periph->layout->offset,
208*4882a593Smuzhiyun 		     (periph->id & periph->layout->pid_mask));
209*4882a593Smuzhiyun 	regmap_read(periph->regmap, periph->layout->offset, &status);
210*4882a593Smuzhiyun 	spin_unlock_irqrestore(periph->lock, flags);
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	return !!(status & AT91_PMC_PCR_EN);
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun static unsigned long
clk_sam9x5_peripheral_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)216*4882a593Smuzhiyun clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
217*4882a593Smuzhiyun 				  unsigned long parent_rate)
218*4882a593Smuzhiyun {
219*4882a593Smuzhiyun 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
220*4882a593Smuzhiyun 	unsigned long flags;
221*4882a593Smuzhiyun 	unsigned int status;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	if (periph->id < PERIPHERAL_ID_MIN)
224*4882a593Smuzhiyun 		return parent_rate;
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	spin_lock_irqsave(periph->lock, flags);
227*4882a593Smuzhiyun 	regmap_write(periph->regmap, periph->layout->offset,
228*4882a593Smuzhiyun 		     (periph->id & periph->layout->pid_mask));
229*4882a593Smuzhiyun 	regmap_read(periph->regmap, periph->layout->offset, &status);
230*4882a593Smuzhiyun 	spin_unlock_irqrestore(periph->lock, flags);
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	if (status & AT91_PMC_PCR_EN) {
233*4882a593Smuzhiyun 		periph->div = field_get(periph->layout->div_mask, status);
234*4882a593Smuzhiyun 		periph->auto_div = false;
235*4882a593Smuzhiyun 	} else {
236*4882a593Smuzhiyun 		clk_sam9x5_peripheral_autodiv(periph);
237*4882a593Smuzhiyun 	}
238*4882a593Smuzhiyun 
239*4882a593Smuzhiyun 	return parent_rate >> periph->div;
240*4882a593Smuzhiyun }
241*4882a593Smuzhiyun 
clk_sam9x5_peripheral_best_diff(struct clk_rate_request * req,struct clk_hw * parent,unsigned long parent_rate,u32 shift,long * best_diff,long * best_rate)242*4882a593Smuzhiyun static void clk_sam9x5_peripheral_best_diff(struct clk_rate_request *req,
243*4882a593Smuzhiyun 					    struct clk_hw *parent,
244*4882a593Smuzhiyun 					    unsigned long parent_rate,
245*4882a593Smuzhiyun 					    u32 shift, long *best_diff,
246*4882a593Smuzhiyun 					    long *best_rate)
247*4882a593Smuzhiyun {
248*4882a593Smuzhiyun 	unsigned long tmp_rate = parent_rate >> shift;
249*4882a593Smuzhiyun 	unsigned long tmp_diff = abs(req->rate - tmp_rate);
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	if (*best_diff < 0 || *best_diff >= tmp_diff) {
252*4882a593Smuzhiyun 		*best_rate = tmp_rate;
253*4882a593Smuzhiyun 		*best_diff = tmp_diff;
254*4882a593Smuzhiyun 		req->best_parent_rate = parent_rate;
255*4882a593Smuzhiyun 		req->best_parent_hw = parent;
256*4882a593Smuzhiyun 	}
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun 
clk_sam9x5_peripheral_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)259*4882a593Smuzhiyun static int clk_sam9x5_peripheral_determine_rate(struct clk_hw *hw,
260*4882a593Smuzhiyun 						struct clk_rate_request *req)
261*4882a593Smuzhiyun {
262*4882a593Smuzhiyun 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
263*4882a593Smuzhiyun 	struct clk_hw *parent = clk_hw_get_parent(hw);
264*4882a593Smuzhiyun 	struct clk_rate_request req_parent = *req;
265*4882a593Smuzhiyun 	unsigned long parent_rate = clk_hw_get_rate(parent);
266*4882a593Smuzhiyun 	unsigned long tmp_rate;
267*4882a593Smuzhiyun 	long best_rate = LONG_MIN;
268*4882a593Smuzhiyun 	long best_diff = LONG_MIN;
269*4882a593Smuzhiyun 	u32 shift;
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
272*4882a593Smuzhiyun 		return parent_rate;
273*4882a593Smuzhiyun 
274*4882a593Smuzhiyun 	/* Fist step: check the available dividers. */
275*4882a593Smuzhiyun 	for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
276*4882a593Smuzhiyun 		tmp_rate = parent_rate >> shift;
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 		if (periph->range.max && tmp_rate > periph->range.max)
279*4882a593Smuzhiyun 			continue;
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 		clk_sam9x5_peripheral_best_diff(req, parent, parent_rate,
282*4882a593Smuzhiyun 						shift, &best_diff, &best_rate);
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 		if (!best_diff || best_rate <= req->rate)
285*4882a593Smuzhiyun 			break;
286*4882a593Smuzhiyun 	}
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 	if (periph->chg_pid < 0)
289*4882a593Smuzhiyun 		goto end;
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	/* Step two: try to request rate from parent. */
292*4882a593Smuzhiyun 	parent = clk_hw_get_parent_by_index(hw, periph->chg_pid);
293*4882a593Smuzhiyun 	if (!parent)
294*4882a593Smuzhiyun 		goto end;
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
297*4882a593Smuzhiyun 		req_parent.rate = req->rate << shift;
298*4882a593Smuzhiyun 
299*4882a593Smuzhiyun 		if (__clk_determine_rate(parent, &req_parent))
300*4882a593Smuzhiyun 			continue;
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 		clk_sam9x5_peripheral_best_diff(req, parent, req_parent.rate,
303*4882a593Smuzhiyun 						shift, &best_diff, &best_rate);
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 		if (!best_diff)
306*4882a593Smuzhiyun 			break;
307*4882a593Smuzhiyun 	}
308*4882a593Smuzhiyun end:
309*4882a593Smuzhiyun 	if (best_rate < 0 ||
310*4882a593Smuzhiyun 	    (periph->range.max && best_rate > periph->range.max))
311*4882a593Smuzhiyun 		return -EINVAL;
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 	pr_debug("PCK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
314*4882a593Smuzhiyun 		 __func__, best_rate,
315*4882a593Smuzhiyun 		 __clk_get_name((req->best_parent_hw)->clk),
316*4882a593Smuzhiyun 		 req->best_parent_rate);
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	req->rate = best_rate;
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun 	return 0;
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun 
clk_sam9x5_peripheral_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * parent_rate)323*4882a593Smuzhiyun static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
324*4882a593Smuzhiyun 					     unsigned long rate,
325*4882a593Smuzhiyun 					     unsigned long *parent_rate)
326*4882a593Smuzhiyun {
327*4882a593Smuzhiyun 	int shift = 0;
328*4882a593Smuzhiyun 	unsigned long best_rate;
329*4882a593Smuzhiyun 	unsigned long best_diff;
330*4882a593Smuzhiyun 	unsigned long cur_rate = *parent_rate;
331*4882a593Smuzhiyun 	unsigned long cur_diff;
332*4882a593Smuzhiyun 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
335*4882a593Smuzhiyun 		return *parent_rate;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	if (periph->range.max) {
338*4882a593Smuzhiyun 		for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
339*4882a593Smuzhiyun 			cur_rate = *parent_rate >> shift;
340*4882a593Smuzhiyun 			if (cur_rate <= periph->range.max)
341*4882a593Smuzhiyun 				break;
342*4882a593Smuzhiyun 		}
343*4882a593Smuzhiyun 	}
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun 	if (rate >= cur_rate)
346*4882a593Smuzhiyun 		return cur_rate;
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	best_diff = cur_rate - rate;
349*4882a593Smuzhiyun 	best_rate = cur_rate;
350*4882a593Smuzhiyun 	for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
351*4882a593Smuzhiyun 		cur_rate = *parent_rate >> shift;
352*4882a593Smuzhiyun 		if (cur_rate < rate)
353*4882a593Smuzhiyun 			cur_diff = rate - cur_rate;
354*4882a593Smuzhiyun 		else
355*4882a593Smuzhiyun 			cur_diff = cur_rate - rate;
356*4882a593Smuzhiyun 
357*4882a593Smuzhiyun 		if (cur_diff < best_diff) {
358*4882a593Smuzhiyun 			best_diff = cur_diff;
359*4882a593Smuzhiyun 			best_rate = cur_rate;
360*4882a593Smuzhiyun 		}
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 		if (!best_diff || cur_rate < rate)
363*4882a593Smuzhiyun 			break;
364*4882a593Smuzhiyun 	}
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	return best_rate;
367*4882a593Smuzhiyun }
368*4882a593Smuzhiyun 
clk_sam9x5_peripheral_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)369*4882a593Smuzhiyun static int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw,
370*4882a593Smuzhiyun 					  unsigned long rate,
371*4882a593Smuzhiyun 					  unsigned long parent_rate)
372*4882a593Smuzhiyun {
373*4882a593Smuzhiyun 	int shift;
374*4882a593Smuzhiyun 	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
375*4882a593Smuzhiyun 	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
376*4882a593Smuzhiyun 		if (parent_rate == rate)
377*4882a593Smuzhiyun 			return 0;
378*4882a593Smuzhiyun 		else
379*4882a593Smuzhiyun 			return -EINVAL;
380*4882a593Smuzhiyun 	}
381*4882a593Smuzhiyun 
382*4882a593Smuzhiyun 	if (periph->range.max && rate > periph->range.max)
383*4882a593Smuzhiyun 		return -EINVAL;
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 	for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
386*4882a593Smuzhiyun 		if (parent_rate >> shift == rate) {
387*4882a593Smuzhiyun 			periph->auto_div = false;
388*4882a593Smuzhiyun 			periph->div = shift;
389*4882a593Smuzhiyun 			return 0;
390*4882a593Smuzhiyun 		}
391*4882a593Smuzhiyun 	}
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	return -EINVAL;
394*4882a593Smuzhiyun }
395*4882a593Smuzhiyun 
396*4882a593Smuzhiyun static const struct clk_ops sam9x5_peripheral_ops = {
397*4882a593Smuzhiyun 	.enable = clk_sam9x5_peripheral_enable,
398*4882a593Smuzhiyun 	.disable = clk_sam9x5_peripheral_disable,
399*4882a593Smuzhiyun 	.is_enabled = clk_sam9x5_peripheral_is_enabled,
400*4882a593Smuzhiyun 	.recalc_rate = clk_sam9x5_peripheral_recalc_rate,
401*4882a593Smuzhiyun 	.round_rate = clk_sam9x5_peripheral_round_rate,
402*4882a593Smuzhiyun 	.set_rate = clk_sam9x5_peripheral_set_rate,
403*4882a593Smuzhiyun };
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun static const struct clk_ops sam9x5_peripheral_chg_ops = {
406*4882a593Smuzhiyun 	.enable = clk_sam9x5_peripheral_enable,
407*4882a593Smuzhiyun 	.disable = clk_sam9x5_peripheral_disable,
408*4882a593Smuzhiyun 	.is_enabled = clk_sam9x5_peripheral_is_enabled,
409*4882a593Smuzhiyun 	.recalc_rate = clk_sam9x5_peripheral_recalc_rate,
410*4882a593Smuzhiyun 	.determine_rate = clk_sam9x5_peripheral_determine_rate,
411*4882a593Smuzhiyun 	.set_rate = clk_sam9x5_peripheral_set_rate,
412*4882a593Smuzhiyun };
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun struct clk_hw * __init
at91_clk_register_sam9x5_peripheral(struct regmap * regmap,spinlock_t * lock,const struct clk_pcr_layout * layout,const char * name,const char * parent_name,u32 id,const struct clk_range * range,int chg_pid)415*4882a593Smuzhiyun at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
416*4882a593Smuzhiyun 				    const struct clk_pcr_layout *layout,
417*4882a593Smuzhiyun 				    const char *name, const char *parent_name,
418*4882a593Smuzhiyun 				    u32 id, const struct clk_range *range,
419*4882a593Smuzhiyun 				    int chg_pid)
420*4882a593Smuzhiyun {
421*4882a593Smuzhiyun 	struct clk_sam9x5_peripheral *periph;
422*4882a593Smuzhiyun 	struct clk_init_data init;
423*4882a593Smuzhiyun 	struct clk_hw *hw;
424*4882a593Smuzhiyun 	int ret;
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 	if (!name || !parent_name)
427*4882a593Smuzhiyun 		return ERR_PTR(-EINVAL);
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun 	periph = kzalloc(sizeof(*periph), GFP_KERNEL);
430*4882a593Smuzhiyun 	if (!periph)
431*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 	init.name = name;
434*4882a593Smuzhiyun 	init.parent_names = &parent_name;
435*4882a593Smuzhiyun 	init.num_parents = 1;
436*4882a593Smuzhiyun 	if (chg_pid < 0) {
437*4882a593Smuzhiyun 		init.flags = 0;
438*4882a593Smuzhiyun 		init.ops = &sam9x5_peripheral_ops;
439*4882a593Smuzhiyun 	} else {
440*4882a593Smuzhiyun 		init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
441*4882a593Smuzhiyun 			     CLK_SET_RATE_PARENT;
442*4882a593Smuzhiyun 		init.ops = &sam9x5_peripheral_chg_ops;
443*4882a593Smuzhiyun 	}
444*4882a593Smuzhiyun 
445*4882a593Smuzhiyun 	periph->id = id;
446*4882a593Smuzhiyun 	periph->hw.init = &init;
447*4882a593Smuzhiyun 	periph->div = 0;
448*4882a593Smuzhiyun 	periph->regmap = regmap;
449*4882a593Smuzhiyun 	periph->lock = lock;
450*4882a593Smuzhiyun 	if (layout->div_mask)
451*4882a593Smuzhiyun 		periph->auto_div = true;
452*4882a593Smuzhiyun 	periph->layout = layout;
453*4882a593Smuzhiyun 	periph->range = *range;
454*4882a593Smuzhiyun 	periph->chg_pid = chg_pid;
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 	hw = &periph->hw;
457*4882a593Smuzhiyun 	ret = clk_hw_register(NULL, &periph->hw);
458*4882a593Smuzhiyun 	if (ret) {
459*4882a593Smuzhiyun 		kfree(periph);
460*4882a593Smuzhiyun 		hw = ERR_PTR(ret);
461*4882a593Smuzhiyun 	} else {
462*4882a593Smuzhiyun 		clk_sam9x5_peripheral_autodiv(periph);
463*4882a593Smuzhiyun 		pmc_register_id(id);
464*4882a593Smuzhiyun 	}
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun 	return hw;
467*4882a593Smuzhiyun }
468