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