ixp425_timer.c revision 1.1 1 /* $NetBSD: ixp425_timer.c,v 1.1 2003/05/23 00:57:26 ichiro Exp $ */
2
3 /*
4 * Copyright (c) 2003
5 * Ichiro FUKUHARA <ichiro (at) 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.1 2003/05/23 00:57:26 ichiro Exp $");
38
39 #include "opt_perfctrs.h"
40
41 #include <sys/types.h>
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/kernel.h>
45 #include <sys/time.h>
46 #include <sys/device.h>
47
48 #include <machine/bus.h>
49 #include <machine/intr.h>
50
51 #include <arm/cpufunc.h>
52
53 #include <arm/xscale/ixp425reg.h>
54 #include <arm/xscale/ixp425var.h>
55 #include <arm/xscale/ixp425_sipvar.h>
56
57 static int ixpclk_match(struct device *, struct cfdata *, void *);
58 static void ixpclk_attach(struct device *, struct device *, void *);
59
60 static uint32_t counts_per_hz;
61
62 static void *clock_ih;
63
64 /* callback functions for intr_functions */
65 int ixpclk_intr(void *);
66
67 struct ixpclk_softc {
68 struct device sc_dev;
69 bus_addr_t sc_baseaddr;
70 bus_space_tag_t sc_iot;
71 bus_space_handle_t sc_ioh;
72 };
73
74 #define COUNTS_PER_SEC 66000000 /* 66MHz */
75 #define COUNTS_PER_USEC (COUNTS_PER_SEC / 1000000)
76
77 static struct ixpclk_softc *ixpclk_sc = NULL;
78
79 CFATTACH_DECL(ixpclk, sizeof(struct ixpclk_softc),
80 ixpclk_match, ixpclk_attach, NULL, NULL);
81
82 #define GET_TIMER_VALUE(sc) (bus_space_read_4((sc)->sc_iot, \
83 (sc)->sc_ioh, \
84 IXP425_OST_TIM0))
85
86 static int
87 ixpclk_match(struct device *parent, struct cfdata *match, void *aux)
88 {
89 return 2;
90 }
91
92 static void
93 ixpclk_attach(struct device *parent, struct device *self, void *aux)
94 {
95 struct ixpclk_softc *sc = (struct ixpclk_softc*) self;
96 struct ixpsip_attach_args *sa = aux;
97
98 printf("\n");
99
100 sc->sc_iot = sa->sa_iot;
101 sc->sc_baseaddr = sa->sa_addr;
102
103 /* using first timer for system ticks */
104 if (ixpclk_sc == NULL)
105 ixpclk_sc = sc;
106 if (bus_space_map(sc->sc_iot, sa->sa_addr, sa->sa_size, 0,
107 &sc->sc_ioh))
108 panic("%s: Cannot map registers", self->dv_xname);
109
110 aprint_normal("%s: IXP425 Interval Timer\n", sc->sc_dev.dv_xname);
111 }
112
113 /*
114 * cpu_initclocks:
115 *
116 * Initialize the clock and get them going.
117 */
118 void
119 cpu_initclocks(void)
120 {
121 struct ixpclk_softc* sc = ixpclk_sc;
122 u_int oldirqstate;
123 #if defined(PERFCTRS)
124 void *pmu_ih;
125 #endif
126
127 if (hz < 50 || COUNTS_PER_SEC % hz) {
128 aprint_error("Cannot get %d Hz clock; using 100 Hz\n", hz);
129 hz = 100;
130 }
131 tick = 1000000 / hz; /* number of microseconds between interrupts */
132 tickfix = 1000000 - (hz * tick);
133 if (tickfix) {
134 int ftp;
135
136 ftp = min(ffs(tickfix), ffs(hz));
137 tickfix >>= (ftp - 1);
138 tickfixinterval = hz >> (ftp - 1);
139 }
140
141 /*
142 * We only have one timer available; stathz and profhz are
143 * always left as 0 (the upper-layer clock code deals with
144 * this situation).
145 */
146 if (stathz != 0)
147 aprint_error("Cannot get %d Hz statclock\n", stathz);
148 stathz = 0;
149
150 if (profhz != 0)
151 aprint_error("Cannot get %d Hz profclock\n", profhz);
152 profhz = 0;
153
154 /* Report the clock frequency. */
155 aprint_normal("clock: hz=%d stathz=%d profhz=%d\n", hz, stathz, profhz);
156
157 oldirqstate = disable_interrupts(I32_bit);
158
159 /* Hook up the clock interrupt handler. */
160 clock_ih = ixp425_intr_establish(IXP425_INT_TMR0, IPL_CLOCK,
161 ixpclk_intr, NULL);
162 if (clock_ih == NULL)
163 panic("cpu_initclocks: unable to register timer interrupt");
164
165 #if defined(PERFCTRS)
166 pmu_ih = ixp425_intr_establish(IXP425_INT_XPMU, IPL_STATCLOCK,
167 xscale_pmc_dispatch, NULL);
168 if (pmu_ih == NULL)
169 panic("cpu_initclocks: unable to register timer interrupt");
170 #endif
171
172 /* Set up the new clock parameters. */
173
174 /* clear interrupt */
175 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS,
176 OST_WARM_RESET | OST_WDOG_INT | OST_TS_INT |
177 OST_TIM1_INT | OST_TIM0_INT);
178
179 counts_per_hz = COUNTS_PER_SEC / hz;
180
181 /* reload value & Timer enable */
182 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_TIM0_RELOAD,
183 (counts_per_hz & TIMERRELOAD_MASK) | OST_TIMER_EN);
184
185 restore_interrupts(oldirqstate);
186 }
187
188 /*
189 * setstatclockrate:
190 *
191 * Set the rate of the statistics clock.
192 *
193 * We assume that hz is either stathz or profhz, and that neither
194 * will change after being set by cpu_initclocks(). We could
195 * recalculate the intervals here, but that would be a pain.
196 */
197 void
198 setstatclockrate(int hz)
199 {
200
201 /*
202 * XXX Use TMR1?
203 */
204 }
205
206 /*
207 * microtime:
208 *
209 * Fill in the specified timeval struct with the current time
210 * accurate to the microsecond.
211 */
212 void
213 microtime(struct timeval *tvp)
214 {
215 struct ixpclk_softc* sc = ixpclk_sc;
216 static struct timeval lasttv;
217 u_int oldirqstate;
218 uint32_t counts;
219
220 oldirqstate = disable_interrupts(I32_bit);
221
222 counts = counts_per_hz - GET_TIMER_VALUE(sc);
223
224 /* Fill in the timeval struct. */
225 *tvp = time;
226 tvp->tv_usec += (counts / COUNTS_PER_USEC);
227
228 /* Make sure microseconds doesn't overflow. */
229 while (tvp->tv_usec >= 1000000) {
230 tvp->tv_usec -= 1000000;
231 tvp->tv_sec++;
232 }
233
234 /* Make sure the time has advanced. */
235 if (tvp->tv_sec == lasttv.tv_sec &&
236 tvp->tv_usec <= lasttv.tv_usec) {
237 tvp->tv_usec = lasttv.tv_usec + 1;
238 if (tvp->tv_usec >= 1000000) {
239 tvp->tv_usec -= 1000000;
240 tvp->tv_sec++;
241 }
242 }
243
244 lasttv = *tvp;
245
246 restore_interrupts(oldirqstate);
247 }
248
249 /*
250 * delay:
251 *
252 * Delay for at least N microseconds.
253 */
254 void
255 delay(u_int n)
256 {
257 struct ixpclk_softc* sc = ixpclk_sc;
258 uint32_t cur, last, delta, usecs;
259
260 /*
261 * This works by polling the timer and counting the
262 * number of microseconds that go by.
263 */
264 last = GET_TIMER_VALUE(sc);
265 delta = usecs = 0;
266
267 while (n > usecs) {
268 cur = GET_TIMER_VALUE(sc);
269
270 /* Check to see if the timer has wrapped around. */
271 if (last < cur)
272 delta += (last + (counts_per_hz - cur));
273 else
274 delta += (last - cur);
275
276 last = cur;
277
278 if (delta >= COUNTS_PER_USEC) {
279 usecs += delta / COUNTS_PER_USEC;
280 delta %= COUNTS_PER_USEC;
281 }
282 }
283 }
284
285 /*
286 * inittodr:
287 *
288 * Initialize time from the time-of-day register.
289 */
290 void
291 inittodr(time_t base)
292 {
293
294 time.tv_sec = base;
295 time.tv_usec = 0;
296 }
297
298 /*
299 * resettodr:
300 *
301 * Reset the time-of-day register with the current time.
302 */
303 void
304 resettodr(void)
305 {
306 }
307
308 /*
309 * ixpclk_intr:
310 *
311 * Handle the hardclock interrupt.
312 */
313 int
314 ixpclk_intr(void *arg)
315 {
316 struct ixpclk_softc* sc = ixpclk_sc;
317 struct clockframe *frame = arg;
318
319 bus_space_write_4(sc->sc_iot, sc->sc_ioh, IXP425_OST_STATUS,
320 OST_TIM0_INT);
321
322 hardclock(frame);
323
324 return (1);
325 }
326