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