/*
* 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
*/
static int atari_rtc_get(todr_chip_handle_t, struct clock_ymdhms *);
static int atari_rtc_set(todr_chip_handle_t, struct clock_ymdhms *);
/*
* The MFP clock runs at 2457600Hz. We use a {system,stat,prof}clock divider
* of 200. Therefore the timer runs at an effective rate of:
* 2457600/200 = 12288Hz.
*/
#define CLOCK_HZ 12288
/*
* Machine-dependent clock routines.
*
* Inittodr initializes the time of day hardware which provides
* date functions.
*
* Resettodr restores the time of day hardware after a time change.
*/
/*
* Statistics and profile clock intervals and variances. Variance must
* be a power of 2. Since this gives us an even number, not an odd number,
* we discard one case and compensate. That is, a variance of 64 would
* give us offsets in [0..63]. Instead, we take offsets in [1..63].
* This is symmetric around the point 32, or statvar/2, and thus averages
* to that value (assuming uniform random numbers).
*/
#ifdef STATCLOCK
static int statvar = 32; /* {stat,prof}clock variance */
static int statmin; /* statclock divisor - variance/2 */
static int profmin; /* profclock divisor - variance/2 */
static int clk2min; /* current, from above choices */
#endif
static int
clockmatch(device_t parent, cfdata_t cf, void *aux)
{
/*
* Initialize Timer-A in the ST-MFP. We use a divisor of 200.
* The MFP clock runs at 2457600Hz. Therefore the timer runs
* at an effective rate of: 2457600/200 = 12288Hz. The
* following expression works for 48, 64 or 96 hz.
*/
divisor = CLOCK_HZ/hz;
MFP->mf_tacr = 0; /* Stop timer */
MFP->mf_iera &= ~IA_TIMA; /* Disable timer interrupts */
MFP->mf_tadr = divisor; /* Set divisor */
clk_timecounter.tc_frequency = CLOCK_HZ;
if (hz != 48 && hz != 64 && hz != 96) { /* XXX */
aprint_normal(": illegal value %d for systemclock, reset to %d\n\t",
hz, 64);
hz = 64;
}
aprint_normal(": system hz %d timer-A divisor 200/%d\n", hz, divisor);
tc_init(&clk_timecounter);
#ifdef STATCLOCK
void
statintr(struct clockframe frame)
{
register int var, r;
var = statvar - 1;
do {
r = random() & var;
} while (r == 0);
/*
* Note that we are always lagging behind as the new divisor
* value will not be loaded until the next interrupt. This
* shouldn't disturb the median frequency (I think ;-) ) as
* only the value used when switching frequencies is used
* twice. This shouldn't happen very often.
*/
MFP->mf_tcdr = clk2min + r;
count = (divisor * cur_hardclock) + delta;
if ((int32_t)(count - lastcount) < 0) {
/* XXX wrapped; maybe hardclock() is blocked more than 2/HZ */
count = lastcount + 1;
}
lastcount = count;
return count;
}
#define TIMB_FREQ 614400
#define TIMB_LIMIT 256
void
init_delay(void)
{
/*
* Initialize Timer-B in the ST-MFP. This timer is used by
* the 'delay' function below. This timer is setup to be
* continueously counting from 255 back to zero at a
* frequency of 614400Hz. We do this *early* in the
* initialisation process.
*/
MFP->mf_tbcr = 0; /* Stop timer */
MFP->mf_iera &= ~IA_TIMB; /* Disable timer interrupts */
MFP->mf_tbdr = 0;
MFP->mf_tbcr = T_Q004; /* Start timer */
}
/*
* Wait "n" microseconds.
* Relies on MFP-Timer B counting down from TIMB_LIMIT at TIMB_FREQ Hz.
* Note: timer had better have been programmed before this is first used!
*/
void
delay(unsigned int n)
{
int ticks, otick, remaining;
/*
* Read the counter first, so that the rest of the setup overhead is
* counted.
*/
otick = MFP->mf_tbdr;
if (n <= UINT_MAX / TIMB_FREQ) {
/*
* For unsigned arithmetic, division can be replaced with
* multiplication with the inverse and a shift.
*/
remaining = n * TIMB_FREQ / 1000000;
} else {
/* This is a very long delay.
* Being slow here doesn't matter.
*/
remaining = (unsigned long long) n * TIMB_FREQ / 1000000;
}
#ifdef GPROF
/*
* profclock() is expanded in line in lev6intr() unless profiling kernel.
* Assumes it is called with clock interrupts blocked.
*/
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
/***********************************************************************
* Real Time Clock support *
***********************************************************************/
s = splclock();
MC146818_PUTTOD(RTC, &clkregs);
splx(s);
return 0;
}
/***********************************************************************
* RTC-device support *
***********************************************************************/
static int
rtcopen(dev_t dev, int flag, int mode, struct lwp *l)
{
int unit = minor(dev);
struct clock_softc *sc;
sc = device_lookup_private(&clock_cd, unit);
if (sc == NULL)
return ENXIO;
if (sc->sc_flags & RTC_OPEN)
return EBUSY;
sc->sc_flags = RTC_OPEN;
return 0;
}
static int
rtcclose(dev_t dev, int flag, int mode, struct lwp *l)
{
int unit = minor(dev);
struct clock_softc *sc = device_lookup_private(&clock_cd, unit);
sc->sc_flags = 0;
return 0;
}
static int
rtcread(dev_t dev, struct uio *uio, int flags)
{
mc_todregs clkregs;
int s, length;
char buffer[16 + 1];
s = splhigh();
MC146818_GETTOD(RTC, &clkregs);
splx(s);