esp_clk.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /*
  2. * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdint.h>
  7. #include <string.h>
  8. #include <sys/param.h>
  9. #include <sys/lock.h>
  10. #include "freertos/FreeRTOS.h"
  11. #include "esp_attr.h"
  12. #include "soc/rtc.h"
  13. #include "soc/soc_caps.h"
  14. #include "esp_rom_caps.h"
  15. #include "esp_rom_sys.h"
  16. #include "esp_private/esp_clk.h"
  17. #include "hal/clk_tree_ll.h"
  18. #if CONFIG_IDF_TARGET_ESP32
  19. #include "esp32/rom/rtc.h"
  20. #include "esp32/rtc.h"
  21. #elif CONFIG_IDF_TARGET_ESP32S2
  22. #include "esp32s2/rom/rtc.h"
  23. #include "esp32s2/rtc.h"
  24. #elif CONFIG_IDF_TARGET_ESP32S3
  25. #include "esp32s3/rom/rtc.h"
  26. #include "esp32s3/rtc.h"
  27. #elif CONFIG_IDF_TARGET_ESP32C3
  28. #include "esp32c3/rom/rtc.h"
  29. #include "esp32c3/rtc.h"
  30. #elif CONFIG_IDF_TARGET_ESP32C2
  31. #include "esp32c2/rom/rtc.h"
  32. #include "esp32c2/rtc.h"
  33. #elif CONFIG_IDF_TARGET_ESP32C6
  34. #include "esp32c6/rom/rtc.h"
  35. #include "esp32c6/rtc.h"
  36. #elif CONFIG_IDF_TARGET_ESP32H2
  37. #include "esp32h2/rom/rtc.h"
  38. #include "esp32h2/rtc.h"
  39. #endif
  40. #define MHZ (1000000)
  41. // g_ticks_us defined in ROMs for PRO and APP CPU
  42. extern uint32_t g_ticks_per_us_pro;
  43. static portMUX_TYPE s_esp_rtc_time_lock = portMUX_INITIALIZER_UNLOCKED;
  44. #if SOC_RTC_MEM_SUPPORTED
  45. typedef struct {
  46. uint64_t rtc_time_us;
  47. uint64_t rtc_last_ticks;
  48. uint32_t reserve;
  49. uint32_t checksum;
  50. } retain_mem_t;
  51. _Static_assert(sizeof(retain_mem_t) == 24, "retain_mem_t must be 24 bytes");
  52. _Static_assert(offsetof(retain_mem_t, checksum) == sizeof(retain_mem_t) - sizeof(uint32_t), "Wrong offset for checksum field in retain_mem_t structure");
  53. static __attribute__((section(".rtc_timer_data_in_rtc_mem"))) retain_mem_t s_rtc_timer_retain_mem;
  54. static uint32_t calc_checksum(void)
  55. {
  56. uint32_t checksum = 0;
  57. uint32_t *data = (uint32_t*) &s_rtc_timer_retain_mem;
  58. for (uint32_t i = 0; i < (sizeof(retain_mem_t) - sizeof(s_rtc_timer_retain_mem.checksum)) / 4; i++) {
  59. checksum = ((checksum << 5) - checksum) ^ data[i];
  60. }
  61. return checksum;
  62. }
  63. #define IS_RETAIN_MEM_VALID() (s_rtc_timer_retain_mem.checksum == calc_checksum())
  64. #endif // SOC_RTC_MEM_SUPPORTED
  65. inline static int IRAM_ATTR s_get_cpu_freq_mhz(void)
  66. {
  67. #if ESP_ROM_GET_CLK_FREQ
  68. return esp_rom_get_cpu_ticks_per_us();
  69. #else
  70. return g_ticks_per_us_pro;
  71. #endif
  72. }
  73. int IRAM_ATTR esp_clk_cpu_freq(void)
  74. {
  75. return s_get_cpu_freq_mhz() * MHZ;
  76. }
  77. int IRAM_ATTR esp_clk_apb_freq(void)
  78. {
  79. // TODO: IDF-5173 Require cleanup, implementation should be unified
  80. #if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2
  81. return rtc_clk_apb_freq_get();
  82. #else
  83. return MIN(s_get_cpu_freq_mhz() * MHZ, APB_CLK_FREQ);
  84. #endif
  85. }
  86. int IRAM_ATTR esp_clk_xtal_freq(void)
  87. {
  88. return rtc_clk_xtal_freq_get() * MHZ;
  89. }
  90. uint64_t esp_rtc_get_time_us(void)
  91. {
  92. portENTER_CRITICAL_SAFE(&s_esp_rtc_time_lock);
  93. const uint32_t cal = esp_clk_slowclk_cal_get();
  94. #if SOC_RTC_MEM_SUPPORTED
  95. static bool first_call = true;
  96. if (cal == 0 || (first_call && !IS_RETAIN_MEM_VALID())) {
  97. /*
  98. If cal is 0, then this is the first power-up. Cal is keeping valid
  99. after reboot and deepsleep. If s_rtc_timer_retain_mem is invalid, it means
  100. that something unexpected happened (the structure was moved around
  101. after OTA update). To keep the system time valid even after OTA we
  102. reset the s_rtc_timer_retain_mem. But the resetting can also lead to some
  103. drift of the system time, because only the last current calibration
  104. value will be applied to all rtc ticks. To mitigate this effect you
  105. might need updating of the system time (via SNTP).
  106. */
  107. memset(&s_rtc_timer_retain_mem, 0, sizeof(retain_mem_t));
  108. }
  109. first_call = false;
  110. const uint64_t rtc_this_ticks = rtc_time_get();
  111. const uint64_t ticks = rtc_this_ticks - s_rtc_timer_retain_mem.rtc_last_ticks;
  112. #else
  113. const uint64_t ticks = rtc_time_get();
  114. #endif
  115. /* RTC counter result is up to 2^48, calibration factor is up to 2^24,
  116. * for a 32kHz clock. We need to calculate (assuming no overflow):
  117. * (ticks * cal) >> RTC_CLK_CAL_FRACT
  118. *
  119. * An overflow in the (ticks * cal) multiplication would cause time to
  120. * wrap around after approximately 13 days, which is probably not enough
  121. * for some applications.
  122. * Therefore multiplication is split into two terms, for the lower 32-bit
  123. * and the upper 16-bit parts of "ticks", i.e.:
  124. * ((ticks_low + 2^32 * ticks_high) * cal) >> RTC_CLK_CAL_FRACT
  125. */
  126. const uint64_t ticks_low = ticks & UINT32_MAX;
  127. const uint64_t ticks_high = ticks >> 32;
  128. const uint64_t delta_time_us = ((ticks_low * cal) >> RTC_CLK_CAL_FRACT) +
  129. ((ticks_high * cal) << (32 - RTC_CLK_CAL_FRACT));
  130. #if SOC_RTC_MEM_SUPPORTED
  131. s_rtc_timer_retain_mem.rtc_time_us += delta_time_us;
  132. s_rtc_timer_retain_mem.rtc_last_ticks = rtc_this_ticks;
  133. s_rtc_timer_retain_mem.checksum = calc_checksum();
  134. uint64_t esp_rtc_time_us = s_rtc_timer_retain_mem.rtc_time_us;
  135. portEXIT_CRITICAL_SAFE(&s_esp_rtc_time_lock);
  136. return esp_rtc_time_us;
  137. #else
  138. uint64_t esp_rtc_time_us = delta_time_us + clk_ll_rtc_slow_load_rtc_fix_us();
  139. portEXIT_CRITICAL_SAFE(&s_esp_rtc_time_lock);
  140. return esp_rtc_time_us;
  141. #endif
  142. }
  143. void esp_clk_slowclk_cal_set(uint32_t new_cal)
  144. {
  145. #if defined(CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER)
  146. /* To force monotonic time values even when clock calibration value changes,
  147. * we adjust esp_rtc_time
  148. */
  149. #if SOC_RTC_MEM_SUPPORTED
  150. esp_rtc_get_time_us();
  151. #else
  152. portENTER_CRITICAL_SAFE(&s_esp_rtc_time_lock);
  153. uint32_t old_cal = clk_ll_rtc_slow_load_cal();
  154. if (old_cal != 0) {
  155. /**
  156. * The logic of time correction is:
  157. * old_rtc_us = ticks * old_cal >> RTC_CLK_CAL_FRACT + old_fix_us
  158. * new_rtc_us = ticks * new_cal >> RTC_CLK_CAL_FRACT + new_fix_us
  159. *
  160. * Keep "old_rtc_us == new_rtc_us" to make time monotonically increasing,
  161. * then we can get new_fix_us:
  162. * new_fix_us = (ticks * old_cal >> RTC_CLK_CAL_FRACT + old_fix_us) - (ticks * new_cal >> RTC_CLK_CAL_FRACT)
  163. */
  164. uint64_t ticks = rtc_time_get();
  165. const uint64_t ticks_low = ticks & UINT32_MAX;
  166. const uint64_t ticks_high = ticks >> 32;
  167. uint64_t old_fix_us = clk_ll_rtc_slow_load_rtc_fix_us();
  168. uint64_t new_fix_us;
  169. old_fix_us += ((ticks_low * old_cal) >> RTC_CLK_CAL_FRACT) + ((ticks_high * old_cal) << (32 - RTC_CLK_CAL_FRACT));
  170. new_fix_us = ((ticks_low * new_cal) >> RTC_CLK_CAL_FRACT) + ((ticks_high * new_cal) << (32 - RTC_CLK_CAL_FRACT));
  171. new_fix_us = old_fix_us - new_fix_us;
  172. clk_ll_rtc_slow_store_rtc_fix_us(new_fix_us);
  173. }
  174. portEXIT_CRITICAL_SAFE(&s_esp_rtc_time_lock);
  175. #endif // SOC_RTC_MEM_SUPPORTED
  176. #endif // CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER
  177. clk_ll_rtc_slow_store_cal(new_cal);
  178. }
  179. uint32_t esp_clk_slowclk_cal_get(void)
  180. {
  181. return clk_ll_rtc_slow_load_cal();
  182. }
  183. uint64_t esp_clk_rtc_time(void)
  184. {
  185. #ifdef CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER
  186. return esp_rtc_get_time_us();
  187. #else
  188. return 0;
  189. #endif
  190. }
  191. void esp_clk_private_lock(void)
  192. {
  193. portENTER_CRITICAL(&s_esp_rtc_time_lock);
  194. }
  195. void esp_clk_private_unlock(void)
  196. {
  197. portEXIT_CRITICAL(&s_esp_rtc_time_lock);
  198. }