kopia lustrzana https://github.com/espressif/esp-idf
588 wiersze
20 KiB
C
588 wiersze
20 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include "soc/rtc.h"
|
|
#include "soc/rtc_periph.h"
|
|
#include "soc/sens_periph.h"
|
|
#include "soc/soc_caps.h"
|
|
#include "soc/chip_revision.h"
|
|
#include "hal/efuse_ll.h"
|
|
#include "hal/efuse_hal.h"
|
|
#include "soc/gpio_struct.h"
|
|
#include "hal/gpio_ll.h"
|
|
#include "esp_hw_log.h"
|
|
#include "sdkconfig.h"
|
|
#include "esp_rom_sys.h"
|
|
#include "esp_rom_gpio.h"
|
|
#include "esp32/rom/ets_sys.h" // for ets_update_cpu_frequency
|
|
#include "esp32/rom/rtc.h"
|
|
#include "hal/clk_tree_ll.h"
|
|
#include "soc/rtc_cntl_reg.h"
|
|
#include "soc/io_mux_reg.h"
|
|
|
|
#define XTAL_32K_BOOTSTRAP_TIME_US 7
|
|
|
|
static void rtc_clk_cpu_freq_to_8m(void);
|
|
static void rtc_clk_cpu_freq_to_pll_mhz(int cpu_freq_mhz);
|
|
|
|
// Current PLL frequency, in MHZ (320 or 480). Zero if PLL is not enabled.
|
|
static uint32_t s_cur_pll_freq;
|
|
|
|
static const char* TAG = "rtc_clk";
|
|
|
|
static void rtc_clk_32k_enable_common(clk_ll_xtal32k_enable_mode_t mode)
|
|
{
|
|
CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG,
|
|
RTC_IO_X32P_RDE | RTC_IO_X32P_RUE | RTC_IO_X32N_RUE |
|
|
RTC_IO_X32N_RDE | RTC_IO_X32N_FUN_IE | RTC_IO_X32P_FUN_IE);
|
|
SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
|
|
|
|
#ifdef CONFIG_RTC_EXT_CRYST_ADDIT_CURRENT
|
|
// version0 and version1 need provide additional current to external XTAL.
|
|
if (!ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 200)) {
|
|
/* TOUCH sensor can provide additional current to external XTAL.
|
|
In some case, X32N and X32P PAD don't have enough drive capability to start XTAL */
|
|
SET_PERI_REG_MASK(RTC_IO_TOUCH_CFG_REG, RTC_IO_TOUCH_XPD_BIAS_M);
|
|
/* Tie PAD Touch8 to VDD
|
|
NOTE: TOUCH8 and TOUCH9 register settings are reversed except for DAC, so we set RTC_IO_TOUCH_PAD9_REG here instead*/
|
|
SET_PERI_REG_MASK(RTC_IO_TOUCH_PAD9_REG, RTC_IO_TOUCH_PAD9_TIE_OPT_M);
|
|
/* Set the current used to compensate TOUCH PAD8 */
|
|
SET_PERI_REG_BITS(RTC_IO_TOUCH_PAD8_REG, RTC_IO_TOUCH_PAD8_DAC, 4, RTC_IO_TOUCH_PAD8_DAC_S);
|
|
/* Power up TOUCH8
|
|
So the Touch DAC start to drive some current from VDD to TOUCH8(which is also XTAL-N)*/
|
|
SET_PERI_REG_MASK(RTC_IO_TOUCH_PAD9_REG, RTC_IO_TOUCH_PAD9_XPD_M);
|
|
}
|
|
#elif defined CONFIG_RTC_EXT_CRYST_ADDIT_CURRENT_V2
|
|
if (!ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 200)) {
|
|
/* TOUCH sensor can provide additional current to external XTAL.
|
|
In some case, X32N and X32P PAD don't have enough drive capability to start XTAL */
|
|
SET_PERI_REG_MASK(RTC_IO_TOUCH_CFG_REG, RTC_IO_TOUCH_XPD_BIAS_M);
|
|
SET_PERI_REG_BITS(RTC_IO_TOUCH_CFG_REG, RTC_IO_TOUCH_DCUR, 3, RTC_IO_TOUCH_DCUR_S);
|
|
CLEAR_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_START_FSM_EN_M);
|
|
/* Tie PAD Touch8 to VDD
|
|
NOTE: TOUCH8 and TOUCH9 register settings are reversed except for DAC, so we set RTC_IO_TOUCH_PAD9_REG here instead
|
|
*/
|
|
SET_PERI_REG_MASK(RTC_IO_TOUCH_PAD9_REG, RTC_IO_TOUCH_PAD9_TIE_OPT_M);
|
|
/* Set the current used to compensate TOUCH PAD8 */
|
|
SET_PERI_REG_BITS(RTC_IO_TOUCH_PAD8_REG, RTC_IO_TOUCH_PAD8_DAC, 1, RTC_IO_TOUCH_PAD8_DAC_S);
|
|
/* Power up TOUCH8
|
|
So the Touch DAC start to drive some current from VDD to TOUCH8(which is also XTAL-N)
|
|
*/
|
|
SET_PERI_REG_MASK(RTC_IO_TOUCH_PAD9_REG, RTC_IO_TOUCH_PAD9_XPD_M);
|
|
CLEAR_PERI_REG_MASK(RTC_IO_TOUCH_PAD9_REG, RTC_IO_TOUCH_PAD9_START_M);
|
|
}
|
|
#endif
|
|
|
|
clk_ll_xtal32k_enable(mode);
|
|
}
|
|
|
|
void rtc_clk_32k_enable(bool enable)
|
|
{
|
|
if (enable) {
|
|
rtc_clk_32k_enable_common(CLK_LL_XTAL32K_ENABLE_MODE_CRYSTAL);
|
|
} else {
|
|
clk_ll_xtal32k_disable();
|
|
/* Disable X32N and X32P pad drive external xtal */
|
|
CLEAR_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32N_MUX_SEL | RTC_IO_X32P_MUX_SEL);
|
|
|
|
#ifdef CONFIG_RTC_EXT_CRYST_ADDIT_CURRENT
|
|
if (!ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 200)) {
|
|
/* Power down TOUCH */
|
|
CLEAR_PERI_REG_MASK(RTC_IO_TOUCH_PAD9_REG, RTC_IO_TOUCH_PAD9_XPD_M);
|
|
}
|
|
#elif defined CONFIG_RTC_EXT_CRYST_ADDIT_CURRENT_V2
|
|
if (!ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 200)) {
|
|
/* Power down TOUCH */
|
|
CLEAR_PERI_REG_MASK(RTC_IO_TOUCH_CFG_REG, RTC_IO_TOUCH_XPD_BIAS_M);
|
|
SET_PERI_REG_BITS(RTC_IO_TOUCH_CFG_REG, RTC_IO_TOUCH_DCUR, 0, RTC_IO_TOUCH_DCUR_S);
|
|
CLEAR_PERI_REG_MASK(RTC_IO_TOUCH_PAD9_REG, RTC_IO_TOUCH_PAD9_XPD_M);
|
|
SET_PERI_REG_MASK(SENS_SAR_TOUCH_CTRL2_REG, SENS_TOUCH_START_FSM_EN_M);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void rtc_clk_32k_enable_external(void)
|
|
{
|
|
rtc_clk_32k_enable_common(CLK_LL_XTAL32K_ENABLE_MODE_EXTERNAL);
|
|
}
|
|
|
|
/* Helping external 32kHz crystal to start up.
|
|
* External crystal connected to outputs GPIO32 GPIO33.
|
|
* Forms N pulses with a frequency of about 32KHz on the outputs of the crystal.
|
|
*/
|
|
void rtc_clk_32k_bootstrap(uint32_t cycle)
|
|
{
|
|
if (cycle){
|
|
esp_rom_gpio_pad_select_gpio(XTAL32K_P_GPIO_NUM);
|
|
esp_rom_gpio_pad_select_gpio(XTAL32K_N_GPIO_NUM);
|
|
gpio_ll_output_enable(&GPIO, XTAL32K_P_GPIO_NUM);
|
|
gpio_ll_output_enable(&GPIO, XTAL32K_N_GPIO_NUM);
|
|
gpio_ll_set_level(&GPIO, XTAL32K_P_GPIO_NUM, 1);
|
|
gpio_ll_set_level(&GPIO, XTAL32K_N_GPIO_NUM, 0);
|
|
|
|
const uint32_t delay_us = (1000000 / SOC_CLK_XTAL32K_FREQ_APPROX / 2);
|
|
while (cycle) {
|
|
gpio_ll_set_level(&GPIO, XTAL32K_P_GPIO_NUM, 1);
|
|
gpio_ll_set_level(&GPIO, XTAL32K_N_GPIO_NUM, 0);
|
|
esp_rom_delay_us(delay_us);
|
|
gpio_ll_set_level(&GPIO, XTAL32K_N_GPIO_NUM, 1);
|
|
gpio_ll_set_level(&GPIO, XTAL32K_P_GPIO_NUM, 0);
|
|
esp_rom_delay_us(delay_us);
|
|
cycle--;
|
|
}
|
|
// disable pins
|
|
gpio_ll_output_disable(&GPIO, XTAL32K_P_GPIO_NUM);
|
|
gpio_ll_output_disable(&GPIO, XTAL32K_N_GPIO_NUM);
|
|
}
|
|
|
|
clk_ll_xtal32k_disable();
|
|
SET_PERI_REG_MASK(RTC_IO_XTAL_32K_PAD_REG, RTC_IO_X32P_RUE | RTC_IO_X32N_RDE);
|
|
esp_rom_delay_us(XTAL_32K_BOOTSTRAP_TIME_US);
|
|
|
|
rtc_clk_32k_enable_common(CLK_LL_XTAL32K_ENABLE_MODE_BOOTSTRAP);
|
|
}
|
|
|
|
bool rtc_clk_32k_enabled(void)
|
|
{
|
|
return clk_ll_xtal32k_is_enabled();
|
|
}
|
|
|
|
void rtc_clk_8m_enable(bool clk_8m_en, bool d256_en)
|
|
{
|
|
if (clk_8m_en) {
|
|
clk_ll_rc_fast_enable();
|
|
if (d256_en) {
|
|
clk_ll_rc_fast_d256_enable();
|
|
} else {
|
|
clk_ll_rc_fast_d256_disable();
|
|
}
|
|
esp_rom_delay_us(SOC_DELAY_RC_FAST_ENABLE);
|
|
} else {
|
|
clk_ll_rc_fast_disable();
|
|
}
|
|
}
|
|
|
|
bool rtc_clk_8m_enabled(void)
|
|
{
|
|
return clk_ll_rc_fast_is_enabled();
|
|
}
|
|
|
|
bool rtc_clk_8md256_enabled(void)
|
|
{
|
|
return clk_ll_rc_fast_d256_is_enabled();
|
|
}
|
|
|
|
void rtc_clk_apll_enable(bool enable)
|
|
{
|
|
if (enable) {
|
|
clk_ll_apll_enable();
|
|
} else {
|
|
clk_ll_apll_disable();
|
|
}
|
|
|
|
if (!enable && (clk_ll_cpu_get_src() != SOC_CPU_CLK_SRC_PLL)) {
|
|
// if apll and bbpll are both not in use, then can also power down the internal I2C bus
|
|
// this power down affects most of the analog peripherals
|
|
clk_ll_i2c_pd();
|
|
} else {
|
|
clk_ll_i2c_pu();
|
|
}
|
|
}
|
|
|
|
uint32_t rtc_clk_apll_coeff_calc(uint32_t freq, uint32_t *_o_div, uint32_t *_sdm0, uint32_t *_sdm1, uint32_t *_sdm2)
|
|
{
|
|
uint32_t xtal_freq_mhz = (uint32_t)rtc_clk_xtal_freq_get();
|
|
if (xtal_freq_mhz == 0) {
|
|
// xtal_freq has not set yet
|
|
ESP_HW_LOGE(TAG, "Get xtal clock frequency failed, it has not been set yet");
|
|
abort();
|
|
}
|
|
/* Reference formula: apll_freq = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536) / ((o_div + 2) * 2)
|
|
* ---------------------------------------------- -----------------
|
|
* 350 MHz <= Numerator <= 500 MHz Denominator
|
|
*/
|
|
int o_div = 0; // range: 0~31
|
|
int sdm0 = 0; // range: 0~255
|
|
int sdm1 = 0; // range: 0~255
|
|
int sdm2 = 0; // range: 0~63
|
|
/* Firstly try to satisfy the condition that the operation frequency of numerator should be greater than 350 MHz,
|
|
* i.e. xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536) >= 350 MHz, '+1' in the following code is to get the ceil value.
|
|
* With this condition, as we know the 'o_div' can't be greater than 31, then we can calculate the APLL minimum support frequency is
|
|
* 350 MHz / ((31 + 2) * 2) = 5303031 Hz (for ceil) */
|
|
o_div = (int)(SOC_APLL_MULTIPLIER_OUT_MIN_HZ / (float)(freq * 2) + 1) - 2;
|
|
if (o_div > 31) {
|
|
ESP_HW_LOGE(TAG, "Expected frequency is too small");
|
|
return 0;
|
|
}
|
|
if (o_div < 0) {
|
|
/* Try to satisfy the condition that the operation frequency of numerator should be smaller than 500 MHz,
|
|
* i.e. xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536) <= 500 MHz, we need to get the floor value in the following code.
|
|
* With this condition, as we know the 'o_div' can't be smaller than 0, then we can calculate the APLL maximum support frequency is
|
|
* 500 MHz / ((0 + 2) * 2) = 125000000 Hz */
|
|
o_div = (int)(SOC_APLL_MULTIPLIER_OUT_MAX_HZ / (float)(freq * 2)) - 2;
|
|
if (o_div < 0) {
|
|
ESP_HW_LOGE(TAG, "Expected frequency is too big");
|
|
return 0;
|
|
}
|
|
}
|
|
// sdm2 = (int)(((o_div + 2) * 2) * apll_freq / xtal_freq) - 4
|
|
sdm2 = (int)(((o_div + 2) * 2 * freq) / (xtal_freq_mhz * MHZ)) - 4;
|
|
// numrator = (((o_div + 2) * 2) * apll_freq / xtal_freq) - 4 - sdm2
|
|
float numrator = (((o_div + 2) * 2 * freq) / ((float)xtal_freq_mhz * MHZ)) - 4 - sdm2;
|
|
// If numrator is bigger than 255/256 + 255/65536 + (1/65536)/2 = 1 - (1 / 65536)/2, carry bit to sdm2
|
|
if (numrator > 1.0 - (1.0 / 65536.0) / 2.0) {
|
|
sdm2++;
|
|
}
|
|
// If numrator is smaller than (1/65536)/2, keep sdm0 = sdm1 = 0, otherwise calculate sdm0 and sdm1
|
|
else if (numrator > (1.0 / 65536.0) / 2.0) {
|
|
// Get the closest sdm1
|
|
sdm1 = (int)(numrator * 65536.0 + 0.5) / 256;
|
|
// Get the closest sdm0
|
|
sdm0 = (int)(numrator * 65536.0 + 0.5) % 256;
|
|
}
|
|
uint32_t real_freq = (uint32_t)(xtal_freq_mhz * MHZ * (4 + sdm2 + (float)sdm1/256.0 + (float)sdm0/65536.0) / (((float)o_div + 2) * 2));
|
|
*_o_div = o_div;
|
|
*_sdm0 = sdm0;
|
|
*_sdm1 = sdm1;
|
|
*_sdm2 = sdm2;
|
|
return real_freq;
|
|
}
|
|
|
|
void rtc_clk_apll_coeff_set(uint32_t o_div, uint32_t sdm0, uint32_t sdm1, uint32_t sdm2)
|
|
{
|
|
bool is_rev0 = (efuse_ll_get_chip_ver_rev1() == 0);
|
|
clk_ll_apll_set_config(is_rev0, o_div, sdm0, sdm1, sdm2);
|
|
|
|
/* calibration */
|
|
clk_ll_apll_set_calibration();
|
|
|
|
/* wait for calibration end */
|
|
while (!clk_ll_apll_calibration_is_done()) {
|
|
/* use esp_rom_delay_us so the RTC bus doesn't get flooded */
|
|
esp_rom_delay_us(1);
|
|
}
|
|
}
|
|
|
|
void rtc_clk_slow_src_set(soc_rtc_slow_clk_src_t clk_src)
|
|
{
|
|
clk_ll_rtc_slow_set_src(clk_src);
|
|
|
|
// The logic should be moved to BT driver
|
|
if (clk_src == SOC_RTC_SLOW_CLK_SRC_XTAL32K) {
|
|
clk_ll_xtal32k_digi_enable();
|
|
} else {
|
|
clk_ll_xtal32k_digi_disable();
|
|
}
|
|
|
|
esp_rom_delay_us(SOC_DELAY_RTC_SLOW_CLK_SWITCH);
|
|
}
|
|
|
|
soc_rtc_slow_clk_src_t rtc_clk_slow_src_get(void)
|
|
{
|
|
return clk_ll_rtc_slow_get_src();
|
|
}
|
|
|
|
uint32_t rtc_clk_slow_freq_get_hz(void)
|
|
{
|
|
switch (rtc_clk_slow_src_get()) {
|
|
case SOC_RTC_SLOW_CLK_SRC_RC_SLOW: return SOC_CLK_RC_SLOW_FREQ_APPROX;
|
|
case SOC_RTC_SLOW_CLK_SRC_XTAL32K: return SOC_CLK_XTAL32K_FREQ_APPROX;
|
|
case SOC_RTC_SLOW_CLK_SRC_RC_FAST_D256: return SOC_CLK_RC_FAST_D256_FREQ_APPROX;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
void rtc_clk_fast_src_set(soc_rtc_fast_clk_src_t clk_src)
|
|
{
|
|
clk_ll_rtc_fast_set_src(clk_src);
|
|
esp_rom_delay_us(SOC_DELAY_RTC_FAST_CLK_SWITCH);
|
|
}
|
|
|
|
soc_rtc_fast_clk_src_t rtc_clk_fast_src_get(void)
|
|
{
|
|
return clk_ll_rtc_fast_get_src();
|
|
}
|
|
|
|
static void rtc_clk_bbpll_disable(void)
|
|
{
|
|
clk_ll_bbpll_disable();
|
|
s_cur_pll_freq = 0;
|
|
|
|
// if apll is under force power down, then can also power down the internal I2C bus
|
|
// this power down affects most of the analog peripherals
|
|
if (clk_ll_apll_is_fpd()) {
|
|
clk_ll_i2c_pd();
|
|
}
|
|
}
|
|
|
|
static void rtc_clk_bbpll_enable(void)
|
|
{
|
|
clk_ll_i2c_pu();
|
|
clk_ll_bbpll_enable();
|
|
}
|
|
|
|
static void rtc_clk_bbpll_configure(rtc_xtal_freq_t xtal_freq, int pll_freq)
|
|
{
|
|
/* Raise the voltage */
|
|
if (pll_freq == CLK_LL_PLL_320M_FREQ_MHZ) {
|
|
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_80M_160M);
|
|
} else { // CLK_LL_PLL_480M_FREQ_MHZ
|
|
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_240M);
|
|
esp_rom_delay_us(SOC_DELAY_PLL_DBIAS_RAISE);
|
|
}
|
|
|
|
clk_ll_bbpll_set_config(pll_freq, xtal_freq);
|
|
uint32_t delay_pll_en = (clk_ll_rtc_slow_get_src() == SOC_RTC_SLOW_CLK_SRC_RC_SLOW) ?
|
|
SOC_DELAY_PLL_ENABLE_WITH_150K : SOC_DELAY_PLL_ENABLE_WITH_32K;
|
|
esp_rom_delay_us(delay_pll_en);
|
|
s_cur_pll_freq = pll_freq;
|
|
}
|
|
|
|
/**
|
|
* Switch to XTAL frequency. Does not disable the PLL.
|
|
*/
|
|
void rtc_clk_cpu_freq_to_xtal(int freq, int div)
|
|
{
|
|
ets_update_cpu_frequency(freq);
|
|
/* set divider from XTAL to APB clock */
|
|
clk_ll_cpu_set_divider(div);
|
|
/* adjust ref_tick */
|
|
clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_XTAL, freq);
|
|
/* switch clock source */
|
|
clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_XTAL);
|
|
rtc_clk_apb_freq_update(freq * MHZ);
|
|
/* lower the voltage */
|
|
int dbias = (freq <= 2) ? DIG_DBIAS_2M : DIG_DBIAS_XTAL;
|
|
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, dbias);
|
|
}
|
|
|
|
static void rtc_clk_cpu_freq_to_8m(void)
|
|
{
|
|
ets_update_cpu_frequency(8);
|
|
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL);
|
|
clk_ll_cpu_set_divider(1);
|
|
/* adjust ref_tick */
|
|
clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_RC_FAST, 8);
|
|
/* switch clock source */
|
|
clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_RC_FAST);
|
|
rtc_clk_apb_freq_update(SOC_CLK_RC_FAST_FREQ_APPROX);
|
|
}
|
|
|
|
/**
|
|
* 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 = (cpu_freq_mhz == 240) ? DIG_DBIAS_240M : DIG_DBIAS_80M_160M;
|
|
clk_ll_cpu_set_freq_mhz_from_pll(cpu_freq_mhz);
|
|
REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, dbias);
|
|
/* adjust ref_tick */
|
|
clk_ll_ref_tick_set_divider(SOC_CPU_CLK_SRC_PLL, cpu_freq_mhz);
|
|
/* switch clock source */
|
|
clk_ll_cpu_set_src(SOC_CPU_CLK_SRC_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_xtal(void)
|
|
{
|
|
int freq_mhz = (int)rtc_clk_xtal_freq_get();
|
|
|
|
rtc_clk_cpu_freq_to_xtal(freq_mhz, 1);
|
|
rtc_clk_wait_for_slow_cycle();
|
|
rtc_clk_bbpll_disable();
|
|
}
|
|
|
|
bool rtc_clk_cpu_freq_mhz_to_config(uint32_t freq_mhz, rtc_cpu_freq_config_t* out_config)
|
|
{
|
|
uint32_t source_freq_mhz;
|
|
soc_cpu_clk_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 && freq_mhz != 0) {
|
|
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 = SOC_CPU_CLK_SRC_XTAL;
|
|
} else if (freq_mhz == 80) {
|
|
real_freq_mhz = freq_mhz;
|
|
source = SOC_CPU_CLK_SRC_PLL;
|
|
source_freq_mhz = CLK_LL_PLL_320M_FREQ_MHZ;
|
|
divider = 4;
|
|
} else if (freq_mhz == 160) {
|
|
real_freq_mhz = freq_mhz;
|
|
source = SOC_CPU_CLK_SRC_PLL;
|
|
source_freq_mhz = CLK_LL_PLL_320M_FREQ_MHZ;
|
|
divider = 2;
|
|
} else if (freq_mhz == 240) {
|
|
real_freq_mhz = freq_mhz;
|
|
source = SOC_CPU_CLK_SRC_PLL;
|
|
source_freq_mhz = CLK_LL_PLL_480M_FREQ_MHZ;
|
|
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();
|
|
soc_cpu_clk_src_t old_cpu_clk_src = clk_ll_cpu_get_src();
|
|
if (old_cpu_clk_src != SOC_CPU_CLK_SRC_XTAL) {
|
|
rtc_clk_cpu_freq_to_xtal(xtal_freq, 1);
|
|
rtc_clk_wait_for_slow_cycle();
|
|
}
|
|
if (old_cpu_clk_src == SOC_CPU_CLK_SRC_PLL) {
|
|
rtc_clk_bbpll_disable();
|
|
}
|
|
|
|
if (config->source == SOC_CPU_CLK_SRC_XTAL) {
|
|
if (config->div > 1) {
|
|
rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div);
|
|
}
|
|
} else if (config->source == SOC_CPU_CLK_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 == SOC_CPU_CLK_SRC_RC_FAST) {
|
|
rtc_clk_cpu_freq_to_8m();
|
|
}
|
|
}
|
|
|
|
void rtc_clk_cpu_freq_get_config(rtc_cpu_freq_config_t* out_config)
|
|
{
|
|
soc_cpu_clk_src_t source = clk_ll_cpu_get_src();
|
|
uint32_t source_freq_mhz;
|
|
uint32_t div;
|
|
uint32_t freq_mhz;
|
|
switch (source) {
|
|
case SOC_CPU_CLK_SRC_XTAL: {
|
|
div = clk_ll_cpu_get_divider();
|
|
source_freq_mhz = (uint32_t) rtc_clk_xtal_freq_get();
|
|
freq_mhz = source_freq_mhz / div;
|
|
}
|
|
break;
|
|
case SOC_CPU_CLK_SRC_PLL: {
|
|
freq_mhz = clk_ll_cpu_get_freq_mhz_from_pll();
|
|
if (freq_mhz == CLK_LL_PLL_80M_FREQ_MHZ) {
|
|
source_freq_mhz = CLK_LL_PLL_320M_FREQ_MHZ;
|
|
div = 4;
|
|
} else if (freq_mhz == CLK_LL_PLL_160M_FREQ_MHZ) {
|
|
source_freq_mhz = CLK_LL_PLL_320M_FREQ_MHZ;
|
|
div = 2;
|
|
} else if (freq_mhz == CLK_LL_PLL_240M_FREQ_MHZ) {
|
|
source_freq_mhz = CLK_LL_PLL_480M_FREQ_MHZ;
|
|
div = 2;
|
|
} else {
|
|
ESP_HW_LOGE(TAG, "unsupported frequency configuration");
|
|
abort();
|
|
}
|
|
break;
|
|
}
|
|
case SOC_CPU_CLK_SRC_RC_FAST:
|
|
source_freq_mhz = 8;
|
|
div = 1;
|
|
freq_mhz = source_freq_mhz;
|
|
break;
|
|
case SOC_CPU_CLK_SRC_APLL:
|
|
default:
|
|
ESP_HW_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 == SOC_CPU_CLK_SRC_XTAL) {
|
|
rtc_clk_cpu_freq_to_xtal(config->freq_mhz, config->div);
|
|
} else if (config->source == SOC_CPU_CLK_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(void)
|
|
{
|
|
uint32_t xtal_freq_mhz = clk_ll_xtal_load_freq_mhz();
|
|
if (xtal_freq_mhz == 0) {
|
|
return RTC_XTAL_FREQ_AUTO;
|
|
}
|
|
return (rtc_xtal_freq_t)xtal_freq_mhz;
|
|
}
|
|
|
|
void rtc_clk_xtal_freq_update(rtc_xtal_freq_t xtal_freq)
|
|
{
|
|
clk_ll_xtal_store_freq_mhz((uint32_t)xtal_freq);
|
|
}
|
|
|
|
void rtc_clk_apb_freq_update(uint32_t apb_freq)
|
|
{
|
|
clk_ll_apb_store_freq_hz(apb_freq);
|
|
}
|
|
|
|
uint32_t rtc_clk_apb_freq_get(void)
|
|
{
|
|
#if CONFIG_IDF_ENV_FPGA
|
|
return CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ * MHZ;
|
|
#endif // CONFIG_IDF_ENV_FPGA
|
|
return clk_ll_apb_load_freq_hz();
|
|
}
|
|
|
|
void rtc_dig_clk8m_enable(void)
|
|
{
|
|
clk_ll_rc_fast_digi_enable();
|
|
esp_rom_delay_us(SOC_DELAY_RC_FAST_DIGI_SWITCH);
|
|
}
|
|
|
|
void rtc_dig_clk8m_disable(void)
|
|
{
|
|
clk_ll_rc_fast_digi_disable();
|
|
esp_rom_delay_us(SOC_DELAY_RC_FAST_DIGI_SWITCH);
|
|
}
|
|
|
|
bool rtc_dig_8m_enabled(void)
|
|
{
|
|
return clk_ll_rc_fast_digi_is_enabled();
|
|
}
|
|
|
|
/* Name used in libphy.a:phy_chip_v7.o
|
|
* TODO: update the library to use rtc_clk_xtal_freq_get
|
|
*/
|
|
rtc_xtal_freq_t rtc_get_xtal(void) __attribute__((alias("rtc_clk_xtal_freq_get")));
|