1 /*
2  * SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <sys/param.h>
9 #include "sdkconfig.h"
10 #include "esp_timer_impl.h"
11 #include "esp_err.h"
12 #include "esp_timer.h"
13 #include "esp_attr.h"
14 #include "esp_log.h"
15 #include "esp_compiler.h"
16 #include "soc/periph_defs.h"
17 #include "soc/soc_caps.h"
18 #include "esp_private/esp_clk.h"
19 #include "esp_private/systimer.h"
20 #include "esp_private/periph_ctrl.h"
21 #include "hal/systimer_ll.h"
22 #include "hal/systimer_types.h"
23 #include "hal/systimer_hal.h"
24 
25 #if defined(CONFIG_SOC_SERIES_ESP32C2) || \
26 	defined(CONFIG_SOC_SERIES_ESP32C3) || \
27 	defined(CONFIG_SOC_SERIES_ESP32C6)
28 #include <zephyr/drivers/interrupt_controller/intc_esp32c3.h>
29 #define ISR_HANDLER isr_handler_t
30 #else
31 #include <zephyr/drivers/interrupt_controller/intc_esp32.h>
32 #define ISR_HANDLER intr_handler_t
33 #endif
34 
35 /**
36  * @file esp_timer_systimer.c
37  * @brief Implementation of esp_timer using systimer.
38  *
39  * This timer is a 64-bit up-counting timer, with a programmable compare value (called 'alarm' hereafter).
40  * When the timer reaches compare value, interrupt is raised.
41  * The timer can be configured to produce an edge interrupt.
42  *
43  * @note systimer counter0 and alarm2 are adopted to implemented esp_timer
44  */
45 
46 static const char *TAG = "esp_timer_systimer";
47 
48 #define NOT_USED 0xBAD00FAD
49 
50 /* Function from the upper layer to be called when the interrupt happens.
51  * Registered in esp_timer_impl_init.
52  */
53 static ISR_HANDLER s_alarm_handler = NULL;
54 
55 /* Systimer HAL layer object */
56 static systimer_hal_context_t systimer_hal;
57 
58 /* Spinlock used to protect access to the hardware registers. */
59 extern unsigned int s_time_update_lock;
60 
61 /* Alarm values to generate interrupt on match */
62 extern uint64_t timestamp_id[2];
63 
esp_timer_impl_get_counter_reg(void)64 uint64_t IRAM_ATTR esp_timer_impl_get_counter_reg(void)
65 {
66     return systimer_hal_get_counter_value(&systimer_hal, SYSTIMER_COUNTER_ESPTIMER);
67 }
68 
esp_timer_impl_get_time(void)69 int64_t IRAM_ATTR esp_timer_impl_get_time(void)
70 {
71     // we hope the execution time of this function won't > 1us
72     // thus, to save one function call, we didn't use the existing `systimer_hal_get_time`
73     return systimer_hal.ticks_to_us(systimer_hal_get_counter_value(&systimer_hal, SYSTIMER_COUNTER_ESPTIMER));
74 }
75 
76 int64_t esp_timer_get_time(void) __attribute__((alias("esp_timer_impl_get_time")));
77 
esp_timer_impl_set_alarm_id(uint64_t timestamp,unsigned alarm_id)78 void IRAM_ATTR esp_timer_impl_set_alarm_id(uint64_t timestamp, unsigned alarm_id)
79 {
80     s_time_update_lock = irq_lock();
81     timestamp_id[alarm_id] = timestamp;
82     timestamp = MIN(timestamp_id[0], timestamp_id[1]);
83     systimer_hal_set_alarm_target(&systimer_hal, SYSTIMER_ALARM_ESPTIMER, timestamp);
84     irq_unlock(s_time_update_lock);
85 }
86 
timer_alarm_isr(void * arg)87 static void IRAM_ATTR timer_alarm_isr(void *arg)
88 {
89     // clear the interrupt
90     systimer_ll_clear_alarm_int(systimer_hal.dev, SYSTIMER_ALARM_ESPTIMER);
91     /* Call the upper layer handler */
92     (*s_alarm_handler)(arg);
93 }
94 
esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us)95 void IRAM_ATTR esp_timer_impl_update_apb_freq(uint32_t apb_ticks_per_us)
96 {
97 #if !SOC_SYSTIMER_FIXED_DIVIDER
98     systimer_hal_on_apb_freq_update(&systimer_hal, apb_ticks_per_us);
99 #endif
100 }
101 
esp_timer_impl_set(uint64_t new_us)102 void esp_timer_impl_set(uint64_t new_us)
103 {
104     s_time_update_lock = irq_lock();
105     systimer_counter_value_t new_count = {
106         .val = systimer_hal.us_to_ticks(new_us),
107     };
108     systimer_ll_set_counter_value(systimer_hal.dev, SYSTIMER_COUNTER_ESPTIMER, new_count.val);
109     systimer_ll_apply_counter_value(systimer_hal.dev, SYSTIMER_COUNTER_ESPTIMER);
110     irq_unlock(s_time_update_lock);
111 }
112 
esp_timer_impl_advance(int64_t time_diff_us)113 void esp_timer_impl_advance(int64_t time_diff_us)
114 {
115     s_time_update_lock = irq_lock();
116     systimer_hal_counter_value_advance(&systimer_hal, SYSTIMER_COUNTER_ESPTIMER, time_diff_us);
117     irq_unlock(s_time_update_lock);
118 }
119 
esp_timer_impl_early_init(void)120 esp_err_t esp_timer_impl_early_init(void)
121 {
122     periph_module_enable(PERIPH_SYSTIMER_MODULE);
123     systimer_hal_tick_rate_ops_t ops = {
124         .ticks_to_us = systimer_ticks_to_us,
125         .us_to_ticks = systimer_us_to_ticks,
126     };
127     systimer_hal_init(&systimer_hal);
128     systimer_hal_set_tick_rate_ops(&systimer_hal, &ops);
129 
130 #if !SOC_SYSTIMER_FIXED_DIVIDER
131     assert(esp_clk_xtal_freq() == (40 * 1000000) &&
132            "update the step for xtal to support other XTAL:APB frequency ratios");
133     systimer_hal_set_steps_per_tick(&systimer_hal, 0, 2); // for xtal
134     systimer_hal_set_steps_per_tick(&systimer_hal, 1, 1); // for pll
135 #endif
136 
137     systimer_hal_enable_counter(&systimer_hal, SYSTIMER_COUNTER_ESPTIMER);
138     systimer_hal_select_alarm_mode(&systimer_hal, SYSTIMER_ALARM_ESPTIMER, SYSTIMER_ALARM_MODE_ONESHOT);
139     systimer_hal_connect_alarm_counter(&systimer_hal, SYSTIMER_ALARM_ESPTIMER, SYSTIMER_COUNTER_ESPTIMER);
140 
141     for (unsigned cpuid = 0; cpuid < SOC_CPU_CORES_NUM; ++cpuid) {
142         systimer_hal_counter_can_stall_by_cpu(&systimer_hal, SYSTIMER_COUNTER_ESPTIMER, cpuid, (cpuid < CONFIG_MP_MAX_NUM_CPUS) ? true : false);
143     }
144 
145     return ESP_OK;
146 }
147 
esp_timer_impl_init(ISR_HANDLER alarm_handler)148 esp_err_t esp_timer_impl_init(ISR_HANDLER alarm_handler)
149 {
150     int isr_flags = 0  /* ZEP-795 (GH #74368): esp_timer ISR priority relaxed to avoid
151                         * IRQ not being allocated when several peripherals are enabled
152                         */
153 #if !SOC_SYSTIMER_INT_LEVEL
154                     | ESP_INTR_FLAG_EDGE
155 #endif
156                     | ESP_INTR_FLAG_IRAM;
157 
158 	esp_err_t err = esp_intr_alloc(ETS_SYSTIMER_TARGET2_EDGE_INTR_SOURCE, isr_flags,
159 								   (ISR_HANDLER)timer_alarm_isr, NULL, NULL);
160 	if (err != ESP_OK) {
161         ESP_EARLY_LOGE(TAG, "esp_intr_alloc failed (0x%x)", err);
162         return err;
163     }
164 
165     if (s_alarm_handler == NULL) {
166         s_alarm_handler = alarm_handler;
167         /* TODO: if SYSTIMER is used for anything else, access to SYSTIMER_INT_ENA_REG has to be
168         * protected by a shared spinlock. Since this code runs as part of early startup, this
169         * is practically not an issue.
170         */
171         systimer_hal_enable_alarm_int(&systimer_hal, SYSTIMER_ALARM_ESPTIMER);
172     }
173 
174     return err;
175 }
176 
esp_timer_impl_deinit(void)177 void esp_timer_impl_deinit(void)
178 {
179     systimer_ll_enable_alarm(systimer_hal.dev, SYSTIMER_ALARM_ESPTIMER, false);
180     /* TODO: may need a spinlock, see the note related to SYSTIMER_INT_ENA_REG in systimer_hal_init */
181     systimer_ll_enable_alarm_int(systimer_hal.dev, SYSTIMER_ALARM_ESPTIMER, false);
182     s_alarm_handler = NULL;
183 }
184 
esp_timer_impl_get_alarm_reg(void)185 uint64_t esp_timer_impl_get_alarm_reg(void)
186 {
187     s_time_update_lock = irq_lock();
188     uint64_t val = systimer_hal_get_alarm_value(&systimer_hal, SYSTIMER_ALARM_ESPTIMER);
189     irq_unlock(s_time_update_lock);
190     return val;
191 }
192 
193 void esp_timer_private_update_apb_freq(uint32_t apb_ticks_per_us) __attribute__((alias("esp_timer_impl_update_apb_freq")));
194 void esp_timer_private_set(uint64_t new_us) __attribute__((alias("esp_timer_impl_set")));
195 void esp_timer_private_advance(int64_t time_diff_us) __attribute__((alias("esp_timer_impl_advance")));
196