summaryrefslogtreecommitdiff
path: root/sys/arch/amd64/isa
diff options
context:
space:
mode:
authorMichael Shalayeff <mickey@cvs.openbsd.org>2004-01-28 01:39:41 +0000
committerMichael Shalayeff <mickey@cvs.openbsd.org>2004-01-28 01:39:41 +0000
commiteb2015b73fc7e8f74be0338c16e873a01653fe03 (patch)
treea0a1beaa9bc6601b949ea8937d79f939833b3cd3 /sys/arch/amd64/isa
parentfc744b6d0908de21ef8f71c7e15dd3b113e9aad8 (diff)
an amd64 arch support.
hacked by art@ from netbsd sources and then later debugged by me into the shape where it can host itself. no bootloader yet as needs redoing from the recent advanced i386 sources (anyone? ;)
Diffstat (limited to 'sys/arch/amd64/isa')
-rw-r--r--sys/arch/amd64/isa/clock.c765
-rw-r--r--sys/arch/amd64/isa/isa_machdep.c1026
2 files changed, 1791 insertions, 0 deletions
diff --git a/sys/arch/amd64/isa/clock.c b/sys/arch/amd64/isa/clock.c
new file mode 100644
index 00000000000..524ad712ec3
--- /dev/null
+++ b/sys/arch/amd64/isa/clock.c
@@ -0,0 +1,765 @@
+/* $OpenBSD: clock.c,v 1.1 2004/01/28 01:39:39 mickey Exp $ */
+/* $NetBSD: clock.c,v 1.1 2003/04/26 18:39:50 fvdl Exp $ */
+
+/*-
+ * Copyright (c) 1993, 1994 Charles M. Hannum.
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz and Don Ahn.
+ *
+ * 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.
+ *
+ * @(#)clock.c 7.2 (Berkeley) 5/12/91
+ */
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ * Primitive clock interrupt routines.
+ */
+
+/* #define CLOCKDEBUG */
+/* #define CLOCK_PARANOIA */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/timeout.h>
+
+#include <machine/cpu.h>
+#include <machine/intr.h>
+#include <machine/pio.h>
+#include <machine/cpufunc.h>
+
+#include <dev/isa/isareg.h>
+#include <dev/isa/isavar.h>
+#include <dev/ic/mc146818reg.h>
+#include <dev/ic/i8253reg.h>
+#include <i386/isa/nvram.h>
+#include <dev/clock_subr.h>
+#include <machine/specialreg.h>
+
+#ifndef __x86_64__
+#include "mca.h"
+#endif
+#if NMCA > 0
+#include <machine/mca_machdep.h> /* for MCA_system */
+#endif
+
+#include "pcppi.h"
+#if (NPCPPI > 0)
+#include <dev/isa/pcppivar.h>
+
+#ifdef CLOCKDEBUG
+int clock_debug = 0;
+#define DPRINTF(arg) if (clock_debug) printf arg
+#else
+#define DPRINTF(arg)
+#endif
+
+int sysbeepmatch(struct device *, void *, void *);
+void sysbeepattach(struct device *, struct device *, void *);
+
+struct cfattach sysbeep_ca = {
+ sizeof(struct device), sysbeepmatch, sysbeepattach
+};
+
+struct cfdriver sysbeep_cd = {
+ NULL, "sysbeep", DV_DULL
+};
+
+static int ppi_attached;
+static pcppi_tag_t ppicookie;
+#endif /* PCPPI */
+
+void spinwait(int);
+int clockintr(void *);
+int rtcintr(void *);
+int gettick(void);
+void sysbeep(int, int);
+void rtcdrain(void *v);
+int rtcget(mc_todregs *);
+void rtcput(mc_todregs *);
+int bcdtobin(int);
+int bintobcd(int);
+void findcpuspeed(void);
+
+__inline u_int mc146818_read(void *, u_int);
+__inline void mc146818_write(void *, u_int, u_int);
+
+__inline u_int
+mc146818_read(sc, reg)
+ void *sc; /* XXX use it? */
+ u_int reg;
+{
+
+ outb(IO_RTC, reg);
+ DELAY(1);
+ return (inb(IO_RTC+1));
+}
+
+__inline void
+mc146818_write(sc, reg, datum)
+ void *sc; /* XXX use it? */
+ u_int reg, datum;
+{
+
+ outb(IO_RTC, reg);
+ DELAY(1);
+ outb(IO_RTC+1, datum);
+ DELAY(1);
+}
+
+/* minimal initialization, enough for delay() */
+void
+initrtclock()
+{
+ u_long tval;
+
+ /*
+ * Compute timer_count, the count-down count the timer will be
+ * set to. Also, correctly round
+ * this by carrying an extra bit through the division.
+ */
+ tval = (TIMER_FREQ * 2) / (u_long) hz;
+ tval = (tval / 2) + (tval & 0x1);
+
+ /* initialize 8253 clock */
+ outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT);
+
+ /* Correct rounding will buy us a better precision in timekeeping */
+ outb(IO_TIMER1+TIMER_CNTR0, tval % 256);
+ outb(IO_TIMER1+TIMER_CNTR0, tval / 256);
+}
+
+/*
+ * microtime() makes use of the following globals. Note that isa_timer_tick
+ * may be redundant to the `tick' variable, but is kept here for stability.
+ * isa_timer_count is the countdown count for the timer. timer_msb_table[]
+ * and timer_lsb_table[] are used to compute the microsecond increment
+ * for time.tv_usec in the follow fashion:
+ *
+ * time.tv_usec += isa_timer_msb_table[cnt_msb] - isa_timer_lsb_table[cnt_lsb];
+ */
+#define ISA_TIMER_MSB_TABLE_SIZE 128
+
+u_long isa_timer_tick; /* the number of microseconds in a tick */
+u_short isa_timer_count; /* the countdown count for the timer */
+u_short isa_timer_msb_table[ISA_TIMER_MSB_TABLE_SIZE]; /* timer->usec MSB */
+u_short isa_timer_lsb_table[256]; /* timer->usec conversion for LSB */
+
+void
+startrtclock()
+{
+ int s;
+
+ findcpuspeed(); /* use the clock (while it's free)
+ to find the cpu speed */
+ initrtclock();
+
+ /* Check diagnostic status */
+ if ((s = mc146818_read(NULL, NVRAM_DIAG)) != 0) /* XXX softc */
+ printf("RTC BIOS diagnostic error %b\n", s, NVRAM_DIAG_BITS);
+}
+
+int
+clockintr(void *arg)
+{
+ struct clockframe *frame = arg;
+#if defined(I586_CPU) || defined(I686_CPU)
+ static int microset_iter; /* call cc_microset once/sec */
+ struct cpu_info *ci = curcpu();
+
+ /*
+ * If we have a cycle counter, do the microset thing.
+ */
+ if (ci->ci_feature_flags & CPUID_TSC) {
+ if (
+#if defined(MULTIPROCESSOR)
+ CPU_IS_PRIMARY(ci) &&
+#endif
+ (microset_iter--) == 0) {
+ cc_microset_time = time;
+ microset_iter = hz - 1;
+#if defined(MULTIPROCESSOR)
+ x86_broadcast_ipi(X86_IPI_MICROSET);
+#endif
+ cc_microset(ci);
+ }
+ }
+#endif
+ hardclock(frame);
+
+ return 1;
+}
+
+int
+rtcintr(void *arg)
+{
+ struct clockframe *frame = arg;
+ u_int stat = 0;
+
+ /*
+ * If rtcintr is 'late', next intr may happen immediately.
+ * Get them all. (Also, see comment in cpu_initclocks().)
+ */
+ while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF) {
+ statclock(frame);
+ stat = 1;
+ }
+
+ return (stat);
+}
+
+int
+gettick()
+{
+ u_long ef;
+ u_char lo, hi;
+
+ /* Don't want someone screwing with the counter while we're here. */
+ ef = read_rflags();
+ disable_intr();
+ /* Select counter 0 and latch it. */
+ outb(IO_TIMER1+TIMER_MODE, TIMER_SEL0 | TIMER_LATCH);
+ lo = inb(IO_TIMER1+TIMER_CNTR0);
+ hi = inb(IO_TIMER1+TIMER_CNTR0);
+ write_rflags(ef);
+ return ((hi << 8) | lo);
+}
+
+/*
+ * Wait "n" microseconds.
+ * Relies on timer 1 counting down from (TIMER_FREQ / hz) at TIMER_FREQ Hz.
+ * Note: timer had better have been programmed before this is first used!
+ * (Note that we use `rate generator' mode, which counts at 1:1; `square
+ * wave' mode counts at 2:1).
+ */
+void
+i8254_delay(int n)
+{
+ int limit, tick, otick;
+
+ /*
+ * Read the counter first, so that the rest of the setup overhead is
+ * counted.
+ */
+ otick = gettick();
+
+#ifdef __GNUC__
+ /*
+ * 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;
+ __asm __volatile("mul %2\n\tdiv %3"
+ : "=a" (n)
+ : "0" (n), "r" (TIMER_FREQ), "r" (1000000)
+ : "%edx", "cc");
+#else
+ /*
+ * Calculate ((n * TIMER_FREQ) / 1e6) without using floating point and
+ * without any avoidable overflows.
+ */
+ n -= 20;
+ {
+ int sec = n / 1000000,
+ usec = n % 1000000;
+ n = sec * TIMER_FREQ +
+ usec * (TIMER_FREQ / 1000000) +
+ usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 +
+ usec * (TIMER_FREQ % 1000) / 1000000;
+ }
+#endif
+
+ limit = TIMER_FREQ / hz;
+
+ while (n > 0) {
+ tick = gettick();
+ if (tick > otick)
+ n -= limit - (tick - otick);
+ else
+ n -= otick - tick;
+ otick = tick;
+ }
+}
+
+#if (NPCPPI > 0)
+int
+sysbeepmatch(parent, match, aux)
+ struct device *parent;
+ void *match;
+ void *aux;
+{
+ return (!ppi_attached);
+}
+
+void
+sysbeepattach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ printf("\n");
+
+ ppicookie = ((struct pcppi_attach_args *)aux)->pa_cookie;
+ ppi_attached = 1;
+}
+#endif
+
+void
+sysbeep(pitch, period)
+ int pitch, period;
+{
+#if (NPCPPI > 0)
+ if (ppi_attached)
+ pcppi_bell(ppicookie, pitch, period, 0);
+#endif
+}
+
+unsigned int delaycount; /* calibrated loop variable (1 millisecond) */
+
+#define FIRST_GUESS 0x2000
+
+void
+findcpuspeed()
+{
+ int i;
+ int remainder;
+
+ /* Put counter in count down mode */
+ outb(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN);
+ outb(TIMER_CNTR0, 0xff);
+ outb(TIMER_CNTR0, 0xff);
+ for (i = FIRST_GUESS; i; i--)
+ ;
+ /* Read the value left in the counter */
+ remainder = gettick();
+ /*
+ * Formula for delaycount is:
+ * (loopcount * timer clock speed) / (counter ticks * 1000)
+ */
+ delaycount = (FIRST_GUESS * TIMER_DIV(1000)) / (0xffff-remainder);
+}
+
+void
+rtcdrain(void *v)
+{
+ struct timeout *to = (struct timeout *)v;
+
+ if (to != NULL)
+ timeout_del(to);
+
+ /*
+ * Drain any un-acknowledged RTC interrupts.
+ * See comment in cpu_initclocks().
+ */
+ while (mc146818_read(NULL, MC_REGC) & MC_REGC_PF)
+ ; /* Nothing. */
+}
+
+void
+i8254_initclocks()
+{
+ static struct timeout rtcdrain_timeout;
+
+ stathz = 128;
+ profhz = 1024;
+
+ /*
+ * XXX If you're doing strange things with multiple clocks, you might
+ * want to keep track of clock handlers.
+ */
+ isa_intr_establish(NULL, 0, IST_PULSE, IPL_CLOCK, clockintr,0,"clock");
+ isa_intr_establish(NULL, 8, IST_PULSE, IPL_CLOCK, rtcintr, 0, "rtc");
+
+ mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
+ mc146818_write(NULL, MC_REGB, MC_REGB_24HR | MC_REGB_PIE);
+
+ /*
+ * On a number of i386 systems, the rtc will fail to start when booting
+ * the system. This is due to us missing to acknowledge an interrupt
+ * during early stages of the boot process. If we do not acknowledge
+ * the interrupt, the rtc clock will not generate further interrupts.
+ * To solve this, once interrupts are enabled, use a timeout (once)
+ * to drain any un-acknowledged rtc interrupt(s).
+ */
+ timeout_set(&rtcdrain_timeout, rtcdrain, (void *)&rtcdrain_timeout);
+ timeout_add(&rtcdrain_timeout, 1);
+}
+
+int
+rtcget(regs)
+ mc_todregs *regs;
+{
+ if ((mc146818_read(NULL, MC_REGD) & MC_REGD_VRT) == 0) /* XXX softc */
+ return (-1);
+ MC146818_GETTOD(NULL, regs); /* XXX softc */
+ return (0);
+}
+
+void
+rtcput(regs)
+ mc_todregs *regs;
+{
+ MC146818_PUTTOD(NULL, regs); /* XXX softc */
+}
+
+int
+bcdtobin(n)
+ int n;
+{
+
+ return (((n >> 4) & 0x0f) * 10 + (n & 0x0f));
+}
+
+int
+bintobcd(n)
+ int n;
+{
+
+ return ((u_char)(((n / 10) << 4) & 0xf0) | ((n % 10) & 0x0f));
+}
+
+static int timeset;
+
+/*
+ * check whether the CMOS layout is "standard"-like (ie, not PS/2-like),
+ * to be called at splclock()
+ */
+static int cmoscheck(void);
+static int
+cmoscheck()
+{
+ int i;
+ unsigned short cksum = 0;
+
+ for (i = 0x10; i <= 0x2d; i++)
+ cksum += mc146818_read(NULL, i); /* XXX softc */
+
+ return (cksum == (mc146818_read(NULL, 0x2e) << 8)
+ + mc146818_read(NULL, 0x2f));
+}
+
+#if NMCA > 0
+/*
+ * Check whether the CMOS layout is PS/2 like, to be called at splclock().
+ */
+static int cmoscheckps2(void);
+static int
+cmoscheckps2()
+{
+#if 0
+ /* Disabled until I find out the CRC checksum algorithm IBM uses */
+ int i;
+ unsigned short cksum = 0;
+
+ for (i = 0x10; i <= 0x31; i++)
+ cksum += mc146818_read(NULL, i); /* XXX softc */
+
+ return (cksum == (mc146818_read(NULL, 0x32) << 8)
+ + mc146818_read(NULL, 0x33));
+#else
+ /* Check 'incorrect checksum' bit of IBM PS/2 Diagnostic Status Byte */
+ return ((mc146818_read(NULL, NVRAM_DIAG) & (1<<6)) == 0);
+#endif
+}
+#endif /* NMCA > 0 */
+
+/*
+ * patchable to control century byte handling:
+ * 1: always update
+ * -1: never touch
+ * 0: try to figure out itself
+ */
+int rtc_update_century = 0;
+
+/*
+ * Expand a two-digit year as read from the clock chip
+ * into full width.
+ * Being here, deal with the CMOS century byte.
+ */
+static int centb = NVRAM_CENTURY;
+static int clock_expandyear(int);
+static int
+clock_expandyear(clockyear)
+ int clockyear;
+{
+ int s, clockcentury, cmoscentury;
+
+ clockcentury = (clockyear < 70) ? 20 : 19;
+ clockyear += 100 * clockcentury;
+
+ if (rtc_update_century < 0)
+ return (clockyear);
+
+ s = splclock();
+ if (cmoscheck())
+ cmoscentury = mc146818_read(NULL, NVRAM_CENTURY);
+#if NMCA > 0
+ else if (MCA_system && cmoscheckps2())
+ cmoscentury = mc146818_read(NULL, (centb = 0x37));
+#endif
+ else
+ cmoscentury = 0;
+ splx(s);
+ if (!cmoscentury) {
+#ifdef DIAGNOSTIC
+ printf("clock: unknown CMOS layout\n");
+#endif
+ return (clockyear);
+ }
+ cmoscentury = bcdtobin(cmoscentury);
+
+ if (cmoscentury != clockcentury) {
+ /* XXX note: saying "century is 20" might confuse the naive. */
+ printf("WARNING: NVRAM century is %d but RTC year is %d\n",
+ cmoscentury, clockyear);
+
+ /* Kludge to roll over century. */
+ if ((rtc_update_century > 0) ||
+ ((cmoscentury == 19) && (clockcentury == 20) &&
+ (clockyear == 2000))) {
+ printf("WARNING: Setting NVRAM century to %d\n",
+ clockcentury);
+ s = splclock();
+ mc146818_write(NULL, centb, bintobcd(clockcentury));
+ splx(s);
+ }
+ } else if (cmoscentury == 19 && rtc_update_century == 0)
+ rtc_update_century = 1; /* will update later in resettodr() */
+
+ return (clockyear);
+}
+
+/*
+ * Initialize the time of day register, based on the time base which is, e.g.
+ * from a filesystem.
+ */
+void
+inittodr(base)
+ time_t base;
+{
+ mc_todregs rtclk;
+ struct clock_ymdhms dt;
+ int s;
+#if defined(I586_CPU) || defined(I686_CPU)
+ struct cpu_info *ci = curcpu();
+#endif
+ /*
+ * We mostly ignore the suggested time (which comes from the
+ * file system) and go for the RTC clock time stored in the
+ * CMOS RAM. If the time can't be obtained from the CMOS, or
+ * if the time obtained from the CMOS is 5 or more years less
+ * than the suggested time, we used the suggested time. (In
+ * the latter case, it's likely that the CMOS battery has
+ * died.)
+ */
+
+ /*
+ * if the file system time is more than a year older than the
+ * kernel, warn and then set the base time to the CONFIG_TIME.
+ */
+ if (base < 30*SECYR) { /* if before 2000, something's odd... */
+ printf("WARNING: preposterous time in file system\n");
+ base = 30*SECYR;
+ }
+
+ s = splclock();
+ if (rtcget(&rtclk)) {
+ splx(s);
+ printf("WARNING: invalid time in clock chip\n");
+ goto fstime;
+ }
+ splx(s);
+#ifdef DEBUG_CLOCK
+ printf("readclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR],
+ rtclk[MC_MONTH], rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN],
+ rtclk[MC_SEC]);
+#endif
+
+ dt.dt_sec = bcdtobin(rtclk[MC_SEC]);
+ dt.dt_min = bcdtobin(rtclk[MC_MIN]);
+ dt.dt_hour = bcdtobin(rtclk[MC_HOUR]);
+ dt.dt_day = bcdtobin(rtclk[MC_DOM]);
+ dt.dt_mon = bcdtobin(rtclk[MC_MONTH]);
+ dt.dt_year = clock_expandyear(bcdtobin(rtclk[MC_YEAR]));
+
+ /*
+ * If time_t is 32 bits, then the "End of Time" is
+ * Mon Jan 18 22:14:07 2038 (US/Eastern)
+ * This code copes with RTC's past the end of time if time_t
+ * is an int32 or less. Needed because sometimes RTCs screw
+ * up or are badly set, and that would cause the time to go
+ * negative in the calculation below, which causes Very Bad
+ * Mojo. This at least lets the user boot and fix the problem.
+ * Note the code is self eliminating once time_t goes to 64 bits.
+ */
+ if (sizeof(time_t) <= sizeof(int32_t)) {
+ if (dt.dt_year >= 2038) {
+ printf("WARNING: RTC time at or beyond 2038.\n");
+ dt.dt_year = 2037;
+ printf("WARNING: year set back to 2037.\n");
+ printf("WARNING: CHECK AND RESET THE DATE!\n");
+ }
+ }
+
+ time.tv_sec = clock_ymdhms_to_secs(&dt);
+#ifdef DEBUG_CLOCK
+ printf("readclock: %ld (%ld)\n", time.tv_sec, base);
+#endif
+#if defined(I586_CPU) || defined(I686_CPU)
+ if (ci->ci_feature_flags & CPUID_TSC) {
+ cc_microset_time = time;
+ cc_microset(ci);
+ }
+#endif
+
+ if (base != 0 && base < time.tv_sec - 5*SECYR)
+ printf("WARNING: file system time much less than clock time\n");
+ else if (base > time.tv_sec + 5*SECYR) {
+ printf("WARNING: clock time much less than file system time\n");
+ printf("WARNING: using file system time\n");
+ goto fstime;
+ }
+
+ timeset = 1;
+ return;
+
+fstime:
+ timeset = 1;
+ time.tv_sec = base;
+ printf("WARNING: CHECK AND RESET THE DATE!\n");
+}
+
+/*
+ * Reset the clock.
+ */
+void
+resettodr()
+{
+ mc_todregs rtclk;
+ struct clock_ymdhms dt;
+ int century;
+ int s;
+
+ /*
+ * We might have been called by boot() due to a crash early
+ * on. Don't reset the clock chip in this case.
+ */
+ if (!timeset)
+ return;
+
+ s = splclock();
+ if (rtcget(&rtclk))
+ memset(&rtclk, 0, sizeof(rtclk));
+ splx(s);
+
+ clock_secs_to_ymdhms(time.tv_sec, &dt);
+
+ rtclk[MC_SEC] = bintobcd(dt.dt_sec);
+ rtclk[MC_MIN] = bintobcd(dt.dt_min);
+ rtclk[MC_HOUR] = bintobcd(dt.dt_hour);
+ rtclk[MC_DOW] = dt.dt_wday + 1;
+ rtclk[MC_YEAR] = bintobcd(dt.dt_year % 100);
+ rtclk[MC_MONTH] = bintobcd(dt.dt_mon);
+ rtclk[MC_DOM] = bintobcd(dt.dt_day);
+
+#ifdef DEBUG_CLOCK
+ printf("setclock: %x/%x/%x %x:%x:%x\n", rtclk[MC_YEAR], rtclk[MC_MONTH],
+ rtclk[MC_DOM], rtclk[MC_HOUR], rtclk[MC_MIN], rtclk[MC_SEC]);
+#endif
+ s = splclock();
+ rtcput(&rtclk);
+ if (rtc_update_century > 0) {
+ century = bintobcd(dt.dt_year / 100);
+ mc146818_write(NULL, centb, century); /* XXX softc */
+ }
+ splx(s);
+}
+
+void
+setstatclockrate(arg)
+ int arg;
+{
+ if (arg == stathz)
+ mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_128_Hz);
+ else
+ mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | MC_RATE_1024_Hz);
+}
diff --git a/sys/arch/amd64/isa/isa_machdep.c b/sys/arch/amd64/isa/isa_machdep.c
new file mode 100644
index 00000000000..f86a49a00ed
--- /dev/null
+++ b/sys/arch/amd64/isa/isa_machdep.c
@@ -0,0 +1,1026 @@
+/* $OpenBSD: isa_machdep.c,v 1.1 2004/01/28 01:39:39 mickey Exp $ */
+/* $NetBSD: isa_machdep.c,v 1.22 1997/06/12 23:57:32 thorpej Exp $ */
+
+#define ISA_DMA_STATS
+
+/*-
+ * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+ * NASA Ames Research Center.
+ *
+ * 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 NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*-
+ * Copyright (c) 1996, 1997 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+ * NASA Ames Research Center.
+ *
+ * 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 NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+/*-
+ * Copyright (c) 1993, 1994, 1996, 1997
+ * Charles M. Hannum. All rights reserved.
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * William Jolitz.
+ *
+ * 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.
+ *
+ * @(#)isa.c 7.2 (Berkeley) 5/13/91
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/syslog.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <uvm/uvm_extern.h>
+
+#define _I386_BUS_DMA_PRIVATE
+#include <machine/bus.h>
+
+#include <machine/intr.h>
+#include <machine/pio.h>
+#include <machine/cpufunc.h>
+
+#include <dev/isa/isareg.h>
+#include <dev/isa/isavar.h>
+#if 0
+#include <dev/isa/isadmavar.h>
+#endif
+#include <i386/isa/isa_machdep.h>
+#include <i386/isa/icu.h>
+
+#include "isadma.h"
+
+/*
+ * ISA can only DMA to 0-16M.
+ */
+#define ISA_DMA_BOUNCE_THRESHOLD 0x00ffffff
+
+extern paddr_t avail_end;
+
+#define IDTVEC(name) __CONCAT(X,name)
+/* default interrupt vector table entries */
+typedef int (*vector)(void);
+extern vector IDTVEC(intr)[];
+void isa_strayintr(int);
+int fakeintr(void *);
+
+#if NISADMA > 0
+int _isa_bus_dmamap_create(bus_dma_tag_t, bus_size_t, int,
+ bus_size_t, bus_size_t, int, bus_dmamap_t *);
+void _isa_bus_dmamap_destroy(bus_dma_tag_t, bus_dmamap_t);
+int _isa_bus_dmamap_load(bus_dma_tag_t, bus_dmamap_t, void *,
+ bus_size_t, struct proc *, int);
+int _isa_bus_dmamap_load_mbuf(bus_dma_tag_t, bus_dmamap_t,
+ struct mbuf *, int);
+int _isa_bus_dmamap_load_uio(bus_dma_tag_t, bus_dmamap_t,
+ struct uio *, int);
+int _isa_bus_dmamap_load_raw(bus_dma_tag_t, bus_dmamap_t,
+ bus_dma_segment_t *, int, bus_size_t, int);
+void _isa_bus_dmamap_unload(bus_dma_tag_t, bus_dmamap_t);
+void _isa_bus_dmamap_sync(bus_dma_tag_t, bus_dmamap_t,
+ bus_addr_t, bus_size_t, int);
+
+int _isa_bus_dmamem_alloc(bus_dma_tag_t, bus_size_t, bus_size_t,
+ bus_size_t, bus_dma_segment_t *, int, int *, int);
+void _isa_bus_dmamem_free(bus_dma_tag_t,
+ bus_dma_segment_t *, int);
+int _isa_bus_dmamem_map(bus_dma_tag_t, bus_dma_segment_t *,
+ int, size_t, caddr_t *, int);
+void _isa_bus_dmamem_unmap(bus_dma_tag_t, caddr_t, size_t);
+paddr_t _isa_bus_dmamem_mmap(bus_dma_tag_t, bus_dma_segment_t *,
+ int, off_t, int, int);
+
+int _isa_dma_check_buffer(void *, bus_size_t, int, bus_size_t,
+ struct proc *);
+int _isa_dma_alloc_bouncebuf(bus_dma_tag_t, bus_dmamap_t,
+ bus_size_t, int);
+void _isa_dma_free_bouncebuf(bus_dma_tag_t, bus_dmamap_t);
+
+/*
+ * Entry points for ISA DMA. These are mostly wrappers around
+ * the generic functions that understand how to deal with bounce
+ * buffers, if necessary.
+ */
+struct i386_bus_dma_tag isa_bus_dma_tag = {
+ NULL, /* _cookie */
+ _isa_bus_dmamap_create,
+ _isa_bus_dmamap_destroy,
+ _isa_bus_dmamap_load,
+ _isa_bus_dmamap_load_mbuf,
+ _isa_bus_dmamap_load_uio,
+ _isa_bus_dmamap_load_raw,
+ _isa_bus_dmamap_unload,
+ _isa_bus_dmamap_sync,
+ _isa_bus_dmamem_alloc,
+ _isa_bus_dmamem_free,
+ _isa_bus_dmamem_map,
+ _isa_bus_dmamem_unmap,
+ _isa_bus_dmamem_mmap,
+};
+#endif /* NISADMA > 0 */
+
+#define GICODE_SEL 10
+
+u_long intrstray[ICU_LEN];
+
+/*
+ * Caught a stray interrupt, notify
+ */
+void
+isa_strayintr(irq)
+ int irq;
+{
+ /*
+ * Stray interrupts on irq 7 occur when an interrupt line is raised
+ * and then lowered before the CPU acknowledges it. This generally
+ * means either the device is screwed or something is cli'ing too
+ * long and it's timing out.
+ */
+ if (++intrstray[irq] <= 5)
+ log(LOG_ERR, "stray interrupt %d%s\n", irq,
+ intrstray[irq] >= 5 ? "; stopped logging" : "");
+}
+
+int intrtype[ICU_LEN], intrmask[ICU_LEN], intrlevel[ICU_LEN];
+int iminlevel[ICU_LEN], imaxlevel[ICU_LEN];
+struct intrhand *intrhand[ICU_LEN];
+
+int
+fakeintr(arg)
+ void *arg;
+{
+ return 0;
+}
+
+#define LEGAL_IRQ(x) ((x) >= 0 && (x) < ICU_LEN && (x) != 2)
+
+int
+isa_intr_alloc(ic, mask, type, irq)
+ isa_chipset_tag_t ic;
+ int mask;
+ int type;
+ int *irq;
+{
+ int i, bestirq, count;
+ int tmp;
+ struct intrhand **p, *q;
+
+ if (type == IST_NONE)
+ panic("intr_alloc: bogus type");
+
+ bestirq = -1;
+ count = -1;
+
+ /* some interrupts should never be dynamically allocated */
+ mask &= 0xdef8;
+
+ /*
+ * XXX some interrupts will be used later (6 for fdc, 12 for pms).
+ * the right answer is to do "breadth-first" searching of devices.
+ */
+ mask &= 0xefbf;
+
+ for (i = 0; i < ICU_LEN; i++) {
+ if (LEGAL_IRQ(i) == 0 || (mask & (1<<i)) == 0)
+ continue;
+
+ switch(intrtype[i]) {
+ case IST_NONE:
+ /*
+ * if nothing's using the irq, just return it
+ */
+ *irq = i;
+ return (0);
+
+ case IST_EDGE:
+ case IST_LEVEL:
+ if (type != intrtype[i])
+ continue;
+ /*
+ * if the irq is shareable, count the number of other
+ * handlers, and if it's smaller than the last irq like
+ * this, remember it
+ *
+ * XXX We should probably also consider the
+ * interrupt level and stick IPL_TTY with other
+ * IPL_TTY, etc.
+ */
+ for (p = &intrhand[i], tmp = 0; (q = *p) != NULL;
+ p = &q->ih_next, tmp++)
+ ;
+ if ((bestirq == -1) || (count > tmp)) {
+ bestirq = i;
+ count = tmp;
+ }
+ break;
+
+ case IST_PULSE:
+ /* this just isn't shareable */
+ continue;
+ }
+ }
+
+ if (bestirq == -1)
+ return (1);
+
+ *irq = bestirq;
+
+ return (0);
+}
+
+/*
+ * Just check to see if an IRQ is available/can be shared.
+ * 0 = interrupt not available
+ * 1 = interrupt shareable
+ * 2 = interrupt all to ourself
+ */
+int
+isa_intr_check(ic, irq, type)
+ isa_chipset_tag_t ic; /* Not used. */
+ int irq;
+ int type;
+{
+ if (!LEGAL_IRQ(irq) || type == IST_NONE)
+ return (0);
+
+ switch (intrtype[irq]) {
+ case IST_NONE:
+ return (2);
+ break;
+ case IST_LEVEL:
+ if (type != intrtype[irq])
+ return (0);
+ return (1);
+ break;
+ case IST_EDGE:
+ case IST_PULSE:
+ if (type != IST_NONE)
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Set up an interrupt handler to start being called.
+ * XXX PRONE TO RACE CONDITIONS, UGLY, 'INTERESTING' INSERTION ALGORITHM.
+ */
+void *
+isa_intr_establish(ic, irq, type, level, ih_fun, ih_arg, ih_what)
+ isa_chipset_tag_t ic;
+ int irq;
+ int type;
+ int level;
+ int (*ih_fun)(void *);
+ void *ih_arg;
+ char *ih_what;
+{
+ struct intrhand **p, *q, *ih;
+ static struct intrhand fakehand = {fakeintr};
+
+ return intr_establish(irq, &i8259_pic, irq, type, level, ih_fun, ih_arg);
+
+ /* no point in sleeping unless someone can free memory. */
+ ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
+ if (ih == NULL) {
+ printf("%s: isa_intr_establish: can't malloc handler info\n",
+ ih_what);
+ return NULL;
+ }
+
+ if (!LEGAL_IRQ(irq) || type == IST_NONE) {
+ printf("%s: intr_establish: bogus irq or type\n", ih_what);
+ return NULL;
+ }
+ switch (intrtype[irq]) {
+ case IST_NONE:
+ intrtype[irq] = type;
+ break;
+ case IST_EDGE:
+ case IST_LEVEL:
+ if (type == intrtype[irq])
+ break;
+ case IST_PULSE:
+ if (type != IST_NONE) {
+ /*printf("%s: intr_establish: can't share %s with %s, irq %d\n",
+ ih_what, isa_intr_typename(intrtype[irq]),
+ isa_intr_typename(type), irq);*/
+ return NULL;
+ }
+ break;
+ }
+
+ /*
+ * Figure out where to put the handler.
+ * This is O(N^2), but we want to preserve the order, and N is
+ * generally small.
+ */
+ for (p = &intrhand[irq]; (q = *p) != NULL; p = &q->ih_next)
+ ;
+
+ /*
+ * Actually install a fake handler momentarily, since we might be doing
+ * this with interrupts enabled and don't want the real routine called
+ * until masking is set up.
+ */
+ fakehand.ih_level = level;
+ *p = &fakehand;
+
+ /*
+ * Poke the real handler in now.
+ */
+ ih->ih_fun = ih_fun;
+ ih->ih_arg = ih_arg;
+ ih->ih_next = NULL;
+ ih->ih_level = level;
+ ih->ih_irq = irq;
+ ih->ih_what = ih_what;
+ *p = ih;
+
+ return (ih);
+}
+
+/*
+ * Deregister an interrupt handler.
+ */
+void
+isa_intr_disestablish(ic, arg)
+ isa_chipset_tag_t ic;
+ void *arg;
+{
+ struct intrhand *ih = arg;
+ int irq = ih->ih_irq;
+ struct intrhand **p, *q;
+
+ intr_disestablish(arg);
+ return;
+
+ if (!LEGAL_IRQ(irq))
+ panic("intr_disestablish: bogus irq");
+
+ /*
+ * Remove the handler from the chain.
+ * This is O(n^2), too.
+ */
+ for (p = &intrhand[irq]; (q = *p) != NULL && q != ih; p = &q->ih_next)
+ ;
+ if (q)
+ *p = q->ih_next;
+ else
+ panic("intr_disestablish: handler not registered");
+ free(ih, M_DEVBUF);
+
+ if (intrhand[irq] == NULL)
+ intrtype[irq] = IST_NONE;
+}
+
+void
+isa_attach_hook(parent, self, iba)
+ struct device *parent, *self;
+ struct isabus_attach_args *iba;
+{
+ extern int isa_has_been_seen;
+
+ /*
+ * Notify others that might need to know that the ISA bus
+ * has now been attached.
+ */
+ if (isa_has_been_seen)
+ panic("isaattach: ISA bus already seen!");
+ isa_has_been_seen = 1;
+}
+
+#if NISADMA > 0
+/**********************************************************************
+ * bus.h dma interface entry points
+ **********************************************************************/
+
+#ifdef ISA_DMA_STATS
+#define STAT_INCR(v) (v)++
+#define STAT_DECR(v) do { \
+ if ((v) == 0) \
+ printf("%s:%d -- Already 0!\n", __FILE__, __LINE__); \
+ else \
+ (v)--; \
+ } while (0)
+u_long isa_dma_stats_loads;
+u_long isa_dma_stats_bounces;
+u_long isa_dma_stats_nbouncebufs;
+#else
+#define STAT_INCR(v)
+#define STAT_DECR(v)
+#endif
+
+/*
+ * Create an ISA DMA map.
+ */
+int
+_isa_bus_dmamap_create(t, size, nsegments, maxsegsz, boundary, flags, dmamp)
+ bus_dma_tag_t t;
+ bus_size_t size;
+ int nsegments;
+ bus_size_t maxsegsz;
+ bus_size_t boundary;
+ int flags;
+ bus_dmamap_t *dmamp;
+{
+ struct i386_isa_dma_cookie *cookie;
+ bus_dmamap_t map;
+ int error, cookieflags;
+ void *cookiestore;
+ size_t cookiesize;
+
+ /* Call common function to create the basic map. */
+ error = _bus_dmamap_create(t, size, nsegments, maxsegsz, boundary,
+ flags, dmamp);
+ if (error)
+ return (error);
+
+ map = *dmamp;
+ map->_dm_cookie = NULL;
+
+ cookiesize = sizeof(struct i386_isa_dma_cookie);
+
+ /*
+ * ISA only has 24-bits of address space. This means
+ * we can't DMA to pages over 16M. In order to DMA to
+ * arbitrary buffers, we use "bounce buffers" - pages
+ * in memory below the 16M boundary. On DMA reads,
+ * DMA happens to the bounce buffers, and is copied into
+ * the caller's buffer. On writes, data is copied into
+ * but bounce buffer, and the DMA happens from those
+ * pages. To software using the DMA mapping interface,
+ * this looks simply like a data cache.
+ *
+ * If we have more than 16M of RAM in the system, we may
+ * need bounce buffers. We check and remember that here.
+ *
+ * There are exceptions, however. VLB devices can do
+ * 32-bit DMA, and indicate that here.
+ *
+ * ...or, there is an opposite case. The most segments
+ * a transfer will require is (maxxfer / NBPG) + 1. If
+ * the caller can't handle that many segments (e.g. the
+ * ISA DMA controller), we may have to bounce it as well.
+ */
+ cookieflags = 0;
+ if ((avail_end > ISA_DMA_BOUNCE_THRESHOLD &&
+ (flags & ISABUS_DMA_32BIT) == 0) ||
+ ((map->_dm_size / NBPG) + 1) > map->_dm_segcnt) {
+ cookieflags |= ID_MIGHT_NEED_BOUNCE;
+ cookiesize += (sizeof(bus_dma_segment_t) * map->_dm_segcnt);
+ }
+
+ /*
+ * Allocate our cookie.
+ */
+ if ((cookiestore = malloc(cookiesize, M_DEVBUF,
+ (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK)) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ bzero(cookiestore, cookiesize);
+ cookie = (struct i386_isa_dma_cookie *)cookiestore;
+ cookie->id_flags = cookieflags;
+ map->_dm_cookie = cookie;
+
+ if (cookieflags & ID_MIGHT_NEED_BOUNCE) {
+ /*
+ * Allocate the bounce pages now if the caller
+ * wishes us to do so.
+ */
+ if ((flags & BUS_DMA_ALLOCNOW) == 0)
+ goto out;
+
+ error = _isa_dma_alloc_bouncebuf(t, map, size, flags);
+ }
+
+ out:
+ if (error) {
+ if (map->_dm_cookie != NULL)
+ free(map->_dm_cookie, M_DEVBUF);
+ _bus_dmamap_destroy(t, map);
+ }
+ return (error);
+}
+
+/*
+ * Destroy an ISA DMA map.
+ */
+void
+_isa_bus_dmamap_destroy(t, map)
+ bus_dma_tag_t t;
+ bus_dmamap_t map;
+{
+ struct i386_isa_dma_cookie *cookie = map->_dm_cookie;
+
+ /*
+ * Free any bounce pages this map might hold.
+ */
+ if (cookie->id_flags & ID_HAS_BOUNCE)
+ _isa_dma_free_bouncebuf(t, map);
+
+ free(cookie, M_DEVBUF);
+ _bus_dmamap_destroy(t, map);
+}
+
+/*
+ * Load an ISA DMA map with a linear buffer.
+ */
+int
+_isa_bus_dmamap_load(t, map, buf, buflen, p, flags)
+ bus_dma_tag_t t;
+ bus_dmamap_t map;
+ void *buf;
+ bus_size_t buflen;
+ struct proc *p;
+ int flags;
+{
+ struct i386_isa_dma_cookie *cookie = map->_dm_cookie;
+ int error;
+
+ STAT_INCR(isa_dma_stats_loads);
+
+ /*
+ * Check to see if we might need to bounce the transfer.
+ */
+ if (cookie->id_flags & ID_MIGHT_NEED_BOUNCE) {
+ /*
+ * Check if all pages are below the bounce
+ * threshold. If they are, don't bother bouncing.
+ */
+ if (_isa_dma_check_buffer(buf, buflen,
+ map->_dm_segcnt, map->_dm_boundary, p) == 0)
+ return (_bus_dmamap_load(t, map, buf, buflen,
+ p, flags));
+
+ STAT_INCR(isa_dma_stats_bounces);
+
+ /*
+ * Allocate bounce pages, if necessary.
+ */
+ if ((cookie->id_flags & ID_HAS_BOUNCE) == 0) {
+ error = _isa_dma_alloc_bouncebuf(t, map, buflen,
+ flags);
+ if (error)
+ return (error);
+ }
+
+ /*
+ * Cache a pointer to the caller's buffer and
+ * load the DMA map with the bounce buffer.
+ */
+ cookie->id_origbuf = buf;
+ cookie->id_origbuflen = buflen;
+ error = _bus_dmamap_load(t, map, cookie->id_bouncebuf,
+ buflen, p, flags);
+
+ if (error) {
+ /*
+ * Free the bounce pages, unless our resources
+ * are reserved for our exclusive use.
+ */
+ if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0)
+ _isa_dma_free_bouncebuf(t, map);
+ }
+
+ /* ...so _isa_bus_dmamap_sync() knows we're bouncing */
+ cookie->id_flags |= ID_IS_BOUNCING;
+ } else {
+ /*
+ * Just use the generic load function.
+ */
+ error = _bus_dmamap_load(t, map, buf, buflen, p, flags);
+ }
+
+ return (error);
+}
+
+/*
+ * Like _isa_bus_dmamap_load(), but for mbufs.
+ */
+int
+_isa_bus_dmamap_load_mbuf(t, map, m, flags)
+ bus_dma_tag_t t;
+ bus_dmamap_t map;
+ struct mbuf *m;
+ int flags;
+{
+
+ panic("_isa_bus_dmamap_load_mbuf: not implemented");
+}
+
+/*
+ * Like _isa_bus_dmamap_load(), but for uios.
+ */
+int
+_isa_bus_dmamap_load_uio(t, map, uio, flags)
+ bus_dma_tag_t t;
+ bus_dmamap_t map;
+ struct uio *uio;
+ int flags;
+{
+
+ panic("_isa_bus_dmamap_load_uio: not implemented");
+}
+
+/*
+ * Like _isa_bus_dmamap_load(), but for raw memory allocated with
+ * bus_dmamem_alloc().
+ */
+int
+_isa_bus_dmamap_load_raw(t, map, segs, nsegs, size, flags)
+ bus_dma_tag_t t;
+ bus_dmamap_t map;
+ bus_dma_segment_t *segs;
+ int nsegs;
+ bus_size_t size;
+ int flags;
+{
+
+ panic("_isa_bus_dmamap_load_raw: not implemented");
+}
+
+/*
+ * Unload an ISA DMA map.
+ */
+void
+_isa_bus_dmamap_unload(t, map)
+ bus_dma_tag_t t;
+ bus_dmamap_t map;
+{
+ struct i386_isa_dma_cookie *cookie = map->_dm_cookie;
+
+ /*
+ * If we have bounce pages, free them, unless they're
+ * reserved for our exclusive use.
+ */
+ if ((cookie->id_flags & ID_HAS_BOUNCE) &&
+ (map->_dm_flags & BUS_DMA_ALLOCNOW) == 0)
+ _isa_dma_free_bouncebuf(t, map);
+
+ cookie->id_flags &= ~ID_IS_BOUNCING;
+
+ /*
+ * Do the generic bits of the unload.
+ */
+ _bus_dmamap_unload(t, map);
+}
+
+/*
+ * Synchronize an ISA DMA map.
+ */
+void
+_isa_bus_dmamap_sync(t, map, offset, len, op)
+ bus_dma_tag_t t;
+ bus_dmamap_t map;
+ bus_addr_t offset;
+ bus_size_t len;
+ int op;
+{
+ struct i386_isa_dma_cookie *cookie = map->_dm_cookie;
+
+#ifdef DEBUG
+ if ((op & (BUS_DMASYNC_PREWRITE|BUS_DMASYNC_POSTREAD)) != 0) {
+ if (offset >= map->dm_mapsize)
+ panic("_isa_bus_dmamap_sync: bad offset");
+ if (len == 0 || (offset + len) > map->dm_mapsize)
+ panic("_isa_bus_dmamap_sync: bad length");
+ }
+#endif
+
+ switch (op) {
+ case BUS_DMASYNC_PREREAD:
+ /*
+ * Nothing to do for pre-read.
+ */
+ break;
+
+ case BUS_DMASYNC_PREWRITE:
+ /*
+ * If we're bouncing this transfer, copy the
+ * caller's buffer to the bounce buffer.
+ */
+ if (cookie->id_flags & ID_IS_BOUNCING)
+ bcopy(cookie->id_origbuf + offset,
+ cookie->id_bouncebuf + offset,
+ len);
+ break;
+
+ case BUS_DMASYNC_POSTREAD:
+ /*
+ * If we're bouncing this transfer, copy the
+ * bounce buffer to the caller's buffer.
+ */
+ if (cookie->id_flags & ID_IS_BOUNCING)
+ bcopy(cookie->id_bouncebuf + offset,
+ cookie->id_origbuf + offset,
+ len);
+ break;
+
+ case BUS_DMASYNC_POSTWRITE:
+ /*
+ * Nothing to do for post-write.
+ */
+ break;
+ }
+
+#if 0
+ /* This is a noop anyhow, so why bother calling it? */
+ _bus_dmamap_sync(t, map, op);
+#endif
+}
+
+/*
+ * Allocate memory safe for ISA DMA.
+ */
+int
+_isa_bus_dmamem_alloc(t, size, alignment, boundary, segs, nsegs, rsegs, flags)
+ bus_dma_tag_t t;
+ bus_size_t size, alignment, boundary;
+ bus_dma_segment_t *segs;
+ int nsegs;
+ int *rsegs;
+ int flags;
+{
+ paddr_t high;
+
+ if (avail_end > ISA_DMA_BOUNCE_THRESHOLD)
+ high = trunc_page(ISA_DMA_BOUNCE_THRESHOLD);
+ else
+ high = trunc_page(avail_end);
+
+ return (_bus_dmamem_alloc_range(t, size, alignment, boundary,
+ segs, nsegs, rsegs, flags, 0, high));
+}
+
+/*
+ * Free memory safe for ISA DMA.
+ */
+void
+_isa_bus_dmamem_free(t, segs, nsegs)
+ bus_dma_tag_t t;
+ bus_dma_segment_t *segs;
+ int nsegs;
+{
+
+ _bus_dmamem_free(t, segs, nsegs);
+}
+
+/*
+ * Map ISA DMA-safe memory into kernel virtual address space.
+ */
+int
+_isa_bus_dmamem_map(t, segs, nsegs, size, kvap, flags)
+ bus_dma_tag_t t;
+ bus_dma_segment_t *segs;
+ int nsegs;
+ size_t size;
+ caddr_t *kvap;
+ int flags;
+{
+
+ return (_bus_dmamem_map(t, segs, nsegs, size, kvap, flags));
+}
+
+/*
+ * Unmap ISA DMA-safe memory from kernel virtual address space.
+ */
+void
+_isa_bus_dmamem_unmap(t, kva, size)
+ bus_dma_tag_t t;
+ caddr_t kva;
+ size_t size;
+{
+
+ _bus_dmamem_unmap(t, kva, size);
+}
+
+/*
+ * mmap(2) ISA DMA-safe memory.
+ */
+paddr_t
+_isa_bus_dmamem_mmap(t, segs, nsegs, off, prot, flags)
+ bus_dma_tag_t t;
+ bus_dma_segment_t *segs;
+ int nsegs;
+ off_t off;
+ int prot, flags;
+{
+
+ return (_bus_dmamem_mmap(t, segs, nsegs, off, prot, flags));
+}
+
+/**********************************************************************
+ * ISA DMA utility functions
+ **********************************************************************/
+
+/*
+ * Return 0 if all pages in the passed buffer lie within the DMA'able
+ * range RAM.
+ */
+int
+_isa_dma_check_buffer(buf, buflen, segcnt, boundary, p)
+ void *buf;
+ bus_size_t buflen;
+ int segcnt;
+ bus_size_t boundary;
+ struct proc *p;
+{
+ vaddr_t vaddr = (vaddr_t)buf;
+ vaddr_t endva;
+ paddr_t pa, lastpa;
+ u_long pagemask = ~(boundary - 1);
+ pmap_t pmap;
+ int nsegs;
+
+ endva = round_page(vaddr + buflen);
+
+ nsegs = 1;
+ lastpa = 0;
+
+ if (p != NULL)
+ pmap = p->p_vmspace->vm_map.pmap;
+ else
+ pmap = pmap_kernel();
+
+ for (; vaddr < endva; vaddr += NBPG) {
+ /*
+ * Get physical address for this segment.
+ */
+ pmap_extract(pmap, (vaddr_t)vaddr, &pa);
+ pa = trunc_page(pa);
+
+ /*
+ * Is it below the DMA'able threshold?
+ */
+ if (pa > ISA_DMA_BOUNCE_THRESHOLD)
+ return (EINVAL);
+
+ if (lastpa) {
+ /*
+ * Check excessive segment count.
+ */
+ if (lastpa + NBPG != pa) {
+ if (++nsegs > segcnt)
+ return (EFBIG);
+ }
+
+ /*
+ * Check boundary restriction.
+ */
+ if (boundary) {
+ if ((lastpa ^ pa) & pagemask)
+ return (EINVAL);
+ }
+ }
+ lastpa = pa;
+ }
+
+ return (0);
+}
+
+int
+_isa_dma_alloc_bouncebuf(t, map, size, flags)
+ bus_dma_tag_t t;
+ bus_dmamap_t map;
+ bus_size_t size;
+ int flags;
+{
+ struct i386_isa_dma_cookie *cookie = map->_dm_cookie;
+ int error = 0;
+
+ cookie->id_bouncebuflen = round_page(size);
+ error = _isa_bus_dmamem_alloc(t, cookie->id_bouncebuflen,
+ NBPG, map->_dm_boundary, cookie->id_bouncesegs,
+ map->_dm_segcnt, &cookie->id_nbouncesegs, flags);
+ if (error)
+ goto out;
+ error = _isa_bus_dmamem_map(t, cookie->id_bouncesegs,
+ cookie->id_nbouncesegs, cookie->id_bouncebuflen,
+ (caddr_t *)&cookie->id_bouncebuf, flags);
+
+ out:
+ if (error) {
+ _isa_bus_dmamem_free(t, cookie->id_bouncesegs,
+ cookie->id_nbouncesegs);
+ cookie->id_bouncebuflen = 0;
+ cookie->id_nbouncesegs = 0;
+ } else {
+ cookie->id_flags |= ID_HAS_BOUNCE;
+ STAT_INCR(isa_dma_stats_nbouncebufs);
+ }
+
+ return (error);
+}
+
+void
+_isa_dma_free_bouncebuf(t, map)
+ bus_dma_tag_t t;
+ bus_dmamap_t map;
+{
+ struct i386_isa_dma_cookie *cookie = map->_dm_cookie;
+
+ STAT_DECR(isa_dma_stats_nbouncebufs);
+
+ _isa_bus_dmamem_unmap(t, cookie->id_bouncebuf,
+ cookie->id_bouncebuflen);
+ _isa_bus_dmamem_free(t, cookie->id_bouncesegs,
+ cookie->id_nbouncesegs);
+ cookie->id_bouncebuflen = 0;
+ cookie->id_nbouncesegs = 0;
+ cookie->id_flags &= ~ID_HAS_BOUNCE;
+}
+#endif /* NISADMA > 0 */