1 /*!
2     \file    gd32f4xx_pmu.c
3     \brief   PMU driver
4 
5     \version 2016-08-15, V1.0.0, firmware for GD32F4xx
6     \version 2018-12-12, V2.0.0, firmware for GD32F4xx
7     \version 2020-09-30, V2.1.0, firmware for GD32F4xx
8     \version 2022-03-09, V3.0.0, firmware for GD32F4xx
9 */
10 
11 /*
12     Copyright (c) 2022, 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 "gd32f4xx_pmu.h"
39 #include "core_cm4.h"
40 
41 /*!
42     \brief      reset PMU registers
43     \param[in]  none
44     \param[out] none
45     \retval     none
46 */
pmu_deinit(void)47 void pmu_deinit(void)
48 {
49     /* reset PMU */
50     rcu_periph_reset_enable(RCU_PMURST);
51     rcu_periph_reset_disable(RCU_PMURST);
52 }
53 
54 /*!
55     \brief      select low voltage detector threshold
56     \param[in]  lvdt_n:
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_CTL &= ~PMU_CTL_LVDEN;
72     /* clear LVDT bits */
73     PMU_CTL &= ~PMU_CTL_LVDT;
74     /* set LVDT bits according to pmu_lvdt_n */
75     PMU_CTL |= lvdt_n;
76     /* enable LVD */
77     PMU_CTL |= PMU_CTL_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_CTL &= ~PMU_CTL_LVDEN;
90 }
91 
92 /*!
93     \brief      select LDO output voltage
94                 this bit set by software when the main PLL closed, before closing PLL, change the system clock to IRC16M or HXTAL
95     \param[in]  ldo_output:
96       \arg        PMU_LDOVS_LOW: low-driver mode enable in deep-sleep mode
97       \arg        PMU_LDOVS_MID: mid-driver mode disable in deep-sleep mode
98       \arg        PMU_LDOVS_HIGH: high-driver mode disable in deep-sleep mode
99     \param[out] none
100     \retval     none
101 */
pmu_ldo_output_select(uint32_t ldo_output)102 void pmu_ldo_output_select(uint32_t ldo_output)
103 {
104     PMU_CTL &= ~PMU_CTL_LDOVS;
105     PMU_CTL |= ldo_output;
106 }
107 
108 /*!
109     \brief      enable high-driver mode
110                 this bit set by software only when IRC16M or HXTAL used as system clock
111     \param[in]  none
112     \param[out] none
113     \retval     none
114 */
pmu_highdriver_mode_enable(void)115 void pmu_highdriver_mode_enable(void)
116 {
117     PMU_CTL |= PMU_CTL_HDEN;
118 }
119 
120 /*!
121     \brief      disable high-driver mode
122     \param[in]  none
123     \param[out] none
124     \retval     none
125 */
pmu_highdriver_mode_disable(void)126 void pmu_highdriver_mode_disable(void)
127 {
128     PMU_CTL &= ~PMU_CTL_HDEN;
129 }
130 
131 /*!
132     \brief      switch high-driver mode
133                 this bit set by software only when IRC16M or HXTAL used as system clock
134     \param[in]  highdr_switch:
135       \arg        PMU_HIGHDR_SWITCH_NONE: disable high-driver mode switch
136       \arg        PMU_HIGHDR_SWITCH_EN: enable high-driver mode switch
137     \param[out] none
138     \retval     none
139 */
pmu_highdriver_switch_select(uint32_t highdr_switch)140 void pmu_highdriver_switch_select(uint32_t highdr_switch)
141 {
142     /* wait for HDRF flag set */
143     while(SET != pmu_flag_get(PMU_FLAG_HDRF)) {
144     }
145     PMU_CTL &= ~PMU_CTL_HDS;
146     PMU_CTL |= highdr_switch;
147 }
148 
149 /*!
150     \brief      enable low-driver mode in deep-sleep
151     \param[in]  none
152     \param[out] none
153     \retval     none
154 */
pmu_lowdriver_mode_enable(void)155 void pmu_lowdriver_mode_enable(void)
156 {
157     PMU_CTL |= PMU_CTL_LDEN;
158 }
159 
160 /*!
161     \brief      disable low-driver mode in deep-sleep
162     \param[in]  none
163     \param[out] none
164     \retval     none
165 */
pmu_lowdriver_mode_disable(void)166 void pmu_lowdriver_mode_disable(void)
167 {
168     PMU_CTL &= ~PMU_CTL_LDEN;
169 }
170 
171 /*!
172     \brief      in deep-sleep mode, driver mode when use low power LDO
173     \param[in]  mode:
174       \arg        PMU_NORMALDR_LOWPWR:  normal driver when use low power LDO
175       \arg        PMU_LOWDR_LOWPWR:  low-driver mode enabled when LDEN is 11 and use low power LDO
176     \param[out] none
177     \retval     none
178 */
pmu_lowpower_driver_config(uint32_t mode)179 void pmu_lowpower_driver_config(uint32_t mode)
180 {
181     PMU_CTL &= ~PMU_CTL_LDLP;
182     PMU_CTL |= mode;
183 }
184 
185 /*!
186     \brief      in deep-sleep mode, driver mode when use normal power LDO
187     \param[in]  mode:
188       \arg        PMU_NORMALDR_NORMALPWR: normal driver when use normal power LDO
189       \arg        PMU_LOWDR_NORMALPWR: low-driver mode enabled when LDEN is 11 and use normal power LDO
190     \param[out] none
191     \retval     none
192 */
pmu_normalpower_driver_config(uint32_t mode)193 void pmu_normalpower_driver_config(uint32_t mode)
194 {
195     PMU_CTL &= ~PMU_CTL_LDNP;
196     PMU_CTL |= mode;
197 }
198 
199 /*!
200     \brief      PMU work in sleep mode
201     \param[in]  sleepmodecmd:
202       \arg        WFI_CMD: use WFI command
203       \arg        WFE_CMD: use WFE command
204     \param[out] none
205     \retval     none
206 */
pmu_to_sleepmode(uint8_t sleepmodecmd)207 void pmu_to_sleepmode(uint8_t sleepmodecmd)
208 {
209     /* clear sleepdeep bit of Cortex-M4 system control register */
210     SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
211 
212     /* select WFI or WFE command to enter sleep mode */
213     if(WFI_CMD == sleepmodecmd) {
214         __WFI();
215     } else {
216         __WFE();
217     }
218 }
219 
220 /*!
221     \brief      PMU work in deep-sleep mode
222     \param[in]  ldo
223       \arg        PMU_LDO_NORMAL: LDO normal work when pmu enter deep-sleep mode
224       \arg        PMU_LDO_LOWPOWER: LDO work at low power mode when pmu enter deep-sleep mode
225     \param[in]  lowdrive:
226                 only one parameter can be selected which is shown as below:
227       \arg        PMU_LOWDRIVER_DISABLE: Low-driver mode disable in deep-sleep mode
228       \arg        PMU_LOWDRIVER_ENABLE: Low-driver mode enable in deep-sleep mode
229     \param[in]  deepsleepmodecmd:
230       \arg        WFI_CMD: use WFI command
231       \arg        WFE_CMD: use WFE command
232     \param[out] none
233     \retval     none
234 */
pmu_to_deepsleepmode(uint32_t ldo,uint32_t lowdrive,uint8_t deepsleepmodecmd)235 void pmu_to_deepsleepmode(uint32_t ldo, uint32_t lowdrive, uint8_t deepsleepmodecmd)
236 {
237     static uint32_t reg_snap[4];
238     /* clear stbmod and ldolp bits */
239     PMU_CTL &= ~((uint32_t)(PMU_CTL_STBMOD | PMU_CTL_LDOLP | PMU_CTL_LDEN | PMU_CTL_LDNP | PMU_CTL_LDLP));
240 
241     /* set ldolp bit according to pmu_ldo */
242     PMU_CTL |= ldo;
243 
244     /* configure low drive mode in deep-sleep mode */
245     if(PMU_LOWDRIVER_ENABLE == lowdrive) {
246         if(PMU_LDO_NORMAL == ldo) {
247             PMU_CTL |= (uint32_t)(PMU_CTL_LDEN | PMU_CTL_LDNP);
248         } else {
249             PMU_CTL |= (uint32_t)(PMU_CTL_LDEN | PMU_CTL_LDLP);
250         }
251     }
252     /* set sleepdeep bit of Cortex-M4 system control register */
253     SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
254 
255     reg_snap[0] = REG32(0xE000E010U);
256     reg_snap[1] = REG32(0xE000E100U);
257     reg_snap[2] = REG32(0xE000E104U);
258     reg_snap[3] = REG32(0xE000E108U);
259 
260     REG32(0xE000E010U) &= 0x00010004U;
261     REG32(0xE000E180U)  = 0XFF7FF831U;
262     REG32(0xE000E184U)  = 0XBFFFF8FFU;
263     REG32(0xE000E188U)  = 0xFFFFEFFFU;
264 
265     /* select WFI or WFE command to enter deep-sleep mode */
266     if(WFI_CMD == deepsleepmodecmd) {
267         __WFI();
268     } else {
269         __SEV();
270         __WFE();
271         __WFE();
272     }
273 
274     REG32(0xE000E010U) = reg_snap[0];
275     REG32(0xE000E100U) = reg_snap[1];
276     REG32(0xE000E104U) = reg_snap[2];
277     REG32(0xE000E108U) = reg_snap[3];
278 
279     /* reset sleepdeep bit of Cortex-M4 system control register */
280     SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
281 }
282 
283 /*!
284     \brief      pmu work in standby mode
285     \param[in]  standbymodecmd:
286       \arg        WFI_CMD: use WFI command
287       \arg        WFE_CMD: use WFE command
288     \param[out] none
289     \retval     none
290 */
pmu_to_standbymode(uint8_t standbymodecmd)291 void pmu_to_standbymode(uint8_t standbymodecmd)
292 {
293     /* set sleepdeep bit of Cortex-M4 system control register */
294     SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
295 
296     /* set stbmod bit */
297     PMU_CTL |= PMU_CTL_STBMOD;
298 
299     /* reset wakeup flag */
300     PMU_CTL |= PMU_CTL_WURST;
301 
302     /* select WFI or WFE command to enter standby mode */
303     if(WFI_CMD == standbymodecmd) {
304         __WFI();
305     } else {
306         __WFE();
307         __WFE();
308     }
309 }
310 
311 /*!
312     \brief      enable PMU wakeup pin
313     \param[in]  none
314     \param[out] none
315     \retval     none
316 */
pmu_wakeup_pin_enable(void)317 void pmu_wakeup_pin_enable(void)
318 {
319     PMU_CS |= PMU_CS_WUPEN;
320 }
321 
322 /*!
323     \brief      disable PMU wakeup pin
324     \param[in]  none
325     \param[out] none
326     \retval     none
327 */
pmu_wakeup_pin_disable(void)328 void pmu_wakeup_pin_disable(void)
329 {
330     PMU_CS &= ~PMU_CS_WUPEN;
331 }
332 
333 /*!
334     \brief      backup SRAM LDO on
335     \param[in]  bkp_ldo:
336       \arg        PMU_BLDOON_OFF: backup SRAM LDO closed
337       \arg        PMU_BLDOON_ON: open the backup SRAM LDO
338     \param[out] none
339     \retval     none
340 */
pmu_backup_ldo_config(uint32_t bkp_ldo)341 void pmu_backup_ldo_config(uint32_t bkp_ldo)
342 {
343     PMU_CS &= ~PMU_CS_BLDOON;
344     PMU_CS |= bkp_ldo;
345 }
346 
347 /*!
348     \brief      enable write access to the registers in backup domain
349     \param[in]  none
350     \param[out] none
351     \retval     none
352 */
pmu_backup_write_enable(void)353 void pmu_backup_write_enable(void)
354 {
355     PMU_CTL |= PMU_CTL_BKPWEN;
356 }
357 
358 /*!
359     \brief      disable write access to the registers in backup domain
360     \param[in]  none
361     \param[out] none
362     \retval     none
363 */
pmu_backup_write_disable(void)364 void pmu_backup_write_disable(void)
365 {
366     PMU_CTL &= ~PMU_CTL_BKPWEN;
367 }
368 
369 /*!
370     \brief      get flag state
371     \param[in]  flag:
372       \arg        PMU_FLAG_WAKEUP: wakeup flag
373       \arg        PMU_FLAG_STANDBY: standby flag
374       \arg        PMU_FLAG_LVD: lvd flag
375       \arg        PMU_FLAG_BLDORF: backup SRAM LDO ready flag
376       \arg        PMU_FLAG_LDOVSRF: LDO voltage select ready flag
377       \arg        PMU_FLAG_HDRF: high-driver ready flag
378       \arg        PMU_FLAG_HDSRF: high-driver switch ready flag
379       \arg        PMU_FLAG_LDRF: low-driver mode ready flag
380     \param[out] none
381     \retval     FlagStatus: SET or RESET
382 */
pmu_flag_get(uint32_t flag)383 FlagStatus pmu_flag_get(uint32_t flag)
384 {
385     if(PMU_CS & flag) {
386         return SET;
387     } else {
388         return RESET;
389     }
390 }
391 
392 /*!
393     \brief      clear flag bit
394     \param[in]  flag:
395       \arg        PMU_FLAG_RESET_WAKEUP: reset wakeup flag
396       \arg        PMU_FLAG_RESET_STANDBY: reset standby flag
397     \param[out] none
398     \retval     none
399 */
pmu_flag_clear(uint32_t flag)400 void pmu_flag_clear(uint32_t flag)
401 {
402     switch(flag) {
403     case PMU_FLAG_RESET_WAKEUP:
404         /* reset wakeup flag */
405         PMU_CTL |= PMU_CTL_WURST;
406         break;
407     case PMU_FLAG_RESET_STANDBY:
408         /* reset standby flag */
409         PMU_CTL |= PMU_CTL_STBRST;
410         break;
411     default :
412         break;
413     }
414 }
415