Skip to content

Commit

Permalink
usb_serial_jtag: Improve the code for the issue of usb cdc device una…
Browse files Browse the repository at this point in the history
…ble to work during sleep

1. Remove RTC_CLOCK_BBPLL_POWER_ON_WITH_USB Kconfig option
   During sleep, BBPLL clock always gets disabled
   esp_restart does not disable BBPLL clock, so that first stage bootloader log can be printed
2. Add a new Kconfig option PM_NO_AUTO_LS_ON_USJ_CONNECTED
   When this option is selected, IDF will constantly monitor USB CDC port connection status.
   As long as it gets connected to a HOST, automatic light-sleep will not happen.

Closes #8507
  • Loading branch information
songruo committed Feb 27, 2023
1 parent 30a46d0 commit 1a66459
Show file tree
Hide file tree
Showing 39 changed files with 493 additions and 226 deletions.
10 changes: 9 additions & 1 deletion components/driver/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,8 @@ endif()

# USB Serial JTAG related source files
if(CONFIG_SOC_USB_SERIAL_JTAG_SUPPORTED)
list(APPEND srcs "usb_serial_jtag/usb_serial_jtag.c")
list(APPEND srcs "usb_serial_jtag/usb_serial_jtag.c"
"usb_serial_jtag/usb_serial_jtag_connection_monitor.c")
endif()

# Other source files
Expand All @@ -204,3 +205,10 @@ else()
REQUIRES esp_pm esp_ringbuf freertos soc hal esp_hw_support
LDFRAGMENTS linker.lf)
endif()

# If system needs to monitor USJ connection status, then usb_serial_jtag_connection_monitor object file has to be linked
# to the binary, to allow tick hook to be registered
if(CONFIG_USJ_NO_AUTO_LS_ON_CONNECTION OR
(CONFIG_SOC_USB_SERIAL_JTAG_SUPPORT_LIGHT_SLEEP AND CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED))
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u usb_serial_jtag_connection_monitor_include")
endif()
17 changes: 17 additions & 0 deletions components/driver/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -461,4 +461,21 @@ menu "Driver Configurations"

endmenu # DAC Configuration

menu "USB Serial/JTAG Configuration"
depends on SOC_USB_SERIAL_JTAG_SUPPORTED
config USJ_NO_AUTO_LS_ON_CONNECTION
bool "Don't enter the automatic light sleep when USB Serial/JTAG port is connected"
depends on PM_ENABLE && ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED && !SOC_USB_SERIAL_JTAG_SUPPORT_LIGHT_SLEEP
default n
help
If enabled, the chip will constantly monitor the connection status of the USB Serial/JTAG port. As long
as the USB Serial/JTAG is connected, a ESP_PM_NO_LIGHT_SLEEP power management lock will be acquired to
prevent the system from entering light sleep.
This option can be useful if serial monitoring is needed via USB Serial/JTAG while power management is
enabled, as the USB Serial/JTAG cannot work under light sleep and after waking up from light sleep.
Note. This option can only control the automatic Light-Sleep behavior. If esp_light_sleep_start() is
called manually from the program, enabling this option will not prevent light sleep entry even if the
USB Serial/JTAG is in use.
endmenu # USB Serial/JTAG Configuration

endmenu # Driver configurations
14 changes: 14 additions & 0 deletions components/driver/usb_serial_jtag/include/driver/usb_serial_jtag.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,20 @@ int usb_serial_jtag_write_bytes(const void* src, size_t size, TickType_t ticks_t
*/
esp_err_t usb_serial_jtag_driver_uninstall(void);

/**
* @brief Check if the USB Serial/JTAG port is connected to the host
*
* This function checks whether the USB Serial/JTAG (USJ) port is currently connected. USJ is considered "connected"
* so long as it is receiving SOF packets from the host, even if there is no serial commuincation occuring (i.e., the
* USJ is connected to the PC, but the serial port is not opened). Having the USB port connected to a power bank will
* never be considered as connected (due to the lack of SOF packets).
*
* @note If your application needs this function, it will add some extra overhead time to every freertos tick.
*
* @return True if USJ is connected, false otherwise
*/
bool usb_serial_jtag_is_connected(void);

#ifdef __cplusplus
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <stdbool.h>
#include "driver/usb_serial_jtag.h"
#include "hal/usb_serial_jtag_ll.h"
#include "esp_private/startup_internal.h"
#include "esp_freertos_hooks.h"
#include "esp_pm.h"
#include "esp_private/rtc_clk.h"
#include "esp_check.h"
#include "sdkconfig.h"

static volatile bool s_usb_serial_jtag_conn_status;
#if CONFIG_USJ_NO_AUTO_LS_ON_CONNECTION
static esp_pm_lock_handle_t s_usb_serial_jtag_pm_lock;
#endif

// FreeRTOS tick interrupt may get delayed in handling, which could lead to no SOF coming in within the period from
// a delayed tick to the following tick. Therefore, when FREERTOS_HZ is comparable to SOF packet frequency, a NO_SOF
// tolerance needs to be given to avoid a false disconnection detection.
#define USJ_DISCONNECT_CONFIRM_PERIOD_MS 3
#define ALLOWED_NO_SOF_TICKS pdMS_TO_TICKS(USJ_DISCONNECT_CONFIRM_PERIOD_MS)

static uint32_t remaining_allowed_no_sof_ticks;

static __attribute__((unused)) const char *USB_SERIAL_JTAG_CONN_MONITOR_TAG = "usb_serial_jtag";


bool usb_serial_jtag_is_connected(void)
{
return s_usb_serial_jtag_conn_status;
}

static void IRAM_ATTR usb_serial_jtag_sof_tick_hook(void)
{
// SOF packet is sent by the HOST every 1ms on a full speed bus
// Between two consecutive tick hooks, there will be at least 1ms (selectable tick rate range is 1 - 1000Hz)
// Therefore, SOF intr bit must have be raised at every tick hook if it is connected to a HOST
// Here, the strategy is: Always assume USB Serial/JTAG is connected until we are sure it is not connected
// Consider it is disconnected only if SOF intr bit is not raised within (ALLOWED_NO_SOF_TICKS + 1) tick periods
bool sof_received = (usb_serial_jtag_ll_get_intraw_mask() & USB_SERIAL_JTAG_INTR_SOF);
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SOF);
if (s_usb_serial_jtag_conn_status != sof_received) {
if (!sof_received) {
if (remaining_allowed_no_sof_ticks > 0) {
remaining_allowed_no_sof_ticks--;
} else {
// Tolerance credit used up, considered as USJ disconnected
#if CONFIG_USJ_NO_AUTO_LS_ON_CONNECTION
esp_pm_lock_release(s_usb_serial_jtag_pm_lock);
#endif
rtc_clk_bbpll_remove_consumer();
s_usb_serial_jtag_conn_status = false;
}
} else {
// USJ re-connected
#if CONFIG_USJ_NO_AUTO_LS_ON_CONNECTION
esp_pm_lock_acquire(s_usb_serial_jtag_pm_lock);
#endif
rtc_clk_bbpll_add_consumer();
s_usb_serial_jtag_conn_status = true;
remaining_allowed_no_sof_ticks = ALLOWED_NO_SOF_TICKS;
}
}
}

ESP_SYSTEM_INIT_FN(usb_serial_jtag_conn_status_init, BIT(0), 230)
{
#if CONFIG_USJ_NO_AUTO_LS_ON_CONNECTION
ESP_RETURN_ON_ERROR(esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "usb_serial_jtag", &s_usb_serial_jtag_pm_lock),
USB_SERIAL_JTAG_CONN_MONITOR_TAG, "create NO_LIGHT_SLEEP lock failed");
// We always assume it is connected at first, so acquires the lock to avoid auto light sleep
esp_pm_lock_acquire(s_usb_serial_jtag_pm_lock);
#endif
rtc_clk_bbpll_add_consumer();
s_usb_serial_jtag_conn_status = true;
remaining_allowed_no_sof_ticks = ALLOWED_NO_SOF_TICKS;

return esp_register_freertos_tick_hook(usb_serial_jtag_sof_tick_hook);
}

void usb_serial_jtag_connection_monitor_include(void)
{
// Linker hook function, exists to make the linker examine this file
}
25 changes: 0 additions & 25 deletions components/esp_hw_support/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -135,31 +135,6 @@ menu "Hardware Settings"

menu "RTC Clock Config"
orsource "./port/$IDF_TARGET/Kconfig.rtc"

config RTC_CLOCK_BBPLL_POWER_ON_WITH_USB
# This is used for configure the RTC clock.
bool "Keep BBPLL clock always work"
depends on ESP_CONSOLE_USB_SERIAL_JTAG || ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG
default y
help
When software switches the CPU clock source from BBPLL clock to XTAL, usually the BBPLL will be
switched off. This helps to save some power consumption in sleep modes. However this may also happen
during the software reset, resulting in the inactive (disconnected from host) of the USB_SERIAL_JTAG
device during software reset.

When USB_SERIAL_JTAG is being used, whether to turn off the clock source during software reset and in
sleep modes is determined by RTC_CLOCK_BBPLL_POWER_ON_WITH_USB.

- When RTC_CLOCK_BBPLL_POWER_ON_WITH_USB is enabled, the clock will be kept, so that the
USB_SERIAL_JTAG will keep alive during software reset. The side-effect is the increasing of power
consumption during sleep modes, even though USB_SERIAL_JTAG will not work in sleep modes.

- When RTC_CLOCK_BBPLL_POWER_ON_WITH_USB is disabled, the clock will be turned off. USB_SERIAL_JTAG
will be inactive during software reset and in sleep modes. This saves some power consumption in
sleep modes.

When USB_SERIAL_JTAG is not being used, software will always turn off BBPLL regardless of
RTC_CLOCK_BBPLL_POWER_ON_WITH_USB is set or not.
endmenu

menu "Peripheral Control"
Expand Down
49 changes: 49 additions & 0 deletions components/esp_hw_support/include/esp_private/rtc_clk.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief Switch CPU clock source to XTAL, and let cpu frequency equal to main XTAL frequency.
*
* This function does not disable BBPLL. If BBPLL requires to be disabled to save power, please call
* `rtc_clk_cpu_freq_set_xtal` instead. It does one extra check to see whether can disable the BBPLL after switching the
* CPU clock source to XTAL.
*
* Currently, this function should only be called in `esp_restart_noos` and `esp_restart_noos_dig` to switch the CPU
* clock source back to XTAL (by default) before reset.
*/
void rtc_clk_cpu_set_to_default_config(void);

/**
* @brief Notify that the BBPLL has a new in-use consumer
*
* Currently, this function is only used for tracking whether USB Serial/JTAG is using the 48MHz PHY clock
*
* Note: Calling this function only helps to not disable the BBPLL clock in `rtc_clk_cpu_freq_set_config`.
* For light and deep sleep, whether to disable the BBPLL in the interal call to `rtc_clk_cpu_freq_set_xtal`
* varies for targets.
* On ESP32C3/S3, USB CDC device can not function properly during sleep due to the lack of APB clock. Therefore.
* `rtc_clk_cpu_freq_set_xtal` will always disable BBPLL, no matter whether BBPLL has any consumer.
* On ESP32C6/H2, USB CDC device can maintain the minimum connection with the host during sleep, so
* `rtc_clk_cpu_freq_set_xtal` will check for BBPLL consumers, and keep BBPLL if USB Serial/JTAG is in use.
*/
void rtc_clk_bbpll_add_consumer(void);

/**
* @brief Notify that the BBPLL has lost a consumer
*/
void rtc_clk_bbpll_remove_consumer(void);

#ifdef __cplusplus
}
#endif
22 changes: 15 additions & 7 deletions components/esp_hw_support/port/esp32/rtc_clk.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <stddef.h>
#include <stdlib.h>
#include "soc/rtc.h"
#include "esp_private/rtc_clk.h"
#include "soc/rtc_periph.h"
#include "soc/sens_periph.h"
#include "soc/soc_caps.h"
Expand Down Expand Up @@ -347,20 +348,22 @@ static void rtc_clk_bbpll_configure(rtc_xtal_freq_t xtal_freq, int pll_freq)
}

/**
* Switch to XTAL frequency. Does not disable the PLL.
* Switch to use XTAL as the CPU clock source.
* Must satisfy: cpu_freq = XTAL_FREQ / div.
* Does not disable the PLL.
*/
void rtc_clk_cpu_freq_to_xtal(int freq, int div)
void rtc_clk_cpu_freq_to_xtal(int cpu_freq, int div)
{
ets_update_cpu_frequency(freq);
ets_update_cpu_frequency(cpu_freq);
/* set divider from XTAL to APB clock */
clk_ll_cpu_set_divider(div);
/* adjust ref_tick */
clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_XTAL, freq);
clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_XTAL, cpu_freq);
/* switch clock source */
clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_XTAL);
rtc_clk_apb_freq_update(freq * MHZ);
rtc_clk_apb_freq_update(cpu_freq * MHZ);
/* lower the voltage */
int dbias = (freq <= 2) ? DIG_DBIAS_2M : DIG_DBIAS_XTAL;
int dbias = (cpu_freq <= 2) ? DIG_DBIAS_2M : DIG_DBIAS_XTAL;
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, dbias);
}

Expand Down Expand Up @@ -396,12 +399,17 @@ static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz)
}

void rtc_clk_cpu_freq_set_xtal(void)
{
rtc_clk_cpu_set_to_default_config();
rtc_clk_bbpll_disable();
}

void rtc_clk_cpu_set_to_default_config(void)
{
int freq_mhz = (int)rtc_clk_xtal_freq_get();

rtc_clk_cpu_freq_to_xtal(freq_mhz, 1);
rtc_clk_wait_for_slow_cycle();
rtc_clk_bbpll_disable();
}

bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t* out_config)
Expand Down
18 changes: 13 additions & 5 deletions components/esp_hw_support/port/esp32c2/rtc_clk.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "esp32c2/rom/uart.h"
#include "esp32c2/rom/gpio.h"
#include "soc/rtc.h"
#include "esp_private/rtc_clk.h"
#include "hal/gpio_ll.h"
#include "soc/io_mux_reg.h"
#include "soc/soc.h"
Expand Down Expand Up @@ -269,25 +270,32 @@ void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t *config)
}

void rtc_clk_cpu_freq_set_xtal(void)
{
rtc_clk_cpu_set_to_default_config();
rtc_clk_bbpll_disable();
}

void rtc_clk_cpu_set_to_default_config(void)
{
int freq_mhz = (int)rtc_clk_xtal_freq_get();

rtc_clk_cpu_freq_to_xtal(freq_mhz, 1);
rtc_clk_bbpll_disable();
}

/**
* Switch to XTAL frequency. Does not disable the PLL.
* Switch to use XTAL as the CPU clock source.
* Must satisfy: cpu_freq = XTAL_FREQ / div.
* Does not disable the PLL.
*/
void rtc_clk_cpu_freq_to_xtal(int freq, int div)
void rtc_clk_cpu_freq_to_xtal(int cpu_freq, int div)
{
ets_update_cpu_frequency(freq);
ets_update_cpu_frequency(cpu_freq);
/* Set divider from XTAL to APB clock. Need to set divider to 1 (reg. value 0) first. */
clk_ll_cpu_set_divider(1);
clk_ll_cpu_set_divider(div);
/* switch clock source */
clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_XTAL);
rtc_clk_apb_freq_update(freq * MHZ);
rtc_clk_apb_freq_update(cpu_freq * MHZ);
}

static void rtc_clk_cpu_freq_to_8m(void)
Expand Down
Loading

0 comments on commit 1a66459

Please sign in to comment.