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