xref: /openbsd-src/sys/arch/arm/cortex/amptimer.c (revision de8cc8edbc71bd3e3bc7fbffa27ba0e564c37d8b)
1 /* $OpenBSD: amptimer.c,v 1.11 2021/02/23 04:44:30 cheloha Exp $ */
2 /*
3  * Copyright (c) 2011 Dale Rahn <drahn@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/queue.h>
21 #include <sys/malloc.h>
22 #include <sys/device.h>
23 #include <sys/kernel.h>
24 #include <sys/timetc.h>
25 #include <sys/evcount.h>
26 
27 #include <arm/cpufunc.h>
28 #include <machine/bus.h>
29 #include <machine/intr.h>
30 #include <arm/cortex/cortex.h>
31 
32 /* offset from periphbase */
33 #define GTIMER_ADDR	0x200
34 #define GTIMER_SIZE	0x100
35 
36 /* registers */
37 #define GTIMER_CNT_LOW		0x00
38 #define GTIMER_CNT_HIGH		0x04
39 #define GTIMER_CTRL		0x08
40 #define GTIMER_CTRL_AA		(1 << 3)
41 #define GTIMER_CTRL_IRQ		(1 << 2)
42 #define GTIMER_CTRL_COMP	(1 << 1)
43 #define GTIMER_CTRL_TIMER	(1 << 0)
44 #define GTIMER_STATUS		0x0c
45 #define GTIMER_STATUS_EVENT	(1 << 0)
46 #define GTIMER_CMP_LOW		0x10
47 #define GTIMER_CMP_HIGH		0x14
48 #define GTIMER_AUTOINC		0x18
49 
50 /* offset from periphbase */
51 #define PTIMER_ADDR		0x600
52 #define PTIMER_SIZE		0x100
53 
54 /* registers */
55 #define PTIMER_LOAD		0x0
56 #define PTIMER_CNT		0x4
57 #define PTIMER_CTRL		0x8
58 #define PTIMER_CTRL_ENABLE	(1<<0)
59 #define PTIMER_CTRL_AUTORELOAD	(1<<1)
60 #define PTIMER_CTRL_IRQEN	(1<<2)
61 #define PTIMER_STATUS		0xC
62 #define PTIMER_STATUS_EVENT	(1<<0)
63 
64 #define TIMER_FREQUENCY		396 * 1000 * 1000 /* ARM core clock */
65 int32_t amptimer_frequency = TIMER_FREQUENCY;
66 
67 u_int amptimer_get_timecount(struct timecounter *);
68 
69 static struct timecounter amptimer_timecounter = {
70 	.tc_get_timecount = amptimer_get_timecount,
71 	.tc_poll_pps = NULL,
72 	.tc_counter_mask = 0xffffffff,
73 	.tc_frequency = 0,
74 	.tc_name = "amptimer",
75 	.tc_quality = 0,
76 	.tc_priv = NULL,
77 	.tc_user = 0,
78 };
79 
80 #define MAX_ARM_CPUS	8
81 
82 struct amptimer_pcpu_softc {
83 	uint64_t 		pc_nexttickevent;
84 	uint64_t 		pc_nextstatevent;
85 	u_int32_t		pc_ticks_err_sum;
86 };
87 
88 struct amptimer_softc {
89 	struct device		sc_dev;
90 	bus_space_tag_t		sc_iot;
91 	bus_space_handle_t	sc_ioh;
92 	bus_space_handle_t	sc_pioh;
93 
94 	struct amptimer_pcpu_softc sc_pstat[MAX_ARM_CPUS];
95 
96 	u_int32_t		sc_ticks_err_cnt;
97 	u_int32_t		sc_ticks_per_second;
98 	u_int32_t		sc_ticks_per_intr;
99 	u_int32_t		sc_statvar;
100 	u_int32_t		sc_statmin;
101 
102 #ifdef AMPTIMER_DEBUG
103 	struct evcount		sc_clk_count;
104 	struct evcount		sc_stat_count;
105 #endif
106 };
107 
108 int		amptimer_match(struct device *, void *, void *);
109 void		amptimer_attach(struct device *, struct device *, void *);
110 uint64_t	amptimer_readcnt64(struct amptimer_softc *sc);
111 int		amptimer_intr(void *);
112 void		amptimer_cpu_initclocks(void);
113 void		amptimer_delay(u_int);
114 void		amptimer_setstatclockrate(int stathz);
115 void		amptimer_set_clockrate(int32_t new_frequency);
116 void		amptimer_startclock(void);
117 
118 /* hack - XXXX
119  * gptimer connects directly to ampintc, not thru the generic
120  * interface because it uses an 'internal' interrupt
121  * not a peripheral interrupt.
122  */
123 void	*ampintc_intr_establish(int, int, int, struct cpu_info *,
124 	    int (*)(void *), void *, char *);
125 
126 
127 
128 struct cfattach amptimer_ca = {
129 	sizeof (struct amptimer_softc), amptimer_match, amptimer_attach
130 };
131 
132 struct cfdriver amptimer_cd = {
133 	NULL, "amptimer", DV_DULL
134 };
135 
136 uint64_t
137 amptimer_readcnt64(struct amptimer_softc *sc)
138 {
139 	uint32_t high0, high1, low;
140 	bus_space_tag_t iot = sc->sc_iot;
141 	bus_space_handle_t ioh = sc->sc_ioh;
142 
143 	do {
144 		high0 = bus_space_read_4(iot, ioh, GTIMER_CNT_HIGH);
145 		low = bus_space_read_4(iot, ioh, GTIMER_CNT_LOW);
146 		high1 = bus_space_read_4(iot, ioh, GTIMER_CNT_HIGH);
147 	} while (high0 != high1);
148 
149 	return ((((uint64_t)high1) << 32) | low);
150 }
151 
152 int
153 amptimer_match(struct device *parent, void *cfdata, void *aux)
154 {
155 	if ((cpufunc_id() & CPU_ID_CORTEX_A9_MASK) == CPU_ID_CORTEX_A9)
156 		return (1);
157 
158 	return 0;
159 }
160 
161 void
162 amptimer_attach(struct device *parent, struct device *self, void *args)
163 {
164 	struct amptimer_softc *sc = (struct amptimer_softc *)self;
165 	struct cortex_attach_args *ia = args;
166 	bus_space_handle_t ioh, pioh;
167 
168 	sc->sc_iot = ia->ca_iot;
169 
170 	if (bus_space_map(sc->sc_iot, ia->ca_periphbase + GTIMER_ADDR,
171 	    GTIMER_SIZE, 0, &ioh))
172 		panic("amptimer_attach: bus_space_map global timer failed!");
173 
174 	if (bus_space_map(sc->sc_iot, ia->ca_periphbase + PTIMER_ADDR,
175 	    PTIMER_SIZE, 0, &pioh))
176 		panic("amptimer_attach: bus_space_map priv timer failed!");
177 
178 	sc->sc_ticks_per_second = amptimer_frequency;
179 	printf(": %d kHz\n", sc->sc_ticks_per_second / 1000);
180 
181 	sc->sc_ioh = ioh;
182 	sc->sc_pioh = pioh;
183 
184 	/* disable global timer */
185 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CTRL, 0);
186 
187 	/* XXX ??? reset counters to 0 - gives us uptime in the counter */
188 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CNT_LOW, 0);
189 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CNT_HIGH, 0);
190 
191 	/* enable global timer */
192 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CTRL, GTIMER_CTRL_TIMER);
193 
194 #if defined(USE_GTIMER_CMP)
195 
196 	/* clear event */
197 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_STATUS, 1);
198 #else
199 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL, 0);
200 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_STATUS,
201 	    PTIMER_STATUS_EVENT);
202 #endif
203 
204 
205 #ifdef AMPTIMER_DEBUG
206 	evcount_attach(&sc->sc_clk_count, "clock", NULL);
207 	evcount_attach(&sc->sc_stat_count, "stat", NULL);
208 #endif
209 
210 	/*
211 	 * private timer and interrupts not enabled until
212 	 * timer configures
213 	 */
214 
215 	arm_clock_register(amptimer_cpu_initclocks, amptimer_delay,
216 	    amptimer_setstatclockrate, amptimer_startclock);
217 
218 	amptimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
219 	amptimer_timecounter.tc_priv = sc;
220 
221 	tc_init(&amptimer_timecounter);
222 }
223 
224 u_int
225 amptimer_get_timecount(struct timecounter *tc)
226 {
227 	struct amptimer_softc *sc = amptimer_timecounter.tc_priv;
228 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CNT_LOW);
229 }
230 
231 int
232 amptimer_intr(void *frame)
233 {
234 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
235 	struct amptimer_pcpu_softc *pc = &sc->sc_pstat[CPU_INFO_UNIT(curcpu())];
236 	uint64_t		 now;
237 	uint64_t		 nextevent;
238 	uint32_t		 r, reg;
239 #if defined(USE_GTIMER_CMP)
240 	int			 skip = 1;
241 #else
242 	int64_t			 delay;
243 #endif
244 	int			 rc = 0;
245 
246 	/*
247 	 * DSR - I know that the tick timer is 64 bits, but the following
248 	 * code deals with rollover, so there is no point in dealing
249 	 * with the 64 bit math, just let the 32 bit rollover
250 	 * do the right thing
251 	 */
252 
253 	now = amptimer_readcnt64(sc);
254 
255 	while (pc->pc_nexttickevent <= now) {
256 		pc->pc_nexttickevent += sc->sc_ticks_per_intr;
257 		pc->pc_ticks_err_sum += sc->sc_ticks_err_cnt;
258 
259 		/* looping a few times is faster than divide */
260 		while (pc->pc_ticks_err_sum  > hz) {
261 			pc->pc_nexttickevent += 1;
262 			pc->pc_ticks_err_sum -= hz;
263 		}
264 
265 #ifdef AMPTIMER_DEBUG
266 		sc->sc_clk_count.ec_count++;
267 #endif
268 		rc = 1;
269 		hardclock(frame);
270 	}
271 	while (pc->pc_nextstatevent <= now) {
272 		do {
273 			r = random() & (sc->sc_statvar -1);
274 		} while (r == 0); /* random == 0 not allowed */
275 		pc->pc_nextstatevent += sc->sc_statmin + r;
276 
277 		/* XXX - correct nextstatevent? */
278 #ifdef AMPTIMER_DEBUG
279 		sc->sc_stat_count.ec_count++;
280 #endif
281 		rc = 1;
282 		statclock(frame);
283 	}
284 
285 	if (pc->pc_nexttickevent < pc->pc_nextstatevent)
286 		nextevent = pc->pc_nexttickevent;
287 	else
288 		nextevent = pc->pc_nextstatevent;
289 
290 #if defined(USE_GTIMER_CMP)
291 again:
292 	reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL);
293 	reg &= ~GTIMER_CTRL_COMP;
294 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL, reg);
295 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CMP_LOW,
296 	    nextevent & 0xffffffff);
297 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CMP_HIGH,
298 	    nextevent >> 32);
299 	reg |= GTIMER_CTRL_COMP;
300 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL, reg);
301 
302 	now = amptimer_readcnt64(sc);
303 	if (now >= nextevent) {
304 		nextevent = now + skip;
305 		skip += 1;
306 		goto again;
307 	}
308 #else
309 	/* clear old status */
310 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_STATUS,
311 	    PTIMER_STATUS_EVENT);
312 
313 	delay = nextevent - now;
314 	if (delay < 0)
315 		delay = 1;
316 
317 	reg = bus_space_read_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL);
318 	if ((reg & (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN)) !=
319 	    (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN))
320 		bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL,
321 		    (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN));
322 
323 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_LOAD, delay);
324 #endif
325 
326 	return (rc);
327 }
328 
329 void
330 amptimer_set_clockrate(int32_t new_frequency)
331 {
332 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
333 
334 	amptimer_frequency = new_frequency;
335 
336 	if (sc == NULL)
337 		return;
338 
339 	sc->sc_ticks_per_second = amptimer_frequency;
340 	amptimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
341 	printf("amptimer0: adjusting clock: new rate %d kHz\n",
342 	    sc->sc_ticks_per_second / 1000);
343 }
344 
345 void
346 amptimer_cpu_initclocks()
347 {
348 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
349 	struct amptimer_pcpu_softc *pc = &sc->sc_pstat[CPU_INFO_UNIT(curcpu())];
350 	uint64_t		 next;
351 #if defined(USE_GTIMER_CMP)
352 	uint32_t		 reg;
353 #endif
354 
355 	stathz = hz;
356 	profhz = hz * 10;
357 
358 	if (sc->sc_ticks_per_second != amptimer_frequency) {
359 		amptimer_set_clockrate(amptimer_frequency);
360 	}
361 
362 	amptimer_setstatclockrate(stathz);
363 
364 	sc->sc_ticks_per_intr = sc->sc_ticks_per_second / hz;
365 	sc->sc_ticks_err_cnt = sc->sc_ticks_per_second % hz;
366 	pc->pc_ticks_err_sum = 0;
367 
368 	/* establish interrupts */
369 	/* XXX - irq */
370 #if defined(USE_GTIMER_CMP)
371 	ampintc_intr_establish(27, IST_EDGE_RISING, IPL_CLOCK,
372 	    NULL, amptimer_intr, NULL, "tick");
373 #else
374 	ampintc_intr_establish(29, IST_EDGE_RISING, IPL_CLOCK,
375 	    NULL, amptimer_intr, NULL, "tick");
376 #endif
377 
378 	next = amptimer_readcnt64(sc) + sc->sc_ticks_per_intr;
379 	pc->pc_nexttickevent = pc->pc_nextstatevent = next;
380 
381 #if defined(USE_GTIMER_CMP)
382 	reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL);
383 	reg &= ~GTIMER_CTRL_COMP;
384 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL, reg);
385 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CMP_LOW,
386 	    next & 0xffffffff);
387 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CMP_HIGH,
388 	    next >> 32);
389 	reg |= GTIMER_CTRL_COMP | GTIMER_CTRL_IRQ;
390 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL, reg);
391 #else
392 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL,
393 	    (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN));
394 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_LOAD,
395 	    sc->sc_ticks_per_intr);
396 #endif
397 }
398 
399 void
400 amptimer_delay(u_int usecs)
401 {
402 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
403 	u_int32_t		clock, oclock, delta, delaycnt;
404 	volatile int		j;
405 	int			csec, usec;
406 
407 	if (usecs > (0x80000000 / (sc->sc_ticks_per_second))) {
408 		csec = usecs / 10000;
409 		usec = usecs % 10000;
410 
411 		delaycnt = (sc->sc_ticks_per_second / 100) * csec +
412 		    (sc->sc_ticks_per_second / 100) * usec / 10000;
413 	} else {
414 		delaycnt = sc->sc_ticks_per_second * usecs / 1000000;
415 	}
416 	if (delaycnt <= 1)
417 		for (j = 100; j > 0; j--)
418 			;
419 
420 	oclock = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CNT_LOW);
421 	while (1) {
422 		for (j = 100; j > 0; j--)
423 			;
424 		clock = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
425 		    GTIMER_CNT_LOW);
426 		delta = clock - oclock;
427 		if (delta > delaycnt)
428 			break;
429 	}
430 }
431 
432 void
433 amptimer_setstatclockrate(int newhz)
434 {
435 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
436 	int			 minint, statint;
437 	int			 s;
438 
439 	s = splclock();
440 
441 	statint = sc->sc_ticks_per_second / newhz;
442 	/* calculate largest 2^n which is smaller that just over half statint */
443 	sc->sc_statvar = 0x40000000; /* really big power of two */
444 	minint = statint / 2 + 100;
445 	while (sc->sc_statvar > minint)
446 		sc->sc_statvar >>= 1;
447 
448 	sc->sc_statmin = statint - (sc->sc_statvar >> 1);
449 
450 	splx(s);
451 
452 	/*
453 	 * XXX this allows the next stat timer to occur then it switches
454 	 * to the new frequency. Rather than switching instantly.
455 	 */
456 }
457 
458 void
459 amptimer_startclock(void)
460 {
461 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
462 	struct amptimer_pcpu_softc *pc = &sc->sc_pstat[CPU_INFO_UNIT(curcpu())];
463 	uint64_t nextevent;
464 
465 	nextevent = amptimer_readcnt64(sc) + sc->sc_ticks_per_intr;
466 	pc->pc_nexttickevent = pc->pc_nextstatevent = nextevent;
467 
468 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_LOAD,
469 		sc->sc_ticks_per_intr);
470 }
471