Merge branch 'feature/esp32s2_pm' into 'master'

esp32s2: power management (DFS and auto light sleep)

Closes IDF-670, IDF-527, and IDFGH-231

See merge request espressif/esp-idf!7578
This commit is contained in:
Ivan Grokhotkov
2020-02-19 18:26:33 +08:00
18 changed files with 718 additions and 607 deletions

View File

@@ -35,15 +35,15 @@ void bootloader_clock_configure(void)
// and will be done with the bootloader much earlier than UART FIFO is empty.
uart_tx_wait_idle(0);
/* Set CPU to 80MHz. Keep other clocks unmodified. */
int cpu_freq_mhz = 80;
#if CONFIG_IDF_TARGET_ESP32
/* On ESP32 rev 0, switching to 80/160 MHz if clock was previously set to
* 240 MHz may cause the chip to lock up (see section 3.5 of the errata
* document). For rev. 0, switch to 240 instead if it has been enabled
* previously.
*/
#if CONFIG_IDF_TARGET_ESP32
/* Set CPU to 80MHz. Keep other clocks unmodified. */
int cpu_freq_mhz = 80;
uint32_t chip_ver_reg = REG_READ(EFUSE_BLK0_RDATA3_REG);
if ((chip_ver_reg & EFUSE_RD_CHIP_VER_REV1_M) == 0 &&
DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL) == DPORT_CPUPERIOD_SEL_240) {
@@ -54,11 +54,9 @@ void bootloader_clock_configure(void)
rtc_clk_config_t clk_cfg = RTC_CLK_CONFIG_DEFAULT();
#if CONFIG_IDF_TARGET_ESP32
clk_cfg.xtal_freq = CONFIG_ESP32_XTAL_FREQ;
clk_cfg.cpu_freq_mhz = cpu_freq_mhz;
#elif CONFIG_IDF_TARGET_ESP32S2
clk_cfg.xtal_freq = RTC_XTAL_FREQ_40M;
clk_cfg.cpu_freq = RTC_CPU_FREQ_80M;
#endif
/* ESP32-S2 doesn't have XTAL_FREQ choice, always 40MHz */
clk_cfg.cpu_freq_mhz = cpu_freq_mhz;
clk_cfg.slow_freq = rtc_clk_slow_freq_get();
clk_cfg.fast_freq = rtc_clk_fast_freq_get();
rtc_clk_init(clk_cfg);
@@ -67,11 +65,20 @@ void bootloader_clock_configure(void)
* part of the start up time by enabling 32k XTAL early.
* App startup code will wait until the oscillator has started up.
*/
#ifdef CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS
/* TODO: move the clock option into esp_system, so that this doesn't have
* to continue:
*/
#if CONFIG_ESP32_RTC_CLK_SRC_EXT_CRYS
if (!rtc_clk_32k_enabled()) {
rtc_clk_32k_bootstrap(CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES);
}
#endif
#if CONFIG_ESP32S2_RTC_CLK_SRC_EXT_CRYS
if (!rtc_clk_32k_enabled()) {
rtc_clk_32k_bootstrap(0);
}
#endif
}
#ifdef BOOTLOADER_BUILD

View File

@@ -12,6 +12,7 @@ menu "ESP32S2-specific"
CPU frequency to be set on application startup.
config ESP32S2_DEFAULT_CPU_FREQ_FPGA
depends on IDF_ENV_FPGA
bool "FPGA"
config ESP32S2_DEFAULT_CPU_FREQ_80
bool "80 MHz"
@@ -431,16 +432,37 @@ menu "ESP32S2-specific"
help
Choose which clock is used as RTC clock source.
- "Internal 90kHz oscillator" option provides lowest deep sleep current
consumption, and does not require extra external components. However
frequency stability with respect to temperature is poor, so time may
drift in deep/light sleep modes.
- "External 32kHz crystal" provides better frequency stability, at the
expense of slightly higher (1uA) deep sleep current consumption.
- "External 32kHz oscillator" allows using 32kHz clock generated by an
external circuit. In this case, external clock signal must be connected
to 32K_XP pin. Amplitude should be <1.2V in case of sine wave signal,
and <1V in case of square wave signal. Common mode voltage should be
0.1 < Vcm < 0.5Vamp, where Vamp is the signal amplitude.
Additionally, 1nF capacitor must be connected between 32K_XN pin and
ground. 32K_XN pin can not be used as a GPIO in this case.
- "Internal 8MHz oscillator divided by 256" option results in higher
deep sleep current (by 5uA) but has better frequency stability than
the internal 90kHz oscillator. It does not require external components.
config ESP32S2_RTC_CLK_SRC_INT_RC
bool "Internal 150kHz RC oscillator"
bool "Internal 90kHz RC oscillator"
config ESP32S2_RTC_CLK_SRC_EXT_CRYS
bool "External 32kHz crystal"
config ESP32S2_RTC_CLK_SRC_EXT_OSC
bool "External 32kHz oscillator at 32K_XP pin"
config ESP32S2_RTC_CLK_SRC_INT_8MD256
bool "Internal 8MHz oscillator, divided by 256 (~32kHz)"
endchoice
config ESP32S2_RTC_CLK_CAL_CYCLES
int "Number of cycles for RTC_SLOW_CLK calibration"
default 3000 if ESP32S2_RTC_CLK_SRC_EXT_CRYS
default 1024 if ESP32S2_RTC_CLK_SRC_INT_RC
default 576 if ESP32S2_RTC_CLK_SRC_INT_RC
range 0 125000
help
When the startup code initializes RTC_SLOW_CLK, it can perform

View File

@@ -44,10 +44,29 @@
#define MHZ (1000000)
static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk);
/* Lower threshold for a reasonably-looking calibration value for a 32k XTAL.
* The ideal value (assuming 32768 Hz frequency) is 1000000/32768*(2**19) = 16*10^6.
*/
#define MIN_32K_XTAL_CAL_VAL 15000000L
/* Indicates that this 32k oscillator gets input from external oscillator, rather
* than a crystal.
*/
#define EXT_OSC_FLAG BIT(3)
/* This is almost the same as rtc_slow_freq_t, except that we define
* an extra enum member for the external 32k oscillator.
* For convenience, lower 2 bits should correspond to rtc_slow_freq_t values.
*/
typedef enum {
SLOW_CLK_RTC = RTC_SLOW_FREQ_RTC, //!< Internal 90 kHz RC oscillator
SLOW_CLK_32K_XTAL = RTC_SLOW_FREQ_32K_XTAL, //!< External 32 kHz XTAL
SLOW_CLK_8MD256 = RTC_SLOW_FREQ_8MD256, //!< Internal 8 MHz RC oscillator, divided by 256
SLOW_CLK_32K_EXT_OSC = RTC_SLOW_FREQ_32K_XTAL | EXT_OSC_FLAG //!< External 32k oscillator connected to 32K_XP pin
} slow_clk_sel_t;
static void select_rtc_slow_clk(slow_clk_sel_t slow_clk);
// g_ticks_us defined in ROMs for PRO CPU
extern uint32_t g_ticks_per_us_pro;
static const char *TAG = "clk";
@@ -72,8 +91,12 @@ void esp_clk_init(void)
rtc_wdt_protect_on();
#endif
#ifdef CONFIG_ESP32S2_RTC_CLK_SRC_EXT_CRYS
select_rtc_slow_clk(RTC_SLOW_FREQ_32K_XTAL);
#if defined(CONFIG_ESP32S2_RTC_CLK_SRC_EXT_CRYS)
select_rtc_slow_clk(SLOW_CLK_32K_XTAL);
#elif defined(CONFIG_ESP32S2_RTC_CLK_SRC_EXT_OSC)
select_rtc_slow_clk(SLOW_CLK_32K_EXT_OSC);
#elif defined(CONFIG_ESP32S2_RTC_CLK_SRC_INT_8MD256)
select_rtc_slow_clk(SLOW_CLK_8MD256);
#else
select_rtc_slow_clk(RTC_SLOW_FREQ_RTC);
#endif
@@ -86,61 +109,49 @@ void esp_clk_init(void)
rtc_wdt_protect_on();
#endif
uint32_t freq_mhz = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ;
rtc_cpu_freq_t freq = RTC_CPU_FREQ_80M;
switch (freq_mhz) {
case 240:
freq = RTC_CPU_FREQ_240M;
break;
case 160:
freq = RTC_CPU_FREQ_160M;
break;
case 80:
freq = RTC_CPU_FREQ_80M;
break;
default:
freq_mhz = 80;
freq = RTC_CPU_FREQ_80M;
break;
}
rtc_cpu_freq_config_t old_config, new_config;
rtc_clk_cpu_freq_get_config(&old_config);
const uint32_t old_freq_mhz = old_config.freq_mhz;
const uint32_t new_freq_mhz = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ;
bool res = rtc_clk_cpu_freq_mhz_to_config(new_freq_mhz, &new_config);
assert(res);
// Wait for UART TX to finish, otherwise some UART output will be lost
// when switching APB frequency
uart_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM);
uint32_t freq_before = rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()) / MHZ ;
rtc_clk_cpu_freq_set(freq);
rtc_clk_cpu_freq_set_config(&new_config);
// Re calculate the ccount to make time calculation correct.
uint32_t freq_after = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ;
XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before );
XTHAL_SET_CCOUNT( (uint64_t)XTHAL_GET_CCOUNT() * new_freq_mhz / old_freq_mhz );
}
int IRAM_ATTR esp_clk_cpu_freq(void)
{
return g_ticks_per_us_pro * 1000000;
return ets_get_cpu_frequency() * 1000000;
}
int IRAM_ATTR esp_clk_apb_freq(void)
{
return MIN(g_ticks_per_us_pro, 80) * 1000000;
return MIN(ets_get_cpu_frequency(), 80) * 1000000;
}
void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us)
{
/* Update scale factors used by ets_delay_us */
g_ticks_per_us_pro = ticks_per_us;
}
static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk)
static void select_rtc_slow_clk(slow_clk_sel_t slow_clk)
{
rtc_slow_freq_t rtc_slow_freq = slow_clk & RTC_CNTL_ANA_CLK_RTC_SEL_V;
uint32_t cal_val = 0;
uint32_t wait = 0;
const uint32_t warning_timeout = 3 /* sec */ * 32768 /* Hz */ / (2 * SLOW_CLK_CAL_CYCLES);
bool changing_clock_to_150k = false;
/* number of times to repeat 32k XTAL calibration
* before giving up and switching to the internal RC
*/
#ifdef CONFIG_IDF_TARGET_ESP32
int retry_32k_xtal = 1; /* don't change the behavior for the ESP32 */
#else
int retry_32k_xtal = 3;
#endif
do {
if (slow_clk == RTC_SLOW_FREQ_32K_XTAL) {
if (rtc_slow_freq == RTC_SLOW_FREQ_32K_XTAL) {
/* 32k XTAL oscillator needs to be enabled and running before it can
* be used. Hardware doesn't have a direct way of checking if the
* oscillator is running. Here we use rtc_clk_cal function to count
@@ -149,22 +160,26 @@ static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk)
* will time out, returning 0.
*/
ESP_EARLY_LOGD(TAG, "waiting for 32k oscillator to start up");
rtc_clk_32k_enable(true);
cal_val = rtc_clk_cal(RTC_CAL_32K_XTAL, SLOW_CLK_CAL_CYCLES);
if (cal_val == 0 || cal_val < 15000000L) {
ESP_EARLY_LOGE(TAG, "RTC: Not found External 32 kHz XTAL. Switching to Internal 150 kHz RC chain");
slow_clk = RTC_SLOW_FREQ_RTC;
changing_clock_to_150k = true;
if (slow_clk == SLOW_CLK_32K_XTAL) {
rtc_clk_32k_enable(true);
} else if (slow_clk == SLOW_CLK_32K_EXT_OSC) {
rtc_clk_32k_enable_external();
}
// When SLOW_CLK_CAL_CYCLES is set to 0, clock calibration will not be performed at startup.
if (SLOW_CLK_CAL_CYCLES > 0) {
cal_val = rtc_clk_cal(RTC_CAL_32K_XTAL, SLOW_CLK_CAL_CYCLES);
if (cal_val == 0 || cal_val < MIN_32K_XTAL_CAL_VAL) {
if (retry_32k_xtal-- > 0) {
continue;
}
ESP_EARLY_LOGW(TAG, "32 kHz XTAL not found, switching to internal 90 kHz oscillator");
rtc_slow_freq = RTC_SLOW_FREQ_RTC;
}
}
} else if (rtc_slow_freq == RTC_SLOW_FREQ_8MD256) {
rtc_clk_8m_enable(true, true);
}
rtc_clk_slow_freq_set(slow_clk);
if (changing_clock_to_150k == true && wait > 1) {
// This helps when there are errors when switching the clock from External 32 kHz XTAL to Internal 150 kHz RC chain.
rtc_clk_32k_enable(false);
uint32_t min_bootstrap = 5; // Min bootstrapping for continue switching the clock.
rtc_clk_32k_bootstrap(min_bootstrap);
rtc_clk_32k_enable(true);
}
rtc_clk_slow_freq_set(rtc_slow_freq);
if (SLOW_CLK_CAL_CYCLES > 0) {
/* TODO: 32k XTAL oscillator has some frequency drift at startup.
@@ -175,9 +190,6 @@ static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk)
const uint64_t cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL;
cal_val = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz());
}
if (++wait % warning_timeout == 0) {
ESP_EARLY_LOGW(TAG, "still waiting for source selection RTC");
}
} while (cal_val == 0);
ESP_EARLY_LOGD(TAG, "RTC_SLOW_CLK calibration value: %d", cal_val);
esp_clk_slowclk_cal_set(cal_val);

View File

@@ -317,11 +317,10 @@ void start_cpu0_default(void)
#ifdef CONFIG_PM_ENABLE
esp_pm_impl_init();
#ifdef CONFIG_PM_DFS_INIT_AUTO
rtc_cpu_freq_t max_freq;
rtc_clk_cpu_freq_from_mhz(CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ, &max_freq);
esp_pm_config_esp32_t cfg = {
.max_cpu_freq = max_freq,
.min_cpu_freq = RTC_CPU_FREQ_XTAL
int xtal_freq = (int) rtc_clk_xtal_freq_get();
esp_pm_config_esp32s2_t cfg = {
.max_freq_mhz = CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ,
.min_freq_mhz = xtal_freq,
};
esp_pm_configure(&cfg);
#endif //CONFIG_PM_DFS_INIT_AUTO

View File

@@ -31,10 +31,10 @@ extern "C" {
* Pass a pointer to this structure as an argument to esp_pm_configure function.
*/
typedef struct {
rtc_cpu_freq_t max_cpu_freq; /*!< Maximum CPU frequency to use */
rtc_cpu_freq_t min_cpu_freq; /*!< Minimum CPU frequency to use when no frequency locks are taken */
int max_freq_mhz; /*!< Maximum CPU frequency, in MHz */
int min_freq_mhz; /*!< Minimum CPU frequency to use when no locks are taken, in MHz */
bool light_sleep_enable; /*!< Enter light sleep when no locks are taken */
} esp_pm_config_esp32_t;
} esp_pm_config_esp32s2_t;
#ifdef __cplusplus

View File

@@ -476,7 +476,7 @@ static void esp_panic_dig_reset(void)
// make sure all the panic handler output is sent from UART FIFO
uart_tx_wait_idle(CONFIG_ESP_CONSOLE_UART_NUM);
// switch to XTAL (otherwise we will keep running from the PLL)
rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL);
rtc_clk_cpu_freq_set_xtal();
// reset the digital part
esp_cpu_unstall(PRO_CPU_NUM);
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_SW_SYS_RST);

View File

@@ -1,4 +1,4 @@
// Copyright 2016-2017 Espressif Systems (Shanghai) PTE LTD
// Copyright 2016-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
#include "esp_private/pm_trace.h"
#include "esp_private/esp_timer_private.h"
#include "esp32s2/pm.h"
#include "esp_sleep.h"
/* CCOMPARE update timeout, in CPU cycles. Any value above ~600 cycles will work
* for the purpose of detecting a deadlock.
@@ -51,6 +52,9 @@
*/
#define LIGHT_SLEEP_EARLY_WAKEUP_US 100
/* Minimal divider at which REF_CLK_FREQ can be obtained */
#define REF_CLK_DIV_MIN 2
#ifdef CONFIG_PM_PROFILING
#define WITH_PROFILING
#endif
@@ -75,49 +79,42 @@ static uint32_t s_mode_mask;
static uint32_t s_ccount_div;
static uint32_t s_ccount_mul;
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
/* Indicates if light sleep entry was skipped in vApplicationSleep for given CPU.
* This in turn gets used in IDLE hook to decide if `waiti` needs
* to be invoked or not.
*/
static bool s_skipped_light_sleep[portNUM_PROCESSORS];
#if portNUM_PROCESSORS == 2
/* When light sleep is finished on one CPU, it is possible that the other CPU
* will enter light sleep again very soon, before interrupts on the first CPU
* get a chance to run. To avoid such situation, set a flag for the other CPU to
* skip light sleep attempt.
*/
static bool s_skip_light_sleep[portNUM_PROCESSORS];
#endif // portNUM_PROCESSORS == 2
#endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE
/* Indicates to the ISR hook that CCOMPARE needs to be updated on the given CPU.
* Used in conjunction with cross-core interrupt to update CCOMPARE on the other CPU.
*/
static volatile bool s_need_update_ccompare[portNUM_PROCESSORS];
/* When no RTOS tasks are active, these locks are released to allow going into
* a lower power mode. Used by ISR hook and idle hook.
*/
static esp_pm_lock_handle_t s_rtos_lock_handle[portNUM_PROCESSORS];
/* A flag indicating that Idle hook has run on a given CPU;
* Next interrupt on the same CPU will take s_rtos_lock_handle.
*/
static bool s_core_idle[portNUM_PROCESSORS];
/* g_ticks_us defined in ROM for PRO CPU */
extern uint32_t g_ticks_per_us_pro;
/* When no RTOS tasks are active, these locks are released to allow going into
* a lower power mode. Used by ISR hook and idle hook.
*/
static esp_pm_lock_handle_t s_rtos_lock_handle[portNUM_PROCESSORS];
/* Lookup table of CPU frequencies to be used in each mode.
/* Lookup table of CPU frequency configs to be used in each mode.
* Initialized by esp_pm_impl_init and modified by esp_pm_configure.
*/
rtc_cpu_freq_t s_cpu_freq_by_mode[PM_MODE_COUNT];
/* Lookup table of CPU ticks per microsecond for each RTC_CPU_FREQ_ value.
* Essentially the same as returned by rtc_clk_cpu_freq_value(), but without
* the function call. Not const because XTAL frequency is only known at run time.
*/
static uint32_t s_cpu_freq_to_ticks[] = {
[RTC_CPU_FREQ_XTAL] = 0, /* This is set by esp_pm_impl_init */
[RTC_CPU_FREQ_80M] = 80,
[RTC_CPU_FREQ_160M] = 160,
[RTC_CPU_FREQ_240M] = 240,
[RTC_CPU_FREQ_2M] = 2
};
/* Lookup table of names for each RTC_CPU_FREQ_ value. Used for logging only. */
static const char* s_freq_names[] __attribute__((unused)) = {
[RTC_CPU_FREQ_XTAL] = "XTAL",
[RTC_CPU_FREQ_80M] = "80",
[RTC_CPU_FREQ_160M] = "160",
[RTC_CPU_FREQ_240M] = "240",
[RTC_CPU_FREQ_2M] = "2"
};
rtc_cpu_freq_config_t s_cpu_freq_by_mode[PM_MODE_COUNT];
/* Whether automatic light sleep is enabled */
static bool s_light_sleep_en = false;
@@ -144,7 +141,7 @@ static const char* s_mode_names[] = {
#endif // WITH_PROFILING
static const char* TAG = "pm_esp32";
static const char* TAG = "pm_esp32s2";
static void update_ccompare(void);
static void do_switch(pm_mode_t new_mode);
@@ -167,74 +164,62 @@ pm_mode_t esp_pm_impl_get_mode(esp_pm_lock_type_t type, int arg)
}
}
/* rtc_cpu_freq_t enum is not ordered by frequency, so convert to MHz,
* figure out the maximum value, then convert back to rtc_cpu_freq_t.
*/
static rtc_cpu_freq_t max_freq_of(rtc_cpu_freq_t f1, rtc_cpu_freq_t f2)
{
int f1_hz = rtc_clk_cpu_freq_value(f1);
int f2_hz = rtc_clk_cpu_freq_value(f2);
int f_max_hz = MAX(f1_hz, f2_hz);
rtc_cpu_freq_t result = RTC_CPU_FREQ_XTAL;
if (!rtc_clk_cpu_freq_from_mhz(f_max_hz/1000000, &result)) {
assert(false && "unsupported frequency");
}
return result;
}
esp_err_t esp_pm_configure(const void* vconfig)
{
#ifndef CONFIG_PM_ENABLE
return ESP_ERR_NOT_SUPPORTED;
#endif
const esp_pm_config_esp32_t* config = (const esp_pm_config_esp32_t*) vconfig;
const esp_pm_config_esp32s2_t* config = (const esp_pm_config_esp32s2_t*) vconfig;
#ifndef CONFIG_FREERTOS_USE_TICKLESS_IDLE
if (config->light_sleep_enable) {
return ESP_ERR_NOT_SUPPORTED;
}
#endif
if (config->min_cpu_freq == RTC_CPU_FREQ_2M) {
/* Minimal APB frequency to achieve 1MHz REF_TICK frequency is 5 MHz */
return ESP_ERR_NOT_SUPPORTED;
}
int min_freq_mhz = config->min_freq_mhz;
int max_freq_mhz = config->max_freq_mhz;
rtc_cpu_freq_t min_freq = config->min_cpu_freq;
rtc_cpu_freq_t max_freq = config->max_cpu_freq;
int min_freq_mhz = rtc_clk_cpu_freq_value(min_freq);
int max_freq_mhz = rtc_clk_cpu_freq_value(max_freq);
if (min_freq_mhz > max_freq_mhz) {
return ESP_ERR_INVALID_ARG;
}
rtc_cpu_freq_t apb_max_freq = max_freq; /* CPU frequency in APB_MAX mode */
if (max_freq == RTC_CPU_FREQ_240M) {
/* We can't switch between 240 and 80/160 without disabling PLL,
* so use 240MHz CPU frequency when 80MHz APB frequency is requested.
*/
apb_max_freq = RTC_CPU_FREQ_240M;
} else if (max_freq == RTC_CPU_FREQ_160M || max_freq == RTC_CPU_FREQ_80M) {
/* Otherwise, can use 80MHz
* CPU frequency when 80MHz APB frequency is requested.
*/
apb_max_freq = RTC_CPU_FREQ_80M;
rtc_cpu_freq_config_t freq_config;
if (!rtc_clk_cpu_freq_mhz_to_config(min_freq_mhz, &freq_config)) {
ESP_LOGW(TAG, "invalid min_freq_mhz value (%d)", min_freq_mhz);
return ESP_ERR_INVALID_ARG;
}
apb_max_freq = max_freq_of(apb_max_freq, min_freq);
int xtal_freq_mhz = (int) rtc_clk_xtal_freq_get();
if (min_freq_mhz < xtal_freq_mhz && min_freq_mhz * MHZ / REF_CLK_FREQ < REF_CLK_DIV_MIN) {
ESP_LOGW(TAG, "min_freq_mhz should be >= %d", REF_CLK_FREQ * REF_CLK_DIV_MIN / MHZ);
return ESP_ERR_INVALID_ARG;
}
if (!rtc_clk_cpu_freq_mhz_to_config(max_freq_mhz, &freq_config)) {
ESP_LOGW(TAG, "invalid max_freq_mhz value (%d)", max_freq_mhz);
return ESP_ERR_INVALID_ARG;
}
int apb_max_freq = MIN(max_freq_mhz, 80); /* CPU frequency in APB_MAX mode */
apb_max_freq = MAX(apb_max_freq, min_freq_mhz);
ESP_LOGI(TAG, "Frequency switching config: "
"CPU_MAX: %s, APB_MAX: %s, APB_MIN: %s, Light sleep: %s",
s_freq_names[max_freq],
s_freq_names[apb_max_freq],
s_freq_names[min_freq],
"CPU_MAX: %d, APB_MAX: %d, APB_MIN: %d, Light sleep: %s",
max_freq_mhz,
apb_max_freq,
min_freq_mhz,
config->light_sleep_enable ? "ENABLED" : "DISABLED");
portENTER_CRITICAL(&s_switch_lock);
s_cpu_freq_by_mode[PM_MODE_CPU_MAX] = max_freq;
s_cpu_freq_by_mode[PM_MODE_APB_MAX] = apb_max_freq;
s_cpu_freq_by_mode[PM_MODE_APB_MIN] = min_freq;
s_cpu_freq_by_mode[PM_MODE_LIGHT_SLEEP] = min_freq;
bool res;
res = rtc_clk_cpu_freq_mhz_to_config(max_freq_mhz, &s_cpu_freq_by_mode[PM_MODE_CPU_MAX]);
assert(res);
res = rtc_clk_cpu_freq_mhz_to_config(apb_max_freq, &s_cpu_freq_by_mode[PM_MODE_APB_MAX]);
assert(res);
res = rtc_clk_cpu_freq_mhz_to_config(min_freq_mhz, &s_cpu_freq_by_mode[PM_MODE_APB_MIN]);
assert(res);
s_cpu_freq_by_mode[PM_MODE_LIGHT_SLEEP] = s_cpu_freq_by_mode[PM_MODE_APB_MIN];
s_light_sleep_en = config->light_sleep_enable;
s_config_changed = true;
portEXIT_CRITICAL(&s_switch_lock);
@@ -261,7 +246,7 @@ void IRAM_ATTR esp_pm_impl_switch_mode(pm_mode_t mode,
{
bool need_switch = false;
uint32_t mode_mask = BIT(mode);
portENTER_CRITICAL(&s_switch_lock);
portENTER_CRITICAL_SAFE(&s_switch_lock);
uint32_t count;
if (lock_or_unlock == MODE_LOCK) {
count = ++s_mode_lock_counts[mode];
@@ -288,7 +273,7 @@ void IRAM_ATTR esp_pm_impl_switch_mode(pm_mode_t mode,
s_last_mode_change_time = now;
#endif // WITH_PROFILING
}
portEXIT_CRITICAL(&s_switch_lock);
portEXIT_CRITICAL_SAFE(&s_switch_lock);
if (need_switch && new_mode != s_mode) {
do_switch(new_mode);
}
@@ -310,7 +295,7 @@ static void IRAM_ATTR on_freq_update(uint32_t old_ticks_per_us, uint32_t ticks_p
}
/* Calculate new tick divisor */
_xt_tick_divisor = ticks_per_us * 1000000 / XT_TICK_PER_SEC;
_xt_tick_divisor = ticks_per_us * MHZ / XT_TICK_PER_SEC;
int core_id = xPortGetCoreID();
if (s_rtos_lock_handle[core_id] != NULL) {
@@ -375,17 +360,18 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode)
s_config_changed = false;
portEXIT_CRITICAL_ISR(&s_switch_lock);
rtc_cpu_freq_t new_freq = s_cpu_freq_by_mode[new_mode];
rtc_cpu_freq_t old_freq;
rtc_cpu_freq_config_t new_config = s_cpu_freq_by_mode[new_mode];
rtc_cpu_freq_config_t old_config;
if (!config_changed) {
old_freq = s_cpu_freq_by_mode[s_mode];
old_config = s_cpu_freq_by_mode[s_mode];
} else {
old_freq = rtc_clk_cpu_freq_get();
rtc_clk_cpu_freq_get_config(&old_config);
}
if (new_freq != old_freq) {
uint32_t old_ticks_per_us = g_ticks_per_us_pro;
uint32_t new_ticks_per_us = s_cpu_freq_to_ticks[new_freq];
if (new_config.freq_mhz != old_config.freq_mhz) {
uint32_t old_ticks_per_us = old_config.freq_mhz;
uint32_t new_ticks_per_us = new_config.freq_mhz;
bool switch_down = new_ticks_per_us < old_ticks_per_us;
@@ -393,7 +379,7 @@ static void IRAM_ATTR do_switch(pm_mode_t new_mode)
if (switch_down) {
on_freq_update(old_ticks_per_us, new_ticks_per_us);
}
rtc_clk_cpu_freq_set_fast(new_freq);
rtc_clk_cpu_freq_set_config_fast(&new_config);
if (!switch_down) {
on_freq_update(old_ticks_per_us, new_ticks_per_us);
}
@@ -449,6 +435,28 @@ void esp_pm_impl_idle_hook(void)
ESP_PM_TRACE_ENTER(IDLE, core_id);
}
void IRAM_ATTR esp_pm_impl_isr_hook(void)
{
int core_id = xPortGetCoreID();
ESP_PM_TRACE_ENTER(ISR_HOOK, core_id);
/* Prevent higher level interrupts (than the one this function was called from)
* from happening in this section, since they will also call into esp_pm_impl_isr_hook.
*/
uint32_t state = portENTER_CRITICAL_NESTED();
#if portNUM_PROCESSORS == 2
if (s_need_update_ccompare[core_id]) {
update_ccompare();
s_need_update_ccompare[core_id] = false;
} else {
leave_idle();
}
#else
leave_idle();
#endif // portNUM_PROCESSORS == 2
portEXIT_CRITICAL_NESTED(state);
ESP_PM_TRACE_EXIT(ISR_HOOK, core_id);
}
void esp_pm_impl_waiti(void)
{
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
@@ -467,30 +475,37 @@ void esp_pm_impl_waiti(void)
#endif // CONFIG_FREERTOS_USE_TICKLESS_IDLE
}
void IRAM_ATTR esp_pm_impl_isr_hook(void)
{
int core_id = xPortGetCoreID();
ESP_PM_TRACE_ENTER(ISR_HOOK, core_id);
#if portNUM_PROCESSORS == 2
if (s_need_update_ccompare[core_id]) {
update_ccompare();
s_need_update_ccompare[core_id] = false;
} else {
leave_idle();
}
#else
leave_idle();
#endif // portNUM_PROCESSORS == 2
ESP_PM_TRACE_EXIT(ISR_HOOK, core_id);
}
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
bool IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
static inline bool IRAM_ATTR should_skip_light_sleep(int core_id)
{
#if portNUM_PROCESSORS == 2
if (s_skip_light_sleep[core_id]) {
s_skip_light_sleep[core_id] = false;
s_skipped_light_sleep[core_id] = true;
return true;
}
#endif // portNUM_PROCESSORS == 2
if (s_mode != PM_MODE_LIGHT_SLEEP || s_is_switching) {
s_skipped_light_sleep[core_id] = true;
} else {
s_skipped_light_sleep[core_id] = false;
}
return s_skipped_light_sleep[core_id];
}
static inline void IRAM_ATTR other_core_should_skip_light_sleep(int core_id)
{
#if portNUM_PROCESSORS == 2
s_skip_light_sleep[!core_id] = true;
#endif
}
void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
{
bool result = false;
portENTER_CRITICAL(&s_switch_lock);
if (s_mode == PM_MODE_LIGHT_SLEEP && !s_is_switching) {
int core_id = xPortGetCoreID();
if (!should_skip_light_sleep(core_id)) {
/* Calculate how much we can sleep */
int64_t next_esp_timer_alarm = esp_timer_get_next_alarm();
int64_t now = esp_timer_get_time();
@@ -504,7 +519,6 @@ bool IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#endif
/* Enter sleep */
int core_id = xPortGetCoreID();
ESP_PM_TRACE_ENTER(SLEEP, core_id);
int64_t sleep_start = esp_timer_get_time();
esp_light_sleep_start();
@@ -526,11 +540,10 @@ bool IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
;
}
}
result = true;
other_core_should_skip_light_sleep(core_id);
}
}
portEXIT_CRITICAL(&s_switch_lock);
return result;
}
#endif //CONFIG_FREERTOS_USE_TICKLESS_IDLE
@@ -554,9 +567,9 @@ void esp_pm_impl_dump_stats(FILE* out)
/* don't display light sleep mode if it's not enabled */
continue;
}
fprintf(out, "%8s %6s %12lld %2d%%\n",
fprintf(out, "%8s %3dM %12lld %2d%%\n",
s_mode_names[i],
s_freq_names[s_cpu_freq_by_mode[i]],
s_cpu_freq_by_mode[i].freq_mhz,
time_in_mode[i],
(int) (time_in_mode[i] * 100 / now));
}
@@ -565,7 +578,6 @@ void esp_pm_impl_dump_stats(FILE* out)
void esp_pm_impl_init(void)
{
s_cpu_freq_to_ticks[RTC_CPU_FREQ_XTAL] = rtc_clk_xtal_freq_get();
#ifdef CONFIG_PM_TRACE
esp_pm_trace_init();
#endif
@@ -581,11 +593,11 @@ void esp_pm_impl_init(void)
/* Configure all modes to use the default CPU frequency.
* This will be modified later by a call to esp_pm_configure.
*/
rtc_cpu_freq_t default_freq;
if (!rtc_clk_cpu_freq_from_mhz(CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ, &default_freq)) {
rtc_cpu_freq_config_t default_config;
if (!rtc_clk_cpu_freq_mhz_to_config(CONFIG_ESP32S2_DEFAULT_CPU_FREQ_MHZ, &default_config)) {
assert(false && "unsupported frequency");
}
for (size_t i = 0; i < PM_MODE_COUNT; ++i) {
s_cpu_freq_by_mode[i] = default_freq;
s_cpu_freq_by_mode[i] = default_config;
}
}

View File

@@ -22,12 +22,12 @@
* Feel free to change when debugging.
*/
static const int DRAM_ATTR s_trace_io[] = {
BIT(4), BIT(5), // ESP_PM_TRACE_IDLE
BIT(16), BIT(17), // ESP_PM_TRACE_TICK
BIT(18), BIT(18), // ESP_PM_TRACE_FREQ_SWITCH
BIT(19), BIT(19), // ESP_PM_TRACE_CCOMPARE_UPDATE
BIT(25), BIT(26), // ESP_PM_TRACE_ISR_HOOK
BIT(27), BIT(27), // ESP_PM_TRACE_SLEEP
BIT(2), 0, // ESP_PM_TRACE_IDLE
BIT(3), 0, // ESP_PM_TRACE_TICK
BIT(4), 0, // ESP_PM_TRACE_FREQ_SWITCH
BIT(5), 0, // ESP_PM_TRACE_CCOMPARE_UPDATE
BIT(6), 0, // ESP_PM_TRACE_ISR_HOOK
BIT(7), 0, // ESP_PM_TRACE_SLEEP
};
void esp_pm_trace_init(void)

View File

@@ -172,8 +172,10 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags)
}
// Save current frequency and switch to XTAL
rtc_cpu_freq_t cpu_freq = rtc_clk_cpu_freq_get();
rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL);
// Save current frequency and switch to XTAL
rtc_cpu_freq_config_t cpu_freq_config;
rtc_clk_cpu_freq_get_config(&cpu_freq_config);
rtc_clk_cpu_freq_set_xtal();
// Configure pins for external wakeup
if (s_config.wakeup_triggers & RTC_EXT0_TRIG_EN) {
@@ -199,7 +201,7 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags)
uint32_t result = rtc_sleep_start(s_config.wakeup_triggers, 0, 1);
// Restore CPU frequency
rtc_clk_cpu_freq_set(cpu_freq);
rtc_clk_cpu_freq_set_config(&cpu_freq_config);
// re-enable UART output
resume_uarts();

View File

@@ -94,7 +94,7 @@ void IRAM_ATTR esp_restart_noos(void)
DPORT_REG_WRITE(DPORT_PERIP_RST_EN_REG, 0);
// Set CPU back to XTAL source, no PLL, same as hard reset
rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL);
rtc_clk_cpu_freq_set_xtal();
// Reset CPUs
if (core_id == 0) {

View File

@@ -173,26 +173,35 @@ esp_err_t esp_pm_dump_locks(FILE* stream)
fprintf(stream, "Lock stats:\n");
esp_pm_lock_t* it;
char line[64];
SLIST_FOREACH(it, &s_list, next) {
char *buf = line;
size_t len = sizeof(line) - 1;
line[len] = 0;
size_t cb;
portENTER_CRITICAL(&it->spinlock);
if (it->name == NULL) {
fprintf(stream, "lock@%p ", it);
cb = snprintf(buf, len, "lock@%p ", it);
} else {
fprintf(stream, "%-15s ", it->name);
cb = snprintf(buf, len, "%-15s ", it->name);
}
buf += cb;
len -= cb;
#ifdef WITH_PROFILING
pm_time_t time_held = it->time_held;
if (it->count > 0) {
time_held += cur_time - it->last_taken;
}
fprintf(stream, "%10s %3d %3d %9d %9lld %3lld%%\n",
snprintf(buf, len, "%10s %3d %3d %9d %9lld %3lld%%\n",
s_lock_type_names[it->type], it->arg,
it->count, it->times_taken, time_held,
(time_held + cur_time_d100 - 1) / cur_time_d100);
#else
fprintf(stream, "%10s %3d %3d\n", s_lock_type_names[it->type], it->arg, it->count);
snprintf(buf, len, "%10s %3d %3d\n", s_lock_type_names[it->type], it->arg, it->count);
#endif // WITH_PROFILING
portEXIT_CRITICAL(&it->spinlock);
fputs(line, stream);
}
_lock_release(&s_list_lock);
#ifdef WITH_PROFILING

View File

@@ -130,7 +130,7 @@ typedef enum {
*/
typedef struct rtc_clk_config_s {
rtc_xtal_freq_t xtal_freq : 8; //!< Main XTAL frequency
rtc_cpu_freq_t cpu_freq_mhz : 10; //!< CPU frequency to set, in MHz
uint32_t cpu_freq_mhz : 10; //!< CPU frequency to set, in MHz
rtc_fast_freq_t fast_freq : 1; //!< RTC_FAST_CLK frequency to set
rtc_slow_freq_t slow_freq : 2; //!< RTC_SLOW_CLK frequency to set
uint32_t clk_8m_div : 3; //!< RTC 8M clock divider (division is by clk_8m_div+1, i.e. 0 means 8MHz frequency)

View File

@@ -148,6 +148,26 @@ typedef enum {
RTC_CPU_FREQ_XTAL_DIV2 = 7, //!< XTAL/2 after reset
} rtc_cpu_freq_t;
/**
* @brief CPU clock source
*/
typedef enum {
RTC_CPU_FREQ_SRC_XTAL, //!< XTAL
RTC_CPU_FREQ_SRC_PLL, //!< PLL (480M or 320M)
RTC_CPU_FREQ_SRC_8M, //!< Internal 8M RTC oscillator
RTC_CPU_FREQ_SRC_APLL //!< APLL
} rtc_cpu_freq_src_t;
/**
* @brief CPU clock configuration structure
*/
typedef struct rtc_cpu_freq_config_s {
rtc_cpu_freq_src_t source; //!< The clock from which CPU clock is derived
uint32_t source_freq_mhz; //!< Source clock frequency
uint32_t div; //!< Divider, freq_mhz = source_freq_mhz / div
uint32_t freq_mhz; //!< CPU clock frequency
} rtc_cpu_freq_config_t;
/**
* @brief RTC SLOW_CLK frequency values
*/
@@ -187,7 +207,7 @@ typedef enum {
*/
typedef struct {
rtc_xtal_freq_t xtal_freq : 8; //!< Main XTAL frequency
rtc_cpu_freq_t cpu_freq : 3; //!< CPU frequency to set
uint32_t cpu_freq_mhz : 10; //!< CPU frequency to set, in MHz
rtc_fast_freq_t fast_freq : 1; //!< RTC_FAST_CLK frequency to set
rtc_slow_freq_t slow_freq : 2; //!< RTC_SLOW_CLK frequency to set
uint32_t clk_rtc_clk_div : 8;
@@ -201,7 +221,7 @@ typedef struct {
*/
#define RTC_CLK_CONFIG_DEFAULT() { \
.xtal_freq = RTC_XTAL_FREQ_40M, \
.cpu_freq = RTC_CPU_FREQ_80M, \
.cpu_freq_mhz = 80, \
.fast_freq = RTC_FAST_FREQ_8M, \
.slow_freq = RTC_SLOW_FREQ_RTC, \
.clk_rtc_clk_div = 0, \
@@ -305,6 +325,11 @@ void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq);
*/
void rtc_clk_32k_enable(bool en);
/**
* @brief Configure 32 kHz XTAL oscillator to accept external clock signal
*/
void rtc_clk_32k_enable_external(void);
/**
* @brief Get the state of 32k XTAL oscillator
* @return true if 32k XTAL oscillator has been enabled
@@ -415,63 +440,63 @@ void rtc_clk_fast_freq_set(rtc_fast_freq_t fast_freq);
rtc_fast_freq_t rtc_clk_fast_freq_get(void);
/**
* @brief Switch CPU frequency
*
* If a PLL-derived frequency is requested (80, 160, 240 MHz), this function
* will enable the PLL. Otherwise, PLL will be disabled.
* Note: this function is not optimized for switching speed. It may take several
* hundred microseconds to perform frequency switch.
*
* @param cpu_freq new CPU frequency
* @brief Get CPU frequency config for a given frequency
* @param freq_mhz Frequency in MHz
* @param[out] out_config Output, CPU frequency configuration structure
* @return true if frequency can be obtained, false otherwise
*/
void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq);
bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t* out_config);
/**
* @brief Switch CPU frequency
*
* This is a faster version of rtc_clk_cpu_freq_set, which can handle some of
* the frequency switch paths (XTAL -> PLL, PLL -> XTAL).
* When switching from PLL to XTAL, PLL is not disabled (unlike rtc_clk_cpu_freq_set).
* When switching back from XTAL to PLL, only the same PLL can be used.
* Therefore it is not possible to switch 240 -> XTAL -> (80 or 160) using this
* function.
* This function sets CPU frequency according to the given configuration
* structure. It enables PLLs, if necessary.
*
* For unsupported cases, this function falls back to rtc_clk_cpu_freq_set.
* @note This function in not intended to be called by applications in FreeRTOS
* environment. This is because it does not adjust various timers based on the
* new CPU frequency.
*
* Unlike rtc_clk_cpu_freq_set, this function relies on static data, so it is
* less safe to use it e.g. from a panic handler (when memory might be corrupted).
*
* @param cpu_freq new CPU frequency
* @param config CPU frequency configuration structure
*/
void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq);
void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t* config);
/**
* @brief Get the currently selected CPU frequency
* @brief Switch CPU frequency (optimized for speed)
*
* Although CPU can be clocked by APLL and RTC 8M sources, such support is not
* exposed through this library. As such, this function will not return
* meaningful values when these clock sources are configured (e.g. using direct
* access to clock selection registers). In debug builds, it will assert; in
* release builds, it will return RTC_CPU_FREQ_XTAL.
* This function is a faster equivalent of rtc_clk_cpu_freq_set_config.
* It works faster because it does not disable PLLs when switching from PLL to
* XTAL and does not enabled them when switching back. If PLL is not already
* enabled when this function is called to switch from XTAL to PLL frequency,
* or the PLL which is enabled is the wrong one, this function will fall back
* to calling rtc_clk_cpu_freq_set_config.
*
* @return CPU frequency (one of rtc_cpu_freq_t values)
* Unlike rtc_clk_cpu_freq_set_config, this function relies on static data,
* so it is less safe to use it e.g. from a panic handler (when memory might
* be corrupted).
*
* @note This function in not intended to be called by applications in FreeRTOS
* environment. This is because it does not adjust various timers based on the
* new CPU frequency.
*
* @param config CPU frequency configuration structure
*/
rtc_cpu_freq_t rtc_clk_cpu_freq_get(void);
void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t* config);
/**
* @brief Get corresponding frequency value for rtc_cpu_freq_t enum value
* @param cpu_freq CPU frequency, on of rtc_cpu_freq_t values
* @return CPU frequency, in HZ
* @brief Get the currently used CPU frequency configuration
* @param[out] out_config Output, CPU frequency configuration structure
*/
uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq);
void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t* out_config);
/**
* @brief Get rtc_cpu_freq_t enum value for given CPU frequency
* @param cpu_freq_mhz CPU frequency, one of 80, 160, 240, 2, and XTAL frequency
* @param[out] out_val output, rtc_cpu_freq_t value corresponding to the frequency
* @return true if the given frequency value matches one of enum values
* @brief Switch CPU clock source to XTAL
*
* Short form for filling in rtc_cpu_freq_config_t structure and calling
* rtc_clk_cpu_freq_set_config when a switch to XTAL is needed.
* Assumes that XTAL frequency has been determined — don't call in startup code.
*/
bool rtc_clk_cpu_freq_from_mhz(int cpu_freq_mhz, rtc_cpu_freq_t* out_val);
void rtc_clk_cpu_freq_set_xtal(void);
/**
* @brief Store new APB frequency value into RTC_APB_FREQ_REG

View File

@@ -1,6 +1,7 @@
set(srcs "brownout_hal.c"
"cpu_util.c"
"rtc_clk.c"
"rtc_clk_init.c"
"rtc_init.c"
"rtc_pm.c"
"rtc_sleep.c"

View File

@@ -16,6 +16,7 @@
#include <stdint.h>
#include <stddef.h>
#include <assert.h>
#include <stdlib.h>
#include "sdkconfig.h"
#include "esp32s2/rom/ets_sys.h"
#include "esp32s2/rom/rtc.h"
@@ -30,21 +31,19 @@
#include "soc/syscon_reg.h"
#include "i2c_rtc_clk.h"
#include "soc_log.h"
#include "rtc_clk_common.h"
#include "sdkconfig.h"
#include "xtensa/core-macros.h"
static const char *TAG = "rtc_clk";
/* PLL currently enabled, if any */
typedef enum {
RTC_PLL_NONE,
RTC_PLL_320M,
RTC_PLL_480M
} rtc_pll_t;
static rtc_pll_t s_cur_pll = RTC_PLL_NONE;
#define RTC_PLL_FREQ_320M 320
#define RTC_PLL_FREQ_480M 480
/* Current CPU frequency; saved in a variable for faster freq. switching */
static rtc_cpu_freq_t s_cur_freq = RTC_CPU_FREQ_XTAL;
// Current PLL frequency, in MHZ (320 or 480). Zero if PLL is not enabled.
static int s_cur_pll_freq;
static void rtc_clk_cpu_freq_to_8m(void);
void rtc_clk_32k_enable_internal(x32k_config_t cfg)
{
@@ -58,6 +57,8 @@ void rtc_clk_32k_enable_internal(x32k_config_t cfg)
void rtc_clk_32k_enable(bool enable)
{
if (enable) {
SET_PERI_REG_MASK(RTC_IO_XTAL_32P_PAD_REG, RTC_IO_X32P_MUX_SEL);
SET_PERI_REG_MASK(RTC_IO_XTAL_32N_PAD_REG, RTC_IO_X32N_MUX_SEL);
x32k_config_t cfg = X32K_CONFIG_DEFAULT();
rtc_clk_32k_enable_internal(cfg);
} else {
@@ -66,8 +67,22 @@ void rtc_clk_32k_enable(bool enable)
}
}
void rtc_clk_32k_enable_external(void)
{
SET_PERI_REG_MASK(RTC_IO_XTAL_32P_PAD_REG, RTC_IO_X32P_MUX_SEL);
SET_PERI_REG_MASK(RTC_IO_XTAL_32N_PAD_REG, RTC_IO_X32N_MUX_SEL);
/* TODO: external 32k source may need different settings */
x32k_config_t cfg = X32K_CONFIG_DEFAULT();
rtc_clk_32k_enable_internal(cfg);
}
void rtc_clk_32k_bootstrap(uint32_t cycle)
{
/* No special bootstrapping needed for ESP32-S2, 'cycle' argument is to keep the signature
* same as for the ESP32. Just enable the XTAL here.
*/
(void) cycle;
rtc_clk_32k_enable(true);
}
bool rtc_clk_32k_enabled(void)
@@ -77,11 +92,8 @@ bool rtc_clk_32k_enabled(void)
bool xtal_xpd_sw = (xtal_conf & RTC_CNTL_XTAL32K_XPD_FORCE) >> RTC_CNTL_XTAL32K_XPD_FORCE_S;
/* If xtal xpd software control is on */
bool xtal_xpd_st = (xtal_conf & RTC_CNTL_XPD_XTAL_32K) >> RTC_CNTL_XPD_XTAL_32K_S;
if (xtal_xpd_sw & !xtal_xpd_st) {
return false;
} else {
return true;
}
bool disabled = xtal_xpd_sw && !xtal_xpd_st;
return !disabled;
}
void rtc_clk_8m_enable(bool clk_8m_en, bool d256_en)
@@ -120,25 +132,7 @@ void rtc_clk_apll_enable(bool enable, uint32_t sdm0, uint32_t sdm1, uint32_t sdm
REG_SET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD, enable ? 0 : 1);
REG_SET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PU, enable ? 1 : 0);
/* BIAS I2C not exist any more, but not sure how to get the same effect yet...
* if (!enable &&
* REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL) != DPORT_SOC_CLK_SEL_PLL) {
* REG_SET_BIT(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FORCE_PD);
* } else {
* REG_CLR_BIT(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FORCE_PD);
* }
*/
if (enable) {
/* no need to differentiate ECO chip any more
uint8_t sdm_stop_val_2 = APLL_SDM_STOP_VAL_2_REV1;
uint32_t is_rev0 = (GET_PERI_REG_BITS2(EFUSE_BLK0_RDATA3_REG, 1, 15) == 0);
if (is_rev0) {
sdm0 = 0;
sdm1 = 0;
sdm_stop_val_2 = APLL_SDM_STOP_VAL_2_REV0;
}
*/
I2C_WRITEREG_MASK_RTC(I2C_APLL, I2C_APLL_DSDM2, sdm2);
I2C_WRITEREG_MASK_RTC(I2C_APLL, I2C_APLL_DSDM0, sdm0);
I2C_WRITEREG_MASK_RTC(I2C_APLL, I2C_APLL_DSDM1, sdm1);
@@ -226,17 +220,20 @@ rtc_fast_freq_t rtc_clk_fast_freq_get(void)
return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL);
}
/* In 7.2.2, cpu can run at 80M/160M/240M if PLL is 480M
* pll can run 80M/160M is PLL is 320M
*/
#define DR_REG_I2C_MST_BASE 0x3f40E000
#define I2C_MST_ANA_STATE_REG (DR_REG_I2C_MST_BASE + 0x040)
#define I2C_MST_BBPLL_CAL_END (BIT(24))
#define I2C_MST_BBPLL_CAL_END_M (BIT(24))
#define I2C_MST_BBPLL_CAL_END_V 0x1
#define I2C_MST_BBPLL_CAL_END_S 24
static void rtc_clk_bbpll_disable(void)
{
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BB_I2C_FORCE_PD |
RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD);
s_cur_pll_freq = 0;
}
void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_pll_t pll_freq)
static void rtc_clk_bbpll_enable(void)
{
CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BB_I2C_FORCE_PD |
RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD);
}
void rtc_clk_bbpll_configure(rtc_xtal_freq_t xtal_freq, int pll_freq)
{
uint8_t div_ref;
uint8_t div7_0;
@@ -247,11 +244,8 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_pll_t pll_freq)
assert(xtal_freq == RTC_XTAL_FREQ_40M);
if (pll_freq == RTC_PLL_480M) {
/* Raise the voltage, if needed */
/* move to 240M logic */
//REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M);
/* Set this register to let digital know pll is 480M */
if (pll_freq == RTC_PLL_FREQ_480M) {
/* Clear this register to let the digital part know 480M PLL is used */
SET_PERI_REG_MASK(DPORT_CPU_PER_CONF_REG, DPORT_PLL_FREQ_SEL);
/* Configure 480M PLL */
div_ref = 0;
@@ -262,11 +256,9 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_pll_t pll_freq)
dcur = 4;
I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6B);
} else {
/* Raise the voltage */
//REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_240M);
//ets_delay_us(DELAY_PLL_DBIAS_RAISE);
/* Clear this register to let the digital part know 320M PLL is used */
CLEAR_PERI_REG_MASK(DPORT_CPU_PER_CONF_REG, DPORT_PLL_FREQ_SEL);
/* Configure 480M PLL */
/* Configure 320M PLL */
div_ref = 0;
div7_0 = 4;
dr1 = 0;
@@ -295,280 +287,213 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_pll_t pll_freq)
}
if (ext_cap == 15) {
SOC_LOGE(TAG, "BBPLL SOFTWARE CAL FAIL");
abort();
}
}
/* this delay is replaced by polling Pll calibration end flag
* uint32_t delay_pll_en = (rtc_clk_slow_freq_get() == RTC_SLOW_FREQ_RTC) ?
* DELAY_PLL_ENABLE_WITH_150K : DELAY_PLL_ENABLE_WITH_32K;
* ets_delay_us(delay_pll_en);
*/
/* this calibration didn't work on 480M
need to test exact delay according to 320M
while (!GET_PERI_REG_MASK(I2C_MST_ANA_STATE_REG, I2C_MST_BBPLL_CAL_END)) {
ets_delay_us(1);
}
*/
}
/**
* Switch to XTAL frequency. Does not disable the PLL.
*/
static void rtc_clk_cpu_freq_to_xtal(void)
{
rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get();
ets_update_cpu_frequency(xtal_freq);
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL);
REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT, 0);
REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, 0);
/* Why we need to do this ? */
//DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0); // clear DPORT_CPUPERIOD_SEL
rtc_clk_apb_freq_update(xtal_freq * MHZ);
s_cur_freq = RTC_CPU_FREQ_XTAL;
s_cur_pll = RTC_PLL_NONE;
s_cur_pll_freq = pll_freq;
}
/**
* Switch to one of PLL-based frequencies. Current frequency can be XTAL or PLL.
* PLL must already be enabled.
* If switching between frequencies derived from different PLLs (320M and 480M),
* fall back to rtc_clk_cpu_freq_set.
* @param cpu_freq new CPU frequency
*/
static void rtc_clk_cpu_freq_to_pll(rtc_cpu_freq_t cpu_freq)
static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz)
{
int freq = 0;
if ((s_cur_pll == RTC_PLL_NONE) || ((s_cur_pll == RTC_PLL_320M) && (cpu_freq == RTC_CPU_FREQ_240M))) {
/*
* if switch from non-pll or switch from PLL 320M to 480M
* need to switch PLLs, fall back to full implementation
*/
rtc_clk_cpu_freq_set(cpu_freq);
return;
int dbias = DIG_DBIAS_80M_160M;
int per_conf = DPORT_CPUPERIOD_SEL_80;
if (cpu_freq_mhz == 80) {
/* nothing to do */
} else if (cpu_freq_mhz == 160) {
per_conf = DPORT_CPUPERIOD_SEL_160;
} else if (cpu_freq_mhz == 240) {
dbias = DIG_DBIAS_240M;
per_conf = DPORT_CPUPERIOD_SEL_240;
} else {
SOC_LOGE(TAG, "invalid frequency");
abort();
}
if ((cpu_freq == RTC_CPU_FREQ_80M) || (cpu_freq == RTC_CPU_320M_80M)) {
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M);
DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0);
freq = 80;
} else if ((cpu_freq == RTC_CPU_FREQ_160M) || (cpu_freq == RTC_CPU_320M_160M)) {
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M);
DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 1);
freq = 160;
} else if (cpu_freq == RTC_CPU_FREQ_240M) {
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_240M);
DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 2);
freq = 240;
}
// REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, DPORT_SOC_CLK_SEL_PLL);
rtc_clk_apb_freq_update(80 * MHZ);
ets_update_cpu_frequency(freq);
s_cur_freq = cpu_freq;
}
void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq)
{
if (cpu_freq == s_cur_freq) {
return;
} else if (cpu_freq == RTC_CPU_FREQ_2M || s_cur_freq == RTC_CPU_FREQ_2M) {
/* fall back to full implementation if switch to/from 2M is needed */
rtc_clk_cpu_freq_set(cpu_freq);
} else if (cpu_freq == RTC_CPU_FREQ_XTAL) {
rtc_clk_cpu_freq_to_xtal();
} else if (cpu_freq > RTC_CPU_FREQ_XTAL) {
rtc_clk_cpu_freq_to_pll(cpu_freq);
/* Not neccessary any more */
//rtc_clk_wait_for_slow_cycle();
}
}
void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
{
rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get();
/* Switch CPU to XTAL frequency first */
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL);
REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, 0);
REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, per_conf);
REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT, 0);
ets_update_cpu_frequency(xtal_freq);
/* Frequency switch is synchronized to SLOW_CLK cycle. Wait until the switch
* is complete before disabling the PLL.
*/
/* register SOC_CLK_SEL is moved to APB domain, so this delay is not neccessary any more */
//rtc_clk_wait_for_slow_cycle();
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, dbias);
REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, DPORT_SOC_CLK_SEL_PLL);
rtc_clk_apb_freq_update(80 * MHZ);
ets_update_cpu_frequency(cpu_freq_mhz);
}
DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0);
bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t* out_config)
{
uint32_t source_freq_mhz;
rtc_cpu_freq_src_t source;
uint32_t divider;
uint32_t real_freq_mhz;
/* BBPLL force power down won't affect force power up setting */
SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG,
RTC_CNTL_BB_I2C_FORCE_PD | RTC_CNTL_BBPLL_FORCE_PD |
RTC_CNTL_BBPLL_I2C_FORCE_PD);
s_cur_pll = RTC_PLL_NONE;
rtc_clk_apb_freq_update(xtal_freq * MHZ);
uint32_t xtal_freq = (uint32_t) rtc_clk_xtal_freq_get();
if (freq_mhz <= xtal_freq) {
divider = xtal_freq / freq_mhz;
real_freq_mhz = (xtal_freq + divider / 2) / divider; /* round */
if (real_freq_mhz != freq_mhz) {
// no suitable divider
return false;
}
/* is APLL under force power down? */
/* may need equivalent function
uint32_t apll_fpd = REG_GET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD);
* if (apll_fpd) {
* SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BIAS_I2C_FORCE_PD);
* }
*/
/* now switch to the desired frequency */
if (cpu_freq == RTC_CPU_FREQ_XTAL) {
/* already at XTAL, nothing to do */
} else if (cpu_freq == RTC_CPU_FREQ_2M) {
/* set up divider to produce 2MHz from XTAL */
REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT, (xtal_freq / 2) - 1);
ets_update_cpu_frequency(2);
rtc_clk_apb_freq_update(2 * MHZ);
/* lower the voltage */
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_2M);
} else {
/* use PLL as clock source */
CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG,
RTC_CNTL_BB_I2C_FORCE_PD |
RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD);
if (cpu_freq > RTC_CPU_FREQ_2M) {
rtc_clk_bbpll_set(xtal_freq, RTC_PLL_320M);
s_cur_pll = RTC_PLL_320M;
} else {
rtc_clk_bbpll_set(xtal_freq, RTC_PLL_480M);
s_cur_pll = RTC_PLL_480M;
}
if ((cpu_freq == RTC_CPU_FREQ_80M) || (cpu_freq == RTC_CPU_320M_80M)) {
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M);
DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0);
ets_update_cpu_frequency(80);
} else if ((cpu_freq == RTC_CPU_FREQ_160M) || (cpu_freq == RTC_CPU_320M_160M)) {
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M);
DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 1);
ets_update_cpu_frequency(160);
} else if (cpu_freq == RTC_CPU_FREQ_240M) {
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_240M);
DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 2);
ets_update_cpu_frequency(240);
}
REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, 1);
//rtc_clk_wait_for_slow_cycle();
rtc_clk_apb_freq_update(80 * MHZ);
}
s_cur_freq = cpu_freq;
}
rtc_cpu_freq_t rtc_clk_cpu_freq_get(void)
{
uint32_t soc_clk_sel = REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL);
switch (soc_clk_sel) {
case 0: {
uint32_t pre_div = REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT);
if (pre_div == 0) {
return RTC_CPU_FREQ_XTAL;
} else if (pre_div == 1) {
return RTC_CPU_FREQ_XTAL_DIV2;
} else if (pre_div == rtc_clk_xtal_freq_get() / 2 - 1) {
return RTC_CPU_FREQ_2M;
} else {
assert(false && "unsupported frequency");
}
break;
}
case 1: {
uint32_t cpuperiod_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL);
uint32_t pllfreq_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_PLL_FREQ_SEL);
if (cpuperiod_sel == 0) {
if (pllfreq_sel == 1) {
return RTC_CPU_FREQ_80M;
} else {
return RTC_CPU_320M_80M;
}
} else if (cpuperiod_sel == 1) {
if (pllfreq_sel == 1) {
return RTC_CPU_FREQ_160M;
} else {
return RTC_CPU_320M_160M;
}
} else if (cpuperiod_sel == 2) {
return RTC_CPU_FREQ_240M;
} else {
assert(false && "unsupported frequency");
}
break;
}
case 2:
case 3:
default:
assert(false && "unsupported frequency");
}
return 0;
}
uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq)
{
switch (cpu_freq) {
case RTC_CPU_FREQ_XTAL:
return ((uint32_t) rtc_clk_xtal_freq_get()) * MHZ;
case RTC_CPU_FREQ_XTAL_DIV2:
return ((uint32_t) rtc_clk_xtal_freq_get()) / 2 * MHZ;
case RTC_CPU_FREQ_2M:
return 2 * MHZ;
case RTC_CPU_FREQ_80M:
return 80 * MHZ;
case RTC_CPU_FREQ_160M:
return 160 * MHZ;
case RTC_CPU_FREQ_240M:
return 240 * MHZ;
case RTC_CPU_320M_80M:
return 80 * MHZ;
case RTC_CPU_320M_160M:
return 160 * MHZ;
default:
assert(false && "invalid rtc_cpu_freq_t value");
return 0;
}
}
bool rtc_clk_cpu_freq_from_mhz(int mhz, rtc_cpu_freq_t *out_val)
{
if (mhz == 240) {
*out_val = RTC_CPU_FREQ_240M;
} else if (mhz == 160) {
*out_val = RTC_CPU_FREQ_160M;
} else if (mhz == 80) {
*out_val = RTC_CPU_FREQ_80M;
} else if (mhz == (int) rtc_clk_xtal_freq_get()) {
*out_val = RTC_CPU_FREQ_XTAL;
} else if (mhz == (int) rtc_clk_xtal_freq_get() / 2) {
*out_val = RTC_CPU_FREQ_XTAL_DIV2;
} else if (mhz == 2) {
*out_val = RTC_CPU_FREQ_2M;
source_freq_mhz = xtal_freq;
source = RTC_CPU_FREQ_SRC_XTAL;
} else if (freq_mhz == 80) {
real_freq_mhz = freq_mhz;
source = RTC_CPU_FREQ_SRC_PLL;
source_freq_mhz = RTC_PLL_FREQ_480M;
divider = 6;
} else if (freq_mhz == 160) {
real_freq_mhz = freq_mhz;
source = RTC_CPU_FREQ_SRC_PLL;
source_freq_mhz = RTC_PLL_FREQ_480M;
divider = 3;
} else if (freq_mhz == 240) {
real_freq_mhz = freq_mhz;
source = RTC_CPU_FREQ_SRC_PLL;
source_freq_mhz = RTC_PLL_FREQ_480M;
divider = 2;
} else {
// unsupported frequency
return false;
}
*out_config = (rtc_cpu_freq_config_t) {
.source = source,
.div = divider,
.source_freq_mhz = source_freq_mhz,
.freq_mhz = real_freq_mhz
};
return true;
}
/* Values of RTC_XTAL_FREQ_REG and RTC_APB_FREQ_REG are stored as two copies in
* lower and upper 16-bit halves. These are the routines to work with such a
* representation.
void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t* config)
{
rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get();
uint32_t soc_clk_sel = REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL);
if (soc_clk_sel != DPORT_SOC_CLK_SEL_XTAL) {
rtc_clk_cpu_freq_to_xtal(xtal_freq, 1);
}
if (soc_clk_sel == DPORT_SOC_CLK_SEL_PLL) {
rtc_clk_bbpll_disable();
}
if (config->source == RTC_CPU_FREQ_SRC_XTAL) {
if (config->div > 1) {
rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div);
}
} else if (config->source == RTC_CPU_FREQ_SRC_PLL) {
rtc_clk_bbpll_enable();
rtc_clk_bbpll_configure(rtc_clk_xtal_freq_get(), config->source_freq_mhz);
rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz);
} else if (config->source == RTC_CPU_FREQ_SRC_8M) {
rtc_clk_cpu_freq_to_8m();
}
}
void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t* out_config)
{
rtc_cpu_freq_src_t source;
uint32_t source_freq_mhz;
uint32_t div;
uint32_t freq_mhz;
uint32_t soc_clk_sel = REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL);
switch (soc_clk_sel) {
case DPORT_SOC_CLK_SEL_XTAL: {
source = RTC_CPU_FREQ_SRC_XTAL;
div = REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT) + 1;
source_freq_mhz = (uint32_t) rtc_clk_xtal_freq_get();
freq_mhz = source_freq_mhz / div;
}
break;
case DPORT_SOC_CLK_SEL_PLL: {
source = RTC_CPU_FREQ_SRC_PLL;
uint32_t cpuperiod_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL);
uint32_t pllfreq_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_PLL_FREQ_SEL);
source_freq_mhz = (pllfreq_sel) ? RTC_PLL_FREQ_480M : RTC_PLL_FREQ_320M;
if (cpuperiod_sel == DPORT_CPUPERIOD_SEL_80) {
div = (source_freq_mhz == RTC_PLL_FREQ_480M) ? 6 : 4;
freq_mhz = 80;
} else if (cpuperiod_sel == DPORT_CPUPERIOD_SEL_160) {
div = (source_freq_mhz == RTC_PLL_FREQ_480M) ? 3 : 2;
div = 3;
freq_mhz = 160;
} else if (cpuperiod_sel == DPORT_CPUPERIOD_SEL_240) {
div = 2;
freq_mhz = 240;
} else {
SOC_LOGE(TAG, "unsupported frequency configuration");
abort();
}
break;
}
case DPORT_SOC_CLK_SEL_8M:
source = RTC_CPU_FREQ_SRC_8M;
source_freq_mhz = 8;
div = 1;
freq_mhz = source_freq_mhz;
break;
case DPORT_SOC_CLK_SEL_APLL:
default:
SOC_LOGE(TAG, "unsupported frequency configuration");
abort();
}
*out_config = (rtc_cpu_freq_config_t) {
.source = source,
.source_freq_mhz = source_freq_mhz,
.div = div,
.freq_mhz = freq_mhz
};
}
void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t* config)
{
if (config->source == RTC_CPU_FREQ_SRC_XTAL) {
rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div);
} else if (config->source == RTC_CPU_FREQ_SRC_PLL &&
s_cur_pll_freq == config->source_freq_mhz) {
rtc_clk_cpu_freq_to_pll_mhz(config->freq_mhz);
} else {
/* fallback */
rtc_clk_cpu_freq_set_config(config);
}
}
void rtc_clk_cpu_freq_set_xtal(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.
*/
static bool clk_val_is_valid(uint32_t val)
void rtc_clk_cpu_freq_to_xtal(int freq, int div)
{
return (val & 0xffff) == ((val >> 16) & 0xffff) &&
val != 0 &&
val != UINT32_MAX;
ets_update_cpu_frequency(freq);
/* Set divider from XTAL to APB clock. Need to set divider to 1 (reg. value 0) first. */
REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT, 0);
REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT, div - 1);
/* no need to adjust the REF_TICK */
/* switch clock source */
REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, DPORT_SOC_CLK_SEL_XTAL);
rtc_clk_apb_freq_update(freq * MHZ);
/* lower the voltage */
if (freq <= 2) {
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_2M);
} else {
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL);
}
}
static uint32_t reg_val_to_clk_val(uint32_t val)
static void rtc_clk_cpu_freq_to_8m(void)
{
return val & UINT16_MAX;
}
static uint32_t clk_val_to_reg_val(uint32_t val)
{
return (val & UINT16_MAX) | ((val & UINT16_MAX) << 16);
ets_update_cpu_frequency(8);
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL);
REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_PRE_DIV_CNT, 0);
REG_SET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL, DPORT_SOC_CLK_SEL_8M);
rtc_clk_apb_freq_update(RTC_FAST_CLK_FREQ_8M);
}
rtc_xtal_freq_t rtc_clk_xtal_freq_get(void)
@@ -614,66 +539,6 @@ void rtc_clk_8m_divider_set(uint32_t div)
SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL_VLD);
}
void rtc_clk_init(rtc_clk_config_t cfg)
{
rtc_cpu_freq_t cpu_source_before = rtc_clk_cpu_freq_get();
/* If we get a TG WDT system reset while running at 240MHz,
* DPORT_CPUPERIOD_SEL register will be reset to 0 resulting in 120MHz
* APB and CPU frequencies after reset. This will cause issues with XTAL
* frequency estimation, so we switch to XTAL frequency first.
*
* Ideally we would only do this if SYSCON_SOC_CLK_SEL == PLL and
* PLL is configured for 480M, but it takes less time to switch to 40M and
* run the following code than querying the PLL does.
*/
if (REG_GET_FIELD(DPORT_SYSCLK_CONF_REG, DPORT_SOC_CLK_SEL) == 1) {
rtc_clk_cpu_freq_set(RTC_CPU_FREQ_XTAL);
}
/* Set tuning parameters for 8M and 150k clocks.
* Note: this doesn't attempt to set the clocks to precise frequencies.
* Instead, we calibrate these clocks against XTAL frequency later, when necessary.
* - SCK_DCAP value controls tuning of 150k clock.
* The higher the value of DCAP is, the lower is the frequency.
* - CK8M_DFREQ value controls tuning of 8M clock.
* CLK_8M_DFREQ constant gives the best temperature characteristics.
*/
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_SCK_DCAP, cfg.slow_clk_dcap);
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DFREQ, cfg.clk_8m_dfreq);
/* Configure 150k clock division */
rtc_clk_divider_set(cfg.clk_rtc_clk_div);
/* Configure 8M clock division */
rtc_clk_8m_divider_set(cfg.clk_8m_clk_div);
/* Enable the internal bus used to configure PLLs */
SET_PERI_REG_BITS(ANA_CONFIG_REG, ANA_CONFIG_M, ANA_CONFIG_M, ANA_CONFIG_S);
CLEAR_PERI_REG_MASK(ANA_CONFIG_REG, I2C_APLL_M | I2C_BBPLL_M);
rtc_xtal_freq_t xtal_freq = cfg.xtal_freq;
uart_tx_wait_idle(0);
rtc_clk_xtal_freq_update(xtal_freq);
rtc_clk_apb_freq_update(xtal_freq * MHZ);
/* Set CPU frequency */
rtc_clk_cpu_freq_set(cfg.cpu_freq);
/* Re-calculate the ccount to make time calculation correct. */
uint32_t freq_before = rtc_clk_cpu_freq_value(cpu_source_before) / MHZ;
uint32_t freq_after = rtc_clk_cpu_freq_value(cfg.cpu_freq) / MHZ;
XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before );
/* Slow & fast clocks setup */
if (cfg.slow_freq == RTC_SLOW_FREQ_32K_XTAL) {
rtc_clk_32k_enable(true);
}
if (cfg.fast_freq == RTC_FAST_FREQ_8M) {
bool need_8md256 = cfg.slow_freq == RTC_SLOW_FREQ_8MD256;
rtc_clk_8m_enable(true, need_8md256);
}
rtc_clk_fast_freq_set(cfg.fast_freq);
rtc_clk_slow_freq_set(cfg.slow_freq);
}
/* Name used in libphy.a:phy_chip_v7.o
* TODO: update the library to use rtc_clk_xtal_freq_get
*/

View File

@@ -0,0 +1,56 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#define MHZ (1000000)
#define DPORT_CPUPERIOD_SEL_80 0
#define DPORT_CPUPERIOD_SEL_160 1
#define DPORT_CPUPERIOD_SEL_240 2
#define DPORT_SOC_CLK_SEL_XTAL 0
#define DPORT_SOC_CLK_SEL_PLL 1
#define DPORT_SOC_CLK_SEL_8M 2
#define DPORT_SOC_CLK_SEL_APLL 3
#define RTC_FAST_CLK_FREQ_8M 8500000
#ifdef __cplusplus
extern "C" {
#endif
void rtc_clk_cpu_freq_to_xtal(int freq, int div);
/* Values of RTC_XTAL_FREQ_REG and RTC_APB_FREQ_REG are stored as two copies in
* lower and upper 16-bit halves. These are the routines to work with such a
* representation.
*/
static inline bool clk_val_is_valid(uint32_t val) {
return (val & 0xffff) == ((val >> 16) & 0xffff) &&
val != 0 &&
val != UINT32_MAX;
}
static inline uint32_t reg_val_to_clk_val(uint32_t val) {
return val & UINT16_MAX;
}
static inline uint32_t clk_val_to_reg_val(uint32_t val) {
return (val & UINT16_MAX) | ((val & UINT16_MAX) << 16);
}
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,88 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include "esp32s2/rom/ets_sys.h"
#include "esp32s2/rom/rtc.h"
#include "esp32s2/rom/uart.h"
#include "soc/rtc.h"
#include "soc/rtc_periph.h"
#include "soc/sens_periph.h"
#include "soc/efuse_periph.h"
#include "soc/apb_ctrl_reg.h"
#include "i2c_rtc_clk.h"
#include "soc_log.h"
#include "sdkconfig.h"
#include "xtensa/core-macros.h"
#include "rtc_clk_common.h"
static const char* TAG = "rtc_clk_init";
void rtc_clk_init(rtc_clk_config_t cfg)
{
rtc_cpu_freq_config_t old_config, new_config;
/* Set tuning parameters for 8M and 90k clocks.
* Note: this doesn't attempt to set the clocks to precise frequencies.
* Instead, we calibrate these clocks against XTAL frequency later, when necessary.
* - SCK_DCAP value controls tuning of 90k clock.
* The higher the value of DCAP is, the lower is the frequency.
* - CK8M_DFREQ value controls tuning of 8M clock.
* CLK_8M_DFREQ constant gives the best temperature characteristics.
*/
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_SCK_DCAP, cfg.slow_clk_dcap);
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DFREQ, cfg.clk_8m_dfreq);
/* Configure 90k clock division */
rtc_clk_divider_set(cfg.clk_rtc_clk_div);
/* Configure 8M clock division */
rtc_clk_8m_divider_set(cfg.clk_8m_clk_div);
/* Enable the internal bus used to configure PLLs */
SET_PERI_REG_BITS(ANA_CONFIG_REG, ANA_CONFIG_M, ANA_CONFIG_M, ANA_CONFIG_S);
CLEAR_PERI_REG_MASK(ANA_CONFIG_REG, I2C_APLL_M | I2C_BBPLL_M);
rtc_xtal_freq_t xtal_freq = cfg.xtal_freq;
uart_tx_wait_idle(0);
rtc_clk_xtal_freq_update(xtal_freq);
rtc_clk_apb_freq_update(xtal_freq * MHZ);
/* Set CPU frequency */
rtc_clk_cpu_freq_get_config(&old_config);
uint32_t freq_before = old_config.freq_mhz;
bool res = rtc_clk_cpu_freq_mhz_to_config(cfg.cpu_freq_mhz, &new_config);
if (!res) {
SOC_LOGE(TAG, "invalid CPU frequency value");
abort();
}
rtc_clk_cpu_freq_set_config(&new_config);
/* Re-calculate the ccount to make time calculation correct. */
XTHAL_SET_CCOUNT( (uint64_t)XTHAL_GET_CCOUNT() * cfg.cpu_freq_mhz / freq_before );
/* Slow & fast clocks setup */
if (cfg.slow_freq == RTC_SLOW_FREQ_32K_XTAL) {
rtc_clk_32k_enable(true);
}
if (cfg.fast_freq == RTC_FAST_FREQ_8M) {
bool need_8md256 = cfg.slow_freq == RTC_SLOW_FREQ_8MD256;
rtc_clk_8m_enable(true, need_8md256);
}
rtc_clk_fast_freq_set(cfg.fast_freq);
rtc_clk_slow_freq_set(cfg.slow_freq);
}

View File

@@ -38,6 +38,19 @@
*/
uint32_t rtc_clk_cal_internal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles)
{
/* On ESP32S2, choosing RTC_CAL_RTC_MUX results in calibration of
* the 90k RTC clock regardless of the currenlty selected SLOW_CLK.
* On the ESP32, it used the currently selected SLOW_CLK.
* The following code emulates ESP32 behavior:
*/
if (cal_clk == RTC_CAL_RTC_MUX) {
rtc_slow_freq_t slow_freq = rtc_clk_slow_freq_get();
if (slow_freq == RTC_SLOW_FREQ_32K_XTAL) {
cal_clk = RTC_CAL_32K_XTAL;
} else if (slow_freq == RTC_SLOW_FREQ_8MD256) {
cal_clk = RTC_CAL_8MD256;
}
}
/* Enable requested clock (150k clock is always on) */
int dig_32k_xtal_state = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN);
if (cal_clk == RTC_CAL_32K_XTAL && !dig_32k_xtal_state) {