xref: /netbsd-src/sys/arch/arm/xscale/becc_timer.c (revision 482eef70502290f7cbd2cb9a24a4f41e6bacd98d)
1*482eef70Srin /*	$NetBSD: becc_timer.c,v 1.16 2020/05/29 12:30:39 rin Exp $	*/
2b1b164a8Sthorpej 
3b1b164a8Sthorpej /*
4b1b164a8Sthorpej  * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
5b1b164a8Sthorpej  * All rights reserved.
6b1b164a8Sthorpej  *
7b1b164a8Sthorpej  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8b1b164a8Sthorpej  *
9b1b164a8Sthorpej  * Redistribution and use in source and binary forms, with or without
10b1b164a8Sthorpej  * modification, are permitted provided that the following conditions
11b1b164a8Sthorpej  * are met:
12b1b164a8Sthorpej  * 1. Redistributions of source code must retain the above copyright
13b1b164a8Sthorpej  *    notice, this list of conditions and the following disclaimer.
14b1b164a8Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
15b1b164a8Sthorpej  *    notice, this list of conditions and the following disclaimer in the
16b1b164a8Sthorpej  *    documentation and/or other materials provided with the distribution.
17b1b164a8Sthorpej  * 3. All advertising materials mentioning features or use of this software
18b1b164a8Sthorpej  *    must display the following acknowledgement:
19b1b164a8Sthorpej  *	This product includes software developed for the NetBSD Project by
20b1b164a8Sthorpej  *	Wasabi Systems, Inc.
21b1b164a8Sthorpej  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22b1b164a8Sthorpej  *    or promote products derived from this software without specific prior
23b1b164a8Sthorpej  *    written permission.
24b1b164a8Sthorpej  *
25b1b164a8Sthorpej  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26b1b164a8Sthorpej  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27b1b164a8Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28b1b164a8Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29b1b164a8Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30b1b164a8Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31b1b164a8Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32b1b164a8Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33b1b164a8Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34b1b164a8Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35b1b164a8Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
36b1b164a8Sthorpej  */
37b1b164a8Sthorpej 
38b1b164a8Sthorpej /*
39b1b164a8Sthorpej  * Timer/clock support for the ADI Engineering Big Endian Companion Chip.
40b1b164a8Sthorpej  */
41b1b164a8Sthorpej 
4208716eaeSlukem #include <sys/cdefs.h>
43*482eef70Srin __KERNEL_RCSID(0, "$NetBSD: becc_timer.c,v 1.16 2020/05/29 12:30:39 rin Exp $");
4408716eaeSlukem 
45b1b164a8Sthorpej #include <sys/param.h>
46b1b164a8Sthorpej #include <sys/systm.h>
47b1b164a8Sthorpej #include <sys/kernel.h>
484e56cdd2Sjoerg #include <sys/atomic.h>
49b1b164a8Sthorpej #include <sys/time.h>
504e56cdd2Sjoerg #include <sys/timetc.h>
51b1b164a8Sthorpej 
52ca601a77Sthorpej #include <dev/clock_subr.h>
53ca601a77Sthorpej 
54ed9977b1Sdyoung #include <sys/bus.h>
55b1b164a8Sthorpej #include <arm/cpufunc.h>
56b1b164a8Sthorpej 
57b1b164a8Sthorpej #include <arm/xscale/beccreg.h>
58b1b164a8Sthorpej #include <arm/xscale/beccvar.h>
59b1b164a8Sthorpej 
60b1b164a8Sthorpej void	(*becc_hardclock_hook)(void);
61b1b164a8Sthorpej 
62b1b164a8Sthorpej /*
63b1b164a8Sthorpej  * Note, since COUNTS_PER_USEC doesn't divide evenly, we round up.
64b1b164a8Sthorpej  */
65b1b164a8Sthorpej #define	COUNTS_PER_SEC		BECC_PERIPH_CLOCK
66b1b164a8Sthorpej #define	COUNTS_PER_USEC		((COUNTS_PER_SEC / 1000000) + 1)
67b1b164a8Sthorpej 
68b1b164a8Sthorpej static void *clock_ih;
69b1b164a8Sthorpej 
704e56cdd2Sjoerg static u_int	becc_get_timecount(struct timecounter *);
714e56cdd2Sjoerg 
724e56cdd2Sjoerg static struct timecounter becc_timecounter = {
73*482eef70Srin 	.tc_get_timecount = becc_get_timecount,
74*482eef70Srin 	.tc_counter_mask = 0xffffffff,
75*482eef70Srin 	.tc_frequency = COUNTS_PER_SEC,
76*482eef70Srin 	.tc_name = "becc",
77*482eef70Srin 	.tc_quality = 100,
784e56cdd2Sjoerg };
794e56cdd2Sjoerg 
804e56cdd2Sjoerg static volatile uint32_t becc_base;
814e56cdd2Sjoerg 
82b1b164a8Sthorpej /*
83b1b164a8Sthorpej  * Since the timer interrupts when the counter underflows, we need to
84b1b164a8Sthorpej  * subtract 1 from counts_per_hz when loading the preload register.
85b1b164a8Sthorpej  */
86b1b164a8Sthorpej static uint32_t counts_per_hz;
87b1b164a8Sthorpej 
88b1b164a8Sthorpej int	clockhandler(void *);
89b1b164a8Sthorpej 
90b1b164a8Sthorpej /*
91b1b164a8Sthorpej  * becc_calibrate_delay:
92b1b164a8Sthorpej  *
93b1b164a8Sthorpej  *	Calibrate the delay loop.
94b1b164a8Sthorpej  */
95b1b164a8Sthorpej void
becc_calibrate_delay(void)96b1b164a8Sthorpej becc_calibrate_delay(void)
97b1b164a8Sthorpej {
98b1b164a8Sthorpej 
99b1b164a8Sthorpej 	/*
100b1b164a8Sthorpej 	 * Just use hz=100 for now -- we'll adjust it, if necessary,
101b1b164a8Sthorpej 	 * in cpu_initclocks().
102b1b164a8Sthorpej 	 */
103b1b164a8Sthorpej 	counts_per_hz = COUNTS_PER_SEC / 100;
104b1b164a8Sthorpej 
105b1b164a8Sthorpej 	/* Stop both timers, clear interrupts. */
106b1b164a8Sthorpej 	BECC_CSR_WRITE(BECC_TSCRA, TSCRx_TIF);
107b1b164a8Sthorpej 	BECC_CSR_WRITE(BECC_TSCRB, TSCRx_TIF);
108b1b164a8Sthorpej 
109b1b164a8Sthorpej 	/* Set the timer preload value. */
110b1b164a8Sthorpej 	BECC_CSR_WRITE(BECC_TPRA, counts_per_hz - 1);
111b1b164a8Sthorpej 
112b1b164a8Sthorpej 	/* Start the timer. */
113b1b164a8Sthorpej 	BECC_CSR_WRITE(BECC_TSCRA, TSCRx_TE | TSCRx_CM);
114b1b164a8Sthorpej }
115b1b164a8Sthorpej 
116b1b164a8Sthorpej /*
117b1b164a8Sthorpej  * cpu_initclocks:
118b1b164a8Sthorpej  *
119b1b164a8Sthorpej  *	Initialize the clock and get them going.
120b1b164a8Sthorpej  */
121b1b164a8Sthorpej void
cpu_initclocks(void)122b1b164a8Sthorpej cpu_initclocks(void)
123b1b164a8Sthorpej {
124b1b164a8Sthorpej 	u_int oldirqstate;
125b1b164a8Sthorpej 
126b1b164a8Sthorpej #if 0
127b1b164a8Sthorpej 	if (hz < 50 || COUNTS_PER_SEC % hz) {
128b1b164a8Sthorpej 		printf("Cannot get %d Hz clock; using 100 Hz\n", hz);
129b1b164a8Sthorpej 		hz = 100;
130b1b164a8Sthorpej 	}
131b1b164a8Sthorpej #endif
132b1b164a8Sthorpej 
133b1b164a8Sthorpej 	/*
134b1b164a8Sthorpej 	 * We only have one timer available; stathz and profhz are
135b1b164a8Sthorpej 	 * always left as 0 (the upper-layer clock code deals with
136b1b164a8Sthorpej 	 * this situation).
137b1b164a8Sthorpej 	 */
138b1b164a8Sthorpej 	if (stathz != 0)
139b1b164a8Sthorpej 		printf("Cannot get %d Hz statclock\n", stathz);
140b1b164a8Sthorpej 	stathz = 0;
141b1b164a8Sthorpej 
142b1b164a8Sthorpej 	if (profhz != 0)
143b1b164a8Sthorpej 		printf("Cannot get %d Hz profclock\n", profhz);
144b1b164a8Sthorpej 	profhz = 0;
145b1b164a8Sthorpej 
146b1b164a8Sthorpej 	/* Report the clock frequency. */
147faaab854Sbriggs 	aprint_normal("clock: hz=%d stathz=%d profhz=%d\n", hz, stathz, profhz);
148b1b164a8Sthorpej 
149b1b164a8Sthorpej 	oldirqstate = disable_interrupts(I32_bit);
150b1b164a8Sthorpej 
151b1b164a8Sthorpej 	/* Hook up the clock interrupt handler. */
152b1b164a8Sthorpej 	clock_ih = becc_intr_establish(ICU_TIMERA, IPL_CLOCK,
153b1b164a8Sthorpej 	    clockhandler, NULL);
154b1b164a8Sthorpej 	if (clock_ih == NULL)
155b1b164a8Sthorpej 		panic("cpu_initclocks: unable to register timer interrupt");
156b1b164a8Sthorpej 
157b1b164a8Sthorpej 	/* Set up the new clock parameters. */
158b1b164a8Sthorpej 
159b1b164a8Sthorpej 	/* Stop timer, clear interrupt */
160b1b164a8Sthorpej 	BECC_CSR_WRITE(BECC_TSCRA, TSCRx_TIF);
161b1b164a8Sthorpej 
162b1b164a8Sthorpej 	counts_per_hz = COUNTS_PER_SEC / hz;
163b1b164a8Sthorpej 
164b1b164a8Sthorpej 	/* Set the timer preload value. */
165b1b164a8Sthorpej 	BECC_CSR_WRITE(BECC_TPRA, counts_per_hz - 1);
166b1b164a8Sthorpej 
167b1b164a8Sthorpej 	/* ...and start it in motion. */
168b1b164a8Sthorpej 	BECC_CSR_WRITE(BECC_TSCRA, TSCRx_TE | TSCRx_CM);
169b1b164a8Sthorpej 
1700c0de807Smatt #ifdef __HAVE_FAST_SOFTINTS
171b1b164a8Sthorpej 	/* register soft interrupt handler as well */
17265d64ddeSad 	becc_intr_establish(ICU_SOFT, IPL_SOFTCLOCK, becc_softint, NULL);
1730c0de807Smatt #endif
174b1b164a8Sthorpej 
175b1b164a8Sthorpej 	restore_interrupts(oldirqstate);
1764e56cdd2Sjoerg 
1774e56cdd2Sjoerg 	tc_init(&becc_timecounter);
178b1b164a8Sthorpej }
179b1b164a8Sthorpej 
180b1b164a8Sthorpej /*
181b1b164a8Sthorpej  * setstatclockrate:
182b1b164a8Sthorpej  *
183b1b164a8Sthorpej  *	Set the rate of the statistics clock.
184b1b164a8Sthorpej  *
185b1b164a8Sthorpej  *	We assume that hz is either stathz or profhz, and that neither
186b1b164a8Sthorpej  *	will change after being set by cpu_initclocks().  We could
187b1b164a8Sthorpej  *	recalculate the intervals here, but that would be a pain.
188b1b164a8Sthorpej  */
189b1b164a8Sthorpej void
setstatclockrate(int new_hz)190b7c8c07eSrearnsha setstatclockrate(int new_hz)
191b1b164a8Sthorpej {
192b1b164a8Sthorpej 
193b1b164a8Sthorpej 	/*
194b1b164a8Sthorpej 	 * XXX Use TMR1?
195b1b164a8Sthorpej 	 */
196b1b164a8Sthorpej }
197b1b164a8Sthorpej 
1984e56cdd2Sjoerg static u_int
becc_get_timecount(struct timecounter * tc)1994e56cdd2Sjoerg becc_get_timecount(struct timecounter *tc)
200b1b164a8Sthorpej {
2014e56cdd2Sjoerg 	uint32_t counter, base;
202b1b164a8Sthorpej 	u_int oldirqstate;
203b1b164a8Sthorpej 
204b1b164a8Sthorpej 	oldirqstate = disable_interrupts(I32_bit);
2054e56cdd2Sjoerg 	counter = BECC_CSR_READ(BECC_TCVRA);
2064e56cdd2Sjoerg 	base = becc_base;
207b1b164a8Sthorpej 	restore_interrupts(oldirqstate);
2084e56cdd2Sjoerg 
2094e56cdd2Sjoerg 	return base - counter;
210b1b164a8Sthorpej }
211b1b164a8Sthorpej 
212b1b164a8Sthorpej /*
213b1b164a8Sthorpej  * delay:
214b1b164a8Sthorpej  *
215b1b164a8Sthorpej  *	Delay for at least N microseconds.
216b1b164a8Sthorpej  */
217b1b164a8Sthorpej void
delay(u_int n)218b1b164a8Sthorpej delay(u_int n)
219b1b164a8Sthorpej {
220b1b164a8Sthorpej 	uint32_t cur, last, delta, usecs;
221b1b164a8Sthorpej 
222b1b164a8Sthorpej 	/*
223b1b164a8Sthorpej 	 * This works by polling the timer and counting the
224b1b164a8Sthorpej 	 * number of microseconds that go by.
225b1b164a8Sthorpej 	 */
226b1b164a8Sthorpej 	last = BECC_CSR_READ(BECC_TCVRA);
227b1b164a8Sthorpej 	delta = usecs = 0;
228b1b164a8Sthorpej 
229b1b164a8Sthorpej 	while (n > usecs) {
230b1b164a8Sthorpej 		cur = BECC_CSR_READ(BECC_TCVRA);
231b1b164a8Sthorpej 
232b1b164a8Sthorpej 		/* Check to see if the timer has wrapped around. */
233b1b164a8Sthorpej 		if (last < cur)
234b1b164a8Sthorpej 			delta += (last + (counts_per_hz - cur));
235b1b164a8Sthorpej 		else
236b1b164a8Sthorpej 			delta += (last - cur);
237b1b164a8Sthorpej 
238b1b164a8Sthorpej 		last = cur;
239b1b164a8Sthorpej 
240b1b164a8Sthorpej 		if (delta >= COUNTS_PER_USEC) {
241b1b164a8Sthorpej 			usecs += delta / COUNTS_PER_USEC;
242b1b164a8Sthorpej 			delta %= COUNTS_PER_USEC;
243b1b164a8Sthorpej 		}
244b1b164a8Sthorpej 	}
245b1b164a8Sthorpej }
246b1b164a8Sthorpej 
247b1b164a8Sthorpej /*
248b1b164a8Sthorpej  * clockhandler:
249b1b164a8Sthorpej  *
250b1b164a8Sthorpej  *	Handle the hardclock interrupt.
251b1b164a8Sthorpej  */
252b1b164a8Sthorpej int
clockhandler(void * arg)253b1b164a8Sthorpej clockhandler(void *arg)
254b1b164a8Sthorpej {
255b1b164a8Sthorpej 	struct clockframe *frame = arg;
256b1b164a8Sthorpej 
257b1b164a8Sthorpej 	/* ACK the interrupt. */
258b1b164a8Sthorpej 	BECC_CSR_WRITE(BECC_TSCRA, TSCRx_TE | TSCRx_CM | TSCRx_TIF);
259b1b164a8Sthorpej 
260b1b164a8Sthorpej 	hardclock(frame);
261b1b164a8Sthorpej 
2624e56cdd2Sjoerg 	atomic_add_32(&becc_base, counts_per_hz);
2634e56cdd2Sjoerg 
264b1b164a8Sthorpej 	if (becc_hardclock_hook != NULL)
265b1b164a8Sthorpej 		(*becc_hardclock_hook)();
266b1b164a8Sthorpej 
267b1b164a8Sthorpej 	return (1);
268b1b164a8Sthorpej }
269