From d47b2e46128b971c6e602b10228a689c93f4cd7c Mon Sep 17 00:00:00 2001 From: xiongweichao Date: Fri, 22 Dec 2023 17:25:00 +0800 Subject: [PATCH] feat(ble): Added API to get low power clock source --- components/bt/controller/esp32c3/bt.c | 401 +++++++++--------- .../bt/include/esp32c3/include/esp_bt.h | 11 +- 2 files changed, 222 insertions(+), 190 deletions(-) diff --git a/components/bt/controller/esp32c3/bt.c b/components/bt/controller/esp32c3/bt.c index 7ac77f3ea1..e93a1a886d 100644 --- a/components/bt/controller/esp32c3/bt.c +++ b/components/bt/controller/esp32c3/bt.c @@ -73,12 +73,12 @@ enum { typedef union { struct { uint32_t enable : 1; // whether low power mode is required - uint32_t lpclk_sel : 2; // low power clock source + uint32_t lpclk_sel : 3; // low power clock source uint32_t mac_bb_pd : 1; // whether hardware(MAC, BB) force-power-down is required during sleep uint32_t wakeup_timer_required : 1; // whether system timer is needed uint32_t no_light_sleep : 1; // do not allow system to enter light sleep after bluetooth is enabled uint32_t main_xtal_pu : 1; // power up main XTAL - uint32_t reserved : 25; // reserved + uint32_t reserved : 24; // reserved }; uint32_t val; } btdm_lpcntl_t; @@ -401,7 +401,7 @@ static DRAM_ATTR uint8_t btdm_lpcycle_us_frac = 0; // semaphore used for blocking VHCI API to wait for controller to wake up static DRAM_ATTR QueueHandle_t s_wakeup_req_sem = NULL; // wakeup timer -static DRAM_ATTR esp_timer_handle_t s_btdm_slp_tmr; +static DRAM_ATTR esp_timer_handle_t s_btdm_slp_tmr = NULL; #ifdef CONFIG_PM_ENABLE static DRAM_ATTR esp_pm_lock_handle_t s_pm_lock; @@ -1154,6 +1154,147 @@ static void IRAM_ATTR btdm_mac_bb_power_up_cb(void) } #endif +// init low-power control resources +static esp_err_t btdm_low_power_mode_init(esp_bt_controller_config_t *cfg) +{ + esp_err_t err = ESP_OK; + + do { + // set default values for global states or resources + s_lp_stat.val = 0; + s_lp_cntl.val = 0; + s_lp_cntl.main_xtal_pu = 0; + s_wakeup_req_sem = NULL; + s_btdm_slp_tmr = NULL; + + // configure and initialize resources + s_lp_cntl.enable = (cfg->sleep_mode == ESP_BT_SLEEP_MODE_1) ? 1 : 0; + s_lp_cntl.lpclk_sel = (cfg->sleep_mode == ESP_BT_SLEEP_MODE_1) ? cfg->sleep_clock : ESP_BT_SLEEP_CLOCK_MAIN_XTAL; + s_lp_cntl.no_light_sleep = 0; + + if (s_lp_cntl.enable) { +#if CONFIG_MAC_BB_PD + if (!btdm_deep_sleep_mem_init()) { + err = ESP_ERR_NO_MEM; + break; + } + s_lp_cntl.mac_bb_pd = 1; +#endif +#ifdef CONFIG_PM_ENABLE + s_lp_cntl.wakeup_timer_required = 1; +#endif + // async wakeup semaphore for VHCI + s_wakeup_req_sem = semphr_create_wrapper(1, 0); + if (s_wakeup_req_sem == NULL) { + err = ESP_ERR_NO_MEM; + break; + } + btdm_vnd_offload_task_register(BTDM_VND_OL_SIG_WAKEUP_TMR, btdm_sleep_exit_phase0); + + if (s_lp_cntl.wakeup_timer_required) { + esp_timer_create_args_t create_args = { + .callback = btdm_slp_tmr_callback, + .arg = NULL, + .name = "btSlp", + }; + if ((err = esp_timer_create(&create_args, &s_btdm_slp_tmr)) != ESP_OK) { + break; + } + } + + // set default bluetooth sleep clock cycle and its fractional bits + btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; + btdm_lpcycle_us = 2 << (btdm_lpcycle_us_frac); + + if (s_lp_cntl.lpclk_sel == ESP_BT_SLEEP_CLOCK_EXT_32K_XTAL) { // External 32 kHz XTAL + // check whether or not EXT_CRYS is working + if (rtc_clk_slow_freq_get() != RTC_SLOW_FREQ_32K_XTAL) { + ESP_LOGW(BT_LOG_TAG, "32.768kHz XTAL not detected, fall back to main XTAL as Bluetooth sleep clock"); + s_lp_cntl.lpclk_sel = ESP_BT_SLEEP_CLOCK_MAIN_XTAL; +#if !CONFIG_BT_CTRL_MAIN_XTAL_PU_DURING_LIGHT_SLEEP + s_lp_cntl.no_light_sleep = 1; +#endif + } + } else if (s_lp_cntl.lpclk_sel == ESP_BT_SLEEP_CLOCK_RTC_SLOW) { // Internal 136kHz RC oscillator + if (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) { + ESP_LOGW(BT_LOG_TAG, "Internal 136kHz RC oscillator. The accuracy of this clock is a lot larger than 500ppm which is " + "required in Bluetooth communication, so don't select this option in scenarios such as BLE connection state."); + } else { + ESP_LOGW(BT_LOG_TAG, "Internal 136kHz RC oscillator not detected."); + assert(0); + } + } else if (s_lp_cntl.lpclk_sel == ESP_BT_SLEEP_CLOCK_MAIN_XTAL) { + ESP_LOGI(BT_LOG_TAG, "Bluetooth will use main XTAL as Bluetooth sleep clock."); +#if !CONFIG_BT_CTRL_MAIN_XTAL_PU_DURING_LIGHT_SLEEP + s_lp_cntl.no_light_sleep = 1; +#endif + } + } else { + s_lp_cntl.no_light_sleep = 1; + } + + bool select_src_ret __attribute__((unused)); + bool set_div_ret __attribute__((unused)); + if (s_lp_cntl.lpclk_sel == ESP_BT_SLEEP_CLOCK_MAIN_XTAL) { +#ifdef CONFIG_BT_CTRL_MAIN_XTAL_PU_DURING_LIGHT_SLEEP + ESP_ERROR_CHECK(esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_ON)); + s_lp_cntl.main_xtal_pu = 1; +#endif + select_src_ret = btdm_lpclk_select_src(BTDM_LPCLK_SEL_XTAL); + set_div_ret = btdm_lpclk_set_div(rtc_clk_xtal_freq_get()); + assert(select_src_ret && set_div_ret); + btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; + btdm_lpcycle_us = 1 << (btdm_lpcycle_us_frac); + } else if (s_lp_cntl.lpclk_sel == ESP_BT_SLEEP_CLOCK_EXT_32K_XTAL) { + select_src_ret = btdm_lpclk_select_src(BTDM_LPCLK_SEL_XTAL32K); + set_div_ret = btdm_lpclk_set_div(0); + assert(select_src_ret && set_div_ret); + btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; + btdm_lpcycle_us = (RTC_CLK_CAL_FRACT > 15) ? (1000000 << (RTC_CLK_CAL_FRACT - 15)) : + (1000000 >> (15 - RTC_CLK_CAL_FRACT)); + assert(btdm_lpcycle_us != 0); + } else if (s_lp_cntl.lpclk_sel == ESP_BT_SLEEP_CLOCK_RTC_SLOW) { + select_src_ret = btdm_lpclk_select_src(BTDM_LPCLK_SEL_RTC_SLOW); + set_div_ret = btdm_lpclk_set_div(0); + assert(select_src_ret && set_div_ret); + btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; + btdm_lpcycle_us = esp_clk_slowclk_cal_get(); + } else { + err = ESP_ERR_INVALID_ARG; + break; + } +#if CONFIG_SW_COEXIST_ENABLE + coex_update_lpclk_interval(); +#endif + +#ifdef CONFIG_PM_ENABLE + if (s_lp_cntl.no_light_sleep) { + if ((err = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "btLS", &s_light_sleep_pm_lock)) != ESP_OK) { + break; + } + ESP_LOGW(BT_LOG_TAG, "light sleep mode will not be able to apply when bluetooth is enabled."); + } + if ((err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "bt", &s_pm_lock)) != ESP_OK) { + break; + } else { + s_lp_stat.pm_lock_released = 1; + } +#endif + } while (0); + + return err; +} + +esp_bt_sleep_clock_t esp_bt_get_lpclk_src(void) +{ + if (btdm_controller_status != ESP_BT_CONTROLLER_STATUS_INITED && + btdm_controller_status != ESP_BT_CONTROLLER_STATUS_ENABLED) { + return ESP_BT_SLEEP_CLOCK_NONE; + } + + return s_lp_cntl.lpclk_sel; +} + esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) { esp_err_t err = ESP_FAIL; @@ -1189,6 +1330,10 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) ESP_LOGE(BT_LOG_TAG, "SLEEP_MODE_1 enabled but sleep clock not configured"); return ESP_ERR_INVALID_ARG; } + if (cfg->sleep_clock > ESP_BT_SLEEP_CLOCK_RTC_SLOW) { + ESP_LOGE(BT_LOG_TAG, "SLEEP_MODE_1 is enabled but this sleep clock is not supported"); + return ESP_ERR_INVALID_ARG; + } } // overwrite some parameters @@ -1214,133 +1359,10 @@ esp_err_t esp_bt_controller_init(esp_bt_controller_config_t *cfg) ESP_LOGI(BT_LOG_TAG, "BT controller compile version [%s]", btdm_controller_get_compile_version()); - // init low-power control resources - do { - // set default values for global states or resources - s_lp_stat.val = 0; - s_lp_cntl.val = 0; - s_lp_cntl.main_xtal_pu = 0; - s_wakeup_req_sem = NULL; - s_btdm_slp_tmr = NULL; - - // configure and initialize resources - s_lp_cntl.enable = (cfg->sleep_mode == ESP_BT_SLEEP_MODE_1) ? 1 : 0; - s_lp_cntl.no_light_sleep = 0; - - if (s_lp_cntl.enable) { -#if CONFIG_MAC_BB_PD - if (!btdm_deep_sleep_mem_init()) { - err = ESP_ERR_NO_MEM; - goto error; - } - s_lp_cntl.mac_bb_pd = 1; -#endif -#ifdef CONFIG_PM_ENABLE - s_lp_cntl.wakeup_timer_required = 1; -#endif - // async wakeup semaphore for VHCI - s_wakeup_req_sem = semphr_create_wrapper(1, 0); - if (s_wakeup_req_sem == NULL) { - err = ESP_ERR_NO_MEM; - goto error; - } - btdm_vnd_offload_task_register(BTDM_VND_OL_SIG_WAKEUP_TMR, btdm_sleep_exit_phase0); - } - - if (s_lp_cntl.wakeup_timer_required) { - esp_timer_create_args_t create_args = { - .callback = btdm_slp_tmr_callback, - .arg = NULL, - .name = "btSlp", - }; - if ((err = esp_timer_create(&create_args, &s_btdm_slp_tmr)) != ESP_OK) { - goto error; - } - } - - // set default bluetooth sleep clock cycle and its fractional bits - btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; - btdm_lpcycle_us = 2 << (btdm_lpcycle_us_frac); - - // set default bluetooth sleep clock source - s_lp_cntl.lpclk_sel = BTDM_LPCLK_SEL_XTAL; // set default value -#if CONFIG_BT_CTRL_LPCLK_SEL_EXT_32K_XTAL - // check whether or not EXT_CRYS is working - if (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_32K_XTAL) { - s_lp_cntl.lpclk_sel = BTDM_LPCLK_SEL_XTAL32K; // External 32 kHz XTAL - } else { - ESP_LOGW(BT_LOG_TAG, "32.768kHz XTAL not detected, fall back to main XTAL as Bluetooth sleep clock"); -#if !CONFIG_BT_CTRL_MAIN_XTAL_PU_DURING_LIGHT_SLEEP - s_lp_cntl.no_light_sleep = 1; -#endif - } -#elif (CONFIG_BT_CTRL_LPCLK_SEL_MAIN_XTAL) - ESP_LOGI(BT_LOG_TAG, "Bluetooth will use main XTAL as Bluetooth sleep clock."); -#if !CONFIG_BT_CTRL_MAIN_XTAL_PU_DURING_LIGHT_SLEEP - s_lp_cntl.no_light_sleep = 1; -#endif -#elif (CONFIG_BT_CTRL_LPCLK_SEL_RTC_SLOW) - // check whether or not internal 150 kHz RC oscillator is working - if (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) { - s_lp_cntl.lpclk_sel = BTDM_LPCLK_SEL_RTC_SLOW; // Internal 150 kHz RC oscillator - ESP_LOGW(BT_LOG_TAG, "Internal 150kHz RC osciallator. The accuracy of this clock is a lot larger than 500ppm which is " - "required in Bluetooth communication, so don't select this option in scenarios such as BLE connection state."); - } else { - ESP_LOGW(BT_LOG_TAG, "Internal 150kHz RC oscillator not detected."); - assert(0); - } -#endif - - bool select_src_ret __attribute__((unused)); - bool set_div_ret __attribute__((unused)); - if (s_lp_cntl.lpclk_sel == BTDM_LPCLK_SEL_XTAL) { -#ifdef CONFIG_BT_CTRL_MAIN_XTAL_PU_DURING_LIGHT_SLEEP - ESP_ERROR_CHECK(esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_ON)); - s_lp_cntl.main_xtal_pu = 1; -#endif - select_src_ret = btdm_lpclk_select_src(BTDM_LPCLK_SEL_XTAL); - set_div_ret = btdm_lpclk_set_div(rtc_clk_xtal_freq_get()); - assert(select_src_ret && set_div_ret); - btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; - btdm_lpcycle_us = 1 << (btdm_lpcycle_us_frac); - } else if (s_lp_cntl.lpclk_sel == BTDM_LPCLK_SEL_XTAL32K) { - select_src_ret = btdm_lpclk_select_src(BTDM_LPCLK_SEL_XTAL32K); - set_div_ret = btdm_lpclk_set_div(0); - assert(select_src_ret && set_div_ret); - btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; - btdm_lpcycle_us = (RTC_CLK_CAL_FRACT > 15) ? (1000000 << (RTC_CLK_CAL_FRACT - 15)) : - (1000000 >> (15 - RTC_CLK_CAL_FRACT)); - assert(btdm_lpcycle_us != 0); - } else if (s_lp_cntl.lpclk_sel == BTDM_LPCLK_SEL_RTC_SLOW) { - select_src_ret = btdm_lpclk_select_src(BTDM_LPCLK_SEL_RTC_SLOW); - set_div_ret = btdm_lpclk_set_div(0); - assert(select_src_ret && set_div_ret); - btdm_lpcycle_us_frac = RTC_CLK_CAL_FRACT; - btdm_lpcycle_us = esp_clk_slowclk_cal_get(); - } else { - err = ESP_ERR_INVALID_ARG; - goto error; - } -#if CONFIG_SW_COEXIST_ENABLE - coex_update_lpclk_interval(); -#endif - -#ifdef CONFIG_PM_ENABLE - if (s_lp_cntl.no_light_sleep) { - if ((err = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "btLS", &s_light_sleep_pm_lock)) != ESP_OK) { - err = ESP_ERR_NO_MEM; - goto error; - } - ESP_LOGW(BT_LOG_TAG, "light sleep mode will not be able to apply when bluetooth is enabled."); - } - if ((err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "bt", &s_pm_lock)) != ESP_OK) { - err = ESP_ERR_NO_MEM; - goto error; - } else { - s_lp_stat.pm_lock_released = 1; - } -#endif - } while (0); + if ((err = btdm_low_power_mode_init(cfg)) != ESP_OK) { + ESP_LOGE(BT_LOG_TAG, "Low power module initialization failed"); + goto error; + } #if CONFIG_SW_COEXIST_ENABLE coex_init(); @@ -1378,69 +1400,70 @@ esp_err_t esp_bt_controller_deinit(void) return ESP_OK; } +// deinit low power control resources +static void btdm_low_power_mode_deinit(void) +{ +#if CONFIG_MAC_BB_PD + if (s_lp_cntl.mac_bb_pd) { + btdm_deep_sleep_mem_deinit(); + s_lp_cntl.mac_bb_pd = 0; + } +#endif + +#ifdef CONFIG_PM_ENABLE + if (s_lp_cntl.no_light_sleep) { + if (s_light_sleep_pm_lock != NULL) { + esp_pm_lock_delete(s_light_sleep_pm_lock); + s_light_sleep_pm_lock = NULL; + } + } + + if (s_pm_lock != NULL) { + esp_pm_lock_delete(s_pm_lock); + s_pm_lock = NULL; + s_lp_stat.pm_lock_released = 0; + } +#endif + + if (s_lp_cntl.wakeup_timer_required && s_btdm_slp_tmr != NULL) { + if (s_lp_stat.wakeup_timer_started) { + esp_timer_stop(s_btdm_slp_tmr); + } + s_lp_stat.wakeup_timer_started = 0; + esp_timer_delete(s_btdm_slp_tmr); + s_btdm_slp_tmr = NULL; + } + + if (s_lp_cntl.enable) { + btdm_vnd_offload_task_deregister(BTDM_VND_OL_SIG_WAKEUP_TMR); + if (s_wakeup_req_sem != NULL) { + semphr_delete_wrapper(s_wakeup_req_sem); + s_wakeup_req_sem = NULL; + } + } + + if (s_lp_cntl.lpclk_sel == ESP_BT_SLEEP_CLOCK_MAIN_XTAL) { +#ifdef CONFIG_BT_CTRL_MAIN_XTAL_PU_DURING_LIGHT_SLEEP + if (s_lp_cntl.main_xtal_pu) { + ESP_ERROR_CHECK(esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_OFF)); + s_lp_cntl.main_xtal_pu = 0; + } +#endif + btdm_lpclk_select_src(BTDM_LPCLK_SEL_RTC_SLOW); + btdm_lpclk_set_div(0); +#if CONFIG_SW_COEXIST_ENABLE + coex_update_lpclk_interval(); +#endif + } + + btdm_lpcycle_us = 0; +} + static void bt_controller_deinit_internal(void) { periph_module_disable(PERIPH_BT_MODULE); - // deinit low power control resources - do { - -#if CONFIG_MAC_BB_PD - if (s_lp_cntl.mac_bb_pd) { - btdm_deep_sleep_mem_deinit(); - s_lp_cntl.mac_bb_pd = 0; - } -#endif - -#ifdef CONFIG_PM_ENABLE - if (s_lp_cntl.no_light_sleep) { - if (s_light_sleep_pm_lock != NULL) { - esp_pm_lock_delete(s_light_sleep_pm_lock); - s_light_sleep_pm_lock = NULL; - } - } - - if (s_pm_lock != NULL) { - esp_pm_lock_delete(s_pm_lock); - s_pm_lock = NULL; - s_lp_stat.pm_lock_released = 0; - } - -#endif - - if (s_lp_cntl.wakeup_timer_required) { - if (s_lp_stat.wakeup_timer_started) { - esp_timer_stop(s_btdm_slp_tmr); - } - s_lp_stat.wakeup_timer_started = 0; - esp_timer_delete(s_btdm_slp_tmr); - s_btdm_slp_tmr = NULL; - } - - if (s_lp_cntl.enable) { - btdm_vnd_offload_task_deregister(BTDM_VND_OL_SIG_WAKEUP_TMR); - if (s_wakeup_req_sem != NULL) { - semphr_delete_wrapper(s_wakeup_req_sem); - s_wakeup_req_sem = NULL; - } - } - - if (s_lp_cntl.lpclk_sel == BTDM_LPCLK_SEL_XTAL) { -#ifdef CONFIG_BT_CTRL_MAIN_XTAL_PU_DURING_LIGHT_SLEEP - if (s_lp_cntl.main_xtal_pu) { - ESP_ERROR_CHECK(esp_sleep_pd_config(ESP_PD_DOMAIN_XTAL, ESP_PD_OPTION_OFF)); - s_lp_cntl.main_xtal_pu = 0; - } -#endif - btdm_lpclk_select_src(BTDM_LPCLK_SEL_RTC_SLOW); - btdm_lpclk_set_div(0); -#if CONFIG_SW_COEXIST_ENABLE - coex_update_lpclk_interval(); -#endif - } - - btdm_lpcycle_us = 0; - } while (0); + btdm_low_power_mode_deinit(); esp_bt_power_domain_off(); #if CONFIG_MAC_BB_PD diff --git a/components/bt/include/esp32c3/include/esp_bt.h b/components/bt/include/esp32c3/include/esp_bt.h index 64760fc62c..3114aee58a 100644 --- a/components/bt/include/esp32c3/include/esp_bt.h +++ b/components/bt/include/esp32c3/include/esp_bt.h @@ -289,7 +289,7 @@ typedef struct { uint8_t sleep_clock; /*!< controller sleep clock */ uint8_t ble_st_acl_tx_buf_nb; /*!< controller static ACL TX BUFFER number */ uint8_t ble_hw_cca_check; /*!< controller hardware triggered CCA check */ - uint16_t ble_adv_dup_filt_max; /*!< maxinum number of duplicate scan filter */ + uint16_t ble_adv_dup_filt_max; /*!< maximum number of duplicate scan filter */ bool coex_param_en; /*!< deprecated */ uint8_t ce_len_type; /*!< connection event length computation method */ bool coex_use_hooks; /*!< deprecated */ @@ -600,6 +600,15 @@ void esp_wifi_bt_power_domain_on(void); */ void esp_wifi_bt_power_domain_off(void); +/** + * @brief Get the Bluetooth module sleep clock source. + * + * Note that this function shall not be invoked before esp_bt_controller_init() + * + * @return clock source used in Bluetooth low power mode + */ +esp_bt_sleep_clock_t esp_bt_get_lpclk_src(void); + #ifdef __cplusplus } #endif