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