1 /* Copyright (c) 2023, Canaan Bright Sight Co., Ltd
2  *
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5  * 1. Redistributions of source code must retain the above copyright
6  * notice, this list of conditions and the following disclaimer.
7  * 2. Redistributions in binary form must reproduce the above copyright
8  * notice, this list of conditions and the following disclaimer in the
9  * documentation and/or other materials provided with the distribution.
10  *
11  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
12  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
13  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
15  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
16  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
17  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
18  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include <rtthread.h>
27 #include <rthw.h>
28 #include <stdio.h>
29 #include "sysctl_pwr.h"
30 #include "drv_hardlock.h"
31 #include "ioremap.h"
32 #include "board.h"
33 
34 volatile sysctl_pwr_s* sysctl_pwr = (volatile sysctl_pwr_s*)PWR_BASE_ADDR;
35 
36 /*****************************************************************************************
37 *                                SET POWER DOMAIN'S TIME
38 *   powerdomain:power domain
39 *   timtype: idleReq_to_idleAck, idleAck_to_idle.....
40 *   tim_value: ack_to_tim, idle_to_tim......
41 *****************************************************************************************/
42 
43 /* ack timeout value, lpi idleReq to idleAck */
sysctl_pwr_set_ack_to_tim(volatile uint32_t * reg,uint32_t ack_to_tim)44 bool sysctl_pwr_set_ack_to_tim(volatile uint32_t *reg, uint32_t ack_to_tim)
45 {
46     volatile uint32_t *ret = reg;
47 
48     if((NULL == reg) || (ack_to_tim > 0x1F))
49         return false;
50     else
51     {
52         *ret &= 0xffffffe0;
53         *reg = (*ret) | (ack_to_tim << 0);
54 
55         return true;
56     }
57 }
58 
59 /* idle timeout value, lpi idleAck to idle */
sysctl_pwr_set_idle_to_tim(volatile uint32_t * reg,uint32_t idle_to_tim)60 bool sysctl_pwr_set_idle_to_tim(volatile uint32_t *reg, uint32_t idle_to_tim)
61 {
62     volatile uint32_t *ret = reg;
63 
64     if((NULL == reg) || (idle_to_tim > 0x1F))
65         return false;
66     else
67     {
68         *ret &= 0xffffe0ff;
69         *reg = (*ret) | (idle_to_tim << 8);
70 
71         return true;
72     }
73 }
74 
75 /* NOC power controller in idle min time, idle to idelReq(inactive) */
sysctl_pwr_set_idle_hd_tim(volatile uint32_t * reg,uint32_t idle_hd_tim)76 bool sysctl_pwr_set_idle_hd_tim(volatile uint32_t *reg, uint32_t idle_hd_tim)
77 {
78     volatile uint32_t *ret = reg;
79 
80     if((NULL == reg) || (idle_hd_tim > 0x3F))
81         return false;
82     else
83     {
84         *ret &= 0xffc0ffff;
85         *reg = (*ret) | (idle_hd_tim << 16);
86 
87         return true;
88     }
89 }
90 
91 /*
92  * After turning ISO on/off, you need to wait for a while to ensure that the
93  * isolation cells in the power domain are actually enabled/disabled.
94  */
sysctl_pwr_set_iso_su_tim(volatile uint32_t * reg,uint32_t iso_su_tim)95 bool sysctl_pwr_set_iso_su_tim(volatile uint32_t *reg, uint32_t iso_su_tim)
96 {
97     volatile uint32_t *ret = reg;
98 
99     if((NULL == reg) || (iso_su_tim > 0xF))
100         return false;
101     else
102     {
103         *ret &= 0xfffffff0;
104         *reg = (*ret) | (iso_su_tim << 0);
105 
106         return true;
107     }
108 }
109 
110 /*
111  * After powering off a power domain, it takes some time to exit the
112  * power-off state.
113  */
sysctl_pwr_set_pd_hd_tim(volatile uint32_t * reg,uint32_t pd_hd_tim)114 bool sysctl_pwr_set_pd_hd_tim(volatile uint32_t *reg, uint32_t pd_hd_tim)
115 {
116     volatile uint32_t *ret = reg;
117 
118     if((NULL == reg) || (pd_hd_tim > 0xFF))
119         return false;
120     else
121     {
122         *ret &= 0xfffff00f;
123         *reg = (*ret) | (pd_hd_tim << 4);
124 
125         return true;
126     }
127 }
128 
129 /*
130  * After restoring the power supply of a power domain (bringup), you need to
131  * wait for a period of time to ensure that the power supply of the power domain
132  * is fully restored.
133  */
sysctl_pwr_set_pwr_su_tim(volatile uint32_t * reg,uint32_t pwr_su_tim)134 bool sysctl_pwr_set_pwr_su_tim(volatile uint32_t *reg, uint32_t pwr_su_tim)
135 {
136     volatile uint32_t *ret = reg;
137 
138     if((NULL == reg) || (pwr_su_tim > 0xFF))
139         return false;
140     else
141     {
142         *ret &= 0xfff00fff;
143         *reg = (*ret) | (pwr_su_tim << 12);
144 
145         return true;
146     }
147 }
148 
149 /* set cpu1 wait for interrupt time */
sysctl_pwr_set_wfi_tim(volatile uint32_t * reg,uint32_t wfi_tim)150 bool sysctl_pwr_set_wfi_tim(volatile uint32_t *reg, uint32_t wfi_tim)
151 {
152     volatile uint32_t *ret = reg;
153 
154     if((NULL == reg) || (wfi_tim > 0xFFF))
155         return false;
156     else
157     {
158         *ret &= 0x000fffff;
159         *reg = (*ret) | (wfi_tim << 20);
160 
161         return true;
162     }
163 }
164 
sysctl_pwr_set_tim(sysctl_pwr_domain_e powerdomain,sysctl_pwr_tim_e timtype,uint32_t tim_value)165 bool sysctl_pwr_set_tim(sysctl_pwr_domain_e powerdomain, sysctl_pwr_tim_e timtype, uint32_t tim_value)
166 {
167     volatile uint32_t *pwr_reg = NULL;
168     volatile uint32_t *lpi_reg = NULL;
169     volatile uint32_t *wfi_reg = NULL;
170 
171     if((powerdomain >= SYSCTL_PD_MAX) || (timtype >= SYSCTL_PWR_MAX_TIM))
172         return false;
173 
174     switch(powerdomain)
175     {
176         case SYSCTL_PD_CPU1:
177         {
178             pwr_reg = (volatile uint32_t *)&sysctl_pwr->cpu1_pwr_tim;
179             lpi_reg = (volatile uint32_t *)&sysctl_pwr->cpu1_lpi_tim;
180             wfi_reg = (volatile uint32_t *)&sysctl_pwr->cpu1_pwr_tim;
181             break;
182         }
183         case SYSCTL_PD_AI:
184         {
185             pwr_reg = (volatile uint32_t *)&sysctl_pwr->ai_pwr_tim;
186             lpi_reg = (volatile uint32_t *)&sysctl_pwr->ai_lpi_tim;
187             break;
188         }
189         case SYSCTL_PD_DISP:
190         {
191             pwr_reg = (volatile uint32_t *)&sysctl_pwr->disp_pwr_tim;
192             lpi_reg = (volatile uint32_t *)&sysctl_pwr->disp_lpi_tim;
193             break;
194         }
195         case SYSCTL_PD_VPU:
196         {
197             pwr_reg = (volatile uint32_t *)&sysctl_pwr->vpu_pwr_tim;
198             lpi_reg = (volatile uint32_t *)&sysctl_pwr->vpu_lpi_tim;
199             break;
200         }
201         case SYSCTL_PD_DPU:
202         {
203             pwr_reg = (volatile uint32_t *)&sysctl_pwr->dpu_pwr_tim;
204             lpi_reg = (volatile uint32_t *)&sysctl_pwr->dpu_lpi_tim;
205             break;
206         }
207         default:
208             return false;
209     }
210 
211     switch(timtype)
212     {
213         case SYSCTL_PWR_ACK_TO_TIM:
214             return sysctl_pwr_set_ack_to_tim(pwr_reg, tim_value);
215         case SYSCTL_PWR_IDLE_TO_TIM:
216             return sysctl_pwr_set_idle_to_tim(pwr_reg, tim_value);
217         case SYSCTL_PWR_IDLE_HD_TIM:
218             return sysctl_pwr_set_idle_hd_tim(pwr_reg, tim_value);
219         case SYSCTL_PWR_ISO_SU_TIM:
220             return sysctl_pwr_set_iso_su_tim(lpi_reg, tim_value);
221         case SYSCTL_PWR_PD_HD_TIM:
222             return sysctl_pwr_set_pd_hd_tim(lpi_reg, tim_value);
223         case SYSCTL_PWR_SU_TIM:
224             return sysctl_pwr_set_pwr_su_tim(lpi_reg, tim_value);
225         case SYSCTL_PWR_WFI_TIM:
226             return sysctl_pwr_set_wfi_tim(wfi_reg,tim_value);
227         default:
228             return false;
229     }
230 }
231 
232 /*****************************************************************************************
233 *                                GET POWER DOMAIN'S TIME
234 *   powerdomain:power domain
235 *   timtype: idleReq_to_idleAck, idleAck_to_idle.....
236 *   tim_value: ack_to_tim, idle_to_tim......
237 *****************************************************************************************/
238 
239 /* ack timeout value, lpi idleReq to idleAck */
sysctl_pwr_get_ack_to_tim(volatile uint32_t * reg,uint32_t * ack_to_tim)240 bool sysctl_pwr_get_ack_to_tim(volatile uint32_t *reg, uint32_t *ack_to_tim)
241 {
242     if((NULL == reg) || (NULL == ack_to_tim))
243         return false;
244 
245     *ack_to_tim = ((*reg) >> 0) & 0x1F;
246     return true;
247 }
248 
249 /* idle timeout value, lpi idleAck to idle */
sysctl_pwr_get_idle_to_tim(volatile uint32_t * reg,uint32_t * idle_to_tim)250 bool sysctl_pwr_get_idle_to_tim(volatile uint32_t *reg, uint32_t *idle_to_tim)
251 {
252     if((NULL == reg) || (NULL == idle_to_tim))
253         return false;
254 
255     *idle_to_tim = ((*reg) >> 8) & 0x1F;
256     return true;
257 }
258 
259 /* NOC power controller in idle min time, idle to idelReq(inactive) */
sysctl_pwr_get_idle_hd_tim(volatile uint32_t * reg,uint32_t * idle_hd_tim)260 bool sysctl_pwr_get_idle_hd_tim(volatile uint32_t *reg, uint32_t *idle_hd_tim)
261 {
262     if((NULL == reg) || (NULL == idle_hd_tim))
263         return false;
264 
265     *idle_hd_tim = ((*reg) >> 16) & 0x3F;
266     return true;
267 }
268 
269 /*
270  * After turning ISO on/off, you need to wait for a while to ensure that the
271  * isolation cells in the power domain are actually enabled/disabled.
272  */
sysctl_pwr_get_iso_su_tim(volatile uint32_t * reg,uint32_t * iso_su_tim)273 bool sysctl_pwr_get_iso_su_tim(volatile uint32_t *reg, uint32_t *iso_su_tim)
274 {
275     if((NULL == reg) || (NULL == iso_su_tim))
276         return false;
277 
278     *iso_su_tim = ((*reg) >> 0) & 0xF;
279     return true;
280 }
281 
282 /*
283  * After powering off a power domain, it takes some time to exit the power-off
284  * state.
285  */
sysctl_pwr_get_pd_hd_tim(volatile uint32_t * reg,uint32_t * pd_hd_tim)286 bool sysctl_pwr_get_pd_hd_tim(volatile uint32_t *reg, uint32_t *pd_hd_tim)
287 {
288     if((NULL == reg) || (NULL == pd_hd_tim))
289         return false;
290 
291     *pd_hd_tim = ((*reg) >> 4) & 0xFF;
292     return true;
293 }
294 
295 /*
296  * After restoring the power supply of a power domain (bringup), you need to
297  * wait for a period of time to ensure that the power supply of the power domain
298  * is fully restored.
299  */
sysctl_pwr_get_pwr_su_tim(volatile uint32_t * reg,uint32_t * pwr_su_tim)300 bool sysctl_pwr_get_pwr_su_tim(volatile uint32_t *reg, uint32_t *pwr_su_tim)
301 {
302     if((NULL == reg) || (NULL == pwr_su_tim))
303         return false;
304 
305     *pwr_su_tim = ((*reg) >> 12) & 0xFF;
306     return true;
307 }
308 
309 /* cpu1 wait for interrupt time */
sysctl_pwr_get_wfi_tim(volatile uint32_t * reg,uint32_t * wfi_tim)310 bool sysctl_pwr_get_wfi_tim(volatile uint32_t *reg, uint32_t *wfi_tim)
311 {
312     if((NULL == reg) || (NULL == wfi_tim))
313         return false;
314 
315     *wfi_tim = (*reg >> 20) & 0xFFF;
316     return true;
317 }
318 
sysctl_pwr_get_tim(sysctl_pwr_domain_e powerdomain,sysctl_pwr_tim_e timtype,uint32_t * tim_value)319 bool sysctl_pwr_get_tim(sysctl_pwr_domain_e powerdomain, sysctl_pwr_tim_e timtype, uint32_t *tim_value)
320 {
321     volatile uint32_t *pwr_reg = NULL;
322     volatile uint32_t *lpi_reg = NULL;
323     volatile uint32_t *wfi_reg = NULL;
324 
325     if((powerdomain >= SYSCTL_PD_MAX) || (timtype >= SYSCTL_PWR_MAX_TIM))
326         return false;
327 
328     switch(powerdomain)
329     {
330         case SYSCTL_PD_CPU1:
331         {
332             pwr_reg = (volatile uint32_t *)&sysctl_pwr->cpu1_pwr_tim;
333             lpi_reg = (volatile uint32_t *)&sysctl_pwr->cpu1_lpi_tim;
334             wfi_reg = (volatile uint32_t *)&sysctl_pwr->cpu1_pwr_tim;
335             break;
336         }
337         case SYSCTL_PD_AI:
338         {
339             pwr_reg = (volatile uint32_t *)&sysctl_pwr->ai_pwr_tim;
340             lpi_reg = (volatile uint32_t *)&sysctl_pwr->ai_lpi_tim;
341             break;
342         }
343         case SYSCTL_PD_DISP:
344         {
345             pwr_reg = (volatile uint32_t *)&sysctl_pwr->disp_pwr_tim;
346             lpi_reg = (volatile uint32_t *)&sysctl_pwr->disp_lpi_tim;
347             break;
348         }
349         case SYSCTL_PD_VPU:
350         {
351             pwr_reg = (volatile uint32_t *)&sysctl_pwr->vpu_pwr_tim;
352             lpi_reg = (volatile uint32_t *)&sysctl_pwr->vpu_lpi_tim;
353             break;
354         }
355         case SYSCTL_PD_DPU:
356         {
357             pwr_reg = (volatile uint32_t *)&sysctl_pwr->dpu_pwr_tim;
358             lpi_reg = (volatile uint32_t *)&sysctl_pwr->dpu_lpi_tim;
359             break;
360         }
361         default:
362             return false;
363     }
364 
365     switch(timtype)
366     {
367         case SYSCTL_PWR_ACK_TO_TIM:
368             return sysctl_pwr_get_ack_to_tim(pwr_reg, tim_value);
369         case SYSCTL_PWR_IDLE_TO_TIM:
370             return sysctl_pwr_get_idle_to_tim(pwr_reg, tim_value);
371         case SYSCTL_PWR_IDLE_HD_TIM:
372             return sysctl_pwr_get_idle_hd_tim(pwr_reg, tim_value);
373         case SYSCTL_PWR_ISO_SU_TIM:
374             return sysctl_pwr_get_iso_su_tim(lpi_reg, tim_value);
375         case SYSCTL_PWR_PD_HD_TIM:
376             return sysctl_pwr_get_pd_hd_tim(lpi_reg, tim_value);
377         case SYSCTL_PWR_SU_TIM:
378             return sysctl_pwr_get_pwr_su_tim(lpi_reg, tim_value);
379         case SYSCTL_PWR_WFI_TIM:
380             return sysctl_pwr_get_wfi_tim(wfi_reg,tim_value);
381         default:
382             return false;
383     }
384 }
385 
386 
387 /*****************************************************************************************
388 *                                CPU1 KEEP RESET IN POWEROFF MODE
389 *   powerdomain: power domain
390 *   enable: poweron, keep reset; poweroff, remove reset
391 *****************************************************************************************/
392 
393 /* It will not be powered off when not working and can be in a reset state */
sysctl_pwr_set_poweroff_keep_reset(sysctl_pwr_domain_e powerdomain,bool enable)394 bool sysctl_pwr_set_poweroff_keep_reset(sysctl_pwr_domain_e powerdomain, bool enable)
395 {
396     volatile uint32_t ret;
397 
398     if(SYSCTL_PD_CPU1 == powerdomain)
399     {
400         ret = sysctl_pwr->cpu1_pwr_lpi_ctl;
401         ret &= 0xfff7fff7;
402         if(true == enable)
403         {
404             sysctl_pwr->cpu1_pwr_lpi_ctl = ret | ((1 << 3) | (1 << 19));
405         }
406         else
407         {
408             sysctl_pwr->cpu1_pwr_lpi_ctl = ret | ((0 << 3) | (1 << 19));
409         }
410 
411         return true;
412     }
413     else
414     {
415         return false;
416     }
417 }
418 
sysctl_pwr_get_poweroff_keep_reset(sysctl_pwr_domain_e powerdomain,bool * enable)419 bool sysctl_pwr_get_poweroff_keep_reset(sysctl_pwr_domain_e powerdomain, bool *enable)
420 {
421     if(SYSCTL_PD_CPU1 == powerdomain)
422     {
423         if(sysctl_pwr->cpu1_pwr_lpi_ctl & (1 << 3))
424             *enable = true;
425         else
426             *enable = false;
427         return true;
428     }
429     else
430     {
431         return false;
432     }
433 }
434 
435 /*****************************************************************************************
436 *                                CPU1 AUTO POWERUP OR POWERDOWN
437 *   powerdomain: power domain
438 *   enable: poweron, enable power control unit auto control mode; poweroff, disable auto
439 *****************************************************************************************/
440 
441 /*
442  * In MAIX3, CPU0 and CPU1 power domains support automatic power on and off
443  * management.
444  */
sysctl_pwr_set_auto_pwr(sysctl_pwr_domain_e powerdomain,bool enable)445 bool sysctl_pwr_set_auto_pwr(sysctl_pwr_domain_e powerdomain, bool enable)
446 {
447     volatile uint32_t ret;
448 
449     if(SYSCTL_PD_CPU1 == powerdomain)
450     {
451         ret = sysctl_pwr->cpu1_pwr_lpi_ctl;
452         ret &= 0xfffbfffb;
453         if(true == enable)
454         {
455             sysctl_pwr->cpu1_pwr_lpi_ctl = ret | ((1 << 2) | (1 << 18));
456         }
457         else
458         {
459             sysctl_pwr->cpu1_pwr_lpi_ctl = ret | ((0 << 2) | (1 << 18));
460         }
461         return true;
462     }
463     else
464     {
465         return false;
466     }
467 }
468 
sysctl_pwr_get_auto_pwr(sysctl_pwr_domain_e powerdomain,bool * enable)469 bool sysctl_pwr_get_auto_pwr(sysctl_pwr_domain_e powerdomain, bool *enable)
470 {
471     if(SYSCTL_PD_CPU1 == powerdomain)
472     {
473         if(sysctl_pwr->cpu1_pwr_lpi_ctl & (1 << 2))
474             *enable = true;
475         else
476             *enable = false;
477         return true;
478     }
479     else
480     {
481         return false;
482     }
483 }
484 
485 /*****************************************************************************************
486 *                                POWER DOMAIN REPAIR
487 *   powerdomain: power domain
488 *****************************************************************************************/
489 
490 /* When powering up, set the power domain to repair */
sysctl_pwr_set_repair_enable(sysctl_pwr_domain_e powerdomain)491 bool sysctl_pwr_set_repair_enable(sysctl_pwr_domain_e powerdomain)
492 {
493     switch(powerdomain)
494     {
495         case SYSCTL_PD_AI:
496             sysctl_pwr->ai_pwr_lpi_ctl |= (1 << 4) | (1 << 20);
497             return true;
498         default:
499             return false;
500     }
501 }
502 
sysctl_pwr_check_repair_done(sysctl_pwr_domain_e powerdomain)503 bool sysctl_pwr_check_repair_done(sysctl_pwr_domain_e powerdomain)
504 {
505     switch(powerdomain)
506     {
507         case SYSCTL_PD_AI:
508             return (sysctl_pwr->repair_status & (1 << 1)) ? true:false;
509         case SYSCTL_PD_MAX:
510             return (sysctl_pwr->repair_status & (1 << 2)) ? true:false;
511         default:
512             return false;
513     }
514 }
515 
516 /*****************************************************************************************
517 *                                NOC POWER CONTROLLER
518 *   powerdomain: power domain
519 *   enable: true, connect noc, exit idle mode; false, disconnect noc, go idle mode.
520 *****************************************************************************************/
521 
522 /*
523  * Set different power domains to disconnect/connect to NOC and enter/leave
524  * idle state.
525  */
sysctl_pwr_set_lpi(sysctl_pwr_domain_e powerdomain,bool enable)526 bool sysctl_pwr_set_lpi(sysctl_pwr_domain_e powerdomain, bool enable)
527 {
528     switch(powerdomain)
529     {
530         case SYSCTL_PD_CPU1:
531         {
532             sysctl_pwr->cpu1_pwr_lpi_ctl |= (true == enable) ? ((1 << 5) | (1 << 21)) : ((1 << 4) | (1 << 20));
533 
534             /* usleep(500); */
535             rt_thread_delay(1);
536 
537             if(true == enable)
538                 return (sysctl_pwr->cpu1_pwr_lpi_state & (1 << 3)) ? true:false;
539             else
540                 return (sysctl_pwr->cpu1_pwr_lpi_state & (1 << 2)) ? true:false;
541         }
542         case SYSCTL_PD_AI:
543         {
544             sysctl_pwr->ai_pwr_lpi_ctl |= (true == enable) ? ((1 << 3) | (1 << 19)) : ((1 << 2) | (1 << 18));
545 
546             /* usleep(500); */
547             rt_thread_delay(1);
548 
549             if(true == enable)
550                 return (sysctl_pwr->ai_pwr_lpi_state & (1 << 3)) ? true:false;
551             else
552                 return (sysctl_pwr->ai_pwr_lpi_state & (1 << 2)) ? true:false;
553         }
554         case SYSCTL_PD_DISP:
555         {
556             sysctl_pwr->disp_lpi_ctl |= (true == enable) ? ((1 << 3) | (1 << 19)) : ((1 << 2) | (1 << 18));
557 
558             /* usleep(500); */
559             rt_thread_delay(1);
560 
561             if(true == enable)
562                 return (sysctl_pwr->disp_lpi_state & (1 << 3)) ? true:false;
563             else
564                 return (sysctl_pwr->disp_lpi_state & (1 << 2)) ? true:false;
565         }
566         case SYSCTL_PD_VPU:
567         {
568             sysctl_pwr->vpu_pwr_lpi_ctl |= (true == enable) ? ((1 << 3) | (1 << 19)) : ((1 << 2) | (1 << 18));
569 
570             /* usleep(500); */
571             rt_thread_delay(1);
572 
573             if(true == enable)
574                 return (sysctl_pwr->vpu_lpi_state & (1 << 3)) ? true:false;
575             else
576                 return (sysctl_pwr->vpu_lpi_state & (1 << 2)) ? true:false;
577         }
578         case SYSCTL_PD_DPU:
579         {
580             sysctl_pwr->dpu_pwr_lpi_ctl |= (true == enable) ? ((1 << 3) | (1 << 19)) : ((1 << 2) | (1 << 18));
581 
582             /* usleep(500); */
583             rt_thread_delay(1);
584 
585             if(true == enable)
586                 return (sysctl_pwr->dpu_pwr_lpi_state & (1 << 3)) ? true:false;
587             else
588                 return (sysctl_pwr->dpu_pwr_lpi_state & (1 << 2)) ? true:false;
589         }
590         default:
591             return false;
592     }
593 }
594 
595 /*****************************************************************************************
596 *                                POWER DOMAIN ON OR OFF
597 *   powerdomain: power domain
598 *   enable: true for powerup, false for poweroff.
599 *****************************************************************************************/
600 
sysctl_pwr_set_pwr_reg(volatile uint32_t * regctl,volatile uint32_t * regsta,bool enable)601 bool sysctl_pwr_set_pwr_reg(volatile uint32_t *regctl, volatile uint32_t *regsta, bool enable)
602 {
603     /* enable==true, power on; enable==false, power off */
604     uint32_t mask;
605 
606     mask = enable ? 0x2 : 0x1;
607     if (*regsta & mask)
608         return true;
609 
610     *regctl = (0x30000 | mask);
611 
612     for (int i = 0; i < 100; i++)
613     {
614         if (*regsta & mask)
615             return true;
616         for (int j = 0; j < 5000; j++);
617     }
618 
619     return false;
620 }
621 
sysctl_pwr_set_power(sysctl_pwr_domain_e powerdomain,bool enable)622 bool sysctl_pwr_set_power(sysctl_pwr_domain_e powerdomain, bool enable)
623 {
624     volatile uint32_t *pwr_ctl_reg = NULL;
625     volatile uint32_t *pwr_sta_reg = NULL;
626 
627     switch(powerdomain)
628     {
629         case SYSCTL_PD_CPU1:
630         {
631             pwr_ctl_reg = (volatile uint32_t *)&sysctl_pwr->cpu1_pwr_lpi_ctl;
632             pwr_sta_reg = (volatile uint32_t *)&sysctl_pwr->cpu1_pwr_lpi_state;
633             break;
634         }
635         case SYSCTL_PD_AI:
636         {
637             pwr_ctl_reg = (volatile uint32_t *)&sysctl_pwr->ai_pwr_lpi_ctl;
638             pwr_sta_reg = (volatile uint32_t *)&sysctl_pwr->ai_pwr_lpi_state;
639             break;
640         }
641         case SYSCTL_PD_DISP:
642         {
643             pwr_ctl_reg = (volatile uint32_t *)&sysctl_pwr->disp_lpi_ctl;
644             pwr_sta_reg = (volatile uint32_t *)&sysctl_pwr->disp_lpi_state;
645             break;
646         }
647         case SYSCTL_PD_VPU:
648         {
649             pwr_ctl_reg = (volatile uint32_t *)&sysctl_pwr->vpu_pwr_lpi_ctl;
650             pwr_sta_reg = (volatile uint32_t *)&sysctl_pwr->vpu_lpi_state;
651             break;
652         }
653         case SYSCTL_PD_DPU:
654         {
655             pwr_ctl_reg = (volatile uint32_t *)&sysctl_pwr->dpu_pwr_lpi_ctl;
656             pwr_sta_reg = (volatile uint32_t *)&sysctl_pwr->dpu_pwr_lpi_state;
657             break;
658         }
659         default:
660             return false;
661     }
662 
663     /* repair powerdomain */
664     /* only powerup need repair */
665     if(true == enable)
666     {
667         (void)sysctl_pwr_set_repair_enable(powerdomain);
668     }
669 
670     return sysctl_pwr_set_pwr_reg(pwr_ctl_reg, pwr_sta_reg, enable);
671 }
672 
sysctl_pwr_set_power_multi(sysctl_pwr_domain_e powerdomain,bool enable)673 bool sysctl_pwr_set_power_multi(sysctl_pwr_domain_e powerdomain, bool enable)
674 {
675     bool ret = true;
676     rt_base_t level;
677     static uint32_t ref_count[SYSCTL_PD_MAX];
678     /*
679     1. enable step for non-DISP power domains:
680         a. disable interrupt
681         b. judge ref_count, if == 0, execute sysctl_pwr_set_pwr_reg
682         c. ref_count++, limit UINT32_MAX
683         d. enable interrupt
684     2. disable step for non-DISP power domains:
685         a. disable interrupt
686         b. judge ref_count, if == 0, go step d
687         c. ref_count--, judge ref_count, if == 0, execute sysctl_pwr_set_pwr_reg
688         d. enable interrupt
689     3. enable step for DISP power domains:
690         a. disable interrupt
691         b. judge ref_count, if == 0, execute
692             b.1 get HARDLOCK_DISP
693             b.2 get HARDLOCK_DISP_CPU1
694             b.3 execute sysctl_pwr_set_pwr_reg
695             b.4 put HARDLOCK_DISP
696         c. ref_count++, limit UINT32_MAX
697         d. enable interrupt
698     4. disable step for DISP power domains:
699         a. disable interrupt
700         b. judge ref_count, if == 0, go step e
701         c. ref_count--, judge ref_count, if == 0, execute
702             c.1 get HARDLOCK_DISP
703             c.2 qeury HARDLOCK_DISP_CPU0, if no get, go step c.4
704             c.3 put HARDLOCK_DISP_CPU0, execute sysctl_pwr_set_pwr_reg
705             c.4 put HARDLOCK_DISP_CPU1
706             c.5 put HARDLOCK_DISP
707         d. enable interrupt
708     */
709     level = rt_hw_interrupt_disable();
710     if (enable == true)
711     {
712         if (ref_count[powerdomain] == 0)
713         {
714             if (powerdomain == SYSCTL_PD_DISP)
715             {
716                 while (kd_hardlock_lock(HARDLOCK_DISP));
717                 kd_hardlock_lock(HARDLOCK_DISP_CPU1);
718                 ret = sysctl_pwr_set_power(powerdomain, enable);
719                 kd_hardlock_unlock(HARDLOCK_DISP);
720             } else {
721                 ret = sysctl_pwr_set_power(powerdomain, enable);
722             }
723         }
724         ref_count[powerdomain]++;
725         if (ref_count[powerdomain] == UINT32_MAX)
726             rt_kprintf("error: enable too many times\n");
727     } else if (ref_count[powerdomain])
728     {
729         ref_count[powerdomain]--;
730         if (ref_count[powerdomain] == 0)
731         {
732             if (powerdomain == SYSCTL_PD_DISP)
733             {
734                 while (kd_hardlock_lock(HARDLOCK_DISP));
735                 if (kd_hardlock_lock(HARDLOCK_DISP_CPU0) == 0)
736                 {
737                     kd_hardlock_unlock(HARDLOCK_DISP_CPU0);
738                     ret = sysctl_pwr_set_power(powerdomain, enable);
739                 }
740                 kd_hardlock_unlock(HARDLOCK_DISP_CPU1);
741                 kd_hardlock_unlock(HARDLOCK_DISP);
742             } else {
743                 ret = sysctl_pwr_set_power(powerdomain, enable);
744             }
745         }
746     }
747     rt_hw_interrupt_enable(level);
748 
749     return ret;
750 }
751 
752 /* Power Domain Power-up */
sysctl_pwr_up(sysctl_pwr_domain_e powerdomain)753 bool sysctl_pwr_up(sysctl_pwr_domain_e powerdomain)
754 {
755     return sysctl_pwr_set_power_multi(powerdomain, true);
756 }
757 
758 /* Power off a power domain */
sysctl_pwr_off(sysctl_pwr_domain_e powerdomain)759 bool sysctl_pwr_off(sysctl_pwr_domain_e powerdomain)
760 {
761     return sysctl_pwr_set_power_multi(powerdomain, false);
762 }
763 
rt_hw_sysctl_pwr_init(void)764 int rt_hw_sysctl_pwr_init(void)
765 {
766     sysctl_pwr = rt_ioremap((void*)PWR_BASE_ADDR, PWR_IO_SIZE);
767     if(!sysctl_pwr)
768     {
769         rt_kprintf("sysctl_pwr ioremap error\n");
770         return -1;
771     }
772 
773     return 0;
774 }
775 INIT_BOARD_EXPORT(rt_hw_sysctl_pwr_init);