xref: /openbsd-src/sys/arch/arm64/dev/agtimer.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /* $OpenBSD: agtimer.c,v 1.11 2019/10/05 11:27:02 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/queue.h>
22 #include <sys/malloc.h>
23 #include <sys/device.h>
24 #include <sys/kernel.h>
25 #include <sys/timetc.h>
26 #include <sys/evcount.h>
27 
28 #include <machine/intr.h>
29 #include <machine/bus.h>
30 #include <machine/fdt.h>
31 
32 #include <dev/ofw/fdt.h>
33 #include <dev/ofw/openfirm.h>
34 
35 /* registers */
36 #define GTIMER_CNTV_CTL_ENABLE		(1 << 0)
37 #define GTIMER_CNTV_CTL_IMASK		(1 << 1)
38 #define GTIMER_CNTV_CTL_ISTATUS		(1 << 2)
39 
40 #define TIMER_FREQUENCY		24 * 1000 * 1000 /* ARM core clock */
41 int32_t agtimer_frequency = TIMER_FREQUENCY;
42 
43 u_int agtimer_get_timecount(struct timecounter *);
44 
45 static struct timecounter agtimer_timecounter = {
46 	agtimer_get_timecount, NULL, 0x7fffffff, 0, "agtimer", 0, NULL
47 };
48 
49 struct agtimer_pcpu_softc {
50 	uint64_t 		pc_nexttickevent;
51 	uint64_t 		pc_nextstatevent;
52 	u_int32_t		pc_ticks_err_sum;
53 };
54 
55 struct agtimer_softc {
56 	struct device		sc_dev;
57 	int			sc_node;
58 
59 	struct agtimer_pcpu_softc sc_pstat[MAXCPUS];
60 
61 	u_int32_t		sc_ticks_err_cnt;
62 	u_int32_t		sc_ticks_per_second;
63 	u_int32_t		sc_ticks_per_intr;
64 	u_int32_t		sc_statvar;
65 	u_int32_t		sc_statmin;
66 
67 #ifdef AMPTIMER_DEBUG
68 	struct evcount		sc_clk_count;
69 	struct evcount		sc_stat_count;
70 #endif
71 	void			*sc_ih;
72 };
73 
74 int		agtimer_match(struct device *, void *, void *);
75 void		agtimer_attach(struct device *, struct device *, void *);
76 uint64_t	agtimer_readcnt64(void);
77 int		agtimer_intr(void *);
78 void		agtimer_cpu_initclocks(void);
79 void		agtimer_delay(u_int);
80 void		agtimer_setstatclockrate(int stathz);
81 void		agtimer_set_clockrate(int32_t new_frequency);
82 void		agtimer_startclock(void);
83 
84 struct cfattach agtimer_ca = {
85 	sizeof (struct agtimer_softc), agtimer_match, agtimer_attach
86 };
87 
88 struct cfdriver agtimer_cd = {
89 	NULL, "agtimer", DV_DULL
90 };
91 
92 uint64_t
93 agtimer_readcnt64(void)
94 {
95 	uint64_t val0, val1;
96 
97 	/*
98 	 * Work around Cortex-A73 errata 858921, where there is a
99 	 * one-cycle window where the read might return the old value
100 	 * for the low 32 bits and the new value for the high 32 bits
101 	 * upon roll-over of the low 32 bits.
102 	 */
103 	__asm volatile("isb" : : : "memory");
104 	__asm volatile("mrs %x0, CNTVCT_EL0" : "=r" (val0));
105 	__asm volatile("mrs %x0, CNTVCT_EL0" : "=r" (val1));
106 	return ((val0 ^ val1) & 0x100000000ULL) ? val0 : val1;
107 }
108 
109 static inline uint64_t
110 agtimer_get_freq(void)
111 {
112 	uint64_t val;
113 
114 	__asm volatile("mrs %x0, CNTFRQ_EL0" : "=r" (val));
115 
116 	return (val);
117 }
118 
119 static inline int
120 agtimer_get_ctrl(void)
121 {
122 	uint32_t val;
123 
124 	__asm volatile("mrs %x0, CNTV_CTL_EL0" : "=r" (val));
125 
126 	return (val);
127 }
128 
129 static inline int
130 agtimer_set_ctrl(uint32_t val)
131 {
132 	__asm volatile("msr CNTV_CTL_EL0, %x0" : : "r" (val));
133 	__asm volatile("isb" : : : "memory");
134 
135 	return (0);
136 }
137 
138 static inline int
139 agtimer_set_tval(uint32_t val)
140 {
141 	__asm volatile("msr CNTV_TVAL_EL0, %x0" : : "r" (val));
142 	__asm volatile("isb" : : : "memory");
143 
144 	return (0);
145 }
146 
147 int
148 agtimer_match(struct device *parent, void *cfdata, void *aux)
149 {
150 	struct fdt_attach_args *faa = (struct fdt_attach_args *)aux;
151 
152 	return (OF_is_compatible(faa->fa_node, "arm,armv7-timer") ||
153 	    OF_is_compatible(faa->fa_node, "arm,armv8-timer"));
154 }
155 
156 void
157 agtimer_attach(struct device *parent, struct device *self, void *aux)
158 {
159 	struct agtimer_softc *sc = (struct agtimer_softc *)self;
160 	struct fdt_attach_args *faa = aux;
161 
162 	sc->sc_node = faa->fa_node;
163 
164 	if (agtimer_get_freq() != 0)
165 		agtimer_frequency = agtimer_get_freq();
166 	agtimer_frequency =
167 	    OF_getpropint(sc->sc_node, "clock-frequency", agtimer_frequency);
168 	sc->sc_ticks_per_second = agtimer_frequency;
169 
170 	printf(": tick rate %d KHz\n", sc->sc_ticks_per_second /1000);
171 
172 	/* XXX: disable user access */
173 
174 #ifdef AMPTIMER_DEBUG
175 	evcount_attach(&sc->sc_clk_count, "clock", NULL);
176 	evcount_attach(&sc->sc_stat_count, "stat", NULL);
177 #endif
178 
179 	/*
180 	 * private timer and interrupts not enabled until
181 	 * timer configures
182 	 */
183 
184 	arm_clock_register(agtimer_cpu_initclocks, agtimer_delay,
185 	    agtimer_setstatclockrate, agtimer_startclock);
186 
187 	agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
188 	agtimer_timecounter.tc_priv = sc;
189 
190 	tc_init(&agtimer_timecounter);
191 }
192 
193 u_int
194 agtimer_get_timecount(struct timecounter *tc)
195 {
196 	return agtimer_readcnt64();
197 }
198 
199 int
200 agtimer_intr(void *frame)
201 {
202 	struct agtimer_softc	*sc = agtimer_cd.cd_devs[0];
203 	struct agtimer_pcpu_softc *pc = &sc->sc_pstat[CPU_INFO_UNIT(curcpu())];
204 	uint64_t		 now;
205 	uint64_t		 nextevent;
206 	uint32_t		 r;
207 #if defined(USE_GTIMER_CMP)
208 	int			 skip = 1;
209 #else
210 	int64_t			 delay;
211 #endif
212 	int			 rc = 0;
213 
214 	/*
215 	 * DSR - I know that the tick timer is 64 bits, but the following
216 	 * code deals with rollover, so there is no point in dealing
217 	 * with the 64 bit math, just let the 32 bit rollover
218 	 * do the right thing
219 	 */
220 
221 	now = agtimer_readcnt64();
222 
223 	while (pc->pc_nexttickevent <= now) {
224 		pc->pc_nexttickevent += sc->sc_ticks_per_intr;
225 		pc->pc_ticks_err_sum += sc->sc_ticks_err_cnt;
226 
227 		/* looping a few times is faster than divide */
228 		while (pc->pc_ticks_err_sum > hz) {
229 			pc->pc_nexttickevent += 1;
230 			pc->pc_ticks_err_sum -= hz;
231 		}
232 
233 #ifdef AMPTIMER_DEBUG
234 		sc->sc_clk_count.ec_count++;
235 #endif
236 		rc = 1;
237 		hardclock(frame);
238 	}
239 	while (pc->pc_nextstatevent <= now) {
240 		do {
241 			r = random() & (sc->sc_statvar -1);
242 		} while (r == 0); /* random == 0 not allowed */
243 		pc->pc_nextstatevent += sc->sc_statmin + r;
244 
245 		/* XXX - correct nextstatevent? */
246 #ifdef AMPTIMER_DEBUG
247 		sc->sc_stat_count.ec_count++;
248 #endif
249 		rc = 1;
250 		statclock(frame);
251 	}
252 
253 	if (pc->pc_nexttickevent < pc->pc_nextstatevent)
254 		nextevent = pc->pc_nexttickevent;
255 	else
256 		nextevent = pc->pc_nextstatevent;
257 
258 	delay = nextevent - now;
259 	if (delay < 0)
260 		delay = 1;
261 
262 	agtimer_set_tval(delay);
263 
264 	return (rc);
265 }
266 
267 void
268 agtimer_set_clockrate(int32_t new_frequency)
269 {
270 	struct agtimer_softc	*sc = agtimer_cd.cd_devs[0];
271 
272 	agtimer_frequency = new_frequency;
273 
274 	if (sc == NULL)
275 		return;
276 
277 	sc->sc_ticks_per_second = agtimer_frequency;
278 	agtimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
279 	printf("agtimer0: adjusting clock: new tick rate %d KHz\n",
280 	    sc->sc_ticks_per_second /1000);
281 }
282 
283 void
284 agtimer_cpu_initclocks()
285 {
286 	struct agtimer_softc	*sc = agtimer_cd.cd_devs[0];
287 	struct agtimer_pcpu_softc *pc = &sc->sc_pstat[CPU_INFO_UNIT(curcpu())];
288 	uint32_t		 reg;
289 	uint64_t		 next;
290 
291 	stathz = hz;
292 	profhz = hz * 10;
293 
294 	if (sc->sc_ticks_per_second != agtimer_frequency) {
295 		agtimer_set_clockrate(agtimer_frequency);
296 	}
297 
298 	agtimer_setstatclockrate(stathz);
299 
300 	sc->sc_ticks_per_intr = sc->sc_ticks_per_second / hz;
301 	sc->sc_ticks_err_cnt = sc->sc_ticks_per_second % hz;
302 	pc->pc_ticks_err_sum = 0;
303 
304 	/* configure virtual timer interupt */
305 	sc->sc_ih = arm_intr_establish_fdt_idx(sc->sc_node, 2,
306 	    IPL_CLOCK|IPL_MPSAFE, agtimer_intr, NULL, "tick");
307 
308 	next = agtimer_readcnt64() + sc->sc_ticks_per_intr;
309 	pc->pc_nexttickevent = pc->pc_nextstatevent = next;
310 
311 	reg = agtimer_get_ctrl();
312 	reg &= ~GTIMER_CNTV_CTL_IMASK;
313 	reg |= GTIMER_CNTV_CTL_ENABLE;
314 	agtimer_set_tval(sc->sc_ticks_per_second);
315 	agtimer_set_ctrl(reg);
316 }
317 
318 void
319 agtimer_delay(u_int usecs)
320 {
321 	uint64_t		clock, oclock, delta, delaycnt;
322 	uint64_t		csec, usec;
323 	volatile int		j;
324 
325 	if (usecs > (0x80000000 / agtimer_frequency)) {
326 		csec = usecs / 10000;
327 		usec = usecs % 10000;
328 
329 		delaycnt = (agtimer_frequency / 100) * csec +
330 		    (agtimer_frequency / 100) * usec / 10000;
331 	} else {
332 		delaycnt = agtimer_frequency * usecs / 1000000;
333 	}
334 	if (delaycnt <= 1)
335 		for (j = 100; j > 0; j--)
336 			;
337 
338 	oclock = agtimer_readcnt64();
339 	while (1) {
340 		for (j = 100; j > 0; j--)
341 			;
342 		clock = agtimer_readcnt64();
343 		delta = clock - oclock;
344 		if (delta > delaycnt)
345 			break;
346 	}
347 }
348 
349 void
350 agtimer_setstatclockrate(int newhz)
351 {
352 	struct agtimer_softc	*sc = agtimer_cd.cd_devs[0];
353 	int			 minint, statint;
354 	int			 s;
355 
356 	s = splclock();
357 
358 	statint = sc->sc_ticks_per_second / newhz;
359 	/* calculate largest 2^n which is smaller that just over half statint */
360 	sc->sc_statvar = 0x40000000; /* really big power of two */
361 	minint = statint / 2 + 100;
362 	while (sc->sc_statvar > minint)
363 		sc->sc_statvar >>= 1;
364 
365 	sc->sc_statmin = statint - (sc->sc_statvar >> 1);
366 
367 	splx(s);
368 
369 	/*
370 	 * XXX this allows the next stat timer to occur then it switches
371 	 * to the new frequency. Rather than switching instantly.
372 	 */
373 }
374 
375 void
376 agtimer_startclock(void)
377 {
378 	struct agtimer_softc	*sc = agtimer_cd.cd_devs[0];
379 	struct agtimer_pcpu_softc *pc = &sc->sc_pstat[CPU_INFO_UNIT(curcpu())];
380 	uint64_t nextevent;
381 	uint32_t reg;
382 
383 	nextevent = agtimer_readcnt64() + sc->sc_ticks_per_intr;
384 	pc->pc_nexttickevent = pc->pc_nextstatevent = nextevent;
385 
386 	arm_intr_route(sc->sc_ih, 1, curcpu());
387 
388 	reg = agtimer_get_ctrl();
389 	reg &= ~GTIMER_CNTV_CTL_IMASK;
390 	reg |= GTIMER_CNTV_CTL_ENABLE;
391 	agtimer_set_tval(sc->sc_ticks_per_second);
392 	agtimer_set_ctrl(reg);
393 }
394 
395 void
396 agtimer_init(void)
397 {
398 	uint64_t cntfrq = 0;
399 
400 	/* XXX: Check for Generic Timer support. */
401 	cntfrq = agtimer_get_freq();
402 
403 	if (cntfrq != 0) {
404 		agtimer_frequency = cntfrq;
405 		arm_clock_register(NULL, agtimer_delay, NULL, NULL);
406 	}
407 }
408