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