soc/rtc: update frequency switching APIs to match the master branch

esp32s2 code was based in IDF v3.1, and used outdated APIs.

Closes IDF-670
This commit is contained in:
Ivan Grokhotkov 2020-02-12 13:56:59 +01:00
parent d2d3269159
commit 74ac618287
11 changed files with 433 additions and 428 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,10 @@ 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
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);

View file

@ -86,35 +86,22 @@ 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)

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

@ -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

@ -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, \
@ -415,63 +435,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)
{
@ -120,25 +119,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 +207,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 +231,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 +243,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 +274,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 +526,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);
}