1  /*
2  * Copyright (C) 2017-2024 Alibaba Group Holding Limited
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 /******************************************************************************
20  * @file     power_manage.c
21  * @brief    CSI Source File for power manage
22  * @version  V1.0
23  * @date     16. Mar 2020
24  ******************************************************************************/
25 
26 #include <stdint.h>
27 #include <soc.h>
28 #include <csi_core.h>
29 #include <csi_config.h>
30 #include <drv/common.h>
31 #include <drv/list.h>
32 
33 #ifdef CONFIG_PM
34 #include <drv/pm.h>
35 
36 #define NODE_USED_MASK 0x00000001U
37 
38 #ifndef CONFIG_PM_DEV_PRIORITY
39 #define CONFIG_PM_DEV_PRIORITY  4
40 #endif
41 
42 typedef struct reten_mem_node {
43     struct reten_mem_node *next;
44     uint32_t              size;
45 } reten_mem_node_t;
46 
47 static slist_t pm_dev_list[CONFIG_PM_DEV_PRIORITY];
48 static uint32_t *pm_dev_reten_mem;
49 
csi_pm_init(void)50 csi_error_t csi_pm_init(void)
51 {
52     for (uint8_t i = 0; i < CONFIG_PM_DEV_PRIORITY; i++) {
53         slist_init(&pm_dev_list[i]);
54     }
55 
56     return CSI_OK;
57 }
58 
csi_pm_uninit(void)59 void csi_pm_uninit(void)
60 {
61     for (uint8_t i = 0; i < CONFIG_PM_DEV_PRIORITY; i++) {
62         slist_init(&pm_dev_list[i]);
63     }
64 }
65 
csi_pm_set_reten_mem(uint32_t * mem,uint32_t num)66 csi_error_t csi_pm_set_reten_mem(uint32_t *mem, uint32_t num)
67 {
68     CSI_PARAM_CHK(mem, CSI_ERROR);
69 
70     csi_error_t ret = CSI_OK;
71     reten_mem_node_t *node;
72 
73     if (num >= 3U) {
74         pm_dev_reten_mem = mem;
75 
76         node = (reten_mem_node_t *)mem;
77         node->next = NULL;
78         node->size = num * 4U - 8U;
79     } else {
80         ret = CSI_ERROR;
81     }
82 
83     return ret;
84 }
85 
pm_alloc_reten_mem(uint32_t size)86 static uint32_t *pm_alloc_reten_mem(uint32_t size)
87 {
88     int32_t found = 0;
89     uint32_t *mem = NULL;
90     reten_mem_node_t *node = (reten_mem_node_t *)pm_dev_reten_mem;
91 
92     while (node) {
93         if (node->size == size) {
94             found = 1;
95             node->size |= NODE_USED_MASK;
96             mem = (uint32_t *)((uint32_t)node + 8U);
97             break;
98         }
99 
100         node = node->next;
101     }
102 
103     if (found == 0) {
104         node = (reten_mem_node_t *)pm_dev_reten_mem;
105 
106         while (node) {
107             if (node->size > size && !(node->size & NODE_USED_MASK)) {
108                 if (node->size >= size + 12U) {
109                     reten_mem_node_t *newnode;
110                     newnode = (reten_mem_node_t *)((uint32_t)node + 8U + size);
111                     newnode->size = node->size - size - 8U;
112                     newnode->next = node->next;
113                     node->next = newnode;
114                     node->size = (size | NODE_USED_MASK);
115                 } else {
116                     node->size |= NODE_USED_MASK;
117                 }
118 
119                 mem = (uint32_t *)((uint32_t)node + 8U);
120                 break;
121             }
122 
123             node = node->next;
124         }
125     }
126 
127     return mem;
128 }
129 
pm_free_reten_mem(uint32_t * mem)130 static void pm_free_reten_mem(uint32_t *mem)
131 {
132     reten_mem_node_t *node = (reten_mem_node_t *)pm_dev_reten_mem;
133 
134     while (node) {
135         uint32_t addr;
136         addr = (uint32_t)node + 8U;
137 
138         if (addr == (uint32_t)mem) {
139             node->size &= ~NODE_USED_MASK;
140             break;
141         }
142 
143         node = node->next;
144     }
145 }
146 
csi_pm_dev_register(csi_dev_t * dev,void * pm_action,uint32_t mem_size,uint8_t priority)147 csi_error_t csi_pm_dev_register(csi_dev_t *dev, void *pm_action, uint32_t mem_size, uint8_t priority)
148 {
149     CSI_PARAM_CHK(dev, CSI_ERROR);
150 
151     csi_error_t ret = CSI_OK;
152     uint32_t *mem = NULL;
153 
154     dev->pm_dev.pm_action = pm_action;
155     dev->pm_dev.size = mem_size;
156 
157     slist_add(&dev->pm_dev.next, &pm_dev_list[priority]);
158 
159     mem = pm_alloc_reten_mem(dev->pm_dev.size);
160     dev->pm_dev.reten_mem = mem;
161 
162     if (mem == NULL) {
163         ret = CSI_ERROR;
164     }
165 
166     return ret;
167 }
168 
csi_pm_dev_unregister(csi_dev_t * dev)169 void csi_pm_dev_unregister(csi_dev_t *dev)
170 {
171     CSI_PARAM_CHK_NORETVAL(dev);
172 
173     for (uint8_t i = 0; i < CONFIG_PM_DEV_PRIORITY; i++) {
174         slist_del(&dev->pm_dev.next, &pm_dev_list[i]);
175     }
176 
177     pm_free_reten_mem(dev->pm_dev.reten_mem);
178 }
179 
csi_pm_dev_save_regs(uint32_t * mem,uint32_t * addr,uint32_t num)180 void csi_pm_dev_save_regs(uint32_t *mem, uint32_t *addr, uint32_t num)
181 {
182     for (uint32_t i = 0U; i < num; i++) {
183         mem[i] = addr[i];
184     }
185 }
186 
csi_pm_dev_restore_regs(uint32_t * mem,uint32_t * addr,uint32_t num)187 void csi_pm_dev_restore_regs(uint32_t *mem, uint32_t *addr, uint32_t num)
188 {
189     for (uint32_t i = 0U; i < num; i++) {
190         addr[i] = mem[i];
191     }
192 }
193 
csi_pm_dev_notify(csi_pm_dev_action_t action)194 csi_error_t csi_pm_dev_notify(csi_pm_dev_action_t action)
195 {
196     csi_error_t ret = CSI_OK;
197     csi_pm_dev_t *node;
198     csi_dev_t *dev;
199     uint32_t flags;
200     int8_t i;
201 
202     flags = csi_irq_save();
203 
204     if (action == PM_DEV_SUSPEND) {
205         for (i = CONFIG_PM_DEV_PRIORITY - 1; i >= 0; i--) {
206             slist_for_each_entry(&pm_dev_list[i], node, csi_pm_dev_t, next) {
207                 if (node) {
208                     dev = (csi_dev_t *)((uint32_t)node - 12U);
209                     ret = node->pm_action(dev, action);
210 
211                     if (ret != CSI_OK) {
212                         break;
213                     }
214                 }
215             }
216         }
217     } else {
218         for (i = 0; i < CONFIG_PM_DEV_PRIORITY; i++) {
219             slist_for_each_entry(&pm_dev_list[i], node, csi_pm_dev_t, next) {
220                 if (node) {
221                     dev = (csi_dev_t *)((uint32_t)node - 12U);
222                     ret = node->pm_action(dev, action);
223 
224                     if (ret != CSI_OK) {
225                         break;
226                     }
227                 }
228             }
229         }
230     }
231 
232     csi_irq_restore(flags);
233 
234     return ret;
235 }
236 
csi_pm_enter_sleep(csi_pm_mode_t mode)237 csi_error_t csi_pm_enter_sleep(csi_pm_mode_t mode)
238 {
239     csi_error_t ret = CSI_OK;
240 
241     switch (mode) {
242         case PM_MODE_SLEEP_1:
243             break;
244 
245         case PM_MODE_SLEEP_2:
246             break;
247 
248         case PM_MODE_DEEP_SLEEP_1:
249             ret = csi_pm_dev_notify(PM_DEV_SUSPEND);
250             break;
251 
252         case PM_MODE_DEEP_SLEEP_2:
253             break;
254 
255         default:
256             break;
257     }
258 
259     if (ret == CSI_OK) {
260         ret = soc_pm_enter_sleep(mode);
261 
262         switch (mode) {
263             case PM_MODE_SLEEP_1:
264                 break;
265 
266             case PM_MODE_SLEEP_2:
267                 break;
268 
269             case PM_MODE_DEEP_SLEEP_1:
270                 ret = csi_pm_dev_notify(PM_DEV_RESUME);
271                 break;
272 
273             case PM_MODE_DEEP_SLEEP_2:
274                 break;
275 
276             default:
277                 break;
278         }
279     }
280 
281     return ret;
282 }
283 
csi_pm_config_wakeup_source(uint32_t wakeup_num,bool enable)284 csi_error_t csi_pm_config_wakeup_source(uint32_t wakeup_num, bool enable)
285 {
286     csi_error_t ret = CSI_OK;
287 
288     ret = soc_pm_config_wakeup_source(wakeup_num, enable);
289 
290     return ret;
291 }
292 #endif
293