/*
* Copyright (c) 1988 University of Utah.
* Copyright (c) 1982, 1990 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.
*
* 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. 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.
*
* 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.
*
* from: Utah $Hdr: clock.c 1.18 91/01/21$
*
* @(#)clock.c 7.6 (Berkeley) 5/7/91
*/
/*
* 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:
* We actually load the clock with amiga_clk_interval-1 instead of amiga_clk_interval.
* This is because the counter decrements to zero after N+1 enabled clock
* periods where N is the value loaded into the counter.
*/
return;
}
#endif
/*
* enable interrupts for timer A
*/
clockcia->icr = (1<<7) | (1<<0);
/*
* start timer A in continuous shot mode
*/
clockcia->cra = (clockcia->cra & 0xc0) | 1;
/*
* and globally enable interrupts for ciab
*/
#ifdef DRACO
if (dracorev) /* we use cia a on DraCo */
single_inst_bset_b(*draco_intena, DRIRQ_INT2);
else
#endif
custom.intena = INTF_SETCLR | INTF_EXTER;
}
void
setstatclockrate(int hertz)
{
}
/*
* Returns ticks since last recorded clock "tick"
* (i.e. clock interrupt).
*/
static u_int
clk_gettick(void)
{
u_int interval;
u_char hi, hi2, lo;
/*
* should read ICR and if there's an int pending, adjust
* interval. However, since reading ICR clears the interrupt,
* we'd lose a hardclock int, and this is not tolerable.
*/
}
return interval;
}
static u_int
clk_getcounter(struct timecounter *tc)
{
static int prev_hardclock;
static u_int prev_counter;
int cur_hardclock;
u_int counter;
do {
cur_hardclock = getticks();
counter = clk_gettick();
} while (cur_hardclock != getticks());
/*
* Handle the situation of a wrapped interval counter, while
* the hardclock() interrupt was not yet executed to update
* hardclock_ticks.
*/
if (cur_hardclock < prev_hardclock)
cur_hardclock = prev_hardclock;
if (counter < prev_counter && cur_hardclock == prev_hardclock)
cur_hardclock++;
/*
* Calibrate delay loop.
* We use two iterations because we don't have enough bits to do a factor of
* 8 with better than 1%.
*
* XXX Note that we MUST stay below 1 tick if using clk_gettick(), even for
* underestimated values of delaydivisor.
*
* XXX the "ns" below is only correct for a shift of 10 bits, and even then
* off by 2.4%
*/
static void
calibrate_delay(device_t self)
{
unsigned long t1, t2;
extern u_int32_t delaydivisor;
/* XXX this should be defined elsewhere */
/* implement this later. I'd suggest using both timers in CIA-A, they're
not yet used. */
#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/resourcevar.h>
#include <sys/ioctl.h>
#include <uvm/uvm_extern.h>
#include <amiga/amiga/clockioctl.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
/*ARGSUSED*/
int
clockopen(dev_t dev, int flags)
{
#ifdef PROFTIMER
#ifdef PROF
/*
* Kernel profiling enabled, give up.
*/
if (profiling)
return(EBUSY);
#endif
/*
* If any user processes are profiling, give up.
*/
if (profprocs)
return(EBUSY);
#endif
if (!clockon) {
startclock();
clockon++;
}
return(0);
}
#ifdef PROFTIMER
/*
* This code allows the amiga 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.
*/
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
/*
* 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) {
p->p_stats->p_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 (p->p_stats->p_prof.pr_scale)
profprocs++;
else
profprocs--;
#endif
/*
* The profile interrupt interval must be an even divisor
* of the amiga_clk_interval so that scaling from a system clock
* tick to a profile clock tick is possible using integer math.
*/
if (profint > amiga_clk_interval || (amiga_clk_interval % profint) != 0)
profint = amiga_clk_interval;
profscale = amiga_clk_interval / profint;
}
void
startprofclock(void)
{
unsigned short interval;
/* stop timer B */
clockcia->crb = clockcia->crb & 0xc0;
/* load interval into registers.
the clocks run at NTSC: 715.909kHz or PAL: 709.379kHz */
interval = profint - 1;
/* order of setting is important ! */
clockcia->tblo = interval & 0xff;
clockcia->tbhi = interval >> 8;
/* enable interrupts for timer B */
clockcia->icr = (1<<7) | (1<<1);
/* start timer B in continuous shot mode */
clockcia->crb = (clockcia->crb & 0xc0) | 1;
}
#ifdef PROF
/*
* profclock() is expanded in line in lev6intr() unless profiling kernel.
* Assumes it is called with clock interrupts blocked.
*/
void
profclock(void *pc, int ps)
{
/*
* Came from user mode.
* If this process is being profiled record the tick.
*/
if (USERMODE(ps)) {
if (p->p_stats.p_prof.pr_scale)
addupc(pc, &curproc->p_stats.p_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;
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.
*/
if (profiling && (profon & PRF_KERNEL)) {
profon &= ~PRF_KERNEL;
if (profon == PRF_NONE)
stopprofclock();
}
}
#endif
#endif