]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/T-HEAD_CB2201_CDK/csi/csi_driver/csky/common/pmu/ck_pmu.c
Introduce a port for T-HEAD CK802. A simple demo for T-HEAD CB2201 is also included.
[freertos] / FreeRTOS / Demo / T-HEAD_CB2201_CDK / csi / csi_driver / csky / common / pmu / ck_pmu.c
1 /*
2  * Copyright (C) 2017 C-SKY Microsystems Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 /******************************************************************************
17  * @file     ck_pmu.c
18  * @brief    CSI Source File for Embedded Flash Driver
19  * @version  V1.0
20  * @date     02. June 2017
21  ******************************************************************************/
22
23 #include <stdio.h>
24 #include <string.h>
25 #include "drv_pmu.h"
26 #include "drv_tee.h"
27 #include "drv_eflash.h"
28 #include "ck_pmu.h"
29
30 #define ERR_PMU(errno) (CSI_DRV_ERRNO_PMU_BASE | errno)
31 #define PMU_NULL_PARAM_CHK(para)                         \
32     do {                                        \
33         if (para == NULL) {                     \
34             return ERR_PMU(EDRV_PARAMETER);   \
35         }                                       \
36     } while (0)
37
38 typedef struct {
39     uint8_t idx;
40     uint32_t base;
41     uint32_t irq;
42     pmu_event_cb_t cb;
43     pmu_action_cb_t callback[32];
44 } ck_pmu_priv_t;
45
46 extern int32_t target_get_pmu(int32_t idx, uint32_t *base, uint32_t *irq);
47 extern int32_t arch_do_cpu_save(void);
48 extern int32_t arch_do_cpu_resume(void);
49 extern int32_t arch_resume_context(void);
50
51 static ck_pmu_priv_t pmu_handle[CONFIG_PMU_NUM];
52 static uint32_t s_callback_count = 0;
53 #define CONFIG_PMU_REGISTER_NUM_SAVE  19
54 static uint32_t pmu_regs_saved[CONFIG_PMU_REGISTER_NUM_SAVE];
55
56 #define CONFIG_CPU_REGISTER_NUM_SAVE    27
57 uint32_t arch_cpu_saved[CONFIG_CPU_REGISTER_NUM_SAVE];
58 /* Driver Capabilities */
59 #if 0
60 static const pmu_capabilities_t driver_capabilities = {
61     .event_ready = 1, /* event_ready */
62     .data_width = 2, /* data_width = 0:8-bit, 1:16-bit, 2:32-bit */
63     .erase_chip = 0  /* erase_chip */
64 };
65 #endif
66 //
67 // Functions
68 //
69
70 static void do_prepare_sleep_action(int32_t idx)
71 {
72     uint8_t i;
73     volatile ck_pmu_reg_t *pbase = (ck_pmu_reg_t *)pmu_handle[idx].base;
74     for (i = 0; i < sizeof(pmu_regs_saved)/4; i++) {
75         pmu_regs_saved[i] = *((volatile uint32_t *)pbase + i);
76     }
77 }
78
79 static void do_wakeup_sleep_action(int32_t idx)
80 {
81     uint8_t i;
82     volatile ck_pmu_reg_t *pbase = (ck_pmu_reg_t *)pmu_handle[idx].base;
83     *((volatile uint32_t *)pbase + 5) = pmu_regs_saved[5];
84     while((*((volatile uint32_t *)pbase + 6) & 0xf) != 0xf);
85     *((volatile uint32_t *)pbase + 11) = pmu_regs_saved[11];
86     while((*((volatile uint32_t *)pbase + 6) & 0x1f) != 0x1f);
87     for (i = 0; i < sizeof(pmu_regs_saved)/4; i++) {
88         if (i != 5 && i != 11) {
89             *((volatile uint32_t *)pbase + i) = pmu_regs_saved[i];
90         }
91     }
92
93 }
94
95 static uint8_t s_action[CONFIG_PMU_NUM] = {0x0};
96 int32_t ck_pmu_power_manager(int32_t idx)
97 {
98     if (!(s_action[idx] % 2)) {
99         do_prepare_sleep_action(idx);
100         s_action[idx]++;
101     } else {
102         do_wakeup_sleep_action(idx);
103         s_action[idx]--;
104     }
105     return 0;
106 }
107
108 int32_t ck_pmu_act_callback(pmu_handle_t handle, pmu_event_e event)
109 {
110     ck_pmu_priv_t *pmu_priv = handle;
111     uint32_t i;
112     for (i = 0; i < s_callback_count; i++) {
113         if (pmu_priv->callback[i]) {
114             pmu_priv->callback[i](event);
115         }
116     }
117
118     if (i != s_callback_count) {
119         return -1;
120     }
121     return 0;
122 }
123
124 void resume_context_from_stop_mode(void)
125 {
126     ck_pmu_priv_t *pmu_priv = &pmu_handle[0];
127 //    ck_pmu_power_manager(PMU_EVENT_SLEEP_DONE);
128 //    ck_pmu_act_callback(pmu_priv, PMU_EVENT_SLEEP_DONE);
129     *((volatile uint32_t *)0x50006100) |= 0xa0000000;
130     if (pmu_priv->cb) {
131         pmu_priv->cb(pmu_priv->idx, PMU_EVENT_SLEEP_DONE, PMU_MODE_STDBY);
132     }
133
134     arch_do_cpu_resume();
135 }
136
137 #define CONFIG_LPM_RESUME_ADDR  0x1003f7f0
138 void set_resume_func(uint32_t *func)
139 {
140     eflash_handle_t eflash = csi_eflash_initialize(0, NULL);
141     csi_eflash_erase_sector(eflash, CONFIG_LPM_RESUME_ADDR);
142     csi_eflash_program(eflash, CONFIG_LPM_RESUME_ADDR, &func, 4);
143 }
144
145 typedef enum {
146     WAIT_MODE = 0,
147     DOZE_MODE,
148     STOP_MODE,
149     STANDBY_MODE,
150     SLEEP_MODE
151 } lpm_mode_t;
152
153
154 void soc_sleep(pmu_handle_t handle, lpm_mode_t mode)
155 {
156 #ifdef CONFIG_TEE_CA
157     tee_lpm_mode_e lpm_mode = 0;
158
159     if (mode == WAIT_MODE) {
160         lpm_mode = TEE_LPM_MODE_WAIT;
161     } else if (mode == DOZE_MODE) {
162         lpm_mode = TEE_LPM_MODE_DOZE;
163     } else if (mode == STOP_MODE) {
164         lpm_mode = TEE_LPM_MODE_STOP;
165     } else if (mode == STANDBY_MODE) {
166         lpm_mode = TEE_LPM_MODE_STANDBY;
167     } else {
168         lpm_mode = TEE_LPM_MODE_WAIT;
169     }
170
171     csi_tee_enter_lpm(0, 0, lpm_mode);
172
173     if (mode == STOP_MODE) {
174         resume_context_from_stop_mode();
175     }
176 #else
177
178     ck_pmu_priv_t *pmu_priv = handle;
179     ck_pmu_reg_t *pmu_reg = (ck_pmu_reg_t *)pmu_priv->base;
180
181     if (mode == WAIT_MODE) {
182         pmu_reg->LPCR |= CONFIG_PMU_ENTER_WAIT_MODE;
183         __WFI();
184     } else if (mode == DOZE_MODE) {
185         pmu_reg->LPCR |= CONFIG_PMU_ENTER_DOZE_MODE;
186         __DOZE();
187     } else if (mode == STOP_MODE) {
188         pmu_reg->LPCR |= CONFIG_PMU_ENTER_STOP_MODE;
189         __STOP();
190     } else if (mode == STANDBY_MODE) {
191         pmu_reg->LPCR |= CONFIG_PMU_ENTER_STANDBY_MODE;
192         __STOP();
193     } else {
194         pmu_reg->LPCR |= CONFIG_PMU_ENTER_WAIT_MODE;
195         __WFI();
196     }
197
198 #endif
199 }
200
201 /**
202   \brief       Initialize PMU Interface. 1. Initializes the resources needed for the PMU interface 2.registers event callback function
203   \param[in]   idx device id
204   \param[in]   cb_event  Pointer to \ref pmu_event_cb_t
205   \return      pointer to pmu handle
206 */
207 pmu_handle_t drv_pmu_initialize(int32_t idx, pmu_event_cb_t cb_event)
208 {
209     if (idx < 0 || idx >= CONFIG_PMU_NUM) {
210         return NULL;
211     }
212
213     /* obtain the pmu information */
214     uint32_t base = 0u;
215     uint32_t irq = 0u;
216     int32_t real_idx = target_get_pmu(idx, &base, &irq);
217
218     if (real_idx != idx) {
219         return NULL;
220     }
221
222     ck_pmu_priv_t *pmu_priv = &pmu_handle[idx];
223
224     /* initialize the pmu context */
225     pmu_priv->idx = idx;
226     pmu_priv->base = base;
227     pmu_priv->irq = irq;
228     pmu_priv->cb = cb_event;
229
230     return (pmu_handle_t)pmu_priv;
231 }
232
233 /**
234   \brief       De-initialize PMU Interface. stops operation and releases the software resources used by the interface
235   \param[in]   handle  pmu handle to operate.
236   \return      error code
237 */
238 int32_t drv_pmu_uninitialize(pmu_handle_t handle)
239 {
240     PMU_NULL_PARAM_CHK(handle);
241
242     ck_pmu_priv_t *pmu_priv = handle;
243     pmu_priv->cb = NULL;
244
245     return 0;
246 }
247
248 int32_t drv_pmu_power_control(int32_t idx, csi_power_stat_e state)
249 {
250     switch (state) {
251     case DRV_POWER_LOW:
252         break;
253     case DRV_POWER_FULL:
254         break;
255     case DRV_POWER_OFF:
256         ck_pmu_power_manager(idx);
257 //        csi_pmu_register_module(dw_usart_power_manager);
258         break;
259     default:
260         break;
261     }
262
263     return 0;
264 }
265
266 /**
267   \brief       Get driver capabilities.
268   \param[in]   idx device id
269   \return      \ref pmu_capabilities_t
270 */
271 #if 0
272 pmu_capabilities_t csi_pmu_get_capabilities(int32_t idx)
273 {
274     if (idx < 0 || idx >= CONFIG_PMU_NUM) {
275         pmu_capabilities_t ret;
276         memset(&ret, 0, sizeof(pmu_capabilities_t));
277         return ret;
278     }
279
280     return driver_capabilities;
281 }
282 #endif
283 /**
284   \brief       choose the pmu mode to enter
285   \param[in]   handle  pmu handle to operate.
286   \param[in]   mode    \ref pmu_mode_e
287   \return      error code
288 */
289 int32_t drv_pmu_enter_sleep(pmu_handle_t handle, pmu_mode_e mode)
290 {
291     PMU_NULL_PARAM_CHK(handle);
292
293     switch (mode) {
294     case PMU_MODE_RUN:
295         break;
296     case PMU_MODE_SLEEP:
297         soc_sleep(handle, WAIT_MODE);
298         break;
299     case PMU_MODE_DORMANT:
300 //        soc_sleep(handle, DOZE_MODE);
301         if (arch_do_cpu_save() == 0) {
302             *(volatile unsigned int *)(0xe000e1c0) = 0xffffffff; // reload wakeup_IRQ
303             *(volatile unsigned int *)(0xe000e280) = 0xffffffff; // clear pend IRQ
304             soc_sleep(handle, STOP_MODE);
305         }
306         break;
307     case PMU_MODE_STDBY:
308         *(volatile unsigned int *)(0xe000e1c0) = 0xffffffff; // reload wakeup_IRQ
309         *(volatile unsigned int *)(0xe000e280) = 0xffffffff; // clear pend IRQ
310         soc_sleep(handle, STANDBY_MODE);
311         break;
312     case PMU_MODE_SHUTDOWN:
313         *(volatile unsigned int *)(0xe000e1c0) = 0xffffffff; // reload wakeup_IRQ
314         *(volatile unsigned int *)(0xe000e280) = 0xffffffff; // clear pend IRQ
315         soc_sleep(handle, STANDBY_MODE);
316         break;
317     default:
318         return ERR_PMU(EDRV_PARAMETER);
319     }
320
321     return 0;
322 }
323
324 /**
325   \brief       register module to action pmu event
326   \param[in]   handle  pmu handle to operate.
327   \param[in]   callback Pointer to \ref pmu_action_cb_t
328   \return      error code
329 */
330 int32_t drv_pmu_register_module(pmu_action_cb_t callback)
331 {
332     ck_pmu_priv_t *pmu_priv = (ck_pmu_priv_t *)&pmu_handle[0];
333
334     if (callback == NULL) {
335         return ERR_PMU(EDRV_PARAMETER);
336     }
337     pmu_priv->callback[s_callback_count] = callback;
338     s_callback_count++;
339     return 0;
340 }
341
342 /**
343   \brief       Config the wakeup source.
344   \param[in]   handle  pmu handle to operate
345   \param[in]   type    \ref pmu_wakeup_type
346   \param[in]   pol     \ref pmu_wakeup_pol
347   \param[in]   enable  flag control the wakeup source is enable or not
348   \return      error code
349 */
350 int32_t drv_pmu_config_wakeup_source(pmu_handle_t handle, uint32_t irq_num, pmu_wakeup_type_e type, pmu_wakeup_pol_e pol, uint8_t enable)
351 {
352     PMU_NULL_PARAM_CHK(handle);
353
354     if (enable) {
355 //        csi_vic_enable_irq(irq_num);
356 //        csi_vic_enable_sirq(irq_num);
357 //        csi_vic_set_wakeup_irq(irq_num);
358         drv_nvic_set_wakeup_irq(irq_num);
359     } else {
360 //        csi_vic_disable_irq(irq_num);
361 //        csi_vic_disable_sirq(irq_num);
362         drv_nvic_clear_wakeup_irq(irq_num);
363 //        csi_vic_clear_wakeup_irq(irq_num);
364     }
365     return 0;
366 }