1 /*
2 * Copyright (c) 2023 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
38 /*Macro for interrupt routing*/
39 #define TIMER2_INT_ROUTE (0x0b)
40
41 typedef struct {
42 uint32_t start : 1; /* start flag */
43 uint32_t one_shot : 1; /* select period/non-period mode */
44 uint32_t rsvd : 29; /* reserved */
45 uint32_t valid : 1; /* Check if the position has been token */
46 uint64_t microsec; /* user's parameter */
47 uint64_t timeout; /* translated parameter, for sort */
48 uint64_t expires; /* current + timeout, for sort */
49 hpet_callback_t callback; /* callback */
50 void *param; /* callback param, user defined */
51 } hpet_timer_ctx_t;
52
53 static hpet_timer_ctx_t bsp_timers[SEDI_HPET_SOC_TIMER_NUM];
54
55 static uint32_t hpet_clock_period_pico = 30517578; /* picoseconds */
56 static uint32_t hpet_min_delay = 5; /* HPET cycles */
57 /* driver version */
58 static const sedi_driver_version_t driver_version = { SEDI_HPET_API_VERSION,
59 SEDI_HPET_DRIVER_VERSION };
60 /* Capabilities for hpet */
61 static const sedi_hpet_capabilities_t hpet_cap = { 0 };
62
wait_for_idle(uint32_t bits)63 static inline void wait_for_idle(uint32_t bits)
64 {
65 while (SEDI_REG_GET(HPET, HPET_CTRL_STS) & bits)
66 ;
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 do {
162 highBits = SEDI_REG_GET(HPET, MCV_HIGH);
163 lowBits = SEDI_REG_GET(HPET, MCV_LOW);
164 } while (highBits != SEDI_REG_GET(HPET, MCV_HIGH));
165
166 return ((uint64_t)highBits << 32) | lowBits;
167 }
168
sedi_hpet_enable_interrupt(IN sedi_hpet_t timer_id)169 void sedi_hpet_enable_interrupt(IN sedi_hpet_t timer_id)
170 {
171 switch (timer_id) {
172 case HPET_0:
173 wait_for_idle(CTRL_STS_TIMER0_CONFIG_CAPS);
174 SEDI_REG_RBFV_SET(HPET, T0C_LOW, IE, 1);
175 break;
176 case HPET_1:
177 wait_for_idle(CTRL_STS_TIMER1_CONFIG_CAPS);
178 SEDI_REG_RBFV_SET(HPET, T1C_LOW, IE, 1);
179 break;
180 case HPET_2:
181 wait_for_idle(CTRL_STS_TIMER2_CONFIG_CAPS);
182 SEDI_REG_RBFV_SET(HPET, T2C_LOW, IE, 1);
183 break;
184 default:
185 break;
186 }
187 }
188
sedi_hpet_disable_interrupt(IN sedi_hpet_t timer_id)189 void sedi_hpet_disable_interrupt(IN sedi_hpet_t timer_id)
190 {
191 switch (timer_id) {
192 case HPET_0:
193 wait_for_idle(CTRL_STS_TIMER0_CONFIG_CAPS);
194 SEDI_REG_RBFV_SET(HPET, T0C_LOW, IE, 0);
195 break;
196 case HPET_1:
197 wait_for_idle(CTRL_STS_TIMER1_CONFIG_CAPS);
198 SEDI_REG_RBFV_SET(HPET, T1C_LOW, IE, 0);
199 break;
200 case HPET_2:
201 wait_for_idle(CTRL_STS_TIMER2_CONFIG_CAPS);
202 SEDI_REG_RBFV_SET(HPET, T2C_LOW, IE, 0);
203 break;
204 default:
205 break;
206 }
207 }
208
sedi_hpet_init(uint32_t clk_divisor,uint32_t min_delay)209 int32_t sedi_hpet_init(uint32_t clk_divisor, uint32_t min_delay)
210 {
211 wait_for_idle(CTRL_STS_GENERAL_CONFIG | CTRL_STS_MAIN_COUNTER_VALUE |
212 CTRL_STS_TIMER0_CONFIG_CAPS | CTRL_STS_TIMER0_COMPARATOR |
213 CTRL_STS_TIMER1_CONFIG_CAPS | CTRL_STS_TIMER1_COMPARATOR |
214 CTRL_STS_TIMER2_CONFIG_CAPS | CTRL_STS_TIMER2_COMPARATOR);
215 /*
216 * Initial state of HPET is unknown, so put it back in a reset-like
217 * state (i.e. set main counter to 0 and disable interrupts)
218 */
219 SEDI_REG_RBFV_SET(HPET, GCFG_LOW, EN, 0);
220 SEDI_REG_SET(HPET, GIS_LOW, SEDI_REG_GET(HPET, GIS_LOW));
221 SEDI_REG_SET(HPET, MCV, (uint64_t)0x0);
222
223 /* Set interrupt router and trigger mode */
224 SEDI_REG_SET(HPET, T0C_LOW,
225 SEDI_RBFM_VALUE(HPET, T0C_LOW, IR, SEDI_IRQ_HPET_TIMER_0) |
226 SEDI_RBFVM(HPET, T0C_LOW, IT, 1));
227 SEDI_REG_SET(HPET, T1C_LOW,
228 SEDI_RBFM_VALUE(HPET, T1C_LOW, IR, SEDI_IRQ_HPET_TIMER_1) |
229 SEDI_RBFVM(HPET, T1C_LOW, IT, 1));
230 SEDI_REG_SET(HPET, T2C_LOW,
231 SEDI_RBFM_VALUE(HPET, T2C_LOW, IR, TIMER2_INT_ROUTE) |
232 SEDI_RBFVM(HPET, T2C_LOW, IT, 1));
233
234 /* Get source clock for this paltform */
235 hpet_clock_period_pico = SEDI_REG_RBFV_GET(HPET, GCID_HIGH, CTP) * clk_divisor;
236 hpet_min_delay = min_delay;
237
238 wait_for_idle(CTRL_STS_GENERAL_CONFIG);
239 /* Enable global settings */
240 SEDI_REG_SET(HPET, GCFG_LOW,
241 SEDI_RBFVM(HPET, GCFG_LOW, LRE, 1) | SEDI_RBFVM(HPET, GCFG_LOW, EN, 1));
242
243 return 0;
244 }
245
sedi_hpet_get_int_status(void)246 uint32_t sedi_hpet_get_int_status(void)
247 {
248 wait_for_idle(CTRL_STS_GENERAL_INT_STATUS);
249 return SEDI_REG_GET(HPET, GIS_LOW);
250 }
251
sedi_hpet_set_int_status(IN uint32_t val)252 void sedi_hpet_set_int_status(IN uint32_t val)
253 {
254 SEDI_REG_SET(HPET, GIS_LOW, val);
255 wait_for_idle(CTRL_STS_GENERAL_INT_STATUS);
256 }
257
sedi_hpet_timer_int_handler(IN sedi_hpet_t timer_id)258 void sedi_hpet_timer_int_handler(IN sedi_hpet_t timer_id)
259 {
260 if (!(SEDI_REG_GET(HPET, GIS_LOW) & BIT(timer_id)))
261 return;
262
263 /* Clear the GIS flags */
264 sedi_hpet_set_int_status(BIT(timer_id));
265
266 if (bsp_timers[timer_id].callback) {
267 bsp_timers[timer_id].callback(bsp_timers[timer_id].param);
268 }
269
270 if (bsp_timers[timer_id].one_shot) {
271 sedi_hpet_kill_timer(timer_id);
272 } else {
273 bsp_timers[timer_id].expires =
274 sedi_hpet_get_main_counter() + bsp_timers[timer_id].timeout;
275 /* Set new comparator */
276 sedi_hpet_set_comparator(timer_id, bsp_timers[timer_id].expires);
277 }
278 }
279
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)280 int32_t sedi_hpet_config_timer(IN sedi_hpet_t timer_id, IN uint64_t microseconds,
281 IN hpet_callback_t callback, IN void *param, IN bool one_shot)
282 {
283 DBG_CHECK(timer_id < SEDI_HPET_SOC_TIMER_NUM, SEDI_DRIVER_ERROR_PARAMETER);
284
285 /* Check if timer is being used */
286 DBG_CHECK(bsp_timers[timer_id].valid == 0, SEDI_DRIVER_ERROR_BUSY);
287
288 /* Just save here, will use when starting the timer */
289 bsp_timers[timer_id].valid = 1;
290 bsp_timers[timer_id].start = 0;
291 bsp_timers[timer_id].one_shot = one_shot ? 1 : 0;
292 bsp_timers[timer_id].microsec = microseconds;
293 bsp_timers[timer_id].timeout = US_TO_HPET_CYCLE(microseconds + HPET_CYCLE_TO_US(1));
294 bsp_timers[timer_id].expires = 0;
295 bsp_timers[timer_id].callback = callback;
296 bsp_timers[timer_id].param = (void *)param;
297 if (bsp_timers[timer_id].timeout < hpet_min_delay) {
298 bsp_timers[timer_id].timeout = hpet_min_delay;
299 }
300
301 return SEDI_DRIVER_OK;
302 }
303
sedi_hpet_kill_timer(IN sedi_hpet_t timer_id)304 int32_t sedi_hpet_kill_timer(IN sedi_hpet_t timer_id)
305 {
306 DBG_CHECK(timer_id < SEDI_HPET_SOC_TIMER_NUM, SEDI_DRIVER_ERROR_PARAMETER);
307
308 /* Disable interrupt */
309 sedi_hpet_disable_interrupt(timer_id);
310
311 /* Set comparator all bits as 1 */
312 sedi_hpet_update_comparator(timer_id, (uint64_t)-1);
313
314 memset(&bsp_timers[timer_id], 0, sizeof(hpet_timer_ctx_t));
315
316 return SEDI_DRIVER_OK;
317 }
318
sedi_hpet_start_timer(IN sedi_hpet_t timer_id)319 int32_t sedi_hpet_start_timer(IN sedi_hpet_t timer_id)
320 {
321 DBG_CHECK(timer_id < SEDI_HPET_SOC_TIMER_NUM, SEDI_DRIVER_ERROR_PARAMETER);
322
323 /* Enable interrupt */
324 sedi_hpet_enable_interrupt(timer_id);
325
326 bsp_timers[timer_id].start = 1;
327 bsp_timers[timer_id].expires = sedi_hpet_get_main_counter() + bsp_timers[timer_id].timeout;
328
329 /* Set comparator to correct value */
330 sedi_hpet_set_comparator(timer_id, bsp_timers[timer_id].expires);
331
332 return SEDI_DRIVER_OK;
333 }
334
sedi_hpet_get_period(void)335 uint32_t sedi_hpet_get_period(void)
336 {
337 return (uint32_t)(hpet_clock_period_pico);
338 }
339