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