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 <sys/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
110static const uint32_t counts_per_usec = (GEMINI_TIMER_CLOCK_FREQ / 1000000);
111static uint32_t counts_per_hz = ~0;
112
113struct geminitmr_softc *clock_sc;
114struct geminitmr_softc *stat_sc;
115struct geminitmr_softc *ref_sc;
116static uint32_t gemini_get_timecount(struct timecounter *);
117static void timer_init(geminitmr_softc_t *, int, boolean_t, boolean_t);
118static void timer_factors(geminitmr_softc_t *, int, boolean_t);
119
120#ifdef GEMINI_TIMER_DEBUG
121static void tfprint(uint, timer_factors_t *);
122#endif
123
124static 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
133static 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
143static 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
153static 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
166static 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
177static 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 */
191static 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
198static 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
223static uint32_t
224gemini_get_timecount(struct timecounter *tc)
225{
226	uint32_t r;
227
228	r = _timer_read(ref_sc);
229
230	return -r;
231}
232
233int
234clockintr(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
246int
247statintr(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
257static void
258timer_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
273void
274gemini_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
281void
282setstatclockrate(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 */
294void
295cpu_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
328void
329delay(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
363static void
364timer_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
407void
408tfprint(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