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