1 /*
2 * SPDX-FileCopyrightText: 2015-2021 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 #endif
32
33 static __attribute__((unused)) const char *TAG = "sleep_modem";
34
35 #if CONFIG_PM_SLP_DEFAULT_PARAMS_OPT
36 static void esp_pm_light_sleep_default_params_config(int min_freq_mhz, int max_freq_mhz);
37 #endif
38
39 #if CONFIG_MAC_BB_PD
40 #define MAC_BB_POWER_DOWN_CB_NO (2)
41 #define MAC_BB_POWER_UP_CB_NO (2)
42
43 static DRAM_ATTR mac_bb_power_down_cb_t s_mac_bb_power_down_cb[MAC_BB_POWER_DOWN_CB_NO];
44 static DRAM_ATTR mac_bb_power_up_cb_t s_mac_bb_power_up_cb[MAC_BB_POWER_UP_CB_NO];
45
esp_register_mac_bb_pd_callback(mac_bb_power_down_cb_t cb)46 esp_err_t esp_register_mac_bb_pd_callback(mac_bb_power_down_cb_t cb)
47 {
48 int index = MAC_BB_POWER_DOWN_CB_NO;
49 for (int i = MAC_BB_POWER_DOWN_CB_NO - 1; i >= 0; i--) {
50 if (s_mac_bb_power_down_cb[i] == cb) {
51 return ESP_OK;
52 }
53
54 if (s_mac_bb_power_down_cb[i] == NULL) {
55 index = i;
56 }
57 }
58
59 if (index < MAC_BB_POWER_DOWN_CB_NO) {
60 s_mac_bb_power_down_cb[index] = cb;
61 return ESP_OK;
62 }
63
64 return ESP_ERR_NO_MEM;
65 }
66
esp_unregister_mac_bb_pd_callback(mac_bb_power_down_cb_t cb)67 esp_err_t esp_unregister_mac_bb_pd_callback(mac_bb_power_down_cb_t cb)
68 {
69 for (int i = MAC_BB_POWER_DOWN_CB_NO - 1; i >= 0; i--) {
70 if (s_mac_bb_power_down_cb[i] == cb) {
71 s_mac_bb_power_down_cb[i] = NULL;
72 return ESP_OK;
73 }
74 }
75 return ESP_ERR_INVALID_STATE;
76 }
77
mac_bb_power_down_cb_execute(void)78 void IRAM_ATTR mac_bb_power_down_cb_execute(void)
79 {
80 for (int i = 0; i < MAC_BB_POWER_DOWN_CB_NO; i++) {
81 if (s_mac_bb_power_down_cb[i]) {
82 s_mac_bb_power_down_cb[i]();
83 }
84 }
85 }
86
esp_register_mac_bb_pu_callback(mac_bb_power_up_cb_t cb)87 esp_err_t esp_register_mac_bb_pu_callback(mac_bb_power_up_cb_t cb)
88 {
89 int index = MAC_BB_POWER_UP_CB_NO;
90 for (int i = MAC_BB_POWER_UP_CB_NO - 1; i >= 0; i--) {
91 if (s_mac_bb_power_up_cb[i] == cb) {
92 return ESP_OK;
93 }
94
95 if (s_mac_bb_power_up_cb[i] == NULL) {
96 index = i;
97 }
98 }
99
100 if (index < MAC_BB_POWER_UP_CB_NO) {
101 s_mac_bb_power_up_cb[index] = cb;
102 return ESP_OK;
103 }
104
105 return ESP_ERR_NO_MEM;
106 }
107
esp_unregister_mac_bb_pu_callback(mac_bb_power_up_cb_t cb)108 esp_err_t esp_unregister_mac_bb_pu_callback(mac_bb_power_up_cb_t cb)
109 {
110 for (int i = MAC_BB_POWER_UP_CB_NO - 1; i >= 0; i--) {
111 if (s_mac_bb_power_up_cb[i] == cb) {
112 s_mac_bb_power_up_cb[i] = NULL;
113 return ESP_OK;
114 }
115 }
116 return ESP_ERR_INVALID_STATE;
117 }
118
mac_bb_power_up_cb_execute(void)119 void IRAM_ATTR mac_bb_power_up_cb_execute(void)
120 {
121 for (int i = 0; i < MAC_BB_POWER_UP_CB_NO; i++) {
122 if (s_mac_bb_power_up_cb[i]) {
123 s_mac_bb_power_up_cb[i]();
124 }
125 }
126 }
127
128 #endif ///CONFIG_MAC_BB_PD
129
130 #if SOC_PM_SUPPORT_PMU_MODEM_STATE
131
132 #define PMU_RF_PWR_REG (0x600b0154)
133 #define SARADC_TSENS_REG (0x6000e058)
134 #define SARADC_TSENS_PU (BIT(22))
135 #define FECOEX_SET_FREQ_SET_CHAN_REG (0x600a00c0)
136 #define FECOEX_SET_CHAN_EN (BIT(14))
137 #define FECOEX_SET_FREQ_SET_CHAN_ST_REG (0x600a00cc)
138 #define FECOEX_SET_CHAN_DONE (BIT(8))
139 #define FECOEX_AGC_CONF_REG (0x600a7030)
140 #define FECOEX_AGC_DIS (BIT(29))
141 #define WDEVTXQ_BLOCK (0x600A4ca8)
142 #define WDEV_RXBLOCK (BIT(12))
143 #define MODEM_FE_DATA_BASE (0x600a0400)
144 #define MODEM_FE_CTRL_BASE (0x600a0800)
145
146 #define I2C_BURST_VAL(host, start, end) (((host) << 31) | ((end) << 22) | ((start) << 16))
147
148 typedef struct {
149 struct {
150 uint8_t start, end; /* the start and end index of phy i2c master command memory */
151 uint8_t host_id; /* phy i2c master host id */
152 } config[2];
153 } phy_i2c_master_command_attribute_t;
154
155 typedef struct sleep_modem_config {
156 struct {
157 void *phy_link;
158 union {
159 struct {
160 uint32_t modem_state_phy_done: 1;
161 uint32_t reserved: 31;
162 };
163 uint32_t flags;
164 };
165 } wifi;
166 } sleep_modem_config_t;
167
168 static sleep_modem_config_t s_sleep_modem = { .wifi.phy_link = NULL, .wifi.flags = 0 };
169
sleep_modem_wifi_modem_state_init(void)170 static __attribute__((unused)) esp_err_t sleep_modem_wifi_modem_state_init(void)
171 {
172 esp_err_t err = ESP_OK;
173 phy_i2c_master_command_attribute_t cmd;
174
175 /* get RF on or off configuration info of i2c master command memory */
176 extern void phy_i2c_master_mem_cfg(phy_i2c_master_command_attribute_t *);
177 phy_i2c_master_mem_cfg(&cmd);
178
179 ESP_LOGD(TAG, "Modem link i2c master configuration: (%d,%d,%d), (%d,%d,%d)", cmd.config[0].host_id, cmd.config[0].start,
180 cmd.config[0].end, cmd.config[1].host_id, cmd.config[1].start, cmd.config[1].end);
181
182 static regdma_link_config_t wifi_modem_config[] = {
183 [0] = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_MODEM_FE_LINK(0), MODEM_FE_DATA_BASE, MODEM_FE_DATA_BASE, 41, 0, 0),
184 [1] = REGDMA_LINK_CONTINUOUS_INIT(REGDMA_MODEM_FE_LINK(1), MODEM_FE_CTRL_BASE, MODEM_FE_CTRL_BASE, 87, 0, 0),
185
186 [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 */
187 [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 */
188
189 /* PMU or software to trigger enable RF PHY */
190 [4] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x02), I2C_ANA_MST_ANA_CONF0_REG, 0x8, 0xc, 1, 0), /* BBPLL calibration enable */
191 [5] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x03), PMU_RF_PWR_REG, 0xf0000000, 0xf0000000, 1, 0),
192 [6] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x04), SARADC_TSENS_REG, SARADC_TSENS_PU, 0x400000, 1, 0),
193 [7] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x05), I2C_ANA_MST_I2C_BURST_CONF_REG, 0, 0xffffffff, 1, 0),
194 [8] = REGDMA_LINK_WAIT_INIT (REGDMA_PHY_LINK(0x06), I2C_ANA_MST_I2C_BURST_STATUS_REG, I2C_ANA_MST_BURST_DONE, 0x1, 1, 0),
195 [9] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x07), FECOEX_SET_FREQ_SET_CHAN_REG, FECOEX_SET_CHAN_EN, 0x4000, 1, 0),
196 [10] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x08), FECOEX_SET_FREQ_SET_CHAN_REG, 0, 0x4000, 1, 0),
197 [11] = REGDMA_LINK_WAIT_INIT (REGDMA_PHY_LINK(0x09), FECOEX_SET_FREQ_SET_CHAN_ST_REG, FECOEX_SET_CHAN_DONE, 0x100, 1, 0),
198 [12] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x0a), MODEM_SYSCON_WIFI_BB_CFG_REG, BIT(1), 0x2, 1, 0),
199 [13] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x0b), FECOEX_AGC_CONF_REG, 0, 0x20000000, 1, 0),
200
201 /* PMU to trigger enable RXBLOCK */
202 [14] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x0c), WDEVTXQ_BLOCK, 0, 0x1000, 1, 0),
203
204 /* PMU or software to trigger disable RF PHY */
205 [15] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x0d), FECOEX_AGC_CONF_REG, FECOEX_AGC_DIS, 0x20000000, 0, 1),
206 [16] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x0e), MODEM_SYSCON_WIFI_BB_CFG_REG, 0, 0x2, 0, 1),
207 [17] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x0f), FECOEX_SET_FREQ_SET_CHAN_REG, 0, 0x4000, 0, 1),
208 [18] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x10), I2C_ANA_MST_I2C_BURST_CONF_REG, 0, 0xffffffff, 0, 1),
209 [19] = REGDMA_LINK_WAIT_INIT (REGDMA_PHY_LINK(0x11), I2C_ANA_MST_I2C_BURST_STATUS_REG, I2C_ANA_MST_BURST_DONE, 0x1, 0, 1),
210 [20] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x12), SARADC_TSENS_REG, 0, 0x400000, 0, 1),
211 [21] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x13), PMU_RF_PWR_REG, 0, 0xf0000000, 0, 1),
212 [22] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x14), I2C_ANA_MST_ANA_CONF0_REG, 0x4, 0xc, 0, 1), /* BBPLL calibration disable */
213
214 [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 */
215
216 /* PMU to trigger disable RXBLOCK */
217 [24] = REGDMA_LINK_WAIT_INIT (REGDMA_PHY_LINK(0x17), WDEVTXQ_BLOCK, 0, 0x6000, 0, 1),
218 [25] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x18), WDEVTXQ_BLOCK, WDEV_RXBLOCK, 0x1000, 0, 1),
219 [26] = REGDMA_LINK_WAIT_INIT (REGDMA_PHY_LINK(0x19), WDEVTXQ_BLOCK, 0, 0x6000, 0, 1),
220
221 [27] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x1a), PMU_SLP_WAKEUP_CNTL7_REG, 0x200000, 0xffff0000, 1, 0),
222 [28] = REGDMA_LINK_WRITE_INIT(REGDMA_PHY_LINK(0x1b), PMU_SLP_WAKEUP_CNTL7_REG, 0x9730000, 0xffff0000, 0, 1)
223 };
224 wifi_modem_config[7].write_wait.value = I2C_BURST_VAL(cmd.config[1].host_id, cmd.config[1].start, cmd.config[1].end);
225 wifi_modem_config[18].write_wait.value = I2C_BURST_VAL(cmd.config[0].host_id, cmd.config[0].start, cmd.config[0].end);
226
227 void *link = NULL;
228 if (s_sleep_modem.wifi.phy_link == NULL) {
229 for (int i = ARRAY_SIZE(wifi_modem_config) - 1; (err == ESP_OK) && (i >= 0); i--) {
230 void *next = regdma_link_init_safe(&wifi_modem_config[i], false, 0, link);
231 if (next) {
232 link = next;
233 } else {
234 regdma_link_destroy(link, 0);
235 err = ESP_ERR_NO_MEM;
236 }
237 }
238 if (err == ESP_OK) {
239 pau_regdma_set_modem_link_addr(link);
240 s_sleep_modem.wifi.phy_link = link;
241 s_sleep_modem.wifi.flags = 0;
242 }
243 }
244 return err;
245 }
246
sleep_modem_wifi_modem_state_deinit(void)247 static __attribute__((unused)) void sleep_modem_wifi_modem_state_deinit(void)
248 {
249 if (s_sleep_modem.wifi.phy_link) {
250 regdma_link_destroy(s_sleep_modem.wifi.phy_link, 0);
251 s_sleep_modem.wifi.phy_link = NULL;
252 s_sleep_modem.wifi.flags = 0;
253 }
254 }
255
sleep_modem_wifi_do_phy_retention(bool restore)256 void IRAM_ATTR sleep_modem_wifi_do_phy_retention(bool restore)
257 {
258 if (restore) {
259 pau_regdma_trigger_modem_link_restore();
260 } else {
261 pau_regdma_trigger_modem_link_backup();
262 s_sleep_modem.wifi.modem_state_phy_done = 1;
263 }
264 }
265
sleep_modem_wifi_modem_state_enabled(void)266 inline __attribute__((always_inline)) bool sleep_modem_wifi_modem_state_enabled(void)
267 {
268 return (s_sleep_modem.wifi.phy_link != NULL);
269 }
270
sleep_modem_wifi_modem_link_done(void)271 inline __attribute__((always_inline)) bool sleep_modem_wifi_modem_link_done(void)
272 {
273 return (s_sleep_modem.wifi.modem_state_phy_done == 1);
274 }
275
276 #endif /* SOC_PM_SUPPORT_PMU_MODEM_STATE */
277
modem_domain_pd_allowed(void)278 bool IRAM_ATTR modem_domain_pd_allowed(void)
279 {
280 #if SOC_PM_MODEM_RETENTION_BY_REGDMA
281 const uint32_t modules = sleep_retention_get_modules();
282 const uint32_t mask_wifi = (const uint32_t) (SLEEP_RETENTION_MODULE_WIFI_MAC |
283 SLEEP_RETENTION_MODULE_WIFI_BB);
284 const uint32_t mask_ble = (const uint32_t) (SLEEP_RETENTION_MODULE_BLE_MAC |
285 SLEEP_RETENTION_MODULE_BT_BB);
286 const uint32_t mask_154 = (const uint32_t) (SLEEP_RETENTION_MODULE_802154_MAC |
287 SLEEP_RETENTION_MODULE_BT_BB);
288 return (((modules & mask_wifi) == mask_wifi) ||
289 ((modules & mask_ble) == mask_ble) ||
290 ((modules & mask_154) == mask_154));
291 #else
292 return false; /* MODEM power domain is controlled by each module (WiFi, Bluetooth or 15.4) of modem */
293 #endif
294 }
295
sleep_modem_reject_triggers(void)296 uint32_t IRAM_ATTR sleep_modem_reject_triggers(void)
297 {
298 uint32_t reject_triggers = 0;
299 #if SOC_PM_SUPPORT_PMU_MODEM_STATE
300 reject_triggers = (s_sleep_modem.wifi.phy_link != NULL) ? BIT(16) : 0;
301 #endif
302 return reject_triggers;
303 }
304
sleep_modem_wifi_modem_state_skip_light_sleep(void)305 static __attribute__((unused)) bool IRAM_ATTR sleep_modem_wifi_modem_state_skip_light_sleep(void)
306 {
307 bool skip = false;
308 #if SOC_PM_SUPPORT_PMU_MODEM_STATE
309 /* To block the system from entering sleep before modem link done. In light
310 * sleep mode, the system may switch to modem state, which will cause
311 * hardware to fail to enable RF */
312 skip = sleep_modem_wifi_modem_state_enabled() && !sleep_modem_wifi_modem_link_done();
313 #endif
314 return skip;
315 }
316
sleep_modem_configure(int max_freq_mhz,int min_freq_mhz,bool light_sleep_enable)317 esp_err_t sleep_modem_configure(int max_freq_mhz, int min_freq_mhz, bool light_sleep_enable)
318 {
319 #if CONFIG_ESP_WIFI_ENHANCED_LIGHT_SLEEP
320 extern int esp_wifi_internal_mac_sleep_configure(bool, bool);
321 if (light_sleep_enable) {
322 if (sleep_modem_wifi_modem_state_init() == ESP_OK) {
323 esp_pm_register_skip_light_sleep_callback(sleep_modem_wifi_modem_state_skip_light_sleep);
324 esp_wifi_internal_mac_sleep_configure(light_sleep_enable, true); /* require WiFi to enable automatically receives the beacon */
325 }
326 } else {
327 esp_wifi_internal_mac_sleep_configure(light_sleep_enable, false); /* require WiFi to disable automatically receives the beacon */
328 esp_pm_unregister_skip_light_sleep_callback(sleep_modem_wifi_modem_state_skip_light_sleep);
329 sleep_modem_wifi_modem_state_deinit();
330 }
331 #endif
332 #if CONFIG_PM_SLP_DEFAULT_PARAMS_OPT
333 if (light_sleep_enable) {
334 esp_pm_light_sleep_default_params_config(min_freq_mhz, max_freq_mhz);
335 }
336 #endif
337 return ESP_OK;
338 }
339
340 #define PERIPH_INFORM_OUT_LIGHT_SLEEP_OVERHEAD_NO 1
341
342 /* Inform peripherals of light sleep wakeup overhead time */
343 static inform_out_light_sleep_overhead_cb_t s_periph_inform_out_light_sleep_overhead_cb[PERIPH_INFORM_OUT_LIGHT_SLEEP_OVERHEAD_NO];
344
esp_pm_register_inform_out_light_sleep_overhead_callback(inform_out_light_sleep_overhead_cb_t cb)345 esp_err_t esp_pm_register_inform_out_light_sleep_overhead_callback(inform_out_light_sleep_overhead_cb_t cb)
346 {
347 for (int i = 0; i < PERIPH_INFORM_OUT_LIGHT_SLEEP_OVERHEAD_NO; i++) {
348 if (s_periph_inform_out_light_sleep_overhead_cb[i] == cb) {
349 return ESP_OK;
350 } else if (s_periph_inform_out_light_sleep_overhead_cb[i] == NULL) {
351 s_periph_inform_out_light_sleep_overhead_cb[i] = cb;
352 return ESP_OK;
353 }
354 }
355 return ESP_ERR_NO_MEM;
356 }
357
esp_pm_unregister_inform_out_light_sleep_overhead_callback(inform_out_light_sleep_overhead_cb_t cb)358 esp_err_t esp_pm_unregister_inform_out_light_sleep_overhead_callback(inform_out_light_sleep_overhead_cb_t cb)
359 {
360 for (int i = 0; i < PERIPH_INFORM_OUT_LIGHT_SLEEP_OVERHEAD_NO; i++) {
361 if (s_periph_inform_out_light_sleep_overhead_cb[i] == cb) {
362 s_periph_inform_out_light_sleep_overhead_cb[i] = NULL;
363 return ESP_OK;
364 }
365 }
366 return ESP_ERR_INVALID_STATE;
367 }
368
periph_inform_out_light_sleep_overhead(uint32_t out_light_sleep_time)369 void periph_inform_out_light_sleep_overhead(uint32_t out_light_sleep_time)
370 {
371 for (int i = 0; i < PERIPH_INFORM_OUT_LIGHT_SLEEP_OVERHEAD_NO; i++) {
372 if (s_periph_inform_out_light_sleep_overhead_cb[i]) {
373 s_periph_inform_out_light_sleep_overhead_cb[i](out_light_sleep_time);
374 }
375 }
376 }
377
378 static update_light_sleep_default_params_config_cb_t s_light_sleep_default_params_config_cb = NULL;
379
esp_pm_register_light_sleep_default_params_config_callback(update_light_sleep_default_params_config_cb_t cb)380 void esp_pm_register_light_sleep_default_params_config_callback(update_light_sleep_default_params_config_cb_t cb)
381 {
382 if (s_light_sleep_default_params_config_cb == NULL) {
383 s_light_sleep_default_params_config_cb = cb;
384 }
385 }
386
esp_pm_unregister_light_sleep_default_params_config_callback(void)387 void esp_pm_unregister_light_sleep_default_params_config_callback(void)
388 {
389 if (s_light_sleep_default_params_config_cb) {
390 s_light_sleep_default_params_config_cb = NULL;
391 }
392 }
393
394 #if CONFIG_PM_SLP_DEFAULT_PARAMS_OPT
esp_pm_light_sleep_default_params_config(int min_freq_mhz,int max_freq_mhz)395 static void esp_pm_light_sleep_default_params_config(int min_freq_mhz, int max_freq_mhz)
396 {
397 if (s_light_sleep_default_params_config_cb) {
398 (*s_light_sleep_default_params_config_cb)(min_freq_mhz, max_freq_mhz);
399 }
400 }
401 #endif
402