xref: /openbsd/sys/arch/arm/cortex/amptimer.c (revision 3cab2bb3)
1 /* $OpenBSD: amptimer.c,v 1.9 2020/07/14 15:34:14 patrick 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 	amptimer_get_timecount, NULL, 0xffffffff, 0, "amptimer", 0, NULL, 0
71 };
72 
73 #define MAX_ARM_CPUS	8
74 
75 struct amptimer_pcpu_softc {
76 	uint64_t 		pc_nexttickevent;
77 	uint64_t 		pc_nextstatevent;
78 	u_int32_t		pc_ticks_err_sum;
79 };
80 
81 struct amptimer_softc {
82 	struct device		sc_dev;
83 	bus_space_tag_t		sc_iot;
84 	bus_space_handle_t	sc_ioh;
85 	bus_space_handle_t	sc_pioh;
86 
87 	struct amptimer_pcpu_softc sc_pstat[MAX_ARM_CPUS];
88 
89 	u_int32_t		sc_ticks_err_cnt;
90 	u_int32_t		sc_ticks_per_second;
91 	u_int32_t		sc_ticks_per_intr;
92 	u_int32_t		sc_statvar;
93 	u_int32_t		sc_statmin;
94 
95 #ifdef AMPTIMER_DEBUG
96 	struct evcount		sc_clk_count;
97 	struct evcount		sc_stat_count;
98 #endif
99 };
100 
101 int		amptimer_match(struct device *, void *, void *);
102 void		amptimer_attach(struct device *, struct device *, void *);
103 uint64_t	amptimer_readcnt64(struct amptimer_softc *sc);
104 int		amptimer_intr(void *);
105 void		amptimer_cpu_initclocks(void);
106 void		amptimer_delay(u_int);
107 void		amptimer_setstatclockrate(int stathz);
108 void		amptimer_set_clockrate(int32_t new_frequency);
109 void		amptimer_startclock(void);
110 
111 /* hack - XXXX
112  * gptimer connects directly to ampintc, not thru the generic
113  * interface because it uses an 'internal' interrupt
114  * not a peripheral interrupt.
115  */
116 void	*ampintc_intr_establish(int, int, int, struct cpu_info *,
117 	    int (*)(void *), void *, char *);
118 
119 
120 
121 struct cfattach amptimer_ca = {
122 	sizeof (struct amptimer_softc), amptimer_match, amptimer_attach
123 };
124 
125 struct cfdriver amptimer_cd = {
126 	NULL, "amptimer", DV_DULL
127 };
128 
129 uint64_t
130 amptimer_readcnt64(struct amptimer_softc *sc)
131 {
132 	uint32_t high0, high1, low;
133 	bus_space_tag_t iot = sc->sc_iot;
134 	bus_space_handle_t ioh = sc->sc_ioh;
135 
136 	do {
137 		high0 = bus_space_read_4(iot, ioh, GTIMER_CNT_HIGH);
138 		low = bus_space_read_4(iot, ioh, GTIMER_CNT_LOW);
139 		high1 = bus_space_read_4(iot, ioh, GTIMER_CNT_HIGH);
140 	} while (high0 != high1);
141 
142 	return ((((uint64_t)high1) << 32) | low);
143 }
144 
145 int
146 amptimer_match(struct device *parent, void *cfdata, void *aux)
147 {
148 	if ((cpufunc_id() & CPU_ID_CORTEX_A9_MASK) == CPU_ID_CORTEX_A9)
149 		return (1);
150 
151 	return 0;
152 }
153 
154 void
155 amptimer_attach(struct device *parent, struct device *self, void *args)
156 {
157 	struct amptimer_softc *sc = (struct amptimer_softc *)self;
158 	struct cortex_attach_args *ia = args;
159 	bus_space_handle_t ioh, pioh;
160 
161 	sc->sc_iot = ia->ca_iot;
162 
163 	if (bus_space_map(sc->sc_iot, ia->ca_periphbase + GTIMER_ADDR,
164 	    GTIMER_SIZE, 0, &ioh))
165 		panic("amptimer_attach: bus_space_map global timer failed!");
166 
167 	if (bus_space_map(sc->sc_iot, ia->ca_periphbase + PTIMER_ADDR,
168 	    PTIMER_SIZE, 0, &pioh))
169 		panic("amptimer_attach: bus_space_map priv timer failed!");
170 
171 	sc->sc_ticks_per_second = amptimer_frequency;
172 	printf(": tick rate %d KHz\n", sc->sc_ticks_per_second /1000);
173 
174 	sc->sc_ioh = ioh;
175 	sc->sc_pioh = pioh;
176 
177 	/* disable global timer */
178 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CTRL, 0);
179 
180 	/* XXX ??? reset counters to 0 - gives us uptime in the counter */
181 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CNT_LOW, 0);
182 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CNT_HIGH, 0);
183 
184 	/* enable global timer */
185 	bus_space_write_4(sc->sc_iot, ioh, GTIMER_CTRL, GTIMER_CTRL_TIMER);
186 
187 #if defined(USE_GTIMER_CMP)
188 
189 	/* clear event */
190 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_STATUS, 1);
191 #else
192 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL, 0);
193 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_STATUS,
194 	    PTIMER_STATUS_EVENT);
195 #endif
196 
197 
198 #ifdef AMPTIMER_DEBUG
199 	evcount_attach(&sc->sc_clk_count, "clock", NULL);
200 	evcount_attach(&sc->sc_stat_count, "stat", NULL);
201 #endif
202 
203 	/*
204 	 * private timer and interrupts not enabled until
205 	 * timer configures
206 	 */
207 
208 	arm_clock_register(amptimer_cpu_initclocks, amptimer_delay,
209 	    amptimer_setstatclockrate, amptimer_startclock);
210 
211 	amptimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
212 	amptimer_timecounter.tc_priv = sc;
213 
214 	tc_init(&amptimer_timecounter);
215 }
216 
217 u_int
218 amptimer_get_timecount(struct timecounter *tc)
219 {
220 	struct amptimer_softc *sc = amptimer_timecounter.tc_priv;
221 	return bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CNT_LOW);
222 }
223 
224 int
225 amptimer_intr(void *frame)
226 {
227 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
228 	struct amptimer_pcpu_softc *pc = &sc->sc_pstat[CPU_INFO_UNIT(curcpu())];
229 	uint64_t		 now;
230 	uint64_t		 nextevent;
231 	uint32_t		 r, reg;
232 #if defined(USE_GTIMER_CMP)
233 	int			 skip = 1;
234 #else
235 	int64_t			 delay;
236 #endif
237 	int			 rc = 0;
238 
239 	/*
240 	 * DSR - I know that the tick timer is 64 bits, but the following
241 	 * code deals with rollover, so there is no point in dealing
242 	 * with the 64 bit math, just let the 32 bit rollover
243 	 * do the right thing
244 	 */
245 
246 	now = amptimer_readcnt64(sc);
247 
248 	while (pc->pc_nexttickevent <= now) {
249 		pc->pc_nexttickevent += sc->sc_ticks_per_intr;
250 		pc->pc_ticks_err_sum += sc->sc_ticks_err_cnt;
251 
252 		/* looping a few times is faster than divide */
253 		while (pc->pc_ticks_err_sum  > hz) {
254 			pc->pc_nexttickevent += 1;
255 			pc->pc_ticks_err_sum -= hz;
256 		}
257 
258 #ifdef AMPTIMER_DEBUG
259 		sc->sc_clk_count.ec_count++;
260 #endif
261 		rc = 1;
262 		hardclock(frame);
263 	}
264 	while (pc->pc_nextstatevent <= now) {
265 		do {
266 			r = random() & (sc->sc_statvar -1);
267 		} while (r == 0); /* random == 0 not allowed */
268 		pc->pc_nextstatevent += sc->sc_statmin + r;
269 
270 		/* XXX - correct nextstatevent? */
271 #ifdef AMPTIMER_DEBUG
272 		sc->sc_stat_count.ec_count++;
273 #endif
274 		rc = 1;
275 		statclock(frame);
276 	}
277 
278 	if (pc->pc_nexttickevent < pc->pc_nextstatevent)
279 		nextevent = pc->pc_nexttickevent;
280 	else
281 		nextevent = pc->pc_nextstatevent;
282 
283 #if defined(USE_GTIMER_CMP)
284 again:
285 	reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL);
286 	reg &= ~GTIMER_CTRL_COMP;
287 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL, reg);
288 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CMP_LOW,
289 	    nextevent & 0xffffffff);
290 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CMP_HIGH,
291 	    nextevent >> 32);
292 	reg |= GTIMER_CTRL_COMP;
293 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL, reg);
294 
295 	now = amptimer_readcnt64(sc);
296 	if (now >= nextevent) {
297 		nextevent = now + skip;
298 		skip += 1;
299 		goto again;
300 	}
301 #else
302 	/* clear old status */
303 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_STATUS,
304 	    PTIMER_STATUS_EVENT);
305 
306 	delay = nextevent - now;
307 	if (delay < 0)
308 		delay = 1;
309 
310 	reg = bus_space_read_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL);
311 	if ((reg & (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN)) !=
312 	    (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN))
313 		bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL,
314 		    (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN));
315 
316 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_LOAD, delay);
317 #endif
318 
319 	return (rc);
320 }
321 
322 void
323 amptimer_set_clockrate(int32_t new_frequency)
324 {
325 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
326 
327 	amptimer_frequency = new_frequency;
328 
329 	if (sc == NULL)
330 		return;
331 
332 	sc->sc_ticks_per_second = amptimer_frequency;
333 	amptimer_timecounter.tc_frequency = sc->sc_ticks_per_second;
334 	printf("amptimer0: adjusting clock: new tick rate %d KHz\n",
335 	    sc->sc_ticks_per_second /1000);
336 }
337 
338 void
339 amptimer_cpu_initclocks()
340 {
341 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
342 	struct amptimer_pcpu_softc *pc = &sc->sc_pstat[CPU_INFO_UNIT(curcpu())];
343 	uint64_t		 next;
344 #if defined(USE_GTIMER_CMP)
345 	uint32_t		 reg;
346 #endif
347 
348 	stathz = hz;
349 	profhz = hz * 10;
350 
351 	if (sc->sc_ticks_per_second != amptimer_frequency) {
352 		amptimer_set_clockrate(amptimer_frequency);
353 	}
354 
355 	amptimer_setstatclockrate(stathz);
356 
357 	sc->sc_ticks_per_intr = sc->sc_ticks_per_second / hz;
358 	sc->sc_ticks_err_cnt = sc->sc_ticks_per_second % hz;
359 	pc->pc_ticks_err_sum = 0;
360 
361 	/* establish interrupts */
362 	/* XXX - irq */
363 #if defined(USE_GTIMER_CMP)
364 	ampintc_intr_establish(27, IST_EDGE_RISING, IPL_CLOCK,
365 	    NULL, amptimer_intr, NULL, "tick");
366 #else
367 	ampintc_intr_establish(29, IST_EDGE_RISING, IPL_CLOCK,
368 	    NULL, amptimer_intr, NULL, "tick");
369 #endif
370 
371 	next = amptimer_readcnt64(sc) + sc->sc_ticks_per_intr;
372 	pc->pc_nexttickevent = pc->pc_nextstatevent = next;
373 
374 #if defined(USE_GTIMER_CMP)
375 	reg = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL);
376 	reg &= ~GTIMER_CTRL_COMP;
377 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL, reg);
378 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CMP_LOW,
379 	    next & 0xffffffff);
380 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CMP_HIGH,
381 	    next >> 32);
382 	reg |= GTIMER_CTRL_COMP | GTIMER_CTRL_IRQ;
383 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GTIMER_CTRL, reg);
384 #else
385 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_CTRL,
386 	    (PTIMER_CTRL_ENABLE | PTIMER_CTRL_IRQEN));
387 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_LOAD,
388 	    sc->sc_ticks_per_intr);
389 #endif
390 }
391 
392 void
393 amptimer_delay(u_int usecs)
394 {
395 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
396 	u_int32_t		clock, oclock, delta, delaycnt;
397 	volatile int		j;
398 	int			csec, usec;
399 
400 	if (usecs > (0x80000000 / (sc->sc_ticks_per_second))) {
401 		csec = usecs / 10000;
402 		usec = usecs % 10000;
403 
404 		delaycnt = (sc->sc_ticks_per_second / 100) * csec +
405 		    (sc->sc_ticks_per_second / 100) * usec / 10000;
406 	} else {
407 		delaycnt = sc->sc_ticks_per_second * usecs / 1000000;
408 	}
409 	if (delaycnt <= 1)
410 		for (j = 100; j > 0; j--)
411 			;
412 
413 	oclock = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GTIMER_CNT_LOW);
414 	while (1) {
415 		for (j = 100; j > 0; j--)
416 			;
417 		clock = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
418 		    GTIMER_CNT_LOW);
419 		delta = clock - oclock;
420 		if (delta > delaycnt)
421 			break;
422 	}
423 }
424 
425 void
426 amptimer_setstatclockrate(int newhz)
427 {
428 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
429 	int			 minint, statint;
430 	int			 s;
431 
432 	s = splclock();
433 
434 	statint = sc->sc_ticks_per_second / newhz;
435 	/* calculate largest 2^n which is smaller that just over half statint */
436 	sc->sc_statvar = 0x40000000; /* really big power of two */
437 	minint = statint / 2 + 100;
438 	while (sc->sc_statvar > minint)
439 		sc->sc_statvar >>= 1;
440 
441 	sc->sc_statmin = statint - (sc->sc_statvar >> 1);
442 
443 	splx(s);
444 
445 	/*
446 	 * XXX this allows the next stat timer to occur then it switches
447 	 * to the new frequency. Rather than switching instantly.
448 	 */
449 }
450 
451 void
452 amptimer_startclock(void)
453 {
454 	struct amptimer_softc	*sc = amptimer_cd.cd_devs[0];
455 	struct amptimer_pcpu_softc *pc = &sc->sc_pstat[CPU_INFO_UNIT(curcpu())];
456 	uint64_t nextevent;
457 
458 	nextevent = amptimer_readcnt64(sc) + sc->sc_ticks_per_intr;
459 	pc->pc_nexttickevent = pc->pc_nextstatevent = nextevent;
460 
461 	bus_space_write_4(sc->sc_iot, sc->sc_pioh, PTIMER_LOAD,
462 		sc->sc_ticks_per_intr);
463 }
464