iomd_clock.c revision 1.4 1 /* $NetBSD: iomd_clock.c,v 1.4 2001/11/27 01:03:52 thorpej Exp $ */
2
3 /*
4 * Copyright (c) 1994-1997 Mark Brinicombe.
5 * Copyright (c) 1994 Brini.
6 * All rights reserved.
7 *
8 * This code is derived from software written for Brini by Mark Brinicombe
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by Mark Brinicombe.
21 * 4. The name of the company nor the name of the author may be used to
22 * endorse or promote products derived from this software without specific
23 * prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
37 * RiscBSD kernel project
38 *
39 * clock.c
40 *
41 * Timer related machine specific code
42 *
43 * Created : 29/09/94
44 */
45
46 /* Include header files */
47
48 #include <sys/types.h>
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/kernel.h>
52 #include <sys/time.h>
53 #include <sys/device.h>
54
55 #include <machine/intr.h>
56
57 #include <arm/cpufunc.h>
58
59 #include <arm/iomd/iomdvar.h>
60 #include <arm/iomd/iomdreg.h>
61
62 struct clock_softc {
63 struct device sc_dev;
64 bus_space_tag_t sc_iot;
65 bus_space_handle_t sc_ioh;
66 };
67
68 #define TIMER_FREQUENCY 2000000 /* 2MHz clock */
69 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY / 1000000)
70
71 static void *clockirq;
72 static void *statclockirq;
73 static struct clock_softc *clock_sc;
74 static int timer0_count;
75
76 static int clockmatch __P((struct device *parent, struct cfdata *cf, void *aux));
77 static void clockattach __P((struct device *parent, struct device *self, void *aux));
78 #ifdef DIAGNOSTIC
79 static void checkdelay __P((void));
80 #endif
81
82 struct cfattach clock_ca = {
83 sizeof(struct clock_softc), clockmatch, clockattach
84 };
85
86 /*
87 * int clockmatch(struct device *parent, void *match, void *aux)
88 *
89 * Just return ok for this if it is device 0
90 */
91
92 static int
93 clockmatch(parent, cf, aux)
94 struct device *parent;
95 struct cfdata *cf;
96 void *aux;
97 {
98 struct clk_attach_args *ca = aux;
99
100 if (strcmp(ca->ca_name, "clk") == 0)
101 return(1);
102 return(0);
103 }
104
105
106 /*
107 * void clockattach(struct device *parent, struct device *dev, void *aux)
108 *
109 * Map the IOMD and identify it.
110 * Then configure the child devices based on the IOMD ID.
111 */
112
113 static void
114 clockattach(parent, self, aux)
115 struct device *parent;
116 struct device *self;
117 void *aux;
118 {
119 struct clock_softc *sc = (struct clock_softc *)self;
120 struct clk_attach_args *ca = aux;
121
122 sc->sc_iot = ca->ca_iot;
123 sc->sc_ioh = ca->ca_ioh; /* This is a handle for the whole IOMD */
124
125 clock_sc = sc;
126
127 /* Cannot do anything until cpu_initclocks() has been called */
128
129 printf("\n");
130 }
131
132
133 /*
134 * int clockhandler(struct clockframe *frame)
135 *
136 * Function called by timer 0 interrupts. This just calls
137 * hardclock(). Eventually the irqhandler can call hardclock() directly
138 * but for now we use this function so that we can debug IRQ's
139 */
140
141 int
142 clockhandler(frame)
143 struct clockframe *frame;
144 {
145 hardclock(frame);
146 return(0); /* Pass the interrupt on down the chain */
147 }
148
149
150 /*
151 * int statclockhandler(struct clockframe *frame)
152 *
153 * Function called by timer 1 interrupts. This just calls
154 * statclock(). Eventually the irqhandler can call statclock() directly
155 * but for now we use this function so that we can debug IRQ's
156 */
157
158 int
159 statclockhandler(frame)
160 struct clockframe *frame;
161 {
162 statclock(frame);
163 return(0); /* Pass the interrupt on down the chain */
164 }
165
166
167 /*
168 * void setstatclockrate(int hz)
169 *
170 * Set the stat clock rate. The stat clock uses timer1
171 */
172
173 void
174 setstatclockrate(hz)
175 int hz;
176 {
177 int count;
178
179 count = TIMER_FREQUENCY / hz;
180
181 printf("Setting statclock to %dHz (%d ticks)\n", hz, count);
182
183 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
184 IOMD_T1LOW, (count >> 0) & 0xff);
185 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
186 IOMD_T1HIGH, (count >> 8) & 0xff);
187
188 /* reload the counter */
189
190 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
191 IOMD_T1GO, 0);
192 }
193
194
195 #ifdef DIAGNOSTIC
196 static void
197 checkdelay()
198 {
199 struct timeval start, end, diff;
200
201 microtime(&start);
202 delay(10000);
203 microtime(&end);
204 timersub(&end, &start, &diff);
205 if (diff.tv_sec > 0)
206 return;
207 if (diff.tv_usec > 10000)
208 return;
209 printf("WARNING: delay(10000) took %ld us\n", diff.tv_usec);
210 }
211 #endif
212
213 /*
214 * void cpu_initclocks(void)
215 *
216 * Initialise the clocks.
217 * This sets up the two timers in the IOMD and installs the IRQ handlers
218 *
219 * NOTE: Currently only timer 0 is setup and the IRQ handler is not installed
220 */
221
222 void
223 cpu_initclocks()
224 {
225 /*
226 * Load timer 0 with count down value
227 * This timer generates 100Hz interrupts for the system clock
228 */
229
230 printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz);
231
232 timer0_count = TIMER_FREQUENCY / hz;
233
234 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
235 IOMD_T0LOW, (timer0_count >> 0) & 0xff);
236 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
237 IOMD_T0HIGH, (timer0_count >> 8) & 0xff);
238
239 /* reload the counter */
240
241 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
242 IOMD_T0GO, 0);
243
244 clockirq = intr_claim(IRQ_TIMER0, IPL_CLOCK, "tmr0 hard clk",
245 clockhandler, 0);
246
247 if (clockirq == NULL)
248 panic("%s: Cannot installer timer 0 IRQ handler\n",
249 clock_sc->sc_dev.dv_xname);
250
251 if (stathz) {
252 setstatclockrate(stathz);
253 statclockirq = intr_claim(IRQ_TIMER1, IPL_CLOCK,
254 "tmr1 stat clk", statclockhandler, 0);
255 if (statclockirq == NULL)
256 panic("%s: Cannot installer timer 1 IRQ handler\n",
257 clock_sc->sc_dev.dv_xname);
258 }
259 #ifdef DIAGNOSTIC
260 checkdelay();
261 #endif
262 }
263
264
265 /*
266 * void microtime(struct timeval *tvp)
267 *
268 * Fill in the specified timeval struct with the current time
269 * accurate to the microsecond.
270 */
271
272 void
273 microtime(tvp)
274 struct timeval *tvp;
275 {
276 int s;
277 int tm;
278 int deltatm;
279 static struct timeval oldtv;
280
281 if (timer0_count == 0)
282 return;
283
284 s = splhigh();
285
286 /*
287 * Latch the current value of the timer and then read it.
288 * This garentees an atmoic reading of the time.
289 */
290
291 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
292 IOMD_T0LATCH, 0);
293
294 tm = bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh,
295 IOMD_T0LOW);
296 tm += (bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh,
297 IOMD_T0HIGH) << 8);
298
299 deltatm = timer0_count - tm;
300 if (deltatm < 0)
301 printf("opps deltatm < 0 tm=%d deltatm=%d\n",
302 tm, deltatm);
303
304 /* Fill in the timeval struct */
305 *tvp = time;
306
307 tvp->tv_usec += (deltatm / TICKS_PER_MICROSECOND);
308
309 /* Make sure the micro seconds don't overflow. */
310 while (tvp->tv_usec >= 1000000) {
311 tvp->tv_usec -= 1000000;
312 ++tvp->tv_sec;
313 }
314
315 /* Make sure the time has advanced. */
316 if (tvp->tv_sec == oldtv.tv_sec &&
317 tvp->tv_usec <= oldtv.tv_usec) {
318 tvp->tv_usec = oldtv.tv_usec + 1;
319 if (tvp->tv_usec >= 1000000) {
320 tvp->tv_usec -= 1000000;
321 ++tvp->tv_sec;
322 }
323 }
324
325 oldtv = *tvp;
326 (void)splx(s);
327 }
328
329 /*
330 * Estimated loop for n microseconds
331 */
332
333 /* Need to re-write this to use the timers */
334
335 /* One day soon I will actually do this */
336
337 int delaycount = 100;
338
339 void
340 delay(n)
341 u_int n;
342 {
343 u_int i;
344
345 if (n == 0) return;
346 while (--n > 0) {
347 if (cputype == CPU_ID_SA110) /* XXX - Seriously gross hack */
348 for (i = delaycount; --i;);
349 else
350 for (i = 8; --i;);
351 }
352 }
353
354 /* End of iomd_clock.c */
355