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);