1 /*!
2     \file    gd32f3x0_pmu.c
3     \brief   PMU driver
4 
5     \version 2017-06-06, V1.0.0, firmware for GD32F3x0
6     \version 2019-06-01, V2.0.0, firmware for GD32F3x0
7     \version 2020-09-30, V2.1.0, firmware for GD32F3x0
8     \version 2021-03-30, V1.0.0, firmware for GD32F3x0
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 "gd32f3x0_pmu.h"
39 
40 
41 /*!
42     \brief      reset PMU register
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                 only one parameter can be selected which is shown as below:
58       \arg        PMU_LVDT_0: voltage threshold is 2.1V
59       \arg        PMU_LVDT_1: voltage threshold is 2.3V
60       \arg        PMU_LVDT_2: voltage threshold is 2.4V
61       \arg        PMU_LVDT_3: voltage threshold is 2.6V
62       \arg        PMU_LVDT_4: voltage threshold is 2.7V
63       \arg        PMU_LVDT_5: voltage threshold is 2.9V
64       \arg        PMU_LVDT_6: voltage threshold is 3.0V
65       \arg        PMU_LVDT_7: voltage threshold is 3.1V
66     \param[out] none
67     \retval     none
68 */
pmu_lvd_select(uint32_t lvdt_n)69 void pmu_lvd_select(uint32_t lvdt_n)
70 {
71     /* disable LVD */
72     PMU_CTL &= ~PMU_CTL_LVDEN;
73     /* clear LVDT bits */
74     PMU_CTL &= ~PMU_CTL_LVDT;
75     /* set LVDT bits according to lvdt_n */
76     PMU_CTL |= lvdt_n;
77     /* enable LVD */
78     PMU_CTL |= PMU_CTL_LVDEN;
79 }
80 
81 /*!
82     \brief      select LDO output voltage
83                 these bits set by software when the main PLL closed
84     \param[in]  ldo_output:
85                 only one parameter can be selected which is shown as below:
86       \arg        PMU_LDOVS_LOW: LDO output voltage low mode
87       \arg        PMU_LDOVS_MID: LDO output voltage mid mode
88       \arg        PMU_LDOVS_HIGH: LDO output voltage high mode
89     \param[out] none
90     \retval     none
91 */
pmu_ldo_output_select(uint32_t ldo_output)92 void pmu_ldo_output_select(uint32_t ldo_output)
93 {
94     PMU_CTL &= ~PMU_CTL_LDOVS;
95     PMU_CTL |= ldo_output;
96 }
97 
98 /*!
99     \brief      disable PMU lvd
100     \param[in]  none
101     \param[out] none
102     \retval     none
103 */
pmu_lvd_disable(void)104 void pmu_lvd_disable(void)
105 {
106     /* disable LVD */
107     PMU_CTL &= ~PMU_CTL_LVDEN;
108 }
109 
110 /*!
111     \brief      enable low-driver mode in deep-sleep mode
112     \param[in]  none
113     \param[out] none
114     \retval     none
115 */
pmu_lowdriver_mode_enable(void)116 void pmu_lowdriver_mode_enable(void)
117 {
118     PMU_CTL &= ~PMU_CTL_LDEN;
119     PMU_CTL |= PMU_LOWDRIVER_ENABLE;
120 }
121 
122 /*!
123     \brief      disable low-driver mode in deep-sleep mode
124     \param[in]  none
125     \param[out] none
126     \retval     none
127 */
pmu_lowdriver_mode_disable(void)128 void pmu_lowdriver_mode_disable(void)
129 {
130     PMU_CTL &= ~PMU_CTL_LDEN;
131     PMU_CTL |= PMU_LOWDRIVER_DISABLE;
132 }
133 
134 /*!
135     \brief      enable high-driver mode
136                 this bit set by software only when IRC8M or HXTAL used as system clock
137     \param[in]  none
138     \param[out] none
139     \retval     none
140 */
pmu_highdriver_mode_enable(void)141 void pmu_highdriver_mode_enable(void)
142 {
143     PMU_CTL |= PMU_CTL_HDEN;
144 }
145 
146 /*!
147     \brief      disable high-driver mode
148     \param[in]  none
149     \param[out] none
150     \retval     none
151 */
pmu_highdriver_mode_disable(void)152 void pmu_highdriver_mode_disable(void)
153 {
154     PMU_CTL &= ~PMU_CTL_HDEN;
155 }
156 
157 /*!
158     \brief      switch high-driver mode
159                 this bit set by software only when IRC8M or HXTAL used as system clock
160     \param[in]  highdr_switch:
161                 only one parameter can be selected which is shown as below:
162       \arg        PMU_HIGHDR_SWITCH_NONE: disable high-driver mode switch
163       \arg        PMU_HIGHDR_SWITCH_EN: enable high-driver mode switch
164     \param[out] none
165     \retval     none
166 */
pmu_highdriver_switch_select(uint32_t highdr_switch)167 void pmu_highdriver_switch_select(uint32_t highdr_switch)
168 {
169     /* wait for HDRF flag to be set */
170     while(SET != pmu_flag_get(PMU_FLAG_HDR)){
171     }
172     PMU_CTL &= ~PMU_CTL_HDS;
173     PMU_CTL |= highdr_switch;
174 }
175 
176 /*!
177     \brief      low-driver mode when use low power LDO
178     \param[in]  mode:
179                 only one parameter can be selected which is shown as below:
180       \arg        PMU_NORMALDR_LOWPWR: normal-driver when use low power LDO
181       \arg        PMU_LOWDR_LOWPWR: low-driver mode enabled when LDEN is 11 and use low power LDO
182     \param[out] none
183     \retval     none
184 */
pmu_lowpower_driver_config(uint32_t mode)185 void pmu_lowpower_driver_config(uint32_t mode)
186 {
187     PMU_CTL &= ~PMU_CTL_LDLP;
188     PMU_CTL |= mode;
189 }
190 
191 /*!
192     \brief      low-driver mode when use normal power LDO
193     \param[in]  mode:
194                 only one parameter can be selected which is shown as below:
195       \arg        PMU_NORMALDR_NORMALPWR: normal-driver when use low power LDO
196       \arg        PMU_LOWDR_NORMALPWR: low-driver mode enabled when LDEN is 11 and use low power LDO
197     \param[out] none
198     \retval     none
199 */
pmu_normalpower_driver_config(uint32_t mode)200 void pmu_normalpower_driver_config(uint32_t mode)
201 {
202     PMU_CTL &= ~PMU_CTL_LDNP;
203     PMU_CTL |= mode;
204 }
205 
206 /*!
207     \brief      PMU work at sleep mode
208     \param[in]  sleepmodecmd:
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[out] none
213     \retval     none
214 */
pmu_to_sleepmode(uint8_t sleepmodecmd)215 void pmu_to_sleepmode(uint8_t sleepmodecmd)
216 {
217     /* clear sleepdeep bit of Cortex-M4 system control register */
218     SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
219 
220     /* select WFI or WFE command to enter sleep mode */
221     if(WFI_CMD == sleepmodecmd){
222         __WFI();
223     }else{
224         __WFE();
225     }
226 }
227 
228 /*!
229     \brief      PMU work at deepsleep mode
230     \param[in]  ldo:
231                 only one parameter can be selected which is shown as below:
232       \arg        PMU_LDO_NORMAL: LDO operates normally when pmu enter deepsleep mode
233       \arg        PMU_LDO_LOWPOWER: LDO work at low power mode when pmu enter deepsleep mode
234     \param[in]  deepsleepmodecmd:
235                 only one parameter can be selected which is shown as below:
236       \arg        WFI_CMD: use WFI command
237       \arg        WFE_CMD: use WFE command
238     \param[out] none
239     \retval     none
240 */
pmu_to_deepsleepmode(uint32_t ldo,uint8_t deepsleepmodecmd)241 void pmu_to_deepsleepmode(uint32_t ldo,uint8_t deepsleepmodecmd)
242 {
243     static uint32_t reg_snap[ 4 ];
244     /* clear stbmod and ldolp bits */
245     PMU_CTL &= ~((uint32_t)(PMU_CTL_STBMOD | PMU_CTL_LDOLP));
246 
247     /* set ldolp bit according to pmu_ldo */
248     PMU_CTL |= ldo;
249 
250     /* set sleepdeep bit of Cortex-M4 system control register */
251     SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
252 
253     reg_snap[0] = REG32(0xE000E010U);
254     reg_snap[1] = REG32(0xE000E100U);
255     reg_snap[2] = REG32(0xE000E104U);
256     reg_snap[3] = REG32(0xE000E108U);
257 
258     REG32(0xE000E010U) &= 0x00010004U;
259     REG32(0xE000E180U)  = 0XB7FFEF19U;
260     REG32(0xE000E184U)  = 0XFFFFFBFFU;
261     REG32(0xE000E188U)  = 0xFFFFFFFFU;
262 
263     /* select WFI or WFE command to enter deepsleep mode */
264     if(WFI_CMD == deepsleepmodecmd){
265         __WFI();
266     }else{
267         __SEV();
268         __WFE();
269         __WFE();
270     }
271 
272     REG32(0xE000E010U) = reg_snap[0 ];
273     REG32(0xE000E100U) = reg_snap[1];
274     REG32(0xE000E104U) = reg_snap[2];
275     REG32(0xE000E108U) = reg_snap[3];
276 
277     /* reset sleepdeep bit of Cortex-M4 system control register */
278     SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
279 }
280 
281 /*!
282     \brief      pmu work at standby mode
283     \param[in]  standbymodecmd:
284                 only one parameter can be selected which is shown as below:
285       \arg        WFI_CMD: use WFI command
286       \arg        WFE_CMD: use WFE command
287     \param[out] none
288     \retval     none
289 */
pmu_to_standbymode(uint8_t standbymodecmd)290 void pmu_to_standbymode(uint8_t standbymodecmd)
291 {
292     /* set sleepdeep bit of Cortex-M4 system control register */
293     SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
294 
295     /* set stbmod bit */
296     PMU_CTL |= PMU_CTL_STBMOD;
297 
298     /* reset wakeup flag */
299     PMU_CTL |= PMU_CTL_WURST;
300 
301     /* select WFI or WFE command to enter standby mode */
302     if(WFI_CMD == standbymodecmd){
303         __WFI();
304     }else{
305         __WFE();
306         __WFE();
307     }
308 }
309 
310 /*!
311     \brief      enable wakeup pin
312     \param[in]  wakeup_pin:
313                 one or more parameters can be selected which are shown as below:
314       \arg        PMU_WAKEUP_PIN0: WKUP Pin 0 (PA0)
315       \arg        PMU_WAKEUP_PIN1: WKUP Pin 1 (PC13)
316       \arg        PMU_WAKEUP_PIN4: WKUP Pin 4 (PC5)
317       \arg        PMU_WAKEUP_PIN5: WKUP Pin 5 (PB5)
318       \arg        PMU_WAKEUP_PIN6: WKUP Pin 6 (PB15)
319     \param[out] none
320     \retval     none
321 */
pmu_wakeup_pin_enable(uint32_t wakeup_pin)322 void pmu_wakeup_pin_enable(uint32_t wakeup_pin)
323 {
324     PMU_CS |= wakeup_pin;
325 }
326 
327 /*!
328     \brief      disable wakeup pin
329     \param[in]  wakeup_pin:
330                 one or more parameters can be selected which are shown as below:
331       \arg        PMU_WAKEUP_PIN0: WKUP Pin 0 (PA0)
332       \arg        PMU_WAKEUP_PIN1: WKUP Pin 1 (PC13)
333       \arg        PMU_WAKEUP_PIN4: WKUP Pin 4 (PC5)
334       \arg        PMU_WAKEUP_PIN5: WKUP Pin 5 (PB5)
335       \arg        PMU_WAKEUP_PIN6: WKUP Pin 6 (PB15)
336     \param[out] none
337     \retval     none
338 */
pmu_wakeup_pin_disable(uint32_t wakeup_pin)339 void pmu_wakeup_pin_disable(uint32_t wakeup_pin)
340 {
341     PMU_CS &= ~(wakeup_pin);
342 }
343 
344 /*!
345     \brief      enable backup domain write
346     \param[in]  none
347     \param[out] none
348     \retval     none
349 */
pmu_backup_write_enable(void)350 void pmu_backup_write_enable(void)
351 {
352     PMU_CTL |= PMU_CTL_BKPWEN;
353 }
354 
355 /*!
356     \brief      disable backup domain write
357     \param[in]  none
358     \param[out] none
359     \retval     none
360 */
pmu_backup_write_disable(void)361 void pmu_backup_write_disable(void)
362 {
363     PMU_CTL &= ~PMU_CTL_BKPWEN;
364 }
365 
366 /*!
367     \brief      clear flag bit
368     \param[in]  flag_clear:
369                 one or more parameters can be selected which are shown as below:
370       \arg        PMU_FLAG_RESET_WAKEUP: reset wakeup flag
371       \arg        PMU_FLAG_RESET_STANDBY: reset standby flag
372     \param[out] none
373     \retval     none
374 */
pmu_flag_clear(uint32_t flag_clear)375 void pmu_flag_clear(uint32_t flag_clear)
376 {
377     if(RESET != (flag_clear & PMU_FLAG_RESET_WAKEUP)){
378         /* reset wakeup flag */
379         PMU_CTL |= PMU_CTL_WURST;
380     }
381     if(RESET != (flag_clear & PMU_FLAG_RESET_STANDBY)){
382         /* reset standby flag */
383         PMU_CTL |= PMU_CTL_STBRST;
384     }
385 }
386 
387 /*!
388     \brief      get flag state
389     \param[in]  flag:
390                 only one parameter can be selected which is shown as below:
391       \arg        PMU_FLAG_WAKEUP: wakeup flag
392       \arg        PMU_FLAG_STANDBY: standby flag
393       \arg        PMU_FLAG_LVD: lvd flag
394       \arg        PMU_FLAG_LDOVSR: LDO voltage select ready flag
395       \arg        PMU_FLAG_HDR: high-driver ready flag
396       \arg        PMU_FLAG_HDSR: high-driver switch ready flag
397       \arg        PMU_FLAG_LDR: low-driver mode ready flag
398     \param[out] none
399     \retval     FlagStatus SET or RESET
400 */
pmu_flag_get(uint32_t flag)401 FlagStatus pmu_flag_get(uint32_t flag)
402 {
403     FlagStatus ret_status = RESET;
404 
405     if(PMU_CS & flag){
406         ret_status = SET;
407     }
408 
409     return ret_status;
410 }
411