iomd_clock.c revision 1.19 1 /* $NetBSD: iomd_clock.c,v 1.19 2006/08/03 23:19:06 bjh21 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/param.h>
49
50 __KERNEL_RCSID(0, "$NetBSD: iomd_clock.c,v 1.19 2006/08/03 23:19:06 bjh21 Exp $");
51
52 #include <sys/systm.h>
53 #include <sys/kernel.h>
54 #include <sys/time.h>
55 #include <sys/timetc.h>
56 #include <sys/device.h>
57 #include <sys/lock.h>
58
59 #include <dev/clock_subr.h>
60
61 #include <machine/intr.h>
62
63 #include <arm/cpufunc.h>
64
65 #include <arm/iomd/iomdvar.h>
66 #include <arm/iomd/iomdreg.h>
67
68 struct clock_softc {
69 struct device sc_dev;
70 bus_space_tag_t sc_iot;
71 bus_space_handle_t sc_ioh;
72 };
73
74 #define TIMER_FREQUENCY 2000000 /* 2MHz clock */
75 #define TICKS_PER_MICROSECOND (TIMER_FREQUENCY / 1000000)
76
77 static void *clockirq;
78 static void *statclockirq;
79 static struct clock_softc *clock_sc;
80 static int timer0_count;
81 static int timeset;
82
83 static int clockmatch __P((struct device *parent, struct cfdata *cf, void *aux));
84 static void clockattach __P((struct device *parent, struct device *self, void *aux));
85 #ifdef DIAGNOSTIC
86 static void checkdelay __P((void));
87 #endif
88
89 static u_int iomd_timecounter0_get(struct timecounter *tc);
90
91
92 static volatile uint32_t timer0_lastcount;
93 static volatile uint32_t timer0_offset;
94 static volatile int timer0_ticked;
95 /* TODO: Get IRQ status */
96
97 static struct simplelock tmr_lock = SIMPLELOCK_INITIALIZER; /* protect TC timer variables */
98
99
100 static struct timecounter iomd_timecounter = {
101 iomd_timecounter0_get,
102 0, /* No poll_pps */
103 ~0, /* 32bit accuracy */
104 TIMER_FREQUENCY,
105 "iomd_timer0",
106 100
107 };
108
109 int clockhandler __P((void *));
110 int statclockhandler __P((void *));
111
112 CFATTACH_DECL(clock, sizeof(struct clock_softc),
113 clockmatch, clockattach, NULL, NULL);
114
115 /*
116 * int clockmatch(struct device *parent, void *match, void *aux)
117 *
118 * Just return ok for this if it is device 0
119 */
120
121 static int
122 clockmatch(parent, cf, aux)
123 struct device *parent;
124 struct cfdata *cf;
125 void *aux;
126 {
127 struct clk_attach_args *ca = aux;
128
129 if (strcmp(ca->ca_name, "clk") == 0)
130 return(1);
131 return(0);
132 }
133
134
135 /*
136 * void clockattach(struct device *parent, struct device *dev, void *aux)
137 *
138 * Map the IOMD and identify it.
139 * Then configure the child devices based on the IOMD ID.
140 */
141
142 static void
143 clockattach(parent, self, aux)
144 struct device *parent;
145 struct device *self;
146 void *aux;
147 {
148 struct clock_softc *sc = (struct clock_softc *)self;
149 struct clk_attach_args *ca = aux;
150
151 sc->sc_iot = ca->ca_iot;
152 sc->sc_ioh = ca->ca_ioh; /* This is a handle for the whole IOMD */
153
154 clock_sc = sc;
155
156 /* Cannot do anything until cpu_initclocks() has been called */
157
158 printf("\n");
159 }
160
161
162 static void
163 tickle_tc(void)
164 {
165 if (timer0_count &&
166 timecounter->tc_get_timecount == iomd_timecounter0_get) {
167 simple_lock(&tmr_lock);
168 if (timer0_ticked)
169 timer0_ticked = 0;
170 else {
171 timer0_offset += timer0_count;
172 timer0_lastcount = 0;
173 }
174 simple_unlock(&tmr_lock);
175 }
176
177 }
178
179
180 /*
181 * int clockhandler(struct clockframe *frame)
182 *
183 * Function called by timer 0 interrupts. This just calls
184 * hardclock(). Eventually the irqhandler can call hardclock() directly
185 * but for now we use this function so that we can debug IRQ's
186 */
187
188 int
189 clockhandler(cookie)
190 void *cookie;
191 {
192 struct clockframe *frame = cookie;
193 tickle_tc();
194
195 hardclock(frame);
196 return(0); /* Pass the interrupt on down the chain */
197 }
198
199
200 /*
201 * int statclockhandler(struct clockframe *frame)
202 *
203 * Function called by timer 1 interrupts. This just calls
204 * statclock(). Eventually the irqhandler can call statclock() directly
205 * but for now we use this function so that we can debug IRQ's
206 */
207
208 int
209 statclockhandler(cookie)
210 void *cookie;
211 {
212 struct clockframe *frame = cookie;
213
214 statclock(frame);
215 return(0); /* Pass the interrupt on down the chain */
216 }
217
218
219 /*
220 * void setstatclockrate(int newhz)
221 *
222 * Set the stat clock rate. The stat clock uses timer1
223 */
224
225 void
226 setstatclockrate(int newhz)
227 {
228 int count;
229
230 count = TIMER_FREQUENCY / newhz;
231
232 printf("Setting statclock to %dHz (%d ticks)\n", newhz, count);
233
234 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
235 IOMD_T1LOW, (count >> 0) & 0xff);
236 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
237 IOMD_T1HIGH, (count >> 8) & 0xff);
238
239 /* reload the counter */
240
241 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
242 IOMD_T1GO, 0);
243 }
244
245
246 #ifdef DIAGNOSTIC
247 static void
248 checkdelay()
249 {
250 struct timeval start, end, diff;
251
252 microtime(&start);
253 delay(10000);
254 microtime(&end);
255 timersub(&end, &start, &diff);
256 if (diff.tv_sec > 0)
257 return;
258 if (diff.tv_usec > 10000)
259 return;
260 printf("WARNING: delay(10000) took %ld us\n", diff.tv_usec);
261 }
262 #endif
263
264 /*
265 * void cpu_initclocks(void)
266 *
267 * Initialise the clocks.
268 * This sets up the two timers in the IOMD and installs the IRQ handlers
269 *
270 * NOTE: Currently only timer 0 is setup and the IRQ handler is not installed
271 */
272
273 void
274 cpu_initclocks()
275 {
276 /*
277 * Load timer 0 with count down value
278 * This timer generates 100Hz interrupts for the system clock
279 */
280
281 printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz);
282
283 timer0_count = TIMER_FREQUENCY / hz;
284
285 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
286 IOMD_T0LOW, (timer0_count >> 0) & 0xff);
287 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
288 IOMD_T0HIGH, (timer0_count >> 8) & 0xff);
289
290 /* reload the counter */
291
292 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
293 IOMD_T0GO, 0);
294
295 clockirq = intr_claim(IRQ_TIMER0, IPL_CLOCK, "tmr0 hard clk",
296 clockhandler, 0);
297
298 if (clockirq == NULL)
299 panic("%s: Cannot installer timer 0 IRQ handler",
300 clock_sc->sc_dev.dv_xname);
301
302 if (stathz) {
303 setstatclockrate(stathz);
304 statclockirq = intr_claim(IRQ_TIMER1, IPL_CLOCK,
305 "tmr1 stat clk", statclockhandler, 0);
306 if (statclockirq == NULL)
307 panic("%s: Cannot installer timer 1 IRQ handler",
308 clock_sc->sc_dev.dv_xname);
309 }
310 #ifdef DIAGNOSTIC
311 checkdelay();
312 #endif
313 tc_init(&iomd_timecounter);
314 }
315
316
317
318 static u_int iomd_timecounter0_get(struct timecounter *tc)
319 {
320 int s;
321 u_int tm;
322
323 /*
324 * Latch the current value of the timer and then read it.
325 * This garentees an atmoic reading of the time.
326 */
327 s = splhigh();
328 bus_space_write_1(clock_sc->sc_iot, clock_sc->sc_ioh,
329 IOMD_T0LATCH, 0);
330
331 tm = bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh,
332 IOMD_T0LOW);
333 tm += (bus_space_read_1(clock_sc->sc_iot, clock_sc->sc_ioh,
334 IOMD_T0HIGH) << 8);
335 splx(s);
336 simple_lock(&tmr_lock);
337
338 tm = timer0_count - tm;
339
340
341 if (timer0_count &&
342 (tm < timer0_lastcount || (!timer0_ticked && FALSE/* XXX: clkintr_pending */))) {
343 timer0_ticked = 1;
344 timer0_offset += timer0_count;
345 }
346
347 timer0_lastcount = tm;
348 tm += timer0_offset;
349
350 simple_unlock(&tmr_lock);
351 return tm;
352 }
353
354
355
356 /*
357 * Estimated loop for n microseconds
358 */
359
360 /* Need to re-write this to use the timers */
361
362 /* One day soon I will actually do this */
363
364 int delaycount = 100;
365
366 void
367 delay(n)
368 u_int n;
369 {
370 volatile u_int n2;
371 volatile u_int i;
372
373 if (n == 0) return;
374 n2 = n;
375 while (n2-- > 0) {
376 if (cputype == CPU_ID_SA110) /* XXX - Seriously gross hack */
377 for (i = delaycount; --i;);
378 else
379 for (i = 8; --i;);
380 }
381 }
382
383 todr_chip_handle_t todr_handle;
384
385 /*
386 * todr_attach:
387 *
388 * Set the specified time-of-day register as the system real-time clock.
389 */
390 void
391 todr_attach(todr_chip_handle_t todr)
392 {
393
394 if (todr_handle)
395 panic("todr_attach: rtc already configured");
396 todr_handle = todr;
397 }
398
399 /*
400 * inittodr:
401 *
402 * Initialize time from the time-of-day register.
403 */
404 #define MINYEAR 2003 /* minimum plausible year */
405 void
406 inittodr(time_t base)
407 {
408 time_t deltat;
409 int badbase;
410 int badtime;
411 struct timeval time;
412 struct timespec tc_time; /* for timecounters */
413
414 /* Default is to assume that time from somewhere will be okay.
415 * If not badtime will be set to 1 */
416 badtime = 0;
417 if (base < (MINYEAR - 1970) * SECYR) {
418 printf("WARNING: preposterous time in file system");
419 /* read the system clock anyway */
420 base = (MINYEAR - 1970) * SECYR;
421 badbase = 1;
422 } else
423 badbase = 0;
424
425 if (todr_handle == NULL ||
426 todr_gettime(todr_handle, &time) != 0 ||
427 time.tv_sec == 0) {
428 /*
429 * Believe the time in the file system for lack of
430 * anything better, resetting the TODR.
431 */
432 time.tv_sec = base;
433 time.tv_usec = 0;
434 if (todr_handle != NULL && !badbase) {
435 printf("WARNING: preposterous clock chip time\n");
436 resettodr();
437 }
438 badtime = 1;
439 }
440
441 if (!badbase) {
442 /*
443 * See if we gained/lost two or more days; if
444 * so, assume something is amiss.
445 */
446 deltat = time.tv_sec - base;
447 if (deltat < 0)
448 deltat = -deltat;
449 if (deltat >= 2 * SECDAY)
450 printf("WARNING: clock %s %ld days\n",
451 time.tv_sec < base ? "lost" : "gained",
452 (long)deltat / SECDAY);
453 }
454 /* At this point time is a time value we can initialise the system
455 * clock with */
456 tc_time.tv_sec = time.tv_sec;
457 tc_time.tv_nsec = time.tv_usec * 1000;
458 tc_setclock(&tc_time);
459 timeset = 1;
460
461 if (badtime)
462 printf("WARNING: CHECK AND RESET THE DATE!\n");
463 }
464
465 /*
466 * resettodr:
467 *
468 * Reset the time-of-day register with the current time.
469 */
470 void
471 resettodr(void)
472 {
473 struct timeval time;
474 if (!timeset)
475 return;
476
477 getmicrotime(&time);
478 if (todr_handle != NULL &&
479 todr_settime(todr_handle, &time) != 0)
480 printf("resettodr: failed to set time\n");
481 }
482
483 /* End of iomd_clock.c */
484