xref: /openbsd-src/sys/arch/arm64/dev/agtimer.c (revision 8550894424f8a4aa4aafb6cd57229dd6ed7cd9dd)
1 /* $OpenBSD: agtimer.c,v 1.21 2023/01/09 15:22:53 kettenis Exp $ */
2 /*
3  * Copyright (c) 2011 Dale Rahn <drahn@openbsd.org>
4  * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/clockintr.h>
22 #include <sys/queue.h>
23 #include <sys/malloc.h>
24 #include <sys/device.h>
25 #include <sys/kernel.h>
26 #include <sys/stdint.h>
27 #include <sys/timetc.h>
28 #include <sys/evcount.h>
29 
30 #include <machine/intr.h>
31 #include <machine/bus.h>
32 #include <machine/fdt.h>
33 
34 #include <dev/ofw/fdt.h>
35 #include <dev/ofw/openfirm.h>
36 
37 /* registers */
38 #define GTIMER_CNTV_CTL_ENABLE		(1 << 0)
39 #define GTIMER_CNTV_CTL_IMASK		(1 << 1)
40 #define GTIMER_CNTV_CTL_ISTATUS		(1 << 2)
41 
42 #define TIMER_FREQUENCY		24 * 1000 * 1000 /* ARM core clock */
43 int32_t agtimer_frequency = TIMER_FREQUENCY;
44 
45 u_int agtimer_get_timecount_default(struct timecounter *);
46 u_int agtimer_get_timecount_sun50i(struct timecounter *);
47 
48 static struct timecounter agtimer_timecounter = {
49 	.tc_get_timecount = agtimer_get_timecount_default,
50 	.tc_poll_pps = NULL,
51 	.tc_counter_mask = 0xffffffff,
52 	.tc_frequency = 0,
53 	.tc_name = "agtimer",
54 	.tc_quality = 0,
55 	.tc_priv = NULL,
56 	.tc_user = TC_AGTIMER,
57 };
58 
59 struct agtimer_softc {
60 	struct device		sc_dev;
61 	int			sc_node;
62 
63 	u_int32_t		sc_ticks_per_second;
64 	uint64_t		sc_nsec_cycle_ratio;
65 	uint64_t		sc_nsec_max;
66 	void			*sc_ih;
67 };
68 
69 int		agtimer_match(struct device *, void *, void *);
70 void		agtimer_attach(struct device *, struct device *, void *);
71 int		agtimer_intr(void *);
72 void		agtimer_cpu_initclocks(void);
73 void		agtimer_delay(u_int);
74 void		agtimer_rearm(void *, uint64_t);
75 void		agtimer_setstatclockrate(int stathz);
76 void		agtimer_set_clockrate(int32_t new_frequency);
77 void		agtimer_startclock(void);
78 void		agtimer_trigger(void *);
79 
80 const struct cfattach agtimer_ca = {
81 	sizeof (struct agtimer_softc), agtimer_match, agtimer_attach
82 };
83 
84 struct cfdriver agtimer_cd = {
85 	NULL, "agtimer", DV_DULL
86 };
87 
88 struct intrclock agtimer_intrclock = {
89 	.ic_rearm = agtimer_rearm,
90 	.ic_trigger = agtimer_trigger
91 };
92 
93 uint64_t
94 agtimer_readcnt64_default(void)
95 {
96 	uint64_t val0, val1;
97 
98 	/*
99 	 * Work around Cortex-A73 errata 858921, where there is a
100 	 * one-cycle window where the read might return the old value
101 	 * for the low 32 bits and the new value for the high 32 bits
102 	 * upon roll-over of the low 32 bits.
103 	 */
104 	__asm volatile("isb" ::: "memory");
105 	__asm volatile("mrs %x0, CNTVCT_EL0" : "=r" (val0));
106 	__asm volatile("mrs %x0, CNTVCT_EL0" : "=r" (val1));
107 	return ((val0 ^ val1) & 0x100000000ULL) ? val0 : val1;
108 }
109 
110 uint64_t
111 agtimer_readcnt64_sun50i(void)
112 {
113 	uint64_t val;
114 	int retry;
115 
116 	__asm volatile("isb" ::: "memory");
117 	for (retry = 0; retry < 150; retry++) {
118 		__asm volatile("mrs %x0, CNTVCT_EL0" : "=r" (val));
119 
120 		if (((val + 1) & 0x1ff) > 1)
121 			break;
122 	}
123 	KASSERT(retry < 150);
124 
125 	return val;
126 }
127 
128 uint64_t (*agtimer_readcnt64)(void) = agtimer_readcnt64_default;
129 
130 static inline uint64_t
131 agtimer_get_freq(void)
132 {
133 	uint64_t val;
134 
135 	__asm volatile("mrs %x0, CNTFRQ_EL0" : "=r" (val));
136 
137 	return (val);
138 }
139 
140 static inline int
141 agtimer_get_ctrl(void)
142 {
143 	uint32_t val;
144 
145 	__asm volatile("mrs %x0, CNTV_CTL_EL0" : "=r" (val));
146 
147 	return (val);
148 }
149 
150 static inline int
151 agtimer_set_ctrl(uint32_t val)
152 {
153 	__asm volatile("msr CNTV_CTL_EL0, %x0" :: "r" (val));
154 	__asm volatile("isb" ::: "memory");
155 
156 	return (0);
157 }
158 
159 static inline int
160 agtimer_set_tval(uint32_t val)
161 {
162 	__asm volatile("msr CNTV_TVAL_EL0, %x0" :: "r" (val));
163 	__asm volatile("isb" ::: "memory");
164 
165 	return (0);
166 }
167 
168 int
169 agtimer_match(struct device *parent, void *cfdata, void *aux)
170 {
171 	struct fdt_attach_args *faa = (struct fdt_attach_args *)aux;
172 
173 	return (OF_is_compatible(faa->fa_node, "arm,armv7-timer") ||
174 	    OF_is_compatible(faa->fa_node, "arm,armv8-timer"));
175 }
176 
177 void
178 agtimer_attach(struct device *parent, struct device *self, void *aux)
179 {
180 	struct agtimer_softc *sc = (struct agtimer_softc *)self;
181 	struct fdt_attach_args *faa = aux;
182 
183 	sc->sc_node = faa->fa_node;
184 
185 	if (agtimer_get_freq() != 0)
186 		agtimer_frequency = agtimer_get_freq();
187 	agtimer_frequency =
188 	    OF_getpropint(sc->sc_node, "clock-frequency", agtimer_frequency);
189 	sc->sc_ticks_per_second = agtimer_frequency;
190 	sc->sc_nsec_cycle_ratio =
191 	    sc->sc_ticks_per_second * (1ULL << 32) / 1000000000;
192 	sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio;
193 
194 	printf(": %u kHz\n", sc->sc_ticks_per_second / 1000);
195 
196 	/*
197 	 * The Allwinner A64 has an erratum where the bottom 9 bits of
198 	 * the counter register can't be trusted if any of the higher
199 	 * bits are rolling over.
200 	 */
201 	if (OF_getpropbool(sc->sc_node, "allwinner,erratum-unknown1")) {
202 		agtimer_readcnt64 = agtimer_readcnt64_sun50i;
203 		agtimer_timecounter.tc_get_timecount =
204 		    agtimer_get_timecount_sun50i;
205 		agtimer_timecounter.tc_user = TC_AGTIMER_SUN50I;
206 	}
207 
208 	/*
209 	 * private timer and interrupts not enabled until
210 	 * timer configures
211 	 */
212 
213 	arm_clock_register(agtimer_cpu_initclocks, agtimer_delay,
214 	    agtimer_setstatclockrate, agtimer_startclock);
215 
216 	agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
217 	agtimer_timecounter.tc_priv = sc;
218 	tc_init(&agtimer_timecounter);
219 
220 	agtimer_intrclock.ic_cookie = sc;
221 }
222 
223 u_int
224 agtimer_get_timecount_default(struct timecounter *tc)
225 {
226 	uint64_t val;
227 
228 	/*
229 	 * No need to work around Cortex-A73 errata 858921 since we
230 	 * only look at the low 32 bits here.
231 	 */
232 	__asm volatile("isb" ::: "memory");
233 	__asm volatile("mrs %x0, CNTVCT_EL0" : "=r" (val));
234 	return (val & 0xffffffff);
235 }
236 
237 u_int
238 agtimer_get_timecount_sun50i(struct timecounter *tc)
239 {
240 	return agtimer_readcnt64_sun50i();
241 }
242 
243 void
244 agtimer_rearm(void *cookie, uint64_t nsecs)
245 {
246 	struct agtimer_softc *sc = cookie;
247 	uint32_t cycles;
248 
249 	if (nsecs > sc->sc_nsec_max)
250 		nsecs = sc->sc_nsec_max;
251 	cycles = (nsecs * sc->sc_nsec_cycle_ratio) >> 32;
252 	if (cycles > INT32_MAX)
253 		cycles = INT32_MAX;
254 	agtimer_set_tval(cycles);
255 }
256 
257 void
258 agtimer_trigger(void *unused)
259 {
260 	agtimer_set_tval(0);
261 }
262 
263 int
264 agtimer_intr(void *frame)
265 {
266 	return clockintr_dispatch(frame);
267 }
268 
269 void
270 agtimer_set_clockrate(int32_t new_frequency)
271 {
272 	struct agtimer_softc	*sc = agtimer_cd.cd_devs[0];
273 
274 	agtimer_frequency = new_frequency;
275 
276 	if (sc == NULL)
277 		return;
278 
279 	sc->sc_ticks_per_second = agtimer_frequency;
280 	sc->sc_nsec_cycle_ratio =
281 	    sc->sc_ticks_per_second * (1ULL << 32) / 1000000000;
282 	sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio;
283 
284 	agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
285 
286 	printf("agtimer0: adjusting clock: new tick rate %u kHz\n",
287 	    sc->sc_ticks_per_second / 1000);
288 }
289 
290 void
291 agtimer_cpu_initclocks(void)
292 {
293 	struct agtimer_softc	*sc = agtimer_cd.cd_devs[0];
294 	uint32_t		 reg;
295 	uint64_t		 kctl;
296 
297 	stathz = hz;
298 	profhz = stathz * 10;
299 	clockintr_init(CL_RNDSTAT);
300 
301 	if (sc->sc_ticks_per_second != agtimer_frequency) {
302 		agtimer_set_clockrate(agtimer_frequency);
303 	}
304 
305 	/* configure virtual timer interrupt */
306 	sc->sc_ih = arm_intr_establish_fdt_idx(sc->sc_node, 2,
307 	    IPL_CLOCK|IPL_MPSAFE, agtimer_intr, NULL, "tick");
308 
309 	clockintr_cpu_init(&agtimer_intrclock);
310 
311 	reg = agtimer_get_ctrl();
312 	reg &= ~GTIMER_CNTV_CTL_IMASK;
313 	reg |= GTIMER_CNTV_CTL_ENABLE;
314 	agtimer_set_tval(INT32_MAX);
315 	agtimer_set_ctrl(reg);
316 
317 	clockintr_trigger();
318 
319 	/* enable userland access to virtual counter */
320 	kctl = READ_SPECIALREG(CNTKCTL_EL1);
321 	WRITE_SPECIALREG(CNTKCTL_EL1, kctl | CNTKCTL_EL0VCTEN);
322 }
323 
324 void
325 agtimer_delay(u_int usecs)
326 {
327 	uint64_t		clock, oclock, delta, delaycnt;
328 	uint64_t		csec, usec;
329 	volatile int		j;
330 
331 	if (usecs > (0x80000000 / agtimer_frequency)) {
332 		csec = usecs / 10000;
333 		usec = usecs % 10000;
334 
335 		delaycnt = (agtimer_frequency / 100) * csec +
336 		    (agtimer_frequency / 100) * usec / 10000;
337 	} else {
338 		delaycnt = agtimer_frequency * usecs / 1000000;
339 	}
340 	if (delaycnt <= 1)
341 		for (j = 100; j > 0; j--)
342 			;
343 
344 	oclock = agtimer_readcnt64();
345 	while (1) {
346 		for (j = 100; j > 0; j--)
347 			;
348 		clock = agtimer_readcnt64();
349 		delta = clock - oclock;
350 		if (delta > delaycnt)
351 			break;
352 	}
353 }
354 
355 void
356 agtimer_setstatclockrate(int newhz)
357 {
358 	clockintr_setstatclockrate(newhz);
359 }
360 
361 void
362 agtimer_startclock(void)
363 {
364 	struct agtimer_softc	*sc = agtimer_cd.cd_devs[0];
365 	uint64_t kctl;
366 	uint32_t reg;
367 
368 	arm_intr_route(sc->sc_ih, 1, curcpu());
369 
370 	clockintr_cpu_init(&agtimer_intrclock);
371 
372 	reg = agtimer_get_ctrl();
373 	reg &= ~GTIMER_CNTV_CTL_IMASK;
374 	reg |= GTIMER_CNTV_CTL_ENABLE;
375 	agtimer_set_tval(INT32_MAX);
376 	agtimer_set_ctrl(reg);
377 
378 	clockintr_trigger();
379 
380 	/* enable userland access to virtual counter */
381 	kctl = READ_SPECIALREG(CNTKCTL_EL1);
382 	WRITE_SPECIALREG(CNTKCTL_EL1, kctl | CNTKCTL_EL0VCTEN);
383 }
384 
385 void
386 agtimer_init(void)
387 {
388 	uint64_t cntfrq = 0;
389 
390 	/* XXX: Check for Generic Timer support. */
391 	cntfrq = agtimer_get_freq();
392 
393 	if (cntfrq != 0) {
394 		agtimer_frequency = cntfrq;
395 		arm_clock_register(NULL, agtimer_delay, NULL, NULL);
396 	}
397 }
398