1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Ingenic SoC CGU driver |
4 | * |
5 | * Copyright (c) 2013-2015 Imagination Technologies |
6 | * Author: Paul Burton <paul.burton@mips.com> |
7 | */ |
8 | |
9 | #include <linux/bitops.h> |
10 | #include <linux/clk.h> |
11 | #include <linux/clk-provider.h> |
12 | #include <linux/clkdev.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/io.h> |
15 | #include <linux/iopoll.h> |
16 | #include <linux/math64.h> |
17 | #include <linux/of.h> |
18 | #include <linux/of_address.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/spinlock.h> |
21 | #include <linux/time.h> |
22 | |
23 | #include "cgu.h" |
24 | |
25 | #define MHZ (1000 * 1000) |
26 | |
27 | static inline const struct ingenic_cgu_clk_info * |
28 | to_clk_info(struct ingenic_clk *clk) |
29 | { |
30 | return &clk->cgu->clock_info[clk->idx]; |
31 | } |
32 | |
33 | /** |
34 | * ingenic_cgu_gate_get() - get the value of clock gate register bit |
35 | * @cgu: reference to the CGU whose registers should be read |
36 | * @info: info struct describing the gate bit |
37 | * |
38 | * Retrieves the state of the clock gate bit described by info. The |
39 | * caller must hold cgu->lock. |
40 | * |
41 | * Return: true if the gate bit is set, else false. |
42 | */ |
43 | static inline bool |
44 | ingenic_cgu_gate_get(struct ingenic_cgu *cgu, |
45 | const struct ingenic_cgu_gate_info *info) |
46 | { |
47 | return !!(readl(addr: cgu->base + info->reg) & BIT(info->bit)) |
48 | ^ info->clear_to_gate; |
49 | } |
50 | |
51 | /** |
52 | * ingenic_cgu_gate_set() - set the value of clock gate register bit |
53 | * @cgu: reference to the CGU whose registers should be modified |
54 | * @info: info struct describing the gate bit |
55 | * @val: non-zero to gate a clock, otherwise zero |
56 | * |
57 | * Sets the given gate bit in order to gate or ungate a clock. |
58 | * |
59 | * The caller must hold cgu->lock. |
60 | */ |
61 | static inline void |
62 | ingenic_cgu_gate_set(struct ingenic_cgu *cgu, |
63 | const struct ingenic_cgu_gate_info *info, bool val) |
64 | { |
65 | u32 clkgr = readl(addr: cgu->base + info->reg); |
66 | |
67 | if (val ^ info->clear_to_gate) |
68 | clkgr |= BIT(info->bit); |
69 | else |
70 | clkgr &= ~BIT(info->bit); |
71 | |
72 | writel(val: clkgr, addr: cgu->base + info->reg); |
73 | } |
74 | |
75 | /* |
76 | * PLL operations |
77 | */ |
78 | |
79 | static unsigned long |
80 | ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) |
81 | { |
82 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); |
83 | const struct ingenic_cgu_clk_info *clk_info = to_clk_info(clk: ingenic_clk); |
84 | struct ingenic_cgu *cgu = ingenic_clk->cgu; |
85 | const struct ingenic_cgu_pll_info *pll_info; |
86 | unsigned m, n, od, od_enc = 0; |
87 | bool bypass; |
88 | u32 ctl; |
89 | |
90 | BUG_ON(clk_info->type != CGU_CLK_PLL); |
91 | pll_info = &clk_info->pll; |
92 | |
93 | ctl = readl(addr: cgu->base + pll_info->reg); |
94 | |
95 | m = (ctl >> pll_info->m_shift) & GENMASK(pll_info->m_bits - 1, 0); |
96 | m += pll_info->m_offset; |
97 | n = (ctl >> pll_info->n_shift) & GENMASK(pll_info->n_bits - 1, 0); |
98 | n += pll_info->n_offset; |
99 | |
100 | if (pll_info->od_bits > 0) { |
101 | od_enc = ctl >> pll_info->od_shift; |
102 | od_enc &= GENMASK(pll_info->od_bits - 1, 0); |
103 | } |
104 | |
105 | if (pll_info->bypass_bit >= 0) { |
106 | ctl = readl(addr: cgu->base + pll_info->bypass_reg); |
107 | |
108 | bypass = !!(ctl & BIT(pll_info->bypass_bit)); |
109 | |
110 | if (bypass) |
111 | return parent_rate; |
112 | } |
113 | |
114 | for (od = 0; od < pll_info->od_max; od++) |
115 | if (pll_info->od_encoding[od] == od_enc) |
116 | break; |
117 | |
118 | /* if od_max = 0, od_bits should be 0 and od is fixed to 1. */ |
119 | if (pll_info->od_max == 0) |
120 | BUG_ON(pll_info->od_bits != 0); |
121 | else |
122 | BUG_ON(od == pll_info->od_max); |
123 | od++; |
124 | |
125 | return div_u64(dividend: (u64)parent_rate * m * pll_info->rate_multiplier, |
126 | divisor: n * od); |
127 | } |
128 | |
129 | static void |
130 | ingenic_pll_calc_m_n_od(const struct ingenic_cgu_pll_info *pll_info, |
131 | unsigned long rate, unsigned long parent_rate, |
132 | unsigned int *pm, unsigned int *pn, unsigned int *pod) |
133 | { |
134 | unsigned int m, n, od = 1; |
135 | |
136 | /* |
137 | * The frequency after the input divider must be between 10 and 50 MHz. |
138 | * The highest divider yields the best resolution. |
139 | */ |
140 | n = parent_rate / (10 * MHZ); |
141 | n = min_t(unsigned int, n, 1 << pll_info->n_bits); |
142 | n = max_t(unsigned int, n, pll_info->n_offset); |
143 | |
144 | m = (rate / MHZ) * od * n / (parent_rate / MHZ); |
145 | m = min_t(unsigned int, m, 1 << pll_info->m_bits); |
146 | m = max_t(unsigned int, m, pll_info->m_offset); |
147 | |
148 | *pm = m; |
149 | *pn = n; |
150 | *pod = od; |
151 | } |
152 | |
153 | static unsigned long |
154 | ingenic_pll_calc(const struct ingenic_cgu_clk_info *clk_info, |
155 | unsigned long rate, unsigned long parent_rate, |
156 | unsigned int *pm, unsigned int *pn, unsigned int *pod) |
157 | { |
158 | const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; |
159 | unsigned int m, n, od; |
160 | |
161 | if (pll_info->calc_m_n_od) |
162 | (*pll_info->calc_m_n_od)(pll_info, rate, parent_rate, &m, &n, &od); |
163 | else |
164 | ingenic_pll_calc_m_n_od(pll_info, rate, parent_rate, pm: &m, pn: &n, pod: &od); |
165 | |
166 | if (pm) |
167 | *pm = m; |
168 | if (pn) |
169 | *pn = n; |
170 | if (pod) |
171 | *pod = od; |
172 | |
173 | return div_u64(dividend: (u64)parent_rate * m * pll_info->rate_multiplier, |
174 | divisor: n * od); |
175 | } |
176 | |
177 | static long |
178 | ingenic_pll_round_rate(struct clk_hw *hw, unsigned long req_rate, |
179 | unsigned long *prate) |
180 | { |
181 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); |
182 | const struct ingenic_cgu_clk_info *clk_info = to_clk_info(clk: ingenic_clk); |
183 | |
184 | return ingenic_pll_calc(clk_info, rate: req_rate, parent_rate: *prate, NULL, NULL, NULL); |
185 | } |
186 | |
187 | static inline int ingenic_pll_check_stable(struct ingenic_cgu *cgu, |
188 | const struct ingenic_cgu_pll_info *pll_info) |
189 | { |
190 | u32 ctl; |
191 | |
192 | if (pll_info->stable_bit < 0) |
193 | return 0; |
194 | |
195 | return readl_poll_timeout(cgu->base + pll_info->reg, ctl, |
196 | ctl & BIT(pll_info->stable_bit), |
197 | 0, 100 * USEC_PER_MSEC); |
198 | } |
199 | |
200 | static int |
201 | ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate, |
202 | unsigned long parent_rate) |
203 | { |
204 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); |
205 | struct ingenic_cgu *cgu = ingenic_clk->cgu; |
206 | const struct ingenic_cgu_clk_info *clk_info = to_clk_info(clk: ingenic_clk); |
207 | const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; |
208 | unsigned long rate, flags; |
209 | unsigned int m, n, od; |
210 | int ret = 0; |
211 | u32 ctl; |
212 | |
213 | rate = ingenic_pll_calc(clk_info, rate: req_rate, parent_rate, |
214 | pm: &m, pn: &n, pod: &od); |
215 | if (rate != req_rate) |
216 | pr_info("ingenic-cgu: request '%s' rate %luHz, actual %luHz\n" , |
217 | clk_info->name, req_rate, rate); |
218 | |
219 | spin_lock_irqsave(&cgu->lock, flags); |
220 | ctl = readl(addr: cgu->base + pll_info->reg); |
221 | |
222 | ctl &= ~(GENMASK(pll_info->m_bits - 1, 0) << pll_info->m_shift); |
223 | ctl |= (m - pll_info->m_offset) << pll_info->m_shift; |
224 | |
225 | ctl &= ~(GENMASK(pll_info->n_bits - 1, 0) << pll_info->n_shift); |
226 | ctl |= (n - pll_info->n_offset) << pll_info->n_shift; |
227 | |
228 | if (pll_info->od_bits > 0) { |
229 | ctl &= ~(GENMASK(pll_info->od_bits - 1, 0) << pll_info->od_shift); |
230 | ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift; |
231 | } |
232 | |
233 | writel(val: ctl, addr: cgu->base + pll_info->reg); |
234 | |
235 | if (pll_info->set_rate_hook) |
236 | pll_info->set_rate_hook(pll_info, rate, parent_rate); |
237 | |
238 | /* If the PLL is enabled, verify that it's stable */ |
239 | if (pll_info->enable_bit >= 0 && (ctl & BIT(pll_info->enable_bit))) |
240 | ret = ingenic_pll_check_stable(cgu, pll_info); |
241 | |
242 | spin_unlock_irqrestore(lock: &cgu->lock, flags); |
243 | |
244 | return ret; |
245 | } |
246 | |
247 | static int ingenic_pll_enable(struct clk_hw *hw) |
248 | { |
249 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); |
250 | struct ingenic_cgu *cgu = ingenic_clk->cgu; |
251 | const struct ingenic_cgu_clk_info *clk_info = to_clk_info(clk: ingenic_clk); |
252 | const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; |
253 | unsigned long flags; |
254 | int ret; |
255 | u32 ctl; |
256 | |
257 | if (pll_info->enable_bit < 0) |
258 | return 0; |
259 | |
260 | spin_lock_irqsave(&cgu->lock, flags); |
261 | if (pll_info->bypass_bit >= 0) { |
262 | ctl = readl(addr: cgu->base + pll_info->bypass_reg); |
263 | |
264 | ctl &= ~BIT(pll_info->bypass_bit); |
265 | |
266 | writel(val: ctl, addr: cgu->base + pll_info->bypass_reg); |
267 | } |
268 | |
269 | ctl = readl(addr: cgu->base + pll_info->reg); |
270 | |
271 | ctl |= BIT(pll_info->enable_bit); |
272 | |
273 | writel(val: ctl, addr: cgu->base + pll_info->reg); |
274 | |
275 | ret = ingenic_pll_check_stable(cgu, pll_info); |
276 | spin_unlock_irqrestore(lock: &cgu->lock, flags); |
277 | |
278 | return ret; |
279 | } |
280 | |
281 | static void ingenic_pll_disable(struct clk_hw *hw) |
282 | { |
283 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); |
284 | struct ingenic_cgu *cgu = ingenic_clk->cgu; |
285 | const struct ingenic_cgu_clk_info *clk_info = to_clk_info(clk: ingenic_clk); |
286 | const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; |
287 | unsigned long flags; |
288 | u32 ctl; |
289 | |
290 | if (pll_info->enable_bit < 0) |
291 | return; |
292 | |
293 | spin_lock_irqsave(&cgu->lock, flags); |
294 | ctl = readl(addr: cgu->base + pll_info->reg); |
295 | |
296 | ctl &= ~BIT(pll_info->enable_bit); |
297 | |
298 | writel(val: ctl, addr: cgu->base + pll_info->reg); |
299 | spin_unlock_irqrestore(lock: &cgu->lock, flags); |
300 | } |
301 | |
302 | static int ingenic_pll_is_enabled(struct clk_hw *hw) |
303 | { |
304 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); |
305 | struct ingenic_cgu *cgu = ingenic_clk->cgu; |
306 | const struct ingenic_cgu_clk_info *clk_info = to_clk_info(clk: ingenic_clk); |
307 | const struct ingenic_cgu_pll_info *pll_info = &clk_info->pll; |
308 | u32 ctl; |
309 | |
310 | if (pll_info->enable_bit < 0) |
311 | return true; |
312 | |
313 | ctl = readl(addr: cgu->base + pll_info->reg); |
314 | |
315 | return !!(ctl & BIT(pll_info->enable_bit)); |
316 | } |
317 | |
318 | static const struct clk_ops ingenic_pll_ops = { |
319 | .recalc_rate = ingenic_pll_recalc_rate, |
320 | .round_rate = ingenic_pll_round_rate, |
321 | .set_rate = ingenic_pll_set_rate, |
322 | |
323 | .enable = ingenic_pll_enable, |
324 | .disable = ingenic_pll_disable, |
325 | .is_enabled = ingenic_pll_is_enabled, |
326 | }; |
327 | |
328 | /* |
329 | * Operations for all non-PLL clocks |
330 | */ |
331 | |
332 | static u8 ingenic_clk_get_parent(struct clk_hw *hw) |
333 | { |
334 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); |
335 | const struct ingenic_cgu_clk_info *clk_info = to_clk_info(clk: ingenic_clk); |
336 | struct ingenic_cgu *cgu = ingenic_clk->cgu; |
337 | u32 reg; |
338 | u8 i, hw_idx, idx = 0; |
339 | |
340 | if (clk_info->type & CGU_CLK_MUX) { |
341 | reg = readl(addr: cgu->base + clk_info->mux.reg); |
342 | hw_idx = (reg >> clk_info->mux.shift) & |
343 | GENMASK(clk_info->mux.bits - 1, 0); |
344 | |
345 | /* |
346 | * Convert the hardware index to the parent index by skipping |
347 | * over any -1's in the parents array. |
348 | */ |
349 | for (i = 0; i < hw_idx; i++) { |
350 | if (clk_info->parents[i] != -1) |
351 | idx++; |
352 | } |
353 | } |
354 | |
355 | return idx; |
356 | } |
357 | |
358 | static int ingenic_clk_set_parent(struct clk_hw *hw, u8 idx) |
359 | { |
360 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); |
361 | const struct ingenic_cgu_clk_info *clk_info = to_clk_info(clk: ingenic_clk); |
362 | struct ingenic_cgu *cgu = ingenic_clk->cgu; |
363 | unsigned long flags; |
364 | u8 curr_idx, hw_idx, num_poss; |
365 | u32 reg, mask; |
366 | |
367 | if (clk_info->type & CGU_CLK_MUX) { |
368 | /* |
369 | * Convert the parent index to the hardware index by adding |
370 | * 1 for any -1 in the parents array preceding the given |
371 | * index. That is, we want the index of idx'th entry in |
372 | * clk_info->parents which does not equal -1. |
373 | */ |
374 | hw_idx = curr_idx = 0; |
375 | num_poss = 1 << clk_info->mux.bits; |
376 | for (; hw_idx < num_poss; hw_idx++) { |
377 | if (clk_info->parents[hw_idx] == -1) |
378 | continue; |
379 | if (curr_idx == idx) |
380 | break; |
381 | curr_idx++; |
382 | } |
383 | |
384 | /* idx should always be a valid parent */ |
385 | BUG_ON(curr_idx != idx); |
386 | |
387 | mask = GENMASK(clk_info->mux.bits - 1, 0); |
388 | mask <<= clk_info->mux.shift; |
389 | |
390 | spin_lock_irqsave(&cgu->lock, flags); |
391 | |
392 | /* write the register */ |
393 | reg = readl(addr: cgu->base + clk_info->mux.reg); |
394 | reg &= ~mask; |
395 | reg |= hw_idx << clk_info->mux.shift; |
396 | writel(val: reg, addr: cgu->base + clk_info->mux.reg); |
397 | |
398 | spin_unlock_irqrestore(lock: &cgu->lock, flags); |
399 | return 0; |
400 | } |
401 | |
402 | return idx ? -EINVAL : 0; |
403 | } |
404 | |
405 | static unsigned long |
406 | ingenic_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) |
407 | { |
408 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); |
409 | const struct ingenic_cgu_clk_info *clk_info = to_clk_info(clk: ingenic_clk); |
410 | struct ingenic_cgu *cgu = ingenic_clk->cgu; |
411 | unsigned long rate = parent_rate; |
412 | u32 div_reg, div; |
413 | u8 parent; |
414 | |
415 | if (clk_info->type & CGU_CLK_DIV) { |
416 | parent = ingenic_clk_get_parent(hw); |
417 | |
418 | if (!(clk_info->div.bypass_mask & BIT(parent))) { |
419 | div_reg = readl(addr: cgu->base + clk_info->div.reg); |
420 | div = (div_reg >> clk_info->div.shift) & |
421 | GENMASK(clk_info->div.bits - 1, 0); |
422 | |
423 | if (clk_info->div.div_table) |
424 | div = clk_info->div.div_table[div]; |
425 | else |
426 | div = (div + 1) * clk_info->div.div; |
427 | |
428 | rate /= div; |
429 | } |
430 | } else if (clk_info->type & CGU_CLK_FIXDIV) { |
431 | rate /= clk_info->fixdiv.div; |
432 | } |
433 | |
434 | return rate; |
435 | } |
436 | |
437 | static unsigned int |
438 | ingenic_clk_calc_hw_div(const struct ingenic_cgu_clk_info *clk_info, |
439 | unsigned int div) |
440 | { |
441 | unsigned int i, best_i = 0, best = (unsigned int)-1; |
442 | |
443 | for (i = 0; i < (1 << clk_info->div.bits) |
444 | && clk_info->div.div_table[i]; i++) { |
445 | if (clk_info->div.div_table[i] >= div && |
446 | clk_info->div.div_table[i] < best) { |
447 | best = clk_info->div.div_table[i]; |
448 | best_i = i; |
449 | |
450 | if (div == best) |
451 | break; |
452 | } |
453 | } |
454 | |
455 | return best_i; |
456 | } |
457 | |
458 | static unsigned |
459 | ingenic_clk_calc_div(struct clk_hw *hw, |
460 | const struct ingenic_cgu_clk_info *clk_info, |
461 | unsigned long parent_rate, unsigned long req_rate) |
462 | { |
463 | unsigned int div, hw_div; |
464 | u8 parent; |
465 | |
466 | parent = ingenic_clk_get_parent(hw); |
467 | if (clk_info->div.bypass_mask & BIT(parent)) |
468 | return 1; |
469 | |
470 | /* calculate the divide */ |
471 | div = DIV_ROUND_UP(parent_rate, req_rate); |
472 | |
473 | if (clk_info->div.div_table) { |
474 | hw_div = ingenic_clk_calc_hw_div(clk_info, div); |
475 | |
476 | return clk_info->div.div_table[hw_div]; |
477 | } |
478 | |
479 | /* Impose hardware constraints */ |
480 | div = clamp_t(unsigned int, div, clk_info->div.div, |
481 | clk_info->div.div << clk_info->div.bits); |
482 | |
483 | /* |
484 | * If the divider value itself must be divided before being written to |
485 | * the divider register, we must ensure we don't have any bits set that |
486 | * would be lost as a result of doing so. |
487 | */ |
488 | div = DIV_ROUND_UP(div, clk_info->div.div); |
489 | div *= clk_info->div.div; |
490 | |
491 | return div; |
492 | } |
493 | |
494 | static int ingenic_clk_determine_rate(struct clk_hw *hw, |
495 | struct clk_rate_request *req) |
496 | { |
497 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); |
498 | const struct ingenic_cgu_clk_info *clk_info = to_clk_info(clk: ingenic_clk); |
499 | unsigned int div = 1; |
500 | |
501 | if (clk_info->type & CGU_CLK_DIV) |
502 | div = ingenic_clk_calc_div(hw, clk_info, parent_rate: req->best_parent_rate, |
503 | req_rate: req->rate); |
504 | else if (clk_info->type & CGU_CLK_FIXDIV) |
505 | div = clk_info->fixdiv.div; |
506 | else if (clk_hw_can_set_rate_parent(hw)) |
507 | req->best_parent_rate = req->rate; |
508 | |
509 | req->rate = DIV_ROUND_UP(req->best_parent_rate, div); |
510 | return 0; |
511 | } |
512 | |
513 | static inline int ingenic_clk_check_stable(struct ingenic_cgu *cgu, |
514 | const struct ingenic_cgu_clk_info *clk_info) |
515 | { |
516 | u32 reg; |
517 | |
518 | return readl_poll_timeout(cgu->base + clk_info->div.reg, reg, |
519 | !(reg & BIT(clk_info->div.busy_bit)), |
520 | 0, 100 * USEC_PER_MSEC); |
521 | } |
522 | |
523 | static int |
524 | ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate, |
525 | unsigned long parent_rate) |
526 | { |
527 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); |
528 | const struct ingenic_cgu_clk_info *clk_info = to_clk_info(clk: ingenic_clk); |
529 | struct ingenic_cgu *cgu = ingenic_clk->cgu; |
530 | unsigned long rate, flags; |
531 | unsigned int hw_div, div; |
532 | u32 reg, mask; |
533 | int ret = 0; |
534 | |
535 | if (clk_info->type & CGU_CLK_DIV) { |
536 | div = ingenic_clk_calc_div(hw, clk_info, parent_rate, req_rate); |
537 | rate = DIV_ROUND_UP(parent_rate, div); |
538 | |
539 | if (rate != req_rate) |
540 | return -EINVAL; |
541 | |
542 | if (clk_info->div.div_table) |
543 | hw_div = ingenic_clk_calc_hw_div(clk_info, div); |
544 | else |
545 | hw_div = ((div / clk_info->div.div) - 1); |
546 | |
547 | spin_lock_irqsave(&cgu->lock, flags); |
548 | reg = readl(addr: cgu->base + clk_info->div.reg); |
549 | |
550 | /* update the divide */ |
551 | mask = GENMASK(clk_info->div.bits - 1, 0); |
552 | reg &= ~(mask << clk_info->div.shift); |
553 | reg |= hw_div << clk_info->div.shift; |
554 | |
555 | /* clear the stop bit */ |
556 | if (clk_info->div.stop_bit != -1) |
557 | reg &= ~BIT(clk_info->div.stop_bit); |
558 | |
559 | /* set the change enable bit */ |
560 | if (clk_info->div.ce_bit != -1) |
561 | reg |= BIT(clk_info->div.ce_bit); |
562 | |
563 | /* update the hardware */ |
564 | writel(val: reg, addr: cgu->base + clk_info->div.reg); |
565 | |
566 | /* wait for the change to take effect */ |
567 | if (clk_info->div.busy_bit != -1) |
568 | ret = ingenic_clk_check_stable(cgu, clk_info); |
569 | |
570 | spin_unlock_irqrestore(lock: &cgu->lock, flags); |
571 | return ret; |
572 | } |
573 | |
574 | return -EINVAL; |
575 | } |
576 | |
577 | static int ingenic_clk_enable(struct clk_hw *hw) |
578 | { |
579 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); |
580 | const struct ingenic_cgu_clk_info *clk_info = to_clk_info(clk: ingenic_clk); |
581 | struct ingenic_cgu *cgu = ingenic_clk->cgu; |
582 | unsigned long flags; |
583 | |
584 | if (clk_info->type & CGU_CLK_GATE) { |
585 | /* ungate the clock */ |
586 | spin_lock_irqsave(&cgu->lock, flags); |
587 | ingenic_cgu_gate_set(cgu, info: &clk_info->gate, val: false); |
588 | spin_unlock_irqrestore(lock: &cgu->lock, flags); |
589 | |
590 | if (clk_info->gate.delay_us) |
591 | udelay(clk_info->gate.delay_us); |
592 | } |
593 | |
594 | return 0; |
595 | } |
596 | |
597 | static void ingenic_clk_disable(struct clk_hw *hw) |
598 | { |
599 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); |
600 | const struct ingenic_cgu_clk_info *clk_info = to_clk_info(clk: ingenic_clk); |
601 | struct ingenic_cgu *cgu = ingenic_clk->cgu; |
602 | unsigned long flags; |
603 | |
604 | if (clk_info->type & CGU_CLK_GATE) { |
605 | /* gate the clock */ |
606 | spin_lock_irqsave(&cgu->lock, flags); |
607 | ingenic_cgu_gate_set(cgu, info: &clk_info->gate, val: true); |
608 | spin_unlock_irqrestore(lock: &cgu->lock, flags); |
609 | } |
610 | } |
611 | |
612 | static int ingenic_clk_is_enabled(struct clk_hw *hw) |
613 | { |
614 | struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw); |
615 | const struct ingenic_cgu_clk_info *clk_info = to_clk_info(clk: ingenic_clk); |
616 | struct ingenic_cgu *cgu = ingenic_clk->cgu; |
617 | int enabled = 1; |
618 | |
619 | if (clk_info->type & CGU_CLK_GATE) |
620 | enabled = !ingenic_cgu_gate_get(cgu, info: &clk_info->gate); |
621 | |
622 | return enabled; |
623 | } |
624 | |
625 | static const struct clk_ops ingenic_clk_ops = { |
626 | .get_parent = ingenic_clk_get_parent, |
627 | .set_parent = ingenic_clk_set_parent, |
628 | |
629 | .recalc_rate = ingenic_clk_recalc_rate, |
630 | .determine_rate = ingenic_clk_determine_rate, |
631 | .set_rate = ingenic_clk_set_rate, |
632 | |
633 | .enable = ingenic_clk_enable, |
634 | .disable = ingenic_clk_disable, |
635 | .is_enabled = ingenic_clk_is_enabled, |
636 | }; |
637 | |
638 | /* |
639 | * Setup functions. |
640 | */ |
641 | |
642 | static int ingenic_register_clock(struct ingenic_cgu *cgu, unsigned idx) |
643 | { |
644 | const struct ingenic_cgu_clk_info *clk_info = &cgu->clock_info[idx]; |
645 | struct clk_init_data clk_init; |
646 | struct ingenic_clk *ingenic_clk = NULL; |
647 | struct clk *clk, *parent; |
648 | const char *parent_names[4]; |
649 | unsigned caps, i, num_possible; |
650 | int err = -EINVAL; |
651 | |
652 | BUILD_BUG_ON(ARRAY_SIZE(clk_info->parents) > ARRAY_SIZE(parent_names)); |
653 | |
654 | if (clk_info->type == CGU_CLK_EXT) { |
655 | clk = of_clk_get_by_name(np: cgu->np, name: clk_info->name); |
656 | if (IS_ERR(ptr: clk)) { |
657 | pr_err("%s: no external clock '%s' provided\n" , |
658 | __func__, clk_info->name); |
659 | err = -ENODEV; |
660 | goto out; |
661 | } |
662 | err = clk_register_clkdev(clk, clk_info->name, NULL); |
663 | if (err) { |
664 | clk_put(clk); |
665 | goto out; |
666 | } |
667 | cgu->clocks.clks[idx] = clk; |
668 | return 0; |
669 | } |
670 | |
671 | if (!clk_info->type) { |
672 | pr_err("%s: no clock type specified for '%s'\n" , __func__, |
673 | clk_info->name); |
674 | goto out; |
675 | } |
676 | |
677 | ingenic_clk = kzalloc(size: sizeof(*ingenic_clk), GFP_KERNEL); |
678 | if (!ingenic_clk) { |
679 | err = -ENOMEM; |
680 | goto out; |
681 | } |
682 | |
683 | ingenic_clk->hw.init = &clk_init; |
684 | ingenic_clk->cgu = cgu; |
685 | ingenic_clk->idx = idx; |
686 | |
687 | clk_init.name = clk_info->name; |
688 | clk_init.flags = clk_info->flags; |
689 | clk_init.parent_names = parent_names; |
690 | |
691 | caps = clk_info->type; |
692 | |
693 | if (caps & CGU_CLK_DIV) { |
694 | caps &= ~CGU_CLK_DIV; |
695 | } else if (!(caps & CGU_CLK_CUSTOM)) { |
696 | /* pass rate changes to the parent clock */ |
697 | clk_init.flags |= CLK_SET_RATE_PARENT; |
698 | } |
699 | |
700 | if (caps & (CGU_CLK_MUX | CGU_CLK_CUSTOM)) { |
701 | clk_init.num_parents = 0; |
702 | |
703 | if (caps & CGU_CLK_MUX) |
704 | num_possible = 1 << clk_info->mux.bits; |
705 | else |
706 | num_possible = ARRAY_SIZE(clk_info->parents); |
707 | |
708 | for (i = 0; i < num_possible; i++) { |
709 | if (clk_info->parents[i] == -1) |
710 | continue; |
711 | |
712 | parent = cgu->clocks.clks[clk_info->parents[i]]; |
713 | parent_names[clk_init.num_parents] = |
714 | __clk_get_name(clk: parent); |
715 | clk_init.num_parents++; |
716 | } |
717 | |
718 | BUG_ON(!clk_init.num_parents); |
719 | BUG_ON(clk_init.num_parents > ARRAY_SIZE(parent_names)); |
720 | } else { |
721 | BUG_ON(clk_info->parents[0] == -1); |
722 | clk_init.num_parents = 1; |
723 | parent = cgu->clocks.clks[clk_info->parents[0]]; |
724 | parent_names[0] = __clk_get_name(clk: parent); |
725 | } |
726 | |
727 | if (caps & CGU_CLK_CUSTOM) { |
728 | clk_init.ops = clk_info->custom.clk_ops; |
729 | |
730 | caps &= ~CGU_CLK_CUSTOM; |
731 | |
732 | if (caps) { |
733 | pr_err("%s: custom clock may not be combined with type 0x%x\n" , |
734 | __func__, caps); |
735 | goto out; |
736 | } |
737 | } else if (caps & CGU_CLK_PLL) { |
738 | clk_init.ops = &ingenic_pll_ops; |
739 | |
740 | caps &= ~CGU_CLK_PLL; |
741 | |
742 | if (caps) { |
743 | pr_err("%s: PLL may not be combined with type 0x%x\n" , |
744 | __func__, caps); |
745 | goto out; |
746 | } |
747 | } else { |
748 | clk_init.ops = &ingenic_clk_ops; |
749 | } |
750 | |
751 | /* nothing to do for gates or fixed dividers */ |
752 | caps &= ~(CGU_CLK_GATE | CGU_CLK_FIXDIV); |
753 | |
754 | if (caps & CGU_CLK_MUX) { |
755 | if (!(caps & CGU_CLK_MUX_GLITCHFREE)) |
756 | clk_init.flags |= CLK_SET_PARENT_GATE; |
757 | |
758 | caps &= ~(CGU_CLK_MUX | CGU_CLK_MUX_GLITCHFREE); |
759 | } |
760 | |
761 | if (caps) { |
762 | pr_err("%s: unknown clock type 0x%x\n" , __func__, caps); |
763 | goto out; |
764 | } |
765 | |
766 | clk = clk_register(NULL, hw: &ingenic_clk->hw); |
767 | if (IS_ERR(ptr: clk)) { |
768 | pr_err("%s: failed to register clock '%s'\n" , __func__, |
769 | clk_info->name); |
770 | err = PTR_ERR(ptr: clk); |
771 | goto out; |
772 | } |
773 | |
774 | err = clk_register_clkdev(clk, clk_info->name, NULL); |
775 | if (err) |
776 | goto out; |
777 | |
778 | cgu->clocks.clks[idx] = clk; |
779 | out: |
780 | if (err) |
781 | kfree(objp: ingenic_clk); |
782 | return err; |
783 | } |
784 | |
785 | struct ingenic_cgu * |
786 | ingenic_cgu_new(const struct ingenic_cgu_clk_info *clock_info, |
787 | unsigned num_clocks, struct device_node *np) |
788 | { |
789 | struct ingenic_cgu *cgu; |
790 | |
791 | cgu = kzalloc(size: sizeof(*cgu), GFP_KERNEL); |
792 | if (!cgu) |
793 | goto err_out; |
794 | |
795 | cgu->base = of_iomap(node: np, index: 0); |
796 | if (!cgu->base) { |
797 | pr_err("%s: failed to map CGU registers\n" , __func__); |
798 | goto err_out_free; |
799 | } |
800 | |
801 | cgu->np = np; |
802 | cgu->clock_info = clock_info; |
803 | cgu->clocks.clk_num = num_clocks; |
804 | |
805 | spin_lock_init(&cgu->lock); |
806 | |
807 | return cgu; |
808 | |
809 | err_out_free: |
810 | kfree(objp: cgu); |
811 | err_out: |
812 | return NULL; |
813 | } |
814 | |
815 | int ingenic_cgu_register_clocks(struct ingenic_cgu *cgu) |
816 | { |
817 | unsigned i; |
818 | int err; |
819 | |
820 | cgu->clocks.clks = kcalloc(n: cgu->clocks.clk_num, size: sizeof(struct clk *), |
821 | GFP_KERNEL); |
822 | if (!cgu->clocks.clks) { |
823 | err = -ENOMEM; |
824 | goto err_out; |
825 | } |
826 | |
827 | for (i = 0; i < cgu->clocks.clk_num; i++) { |
828 | err = ingenic_register_clock(cgu, idx: i); |
829 | if (err) |
830 | goto err_out_unregister; |
831 | } |
832 | |
833 | err = of_clk_add_provider(np: cgu->np, clk_src_get: of_clk_src_onecell_get, |
834 | data: &cgu->clocks); |
835 | if (err) |
836 | goto err_out_unregister; |
837 | |
838 | return 0; |
839 | |
840 | err_out_unregister: |
841 | for (i = 0; i < cgu->clocks.clk_num; i++) { |
842 | if (!cgu->clocks.clks[i]) |
843 | continue; |
844 | if (cgu->clock_info[i].type & CGU_CLK_EXT) |
845 | clk_put(clk: cgu->clocks.clks[i]); |
846 | else |
847 | clk_unregister(clk: cgu->clocks.clks[i]); |
848 | } |
849 | kfree(objp: cgu->clocks.clks); |
850 | err_out: |
851 | return err; |
852 | } |
853 | |