1 /*!
2     \file    gd32l23x_pmu.c
3     \brief   PMU driver
4 
5     \version 2021-08-04, V1.0.0, firmware for GD32L23x
6 */
7 
8 /*
9     Copyright (c) 2021, GigaDevice Semiconductor Inc.
10 
11     Redistribution and use in source and binary forms, with or without modification,
12 are permitted provided that the following conditions are met:
13 
14     1. Redistributions of source code must retain the above copyright notice, this
15        list of conditions and the following disclaimer.
16     2. Redistributions in binary form must reproduce the above copyright notice,
17        this list of conditions and the following disclaimer in the documentation
18        and/or other materials provided with the distribution.
19     3. Neither the name of the copyright holder nor the names of its contributors
20        may be used to endorse or promote products derived from this software without
21        specific prior written permission.
22 
23     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
32 OF SUCH DAMAGE.
33 */
34 
35 #include "gd32l23x_pmu.h"
36 
37 /* PMU register bit offset */
38 #define PAR_TWK_CORE1_OFFSET               ((uint32_t)0x00000015U)               /*!< bit offset of TWK_CORE1 in PMU_PAR */
39 #define PAR_TSW_IRC16MCNT_OFFSET           ((uint32_t)0x00000010U)               /*!< bit offset of TSW_IRC16MCNT in PMU_PAR */
40 #define PAR_TWK_SRAM1_OFFSET               ((uint32_t)0x00000008U)               /*!< bit offset of TWK_SRAM1 in PMU_PAR */
41 
42 /*!
43     \brief      reset PMU
44     \param[in]  none
45     \param[out] none
46     \retval     none
47 */
pmu_deinit(void)48 void pmu_deinit(void)
49 {
50     /* reset PMU */
51     rcu_periph_reset_enable(RCU_PMURST);
52     rcu_periph_reset_disable(RCU_PMURST);
53 }
54 
55 /*!
56     \brief      select low voltage detector threshold
57     \param[in]  lvdt_n: low voltage detector threshold
58                 only one parameter can be selected which is shown as below:
59       \arg        PMU_LVDT_0: voltage threshold is 2.1V
60       \arg        PMU_LVDT_1: voltage threshold is 2.3V
61       \arg        PMU_LVDT_2: voltage threshold is 2.4V
62       \arg        PMU_LVDT_3: voltage threshold is 2.6V
63       \arg        PMU_LVDT_4: voltage threshold is 2.7V
64       \arg        PMU_LVDT_5: voltage threshold is 2.9V
65       \arg        PMU_LVDT_6: voltage threshold is 3.0V
66       \arg        PMU_LVDT_7: input analog voltage on PB7 (compared with 0.8V)
67     \param[out] none
68     \retval     none
69 */
pmu_lvd_select(uint32_t lvdt_n)70 void pmu_lvd_select(uint32_t lvdt_n)
71 {
72     /* disable LVD */
73     PMU_CTL0 &= ~PMU_CTL0_LVDEN;
74     /* clear LVDT bits */
75     PMU_CTL0 &= ~PMU_CTL0_LVDT;
76     /* set LVDT bits according to lvdt_n */
77     PMU_CTL0 |= lvdt_n;
78     /* enable LVD */
79     PMU_CTL0 |= PMU_CTL0_LVDEN;
80 }
81 
82 /*!
83     \brief      disable PMU lvd
84     \param[in]  none
85     \param[out] none
86     \retval     none
87 */
pmu_lvd_disable(void)88 void pmu_lvd_disable(void)
89 {
90     PMU_CTL0 &= ~PMU_CTL0_LVDEN;
91 }
92 
93 /*!
94     \brief      select LDO output voltage
95                 this bit set by software when the main PLL closed, before closing PLL, change the system clock to IRC16M or HXTAL
96     \param[in]  ldo_output:
97                 only one parameter can be selected which is shown as below:
98       \arg        PMU_LDOVS_LOW: LDO output voltage low mode
99       \arg        PMU_LDOVS_HIGH: LDO output voltage high mode
100     \param[out] none
101     \retval     none
102 */
pmu_ldo_output_select(uint32_t ldo_output)103 void pmu_ldo_output_select(uint32_t ldo_output)
104 {
105     PMU_CTL0 &= ~PMU_CTL0_LDOVS;
106     PMU_CTL0 |= ldo_output;
107 }
108 
109 /*!
110     \brief      enable VBAT battery charging
111     \param[in]  none
112     \param[out] none
113     \retval     none
114 */
pmu_vc_enable(void)115 void pmu_vc_enable(void)
116 {
117     PMU_CTL0 |= PMU_CTL0_VCEN;
118 }
119 
120 /*!
121     \brief      disable VBAT battery charging
122     \param[in]  none
123     \param[out] none
124     \retval     none
125 */
pmu_vc_disable(void)126 void pmu_vc_disable(void)
127 {
128     PMU_CTL0 &= ~PMU_CTL0_VCEN;
129 }
130 
131 /*!
132     \brief      select PMU VBAT battery charging resistor
133     \param[in]  resistor: VBAT battery charging resistor
134                 only one parameter can be selected which is shown as below:
135       \arg        PMU_VCRSEL_5K: 5 kOhms resistor is selected for charing VBAT battery
136       \arg        PMU_VCRSEL_1P5K: 1.5 kOhms resistor is selected for charing VBAT battery
137     \param[out] none
138     \retval     none
139 */
pmu_vcr_select(uint32_t resistor)140 void pmu_vcr_select(uint32_t resistor)
141 {
142     PMU_CTL0 &= ~PMU_CTL0_VCRSEL;
143     PMU_CTL0 |= resistor;
144 }
145 
146 /*!
147     \brief      enable low power in Run/Sleep mode
148     \param[in]  none
149     \param[out] none
150     \retval     none
151 */
pmu_low_power_enable(void)152 void pmu_low_power_enable(void)
153 {
154     PMU_CTL0 |= PMU_CTL0_LDNP;
155 }
156 
157 /*!
158     \brief      disable low power in Run/Sleep mode
159     \param[in]  none
160     \param[out] none
161     \retval     none
162 */
pmu_low_power_disable(void)163 void pmu_low_power_disable(void)
164 {
165     PMU_CTL0 &= ~PMU_CTL0_LDNP;
166 }
167 
168 /*!
169     \brief      PMU work at sleep mode
170     \param[in]  lowdrive: low-driver mode when use normal power LDO in Run/Sleep mode.
171                 only one parameter can be selected which is shown as below:
172       \arg        PMU_LDNP_NORMALDRIVE: low-driver mode disable
173       \arg        PMU_LDNP_LOWDRIVE: low-driver mode enable
174     \param[in]  sleepmodecmd: sleep mode command
175                 only one parameter can be selected which is shown as below:
176       \arg        WFI_CMD: use WFI command
177       \arg        WFE_CMD: use WFE command
178     \param[out] none
179     \retval     none
180 */
pmu_to_sleepmode(uint32_t lowdrive,uint8_t sleepmodecmd)181 void pmu_to_sleepmode(uint32_t lowdrive, uint8_t sleepmodecmd)
182 {
183     /* clear sleepDeep bit of Cortex-M23 system control register */
184     SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
185 
186     /* clear lowpower mode and low drive bits */
187     PMU_CTL0 &= ~((uint32_t)(PMU_CTL0_LPMOD | PMU_CTL0_LDNP));
188 
189     /* configure low drive mode in Sleep mode */
190     if(PMU_LDNP_LOWDRIVE == lowdrive) {
191         PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDNP);
192     }
193 
194     /* select WFI or WFE command to enter sleep mode */
195     if(WFI_CMD == sleepmodecmd) {
196         __WFI();
197     } else {
198         __WFE();
199     }
200 }
201 
202 /*!
203     \brief      PMU work at Deep-sleep mode
204     \param[in]  lowdrive: low-driver mode when use normal power LDO in Deep-sleep mode
205                 only one parameter can be selected which is shown as below:
206       \arg        PMU_LDNPDSP_NORMALDRIVE: low-driver mode disable
207       \arg        PMU_LDNPDSP_LOWDRIVE: low-driver mode enable
208     \param[in]  deepsleepmodecmd: deepsleep mode command
209                 only one parameter can be selected which is shown as below:
210       \arg        WFI_CMD: use WFI command
211       \arg        WFE_CMD: use WFE command
212     \param[in]  deepsleepmode: deepsleep mode
213                 only one parameter can be selected which is shown as below:
214       \arg        PMU_DEEPSLEEP: Deep-sleep mode
215       \arg        PMU_DEEPSLEEP1: Deep-sleep mode 1
216       \arg        PMU_DEEPSLEEP2: Deep-sleep mode 2
217     \param[out] none
218     \retval     none
219 */
pmu_to_deepsleepmode(uint32_t lowdrive,uint8_t deepsleepmodecmd,uint8_t deepsleepmode)220 void pmu_to_deepsleepmode(uint32_t lowdrive, uint8_t deepsleepmodecmd, uint8_t deepsleepmode)
221 {
222     /* clear lowpower mode and low drive bits */
223     PMU_CTL0 &= ~((uint32_t)(PMU_CTL0_LPMOD | PMU_CTL0_LDNPDSP));
224 
225     /* low drive mode config in Deep-sleep mode */
226     /* PMU_CTL0_LDNPDSP only useful in PMU_DEEPSLEEP */
227     if((PMU_LDNPDSP_LOWDRIVE == lowdrive) && (PMU_DEEPSLEEP == deepsleepmode)) {
228         PMU_CTL0 |= (uint32_t)(PMU_CTL0_LDNPDSP);
229     }
230 
231     /* configure Deep-sleep mode */
232     PMU_CTL0 |= deepsleepmode;
233 
234     /* set sleepdeep bit of Cortex-M23 system control register */
235     SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
236 
237     /* select WFI or WFE command to enter Deep-sleep mode */
238     if(WFI_CMD == deepsleepmodecmd) {
239         __WFI();
240     } else {
241         __SEV();
242         __WFE();
243         __WFE();
244     }
245     /* reset sleepdeep bit of Cortex-M23 system control register */
246     SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
247 }
248 
249 /*!
250     \brief      pmu work at standby mode
251     \param[in]  standbymodecmd:
252                 only one parameter can be selected which is shown as below:
253       \arg        WFI_CMD: use WFI command
254       \arg        WFE_CMD: use WFE command
255     \param[out] none
256     \retval     none
257 */
pmu_to_standbymode(uint8_t standbymodecmd)258 void pmu_to_standbymode(uint8_t standbymodecmd)
259 {
260     /* set sleepdeep bit of Cortex-M23 system control register */
261     SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
262 
263     /* select the low-power mode to enter */
264     PMU_CTL0 |= PMU_STANDBY;
265 
266     /* reset wakeup flag and standby flag */
267     PMU_CTL0 |= PMU_CTL0_WURST | PMU_CTL0_STBRST;
268 
269     /* select WFI or WFE command to enter standby mode */
270     if(WFI_CMD == standbymodecmd) {
271         __WFI();
272     } else {
273         __WFE();
274         __WFE();
275     }
276 }
277 
278 /*!
279     \brief      enable wakeup pin
280     \param[in]  wakeup_pin: wakeup pin
281                 one or more parameters can be selected which are shown as below:
282       \arg        PMU_WAKEUP_PIN0: WKUP Pin 0 (PA0)
283       \arg        PMU_WAKEUP_PIN1: WKUP Pin 1 (PC13)
284       \arg        PMU_WAKEUP_PIN2: WKUP Pin 2 (PA2)
285       \arg        PMU_WAKEUP_PIN3: WKUP Pin 3 (PB2)
286       \arg        PMU_WAKEUP_PIN4: WKUP Pin 4 (PC6)
287     \param[out] none
288     \retval     none
289 */
pmu_wakeup_pin_enable(uint32_t wakeup_pin)290 void pmu_wakeup_pin_enable(uint32_t wakeup_pin)
291 {
292     PMU_CS |= wakeup_pin;
293 }
294 
295 /*!
296     \brief      disable wakeup pin
297     \param[in]  wakeup_pin: wakeup pin
298                 one or more parameters can be selected which are shown as below:
299       \arg        PMU_WAKEUP_PIN0: WKUP Pin 0 (PA0)
300       \arg        PMU_WAKEUP_PIN1: WKUP Pin 1 (PC13)
301       \arg        PMU_WAKEUP_PIN2: WKUP Pin 2 (PA2)
302       \arg        PMU_WAKEUP_PIN3: WKUP Pin 3 (PB2)
303       \arg        PMU_WAKEUP_PIN4: WKUP Pin 4 (PC6)
304     \param[out] none
305     \retval     none
306 */
pmu_wakeup_pin_disable(uint32_t wakeup_pin)307 void pmu_wakeup_pin_disable(uint32_t wakeup_pin)
308 {
309     PMU_CS &= ~(wakeup_pin);
310 }
311 
312 /*!
313     \brief      enable backup domain write
314     \param[in]  none
315     \param[out] none
316     \retval     none
317 */
pmu_backup_write_enable(void)318 void pmu_backup_write_enable(void)
319 {
320     PMU_CTL0 |= PMU_CTL0_BKPWEN;
321 }
322 
323 /*!
324     \brief      disable backup domain write
325     \param[in]  none
326     \param[out] none
327     \retval     none
328 */
pmu_backup_write_disable(void)329 void pmu_backup_write_disable(void)
330 {
331     PMU_CTL0 &= ~PMU_CTL0_BKPWEN;
332 }
333 
334 /*!
335     \brief      configure power state of SRAM1
336     \param[in]  state: power state of SRAM1
337                 only one parameter can be selected which is shown as below:
338       \arg        PMU_SRAM1_SLEEP: SRAM1 go to power-off
339       \arg        PMU_SRAM1_WAKE: SRAM1 wakeup
340     \param[out] none
341     \retval     none
342 */
pmu_sram_power_config(uint32_t state)343 void pmu_sram_power_config(uint32_t state)
344 {
345     PMU_CTL1 |= state;
346 }
347 
348 /*!
349     \brief      configure power state of COREOFF1 domain
350     \param[in]  state: power state of COREOFF1 domain
351                 only one parameter can be selected which is shown as below:
352       \arg        PMU_CORE1_SLEEP: COREOFF1 domain go to power-off
353       \arg        PMU_CORE1_WAKE: COREOFF1 domain wakeup
354     \param[out] none
355     \retval     none
356 */
pmu_core1_power_config(uint32_t state)357 void pmu_core1_power_config(uint32_t state)
358 {
359     PMU_CTL1 |= state;
360 }
361 
362 /*!
363     \brief      have retention register in Deep-sleep 2
364     \param[in]  none
365     \param[out] none
366     \retval     none
367 */
pmu_deepsleep2_retention_enable(void)368 void pmu_deepsleep2_retention_enable(void)
369 {
370     PMU_CTL1 &= ~PMU_CTL1_NRRD2;
371 }
372 
373 /*!
374     \brief      no retention register in Deep-sleep 2
375     \param[in]  none
376     \param[out] none
377     \retval     none
378 */
pmu_deepsleep2_retention_disable(void)379 void pmu_deepsleep2_retention_disable(void)
380 {
381     PMU_CTL1 |= PMU_CTL1_NRRD2;
382 }
383 
384 /*!
385     \brief      configure SRAM1 power state when enter Deep-sleep 2
386     \param[in]  state: power state of SRAM1 when enters Deep-sleep 2 mode
387                 only one parameter can be selected which is shown as below:
388       \arg        PMU_SRAM1_POWER_OFF: SRAM1 power-off
389       \arg        PMU_SRAM1_POWER_REMAIN: SRAM1 power same as Run/Run1/Run2 mode
390     \param[out] none
391     \retval     none
392 */
pmu_deepsleep2_sram_power_config(uint32_t state)393 void pmu_deepsleep2_sram_power_config(uint32_t state)
394 {
395     PMU_CTL1 &= ~PMU_CTL1_SRAM1PD2;
396     PMU_CTL1 |= state;
397 }
398 
399 /*!
400     \brief      configure IRC16M counter before enter Deep-sleep mode
401     \param[in]  wait_time: 0x0~0x1F, IRC16M counter before enter Deep-sleep mode
402     \param[out] none
403     \retval     none
404 */
pmu_deepsleep_wait_time_config(uint32_t wait_time)405 void pmu_deepsleep_wait_time_config(uint32_t wait_time)
406 {
407     PMU_PAR &= ~PMU_PAR_TSW_IRC16MCNT;
408     PMU_PAR |= (uint32_t)(wait_time << PAR_TSW_IRC16MCNT_OFFSET);
409 }
410 
411 /*!
412     \brief      use software value signal when wake up COREOFF1 domain
413     \param[in]  wakeup_time: 0x0~0xFF, wakeup time of power switch of COREOFF1 domain. 4 IRC16M clock step and the max value is 64us.
414     \param[out] none
415     \retval     none
416 */
pmu_wakeuptime_core1_software_enable(uint32_t wakeup_time)417 void pmu_wakeuptime_core1_software_enable(uint32_t wakeup_time)
418 {
419     PMU_PAR &= ~PMU_PAR_TWK_CORE1;
420     PMU_PAR |= (uint32_t)(wakeup_time << PAR_TWK_CORE1_OFFSET);
421     PMU_PAR |= PMU_PAR_TWKCORE1EN;
422 }
423 
424 /*!
425     \brief      use hardware ack signal when wake up COREOFF1 domain
426     \param[in]  none
427     \param[out] none
428     \retval     none
429 */
pmu_wakeuptime_core1_software_disable(void)430 void pmu_wakeuptime_core1_software_disable(void)
431 {
432     PMU_PAR &= ~PMU_PAR_TWKCORE1EN;
433 }
434 
435 /*!
436     \brief      use software value signal when wake up SRAM1
437     \param[in]  wakeup_time: 0x0~0xFF, wakeup time of power switch of SRAM1. The step is 4 IRC16M clock and the max value is 64us.
438     \param[out] none
439     \retval     none
440 */
pmu_wakeuptime_sram_software_enable(uint32_t wakeup_time)441 void pmu_wakeuptime_sram_software_enable(uint32_t wakeup_time)
442 {
443     PMU_PAR &= ~PMU_PAR_TWK_SRAM1;
444     PMU_PAR |= (uint32_t)(wakeup_time << PAR_TWK_SRAM1_OFFSET);
445     PMU_PAR |= PMU_PAR_TWKSRAM1EN;
446 }
447 
448 /*!
449     \brief      use hardware ack signal when wake up SRAM1
450     \param[in]  none
451     \param[out] none
452     \retval     none
453 */
pmu_wakeuptime_sram_software_disable(void)454 void pmu_wakeuptime_sram_software_disable(void)
455 {
456     PMU_PAR &= ~PMU_PAR_TWKSRAM1EN;
457 }
458 
459 /*!
460     \brief      use software value signal when wake up Deep-sleep 2
461     \param[in]  wakeup_time: 0x0~0xFF, wakeup time of power switch of COREOFF0 domain. The step is 2 IRC16M clock and the max value is 32us.
462     \param[out] none
463     \retval     none
464 */
pmu_wakeuptime_deepsleep2_software_enable(uint32_t wakeup_time)465 void pmu_wakeuptime_deepsleep2_software_enable(uint32_t wakeup_time)
466 {
467     PMU_PAR &= ~PMU_PAR_TWK_CORE0;
468     PMU_PAR |= wakeup_time;
469     PMU_PAR |= PMU_PAR_TWKEN;
470 }
471 
472 /*!
473     \brief      use hardware ack signal when wake up Deep-sleep 2
474     \param[in]  none
475     \param[out] none
476     \retval     none
477 */
pmu_wakeuptime_deepsleep2_software_disable(void)478 void pmu_wakeuptime_deepsleep2_software_disable(void)
479 {
480     PMU_PAR &= ~PMU_PAR_TWKEN;
481 }
482 
483 /*!
484     \brief      get PMU flag status
485     \param[in]  flag: PMU flags
486                 only one parameter can be selected which is shown as below:
487       \arg        PMU_FLAG_WAKEUP: wakeup flag
488       \arg        PMU_FLAG_STANDBY: standby flag
489       \arg        PMU_FLAG_LVD: lvd flag
490       \arg        PMU_FLAG_LDOVSRF: LDO voltage select ready flag
491       \arg        PMU_FLAG_NPRDY: normal-power LDO ready flag
492       \arg        PMU_FLAG_LPRDY: low-power LDO ready flag
493       \arg        PMU_FLAG_SRAM1_SLEEP: SRAM1 is in sleep state flag
494       \arg        PMU_FLAG_SRAM1_ACTIVE: SRAM1 is in active state flag
495       \arg        PMU_FLAG_CORE1_SLEEP: COREOFF1 domain is in sleep state flag
496       \arg        PMU_FLAG_CORE1_ACTIVE: COREOFF1 domain is in active state flag
497       \arg        PMU_FLAG_DEEPSLEEP_2: Deep-sleep 2 mode status flag
498     \param[out] none
499     \retval     FlagStatus: SET or RESET
500 */
pmu_flag_get(uint32_t flag)501 FlagStatus pmu_flag_get(uint32_t flag)
502 {
503     FlagStatus ret = RESET;
504 
505     if(RESET != (flag & BIT(31))) {
506         flag &= ~BIT(31);
507         if(PMU_STAT & flag) {
508             ret = SET;
509         }
510     } else {
511         if(PMU_CS & flag) {
512             ret = SET;
513         }
514     }
515     return ret;
516 }
517 
518 /*!
519     \brief      clear PMU flag status
520     \param[in]  flag: PMU flags
521                 only one parameter can be selected which is shown as below:
522       \arg        PMU_FLAG_WAKEUP: wakeup flag
523       \arg        PMU_FLAG_STANDBY: standby flag
524       \arg        PMU_FLAG_DEEPSLEEP_2: Deep-sleep 2 mode status flag
525     \param[out] none
526     \retval     none
527 */
pmu_flag_clear(uint32_t flag)528 void pmu_flag_clear(uint32_t flag)
529 {
530     switch(flag) {
531     case PMU_FLAG_WAKEUP:
532         /* clear wakeup flag */
533         PMU_CTL0 |= PMU_CTL0_WURST;
534         break;
535     case PMU_FLAG_STANDBY:
536         /* clear standby flag */
537         PMU_CTL0 |= PMU_CTL0_STBRST;
538         break;
539     case PMU_FLAG_DEEPSLEEP_2:
540         /* clear Deep-sleep 2 mode status flag */
541         PMU_STAT &= ~PMU_STAT_DPF2;
542         break;
543     default :
544         break;
545     }
546 }
547