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