summaryrefslogtreecommitdiff
path: root/sys/arch/atari/dev/clock.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/atari/dev/clock.c')
-rw-r--r--sys/arch/atari/dev/clock.c466
1 files changed, 466 insertions, 0 deletions
diff --git a/sys/arch/atari/dev/clock.c b/sys/arch/atari/dev/clock.c
new file mode 100644
index 00000000000..c1471e4dc8d
--- /dev/null
+++ b/sys/arch/atari/dev/clock.c
@@ -0,0 +1,466 @@
+/* $NetBSD: clock.c,v 1.4 1995/09/23 20:23:28 leo Exp $ */
+
+/*
+ * 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. 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.
+ *
+ * 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
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <machine/psl.h>
+#include <machine/cpu.h>
+#include <machine/iomap.h>
+#include <machine/mfp.h>
+#include <atari/dev/clockreg.h>
+
+#if defined(GPROF) && defined(PROFTIMER)
+#include <machine/profile.h>
+#endif
+
+/*
+ * 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 CLK_INTERVAL-1 instead of 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.
+ */
+
+int clockmatch __P((struct device *, struct cfdata *, void *));
+void clockattach __P((struct device *, struct device *, void *));
+
+struct cfdriver clockcd = {
+ NULL, "clock", (cfmatch_t)clockmatch, clockattach,
+ DV_DULL, sizeof(struct device), NULL, 0
+};
+
+static u_long gettod __P((void));
+static int settod __P((u_long));
+
+static int divisor;
+
+int
+clockmatch(pdp, cfp, auxp)
+struct device *pdp;
+struct cfdata *cfp;
+void *auxp;
+{
+ if(!strcmp("clock", auxp))
+ return(1);
+ return(0);
+}
+
+/*
+ * Start the real-time clock.
+ */
+void clockattach(pdp, dp, auxp)
+struct device *pdp, *dp;
+void *auxp;
+{
+ /*
+ * 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 = 12288/hz;
+ MFP->mf_tacr = 0; /* Stop timer */
+ MFP->mf_iera &= ~IA_TIMA; /* Disable timer interrupts */
+ MFP->mf_tadr = divisor; /* Set divisor */
+
+ printf(": system hz %d timer-A divisor 200/%d\n", hz, divisor);
+
+ /*
+ * Initialize Timer-B in the ST-MFP. This timer is used by the 'delay'
+ * function below. This time is setup to be continueously counting from
+ * 255 back to zero at a frequency of 614400Hz.
+ */
+ 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 */
+
+}
+
+void cpu_initclocks()
+{
+ MFP->mf_tacr = T_Q200; /* Start timer */
+ MFP->mf_ipra &= ~IA_TIMA; /* Clear pending interrupts */
+ MFP->mf_iera |= IA_TIMA; /* Enable timer interrupts */
+ MFP->mf_imra |= IA_TIMA; /* ..... */
+}
+
+setstatclockrate(hz)
+ int hz;
+{
+}
+
+/*
+ * Returns number of usec since last recorded clock "tick"
+ * (i.e. clock interrupt).
+ */
+clkread()
+{
+ u_int delta;
+
+ delta = ((divisor - MFP->mf_tadr) * tick) / divisor;
+ /*
+ * Account for pending clock interrupts
+ */
+ if(MFP->mf_iera & IA_TIMA)
+ return(delta + tick);
+ return(delta);
+}
+
+#define TIMB_FREQ 614400
+#define TIMB_LIMIT 256
+
+/*
+ * 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(n)
+int n;
+{
+ int tick, otick;
+
+ /*
+ * Read the counter first, so that the rest of the setup overhead is
+ * counted.
+ */
+ otick = MFP->mf_tbdr;
+
+ /*
+ * Calculate ((n * TIMER_FREQ) / 1e6) using explicit assembler code so
+ * we can take advantage of the intermediate 64-bit quantity to prevent
+ * loss of significance.
+ */
+ n -= 5;
+ if(n < 0)
+ return;
+ {
+ u_int temp;
+
+ __asm __volatile ("mulul %2,%1:%0" : "=d" (n), "=d" (temp)
+ : "d" (TIMB_FREQ));
+ __asm __volatile ("divul %1,%2:%0" : "=d" (n)
+ : "d"(1000000),"d"(temp),"0"(n));
+ }
+
+ while(n > 0) {
+ tick = MFP->mf_tbdr;
+ if(tick > otick)
+ n -= TIMB_LIMIT - (tick - otick);
+ else n -= otick - tick;
+ otick = tick;
+ }
+}
+
+#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
+
+initprofclock()
+{
+#if NCLOCK > 0
+ struct proc *p = curproc; /* XXX */
+
+ /*
+ * 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 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;
+}
+
+startprofclock()
+{
+ unsigned short interval;
+
+ /* stop timer B */
+ ciab.crb = ciab.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 ! */
+ ciab.tblo = interval & 0xff;
+ ciab.tbhi = interval >> 8;
+
+ /* enable interrupts for timer B */
+ ciab.icr = (1<<7) | (1<<1);
+
+ /* start timer B in continuous shot mode */
+ ciab.crb = (ciab.crb & 0xc0) | 1;
+}
+
+stopprofclock()
+{
+ /* stop timer B */
+ ciab.crb = ciab.crb & 0xc0;
+}
+
+#ifdef GPROF
+/*
+ * profclock() is expanded in line in lev6intr() unless profiling kernel.
+ * Assumes it is called with clock interrupts blocked.
+ */
+profclock(pc, ps)
+ caddr_t 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
+
+/*
+ * Initialize the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+inittodr(base)
+time_t base;
+{
+ u_long timbuf = base; /* assume no battery clock exists */
+
+ timbuf = gettod();
+
+ if(timbuf < base) {
+ printf("WARNING: bad date in battery clock\n");
+ timbuf = base;
+ }
+
+ /* Battery clock does not store usec's, so forget about it. */
+ time.tv_sec = timbuf;
+}
+
+resettodr()
+{
+ if(settod(time.tv_sec) == 1)
+ return;
+ printf("Cannot set battery backed clock\n");
+}
+
+static char dmsize[12] =
+{
+ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+static char ldmsize[12] =
+{
+ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+};
+
+static u_long
+gettod()
+{
+ int i, sps;
+ u_long new_time = 0;
+ char *msize;
+ mc_todregs clkregs;
+
+ sps = splhigh();
+ MC146818_GETTOD(RTC, &clkregs);
+ splx(sps);
+
+ if(range_test(clkregs[MC_HOUR], 0, 23))
+ return(0);
+ if(range_test(clkregs[MC_DOM], 1, 31))
+ return(0);
+ if (range_test(clkregs[MC_MONTH], 1, 12))
+ return(0);
+ if(range_test(clkregs[MC_YEAR], 0, 2000 - GEMSTARTOFTIME))
+ return(0);
+ clkregs[MC_YEAR] += GEMSTARTOFTIME;
+
+ for(i = BSDSTARTOFTIME; i < clkregs[MC_YEAR]; i++) {
+ if(is_leap(i))
+ new_time += 366;
+ else new_time += 365;
+ }
+
+ msize = is_leap(clkregs[MC_YEAR]) ? ldmsize : dmsize;
+ for(i = 0; i < (clkregs[MC_MONTH] - 1); i++)
+ new_time += msize[i];
+ new_time += clkregs[MC_DOM] - 1;
+ new_time *= SECS_DAY;
+ new_time += (clkregs[MC_HOUR] * 3600) + (clkregs[MC_MIN] * 60);
+ return(new_time + clkregs[MC_SEC]);
+}
+
+static int
+settod(newtime)
+u_long newtime;
+{
+ register long days, rem, year;
+ register char *ml;
+ int sps, sec, min, hour, month;
+ mc_todregs clkregs;
+
+ /* Number of days since Jan. 1 'BSDSTARTOFTIME' */
+ days = newtime / SECS_DAY;
+ rem = newtime % SECS_DAY;
+
+ /*
+ * Calculate sec, min, hour
+ */
+ hour = rem / SECS_HOUR;
+ rem %= SECS_HOUR;
+ min = rem / 60;
+ sec = rem % 60;
+
+ /*
+ * Figure out the year. Day in year is left in 'days'.
+ */
+ year = BSDSTARTOFTIME;
+ while(days >= (rem = is_leap(year) ? 366 : 365)) {
+ ++year;
+ days -= rem;
+ }
+
+ /*
+ * Determine the month
+ */
+ ml = is_leap(year) ? ldmsize : dmsize;
+ for(month = 0; days >= ml[month]; ++month)
+ days -= ml[month];
+
+ /*
+ * Now that everything is calculated, program the RTC
+ */
+ mc146818_write(RTC, MC_REGA, MC_BASE_32_KHz);
+ mc146818_write(RTC, MC_REGB, MC_REGB_24HR | MC_REGB_BINARY);
+ sps = splhigh();
+ MC146818_GETTOD(RTC, &clkregs);
+ clkregs[MC_SEC] = sec;
+ clkregs[MC_MIN] = min;
+ clkregs[MC_HOUR] = hour;
+ clkregs[MC_DOM] = days+1;
+ clkregs[MC_MONTH] = month+1;
+ clkregs[MC_YEAR] = year - GEMSTARTOFTIME;
+ MC146818_PUTTOD(RTC, &clkregs);
+ splx(sps);
+
+ return(1);
+}