1 /*
2 * Copyright (C) 2022-2024, Xiaohua Semiconductor Co., Ltd.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2023-06-12 CDT first version
9 * 2024-06-14 CDT Move common function SysTick_Configuration to _pm_run
10 */
11
12 #include <board.h>
13 #include <drv_pm.h>
14 #include <drv_common.h>
15 #include <drv_wktm.h>
16 #include <drv_config.h>
17
18 #if defined(RT_USING_PM)
19
20 #if defined(BSP_USING_PM)
21
22 #define LOG_TAG "drv_pm"
23 #include <drv_log.h>
24
25 #define IS_PWC_UNLOCKED() ((CM_PWC->FPRC & PWC_FPRC_FPRCB1) == PWC_FPRC_FPRCB1)
26
27 typedef void (* run_switch_func_type)(void);
28 typedef void (* sleep_enter_func_type)(void);
29 static void _sleep_enter_idle(void);
30 static void _sleep_enter_deep(void);
31 static void _sleep_enter_standby(void);
32 static void _sleep_enter_shutdown(void);
33 static void _run_switch_high_to_low(void);
34 static void _run_switch_low_to_high(void);
35
36 static run_switch_func_type _run_switch_func[PM_RUN_MODE_MAX][PM_RUN_MODE_MAX] =
37 {
38 {RT_NULL, RT_NULL, RT_NULL, _run_switch_high_to_low},
39 {RT_NULL, RT_NULL, RT_NULL, _run_switch_high_to_low},
40 {RT_NULL, RT_NULL, RT_NULL, RT_NULL},
41 {_run_switch_low_to_high, _run_switch_low_to_high, RT_NULL, RT_NULL},
42 };
43
44 static sleep_enter_func_type _sleep_enter_func[PM_SLEEP_MODE_MAX] =
45 {
46 RT_NULL,
47 _sleep_enter_idle,
48 RT_NULL,
49 _sleep_enter_deep,
50 _sleep_enter_standby,
51 _sleep_enter_shutdown,
52 };
53
_uart_console_reconfig(void)54 static void _uart_console_reconfig(void)
55 {
56 struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
57
58 rt_device_control(rt_console_get_device(), RT_DEVICE_CTRL_CONFIG, &config);
59 }
60
61 /**
62 * @brief Enter sleep mode.
63 * @param [in] u8SleepType specifies the type of enter sleep's command.
64 * @arg PWC_SLEEP_WFI Enter sleep mode by WFI, and wake-up by interrupt handle.
65 * @arg PWC_SLEEP_WFE_INT Enter sleep mode by WFE, and wake-up by interrupt request(SEVONPEND=1)
66 * @arg PWC_SLEEP_WFE_EVT Enter sleep mode by WFE, and wake-up by event(SEVONPEND=0).
67
68 * @retval None
69 */
pwc_sleep_enter(uint8_t u8SleepType)70 __WEAKDEF void pwc_sleep_enter(uint8_t u8SleepType)
71 {
72 DDL_ASSERT(IS_PWC_UNLOCKED());
73
74 CLR_REG16_BIT(CM_PWC->STPMCR, PWC_STPMCR_STOP);
75 CLR_REG8_BIT(CM_PWC->PWRC0, PWC_PWRC0_PWDN);
76
77 if (PWC_SLEEP_WFI == u8SleepType)
78 {
79 __WFI();
80 }
81 else
82 {
83 if (PWC_SLEEP_WFE_INT == u8SleepType)
84 {
85 SET_REG32_BIT(SCB->SCR, SCB_SCR_SEVONPEND_Msk);
86 }
87 else
88 {
89 CLR_REG32_BIT(SCB->SCR, SCB_SCR_SEVONPEND_Msk);
90 }
91 __SEV();
92 __WFE();
93 __WFE();
94 }
95 }
96
_sleep_enter_idle(void)97 static void _sleep_enter_idle(void)
98 {
99 struct pm_sleep_mode_idle_config sleep_idle_cfg = PM_SLEEP_IDLE_CFG;
100 pwc_sleep_enter(sleep_idle_cfg.pwc_sleep_type);
101 }
102
_sleep_enter_deep(void)103 static void _sleep_enter_deep(void)
104 {
105 struct pm_sleep_mode_deep_config sleep_deep_cfg = PM_SLEEP_DEEP_CFG;
106
107 RT_ASSERT(PM_SLEEP_CHECK(PM_SLEEP_MODE_DEEP));
108
109 (void)PWC_STOP_Config(&sleep_deep_cfg.cfg);
110
111 #if defined(HC32F4A0) || defined(HC32F460) || defined(HC32F448) || defined(HC32F4A8)
112 if (PWC_PWRC2_DVS == (READ_REG8(CM_PWC->PWRC2) & PWC_PWRC2_DVS))
113 {
114 CLR_REG8_BIT(CM_PWC->PWRC1, PWC_PWRC1_STPDAS);
115 }
116 else
117 {
118 SET_REG8_BIT(CM_PWC->PWRC1, PWC_PWRC1_STPDAS);
119 }
120 #endif
121 PWC_STOP_Enter(sleep_deep_cfg.pwc_stop_type);
122 }
123
_sleep_enter_standby(void)124 static void _sleep_enter_standby(void)
125 {
126 struct pm_sleep_mode_standby_config sleep_standby_cfg = PM_SLEEP_STANDBY_CFG;
127 RT_ASSERT(PM_SLEEP_CHECK(PM_SLEEP_MODE_SHUTDOWN));
128 RT_ASSERT(sleep_standby_cfg.cfg.u8Mode == PWC_PD_MD1 || sleep_standby_cfg.cfg.u8Mode == PWC_PD_MD2);
129
130 (void)PWC_PD_Config(&sleep_standby_cfg.cfg);
131 PWC_PD_ClearWakeupStatus(PWC_PD_WKUP_FLAG_ALL);
132 __set_FAULTMASK(1);
133 PWC_PD_Enter();
134 }
135
_sleep_enter_shutdown(void)136 static void _sleep_enter_shutdown(void)
137 {
138 struct pm_sleep_mode_shutdown_config sleep_shutdown_cfg = PM_SLEEP_SHUTDOWN_CFG;
139 RT_ASSERT(PM_SLEEP_CHECK(PM_SLEEP_MODE_SHUTDOWN));
140 RT_ASSERT(sleep_shutdown_cfg.cfg.u8Mode == PWC_PD_MD3 || sleep_shutdown_cfg.cfg.u8Mode == PWC_PD_MD4);
141
142 (void)PWC_PD_Config(&sleep_shutdown_cfg.cfg);
143 PWC_PD_ClearWakeupStatus(PWC_PD_WKUP_FLAG_ALL);
144 __set_FAULTMASK(1);
145 PWC_PD_Enter();
146 }
147
148 /**
149 * @param pm pointer to power manage structure
150 */
_pm_sleep(struct rt_pm * pm,uint8_t mode)151 static void _pm_sleep(struct rt_pm *pm, uint8_t mode)
152 {
153 RT_ASSERT(mode < PM_SLEEP_MODE_MAX);
154
155 if (_sleep_enter_func[mode] != NULL)
156 {
157 _sleep_enter_func[mode]();
158 }
159 }
160
_run_switch_high_to_low(void)161 static void _run_switch_high_to_low(void)
162 {
163 struct pm_run_mode_config st_run_mode_cfg = PM_RUN_MODE_CFG;
164
165 st_run_mode_cfg.sys_clk_cfg(PM_RUN_MODE_LOW_SPEED);
166
167 #if defined(HC32F4A0) || defined(HC32F460) || defined(HC32F448) || defined(HC32F4A8)
168 PWC_HighSpeedToLowSpeed();
169 #endif
170 }
171
_run_switch_low_to_high(void)172 static void _run_switch_low_to_high(void)
173 {
174 struct pm_run_mode_config st_run_mode_cfg = PM_RUN_MODE_CFG;
175
176 #if defined(HC32F4A0) || defined(HC32F460) || defined(HC32F448) || defined(HC32F4A8)
177 PWC_LowSpeedToHighSpeed();
178 #endif
179
180 st_run_mode_cfg.sys_clk_cfg(PM_RUN_MODE_HIGH_SPEED);
181 }
182
_pm_run(struct rt_pm * pm,uint8_t mode)183 static void _pm_run(struct rt_pm *pm, uint8_t mode)
184 {
185 static uint8_t last_mode = PM_RUN_MODE_NORMAL_SPEED;
186
187 if (mode == last_mode)
188 return;
189
190 if (_run_switch_func[last_mode][mode] != RT_NULL)
191 {
192 _run_switch_func[last_mode][mode]();
193 SysTick_Configuration();
194 }
195
196 _uart_console_reconfig();
197
198 last_mode = mode;
199 }
200
201 /**
202 * This function calculate the PM tick from OS tick
203 *
204 * @param tick OS tick
205 *
206 * @return the PM tick
207 */
_pm_wakeup_timer_tick_from_os_tick(rt_tick_t tick)208 static rt_tick_t _pm_wakeup_timer_tick_from_os_tick(rt_tick_t tick)
209 {
210 rt_uint32_t freq = hc32_wktm_get_countfreq();
211
212 return (freq * tick / RT_TICK_PER_SECOND);
213 }
214
215 /**
216 * This function start the timer of pm
217 *
218 * @param pm Pointer to power manage structure
219 * @param timeout How many OS Ticks that MCU can sleep
220 */
_pm_wakeup_timer_start(struct rt_pm * pm,rt_uint32_t timeout)221 static void _pm_wakeup_timer_start(struct rt_pm *pm, rt_uint32_t timeout)
222 {
223 RT_ASSERT(pm != RT_NULL);
224
225 /* Convert OS Tick to pmtimer timeout value */
226 timeout = _pm_wakeup_timer_tick_from_os_tick(timeout);
227
228 /* Enter __WAKEUP_TIMER_MODE */
229 hc32_wktm_start(timeout);
230 }
231
232 /**
233 * This function stop the timer of pm
234 *
235 * @param pm Pointer to power manage structure
236 */
_pm_wakeup_timer_stop(struct rt_pm * pm)237 static void _pm_wakeup_timer_stop(struct rt_pm *pm)
238 {
239 RT_ASSERT(pm != RT_NULL);
240
241 /* Reset pmtimer status */
242 hc32_wktm_stop();
243 }
244
_timer_get_tick(struct rt_pm * pm)245 static rt_tick_t _timer_get_tick(struct rt_pm *pm)
246 {
247 RT_ASSERT(pm != RT_NULL);
248
249 /* Get timeout tick */
250 return hc32_wktm_get_timeout_tick();
251 }
252
253 /**
254 * This function initialize the power manager
255 * @note timer feature: only work as wake up timer
256 */
rt_hw_pm_init(void)257 int rt_hw_pm_init(void)
258 {
259 static const struct rt_pm_ops _ops =
260 {
261 _pm_sleep,
262 _pm_run,
263 _pm_wakeup_timer_start,
264 _pm_wakeup_timer_stop,
265 _timer_get_tick,
266 };
267
268 rt_uint8_t timer_mask = PM_TICKLESS_TIMER_ENABLE_MASK;
269 /* initialize system pm module */
270 rt_system_pm_init(&_ops, timer_mask, RT_NULL);
271 return 0;
272 }
273
274 INIT_DEVICE_EXPORT(rt_hw_pm_init);
275
276 #endif
277
278 #endif /* RT_USING_PM */
279