mirror of
https://github.com/espressif/esp-idf.git
synced 2025-09-06 15:58:47 +00:00
Merge branch 'feature/rtc_cpu_freq_config' into 'master'
soc/rtc: Refactoring, support CPU frequencies lower than XTAL See merge request idf/esp-idf!2856
This commit is contained in:
@@ -29,7 +29,7 @@ void bootloader_clock_configure()
|
||||
uart_tx_wait_idle(0);
|
||||
|
||||
/* Set CPU to 80MHz. Keep other clocks unmodified. */
|
||||
rtc_cpu_freq_t cpu_freq = RTC_CPU_FREQ_80M;
|
||||
int cpu_freq_mhz = 80;
|
||||
|
||||
/* On ESP32 rev 0, switching to 80MHz if clock was previously set to
|
||||
* 240 MHz may cause the chip to lock up (see section 3.5 of the errata
|
||||
@@ -39,12 +39,12 @@ void bootloader_clock_configure()
|
||||
uint32_t chip_ver_reg = REG_READ(EFUSE_BLK0_RDATA3_REG);
|
||||
if ((chip_ver_reg & EFUSE_RD_CHIP_VER_REV1_M) == 0 &&
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ == 240) {
|
||||
cpu_freq = RTC_CPU_FREQ_240M;
|
||||
cpu_freq_mhz = 240;
|
||||
}
|
||||
|
||||
rtc_clk_config_t clk_cfg = RTC_CLK_CONFIG_DEFAULT();
|
||||
clk_cfg.xtal_freq = CONFIG_ESP32_XTAL_FREQ;
|
||||
clk_cfg.cpu_freq = cpu_freq;
|
||||
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);
|
||||
|
@@ -77,44 +77,37 @@ void esp_clk_init(void)
|
||||
select_rtc_slow_clk(RTC_SLOW_FREQ_RTC);
|
||||
#endif
|
||||
|
||||
uint32_t freq_mhz = CONFIG_ESP32_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;
|
||||
default:
|
||||
freq_mhz = 80;
|
||||
/* falls through */
|
||||
case 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_ESP32_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_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_ESP32_DEFAULT_CPU_FREQ_MHZ;
|
||||
XTHAL_SET_CCOUNT( XTHAL_GET_CCOUNT() * freq_after / freq_before );
|
||||
XTHAL_SET_CCOUNT( 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 g_ticks_per_us_pro * MHZ;
|
||||
}
|
||||
|
||||
int IRAM_ATTR esp_clk_apb_freq(void)
|
||||
{
|
||||
return MIN(g_ticks_per_us_pro, 80) * 1000000;
|
||||
return MIN(g_ticks_per_us_pro, 80) * MHZ;
|
||||
}
|
||||
|
||||
int IRAM_ATTR esp_clk_xtal_freq(void)
|
||||
{
|
||||
return rtc_clk_xtal_freq_get() * MHZ;
|
||||
}
|
||||
|
||||
void IRAM_ATTR ets_update_cpu_frequency(uint32_t ticks_per_us)
|
||||
|
@@ -31,8 +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 */
|
||||
rtc_cpu_freq_t max_cpu_freq __attribute__((deprecated)); /*!< Maximum CPU frequency to use. Deprecated, use max_freq_mhz instead. */
|
||||
int max_freq_mhz; /*!< Maximum CPU frequency, in MHz */
|
||||
rtc_cpu_freq_t min_cpu_freq __attribute__((deprecated)); /*!< Minimum CPU frequency to use when no frequency locks are taken. Deprecated, use min_freq_mhz instead. */
|
||||
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;
|
||||
|
||||
|
@@ -62,6 +62,17 @@ int esp_clk_cpu_freq(void);
|
||||
*/
|
||||
int esp_clk_apb_freq(void);
|
||||
|
||||
/**
|
||||
* @brief Return frequency of the main XTAL
|
||||
*
|
||||
* Frequency of the main XTAL can be either auto-detected or set at compile
|
||||
* time (see CONFIG_ESP32_XTAL_FREQ_SEL sdkconfig option). In both cases, this
|
||||
* function returns the actual value at run time.
|
||||
*
|
||||
* @return XTAL frequency, in Hz
|
||||
*/
|
||||
int esp_clk_xtal_freq(void);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Read value of RTC counter, converting it to microseconds
|
||||
|
@@ -407,7 +407,7 @@ static void esp_panic_dig_reset()
|
||||
// make sure all the panic handler output is sent from UART FIFO
|
||||
uart_tx_wait_idle(CONFIG_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);
|
||||
|
@@ -51,6 +51,11 @@
|
||||
*/
|
||||
#define LIGHT_SLEEP_EARLY_WAKEUP_US 100
|
||||
|
||||
/* Minimal divider at which REF_CLK_FREQ can be obtained */
|
||||
#define REF_CLK_DIV_MIN 10
|
||||
|
||||
#define MHZ 1000000
|
||||
|
||||
#ifdef CONFIG_PM_PROFILING
|
||||
#define WITH_PROFILING
|
||||
#endif
|
||||
@@ -80,44 +85,20 @@ static uint32_t s_ccount_mul;
|
||||
*/
|
||||
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;
|
||||
@@ -167,21 +148,6 @@ 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
|
||||
@@ -195,46 +161,66 @@ esp_err_t esp_pm_configure(const void* vconfig)
|
||||
}
|
||||
#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;
|
||||
|
||||
if (min_freq_mhz == 0 && max_freq_mhz == 0) {
|
||||
/* For compatibility, handle deprecated fields, min_cpu_freq and max_cpu_freq. */
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
min_freq_mhz = rtc_clk_cpu_freq_value(config->min_cpu_freq) / MHZ;
|
||||
max_freq_mhz = rtc_clk_cpu_freq_value(config->max_cpu_freq) / MHZ;
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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 = max_freq_mhz; /* CPU frequency in APB_MAX mode */
|
||||
if (max_freq_mhz == 240) {
|
||||
/* 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) {
|
||||
apb_max_freq = 240;
|
||||
} else if (max_freq_mhz == 160 || max_freq_mhz == 80) {
|
||||
/* Otherwise, can use 80MHz
|
||||
* CPU frequency when 80MHz APB frequency is requested.
|
||||
*/
|
||||
apb_max_freq = RTC_CPU_FREQ_80M;
|
||||
apb_max_freq = 80;
|
||||
}
|
||||
|
||||
apb_max_freq = max_freq_of(apb_max_freq, min_freq);
|
||||
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;
|
||||
rtc_clk_cpu_freq_mhz_to_config(max_freq_mhz, &s_cpu_freq_by_mode[PM_MODE_CPU_MAX]);
|
||||
rtc_clk_cpu_freq_mhz_to_config(apb_max_freq, &s_cpu_freq_by_mode[PM_MODE_APB_MAX]);
|
||||
rtc_clk_cpu_freq_mhz_to_config(min_freq_mhz, &s_cpu_freq_by_mode[PM_MODE_APB_MIN]);
|
||||
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);
|
||||
@@ -310,7 +296,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 +361,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 +380,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);
|
||||
}
|
||||
@@ -536,9 +523,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));
|
||||
}
|
||||
@@ -547,7 +534,6 @@ void esp_pm_impl_dump_stats(FILE* out)
|
||||
|
||||
void esp_pm_impl_init()
|
||||
{
|
||||
s_cpu_freq_to_ticks[RTC_CPU_FREQ_XTAL] = rtc_clk_xtal_freq_get();
|
||||
#ifdef CONFIG_PM_TRACE
|
||||
esp_pm_trace_init();
|
||||
#endif
|
||||
@@ -563,11 +549,11 @@ void esp_pm_impl_init()
|
||||
/* 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_ESP32_DEFAULT_CPU_FREQ_MHZ, &default_freq)) {
|
||||
rtc_cpu_freq_config_t default_config;
|
||||
if (!rtc_clk_cpu_freq_mhz_to_config(CONFIG_ESP32_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;
|
||||
}
|
||||
}
|
||||
|
@@ -178,8 +178,9 @@ 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);
|
||||
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) {
|
||||
@@ -205,7 +206,7 @@ static uint32_t IRAM_ATTR esp_sleep_start(uint32_t pd_flags)
|
||||
uint32_t result = rtc_sleep_start(s_config.wakeup_triggers, 0);
|
||||
|
||||
// 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();
|
||||
|
@@ -334,7 +334,7 @@ void IRAM_ATTR esp_restart_noos()
|
||||
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();
|
||||
|
||||
// Clear entry point for APP CPU
|
||||
DPORT_REG_WRITE(DPORT_APPCPU_CTRL_D_REG, 0);
|
||||
|
@@ -1,11 +1,12 @@
|
||||
|
||||
#include <esp_types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "esp_types.h"
|
||||
#include "esp_clk.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/xtensa_timer.h"
|
||||
#include "soc/cpu.h"
|
||||
#include "unity.h"
|
||||
#include "rom/uart.h"
|
||||
@@ -99,49 +100,52 @@ TEST_CASE("access DPORT and APB at same time", "[esp32]")
|
||||
{
|
||||
dport_test_result = false;
|
||||
apb_test_result = false;
|
||||
printf("CPU_FREQ = %d MHz\n", rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()) / MHZ);
|
||||
printf("CPU_FREQ = %d MHz\n", esp_clk_cpu_freq());
|
||||
run_tasks("accessDPORT", accessDPORT, "accessAPB", accessAPB, 10000);
|
||||
}
|
||||
|
||||
void run_tasks_with_change_freq_cpu (rtc_cpu_freq_t cpu_freq)
|
||||
void run_tasks_with_change_freq_cpu(int cpu_freq_mhz)
|
||||
{
|
||||
dport_test_result = false;
|
||||
apb_test_result = false;
|
||||
rtc_cpu_freq_t cur_freq = rtc_clk_cpu_freq_get();
|
||||
uint32_t freq_before_changed = rtc_clk_cpu_freq_value(cur_freq) / MHZ;
|
||||
uint32_t freq_changed = freq_before_changed;
|
||||
printf("CPU_FREQ = %d MHz\n", freq_before_changed);
|
||||
|
||||
if (cur_freq != cpu_freq) {
|
||||
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
|
||||
|
||||
rtc_clk_cpu_freq_set(cpu_freq);
|
||||
|
||||
const int uart_num = CONFIG_CONSOLE_UART_NUM;
|
||||
const int uart_baud = CONFIG_CONSOLE_UART_BAUDRATE;
|
||||
uart_div_modify(uart_num, (rtc_clk_apb_freq_get() << 4) / uart_baud);
|
||||
|
||||
freq_changed = rtc_clk_cpu_freq_value(rtc_clk_cpu_freq_get()) / MHZ;
|
||||
printf("CPU_FREQ switching to %d MHz\n", freq_changed);
|
||||
}
|
||||
run_tasks("accessDPORT", accessDPORT, "accessAPB", accessAPB, 10000 / ((freq_before_changed <= freq_changed) ? 1 : (freq_before_changed / freq_changed)));
|
||||
|
||||
// return old freq.
|
||||
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
|
||||
rtc_clk_cpu_freq_set(cur_freq);
|
||||
const int uart_num = CONFIG_CONSOLE_UART_NUM;
|
||||
const int uart_baud = CONFIG_CONSOLE_UART_BAUDRATE;
|
||||
dport_test_result = false;
|
||||
apb_test_result = false;
|
||||
rtc_cpu_freq_config_t old_config;
|
||||
rtc_clk_cpu_freq_get_config(&old_config);
|
||||
|
||||
printf("CPU_FREQ = %d MHz\n", old_config.freq_mhz);
|
||||
|
||||
if (cpu_freq_mhz != old_config.freq_mhz) {
|
||||
rtc_cpu_freq_config_t new_config;
|
||||
bool res = rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &new_config);
|
||||
assert(res && "invalid frequency value");
|
||||
|
||||
uart_tx_wait_idle(uart_num);
|
||||
rtc_clk_cpu_freq_set_config(&new_config);
|
||||
uart_div_modify(uart_num, (rtc_clk_apb_freq_get() << 4) / uart_baud);
|
||||
/* adjust RTOS ticks */
|
||||
_xt_tick_divisor = cpu_freq_mhz * 1000000 / XT_TICK_PER_SEC;
|
||||
vTaskDelay(2);
|
||||
|
||||
printf("CPU_FREQ switched to %d MHz\n", cpu_freq_mhz);
|
||||
}
|
||||
run_tasks("accessDPORT", accessDPORT, "accessAPB", accessAPB, 10000);
|
||||
|
||||
// return old freq.
|
||||
uart_tx_wait_idle(uart_num);
|
||||
rtc_clk_cpu_freq_set_config(&old_config);
|
||||
uart_div_modify(uart_num, (rtc_clk_apb_freq_get() << 4) / uart_baud);
|
||||
_xt_tick_divisor = old_config.freq_mhz * 1000000 / XT_TICK_PER_SEC;
|
||||
}
|
||||
|
||||
TEST_CASE("access DPORT and APB at same time (Freq CPU and APB = 80 MHz)", "[esp32] [ignore]")
|
||||
{
|
||||
run_tasks_with_change_freq_cpu(RTC_CPU_FREQ_80M);
|
||||
run_tasks_with_change_freq_cpu(80);
|
||||
}
|
||||
|
||||
TEST_CASE("access DPORT and APB at same time (Freq CPU and APB = 40 MHz (XTAL))", "[esp32]")
|
||||
{
|
||||
run_tasks_with_change_freq_cpu(RTC_CPU_FREQ_XTAL);
|
||||
run_tasks_with_change_freq_cpu((int) rtc_clk_xtal_freq_get());
|
||||
}
|
||||
|
||||
static uint32_t stall_other_cpu_counter;
|
||||
|
@@ -2,6 +2,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/param.h>
|
||||
#include "unity.h"
|
||||
#include "esp_pm.h"
|
||||
#include "esp_clk.h"
|
||||
@@ -25,19 +26,17 @@ TEST_CASE("Can dump power management lock stats", "[pm]")
|
||||
|
||||
static void switch_freq(int mhz)
|
||||
{
|
||||
rtc_cpu_freq_t max_freq;
|
||||
assert(rtc_clk_cpu_freq_from_mhz(mhz, &max_freq));
|
||||
int xtal_freq = rtc_clk_xtal_freq_get();
|
||||
esp_pm_config_esp32_t pm_config = {
|
||||
.max_cpu_freq = max_freq,
|
||||
.min_cpu_freq = RTC_CPU_FREQ_XTAL,
|
||||
.max_freq_mhz = mhz,
|
||||
.min_freq_mhz = MIN(mhz, xtal_freq),
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_pm_configure(&pm_config) );
|
||||
printf("Waiting for frequency to be set to %d (%d MHz)...\n", max_freq, mhz);
|
||||
printf("Waiting for frequency to be set to %d MHz...\n", mhz);
|
||||
while (esp_clk_cpu_freq() / 1000000 != mhz) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
printf("Frequency is %d MHz\n", esp_clk_cpu_freq());
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
printf("Frequency is %d MHz\n", esp_clk_cpu_freq() / 1000000);
|
||||
}
|
||||
printf("Frequency is set to %d MHz\n", mhz);
|
||||
}
|
||||
|
||||
TEST_CASE("Can switch frequency using esp_pm_configure", "[pm]")
|
||||
@@ -52,6 +51,10 @@ TEST_CASE("Can switch frequency using esp_pm_configure", "[pm]")
|
||||
switch_freq(240);
|
||||
switch_freq(40);
|
||||
switch_freq(80);
|
||||
switch_freq(10);
|
||||
switch_freq(80);
|
||||
switch_freq(20);
|
||||
switch_freq(40);
|
||||
switch_freq(orig_freq_mhz);
|
||||
}
|
||||
|
||||
|
@@ -177,13 +177,16 @@ TEST_CASE("light sleep and frequency switching", "[deepsleep]")
|
||||
uart_div_modify(CONFIG_CONSOLE_UART_NUM, (uart_clk_freq << 4) / CONFIG_CONSOLE_UART_BAUDRATE);
|
||||
#endif
|
||||
|
||||
rtc_cpu_freq_config_t config_xtal, config_default;
|
||||
rtc_clk_cpu_freq_get_config(&config_default);
|
||||
rtc_clk_cpu_freq_mhz_to_config((int) rtc_clk_xtal_freq_get(), &config_xtal);
|
||||
|
||||
esp_sleep_enable_timer_wakeup(1000);
|
||||
rtc_cpu_freq_t default_freq = rtc_clk_cpu_freq_get();
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
if (i % 2 == 0) {
|
||||
rtc_clk_cpu_freq_set_fast(RTC_CPU_FREQ_XTAL);
|
||||
rtc_clk_cpu_freq_set_config_fast(&config_xtal);
|
||||
} else {
|
||||
rtc_clk_cpu_freq_set_fast(default_freq);
|
||||
rtc_clk_cpu_freq_set_config_fast(&config_default);
|
||||
}
|
||||
printf("%d\n", i);
|
||||
fflush(stdout);
|
||||
|
@@ -4,3 +4,5 @@ SOC_NAME := esp32
|
||||
COMPONENT_SRCDIRS := $(SOC_NAME) src/
|
||||
|
||||
COMPONENT_ADD_INCLUDEDIRS := $(SOC_NAME)/include include
|
||||
|
||||
-include $(COMPONENT_PATH)/$(SOC_NAME)/component.mk
|
||||
|
1
components/soc/esp32/component.mk
Normal file
1
components/soc/esp32/component.mk
Normal file
@@ -0,0 +1 @@
|
||||
esp32/rtc_clk.o: CFLAGS += -fno-jump-tables -fno-tree-switch-conversion
|
@@ -75,6 +75,26 @@ typedef enum {
|
||||
RTC_CPU_FREQ_2M = 4, //!< 2 MHz
|
||||
} 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_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
|
||||
*/
|
||||
@@ -108,13 +128,13 @@ typedef enum {
|
||||
* Initialization parameters for rtc_clk_init
|
||||
*/
|
||||
typedef struct {
|
||||
rtc_xtal_freq_t xtal_freq : 8; //!< Main XTAL frequency
|
||||
rtc_cpu_freq_t cpu_freq : 3; //!< CPU frequency to set
|
||||
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)
|
||||
uint32_t slow_clk_dcap : 8; //!< RTC 150k clock adjustment parameter (higher value leads to lower frequency)
|
||||
uint32_t clk_8m_dfreq : 8; //!< RTC 8m clock adjustment parameter (higher value leads to higher frequency)
|
||||
rtc_xtal_freq_t xtal_freq : 8; //!< Main XTAL frequency
|
||||
rtc_cpu_freq_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)
|
||||
uint32_t slow_clk_dcap : 8; //!< RTC 150k clock adjustment parameter (higher value leads to lower frequency)
|
||||
uint32_t clk_8m_dfreq : 8; //!< RTC 8m clock adjustment parameter (higher value leads to higher frequency)
|
||||
} rtc_clk_config_t;
|
||||
|
||||
/**
|
||||
@@ -122,7 +142,7 @@ typedef struct {
|
||||
*/
|
||||
#define RTC_CLK_CONFIG_DEFAULT() { \
|
||||
.xtal_freq = RTC_XTAL_FREQ_AUTO, \
|
||||
.cpu_freq = RTC_CPU_FREQ_80M, \
|
||||
.cpu_freq_mhz = 80, \
|
||||
.fast_freq = RTC_FAST_FREQ_8M, \
|
||||
.slow_freq = RTC_SLOW_FREQ_RTC, \
|
||||
.clk_8m_div = 0, \
|
||||
@@ -281,6 +301,9 @@ rtc_fast_freq_t rtc_clk_fast_freq_get();
|
||||
/**
|
||||
* @brief Switch CPU frequency
|
||||
*
|
||||
* @note This function is deprecated and will be removed.
|
||||
* See rtc_clk_cpu_freq_config_set instead.
|
||||
*
|
||||
* 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
|
||||
@@ -288,11 +311,14 @@ rtc_fast_freq_t rtc_clk_fast_freq_get();
|
||||
*
|
||||
* @param cpu_freq new CPU frequency
|
||||
*/
|
||||
void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq);
|
||||
void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq) __attribute__((deprecated));
|
||||
|
||||
/**
|
||||
* @brief Switch CPU frequency
|
||||
*
|
||||
* @note This function is deprecated and will be removed.
|
||||
* See rtc_clk_cpu_freq_set_config_fast instead.
|
||||
*
|
||||
* 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).
|
||||
@@ -307,11 +333,14 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq);
|
||||
*
|
||||
* @param cpu_freq new CPU frequency
|
||||
*/
|
||||
void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq);
|
||||
void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq) __attribute__((deprecated));
|
||||
|
||||
/**
|
||||
* @brief Get the currently selected CPU frequency
|
||||
*
|
||||
* @note This function is deprecated and will be removed.
|
||||
* See rtc_clk_cpu_freq_get_config instead.
|
||||
*
|
||||
* 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
|
||||
@@ -320,22 +349,97 @@ void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq);
|
||||
*
|
||||
* @return CPU frequency (one of rtc_cpu_freq_t values)
|
||||
*/
|
||||
rtc_cpu_freq_t rtc_clk_cpu_freq_get();
|
||||
rtc_cpu_freq_t rtc_clk_cpu_freq_get() __attribute__((deprecated));
|
||||
|
||||
/**
|
||||
* @brief Get corresponding frequency value for rtc_cpu_freq_t enum value
|
||||
*
|
||||
* @note This function is deprecated and will be removed.
|
||||
* See rtc_clk_cpu_freq_get/set_config instead.
|
||||
*
|
||||
* @param cpu_freq CPU frequency, on of rtc_cpu_freq_t values
|
||||
* @return CPU frequency, in HZ
|
||||
*/
|
||||
uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq);
|
||||
uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq) __attribute__((deprecated));
|
||||
|
||||
/**
|
||||
* @brief Get rtc_cpu_freq_t enum value for given CPU frequency
|
||||
*
|
||||
* @note This function is deprecated and will be removed.
|
||||
* See rtc_clk_cpu_freq_mhz_to_config instead.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
bool rtc_clk_cpu_freq_from_mhz(int cpu_freq_mhz, rtc_cpu_freq_t* out_val);
|
||||
bool rtc_clk_cpu_freq_from_mhz(int cpu_freq_mhz, rtc_cpu_freq_t* out_val) __attribute__((deprecated));
|
||||
|
||||
/**
|
||||
* @brief Get CPU frequency config corresponding to a rtc_cpu_freq_t value
|
||||
* @param cpu_freq CPU frequency enumeration value
|
||||
* @param[out] out_config Output, CPU frequency configuration structure
|
||||
*/
|
||||
void rtc_clk_cpu_freq_to_config(rtc_cpu_freq_t cpu_freq, rtc_cpu_freq_config_t* out_config);
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t* out_config);
|
||||
|
||||
/**
|
||||
* @brief Switch CPU frequency
|
||||
*
|
||||
* This function sets CPU frequency according to the given configuration
|
||||
* structure. It enables PLLs, if necessary.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
void rtc_clk_cpu_freq_set_config(const rtc_cpu_freq_config_t* config);
|
||||
|
||||
/**
|
||||
* @brief Switch CPU frequency (optimized for speed)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
void rtc_clk_cpu_freq_set_config_fast(const rtc_cpu_freq_config_t* config);
|
||||
|
||||
/**
|
||||
* @brief Get the currently used CPU frequency configuration
|
||||
* @param[out] out_config Output, CPU frequency configuration structure
|
||||
*/
|
||||
void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t* out_config);
|
||||
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
void rtc_clk_cpu_freq_set_xtal();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Store new APB frequency value into RTC_APB_FREQ_REG
|
||||
|
@@ -16,6 +16,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "rom/ets_sys.h"
|
||||
#include "rom/rtc.h"
|
||||
#include "rom/uart.h"
|
||||
@@ -31,9 +32,7 @@
|
||||
#include "soc_log.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "xtensa/core-macros.h"
|
||||
|
||||
|
||||
#define MHZ (1000000)
|
||||
#include "rtc_clk_common.h"
|
||||
|
||||
/* Frequency of the 8M oscillator is 8.5MHz +/- 5%, at the default DCAP setting */
|
||||
#define RTC_FAST_CLK_FREQ_8M 8500000
|
||||
@@ -41,12 +40,6 @@
|
||||
#define RTC_SLOW_CLK_FREQ_8MD256 (RTC_FAST_CLK_FREQ_8M / 256)
|
||||
#define RTC_SLOW_CLK_FREQ_32K 32768
|
||||
|
||||
static const char* TAG = "rtc_clk";
|
||||
|
||||
/* Various constants related to the analog internals of the chip.
|
||||
* Defined here because they don't have any use outside of this file.
|
||||
*/
|
||||
|
||||
#define BBPLL_ENDIV5_VAL_320M 0x43
|
||||
#define BBPLL_BBADC_DSMP_VAL_320M 0x84
|
||||
#define BBPLL_ENDIV5_VAL_480M 0xc3
|
||||
@@ -80,11 +73,6 @@ static const char* TAG = "rtc_clk";
|
||||
#define DELAY_SLOW_CLK_SWITCH 300
|
||||
#define DELAY_8M_ENABLE 50
|
||||
|
||||
/* Number of 8M/256 clock cycles to use for XTAL frequency estimation.
|
||||
* 10 cycles will take approximately 300 microseconds.
|
||||
*/
|
||||
#define XTAL_FREQ_EST_CYCLES 10
|
||||
|
||||
/* Core voltage needs to be increased in two cases:
|
||||
* 1. running at 240 MHz
|
||||
* 2. running with 80MHz Flash frequency
|
||||
@@ -98,17 +86,17 @@ static const char* TAG = "rtc_clk";
|
||||
#define DIG_DBIAS_XTAL RTC_CNTL_DBIAS_1V10
|
||||
#define DIG_DBIAS_2M RTC_CNTL_DBIAS_1V00
|
||||
|
||||
/* 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;
|
||||
static void rtc_clk_cpu_freq_to_8m();
|
||||
static void rtc_clk_bbpll_disable();
|
||||
static void rtc_clk_bbpll_enable();
|
||||
static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz);
|
||||
static bool rtc_clk_cpu_freq_from_mhz_internal(int mhz, rtc_cpu_freq_t* out_val);
|
||||
|
||||
// Current PLL frequency, in MHZ (320 or 480). Zero if PLL is not enabled.
|
||||
static int s_cur_pll_freq;
|
||||
|
||||
static void rtc_clk_32k_enable_internal(int dac, int dres, int dbias)
|
||||
{
|
||||
@@ -275,7 +263,7 @@ rtc_fast_freq_t rtc_clk_fast_freq_get()
|
||||
return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL);
|
||||
}
|
||||
|
||||
void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq)
|
||||
void rtc_clk_bbpll_configure(rtc_xtal_freq_t xtal_freq, int pll_freq)
|
||||
{
|
||||
uint8_t div_ref;
|
||||
uint8_t div7_0;
|
||||
@@ -284,7 +272,7 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq)
|
||||
uint8_t dcur;
|
||||
uint8_t bw;
|
||||
|
||||
if (cpu_freq != RTC_CPU_FREQ_240M) {
|
||||
if (pll_freq == RTC_PLL_FREQ_320M) {
|
||||
/* Raise the voltage, if needed */
|
||||
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M);
|
||||
/* Configure 320M PLL */
|
||||
@@ -376,96 +364,47 @@ void rtc_clk_bbpll_set(rtc_xtal_freq_t xtal_freq, rtc_cpu_freq_t cpu_freq)
|
||||
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);
|
||||
s_cur_pll_freq = pll_freq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to XTAL frequency. Does not disable the PLL.
|
||||
*/
|
||||
static void rtc_clk_cpu_freq_to_xtal()
|
||||
void rtc_clk_cpu_freq_to_xtal(int freq, int div)
|
||||
{
|
||||
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(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, 0);
|
||||
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL);
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
int freq = 0;
|
||||
if (s_cur_pll == RTC_PLL_NONE ||
|
||||
(cpu_freq == RTC_CPU_FREQ_240M && s_cur_pll == RTC_PLL_320M) ||
|
||||
(cpu_freq != RTC_CPU_FREQ_240M && s_cur_pll == RTC_PLL_480M)) {
|
||||
/* need to switch PLLs, fall back to full implementation */
|
||||
rtc_clk_cpu_freq_set(cpu_freq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu_freq == RTC_CPU_FREQ_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) {
|
||||
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(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_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);
|
||||
rtc_clk_wait_for_slow_cycle();
|
||||
/* set divider from XTAL to APB clock */
|
||||
REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, div - 1);
|
||||
/* adjust ref_tick */
|
||||
REG_WRITE(APB_CTRL_XTAL_TICK_CONF_REG, freq * MHZ / REF_CLK_FREQ - 1);
|
||||
/* switch clock source */
|
||||
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL);
|
||||
DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0); /* clear DPORT_CPUPERIOD_SEL */
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
|
||||
static void rtc_clk_cpu_freq_to_8m()
|
||||
{
|
||||
rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get();
|
||||
/* Switch CPU to XTAL frequency first */
|
||||
ets_update_cpu_frequency(8);
|
||||
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL);
|
||||
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_XTL);
|
||||
REG_SET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT, 0);
|
||||
ets_update_cpu_frequency(xtal_freq);
|
||||
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_8M);
|
||||
DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, 0); // clear DPORT_CPUPERIOD_SEL
|
||||
rtc_clk_apb_freq_update(RTC_FAST_CLK_FREQ_8M);
|
||||
}
|
||||
|
||||
/* Frequency switch is synchronized to SLOW_CLK cycle. Wait until the switch
|
||||
* is complete before disabling the PLL.
|
||||
*/
|
||||
rtc_clk_wait_for_slow_cycle();
|
||||
|
||||
DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0);
|
||||
static void rtc_clk_bbpll_disable()
|
||||
{
|
||||
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);
|
||||
s_cur_pll_freq = 0;
|
||||
|
||||
/* is APLL under force power down? */
|
||||
uint32_t apll_fpd = REG_GET_FIELD(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD);
|
||||
@@ -473,76 +412,73 @@ void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
|
||||
/* then also power down the internal I2C bus */
|
||||
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(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_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);
|
||||
}
|
||||
|
||||
static void rtc_clk_bbpll_enable()
|
||||
{
|
||||
CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG,
|
||||
RTC_CNTL_BIAS_I2C_FORCE_PD | RTC_CNTL_BB_I2C_FORCE_PD |
|
||||
RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to one of PLL-based frequencies. Current frequency can be XTAL or PLL.
|
||||
* PLL must already be enabled.
|
||||
* @param cpu_freq new CPU frequency
|
||||
*/
|
||||
static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz)
|
||||
{
|
||||
int dbias = DIG_DBIAS_80M_160M;
|
||||
int per_conf = 0;
|
||||
if (cpu_freq_mhz == 80) {
|
||||
/* nothing to do */
|
||||
} else if (cpu_freq_mhz == 160) {
|
||||
per_conf = 1;
|
||||
} else if (cpu_freq_mhz == 240) {
|
||||
dbias = DIG_DBIAS_240M;
|
||||
per_conf = 2;
|
||||
} else {
|
||||
/* use PLL as clock source */
|
||||
CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG,
|
||||
RTC_CNTL_BIAS_I2C_FORCE_PD | RTC_CNTL_BB_I2C_FORCE_PD |
|
||||
RTC_CNTL_BBPLL_FORCE_PD | RTC_CNTL_BBPLL_I2C_FORCE_PD);
|
||||
rtc_clk_bbpll_set(xtal_freq, cpu_freq);
|
||||
if (cpu_freq == RTC_CPU_FREQ_80M) {
|
||||
DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 0);
|
||||
ets_update_cpu_frequency(80);
|
||||
s_cur_pll = RTC_PLL_320M;
|
||||
} else if (cpu_freq == RTC_CPU_FREQ_160M) {
|
||||
DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 1);
|
||||
ets_update_cpu_frequency(160);
|
||||
s_cur_pll = RTC_PLL_320M;
|
||||
} else if (cpu_freq == RTC_CPU_FREQ_240M) {
|
||||
DPORT_REG_SET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL, 2);
|
||||
ets_update_cpu_frequency(240);
|
||||
s_cur_pll = RTC_PLL_480M;
|
||||
}
|
||||
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL);
|
||||
rtc_clk_wait_for_slow_cycle();
|
||||
rtc_clk_apb_freq_update(80 * MHZ);
|
||||
assert(false && "invalid frequency");
|
||||
}
|
||||
s_cur_freq = cpu_freq;
|
||||
DPORT_REG_WRITE(DPORT_CPU_PER_CONF_REG, per_conf);
|
||||
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, dbias);
|
||||
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL, RTC_CNTL_SOC_CLK_SEL_PLL);
|
||||
rtc_clk_apb_freq_update(80 * MHZ);
|
||||
ets_update_cpu_frequency(cpu_freq_mhz);
|
||||
rtc_clk_wait_for_slow_cycle();
|
||||
}
|
||||
|
||||
|
||||
void rtc_clk_cpu_freq_set(rtc_cpu_freq_t cpu_freq)
|
||||
{
|
||||
rtc_cpu_freq_config_t config;
|
||||
rtc_clk_cpu_freq_to_config(cpu_freq, &config);
|
||||
rtc_clk_cpu_freq_set_config(&config);
|
||||
}
|
||||
|
||||
void rtc_clk_cpu_freq_set_fast(rtc_cpu_freq_t cpu_freq)
|
||||
{
|
||||
rtc_cpu_freq_config_t config;
|
||||
rtc_clk_cpu_freq_to_config(cpu_freq, &config);
|
||||
rtc_clk_cpu_freq_set_config_fast(&config);
|
||||
}
|
||||
|
||||
void rtc_clk_cpu_freq_set_xtal()
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
rtc_cpu_freq_t rtc_clk_cpu_freq_get()
|
||||
{
|
||||
uint32_t soc_clk_sel = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL);
|
||||
switch (soc_clk_sel) {
|
||||
case RTC_CNTL_SOC_CLK_SEL_XTL: {
|
||||
uint32_t pre_div = REG_GET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT);
|
||||
if (pre_div == 0) {
|
||||
return RTC_CPU_FREQ_XTAL;
|
||||
} else if (pre_div == rtc_clk_xtal_freq_get() / 2 - 1) {
|
||||
return RTC_CPU_FREQ_2M;
|
||||
} else {
|
||||
assert(false && "unsupported frequency");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RTC_CNTL_SOC_CLK_SEL_PLL: {
|
||||
uint32_t cpuperiod_sel = DPORT_REG_GET_FIELD(DPORT_CPU_PER_CONF_REG, DPORT_CPUPERIOD_SEL);
|
||||
if (cpuperiod_sel == 0) {
|
||||
return RTC_CPU_FREQ_80M;
|
||||
} else if (cpuperiod_sel == 1) {
|
||||
return RTC_CPU_FREQ_160M;
|
||||
} else if (cpuperiod_sel == 2) {
|
||||
return RTC_CPU_FREQ_240M;
|
||||
} else {
|
||||
assert(false && "unsupported frequency");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RTC_CNTL_SOC_CLK_SEL_APLL:
|
||||
case RTC_CNTL_SOC_CLK_SEL_8M:
|
||||
default:
|
||||
assert(false && "unsupported frequency");
|
||||
}
|
||||
return RTC_CNTL_SOC_CLK_SEL_XTL;
|
||||
rtc_cpu_freq_config_t config;
|
||||
rtc_clk_cpu_freq_get_config(&config);
|
||||
rtc_cpu_freq_t freq;
|
||||
rtc_clk_cpu_freq_from_mhz_internal(config.freq_mhz, &freq);
|
||||
return freq;
|
||||
}
|
||||
|
||||
uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq)
|
||||
@@ -564,7 +500,7 @@ uint32_t rtc_clk_cpu_freq_value(rtc_cpu_freq_t cpu_freq)
|
||||
}
|
||||
}
|
||||
|
||||
bool rtc_clk_cpu_freq_from_mhz(int mhz, rtc_cpu_freq_t* out_val)
|
||||
static bool rtc_clk_cpu_freq_from_mhz_internal(int mhz, rtc_cpu_freq_t* out_val)
|
||||
{
|
||||
if (mhz == 240) {
|
||||
*out_val = RTC_CPU_FREQ_240M;
|
||||
@@ -582,22 +518,197 @@ bool rtc_clk_cpu_freq_from_mhz(int mhz, rtc_cpu_freq_t* out_val)
|
||||
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.
|
||||
*/
|
||||
static bool clk_val_is_valid(uint32_t val) {
|
||||
return (val & 0xffff) == ((val >> 16) & 0xffff) &&
|
||||
val != 0 &&
|
||||
val != UINT32_MAX;
|
||||
bool rtc_clk_cpu_freq_from_mhz(int mhz, rtc_cpu_freq_t* out_val)
|
||||
{
|
||||
return rtc_clk_cpu_freq_from_mhz_internal(mhz, out_val);
|
||||
}
|
||||
|
||||
static uint32_t reg_val_to_clk_val(uint32_t val) {
|
||||
return val & UINT16_MAX;
|
||||
void rtc_clk_cpu_freq_to_config(rtc_cpu_freq_t cpu_freq, rtc_cpu_freq_config_t* out_config)
|
||||
{
|
||||
uint32_t source_freq_mhz;
|
||||
rtc_cpu_freq_src_t source;
|
||||
uint32_t freq_mhz;
|
||||
uint32_t divider;
|
||||
|
||||
switch (cpu_freq) {
|
||||
case RTC_CPU_FREQ_XTAL:
|
||||
case RTC_CPU_FREQ_2M:
|
||||
source_freq_mhz = rtc_clk_xtal_freq_get();
|
||||
source = RTC_CPU_FREQ_SRC_XTAL;
|
||||
if (cpu_freq == RTC_CPU_FREQ_2M) {
|
||||
freq_mhz = 2;
|
||||
divider = out_config->source_freq_mhz / 2;
|
||||
} else {
|
||||
freq_mhz = source_freq_mhz;
|
||||
divider = 1;
|
||||
}
|
||||
break;
|
||||
case RTC_CPU_FREQ_80M:
|
||||
source = RTC_CPU_FREQ_SRC_PLL;
|
||||
source_freq_mhz = RTC_PLL_FREQ_320M;
|
||||
divider = 4;
|
||||
freq_mhz = 80;
|
||||
break;
|
||||
case RTC_CPU_FREQ_160M:
|
||||
source = RTC_CPU_FREQ_SRC_PLL;
|
||||
source_freq_mhz = RTC_PLL_FREQ_320M;
|
||||
divider = 2;
|
||||
freq_mhz = 160;
|
||||
break;
|
||||
case RTC_CPU_FREQ_240M:
|
||||
source = RTC_CPU_FREQ_SRC_PLL;
|
||||
source_freq_mhz = RTC_PLL_FREQ_480M;
|
||||
divider = 2;
|
||||
freq_mhz = 240;
|
||||
break;
|
||||
default:
|
||||
assert(false && "invalid rtc_cpu_freq_t value");
|
||||
abort();
|
||||
}
|
||||
|
||||
*out_config = (rtc_cpu_freq_config_t) {
|
||||
.source = source,
|
||||
.source_freq_mhz = source_freq_mhz,
|
||||
.div = divider,
|
||||
.freq_mhz = freq_mhz
|
||||
};
|
||||
}
|
||||
|
||||
static uint32_t clk_val_to_reg_val(uint32_t val) {
|
||||
return (val & UINT16_MAX) | ((val & UINT16_MAX) << 16);
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_320M;
|
||||
divider = 4;
|
||||
} else if (freq_mhz == 160) {
|
||||
real_freq_mhz = freq_mhz;
|
||||
source = RTC_CPU_FREQ_SRC_PLL;
|
||||
source_freq_mhz = RTC_PLL_FREQ_320M;
|
||||
divider = 2;
|
||||
} 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;
|
||||
}
|
||||
|
||||
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(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL);
|
||||
if (soc_clk_sel != RTC_CNTL_SOC_CLK_SEL_XTL) {
|
||||
rtc_clk_cpu_freq_to_xtal(xtal_freq, 1);
|
||||
rtc_clk_wait_for_slow_cycle();
|
||||
}
|
||||
if (soc_clk_sel == RTC_CNTL_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_wait_for_slow_cycle();
|
||||
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(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL);
|
||||
switch (soc_clk_sel) {
|
||||
case RTC_CNTL_SOC_CLK_SEL_XTL: {
|
||||
source = RTC_CPU_FREQ_SRC_XTAL;
|
||||
div = REG_GET_FIELD(APB_CTRL_SYSCLK_CONF_REG, APB_CTRL_PRE_DIV_CNT) + 1;
|
||||
source_freq_mhz = (uint32_t) rtc_clk_xtal_freq_get();
|
||||
freq_mhz = source_freq_mhz / div;
|
||||
}
|
||||
break;
|
||||
case RTC_CNTL_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);
|
||||
if (cpuperiod_sel == 0) {
|
||||
source_freq_mhz = RTC_PLL_FREQ_320M;
|
||||
div = 4;
|
||||
freq_mhz = 80;
|
||||
} else if (cpuperiod_sel == 1) {
|
||||
source_freq_mhz = RTC_PLL_FREQ_320M;
|
||||
div = 2;
|
||||
freq_mhz = 160;
|
||||
} else if (cpuperiod_sel == 2) {
|
||||
source_freq_mhz = RTC_PLL_FREQ_480M;
|
||||
div = 2;
|
||||
freq_mhz = 240;
|
||||
} else {
|
||||
assert(false && "unsupported frequency configuration");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RTC_CNTL_SOC_CLK_SEL_8M:
|
||||
source = RTC_CPU_FREQ_SRC_8M;
|
||||
source_freq_mhz = 8;
|
||||
div = 1;
|
||||
freq_mhz = source_freq_mhz;
|
||||
break;
|
||||
case RTC_CNTL_SOC_CLK_SEL_APLL:
|
||||
default:
|
||||
assert(false && "unsupported frequency configuration");
|
||||
}
|
||||
*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);
|
||||
}
|
||||
}
|
||||
|
||||
rtc_xtal_freq_t rtc_clk_xtal_freq_get()
|
||||
@@ -605,7 +716,6 @@ rtc_xtal_freq_t rtc_clk_xtal_freq_get()
|
||||
/* We may have already written XTAL value into RTC_XTAL_FREQ_REG */
|
||||
uint32_t xtal_freq_reg = READ_PERI_REG(RTC_XTAL_FREQ_REG);
|
||||
if (!clk_val_is_valid(xtal_freq_reg)) {
|
||||
SOC_LOGW(TAG, "invalid RTC_XTAL_FREQ_REG value: 0x%08x", xtal_freq_reg);
|
||||
return RTC_XTAL_FREQ_AUTO;
|
||||
}
|
||||
return reg_val_to_clk_val(xtal_freq_reg);
|
||||
@@ -616,42 +726,6 @@ void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq)
|
||||
WRITE_PERI_REG(RTC_XTAL_FREQ_REG, clk_val_to_reg_val(xtal_freq));
|
||||
}
|
||||
|
||||
static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate()
|
||||
{
|
||||
/* Enable 8M/256 clock if needed */
|
||||
const bool clk_8m_enabled = rtc_clk_8m_enabled();
|
||||
const bool clk_8md256_enabled = rtc_clk_8md256_enabled();
|
||||
if (!clk_8md256_enabled) {
|
||||
rtc_clk_8m_enable(true, true);
|
||||
}
|
||||
|
||||
uint64_t cal_val = rtc_clk_cal_ratio(RTC_CAL_8MD256, XTAL_FREQ_EST_CYCLES);
|
||||
/* cal_val contains period of 8M/256 clock in XTAL clock cycles
|
||||
* (shifted by RTC_CLK_CAL_FRACT bits).
|
||||
* Xtal frequency will be (cal_val * 8M / 256) / 2^19
|
||||
*/
|
||||
uint32_t freq_mhz = (cal_val * RTC_FAST_CLK_FREQ_APPROX / MHZ / 256 ) >> RTC_CLK_CAL_FRACT;
|
||||
/* Guess the XTAL type. For now, only 40 and 26MHz are supported.
|
||||
*/
|
||||
switch (freq_mhz) {
|
||||
case 21 ... 31:
|
||||
return RTC_XTAL_FREQ_26M;
|
||||
case 32 ... 33:
|
||||
SOC_LOGW(TAG, "Potentially bogus XTAL frequency: %d MHz, guessing 26 MHz", freq_mhz);
|
||||
return RTC_XTAL_FREQ_26M;
|
||||
case 34 ... 35:
|
||||
SOC_LOGW(TAG, "Potentially bogus XTAL frequency: %d MHz, guessing 40 MHz", freq_mhz);
|
||||
return RTC_XTAL_FREQ_40M;
|
||||
case 36 ... 45:
|
||||
return RTC_XTAL_FREQ_40M;
|
||||
default:
|
||||
SOC_LOGW(TAG, "Bogus XTAL frequency: %d MHz", freq_mhz);
|
||||
return RTC_XTAL_FREQ_AUTO;
|
||||
}
|
||||
/* Restore 8M and 8md256 clocks to original state */
|
||||
rtc_clk_8m_enable(clk_8m_enabled, clk_8md256_enabled);
|
||||
}
|
||||
|
||||
void rtc_clk_apb_freq_update(uint32_t apb_freq)
|
||||
{
|
||||
WRITE_PERI_REG(RTC_APB_FREQ_REG, clk_val_to_reg_val(apb_freq >> 12));
|
||||
@@ -666,90 +740,6 @@ uint32_t rtc_clk_apb_freq_get()
|
||||
return freq_hz - remainder;
|
||||
}
|
||||
|
||||
|
||||
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 RTC_CNTL_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(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL) == RTC_CNTL_SOC_CLK_SEL_PLL) {
|
||||
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 8M clock division */
|
||||
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, cfg.clk_8m_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);
|
||||
|
||||
/* Estimate XTAL frequency */
|
||||
rtc_xtal_freq_t xtal_freq = cfg.xtal_freq;
|
||||
if (xtal_freq == RTC_XTAL_FREQ_AUTO) {
|
||||
if (clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) {
|
||||
/* XTAL frequency has already been set, use existing value */
|
||||
xtal_freq = rtc_clk_xtal_freq_get();
|
||||
} else {
|
||||
/* Not set yet, estimate XTAL frequency based on RTC_FAST_CLK */
|
||||
xtal_freq = rtc_clk_xtal_freq_estimate();
|
||||
if (xtal_freq == RTC_XTAL_FREQ_AUTO) {
|
||||
SOC_LOGW(TAG, "Can't estimate XTAL frequency, assuming 26MHz");
|
||||
xtal_freq = RTC_XTAL_FREQ_26M;
|
||||
}
|
||||
}
|
||||
} else if (!clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) {
|
||||
/* Exact frequency was set in sdkconfig, but still warn if autodetected
|
||||
* frequency is different. If autodetection failed, worst case we get a
|
||||
* bit of garbage output.
|
||||
*/
|
||||
rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate();
|
||||
if (est_xtal_freq != xtal_freq) {
|
||||
SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.",
|
||||
xtal_freq, est_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
|
||||
*/
|
||||
|
38
components/soc/esp32/rtc_clk_common.h
Normal file
38
components/soc/esp32/rtc_clk_common.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2015-2018 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)
|
||||
|
||||
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);
|
||||
}
|
||||
|
176
components/soc/esp32/rtc_clk_init.c
Normal file
176
components/soc/esp32/rtc_clk_init.c
Normal file
@@ -0,0 +1,176 @@
|
||||
// Copyright 2015-2018 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 <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "rom/ets_sys.h"
|
||||
#include "rom/rtc.h"
|
||||
#include "rom/uart.h"
|
||||
#include "rom/gpio.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/rtc_io_reg.h"
|
||||
#include "soc/sens_reg.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#include "soc/efuse_reg.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"
|
||||
|
||||
/* Number of 8M/256 clock cycles to use for XTAL frequency estimation.
|
||||
* 10 cycles will take approximately 300 microseconds.
|
||||
*/
|
||||
#define XTAL_FREQ_EST_CYCLES 10
|
||||
|
||||
static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate();
|
||||
|
||||
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;
|
||||
|
||||
/* 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 RTC_CNTL_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(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_SOC_CLK_SEL) == RTC_CNTL_SOC_CLK_SEL_PLL) {
|
||||
/* We don't know actual XTAL frequency yet, assume 40MHz.
|
||||
* REF_TICK divider will be corrected below, once XTAL frequency is
|
||||
* determined.
|
||||
*/
|
||||
rtc_clk_cpu_freq_to_xtal(40, 1);
|
||||
}
|
||||
|
||||
/* 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 8M clock division */
|
||||
REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, cfg.clk_8m_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);
|
||||
|
||||
/* Estimate XTAL frequency */
|
||||
rtc_xtal_freq_t xtal_freq = cfg.xtal_freq;
|
||||
if (xtal_freq == RTC_XTAL_FREQ_AUTO) {
|
||||
if (clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) {
|
||||
/* XTAL frequency has already been set, use existing value */
|
||||
xtal_freq = rtc_clk_xtal_freq_get();
|
||||
} else {
|
||||
/* Not set yet, estimate XTAL frequency based on RTC_FAST_CLK */
|
||||
xtal_freq = rtc_clk_xtal_freq_estimate();
|
||||
if (xtal_freq == RTC_XTAL_FREQ_AUTO) {
|
||||
SOC_LOGW(TAG, "Can't estimate XTAL frequency, assuming 26MHz");
|
||||
xtal_freq = RTC_XTAL_FREQ_26M;
|
||||
}
|
||||
}
|
||||
} else if (!clk_val_is_valid(READ_PERI_REG(RTC_XTAL_FREQ_REG))) {
|
||||
/* Exact frequency was set in sdkconfig, but still warn if autodetected
|
||||
* frequency is different. If autodetection failed, worst case we get a
|
||||
* bit of garbage output.
|
||||
*/
|
||||
|
||||
rtc_xtal_freq_t est_xtal_freq = rtc_clk_xtal_freq_estimate();
|
||||
if (est_xtal_freq != xtal_freq) {
|
||||
SOC_LOGW(TAG, "Possibly invalid CONFIG_ESP32_XTAL_FREQ setting (%dMHz). Detected %d MHz.",
|
||||
xtal_freq, est_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);
|
||||
assert(res && "invalid CPU frequency value");
|
||||
|
||||
/* Configure REF_TICK */
|
||||
REG_WRITE(APB_CTRL_XTAL_TICK_CONF_REG, xtal_freq - 1);
|
||||
REG_WRITE(APB_CTRL_PLL_TICK_CONF_REG, APB_CLK_FREQ / MHZ - 1); /* Under PLL, APB frequency is always 80MHz */
|
||||
|
||||
/* Re-calculate the ccount to make time calculation correct. */
|
||||
XTHAL_SET_CCOUNT( 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);
|
||||
}
|
||||
|
||||
static rtc_xtal_freq_t rtc_clk_xtal_freq_estimate()
|
||||
{
|
||||
/* Enable 8M/256 clock if needed */
|
||||
const bool clk_8m_enabled = rtc_clk_8m_enabled();
|
||||
const bool clk_8md256_enabled = rtc_clk_8md256_enabled();
|
||||
if (!clk_8md256_enabled) {
|
||||
rtc_clk_8m_enable(true, true);
|
||||
}
|
||||
|
||||
uint64_t cal_val = rtc_clk_cal_ratio(RTC_CAL_8MD256, XTAL_FREQ_EST_CYCLES);
|
||||
/* cal_val contains period of 8M/256 clock in XTAL clock cycles
|
||||
* (shifted by RTC_CLK_CAL_FRACT bits).
|
||||
* Xtal frequency will be (cal_val * 8M / 256) / 2^19
|
||||
*/
|
||||
uint32_t freq_mhz = (cal_val * RTC_FAST_CLK_FREQ_APPROX / MHZ / 256 ) >> RTC_CLK_CAL_FRACT;
|
||||
/* Guess the XTAL type. For now, only 40 and 26MHz are supported.
|
||||
*/
|
||||
switch (freq_mhz) {
|
||||
case 21 ... 31:
|
||||
return RTC_XTAL_FREQ_26M;
|
||||
case 32 ... 33:
|
||||
SOC_LOGW(TAG, "Potentially bogus XTAL frequency: %d MHz, guessing 26 MHz", freq_mhz);
|
||||
return RTC_XTAL_FREQ_26M;
|
||||
case 34 ... 35:
|
||||
SOC_LOGW(TAG, "Potentially bogus XTAL frequency: %d MHz, guessing 40 MHz", freq_mhz);
|
||||
return RTC_XTAL_FREQ_40M;
|
||||
case 36 ... 45:
|
||||
return RTC_XTAL_FREQ_40M;
|
||||
default:
|
||||
SOC_LOGW(TAG, "Bogus XTAL frequency: %d MHz", freq_mhz);
|
||||
return RTC_XTAL_FREQ_AUTO;
|
||||
}
|
||||
/* Restore 8M and 8md256 clocks to original state */
|
||||
rtc_clk_8m_enable(clk_8m_enabled, clk_8md256_enabled);
|
||||
}
|
@@ -95,7 +95,7 @@ TEST_CASE("Output 8M XTAL clock to GPIO25", "[rtc_clk][ignore]")
|
||||
pull_out_clk(RTC_IO_DEBUG_SEL0_8M);
|
||||
}
|
||||
|
||||
static void test_clock_switching(void (*switch_func)(rtc_cpu_freq_t))
|
||||
static void test_clock_switching(void (*switch_func)(const rtc_cpu_freq_config_t* config))
|
||||
{
|
||||
uart_tx_wait_idle(CONFIG_CONSOLE_UART_NUM);
|
||||
|
||||
@@ -103,11 +103,16 @@ static void test_clock_switching(void (*switch_func)(rtc_cpu_freq_t))
|
||||
ref_clock_init();
|
||||
uint64_t t_start = ref_clock_get();
|
||||
|
||||
rtc_cpu_freq_t cur_freq = rtc_clk_cpu_freq_get();
|
||||
rtc_cpu_freq_config_t cur_config;
|
||||
rtc_clk_cpu_freq_get_config(&cur_config);
|
||||
|
||||
rtc_cpu_freq_config_t xtal_config;
|
||||
rtc_clk_cpu_freq_mhz_to_config((uint32_t) rtc_clk_xtal_freq_get(), &xtal_config);
|
||||
|
||||
int count = 0;
|
||||
while (ref_clock_get() - t_start < test_duration_sec * 1000000) {
|
||||
switch_func(RTC_CPU_FREQ_XTAL);
|
||||
switch_func(cur_freq);
|
||||
switch_func(&xtal_config);
|
||||
switch_func(&cur_config);
|
||||
++count;
|
||||
}
|
||||
uint64_t t_end = ref_clock_get();
|
||||
@@ -126,12 +131,12 @@ TEST_CASE("Calculate 8M clock frequency", "[rtc_clk]")
|
||||
|
||||
TEST_CASE("Test switching between PLL and XTAL", "[rtc_clk]")
|
||||
{
|
||||
test_clock_switching(rtc_clk_cpu_freq_set);
|
||||
test_clock_switching(rtc_clk_cpu_freq_set_config);
|
||||
}
|
||||
|
||||
TEST_CASE("Test fast switching between PLL and XTAL", "[rtc_clk]")
|
||||
{
|
||||
test_clock_switching(rtc_clk_cpu_freq_set_fast);
|
||||
test_clock_switching(rtc_clk_cpu_freq_set_config_fast);
|
||||
}
|
||||
|
||||
#define COUNT_TEST 3
|
||||
|
Reference in New Issue
Block a user