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