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