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