1 /*
2 * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <stddef.h>
8 #include <sys/param.h>
9 #include "soc/soc_caps.h"
10 #include "hal/systimer_hal.h"
11 #include "hal/systimer_ll.h"
12 #include "hal/systimer_types.h"
13 #include "hal/assert.h"
14
systimer_hal_init(systimer_hal_context_t * hal)15 void systimer_hal_init(systimer_hal_context_t *hal)
16 {
17 hal->dev = &SYSTIMER;
18 systimer_ll_enable_clock(hal->dev, true);
19 #if SOC_SYSTIMER_SUPPORT_ETM
20 systimer_ll_enable_etm(&SYSTIMER, true);
21 #endif
22 }
23
systimer_hal_deinit(systimer_hal_context_t * hal)24 void systimer_hal_deinit(systimer_hal_context_t *hal)
25 {
26 #if SOC_SYSTIMER_SUPPORT_ETM
27 systimer_ll_enable_etm(&SYSTIMER, false);
28 #endif
29 systimer_ll_enable_clock(hal->dev, false);
30 hal->dev = NULL;
31 }
32
systimer_hal_set_clock_source(systimer_hal_context_t * hal,systimer_clock_source_t clk_src)33 void systimer_hal_set_clock_source(systimer_hal_context_t *hal, systimer_clock_source_t clk_src)
34 {
35 (void)hal;
36 systimer_ll_set_clock_source(clk_src);
37 }
38
systimer_hal_get_clock_source(systimer_hal_context_t * hal)39 systimer_clock_source_t systimer_hal_get_clock_source(systimer_hal_context_t *hal)
40 {
41 (void)hal;
42 return systimer_ll_get_clock_source();
43 }
44
systimer_hal_set_tick_rate_ops(systimer_hal_context_t * hal,systimer_hal_tick_rate_ops_t * ops)45 void systimer_hal_set_tick_rate_ops(systimer_hal_context_t *hal, systimer_hal_tick_rate_ops_t *ops)
46 {
47 hal->ticks_to_us = ops->ticks_to_us;
48 hal->us_to_ticks = ops->us_to_ticks;
49 }
50
systimer_hal_get_counter_value(systimer_hal_context_t * hal,uint32_t counter_id)51 uint64_t systimer_hal_get_counter_value(systimer_hal_context_t *hal, uint32_t counter_id)
52 {
53 uint32_t lo, lo_start, hi;
54 /* Set the "update" bit and wait for acknowledgment */
55 systimer_ll_counter_snapshot(hal->dev, counter_id);
56 while (!systimer_ll_is_counter_value_valid(hal->dev, counter_id));
57 /* Read LO, HI, then LO again, check that LO returns the same value.
58 * This accounts for the case when an interrupt may happen between reading
59 * HI and LO values, and this function may get called from the ISR.
60 * In this case, the repeated read will return consistent values.
61 */
62 lo_start = systimer_ll_get_counter_value_low(hal->dev, counter_id);
63 do {
64 lo = lo_start;
65 hi = systimer_ll_get_counter_value_high(hal->dev, counter_id);
66 lo_start = systimer_ll_get_counter_value_low(hal->dev, counter_id);
67 } while (lo_start != lo);
68
69 systimer_counter_value_t result = {
70 .lo = lo,
71 .hi = hi
72 };
73
74 return result.val;
75 }
76
systimer_hal_get_time(systimer_hal_context_t * hal,uint32_t counter_id)77 uint64_t systimer_hal_get_time(systimer_hal_context_t *hal, uint32_t counter_id)
78 {
79 return hal->ticks_to_us(systimer_hal_get_counter_value(hal, counter_id));
80 }
81
82 #if SOC_SYSTIMER_ALARM_MISS_COMPENSATE
systimer_hal_set_alarm_target(systimer_hal_context_t * hal,uint32_t alarm_id,uint64_t target)83 void systimer_hal_set_alarm_target(systimer_hal_context_t *hal, uint32_t alarm_id, uint64_t target)
84 {
85 systimer_counter_value_t alarm = {
86 .val = hal->us_to_ticks(target),
87 };
88 systimer_ll_enable_alarm(hal->dev, alarm_id, false);
89 systimer_ll_set_alarm_target(hal->dev, alarm_id, alarm.val);
90 systimer_ll_apply_alarm_value(hal->dev, alarm_id);
91 systimer_ll_enable_alarm(hal->dev, alarm_id, true);
92 }
93
94 #else // SOC_SYSTIMER_ALARM_MISS_COMPENSATE
95
systimer_hal_set_alarm_target(systimer_hal_context_t * hal,uint32_t alarm_id,uint64_t timestamp)96 void systimer_hal_set_alarm_target(systimer_hal_context_t *hal, uint32_t alarm_id, uint64_t timestamp)
97 {
98 int64_t offset = hal->us_to_ticks(1) * 2;
99 uint64_t now_time = systimer_hal_get_counter_value(hal, 0);
100 systimer_counter_value_t alarm = { .val = MAX(hal->us_to_ticks(timestamp), now_time + offset) };
101 do {
102 systimer_ll_enable_alarm(hal->dev, alarm_id, false);
103 systimer_ll_set_alarm_target(hal->dev, alarm_id, alarm.val);
104 systimer_ll_enable_alarm(hal->dev, alarm_id, true);
105 now_time = systimer_hal_get_counter_value(hal, 0);
106 int64_t delta = (int64_t)alarm.val - (int64_t)now_time;
107 if (delta <= 0 && !systimer_ll_is_alarm_int_fired(hal->dev, alarm_id)) {
108 // new alarm is less than the counter and the interrupt flag is not set
109 offset += -1 * delta + hal->us_to_ticks(1) * 2;
110 alarm.val = now_time + offset;
111 } else {
112 // finish if either (alarm > counter) or the interrupt flag is already set.
113 break;
114 }
115 } while (1);
116 }
117 #endif // SOC_SYSTIMER_ALARM_MISS_COMPENSATE
118
systimer_hal_set_alarm_period(systimer_hal_context_t * hal,uint32_t alarm_id,uint32_t period)119 void systimer_hal_set_alarm_period(systimer_hal_context_t *hal, uint32_t alarm_id, uint32_t period)
120 {
121 systimer_ll_enable_alarm(hal->dev, alarm_id, false);
122 systimer_ll_set_alarm_period(hal->dev, alarm_id, hal->us_to_ticks(period));
123 systimer_ll_apply_alarm_value(hal->dev, alarm_id);
124 systimer_ll_enable_alarm(hal->dev, alarm_id, true);
125 }
126
systimer_hal_get_alarm_value(systimer_hal_context_t * hal,uint32_t alarm_id)127 uint64_t systimer_hal_get_alarm_value(systimer_hal_context_t *hal, uint32_t alarm_id)
128 {
129 return systimer_ll_get_alarm_target(hal->dev, alarm_id);
130 }
131
systimer_hal_enable_alarm_int(systimer_hal_context_t * hal,uint32_t alarm_id)132 void systimer_hal_enable_alarm_int(systimer_hal_context_t *hal, uint32_t alarm_id)
133 {
134 systimer_ll_enable_alarm_int(hal->dev, alarm_id, true);
135 }
136
systimer_hal_counter_value_advance(systimer_hal_context_t * hal,uint32_t counter_id,int64_t time_us)137 void systimer_hal_counter_value_advance(systimer_hal_context_t *hal, uint32_t counter_id, int64_t time_us)
138 {
139 systimer_counter_value_t new_count = {
140 .val = systimer_hal_get_counter_value(hal, counter_id) + hal->us_to_ticks(time_us),
141 };
142 systimer_ll_set_counter_value(hal->dev, counter_id, new_count.val);
143 systimer_ll_apply_counter_value(hal->dev, counter_id);
144 }
145
systimer_hal_enable_counter(systimer_hal_context_t * hal,uint32_t counter_id)146 void systimer_hal_enable_counter(systimer_hal_context_t *hal, uint32_t counter_id)
147 {
148 systimer_ll_enable_counter(hal->dev, counter_id, true);
149 }
150
systimer_hal_select_alarm_mode(systimer_hal_context_t * hal,uint32_t alarm_id,systimer_alarm_mode_t mode)151 void systimer_hal_select_alarm_mode(systimer_hal_context_t *hal, uint32_t alarm_id, systimer_alarm_mode_t mode)
152 {
153 switch (mode) {
154 case SYSTIMER_ALARM_MODE_ONESHOT:
155 systimer_ll_enable_alarm_oneshot(hal->dev, alarm_id);
156 break;
157 case SYSTIMER_ALARM_MODE_PERIOD:
158 systimer_ll_enable_alarm_period(hal->dev, alarm_id);
159 break;
160 default:
161 break;
162 }
163 }
164
systimer_hal_connect_alarm_counter(systimer_hal_context_t * hal,uint32_t alarm_id,uint32_t counter_id)165 void systimer_hal_connect_alarm_counter(systimer_hal_context_t *hal, uint32_t alarm_id, uint32_t counter_id)
166 {
167 systimer_ll_connect_alarm_counter(hal->dev, alarm_id, counter_id);
168 }
169
systimer_hal_counter_can_stall_by_cpu(systimer_hal_context_t * hal,uint32_t counter_id,uint32_t cpu_id,bool can)170 void systimer_hal_counter_can_stall_by_cpu(systimer_hal_context_t *hal, uint32_t counter_id, uint32_t cpu_id, bool can)
171 {
172 systimer_ll_counter_can_stall_by_cpu(hal->dev, counter_id, cpu_id, can);
173 }
174
175 #if !SOC_SYSTIMER_FIXED_DIVIDER
176
systimer_hal_set_steps_per_tick(systimer_hal_context_t * hal,int clock_source,uint32_t steps)177 void systimer_hal_set_steps_per_tick(systimer_hal_context_t *hal, int clock_source, uint32_t steps)
178 {
179 /* Configure the counter:
180 * - increment by 1 when running from PLL (80 ticks per microsecond),
181 * - increment by 2 when running from XTAL (40 ticks per microsecond).
182 * Note that if the APB frequency is derived from XTAL with divider != 1,
183 * XTAL_STEP needs to be adjusted accordingly. For example, if
184 * the APB frequency is XTAL/4 = 10 MHz, then XTAL_STEP should be set to 8.
185 * This is handled in systimer_hal_on_apb_freq_update function.
186 */
187 switch (clock_source) {
188 case 0:
189 systimer_ll_set_step_for_xtal(hal->dev, steps);
190 break;
191 case 1:
192 systimer_ll_set_step_for_pll(hal->dev, steps);
193 default:
194 break;
195 }
196 }
197
systimer_hal_on_apb_freq_update(systimer_hal_context_t * hal,uint32_t apb_ticks_per_us)198 void systimer_hal_on_apb_freq_update(systimer_hal_context_t *hal, uint32_t apb_ticks_per_us)
199 {
200 /* If this function was called when switching APB clock to PLL, don't need
201 * do anything: the SYSTIMER_TIMER_PLL_STEP is already correct.
202 * If this was called when switching APB clock to XTAL, need to adjust
203 * XTAL_STEP value accordingly.
204 */
205 if (apb_ticks_per_us != hal->us_to_ticks(1)) {
206 HAL_ASSERT((hal->us_to_ticks(1) % apb_ticks_per_us) == 0 && "TICK_PER_US should be divisible by APB frequency (in MHz)");
207 systimer_ll_set_step_for_xtal(hal->dev, hal->us_to_ticks(1) / apb_ticks_per_us);
208 }
209 }
210 #endif // !SOC_SYSTIMER_FIXED_DIVIDER
211