From ee69a0b0a093e27b6a0d5e5f33f601fc9d88ca17 Mon Sep 17 00:00:00 2001 From: tv Date: Mon, 8 Jun 1998 17:49:42 +0000 Subject: [PATCH] Add ISA support needed for Shark and the CATS board (CATS isa_machdep not here yet). --- sys/arch/arm32/isa/clock.c | 676 ++++++ sys/arch/arm32/isa/ess.c | 2646 +++++++++++++++++++++++ sys/arch/arm32/isa/essreg.h | 254 +++ sys/arch/arm32/isa/essvar.h | 211 ++ sys/arch/arm32/isa/icu.h | 71 + sys/arch/arm32/isa/if_cs_isa.c | 3093 +++++++++++++++++++++++++++ sys/arch/arm32/isa/if_csvar.h | 420 ++++ sys/arch/arm32/isa/isa_io.c | 290 +++ sys/arch/arm32/isa/isa_io_asm.S | 391 ++++ sys/arch/arm32/isa/isa_irq.S | 585 +++++ sys/arch/arm32/isa/isa_irqhandler.c | 591 +++++ sys/arch/arm32/isa/isa_machdep.h | 65 + sys/arch/arm32/isa/isadma.c | 432 ++++ sys/arch/arm32/isa/isadmavar.h | 18 + sys/arch/arm32/isa/nvram.h | 86 + sys/arch/arm32/isa/spkrreg.h | 11 + sys/arch/arm32/isa/timerreg.h | 100 + 17 files changed, 9940 insertions(+) create mode 100644 sys/arch/arm32/isa/clock.c create mode 100644 sys/arch/arm32/isa/ess.c create mode 100644 sys/arch/arm32/isa/essreg.h create mode 100644 sys/arch/arm32/isa/essvar.h create mode 100644 sys/arch/arm32/isa/icu.h create mode 100644 sys/arch/arm32/isa/if_cs_isa.c create mode 100644 sys/arch/arm32/isa/if_csvar.h create mode 100644 sys/arch/arm32/isa/isa_io.c create mode 100644 sys/arch/arm32/isa/isa_io_asm.S create mode 100644 sys/arch/arm32/isa/isa_irq.S create mode 100644 sys/arch/arm32/isa/isa_irqhandler.c create mode 100644 sys/arch/arm32/isa/isa_machdep.h create mode 100644 sys/arch/arm32/isa/isadma.c create mode 100644 sys/arch/arm32/isa/isadmavar.h create mode 100644 sys/arch/arm32/isa/nvram.h create mode 100644 sys/arch/arm32/isa/spkrreg.h create mode 100644 sys/arch/arm32/isa/timerreg.h diff --git a/sys/arch/arm32/isa/clock.c b/sys/arch/arm32/isa/clock.c new file mode 100644 index 000000000000..cfd568b19f58 --- /dev/null +++ b/sys/arch/arm32/isa/clock.c @@ -0,0 +1,676 @@ +/* $NetBSD: clock.c,v 1.1 1998/06/08 17:49:42 tv Exp $ */ + +/* + * Copyright 1997 + * Digital Equipment Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and conditions. + * Subject to these conditions, you may download, copy, install, + * use, modify and distribute this software in source and/or binary + * form. No title or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions as + * they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or logo of + * Digital Equipment Corporation. Neither the "Digital Equipment + * Corporation" name nor any trademark or logo of Digital Equipment + * Corporation may be used to endorse or promote products derived + * from this software without the prior written permission of + * Digital Equipment Corporation. + * + * 3) This software is provided "AS-IS" and any express or implied + * warranties, including but not limited to, any implied warranties + * of merchantability, fitness for a particular purpose, or + * non-infringement are disclaimed. In no event shall DIGITAL be + * liable for any damages whatsoever, and in particular, DIGITAL + * shall not be liable for special, indirect, consequential, or + * incidental damages or damages for lost profits, loss of + * revenue or loss of use, whether such damages arise in contract, + * negligence, tort, under statute, in equity, at law or otherwise, + * even if advised of the possibility of such damage. + */ + +/*- + * Copyright (c) 1993, 1994 Charles 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. + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef SHARK +#include +#endif + +void sysbeepstop __P((void *)); +void sysbeep __P((int, int)); +void rtcinit __P((void)); +int timer_hz_to_count(int); + +static void findcpuspeed __P((void)); +static void init_isa_timer_tables(); +static void delayloop(int); +static int clockintr __P((void *)); +static int gettick __P((void)); + +__inline u_int mc146818_read __P((void *, u_int)); +__inline void mc146818_write __P((void *, u_int, u_int)); + +#define SECMIN ((unsigned)60) /* seconds per minute */ +#define SECHOUR ((unsigned)(60*SECMIN)) /* seconds per hour */ +#define SECDAY ((unsigned)(24*SECHOUR)) /* seconds per day */ +#define SECYR ((unsigned)(365*SECDAY)) /* seconds per common year */ + +__inline u_int +mc146818_read(sc, reg) + void *sc; /* XXX use it? */ + u_int reg; +{ + + outb(IO_RTC, reg); + 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); + outb(IO_RTC+1, datum); +} + +unsigned int count1024usec; /* calibrated loop variable (1024 microseconds) */ + +/* number of timer ticks in a Musec = 2^20 usecs */ +#define TIMER_MUSECFREQ\ + (((((((TIMER_FREQ) * 1024) + 999) / 1000) * 1024) + 999) / 1000) +#define TIMER_MUSECDIV(x) ((TIMER_MUSECFREQ+(x)/2)/(x)) + +/* + * microtime() makes use of the following globals. + * timer_msb_table[] and timer_lsb_table[] are used to compute the + * microsecond increment. + * + * time.tv_usec += isa_timer_msb_table[cnt_msb] + isa_timer_lsb_table[cnt_lsb]; + */ + +u_short isa_timer_msb_table[256]; /* timer->usec MSB */ +u_short isa_timer_lsb_table[256]; /* timer->usec conversion for LSB */ + +/* 64 bit counts from timer 0 */ +struct count64 { + unsigned lo; /* low 32 bits */ + unsigned hi; /* high 32 bits */ +}; + +#define TIMER0_ROLLOVER 0xFFFF /* maximum rollover for 8254 counter */ + +struct count64 timer0count; +struct count64 timer0_at_last_clockintr; +unsigned timer0last; + +/*#define TESTHAT*/ +#ifdef TESTHAT +#define HATSTACKSIZE 1024 +#define HATHZ 50000 +#define HATHZ2 10000 +unsigned char hatStack[HATSTACKSIZE]; + +unsigned testHatOn = 0; +unsigned nHats = 0; +unsigned nHatWedges = 0; +unsigned fiqReason = 0; +unsigned hatCount = 0; +unsigned hatCount2 = 0; + +void hatTest(int testReason) +{ + fiqReason |= testReason; + nHats++; + +} + +void hatWedge(int nFIQs) +{ + printf("Unwedging the HAT. fiqs_happened = %d\n", nFIQs); + nHatWedges++; +} +#endif + +void +startrtclock() +{ + findcpuspeed(); /* use the clock (while it's free) + to find the cpu speed */ + + init_isa_timer_tables(); + + timer0count.lo = 0; + timer0count.hi = 0; + timer0_at_last_clockintr.lo = 0; + timer0_at_last_clockintr.hi = 0; + timer0last = 0; + + /* initialize 8253 clock */ + outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT); + outb(TIMER_CNTR0, TIMER0_ROLLOVER % 256); + outb(TIMER_CNTR0, TIMER0_ROLLOVER / 256); + +#ifdef TESTHAT + hatCount = timer_hz_to_count(HATHZ); + hatCount2 = timer_hz_to_count(HATHZ2); + printf("HAT test on @ %d Hz = %d ticks\n", HATHZ, hatCount); +#endif +} + +static void +init_isa_timer_tables() +{ + int s; + u_long t, msbmillion, quotient, remainder; + + for (s = 0; s < 256; s++) { + /* LSB table is easy, just divide and round */ + t = ((u_long) s * 1000000 * 2) / TIMER_FREQ; + isa_timer_lsb_table[s] = (u_short) ((t / 2) + (t & 0x1)); + + msbmillion = s * 1000000; + quotient = msbmillion / TIMER_FREQ; + remainder = msbmillion % TIMER_FREQ; + t = (remainder * 256 * 2) / TIMER_FREQ; + isa_timer_msb_table[s] = + (u_short)((t / 2) + (t & 1) + (quotient * 256)); + +#ifdef DIAGNOSTIC + if ((s > 0) && + (isa_timer_msb_table[s] < + (isa_timer_msb_table[s - 1] + isa_timer_lsb_table[0xFF]))) + panic ("time tables not monotonic %d: %d < (%d + %d) = %d\n", + s, isa_timer_msb_table[s], + isa_timer_msb_table[s - 1], + isa_timer_lsb_table[0xFF], + isa_timer_msb_table[s - 1] + + isa_timer_lsb_table[0xFF]); +#endif + } /* END for */ +} + +int +timer_hz_to_count(timer_hz) + int timer_hz; +{ + u_long tval; + + tval = (TIMER_FREQ * 2) / (u_long) timer_hz; + tval = (tval / 2) + (tval & 0x1); + + return (int)tval; + +} + +/* must be called at SPL_CLOCK or higher */ +void gettimer0count(pcount) + struct count64 *pcount; +{ + unsigned current, ticks, oldlo; + + /* + * Latch the current value of the timer and then read it. + * This guarentees an atomic reading of the time. + */ + + current = gettick(); + + if (timer0last >= current) + ticks = timer0last - current; + else + ticks = timer0last + (TIMER0_ROLLOVER - current); + + timer0last = current; + + oldlo = timer0count.lo; + + if (oldlo > (timer0count.lo = oldlo + ticks)) /* carry? */ + timer0count.hi++; + + *pcount = timer0count; +} + +static int +clockintr(arg) + void *arg; +{ + struct clockframe *frame = arg; /* not strictly necessary */ + extern void isa_specific_eoi(int irq); +#ifdef TESTHAT + static int ticks = 0; +#endif +#ifdef SHARK + static int hatUnwedgeCtr = 0; +#endif + + gettimer0count(&timer0_at_last_clockintr); + + mc146818_read(NULL, MC_REGC); /* clear the clock interrupt */ + +#ifdef SHARK + /* check to see if the high-availability timer needs to be unwedged */ + if (++hatUnwedgeCtr >= (hz / HAT_MIN_FREQ)) { + hatUnwedgeCtr = 0; + hatUnwedge(); + } +#endif + +#ifdef TESTHAT + ++ticks; + + if (testHatOn && ((ticks & 0x3f) == 0)) { + if (testHatOn == 1) { + hatClkAdjust(hatCount2); + testHatOn = 2; + } else { + testHatOn = 0; + hatClkOff(); + printf("hat off status: %d %d %x\n", nHats, nHatWedges, fiqReason); + } + } else if (!testHatOn && (ticks & 0x1ff) == 0) { + printf("hat on status: %d %d %x\n", nHats, nHatWedges, fiqReason); + testHatOn = 1; + nHats = 0; + fiqReason = 0; + hatClkOn(hatCount, hatTest, 0xfeedface, + hatStack + HATSTACKSIZE - sizeof(unsigned), + hatWedge); + } +#endif + hardclock(frame); + return(0); +} + +static int +gettick() +{ + u_char lo, hi; + u_int savedints; + + /* Don't want someone screwing with the counter while we're here. */ + savedints = disable_interrupts(I32_bit); + /* Select counter 0 and latch it. */ + outb(TIMER_MODE, TIMER_SEL0 | TIMER_LATCH); + lo = inb(TIMER_CNTR0); + hi = inb(TIMER_CNTR0); + restore_interrupts(savedints); + return ((hi << 8) | lo); +} + +/* modifications from i386 to arm32 isa version: + - removed hardcoded "n -=" values that approximated the time to + calculate delay ticks + - made the time to calculate delay ticks almost negligable. 4 multiplies + = maximum of 12 cycles = 75ns on a slow SA-110, plus a bunch of shifts; + as opposed to 4 multiplies plus a bunch of divides. + - removed i386 assembly language hack + - put code in findcpuspeed that works even if FIRST_GUESS is orders + of magnitude low + - put code in delay() to use delayloop() for short delays + - microtime no longer in assembly language +*/ + +/* + * 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 +delay(n) + unsigned n; +{ + int tick, otick; + int nticks; + + if (n < 100) { + /* it can take a long time (1 usec or longer) just for 1 ISA read, + so it's best not to use the timer for short delays */ + delayloop((n * count1024usec) >> 10); + return; + } + + /* + * Read the counter first, so that the rest of the setup overhead is + * counted. + */ + otick = gettick(); + + /* + * Calculate ((n * TIMER_FREQ) / 1e6) without using floating point and + * without any avoidable overflows. + */ + { + /* a Musec = 2^20 usec */ + int Musec = n >> 20, + usec = n & ((1 << 20) - 1); + nticks + = (Musec * TIMER_MUSECFREQ) + + (usec * (TIMER_MUSECFREQ >> 20)) + + ((usec * ((TIMER_MUSECFREQ & ((1 <<20) - 1)) >>10)) >>10) + + ((usec * (TIMER_MUSECFREQ & ((1 << 10) - 1))) >> 20); + } + + while (nticks > 0) { + tick = gettick(); + if (tick > otick) + nticks -= TIMER0_ROLLOVER - (tick - otick); + else + nticks -= otick - tick; + otick = tick; + } + +} + +void +sysbeepstop(arg) + void *arg; +{ +} + +void +sysbeep(pitch, period) + int pitch, period; +{ +} + +#define FIRST_GUESS 0x2000 + +static void +findcpuspeed() +{ + int ticks; + unsigned int guess = FIRST_GUESS; + + while (1) { /* loop until accurate enough */ + /* Put counter in count down mode */ + outb(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN); + outb(TIMER_CNTR0, 0xff); + outb(TIMER_CNTR0, 0xff); + delayloop(guess); + + /* Read the value left in the counter */ + /* + * Formula for delaycount is: + * (loopcount * timer clock speed) / (counter ticks * 1000) + */ + ticks = 0xFFFF - gettick(); + if (ticks == 0) ticks = 1; /* just in case */ + if (ticks < (TIMER_MUSECDIV(1024))) { /* not accurate enough */ + guess *= max(2, (TIMER_MUSECDIV(1024) / ticks)); + continue; + } + count1024usec = (guess * (TIMER_MUSECDIV(1024))) / ticks; + return; + } +} + +static void +delayloop(counts) +{ + while (counts--); +} + +void +cpu_initclocks() +{ + unsigned hzval; + + printf("clock: hz=%d stathz = %d profhz = %d\n", hz, stathz, profhz); + + /* install RTC interrupt handler */ + (void)isa_intr_establish(NULL, IRQ_RTC, IST_LEVEL, IPL_CLOCK, + clockintr, 0); + + /* code for values of hz that don't divide 1000000 exactly */ + tickfix = 1000000 - (hz * tick); + if (tickfix) { + int ftp; + + ftp = min(ffs(tickfix), ffs(hz)); + tickfix >>= (ftp - 1); + tickfixinterval = hz >> (ftp - 1); + } + + /* set up periodic interrupt @ hz + this is the subset of hz values in kern_clock.c that are + supported by the ISA RTC */ + switch (hz) { + case 64: + hzval = MC_RATE_64_Hz; + break; + case 128: + hzval = MC_RATE_128_Hz; + break; + case 256: + hzval = MC_RATE_256_Hz; + break; + case 1024: + hzval = MC_RATE_1024_Hz; + break; + default: + panic("cannot configure hz = %d\n", hz); + } + + rtcinit(); /* make sure basics are done by now */ + + /* blast values to set up clock interrupt */ + mc146818_write(NULL, MC_REGA, MC_BASE_32_KHz | hzval); + /* enable periodic interrupt */ + mc146818_write(NULL, MC_REGB, + mc146818_read(NULL, MC_REGB) | MC_REGB_PIE); +} + +void +rtcinit() +{ + static int first_rtcopen_ever = 1; + + if (!first_rtcopen_ever) + return; + first_rtcopen_ever = 0; + + mc146818_write(NULL, MC_REGA, /* XXX softc */ + MC_BASE_32_KHz | MC_RATE_1024_Hz); + mc146818_write(NULL, MC_REGB, MC_REGB_24HR); /* XXX softc */ +} + +void +setstatclockrate(arg) + int arg; +{ +} + +/* + * void microtime(struct timeval *tvp) + * + * Fill in the specified timeval struct with the current time + * accurate to the microsecond. + */ + +void +microtime(tvp) + struct timeval *tvp; +{ + int s; + unsigned lsb, msb; + int tm; + static struct timeval oldtv; + struct count64 timer0current; + int ticks; + + s = splstatclock(); + + gettimer0count(&timer0current); + + tm = time.tv_usec; + + /* unsigned arithmetic should take care of overflow */ + /* with a >= 32 Hz clock, ticks will always be < 0x7FFF */ + ticks = (int)((unsigned) + (timer0current.lo - timer0_at_last_clockintr.lo)); + +#ifdef DIAGNOSTIC + if ((ticks < 0) || (ticks > 0xffff)) + printf("microtime bug: ticks = %x\n", ticks); +#endif + + while (ticks > 0) { + + if (ticks < 0xffff) { + msb = (ticks >> 8) & 0xFF; + lsb = ticks & 0xFF; + } else { + msb = 0xff; + lsb = 0xff; + } + + /* see comments above */ + tm += isa_timer_msb_table[msb] + isa_timer_lsb_table[lsb]; + + /* for a 64 Hz RTC, ticks will never overflow table */ + /* microtime will be less accurate if the RTC is < 36 Hz */ + ticks -= 0xffff; + } + + tvp->tv_sec = time.tv_sec; + if (tm > 1000000) { + tvp->tv_sec += 1; + tm -= 1000000; + } + + tvp->tv_usec = tm; + + /* Make sure the time has advanced. */ + + if (tvp->tv_sec == oldtv.tv_sec && + tvp->tv_usec <= oldtv.tv_usec) { + tvp->tv_usec = oldtv.tv_usec + 1; + if (tvp->tv_usec > 1000000) { + tvp->tv_usec -= 1000000; + ++tvp->tv_sec; + } + } + + oldtv = *tvp; + (void)splx(s); +} + +void +need_proftick(p) + struct proc *p; +{ +} + +/* End of clock.c */ diff --git a/sys/arch/arm32/isa/ess.c b/sys/arch/arm32/isa/ess.c new file mode 100644 index 000000000000..2c278921e3f1 --- /dev/null +++ b/sys/arch/arm32/isa/ess.c @@ -0,0 +1,2646 @@ +/* $NetBSD: ess.c,v 1.1 1998/06/08 17:49:42 tv Exp $ */ + +/* + * Copyright 1997 + * Digital Equipment Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and conditions. + * Subject to these conditions, you may download, copy, install, + * use, modify and distribute this software in source and/or binary + * form. No title or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions as + * they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or logo of + * Digital Equipment Corporation. Neither the "Digital Equipment + * Corporation" name nor any trademark or logo of Digital Equipment + * Corporation may be used to endorse or promote products derived + * from this software without the prior written permission of + * Digital Equipment Corporation. + * + * 3) This software is provided "AS-IS" and any express or implied + * warranties, including but not limited to, any implied warranties + * of merchantability, fitness for a particular purpose, or + * non-infringement are disclaimed. In no event shall DIGITAL be + * liable for any damages whatsoever, and in particular, DIGITAL + * shall not be liable for special, indirect, consequential, or + * incidental damages or damages for lost profits, loss of + * revenue or loss of use, whether such damages arise in contract, + * negligence, tort, under statute, in equity, at law or otherwise, + * even if advised of the possibility of such damage. + */ + +#if 0 +static char *rcsid = "@(#) $RCSfile: ess.c,v $ $Revision: 1.1 $ (SHARK) $Date: 1998/06/08 17:49:42 $"; +#endif + +/* +**++ +** +** ess.c +** +** FACILITY: +** +** DIGITAL Network Appliance Reference Design (DNARD) +** +** MODULE DESCRIPTION: +** +** This module contains the device driver for the ESS +** Technologies 1888/1887/888 sound chip. The code in sbdsp.c was +** used as a reference point when implementing this driver. +** +** AUTHORS: +** +** Blair Fidler Software Engineering Australia +** Gold Coast, Australia. +** +** CREATION DATE: +** +** March 10, 1997. +** +** MODIFICATION HISTORY: +** +**-- +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#ifdef AUDIO_DEBUG +extern void Dprintf __P((const char *, ...)); +#define DPRINTF(x) if (essdebug) Dprintf x +int essdebug = 0; +#else +#define DPRINTF(x) +#endif + +struct { + u_int wdsp; + u_int rdsp; +} eserr; + +struct cfattach ess_ca = { + sizeof(struct ess_softc), ess_probe, ess_attach +}; + +struct cfdriver ess_cd = { + NULL, "ess", DV_DULL +}; + +static char *essmodel[] = { + "unsupported", + "1888", + "1887", + "888" +}; + +struct audio_device ess_device = { + "ESS Technology", + "x", + "ess" +}; + +int ess_config_addr __P((struct ess_softc *)); +void ess_config_intr __P((struct ess_softc *)); +void ess_identify __P((struct ess_softc *)); + +int ess_reset __P((struct ess_softc *)); +void ess_set_gain __P((struct ess_softc *, int, int)); +int ess_set_in_ports __P((struct ess_softc *, int)); +void ess_speaker_on __P((struct ess_softc *)); +void ess_speaker_off __P((struct ess_softc *)); +u_int ess_srtotc __P((u_int)); +u_int ess_srtofc __P((u_int)); +u_char ess_get_dsp_status __P((struct ess_softc *)); +u_char ess_dsp_read_ready __P((struct ess_softc *)); +u_char ess_dsp_write_ready __P((struct ess_softc *sc)); +int ess_rdsp __P((struct ess_softc *)); +int ess_wdsp __P((struct ess_softc *, u_char)); +u_char ess_read_x_reg __P((struct ess_softc *, u_char)); +int ess_write_x_reg __P((struct ess_softc *, u_char, u_char)); +void ess_clear_xreg_bits __P((struct ess_softc *, u_char, u_char)); +void ess_set_xreg_bits __P((struct ess_softc *, u_char, u_char)); +u_char ess_read_mix_reg __P((struct ess_softc *, u_char)); +void ess_write_mix_reg __P((struct ess_softc *, u_char, u_char)); +void ess_clear_mreg_bits __P((struct ess_softc *, u_char, u_char)); +void ess_set_mreg_bits __P((struct ess_softc *, u_char, u_char)); + + +/* + * Define our interface to the higher level audio driver. + */ + +struct audio_hw_if ess_hw_if = { + ess_open, + ess_close, + NULL, + ess_query_encoding, + ess_set_params, + ess_round_blocksize, + ess_set_out_port, + ess_get_out_port, + ess_set_in_port, + ess_get_in_port, + ess_commit_settings, + ess_dma_output, + ess_dma_input, + ess_halt_output, + ess_halt_input, + ess_cont_output, + ess_cont_input, + ess_speaker_ctl, + ess_getdev, + ess_setfd, + ess_set_port, + ess_get_port, + ess_query_devinfo, + 1, /* full-duplex */ + 0 +}; + +#ifdef AUDIO_DEBUG +void ess_printsc __P((struct ess_softc *)); +void ess_dump_mixer __P((struct ess_softc *)); + +void +ess_printsc(sc) + struct ess_softc *sc; +{ + int i; + + printf("open %d iobase 0x%x outport %u inport %u speaker %s\n", + (int)sc->sc_open, sc->sc_iobase, sc->out_port, + sc->in_port, sc->spkr_state ? "on" : "off"); + + printf("play: dmachan %d irq %d nintr %lu intr %p arg %p\n", + sc->sc_out.drq, sc->sc_out.irq, sc->sc_out.nintr, + sc->sc_out.intr, sc->sc_out.arg); + + printf("record: dmachan %d irq %d nintr %lu intr %p arg %p\n", + sc->sc_in.drq, sc->sc_in.irq, sc->sc_in.nintr, + sc->sc_in.intr, sc->sc_in.arg); + + printf("gain:"); + for (i = 0; i < ESS_NDEVS; i++) + printf(" %u,%u", sc->gain[i][ESS_LEFT], sc->gain[i][ESS_RIGHT]); + printf("\n"); +} + +void +ess_dump_mixer(struct ess_softc *sc) +{ + int gain, left, right; + int src; + int stereo; + + printf("ESS_DAC_PLAY_VOL: mix reg 0x%02x=0x%02x\n", + 0x7C, ess_read_mix_reg(sc, 0x7C)); + printf("ESS_MIC_PLAY_VOL: mix reg 0x%02x=0x%02x\n", + 0x1A, ess_read_mix_reg(sc, 0x1A)); + printf("ESS_LINE_PLAY_VOL: mix reg 0x%02x=0x%02x\n", + 0x3E, ess_read_mix_reg(sc, 0x3E)); + printf("ESS_SYNTH_PLAY_VOL: mix reg 0x%02x=0x%02x\n", + 0x36, ess_read_mix_reg(sc, 0x36)); + printf("ESS_CD_PLAY_VOL: mix reg 0x%02x=0x%02x\n", + 0x38, ess_read_mix_reg(sc, 0x38)); + printf("ESS_AUXB_PLAY_VOL: mix reg 0x%02x=0x%02x\n", + 0x3A, ess_read_mix_reg(sc, 0x3A)); + printf("ESS_MASTER_VOL: mix reg 0x%02x=0x%02x\n", + 0x32, ess_read_mix_reg(sc, 0x32)); + printf("ESS_PCSPEAKER_VOL: mix reg 0x%02x=0x%02x\n", + 0x3C, ess_read_mix_reg(sc, 0x3C)); + printf("ESS_DAC_REC_VOL: mix reg 0x%02x=0x%02x\n", + 0x69, ess_read_mix_reg(sc, 0x69)); + printf("ESS_MIC_REC_VOL: mix reg 0x%02x=0x%02x\n", + 0x68, ess_read_mix_reg(sc, 0x68)); + printf("ESS_LINE_REC_VOL: mix reg 0x%02x=0x%02x\n", + 0x6E, ess_read_mix_reg(sc, 0x6E)); + printf("ESS_SYNTH_REC_VOL: mix reg 0x%02x=0x%02x\n", + 0x6B, ess_read_mix_reg(sc, 0x6B)); + printf("ESS_CD_REC_VOL: mix reg 0x%02x=0x%02x\n", + 0x6A, ess_read_mix_reg(sc, 0x6A)); + printf("ESS_AUXB_REC_VOL: mix reg 0x%02x=0x%02x\n", + 0x6C, ess_read_mix_reg(sc, 0x6C)); + printf("ESS_RECORD_VOL: x reg 0x%02x=0x%02x\n", + 0xB4, ess_read_x_reg(sc, 0xB4)); + printf("Audio 1 play vol (unused): mix reg 0x%02x=0x%02x\n", + 0x14, ess_read_mix_reg(sc, 0x14)); + + printf("ESS_MIC_PREAMP: x reg 0x%02x=0x%02x\n", + ESS_XCMD_PREAMP_CTRL, ess_read_x_reg(sc, ESS_XCMD_PREAMP_CTRL)); + printf("ESS_RECORD_MONITOR: x reg 0x%02x=0x%02x\n", + ESS_XCMD_AUDIO_CTRL, ess_read_x_reg(sc, ESS_XCMD_AUDIO_CTRL)); + printf("Record source: mix reg 0x%02x=0x%02x, 0x%02x=0x%02x\n", + 0x1c, ess_read_mix_reg(sc, 0x1c), + 0x7a, ess_read_mix_reg(sc, 0x7a)); +} + +#endif + +/* + * Configure the ESS chip for the desired audio base address. + */ +int +ess_config_addr(sc) + struct ess_softc *sc; +{ + register int iobase = sc->sc_iobase; + register bus_space_tag_t iot = sc->sc_iot; + +#ifdef ESS_AMODE_LOW + /* + * Configure using the Read-Sequence-Key method. This method + * is used when the AMODE line is tied low, which is the case + * for the evaluation board, but not for the Shark. First we + * read a magic sequence of registers, then we read from the + * desired base addresses. See page 21 of ES1887 data sheet + * for details. + */ + + bus_space_handle_t ioh; + + /* + * Get a mapping for the configuration key registers. + */ + if (bus_space_map(iot, ESS_CONFIG_KEY_BASE, ESS_CONFIG_KEY_PORTS, + 0, &ioh)) + { + printf("ess: can't map configuration key registers\n"); + return -1; + } + + /* + * Read the magic key sequence. + */ + bus_space_read_1(iot, ioh, 0); + bus_space_read_1(iot, ioh, 0); + bus_space_read_1(iot, ioh, 0); + + bus_space_read_1(iot, ioh, 2); + bus_space_read_1(iot, ioh, 0); + bus_space_read_1(iot, ioh, 2); + bus_space_read_1(iot, ioh, 0); + bus_space_read_1(iot, ioh, 0); + bus_space_read_1(iot, ioh, 2); + bus_space_read_1(iot, ioh, 0); + + /* + * Unmap the configuration key registers. + */ + bus_space_unmap(iot, ioh, ESS_CONFIG_KEY_PORTS); + + + /* + * Get a mapping for the audio base address. + */ + if (bus_space_map(iot, iobase, 1, 0, &ioh)) + { + printf("ess: can't map audio base address (0x%x)\n", iobase); + return -1; + } + + /* + * Read from the audio base address. + */ + bus_space_read_1(iot, ioh, 0); + + /* + * Unmap the audio base address + */ + bus_space_unmap(iot, ioh, 1); +#else + /* + * Configure using the System Control Register method. This + * method is used when the AMODE line is tied high, which is + * the case for the Shark, but not for the evaluation board. + */ + + bus_space_handle_t scr_access_ioh; + bus_space_handle_t scr_ioh; + u_short scr_value = 0; + + /* + * Set the SCR bit to enable audio. + */ + scr_value |= ESS_SCR_AUDIO_ENABLE; + + /* + * Set the SCR bits necessary to select the specified audio + * base address. + */ + switch(iobase) + { + case 0x220: + scr_value |= ESS_SCR_AUDIO_220; + break; + + case 0x230: + scr_value |= ESS_SCR_AUDIO_230; + break; + case 0x240: + scr_value |= ESS_SCR_AUDIO_240; + break; + case 0x250: + scr_value |= ESS_SCR_AUDIO_250; + break; + default: + printf("ess: configured iobase 0x%x invalid\n", iobase); + return -1; + break; + } + + /* + * Get a mapping for the System Control Register (SCR) access + * registers and the SCR data registers. + */ + if (bus_space_map(iot, ESS_SCR_ACCESS_BASE, ESS_SCR_ACCESS_PORTS, + 0, &scr_access_ioh)) + { + printf("ess: can't map SCR access registers\n"); + return -1; + } + if (bus_space_map(iot, ESS_SCR_BASE, ESS_SCR_PORTS, + 0, &scr_ioh)) + { + printf("ess: can't map SCR registers\n"); + bus_space_unmap(iot, scr_access_ioh, ESS_SCR_ACCESS_PORTS); + return -1; + } + + /* + * Unlock the SCR. + */ + bus_space_write_1(iot, scr_access_ioh, ESS_SCR_UNLOCK, 0); + + /* + * Write the base address information into SCR[0]. + */ + bus_space_write_1(iot, scr_ioh, ESS_SCR_INDEX, 0); + bus_space_write_1(iot, scr_ioh, ESS_SCR_DATA, scr_value); + + /* + * Lock the SCR. + */ + bus_space_write_1(iot, scr_access_ioh, ESS_SCR_LOCK, 0); + + /* + * Unmap the SCR access ports and the SCR data ports. + */ + bus_space_unmap(iot, scr_access_ioh, ESS_SCR_ACCESS_PORTS); + bus_space_unmap(iot, scr_ioh, ESS_SCR_PORTS); +#endif + + return 0; +} + + +/* + * Configure the ESS chip for the desired IRQ and DMA channels. + */ +void +ess_config_intr(sc) + struct ess_softc *sc; +{ + /* + * Configure Audio 1 (record) for the appropriate IRQ line. + */ + switch(sc->sc_in.irq) + { + case 9: + ess_wdsp(sc, 0xb1); + ess_wdsp(sc, 0x50); + break; + case 5: + ess_wdsp(sc, 0xb1); + ess_wdsp(sc, 0x54); + break; + case 7: + ess_wdsp(sc, 0xb1); + ess_wdsp(sc, 0x58); + break; + case 10: + ess_wdsp(sc, 0xb1); + ess_wdsp(sc, 0x5c); + break; + default: + printf("ess: configured irq chan %d not supported for Audio 1\n", sc->sc_in.irq); + break; + } + + + /* REVISIT: tidy up following which enables DMA, IRQ for Audio 2 */ + /* note that for 1888/888 IRQ must be 15 and DRQ must be 5 */ + ess_set_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, 0x60); + +#if 0 + /* + * REVISIT: Only the ES1887 supports register 0x7d - for all + * other chips, DRQ is hardwired to 5 anyway. + */ + /* REVISIT: tidy up following which hardcodes DRQ to 5 */ + if (sc->sc_model = ESS_1887) + { + ess_set_mreg_bits(sc, 0x7d, 0x03); + } + + /* REVISIT: Only the ES1887 supports register 0x7f */ + /* REVISIT: tidy up following which hardcodes IRQ to 15 for Audio 2*/ + ess_set_mreg_bits(sc, 0x7f, 0x03); + ess_clear_mreg_bits(sc, 0x7f, 0x0c); +#endif + + /* + * Configure Audio 1 (record) for DMA on the appropriate + * channel. + */ + /* + * REVISIT: Does bit 4 really need to be set? Reading the data + * sheet, it seems that this is only necessary for + * compatibility mode. + */ + switch(sc->sc_in.drq) + { + case 0: + ess_wdsp(sc, 0xb2); + ess_wdsp(sc, 0x54); + break; + case 1: + ess_wdsp(sc, 0xb2); + ess_wdsp(sc, 0x58); + break; + case 3: + ess_wdsp(sc, 0xb2); + ess_wdsp(sc, 0x5c); + break; + default: + printf("essdsp: configured dma chan %d not supported for Audio 1\n", sc->sc_in.drq); + break; + } +} + + +/* + * Determine the model of ESS chip we are talking to. Currently we + * only support ES1888, ES1887 and ES888. The method of determining + * the chip is based on the information on page 27 of the ES1887 data + * sheet. + * + * This routine sets the values of sc->sc_model and sc->sc_version. + */ +void +ess_identify(sc) + struct ess_softc *sc; +{ + u_char reg1; + u_char reg2; + u_char reg3; + + sc->sc_model = ESS_UNSUPPORTED; + sc->sc_version = 0; + + + /* + * 1. Check legacy ID bytes. These should be 0x68 0x8n, where + * n >= 8 for an ES1887 or an ES888. Other values indicate + * earlier (unsupported) chips. + */ + ess_wdsp(sc, ESS_ACMD_LEGACY_ID); + + if ((reg1 = ess_rdsp(sc)) != 0x68) + { + printf("ess: First ID byte wrong (0x%02x)\n", reg1); + return; + } + + reg2 = ess_rdsp(sc); + if (((reg2 & 0xf0) != 0x80) || + ((reg2 & 0x0f) < 8)) + { + printf("ess: Second ID byte wrong (0x%02x)\n", reg2); + return; + } + + /* + * Store the ID bytes as the version. + */ + sc->sc_version = (reg1 << 8) + reg2; + + + /* + * 2. Verify we can change bit 2 in mixer register 0x64. This + * should be possible on all supported chips. + */ + reg1 = ess_read_mix_reg(sc, 0x64); + reg2 = reg1 ^ 0x04; /* toggle bit 2 */ + + ess_write_mix_reg(sc, 0x64, reg2); + + if (ess_read_mix_reg(sc, 0x64) != reg2) + { + printf("ess: Hardware error (unable to toggle bit 2 of mixer register 0x64)\n"); + return; + } + + /* + * Restore the original value of mixer register 0x64. + */ + ess_write_mix_reg(sc, 0x64, reg1); + + + /* + * 3. Verify we can change the value of mixer register 0x70. + * This should be possible on all supported chips. + */ + reg1 = ess_read_mix_reg(sc, 0x70); + reg2 = reg1 ^ 0xff; /* toggle all bits */ + + ess_write_mix_reg(sc, 0x70, reg2); + + if (ess_read_mix_reg(sc, 0x70) != reg2) + { + printf("ess: Harware error (unable to change mixer register 0x70)\n"); + return; + } + + /* + * It is not necessary to restore the value of mixer register 0x70. + */ + + + /* + * 4. Determine if we can change bit 5 in mixer register 0x64. + * This determines whether we have an ES1887: + * + * - can change indicates ES1887 + * - can't change indicates ES1888 or ES888 + */ + reg1 = ess_read_mix_reg(sc, 0x64); + reg2 = reg1 ^ 0x20; /* toggle bit 5 */ + + ess_write_mix_reg(sc, 0x64, reg2); + + if (ess_read_mix_reg(sc, 0x64) == reg2) + { + sc->sc_model = ESS_1887; + + /* + * Restore the original value of mixer register 0x64. + */ + ess_write_mix_reg(sc, 0x64, reg1); + } + else + { + /* + * 5. Determine if we can change the value of mixer + * register 0x69 independently of mixer register + * 0x68. This determines which chip we have: + * + * - can modify idependently indicates ES888 + * - register 0x69 is an alias of 0x68 indicates ES1888 + */ + reg1 = ess_read_mix_reg(sc, 0x68); + reg2 = ess_read_mix_reg(sc, 0x69); + reg3 = reg2 ^ 0xff; /* toggle all bits */ + + /* + * Write different values to each register. + */ + ess_write_mix_reg(sc, 0x68, reg2); + ess_write_mix_reg(sc, 0x69, reg3); + + if (ess_read_mix_reg(sc, 0x68) == reg2) + { + sc->sc_model = ESS_888; + } + else + { + sc->sc_model = ESS_1888; + } + + /* + * Restore the original value of the registers. + */ + ess_write_mix_reg(sc, 0x68, reg1); + ess_write_mix_reg(sc, 0x69, reg2); + } + + return; +} + + +/* + * Probe / attach routines. + */ + +/* + * Probe for the ESS hardware. + */ +int +#ifdef __BROKEN_INDIRECT_CONFIG +ess_probe(struct device *parent, void *match, void *aux) +#else +ess_probe(struct device *parent, struct cfdata *cf, void *aux) +#endif +{ +#ifdef __BROKEN_INDIRECT_CONFIG + register struct ess_softc *sc = match; +#else + struct ess_softc tempsc; + register struct ess_softc *sc = &tempsc; +#endif + register struct isa_attach_args *ia = aux; + + if (!ESS_BASE_VALID(ia->ia_iobase)) { + printf("ess: configured iobase 0x%x invalid\n", ia->ia_iobase); + return 0; + } + + + /* + * Copy the appropriate ISA attach arguments to the ess softc + * structure. + */ + sc->sc_iot = ia->ia_iot; + sc->sc_iobase = ia->ia_iobase; + /* REVISIT: how do we properly get IRQ/DRQ for record channel */ + sc->sc_in.irq = 9; + sc->sc_in.drq = 0; + sc->sc_in.mode = ESS_DMA_SIZE(sc->sc_in.drq); + sc->sc_out.irq = ia->ia_irq; + sc->sc_out.drq = ia->ia_drq; + sc->sc_out.mode = ESS_DMA_SIZE(sc->sc_out.drq); + sc->sc_ic = ia->ia_ic; + + /* + * Configure the ESS chip for the desired audio base address. + */ + if (ess_config_addr(sc) != 0) + { + return 0; + } + + /* + * Map the device-specific address space. If the mapping + * fails, then we can't talk to the device. + */ + if (bus_space_map(sc->sc_iot, sc->sc_iobase, + ia->ia_iosize, 0, &(sc->sc_ioh))) + { + printf("ess: can't map I/O space for device\n"); + return 0; + } + + /* + * Reset the chip. + */ + if (ess_reset(sc) < 0) { + DPRINTF(("ess: couldn't reset chip\n")); + return 0; + } + + /* + * Identify the ESS chip, and check that it is supported. + */ + ess_identify(sc); + + if (sc->sc_model == ESS_UNSUPPORTED) + { + DPRINTF(("ess: Unsupported model\n")); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, ia->ia_iosize); + return 0; + } + + /* + * Check that requested DMA channels are valid and different. + */ + if (!ESS_DRQ1_VALID(sc->sc_in.drq,sc->sc_model)) + { + printf("ess: record dma chan %d invalid\n", sc->sc_in.drq); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, ia->ia_iosize); + return 0; + } + if (!ESS_DRQ2_VALID(sc->sc_out.drq,sc->sc_model)) + { + printf("ess: play dma chan %d invalid\n", sc->sc_out.drq); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, ia->ia_iosize); + return 0; + } + if (sc->sc_in.drq == sc->sc_out.drq) + { + printf("ess: play and record dma chan both %d\n", + sc->sc_in.drq); + } + + /* + * Check that requested IRQ lines are valid and differen. + */ + if (!ESS_IRQ1_VALID(sc->sc_in.irq,sc->sc_model)) + { + printf("ess: record irq %d invalid\n", sc->sc_in.irq); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, ia->ia_iosize); + return 0; + } + if (!ESS_IRQ2_VALID(sc->sc_out.irq,sc->sc_model)) + { + printf("ess: play irq %d invalid\n", sc->sc_out.irq); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, ia->ia_iosize); + return 0; + } + if (sc->sc_in.irq == sc->sc_out.irq) + { + printf("ess: play and record irq both %d\n", + sc->sc_in.irq); + } + +#ifndef __BROKEN_INDIRECT_CONFIG + /* + * Unmap the device-specific address space before returning a + * successful probe. This is necessary because we can no + * longer pass the necessary mapping tag and handle to + * ess_attach via the softc structure. Instead, ess_attach + * must re-map the device ports and initialise the softc + * structure itself. + */ + bus_space_unmap(sc->sc_iot, sc->sc_ioh, ia->ia_iosize); +#endif + + return 1; +} + + +/* + * Attach hardware to driver, attach hardware driver to audio + * pseudo-device driver . + */ +void +ess_attach(struct device *parent, + struct device *self, + void *aux) +{ + register struct ess_softc *sc = (struct ess_softc *)self; + struct isa_attach_args *ia = (struct isa_attach_args *)aux; + int err; + struct audio_params xparams; + int i; + u_int v; + +#ifndef __BROKEN_INDIRECT_CONFIG + /* + * Map the device-specific address space. If the mapping + * fails, then we panic because we've already successfully + * mapped these ports when ess_probe was called. + */ + if (bus_space_map(ia->ia_iot, ia->ia_iobase, + ia->ia_iosize, 0, &(sc->sc_ioh))) + { + panic("ess_attach: can't map I/O space for device\n"); + } + + /* + * Copy the appropriate ISA attach arguments to the ess softc + * structure. + */ + sc->sc_iot = ia->ia_iot; + sc->sc_iobase = ia->ia_iobase; + /* REVISIT: how do we properly get IRQ/DRQ for record channel */ + sc->sc_in.irq = 9; + sc->sc_in.drq = 0; + sc->sc_in.mode = ESS_DMA_SIZE(sc->sc_in.drq); + sc->sc_out.irq = ia->ia_irq; + sc->sc_out.drq = ia->ia_drq; + sc->sc_out.mode = ESS_DMA_SIZE(sc->sc_out.drq); + sc->sc_ic = ia->ia_ic; + + /* + * Identify the ESS chip again to fill in the sc_model and + * sc_version fields of ess softc structure. + */ + ess_identify(sc); + +#endif + /* + * Establish interrupt handlers for Audio 1 (record) and + * Audio 2 (playback). (This must be done before configuring + * the chip for interrupts, otherwise we migt get a stray + * interrupt. + */ + sc->sc_out.ih = isa_intr_establish(ia->ia_ic, sc->sc_out.irq, + IST_LEVEL, IPL_AUDIO, + ess_intr_output, sc); + sc->sc_in.ih = isa_intr_establish(ia->ia_ic, sc->sc_in.irq, + IST_LEVEL, IPL_AUDIO, + ess_intr_input, sc); + + printf(" ESS Technology ES%s [version 0x%04x]\n", essmodel[sc->sc_model], sc->sc_version); + + /* + * Set record and play parameters to default values defined in + * generic audio driver. + */ + ess_set_params(sc, AUMODE_RECORD, &audio_default, &xparams); + ess_set_params(sc, AUMODE_PLAY, &audio_default, &xparams); + + /* + * Do a hardware reset on the mixer. + */ + ess_write_mix_reg(sc, ESS_MIX_RESET, ESS_MIX_RESET); + + /* + * Set volume of Audio 1 to zero and disable Audio 1 DAC input + * to playback mixer, since playback is always through Audio 2. + */ + ess_write_mix_reg(sc, 0x14, 0); + ess_wdsp(sc, ESS_ACMD_DISABLE_SPKR); + + /* + * Set hardware record source to use output of the record + * mixer. We do the selection of record source in software by + * setting the gain of the unused sources to zero. (See + * ess_set_in_ports.) + */ + ess_set_mreg_bits(sc, 0x1c, 0x07); + ess_clear_mreg_bits(sc, 0x7a, 0x10); + ess_set_mreg_bits(sc, 0x7a, 0x08); + + /* + * Set gain on each mixer device to a sensible value. + * Devices not normally used are turned off, and other devices + * are set to 75% volume. + */ + for (i = 0; i < ESS_NDEVS; i++) { + switch(i) { + case ESS_MIC_PLAY_VOL: + case ESS_LINE_PLAY_VOL: + case ESS_CD_PLAY_VOL: + case ESS_AUXB_PLAY_VOL: + case ESS_DAC_REC_VOL: + case ESS_LINE_REC_VOL: + case ESS_SYNTH_REC_VOL: + case ESS_CD_REC_VOL: + case ESS_AUXB_REC_VOL: + v = 0; + break; + default: + v = ESS_4BIT_GAIN(AUDIO_MAX_GAIN * 3 / 4); + break; + } + sc->gain[i][ESS_LEFT] = sc->gain[i][ESS_RIGHT] = v; + ess_set_gain(sc, i, 1); + } + + /* + * Set the default input and output devices. + */ + ess_set_in_port(sc, ESS_MIC_REC_VOL); + ess_set_out_port(sc, ESS_MASTER_VOL); + + /* + * Disable the speaker until the device is opened. + */ + ess_speaker_off(sc); + sc->spkr_state = SPKR_OFF; + + sprintf(ess_device.name, "ES%s", essmodel[sc->sc_model]); + sprintf(ess_device.version, "0x%04x", sc->sc_version); + + if ((err = audio_hardware_attach(&ess_hw_if, sc)) != 0) + printf("ess: could not attach to audio pseudo-device driver (%d)\n", err); +} + +/* + * Various routines to interface to higher level audio driver + */ + +int +ess_open(dev_t dev, + int flags) +{ + struct ess_softc *sc; + int unit = AUDIOUNIT(dev); + + DPRINTF(("ess_open: sc=0x%x\n", sc)); + + if (unit >= ess_cd.cd_ndevs) + return ENODEV; + + sc = ess_cd.cd_devs[unit]; + + if (!sc || sc->sc_open != 0 || ess_reset(sc) != 0) + return ENXIO; + + /* + * Configure the ESS chip for the desired IRQ and DMA channel. + */ + ess_config_intr(sc); + + sc->sc_open = 1; + sc->sc_mintr = 0; + + /* + * Leave most things as they were; users must change things if + * the previous process didn't leave it they way they wanted. + * Looked at another way, it's easy to set up a configuration + * in one program and leave it for another to inherit. + */ + DPRINTF(("ess_open: opened\n")); + + return 0; +} + +void +ess_close(void *addr) +{ + /* REVISIT: currently just copied from sbdsp.c */ + struct ess_softc *sc = addr; + + DPRINTF(("ess_close: sc=%p\n", sc)); + + sc->sc_open = 0; + ess_speaker_off(sc); + sc->spkr_state = SPKR_OFF; + sc->sc_in.intr = 0; + sc->sc_out.intr = 0; + sc->sc_mintr = 0; + ess_halt_output(sc); + ess_halt_input(sc); + + DPRINTF(("ess_close: closed\n")); +} + +int +ess_getdev(void *addr, + struct audio_device *retp) +{ + *retp = ess_device; + return 0; +} + +int +ess_query_encoding(void *addr, + struct audio_encoding *fp) +{ + struct ess_softc *sc = addr; + + /* + * REVISIT: Review the following, esp to check if there should + * be other cases added to the switch. + */ + switch (fp->index) { + case 0: + strcpy(fp->name, AudioEulinear); + fp->encoding = AUDIO_ENCODING_ULINEAR; + fp->precision = 8; + fp->flags = 0; + return 0; + case 1: + strcpy(fp->name, AudioEmulaw); + fp->encoding = AUDIO_ENCODING_ULAW; + fp->precision = 8; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return 0; + case 2: + strcpy(fp->name, AudioElinear); + fp->encoding = AUDIO_ENCODING_LINEAR; + fp->precision = 8; + fp->flags = 0; + return 0; + case 3: + strcpy(fp->name, AudioElinear_le); + fp->encoding = AUDIO_ENCODING_LINEAR_LE; + fp->precision = 16; + fp->flags = 0; + return 0; + case 4: + strcpy(fp->name, AudioEulinear_le); + fp->encoding = AUDIO_ENCODING_ULINEAR_LE; + fp->precision = 16; + fp->flags = 0; + return 0; + case 5: + strcpy(fp->name, AudioElinear_be); + fp->encoding = AUDIO_ENCODING_LINEAR_BE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return 0; + case 6: + strcpy(fp->name, AudioEulinear_be); + fp->encoding = AUDIO_ENCODING_ULINEAR_BE; + fp->precision = 16; + fp->flags = AUDIO_ENCODINGFLAG_EMULATED; + return 0; + default: + return EINVAL; + } + return 0; +} + +int +ess_set_params(void *addr, + int mode, + struct audio_params *p, + struct audio_params *q) +{ + struct ess_softc *sc = addr; + void (*swcode) __P((void *, u_char *buf, int cnt)); + + switch (mode) + { + case AUMODE_PLAY: + if (ess_set_out_sr(sc, p->sample_rate, p->channels) != 0 || + ess_set_out_precision(sc, p->precision) != 0 || + ess_set_out_channels(sc, p->channels) != 0) + { + return EINVAL; + } + sc->sc_out.encoding = p->encoding; + break; + + case AUMODE_RECORD: + if (ess_set_in_sr(sc, p->sample_rate, p->channels) != 0 || + ess_set_in_precision(sc, p->precision) != 0 || + ess_set_in_channels(sc, p->channels) != 0) + { + return EINVAL; + } + sc->sc_in.encoding = p->encoding; + break; + + default: + return EINVAL; + break; + } + + + swcode = 0; + + /* + * REVISIT: could abstract some of following around the + * setting of signed/unsigned samples + */ + switch (p->encoding) { + case AUDIO_ENCODING_LINEAR_BE: + if (p->precision == 16) + swcode = swap_bytes; + /* fall into */ + case AUDIO_ENCODING_LINEAR_LE: + if (mode == AUMODE_PLAY) + ess_set_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, + ESS_AUDIO2_CTRL2_FIFO_SIGNED); + else + ess_set_xreg_bits(sc, ESS_XCMD_AUDIO1_CTRL1, + ESS_AUDIO1_CTRL1_FIFO_SIGNED); + break; + case AUDIO_ENCODING_ULINEAR_BE: + if (p->precision == 16) + swcode = swap_bytes; + /* fall into */ + case AUDIO_ENCODING_ULINEAR_LE: + if (mode == AUMODE_PLAY) + ess_clear_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, + ESS_AUDIO2_CTRL2_FIFO_SIGNED); + else + ess_clear_xreg_bits(sc, ESS_XCMD_AUDIO1_CTRL1, + ESS_AUDIO1_CTRL1_FIFO_SIGNED); + break; + case AUDIO_ENCODING_ULAW: + swcode = mode == AUMODE_PLAY ? + mulaw_to_ulinear8 : ulinear8_to_mulaw; + if (mode == AUMODE_PLAY) + ess_clear_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, + ESS_AUDIO2_CTRL2_FIFO_SIGNED); + else + ess_clear_xreg_bits(sc, ESS_XCMD_AUDIO1_CTRL1, + ESS_AUDIO1_CTRL1_FIFO_SIGNED); + break; + default: + return EINVAL; + } + p->sw_code = swcode; + + + /* Update setting for the other mode. */ + /* REVISIT: what the hell is this supposed to do. what is q anyway? */ + q->encoding = p->encoding; + q->channels = p->channels; + q->precision = p->precision; + + /* + * REVISIT: does the following apply to ESS? + * Should wait for chip to be idle. + */ + sc->sc_in.active = 0; + sc->sc_out.active = 0; + + return 0; +} +int +ess_set_in_sr(void *addr, + u_long sr, + int channels) +{ + register struct ess_softc *sc = addr; + + /* REVISIT: check range? */ + + /* + * Program the sample rate and filter clock for the record + * channel (Audio 1). + */ + ess_write_x_reg(sc, ESS_XCMD_SAMPLE_RATE, ess_srtotc(sr)); + ess_write_x_reg(sc, ESS_XCMD_FILTER_CLOCK, ess_srtofc(sr)); + + sc->sc_in.rate = sr; + + return 0; +} + +int +ess_set_out_sr(void *addr, + u_long sr, + int channels) +{ + register struct ess_softc *sc = addr; + + /* REVISIT: check range? */ + + /* + * Program the sample rate and filter clock for the playback + * channel (Audio 2). + */ + ess_write_mix_reg(sc, 0x70, ess_srtotc(sr)); + ess_write_mix_reg(sc, 0x72, ess_srtofc(sr)); + + sc->sc_out.rate = sr; + + return 0; +} + +int +ess_set_in_precision(void *addr, + u_int precision) +{ + register struct ess_softc *sc = addr; + int error = 0; + + /* + * REVISIT: Should we set DMA transfer type to 2-byte or + * 4-byte demand? This would probably better be done + * when configuring the DMA channel. See xreg 0xB9. + */ + switch (precision) + { + case 8: + ess_clear_xreg_bits(sc, ESS_XCMD_AUDIO1_CTRL1, + ESS_AUDIO1_CTRL1_FIFO_SIZE); + break; + + case 16: + ess_set_xreg_bits(sc, ESS_XCMD_AUDIO1_CTRL1, + ESS_AUDIO1_CTRL1_FIFO_SIZE); + break; + + default: + error = EINVAL; + break; + } + + sc->sc_in.precision = precision; + return error; +} + +int +ess_set_out_precision(void *addr, + u_int precision) +{ + register struct ess_softc *sc = addr; + int error = 0; + switch (precision) + { + case 8: + ess_clear_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, + ESS_AUDIO2_CTRL2_FIFO_SIZE); + break; + + case 16: + ess_set_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, + ESS_AUDIO2_CTRL2_FIFO_SIZE); + break; + + default: + error = EINVAL; + break; + } + + /* + * REVISIT: This actually sets transfer size to 16 + * bits. Should this really be hard-coded? This would + * probably better be done when configuring the DMA + * channel. + */ + ess_set_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL1, + ESS_AUDIO2_CTRL1_XFER_SIZE); + +#if 0 + /* + * REVISIT: Should we set DMA transfer type to 2-byte, + * 4-byte, or 8-byte demand? (Following does 8-byte.) + * This would probably better be done when + * configuring the DMA channel. + */ + ess_set_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL1, + 0xc0); +#endif + if (!error) + { + sc->sc_out.precision = precision; + } + return error; +} + +int +ess_set_in_channels(void *addr, + int channels) +{ + register struct ess_softc *sc = addr; + int error = 0; + + switch(channels) + { + case 1: + ess_set_xreg_bits(sc, ESS_XCMD_AUDIO_CTRL, + ESS_AUDIO_CTRL_MONO); + ess_clear_xreg_bits(sc, ESS_XCMD_AUDIO_CTRL, + ESS_AUDIO_CTRL_STEREO); + break; + + case 2: + ess_set_xreg_bits(sc, ESS_XCMD_AUDIO_CTRL, + ESS_AUDIO_CTRL_STEREO); + ess_clear_xreg_bits(sc, ESS_XCMD_AUDIO_CTRL, + ESS_AUDIO_CTRL_MONO); + break; + + default: + error = EINVAL; + break; + } + + sc->sc_in.active = 0; + sc->sc_in.channels = channels; + + return error; +} + +int +ess_set_out_channels(void *addr, + int channels) +{ + register struct ess_softc *sc = addr; + int error = 0; + + switch(channels) + { + case 1: + ess_clear_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, + ESS_AUDIO2_CTRL2_CHANNELS); + break; + + case 2: + ess_set_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, + ESS_AUDIO2_CTRL2_CHANNELS); + break; + + default: + error = EINVAL; + break; + } + + sc->sc_out.active = 0; + sc->sc_out.channels = channels; + + return error; +} + +int +ess_dma_output(void *addr, + void *p, + int cc, + void (*intr) __P((void *)), + void *arg) +{ + register struct ess_softc *sc = addr; + +#ifdef AUDIO_DEBUG + if (essdebug > 1) + Dprintf("ess_dma_output: cc=%d 0x%x (0x%x)\n", cc, intr, arg); +#endif + + if (sc->sc_out.channels == 2 && (cc & 1)) + { + DPRINTF(("stereo playback odd bytes (%d)\n", cc)); + return EIO; + } + + isa_dmastart(DMAMODE_WRITE, p, cc, sc->sc_out.drq); + sc->sc_out.active = 1; + sc->sc_out.intr = intr; + sc->sc_out.arg = arg; + sc->sc_out.dmaflags = DMAMODE_WRITE; + sc->sc_out.dmaaddr = p; + + if (sc->sc_out.dmacnt != cc) + { + sc->sc_out.dmacnt = cc; + + /* + * If doing 16-bit DMA transfers, then the number of + * transfers required is half the number of bytes to + * be transferred. + */ + if (sc->sc_out.mode == ESS_MODE_16BIT) + { + cc >>= 1; + } + + /* + * Program transfer count registers with 2's + * complement of count. + */ + cc = ~cc; /* 1's complement */ + cc++; /* 2's complement */ + ess_write_mix_reg(sc, ESS_MREG_XFER_COUNTLO, cc); + ess_write_mix_reg(sc, ESS_MREG_XFER_COUNTHI, cc >> 8); + } + +/* REVISIT: is it really necessary to clear then set these bits to get +the next lot of DMA to happen? Would it be sufficient to set the bits +the first time round and leave it at that? (No, because the chip automatically clears the FIFO_ENABLE bit after the DMA is complete.) +*/ + ess_set_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL1, + ESS_AUDIO2_CTRL1_DAC_ENABLE);/* REVISIT: once only */ + ess_set_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL1, + ESS_AUDIO2_CTRL1_FIFO_ENABLE); +#if 1 +/* REVISIT: seems like the 888 and 1888 have an interlock that + * prevents audio2 channel from working if audio1 channel is not + * connected to the FIFO. + */ + ess_set_xreg_bits(sc, 0xB7, 0x80); +#endif + return 0; + +} + +int +ess_dma_input(void *addr, + void *p, + int cc, + void (*intr) __P((void *)), + void *arg) +{ + register struct ess_softc *sc = addr; + +#ifdef AUDIO_DEBUG + if (essdebug > 1) + Dprintf("ess_dma_input: cc=%d 0x%x (0x%x)\n", cc, intr, arg); +#endif + /* REVISIT: Hack to enable Audio1 FIFO connection to CODEC. */ + ess_set_xreg_bits(sc, 0xB7, 0x80); + + if (sc->sc_in.channels == 2 && (cc & 1)) + { + DPRINTF(("stereo record odd bytes (%d)\n", cc)); + return EIO; + } + + isa_dmastart(DMAMODE_READ, p, cc, sc->sc_in.drq); + sc->sc_in.active = 1; + sc->sc_in.intr = intr; + sc->sc_in.arg = arg; + sc->sc_in.dmaflags = DMAMODE_READ; + sc->sc_in.dmaaddr = p; + + if (sc->sc_in.dmacnt != cc) + { + sc->sc_in.dmacnt = cc; + + /* + * If doing 16-bit DMA transfers, then the number of + * transfers required is half the number of bytes to + * be transferred. + */ + if (sc->sc_in.mode == ESS_MODE_16BIT) + { + cc >>= 1; + } + + /* + * Program transfer count registers with 2's + * complement of count. + */ + cc = ~cc; /* 1's complement */ + cc++; /* 2's complement */ + ess_write_x_reg(sc, ESS_XCMD_XFER_COUNTLO, cc); + ess_write_x_reg(sc, ESS_XCMD_XFER_COUNTHI, cc >> 8); + } + +/* REVISIT: is it really necessary to clear then set these bits to get +the next lot of DMA to happen? Would it be sufficient to set the bits +the first time round and leave it at that? (No, because the chip automatically clears the FIFO_ENABLE bit after the DMA is complete.) +*/ + ess_set_xreg_bits(sc, ESS_XCMD_AUDIO1_CTRL2, + ESS_AUDIO1_CTRL2_DMA_READ | /* REVISIT: once only */ + ESS_AUDIO1_CTRL2_ADC_ENABLE |/* REVISIT: once only */ + ESS_AUDIO1_CTRL2_FIFO_ENABLE); + + return 0; + +} + +int +ess_halt_output(void *addr) +{ + struct ess_softc *sc = addr; + + DPRINTF(("ess_halt_output: sc=%p\n", sc)); + + ess_clear_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, + ESS_AUDIO2_CTRL2_DMA_ENABLE); + return 0; +} + +int +ess_halt_input(void *addr) +{ + struct ess_softc *sc = addr; + + DPRINTF(("ess_halt_input: sc=%p\n", sc)); + + ess_clear_xreg_bits(sc, ESS_XCMD_AUDIO1_CTRL2, + ESS_AUDIO1_CTRL2_FIFO_ENABLE); + return 0; +} + +int +ess_cont_output(void *addr) +{ + struct ess_softc *sc = addr; + + DPRINTF(("ess_cont_output: sc=%p\n", sc)); + + ess_set_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, + ESS_AUDIO2_CTRL2_DMA_ENABLE); + return 0; +} + +int +ess_cont_input(void *addr) +{ + struct ess_softc *sc = addr; + + DPRINTF(("ess_cont_output: sc=%p\n", sc)); + + ess_clear_xreg_bits(sc, ESS_XCMD_AUDIO1_CTRL2, + ESS_AUDIO1_CTRL2_FIFO_ENABLE); + return 0; +} + +int +ess_speaker_ctl(void *addr, + int newstate) +{ + struct ess_softc *sc = addr; + + if ((newstate == SPKR_ON) && (sc->spkr_state == SPKR_OFF)) + { + ess_speaker_on(sc); + sc->spkr_state = SPKR_ON; + } + if ((newstate == SPKR_OFF) && (sc->spkr_state == SPKR_ON)) + { + ess_speaker_off(sc); + sc->spkr_state = SPKR_OFF; + } + return 0; +} + +int +ess_intr_output(arg) + void *arg; +{ + register struct ess_softc *sc = arg; + u_char x; + +#ifdef AUDIO_DEBUG + if (essdebug > 1) + Dprintf("ess_intr_output: intr=0x%x\n", sc->sc_out.intr); +#endif + + /* clear interrupt on Audio channel 2*/ + ess_clear_mreg_bits(sc, ESS_MREG_AUDIO2_CTRL2, 0x80); + + sc->sc_out.nintr++; + + if (sc->sc_out.intr != 0) { + isa_dmadone(sc->sc_out.dmaflags, sc->sc_out.dmaaddr, + sc->sc_out.dmacnt, sc->sc_out.drq); + (*sc->sc_out.intr)(sc->sc_out.arg); + } + else + return 1; /* revisit: was 0 */ + + return 1; +} + +int +ess_intr_input(arg) + void *arg; +{ + register struct ess_softc *sc = arg; + u_char x; + +#ifdef AUDIO_DEBUG + if (essdebug > 1) + Dprintf("ess_intr_input: intr=0x%x\n", sc->sc_in.intr); +#endif + + /* + * Disable DMA for Audio 1; it will be enabled again the next + * time ess_dma_input is called. Note that for single DMAs, + * this bit must be toggled for each DMA. For auto-initialize + * DMAs, this bit should be left high. + */ + ess_clear_xreg_bits(sc, ESS_XCMD_AUDIO1_CTRL2, + ESS_AUDIO1_CTRL2_FIFO_ENABLE); + + /* clear interrupt on Audio channel 1*/ + x = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ESS_CLEAR_INTR); + + sc->sc_in.nintr++; + + if (sc->sc_in.intr != 0) { + isa_dmadone(sc->sc_in.dmaflags, sc->sc_in.dmaaddr, + sc->sc_in.dmacnt, sc->sc_in.drq); + (*sc->sc_in.intr)(sc->sc_in.arg); + } + else + return 0; + + return 1; +} + +int +ess_round_blocksize(addr, blk) + void *addr; + int blk; +{ + register struct ess_softc *sc = addr; + /* REVISIT: need to do a proper implementation for ESS */ + return blk; + + sc->sc_in.dmacnt = 0; + sc->sc_out.dmacnt = 0; + + /* Higher speeds need bigger blocks to avoid popping and silence gaps. */ + if (blk < NBPG/4 || blk > NBPG/2) { + if (sc->sc_out.rate > 8000 || sc->sc_in.rate > 8000) + blk = NBPG/2; + } + /* don't try to DMA too much at once, though. */ + if (blk > NBPG) + blk = NBPG; + + if (sc->sc_out.channels == 2) + return (blk & ~1); /* must be even to preserve stereo separation */ + else + return (blk); /* Anything goes :-) */ +} + +int +ess_set_out_port(void *addr, + int port) +{ + struct ess_softc *sc = addr; + + sc->out_port = port; /* Just record it */ + + return 0; +} + +int +ess_get_out_port(void *addr) +{ + struct ess_softc *sc = addr; + + return sc->out_port; +} + + +int +ess_set_in_port(void *addr, + int port) +{ + return ess_set_in_ports(addr, 1 << port); +} + +int +ess_get_in_port(void *addr) +{ + struct ess_softc *sc = addr; + + return sc->in_port; +} +int +ess_commit_settings(void *addr) +{ + /* + * REVISIT: What adjustments do we need to make here? + * (Probably none since everything is now done via + * ess_set_params.) + */ + return 0; +} + +int +ess_setfd(addr, flag) + void *addr; + int flag; +{ + /* REVISIT: what do we do once we can do full-duplex? */ + return ENOTTY; +} + +int +ess_set_port(void *addr, + mixer_ctrl_t *cp) +{ + struct ess_softc *sc = addr; + int lgain, rgain; + + DPRINTF(("ess_set_port: port=%d num_channels=%d\n", + cp->dev, cp->un.value.num_channels)); + + switch (cp->dev) + { + /* + * The following mixer ports are all stereo. If we get a + * single-channel gain value passed in, then we duplicate it + * to both left and right channels. + */ + case ESS_MASTER_VOL: + case ESS_DAC_PLAY_VOL: + case ESS_MIC_PLAY_VOL: + case ESS_LINE_PLAY_VOL: + case ESS_SYNTH_PLAY_VOL: + case ESS_CD_PLAY_VOL: + case ESS_AUXB_PLAY_VOL: + case ESS_DAC_REC_VOL: + case ESS_MIC_REC_VOL: + case ESS_LINE_REC_VOL: + case ESS_SYNTH_REC_VOL: + case ESS_CD_REC_VOL: + case ESS_AUXB_REC_VOL: + case ESS_RECORD_VOL: + if (cp->type != AUDIO_MIXER_VALUE) + return EINVAL; + + switch (cp->un.value.num_channels) { + case 1: + lgain = rgain = ESS_4BIT_GAIN( + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]); + break; + case 2: + lgain = ESS_4BIT_GAIN( + cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT]); + rgain = ESS_4BIT_GAIN( + cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT]); + break; + default: + return EINVAL; + } + + sc->gain[cp->dev][ESS_LEFT] = lgain; + sc->gain[cp->dev][ESS_RIGHT] = rgain; + + ess_set_gain(sc, cp->dev, 1); + break; + + + /* + * The PC speaker port is mono. If we get a stereo gain value + * passed in, then we return EINVAL. + */ + case ESS_PCSPEAKER_VOL: + if (cp->un.value.num_channels != 1) + return EINVAL; + + sc->gain[cp->dev][ESS_LEFT] = sc->gain[cp->dev][ESS_RIGHT] = + ESS_3BIT_GAIN(cp->un.value.level[AUDIO_MIXER_LEVEL_MONO]); + ess_set_gain(sc, cp->dev, 1); + break; + + + case ESS_MIC_PREAMP: + if (cp->type != AUDIO_MIXER_ENUM) + return EINVAL; + + if (cp->un.ord) + { + /* Enable microphone preamp */ + ess_set_xreg_bits(sc, ESS_XCMD_PREAMP_CTRL, + ESS_PREAMP_CTRL_ENABLE); + } + else + { + /* Disable microphone preamp */ + ess_clear_xreg_bits(sc, ESS_XCMD_PREAMP_CTRL, + ESS_PREAMP_CTRL_ENABLE); + } + break; + + case ESS_RECORD_SOURCE: + if (cp->type == AUDIO_MIXER_SET) + { + return ess_set_in_ports(sc, cp->un.mask); + } + else + { + return EINVAL; + } + break; + + case ESS_RECORD_MONITOR: + if (cp->type != AUDIO_MIXER_ENUM) + return EINVAL; + + if (cp->un.ord) + { + /* Enable monitor */ + ess_set_xreg_bits(sc, ESS_XCMD_AUDIO_CTRL, + ESS_AUDIO_CTRL_MONITOR); + } + else + { + /* Disable monitor */ + ess_clear_xreg_bits(sc, ESS_XCMD_AUDIO_CTRL, + ESS_AUDIO_CTRL_MONITOR); + } + break; + + default: + return EINVAL; + } + + return 0; +} + +int +ess_get_port(void *addr, + mixer_ctrl_t *cp) +{ + struct ess_softc *sc = addr; + + DPRINTF(("ess_get_port: port=%d\n", cp->dev)); + + switch (cp->dev) + { + case ESS_DAC_PLAY_VOL: + case ESS_MIC_PLAY_VOL: + case ESS_LINE_PLAY_VOL: + case ESS_SYNTH_PLAY_VOL: + case ESS_CD_PLAY_VOL: + case ESS_AUXB_PLAY_VOL: + case ESS_MASTER_VOL: + case ESS_PCSPEAKER_VOL: + case ESS_DAC_REC_VOL: + case ESS_MIC_REC_VOL: + case ESS_LINE_REC_VOL: + case ESS_SYNTH_REC_VOL: + case ESS_CD_REC_VOL: + case ESS_AUXB_REC_VOL: + case ESS_RECORD_VOL: + if (cp->dev == ESS_PCSPEAKER_VOL && + cp->un.value.num_channels != 1) + return EINVAL; + + switch (cp->un.value.num_channels) + { + case 1: + cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = + sc->gain[cp->dev][ESS_LEFT]; + break; + case 2: + cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = + sc->gain[cp->dev][ESS_LEFT]; + cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = + sc->gain[cp->dev][ESS_RIGHT]; + break; + default: + return EINVAL; + } + break; + + case ESS_MIC_PREAMP: + cp->un.ord = (ess_read_x_reg(sc, ESS_XCMD_PREAMP_CTRL) & + ESS_PREAMP_CTRL_ENABLE) ? 1 : 0; + break; + + case ESS_RECORD_SOURCE: + cp->un.mask = sc->in_mask; + break; + + case ESS_RECORD_MONITOR: + cp->un.ord = (ess_read_x_reg(sc, ESS_XCMD_AUDIO_CTRL) & + ESS_AUDIO_CTRL_MONITOR) ? 1 : 0; + break; + + default: + return EINVAL; + } + + return 0; +} + +int +ess_query_devinfo(void *addr, + mixer_devinfo_t *dip) +{ + struct ess_softc *sc = addr; + + DPRINTF(("ess_query_devinfo: model=%d index=%d\n", + sc->sc_model, dip->index)); + + /* + * REVISIT: There are some slight differences between the + * mixers on the different ESS chips, which can + * be sorted out using the chip model rather than a + * separate mixer model. + * This is currently coded assuming an ES1887; we + * need to work out which bits are not applicable to + * the other models (1888 and 888). + */ + switch (dip->index) { + case ESS_DAC_PLAY_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = ESS_INPUT_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNdac); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case ESS_MIC_PLAY_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = ESS_INPUT_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNmicrophone); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case ESS_LINE_PLAY_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = ESS_INPUT_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNline); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case ESS_SYNTH_PLAY_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = ESS_INPUT_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNfmsynth); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case ESS_CD_PLAY_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = ESS_INPUT_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNcd); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case ESS_AUXB_PLAY_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = ESS_INPUT_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, "auxb"); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case ESS_INPUT_CLASS: + dip->type = AUDIO_MIXER_CLASS; + dip->mixer_class = ESS_INPUT_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioCInputs); + return 0; + + + case ESS_MASTER_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = ESS_OUTPUT_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNmaster); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case ESS_PCSPEAKER_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = ESS_OUTPUT_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, "pc_speaker"); + dip->un.v.num_channels = 1; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case ESS_OUTPUT_CLASS: + dip->type = AUDIO_MIXER_CLASS; + dip->mixer_class = ESS_OUTPUT_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioCOutputs); + return 0; + + + case ESS_DAC_REC_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = ESS_RECORD_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNdac); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case ESS_MIC_REC_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = ESS_RECORD_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = ESS_MIC_PREAMP; + strcpy(dip->label.name, AudioNmicrophone); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case ESS_LINE_REC_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = ESS_RECORD_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNline); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case ESS_SYNTH_REC_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = ESS_RECORD_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNfmsynth); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case ESS_CD_REC_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = ESS_RECORD_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNcd); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case ESS_AUXB_REC_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = ESS_RECORD_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, "auxb"); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case ESS_MIC_PREAMP: + dip->type = AUDIO_MIXER_ENUM; + dip->mixer_class = ESS_RECORD_CLASS; + dip->prev = ESS_MIC_REC_VOL; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNenhanced); + dip->un.e.num_mem = 2; + strcpy(dip->un.e.member[0].label.name, AudioNoff); + dip->un.e.member[0].ord = 0; + strcpy(dip->un.e.member[1].label.name, AudioNon); + dip->un.e.member[1].ord = 1; + return 0; + + case ESS_RECORD_VOL: + dip->type = AUDIO_MIXER_VALUE; + dip->mixer_class = ESS_RECORD_CLASS; + dip->prev = AUDIO_MIXER_LAST; + dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNrecord); + dip->un.v.num_channels = 2; + strcpy(dip->un.v.units.name, AudioNvolume); + return 0; + + case ESS_RECORD_SOURCE: + dip->mixer_class = ESS_RECORD_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNsource); + dip->type = AUDIO_MIXER_SET; + dip->un.s.num_mem = 6; + strcpy(dip->un.s.member[0].label.name, AudioNdac); + dip->un.s.member[0].mask = 1 << ESS_DAC_REC_VOL; + strcpy(dip->un.s.member[1].label.name, AudioNmicrophone); + dip->un.s.member[1].mask = 1 << ESS_MIC_REC_VOL; + strcpy(dip->un.s.member[2].label.name, AudioNline); + dip->un.s.member[2].mask = 1 << ESS_LINE_REC_VOL; + strcpy(dip->un.s.member[3].label.name, AudioNfmsynth); + dip->un.s.member[3].mask = 1 << ESS_SYNTH_REC_VOL; + strcpy(dip->un.s.member[4].label.name, AudioNcd); + dip->un.s.member[4].mask = 1 << ESS_CD_REC_VOL; + strcpy(dip->un.s.member[5].label.name, "auxb"); + dip->un.s.member[5].mask = 1 << ESS_AUXB_REC_VOL; + return 0; + + case ESS_RECORD_CLASS: + dip->type = AUDIO_MIXER_CLASS; + dip->mixer_class = ESS_RECORD_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioCRecord); + return 0; + + + case ESS_RECORD_MONITOR: + dip->mixer_class = ESS_MONITOR_CLASS; + dip->prev = dip->next = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioNmonitor); + dip->type = AUDIO_MIXER_ENUM; + dip->un.e.num_mem = 2; + strcpy(dip->un.e.member[0].label.name, AudioNoff); + dip->un.e.member[0].ord = 0; + strcpy(dip->un.e.member[1].label.name, AudioNon); + dip->un.e.member[1].ord = 1; + return 0; + + case ESS_MONITOR_CLASS: + dip->type = AUDIO_MIXER_CLASS; + dip->mixer_class = ESS_MONITOR_CLASS; + dip->next = dip->prev = AUDIO_MIXER_LAST; + strcpy(dip->label.name, AudioCMonitor); + return 0; + } + + return ENXIO; +} +/* ============================================ + * Generic functions for ess, not used by audio h/w i/f + * ============================================= + */ + +/* + * Reset the chip. + * Return non-zero if the chip isn't detected. + */ +int +ess_reset(struct ess_softc *sc) +{ + /* REVISIT: currently just copied from sbdsp.c */ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + sc->sc_in.intr = 0; + sc->sc_in.dmacnt = 0; + if (sc->sc_in.active) { + isa_dmaabort(sc->sc_in.drq); + sc->sc_in.active = 0; + } + + sc->sc_out.intr = 0; + sc->sc_out.dmacnt = 0; + if (sc->sc_out.active) { + isa_dmaabort(sc->sc_out.drq); + sc->sc_out.active = 0; + } + + /* + * See SBK, section 11.3. + * We pulse a reset signal into the card. + * Gee, what a brilliant hardware design. + */ + /* REVISIT: need to properly document the use of 3 below */ + bus_space_write_1(iot, ioh, ESS_DSP_RESET, 3); + delay(10); + bus_space_write_1(iot, ioh, ESS_DSP_RESET, 0); + delay(30); + if (ess_rdsp(sc) != ESS_MAGIC) + return -1; + + /* + * Enable access to the ESS extension commands, which are + * disabled by each reset. + */ + ess_wdsp(sc, ESS_ACMD_ENABLE_EXT); + + return 0; +} + +void +ess_set_gain(struct ess_softc *sc, + int port, + int on) +{ + int gain, left, right; + int mix; + int src; + int stereo; + + /* + * Most gain controls are found in the mixer registers and + * are stereo. Any that are not, must set mix and stereo as + * required. + */ + mix = 1; + stereo = 1; + + switch (port) { + case ESS_MASTER_VOL: + src = 0x32; + break; + case ESS_DAC_PLAY_VOL: + src = 0x7C; + break; + case ESS_MIC_PLAY_VOL: + src = 0x1A; + break; + case ESS_LINE_PLAY_VOL: + src = 0x3E; + break; + case ESS_SYNTH_PLAY_VOL: + src = 0x36; + break; + case ESS_CD_PLAY_VOL: + src = 0x38; + break; + case ESS_AUXB_PLAY_VOL: + src = 0x3A; + break; + case ESS_PCSPEAKER_VOL: + src = 0x3C; + stereo = 0; + break; + case ESS_DAC_REC_VOL: + src = 0x69; + break; + case ESS_MIC_REC_VOL: + src = 0x68; + break; + case ESS_LINE_REC_VOL: + src = 0x6E; + break; + case ESS_SYNTH_REC_VOL: + src = 0x6B; + break; + case ESS_CD_REC_VOL: + src = 0x6A; + break; + case ESS_AUXB_REC_VOL: + src = 0x6C; + break; + case ESS_RECORD_VOL: + src = 0xB4; + mix = 0; + break; + default: + return; + } + + if (on) + { + left = sc->gain[port][ESS_LEFT]; + right = sc->gain[port][ESS_RIGHT]; + } + else + { + left = right = 0; + } + + if (stereo) + { + gain = ESS_STEREO_GAIN(left, right); + } + else + { + gain = ESS_MONO_GAIN(left); + } + + if (mix) + { + ess_write_mix_reg(sc, src, gain); + } + else + { + ess_write_x_reg(sc, src, gain); + } +} + +int +ess_set_in_ports(struct ess_softc *sc, + int mask) +{ + mixer_devinfo_t di; + int i; + int on; + int port; + int tmp; + + DPRINTF(("ess_set_in_ports: mask=0x%x\n", mask)); + + /* + * Get the device info for the record source control, + * including the list of available sources. + */ + di.index = ESS_RECORD_SOURCE; + if (ess_query_devinfo(sc, &di)) + return EINVAL; + + /* + * Set or disable the record volume control for each of the + * possible sources. + */ + for (i = 0; i < di.un.s.num_mem; i++) + { + /* + * Calculate the source port number from its mask. + */ + tmp = di.un.s.member[i].mask >> 1; + for (port = 0; tmp; port++) + { + tmp >>= 1; + } + + /* + * Set the source gain: + * to the current value if source is enabled + * to zero if source is disabled + */ + ess_set_gain(sc, port, mask & di.un.s.member[i].mask); + } + + sc->in_mask = mask; + + /* + * We have to fake a single port since the upper layer expects + * one only. We choose the lowest numbered port that is enabled. + */ + for(i = 0; i < ESS_NPORT; i++) { + if (mask & (1 << i)) { + sc->in_port = i; + break; + } + } + + return 0; +} + +void +ess_speaker_on(struct ess_softc *sc) +{ + /* + * Disable mute on left- and right-master volume. + */ + ess_clear_mreg_bits(sc, 0x60, 0x40); + ess_clear_mreg_bits(sc, 0x62, 0x40); +} + +void +ess_speaker_off(struct ess_softc *sc) +{ + /* + * Enable mute on left- and right-master volume. + */ + ess_set_mreg_bits(sc, 0x60, 0x40); + ess_set_mreg_bits(sc, 0x62, 0x40); +} + +/* + * Calculate the time constant for the requested sampling rate. + */ +u_int +ess_srtotc(u_int rate) +{ + u_int tc; + +/* + * REVISIT: Should we clamp the rate? + */ +#if 0 + if (rate < ESS_MINRATE) + { + rate = ESS_MINRATE; + } + else if (rate > ESS_MAXRATE) + { + rate = ESS_MAXRATE; + } +#endif + + /* + * The following formulae are from the ESS data sheet. + */ + if (rate < 22050) + { + tc = 128 - 397700L / rate; + } + else + { + tc = 256 - 795500L / rate; + } + + return (tc); +} + + +/* + * Calculate the filter constant for the reuqested sampling rate. + */ +u_int +ess_srtofc(u_int rate) +{ + /* + * The following formula is derived from the information in + * the ES1887 data sheet, based on a roll-off frequency of + * 87%. + */ + return (256 - 200279L / rate); +} + + +/* + * Return the status of the DSP. + */ +u_char +ess_get_dsp_status(struct ess_softc *sc) +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + return (bus_space_read_1(iot, ioh, ESS_DSP_RW_STATUS)); +} + + +/* + * Return the read status of the DSP: 1 -> DSP ready for reading + * 0 -> DSP not ready for reading + */ +u_char +ess_dsp_read_ready(struct ess_softc *sc) +{ + return (((ess_get_dsp_status(sc) & ESS_DSP_READ_MASK) == + ESS_DSP_READ_READY) ? 1 : 0); +} + + +/* + * Return the write status of the DSP: 1 -> DSP ready for writing + * 0 -> DSP not ready for writing + */ +u_char +ess_dsp_write_ready(struct ess_softc *sc) +{ + return (((ess_get_dsp_status(sc) & ESS_DSP_WRITE_MASK) == + ESS_DSP_WRITE_READY) ? 1 : 0); +} + + +/* + * Read a byte from the DSP. + */ +int +ess_rdsp(struct ess_softc *sc) + +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + register int i; + + for (i = ESS_READ_TIMEOUT; i > 0; --i) + { + if (ess_dsp_read_ready(sc)) + { +#if 1 + return bus_space_read_1(iot, ioh, ESS_DSP_READ); +#else + i = bus_space_read_1(iot, ioh, ESS_DSP_READ); + printf("ess_rdsp() = 0x%02x\n", i); + return i; +#endif + } + else + { + delay(10); + } + } + + ++eserr.rdsp; + DPRINTF(("ess_rdsp: timed out\n")); + return -1; +} + +/* + * Write a byte to the DSP. + */ +int +ess_wdsp(struct ess_softc *sc, + u_char v) +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + register int i; +#if 0 +printf("ess_wdsp(0x%02x)\n", v); +#endif + for (i = ESS_WRITE_TIMEOUT; i > 0; --i) + { + if (ess_dsp_write_ready(sc)) + { + bus_space_write_1(iot, ioh, ESS_DSP_WRITE, v); + return 0; + } + else + { + delay(10); + } + } + + ++eserr.wdsp; + DPRINTF(("ess_wdsp(0x%02x): timed out\n", v)); + return -1; +} + +/* + * Write a value to one of the ESS extended registers. + */ +int +ess_write_x_reg(struct ess_softc *sc, + u_char reg, + u_char val) +{ + int error; + + if ((error = ess_wdsp(sc, reg)) == 0) + { + error = ess_wdsp(sc, val); + } + + return error; +} + +/* + * Read the value of one of the ESS extended registers. + */ +u_char +ess_read_x_reg(struct ess_softc *sc, + u_char reg) +{ + int error; + + if ((error = ess_wdsp(sc, 0xC0)) == 0) + { + error = ess_wdsp(sc, reg); + } + if (error) + DPRINTF(("Error reading extended register 0x%02x\n", reg)); +/* REVISIT: what if an error is returned above? */ + return ess_rdsp(sc); +} + +void +ess_clear_xreg_bits(struct ess_softc *sc, + u_char reg, + u_char mask) +{ + if (ess_write_x_reg(sc, reg, ess_read_x_reg(sc, reg) & ~mask) == -1) + DPRINTF(("Error clearing bits in extended register 0x%02x\n", + reg)); +} + +void +ess_set_xreg_bits(struct ess_softc *sc, + u_char reg, + u_char mask) +{ + if (ess_write_x_reg(sc, reg, ess_read_x_reg(sc, reg) | mask) == -1) + DPRINTF(("Error setting bits in extended register 0x%02x\n", + reg)); +} + + + +/* + * Write a value to one of the ESS mixer registers. + */ +void +ess_write_mix_reg(struct ess_softc *sc, + u_char reg, + u_char val) +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + int s; + + s = splaudio(); + + /* + * Select the register to be written. + */ + bus_space_write_1(iot, ioh, ESS_MIX_REG_SELECT, reg); + + /* + * Write the desired value. + */ + bus_space_write_1(iot, ioh, ESS_MIX_REG_DATA, val); + + splx(s); +} + +/* + * Read the value of one of the ESS mixer registers. + */ +u_char +ess_read_mix_reg(struct ess_softc *sc, + u_char reg) +{ + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + int s; + u_char val; + + s = splaudio(); + + /* + * Select the register to be read. + */ + bus_space_write_1(iot, ioh, ESS_MIX_REG_SELECT, reg); + + /* + * Read the current value. + */ + val = bus_space_read_1(iot, ioh, ESS_MIX_REG_DATA); + + splx(s); + return val; +} + +void +ess_clear_mreg_bits(struct ess_softc *sc, + u_char reg, + u_char mask) +{ + ess_write_mix_reg(sc, reg, ess_read_mix_reg(sc, reg) & ~mask); +} + +void +ess_set_mreg_bits(struct ess_softc *sc, + u_char reg, + u_char mask) +{ + ess_write_mix_reg(sc, reg, ess_read_mix_reg(sc, reg) | mask); +} diff --git a/sys/arch/arm32/isa/essreg.h b/sys/arch/arm32/isa/essreg.h new file mode 100644 index 000000000000..6c56712e3a64 --- /dev/null +++ b/sys/arch/arm32/isa/essreg.h @@ -0,0 +1,254 @@ +/* $NetBSD: essreg.h,v 1.1 1998/06/08 17:49:43 tv Exp $ */ + +/* + * Copyright 1997 + * Digital Equipment Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and conditions. + * Subject to these conditions, you may download, copy, install, + * use, modify and distribute this software in source and/or binary + * form. No title or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions as + * they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or logo of + * Digital Equipment Corporation. Neither the "Digital Equipment + * Corporation" name nor any trademark or logo of Digital Equipment + * Corporation may be used to endorse or promote products derived + * from this software without the prior written permission of + * Digital Equipment Corporation. + * + * 3) This software is provided "AS-IS" and any express or implied + * warranties, including but not limited to, any implied warranties + * of merchantability, fitness for a particular purpose, or + * non-infringement are disclaimed. In no event shall DIGITAL be + * liable for any damages whatsoever, and in particular, DIGITAL + * shall not be liable for special, indirect, consequential, or + * incidental damages or damages for lost profits, loss of + * revenue or loss of use, whether such damages arise in contract, + * negligence, tort, under statute, in equity, at law or otherwise, + * even if advised of the possibility of such damage. + */ + +/* +** @(#) $RCSfile: essreg.h,v $ $Revision: 1.1 $ (SHARK) $Date: 1998/06/08 17:49:43 $ +** +**++ +** +** essreg.h +** +** FACILITY: +** +** DIGITAL Network Appliance Reference Design (DNARD) +** +** MODULE DESCRIPTION: +** +** This module contains the constant definitions for the device +** registers on the ESS Technologies 1888/1887/888 sound chip. +** +** AUTHORS: +** +** Blair Fidler Software Engineering Australia +** Gold Coast, Australia. +** +** CREATION DATE: +** +** March 10, 1997. +** +** MODIFICATION HISTORY: +** +**-- +*/ + +/* + * DSP commands. This unit handles MIDI and audio capabilities. + * The DSP can be reset, data/commands can be read or written to it, + * and it can generate interrupts. Interrupts are generated for MIDI + * input or DMA completion. They seem to have neglected the fact + * that it would be nice to have a MIDI transmission complete interrupt. + * Worse, the DMA engine is half-duplex. This means you need to do + * (timed) programmed I/O to be able to record and play simulataneously. + */ +#define ESS_ACMD_DAC8WRITE 0x10 /* direct-mode 8-bit DAC write */ +#define ESS_ACMD_DAC16WRITE 0x11 /* direct-mode 16-bit DAC write */ +#define ESS_ACMD_DMA8OUT 0x14 /* 8-bit linear DMA output */ +#define ESS_ACMD_DMA16OUT 0x15 /* 16-bit linear DMA output */ +#define ESS_ACMD_AUTODMA8OUT 0x1C /* auto-init 8-bit linear DMA output */ +#define ESS_ACMD_AUTODMA16OUT 0x1D /* auto-init 16-bit linear DMA output */ +#define ESS_ACMD_ADC8READ 0x20 /* direct-mode 8-bit ADC read */ +#define ESS_ACMD_ADC16READ 0x21 /* direct-mode 16-bit ADC read */ +#define ESS_ACMD_DMA8IN 0x24 /* 8-bit linear DMA input */ +#define ESS_ACMD_DMA16IN 0x25 /* 16-bit linear DMA input */ +#define ESS_ACMD_AUTODMA8IN 0x2C /* auto-init 8-bit linear DMA input */ +#define ESS_ACMD_AUTODMA16IN 0x2D /* auto-init 16-bit linear DMA input */ +#define ESS_ACMD_SETTIMECONST1 0x40 /* set time constant (1MHz base) */ +#define ESS_ACMD_SETTIMECONST15 0x41 /* set time constant (1.5MHz base) */ +#define ESS_ACMD_SETFILTER 0x42 /* set filter clock independently */ +#define ESS_ACMD_BLOCKSIZE 0x48 /* set blk size for high speed xfer */ + +#define ESS_ACMD_DMA4OUT 0x74 /* 4-bit ADPCM DMA output */ +#define ESS_ACMD_DMA4OUTREF 0x75 /* 4-bit ADPCM DMA output with ref */ +#define ESS_ACMD_DMA2_6OUT 0x76 /* 2.6-bit ADPCM DMA output */ +#define ESS_ACMD_DMA2_6OUTREF 0x77 /* 2.6-bit ADPCM DMA output with ref */ +#define ESS_ACMD_DMA2OUT 0x7A /* 2-bit ADPCM DMA output */ +#define ESS_ACMD_DMA2OUTREF 0x7B /* 2-bit ADPCM DMA output with ref */ +#define ESS_ACMD_SILENCEOUT 0x80 /* output a block of silence */ +#define ESS_ACMD_START_AUTO_OUT 0x90 /* start auto-init 8-bit DMA output */ +#define ESS_ACMD_START_OUT 0x91 /* start 8-bit DMA output */ +#define ESS_ACMD_START_AUTO_IN 0x98 /* start auto-init 8-bit DMA input */ +#define ESS_ACMD_START_IN 0x99 /* start 8-bit DMA input */ + +#define ESS_XCMD_SAMPLE_RATE 0xA1 /* sample rate for Audio1 channel */ +#define ESS_XCMD_FILTER_CLOCK 0xA2 /* filter clock for Audio1 channel*/ +#define ESS_XCMD_XFER_COUNTLO 0xA4 /* */ +#define ESS_XCMD_XFER_COUNTHI 0xA5 /* */ +#define ESS_XCMD_AUDIO_CTRL 0xA8 /* */ +#define ESS_AUDIO_CTRL_MONITOR 0x08 + /* 0=disable/1=enable */ +#define ESS_AUDIO_CTRL_MONO 0x02 + /* 0=disable/1=enable */ +#define ESS_AUDIO_CTRL_STEREO 0x01 + /* 0=disable/1=enable */ +#define ESS_XCMD_PREAMP_CTRL 0xA9 /* */ +#define ESS_PREAMP_CTRL_ENABLE 0x04 + +#define ESS_XCMD_IRQ_CTRL 0xB1 /* legacy audio interrupt control */ +#define ESS_XCMD_DRQ_CTRL 0xB2 /* audio DRQ control */ +#define ESS_XCMD_VOLIN_CTRL 0xB4 /* stereo input volume control */ +#define ESS_XCMD_AUDIO1_CTRL1 0xB7 /* */ +#define ESS_AUDIO1_CTRL1_FIFO_SIGNED 0x20 + /* 0=unsigned/1=signed */ +#define ESS_AUDIO1_CTRL1_FIFO_SIZE 0x04 + /* 0=8-bit/1=16-bit */ +#define ESS_XCMD_AUDIO1_CTRL2 0xB8 /* */ +#define ESS_AUDIO1_CTRL2_FIFO_ENABLE 0x01 + /* 0=disable/1=enable */ +#define ESS_AUDIO1_CTRL2_DMA_READ 0x02 + /* 0=DMA write/1=DMA read */ +#define ESS_AUDIO1_CTRL2_ADC_ENABLE 0x08 + /* 0=DAC mode/1=ADC mode */ +#define ESS_XCMD_DEMAND_CTRL 0xB9 /* */ + +#define ESS_ACMD_ENABLE_EXT 0xC6 /* enable ESS extension commands */ +#define ESS_ACMD_DISABLE_EXT 0xC7 /* enable ESS extension commands */ + +#define ESS_ACMD_PAUSE_DMA 0xD0 /* pause DMA */ +#define ESS_ACMD_ENABLE_SPKR 0xD1 /* enable Audio1 DAC input to mixer */ +#define ESS_ACMD_DISABLE_SPKR 0xD3 /* disable Audio1 DAC input to mixer */ +#define ESS_ACMD_CONT_DMA 0xD4 /* continue paused DMA */ +#define ESS_ACMD_SPKR_STATUS 0xD8 /* return Audio1 DAC status: */ +#define ESS_SPKR_OFF 0x00 +#define ESS_SPKR_ON 0xFF +#define ESS_ACMD_VERSION 0xE1 /* get version number */ +#define ESS_ACMD_LEGACY_ID 0xE7 /* get legacy ES688/ES1688 ID bytes */ +/* + * Macros to detect valid hardware configuration data. + */ +#define ESS_IRQ1_VALID(irq, model) ((irq) == 5 || (irq) == 7 || (irq) == 9 || (irq) == 10 || ((model) == ESS_1887 && (irq) == 15)) + +#define ESS_IRQ2_VALID(irq, model) (((model) != ESS_1887) ? ((irq) == 15) : ((irq) == 5 || (irq) == 7 || (irq) == 9 || (irq) == 10) || (irq) == 15) + +#define ESS_DRQ1_VALID(chan, model) ((chan) == 0 || (chan) == 1 || (chan) == 3 || ((model) == ESS_1887 && (chan) == 5)) + +#define ESS_DRQ2_VALID(chan, model) (((model) != ESS_1887) ? ((chan) == 5) : ((chan) == 0 || (chan) == 1 || (chan) == 3 || (chan) == 5)) + +#define ESS_BASE_VALID(base) ((base) == 0x220 || (base) == 0x230 || (base) == 0x240 || (base) == 0x250) + +#define ESS_DMA_SIZE(chan) ((chan & 4) ? ESS_MODE_16BIT: ESS_MODE_8BIT) + +/* + * Macros to manipulate gain values + */ +#define ESS_4BIT_GAIN(x) ((x) & 0xf0) +#define ESS_3BIT_GAIN(x) (((x) & 0xe0) >> 1) +#define ESS_STEREO_GAIN(l, r) ((l) | ((r) >> 4)) +#define ESS_MONO_GAIN(x) ((x) >> 4) + +#ifdef ESS_AMODE_LOW +/* + * Registers used to configure ESS chip via Read Key Sequence + */ +#define ESS_CONFIG_KEY_BASE 0x229 +#define ESS_CONFIG_KEY_PORTS 3 +#else +/* + * Registers used to configure ESS chip via System Control Register (SCR) + */ +#define ESS_SCR_ACCESS_BASE 0xF9 +#define ESS_SCR_ACCESS_PORTS 3 +#define ESS_SCR_LOCK 0 +#define ESS_SCR_UNLOCK 2 + +#define ESS_SCR_BASE 0xE0 +#define ESS_SCR_PORTS 2 +#define ESS_SCR_INDEX 0 +#define ESS_SCR_DATA 1 + +/* + * Bit definitions for SCR + */ +#define ESS_SCR_AUDIO_ENABLE 0x04 +#define ESS_SCR_AUDIO_220 0x00 +#define ESS_SCR_AUDIO_230 0x01 +#define ESS_SCR_AUDIO_240 0x02 +#define ESS_SCR_AUDIO_250 0x03 +#endif + +/*****************************************************************************/ +/* DSP Timeout Definitions */ +/*****************************************************************************/ +#define ESS_RESET_TIMEOUT 5000 /* ??? */ +#define ESS_READ_TIMEOUT 5000 /* number of times to try a read */ +#define ESS_WRITE_TIMEOUT 5000 /* number of times to try a write */ + + +#define ESS_NPORT 16 +#define ESS_DSP_RESET 0x06 +#define ESS_MAGIC 0xAA /* response to successful reset */ + +#define ESS_DSP_READ 0x0A +#define ESS_DSP_WRITE 0x0C + +#define ESS_DSP_RW_STATUS 0x0C +#define ESS_CLEAR_INTR 0x0E + +#define ESS_DSP_READ_STATUS 0x0C +#define ESS_DSP_READ_MASK 0x40 +#define ESS_DSP_READ_READY 0x40 + +#define ESS_DSP_WRITE_STATUS 0x0C +#define ESS_DSP_WRITE_MASK 0x80 +#define ESS_DSP_WRITE_READY 0x00 + + +#define ESS_MIX_REG_SELECT 0x04 +#define ESS_MIX_REG_DATA 0x05 +#define ESS_MIX_RESET 0x00 /* mixer reset port and value */ + + +/* + * ESS Mixer registers + */ +#define ESS_MREG_SAMPLE_RATE 0x70 /* sample rate for Audio2 channel */ +#define ESS_MREG_FILTER_CLOCK 0x72 /* filter clock for Audio2 channel */ +#define ESS_MREG_XFER_COUNTLO 0x74 /* low-byte of DMA transfer size */ +#define ESS_MREG_XFER_COUNTHI 0x76 /* high-byte of DMA transfer size */ +#define ESS_MREG_AUDIO2_CTRL1 0x78 /* control register 1 for Audio2: */ +#define ESS_AUDIO2_CTRL1_XFER_SIZE 0x20 + /* 0=8-bit/1=16-bit */ +#define ESS_AUDIO2_CTRL1_FIFO_ENABLE 0x02 + /* 0=disable/1=enable */ +#define ESS_AUDIO2_CTRL1_DAC_ENABLE 0x01 + /* 0=disable/1=enable */ +#define ESS_MREG_AUDIO2_CTRL2 0x7A /* control register 2 for Audio2: */ +#define ESS_AUDIO2_CTRL2_FIFO_SIZE 0x01 + /* 0=8-bit/1=16-bit */ +#define ESS_AUDIO2_CTRL2_CHANNELS 0x02 + /* 0=mono/1=stereo */ +#define ESS_AUDIO2_CTRL2_FIFO_SIGNED 0x04 + /* 0=unsigned/1=signed */ +#define ESS_AUDIO2_CTRL2_DMA_ENABLE 0x20 + /* 0=disable/1=enable */ diff --git a/sys/arch/arm32/isa/essvar.h b/sys/arch/arm32/isa/essvar.h new file mode 100644 index 000000000000..98ee9900fc59 --- /dev/null +++ b/sys/arch/arm32/isa/essvar.h @@ -0,0 +1,211 @@ +/* $NetBSD: essvar.h,v 1.1 1998/06/08 17:49:43 tv Exp $ */ + +/* + * Copyright 1997 + * Digital Equipment Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and conditions. + * Subject to these conditions, you may download, copy, install, + * use, modify and distribute this software in source and/or binary + * form. No title or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions as + * they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or logo of + * Digital Equipment Corporation. Neither the "Digital Equipment + * Corporation" name nor any trademark or logo of Digital Equipment + * Corporation may be used to endorse or promote products derived + * from this software without the prior written permission of + * Digital Equipment Corporation. + * + * 3) This software is provided "AS-IS" and any express or implied + * warranties, including but not limited to, any implied warranties + * of merchantability, fitness for a particular purpose, or + * non-infringement are disclaimed. In no event shall DIGITAL be + * liable for any damages whatsoever, and in particular, DIGITAL + * shall not be liable for special, indirect, consequential, or + * incidental damages or damages for lost profits, loss of + * revenue or loss of use, whether such damages arise in contract, + * negligence, tort, under statute, in equity, at law or otherwise, + * even if advised of the possibility of such damage. + */ + +/* +** @(#) $RCSfile: essvar.h,v $ $Revision: 1.1 $ (SHARK) $Date: 1998/06/08 17:49:43 $ +** +**++ +** +** essvar.h +** +** FACILITY: +** +** DIGITAL Network Appliance Reference Design (DNARD) +** +** MODULE DESCRIPTION: +** +** This module contains the structure definitions and function +** prototypes for the ESS Technologies 1887/888 sound chip +** driver. +** +** AUTHORS: +** +** Blair Fidler Software Engineering Australia +** Gold Coast, Australia. +** +** CREATION DATE: +** +** May 12, 1997. +** +** MODIFICATION HISTORY: +** +**-- +*/ +#define ESS_DAC_PLAY_VOL 0 +#define ESS_MIC_PLAY_VOL 1 +#define ESS_LINE_PLAY_VOL 2 +#define ESS_SYNTH_PLAY_VOL 3 +#define ESS_CD_PLAY_VOL 4 +#define ESS_AUXB_PLAY_VOL 5 +#define ESS_INPUT_CLASS 6 + +#define ESS_MASTER_VOL 7 +#define ESS_PCSPEAKER_VOL 8 +#define ESS_OUTPUT_CLASS 9 + +#define ESS_DAC_REC_VOL 10 +#define ESS_MIC_REC_VOL 11 +#define ESS_LINE_REC_VOL 12 +#define ESS_SYNTH_REC_VOL 13 +#define ESS_CD_REC_VOL 14 +#define ESS_AUXB_REC_VOL 15 +#define ESS_MIC_PREAMP 16 +#define ESS_RECORD_VOL 17 +#define ESS_RECORD_SOURCE 18 +#define ESS_RECORD_CLASS 19 + +#define ESS_RECORD_MONITOR 20 +#define ESS_MONITOR_CLASS 21 + +#define ESS_NDEVS 22 + +struct ess_audio_channel +{ + int drq; /* DMA channel */ + int mode; /* DMA mode */ +#define ESS_MODE_8BIT 0 +#define ESS_MODE_16BIT 1 + int irq; /* IRQ line for this DMA channel */ + void *ih; /* interrupt vectoring */ + u_long nintr; /* number of interrupts taken */ + void (*intr)(void*); /* ISR for DMA complete */ + void *arg; /* arg for intr() */ + + /* Status information */ + int active; /* boolean: channel in use? */ + int dmaflags; /* value last passed to isa_dmastart */ + caddr_t dmaaddr; /* value last passed to isa_dmastart */ + vm_size_t dmacnt; /* value last passed to isa_dmastart */ + + u_int rate; /* sample rate in Hz*/ + u_int channels; /* 1:mono, 2:stereo */ + u_int precision; /* size of each sample in bits */ + u_int encoding; /* method used to encode samples */ +}; + +struct ess_softc +{ + /* REVISIT: currently the initial part of this is directly from sbdsp */ + struct device sc_dev; /* base device */ + struct isadev sc_id; /* ISA device */ + isa_chipset_tag_t sc_ic; + bus_space_tag_t sc_iot; /* tag */ + bus_space_handle_t sc_ioh; /* handle */ + void *sc_ih; /* interrupt vectoring */ + + int sc_iobase; /* I/O port base address */ + + u_short sc_open; /* reference count of open calls */ + + u_char gain[ESS_NDEVS][2]; /* kept in input levels */ +#define ESS_LEFT 0 +#define ESS_RIGHT 1 + + u_int out_port; /* output port */ + u_int in_mask; /* input ports */ + u_int in_port; /* XXX needed for MI interface */ + + u_int spkr_state; /* non-null is on */ + + struct ess_audio_channel sc_in; /* audio channel for record */ + struct ess_audio_channel sc_out;/* audio channel for playback */ + + void (*sc_mintr)(void*, int);/* midi input intr handler */ + + u_int sc_model; +#define ESS_UNSUPPORTED 0 +#define ESS_1888 1 +#define ESS_1887 2 +#define ESS_888 3 + + u_int sc_version; /* Legacy ES688/ES1688 ID */ +}; + + +#ifdef _KERNEL +#ifdef __BROKEN_INDIRECT_CONFIG +int ess_probe __P((struct device *, void *, void *)); +#else +int ess_probe __P((struct device *, struct cfdata *, void *)); +#endif +void ess_attach __P((struct device *, struct device *, void *)); + +int ess_open __P((dev_t, int)); +void ess_close __P((void *)); +int ess_getdev __P((void *, struct audio_device *)); +int ess_drain __P((void *)); + +int ess_query_encoding __P((void *, struct audio_encoding *)); + +int ess_set_params __P((void *, int, struct audio_params *, struct audio_params *)); +int ess_set_in_sr __P((void *, u_long, int)); +int ess_set_out_sr __P((void *, u_long, int)); +int ess_set_in_precision __P((void *, u_int)); +int ess_set_out_precision __P((void *, u_int)); +int ess_set_in_channels __P((void *, int)); +int ess_set_out_channels __P((void *, int)); + +int ess_round_blocksize __P((void *, int)); + +int ess_set_out_port __P((void *, int)); +int ess_get_out_port __P((void *)); +int ess_set_in_port __P((void *, int)); +int ess_get_in_port __P((void *)); + +int ess_commit_settings __P((void *)); + +u_int ess_get_silence __P((int)); + +int ess_dma_output __P((void *, void *, int, void (*)(void *), void *)); +int ess_dma_input __P((void *, void *, int, void (*)(void *), void *)); +int ess_halt_output __P((void *)); +int ess_halt_input __P((void *)); +int ess_cont_output __P((void *)); +int ess_cont_input __P((void *)); + +int ess_intr_output __P((void *)); +int ess_intr_input __P((void *)); + +int ess_speaker_ctl __P((void *, int)); + +int ess_getdev __P((void *, struct audio_device *)); +int ess_setfd __P((void *, int)); + +int ess_set_port __P((void *, mixer_ctrl_t *)); +int ess_get_port __P((void *, mixer_ctrl_t *)); + +int ess_query_devinfo __P((void *, mixer_devinfo_t *)); + +#endif diff --git a/sys/arch/arm32/isa/icu.h b/sys/arch/arm32/isa/icu.h new file mode 100644 index 000000000000..50d9354afd69 --- /dev/null +++ b/sys/arch/arm32/isa/icu.h @@ -0,0 +1,71 @@ +/* $NetBSD: icu.h,v 1.1 1998/06/08 17:49:43 tv Exp $ */ + +/*- + * 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. + * + * 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. + * + * @(#)icu.h 5.6 (Berkeley) 5/9/91 + */ + +/* + * AT/386 Interrupt Control constants + * W. Jolitz 8/89 + */ + +#ifndef _ARM32_ISA_ICU_H_ +#define _ARM32_ISA_ICU_H_ + +#ifndef _LOCORE + +/* + * Interrupt "level" mechanism variables, masks, and macros + */ +extern unsigned imen; /* interrupt mask enable */ + +#define SET_ICUS() (outb(IO_ICU1 + 1, imen), outb(IO_ICU2 + 1, imen >> 8)) + +#endif /* !_LOCORE */ + +/* + * Interrupt enable bits -- in order of priority + */ +#define IRQ_SLAVE 2 + +/* + * Interrupt Control offset into Interrupt descriptor table (IDT) + */ +#define ICU_OFFSET 32 /* 0-31 are processor exceptions */ +#define ICU_LEN 16 /* 32-47 are ISA interrupts */ + +#endif /* !_ARM32_ISA_ICU_H_ */ diff --git a/sys/arch/arm32/isa/if_cs_isa.c b/sys/arch/arm32/isa/if_cs_isa.c new file mode 100644 index 000000000000..a4a0f3272af2 --- /dev/null +++ b/sys/arch/arm32/isa/if_cs_isa.c @@ -0,0 +1,3093 @@ +/* $NetBSD: if_cs_isa.c,v 1.1 1998/06/08 17:49:43 tv Exp $ */ + +/* + * Copyright 1997 + * Digital Equipment Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and conditions. + * Subject to these conditions, you may download, copy, install, + * use, modify and distribute this software in source and/or binary + * form. No title or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions as + * they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or logo of + * Digital Equipment Corporation. Neither the "Digital Equipment + * Corporation" name nor any trademark or logo of Digital Equipment + * Corporation may be used to endorse or promote products derived + * from this software without the prior written permission of + * Digital Equipment Corporation. + * + * 3) This software is provided "AS-IS" and any express or implied + * warranties, including but not limited to, any implied warranties + * of merchantability, fitness for a particular purpose, or + * non-infringement are disclaimed. In no event shall DIGITAL be + * liable for any damages whatsoever, and in particular, DIGITAL + * shall not be liable for special, indirect, consequential, or + * incidental damages or damages for lost profits, loss of + * revenue or loss of use, whether such damages arise in contract, + * negligence, tort, under statute, in equity, at law or otherwise, + * even if advised of the possibility of such damage. + */ + +/* +**++ +** FACILITY +** +** Device Driver for the Crystal CS8900 ISA Ethernet Controller. +** +** ABSTRACT +** +** This module provides standard ethernet access for INET protocols +** only. +** +** AUTHORS +** +** Peter Dettori SEA - Software Engineering. +** +** CREATION DATE: +** +** 13-Feb-1997. +** +** MODIFICATION HISTORY: +** +** $Log: if_cs_isa.c,v $ +** Revision 1.1 1998/06/08 17:49:43 tv +** Add ISA support needed for Shark and the CATS board (CATS isa_machdep not +** here yet). +** +** Revision 1.1 1998/06/08 17:23:49 tv +** Add ISA support files for Shark (and the CATS board, which shares much of +** this code). This will probably still need some merging with the CATS +** source. +** +** Revision 1.27 1998/01/20 17:59:40 cgd +** update for moved headers +** +** Revision 1.26 1998/01/12 19:29:36 cgd +** use arm32/isa versions of isadma code. +** +** Revision 1.25 1997/12/12 01:35:27 cgd +** convert to use new arp code (from Brini) +** +** Revision 1.24 1997/12/10 22:31:56 cgd +** trim some fat (get rid of ability to explicitly supply enet addr, since +** it was never used and added a bunch of code which really doesn't belong in +** an enet driver), and clean up slightly. +** +** Revision 1.23 1997/10/06 16:42:12 cgd +** copyright notices +** +** Revision 1.22 1997/06/20 19:38:01 chaiken +** fixes some smartcard problems +** +** Revision 1.21 1997/06/10 02:56:20 grohn +** Added call to ledNetActive +** +** Revision 1.20 1997/06/05 00:47:06 dettori +** Changed csProcessRxDMA to reset and re-initialise the +** ethernet chip when DMA gets out of sync, or mbufs +** can't be allocated. +** +** Revision 1.19 1997/06/03 03:09:58 dettori +** Turn off txInProgress flag when a transmit underrun +** occurs. +** +** Revision 1.18 1997/06/02 00:04:35 dettori +** redefined the transmit table to get around the nfs_timer bug while we are +** looking into it further. +** +** Also changed interrupts from EDGE to LEVEL. +** +** Revision 1.17 1997/05/27 23:31:01 dettori +** Pulled out changes to DMAMODE defines. +** +** Revision 1.16 1997/05/23 04:25:16 cgd +** reformat log so it fits in 80cols +** +** Revision 1.15 1997/05/23 04:22:18 cgd +** remove the existing copyright notice (which Peter Dettori indicated +** was incorrect, copied from an existing NetBSD file only so that the +** file would have a copyright notice on it, and which he'd intended to +** replace). Replace it with a Digital copyright notice, cloned from +** ess.c. It's not really correct either (it indicates that the source +** is Digital confidential!), but is better than nothing and more +** correct than what was there before. +** +** Revision 1.14 1997/05/23 04:12:50 cgd +** use an adaptive transmit start algorithm: start by telling the chip +** to start transmitting after 381 bytes have been fed to it. if that +** gets transmit underruns, ramp down to 1021 bytes then "whole +** packet." If successful at a given level for a while, try the next +** more agressive level. This code doesn't ever try to start +** transmitting after 5 bytes have been sent to the NIC, because +** that underruns rather regularly. The back-off and ramp-up mechanism +** could probably be tuned a little bit, but this works well enough to +** support > 1MB/s transmit rates on a clear ethernet (which is about +** 20-25% better than the driver had previously been getting). +** +** Revision 1.13 1997/05/22 21:06:54 cgd +** redo csCopyTxFrame() from scratch. It had a fatal flaw: it was blindly +** casting from u_int8_t * to u_int16_t * without worrying about alignment +** issues. This would cause bogus data to be spit out for mbufs with +** misaligned data. For instance, it caused the following bits to appear +** on the wire: +** ... etBND 1S2C .SHA(K) R ... +** 11112222333344445555 +** which should have appeared as: +** ... NetBSD 1.2C (SHARK) ... +** 11112222333344445555 +** Note the apparent 'rotate' of the bytes in the word, which was due to +** incorrect unaligned accesses. This data corruption was the cause of +** incoming telnet/rlogin hangs. +** +** Revision 1.12 1997/05/22 01:55:32 cgd +** reformat log so it fits in 80cols +** +** Revision 1.11 1997/05/22 01:50:27 cgd +** * enable input packet address checking in the BPF+IFF_PROMISCUOUS case, +** so packets aimed at other hosts don't get sent to ether_input(). +** * Add a static const char *rcsid initialized with an RCS Id tag, so that +** you can easily tell (`strings`) what version of the driver is in your +** kernel binary. +** * get rid of ether_cmp(). It was inconsistently used, not necessarily +** safe, and not really a performance win anyway. (It was only used when +** setting up the multicast logical address filter, which is an +** infrequent event. It could have been used in the IFF_PROMISCUOUS +** address check above, but the benefit of it vs. bcmp or memcmp would be +** inconsequential, there.) Use bcmp() instead. Eventually, this should +** use memcmp(), so that the compiler can optimize it into inline code. +** * restructure csStartOuput to avoid the following bugs in the case where +** txWait was being set: +** * it would accidentally drop the outgoing packet if told to wait +** but the outgoing packet queue was empty. +** * it would bpf_mtap() the outgoing packet multiple times (once for +** each time it was told to wait), and would also recalculate +** the length of the outgoing packet each time it was told to +** wait. +** While there, rename txWait to txLoop, since with the new structure of +** the code, the latter name makes more sense. +** +** Revision 1.10 1997/05/19 02:03:20 cgd +** Set RX_CTL in csSetLadrFilt(), rather than csInitChip(). csInitChip() +** is the only caller of csSetLadrFilt(), and always calls it, so this +** ends up being logically the same. In csSetLadrFilt(), if IFF_PROMISC +** is set, enable promiscuous mode (and set IFF_ALLMULTI), otherwise behave +** as before. +** +** Revision 1.9 1997/05/19 01:45:37 cgd +** create a new function, csEtherInput(), which does received-packet +** BPF and ether_input processing. This code used to be in three places, +** and centralizing it will make adding IFF_PROMISC support much easier. +** Also, in csCopyTxFrame(), put it some (currently disabled) code to +** do copies with bus_space_write_region_2(). It's more correct, and +** potentially more efficient. That function needs to be gutted (to +** deal properly with alignment issues, which it currently does wrong), +** however, and the change doesn't gain much, so there's no point in +** enabling it now. +** +** Revision 1.8 1997/05/19 01:17:10 cgd +** fix a comment re: the setting of the TxConfig register. Clean up +** interface counter maintenance (make it use standard idiom). +** +**-- +*/ + +#if 0 +static const char *rcsid = "$Id: if_cs_isa.c,v 1.1 1998/06/08 17:49:43 tv Exp $"; +#endif + + +/* + * INCLUDE DEFINITIONS + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#ifdef INET +#include +#include +#endif + +#include + +#include +#include +#include + +#include +#include +#include + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include +#include +#endif + +#include "if_csvar.h" +#ifdef SHARK +#include +#endif + +#ifdef OFW +#include +#endif + +/* + * MACRO DEFINITIONS + * + */ +#define CS_OUTPUT_LOOP_MAX 100 /* max times round notorious tx loop */ +#define DMA_BUFFER_SIZE 16384 /* 16K or 64K */ +#define DMA_STATUS_BITS 0x0007 /* bit masks for checking DMA status */ +#define DMA_STATUS_OK 0x0004 +#define CS_MEM_SIZE 4096 /* 4096 bytes of on chip memory */ +#define ETHER_MTU 1518 /* ETHERMTU is defiend in if_ether.h as 1500 + * ie. without the header. + */ + +/* + * the kerndebug macros reserve use of the + * first and last byte, so we can only use the + * middle two bytes for flags. + */ + +#define CSPROBE_DBG_INFO 0x00000100 +#define CSATTACH_DBG_INFO 0x00000200 +#define CSRESET_DBG_INFO 0x00000400 +#define CSINIT_DBG_INFO 0x00000800 +#define CSIOCTL_DBG_INFO 0x00001000 +#define CSSTARTTX_DBG_INFO 0x00002000 +#define CSTXEVENT_DBG_INFO 0x00004000 +#define CSRXNORM_DBG_INFO 0x00008000 +#define CSRXEARLY_DBG_INFO 0x00010000 +#define CSRXDMA_DBG_INFO 0x00020000 +#define CSMCAST_DBG_INFO 0x00040000 +#define CSBUFF_DBG_INFO 0x00080000 +#define CSUTIL_DBG_INFO 0x00800000 + +/* + * FUNCTION PROTOTYPES + */ +#ifdef __BROKEN_INDIRECT_CONFIG +int csProbe __P((struct device *, void *, void *)); +#else +int csProbe __P((struct device *, struct cfdata *, void *)); +#endif +void csAttach __P((struct device *, struct device *, void *)); +ushort csReadPacketPage __P(( struct cs_softc *sc, ushort offset )); +void csWritePacketPage __P(( struct cs_softc *sc, ushort offset, ushort value )); +int csGetUnspecifiedParms __P(( struct cs_softc *sc )); +int csValidateParms __P(( struct cs_softc *sc )); +int csGetEthernetAddr __P(( struct cs_softc *sc )); +int csReadEEPROM __P(( struct cs_softc *sc, ushort offset, ushort *pValue )); +int csResetChip __P(( struct cs_softc *sc )); +void csShow __P(( struct cs_softc *sc, int Zap )); +int csInit __P(( struct cs_softc *sc )); +void csReset __P(( void * )); +int csIoctl __P(( struct ifnet *, ulong com, caddr_t pData )); +void csInitChip __P(( struct cs_softc *sc )); +int csIntr __P(( void *arg )); +void csBufferEvent __P(( struct cs_softc *sc, ushort BuffEvent )); +void csTransmitEvent __P(( struct cs_softc *sc, ushort txEvent )); +void csReceiveEvent __P(( struct cs_softc *sc, ushort rxEvent )); +void csEtherInput __P(( struct cs_softc *sc, struct mbuf *m )); +void csProcessReceive __P(( struct cs_softc *sc )); +void csProcessRxEarly __P(( struct cs_softc *sc )); +void csProcessRxDMA(struct cs_softc *sc); +void csStartOutput __P((struct ifnet *ifp )); +void csCopyTxFrame __P(( struct cs_softc *sc, struct mbuf *pMbufChain )); +void csSetLadrFilt( struct cs_softc *sc, struct ethercom *ec ); +ushort csHashIndex(char *addr); +void csCounterEvent( struct cs_softc *sc, ushort cntEvent ); + +/* + * GLOBAL DECLARATIONS + */ + +/* + * Xmit-early table. + * + * To get better performance, we tell the chip to start packet + * transmission before the whole packet is copied to the chip. + * However, this can fail under load. When it fails, we back off + * to a safer setting for a little while. + * + * txcmd is the value of txcmd used to indicate when to start transmission. + * better is the next 'better' state in the table. + * better_count is the number of output packets before transition to the + * better state. + * worse is the next 'worse' state in the table. + * + * Transition to the next worse state happens automatically when a + * transmittion underrun occurs. + */ +struct cs_xmit_early { + u_int16_t txcmd; + int better; + int better_count; + int worse; +} cs_xmit_early_table[3] = { + { TX_CMD_START_381, 0, INT_MAX, 1, }, + { TX_CMD_START_1021, 0, 50000, 2, }, + { TX_CMD_START_ALL, 1, 5000, 2, }, +}; + +/* + * this is static config information, the configuration file + * expects this struct to exist and contain the probe and attach + * functions, and softc size for allocation. + */ +struct cfattach cs_ca = +{ + sizeof(struct cs_softc), csProbe, csAttach +}; + +extern struct cfdriver cs_cd; + +int csdebug = 0x00000000; /* debug status, used with kerndebug macros */ +#ifndef SHARK +/* SHARKS don't have the kernel in the lower megs of physical + * and hence this won't work. I don't know any other machines that + * this won't work for (I didn't look either) so I ifdef'd it like this. + */ +char cs_dma_buffer[DMA_BUFFER_SIZE] __attribute__ ((aligned (4096))); +#endif + + +#ifdef __BROKEN_INDIRECT_CONFIG +/*-------------------------------------------------------------------------*/ +/* +** Function : csProbe. +** +** Description : This function will be called at system startup +** if the kernel is configured for +** cs8900 ethernet devices. The probe will +** check if a cs8900 ethernet device is plugged +** into the isa bus. +** +** Parameters : struct device *parent unused parent device +** void *match is a cs_softc structure +** void *aux is a pointer to isa_attach_args +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : 1 if the device is found 0 if not. +** +** Side Effects : None. +*/ +int csProbe(struct device *parent, void *match, void *aux) +#else +int csProbe(struct device *parent, struct cfdata *cf, void *aux) +#endif +{ +#ifdef __BROKEN_INDIRECT_CONFIG + struct cs_softc *sc = match; +#else + struct cs_softc junksc; + struct cs_softc *sc = &junksc; +#endif + struct isa_attach_args *ia = aux; /* this holds info statically + * configured in the config file + */ + ushort isaId; + + sc->sc_iobase = ia->ia_iobase; /* store the IO base address for later use */ + sc->sc_drq = ia->ia_drq; /* store the drq line */ + sc->inMemoryMode = FALSE; /* must work in IO mode initially */ + + /* do bus handle stuff + */ + sc->sc_iot = ia->ia_iot; + sc->sc_memt = ia->ia_memt; + + /* map the bus space here and put the arguments in the softc + * so that the csRead/WritePacketPage can operate. + */ + if ( bus_space_map(sc->sc_iot, ia->ia_iobase, CS8900_IOSIZE, 0, + &sc->sc_ioh) != 0 ) + { + return 0; /* bus map didn't work, not much we can do */ + } + + /* Verify that we can read from the chip */ + isaId = csReadPacketPage( sc, PKTPG_EISA_NUM ); + + /* Verify that the chip is a Crystal Semiconductor chip */ + if ( isaId != EISA_NUM_CRYSTAL ) + { + /* unmap the bus space and clear the softc + * structure. + */ + bus_space_unmap(sc->sc_iot, sc->sc_ioh, CS8900_IOSIZE); + sc->sc_iot = 0; + sc->sc_ioh = 0; + printf("\n%s - Chip is not a Crystal Semiconductor chip\n", + (sc->sc_dev).dv_xname); + return 0; + } + + /* Verify that the chip is a CS8900 */ + if ( (csReadPacketPage( sc, PKTPG_PRODUCT_ID ) & PROD_ID_MASK ) + != PROD_ID_CS8900 ) + { + /* unmap the bus space and clear the softc + * structure. + */ + bus_space_unmap(sc->sc_iot, sc->sc_ioh, CS8900_IOSIZE); + sc->sc_iot = 0; + sc->sc_ioh = 0; + printf("\n%s - Chip is not a CS8900\n", (sc->sc_dev).dv_xname); + return 0; + } + +#ifdef __BROKEN_INDIRECT_CONFIG + /* because of the BROKEN_INDIRECT... + * macro must unmap the bus space here. + */ + bus_space_unmap(sc->sc_iot, sc->sc_ioh, CS8900_IOSIZE); +#endif + + ia->ia_iosize = CS8900_IOSIZE; /* this is not passed to us for some reason, + * so we just hardcode it here + */ + return 1; /* chip has been located can now attach it */ +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csAttach. +** +** Description : This function will be called at system startup +** time if the kernel is configured for +** cs8900 ethernet devices and one has been found +** on the isa bus by the probe. The attach will +** will register the device with the system structures +** so that upper layer protocols can access the network. +** +** Parameters : struct device *parent unused parent device +** struct device *self is actually a softc struct +** void *aux is a pointer to isa_attach_args +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : None. +** +** Side Effects : The device is registered as an ether device on +** an isa bus. +*/ +void csAttach(struct device *parent, struct device *self, void *aux) +{ + struct cs_softc *sc = (struct cs_softc *)self; + struct isa_attach_args *ia = aux; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + +#ifndef __BROKEN_INDIRECT_CONFIG + sc->sc_iobase = ia->ia_iobase; + sc->sc_drq = ia->ia_drq; + sc->inMemoryMode = FALSE; + sc->sc_iot = ia->ia_iot; + sc->sc_memt = ia->ia_memt; + if (bus_space_map(sc->sc_iot, ia->ia_iobase, + ia->ia_iosize, 0, &sc->sc_ioh) != 0) + { + panic("csAttach: bus space map failed"); + } +#endif + + printf(": %s Ethernet\n", sc->sc_dev.dv_xname); + + /* the first thing to do is check that the mbuf cluster size is + * greater than the MTU for an ethernet frame. The code depends + * on this and to port this to a OS where this was not the case + * would not be straightforward. + */ + if (MCLBYTES < ETHER_MTU) + { + panic("%s : Attach failed -> machines mbuf cluster size is insufficient\n", + sc->sc_dev.dv_xname); + } + + /* save relevant data passed in through the isa_attach_args structure */ + sc->sc_int = ia->ia_irq; + + /* this driver only supports mapped memory and DMA modes of + * operation. So if a memory base address is not given we can't + * go on. + */ + if (ia->ia_maddr != MADDRUNK) + { + sc->pPacketPagePhys = ia->ia_maddr; + } + else + { + panic("%s : Attach failed -> configuration info incomplete, no maddr\n", + sc->sc_dev.dv_xname); + } + + /* Start out in IO mode */ + sc->inMemoryMode = FALSE; + + /* Start out not transmitting */ + sc->txInProgress = FALSE; + + /* Set up early transmit threshhold */ + sc->sc_xe_ent = 0; + sc->sc_xe_togo = cs_xmit_early_table[sc->sc_xe_ent].better_count; + + /* Initialize ifnet structure. */ + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_start = csStartOutput; + ifp->if_ioctl = csIoctl; + ifp->if_watchdog = NULL; /* no watchdog at this stage */ + ifp->if_flags = IFF_SIMPLEX | IFF_NOTRAILERS | + IFF_BROADCAST | IFF_MULTICAST; + +#ifdef OFW + /* Dig MAC address out of the firmware. */ + if (OF_getprop((int)ia->ia_aux, "mac-address", sc->sc_enaddr, + sizeof(sc->sc_enaddr)) < 0) { + int ofnet_handle; + ofnet_handle = OF_open("/isa/ethernet"); + if (!ofnet_handle) + panic("OF_open: Unable to open /isa/ethernet"); + if (OF_getprop((int)ia->ia_aux, "mac-address", + sc->sc_enaddr, + sizeof(sc->sc_enaddr)) < 0) + panic("csAttach: no mac-address"); + /* closing the device causes the kernel to panic ... */ +#if 0 + OF_close(ofnet_handle); +#endif + } + + sc->mediaType = MEDIA_10BASET; +#else + /* Get parameters, which were not specified, from the EEPROM */ + if ( csGetUnspecifiedParms(sc) == CS_ERROR ) + { + printf("%s : Couldn't get the unspecified parameters in the attach\n",\ + sc->sc_dev.dv_xname); + return ; + } + + /* Verify that parameters are valid */ + if ( csValidateParms(sc) == CS_ERROR ) + { + printf("%s : Couldn't get the validate parameters in the attach\n",\ + sc->sc_dev.dv_xname ); + return ; + } + + /* Get and store the Ethernet address */ + if ( csGetEthernetAddr(sc) == CS_ERROR ) + { + printf("%s : couldn't get the ethernet address in the attach\n",\ + sc->sc_dev.dv_xname ); + return ; + } +#endif + + printf("%s : address %s\n", sc->sc_dev.dv_xname, + ether_sprintf(sc->sc_enaddr)); + + /* + * First figure out which memory mode to use, by: + * 1. if we have a valid drq then use DMA only mode + * for receives. Chip only supports 5,6 & 7 + * 2. else we have been given a memory address for IO + * mapping then use it. + */ + + if ( (sc->sc_drq >= 5) && (sc->sc_drq <= 7) ) + { + /* The sharks can't use the cs_dma_buffer because + * kernel is not placed in the lower megs of physical + * memory. Instead they call a function which returns + * an offset into the isaphysmem, which is a block + * of memory allocated at boot-time for use as + * DMA bounce buffers. The SHARKS allocate another + * 64Kbytes for a possible large buffer and allow + * access to it through isa_dmabuffer_get() defined + * in isadmavar.h + */ +#ifdef SHARK + /* If we get the buffer we never relinquish it, + * primarily because I expect the ethernet + * would be required always. + */ + sc->dmaBase = (char *) isa_dmabuffer_get(); +#else + sc->dmaBase = cs_dma_buffer; +#endif + + /* if we couldn't get a dma buffer then we can't + * go on with DMA. + */ + if ( sc->dmaBase != NULL ) + { + KERN_DEBUG(csdebug, CSATTACH_DBG_INFO, + ("csAttach : alloced memory address = 0x%x\n"\ + ,(int) sc->dmaBase)); + + sc->dmaMemSize = CS8900_DMA_BUFFER_SIZE; + + /* now set the config flag and let the csResetChip and csInitChip + * routines do the rest. + */ + KERN_DEBUG(csdebug, CSATTACH_DBG_INFO, + ("csAttach : setting DMA mode\n")); + + sc->configFlags |= CFGFLG_DMA_MODE; + } + /* now go on and try mapped memory, or programmed IO. */ + } /* if (drq) */ + + /* the default EEPROM configuration is to use programmed IO. If + * we want to use mapped memory we must manually set the + * chips registers. + * + * check here to see if we are configured to use mapped memory. + */ + if ( sc->pPacketPagePhys != MADDRUNK ) + { + /* check that the configured IO memory size is 4K + * otherwise hardcode the driver to use programmed IO. + */ + if ( ia->ia_msize != CS_MEM_SIZE ) + { + panic(": %s IO mem size is not CS_MEM_SIZE invalid configuration\n" + , (sc->sc_dev).dv_xname); + } + else + { + /* map the memory addresses */ + if (bus_space_map(sc->sc_memt, sc->pPacketPagePhys, + CS_MEM_SIZE, 0, &sc->sc_memh) != 0) + { + panic("%s : csAttach : bus space map failed\n", + (sc->sc_dev).dv_xname); + } + else + { + /* I think we always want to use the external latch logic */ + sc->configFlags = sc->configFlags | CFGFLG_MEM_MODE | + CFGFLG_USE_SA | CFGFLG_IOCHRDY; + } + } + } + + /* Attach the interface. */ + if_attach(ifp); + /* add to generic ethernet code */ + ether_ifattach(ifp, sc->sc_enaddr); + + /* attach the Berkely Packet Filter interface */ +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + + sc->sc_sh = shutdownhook_establish(csReset, sc); + if (sc->sc_sh == NULL) + { + panic("csAttach: can't establish shutdownhook"); + } + /* Setup the interrupt handler */ + sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_LEVEL, + IPL_NET, csIntr, sc); + + /* Reset the chip */ + if ( csResetChip(sc) == CS_ERROR ) + { + panic("\n%s - Can not reset the ethernet chip\n", + (sc->sc_dev).dv_xname ); + } + + return ; +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csReadPacketPage +** +** Description : This routine reads a word from the PacketPage at +** the specified offset. +** +** This routine should be maually in-lined as required +** for fast loops reading from the chip. ie. the +** loop should be inside the if (memory-mode) statement. +** +** Parameters : struct cs_softc *sc +** ushort offset is the offset in the PacketPage to +** read +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : The 16 bit PacketPage register. +** +** Side Effects : None. +** +*/ +ushort csReadPacketPage( struct cs_softc *sc, ushort offset ) +{ + if ( sc->inMemoryMode ) + { + return bus_space_read_2( sc->sc_memt, sc->sc_memh, offset ); + } + else /* In IO mode */ + { + bus_space_write_2( sc->sc_iot, sc->sc_ioh, PORT_PKTPG_PTR, offset ); + return bus_space_read_2( sc->sc_iot, sc->sc_ioh, PORT_PKTPG_DATA ); + } +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csWritePacketPage +** +** Description : This routine writes a value to the PacketPage at +** the specified offset. +** +** This routine should be maually in-lined as required +** for fast loops writing to the chip. +** +** Parameters : struct cs_softc *sc +** offset, PacketPage register offset +** value, value to write to the register +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : void. +** +** Side Effects : None. +** +*/ +void csWritePacketPage( struct cs_softc *sc, ushort offset, ushort value ) +{ + if ( sc->inMemoryMode ) + { + bus_space_write_2( sc->sc_memt, sc->sc_memh, offset, value ); + } + else /* In IO mode */ + { + bus_space_write_2( sc->sc_iot, sc->sc_ioh, PORT_PKTPG_PTR, offset ); + bus_space_write_2( sc->sc_iot, sc->sc_ioh, PORT_PKTPG_DATA, value ); + } +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csGetUnspecifiedParms +** +** Description : This routine gets parameters, that were not +** specifed to csAttach(), from the EEPROM and puts +** them in the cs_softc structure. If all the +** parameters were specified in csAttach(), then this +** routine does not attempt to read the EEPROM. +** +** Parameters : struct cs_softc *sc +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : CS_ERROR if it can't get the info from the EEPROM, +** else CS_OK. +** +** Side Effects : None. +** +*/ +int csGetUnspecifiedParms( struct cs_softc *sc ) +{ + ushort selfStatus; + ushort isaConfig; + ushort memBase; + ushort adapterConfig; + ushort xmitCtl; + + /* If all of these parameters were specified */ + if ( sc->configFlags != 0 && sc->pPacketPagePhys != MADDRUNK + && sc->sc_int != 0 && sc->mediaType != 0 ) + { + KERN_DEBUG(csdebug, CSUTIL_DBG_INFO, + ("csGetUnspecifiedParms : have got everything already\n")); + return CS_OK; /* Don't need to get anything from the EEPROM */ + } + + /* Verify that the EEPROM is present and OK */ + selfStatus = csReadPacketPage( sc, PKTPG_SELF_ST ); + if ( !((selfStatus & SELF_ST_EEP_PRES) && (selfStatus & SELF_ST_EEP_OK)) ) + { + printf("\n%s - EEPROM is missing or bad\n", (sc->sc_dev).dv_xname); + return CS_ERROR; + } + + /* Get ISA configuration from the EEPROM */ + if ( csReadEEPROM(sc, EEPROM_ISA_CFG, &isaConfig) == CS_ERROR ) + return CS_ERROR; + + /* Get adapter configuration from the EEPROM */ + if ( csReadEEPROM(sc, EEPROM_ADPTR_CFG, &adapterConfig) == CS_ERROR ) + return CS_ERROR; + + /* Get transmission control from the EEPROM */ + if ( csReadEEPROM(sc, EEPROM_XMIT_CTL, &xmitCtl) == CS_ERROR ) + return CS_ERROR; + + /* If the configuration flags were not specified */ + if ( sc->configFlags == 0 ) + { + /* Copy the memory mode flag */ + if ( isaConfig & ISA_CFG_MEM_MODE ) + sc->configFlags |= CFGFLG_MEM_MODE; + + /* Copy the USE_SA flag */ + if ( isaConfig & ISA_CFG_USE_SA ) + sc->configFlags |= CFGFLG_USE_SA; + + /* Copy the IO Channel Ready flag */ + if ( isaConfig & ISA_CFG_IOCHRDY ) + sc->configFlags |= CFGFLG_IOCHRDY; + + /* Copy the DC/DC Polarity flag */ + if ( adapterConfig & ADPTR_CFG_DCDC_POL ) + sc->configFlags |= CFGFLG_DCDC_POL; + + /* Copy the Full Duplex flag */ + if ( xmitCtl & XMIT_CTL_FDX ) + sc->configFlags |= CFGFLG_FDX; + } + + /* If the PacketPage pointer was not specified */ + if ( sc->pPacketPagePhys == MADDRUNK ) + { + /* If memory mode is enabled */ + if ( sc->configFlags & CFGFLG_MEM_MODE ) + { + /* Get the memory base address from EEPROM */ + if ( csReadEEPROM(sc,EEPROM_MEM_BASE,&memBase) == CS_ERROR ) + return CS_ERROR; + + memBase &= MEM_BASE_MASK; /* Clear unused bits */ + + /* Setup the PacketPage pointer */ + sc->pPacketPagePhys = ( ( (ulong) memBase) << 8 ); + } + } + + /* If the interrupt level was not specified */ + if ( sc->sc_int == 0 ) + { + /* Get the interrupt level from the ISA config */ + sc->sc_int = isaConfig & ISA_CFG_IRQ_MASK; + if ( sc->sc_int == 3 ) + sc->sc_int = 5; + else + sc->sc_int += 10; + } + + /* If the media type was not specified */ + if ( sc->mediaType == 0 ) + { + switch( adapterConfig & ADPTR_CFG_MEDIA ) + { + case ADPTR_CFG_AUI: + sc->mediaType = MEDIA_AUI; + break; + case ADPTR_CFG_10BASE2: + sc->mediaType = MEDIA_10BASE2; + break; + case ADPTR_CFG_10BASET: + default: + sc->mediaType = MEDIA_10BASET; + break; + } + } + + return CS_OK; +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csValidateParms +** +** Description : This routine verifies that the memory address, +** interrupt level and media type are valid. If any +** of these parameters are invalid, then an error +** message is printed and CS_ERROR is returned. +** +** Parameters : struct cs_softc *sc +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : returns CS_ERROR if any of the parameters are invalid +** +** Side Effects : None. +** +*/ + +int csValidateParms( struct cs_softc *sc ) +{ + int memAddr; /* this is an int so that we can + * do integer manipulation, not + * allowed on pointers. + */ + + memAddr = sc->pPacketPagePhys; + + if ( (memAddr & 0x000FFF) != 0 ) + { + printf("\n%s - Memory address (0x%X) must start on a 4K boundary\n", + (sc->sc_dev).dv_xname, memAddr ); + return CS_ERROR; + } + + /* check if beyond 16M ISA space */ + if ( memAddr > 0xFFF000 ) + { + printf("\n%s - Memory address (0x%X) is too large\n", + (sc->sc_dev).dv_xname, memAddr ); + return CS_ERROR; + } + + if ( !(sc->sc_int==5 || sc->sc_int==10 || sc->sc_int==11 || + sc->sc_int==12) ) + { + printf("\n%s - Interrupt level (%d) is invalid\n", + (sc->sc_dev).dv_xname, sc->sc_int ); + return CS_ERROR; + } + + if ( !(sc->mediaType==MEDIA_AUI || sc->mediaType==MEDIA_10BASE2 || + sc->mediaType==MEDIA_10BASET) ) + { + printf("\n%s - Media type (%d) is invalid\n", + (sc->sc_dev).dv_xname, sc->mediaType ); + return CS_ERROR; + } + + return CS_OK; +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csGetEthernetAddr +** +** Description : Read the Ethernet address from the EEPROM +** and save it in the arpcom structure. +** +** Parameters : struct cs_softc *sc +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : returns CS_ERROR if EEPROM can't be read. +** +** Side Effects : None. +** +*/ +int csGetEthernetAddr( struct cs_softc *sc) +{ + ushort selfStatus; + pia pIA; + + /* Setup pointer of where to store the Ethernet address */ + pIA = (pia) sc->sc_enaddr; + + /* Verify that the EEPROM is present and OK */ + selfStatus = csReadPacketPage( sc, PKTPG_SELF_ST ); + if ( !((selfStatus & SELF_ST_EEP_PRES) && (selfStatus & SELF_ST_EEP_OK))) + { + printf("\n%s - EEPROM is missing or bad\n", (sc->sc_dev).dv_xname); + return CS_ERROR; + } + + /* Get Ethernet address from the EEPROM */ + /* XXX this will likely lose on a big-endian machine. -- cgd */ + if ( csReadEEPROM(sc,EEPROM_IND_ADDR_H,&pIA->word[0]) == CS_ERROR ) + return CS_ERROR; + if ( csReadEEPROM(sc,EEPROM_IND_ADDR_M,&pIA->word[1]) == CS_ERROR ) + return CS_ERROR; + if ( csReadEEPROM(sc,EEPROM_IND_ADDR_L,&pIA->word[2]) == CS_ERROR ) + return CS_ERROR; + + return CS_OK; +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csResetChip +** +** Description : This routine resets the CS8900 chip. It +** will wait until the chip is ready for use again. +** +** Parameters : struct cs_softc sc +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : Returns error if can't read from the chip. +** +** Side Effects : Memory mode will always be programmed IO after +** a reset. +** +*/ +int csResetChip( struct cs_softc *sc ) +{ + int intState; + int x; + + /* Disable interrupts at the CPU so reset command is atomic */ + intState = splimp(); + + /* We are now resetting the chip + * + * A spurious interrupt is generated by the + * chip when it is reset. This variable + * informs the interrupt handler to ignore + * this interrupt. + */ + sc->resetting = TRUE; + + /* Issue a reset command to the chip */ + csWritePacketPage( sc, PKTPG_SELF_CTL, SELF_CTL_RESET ); + + /* Re-enable interrupts at the CPU */ + splx( intState ); + + /* The chip is always in IO mode after a reset */ + sc->inMemoryMode = FALSE; + + /* If transmission was in progress, it is not now */ + sc->txInProgress = FALSE; + + /* + * there was a delay(125); here, but it seems uneccesary + * 125 usec is 1/8000 of a second, not 1/8 of a second. + * the data sheet advises 1/10 of a second here, but the + * SI_BUSY and INIT_DONE loops below should be sufficient. + */ + + /* Transition SBHE to switch chip from 8-bit to 16-bit */ + bus_space_read_1( sc->sc_iot, sc->sc_iobase, PORT_PKTPG_PTR ); + bus_space_read_1( sc->sc_iot, sc->sc_iobase, PORT_PKTPG_PTR + 1 ); + bus_space_read_1( sc->sc_iot, sc->sc_iobase, PORT_PKTPG_PTR ); + bus_space_read_1( sc->sc_iot, sc->sc_iobase, PORT_PKTPG_PTR + 1 ); + + /* Wait until the EEPROM is not busy */ + for ( x = 0; x < MAXLOOP; x++ ) + { + if ( !(csReadPacketPage(sc, PKTPG_SELF_ST ) & SELF_ST_SI_BUSY) ) + break; + } + + if ( x == MAXLOOP ) + return CS_ERROR; + + /* Wait until initialization is done */ + for ( x = 0; x < MAXLOOP; x++ ) + { + if ( csReadPacketPage(sc, PKTPG_SELF_ST ) & SELF_ST_INIT_DONE ) + break; + } + + if ( x == MAXLOOP ) + return CS_ERROR; + + /* Reset is no longer in progress */ + sc->resetting = FALSE; + + return CS_OK; +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csReadEEPROM +** +** Description : This routine reads a word from the EEPROM at the +** specified offset. +** +** Parameters : struct cs_softc *sc, +** ushort offset, into the EEPROM +** ushort *pValue, EEPROM data is placed here +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : CS_OK, else CS_ERROR if can't read from the EEPROM. +** +** Side Effects : None. +** +*/ +int csReadEEPROM( struct cs_softc *sc, ushort offset, ushort *pValue ) +{ + int x; + + /* Ensure that the EEPROM is not busy */ + for ( x=0; x < MAXLOOP; x++ ) + { + if ( !(csReadPacketPage(sc, PKTPG_SELF_ST) & SELF_ST_SI_BUSY) ) + break; + } + + if ( x == MAXLOOP ) + { + printf("\n%s - Can not read from EEPROM\n", (sc->sc_dev).dv_xname); + return CS_ERROR; + } + + /* Issue the command to read the offset within the EEPROM */ + csWritePacketPage( sc, PKTPG_EEPROM_CMD, offset | EEPROM_CMD_READ ); + + /* Wait until the command is completed */ + for ( x=0; x < MAXLOOP; x++ ) + { + if ( !(csReadPacketPage(sc, PKTPG_SELF_ST) & SELF_ST_SI_BUSY) ) + break; + } + + if ( x == MAXLOOP ) + { + printf("\n%s - Can not read from EEPROM\n", (sc->sc_dev).dv_xname); + return CS_ERROR; + } + + /* Get the EEPROM data from the EEPROM Data register */ + *pValue = csReadPacketPage( sc, PKTPG_EEPROM_DATA ); + + return CS_OK; +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csInitChip +** +** Description : This routine uses the instance global variables +** in the cs_softc structure to initialize the CS8900 +** chip. +** +** Parameters : struct cs_softc *sc +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : None. +** +** Side Effects : None. +** +*/ +void csInitChip( struct cs_softc *sc ) +{ + ushort busCtl; + ushort selfCtl; + pia pIA; + ushort isaId; + + /* Disable reception and transmission of frames */ + csWritePacketPage( sc, PKTPG_LINE_CTL, + csReadPacketPage(sc,PKTPG_LINE_CTL) & + ~LINE_CTL_RX_ON & ~LINE_CTL_TX_ON ); + + /* Disable interrupt at the chip */ + csWritePacketPage( sc, PKTPG_BUS_CTL, + csReadPacketPage(sc,PKTPG_BUS_CTL) & ~BUS_CTL_INT_ENBL ); + + /* If IOCHRDY is enabled then clear the bit in the busCtl register */ + busCtl = csReadPacketPage( sc, PKTPG_BUS_CTL ); + if ( sc->configFlags & CFGFLG_IOCHRDY ) + { + csWritePacketPage( sc, PKTPG_BUS_CTL, busCtl & ~BUS_CTL_IOCHRDY ); + } + else + { + csWritePacketPage( sc, PKTPG_BUS_CTL, busCtl | BUS_CTL_IOCHRDY ); + } + + /* Set the Line Control register to match the media type */ + if ( sc->mediaType == MEDIA_10BASET ) + { + csWritePacketPage( sc, PKTPG_LINE_CTL, LINE_CTL_10BASET ); + } + else + { + csWritePacketPage( sc, PKTPG_LINE_CTL, LINE_CTL_AUI_ONLY ); + } + + /* Set the BSTATUS/HC1 pin to be used as HC1 */ + /* HC1 is used to enable the DC/DC converter */ + selfCtl = SELF_CTL_HC1E; + + /* If the media type is 10Base2 */ + if ( sc->mediaType == MEDIA_10BASE2 ) + { + /* Enable the DC/DC converter */ + /* If the DC/DC converter has a low enable */ + if ( (sc->configFlags & CFGFLG_DCDC_POL) == 0 ) + /* Set the HCB1 bit, which causes the HC1 pin to go low */ + selfCtl |= SELF_CTL_HCB1; + } + else /* Media type is 10BaseT or AUI */ + { + /* Disable the DC/DC converter */ + /* If the DC/DC converter has a high enable */ + if ( (sc->configFlags & CFGFLG_DCDC_POL) != 0 ) + { + /* Set the HCB1 bit, which causes the HC1 pin to go low */ + selfCtl |= SELF_CTL_HCB1; + } + } + csWritePacketPage( sc, PKTPG_SELF_CTL, selfCtl ); + + /* If media type is 10BaseT */ + if ( sc->mediaType == MEDIA_10BASET ) + { + /* If full duplex mode then set the FDX bit in TestCtl register */ + if ( sc->configFlags & CFGFLG_FDX ) + { + KERN_DEBUG(csdebug, CSINIT_DBG_INFO, + ("csInitChip : in full duplex mode\n")); + csWritePacketPage( sc, PKTPG_TEST_CTL, TEST_CTL_FDX ); + } + } + + /* RX_CTL set in csSetLadrFilt(), below */ + + /* enable all transmission interrupts */ + csWritePacketPage( sc, PKTPG_TX_CFG, TX_CFG_ALL_IE ); + + /* Accept all receive interrupts */ + csWritePacketPage( sc, PKTPG_RX_CFG, RX_CFG_ALL_IE ); + + /* + * Configure Operational Modes + * + * I have turned off the BUF_CFG_RX_MISS_IE, to speed things up, this is + * a better way to do it because the card has a counter which can be + * read to update the RX_MISS counter. This saves many interupts. + * + * I have turned on the tx and rx overflow interupts to counter using + * the receive miss interrupt. This is a better estimate of errors + * and requires lower system overhead. + */ + csWritePacketPage( sc, PKTPG_BUF_CFG, BUF_CFG_TX_UNDR_IE | + BUF_CFG_RX_DMA_IE ); + + if (sc->configFlags & CFGFLG_DMA_MODE) + { + /* + * First we program the DMA controller + * and ensure the memory buffer is valid. + * If it isn't then we just go on without DMA + */ + isa_dmastart(DMAMODE_READ | DMAMODE_LOOP, sc->dmaBase, sc->dmaMemSize, + sc->sc_drq ); + + sc->dma_offset = sc->dmaBase; + /* interrupt when a DMA'd frame is received */ + csWritePacketPage( sc, PKTPG_RX_CFG, RX_CFG_ALL_IE + | RX_CFG_RX_DMA_ONLY ); + + /* set the DMA burst bit so we don't tie up the bus + * for too long. + */ + if ( sc->dmaMemSize == 16384 ) + { + KERN_DEBUG(csdebug, CSINIT_DBG_INFO, + ("csInitChip : using 16K buffer\n")); + csWritePacketPage( sc, PKTPG_BUS_CTL, + ( (csReadPacketPage(sc, PKTPG_BUS_CTL) \ + & ~BUS_CTL_DMA_SIZE) | BUS_CTL_DMA_BURST)); + } + else /* use 64K */ + { + csWritePacketPage( sc, PKTPG_BUS_CTL, + csReadPacketPage(sc, PKTPG_BUS_CTL) \ + | BUS_CTL_DMA_SIZE | BUS_CTL_DMA_BURST); + } + + csWritePacketPage( sc, PKTPG_DMA_CHANNEL, sc->sc_drq - 5); + + } + + /* If memory mode is enabled */ + if ( sc->configFlags & CFGFLG_MEM_MODE ) + { + KERN_DEBUG(csdebug , CSINIT_DBG_INFO, + ("csInitChip : Using Memory Mode\n")); + + /* If external logic is present for address decoding */ + if ( csReadPacketPage(sc,PKTPG_SELF_ST) & SELF_ST_EL_PRES ) + { + /* Program the external logic to decode address bits SA20-SA23 */ + csWritePacketPage( sc, PKTPG_EEPROM_CMD, + ( (sc->pPacketPagePhys & 0xffffff) >> 20) | + EEPROM_CMD_ELSEL ); + } + + /* Setup chip for memory mode */ + /* Write the packet page base physical address to the memory + * base register. + */ + csWritePacketPage( sc, PKTPG_MEM_BASE, + sc->pPacketPagePhys & 0xFFFF ); + csWritePacketPage( sc, PKTPG_MEM_BASE + 2, + sc->pPacketPagePhys >> 16 ); + busCtl = BUS_CTL_MEM_MODE; + + /* tell the chip to read the addresses off the SA pins */ + if ( sc->configFlags & CFGFLG_USE_SA ) + { + busCtl |= BUS_CTL_USE_SA; + } + csWritePacketPage( sc, PKTPG_BUS_CTL, + csReadPacketPage(sc, PKTPG_BUS_CTL) | busCtl ); + + /* We are in memory mode now! */ + sc->inMemoryMode = TRUE; + + /* wait here (10ms) for the chip to swap over. + * this is the maximum time that this could take. + */ + delay(10000); + + /* Verify that we can read from the chip */ + isaId = csReadPacketPage( sc, PKTPG_EISA_NUM ); + + /* As a last minute sanity check before actually using mapped memory + * we verify that we can read the isa number from the chip + * in memory mode. + */ + if ( isaId != EISA_NUM_CRYSTAL ) + { + panic("\n%s - Mapped memory mode failed.\n", + (sc->sc_dev).dv_xname); + /* nope didn't work abandon the idea and use pIO */ + sc->inMemoryMode = FALSE; + } + else + { + /* we are in memory mode so if we aren't using DMA, then + * program the chip to interrupt early. + */ + if ( (sc->configFlags & CFGFLG_DMA_MODE) == 0 ) + { + KERN_DEBUG(csdebug, CSINIT_DBG_INFO, + ("csInitChip : about to turn on early interrupts\n")); + csWritePacketPage(sc, PKTPG_BUF_CFG, BUF_CFG_RX_DEST_IE | + BUF_CFG_RX_MISS_OVER_IE | + BUF_CFG_TX_COL_OVER_IE ); + } + } + + } /* if configFlags=memory mapped */ + + /* Put Ethernet address into the Individual Address register */ + pIA = (pia)sc->sc_enaddr; + csWritePacketPage( sc, PKTPG_IND_ADDR, pIA->word[0] ); + csWritePacketPage( sc, PKTPG_IND_ADDR+2, pIA->word[1] ); + csWritePacketPage( sc, PKTPG_IND_ADDR+4, pIA->word[2] ); + + /* Set the interrupt level in the chip */ + if ( sc->sc_int == 5 ) + { + csWritePacketPage( sc, PKTPG_INT_NUM, 3 ); + } + else + { + csWritePacketPage( sc, PKTPG_INT_NUM, (sc->sc_int)-10 ); + } + + /* write the multicast mask to the address filter register */ + csSetLadrFilt(sc, &sc->sc_ethercom); + + /* Enable reception and transmission of frames */ + csWritePacketPage( sc, PKTPG_LINE_CTL, + csReadPacketPage(sc,PKTPG_LINE_CTL) | + LINE_CTL_RX_ON | LINE_CTL_TX_ON ); + + /* Enable interrupt at the chip */ + csWritePacketPage( sc, PKTPG_BUS_CTL, + csReadPacketPage(sc,PKTPG_BUS_CTL) | BUS_CTL_INT_ENBL ); + +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csInit +** +** Description : This routine is called +** to initialize this network interface driver. This +** routine may be called several times for each +** operating system reboot to dynamically bring the +** network interface driver to an up and running state. +** This routine is called by the csIoctl() routine. +** +** This routine resets and then initializes the +** CS8900 chip. +** +** Parameters : struct cs_softc *sc +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : CS_ERROR if can't read from the chip. +** +** Side Effects : None. +** +*/ +int csInit( struct cs_softc *sc ) +{ + int intState; + int error = CS_OK; + + intState = splimp(); + + /* Mark the interface as down */ + sc->sc_ethercom.ec_if.if_flags &= ~(IFF_UP | IFF_RUNNING); + + /* Enable debugging */ +#ifdef CS_DEBUG + sc->sc_ethercom.ec_if.if_flags |= IFF_DEBUG; +#endif + + /* Reset the chip */ + if ( (error = csResetChip(sc)) == CS_OK ) + { + /* Initialize the chip */ + csInitChip( sc ); + + /* Mark the interface as up and running */ + sc->sc_ethercom.ec_if.if_flags |= (IFF_UP | IFF_RUNNING); + sc->sc_ethercom.ec_if.if_flags &= ~IFF_OACTIVE; + sc->sc_ethercom.ec_if.if_timer = 0; + } + else + { + printf("csInit : couldn't reset the chip!\n"); + } + + splx(intState); + return error; +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csSetLadrFilt +** +** Description : Reads the multicast addresses from the sc_enaddr +** structure and programs the cs8900's logical +** address filter to accept the frames. +** +** This function should only be called when the chip is +** being reset, it will overwrite whatever is in the +** logical address filter register. +** +** Parameters : struct ethercom +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : +** +** Side Effects : The logical address filter register on the +** cs8900 is reprogrammed, any addresses which +** were set will be cleared unless they are still +** in the multicast list. +** +*/ +void csSetLadrFilt( struct cs_softc *sc, struct ethercom *ec ) +{ + struct ifnet *ifp = &(ec->ec_if); + struct ether_multi *enm; + struct ether_multistep step; + ushort af[4]; + ushort port, mask, index; + + /* + * Set up multicast address filter by passing all multicast addresses + * through a crc generator, and then using the high order 6 bits as an + * index into the 64 bit logical address filter. The high order bit + * selects the word, while the rest of the bits select the bit within + * the word. + * + */ + if (ifp->if_flags & IFF_PROMISC) + { + /* accept all valid frames. */ + csWritePacketPage( sc, PKTPG_RX_CTL, + RX_CTL_PROMISC_A | RX_CTL_RX_OK_A | + RX_CTL_IND_A | RX_CTL_BCAST_A | RX_CTL_MCAST_A ); + ifp->if_flags |= IFF_ALLMULTI; + return; + } + + /* accept frames if a. crc valid, b. individual address match + * c. broadcast address,and d. multicast addresses matched in the + * hash filter + */ + csWritePacketPage( sc, PKTPG_RX_CTL, + RX_CTL_RX_OK_A | RX_CTL_IND_A | RX_CTL_BCAST_A | + RX_CTL_MCAST_A ); + + + /* start off with all multicast flag clear, set it if we need to later, + * otherwise we will leave it. + */ + ifp->if_flags &= ~IFF_ALLMULTI; + + af[0] = af[1] = af[2] = af[3] = 0x0000; + ETHER_FIRST_MULTI(step, ec, enm); + /* + * Loop through all the multicast addresses unless we get a range + * of addresses, in which case we will just accept all packets. + * Justification for this is given in the next comment. + */ + while ( (enm != NULL) && ( (ifp->if_flags | IFF_ALLMULTI) == 0) ) + { + if (bcmp(enm->enm_addrlo, enm->enm_addrhi, sizeof enm->enm_addrlo)) + { + /* + * We must listen to a range of multicast addresses. + * For now, just accept all multicasts, rather than + * trying to set only those filter bits needed to match + * the range. (At this time, the only use of address + * ranges is for IP multicast routing, for which the + * range is big enough to require all bits set.) + */ + ifp->if_flags |= IFF_ALLMULTI; + af[0] = af[1] = af[2] = af[3] = 0xffff; + } + else + { + /* + * we have got an individual address so just set that + * bit. + */ + index = csHashIndex(enm->enm_addrlo); + + /* Set the bit the Logical address filter. */ + port = (ushort) ( 2 * ((index / 16)) ); + mask = (ushort) ( 1 << (15 - (index % 16)) ); + af[port] |= mask; + + ETHER_NEXT_MULTI(step, enm); + } + } + /* now program the chip with the addresses */ + csWritePacketPage(sc, PKTPG_LOG_ADDR + 2, af[0]); + csWritePacketPage(sc, PKTPG_LOG_ADDR + 2, af[1]); + csWritePacketPage(sc, PKTPG_LOG_ADDR + 2, af[2]); + csWritePacketPage(sc, PKTPG_LOG_ADDR + 2, af[3]); + return; +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csHashIndex. +** +** Description : This function calculates Hash index for address. +** This code is chip specific, I pulled it from the +** SCO Unix driver I think. I could not find this +** algorithm anywhere in the manuals. +** +** Parameters : char *addr Pointer to address. +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : The corresponding bit number in the logical +** address filter corresponding to addr. +** +** Side Effects : None. +*/ +ushort csHashIndex(char *addr) +{ + uint POLY = 0x04c11db6; + uint crc_value = 0xffffffff; + ushort hash_code = 0; + int i; + uint current_bit; + char current_byte = *addr; + uint cur_crc_high; + + for ( i = 0; i < 6; i++) + { + current_byte = *addr; + addr++; + + for(current_bit = 8; current_bit; current_bit--) + { + cur_crc_high = crc_value >> 31; + crc_value <<= 1; + if(cur_crc_high ^ (current_byte & 0x01)) + { + crc_value ^= POLY; + crc_value |= 0x00000001; + } + current_byte >>= 1; + } + } + + /* + ** The hash code is the 6 least significant bits of the CRC + ** in the reverse order: CRC[0] = hash[5],CRC[1] = hash[4],etc. + */ + for(i=0; i < 6; i++) + { + hash_code = (ushort)( (hash_code << 1) | + (ushort)((crc_value >> i) & 0x00000001)); + } + + return hash_code; +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csReset +** +** Description : This routine is a major entry point and is called +** by the protocol stack to shut down this network +** interface driver. This routine may be called +** several times for each operating system reboot +** to dynamically bring the network interface driver +** to a non-running state. +** +** This routine resets the CS8900 chip. +** +** Parameters : struct cs_softc *sc +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : void +** +** Side Effects : None. +** +*/ +void csReset( void * arg) +{ + struct cs_softc *sc = arg; + /* Mark the interface as down */ + sc->sc_ethercom.ec_if.if_flags &= ~IFF_RUNNING; + + /* Reset the chip */ + csResetChip( sc ); +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csIoctl +** +** Description : This routine is a major entry point and is called +** by the protocol stack to modify characteristics +** of this network interface driver. There are many +** network interface ioctl commands, this +** driver only supports: +** Set Interface Address +** Set Interface Flags. +** Add/Delete Multicast addresses +** +** Parameters : struct ifnet +** command +** pData +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : 0, if successful, else a standard error code +** is returned. +** +** Side Effects : None. +** +*/ +int csIoctl( struct ifnet *pIf, ulong com, caddr_t pData ) +{ + int state; + int result; + struct cs_softc *sc = pIf->if_softc; + struct ifaddr *ifa = (struct ifaddr *) pData; + struct ifreq *ifr = (struct ifreq *)pData; + + state = splimp(); + + result = 0; /* only set if something goes wrong */ + + switch( com ) + { + case SIOCSIFADDR: + /* only support INET at this stage */ + if ( ifa->ifa_addr->sa_family == AF_INET ) + { + csInit(sc); + /* Set interface address (IP address) */ + arp_ifinit(&sc->sc_ethercom.ec_if, ifa); + } + else + { + result = EPERM; + } + break; + case SIOCSIFFLAGS: + /* Set interface flags */ + KERN_DEBUG(csdebug, CSIOCTL_DBG_INFO, + ("csIoctl: Set interface flags\n")); + if ( pIf->if_flags & IFF_UP ) + { + /* Mark the interface as up and running */ + sc->sc_ethercom.ec_if.if_flags |= (IFF_UP | IFF_RUNNING); + + /* Initialize the interface */ + csInit( pIf->if_softc ); + } + else /* The interface is down */ + { + /* Mark the interface as down */ + sc->sc_ethercom.ec_if.if_flags &= ~(IFF_UP | IFF_RUNNING); + + /* Reset the chip */ + csResetChip( sc ); + } + break; + case SIOCADDMULTI: + result = ether_addmulti(ifr, &sc->sc_ethercom); + + if (result == ENETRESET) + { + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + csInit(sc); + result = 0; + } + break; + case SIOCDELMULTI: + result = ether_delmulti(ifr, &sc->sc_ethercom); + + if (result == ENETRESET) + { + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + csInit(sc); + result = 0; + } + break; + default: + result = EINVAL; /* Invalid argument */ + break; + } + + splx( state ); + + return result; +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csIntr +** +** Description : This routine is the interrupt service routine. +** This routine is called by assembly language wrapper +** code whenever the CS8900 chip generates an +** interrupt. The wrapper code issues an EOI command +** to the 8259 PIC. +** +** This routine processes the events on the Interrupt +** Status Queue. The events are read one at a time +** from the ISQ and the appropriate event handlers are +** called. The ISQ is read until it is empty. If +** the chip's interrupt request line is active, then +** reading a zero from the ISQ will deactivate the +** interrupt request line. +** +** Parameters : arg is a pointer to the softc struct +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : 0 if the interupt was not handled (chaining) +** 1 if the interupt was handled. +** -1 is also valid, but we don't return it, +** because I don't know what it means. +** +** Side Effects : None. +** +*/ +int csIntr( void *arg ) +{ + struct cs_softc *sc = arg; + ushort Event; + + /* Ignore any interrupts that happen while the chip is being reset */ + if ( sc->resetting ) + { + printf("%s : interupt handler: already resetting\n", + (sc->sc_dev).dv_xname); + /* this was probably still caused by the chip but don't worry */ + return 1; + } + + /* Read an event from the Interrupt Status Queue */ + Event = bus_space_read_2( sc->sc_memt, sc->sc_memh, PKTPG_ISQ ); + + /* Process all the events in the Interrupt Status Queue */ + while ( Event != 0 ) + { + /* Dispatch to an event handler based on the register number */ + switch ( Event & REG_NUM_MASK ) + { + case REG_NUM_RX_EVENT: + csReceiveEvent( sc, Event ); + break; + case REG_NUM_TX_EVENT: + csTransmitEvent(sc, Event); + break; + case REG_NUM_BUF_EVENT: + csBufferEvent( sc, Event ); + break; + case REG_NUM_TX_COL: + case REG_NUM_RX_MISS: + csCounterEvent( sc, Event ); + break; + default: + printf("%s : Unknown interrupt event 0x%x\n", + (sc->sc_dev).dv_xname, Event ); + break; + } + + /* Read another event from the Interrupt Status Queue */ + Event = bus_space_read_2( sc->sc_memt, sc->sc_memh, PKTPG_ISQ ); + } + /* have handled the interupt */ + return 1; +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csCounterEvent +** +** Description : Handles the transmit collision and receive miss +** counter overflow events. These event occur +** when the counters reach 0x200. This should +** leave ample time to read the counter before +** overflow of the 11 bit counter. +** +** Parameters : struct cs_softc *sc +** cntEvent : the interrupt status queue register. +** +** Implicit Inputs : none. +** +** Implicit Outputs : none. +** +** Return Value : void. +** +** Side Effects : the ifnet's counters have been updated. +** +*/ +void csCounterEvent( struct cs_softc *sc, ushort cntEvent ) +{ + struct ifnet *pIf; + ushort errorCount; + + pIf = &sc->sc_ethercom.ec_if; + + switch ( cntEvent & REG_NUM_MASK ) + { + case REG_NUM_TX_COL : + /* the count should be read before + * an overflow occurs. + */ + errorCount = csReadPacketPage(sc, PKTPG_TX_COL); + /* the tramsit event routine always + * checks the number of collisions for any packet + * so we don't increment any counters here, as they + * should already have been considered. + */ + break; + case REG_NUM_RX_MISS : + /* the count should be read before + * an overflow occurs. + */ + errorCount = csReadPacketPage(sc, PKTPG_RX_MISS); + /* Increment the input error count, + * the first 6bits are the register id. + */ + pIf->if_ierrors += ( (errorCount & 0xffC0) >> 6 ); + break; + default : + /* do nothing */ + break; + } + return ; +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csBufferEvent +** +** Description : The routine is called whenever an event occurs +** regarding the transmit and receive buffers within +** the CS8900 chip. This should be called when +** early detection of received packets is enabled. +** +** Software generated interrupts are checked but +** should never be generated. +** +** Parameters : struct cs_softc *sc +** bufEvent +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : void. +** +** Side Effects : None. +** +*/ +void csBufferEvent( struct cs_softc *sc, ushort bufEvent ) +{ + struct ifnet *pIf; + + pIf = &sc->sc_ethercom.ec_if; + + KERN_DEBUG(csdebug, CSBUFF_DBG_INFO, + ("csBufferEvent : event = 0x%x\n",bufEvent)); + + /* multiple events can be in the buffer event register at + * one time so a standard switch statement will not suffice, here + * every event must be checked. + */ + + /* if 128 bits have been rxed by the time we get here, the + * dest event will be cleared and 128 event will be set. + */ + if ( (bufEvent & (BUF_EVENT_RX_DEST | BUF_EVENT_RX_128)) != 0 ) + { + csProcessRxEarly(sc); + } + + if ( bufEvent & BUF_EVENT_RX_DMA ) + { + /* process the receive data */ + csProcessRxDMA(sc); + } + + if (bufEvent & BUF_EVENT_TX_UNDR) { +#if 0 /* this is an expected condition (occasionally) */ + printf("%s: transmit underrun (%d -> %d)\n", sc->sc_dev.dv_xname, + sc->sc_xe_ent, cs_xmit_early_table[sc->sc_xe_ent].worse); +#endif + sc->sc_xe_ent = cs_xmit_early_table[sc->sc_xe_ent].worse; + sc->sc_xe_togo = cs_xmit_early_table[sc->sc_xe_ent].better_count; + /* had an underrun, transmit is finished */ + sc->txInProgress = FALSE; + } + + if ( bufEvent & BUF_EVENT_SW_INT ) + { + printf( "%s : Software initiated interrupt\n", + (sc->sc_dev).dv_xname ); + } + +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csTransmitEvent +** +** Description : This routine is called whenever the transmission +** of a packet has completed successfully or +** unsuccessfully. If the transmission was not +** successful, then the output error count is +** incremented. If collisions occured while +** sending the packet, then the number of collisions +** is added to the collision counter. If there are +** more packets on the transmit queue, then the next +** packet is started by calling +** csStartOutput(). +** +** Parameters : struct cs_softc *sc +** txEvent +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : void. +** +** Side Effects : None. +** +*/ +void csTransmitEvent( struct cs_softc *sc, ushort txEvent ) +{ + struct ifnet *pIf = &sc->sc_ethercom.ec_if; + + KERN_DEBUG(csdebug, CSTXEVENT_DBG_INFO, + ("csTransmitEvent : event = 0x%x\n", txEvent)); + + /* If there were any errors transmitting this frame */ + if ( txEvent & (TX_EVENT_LOSS_CRS | TX_EVENT_SQE_ERR | TX_EVENT_OUT_WIN | + TX_EVENT_JABBER | TX_EVENT_16_COLL) ) + { + /* Increment the output error count */ + pIf->if_oerrors++; + + /* If debugging is enabled then log error messages */ + if ( pIf->if_flags & IFF_DEBUG ) + { + if ( txEvent & TX_EVENT_LOSS_CRS ) + { + printf( "%s : Loss of carrier\n", (sc->sc_dev).dv_xname ); + } + if ( txEvent & TX_EVENT_SQE_ERR ) + { + printf( "%s : SQE error\n", (sc->sc_dev).dv_xname ); + } + if ( txEvent & TX_EVENT_OUT_WIN ) + { + printf( "%s : Out-of-window collision\n", + (sc->sc_dev).dv_xname ); + } + if ( txEvent & TX_EVENT_JABBER ) + { + printf("%s : Jabber\n", (sc->sc_dev).dv_xname ); + } + if ( txEvent & TX_EVENT_16_COLL ) + { + printf( "%s : 16 collisions\n", (sc->sc_dev).dv_xname ); + } + } + } + else + { + ledNetActive(); + } + + /* Add the number of collisions for this frame */ + if ( txEvent & TX_EVENT_16_COLL ) + { + pIf->if_collisions += 16; + } + else + { + pIf->if_collisions += ((txEvent & TX_EVENT_COLL_MASK) >> 11); + } + + pIf->if_opackets++; + /* Transmission is no longer in progress */ + sc->txInProgress = FALSE; + + /* If there is more to transmit */ + if ( pIf->if_snd.ifq_head != NULL ) + { + /* Start the next transmission */ + csStartOutput(pIf); + } +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csReceiveEvent +** +** Description : This routine is called whenever a packet is +** received at the chip. If the packet is received +** with errors, then the input error count is +** incremented. If the packet is received OK, then +** the data is copied to an internal receive +** buffer and the csProcessReceive() routine is +** called to process the received packet. +** +** Parameters : struct cs_softc *sc +** rxEvent +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : void. +** +** Side Effects : None. +** +*/ +void csReceiveEvent( struct cs_softc *sc, ushort rxEvent ) +{ + struct ifnet *pIf = &sc->sc_ethercom.ec_if; + + KERN_DEBUG(csdebug, CSRXNORM_DBG_INFO, + ("csProcessReceive : packet received event = 0x%x\n",rxEvent)); + + /* If the frame was not received OK */ + if ( !(rxEvent & RX_EVENT_RX_OK) ) + { + /* Increment the input error count */ + pIf->if_ierrors++; + + /* If debugging is enabled then log error messages */ + if ( pIf->if_flags & IFF_DEBUG ) + { + /* If an error bit is set */ + if ( rxEvent != REG_NUM_RX_EVENT ) + { + if ( rxEvent & RX_EVENT_RUNT ) + { + printf( "%s : Runt\n", (sc->sc_dev).dv_xname ); + } + if ( rxEvent & RX_EVENT_X_DATA ) + { + printf( "%s : Extra data\n", (sc->sc_dev).dv_xname ); + } + if ( rxEvent & RX_EVENT_CRC_ERR ) + { + if ( rxEvent & RX_EVENT_DRIBBLE ) + { + printf( "%s : Alignment error\n", + (sc->sc_dev).dv_xname ); + } + else + { + printf( "%s : CRC Error\n", (sc->sc_dev).dv_xname ); + } + } + else + { + if ( rxEvent & RX_EVENT_DRIBBLE ) + { + printf( "%s : Dribble bits\n", (sc->sc_dev).dv_xname ); + } + } + + /* Must read the length of all received frames */ + csReadPacketPage( sc, PKTPG_RX_LENGTH ); + + /* Skip the received frame */ + csWritePacketPage( sc, PKTPG_RX_CFG, + csReadPacketPage(sc,PKTPG_RX_CFG) | + RX_CFG_SKIP ); + } + else + { + printf( "%s : Implied skip\n", (sc->sc_dev).dv_xname ); + } + } + } + else + { + /* process the received frame and pass it up to the upper layers. */ + csProcessReceive(sc); + } +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csEtherInput +** +** Description : This routine hands a received packet to the higher- +** level networking code, taking care to pass it +** to BPF as appropriate and to discard it when +** necessary. +** +** Parameters : struct cs_softc *sc network interface softc +** struct mbuf *m incoming packet +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : void. +** +** Side Effects : Mbuf chain passed to higher-level network code, +** or deallocated if it shouldn't be passed up. +** +*/ +void +csEtherInput(sc, m) + struct cs_softc *sc; + struct mbuf *m; +{ + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + struct ether_header *eh; + + ifp->if_ipackets++; + + /* the first thing in the mbuf is the ethernet header. + * we need to pass this header to the upper layers as a structure, + * so cast the start of the mbuf, and adjust the mbuf to point to + * the end of the ethernet header, ie the ethernet packet data. + */ + eh = mtod(m, struct ether_header *); + +#if NBPFILTER > 0 + /* + * Check if there's a BPF listener on this interface. + * If so, hand off the raw packet to BPF. + * + */ + if (ifp->if_bpf) { + bpf_mtap(ifp->if_bpf, m); + + /* + * Note that the interface cannot be in promiscuous mode if + * there are no BPF listeners. And if we are in promiscuous + * mode, we have to check if this packet is really ours. + */ + if ((ifp->if_flags & IFF_PROMISC) && + (eh->ether_dhost[0] & 1) == 0 && /* !mcast and !bcast */ + bcmp(eh->ether_dhost, sc->sc_enaddr, + sizeof(eh->ether_dhost)) != 0) { + m_freem(m); + return; + } + } +#endif + + /* Pass the packet up, with the ether header sort-of removed. + * ie. adjust the data pointer to point to the data. + */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csProcessReceive +** +** Description : This routine processes a received packet. The +** received packet is read from the chip directly +** into a clustered mbuf. These being 1 page +** on i386 netBSD, and thus guarenteed to fit +** an ethernet frame. +** +** Parameters : struct cs_softc *sc +** pRxBuf +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : void. +** +** Side Effects : None. +** +*/ +void csProcessReceive( struct cs_softc *sc ) +{ + struct ifnet *pIf; + struct mbuf *m; + int totlen; + int len; + volatile ushort *pBuff, *pBuffLimit; + int pad; + unsigned int frameOffset; + + ledNetActive(); + + pIf = &sc->sc_ethercom.ec_if; + + /* Initialize the frame offset*/ + frameOffset = PKTPG_RX_LENGTH; + + /* Get the length of the received frame */ + totlen = bus_space_read_2( sc->sc_memt, sc->sc_memh, + frameOffset ); + frameOffset += 2; + KERN_DEBUG(csdebug, CSRXNORM_DBG_INFO, + ("csProcessReceive : length = 0x%x\n",totlen)); + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + { + printf("%s : csProcessReceive : Couldn't allocate mbuf\n", + (sc->sc_dev).dv_xname); + pIf->if_ierrors++; + /* couldn't allocate an mbuf so things are not good, + * may as well drop the packet I think. + * + * have already read the length so we should be + * right to skip the packet. + */ + csWritePacketPage( sc, PKTPG_RX_CFG, + csReadPacketPage(sc,PKTPG_RX_CFG) | RX_CFG_SKIP ); + return ; + } + + m->m_pkthdr.rcvif = pIf; + m->m_pkthdr.len = totlen; + + /* save processing by always using a mbuf cluster, + * guarenteed to fit packet, on i386 netBSD anyway. + */ + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) + { + len = MCLBYTES; + } + else + { + /* couldn't allocate an mbuf cluster */ + printf("%s : csProcessReceive : couldn't allocate a cluster\n", + (sc->sc_dev).dv_xname); + m_freem(m); + /* skip the received frame */ + csWritePacketPage( sc, PKTPG_RX_CFG, + csReadPacketPage(sc,PKTPG_RX_CFG) | RX_CFG_SKIP ); + + return ; + } + + /* align ip header on word boundary for ipintr */ + pad = ALIGN(sizeof(struct ether_header)) + - sizeof(struct ether_header); + m->m_data += pad; + + m->m_len = len = min(totlen, len); + pBuff = mtod(m, ushort *); + pBuffLimit = pBuff + (len + 1 )/2; /* don't want to go over */ + /* now read the data from the chip */ + while ( pBuff < pBuffLimit ) + { + *pBuff++ = bus_space_read_2( sc->sc_memt, sc->sc_memh, + frameOffset ); + frameOffset += 2; + } + + csEtherInput(sc, m); +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csProcessRxDMA +** +** Description : This routine is called by the ISR to accept a +** frame from the adapter. (Called on a Rx ISQ +** event). It copies frames which have been +** DMA transfered to memory. +** +** Parameters : softc +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : None. +** +** Side Effects : +*/ +void csProcessRxDMA(struct cs_softc *sc) +{ + struct ifnet *pIf; + ushort num_dma_frames; + ushort pkt_length; + ushort status; + uint to_copy; + char *dma_mem_ptr; + struct mbuf *m; + u_char *pBuff; + int pad; + + /* initialise the pointers */ + pIf = &sc->sc_ethercom.ec_if; + + /* Read the number of frames DMAed. */ + num_dma_frames = csReadPacketPage( sc, PKTPG_DMA_FRAME_COUNT); + num_dma_frames &= (ushort) (0x0fff); + + /* Loop till number of DMA frames ready to read is zero. + * After reading the frame out of memory we must check + * if any have been received while we were processing + */ + while ( num_dma_frames != 0 ) + { + dma_mem_ptr = sc->dma_offset; + + /* process all of the dma frames in memory + * + * This loop relies on the dma_mem_ptr variable being set + * to the next frames start address. + */ + for(; num_dma_frames > 0; num_dma_frames--) + { + + /* Get the length and status of the packet. + * Only the status is guarenteed to be at + * dma_mem_ptr, ie need to check for wraparound + * before reading the length + */ + status = *((unsigned short *)dma_mem_ptr)++; + if (dma_mem_ptr > (sc->dmaBase + sc->dmaMemSize)) + { + dma_mem_ptr = sc->dmaBase; + } + pkt_length = *((unsigned short *)dma_mem_ptr)++; + + /* Do some sanity checks on the length and status. */ + if( (pkt_length > ETHER_MTU) || ((status & DMA_STATUS_BITS) != + DMA_STATUS_OK) ) + { + /* the SCO driver kills the adapter in this situation */ + /* should increment the error count + * and reset the dma operation. + */ + printf("%s : csProcessRxDMA : DMA buffer out of sync about to reset\n", + (sc->sc_dev).dv_xname); + KERN_DEBUG(csdebug, CSRXDMA_DBG_INFO, + ("csProcessRxDMA : error length = %d, \ + status = 0x%x\n", pkt_length, status)); + KERN_DEBUG(csdebug, CSRXDMA_DBG_INFO, + ("csProcessRxDMA : the offset is 0x%x\n", \ + (int) sc->dma_offset)); + pIf->if_ierrors++; + /* skip the rest of the DMA buffer */ + isa_dmadone(DMAMODE_READ | DMAMODE_LOOP, sc->dmaBase, sc->dmaMemSize, + sc->sc_drq); + /* now reset the chip and reinitialise */ + csInit(sc); + return ; + } + + /* Check the status of the received packet. */ + if ( status & RX_EVENT_RX_OK ) + { + /* get a new mbuf */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + { + printf("%s : csProcessRxDMA : Couldn't allocate mbuf\n",\ + (sc->sc_dev).dv_xname); + pIf->if_ierrors++; + /* couldn't allocate an mbuf so things are not good, + * may as well drop all the packets I think. + */ + csReadPacketPage( sc, PKTPG_DMA_FRAME_COUNT ); + /* now reset DMA operation */ + isa_dmadone(DMAMODE_READ | DMAMODE_LOOP, sc->dmaBase, + sc->dmaMemSize, sc->sc_drq); + /* now reset the chip and reinitialise */ + csInit(sc); + return ; + } + + + /* save processing by always using a mbuf cluster, + * guarenteed to fit packet + */ + MCLGET(m, M_DONTWAIT); + if ( (m->m_flags & M_EXT) == 0) + { + /* couldn't allocate an mbuf cluster */ + printf("%s : csProcessRxDMA : couldn't allocate \ + a cluster\n", (sc->sc_dev).dv_xname); + m_freem(m); + /* skip the frame */ + csReadPacketPage( sc, PKTPG_DMA_FRAME_COUNT ); + isa_dmadone(DMAMODE_READ | DMAMODE_LOOP, sc->dmaBase, + sc->dmaMemSize, sc->sc_drq); + /* now reset the chip and reinitialise */ + csInit(sc); + return ; + } + m->m_pkthdr.rcvif = pIf; + m->m_pkthdr.len = pkt_length; + m->m_len = pkt_length; + + /* align ip header on word boundary for ipintr */ + pad = ALIGN(sizeof(struct ether_header)) + - sizeof(struct ether_header); + m->m_data += pad; + + /* set up the buffer pointer to point + * to the data area + */ + pBuff = mtod(m, char *); + + /* + * Read the frame into free_pktbuf + * The buffer is circular buffer, either + * 16K or 64K in length. + * + * need to check where the end of the buffer + * is and go back to the start. + */ + if ( (dma_mem_ptr + pkt_length ) + < sc->dmaBase + sc->dmaMemSize) + { + /* No wrap around. Copy the frame header */ + bcopy(dma_mem_ptr, pBuff, pkt_length); + dma_mem_ptr += pkt_length; + } + else + { + KERN_DEBUG(csdebug, CSRXDMA_DBG_INFO, + ("csProcessRxDMA : wraparound\n")); + to_copy = (uint) ( (sc->dmaBase + sc->dmaMemSize) + - dma_mem_ptr); + + /* Copy the first half of the frame. */ + bcopy(dma_mem_ptr, pBuff, to_copy); + pBuff += to_copy; + + /* + * Rest of the frame is to be read from the + * first byte of the DMA memory. + */ + /* Get the number of bytes leftout in the frame. */ + to_copy = pkt_length - to_copy; + + dma_mem_ptr = sc->dmaBase; + + /* Copy rest of the frame. */ + bcopy(dma_mem_ptr, pBuff, to_copy); + dma_mem_ptr += to_copy; + } + + KERN_DEBUG(csdebug, CSRXDMA_DBG_INFO, + ("csProcessRxDMA : about to pass packet up protocol stack\n")); + KERN_DEBUG(csdebug, CSRXDMA_DBG_INFO, + ("csProcessRxDMA : start addr = 0x%x\n", + (unsigned int) mtod(m, char *)) ); + csEtherInput(sc, m); + } /* (status & RX_OK) */ + else + { + /* the frame was not received OK */ + /* Increment the input error count */ + pIf->if_ierrors++; + + /* If debugging is enabled then log error messages */ + if ( pIf->if_flags & IFF_DEBUG ) + { + /* If an error bit is set */ + if ( status != REG_NUM_RX_EVENT ) + { + if ( status & RX_EVENT_RUNT ) + { + printf( "%s : Runt\n", (sc->sc_dev).dv_xname ); + } + if ( status & RX_EVENT_X_DATA ) + { + printf( "%s : Extra data\n", + (sc->sc_dev).dv_xname ); + } + if ( status & RX_EVENT_CRC_ERR ) + { + if ( status & RX_EVENT_DRIBBLE ) + { + printf( "%s : Alignment error\n", \ + (sc->sc_dev).dv_xname); + } + else + { + printf("%s : CRC Error\n", \ + (sc->sc_dev).dv_xname ); + } + } + else + { + if ( status & RX_EVENT_DRIBBLE ) + { + printf("%s : Dribble bits\n", \ + (sc->sc_dev).dv_xname ); + } + } + } + } + } + /* now update the current frame pointer. + * the dma_mem_ptr should point to the next packet to + * be received, without the alignment considerations. + * + * The cs8900 pads all frames to start at the next 32bit + * aligned addres. hence we need to pad our offset pointer. + */ + dma_mem_ptr += 3; + dma_mem_ptr = (char *) ((long) dma_mem_ptr & 0xfffffffc); + if ( dma_mem_ptr < (sc->dmaBase + sc->dmaMemSize) ) + { + sc->dma_offset = dma_mem_ptr; + } + else + { + dma_mem_ptr = sc->dma_offset = sc->dmaBase; + } + } /* for all frames */ + /* Read the number of frames DMAed again. */ + num_dma_frames = csReadPacketPage( sc, PKTPG_DMA_FRAME_COUNT ); + num_dma_frames &= (ushort) (0x0fff); + } /* while there are frames left */ +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csProcessRxEarly +** +** Description : This routine is called after a buffer event +** RxDest or Rx128 has occurred. The frame is read +** from the chip as it arrives. The packet is then +** skipped, so that the next packet can be read. +** +** For a more detailed description of this operation +** refer to section "5.2.9 Receive Frame Byte +** Counter" in the chip spec. +** +** This code presently supports both mapped memory +** mode and programmed I/O mode. Programmed I/O is +** not as fast as mapped memory and the programmed +** I/O code slows the mapped memory implementation +** down. To improve performance remove the programmed +** I/O support. +** +** Parameters : struct cs_softc *sc +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : void. +** +** Side Effects : The frame is read from the chip and passed to the +** upper layer protocols if it is ok. +** +*/ +void csProcessRxEarly( struct cs_softc *sc ) +{ + struct ifnet *pIf; + struct mbuf *m; + ushort frameCount, oldFrameCount; + ushort rxEvent; + ushort *pBuff; + int pad; + unsigned int frameOffset; + + + pIf = &sc->sc_ethercom.ec_if; + + /* Initialize the frame offset */ + frameOffset = PKTPG_RX_FRAME; + frameCount = 0; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + { + printf("%s : csProcessRxEarly : Couldn't allocate mbuf\n", + (sc->sc_dev).dv_xname); + pIf->if_ierrors++; + /* couldn't allocate an mbuf so things are not good, may as well drop + * the packet I think. + * + * have already read the length so we should be + * right to skip the packet. + */ + csWritePacketPage( sc, PKTPG_RX_CFG, + csReadPacketPage(sc,PKTPG_RX_CFG) | RX_CFG_SKIP ); + return ; + } + + m->m_pkthdr.rcvif = pIf; + /* save processing by always using a mbuf cluster, + * guarenteed to fit packet + */ + MCLGET(m, M_DONTWAIT); + if ( (m->m_flags & M_EXT) == 0) + { + /* couldn't allocate an mbuf cluster */ + printf("%s : csProcessRxEarly : couldn't allocate a cluster\n", + (sc->sc_dev).dv_xname); + m_freem(m); + /* skip the frame */ + csWritePacketPage( sc, PKTPG_RX_CFG, + csReadPacketPage(sc,PKTPG_RX_CFG) | RX_CFG_SKIP ); + return ; + } + + /* align ip header on word boundary for ipintr */ + pad = ALIGN(sizeof(struct ether_header)) + - sizeof(struct ether_header); + m->m_data += pad; + + /* set up the buffer pointer to point to the data area */ + pBuff = mtod(m, ushort *); + + /* now read the frame byte counter until we have + * finished reading the frame + */ + oldFrameCount = 0; + frameCount = bus_space_read_2( sc->sc_memt, sc->sc_memh, + PKTPG_FRAME_BYTE_COUNT ); + while ( (frameCount != 0) && ( frameCount < MCLBYTES ) ) + { + for (; oldFrameCount < frameCount; oldFrameCount += 2) + { + *pBuff++ = bus_space_read_2( sc->sc_memt, sc->sc_memh, + frameOffset ); + + frameOffset += 2; + } + /* read the new count from the chip */ + frameCount = bus_space_read_2( sc->sc_memt, sc->sc_memh, + PKTPG_FRAME_BYTE_COUNT ); + } + + /* update the mbuf counts */ + m->m_len = oldFrameCount; + m->m_pkthdr.len = oldFrameCount; + + /* now check the Rx Event register */ + rxEvent = csReadPacketPage(sc,PKTPG_RX_EVENT); + + if ( (rxEvent & RX_EVENT_RX_OK ) != 0 ) + { + + /* do an implied skip, it seems to be more reliable + * than a forced skip. + */ + rxEvent = bus_space_read_2( sc->sc_memt, sc->sc_memh, + PKTPG_RX_STATUS ); + rxEvent = bus_space_read_2( sc->sc_memt, sc->sc_memh, + PKTPG_RX_LENGTH ); + + /* now read the RX_EVENT register to + * perform an implied skip. + */ + rxEvent = csReadPacketPage(sc,PKTPG_RX_EVENT); + + csEtherInput(sc, m); + } + else + { + m_freem(m); + pIf->if_ierrors++; + } +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csStartOutput +** +** Description : This routine is called to start the transmission +** of the packet at the head of the transmit queue. +** This routine is called by the ether_output() +** routine when a new packet is added to the transmit +** queue and this routine is called by +** csTransmitEvent() when a previous packet +** transmission is completed. +** +** This should always be called at splimp. +** +** Parameters : pIf : pointer to devices ifnet +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : void. +** +** Side Effects : None. +** +*/ +void csStartOutput( struct ifnet *pIf ) +{ + struct cs_softc *sc; + struct mbuf *pMbuf; + struct mbuf *pMbufChain; + struct ifqueue *pTxQueue; + ushort BusStatus; + ushort Length; + int txLoop = 0; + int dropout = 0; + + sc = pIf->if_softc; + pTxQueue = &sc->sc_ethercom.ec_if.if_snd; + + /* check that the interface is up and running */ + if ((pIf->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + { + return ; + } + + /* Don't interrupt a transmission in progress */ + if ( sc->txInProgress ) + { + return; + } + + /* this loop will only run through once if transmission is successful */ + /* While there are packets to transmit and a transmit is not in progress */ + while ( (pTxQueue->ifq_head != NULL) && !(sc->txInProgress) + && !(dropout) ) + { + IF_DEQUEUE( pTxQueue, pMbufChain ); + +#if NBPFILTER > 0 + /* + * If BPF is listening on this interface, let it see the packet + * before we commit it to the wire. + */ + if (pIf->if_bpf) + bpf_mtap(pIf->if_bpf, pMbufChain); +#endif + + /* Find the total length of the data to transmit */ + Length = 0; + for ( pMbuf=pMbufChain; pMbuf!=NULL; pMbuf=pMbuf->m_next ) + Length += pMbuf->m_len; + + do { + /* Request that the transmit be started after all data has + * been copied + * + * In IO mode must write to the IO port not the packet page + * address + * + * If this is changed to start transmission after a small + * amount of data has been copied you tend to get packet + * missed errors i think because the ISA bus is too slow. Or + * possibly the copy routine is not streamlined enough. + */ + csWritePacketPage( sc, PKTPG_TX_CMD, + cs_xmit_early_table[sc->sc_xe_ent].txcmd ); + csWritePacketPage( sc, PKTPG_TX_LENGTH, Length ); + + /* Adjust early-transmit machinery. + */ + if (--sc->sc_xe_togo == 0) { + sc->sc_xe_ent = cs_xmit_early_table[sc->sc_xe_ent].better; + sc->sc_xe_togo = + cs_xmit_early_table[sc->sc_xe_ent].better_count; + } + + /* Read the BusStatus register which indicates success of + * the request + */ + BusStatus = csReadPacketPage( sc, PKTPG_BUS_ST ); + + /* If there was an error in the transmit bid + * free the mbuf and go on. This is presuming that + * mbuf is corrupt. + */ + if ( BusStatus & BUS_ST_TX_BID_ERR ) + { + printf( "%s : Transmit bid error (too big)", + (sc->sc_dev).dv_xname ); + /* Discard the bad mbuf chain */ + m_freem( pMbufChain ); + sc->sc_ethercom.ec_if.if_oerrors++; + /* Loop up to transmit the next chain */ + txLoop = 0; + } + else + { + if ( BusStatus & BUS_ST_RDY4TXNOW ) + { + KERN_DEBUG(csdebug, CSSTARTTX_DBG_INFO, + ("csStartOutput : about to send frame\n")); + + /* The chip is ready for transmission now */ + /* Copy the frame to the chip to start + * transmission + */ + csCopyTxFrame( sc, pMbufChain ); + /* Free the mbuf chain */ + m_freem( pMbufChain ); + /* Transmission is now in progress */ + sc->txInProgress = TRUE; + txLoop = 0; + } + else + { + /* if we get here we want to try again with + * the same mbuf, until the chip lets us + * transmit. + */ + txLoop++; + if ( txLoop > CS_OUTPUT_LOOP_MAX ) + { + /* Free the mbuf chain */ + m_freem( pMbufChain ); + /* Transmission is not in progress */ + sc->txInProgress = FALSE; + /* Increment the output error count */ + pIf->if_oerrors++; + /* exit the routine and drop the packet. */ + txLoop = 0; + dropout = 1; + } + KERN_DEBUG(csdebug, CSSTARTTX_DBG_INFO, + ( "%s : Not ready for transmission now", + (sc->sc_dev).dv_xname )); + } + } + } while (txLoop); + } +} + + +/*-------------------------------------------------------------------------*/ +/* +** Function : csCopyTxFrame +** +** Description : This routine copies the packet from a chain of +** mbufs to the chip. When all the data has been +** copied, then the chip automatically begins +** transmitting the data. +** +** The reason why this "simple" copy routine is so +** long and complicated is because all reads and writes +** to the chip must be done as 16-bit words. If an mbuf +** has an odd number of bytes, then the last byte must +** be saved and combined with the first byte of the +** next mbuf. +** +** Parameters : struct cs_softc *sc : device softc structure +** pMbufChain : mbuf to transmit. +** +** Implicit Inputs : None. +** +** Implicit Outputs : None. +** +** Return Value : void. +** +** Side Effects : None. +** +*/ +void csCopyTxFrame( struct cs_softc *sc, struct mbuf *m0 ) +{ + bus_space_tag_t memt = sc->sc_memt; + bus_space_handle_t memh = sc->sc_memh; + struct mbuf *m; + int len, leftover, frameoff; + u_int16_t dbuf; + u_int8_t *p, *lim; + + /* Initialize frame pointer and data port address */ + frameoff = PKTPG_TX_FRAME; + + /* start out with no leftover data */ + leftover = 0; + dbuf = 0; + + /* Process the chain of mbufs */ + for (m = m0; m != NULL; m = m->m_next) { + + /* + * Process all of the data in a single mbuf. + */ + p = mtod(m, u_int8_t *); + len = m->m_len; +#ifdef DIAGNOSTIC + lim = p + len; +#endif + + while (len > 0) { + if (leftover) { + /* + * Data left over (from mbuf or realignment). + * Buffer the next byte, and write it and + * the leftover data out. + */ + dbuf |= *p++ << 8; + len--; + + bus_space_write_2(memt, memh, frameoff, + dbuf); + frameoff += 2; + + leftover = 0; + } else if ((long)p & 1) { + /* + * Misaligned data. Buffer the next byte. + */ + dbuf = *p++; + len--; + + leftover = 1; + } else { + /* + * Aligned data. This is the case we like. + * + * Write-region out as much as we can, then + * buffer the remaining byte (if any). + */ + leftover = len & 1; + len &= ~1; + + bus_space_write_region_2(memt, memh, frameoff, + (u_int16_t *)p, len >> 1); + p += len; + frameoff += len; + + if (leftover) + dbuf = *p++; + len = 0; + } + } + if (len < 0) + panic("csCopyTxFrame: negative len"); +#ifdef DIAGNOSTIC + if (p != lim) + panic("csCopyTxFrame: p != lim"); +#endif + } + if (leftover) + bus_space_write_2(memt, memh, frameoff, dbuf); +} diff --git a/sys/arch/arm32/isa/if_csvar.h b/sys/arch/arm32/isa/if_csvar.h new file mode 100644 index 000000000000..ae9d5c269efc --- /dev/null +++ b/sys/arch/arm32/isa/if_csvar.h @@ -0,0 +1,420 @@ +/* $NetBSD: if_csvar.h,v 1.1 1998/06/08 17:49:43 tv Exp $ */ + +/* + * Copyright 1997 + * Digital Equipment Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and conditions. + * Subject to these conditions, you may download, copy, install, + * use, modify and distribute this software in source and/or binary + * form. No title or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions as + * they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or logo of + * Digital Equipment Corporation. Neither the "Digital Equipment + * Corporation" name nor any trademark or logo of Digital Equipment + * Corporation may be used to endorse or promote products derived + * from this software without the prior written permission of + * Digital Equipment Corporation. + * + * 3) This software is provided "AS-IS" and any express or implied + * warranties, including but not limited to, any implied warranties + * of merchantability, fitness for a particular purpose, or + * non-infringement are disclaimed. In no event shall DIGITAL be + * liable for any damages whatsoever, and in particular, DIGITAL + * shall not be liable for special, indirect, consequential, or + * incidental damages or damages for lost profits, loss of + * revenue or loss of use, whether such damages arise in contract, + * negligence, tort, under statute, in equity, at law or otherwise, + * even if advised of the possibility of such damage. + */ + +/* +**++ +** FACILITY Crystal CS8900 Ethernet driver header file +** +** ABSTRACT +** +** This module provides CS8900 register defines in addition +** to defining the driver softc structure. +** +** AUTHORS +** +** Peter Dettori SEA - Software Engineering. +** +** CREATION DATE: +** +** 13-Feb-1997. +** +** MODIFICATION HISTORY: +** +**-- +*/ + +#ifndef __IF_CSVAR_H__ +#define __IF_CSVAR_H__ + +extern struct cfdriver cs_cd; + +/* Individual Address (Ethernet Address) */ +typedef struct +{ + ushort word[3]; +} ia, *pia; + +/* + * Ethernet software status per interface. + * + * Each interface is referenced by a network interface structure, + * arpcom.ac_if, which the routing code uses to locate the interface. + * This structure contains the output queue for the interface, + * its address, ... + */ +struct cs_softc { + struct device sc_dev; /* base device glue */ + struct ethercom sc_ethercom; /* Ethernet common */ + + void *sc_ih; /* interupt handler */ + void *sc_sh; /* shutdown hook */ + bus_space_tag_t sc_iot; /* bus space tag for IO */ + bus_space_tag_t sc_memt; /* bus space tag for memory mode */ + bus_space_handle_t sc_ioh; /* bus space handles */ + bus_space_handle_t sc_memh; + ushort sc_iobase; /* base IO port address */ + ushort sc_int; /* interrupt level */ + int pPacketPagePhys; /* physical io address */ + ushort mediaType; /* 10BaseT, thin wire */ + ushort configFlags; /* chip configuration software flag */ + int inMemoryMode; /* status */ + int txInProgress; /* flags */ + int resetting; /* chip in reset mode flag */ + uint dmaMemSize; /* 16K or 64K if DMA being used */ + char *dmaBase; /* DMA memory base */ + char *dma_offset; /* offset into DMA space */ + int sc_drq; /* DMA request channel : 5,6,7 */ + + int sc_xe_ent; /* current early-xmit table entry */ + int sc_xe_togo; /* # of packets to go at this ent */ + + u_int8_t sc_enaddr[6]; /* MAC address */ +}; + +/* Debug Status */ +/*#define CS_DEBUG 0*/ + +#ifdef CS_DEBUG +#define dbg_print(x) printf x +#else +#define dbg_print(x) +#endif + +/* Return Status */ +#define CS_ERROR -1 +#define CS_OK 1 + +/* Hardcoded CS8900 specific configuration information */ +#define CS8900_IOSIZE 16 /* CS8900 has 8 2byte IO registers */ +#define CS8900_DMA_BUFFER_SIZE 16384 /* 16Kbyte or 64Kbyte, if + * supported + */ + +/* Receive buffer status */ + +#define RXBUF_FREE 0x0000 +#define RXBUF_ALLOCATED 0x0001 +#define RXBUF_LOANED 0x0002 + + +/* Config Flags in cs_softc */ + +#define CFGFLG_MEM_MODE 0x0001 +#define CFGFLG_USE_SA 0x0002 +#define CFGFLG_IOCHRDY 0x0004 +#define CFGFLG_DCDC_POL 0x0008 +#define CFGFLG_FDX 0x0010 +#define CFGFLG_DMA_MODE 0x0020 +#define CFGFLG_NOT_EEPROM 0x8000 + + +/* Media Type in cs_softc */ + +#define MEDIA_AUI 0x0001 +#define MEDIA_10BASE2 0x0002 +#define MEDIA_10BASET 0x0003 + + +/* Chip Identification */ + +#define EISA_NUM_CRYSTAL 0x630E +#define PROD_ID_MASK 0xE000 +#define PROD_ID_CS8900 0x0000 +#define PROD_ID_CS8920 0x4000 +#define PROD_ID_CS892X 0x6000 +#define PROD_REV_MASK 0x1F00 + + +/* IO Port Offsets */ + +#define PORT_RXTX_DATA 0x0000 +#define PORT_RXTX_DATA_1 0x0002 +#define PORT_TX_CMD 0x0004 +#define PORT_TX_LENGTH 0x0006 +#define PORT_ISQ 0x0008 +#define PORT_PKTPG_PTR 0x000A +#define PORT_PKTPG_DATA 0x000C +#define PORT_PKTPG_DATA_1 0x000E + + +/* PacketPage Offsets */ + +#define PKTPG_EISA_NUM 0x0000 +#define PKTPG_PRODUCT_ID 0x0002 +#define PKTPG_IO_BASE 0x0020 +#define PKTPG_INT_NUM 0x0022 +#define PKTPG_DMA_CHANNEL 0x0024 +#define PKTPG_DMA_START_FRAME 0x0026 +#define PKTPG_DMA_FRAME_COUNT 0x0028 +#define PKTPG_DMA_BYTE_COUNT 0x002A +#define PKTPG_MEM_BASE 0x002C +#define PKTPG_EEPROM_CMD 0x0040 +#define PKTPG_EEPROM_DATA 0x0042 +#define PKTPG_FRAME_BYTE_COUNT 0x0050 +#define PKTPG_RX_CFG 0x0102 +#define PKTPG_RX_CTL 0x0104 +#define PKTPG_TX_CFG 0x0106 +#define PKTPG_BUF_CFG 0x010A +#define PKTPG_LINE_CTL 0x0112 +#define PKTPG_SELF_CTL 0x0114 +#define PKTPG_BUS_CTL 0x0116 +#define PKTPG_TEST_CTL 0x0118 +#define PKTPG_ISQ 0x0120 +#define PKTPG_RX_EVENT 0x0124 +#define PKTPG_TX_EVENT 0x0128 +#define PKTPG_BUF_EVENT 0x012C +#define PKTPG_RX_MISS 0x0130 +#define PKTPG_TX_COL 0x0132 +#define PKTPG_LINE_ST 0x0134 +#define PKTPG_SELF_ST 0x0136 +#define PKTPG_BUS_ST 0x0138 +#define PKTPG_TX_CMD 0x0144 +#define PKTPG_TX_LENGTH 0x0146 +#define PKTPG_LOG_ADDR 0x0150 /* logical address filter + * (hash table) + */ +#define PKTPG_IND_ADDR 0x0158 +#define PKTPG_RX_STATUS 0x0400 +#define PKTPG_RX_LENGTH 0x0402 +#define PKTPG_RX_FRAME 0x0404 +#define PKTPG_TX_FRAME 0x0A00 + + +/* EEPROM Offsets */ + +#define EEPROM_IND_ADDR_H 0x001C +#define EEPROM_IND_ADDR_M 0x001D +#define EEPROM_IND_ADDR_L 0x001E +#define EEPROM_ISA_CFG 0x001F +#define EEPROM_MEM_BASE 0x0020 +#define EEPROM_XMIT_CTL 0x0023 +#define EEPROM_ADPTR_CFG 0x0024 + + +/* Register Numbers */ + +#define REG_NUM_MASK 0x003F +#define REG_NUM_RX_EVENT 0x0004 +#define REG_NUM_TX_EVENT 0x0008 +#define REG_NUM_BUF_EVENT 0x000C +#define REG_NUM_RX_MISS 0x0010 +#define REG_NUM_TX_COL 0x0012 + + +/* Self Control Register */ + +#define SELF_CTL_RESET 0x0040 +#define SELF_CTL_HC1E 0x2000 +#define SELF_CTL_HCB1 0x8000 + + +/* Self Status Register */ + +#define SELF_ST_INIT_DONE 0x0080 +#define SELF_ST_SI_BUSY 0x0100 +#define SELF_ST_EEP_PRES 0x0200 +#define SELF_ST_EEP_OK 0x0400 +#define SELF_ST_EL_PRES 0x0800 + + +/* EEPROM Command Register */ + +#define EEPROM_CMD_READ 0x0200 +#define EEPROM_CMD_ELSEL 0x0400 + + +/* Bus Control Register */ + +#define BUS_CTL_RESET_DMA 0x0040 +#define BUS_CTL_USE_SA 0x0200 +#define BUS_CTL_MEM_MODE 0x0400 +#define BUS_CTL_DMA_BURST 0x0800 +#define BUS_CTL_IOCHRDY 0x1000 +#define BUS_CTL_DMA_SIZE 0x2000 +#define BUS_CTL_INT_ENBL 0x8000 + + +/* Bus Status Register */ + +#define BUS_ST_TX_BID_ERR 0x0080 +#define BUS_ST_RDY4TXNOW 0x0100 + + +/* Line Control Register */ + +#define LINE_CTL_RX_ON 0x0040 +#define LINE_CTL_TX_ON 0x0080 +#define LINE_CTL_AUI_ONLY 0x0100 +#define LINE_CTL_10BASET 0x0000 +#define LINE_CTL_AUTO_SEL 0x0200 + + +/* Test Control Register */ + +#define TEST_CTL_DIS_LT 0x0080 +#define TEST_CTL_ENDEC_LP 0x0200 +#define TEST_CTL_AUI_LOOP 0x0400 +#define TEST_CTL_DIS_BKOFF 0x0800 +#define TEST_CTL_FDX 0x4000 + + +/* Receiver Configuration Register */ + +#define RX_CFG_SKIP 0x0040 +#define RX_CFG_RX_OK_IE 0x0100 +#define RX_CFG_RX_DMA_ONLY 0x0200 +#define RX_CFG_CRC_ERR_IE 0x1000 +#define RX_CFG_RUNT_IE 0x2000 +#define RX_CFG_X_DATA_IE 0x4000 +#define RX_CFG_ALL_IE 0x7100 + + +/* Receiver Event Register */ + +#define RX_EVENT_DRIBBLE 0x0080 +#define RX_EVENT_RX_OK 0x0100 +#define RX_EVENT_IND_ADDR 0x0400 +#define RX_EVENT_BCAST 0x0800 +#define RX_EVENT_CRC_ERR 0x1000 +#define RX_EVENT_RUNT 0x2000 +#define RX_EVENT_X_DATA 0x4000 + + +/* Receiver Control Register */ + +#define RX_CTL_INDHASH_A 0x0040 +#define RX_CTL_PROMISC_A 0x0080 +#define RX_CTL_RX_OK_A 0x0100 +#define RX_CTL_MCAST_A 0x0200 +#define RX_CTL_IND_A 0x0400 +#define RX_CTL_BCAST_A 0x0800 +#define RX_CTL_CRC_ERR_A 0x1000 +#define RX_CTL_RUNT_A 0x2000 +#define RX_CTL_X_DATA_A 0x4000 + + +/* Transmit Configuration Register */ + +#define TX_CFG_LOSS_CRS_IE 0x0040 +#define TX_CFG_SQE_ERR_IE 0x0080 +#define TX_CFG_TX_OK_IE 0x0100 +#define TX_CFG_OUT_WIN_IE 0x0200 +#define TX_CFG_JABBER_IE 0x0400 +#define TX_CFG_16_COLL_IE 0x8000 +#define TX_CFG_ALL_IE 0x8FC0 + + + +/* Transmit Configuration Register */ + +#define TX_EVENT_LOSS_CRS 0x0040 +#define TX_EVENT_SQE_ERR 0x0080 +#define TX_EVENT_TX_OK 0x0100 +#define TX_EVENT_OUT_WIN 0x0200 +#define TX_EVENT_JABBER 0x0400 +#define TX_EVENT_COLL_MASK 0x7800 +#define TX_EVENT_16_COLL 0x8000 + + +/* Transmit Command Register */ + +#define TX_CMD_START_5 0x0000 +#define TX_CMD_START_381 0x0080 +#define TX_CMD_START_1021 0x0040 +#define TX_CMD_START_ALL 0x00C0 +#define TX_CMD_FORCE 0x0100 +#define TX_CMD_ONE_COLL 0x0200 +#define TX_CMD_NO_CRC 0x1000 +#define TX_CMD_NO_PAD 0x2000 + + +/* Buffer Configuration Register */ + +#define BUF_CFG_SW_INT 0x0040 +#define BUF_CFG_RX_DMA_IE 0x0080 +#define BUF_CFG_RDY4TX_IE 0x0100 +#define BUF_CFG_RX_MISS_IE 0x0400 +#define BUF_CFG_TX_UNDR_IE 0x0200 +#define BUF_CFG_RX_128_IE 0x0800 +#define BUF_CFG_TX_COL_OVER_IE 0x1000 +#define BUF_CFG_RX_MISS_OVER_IE 0x2000 +#define BUF_CFG_RX_DEST_IE 0x8000 + +/* Buffer Event Register */ + +#define BUF_EVENT_SW_INT 0x0040 +#define BUF_EVENT_RX_DMA 0x0080 +#define BUF_EVENT_RDY4TX 0x0100 +#define BUF_EVENT_TX_UNDR 0x0200 +#define BUF_EVENT_RX_MISS 0x0400 +#define BUF_EVENT_RX_128 0x0800 +#define BUF_EVENT_RX_DEST 0x8000 + + +/* ISA Configuration from EEPROM */ + +#define ISA_CFG_IRQ_MASK 0x000F +#define ISA_CFG_USE_SA 0x0080 +#define ISA_CFG_IOCHRDY 0x0100 +#define ISA_CFG_MEM_MODE 0x8000 + + +/* Memory Base from EEPROM */ + +#define MEM_BASE_MASK 0xFFF0 + + +/* Adpater Configuration from EEPROM */ + +#define ADPTR_CFG_MEDIA 0x0060 +#define ADPTR_CFG_10BASET 0x0020 +#define ADPTR_CFG_AUI 0x0040 +#define ADPTR_CFG_10BASE2 0x0060 +#define ADPTR_CFG_DCDC_POL 0x0080 + + +/* Transmission Control from EEPROM */ + +#define XMIT_CTL_FDX 0x8000 + + +/* Miscellaneous definitions */ + +#define MAXLOOP 0x8888 +#define CS_DMA_FRAME_HEADER_SIZE (sizeof(ushort) * 2) +#define RXBUFCOUNT 16 +#define MC_LOANED 5 + +#endif diff --git a/sys/arch/arm32/isa/isa_io.c b/sys/arch/arm32/isa/isa_io.c new file mode 100644 index 000000000000..5da94f0272f0 --- /dev/null +++ b/sys/arch/arm32/isa/isa_io.c @@ -0,0 +1,290 @@ +/* $NetBSD: isa_io.c,v 1.1 1998/06/08 17:49:44 tv Exp $ */ + +/* + * Copyright 1997 + * Digital Equipment Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and conditions. + * Subject to these conditions, you may download, copy, install, + * use, modify and distribute this software in source and/or binary + * form. No title or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions as + * they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or logo of + * Digital Equipment Corporation. Neither the "Digital Equipment + * Corporation" name nor any trademark or logo of Digital Equipment + * Corporation may be used to endorse or promote products derived + * from this software without the prior written permission of + * Digital Equipment Corporation. + * + * 3) This software is provided "AS-IS" and any express or implied + * warranties, including but not limited to, any implied warranties + * of merchantability, fitness for a particular purpose, or + * non-infringement are disclaimed. In no event shall DIGITAL be + * liable for any damages whatsoever, and in particular, DIGITAL + * shall not be liable for special, indirect, consequential, or + * incidental damages or damages for lost profits, loss of + * revenue or loss of use, whether such damages arise in contract, + * negligence, tort, under statute, in equity, at law or otherwise, + * even if advised of the possibility of such damage. + */ + +/* + * bus_space I/O functions for isa + */ + +#include +#include +#include +#include +#include + +/* Proto types for all the bus_space structure functions */ + +bs_protos(isa); +bs_protos(bs_notimpl); + +/* Declare the isa bus space tags */ +/* The IO and MEM structs are identical, except for the cookies, */ +/* which contain the address space bases. */ + +/* + * NOTE: ASSEMBLY LANGUAGE RELIES ON THE COOKIE -- THE FIRST MEMBER OF + * THIS STRUCTURE -- TO BE THE VIRTUAL ADDRESS OF ISA/IO! + */ +struct bus_space isa_io_bs_tag = { + /* cookie */ + NULL, /* initialized below */ + + /* mapping/unmapping */ + isa_map, + isa_unmap, + isa_subregion, + + /* allocation/deallocation */ + isa_alloc, + isa_free, + + /* barrier */ + isa_barrier, + + /* read (single) */ + isa_r_1, + isa_r_2, + isa_r_4, + bs_notimpl_r_8, + + /* read multiple */ + isa_rm_1, + isa_rm_2, + isa_rm_4, + bs_notimpl_rm_8, + + /* read region */ + isa_rr_1, + isa_rr_2, + isa_rr_4, + bs_notimpl_rr_8, + + /* write (single) */ + isa_w_1, + isa_w_2, + isa_w_4, + bs_notimpl_w_8, + + /* write multiple */ + isa_wm_1, + isa_wm_2, + isa_wm_4, + bs_notimpl_wm_8, + + /* write region */ + isa_wr_1, + isa_wr_2, + isa_wr_4, + bs_notimpl_wr_8, + + /* set multiple */ + bs_notimpl_sm_1, + bs_notimpl_sm_2, + bs_notimpl_sm_4, + bs_notimpl_sm_8, + + /* set region */ + bs_notimpl_sr_1, + bs_notimpl_sr_2, + bs_notimpl_sr_4, + bs_notimpl_sr_8, + + /* copy */ + bs_notimpl_c_1, + bs_notimpl_c_2, + bs_notimpl_c_4, + bs_notimpl_c_8, +}; + +/* + * NOTE: ASSEMBLY LANGUAGE RELIES ON THE COOKIE -- THE FIRST MEMBER OF + * THIS STRUCTURE -- TO BE THE VIRTUAL ADDRESS OF ISA/MEMORY! + */ +struct bus_space isa_mem_bs_tag = { + /* cookie */ + NULL, /* initialized below */ + + /* mapping/unmapping */ + isa_map, + isa_unmap, + isa_subregion, + + /* allocation/deallocation */ + isa_alloc, + isa_free, + + /* barrier */ + isa_barrier, + + /* read (single) */ + isa_r_1, + isa_r_2, + isa_r_4, + bs_notimpl_r_8, + + /* read multiple */ + isa_rm_1, + isa_rm_2, + isa_rm_4, + bs_notimpl_rm_8, + + /* read region */ + isa_rr_1, + isa_rr_2, + isa_rr_4, + bs_notimpl_rr_8, + + /* write (single) */ + isa_w_1, + isa_w_2, + isa_w_4, + bs_notimpl_w_8, + + /* write multiple */ + isa_wm_1, + isa_wm_2, + isa_wm_4, + bs_notimpl_wm_8, + + /* write region */ + isa_wr_1, + isa_wr_2, + isa_wr_4, + bs_notimpl_wr_8, + + /* set multiple */ + bs_notimpl_sm_1, + bs_notimpl_sm_2, + bs_notimpl_sm_4, + bs_notimpl_sm_8, + + /* set region */ + bs_notimpl_sr_1, + bs_notimpl_sr_2, + bs_notimpl_sr_4, + bs_notimpl_sr_8, + + /* copy */ + bs_notimpl_c_1, + bs_notimpl_c_2, + bs_notimpl_c_4, + bs_notimpl_c_8, +}; + +/* bus space functions */ + +void +isa_io_init(vm_offset_t isa_io_addr, vm_offset_t isa_mem_addr) +{ + isa_io_bs_tag.bs_cookie = (void *)isa_io_addr; + isa_mem_bs_tag.bs_cookie = (void *)isa_mem_addr; +} + +/* break the abstraction: sometimes, other parts of the system + (e.g. X servers) need to map ISA space directly. use these + functions sparingly! */ +vm_offset_t +isa_io_data_vaddr() +{ + return (vm_offset_t)isa_io_bs_tag.bs_cookie; +} + +vm_offset_t +isa_mem_data_vaddr() +{ + return (vm_offset_t)isa_mem_bs_tag.bs_cookie; +} + +int +isa_map(t, bpa, size, cacheable, bshp) + void *t; + bus_addr_t bpa; + bus_size_t size; + int cacheable; + bus_space_handle_t *bshp; +{ + *bshp = bpa + (bus_addr_t)t; + return(0); +} + +void +isa_unmap(t, bsh, size) + void *t; + bus_space_handle_t bsh; + bus_size_t size; +{ + /* Nothing to do. */ +} + +int +isa_subregion(t, bsh, offset, size, nbshp) + void *t; + bus_space_handle_t bsh; + bus_size_t offset, size; + bus_space_handle_t *nbshp; +{ + panic("isa_subregion(): Help!\n"); +} + +int +isa_alloc(t, rstart, rend, size, alignment, boundary, cacheable, + bpap, bshp) + void *t; + bus_addr_t rstart, rend; + bus_size_t size, alignment, boundary; + int cacheable; + bus_addr_t *bpap; + bus_space_handle_t *bshp; +{ + panic("isa_alloc(): Help!\n"); +} + +void +isa_free(t, bsh, size) + void *t; + bus_space_handle_t bsh; + bus_size_t size; +{ + panic("isa_free(): Help!\n"); +} + +void +isa_barrier(t, bsh, offset, len, flags) + void *t; + bus_space_handle_t bsh; + bus_size_t offset, len; + int flags; +{ + panic("isa_barrier(): Help!\n"); +} diff --git a/sys/arch/arm32/isa/isa_io_asm.S b/sys/arch/arm32/isa/isa_io_asm.S new file mode 100644 index 000000000000..b63df220f1c1 --- /dev/null +++ b/sys/arch/arm32/isa/isa_io_asm.S @@ -0,0 +1,391 @@ +/* $NetBSD: isa_io_asm.S,v 1.1 1998/06/08 17:49:44 tv Exp $ */ + +/* + * Copyright 1997 + * Digital Equipment Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and conditions. + * Subject to these conditions, you may download, copy, install, + * use, modify and distribute this software in source and/or binary + * form. No title or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions as + * they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or logo of + * Digital Equipment Corporation. Neither the "Digital Equipment + * Corporation" name nor any trademark or logo of Digital Equipment + * Corporation may be used to endorse or promote products derived + * from this software without the prior written permission of + * Digital Equipment Corporation. + * + * 3) This software is provided "AS-IS" and any express or implied + * warranties, including but not limited to, any implied warranties + * of merchantability, fitness for a particular purpose, or + * non-infringement are disclaimed. In no event shall DIGITAL be + * liable for any damages whatsoever, and in particular, DIGITAL + * shall not be liable for special, indirect, consequential, or + * incidental damages or damages for lost profits, loss of + * revenue or loss of use, whether such damages arise in contract, + * negligence, tort, under statute, in equity, at law or otherwise, + * even if advised of the possibility of such damage. + */ + +/* + * bus_space I/O functions for isa + */ + +#include + +fp .req r11 +ip .req r12 +sp .req r13 +lr .req r14 +pc .req r15 + + .text + +/* + * read single + */ + +ENTRY(isa_r_1) + ldrb r0, [r1, r2] +#ifdef GPROF + nop ; nop ; nop ; nop ; nop +#endif + mov pc, lr + +ENTRY(isa_r_2) + add r0, r1, r2 +/* ldrh r0, [r0] */ .word 0xe1d000b0 +#ifdef GPROF + nop ; nop ; nop ; nop ; nop +#endif + mov pc, lr + +ENTRY(isa_r_4) + ldr r0, [r1, r2] +#ifdef GPROF + nop ; nop ; nop ; nop ; nop +#endif + mov pc, lr +/* + * read multiple. + * + * cloned from assembly generated for: + * + * void + * isa_rm_4(v, h, o, p, s) + * void *v; + * bus_space_handle_t h; + * bus_size_t o, s; + * u_int32_t *p; + * { + * u_int32_t *port, *lim; + * + * port = (u_in32_t *)(h + o); + * lim = p + s; + * + * while (p < lim) + * *p++ = *port;; + * } + */ + +ENTRY(isa_rm_1) + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + mov r0, r3 + add r1, r1, r2 + sub fp, ip, #4 + ldr r3, [fp, #4] + add ip, r0, r3 + cmp r0, ip + ldmcsea fp, {fp, sp, pc} +Lisa_rm_1_loop: + ldrb r3, [r1, #0] + strb r3, [r0], #1 + cmp r0, ip + bcc Lisa_rm_1_loop + ldmea fp, {fp, sp, pc} + +ENTRY(isa_rm_2) + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + mov r0, r3 + add r1, r1, r2 + sub fp, ip, #4 + ldr r3, [fp, #4] + add ip, r0, r3, asl #1 + cmp r0, ip + ldmcsea fp, {fp, sp, pc} +Lisa_rm_2_loop: + /* ldrh r3, [r1] */ .word 0xe1d130b0 + /* strh r3, [r0], #2 */ .word 0xe0c030b2 + cmp r0, ip + bcc Lisa_rm_2_loop + ldmea fp, {fp, sp, pc} + +ENTRY(isa_rm_4) + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + mov r0, r3 + add r1, r1, r2 + sub fp, ip, #4 + ldr r3, [fp, #4] + add ip, r0, r3, asl #2 + cmp r0, ip + ldmcsea fp, {fp, sp, pc} +Lisa_rm_4_loop: + ldr r3, [r1, #0] + str r3, [r0], #4 + cmp r0, ip + bcc Lisa_rm_4_loop + ldmea fp, {fp, sp, pc} + +/* + * read region. + * + * cloned from assembly generated for: + * + * void + * isa_wr_4(v, h, o, p, s) + * void *v; + * bus_space_handle_t h; + * bus_size_t o, s; + * u_int32_t *p; + * { + * u_int32_t *cur, *lim; + * + * cur = (u_in32_t *)(h + o); + * lim = cur + s; + * + * while (cur < lim) + * *cur++ = *p++; + * } + * + */ + +ENTRY(isa_rr_1) + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + mov r0, r3 + add r1, r1, r2 + sub fp, ip, #4 + ldr r3, [fp, #4] + add r2, r1, r3 + cmp r1, r2 + ldmcsea fp, {fp, sp, pc} +Lisa_rr_1_loop: + ldrb r3, [r1], #1 + strb r3, [r0], #1 + cmp r1, r2 + bcc Lisa_rr_1_loop + ldmea fp, {fp, sp, pc} + +ENTRY(isa_rr_2) + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + mov r0, r3 + add r1, r1, r2 + sub fp, ip, #4 + ldr r3, [fp, #4] + add r2, r1, r3, asl #1 + cmp r1, r2 + ldmcsea fp, {fp, sp, pc} +Lisa_rr_2_loop: + /* ldrh r3, [r1], #2 */ .word 0xe0d130b2 + /* strh r3, [r0], #2 */ .word 0xe0c030b2 + cmp r1, r2 + bcc Lisa_rr_2_loop + ldmea fp, {fp, sp, pc} + +ENTRY(isa_rr_4) + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + mov r0, r3 + add r1, r1, r2 + sub fp, ip, #4 + ldr r3, [fp, #4] + add r2, r1, r3, asl #2 + cmp r1, r2 + ldmcsea fp, {fp, sp, pc} +Lisa_rr_4_loop: + ldr r3, [r1], #4 + str r3, [r0], #4 + cmp r1, r2 + bcc Lisa_rr_4_loop + ldmea fp, {fp, sp, pc} + +/* + * write single + */ + +ENTRY(isa_w_1) + strb r3, [r1, r2] +#ifdef GPROF + nop ; nop ; nop ; nop ; nop +#endif + mov pc, lr + +ENTRY(isa_w_2) + add r0, r1, r2 +/* strh r3, [r0] */ .word 0xe1c030b0 +#ifdef GPROF + nop ; nop ; nop ; nop ; nop +#endif + mov pc, lr + +ENTRY(isa_w_4) + str r3, [r1, r2] +#ifdef GPROF + nop ; nop ; nop ; nop ; nop +#endif + mov pc, lr + +/* write multiple + * + * cloned from assembly generated for: + * + * void + * isa_wm_4(v, h, o, p, s) + * void *v; + * bus_space_handle_t h; + * bus_size_t o, s; + * u_int32_t *p; + * { + * u_int32_t *port, *lim; + * + * port = (u_in32_t *)(h + o); + * lim = p + s; + * + * while (p < lim) + * *port = *p++; + * } + */ +ENTRY(isa_wm_1) + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + mov r0, r3 + add r1, r1, r2 + sub fp, ip, #4 + ldr r3, [fp, #4] + add ip, r0, r3 + cmp r0, ip + ldmcsea fp, {fp, sp, pc} +Lisa_wm_1_loop: + ldrb r3, [r0], #1 + strb r3, [r1] + cmp r0, ip + bcc Lisa_wm_1_loop + ldmea fp, {fp, sp, pc} + +ENTRY(isa_wm_2) + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + mov r0, r3 + add r1, r1, r2 + sub fp, ip, #4 + ldr r3, [fp, #4] + add ip, r0, r3, asl #1 + cmp r0, ip + ldmcsea fp, {fp, sp, pc} +Lisa_wm_2_loop: + /* ldrh r3, [r0], #2 */ .word 0xe0d030b2 + /* strh r3, [r1] */ .word 0xe1c130b0 + cmp r0, ip + bcc Lisa_wm_2_loop + ldmea fp, {fp, sp, pc} + +ENTRY(isa_wm_4) + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + mov r0, r3 + add r1, r1, r2 + sub fp, ip, #4 + ldr r3, [fp, #4] + add ip, r0, r3, asl #2 + cmp r0, ip + ldmcsea fp, {fp, sp, pc} +Lisa_wm_4_loop: + ldr r3, [r0], #4 + str r3, [r1] + cmp r0, ip + bcc Lisa_wm_4_loop + ldmea fp, {fp, sp, pc} + +/* + * write region. + * + * cloned from assembly generated for: + * + * void + * isa_wr_4(v, h, o, p, s) + * void *v; + * bus_space_handle_t h; + * bus_size_t o, s; + * u_int32_t *p; + * { + * u_int32_t *cur, *lim; + * + * cur = (u_in32_t *)(h + o); + * lim = cur + s; + * + * while (cur < lim) + * *cur++ = *p++; + * } + * + */ + +ENTRY(isa_wr_1) + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + mov r0, r3 + add r1, r1, r2 + sub fp, ip, #4 + ldr r3, [fp, #4] + add r2, r1, r3 + cmp r1, r2 + ldmcsea fp, {fp, sp, pc} +Lisa_wr_1_loop: + ldrb r3, [r0], #1 + strb r3, [r1], #1 + cmp r1, r2 + bcc Lisa_wr_1_loop + ldmea fp, {fp, sp, pc} + +ENTRY(isa_wr_2) + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + mov r0, r3 + add r1, r1, r2 + sub fp, ip, #4 + ldr r3, [fp, #4] + add r2, r1, r3, asl #1 + cmp r1, r2 + ldmcsea fp, {fp, sp, pc} +Lisa_wr_2_loop: + /* ldrh r3, [r0], #2 */ .word 0xe0d030b2 + /* strh r3, [r1], #2 */ .word 0xe0c130b2 + cmp r1, r2 + bcc Lisa_wr_2_loop + ldmea fp, {fp, sp, pc} + +ENTRY(isa_wr_4) + mov ip, sp + stmfd sp!, {fp, ip, lr, pc} + mov r0, r3 + add r1, r1, r2 + sub fp, ip, #4 + ldr r3, [fp, #4] + add r2, r1, r3, asl #2 + cmp r1, r2 + ldmcsea fp, {fp, sp, pc} +Lisa_wr_4_loop: + ldr r3, [r0], #4 + str r3, [r1], #4 + cmp r1, r2 + bcc Lisa_wr_4_loop + ldmea fp, {fp, sp, pc} diff --git a/sys/arch/arm32/isa/isa_irq.S b/sys/arch/arm32/isa/isa_irq.S new file mode 100644 index 000000000000..734552996a76 --- /dev/null +++ b/sys/arch/arm32/isa/isa_irq.S @@ -0,0 +1,585 @@ +/* $NetBSD: isa_irq.S,v 1.1 1998/06/08 17:49:44 tv Exp $ */ + +/* + * Copyright 1997 + * Digital Equipment Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and conditions. + * Subject to these conditions, you may download, copy, install, + * use, modify and distribute this software in source and/or binary + * form. No title or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions as + * they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or logo of + * Digital Equipment Corporation. Neither the "Digital Equipment + * Corporation" name nor any trademark or logo of Digital Equipment + * Corporation may be used to endorse or promote products derived + * from this software without the prior written permission of + * Digital Equipment Corporation. + * + * 3) This software is provided "AS-IS" and any express or implied + * warranties, including but not limited to, any implied warranties + * of merchantability, fitness for a particular purpose, or + * non-infringement are disclaimed. In no event shall DIGITAL be + * liable for any damages whatsoever, and in particular, DIGITAL + * shall not be liable for special, indirect, consequential, or + * incidental damages or damages for lost profits, loss of + * revenue or loss of use, whether such damages arise in contract, + * negligence, tort, under statute, in equity, at law or otherwise, + * even if advised of the possibility of such damage. + */ + +/* + * Copyright (c) 1994-1997 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * All rights reserved. + * + * This code is derived from software written for Brini by Mark Brinicombe + * + * 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 Brini. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``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 BRINI 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. + * + * RiscBSD kernel project + * + * irq.S + * + * Low level irq and fiq handlers + * + * Created : 27/09/94 + */ + +#include "assym.h" +#include +#include +#include +#include +#include +#include + +sp .req r13 +lr .req r14 +pc .req r15 + + .text + +/* + * + * irq_entry + * + * Main entry point for the IRQ vector + * + * This function reads the irq request bits in the IOMD registers + * IRQRQA, IRQRQB and DMARQ + * It then calls an installed handler for each bit that is set. + * The function stray_irqhandler is called if a handler is not defined + * for a particular interrupt. + * If a interrupt handler is found then it is called with r0 containing + * the argument defined in the handler structure. If the field ih_arg + * is zero then a pointer to the IRQ frame on the stack is passed instead. + */ + +Ldisabled_mask: + .word _disabled_mask + +Lcurrent_spl_level: + .word _current_spl_level + +Lcurrent_intr_depth: + .word _current_intr_depth + +Lvam_io_data: + .word _isa_io_bs_tag + + .text + + .global irq_entry + +/* + * Register usage + * + * r6 - Address of current handler + * r7 - Pointer to handler pointer list + * r8 - Current IRQ requests. + * r9 - Used to count through possible IRQ bits. + * r10 - Base address of IOMD + */ + +/* Some documentation is in isa_machdep.c */ +irq_entry: + sub lr, lr, #0x00000004 /* Adjust the lr */ + + PUSHFRAMEINSVC /* Push an interrupt frame */ + +/* mov r11, #0x00000000*/ /* Trace back stops here */ + + /* Load r8 with the ISA 8259 irqs */ + /* r8 <- irq's pending [15:0] */ + + /* address of 8259 #1 */ + ldr r0, Lvam_io_data + ldr r0, [r0] + ldrb r8, [r0, #IO_ICU1] /* ocw3 = irr */ + + /* clear the IRR bits that are currently masked. */ + ldr r2, Li8259_mask + ldr r2, [r2] + mvn r2, r2 /* disabled -> enabled */ + + /* address of 8259 #2 */ + tst r2, #(1 << IRQ_SLAVE) /* if slave is enabled */ + tstne r8, #(1 << IRQ_SLAVE) /* anything from slave? */ + ldrneb r1, [r0, #IO_ICU2] /* ocw3 = irr */ + orrne r8, r8, r1, lsl #8 + + and r8, r8, r2 /* clear disabled */ + + /* clear IRQ 2, which is only used for slave 8259 */ + bic r8, r8, #(1 << IRQ_SLAVE) + + /* + * Note that we have entered the IRQ handler. + * We are in SVC mode so we cannot use the processor mode + * to determine if we are in an IRQ. Instead we will count the + * each time the interrupt handler is nested. + */ + + ldr r0, Lcurrent_intr_depth + ldr r1, [r0] + add r1, r1, #1 + str r1, [r0] + + /* Block the current requested interrupts */ + + ldr r1, Ldisabled_mask + ldr r0, [r1] + stmfd sp!, {r0} + orr r0, r0, r8 + str r0, [r1] + + /* + * Need to block all interrupts at the IPL or lower for + * all asserted interrupts. + * This basically emulates hardware interrupt priority levels. + * Means we need to go through the interrupt mask and for + * every asserted interrupt we need to mask out all other + * interrupts at the same or lower IPL. + * If only we could wait until the main loop but we need to sort + * this out first so interrupts can be re-enabled. + * + * This would benefit from a special ffs type routine + */ + + mov r9, #0x00000001 + ldr r7, Lirqblock + +emulate_hwipl_loop: + tst r8, r9 + ldrne r6, [r7] + orrne r0, r0, r6 + + add r7, r7, #4 + mov r9, r9, lsl #1 /* move on to next bit */ +#ifdef CPU_ARM7500 + teq r9, #0 /* done the last bit ? */ +#else + teq r9, #(1 << 24) /* done the last bit ? */ +#endif /* CPU_ARM7500 */ + bne emulate_hwipl_loop /* no - loop back. */ + + str r0, [r1] + + /* Update the IOMD irq masks */ + + bl _irq_setmasks + + mrs r0, cpsr_all /* Enable IRQ's */ + bic r0, r0, #I32_bit + msr cpsr_all, r0 + + ldr r7, [pc, #irqhandlers - . - 8] + mov r9, #0x00000001 + + stmfd sp!, {r8} + +irqloop: + /* This would benefit from a special ffs type routine */ + tst r8, r9 /* Is a bit set ? */ + beq nextirq /* No ? try next bit */ + + ldr r6, [r7] /* Get address of first handler structure */ + + teq r6, #0x00000000 /* Do we have a handler */ + moveq r0, r8 /* IRQ requests as arg 0 */ + beq _stray_irqhandler /* call special handler */ + + ldr r0, Lcnt + ldr r1, [r0, #(V_INTR)] + add r1, r1, #0x00000001 + str r1, [r0, #(V_INTR)] + +irqchainloop: + add lr, pc, #nextinchain - . - 8 /* return address */ + +/* + * XXX: Should stats be accumlated for every interrupt routine called + * or for every physical interrupt that is serviced. + */ + +#ifdef IRQSTATS + ldr r0, Lintrcnt + ldr r1, [r6, #(IH_NUM)] + + add r0, r0, r1, lsl #2 + ldr r1, [r0] + add r1, r1, #0x00000001 + str r1, [r0] +#endif /* IRQSTATS */ + + ldr r0, [r6, #(IH_ARG)] /* Get argument pointer */ + teq r0, #0x00000000 /* If arg is zero pass stack frame */ + addeq r0, sp, #8 /* ... stack frame */ + ldr pc, [r6, #(IH_FUNC)] /* Call handler */ + +nextinchain: + teq r0, #0x00000001 /* Was the irq serviced ? */ + beq irqdone + + ldr r6, [r6, #(IH_NEXT)] + teq r6, #0x00000000 + bne irqchainloop + +irqdone: +nextirq: + add r7, r7, #0x00000004 /* update pointer to handlers */ + mov r9, r9, lsl #1 /* move on to next bit */ +#ifdef CPU_ARM7500 + teq r9, #0 /* done the last bit ? */ +#else + teq r9, #(1 << 24) /* done the last bit ? */ +#endif /* CPU_ARM7500 */ + bne irqloop /* no - loop back. */ + + ldmfd sp!, {r8} + + /* Restore previous disabled mask */ + ldmfd sp!, {r2} + ldr r1, Ldisabled_mask + str r2, [r1] + bl _irq_setmasks + + bl _dosoftints /* Handle the soft interrupts */ + + /* Manage AST's. Maybe this should be done as a soft interrupt ? */ + + ldr r0, [sp] /* Get the SPSR from stack */ + + and r0, r0, #(PSR_MODE) /* Test for USR32 mode before the IRQ */ + teq r0, #(PSR_USR32_MODE) + ldreq r0, Lastpending /* Do we have an AST pending ? */ + ldreq r1, [r0] + teqeq r1, #0x00000001 + + beq irqast /* call the AST handler */ + + /* Kill IRQ's in preparation for exit */ + + mrs r0, cpsr_all + orr r0, r0, #(I32_bit) + msr cpsr_all, r0 + + /* Decrement the nest count */ + + ldr r0, Lcurrent_intr_depth + ldr r1, [r0] + sub r1, r1, #1 + str r1, [r0] + + PULLFRAMEFROMSVCANDEXIT + + movs pc, lr /* Exit */ + + /* + * Ok, snag with current intr depth ... + * If ast() calls mi_sleep() the current_intr_depth will not be + * decremented until the process is woken up. This can result + * in the system believing it is still in the interrupt handler. + * If we are calling ast() then correct the current_intr_depth + * before the call. + */ +irqast: + mov r1, #0x00000000 /* Clear ast_pending */ + str r1, [r0] + + /* Kill IRQ's so we atomically decrement current_intr_depth */ + + mrs r2, cpsr_all + orr r3, r2, #(I32_bit) + msr cpsr_all, r3 + + /* Decrement the nest count */ + + ldr r0, Lcurrent_intr_depth + ldr r1, [r0] + sub r1, r1, #1 + str r1, [r0] + + /* Restore IRQ's */ + msr cpsr_all, r2 + + mov r0, sp + bl _ast + +/* Kill IRQ's in preparation for exit */ + + mrs r0, cpsr_all + orr r0, r0, #(I32_bit) + msr cpsr_all, r0 + + PULLFRAMEFROMSVCANDEXIT + + movs pc, lr /* Exit */ + + +Lspl_mask: + .word _spl_mask /* irq's allowed at current spl level */ + +Lcurrent_mask: + .word _current_mask /* irq's that are usable */ + +Lirqblock: + .word _irqblock + +ENTRY(irq_setmasks) + /* Disable interrupts */ + mrs r3, cpsr_all + orr r1, r3, #(I32_bit) + msr cpsr_all, r1 + + /* Calculate interrupt mask */ + ldr r1, Lcurrent_mask /* All the enabled interrupts */ +#ifdef LDRH + ldrh r1, [r1] /* get hardware bits of mask */ +#else + .word 0xe0d110b0 /* hand-assembled ldrh r1, [r1] */ +#endif + ldr r2, Lspl_mask /* Block due to current spl level */ + ldr r2, [r2] + and r1, r1, r2 + ldr r2, Ldisabled_mask /* Block due to active interrupts */ + ldr r2, [r2] + bic r1, r1, r2 + + /* since 8259's are so slow to access, this code does everything + possible to avoid them */ + + /* get current mask: these are the bits */ + ldr r0, Li8259_mask + ldr r2, [r0] + /* r2 = 0000.0000.0000.0000.ZZZZ.ZZZZ.ZZZZ.ZZZZ */ + + /* see if there's anything enabled on 8259 #2 */ + tst r1, #0xff00 + + biceq r1, r1, #(1 << IRQ_SLAVE) /* no, so disable it */ + orrne r1, r1, #(1 << IRQ_SLAVE) /* yes, so enable it */ + /* eq => r1 = 0000.0000.0000.0000.0000.0000.MMMM.M0MM + ne => r1 = 0000.0000.0000.0000.MMMM.MMMM.MMMM.M1MM */ + + /* 8259 bit high => disable */ + mvn r1, r1 + /* eq => r1 = 1111.1111.1111.1111.1111.1111.YYYY.Y1YY + ne => r1 = 1111.1111.1111.1111.YYYY.YYYY.YYYY.Y0YY + (for each bit position Y = !M) */ + + orreq r1, r2, r1, lsl #16 + /* eq => r1 = 1111.1111.YYYY.Y1YY.ZZZZ.ZZZZ.ZZZZ.ZZZZ + ne => r1 = 1111.1111.1111.1111.YYYY.YYYY.YYYY.Y0YY */ + orreq r1, r1, #0x000000FF + /* eq => r1 = 1111.1111.YYYY.Y1YY.ZZZZ.ZZZZ.1111.1111 + ne => r1 = 1111.1111.1111.1111.YYYY.YYYY.YYYY.Y0YY */ + and r1, r1, r1, lsr #16 + /* eq => r1 = 0000.0000.0000.0000.ZZZZ.ZZZZ.YYYY.Y1YY + ne => r1 = 0000.0000.0000.0000.YYYY.YYYY.YYYY.Y0YY */ + + /* if old = new, don't bother to set again. + fast path to exit, since 8259's are so slow anyway */ + eors r2, r1, r2 /* which bits are different? */ + msreq cpsr_all, r3 /* no bits are different, return */ + moveq pc, lr + + /* have to set at least one of the 8259's, store new mask */ + str r1, [r0] + ldr r0, Lvam_io_data + ldr r0, [r0] + + /* see if there's any change for 8259 #1 (master) */ + tst r2, #0x00FF /* bottom 8 bits different? */ + strneb r1, [r0, #(IO_ICU1 + 1)] /* icu1 / ocw1 */ + + /* anything for 8259 #2? */ + tst r2, #0xFF00 + mov r1, r1, lsr #8 /* next byte */ + strneb r1, [r0, #(IO_ICU2 + 1)] /* icu2 / ocw1 */ + + /* Restore old cpsr and exit */ + msr cpsr_all, r3 + mov pc, lr + + +Lcnt: + .word _cnt + +Lintrcnt: + .word _intrcnt + +Li8259_mask: + .word _i8259_mask + +irqhandlers: + .word _irqhandlers /* Pointer to array of irqhandlers */ + +Lastpending: + .word _astpending + +#ifdef IRQSTATS +/* These symbols are used by vmstat */ + + .text + .global __intrnames +__intrnames: + .word _intrnames + + .data + + /* XXX fix */ + .globl _intrnames, _eintrnames, _intrcnt, _eintrcnt +_intrnames: + .asciz "interrupt 0 " + .asciz "softnet " /* reserved0 */ + .asciz "interrupt 2 " + .asciz "interrupt 3 " + .asciz "interrupt 4 " + .asciz "interrupt 5 " + .asciz "interrupt 6 " + .asciz "softclock " /* reserved1 */ + .asciz "softplip " /* reserved2 */ + .asciz "interrupt 9 " + .asciz "interrupt 10 " + .asciz "interrupt 11 " + .asciz "interrupt 12 " + .asciz "interrupt 13 " + .asciz "interrupt 14 " + .asciz "interrupt 15 " + .asciz "dma channel 0" + .asciz "dma channel 1" + .asciz "dma channel 2" + .asciz "dma channel 3" + .asciz "interrupt 20 " + .asciz "interrupt 21 " + .asciz "reserved 3 " + .asciz "reserved 4 " + .asciz "exp card 0 " + .asciz "exp card 1 " + .asciz "exp card 2 " + .asciz "exp card 3 " + .asciz "exp card 4 " + .asciz "exp card 5 " + .asciz "exp card 6 " + .asciz "exp card 7 " +_eintrnames: + + .bss + .align 0 +_intrcnt: + .space 32*4 /* XXX Should be linked to number of interrupts */ +_eintrcnt: + +#else /* IRQSTATS */ +/* Dummy entries to keep vmstat happy */ + + .text + .globl _intrnames, _eintrnames, _intrcnt, _eintrcnt +_intrnames: + .long 0 +_eintrnames: + +_intrcnt: + .long 0 +_eintrcnt: +#endif /* IRQSTATS */ + +/* FIQ code */ + + .text + .align 0 + .global _fiq_setregs /* Sets up the FIQ handler */ + +_fiq_setregs: + mrs r2, cpsr_all + mov r3, r2 + bic r2, r2, #(PSR_MODE) + orr r2, r2, #(PSR_FIQ32_MODE) + orr r2, r2, #(I32_bit | F32_bit) /* IRQs/FIQs definitely off */ + msr cpsr_all, r2 + + ldr r8, [r0, #FH_R8] /* Update FIQ registers*/ + ldr r9, [r0, #FH_R9] + ldr r10, [r0, #FH_R10] + ldr r11, [r0, #FH_R11] + ldr r12, [r0, #FH_R12] + ldr r13, [r0, #FH_R13] + + msr cpsr_all, r3 /* Back to old mode */ + + mov pc, lr /* Exit */ + + .global _fiq_getregs /* Gets the FIQ registers */ + +_fiq_getregs: + mrs r2, cpsr_all + mov r3, r2 + bic r2, r2, #(PSR_MODE) + orr r2, r2, #(PSR_FIQ32_MODE) + orr r2, r2, #(I32_bit | F32_bit) /* IRQs/FIQs definitely off */ + msr cpsr_all, r2 + + str r8, [r0, #FH_R8] /* Update FIQ registers*/ + str r9, [r0, #FH_R9] + str r10, [r0, #FH_R10] + str r11, [r0, #FH_R11] + str r12, [r0, #FH_R12] + str r13, [r0, #FH_R13] + + msr cpsr_all, r3 /* Back to old mode */ + + mov pc, lr /* Exit */ + +/* End of irq.S */ diff --git a/sys/arch/arm32/isa/isa_irqhandler.c b/sys/arch/arm32/isa/isa_irqhandler.c new file mode 100644 index 000000000000..5705519ce7dc --- /dev/null +++ b/sys/arch/arm32/isa/isa_irqhandler.c @@ -0,0 +1,591 @@ +/* $NetBSD: isa_irqhandler.c,v 1.1 1998/06/08 17:49:44 tv Exp $ */ + +/* + * Copyright 1997 + * Digital Equipment Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and conditions. + * Subject to these conditions, you may download, copy, install, + * use, modify and distribute this software in source and/or binary + * form. No title or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions as + * they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or logo of + * Digital Equipment Corporation. Neither the "Digital Equipment + * Corporation" name nor any trademark or logo of Digital Equipment + * Corporation may be used to endorse or promote products derived + * from this software without the prior written permission of + * Digital Equipment Corporation. + * + * 3) This software is provided "AS-IS" and any express or implied + * warranties, including but not limited to, any implied warranties + * of merchantability, fitness for a particular purpose, or + * non-infringement are disclaimed. In no event shall DIGITAL be + * liable for any damages whatsoever, and in particular, DIGITAL + * shall not be liable for special, indirect, consequential, or + * incidental damages or damages for lost profits, loss of + * revenue or loss of use, whether such damages arise in contract, + * negligence, tort, under statute, in equity, at law or otherwise, + * even if advised of the possibility of such damage. + */ + +/* + * Copyright (c) 1994-1996 Mark Brinicombe. + * Copyright (c) 1994 Brini. + * All rights reserved. + * + * This code is derived from software written for Brini by Mark Brinicombe + * + * 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 Brini. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BRINI ``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 BRINI 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. + * + * RiscBSD kernel project + * + * irqhandler.c + * + * IRQ/FIQ initialisation, claim, release and handler routines + * + * Created : 30/09/94 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +irqhandler_t *irqhandlers[NIRQS]; +fiqhandler_t *fiqhandlers; + +int current_intr_depth = 0; +u_int irqmasks[IPL_LEVELS]; +u_int current_mask; +u_int actual_mask; +u_int disabled_mask = 0; +u_int spl_mask; +u_int soft_interrupts; +extern u_int intrcnt[]; + +u_int irqblock[NIRQS]; + +typedef struct { + vm_offset_t physical; + vm_offset_t virtual; +} pv_addr_t; + +extern pv_addr_t systempage; +extern char *_intrnames; + +/* Prototypes */ + +extern void zero_page_readonly __P((void)); +extern void zero_page_readwrite __P((void)); +extern int fiq_setregs __P((fiqhandler_t *)); +extern int fiq_getregs __P((fiqhandler_t *)); +extern void set_spl_masks __P((void)); + +extern void arpintr __P((void)); +extern void ipintr __P((void)); +extern void pppintr __P((void)); +extern void plipintr __P((void)); + +void irq_calculatemasks __P((void)); + +#define WriteWord(a, b) \ +*((volatile unsigned int *)(a)) = (b) + +/* + * void irq_init(void) + * + * Initialise the IRQ/FIQ sub system + */ + +void +irq_init() +{ + int loop; + + /* Clear all the IRQ handlers and the irq block masks */ + + for (loop = 0; loop < NIRQS; ++loop) { + irqhandlers[loop] = NULL; + irqblock[loop] = 0; + } + + /* Clear the FIQ handler */ + + fiqhandlers = NULL; + + /* + * Setup the irqmasks for the different Interrupt Priority Levels + * We will start with no bits set and these will be updated as handlers + * are installed at different IPL's. + */ + + irqmasks[IPL_BIO] = 0x00000000; + irqmasks[IPL_NET] = 0x00000000; + irqmasks[IPL_TTY] = 0x00000000; + irqmasks[IPL_CLOCK] = 0x00000000; + irqmasks[IPL_IMP] = 0x00000000; + irqmasks[IPL_NONE] = 0x00000000; + + current_mask = 0x00000000; + actual_mask = 0x00000000; + spl_mask = 0x00000000; + soft_interrupts = 0x00000000; + + set_spl_masks(); + + /* Enable IRQ's and FIQ's */ + + enable_interrupts(I32_bit | F32_bit); +} + + +/* + * int irq_claim(int irq, irqhandler_t *handler) + * + * Enable an IRQ and install a handler for it. + */ + +int +irq_claim(irq, handler) + int irq; + irqhandler_t *handler; +{ + +#ifdef DIAGNOSTIC + /* Sanity check */ + if (handler == NULL) + panic("NULL interrupt handler\n"); + if (handler->ih_func == NULL) + panic("Interrupt handler does not have a function\n"); +#endif /* DIAGNOSTIC */ + + /* + * IRQ_INSTRUCT indicates that we should get the irq number + * from the irq structure + */ + + if (irq == IRQ_INSTRUCT) + irq = handler->ih_num; + + /* Make sure the irq number is valid */ + + if (irq < 0 || irq >= NIRQS) + return(-1); + + /* Make sure the level is valid */ + + if (handler->ih_level < 0 || handler->ih_level >= IPL_LEVELS) + return(-1); + + /* Attach handler at top of chain */ + + handler->ih_next = irqhandlers[irq]; + irqhandlers[irq] = handler; + + /* + * Reset the flags for this handler. + * As the handler is now in the chain mark it as active. + */ + handler->ih_flags = 0 | IRQ_FLAG_ACTIVE; + + /* + * Record the interrupt number for accounting. + * Done here as the accounting number may not be the same as the IRQ number + * though for the moment they are + */ + + handler->ih_num = irq; + +#ifdef IRQSTATS + /* Get the interrupt name from the head of the list */ + + if (handler->ih_name) { + char *ptr = _intrnames + (irq * 14); + strcpy(ptr, " "); + strncpy(ptr, handler->ih_name, + min(strlen(handler->ih_name), 13)); + } else { + char *ptr = _intrnames + (irq * 14); + sprintf(ptr, "irq %2d ", irq); + } +#endif /* IRQSTATS */ + + irq_calculatemasks(); + + enable_irq(irq); + set_spl_masks(); + return(0); +} + + +/* + * int irq_release(int irq, irqhandler_t *handler) + * + * Disable an IRQ and remove a handler for it. + */ + +int +irq_release(irq, handler) + int irq; + irqhandler_t *handler; +{ + irqhandler_t *irqhand; + irqhandler_t **prehand; + extern char *_intrnames; + + /* + * IRQ_INSTRUCT indicates that we should get the irq number + * from the irq structure + */ + + if (irq == IRQ_INSTRUCT) + irq = handler->ih_num; + + /* Make sure the irq number is valid */ + + if (irq < 0 || irq >= NIRQS) + return(-1); + + + /* Locate the handler */ + + irqhand = irqhandlers[irq]; + prehand = &irqhandlers[irq]; + + while (irqhand && handler != irqhand) { + prehand = &irqhand; + irqhand = irqhand->ih_next; + } + + /* Remove the handler if located */ + + if (irqhand) + *prehand = irqhand->ih_next; + else + return(-1); + + + /* Now the handler has been removed from the chain mark is as inactive */ + + irqhand->ih_flags &= ~IRQ_FLAG_ACTIVE; + + /* Make sure the head of the handler list is active */ + + if (irqhandlers[irq]) + irqhandlers[irq]->ih_flags |= IRQ_FLAG_ACTIVE; + +#ifdef IRQSTATS + /* Get the interrupt name from the head of the list */ + if (irqhandlers[irq] && irqhandlers[irq]->ih_name) { + char *ptr = _intrnames + (irq * 14); + strcpy(ptr, " "); + strncpy(ptr, irqhandlers[irq]->ih_name, + min(strlen(irqhandlers[irq]->ih_name), 13)); + } else { + char *ptr = _intrnames + (irq * 14); + sprintf(ptr, "irq %2d ", irq); + } +#endif /* IRQSTATS */ + + irq_calculatemasks(); + + /* + * Disable the appropriate mask bit if there are no handlers left for + * this IRQ. + */ + + if (irqhandlers[irq] == NULL) + disable_irq(irq); + + set_spl_masks(); + + return(0); +} + +/* adapted from .../i386/isa/isa_machdep.c */ +/* + * Recalculate the interrupt masks from scratch. + * We could code special registry and deregistry versions of this function that + * would be faster, but the code would be nastier, and we don't expect this to + * happen very much anyway. + */ +void +irq_calculatemasks() +{ + int irq, level; + irqhandler_t *ptr; + int irqlevel[NIRQS]; + + /* First, figure out which levels each IRQ uses. */ + for (irq = 0; irq < NIRQS; irq++) { + register int levels = 0; + for (ptr = irqhandlers[irq]; ptr; ptr = ptr->ih_next) + levels |= 1 << ptr->ih_level; + irqlevel[irq] = levels; + } + + /* Then figure out which IRQs use each level. */ + for (level = 0; level < IPL_LEVELS; level++) { + register int irqs = 0; + for (irq = 0; irq < NIRQS; irq++) + if (irqlevel[irq] & (1 << level)) + irqs |= 1 << irq; + irqmasks[level] = ~(irqs | IRQMASK_ALLSOFT); + } + + /* + * Since run queues may be manipulated by both the statclock and tty, + * network, and disk drivers, statclock > (tty | net | bio). + */ + irqmasks[IPL_CLOCK] &= (irqmasks[IPL_TTY] & irqmasks[IPL_NET] & + irqmasks[IPL_BIO]); + + /* + * There are tty, network and disk drivers that use free() at interrupt + * time, so imp > (tty | net | bio). + */ + irqmasks[IPL_IMP] &= (irqmasks[IPL_TTY] & irqmasks[IPL_NET] & + irqmasks[IPL_BIO]); + + /* + * Enforce a hierarchy that gives slow devices a better chance at not + * dropping data. + */ + irqmasks[IPL_TTY] &= irqmasks[IPL_NET] & irqmasks[IPL_BIO]; + irqmasks[IPL_NET] &= irqmasks[IPL_BIO]; + +#include "sl.h" +#include "ppp.h" +#if NSL > 0 || NPPP > 0 +/* In the presence of SLIP or PPP, splimp > spltty. */ + irqmasks[IPL_NET] &= irqmasks[IPL_TTY]; +#endif + + /* + * We now need to update the irqblock array. This array indicates + * what other interrupts should be blocked when interrupt is asserted + * This basically emulates hardware interrupt priorities e.g. by blocking + * all other IPL_BIO interrupts with an IPL_BIO interrupt is asserted. + * For each interrupt we find the highest IPL and set the block mask to + * the interrupt mask for that level. + */ + + /* And eventually calculate the complete masks. */ + for (irq = 0; irq < NIRQS; irq++) { + register int irqs = 1 << irq; + for (ptr = irqhandlers[irq]; ptr; ptr = ptr->ih_next) + irqs |= ~(irqmasks[ptr->ih_level]); + irqblock[irq] = irqs | IRQMASK_ALLSOFT; + } +} + + +void * +intr_claim(irq, level, name, ih_func, ih_arg) + int irq; + int level; + const char *name; + int (*ih_func) __P((void *)); + void *ih_arg; +{ + irqhandler_t *ih; + + ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT); + if (!ih) + panic("intr_claim(): Cannot malloc handler memory\n"); + + ih->ih_level = level; + ih->ih_name = name; + ih->ih_func = ih_func; + ih->ih_arg = ih_arg; + ih->ih_flags = 0; + + if (irq_claim(irq, ih) != 0) + return(NULL); + return(ih); +} + +int +intr_release(arg) + void *arg; +{ + irqhandler_t *ih = (irqhandler_t *)arg; + + if (irq_release(ih->ih_num, ih) == 0) { + free(ih, M_DEVBUF); + return(0); + } + return(1); +} + + +/* + * void disable_irq(int irq) + * + * Disables a specific irq. The irq is removed from the master irq mask + */ + +void +disable_irq(irq) + int irq; +{ + register int oldirqstate; + + oldirqstate = disable_interrupts(I32_bit); + current_mask &= ~(1 << irq); + irq_setmasks(); + restore_interrupts(oldirqstate); +} + + +/* + * void enable_irq(int irq) + * + * Enables a specific irq. The irq is added to the master irq mask + * This routine should be used with caution. A handler should already + * be installed. + */ + +void +enable_irq(irq) + int irq; +{ + register u_int oldirqstate; + + oldirqstate = disable_interrupts(I32_bit); + + current_mask |= (1 << irq); + irq_setmasks(); + restore_interrupts(oldirqstate); +} + + +/* + * void stray_irqhandler(u_int mask) + * + * Handler for stray interrupts. This gets called if a handler cannot be + * found for an interrupt. + */ + +void +stray_irqhandler(mask) + u_int mask; +{ + static u_int stray_irqs = 0; + + if (++stray_irqs <= 8) + log(LOG_ERR, "Stray interrupt %08x%s\n", mask, + stray_irqs >= 8 ? ": stopped logging" : ""); +} + + +/* + * int fiq_claim(fiqhandler_t *handler) + * + * Claim FIQ's and install a handler for them. + */ + +int +fiq_claim(handler) + fiqhandler_t *handler; +{ + /* Fail if the FIQ's are already claimed */ + + if (fiqhandlers) + return(-1); + + if (handler->fh_size > 0xc0) + return(-1); + + /* Install the handler */ + + fiqhandlers = handler; + + /* Now we have to actually install the FIQ handler */ + + /* Eventually we will copy this down but for the moment ... */ + + zero_page_readwrite(); + + WriteWord(0x0000003c, (u_int) handler->fh_func); + + zero_page_readonly(); + cpu_cache_syncI(); + + /* We must now set up the FIQ registers */ + + fiq_setregs(handler); + + /* Make sure that the FIQ's are enabled */ + + enable_interrupts(F32_bit); + + return(0); +} + + +/* + * int fiq_release(fiqhandler_t *handler) + * + * Release FIQ's and remove a handler for them. + */ + +int +fiq_release(handler) + fiqhandler_t *handler; +{ + /* Fail if the handler is wrong */ + + if (fiqhandlers != handler) + return(-1); + + /* Disable FIQ interrupts */ + + disable_interrupts(F32_bit); + + /* Retrieve the FIQ registers */ + + fiq_getregs(handler); + + /* Remove the handler */ + + fiqhandlers = NULL; + return(0); +} + +/* End of irqhandler.c */ diff --git a/sys/arch/arm32/isa/isa_machdep.h b/sys/arch/arm32/isa/isa_machdep.h new file mode 100644 index 000000000000..dd1ada901d6a --- /dev/null +++ b/sys/arch/arm32/isa/isa_machdep.h @@ -0,0 +1,65 @@ +/* $NetBSD: isa_machdep.h,v 1.1 1998/06/08 17:49:44 tv Exp $ */ + +#ifndef _ARM32_ISA_MACHDEP_H_ +#define _ARM32_ISA_MACHDEP_H_ + +/* + * Types provided to machine-independent ISA code. + */ +typedef void *isa_chipset_tag_t; + +struct device; /* XXX */ +struct isabus_attach_args; /* XXX */ + +/* + * ISA DMA bounce buffers. + * XXX should be made partially machine- and bus-mapping-independent. + * + * DMA_BOUNCE is the number of pages of low-addressed physical memory + * to acquire for ISA bounce buffers. + * + * isaphysmem is the location of those bounce buffers. (They are currently + * assumed to be contiguous. + */ + +#ifndef DMA_BOUNCE +#define DMA_BOUNCE 8 /* one buffer per channel */ +#endif + +#define DMA_LARGE_BUFFER_SIZE 65536 /* 64K */ + +extern vm_offset_t isaphysmem; + +/* + * Functions provided to machine-independent ISA code. + */ +void isa_attach_hook __P((struct device *, struct device *, + struct isabus_attach_args *)); +void *isa_intr_establish __P((isa_chipset_tag_t ic, int irq, int type, + int level, int (*ih_fun)(void *), void *ih_arg)); +void isa_intr_disestablish __P((isa_chipset_tag_t ic, void *handler)); + +/* + * ALL OF THE FOLLOWING ARE MACHINE-DEPENDENT, AND SHOULD NOT BE USED + * BY PORTABLE CODE. + */ + +/* bus space tags */ +extern struct bus_space isa_io_bs_tag; +extern struct bus_space isa_mem_bs_tag; + +/* for pccons.c */ +#define MONO_BASE 0x3B4 +#define MONO_BUF 0x000B0000 +#define CGA_BASE 0x3D4 +#define CGA_BUF 0x000B8000 +#define VGA_BUF 0xA0000 +#define VGA_BUF_LEN (0xBFFFF - 0xA0000) + + +void isa_init __P((vm_offset_t, vm_offset_t)); +void isa_io_init __P((vm_offset_t, vm_offset_t)); +vm_offset_t isa_io_data_vaddr __P((void)); +vm_offset_t isa_mem_data_vaddr __P((void)); + +#endif /* _ARM32_ISA_MACHDEP_H_ XXX */ diff --git a/sys/arch/arm32/isa/isadma.c b/sys/arch/arm32/isa/isadma.c new file mode 100644 index 000000000000..2a43e9e32869 --- /dev/null +++ b/sys/arch/arm32/isa/isadma.c @@ -0,0 +1,432 @@ +/* $NetBSD: isadma.c,v 1.1 1998/06/08 17:49:44 tv Exp $ */ + +/* + * Copyright 1997 + * Digital Equipment Corporation. All rights reserved. + * + * This software is furnished under license and may be used and + * copied only in accordance with the following terms and conditions. + * Subject to these conditions, you may download, copy, install, + * use, modify and distribute this software in source and/or binary + * form. No title or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce + * and retain this copyright notice and list of conditions as + * they appear in the source file. + * + * 2) No right is granted to use any trade name, trademark, or logo of + * Digital Equipment Corporation. Neither the "Digital Equipment + * Corporation" name nor any trademark or logo of Digital Equipment + * Corporation may be used to endorse or promote products derived + * from this software without the prior written permission of + * Digital Equipment Corporation. + * + * 3) This software is provided "AS-IS" and any express or implied + * warranties, including but not limited to, any implied warranties + * of merchantability, fitness for a particular purpose, or + * non-infringement are disclaimed. In no event shall DIGITAL be + * liable for any damages whatsoever, and in particular, DIGITAL + * shall not be liable for special, indirect, consequential, or + * incidental damages or damages for lost profits, loss of + * revenue or loss of use, whether such damages arise in contract, + * negligence, tort, under statute, in equity, at law or otherwise, + * even if advised of the possibility of such damage. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +/* region of physical memory known to be contiguous */ +vm_offset_t isaphysmem; +static caddr_t dma_bounce[8]; /* XXX */ +static char bounced[8]; /* XXX */ +#define MAXDMASZ 512 /* XXX */ +static u_int8_t dma_finished; +static u_int8_t dma_buffer_used=0; + +static unsigned isa_dma_masks = 0xFF; +static int isa_dma_frozen = 0; +#define ISA_DMA_MASK_SET(CHAN) isa_dma_masks |= (1 << (CHAN)) +#define ISA_DMA_MASK_CLR(CHAN) isa_dma_masks &= ~(1 << (CHAN)) + +/* high byte of address is stored in this port for i-th dma channel */ +static int dmapageport[2][4] = { + {0x87, 0x83, 0x81, 0x82}, + {0x8f, 0x8b, 0x89, 0x8a} +}; + +static u_int8_t dmamode[4] = { + DMA37MD_READ | DMA37MD_SINGLE, + DMA37MD_WRITE | DMA37MD_SINGLE, + DMA37MD_READ | DMA37MD_LOOP, + DMA37MD_WRITE | DMA37MD_LOOP +}; + +int isa_dmarangecheck __P((vm_offset_t, u_long, int)); +caddr_t isa_allocphysmem __P((caddr_t, unsigned, void (*)(void))); +void isa_freephysmem __P((caddr_t, unsigned)); + +#ifdef SHARK +/* returns a pointer to the dma buffer if it is available, + * otherwise will return null. + */ +caddr_t isa_dmabuffer_get() +{ + caddr_t buffer; + int x; + + x = splhigh(); + + if (dma_buffer_used == 0) + { + dma_buffer_used = 1; + buffer = (caddr_t) isaphysmem; + } + else + { + /* no-can-do, already in use */ + buffer = (caddr_t) NULL; + } + + splx(x); + return buffer; +} + +/* relinquishes the dma buffer, this does no checks + */ +void isa_dmabuffer_finish() +{ + dma_buffer_used = 0; +} +#endif + +/* + * isa_dmacascade(): program 8237 DMA controller channel to accept + * external dma control by a board. + */ +void +isa_dmacascade(chan) + int chan; +{ + +#ifdef ISADMA_DEBUG + if (chan < 0 || chan > 7) + panic("isa_dmacascade: impossible request"); +#endif + + /* set dma channel mode, and set dma channel mode */ + if ((chan & 4) == 0) { + outb(IO_DMA1 + DMA1_MODE, chan | DMA37MD_CASCADE); + ISA_DMA_MASK_CLR(chan); + if (!isa_dma_frozen) + outb(IO_DMA1 + DMA1_SMSK, chan); + } else { + outb(IO_DMA2 + DMA2_MODE, (chan & 3) | DMA37MD_CASCADE); + ISA_DMA_MASK_CLR(chan); + if (!isa_dma_frozen) + outb(IO_DMA2 + DMA2_SMSK, (chan & 3)); + } +} + +/* + * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment + * problems by using a bounce buffer. + */ +void +isa_dmastart(flags, addr, nbytes, chan) + int flags; + caddr_t addr; + vm_size_t nbytes; + int chan; +{ + vm_offset_t phys; + int waport; + caddr_t newaddr; + +#ifdef ISADMA_DEBUG + if (chan < 0 || chan > 7 || + ((chan & 4) ? (nbytes >= (1<<17) || nbytes & 1 || (u_int)addr & 1) : + (nbytes >= (1<<16)))) + panic("isa_dmastart: impossible request"); +#endif + + if (isa_dmarangecheck((vm_offset_t) addr, nbytes, chan)) { + if (dma_bounce[chan] == 0) + dma_bounce[chan] = + /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/ + (caddr_t) isaphysmem + NBPG*chan; + bounced[chan] = 1; + newaddr = dma_bounce[chan]; + *(int *) newaddr = 0; /* XXX */ + /* copy bounce buffer on write */ + if ((flags & DMAMODE_READ) == 0) + bcopy(addr, newaddr, nbytes); + addr = newaddr; + } + + /* translate to physical */ + phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); + + dma_finished &= ~(1 << chan); + + if ((chan & 4) == 0) { + /* + * Program one of DMA channels 0..3. These are + * byte mode channels. + */ + /* set dma channel mode, and reset address ff */ + outb(IO_DMA1 + DMA1_MODE, chan | dmamode[flags]); + outb(IO_DMA1 + DMA1_FFC, 0); + + /* send start address */ + waport = IO_DMA1 + DMA1_CHN(chan); + outb(dmapageport[0][chan], phys>>16); + outb(waport, phys); + outb(waport, phys>>8); + + /* send count */ + outb(waport + 1, --nbytes); + outb(waport + 1, nbytes>>8); + + /* unmask channel */ + ISA_DMA_MASK_CLR(chan); + if (!isa_dma_frozen) + outb(IO_DMA1 + DMA1_SMSK, chan | DMA37SM_CLEAR); + } else { + /* + * Program one of DMA channels 4..7. These are + * word mode channels. + */ + /* set dma channel mode, and reset address ff */ + outb(IO_DMA2 + DMA2_MODE, (chan & 3) | dmamode[flags]); + outb(IO_DMA2 + DMA2_FFC, 0); + + /* send start address */ + waport = IO_DMA2 + DMA2_CHN(chan & 3); + outb(dmapageport[1][chan & 3], phys>>16); + phys >>= 1; + outb(waport, phys); + outb(waport, phys>>8); + + /* send count */ + nbytes >>= 1; + outb(waport + 2, --nbytes); + outb(waport + 2, nbytes>>8); + + /* unmask channel */ + ISA_DMA_MASK_CLR(chan); + if (!isa_dma_frozen) + outb(IO_DMA2 + DMA2_SMSK, (chan & 3) | DMA37SM_CLEAR); + } +} + +void +isa_dmaabort(chan) + int chan; +{ + +#ifdef ISADMA_DEBUG + if (chan < 0 || chan > 7) + panic("isa_dmaabort: impossible request"); +#endif + + bounced[chan] = 0; + + /* mask channel */ + ISA_DMA_MASK_SET(chan); + if ((chan & 4) == 0) + if (!isa_dma_frozen) + outb(IO_DMA1 + DMA1_SMSK, DMA37SM_SET | chan); + else + if (!isa_dma_frozen) + outb(IO_DMA2 + DMA2_SMSK, DMA37SM_SET | (chan & 3)); +} + +int +isa_dmafinished(chan) + int chan; +{ + +#ifdef ISADMA_DEBUG + if (chan < 0 || chan > 7) + panic("isa_dmafinished: impossible request"); +#endif + + /* check that the terminal count was reached */ + if ((chan & 4) == 0) + dma_finished |= inb(IO_DMA1 + DMA1_SR) & 0x0f; + else + dma_finished |= (inb(IO_DMA2 + DMA2_SR) & 0x0f) << 4; + + return ((dma_finished & (1 << chan)) != 0); +} + +void +isa_dmadone(flags, addr, nbytes, chan) + int flags; + caddr_t addr; + vm_size_t nbytes; + int chan; +{ + + +#ifdef ISADMA_DEBUG + if (chan < 0 || chan > 7) + panic("isa_dmadone: impossible request"); +#endif + + if (!isa_dmafinished(chan)) + printf("isa_dmadone: channel %d not finished\n", chan); + + /* mask channel */ + ISA_DMA_MASK_SET(chan); + if ((chan & 4) == 0) + if (!isa_dma_frozen) + outb(IO_DMA1 + DMA1_SMSK, DMA37SM_SET | chan); + else + if (!isa_dma_frozen) + outb(IO_DMA2 + DMA2_SMSK, DMA37SM_SET | (chan & 3)); + + /* copy bounce buffer on read */ + if (bounced[chan]) { + bcopy(dma_bounce[chan], addr, nbytes); + bounced[chan] = 0; + } +} + +/* + * Check for problems with the address range of a DMA transfer + * (non-contiguous physical pages, outside of bus address space, + * crossing DMA page boundaries). + * Return true if special handling needed. + */ +int +isa_dmarangecheck(va, length, chan) + vm_offset_t va; + u_long length; + int chan; +{ + vm_offset_t phys, priorpage = 0, endva; + u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); + + endva = round_page(va + length); + for (; va < endva ; va += NBPG) { + phys = trunc_page(pmap_extract(pmap_kernel(), va)); + if (phys == 0) + panic("isa_dmacheck: no physical page present"); +#ifdef ISA_MACHDEP_DMARANGECHECK + if (isa_machdep_dmarangecheck(phys, NBPG)) +#else + if (phys >= (1<<24)) +#endif + return 1; + if (priorpage) { + if (priorpage + NBPG != phys) + return 1; + /* check if crossing a DMA page boundary */ + if ((priorpage ^ phys) & dma_pgmsk) + return 1; + } + priorpage = phys; + } + return 0; +} + +/* head of queue waiting for physmem to become available */ +struct buf isa_physmemq; + +/* blocked waiting for resource to become free for exclusive use */ +static int isaphysmemflag; +/* if waited for and call requested when free (B_CALL) */ +static void (*isaphysmemunblock) __P((void)); /* needs to be a list */ + +/* + * Allocate contiguous physical memory for transfer, returning + * a *virtual* address to region. May block waiting for resource. + * (assumed to be called at splbio()) + */ +caddr_t +isa_allocphysmem(ca, length, func) + caddr_t ca; + unsigned length; + void (*func) __P((void)); +{ + + isaphysmemunblock = func; + while (isaphysmemflag & B_BUSY) { + isaphysmemflag |= B_WANTED; + sleep((caddr_t)&isaphysmemflag, PRIBIO); + } + isaphysmemflag |= B_BUSY; + + return((caddr_t)isaphysmem); +} + +/* + * Free contiguous physical memory used for transfer. + * (assumed to be called at splbio()) + */ +void +isa_freephysmem(va, length) + caddr_t va; + unsigned length; +{ + + isaphysmemflag &= ~B_BUSY; + if (isaphysmemflag & B_WANTED) { + isaphysmemflag &= B_WANTED; + wakeup((caddr_t)&isaphysmemflag); + if (isaphysmemunblock) + (*isaphysmemunblock)(); + } +} + +void +isa_dmafreeze() +{ + int x; + + x = splhigh(); + + if (!isa_dma_frozen) { + outb(IO_DMA1 + DMA1_MASK, 0xF); + outb(IO_DMA2 + DMA2_MASK, 0xF); + /* blam! */ + } + isa_dma_frozen++; + + splx(x); +} + +void +isa_dmathaw() +{ + int x; + + x = splhigh(); + + isa_dma_frozen--; + + if (isa_dma_frozen == 0) { + outb(IO_DMA1 + DMA1_MASK, isa_dma_masks & 0xF); + outb(IO_DMA2 + DMA2_MASK, (isa_dma_masks >> 4) & 0xF); + } else if (isa_dma_frozen < 0) { + isa_dma_frozen = 0; + } + + splx(x); +} diff --git a/sys/arch/arm32/isa/isadmavar.h b/sys/arch/arm32/isa/isadmavar.h new file mode 100644 index 000000000000..eb0dd53359af --- /dev/null +++ b/sys/arch/arm32/isa/isadmavar.h @@ -0,0 +1,18 @@ +/* $NetBSD: isadmavar.h,v 1.1 1998/06/08 17:49:44 tv Exp $ */ + +#define DMAMODE_WRITE 0 +#define DMAMODE_READ 1 +#define DMAMODE_LOOP 2 + +void isa_dmacascade __P((int)); +void isa_dmastart __P((int, caddr_t, vm_size_t, int)); +void isa_dmaabort __P((int)); +int isa_dmafinished __P((int)); +void isa_dmadone __P((int, caddr_t, vm_size_t, int)); + +#ifdef SHARK +caddr_t isa_dmabuffer_get __P(()); +void isa_dmabuffer_finish __P(()); +void isa_dmafreeze __P((void)); +void isa_dmathaw __P((void)); +#endif diff --git a/sys/arch/arm32/isa/nvram.h b/sys/arch/arm32/isa/nvram.h new file mode 100644 index 000000000000..fb38847bc127 --- /dev/null +++ b/sys/arch/arm32/isa/nvram.h @@ -0,0 +1,86 @@ +/* $NetBSD: nvram.h,v 1.1 1998/06/08 17:49:44 tv Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * 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. + * + * @(#)rtc.h 8.1 (Berkeley) 6/11/93 + */ + +/* + * The following information is found in the non-volatile RAM in the + * MC146818A (or DS1287A or other compatible) RTC on AT-compatible PCs. + */ + +/* NVRAM byte 0: bios diagnostic */ +#define NVRAM_DIAG (MC_NVRAM_START + 0) /* RTC offset 0xe */ + +#define NVRAM_DIAG_BITS "\020\010clock_battery\007ROM_cksum\006config_unit\005memory_size\004fixed_disk\003invalid_time" + +/* NVRAM byte 1: reset code */ +#define NVRAM_RESET (MC_NVRAM_START + 1) /* RTC offset 0xf */ + +#define NVRAM_RESET_RST 0x00 /* normal reset */ +#define NVRAM_RESET_LOAD 0x04 /* load system */ + +/* NVRAM byte 2: diskette drive type in upper/lower nibble */ +#define NVRAM_DISKETTE (MC_NVRAM_START + 2) /* RTC offset 0x10 */ + +#define NVRAM_DISKETTE_NONE 0 /* none present */ +#define NVRAM_DISKETTE_360K 0x10 /* 360K */ +#define NVRAM_DISKETTE_12M 0x20 /* 1.2M */ +#define NVRAM_DISKETTE_720K 0x30 /* 720K */ +#define NVRAM_DISKETTE_144M 0x40 /* 1.44M */ +#define NVRAM_DISKETTE_TYPE5 0x50 /* 2.88M, presumably */ +#define NVRAM_DISKETTE_TYPE6 0x60 /* 2.88M */ + +/* NVRAM byte 6: equipment type */ +#define NVRAM_EQUIPMENT (MC_NVRAM_START + 6) + +#define NVRAM_EQUIPMENT_EGAVGA 0x00 /* EGA or VGA */ +#define NVRAM_EQUIPMENT_COLOR40 0x10 /* 40 column color */ +#define NVRAM_EQUIPMENT_COLOR80 0x20 /* 80 column color */ +#define NVRAM_EQUIPMENT_MONO80 0x30 /* 80 column mono */ +#define NVRAM_EQUIPMENT_MONITOR 0x30 /* mask for monitor type */ + +/* NVRAM bytes 7 & 8: base memory size */ +#define NVRAM_BASELO (MC_NVRAM_START + 7) /* low byte; RTC off. 0x15 */ +#define NVRAM_BASEHI (MC_NVRAM_START + 8) /* high byte; RTC off. 0x16 */ + +/* NVRAM bytes 9 & 10: extended memory size */ +#define NVRAM_EXTLO (MC_NVRAM_START + 9) /* low byte; RTC off. 0x17 */ +#define NVRAM_EXTHI (MC_NVRAM_START + 10) /* high byte; RTC off. 0x18 */ + +/* NVRAM byte 36: current century. (please increment in Dec99!) */ +#define NVRAM_CENTURY (MC_NVRAM_START + 36) /* RTC offset 0x32 */ diff --git a/sys/arch/arm32/isa/spkrreg.h b/sys/arch/arm32/isa/spkrreg.h new file mode 100644 index 000000000000..426487088559 --- /dev/null +++ b/sys/arch/arm32/isa/spkrreg.h @@ -0,0 +1,11 @@ +/* $NetBSD: spkrreg.h,v 1.1 1998/06/08 17:49:44 tv Exp $ */ + +/* + * PIT port addresses and speaker control values + */ + +#define PITAUX_PORT 0x61 /* port of Programmable Peripheral Interface */ +#define PIT_ENABLETMR2 0x01 /* Enable timer/counter 2 */ +#define PIT_SPKRDATA 0x02 /* Direct to speaker */ + +#define PIT_SPKR (PIT_ENABLETMR2|PIT_SPKRDATA) diff --git a/sys/arch/arm32/isa/timerreg.h b/sys/arch/arm32/isa/timerreg.h new file mode 100644 index 000000000000..dd71f793a2b7 --- /dev/null +++ b/sys/arch/arm32/isa/timerreg.h @@ -0,0 +1,100 @@ +/* $NetBSD: timerreg.h,v 1.1 1998/06/08 17:49:45 tv Exp $ */ + +/*- + * Copyright (c) 1993 The Regents of the University of California. + * All rights reserved. + * + * 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. + */ + +/* + * Register definitions for the Intel 8253 Programmable Interval Timer. + * + * This chip has three independent 16-bit down counters that can be + * read on the fly. There are three mode registers and three countdown + * registers. The countdown registers are addressed directly, via the + * first three I/O ports. The three mode registers are accessed via + * the fourth I/O port, with two bits in the mode byte indicating the + * register. (Why are hardware interfaces always so braindead?). + * + * To write a value into the countdown register, the mode register + * is first programmed with a command indicating the which byte of + * the two byte register is to be modified. The three possibilities + * are load msb (TMR_MR_MSB), load lsb (TMR_MR_LSB), or load lsb then + * msb (TMR_MR_BOTH). + * + * To read the current value ("on the fly") from the countdown register, + * you write a "latch" command into the mode register, then read the stable + * value from the corresponding I/O port. For example, you write + * TMR_MR_LATCH into the corresponding mode register. Presumably, + * after doing this, a write operation to the I/O port would result + * in undefined behavior (but hopefully not fry the chip). + * Reading in this manner has no side effects. + * + * The outputs of the three timers are connected as follows: + * + * timer 0 -> irq 0 + * timer 1 -> dma chan 0 (for dram refresh) + * timer 2 -> speaker (via keyboard controller) + * + * Timer 0 is used to call hardclock. + * Timer 2 is used to generate console beeps. + */ + +/* + * Frequency of all three count-down timers; (TIMER_FREQ/freq) is the + * appropriate count to generate a frequency of freq hz. + */ +#ifndef TIMER_FREQ +#define TIMER_FREQ 1193182 +#endif +#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x)) + +/* + * Macros for specifying values to be written into a mode register. + */ +#define TIMER_CNTR0 (IO_TIMER1 + 0) /* timer 0 counter port */ +#define TIMER_CNTR1 (IO_TIMER1 + 1) /* timer 1 counter port */ +#define TIMER_CNTR2 (IO_TIMER1 + 2) /* timer 2 counter port */ +#define TIMER_MODE (IO_TIMER1 + 3) /* timer mode port */ +#define TIMER_SEL0 0x00 /* select counter 0 */ +#define TIMER_SEL1 0x40 /* select counter 1 */ +#define TIMER_SEL2 0x80 /* select counter 2 */ +#define TIMER_INTTC 0x00 /* mode 0, intr on terminal cnt */ +#define TIMER_ONESHOT 0x02 /* mode 1, one shot */ +#define TIMER_RATEGEN 0x04 /* mode 2, rate generator */ +#define TIMER_SQWAVE 0x06 /* mode 3, square wave */ +#define TIMER_SWSTROBE 0x08 /* mode 4, s/w triggered strobe */ +#define TIMER_HWSTROBE 0x0a /* mode 5, h/w triggered strobe */ +#define TIMER_LATCH 0x00 /* latch counter for reading */ +#define TIMER_LSB 0x10 /* r/w counter LSB */ +#define TIMER_MSB 0x20 /* r/w counter MSB */ +#define TIMER_16BIT 0x30 /* r/w counter 16 bits, LSB first */ +#define TIMER_BCD 0x01 /* count in BCD */ +