1 /*!
2     \file    gd32f403_pmu.c
3     \brief   PMU driver
4 
5     \version 2017-02-10, V1.0.0, firmware for GD32F403
6     \version 2018-12-25, V2.0.0, firmware for GD32F403
7     \version 2020-09-30, V2.1.0, firmware for GD32F403
8 */
9 
10 /*
11     Copyright (c) 2020, GigaDevice Semiconductor Inc.
12 
13     Redistribution and use in source and binary forms, with or without modification,
14 are permitted provided that the following conditions are met:
15 
16     1. Redistributions of source code must retain the above copyright notice, this
17        list of conditions and the following disclaimer.
18     2. Redistributions in binary form must reproduce the above copyright notice,
19        this list of conditions and the following disclaimer in the documentation
20        and/or other materials provided with the distribution.
21     3. Neither the name of the copyright holder nor the names of its contributors
22        may be used to endorse or promote products derived from this software without
23        specific prior written permission.
24 
25     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
32 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
34 OF SUCH DAMAGE.
35 */
36 
37 #include "gd32f403_pmu.h"
38 
39 /*!
40     \brief      reset PMU register
41     \param[in]  none
42     \param[out] none
43     \retval     none
44 */
pmu_deinit(void)45 void pmu_deinit(void)
46 {
47     /* reset PMU */
48     rcu_periph_reset_enable(RCU_PMURST);
49     rcu_periph_reset_disable(RCU_PMURST);
50 }
51 
52 /*!
53     \brief      select low voltage detector threshold
54     \param[in]  lvdt_n:
55       \arg        PMU_LVDT_0: voltage threshold is 2.1V
56       \arg        PMU_LVDT_1: voltage threshold is 2.3V
57       \arg        PMU_LVDT_2: voltage threshold is 2.4V
58       \arg        PMU_LVDT_3: voltage threshold is 2.6V
59       \arg        PMU_LVDT_4: voltage threshold is 2.7V
60       \arg        PMU_LVDT_5: voltage threshold is 2.9V
61       \arg        PMU_LVDT_6: voltage threshold is 3.0V
62       \arg        PMU_LVDT_7: voltage threshold is 3.1V
63     \param[out] none
64     \retval     none
65 */
pmu_lvd_select(uint32_t lvdt_n)66 void pmu_lvd_select(uint32_t lvdt_n)
67 {
68     /* disable LVD */
69     PMU_CTL &= ~PMU_CTL_LVDEN;
70     /* clear LVDT bits */
71     PMU_CTL &= ~PMU_CTL_LVDT;
72     /* set LVDT bits according to lvdt_n */
73     PMU_CTL |= lvdt_n;
74     /* enable LVD */
75     PMU_CTL |= PMU_CTL_LVDEN;
76 }
77 
78 /*!
79     \brief      select LDO output voltage
80                 this bit set by software when the main PLL closed, before closing PLL, change the system clock to IRC16M or HXTAL
81     \param[in]  ldo_output:
82       \arg        PMU_LDOVS_LOW: low-driver mode enable in deep-sleep mode
83       \arg        PMU_LDOVS_MID: low-driver mode disable in deep-sleep mode
84       \arg        PMU_LDOVS_HIGH: low-driver mode disable in deep-sleep mode
85     \param[out] none
86     \retval     none
87 */
pmu_ldo_output_select(uint32_t ldo_output)88 void pmu_ldo_output_select(uint32_t ldo_output)
89 {
90     PMU_CTL &= ~PMU_CTL_LDOVS;
91     PMU_CTL |= ldo_output;
92 }
93 
94 /*!
95     \brief      switch high-driver mode
96                 this bit set by software only when IRC16M or HXTAL used as system clock
97     \param[in]  highdr_switch:
98       \arg        PMU_HIGHDR_SWITCH_NONE: disable high-driver mode switch
99       \arg        PMU_HIGHDR_SWITCH_EN: enable high-driver mode switch
100     \param[out] none
101     \retval     none
102 */
pmu_highdriver_switch_select(uint32_t highdr_switch)103 void pmu_highdriver_switch_select(uint32_t highdr_switch)
104 {
105     /* wait for HDRF flag set */
106     while(SET != pmu_flag_get(PMU_FLAG_HDRF)){
107     }
108     PMU_CTL &= ~PMU_CTL_HDS;
109     PMU_CTL |= highdr_switch;
110 }
111 
112 /*!
113     \brief      enable low-driver mode in deep-sleep mode
114     \param[in]  none
115     \param[out] none
116     \retval     none
117 */
pmu_lowdriver_mode_enable(void)118 void pmu_lowdriver_mode_enable(void)
119 {
120     PMU_CTL |= PMU_CTL_LDEN;
121 }
122 
123 /*!
124     \brief      disable low-driver mode in deep-sleep mode
125     \param[in]  none
126     \param[out] none
127     \retval     none
128 */
pmu_lowdriver_mode_disable(void)129 void pmu_lowdriver_mode_disable(void)
130 {
131     PMU_CTL &= ~PMU_CTL_LDEN;
132 }
133 
134 /*!
135     \brief      enable high-driver mode
136                 this bit set by software only when IRC16M 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      disable PMU lvd
159     \param[in]  none
160     \param[out] none
161     \retval     none
162 */
pmu_lvd_disable(void)163 void pmu_lvd_disable(void)
164 {
165     /* disable LVD */
166     PMU_CTL &= ~PMU_CTL_LVDEN;
167 }
168 
169 /*!
170     \brief      driver mode when use low power LDO
171     \param[in]  mode:
172       \arg        PMU_NORMALDR_LOWPWR:  normal driver when use low power LDO
173       \arg        PMU_LOWDR_LOWPWR:  low-driver mode enabled when LDEN is 11 and use low power LDO
174     \param[out] none
175     \retval     none
176 */
pmu_lowpower_driver_config(uint32_t mode)177 void pmu_lowpower_driver_config(uint32_t mode)
178 {
179     PMU_CTL &= ~PMU_CTL_LDLP;
180     PMU_CTL |= mode;
181 }
182 
183 /*!
184     \brief      driver mode when use normal power LDO
185     \param[in]  mode:
186       \arg        PMU_NORMALDR_NORMALPWR:  normal driver when use low power LDO
187       \arg        PMU_LOWDR_NORMALPWR:  low-driver mode enabled when LDEN is 11 and use low power LDO
188     \param[out] none
189     \retval     none
190 */
pmu_normalpower_driver_config(uint32_t mode)191 void pmu_normalpower_driver_config(uint32_t mode)
192 {
193     PMU_CTL &= ~PMU_CTL_LDNP;
194     PMU_CTL |= mode;
195 }
196 
197 /*!
198     \brief      PMU work at sleep mode
199     \param[in]  sleepmodecmd:
200       \arg        WFI_CMD: use WFI command
201       \arg        WFE_CMD: use WFE command
202     \param[out] none
203     \retval     none
204 */
pmu_to_sleepmode(uint8_t sleepmodecmd)205 void pmu_to_sleepmode(uint8_t sleepmodecmd)
206 {
207     /* clear sleepdeep bit of Cortex-M4 system control register */
208     SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
209 
210     /* select WFI or WFE command to enter sleep mode */
211     if(WFI_CMD == sleepmodecmd){
212         __WFI();
213     }else{
214         __WFE();
215     }
216 }
217 
218 /*!
219     \brief      PMU work at deepsleep mode
220     \param[in]  ldo
221       \arg        PMU_LDO_NORMAL: LDO normal work when pmu enter deepsleep mode
222       \arg        PMU_LDO_LOWPOWER: LDO work at low power mode when pmu enter deepsleep mode
223     \param[in]  deepsleepmodecmd:
224       \arg        WFI_CMD: use WFI command
225       \arg        WFE_CMD: use WFE command
226     \param[out] none
227     \retval     none
228 */
pmu_to_deepsleepmode(uint32_t ldo,uint8_t deepsleepmodecmd)229 void pmu_to_deepsleepmode(uint32_t ldo,uint8_t deepsleepmodecmd)
230 {
231     static uint32_t reg_snap[ 4 ];
232     /* clear stbmod and ldolp bits */
233     PMU_CTL &= ~((uint32_t)(PMU_CTL_STBMOD | PMU_CTL_LDOLP));
234 
235     /* set ldolp bit according to pmu_ldo */
236     PMU_CTL |= ldo;
237 
238     /* set sleepdeep bit of Cortex-M4 system control register */
239     SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
240 
241     reg_snap[ 0 ] = REG32( 0xE000E010U );
242     reg_snap[ 1 ] = REG32( 0xE000E100U );
243     reg_snap[ 2 ] = REG32( 0xE000E104U );
244     reg_snap[ 3 ] = REG32( 0xE000E108U );
245 
246     REG32( 0xE000E010U ) &= 0x00010004U;
247     REG32( 0xE000E180U )  = 0XFF7FF83DU;
248     REG32( 0xE000E184U )  = 0XFFFFF8FFU;
249     REG32( 0xE000E188U )  = 0xFFFFFFFFU;
250 
251     /* select WFI or WFE command to enter deepsleep mode */
252     if(WFI_CMD == deepsleepmodecmd){
253         __WFI();
254     }else{
255         __SEV();
256         __WFE();
257         __WFE();
258     }
259 
260     REG32( 0xE000E010U ) = reg_snap[ 0 ] ;
261     REG32( 0xE000E100U ) = reg_snap[ 1 ] ;
262     REG32( 0xE000E104U ) = reg_snap[ 2 ] ;
263     REG32( 0xE000E108U ) = reg_snap[ 3 ] ;
264 
265     /* reset sleepdeep bit of Cortex-M4 system control register */
266     SCB->SCR &= ~((uint32_t)SCB_SCR_SLEEPDEEP_Msk);
267 }
268 
269 /*!
270     \brief      pmu work at standby mode
271     \param[in]  standbymodecmd:
272       \arg        WFI_CMD: use WFI command
273       \arg        WFE_CMD: use WFE command
274     \param[out] none
275     \retval     none
276 */
pmu_to_standbymode(uint8_t standbymodecmd)277 void pmu_to_standbymode(uint8_t standbymodecmd)
278 {
279     /* set sleepdeep bit of Cortex-M4 system control register */
280     SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
281 
282     /* set stbmod bit */
283     PMU_CTL |= PMU_CTL_STBMOD;
284 
285     /* reset wakeup flag */
286     PMU_CTL |= PMU_CTL_WURST;
287 
288     /* select WFI or WFE command to enter standby mode */
289     if(WFI_CMD == standbymodecmd){
290         __WFI();
291     }else{
292         __WFE();
293     }
294 }
295 
296 /*!
297     \brief      clear flag bit
298     \param[in]  flag_reset:
299       \arg        PMU_FLAG_RESET_WAKEUP: reset wakeup flag
300       \arg        PMU_FLAG_RESET_STANDBY: reset standby flag
301     \param[out] none
302     \retval     none
303 */
pmu_flag_clear(uint32_t flag_reset)304 void pmu_flag_clear(uint32_t flag_reset)
305 {
306     switch(flag_reset){
307     case PMU_FLAG_RESET_WAKEUP:
308         /* reset wakeup flag */
309         PMU_CTL |= PMU_CTL_WURST;
310         break;
311     case PMU_FLAG_RESET_STANDBY:
312         /* reset standby flag */
313         PMU_CTL |= PMU_CTL_STBRST;
314         break;
315     default :
316         break;
317     }
318 }
319 
320 /*!
321     \brief      get flag state
322     \param[in]  flag:
323       \arg        PMU_FLAG_WAKEUP: wakeup flag
324       \arg        PMU_FLAG_STANDBY: standby flag
325       \arg        PMU_FLAG_LVD: lvd flag
326       \arg        PMU_FLAG_LDOVSRF: LDO voltage select ready flag
327       \arg        PMU_FLAG_HDRF: high-driver ready flag
328       \arg        PMU_FLAG_HDSRF: high-driver switch ready flag
329       \arg        PMU_FLAG_LDRF: low-driver mode ready flag
330     \param[out] none
331     \retval     FlagStatus SET or RESET
332 */
pmu_flag_get(uint32_t flag)333 FlagStatus pmu_flag_get(uint32_t flag)
334 {
335     if(PMU_CS & flag){
336         return  SET;
337     }else{
338         return  RESET;
339     }
340 }
341 
342 /*!
343     \brief      enable backup domain write
344     \param[in]  none
345     \param[out] none
346     \retval     none
347 */
pmu_backup_write_enable(void)348 void pmu_backup_write_enable(void)
349 {
350     PMU_CTL |= PMU_CTL_BKPWEN;
351 }
352 
353 /*!
354     \brief      disable backup domain write
355     \param[in]  none
356     \param[out] none
357     \retval     none
358 */
pmu_backup_write_disable(void)359 void pmu_backup_write_disable(void)
360 {
361     PMU_CTL &= ~PMU_CTL_BKPWEN;
362 }
363 
364 /*!
365     \brief      enable wakeup pin
366     \param[in]  none
367     \param[out] none
368     \retval     none
369 */
pmu_wakeup_pin_enable(void)370 void pmu_wakeup_pin_enable(void)
371 {
372     PMU_CS |= PMU_CS_WUPEN;
373 }
374 
375 /*!
376     \brief      disable wakeup pin
377     \param[in]  none
378     \param[out] none
379     \retval     none
380 */
pmu_wakeup_pin_disable(void)381 void pmu_wakeup_pin_disable(void)
382 {
383     PMU_CS &= ~PMU_CS_WUPEN;
384 }
385