1 /*
2 * SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <zephyr/kernel.h>
7 #include <zephyr/logging/log.h>
8
9 #include <stdlib.h>
10
11 #include "sys/param.h"
12 #include "esp_timer_impl.h"
13 #include "esp_timer.h"
14 #include "esp_err.h"
15 #include "esp_system.h"
16 #include "esp_task.h"
17 #include "esp_attr.h"
18 #include "esp_intr_alloc.h"
19 #include "esp_log.h"
20 #include "esp_private/esp_clk.h"
21 #include "esp_private/periph_ctrl.h"
22 #include "soc/soc.h"
23 #include "soc/timer_group_reg.h"
24 #include "soc/rtc.h"
25
26 #ifdef CONFIG_SOC_SERIES_ESP32C3
27 #include <zephyr/drivers/interrupt_controller/intc_esp32c3.h>
28 #else
29 #include <zephyr/drivers/interrupt_controller/intc_esp32.h>
30 #endif
31
32 /**
33 * @file esp_timer_lac.c
34 * @brief Implementation of chip-specific part of esp_timer
35 *
36 * This implementation uses TG0 LAC timer of the ESP32. This timer is
37 * a 64-bit up-counting timer, with a programmable compare value (called 'alarm'
38 * hereafter). When the timer reaches compare value, interrupt is raised.
39 * The timer can be configured to produce an edge or a level interrupt.
40 */
41
42 /* Selects which Timer Group peripheral to use */
43 #define LACT_MODULE 0
44
45 #if LACT_MODULE == 0
46 #define INTR_SOURCE_LACT ETS_TG0_LACT_LEVEL_INTR_SOURCE
47 #define PERIPH_LACT PERIPH_TIMG0_MODULE
48 #elif LACT_MODULE == 1
49 #define INTR_SOURCE_LACT ETS_TG1_LACT_LEVEL_INTR_SOURCE
50 #define PERIPH_LACT PERIPH_TIMG1_MODULE
51 #else
52 #error "Incorrect the number of LACT module (only 0 or 1)"
53 #endif
54
55 /* Desired number of timer ticks per microsecond.
56 * This value should be small enough so that all possible APB frequencies
57 * could be divided by it without remainder.
58 * On the other hand, the smaller this value is, the longer we need to wait
59 * after setting UPDATE_REG before the timer value can be read.
60 * If TICKS_PER_US == 1, then we need to wait up to 1 microsecond, which
61 * makes esp_timer_impl_get_time function take too much time.
62 * The value TICKS_PER_US == 2 allows for most of the APB frequencies, and
63 * allows reading the counter quickly enough.
64 */
65 #define TICKS_PER_US 2
66
67 /* Shorter register names, used in this file */
68 #define CONFIG_REG (TIMG_LACTCONFIG_REG(LACT_MODULE))
69 #define RTC_STEP_REG (TIMG_LACTRTC_REG(LACT_MODULE))
70 #define ALARM_LO_REG (TIMG_LACTALARMLO_REG(LACT_MODULE))
71 #define ALARM_HI_REG (TIMG_LACTALARMHI_REG(LACT_MODULE))
72 #define COUNT_LO_REG (TIMG_LACTLO_REG(LACT_MODULE))
73 #define COUNT_HI_REG (TIMG_LACTHI_REG(LACT_MODULE))
74 #define UPDATE_REG (TIMG_LACTUPDATE_REG(LACT_MODULE))
75 #define LOAD_REG (TIMG_LACTLOAD_REG(LACT_MODULE))
76 #define LOAD_LO_REG (TIMG_LACTLOADLO_REG(LACT_MODULE))
77 #define LOAD_HI_REG (TIMG_LACTLOADHI_REG(LACT_MODULE))
78 #define INT_ENA_REG (TIMG_INT_ENA_TIMERS_REG(LACT_MODULE))
79 #define INT_ST_REG (TIMG_INT_ST_TIMERS_REG(LACT_MODULE))
80 #define INT_CLR_REG (TIMG_INT_CLR_TIMERS_REG(LACT_MODULE))
81
82 /* Function prototype for alarm interrupt handler function */
83 typedef void (*alarm_intr_handler_t)(const void *arg);
84
85 /* Helper type to convert between a 64-bit value and a pair of 32-bit values without shifts and masks */
86 typedef struct {
87 union {
88 struct {
89 uint32_t lo;
90 uint32_t hi;
91 };
92 uint64_t val;
93 };
94 } timer_64b_reg_t;
95
96 static const char* TAG = "esp_timer_impl";
97
98 #define NOT_USED 0xBAD00FAD
99
100 /* Function from the upper layer to be called when the interrupt happens.
101 * Registered in esp_timer_impl_init.
102 */
103 static alarm_intr_handler_t s_alarm_handler;
104
105 /* Spinlock used to protect access to the hardware registers. */
106 extern unsigned int s_time_update_lock;
107
108 /* Alarm values to generate interrupt on match */
109 extern uint64_t timestamp_id[2];
110
esp_timer_impl_get_counter_reg(void)111 uint64_t IRAM_ATTR esp_timer_impl_get_counter_reg(void)
112 {
113 uint32_t lo, hi;
114 uint32_t lo_start = REG_READ(COUNT_LO_REG);
115 uint32_t div = REG_GET_FIELD(CONFIG_REG, TIMG_LACT_DIVIDER);
116 /* The peripheral doesn't have a bit to indicate that the update is done, so we poll the
117 * lower 32 bit part of the counter until it changes, or a timeout expires.
118 */
119 REG_WRITE(UPDATE_REG, 1);
120 do {
121 lo = REG_READ(COUNT_LO_REG);
122 } while (lo == lo_start && div-- > 0);
123
124 /* Since this function is called without a critical section, verify that LO and HI
125 * registers are consistent. That is, if an interrupt happens between reading LO and
126 * HI registers, and esp_timer_impl_get_time is called from an ISR, then try to
127 * detect this by the change in LO register value, and re-read both registers.
128 */
129 do {
130 lo_start = lo;
131 hi = REG_READ(COUNT_HI_REG);
132 lo = REG_READ(COUNT_LO_REG);
133 } while (lo != lo_start);
134
135 timer_64b_reg_t result = {
136 .lo = lo,
137 .hi = hi
138 };
139 return result.val;
140 }
141
esp_timer_impl_get_time(void)142 int64_t IRAM_ATTR esp_timer_impl_get_time(void)
143 {
144 return esp_timer_impl_get_counter_reg() / TICKS_PER_US;
145 }
146
147 int64_t esp_timer_get_time(void) __attribute__((alias("esp_timer_impl_get_time")));
148
esp_timer_impl_set_alarm_id(uint64_t timestamp,unsigned alarm_id)149 void IRAM_ATTR esp_timer_impl_set_alarm_id(uint64_t timestamp, unsigned alarm_id)
150 {
151 s_time_update_lock = irq_lock();
152 timestamp_id[alarm_id] = timestamp;
153 timestamp = MIN(timestamp_id[0], timestamp_id[1]);
154 if (timestamp != UINT64_MAX) {
155 int64_t offset = TICKS_PER_US * 2;
156 uint64_t now_time = esp_timer_impl_get_counter_reg();
157 timer_64b_reg_t alarm = { .val = MAX(timestamp * TICKS_PER_US, now_time + offset) };
158 do {
159 REG_CLR_BIT(CONFIG_REG, TIMG_LACT_ALARM_EN);
160 REG_WRITE(ALARM_LO_REG, alarm.lo);
161 REG_WRITE(ALARM_HI_REG, alarm.hi);
162 REG_SET_BIT(CONFIG_REG, TIMG_LACT_ALARM_EN);
163 now_time = esp_timer_impl_get_counter_reg();
164 int64_t delta = (int64_t)alarm.val - (int64_t)now_time;
165 if (delta <= 0 && REG_GET_FIELD(INT_ST_REG, TIMG_LACT_INT_ST) == 0) {
166 // new alarm is less than the counter and the interrupt flag is not set
167 offset += llabs(delta) + TICKS_PER_US * 2;
168 alarm.val = now_time + offset;
169 } else {
170 // finish if either (alarm > counter) or the interrupt flag is already set.
171 break;
172 }
173 } while(1);
174 }
175 irq_unlock(s_time_update_lock);
176 }
177
timer_alarm_isr(void * arg)178 static void IRAM_ATTR timer_alarm_isr(void *arg)
179 {
180 /* Clear interrupt status */
181 REG_WRITE(INT_CLR_REG, TIMG_LACT_INT_CLR);
182 /* Call the upper layer handler */
183 (*s_alarm_handler)(arg);
184 }
185
esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us)186 void IRAM_ATTR esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us)
187 {
188 s_time_update_lock = irq_lock();
189 assert(apb_ticks_per_us >= 3 && "divider value too low");
190 assert(apb_ticks_per_us % TICKS_PER_US == 0 && "APB frequency (in MHz) should be divisible by TICK_PER_US");
191 REG_SET_FIELD(CONFIG_REG, TIMG_LACT_DIVIDER, apb_ticks_per_us / TICKS_PER_US);
192 irq_unlock(s_time_update_lock);
193 }
194
esp_timer_impl_set(uint64_t new_us)195 void esp_timer_impl_set(uint64_t new_us)
196 {
197 s_time_update_lock = irq_lock();
198 timer_64b_reg_t dst = { .val = new_us * TICKS_PER_US };
199 REG_WRITE(LOAD_LO_REG, dst.lo);
200 REG_WRITE(LOAD_HI_REG, dst.hi);
201 REG_WRITE(LOAD_REG, 1);
202 irq_unlock(s_time_update_lock);
203 }
204
esp_timer_impl_advance(int64_t time_diff_us)205 void esp_timer_impl_advance(int64_t time_diff_us)
206 {
207 uint64_t now = esp_timer_impl_get_time();
208 esp_timer_impl_set(now + time_diff_us);
209 }
210
esp_timer_impl_early_init(void)211 esp_err_t esp_timer_impl_early_init(void)
212 {
213 periph_module_enable(PERIPH_LACT);
214
215 REG_WRITE(CONFIG_REG, 0);
216 REG_WRITE(LOAD_LO_REG, 0);
217 REG_WRITE(LOAD_HI_REG, 0);
218 REG_WRITE(ALARM_LO_REG, UINT32_MAX);
219 REG_WRITE(ALARM_HI_REG, UINT32_MAX);
220 REG_WRITE(LOAD_REG, 1);
221 REG_SET_BIT(INT_CLR_REG, TIMG_LACT_INT_CLR);
222 REG_SET_FIELD(CONFIG_REG, TIMG_LACT_DIVIDER, APB_CLK_FREQ / 1000000 / TICKS_PER_US);
223 REG_SET_BIT(CONFIG_REG, TIMG_LACT_INCREASE |
224 TIMG_LACT_LEVEL_INT_EN |
225 TIMG_LACT_EN);
226
227 return ESP_OK;
228 }
229
esp_timer_impl_init(intr_handler_t alarm_handler)230 esp_err_t esp_timer_impl_init(intr_handler_t alarm_handler)
231 {
232 int isr_flags = ((1 << 1) & ESP_INTR_FLAG_LEVELMASK) | ESP_INTR_FLAG_IRAM;
233
234 esp_err_t err = esp_intr_alloc(INTR_SOURCE_LACT, isr_flags,
235 (intr_handler_t)timer_alarm_isr, NULL, NULL);
236
237 if (err != ESP_OK) {
238 ESP_EARLY_LOGE(TAG, "Can not allocate ISR handler (0x%0x)", err);
239 return err;
240 }
241
242 if (s_alarm_handler == NULL) {
243 s_alarm_handler = (alarm_intr_handler_t) alarm_handler;
244 /* In theory, this needs a shared spinlock with the timer group driver.
245 * However since esp_timer_impl_init is called early at startup, this
246 * will not cause issues in practice.
247 */
248 REG_SET_BIT(INT_ENA_REG, TIMG_LACT_INT_ENA);
249
250 esp_timer_impl_update_apb_freq(esp_clk_apb_freq() / 1000000);
251
252 // Set the step for the sleep mode when the timer will work
253 // from a slow_clk frequency instead of the APB frequency.
254 uint32_t slowclk_ticks_per_us = esp_clk_slowclk_cal_get() * TICKS_PER_US;
255 REG_SET_FIELD(RTC_STEP_REG, TIMG_LACT_RTC_STEP_LEN, slowclk_ticks_per_us);
256 }
257
258 return err;
259 }
260
esp_timer_impl_deinit(void)261 void esp_timer_impl_deinit(void)
262 {
263 REG_WRITE(CONFIG_REG, 0);
264 REG_SET_BIT(INT_CLR_REG, TIMG_LACT_INT_CLR);
265 /* TODO: also clear TIMG_LACT_INT_ENA; however see the note in esp_timer_impl_init. */
266 s_alarm_handler = NULL;
267 }
268
esp_timer_impl_get_alarm_reg(void)269 uint64_t esp_timer_impl_get_alarm_reg(void)
270 {
271 s_time_update_lock = irq_lock();
272 timer_64b_reg_t alarm = {
273 .lo = REG_READ(ALARM_LO_REG),
274 .hi = REG_READ(ALARM_HI_REG)
275 };
276 irq_unlock(s_time_update_lock);
277 return alarm.val;
278 }
279
280 void esp_timer_private_update_apb_freq(uint32_t apb_ticks_per_us) __attribute__((alias("esp_timer_impl_update_apb_freq")));
281 void esp_timer_private_set(uint64_t new_us) __attribute__((alias("esp_timer_impl_set")));
282 void esp_timer_private_advance(int64_t time_diff_us) __attribute__((alias("esp_timer_impl_advance")));
283