diff --git a/components/esp_hw_support/cpu_util.c b/components/esp_hw_support/cpu_util.c index 09b7c2a0c0..a24f5c5cd7 100644 --- a/components/esp_hw_support/cpu_util.c +++ b/components/esp_hw_support/cpu_util.c @@ -77,8 +77,10 @@ void IRAM_ATTR esp_clear_watchpoint(int no) bool IRAM_ATTR esp_cpu_in_ocd_debug_mode(void) { -#if (CONFIG_ESP32_DEBUG_OCDAWARE == 1) || \ - (CONFIG_ESP32S2_DEBUG_OCDAWARE == 1) +#if CONFIG_ESP32_DEBUG_OCDAWARE || \ + CONFIG_ESP32S2_DEBUG_OCDAWARE || \ + CONFIG_ESP32S3_DEBUG_OCDAWARE || \ + CONFIG_ESP32C3_DEBUG_OCDAWARE return cpu_ll_is_debugger_attached(); #else return false; // Always return false if "OCD aware" is disabled diff --git a/components/esp_hw_support/include/soc_log.h b/components/esp_hw_support/include/soc_log.h index 2f2bfd4e96..894f0962ce 100644 --- a/components/esp_hw_support/include/soc_log.h +++ b/components/esp_hw_support/include/soc_log.h @@ -39,6 +39,8 @@ #include "esp32s2/rom/ets_sys.h" #elif CONFIG_IDF_TARGET_ESP32S3 #include "esp32s3/rom/ets_sys.h" +#elif CONFIG_IDF_TARGET_ESP32C3 +#include "esp32c3/rom/ets_sys.h" #endif #define SOC_LOGE(tag, fmt, ...) esp_rom_printf("%s(err): " fmt, tag, ##__VA_ARGS__) diff --git a/components/esp_hw_support/port/esp32c3/CMakeLists.txt b/components/esp_hw_support/port/esp32c3/CMakeLists.txt new file mode 100644 index 0000000000..9701d235e0 --- /dev/null +++ b/components/esp_hw_support/port/esp32c3/CMakeLists.txt @@ -0,0 +1,20 @@ +set(srcs "cpu_util_esp32c3.c" + "rtc_clk_init.c" + "rtc_clk.c" + "rtc_init.c" + "rtc_pm.c" + "rtc_sleep.c" + "rtc_time.c" + "soc_memory_layout.c" + ) + +add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" "${srcs}") + +target_sources(${COMPONENT_LIB} PRIVATE "${srcs}") +target_include_directories(${COMPONENT_LIB} PUBLIC . include) +target_include_directories(${COMPONENT_LIB} PRIVATE ../hal) + +if(NOT CMAKE_BUILD_EARLY_EXPANSION) + set_source_files_properties("${CMAKE_CURRENT_LIST_DIR}/rtc_clk.c" PROPERTIES + COMPILE_FLAGS "-fno-jump-tables -fno-tree-switch-conversion") +endif() diff --git a/components/esp_hw_support/port/esp32c3/cpu_util_esp32c3.c b/components/esp_hw_support/port/esp32c3/cpu_util_esp32c3.c new file mode 100644 index 0000000000..b070320bb2 --- /dev/null +++ b/components/esp_hw_support/port/esp32c3/cpu_util_esp32c3.c @@ -0,0 +1,112 @@ +// Copyright 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 +#include "soc/cpu.h" + +void esp_cpu_configure_region_protection(void) +{ + /* Notes on implementation: + * + * 1) Note: ESP32-C3 CPU doesn't support overlapping PMP regions + * + * 2) Therefore, we use TOR (top of range) entries to map the whole address + * space, bottom to top. + * + * 3) There are not enough entries to describe all the memory regions 100% accurately. + * + * 4) This means some gaps (invalid memory) are accessible. Priority for extending regions + * to cover gaps is to extend read-only or read-execute regions or read-only regions only + * (executing unmapped addresses should always fault with invalid instruction, read-only means + * stores will correctly fault even if reads may return some invalid value.) + * + * 5) Entries are grouped in order with some static asserts to try and verify everything is + * correct. + */ + const unsigned NONE = PMP_L | PMP_TOR; + const unsigned R = PMP_L | PMP_TOR | PMP_R; + const unsigned RW = PMP_L | PMP_TOR | PMP_R | PMP_W; + const unsigned RX = PMP_L | PMP_TOR | PMP_R | PMP_X; + const unsigned RWX = PMP_L | PMP_TOR | PMP_R | PMP_W | PMP_X; + + // 1. Gap at bottom of address space + PMP_ENTRY_SET(0, SOC_DEBUG_LOW, NONE); + + // 2. Debug region + PMP_ENTRY_SET(1, SOC_DEBUG_HIGH, RWX); + _Static_assert(SOC_DEBUG_LOW < SOC_DEBUG_HIGH, "Invalid CPU debug region"); + + // 3. Gap between debug region & DROM (flash cache) + PMP_ENTRY_SET(2, SOC_DROM_LOW, NONE); + _Static_assert(SOC_DEBUG_HIGH < SOC_DROM_LOW, "Invalid PMP entry order"); + + // 4. DROM (flash cache) + // 5. Gap between DROM & DRAM + // (Note: To save PMP entries these two are merged into one read-only region) + PMP_ENTRY_SET(3, SOC_DRAM_LOW, R); + _Static_assert(SOC_DROM_LOW < SOC_DROM_HIGH, "Invalid DROM region"); + _Static_assert(SOC_DROM_HIGH < SOC_DRAM_LOW, "Invalid PMP entry order"); + + // 6. DRAM + PMP_ENTRY_SET(4, SOC_DRAM_HIGH, RW); + _Static_assert(SOC_DRAM_LOW < SOC_DRAM_HIGH, "Invalid DRAM region"); + + // 7. Gap between DRAM and Mask DROM + // 8. Mask DROM + // (Note: to save PMP entries these two are merged into one read-only region) + PMP_ENTRY_SET(5, SOC_DROM_MASK_HIGH, R); + _Static_assert(SOC_DRAM_HIGH < SOC_DROM_MASK_LOW, "Invalid PMP entry order"); + _Static_assert(SOC_DROM_MASK_LOW < SOC_DROM_MASK_HIGH, "Invalid mask DROM region"); + + // 9. Gap between mask DROM and mask IROM + // 10. Mask IROM + // (Note: to save PMP entries these two are merged into one RX region) + PMP_ENTRY_SET(6, SOC_IROM_MASK_HIGH, RX); + _Static_assert(SOC_DROM_MASK_HIGH < SOC_IROM_MASK_LOW, "Invalid PMP entry order"); + _Static_assert(SOC_IROM_MASK_LOW < SOC_IROM_MASK_HIGH, "Invalid mask IROM region"); + + // 11. Gap between mask IROM & IRAM + PMP_ENTRY_SET(7, SOC_IRAM_LOW, NONE); + _Static_assert(SOC_IROM_MASK_HIGH < SOC_IRAM_LOW, "Invalid PMP entry order"); + + // 12. IRAM + PMP_ENTRY_SET(8, SOC_IRAM_HIGH, RWX); + _Static_assert(SOC_IRAM_LOW < SOC_IRAM_HIGH, "Invalid IRAM region"); + + // 13. Gap between IRAM and IROM + // 14. IROM (flash cache) + // (Note: to save PMP entries these two are merged into one RX region) + PMP_ENTRY_SET(9, SOC_IROM_HIGH, RX); + _Static_assert(SOC_IRAM_HIGH < SOC_IROM_LOW, "Invalid PMP entry order"); + _Static_assert(SOC_IROM_LOW < SOC_IROM_HIGH, "Invalid IROM region"); + + // 15. Gap between IROM & RTC slow memory + PMP_ENTRY_SET(10, SOC_RTC_IRAM_LOW, NONE); + _Static_assert(SOC_IROM_HIGH < SOC_RTC_IRAM_LOW, "Invalid PMP entry order"); + + // 16. RTC fast memory + PMP_ENTRY_SET(11, SOC_RTC_IRAM_HIGH, RWX); + _Static_assert(SOC_RTC_IRAM_LOW < SOC_RTC_IRAM_HIGH, "Invalid RTC IRAM region"); + + // 17. Gap between RTC fast memory & peripheral addresses + PMP_ENTRY_SET(12, SOC_PERIPHERAL_LOW, NONE); + _Static_assert(SOC_RTC_IRAM_HIGH < SOC_PERIPHERAL_LOW, "Invalid PMP entry order"); + + // 18. Peripheral addresses + PMP_ENTRY_SET(13, SOC_PERIPHERAL_HIGH, RW); + _Static_assert(SOC_PERIPHERAL_LOW < SOC_PERIPHERAL_HIGH, "Invalid peripheral region"); + + // 19. End of address space + PMP_ENTRY_SET(14, UINT32_MAX, NONE); // all but last 4 bytes + PMP_ENTRY_SET(15, UINT32_MAX, PMP_L | PMP_NA4); // last 4 bytes +} diff --git a/components/esp_hw_support/port/esp32c3/i2c_brownout.h b/components/esp_hw_support/port/esp32c3/i2c_brownout.h new file mode 100644 index 0000000000..5fac2c91b3 --- /dev/null +++ b/components/esp_hw_support/port/esp32c3/i2c_brownout.h @@ -0,0 +1,30 @@ +// Copyright 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 + +/** + * @file i2c_brownout.h + * @brief Register definitions for brownout detector + * + * This file lists register fields of the brownout detector, located on an internal configuration + * bus. These definitions are used via macros defined in i2c_rtc_clk.h. + */ + +#define I2C_BOD 0x61 +#define I2C_BOD_HOSTID 1 + +#define I2C_BOD_THRESHOLD 0x5 +#define I2C_BOD_THRESHOLD_MSB 2 +#define I2C_BOD_THRESHOLD_LSB 0 diff --git a/components/esp_hw_support/port/esp32c3/i2c_rtc_clk.h b/components/esp_hw_support/port/esp32c3/i2c_rtc_clk.h new file mode 100644 index 0000000000..7042b8d1c7 --- /dev/null +++ b/components/esp_hw_support/port/esp32c3/i2c_rtc_clk.h @@ -0,0 +1,49 @@ +// Copyright 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 + +#include +#include "i2c_apll.h" +#include "i2c_bbpll.h" + +/* Analog function control register */ +#define ANA_CONFIG_REG 0x6000E044 +#define ANA_CONFIG_S (8) +#define ANA_CONFIG_M (0x3FF) +/* Clear to enable APLL */ +#define I2C_APLL_M (BIT(14)) +/* Clear to enable BBPLL */ +#define I2C_BBPLL_M (BIT(17)) + +/* ROM functions which read/write internal control bus */ +uint8_t rom_i2c_readReg(uint8_t block, uint8_t host_id, uint8_t reg_add); +uint8_t rom_i2c_readReg_Mask(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t msb, uint8_t lsb); +void rom_i2c_writeReg(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t data); +void rom_i2c_writeReg_Mask(uint8_t block, uint8_t host_id, uint8_t reg_add, uint8_t msb, uint8_t lsb, uint8_t data); + +/* Convenience macros for the above functions, these use register definitions + * from i2c_apll.h/i2c_bbpll.h header files. + */ +#define I2C_WRITEREG_MASK_RTC(block, reg_add, indata) \ + rom_i2c_writeReg_Mask(block, block##_HOSTID, reg_add, reg_add##_MSB, reg_add##_LSB, indata) + +#define I2C_READREG_MASK_RTC(block, reg_add) \ + rom_i2c_readReg_Mask(block, block##_HOSTID, reg_add, reg_add##_MSB, reg_add##_LSB) + +#define I2C_WRITEREG_RTC(block, reg_add, indata) \ + rom_i2c_writeReg(block, block##_HOSTID, reg_add, indata) + +#define I2C_READREG_RTC(block, reg_add) \ + rom_i2c_readReg(block, block##_HOSTID, reg_add) diff --git a/components/esp_hw_support/port/esp32c3/rtc_clk.c b/components/esp_hw_support/port/esp32c3/rtc_clk.c new file mode 100644 index 0000000000..d85901963c --- /dev/null +++ b/components/esp_hw_support/port/esp32c3/rtc_clk.c @@ -0,0 +1,565 @@ +// Copyright 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 +#include +#include +#include +#include +#include "sdkconfig.h" +#include "esp32c3/rom/ets_sys.h" +#include "esp32c3/rom/rtc.h" +#include "esp32c3/rom/uart.h" +#include "esp32c3/rom/gpio.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/sens_reg.h" +#include "soc/efuse_reg.h" +#include "soc/syscon_reg.h" +#include "soc/system_reg.h" +#include "i2c_rtc_clk.h" +#include "soc_log.h" +#include "rtc_clk_common.h" +#include "esp_rom_sys.h" + +static const char *TAG = "rtc_clk"; + +#define RTC_PLL_FREQ_320M 320 +#define RTC_PLL_FREQ_480M 480 + +// 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) +{ + REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DAC_XTAL_32K, cfg.dac); + REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DRES_XTAL_32K, cfg.dres); + REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DGM_XTAL_32K, cfg.dgm); + REG_SET_FIELD(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_DBUF_XTAL_32K, cfg.dbuf); + SET_PERI_REG_MASK(RTC_CNTL_EXT_XTL_CONF_REG, RTC_CNTL_XPD_XTAL_32K); +} + +void rtc_clk_32k_enable(bool enable) +{ + abort(); // TODO ESP32-C3 IDF-2408 +} + +void rtc_clk_32k_enable_external(void) +{ + abort(); // TODO ESP32-C3 IDF-2408 +} + +void rtc_clk_32k_bootstrap(uint32_t cycle) +{ + /* No special bootstrapping needed. Just enable the XTAL here. */ + (void) cycle; + rtc_clk_32k_enable(true); +} + +bool rtc_clk_32k_enabled(void) +{ + uint32_t xtal_conf = READ_PERI_REG(RTC_CNTL_EXT_XTL_CONF_REG); + /* If xtal xpd is controlled by software */ + bool xtal_xpd_sw = (xtal_conf & RTC_CNTL_XTAL32K_XPD_FORCE) >> RTC_CNTL_XTAL32K_XPD_FORCE_S; + /* If xtal xpd software control is on */ + bool xtal_xpd_st = (xtal_conf & RTC_CNTL_XPD_XTAL_32K) >> RTC_CNTL_XPD_XTAL_32K_S; + bool disabled = xtal_xpd_sw && !xtal_xpd_st; + return !disabled; +} + +void rtc_clk_8m_enable(bool clk_8m_en, bool d256_en) +{ + if (clk_8m_en) { + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M); + /* no need to wait once enabled by software */ + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, RTC_CK8M_ENABLE_WAIT_DEFAULT); + esp_rom_delay_us(DELAY_8M_ENABLE); + } else { + SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M); + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, RTC_CNTL_CK8M_WAIT_DEFAULT); + } + /* d256 should be independent configured with 8M + * Maybe we can split this function into 8m and dmd256 + */ + if (d256_en) { + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M_DIV); + } else { + SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M_DIV); + } +} + +bool rtc_clk_8m_enabled(void) +{ + return GET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M) == 0; +} + +bool rtc_clk_8md256_enabled(void) +{ + return GET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ENB_CK8M_DIV) == 0; +} + +void rtc_clk_apll_enable(bool enable, uint32_t sdm0, uint32_t sdm1, uint32_t sdm2, uint32_t o_div) +{ + 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); + + if (enable) { + 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); + I2C_WRITEREG_RTC(I2C_APLL, I2C_APLL_SDM_STOP, APLL_SDM_STOP_VAL_1); + I2C_WRITEREG_RTC(I2C_APLL, I2C_APLL_SDM_STOP, APLL_SDM_STOP_VAL_2_REV1); + I2C_WRITEREG_MASK_RTC(I2C_APLL, I2C_APLL_OR_OUTPUT_DIV, o_div); + + /* calibration */ + I2C_WRITEREG_RTC(I2C_APLL, I2C_APLL_IR_CAL_DELAY, APLL_CAL_DELAY_1); + I2C_WRITEREG_RTC(I2C_APLL, I2C_APLL_IR_CAL_DELAY, APLL_CAL_DELAY_2); + I2C_WRITEREG_RTC(I2C_APLL, I2C_APLL_IR_CAL_DELAY, APLL_CAL_DELAY_3); + + /* wait for calibration end */ + while (!(I2C_READREG_MASK_RTC(I2C_APLL, I2C_APLL_OR_CAL_END))) { + /* use esp_rom_delay_us so the RTC bus doesn't get flooded */ + esp_rom_delay_us(1); + } + } +} + +void rtc_clk_set_xtal_wait(void) +{ + /* + the `xtal_wait` time need 1ms, so we need calibrate slow clk period, + and `RTC_CNTL_XTL_BUF_WAIT` depend on it. + */ + rtc_slow_freq_t slow_clk_freq = rtc_clk_slow_freq_get(); + rtc_slow_freq_t rtc_slow_freq_x32k = RTC_SLOW_FREQ_32K_XTAL; + rtc_slow_freq_t rtc_slow_freq_8MD256 = RTC_SLOW_FREQ_8MD256; + rtc_cal_sel_t cal_clk = RTC_CAL_RTC_MUX; + if (slow_clk_freq == (rtc_slow_freq_x32k)) { + cal_clk = RTC_CAL_32K_XTAL; + } else if (slow_clk_freq == rtc_slow_freq_8MD256) { + cal_clk = RTC_CAL_8MD256; + } + uint32_t slow_clk_period = rtc_clk_cal(cal_clk, 2000); + uint32_t xtal_wait_1ms = 100; + if (slow_clk_period) { + xtal_wait_1ms = (1000 << RTC_CLK_CAL_FRACT) / slow_clk_period; + } + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_XTL_BUF_WAIT, xtal_wait_1ms); +} + +void rtc_clk_slow_freq_set(rtc_slow_freq_t slow_freq) +{ + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL, slow_freq); + + /* Why we need to connect this clock to digital? + * Or maybe this clock should be connected to digital when xtal 32k clock is enabled instead? + */ + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN, + (slow_freq == RTC_SLOW_FREQ_32K_XTAL) ? 1 : 0); + + /* The clk_8m_d256 will be closed when rtc_state in SLEEP, + so if the slow_clk is 8md256, clk_8m must be force power on + */ + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_FORCE_PU, (slow_freq == RTC_SLOW_FREQ_8MD256) ? 1 : 0); + rtc_clk_set_xtal_wait(); + esp_rom_delay_us(DELAY_SLOW_CLK_SWITCH); +} + +rtc_slow_freq_t rtc_clk_slow_freq_get(void) +{ + return REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_ANA_CLK_RTC_SEL); +} + +uint32_t rtc_clk_slow_freq_get_hz(void) +{ + switch (rtc_clk_slow_freq_get()) { + case RTC_SLOW_FREQ_RTC: return RTC_SLOW_CLK_FREQ_90K; + case RTC_SLOW_FREQ_32K_XTAL: return RTC_SLOW_CLK_FREQ_32K; + case RTC_SLOW_FREQ_8MD256: return RTC_SLOW_CLK_FREQ_8MD256; + } + return 0; +} + +void rtc_clk_fast_freq_set(rtc_fast_freq_t fast_freq) +{ + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_FAST_CLK_RTC_SEL, fast_freq); + esp_rom_delay_us(DELAY_FAST_CLK_SWITCH); +} + +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); +} + +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; +} + +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; + uint8_t dr1; + uint8_t dr3; + uint8_t dchgp; + uint8_t dcur; + + assert(xtal_freq == RTC_XTAL_FREQ_40M); + + if (pll_freq == RTC_PLL_FREQ_480M) { + /* Clear this register to let the digital part know 480M PLL is used */ + SET_PERI_REG_MASK(SYSTEM_CPU_PER_CONF_REG, SYSTEM_PLL_FREQ_SEL); + /* Configure 480M PLL */ + div_ref = 0; + div7_0 = 8; + dr1 = 0; + dr3 = 0; + dchgp = 5; + dcur = 4; + I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x6B); + } else { + /* Clear this register to let the digital part know 320M PLL is used */ + CLEAR_PERI_REG_MASK(SYSTEM_CPU_PER_CONF_REG, SYSTEM_PLL_FREQ_SEL); + /* Configure 320M PLL */ + div_ref = 0; + div7_0 = 4; + dr1 = 0; + dr3 = 0; + dchgp = 5; + dcur = 5; + I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_MODE_HF, 0x69); + } + uint8_t i2c_bbpll_lref = (dchgp << I2C_BBPLL_OC_DCHGP_LSB) | (div_ref); + uint8_t i2c_bbpll_div_7_0 = div7_0; + uint8_t i2c_bbpll_dcur = (2 << I2C_BBPLL_OC_DLREF_SEL_LSB ) | (1 << I2C_BBPLL_OC_DHREF_SEL_LSB) | dcur; + I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_REF_DIV, i2c_bbpll_lref); + I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_DIV_7_0, i2c_bbpll_div_7_0); + I2C_WRITEREG_MASK_RTC(I2C_BBPLL, I2C_BBPLL_OC_DR1, dr1); + I2C_WRITEREG_MASK_RTC(I2C_BBPLL, I2C_BBPLL_OC_DR3, dr3); + I2C_WRITEREG_RTC(I2C_BBPLL, I2C_BBPLL_OC_DCUR, i2c_bbpll_dcur); + + // Enable calibration by software + I2C_WRITEREG_MASK_RTC(I2C_BBPLL, I2C_BBPLL_IR_CAL_ENX_CAP, 1); + for (int ext_cap = 0; ext_cap < 16; ext_cap++) { + uint8_t cal_result; + I2C_WRITEREG_MASK_RTC(I2C_BBPLL, I2C_BBPLL_IR_CAL_EXT_CAP, ext_cap); + cal_result = I2C_READREG_MASK_RTC(I2C_BBPLL, I2C_BBPLL_OR_CAL_CAP); + if (cal_result == 0) { + break; + } + if (ext_cap == 15) { + SOC_LOGE(TAG, "BBPLL SOFTWARE CAL FAIL"); + abort(); + } + } + 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. + * @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 = 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 { + SOC_LOGE(TAG, "invalid frequency"); + abort(); + } + REG_SET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPUPERIOD_SEL, per_conf); + REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_PRE_DIV_CNT, 0); + //REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, dbias); + REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL, DPORT_SOC_CLK_SEL_PLL); + rtc_clk_apb_freq_update(80 * MHZ); + ets_update_cpu_frequency(cpu_freq_mhz); +} + +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_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 { + // 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(SYSTEM_SYSCLK_CONF_REG, SYSTEM_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(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL); + switch (soc_clk_sel) { + case DPORT_SOC_CLK_SEL_XTAL: { + source = RTC_CPU_FREQ_SRC_XTAL; + div = REG_GET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_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 = REG_GET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPUPERIOD_SEL); + uint32_t pllfreq_sel = REG_GET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_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 { + 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(rtc_cpu_freq_t cpu_freq) +{ + rtc_xtal_freq_t xtal_freq = RTC_XTAL_FREQ_40M; + /* Switch CPU to XTAL frequency first */ + //REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DIG_DBIAS_WAK, DIG_DBIAS_XTAL); + //REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL, APB_CTRL_SOC_CLK_SEL_XTL); + //REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_PRE_DIV_CNT, 1); + //ets_update_cpu_frequency(xtal_freq); + /* Frequency switch is synchronized to SLOW_CLK cycle. Wait until the switch + * is complete before disabling the PLL. + */ + rtc_clk_wait_for_slow_cycle(); + REG_SET_BIT(SYSTEM_CPU_PER_CONF_REG, SYSTEM_PLL_FREQ_SEL); + //REG_SET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPUPERIOD_SEL, 0); + // 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; + rtc_clk_apb_freq_update(xtal_freq * MHZ); + 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(SYSTEM_SYSCLK_CONF_REG, SYSTEM_PRE_DIV_CNT, (xtal_freq / 2) - 1); + ets_update_cpu_frequency(2); + rtc_clk_apb_freq_update(2 * MHZ); + } else { + /* use PLL as clock source */ + if (cpu_freq == RTC_CPU_FREQ_80M) { + + REG_SET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPUPERIOD_SEL, 0); + ets_update_cpu_frequency(80); + s_cur_pll_freq = RTC_PLL_FREQ_480M; + } else if (cpu_freq == RTC_CPU_FREQ_160M) { + //REG_CLR_BIT(SYSTEM_CPU_PER_CONF_REG, SYSTEM_PLL_FREQ_SEL); + REG_SET_FIELD(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPUPERIOD_SEL, 1); + ets_update_cpu_frequency(160); + s_cur_pll_freq = RTC_PLL_FREQ_480M; + } +#define APB_CTRL_SOC_CLK_SEL_PLL 1 + REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_SOC_CLK_SEL, APB_CTRL_SOC_CLK_SEL_PLL); + rtc_clk_wait_for_slow_cycle(); + rtc_clk_apb_freq_update(80 * MHZ); + } + s_cur_pll_freq = cpu_freq; +} + +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. + */ +void rtc_clk_cpu_freq_to_xtal(int freq, int div) +{ + 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(SYSTEM_SYSCLK_CONF_REG, SYSTEM_PRE_DIV_CNT, 0); + REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_PRE_DIV_CNT, div - 1); + /* no need to adjust the REF_TICK */ + /* switch clock source */ + REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_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 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); + REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_PRE_DIV_CNT, 0); + REG_SET_FIELD(SYSTEM_SYSCLK_CONF_REG, SYSTEM_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) +{ + 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_40M; + } + return reg_val_to_clk_val(xtal_freq_reg); +} + +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)); +} + +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)); +} + +uint32_t rtc_clk_apb_freq_get(void) +{ + uint32_t freq_hz = reg_val_to_clk_val(READ_PERI_REG(RTC_APB_FREQ_REG)) << 12; + // round to the nearest MHz + freq_hz += MHZ / 2; + uint32_t remainder = freq_hz % MHZ; + return freq_hz - remainder; +} + +void rtc_clk_divider_set(uint32_t div) +{ + CLEAR_PERI_REG_MASK(RTC_CNTL_SLOW_CLK_CONF_REG, RTC_CNTL_ANA_CLK_DIV_VLD); + REG_SET_FIELD(RTC_CNTL_SLOW_CLK_CONF_REG, RTC_CNTL_ANA_CLK_DIV, div); + SET_PERI_REG_MASK(RTC_CNTL_SLOW_CLK_CONF_REG, RTC_CNTL_ANA_CLK_DIV_VLD); +} + +void rtc_clk_8m_divider_set(uint32_t div) +{ + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL_VLD); + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, div); + SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL_VLD); +} + +/* 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"))); diff --git a/components/esp_hw_support/port/esp32c3/rtc_clk_common.h b/components/esp_hw_support/port/esp32c3/rtc_clk_common.h new file mode 100644 index 0000000000..787e36d08d --- /dev/null +++ b/components/esp_hw_support/port/esp32c3/rtc_clk_common.h @@ -0,0 +1,59 @@ +// Copyright 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 diff --git a/components/esp_hw_support/port/esp32c3/rtc_clk_init.c b/components/esp_hw_support/port/esp32c3/rtc_clk_init.c new file mode 100644 index 0000000000..19708e91dd --- /dev/null +++ b/components/esp_hw_support/port/esp32c3/rtc_clk_init.c @@ -0,0 +1,87 @@ +// Copyright 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 +#include +#include +#include +#include "esp32c3/rom/ets_sys.h" +#include "esp32c3/rom/rtc.h" +#include "esp32c3/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 "rtc_clk_common.h" +#include "esp_rom_uart.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; + esp_rom_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); + 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); +} diff --git a/components/esp_hw_support/port/esp32c3/rtc_init.c b/components/esp_hw_support/port/esp32c3/rtc_init.c new file mode 100644 index 0000000000..9361cf6c73 --- /dev/null +++ b/components/esp_hw_support/port/esp32c3/rtc_init.c @@ -0,0 +1,187 @@ +// Copyright 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 +#include "sdkconfig.h" +#include "soc/soc.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/efuse_periph.h" +#include "soc/gpio_reg.h" +#include "soc/spi_mem_reg.h" +#include "soc/extmem_reg.h" +#include "soc/system_reg.h" +#include "i2c_rtc_clk.h" + +void rtc_init(rtc_config_t cfg) +{ + CLEAR_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PVTMON_PU); + rtc_clk_set_xtal_wait(); + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_PLL_BUF_WAIT, cfg.pll_wait); + REG_SET_FIELD(RTC_CNTL_TIMER1_REG, RTC_CNTL_CK8M_WAIT, cfg.ck8m_wait); + + /* Moved from rtc sleep to rtc init to save sleep function running time */ + // set shortest possible sleep time limit + //REG_SET_FIELD(RTC_CNTL_TIMER5_REG, RTC_CNTL_MIN_SLP_VAL, RTC_CNTL_MIN_SLP_VAL_MIN); + + /* This power domian removed + * set rom&ram timer + * REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_ROM_RAM_POWERUP_TIMER, ROM_RAM_POWERUP_CYCLES); + * REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_ROM_RAM_WAIT_TIMER, ROM_RAM_WAIT_CYCLES); + */ + // set wifi timer + rtc_init_config_t rtc_init_cfg = RTC_INIT_CONFIG_DEFAULT(); + REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_WIFI_POWERUP_TIMER, rtc_init_cfg.wifi_powerup_cycles); + REG_SET_FIELD(RTC_CNTL_TIMER3_REG, RTC_CNTL_WIFI_WAIT_TIMER, rtc_init_cfg.wifi_wait_cycles); + // set rtc peri timer + //REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_POWERUP_TIMER, rtc_init_cfg.rtc_powerup_cycles); + //REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_WAIT_TIMER, rtc_init_cfg.rtc_wait_cycles); + // set digital wrap timer + REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_DG_WRAP_POWERUP_TIMER, rtc_init_cfg.dg_wrap_powerup_cycles); + REG_SET_FIELD(RTC_CNTL_TIMER4_REG, RTC_CNTL_DG_WRAP_WAIT_TIMER, rtc_init_cfg.dg_wrap_wait_cycles); + // set rtc memory timer + //REG_SET_FIELD(RTC_CNTL_TIMER5_REG, RTC_CNTL_RTCMEM_POWERUP_TIMER, rtc_init_cfg.rtc_mem_powerup_cycles); + //REG_SET_FIELD(RTC_CNTL_TIMER5_REG, RTC_CNTL_RTCMEM_WAIT_TIMER, rtc_init_cfg.rtc_mem_wait_cycles); + + //SET_PERI_REG_MASK(RTC_CNTL_BIAS_CONF_REG, RTC_CNTL_INC_HEARTBEAT_PERIOD); + + /* Reset RTC bias to default value (needed if waking up from deep sleep) */ + //REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DBIAS_WAK, RTC_CNTL_DBIAS_1V10); + //REG_SET_FIELD(RTC_CNTL_REG, RTC_CNTL_DBIAS_SLP, RTC_CNTL_DBIAS_1V10); + + + if (cfg.clkctl_init) { + //clear CMMU clock force on + CLEAR_PERI_REG_MASK(EXTMEM_CACHE_MMU_POWER_CTRL_REG, EXTMEM_CACHE_MMU_MEM_FORCE_ON); + //clear rom clock force on + //REG_SET_FIELD(SYSTEM_ROM_CTRL_0_REG, SYSTEM_ROM_IRAM0_CLKGATE_FORCE_ON, 0); + //clear sram clock force on + //REG_SET_FIELD(SYSTEM_SRAM_CTRL_0_REG, SYSTEM_SRAM_CLKGATE_FORCE_ON, 0); + //clear tag clock force on + CLEAR_PERI_REG_MASK(EXTMEM_ICACHE_TAG_POWER_CTRL_REG, EXTMEM_ICACHE_TAG_MEM_FORCE_ON); + //clear register clock force on + CLEAR_PERI_REG_MASK(SPI_MEM_CLOCK_GATE_REG(0), SPI_MEM_CLK_EN); + CLEAR_PERI_REG_MASK(SPI_MEM_CLOCK_GATE_REG(1), SPI_MEM_CLK_EN); + } + + if (cfg.pwrctl_init) { + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_FORCE_PU); + //cancel xtal force pu if no need to force power up + //cannot cancel xtal force pu if pll is force power on + if (!(cfg.xtal_fpu | cfg.bbpll_fpu)) { + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_XTL_FORCE_PU); + } else { + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_XTL_FORCE_PU); + } + // CLEAR APLL close + CLEAR_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PU); + SET_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_PLLA_FORCE_PD); + + //cancel bbpll force pu if setting no force power up + if (!cfg.bbpll_fpu) { + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BBPLL_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BBPLL_I2C_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BB_I2C_FORCE_PU); + } else { + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BBPLL_FORCE_PU); + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BBPLL_I2C_FORCE_PU); + SET_PERI_REG_MASK(RTC_CNTL_OPTIONS0_REG, RTC_CNTL_BB_I2C_FORCE_PU); + } + //cancel RTC REG force PU + //CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_REG, RTC_CNTL_REGULATOR_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_REG, RTC_CNTL_DBOOST_FORCE_PU); + + //combine two rtc memory options + //CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_MEM_FORCE_PU); + //CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_MEM_FORCE_NOISO); + + if (cfg.rtc_dboost_fpd) { + SET_PERI_REG_MASK(RTC_CNTL_REG, RTC_CNTL_DBOOST_FORCE_PD); + } else { + CLEAR_PERI_REG_MASK(RTC_CNTL_REG, RTC_CNTL_DBOOST_FORCE_PD); + } + + //cancel sar i2c pd force + //CLEAR_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG, RTC_CNTL_SAR_I2C_FORCE_PD); + //cancel digital pu force + //CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_MEM_FORCE_PU); + //cannel i2c_reset_protect pd force + //CLEAR_PERI_REG_MASK(RTC_CNTL_ANA_CONF_REG,RTC_CNTL_I2C_RESET_POR_FORCE_PD); + + /* If this mask is enabled, all soc memories cannot enter power down mode */ + /* We should control soc memory power down mode from RTC, so we will not touch this register any more */ + //CLEAR_PERI_REG_MASK(SYSTEM_MEM_PD_MASK_REG, SYSTEM_LSLP_MEM_PD_MASK); + /* If this pd_cfg is set to 1, all memory won't enter low power mode during light sleep */ + /* If this pd_cfg is set to 0, all memory will enter low power mode during light sleep */ + rtc_sleep_pd_config_t pd_cfg = RTC_SLEEP_PD_CONFIG_ALL(0); + rtc_sleep_pd(pd_cfg); + + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_DG_WRAP_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_WIFI_FORCE_PU); + // ROM_RAM power domain is removed + // CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_PWC_REG, RTC_CNTL_CPU_ROM_RAM_FORCE_PU); + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_WRAP_FORCE_NOISO); + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_WIFI_FORCE_NOISO); + // CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_CPU_ROM_RAM_FORCE_NOISO); + //CLEAR_PERI_REG_MASK(RTC_CNTL_PWC_REG, RTC_CNTL_FORCE_NOISO); + //cancel digital PADS force no iso + if (cfg.cpu_waiti_clk_gate) { + CLEAR_PERI_REG_MASK(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPU_WAIT_MODE_FORCE_ON); + } else { + SET_PERI_REG_MASK(SYSTEM_CPU_PER_CONF_REG, SYSTEM_CPU_WAIT_MODE_FORCE_ON); + } + /*if SYSTEM_CPU_WAIT_MODE_FORCE_ON == 0 , the cpu clk will be closed when cpu enter WAITI mode*/ + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_PAD_FORCE_UNHOLD); + CLEAR_PERI_REG_MASK(RTC_CNTL_DIG_ISO_REG, RTC_CNTL_DG_PAD_FORCE_NOISO); + } +} + +rtc_vddsdio_config_t rtc_vddsdio_get_config() +{ + rtc_vddsdio_config_t result; + uint32_t sdio_conf_reg = REG_READ(RTC_CNTL_SDIO_CONF_REG); + result.drefh = (sdio_conf_reg & RTC_CNTL_DREFH_SDIO_M) >> RTC_CNTL_DREFH_SDIO_S; + result.drefm = (sdio_conf_reg & RTC_CNTL_DREFM_SDIO_M) >> RTC_CNTL_DREFM_SDIO_S; + result.drefl = (sdio_conf_reg & RTC_CNTL_DREFL_SDIO_M) >> RTC_CNTL_DREFL_SDIO_S; + if (sdio_conf_reg & RTC_CNTL_SDIO_FORCE) { + // Get configuration from RTC + result.force = 1; + result.enable = (sdio_conf_reg & RTC_CNTL_XPD_SDIO_REG_M) >> RTC_CNTL_XPD_SDIO_REG_S; + result.tieh = (sdio_conf_reg & RTC_CNTL_SDIO_TIEH_M) >> RTC_CNTL_SDIO_TIEH_S; + return result; + } else { + result.force = 0; + } + + // Otherwise, VDD_SDIO is controlled by bootstrapping pin + uint32_t strap_reg = REG_READ(GPIO_STRAP_REG); + result.force = 0; + result.tieh = (strap_reg & BIT(5)) ? RTC_VDDSDIO_TIEH_1_8V : RTC_VDDSDIO_TIEH_3_3V; + result.enable = 1; + return result; +} + +void rtc_vddsdio_set_config(rtc_vddsdio_config_t config) +{ + uint32_t val = 0; + val |= (config.force << RTC_CNTL_SDIO_FORCE_S); + val |= (config.enable << RTC_CNTL_XPD_SDIO_REG_S); + val |= (config.drefh << RTC_CNTL_DREFH_SDIO_S); + val |= (config.drefm << RTC_CNTL_DREFM_SDIO_S); + val |= (config.drefl << RTC_CNTL_DREFL_SDIO_S); + val |= (config.tieh << RTC_CNTL_SDIO_TIEH_S); + val |= RTC_CNTL_SDIO_PD_EN; + REG_WRITE(RTC_CNTL_SDIO_CONF_REG, val); +} diff --git a/components/esp_hw_support/port/esp32c3/rtc_pm.c b/components/esp_hw_support/port/esp32c3/rtc_pm.c new file mode 100644 index 0000000000..58f2719418 --- /dev/null +++ b/components/esp_hw_support/port/esp32c3/rtc_pm.c @@ -0,0 +1,67 @@ +// Copyright 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 +#include +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/apb_ctrl_reg.h" + +typedef enum { + PM_LIGHT_SLEEP = BIT(2), /*!< WiFi PD, memory in light sleep */ +} pm_sleep_mode_t; + +typedef enum { + PM_SW_NOREJECT = 0, + PM_SW_REJECT = 1 +} pm_sw_reject_t; + + +/* These MAC-related functions are defined in the closed source part of + * RTC library + */ +extern void pm_mac_init(void); +extern int pm_check_mac_idle(void); +extern void pm_mac_deinit(void); + +/* This sleep-related function is called from the closed source part of RTC + * library. + */ +pm_sw_reject_t pm_set_sleep_mode(pm_sleep_mode_t sleep_mode, void(*pmac_save_params)(void)) +{ + (void) pmac_save_params; /* unused */ + + pm_mac_deinit(); + if (pm_check_mac_idle()) { + pm_mac_init(); + return PM_SW_REJECT; + } + + rtc_sleep_config_t cfg = { 0 }; + + switch (sleep_mode) { + case PM_LIGHT_SLEEP: + cfg.wifi_pd_en = 1; + cfg.dig_dbias_wak = 4; + cfg.dig_dbias_slp = 0; + cfg.rtc_dbias_wak = 0; + cfg.rtc_dbias_slp = 0; + rtc_sleep_init(cfg); + break; + + default: + assert(0 && "unsupported sleep mode"); + } + return PM_SW_NOREJECT; +} diff --git a/components/esp_hw_support/port/esp32c3/rtc_sleep.c b/components/esp_hw_support/port/esp32c3/rtc_sleep.c new file mode 100644 index 0000000000..d2775fdcda --- /dev/null +++ b/components/esp_hw_support/port/esp32c3/rtc_sleep.c @@ -0,0 +1,69 @@ +// Copyright 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 +#include "soc/soc.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/apb_ctrl_reg.h" +#include "soc/rtc.h" +#include "soc/i2s_reg.h" +#include "soc/timer_group_reg.h" +#include "soc/rtc.h" +#include "esp32c3/rom/ets_sys.h" + +/** + * Configure whether certain peripherals are powered down in deep sleep + * @param cfg power down flags as rtc_sleep_pd_config_t structure + */ +void rtc_sleep_pd(rtc_sleep_pd_config_t cfg) +{ + abort(); // TODO ESP32-C3 IDF-2106 +} + +void rtc_sleep_init(rtc_sleep_config_t cfg) +{ + abort(); // TODO ESP32-C3 IDF-2106 +} + +void rtc_sleep_set_wakeup_time(uint64_t t) +{ + WRITE_PERI_REG(RTC_CNTL_SLP_TIMER0_REG, t & UINT32_MAX); + WRITE_PERI_REG(RTC_CNTL_SLP_TIMER1_REG, t >> 32); +} + +uint32_t rtc_sleep_start(uint32_t wakeup_opt, uint32_t reject_opt, uint32_t lslp_mem_inf_fpu) +{ + REG_SET_FIELD(RTC_CNTL_WAKEUP_STATE_REG, RTC_CNTL_WAKEUP_ENA, wakeup_opt); + REG_SET_FIELD(RTC_CNTL_SLP_REJECT_CONF_REG, RTC_CNTL_SLEEP_REJECT_ENA, reject_opt); + + /* Start entry into sleep mode */ + SET_PERI_REG_MASK(RTC_CNTL_STATE0_REG, RTC_CNTL_SLEEP_EN); + + while (GET_PERI_REG_MASK(RTC_CNTL_INT_RAW_REG, + RTC_CNTL_SLP_REJECT_INT_RAW | RTC_CNTL_SLP_WAKEUP_INT_RAW) == 0) { + ; + } + /* In deep sleep mode, we never get here */ + uint32_t reject = REG_GET_FIELD(RTC_CNTL_INT_RAW_REG, RTC_CNTL_SLP_REJECT_INT_RAW); + SET_PERI_REG_MASK(RTC_CNTL_INT_CLR_REG, + RTC_CNTL_SLP_REJECT_INT_CLR | RTC_CNTL_SLP_WAKEUP_INT_CLR); + + /* restore config if it is a light sleep */ + if (lslp_mem_inf_fpu) { + rtc_sleep_pd_config_t pd_cfg = RTC_SLEEP_PD_CONFIG_ALL(0); + rtc_sleep_pd(pd_cfg); + } + return reject; +} diff --git a/components/esp_hw_support/port/esp32c3/rtc_time.c b/components/esp_hw_support/port/esp32c3/rtc_time.c new file mode 100644 index 0000000000..e9996ba03d --- /dev/null +++ b/components/esp_hw_support/port/esp32c3/rtc_time.c @@ -0,0 +1,181 @@ +// Copyright 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 +#include "esp32c3/rom/ets_sys.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/timer_group_reg.h" +#include "esp_rom_sys.h" + +/* Calibration of RTC_SLOW_CLK is performed using a special feature of TIMG0. + * This feature counts the number of XTAL clock cycles within a given number of + * RTC_SLOW_CLK cycles. + * + * Slow clock calibration feature has two modes of operation: one-off and cycling. + * In cycling mode (which is enabled by default on SoC reset), counting of XTAL + * cycles within RTC_SLOW_CLK cycle is done continuously. Cycling mode is enabled + * using TIMG_RTC_CALI_START_CYCLING bit. In one-off mode counting is performed + * once, and TIMG_RTC_CALI_RDY bit is set when counting is done. One-off mode is + * enabled using TIMG_RTC_CALI_START bit. + */ + +/** + * @brief Clock calibration function used by rtc_clk_cal and rtc_clk_cal_ratio + * @param cal_clk which clock to calibrate + * @param slowclk_cycles number of slow clock cycles to count + * @return number of XTAL clock cycles within the given number of slow clock cycles + */ +uint32_t rtc_clk_cal_internal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles) +{ + /* On ESP32S3, choosing RTC_CAL_RTC_MUX results in calibration of + * the 90k RTC clock regardless of the currenlty selected SLOW_CLK. + * On the ESP32, it used the currently selected SLOW_CLK. + * The following code emulates ESP32 behavior: + */ + if (cal_clk == RTC_CAL_RTC_MUX) { + rtc_slow_freq_t slow_freq = rtc_clk_slow_freq_get(); + if (slow_freq == RTC_SLOW_FREQ_32K_XTAL) { + cal_clk = RTC_CAL_32K_XTAL; + } else if (slow_freq == RTC_SLOW_FREQ_8MD256) { + cal_clk = RTC_CAL_8MD256; + } + } + /* Enable requested clock (150k clock is always on) */ + int dig_32k_xtal_state = REG_GET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN); + if (cal_clk == RTC_CAL_32K_XTAL && !dig_32k_xtal_state) { + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN, 1); + } + + if (cal_clk == RTC_CAL_8MD256) { + SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_D256_EN); + } + /* Prepare calibration */ + REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_CLK_SEL, cal_clk); + /* There may be another calibration process already running during we call this function, + * so we should wait the last process is done. + */ + if (!GET_PERI_REG_MASK(TIMG_RTCCALICFG2_REG(0), TIMG_RTC_CALI_TIMEOUT)) { + if (GET_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START_CYCLING)) { + while (!GET_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_RDY)); + } + } + CLEAR_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START_CYCLING); + REG_SET_FIELD(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_MAX, slowclk_cycles); + /* Figure out how long to wait for calibration to finish */ + + /* Set timeout reg and expect time delay*/ + uint32_t expected_freq; + if (cal_clk == RTC_CAL_32K_XTAL) { + REG_SET_FIELD(TIMG_RTCCALICFG2_REG(0), TIMG_RTC_CALI_TIMEOUT_THRES, RTC_SLOW_CLK_X32K_CAL_TIMEOUT_THRES(slowclk_cycles)); + expected_freq = RTC_SLOW_CLK_FREQ_32K; + } else if (cal_clk == RTC_CAL_8MD256) { + REG_SET_FIELD(TIMG_RTCCALICFG2_REG(0), TIMG_RTC_CALI_TIMEOUT_THRES, RTC_SLOW_CLK_8MD256_CAL_TIMEOUT_THRES(slowclk_cycles)); + expected_freq = RTC_SLOW_CLK_FREQ_8MD256; + } else { + REG_SET_FIELD(TIMG_RTCCALICFG2_REG(0), TIMG_RTC_CALI_TIMEOUT_THRES, RTC_SLOW_CLK_150K_CAL_TIMEOUT_THRES(slowclk_cycles)); + expected_freq = RTC_SLOW_CLK_FREQ_90K; + } + uint32_t us_time_estimate = (uint32_t) (((uint64_t) slowclk_cycles) * MHZ / expected_freq); + /* Start calibration */ + CLEAR_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START); + SET_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START); + + /* Wait for calibration to finish up to another us_time_estimate */ + esp_rom_delay_us(us_time_estimate); + uint32_t cal_val; + while (true) { + if (GET_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_RDY)) { + cal_val = REG_GET_FIELD(TIMG_RTCCALICFG1_REG(0), TIMG_RTC_CALI_VALUE); + break; + } + if (GET_PERI_REG_MASK(TIMG_RTCCALICFG2_REG(0), TIMG_RTC_CALI_TIMEOUT)) { + cal_val = 0; + break; + } + } + CLEAR_PERI_REG_MASK(TIMG_RTCCALICFG_REG(0), TIMG_RTC_CALI_START); + + REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_XTAL32K_EN, dig_32k_xtal_state); + + if (cal_clk == RTC_CAL_8MD256) { + CLEAR_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_D256_EN); + } + + return cal_val; +} + +uint32_t rtc_clk_cal_ratio(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles) +{ + uint64_t xtal_cycles = rtc_clk_cal_internal(cal_clk, slowclk_cycles); + uint64_t ratio_64 = ((xtal_cycles << RTC_CLK_CAL_FRACT)) / slowclk_cycles; + uint32_t ratio = (uint32_t)(ratio_64 & UINT32_MAX); + return ratio; +} + +uint32_t rtc_clk_cal(rtc_cal_sel_t cal_clk, uint32_t slowclk_cycles) +{ + rtc_xtal_freq_t xtal_freq = rtc_clk_xtal_freq_get(); + uint64_t xtal_cycles = rtc_clk_cal_internal(cal_clk, slowclk_cycles); + uint64_t divider = ((uint64_t)xtal_freq) * slowclk_cycles; + uint64_t period_64 = ((xtal_cycles << RTC_CLK_CAL_FRACT) + divider / 2 - 1) / divider; + uint32_t period = (uint32_t)(period_64 & UINT32_MAX); + return period; +} + +uint64_t rtc_time_us_to_slowclk(uint64_t time_in_us, uint32_t period) +{ + /* Overflow will happen in this function if time_in_us >= 2^45, which is about 400 days. + * TODO: fix overflow. + */ + return (time_in_us << RTC_CLK_CAL_FRACT) / period; +} + +uint64_t rtc_time_slowclk_to_us(uint64_t rtc_cycles, uint32_t period) +{ + return (rtc_cycles * period) >> RTC_CLK_CAL_FRACT; +} + +uint64_t rtc_time_get(void) +{ + SET_PERI_REG_MASK(RTC_CNTL_TIME_UPDATE_REG, RTC_CNTL_TIME_UPDATE); + uint64_t t = READ_PERI_REG(RTC_CNTL_TIME0_REG); + t |= ((uint64_t) READ_PERI_REG(RTC_CNTL_TIME1_REG)) << 32; + return t; +} + +uint64_t rtc_light_slp_time_get(void) +{ + uint64_t t_wake = READ_PERI_REG(RTC_CNTL_TIME_LOW0_REG); + t_wake |= ((uint64_t) READ_PERI_REG(RTC_CNTL_TIME_HIGH0_REG)) << 32; + uint64_t t_slp = READ_PERI_REG(RTC_CNTL_TIME_LOW1_REG); + t_slp |= ((uint64_t) READ_PERI_REG(RTC_CNTL_TIME_HIGH1_REG)) << 32; + return (t_wake - t_slp); +} + +uint64_t rtc_deep_slp_time_get(void) +{ + uint64_t t_slp = READ_PERI_REG(RTC_CNTL_TIME_LOW1_REG); + t_slp |= ((uint64_t) READ_PERI_REG(RTC_CNTL_TIME_HIGH1_REG)) << 32; + uint64_t t_wake = rtc_time_get(); + return (t_wake - t_slp); +} + +void rtc_clk_wait_for_slow_cycle(void) //This function may not by useful any more +{ + SET_PERI_REG_MASK(RTC_CNTL_SLOW_CLK_CONF_REG, RTC_CNTL_SLOW_CLK_NEXT_EDGE); + while (GET_PERI_REG_MASK(RTC_CNTL_SLOW_CLK_CONF_REG, RTC_CNTL_SLOW_CLK_NEXT_EDGE)) { + esp_rom_delay_us(1); + } +} diff --git a/components/esp_hw_support/port/esp32c3/soc_memory_layout.c b/components/esp_hw_support/port/esp32c3/soc_memory_layout.c new file mode 100644 index 0000000000..2aa3469e88 --- /dev/null +++ b/components/esp_hw_support/port/esp32c3/soc_memory_layout.c @@ -0,0 +1,91 @@ +// Copyright 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. +#ifndef BOOTLOADER_BUILD + +#include +#include +#include "esp_attr.h" +#include "sdkconfig.h" +#include "soc/soc.h" +#include "soc/soc_memory_layout.h" +#include "esp_heap_caps.h" + +/** + * @brief Memory type descriptors. These describe the capabilities of a type of memory in the SoC. + * Each type of memory map consists of one or more regions in the address space. + * Each type contains an array of prioritized capabilities. + * Types with later entries are only taken if earlier ones can't fulfill the memory request. + * + * - For a normal malloc (MALLOC_CAP_DEFAULT), give away the DRAM-only memory first, then pass off any dual-use IRAM regions, finally eat into the application memory. + * - For a malloc where 32-bit-aligned-only access is okay, first allocate IRAM, then DRAM, finally application IRAM. + * - Application mallocs (PIDx) will allocate IRAM first, if possible, then DRAM. + * - Most other malloc caps only fit in one region anyway. + * + */ +const soc_memory_type_desc_t soc_memory_types[] = { + // Type 0: DRAM + { "DRAM", { MALLOC_CAP_8BIT | MALLOC_CAP_DEFAULT, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_32BIT, 0 }, false, false}, + // Type 1: DRAM used for startup stacks + { "STACK/DRAM", { MALLOC_CAP_8BIT | MALLOC_CAP_DEFAULT, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA | MALLOC_CAP_32BIT, MALLOC_CAP_RETENTION }, false, true}, + // Type 2: DRAM which has an alias on the I-port + { "D/IRAM", { 0, MALLOC_CAP_DMA | MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL | MALLOC_CAP_DEFAULT, MALLOC_CAP_32BIT | MALLOC_CAP_EXEC }, true, false}, + // Type 3: IRAM + { "IRAM", { MALLOC_CAP_EXEC | MALLOC_CAP_32BIT | MALLOC_CAP_INTERNAL, 0, 0 }, false, false}, + { "FAKEDRAM", { MALLOC_CAP_8BIT|MALLOC_CAP_DEFAULT, MALLOC_CAP_INTERNAL|MALLOC_CAP_32BIT, 0 }, false, false}, +}; + +#ifdef CONFIG_ESP32C3_MEMPROT_FEATURE //TODO ESP32-C3 IDF-2092 +#define SOC_MEMORY_TYPE_DEFAULT 0 +#else +#define SOC_MEMORY_TYPE_DEFAULT 2 +#endif + +const size_t soc_memory_type_count = sizeof(soc_memory_types) / sizeof(soc_memory_type_desc_t); + +/** + * @brief Region descriptors. These describe all regions of memory available, and map them to a type in the above type. + * + * @note Because of requirements in the coalescing code which merges adjacent regions, + * this list should always be sorted from low to high by start address. + * + */ +const soc_memory_region_t soc_memory_regions[] = { + { 0x3FC80000, 0x20000, SOC_MEMORY_TYPE_DEFAULT, 0x40380000}, //Block 4, can be remapped to ROM, can be used as trace memory + { 0x3FCA0000, 0x20000, SOC_MEMORY_TYPE_DEFAULT, 0x403A0000}, //Block 5, can be remapped to ROM, can be used as trace memory + { 0x3FCC0000, 0x20000, 1, 0x403C0000}, //Block 9, can be used as trace memory +#ifdef CONFIG_ESP32C3_ALLOW_RTC_FAST_MEM_AS_HEAP + { 0x50000000, 0x2000, 5, 0}, //Fast RTC memory +#endif +}; + +const size_t soc_memory_region_count = sizeof(soc_memory_regions) / sizeof(soc_memory_region_t); + + +extern int _data_start, _heap_start, _iram_start, _iram_end; + +/** + * Reserved memory regions. + * These are removed from the soc_memory_regions array when heaps are created. + * + */ + +// Static data region. DRAM used by data+bss and possibly rodata +SOC_RESERVE_MEMORY_REGION((intptr_t)&_data_start, (intptr_t)&_heap_start, dram_data); + +// Target has a big D/IRAM region, the part used by code is reserved +// The address of the D/I bus are in the same order, directly shift IRAM address to get reserved DRAM address +#define I_D_OFFSET (SOC_DIRAM_IRAM_LOW - SOC_DIRAM_DRAM_LOW) +SOC_RESERVE_MEMORY_REGION((intptr_t)&_iram_start - I_D_OFFSET, (intptr_t)&_iram_end - I_D_OFFSET, iram_code); + +#endif // BOOTLOADER_BUILD