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