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