xref: /netbsd/sys/arch/arm/xscale/i80321_timer.c (revision bf9ec67e)
1 /*	$NetBSD: i80321_timer.c,v 1.1 2002/03/27 21:45:48 thorpej Exp $	*/
2 
3 /*
4  * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
5  * All rights reserved.
6  *
7  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed for the NetBSD Project by
20  *	Wasabi Systems, Inc.
21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22  *    or promote products derived from this software without specific prior
23  *    written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 /*
39  * Timer/clock support for the Intel i80321 I/O processor.
40  */
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/time.h>
46 
47 #include <machine/bus.h>
48 #include <arm/cpufunc.h>
49 
50 #include <arm/xscale/i80321reg.h>
51 #include <arm/xscale/i80321var.h>
52 
53 void	(*i80321_hardclock_hook)(void);
54 
55 #define	COUNTS_PER_SEC		200000000	/* 200MHz */
56 #define	COUNTS_PER_USEC		(COUNTS_PER_SEC / 1000000)
57 
58 static void *clock_ih;
59 
60 static uint32_t counts_per_hz;
61 
62 int	clockhandler(void *);
63 
64 static __inline uint32_t
65 tmr0_read(void)
66 {
67 	uint32_t rv;
68 
69 	__asm __volatile("mrc p6, 0, %0, c0, c1, 0"
70 		: "=r" (rv));
71 	return (rv);
72 }
73 
74 static __inline void
75 tmr0_write(uint32_t val)
76 {
77 
78 	__asm __volatile("mcr p6, 0, %0, c0, c1, 0"
79 		:
80 		: "r" (val));
81 }
82 
83 static __inline uint32_t
84 tcr0_read(void)
85 {
86 	uint32_t rv;
87 
88 	__asm __volatile("mrc p6, 0, %0, c2, c1, 0"
89 		: "=r" (rv));
90 	return (rv);
91 }
92 
93 static __inline void
94 tcr0_write(uint32_t val)
95 {
96 
97 	__asm __volatile("mcr p6, 0, %0, c2, c1, 0"
98 		:
99 		: "r" (val));
100 }
101 
102 static __inline void
103 trr0_write(uint32_t val)
104 {
105 
106 	__asm __volatile("mcr p6, 0, %0, c4, c1, 0"
107 		:
108 		: "r" (val));
109 }
110 
111 static __inline void
112 tisr_write(uint32_t val)
113 {
114 
115 	__asm __volatile("mcr p6, 0, %0, c6, c1, 0"
116 		:
117 		: "r" (val));
118 }
119 
120 /*
121  * i80321_calibrate_delay:
122  *
123  *	Calibrate the delay loop.
124  */
125 void
126 i80321_calibrate_delay(void)
127 {
128 
129 	/*
130 	 * Just use hz=100 for now -- we'll adjust it, if necessary,
131 	 * in cpu_initclocks().
132 	 */
133 	counts_per_hz = COUNTS_PER_SEC / 100;
134 
135 	tmr0_write(0);			/* stop timer */
136 	tisr_write(TISR_TMR0);		/* clear interrupt */
137 	trr0_write(counts_per_hz);	/* reload value */
138 	tcr0_write(counts_per_hz);	/* current value */
139 
140 	tmr0_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_CSEL_CORE);
141 }
142 
143 /*
144  * cpu_initclocks:
145  *
146  *	Initialize the clock and get them going.
147  */
148 void
149 cpu_initclocks(void)
150 {
151 	u_int oldirqstate;
152 
153 	if (hz < 50 || COUNTS_PER_SEC % hz) {
154 		printf("Cannot get %d Hz clock; using 100 Hz\n", hz);
155 		hz = 100;
156 	}
157 	tick = 1000000 / hz;	/* number of microseconds between interrupts */
158 	tickfix = 1000000 - (hz * tick);
159 	if (tickfix) {
160 		int ftp;
161 
162 		ftp = min(ffs(tickfix), ffs(hz));
163 		tickfix >>= (ftp - 1);
164 		tickfixinterval = hz >> (ftp - 1);
165 	}
166 
167 	/*
168 	 * We only have one timer available; stathz and profhz are
169 	 * always left as 0 (the upper-layer clock code deals with
170 	 * this situation).
171 	 */
172 	if (stathz != 0)
173 		printf("Cannot get %d Hz statclock\n", stathz);
174 	stathz = 0;
175 
176 	if (profhz != 0)
177 		printf("Cannot get %d Hz profclock\n", profhz);
178 	profhz = 0;
179 
180 	/* Report the clock frequency. */
181 	printf("clock: hz=%d stathz=%d profhz=%d\n", hz, stathz, profhz);
182 
183 	oldirqstate = disable_interrupts(I32_bit);
184 
185 	/* Hook up the clock interrupt handler. */
186 	clock_ih = i80321_intr_establish(ICU_INT_TMR0, IPL_CLOCK,
187 	    clockhandler, NULL);
188 	if (clock_ih == NULL)
189 		panic("cpu_initclocks: unable to register timer interrupt");
190 
191 	/* Set up the new clock parameters. */
192 
193 	tmr0_write(0);			/* stop timer */
194 	tisr_write(TISR_TMR0);		/* clear interrupt */
195 
196 	counts_per_hz = COUNTS_PER_SEC / hz;
197 
198 	trr0_write(counts_per_hz);	/* reload value */
199 	tcr0_write(counts_per_hz);	/* current value */
200 
201 	tmr0_write(TMRx_ENABLE|TMRx_RELOAD|TMRx_CSEL_CORE);
202 
203 	restore_interrupts(oldirqstate);
204 }
205 
206 /*
207  * setstatclockrate:
208  *
209  *	Set the rate of the statistics clock.
210  *
211  *	We assume that hz is either stathz or profhz, and that neither
212  *	will change after being set by cpu_initclocks().  We could
213  *	recalculate the intervals here, but that would be a pain.
214  */
215 void
216 setstatclockrate(int hz)
217 {
218 
219 	/*
220 	 * XXX Use TMR1?
221 	 */
222 }
223 
224 /*
225  * microtime:
226  *
227  *	Fill in the specified timeval struct with the current time
228  *	accurate to the microsecond.
229  */
230 void
231 microtime(struct timeval *tvp)
232 {
233 	static struct timeval lasttv;
234 	u_int oldirqstate;
235 	uint32_t counts;
236 
237 	oldirqstate = disable_interrupts(I32_bit);
238 
239 	counts = counts_per_hz - tcr0_read();
240 
241 	/* Fill in the timeval struct. */
242 	*tvp = time;
243 	tvp->tv_usec += (counts / COUNTS_PER_USEC);
244 
245 	/* Make sure microseconds doesn't overflow. */
246 	while (tvp->tv_usec >= 1000000) {
247 		tvp->tv_usec -= 1000000;
248 		tvp->tv_sec++;
249 	}
250 
251 	/* Make sure the time has advanced. */
252 	if (tvp->tv_sec == lasttv.tv_sec &&
253 	    tvp->tv_usec <= lasttv.tv_usec) {
254 		tvp->tv_usec = lasttv.tv_usec + 1;
255 		if (tvp->tv_usec >= 1000000) {
256 			tvp->tv_usec -= 1000000;
257 			tvp->tv_sec++;
258 		}
259 	}
260 
261 	lasttv = *tvp;
262 
263 	restore_interrupts(oldirqstate);
264 }
265 
266 /*
267  * delay:
268  *
269  *	Delay for at least N microseconds.
270  */
271 void
272 delay(u_int n)
273 {
274 	uint32_t cur, last, delta, usecs;
275 
276 	/*
277 	 * This works by polling the timer and counting the
278 	 * number of microseconds that go by.
279 	 */
280 	last = tcr0_read();
281 	delta = usecs = 0;
282 
283 	while (n > usecs) {
284 		cur = tcr0_read();
285 
286 		/* Check to see if the timer has wrapped around. */
287 		if (last < cur)
288 			delta += (last + (counts_per_hz - cur));
289 		else
290 			delta += (last - cur);
291 
292 		last = cur;
293 
294 		if (delta >= COUNTS_PER_USEC) {
295 			usecs += delta / COUNTS_PER_USEC;
296 			delta %= COUNTS_PER_USEC;
297 		}
298 	}
299 }
300 
301 /*
302  * inittodr:
303  *
304  *	Initialize time from the time-of-day register.
305  */
306 void
307 inittodr(time_t base)
308 {
309 
310 	time.tv_sec = base;
311 	time.tv_usec = 0;
312 }
313 
314 /*
315  * resettodr:
316  *
317  *	Reset the time-of-day register with the current time.
318  */
319 void
320 resettodr(void)
321 {
322 }
323 
324 /*
325  * clockhandler:
326  *
327  *	Handle the hardclock interrupt.
328  */
329 int
330 clockhandler(void *arg)
331 {
332 	struct clockframe *frame = arg;
333 
334 	tisr_write(TISR_TMR0);
335 
336 	hardclock(frame);
337 
338 	if (i80321_hardclock_hook != NULL)
339 		(*i80321_hardclock_hook)();
340 
341 	return (1);
342 }
343