xref: /netbsd-src/sys/arch/evbarm/iq80310/iq80310_timer.c (revision 5dfdc49f47499b953e8cca5be1f6fdc679b3e681)
1*5dfdc49fSandvar /*	$NetBSD: iq80310_timer.c,v 1.24 2024/07/20 20:36:33 andvar Exp $	*/
2acf9a688Sthorpej 
3acf9a688Sthorpej /*
4727b4699Sthorpej  * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
5acf9a688Sthorpej  * All rights reserved.
6acf9a688Sthorpej  *
7acf9a688Sthorpej  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8acf9a688Sthorpej  *
9acf9a688Sthorpej  * Redistribution and use in source and binary forms, with or without
10acf9a688Sthorpej  * modification, are permitted provided that the following conditions
11acf9a688Sthorpej  * are met:
12acf9a688Sthorpej  * 1. Redistributions of source code must retain the above copyright
13acf9a688Sthorpej  *    notice, this list of conditions and the following disclaimer.
14acf9a688Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
15acf9a688Sthorpej  *    notice, this list of conditions and the following disclaimer in the
16acf9a688Sthorpej  *    documentation and/or other materials provided with the distribution.
17acf9a688Sthorpej  * 3. All advertising materials mentioning features or use of this software
18acf9a688Sthorpej  *    must display the following acknowledgement:
19acf9a688Sthorpej  *	This product includes software developed for the NetBSD Project by
20acf9a688Sthorpej  *	Wasabi Systems, Inc.
21acf9a688Sthorpej  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22acf9a688Sthorpej  *    or promote products derived from this software without specific prior
23acf9a688Sthorpej  *    written permission.
24acf9a688Sthorpej  *
25acf9a688Sthorpej  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26acf9a688Sthorpej  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27acf9a688Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28acf9a688Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29acf9a688Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30acf9a688Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31acf9a688Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32acf9a688Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33acf9a688Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34acf9a688Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35acf9a688Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
36acf9a688Sthorpej  */
37acf9a688Sthorpej 
38acf9a688Sthorpej /*
39acf9a688Sthorpej  * Timer/clock support for the Intel IQ80310.
40acf9a688Sthorpej  *
41acf9a688Sthorpej  * The IQ80310 has a 22-bit reloadable timer implemented in the CPLD.
42acf9a688Sthorpej  * We use it to provide a hardclock interrupt.  There is no RTC on
43acf9a688Sthorpej  * the IQ80310.
44acf9a688Sthorpej  *
45acf9a688Sthorpej  * The timer uses the SPCI clock.  The timer uses the 33MHz clock by
46acf9a688Sthorpej  * reading the SPCI_66EN signal and dividing the clock if necessary.
47acf9a688Sthorpej  */
48acf9a688Sthorpej 
4908716eaeSlukem #include <sys/cdefs.h>
50*5dfdc49fSandvar __KERNEL_RCSID(0, "$NetBSD: iq80310_timer.c,v 1.24 2024/07/20 20:36:33 andvar Exp $");
5108716eaeSlukem 
52acf9a688Sthorpej #include <sys/param.h>
53acf9a688Sthorpej #include <sys/systm.h>
54acf9a688Sthorpej #include <sys/kernel.h>
554e56cdd2Sjoerg #include <sys/atomic.h>
56acf9a688Sthorpej #include <sys/time.h>
574e56cdd2Sjoerg #include <sys/timetc.h>
58acf9a688Sthorpej 
59ca601a77Sthorpej #include <dev/clock_subr.h>
60ca601a77Sthorpej 
61fea15f47Sdyoung #include <sys/bus.h>
6296959902Sthorpej #include <arm/cpufunc.h>
63acf9a688Sthorpej 
64acf9a688Sthorpej #include <evbarm/iq80310/iq80310reg.h>
65acf9a688Sthorpej #include <evbarm/iq80310/iq80310var.h>
66acf9a688Sthorpej #include <evbarm/iq80310/obiovar.h>
67acf9a688Sthorpej 
68727b4699Sthorpej /*
69727b4699Sthorpej  * Some IQ80310-based designs have fewer bits in the timer counter.
70727b4699Sthorpej  * Deal with them here.
71727b4699Sthorpej  */
72727b4699Sthorpej #if defined(IOP310_TEAMASA_NPWR)
73a9e4c1a9Sthorpej #define	COUNTER_MASK		0x0007ffff
74727b4699Sthorpej #else /* Default to stock IQ80310 */
75a9e4c1a9Sthorpej #define	COUNTER_MASK		0x003fffff
76727b4699Sthorpej #endif /* list of IQ80310-based designs */
77727b4699Sthorpej 
78acf9a688Sthorpej #define	COUNTS_PER_SEC		33000000	/* 33MHz */
79acf9a688Sthorpej #define	COUNTS_PER_USEC		(COUNTS_PER_SEC / 1000000)
80acf9a688Sthorpej 
81acf9a688Sthorpej static void *clock_ih;
82acf9a688Sthorpej 
83acf9a688Sthorpej static uint32_t counts_per_hz;
84acf9a688Sthorpej 
854e56cdd2Sjoerg static u_int	iq80310_get_timecount(struct timecounter *);
864e56cdd2Sjoerg 
874e56cdd2Sjoerg static struct timecounter iq80310_timecounter = {
88482eef70Srin 	.tc_get_timecount = iq80310_get_timecount,
89482eef70Srin 	.tc_counter_mask = 0xffffffff,
90482eef70Srin 	.tc_frequency = COUNTS_PER_SEC,
91482eef70Srin 	.tc_name = "iq80310",
92482eef70Srin 	.tc_quality = 100,
934e56cdd2Sjoerg };
944e56cdd2Sjoerg 
954e56cdd2Sjoerg static volatile uint32_t iq80310_base;
964e56cdd2Sjoerg 
97acf9a688Sthorpej int	clockhandler(void *);
98acf9a688Sthorpej 
995f1c88d7Sperry static inline void
100acf9a688Sthorpej timer_enable(uint8_t bit)
101acf9a688Sthorpej {
102acf9a688Sthorpej 
1030ea59754Sthorpej 	CPLD_WRITE(IQ80310_TIMER_ENABLE,
1040ea59754Sthorpej 	    CPLD_READ(IQ80310_TIMER_ENABLE) | bit);
105acf9a688Sthorpej }
106acf9a688Sthorpej 
1075f1c88d7Sperry static inline void
108acf9a688Sthorpej timer_disable(uint8_t bit)
109acf9a688Sthorpej {
110acf9a688Sthorpej 
1110ea59754Sthorpej 	CPLD_WRITE(IQ80310_TIMER_ENABLE,
1120ea59754Sthorpej 	    CPLD_READ(IQ80310_TIMER_ENABLE) & ~bit);
113acf9a688Sthorpej }
114acf9a688Sthorpej 
1155f1c88d7Sperry static inline uint32_t
116acf9a688Sthorpej timer_read(void)
117acf9a688Sthorpej {
118f30c8426Sthorpej 	uint32_t rv;
119a9e4c1a9Sthorpej 	uint8_t la0, la1, la2, la3;
120acf9a688Sthorpej 
121acf9a688Sthorpej 	/*
122acf9a688Sthorpej 	 * First read latches count.
123acf9a688Sthorpej 	 *
124*5dfdc49fSandvar 	 * From RedBoot: hardware bug that causes invalid counts to be
125acf9a688Sthorpej 	 * latched.  The loop appears to work around the problem.
126acf9a688Sthorpej 	 */
127acf9a688Sthorpej 	do {
128a9e4c1a9Sthorpej 		la0 = CPLD_READ(IQ80310_TIMER_LA0);
129a9e4c1a9Sthorpej 	} while (la0 == 0);
130a9e4c1a9Sthorpej 	la1 = CPLD_READ(IQ80310_TIMER_LA1);
131a9e4c1a9Sthorpej 	la2 = CPLD_READ(IQ80310_TIMER_LA2);
132a9e4c1a9Sthorpej 	la3 = CPLD_READ(IQ80310_TIMER_LA3);
133acf9a688Sthorpej 
134a9e4c1a9Sthorpej 	rv  =  ((la0 & 0x40) >> 1) | (la0 & 0x1f);
135a9e4c1a9Sthorpej 	rv |= (((la1 & 0x40) >> 1) | (la1 & 0x1f)) << 6;
136a9e4c1a9Sthorpej 	rv |= (((la2 & 0x40) >> 1) | (la2 & 0x1f)) << 12;
137a9e4c1a9Sthorpej 	rv |= (la3 & 0x0f) << 18;
138acf9a688Sthorpej 
139f30c8426Sthorpej 	return (rv);
140acf9a688Sthorpej }
141acf9a688Sthorpej 
1425f1c88d7Sperry static inline void
143acf9a688Sthorpej timer_write(uint32_t x)
144acf9a688Sthorpej {
145acf9a688Sthorpej 
146727b4699Sthorpej 	KASSERT((x & COUNTER_MASK) == x);
147727b4699Sthorpej 
1480ea59754Sthorpej 	CPLD_WRITE(IQ80310_TIMER_LA0, x & 0xff);
1490ea59754Sthorpej 	CPLD_WRITE(IQ80310_TIMER_LA1, (x >> 8) & 0xff);
1500ea59754Sthorpej 	CPLD_WRITE(IQ80310_TIMER_LA2, (x >> 16) & 0x3f);
151acf9a688Sthorpej }
152acf9a688Sthorpej 
153acf9a688Sthorpej /*
154acf9a688Sthorpej  * iq80310_calibrate_delay:
155acf9a688Sthorpej  *
156acf9a688Sthorpej  *	Calibrate the delay loop.
157acf9a688Sthorpej  */
158acf9a688Sthorpej void
159acf9a688Sthorpej iq80310_calibrate_delay(void)
160acf9a688Sthorpej {
161acf9a688Sthorpej 
162acf9a688Sthorpej 	/*
163acf9a688Sthorpej 	 * We'll use the CPLD timer for delay(), as well.  We go
164acf9a688Sthorpej 	 * ahead and start it up now, just don't enable interrupts
165acf9a688Sthorpej 	 * until cpu_initclocks().
166acf9a688Sthorpej 	 *
167acf9a688Sthorpej 	 * Just use hz=100 for now -- we'll adjust it, if necessary,
168acf9a688Sthorpej 	 * in cpu_initclocks().
169acf9a688Sthorpej 	 */
170acf9a688Sthorpej 	counts_per_hz = COUNTS_PER_SEC / 100;
171acf9a688Sthorpej 
172acf9a688Sthorpej 	timer_disable(TIMER_ENABLE_INTEN);
173acf9a688Sthorpej 	timer_disable(TIMER_ENABLE_EN);
174acf9a688Sthorpej 
175acf9a688Sthorpej 	timer_write(counts_per_hz);
176acf9a688Sthorpej 
177acf9a688Sthorpej 	timer_enable(TIMER_ENABLE_EN);
178acf9a688Sthorpej }
179acf9a688Sthorpej 
180acf9a688Sthorpej /*
181acf9a688Sthorpej  * cpu_initclocks:
182acf9a688Sthorpej  *
183acf9a688Sthorpej  *	Initialize the clock and get them going.
184acf9a688Sthorpej  */
185acf9a688Sthorpej void
186acf9a688Sthorpej cpu_initclocks(void)
187acf9a688Sthorpej {
188acf9a688Sthorpej 	u_int oldirqstate;
189acf9a688Sthorpej 
190acf9a688Sthorpej 	if (hz < 50 || COUNTS_PER_SEC % hz) {
191acf9a688Sthorpej 		printf("Cannot get %d Hz clock; using 100 Hz\n", hz);
192acf9a688Sthorpej 		hz = 100;
193d8415403Sthorpej 	}
194acf9a688Sthorpej 
195acf9a688Sthorpej 	/*
196acf9a688Sthorpej 	 * We only have one timer available; stathz and profhz are
197d8415403Sthorpej 	 * always left as 0 (the upper-layer clock code deals with
198d8415403Sthorpej 	 * this situation).
199acf9a688Sthorpej 	 */
200acf9a688Sthorpej 	if (stathz != 0)
201d8415403Sthorpej 		printf("Cannot get %d Hz statclock\n", stathz);
202d8415403Sthorpej 	stathz = 0;
203acf9a688Sthorpej 
204acf9a688Sthorpej 	if (profhz != 0)
205d8415403Sthorpej 		printf("Cannot get %d Hz profclock\n", profhz);
206d8415403Sthorpej 	profhz = 0;
207acf9a688Sthorpej 
208acf9a688Sthorpej 	/* Report the clock frequency. */
209acf9a688Sthorpej 	printf("clock: hz=%d stathz=%d profhz=%d\n", hz, stathz, profhz);
210acf9a688Sthorpej 
211acf9a688Sthorpej 	/* Hook up the clock interrupt handler. */
212acf9a688Sthorpej 	clock_ih = iq80310_intr_establish(XINT3_IRQ(XINT3_TIMER), IPL_CLOCK,
213acf9a688Sthorpej 	    clockhandler, NULL);
214acf9a688Sthorpej 	if (clock_ih == NULL)
215acf9a688Sthorpej 		panic("cpu_initclocks: unable to register timer interrupt");
216acf9a688Sthorpej 
217acf9a688Sthorpej 	/* Set up the new clock parameters. */
218acf9a688Sthorpej 	oldirqstate = disable_interrupts(I32_bit);
219acf9a688Sthorpej 
220acf9a688Sthorpej 	timer_disable(TIMER_ENABLE_EN);
221acf9a688Sthorpej 
222acf9a688Sthorpej 	counts_per_hz = COUNTS_PER_SEC / hz;
223acf9a688Sthorpej 	timer_write(counts_per_hz);
224acf9a688Sthorpej 
225acf9a688Sthorpej 	timer_enable(TIMER_ENABLE_INTEN);
226acf9a688Sthorpej 	timer_enable(TIMER_ENABLE_EN);
227acf9a688Sthorpej 
228acf9a688Sthorpej 	restore_interrupts(oldirqstate);
2294e56cdd2Sjoerg 
2304e56cdd2Sjoerg 	tc_init(&iq80310_timecounter);
231acf9a688Sthorpej }
232acf9a688Sthorpej 
233acf9a688Sthorpej /*
234acf9a688Sthorpej  * setstatclockrate:
235acf9a688Sthorpej  *
236acf9a688Sthorpej  *	Set the rate of the statistics clock.
237acf9a688Sthorpej  *
238acf9a688Sthorpej  *	We assume that hz is either stathz or profhz, and that neither
239acf9a688Sthorpej  *	will change after being set by cpu_initclocks().  We could
240acf9a688Sthorpej  *	recalculate the intervals here, but that would be a pain.
241acf9a688Sthorpej  */
242acf9a688Sthorpej void
243fb96dc83She setstatclockrate(int newhz)
244acf9a688Sthorpej {
245acf9a688Sthorpej 
246acf9a688Sthorpej 	/*
247acf9a688Sthorpej 	 * Nothing to do, here; we can't change the statclock
248acf9a688Sthorpej 	 * rate on the IQ80310.
249acf9a688Sthorpej 	 */
250acf9a688Sthorpej }
251acf9a688Sthorpej 
2524e56cdd2Sjoerg static u_int
2534e56cdd2Sjoerg iq80310_get_timecount(struct timecounter *tc)
254acf9a688Sthorpej {
2554e56cdd2Sjoerg 	u_int oldirqstate, base, counter;
256acf9a688Sthorpej 
257acf9a688Sthorpej 	oldirqstate = disable_interrupts(I32_bit);
2584e56cdd2Sjoerg 	base = iq80310_base;
2594e56cdd2Sjoerg 	counter = timer_read();
260acf9a688Sthorpej 	restore_interrupts(oldirqstate);
2614e56cdd2Sjoerg 
2624e56cdd2Sjoerg 	return base + counter;
263acf9a688Sthorpej }
264acf9a688Sthorpej 
265acf9a688Sthorpej /*
266acf9a688Sthorpej  * delay:
267acf9a688Sthorpej  *
268acf9a688Sthorpej  *	Delay for at least N microseconds.
269acf9a688Sthorpej  */
270acf9a688Sthorpej void
271acf9a688Sthorpej delay(u_int n)
272acf9a688Sthorpej {
273acf9a688Sthorpej 	uint32_t cur, last, delta, usecs;
274acf9a688Sthorpej 
275acf9a688Sthorpej 	/*
276acf9a688Sthorpej 	 * This works by polling the timer and counting the
277acf9a688Sthorpej 	 * number of microseconds that go by.
278acf9a688Sthorpej 	 */
279acf9a688Sthorpej 	last = timer_read();
280acf9a688Sthorpej 	delta = usecs = 0;
281acf9a688Sthorpej 
282f30c8426Sthorpej 	while (n > usecs) {
283acf9a688Sthorpej 		cur = timer_read();
284acf9a688Sthorpej 
285acf9a688Sthorpej 		/* Check to see if the timer has wrapped around. */
286acf9a688Sthorpej 		if (cur < last)
287f30c8426Sthorpej 			delta += ((counts_per_hz - last) + cur);
288acf9a688Sthorpej 		else
289f30c8426Sthorpej 			delta += (cur - last);
290acf9a688Sthorpej 
291acf9a688Sthorpej 		last = cur;
292acf9a688Sthorpej 
293acf9a688Sthorpej 		if (delta >= COUNTS_PER_USEC) {
294acf9a688Sthorpej 			usecs += delta / COUNTS_PER_USEC;
295acf9a688Sthorpej 			delta %= COUNTS_PER_USEC;
296acf9a688Sthorpej 		}
297acf9a688Sthorpej 	}
298acf9a688Sthorpej }
299acf9a688Sthorpej 
300acf9a688Sthorpej /*
301acf9a688Sthorpej  * clockhandler:
302acf9a688Sthorpej  *
303acf9a688Sthorpej  *	Handle the hardclock interrupt.
304acf9a688Sthorpej  */
305acf9a688Sthorpej int
306acf9a688Sthorpej clockhandler(void *arg)
307acf9a688Sthorpej {
308acf9a688Sthorpej 	struct clockframe *frame = arg;
309acf9a688Sthorpej 
310acf9a688Sthorpej 	timer_disable(TIMER_ENABLE_INTEN);
311acf9a688Sthorpej 	timer_enable(TIMER_ENABLE_INTEN);
312acf9a688Sthorpej 
3134e56cdd2Sjoerg 	atomic_add_32(&iq80310_base, counts_per_hz);
3144e56cdd2Sjoerg 
315acf9a688Sthorpej 	hardclock(frame);
316acf9a688Sthorpej 
317755369ecSthorpej 	/*
318755369ecSthorpej 	 * Don't run the snake on IOP310-based systems that
319755369ecSthorpej 	 * don't have the 7-segment display.
320755369ecSthorpej 	 */
321755369ecSthorpej #if !defined(IOP310_TEAMASA_NPWR)
322755369ecSthorpej 	{
323755369ecSthorpej 		static int snakefreq;
324755369ecSthorpej 
325a7cfcd87Sthorpej 		if ((snakefreq++ & 15) == 0)
326a7cfcd87Sthorpej 			iq80310_7seg_snake();
327755369ecSthorpej 	}
328755369ecSthorpej #endif
329a7cfcd87Sthorpej 
330acf9a688Sthorpej 	return (1);
331acf9a688Sthorpej }
332