modem_clock.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. /*
  2. * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdint.h>
  7. #include <stdlib.h>
  8. #include <esp_types.h>
  9. #include "sdkconfig.h"
  10. #include "esp_attr.h"
  11. #include "soc/soc.h"
  12. #include "freertos/FreeRTOS.h"
  13. #include "hal/clk_gate_ll.h"
  14. #include "esp_private/esp_modem_clock.h"
  15. #include "esp_private/esp_pmu.h"
  16. #include "esp_sleep.h"
  17. // Please define the frequently called modules in the low bit,
  18. // which will improve the execution efficiency
  19. typedef enum {
  20. MODEM_CLOCK_FE = BIT(0),
  21. MODEM_CLOCK_COEXIST = BIT(1),
  22. MODEM_CLOCK_I2C_MASTER = BIT(2),
  23. MODEM_CLOCK_WIFI_MAC = BIT(3),
  24. MODEM_CLOCK_WIFI_BB = BIT(4),
  25. MODEM_CLOCK_ETM = BIT(5),
  26. MODEM_CLOCK_BLE_MAC = BIT(6),
  27. MODEM_CLOCK_BLE_BB = BIT(7),
  28. MODEM_CLOCK_802154_MAC = BIT(8),
  29. MODEM_CLOCK_DATADUMP = BIT(9),
  30. MODEM_CLOCK_DEVICE_MAX = 10
  31. } modem_clock_device_t;
  32. typedef struct modem_clock_context {
  33. modem_clock_hal_context_t *hal;
  34. portMUX_TYPE lock;
  35. struct {
  36. int16_t refs;
  37. uint16_t reserved; /* reserved for 4 bytes aligned */
  38. void (*configure)(struct modem_clock_context *, bool);
  39. } dev[MODEM_CLOCK_DEVICE_MAX];
  40. /* the low-power clock source for each module */
  41. modem_clock_lpclk_src_t lpclk_src[PERIPH_MODEM_MODULE_NUM];
  42. } modem_clock_context_t;
  43. static void IRAM_ATTR modem_clock_wifi_mac_configure(modem_clock_context_t *ctx, bool enable)
  44. {
  45. if (enable) {
  46. modem_syscon_ll_enable_wifi_apb_clock(ctx->hal->syscon_dev, enable);
  47. modem_syscon_ll_enable_wifi_mac_clock(ctx->hal->syscon_dev, enable);
  48. }
  49. }
  50. static void IRAM_ATTR modem_clock_wifi_bb_configure(modem_clock_context_t *ctx, bool enable)
  51. {
  52. if (enable) {
  53. modem_syscon_ll_clk_wifibb_configure(ctx->hal->syscon_dev, enable);
  54. }
  55. }
  56. static void IRAM_ATTR modem_clock_ble_mac_configure(modem_clock_context_t *ctx, bool enable)
  57. {
  58. modem_syscon_ll_enable_etm_clock(ctx->hal->syscon_dev, enable);
  59. modem_syscom_ll_enable_modem_sec_clock(ctx->hal->syscon_dev, enable);
  60. modem_syscon_ll_enable_ble_timer_clock(ctx->hal->syscon_dev, enable);
  61. }
  62. static void IRAM_ATTR modem_clock_ble_bb_configure(modem_clock_context_t *ctx, bool enable)
  63. {
  64. modem_syscon_ll_enable_bt_apb_clock(ctx->hal->syscon_dev, enable);
  65. modem_syscon_ll_enable_bt_clock(ctx->hal->syscon_dev, enable);
  66. }
  67. static void IRAM_ATTR modem_clock_ieee802154_mac_configure(modem_clock_context_t *ctx, bool enable)
  68. {
  69. modem_syscon_ll_enable_ieee802154_apb_clock(ctx->hal->syscon_dev, enable);
  70. modem_syscon_ll_enable_ieee802154_mac_clock(ctx->hal->syscon_dev, enable);
  71. }
  72. static void IRAM_ATTR modem_clock_coex_configure(modem_clock_context_t *ctx, bool enable)
  73. {
  74. modem_lpcon_ll_enable_coex_clock(ctx->hal->lpcon_dev, enable);
  75. }
  76. static void IRAM_ATTR modem_clock_fe_configure(modem_clock_context_t *ctx, bool enable)
  77. {
  78. if (enable) {
  79. modem_syscon_ll_enable_fe_apb_clock(ctx->hal->syscon_dev, enable);
  80. modem_syscon_ll_enable_fe_cal_160m_clock(ctx->hal->syscon_dev, enable);
  81. modem_syscon_ll_enable_fe_160m_clock(ctx->hal->syscon_dev, enable);
  82. modem_syscon_ll_enable_fe_80m_clock(ctx->hal->syscon_dev, enable);
  83. }
  84. }
  85. static void IRAM_ATTR modem_clock_i2c_master_configure(modem_clock_context_t *ctx, bool enable)
  86. {
  87. modem_lpcon_ll_enable_i2c_master_clock(ctx->hal->lpcon_dev, enable);
  88. modem_lpcon_ll_enable_i2c_master_160m_clock(ctx->hal->lpcon_dev, enable);
  89. }
  90. static void IRAM_ATTR modem_clock_etm_configure(modem_clock_context_t *ctx, bool enable)
  91. {
  92. modem_syscon_ll_enable_etm_clock(ctx->hal->syscon_dev, enable);
  93. }
  94. static void IRAM_ATTR modem_clock_data_dump_configure(modem_clock_context_t *ctx, bool enable)
  95. {
  96. modem_syscon_ll_enable_data_dump_clock(ctx->hal->syscon_dev, enable);
  97. modem_syscon_ll_enable_data_dump_mux_clock(ctx->hal->syscon_dev, enable);
  98. }
  99. modem_clock_context_t * __attribute__((weak)) IRAM_ATTR MODEM_CLOCK_instance(void)
  100. {
  101. /* It should be explicitly defined in the internal RAM */
  102. static DRAM_ATTR modem_clock_hal_context_t modem_clock_hal = { .syscon_dev = &MODEM_SYSCON, .lpcon_dev = &MODEM_LPCON };
  103. static DRAM_ATTR modem_clock_context_t modem_clock_context = {
  104. .hal = &modem_clock_hal, .lock = portMUX_INITIALIZER_UNLOCKED,
  105. .dev = {
  106. { .refs = 0, .configure = modem_clock_fe_configure },
  107. { .refs = 0, .configure = modem_clock_coex_configure },
  108. { .refs = 0, .configure = modem_clock_i2c_master_configure },
  109. { .refs = 0, .configure = modem_clock_wifi_mac_configure },
  110. { .refs = 0, .configure = modem_clock_wifi_bb_configure },
  111. { .refs = 0, .configure = modem_clock_etm_configure },
  112. { .refs = 0, .configure = modem_clock_ble_mac_configure },
  113. { .refs = 0, .configure = modem_clock_ble_bb_configure },
  114. { .refs = 0, .configure = modem_clock_ieee802154_mac_configure },
  115. { .refs = 0, .configure = modem_clock_data_dump_configure }
  116. },
  117. .lpclk_src = { [0 ... PERIPH_MODEM_MODULE_NUM - 1] = MODEM_CLOCK_LPCLK_SRC_INVALID }
  118. };
  119. return &modem_clock_context;
  120. }
  121. static void IRAM_ATTR modem_clock_domain_power_state_icg_map_init(modem_clock_context_t *ctx)
  122. {
  123. #define ICG_NOGATING_SLEEP (BIT(PMU_HP_ICG_MODEM_CODE_SLEEP))
  124. #define ICG_NOGATING_MODEM (BIT(PMU_HP_ICG_MODEM_CODE_MODEM))
  125. #define ICG_NOGATING_ACTIVE (BIT(PMU_HP_ICG_MODEM_CODE_ACTIVE))
  126. /* the ICG code's bit 0, 1 and 2 indicates the ICG state
  127. * of pmu SLEEP, MODEM and ACTIVE mode respectively */
  128. const uint32_t code[MODEM_CLOCK_DOMAIN_MAX] = {
  129. [MODEM_CLOCK_DOMAIN_MODEM_APB] = ICG_NOGATING_ACTIVE | ICG_NOGATING_MODEM,
  130. [MODEM_CLOCK_DOMAIN_MODEM_PERIPH] = ICG_NOGATING_ACTIVE,
  131. [MODEM_CLOCK_DOMAIN_WIFI] = ICG_NOGATING_ACTIVE | ICG_NOGATING_MODEM,
  132. [MODEM_CLOCK_DOMAIN_BT] = ICG_NOGATING_ACTIVE | ICG_NOGATING_MODEM,
  133. [MODEM_CLOCK_DOMAIN_FE] = ICG_NOGATING_ACTIVE | ICG_NOGATING_MODEM,
  134. [MODEM_CLOCK_DOMAIN_IEEE802154] = ICG_NOGATING_ACTIVE | ICG_NOGATING_MODEM,
  135. [MODEM_CLOCK_DOMAIN_LP_APB] = ICG_NOGATING_ACTIVE | ICG_NOGATING_MODEM,
  136. [MODEM_CLOCK_DOMAIN_I2C_MASTER] = ICG_NOGATING_ACTIVE | ICG_NOGATING_MODEM,
  137. [MODEM_CLOCK_DOMAIN_COEX] = ICG_NOGATING_ACTIVE | ICG_NOGATING_MODEM,
  138. [MODEM_CLOCK_DOMAIN_WIFIPWR] = ICG_NOGATING_ACTIVE | ICG_NOGATING_MODEM,
  139. };
  140. for (modem_clock_domain_t domain = MODEM_CLOCK_DOMAIN_MODEM_APB; domain < MODEM_CLOCK_DOMAIN_MAX; domain++) {
  141. modem_clock_hal_set_clock_domain_icg_bitmap(ctx->hal, domain, code[domain]);
  142. }
  143. }
  144. void modem_clock_domain_pmu_state_icg_map_init(void)
  145. {
  146. modem_clock_domain_power_state_icg_map_init(MODEM_CLOCK_instance());
  147. }
  148. static void IRAM_ATTR modem_clock_device_enable(modem_clock_context_t *ctx, uint32_t dev_map)
  149. {
  150. int16_t refs = 0;
  151. portENTER_CRITICAL_SAFE(&ctx->lock);
  152. for (int i = 0; dev_map; dev_map >>= 1, i++) {
  153. if (dev_map & BIT(0)) {
  154. refs = ctx->dev[i].refs++;
  155. if (refs == 0) {
  156. (*ctx->dev[i].configure)(ctx, true);
  157. }
  158. }
  159. }
  160. portEXIT_CRITICAL_SAFE(&ctx->lock);
  161. assert(refs >= 0);
  162. }
  163. static void IRAM_ATTR modem_clock_device_disable(modem_clock_context_t *ctx, uint32_t dev_map)
  164. {
  165. int16_t refs = 0;
  166. portENTER_CRITICAL_SAFE(&ctx->lock);
  167. for (int i = 0; dev_map; dev_map >>= 1, i++) {
  168. if (dev_map & BIT(0)) {
  169. refs = --ctx->dev[i].refs;
  170. if (refs == 0) {
  171. (*ctx->dev[i].configure)(ctx, false);
  172. }
  173. }
  174. }
  175. portEXIT_CRITICAL_SAFE(&ctx->lock);
  176. assert(refs >= 0);
  177. }
  178. #define WIFI_CLOCK_DEPS (MODEM_CLOCK_WIFI_MAC | MODEM_CLOCK_FE | MODEM_CLOCK_WIFI_BB | MODEM_CLOCK_COEXIST)
  179. #define BLE_CLOCK_DEPS (MODEM_CLOCK_BLE_MAC | MODEM_CLOCK_FE | MODEM_CLOCK_BLE_BB | MODEM_CLOCK_ETM | MODEM_CLOCK_COEXIST)
  180. #define IEEE802154_CLOCK_DEPS (MODEM_CLOCK_802154_MAC | MODEM_CLOCK_FE | MODEM_CLOCK_BLE_BB | MODEM_CLOCK_ETM | MODEM_CLOCK_COEXIST)
  181. #define COEXIST_CLOCK_DEPS (MODEM_CLOCK_COEXIST)
  182. #define PHY_CLOCK_DEPS (MODEM_CLOCK_I2C_MASTER)
  183. void IRAM_ATTR modem_clock_module_enable(periph_module_t module)
  184. {
  185. assert(IS_MODEM_MODULE(module));
  186. const int deps = (module == PERIPH_WIFI_MODULE) ? WIFI_CLOCK_DEPS \
  187. : (module == PERIPH_BT_MODULE) ? BLE_CLOCK_DEPS \
  188. : (module == PERIPH_IEEE802154_MODULE) ? IEEE802154_CLOCK_DEPS \
  189. : (module == PERIPH_COEX_MODULE) ? COEXIST_CLOCK_DEPS \
  190. : (module == PERIPH_PHY_MODULE) ? PHY_CLOCK_DEPS \
  191. : 0;
  192. modem_clock_device_enable(MODEM_CLOCK_instance(), deps);
  193. }
  194. void IRAM_ATTR modem_clock_module_disable(periph_module_t module)
  195. {
  196. assert(IS_MODEM_MODULE(module));
  197. const int deps = (module == PERIPH_WIFI_MODULE) ? WIFI_CLOCK_DEPS \
  198. : (module == PERIPH_BT_MODULE) ? BLE_CLOCK_DEPS \
  199. : (module == PERIPH_IEEE802154_MODULE) ? IEEE802154_CLOCK_DEPS \
  200. : (module == PERIPH_COEX_MODULE) ? COEXIST_CLOCK_DEPS \
  201. : (module == PERIPH_PHY_MODULE) ? PHY_CLOCK_DEPS \
  202. : 0;
  203. modem_clock_device_disable(MODEM_CLOCK_instance(), deps);
  204. }
  205. void modem_clock_select_lp_clock_source(periph_module_t module, modem_clock_lpclk_src_t src, uint32_t divider)
  206. {
  207. assert(IS_MODEM_MODULE(module));
  208. portENTER_CRITICAL_SAFE(&MODEM_CLOCK_instance()->lock);
  209. switch (module)
  210. {
  211. case PERIPH_WIFI_MODULE:
  212. modem_clock_hal_deselect_all_wifi_lpclk_source(MODEM_CLOCK_instance()->hal);
  213. modem_clock_hal_select_wifi_lpclk_source(MODEM_CLOCK_instance()->hal, src);
  214. modem_lpcon_ll_set_wifi_lpclk_divisor_value(MODEM_CLOCK_instance()->hal->lpcon_dev, divider);
  215. modem_lpcon_ll_enable_wifipwr_clock(MODEM_CLOCK_instance()->hal->lpcon_dev, true);
  216. break;
  217. case PERIPH_BT_MODULE:
  218. modem_clock_hal_deselect_all_lp_timer_lpclk_source(MODEM_CLOCK_instance()->hal);
  219. modem_clock_hal_select_lp_timer_lpclk_source(MODEM_CLOCK_instance()->hal, src);
  220. modem_lpcon_ll_set_lp_timer_divisor_value(MODEM_CLOCK_instance()->hal->lpcon_dev, divider);
  221. modem_lpcon_ll_enable_lp_timer_clock(MODEM_CLOCK_instance()->hal->lpcon_dev, true);
  222. break;
  223. case PERIPH_COEX_MODULE:
  224. modem_clock_hal_deselect_all_coex_lpclk_source(MODEM_CLOCK_instance()->hal);
  225. modem_clock_hal_select_coex_lpclk_source(MODEM_CLOCK_instance()->hal, src);
  226. modem_lpcon_ll_set_coex_lpclk_divisor_value(MODEM_CLOCK_instance()->hal->lpcon_dev, divider);
  227. // modem_lpcon_ll_enable_coex_clock(MODEM_CLOCK_instance()->hal->lpcon_dev, true); // TODO: IDF-5727
  228. break;
  229. default:
  230. break;
  231. }
  232. modem_clock_lpclk_src_t last_src = MODEM_CLOCK_instance()->lpclk_src[module - PERIPH_MODEM_MODULE_MIN];
  233. MODEM_CLOCK_instance()->lpclk_src[module - PERIPH_MODEM_MODULE_MIN] = src;
  234. portEXIT_CRITICAL_SAFE(&MODEM_CLOCK_instance()->lock);
  235. /* The power domain of the low-power clock source required by the modem
  236. * module remains powered on during sleep */
  237. esp_sleep_pd_domain_t pd_domain = (esp_sleep_pd_domain_t) ( \
  238. (last_src == MODEM_CLOCK_LPCLK_SRC_RC_FAST) ? ESP_PD_DOMAIN_RC_FAST \
  239. : (last_src == MODEM_CLOCK_LPCLK_SRC_MAIN_XTAL) ? ESP_PD_DOMAIN_XTAL \
  240. : (last_src == MODEM_CLOCK_LPCLK_SRC_RC32K) ? ESP_PD_DOMAIN_RC32K \
  241. : (last_src == MODEM_CLOCK_LPCLK_SRC_XTAL32K) ? ESP_PD_DOMAIN_XTAL32K \
  242. : ESP_PD_DOMAIN_MAX);
  243. esp_sleep_pd_domain_t pu_domain = (esp_sleep_pd_domain_t) ( \
  244. (src == MODEM_CLOCK_LPCLK_SRC_RC_FAST) ? ESP_PD_DOMAIN_RC_FAST \
  245. : (src == MODEM_CLOCK_LPCLK_SRC_MAIN_XTAL) ? ESP_PD_DOMAIN_XTAL \
  246. : (src == MODEM_CLOCK_LPCLK_SRC_RC32K) ? ESP_PD_DOMAIN_RC32K \
  247. : (src == MODEM_CLOCK_LPCLK_SRC_XTAL32K) ? ESP_PD_DOMAIN_XTAL32K \
  248. : ESP_PD_DOMAIN_MAX);
  249. esp_sleep_pd_config(pd_domain, ESP_PD_OPTION_OFF);
  250. esp_sleep_pd_config(pu_domain, ESP_PD_OPTION_ON);
  251. }
  252. void modem_clock_deselect_lp_clock_source(periph_module_t module)
  253. {
  254. assert(IS_MODEM_MODULE(module));
  255. portENTER_CRITICAL_SAFE(&MODEM_CLOCK_instance()->lock);
  256. switch (module)
  257. {
  258. case PERIPH_WIFI_MODULE:
  259. modem_clock_hal_deselect_all_wifi_lpclk_source(MODEM_CLOCK_instance()->hal);
  260. modem_lpcon_ll_enable_wifipwr_clock(MODEM_CLOCK_instance()->hal->lpcon_dev, false);
  261. break;
  262. case PERIPH_BT_MODULE:
  263. modem_clock_hal_deselect_all_lp_timer_lpclk_source(MODEM_CLOCK_instance()->hal);
  264. modem_lpcon_ll_enable_lp_timer_clock(MODEM_CLOCK_instance()->hal->lpcon_dev, false);
  265. break;
  266. case PERIPH_COEX_MODULE:
  267. modem_clock_hal_deselect_all_coex_lpclk_source(MODEM_CLOCK_instance()->hal);
  268. // modem_lpcon_ll_enable_coex_clock(MODEM_CLOCK_instance()->hal->lpcon_dev, false); // TODO: IDF-5727
  269. break;
  270. default:
  271. break;
  272. }
  273. modem_clock_lpclk_src_t last_src = MODEM_CLOCK_instance()->lpclk_src[module - PERIPH_MODEM_MODULE_MIN];
  274. MODEM_CLOCK_instance()->lpclk_src[module - PERIPH_MODEM_MODULE_MIN] = MODEM_CLOCK_LPCLK_SRC_INVALID;
  275. portEXIT_CRITICAL_SAFE(&MODEM_CLOCK_instance()->lock);
  276. esp_sleep_pd_domain_t pd_domain = (esp_sleep_pd_domain_t) ( \
  277. (last_src == MODEM_CLOCK_LPCLK_SRC_RC_FAST) ? ESP_PD_DOMAIN_RC_FAST \
  278. : (last_src == MODEM_CLOCK_LPCLK_SRC_MAIN_XTAL) ? ESP_PD_DOMAIN_XTAL \
  279. : (last_src == MODEM_CLOCK_LPCLK_SRC_RC32K) ? ESP_PD_DOMAIN_RC32K \
  280. : (last_src == MODEM_CLOCK_LPCLK_SRC_XTAL32K) ? ESP_PD_DOMAIN_XTAL32K \
  281. : ESP_PD_DOMAIN_MAX);
  282. esp_sleep_pd_config(pd_domain, ESP_PD_OPTION_OFF);
  283. }