// limitations under the License.
#include <stdint.h>
+#include <sys/cdefs.h>
+#include <sys/time.h>
#include "sdkconfig.h"
#include "esp_attr.h"
#include "esp_log.h"
+#include "esp_clk.h"
#include "rom/ets_sys.h"
#include "rom/uart.h"
+#include "rom/rtc.h"
#include "soc/soc.h"
#include "soc/rtc.h"
#include "soc/rtc_cntl_reg.h"
g_ticks_per_us_app = ticks_per_us;
}
-/* This is a cached value of RTC slow clock period; it is updated by
- * the select_rtc_slow_clk function at start up. This cached value is used in
- * other places, like time syscalls and deep sleep.
- */
-static uint32_t s_rtc_slow_clk_cal = 0;
-
static void select_rtc_slow_clk(rtc_slow_freq_t slow_clk)
{
if (slow_clk == RTC_SLOW_FREQ_32K_XTAL) {
ESP_EARLY_LOGD(TAG, "32k oscillator ready, wait=%d", wait);
}
rtc_clk_slow_freq_set(slow_clk);
+ uint32_t cal_val;
if (SLOW_CLK_CAL_CYCLES > 0) {
/* TODO: 32k XTAL oscillator has some frequency drift at startup.
* Improve calibration routine to wait until the frequency is stable.
*/
- s_rtc_slow_clk_cal = rtc_clk_cal(RTC_CAL_RTC_MUX, SLOW_CLK_CAL_CYCLES);
+ cal_val = rtc_clk_cal(RTC_CAL_RTC_MUX, SLOW_CLK_CAL_CYCLES);
} else {
const uint64_t cal_dividend = (1ULL << RTC_CLK_CAL_FRACT) * 1000000ULL;
- s_rtc_slow_clk_cal = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz());
+ cal_val = (uint32_t) (cal_dividend / rtc_clk_slow_freq_get_hz());
}
- ESP_EARLY_LOGD(TAG, "RTC_SLOW_CLK calibration value: %d", s_rtc_slow_clk_cal);
-}
-
-uint32_t esp_clk_slowclk_cal_get()
-{
- return s_rtc_slow_clk_cal;
+ ESP_EARLY_LOGD(TAG, "RTC_SLOW_CLK calibration value: %d", cal_val);
+ esp_clk_slowclk_cal_set(cal_val);
}
/**
- * @brief Get the cached calibration value of RTC slow clock
+ * @brief Get the calibration value of RTC slow clock
*
* The value is in the same format as returned by rtc_clk_cal (microseconds,
* in Q13.19 fixed-point format).
*/
uint32_t esp_clk_slowclk_cal_get();
+
+/**
+ * @brief Update the calibration value of RTC slow clock
+ *
+ * The value has to be in the same format as returned by rtc_clk_cal (microseconds,
+ * in Q13.19 fixed-point format).
+ * This value is used by timekeeping functions (such as gettimeofday) to
+ * calculate current time based on RTC counter value.
+ * @param value calibration value obtained using rtc_clk_cal
+ */
+void esp_clk_slowclk_cal_set(uint32_t value);
+
* 0x3ff80000(0x400c0000) Fast 8192 deep sleep entry code
*
*************************************************************************************
- * Rtc store registers usage
- * RTC_CNTL_STORE0_REG
- * RTC_CNTL_STORE1_REG
+ * RTC store registers usage
+ * RTC_CNTL_STORE0_REG Reserved
+ * RTC_CNTL_STORE1_REG RTC_SLOW_CLK calibration value
* RTC_CNTL_STORE2_REG Boot time, low word
* RTC_CNTL_STORE3_REG Boot time, high word
* RTC_CNTL_STORE4_REG External XTAL frequency
*************************************************************************************
*/
+#define RTC_SLOW_CLK_CAL_REG RTC_CNTL_STORE1_REG
#define RTC_BOOT_TIME_LOW_REG RTC_CNTL_STORE2_REG
#define RTC_BOOT_TIME_HIGH_REG RTC_CNTL_STORE3_REG
#define RTC_XTAL_FREQ_REG RTC_CNTL_STORE4_REG
}
#endif //defined(WITH_RTC) || defined(WITH_FRC1)
+
+void esp_clk_slowclk_cal_set(uint32_t new_cal)
+{
+#if defined(WITH_RTC)
+ /* To force monotonic time values even when clock calibration value changes,
+ * we adjust boot time, given current time and the new calibration value:
+ * T = boot_time_old + cur_cal * ticks / 2^19
+ * T = boot_time_adj + new_cal * ticks / 2^19
+ * which results in:
+ * boot_time_adj = boot_time_old + ticks * (cur_cal - new_cal) / 2^19
+ */
+ const int64_t ticks = (int64_t) rtc_time_get();
+ const uint32_t cur_cal = REG_READ(RTC_SLOW_CLK_CAL_REG);
+ int32_t cal_diff = (int32_t) (cur_cal - new_cal);
+ int64_t boot_time_diff = ticks * cal_diff / (1LL << RTC_CLK_CAL_FRACT);
+ uint64_t boot_time_adj = get_boot_time() + boot_time_diff;
+ set_boot_time(boot_time_adj);
+#endif // WITH_RTC
+ REG_WRITE(RTC_SLOW_CLK_CAL_REG, new_cal);
+}
+
+uint32_t esp_clk_slowclk_cal_get()
+{
+ return REG_READ(RTC_SLOW_CLK_CAL_REG);
+}
+
void esp_setup_time_syscalls()
{
#if defined( WITH_FRC1 )