1 /*
2  * SPDX-FileCopyrightText: 2015-2024 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_log.h"
13 #include "esp_attr.h"
14 #include "esp_sleep.h"
15 #include "soc/soc_caps.h"
16 #include "esp_private/pm_impl.h"
17 #include "esp_private/sleep_modem.h"
18 #include "esp_private/sleep_retention.h"
19 #include "sdkconfig.h"
20 
21 #if SOC_PM_MODEM_RETENTION_BY_REGDMA
22 #include "modem/modem_syscon_reg.h"
23 #include "modem/modem_lpcon_reg.h"
24 #include "soc/i2c_ana_mst_reg.h"
25 #include "esp_pau.h"
26 #endif
27 
28 #if SOC_PM_SUPPORT_PMU_MODEM_STATE
29 #include "soc/pmu_reg.h"
30 #include "esp_private/esp_pau.h"
31 #include "esp_private/esp_pmu.h"
32 #endif
33 
34 static __attribute__((unused)) const char *TAG = "sleep_modem";
35 
36 #if CONFIG_PM_SLP_DEFAULT_PARAMS_OPT
37 static void esp_pm_light_sleep_default_params_config(int min_freq_mhz, int max_freq_mhz);
38 #endif
39 
40 #if SOC_PM_RETENTION_HAS_CLOCK_BUG && CONFIG_MAC_BB_PD
41 static bool s_modem_sleep = false;
42 static uint8_t s_modem_prepare_ref = 0;
43 static _lock_t s_modem_prepare_lock;
44 #endif // SOC_PM_RETENTION_HAS_CLOCK_BUG && CONFIG_MAC_BB_PD
45 
46 #if CONFIG_MAC_BB_PD
47 #define MAC_BB_POWER_DOWN_CB_NO     (3)
48 #define MAC_BB_POWER_UP_CB_NO       (3)
49 
50 static DRAM_ATTR mac_bb_power_down_cb_t s_mac_bb_power_down_cb[MAC_BB_POWER_DOWN_CB_NO];
51 static DRAM_ATTR mac_bb_power_up_cb_t   s_mac_bb_power_up_cb[MAC_BB_POWER_UP_CB_NO];
52 
esp_register_mac_bb_pd_callback(mac_bb_power_down_cb_t cb)53 esp_err_t esp_register_mac_bb_pd_callback(mac_bb_power_down_cb_t cb)
54 {
55     int index = MAC_BB_POWER_DOWN_CB_NO;
56     for (int i = MAC_BB_POWER_DOWN_CB_NO - 1; i >= 0; i--) {
57         if (s_mac_bb_power_down_cb[i] == cb) {
58             return ESP_OK;
59         }
60 
61         if (s_mac_bb_power_down_cb[i] == NULL) {
62             index = i;
63         }
64     }
65 
66     if (index < MAC_BB_POWER_DOWN_CB_NO) {
67         s_mac_bb_power_down_cb[index] = cb;
68         return ESP_OK;
69     }
70 
71     return ESP_ERR_NO_MEM;
72 }
73 
esp_unregister_mac_bb_pd_callback(mac_bb_power_down_cb_t cb)74 esp_err_t esp_unregister_mac_bb_pd_callback(mac_bb_power_down_cb_t cb)
75 {
76     for (int i = MAC_BB_POWER_DOWN_CB_NO - 1; i >= 0; i--) {
77         if (s_mac_bb_power_down_cb[i] == cb) {
78             s_mac_bb_power_down_cb[i] = NULL;
79             return ESP_OK;
80         }
81     }
82     return ESP_ERR_INVALID_STATE;
83 }
84 
mac_bb_power_down_cb_execute(void)85 void IRAM_ATTR mac_bb_power_down_cb_execute(void)
86 {
87     for (int i = 0; i < MAC_BB_POWER_DOWN_CB_NO; i++) {
88         if (s_mac_bb_power_down_cb[i]) {
89             s_mac_bb_power_down_cb[i]();
90         }
91     }
92 }
93 
esp_register_mac_bb_pu_callback(mac_bb_power_up_cb_t cb)94 esp_err_t esp_register_mac_bb_pu_callback(mac_bb_power_up_cb_t cb)
95 {
96     int index = MAC_BB_POWER_UP_CB_NO;
97     for (int i = MAC_BB_POWER_UP_CB_NO - 1; i >= 0; i--) {
98         if (s_mac_bb_power_up_cb[i] == cb) {
99             return ESP_OK;
100         }
101 
102         if (s_mac_bb_power_up_cb[i] == NULL) {
103             index = i;
104         }
105     }
106 
107     if (index < MAC_BB_POWER_UP_CB_NO) {
108         s_mac_bb_power_up_cb[index] = cb;
109         return ESP_OK;
110     }
111 
112     return ESP_ERR_NO_MEM;
113 }
114 
esp_unregister_mac_bb_pu_callback(mac_bb_power_up_cb_t cb)115 esp_err_t esp_unregister_mac_bb_pu_callback(mac_bb_power_up_cb_t cb)
116 {
117     for (int i = MAC_BB_POWER_UP_CB_NO - 1; i >= 0; i--) {
118         if (s_mac_bb_power_up_cb[i] == cb) {
119             s_mac_bb_power_up_cb[i] = NULL;
120             return ESP_OK;
121         }
122     }
123     return ESP_ERR_INVALID_STATE;
124 }
125 
mac_bb_power_up_cb_execute(void)126 void IRAM_ATTR mac_bb_power_up_cb_execute(void)
127 {
128     for (int i = 0; i < MAC_BB_POWER_UP_CB_NO; i++) {
129         if (s_mac_bb_power_up_cb[i]) {
130             s_mac_bb_power_up_cb[i]();
131         }
132     }
133 }
134 
135 #endif ///CONFIG_MAC_BB_PD
136 
137 #if SOC_PM_SUPPORT_PMU_MODEM_STATE
138 
139 #define PMU_RF_PWR_REG                  (0x600b0154)
140 #define SARADC_TSENS_REG                (0x6000e058)
141 #define SARADC_TSENS_PU                 (BIT(22))
142 #define FECOEX_SET_FREQ_SET_CHAN_REG    (0x600a00c0)
143 #define FECOEX_SET_CHAN_EN              (BIT(14))
144 #define FECOEX_SET_FREQ_SET_CHAN_ST_REG (0x600a00cc)
145 #define FECOEX_SET_CHAN_DONE            (BIT(8))
146 #define FECOEX_AGC_CONF_REG             (0x600a7030)
147 #define FECOEX_AGC_DIS                  (BIT(29))
148 #define WDEVTXQ_BLOCK                   (0x600A4ca8)
149 #define WDEV_RXBLOCK                    (BIT(12))
150 #define MODEM_FE_DATA_BASE              (0x600a0400)
151 #define MODEM_FE_CTRL_BASE              (0x600a0800)
152 
153 #define I2C_BURST_VAL(host, start, end) (((host) << 31) | ((end) << 22) | ((start) << 16))
154 
155 typedef struct {
156     struct {
157         uint8_t start, end; /* the start and end index of phy i2c master command memory */
158         uint8_t host_id;    /* phy i2c master host id */
159     } config[2];
160 } phy_i2c_master_command_attribute_t;
161 
162 typedef struct sleep_modem_config {
163     struct {
164         void    *phy_link;
165         union {
166             struct {
167                 uint32_t modem_state_phy_done: 1;
168                 uint32_t reserved: 31;
169             };
170             uint32_t flags;
171         };
172     } wifi;
173 } sleep_modem_config_t;
174 
175 static sleep_modem_config_t s_sleep_modem = { .wifi.phy_link = NULL, .wifi.flags = 0 };
176 
sleep_modem_wifi_modem_state_init(void)177 esp_err_t sleep_modem_wifi_modem_state_init(void)
178 {
179     esp_err_t err = ESP_OK;
180     phy_i2c_master_command_attribute_t cmd;
181 
182     /* get RF on or off configuration info of i2c master command memory */
183     extern void phy_i2c_master_mem_cfg(phy_i2c_master_command_attribute_t *);
184     phy_i2c_master_mem_cfg(&cmd);
185 
186     ESP_LOGD(TAG, "Modem link i2c master configuration: (%d,%d,%d), (%d,%d,%d)", cmd.config[0].host_id, cmd.config[0].start,
187             cmd.config[0].end, cmd.config[1].host_id, cmd.config[1].start, cmd.config[1].end);
188 
189     static regdma_link_config_t wifi_modem_config[] = {
190         [0] = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_MODEM_FE_LINK(0), MODEM_FE_DATA_BASE, MODEM_FE_DATA_BASE, 41, 0, 0),
191         [1] = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_MODEM_FE_LINK(1), MODEM_FE_CTRL_BASE, MODEM_FE_CTRL_BASE, 87, 0, 0),
192 
193         [2]  = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x00), MODEM_LPCON_CLK_CONF_REG,         MODEM_LPCON_CLK_I2C_MST_EN,        MODEM_LPCON_CLK_I2C_MST_EN_M,       1, 0), /* I2C MST enable */
194         [3]  = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x01), MODEM_LPCON_I2C_MST_CLK_CONF_REG, MODEM_LPCON_CLK_I2C_MST_SEL_160M,  MODEM_LPCON_CLK_I2C_MST_SEL_160M_M, 1, 0), /* I2C MST sel 160m enable */
195 
196         /* PMU or software to trigger enable RF PHY */
197         [4]  = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x02), I2C_ANA_MST_ANA_CONF0_REG,        0x8,                       0xc,        1, 0), /* BBPLL calibration enable */
198         [5]  = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x03), PMU_RF_PWR_REG,                   0xf0000000,                0xf0000000, 1, 0),
199         [6]  = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x04), SARADC_TSENS_REG,                 SARADC_TSENS_PU,           0x400000,   1, 0),
200         [7]  = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x05), I2C_ANA_MST_I2C_BURST_CONF_REG,   0,                         0xffffffff, 1, 0),
201         [8]  = REGDMA_LINK_WAIT_INIT (REGDMA_PHY_LINK(0x06), I2C_ANA_MST_I2C_BURST_STATUS_REG, I2C_ANA_MST_BURST_DONE,    0x1,        1, 0),
202         [9]  = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x07), FECOEX_SET_FREQ_SET_CHAN_REG,     FECOEX_SET_CHAN_EN,        0x4000,     1, 0),
203         [10] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x08), FECOEX_SET_FREQ_SET_CHAN_REG,     0,                         0x4000,     1, 0),
204         [11] = REGDMA_LINK_WAIT_INIT (REGDMA_PHY_LINK(0x09), FECOEX_SET_FREQ_SET_CHAN_ST_REG,  FECOEX_SET_CHAN_DONE,      0x100,      1, 0),
205         [12] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x0a), MODEM_SYSCON_WIFI_BB_CFG_REG,     BIT(1),                    0x2,        1, 0),
206         [13] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x0b), FECOEX_AGC_CONF_REG,              0,                         0x20000000, 1, 0),
207 
208         /* PMU to trigger enable RXBLOCK */
209         [14] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x0c), WDEVTXQ_BLOCK,                    0,                         0x1000,     1, 0),
210 
211         /* PMU or software to trigger disable RF PHY */
212         [15] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x0d), FECOEX_AGC_CONF_REG,              FECOEX_AGC_DIS,            0x20000000, 0, 1),
213         [16] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x0e), MODEM_SYSCON_WIFI_BB_CFG_REG,     0,                         0x2,        0, 1),
214         [17] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x0f), FECOEX_SET_FREQ_SET_CHAN_REG,     0,                         0x4000,     0, 1),
215         [18] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x10), I2C_ANA_MST_I2C_BURST_CONF_REG,   0,                         0xffffffff, 0, 1),
216         [19] = REGDMA_LINK_WAIT_INIT (REGDMA_PHY_LINK(0x11), I2C_ANA_MST_I2C_BURST_STATUS_REG, I2C_ANA_MST_BURST_DONE,    0x1,        0, 1),
217         [20] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x12), SARADC_TSENS_REG,                 0,                         0x400000,   0, 1),
218         [21] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x13), PMU_RF_PWR_REG,                   0,                         0xf0000000, 0, 1),
219         [22] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x14), I2C_ANA_MST_ANA_CONF0_REG,        0x4,                       0xc,        0, 1), /* BBPLL calibration disable */
220 
221         [23] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x15), MODEM_LPCON_CLK_CONF_REG,         0,                         MODEM_LPCON_CLK_I2C_MST_EN_M,       0, 1), /* I2C MST disable */
222 
223         /* PMU to trigger disable RXBLOCK */
224         [24] = REGDMA_LINK_WAIT_INIT (REGDMA_PHY_LINK(0x17), WDEVTXQ_BLOCK,                    0,                         0x6000,     0, 1),
225         [25] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x18), WDEVTXQ_BLOCK,                    WDEV_RXBLOCK,              0x1000,     0, 1),
226         [26] = REGDMA_LINK_WAIT_INIT (REGDMA_PHY_LINK(0x19), WDEVTXQ_BLOCK,                    0,                         0x6000,     0, 1),
227 
228         [27] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x1a), PMU_SLP_WAKEUP_CNTL7_REG,         0x200000,                  0xffff0000, 1, 0),
229         [28] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x1b), PMU_SLP_WAKEUP_CNTL7_REG,         0x9730000,                 0xffff0000, 0, 1)
230     };
231     wifi_modem_config[7].write_wait.value  = I2C_BURST_VAL(cmd.config[1].host_id, cmd.config[1].start, cmd.config[1].end);
232     wifi_modem_config[18].write_wait.value = I2C_BURST_VAL(cmd.config[0].host_id, cmd.config[0].start, cmd.config[0].end);
233 
234     void *link = NULL;
235     if (s_sleep_modem.wifi.phy_link == NULL) {
236         for (int i = ARRAY_SIZE(wifi_modem_config) - 1; (err == ESP_OK) && (i >= 0); i--) {
237             void *next = regdma_link_init_safe(&wifi_modem_config[i], false, 0, link);
238             if (next) {
239                 link = next;
240             } else {
241                 regdma_link_destroy(link, 0);
242                 err = ESP_ERR_NO_MEM;
243             }
244         }
245         if (err == ESP_OK) {
246             pau_regdma_set_modem_link_addr(link);
247             s_sleep_modem.wifi.phy_link = link;
248             s_sleep_modem.wifi.flags = 0;
249         }
250     }
251     return err;
252 }
253 
sleep_modem_wifi_modem_state_deinit(void)254 __attribute__((unused)) void sleep_modem_wifi_modem_state_deinit(void)
255 {
256     if (s_sleep_modem.wifi.phy_link) {
257         regdma_link_destroy(s_sleep_modem.wifi.phy_link, 0);
258         s_sleep_modem.wifi.phy_link = NULL;
259         s_sleep_modem.wifi.flags = 0;
260     }
261 }
262 
sleep_modem_wifi_do_phy_retention(bool restore)263 void IRAM_ATTR sleep_modem_wifi_do_phy_retention(bool restore)
264 {
265     if (restore) {
266         pau_regdma_trigger_modem_link_restore();
267     } else {
268         pau_regdma_trigger_modem_link_backup();
269         s_sleep_modem.wifi.modem_state_phy_done = 1;
270     }
271 }
272 
sleep_modem_wifi_modem_state_enabled(void)273 inline __attribute__((always_inline)) bool sleep_modem_wifi_modem_state_enabled(void)
274 {
275     return (s_sleep_modem.wifi.phy_link != NULL);
276 }
277 
sleep_modem_wifi_modem_link_done(void)278 inline __attribute__((always_inline)) bool sleep_modem_wifi_modem_link_done(void)
279 {
280     return (s_sleep_modem.wifi.modem_state_phy_done == 1);
281 }
282 
283 #endif /* SOC_PM_SUPPORT_PMU_MODEM_STATE */
284 
modem_domain_pd_allowed(void)285 bool IRAM_ATTR modem_domain_pd_allowed(void)
286 {
287 #if SOC_PM_MODEM_RETENTION_BY_REGDMA
288     const uint32_t inited_modules = sleep_retention_get_inited_modules();
289     const uint32_t created_modules = sleep_retention_get_created_modules();
290 
291     uint32_t mask = 0;
292 #if SOC_WIFI_SUPPORTED
293     mask |= BIT(SLEEP_RETENTION_MODULE_WIFI_MAC) | BIT(SLEEP_RETENTION_MODULE_WIFI_BB);
294 #endif
295 #if SOC_BT_SUPPORTED
296     mask |= BIT(SLEEP_RETENTION_MODULE_BLE_MAC) | BIT(SLEEP_RETENTION_MODULE_BT_BB);
297 #endif
298 #if SOC_IEEE802154_SUPPORTED
299     mask |= BIT(SLEEP_RETENTION_MODULE_802154_MAC) | BIT(SLEEP_RETENTION_MODULE_BT_BB);
300 #endif
301     return ((inited_modules & mask) == (created_modules & mask));
302 #else
303     return false; /* MODEM power domain is controlled by each module (WiFi, Bluetooth or 15.4) of modem */
304 #endif
305 }
306 
sleep_modem_reject_triggers(void)307 uint32_t IRAM_ATTR sleep_modem_reject_triggers(void)
308 {
309     uint32_t reject_triggers = 0;
310 #if SOC_PM_SUPPORT_PMU_MODEM_STATE
311     reject_triggers = (s_sleep_modem.wifi.phy_link != NULL) ? BIT(16) : 0;
312 #endif
313     return reject_triggers;
314 }
315 
sleep_modem_wifi_modem_state_skip_light_sleep(void)316 bool IRAM_ATTR sleep_modem_wifi_modem_state_skip_light_sleep(void)
317 {
318     bool skip = false;
319 #if SOC_PM_SUPPORT_PMU_MODEM_STATE
320     /* To block the system from entering sleep before modem link done. In light
321      * sleep mode, the system may switch to modem state, which will cause
322      * hardware to fail to enable RF */
323     skip = sleep_modem_wifi_modem_state_enabled() && !sleep_modem_wifi_modem_link_done();
324 #endif
325     return skip;
326 }
327 
sleep_modem_configure(int max_freq_mhz,int min_freq_mhz,bool light_sleep_enable)328 esp_err_t sleep_modem_configure(int max_freq_mhz, int min_freq_mhz, bool light_sleep_enable)
329 {
330 #if CONFIG_ESP_WIFI_ENHANCED_LIGHT_SLEEP
331     extern int esp_wifi_internal_light_sleep_configure(bool);
332     esp_wifi_internal_light_sleep_configure(light_sleep_enable);
333 #endif
334 #if CONFIG_PM_SLP_DEFAULT_PARAMS_OPT
335     if (light_sleep_enable) {
336         esp_pm_light_sleep_default_params_config(min_freq_mhz, max_freq_mhz);
337     }
338 #endif
339     return ESP_OK;
340 }
341 
342 #define PERIPH_INFORM_OUT_LIGHT_SLEEP_OVERHEAD_NO 2
343 
344 /* Inform peripherals of light sleep wakeup overhead time */
345 static inform_out_light_sleep_overhead_cb_t s_periph_inform_out_light_sleep_overhead_cb[PERIPH_INFORM_OUT_LIGHT_SLEEP_OVERHEAD_NO];
346 
esp_pm_register_inform_out_light_sleep_overhead_callback(inform_out_light_sleep_overhead_cb_t cb)347 esp_err_t esp_pm_register_inform_out_light_sleep_overhead_callback(inform_out_light_sleep_overhead_cb_t cb)
348 {
349     for (int i = 0; i < PERIPH_INFORM_OUT_LIGHT_SLEEP_OVERHEAD_NO; i++) {
350         if (s_periph_inform_out_light_sleep_overhead_cb[i] == cb) {
351             return ESP_OK;
352         } else if (s_periph_inform_out_light_sleep_overhead_cb[i] == NULL) {
353             s_periph_inform_out_light_sleep_overhead_cb[i] = cb;
354             return ESP_OK;
355         }
356     }
357     return ESP_ERR_NO_MEM;
358 }
359 
esp_pm_unregister_inform_out_light_sleep_overhead_callback(inform_out_light_sleep_overhead_cb_t cb)360 esp_err_t esp_pm_unregister_inform_out_light_sleep_overhead_callback(inform_out_light_sleep_overhead_cb_t cb)
361 {
362     for (int i = 0; i < PERIPH_INFORM_OUT_LIGHT_SLEEP_OVERHEAD_NO; i++) {
363         if (s_periph_inform_out_light_sleep_overhead_cb[i] == cb) {
364             s_periph_inform_out_light_sleep_overhead_cb[i] = NULL;
365             return ESP_OK;
366         }
367     }
368     return ESP_ERR_INVALID_STATE;
369 }
370 
periph_inform_out_light_sleep_overhead(uint32_t out_light_sleep_time)371 void periph_inform_out_light_sleep_overhead(uint32_t out_light_sleep_time)
372 {
373     for (int i = 0; i < PERIPH_INFORM_OUT_LIGHT_SLEEP_OVERHEAD_NO; i++) {
374         if (s_periph_inform_out_light_sleep_overhead_cb[i]) {
375             s_periph_inform_out_light_sleep_overhead_cb[i](out_light_sleep_time);
376         }
377     }
378 }
379 
380 static update_light_sleep_default_params_config_cb_t s_light_sleep_default_params_config_cb = NULL;
381 
esp_pm_register_light_sleep_default_params_config_callback(update_light_sleep_default_params_config_cb_t cb)382 void esp_pm_register_light_sleep_default_params_config_callback(update_light_sleep_default_params_config_cb_t cb)
383 {
384     if (s_light_sleep_default_params_config_cb == NULL) {
385         s_light_sleep_default_params_config_cb = cb;
386     }
387 }
388 
esp_pm_unregister_light_sleep_default_params_config_callback(void)389 void esp_pm_unregister_light_sleep_default_params_config_callback(void)
390 {
391     if (s_light_sleep_default_params_config_cb) {
392         s_light_sleep_default_params_config_cb = NULL;
393     }
394 }
395 
396 #if CONFIG_PM_SLP_DEFAULT_PARAMS_OPT
esp_pm_light_sleep_default_params_config(int min_freq_mhz,int max_freq_mhz)397 static void esp_pm_light_sleep_default_params_config(int min_freq_mhz, int max_freq_mhz)
398 {
399     if (s_light_sleep_default_params_config_cb) {
400         (*s_light_sleep_default_params_config_cb)(min_freq_mhz, max_freq_mhz);
401     }
402 }
403 #endif
404 
405 #if SOC_PM_RETENTION_HAS_CLOCK_BUG && CONFIG_MAC_BB_PD
sleep_modem_register_mac_bb_module_prepare_callback(mac_bb_power_down_cb_t pd_cb,mac_bb_power_up_cb_t pu_cb)406 void sleep_modem_register_mac_bb_module_prepare_callback(mac_bb_power_down_cb_t pd_cb,
407                                                          mac_bb_power_up_cb_t pu_cb)
408 {
409     _lock_acquire(&s_modem_prepare_lock);
410     if (s_modem_prepare_ref++ == 0) {
411         esp_register_mac_bb_pd_callback(pd_cb);
412         esp_register_mac_bb_pu_callback(pu_cb);
413     }
414     _lock_release(&s_modem_prepare_lock);
415 }
416 
sleep_modem_unregister_mac_bb_module_prepare_callback(mac_bb_power_down_cb_t pd_cb,mac_bb_power_up_cb_t pu_cb)417 void sleep_modem_unregister_mac_bb_module_prepare_callback(mac_bb_power_down_cb_t pd_cb,
418                                                            mac_bb_power_up_cb_t pu_cb)
419 {
420     _lock_acquire(&s_modem_prepare_lock);
421     assert(s_modem_prepare_ref);
422     if (--s_modem_prepare_ref == 0) {
423         esp_unregister_mac_bb_pd_callback(pd_cb);
424         esp_unregister_mac_bb_pu_callback(pu_cb);
425     }
426     _lock_release(&s_modem_prepare_lock);
427 
428 }
429 
430 /**
431  * @brief Switch root clock source to PLL do retention and switch back
432  *
433  * This function is used when Bluetooth/IEEE802154 module requires register backup/restore, this function
434  * is called ONLY when SOC_PM_RETENTION_HAS_CLOCK_BUG is set.
435  * @param backup true for backup, false for restore
436  * @param cpu_freq_mhz cpu frequency to do retention
437  * @param do_retention function for retention
438  */
rtc_clk_cpu_freq_to_pll_mhz_and_do_retention(bool backup,int cpu_freq_mhz,void (* do_retention)(bool))439 static void IRAM_ATTR rtc_clk_cpu_freq_to_pll_mhz_and_do_retention(bool backup, int cpu_freq_mhz, void (*do_retention)(bool))
440 {
441 #if SOC_PM_SUPPORT_PMU_MODEM_STATE
442     if (pmu_sleep_pll_already_enabled()) {
443         return;
444     }
445 #endif
446     rtc_cpu_freq_config_t config, pll_config;
447     rtc_clk_cpu_freq_get_config(&config);
448 
449     rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &pll_config);
450     rtc_clk_cpu_freq_set_config(&pll_config);
451 
452     if (do_retention) {
453         (*do_retention)(backup);
454     }
455 
456     rtc_clk_cpu_freq_set_config(&config);
457 }
458 
sleep_modem_mac_bb_power_down_prepare(void)459 void IRAM_ATTR sleep_modem_mac_bb_power_down_prepare(void)
460 {
461     if (s_modem_sleep == false) {
462         rtc_clk_cpu_freq_to_pll_mhz_and_do_retention(true,
463                                                      CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ,
464                                                      sleep_retention_do_extra_retention);
465         s_modem_sleep = true;
466     }
467 }
468 
sleep_modem_mac_bb_power_up_prepare(void)469 void IRAM_ATTR sleep_modem_mac_bb_power_up_prepare(void)
470 {
471     if (s_modem_sleep) {
472         rtc_clk_cpu_freq_to_pll_mhz_and_do_retention(false,
473                                                      CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ,
474                                                      sleep_retention_do_extra_retention);
475         s_modem_sleep = false;
476     }
477 }
478 #endif /* SOC_PM_RETENTION_HAS_CLOCK_BUG && CONFIG_MAC_BB_PD */
479