1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author            Notes
8  * 2020-07-27     thread-liu        first version
9  */
10 
11 #include "board.h"
12 //#define DRV_DEBUG
13 #define LOG_TAG             "drv.pwr"
14 #include <drv_log.h>
15 
16 extern int lptim_start(void);
17 extern int lptim_stop(void);
18 
19 static RCC_ClkInitTypeDef  RCC_ClkInit = {0};
20 
21 #define __WAIT_EVENT_TIMEOUT(__CONDITION__, __TIMEOUT_VAL__)                 \
22   do {                                                                       \
23     __IO uint32_t count = __TIMEOUT_VAL__ * (SystemCoreClock / 20U / 1000U); \
24     do                                                                       \
25     {                                                                        \
26       if (count-- == 0U)                                                     \
27       {                                                                      \
28         return  HAL_TIMEOUT;                                                 \
29       }                                                                      \
30     }                                                                        \
31     while (__CONDITION__ == 0U);                                             \
32   } while(0)
33 
34 /* Back up clock tree */
backup_cm4_clocks(void)35 static void backup_cm4_clocks(void)
36 {
37     rt_uint32_t *pFLatency = NULL;
38 
39     /* Back up MCU clock configuration */
40     HAL_RCC_GetClockConfig(&RCC_ClkInit, pFLatency);
41 }
42 
43 /* Restore the CM4 clock source muxer and the CM4 prescaler. */
restore_cm4_clock(void)44 rt_err_t restore_cm4_clock(void)
45 {
46     /* Update SystemCoreClock variable */
47     SystemCoreClock = HAL_RCC_GetSystemCoreClockFreq();
48 
49     /* Enable PLL3 if needed */
50     if (RCC_ClkInit.MCUInit.MCU_Clock == RCC_MCUSSOURCE_PLL3)
51     {
52     /* Enable PLL3 */
53     __HAL_RCC_PLL3_ENABLE();
54 
55     /* Wait till PLL3 is ready */
56     __WAIT_EVENT_TIMEOUT(__HAL_RCC_GET_FLAG(RCC_FLAG_PLL3RDY), CLOCKSWITCH_TIMEOUT_VALUE);
57 
58     /* Enable PLL3 outputs */
59     __HAL_RCC_PLL3CLKOUT_ENABLE(RCC_PLL3_DIVP | RCC_PLL3_DIVQ | RCC_PLL3_DIVR);
60     }
61 
62     /* Configure MCU clock only */
63     __HAL_RCC_MCU_SOURCE(RCC_ClkInit.MCUInit.MCU_Clock);
64 
65     /* Wait till MCU is ready */
66     __WAIT_EVENT_TIMEOUT(__HAL_RCC_GET_FLAG(RCC_FLAG_MCUSSRCRDY),
67                        CLOCKSWITCH_TIMEOUT_VALUE);
68 
69     /* Update SystemCoreClock variable */
70     SystemCoreClock = HAL_RCC_GetSystemCoreClockFreq();
71 
72     /* Reconfigure Systick */
73     if (HAL_InitTick(uwTickPrio) != HAL_OK)
74     {
75         return -RT_ERROR;
76     }
77 
78     /* Set MCU division factor */
79     __HAL_RCC_MCU_DIV(RCC_ClkInit.MCUInit.MCU_Div);
80 
81     /* Wait till MCUDIV is ready */
82     __WAIT_EVENT_TIMEOUT(__HAL_RCC_GET_FLAG(RCC_FLAG_MCUDIVRDY),
83                        CLOCKSWITCH_TIMEOUT_VALUE);
84 
85     /* Update SystemCoreClock variable */
86     SystemCoreClock = HAL_RCC_GetSystemCoreClockFreq();
87 
88     /* Reconfigure Systick */
89     if (HAL_InitTick(uwTickPrio) != HAL_OK)
90     {
91         return -RT_ERROR;
92     }
93 
94     return RT_EOK;
95 }
96 
RCC_WAKEUP_IRQHandler(void)97 void RCC_WAKEUP_IRQHandler(void)
98 {
99     /* enter interrupt */
100     rt_interrupt_enter();
101 
102     HAL_RCC_WAKEUP_IRQHandler();
103 
104     /* leave interrupt */
105     rt_interrupt_leave();
106 }
107 
HAL_RCC_WAKEUP_Callback()108 void HAL_RCC_WAKEUP_Callback()
109 {
110     if (__HAL_PWR_GET_FLAG(PWR_FLAG_STOP) == 1U)
111     {
112         __HAL_PWR_CLEAR_FLAG(PWR_FLAG_STOP);
113     }
114 
115     restore_cm4_clock();
116     /* All level of ITs can interrupt */
117     __set_BASEPRI(0U);
118 
119     rt_kprintf("system exit stop mode success!\n");
120 }
121 
enter_sleep_mode(void)122 static void enter_sleep_mode(void)
123 {
124     __set_BASEPRI((1) << (8 - __NVIC_PRIO_BITS));
125 
126     lptim_start();
127 
128     HAL_PWR_EnterSLEEPMode(PWR_LOWPOWERREGULATOR_ON, PWR_SLEEPENTRY_WFI);
129 }
130 
enter_stop_mode(void)131 static void enter_stop_mode(void)
132 {
133     /*
134      * Only the IT with the highest priority (0 value) can interrupt.
135      * RCC_WAKEUP_IRQn IT is intended to have the highest priority and to be the
136      * only one IT having this value
137      * RCC_WAKEUP_IRQn is generated only when RCC is completely resumed from
138      * CSTOP (protection mechanism)
139      */
140     __set_BASEPRI((1) << (8 - __NVIC_PRIO_BITS));
141 
142     __HAL_PWR_CLEAR_FLAG(PWR_FLAG_STOP);
143     backup_cm4_clocks();
144     HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
145 }
146 
pm_wackup_key_init(void)147 static void pm_wackup_key_init(void)
148 {
149     GPIO_InitTypeDef GPIO_InitStruct = {0};
150 
151     __HAL_RCC_GPIOA_CLK_ENABLE();
152 
153     GPIO_InitStruct.Pin = GPIO_PIN_13;
154     GPIO_InitStruct.Pull = GPIO_PULLUP;
155     GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
156     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
157     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
158 
159     HAL_NVIC_SetPriority(EXTI13_IRQn, 0x01, 0);
160     HAL_NVIC_EnableIRQ(EXTI13_IRQn);
161 }
162 
drv_pm_hw_init(void)163 int drv_pm_hw_init(void)
164 {
165     pm_wackup_key_init();
166 
167     return RT_EOK;
168 }
169 INIT_BOARD_EXPORT(drv_pm_hw_init);
170 
pwr_sample(int argc,char * argv[])171 static int pwr_sample(int argc, char *argv[])
172 {
173     if (argc > 1)
174     {
175         if (!rt_strcmp(argv[1], "stop"))
176         {
177            rt_kprintf("system will enter stop mode! you can press USER2 button to exit this mode\n");
178            enter_stop_mode();
179            return RT_EOK;
180 
181         }
182         else if (!rt_strcmp(argv[1], "sleep"))
183         {
184            rt_kprintf("system will enter sleep mode! lptim1 will wake up the system\n");
185            enter_sleep_mode();
186            return RT_EOK;
187         }
188         else
189         {
190             goto _exit;
191         }
192     }
193 _exit:
194     {
195         rt_kprintf("Usage:\n");
196         rt_kprintf("pwr_sample stop      - system enter stop mode\n");
197         rt_kprintf("pwr_sample sleep     - system enter sleep mode\n");
198     }
199 
200     return -RT_ERROR;
201 }
202 MSH_CMD_EXPORT(pwr_sample, enter low power mode sample);
203