1 /* Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
2 *
3 * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
4 *the the People's Republic of China and other countries.
5 * All Allwinner Technology Co.,Ltd. trademarks are used with permission.
6 *
7 * DISCLAIMER
8 * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
9 * IF YOU NEED TO INTEGRATE THIRD PARTY’S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
10 * IN ALLWINNERS’SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
11 * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
12 * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
13 * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
14 * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY’S TECHNOLOGY.
15 *
16 *
17 * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
18 * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
19 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
20 * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
21 * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22 * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "sunxi_hal_common.h"
33 #include "clk_periph.h"
34
35 #define NEW_RATE_CALCULATE 1
36
sunxi_clk_periph_get_parent(clk_periph_pt clk,u8 * parent_index)37 hal_clk_status_t sunxi_clk_periph_get_parent(clk_periph_pt clk, u8 *parent_index)
38 {
39 u32 reg = 0;
40 struct sunxi_clk_periph *periph = clk->config;
41
42 CCMU_TRACE();
43 if (!periph->mux.reg)
44 {
45 *parent_index = 0;
46 return HAL_CLK_STATUS_OK;
47 }
48
49 //if (periph->lock)
50 // spin_lock_irqsave(periph->lock, flags);
51
52 reg = readl(periph->mux.reg);
53 *parent_index = GET_BITS(periph->mux.shift, periph->mux.width, reg);
54
55 //if (periph->lock)
56 // spin_unlock_irqrestore(periph->lock, flags);
57
58 CCMU_DBG("parent index %d \n", (*parent_index));
59 return HAL_CLK_STATUS_OK;
60 }
61
62
sunxi_clk_periph_set_parent(clk_periph_pt clk,u8 index)63 hal_clk_status_t sunxi_clk_periph_set_parent(clk_periph_pt clk, u8 index)
64 {
65 unsigned long reg, flags = 0;
66 struct sunxi_clk_periph *periph = clk->config;
67
68 //if (periph->flags & CLK_READONLY)
69 // return 0;
70
71 if (!periph->mux.reg)
72 {
73 return HAL_CLK_STATUS_OK;
74 }
75
76 //if (periph->lock)
77 // spin_lock_irqsave(periph->lock, flags);
78
79 reg = readl(periph->mux.reg);
80 reg = SET_BITS(periph->mux.shift, periph->mux.width, reg, index);
81 writel(reg, periph->mux.reg);
82
83 //if (periph->lock)
84 // spin_unlock_irqrestore(periph->lock, flags);
85
86 return HAL_CLK_STATUS_OK;
87 }
88
__sunxi_clk_periph_enable_shared(struct sunxi_clk_periph * periph)89 hal_clk_status_t __sunxi_clk_periph_enable_shared(struct sunxi_clk_periph *periph)
90 {
91 unsigned long reg;
92 struct sunxi_clk_periph_gate *gate = &periph->gate;
93
94 if (!periph->com_gate->val)
95 {
96 /* de-assert module */
97 if (gate->reset && IS_SHARE_RST_GATE(periph))
98 {
99 reg = readl(gate->reset);
100 reg = SET_BITS(gate->rst_shift, 1, reg, 1);
101 writel(reg, gate->reset);
102 }
103 /* enable bus gating */
104 if (gate->bus && IS_SHARE_BUS_GATE(periph))
105 {
106 reg = readl(gate->bus);
107 reg = SET_BITS(gate->bus_shift, 1, reg, 1);
108 writel(reg, gate->bus);
109 }
110
111 /* enable module gating */
112 if (gate->enable && IS_SHARE_MOD_GATE(periph))
113 {
114 reg = readl(gate->enable);
115 reg = SET_BITS(gate->enb_shift, 1, reg, 1);
116 writel(reg, gate->enable);
117 }
118
119 /* enable dram gating */
120 if (gate->dram && IS_SHARE_MBUS_GATE(periph))
121 {
122 reg = readl(gate->dram);
123 reg = SET_BITS(gate->ddr_shift, 1, reg, 1);
124 writel(reg, gate->dram);
125 }
126 }
127 periph->com_gate->val |= 1 << periph->com_gate_off;
128
129 return HAL_CLK_STATUS_OK;
130 }
131
__sunxi_clk_periph_enable(struct sunxi_clk_periph * periph)132 hal_clk_status_t __sunxi_clk_periph_enable(struct sunxi_clk_periph *periph)
133 {
134 u32 reg;
135 struct sunxi_clk_periph_gate *gate = &periph->gate;
136
137 /* de-assert module */
138 if (gate->reset && !IS_SHARE_RST_GATE(periph))
139 {
140 reg = readl(gate->reset);
141 reg = SET_BITS(gate->rst_shift, 1, reg, 1);
142 writel(reg, gate->reset);
143 }
144
145 /* enable bus gating */
146 if (gate->bus && !IS_SHARE_BUS_GATE(periph))
147 {
148 reg = readl(gate->bus);
149 reg = SET_BITS(gate->bus_shift, 1, reg, 1);
150 writel(reg, gate->bus);
151 }
152
153 /* enable module gating */
154 if (gate->enable && !IS_SHARE_MOD_GATE(periph))
155 {
156 reg = readl(gate->enable);
157 //if (periph->flags & CLK_REVERT_ENABLE)
158 // reg = SET_BITS(gate->enb_shift, 1, reg, 0);
159 //else
160 reg = SET_BITS(gate->enb_shift, 1, reg, 1);
161 writel(reg, gate->enable);
162 }
163
164 /* enable dram gating */
165 if (gate->dram && !IS_SHARE_MBUS_GATE(periph))
166 {
167 reg = readl(gate->dram);
168 reg = SET_BITS(gate->ddr_shift, 1, reg, 1);
169 writel(reg, gate->dram);
170 }
171
172 return HAL_CLK_STATUS_OK;
173 }
174
sunxi_clk_periph_enable(clk_periph_pt clk)175 hal_clk_status_t sunxi_clk_periph_enable(clk_periph_pt clk)
176 {
177 //unsigned long flags = 0;
178 hal_clk_status_t ret = 0;
179 struct sunxi_clk_periph *periph = NULL;
180
181 //if (periph->flags & CLK_READONLY)
182 // return 0;
183 periph = clk->config;
184
185 //if (periph->lock)
186 // spin_lock_irqsave(periph->lock, flags);
187
188 /* if common gate exist, enable it first */
189 if (periph->com_gate)
190 {
191 ret = __sunxi_clk_periph_enable_shared(periph);
192 }
193 if (!ret)
194 {
195 ret = __sunxi_clk_periph_enable(periph);
196 }
197
198 //if (periph->lock)
199 // spin_unlock_irqrestore(periph->lock, flags);
200
201 return ret == HAL_CLK_STATUS_OK ? \
202 HAL_CLK_STATUS_ENABLED : HAL_CLK_STATUS_ERROR_CLK_ENABLED_FAILED;
203 }
204
sunxi_clk_periph_is_enabled(clk_periph_pt clk)205 hal_clk_status_t sunxi_clk_periph_is_enabled(clk_periph_pt clk)
206 {
207 u32 state = 0, reg = 0;
208 //unsigned long flags = 0;
209 struct sunxi_clk_periph *periph = NULL;
210 struct sunxi_clk_periph_gate *gate = NULL;
211
212 periph = clk->config;
213 gate = &periph->gate;
214 //if (periph->lock)
215 // spin_lock_irqsave(periph->lock, flags);
216
217 /* enable bus gating */
218 if (gate->bus)
219 {
220 reg = readl(gate->bus);
221 state &= GET_BITS(gate->bus_shift, 1, reg);
222 }
223
224 /* enable module gating */
225 if (gate->enable)
226 {
227 reg = readl(gate->enable);
228 state &= GET_BITS(gate->enb_shift, 1, reg);
229 }
230
231 /* de-assert module */
232 if (gate->reset)
233 {
234 reg = readl(gate->reset);
235 state &= GET_BITS(gate->rst_shift, 1, reg);
236 }
237
238 /* enable dram gating */
239 if (gate->dram)
240 {
241 reg = readl(gate->dram);
242 state &= GET_BITS(gate->ddr_shift, 1, reg);
243 }
244
245 //if (periph->lock)
246 // spin_unlock_irqrestore(periph->lock, flags);
247
248 return state ? HAL_CLK_STATUS_ENABLED : HAL_CLK_STATUS_DISABLED;
249 }
250
__sunxi_clk_periph_disable_shared(struct sunxi_clk_periph * periph)251 static void __sunxi_clk_periph_disable_shared(struct sunxi_clk_periph *periph)
252 {
253 unsigned long reg;
254 struct sunxi_clk_periph_gate *gate = &periph->gate;
255
256 if (!periph->com_gate->val)
257 {
258 return;
259 }
260
261 periph->com_gate->val &= ~(1 << periph->com_gate_off);
262
263 if (!periph->com_gate->val)
264 {
265 /* disable dram gating */
266 if (gate->dram && IS_SHARE_MBUS_GATE(periph))
267 {
268 reg = readl(gate->dram);
269 reg = SET_BITS(gate->ddr_shift, 1, reg, 0);
270 writel(reg, gate->dram);
271 }
272
273 /* disable module gating */
274 if (gate->enable && IS_SHARE_MOD_GATE(periph))
275 {
276 reg = readl(gate->enable);
277 reg = SET_BITS(gate->enb_shift, 1, reg, 0);
278 writel(reg, gate->enable);
279 }
280
281 /* disable bus gating */
282 if (gate->bus && IS_SHARE_BUS_GATE(periph))
283 {
284 reg = readl(gate->bus);
285 reg = SET_BITS(gate->bus_shift, 1, reg, 0);
286 writel(reg, gate->bus);
287 }
288
289 /* assert module */
290 if (gate->reset && IS_SHARE_RST_GATE(periph))
291 {
292 reg = readl(gate->reset);
293 reg = SET_BITS(gate->rst_shift, 1, reg, 0);
294 writel(reg, gate->reset);
295 }
296 }
297
298 }
299
__sunxi_clk_periph_disable(struct sunxi_clk_periph * periph)300 static void __sunxi_clk_periph_disable(struct sunxi_clk_periph *periph)
301 {
302 u32 reg;
303 struct sunxi_clk_periph_gate *gate = &periph->gate;
304
305 /* disable dram gating */
306 if (gate->dram && !IS_SHARE_MBUS_GATE(periph))
307 {
308 reg = readl(gate->dram);
309 reg = SET_BITS(gate->ddr_shift, 1, reg, 0);
310 writel(reg, gate->dram);
311 }
312
313 /* disable module gating */
314 if (gate->enable && !IS_SHARE_MOD_GATE(periph))
315 {
316 reg = readl(gate->enable);
317 //if (periph->flags & CLK_REVERT_ENABLE)
318 // reg = SET_BITS(gate->enb_shift, 1, reg, 1);
319 //else
320 reg = SET_BITS(gate->enb_shift, 1, reg, 0);
321
322 writel(reg, gate->enable);
323 }
324
325 /* disable bus gating */
326 if (gate->bus && !IS_SHARE_BUS_GATE(periph))
327 {
328 reg = readl(gate->bus);
329 reg = SET_BITS(gate->bus_shift, 1, reg, 0);
330 writel(reg, gate->bus);
331 }
332
333 /* assert module */
334 if (gate->reset && !IS_SHARE_RST_GATE(periph))
335 {
336 reg = readl(gate->reset);
337 reg = SET_BITS(gate->rst_shift, 1, reg, 0);
338 writel(reg, gate->reset);
339 }
340 }
341
sunxi_clk_periph_disable(clk_periph_pt clk)342 hal_clk_status_t sunxi_clk_periph_disable(clk_periph_pt clk)
343 {
344 //unsigned long flags = 0;
345 struct sunxi_clk_periph *periph = NULL;
346
347 //if (periph->flags & CLK_READONLY)
348 // return 0;
349 periph = clk->config;
350
351 //if (periph->lock)
352 // spin_lock_irqsave(periph->lock, flags);
353
354 __sunxi_clk_periph_disable(periph);
355
356 /* if common gate exist, disable it */
357 if (periph->com_gate)
358 {
359 __sunxi_clk_periph_disable_shared(periph);
360 }
361
362 //if (periph->lock)
363 // spin_unlock_irqrestore(periph->lock, flags);
364 return HAL_CLK_STATUS_DISABLED;
365 }
366
sunxi_clk_periph_recalc_rate(clk_periph_pt clk,u32 * rate)367 hal_clk_status_t sunxi_clk_periph_recalc_rate(clk_periph_pt clk, u32 *rate)
368 {
369 u32 reg = 0, parent_rate = 0;
370 u64 div, div_m = 0, div_n = 0, clk_rate = 0;
371 hal_clk_status_t ret = HAL_CLK_STATUS_OK;
372 struct sunxi_clk_periph *periph = NULL;
373 struct sunxi_clk_periph_div *divider = NULL;
374
375 periph = clk->config;
376 divider = &periph->divider;
377 parent_rate = clk->clk_core.parent_rate;
378 clk_rate = (u64)parent_rate;
379 if (!divider->reg)
380 {
381 *rate = parent_rate;
382 return ret;
383 }
384
385 //if (periph->lock)
386 // spin_lock_irqsave(periph->lock, flags);
387
388 reg = readl(divider->reg);
389 if (divider->mwidth)
390 {
391 div_m = GET_BITS(divider->mshift, divider->mwidth, reg);
392 }
393 if (divider->nwidth)
394 {
395 div_n = GET_BITS(divider->nshift, divider->nwidth, reg);
396 }
397 div = (div_m + 1) * (1 << div_n);
398 do_div(clk_rate, div);
399
400 //if (periph->lock)
401 // spin_unlock_irqrestore(periph->lock, flags);
402 *rate = (u32)clk_rate;
403 return ret;
404 }
405
406
sunxi_clk_periph_round_rate(clk_periph_pt clk,u32 rate,u32 prate)407 u32 sunxi_clk_periph_round_rate(clk_periph_pt clk, u32 rate, u32 prate)
408 {
409 struct sunxi_clk_periph *periph = NULL;
410 struct sunxi_clk_periph_div *divider = NULL;
411
412 u32 i = 0, round_rate = 0;
413 u64 div, div_m = 0, div_n = 0;
414 u64 factor_m = 0, factor_n = 0, found = 0;
415
416 CCMU_TRACE();
417 periph = clk->config;
418 divider = &periph->divider;
419
420 round_rate = (prate + rate / 2 - 1);
421 if (!rate)
422 {
423 return 0;
424 }
425
426 do_div(round_rate, rate);
427 div = round_rate;
428 if (!div)
429 {
430 return prate;
431 }
432
433 round_rate = prate;
434 div_m = 1 << divider->mwidth;
435 if (divider->nwidth)
436 {
437 div_n = 1 << divider->nwidth;
438 div_n = 1 << (div_n - 1);
439 }
440 else
441 {
442 div_n = 1;
443 }
444
445 /* NEW_RATE_CALCULATE */
446 while (i < (1 << divider->nwidth))
447 {
448 if (div <= div_m)
449 {
450 factor_m = div - 1;
451 factor_n = i;
452 do_div(round_rate, (factor_m + 1) * (1 << factor_n));
453 found = 1;
454 break;
455 }
456 div = div >> 1;
457 i++;
458
459 if (!div)
460 {
461 factor_m = 0;
462 factor_n = i;
463 do_div(round_rate, (factor_m + 1) * (1 << factor_n));
464 found = 1;
465 break;
466 }
467 }
468 if (!found)
469 {
470 factor_m = (div > div_m ? div_m : div) - 1;
471 factor_n = (1 << divider->nwidth) - 1;
472 do_div(round_rate, (factor_m + 1) * (1 << factor_n));
473 }
474
475 CCMU_DBG("parent rate %dHZ, target rate %dHZ, round rate %dHZ\n", prate, rate, round_rate);
476 CCMU_TRACE();
477 return round_rate;
478 }
479
sunxi_clk_periph_set_rate(clk_periph_pt clk,u32 rate)480 hal_clk_status_t sunxi_clk_periph_set_rate(clk_periph_pt clk, u32 rate)
481 {
482 u32 i = 0, factor_m = 0, factor_n = 0, found = 0, parent_rate = 0;
483 u32 reg = 0;
484 struct sunxi_clk_periph *periph = NULL;
485 struct sunxi_clk_periph_div *divider = NULL;
486 u32 div, div_m = 0, div_n = 0;
487
488 CCMU_TRACE();
489 periph = clk->config;
490 divider = &periph->divider;
491 parent_rate = clk->clk_core.parent_rate;
492
493 CCMU_DBG("parent rate %dHZ, set rate %dHZ\n", parent_rate, rate);
494 u64 tmp_rate = parent_rate;
495 do_div(tmp_rate, rate);
496 div = tmp_rate;
497
498 if (!div)
499 {
500 div_m = div_n = 0;
501 }
502 else
503 {
504 div_m = 1 << divider->mwidth;
505 div_n = (1 << divider->nwidth) - 1;
506
507 if (div > (div_m << div_n))
508 {
509 //WARN(1, "clk %s rate is too large : %lu\n", hw->init->name, rate);
510 div = div_m << div_n;
511 }
512 found = 0;
513 while (i < (1 << divider->nwidth))
514 {
515 if (div <= div_m)
516 {
517 factor_m = div - 1;
518 factor_n = i;
519 found = 1;
520 break;
521 }
522 div = div >> 1;
523 i++;
524 if (!div)
525 {
526 factor_m = 0;
527 factor_n = i;
528 found = 1;
529 break;
530 }
531 }
532
533 if (!found)
534 {
535 factor_m = (div > div_m ? div_m : div) - 1;
536 factor_n = (1 << divider->nwidth) - 1;
537 }
538 div_m = factor_m;
539 div_n = factor_n;
540 }
541
542 CCMU_DBG("divider->reg %d divider->mwidth %d divider->nshift %d \n", divider->reg, divider->mwidth, divider->nshift);
543 if (!divider->reg)
544 {
545 return HAL_CLK_STATUS_OK;
546 }
547 reg = readl(divider->reg);
548 if (divider->mwidth)
549 {
550 reg = SET_BITS(divider->mshift, divider->mwidth, reg, div_m);
551 }
552 if (divider->nwidth)
553 {
554 reg = SET_BITS(divider->nshift, divider->nwidth, reg, div_n);
555 }
556 writel(reg, divider->reg);
557
558 CCMU_TRACE();
559 return HAL_CLK_STATUS_OK;
560 }
561