xref: /netbsd-src/sys/arch/arm/xscale/ixp425_timer.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: ixp425_timer.c,v 1.13 2007/01/06 16:18:18 christos Exp $ */
2 
3 /*
4  * Copyright (c) 2003
5  *	Ichiro FUKUHARA <ichiro@ichiro.org>.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by Ichiro FUKUHARA.
19  * 4. The name of the company nor the name of the author may be used to
20  *    endorse or promote products derived from this software without specific
21  *    prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY ICHIRO FUKUHARA ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL ICHIRO FUKUHARA OR THE VOICES IN HIS HEAD BE LIABLE FOR
27  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: ixp425_timer.c,v 1.13 2007/01/06 16:18:18 christos Exp $");
38 
39 #include "opt_ixp425.h"
40 #include "opt_perfctrs.h"
41 
42 #include <sys/types.h>
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/kernel.h>
46 #include <sys/time.h>
47 #include <sys/device.h>
48 
49 #include <dev/clock_subr.h>
50 
51 #include <machine/bus.h>
52 #include <machine/intr.h>
53 
54 #include <arm/cpufunc.h>
55 
56 #include <arm/xscale/ixp425reg.h>
57 #include <arm/xscale/ixp425var.h>
58 #include <arm/xscale/ixp425_sipvar.h>
59 
60 static int	ixpclk_match(struct device *, struct cfdata *, void *);
61 static void	ixpclk_attach(struct device *, struct device *, void *);
62 
63 static uint32_t counts_per_hz;
64 
65 static void *clock_ih;
66 
67 /* callback functions for intr_functions */
68 int	ixpclk_intr(void *);
69 
70 struct ixpclk_softc {
71 	struct device		sc_dev;
72 	bus_addr_t		sc_baseaddr;
73 	bus_space_tag_t		sc_iot;
74 	bus_space_handle_t      sc_ioh;
75 };
76 
77 #ifndef IXP425_CLOCK_FREQ
78 #define	COUNTS_PER_SEC		66666600	/* 66MHz */
79 #else
80 #define	COUNTS_PER_SEC		IXP425_CLOCK_FREQ
81 #endif
82 #define	COUNTS_PER_USEC		((COUNTS_PER_SEC / 1000000) + 1)
83 
84 static struct ixpclk_softc *ixpclk_sc;
85 
86 CFATTACH_DECL(ixpclk, sizeof(struct ixpclk_softc),
87 		ixpclk_match, ixpclk_attach, NULL, NULL);
88 
89 #define GET_TIMER_VALUE(sc)	(bus_space_read_4((sc)->sc_iot,		\
90 						  (sc)->sc_ioh,		\
91 						  IXP425_OST_TIM0))
92 
93 #define GET_TS_VALUE(sc)	(*(volatile u_int32_t *) \
94 				  (IXP425_TIMER_VBASE + IXP425_OST_TS))
95 
96 static int
97 ixpclk_match(struct device *parent, struct cfdata *match, void *aux)
98 {
99 	return 2;
100 }
101 
102 static void
103 ixpclk_attach(struct device *parent, struct device *self, void *aux)
104 {
105 	struct ixpclk_softc		*sc = (struct ixpclk_softc*) self;
106 	struct ixpsip_attach_args	*sa = aux;
107 
108 	printf("\n");
109 
110 	ixpclk_sc = sc;
111 
112 	sc->sc_iot = sa->sa_iot;
113 	sc->sc_baseaddr = sa->sa_addr;
114 
115 	if (bus_space_map(sc->sc_iot, sa->sa_addr, sa->sa_size, 0,
116 			  &sc->sc_ioh))
117 		panic("%s: Cannot map registers", self->dv_xname);
118 
119 	aprint_normal("%s: IXP425 Interval Timer\n", sc->sc_dev.dv_xname);
120 }
121 
122 /*
123  * cpu_initclocks:
124  *
125  *	Initialize the clock and get them going.
126  */
127 void
128 cpu_initclocks(void)
129 {
130 	struct ixpclk_softc* sc = ixpclk_sc;
131 	u_int oldirqstate;
132 #if defined(PERFCTRS)
133 	void *pmu_ih;
134 #endif
135 
136 	if (hz < 50 || COUNTS_PER_SEC % hz) {
137 		aprint_error("Cannot get %d Hz clock; using 100 Hz\n", hz);
138 		hz = 100;
139 	}
140 	tick = 1000000 / hz;	/* number of microseconds between interrupts */
141 	tickfix = 1000000 - (hz * tick);
142 	if (tickfix) {
143 		int ftp;
144 
145 		ftp = min(ffs(tickfix), ffs(hz));
146 		tickfix >>= (ftp - 1);
147 		tickfixinterval = hz >> (ftp - 1);
148 	}
149 
150 	/*
151 	 * We only have one timer available; stathz and profhz are
152 	 * always left as 0 (the upper-layer clock code deals with
153 	 * this situation).
154 	 */
155 	if (stathz != 0)
156 		aprint_error("Cannot get %d Hz statclock\n", stathz);
157 	stathz = 0;
158 
159 	if (profhz != 0)
160 		aprint_error("Cannot get %d Hz profclock\n", profhz);
161 	profhz = 0;
162 
163 	/* Report the clock frequency. */
164 	aprint_normal("clock: hz=%d stathz=%d profhz=%d\n", hz, stathz, profhz);
165 
166 	oldirqstate = disable_interrupts(I32_bit);
167 
168 	/* Hook up the clock interrupt handler. */
169 	clock_ih = ixp425_intr_establish(IXP425_INT_TMR0, IPL_CLOCK,
170 					 ixpclk_intr, NULL);
171 	if (clock_ih == NULL)
172 		panic("cpu_initclocks: unable to register timer interrupt");
173 
174 #if defined(PERFCTRS)
175 	pmu_ih = ixp425_intr_establish(IXP425_INT_XPMU, IPL_STATCLOCK,
176 					xscale_pmc_dispatch, NULL);
177 	if (pmu_ih == NULL)
178 		panic("cpu_initclocks: unable to register timer interrupt");
179 #endif
180 
181 	/* Set up the new clock parameters. */
182 
183 	/* clear interrupt */
184 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS,
185 			  OST_WARM_RESET | OST_WDOG_INT | OST_TS_INT |
186 			  OST_TIM1_INT | OST_TIM0_INT);
187 
188 	counts_per_hz = COUNTS_PER_SEC / hz;
189 
190 	/* reload value & Timer enable */
191 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_TIM0_RELOAD,
192 			  (counts_per_hz & TIMERRELOAD_MASK) | OST_TIMER_EN);
193 
194 	restore_interrupts(oldirqstate);
195 }
196 
197 /*
198  * setstatclockrate:
199  *
200  *	Set the rate of the statistics clock.
201  *
202  *	We assume that hz is either stathz or profhz, and that neither
203  *	will change after being set by cpu_initclocks().  We could
204  *	recalculate the intervals here, but that would be a pain.
205  */
206 void
207 setstatclockrate(int newhz)
208 {
209 
210 	/*
211 	 * XXX Use TMR1?
212 	 */
213 }
214 
215 /*
216  * microtime:
217  *
218  *	Fill in the specified timeval struct with the current time
219  *	accurate to the microsecond.
220  */
221 void
222 microtime(struct timeval *tvp)
223 {
224 	struct ixpclk_softc* sc = ixpclk_sc;
225 	static struct timeval lasttv;
226 	u_int oldirqstate;
227 	uint32_t counts;
228 
229 	oldirqstate = disable_interrupts(I32_bit);
230 
231 	counts = counts_per_hz - GET_TIMER_VALUE(sc);
232 
233 	/* Fill in the timeval struct. */
234 	*tvp = time;
235 	tvp->tv_usec += (counts / COUNTS_PER_USEC);
236 
237 	/* Make sure microseconds doesn't overflow. */
238 	while (tvp->tv_usec >= 1000000) {
239 		tvp->tv_usec -= 1000000;
240 		tvp->tv_sec++;
241 	}
242 
243 	/* Make sure the time has advanced. */
244 	if (tvp->tv_sec == lasttv.tv_sec &&
245 	    tvp->tv_usec <= lasttv.tv_usec) {
246 		tvp->tv_usec = lasttv.tv_usec + 1;
247 		if (tvp->tv_usec >= 1000000) {
248 			tvp->tv_usec -= 1000000;
249 			tvp->tv_sec++;
250 		}
251 	}
252 
253 	lasttv = *tvp;
254 
255 	restore_interrupts(oldirqstate);
256 }
257 
258 /*
259  * delay:
260  *
261  *	Delay for at least N microseconds.
262  */
263 void
264 delay(u_int n)
265 {
266 	u_int32_t first, last;
267 	int usecs;
268 
269 	if (n == 0)
270 		return;
271 
272 	/*
273 	 * Clamp the timeout at a maximum value (about 32 seconds with
274 	 * a 66MHz clock). *Nobody* should be delay()ing for anywhere
275 	 * near that length of time and if they are, they should be hung
276 	 * out to dry.
277 	 */
278 	if (n >= (0x80000000U / COUNTS_PER_USEC))
279 		usecs = (0x80000000U / COUNTS_PER_USEC) - 1;
280 	else
281 		usecs = n * COUNTS_PER_USEC;
282 
283 	/* Note: Timestamp timer counts *up*, unlike the other timers */
284 	first = GET_TS_VALUE();
285 
286 	while (usecs > 0) {
287 		last = GET_TS_VALUE();
288 		usecs -= (int)(last - first);
289 		first = last;
290 	}
291 }
292 
293 /*
294  * ixpclk_intr:
295  *
296  *	Handle the hardclock interrupt.
297  */
298 int
299 ixpclk_intr(void *arg)
300 {
301 	struct ixpclk_softc* sc = ixpclk_sc;
302 	struct clockframe *frame = arg;
303 
304 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS,
305 			  OST_TIM0_INT);
306 
307 	hardclock(frame);
308 
309 	return (1);
310 }
311