xref: /netbsd/sys/arch/arm/gemini/gemini_timer.c (revision 6550d01e)
1 /*	$NetBSD: gemini_timer.c,v 1.4 2010/01/05 13:14:56 mbalmer Exp $	*/
2 
3 /* adapted from:
4  *	NetBSD: omap2_geminitmr.c,v 1.1 2008/08/27 11:03:10 matt Exp
5  */
6 
7 /*
8  * GEMINI Timers
9  */
10 
11 /*
12  * Based on i80321_timer.c and arch/arm/sa11x0/sa11x0_ost.c
13  *
14  * Copyright (c) 1997 Mark Brinicombe.
15  * Copyright (c) 1997 Causality Limited.
16  * All rights reserved.
17  *
18  * This code is derived from software contributed to The NetBSD Foundation
19  * by IWAMOTO Toshihiro and Ichiro FUKUHARA.
20  *
21  * Redistribution and use in source and binary forms, with or without
22  * modification, are permitted provided that the following conditions
23  * are met:
24  * 1. Redistributions of source code must retain the above copyright
25  *    notice, this list of conditions and the following disclaimer.
26  * 2. Redistributions in binary form must reproduce the above copyright
27  *    notice, this list of conditions and the following disclaimer in the
28  *    documentation and/or other materials provided with the distribution.
29  * 3. All advertising materials mentioning features or use of this software
30  *    must display the following acknowledgement:
31  *	This product includes software developed by the NetBSD
32  *	Foundation, Inc. and its contributors.
33  * 4. Neither the name of The NetBSD Foundation nor the names of its
34  *    contributors may be used to endorse or promote products derived
35  *    from this software without specific prior written permission.
36  *
37  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
38  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
39  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
40  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
41  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
42  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
43  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
44  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
45  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
46  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
47  * POSSIBILITY OF SUCH DAMAGE.
48  *
49  * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
50  * All rights reserved.
51  *
52  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
53  *
54  * Redistribution and use in source and binary forms, with or without
55  * modification, are permitted provided that the following conditions
56  * are met:
57  * 1. Redistributions of source code must retain the above copyright
58  *    notice, this list of conditions and the following disclaimer.
59  * 2. Redistributions in binary form must reproduce the above copyright
60  *    notice, this list of conditions and the following disclaimer in the
61  *    documentation and/or other materials provided with the distribution.
62  * 3. All advertising materials mentioning features or use of this software
63  *    must display the following acknowledgement:
64  *	This product includes software developed for the NetBSD Project by
65  *	Wasabi Systems, Inc.
66  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
67  *    or promote products derived from this software without specific prior
68  *    written permission.
69  *
70  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
71  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
72  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
73  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
74  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
75  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
76  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
77  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
78  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
79  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
80  * POSSIBILITY OF SUCH DAMAGE.
81  */
82 
83 #include <sys/cdefs.h>
84 __KERNEL_RCSID(0, "$NetBSD: gemini_timer.c,v 1.4 2010/01/05 13:14:56 mbalmer Exp $");
85 
86 #include "opt_gemini.h"
87 #include "opt_cpuoptions.h"
88 
89 #include <sys/types.h>
90 #include <sys/param.h>
91 #include <sys/systm.h>
92 #include <sys/kernel.h>
93 #include <sys/time.h>
94 #include <sys/timetc.h>
95 #include <sys/device.h>
96 
97 #include <dev/clock_subr.h>
98 
99 #include <machine/bus.h>
100 #include <machine/intr.h>
101 
102 #include <arm/cpufunc.h>
103 #include <arm/pic/picvar.h>
104 
105 #include <arm/gemini/gemini_reg.h>
106 #include <arm/gemini/gemini_timervar.h>
107 #include <arm/gemini/gemini_timervar.h>
108 
109 
110 static const uint32_t counts_per_usec = (GEMINI_TIMER_CLOCK_FREQ / 1000000);
111 static uint32_t counts_per_hz = ~0;
112 
113 struct geminitmr_softc *clock_sc;
114 struct geminitmr_softc *stat_sc;
115 struct geminitmr_softc *ref_sc;
116 static uint32_t gemini_get_timecount(struct timecounter *);
117 static void timer_init(geminitmr_softc_t *, int, boolean_t, boolean_t);
118 static void timer_factors(geminitmr_softc_t *, int, boolean_t);
119 
120 #ifdef GEMINI_TIMER_DEBUG
121 static void tfprint(uint, timer_factors_t *);
122 #endif
123 
124 static struct timecounter gemini_timecounter = {
125 	.tc_get_timecount = gemini_get_timecount,
126 	.tc_counter_mask = 0xffffffff,
127 	.tc_frequency = GEMINI_TIMER_CLOCK_FREQ,
128 	.tc_name = "gpt",
129 	.tc_quality = 100,
130 	.tc_priv = NULL
131 };
132 
133 static inline void
134 _timer_intr_dis(struct geminitmr_softc *sc)
135 {
136 	uint32_t r;
137 
138 	r  = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GEMINI_TIMER_INTRMASK);
139 	r |= GEMINI_TIMERn_INTRMASK(sc->sc_timerno);
140 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GEMINI_TIMER_INTRMASK, r);
141 }
142 
143 static inline void
144 _timer_intr_enb(struct geminitmr_softc *sc)
145 {
146 	uint32_t r;
147 
148 	r  = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GEMINI_TIMER_INTRMASK);
149 	r &= ~TIMER_INTRMASK_TMnMATCH1(sc->sc_timerno);
150 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GEMINI_TIMER_INTRMASK, r);
151 }
152 
153 static inline void
154 _timer_intr_clr(struct geminitmr_softc *sc)
155 {
156 	uint32_t r;
157 	int psw;
158 
159 	psw = disable_interrupts(I32_bit);
160 	r  = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GEMINI_TIMER_INTRSTATE);
161 	r &= ~GEMINI_TIMERn_INTRMASK(sc->sc_timerno);
162 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GEMINI_TIMER_INTRSTATE, r);
163 	restore_interrupts(psw);
164 }
165 
166 static inline uint32_t
167 _timer_read(struct geminitmr_softc *sc)
168 {
169 	uint32_t r;
170 
171 	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
172 		GEMINI_TIMERn_COUNTER(sc->sc_timerno));
173 
174 	return r;
175 }
176 
177 static inline void
178 _timer_stop(struct geminitmr_softc *sc)
179 {
180 	uint32_t r;
181 
182 	r  = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GEMINI_TIMER_TMCR);
183 	r &= ~GEMINI_TIMER_TMnCR_MASK(sc->sc_timerno);
184 }
185 
186 /*
187  * note:
188  *  This function assumes the timer is enabled.
189  *  If the timer is disabled, GEMINI_TIMERn_COUNTER(n) will hold the value.
190  */
191 static inline void
192 _timer_reload(struct geminitmr_softc *sc, uint32_t val)
193 {
194 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
195 		GEMINI_TIMERn_COUNTER(sc->sc_timerno), val);
196 }
197 
198 static inline void
199 _timer_start(struct geminitmr_softc *sc)
200 {
201 	uint32_t r;
202 	uint n = sc->sc_timerno;
203 	timer_factors_t *tfp = &sc->sc_tf;
204 
205 	/* set Counter, TmLoad, Match1, Match2 */
206 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
207 		GEMINI_TIMERn_COUNTER(n), tfp->tf_counter);
208 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
209 		GEMINI_TIMERn_LOAD(n), tfp->tf_reload);
210 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
211 		GEMINI_TIMERn_MATCH1(n), tfp->tf_match1);
212 	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
213 		GEMINI_TIMERn_MATCH2(n), tfp->tf_match2);
214 
215 	/* set TmCR */
216 	r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GEMINI_TIMER_TMCR);
217 	r &= ~GEMINI_TIMER_TMnCR_MASK(n);
218 	r |= tfp->tf_tmcr & GEMINI_TIMER_TMnCR_MASK(n);
219 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, GEMINI_TIMER_TMCR, r);
220 
221 }
222 
223 static uint32_t
224 gemini_get_timecount(struct timecounter *tc)
225 {
226 	uint32_t r;
227 
228 	r = _timer_read(ref_sc);
229 
230 	return -r;
231 }
232 
233 int
234 clockintr(void *frame)
235 {
236 	struct geminitmr_softc *sc = clock_sc;
237 
238 	_timer_intr_clr(sc);
239 	_timer_reload(sc, sc->sc_tf.tf_counter);
240 	hardclock(frame);
241 	if (clock_sc == stat_sc)
242 		statclock(frame);
243 	return 1;
244 }
245 
246 int
247 statintr(void *frame)
248 {
249 	struct geminitmr_softc *sc = stat_sc;
250 
251 	_timer_intr_clr(sc);
252 	_timer_reload(sc, sc->sc_tf.tf_counter);
253 	statclock(frame);
254 	return 1;
255 }
256 
257 static void
258 timer_init(geminitmr_softc_t *sc, int schz, boolean_t autoload, boolean_t intr)
259 {
260 	int psw;
261 
262 	psw = disable_interrupts(I32_bit);
263 	timer_factors(sc, schz, autoload);
264 	_timer_stop(sc);
265 	_timer_intr_dis(sc);
266 	_timer_intr_clr(sc);
267 	if (intr)
268 		_timer_intr_enb(sc);
269 	_timer_start(sc);
270 	psw = disable_interrupts(I32_bit);
271 }
272 
273 void
274 gemini_microtime_init(void)
275 {
276 	if (ref_sc == NULL)
277 		panic("microtime reference timer was not configured.");
278 	timer_init(ref_sc, 0, TRUE, FALSE);
279 }
280 
281 void
282 setstatclockrate(int schz)
283 {
284 	if (stat_sc == NULL)
285 		panic("Statistics timer was not configured.");
286 	if (stat_sc != clock_sc)
287 		timer_init(stat_sc, schz, FALSE, TRUE);
288 }
289 
290 /*
291  * clock_sc and stat_sc starts here
292  * ref_sc is initialized already by obiotimer_attach
293  */
294 void
295 cpu_initclocks(void)
296 {
297 	if (clock_sc == NULL)
298 		panic("Clock timer was not configured.");
299 	if (stat_sc == NULL)
300 		panic("Statistics timer was not configured.");
301 	if (ref_sc == NULL)
302 		panic("Microtime reference timer was not configured.");
303 
304 	/*
305 	 * We already have the timers running, but not generating interrupts.
306 	 * In addition, we've set stathz and profhz.
307 	 */
308 	printf("clock: hz=%d stathz=%d\n", hz, stathz);
309 
310 	/*
311 	 * The "cookie" parameter must be zero to pass the interrupt frame
312 	 * through to hardclock() and statclock().
313 	 */
314 	intr_establish(clock_sc->sc_intr, IPL_CLOCK, IST_LEVEL_HIGH,
315 		clockintr, 0);
316 
317 	if (clock_sc != stat_sc)
318 		intr_establish(stat_sc->sc_intr, IPL_HIGH, IST_LEVEL_HIGH,
319 			statintr, 0);
320 
321 	timer_init(clock_sc, hz, FALSE, TRUE);
322 	if (clock_sc != stat_sc)
323 		timer_init(stat_sc, stathz, FALSE, TRUE);
324 
325 	tc_init(&gemini_timecounter);
326 }
327 
328 void
329 delay(u_int n)
330 {
331 	struct geminitmr_softc *sc = ref_sc;
332 	uint32_t cur, last, delta, usecs;
333 
334 	if (sc == NULL)
335 		panic("The timer must be initialized sooner.");
336 
337 	/*
338 	 * This works by polling the timer and counting the
339 	 * number of microseconds that go by.
340 	 */
341 	last = _timer_read(sc);
342 
343 	delta = usecs = 0;
344 
345 	while (n > usecs) {
346 		cur = _timer_read(sc);
347 
348 		/* Check to see if the timer has wrapped around. */
349 		if (last < cur)
350 			delta += (last + (counts_per_hz - cur));
351 		else
352 			delta += (last - cur);
353 
354 		last = cur;
355 
356 		if (delta >= counts_per_usec) {
357 			usecs += delta / counts_per_usec;
358 			delta %= counts_per_usec;
359 		}
360 	}
361 }
362 
363 static void
364 timer_factors(
365 	geminitmr_softc_t *sc,
366 	int ints_per_sec,
367 	boolean_t autoload)
368 {
369 	timer_factors_t *tfp = &sc->sc_tf;
370 	uint n = sc->sc_timerno;
371 	const uint32_t us_per_sec = 1000000;
372 
373 	/*
374 	 * UPDOWN=0		(Down)
375 	 * OFENABLE=0		(no Irpt on overflow)
376 	 * CLOCK=0		(PCLK)
377 	 * ENABLE=1
378 	 */
379 	tfp->tf_tmcr = TIMER_TMCR_TMnENABLE(n);
380 
381 	if (ints_per_sec == 0) {
382 		tfp->tf_counter = ~0U;
383 	} else {
384 		uint32_t count_freq;
385 
386 		count_freq = GEMINI_TIMER_CLOCK_FREQ;
387 		count_freq /= ints_per_sec;
388 		tfp->tf_counter = count_freq;
389 	}
390 	tfp->tf_counts_per_usec = GEMINI_TIMER_CLOCK_FREQ / us_per_sec;
391 
392 	if (autoload)
393 		tfp->tf_reload = tfp->tf_counter;	/* auto-reload */
394 	else
395 		tfp->tf_reload = 0;			/* no-auto_reload */
396 
397 	tfp->tf_match1 = 0;
398 	tfp->tf_match2 = 0;
399 
400 #ifdef GEMINI_TIMER_DEBUG
401 	tfprint(sc->sc_timerno, tfp);
402 	Debugger();
403 #endif
404 }
405 
406 #ifdef GEMINI_TIMER_DEBUG
407 void
408 tfprint(uint n, timer_factors_t *tfp)
409 {
410 	printf("%s: timer# %d\n", __FUNCTION__, n);
411 	printf("\ttf_counts_per_usec: %#x\n", tfp->tf_counts_per_usec);
412 	printf("\ttf_tmcr: %#x\n", tfp->tf_tmcr);
413 	printf("\ttf_counter: %#x\n", tfp->tf_counter);
414 	printf("\ttf_reload: %#x\n", tfp->tf_reload);
415 	printf("\ttf_match1: %#x\n", tfp->tf_match1);
416 	printf("\ttf_match2: %#x\n", tfp->tf_match2);
417 }
418 #endif
419