1 /*
2 * Copyright (c) 2023 - 2024 Intel Corporation
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <sedi_driver_hpet.h>
8 #include <sedi_hpet_regs.h>
9 #include <string.h>
10
11 /* define two 64-bit registers for easy access with SEDI REG APIs */
12 SEDI_REG_DEFINE(HPET, T0CV, 0x108, RW, (uint64_t)-1, (uint64_t)-1);
13 SEDI_REG_DEFINE(HPET, MCV, 0x0f0, RW, (uint64_t)-1, (uint64_t)0x0);
14
15 /* driver version */
16 #define SEDI_HPET_DRIVER_VERSION SEDI_DRIVER_VERSION_MAJOR_MINOR(0, 1)
17
18 #define PICOSECS_PER_USEC 1000000
19
20 #define US_TO_HPET_CYCLE(us) ((uint64_t)(us)*PICOSECS_PER_USEC / hpet_clock_period_pico)
21 #define HPET_CYCLE_TO_US(cycle) ((uint64_t)(cycle)*hpet_clock_period_pico / PICOSECS_PER_USEC)
22
23 /* control and status register macros */
24 #define CTRL_STS_GENERAL_CONFIG SEDI_RBFM(HPET, HPET_CTRL_STS, HPET_CTRL_STS0)
25 #define CTRL_STS_GENERAL_INT_STATUS SEDI_RBFM(HPET, HPET_CTRL_STS, HPET_CTRL_STS1)
26 #define CTRL_STS_MAIN_COUNTER_VALUE \
27 (SEDI_RBFM(HPET, HPET_CTRL_STS, HPET_CTRL_STS2) | \
28 SEDI_RBFM(HPET, HPET_CTRL_STS, HPET_CTRL_STS3))
29 #define CTRL_STS_TIMER0_CONFIG_CAPS SEDI_RBFM(HPET, HPET_CTRL_STS, HPET_CTRL_STS4)
30 #define CTRL_STS_TIMER1_CONFIG_CAPS SEDI_RBFM(HPET, HPET_CTRL_STS, HPET_CTRL_STS5)
31 #define CTRL_STS_TIMER2_CONFIG_CAPS SEDI_RBFM(HPET, HPET_CTRL_STS, HPET_CTRL_STS6)
32 #define CTRL_STS_TIMER0_COMPARATOR \
33 (SEDI_RBFM(HPET, HPET_CTRL_STS, HPET_CTRL_STS7) | \
34 SEDI_RBFM(HPET, HPET_CTRL_STS, HPET_CTRL_STS8))
35 #define CTRL_STS_TIMER1_COMPARATOR SEDI_RBFM(HPET, HPET_CTRL_STS, HPET_CTRL_STS9)
36 #define CTRL_STS_TIMER2_COMPARATOR SEDI_RBFM(HPET, HPET_CTRL_STS, HPET_CTRL_STS10)
37 #define CTRL_STS_MAIN_COUNTER_VALUE_INVALID SEDI_RBFM(HPET, HPET_CTRL_STS, HPET_CTRL_STS13)
38
39 /*Macro for interrupt routing*/
40 #define TIMER2_INT_ROUTE (0x0b)
41
42 typedef struct {
43 uint32_t start : 1; /* start flag */
44 uint32_t one_shot : 1; /* select period/non-period mode */
45 uint32_t rsvd : 29; /* reserved */
46 uint32_t valid : 1; /* Check if the position has been token */
47 uint64_t microsec; /* user's parameter */
48 uint64_t timeout; /* translated parameter, for sort */
49 uint64_t expires; /* current + timeout, for sort */
50 hpet_callback_t callback; /* callback */
51 void *param; /* callback param, user defined */
52 } hpet_timer_ctx_t;
53
54 static hpet_timer_ctx_t bsp_timers[SEDI_HPET_SOC_TIMER_NUM];
55
56 static uint32_t hpet_clock_period_pico = 30517578; /* picoseconds */
57 static uint32_t hpet_min_delay = 5; /* HPET cycles */
58 /* driver version */
59 static const sedi_driver_version_t driver_version = { SEDI_HPET_API_VERSION,
60 SEDI_HPET_DRIVER_VERSION };
61 /* Capabilities for hpet */
62 static const sedi_hpet_capabilities_t hpet_cap = { 0 };
63
wait_for_idle(uint32_t bits)64 static inline void wait_for_idle(uint32_t bits)
65 {
66 SEDI_POLL_WAIT(SEDI_REG_GET(HPET, HPET_CTRL_STS) & bits, 10);
67 }
68
sedi_hpet_get_min_delay(void)69 uint32_t sedi_hpet_get_min_delay(void)
70 {
71 return hpet_min_delay;
72 }
73
sedi_hpet_set_min_delay(uint32_t min_delay)74 void sedi_hpet_set_min_delay(uint32_t min_delay)
75 {
76 hpet_min_delay = min_delay;
77 }
78
sedi_hpet_get_version(void)79 sedi_driver_version_t sedi_hpet_get_version(void)
80 {
81 return driver_version;
82 }
83
sedi_hpet_get_capabilities(void)84 sedi_hpet_capabilities_t sedi_hpet_get_capabilities(void)
85 {
86 return hpet_cap;
87 }
88
sedi_hpet_uninit(void)89 int32_t sedi_hpet_uninit(void)
90 {
91 return SEDI_DRIVER_OK;
92 }
93
sedi_hpet_set_power(IN sedi_power_state_t state)94 int32_t sedi_hpet_set_power(IN sedi_power_state_t state)
95 {
96 PARAM_UNUSED(state);
97 return SEDI_DRIVER_OK;
98 }
99
sedi_hpet_update_comparator(IN sedi_hpet_t timer_id,IN uint64_t value)100 static int sedi_hpet_update_comparator(IN sedi_hpet_t timer_id, IN uint64_t value)
101 {
102 switch (timer_id) {
103 case HPET_0:
104 SEDI_REG_SET(HPET, T0CV, value);
105 break;
106 case HPET_1:
107 SEDI_REG_SET(HPET, T1CV_LOW, (uint32_t)value);
108 break;
109 case HPET_2:
110 SEDI_REG_SET(HPET, T2CV_LOW, (uint32_t)value);
111 break;
112 default:
113 return SEDI_DRIVER_ERROR_NO_DEV;
114 }
115
116 return SEDI_DRIVER_OK;
117 }
118
sedi_hpet_set_comparator(IN sedi_hpet_t timer_id,IN uint64_t value)119 int sedi_hpet_set_comparator(IN sedi_hpet_t timer_id, IN uint64_t value)
120 {
121 uint64_t _value = value;
122 /* need to wait until INT clearing is finished, or HPET
123 * goes out of work
124 */
125 uint32_t sts_wait = CTRL_STS_GENERAL_INT_STATUS | CTRL_STS_TIMER0_COMPARATOR |
126 CTRL_STS_TIMER1_COMPARATOR | CTRL_STS_TIMER2_COMPARATOR;
127 uint64_t now;
128 int64_t diff;
129
130 if ((timer_id != HPET_0) && (value >> 32)) {
131 /* it's wrong to set into a 32-bits timer */
132 return SEDI_DRIVER_ERROR_PARAMETER;
133 }
134
135 wait_for_idle(sts_wait);
136 now = sedi_hpet_get_main_counter();
137
138 if (timer_id == HPET_0) {
139 diff = (int64_t)(value - now);
140 } else {
141 diff = (int32_t)((uint32_t)value - (uint32_t)now);
142 }
143 if (diff < hpet_min_delay) {
144 _value = now + hpet_min_delay;
145 }
146
147 return sedi_hpet_update_comparator(timer_id, _value);
148 }
149
sedi_hpet_set_main_counter(uint64_t value)150 void sedi_hpet_set_main_counter(uint64_t value)
151 {
152 wait_for_idle(CTRL_STS_MAIN_COUNTER_VALUE);
153 SEDI_REG_SET(HPET, MCV, value);
154 }
155
sedi_hpet_get_main_counter(void)156 uint64_t sedi_hpet_get_main_counter(void)
157 {
158 uint32_t highBits;
159 uint32_t lowBits;
160
161 wait_for_idle(CTRL_STS_MAIN_COUNTER_VALUE_INVALID);
162 do {
163 highBits = SEDI_REG_GET(HPET, MCV_HIGH);
164 lowBits = SEDI_REG_GET(HPET, MCV_LOW);
165 } while (highBits != SEDI_REG_GET(HPET, MCV_HIGH));
166
167 return ((uint64_t)highBits << 32) | lowBits;
168 }
169
sedi_hpet_get_us(void)170 uint64_t sedi_hpet_get_us(void)
171 {
172 return HPET_CYCLE_TO_US(sedi_hpet_get_main_counter());
173 }
174
sedi_hpet_enable_interrupt(IN sedi_hpet_t timer_id)175 void sedi_hpet_enable_interrupt(IN sedi_hpet_t timer_id)
176 {
177 switch (timer_id) {
178 case HPET_0:
179 wait_for_idle(CTRL_STS_TIMER0_CONFIG_CAPS);
180 SEDI_REG_RBFV_SET(HPET, T0C_LOW, IE, 1);
181 break;
182 case HPET_1:
183 wait_for_idle(CTRL_STS_TIMER1_CONFIG_CAPS);
184 SEDI_REG_RBFV_SET(HPET, T1C_LOW, IE, 1);
185 break;
186 case HPET_2:
187 wait_for_idle(CTRL_STS_TIMER2_CONFIG_CAPS);
188 SEDI_REG_RBFV_SET(HPET, T2C_LOW, IE, 1);
189 break;
190 default:
191 break;
192 }
193 }
194
sedi_hpet_disable_interrupt(IN sedi_hpet_t timer_id)195 void sedi_hpet_disable_interrupt(IN sedi_hpet_t timer_id)
196 {
197 switch (timer_id) {
198 case HPET_0:
199 wait_for_idle(CTRL_STS_TIMER0_CONFIG_CAPS);
200 SEDI_REG_RBFV_SET(HPET, T0C_LOW, IE, 0);
201 break;
202 case HPET_1:
203 wait_for_idle(CTRL_STS_TIMER1_CONFIG_CAPS);
204 SEDI_REG_RBFV_SET(HPET, T1C_LOW, IE, 0);
205 break;
206 case HPET_2:
207 wait_for_idle(CTRL_STS_TIMER2_CONFIG_CAPS);
208 SEDI_REG_RBFV_SET(HPET, T2C_LOW, IE, 0);
209 break;
210 default:
211 break;
212 }
213 }
214
sedi_hpet_init(uint32_t clk_divisor,uint32_t min_delay)215 int32_t sedi_hpet_init(uint32_t clk_divisor, uint32_t min_delay)
216 {
217 wait_for_idle(CTRL_STS_GENERAL_CONFIG | CTRL_STS_MAIN_COUNTER_VALUE |
218 CTRL_STS_TIMER0_CONFIG_CAPS | CTRL_STS_TIMER0_COMPARATOR |
219 CTRL_STS_TIMER1_CONFIG_CAPS | CTRL_STS_TIMER1_COMPARATOR |
220 CTRL_STS_TIMER2_CONFIG_CAPS | CTRL_STS_TIMER2_COMPARATOR);
221 /*
222 * Initial state of HPET is unknown, so put it back in a reset-like
223 * state (i.e. set main counter to 0 and disable interrupts)
224 */
225 sedi_hpet_update_comparator(HPET_0, (uint64_t)-1);
226 sedi_hpet_update_comparator(HPET_1, (uint64_t)-1);
227 sedi_hpet_update_comparator(HPET_2, (uint64_t)-1);
228 SEDI_REG_RBFV_SET(HPET, GCFG_LOW, EN, 0);
229 SEDI_REG_SET(HPET, GIS_LOW, SEDI_REG_GET(HPET, GIS_LOW));
230 SEDI_REG_SET(HPET, MCV, (uint64_t)0x0);
231
232 /* Set interrupt router and trigger mode */
233 SEDI_REG_SET(HPET, T0C_LOW,
234 SEDI_RBFM_VALUE(HPET, T0C_LOW, IR, SEDI_IRQ_HPET_TIMER_0) |
235 SEDI_RBFVM(HPET, T0C_LOW, IT, 1));
236 SEDI_REG_SET(HPET, T1C_LOW,
237 SEDI_RBFM_VALUE(HPET, T1C_LOW, IR, SEDI_IRQ_HPET_TIMER_1) |
238 SEDI_RBFVM(HPET, T1C_LOW, IT, 1));
239 SEDI_REG_SET(HPET, T2C_LOW,
240 SEDI_RBFM_VALUE(HPET, T2C_LOW, IR, TIMER2_INT_ROUTE) |
241 SEDI_RBFVM(HPET, T2C_LOW, IT, 1));
242
243 /* Get source clock for this paltform */
244 hpet_clock_period_pico = SEDI_REG_RBFV_GET(HPET, GCID_HIGH, CTP) * clk_divisor;
245 hpet_min_delay = min_delay;
246
247 wait_for_idle(CTRL_STS_GENERAL_CONFIG);
248 /* Enable global settings */
249 SEDI_REG_SET(HPET, GCFG_LOW,
250 SEDI_RBFVM(HPET, GCFG_LOW, LRE, 1) | SEDI_RBFVM(HPET, GCFG_LOW, EN, 1));
251
252 return 0;
253 }
254
sedi_hpet_get_int_status(void)255 uint32_t sedi_hpet_get_int_status(void)
256 {
257 wait_for_idle(CTRL_STS_GENERAL_INT_STATUS);
258 return SEDI_REG_GET(HPET, GIS_LOW);
259 }
260
sedi_hpet_set_int_status(IN uint32_t val)261 void sedi_hpet_set_int_status(IN uint32_t val)
262 {
263 SEDI_REG_SET(HPET, GIS_LOW, val);
264 wait_for_idle(CTRL_STS_GENERAL_INT_STATUS);
265 }
266
sedi_hpet_timer_int_handler(IN sedi_hpet_t timer_id)267 void sedi_hpet_timer_int_handler(IN sedi_hpet_t timer_id)
268 {
269 if (!(SEDI_REG_GET(HPET, GIS_LOW) & BIT(timer_id)))
270 return;
271
272 /* Clear the GIS flags */
273 sedi_hpet_set_int_status(BIT(timer_id));
274
275 if (bsp_timers[timer_id].callback) {
276 bsp_timers[timer_id].callback(bsp_timers[timer_id].param);
277 }
278
279 if (bsp_timers[timer_id].one_shot) {
280 sedi_hpet_kill_timer(timer_id);
281 } else {
282 bsp_timers[timer_id].expires =
283 sedi_hpet_get_main_counter() + bsp_timers[timer_id].timeout;
284 /* Set new comparator */
285 sedi_hpet_set_comparator(timer_id, bsp_timers[timer_id].expires);
286 }
287 }
288
sedi_hpet_config_timer(IN sedi_hpet_t timer_id,IN uint64_t microseconds,IN hpet_callback_t callback,IN void * param,IN bool one_shot)289 int32_t sedi_hpet_config_timer(IN sedi_hpet_t timer_id, IN uint64_t microseconds,
290 IN hpet_callback_t callback, IN void *param, IN bool one_shot)
291 {
292 DBG_CHECK(timer_id < SEDI_HPET_SOC_TIMER_NUM, SEDI_DRIVER_ERROR_PARAMETER);
293
294 /* Check if timer is being used */
295 DBG_CHECK(bsp_timers[timer_id].valid == 0, SEDI_DRIVER_ERROR_BUSY);
296
297 /* Just save here, will use when starting the timer */
298 bsp_timers[timer_id].valid = 1;
299 bsp_timers[timer_id].start = 0;
300 bsp_timers[timer_id].one_shot = one_shot ? 1 : 0;
301 bsp_timers[timer_id].microsec = microseconds;
302 bsp_timers[timer_id].timeout = US_TO_HPET_CYCLE(microseconds + HPET_CYCLE_TO_US(1));
303 bsp_timers[timer_id].expires = 0;
304 bsp_timers[timer_id].callback = callback;
305 bsp_timers[timer_id].param = (void *)param;
306 if (bsp_timers[timer_id].timeout < hpet_min_delay) {
307 bsp_timers[timer_id].timeout = hpet_min_delay;
308 }
309
310 return SEDI_DRIVER_OK;
311 }
312
sedi_hpet_kill_timer(IN sedi_hpet_t timer_id)313 int32_t sedi_hpet_kill_timer(IN sedi_hpet_t timer_id)
314 {
315 DBG_CHECK(timer_id < SEDI_HPET_SOC_TIMER_NUM, SEDI_DRIVER_ERROR_PARAMETER);
316
317 /* Disable interrupt */
318 sedi_hpet_disable_interrupt(timer_id);
319 sedi_hpet_set_int_status(BIT(timer_id));
320
321 /* Set comparator all bits as 1 */
322 sedi_hpet_update_comparator(timer_id, (uint64_t)-1);
323
324 memset(&bsp_timers[timer_id], 0, sizeof(hpet_timer_ctx_t));
325
326 return SEDI_DRIVER_OK;
327 }
328
sedi_hpet_start_timer(IN sedi_hpet_t timer_id)329 int32_t sedi_hpet_start_timer(IN sedi_hpet_t timer_id)
330 {
331 DBG_CHECK(timer_id < SEDI_HPET_SOC_TIMER_NUM, SEDI_DRIVER_ERROR_PARAMETER);
332
333 /* Enable interrupt */
334 sedi_hpet_enable_interrupt(timer_id);
335
336 bsp_timers[timer_id].start = 1;
337 bsp_timers[timer_id].expires = sedi_hpet_get_main_counter() + bsp_timers[timer_id].timeout;
338
339 /* Set comparator to correct value */
340 sedi_hpet_set_comparator(timer_id, bsp_timers[timer_id].expires);
341
342 return SEDI_DRIVER_OK;
343 }
344
sedi_hpet_get_period(void)345 uint32_t sedi_hpet_get_period(void)
346 {
347 return (uint32_t)(hpet_clock_period_pico);
348 }
349