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