1 /*!
2     \file    gd32e50x_pmu.c
3     \brief   PMU driver
4 
5     \version 2020-03-10, V1.0.0, firmware for GD32E50x
6     \version 2020-08-26, V1.1.0, firmware for GD32E50x
7     \version 2020-12-15, V1.1.1, firmware for GD32E50x
8     \version 2021-03-23, V1.2.0, firmware for GD32E50x
9 */
10 
11 /*
12     Copyright (c) 2021, GigaDevice Semiconductor Inc.
13 
14     Redistribution and use in source and binary forms, with or without modification,
15 are permitted provided that the following conditions are met:
16 
17     1. Redistributions of source code must retain the above copyright notice, this
18        list of conditions and the following disclaimer.
19     2. Redistributions in binary form must reproduce the above copyright notice,
20        this list of conditions and the following disclaimer in the documentation
21        and/or other materials provided with the distribution.
22     3. Neither the name of the copyright holder nor the names of its contributors
23        may be used to endorse or promote products derived from this software without
24        specific prior written permission.
25 
26     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
28 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
35 OF SUCH DAMAGE.
36 */
37 
38 #include "gd32e50x_pmu.h"
39 
40 /*!
41     \brief      reset PMU registers
42     \param[in]  none
43     \param[out] none
44     \retval     none
45 */
pmu_deinit(void)46 void pmu_deinit(void)
47 {
48     /* reset PMU */
49     rcu_periph_reset_enable(RCU_PMURST);
50     rcu_periph_reset_disable(RCU_PMURST);
51 }
52 
53 /*!
54     \brief      select low voltage detector threshold
55     \param[in]  lvdt_n:
56                 only one parameter can be selected which is shown as below:
57       \arg        PMU_LVDT_0: voltage threshold is 2.1V
58       \arg        PMU_LVDT_1: voltage threshold is 2.3V
59       \arg        PMU_LVDT_2: voltage threshold is 2.4V
60       \arg        PMU_LVDT_3: voltage threshold is 2.6V
61       \arg        PMU_LVDT_4: voltage threshold is 2.7V
62       \arg        PMU_LVDT_5: voltage threshold is 2.9V
63       \arg        PMU_LVDT_6: voltage threshold is 3.0V
64       \arg        PMU_LVDT_7: voltage threshold is 3.1V
65     \param[out] none
66     \retval     none
67 */
pmu_lvd_select(uint32_t lvdt_n)68 void pmu_lvd_select(uint32_t lvdt_n)
69 {
70     /* disable LVD */
71     PMU_CTL0 &= ~PMU_CTL0_LVDEN;
72     /* clear LVDT bits */
73     PMU_CTL0 &= ~PMU_CTL0_LVDT;
74     /* set LVDT bits according to lvdt_n */
75     PMU_CTL0 |= lvdt_n;
76     /* enable LVD */
77     PMU_CTL0 |= PMU_CTL0_LVDEN;
78 }
79 
80 /*!
81     \brief      disable PMU LVD
82     \param[in]  none
83     \param[out] none
84     \retval     none
85 */
pmu_lvd_disable(void)86 void pmu_lvd_disable(void)
87 {
88     /* disable LVD */
89     PMU_CTL0 &= ~PMU_CTL0_LVDEN;
90 }
91 
92 /*!
93     \brief      enable high-driver mode
94                 this bit set by software only when IRC8M or HXTAL used as system clock
95     \param[in]  none
96     \param[out] none
97     \retval     none
98 */
pmu_highdriver_mode_enable(void)99 void pmu_highdriver_mode_enable(void)
100 {
101     PMU_CTL0 |= PMU_CTL0_HDEN;
102 }
103 
104 /*!
105     \brief      disable high-driver mode
106     \param[in]  none
107     \param[out] none
108     \retval     none
109 */
pmu_highdriver_mode_disable(void)110 void pmu_highdriver_mode_disable(void)
111 {
112     PMU_CTL0 &= ~PMU_CTL0_HDEN;
113 }
114 
115 /*!
116     \brief      switch high-driver mode
117                 this bit set by software only when IRC8M or HXTAL used as system clock
118     \param[in]  highdr_switch:
119                 only one parameter can be selected which is shown as below:
120       \arg        PMU_HIGHDR_SWITCH_NONE: disable high-driver mode switch
121       \arg        PMU_HIGHDR_SWITCH_EN: enable high-driver mode switch
122     \param[out] none
123     \retval     none
124 */
pmu_highdriver_switch_select(uint32_t highdr_switch)125 void pmu_highdriver_switch_select(uint32_t highdr_switch)
126 {
127     /* wait for HDRF flag set */
128     while(SET != pmu_flag_get(PMU_FLAG_HDRF)){
129     }
130     PMU_CTL0 &= ~PMU_CTL0_HDS;
131     PMU_CTL0 |= highdr_switch;
132 }
133 
134 /*!
135     \brief      enable low-driver mode in deep-sleep/deep-sleep 1/deep-sleep 2 mode
136     \param[in]  none
137     \param[out] none
138     \retval     none
139 */
pmu_lowdriver_mode_enable(void)140 void pmu_lowdriver_mode_enable(void)
141 {
142     PMU_CTL0 |= PMU_CTL0_LDEN;
143 }
144 
145 /*!
146     \brief      disable low-driver mode in deep-sleep/deep-sleep 1/deep-sleep 2 mode
147     \param[in]  none
148     \param[out] none
149     \retval     none
150 */
pmu_lowdriver_mode_disable(void)151 void pmu_lowdriver_mode_disable(void)
152 {
153     PMU_CTL0 &= ~PMU_CTL0_LDEN;
154 }
155 
156 /*!
157     \brief      in deep-sleep/deep-sleep 1/deep-sleep 2 mode, driver mode when use low power LDO
158     \param[in]  mode:
159                 only one parameter can be selected which is shown as below:
160       \arg        PMU_NORMALDR_LOWPWR: normal driver when use low power LDO
161       \arg        PMU_LOWDR_LOWPWR: low-driver mode enabled when LDEN is 11 and use low power LDO
162     \param[out] none
163     \retval     none
164 */
pmu_lowpower_driver_config(uint32_t mode)165 void pmu_lowpower_driver_config(uint32_t mode)
166 {
167     PMU_CTL0 &= ~PMU_CTL0_LDLP;
168     PMU_CTL0 |= mode;
169 }
170 
171 /*!
172     \brief      in deep-sleep/deep-sleep 1/deep-sleep 2 mode, driver mode when use normal power LDO
173     \param[in]  mode:
174                 only one parameter can be selected which is shown as below:
175       \arg        PMU_NORMALDR_NORMALPWR: normal driver when use normal power LDO
176       \arg        PMU_LOWDR_NORMALPWR: low-driver mode enabled when LDEN is 11 and use normal power LDO
177     \param[out] none
178     \retval     none
179 */
pmu_normalpower_driver_config(uint32_t mode)180 void pmu_normalpower_driver_config(uint32_t mode)
181 {
182     PMU_CTL0 &= ~PMU_CTL0_LDNP;
183     PMU_CTL0 |= mode;
184 }
185 
186 /*!
187     \brief      PMU work at sleep mode
188     \param[in]  sleepmodecmd:
189                 only one parameter can be selected which is shown as below:
190       \arg        WFI_CMD: use WFI command
191       \arg        WFE_CMD: use WFE command
192     \param[out] none
193     \retval     none
194 */
pmu_to_sleepmode(uint8_t sleepmodecmd)195 void pmu_to_sleepmode(uint8_t sleepmodecmd)
196 {
197     /* clear sleepdeep bit of Cortex-M33 system control register */
198     SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
199 
200     /* select WFI or WFE command to enter sleep mode */
201     if(WFI_CMD == sleepmodecmd){
202         __WFI();
203     }else{
204         __WFE();
205     }
206 }
207 
208 /*!
209     \brief      PMU work at deepsleep mode
210     \param[in]  ldo:
211                 only one parameter can be selected which is shown as below:
212       \arg        PMU_LDO_NORMAL: LDO normal work when pmu enter deepsleep mode
213       \arg        PMU_LDO_LOWPOWER: LDO work at low power mode when pmu enter deepsleep mode
214     \param[in]  lowdrive:
215                 only one parameter can be selected which is shown as below:
216       \arg        PMU_LOWDRIVER_ENABLE: low-driver mode enable in deep-sleep mode
217       \arg        PMU_LOWDRIVER_DISABLE: low-driver mode disable in deep-sleep mode
218     \param[in]  deepsleepmodecmd:
219                 only one parameter can be selected which is shown as below:
220       \arg        WFI_CMD: use WFI command
221       \arg        WFE_CMD: use WFE command
222     \param[out] none
223     \retval     none
224 */
pmu_to_deepsleepmode(uint32_t ldo,uint32_t lowdrive,uint8_t deepsleepmodecmd)225 void pmu_to_deepsleepmode(uint32_t ldo, uint32_t lowdrive, uint8_t deepsleepmodecmd)
226 {
227     /* clear stbmod and ldolp bits and low drive bits */
228     PMU_CTL0 &= ~((uint32_t)(PMU_CTL0_STBMOD | PMU_CTL0_LDOLP | PMU_CTL0_LDEN | PMU_CTL0_LDNP | PMU_CTL0_LDLP));
229     /* clear deep-sleep 1/2 mode enable bits  */
230     PMU_CTL1 &= ~(PMU_CTL1_DPMOD1 | PMU_CTL1_DPMOD2);
231 
232     /* set ldolp bit according to pmu_ldo */
233     PMU_CTL0 |= ldo;
234 
235     /* low drive mode config in deep-sleep mode */
236     if(PMU_LOWDRIVER_ENABLE == lowdrive){
237         if(PMU_LDO_NORMAL == ldo){
238             PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDNP);
239         }else{
240             PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDLP);
241         }
242     }
243 
244     /* set sleepdeep bit of Cortex-M33 system control register */
245     SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
246 
247     /* select WFI or WFE command to enter deepsleep mode */
248     if(WFI_CMD == deepsleepmodecmd){
249         __WFI();
250     }else{
251         __SEV();
252         __WFE();
253         __WFE();
254     }
255 
256     /* reset sleepdeep bit of Cortex-M33 system control register */
257     SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
258 }
259 
260 /*!
261     \brief      PMU work at deepsleep mode 1
262     \param[in]  ldo:
263                 only one parameter can be selected which is shown as below:
264       \arg        PMU_LDO_NORMAL: LDO normal work when pmu enter deepsleep mode 1
265       \arg        PMU_LDO_LOWPOWER: LDO work at low power mode when pmu enter deepsleep mode 1
266     \param[in]  lowdrive:
267                 only one parameter can be selected which is shown as below:
268       \arg        PMU_LOWDRIVER_ENABLE: low-driver mode enable in deep-sleep 1 mode
269       \arg        PMU_LOWDRIVER_DISABLE: low-driver mode disable in deep-sleep 1 mode
270     \param[in]  deepsleepmode1cmd:
271                 only one parameter can be selected which is shown as below:
272       \arg        WFI_CMD: use WFI command
273       \arg        WFE_CMD: use WFE command
274     \param[out] none
275     \retval     none
276 */
pmu_to_deepsleepmode_1(uint32_t ldo,uint32_t lowdrive,uint8_t deepsleepmode1cmd)277 void pmu_to_deepsleepmode_1(uint32_t ldo, uint32_t lowdrive, uint8_t deepsleepmode1cmd)
278 {
279     /* clear stbmod and ldolp bits and low drive bits */
280     PMU_CTL0 &= ~((uint32_t)(PMU_CTL0_STBMOD | PMU_CTL0_LDOLP | PMU_CTL0_LDEN | PMU_CTL0_LDNP | PMU_CTL0_LDLP));
281     /* clear deep-sleep 2 mode enable bit */
282     PMU_CTL1 &= ~PMU_CTL1_DPMOD2;
283     /* enable deep-sleep 1 mode */
284     PMU_CTL1 |= PMU_CTL1_DPMOD1;
285 
286     /* set ldolp bit according to pmu_ldo */
287     PMU_CTL0 |= ldo;
288 
289     /* low drive mode config in deep-sleep 1 mode */
290     if(PMU_LOWDRIVER_ENABLE == lowdrive){
291         if(PMU_LDO_NORMAL == ldo){
292             PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDNP);
293         }else{
294             PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDLP);
295         }
296     }
297 
298     /* set sleepdeep bit of Cortex-M33 system control register */
299     SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
300 
301     /* select WFI or WFE command to enter deepsleep mode 1 */
302     if(WFI_CMD == deepsleepmode1cmd){
303         __WFI();
304     }else{
305         __SEV();
306         __WFE();
307         __WFE();
308     }
309 
310     /* reset sleepdeep bit of Cortex-M33 system control register */
311     SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
312     PMU_CTL1 &= ~PMU_CTL1_DPMOD1;
313 }
314 
315 /*!
316     \brief      PMU work at deepsleep mode 2
317     \param[in]  ldo:
318                 only one parameter can be selected which is shown as below:
319       \arg        PMU_LDO_NORMAL: LDO normal work when pmu enter deepsleep mode 2
320       \arg        PMU_LDO_LOWPOWER: LDO work at low power mode when pmu enter deepsleep mode 2
321     \param[in]  lowdrive:
322                 only one parameter can be selected which is shown as below:
323       \arg        PMU_LOWDRIVER_ENABLE: low-driver mode enable in deep-sleep 2 mode
324       \arg        PMU_LOWDRIVER_DISABLE: low-driver mode disable in deep-sleep 2 mode
325     \param[in]  deepsleepmode2cmd:
326                 only one parameter can be selected which is shown as below:
327       \arg        WFI_CMD: use WFI command
328       \arg        WFE_CMD: use WFE command
329     \param[out] none
330     \retval     none
331 */
pmu_to_deepsleepmode_2(uint32_t ldo,uint32_t lowdrive,uint8_t deepsleepmode2cmd)332 void pmu_to_deepsleepmode_2(uint32_t ldo, uint32_t lowdrive, uint8_t deepsleepmode2cmd)
333 {
334     /* clear stbmod and ldolp bits and low drive bits */
335     PMU_CTL0 &= ~((uint32_t)(PMU_CTL0_STBMOD | PMU_CTL0_LDOLP | PMU_CTL0_LDEN | PMU_CTL0_LDNP | PMU_CTL0_LDLP));
336     /* clear deep-sleep 1 mode enable bit */
337     PMU_CTL1 &= ~PMU_CTL1_DPMOD1;
338     /* enable deep-sleep 2 mode */
339     PMU_CTL1 |= PMU_CTL1_DPMOD2;
340 
341     /* set ldolp bit according to pmu_ldo */
342     PMU_CTL0 |= ldo;
343 
344     /* low drive mode config in deep-sleep 2 mode */
345     if(PMU_LOWDRIVER_ENABLE == lowdrive){
346         if(PMU_LDO_NORMAL == ldo){
347             PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDNP);
348         }else{
349             PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDEN | PMU_CTL0_LDLP);
350         }
351     }
352 
353     /* set sleepdeep bit of Cortex-M33 system control register */
354     SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
355 
356     /* select WFI or WFE command to enter deepsleep mode 2 */
357     if(WFI_CMD == deepsleepmode2cmd){
358         __WFI();
359     }else{
360         __SEV();
361         __WFE();
362         __WFE();
363     }
364 
365     /* reset sleepdeep bit of Cortex-M33 system control register */
366     SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
367     PMU_CTL1 &= ~PMU_CTL1_DPMOD2;
368 }
369 
370 /*!
371     \brief      pmu work at standby mode
372     \param[in]  standbymodecmd:
373                 only one parameter can be selected which is shown as below:
374       \arg        WFI_CMD: use WFI command
375       \arg        WFE_CMD: use WFE command
376     \param[out] none
377     \retval     none
378 */
pmu_to_standbymode(uint8_t standbymodecmd)379 void pmu_to_standbymode(uint8_t standbymodecmd)
380 {
381     /* set sleepdeep bit of Cortex-M33 system control register */
382     SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
383 
384     /* set stbmod bit */
385     PMU_CTL0 |= PMU_CTL0_STBMOD;
386 
387     /* reset wakeup flag */
388     PMU_CTL0 |= PMU_CTL0_WURST;
389 
390     /* select WFI or WFE command to enter standby mode */
391     if(WFI_CMD == standbymodecmd){
392         __WFI();
393     }else{
394         __WFE();
395         __WFE();
396     }
397 }
398 
399 /*!
400     \brief      enable PMU wakeup pin
401     \param[in]  wakeup_pin:
402                 one or more parameters can be selected which are shown as below:
403       \arg        PMU_WAKEUP_PIN0: WKUP Pin 0 (PA0)
404       \arg        PMU_WAKEUP_PIN1: WKUP Pin 1 (PC13)
405       \arg        PMU_WAKEUP_PIN2: WKUP Pin 2 (PE6)
406       \arg        PMU_WAKEUP_PIN3: WKUP Pin 3 (PA2)
407       \arg        PMU_WAKEUP_PIN4: WKUP Pin 4 (PC5)
408       \arg        PMU_WAKEUP_PIN5: WKUP Pin 5 (PB5)
409       \arg        PMU_WAKEUP_PIN6: WKUP Pin 6 (PB15)
410       \arg        PMU_WAKEUP_PIN7: WKUP Pin 7 (PF8)
411     \param[out] none
412     \retval     none
413 */
pmu_wakeup_pin_enable(uint32_t wakeup_pin)414 void pmu_wakeup_pin_enable(uint32_t wakeup_pin)
415 {
416     PMU_CS0 |= wakeup_pin;
417 }
418 
419 /*!
420     \brief      disable PMU wakeup pin
421     \param[in]  wakeup_pin:
422                 one or more parameters can be selected which are shown as below:
423       \arg        PMU_WAKEUP_PIN0: WKUP Pin 0 (PA0)
424       \arg        PMU_WAKEUP_PIN1: WKUP Pin 1 (PC13)
425       \arg        PMU_WAKEUP_PIN2: WKUP Pin 2 (PE6)
426       \arg        PMU_WAKEUP_PIN3: WKUP Pin 3 (PA2)
427       \arg        PMU_WAKEUP_PIN4: WKUP Pin 4 (PC5)
428       \arg        PMU_WAKEUP_PIN5: WKUP Pin 5 (PB5)
429       \arg        PMU_WAKEUP_PIN6: WKUP Pin 6 (PB15)
430       \arg        PMU_WAKEUP_PIN7: WKUP Pin 7 (PF8)
431     \param[out] none
432     \retval     none
433 */
pmu_wakeup_pin_disable(uint32_t wakeup_pin)434 void pmu_wakeup_pin_disable(uint32_t wakeup_pin)
435 {
436     PMU_CS0 &= ~(wakeup_pin);
437 }
438 
439 /*!
440     \brief      enable backup domain write
441     \param[in]  none
442     \param[out] none
443     \retval     none
444 */
pmu_backup_write_enable(void)445 void pmu_backup_write_enable(void)
446 {
447     PMU_CTL0 |= PMU_CTL0_BKPWEN;
448 }
449 
450 /*!
451     \brief      disable backup domain write
452     \param[in]  none
453     \param[out] none
454     \retval     none
455 */
pmu_backup_write_disable(void)456 void pmu_backup_write_disable(void)
457 {
458     PMU_CTL0 &= ~PMU_CTL0_BKPWEN;
459 }
460 
461 /*!
462     \brief      get flag state
463     \param[in]  flag:
464                 only one parameter can be selected which is shown as below:
465       \arg        PMU_FLAG_WAKEUP: wakeup flag
466       \arg        PMU_FLAG_STANDBY: standby flag
467       \arg        PMU_FLAG_LVD: lvd flag
468       \arg        PMU_FLAG_HDRF: high-driver ready flag
469       \arg        PMU_FLAG_HDSRF: high-driver switch ready flag
470       \arg        PMU_FLAG_LDRF: low-driver mode ready flag
471       \arg        PMU_FLAG_DEEPSLEEP_1: deep-sleep 1 mode status flag
472       \arg        PMU_FLAG_DEEPSLEEP_2: deep-sleep 2 mode status flag
473     \param[out] none
474     \retval     FlagStatus: SET or RESET
475 */
pmu_flag_get(uint32_t flag)476 FlagStatus pmu_flag_get(uint32_t flag)
477 {
478     FlagStatus ret = RESET;
479 
480     if(RESET != (flag & BIT(31))){
481         flag &= ~BIT(31);
482         if(PMU_CS1 & flag){
483             ret = SET;
484         }
485     }else{
486         if(PMU_CS0 & flag){
487             ret = SET;
488         }
489     }
490     return ret;
491 }
492 
493 /*!
494     \brief      clear flag bit
495     \param[in]  flag:
496                 only one parameter can be selected which is shown as below:
497       \arg        PMU_FLAG_RESET_WAKEUP: reset wakeup flag
498       \arg        PMU_FLAG_RESET_STANDBY: reset standby flag
499       \arg        PMU_FLAG_RESET_DEEPSLEEP_1: reset deep-sleep 1 mode status flag
500       \arg        PMU_FLAG_RESET_DEEPSLEEP_2: reset deep-sleep 2 mode status flag
501     \param[out] none
502     \retval     none
503 */
pmu_flag_clear(uint32_t flag)504 void pmu_flag_clear(uint32_t flag)
505 {
506     switch(flag){
507     case PMU_FLAG_RESET_WAKEUP:
508         /* reset wakeup flag */
509         PMU_CTL0 |= PMU_CTL0_WURST;
510         break;
511     case PMU_FLAG_RESET_STANDBY:
512         /* reset standby flag */
513         PMU_CTL0 |= PMU_CTL0_STBRST;
514         break;
515     case PMU_FLAG_RESET_DEEPSLEEP_1:
516         /* reset deep-sleep 1 mode status flag */
517         PMU_CS1 &= ~PMU_CS1_DPF1;
518         break;
519     case PMU_FLAG_RESET_DEEPSLEEP_2:
520         /* reset deep-sleep 2 mode status flag */
521         PMU_CS1 &= ~PMU_CS1_DPF2;
522         break;
523     default :
524         break;
525     }
526 }
527