xref: /netbsd-src/sys/arch/arm/gemini/gemini_timer.c (revision f5d7ce3d2fc36b2965729a33eb02fb95c9fb56cd)
1 /*	$NetBSD: gemini_timer.c,v 1.1 2008/10/24 04:23:18 matt 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.1 2008/10/24 04:23:18 matt 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 	return 1;
242 }
243 
244 int
245 statintr(void *frame)
246 {
247 	struct geminitmr_softc *sc = stat_sc;;
248 
249 	_timer_intr_clr(sc);
250 	_timer_reload(sc, sc->sc_tf.tf_counter);
251 	statclock(frame);
252 	return 1;
253 }
254 
255 static void
256 timer_init(geminitmr_softc_t *sc, int schz, boolean_t autoload, boolean_t intr)
257 {
258 	int psw;
259 
260 	psw = disable_interrupts(I32_bit);
261 	timer_factors(sc, schz, autoload);
262 	_timer_stop(sc);
263 	_timer_intr_dis(sc);
264 	_timer_intr_clr(sc);
265 	if (intr)
266 		_timer_intr_enb(sc);
267 	_timer_start(sc);
268 	psw = disable_interrupts(I32_bit);
269 }
270 
271 void
272 gemini_microtime_init()
273 {
274 	if (ref_sc == NULL)
275 		panic("microtime reference timer was not configured.");
276 	timer_init(ref_sc, 0, TRUE, FALSE);
277 }
278 
279 void
280 setstatclockrate(int schz)
281 {
282 	if (stat_sc == NULL)
283 		panic("Statistics timer was not configured.");
284 	timer_init(stat_sc, schz, FALSE, TRUE);
285 }
286 
287 /*
288  * clock_sc and stat_sc starts here
289  * ref_sc is initialized already by obiotimer_attach
290  */
291 void
292 cpu_initclocks(void)
293 {
294 	if (clock_sc == NULL)
295 		panic("Clock timer was not configured.");
296 	if (stat_sc == NULL)
297 		panic("Statistics timer was not configured.");
298 	if (ref_sc == NULL)
299 		panic("Microtime reference timer was not configured.");
300 
301 	/*
302 	 * We already have the timers running, but not generating interrupts.
303 	 * In addition, we've set stathz and profhz.
304 	 */
305 	printf("clock: hz=%d stathz=%d\n", hz, stathz);
306 
307 	/*
308 	 * The "cookie" parameter must be zero to pass the interrupt frame
309 	 * through to hardclock() and statclock().
310 	 */
311 	intr_establish(clock_sc->sc_intr, IPL_CLOCK, IST_LEVEL_HIGH,
312 		clockintr, 0);
313 
314 	intr_establish(stat_sc->sc_intr, IPL_HIGH, IST_LEVEL_HIGH,
315 		statintr, 0);
316 
317 	timer_init(clock_sc,     hz, FALSE, TRUE);
318 	timer_init(stat_sc,  stathz, FALSE, TRUE);
319 
320 	tc_init(&gemini_timecounter);
321 }
322 
323 void
324 delay(u_int n)
325 {
326 	struct geminitmr_softc *sc = ref_sc;
327 	uint32_t cur, last, delta, usecs;
328 
329 	if (sc == NULL)
330 		panic("The timer must be initialized sooner.");
331 
332 	/*
333 	 * This works by polling the timer and counting the
334 	 * number of microseconds that go by.
335 	 */
336 	last = _timer_read(sc);
337 
338 	delta = usecs = 0;
339 
340 	while (n > usecs) {
341 		cur = _timer_read(sc);
342 
343 		/* Check to see if the timer has wrapped around. */
344 		if (last < cur)
345 			delta += (last + (counts_per_hz - cur));
346 		else
347 			delta += (last - cur);
348 
349 		last = cur;
350 
351 		if (delta >= counts_per_usec) {
352 			usecs += delta / counts_per_usec;
353 			delta %= counts_per_usec;
354 		}
355 	}
356 }
357 
358 static void
359 timer_factors(
360 	geminitmr_softc_t *sc,
361 	int ints_per_sec,
362 	boolean_t autoload)
363 {
364 	timer_factors_t *tfp = &sc->sc_tf;
365 	uint n = sc->sc_timerno;
366 	const uint32_t us_per_sec = 1000000;
367 
368 	/*
369 	 * UPDOWN=0		(Down)
370 	 * OFENABLE=0		(no Irpt on overflow)
371 	 * CLOCK=0		(PCLK)
372 	 * ENABLE=1
373 	 */
374 	tfp->tf_tmcr = TIMER_TMCR_TMnENABLE(n);
375 
376 	if (ints_per_sec == 0) {
377 		tfp->tf_counter = ~0U;
378 	} else {
379 		uint32_t count_freq;
380 
381 		count_freq = GEMINI_TIMER_CLOCK_FREQ;
382 		count_freq /= ints_per_sec;
383 		tfp->tf_counter = count_freq;
384 	}
385 	tfp->tf_counts_per_usec = GEMINI_TIMER_CLOCK_FREQ / us_per_sec;
386 
387 	if (autoload)
388 		tfp->tf_reload = tfp->tf_counter;	/* auto-reload */
389 	else
390 		tfp->tf_reload = 0;			/* no-auto_reload */
391 
392 	tfp->tf_match1 = 0;
393 	tfp->tf_match2 = 0;
394 
395 #ifdef GEMINI_TIMER_DEBUG
396 	tfprint(sc->sc_timerno, tfp);
397 	Debugger();
398 #endif
399 }
400 
401 #ifdef GEMINI_TIMER_DEBUG
402 void
403 tfprint(uint n, timer_factors_t *tfp)
404 {
405 	printf("%s: timer# %d\n", __FUNCTION__, n);
406 	printf("\ttf_counts_per_usec: %#x\n", tfp->tf_counts_per_usec);
407 	printf("\ttf_tmcr: %#x\n", tfp->tf_tmcr);
408 	printf("\ttf_counter: %#x\n", tfp->tf_counter);
409 	printf("\ttf_reload: %#x\n", tfp->tf_reload);
410 	printf("\ttf_match1: %#x\n", tfp->tf_match1);
411 	printf("\ttf_match2: %#x\n", tfp->tf_match2);
412 }
413 #endif
414