diff options
author | Michael Shalayeff <mickey@cvs.openbsd.org> | 2004-01-28 01:39:41 +0000 |
---|---|---|
committer | Michael Shalayeff <mickey@cvs.openbsd.org> | 2004-01-28 01:39:41 +0000 |
commit | eb2015b73fc7e8f74be0338c16e873a01653fe03 (patch) | |
tree | a0a1beaa9bc6601b949ea8937d79f939833b3cd3 /sys/arch/amd64/isa | |
parent | fc744b6d0908de21ef8f71c7e15dd3b113e9aad8 (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.c | 765 | ||||
-rw-r--r-- | sys/arch/amd64/isa/isa_machdep.c | 1026 |
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 */ |