1 /*
2 * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stddef.h>
8 #include <string.h>
9 #include <sys/lock.h>
10 #include <sys/param.h>
11
12 #include "esp_err.h"
13 #include "esp_attr.h"
14 #include "esp_log.h"
15 #include "esp_heap_caps.h"
16 #include "esp_sleep.h"
17 #include "soc/soc_caps.h"
18 #include "esp_private/esp_regdma.h"
19 #include "esp_private/esp_pau.h"
20 #include "esp_private/sleep_retention.h"
21 #include "sdkconfig.h"
22 #include "esp_pmu.h"
23
24 static __attribute__((unused)) const char *TAG = "sleep";
25
26 /**
27 * Internal structure which holds all requested sleep retention parameters
28 */
29 typedef struct {
30 /* The hardware retention module (REGDMA and PMU) uses 4 linked lists to
31 * record the hardware context information that needs to be backed up and
32 * restored when switching between different power states. The 4 linked
33 * lists are linked by 8 types of nodes. The 4 linked lists can reuse some
34 * nodes with each other, or separate their own unique nodes after branch
35 * type nodes.
36 * The REGDMA module iterates the entire linked list from the head of a
37 * linked list and backs up and restores the corresponding register context
38 * information according to the configuration information of the linked list
39 * nodes.
40 * The PMU module triggers REGDMA to use the corresponding linked list when
41 * swtiching between different power states. For example:
42 *
43 * +---------------+---------------+-------------------+-----------+
44 * | Current | The next | The entry will be | Retention |
45 * | PMU state | PMU state | used by REGDMA | clock |
46 * +---------------+---------------+-------------------+-----------+
47 * | PMU_HP_ACTIVE | PMU_HP_SLEEP | entry0 | XTAL |
48 * | PMU_HP_SLEEP | PMU_HP_ACTIVE | entry0 | XTAL |
49 * | PMU_HP_MODEM | PMU_HP_SLEEP | ------ | XTAL |
50 * | PMU_HP_SLEEP | PMU_HP_MODEM | entry1 | XTAL |
51 * | PMU_HP_MODEM | PMU_HP_ACTIVE | entry2 | PLL |
52 * |---------------------------------------------------------------|
53 * | PMU_HP_ACTIVE | PMU_HP_ACTIVE | entry3 | PLL | (Clock BUG)
54 * +---------------+---------------+-------------------+-----------+
55 *
56 * +--------+ +-------------------------+ +-------------+ +-----------+ +--------+ +-----+
57 * entry2 -> | | -> | WiFi MAC Minimum System | -> | | -------------------------> | ######### | -> | ###### | -> | End |
58 * | SOC | +-------------------------+ | Digital | | Bluetooth | | Zigbee | +-----+
59 * | System | +--------+ | Peripherals | +------+ +------+ | / BLE | | | +-----+
60 * entry0 -> | | ----------> | | ---------> | | -> | | -> | | -> | | -> | | -> | End |
61 * +--------+ | Modem | +-------------+ | WiFi | | WiFi | +-----------+ +--------+ +-----+
62 * | System | | MAC | | BB | +-----+
63 * entry1 ------------------------> | |-----------------------------> | | -> | | -> | End |
64 * +--------+ +------+ +------+ +-----+
65 *
66 * The entry3 (alias: extra linked list) is used for backup and restore of
67 * modules (such as BLE or 15.4 modules) with retention clock bugs.
68 *
69 * +---------+ +----------+ +-------------+ +-----+
70 * entry3 -> | BLE MAC | -> | 15.4 MAC | -> | BLE/15.4 BB | -> | End |
71 * +---------+ +----------+ +-------------+ +-----+
72 *
73 * Using it (extra linked list) for retention has the following constraints:
74 * 1. The PLL clock must be enabled (can be done with esp_pm_lock_acquire()
75 * interface to acquire a pm lock of type ESP_PM_APB_FREQ_MAX.
76 * 2. When using the sleep_retention_entries_create() interface to create an
77 * extra linked list, the node owner must be equal to BIT(3).
78 * 3. Use the sleep_retention_do_extra_retention() interface to backup or
79 * restore the register context, which ensures only one backup or restore
80 * when multiple modules (BLE and 15.4) exists.
81 */
82 #define SLEEP_RETENTION_REGDMA_LINK_NR_PRIORITIES (8u)
83 #define SLEEP_RETENTION_REGDMA_LINK_HIGHEST_PRIORITY (0)
84 #define SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY (SLEEP_RETENTION_REGDMA_LINK_NR_PRIORITIES - 1)
85 struct {
86 sleep_retention_entries_t entries;
87 uint32_t entries_bitmap: REGDMA_LINK_ENTRY_NUM,
88 runtime_bitmap: REGDMA_LINK_ENTRY_NUM,
89 reserved: 32-(2*REGDMA_LINK_ENTRY_NUM);
90 void *entries_tail;
91 } lists[SLEEP_RETENTION_REGDMA_LINK_NR_PRIORITIES];
92 _lock_t lock;
93 regdma_link_priority_t highpri;
94 uint32_t modules;
95 #if SOC_PM_RETENTION_HAS_CLOCK_BUG
96 #define EXTRA_LINK_NUM (REGDMA_LINK_ENTRY_NUM - 1)
97 int extra_refs;
98 #endif
99 } sleep_retention_t;
100
101 static DRAM_ATTR __attribute__((unused)) sleep_retention_t s_retention = {
102 .highpri = (uint8_t)-1, .modules = 0
103 #if SOC_PM_RETENTION_HAS_CLOCK_BUG
104 , .extra_refs = 0
105 #endif
106 };
107
108 #define SLEEP_RETENTION_ENTRY_BITMAP_MASK (BIT(REGDMA_LINK_ENTRY_NUM) - 1)
109 #define SLEEP_RETENTION_ENTRY_BITMAP(bitmap) ((bitmap) & SLEEP_RETENTION_ENTRY_BITMAP_MASK)
110
111 static esp_err_t sleep_retention_entries_create_impl(const sleep_retention_entries_config_t retent[], int num, regdma_link_priority_t priority, int module);
112 static void sleep_retention_entries_join(void);
113
sleep_retention_entries_require_branch(uint32_t owner,uint32_t runtime_bitmap)114 static inline bool sleep_retention_entries_require_branch(uint32_t owner, uint32_t runtime_bitmap)
115 {
116 bool use_new_entry = SLEEP_RETENTION_ENTRY_BITMAP(owner & ~runtime_bitmap) ? true : false;
117 bool intersection_exist = SLEEP_RETENTION_ENTRY_BITMAP(owner & runtime_bitmap) ? true : false;
118 return use_new_entry && intersection_exist;
119 }
120
sleep_retention_entries_check_and_create_default(uint32_t owner,uint32_t runtime_bitmap,uint32_t entries_bitmap,regdma_link_priority_t priority,uint32_t module)121 static esp_err_t sleep_retention_entries_check_and_create_default(uint32_t owner, uint32_t runtime_bitmap, uint32_t entries_bitmap, regdma_link_priority_t priority, uint32_t module)
122 {
123 assert(sleep_retention_entries_require_branch(owner, runtime_bitmap));
124
125 static sleep_retention_entries_config_t dummy = { REGDMA_LINK_WAIT_INIT(0xffff, 0, 0, 0, 1, 1), 0 };
126 dummy.owner = SLEEP_RETENTION_ENTRY_BITMAP(owner & ~entries_bitmap);
127 if (dummy.owner) {
128 return sleep_retention_entries_create_impl(&dummy, 1, priority, module);
129 }
130 return ESP_OK;
131 }
132
sleep_retention_entries_check_and_create_final_default(void)133 static esp_err_t sleep_retention_entries_check_and_create_final_default(void)
134 {
135 static const sleep_retention_entries_config_t final_dummy = { REGDMA_LINK_WAIT_INIT(0xffff, 0, 0, 0, 1, 1), SLEEP_RETENTION_ENTRY_BITMAP_MASK };
136
137 esp_err_t err = ESP_OK;
138 _lock_acquire_recursive(&s_retention.lock);
139 if (s_retention.lists[SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY].entries_bitmap == 0) {
140 err = sleep_retention_entries_create_impl(&final_dummy, 1, SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY, 0);
141 }
142 _lock_release_recursive(&s_retention.lock);
143 return err;
144 }
145
sleep_retention_entries_update(uint32_t owner,void * new_link,regdma_link_priority_t priority)146 static void sleep_retention_entries_update(uint32_t owner, void *new_link, regdma_link_priority_t priority)
147 {
148 _lock_acquire_recursive(&s_retention.lock);
149 sleep_retention_entries_t retention_entries = {
150 (owner & BIT(0)) ? new_link : s_retention.lists[priority].entries[0],
151 (owner & BIT(1)) ? new_link : s_retention.lists[priority].entries[1],
152 (owner & BIT(2)) ? new_link : s_retention.lists[priority].entries[2],
153 (owner & BIT(3)) ? new_link : s_retention.lists[priority].entries[3]
154 };
155 if (s_retention.lists[priority].entries_bitmap == 0) {
156 s_retention.lists[priority].entries_tail = new_link;
157 }
158 memcpy(s_retention.lists[priority].entries, retention_entries, sizeof(sleep_retention_entries_t));
159 s_retention.lists[priority].runtime_bitmap = owner;
160 s_retention.lists[priority].entries_bitmap |= owner;
161 _lock_release_recursive(&s_retention.lock);
162 }
163
sleep_retention_entries_try_create(const regdma_link_config_t * config,uint32_t owner,regdma_link_priority_t priority,uint32_t module)164 static void * sleep_retention_entries_try_create(const regdma_link_config_t *config, uint32_t owner, regdma_link_priority_t priority, uint32_t module)
165 {
166 void *link = NULL;
167 assert(owner > 0 && owner < BIT(REGDMA_LINK_ENTRY_NUM));
168
169 _lock_acquire_recursive(&s_retention.lock);
170 if (sleep_retention_entries_require_branch(owner, s_retention.lists[priority].runtime_bitmap)) {
171 if (sleep_retention_entries_check_and_create_default(owner, s_retention.lists[priority].runtime_bitmap,
172 s_retention.lists[priority].entries_bitmap, priority, module) == ESP_OK) { /* branch node can't as tail node */
173 link = regdma_link_init_safe(
174 config, true, module,
175 (owner & BIT(0)) ? s_retention.lists[priority].entries[0] : NULL,
176 (owner & BIT(1)) ? s_retention.lists[priority].entries[1] : NULL,
177 (owner & BIT(2)) ? s_retention.lists[priority].entries[2] : NULL,
178 (owner & BIT(3)) ? s_retention.lists[priority].entries[3] : NULL
179 );
180 }
181 } else {
182 link = regdma_link_init_safe(config, false, module, s_retention.lists[priority].entries[__builtin_ffs(owner) - 1]);
183 }
184 _lock_release_recursive(&s_retention.lock);
185 return link;
186 }
187
sleep_retention_entries_try_create_bonding(const regdma_link_config_t * config,uint32_t owner,regdma_link_priority_t priority,uint32_t module)188 static void * sleep_retention_entries_try_create_bonding(const regdma_link_config_t *config, uint32_t owner, regdma_link_priority_t priority, uint32_t module)
189 {
190 assert(owner > 0 && owner < BIT(REGDMA_LINK_ENTRY_NUM));
191 _lock_acquire_recursive(&s_retention.lock);
192 void *link = regdma_link_init_safe(
193 config, true, module,
194 (owner & BIT(0)) ? s_retention.lists[priority].entries[0] : NULL,
195 (owner & BIT(1)) ? s_retention.lists[priority].entries[1] : NULL,
196 (owner & BIT(2)) ? s_retention.lists[priority].entries[2] : NULL,
197 (owner & BIT(3)) ? s_retention.lists[priority].entries[3] : NULL
198 );
199 _lock_release_recursive(&s_retention.lock);
200 return link;
201 }
202
sleep_retention_entries_stats(void)203 static void sleep_retention_entries_stats(void)
204 {
205 _lock_acquire_recursive(&s_retention.lock);
206 if (s_retention.highpri >= SLEEP_RETENTION_REGDMA_LINK_HIGHEST_PRIORITY && s_retention.highpri <= SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY) {
207 for (int entry = 0; entry < ARRAY_SIZE(s_retention.lists[s_retention.highpri].entries); entry++) {
208 regdma_link_stats(s_retention.lists[s_retention.highpri].entries[entry], entry);
209 }
210 }
211 _lock_release_recursive(&s_retention.lock);
212 }
213
214 #if REGDMA_LINK_DBG
sleep_retention_entries_show_memories(void)215 void sleep_retention_entries_show_memories(void)
216 {
217 _lock_acquire_recursive(&s_retention.lock);
218 if (s_retention.highpri >= SLEEP_RETENTION_REGDMA_LINK_HIGHEST_PRIORITY && s_retention.highpri <= SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY) {
219 for (int entry = 0; entry < ARRAY_SIZE(s_retention.lists[s_retention.highpri].entries); entry++) {
220 ESP_LOGW(TAG, "Print sleep retention entries[%d] memories:", entry);
221 regdma_link_show_memories(s_retention.lists[s_retention.highpri].entries[entry], entry);
222 }
223 }
224 _lock_release_recursive(&s_retention.lock);
225 }
226 #endif
227
sleep_retention_find_link_by_id(int id)228 void * sleep_retention_find_link_by_id(int id)
229 {
230 void *link = NULL;
231 _lock_acquire_recursive(&s_retention.lock);
232 if (s_retention.highpri >= SLEEP_RETENTION_REGDMA_LINK_HIGHEST_PRIORITY &&
233 s_retention.highpri <= SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY) {
234 for (int entry = 0; (link == NULL && entry < ARRAY_SIZE(s_retention.lists[s_retention.highpri].entries)); entry++) {
235 link = regdma_find_link_by_id(s_retention.lists[s_retention.highpri].entries[entry], entry, id);
236 }
237 }
238 _lock_release_recursive(&s_retention.lock);
239 return link;
240 }
241
sleep_retention_entries_owner_bitmap(sleep_retention_entries_t * entries,sleep_retention_entries_t * tails)242 static uint32_t sleep_retention_entries_owner_bitmap(sleep_retention_entries_t *entries, sleep_retention_entries_t *tails)
243 {
244 uint32_t owner = 0;
245 _lock_acquire_recursive(&s_retention.lock);
246 for (int entry = 0; entry < ARRAY_SIZE(*entries); entry++) {
247 owner |= regdma_link_get_owner_bitmap((*entries)[entry], (*tails)[entry], entry);
248 }
249 _lock_release_recursive(&s_retention.lock);
250 return owner;
251 }
252
sleep_retention_entries_get_destroy_context(regdma_link_priority_t priority,uint32_t module,sleep_retention_entries_t * destroy_entries,void ** destroy_tail,sleep_retention_entries_t * next_entries,void ** prev_tail)253 static bool sleep_retention_entries_get_destroy_context(regdma_link_priority_t priority, uint32_t module, sleep_retention_entries_t *destroy_entries, void **destroy_tail, sleep_retention_entries_t *next_entries, void **prev_tail)
254 {
255 bool exist = false;
256 sleep_retention_entries_t destroy_tails, prev_tails;
257
258 memset(&destroy_tails, 0, sizeof(sleep_retention_entries_t));
259 memset(&prev_tails, 0, sizeof(sleep_retention_entries_t));
260
261 _lock_acquire_recursive(&s_retention.lock);
262 for (int entry = 0; entry < ARRAY_SIZE(s_retention.lists[priority].entries); entry++) {
263 (*destroy_entries)[entry] = regdma_find_module_link_head(
264 s_retention.lists[priority].entries[entry], s_retention.lists[priority].entries_tail, entry, module);
265 destroy_tails [entry] = regdma_find_module_link_tail(
266 s_retention.lists[priority].entries[entry], s_retention.lists[priority].entries_tail, entry, module);
267 (*next_entries) [entry] = regdma_find_next_module_link_head(
268 s_retention.lists[priority].entries[entry], s_retention.lists[priority].entries_tail, entry, module);
269 prev_tails [entry] = regdma_find_prev_module_link_tail(
270 s_retention.lists[priority].entries[entry], s_retention.lists[priority].entries_tail, entry, module);
271 if ((*destroy_entries)[entry] && destroy_tails[entry]) {
272 exist = true;
273 }
274 assert(destroy_tails[entry] == destroy_tails[0]);
275 assert(prev_tails[entry] == prev_tails[0]);
276 }
277 *destroy_tail = destroy_tails[0];
278 *prev_tail = prev_tails[0];
279 _lock_release_recursive(&s_retention.lock);
280 return exist;
281 }
282
sleep_retention_entries_context_update(regdma_link_priority_t priority)283 static void sleep_retention_entries_context_update(regdma_link_priority_t priority)
284 {
285 _lock_acquire_recursive(&s_retention.lock);
286 sleep_retention_entries_t tails = {
287 s_retention.lists[priority].entries_tail, s_retention.lists[priority].entries_tail,
288 s_retention.lists[priority].entries_tail, s_retention.lists[priority].entries_tail
289 };
290 s_retention.lists[priority].entries_bitmap = sleep_retention_entries_owner_bitmap(&s_retention.lists[priority].entries, &tails);
291 s_retention.lists[priority].runtime_bitmap = sleep_retention_entries_owner_bitmap(&s_retention.lists[priority].entries, &s_retention.lists[priority].entries);
292 _lock_release_recursive(&s_retention.lock);
293 }
294
sleep_retention_entries_dettach(regdma_link_priority_t priority,sleep_retention_entries_t * destroy_entries,void * destroy_tail,sleep_retention_entries_t * next_entries,void * prev_tail)295 static bool sleep_retention_entries_dettach(regdma_link_priority_t priority, sleep_retention_entries_t *destroy_entries, void *destroy_tail, sleep_retention_entries_t *next_entries, void *prev_tail)
296 {
297 _lock_acquire_recursive(&s_retention.lock);
298 bool is_head = (memcmp(destroy_entries, &s_retention.lists[priority].entries, sizeof(sleep_retention_entries_t)) == 0);
299 bool is_tail = (destroy_tail == s_retention.lists[priority].entries_tail);
300
301 if (is_head && is_tail) {
302 memset(s_retention.lists[priority].entries, 0, sizeof(sleep_retention_entries_t));
303 s_retention.lists[priority].entries_tail = NULL;
304 } else if (is_head) {
305 memcpy(&s_retention.lists[priority].entries, next_entries, sizeof(sleep_retention_entries_t));
306 } else if (is_tail) {
307 s_retention.lists[priority].entries_tail = prev_tail;
308 } else {
309 regdma_link_update_next_safe(prev_tail, (*next_entries)[0], (*next_entries)[1], (*next_entries)[2], (*next_entries)[3]);
310 }
311 sleep_retention_entries_context_update(priority);
312
313 regdma_link_update_next_safe(destroy_tail, NULL, NULL, NULL, NULL);
314 _lock_release_recursive(&s_retention.lock);
315 return (is_head || is_tail);
316 }
317
sleep_retention_entries_destroy_wrapper(sleep_retention_entries_t * destroy_entries)318 static void sleep_retention_entries_destroy_wrapper(sleep_retention_entries_t *destroy_entries)
319 {
320 for (int entry = 0; entry < ARRAY_SIZE(*destroy_entries); entry++) {
321 regdma_link_destroy((*destroy_entries)[entry], entry);
322 }
323 }
324
sleep_retention_entries_check_and_distroy_final_default(void)325 static void sleep_retention_entries_check_and_distroy_final_default(void)
326 {
327 _lock_acquire_recursive(&s_retention.lock);
328 assert(s_retention.highpri == SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY);
329 assert(s_retention.modules == 0);
330 sleep_retention_entries_destroy_wrapper(&s_retention.lists[SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY].entries);
331 _lock_release_recursive(&s_retention.lock);
332 }
333
sleep_retention_entries_all_destroy_wrapper(uint32_t module)334 static void sleep_retention_entries_all_destroy_wrapper(uint32_t module)
335 {
336 void *destroy_tail = NULL, *prev_tail = NULL;
337 sleep_retention_entries_t destroy_entries, next_entries;
338
339 memset(&destroy_entries, 0, sizeof(sleep_retention_entries_t));
340 memset(&next_entries, 0, sizeof(sleep_retention_entries_t));
341
342 _lock_acquire_recursive(&s_retention.lock);
343 regdma_link_priority_t priority = 0;
344 do {
345 bool exist = sleep_retention_entries_get_destroy_context(priority, module, &destroy_entries, &destroy_tail, &next_entries, &prev_tail);
346 if (s_retention.lists[priority].entries_bitmap && exist) {
347 if (sleep_retention_entries_dettach(priority, &destroy_entries, destroy_tail, &next_entries, prev_tail)) {
348 sleep_retention_entries_join();
349 }
350 sleep_retention_entries_destroy_wrapper(&destroy_entries);
351 } else {
352 priority++;
353 }
354 } while (priority < SLEEP_RETENTION_REGDMA_LINK_NR_PRIORITIES);
355 s_retention.modules &= ~module;
356 _lock_release_recursive(&s_retention.lock);
357 }
358
sleep_retention_entries_do_destroy(int module)359 static void sleep_retention_entries_do_destroy(int module)
360 {
361 assert(module != 0);
362 _lock_acquire_recursive(&s_retention.lock);
363 sleep_retention_entries_join();
364 sleep_retention_entries_stats();
365 sleep_retention_entries_all_destroy_wrapper(module);
366 _lock_release_recursive(&s_retention.lock);
367 }
368
sleep_retention_entries_destroy(int module)369 void sleep_retention_entries_destroy(int module)
370 {
371 assert(module != 0);
372 _lock_acquire_recursive(&s_retention.lock);
373 sleep_retention_entries_do_destroy(module);
374 if (s_retention.modules == 0) {
375 sleep_retention_entries_check_and_distroy_final_default();
376 pmu_sleep_disable_regdma_backup();
377 memset((void *)s_retention.lists, 0, sizeof(s_retention.lists));
378 s_retention.highpri = (uint8_t)-1;
379 _lock_release_recursive(&s_retention.lock);
380 _lock_close_recursive(&s_retention.lock);
381 s_retention.lock = NULL;
382 return;
383 }
384 _lock_release_recursive(&s_retention.lock);
385 }
386
sleep_retention_entries_create_impl(const sleep_retention_entries_config_t retent[],int num,regdma_link_priority_t priority,int module)387 static esp_err_t sleep_retention_entries_create_impl(const sleep_retention_entries_config_t retent[], int num, regdma_link_priority_t priority, int module)
388 {
389 _lock_acquire_recursive(&s_retention.lock);
390 for (int i = num - 1; i >= 0; i--) {
391 #if SOC_PM_RETENTION_HAS_CLOCK_BUG
392 if ((retent[i].owner > BIT(EXTRA_LINK_NUM)) && (retent[i].config.id != 0xffff)) {
393 _lock_release_recursive(&s_retention.lock);
394 sleep_retention_entries_do_destroy(module);
395 return ESP_ERR_NOT_SUPPORTED;
396 }
397 #endif
398 void *link = sleep_retention_entries_try_create(&retent[i].config, retent[i].owner, priority, module);
399 if (link == NULL) {
400 _lock_release_recursive(&s_retention.lock);
401 sleep_retention_entries_do_destroy(module);
402 return ESP_ERR_NO_MEM;
403 }
404 sleep_retention_entries_update(retent[i].owner, link, priority);
405 }
406 _lock_release_recursive(&s_retention.lock);
407 return ESP_OK;
408 }
409
sleep_retention_entries_create_bonding(regdma_link_priority_t priority,uint32_t module)410 static esp_err_t sleep_retention_entries_create_bonding(regdma_link_priority_t priority, uint32_t module)
411 {
412 static const sleep_retention_entries_config_t bonding_dummy = { REGDMA_LINK_WAIT_INIT(0xffff, 0, 0, 0, 1, 1), SLEEP_RETENTION_ENTRY_BITMAP_MASK };
413
414 _lock_acquire_recursive(&s_retention.lock);
415 void *link = sleep_retention_entries_try_create_bonding(&bonding_dummy.config, bonding_dummy.owner, priority, module);
416 if (link == NULL) {
417 _lock_release_recursive(&s_retention.lock);
418 sleep_retention_entries_do_destroy(module);
419 return ESP_ERR_NO_MEM;
420 }
421 sleep_retention_entries_update(bonding_dummy.owner, link, priority);
422 _lock_release_recursive(&s_retention.lock);
423 return ESP_OK;
424 }
425
sleep_retention_entries_join(void)426 static void sleep_retention_entries_join(void)
427 {
428 void *entries_tail = NULL;
429 _lock_acquire_recursive(&s_retention.lock);
430 s_retention.highpri = SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY;
431 for (regdma_link_priority_t priority = 0; priority < SLEEP_RETENTION_REGDMA_LINK_NR_PRIORITIES; priority++) {
432 if (s_retention.lists[priority].entries_bitmap == 0) continue;
433 if (priority < s_retention.highpri) { s_retention.highpri = priority; }
434 if (entries_tail) {
435 regdma_link_update_next_safe(
436 entries_tail,
437 s_retention.lists[priority].entries[0],
438 s_retention.lists[priority].entries[1],
439 s_retention.lists[priority].entries[2],
440 s_retention.lists[priority].entries[3]
441 );
442 }
443 entries_tail = s_retention.lists[priority].entries_tail;
444 }
445 pau_regdma_set_entry_link_addr(&(s_retention.lists[s_retention.highpri].entries));
446 _lock_release_recursive(&s_retention.lock);
447 }
448
sleep_retention_entries_create_wrapper(const sleep_retention_entries_config_t retent[],int num,regdma_link_priority_t priority,uint32_t module)449 static esp_err_t sleep_retention_entries_create_wrapper(const sleep_retention_entries_config_t retent[], int num, regdma_link_priority_t priority, uint32_t module)
450 {
451 _lock_acquire_recursive(&s_retention.lock);
452 esp_err_t err = sleep_retention_entries_create_bonding(priority, module);
453 if(err) goto error;
454 err = sleep_retention_entries_create_impl(retent, num, priority, module);
455 if(err) goto error;
456 err = sleep_retention_entries_create_bonding(priority, module);
457 if(err) goto error;
458 s_retention.modules |= module;
459 sleep_retention_entries_join();
460
461 error:
462 _lock_release_recursive(&s_retention.lock);
463 return err;
464 }
465
sleep_retention_entries_create(const sleep_retention_entries_config_t retent[],int num,regdma_link_priority_t priority,int module)466 esp_err_t sleep_retention_entries_create(const sleep_retention_entries_config_t retent[], int num, regdma_link_priority_t priority, int module)
467 {
468 if (!(retent && num > 0 && (priority < SLEEP_RETENTION_REGDMA_LINK_NR_PRIORITIES) && (module != 0))) {
469 return ESP_ERR_INVALID_ARG;
470 }
471 if (s_retention.lock == NULL) {
472 _lock_init_recursive(&s_retention.lock);
473 if (s_retention.lock == NULL) {
474 ESP_LOGE(TAG, "Create sleep retention lock failed");
475 return ESP_ERR_NO_MEM;
476 }
477 }
478 esp_err_t err = sleep_retention_entries_check_and_create_final_default();
479 if (err) goto error;
480 err = sleep_retention_entries_create_wrapper(retent, num, priority, module);
481 if (err) goto error;
482 pmu_sleep_enable_regdma_backup();
483 ESP_ERROR_CHECK(esp_deep_sleep_register_hook(&pmu_sleep_disable_regdma_backup));
484
485 error:
486 return err;
487 }
488
sleep_retention_entries_get(sleep_retention_entries_t * entries)489 void sleep_retention_entries_get(sleep_retention_entries_t *entries)
490 {
491 memset(entries, 0, sizeof(sleep_retention_entries_t));
492 _lock_acquire_recursive(&s_retention.lock);
493 if (s_retention.highpri >= SLEEP_RETENTION_REGDMA_LINK_HIGHEST_PRIORITY &&
494 s_retention.highpri <= SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY) {
495 memcpy(entries, &s_retention.lists[s_retention.highpri].entries, sizeof(sleep_retention_entries_t));
496 }
497 _lock_release_recursive(&s_retention.lock);
498 }
499
sleep_retention_get_modules(void)500 uint32_t IRAM_ATTR sleep_retention_get_modules(void)
501 {
502 return s_retention.modules;
503 }
504
505 #if SOC_PM_RETENTION_HAS_CLOCK_BUG
sleep_retention_do_extra_retention(bool backup_or_restore)506 void sleep_retention_do_extra_retention(bool backup_or_restore)
507 {
508 _lock_acquire_recursive(&s_retention.lock);
509 if (s_retention.highpri < SLEEP_RETENTION_REGDMA_LINK_HIGHEST_PRIORITY ||
510 s_retention.highpri > SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY) {
511 _lock_release_recursive(&s_retention.lock);
512 return;
513 }
514 const uint32_t clk_bug_modules = SLEEP_RETENTION_MODULE_BLE_MAC | SLEEP_RETENTION_MODULE_802154_MAC;
515 const int cnt_modules = __builtin_popcount(clk_bug_modules & s_retention.modules);
516 // Set extra linked list head pointer to hardware
517 pau_regdma_set_extra_link_addr(s_retention.lists[s_retention.highpri].entries[EXTRA_LINK_NUM]);
518 if (backup_or_restore) {
519 if (s_retention.extra_refs++ == (cnt_modules - 1)) {
520 pau_regdma_trigger_extra_link_backup();
521 }
522 } else {
523 if (--s_retention.extra_refs == (cnt_modules - 1)) {
524 pau_regdma_trigger_extra_link_restore();
525 }
526 }
527 int refs = s_retention.extra_refs;
528 _lock_release_recursive(&s_retention.lock);
529 assert(refs >= 0 && refs <= cnt_modules);
530 }
531
sleep_retention_module_deinit(void)532 void sleep_retention_module_deinit(void)
533 {
534 _lock_acquire_recursive(&s_retention.lock);
535 if (s_retention.extra_refs) {
536 s_retention.extra_refs--;
537 }
538 _lock_release_recursive(&s_retention.lock);
539 }
540 #endif
541
542 #if SOC_PM_RETENTION_HAS_REGDMA_POWER_BUG
sleep_retention_do_system_retention(bool backup_or_restore)543 void IRAM_ATTR sleep_retention_do_system_retention(bool backup_or_restore)
544 {
545 #define SYSTEM_LINK_NUM (0)
546 if (s_retention.highpri >= SLEEP_RETENTION_REGDMA_LINK_HIGHEST_PRIORITY &&
547 s_retention.highpri <= SLEEP_RETENTION_REGDMA_LINK_LOWEST_PRIORITY) {
548 // Set extra linked list head pointer to hardware
549 pau_regdma_set_system_link_addr(s_retention.lists[s_retention.highpri].entries[SYSTEM_LINK_NUM]);
550 if (backup_or_restore) {
551 pau_regdma_trigger_system_link_backup();
552 } else {
553 pau_regdma_trigger_system_link_restore();
554 }
555 }
556 }
557 #endif
558