clock.c revision 1.1 1 /* $NetBSD: clock.c,v 1.1 1995/03/26 07:12:13 leo Exp $ */
2
3 /*
4 * Copyright (c) 1988 University of Utah.
5 * Copyright (c) 1982, 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * the Systems Programming Group of the University of Utah Computer
10 * Science Department.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 * must display the following acknowledgement:
22 * This product includes software developed by the University of
23 * California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 *
40 * from: Utah $Hdr: clock.c 1.18 91/01/21$
41 *
42 * @(#)clock.c 7.6 (Berkeley) 5/7/91
43 */
44
45 #include <sys/param.h>
46 #include <sys/kernel.h>
47 #include <sys/device.h>
48 #include <machine/psl.h>
49 #include <machine/cpu.h>
50 #include <machine/iomap.h>
51 #include <machine/mfp.h>
52 #include <atari/dev/clockreg.h>
53
54 #if defined(PROF) && defined(PROFTIMER)
55 #include <sys/PROF.h>
56 #endif
57
58
59 /*
60 * Machine-dependent clock routines.
61 *
62 * Startrtclock restarts the real-time clock, which provides
63 * hardclock interrupts to kern_clock.c.
64 *
65 * Inittodr initializes the time of day hardware which provides
66 * date functions.
67 *
68 * Resettodr restores the time of day hardware after a time change.
69 *
70 * A note on the real-time clock:
71 * We actually load the clock with CLK_INTERVAL-1 instead of CLK_INTERVAL.
72 * This is because the counter decrements to zero after N+1 enabled clock
73 * periods where N is the value loaded into the counter.
74 */
75
76 int clockmatch __P((struct device *, struct cfdata *, void *));
77 void clockattach __P((struct device *, struct device *, void *));
78
79 struct cfdriver clockcd = {
80 NULL, "clock", (cfmatch_t)clockmatch, clockattach,
81 DV_DULL, sizeof(struct device), NULL, 0
82 };
83
84 static u_long gettod __P((void));
85 static int settod __P((u_long));
86
87 static int divisor;
88
89 int
90 clockmatch(pdp, cfp, auxp)
91 struct device *pdp;
92 struct cfdata *cfp;
93 void *auxp;
94 {
95 if(!strcmp("clock", auxp))
96 return(1);
97 return(0);
98 }
99
100 /*
101 * Start the real-time clock.
102 */
103 void clockattach(pdp, dp, auxp)
104 struct device *pdp, *dp;
105 void *auxp;
106 {
107 /*
108 * Initialize Timer-A in the TT-MFP. An exact reduce to HZ is not
109 * possible by hardware. We use a divisor of 64 and reduce by software
110 * with a factor of 4. The MFP clock runs at 2457600Hz. Therefore the
111 * timer runs at an effective rate of: 2457600/(64*4) = 9600Hz. The
112 * following expression works for all 'normal' values of hz.
113 */
114 divisor = 9600/hz;
115 MFP2->mf_tacr = 0; /* Stop timer */
116 MFP2->mf_iera &= ~IA_TIMA2; /* Disable timer interrupts */
117 MFP2->mf_tadr = divisor; /* Set divisor */
118
119 printf(": system hz %d timer-A divisor %d\n", hz, divisor);
120
121 /*
122 * Initialize Timer-B in the TT-MFP. This timer is used by the 'delay'
123 * function below. This time is setup to be continueously counting from
124 * 255 back to zero at a frequency of 614400Hz.
125 */
126 MFP2->mf_tbcr = 0; /* Stop timer */
127 MFP2->mf_iera &= ~IA_TIMB2; /* Disable timer interrupts */
128 MFP2->mf_tbdr = 0;
129 MFP2->mf_tbcr = T_Q004; /* Start timer */
130
131 }
132
133 void cpu_initclocks()
134 {
135 MFP2->mf_tacr = T_Q064; /* Start timer */
136 MFP2->mf_ipra &= ~IA_TIMA2; /* Clear pending interrupts */
137 MFP2->mf_iera |= IA_TIMA2; /* Enable timer interrupts */
138 MFP2->mf_imra |= IA_TIMA2; /* ..... */
139 }
140
141 setstatclockrate(hz)
142 int hz;
143 {
144 }
145
146 /*
147 * Returns number of usec since last recorded clock "tick"
148 * (i.e. clock interrupt).
149 */
150 clkread()
151 {
152 extern short clk_div;
153 u_int delta, elapsed;
154
155 elapsed = (divisor - MFP2->mf_tadr) + ((4 - clk_div) * divisor);
156 delta = (elapsed * tick) / (divisor << 2);
157
158 /*
159 * Account for pending clock interrupts
160 */
161 if(MFP2->mf_iera & IA_TIMA2)
162 return(delta + tick);
163 return(delta);
164 }
165
166 #define TIMB2_FREQ 614400
167 #define TIMB2_LIMIT 256
168
169 /*
170 * Wait "n" microseconds.
171 * Relies on MFP2-Timer B counting down from TIMB2_LIMIT at TIMB2_FREQ Hz.
172 * Note: timer had better have been programmed before this is first used!
173 */
174 void delay(n)
175 int n;
176 {
177 int tick, otick;
178
179 /*
180 * Read the counter first, so that the rest of the setup overhead is
181 * counted.
182 */
183 otick = MFP2->mf_tbdr;
184
185 /*
186 * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
187 * we can take advantage of the intermediate 64-bit quantity to prevent
188 * loss of significance.
189 */
190 n -= 5;
191 if(n < 0)
192 return;
193 {
194 u_int temp;
195
196 __asm __volatile ("mulul %2,%1:%0" : "=d" (n), "=d" (temp)
197 : "d" (TIMB2_FREQ));
198 __asm __volatile ("divul %1,%2:%0" : "=d" (n)
199 : "d"(1000000),"d"(temp),"0"(n));
200 }
201
202 while(n > 0) {
203 tick = MFP2->mf_tbdr;
204 if(tick > otick)
205 n -= TIMB2_LIMIT - (tick - otick);
206 else n -= otick - tick;
207 otick = tick;
208 }
209 }
210
211 #ifdef notyet
212 /*
213 * Needs to be calibrated for use, its way off most of the time
214 */
215 void
216 DELAY(mic)
217 int mic;
218 {
219 u_long n;
220 short hpos;
221
222 /*
223 * this function uses HSync pulses as base units. The custom chips
224 * display only deals with 31.6kHz/2 refresh, this gives us a
225 * resolution of 1/15800 s, which is ~63us (add some fuzz so we really
226 * wait awhile, even if using small timeouts)
227 */
228 n = mic/63 + 2;
229 do {
230 hpos = custom.vhposr & 0xff00;
231 while (hpos == (custom.vhposr & 0xff00))
232 ;
233 } while (n--);
234 }
235 #endif /* notyet */
236
237 #if notyet
238
239 /* implement this later. I'd suggest using both timers in CIA-A, they're
240 not yet used. */
241
242 #include "clock.h"
243 #if NCLOCK > 0
244 /*
245 * /dev/clock: mappable high resolution timer.
246 *
247 * This code implements a 32-bit recycling counter (with a 4 usec period)
248 * using timers 2 & 3 on the 6840 clock chip. The counter can be mapped
249 * RO into a user's address space to achieve low overhead (no system calls),
250 * high-precision timing.
251 *
252 * Note that timer 3 is also used for the high precision profiling timer
253 * (PROFTIMER code above). Care should be taken when both uses are
254 * configured as only a token effort is made to avoid conflicting use.
255 */
256 #include <sys/proc.h>
257 #include <sys/resourcevar.h>
258 #include <sys/ioctl.h>
259 #include <sys/malloc.h>
260 #include <vm/vm.h>
261 #include <amiga/amiga/clockioctl.h>
262 #include <sys/specdev.h>
263 #include <sys/vnode.h>
264 #include <sys/mman.h>
265
266 int clockon = 0; /* non-zero if high-res timer enabled */
267 #ifdef PROFTIMER
268 int profprocs = 0; /* # of procs using profiling timer */
269 #endif
270 #ifdef DEBUG
271 int clockdebug = 0;
272 #endif
273
274 /*ARGSUSED*/
275 clockopen(dev, flags)
276 dev_t dev;
277 {
278 #ifdef PROFTIMER
279 #ifdef PROF
280 /*
281 * Kernel profiling enabled, give up.
282 */
283 if (profiling)
284 return(EBUSY);
285 #endif
286 /*
287 * If any user processes are profiling, give up.
288 */
289 if (profprocs)
290 return(EBUSY);
291 #endif
292 if (!clockon) {
293 startclock();
294 clockon++;
295 }
296 return(0);
297 }
298
299 /*ARGSUSED*/
300 clockclose(dev, flags)
301 dev_t dev;
302 {
303 (void) clockunmmap(dev, (caddr_t)0, curproc); /* XXX */
304 stopclock();
305 clockon = 0;
306 return(0);
307 }
308
309 /*ARGSUSED*/
310 clockioctl(dev, cmd, data, flag, p)
311 dev_t dev;
312 u_long cmd;
313 caddr_t data;
314 struct proc *p;
315 {
316 int error = 0;
317
318 switch (cmd) {
319
320 case CLOCKMAP:
321 error = clockmmap(dev, (caddr_t *)data, p);
322 break;
323
324 case CLOCKUNMAP:
325 error = clockunmmap(dev, *(caddr_t *)data, p);
326 break;
327
328 case CLOCKGETRES:
329 *(int *)data = CLK_RESOLUTION;
330 break;
331
332 default:
333 error = EINVAL;
334 break;
335 }
336 return(error);
337 }
338
339 /*ARGSUSED*/
340 clockmap(dev, off, prot)
341 dev_t dev;
342 {
343 return((off + (INTIOBASE+CLKBASE+CLKSR-1)) >> PGSHIFT);
344 }
345
346 clockmmap(dev, addrp, p)
347 dev_t dev;
348 caddr_t *addrp;
349 struct proc *p;
350 {
351 int error;
352 struct vnode vn;
353 struct specinfo si;
354 int flags;
355
356 flags = MAP_FILE|MAP_SHARED;
357 if (*addrp)
358 flags |= MAP_FIXED;
359 else
360 *addrp = (caddr_t)0x1000000; /* XXX */
361 vn.v_type = VCHR; /* XXX */
362 vn.v_specinfo = &si; /* XXX */
363 vn.v_rdev = dev; /* XXX */
364 error = vm_mmap(&p->p_vmspace->vm_map, (vm_offset_t *)addrp,
365 PAGE_SIZE, VM_PROT_ALL, flags, (caddr_t)&vn, 0);
366 return(error);
367 }
368
369 clockunmmap(dev, addr, p)
370 dev_t dev;
371 caddr_t addr;
372 struct proc *p;
373 {
374 int rv;
375
376 if (addr == 0)
377 return(EINVAL); /* XXX: how do we deal with this? */
378 rv = vm_deallocate(p->p_vmspace->vm_map, (vm_offset_t)addr, PAGE_SIZE);
379 return(rv == KERN_SUCCESS ? 0 : EINVAL);
380 }
381
382 startclock()
383 {
384 register struct clkreg *clk = (struct clkreg *)clkstd[0];
385
386 clk->clk_msb2 = -1; clk->clk_lsb2 = -1;
387 clk->clk_msb3 = -1; clk->clk_lsb3 = -1;
388
389 clk->clk_cr2 = CLK_CR3;
390 clk->clk_cr3 = CLK_OENAB|CLK_8BIT;
391 clk->clk_cr2 = CLK_CR1;
392 clk->clk_cr1 = CLK_IENAB;
393 }
394
395 stopclock()
396 {
397 register struct clkreg *clk = (struct clkreg *)clkstd[0];
398
399 clk->clk_cr2 = CLK_CR3;
400 clk->clk_cr3 = 0;
401 clk->clk_cr2 = CLK_CR1;
402 clk->clk_cr1 = CLK_IENAB;
403 }
404 #endif
405
406 #endif
407
408
409 #ifdef PROFTIMER
410 /*
411 * This code allows the amiga kernel to use one of the extra timers on
412 * the clock chip for profiling, instead of the regular system timer.
413 * The advantage of this is that the profiling timer can be turned up to
414 * a higher interrupt rate, giving finer resolution timing. The profclock
415 * routine is called from the lev6intr in locore, and is a specialized
416 * routine that calls addupc. The overhead then is far less than if
417 * hardclock/softclock was called. Further, the context switch code in
418 * locore has been changed to turn the profile clock on/off when switching
419 * into/out of a process that is profiling (startprofclock/stopprofclock).
420 * This reduces the impact of the profiling clock on other users, and might
421 * possibly increase the accuracy of the profiling.
422 */
423 int profint = PRF_INTERVAL; /* Clock ticks between interrupts */
424 int profscale = 0; /* Scale factor from sys clock to prof clock */
425 char profon = 0; /* Is profiling clock on? */
426
427 /* profon values - do not change, locore.s assumes these values */
428 #define PRF_NONE 0x00
429 #define PRF_USER 0x01
430 #define PRF_KERNEL 0x80
431
432 initprofclock()
433 {
434 #if NCLOCK > 0
435 struct proc *p = curproc; /* XXX */
436
437 /*
438 * If the high-res timer is running, force profiling off.
439 * Unfortunately, this gets reflected back to the user not as
440 * an error but as a lack of results.
441 */
442 if (clockon) {
443 p->p_stats->p_prof.pr_scale = 0;
444 return;
445 }
446 /*
447 * Keep track of the number of user processes that are profiling
448 * by checking the scale value.
449 *
450 * XXX: this all assumes that the profiling code is well behaved;
451 * i.e. profil() is called once per process with pcscale non-zero
452 * to turn it on, and once with pcscale zero to turn it off.
453 * Also assumes you don't do any forks or execs. Oh well, there
454 * is always adb...
455 */
456 if (p->p_stats->p_prof.pr_scale)
457 profprocs++;
458 else
459 profprocs--;
460 #endif
461 /*
462 * The profile interrupt interval must be an even divisor
463 * of the CLK_INTERVAL so that scaling from a system clock
464 * tick to a profile clock tick is possible using integer math.
465 */
466 if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0)
467 profint = CLK_INTERVAL;
468 profscale = CLK_INTERVAL / profint;
469 }
470
471 startprofclock()
472 {
473 unsigned short interval;
474
475 /* stop timer B */
476 ciab.crb = ciab.crb & 0xc0;
477
478 /* load interval into registers.
479 the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz */
480
481 interval = profint - 1;
482
483 /* order of setting is important ! */
484 ciab.tblo = interval & 0xff;
485 ciab.tbhi = interval >> 8;
486
487 /* enable interrupts for timer B */
488 ciab.icr = (1<<7) | (1<<1);
489
490 /* start timer B in continuous shot mode */
491 ciab.crb = (ciab.crb & 0xc0) | 1;
492 }
493
494 stopprofclock()
495 {
496 /* stop timer B */
497 ciab.crb = ciab.crb & 0xc0;
498 }
499
500 #ifdef PROF
501 /*
502 * profclock() is expanded in line in lev6intr() unless profiling kernel.
503 * Assumes it is called with clock interrupts blocked.
504 */
505 profclock(pc, ps)
506 caddr_t pc;
507 int ps;
508 {
509 /*
510 * Came from user mode.
511 * If this process is being profiled record the tick.
512 */
513 if (USERMODE(ps)) {
514 if (p->p_stats.p_prof.pr_scale)
515 addupc(pc, &curproc->p_stats.p_prof, 1);
516 }
517 /*
518 * Came from kernel (supervisor) mode.
519 * If we are profiling the kernel, record the tick.
520 */
521 else if (profiling < 2) {
522 register int s = pc - s_lowpc;
523
524 if (s < s_textsize)
525 kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
526 }
527 /*
528 * Kernel profiling was on but has been disabled.
529 * Mark as no longer profiling kernel and if all profiling done,
530 * disable the clock.
531 */
532 if (profiling && (profon & PRF_KERNEL)) {
533 profon &= ~PRF_KERNEL;
534 if (profon == PRF_NONE)
535 stopprofclock();
536 }
537 }
538 #endif
539 #endif
540
541 /*
542 * Initialize the time of day register, based on the time base which is, e.g.
543 * from a filesystem.
544 */
545 inittodr(base)
546 time_t base;
547 {
548 u_long timbuf = base; /* assume no battery clock exists */
549
550 timbuf = gettod();
551
552 if(timbuf < base) {
553 printf("WARNING: bad date in battery clock\n");
554 timbuf = base;
555 }
556
557 /* Battery clock does not store usec's, so forget about it. */
558 time.tv_sec = timbuf;
559 }
560
561 resettodr()
562 {
563 if(settod(time.tv_sec) == 1)
564 return;
565 printf("Cannot set battery backed clock\n");
566 }
567
568 static char dmsize[12] =
569 {
570 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
571 };
572
573 static char ldmsize[12] =
574 {
575 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
576 };
577
578 static __inline__ int rtc_getclkreg(regno)
579 int regno;
580 {
581 RTC->rtc_regno = RTC_REGA;
582 RTC->rtc_regno = regno;
583 return(RTC->rtc_data & 0377);
584 }
585
586 static __inline__ void rtc_setclkreg(regno, value)
587 int regno, value;
588 {
589 RTC->rtc_regno = regno;
590 RTC->rtc_data = value;
591 }
592
593 static u_long
594 gettod()
595 {
596 int i, year, mon, day, hour, min, sec;
597 u_long new_time = 0;
598 char *msize;
599
600 /*
601 * Hold clock
602 */
603 rtc_setclkreg(RTC_REGB, rtc_getclkreg(RTC_REGB) | RTC_B_SET);
604
605 /*
606 * Read clock
607 */
608 sec = rtc_getclkreg(RTC_SEC);
609 min = rtc_getclkreg(RTC_MIN);
610 hour = rtc_getclkreg(RTC_HOUR);
611 day = rtc_getclkreg(RTC_DAY) - 1;
612 mon = rtc_getclkreg(RTC_MONTH) - 1;
613 year = rtc_getclkreg(RTC_YEAR) + STARTOFTIME;
614
615 /*
616 * Let it run again..
617 */
618 rtc_setclkreg(RTC_REGB, rtc_getclkreg(RTC_REGB) & ~RTC_B_SET);
619
620 if(range_test(hour, 0, 23))
621 return(0);
622 if(range_test(day, 0, 30))
623 return(0);
624 if (range_test(mon, 0, 11))
625 return(0);
626 if(range_test(year, STARTOFTIME, 2000))
627 return(0);
628
629 for(i = STARTOFTIME; i < year; i++) {
630 if(is_leap(i))
631 new_time += 366;
632 else new_time += 365;
633 }
634
635 msize = is_leap(year) ? ldmsize : dmsize;
636 for(i = 0; i < mon; i++)
637 new_time += msize[i];
638 new_time += day;
639 return((new_time * SECS_DAY) + (hour * 3600) + (min * 60) + sec);
640 }
641
642 static int
643 settod(newtime)
644 u_long newtime;
645 {
646 register long days, rem, year;
647 register char *ml;
648 int sec, min, hour, month;
649
650 /* Number of days since Jan. 1 1970 */
651 days = newtime / SECS_DAY;
652 rem = newtime % SECS_DAY;
653
654 /*
655 * Calculate sec, min, hour
656 */
657 hour = rem / SECS_HOUR;
658 rem %= SECS_HOUR;
659 min = rem / 60;
660 sec = rem % 60;
661
662 /*
663 * Figure out the year. Day in year is left in 'days'.
664 */
665 year = STARTOFTIME;
666 while(days >= (rem = is_leap(year) ? 366 : 365)) {
667 ++year;
668 days -= rem;
669 }
670 while(days < 0) {
671 --year;
672 days += is_leap(year) ? 366 : 365;
673 }
674
675 /*
676 * Determine the month
677 */
678 ml = is_leap(year) ? ldmsize : dmsize;
679 for(month = 0; days >= ml[month]; ++month)
680 days -= ml[month];
681
682 /*
683 * Now that everything is calculated, program the RTC
684 */
685 rtc_setclkreg(RTC_REGB, RTC_B_SET);
686 rtc_setclkreg(RTC_REGA, RTC_A_DV1|RTC_A_RS2|RTC_A_RS3);
687 rtc_setclkreg(RTC_REGB, RTC_B_SET|RTC_B_SQWE|RTC_B_DM|RTC_B_24_12);
688 rtc_setclkreg(RTC_SEC, sec);
689 rtc_setclkreg(RTC_MIN, min);
690 rtc_setclkreg(RTC_HOUR, hour);
691 rtc_setclkreg(RTC_DAY, days+1);
692 rtc_setclkreg(RTC_MONTH, month+1);
693 rtc_setclkreg(RTC_YEAR, year-1970);
694 rtc_setclkreg(RTC_REGB, RTC_B_SQWE|RTC_B_DM|RTC_B_24_12);
695
696 return(1);
697 }
698