xref: /openbsd-src/sys/arch/arm/cortex/agtimer.c (revision 0ed1bf01ac7e45759b65902d0ab711deb359316d)
1*0ed1bf01Scheloha /* $OpenBSD: agtimer.c,v 1.21 2023/09/17 14:50:51 cheloha Exp $ */
2466195a2Spatrick /*
3466195a2Spatrick  * Copyright (c) 2011 Dale Rahn <drahn@openbsd.org>
4466195a2Spatrick  * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se>
5466195a2Spatrick  *
6466195a2Spatrick  * Permission to use, copy, modify, and distribute this software for any
7466195a2Spatrick  * purpose with or without fee is hereby granted, provided that the above
8466195a2Spatrick  * copyright notice and this permission notice appear in all copies.
9466195a2Spatrick  *
10466195a2Spatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11466195a2Spatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12466195a2Spatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13466195a2Spatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14466195a2Spatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15466195a2Spatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16466195a2Spatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17466195a2Spatrick  */
18466195a2Spatrick 
19466195a2Spatrick #include <sys/param.h>
20466195a2Spatrick #include <sys/systm.h>
2118cf9b3aScheloha #include <sys/clockintr.h>
22466195a2Spatrick #include <sys/device.h>
23466195a2Spatrick #include <sys/kernel.h>
2418cf9b3aScheloha #include <sys/stdint.h>
25466195a2Spatrick #include <sys/timetc.h>
26466195a2Spatrick 
27466195a2Spatrick #include <machine/intr.h>
2824e5cb75Skettenis #include <machine/bus.h>
2924e5cb75Skettenis #include <machine/fdt.h>
3024e5cb75Skettenis 
3124e5cb75Skettenis #include <arm/cpufunc.h>
32466195a2Spatrick 
3324e5cb75Skettenis #include <dev/ofw/fdt.h>
3424e5cb75Skettenis #include <dev/ofw/openfirm.h>
3524e5cb75Skettenis 
36466195a2Spatrick /* registers */
37466195a2Spatrick #define GTIMER_CNTP_CTL_ENABLE		(1 << 0)
38466195a2Spatrick #define GTIMER_CNTP_CTL_IMASK		(1 << 1)
39466195a2Spatrick #define GTIMER_CNTP_CTL_ISTATUS		(1 << 2)
40466195a2Spatrick 
41466195a2Spatrick #define TIMER_FREQUENCY		24 * 1000 * 1000 /* ARM core clock */
42466195a2Spatrick int32_t agtimer_frequency = TIMER_FREQUENCY;
43466195a2Spatrick 
44466195a2Spatrick u_int agtimer_get_timecount(struct timecounter *);
45466195a2Spatrick 
46466195a2Spatrick static struct timecounter agtimer_timecounter = {
478611d3cdScheloha 	.tc_get_timecount = agtimer_get_timecount,
488611d3cdScheloha 	.tc_counter_mask = 0xffffffff,
498611d3cdScheloha 	.tc_frequency = 0,
508611d3cdScheloha 	.tc_name = "agtimer",
518611d3cdScheloha 	.tc_quality = 0,
528611d3cdScheloha 	.tc_priv = NULL,
53466195a2Spatrick };
54466195a2Spatrick 
55466195a2Spatrick struct agtimer_softc {
56466195a2Spatrick 	struct device		sc_dev;
5724e5cb75Skettenis 	int			sc_node;
58466195a2Spatrick 	u_int32_t		sc_ticks_per_second;
5918cf9b3aScheloha 	uint64_t		sc_nsec_cycle_ratio;
6018cf9b3aScheloha 	uint64_t		sc_nsec_max;
61466195a2Spatrick };
62466195a2Spatrick 
63466195a2Spatrick int		agtimer_match(struct device *, void *, void *);
64466195a2Spatrick void		agtimer_attach(struct device *, struct device *, void *);
6524e5cb75Skettenis uint64_t	agtimer_readcnt64(void);
66466195a2Spatrick int		agtimer_intr(void *);
67466195a2Spatrick void		agtimer_cpu_initclocks(void);
68466195a2Spatrick void		agtimer_delay(u_int);
69466195a2Spatrick void		agtimer_setstatclockrate(int stathz);
70466195a2Spatrick void		agtimer_set_clockrate(int32_t new_frequency);
71466195a2Spatrick void		agtimer_startclock(void);
72466195a2Spatrick 
73e3ee5e84Smpi const struct cfattach agtimer_ca = {
74466195a2Spatrick 	sizeof (struct agtimer_softc), agtimer_match, agtimer_attach
75466195a2Spatrick };
76466195a2Spatrick 
77466195a2Spatrick struct cfdriver agtimer_cd = {
78466195a2Spatrick 	NULL, "agtimer", DV_DULL
79466195a2Spatrick };
80466195a2Spatrick 
8118cf9b3aScheloha void agtimer_rearm(void *, uint64_t);
8218cf9b3aScheloha void agtimer_trigger(void *);
8318cf9b3aScheloha 
8418cf9b3aScheloha struct intrclock agtimer_intrclock = {
8518cf9b3aScheloha 	.ic_rearm = agtimer_rearm,
8618cf9b3aScheloha 	.ic_trigger = agtimer_trigger
8718cf9b3aScheloha };
8818cf9b3aScheloha 
89466195a2Spatrick uint64_t
agtimer_readcnt64(void)9024e5cb75Skettenis agtimer_readcnt64(void)
91466195a2Spatrick {
92466195a2Spatrick 	uint64_t val;
93466195a2Spatrick 
94466195a2Spatrick 	__asm volatile("mrrc p15, 0, %Q0, %R0, c14" : "=r" (val));
95466195a2Spatrick 
96466195a2Spatrick 	return (val);
97466195a2Spatrick }
98466195a2Spatrick 
99466195a2Spatrick static inline int
agtimer_get_ctrl(void)100466195a2Spatrick agtimer_get_ctrl(void)
101466195a2Spatrick {
102466195a2Spatrick 	uint32_t val;
103466195a2Spatrick 
104466195a2Spatrick 	__asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
105466195a2Spatrick 
106466195a2Spatrick 	return (val);
107466195a2Spatrick }
108466195a2Spatrick 
109466195a2Spatrick static inline int
agtimer_set_ctrl(uint32_t val)110466195a2Spatrick agtimer_set_ctrl(uint32_t val)
111466195a2Spatrick {
112466195a2Spatrick 	__asm volatile("mcr p15, 0, %[val], c14, c2, 1" : :
113466195a2Spatrick 	    [val] "r" (val));
114466195a2Spatrick 
115466195a2Spatrick 	cpu_drain_writebuf();
116466195a2Spatrick 	//isb();
117466195a2Spatrick 
118466195a2Spatrick 	return (0);
119466195a2Spatrick }
120466195a2Spatrick 
121466195a2Spatrick static inline int
agtimer_set_tval(uint32_t val)122466195a2Spatrick agtimer_set_tval(uint32_t val)
123466195a2Spatrick {
124466195a2Spatrick 	__asm volatile("mcr p15, 0, %[val], c14, c2, 0" : :
125466195a2Spatrick 	    [val] "r" (val));
126466195a2Spatrick 	cpu_drain_writebuf();
127466195a2Spatrick 	//isb();
128466195a2Spatrick 
129466195a2Spatrick 	return (0);
130466195a2Spatrick }
131466195a2Spatrick 
132466195a2Spatrick int
agtimer_match(struct device * parent,void * cfdata,void * aux)133466195a2Spatrick agtimer_match(struct device *parent, void *cfdata, void *aux)
134466195a2Spatrick {
13524e5cb75Skettenis 	struct fdt_attach_args *faa = (struct fdt_attach_args *)aux;
136466195a2Spatrick 
13724e5cb75Skettenis 	return OF_is_compatible(faa->fa_node, "arm,armv7-timer");
138466195a2Spatrick }
139466195a2Spatrick 
140466195a2Spatrick void
agtimer_attach(struct device * parent,struct device * self,void * aux)14124e5cb75Skettenis agtimer_attach(struct device *parent, struct device *self, void *aux)
142466195a2Spatrick {
143466195a2Spatrick 	struct agtimer_softc *sc = (struct agtimer_softc *)self;
14424e5cb75Skettenis 	struct fdt_attach_args *faa = aux;
145466195a2Spatrick 
14624e5cb75Skettenis 	sc->sc_node = faa->fa_node;
14724e5cb75Skettenis 
14824e5cb75Skettenis 	agtimer_frequency =
14924e5cb75Skettenis 	    OF_getpropint(sc->sc_node, "clock-frequency", agtimer_frequency);
150466195a2Spatrick 	sc->sc_ticks_per_second = agtimer_frequency;
15118cf9b3aScheloha 	sc->sc_nsec_cycle_ratio =
15218cf9b3aScheloha 	    sc->sc_ticks_per_second * (1ULL << 32) / 1000000000;
15318cf9b3aScheloha 	sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio;
1542252d02cSkettenis 	printf(": %d kHz\n", sc->sc_ticks_per_second / 1000);
155466195a2Spatrick 
156466195a2Spatrick 	/* XXX: disable user access */
157466195a2Spatrick 
158466195a2Spatrick 	/*
159466195a2Spatrick 	 * private timer and interrupts not enabled until
160466195a2Spatrick 	 * timer configures
161466195a2Spatrick 	 */
162466195a2Spatrick 
163466195a2Spatrick 	arm_clock_register(agtimer_cpu_initclocks, agtimer_delay,
164466195a2Spatrick 	    agtimer_setstatclockrate, agtimer_startclock);
165466195a2Spatrick 
166466195a2Spatrick 	agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
167466195a2Spatrick 	agtimer_timecounter.tc_priv = sc;
168466195a2Spatrick 	tc_init(&agtimer_timecounter);
16918cf9b3aScheloha 
17018cf9b3aScheloha 	agtimer_intrclock.ic_cookie = sc;
171466195a2Spatrick }
172466195a2Spatrick 
173466195a2Spatrick u_int
agtimer_get_timecount(struct timecounter * tc)174466195a2Spatrick agtimer_get_timecount(struct timecounter *tc)
175466195a2Spatrick {
17624e5cb75Skettenis 	return agtimer_readcnt64();
177466195a2Spatrick }
178466195a2Spatrick 
17918cf9b3aScheloha void
agtimer_rearm(void * cookie,uint64_t nsecs)18018cf9b3aScheloha agtimer_rearm(void *cookie, uint64_t nsecs)
18118cf9b3aScheloha {
18218cf9b3aScheloha 	struct agtimer_softc *sc = cookie;
18318cf9b3aScheloha 	uint32_t cycles;
18418cf9b3aScheloha 
18518cf9b3aScheloha 	if (nsecs > sc->sc_nsec_max)
18618cf9b3aScheloha 		nsecs = sc->sc_nsec_max;
18718cf9b3aScheloha 	cycles = (nsecs * sc->sc_nsec_cycle_ratio) >> 32;
18818cf9b3aScheloha 	if (cycles > INT32_MAX)
18918cf9b3aScheloha 		cycles = INT32_MAX;
19018cf9b3aScheloha 	agtimer_set_tval(cycles);
19118cf9b3aScheloha }
19218cf9b3aScheloha 
19318cf9b3aScheloha void
agtimer_trigger(void * unused)19418cf9b3aScheloha agtimer_trigger(void *unused)
19518cf9b3aScheloha {
19618cf9b3aScheloha 	agtimer_set_tval(0);
19718cf9b3aScheloha }
19818cf9b3aScheloha 
199466195a2Spatrick int
agtimer_intr(void * frame)200466195a2Spatrick agtimer_intr(void *frame)
201466195a2Spatrick {
20218cf9b3aScheloha 	return clockintr_dispatch(frame);
203466195a2Spatrick }
204466195a2Spatrick 
205466195a2Spatrick void
agtimer_set_clockrate(int32_t new_frequency)206466195a2Spatrick agtimer_set_clockrate(int32_t new_frequency)
207466195a2Spatrick {
208466195a2Spatrick 	struct agtimer_softc	*sc = agtimer_cd.cd_devs[0];
209466195a2Spatrick 
210466195a2Spatrick 	agtimer_frequency = new_frequency;
211466195a2Spatrick 
212466195a2Spatrick 	if (sc == NULL)
213466195a2Spatrick 		return;
214466195a2Spatrick 
215466195a2Spatrick 	sc->sc_ticks_per_second = agtimer_frequency;
21618cf9b3aScheloha 	sc->sc_nsec_cycle_ratio =
21718cf9b3aScheloha 	    sc->sc_ticks_per_second * (1ULL << 32) / 1000000000;
21818cf9b3aScheloha 	sc->sc_nsec_max = UINT64_MAX / sc->sc_nsec_cycle_ratio;
21918cf9b3aScheloha 
220466195a2Spatrick 	agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
22118cf9b3aScheloha 
2222252d02cSkettenis 	printf("agtimer0: adjusting clock: new rate %d kHz\n",
223466195a2Spatrick 	    sc->sc_ticks_per_second / 1000);
224466195a2Spatrick }
225466195a2Spatrick 
226466195a2Spatrick void
agtimer_cpu_initclocks(void)22737e35e27Sjsg agtimer_cpu_initclocks(void)
228466195a2Spatrick {
229466195a2Spatrick 	struct agtimer_softc	*sc = agtimer_cd.cd_devs[0];
230466195a2Spatrick 
231466195a2Spatrick 	stathz = hz;
23218cf9b3aScheloha 	profhz = stathz * 10;
233b3ef18bdScheloha 	statclock_is_randomized = 1;
234466195a2Spatrick 
235466195a2Spatrick 	if (sc->sc_ticks_per_second != agtimer_frequency) {
236466195a2Spatrick 		agtimer_set_clockrate(agtimer_frequency);
237466195a2Spatrick 	}
238466195a2Spatrick 
23924e5cb75Skettenis 	/* Setup secure and non-secure timer IRQs. */
24024e5cb75Skettenis 	arm_intr_establish_fdt_idx(sc->sc_node, 0, IPL_CLOCK,
24124e5cb75Skettenis 	    agtimer_intr, NULL, "tick");
24224e5cb75Skettenis 	arm_intr_establish_fdt_idx(sc->sc_node, 1, IPL_CLOCK,
24324e5cb75Skettenis 	    agtimer_intr, NULL, "tick");
244466195a2Spatrick }
245466195a2Spatrick 
246466195a2Spatrick void
agtimer_delay(u_int usecs)247466195a2Spatrick agtimer_delay(u_int usecs)
248466195a2Spatrick {
249466195a2Spatrick 	u_int32_t		clock, oclock, delta, delaycnt;
250466195a2Spatrick 	volatile int		j;
251466195a2Spatrick 	int			csec, usec;
252466195a2Spatrick 
25324e5cb75Skettenis 	if (usecs > (0x80000000 / agtimer_frequency)) {
254466195a2Spatrick 		csec = usecs / 10000;
255466195a2Spatrick 		usec = usecs % 10000;
256466195a2Spatrick 
25724e5cb75Skettenis 		delaycnt = (agtimer_frequency / 100) * csec +
25824e5cb75Skettenis 		    (agtimer_frequency / 100) * usec / 10000;
259466195a2Spatrick 	} else {
26024e5cb75Skettenis 		delaycnt = agtimer_frequency * usecs / 1000000;
261466195a2Spatrick 	}
262466195a2Spatrick 	if (delaycnt <= 1)
263466195a2Spatrick 		for (j = 100; j > 0; j--)
264466195a2Spatrick 			;
265466195a2Spatrick 
26624e5cb75Skettenis 	oclock = agtimer_readcnt64();
267466195a2Spatrick 	while (1) {
268466195a2Spatrick 		for (j = 100; j > 0; j--)
269466195a2Spatrick 			;
27024e5cb75Skettenis 		clock = agtimer_readcnt64();
271466195a2Spatrick 		delta = clock - oclock;
272466195a2Spatrick 		if (delta > delaycnt)
273466195a2Spatrick 			break;
274466195a2Spatrick 	}
275466195a2Spatrick }
276466195a2Spatrick 
277466195a2Spatrick void
agtimer_setstatclockrate(int newhz)278466195a2Spatrick agtimer_setstatclockrate(int newhz)
279466195a2Spatrick {
280466195a2Spatrick }
281466195a2Spatrick 
282466195a2Spatrick void
agtimer_startclock(void)283466195a2Spatrick agtimer_startclock(void)
284466195a2Spatrick {
285466195a2Spatrick 	uint32_t reg;
286466195a2Spatrick 
28718cf9b3aScheloha 	clockintr_cpu_init(&agtimer_intrclock);
288466195a2Spatrick 
289466195a2Spatrick 	reg = agtimer_get_ctrl();
290df7cbe9dSkettenis 	reg &= ~GTIMER_CNTP_CTL_IMASK;
291466195a2Spatrick 	reg |= GTIMER_CNTP_CTL_ENABLE;
29218cf9b3aScheloha 	agtimer_set_tval(INT32_MAX);
293466195a2Spatrick 	agtimer_set_ctrl(reg);
29418cf9b3aScheloha 
29518cf9b3aScheloha 	clockintr_trigger();
296466195a2Spatrick }
29724e5cb75Skettenis 
29824e5cb75Skettenis void
agtimer_init(void)29924e5cb75Skettenis agtimer_init(void)
30024e5cb75Skettenis {
30124e5cb75Skettenis 	uint32_t id_pfr1, cntfrq = 0;
30224e5cb75Skettenis 
30324e5cb75Skettenis 	/* Check for Generic Timer support. */
30424e5cb75Skettenis 	__asm volatile("mrc p15, 0, %0, c0, c1, 1" : "=r"(id_pfr1));
30524e5cb75Skettenis 	if ((id_pfr1 & 0x000f0000) == 0x00010000)
30624e5cb75Skettenis 		__asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (cntfrq));
30724e5cb75Skettenis 
30824e5cb75Skettenis 	if (cntfrq != 0) {
30924e5cb75Skettenis 		agtimer_frequency = cntfrq;
31024e5cb75Skettenis 		arm_clock_register(NULL, agtimer_delay, NULL, NULL);
31124e5cb75Skettenis 	}
31224e5cb75Skettenis }
313