BSD 4_4_Lite1 release
[unix-history] / usr / src / sys / hp300 / hp300 / clock.c
index b2a9579..1247837 100644 (file)
@@ -1,34 +1,82 @@
 /*
  * Copyright (c) 1988 University of Utah.
 /*
  * Copyright (c) 1988 University of Utah.
- * Copyright (c) 1982, 1990 The Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1982, 1990, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * the Systems Programming Group of the University of Utah Computer
  * Science Department.
  *
  *
  * This code is derived from software contributed to Berkeley by
  * the Systems Programming Group of the University of Utah Computer
  * Science Department.
  *
- * %sccs.include.redist.c%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
  *
  *
- * from: Utah $Hdr: clock.c 1.17 89/11/30$
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  *
  *
- *     @(#)clock.c     7.4 (Berkeley) %G%
+ * from: Utah $Hdr: clock.c 1.18 91/01/21$
+ *
+ *     @(#)clock.c     8.2 (Berkeley) 1/12/94
+ */
+
+/*
+ * HPs use the MC6840 PTM with the following arrangement:
+ *     Timers 1 and 3 are externally driver from a 25Mhz source.
+ *     Output from timer 3 is tied to the input of timer 2.
+ * The latter makes it possible to use timers 3 and 2 together to get
+ * a 32-bit countdown timer.
  */
 
  */
 
-#include "sys/param.h"
-#include "sys/user.h"
-#include "sys/kernel.h"
-#include "../dev/hilreg.h"
-#include "clockreg.h"
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <hp/dev/hilreg.h>
+#include <hp300/hp300/clockreg.h>
 
 
-#include "../include/psl.h"
-#include "../include/cpu.h"
+#include <machine/psl.h>
+#include <machine/cpu.h>
 
 
-#if defined(GPROF) && defined(PROFTIMER)
-#include "sys/gprof.h"
+#ifdef GPROF
+#include <sys/gmon.h>
 #endif
 
 int    clkstd[1];
 
 #endif
 
 int    clkstd[1];
 
+static int clkint;             /* clock interval, as loaded */
+/*
+ * Statistics clock interval and variance, in usec.  Variance must be a
+ * power of two.  Since this gives us an even number, not an odd number,
+ * we discard one case and compensate.  That is, a variance of 1024 would
+ * give us offsets in [0..1023].  Instead, we take offsets in [1..1023].
+ * This is symmetric about the point 512, or statvar/2, and thus averages
+ * to that value (assuming uniform random numbers).
+ */
+static int statvar = 1024 / 4; /* {stat,prof}clock variance */
+static int statmin;            /* statclock interval - variance/2 */
+static int profmin;            /* profclock interval - variance/2 */
+static int timer3min;          /* current, from above choices */
+static int statprev;           /* previous value in stat timer */
+
 static int month_days[12] = {
        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
 };
 static int month_days[12] = {
        31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
 };
@@ -40,350 +88,172 @@ struct hil_dev *bbcaddr = NULL;
 /*
  * Machine-dependent clock routines.
  *
 /*
  * Machine-dependent clock routines.
  *
- * Startrtclock restarts the real-time clock, which provides
- * hardclock interrupts to kern_clock.c.
- *
- * Inittodr initializes the time of day hardware which provides
- * date functions.
- *
- * Resettodr restores the time of day hardware after a time change.
- *
  * A note on the real-time clock:
  * A note on the real-time clock:
- * We actually load the clock with CLK_INTERVAL-1 instead of CLK_INTERVAL.
+ * We actually load the clock with interval-1 instead of interval.
  * This is because the counter decrements to zero after N+1 enabled clock
  * periods where N is the value loaded into the counter.
  * This is because the counter decrements to zero after N+1 enabled clock
  * periods where N is the value loaded into the counter.
+ *
+ * The frequencies of the HP300 clocks must be a multiple of four
+ * microseconds (since the clock counts in 4 us units).
  */
  */
+#define        COUNTS_PER_SEC  (1000000 / CLK_RESOLUTION)
 
 /*
 
 /*
- * Start the real-time clock.
- */
-startrtclock()
-{
-       register struct clkreg *clk;
-
-       clkstd[0] = IOV(0x5F8000);
-       clk = (struct clkreg *) clkstd[0];
-
-       clk->clk_cr2 = CLK_CR1;
-       clk->clk_cr1 = CLK_RESET;
-       clk->clk_cr2 = CLK_CR3;
-       clk->clk_cr3 = 0;
-       clk->clk_msb1 = (CLK_INTERVAL-1) >> 8 & 0xFF;
-       clk->clk_lsb1 = (CLK_INTERVAL-1) & 0xFF;
-       clk->clk_msb2 = 0;
-       clk->clk_lsb2 = 0;
-       clk->clk_msb3 = 0;
-       clk->clk_lsb3 = 0;
-       clk->clk_cr2 = CLK_CR1;
-       clk->clk_cr1 = CLK_IENAB;
-}
-
-/*
- * Returns number of usec since last recorded clock "tick"
- * (i.e. clock interrupt).
+ * Set up the real-time and statistics clocks.  Leave stathz 0 only if
+ * no alternative timer is available.
+ *
  */
  */
-clkread()
+cpu_initclocks()
 {
 {
-       register struct clkreg *clk = (struct clkreg *) clkstd[0];
-       register int high, low;
-
-       high = clk->clk_msb1;
-       low = clk->clk_lsb1;
-       if (high != clk->clk_msb1)
-               high = clk->clk_msb1;
-
-       high = (CLK_INTERVAL-1) - ((high << 8) | low);
-       /*
-        * Pending interrupt indicates that the counter has wrapped
-        * since we went to splhigh().  Need to compensate.
-        */
-       if (clk->clk_sr & CLK_INT1)
-               high += CLK_INTERVAL;
-       return((high * tick) / CLK_INTERVAL);
-}
+       register volatile struct clkreg *clk;
+       register int intvl, statint, profint, minint;
 
 
-#include "clock.h"
-#if NCLOCK > 0
-/*
- * /dev/clock: mappable high resolution timer.
- *
- * This code implements a 32-bit recycling counter (with a 4 usec period)
- * using timers 2 & 3 on the 6840 clock chip.  The counter can be mapped
- * RO into a user's address space to achieve low overhead (no system calls),
- * high-precision timing.
- *
- * Note that timer 3 is also used for the high precision profiling timer
- * (PROFTIMER code above).  Care should be taken when both uses are
- * configured as only a token effort is made to avoid conflicting use.
- */
-#include "sys/proc.h"
-#include "sys/ioctl.h"
-#include "sys/malloc.h"
-#include "clockioctl.h"
-#include "vm/vm_param.h"
-#include "vm/vm_pager.h"
-#include "vm/vm_prot.h"
-#include "sys/specdev.h"
-#include "sys/vnode.h"
-#include "sys/mman.h"
-
-int clockon = 0;               /* non-zero if high-res timer enabled */
-#ifdef PROFTIMER
-int  profprocs = 0;            /* # of procs using profiling timer */
-#endif
-#ifdef DEBUG
-int clockdebug = 0;
-#endif
+       clkstd[0] = IIOV(0x5F8000);             /* XXX grot */
+       clk = (volatile struct clkreg *)clkstd[0];
 
 
-/*ARGSUSED*/
-clockopen(dev, flags)
-       dev_t dev;
-{
-#ifdef PROFTIMER
-#ifdef GPROF
-       /*
-        * Kernel profiling enabled, give up.
-        */
-       if (profiling)
-               return(EBUSY);
-#endif
+       if (COUNTS_PER_SEC % hz) {
+               printf("cannot get %d Hz clock; using 100 Hz\n", hz);
+               hz = 100;
+       }
        /*
        /*
-        * If any user processes are profiling, give up.
+        * Clock has several counters, so we can always use separate
+        * statclock.
         */
         */
-       if (profprocs)
-               return(EBUSY);
-#endif
-       if (!clockon) {
-               startclock();
-               clockon++;
+       if (stathz == 0)                /* XXX should be set in param.c */
+               stathz = hz;
+       else if (COUNTS_PER_SEC % stathz) {
+               printf("cannot get %d Hz statclock; using 100 Hz\n", stathz);
+               stathz = 100;
        }
        }
-       return(0);
-}
-
-/*ARGSUSED*/
-clockclose(dev, flags)
-       dev_t dev;
-{
-       (void) clockunmmap(dev, (caddr_t)0);
-       stopclock();
-       clockon = 0;
-       return(0);
-}
-
-/*ARGSUSED*/
-clockioctl(dev, cmd, data, flag)
-       dev_t dev;
-       caddr_t data;
-{
-       int error = 0;
-       
-       switch (cmd) {
-
-       case CLOCKMAP:
-               error = clockmmap(dev, (caddr_t *)data);
-               break;
-
-       case CLOCKUNMAP:
-               error = clockunmmap(dev, *(caddr_t *)data);
-               break;
-
-       case CLOCKGETRES:
-               *(int *)data = CLK_RESOLUTION;
-               break;
-
-       default:
-               error = EINVAL;
-               break;
+       if (profhz == 0)                /* XXX should be set in param.c */
+               profhz = stathz * 5;
+       else if (profhz < stathz || COUNTS_PER_SEC % profhz) {
+               printf("cannot get %d Hz profclock; using %d Hz\n",
+                   profhz, stathz);
+               profhz = stathz;
        }
        }
-       return(error);
-}
-
-/*ARGSUSED*/
-clockmap(dev, off, prot)
-       dev_t dev;
-{
-       return((off + (IOBASE+CLKBASE+CLKSR-1)) >> PGSHIFT);
-}
 
 
-clockmmap(dev, addrp)
-       dev_t dev;
-       caddr_t *addrp;
-{
-       struct proc *p = u.u_procp;             /* XXX */
-       int error;
-       struct vnode vn;
-       struct specinfo si;
-       int flags;
-
-       flags = MAP_FILE|MAP_SHARED;
-       if (*addrp)
-               flags |= MAP_FIXED;
-       else
-               *addrp = (caddr_t)0x1000000;    /* XXX */
-       vn.v_type = VCHR;                       /* XXX */
-       vn.v_specinfo = &si;                    /* XXX */
-       vn.v_rdev = dev;                        /* XXX */
-       error = vm_mmap(u.u_procp->p_map, (vm_offset_t *)addrp,
-                       PAGE_SIZE, VM_PROT_ALL, flags, (caddr_t)&vn, 0);
-       return(error);
-}
-
-clockunmmap(dev, addr)
-       dev_t dev;
-       caddr_t addr;
-{
-       struct proc *p = u.u_procp;             /* XXX */
-       int rv;
+       intvl = COUNTS_PER_SEC / hz;
+       statint = COUNTS_PER_SEC / stathz;
+       profint = COUNTS_PER_SEC / profhz;
+       minint = statint / 2 + 100;
+       while (statvar > minint)
+               statvar >>= 1;
 
 
-       if (addr == 0)
-               return(EINVAL);         /* XXX: how do we deal with this? */
-       rv = vm_deallocate(u.u_procp->p_map, (vm_offset_t)addr, PAGE_SIZE);
-       return(rv == KERN_SUCCESS ? 0 : EINVAL);
-}
+       tick = intvl * CLK_RESOLUTION;
 
 
-startclock()
-{
-       register struct clkreg *clk = (struct clkreg *)clkstd[0];
+       /* adjust interval counts, per note above */
+       intvl--;
+       statint--;
+       profint--;
 
 
-       clk->clk_msb2 = -1; clk->clk_lsb2 = -1;
-       clk->clk_msb3 = -1; clk->clk_lsb3 = -1;
+       /* calculate base reload values */
+       clkint = intvl;
+       statmin = statint - (statvar >> 1);
+       profmin = profint - (statvar >> 1);
+       timer3min = statmin;
+       statprev = statint;
 
 
-       clk->clk_cr2 = CLK_CR3;
-       clk->clk_cr3 = CLK_OENAB|CLK_8BIT;
+       /* finally, load hardware */
        clk->clk_cr2 = CLK_CR1;
        clk->clk_cr2 = CLK_CR1;
-       clk->clk_cr1 = CLK_IENAB;
-}
-
-stopclock()
-{
-       register struct clkreg *clk = (struct clkreg *)clkstd[0];
-
-       clk->clk_cr2 = CLK_CR3;
-       clk->clk_cr3 = 0;
+       clk->clk_cr1 = CLK_RESET;
+       asm volatile(" movpw %0,%1@(5)" : : "d" (intvl), "a" (clk));
+       asm volatile(" movpw %0,%1@(9)" : : "d" (0), "a" (clk));
+       asm volatile(" movpw %0,%1@(13)" : : "d" (statint), "a" (clk));
        clk->clk_cr2 = CLK_CR1;
        clk->clk_cr1 = CLK_IENAB;
        clk->clk_cr2 = CLK_CR1;
        clk->clk_cr1 = CLK_IENAB;
+       clk->clk_cr2 = CLK_CR3;
+       clk->clk_cr3 = CLK_IENAB;
 }
 }
-#endif
 
 
-#ifdef PROFTIMER
 /*
 /*
- * This code allows the hp300 kernel to use one of the extra timers on
- * the clock chip for profiling, instead of the regular system timer.
- * The advantage of this is that the profiling timer can be turned up to
- * a higher interrupt rate, giving finer resolution timing. The profclock
- * routine is called from the lev6intr in locore, and is a specialized
- * routine that calls addupc. The overhead then is far less than if
- * hardclock/softclock was called. Further, the context switch code in
- * locore has been changed to turn the profile clock on/off when switching
- * into/out of a process that is profiling (startprofclock/stopprofclock).
- * This reduces the impact of the profiling clock on other users, and might
- * possibly increase the accuracy of the profiling. 
+ * We assume newhz is either stathz or profhz, and that neither will
+ * change after being set up above.  Could recalculate intervals here
+ * but that would be a drag.
  */
  */
-int  profint   = PRF_INTERVAL; /* Clock ticks between interrupts */
-int  profscale = 0;            /* Scale factor from sys clock to prof clock */
-char profon    = 0;            /* Is profiling clock on? */
-
-/* profon values - do not change, locore.s assumes these values */
-#define PRF_NONE       0x00
-#define        PRF_USER        0x01
-#define        PRF_KERNEL      0x80
-
-initprofclock()
+void
+setstatclockrate(newhz)
+       int newhz;
 {
 {
-#if NCLOCK > 0
-       /*
-        * If the high-res timer is running, force profiling off.
-        * Unfortunately, this gets reflected back to the user not as
-        * an error but as a lack of results.
-        */
-       if (clockon) {
-               u.u_prof.pr_scale = 0;
-               return;
-       }
-       /*
-        * Keep track of the number of user processes that are profiling
-        * by checking the scale value.
-        *
-        * XXX: this all assumes that the profiling code is well behaved;
-        * i.e. profil() is called once per process with pcscale non-zero
-        * to turn it on, and once with pcscale zero to turn it off.
-        * Also assumes you don't do any forks or execs.  Oh well, there
-        * is always adb...
-        */
-       if (u.u_prof.pr_scale)
-               profprocs++;
+
+       if (newhz == stathz)
+               timer3min = statmin;
        else
        else
-               profprocs--;
-#endif
-       /*
-        * The profile interrupt interval must be an even divisor
-        * of the CLK_INTERVAL so that scaling from a system clock
-        * tick to a profile clock tick is possible using integer math.
-        */
-       if (profint > CLK_INTERVAL || (CLK_INTERVAL % profint) != 0)
-               profint = CLK_INTERVAL;
-       profscale = CLK_INTERVAL / profint;
+               timer3min = profmin;
 }
 
 }
 
-startprofclock()
+/*
+ * Statistics/profiling clock interrupt.  Compute a new interval.
+ * Interrupt has already been cleared.
+ *
+ * DO THIS INLINE IN locore.s?
+ */
+void
+statintr(fp)
+       struct clockframe *fp;
 {
 {
-       register struct clkreg *clk = (struct clkreg *)clkstd[0];
+       register volatile struct clkreg *clk;
+       register int newint, r, var;
 
 
-       clk->clk_msb3 = (profint-1) >> 8 & 0xFF;
-       clk->clk_lsb3 = (profint-1) & 0xFF;
+       clk = (volatile struct clkreg *)clkstd[0];
+       var = statvar;
+       do {
+               r = random() & (var - 1);
+       } while (r == 0);
+       newint = timer3min + r;
 
 
-       clk->clk_cr2 = CLK_CR3;
-       clk->clk_cr3 = CLK_IENAB;
-}
-
-stopprofclock()
-{
-       register struct clkreg *clk = (struct clkreg *)clkstd[0];
+       /*
+        * The timer was automatically reloaded with the previous latch
+        * value at the time of the interrupt.  Compensate now for the
+        * amount of time that has run off since then (minimum of 2-12
+        * timer ticks depending on CPU type) plus one tick roundoff.
+        * This should keep us closer to the mean.
+        */
+       asm volatile(" clrl %0; movpw %1@(13),%0" : "=d" (r) : "a" (clk));
+       newint -= (statprev - r + 1);
 
 
-       clk->clk_cr2 = CLK_CR3;
-       clk->clk_cr3 = 0;
+       asm volatile(" movpw %0,%1@(13)" : : "d" (newint), "a" (clk));
+       statprev = newint;
+       statclock(fp);
 }
 
 }
 
-#ifdef GPROF
 /*
 /*
- * profclock() is expanded in line in lev6intr() unless profiling kernel.
- * Assumes it is called with clock interrupts blocked.
+ * Return the best possible estimate of the current time.
  */
  */
-profclock(pc, ps)
-       caddr_t pc;
-       int ps;
+microtime(tvp)
+       register struct timeval *tvp;
 {
 {
-       /*
-        * Came from user mode.
-        * If this process is being profiled record the tick.
-        */
-       if (USERMODE(ps)) {
-               if (u.u_prof.pr_scale)
-                       addupc(pc, &u.u_prof, 1);
-       }
-       /*
-        * Came from kernel (supervisor) mode.
-        * If we are profiling the kernel, record the tick.
-        */
-       else if (profiling < 2) {
-               register int s = pc - s_lowpc;
+       register volatile struct clkreg *clk;
+       register int s, u, t, u2, s2;
 
 
-               if (s < s_textsize)
-                       kcount[s / (HISTFRACTION * sizeof (*kcount))]++;
-       }
        /*
        /*
-        * Kernel profiling was on but has been disabled.
-        * Mark as no longer profiling kernel and if all profiling done,
-        * disable the clock.
+        * Read registers from slowest-changing to fastest-changing,
+        * then re-read out to slowest.  If the values read before the
+        * innermost match those read after, the innermost value is
+        * consistent with the outer values.  If not, it may not be and
+        * we must retry.  Typically this loop runs only once; occasionally
+        * it runs twice, and only rarely does it run longer.
+        *
+        * (Using this loop avoids the need to block interrupts.)
         */
         */
-       if (profiling && (profon & PRF_KERNEL)) {
-               profon &= ~PRF_KERNEL;
-               if (profon == PRF_NONE)
-                       stopprofclock();
+       clk = (volatile struct clkreg *)clkstd[0];
+       do {
+               s = time.tv_sec;
+               u = time.tv_usec;
+               asm volatile (" clrl %0; movpw %1@(5),%0"
+                             : "=d" (t) : "a" (clk));
+               u2 = time.tv_usec;
+               s2 = time.tv_sec;
+       } while (u != u2 || s != s2);
+
+       u += (clkint - t) * CLK_RESOLUTION;
+       if (u >= 1000000) {             /* normalize */
+               s++;
+               u -= 1000000;
        }
        }
+       tvp->tv_sec = s;
+       tvp->tv_usec = u;
 }
 }
-#endif
-#endif
 
 /*
  * Initialize the time of day register, based on the time base which is, e.g.
 
 /*
  * Initialize the time of day register, based on the time base which is, e.g.
@@ -424,6 +294,9 @@ inittodr(base)
        time.tv_sec = timbuf;
 }
 
        time.tv_sec = timbuf;
 }
 
+/*
+ * Restore the time of day hardware after a time change.
+ */
 resettodr()
 {
        register int i;
 resettodr()
 {
        register int i;
@@ -557,6 +430,7 @@ read_bbc_reg(reg)
 
 u_char
 write_bbc_reg(reg, data)
 
 u_char
 write_bbc_reg(reg, data)
+       int reg;
        u_int data;
 {
        u_char tmp;
        u_int data;
 {
        u_char tmp;