xref: /netbsd-src/sys/arch/arm/xscale/i80321_timer.c (revision 62c8988166d9cbbc196f28cb66639bad5c7722c1)
1*62c89881Smaxv /*	$NetBSD: i80321_timer.c,v 1.22 2018/07/12 10:46:42 maxv Exp $ */
2f5362116Sthorpej 
3f5362116Sthorpej /*
4f5362116Sthorpej  * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
5f5362116Sthorpej  * All rights reserved.
6f5362116Sthorpej  *
7f5362116Sthorpej  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8f5362116Sthorpej  *
9f5362116Sthorpej  * Redistribution and use in source and binary forms, with or without
10f5362116Sthorpej  * modification, are permitted provided that the following conditions
11f5362116Sthorpej  * are met:
12f5362116Sthorpej  * 1. Redistributions of source code must retain the above copyright
13f5362116Sthorpej  *    notice, this list of conditions and the following disclaimer.
14f5362116Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
15f5362116Sthorpej  *    notice, this list of conditions and the following disclaimer in the
16f5362116Sthorpej  *    documentation and/or other materials provided with the distribution.
17f5362116Sthorpej  * 3. All advertising materials mentioning features or use of this software
18f5362116Sthorpej  *    must display the following acknowledgement:
19f5362116Sthorpej  *	This product includes software developed for the NetBSD Project by
20f5362116Sthorpej  *	Wasabi Systems, Inc.
21f5362116Sthorpej  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22f5362116Sthorpej  *    or promote products derived from this software without specific prior
23f5362116Sthorpej  *    written permission.
24f5362116Sthorpej  *
25f5362116Sthorpej  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26f5362116Sthorpej  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27f5362116Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28f5362116Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29f5362116Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30f5362116Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31f5362116Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32f5362116Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33f5362116Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34f5362116Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35f5362116Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
36f5362116Sthorpej  */
37f5362116Sthorpej 
38f5362116Sthorpej /*
39f5362116Sthorpej  * Timer/clock support for the Intel i80321 I/O processor.
40f5362116Sthorpej  */
41f5362116Sthorpej 
4208716eaeSlukem #include <sys/cdefs.h>
43*62c89881Smaxv __KERNEL_RCSID(0, "$NetBSD: i80321_timer.c,v 1.22 2018/07/12 10:46:42 maxv Exp $");
4408716eaeSlukem 
4531711b75Srearnsha #include "opt_i80321.h"
460b956d0bSbriggs 
47f5362116Sthorpej #include <sys/param.h>
48f5362116Sthorpej #include <sys/systm.h>
49f5362116Sthorpej #include <sys/kernel.h>
50f5362116Sthorpej #include <sys/time.h>
518814e529Sgavan #include <sys/timetc.h>
52f5362116Sthorpej 
53ca601a77Sthorpej #include <dev/clock_subr.h>
54ca601a77Sthorpej 
55ed9977b1Sdyoung #include <sys/bus.h>
56f5362116Sthorpej #include <arm/cpufunc.h>
57f5362116Sthorpej 
58f5362116Sthorpej #include <arm/xscale/i80321reg.h>
59f5362116Sthorpej #include <arm/xscale/i80321var.h>
60f5362116Sthorpej 
61f5362116Sthorpej void	(*i80321_hardclock_hook)(void);
62f5362116Sthorpej 
6331711b75Srearnsha #ifndef COUNTS_PER_SEC
64f5362116Sthorpej #define	COUNTS_PER_SEC		200000000	/* 200MHz */
6531711b75Srearnsha #endif
66f5362116Sthorpej #define	COUNTS_PER_USEC		(COUNTS_PER_SEC / 1000000)
67f5362116Sthorpej 
688814e529Sgavan static void tmr1_tc_init(void);
698814e529Sgavan 
70f5362116Sthorpej static void *clock_ih;
71f5362116Sthorpej 
72f5362116Sthorpej static uint32_t counts_per_hz;
73f5362116Sthorpej 
74f5362116Sthorpej int	clockhandler(void *);
75f5362116Sthorpej 
76b6c4ec7eSjoerg __unused static inline uint32_t
tmr0_read(void)77f5362116Sthorpej tmr0_read(void)
78f5362116Sthorpej {
79f5362116Sthorpej 	uint32_t rv;
80f5362116Sthorpej 
815f1c88d7Sperry 	__asm volatile("mrc p6, 0, %0, c0, c1, 0"
82f5362116Sthorpej 		: "=r" (rv));
83f5362116Sthorpej 	return (rv);
84f5362116Sthorpej }
85f5362116Sthorpej 
865f1c88d7Sperry static inline void
tmr0_write(uint32_t val)87f5362116Sthorpej tmr0_write(uint32_t val)
88f5362116Sthorpej {
89f5362116Sthorpej 
905f1c88d7Sperry 	__asm volatile("mcr p6, 0, %0, c0, c1, 0"
91f5362116Sthorpej 		:
92f5362116Sthorpej 		: "r" (val));
93f5362116Sthorpej }
94f5362116Sthorpej 
955f1c88d7Sperry static inline uint32_t
tcr0_read(void)96f5362116Sthorpej tcr0_read(void)
97f5362116Sthorpej {
98f5362116Sthorpej 	uint32_t rv;
99f5362116Sthorpej 
1005f1c88d7Sperry 	__asm volatile("mrc p6, 0, %0, c2, c1, 0"
101f5362116Sthorpej 		: "=r" (rv));
102f5362116Sthorpej 	return (rv);
103f5362116Sthorpej }
104f5362116Sthorpej 
1055f1c88d7Sperry static inline void
tcr0_write(uint32_t val)106f5362116Sthorpej tcr0_write(uint32_t val)
107f5362116Sthorpej {
108f5362116Sthorpej 
1095f1c88d7Sperry 	__asm volatile("mcr p6, 0, %0, c2, c1, 0"
110f5362116Sthorpej 		:
111f5362116Sthorpej 		: "r" (val));
112f5362116Sthorpej }
113f5362116Sthorpej 
1145f1c88d7Sperry static inline void
trr0_write(uint32_t val)115f5362116Sthorpej trr0_write(uint32_t val)
116f5362116Sthorpej {
117f5362116Sthorpej 
1185f1c88d7Sperry 	__asm volatile("mcr p6, 0, %0, c4, c1, 0"
119f5362116Sthorpej 		:
120f5362116Sthorpej 		: "r" (val));
121f5362116Sthorpej }
122f5362116Sthorpej 
123b6c4ec7eSjoerg __unused static inline uint32_t
tmr1_read(void)1248814e529Sgavan tmr1_read(void)
1258814e529Sgavan {
1268814e529Sgavan 	uint32_t rv;
1278814e529Sgavan 
1288814e529Sgavan 	__asm volatile("mrc p6, 0, %0, c1, c1, 0"
1298814e529Sgavan 		: "=r" (rv));
1308814e529Sgavan 	return (rv);
1318814e529Sgavan }
1328814e529Sgavan 
1338814e529Sgavan static inline void
tmr1_write(uint32_t val)1348814e529Sgavan tmr1_write(uint32_t val)
1358814e529Sgavan {
1368814e529Sgavan 
1378814e529Sgavan 	__asm volatile("mcr p6, 0, %0, c1, c1, 0"
1388814e529Sgavan 		:
1398814e529Sgavan 		: "r" (val));
1408814e529Sgavan }
1418814e529Sgavan 
1428814e529Sgavan static inline uint32_t
tcr1_read(void)1438814e529Sgavan tcr1_read(void)
1448814e529Sgavan {
1458814e529Sgavan 	uint32_t rv;
1468814e529Sgavan 
1478814e529Sgavan 	__asm volatile("mrc p6, 0, %0, c3, c1, 0"
1488814e529Sgavan 		: "=r" (rv));
1498814e529Sgavan 	return (rv);
1508814e529Sgavan }
1518814e529Sgavan 
1528814e529Sgavan static inline void
tcr1_write(uint32_t val)1538814e529Sgavan tcr1_write(uint32_t val)
1548814e529Sgavan {
1558814e529Sgavan 
1568814e529Sgavan 	__asm volatile("mcr p6, 0, %0, c3, c1, 0"
1578814e529Sgavan 		:
1588814e529Sgavan 		: "r" (val));
1598814e529Sgavan }
1608814e529Sgavan 
1618814e529Sgavan static inline void
trr1_write(uint32_t val)1628814e529Sgavan trr1_write(uint32_t val)
1638814e529Sgavan {
1648814e529Sgavan 
1658814e529Sgavan 	__asm volatile("mcr p6, 0, %0, c5, c1, 0"
1668814e529Sgavan 		:
1678814e529Sgavan 		: "r" (val));
1688814e529Sgavan }
1698814e529Sgavan 
1705f1c88d7Sperry static inline void
tisr_write(uint32_t val)171f5362116Sthorpej tisr_write(uint32_t val)
172f5362116Sthorpej {
173f5362116Sthorpej 
1745f1c88d7Sperry 	__asm volatile("mcr p6, 0, %0, c6, c1, 0"
175f5362116Sthorpej 		:
176f5362116Sthorpej 		: "r" (val));
177f5362116Sthorpej }
178f5362116Sthorpej 
179f5362116Sthorpej /*
180f5362116Sthorpej  * i80321_calibrate_delay:
181f5362116Sthorpej  *
182f5362116Sthorpej  *	Calibrate the delay loop.
183f5362116Sthorpej  */
184f5362116Sthorpej void
i80321_calibrate_delay(void)185f5362116Sthorpej i80321_calibrate_delay(void)
186f5362116Sthorpej {
187f5362116Sthorpej 
188f5362116Sthorpej 	/*
189f5362116Sthorpej 	 * Just use hz=100 for now -- we'll adjust it, if necessary,
190f5362116Sthorpej 	 * in cpu_initclocks().
191f5362116Sthorpej 	 */
192f5362116Sthorpej 	counts_per_hz = COUNTS_PER_SEC / 100;
193f5362116Sthorpej 
194f5362116Sthorpej 	tmr0_write(0);			/* stop timer */
195f5362116Sthorpej 	tisr_write(TISR_TMR0);		/* clear interrupt */
196f5362116Sthorpej 	trr0_write(counts_per_hz);	/* reload value */
197f5362116Sthorpej 	tcr0_write(counts_per_hz);	/* current value */
198f5362116Sthorpej 
199f5362116Sthorpej 	tmr0_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_CSEL_CORE);
200f5362116Sthorpej }
201f5362116Sthorpej 
202f5362116Sthorpej /*
203f5362116Sthorpej  * cpu_initclocks:
204f5362116Sthorpej  *
205f5362116Sthorpej  *	Initialize the clock and get them going.
206f5362116Sthorpej  */
207f5362116Sthorpej void
cpu_initclocks(void)208f5362116Sthorpej cpu_initclocks(void)
209f5362116Sthorpej {
210f5362116Sthorpej 	u_int oldirqstate;
211f5362116Sthorpej 
212f5362116Sthorpej 	if (hz < 50 || COUNTS_PER_SEC % hz) {
213b43b1645Sthorpej 		aprint_error("Cannot get %d Hz clock; using 100 Hz\n", hz);
214f5362116Sthorpej 		hz = 100;
215f5362116Sthorpej 	}
216f5362116Sthorpej 
217f5362116Sthorpej 	/*
218f5362116Sthorpej 	 * We only have one timer available; stathz and profhz are
219f5362116Sthorpej 	 * always left as 0 (the upper-layer clock code deals with
220f5362116Sthorpej 	 * this situation).
221f5362116Sthorpej 	 */
222f5362116Sthorpej 	if (stathz != 0)
223b43b1645Sthorpej 		aprint_error("Cannot get %d Hz statclock\n", stathz);
224f5362116Sthorpej 	stathz = 0;
225f5362116Sthorpej 
226f5362116Sthorpej 	if (profhz != 0)
227b43b1645Sthorpej 		aprint_error("Cannot get %d Hz profclock\n", profhz);
228f5362116Sthorpej 	profhz = 0;
229f5362116Sthorpej 
230f5362116Sthorpej 	/* Report the clock frequency. */
231b43b1645Sthorpej 	aprint_normal("clock: hz=%d stathz=%d profhz=%d\n", hz, stathz, profhz);
232f5362116Sthorpej 
233f5362116Sthorpej 	oldirqstate = disable_interrupts(I32_bit);
234f5362116Sthorpej 
235f5362116Sthorpej 	/* Hook up the clock interrupt handler. */
236f5362116Sthorpej 	clock_ih = i80321_intr_establish(ICU_INT_TMR0, IPL_CLOCK,
237f5362116Sthorpej 	    clockhandler, NULL);
238f5362116Sthorpej 	if (clock_ih == NULL)
239f5362116Sthorpej 		panic("cpu_initclocks: unable to register timer interrupt");
240f5362116Sthorpej 
241f5362116Sthorpej 	/* Set up the new clock parameters. */
242f5362116Sthorpej 
243f5362116Sthorpej 	tmr0_write(0);			/* stop timer */
244f5362116Sthorpej 	tisr_write(TISR_TMR0);		/* clear interrupt */
245f5362116Sthorpej 
246f5362116Sthorpej 	counts_per_hz = COUNTS_PER_SEC / hz;
247f5362116Sthorpej 
248f5362116Sthorpej 	trr0_write(counts_per_hz);	/* reload value */
249f5362116Sthorpej 	tcr0_write(counts_per_hz);	/* current value */
250f5362116Sthorpej 
251f5362116Sthorpej 	tmr0_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_CSEL_CORE);
252f5362116Sthorpej 
253f5362116Sthorpej 	restore_interrupts(oldirqstate);
2548814e529Sgavan 
2558814e529Sgavan 	tmr1_tc_init();
256f5362116Sthorpej }
257f5362116Sthorpej 
258f5362116Sthorpej /*
259f5362116Sthorpej  * setstatclockrate:
260f5362116Sthorpej  *
261f5362116Sthorpej  *	Set the rate of the statistics clock.
262f5362116Sthorpej  *
263f5362116Sthorpej  *	We assume that hz is either stathz or profhz, and that neither
264f5362116Sthorpej  *	will change after being set by cpu_initclocks().  We could
265f5362116Sthorpej  *	recalculate the intervals here, but that would be a pain.
266f5362116Sthorpej  */
267f5362116Sthorpej void
setstatclockrate(int newhz)268848febeaShe setstatclockrate(int newhz)
269f5362116Sthorpej {
270f5362116Sthorpej 
271f5362116Sthorpej 	/*
272f5362116Sthorpej 	 * XXX Use TMR1?
273f5362116Sthorpej 	 */
274f5362116Sthorpej }
275f5362116Sthorpej 
2768814e529Sgavan static inline uint32_t
tmr1_tc_get(struct timecounter * tch)2778814e529Sgavan tmr1_tc_get(struct timecounter *tch)
2788814e529Sgavan {
2798814e529Sgavan 	return (~tcr1_read());
2808814e529Sgavan }
2818814e529Sgavan 
2828814e529Sgavan void
tmr1_tc_init(void)2838814e529Sgavan tmr1_tc_init(void)
2848814e529Sgavan {
2858814e529Sgavan 	static struct timecounter tmr1_tc = {
2868814e529Sgavan 		.tc_get_timecount = tmr1_tc_get,
2878814e529Sgavan 		.tc_frequency = COUNTS_PER_SEC,
2888814e529Sgavan 		.tc_counter_mask = ~0,
2898814e529Sgavan 		.tc_name = "tmr1_count",
2908814e529Sgavan 		.tc_quality = 100,
2918814e529Sgavan 	};
2928814e529Sgavan 
2938814e529Sgavan 	/* program the tc */
2948814e529Sgavan 	trr1_write(~0);	/* reload value */
2958814e529Sgavan 	tcr1_write(~0);	/* current value */
2968814e529Sgavan 
2978814e529Sgavan 	tmr1_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_CSEL_CORE);
2988814e529Sgavan 
2998814e529Sgavan 
3008814e529Sgavan 	trr1_write(~0);
3018814e529Sgavan 	tc_init(&tmr1_tc);
3028814e529Sgavan }
3038814e529Sgavan 
304f5362116Sthorpej /*
305f5362116Sthorpej  * delay:
306f5362116Sthorpej  *
307f5362116Sthorpej  *	Delay for at least N microseconds.
308f5362116Sthorpej  */
309f5362116Sthorpej void
delay(u_int n)310f5362116Sthorpej delay(u_int n)
311f5362116Sthorpej {
312f5362116Sthorpej 	uint32_t cur, last, delta, usecs;
313f5362116Sthorpej 
314f5362116Sthorpej 	/*
315f5362116Sthorpej 	 * This works by polling the timer and counting the
316f5362116Sthorpej 	 * number of microseconds that go by.
317f5362116Sthorpej 	 */
318f5362116Sthorpej 	last = tcr0_read();
319f5362116Sthorpej 	delta = usecs = 0;
320f5362116Sthorpej 
321f5362116Sthorpej 	while (n > usecs) {
322f5362116Sthorpej 		cur = tcr0_read();
323f5362116Sthorpej 
324f5362116Sthorpej 		/* Check to see if the timer has wrapped around. */
325f5362116Sthorpej 		if (last < cur)
326f5362116Sthorpej 			delta += (last + (counts_per_hz - cur));
327f5362116Sthorpej 		else
328f5362116Sthorpej 			delta += (last - cur);
329f5362116Sthorpej 
330f5362116Sthorpej 		last = cur;
331f5362116Sthorpej 
332f5362116Sthorpej 		if (delta >= COUNTS_PER_USEC) {
333f5362116Sthorpej 			usecs += delta / COUNTS_PER_USEC;
334f5362116Sthorpej 			delta %= COUNTS_PER_USEC;
335f5362116Sthorpej 		}
336f5362116Sthorpej 	}
337f5362116Sthorpej }
338f5362116Sthorpej 
339f5362116Sthorpej /*
340f5362116Sthorpej  * clockhandler:
341f5362116Sthorpej  *
342f5362116Sthorpej  *	Handle the hardclock interrupt.
343f5362116Sthorpej  */
344f5362116Sthorpej int
clockhandler(void * arg)345f5362116Sthorpej clockhandler(void *arg)
346f5362116Sthorpej {
347f5362116Sthorpej 	struct clockframe *frame = arg;
348f5362116Sthorpej 
349f5362116Sthorpej 	tisr_write(TISR_TMR0);
350f5362116Sthorpej 
351f5362116Sthorpej 	hardclock(frame);
352f5362116Sthorpej 
353f5362116Sthorpej 	if (i80321_hardclock_hook != NULL)
354f5362116Sthorpej 		(*i80321_hardclock_hook)();
355f5362116Sthorpej 
356f5362116Sthorpej 	return (1);
357f5362116Sthorpej }
358