clock.c revision 1.39 1 /* $NetBSD: clock.c,v 1.39 2008/01/03 01:02:04 joerg Exp $ */
2
3 /*
4 * Copyright (c) 1982, 1990 The Regents of the University of California.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * the Systems Programming Group of the University of Utah Computer
9 * Science Department.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR 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 * from: Utah $Hdr: clock.c 1.18 91/01/21$
36 *
37 * @(#)clock.c 7.6 (Berkeley) 5/7/91
38 */
39 /*
40 * Copyright (c) 1988 University of Utah.
41 *
42 * This code is derived from software contributed to Berkeley by
43 * the Systems Programming Group of the University of Utah Computer
44 * Science Department.
45 *
46 * Redistribution and use in source and binary forms, with or without
47 * modification, are permitted provided that the following conditions
48 * are met:
49 * 1. Redistributions of source code must retain the above copyright
50 * notice, this list of conditions and the following disclaimer.
51 * 2. Redistributions in binary form must reproduce the above copyright
52 * notice, this list of conditions and the following disclaimer in the
53 * documentation and/or other materials provided with the distribution.
54 * 3. All advertising materials mentioning features or use of this software
55 * must display the following acknowledgement:
56 * This product includes software developed by the University of
57 * California, Berkeley and its contributors.
58 * 4. Neither the name of the University nor the names of its contributors
59 * may be used to endorse or promote products derived from this software
60 * without specific prior written permission.
61 *
62 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
63 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
64 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
65 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
66 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
67 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
68 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
69 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
70 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
71 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
72 * SUCH DAMAGE.
73 *
74 * from: Utah $Hdr: clock.c 1.18 91/01/21$
75 *
76 * @(#)clock.c 7.6 (Berkeley) 5/7/91
77 */
78
79 #include <sys/cdefs.h>
80 __KERNEL_RCSID(0, "$NetBSD: clock.c,v 1.39 2008/01/03 01:02:04 joerg Exp $");
81
82 #include <sys/param.h>
83 #include <sys/kernel.h>
84 #include <sys/systm.h>
85 #include <sys/device.h>
86 #include <sys/uio.h>
87 #include <sys/conf.h>
88 #include <sys/proc.h>
89 #include <sys/event.h>
90
91 #include <dev/clock_subr.h>
92
93 #include <machine/psl.h>
94 #include <machine/cpu.h>
95 #include <machine/iomap.h>
96 #include <machine/mfp.h>
97 #include <atari/dev/clockreg.h>
98 #include <atari/atari/device.h>
99
100 #if defined(GPROF) && defined(PROFTIMER)
101 #include <machine/profile.h>
102 #endif
103
104 /*
105 * The MFP clock runs at 2457600Hz. We use a {system,stat,prof}clock divider
106 * of 200. Therefore the timer runs at an effective rate of:
107 * 2457600/200 = 12288Hz.
108 */
109 #define CLOCK_HZ 12288
110
111 /*
112 * Machine-dependent clock routines.
113 *
114 * Inittodr initializes the time of day hardware which provides
115 * date functions.
116 *
117 * Resettodr restores the time of day hardware after a time change.
118 */
119
120 struct clock_softc {
121 struct device sc_dev;
122 int sc_flags;
123 };
124
125 /*
126 * 'sc_flags' state info. Only used by the rtc-device functions.
127 */
128 #define RTC_OPEN 1
129
130 dev_type_open(rtcopen);
131 dev_type_close(rtcclose);
132 dev_type_read(rtcread);
133 dev_type_write(rtcwrite);
134
135 static void clockattach __P((struct device *, struct device *, void *));
136 static int clockmatch __P((struct device *, struct cfdata *, void *));
137
138 CFATTACH_DECL(clock, sizeof(struct clock_softc),
139 clockmatch, clockattach, NULL, NULL);
140
141 extern struct cfdriver clock_cd;
142
143 const struct cdevsw rtc_cdevsw = {
144 rtcopen, rtcclose, rtcread, rtcwrite, noioctl,
145 nostop, notty, nopoll, nommap, nokqfilter,
146 };
147
148 void statintr __P((struct clockframe));
149
150 static u_long gettod __P((void));
151 static int twodigits __P((char *, int));
152
153 static int divisor; /* Systemclock divisor */
154
155 /*
156 * Statistics and profile clock intervals and variances. Variance must
157 * be a power of 2. Since this gives us an even number, not an odd number,
158 * we discard one case and compensate. That is, a variance of 64 would
159 * give us offsets in [0..63]. Instead, we take offsets in [1..63].
160 * This is symmetric around the point 32, or statvar/2, and thus averages
161 * to that value (assuming uniform random numbers).
162 */
163 #ifdef STATCLOCK
164 static int statvar = 32; /* {stat,prof}clock variance */
165 static int statmin; /* statclock divisor - variance/2 */
166 static int profmin; /* profclock divisor - variance/2 */
167 static int clk2min; /* current, from above choices */
168 #endif
169
170 int
171 clockmatch(pdp, cfp, auxp)
172 struct device *pdp;
173 struct cfdata *cfp;
174 void *auxp;
175 {
176 if (!atari_realconfig) {
177 /*
178 * Initialize Timer-B in the ST-MFP. This timer is used by
179 * the 'delay' function below. This timer is setup to be
180 * continueously counting from 255 back to zero at a
181 * frequency of 614400Hz. We do this *early* in the
182 * initialisation process.
183 */
184 MFP->mf_tbcr = 0; /* Stop timer */
185 MFP->mf_iera &= ~IA_TIMB; /* Disable timer interrupts */
186 MFP->mf_tbdr = 0;
187 MFP->mf_tbcr = T_Q004; /* Start timer */
188
189 /*
190 * Initialize the time structure
191 */
192 time.tv_sec = 0;
193 time.tv_usec = 0;
194
195 return 0;
196 }
197 if(!strcmp("clock", auxp))
198 return(1);
199 return(0);
200 }
201
202 /*
203 * Start the real-time clock.
204 */
205 void clockattach(pdp, dp, auxp)
206 struct device *pdp, *dp;
207 void *auxp;
208 {
209 struct clock_softc *sc = (void *)dp;
210
211 sc->sc_flags = 0;
212
213 /*
214 * Initialize Timer-A in the ST-MFP. We use a divisor of 200.
215 * The MFP clock runs at 2457600Hz. Therefore the timer runs
216 * at an effective rate of: 2457600/200 = 12288Hz. The
217 * following expression works for 48, 64 or 96 hz.
218 */
219 divisor = CLOCK_HZ/hz;
220 MFP->mf_tacr = 0; /* Stop timer */
221 MFP->mf_iera &= ~IA_TIMA; /* Disable timer interrupts */
222 MFP->mf_tadr = divisor; /* Set divisor */
223
224 if (hz != 48 && hz != 64 && hz != 96) { /* XXX */
225 printf (": illegal value %d for systemclock, reset to %d\n\t",
226 hz, 64);
227 hz = 64;
228 }
229 printf(": system hz %d timer-A divisor 200/%d\n", hz, divisor);
230
231 #ifdef STATCLOCK
232 if ((stathz == 0) || (stathz > hz) || (CLOCK_HZ % stathz))
233 stathz = hz;
234 if ((profhz == 0) || (profhz > (hz << 1)) || (CLOCK_HZ % profhz))
235 profhz = hz << 1;
236
237 MFP->mf_tcdcr &= 0x7; /* Stop timer */
238 MFP->mf_ierb &= ~IB_TIMC; /* Disable timer inter. */
239 MFP->mf_tcdr = CLOCK_HZ/stathz; /* Set divisor */
240
241 statmin = (CLOCK_HZ/stathz) - (statvar >> 1);
242 profmin = (CLOCK_HZ/profhz) - (statvar >> 1);
243 clk2min = statmin;
244 #endif /* STATCLOCK */
245
246 }
247
248 void cpu_initclocks()
249 {
250 MFP->mf_tacr = T_Q200; /* Start timer */
251 MFP->mf_ipra = (u_int8_t)~IA_TIMA;/* Clear pending interrupts */
252 MFP->mf_iera |= IA_TIMA; /* Enable timer interrupts */
253 MFP->mf_imra |= IA_TIMA; /* ..... */
254
255 #ifdef STATCLOCK
256 MFP->mf_tcdcr = (MFP->mf_tcdcr & 0x7) | (T_Q200<<4); /* Start */
257 MFP->mf_iprb = (u_int8_t)~IB_TIMC;/* Clear pending interrupts */
258 MFP->mf_ierb |= IB_TIMC; /* Enable timer interrupts */
259 MFP->mf_imrb |= IB_TIMC; /* ..... */
260 #endif /* STATCLOCK */
261 }
262
263 void
264 setstatclockrate(newhz)
265 int newhz;
266 {
267 #ifdef STATCLOCK
268 if (newhz == stathz)
269 clk2min = statmin;
270 else clk2min = profmin;
271 #endif /* STATCLOCK */
272 }
273
274 #ifdef STATCLOCK
275 void
276 statintr(frame)
277 struct clockframe frame;
278 {
279 register int var, r;
280
281 var = statvar - 1;
282 do {
283 r = random() & var;
284 } while(r == 0);
285
286 /*
287 * Note that we are always lagging behind as the new divisor
288 * value will not be loaded until the next interrupt. This
289 * shouldn't disturb the median frequency (I think ;-) ) as
290 * only the value used when switching frequencies is used
291 * twice. This shouldn't happen very often.
292 */
293 MFP->mf_tcdr = clk2min + r;
294
295 statclock(&frame);
296 }
297 #endif /* STATCLOCK */
298
299 /*
300 * Returns number of usec since last recorded clock "tick"
301 * (i.e. clock interrupt).
302 */
303 long
304 clkread()
305 {
306 u_int delta;
307 u_char ipra, tadr;
308
309 /*
310 * Note: Order is important!
311 * By reading 'ipra' before 'tadr' and caching the data, I try to avoid
312 * the situation that very low value in 'tadr' is read (== a big delta)
313 * while also acccounting for a full 'tick' because the counter went
314 * through zero during the calculations.
315 */
316 ipra = MFP->mf_ipra; tadr = MFP->mf_tadr;
317
318 delta = ((divisor - tadr) * tick) / divisor;
319 /*
320 * Account for pending clock interrupts
321 */
322 if(ipra & IA_TIMA)
323 return(delta + tick);
324 return(delta);
325 }
326
327 #define TIMB_FREQ 614400
328 #define TIMB_LIMIT 256
329
330 /*
331 * Wait "n" microseconds.
332 * Relies on MFP-Timer B counting down from TIMB_LIMIT at TIMB_FREQ Hz.
333 * Note: timer had better have been programmed before this is first used!
334 */
335 void
336 delay(unsigned int n)
337 {
338 int ticks, otick, remaining;
339
340 /*
341 * Read the counter first, so that the rest of the setup overhead is
342 * counted.
343 */
344 otick = MFP->mf_tbdr;
345
346 if (n <= UINT_MAX / TIMB_FREQ) {
347 /*
348 * For unsigned arithmetic, division can be replaced with
349 * multiplication with the inverse and a shift.
350 */
351 remaining = n * TIMB_FREQ / 1000000;
352 } else {
353 /* This is a very long delay.
354 * Being slow here doesn't matter.
355 */
356 remaining = (unsigned long long) n * TIMB_FREQ / 1000000;
357 }
358
359 while(remaining > 0) {
360 ticks = MFP->mf_tbdr;
361 if(ticks > otick)
362 remaining -= TIMB_LIMIT - (ticks - otick);
363 else
364 remaining -= otick - ticks;
365 otick = ticks;
366 }
367 }
368
369 #ifdef GPROF
370 /*
371 * profclock() is expanded in line in lev6intr() unless profiling kernel.
372 * Assumes it is called with clock interrupts blocked.
373 */
374 profclock(pc, ps)
375 void *pc;
376 int ps;
377 {
378 /*
379 * Came from user mode.
380 * If this process is being profiled record the tick.
381 */
382 if (USERMODE(ps)) {
383 if (p->p_stats.p_prof.pr_scale)
384 addupc(pc, &curproc->p_stats.p_prof, 1);
385 }
386 /*
387 * Came from kernel (supervisor) mode.
388 * If we are profiling the kernel, record the tick.
389 */
390 else if (profiling < 2) {
391 register int s = pc - s_lowpc;
392
393 if (s < s_textsize)
394 kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
395 }
396 /*
397 * Kernel profiling was on but has been disabled.
398 * Mark as no longer profiling kernel and if all profiling done,
399 * disable the clock.
400 */
401 if (profiling && (profon & PRF_KERNEL)) {
402 profon &= ~PRF_KERNEL;
403 if (profon == PRF_NONE)
404 stopprofclock();
405 }
406 }
407 #endif
408
409 /***********************************************************************
410 * Real Time Clock support *
411 ***********************************************************************/
412
413 u_int mc146818_read(rtc, regno)
414 void *rtc;
415 u_int regno;
416 {
417 ((struct rtc *)rtc)->rtc_regno = regno;
418 return(((struct rtc *)rtc)->rtc_data & 0377);
419 }
420
421 void mc146818_write(rtc, regno, value)
422 void *rtc;
423 u_int regno, value;
424 {
425 ((struct rtc *)rtc)->rtc_regno = regno;
426 ((struct rtc *)rtc)->rtc_data = value;
427 }
428
429 /*
430 * Initialize the time of day register, assuming the RTC runs in UTC.
431 * Since we've got the 'rtc' device, this functionality should be removed
432 * from the kernel. The only problem to be solved before that can happen
433 * is the possibility of init(1) providing a way (rc.boot?) to set
434 * the RTC before single-user mode is entered.
435 */
436 void
437 inittodr(base)
438 time_t base;
439 {
440 /* Battery clock does not store usec's, so forget about it. */
441 time.tv_sec = gettod();
442 time.tv_usec = 0;
443 }
444
445 /*
446 * Function turned into a No-op. Use /dev/rtc to update the RTC.
447 */
448 void
449 resettodr()
450 {
451 return;
452 }
453
454 static u_long
455 gettod()
456 {
457 int sps;
458 mc_todregs clkregs;
459 u_int regb;
460 struct clock_ymdhms dt;
461
462 sps = splhigh();
463 regb = mc146818_read(RTC, MC_REGB);
464 MC146818_GETTOD(RTC, &clkregs);
465 splx(sps);
466
467 regb &= MC_REGB_24HR|MC_REGB_BINARY;
468 if (regb != (MC_REGB_24HR|MC_REGB_BINARY)) {
469 printf("Error: Nonstandard RealTimeClock Configuration -"
470 " value ignored\n"
471 " A write to /dev/rtc will correct this.\n");
472 return(0);
473 }
474 if(clkregs[MC_SEC] > 59)
475 return(0);
476 if(clkregs[MC_MIN] > 59)
477 return(0);
478 if(clkregs[MC_HOUR] > 23)
479 return(0);
480 if(range_test(clkregs[MC_DOM], 1, 31))
481 return(0);
482 if (range_test(clkregs[MC_MONTH], 1, 12))
483 return(0);
484 if(clkregs[MC_YEAR] > 99)
485 return(0);
486
487 dt.dt_year = clkregs[MC_YEAR] + GEMSTARTOFTIME;
488 dt.dt_mon = clkregs[MC_MONTH];
489 dt.dt_day = clkregs[MC_DOM];
490 dt.dt_hour = clkregs[MC_HOUR];
491 dt.dt_min = clkregs[MC_MIN];
492 dt.dt_sec = clkregs[MC_SEC];
493
494 return(clock_ymdhms_to_secs(&dt));
495 }
496 /***********************************************************************
497 * RTC-device support *
498 ***********************************************************************/
499 int
500 rtcopen(dev, flag, mode, l)
501 dev_t dev;
502 int flag, mode;
503 struct lwp *l;
504 {
505 int unit = minor(dev);
506 struct clock_softc *sc;
507
508 if (unit >= clock_cd.cd_ndevs)
509 return ENXIO;
510 sc = clock_cd.cd_devs[unit];
511 if (!sc)
512 return ENXIO;
513 if (sc->sc_flags & RTC_OPEN)
514 return EBUSY;
515
516 sc->sc_flags = RTC_OPEN;
517 return 0;
518 }
519
520 int
521 rtcclose(dev, flag, mode, l)
522 dev_t dev;
523 int flag;
524 int mode;
525 struct lwp *l;
526 {
527 int unit = minor(dev);
528 struct clock_softc *sc = clock_cd.cd_devs[unit];
529
530 sc->sc_flags = 0;
531 return 0;
532 }
533
534 int
535 rtcread(dev, uio, flags)
536 dev_t dev;
537 struct uio *uio;
538 int flags;
539 {
540 struct clock_softc *sc;
541 mc_todregs clkregs;
542 int s, length;
543 char buffer[16];
544
545 sc = clock_cd.cd_devs[minor(dev)];
546
547 s = splhigh();
548 MC146818_GETTOD(RTC, &clkregs);
549 splx(s);
550
551 sprintf(buffer, "%4d%02d%02d%02d%02d.%02d\n",
552 clkregs[MC_YEAR] + GEMSTARTOFTIME,
553 clkregs[MC_MONTH], clkregs[MC_DOM],
554 clkregs[MC_HOUR], clkregs[MC_MIN], clkregs[MC_SEC]);
555
556 if (uio->uio_offset > strlen(buffer))
557 return 0;
558
559 length = strlen(buffer) - uio->uio_offset;
560 if (length > uio->uio_resid)
561 length = uio->uio_resid;
562
563 return(uiomove((void *)buffer, length, uio));
564 }
565
566 static int
567 twodigits(buffer, pos)
568 char *buffer;
569 int pos;
570 {
571 int result = 0;
572
573 if (buffer[pos] >= '0' && buffer[pos] <= '9')
574 result = (buffer[pos] - '0') * 10;
575 if (buffer[pos+1] >= '0' && buffer[pos+1] <= '9')
576 result += (buffer[pos+1] - '0');
577 return(result);
578 }
579
580 int
581 rtcwrite(dev, uio, flags)
582 dev_t dev;
583 struct uio *uio;
584 int flags;
585 {
586 mc_todregs clkregs;
587 int s, length, error;
588 char buffer[16];
589
590 /*
591 * We require atomic updates!
592 */
593 length = uio->uio_resid;
594 if (uio->uio_offset || (length != sizeof(buffer)
595 && length != sizeof(buffer - 1)))
596 return(EINVAL);
597
598 if ((error = uiomove((void *)buffer, sizeof(buffer), uio)))
599 return(error);
600
601 if (length == sizeof(buffer) && buffer[sizeof(buffer) - 1] != '\n')
602 return(EINVAL);
603
604 s = splclock();
605 mc146818_write(RTC, MC_REGB,
606 mc146818_read(RTC, MC_REGB) | MC_REGB_24HR | MC_REGB_BINARY);
607 MC146818_GETTOD(RTC, &clkregs);
608 splx(s);
609
610 clkregs[MC_SEC] = twodigits(buffer, 13);
611 clkregs[MC_MIN] = twodigits(buffer, 10);
612 clkregs[MC_HOUR] = twodigits(buffer, 8);
613 clkregs[MC_DOM] = twodigits(buffer, 6);
614 clkregs[MC_MONTH] = twodigits(buffer, 4);
615 s = twodigits(buffer, 0) * 100 + twodigits(buffer, 2);
616 clkregs[MC_YEAR] = s - GEMSTARTOFTIME;
617
618 s = splclock();
619 MC146818_PUTTOD(RTC, &clkregs);
620 splx(s);
621
622 return(0);
623 }
624