diff options
author | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 10:44:53 +0000 |
---|---|---|
committer | Theo de Raadt <deraadt@cvs.openbsd.org> | 1995-10-18 10:44:53 +0000 |
commit | 49235ceee0c25492d4ca35194d7c72838a9ec284 (patch) | |
tree | 14f77677934d4f4cb9baa428691ace792fd0deed /sys/arch/mvme68k/dev | |
parent | c9328c850e70436131e06a34f73c14cc230c18f9 (diff) |
mvme68k port by me. some parts by dale rahn.
Diffstat (limited to 'sys/arch/mvme68k/dev')
40 files changed, 17225 insertions, 0 deletions
diff --git a/sys/arch/mvme68k/dev/bug.c b/sys/arch/mvme68k/dev/bug.c new file mode 100644 index 00000000000..808cf6d4413 --- /dev/null +++ b/sys/arch/mvme68k/dev/bug.c @@ -0,0 +1,248 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Dale Rahn. + * 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 Dale Rahn. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * bug routines -- assumes that the necessary sections of memory + * are preserved. + */ +#include <sys/param.h> +#include <machine/prom.h> + +/* flag to traphandler to signify prom call. presumes splhigh() */ +extern volatile int promcall; + +/* tty routines */ +char +bug_inchr() +{ + int s = splhigh(); + char a; + + promcall = 1; + asm volatile ("subql #2,sp"); + MVMEPROM_CALL(MVMEPROM_INCHR); + asm volatile ("nop"); + asm volatile ("movb sp@+,%0" : "=d" (a)); + promcall = 0; + splx(s); + return (a); +} + +/* returns 0 if no characters ready to read */ +int +bug_instat() +{ + int s = splhigh(); + short ret; + + promcall = 1; + MVMEPROM_CALL(MVMEPROM_INSTAT); + asm volatile ("nop"); + asm volatile ("movw ccr,%0" : "=d" (ret)); + promcall = 0; + splx(s); + return (!(ret & 0x4)); +} + +void +bug_outchr(a) + char a; +{ + int s = splhigh(); + + promcall = 1; + asm volatile ("movb %0,sp@-" :: "d" (a)); + MVMEPROM_CALL(MVMEPROM_OUTCHR); + asm volatile ("nop"); + promcall = 0; + splx(s); +} + +void +bug_outstr(pstrb, pstre) + char *pstrb; + char *pstre; +{ + int s = splhigh(); + + promcall = 1; + asm volatile ("movl %0,sp@-" :: "d" (pstre)); + asm volatile ("movl %0,sp@-" :: "d" (pstrb)); + MVMEPROM_CALL(MVMEPROM_OUTSTR); + promcall = 0; + splx(s); +} + +void +bug_outln(pstrb, pstre) + char *pstrb; + char *pstre; +{ + int s = splhigh(); + + promcall = 1; + asm volatile ("movl %0,sp@-" :: "d" (pstre)); + asm volatile ("movl %0,sp@-" :: "d" (pstrb)); + MVMEPROM_CALL(MVMEPROM_OUTSTRCRLF); + promcall = 0; + splx(s); +} + +/* BUG - disk routines */ + +#if 0 +/* returns 0: success, nonzero: error */ +u_int bug_drdcnt = 0; +int +bug_diskrd(arg) + bug_dskio *arg; +{ + int ret; + + promcall = 1; + bug_drdcnt++; + asm volatile ("movl %0, sp@-" :: "d" (arg)); + MVMEPROM_CALL(MVMEPROM_DSKRD); + asm volatile ("nop"); + asm volatile ("movw ccr,%0" : "=d" (ret)); + promcall = 0; + return (!(ret & 0x4)); +} +/* returns 0: success, nonzero: error */ +u_int bug_dwrcnt = 0; +int +bug_diskwr(arg) + bug_dskio *arg; +{ + int ret; + + promcall = 1; + bug_dwrcnt ++; + asm volatile ("movl %0, sp@-" :: "d" (arg)); + MVMEPROM_CALL(MVMEPROM_DSKWR); + asm volatile ("nop"); + asm volatile ("movw ccr,%0" : "=d" (ret)); + promcall = 0; + return (!(ret & 0x4)); +} + +bug_diskcfig() +{ +} + +bug_diskfmt() +{ +} + +bug_diskctrl() +{ +} +#endif + +/* BUG - timing routine */ +void +bug_delay(delay_msec) + int delay_msec; +{ + int s = splhigh(); + + promcall = 1; + asm volatile ("movl %0,sp@-" :: "d" (delay_msec)); + MVMEPROM_CALL(MVMEPROM_DELAY); + asm volatile ("nop"); + promcall = 0; + splx(s); +} + +/* BUG - return to bug routine */ +void +bug_return() +{ + promcall = 1; + MVMEPROM_CALL(MVMEPROM_EXIT); + promcall = 0; + /*NOTREACHED*/ +} + +/* BUG - query board routines */ +struct bug_brdid * +bug_brdid() +{ + struct bug_brdid *pbrd_id; + + promcall = 1; + asm volatile ("clrl sp@-"); + MVMEPROM_CALL(MVMEPROM_GETBRDID); + asm volatile ("movl sp@+,%0" : "=d" (pbrd_id):); + promcall = 0; + return (pbrd_id); +} + +void +bug_rtc_rd(ptime) + struct bug_time *ptime; +{ + promcall = 1; + asm volatile ("movl %0,sp@-" :: "a" (ptime)); + MVMEPROM_CALL(MVMEPROM_RTC_RD); + asm volatile ("nop"); + promcall = 0; +} + +int asm_callbuf[4]; + +void +bug_stat() +{ + char val[] = "|/-\\"; + static int cnt = 0; + + bug_outchr('\b'); + bug_outchr(val[cnt]); + cnt = (cnt + 1) % (sizeof(val) -1); +} + +void +asm_bug_stat() +{ + asm volatile ("movl a0,_asm_callbuf+0"); + asm volatile ("movl a1,_asm_callbuf+4"); + asm volatile ("movl d0,_asm_callbuf+8"); + asm volatile ("movl d1,_asm_callbuf+12"); + + bug_stat(); + + asm volatile ("movl _asm_callbuf+0,a0"); + asm volatile ("movl _asm_callbuf+4,a1"); + asm volatile ("movl _asm_callbuf+8,d0"); + asm volatile ("movl _asm_callbuf+12,d1"); +} diff --git a/sys/arch/mvme68k/dev/bugtty.c b/sys/arch/mvme68k/dev/bugtty.c new file mode 100644 index 00000000000..367db8f804b --- /dev/null +++ b/sys/arch/mvme68k/dev/bugtty.c @@ -0,0 +1,496 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Dale Rahn. + * 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 Dale Rahn. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/ioctl.h> +#include <sys/device.h> +#include <sys/tty.h> +#include <sys/proc.h> +#include <sys/conf.h> +#include <sys/uio.h> +#include <sys/queue.h> +#include <dev/cons.h> + +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <machine/prom.h> + +#include "bugtty.h" + +int bugttymatch __P((struct device *parent, void *self, void *aux)); +void bugttyattach __P((struct device *parent, struct device *self, void *aux)); + +struct cfdriver bugttycd = { + NULL, "bugtty", bugttymatch, bugttyattach, + DV_TTY, sizeof(struct device) +}; + +/* prototypes */ +int bugttycnprobe __P((struct consdev *cp)); +int bugttycninit __P((struct consdev *cp)); +int bugttycngetc __P((dev_t dev)); +int bugttycnputc __P((dev_t dev, char c)); + +int bugttyopen __P((dev_t dev, int flag, int mode, struct proc *p)); +int bugttyclose __P((dev_t dev, int flag, int mode, struct proc *p)); +int bugttyread __P((dev_t dev, struct uio *uio, int flag)); +int bugttywrite __P((dev_t dev, struct uio *uio, int flag)); +int bugttyioctl __P((dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)); +int bugttystop __P((struct tty *tp, int flag)); + +#define DIALOUT(x) ((x) & 0x80) +#define SWFLAGS(dev) (bugttyswflags | (DIALOUT(dev) ? TIOCFLAG_SOFTCAR : 0)) +#define BUGTTYUNIT(x) ((x) & 0x7f) + +#define BUGBUF 80 +char bugtty_ibuffer[BUGBUF+1]; +volatile char *pinchar = bugtty_ibuffer; +char bug_obuffer[BUGBUF+1]; + +struct tty *bugtty_tty[NBUGTTY]; + +struct tty * +bugttytty(dev) + dev_t dev; +{ + int unit; + + unit = BUGTTYUNIT(dev); + if (unit >= NBUGTTY) + return (NULL); + return (bugtty_tty[unit]); +} + +int +bugttymatch(parent, self, aux) + struct device *parent; + void *self; + void *aux; +{ + extern int needprom; + + if (needprom == 0) + return (0); + return (1); +} + +void +bugttyattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + printf("\n"); +} + +void bugttyoutput __P((struct tty *tp)); + +int bugttydefaultrate = TTYDEF_SPEED; +int bugttyswflags; + +int +bugttymctl(dev, bits, how) + dev_t dev; + int bits, how; +{ + static int settings = TIOCM_DTR | TIOCM_RTS | + TIOCM_CTS | TIOCM_CD | TIOCM_DSR; + int s; + + /*printf("mctl: dev %x, bits %x, how %x,",dev, bits, how);*/ + + /* settings are currently ignored */ + s = spltty(); + switch (how) { + case DMSET: + break; + case DMBIC: + break; + case DMBIS: + break; + case DMGET: + break; + } + (void)splx(s); + + bits = 0; + /* proper defaults? */ + bits |= TIOCM_DTR; + bits |= TIOCM_RTS; + bits |= TIOCM_CTS; + bits |= TIOCM_CD; + /* bits |= TIOCM_RI; */ + bits |= TIOCM_DSR; + + /* printf("retbits %x\n", bits); */ + return (bits); +} + +int +bugttyopen(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + int s, unit = BUGTTYUNIT(dev); + struct tty *tp; + extern int needprom; + + if (needprom == 0) + return (ENODEV); + + s = spltty(); + if (bugtty_tty[unit]) { + tp = bugtty_tty[unit]; + } else { + tp = bugtty_tty[unit] = ttymalloc(); + } + tp->t_oproc = bugttyoutput; + tp->t_param = NULL; + tp->t_dev = dev; + + if ((tp->t_state & TS_ISOPEN) == 0) { + tp->t_state |= TS_WOPEN; + ttychars(tp); + if (tp->t_ispeed == 0) { + /* + * only when cleared do we reset to defaults. + */ + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_cflag = TTYDEF_CFLAG; + tp->t_lflag = TTYDEF_LFLAG; + tp->t_ispeed = tp->t_ospeed = bugttydefaultrate; + } + /* bugtty does not have carrier */ + tp->t_cflag |= CLOCAL; + /* + * do these all the time + */ + if (bugttyswflags & TIOCFLAG_CLOCAL) + tp->t_cflag |= CLOCAL; + if (bugttyswflags & TIOCFLAG_CRTSCTS) + tp->t_cflag |= CRTSCTS; + if (bugttyswflags & TIOCFLAG_MDMBUF) + tp->t_cflag |= MDMBUF; + bugttyparam(tp, &tp->t_termios); + ttsetwater(tp); + + (void)bugttymctl(dev, TIOCM_DTR | TIOCM_RTS, DMSET); + /* + if ((SWFLAGS(dev) & TIOCFLAG_SOFTCAR) || + (bugttymctl(dev, 0, DMGET) & TIOCM_CD)) + tp->t_state |= TS_CARR_ON; + else + tp->t_state &= ~TS_CARR_ON; + */ + tp->t_state |= TS_CARR_ON; + } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { + splx(s); + return (EBUSY); + } + + /* + * if NONBLOCK requested, ignore carrier + */ +/* + if (flag & O_NONBLOCK) + goto done; +*/ + + splx(s); + /* + * Reset the tty pointer, as there could have been a dialout + * use of the tty with a dialin open waiting. + */ + tp->t_dev = dev; + return ((*linesw[tp->t_line].l_open)(dev, tp)); +} + +int +bugttyparam() +{ + return (0); +} + +void +bugttyoutput(tp) + struct tty *tp; +{ + int cc, s, unit, cnt ; + + /* only supports one unit */ + + if ((tp->t_state & TS_ISOPEN) == 0) + return; + + s = spltty(); + cc = tp->t_outq.c_cc; + while (cc > 0) { + cnt = min(BUGBUF, cc); + cnt = q_to_b(&tp->t_outq, bug_obuffer, cnt); + bug_outstr(bug_obuffer, &bug_obuffer[cnt]); + cc -= cnt; + } + splx(s); +} + +int +bugttyclose(dev, flag, mode, p) + dev_t dev; + int flag, mode; + struct proc *p; +{ + int unit = BUGTTYUNIT(dev); + struct tty *tp = bugtty_tty[unit]; + + (*linesw[tp->t_line].l_close)(tp, flag); + + ttyclose(tp); +#if 0 + bugtty_tty[unit] = NULL; +#endif + return (0); +} + +int +bugttyread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct tty *tp; + + if ((tp = bugtty_tty[BUGTTYUNIT(dev)]) == NULL) + return (ENXIO); + return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); +} + +#if 1 +/* only to be called at splclk() */ +bugtty_chkinput() +{ + struct tty *tp; + + tp = bugtty_tty[0]; /* Kinda ugly hack */ + if (tp == NULL ) + return; + + if (bug_instat()) { + while (bug_instat()) { + u_char c = bug_inchr() & 0xff; + (*linesw[tp->t_line].l_rint)(c, tp); + } + /* + wakeup(tp); + */ + } +} +#endif + +int +bugttywrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ +#if 0 + /* bypass tty output routines. */ + int i, cnt, s; + int oldoff; + + s = spltty(); + oldoff = uio->uio_offset; + do { + uiomove(bug_obuffer, BUGBUF, uio); + bug_outstr(bug_obuffer, &bug_obuffer[uio->uio_offset - oldoff]); + oldoff = uio->uio_offset; + } while (uio->uio_resid != 0); + splx(s); + + return (0); +#else + struct tty *tp; + if((tp = bugtty_tty[BUGTTYUNIT(dev)]) == NULL) + return (ENXIO); + return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); +#endif +} + +int +bugttyioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + int unit = BUGTTYUNIT(dev); + struct tty *tp = bugtty_tty[unit]; + int error; + + if (!tp) + return (ENXIO); + + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + error = ttioctl(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + switch (cmd) { + case TIOCSBRK: + /* */ + break; + + case TIOCCBRK: + /* */ + break; + + case TIOCSDTR: + (void) bugttymctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIS); + break; + + case TIOCCDTR: + (void) bugttymctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIC); + break; + + case TIOCMSET: + (void) bugttymctl(dev, *(int *) data, DMSET); + break; + + case TIOCMBIS: + (void) bugttymctl(dev, *(int *) data, DMBIS); + break; + + case TIOCMBIC: + (void) bugttymctl(dev, *(int *) data, DMBIC); + break; + + case TIOCMGET: + *(int *)data = bugttymctl(dev, 0, DMGET); + break; + case TIOCGFLAGS: + *(int *)data = SWFLAGS(dev); + break; + case TIOCSFLAGS: + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) + return (EPERM); + + bugttyswflags = *(int *)data; + bugttyswflags &= /* only allow valid flags */ + (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | TIOCFLAG_CRTSCTS); + break; + default: + return (ENOTTY); + } + + return (0); +} + +int +bugttystop(tp, flag) + struct tty *tp; + int flag; +{ + int s; + + s = spltty(); + if (tp->t_state & TS_BUSY) { + if ((tp->t_state & TS_TTSTOP) == 0) + tp->t_state |= TS_FLUSH; + } + splx(s); + return (0); +} + +/* + * bugtty is the last possible choice for a console device. + */ +int +bugttycnprobe(cp) + struct consdev *cp; +{ + int maj; + extern int needprom; + + if (needprom == 0) { + cp->cn_pri = CN_DEAD; + return (0); + } + + switch (cputyp) { + case CPU_147: + case CPU_162: + cp->cn_pri = CN_NORMAL; + return (0); + default: + break; + } + + /* locate the major number */ + for (maj = 0; maj < nchrdev; maj++) + if (cdevsw[maj].d_open == bugttyopen) + break; + + cp->cn_dev = makedev(maj, 0); + cp->cn_pri = CN_NORMAL; + + return (1); +} + +int +bugttycninit(cp) + struct consdev *cp; +{ +} + +int +bugttycngetc(dev) + dev_t dev; +{ + return (bug_inchr()); +} + +int +bugttycnputc(dev, c) + dev_t dev; + char c; +{ + if (c == '\n') + bug_outchr('\r'); + bug_outchr(c); +} diff --git a/sys/arch/mvme68k/dev/cd2400reg.h b/sys/arch/mvme68k/dev/cd2400reg.h new file mode 100644 index 00000000000..d42fe00c7b2 --- /dev/null +++ b/sys/arch/mvme68k/dev/cd2400reg.h @@ -0,0 +1,151 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Dale Rahn. 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 Dale Rahn. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Memory map for CL-CD2400 (CD2401) + * NOTE: intel addresses are different than motorola addresss + * do we want both here (really is based on endian) + * or do we want to put these in a carefully packed structure? + */ + +/* these are mot addresses */ +/* global registers */ +#define CD2400_GFRCR 0x81 +#define CD2400_CAR 0xee + +/* option registers */ +#define CD2400_CMR 0x1b +#define CD2400_COR1 0x10 +#define CD2400_COR2 0x17 +#define CD2400_COR3 0x16 +#define CD2400_COR4 0x15 +#define CD2400_COR5 0x14 +#define CD2400_COR6 0x18 +#define CD2400_COR7 0x07 +#define CD2400_SCHR1 0x1f /* async */ +#define CD2400_SCHR2 0x1e /* async */ +#define CD2400_SCHR3 0x1d /* async */ +#define CD2400_SCHR4 0x1c /* async */ +#define CD2400_SCRl 0x23 /* async */ +#define CD2400_SCRh 0x22 /* async */ +#define CD2400_LNXT 0x2e +#define CD2400_RFAR1 0x1f /* sync */ +#define CD2400_RFAR2 0x1e /* sync */ +#define CD2400_RFAR3 0x1d /* sync */ +#define CD2400_RFAR4 0x1c /* sync */ + +#define CD2400_CPSR 0xd6 + +/* bit rate and clock option registers */ +#define CD2400_RBPR 0xcb +#define CD2400_RCOR 0xc8 +#define CD2400_TBPR 0xc3 +#define CD2400_TCOR 0xc0 + +/* channel command and status registers */ +#define CD2400_CCR 0x13 +#define CD2400_STCR 0x12 /* sync */ +#define CD2400_CSR 0x1a +#define CD2400_MSVR_RTS 0xde +#define CD2400_MSVR_DTR 0xdf + +/* interrupt registers */ +#define CD2400_LIVR 0x09 +#define CD2400_IER 0x11 +#define CD2400_LICR 0x26 +#define CD2400_STK 0xe2 + +/* receive interrupt registers */ +#define CD2400_RPILR 0xe1 +#define CD2400_RIR 0xeD +#define CD2400_RISR 0x88 +#define CD2400_RISRl 0x89 +#define CD2400_RISRh 0x88 +#define CD2400_RFOC 0x30 +#define CD2400_RDR 0xf8 +#define CD2400_REOIR 0x84 + +/* transmit interrupt registers */ +#define CD2400_TPILR 0xe0 +#define CD2400_TIR 0xec +#define CD2400_TISR 0x8a +#define CD2400_TFTC 0x80 +#define CD2400_TDR 0xf8 +#define CD2400_TEOIR 0x85 + +/* modem interrrupt registers */ +#define CD2400_MPILR 0xe3 +#define CD2400_MIR 0xef +#define CD2400_MISR 0x8B +#define CD2400_MEOIR 0x86 + +/* dma registers */ +#define CD2400_DMR 0xf6 +#define CD2400_BERCNT 0x8e +#define CD2400_DMABSTS 0x19 + +/* dma receive registers - leave these long names, as in manual */ +#define CD2400_ARBADRL 0x42 +#define CD2400_ARBADRU 0x40 +#define CD2400_BRBADRL 0x46 +#define CD2400_BRBADRU 0x44 +#define CD2400_ARBCNT 0x4a +#define CD2400_BRBCNT 0x48 +#define CD2400_ARBSTS 0x4f +#define CD2400_BRBSTS 0x4e +#define CD2400_RCBADRL 0x3e +#define CD2400_RCBADRU 0x3c + +/* dma transmit registers */ +#define CD2400_ATBADRL 0x52 +#define CD2400_ATBADRU 0x50 +#define CD2400_BTBADRL 0x56 +#define CD2400_BTBADRU 0x54 +#define CD2400_ATBCNT 0x5a +#define CD2400_BTBCNT 0x58 +#define CD2400_ATBSTS 0x5f +#define CD2400_BTBSTS 0x5e +#define CD2400_RTBADRL 0x3a +#define CD2400_RTBADRU 0x38 + +/* timer registers */ +#define CD2400_TPR 0xda +#define CD2400_RTPR 0x24 /* async */ +#define CD2400_RTPRl 0x25 /* async */ +#define CD2400_RTPRh 0x24 /* async */ +#define CD2400_GT1 0x2a /* sync */ +#define CD2400_GT1l 0x2b /* sync */ +#define CD2400_GT1h 0x2a /* sync */ +#define CD2400_GT2 0x29 /* sync */ +#define CD2400_TTR 0x29 /* async */ + + +#define CD2400_SIZE 0x200 diff --git a/sys/arch/mvme68k/dev/cl.c b/sys/arch/mvme68k/dev/cl.c new file mode 100644 index 00000000000..38f0469f53c --- /dev/null +++ b/sys/arch/mvme68k/dev/cl.c @@ -0,0 +1,1665 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Dale Rahn. 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 Dale Rahn. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <sys/param.h> +#include <sys/callout.h> +#include <sys/conf.h> +#include <sys/ioctl.h> +#include <sys/proc.h> +#include <sys/tty.h> +#include <sys/uio.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/device.h> +/* #include <sys/queue.h> */ +#include <machine/cpu.h> +#include <machine/autoconf.h> +#include <dev/cons.h> +#include <mvme68k/dev/cd2400reg.h> +#include <sys/syslog.h> +#include "cl.h" + +#include "pcctwo.h" + +#if NPCCTWO > 0 +#include <mvme68k/dev/pcctworeg.h> +#endif + +/* min timeout 0xa, what is a good value */ +#define CL_TIMEOUT 0x10 +#define CL_FIFO_MAX 0x10 +#define CL_FIFO_CNT 0xc +#define CL_RX_TIMEOUT 0x10 + +#define CL_DMAMODE 0x1 +#define CL_INTRMODE 0x0 + +struct cl_cons { + u_char *cl_paddr; + volatile u_char *cl_vaddr; + volatile struct pcctworeg *pcctwoaddr; + u_char channel; +} cl_cons; + +struct cl_info { + struct tty *tty; + u_char cl_swflags; + u_char cl_softchar; + u_char cl_consio; + u_char cl_speed; + u_char cl_parstop; /* parity, stop bits. */ + u_char cl_rxmode; + u_char cl_txmode; + u_char cl_clen; + u_char cl_parity; + u_char transmitting; + u_long txcnt; + u_long rxcnt; +}; + +#define CLCD_PORTS_PER_CHIP 4 +struct clsoftc { + struct device sc_dev; + struct evcnt sc_txintrcnt; + struct evcnt sc_rxintrcnt; + struct evcnt sc_mxintrcnt; + time_t sc_rotime; /* time of last ring overrun */ + time_t sc_fotime; /* time of last fifo overrun */ + volatile u_char *vbase; + struct cl_info sc_cl[CLCD_PORTS_PER_CHIP]; + struct intrhand sc_ih_e; + struct intrhand sc_ih_m; + struct intrhand sc_ih_t; + struct intrhand sc_ih_r; + struct pcctworeg *sc_pcctwo; + int sc_flags; +}; + +struct { + u_int speed; + u_char divisor; + u_char clock; + u_char rx_timeout; +} cl_clocks[] = { + { 64000, 0x26, 0, 0x01}, + { 56000, 0x2c, 0, 0x01}, + { 38400, 0x40, 0, 0x01}, + { 19200, 0x81, 0, 0x02}, + { 9600, 0x40, 1, 0x04}, + { 7200, 0x56, 1, 0x04}, + { 4800, 0x81, 1, 0x08}, + { 3600, 0xad, 1, 0x08}, + { 2400, 0x40, 2, 0x10}, + { 1200, 0x81, 2, 0x20}, + { 600, 0x40, 3, 0x40}, + { 300, 0x81, 3, 0x80}, + { 150, 0x40, 3, 0x80}, + { 110, 0x58, 4, 0xff}, + { 50, 0xC2, 4, 0xff}, + { 0, 0x00, 0, 0}, +}; + +/* prototypes */ +int clcnprobe __P((struct consdev *cp)); +int clcninit __P((struct consdev *cp)); +int clcngetc __P((dev_t dev)); +int clcnputc __P((dev_t dev, char c)); +u_char cl_clkdiv __P((int speed)); +u_char cl_clknum __P((int speed)); +u_char cl_clkrxtimeout __P((int speed)); +void clstart __P((struct tty *tp)); +void cl_unblock __P((struct tty *tp)); +int clccparam __P((struct clsoftc *sc, struct termios *par, int channel)); + +int clparam __P((struct tty *tp, struct termios *t)); +int cl_mintr __P((struct clsoftc *sc)); +int cl_txintr __P((struct clsoftc *sc)); +int cl_rxintr __P((struct clsoftc *sc)); +void cl_overflow __P((struct clsoftc *sc, int channel, long *ptime, char *msg)); +void cl_parity __P((struct clsoftc *sc, int channel)); +void cl_frame __P((struct clsoftc *sc, int channel)); +void cl_break __P(( struct clsoftc *sc, int channel)); +int clmctl __P((dev_t dev, int bits, int how)); +void cl_dumpport __P((int channel)); + +int clprobe __P((struct device *parent, void *self, void *aux)); +void clattach __P((struct device *parent, struct device *self, void *aux)); + +int clopen __P((dev_t dev, int flag, int mode, struct proc *p)); +int clclose __P((dev_t dev, int flag, int mode, struct proc *p)); +int clread __P((dev_t dev, struct uio *uio, int flag)); +int clwrite __P((dev_t dev, struct uio *uio, int flag)); +int clioctl __P((dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)); +int clstop __P((struct tty *tp, int flag)); + +static void cl_initchannel __P((struct clsoftc *sc, int channel)); +static void clputc __P((struct clsoftc *sc, int unit, char c)); +static u_char clgetc __P((struct clsoftc *sc, int *channel)); +static void cloutput __P((struct tty *tp)); + +struct cfdriver clcd = { + NULL, "cl", clprobe, clattach, DV_TTY, sizeof(struct clsoftc), 0 +}; + +#define CLCDBUF 80 +char cltty_ibuffer[CLCDBUF+1]; +char cl_obuffer[CLCDBUF+1]; + +int dopoll = 1; + +#define CL_UNIT(x) (minor(x) >> 2) +#define CL_CHANNEL(x) (minor(x) & 3) +#define CL_TTY(x) (minor(x)) + +extern int cputyp; + +struct tty * +cltty(dev) + dev_t dev; +{ + int unit, channel; + struct clsoftc *sc; + + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return (NULL); + } + channel = CL_CHANNEL(dev); + return (sc->sc_cl[channel].tty); +} + +/* + * probing onboard 166/167/187 CL-cd2400 + * should be previously configured, + * we can check the value before resetting the chip + */ +int +clprobe(parent, self, aux) + struct device *parent; + void *self; + void *aux; +{ + volatile u_char *cd_base; + struct cfdata *cf = self; + struct confargs *ca = aux; + int ret; + + if (cputyp != CPU_167 && cputyp != CPU_166) + return (0); + + cd_base = ca->ca_vaddr; +#if 0 + return (!badvaddr(&cd_base[CD2400_GFRCR], 1)); +#endif + return (ret); +} + +void +clattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct clsoftc *sc = (struct clsoftc *)self; + struct confargs *ca = aux; + int i; +#if 0 + int size = (CD2400_SIZE + PGOFSET) & ~PGOFSET; +#endif + + sc->vbase = ca->ca_vaddr; + sc->sc_pcctwo = ca->ca_master; + + if (ca->ca_paddr == cl_cons.cl_paddr) { + /* if this device is configured as console, + * line cl_cons.channel is the console */ + sc->sc_cl[cl_cons.channel].cl_consio = 1; + printf(" console"); + } else { + /* reset chip only if we are not console device */ + /* wait for GFRCR */ + } + /* set up global registers */ + sc->vbase[CD2400_TPR] = CL_TIMEOUT; + sc->vbase[CD2400_RPILR] = 0x03; + sc->vbase[CD2400_TPILR] = 0x02; + sc->vbase[CD2400_MPILR] = 0x01; + + for (i = 0; i < CLCD_PORTS_PER_CHIP; i++) { +#if 0 + sc->sc_cl[i].cl_rxmode = + !(!((flags >> (i * CL_FLAG_BIT_PCH)) & 0x01)); + sc->sc_cl[i].cl_txmode = + !(!((flags >> (i * CL_FLAG_BIT_PCH)) & 0x02)); + sc->sc_cl[i].cl_softchar = + !(!((flags >> (i * CL_FLAG_BIT_PCH)) & 0x04)); +#endif + cl_initchannel(sc, i); + } + /* enable interrupts */ + sc->sc_ih_e.ih_fn = cl_rxintr; + sc->sc_ih_e.ih_arg = sc; + sc->sc_ih_e.ih_ipl = ca->ca_ipl; + sc->sc_ih_e.ih_wantframe = 0; + + sc->sc_ih_m.ih_fn = cl_mintr; + sc->sc_ih_m.ih_arg = sc; + sc->sc_ih_m.ih_ipl = ca->ca_ipl; + sc->sc_ih_m.ih_wantframe = 0; + + sc->sc_ih_t.ih_fn = cl_txintr; + sc->sc_ih_t.ih_arg = sc; + sc->sc_ih_t.ih_ipl = ca->ca_ipl; + sc->sc_ih_t.ih_wantframe = 0; + + sc->sc_ih_r.ih_fn = cl_rxintr; + sc->sc_ih_r.ih_arg = sc; + sc->sc_ih_r.ih_ipl = ca->ca_ipl; + sc->sc_ih_r.ih_wantframe = 0; + switch (ca->ca_bustype) { + case BUS_PCCTWO: + dopoll = 0; + pcctwointr_establish(PCC2V_SCC_RXE, &sc->sc_ih_e); + pcctwointr_establish(PCC2V_SCC_M, &sc->sc_ih_m); + pcctwointr_establish(PCC2V_SCC_TX, &sc->sc_ih_t); + pcctwointr_establish(PCC2V_SCC_RX, &sc->sc_ih_r); + sc->sc_pcctwo = (void *)ca->ca_master; + sc->sc_pcctwo->pcc2_sccerr = 0x01; /* clear errors */ + + /* enable all interrupts at ca_ipl */ + sc->sc_pcctwo->pcc2_sccirq = PCC2_IRQ_IEN | (ca->ca_ipl & 0x7); + sc->sc_pcctwo->pcc2_scctx = PCC2_IRQ_IEN | (ca->ca_ipl & 0x7); + sc->sc_pcctwo->pcc2_sccrx = PCC2_IRQ_IEN | (ca->ca_ipl & 0x7); + break; + } + + evcnt_attach(&sc->sc_dev, "intr", &sc->sc_txintrcnt); + evcnt_attach(&sc->sc_dev, "intr", &sc->sc_rxintrcnt); + evcnt_attach(&sc->sc_dev, "intr", &sc->sc_mxintrcnt); + printf("\n"); +} + +static void +cl_initchannel(sc, channel) + struct clsoftc *sc; + int channel; +{ + int s; + volatile u_char *cd_base = sc->vbase; + + /* set up option registers */ + sc->sc_cl[channel].tty = NULL; + s = splhigh(); + cd_base[CD2400_CAR] = (char)channel; + /* async, do we want to try DMA at some point? */ + cd_base[CD2400_LIVR] = PCC2_VECBASE + 0xc;/* set vector base at 5C */ + cd_base[CD2400_IER] = 0x88; /* should change XXX */ + cd_base[CD2400_LICR] = 0x00; /* will change if DMA support XXX */ + /* if the port is not the console */ + if (sc->sc_cl[channel].cl_consio != 1) { + cd_base[CD2400_CMR] = 0x02; + cd_base[CD2400_COR1] = 0x17; + cd_base[CD2400_COR2] = 0x00; + cd_base[CD2400_COR3] = 0x02; + cd_base[CD2400_COR4] = 0xec; + cd_base[CD2400_COR5] = 0xec; + cd_base[CD2400_COR6] = 0x00; + cd_base[CD2400_COR7] = 0x00; + cd_base[CD2400_SCHR1] = 0x00; + cd_base[CD2400_SCHR2] = 0x00; + cd_base[CD2400_SCHR3] = 0x00; + cd_base[CD2400_SCHR4] = 0x00; + cd_base[CD2400_SCRl] = 0x00; + cd_base[CD2400_SCRh] = 0x00; + cd_base[CD2400_LNXT] = 0x00; + cd_base[CD2400_RBPR] = 0x40; /* 9600 */ + cd_base[CD2400_RCOR] = 0x01; + cd_base[CD2400_TBPR] = 0x40; /* 9600 */ + cd_base[CD2400_TCOR] = 0x01 << 5; + /* console port should be 0x88 already */ + cd_base[CD2400_MSVR_RTS] = 0x00; + cd_base[CD2400_MSVR_DTR] = 0x00; + cd_base[CD2400_RTPRl] = CL_RX_TIMEOUT; + cd_base[CD2400_RTPRh] = 0x00; + } + splx(s); +} + +int cldefaultrate = TTYDEF_SPEED; + +int clmctl(dev, bits, how) + dev_t dev; + int bits; + int how; +{ + struct clsoftc *sc = (struct clsoftc *)clcd.cd_devs[CL_UNIT(dev)]; + int s; + + /* settings are currently ignored */ + s = spltty(); + switch (how) { + case DMSET: + if (bits & TIOCM_RTS) { + sc->vbase[CD2400_MSVR_RTS] = 0x01; + } else { + sc->vbase[CD2400_MSVR_RTS] = 0x00; + } + if (bits & TIOCM_DTR) { + sc->vbase[CD2400_MSVR_DTR] = 0x02; + } else { + sc->vbase[CD2400_MSVR_DTR] = 0x00; + } + break; + + case DMBIC: + if (bits & TIOCM_RTS) { + sc->vbase[CD2400_MSVR_RTS] = 0x00; + } + if (bits & TIOCM_DTR) { + sc->vbase[CD2400_MSVR_DTR] = 0x00; + } + break; + + case DMBIS: + if (bits & TIOCM_RTS) { + sc->vbase[CD2400_MSVR_RTS] = 0x01; + } + if (bits & TIOCM_DTR) { + sc->vbase[CD2400_MSVR_DTR] = 0x02; + } + break; + + case DMGET: + bits = 0; + { + u_char msvr = sc->vbase[CD2400_MSVR_RTS]; + + if (msvr & 0x80) + bits |= TIOCM_DSR; + if (msvr & 0x40) + bits |= TIOCM_CD; + if (msvr & 0x20) + bits |= TIOCM_CTS; + if (msvr & 0x10) + bits |= TIOCM_DTR; + if (msvr & 0x02) + bits |= TIOCM_DTR; + if (msvr & 0x01) + bits |= TIOCM_RTS; + } + break; + } + (void)splx(s); +#if 0 + bits = 0; + /* proper defaults? */ + bits |= TIOCM_DTR; + bits |= TIOCM_RTS; + bits |= TIOCM_CTS; + bits |= TIOCM_CD; + /* bits |= TIOCM_RI; */ + bits |= TIOCM_DSR; +#endif + + /* + printf("retbits %x\n", bits); + */ + return (bits); +} + +int +clopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + int s, unit, channel; + struct cl_info *cl; + struct clsoftc *sc; + struct tty *tp; + + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return (ENODEV); + } + channel = CL_CHANNEL(dev); + cl = &sc->sc_cl[channel]; + s = spltty(); + if (cl->tty) { + tp = cl->tty; + } else { + tp = cl->tty = ttymalloc(); + } + tp->t_oproc = clstart; + tp->t_param = clparam; + tp->t_dev = dev; + + if ((tp->t_state & TS_ISOPEN) == 0) { + tp->t_state |= TS_WOPEN; + ttychars(tp); + if (tp->t_ispeed == 0) { + /* + * only when cleared do we reset to defaults. + */ + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_cflag = TTYDEF_CFLAG; + tp->t_lflag = TTYDEF_LFLAG; + tp->t_ispeed = tp->t_ospeed = cldefaultrate; + } + /* + * do these all the time + */ + if (cl->cl_swflags & TIOCFLAG_CLOCAL) + tp->t_cflag |= CLOCAL; + if (cl->cl_swflags & TIOCFLAG_CRTSCTS) + tp->t_cflag |= CRTSCTS; + if (cl->cl_swflags & TIOCFLAG_MDMBUF) + tp->t_cflag |= MDMBUF; + clparam(tp, &tp->t_termios); + ttsetwater(tp); + + (void)clmctl(dev, TIOCM_DTR | TIOCM_RTS, DMSET); +#ifdef XXX + if ((cl->cl_swflags & TIOCFLAG_SOFTCAR) || + (clmctl(dev, 0, DMGET) & TIOCM_CD)) { + tp->t_state |= TS_CARR_ON; + } else { + tp->t_state &= ~TS_CARR_ON; + } +#endif + tp->t_state |= TS_CARR_ON; + } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { + splx(s); + return (EBUSY); + } +#ifdef XXX + /* + * if NONBLOCK requested, ignore carrier + */ + if (flag & O_NONBLOCK) + goto done; +#endif + + splx(s); + /* + * Reset the tty pointer, as there could have been a dialout + * use of the tty with a dialin open waiting. + */ + tp->t_dev = dev; +#ifdef DEBUG + cl_dumpport(channel); +#endif + return ((*linesw[tp->t_line].l_open)(dev, tp)); +} + +int +clparam(tp, t) + struct tty *tp; + struct termios *t; +{ + int unit, channel; + struct clsoftc *sc; + int s; + dev_t dev; + + dev = tp->t_dev; + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return (ENODEV); + } + channel = CL_CHANNEL(dev); + tp->t_ispeed = t->c_ispeed; + tp->t_ospeed = t->c_ospeed; + tp->t_cflag = t->c_cflag; +/* + t->c_ispeed = tp->t_ispeed; + t->c_ospeed = tp->t_ospeed; + t->c_cflag = tp->t_cflag; +*/ + clccparam(sc, t, channel); + s = spltty(); + cl_unblock(tp); + splx(s); + return (0); +} + +void +cloutput(tp) + struct tty *tp; +{ + int cc, s, unit, cnt; + char *tptr; + int channel; + struct clsoftc *sc; + dev_t dev; + + dev = tp->t_dev; + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return; + } + channel = CL_CHANNEL(dev); + + if ((tp->t_state & TS_ISOPEN) == 0) + return; + + s = spltty(); + cc = tp->t_outq.c_cc; + while (cc > 0) { +/*XXX*/ + cnt = min(CLCDBUF, cc); + cnt = q_to_b(&tp->t_outq, cl_obuffer, cnt); + if (cnt == 0) { + break; + } + for (tptr = cl_obuffer; tptr < &cl_obuffer[cnt]; tptr++) { + clputc(sc, channel, *tptr); + } + cc -= cnt; + } + splx(s); +} + +int +clclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + int unit, channel; + struct tty *tp; + struct cl_info *cl; + struct clsoftc *sc; + int s; + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return (ENODEV); + } + channel = CL_CHANNEL(dev); + cl = &sc->sc_cl[channel]; + tp = cl->tty; + (*linesw[tp->t_line].l_close)(tp, flag); + + s = spltty(); + sc->vbase[CD2400_CAR] = channel; + if (cl->cl_consio == 0 && (tp->t_cflag & HUPCL) != 0) { + sc->vbase[CD2400_MSVR_RTS] = 0x00; + sc->vbase[CD2400_MSVR_DTR] = 0x00; + sc->vbase[CD2400_CCR] = 0x05; + } + + splx(s); + ttyclose(tp); + +#if 0 + cl->tty = NULL; +#endif +#if 0 + cl_dumpport(channel); +#endif + return (0); +} + +int +clread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + int unit, channel; + struct tty *tp; + struct cl_info *cl; + struct clsoftc *sc; + + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return (ENODEV); + } + channel = CL_CHANNEL(dev); + cl = &sc->sc_cl[channel]; + tp = cl->tty; + if (!tp) + return (ENXIO); + return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); +} + +int +clwrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + int unit, channel; + struct tty *tp; + struct cl_info *cl; + struct clsoftc *sc; + + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return (ENODEV); + } + channel = CL_CHANNEL(dev); + cl = &sc->sc_cl[channel]; + tp = cl->tty; + if (!tp) + return (ENXIO); + return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); +} + +int +clioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + int error; + int unit, channel; + struct tty *tp; + struct cl_info *cl; + struct clsoftc *sc; + + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return (ENODEV); + } + channel = CL_CHANNEL(dev); + cl = &sc->sc_cl[channel]; + tp = cl->tty; + if (!tp) + return (ENXIO); + + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + error = ttioctl(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + switch (cmd) { + case TIOCSBRK: + /* */ + break; + + case TIOCCBRK: + /* */ + break; + + case TIOCSDTR: + (void) clmctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIS); + break; + + case TIOCCDTR: + (void) clmctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIC); + break; + + case TIOCMSET: + (void) clmctl(dev, *(int *) data, DMSET); + break; + + case TIOCMBIS: + (void) clmctl(dev, *(int *) data, DMBIS); + break; + + case TIOCMBIC: + (void) clmctl(dev, *(int *) data, DMBIC); + break; + + case TIOCMGET: + *(int *)data = clmctl(dev, 0, DMGET); + break; + case TIOCGFLAGS: + *(int *)data = cl->cl_swflags; + break; + case TIOCSFLAGS: + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) + return (EPERM); + + cl->cl_swflags = *(int *)data; + cl->cl_swflags &= /* only allow valid flags */ + (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | TIOCFLAG_CRTSCTS); + break; + default: + return (ENOTTY); + } + + return (0); +} + +int +clstop(tp, flag) + struct tty *tp; + int flag; +{ + int s; + + s = spltty(); + if (tp->t_state & TS_BUSY) { + if ((tp->t_state & TS_TTSTOP) == 0) + tp->t_state |= TS_FLUSH; + } + splx(s); + return (0); +} + +int +clcnprobe(cp) + struct consdev *cp; +{ + /* always there ? */ + /* serial major */ + int maj; + + /* locate the major number */ + for (maj = 0; maj < nchrdev; maj++) + if (cdevsw[maj].d_open == clopen) + break; + cp->cn_dev = makedev (maj, 0); + cp->cn_pri = CN_NORMAL; + + return (1); +} + +int +clcninit(cp) + struct consdev *cp; +{ +#ifdef MAP_DOES_WORK + int size = (0x1ff + PGOFSET) & ~PGOFSET; + int pcc2_size = (0x3C + PGOFSET) & ~PGOFSET; +#endif + volatile u_char *cd_base; + + cl_cons.cl_paddr = (void *)0xfff45000; +#ifdef MAP_DOES_WORK + cl_cons.cl_vaddr = mapiodev(cl_cons.cl_paddr, size); + cd_pcc2_base = mapiodev(0xfff42000, pcc2_size); +#else + cl_cons.cl_vaddr = cl_cons.cl_paddr; + cl_cons.pcctwoaddr = (void *)0xfff42000; +#endif + cd_base = cl_cons.cl_vaddr; + /* reset the chip? */ +#ifdef CLCD_DO_RESET +#endif +#ifdef NEW_CLCD_STRUCT + /* set up globals */ + cl->tftc = 0x10; + cl->tpr = CL_TIMEOUT; /* is this correct?? */ + cl->rpilr = 0x03; + cl->tpilr = 0x02; + cl->mpilr = 0x01; + + /* set up the tty00 to be 9600 8N1 */ + cl->car = 0x00; + cl->cor1 = 0x17; /* No parity, ignore parity, 8 bit char */ + cl->cor2 = 0x00; + cl->cor3 = 0x02; /* 1 stop bit */ + cl->cor4 = 0x00; + cl->cor5 = 0x00; + cl->cor6 = 0x00; + cl->cor7 = 0x00; + cl->schr1 = 0x00; + cl->schr2 = 0x00; + cl->schr3 = 0x00; + cl->schr4 = 0x00; + cl->scrl = 0x00; + cl->scrh = 0x00; + cl->lnxt = 0x00; + cl->cpsr = 0x00; +#else + /* set up globals */ +#ifdef NOT_ALREADY_SETUP + cd_base[CD2400_TFTC] = 0x10; + cd_base[CD2400_TPR] = CL_TIMEOUT; /* is this correct?? */ + cd_base[CD2400_RPILR] = 0x03; + cd_base[CD2400_TPILR] = 0x02; + cd_base[CD2400_MPILR] = 0x01; + + /* set up the tty00 to be 9600 8N1 */ + cd_base[CD2400_CAR] = 0x00; + cd_base[CD2400_COR1] = 0x17; /* No parity, ignore parity, 8 bit char */ + cd_base[CD2400_COR2] = 0x00; + cd_base[CD2400_COR3] = 0x02; /* 1 stop bit */ + cd_base[CD2400_COR4] = 0x00; + cd_base[CD2400_COR5] = 0x00; + cd_base[CD2400_COR6] = 0x00; + cd_base[CD2400_COR7] = 0x00; + cd_base[CD2400_SCHR1] = 0x00; + cd_base[CD2400_SCHR2] = 0x00; + cd_base[CD2400_SCHR3] = 0x00; + cd_base[CD2400_SCHR4] = 0x00; + cd_base[CD2400_SCRl] = 0x00; + cd_base[CD2400_SCRh] = 0x00; + cd_base[CD2400_LNXT] = 0x00; + cd_base[CD2400_CPSR] = 0x00; +#endif +#endif + return (0); +} + +int +cl_instat(sc) + struct clsoftc *sc; +{ + volatile u_char *cd_base; + + if ( NULL == sc) { + cd_base = cl_cons.cl_vaddr; + } else { + cd_base = sc->vbase; + } + return (cd_base[CD2400_RIR] & 0x80); +} + +int +clcngetc(dev) + dev_t dev; +{ + u_char val, reoir, licr, isrl, data, status, fifo_cnt; + int got_char = 0; + volatile u_char *cd_base = cl_cons.cl_vaddr; + volatile struct pcctworeg *pcc2_base = cl_cons.pcctwoaddr; + + while (got_char == 0) { + val = cd_base[CD2400_RIR]; + /* if no receive interrupt pending wait */ + if (!(val & 0x80)) { + continue; + } + /* XXX do we need to suck the entire FIFO contents? */ + reoir = pcc2_base->pcc2_sccrxiack; /* receive PIACK */ + licr = cd_base[CD2400_LICR]; + if (((licr >> 2) & 0x3) == 0) { + /* is the interrupt for us (port 0) */ + /* the character is for us. */ + isrl = cd_base[CD2400_RISRl]; +#if 0 + if (isrl & 0x01) { + status = BREAK; + } + if (isrl & 0x02) { + status = FRAME; + } + if (isrl & 0x04) { + status = PARITY; + } + if (isrl & 0x08) { + status = OVERFLOW; + } + /* we do not have special characters ;-) */ +#endif + fifo_cnt = cd_base[CD2400_RFOC]; + data = cd_base[CD2400_RDR]; + got_char = 1; + cd_base[CD2400_TEOIR] = 0x00; + } else { + data = cd_base[CD2400_RDR]; + cd_base[CD2400_TEOIR] = 0x00; + } + + } + return (data); +} + +int +clcnputc(dev, c) + dev_t dev; + char c; +{ + /* is this the correct location for the cr -> cr/lf tranlation? */ + if (c == '\n') + clputc(0, 0, '\r'); + + clputc(0, 0, c); + return (0); +} + +clcnpollc(dev, on) + dev_t dev; + int on; +{ + if (1 == on) { + /* enable polling */ + } else { + /* disable polling */ + } +} + +static void +clputc(sc, unit, c) + struct clsoftc *sc; + int unit; + char c; +{ + int s; + u_char schar; + u_char oldchannel; + volatile u_char *cd_base; + if (0 == sc) { + /* output on console */ + cd_base = cl_cons.cl_vaddr; + } else { + cd_base = sc->vbase; + } +#ifdef NEW_CLCD_STRUCT + /* should we disable, flush and all that goo? */ + cl->car = unit; + schar = cl->schr3; + cl->schr3 = c; + cl->stcr = 0x08 | 0x03; /* send special char, char 3 */ + while (0 != cl->stcr) { + /* wait until cl notices the command + * otherwise it may not notice the character + * if we send characters too fast. + */ + } + cl->schr3 = schar; +#else +if (unit == 0) { + s = splhigh(); + oldchannel = cd_base[CD2400_CAR]; + cd_base[CD2400_CAR] = unit; + schar = cd_base[CD2400_SCHR3]; + cd_base[CD2400_SCHR3] = c; + cd_base[CD2400_STCR] = 0x08 | 0x03; /* send special char, char 3 */ + while (0 != cd_base[CD2400_STCR]) { + /* wait until cl notices the command + * otherwise it may not notice the character + * if we send characters too fast. + */ + } + DELAY(5); + cd_base[CD2400_SCHR3] = schar; + cd_base[CD2400_CAR] = oldchannel; + splx(s); +} else { + s = splhigh(); + oldchannel = cd_base[CD2400_CAR]; + cd_base[CD2400_CAR] = unit; + if (cd_base[CD2400_TFTC] > 0) { + cd_base[CD2400_TDR] = c; + } + cd_base[CD2400_CAR] = oldchannel; + splx(s); +} +#endif +} + +/* +#ifdef CLCD_DO_POLLED_INPUT +*/ +#if 1 +void +cl_chkinput() +{ + struct tty *tp; + int unit; + struct clsoftc *sc; + int channel; + + if (dopoll == 0) + return; + for (unit = 0; unit < clcd.cd_ndevs; unit++) { + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + continue; + } + if (cl_instat(sc)) { + while (cl_instat(sc)){ + int ch; + u_char c; + /* + *(pinchar++) = clcngetc(); + */ + ch = clgetc(sc, &channel) & 0xff; + c = ch; + + tp = sc->sc_cl[channel].tty; + if (NULL != tp) { + (*linesw[tp->t_line].l_rint)(c, tp); + } + } + /* + wakeup(tp); + */ + } + } +} +#endif + +static u_char +clgetc(sc, channel) + struct clsoftc *sc; + int *channel; +{ + volatile u_char *cd_base; + volatile struct pcctworeg *pcc2_base; + u_char val, reoir, licr, isrl, fifo_cnt, data; + + if (0 == sc) { + cd_base = cl_cons.cl_vaddr; + pcc2_base = cl_cons.pcctwoaddr; + } else { + cd_base = sc->vbase; + pcc2_base = sc->sc_pcctwo; + } + val = cd_base[CD2400_RIR]; + /* if no receive interrupt pending wait */ + if (!(val & 0x80)) { + return (0); + } + /* XXX do we need to suck the entire FIFO contents? */ + reoir = pcc2_base->pcc2_sccrxiack; /* receive PIACK */ + licr = cd_base[CD2400_LICR]; + *channel = (licr >> 2) & 0x3; + /* is the interrupt for us (port 0) */ + /* the character is for us yea. */ + isrl = cd_base[CD2400_RISRl]; +#if 0 + if (isrl & 0x01) { + status = BREAK; + } + if (isrl & 0x02) { + status = FRAME; + } + if (isrl & 0x04) { + status = PARITY; + } + if (isrl & 0x08) { + status = OVERFLOW; + } + /* we do not have special characters ;-) */ +#endif + fifo_cnt = cd_base[CD2400_RFOC]; + if (fifo_cnt > 0) { + data = cd_base[CD2400_RDR]; + cd_base[CD2400_TEOIR] = 0x00; + } else { + data = 0; + cd_base[CD2400_TEOIR] = 0x08; + } + return (data); +} + +int +clccparam(sc, par, channel) + struct clsoftc *sc; + struct termios *par; + int channel; +{ + u_int divisor, clk, clen; + int s, imask, ints; + + s = spltty(); + sc->vbase[CD2400_CAR] = channel; + if (par->c_ospeed == 0) { + /* dont kill the console */ + if (sc->sc_cl[channel].cl_consio == 0) { + /* disconnect, drop RTS DTR stop reciever */ + sc->vbase[CD2400_MSVR_RTS] = 0x00; + sc->vbase[CD2400_MSVR_DTR] = 0x00; + sc->vbase[CD2400_CCR] = 0x05; + } + splx(s); + return (0xff); + } + + sc->vbase[CD2400_MSVR_RTS] = 0x03; + sc->vbase[CD2400_MSVR_DTR] = 0x03; + + divisor = cl_clkdiv(par->c_ospeed); + clk = cl_clknum(par->c_ospeed); + sc->vbase[CD2400_TBPR] = divisor; + sc->vbase[CD2400_TCOR] = clk << 5; + divisor = cl_clkdiv(par->c_ispeed); + clk = cl_clknum(par->c_ispeed); + sc->vbase[CD2400_RBPR] = divisor; + sc->vbase[CD2400_RCOR] = clk; + sc->vbase[CD2400_RTPRl] = cl_clkrxtimeout(par->c_ispeed); + sc->vbase[CD2400_RTPRh] = 0x00; + + switch (par->c_cflag & CSIZE) { + case CS5: + clen = 4; /* this is the mask for the chip. */ + imask = 0x1F; + break; + case CS6: + clen = 5; + imask = 0x3F; + break; + case CS7: + clen = 6; + imask = 0x7F; + break; + default: + clen = 7; + imask = 0xFF; + } + sc->vbase[CD2400_COR3] = par->c_cflag & PARENB ? 4 : 2; + + if (par->c_cflag & PARENB) { + if (par->c_cflag & PARODD) { + sc->vbase[CD2400_COR1] = 0xE0 | clen ; /* odd */ + } else { + sc->vbase[CD2400_COR1] = 0x40 | clen ; /* even */ + } + } else { + sc->vbase[CD2400_COR1] = 0x10 | clen; /* ignore parity */ + } + + if (sc->sc_cl[channel].cl_consio == 0 && + (par->c_cflag & CREAD) == 0 ) { +/* + sc->vbase[CD2400_CSR] = 0x08; +*/ + sc->vbase[CD2400_CCR] = 0x08; + } else { + sc->vbase[CD2400_CCR] = 0x0a; + } + ints = 0; +#define SCC_DSR 0x80 +#define SCC_DCD 0x40 +#define SCC_CTS 0x20 + if ((par->c_cflag & CLOCAL) == 0) { + ints |= SCC_DCD; + } + if ((par->c_cflag & CCTS_OFLOW) != 0) { + ints |= SCC_CTS; + } + if ((par->c_cflag & CRTSCTS) != 0) { + ints |= SCC_CTS; + } +#ifdef DONT_LET_HARDWARE + if ((par->c_cflag & CCTS_IFLOW) != 0) { + ints |= SCC_DSR; + } +#endif + sc->vbase[CD2400_COR4] = ints | CL_FIFO_CNT; + sc->vbase[CD2400_COR5] = ints | CL_FIFO_CNT; + return (imask); +} + +static int clknum = 0; + +u_char +cl_clkdiv(speed) + int speed; +{ + int i = 0; + + if (cl_clocks[clknum].speed == speed) { + return (cl_clocks[clknum].divisor); + } + for (i = 0; cl_clocks[i].speed != 0; i++) { + if (cl_clocks[i].speed == speed) { + clknum = i; + return (cl_clocks[clknum].divisor); + } + } + /* return some sane value if unknown speed */ + return (cl_clocks[4].divisor); +} + +u_char +cl_clknum(speed) + int speed; +{ + int found = 0; + int i = 0; + + if (cl_clocks[clknum].speed == speed) { + return (cl_clocks[clknum].clock); + } + for (i = 0; found != 0 && cl_clocks[i].speed != 0; i++) { + if (cl_clocks[clknum].speed == speed) { + clknum = i; + return (cl_clocks[clknum].clock); + } + } + /* return some sane value if unknown speed */ + return (cl_clocks[4].clock); +} + +u_char +cl_clkrxtimeout(speed) + int speed; +{ + int i = 0; + + if (cl_clocks[clknum].speed == speed) { + return (cl_clocks[clknum].rx_timeout); + } + for (i = 0; cl_clocks[i].speed != 0; i++) { + if (cl_clocks[i].speed == speed) { + clknum = i; + return (cl_clocks[clknum].rx_timeout); + } + } + /* return some sane value if unknown speed */ + return (cl_clocks[4].rx_timeout); +} + +void +cl_unblock(tp) + struct tty *tp; +{ + tp->t_state &= ~TS_FLUSH; + if (tp->t_outq.c_cc != 0) + clstart(tp); +} + +void +clstart(tp) + struct tty *tp; +{ + dev_t dev; + u_char cbuf; + struct clsoftc *sc; + int channel, unit, s, cnt; + + dev = tp->t_dev; + channel = CL_CHANNEL(dev); +/* hack to test output on non console only */ +#if 0 + if (channel == 0) { + cloutput(tp); + return; + } +#endif + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return; + } + + if ((tp->t_state & TS_ISOPEN) == 0) + return; + + s = spltty(); +#if 0 + if (sc->sc_cl[channel].transmitting == 1) { + /* i'm busy, go away, I will get to it later. */ + splx(s); + return; + } + cnt = q_to_b(&tp->t_outq, &cbuf, 1); + if (cnt != 0) { + sc->sc_cl[channel].transmitting = 1; + sc->vbase[CD2400_CAR] = channel; + sc->vbase[CD2400_TDR] = cbuf; + } else { + sc->sc_cl[channel].transmitting = 0; + } +#else + if ((tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP | TS_FLUSH)) == 0) { + tp->t_state |= TS_BUSY; + sc->vbase[CD2400_CAR] = channel; + sc->vbase[CD2400_IER] = 0xb; + } +#endif + splx(s); +} + +int +cl_mintr(sc) + struct clsoftc *sc; +{ + u_char mir, misr, msvr; + int channel; + struct tty *tp; + + mir = sc->vbase[CD2400_MIR]; + if ((mir & 0x40) == 0x0) { + /* only if intr is not shared? */ + printf("cl_mintr extra intr\n"); + return (0); + } + sc->sc_mxintrcnt.ev_count++; + + channel = mir & 0x03; + misr = sc->vbase[CD2400_MISR]; + msvr = sc->vbase[CD2400_MSVR_RTS]; + if (misr & 0x01) { + /* timers are not currently used?? */ + printf("cl_mintr: channel %x timer 1 unexpected\n", channel); + } + if (misr & 0x02) { + /* timers are not currently used?? */ + printf("cl_mintr: channel %x timer 2 unexpected\n", channel); + } + if (misr & 0x20) { + printf("cl_mintr: channel %x cts %x\n", channel, + (msvr & 0x20) != 0x0); + } + if (misr & 0x40) { + struct tty *tp = sc->sc_cl[channel].tty; + + printf("cl_mintr: channel %x cd %x\n", channel, + (msvr & 0x40) != 0x0); + ttymodem(tp, (msvr & 0x40) != 0x0); + } + if (misr & 0x80) { + printf("cl_mintr: channel %x dsr %x\n", channel, + (msvr & 0x80) != 0x0); + } + sc->vbase[CD2400_MEOIR] = 0x00; + return (1); +} + +int +cl_txintr(sc) + struct clsoftc *sc; +{ + static empty = 0; + u_char tir, licr, teoir; + u_char max; + int channel; + struct tty *tp; + int cnt; + u_char buffer[CL_FIFO_MAX +1]; + u_char *tptr; + + tir = sc->vbase[CD2400_TIR]; + if ((tir & 0x40) == 0x0) { + /* only if intr is not shared ??? */ + printf("cl_txintr extra intr\n"); + return (0); + } + sc->sc_txintrcnt.ev_count++; + + channel = tir & 0x03; + licr = sc->vbase[CD2400_LICR]; + + sc->sc_cl[channel].txcnt ++; + + tp = sc->sc_cl[channel].tty; + if (tp == NULL || (tp->t_state & TS_ISOPEN) == 0) { + sc->vbase[CD2400_TEOIR] = 0x08; + return (1); + } + switch ((licr >> 4) & 0x3) { + case CL_DMAMODE: + teoir = 0x08; + break; + case CL_INTRMODE: + max = sc->vbase[CD2400_TFTC]; + cnt = min((int)max, tp->t_outq.c_cc); + if (cnt != 0) { + cnt = q_to_b(&tp->t_outq, buffer, cnt); + empty = 0; + for (tptr = buffer; tptr < &buffer[cnt]; tptr++) { + sc->vbase[CD2400_TDR]= *tptr; + } + teoir = 0x00; + } else { + if (empty > 5 && (empty % 20000 ) == 0) { + printf("cl_txintr: too many empty intr %d chan %d\n", + empty, channel); + } + empty++; + teoir = 0x08; + if (tp->t_state & TS_BUSY) { + tp->t_state &= ~(TS_BUSY | TS_FLUSH); + if (tp->t_state & TS_ASLEEP) { + tp->t_state &= ~TS_ASLEEP; + wakeup((caddr_t)&tp->t_outq); + } + selwakeup(&tp->t_wsel); + } + sc->vbase[CD2400_IER] = sc->vbase[CD2400_IER] & ~0x3; + } + break; + default: + printf("cl_txintr unknown mode %x\n", (licr >> 4) & 0x3); + /* we probably will go to hell quickly now */ + teoir = 0x08; + } + sc->vbase[CD2400_TEOIR] = teoir; + return (1); +} + +int +cl_rxintr(sc) + struct clsoftc *sc; +{ + u_char rir, channel, licr, risrl; + u_char c; + u_char fifocnt; + struct tty *tp; + int i; + u_char reoir; + + rir = sc->vbase[CD2400_RIR]; + if ((rir & 0x40) == 0x0) { + /* only if intr is not shared ??? */ + printf("cl_rxintr extra intr\n"); + return (0); + } + sc->sc_rxintrcnt.ev_count++; + channel = rir & 0x3; + licr = sc->vbase[CD2400_LICR]; + reoir = 0x08; + + sc->sc_cl[channel].rxcnt ++; + + switch (licr & 0x03) { + case CL_DMAMODE: + reoir = 0x08; + break; + case CL_INTRMODE: + risrl = sc->vbase[CD2400_RISRl]; + if (risrl & 0x80) { + /* timeout, no characters */ + reoir = 0x08; + } else + /* We don't need no sinkin special characters */ + if (risrl & 0x08) { + cl_overflow (sc, channel, &sc->sc_fotime, "fifo"); + reoir = 0x08; + } else + if (risrl & 0x04) { + cl_parity(sc, channel); + reoir = 0x08; + } else + if (risrl & 0x02) { + cl_frame(sc, channel); + reoir = 0x08; + } else + if (risrl & 0x01) { + cl_break(sc, channel); + reoir = 0x08; + } else { + fifocnt = sc->vbase[CD2400_RFOC]; + tp = sc->sc_cl[channel].tty; + for (i = 0; i < fifocnt; i++) { + c = sc->vbase[CD2400_RDR]; +#if USE_BUFFER + cl_appendbuf(sc, channel, c); +#else + /* does any restricitions exist on spl + * for this call + */ + (*linesw[tp->t_line].l_rint)(c, tp); + reoir = 0x00; +#endif + } + } + break; + default: + printf("cl_rxintr unknown mode %x\n", licr & 0x03); + /* we probably will go to hell quickly now */ + reoir = 0x08; + } + sc->vbase[CD2400_REOIR] = reoir; + return (1); +} + +void +cl_overflow(sc, channel, ptime, msg) + struct clsoftc *sc; + int channel; + long *ptime; + char *msg; +{ +/* + if (*ptime != time.tv_sec) { +*/ + { +/* + *ptime = time.tv_sec); +*/ + log(LOG_WARNING, "%s%d[%d]: %s overrun", clcd.cd_name, + 0 /* fix */, channel, msg); + } +} + +void +cl_parity(sc, channel) + struct clsoftc *sc; + int channel; +{ + log(LOG_WARNING, "%s%d[%d]: parity error", clcd.cd_name, 0, channel); +} + +void +cl_frame(sc, channel) + struct clsoftc *sc; + int channel; +{ + log(LOG_WARNING, "%s%d[%d]: frame error", clcd.cd_name, 0, channel); +} + +void +cl_break(sc, channel) + struct clsoftc *sc; + int channel; +{ + log(LOG_WARNING, "%s%d[%d]: break detected", clcd.cd_name, 0, channel); +} + +void +cl_dumpport0() +{ + cl_dumpport(0); +} + +void +cl_dumpport1() +{ + cl_dumpport(1); +} + +void +cl_dumpport2() +{ + cl_dumpport(2); +} + +void +cl_dumpport3() +{ + cl_dumpport(3); +} + +void +cl_dumpport(channel) + int channel; +{ + u_char livr, cmr, cor1, cor2, cor3, cor4, cor5, cor6, cor7; + u_char schr1, schr2, schr3, schr4, scrl, scrh, lnxt; + u_char rbpr, rcor, tbpr, tcor, rpilr, rir, tpr, ier, ccr; + u_char csr, rts, dtr, rtprl, rtprh; + struct clsoftc *sc = (struct clsoftc *) clcd.cd_devs[0]; + volatile u_char *cd_base = cl_cons.cl_vaddr; + int s; + + s = spltty(); + cd_base[CD2400_CAR] = (char) channel; + livr = cd_base[CD2400_LIVR]; + cmr = cd_base[CD2400_CMR]; + cor1 = cd_base[CD2400_COR1]; + cor2 = cd_base[CD2400_COR2]; + cor3 = cd_base[CD2400_COR3]; + cor4 = cd_base[CD2400_COR4]; + cor5 = cd_base[CD2400_COR5]; + cor6 = cd_base[CD2400_COR6]; + cor7 = cd_base[CD2400_COR7]; + schr1 = cd_base[CD2400_SCHR1]; + schr2 = cd_base[CD2400_SCHR2]; + schr3 = cd_base[CD2400_SCHR3]; + schr4 = cd_base[CD2400_SCHR4]; + scrl = cd_base[CD2400_SCRl]; + scrh = cd_base[CD2400_SCRh]; + lnxt = cd_base[CD2400_LNXT]; + rbpr = cd_base[CD2400_RBPR]; + rcor = cd_base[CD2400_RCOR]; + tbpr = cd_base[CD2400_TBPR]; + rpilr = cd_base[CD2400_RPILR]; + ier = cd_base[CD2400_IER]; + ccr = cd_base[CD2400_CCR]; + tcor = cd_base[CD2400_TCOR]; + csr = cd_base[CD2400_CSR]; + tpr = cd_base[CD2400_TPR]; + rts = cd_base[CD2400_MSVR_RTS]; + dtr = cd_base[CD2400_MSVR_DTR]; + rtprl = cd_base[CD2400_RTPRl]; + rtprh = cd_base[CD2400_RTPRh]; + splx(s); + + printf("{ port %x livr %x cmr %x\n", channel, livr, cmr); + printf("cor1 %x cor2 %x cor3 %x cor4 %x cor5 %x cor6 %x cor7 %x\n", + cor1, cor2, cor3, cor4, cor5, cor6, cor7); + printf("schr1 %x schr2 %x schr3 %x schr4 %x\n", schr1, schr2, schr3, + schr4); + printf("scrl %x scrh %x lnxt %x\n", scrl, scrh, lnxt); + printf("rbpr %x rcor %x tbpr %x tcor %x\n", rbpr, rcor, tbpr, tcor); + printf("rpilr %x rir %x ier %x ccr %x\n", rpilr, rir, ier, ccr); + printf("tpr %x csr %x rts %x dtr %x\n", tpr, csr, rts, dtr); + printf("rtprl %x rtprh %x\n", rtprl, rtprh); + printf("rxcnt %x txcnt %x\n", sc->sc_cl[channel].rxcnt, + sc->sc_cl[channel].txcnt); + printf("}\n"); +} diff --git a/sys/arch/mvme68k/dev/cl.c.dale b/sys/arch/mvme68k/dev/cl.c.dale new file mode 100644 index 00000000000..c59ad812a10 --- /dev/null +++ b/sys/arch/mvme68k/dev/cl.c.dale @@ -0,0 +1,1666 @@ +/* $NetBSD$ */ +/* + * Copyright (c) 1995 Dale Rahn. 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 Dale Rahn. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <sys/param.h> +#include <sys/callout.h> +#include <sys/conf.h> +#include <sys/ioctl.h> +#include <sys/proc.h> +#include <sys/tty.h> +#include <sys/uio.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/device.h> +/* #include <sys/queue.h> */ +#include <machine/cpu.h> +#include <machine/autoconf.h> +#include <dev/cons.h> +#include <mvme68k/dev/cd2400reg.h> +#include <sys/syslog.h> +#include "cl.h" + +#include "pcctwo.h" + +#if NPCCTWO > 0 +#include <mvme68k/dev/pcctworeg.h> +#endif + +#define splcl() spl3() + +/* min timeout 0xa, what is a good value */ +#define CL_TIMEOUT 0x10 +#define CL_FIFO_MAX 0x10 +#define CL_FIFO_CNT 0xc +#define CL_RX_TIMEOUT 0x10 + +#define CL_DMAMODE 0x1 +#define CL_INTRMODE 0x0 + +struct cl_cons { + u_char *cl_paddr; + volatile u_char *cl_vaddr; + volatile struct pcctworeg *pcctwoaddr; + u_char channel; +} cl_cons; + +struct cl_info { + struct tty *tty; + u_char cl_swflags; + u_char cl_softchar; + u_char cl_consio; + u_char cl_speed; + u_char cl_parstop; /* parity, stop bits. */ + u_char cl_rxmode; + u_char cl_txmode; + u_char cl_clen; + u_char cl_parity; + u_char transmitting; + u_long txcnt; + u_long rxcnt; +}; +#define CLCD_PORTS_PER_CHIP 4 +struct clsoftc { + struct device sc_dev; + struct evcnt sc_txintrcnt; + struct evcnt sc_rxintrcnt; + struct evcnt sc_mxintrcnt; + time_t sc_rotime; /* time of last ring overrun */ + time_t sc_fotime; /* time of last fifo overrun */ + u_char *pbase; + volatile u_char *vbase; + struct cl_info sc_cl[CLCD_PORTS_PER_CHIP]; + struct intrhand sc_ih_e; + struct intrhand sc_ih_m; + struct intrhand sc_ih_t; + struct intrhand sc_ih_r; + struct pcctworeg *sc_pcctwo; + int sc_flags; +}; +struct { + u_int speed; + u_char divisor; + u_char clock; + u_char rx_timeout; +} cl_clocks[] = { + { 64000, 0x26, 0, 0x01}, + { 56000, 0x2c, 0, 0x01}, + { 38400, 0x40, 0, 0x01}, + { 19200, 0x81, 0, 0x02}, + { 9600, 0x40, 1, 0x04}, + { 7200, 0x56, 1, 0x04}, + { 4800, 0x81, 1, 0x08}, + { 3600, 0xad, 1, 0x08}, + { 2400, 0x40, 2, 0x10}, + { 1200, 0x81, 2, 0x20}, + { 600, 0x40, 3, 0x40}, + { 300, 0x81, 3, 0x80}, + { 150, 0x40, 3, 0x80}, + { 110, 0x58, 4, 0xff}, + { 50, 0xC2, 4, 0xff}, + { 0, 0x00, 0, 0}, +}; + +/* prototypes */ +int clcnprobe __P((struct consdev *cp)); +int clcninit __P((struct consdev *cp)); +int clcngetc __P((dev_t dev)); +int clcnputc __P((dev_t dev, char c)); +u_char cl_clkdiv __P((int speed)); +u_char cl_clknum __P((int speed)); +u_char cl_clkrxtimeout __P((int speed)); +void clstart __P((struct tty *tp)); +void cl_unblock __P((struct tty *tp)); +int clccparam __P((struct clsoftc *sc, struct termios *par, int channel)); + +int clparam __P((struct tty *tp, struct termios *t)); +int cl_mintr __P((struct clsoftc *sc)); +int cl_txintr __P((struct clsoftc *sc)); +int cl_rxintr __P((struct clsoftc *sc)); +void cl_overflow __P((struct clsoftc *sc, int channel, long *ptime, char *msg)); +void cl_parity __P((struct clsoftc *sc, int channel)); +void cl_frame __P((struct clsoftc *sc, int channel)); +void cl_break __P(( struct clsoftc *sc, int channel)); +int clmctl __P((dev_t dev, int bits, int how)); +void cl_dumpport __P((int channel)); + +int clprobe __P((struct device *parent, void *self, void *aux)); +void clattach __P((struct device *parent, struct device *self, void *aux)); + +int clopen __P((dev_t dev, int flag, int mode, struct proc *p)); +int clclose __P((dev_t dev, int flag, int mode, struct proc *p)); +int clread __P((dev_t dev, struct uio *uio, int flag)); +int clwrite __P((dev_t dev, struct uio *uio, int flag)); +int clioctl __P((dev_t dev, int cmd, caddr_t data, int flag, struct proc *p)); +int clstop __P((struct tty *tp, int flag)); + +static void cl_initchannel __P((struct clsoftc *sc, int channel)); +static void clputc __P((struct clsoftc *sc, int unit, char c)); +static u_char clgetc __P((struct clsoftc *sc, int *channel)); +static void cloutput __P( (struct tty *tp)); + +struct cfdriver clcd = { + NULL, "cl", clprobe, clattach, DV_TTY, sizeof(struct clsoftc), 0 +}; + +#if 0 +struct { + u_char *pbase; + u_char *vbase; + struct cl_info info[CLCD_PORTS_PER_CHIP]; +} cl[NCL]; +#endif + +#define CLCDBUF 80 + +int dopoll = 1; + +#define CL_UNIT(x) (minor(x) >> 2) +#define CL_CHANNEL(x) (minor(x) & 3) +#define CL_TTY(x) (minor(x)) + +extern int cputyp; + +struct tty * cltty(dev) + dev_t dev; +{ + int unit, channel; + struct clsoftc *sc; + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return (NULL); + } + channel = CL_CHANNEL(dev); + return sc->sc_cl[channel].tty; +} + +int clprobe(parent, self, aux) + struct device *parent; + void *self; + void *aux; +{ + /* probing onboard 166/167/187 CL-cd2400 + * should be previously configured, + * we can check the value before resetting the chip + */ + volatile u_char *cd_base; + struct confargs *ca = aux; + int ret; + if (cputyp != CPU_167 && cputyp != CPU_166 +#ifdef CPU_187 + && cputyp != CPU_187 +#endif + ) + { + return 0; + } + cd_base = ca->ca_vaddr; + +#if 0 + ret = !badvaddr(&cd_base[CD2400_GFRCR],1); +#else + ret = 1; +#endif + return ret; +} + +void +clattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct clsoftc *sc = (struct clsoftc *)self; + struct confargs *ca = aux; + int i; + + sc->vbase = ca->ca_vaddr; + sc->sc_pcctwo = ca->ca_master; + + if (ca->ca_paddr == cl_cons.cl_paddr) { + /* if this device is configured as console, + * line cl_cons.channel is the console */ + sc->sc_cl[cl_cons.channel].cl_consio = 1; + printf(" console"); + } else { + /* reset chip only if we are not console device */ + /* wait for GFRCR */ + } + /* set up global registers */ + sc->vbase[CD2400_TPR] = CL_TIMEOUT; + sc->vbase[CD2400_RPILR] = 0x03; + sc->vbase[CD2400_TPILR] = 0x02; + sc->vbase[CD2400_MPILR] = 0x01; + + for (i = 0; i < CLCD_PORTS_PER_CHIP; i++) { +#if 0 + sc->sc_cl[i].cl_rxmode = + !(!((flags >> (i * CL_FLAG_BIT_PCH)) & 0x01)); + sc->sc_cl[i].cl_txmode = + !(!((flags >> (i * CL_FLAG_BIT_PCH)) & 0x02)); + sc->sc_cl[i].cl_softchar = + !(!((flags >> (i * CL_FLAG_BIT_PCH)) & 0x04)); +#endif + cl_initchannel(sc, i); + } + /* enable interrupts */ + sc->sc_ih_e.ih_fn = cl_rxintr; + sc->sc_ih_e.ih_arg = sc; + sc->sc_ih_e.ih_ipl = ca->ca_ipl; + sc->sc_ih_e.ih_wantframe = 0; + + sc->sc_ih_m.ih_fn = cl_mintr; + sc->sc_ih_m.ih_arg = sc; + sc->sc_ih_m.ih_ipl = ca->ca_ipl; + sc->sc_ih_m.ih_wantframe = 0; + + sc->sc_ih_t.ih_fn = cl_txintr; + sc->sc_ih_t.ih_arg = sc; + sc->sc_ih_t.ih_ipl = ca->ca_ipl; + sc->sc_ih_t.ih_wantframe = 0; + + sc->sc_ih_r.ih_fn = cl_rxintr; + sc->sc_ih_r.ih_arg = sc; + sc->sc_ih_r.ih_ipl = ca->ca_ipl; + sc->sc_ih_r.ih_wantframe = 0; + switch (ca->ca_bustype) { + case BUS_PCCTWO: + dopoll = 0; + pcctwointr_establish(PCC2V_SCC_RXE,&sc->sc_ih_e); + pcctwointr_establish(PCC2V_SCC_M,&sc->sc_ih_m); + pcctwointr_establish(PCC2V_SCC_TX,&sc->sc_ih_t); + pcctwointr_establish(PCC2V_SCC_RX,&sc->sc_ih_r); + sc->sc_pcctwo = (void *)ca->ca_master; + sc->sc_pcctwo->pcc2_sccerr = 0x01; /* clear errors */ + + /* enable all interrupts at ca_ipl */ + sc->sc_pcctwo->pcc2_sccirq = 0x10 | (ca->ca_ipl & 0x7); + sc->sc_pcctwo->pcc2_scctx = 0x10 | (ca->ca_ipl & 0x7); + sc->sc_pcctwo->pcc2_sccrx = 0x10 | (ca->ca_ipl & 0x7); + break; + default: + /* oops */ + panic ("cl driver on unknown bus\n"); + } + + evcnt_attach(&sc->sc_dev, "intr", &sc->sc_txintrcnt); + evcnt_attach(&sc->sc_dev, "intr", &sc->sc_rxintrcnt); + evcnt_attach(&sc->sc_dev, "intr", &sc->sc_mxintrcnt); + printf("\n"); +} +static void +cl_initchannel(sc, channel) + struct clsoftc *sc; + int channel; +{ + int s; + volatile u_char *cd_base = sc->vbase; + /* set up option registers */ + sc->sc_cl[channel].tty = NULL; + s = splhigh(); + cd_base[CD2400_CAR] = (char) channel; + /* async, do we want to try DMA at some point? */ + cd_base[CD2400_LIVR] = PCC2_VECBASE + 0xc;/* set vector base at 5C */ + cd_base[CD2400_IER] = 0x88; /* should change XXX */ + cd_base[CD2400_LICR] = 0x00; /* will change if DMA support XXX */ + /* if the port is not the console */ + if (sc->sc_cl[channel].cl_consio != 1) { + cd_base[CD2400_CMR] = 0x02; + cd_base[CD2400_COR1] = 0x17; + cd_base[CD2400_COR2] = 0x00; + cd_base[CD2400_COR3] = 0x02; + cd_base[CD2400_COR4] = 0xec; + cd_base[CD2400_COR5] = 0xec; + cd_base[CD2400_COR6] = 0x00; + cd_base[CD2400_COR7] = 0x00; + cd_base[CD2400_SCHR1] = 0x00; + cd_base[CD2400_SCHR2] = 0x00; + cd_base[CD2400_SCHR3] = 0x00; + cd_base[CD2400_SCHR4] = 0x00; + cd_base[CD2400_SCRl] = 0x00; + cd_base[CD2400_SCRh] = 0x00; + cd_base[CD2400_LNXT] = 0x00; + cd_base[CD2400_RBPR] = 0x40; /* 9600 */ + cd_base[CD2400_RCOR] = 0x01; + cd_base[CD2400_TBPR] = 0x40; /* 9600 */ + cd_base[CD2400_TCOR] = 0x01 << 5; + /* console port should be 0x88 already */ + cd_base[CD2400_MSVR_RTS] = 0x00; + cd_base[CD2400_MSVR_DTR] = 0x00; + cd_base[CD2400_RTPRl] = CL_RX_TIMEOUT; + cd_base[CD2400_RTPRh] = 0x00; + } + + splx(s); +} + + +int cldefaultrate = TTYDEF_SPEED; + +int clmctl (dev, bits, how) + dev_t dev; + int bits; + int how; +{ + int s; + struct clsoftc *sc; + /* should only be called with valid device */ + sc = (struct clsoftc *) clcd.cd_devs[CL_UNIT(dev)]; + /* + printf("mctl: dev %x, bits %x, how %x,\n",dev, bits, how); + */ + /* settings are currently ignored */ + s = splcl(); + switch (how) { + case DMSET: + if( bits & TIOCM_RTS) { + sc->vbase[CD2400_MSVR_RTS] = 0x01; + } else { + sc->vbase[CD2400_MSVR_RTS] = 0x00; + } + if( bits & TIOCM_DTR) { + sc->vbase[CD2400_MSVR_DTR] = 0x02; + } else { + sc->vbase[CD2400_MSVR_DTR] = 0x00; + } + break; + + case DMBIC: + if( bits & TIOCM_RTS) { + sc->vbase[CD2400_MSVR_RTS] = 0x00; + } + if( bits & TIOCM_DTR) { + sc->vbase[CD2400_MSVR_DTR] = 0x00; + } + break; + + case DMBIS: + if( bits & TIOCM_RTS) { + sc->vbase[CD2400_MSVR_RTS] = 0x01; + } + if( bits & TIOCM_DTR) { + sc->vbase[CD2400_MSVR_DTR] = 0x02; + } + break; + + case DMGET: + bits = 0; + + { + u_char msvr; + msvr = sc->vbase[CD2400_MSVR_RTS]; + if( msvr & 0x80) { + bits |= TIOCM_DSR; + } + if( msvr & 0x40) { + bits |= TIOCM_CD; + } + if( msvr & 0x20) { + bits |= TIOCM_CTS; + } + if( msvr & 0x10) { + bits |= TIOCM_DTR; + } + if( msvr & 0x02) { + bits |= TIOCM_DTR; + } + if( msvr & 0x01) { + bits |= TIOCM_RTS; + } + + } + break; + } + (void)splx(s); +#if 0 + bits = 0; + /* proper defaults? */ + bits |= TIOCM_DTR; + bits |= TIOCM_RTS; + bits |= TIOCM_CTS; + bits |= TIOCM_CD; + /* bits |= TIOCM_RI; */ + bits |= TIOCM_DSR; +#endif + + /* + printf("retbits %x\n", bits); + */ + return(bits); +} + +int clopen (dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + int s, unit, channel; + struct cl_info *cl; + struct clsoftc *sc; + struct tty *tp; + + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return (ENODEV); + } + channel = CL_CHANNEL(dev); + cl = &sc->sc_cl[channel]; + s = splcl(); + if (cl->tty) { + tp = cl->tty; + } else { + tp = cl->tty = ttymalloc(); + } + tp->t_oproc = clstart; + tp->t_param = clparam; + tp->t_dev = dev; + + if ((tp->t_state & TS_ISOPEN) == 0) { + tp->t_state |= TS_WOPEN; + ttychars(tp); + if (tp->t_ispeed == 0) { + /* + * only when cleared do we reset to defaults. + */ + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_cflag = TTYDEF_CFLAG; + tp->t_lflag = TTYDEF_LFLAG; + tp->t_ispeed = tp->t_ospeed = cldefaultrate; + } + /* + * do these all the time + */ + if (cl->cl_swflags & TIOCFLAG_CLOCAL) + tp->t_cflag |= CLOCAL; + if (cl->cl_swflags & TIOCFLAG_CRTSCTS) + tp->t_cflag |= CRTSCTS; + if (cl->cl_swflags & TIOCFLAG_MDMBUF) + tp->t_cflag |= MDMBUF; + clparam(tp, &tp->t_termios); + ttsetwater(tp); + + (void)clmctl(dev, TIOCM_DTR | TIOCM_RTS, DMSET); +#ifdef XXX + if ((cl->cl_swflags & TIOCFLAG_SOFTCAR) || + (clmctl(dev, 0, DMGET) & TIOCM_CD)) { + tp->t_state |= TS_CARR_ON; + } else { + tp->t_state &= ~TS_CARR_ON; + } +#endif + tp->t_state |= TS_CARR_ON; + } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { + splx(s); + return(EBUSY); + } +#ifdef XXX + /* + * if NONBLOCK requested, ignore carrier + */ + if (flag & O_NONBLOCK) + goto done; +#endif + + splx(s); + /* + * Reset the tty pointer, as there could have been a dialout + * use of the tty with a dialin open waiting. + */ + tp->t_dev = dev; +#ifdef DEBUG + cl_dumpport(channel); +#endif + return((*linesw[tp->t_line].l_open)(dev, tp)); +} +int clparam(tp, t) + struct tty *tp; + struct termios *t; +{ + int unit, channel; + struct clsoftc *sc; + int s; + dev_t dev; + + dev = tp->t_dev; + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return (ENODEV); + } + channel = CL_CHANNEL(dev); + tp->t_ispeed = t->c_ispeed; + tp->t_ospeed = t->c_ospeed; + tp->t_cflag = t->c_cflag; + clccparam(sc, t, channel); + s = splcl(); + cl_unblock(tp); + splx(s); + return 0; +} + +void cloutput(tp) + struct tty *tp; +{ + int cc, s, unit, cnt; + char *tptr; + int channel; + struct clsoftc *sc; + dev_t dev; + char cl_obuffer[CLCDBUF+1]; + + dev = tp->t_dev; + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return; + } + channel = CL_CHANNEL(dev); + + if ((tp->t_state & TS_ISOPEN) == 0) + return; + + s = splcl(); + cc = tp->t_outq.c_cc; + while (cc > 0) { +/*XXX*/ + cnt = min (CLCDBUF,cc); + cnt = q_to_b(&tp->t_outq, cl_obuffer, cnt); + if (cnt == 0) { + break; + } + for (tptr = cl_obuffer; tptr < &cl_obuffer[cnt]; tptr++) { + clputc(sc, channel, *tptr); + } + cc -= cnt; + } + splx(s); +} + +int clclose (dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + int unit, channel; + struct tty *tp; + struct cl_info *cl; + struct clsoftc *sc; + int s; + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return (ENODEV); + } + channel = CL_CHANNEL(dev); + cl = &sc->sc_cl[channel]; + tp = cl->tty; + (*linesw[tp->t_line].l_close)(tp, flag); + + s = splcl(); + + sc->vbase[CD2400_CAR] = channel; + if(cl->cl_consio == 0 && (tp->t_cflag & HUPCL) != 0) { + sc->vbase[CD2400_MSVR_RTS] = 0x00; + sc->vbase[CD2400_MSVR_DTR] = 0x00; + sc->vbase[CD2400_CCR] = 0x05; + } + + splx(s); + ttyclose(tp); + +#if 0 + cl->tty = NULL; +#endif +#ifdef DEBUG + cl_dumpport(channel); +#endif + + return 0; +} +int clread (dev, uio, flag) + dev_t dev; + struct uio *uio; +int flag; +{ + int unit, channel; + struct tty *tp; + struct cl_info *cl; + struct clsoftc *sc; + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return (ENODEV); + } + channel = CL_CHANNEL(dev); + cl = &sc->sc_cl[channel]; + tp = cl->tty; + if (!tp) + return ENXIO; + return((*linesw[tp->t_line].l_read)(tp, uio, flag)); +} +int clwrite (dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + int unit, channel; + struct tty *tp; + struct cl_info *cl; + struct clsoftc *sc; + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return (ENODEV); + } + channel = CL_CHANNEL(dev); + cl = &sc->sc_cl[channel]; + tp = cl->tty; + if (!tp) + return ENXIO; + return((*linesw[tp->t_line].l_write)(tp, uio, flag)); +} +int clioctl (dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + int error; + int unit, channel; + struct tty *tp; + struct cl_info *cl; + struct clsoftc *sc; + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return (ENODEV); + } + channel = CL_CHANNEL(dev); + cl = &sc->sc_cl[channel]; + tp = cl->tty; + if (!tp) + return ENXIO; + + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return(error); + + error = ttioctl(tp, cmd, data, flag, p); + if (error >= 0) + return(error); + + switch (cmd) { + case TIOCSBRK: + /* */ + break; + + case TIOCCBRK: + /* */ + break; + + case TIOCSDTR: + (void) clmctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIS); + break; + + case TIOCCDTR: + (void) clmctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIC); + break; + + case TIOCMSET: + (void) clmctl(dev, *(int *) data, DMSET); + break; + + case TIOCMBIS: + (void) clmctl(dev, *(int *) data, DMBIS); + break; + + case TIOCMBIC: + (void) clmctl(dev, *(int *) data, DMBIC); + break; + + case TIOCMGET: + *(int *)data = clmctl(dev, 0, DMGET); + break; + case TIOCGFLAGS: + *(int *)data = cl->cl_swflags; + break; + case TIOCSFLAGS: + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) + return(EPERM); + + cl->cl_swflags = *(int *)data; + cl->cl_swflags &= /* only allow valid flags */ + (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | TIOCFLAG_CRTSCTS); + break; + default: + return(ENOTTY); + } + + return 0; +} +int +clstop(tp, flag) + struct tty *tp; + int flag; +{ + int s; + + s = splcl(); + if (tp->t_state & TS_BUSY) { + if ((tp->t_state & TS_TTSTOP) == 0) + tp->t_state |= TS_FLUSH; + } + splx(s); + return 0; +} + +int +clcnprobe(cp) + struct consdev *cp; +{ + /* always there ? */ + /* serial major */ + int maj; + + /* locate the major number */ + for (maj = 0; maj < nchrdev; maj++) + if (cdevsw[maj].d_open == clopen) + break; + cp->cn_dev = makedev (maj, 0); + cp->cn_pri = CN_NORMAL; + + return 1; +} + +int +clcninit(cp) +struct consdev *cp; +{ +#ifdef MAP_DOES_WORK + int size = (0x1ff + PGOFSET) & ~PGOFSET; + int pcc2_size = (0x3C + PGOFSET) & ~PGOFSET; +#endif + volatile u_char *cd_base; + + cl_cons.cl_paddr = (void *)0xfff45000; +#ifdef MAP_DOES_WORK + cl_cons.cl_vaddr = mapiodev(cl_cons.cl_paddr,size); + cd_pcc2_base = mapiodev(0xfff42000,pcc2_size); +#else + cl_cons.cl_vaddr = cl_cons.cl_paddr; + cl_cons.pcctwoaddr = (void *)0xfff42000; +#endif + cd_base = cl_cons.cl_vaddr; + /* reset the chip? */ +#ifdef CLCD_DO_RESET +#endif +#ifdef NEW_CLCD_STRUCT + /* set up globals */ + cl->tftc = 0x10; + cl->tpr = CL_TIMEOUT; /* is this correct?? */ + cl->rpilr = 0x03; + cl->tpilr = 0x02; + cl->mpilr = 0x01; + + /* set up the tty00 to be 9600 8N1 */ + cl->car = 0x00; + cl->cor1 = 0x17; /* No parity, ignore parity, 8 bit char */ + cl->cor2 = 0x00; + cl->cor3 = 0x02; /* 1 stop bit */ + cl->cor4 = 0x00; + cl->cor5 = 0x00; + cl->cor6 = 0x00; + cl->cor7 = 0x00; + cl->schr1 = 0x00; + cl->schr2 = 0x00; + cl->schr3 = 0x00; + cl->schr4 = 0x00; + cl->scrl = 0x00; + cl->scrh = 0x00; + cl->lnxt = 0x00; + cl->cpsr = 0x00; +#else + /* set up globals */ +#ifdef NOT_ALREADY_SETUP + cd_base[CD2400_TFTC] = 0x10; + cd_base[CD2400_TPR] = CL_TIMEOUT; /* is this correct?? */ + cd_base[CD2400_RPILR] = 0x03; + cd_base[CD2400_TPILR] = 0x02; + cd_base[CD2400_MPILR] = 0x01; + + /* set up the tty00 to be 9600 8N1 */ + cd_base[CD2400_CAR] = 0x00; + cd_base[CD2400_COR1] = 0x17; /* No parity, ignore parity, 8 bit char */ + cd_base[CD2400_COR2] = 0x00; + cd_base[CD2400_COR3] = 0x02; /* 1 stop bit */ + cd_base[CD2400_COR4] = 0x00; + cd_base[CD2400_COR5] = 0x00; + cd_base[CD2400_COR6] = 0x00; + cd_base[CD2400_COR7] = 0x00; + cd_base[CD2400_SCHR1] = 0x00; + cd_base[CD2400_SCHR2] = 0x00; + cd_base[CD2400_SCHR3] = 0x00; + cd_base[CD2400_SCHR4] = 0x00; + cd_base[CD2400_SCRl] = 0x00; + cd_base[CD2400_SCRh] = 0x00; + cd_base[CD2400_LNXT] = 0x00; + cd_base[CD2400_CPSR] = 0x00; +#endif +#endif + return 0; +} + +int +cl_instat(sc) + struct clsoftc *sc; +{ + volatile u_char *cd_base; + if ( NULL == sc) { + cd_base = cl_cons.cl_vaddr; + } else { + cd_base = sc->vbase; + } + return (cd_base[CD2400_RIR] & 0x80); +} +int +clcngetc(dev) + dev_t dev; +{ + u_char val, reoir, licr, isrl, data, status, fifo_cnt; + int got_char = 0; + volatile u_char *cd_base = cl_cons.cl_vaddr; + volatile struct pcctworeg *pcc2_base = cl_cons.pcctwoaddr; + while (got_char == 0) { + val = cd_base[CD2400_RIR]; + /* if no receive interrupt pending wait */ + if (!(val & 0x80)) { + continue; + } + /* XXX do we need to suck the entire FIFO contents? */ + reoir = pcc2_base->pcc2_sccrxiack; /* receive PIACK */ + licr = cd_base[CD2400_LICR]; + if (((licr >> 2) & 0x3) == 0) { + /* is the interrupt for us (port 0) */ + /* the character is for us yea. */ + isrl = cd_base[CD2400_RISRl]; +#if 0 + if (isrl & 0x01) { + status = BREAK; + } + if (isrl & 0x02) { + status = FRAME; + } + if (isrl & 0x04) { + status = PARITY; + } + if (isrl & 0x08) { + status = OVERFLOW; + } + /* we do not have special characters ;-) */ +#endif + fifo_cnt = cd_base[CD2400_RFOC]; + data = cd_base[CD2400_RDR]; + got_char = 1; + cd_base[CD2400_TEOIR] = 0x00; + } else { + data = cd_base[CD2400_RDR]; + cd_base[CD2400_TEOIR] = 0x00; + } + + } + + return data; +} + +int +clcnputc(dev, c) + dev_t dev; + char c; +{ + /* is this the correct location for the cr -> cr/lf tranlation? */ + if (c == '\n') + clputc(0, 0, '\r'); + + clputc(0, 0, c); + return 0; +} +clcnpollc(dev, on) + dev_t dev; + int on; +{ + if (1 == on) { + /* enable polling */ + } else { + /* disable polling */ + } + return; +} +static void +clputc(sc, unit, c) + struct clsoftc *sc; + int unit; + char c; +{ + int s; + u_char schar; + u_char oldchannel; + volatile u_char *cd_base; + if (0 == sc) { + /* output on console */ + cd_base = cl_cons.cl_vaddr; + } else { + cd_base = sc->vbase; + } +#ifdef NEW_CLCD_STRUCT + /* should we disable, flush and all that goo? */ + cl->car = unit; + schar = cl->schr3; + cl->schr3 = c; + cl->stcr = 0x08 | 0x03; /* send special char, char 3 */ + while (0 != cl->stcr) { + /* wait until cl notices the command + * otherwise it may not notice the character + * if we send characters too fast. + */ + } + cl->schr3 = schar; +#else +if (unit == 0) { + s = splhigh(); + oldchannel = cd_base[CD2400_CAR]; + cd_base[CD2400_CAR] = unit; + schar = cd_base[CD2400_SCHR3]; + cd_base[CD2400_SCHR3] = c; + cd_base[CD2400_STCR] = 0x08 | 0x03; /* send special char, char 3 */ + while (0 != cd_base[CD2400_STCR]) { + /* wait until cl notices the command + * otherwise it may not notice the character + * if we send characters too fast. + */ + } + DELAY(5); + cd_base[CD2400_SCHR3] = schar; + cd_base[CD2400_CAR] = oldchannel; + splx(s); +} else { + s = splhigh(); + oldchannel = cd_base[CD2400_CAR]; + cd_base[CD2400_CAR] = unit; + if (cd_base[CD2400_TFTC] > 0) { + cd_base[CD2400_TDR] = c; + } + cd_base[CD2400_CAR] = oldchannel; + splx(s); +} +#endif + return; +} + +/* +#ifdef CLCD_DO_POLLED_INPUT +*/ +#if 1 +void +cl_chkinput() +{ + struct tty *tp; + int unit; + struct clsoftc *sc; + int channel; + + if (dopoll == 0) + return; + for (unit = 0; unit < clcd.cd_ndevs; unit++) { + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + continue; + } + if (cl_instat(sc)) { + while (cl_instat(sc)){ + int ch; + u_char c; + /* + *(pinchar++) = clcngetc(); + */ + ch = clgetc(sc,&channel) & 0xff; + c = ch; + + tp = sc->sc_cl[channel].tty; + if (NULL != tp) { + (*linesw[tp->t_line].l_rint)(c,tp); + } + } + /* + wakeup(tp); + */ + } + } +} +#endif +static u_char +clgetc(sc, channel) + struct clsoftc *sc; + int *channel; +{ + volatile u_char *cd_base; + volatile struct pcctworeg *pcc2_base; + u_char val, reoir, licr, isrl, fifo_cnt, data; + if (0 == sc) { + cd_base = cl_cons.cl_vaddr; + pcc2_base = cl_cons.pcctwoaddr; + } else { + cd_base = sc->vbase; + pcc2_base = sc->sc_pcctwo; + } + val = cd_base[CD2400_RIR]; + /* if no receive interrupt pending wait */ + if (!(val & 0x80)) { + return 0; + } + /* XXX do we need to suck the entire FIFO contents? */ + reoir = pcc2_base->pcc2_sccrxiack; /* receive PIACK */ + licr = cd_base[CD2400_LICR]; + *channel = (licr >> 2) & 0x3; + /* is the interrupt for us (port 0) */ + /* the character is for us yea. */ + isrl = cd_base[CD2400_RISRl]; +#if 0 + if (isrl & 0x01) { + status = BREAK; + } + if (isrl & 0x02) { + status = FRAME; + } + if (isrl & 0x04) { + status = PARITY; + } + if (isrl & 0x08) { + status = OVERFLOW; + } + /* we do not have special characters ;-) */ +#endif + fifo_cnt = cd_base[CD2400_RFOC]; + if (fifo_cnt > 0) { + data = cd_base[CD2400_RDR]; + cd_base[CD2400_TEOIR] = 0x00; + } else { + data = 0; + cd_base[CD2400_TEOIR] = 0x08; + } + return data; +} +int +clccparam(sc, par, channel) + struct clsoftc *sc; + struct termios *par; + int channel; +{ + u_int divisor, clk, clen; + int s, imask, ints; + + s = splcl(); + sc->vbase[CD2400_CAR] = channel; + if (par->c_ospeed == 0) { + /* dont kill the console */ + if(sc->sc_cl[channel].cl_consio == 0) { + /* disconnect, drop RTS DTR stop reciever */ + sc->vbase[CD2400_MSVR_RTS] = 0x00; + sc->vbase[CD2400_MSVR_DTR] = 0x00; + sc->vbase[CD2400_CCR] = 0x05; + } + splx(s); + return (0xff); + } + + sc->vbase[CD2400_MSVR_RTS] = 0x03; + sc->vbase[CD2400_MSVR_DTR] = 0x03; + + divisor = cl_clkdiv(par->c_ospeed); + clk = cl_clknum(par->c_ospeed); + sc->vbase[CD2400_TBPR] = divisor; + sc->vbase[CD2400_TCOR] = clk << 5; + divisor = cl_clkdiv(par->c_ispeed); + clk = cl_clknum(par->c_ispeed); + sc->vbase[CD2400_RBPR] = divisor; + sc->vbase[CD2400_RCOR] = clk; + sc->vbase[CD2400_RTPRl] = cl_clkrxtimeout(par->c_ispeed); + sc->vbase[CD2400_RTPRh] = 0x00; + + switch (par->c_cflag & CSIZE) { + case CS5: + clen = 4; /* this is the mask for the chip. */ + imask = 0x1F; + break; + case CS6: + clen = 5; + imask = 0x3F; + break; + case CS7: + clen = 6; + imask = 0x7F; + break; + default: + clen = 7; + imask = 0xFF; + } + sc->vbase[CD2400_COR3] = par->c_cflag & PARENB ? 4 : 2; + + if (par->c_cflag & PARENB) { + if (par->c_cflag & PARODD) { + sc->vbase[CD2400_COR1] = 0xE0 | clen ; /* odd */ + } else { + sc->vbase[CD2400_COR1] = 0x40 | clen ; /* even */ + } + } else { + sc->vbase[CD2400_COR1] = 0x10 | clen; /* ignore parity */ + } + + if (sc->sc_cl[channel].cl_consio == 0 + && (par->c_cflag & CREAD) == 0 ) + { + sc->vbase[CD2400_CCR] = 0x08; + } else { + sc->vbase[CD2400_CCR] = 0x0a; + } + ints = 0; +#define SCC_DSR 0x80 +#define SCC_DCD 0x40 +#define SCC_CTS 0x20 + if ((par->c_cflag & CLOCAL) == 0) { + ints |= SCC_DCD; + } + if ((par->c_cflag & CCTS_OFLOW) != 0) { + ints |= SCC_CTS; + } + if ((par->c_cflag & CRTSCTS) != 0) { + ints |= SCC_CTS; + } +#ifdef DONT_LET_HARDWARE + if ((par->c_cflag & CCTS_IFLOW) != 0) { + ints |= SCC_DSR; + } +#endif + sc->vbase[CD2400_COR4] = ints | CL_FIFO_CNT; + sc->vbase[CD2400_COR5] = ints | CL_FIFO_CNT; + + return imask; +} +static int clknum = 0; +u_char +cl_clkdiv(speed) + int speed; +{ + int i = 0; + if (cl_clocks[clknum].speed == speed) { + return cl_clocks[clknum].divisor; + } + for (i = 0; cl_clocks[i].speed != 0; i++) { + if (cl_clocks[i].speed == speed) { + clknum = i; + return cl_clocks[clknum].divisor; + } + } + /* return some sane value if unknown speed */ + return cl_clocks[4].divisor; +} +u_char +cl_clknum(speed) + int speed; +{ + int found = 0; + int i = 0; + if (cl_clocks[clknum].speed == speed) { + return cl_clocks[clknum].clock; + } + for (i = 0; found != 0 && cl_clocks[i].speed != 0; i++) { + if (cl_clocks[clknum].speed == speed) { + clknum = i; + return cl_clocks[clknum].clock; + } + } + /* return some sane value if unknown speed */ + return cl_clocks[4].clock; +} +u_char +cl_clkrxtimeout(speed) + int speed; +{ + int i = 0; + if (cl_clocks[clknum].speed == speed) { + return cl_clocks[clknum].rx_timeout; + } + for (i = 0; cl_clocks[i].speed != 0; i++) { + if (cl_clocks[i].speed == speed) { + clknum = i; + return cl_clocks[clknum].rx_timeout; + } + } + /* return some sane value if unknown speed */ + return cl_clocks[4].rx_timeout; +} +void +cl_unblock(tp) + struct tty *tp; +{ + tp->t_state &= ~TS_FLUSH; + if (tp->t_outq.c_cc != 0) + clstart(tp); +} +void +clstart(tp) + struct tty *tp; +{ + dev_t dev; + u_char cbuf; + struct clsoftc *sc; + int channel, unit, s, cnt; + + dev = tp->t_dev; + channel = CL_CHANNEL(dev); +/* hack to test output on non console only */ +#if 0 + if (channel == 0) { + cloutput(tp); + return; + } +#endif + unit = CL_UNIT(dev); + if (unit >= clcd.cd_ndevs || + (sc = (struct clsoftc *) clcd.cd_devs[unit]) == NULL) { + return; + } + + if ((tp->t_state & TS_ISOPEN) == 0) + return; + + s = splcl(); +#if 0 + if (sc->sc_cl[channel].transmitting == 1) { + /* i'm busy, go away, I will get to it later. */ + splx(s); + return; + } + cnt = q_to_b(&tp->t_outq, &cbuf, 1); + if ( cnt != 0 ) { + sc->sc_cl[channel].transmitting = 1; + sc->vbase[CD2400_CAR] = channel; + sc->vbase[CD2400_TDR] = cbuf; + } else { + sc->sc_cl[channel].transmitting = 0; + } +#else + if ((tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP | TS_FLUSH)) == 0) + { + tp->t_state |= TS_BUSY; + sc->vbase[CD2400_CAR] = channel; + sc->vbase[CD2400_IER] = sc->vbase[CD2400_IER] | 0x3; + } +#endif + splx(s); + return; +} +int +cl_mintr(sc) + struct clsoftc *sc; +{ + u_char mir, misr, msvr; + int channel; + struct tty *tp; + if(((mir = sc->vbase[CD2400_MIR]) & 0x40) == 0x0) { + /* only if intr is not shared? */ + printf("cl_mintr extra intr\n"); + return 0; + } + sc->sc_mxintrcnt.ev_count++; + + channel = mir & 0x03; + misr = sc->vbase[CD2400_MISR]; + msvr = sc->vbase[CD2400_MSVR_RTS]; + if (misr & 0x01) { + /* timers are not currently used?? */ + printf ("cl_mintr: channel %x timer 1 unexpected\n",channel); + } + if (misr & 0x02) { + /* timers are not currently used?? */ + printf ("cl_mintr: channel %x timer 2 unexpected\n",channel); + } + if (misr & 0x20) { + printf ("cl_mintr: channel %x cts %x\n",channel, + ((msvr & 0x20) != 0x0) + ); + } + if (misr & 0x40) { + struct tty *tp = sc->sc_cl[channel].tty; + printf ("cl_mintr: channel %x cd %x\n",channel, + ((msvr & 0x40) != 0x0) + ); + ttymodem(tp, ((msvr & 0x40) != 0x0) ); + } + if (misr & 0x80) { + printf ("cl_mintr: channel %x dsr %x\n",channel, + ((msvr & 0x80) != 0x0) + ); + } + sc->vbase[CD2400_MEOIR] = 0x00; + return 1; +} + +int +cl_txintr(sc) + struct clsoftc *sc; +{ + static empty = 0; + u_char tir, licr, teoir; + u_char max; + int channel; + struct tty *tp; + int cnt; + u_char buffer[CL_FIFO_MAX +1]; + u_char *tptr; + if(((tir = sc->vbase[CD2400_TIR]) & 0x40) == 0x0) { + /* only if intr is not shared ??? */ + printf ("cl_txintr extra intr\n"); + return 0; + } + sc->sc_txintrcnt.ev_count++; + + channel = tir & 0x03; + licr = sc->vbase[CD2400_LICR]; + + sc->sc_cl[channel].txcnt ++; + + tp = sc->sc_cl[channel].tty; + if (tp == NULL || (tp->t_state & TS_ISOPEN) == 0) { + sc->vbase[CD2400_TEOIR] = 0x08; + return 1; + } + switch ((licr >> 4)& 0x3) { + case CL_DMAMODE: + teoir = 0x08; + break; + case CL_INTRMODE: + max = sc->vbase[CD2400_TFTC]; + cnt = min ((int)max,tp->t_outq.c_cc); + if (cnt != 0) { + cnt = q_to_b(&tp->t_outq, buffer, cnt); + empty = 0; + for (tptr = buffer; tptr < &buffer[cnt]; tptr++) { + sc->vbase[CD2400_TDR]= *tptr; + } + teoir = 0x00; + } else { + if (empty > 5 && ((empty % 20000 )== 0)) { + printf("cl_txintr to many empty intr %d channel %d\n", + empty, channel); + } + empty++; + teoir = 0x08; + if (tp->t_state & TS_BUSY) { + tp->t_state &= ~(TS_BUSY | TS_FLUSH); + if (tp->t_state & TS_ASLEEP) { + tp->t_state &= ~TS_ASLEEP; + wakeup((caddr_t) &tp->t_outq); + } + selwakeup(&tp->t_wsel); + } + sc->vbase[CD2400_IER] = sc->vbase[CD2400_IER] & ~0x3; + } + break; + default: + printf("cl_txintr unknown mode %x\n", ((licr >> 4) & 0x3)); + /* we probably will go to hell quickly now */ + teoir = 0x08; + } + sc->vbase[CD2400_TEOIR] = teoir; + return 1; +} + +int +cl_rxintr(sc) + struct clsoftc *sc; +{ + u_char rir, channel, licr, risrl; + u_char c; + u_char fifocnt; + struct tty *tp; + int i; + u_char reoir; + + if(((rir = sc->vbase[CD2400_RIR]) & 0x40) == 0x0) { + /* only if intr is not shared ??? */ + printf ("cl_rxintr extra intr\n"); + return 0; + } + sc->sc_rxintrcnt.ev_count++; + channel = rir & 0x3; + licr = sc->vbase[CD2400_LICR]; + reoir = 0x08; + + sc->sc_cl[channel].rxcnt ++; + + switch (licr & 0x03) { + case CL_DMAMODE: + reoir = 0x08; + break; + case CL_INTRMODE: + risrl = sc->vbase[CD2400_RISRl]; + if (risrl & 0x80) { + /* timeout, no characters */ + reoir = 0x08; + } else + /* We don't need no sinkin special characters */ + if (risrl & 0x08) { + cl_overflow (sc, channel, &sc->sc_fotime, "fifo"); + reoir = 0x08; + } else + if (risrl & 0x04) { + cl_parity(sc, channel); + reoir = 0x08; + } else + if (risrl & 0x02) { + cl_frame(sc, channel); + reoir = 0x08; + } else + if (risrl & 0x01) { + cl_break(sc, channel); + reoir = 0x08; + } else { + fifocnt = sc->vbase[CD2400_RFOC]; + tp = sc->sc_cl[channel].tty; + for (i = 0; i < fifocnt; i++) { + c = sc->vbase[CD2400_RDR]; +#if USE_BUFFER + cl_appendbuf(sc, channel, c); +#else + /* does any restricitions exist on spl + * for this call + */ + (*linesw[tp->t_line].l_rint)(c,tp); + reoir = 0x00; +#endif + } + } + break; + default: + printf("cl_rxintr unknown mode %x\n",licr & 0x03); + /* we probably will go to hell quickly now */ + reoir = 0x08; + } + sc->vbase[CD2400_REOIR] = reoir; + return 1; +} + +void +cl_overflow (sc, channel, ptime, msg) +struct clsoftc *sc; +int channel; +long *ptime; +char *msg; +{ +/* + if (*ptime != time.tv_sec) { +*/ + { +/* + *ptime = time.tv_sec); +*/ + log(LOG_WARNING, "%s%d[%d]: %s overrun", clcd.cd_name, + 0 /* fix */, channel, msg); + } + return; +} +void +cl_parity (sc, channel) + struct clsoftc *sc; + int channel; +{ + log(LOG_WARNING, "%s%d[%d]: parity error", clcd.cd_name, 0, channel); + return; +} +void +cl_frame (sc, channel) + struct clsoftc *sc; + int channel; +{ + log(LOG_WARNING, "%s%d[%d]: frame error", clcd.cd_name, 0, channel); + return; +} +void +cl_break (sc, channel) + struct clsoftc *sc; + int channel; +{ + log(LOG_WARNING, "%s%d[%d]: break detected", clcd.cd_name, 0, channel); + return; +} + +void +cl_dumpport0() +{ + cl_dumpport(0); + return; +} +void +cl_dumpport1() +{ + cl_dumpport(1); + return; +} +void +cl_dumpport2() +{ + cl_dumpport(2); + return; +} +void +cl_dumpport3() +{ + cl_dumpport(3); + return; +} + +void +cl_dumpport(channel) + int channel; +{ + u_char livr, cmr, cor1, cor2, cor3, cor4, cor5, cor6, cor7, + schr1, schr2, schr3, schr4, scrl, scrh, lnxt, + rbpr, rcor, tbpr, tcor, rpilr, rir, tpr, ier, ccr, + csr, rts, dtr, rtprl, rtprh; + struct clsoftc *sc; + + volatile u_char *cd_base; + int s; + + cd_base = cl_cons.cl_vaddr; + + sc = (struct clsoftc *) clcd.cd_devs[0]; + + s = splcl(); + cd_base[CD2400_CAR] = (char) channel; + livr = cd_base[CD2400_LIVR]; + cmr = cd_base[CD2400_CMR]; + cor1 = cd_base[CD2400_COR1]; + cor2 = cd_base[CD2400_COR2]; + cor3 = cd_base[CD2400_COR3]; + cor4 = cd_base[CD2400_COR4]; + cor5 = cd_base[CD2400_COR5]; + cor6 = cd_base[CD2400_COR6]; + cor7 = cd_base[CD2400_COR7]; + schr1 = cd_base[CD2400_SCHR1]; + schr2 = cd_base[CD2400_SCHR2]; + schr3 = cd_base[CD2400_SCHR3]; + schr4 = cd_base[CD2400_SCHR4]; + scrl = cd_base[CD2400_SCRl]; + scrh = cd_base[CD2400_SCRh]; + lnxt = cd_base[CD2400_LNXT]; + rbpr = cd_base[CD2400_RBPR]; + rcor = cd_base[CD2400_RCOR]; + tbpr = cd_base[CD2400_TBPR]; + rpilr = cd_base[CD2400_RPILR]; + ier = cd_base[CD2400_IER]; + ccr = cd_base[CD2400_CCR]; + tcor = cd_base[CD2400_TCOR]; + csr = cd_base[CD2400_CSR]; + tpr = cd_base[CD2400_TPR]; + rts = cd_base[CD2400_MSVR_RTS]; + dtr = cd_base[CD2400_MSVR_DTR]; + rtprl = cd_base[CD2400_RTPRl]; + rtprh = cd_base[CD2400_RTPRh]; + splx(s); + + printf("{ port %x livr %x cmr %x\n", + channel,livr, cmr); + printf("cor1 %x cor2 %x cor3 %x cor4 %x cor5 %x cor6 %x cor7 %x\n", + cor1, cor2, cor3, cor4, cor5, cor6, cor7); + printf("schr1 %x schr2 %x schr3 %x schr4 %x\n", + schr1, schr2, schr3, schr4); + printf("scrl %x scrh %x lnxt %x\n", + scrl, scrh, lnxt); + printf("rbpr %x rcor %x tbpr %x tcor %x\n", + rbpr, rcor, tbpr, tcor); + printf("rpilr %x rir %x ier %x ccr %x\n", + rpilr, rir, ier, ccr); + printf("tpr %x csr %x rts %x dtr %x\n", + tpr, csr, rts, dtr); + printf("rtprl %x rtprh %x\n", + rtprl, rtprh); + printf("rxcnt %x txcnt %x\n", + sc->sc_cl[channel].rxcnt, sc->sc_cl[channel].txcnt); + printf("}\n"); + return; +} diff --git a/sys/arch/mvme68k/dev/clock.c b/sys/arch/mvme68k/dev/clock.c new file mode 100644 index 00000000000..e4871cc2db3 --- /dev/null +++ b/sys/arch/mvme68k/dev/clock.c @@ -0,0 +1,393 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * 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, Lawrence Berkeley Laboratory. + * + * 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 8.1 (Berkeley) 6/11/93 + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/device.h> + +#include <machine/psl.h> +#include <machine/autoconf.h> +#include <machine/cpu.h> + +#include "pcc.h" +#include "mc.h" +#include "pcctwo.h" + +#if NPCC > 0 +#include <mvme68k/dev/pccreg.h> +#endif +#if NPCCTWO > 0 +#include <mvme68k/dev/pcctworeg.h> +#endif +#if NMC > 0 +#include <mvme68k/dev/mcreg.h> +#endif + +#if defined(GPROF) +#include <sys/gmon.h> +#endif + +/* + * Statistics clock interval and variance, in usec. Variance must be a + * power of two. Since this gives us an even number, not an odd number, + * we discard one case and compensate. That is, a variance of 8192 would + * give us offsets in [0..8191]. Instead, we take offsets in [1..8191]. + * This is symmetric about the point 2048, or statvar/2, and thus averages + * to that value (assuming uniform random numbers). + */ +int statvar = 8192; +int statmin; /* statclock interval - 1/2*variance */ + +struct clocksoftc { + struct device sc_dev; + struct intrhand sc_profih; + struct intrhand sc_statih; +}; + +void clockattach __P((struct device *, struct device *, void *)); +int clockmatch __P((struct device *, void *, void *)); + +struct cfdriver clockcd = { + NULL, "clock", clockmatch, clockattach, + DV_DULL, sizeof(struct clocksoftc), 0 +}; + +int clockintr __P((void *)); +int statintr __P((void *)); + +int clockbus; +u_char stat_reset, prof_reset; + +/* + * Every machine must have a clock tick device of some sort; for this + * platform this file manages it, no matter what form it takes. + */ +int +clockmatch(parent, vcf, args) + struct device *parent; + void *vcf, *args; +{ + return (1); +} + +void +clockattach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct confargs *ca = args; + struct clocksoftc *sc = (struct clocksoftc *)self; + + sc->sc_profih.ih_fn = clockintr; + sc->sc_profih.ih_arg = 0; + sc->sc_profih.ih_wantframe = 1; + sc->sc_profih.ih_ipl = ca->ca_ipl; + + sc->sc_statih.ih_fn = statintr; + sc->sc_statih.ih_arg = 0; + sc->sc_statih.ih_wantframe = 1; + sc->sc_statih.ih_ipl = ca->ca_ipl; + + clockbus = ca->ca_bustype; + switch (ca->ca_bustype) { +#if NPCC > 0 + case BUS_PCC: + prof_reset = ca->ca_ipl | PCC_IRQ_IEN | PCC_TIMERACK; + stat_reset = ca->ca_ipl | PCC_IRQ_IEN | PCC_TIMERACK; + pccintr_establish(PCCV_TIMER1, &sc->sc_profih); + pccintr_establish(PCCV_TIMER2, &sc->sc_statih); + break; +#endif +#if NMC > 0 + case BUS_MC: + prof_reset = ca->ca_ipl | MC_IRQ_IEN | MC_IRQ_ICLR; + stat_reset = ca->ca_ipl | MC_IRQ_IEN | MC_IRQ_ICLR; + mcintr_establish(MCV_TIMER1, &sc->sc_profih); + mcintr_establish(MCV_TIMER2, &sc->sc_statih); + break; +#endif +#if NPCCTWO > 0 + case BUS_PCCTWO: + prof_reset = ca->ca_ipl | PCC2_IRQ_IEN | PCC2_IRQ_ICLR; + stat_reset = ca->ca_ipl | PCC2_IRQ_IEN | PCC2_IRQ_ICLR; + pcctwointr_establish(PCC2V_TIMER1, &sc->sc_profih); + pcctwointr_establish(PCC2V_TIMER2, &sc->sc_statih); + break; +#endif + } + + printf("\n"); +} + +/* + * clockintr: ack intr and call hardclock + */ +int +clockintr(arg) + void *arg; +{ + switch (clockbus) { +#if NPCC > 0 + case BUS_PCC: + sys_pcc->pcc_t1irq = prof_reset; + break; +#endif +#if NMC > 0 + case BUS_MC: + sys_mc->mc_t1irq = prof_reset; + break; +#endif +#if NPCCTWO > 0 + case BUS_PCCTWO: + sys_pcc2->pcc2_t1irq = prof_reset; + break; +#endif + } + hardclock(arg); + return (1); +} + +/* + * Set up real-time clock; we don't have a statistics clock at + * present. + */ +cpu_initclocks() +{ + register int statint, minint; + + if (1000000 % hz) { + printf("cannot get %d Hz clock; using 100 Hz\n", hz); + hz = 100; + tick = 1000000 / hz; + } + if (stathz == 0) + stathz = hz; + if (1000000 % stathz) { + printf("cannot get %d Hz statclock; using 100 Hz\n", stathz); + stathz = 100; + } + profhz = stathz; /* always */ + + statint = 1000000 / stathz; + minint = statint / 2 + 100; + while (statvar > minint) + statvar >>= 1; + switch (clockbus) { +#if NPCC > 0 + case BUS_PCC: + sys_pcc->pcc_t1pload = pcc_timer_us2lim(tick); + sys_pcc->pcc_t1ctl = PCC_TIMERCLEAR; + sys_pcc->pcc_t1ctl = PCC_TIMERSTART; + sys_pcc->pcc_t1irq = prof_reset; + + sys_pcc->pcc_t2pload = pcc_timer_us2lim(statint); + sys_pcc->pcc_t2ctl = PCC_TIMERCLEAR; + sys_pcc->pcc_t2ctl = PCC_TIMERSTART; + sys_pcc->pcc_t2irq = stat_reset; + break; +#endif +#if NMC > 0 + case BUS_MC: + /* profclock */ + sys_mc->mc_t1ctl = 0; + sys_mc->mc_t1cmp = mc_timer_us2lim(tick); + sys_mc->mc_t1count = 0; + sys_mc->mc_t1ctl = MC_TCTL_CEN | MC_TCTL_COC | MC_TCTL_COVF; + sys_mc->mc_t1irq = prof_reset; + + /* statclock */ + sys_mc->mc_t2ctl = 0; + sys_mc->mc_t2cmp = mc_timer_us2lim(statint); + sys_mc->mc_t2count = 0; + sys_mc->mc_t2ctl = MC_TCTL_CEN | MC_TCTL_COC | MC_TCTL_COVF; + sys_mc->mc_t2irq = stat_reset; + break; +#endif +#if NPCCTWO > 0 + case BUS_PCCTWO: + /* profclock */ + sys_pcc2->pcc2_t1ctl = 0; + sys_pcc2->pcc2_t1cmp = pcc2_timer_us2lim(tick); + sys_pcc2->pcc2_t1count = 0; + sys_pcc2->pcc2_t1ctl = PCC2_TCTL_CEN | PCC2_TCTL_COC | + PCC2_TCTL_COVF; + sys_pcc2->pcc2_t1irq = prof_reset; + + /* statclock */ + sys_pcc2->pcc2_t2ctl = 0; + sys_pcc2->pcc2_t2cmp = pcc2_timer_us2lim(statint); + sys_pcc2->pcc2_t2count = 0; + sys_pcc2->pcc2_t2ctl = PCC2_TCTL_CEN | PCC2_TCTL_COC | + PCC2_TCTL_COVF; + sys_pcc2->pcc2_t2irq = stat_reset; + break; +#endif + } + statmin = statint - (statvar >> 1); +} + +void +setstatclockrate(newhz) + int newhz; +{ +} + +int +statintr(cap) + void *cap; +{ + register u_long newint, r, var; + + switch (clockbus) { +#if NPCC > 0 + case BUS_PCC: + sys_pcc->pcc_t2irq = stat_reset; + break; +#endif +#if NMC > 0 + case BUS_MC: + sys_mc->mc_t2irq = stat_reset; + break; +#endif +#if NPCCTWO > 0 + case BUS_PCCTWO: + sys_pcc2->pcc2_t2irq = stat_reset; + break; +#endif + } + + statclock((struct clockframe *)cap); + + /* + * Compute new randomized interval. The intervals are uniformly + * distributed on [statint - statvar / 2, statint + statvar / 2], + * and therefore have mean statint, giving a stathz frequency clock. + */ + var = statvar; + do { + r = random() & (var - 1); + } while (r == 0); + newint = statmin + r; + + switch (clockbus) { +#if NPCC > 0 + case BUS_PCC: + sys_pcc->pcc_t2pload = pcc_timer_us2lim(newint); + sys_pcc->pcc_t2ctl = PCC_TIMERCLEAR; + sys_pcc->pcc_t2ctl = PCC_TIMERSTART; + sys_pcc->pcc_t2irq = stat_reset; + break; +#endif +#if NMC > 0 + case BUS_MC: + sys_mc->mc_t2ctl = 0; + sys_mc->mc_t2cmp = mc_timer_us2lim(newint); + sys_mc->mc_t2count = 0; /* should I? */ + sys_mc->mc_t2irq = stat_reset; + sys_mc->mc_t2ctl = MC_TCTL_CEN | MC_TCTL_COC; + break; +#endif +#if NPCCTWO > 0 + case BUS_PCCTWO: + sys_pcc2->pcc2_t2ctl = 0; + sys_pcc2->pcc2_t2cmp = pcc2_timer_us2lim(newint); + sys_pcc2->pcc2_t2count = 0; /* should I? */ + sys_pcc2->pcc2_t2irq = stat_reset; + sys_pcc2->pcc2_t2ctl = PCC2_TCTL_CEN | PCC2_TCTL_COC; + break; +#endif + } + return (1); +} + +delay(us) + register int us; +{ + volatile register int c; + + switch (clockbus) { +#if NPCC > 0 + case BUS_PCC: + /* + * XXX MVME147 doesn't have a 3rd free-running timer, + * so we use a stupid loop. Fix the code to watch t1: + * the profiling timer. + */ + c = 2 * us; + while (--c > 0) + ; + return (0); +#endif +#if NMC > 0 + case BUS_MC: + /* + * Reset and restart a free-running timer 1MHz, watch + * for it to reach the required count. + */ + sys_mc->mc_t3irq = 0; + sys_mc->mc_t3ctl = 0; + sys_mc->mc_t3count = 0; + sys_mc->mc_t3ctl = MC_TCTL_CEN | MC_TCTL_COVF; + + while (sys_mc->mc_t3count < us) + ; + return (0); +#endif +#if NPCCTWO > 0 + case BUS_PCCTWO: + /* + * XXX MVME167 doesn't have a 3rd free-running timer, + * so we use a stupid loop. Fix the code to watch t1: + * the profiling timer. + */ + c = 4 * us; + while (--c > 0) + ; + return (0); +#endif + } +} diff --git a/sys/arch/mvme68k/dev/dmavar.h b/sys/arch/mvme68k/dev/dmavar.h new file mode 100644 index 00000000000..99da32b62c1 --- /dev/null +++ b/sys/arch/mvme68k/dev/dmavar.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 1982, 1990 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. + * + * @(#)dmavar.h 7.2 (Berkeley) 11/4/90 + * $Id: dmavar.h,v 1.1 1995/10/18 10:43:06 deraadt Exp $ + */ + +/* dmago flags */ +#define DMAGO_READ 0x08 /* transfer is a read */ +#define DMAGO_NOINT 0x80 /* don't interrupt on completion */ + +#ifdef KERNEL +typedef void (*dmafree_t) __P((void *dev)); +typedef int (*dmago_t) __P((void *dev, char *, int, int)); +typedef int (*dmanext_t) __P((void *dev)); +typedef void (*dmastop_t) __P((void *dev)); +#endif + diff --git a/sys/arch/mvme68k/dev/flash.c b/sys/arch/mvme68k/dev/flash.c new file mode 100644 index 00000000000..933b2a199a6 --- /dev/null +++ b/sys/arch/mvme68k/dev/flash.c @@ -0,0 +1,360 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/ioctl.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/tty.h> +#include <sys/uio.h> +#include <sys/callout.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/syslog.h> +#include <sys/fcntl.h> +#include <sys/device.h> +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <machine/mioctl.h> + +#include "mc.h" + +#if NMC > 0 +#include <mvme68k/dev/mcreg.h> +#endif + +#include <mvme68k/dev/flashreg.h> + +struct flashsoftc { + struct device sc_dev; + caddr_t sc_paddr; + volatile u_char * sc_vaddr; + u_char sc_manu; + u_char sc_ii; + int sc_len; + int sc_zonesize; +}; + +void flashattach __P((struct device *, struct device *, void *)); +int flashmatch __P((struct device *, void *, void *)); + +struct cfdriver flashcd = { + NULL, "flash", flashmatch, flashattach, + DV_DULL, sizeof(struct flashsoftc), 0 +}; + +int flashwritebyte __P((struct flashsoftc *sc, int addr, u_char val)); +int flasherasezone __P((struct flashsoftc *sc, int addr)); + +struct flashii intel_flashii[] = { + { "28F008SA", FLII_INTEL_28F008SA, 1024*1024, 64*1024 }, + { "28F008SA-L", FLII_INTEL_28F008SA_L, 1024*1024, 64*1024 }, + { NULL }, +}; + +struct flashmanu { + char *name; + u_char manu; + struct flashii *flashii; +} flashmanu[] = { + { "intel", FLMANU_INTEL, intel_flashii }, + { NULL } +}; + +int +flashmatch(parent, cf, args) + struct device *parent; + void *cf; + void *args; +{ + struct confargs *ca = args; + +#ifdef MVME147 + if (cputyp == CPU_147) + return (0); +#endif +#ifdef MVME167 + /* + * XXX: 166 has 4 byte-wide flash rams side-by-side, and + * isn't supported (yet). + */ + if (cputyp == CPU_166) + return (0); +#endif + + if (badpaddr(ca->ca_paddr, 1)) + return (0); + + /* + * XXX: need to determine if it is flash or rom + */ + return (1); +} + +void +flashattach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct flashsoftc *sc = (struct flashsoftc *)self; + struct confargs *ca = args; + int manu, ident; + + sc->sc_paddr = ca->ca_paddr; + sc->sc_vaddr = mapiodev(sc->sc_paddr, NBPG); + + switch (cputyp) { +#ifdef MVME162 + case CPU_162: + mc_enableflashwrite(1); + break; +#endif + } + + /* read manufacturer and product identifier from flash */ + sc->sc_vaddr[0] = FLCMD_RESET; + sc->sc_vaddr[0] = FLCMD_READII; + sc->sc_manu = sc->sc_vaddr[0]; + sc->sc_ii = sc->sc_vaddr[1]; + sc->sc_vaddr[0] = FLCMD_RESET; + + for (manu = 0; flashmanu[manu].name; manu++) + if (flashmanu[manu].manu == sc->sc_manu) + break; + if (flashmanu[manu].name == NULL) { + printf(": unknown manu 0x%02x ident %02x\n", + sc->sc_manu, sc->sc_ii); + return; + } + for (ident = 0; flashmanu[manu].flashii[ident].name; ident++) + if (flashmanu[manu].flashii[ident].ii == sc->sc_ii) + break; + if (flashmanu[manu].flashii[ident].name == NULL) { + printf(": unknown manu %s ident 0x%02x\n", + flashmanu[manu].name, sc->sc_ii); + return; + } + sc->sc_len = flashmanu[manu].flashii[ident].size; + sc->sc_zonesize = flashmanu[manu].flashii[ident].zonesize; + printf(": %s %s len %d", flashmanu[manu].name, + flashmanu[manu].flashii[ident].name, sc->sc_len); + + unmapiodev(sc->sc_vaddr, NBPG); + sc->sc_vaddr = mapiodev(sc->sc_paddr, sc->sc_len); + if (sc->sc_vaddr == NULL) { + sc->sc_len = 0; + printf(" -- failed to map"); + } + printf("\n"); +} + +/*ARGSUSED*/ +int +flashopen(dev, flag, mode) + dev_t dev; + int flag, mode; +{ + if (minor(dev) >= flashcd.cd_ndevs || + flashcd.cd_devs[minor(dev)] == NULL) + return (ENODEV); + return (0); +} + +/*ARGSUSED*/ +int +flashclose(dev, flag, mode) + dev_t dev; + int flag, mode; +{ + + return (0); +} + +/*ARGSUSED*/ +int +flashioctl(dev, cmd, data, flag, p) + dev_t dev; + caddr_t data; + int cmd, flag; + struct proc *p; +{ + int unit = minor(dev); + struct flashsoftc *sc = (struct flashsoftc *) flashcd.cd_devs[unit]; + int error = 0; + + switch (cmd) { + case MIOCGSIZ: + *(int *)data = sc->sc_len; + break; + default: + error = ENOTTY; + break; + } + return (error); +} + +/*ARGSUSED*/ +int +flashread(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + int unit = minor(dev); + struct flashsoftc *sc = (struct flashsoftc *) flashcd.cd_devs[unit]; + register vm_offset_t v; + register int c; + register struct iovec *iov; + int error = 0; + + while (uio->uio_resid > 0 && error == 0) { + iov = uio->uio_iov; + if (iov->iov_len == 0) { + uio->uio_iov++; + uio->uio_iovcnt--; + if (uio->uio_iovcnt < 0) + panic("flashrw"); + continue; + } + + v = uio->uio_offset; + c = min(iov->iov_len, MAXPHYS); + if (v + c > sc->sc_len) + c = sc->sc_len - v; /* till end of FLASH */ + if (c == 0) + return (0); + error = uiomove((caddr_t)sc->sc_vaddr + v, c, uio); + } + return (error); +} + +/*ARGSUSED*/ +int +flashwrite(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + return (ENXIO); +} + +int +flashmmap(dev, off, prot) + dev_t dev; + int off, prot; +{ + int unit = minor(dev); + struct flashsoftc *sc = (struct flashsoftc *) flashcd.cd_devs[unit]; + + /* allow access only in RAM */ + if (off > sc->sc_len) + return (-1); + return (m68k_btop(sc->sc_paddr + off)); +} + +int +flasherasezone(sc, addr) + struct flashsoftc *sc; + int addr; +{ + u_char sr; + + sc->sc_vaddr[addr] = FLCMD_ESETUP; + sc->sc_vaddr[addr] = FLCMD_ECONFIRM; + + /* XXX should use sleep/wakeup/timeout combination */ + do { + sc->sc_vaddr[0] = FLCMD_READSTAT; + sr = sc->sc_vaddr[0]; + } while (sr & FLSR_WSMS == 0); + if (sr & FLSR_ES) + return (-1); + return (0); +} + +/* + * Should add some light retry code. If a write fails see if an + * erase helps the situation... eventually flash rams become + * useless but perhaps we can get just one more cycle out of it. + */ +int +flashwritebyte(sc, addr, val) + struct flashsoftc *sc; + int addr; + u_char val; +{ + u_char sr; + + /* + * If a zero'd bit in the flash memory needs to become set, + * then the zone must be erased and rebuilt. + */ + if (val & ~sc->sc_vaddr[addr]) { + int faddr = addr & ~(sc->sc_zonesize - 1); + u_char *zone; + int i; + + zone = (u_char *)malloc(sc->sc_zonesize, M_TEMP, M_WAITOK); + if (!zone) + return (-1); + bcopy((caddr_t)&sc->sc_vaddr[faddr], zone, sc->sc_zonesize); + + if (flasherasezone(sc, faddr) == -1) + return (-1); + + zone[addr - faddr] = val; + for (i = 0; i < sc->sc_zonesize; i++) { + sc->sc_vaddr[faddr + i] = FLCMD_WSETUP; + sc->sc_vaddr[faddr + i] = zone[i]; + do { + sc->sc_vaddr[0] = FLCMD_READSTAT; + sr = sc->sc_vaddr[0]; + } while (sr & FLSR_WSMS == 0); + if (sr & FLSR_BWS) + return (-1); /* write failed! */ + } + free(zone, M_TEMP); + return (0); + } + + sc->sc_vaddr[addr] = FLCMD_WSETUP; + sc->sc_vaddr[addr] = val; + do { + sc->sc_vaddr[0] = FLCMD_READSTAT; + sr = sc->sc_vaddr[0]; + } while (sr & FLSR_WSMS == 0); + if (sr & FLSR_BWS) + return (-1); /* write failed! */ + return (0); +} diff --git a/sys/arch/mvme68k/dev/flashreg.h b/sys/arch/mvme68k/dev/flashreg.h new file mode 100644 index 00000000000..d9d82c5e492 --- /dev/null +++ b/sys/arch/mvme68k/dev/flashreg.h @@ -0,0 +1,63 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#define FLCMD_RESET 0xff +#define FLCMD_READII 0x90 +#define FLCMD_READSTAT 0x70 +#define FLCMD_CLEARSTAT 0x50 +#define FLCMD_ESETUP 0x20 +#define FLCMD_ECONFIRM 0xd0 +#define FLCMD_ESUSPEND 0xb0 +#define FLCMD_ERESUME 0xd0 +#define FLCMD_WSETUP 0x40 +#define FLCMD_AWSETUP 0x10 + +#define FLSR_WSMS 0x80 +#define FLSR_ESS 0x40 +#define FLSR_ES 0x20 +#define FLSR_BWS 0x10 +#define FLSR_VPPS 0x08 + +/* manufacturers */ +#define FLMANU_INTEL 0x89 + +/* intel parts */ +#define FLII_INTEL_28F020 0xbd +#define FLII_INTEL_28F008SA 0xa1 +#define FLII_INTEL_28F008SA_L 0xa2 + +struct flashii { + char *name; + u_char ii; + int size; + int zonesize; +}; diff --git a/sys/arch/mvme68k/dev/fooip.c b/sys/arch/mvme68k/dev/fooip.c new file mode 100644 index 00000000000..f0217ba369a --- /dev/null +++ b/sys/arch/mvme68k/dev/fooip.c @@ -0,0 +1,123 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * A sample framework for writing an IP module driver. + */ +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/ioctl.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/tty.h> +#include <sys/uio.h> +#include <sys/callout.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/syslog.h> +#include <sys/fcntl.h> +#include <sys/device.h> +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <mvme68k/dev/ipicreg.h> + +struct fooipregs { + volatile u_char fooip_reg1; + volatile u_char fooip_vec; +}; + +struct fooipsoftc { + struct device sc_dev; + struct ipicsoftc *sc_ipicsc; + struct intrhand sc_ih; + + int sc_slot; + struct fooipregs *sc_regs; +}; + +void fooipattach __P((struct device *, struct device *, void *)); +int fooipmatch __P((struct device *, void *, void *)); + +struct cfdriver fooipcd = { + NULL, "fooip", fooipmatch, fooipattach, + DV_DULL, sizeof(struct fooipsoftc), 0 +}; + +int fooipintr __P((void *)); + +int +fooipmatch(parent, cf, args) + struct device *parent; + void *cf; + void *args; +{ + return (1); +} + +void +fooipattach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct fooipsoftc *sc = (struct fooipsoftc *)self; + struct confargs *ca = args; + + sc->sc_ipicsc = (struct ipicsoftc *)ca->ca_master; + sc->sc_regs = (struct fooipregs *)(ca->ca_vaddr + + IPIC_IP_REGOFFSET); + sc->sc_slot = ca->ca_offset; + + /* this device uses only one interrupt */ + sc->sc_ih.ih_fn = fooipintr; + sc->sc_ih.ih_arg = sc; + sc->sc_ih.ih_ipl = ca->ca_ipl; + ipicintr_establish(ca->ca_vec, &sc->sc_ih); + + sc->sc_regs->fooip_vec = ca->ca_vec; + sc->sc_ipicsc->sc_ipic->ipic_irq[sc->sc_slot][0] = ca->ca_ipl | + IPIC_IRQ_ICLR | IPIC_IRQ_IEN; + + printf("\n"); +} + +int +fooipintr(arg) + void *arg; +{ + struct fooipsoftc *sc = arg; + + if (sc->sc_ipicsc->sc_ipic->ipic_irq[sc->sc_slot][0] & IPIC_IRQ_INT) { + /* clear interrupt on device */ + return (1); + } + return (0); +} diff --git a/sys/arch/mvme68k/dev/i82586.h b/sys/arch/mvme68k/dev/i82586.h new file mode 100644 index 00000000000..cb40672da90 --- /dev/null +++ b/sys/arch/mvme68k/dev/i82586.h @@ -0,0 +1,286 @@ +/* $NetBSD: i82586.h,v 1.3 1995/01/27 09:49:55 pk Exp $ */ + +/*- + * Copyright (c) 1995 Theo de Raadt + * Copyright (c) 1992, University of Vermont and State Agricultural College. + * Copyright (c) 1992, Garrett A. Wollman. + * 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 + * Vermont and State Agricultural College and Garrett A. Wollman. + * 4. Neither the name of the University 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 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 UNIVERSITY OR AUTHOR 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. + */ + +/* + * Intel 82586 Ethernet chip + * Register, bit, and structure definitions. + * + * Written by GAW with reference to the Clarkson Packet Driver code for this + * chip written by Russ Nelson and others. + * + * Sun support added by Charles D. Cranor, 25-Oct-94 + */ + +struct ie_en_addr { + u_char data[6]; +}; + +/* + * This is the master configuration block. It tells the hardware where all + * the rest of the stuff is. + */ +struct ie_sys_conf_ptr { + u_char mbz1[3]; /* must be zero */ + u_char ie_bus_use; /* true if 8-bit only */ + u_char mbz2[4]; /* must be zero */ + caddr_t ie_iscp_ptr; /* 24-bit physaddr of ISCP */ +}; + +/* + * The tells the hardware where all the rest of the stuff is, too. + * FIXME: some of these should be re-commented after we figure out their + * REAL function. + */ +struct ie_int_sys_conf_ptr { + u_char mbz1[1]; + u_char ie_busy; /* zeroed after init */ + u_short ie_scb_offset; /* 16-bit physaddr of next struct */ + caddr_t ie_base; /* 24-bit physaddr for all 16-bit vars */ +}; + +/* + * This FINALLY tells the hardware what to do and where to put it. + */ +struct ie_sys_ctl_block { + u_short ie_status; /* status word */ + u_short ie_command; /* command word */ + u_short ie_command_list; /* 16-pointer to command block list */ + u_short ie_recv_list; /* 16-pointer to receive frame list */ + u_short ie_err_crc; /* CRC errors */ + u_short ie_err_align; /* Alignment errors */ + u_short ie_err_resource; /* Resource errors */ + u_short ie_err_overrun; /* Overrun errors */ +}; + +/* Command values */ +#define IE_RU_COMMAND SWAP(0x0070) /* mask for RU command */ +#define IE_RU_NOP SWAP(0) /* for completeness */ +#define IE_RU_START SWAP(0x0010) /* start receive unit command */ +#define IE_RU_ENABLE SWAP(0x0020) /* enable receiver command */ +#define IE_RU_DISABLE SWAP(0x0030) /* disable receiver command */ +#define IE_RU_ABORT SWAP(0x0040) /* abort current receive operation */ + +#define IE_CU_COMMAND SWAP(0x0700) /* mask for CU command */ +#define IE_CU_NOP SWAP(0) /* included for completeness */ +#define IE_CU_START SWAP(0x0100) /* do-command command */ +#define IE_CU_RESUME SWAP(0x0200) /* resume a suspended cmd list */ +#define IE_CU_STOP SWAP(0x0300) /* SUSPEND was already taken */ +#define IE_CU_ABORT SWAP(0x0400) /* abort current command */ + +#define IE_ACK_COMMAND SWAP(0xf000) /* mask for ACK command */ +#define IE_ACK_CX SWAP(0x8000) /* ack IE_ST_DONE */ +#define IE_ACK_FR SWAP(0x4000) /* ack IE_ST_RECV */ +#define IE_ACK_CNA SWAP(0x2000) /* ack IE_ST_ALLDONE */ +#define IE_ACK_RNR SWAP(0x1000) /* ack IE_ST_RNR */ + +#define IE_ACTION_COMMAND(x) (((x) & IE_CU_COMMAND) == IE_CU_START) + /* is this command an action command? */ + +/* Status values */ +#define IE_ST_WHENCE SWAP(0xf000) /* mask for cause of interrupt */ +#define IE_ST_DONE SWAP(0x8000) /* command with I bit completed */ +#define IE_ST_RECV SWAP(0x4000) /* frame received */ +#define IE_ST_ALLDONE SWAP(0x2000) /* all commands completed */ +#define IE_ST_RNR SWAP(0x1000) /* receive not ready */ + +#define IE_CU_STATUS SWAP(0x700) /* mask for command unit status */ +#define IE_CU_ACTIVE SWAP(0x200) /* command unit is active */ +#define IE_CU_SUSPEND SWAP(0x100) /* command unit is suspended */ + +#define IE_RU_STATUS SWAP(0x70) /* mask for receiver unit status */ +#define IE_RU_SUSPEND SWAP(0x10) /* receiver is suspended */ +#define IE_RU_NOSPACE SWAP(0x20) /* receiver has no resources */ +#define IE_RU_READY SWAP(0x40) /* reveiver is ready */ + +/* + * This is filled in partially by the chip, partially by us. + */ +struct ie_recv_frame_desc { + u_short ie_fd_status; /* status for this frame */ + u_short ie_fd_last; /* end of frame list flag */ + u_short ie_fd_next; /* 16-pointer to next RFD */ + u_short ie_fd_buf_desc; /* 16-pointer to list of buffer desc's */ + struct ie_en_addr dest; /* destination ether */ + struct ie_en_addr src; /* source ether */ + u_short ie_length; /* 802 length/Ether type */ + u_short mbz; /* must be zero */ +}; + +#define IE_FD_LAST SWAP(0x8000) /* last rfd in list */ +#define IE_FD_SUSP SWAP(0x4000) /* suspend RU after receipt */ + +#define IE_FD_COMPLETE SWAP(0x8000) /* frame is complete */ +#define IE_FD_BUSY SWAP(0x4000) /* frame is busy */ +#define IE_FD_OK SWAP(0x2000) /* frame is bad */ +#define IE_FD_RNR SWAP(0x0200) /* receiver out of resources here */ + +/* + * linked list of buffers... + */ +struct ie_recv_buf_desc { + u_short ie_rbd_actual; /* status for this buffer */ + u_short ie_rbd_next; /* 16-pointer to next RBD */ + caddr_t ie_rbd_buffer; /* 24-pointer to buffer for this RBD */ + u_short ie_rbd_length; /* length of the buffer */ + u_short mbz; /* must be zero */ +}; + +#define IE_RBD_LAST SWAP(0x8000) /* last buffer */ +#define IE_RBD_USED SWAP(0x4000) /* this buffer has data */ +/* + * All commands share this in common. + */ +struct ie_cmd_common { + u_short ie_cmd_status; /* status of this command */ + u_short ie_cmd_cmd; /* command word */ + u_short ie_cmd_link; /* link to next command */ +}; + +#define IE_STAT_COMPL SWAP(0x8000) /* command is completed */ +#define IE_STAT_BUSY SWAP(0x4000) /* command is running now */ +#define IE_STAT_OK SWAP(0x2000) /* command completed successfully */ +#define IE_STAT_ABORT SWAP(0x1000) /* command was aborted */ + + +#define IE_CMD_NOP SWAP(0x0000) /* NOP */ +#define IE_CMD_IASETUP SWAP(0x0001) /* initial address setup */ +#define IE_CMD_CONFIG SWAP(0x0002) /* configure command */ +#define IE_CMD_MCAST SWAP(0x0003) /* multicast setup command */ +#define IE_CMD_XMIT SWAP(0x0004) /* transmit command */ +#define IE_CMD_TDR SWAP(0x0005) /* time-domain reflectometer command */ +#define IE_CMD_DUMP SWAP(0x0006) /* dump command */ +#define IE_CMD_DIAGNOSE SWAP(0x0007) /* diagnostics command */ + +#define IE_CMD_LAST SWAP(0x8000) /* this is the last command in the list */ +#define IE_CMD_SUSPEND SWAP(0x4000) /* suspend CU after this command */ +#define IE_CMD_INTR SWAP(0x2000) /* post an interrupt after completion */ + +/* + * This is the command to transmit a frame. + */ +struct ie_xmit_cmd { + struct ie_cmd_common com; /* common part */ +#define ie_xmit_status com.ie_cmd_status + + u_short ie_xmit_desc; /* 16-pointer to buffer descriptor */ + struct ie_en_addr ie_xmit_addr; /* destination address */ + + u_short ie_xmit_length; /* 802.3 length/Ether type field */ +}; + +#define IE_XS_MAXCOLL SWAP(0x000f) /* number of collisions during transmit */ +#define IE_XS_EXCMAX SWAP(0x0020) /* exceeded maximum number of collisions */ +#define IE_XS_SQE SWAP(0x0040) /* SQE positive */ +#define IE_XS_DEFERRED SWAP(0x0080) /* transmission deferred */ +#define IE_XS_UNDERRUN SWAP(0x0100) /* DMA underrun */ +#define IE_XS_LOSTCTS SWAP(0x0200) /* Lost CTS */ +#define IE_XS_NOCARRIER SWAP(0x0400) /* No Carrier */ + +/* + * This is a buffer descriptor for a frame to be transmitted. + */ + +struct ie_xmit_buf { + u_short ie_xmit_flags; /* see below */ + u_short ie_xmit_next; /* 16-pointer to next desc. */ + caddr_t ie_xmit_buf; /* 24-pointer to the actual buffer */ +}; + +#define IE_XMIT_LAST SWAP(0x8000) /* this TBD is the last one */ +/* The rest of the `flags' word is actually the length. */ + +/* + * Multicast setup command. + */ + +#define MAXMCAST 250 /* must fit in transmit buffer */ + +struct ie_mcast_cmd { + struct ie_cmd_common com; /* common part */ +#define ie_mcast_status com.ie_cmd_status + + u_short ie_mcast_bytes; /* size (in bytes) of multicast addresses */ + struct ie_en_addr ie_mcast_addrs[MAXMCAST + 1]; /* space for them */ +}; + +/* + * Time Domain Reflectometer command. + */ + +struct ie_tdr_cmd { + struct ie_cmd_common com; /* common part */ +#define ie_tdr_status com.ie_cmd_status + + u_short ie_tdr_time; /* error bits and time */ +}; + +#define IE_TDR_SUCCESS SWAP(0x8000) /* TDR succeeded without error */ +#define IE_TDR_XCVR SWAP(0x4000) /* detected a transceiver problem */ +#define IE_TDR_OPEN SWAP(0x2000) /* detected an open */ +#define IE_TDR_SHORT SWAP(0x1000) /* TDR detected a short */ +#define IE_TDR_TIME SWAP(0x07ff) /* mask for reflection time */ + +/* + * Initial Address Setup command + */ +struct ie_iasetup_cmd { + struct ie_cmd_common com; +#define ie_iasetup_status com.ie_cmd_status + + struct ie_en_addr ie_address; +}; + +/* + * Configuration command + */ +struct ie_config_cmd { + struct ie_cmd_common com; /* common part */ +#define ie_config_status com.ie_cmd_status + + u_char ie_config_count; /* byte count (0x0c) */ + u_char ie_fifo; /* fifo (8) */ + u_char ie_save_bad; /* save bad frames (0x40) */ + u_char ie_addr_len; /* address length (0x2e) (AL-LOC == 1) */ + u_char ie_priority; /* priority and backoff (0x0) */ + u_char ie_ifs; /* inter-frame spacing (0x60) */ + u_char ie_slot_low; /* slot time, LSB (0x0) */ + u_char ie_slot_high; /* slot time, MSN, and retries (0xf2) */ + u_char ie_promisc; /* 1 if promiscuous, else 0 */ + u_char ie_crs_cdt; /* CSMA/CD parameters (0x0) */ + u_char ie_min_len; /* min frame length (0x40) */ + u_char ie_junk; /* stuff for 82596 (0xff) */ +}; diff --git a/sys/arch/mvme68k/dev/if_ie.c b/sys/arch/mvme68k/dev/if_ie.c new file mode 100644 index 00000000000..e24a121d0e9 --- /dev/null +++ b/sys/arch/mvme68k/dev/if_ie.c @@ -0,0 +1,1958 @@ +/* $NetBSD: if_ie.c,v 1.15 1995/04/11 09:18:09 pk Exp $ */ + +/*- + * Copyright (c) 1995 Theo de Raadt + * Copyright (c) 1993, 1994, 1995 Charles Hannum. + * Copyright (c) 1992, 1993, University of Vermont and State + * Agricultural College. + * Copyright (c) 1992, 1993, Garrett A. Wollman. + * + * Portions: + * Copyright (c) 1994, 1995, Rafal K. Boni + * Copyright (c) 1990, 1991, William F. Jolitz + * Copyright (c) 1990, 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 Charles Hannum, by the + * University of Vermont and State Agricultural College and Garrett A. + * Wollman, by William F. Jolitz, and by the University of California, + * Berkeley, Lawrence Berkeley Laboratory, and its contributors. + * 4. Neither the names of the Universities nor the names of the authors + * 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 UNIVERSITY OR AUTHORS 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. + */ + +/* + * Intel 82586 Ethernet chip + * Register, bit, and structure definitions. + * + * Original StarLAN driver written by Garrett Wollman with reference to the + * Clarkson Packet Driver code for this chip written by Russ Nelson and others. + * + * BPF support code taken from hpdev/if_le.c, supplied with tcpdump. + * + * 3C507 support is loosely based on code donated to NetBSD by Rafal Boni. + * + * Majorly cleaned up and 3C507 code merged by Charles Hannum. + * + * Converted to SUN ie driver by Charles D. Cranor, + * October 1994, January 1995. + * This sun version based on i386 version 1.30. + */ + +extern void *etherbuf; +extern int etherlen; + +/* +Mode of operation: + + We run the 82586 in a standard Ethernet mode. We keep NFRAMES + received frame descriptors around for the receiver to use, and + NRXBUF associated receive buffer descriptors, both in a circular + list. Whenever a frame is received, we rotate both lists as + necessary. (The 586 treats both lists as a simple queue.) We also + keep a transmit command around so that packets can be sent off + quickly. + + We configure the adapter in AL-LOC = 1 mode, which means that the + Ethernet/802.3 MAC header is placed at the beginning of the receive + buffer rather than being split off into various fields in the RFD. + This also means that we must include this header in the transmit + buffer as well. + + By convention, all transmit commands, and only transmit commands, + shall have the I (IE_CMD_INTR) bit set in the command. This way, + when an interrupt arrives at ieintr(), it is immediately possible + to tell what precisely caused it. ANY OTHER command-sending + routines should run at splimp(), and should post an acknowledgement + to every interrupt they generate. + +*/ + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/buf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/syslog.h> +#include <sys/device.h> + +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_dl.h> +#include <net/netisr.h> +#include <net/route.h> + +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#endif + +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#include <vm/vm.h> + +/* + * ugly byte-order hack for SUNs + */ + +#define SWAP(x) (x) + +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <machine/pmap.h> + +#include "mc.h" +#include "pcctwo.h" + +#if NMC > 0 +#include <mvme68k/dev/mcreg.h> +#endif +#if NPCCTWO > 0 +#include <mvme68k/dev/pcctworeg.h> +#endif + +#include <mvme68k/dev/if_ie.h> +#include <mvme68k/dev/i82586.h> + +static struct mbuf *last_not_for_us; +vm_map_t ie_map; /* for obio */ + +#define IED_RINT 0x01 +#define IED_TINT 0x02 +#define IED_RNR 0x04 +#define IED_CNA 0x08 +#define IED_READFRAME 0x10 +#define IED_ALL 0x1f + +#define ETHER_MIN_LEN 64 +#define ETHER_MAX_LEN 1518 +#define ETHER_ADDR_LEN 6 + +#define B_PER_F 3 /* recv buffers per frame */ +#define MXFRAMES 300 /* max number of recv frames */ +#define MXRXBUF (MXFRAMES*B_PER_F) /* number of buffers to allocate */ +#define IE_RBUF_SIZE 256 /* size of each receive buffer; + MUST BE POWER OF TWO */ +#define NTXBUF 2 /* number of transmit commands */ +#define IE_TBUF_SIZE ETHER_MAX_LEN /* length of transmit buffer */ + + +/* + * Ethernet status, per interface. + * + * hardware addresses/sizes to know (all KVA): + * sc_iobase = base of chip's 24 bit address space + * sc_maddr = base address of chip RAM as stored in ie_base of iscp + * sc_msize = size of chip's RAM + * sc_reg = address of card dependent registers + * + * the chip uses two types of pointers: 16 bit and 24 bit + * 16 bit pointers are offsets from sc_maddr/ie_base + * KVA(16 bit offset) = offset + sc_maddr + * 24 bit pointers are offset from sc_iobase in KVA + * KVA(24 bit address) = address + sc_iobase + * + * on the vme/multibus we have the page map to control where ram appears + * in the address space. we choose to have RAM start at 0 in the + * 24 bit address space. this means that sc_iobase == sc_maddr! + * to get the phyiscal address of the board's RAM you must take the + * top 12 bits of the physical address of the register address + * and or in the 4 bits from the status word as bits 17-20 (remember that + * the board ignores the chip's top 4 address lines). + * For example: + * if the register is @ 0xffe88000, then the top 12 bits are 0xffe00000. + * to get the 4 bits from the the status word just do status & IEVME_HADDR. + * suppose the value is "4". Then just shift it left 16 bits to get + * it into bits 17-20 (e.g. 0x40000). Then or it to get the + * address of RAM (in our example: 0xffe40000). see the attach routine! + * + * on the onboard ie interface the 24 bit address space is hardwired + * to be 0xff000000 -> 0xffffffff of KVA. this means that sc_iobase + * will be 0xff000000. sc_maddr will be where ever we allocate RAM + * in KVA. note that since the SCP is at a fixed address it means + * that we have to allocate a fixed KVA for the SCP. + */ + +struct ie_softc { + struct device sc_dev; /* device structure */ + struct intrhand sc_ih, sc_failih; /* interrupt info */ + struct evcnt sc_intrcnt; /* # of interrupts, per ie */ + + caddr_t sc_iobase; /* KVA of base of 24 bit addr space */ + caddr_t sc_maddr; /* KVA of base of chip's RAM (16bit addr sp.)*/ + u_int sc_msize; /* how much RAM we have/use */ + caddr_t sc_reg; /* KVA of car's register */ + int sc_bustype; + + struct arpcom sc_arpcom;/* system arpcom structure */ + + void (*reset_586)(); /* card dependent reset function */ + void (*chan_attn)(); /* card dependent attn function */ + void (*run_586)(); /* card depenent "go on-line" function */ + void (*memcopy) __P((const void *, void *, u_int)); + /* card dependent memory copy function */ + void (*memzero) __P((void *, u_int)); + /* card dependent memory zero function */ + + + int want_mcsetup; /* mcsetup flag */ + int promisc; /* are we in promisc mode? */ + + /* + * pointers to the 3 major control structures + */ + + volatile struct ie_sys_conf_ptr *scp; + volatile struct ie_int_sys_conf_ptr *iscp; + volatile struct ie_sys_ctl_block *scb; + + /* + * pointer and size of a block of KVA where the buffers + * are to be allocated from + */ + + caddr_t buf_area; + int buf_area_sz; + + /* + * the actual buffers (recv and xmit) + */ + + volatile struct ie_recv_frame_desc *rframes[MXFRAMES]; + volatile struct ie_recv_buf_desc *rbuffs[MXRXBUF]; + volatile char *cbuffs[MXRXBUF]; + int rfhead, rftail, rbhead, rbtail; + + volatile struct ie_xmit_cmd *xmit_cmds[NTXBUF]; + volatile struct ie_xmit_buf *xmit_buffs[NTXBUF]; + u_char *xmit_cbuffs[NTXBUF]; + int xmit_busy; + int xmit_free; + int xchead, xctail; + + struct ie_en_addr mcast_addrs[MAXMCAST + 1]; + int mcast_count; + + int nframes; /* number of frames in use */ + int nrxbuf; /* number of recv buffs in use */ + +#ifdef IEDEBUG + int sc_debug; +#endif +#if NMC > 0 + struct mcreg *sc_mc; +#endif +#if NPCCTWO > 0 + struct pcctworeg *sc_pcc2; +#endif +}; + +static void ie_obreset __P((struct ie_softc *)); +static void ie_obattend __P((struct ie_softc *)); +static void ie_obrun __P((struct ie_softc *)); + +void iewatchdog __P((/* short */)); +int ieintr __P((void *)); +int iefailintr __P((void *)); +int ieinit __P((struct ie_softc *)); +int ieioctl __P((struct ifnet *, u_long, caddr_t)); +void iestart __P((struct ifnet *)); +void iereset __P((struct ie_softc *)); +static void ie_readframe __P((struct ie_softc *, int)); +static void ie_drop_packet_buffer __P((struct ie_softc *)); +static int command_and_wait __P((struct ie_softc *, int, + void volatile *, int)); +/*static*/ void ierint __P((struct ie_softc *)); +/*static*/ void ietint __P((struct ie_softc *)); +static int ieget __P((struct ie_softc *, struct mbuf **, + struct ether_header *, int *)); +static void setup_bufs __P((struct ie_softc *)); +static int mc_setup __P((struct ie_softc *, void *)); +static void mc_reset __P((struct ie_softc *)); + +#ifdef IEDEBUG +void print_rbd __P((volatile struct ie_recv_buf_desc *)); + +int in_ierint = 0; +int in_ietint = 0; +#endif + +int iematch(); +void ieattach(); + +struct cfdriver iecd = { + NULL, "ie", iematch, ieattach, DV_IFNET, sizeof(struct ie_softc) +}; + +/* + * address generation macros + * MK_24 = KVA -> 24 bit address in SUN byte order + * MK_16 = KVA -> 16 bit address in INTEL byte order + * ST_24 = store a 24 bit address in SUN byte order to INTEL byte order + */ +#define MK_24(base, ptr) ((caddr_t)((u_long)ptr)) +#define MK_16(base, ptr) SWAP((u_short)( ((u_long)(ptr)) - ((u_long)(base)) )) +#define ST_24(to, from) { \ + u_long fval = (u_long)(from); \ + u_char *t = (u_char *)&(to), *f = (u_char *)&fval; \ + t[0] = f[2]; t[1] = f[3]; /*t[2] = f[0]*/; t[3] = f[1]; \ + } +/* + * Here are a few useful functions. We could have done these as macros, but + * since we have the inline facility, it makes sense to use that instead. + */ +static inline void +ie_setup_config(cmd, promiscuous, manchester) + volatile struct ie_config_cmd *cmd; + int promiscuous, manchester; +{ + + cmd->ie_config_count = 0x0c; + cmd->ie_fifo = 8; + cmd->ie_save_bad = 0x40; + cmd->ie_addr_len = 0x2e; + cmd->ie_priority = 0; + cmd->ie_ifs = 0x60; + cmd->ie_slot_low = 0; + cmd->ie_slot_high = 0xf2; + cmd->ie_promisc = !!promiscuous | manchester << 2; + cmd->ie_crs_cdt = 0; + cmd->ie_min_len = 64; + cmd->ie_junk = 0xff; +} + +static inline void +ie_ack(sc, mask) + struct ie_softc *sc; + u_int mask; +{ + volatile struct ie_sys_ctl_block *scb = sc->scb; + + command_and_wait(sc, scb->ie_status & mask, 0, 0); +} + +int +iematch(parent, vcf, args) + struct device *parent; + void *vcf, *args; +{ + struct cfdata *cf = vcf; + struct confargs *ca = args; + + return (!badvaddr(ca->ca_vaddr, 4)); +} + +/* + * Deep Magic: reset it, then set SCP address again. Pray. + */ +void +ie_obreset(sc) + struct ie_softc *sc; +{ + volatile struct ieob *ieo = (struct ieob *) sc->sc_reg; + volatile int t; + u_long a; + + a = IE_PORT_RESET; + ieo->porthigh = a & 0xffff; + t = 0; t = 1; + ieo->portlow = a >> 16; + delay(1000); + + a = (u_long)pmap_extract(pmap_kernel(), (vm_offset_t)sc->scp) | + IE_PORT_NEWSCPADDR; + ieo->porthigh = a & 0xffff; + t = 0; t = 1; + ieo->portlow = a >> 16; + delay(1000); +} + +void +ie_obattend(sc) + struct ie_softc *sc; +{ + volatile struct ieob *ieo = (struct ieob *) sc->sc_reg; + + ieo->attn = 1; +} + +void +ie_obrun(sc) + struct ie_softc *sc; +{ +} + +/* + * Taken almost exactly from Bill's if_is.c, then modified beyond recognition. + */ +void +ieattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct ie_softc *sc = (void *) self; + struct confargs *ca = aux; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + extern void myetheraddr(u_char *); /* should be elsewhere */ + register struct bootpath *bp; + int pri = ca->ca_ipl; + volatile struct ieob *ieo; + vm_offset_t pa; + + sc->reset_586 = ie_obreset; + sc->chan_attn = ie_obattend; + sc->run_586 = ie_obrun; + sc->memcopy = bcopy; + sc->memzero = bzero; + sc->sc_msize = etherlen; + sc->sc_reg = ca->ca_vaddr; + ieo = (volatile struct ieob *) sc->sc_reg; + + sc->sc_maddr = etherbuf; /* maddr = vaddr */ + pa = pmap_extract(pmap_kernel(), (vm_offset_t)sc->sc_maddr); + if (pa == 0) panic("ie pmap_extract"); + sc->sc_iobase = (caddr_t)pa; /* iobase = paddr (24 bit) */ + + /*printf("maddrP %x iobaseV %x\n", sc->sc_maddr, sc->sc_iobase);*/ + + (sc->memzero)(sc->sc_maddr, sc->sc_msize); + sc->iscp = (volatile struct ie_int_sys_conf_ptr *) + sc->sc_maddr; /* @ location zero */ + sc->scb = (volatile struct ie_sys_ctl_block *) + roundup((int)sc->iscp + sizeof(struct ie_int_sys_conf_ptr), 16); + sc->scp = (struct ie_sys_conf_ptr *) + roundup((int)sc->scb + sizeof(struct ie_sys_ctl_block), 16); + /*printf("scpV %x iscpV %x scbV %x\n", sc->scp, sc->iscp, sc->scb);*/ + + sc->scp->ie_bus_use = 0; /* 16-bit */ + ST_24(sc->scp->ie_iscp_ptr, + pmap_extract(pmap_kernel(), (vm_offset_t)sc->iscp)); + + /*printf("iscpV(%x) = iscpP(%x) -> scp.ptr@%x = val:%x\n", + sc->iscp, pmap_extract(pmap_kernel(), (vm_offset_t)sc->iscp), + &sc->scp->ie_iscp_ptr, sc->scp->ie_iscp_ptr);*/ + + /* + * rest of first page is unused (wasted!), rest of ram + * for buffers + */ + sc->buf_area = sc->sc_maddr + NBPG; + sc->buf_area_sz = sc->sc_msize - NBPG; + myetheraddr(sc->sc_arpcom.ac_enaddr); + + if (ie_setupram(sc) == 0) { + printf(": RAM CONFIG FAILED!\n"); + /* XXX should reclaim resources? */ + return; + } + ifp->if_unit = sc->sc_dev.dv_unit; + ifp->if_name = iecd.cd_name; + ifp->if_start = iestart; + ifp->if_ioctl = ieioctl; + ifp->if_watchdog = iewatchdog; + ifp->if_flags = + IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST; + + /* Attach the interface. */ + if_attach(ifp); + ether_ifattach(ifp); + + printf(": address %s\n", ether_sprintf(sc->sc_arpcom.ac_enaddr)); + +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + + sc->sc_bustype = ca->ca_bustype; + + sc->sc_ih.ih_fn = ieintr; + sc->sc_ih.ih_arg = sc; + sc->sc_ih.ih_ipl = pri; + sc->sc_failih.ih_fn = iefailintr; + sc->sc_failih.ih_arg = sc; + sc->sc_failih.ih_ipl = pri; + + switch (sc->sc_bustype) { +#if NMC > 0 + case BUS_MC: + mcintr_establish(MCV_IE, &sc->sc_ih); + sc->sc_mc = (struct mcreg *)ca->ca_master; + sc->sc_mc->mc_ieirq = pri | MC_SC_SNOOP | MC_IRQ_IEN | + MC_IRQ_ICLR; + mcintr_establish(MCV_IEFAIL, &sc->sc_failih); + sc->sc_mc->mc_iefailirq = pri | MC_IRQ_IEN | MC_IRQ_ICLR; + break; +#endif +#if NPCCTWO > 0 + case BUS_PCCTWO: + pcctwointr_establish(PCC2V_IE, &sc->sc_ih); + sc->sc_pcc2 = (struct pcctworeg *)ca->ca_master; + sc->sc_pcc2->pcc2_ieirq = pri | PCC2_SC_SNOOP | + PCC2_IRQ_IEN | PCC2_IRQ_ICLR; + pcctwointr_establish(PCC2V_IEFAIL, &sc->sc_failih); + sc->sc_pcc2->pcc2_iefailirq = pri | PCC2_IRQ_IEN | + PCC2_IRQ_ICLR; + break; +#endif + } + + evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt); +} + +/* + * Device timeout/watchdog routine. Entered if the device neglects to generate + * an interrupt after a transmit has been started on it. + */ +void +iewatchdog(unit) + short unit; +{ + struct ie_softc *sc = iecd.cd_devs[unit]; + + log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname); + ++sc->sc_arpcom.ac_if.if_oerrors; + + iereset(sc); +} + +int +iefailintr(v) +void *v; +{ + struct ie_softc *sc = v; + + switch (sc->sc_bustype) { +#if NMC > 0 + case BUS_MC: + sc->sc_mc->mc_ieirq |= MC_IRQ_ICLR; /* safe: clear irq */ + sc->sc_mc->mc_iefailirq |= MC_IRQ_ICLR; /* clear failure */ + sc->sc_mc->mc_ieerr = MC_IEERR_SCLR; /* reset error */ + break; +#endif +#if NPCCTWO > 0 + case BUS_PCCTWO: + sc->sc_pcc2->pcc2_ieirq |= PCC2_IRQ_ICLR; /* safe: clear irq */ + sc->sc_pcc2->pcc2_iefailirq |= PCC2_IRQ_ICLR; /* clear failure */ + sc->sc_pcc2->pcc2_ieerr = PCC2_IEERR_SCLR; /* reset error */ + break; +#endif + } + + iereset(sc); + return (1); +} + +/* + * What to do upon receipt of an interrupt. + */ +int +ieintr(v) +void *v; +{ + struct ie_softc *sc = v; + register u_short status; + + status = sc->scb->ie_status; +/*printf("I");*/ + +loop: + /* Ack interrupts FIRST in case we receive more during the ISR. */ + ie_ack(sc, IE_ST_WHENCE & status); + switch (sc->sc_bustype) { +#if NMC > 0 + case BUS_MC: + sc->sc_mc->mc_ieirq |= MC_IRQ_ICLR; /* clear irq */ + break; +#endif +#if NPCCTWO > 0 + case BUS_PCCTWO: + sc->sc_pcc2->pcc2_ieirq |= PCC2_IRQ_ICLR; /* clear irq */ + break; +#endif + } + + if (status & (IE_ST_RECV | IE_ST_RNR)) { +#ifdef IEDEBUG + in_ierint++; + if (sc->sc_debug & IED_RINT) + printf("%s: rint\n", sc->sc_dev.dv_xname); +#endif + ierint(sc); +#ifdef IEDEBUG + in_ierint--; +#endif + } + + if (status & IE_ST_DONE) { +#ifdef IEDEBUG + in_ietint++; + if (sc->sc_debug & IED_TINT) + printf("%s: tint\n", sc->sc_dev.dv_xname); +#endif + ietint(sc); +#ifdef IEDEBUG + in_ietint--; +#endif + } + + if (status & IE_ST_RNR) { + printf("%s: receiver not ready\n", sc->sc_dev.dv_xname); + sc->sc_arpcom.ac_if.if_ierrors++; + iereset(sc); + } + +#ifdef IEDEBUG + if ((status & IE_ST_ALLDONE) && (sc->sc_debug & IED_CNA)) + printf("%s: cna\n", sc->sc_dev.dv_xname); +#endif + + if ((status = sc->scb->ie_status) & IE_ST_WHENCE) + goto loop; + + sc->sc_intrcnt.ev_count++; + return 1; +} + +/* + * Process a received-frame interrupt. + */ +void +ierint(sc) + struct ie_softc *sc; +{ + volatile struct ie_sys_ctl_block *scb = sc->scb; + int i, status; + static int timesthru = 1024; + + i = sc->rfhead; + for (;;) { + status = sc->rframes[i]->ie_fd_status; + + if ((status & IE_FD_COMPLETE) && (status & IE_FD_OK)) { + sc->sc_arpcom.ac_if.if_ipackets++; + if (!--timesthru) { + sc->sc_arpcom.ac_if.if_ierrors += + SWAP(scb->ie_err_crc) + + SWAP(scb->ie_err_align) + + SWAP(scb->ie_err_resource) + + SWAP(scb->ie_err_overrun); + scb->ie_err_crc = scb->ie_err_align = + scb->ie_err_resource = scb->ie_err_overrun = + 0; + timesthru = 1024; + } + ie_readframe(sc, i); + } else { + if ((status & IE_FD_RNR) != 0 && + (scb->ie_status & IE_RU_READY) == 0) { + sc->rframes[0]->ie_fd_buf_desc = + MK_16(sc->sc_maddr, sc->rbuffs[0]); + scb->ie_recv_list = + MK_16(sc->sc_maddr, sc->rframes[0]); + command_and_wait(sc, IE_RU_START, 0, 0); + } + break; + } + i = (i + 1) % sc->nframes; + } +} + +/* + * Process a command-complete interrupt. These are only generated by the + * transmission of frames. This routine is deceptively simple, since most of + * the real work is done by iestart(). + */ +void +ietint(sc) + struct ie_softc *sc; +{ + int status; + + sc->sc_arpcom.ac_if.if_timer = 0; + sc->sc_arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + + status = sc->xmit_cmds[sc->xctail]->ie_xmit_status; + + if (!(status & IE_STAT_COMPL) || (status & IE_STAT_BUSY)) + printf("ietint: command still busy!\n"); + + if (status & IE_STAT_OK) { + sc->sc_arpcom.ac_if.if_opackets++; + sc->sc_arpcom.ac_if.if_collisions += + SWAP(status & IE_XS_MAXCOLL); + } else if (status & IE_STAT_ABORT) { + printf("%s: send aborted\n", sc->sc_dev.dv_xname); + sc->sc_arpcom.ac_if.if_oerrors++; + } else if (status & IE_XS_NOCARRIER) { + printf("%s: no carrier\n", sc->sc_dev.dv_xname); + sc->sc_arpcom.ac_if.if_oerrors++; + } else if (status & IE_XS_LOSTCTS) { + printf("%s: lost CTS\n", sc->sc_dev.dv_xname); + sc->sc_arpcom.ac_if.if_oerrors++; + } else if (status & IE_XS_UNDERRUN) { + printf("%s: DMA underrun\n", sc->sc_dev.dv_xname); + sc->sc_arpcom.ac_if.if_oerrors++; + } else if (status & IE_XS_EXCMAX) { + printf("%s: too many collisions\n", sc->sc_dev.dv_xname); + sc->sc_arpcom.ac_if.if_collisions += 16; + sc->sc_arpcom.ac_if.if_oerrors++; + } + + /* + * If multicast addresses were added or deleted while transmitting, + * mc_reset() set the want_mcsetup flag indicating that we should do + * it. + */ + if (sc->want_mcsetup) { + mc_setup(sc, (caddr_t)sc->xmit_cbuffs[sc->xctail]); + sc->want_mcsetup = 0; + } + + /* Done with the buffer. */ + sc->xmit_free++; + sc->xmit_busy = 0; + sc->xctail = (sc->xctail + 1) % NTXBUF; + + iestart(&sc->sc_arpcom.ac_if); +} + +/* + * Compare two Ether/802 addresses for equality, inlined and unrolled for + * speed. I'd love to have an inline assembler version of this... + */ +static inline int +ether_equal(one, two) + u_char *one, *two; +{ + + if (one[0] != two[0] || one[1] != two[1] || one[2] != two[2] || + one[3] != two[3] || one[4] != two[4] || one[5] != two[5]) + return 0; + return 1; +} + +/* + * Check for a valid address. to_bpf is filled in with one of the following: + * 0 -> BPF doesn't get this packet + * 1 -> BPF does get this packet + * 2 -> BPF does get this packet, but we don't + * Return value is true if the packet is for us, and false otherwise. + * + * This routine is a mess, but it's also critical that it be as fast + * as possible. It could be made cleaner if we can assume that the + * only client which will fiddle with IFF_PROMISC is BPF. This is + * probably a good assumption, but we do not make it here. (Yet.) + */ +static inline int +check_eh(sc, eh, to_bpf) + struct ie_softc *sc; + struct ether_header *eh; + int *to_bpf; +{ + int i; + + switch(sc->promisc) { + case IFF_ALLMULTI: + /* + * Receiving all multicasts, but no unicasts except those + * destined for us. + */ +#if NBPFILTER > 0 + *to_bpf = (sc->sc_arpcom.ac_if.if_bpf != 0); /* BPF gets this packet if anybody cares */ +#endif + if (eh->ether_dhost[0] & 1) + return 1; + if (ether_equal(eh->ether_dhost, sc->sc_arpcom.ac_enaddr)) return 1; + return 0; + + case IFF_PROMISC: + /* + * Receiving all packets. These need to be passed on to BPF. + */ +#if NBPFILTER > 0 + *to_bpf = (sc->sc_arpcom.ac_if.if_bpf != 0); +#endif + /* If for us, accept and hand up to BPF */ + if (ether_equal(eh->ether_dhost, sc->sc_arpcom.ac_enaddr)) return 1; + +#if NBPFILTER > 0 + if (*to_bpf) + *to_bpf = 2; /* we don't need to see it */ +#endif + + /* + * Not a multicast, so BPF wants to see it but we don't. + */ + if (!(eh->ether_dhost[0] & 1)) + return 1; + + /* + * If it's one of our multicast groups, accept it and pass it + * up. + */ + for (i = 0; i < sc->mcast_count; i++) { + if (ether_equal(eh->ether_dhost, (u_char *)&sc->mcast_addrs[i])) { +#if NBPFILTER > 0 + if (*to_bpf) + *to_bpf = 1; +#endif + return 1; + } + } + return 1; + + case IFF_ALLMULTI | IFF_PROMISC: + /* + * Acting as a multicast router, and BPF running at the same + * time. Whew! (Hope this is a fast machine...) + */ +#if NBPFILTER > 0 + *to_bpf = (sc->sc_arpcom.ac_if.if_bpf != 0); +#endif + /* We want to see multicasts. */ + if (eh->ether_dhost[0] & 1) + return 1; + + /* We want to see our own packets */ + if (ether_equal(eh->ether_dhost, sc->sc_arpcom.ac_enaddr)) + return 1; + + /* Anything else goes to BPF but nothing else. */ +#if NBPFILTER > 0 + if (*to_bpf) + *to_bpf = 2; +#endif + return 1; + + default: + /* + * Only accept unicast packets destined for us, or multicasts + * for groups that we belong to. For now, we assume that the + * '586 will only return packets that we asked it for. This + * isn't strictly true (it uses hashing for the multicast + * filter), but it will do in this case, and we want to get out + * of here as quickly as possible. + */ +#if NBPFILTER > 0 + *to_bpf = (sc->sc_arpcom.ac_if.if_bpf != 0); +#endif + return 1; + } + return 0; +} + +/* + * We want to isolate the bits that have meaning... This assumes that + * IE_RBUF_SIZE is an even power of two. If somehow the act_len exceeds + * the size of the buffer, then we are screwed anyway. + */ +static inline int +ie_buflen(sc, head) + struct ie_softc *sc; + int head; +{ + + return (SWAP(sc->rbuffs[head]->ie_rbd_actual) + & (IE_RBUF_SIZE | (IE_RBUF_SIZE - 1))); +} + +static inline int +ie_packet_len(sc) + struct ie_softc *sc; +{ + int i; + int head = sc->rbhead; + int acc = 0; + + do { + if (!(sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_USED)) { +#ifdef IEDEBUG + print_rbd(sc->rbuffs[sc->rbhead]); +#endif + log(LOG_ERR, "%s: receive descriptors out of sync at %d\n", + sc->sc_dev.dv_xname, sc->rbhead); + iereset(sc); + return -1; + } + + i = sc->rbuffs[head]->ie_rbd_actual & IE_RBD_LAST; + + acc += ie_buflen(sc, head); + head = (head + 1) % sc->nrxbuf; + } while (!i); + + return acc; +} + +/* + * Setup all necessary artifacts for an XMIT command, and then pass the XMIT + * command to the chip to be executed. On the way, if we have a BPF listener + * also give him a copy. + */ +inline static void +iexmit(sc) + struct ie_softc *sc; +{ + +#if NBPFILTER > 0 + /* + * If BPF is listening on this interface, let it see the packet before + * we push it on the wire. + */ + if (sc->sc_arpcom.ac_if.if_bpf) + bpf_tap(sc->sc_arpcom.ac_if.if_bpf, + sc->xmit_cbuffs[sc->xctail], + SWAP(sc->xmit_buffs[sc->xctail]->ie_xmit_flags)); +#endif + +/*printf("iexmit base %x cmd %x bfd %x to %x\n", +sc->sc_maddr, +sc->xmit_cmds[sc->xctail], +sc->xmit_buffs[sc->xctail], +sc->xmit_cbuffs[sc->xctail]);*/ + sc->xmit_buffs[sc->xctail]->ie_xmit_flags |= IE_XMIT_LAST; + sc->xmit_buffs[sc->xctail]->ie_xmit_next = SWAP(0xffff); + ST_24(sc->xmit_buffs[sc->xctail]->ie_xmit_buf, + MK_24(sc->sc_iobase, sc->xmit_cbuffs[sc->xctail])); + + sc->xmit_cmds[sc->xctail]->com.ie_cmd_link = SWAP(0xffff); + sc->xmit_cmds[sc->xctail]->com.ie_cmd_cmd = + IE_CMD_XMIT | IE_CMD_INTR | IE_CMD_LAST; + + sc->xmit_cmds[sc->xctail]->ie_xmit_status = SWAP(0); + sc->xmit_cmds[sc->xctail]->ie_xmit_desc = + MK_16(sc->sc_maddr, sc->xmit_buffs[sc->xctail]); + + sc->scb->ie_command_list = + MK_16(sc->sc_maddr, sc->xmit_cmds[sc->xctail]); + command_and_wait(sc, IE_CU_START, 0, 0); + + sc->xmit_busy = 1; + sc->sc_arpcom.ac_if.if_timer = 5; +} + +/* + * Read data off the interface, and turn it into an mbuf chain. + * + * This code is DRAMATICALLY different from the previous version; this + * version tries to allocate the entire mbuf chain up front, given the + * length of the data available. This enables us to allocate mbuf + * clusters in many situations where before we would have had a long + * chain of partially-full mbufs. This should help to speed up the + * operation considerably. (Provided that it works, of course.) + */ +static inline int +ieget(sc, mp, ehp, to_bpf) + struct ie_softc *sc; + struct mbuf **mp; + struct ether_header *ehp; + int *to_bpf; +{ + struct mbuf *m, *top, **mymp; + int i; + int offset; + int totlen, resid; + int thismboff; + int head; + + totlen = ie_packet_len(sc); + if (totlen <= 0) + return -1; + + i = sc->rbhead; + + /* + * Snarf the Ethernet header. + */ + (sc->memcopy)((caddr_t)sc->cbuffs[i], (caddr_t)ehp, sizeof *ehp); + + /* + * As quickly as possible, check if this packet is for us. + * If not, don't waste a single cycle copying the rest of the + * packet in. + * This is only a consideration when FILTER is defined; i.e., when + * we are either running BPF or doing multicasting. + */ + if (!check_eh(sc, ehp, to_bpf)) { + ie_drop_packet_buffer(sc); + sc->sc_arpcom.ac_if.if_ierrors--; /* just this case, it's not an error */ + return -1; + } + totlen -= (offset = sizeof *ehp); + + MGETHDR(*mp, M_DONTWAIT, MT_DATA); + if (!*mp) { + ie_drop_packet_buffer(sc); + return -1; + } + + m = *mp; + m->m_pkthdr.rcvif = &sc->sc_arpcom.ac_if; + m->m_len = MHLEN; + resid = m->m_pkthdr.len = totlen; + top = 0; + mymp = ⊤ + + /* + * This loop goes through and allocates mbufs for all the data we will + * be copying in. It does not actually do the copying yet. + */ + do { /* while (resid > 0) */ + /* + * Try to allocate an mbuf to hold the data that we have. If + * we already allocated one, just get another one and stick it + * on the end (eventually). If we don't already have one, try + * to allocate an mbuf cluster big enough to hold the whole + * packet, if we think it's reasonable, or a single mbuf which + * may or may not be big enough. + * Got that? + */ + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (!m) { + m_freem(top); + ie_drop_packet_buffer(sc); + return -1; + } + m->m_len = MLEN; + } + + if (resid >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) + m->m_len = min(resid, MCLBYTES); + } else { + if (resid < m->m_len) { + if (!top && resid + max_linkhdr <= m->m_len) + m->m_data += max_linkhdr; + m->m_len = resid; + } + } + resid -= m->m_len; + *mymp = m; + mymp = &m->m_next; + } while (resid > 0); + + resid = totlen; + m = top; + thismboff = 0; + head = sc->rbhead; + + /* + * Now we take the mbuf chain (hopefully only one mbuf most of the + * time) and stuff the data into it. There are no possible failures at + * or after this point. + */ + while (resid > 0) { /* while there's stuff left */ + int thislen = ie_buflen(sc, head) - offset; + + /* + * If too much data for the current mbuf, then fill the current + * one up, go to the next one, and try again. + */ + if (thislen > m->m_len - thismboff) { + int newlen = m->m_len - thismboff; + (sc->memcopy)((caddr_t)(sc->cbuffs[head] + offset), + mtod(m, caddr_t) + thismboff, (u_int)newlen); + m = m->m_next; + thismboff = 0; /* new mbuf, so no offset */ + offset += newlen; /* we are now this far + into the packet */ + resid -= newlen; /* so there is this much + left to get */ + continue; + } + + /* + * If there is more than enough space in the mbuf to hold the + * contents of this buffer, copy everything in, advance + * pointers and so on. + */ + if (thislen < m->m_len - thismboff) { + (sc->memcopy)((caddr_t)(sc->cbuffs[head] + offset), + mtod(m, caddr_t) + thismboff, (u_int)thislen); + thismboff += thislen; /* we are this far into the mbuf */ + resid -= thislen; /* and this much is left */ + goto nextbuf; + } + + /* + * Otherwise, there is exactly enough space to put this + * buffer's contents into the current mbuf. Do the combination + * of the above actions. + */ + (sc->memcopy)((caddr_t)(sc->cbuffs[head] + offset), + mtod(m, caddr_t) + thismboff, (u_int)thislen); + m = m->m_next; + thismboff = 0; /* new mbuf, start at the beginning */ + resid -= thislen; /* and we are this far through */ + + /* + * Advance all the pointers. We can get here from either of + * the last two cases, but never the first. + */ + nextbuf: + offset = 0; + sc->rbuffs[head]->ie_rbd_actual = SWAP(0); + sc->rbuffs[head]->ie_rbd_length |= IE_RBD_LAST; + sc->rbhead = head = (head + 1) % sc->nrxbuf; + sc->rbuffs[sc->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; + sc->rbtail = (sc->rbtail + 1) % sc->nrxbuf; + } + + /* + * Unless something changed strangely while we were doing the copy, we + * have now copied everything in from the shared memory. + * This means that we are done. + */ + return 0; +} + +/* + * Read frame NUM from unit UNIT (pre-cached as IE). + * + * This routine reads the RFD at NUM, and copies in the buffers from the list + * of RBD, then rotates the RBD and RFD lists so that the receiver doesn't + * start complaining. Trailers are DROPPED---there's no point in wasting time + * on confusing code to deal with them. Hopefully, this machine will never ARP + * for trailers anyway. + */ +static void +ie_readframe(sc, num) + struct ie_softc *sc; + int num; /* frame number to read */ +{ + int status; + struct mbuf *m = 0; + struct ether_header eh; +#if NBPFILTER > 0 + int bpf_gets_it = 0; +#endif + + status = sc->rframes[num]->ie_fd_status; + + /* Immediately advance the RFD list, since we have copied ours now. */ + sc->rframes[num]->ie_fd_status = SWAP(0); + sc->rframes[num]->ie_fd_last |= IE_FD_LAST; + sc->rframes[sc->rftail]->ie_fd_last &= ~IE_FD_LAST; + sc->rftail = (sc->rftail + 1) % sc->nframes; + sc->rfhead = (sc->rfhead + 1) % sc->nframes; + + if (status & IE_FD_OK) { +#if NBPFILTER > 0 + if (ieget(sc, &m, &eh, &bpf_gets_it)) { +#else + if (ieget(sc, &m, &eh, 0)) { +#endif + sc->sc_arpcom.ac_if.if_ierrors++; + return; + } + } + +#ifdef IEDEBUG + if (sc->sc_debug & IED_READFRAME) + printf("%s: frame from ether %s type %x\n", sc->sc_dev.dv_xname, + ether_sprintf(eh.ether_shost), (u_int)eh.ether_type); +#endif + + if (!m) + return; + + if (last_not_for_us) { + m_freem(last_not_for_us); + last_not_for_us = 0; + } + +#if NBPFILTER > 0 + /* + * Check for a BPF filter; if so, hand it up. + * Note that we have to stick an extra mbuf up front, because bpf_mtap + * expects to have the ether header at the front. + * It doesn't matter that this results in an ill-formatted mbuf chain, + * since BPF just looks at the data. (It doesn't try to free the mbuf, + * tho' it will make a copy for tcpdump.) + */ + if (bpf_gets_it) { + struct mbuf m0; + m0.m_len = sizeof eh; + m0.m_data = (caddr_t)&eh; + m0.m_next = m; + + /* Pass it up. */ + bpf_mtap(sc->sc_arpcom.ac_if.if_bpf, &m0); + } + /* + * A signal passed up from the filtering code indicating that the + * packet is intended for BPF but not for the protocol machinery. + * We can save a few cycles by not handing it off to them. + */ + if (bpf_gets_it == 2) { + last_not_for_us = m; + return; + } +#endif /* NBPFILTER > 0 */ + + /* + * In here there used to be code to check destination addresses upon + * receipt of a packet. We have deleted that code, and replaced it + * with code to check the address much earlier in the cycle, before + * copying the data in; this saves us valuable cycles when operating + * as a multicast router or when using BPF. + */ + + /* + * Finally pass this packet up to higher layers. + */ + ether_input(&sc->sc_arpcom.ac_if, &eh, m); +} + +static void +ie_drop_packet_buffer(sc) + struct ie_softc *sc; +{ + int i; + + do { + /* + * This means we are somehow out of sync. So, we reset the + * adapter. + */ + if (!(sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_USED)) { +#ifdef IEDEBUG + print_rbd(sc->rbuffs[sc->rbhead]); +#endif + log(LOG_ERR, "%s: receive descriptors out of sync at %d\n", + sc->sc_dev.dv_xname, sc->rbhead); + iereset(sc); + return; + } + + i = sc->rbuffs[sc->rbhead]->ie_rbd_actual & IE_RBD_LAST; + + sc->rbuffs[sc->rbhead]->ie_rbd_length |= IE_RBD_LAST; + sc->rbuffs[sc->rbhead]->ie_rbd_actual = SWAP(0); + sc->rbhead = (sc->rbhead + 1) % sc->nrxbuf; + sc->rbuffs[sc->rbtail]->ie_rbd_length &= ~IE_RBD_LAST; + sc->rbtail = (sc->rbtail + 1) % sc->nrxbuf; + } while (!i); +} + + +/* + * Start transmission on an interface. + */ +void +iestart(ifp) + struct ifnet *ifp; +{ + struct ie_softc *sc = iecd.cd_devs[ifp->if_unit]; + struct mbuf *m0, *m; + u_char *buffer; + u_short len; + +/*printf("iestart\n");*/ + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + + if (sc->xmit_free == 0) { + ifp->if_flags |= IFF_OACTIVE; + if (!sc->xmit_busy) + iexmit(sc); + return; + } + + do { + IF_DEQUEUE(&sc->sc_arpcom.ac_if.if_snd, m); + if (!m) + break; + + len = 0; + buffer = sc->xmit_cbuffs[sc->xchead]; + + for (m0 = m; m && (len +m->m_len) < IE_TBUF_SIZE; + m = m->m_next) { + bcopy(mtod(m, caddr_t), buffer, m->m_len); + buffer += m->m_len; + len += m->m_len; + } + if (m) + printf("%s: tbuf overflow\n", sc->sc_dev.dv_xname); + + m_freem(m0); + len = max(len, ETHER_MIN_LEN); + sc->xmit_buffs[sc->xchead]->ie_xmit_flags = SWAP(len); + + sc->xmit_free--; + sc->xchead = (sc->xchead + 1) % NTXBUF; + } while (sc->xmit_free > 0); + + /* If we stuffed any packets into the card's memory, send now. */ + if ((sc->xmit_free < NTXBUF) && (!sc->xmit_busy)) + iexmit(sc); + + return; +} + +/* + * set up IE's ram space + */ +int +ie_setupram(sc) + struct ie_softc *sc; +{ + volatile struct ie_int_sys_conf_ptr *iscp; + volatile struct ie_sys_ctl_block *scb; + int s; + + s = splimp(); + + iscp = sc->iscp; + (sc->memzero)((char *) iscp, sizeof *iscp); + + scb = sc->scb; + (sc->memzero)((char *) scb, sizeof *scb); + + iscp->ie_busy = 1; /* ie_busy == char */ + iscp->ie_scb_offset = MK_16(sc->sc_maddr, scb); + ST_24(iscp->ie_base, sc->sc_iobase); + + (sc->reset_586) (sc); + (sc->chan_attn) (sc); + + delay(100); /* wait a while... */ + + if (iscp->ie_busy) { + splx(s); + return 0; + } + /* + * Acknowledge any interrupts we may have caused... + */ + ie_ack(sc, IE_ST_WHENCE); + splx(s); + + return 1; +} + +void +iereset(sc) + struct ie_softc *sc; +{ + int s = splimp(); + + printf("%s: reset\n", sc->sc_dev.dv_xname); + + /* Clear OACTIVE in case we're called from watchdog (frozen xmit). */ + sc->sc_arpcom.ac_if.if_flags &= ~(IFF_UP | IFF_OACTIVE); + ieioctl(&sc->sc_arpcom.ac_if, SIOCSIFFLAGS, 0); + + /* + * Stop i82586 dead in its tracks. + */ + if (command_and_wait(sc, IE_RU_ABORT | IE_CU_ABORT, 0, 0)) + printf("%s: abort commands timed out\n", sc->sc_dev.dv_xname); + + if (command_and_wait(sc, IE_RU_DISABLE | IE_CU_STOP, 0, 0)) + printf("%s: disable commands timed out\n", sc->sc_dev.dv_xname); + +#ifdef notdef + if (!check_ie_present(sc, sc->sc_maddr, sc->sc_msize)) + panic("ie disappeared!\n"); +#endif + + sc->sc_arpcom.ac_if.if_flags |= IFF_UP; + ieioctl(&sc->sc_arpcom.ac_if, SIOCSIFFLAGS, 0); + + splx(s); +} + +/* + * This is called if we time out. + */ +static void +chan_attn_timeout(rock) + caddr_t rock; +{ + + *(int *)rock = 1; +} + +/* + * Send a command to the controller and wait for it to either complete + * or be accepted, depending on the command. If the command pointer + * is null, then pretend that the command is not an action command. + * If the command pointer is not null, and the command is an action + * command, wait for + * ((volatile struct ie_cmd_common *)pcmd)->ie_cmd_status & MASK + * to become true. + */ +static int +command_and_wait(sc, cmd, pcmd, mask) + struct ie_softc *sc; + int cmd; + volatile void *pcmd; + int mask; +{ + volatile struct ie_cmd_common *cc = pcmd; + volatile struct ie_sys_ctl_block *scb = sc->scb; + volatile int timedout = 0; + extern int hz; + + scb->ie_command = (u_short)cmd; + + if (IE_ACTION_COMMAND(cmd) && pcmd) { + (sc->chan_attn)(sc); + +#if 0 + /* + * XXX + * I don't think this timeout works on suns. + * we are at splimp() in the loop, and the timeout + * stuff runs at software spl (so it is masked off?). + */ + + /* + * According to the packet driver, the minimum timeout should + * be .369 seconds, which we round up to .4. + */ + timeout(chan_attn_timeout, (caddr_t)&timedout, 2 * hz / 5); +#endif + + /* + * Now spin-lock waiting for status. This is not a very nice + * thing to do, but I haven't figured out how, or indeed if, we + * can put the process waiting for action to sleep. (We may + * be getting called through some other timeout running in the + * kernel.) + */ + for (;;) + if ((cc->ie_cmd_status & mask) || timedout) + break; +#if 0 + untimeout(chan_attn_timeout, (caddr_t)&timedout); +#endif + + return timedout; + } else { + /* + * Otherwise, just wait for the command to be accepted. + */ + (sc->chan_attn)(sc); + + while (scb->ie_command) + ; /* XXX spin lock */ + + return 0; + } +} + +/* + * Run the time-domain reflectometer. + */ +static void +run_tdr(sc, cmd) + struct ie_softc *sc; + struct ie_tdr_cmd *cmd; +{ + int result; + + cmd->com.ie_cmd_status = SWAP(0); + cmd->com.ie_cmd_cmd = IE_CMD_TDR | IE_CMD_LAST; + cmd->com.ie_cmd_link = SWAP(0xffff); + + sc->scb->ie_command_list = MK_16(sc->sc_maddr, cmd); + cmd->ie_tdr_time = SWAP(0); + + if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) || + !(cmd->com.ie_cmd_status & IE_STAT_OK)) + result = 0x10000; /* XXX */ + else + result = cmd->ie_tdr_time; + + ie_ack(sc, IE_ST_WHENCE); + + if (result & IE_TDR_SUCCESS) + return; + + if (result & 0x10000) + printf("%s: TDR command failed\n", sc->sc_dev.dv_xname); + else if (result & IE_TDR_XCVR) + printf("%s: transceiver problem\n", sc->sc_dev.dv_xname); + else if (result & IE_TDR_OPEN) + printf("%s: TDR detected an open %d clocks away\n", + sc->sc_dev.dv_xname, result & IE_TDR_TIME); + else if (result & IE_TDR_SHORT) + printf("%s: TDR detected a short %d clocks away\n", + sc->sc_dev.dv_xname, result & IE_TDR_TIME); + else + printf("%s: TDR returned unknown status %x\n", + sc->sc_dev.dv_xname, result); +} + +#ifdef notdef +/* ALIGN works on 8 byte boundaries.... but 4 byte boundaries are ok for sun */ +#define _ALLOC(p, n) (bzero(p, n), p += n, p - n) +#define ALLOC(p, n) _ALLOC(p, ALIGN(n)) /* XXX convert to this? */ +#endif + +static inline caddr_t +Align(ptr) + caddr_t ptr; +{ + u_long l = (u_long)ptr; + + l = (l + 3) & ~3L; + return (caddr_t)l; +} + +/* + * setup_bufs: set up the buffers + * + * we have a block of KVA at sc->buf_area which is of size sc->buf_area_sz. + * this is to be used for the buffers. the chip indexs its control data + * structures with 16 bit offsets, and it indexes actual buffers with + * 24 bit addresses. so we should allocate control buffers first so that + * we don't overflow the 16 bit offset field. The number of transmit + * buffers is fixed at compile time. + * + * note: this function was written to be easy to understand, rather than + * highly efficient (it isn't in the critical path). + */ +static void +setup_bufs(sc) + struct ie_softc *sc; +{ + caddr_t ptr = sc->buf_area; /* memory pool */ + volatile struct ie_recv_frame_desc *rfd = (void *) ptr; + volatile struct ie_recv_buf_desc *rbd; + int n, r; + + /* + * step 0: zero memory and figure out how many recv buffers and + * frames we can have. XXX CURRENTLY HARDWIRED AT MAX + */ + (sc->memzero)(ptr, sc->buf_area_sz); + ptr = Align(ptr); /* set alignment and stick with it */ + + n = (int)Align(sizeof(struct ie_xmit_cmd)) + + (int)Align(sizeof(struct ie_xmit_buf)) + IE_TBUF_SIZE; + n *= NTXBUF; /* n = total size of xmit area */ + + n = sc->buf_area_sz - n;/* n = free space for recv stuff */ + + r = (int)Align(sizeof(struct ie_recv_frame_desc)) + + (((int)Align(sizeof(struct ie_recv_buf_desc)) + IE_RBUF_SIZE) * B_PER_F); + + /* r = size of one R frame */ + + sc->nframes = n / r; + if (sc->nframes <= 0) + panic("ie: bogus buffer calc\n"); + if (sc->nframes > MXFRAMES) + sc->nframes = MXFRAMES; + + sc->nrxbuf = sc->nframes * B_PER_F; + +#ifdef IEDEBUG + printf("IEDEBUG: %d frames %d bufs\n", sc->nframes, sc->nrxbuf); +#endif + + /* + * step 1a: lay out and zero frame data structures for transmit and recv + */ + for (n = 0; n < NTXBUF; n++) { + sc->xmit_cmds[n] = (volatile struct ie_xmit_cmd *) ptr; + ptr = Align(ptr + sizeof(struct ie_xmit_cmd)); + } + + for (n = 0; n < sc->nframes; n++) { + sc->rframes[n] = (volatile struct ie_recv_frame_desc *) ptr; + ptr = Align(ptr + sizeof(struct ie_recv_frame_desc)); + } + + /* + * step 1b: link together the recv frames and set EOL on last one + */ + for (n = 0; n < sc->nframes; n++) { + sc->rframes[n]->ie_fd_next = + MK_16(sc->sc_maddr, sc->rframes[(n + 1) % sc->nframes]); + } + sc->rframes[sc->nframes - 1]->ie_fd_last |= IE_FD_LAST; + + /* + * step 2a: lay out and zero frame buffer structures for xmit and recv + */ + for (n = 0; n < NTXBUF; n++) { + sc->xmit_buffs[n] = (volatile struct ie_xmit_buf *) ptr; + ptr = Align(ptr + sizeof(struct ie_xmit_buf)); + } + + for (n = 0; n < sc->nrxbuf; n++) { + sc->rbuffs[n] = (volatile struct ie_recv_buf_desc *) ptr; + ptr = Align(ptr + sizeof(struct ie_recv_buf_desc)); + } + + /* + * step 2b: link together recv bufs and set EOL on last one + */ + for (n = 0; n < sc->nrxbuf; n++) { + sc->rbuffs[n]->ie_rbd_next = + MK_16(sc->sc_maddr, sc->rbuffs[(n + 1) % sc->nrxbuf]); + } + sc->rbuffs[sc->nrxbuf - 1]->ie_rbd_length |= IE_RBD_LAST; + + /* + * step 3: allocate the actual data buffers for xmit and recv + * recv buffer gets linked into recv_buf_desc list here + */ + for (n = 0; n < NTXBUF; n++) { + sc->xmit_cbuffs[n] = (u_char *) ptr; + ptr = Align(ptr + IE_TBUF_SIZE); + } + + /* Pointers to last packet sent and next available transmit buffer. */ + sc->xchead = sc->xctail = 0; + + /* Clear transmit-busy flag and set number of free transmit buffers. */ + sc->xmit_busy = 0; + sc->xmit_free = NTXBUF; + + for (n = 0; n < sc->nrxbuf; n++) { + sc->cbuffs[n] = (char *) ptr; /* XXX why char vs uchar? */ + sc->rbuffs[n]->ie_rbd_length = SWAP(IE_RBUF_SIZE); + ST_24(sc->rbuffs[n]->ie_rbd_buffer, MK_24(sc->sc_iobase, ptr)); + ptr = Align(ptr + IE_RBUF_SIZE); + } + + /* + * step 4: set the head and tail pointers on receive to keep track of + * the order in which RFDs and RBDs are used. link in recv frames + * and buffer into the scb. + */ + + sc->rfhead = 0; + sc->rftail = sc->nframes - 1; + sc->rbhead = 0; + sc->rbtail = sc->nrxbuf - 1; + + sc->scb->ie_recv_list = MK_16(sc->sc_maddr, sc->rframes[0]); + sc->rframes[0]->ie_fd_buf_desc = MK_16(sc->sc_maddr, sc->rbuffs[0]); + +#ifdef IEDEBUG + printf("IE_DEBUG: reserved %d bytes\n", ptr - sc->buf_area); +#endif +} + +/* + * Run the multicast setup command. + * Called at splimp(). + */ +static int +mc_setup(sc, ptr) + struct ie_softc *sc; + void *ptr; +{ + volatile struct ie_mcast_cmd *cmd = ptr; + + cmd->com.ie_cmd_status = SWAP(0); + cmd->com.ie_cmd_cmd = IE_CMD_MCAST | IE_CMD_LAST; + cmd->com.ie_cmd_link = SWAP(0xffff); + + (sc->memcopy)((caddr_t)sc->mcast_addrs, (caddr_t)cmd->ie_mcast_addrs, + sc->mcast_count * sizeof *sc->mcast_addrs); + + cmd->ie_mcast_bytes = + SWAP(sc->mcast_count * ETHER_ADDR_LEN); /* grrr... */ + + sc->scb->ie_command_list = MK_16(sc->sc_maddr, cmd); + if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) || + !(cmd->com.ie_cmd_status & IE_STAT_OK)) { + printf("%s: multicast address setup command failed\n", + sc->sc_dev.dv_xname); + return 0; + } + return 1; +} + +/* + * This routine takes the environment generated by check_ie_present() and adds + * to it all the other structures we need to operate the adapter. This + * includes executing the CONFIGURE, IA-SETUP, and MC-SETUP commands, starting + * the receiver unit, and clearing interrupts. + * + * THIS ROUTINE MUST BE CALLED AT splimp() OR HIGHER. + */ +int +ieinit(sc) + struct ie_softc *sc; +{ + volatile struct ie_sys_ctl_block *scb = sc->scb; + void *ptr; + int n; + + ptr = sc->buf_area; + + /* + * Send the configure command first. + */ + { + volatile struct ie_config_cmd *cmd = ptr; + + scb->ie_command_list = MK_16(sc->sc_maddr, cmd); + cmd->com.ie_cmd_status = SWAP(0); + cmd->com.ie_cmd_cmd = IE_CMD_CONFIG | IE_CMD_LAST; + cmd->com.ie_cmd_link = SWAP(0xffff); + + ie_setup_config(cmd, sc->promisc, 0); + + if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) || + !(cmd->com.ie_cmd_status & IE_STAT_OK)) { + printf("%s: configure command failed\n", + sc->sc_dev.dv_xname); + return 0; + } + } + + /* + * Now send the Individual Address Setup command. + */ + { + volatile struct ie_iasetup_cmd *cmd = ptr; + + scb->ie_command_list = MK_16(sc->sc_maddr, cmd); + cmd->com.ie_cmd_status = SWAP(0); + cmd->com.ie_cmd_cmd = IE_CMD_IASETUP | IE_CMD_LAST; + cmd->com.ie_cmd_link = SWAP(0xffff); + + (sc->memcopy)(sc->sc_arpcom.ac_enaddr, + (caddr_t)&cmd->ie_address, sizeof cmd->ie_address); + + if (command_and_wait(sc, IE_CU_START, cmd, IE_STAT_COMPL) || + !(cmd->com.ie_cmd_status & IE_STAT_OK)) { + printf("%s: individual address setup command failed\n", + sc->sc_dev.dv_xname); + return 0; + } + } + + /* + * Now run the time-domain reflectometer. + */ + run_tdr(sc, ptr); + + /* + * Acknowledge any interrupts we have generated thus far. + */ + ie_ack(sc, IE_ST_WHENCE); + + /* + * Set up the transmit and recv buffers. + */ + setup_bufs(sc); + + sc->sc_arpcom.ac_if.if_flags |= IFF_RUNNING; /* tell higher levels that we are here */ + + sc->scb->ie_recv_list = MK_16(sc->sc_maddr, sc->rframes[0]); + command_and_wait(sc, IE_RU_START, 0, 0); + + ie_ack(sc, IE_ST_WHENCE); + + if (sc->run_586) + (sc->run_586)(sc); + + return 0; +} + +static void +iestop(sc) + struct ie_softc *sc; +{ + + command_and_wait(sc, IE_RU_DISABLE, 0, 0); +} + +int +ieioctl(ifp, cmd, data) + register struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct ie_softc *sc = iecd.cd_devs[ifp->if_unit]; + struct ifaddr *ifa = (struct ifaddr *)data; + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + + s = splimp(); + + switch(cmd) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch(ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + ieinit(sc); + arp_ifinit(&sc->sc_arpcom, ifa); + break; +#endif +#ifdef NS + /* XXX - This code is probably wrong. */ + case AF_NS: + { + struct ns_addr *ina = &IA_SNS(ifa)->sns_addr; + + if (ns_nullhost(*ina)) + ina->x_host = + *(union ns_host *)(sc->sc_arpcom.ac_enaddr); + else + bcopy(ina->x_host.c_host, + sc->sc_arpcom.ac_enaddr, + sizeof(sc->sc_arpcom.ac_enaddr)); + /* Set new address. */ + ieinit(sc); + break; + } +#endif /* NS */ + default: + ieinit(sc); + break; + } + break; + + case SIOCSIFFLAGS: + sc->promisc = ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); + if ((ifp->if_flags & IFF_UP) == 0 && + (ifp->if_flags & IFF_RUNNING) != 0) { + /* + * If interface is marked down and it is running, then + * stop it. + */ + iestop(sc); + ifp->if_flags &= ~IFF_RUNNING; + } else if ((ifp->if_flags & IFF_UP) != 0 && + (ifp->if_flags & IFF_RUNNING) == 0) { + /* + * If interface is marked up and it is stopped, then + * start it. + */ + ieinit(sc); + } else { + /* + * Reset the interface to pick up changes in any other + * flags that affect hardware registers. + */ + iestop(sc); + ieinit(sc); + } +#ifdef IEDEBUG + if (ifp->if_flags & IFF_DEBUG) + sc->sc_debug = IED_ALL; + else + sc->sc_debug = 0; +#endif + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->sc_arpcom): + ether_delmulti(ifr, &sc->sc_arpcom); + + if (error == ENETRESET) { + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + mc_reset(sc); + error = 0; + } + break; + + default: + error = EINVAL; + } + splx(s); + return error; +} + +static void +mc_reset(sc) + struct ie_softc *sc; +{ + struct ether_multi *enm; + struct ether_multistep step; + + /* + * Step through the list of addresses. + */ + sc->mcast_count = 0; + ETHER_FIRST_MULTI(step, &sc->sc_arpcom, enm); + while (enm) { + if (sc->mcast_count >= MAXMCAST || + bcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) { + sc->sc_arpcom.ac_if.if_flags |= IFF_ALLMULTI; + ieioctl(&sc->sc_arpcom.ac_if, SIOCSIFFLAGS, (void *)0); + goto setflag; + } + + bcopy(enm->enm_addrlo, &sc->mcast_addrs[sc->mcast_count], 6); + sc->mcast_count++; + ETHER_NEXT_MULTI(step, enm); + } +setflag: + sc->want_mcsetup = 1; +} + +#ifdef IEDEBUG +void +print_rbd(rbd) + volatile struct ie_recv_buf_desc *rbd; +{ + + printf("RBD at %08lx:\nactual %04x, next %04x, buffer %08x\n" + "length %04x, mbz %04x\n", (u_long)rbd, rbd->ie_rbd_actual, + rbd->ie_rbd_next, rbd->ie_rbd_buffer, rbd->ie_rbd_length, + rbd->mbz); +} +#endif diff --git a/sys/arch/mvme68k/dev/if_ie.h b/sys/arch/mvme68k/dev/if_ie.h new file mode 100644 index 00000000000..a17de666905 --- /dev/null +++ b/sys/arch/mvme68k/dev/if_ie.h @@ -0,0 +1,191 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * XXX where else is this from? + * if_sunie.h + * + * sun's ie interface + */ + +/* + * programming notes: + * + * the ie chip operates in a 24 bit address space. + * + * most ie interfaces appear to be divided into two parts: + * - generic 586 stuff + * - board specific + * + * generic: + * the generic stuff of the ie chip is all done with data structures + * that live in the chip's memory address space. the chip expects + * its main data structure (the sys conf ptr -- SCP) to be at a fixed + * address in its 24 bit space: 0xfffff4 + * + * the SCP points to another structure called the ISCP. + * the ISCP points to another structure called the SCB. + * the SCB has a status field, a linked list of "commands", and + * a linked list of "receive buffers". these are data structures that + * live in memory, not registers. + * + * board: + * to get the chip to do anything, you first put a command in the + * command data structure list. then you have to signal "attention" + * to the chip to get it to look at the command. how you + * signal attention depends on what board you have... on PC's + * there is an i/o port number to do this, on sun's there is a + * register bit you toggle. + * + * to get data from the chip you program it to interrupt... + * + * + * sun issues: + * + * there are 3 kinds of sun "ie" interfaces: + * 1 - a VME/multibus card + * 2 - an on-board interface (sun3's, sun-4/100's, and sun-4/200's) + * 3 - another VME board called the 3E + * + * the VME boards lives in vme16 space. only 16 and 8 bit accesses + * are allowed, so functions that copy data must be aware of this. + * + * the chip is an intel chip. this means that the byte order + * on all the "short"s in the chip's data structures is wrong. + * so, constants described in the intel docs are swapped for the sun. + * that means that any buffer pointers you give the chip must be + * swapped to intel format. yuck. + * + * VME/multibus interface: + * for the multibus interface the board ignores the top 4 bits + * of the chip address. the multibus interface seems to have its + * own MMU like page map (without protections or valid bits, etc). + * there are 256 pages of physical memory on the board (each page + * is 1024 bytes). there are 1024 slots in the page map. so, + * a 1024 byte page takes up 10 bits of address for the offset, + * and if there are 1024 slots in the page that is another 10 bits + * of the address. that makes a 20 bit address, and as stated + * earlier the board ignores the top 4 bits, so that accounts + * for all 24 bits of address. + * + * note that the last entry of the page map maps the top of the + * 24 bit address space and that the SCP is supposed to be at + * 0xfffff4 (taking into account allignment). so, + * for multibus, that entry in the page map has to be used for the SCP. + * + * the page map effects BOTH how the ie chip sees the + * memory, and how the host sees it. + * + * the page map is part of the "register" area of the board + * + * on-board interface: + * + * <fill in useful info later> + * + * + * VME3E interface: + * + * <fill in useful info later> + * + */ + +/* + * PART 1: VME/multibus defs + */ +#define IEVME_PAGESIZE 1024 /* bytes */ +#define IEVME_PAGSHIFT 10 /* bits */ +#define IEVME_NPAGES 256 /* number of pages on chip */ +#define IEVME_MAPSZ 1024 /* number of entries in the map */ + +/* + * PTE for the page map + */ +#define IEVME_SBORDR 0x8000 /* sun byte order */ +#define IEVME_IBORDR 0x0000 /* intel byte ordr */ + +#define IEVME_P2MEM 0x2000 /* memory is on P2 */ +#define IEVME_OBMEM 0x0000 /* memory is on board */ + +#define IEVME_PGMASK 0x0fff /* gives the physical page frame number */ + +struct ievme { + u_short pgmap[IEVME_MAPSZ]; + u_short xxx[32]; /* prom */ + u_short status; /* see below for bits */ + u_short xxx2; /* filler */ + u_short pectrl; /* parity control (see below) */ + u_short peaddr; /* low 16 bits of address */ +}; + +/* + * status bits + */ +#define IEVME_RESET 0x8000 /* reset board */ +#define IEVME_ONAIR 0x4000 /* go out of loopback 'on-air' */ +#define IEVME_ATTEN 0x2000 /* attention */ +#define IEVME_IENAB 0x1000 /* interrupt enable */ +#define IEVME_PEINT 0x0800 /* parity error interrupt enable */ +#define IEVME_PERR 0x0200 /* parity error flag */ +#define IEVME_INT 0x0100 /* interrupt flag */ +#define IEVME_P2EN 0x0020 /* enable p2 bus */ +#define IEVME_256K 0x0010 /* 256kb rams */ +#define IEVME_HADDR 0x000f /* mask for bits 17-20 of address */ + +/* + * parity control + */ +#define IEVME_PARACK 0x0100 /* parity error ack */ +#define IEVME_PARSRC 0x0080 /* parity error source */ +#define IEVME_PAREND 0x0040 /* which end of the data got the error */ +#define IEVME_PARADR 0x000f /* mask to get bits 17-20 of parity address */ + + +/* + * PART 2: the on-board interface + */ +struct ieob { + u_short porthigh; + u_short portlow; + u_long attn; +}; +#define IE_PORT_NEWSCPADDR 0x00000002 +#define IE_PORT_RESET 0x00000000 + +#define IEOB_ADBASE 0xff000000 /* KVA base addr of 24 bit address space */ + +/* + * PART 3: the 3E board + */ + +/* + * not supported (yet?) + */ diff --git a/sys/arch/mvme68k/dev/ipic.c b/sys/arch/mvme68k/dev/ipic.c new file mode 100644 index 00000000000..400f57b25ba --- /dev/null +++ b/sys/arch/mvme68k/dev/ipic.c @@ -0,0 +1,195 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/ioctl.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/tty.h> +#include <sys/uio.h> +#include <sys/callout.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/syslog.h> +#include <sys/fcntl.h> +#include <sys/device.h> +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <mvme68k/dev/ipicreg.h> +#include <mvme68k/dev/mcreg.h> + +void ipicattach __P((struct device *, struct device *, void *)); +int ipicmatch __P((struct device *, void *, void *)); + +struct cfdriver ipiccd = { + NULL, "ipic", ipicmatch, ipicattach, + DV_DULL, sizeof(struct ipicsoftc), 0 +}; + +int +ipicmatch(parent, cf, args) + struct device *parent; + void *cf; + void *args; +{ + struct confargs *ca = args; + struct ipicreg *ipic = (struct ipicreg *)ca->ca_vaddr; + + if (badvaddr(ipic, 1) || ipic->ipic_chipid != IPIC_CHIPID) + return (0); + return (1); +} + +int +ipicprint(args, bus) + void *args; + char *bus; +{ + struct confargs *ca = args; + + printf(" slot %d", ca->ca_offset); + if (ca->ca_vec > 0) + printf(" vec %d", ca->ca_vec); + if (ca->ca_ipl > 0) + printf(" ipl %d", ca->ca_ipl); + return (UNCONF); +} + +int +ipicscan(parent, child, args) + struct device *parent; + void *child, *args; +{ + struct cfdata *cf = child; + struct ipicsoftc *sc = (struct ipicsoftc *)parent; + register struct confargs *ca = args; + struct confargs oca; + int slot, n = 0; + caddr_t ipv, ipp; + struct ipid *ipid; + +#if 0 +/* XXX all these are indirect!! how to fix? */ + if (parent->dv_cfdata->cf_driver->cd_indirect) { + printf(" indirect devices not supported\n"); + return 0; + } +#endif + + /* XXX can we determing IPIC_IPSPACE automatically? */ + for (slot = 0; slot < sc->sc_nip; slot++) { + ipp = sc->sc_ipspace + (slot * IPIC_IP_MODSIZE); + if (badpaddr(ipp + IPIC_IP_IDOFFSET, 2)) + continue; + + ipv = mapiodev(ipp, NBPG); + if (ipv == NULL) + continue; + + ipid = (struct ipid *)(ipv + IPIC_IP_IDOFFSET); + if (ipid->ipid_A != 'A' || ipid->ipid_P != 'P' || + ipid->ipid_I != 'I' || ipid->ipid_C != 'C' || + (u_char)cf->cf_loc[0] != ipid->ipid_manu || + (u_char)cf->cf_loc[1] != ipid->ipid_prod) { + unmapiodev(ipv, NBPG); + continue; + } + + bzero(&oca, sizeof oca); + oca.ca_bustype = BUS_IP; + oca.ca_offset = slot; /* slot number */ + oca.ca_paddr = ipp; + oca.ca_vaddr = ipv; + oca.ca_len = NBPG; + oca.ca_ipl = cf->cf_loc[2]; + oca.ca_vec = cf->cf_loc[3]; + if (oca.ca_ipl > 0 && oca.ca_vec == -1) + oca.ca_vec = intr_freevec(); + + oca.ca_master = (void *)sc; + oca.ca_name = cf->cf_driver->cd_name; + + if ((*cf->cf_driver->cd_match)(parent, cf, &oca) == 0) { + unmapiodev(ipv, NBPG); + continue; + } + config_attach(parent, cf, &oca, ipicprint); + n++; + } + return (n); +} + +void +ipicattach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct ipicsoftc *sc = (struct ipicsoftc *)self; + struct confargs *ca = args; + + sc->sc_ipic = (struct ipicreg *)ca->ca_vaddr; + sc->sc_ipspace = (caddr_t)IPIC_IPSPACE; + sc->sc_nip = 2; + + printf(": rev %d\n", sc->sc_ipic->ipic_chiprev); + + sc->sc_ipic->ipic_reset = IPIC_RESET; + delay(2); + + config_search(ipicscan, self, args); +} + +caddr_t +ipicmap(sc, addr, len) + struct ipicsoftc *sc; + caddr_t addr; + int len; +{ + return (NULL); +} + +void +ipicunmap(sc, addr, len) + struct ipicsoftc *sc; + caddr_t addr; + int len; +{ +} + +int +ipicintr_establish(vec, ih) + int vec; + struct intrhand *ih; +{ + return (intr_establish(vec, ih)); +} diff --git a/sys/arch/mvme68k/dev/ipicreg.h b/sys/arch/mvme68k/dev/ipicreg.h new file mode 100644 index 00000000000..b329a7342f9 --- /dev/null +++ b/sys/arch/mvme68k/dev/ipicreg.h @@ -0,0 +1,114 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +struct ipicreg { + volatile u_char ipic_chipid; +#define IPIC_CHIPID 0x23 + volatile u_char ipic_chiprev; + volatile u_char x0[2]; + volatile u_short ipic_base[4]; /* [slot] */ + volatile u_char ipic_size[4]; /* [slot] */ + volatile u_char ipic_irq[4][2]; /* [slot][irq#] */ +#define IPIC_IRQ_PLTY 0x80 /* 1 = rising edge activated */ +#define IPIC_IRQ_EL 0x40 /* 1 = edge, 0 = level sensitive */ +#define IPIC_IRQ_INT 0x20 /* interrupt is active */ +#define IPIC_IRQ_IEN 0x10 /* enable interrupt */ +#define IPIC_IRQ_ICLR 0x08 /* clear interrupt */ +#define IPIC_IRQ_IPLMASK 0x07 + volatile u_char ipic_ctl[4]; /* [slot] */ +#define IPIC_CTL_ERR 0x80 /* error from IP module */ +#define IPIC_CTL_RTMASK 0x30 /* recovery time, measured in ms */ +#define IPIC_CTL_RT0MS 0x00 +#define IPIC_CTL_RT2MS 0x10 +#define IPIC_CTL_RT4MS 0x20 +#define IPIC_CTL_RT8MS 0x30 +#define IPIC_CTL_WIDTHMASK 0x0c /* bus width */ +#define IPIC_CTL_WIDTH32 0x00 +#define IPIC_CTL_WIDTH8 0x04 +#define IPIC_CTL_WIDTH16 0x08 +#define IPIC_CTL_MEN 0x01 /* ??? */ + volatile u_char x1[3]; + volatile u_char ipic_reset; +#define IPIC_RESET 0x01 /* bit clears automatically after 1ms */ +}; + +/* + * the following macros convert the size in bytes to/from the + * ipic_Xsize reg values. you should ensure your size in bytes + * is a legal value, which are 16K, or all the powers of 2 from + * 64K to 8M. + */ +#define IPIC_SIZE_BTOR(x) \ + (((x) == 16*1024) ? (0xff) : (((x) / 64*1024) - 1)) +#define IPIC_SIZE_RTOB(x) \ + (((x) == 0xff) ? (16*1024) : (((x) + 1) * 64*1024)) + +struct ipid { + volatile u_char ipid_I; /* must be 'I' */ + volatile u_char :8; + volatile u_char ipid_P; /* must be 'P' */ + volatile u_char :8; + volatile u_char ipid_A; /* must be 'A' */ + volatile u_char :8; + volatile u_char ipid_C; /* must be 'C' */ + volatile u_char :8; + volatile u_char ipid_manu; + volatile u_char :8; + volatile u_char ipid_prod; + volatile u_char :8; + volatile u_char ipid_rev; + volatile u_char :8; + volatile u_char ipid_zero; + volatile u_char :8; + volatile u_char ipid_didl; + volatile u_char :8; + volatile u_char ipid_didh; + volatile u_char :8; + volatile u_char ipid_pspecn; /* # shorts in pack-spec id */ + volatile u_char :8; + volatile u_char ipid_crc; + volatile u_char :8; + volatile u_char ipic_pspecbase; /* start of pack-spec id */ +}; + +#define IPIC_IPSPACE 0xfff58000 +#define IPIC_IP_MODSIZE 0x00000100 +#define IPIC_IP_IDOFFSET 0x80 +#define IPIC_IP_REGOFFSET 0x00 + +struct ipicsoftc { + struct device sc_dev; + struct ipicreg *sc_ipic; + + caddr_t sc_ipspace; + int sc_nip; +}; diff --git a/sys/arch/mvme68k/dev/lp.c b/sys/arch/mvme68k/dev/lp.c new file mode 100644 index 00000000000..77f02834c2a --- /dev/null +++ b/sys/arch/mvme68k/dev/lp.c @@ -0,0 +1,144 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/ioctl.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/tty.h> +#include <sys/uio.h> +#include <sys/callout.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/syslog.h> +#include <sys/fcntl.h> +#include <sys/device.h> +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <mvme68k/dev/pccreg.h> + +struct lpsoftc { + struct device sc_dev; + struct intrhand sc_ih; + struct pccreg *sc_pcc; +}; + +void lpattach __P((struct device *, struct device *, void *)); +int lpmatch __P((struct device *, void *, void *)); + +struct cfdriver lpcd = { + NULL, "lp", lpmatch, lpattach, + DV_DULL, sizeof(struct lpsoftc), 0 +}; + +int lpintr __P((void *)); + +/* + * a PCC chip always has an lp attached to it. + */ +int +lpmatch(parent, cf, args) + struct device *parent; + void *cf; + void *args; +{ + return (1); +} + +void +lpattach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct lpsoftc *sc = (struct lpsoftc *)self; + struct confargs *ca = args; + + sc->sc_pcc = (struct pccreg *)ca->ca_master; + + printf(": unsupported\n"); + + sc->sc_ih.ih_fn = lpintr; + sc->sc_ih.ih_arg = sc; + sc->sc_ih.ih_ipl = ca->ca_ipl; + pccintr_establish(PCCV_PRINTER, &sc->sc_ih); + + sc->sc_pcc->pcc_lpirq = ca->ca_ipl | PCC_IRQ_IEN | PCC_LPIRQ_ACK; +} + +int +lpintr(dev) + void *dev; +{ + struct lpsoftc *sc = dev; + + return (0); +} + +/*ARGSUSED*/ +int +lpopen(dev, flag, mode) + dev_t dev; + int flag, mode; +{ + + return (0); +} + +/*ARGSUSED*/ +int +lpclose(dev, flag, mode) + dev_t dev; + int flag, mode; +{ + + return (0); +} + +/*ARGSUSED*/ +int +lpwrite(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ +} + +lpioctl(dev, cmd, data, flag, p) + dev_t dev; + u_long cmd; + caddr_t data; + int flag; + struct proc *p; +{ +} + diff --git a/sys/arch/mvme68k/dev/mc.c b/sys/arch/mvme68k/dev/mc.c new file mode 100644 index 00000000000..1c7dc2889d1 --- /dev/null +++ b/sys/arch/mvme68k/dev/mc.c @@ -0,0 +1,217 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * VME162 MCchip + */ +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/ioctl.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/tty.h> +#include <sys/uio.h> +#include <sys/callout.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/syslog.h> +#include <sys/fcntl.h> +#include <sys/device.h> +#include <machine/cpu.h> +#include <machine/autoconf.h> +#include <dev/cons.h> + +#include <mvme68k/dev/mcreg.h> + +struct mcsoftc { + struct device sc_dev; + caddr_t sc_vaddr; + caddr_t sc_paddr; + struct mcreg *sc_mc; + struct intrhand sc_nmiih; +}; + +void mcattach __P((struct device *, struct device *, void *)); +int mcmatch __P((struct device *, void *, void *)); +int mcabort __P((struct frame *)); + +struct cfdriver mccd = { + NULL, "mc", mcmatch, mcattach, + DV_DULL, sizeof(struct mcsoftc), 0 +}; + +struct mcreg *sys_mc = NULL; + +int +mcmatch(parent, vcf, args) + struct device *parent; + void *vcf, *args; +{ + struct cfdata *cf = vcf; + struct confargs *ca = args; + struct mcreg *mc = (struct mcreg *)(IIOV(ca->ca_paddr) + MC_MCCHIP_OFF); + + if (cputyp != CPU_162 || badvaddr(mc, 1) || + mc->mc_chipid != MC_CHIPID) + return (0); + return (1); +} + +int +mc_print(args, bus) + void *args; + char *bus; +{ + struct confargs *ca = args; + + if (ca->ca_offset != -1) + printf(" offset 0x%x", ca->ca_offset); + if (ca->ca_ipl > 0) + printf(" ipl %d", ca->ca_ipl); + return (UNCONF); +} + +int +mc_scan(parent, child, args) + struct device *parent; + void *child, *args; +{ + struct cfdata *cf = child; + struct mcsoftc *sc = (struct mcsoftc *)parent; + struct confargs *ca = args; + struct confargs oca; + + if (parent->dv_cfdata->cf_driver->cd_indirect) { + printf(" indirect devices not supported\n"); + return 0; + } + + bzero(&oca, sizeof oca); + oca.ca_offset = cf->cf_loc[0]; + oca.ca_ipl = cf->cf_loc[1]; + if (oca.ca_offset != -1 && ISIIOVA(sc->sc_vaddr + oca.ca_offset)) { + oca.ca_paddr = sc->sc_paddr + oca.ca_offset; + oca.ca_vaddr = sc->sc_vaddr + oca.ca_offset; + } else { + oca.ca_paddr = (caddr_t)-1; + oca.ca_vaddr = (caddr_t)-1; + } + oca.ca_bustype = BUS_MC; + oca.ca_master = (void *)sc->sc_mc; + oca.ca_name = cf->cf_driver->cd_name; + if ((*cf->cf_driver->cd_match)(parent, cf, &oca) == 0) + return (0); + config_attach(parent, cf, &oca, mc_print); + return (1); +} + +void +mcattach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct confargs *ca = args; + struct mcsoftc *sc = (struct mcsoftc *)self; + int i; + + if (sys_mc) + panic("mc already attached!"); + + /* + * since we know ourself to land in intiobase land, + * we must adjust our address + */ + sc->sc_paddr = ca->ca_paddr; + sc->sc_vaddr = (caddr_t)IIOV(sc->sc_paddr); + sc->sc_mc = (struct mcreg *)(sc->sc_vaddr + MC_MCCHIP_OFF); + sys_mc = sc->sc_mc; + + printf(": rev %d\n", sc->sc_mc->mc_chiprev); + + sc->sc_nmiih.ih_fn = mcabort; + sc->sc_nmiih.ih_arg = 0; + sc->sc_nmiih.ih_ipl = 7; + sc->sc_nmiih.ih_wantframe = 1; + mcintr_establish(MCV_ABORT, &sc->sc_nmiih); + + sc->sc_mc->mc_abortirq = 7 | MC_IRQ_IEN | MC_IRQ_ICLR; + sc->sc_mc->mc_vecbase = MC_VECBASE; + + sc->sc_mc->mc_genctl |= MC_GENCTL_IEN; /* global irq enable */ + + config_search(mc_scan, self, args); +} + +/* + * MC interrupts land in a MC_NVEC sized hole starting at MC_VECBASE + */ +int +mcintr_establish(vec, ih) + int vec; + struct intrhand *ih; +{ + if (vec >= MC_NVEC) { + printf("mc: illegal vector: 0x%x\n", vec); + panic("mcintr_establish"); + } + return (intr_establish(MC_VECBASE+vec, ih)); +} + +int +mcabort(frame) + struct frame *frame; +{ + /* wait for it to debounce */ + while (sys_mc->mc_abortirq & MC_ABORT_ABS) + ; + + sys_mc->mc_abortirq = sys_mc->mc_abortirq | MC_IRQ_ICLR; + + nmihand(frame); + return (1); +} + +#include "flash.h" + +#if NFLASH > 0 +void +mc_enableflashwrite(on) + int on; +{ + struct mcsoftc *sc = (struct mcsoftc *) mccd.cd_devs[0]; + volatile char *ena, x; + + ena = sc->sc_vaddr + + (on ? MC_ENAFLASHWRITE_OFFSET : MC_DISFLASHWRITE_OFFSET); + x = *ena; +} +#endif diff --git a/sys/arch/mvme68k/dev/mcreg.h b/sys/arch/mvme68k/dev/mcreg.h new file mode 100644 index 00000000000..ead6d812d0f --- /dev/null +++ b/sys/arch/mvme68k/dev/mcreg.h @@ -0,0 +1,164 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * VME162 MCchip + */ +struct mcreg { + volatile u_char mc_chipid; + volatile u_char mc_chiprev; + volatile u_char mc_genctl; + volatile u_char mc_vecbase; + volatile u_long mc_t1cmp; + volatile u_long mc_t1count; + volatile u_long mc_t2cmp; + volatile u_long mc_t2count; + volatile u_char mc_lsbprescale; + volatile u_char mc_adjprescale; + volatile u_char mc_t2ctl; + volatile u_char mc_t1ctl; + volatile u_char mc_t4irq; + volatile u_char mc_t3irq; + volatile u_char mc_t2irq; + volatile u_char mc_t1irq; + volatile u_char mc_parity; + volatile u_char mc_zsirq; + volatile u_char mc_t4ctl; + volatile u_char mc_t3ctl; + volatile u_short mc_drambase; + volatile u_short mc_srambase; + volatile u_char mc_dramsize; + volatile u_char mc_memoptions; +#define MC_MEMOPTIONS_SRAMMASK 0x18 +#define MC_MEMOPTIONS_SRAM128K 0x00 +#define MC_MEMOPTIONS_SRAM512K 0x08 +#define MC_MEMOPTIONS_SRAM1M 0x10 +#define MC_MEMOPTIONS_SRAM2M 0x18 +#define MC_MEMOPTIONS_DRAMMASK 0x07 +#define MC_MEMOPTIONS_DRAM1M 0x00 +#define MC_MEMOPTIONS_DRAM2M 0x01 +#define MC_MEMOPTIONS_DRAM4M 0x03 +#define MC_MEMOPTIONS_DRAM4M2 0x04 +#define MC_MEMOPTIONS_DRAM8M 0x05 +#define MC_MEMOPTIONS_DRAM16M 0x07 + volatile u_char mc_sramsize; + volatile u_char mc_resv1; + volatile u_char mc_ieerr; + volatile u_char mc_resv2; + volatile u_char mc_ieirq; + volatile u_char mc_iefailirq; + volatile u_char mc_ncrerr; + volatile u_char mc_input; + volatile u_char mc_ver; + volatile u_char mc_ncrirq; + volatile u_long mc_t3cmp; + volatile u_long mc_t3count; + volatile u_long mc_t4cmp; + volatile u_long mc_t4count; + volatile u_char mc_busclock; + volatile u_char mc_promtime; + volatile u_char mc_flashctl; + volatile u_char mc_abortirq; + volatile u_char mc_resetctl; + volatile u_char mc_watchdogctl; + volatile u_char mc_watchdogtime; + volatile u_char mc_resv3; + volatile u_char mc_dramctl; + volatile u_char mc_resv4; + volatile u_char mc_mpustat; + volatile u_char mc_resv5; + volatile u_long mc_prescale; +}; +#define MC_MCCHIP_OFF 0x42000 +#define MC_CHIPID 0x84 + +/* + * points to system's MCchip registers + */ +extern struct mcreg *sys_mc; + +/* + * for the console we need zs phys addr + */ +#define ZS0_PHYS_162 (0xfff45000) +#define ZS1_PHYS_162 (0xfff45801) + +/* + * We lock off our interrupt vector at 0x50. + */ +#define MC_VECBASE 0x50 +#define MC_NVEC 16 + +#define MCV_ZS 0x00 +#define MCV_TIMER4 0x03 +#define MCV_TIMER3 0x04 +#define MCV_NCR 0x05 +#define MCV_IEFAIL 0x06 +#define MCV_IE 0x07 +#define MCV_TIMER2 0x08 +#define MCV_TIMER1 0x09 +#define MCV_PARITY 0x0b +#define MCV_ABORT 0x0e + +#define MC_TCTL_CEN 0x01 +#define MC_TCTL_COC 0x02 +#define MC_TCTL_COVF 0x04 +#define MC_TCTL_OVF 0xf0 + +#define MC_ABORT_ABS 0x40 + +#define mc_timer_us2lim(us) (us) /* timer increments in "us" */ + +#define MC_IRQ_IPL 0x07 +#define MC_IRQ_ICLR 0x08 +#define MC_IRQ_IEN 0x10 +#define MC_IRQ_INT 0x20 + +#define MC_GENCTL_IEN 0x02 + +#define MC_IEERR_SCLR 0x01 + +#define MC_SC_INHIBIT (0 << 6) +#define MC_SC_SNOOP (1 << 6) +#define MC_SC_INVAL (2 << 6) +#define MC_SC_RESV (3 << 6) + +#define MC_VER_ISLX 0x40 +#define MC_VER_REAL040 0x10 +#define MC_VER_NOIE 0x08 +#define MC_VER_NONCR 0x04 +#define MC_VER_NOVME 0x02 +#define MC_VER_33MHZ 0x01 + +void mc_enableflashwrite __P((int on)); +#define MC_ENAFLASHWRITE_OFFSET 0xcc000 +#define MC_DISFLASHWRITE_OFFSET 0xc8000 diff --git a/sys/arch/mvme68k/dev/memc.c b/sys/arch/mvme68k/dev/memc.c new file mode 100644 index 00000000000..9a16dd2a393 --- /dev/null +++ b/sys/arch/mvme68k/dev/memc.c @@ -0,0 +1,131 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * MEMC/MCECC chips + * these chips are rather similar in appearance except that the MEMC + * does parity while the MCECC does ECC. + */ +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/ioctl.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/tty.h> +#include <sys/uio.h> +#include <sys/callout.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/syslog.h> +#include <sys/fcntl.h> +#include <sys/device.h> +#include <machine/cpu.h> +#include <machine/autoconf.h> +#include <dev/cons.h> + +#include <mvme68k/dev/memcreg.h> + +struct memcsoftc { + struct device sc_dev; + caddr_t sc_vaddr; + struct memcreg *sc_memc; + struct intrhand sc_ih; +}; + +void memcattach __P((struct device *, struct device *, void *)); +int memcmatch __P((struct device *, void *, void *)); + +struct cfdriver memccd = { + NULL, "memc", memcmatch, memcattach, + DV_DULL, sizeof(struct memcsoftc), 0 +}; + +int memcintr __P((struct frame *frame)); + +int +memcmatch(parent, vcf, args) + struct device *parent; + void *vcf, *args; +{ + struct cfdata *cf = vcf; + struct confargs *ca = args; + struct memcreg *memc = (struct memcreg *)ca->ca_vaddr; + + if (badvaddr(memc, 1)) + return (0); + if (memc->memc_chipid==MEMC_CHIPID || memc->memc_chipid==MCECC_CHIPID) + return (1); + return (0); +} + +void +memcattach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct confargs *ca = args; + struct memcsoftc *sc = (struct memcsoftc *)self; + + /* + * since we know ourself to land in intiobase land, + * we must adjust our address + */ + sc->sc_memc = (struct memcreg *)ca->ca_vaddr; + + printf(": %s rev %d", + (sc->sc_memc->memc_chipid == MEMC_CHIPID) ? "MEMC040" : "MCECC", + sc->sc_memc->memc_chiprev); + +#if 0 + sc->sc_ih.ih_fn = memcintr; + sc->sc_ih.ih_arg = 0; + sc->sc_ih.ih_ipl = 7; + sc->sc_ih.ih_wantframe = 1; + mcintr_establish(xxx, &sc->sc_ih); +#endif + + switch (sc->sc_memc->memc_chipid) { + case MEMC_CHIPID: + break; + case MCECC_CHIPID: + break; + } + + printf("\n"); +} + +int +memcintr(frame) + struct frame *frame; +{ + return (0); +} diff --git a/sys/arch/mvme68k/dev/memcreg.h b/sys/arch/mvme68k/dev/memcreg.h new file mode 100644 index 00000000000..6bb7b901dec --- /dev/null +++ b/sys/arch/mvme68k/dev/memcreg.h @@ -0,0 +1,106 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * the MEMC's registers are a subset of the MCECC chip + */ +struct memcreg { + volatile u_char memc_chipid; + volatile u_char xx0[3]; + volatile u_char memc_chiprev; + volatile u_char xx1[3]; + volatile u_char memc_memconf; +#define MEMC_MEMCONF_MSIZ 0x07 +#define MEMC_MEMCONF_RTOB(x) ((4*1024*1024) << ((x) & MEMC_MEMCONF_MSIZ)) + volatile u_char xx2[3]; + volatile u_char memc_x0; + volatile u_char xx3[3]; + volatile u_char memc_x1; + volatile u_char xx4[3]; + volatile u_char memc_baseaddr; + volatile u_char xx5[3]; + volatile u_char memc_control; + volatile u_char xx6[3]; + volatile u_char memc_bclk; + volatile u_char xx7[3]; + + /* the following registers only exist on the MCECC */ + volatile u_char memc_datactl; + volatile u_char xx8[3]; + volatile u_char memc_scrubctl; + volatile u_char xx9[3]; + volatile u_char memc_scrubperh; + volatile u_char xx10[3]; + volatile u_char memc_scrubperl; + volatile u_char xx11[3]; + volatile u_char memc_chipprescale; + volatile u_char xx12[3]; + volatile u_char memc_scrubtime; + volatile u_char xx13[3]; + volatile u_char memc_scrubprescaleh; + volatile u_char xx14[3]; + volatile u_char memc_scrubprescalem; + volatile u_char xx15[3]; + volatile u_char memc_scrubprescalel; + volatile u_char xx16[3]; + volatile u_char memc_scrubtimeh; + volatile u_char xx17[3]; + volatile u_char memc_scrubtimel; + volatile u_char xx18[3]; + volatile u_char memc_scrubaddrhh; + volatile u_char xx19[3]; + volatile u_char memc_scrubaddrhm; + volatile u_char xx20[3]; + volatile u_char memc_scrubaddrlm; + volatile u_char xx21[3]; + volatile u_char memc_scrubaddrll; + volatile u_char xx22[3]; + volatile u_char memc_errlog; + volatile u_char xx23[3]; + volatile u_char memc_errloghh; + volatile u_char xx24[3]; + volatile u_char memc_errloghm; + volatile u_char xx25[3]; + volatile u_char memc_errloglm; + volatile u_char xx26[3]; + volatile u_char memc_errlogll; + volatile u_char xx27[3]; + volatile u_char memc_errsyndrome; + volatile u_char xx28[3]; + volatile u_char memc_defaults1; + volatile u_char xx29[3]; + volatile u_char memc_defaults2; + volatile u_char xx30[3]; +}; + +#define MEMC_CHIPID 0x80 +#define MCECC_CHIPID 0x81 diff --git a/sys/arch/mvme68k/dev/memdevs.c b/sys/arch/mvme68k/dev/memdevs.c new file mode 100644 index 00000000000..a966b8778cc --- /dev/null +++ b/sys/arch/mvme68k/dev/memdevs.c @@ -0,0 +1,76 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/buf.h> +#include <sys/systm.h> +#include <sys/uio.h> +#include <sys/malloc.h> + +#include <sys/device.h> +#include <machine/cpu.h> +#include <machine/autoconf.h> + +/*ARGSUSED*/ +int +memdevrw(base, len, uio, flags) + caddr_t base; + int len; + struct uio *uio; + int flags; +{ + register vm_offset_t o, v; + register int c; + register struct iovec *iov; + int error = 0; + + while (uio->uio_resid > 0 && error == 0) { + iov = uio->uio_iov; + if (iov->iov_len == 0) { + uio->uio_iov++; + uio->uio_iovcnt--; + if (uio->uio_iovcnt < 0) + panic("memdevrw"); + continue; + } + + v = uio->uio_offset; + c = min(iov->iov_len, MAXPHYS); + if (v + c > len) + c = len - v; /* till end of dev */ + if (c == 0) + return (0); + error = uiomove(base + v, c, uio); + } + return (error); +} diff --git a/sys/arch/mvme68k/dev/nvram.c b/sys/arch/mvme68k/dev/nvram.c new file mode 100644 index 00000000000..93f484542a6 --- /dev/null +++ b/sys/arch/mvme68k/dev/nvram.c @@ -0,0 +1,416 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * 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, Lawrence Berkeley Laboratory. + * + * 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. + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/ioctl.h> +#include <sys/device.h> +#include <machine/psl.h> +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <machine/mioctl.h> +#include <mvme68k/dev/nvramreg.h> + +#if defined(GPROF) +#include <sys/gmon.h> +#endif + +struct nvramsoftc { + struct device sc_dev; + caddr_t sc_paddr; + caddr_t sc_vaddr; + int sc_len; + struct clockreg *sc_regs; +}; + +void nvramattach __P((struct device *, struct device *, void *)); +int nvrammatch __P((struct device *, void *, void *)); + +struct cfdriver nvramcd = { + NULL, "nvram", nvrammatch, nvramattach, + DV_DULL, sizeof(struct nvramsoftc), 0 +}; + +int +nvrammatch(parent, vcf, args) + struct device *parent; + void *vcf, *args; +{ + struct cfdata *cf = vcf; + struct confargs *ca = args; + +/*X*/ if (ca->ca_vaddr == (caddr_t)-1) +/*X*/ return (1); + return (!badvaddr(ca->ca_vaddr, 1)); +} + +void +nvramattach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct confargs *ca = args; + struct nvramsoftc *sc = (struct nvramsoftc *)self; + + sc->sc_paddr = ca->ca_paddr; + sc->sc_vaddr = ca->ca_vaddr; + + sc->sc_len = MK48T08_SIZE; + if (cputyp == CPU_147) + sc->sc_len = MK48T02_SIZE; + +/*X*/ if (sc->sc_vaddr == (caddr_t)-1) +/*X*/ sc->sc_vaddr = mapiodev((caddr_t)sc->sc_paddr, +/*X*/ max(sc->sc_len, NBPG)); +/*X*/ if (sc->sc_vaddr == NULL) +/*X*/ panic("failed to map!\n"); + + sc->sc_regs = (struct clockreg *)(sc->sc_vaddr + sc->sc_len - + sizeof(struct clockreg)); + + printf(": MK48T0%d len %d\n", sc->sc_len / 1024, sc->sc_len); +} + +/* + * Return the best possible estimate of the time in the timeval + * to which tvp points. We do this by returning the current time + * plus the amount of time since the last clock interrupt (clock.c:clkread). + * + * Check that this time is no less than any previously-reported time, + * which could happen around the time of a clock adjustment. Just for fun, + * we guarantee that the time will be greater than the value obtained by a + * previous call. + */ +void +microtime(tvp) + register struct timeval *tvp; +{ + int s = splhigh(); + static struct timeval lasttime; + + *tvp = time; + tvp->tv_usec; + while (tvp->tv_usec > 1000000) { + tvp->tv_sec++; + tvp->tv_usec -= 1000000; + } + if (tvp->tv_sec == lasttime.tv_sec && + tvp->tv_usec <= lasttime.tv_usec && + (tvp->tv_usec = lasttime.tv_usec + 1) > 1000000) { + tvp->tv_sec++; + tvp->tv_usec -= 1000000; + } + lasttime = *tvp; + splx(s); +} + +/* + * BCD to decimal and decimal to BCD. + */ +#define FROMBCD(x) (((x) >> 4) * 10 + ((x) & 0xf)) +#define TOBCD(x) (((x) / 10 * 16) + ((x) % 10)) + +#define SECDAY (24 * 60 * 60) +#define SECYR (SECDAY * 365) +#define LEAPYEAR(y) (((y) & 3) == 0) + +/* + * This code is defunct after 2068. + * Will Unix still be here then?? + */ +const short dayyr[12] = + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; + +static u_long +chiptotime(sec, min, hour, day, mon, year) + register int sec, min, hour, day, mon, year; +{ + register int days, yr; + + sec = FROMBCD(sec); + min = FROMBCD(min); + hour = FROMBCD(hour); + day = FROMBCD(day); + mon = FROMBCD(mon); + year = FROMBCD(year) + YEAR0; + + /* simple sanity checks */ + if (year < 70 || year > 164 || mon < 1 || mon > 12 || day < 1 || day > 31) + return (0); + days = 0; + for (yr = 70; yr < year; yr++) + days += LEAPYEAR(yr) ? 366 : 365; + days += dayyr[mon - 1] + day - 1; + if (LEAPYEAR(yr) && mon > 2) + days++; + /* now have days since Jan 1, 1970; the rest is easy... */ + return (days * SECDAY + hour * 3600 + min * 60 + sec); +} + +struct chiptime { + int sec; + int min; + int hour; + int wday; + int day; + int mon; + int year; +}; + +timetochip(c) + register struct chiptime *c; +{ + register int t, t2, t3, now = time.tv_sec; + + /* compute the year */ + t2 = now / SECDAY; + t3 = (t2 + 2) % 7; /* day of week */ + c->wday = TOBCD(t3 + 1); + + t = 69; + while (t2 >= 0) { /* whittle off years */ + t3 = t2; + t++; + t2 -= LEAPYEAR(t) ? 366 : 365; + } + c->year = t; + + /* t3 = month + day; separate */ + t = LEAPYEAR(t); + for (t2 = 1; t2 < 12; t2++) + if (t3 < dayyr[t2] + (t && t2 > 1)) + break; + + /* t2 is month */ + c->mon = t2; + c->day = t3 - dayyr[t2 - 1] + 1; + if (t && t2 > 2) + c->day--; + + /* the rest is easy */ + t = now % SECDAY; + c->hour = t / 3600; + t %= 3600; + c->min = t / 60; + c->sec = t % 60; + + c->sec = TOBCD(c->sec); + c->min = TOBCD(c->min); + c->hour = TOBCD(c->hour); + c->day = TOBCD(c->day); + c->mon = TOBCD(c->mon); + c->year = TOBCD(c->year - YEAR0); +} + +/* + * Set up the system's time, given a `reasonable' time value. + */ +inittodr(base) + time_t base; +{ + struct nvramsoftc *sc = (struct nvramsoftc *) nvramcd.cd_devs[0]; + register struct clockreg *cl = sc->sc_regs; + int sec, min, hour, day, mon, year; + int badbase = 0, waszero = base == 0; + + if (base < 5 * SECYR) { + /* + * If base is 0, assume filesystem time is just unknown + * instead of preposterous. Don't bark. + */ + if (base != 0) + printf("WARNING: preposterous time in file system\n"); + /* not going to use it anyway, if the chip is readable */ + base = 21*SECYR + 186*SECDAY + SECDAY/2; + badbase = 1; + } + cl->cl_csr |= CLK_READ; /* enable read (stop time) */ + sec = cl->cl_sec; + min = cl->cl_min; + hour = cl->cl_hour; + day = cl->cl_mday; + mon = cl->cl_month; + year = cl->cl_year; + cl->cl_csr &= ~CLK_READ; /* time wears on */ + if ((time.tv_sec = chiptotime(sec, min, hour, day, mon, year)) == 0) { + printf("WARNING: bad date in nvram"); + /* + * Believe the time in the file system for lack of + * anything better, resetting the clock. + */ + time.tv_sec = base; + if (!badbase) + resettodr(); + } else { + int deltat = time.tv_sec - base; + + if (deltat < 0) + deltat = -deltat; + if (waszero || deltat < 2 * SECDAY) + return; + printf("WARNING: clock %s %d days", + time.tv_sec < base ? "lost" : "gained", deltat / SECDAY); + } + printf(" -- CHECK AND RESET THE DATE!\n"); +} + +/* + * Reset the clock based on the current time. + * Used when the current clock is preposterous, when the time is changed, + * and when rebooting. Do nothing if the time is not yet known, e.g., + * when crashing during autoconfig. + */ +resettodr() +{ + struct nvramsoftc *sc = (struct nvramsoftc *) nvramcd.cd_devs[0]; + register struct clockreg *cl = sc->sc_regs; + struct chiptime c; + + if (!time.tv_sec || cl == NULL) + return; + timetochip(&c); + cl->cl_csr |= CLK_WRITE; /* enable write */ + cl->cl_sec = c.sec; + cl->cl_min = c.min; + cl->cl_hour = c.hour; + cl->cl_wday = c.wday; + cl->cl_mday = c.day; + cl->cl_month = c.mon; + cl->cl_year = c.year; + cl->cl_csr &= ~CLK_WRITE; /* load them up */ +} + +/*ARGSUSED*/ +int +nvramopen(dev, flag, mode) + dev_t dev; + int flag, mode; +{ + if (minor(dev) >= nvramcd.cd_ndevs || + nvramcd.cd_devs[minor(dev)] == NULL) + return (ENODEV); + return (0); +} + +/*ARGSUSED*/ +int +nvramclose(dev, flag, mode) + dev_t dev; + int flag, mode; +{ + + return (0); +} + +/*ARGSUSED*/ +int +nvramioctl(dev, cmd, data, flag, p) + dev_t dev; + caddr_t data; + int cmd, flag; + struct proc *p; +{ + int unit = minor(dev); + struct nvramsoftc *sc = (struct nvramsoftc *) nvramcd.cd_devs[unit]; + int error = 0; + + switch (cmd) { + case MIOCGSIZ: + *(int *)data = sc->sc_len; + break; + default: + error = ENOTTY; + break; + } + return (error); +} + +/*ARGSUSED*/ +int +nvramread(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + int unit = minor(dev); + struct nvramsoftc *sc = (struct nvramsoftc *) nvramcd.cd_devs[unit]; + + return (memdevrw(sc->sc_vaddr, sc->sc_len, uio, flags)); +} + +/*ARGSUSED*/ +int +nvramwrite(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + int unit = minor(dev); + struct nvramsoftc *sc = (struct nvramsoftc *) nvramcd.cd_devs[unit]; + + return (memdevrw(sc->sc_vaddr, sc->sc_len, uio, flags)); +} + +/* + * If the NVRAM is of the 2K variety, an extra 2K of who-knows-what + * will also be mmap'd, due to NBPG being 4K. On the MVME147 the NVRAM + * repeats, so userland gets two copies back-to-back. + */ +int +nvrammmap(dev, off, prot) + dev_t dev; + int off, prot; +{ + int unit = minor(dev); + struct nvramsoftc *sc = (struct nvramsoftc *) nvramcd.cd_devs[unit]; + + if (minor(dev) != 0) + return (-1); + + /* allow access only in RAM */ + if (off > sc->sc_len) + return (-1); + return (m68k_btop(sc->sc_paddr + off)); +} diff --git a/sys/arch/mvme68k/dev/nvramreg.h b/sys/arch/mvme68k/dev/nvramreg.h new file mode 100644 index 00000000000..ec804e1e049 --- /dev/null +++ b/sys/arch/mvme68k/dev/nvramreg.h @@ -0,0 +1,70 @@ +/* $NetBSD: clockreg.h,v 1.5 1994/11/20 20:54:07 deraadt Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * 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, Lawrence Berkeley Laboratory. + * + * 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. + */ + +/* + * Mostek MK48T02 clock. + */ +struct clockreg { + volatile u_char cl_csr; /* control register */ + volatile u_char cl_sec; /* seconds (0..59; BCD) */ + volatile u_char cl_min; /* minutes (0..59; BCD) */ + volatile u_char cl_hour; /* hour (0..23; BCD) */ + volatile u_char cl_wday; /* weekday (1..7) */ + volatile u_char cl_mday; /* day in month (1..31; BCD) */ + volatile u_char cl_month; /* month (1..12; BCD) */ + volatile u_char cl_year; /* year (0..99; BCD) */ +}; + +/* bits in cl_csr */ +#define CLK_WRITE 0x80 /* want to write */ +#define CLK_READ 0x40 /* want to read (freeze clock) */ + +/* + * Motorola chose the year `1900' as their base count. + * XXX what happens when it wraps? + */ +#define YEAR0 0 + +#define MK48T02_SIZE 2*1024 +#define MK48T08_SIZE 8*1024 diff --git a/sys/arch/mvme68k/dev/pcctwo.c b/sys/arch/mvme68k/dev/pcctwo.c new file mode 100644 index 00000000000..6b694606f3b --- /dev/null +++ b/sys/arch/mvme68k/dev/pcctwo.c @@ -0,0 +1,180 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * VME16x PCC2 chip + */ +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/ioctl.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/tty.h> +#include <sys/uio.h> +#include <sys/callout.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/syslog.h> +#include <sys/fcntl.h> +#include <sys/device.h> +#include <machine/cpu.h> +#include <machine/autoconf.h> +#include <dev/cons.h> + +#include <mvme68k/dev/pcctworeg.h> + +struct pcctwosoftc { + struct device sc_dev; + caddr_t sc_vaddr; /* PCC2 space */ + caddr_t sc_paddr; + struct pcctworeg *sc_pcc2; /* the actual registers */ +}; + +void pcctwoattach __P((struct device *, struct device *, void *)); +int pcctwomatch __P((struct device *, void *, void *)); + +struct cfdriver pcctwocd = { + NULL, "pcctwo", pcctwomatch, pcctwoattach, + DV_DULL, sizeof(struct pcctwosoftc), 0 +}; + +struct pcctworeg *sys_pcc2 = NULL; + +int +pcctwomatch(parent, vcf, args) + struct device *parent; + void *vcf, *args; +{ + struct cfdata *cf = vcf; + struct confargs *ca = args; + struct pcctworeg *pcc2; + + /* the PCC2 only exists on MVME16x's except the 162, right? */ + if (cputyp == CPU_162 || cputyp == CPU_147) + return (0); + pcc2 = (struct pcctworeg *)(IIOV(ca->ca_paddr) + PCC2_PCC2CHIP_OFF); + if (badvaddr(pcc2, 1) || pcc2->pcc2_chipid != PCC2_CHIPID) + return (0); + return (1); +} + +int +pcctwo_print(args, bus) + void *args; + char *bus; +{ + struct confargs *ca = args; + + if (ca->ca_offset != -1) + printf(" offset 0x%x", ca->ca_offset); + if (ca->ca_ipl > 0) + printf(" ipl %d", ca->ca_ipl); + return (UNCONF); +} + +int +pcctwo_scan(parent, child, args) + struct device *parent; + void *child, *args; +{ + struct cfdata *cf = child; + struct pcctwosoftc *sc = (struct pcctwosoftc *)parent; + struct confargs *ca = args; + struct confargs oca; + + if (parent->dv_cfdata->cf_driver->cd_indirect) { + printf(" indirect devices not supported\n"); + return 0; + } + + bzero(&oca, sizeof oca); + oca.ca_offset = cf->cf_loc[0]; + oca.ca_ipl = cf->cf_loc[1]; + if (oca.ca_offset != -1 && ISIIOVA(sc->sc_vaddr + oca.ca_offset)) { + oca.ca_vaddr = sc->sc_vaddr + oca.ca_offset; + oca.ca_paddr = sc->sc_paddr + oca.ca_offset; + } else { + oca.ca_vaddr = (caddr_t)-1; + oca.ca_paddr = (caddr_t)-1; + } + oca.ca_bustype = BUS_PCCTWO; + oca.ca_master = (void *)sc->sc_pcc2; + oca.ca_name = cf->cf_driver->cd_name; + if ((*cf->cf_driver->cd_match)(parent, cf, &oca) == 0) + return (0); + config_attach(parent, cf, &oca, pcctwo_print); + return (1); +} + +void +pcctwoattach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct confargs *ca = args; + struct pcctwosoftc *sc = (struct pcctwosoftc *)self; + int i; + + if (sys_pcc2) + panic("pcc2 already attached!"); + + /* + * since we know ourself to land in intiobase land, + * we must adjust our address + */ + sc->sc_paddr = ca->ca_paddr; + sc->sc_vaddr = (caddr_t)IIOV(sc->sc_paddr); + sc->sc_pcc2 = (struct pcctworeg *)(sc->sc_vaddr + PCC2_PCC2CHIP_OFF); + sys_pcc2 = sc->sc_pcc2; + + printf(": rev %d\n", sc->sc_pcc2->pcc2_chiprev); + + sc->sc_pcc2->pcc2_vecbase = PCC2_VECBASE; + sc->sc_pcc2->pcc2_genctl |= PCC2_GENCTL_IEN; /* global irq enable */ + + config_search(pcctwo_scan, self, args); +} + +/* + * PCC2 interrupts land in a PCC2_NVEC sized hole starting at PCC2_VECBASE + */ +int +pcctwointr_establish(vec, ih) + int vec; + struct intrhand *ih; +{ + if (vec >= PCC2_NVEC) { + printf("pcctwo: illegal vector: 0x%x\n", vec); + panic("pcctwointr_establish"); + } + return (intr_establish(PCC2_VECBASE+vec, ih)); +} diff --git a/sys/arch/mvme68k/dev/pcctworeg.h b/sys/arch/mvme68k/dev/pcctworeg.h new file mode 100644 index 00000000000..4c02c246797 --- /dev/null +++ b/sys/arch/mvme68k/dev/pcctworeg.h @@ -0,0 +1,150 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * MVME16x PCC2 chip: sort of a confused mish-mash of the MC in the 162 + * and the PCC in the 147 + */ +struct pcctworeg { + volatile u_char pcc2_chipid; + volatile u_char pcc2_chiprev; + volatile u_char pcc2_genctl; + volatile u_char pcc2_vecbase; /* irq vector base */ + volatile u_long pcc2_t1cmp; /* timer1 compare */ + volatile u_long pcc2_t1count; /* timer1 count */ + volatile u_long pcc2_t2cmp; /* timer2 compare */ + volatile u_long pcc2_t2count; /* timer2 count */ + volatile u_char pcc2_pscalecnt; /* timer prescaler counter */ + volatile u_char pcc2_pscaleadj; /* timer prescaler adjust */ + volatile u_char pcc2_t2ctl; /* timer2 ctrl reg */ + volatile u_char pcc2_t1ctl; /* timer1 ctrl reg */ + volatile u_char pcc2_gpioirq; /* gpio irq */ + volatile u_char pcc2_gpio; /* gpio i/o */ + volatile u_char pcc2_t2irq; + volatile u_char pcc2_t1irq; + volatile u_char pcc2_sccerr; + volatile u_char pcc2_sccirq; + volatile u_char pcc2_scctx; + volatile u_char pcc2_sccrx; + volatile u_char :8; + volatile u_char :8; + volatile u_char :8; + volatile u_char pcc2_sccmoiack; + volatile u_char :8; + volatile u_char pcc2_scctxiack; + volatile u_char :8; + volatile u_char pcc2_sccrxiack; + volatile u_char pcc2_ieerr; + volatile u_char :8; + volatile u_char pcc2_ieirq; + volatile u_char pcc2_iefailirq; + volatile u_char pcc2_ncrerr; + volatile u_char :8; + volatile u_char :8; + volatile u_char pcc2_ncrirq; + volatile u_char pcc2_prtairq; + volatile u_char pcc2_prtfirq; + volatile u_char pcc2_prtsirq; + volatile u_char pcc2_prtpirq; + volatile u_char pcc2_prtbirq; + volatile u_char :8; + volatile u_char pcc2_prtstat; + volatile u_char pcc2_prtctl; + volatile u_short pcc2_speed; /* DO NOT USE */ + volatile u_short pcc2_prtdat; + volatile u_short :16; + volatile u_char pcc2_ipl; + volatile u_char pcc2_mask; +}; +#define PCC2_PCC2CHIP_OFF 0x42000 +#define PCC2_CHIPID 0x20 + +/* + * points to system's PCCTWO. This is not active until the pcctwo0 + * device has been attached. + */ +extern struct pcctworeg *sys_pcc2; + +/* + * We lock off our interrupt vector at 0x50. + */ +#define PCC2_VECBASE 0x50 +#define PCC2_NVEC 16 + +/* + * Vectors we use + */ +#define PCC2V_NCR 0x05 +#define PCC2V_IEFAIL 0x06 +#define PCC2V_IE 0x07 +#define PCC2V_TIMER2 0x08 +#define PCC2V_TIMER1 0x09 +#define PCC2V_GPIO 0x0a +#define PCC2V_SCC_RXE 0x0c +#define PCC2V_SCC_M 0x0d +#define PCC2V_SCC_TX 0x0e +#define PCC2V_SCC_RX 0x0f + +#define PCC2_TCTL_CEN 0x01 +#define PCC2_TCTL_COC 0x02 +#define PCC2_TCTL_COVF 0x04 +#define PCC2_TCTL_OVF 0xf0 + +#define PCC2_GPIO_PLTY 0x80 +#define PCC2_GPIO_EL 0x40 + +#define PCC2_GPIOCR_OE 0x2 +#define PCC2_GPIOCR_O 0x1 + +#define PCC2_SCC_AVEC 0x08 +#define PCC2_SCCRX_INHIBIT (0 << 6) +#define PCC2_SCCRX_SNOOP (1 << 6) +#define PCC2_SCCRX_INVAL (2 << 6) +#define PCC2_SCCRX_RESV (3 << 6) + +#define pcc2_timer_us2lim(us) (us) /* timer increments in "us" */ + +#define PCC2_IRQ_IPL 0x07 +#define PCC2_IRQ_ICLR 0x08 +#define PCC2_IRQ_IEN 0x10 +#define PCC2_IRQ_INT 0x20 + +#define PCC2_IEERR_SCLR 0x01 + +#define PCC2_GENCTL_FAST 0x01 +#define PCC2_GENCTL_IEN 0x02 +#define PCC2_GENCTL_C040 0x03 + +#define PCC2_SC_INHIBIT (0 << 6) +#define PCC2_SC_SNOOP (1 << 6) +#define PCC2_SC_INVAL (2 << 6) +#define PCC2_SC_RESV (3 << 6) diff --git a/sys/arch/mvme68k/dev/sbic.c b/sys/arch/mvme68k/dev/sbic.c new file mode 100644 index 00000000000..b871cdd95a2 --- /dev/null +++ b/sys/arch/mvme68k/dev/sbic.c @@ -0,0 +1,2561 @@ +/* $NetBSD: sbic.c,v 1.14 1995/08/18 15:28:03 chopps Exp $ */ + +/* + * Copyright (c) 1994 Christian E. Hopps + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * 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. + * + * @(#)scsi.c 7.5 (Berkeley) 5/4/91 + */ + +/* + * AMD 33C93 scsi adaptor driver + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kernel.h> /* For hz */ +#include <sys/disklabel.h> +#include <sys/dkstat.h> +#include <sys/buf.h> +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <vm/vm.h> +#include <vm/vm_kern.h> +#include <vm/vm_page.h> +#include <machine/pmap.h> +#include <machine/autoconf.h> +#include <mvme68k/dev/dmavar.h> +#include <mvme68k/dev/sbicreg.h> +#include <mvme68k/dev/sbicvar.h> + +#include <vm/pmap.h> + +/* Since I can't find this in any other header files */ +#define SCSI_PHASE(reg) (reg&0x07) + +/* + * SCSI delays + * In u-seconds, primarily for state changes on the SPC. + */ +#define SBIC_CMD_WAIT 50000 /* wait per step of 'immediate' cmds */ +#define SBIC_DATA_WAIT 50000 /* wait per data in/out step */ +#define SBIC_INIT_WAIT 50000 /* wait per step (both) during init */ + +#define b_cylin b_resid +#define SBIC_WAIT(regs, until, timeo) sbicwait(regs, until, timeo, __LINE__) + +extern u_int kvtop(); + +int sbicicmd __P((struct sbic_softc *, int, int, void *, int, void *, int)); +int sbicgo __P((struct sbic_softc *, struct scsi_xfer *)); +int sbicdmaok __P((struct sbic_softc *, struct scsi_xfer *)); +int sbicwait __P((sbic_regmap_p, char, int , int)); +int sbiccheckdmap __P((void *, u_long, u_long)); +int sbicselectbus __P((struct sbic_softc *, sbic_regmap_p, u_char, u_char, u_char)); +int sbicxfstart __P((sbic_regmap_p, int, u_char, int)); +int sbicxfout __P((sbic_regmap_p regs, int, void *, int)); +int sbicfromscsiperiod __P((struct sbic_softc *, sbic_regmap_p, int)); +int sbictoscsiperiod __P((struct sbic_softc *, sbic_regmap_p, int)); +int sbicintr __P((struct sbic_softc *)); +int sbicpoll __P((struct sbic_softc *)); +int sbicnextstate __P((struct sbic_softc *, u_char, u_char)); +int sbicmsgin __P((struct sbic_softc *)); +int sbicxfin __P((sbic_regmap_p regs, int, void *)); +int sbicabort __P((struct sbic_softc *, sbic_regmap_p, char *)); +void sbicxfdone __P((struct sbic_softc *, sbic_regmap_p, int)); +void sbicerror __P((struct sbic_softc *, sbic_regmap_p, u_char)); +void sbicstart __P((struct sbic_softc *)); +void sbicreset __P((struct sbic_softc *)); +void sbic_scsidone __P((struct sbic_acb *, int)); +void sbic_sched __P((struct sbic_softc *)); +void sbic_save_ptrs __P((struct sbic_softc *, sbic_regmap_p,int,int)); +void sbic_load_ptrs __P((struct sbic_softc *, sbic_regmap_p,int,int)); + +/* + * Synch xfer parameters, and timing conversions + */ +int sbic_min_period = SBIC_SYN_MIN_PERIOD; /* in cycles = f(ICLK,FSn) */ +int sbic_max_offset = SBIC_SYN_MAX_OFFSET; /* pure number */ + +int sbic_cmd_wait = SBIC_CMD_WAIT; +int sbic_data_wait = SBIC_DATA_WAIT; +int sbic_init_wait = SBIC_INIT_WAIT; + +/* + * was broken before.. now if you want this you get it for all drives + * on sbic controllers. + */ +int sbic_inhibit_sync = 1; +int sbic_enable_reselect = 1; +int sbic_clock_override = 0; +int sbic_no_dma = 0; +int sbic_parallel_operations = 1; + +#ifdef DEBUG +sbic_regmap_p debug_sbic_regs; +int sbicdma_ops = 0; /* total DMA operations */ +int sbicdma_bounces = 0; /* number operations using bounce buffer */ +int sbicdma_hits = 0; /* number of DMA chains that were contiguous */ +int sbicdma_misses = 0; /* number of DMA chains that were not contiguous */ +int sbicdma_saves = 0; +#define QPRINTF(a) if (sbic_debug > 1) printf a +int sbic_debug = 0; +int sync_debug = 0; +int sbic_dma_debug = 0; +int reselect_debug = 0; +int report_sense = 0; +int data_pointer_debug = 0; +int sbic_timeout = 0; +u_char debug_asr, debug_csr, timeout_active=0, routine; +void sbictimeout __P((struct sbic_softc *dev)); +#else +#define QPRINTF +#endif + +/* + * default minphys routine for sbic based controllers + */ +void +sbic_minphys(bp) + struct buf *bp; +{ + + /* + * No max transfer at this level. + */ + minphys(bp); +} + +/* + * Save DMA pointers. Take into account partial transfer. Shut down DMA. + */ +void +sbic_save_ptrs(dev, regs, target, lun) + struct sbic_softc *dev; + sbic_regmap_p regs; + int target, lun; +{ + int count, asr, csr, s; + unsigned long ptr; + char *vptr; + struct sbic_acb* acb; + + extern vm_offset_t vm_first_phys; + + if( !dev->sc_cur ) return; + if( !(dev->sc_flags & SBICF_INDMA) ) return; /* DMA not active */ + + s = splbio(); + + acb = dev->sc_nexus; + count = -1; + do { + GET_SBIC_asr(regs, asr); + if( asr & SBIC_ASR_DBR ) { + printf("sbic_save_ptrs: asr %02x canceled!\n", asr); + splx(s); + return; + } + } while( asr & (SBIC_ASR_BSY|SBIC_ASR_CIP) ); + + /* Save important state */ + /* must be done before dmastop */ + acb->sc_dmacmd = dev->sc_dmacmd; + SBIC_TC_GET(regs, count); + + /* Shut down DMA ====CAREFUL==== */ + dev->sc_dmastop(dev); + dev->sc_flags &= ~SBICF_INDMA; + SBIC_TC_PUT(regs, 0); + +#ifdef DEBUG + if(!count && sbic_debug) printf("%dcount0",target); + if(data_pointer_debug == -1) + printf("SBIC saving target %d data pointers from (%x,%x)%xASR:%02x", + target, dev->sc_cur->dc_addr, dev->sc_cur->dc_count, + acb->sc_dmacmd, asr); +#endif + + /* Fixup partial xfers */ + acb->sc_kv.dc_addr += (dev->sc_tcnt - count); + acb->sc_kv.dc_count -= (dev->sc_tcnt - count); + acb->sc_pa.dc_addr += (dev->sc_tcnt - count); + acb->sc_pa.dc_count -= ((dev->sc_tcnt - count)>>1); + + acb->sc_tcnt = dev->sc_tcnt = count; +#ifdef DEBUG + if(data_pointer_debug) + printf(" at (%x,%x):%x\n", + dev->sc_cur->dc_addr, dev->sc_cur->dc_count,count); + sbicdma_saves++; +#endif + splx(s); +} + + +/* + * DOES NOT RESTART DMA!!! + */ +void sbic_load_ptrs(dev, regs, target, lun) + struct sbic_softc *dev; + sbic_regmap_p regs; + int target, lun; +{ + int i, s, asr, count; + char* vaddr, * paddr; + struct sbic_acb *acb; + + acb = dev->sc_nexus; + if( !acb->sc_kv.dc_count ) + /* No data to xfer */ + return; + + s = splbio(); + + dev->sc_last = dev->sc_cur = &acb->sc_pa; + dev->sc_tcnt = acb->sc_tcnt; + dev->sc_dmacmd = acb->sc_dmacmd; + +#ifdef DEBUG + sbicdma_ops++; +#endif + if( !dev->sc_tcnt ) { + /* sc_tcnt == 0 implies end of segment */ + + /* do kvm to pa mappings */ + paddr = acb->sc_pa.dc_addr = + (char *) kvtop(acb->sc_kv.dc_addr); + + vaddr = acb->sc_kv.dc_addr; + count = acb->sc_kv.dc_count; + for(count = (NBPG - ((int)vaddr & PGOFSET)); + count < acb->sc_kv.dc_count + && (char*)kvtop(vaddr + count + 4) == paddr + count + 4; + count += NBPG); + /* If it's all contiguous... */ + if(count > acb->sc_kv.dc_count ) { + count = acb->sc_kv.dc_count; +#ifdef DEBUG + sbicdma_hits++; +#endif + } else { +#ifdef DEBUG + sbicdma_misses++; +#endif + } + acb->sc_tcnt = count; + acb->sc_pa.dc_count = count >> 1; + +#ifdef DEBUG + if(data_pointer_debug) + printf("DMA recalc:kv(%x,%x)pa(%x,%x)\n", + acb->sc_kv.dc_addr, + acb->sc_kv.dc_count, + acb->sc_pa.dc_addr, + acb->sc_tcnt); +#endif + } + splx(s); +#ifdef DEBUG + if(data_pointer_debug) + printf("SBIC restoring target %d data pointers at (%x,%x)%x\n", + target, dev->sc_cur->dc_addr, dev->sc_cur->dc_count, + dev->sc_dmacmd); +#endif +} + +/* + * used by specific sbic controller + * + * it appears that the higher level code does nothing with LUN's + * so I will too. I could plug it in, however so could they + * in scsi_scsi_cmd(). + */ +int +sbic_scsicmd(xs) + struct scsi_xfer *xs; +{ + struct sbic_acb *acb; + struct sbic_softc *dev; + struct scsi_link *slp; + int flags, s, stat; + + slp = xs->sc_link; + dev = slp->adapter_softc; + flags = xs->flags; + + if (flags & SCSI_DATA_UIO) + panic("sbic: scsi data uio requested"); + + if (dev->sc_nexus && flags & SCSI_POLL) + panic("sbic_scsicmd: busy"); + + if (slp->target == slp->adapter_target) + return ESCAPE_NOT_SUPPORTED; + + s = splbio(); + acb = dev->free_list.tqh_first; + if (acb) + TAILQ_REMOVE(&dev->free_list, acb, chain); + splx(s); + + if (acb == NULL) { +#ifdef DEBUG + printf("sbic_scsicmd: unable to queue request for target %d\n", + slp->target); +#ifdef DDB + Debugger(); +#endif +#endif + xs->error = XS_DRIVER_STUFFUP; + return(TRY_AGAIN_LATER); + } + + acb->flags = ACB_ACTIVE; + if (flags & SCSI_DATA_IN) + acb->flags |= ACB_DATAIN; + acb->xs = xs; + bcopy(xs->cmd, &acb->cmd, xs->cmdlen); + acb->clen = xs->cmdlen; + acb->sc_kv.dc_addr = xs->data; + acb->sc_kv.dc_count = xs->datalen; + acb->pa_addr = xs->data ? (char *)kvtop(xs->data) : 0; /* XXXX check */ + + if (flags & SCSI_POLL) { + s = splbio(); + /* + * This has major side effects -- it locks up the machine + */ + + dev->sc_flags |= SBICF_ICMD; + do { + while(dev->sc_nexus) + sbicpoll(dev); + dev->sc_nexus = acb; + dev->sc_stat[0] = -1; + dev->sc_xs = xs; + dev->target = slp->target; + dev->lun = slp->lun; + stat = sbicicmd(dev, slp->target, slp->lun, + &acb->cmd, acb->clen, + acb->sc_kv.dc_addr, acb->sc_kv.dc_count); + } while (dev->sc_nexus != acb); + sbic_scsidone(acb, stat); + + splx(s); + return(COMPLETE); + } + + s = splbio(); + TAILQ_INSERT_TAIL(&dev->ready_list, acb, chain); + + if (dev->sc_nexus) { + splx(s); + return(SUCCESSFULLY_QUEUED); + } + + /* + * nothing is active, try to start it now. + */ + sbic_sched(dev); + splx(s); + +/* TODO: add sbic_poll to do SCSI_POLL operations */ +#if 0 + if (flags & SCSI_POLL) + return(COMPLETE); +#endif + return(SUCCESSFULLY_QUEUED); +} + +/* + * attempt to start the next available command + */ +void +sbic_sched(dev) + struct sbic_softc *dev; +{ + struct scsi_xfer *xs; + struct scsi_link *slp; + struct sbic_acb *acb; + int flags, /*phase,*/ stat, i; + + if (dev->sc_nexus) + return; /* a command is current active */ + + for (acb = dev->ready_list.tqh_first; acb; acb = acb->chain.tqe_next) { + slp = acb->xs->sc_link; + i = slp->target; + if (!(dev->sc_tinfo[i].lubusy & (1 << slp->lun))) { + struct sbic_tinfo *ti = &dev->sc_tinfo[i]; + + TAILQ_REMOVE(&dev->ready_list, acb, chain); + dev->sc_nexus = acb; + slp = acb->xs->sc_link; + ti = &dev->sc_tinfo[slp->target]; + ti->lubusy |= (1 << slp->lun); + acb->sc_pa.dc_addr = acb->pa_addr; /* XXXX check */ + break; + } + } + + if (acb == NULL) + return; /* did not find an available command */ + + dev->sc_xs = xs = acb->xs; + slp = xs->sc_link; + flags = xs->flags; + + if (flags & SCSI_RESET) + sbicreset(dev); + +#ifdef DEBUG + if( data_pointer_debug > 1 ) + printf("sbic_sched(%d,%d)\n",slp->target,slp->lun); +#endif + dev->sc_stat[0] = -1; + dev->target = slp->target; + dev->lun = slp->lun; + if ( flags & SCSI_POLL || ( !sbic_parallel_operations + && (/*phase == STATUS_PHASE ||*/ + sbicdmaok(dev, xs) == 0) ) ) + stat = sbicicmd(dev, slp->target, slp->lun, &acb->cmd, + acb->clen, acb->sc_kv.dc_addr, acb->sc_kv.dc_count); + else if (sbicgo(dev, xs) == 0) + return; + else + stat = dev->sc_stat[0]; + + sbic_scsidone(acb, stat); +} + +void +sbic_scsidone(acb, stat) + struct sbic_acb *acb; + int stat; +{ + struct scsi_xfer *xs; + struct scsi_link *slp; + struct sbic_softc *dev; + int s, dosched = 0; + + xs = acb->xs; + slp = xs->sc_link; + dev = slp->adapter_softc; +#ifdef DIAGNOSTIC + if (acb == NULL || xs == NULL) { + printf("sbic_scsidone -- (%d,%d) no scsi_xfer\n", + dev->target, dev->lun); +#ifdef DDB + Debugger(); +#endif + return; + } +#endif +#if 1 + if (((struct device *)(slp->device_softc))->dv_unit < dk_ndrive) + ++dk_xfer[((struct device *)(slp->device_softc))->dv_unit]; +#endif + /* + * is this right? + */ + xs->status = stat; + +#ifdef DEBUG + if( data_pointer_debug > 1 ) + printf("scsidone: (%d,%d)->(%d,%d)%02x\n", + slp->target, slp->lun, + dev->target, dev->lun, stat); + if( xs->sc_link->target == dev->sc_link.adapter_target ) + panic("target == hostid"); +#endif + + if (xs->error == XS_NOERROR && !(acb->flags & ACB_CHKSENSE)) { + if (stat == SCSI_CHECK) { + /* Schedule a REQUEST SENSE */ + struct scsi_sense *ss = (void *)&acb->cmd; +#ifdef DEBUG + if (report_sense) + printf("sbic_scsidone: autosense %02x targ %d lun %d", + acb->cmd.opcode, slp->target, slp->lun); +#endif + bzero(ss, sizeof(*ss)); + ss->opcode = REQUEST_SENSE; + ss->byte2 = slp->lun << 5; + ss->length = sizeof(struct scsi_sense_data); + acb->clen = sizeof(*ss); + acb->sc_kv.dc_addr = (char *)&xs->sense; + acb->sc_kv.dc_count = sizeof(struct scsi_sense_data); + acb->pa_addr = (char *)kvtop(&xs->sense); /* XXX check */ + acb->flags = ACB_ACTIVE | ACB_CHKSENSE | ACB_DATAIN; + TAILQ_INSERT_HEAD(&dev->ready_list, acb, chain); + dev->sc_tinfo[slp->target].lubusy &= + ~(1 << slp->lun); + dev->sc_tinfo[slp->target].senses++; + if (dev->sc_nexus == acb) { + dev->sc_nexus = NULL; + sbic_sched(dev); + } + return; + } + } + if (xs->error == XS_NOERROR && (acb->flags & ACB_CHKSENSE)) { + xs->error = XS_SENSE; +#ifdef DEBUG + if (report_sense) + printf(" => %02x %02x\n", xs->sense.extended_flags, + xs->sense.extended_extra_bytes[3]); +#endif + } else { + xs->resid = 0; /* XXXX */ + } +#if whataboutthisone + case SCSI_BUSY: + xs->error = XS_BUSY; + break; +#endif + xs->flags |= ITSDONE; + + /* + * Remove the ACB from whatever queue it's on. We have to do a bit of + * a hack to figure out which queue it's on. Note that it is *not* + * necessary to cdr down the ready queue, but we must cdr down the + * nexus queue and see if it's there, so we can mark the unit as no + * longer busy. This code is sickening, but it works. + */ + if (acb == dev->sc_nexus) { + dev->sc_nexus = NULL; + dev->sc_tinfo[slp->target].lubusy &= ~(1<<slp->lun); + if (dev->ready_list.tqh_first) + dosched = 1; /* start next command */ + } else if (dev->ready_list.tqh_last == &acb->chain.tqe_next) { + TAILQ_REMOVE(&dev->ready_list, acb, chain); + } else { + register struct sbic_acb *acb2; + for (acb2 = dev->nexus_list.tqh_first; acb2; + acb2 = acb2->chain.tqe_next) + if (acb2 == acb) { + TAILQ_REMOVE(&dev->nexus_list, acb, chain); + dev->sc_tinfo[slp->target].lubusy + &= ~(1<<slp->lun); + break; + } + if (acb2) + ; + else if (acb->chain.tqe_next) { + TAILQ_REMOVE(&dev->ready_list, acb, chain); + } else { + printf("%s: can't find matching acb\n", + dev->sc_dev.dv_xname); +#ifdef DDB + Debugger(); +#endif + } + } + /* Put it on the free list. */ + acb->flags = ACB_FREE; + TAILQ_INSERT_HEAD(&dev->free_list, acb, chain); + + dev->sc_tinfo[slp->target].cmds++; + + scsi_done(xs); + + if (dosched) + sbic_sched(dev); +} + +int +sbicdmaok(dev, xs) + struct sbic_softc *dev; + struct scsi_xfer *xs; +{ + if (sbic_no_dma || xs->datalen & 0x1 || (u_int)xs->data & 0x3) + return(0); + /* + * controller supports dma to any addresses? + */ + else if ((dev->sc_flags & SBICF_BADDMA) == 0) + return(1); + /* + * this address is ok for dma? + */ + else if (sbiccheckdmap(xs->data, xs->datalen, dev->sc_dmamask) == 0) + return(1); +#if 0 + /* + * we have a bounce buffer? + */ + else if (dev->sc_tinfo[xs->sc_link->target].bounce) + return(1); + /* + * try to get one + */ + else if (dev->sc_tinfo[xs->sc_link->target].bounce + = (char *)alloc_z2mem(MAXPHYS)) { + if (isztwomem(dev->sc_tinfo[xs->sc_link->target].bounce)) + printf("alloc ZII target %d bounce pa 0x%x\n", + xs->sc_link->target, + kvtop(dev->sc_tinfo[xs->sc_link->target].bounce)); + else if (dev->sc_tinfo[xs->sc_link->target].bounce) + printf("alloc CHIP target %d bounce pa 0x%x\n", + xs->sc_link->target, + PREP_DMA_MEM(dev->sc_tinfo[xs->sc_link->target].bounce)); + return(1); + } +#endif + + return(0); +} + + +int +sbicwait(regs, until, timeo, line) + sbic_regmap_p regs; + char until; + int timeo; + int line; +{ + u_char val; + int csr; + + if (timeo == 0) + timeo = 10000; /* some large value.. */ + + GET_SBIC_asr(regs,val); + while ((val & until) == 0) { + if (timeo-- == 0) { + GET_SBIC_csr(regs, csr); + printf("sbicwait TIMEO @%d with asr=x%x csr=x%x\n", + line, val, csr); +#if defined(DDB) && defined(DEBUG) + Debugger(); +#endif + return(val); /* Maybe I should abort */ + break; + } + DELAY(1); + GET_SBIC_asr(regs,val); + } + return(val); +} + +int +sbicabort(dev, regs, where) + struct sbic_softc *dev; + sbic_regmap_p regs; + char *where; +{ + u_char csr, asr; + + GET_SBIC_csr(regs, csr); + GET_SBIC_asr(regs, asr); + + printf ("%s: abort %s: csr = 0x%02x, asr = 0x%02x\n", + dev->sc_dev.dv_xname, where, csr, asr); + + +#if 0 + /* Clean up running command */ + if (dev->sc_nexus != NULL) { + dev->sc_nexus->xs->error = XS_DRIVER_STUFFUP; + sbic_scsidone(dev->sc_nexus, dev->sc_stat[0]); + } + while (acb = dev->nexus_list.tqh_first) { + acb->xs->error = XS_DRIVER_STUFFUP; + sbic_scsidone(acb, -1 /*acb->stat[0]*/); + } +#endif + + /* Clean up chip itself */ + if (dev->sc_flags & SBICF_SELECTED) { + while( asr & SBIC_ASR_DBR ) { + /* sbic is jammed w/data. need to clear it */ + /* But we don't know what direction it needs to go */ + GET_SBIC_data(regs, asr); + printf("%s: abort %s: clearing data buffer 0x%02x\n", + dev->sc_dev.dv_xname, where, asr); + GET_SBIC_asr(regs, asr); + if( asr & SBIC_ASR_DBR ) /* Not the read direction, then */ + SET_SBIC_data(regs, asr); + GET_SBIC_asr(regs, asr); + } + WAIT_CIP(regs); + SET_SBIC_cmd(regs, SBIC_CMD_ABORT); + WAIT_CIP(regs); + + GET_SBIC_asr(regs, asr); + if (asr & (SBIC_ASR_BSY|SBIC_ASR_LCI)) { + /* ok, get more drastic.. */ + + sbicreset(dev); + dev->sc_flags &= ~SBICF_SELECTED; + return -1; + } + SET_SBIC_cmd(regs, SBIC_CMD_DISC); + + do { + SBIC_WAIT (regs, SBIC_ASR_INT, 0); + GET_SBIC_csr (regs, csr); + } while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1) + && (csr != SBIC_CSR_CMD_INVALID)); + + /* lets just hope it worked.. */ + dev->sc_flags &= ~SBICF_SELECTED; + } + return -1; +} + + +/* + * Initialize driver-private structures + */ + +void +sbicinit(dev) + struct sbic_softc *dev; +{ + sbic_regmap_p regs; + u_int my_id, i, s; + u_char csr; + struct sbic_acb *acb; + + regs = dev->sc_sbicp; + + if ((dev->sc_flags & SBICF_ALIVE) == 0) { + TAILQ_INIT(&dev->ready_list); + TAILQ_INIT(&dev->nexus_list); + TAILQ_INIT(&dev->free_list); + dev->sc_nexus = NULL; + dev->sc_xs = NULL; + acb = dev->sc_acb; + bzero(acb, sizeof(dev->sc_acb)); + for (i = 0; i < sizeof(dev->sc_acb) / sizeof(*acb); i++) { + TAILQ_INSERT_TAIL(&dev->free_list, acb, chain); + acb++; + } + bzero(dev->sc_tinfo, sizeof(dev->sc_tinfo)); + } else panic("sbic: reinitializing driver!"); + + dev->sc_flags |= SBICF_ALIVE; + dev->sc_flags &= ~SBICF_SELECTED; + + sbicreset(dev); +} + +void +sbicreset(dev) + struct sbic_softc *dev; +{ + sbic_regmap_p regs; + u_int my_id, i, s; + u_char csr; + struct sbic_acb *acb; + + regs = dev->sc_sbicp; +#if 0 + if (dev->sc_flags & SBICF_ALIVE) { + SET_SBIC_cmd(regs, SBIC_CMD_ABORT); + WAIT_CIP(regs); + } +#else + SET_SBIC_cmd(regs, SBIC_CMD_ABORT); + WAIT_CIP(regs); +#endif + s = splbio(); + my_id = dev->sc_link.adapter_target & SBIC_ID_MASK; + + /* Enable advanced mode */ + my_id |= SBIC_ID_EAF /*| SBIC_ID_EHP*/ ; + SET_SBIC_myid(regs, my_id); + + /* + * Disable interrupts (in dmainit) then reset the chip + */ + SET_SBIC_cmd(regs, SBIC_CMD_RESET); + DELAY(25); + SBIC_WAIT(regs, SBIC_ASR_INT, 0); + GET_SBIC_csr(regs, csr); /* clears interrupt also */ + + if (dev->sc_clkfreq < 110) + my_id |= SBIC_ID_FS_8_10; + else if (dev->sc_clkfreq < 160) + my_id |= SBIC_ID_FS_12_15; + else if (dev->sc_clkfreq < 210) + my_id |= SBIC_ID_FS_16_20; + + SET_SBIC_myid(regs, my_id); + + /* + * Set up various chip parameters + */ + SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI /* | SBIC_CTL_HSP */ + | SBIC_MACHINE_DMA_MODE); + /* + * don't allow (re)selection (SBIC_RID_ES) + * until we can handle target mode!! + */ + SET_SBIC_rselid(regs, SBIC_RID_ER); + SET_SBIC_syn(regs, 0); /* asynch for now */ + + /* + * anything else was zeroed by reset + */ + splx(s); + +#if 0 + if ((dev->sc_flags & SBICF_ALIVE) == 0) { + TAILQ_INIT(&dev->ready_list); + TAILQ_INIT(&dev->nexus_list); + TAILQ_INIT(&dev->free_list); + dev->sc_nexus = NULL; + dev->sc_xs = NULL; + acb = dev->sc_acb; + bzero(acb, sizeof(dev->sc_acb)); + for (i = 0; i < sizeof(dev->sc_acb) / sizeof(*acb); i++) { + TAILQ_INSERT_TAIL(&dev->free_list, acb, chain); + acb++; + } + bzero(dev->sc_tinfo, sizeof(dev->sc_tinfo)); + } else { + if (dev->sc_nexus != NULL) { + dev->sc_nexus->xs->error = XS_DRIVER_STUFFUP; + sbic_scsidone(dev->sc_nexus, dev->sc_stat[0]); + } + while (acb = dev->nexus_list.tqh_first) { + acb->xs->error = XS_DRIVER_STUFFUP; + sbic_scsidone(acb, -1 /*acb->stat[0]*/); + } + } + + dev->sc_flags |= SBICF_ALIVE; +#endif + dev->sc_flags &= ~SBICF_SELECTED; +} + +void +sbicerror(dev, regs, csr) + struct sbic_softc *dev; + sbic_regmap_p regs; + u_char csr; +{ + struct scsi_xfer *xs; + + xs = dev->sc_xs; + +#ifdef DIAGNOSTIC + if (xs == NULL) + panic("sbicerror"); +#endif + if (xs->flags & SCSI_SILENT) + return; + + printf("%s: ", dev->sc_dev.dv_xname); + printf("csr == 0x%02x\n", csr); /* XXX */ +} + +/* + * select the bus, return when selected or error. + */ +int +sbicselectbus(dev, regs, target, lun, our_addr) + struct sbic_softc *dev; + sbic_regmap_p regs; + u_char target, lun, our_addr; +{ + u_char asr, csr, id; + + QPRINTF(("sbicselectbus %d\n", target)); + + /* + * if we're already selected, return (XXXX panic maybe?) + */ + if (dev->sc_flags & SBICF_SELECTED) + return(1); + + SET_SBIC_rselid (regs, 0); +#if 0 + GET_SBIC_asr(regs, asr); + if( asr & (SBIC_ASR_INT|SBIC_ASR_BSY) ) { + /* This means we got ourselves reselected upon */ +/* printf("sbicselectbus: weird asr %02x\n", asr);*/ +#ifdef DDB +/* Debugger();*/ +#endif + SET_SBIC_rselid (regs, SBIC_RID_ER); + return 1; + } +#endif + /* + * issue select + */ + SBIC_TC_PUT(regs, 0); + SET_SBIC_selid(regs, target); + SET_SBIC_timeo(regs, SBIC_TIMEOUT(250,dev->sc_clkfreq)); + + /* + * set sync or async + */ + if (dev->sc_sync[target].state == SYNC_DONE) + SET_SBIC_syn(regs, SBIC_SYN (dev->sc_sync[target].offset, + dev->sc_sync[target].period)); + else + SET_SBIC_syn(regs, SBIC_SYN (0, sbic_min_period)); + + GET_SBIC_asr(regs, asr); + if( asr & (SBIC_ASR_INT|SBIC_ASR_BSY) ) { + /* This means we got ourselves reselected upon */ +/* printf("sbicselectbus: INT/BSY asr %02x\n", asr);*/ +#ifdef DDB +/* Debugger();*/ +#endif + return 1; + } + + SET_SBIC_cmd(regs, SBIC_CMD_SEL_ATN); + + /* + * wait for select (merged from seperate function may need + * cleanup) + */ + WAIT_CIP(regs); +#if 0 + GET_SBIC_asr(regs, asr); + if( asr & SBIC_ASR_LCI ) { + /* This means we got ourselves reselected upon */ +#ifdef DEBUG + if (reselect_debug) + printf("sbicselectbus: LCI asr %02x\n", asr); +/* Debugger();*/ +#endif + return 1; + } +#endif + do { + asr = SBIC_WAIT(regs, SBIC_ASR_INT | SBIC_ASR_LCI, 0); + if (asr & SBIC_ASR_LCI) { +#ifdef DEBUG + if (reselect_debug) + printf("sbicselectbus: late LCI asr %02x\n", asr); +#endif + return 1; + } + GET_SBIC_csr (regs, csr); + QPRINTF(("%02x ", csr)); + if( csr == SBIC_CSR_RSLT_NI || csr == SBIC_CSR_RSLT_IFY) { +#ifdef DEBUG + if( reselect_debug || 1 ) + printf("sbicselectbus: reselected asr %02x\n", asr); +#endif + /* We need to handle this now so we don't lock up later */ + sbicnextstate(dev, csr, asr); + return 1; + } + if( csr == SBIC_CSR_SLT || csr == SBIC_CSR_SLT_ATN) { + panic("sbicselectbus: target issued select!"); + return 1; + } + } while (csr != (SBIC_CSR_MIS_2|MESG_OUT_PHASE) + && csr != (SBIC_CSR_MIS_2|CMD_PHASE) && csr != SBIC_CSR_SEL_TIMEO); + + /* Enable (or not) reselection */ + if( (dev->sc_xs->flags & SCSI_POLL + || (dev->sc_flags & SBICF_ICMD) + || !sbic_enable_reselect) + && dev->nexus_list.tqh_first == NULL ) + SET_SBIC_rselid (regs, 0); + else + SET_SBIC_rselid (regs, SBIC_RID_ER); + + if (csr == (SBIC_CSR_MIS_2|CMD_PHASE)) { + dev->sc_flags |= SBICF_SELECTED; /* device ignored ATN */ + GET_SBIC_selid(regs, id); + dev->target = id; + GET_SBIC_tlun(regs,dev->lun); + if( dev->lun & SBIC_TLUN_VALID ) + dev->lun &= SBIC_TLUN_MASK; + else + dev->lun = lun; + } else if (csr == (SBIC_CSR_MIS_2|MESG_OUT_PHASE)) { + /* + * Send identify message + * (SCSI-2 requires an identify msg (?)) + */ + GET_SBIC_selid(regs, id); + dev->target = id; + GET_SBIC_tlun(regs,dev->lun); + if( dev->lun & SBIC_TLUN_VALID ) + dev->lun &= SBIC_TLUN_MASK; + else + dev->lun = lun; + /* + * handle drives that don't want to be asked + * whether to go sync at all. + */ + if (sbic_inhibit_sync && dev->sc_sync[id].state == SYNC_START) { +#ifdef DEBUG + if (sync_debug) + printf("Forcing target %d asynchronous.\n", id); +#endif + dev->sc_sync[id].offset = 0; + dev->sc_sync[id].period = sbic_min_period; + dev->sc_sync[id].state = SYNC_DONE; + } + + + if (dev->sc_sync[id].state != SYNC_START){ + if( dev->sc_xs->flags & SCSI_POLL + || (dev->sc_flags & SBICF_ICMD) + || !sbic_enable_reselect ) + SEND_BYTE (regs, MSG_IDENTIFY | lun); + else + SEND_BYTE (regs, MSG_IDENTIFY_DR | lun); + } else { + /* + * try to initiate a sync transfer. + * So compose the sync message we're going + * to send to the target + */ + +#ifdef DEBUG + if (sync_debug) + printf("Sending sync request to target %d ... ", + id); +#endif + /* + * setup scsi message sync message request + */ + dev->sc_msg[0] = MSG_IDENTIFY | lun; + dev->sc_msg[1] = MSG_EXT_MESSAGE; + dev->sc_msg[2] = 3; + dev->sc_msg[3] = MSG_SYNC_REQ; + dev->sc_msg[4] = sbictoscsiperiod(dev, regs, + sbic_min_period); + dev->sc_msg[5] = sbic_max_offset; + + if (sbicxfstart(regs, 6, MESG_OUT_PHASE, sbic_cmd_wait)) + sbicxfout(regs, 6, dev->sc_msg, MESG_OUT_PHASE); + + dev->sc_sync[id].state = SYNC_SENT; +#ifdef DEBUG + if (sync_debug) + printf ("sent\n"); +#endif + } + + SBIC_WAIT (regs, SBIC_ASR_INT, 0); + GET_SBIC_csr (regs, csr); + QPRINTF(("[%02x]", csr)); +#ifdef DEBUG + if (sync_debug && dev->sc_sync[id].state == SYNC_SENT) + printf("csr-result of last msgout: 0x%x\n", csr); +#endif + + if (csr != SBIC_CSR_SEL_TIMEO) + dev->sc_flags |= SBICF_SELECTED; + } + if (csr == SBIC_CSR_SEL_TIMEO) + dev->sc_xs->error = XS_SELTIMEOUT; + + QPRINTF(("\n")); + + return(csr == SBIC_CSR_SEL_TIMEO); +} + +int +sbicxfstart(regs, len, phase, wait) + sbic_regmap_p regs; + int len, wait; + u_char phase; +{ + u_char id; + + switch (phase) { + case DATA_IN_PHASE: + case MESG_IN_PHASE: + GET_SBIC_selid (regs, id); + id |= SBIC_SID_FROM_SCSI; + SET_SBIC_selid (regs, id); + SBIC_TC_PUT (regs, (unsigned)len); + break; + case DATA_OUT_PHASE: + case MESG_OUT_PHASE: + case CMD_PHASE: + GET_SBIC_selid (regs, id); + id &= ~SBIC_SID_FROM_SCSI; + SET_SBIC_selid (regs, id); + SBIC_TC_PUT (regs, (unsigned)len); + break; + default: + SBIC_TC_PUT (regs, 0); + } + QPRINTF(("sbicxfstart %d, %d, %d\n", len, phase, wait)); + + return(1); +} + +int +sbicxfout(regs, len, bp, phase) + sbic_regmap_p regs; + int len; + void *bp; + int phase; +{ + u_char orig_csr, csr, asr, *buf; + int wait; + + buf = bp; + wait = sbic_data_wait; + + QPRINTF(("sbicxfout {%d} %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x %02x\n", len, buf[0], buf[1], buf[2], + buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9])); + + GET_SBIC_csr (regs, orig_csr); + + /* + * sigh.. WD-PROTO strikes again.. sending the command in one go + * causes the chip to lock up if talking to certain (misbehaving?) + * targets. Anyway, this procedure should work for all targets, but + * it's slightly slower due to the overhead + */ + WAIT_CIP (regs); + SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO); + for (;len > 0; len--) { + GET_SBIC_asr (regs, asr); + while ((asr & SBIC_ASR_DBR) == 0) { + if ((asr & SBIC_ASR_INT) || --wait < 0) { +#ifdef DEBUG + if (sbic_debug) + printf("sbicxfout fail: l%d i%x w%d\n", + len, asr, wait); +#endif + return (len); + } +/* DELAY(1);*/ + GET_SBIC_asr (regs, asr); + } + + SET_SBIC_data (regs, *buf); + buf++; + } + SBIC_TC_GET(regs, len); + QPRINTF(("sbicxfout done %d bytes\n", len)); + /* + * this leaves with one csr to be read + */ + return(0); +} + +/* returns # bytes left to read */ +int +sbicxfin(regs, len, bp) + sbic_regmap_p regs; + int len; + void *bp; +{ + int wait, read; + u_char *obp, *buf; + u_char orig_csr, csr, asr; + + wait = sbic_data_wait; + obp = bp; + buf = bp; + + GET_SBIC_csr (regs, orig_csr); + + QPRINTF(("sbicxfin %d, csr=%02x\n", len, orig_csr)); + + WAIT_CIP (regs); + SET_SBIC_cmd (regs, SBIC_CMD_XFER_INFO); + for (;len > 0; len--) { + GET_SBIC_asr (regs, asr); + if((asr & SBIC_ASR_PE)) { +#ifdef DEBUG + printf("sbicxfin parity error: l%d i%x w%d\n", + len, asr, wait); +/* return ((unsigned long)buf - (unsigned long)bp); */ +#ifdef DDB + Debugger(); +#endif +#endif + } + while ((asr & SBIC_ASR_DBR) == 0) { + if ((asr & SBIC_ASR_INT) || --wait < 0) { +#ifdef DEBUG + if (sbic_debug) { + QPRINTF(("sbicxfin fail:{%d} %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x\n", len, obp[0], obp[1], obp[2], + obp[3], obp[4], obp[5], obp[6], obp[7], obp[8], obp[9])); + printf("sbicxfin fail: l%d i%x w%d\n", + len, asr, wait); +} +#endif + return len; + } + + if( ! asr & SBIC_ASR_BSY ) { + GET_SBIC_csr(regs, csr); + QPRINTF(("[CSR%02xASR%02x]", csr, asr)); + } + +/* DELAY(1);*/ + GET_SBIC_asr (regs, asr); + } + + GET_SBIC_data (regs, *buf); +/* QPRINTF(("asr=%02x, csr=%02x, data=%02x\n", asr, csr, *buf));*/ + buf++; + } + + QPRINTF(("sbicxfin {%d} %02x %02x %02x %02x %02x %02x " + "%02x %02x %02x %02x\n", len, obp[0], obp[1], obp[2], + obp[3], obp[4], obp[5], obp[6], obp[7], obp[8], obp[9])); + + /* this leaves with one csr to be read */ + return len; +} + +/* + * SCSI 'immediate' command: issue a command to some SCSI device + * and get back an 'immediate' response (i.e., do programmed xfer + * to get the response data). 'cbuf' is a buffer containing a scsi + * command of length clen bytes. 'buf' is a buffer of length 'len' + * bytes for data. The transfer direction is determined by the device + * (i.e., by the scsi bus data xfer phase). If 'len' is zero, the + * command must supply no data. + */ +int +sbicicmd(dev, target, lun, cbuf, clen, buf, len) + struct sbic_softc *dev; + void *cbuf, *buf; + int clen, len; +{ + sbic_regmap_p regs; + u_char phase, csr, asr; + int wait, newtarget, cmd_sent, parity_err; + struct sbic_acb *acb; + + int discon; + int i; + +#define CSR_LOG_BUF_SIZE 0 +#if CSR_LOG_BUF_SIZE + int bufptr; + int csrbuf[CSR_LOG_BUF_SIZE]; + bufptr=0; +#endif + + regs = dev->sc_sbicp; + acb = dev->sc_nexus; + + /* Make sure pointers are OK */ + dev->sc_last = dev->sc_cur = &acb->sc_pa; + dev->sc_tcnt = acb->sc_tcnt = 0; + acb->sc_pa.dc_count = 0; /* No DMA */ + acb->sc_kv.dc_addr = buf; + acb->sc_kv.dc_count = len; + +#ifdef DEBUG + routine = 3; + debug_sbic_regs = regs; /* store this to allow debug calls */ + if( data_pointer_debug > 1 ) + printf("sbicicmd(%d,%d):%d\n", target, lun, + acb->sc_kv.dc_count); +#endif + + /* + * set the sbic into non-DMA mode + */ + SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI /*| SBIC_CTL_HSP*/); + + dev->sc_stat[0] = 0xff; + dev->sc_msg[0] = 0xff; + i = 1; /* pre-load */ + + /* We're stealing the SCSI bus */ + dev->sc_flags |= SBICF_ICMD; + + do { + /* + * select the SCSI bus (it's an error if bus isn't free) + */ + if (!( dev->sc_flags & SBICF_SELECTED ) + && sbicselectbus(dev, regs, target, lun, dev->sc_scsiaddr)) { + /*printf("sbicicmd trying to select busy bus!\n");*/ + dev->sc_flags &= ~SBICF_ICMD; + return(-1); + } + + /* + * Wait for a phase change (or error) then let the device sequence + * us through the various SCSI phases. + */ + + wait = sbic_cmd_wait; + + GET_SBIC_asr (regs, asr); + GET_SBIC_csr (regs, csr); + QPRINTF((">ASR:%02xCSR:%02x<", asr, csr)); + +#if CSR_LOG_BUF_SIZE + csrbuf[bufptr++] = csr; +#endif + + + switch (csr) { + case SBIC_CSR_S_XFERRED: + case SBIC_CSR_DISC: + case SBIC_CSR_DISC_1: + dev->sc_flags &= ~SBICF_SELECTED; + GET_SBIC_cmd_phase (regs, phase); + if (phase == 0x60) { + GET_SBIC_tlun (regs, dev->sc_stat[0]); + i = 0; /* done */ +/* break; /* Bypass all the state gobldygook */ + } else { +#ifdef DEBUG + if(reselect_debug>1) + printf("sbicicmd: handling disconnect\n"); +#endif + i = SBIC_STATE_DISCONNECT; + } + break; + + case SBIC_CSR_XFERRED|CMD_PHASE: + case SBIC_CSR_MIS|CMD_PHASE: + case SBIC_CSR_MIS_1|CMD_PHASE: + case SBIC_CSR_MIS_2|CMD_PHASE: + if (sbicxfstart(regs, clen, CMD_PHASE, sbic_cmd_wait)) + if (sbicxfout(regs, clen, + cbuf, CMD_PHASE)) + i = sbicabort(dev, regs,"icmd sending cmd"); +#if 0 + GET_SBIC_csr(regs, csr); /* Lets us reload tcount */ + WAIT_CIP(regs); + GET_SBIC_asr(regs, asr); + if( asr & (SBIC_ASR_BSY|SBIC_ASR_LCI|SBIC_ASR_CIP) ) + printf("next: cmd sent asr %02x, csr %02x\n", + asr, csr); +#endif + break; + +#if 0 + case SBIC_CSR_XFERRED|DATA_OUT_PHASE: + case SBIC_CSR_XFERRED|DATA_IN_PHASE: + case SBIC_CSR_MIS|DATA_OUT_PHASE: + case SBIC_CSR_MIS|DATA_IN_PHASE: + case SBIC_CSR_MIS_1|DATA_OUT_PHASE: + case SBIC_CSR_MIS_1|DATA_IN_PHASE: + case SBIC_CSR_MIS_2|DATA_OUT_PHASE: + case SBIC_CSR_MIS_2|DATA_IN_PHASE: + if (acb->sc_kv.dc_count <= 0) + i = sbicabort(dev, regs, "icmd out of data"); + else { + wait = sbic_data_wait; + if (sbicxfstart(regs, + acb->sc_kv.dc_count, + SBIC_PHASE(csr), wait)) + if (csr & 0x01) + /* data in? */ + i=sbicxfin(regs, + acb->sc_kv.dc_count, + acb->sc_kv.dc_addr); + else + i=sbicxfout(regs, + acb->sc_kv.dc_count, + acb->sc_kv.dc_addr, + SBIC_PHASE(csr)); + acb->sc_kv.dc_addr += + (acb->sc_kv.dc_count - i); + acb->sc_kv.dc_count = i; + i = 1; + } + break; + +#endif + case SBIC_CSR_XFERRED|STATUS_PHASE: + case SBIC_CSR_MIS|STATUS_PHASE: + case SBIC_CSR_MIS_1|STATUS_PHASE: + case SBIC_CSR_MIS_2|STATUS_PHASE: + /* + * the sbic does the status/cmd-complete reading ok, + * so do this with its hi-level commands. + */ +#ifdef DEBUG + if(sbic_debug) + printf("SBICICMD status phase\n"); +#endif + SBIC_TC_PUT(regs, 0); + SET_SBIC_cmd_phase(regs, 0x46); + SET_SBIC_cmd(regs, SBIC_CMD_SEL_ATN_XFER); + break; + +#if THIS_IS_A_RESERVED_STATE + case BUS_FREE_PHASE: /* This is not legal */ + if( dev->sc_stat[0] != 0xff ) + goto out; + break; +#endif + + default: + i = sbicnextstate(dev, csr, asr); + } + + /* + * make sure the last command was taken, + * ie. we're not hunting after an ignored command.. + */ + GET_SBIC_asr(regs, asr); + + /* tapes may take a loooong time.. */ + while (asr & SBIC_ASR_BSY){ + if(asr & SBIC_ASR_DBR) { + printf("sbicicmd: Waiting while sbic is jammed, CSR:%02x,ASR:%02x\n", + csr,asr); +#ifdef DDB + Debugger(); +#endif + /* SBIC is jammed */ + /* DUNNO which direction */ + /* Try old direction */ + GET_SBIC_data(regs,i); + GET_SBIC_asr(regs, asr); + if( asr & SBIC_ASR_DBR) /* Wants us to write */ + SET_SBIC_data(regs,i); + } + GET_SBIC_asr(regs, asr); + } + + /* + * wait for last command to complete + */ + if (asr & SBIC_ASR_LCI) { + printf("sbicicmd: last command ignored\n"); + } + else if( i == 1 ) /* Bsy */ + SBIC_WAIT (regs, SBIC_ASR_INT, wait); + + /* + * do it again + */ + } while ( i > 0 && dev->sc_stat[0] == 0xff); + + /* Sometimes we need to do an extra read of the CSR */ + GET_SBIC_csr(regs, csr); + +#if CSR_LOG_BUF_SIZE + if(reselect_debug>1) + for(i=0; i<bufptr; i++) + printf("CSR:%02x", csrbuf[i]); +#endif + +#ifdef DEBUG + if(data_pointer_debug > 1) + printf("sbicicmd done(%d,%d):%d =%d=\n", + dev->target, lun, + acb->sc_kv.dc_count, + dev->sc_stat[0]); +#endif + + QPRINTF(("=STS:%02x=", dev->sc_stat[0])); + dev->sc_flags &= ~SBICF_ICMD; + + return(dev->sc_stat[0]); +} + +/* + * Finish SCSI xfer command: After the completion interrupt from + * a read/write operation, sequence through the final phases in + * programmed i/o. This routine is a lot like sbicicmd except we + * skip (and don't allow) the select, cmd out and data in/out phases. + */ +void +sbicxfdone(dev, regs, target) + struct sbic_softc *dev; + sbic_regmap_p regs; + int target; +{ + u_char phase, csr; + int s; + + QPRINTF(("{")); + s = splbio(); + + /* + * have the sbic complete on its own + */ + SBIC_TC_PUT(regs, 0); + SET_SBIC_cmd_phase(regs, 0x46); + SET_SBIC_cmd(regs, SBIC_CMD_SEL_ATN_XFER); + + do { + SBIC_WAIT (regs, SBIC_ASR_INT, 0); + GET_SBIC_csr (regs, csr); + QPRINTF(("%02x:", csr)); + } while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1) + && (csr != SBIC_CSR_S_XFERRED)); + + dev->sc_flags &= ~SBICF_SELECTED; + + GET_SBIC_cmd_phase (regs, phase); + QPRINTF(("}%02x", phase)); + if (phase == 0x60) + GET_SBIC_tlun(regs, dev->sc_stat[0]); + else + sbicerror(dev, regs, csr); + + QPRINTF(("=STS:%02x=\n", dev->sc_stat[0])); + splx(s); +} + + /* + * No DMA chains + */ + +int +sbicgo(dev, xs) + struct sbic_softc *dev; + struct scsi_xfer *xs; +{ + int i, dmaflags, count, wait, usedma; + u_char csr, asr, cmd, *addr; + sbic_regmap_p regs; + struct sbic_acb *acb; + + dev->target = xs->sc_link->target; + dev->lun = xs->sc_link->lun; + acb = dev->sc_nexus; + regs = dev->sc_sbicp; + + usedma = sbicdmaok(dev, xs); +#ifdef DEBUG + routine = 1; + debug_sbic_regs = regs; /* store this to allow debug calls */ + if( data_pointer_debug > 1 ) + printf("sbicgo(%d,%d)\n", dev->target, dev->lun); +#endif + + /* + * set the sbic into DMA mode + */ + if( usedma ) + SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI | + SBIC_MACHINE_DMA_MODE); + else + SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI); + + + /* + * select the SCSI bus (it's an error if bus isn't free) + */ + if (sbicselectbus(dev, regs, dev->target, dev->lun, + dev->sc_scsiaddr)) { +/* printf("sbicgo: Trying to select busy bus!\n"); */ + return(0); /* Not done: needs to be rescheduled */ + } + dev->sc_stat[0] = 0xff; + + /* + * Calculate DMA chains now + */ + + dmaflags = 0; + if (acb->flags & ACB_DATAIN) + dmaflags |= DMAGO_READ; + + + /* + * Deal w/bounce buffers. + */ + + addr = acb->sc_kv.dc_addr; + count = acb->sc_kv.dc_count; + if (count && (char *)kvtop(addr) != acb->sc_pa.dc_addr) { /* XXXX check */ + printf("sbic: DMA buffer mapping changed %x->%x\n", + acb->sc_pa.dc_addr, kvtop(addr)); +#ifdef DDB + Debugger(); +#endif + } + +#ifdef DEBUG + ++sbicdma_ops; /* count total DMA operations */ +#endif + if (count && usedma && dev->sc_flags & SBICF_BADDMA && + sbiccheckdmap(addr, count, dev->sc_dmamask)) { + /* + * need to bounce the dma. + */ + if (dmaflags & DMAGO_READ) { + acb->flags |= ACB_BBUF; + acb->sc_dmausrbuf = addr; + acb->sc_dmausrlen = count; + acb->sc_usrbufpa = (u_char *)kvtop(addr); + if(!dev->sc_tinfo[dev->target].bounce) { + printf("sbicgo: HELP! no bounce allocated for %d\n", + dev->target); + printf("xfer: (%x->%x,%x)\n", acb->sc_dmausrbuf, + acb->sc_usrbufpa, acb->sc_dmausrlen); +#if 0 + dev->sc_tinfo[xs->sc_link->target].bounce + = (char *)alloc_z2mem(MAXPHYS); + if (isztwomem(dev->sc_tinfo[xs->sc_link->target].bounce)) + printf("alloc ZII target %d bounce pa 0x%x\n", + xs->sc_link->target, + kvtop(dev->sc_tinfo[xs->sc_link->target].bounce)); + else if (dev->sc_tinfo[xs->sc_link->target].bounce) + printf("alloc CHIP target %d bounce pa 0x%x\n", + xs->sc_link->target, + PREP_DMA_MEM(dev->sc_tinfo[xs->sc_link->target].bounce)); +#endif + + printf("Allocating %d bounce at %x\n", + dev->target, + kvtop(dev->sc_tinfo[dev->target].bounce)); + } + } else { /* write: copy to dma buffer */ +#ifdef DEBUG + if(data_pointer_debug) + printf("sbicgo: copying %x bytes to target %d bounce %x\n", + count, dev->target, + kvtop(dev->sc_tinfo[dev->target].bounce)); +#endif + bcopy (addr, dev->sc_tinfo[dev->target].bounce, count); + } + addr = dev->sc_tinfo[dev->target].bounce;/* and use dma buffer */ + acb->sc_kv.dc_addr = addr; +#ifdef DEBUG + ++sbicdma_bounces; /* count number of bounced */ +#endif + } + + /* + * Allocate the DMA chain + */ + + /* Set start KVM addresses */ +#if 0 + acb->sc_kv.dc_addr = addr; + acb->sc_kv.dc_count = count; +#endif + + /* Mark end of segment */ + acb->sc_tcnt = dev->sc_tcnt = 0; + acb->sc_pa.dc_count = 0; + + sbic_load_ptrs(dev, regs, dev->target, dev->lun); + /* Enable interrupts but don't do any DMA */ + dev->sc_tcnt = dev->sc_dmago(dev, acb->sc_pa.dc_addr, + acb->sc_pa.dc_count, + dmaflags); + dev->sc_flags |= SBICF_INDMA; + if( !usedma ) + dev->sc_dmacmd = 0; /* Don't use DMA */ +/* SBIC_TC_PUT(regs, dev->sc_tcnt); /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + sbic_save_ptrs(dev, regs, dev->target, dev->lun); + + /* + * push the data cache ( I think this won't work (EH)) + */ +#if defined(M68040) + if (mmutype == MMU_68040 && usedma && count) { + dma_cachectl(addr, count); + if (((u_int)addr & 0xF) || (((u_int)addr + count) & 0xF)) + dev->sc_flags |= SBICF_DCFLUSH; + } +#endif + + /* + * dmago() also enables interrupts for the sbic + */ +#ifdef DEBUG + if( data_pointer_debug > 1 ) + printf("sbicgo dmago:%d(%x:%x)\n", + dev->target,dev->sc_cur->dc_addr,dev->sc_tcnt); + if( sbic_timeout && !timeout_active ) { + timeout((void *)sbictimeout, (void*)dev, sbic_timeout * hz); + timeout_active = 1; + } + debug_asr = asr; + debug_csr = csr; +#endif + + /* + * Lets cycle a while then let the interrupt handler take over + */ + + GET_SBIC_asr(regs, asr); + do { + GET_SBIC_csr(regs, csr); +#ifdef DEBUG + debug_csr = csr; + routine = 1; +#endif + QPRINTF(("go[0x%x]", csr)); + + i = sbicnextstate(dev, csr, asr); + + WAIT_CIP(regs); + GET_SBIC_asr(regs, asr); +#ifdef DEBUG + debug_asr = asr; +#endif + if(asr & SBIC_ASR_LCI) printf("sbicgo: LCI asr:%02x csr:%02x\n", + asr,csr); + } while( i == SBIC_STATE_RUNNING + && asr & (SBIC_ASR_INT|SBIC_ASR_LCI) ); + + if (i == SBIC_STATE_DONE && dev->sc_stat[0] != 0xff) { + /* Did we really finish that fast? */ + return 1; + } + return 0; +} + + +int +sbicintr(dev) + struct sbic_softc *dev; +{ + sbic_regmap_p regs; + struct dma_chain *df, *dl; + u_char asr, csr, *tmpaddr; + struct sbic_acb *acb; + int i, newtarget, newlun; + unsigned tcnt; + + regs = dev->sc_sbicp; + + /* + * pending interrupt? + */ + GET_SBIC_asr (regs, asr); + if ((asr & SBIC_ASR_INT) == 0) + return(0); + + do { + GET_SBIC_csr(regs, csr); +#ifdef DEBUG + debug_csr = csr; + routine = 2; +#endif + QPRINTF(("intr[0x%x]", csr)); + + i = sbicnextstate(dev, csr, asr); + + WAIT_CIP(regs); + GET_SBIC_asr(regs, asr); +#ifdef DEBUG + debug_asr = asr; +#endif +#if 0 + if(asr & SBIC_ASR_LCI) printf("sbicintr: LCI asr:%02x csr:%02x\n", + asr,csr); +#endif + } while(i == SBIC_STATE_RUNNING && + asr & (SBIC_ASR_INT|SBIC_ASR_LCI)); + return(1); +} + +/* + * Run commands and wait for disconnect + */ +int +sbicpoll(dev) + struct sbic_softc *dev; +{ + sbic_regmap_p regs; + u_char asr, csr; + struct sbic_pending* pendp; + int i; + unsigned tcnt; + + regs = dev->sc_sbicp; + + do { + GET_SBIC_asr (regs, asr); +#ifdef DEBUG + debug_asr = asr; +#endif + GET_SBIC_csr(regs, csr); +#ifdef DEBUG + debug_csr = csr; + routine = 2; +#endif + QPRINTF(("poll[0x%x]", csr)); + + i = sbicnextstate(dev, csr, asr); + + WAIT_CIP(regs); + GET_SBIC_asr(regs, asr); + /* tapes may take a loooong time.. */ + while (asr & SBIC_ASR_BSY){ + if(asr & SBIC_ASR_DBR) { + printf("sbipoll: Waiting while sbic is jammed, CSR:%02x,ASR:%02x\n", + csr,asr); +#ifdef DDB + Debugger(); +#endif + /* SBIC is jammed */ + /* DUNNO which direction */ + /* Try old direction */ + GET_SBIC_data(regs,i); + GET_SBIC_asr(regs, asr); + if( asr & SBIC_ASR_DBR) /* Wants us to write */ + SET_SBIC_data(regs,i); + } + GET_SBIC_asr(regs, asr); + } + + if(asr & SBIC_ASR_LCI) printf("sbicpoll: LCI asr:%02x csr:%02x\n", + asr,csr); + else if( i == 1 ) /* BSY */ + SBIC_WAIT(regs, SBIC_ASR_INT, sbic_cmd_wait); + } while(i == SBIC_STATE_RUNNING); + return(1); +} + +/* + * Handle a single msgin + */ + +int +sbicmsgin(dev) + struct sbic_softc *dev; +{ + sbic_regmap_p regs; + int recvlen; + u_char asr, csr, *tmpaddr; + + regs = dev->sc_sbicp; + + dev->sc_msg[0] = 0xff; + dev->sc_msg[1] = 0xff; + + GET_SBIC_asr(regs, asr); +#ifdef DEBUG + if(reselect_debug>1) + printf("sbicmsgin asr=%02x\n", asr); +#endif + + sbic_save_ptrs(dev, regs, dev->target, dev->lun); + + GET_SBIC_selid (regs, csr); + SET_SBIC_selid (regs, csr | SBIC_SID_FROM_SCSI); + + SBIC_TC_PUT(regs, 0); + tmpaddr = dev->sc_msg; + recvlen = 1; + do { + while( recvlen-- ) { + GET_SBIC_asr(regs, asr); + GET_SBIC_csr(regs, csr); + QPRINTF(("sbicmsgin ready to go (csr,asr)=(%02x,%02x)\n", + csr, asr)); + + RECV_BYTE(regs, *tmpaddr); +#if 1 + /* + * get the command completion interrupt, or we + * can't send a new command (LCI) + */ + SBIC_WAIT(regs, SBIC_ASR_INT, 0); + GET_SBIC_csr(regs, csr); +#else + WAIT_CIP(regs); + do { + GET_SBIC_asr(regs, asr); + csr = 0xff; + GET_SBIC_csr(regs, csr); + if( csr == 0xff ) + printf("sbicmsgin waiting: csr %02x asr %02x\n", csr, asr); + } while( csr == 0xff ); +#endif +#ifdef DEBUG + if(reselect_debug>1) + printf("sbicmsgin: got %02x csr %02x asr %02x\n", + *tmpaddr, csr, asr); +#endif +#if do_parity_check + if( asr & SBIC_ASR_PE ) { + printf ("Parity error"); + /* This code simply does not work. */ + WAIT_CIP(regs); + SET_SBIC_cmd(regs, SBIC_CMD_SET_ATN); + WAIT_CIP(regs); + GET_SBIC_asr(regs, asr); + WAIT_CIP(regs); + SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK); + WAIT_CIP(regs); + if( !(asr & SBIC_ASR_LCI) ) + /* Target wants to send garbled msg*/ + continue; + printf("--fixing\n"); + /* loop until a msgout phase occurs on target */ + while(csr & 0x07 != MESG_OUT_PHASE) { + while( asr & SBIC_ASR_BSY && + !(asr & SBIC_ASR_DBR|SBIC_ASR_INT) ) + GET_SBIC_asr(regs, asr); + if( asr & SBIC_ASR_DBR ) + panic("msgin: jammed again!\n"); + GET_SBIC_csr(regs, csr); + if( csr & 0x07 != MESG_OUT_PHASE ) { + sbicnextstate(dev, csr, asr); + sbic_save_ptrs(dev, regs, + dev->target, + dev->lun); + } + } + /* Should be msg out by now */ + SEND_BYTE(regs, MSG_PARITY_ERROR); + } + else +#endif + tmpaddr++; + + if(recvlen) { + /* Clear ACK */ + WAIT_CIP(regs); + GET_SBIC_asr(regs, asr); + GET_SBIC_csr(regs, csr); + QPRINTF(("sbicmsgin pre byte CLR_ACK (csr,asr)=(%02x,%02x)\n", + csr, asr)); + SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK); + SBIC_WAIT(regs, SBIC_ASR_INT, 0); + } + + }; + + if(dev->sc_msg[0] == 0xff) { + printf("sbicmsgin: sbic swallowed our message\n"); + break; + } +#ifdef DEBUG + if (sync_debug) + printf("msgin done csr 0x%x asr 0x%x msg 0x%x\n", + csr, asr, dev->sc_msg[0]); +#endif + /* + * test whether this is a reply to our sync + * request + */ + if (MSG_ISIDENTIFY(dev->sc_msg[0])) { + QPRINTF(("IFFY")); +#if 0 + /* There is an implied load-ptrs here */ + sbic_load_ptrs(dev, regs, dev->target, dev->lun); +#endif + /* Got IFFY msg -- ack it */ + } else if (dev->sc_msg[0] == MSG_REJECT + && dev->sc_sync[dev->target].state == SYNC_SENT) { + QPRINTF(("REJECT of SYN")); +#ifdef DEBUG + if (sync_debug) + printf("target %d rejected sync, going async\n", + dev->target); +#endif + dev->sc_sync[dev->target].period = sbic_min_period; + dev->sc_sync[dev->target].offset = 0; + dev->sc_sync[dev->target].state = SYNC_DONE; + SET_SBIC_syn(regs, + SBIC_SYN(dev->sc_sync[dev->target].offset, + dev->sc_sync[dev->target].period)); + } else if ((dev->sc_msg[0] == MSG_REJECT)) { + QPRINTF(("REJECT")); + /* + * we'll never REJECt a REJECT message.. + */ + } else if ((dev->sc_msg[0] == MSG_SAVE_DATA_PTR)) { + QPRINTF(("MSG_SAVE_DATA_PTR")); + /* + * don't reject this either. + */ + } else if ((dev->sc_msg[0] == MSG_DISCONNECT)) { + QPRINTF(("DISCONNECT")); +#ifdef DEBUG + if( reselect_debug>1 && dev->sc_msg[0] == MSG_DISCONNECT ) + printf("sbicmsgin: got disconnect msg %s\n", + (dev->sc_flags & SBICF_ICMD)?"rejecting":""); +#endif + if( dev->sc_flags & SBICF_ICMD ) { + /* We're in immediate mode. Prevent disconnects. */ + /* prepare to reject the message, NACK */ + SET_SBIC_cmd(regs, SBIC_CMD_SET_ATN); + WAIT_CIP(regs); + } + } else if (dev->sc_msg[0] == MSG_CMD_COMPLETE ) { + QPRINTF(("CMD_COMPLETE")); + /* !! KLUDGE ALERT !! quite a few drives don't seem to + * really like the current way of sending the + * sync-handshake together with the ident-message, and + * they react by sending command-complete and + * disconnecting right after returning the valid sync + * handshake. So, all I can do is reselect the drive, + * and hope it won't disconnect again. I don't think + * this is valid behavior, but I can't help fixing a + * problem that apparently exists. + * + * Note: we should not get here on `normal' command + * completion, as that condition is handled by the + * high-level sel&xfer resume command used to walk + * thru status/cc-phase. + */ + +#ifdef DEBUG + if (sync_debug) + printf ("GOT MSG %d! target %d acting weird.." + " waiting for disconnect...\n", + dev->sc_msg[0], dev->target); +#endif + /* Check to see if sbic is handling this */ + GET_SBIC_asr(regs, asr); + if(asr & SBIC_ASR_BSY) + return SBIC_STATE_RUNNING; + + /* Let's try this: Assume it works and set status to 00 */ + dev->sc_stat[0] = 0; + } else if (dev->sc_msg[0] == MSG_EXT_MESSAGE + && tmpaddr == &dev->sc_msg[1]) { + QPRINTF(("ExtMSG\n")); + /* Read in whole extended message */ + SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK); + SBIC_WAIT(regs, SBIC_ASR_INT, 0); + GET_SBIC_asr(regs, asr); + GET_SBIC_csr(regs, csr); + QPRINTF(("CLR ACK asr %02x, csr %02x\n", asr, csr)); + RECV_BYTE(regs, *tmpaddr); + /* Wait for command completion IRQ */ + SBIC_WAIT(regs, SBIC_ASR_INT, 0); + recvlen = *tmpaddr++; + QPRINTF(("Recving ext msg, asr %02x csr %02x len %02x\n", + asr, csr, recvlen)); + } else if (dev->sc_msg[0] == MSG_EXT_MESSAGE && dev->sc_msg[1] == 3 + && dev->sc_msg[2] == MSG_SYNC_REQ) { + QPRINTF(("SYN")); + dev->sc_sync[dev->target].period = + sbicfromscsiperiod(dev, + regs, dev->sc_msg[3]); + dev->sc_sync[dev->target].offset = dev->sc_msg[4]; + dev->sc_sync[dev->target].state = SYNC_DONE; + SET_SBIC_syn(regs, + SBIC_SYN(dev->sc_sync[dev->target].offset, + dev->sc_sync[dev->target].period)); + printf("%s: target %d now synchronous," + " period=%dns, offset=%d.\n", + dev->sc_dev.dv_xname, dev->target, + dev->sc_msg[3] * 4, dev->sc_msg[4]); + } else { +#ifdef DEBUG + if (sbic_debug || sync_debug) + printf ("sbicmsgin: Rejecting message 0x%02x\n", + dev->sc_msg[0]); +#endif + /* prepare to reject the message, NACK */ + SET_SBIC_cmd(regs, SBIC_CMD_SET_ATN); + WAIT_CIP(regs); + } + /* Clear ACK */ + WAIT_CIP(regs); + GET_SBIC_asr(regs, asr); + GET_SBIC_csr(regs, csr); + QPRINTF(("sbicmsgin pre CLR_ACK (csr,asr)=(%02x,%02x)%d\n", + csr, asr, recvlen)); + SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK); + SBIC_WAIT(regs, SBIC_ASR_INT, 0); + } +#if 0 + while((csr == SBIC_CSR_MSGIN_W_ACK) + || (SBIC_PHASE(csr) == MESG_IN_PHASE)); +#else + while (recvlen>0); +#endif + + QPRINTF(("sbicmsgin finished: csr %02x, asr %02x\n",csr, asr)); + + /* Should still have one CSR to read */ + return SBIC_STATE_RUNNING; +} + + +/* + * sbicnextstate() + * return: + * 0 == done + * 1 == working + * 2 == disconnected + * -1 == error + */ +int +sbicnextstate(dev, csr, asr) + struct sbic_softc *dev; + u_char csr, asr; +{ + sbic_regmap_p regs; + struct dma_chain *df, *dl; + struct sbic_acb *acb; + int i, newtarget, newlun, wait; + unsigned tcnt; + + regs = dev->sc_sbicp; + acb = dev->sc_nexus; + + QPRINTF(("next[%02x,%02x]",asr,csr)); + + switch (csr) { + case SBIC_CSR_XFERRED|CMD_PHASE: + case SBIC_CSR_MIS|CMD_PHASE: + case SBIC_CSR_MIS_1|CMD_PHASE: + case SBIC_CSR_MIS_2|CMD_PHASE: + sbic_save_ptrs(dev, regs, dev->target, dev->lun); + if (sbicxfstart(regs, acb->clen, CMD_PHASE, sbic_cmd_wait)) + if (sbicxfout(regs, acb->clen, + &acb->cmd, CMD_PHASE)) + goto abort; + break; + + case SBIC_CSR_XFERRED|STATUS_PHASE: + case SBIC_CSR_MIS|STATUS_PHASE: + case SBIC_CSR_MIS_1|STATUS_PHASE: + case SBIC_CSR_MIS_2|STATUS_PHASE: + /* + * this should be the normal i/o completion case. + * get the status & cmd complete msg then let the + * device driver look at what happened. + */ + sbicxfdone(dev,regs,dev->target); + /* + * check for overlapping cache line, flush if so + */ +#ifdef M68040 + if (dev->sc_flags & SBICF_DCFLUSH) { +#if 0 + printf("sbic: 68040 DMA cache flush needs fixing? %x:%x\n", + dev->sc_xs->data, dev->sc_xs->datalen); +#endif + } +#endif +#ifdef DEBUG + if( data_pointer_debug > 1 ) + printf("next dmastop: %d(%x:%x)\n", + dev->target,dev->sc_cur->dc_addr,dev->sc_tcnt); +#endif + dev->sc_dmastop(dev); /* was dmafree */ + if (acb->flags & ACB_BBUF) { + if ((u_char *)kvtop(acb->sc_dmausrbuf) != acb->sc_usrbufpa) + printf("%s: WARNING - buffer mapping changed %x->%x\n", + dev->sc_dev.dv_xname, acb->sc_usrbufpa, + kvtop(acb->sc_dmausrbuf)); +#ifdef DEBUG + if(data_pointer_debug) + printf("sbicgo:copying %x bytes from target %d bounce %x\n", + acb->sc_dmausrlen, + dev->target, + kvtop(dev->sc_tinfo[dev->target].bounce)); +#endif + bcopy(dev->sc_tinfo[dev->target].bounce, + acb->sc_dmausrbuf, + acb->sc_dmausrlen); + } + dev->sc_flags &= ~(SBICF_INDMA | SBICF_DCFLUSH); + sbic_scsidone(acb, dev->sc_stat[0]); + return SBIC_STATE_DONE; + + case SBIC_CSR_XFERRED|DATA_OUT_PHASE: + case SBIC_CSR_XFERRED|DATA_IN_PHASE: + case SBIC_CSR_MIS|DATA_OUT_PHASE: + case SBIC_CSR_MIS|DATA_IN_PHASE: + case SBIC_CSR_MIS_1|DATA_OUT_PHASE: + case SBIC_CSR_MIS_1|DATA_IN_PHASE: + case SBIC_CSR_MIS_2|DATA_OUT_PHASE: + case SBIC_CSR_MIS_2|DATA_IN_PHASE: + if( dev->sc_xs->flags & SCSI_POLL || dev->sc_flags & SBICF_ICMD + || acb->sc_dmacmd == 0 ) { + /* Do PIO */ + SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI); + if (acb->sc_kv.dc_count <= 0) { + printf("sbicnextstate:xfer count %d asr%x csr%x\n", + acb->sc_kv.dc_count, asr, csr); + goto abort; + } + wait = sbic_data_wait; + if( sbicxfstart(regs, + acb->sc_kv.dc_count, + SBIC_PHASE(csr), wait)) + if( SBIC_PHASE(csr) == DATA_IN_PHASE ) + /* data in? */ + i=sbicxfin(regs, + acb->sc_kv.dc_count, + acb->sc_kv.dc_addr); + else + i=sbicxfout(regs, + acb->sc_kv.dc_count, + acb->sc_kv.dc_addr, + SBIC_PHASE(csr)); + acb->sc_kv.dc_addr += + (acb->sc_kv.dc_count - i); + acb->sc_kv.dc_count = i; + } else { + /* + * do scatter-gather dma + * hacking the controller chip, ouch.. + */ + SET_SBIC_control(regs, SBIC_CTL_EDI | SBIC_CTL_IDI | + SBIC_MACHINE_DMA_MODE); + /* + * set next dma addr and dec count + */ +#if 0 + SBIC_TC_GET(regs, tcnt); + dev->sc_cur->dc_count -= ((dev->sc_tcnt - tcnt) >> 1); + dev->sc_cur->dc_addr += (dev->sc_tcnt - tcnt); + dev->sc_tcnt = acb->sc_tcnt = tcnt; +#else + sbic_save_ptrs(dev, regs, dev->target, dev->lun); + sbic_load_ptrs(dev, regs, dev->target, dev->lun); +#endif +#ifdef DEBUG + if( data_pointer_debug > 1 ) + printf("next dmanext: %d(%x:%x)\n", + dev->target,dev->sc_cur->dc_addr, + dev->sc_tcnt); +#endif + dev->sc_tcnt = dev->sc_dmanext(dev); + SBIC_TC_PUT(regs, (unsigned)dev->sc_tcnt); + SET_SBIC_cmd(regs, SBIC_CMD_XFER_INFO); + dev->sc_flags |= SBICF_INDMA; + } + break; + + case SBIC_CSR_XFERRED|MESG_IN_PHASE: + case SBIC_CSR_MIS|MESG_IN_PHASE: + case SBIC_CSR_MIS_1|MESG_IN_PHASE: + case SBIC_CSR_MIS_2|MESG_IN_PHASE: + return sbicmsgin(dev); + + case SBIC_CSR_MSGIN_W_ACK: + SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK); /* Dunno what I'm ACKing */ + printf("Acking unknown msgin CSR:%02x",csr); + break; + + case SBIC_CSR_XFERRED|MESG_OUT_PHASE: + case SBIC_CSR_MIS|MESG_OUT_PHASE: + case SBIC_CSR_MIS_1|MESG_OUT_PHASE: + case SBIC_CSR_MIS_2|MESG_OUT_PHASE: +#ifdef DEBUG + if (sync_debug) + printf ("sending REJECT msg to last msg.\n"); +#endif + + sbic_save_ptrs(dev, regs, dev->target, dev->lun); + /* + * should only get here on reject, + * since it's always US that + * initiate a sync transfer + */ + SEND_BYTE(regs, MSG_REJECT); + WAIT_CIP(regs); + if( asr & (SBIC_ASR_BSY|SBIC_ASR_LCI|SBIC_ASR_CIP) ) + printf("next: REJECT sent asr %02x\n", asr); + return SBIC_STATE_RUNNING; + + case SBIC_CSR_DISC: + case SBIC_CSR_DISC_1: + dev->sc_flags &= ~(SBICF_INDMA|SBICF_SELECTED); + + /* Try to schedule another target */ +#ifdef DEBUG + if(reselect_debug>1) + printf("sbicnext target %d disconnected\n", dev->target); +#endif + TAILQ_INSERT_HEAD(&dev->nexus_list, acb, chain); + ++dev->sc_tinfo[dev->target].dconns; + dev->sc_nexus = NULL; + dev->sc_xs = NULL; + + if( acb->xs->flags & SCSI_POLL + || (dev->sc_flags & SBICF_ICMD) + || !sbic_parallel_operations ) + return SBIC_STATE_DISCONNECT; + sbic_sched(dev); + return SBIC_STATE_DISCONNECT; + + case SBIC_CSR_RSLT_NI: + case SBIC_CSR_RSLT_IFY: + GET_SBIC_rselid(regs, newtarget); + /* check SBIC_RID_SIV? */ + newtarget &= SBIC_RID_MASK; + if (csr == SBIC_CSR_RSLT_IFY) { + /* Read IFY msg to avoid lockup */ + GET_SBIC_data(regs, newlun); + WAIT_CIP(regs); + newlun &= SBIC_TLUN_MASK; + } else { + /* Need to get IFY message */ + for (newlun = 256; newlun; --newlun) { + GET_SBIC_asr(regs, asr); + if (asr & SBIC_ASR_INT) + break; + delay(1); + } + newlun = 0; /* XXXX */ + if ((asr & SBIC_ASR_INT) == 0) { +#ifdef DEBUG + if (reselect_debug) + printf("RSLT_NI - no IFFY message? asr %x\n", asr); +#endif + } else { + GET_SBIC_csr(regs,csr); + if (csr == SBIC_CSR_MIS|MESG_IN_PHASE || + csr == SBIC_CSR_MIS_1|MESG_IN_PHASE || + csr == SBIC_CSR_MIS_2|MESG_IN_PHASE) { + sbicmsgin(dev); + newlun = dev->sc_msg[0] & 7; + } else { + printf("RSLT_NI - not MESG_IN_PHASE %x\n", + csr); + } + } + } +#ifdef DEBUG + if(reselect_debug>1 || (reselect_debug && csr==SBIC_CSR_RSLT_NI)) + printf("sbicnext: reselect %s from targ %d lun %d\n", + csr == SBIC_CSR_RSLT_NI ? "NI" : "IFY", + newtarget, newlun); +#endif + if (dev->sc_nexus) { +#ifdef DEBUG + if (reselect_debug > 1) + printf("%s: reselect %s with active command\n", + dev->sc_dev.dv_xname, + csr == SBIC_CSR_RSLT_NI ? "NI" : "IFY"); +#ifdef DDB +/* Debugger();*/ +#endif +#endif + TAILQ_INSERT_HEAD(&dev->ready_list, dev->sc_nexus, chain); + dev->sc_tinfo[dev->target].lubusy &= ~(1 << dev->lun); + } + /* Reload sync values for this target */ + if (dev->sc_sync[newtarget].state == SYNC_DONE) + SET_SBIC_syn(regs, SBIC_SYN (dev->sc_sync[newtarget].offset, + dev->sc_sync[newtarget].period)); + else + SET_SBIC_syn(regs, SBIC_SYN (0, sbic_min_period)); + for (acb = dev->nexus_list.tqh_first; acb; + acb = acb->chain.tqe_next) { + if (acb->xs->sc_link->target != newtarget || + acb->xs->sc_link->lun != newlun) + continue; + TAILQ_REMOVE(&dev->nexus_list, acb, chain); + dev->sc_nexus = acb; + dev->sc_xs = acb->xs; + dev->sc_flags |= SBICF_SELECTED; + dev->target = newtarget; + dev->lun = newlun; + break; + } + if (acb == NULL) { + printf("%s: reselect %s targ %d not in nexus_list %x\n", + dev->sc_dev.dv_xname, + csr == SBIC_CSR_RSLT_NI ? "NI" : "IFY", newtarget, + &dev->nexus_list.tqh_first); + panic("bad reselect in sbic"); + } + if (csr == SBIC_CSR_RSLT_IFY) + SET_SBIC_cmd(regs, SBIC_CMD_CLR_ACK); + break; + + default: + abort: + /* + * Something unexpected happened -- deal with it. + */ + printf("sbicnextstate: aborting csr %02x asr %02x\n", csr, asr); +#ifdef DDB + Debugger(); +#endif +#ifdef DEBUG + if( data_pointer_debug > 1 ) + printf("next dmastop: %d(%x:%x)\n", + dev->target,dev->sc_cur->dc_addr,dev->sc_tcnt); +#endif + dev->sc_dmastop(dev); + sbicerror(dev, regs, csr); + sbicabort(dev, regs, "next"); + if (dev->sc_flags & SBICF_INDMA) { + /* + * check for overlapping cache line, flush if so + */ +#ifdef M68040 + if (dev->sc_flags & SBICF_DCFLUSH) { +#if 0 + printf("sibc: 68040 DMA cache flush needs fixing? %x:%x\n", + dev->sc_xs->data, dev->sc_xs->datalen); +#endif + } +#endif + dev->sc_flags &= + ~(SBICF_INDMA | SBICF_DCFLUSH); +#ifdef DEBUG + if( data_pointer_debug > 1 ) + printf("next dmastop: %d(%x:%x)\n", + dev->target,dev->sc_cur->dc_addr,dev->sc_tcnt); +#endif + dev->sc_dmastop(dev); + sbic_scsidone(acb, -1); + } + return SBIC_STATE_ERROR; + } + + return(SBIC_STATE_RUNNING); +} + + +/* + * Check if DMA can not be used with specified buffer + */ + +int +sbiccheckdmap(bp, len, mask) + void *bp; + u_long len, mask; +{ + u_char *buffer; + u_long phy_buf; + u_long phy_len; + + buffer = bp; + + if (len == 0) + return(0); + + while (len) { + phy_buf = kvtop(buffer); + if (len < (phy_len = NBPG - ((int) buffer & PGOFSET))) + phy_len = len; + if (phy_buf & mask) + return(1); + buffer += phy_len; + len -= phy_len; + } + return(0); +} + +int +sbictoscsiperiod(dev, regs, a) + struct sbic_softc *dev; + sbic_regmap_p regs; + int a; +{ + unsigned int fs; + + /* + * cycle = DIV / (2*CLK) + * DIV = FS+2 + * best we can do is 200ns at 20Mhz, 2 cycles + */ + + GET_SBIC_myid(regs,fs); + fs = (fs >>6) + 2; /* DIV */ + fs = (fs * 10000) / (dev->sc_clkfreq<<1); /* Cycle, in ns */ + if (a < 2) a = 8; /* map to Cycles */ + return ((fs*a)>>2); /* in 4 ns units */ +} + +int +sbicfromscsiperiod(dev, regs, p) + struct sbic_softc *dev; + sbic_regmap_p regs; + int p; +{ + register unsigned int fs, ret; + + /* Just the inverse of the above */ + + GET_SBIC_myid(regs,fs); + fs = (fs >>6) + 2; /* DIV */ + fs = (fs * 10000) / (dev->sc_clkfreq<<1); /* Cycle, in ns */ + + ret = p << 2; /* in ns units */ + ret = ret / fs; /* in Cycles */ + if (ret < sbic_min_period) + return(sbic_min_period); + + /* verify rounding */ + if (sbictoscsiperiod(dev, regs, ret) < p) + ret++; + return (ret >= 8) ? 0 : ret; +} + +#ifdef DEBUG + +void sbicdumpstate() +{ + u_char csr, asr; + + GET_SBIC_asr(debug_sbic_regs,asr); + GET_SBIC_csr(debug_sbic_regs,csr); + printf("%s: asr:csr(%02x:%02x)->(%02x:%02x)\n", + (routine==1)?"sbicgo": + (routine==2)?"sbicintr": + (routine==3)?"sbicicmd": + (routine==4)?"sbicnext":"unknown", + debug_asr, debug_csr, asr, csr); + +} + +void sbictimeout(dev) + struct sbic_softc *dev; +{ + int s, asr; + + /* Dump this every second while there's an active command */ + if( dev->sc_nexus ) { + s = splbio(); + GET_SBIC_asr(dev->sc_sbicp, asr); + if( asr & SBIC_ASR_INT ) { + /* We need to service a missed IRQ */ + printf("Servicing a missed int:(%02x,%02x)->(%02x,??)\n", + debug_asr, debug_csr, asr); + sbicintr(dev); + } + splx(s); + sbicdumpstate(); + timeout((void *)sbictimeout, (void*)dev, sbic_timeout * hz); + } else timeout_active = 0; +} + +#endif diff --git a/sys/arch/mvme68k/dev/sbicdma.c b/sys/arch/mvme68k/dev/sbicdma.c new file mode 100644 index 00000000000..193cc674efa --- /dev/null +++ b/sys/arch/mvme68k/dev/sbicdma.c @@ -0,0 +1,278 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * Copyright (c) 1995 Dale Rahn. 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 Dale Rahn. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * as you may guess by some of the routines in this file, + * dma is not yet working. - its on the TODO list + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/proc.h> +#include <sys/conf.h> +#include <vm/vm.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <machine/autoconf.h> +#include <mvme68k/dev/pccreg.h> +#include <mvme68k/dev/sbicreg.h> +#include <mvme68k/dev/sbicvar.h> +#include <mvme68k/dev/dmavar.h> + +void sbicdmaattach __P((struct device *, struct device *, void *)); +int sbicdmamatch __P((struct device *, void *, void *)); +int sbicdmaprint __P((void *auxp, char *)); + +void sbicdma_dmafree __P((struct sbic_softc *)); +void sbicdma_dmastop __P((struct sbic_softc *)); +int sbicdma_dmanext __P((struct sbic_softc *)); +int sbicdma_dmago __P((struct sbic_softc *, char *, int, int)); +int sbicdma_dmaintr __P((struct sbic_softc *)); +int sbicdma_scintr __P((struct sbic_softc *)); + +struct scsi_adapter sbicdma_scsiswitch = { + sbic_scsicmd, + sbic_minphys, + 0, /* no lun support */ + 0, /* no lun support */ +}; + +struct scsi_device sbicdma_scsidev = { + NULL, /* use default error handler */ + NULL, /* have a queue served by this ??? */ + NULL, /* have no async handler ??? */ + NULL, /* Use default done routine */ +}; + +#ifdef DEBUG +int sbicdma_debug = 1; +#endif + +int sbicdma_maxdma = 0; /* Maximum size per DMA transfer */ +int sbicdma_dmamask = 0; +int sbicdma_dmabounce = 0; + +struct cfdriver sbiccd = { + NULL, "sbic", sbicdmamatch, sbicdmaattach, + DV_DULL, sizeof(struct sbic_softc), +}; + +int +sbicdmamatch(parent, vcf, args) + struct device *parent; + void *vcf, *args; +{ + /* + * XXX finish to properly probe + */ + return (1); +} + +void +sbicdmaattach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct sbic_softc *sc = (struct sbic_softc *)self; + struct pccreg *pcc; + struct confargs *ca = args; + + sc->sc_cregs = (struct pccreg *)ca->ca_master; + sc->sc_dmafree = sbicdma_dmafree; + sc->sc_dmago = sbicdma_dmago; + sc->sc_dmanext = sbicdma_dmanext; + sc->sc_dmastop = sbicdma_dmastop; + sc->sc_dmacmd = 0; + + sc->sc_flags |= SBICF_BADDMA; + sc->sc_dmamask = 0; + sc->sc_sbicp = (sbic_regmap_p)ca->ca_vaddr; + sc->sc_clkfreq = 100; + + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter_target = 7; + sc->sc_link.adapter = &sbicdma_scsiswitch; + sc->sc_link.device = &sbicdma_scsidev; + sc->sc_link.openings = 1; + + printf(": target %d\n", sc->sc_link.adapter_target); + + /* connect the interrupts */ + sc->sc_ih.ih_fn = sbicdma_scintr; + sc->sc_ih.ih_arg = sc; + sc->sc_ih.ih_ipl = ca->ca_ipl; + pccintr_establish(PCCV_SBIC, &sc->sc_ih); + + sc->sc_dmaih.ih_fn = sbicdma_dmaintr; + sc->sc_dmaih.ih_arg = sc; + sc->sc_dmaih.ih_ipl = ca->ca_ipl; + pccintr_establish(PCCV_DMA, &sc->sc_dmaih); + + pcc = (struct pccreg *)sc->sc_cregs; + pcc->pcc_dmairq = sc->sc_dmaih.ih_ipl | PCC_IRQ_INT; + pcc->pcc_sbicirq = sc->sc_ih.ih_ipl | PCC_SBIC_RESETIRQ; + + sbicreset(sc); + + evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt); + evcnt_attach(&sc->sc_dev, "intr", &sc->sc_dmaintrcnt); + + config_found(self, &sc->sc_link, sbicdmaprint); +} + +/* + * print diag if pnp is NULL else just extra + */ +int +sbicdmaprint(auxp, pnp) + void *auxp; + char *pnp; +{ + if (pnp == NULL) + return (UNCONF); + return (QUIET); +} + +int +sbicdma_dmago(sc, va, count, flags) + struct sbic_softc *sc; + char *va; + int count, flags; +{ + struct pccreg *pcc = (struct pccreg *)sc->sc_cregs; + u_char csr; + u_long pa; + + pa = (u_long)pmap_extract(pmap_kernel(), (vm_offset_t)va); +#ifdef DEBUG + if (sbicdma_debug) + printf("%s: dmago: va 0x%x pa 0x%x cnt %d flags %x\n", + sc->sc_dev.dv_xname, va, pa, count, flags); +#endif + + sc->sc_flags |= SBICF_INTR; + pcc->pcc_dmadaddr = (u_long)pa; + if (count & PCC_DMABCNT_CNTMASK) { + printf("%s: dma count 0x%x too large\n", + sc->sc_dev.dv_xname, count); + return (0); + } + pcc->pcc_dmabcnt = (PCC_DMABCNT_MAKEFC(FC_USERD)) | + (count & PCC_DMABCNT_CNTMASK); + + /* make certain interupts are disabled first, and reset */ + pcc->pcc_dmairq = sc->sc_dmaih.ih_ipl | PCC_IRQ_IEN | PCC_IRQ_INT; + pcc->pcc_sbicirq = sc->sc_ih.ih_ipl | PCC_SBIC_RESETIRQ | PCC_IRQ_IEN; + pcc->pcc_dmacsr = 0; + pcc->pcc_dmacsr = PCC_DMACSR_DEN | + ((flags & DMAGO_READ) == 0) ? PCC_DMACSR_TOSCSI : 0; + + return (sc->sc_tcnt); +} + +int +sbicdma_dmaintr(sc) + struct sbic_softc *sc; +{ + struct pccreg *pcc = (struct pccreg *)sc->sc_cregs; + u_char stat; + int ret = 0; + + /* DMA done */ + stat = pcc->pcc_dmacsr; +printf("dmaintr%d ", stat); + if (stat & PCC_DMACSR_DONE) { + pcc->pcc_dmacsr = 0; + pcc->pcc_dmairq = 0; /* ack and remove intr */ + if (stat & PCC_DMACSR_ERR8BIT) { + printf("%s: 8 bit error\n", sc->sc_dev.dv_xname); + } + if (stat & PCC_DMACSR_DMAERRDATA) { + printf("%s: DMA bus error\n", sc->sc_dev.dv_xname); + } + sc->sc_dmaintrcnt.ev_count++; + return (1); + } + return (0); +} + +int +sbicdma_scintr(sc) + struct sbic_softc *sc; +{ + struct pccreg *pcc = (struct pccreg *)sc->sc_cregs; + u_char stat; + int ret = 0; + +printf("scintr%d ", stat); + stat = pcc->pcc_sbicirq; + if (stat & PCC_SBIC_RESETIRQ) { + printf("%s: scintr: a scsi device pulled reset\n", + sc->sc_dev.dv_xname); + pcc->pcc_sbicirq = pcc->pcc_sbicirq | PCC_SBIC_RESETIRQ; + } else if (stat & PCC_IRQ_INT) { + pcc->pcc_sbicirq = 0; + sbicintr(sc); + ret = 1; + sc->sc_intrcnt.ev_count++; + } + return (ret); +} + +void +sbicdma_dmastop(sc) + struct sbic_softc *sc; +{ + printf("sbicdma_dmastop called\n"); + /* XXX do nothing */ +} + +int +sbicdma_dmanext(sc) + struct sbic_softc *sc; +{ + printf("sbicdma_dmanext called\n"); +} + +void +sbicdma_dmafree(sc) + struct sbic_softc *sc; +{ + struct pccreg *pcc = (struct pccreg *)sc->sc_cregs; + + printf("sbicdma_dmafree called\n"); + /* make certain interupts are disabled first, reset */ + pcc->pcc_dmairq = 0; +} diff --git a/sys/arch/mvme68k/dev/sbicreg.h b/sys/arch/mvme68k/dev/sbicreg.h new file mode 100644 index 00000000000..f3844dcd246 --- /dev/null +++ b/sys/arch/mvme68k/dev/sbicreg.h @@ -0,0 +1,424 @@ +/* $NetBSD: sbicreg.h,v 1.2 1994/10/26 02:04:40 cgd Exp $ */ + +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * 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. + * + * @(#)scsireg.h 7.3 (Berkeley) 2/5/91 + */ + +/* + * AMD AM33C93A SCSI interface hardware description. + * + * Using parts of the Mach scsi driver for the 33C93 + */ + +#define SBIC_myid 0 +#define SBIC_cdbsize 0 +#define SBIC_control 1 +#define SBIC_timeo 2 +#define SBIC_cdb1 3 +#define SBIC_tsecs 3 +#define SBIC_cdb2 4 +#define SBIC_theads 4 +#define SBIC_cdb3 5 +#define SBIC_tcyl_hi 5 +#define SBIC_cdb4 6 +#define SBIC_tcyl_lo 6 +#define SBIC_cdb5 7 +#define SBIC_addr_hi 7 +#define SBIC_cdb6 8 +#define SBIC_addr_2 8 +#define SBIC_cdb7 9 +#define SBIC_addr_3 9 +#define SBIC_cdb8 10 +#define SBIC_addr_lo 10 +#define SBIC_cdb9 11 +#define SBIC_secno 11 +#define SBIC_cdb10 12 +#define SBIC_headno 12 +#define SBIC_cdb11 13 +#define SBIC_cylno_hi 13 +#define SBIC_cdb12 14 +#define SBIC_cylno_lo 14 +#define SBIC_tlun 15 +#define SBIC_cmd_phase 16 +#define SBIC_syn 17 +#define SBIC_count_hi 18 +#define SBIC_count_med 19 +#define SBIC_count_lo 20 +#define SBIC_selid 21 +#define SBIC_rselid 22 +#define SBIC_csr 23 +#define SBIC_cmd 24 +#define SBIC_data 25 +/* sbic_asr is addressed directly */ + +/* + * Register defines + */ + +/* + * Auxiliary Status Register + */ + +#define SBIC_ASR_INT 0x80 /* Interrupt pending */ +#define SBIC_ASR_LCI 0x40 /* Last command ignored */ +#define SBIC_ASR_BSY 0x20 /* Busy, only cmd/data/asr readable */ +#define SBIC_ASR_CIP 0x10 /* Busy, cmd unavail also */ +#define SBIC_ASR_xxx 0x0c +#define SBIC_ASR_PE 0x02 /* Parity error (even) */ +#define SBIC_ASR_DBR 0x01 /* Data Buffer Ready */ + +/* + * My ID register, and/or CDB Size + */ + +#define SBIC_ID_FS_8_10 0x00 /* Input clock is 8-10 Mhz */ + /* 11 Mhz is invalid */ +#define SBIC_ID_FS_12_15 0x40 /* Input clock is 12-15 Mhz */ +#define SBIC_ID_FS_16_20 0x80 /* Input clock is 16-20 Mhz */ +#define SBIC_ID_EHP 0x10 /* Enable host parity */ +#define SBIC_ID_EAF 0x08 /* Enable Advanced Features */ +#define SBIC_ID_MASK 0x07 +#define SBIC_ID_CBDSIZE_MASK 0x0f /* if unk SCSI cmd group */ + +/* + * Control register + */ + +#define SBIC_CTL_DMA 0x80 /* Single byte dma */ +#define SBIC_CTL_DBA_DMA 0x40 /* direct buffer acces (bus master)*/ +#define SBIC_CTL_BURST_DMA 0x20 /* continuous mode (8237) */ +#define SBIC_CTL_NO_DMA 0x00 /* Programmed I/O */ +#define SBIC_CTL_HHP 0x10 /* Halt on host parity error */ +#define SBIC_CTL_EDI 0x08 /* Ending disconnect interrupt */ +#define SBIC_CTL_IDI 0x04 /* Intermediate disconnect interrupt*/ +#define SBIC_CTL_HA 0x02 /* Halt on ATN */ +#define SBIC_CTL_HSP 0x01 /* Halt on SCSI parity error */ + +/* + * Timeout period register + * [val in msecs, input clk in 0.1 Mhz] + */ + +#define SBIC_TIMEOUT(val,clk) ((((val) * (clk)) / 800) + 1) + +/* + * CDBn registers, note that + * cdb11 is used for status byte in target mode (send-status-and-cc) + * cdb12 sez if linked command complete, and w/flag if so + */ + +/* + * Target LUN register + * [holds target status when select-and-xfer] + */ + +#define SBIC_TLUN_VALID 0x80 /* did we receive an Identify msg */ +#define SBIC_TLUN_DOK 0x40 /* Disconnect OK */ +#define SBIC_TLUN_xxx 0x38 +#define SBIC_TLUN_MASK 0x07 + +/* + * Command Phase register + */ + +#define SBIC_CPH_MASK 0x7f /* values/restarts are cmd specific */ +#define SBIC_CPH(p) ((p) & SBIC_CPH_MASK) + +/* + * FIFO register + */ + +#define SBIC_FIFO_DEEP 12 + +/* + * maximum possible size in TC registers. Since this is 24 bit, it's easy + */ +#define SBIC_TC_MAX ((1 << 24) - 1) + +/* + * Synchronous xfer register + */ + +#define SBIC_SYN_OFF_MASK 0x0f +#define SBIC_SYN_MAX_OFFSET SBIC_FIFO_DEEP +#define SBIC_SYN_PER_MASK 0x70 +#define SBIC_SYN_MIN_PERIOD 2 /* upto 8, encoded as 0 */ + +#define SBIC_SYN(o,p) \ + (((o) & SBIC_SYN_OFF_MASK) | (((p) << 4) & SBIC_SYN_PER_MASK)) + +/* + * Transfer count register + * optimal access macros depend on addressing + */ + +/* + * Destination ID (selid) register + */ + +#define SBIC_SID_SCC 0x80 /* Select command chaining (tgt) */ +#define SBIC_SID_DPD 0x40 /* Data phase direction (inittor) */ +#define SBIC_SID_FROM_SCSI 0x40 +#define SBIC_SID_TO_SCSI 0x00 +#define SBIC_SID_xxx 0x38 +#define SBIC_SID_IDMASK 0x07 + +/* + * Source ID (rselid) register + */ + +#define SBIC_RID_ER 0x80 /* Enable reselection */ +#define SBIC_RID_ES 0x40 /* Enable selection */ +#define SBIC_RID_DSP 0x20 /* Disable select parity */ +#define SBIC_RID_SIV 0x08 /* Source ID valid */ +#define SBIC_RID_MASK 0x07 + +/* + * Status register + */ + +#define SBIC_CSR_CAUSE 0xf0 +#define SBIC_CSR_RESET 0x00 /* chip was reset */ +#define SBIC_CSR_CMD_DONE 0x10 /* cmd completed */ +#define SBIC_CSR_CMD_STOPPED 0x20 /* interrupted or abrted*/ +#define SBIC_CSR_CMD_ERR 0x40 /* end with error */ +#define SBIC_CSR_BUS_SERVICE 0x80 /* REQ pending on the bus */ + + +#define SBIC_CSR_QUALIFIER 0x0f +/* Reset State Interrupts */ +#define SBIC_CSR_RESET 0x00 /* reset w/advanced features*/ +#define SBIC_CSR_RESET_AM 0x01 /* reset w/advanced features*/ +/* Successful Completion Interrupts */ +#define SBIC_CSR_TARGET 0x10 /* reselect complete */ +#define SBIC_CSR_INITIATOR 0x11 /* select complete */ +#define SBIC_CSR_WO_ATN 0x13 /* tgt mode completion */ +#define SBIC_CSR_W_ATN 0x14 /* ditto */ +#define SBIC_CSR_XLATED 0x15 /* translate address cmd */ +#define SBIC_CSR_S_XFERRED 0x16 /* initiator mode completion*/ +#define SBIC_CSR_XFERRED 0x18 /* phase in low bits */ +/* Paused or Aborted Interrupts */ +#define SBIC_CSR_MSGIN_W_ACK 0x20 /* (I) msgin, ACK asserted*/ +#define SBIC_CSR_SDP 0x21 /* (I) SDP msg received */ +#define SBIC_CSR_SEL_ABRT 0x22 /* sel/resel aborted */ +#define SBIC_CSR_XFR_PAUSED 0x23 /* (T) no ATN */ +#define SBIC_CSR_XFR_PAUSED_ATN 0x24 /* (T) ATN is asserted */ +#define SBIC_CSR_RSLT_AM 0x27 /* (I) lost selection (AM) */ +#define SBIC_CSR_MIS 0x28 /* (I) xfer aborted, ph mis */ +/* Terminated Interrupts */ +#define SBIC_CSR_CMD_INVALID 0x40 +#define SBIC_CSR_DISC 0x41 /* (I) tgt disconnected */ +#define SBIC_CSR_SEL_TIMEO 0x42 +#define SBIC_CSR_PE 0x43 /* parity error */ +#define SBIC_CSR_PE_ATN 0x44 /* ditto, ATN is asserted */ +#define SBIC_CSR_XLATE_TOOBIG 0x45 +#define SBIC_CSR_RSLT_NOAM 0x46 /* (I) lost sel, no AM mode */ +#define SBIC_CSR_BAD_STATUS 0x47 /* status byte was nok */ +#define SBIC_CSR_MIS_1 0x48 /* ph mis, see low bits */ +/* Service Required Interrupts */ +#define SBIC_CSR_RSLT_NI 0x80 /* reselected, no ify msg */ +#define SBIC_CSR_RSLT_IFY 0x81 /* ditto, AM mode, got ify */ +#define SBIC_CSR_SLT 0x82 /* selected, no ATN */ +#define SBIC_CSR_SLT_ATN 0x83 /* selected with ATN */ +#define SBIC_CSR_ATN 0x84 /* (T) ATN asserted */ +#define SBIC_CSR_DISC_1 0x85 /* (I) bus is free */ +#define SBIC_CSR_UNK_GROUP 0x87 /* strange CDB1 */ +#define SBIC_CSR_MIS_2 0x88 /* (I) ph mis, see low bits */ + +#define SBIC_PHASE(csr) SCSI_PHASE(csr) + +/* + * Command register (command codes) + */ + +#define SBIC_CMD_SBT 0x80 /* Single byte xfer qualifier */ +#define SBIC_CMD_MASK 0x7f + + /* Miscellaneous */ +#define SBIC_CMD_RESET 0x00 /* (DTI) lev I */ +#define SBIC_CMD_ABORT 0x01 /* (DTI) lev I */ +#define SBIC_CMD_DISC 0x04 /* ( TI) lev I */ +#define SBIC_CMD_SSCC 0x0d /* ( TI) lev I */ +#define SBIC_CMD_SET_IDI 0x0f /* (DTI) lev I */ +#define SBIC_CMD_XLATE 0x18 /* (DT ) lev II */ + + /* Initiator state */ +#define SBIC_CMD_SET_ATN 0x02 /* ( I) lev I */ +#define SBIC_CMD_CLR_ACK 0x03 /* ( I) lev I */ +#define SBIC_CMD_XFER_PAD 0x19 /* ( I) lev II */ +#define SBIC_CMD_XFER_INFO 0x20 /* ( I) lev II */ + + /* Target state */ +#define SBIC_CMD_SND_DISC 0x0e /* ( T ) lev II */ +#define SBIC_CMD_RCV_CMD 0x10 /* ( T ) lev II */ +#define SBIC_CMD_RCV_DATA 0x11 /* ( T ) lev II */ +#define SBIC_CMD_RCV_MSG_OUT 0x12 /* ( T ) lev II */ +#define SBIC_CMD_RCV 0x13 /* ( T ) lev II */ +#define SBIC_CMD_SND_STATUS 0x14 /* ( T ) lev II */ +#define SBIC_CMD_SND_DATA 0x15 /* ( T ) lev II */ +#define SBIC_CMD_SND_MSG_IN 0x16 /* ( T ) lev II */ +#define SBIC_CMD_SND 0x17 /* ( T ) lev II */ + + /* Disconnected state */ +#define SBIC_CMD_RESELECT 0x05 /* (D ) lev II */ +#define SBIC_CMD_SEL_ATN 0x06 /* (D ) lev II */ +#define SBIC_CMD_SEL 0x07 /* (D ) lev II */ +#define SBIC_CMD_SEL_ATN_XFER 0x08 /* (D I) lev II */ +#define SBIC_CMD_SEL_XFER 0x09 /* (D I) lev II */ +#define SBIC_CMD_RESELECT_RECV 0x0a /* (DT ) lev II */ +#define SBIC_CMD_RESELECT_SEND 0x0b /* (DT ) lev II */ +#define SBIC_CMD_WAIT_SEL_RECV 0x0c /* (DT ) lev II */ + +/* approximate, but we won't do SBT on selects */ +#define sbic_isa_select(cmd) (((cmd) > 0x5) && ((cmd) < 0xa)) + +#define PAD(n) char n; +#define SBIC_MACHINE_DMA_MODE SBIC_CTL_DMA + +typedef struct { + volatile unsigned char sbic_asr; /* r : Aux Status Register */ +#define sbic_address sbic_asr /* w : desired register no */ + /* PAD(pad1); */ + volatile unsigned char sbic_value; /* rw: register value */ +} sbic_padded_ind_regmap_t; +typedef volatile sbic_padded_ind_regmap_t *sbic_regmap_p; + +#define sbic_read_reg(regs,regno,val) do { \ + (regs)->sbic_address = (regno); \ + (val) = (regs)->sbic_value; \ + } while (0) + +#define sbic_write_reg(regs,regno,val) do { \ + (regs)->sbic_address = (regno); \ + (regs)->sbic_value = (val); \ + } while (0) + +#define SET_SBIC_myid(regs,val) sbic_write_reg(regs,SBIC_myid,val) +#define GET_SBIC_myid(regs,val) sbic_read_reg(regs,SBIC_myid,val) +#define SET_SBIC_cdbsize(regs,val) sbic_write_reg(regs,SBIC_cdbsize,val) +#define GET_SBIC_cdbsize(regs,val) sbic_read_reg(regs,SBIC_cdbsize,val) +#define SET_SBIC_control(regs,val) sbic_write_reg(regs,SBIC_control,val) +#define GET_SBIC_control(regs,val) sbic_read_reg(regs,SBIC_control,val) +#define SET_SBIC_timeo(regs,val) sbic_write_reg(regs,SBIC_timeo,val) +#define GET_SBIC_timeo(regs,val) sbic_read_reg(regs,SBIC_timeo,val) +#define SET_SBIC_cdb1(regs,val) sbic_write_reg(regs,SBIC_cdb1,val) +#define GET_SBIC_cdb1(regs,val) sbic_read_reg(regs,SBIC_cdb1,val) +#define SET_SBIC_cdb2(regs,val) sbic_write_reg(regs,SBIC_cdb2,val) +#define GET_SBIC_cdb2(regs,val) sbic_read_reg(regs,SBIC_cdb2,val) +#define SET_SBIC_cdb3(regs,val) sbic_write_reg(regs,SBIC_cdb3,val) +#define GET_SBIC_cdb3(regs,val) sbic_read_reg(regs,SBIC_cdb3,val) +#define SET_SBIC_cdb4(regs,val) sbic_write_reg(regs,SBIC_cdb4,val) +#define GET_SBIC_cdb4(regs,val) sbic_read_reg(regs,SBIC_cdb4,val) +#define SET_SBIC_cdb5(regs,val) sbic_write_reg(regs,SBIC_cdb5,val) +#define GET_SBIC_cdb5(regs,val) sbic_read_reg(regs,SBIC_cdb5,val) +#define SET_SBIC_cdb6(regs,val) sbic_write_reg(regs,SBIC_cdb6,val) +#define GET_SBIC_cdb6(regs,val) sbic_read_reg(regs,SBIC_cdb6,val) +#define SET_SBIC_cdb7(regs,val) sbic_write_reg(regs,SBIC_cdb7,val) +#define GET_SBIC_cdb7(regs,val) sbic_read_reg(regs,SBIC_cdb7,val) +#define SET_SBIC_cdb8(regs,val) sbic_write_reg(regs,SBIC_cdb8,val) +#define GET_SBIC_cdb8(regs,val) sbic_read_reg(regs,SBIC_cdb8,val) +#define SET_SBIC_cdb9(regs,val) sbic_write_reg(regs,SBIC_cdb9,val) +#define GET_SBIC_cdb9(regs,val) sbic_read_reg(regs,SBIC_cdb9,val) +#define SET_SBIC_cdb10(regs,val) sbic_write_reg(regs,SBIC_cdb10,val) +#define GET_SBIC_cdb10(regs,val) sbic_read_reg(regs,SBIC_cdb10,val) +#define SET_SBIC_cdb11(regs,val) sbic_write_reg(regs,SBIC_cdb11,val) +#define GET_SBIC_cdb11(regs,val) sbic_read_reg(regs,SBIC_cdb11,val) +#define SET_SBIC_cdb12(regs,val) sbic_write_reg(regs,SBIC_cdb12,val) +#define GET_SBIC_cdb12(regs,val) sbic_read_reg(regs,SBIC_cdb12,val) +#define SET_SBIC_tlun(regs,val) sbic_write_reg(regs,SBIC_tlun,val) +#define GET_SBIC_tlun(regs,val) sbic_read_reg(regs,SBIC_tlun,val) +#define SET_SBIC_cmd_phase(regs,val) sbic_write_reg(regs,SBIC_cmd_phase,val) +#define GET_SBIC_cmd_phase(regs,val) sbic_read_reg(regs,SBIC_cmd_phase,val) +#define SET_SBIC_syn(regs,val) sbic_write_reg(regs,SBIC_syn,val) +#define GET_SBIC_syn(regs,val) sbic_read_reg(regs,SBIC_syn,val) +#define SET_SBIC_count_hi(regs,val) sbic_write_reg(regs,SBIC_count_hi,val) +#define GET_SBIC_count_hi(regs,val) sbic_read_reg(regs,SBIC_count_hi,val) +#define SET_SBIC_count_med(regs,val) sbic_write_reg(regs,SBIC_count_med,val) +#define GET_SBIC_count_med(regs,val) sbic_read_reg(regs,SBIC_count_med,val) +#define SET_SBIC_count_lo(regs,val) sbic_write_reg(regs,SBIC_count_lo,val) +#define GET_SBIC_count_lo(regs,val) sbic_read_reg(regs,SBIC_count_lo,val) +#define SET_SBIC_selid(regs,val) sbic_write_reg(regs,SBIC_selid,val) +#define GET_SBIC_selid(regs,val) sbic_read_reg(regs,SBIC_selid,val) +#define SET_SBIC_rselid(regs,val) sbic_write_reg(regs,SBIC_rselid,val) +#define GET_SBIC_rselid(regs,val) sbic_read_reg(regs,SBIC_rselid,val) +#define SET_SBIC_csr(regs,val) sbic_write_reg(regs,SBIC_csr,val) +#define GET_SBIC_csr(regs,val) sbic_read_reg(regs,SBIC_csr,val) +#define SET_SBIC_cmd(regs,val) sbic_write_reg(regs,SBIC_cmd,val) +#define GET_SBIC_cmd(regs,val) sbic_read_reg(regs,SBIC_cmd,val) +#define SET_SBIC_data(regs,val) sbic_write_reg(regs,SBIC_data,val) +#define GET_SBIC_data(regs,val) sbic_read_reg(regs,SBIC_data,val) + +#define SBIC_TC_PUT(regs,val) do { \ + sbic_write_reg(regs,SBIC_count_hi,((val)>>16)); \ + (regs)->sbic_value = (val)>>8; \ + (regs)->sbic_value = (val); \ +} while (0) +#define SBIC_TC_GET(regs,val) do { \ + sbic_read_reg(regs,SBIC_count_hi,(val)); \ + (val) = ((val)<<8) | (regs)->sbic_value; \ + (val) = ((val)<<8) | (regs)->sbic_value; \ +} while (0) + +#define SBIC_LOAD_COMMAND(regs,cmd,cmdsize) do { \ + int n=(cmdsize)-1; \ + char *ptr = (char*)(cmd); \ + sbic_write_reg(regs,SBIC_cdb1,*ptr++); \ + while (n-- > 0) (regs)->sbic_value = *ptr++; \ +} while (0) + +#define GET_SBIC_asr(regs,val) (val) = (regs)->sbic_asr + +#define WAIT_CIP(regs) do { \ + while ((regs)->sbic_asr & SBIC_ASR_CIP) \ + ; \ +} while (0) + +/* transmit a byte in programmed I/O mode */ +#define SEND_BYTE(regs, ch) do { \ + WAIT_CIP(regs); \ + SET_SBIC_cmd(regs, SBIC_CMD_SBT | SBIC_CMD_XFER_INFO); \ + SBIC_WAIT(regs, SBIC_ASR_DBR, 0); \ + SET_SBIC_data(regs, ch); \ + } while (0) + +/* receive a byte in programmed I/O mode */ +#define RECV_BYTE(regs, ch) do { \ + WAIT_CIP(regs); \ + SET_SBIC_cmd(regs, SBIC_CMD_SBT | SBIC_CMD_XFER_INFO); \ + SBIC_WAIT(regs, SBIC_ASR_DBR, 0); \ + GET_SBIC_data(regs, ch); \ + } while (0) diff --git a/sys/arch/mvme68k/dev/sbicvar.h b/sys/arch/mvme68k/dev/sbicvar.h new file mode 100644 index 00000000000..339d5f73c39 --- /dev/null +++ b/sys/arch/mvme68k/dev/sbicvar.h @@ -0,0 +1,230 @@ +/* $NetBSD: sbicvar.h,v 1.8 1995/08/18 15:28:05 chopps Exp $ */ + +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * 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. + * + * @(#)scsivar.h 7.1 (Berkeley) 5/8/90 + */ +#ifndef _SBICVAR_H_ +#define _SBICVAR_H_ +#include <sys/malloc.h> + +/* + * The largest single request will be MAXPHYS bytes which will require + * at most MAXPHYS/NBPG+1 chain elements to describe, i.e. if none of + * the buffer pages are physically contiguous (MAXPHYS/NBPG) and the + * buffer is not page aligned (+1). + */ +#define DMAMAXIO (MAXPHYS/NBPG+1) + +struct dma_chain { + int dc_count; + char *dc_addr; +}; + +/* + * ACB. Holds additional information for each SCSI command Comments: We + * need a separate scsi command block because we may need to overwrite it + * with a request sense command. Basicly, we refrain from fiddling with + * the scsi_xfer struct (except do the expected updating of return values). + * We'll generally update: xs->{flags,resid,error,sense,status} and + * occasionally xs->retries. + */ +struct sbic_acb { + TAILQ_ENTRY(sbic_acb) chain; + struct scsi_xfer *xs; /* SCSI xfer ctrl block from above */ + int flags; /* Status */ +#define ACB_FREE 0x00 +#define ACB_ACTIVE 0x01 +#define ACB_DONE 0x04 +#define ACB_CHKSENSE 0x08 +#define ACB_BBUF 0x10 /* DMA input needs to be copied from bounce */ +#define ACB_DATAIN 0x20 /* DMA direction flag */ + struct scsi_generic cmd; /* SCSI command block */ + int clen; + struct dma_chain sc_kv; /* Virtual address of whole DMA */ + struct dma_chain sc_pa; /* Physical address of DMA segment */ + u_long sc_tcnt; /* number of bytes for this DMA */ + u_char *sc_dmausrbuf; /* user buffer kva - for bounce copy */ + u_long sc_dmausrlen; /* length of bounce copy */ + u_short sc_dmacmd; /* Internal data for this DMA */ + char *pa_addr; /* XXXX initial phys addr */ + u_char *sc_usrbufpa; /* user buffer phys addr */ +}; + +/* + * Some info about each (possible) target on the SCSI bus. This should + * probably have been a "per target+lunit" structure, but we'll leave it at + * this for now. Is there a way to reliably hook it up to sc->fordriver?? + */ +struct sbic_tinfo { + int cmds; /* #commands processed */ + int dconns; /* #disconnects */ + int touts; /* #timeouts */ + int perrs; /* #parity errors */ + int senses; /* #request sense commands sent */ + u_char* bounce; /* Bounce buffer for this device */ + ushort lubusy; /* What local units/subr. are busy? */ + u_char flags; + u_char period; /* Period suggestion */ + u_char offset; /* Offset suggestion */ +} tinfo_t; + +struct sbic_softc { + struct device sc_dev; + struct intrhand sc_ih, sc_dmaih; + struct evcnt sc_intrcnt, sc_dmaintrcnt; + struct target_sync { + u_char state; + u_char period; + u_char offset; + } sc_sync[8]; + u_char target; /* Currently active target */ + u_char lun; + struct scsi_link sc_link; /* proto for sub devices */ + sbic_regmap_p sc_sbicp; /* the SBIC */ + volatile void *sc_cregs; /* driver specific regs */ + + /* Lists of command blocks */ + TAILQ_HEAD(acb_list, sbic_acb) free_list, + ready_list, + nexus_list; + + struct sbic_acb *sc_nexus; /* current command */ + struct sbic_acb sc_acb[8]; /* the real command blocks */ + struct sbic_tinfo sc_tinfo[8]; + + struct scsi_xfer *sc_xs; /* transfer from high level code */ + u_char sc_flags; + u_char sc_scsiaddr; + u_char sc_stat[2]; + u_char sc_msg[7]; + u_long sc_clkfreq; + u_long sc_tcnt; /* number of bytes transfered */ + u_short sc_dmacmd; /* used by dma drivers */ + u_short sc_dmatimo; /* dma timeout */ + u_long sc_dmamask; /* dma valid mem mask */ + struct dma_chain *sc_cur; + struct dma_chain *sc_last; + int (*sc_dmago) __P((struct sbic_softc *, char *, int, int)); + int (*sc_dmanext) __P((struct sbic_softc *)); + void (*sc_dmafree) __P((struct sbic_softc *)); + void (*sc_dmastop) __P((struct sbic_softc *)); + u_short gtsc_bankmask; /* GVP specific bank selected */ +}; + +/* sc_flags */ +#define SBICF_ALIVE 0x01 /* controller initialized */ +#define SBICF_DCFLUSH 0x02 /* need flush for overlap after dma finishes */ +#define SBICF_SELECTED 0x04 /* bus is in selected state. */ +#define SBICF_ICMD 0x08 /* Immediate command in execution */ +#define SBICF_BADDMA 0x10 /* controller can only DMA to ztwobus space */ +#define SBICF_INTR 0x40 /* SBICF interrupt expected */ +#define SBICF_INDMA 0x80 /* not used yet, DMA I/O in progress */ + +/* sync states */ +#define SYNC_START 0 /* no sync handshake started */ +#define SYNC_SENT 1 /* we sent sync request, no answer yet */ +#define SYNC_DONE 2 /* target accepted our (or inferior) settings, + or it rejected the request and we stay async */ +#ifdef DEBUG +#define DDB_FOLLOW 0x04 +#define DDB_IO 0x08 +#endif +extern int sbic_inhibit_sync; +extern int sbic_no_dma; +extern int sbic_clock_override; + +#define PHASE 0x07 /* mask for psns/pctl phase */ +#define DATA_OUT_PHASE 0x00 +#define DATA_IN_PHASE 0x01 +#define CMD_PHASE 0x02 +#define STATUS_PHASE 0x03 +#define BUS_FREE_PHASE 0x04 +#define ARB_SEL_PHASE 0x05 /* Fuji chip combines arbitration with sel. */ +#define MESG_OUT_PHASE 0x06 +#define MESG_IN_PHASE 0x07 + +#define MSG_CMD_COMPLETE 0x00 +#define MSG_EXT_MESSAGE 0x01 +#define MSG_SAVE_DATA_PTR 0x02 +#define MSG_RESTORE_PTR 0x03 +#define MSG_DISCONNECT 0x04 +#define MSG_INIT_DETECT_ERROR 0x05 +#define MSG_ABORT 0x06 +#define MSG_REJECT 0x07 +#define MSG_NOOP 0x08 +#define MSG_PARITY_ERROR 0x09 +#define MSG_BUS_DEVICE_RESET 0x0C +#define MSG_IDENTIFY 0x80 +#define MSG_IDENTIFY_DR 0xc0 /* (disconnect/reconnect allowed) */ +#define MSG_SYNC_REQ 0x01 + +#define MSG_ISIDENTIFY(x) (x&MSG_IDENTIFY) +#define IFY_TRN 0x20 +#define IFY_LUNTRN(x) (x&0x07) +#define IFY_LUN(x) (!(x&0x20)) + +/* Check if high bit set */ + +#define STS_CHECKCOND 0x02 /* Check Condition (ie., read sense) */ +#define STS_CONDMET 0x04 /* Condition Met (ie., search worked) */ +#define STS_BUSY 0x08 +#define STS_INTERMED 0x10 /* Intermediate status sent */ +#define STS_EXT 0x80 /* Extended status valid */ + + +/* States returned by our state machine */ + +#define SBIC_STATE_ERROR -1 +#define SBIC_STATE_DONE 0 +#define SBIC_STATE_RUNNING 1 +#define SBIC_STATE_DISCONNECT 2 + +/* + * XXXX + */ +struct scsi_fmt_cdb { + int len; /* cdb length (in bytes) */ + u_char cdb[28]; /* cdb to use on next read/write */ +}; + +struct buf; +struct scsi_xfer; + +void sbic_minphys __P((struct buf *bp)); +int sbic_scsicmd __P((struct scsi_xfer *)); + +#endif /* _SBICVAR_H_ */ diff --git a/sys/arch/mvme68k/dev/siop.c b/sys/arch/mvme68k/dev/siop.c new file mode 100644 index 00000000000..be0b8b32d9f --- /dev/null +++ b/sys/arch/mvme68k/dev/siop.c @@ -0,0 +1,1523 @@ +/* $NetBSD: siop.c,v 1.23 1995/08/18 15:28:08 chopps Exp $ */ + +/* + * Copyright (c) 1994 Michael L. Hitch + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * 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. + * + * @(#)siop.c 7.5 (Berkeley) 5/4/91 + */ + +/* + * 53C710 scsi adaptor driver + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/disklabel.h> +#include <sys/dkstat.h> +#include <sys/buf.h> +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <machine/autoconf.h> +#include <mvme68k/dev/siopreg.h> +#include <mvme68k/dev/siopvar.h> + +extern u_int kvtop(); + +/* + * SCSI delays + * In u-seconds, primarily for state changes on the SPC. + */ +#define SCSI_CMD_WAIT 500000 /* wait per step of 'immediate' cmds */ +#define SCSI_DATA_WAIT 500000 /* wait per data in/out step */ +#define SCSI_INIT_WAIT 500000 /* wait per step (both) during init */ + +void siop_select __P((struct siop_softc *)); +void siopabort __P((struct siop_softc *, siop_regmap_p, char *)); +void sioperror __P((struct siop_softc *, siop_regmap_p, u_char)); +void siopstart __P((struct siop_softc *)); +void siopreset __P((struct siop_softc *)); +void siopsetdelay __P((int)); +void siop_scsidone __P((struct siop_acb *, int)); +void siop_sched __P((struct siop_softc *)); +int siop_poll __P((struct siop_softc *, struct siop_acb *)); +int siopintr __P((struct siop_softc *)); + +/* 53C710 script */ +const +#include <mvme68k/dev/siop_script.out> + +/* default to not inhibit sync negotiation on any drive */ +u_char siop_inhibit_sync[8] = { 0, 0, 0, 0, 0, 0, 0 }; /* initialize, so patchable */ +u_char siop_allow_disc[8] = { 3, 3, 3, 3, 3, 3, 3, 3 }; +int siop_no_dma = 0; + +int siop_reset_delay = 250; /* delay after reset, in milleseconds */ + +int siop_cmd_wait = SCSI_CMD_WAIT; +int siop_data_wait = SCSI_DATA_WAIT; +int siop_init_wait = SCSI_INIT_WAIT; + +#ifdef DEBUG +/* + * 0x01 - full debug + * 0x02 - DMA chaining + * 0x04 - siopintr + * 0x08 - phase mismatch + * 0x10 - <not used> + * 0x20 - panic on unhandled exceptions + * 0x100 - disconnect/reselect + */ +int siop_debug = 0; +int siopsync_debug = 0; +int siopdma_hits = 0; +int siopdma_misses = 0; +int siopchain_ints = 0; +int siopstarts = 0; +int siopints = 0; +int siopphmm = 0; +#define SIOP_TRACE_SIZE 128 +#define SIOP_TRACE(a,b,c,d) \ + siop_trbuf[siop_trix] = (a); \ + siop_trbuf[siop_trix+1] = (b); \ + siop_trbuf[siop_trix+2] = (c); \ + siop_trbuf[siop_trix+3] = (d); \ + siop_trix = (siop_trix + 4) & (SIOP_TRACE_SIZE - 1); +u_char siop_trbuf[SIOP_TRACE_SIZE]; +int siop_trix; +#else +#define SIOP_TRACE(a,b,c,d) +#endif + + +/* + * default minphys routine for siop based controllers + */ +void +siop_minphys(bp) + struct buf *bp; +{ + + /* + * No max transfer at this level. + */ + minphys(bp); +} + +/* + * used by specific siop controller + * + */ +int +siop_scsicmd(xs) + struct scsi_xfer *xs; +{ + struct siop_acb *acb; + struct siop_softc *sc; + struct scsi_link *slp; + int flags, s, i; + + slp = xs->sc_link; + sc = slp->adapter_softc; + flags = xs->flags; + + /* XXXX ?? */ + if (flags & SCSI_DATA_UIO) + panic("siop: scsi data uio requested"); + + /* XXXX ?? */ + if (sc->sc_nexus && flags & SCSI_POLL) + panic("siop_scsicmd: busy"); + + s = splbio(); + acb = sc->free_list.tqh_first; + if (acb) { + TAILQ_REMOVE(&sc->free_list, acb, chain); + } + splx(s); + + if (acb == NULL) { + xs->error = XS_DRIVER_STUFFUP; + return(TRY_AGAIN_LATER); + } + + acb->flags = ACB_ACTIVE; + acb->xs = xs; + bcopy(xs->cmd, &acb->cmd, xs->cmdlen); + acb->clen = xs->cmdlen; + acb->daddr = xs->data; + acb->dleft = xs->datalen; + + s = splbio(); + TAILQ_INSERT_TAIL(&sc->ready_list, acb, chain); + + if (sc->sc_nexus == NULL) + siop_sched(sc); + + splx(s); + + if (flags & SCSI_POLL || siop_no_dma) + return(siop_poll(sc, acb)); + return(SUCCESSFULLY_QUEUED); +} + +int +siop_poll(sc, acb) + struct siop_softc *sc; + struct siop_acb *acb; +{ + siop_regmap_p rp = sc->sc_siopp; + struct scsi_xfer *xs = acb->xs; + int i; + int status; + u_char istat; + u_char dstat; + u_char sstat0; + int s; + int to; + + s = splbio(); + to = xs->timeout / 1000; + if (sc->nexus_list.tqh_first) + printf("%s: siop_poll called with disconnected device\n", + sc->sc_dev.dv_xname); + for (;;) { + /* use cmd_wait values? */ + i = 50000; + spl0(); + while (((istat = rp->siop_istat) & + (SIOP_ISTAT_SIP | SIOP_ISTAT_DIP)) == 0) { + if (--i <= 0) { +#ifdef DEBUG + printf ("waiting: tgt %d cmd %02x sbcl %02x dsp %x (+%x) dcmd %x ds %x timeout %d\n", + xs->sc_link->target, acb->cmd.opcode, + rp->siop_sbcl, rp->siop_dsp, + rp->siop_dsp - sc->sc_scriptspa, + *((long *)&rp->siop_dcmd), &acb->ds, acb->xs->timeout); +#endif + i = 50000; + --to; + if (to <= 0) { + siopreset(sc); + return(COMPLETE); + } + } + delay(10); + } + sstat0 = rp->siop_sstat0; + dstat = rp->siop_dstat; + if (siop_checkintr(sc, istat, dstat, sstat0, &status)) { + if (acb != sc->sc_nexus) + printf("%s: siop_poll disconnected device completed\n", + sc->sc_dev.dv_xname); + else if ((sc->sc_flags & SIOP_INTDEFER) == 0) { + sc->sc_flags &= ~SIOP_INTSOFF; + rp->siop_sien = sc->sc_sien; + rp->siop_dien = sc->sc_dien; + } + siop_scsidone(sc->sc_nexus, status); + } + if (xs->flags & ITSDONE) + break; + } + splx(s); + return (COMPLETE); +} + +/* + * start next command that's ready + */ +void +siop_sched(sc) + struct siop_softc *sc; +{ + struct scsi_link *slp; + struct siop_acb *acb; + int stat, i; + +#ifdef DEBUG + if (sc->sc_nexus) { + printf("%s: siop_sched- nexus %x/%d ready %x/%d\n", + sc->sc_dev.dv_xname, sc->sc_nexus, + sc->sc_nexus->xs->sc_link->target, + sc->ready_list.tqh_first, + sc->ready_list.tqh_first->xs->sc_link->target); + return; + } +#endif + for (acb = sc->ready_list.tqh_first; acb; acb = acb->chain.tqe_next) { + slp = acb->xs->sc_link; + i = slp->target; + if(!(sc->sc_tinfo[i].lubusy & (1 << slp->lun))) { + struct siop_tinfo *ti = &sc->sc_tinfo[i]; + + TAILQ_REMOVE(&sc->ready_list, acb, chain); + sc->sc_nexus = acb; + slp = acb->xs->sc_link; + ti = &sc->sc_tinfo[slp->target]; + ti->lubusy |= (1 << slp->lun); + break; + } + } + + if (acb == NULL) { +#ifdef DEBUGXXX + printf("%s: siop_sched didn't find ready command\n", + sc->sc_dev.dv_xname); +#endif + return; + } + + if (acb->xs->flags & SCSI_RESET) + siopreset(sc); + +#if 0 + acb->cmd.bytes[0] |= slp->lun << 5; /* XXXX */ +#endif + ++sc->sc_active; + siop_select(sc); +} + +void +siop_scsidone(acb, stat) + struct siop_acb *acb; + int stat; +{ + struct scsi_xfer *xs = acb->xs; + struct scsi_link *slp = xs->sc_link; + struct siop_softc *sc = slp->adapter_softc; + int s, dosched = 0; + +#ifdef DIAGNOSTIC + if (acb == NULL || xs == NULL) + panic("siop_scsidone"); +#endif +#if 0 + if (((struct device *)(slp->device_softc))->dv_unit < dk_ndrive) + ++dk_xfer[((struct device *)(slp->device_softc))->dv_unit]; +#endif + /* + * is this right? + */ + xs->status = stat; + + if (xs->error == XS_NOERROR && !(acb->flags & ACB_CHKSENSE)) { + if (stat == SCSI_CHECK) { + struct scsi_sense *ss = (void *)&acb->cmd; + bzero(ss, sizeof(*ss)); + ss->opcode = REQUEST_SENSE; + ss->byte2 = slp->lun << 5; + ss->length = sizeof(struct scsi_sense_data); + acb->clen = sizeof(*ss); + acb->daddr = (char *)&xs->sense; + acb->dleft = sizeof(struct scsi_sense_data); + acb->flags = ACB_ACTIVE | ACB_CHKSENSE; + TAILQ_INSERT_HEAD(&sc->ready_list, acb, chain); + --sc->sc_active; + sc->sc_tinfo[slp->target].lubusy &= + ~(1 << slp->lun); + sc->sc_tinfo[slp->target].senses++; + if (sc->sc_nexus == acb) { + sc->sc_nexus = NULL; + siop_sched(sc); + } + SIOP_TRACE('d','s',0,0) + return; + } + } + if (xs->error == XS_NOERROR && (acb->flags & ACB_CHKSENSE)) { + xs->error = XS_SENSE; + } else { + xs->resid = 0; /* XXXX */ + } +#if whataboutthisone + case SCSI_BUSY: + xs->error = XS_BUSY; + break; +#endif + xs->flags |= ITSDONE; + + /* + * Remove the ACB from whatever queue it's on. We have to do a bit of + * a hack to figure out which queue it's on. Note that it is *not* + * necessary to cdr down the ready queue, but we must cdr down the + * nexus queue and see if it's there, so we can mark the unit as no + * longer busy. This code is sickening, but it works. + */ + if (acb == sc->sc_nexus) { + sc->sc_nexus = NULL; + sc->sc_tinfo[slp->target].lubusy &= ~(1<<slp->lun); + if (sc->ready_list.tqh_first) + dosched = 1; /* start next command */ + --sc->sc_active; + SIOP_TRACE('d','a',stat,0) + } else if (sc->ready_list.tqh_last == &acb->chain.tqe_next) { + TAILQ_REMOVE(&sc->ready_list, acb, chain); + SIOP_TRACE('d','r',stat,0) + } else { + register struct siop_acb *acb2; + for (acb2 = sc->nexus_list.tqh_first; acb2; + acb2 = acb2->chain.tqe_next) + if (acb2 == acb) { + TAILQ_REMOVE(&sc->nexus_list, acb, chain); + sc->sc_tinfo[slp->target].lubusy + &= ~(1<<slp->lun); + --sc->sc_active; + break; + } + if (acb2) + ; + else if (acb->chain.tqe_next) { + TAILQ_REMOVE(&sc->ready_list, acb, chain); + --sc->sc_active; + } else { + printf("%s: can't find matching acb\n", + sc->sc_dev.dv_xname); +#ifdef DDB +/* Debugger(); */ +#endif + } + SIOP_TRACE('d','n',stat,0); + } + /* Put it on the free list. */ + acb->flags = ACB_FREE; + TAILQ_INSERT_HEAD(&sc->free_list, acb, chain); + + sc->sc_tinfo[slp->target].cmds++; + + scsi_done(xs); + + if (dosched && sc->sc_nexus == NULL) + siop_sched(sc); +} + +void +siopabort(sc, rp, where) + register struct siop_softc *sc; + siop_regmap_p rp; + char *where; +{ + int i; + + printf ("%s: abort %s: dstat %02x, sstat0 %02x sbcl %02x\n", + sc->sc_dev.dv_xname, + where, rp->siop_dstat, rp->siop_sstat0, rp->siop_sbcl); + + if (sc->sc_active > 0) { +#ifdef TODO + SET_SBIC_cmd (rp, SBIC_CMD_ABORT); + WAIT_CIP (rp); + + GET_SBIC_asr (rp, asr); + if (asr & (SBIC_ASR_BSY|SBIC_ASR_LCI)) + { + /* ok, get more drastic.. */ + + SET_SBIC_cmd (rp, SBIC_CMD_RESET); + delay(25); + SBIC_WAIT(rp, SBIC_ASR_INT, 0); + GET_SBIC_csr (rp, csr); /* clears interrupt also */ + + return; + } + + do + { + SBIC_WAIT (rp, SBIC_ASR_INT, 0); + GET_SBIC_csr (rp, csr); + } + while ((csr != SBIC_CSR_DISC) && (csr != SBIC_CSR_DISC_1) + && (csr != SBIC_CSR_CMD_INVALID)); +#endif + + /* lets just hope it worked.. */ +#ifdef fix_this + for (i = 0; i < 2; ++i) { + if (sc->sc_iob[i].sc_xs && &sc->sc_iob[i] != + sc->sc_cur) { + printf ("siopabort: cleanup!\n"); + sc->sc_iob[i].sc_xs = NULL; + } + } +#endif /* fix_this */ +/* sc->sc_active = 0; */ + } +} + +void +siopinitialize(sc) + struct siop_softc *sc; +{ + /* + * Need to check that scripts is on a long word boundary + * and that DS is on a long word boundary. + * Also should verify that dev doesn't span non-contiguous + * physical pages. + */ + sc->sc_scriptspa = kvtop(scripts); + sc->sc_tcp[1] = 1000 / sc->sc_clock_freq; + sc->sc_tcp[2] = 1500 / sc->sc_clock_freq; + sc->sc_tcp[3] = 2000 / sc->sc_clock_freq; + sc->sc_minsync = sc->sc_tcp[1]; /* in 4ns units */ + if (sc->sc_minsync < 25) + sc->sc_minsync = 25; +#if not_used + if (sc->sc_clock_freq <= 25) + sc->sc_tcp[0] = sc->sc_tcp[1]; + else if (sc->sc_clock_freq <= 37) + sc->sc_tcp[0] = sc->sc_tcp[2]; + else if (sc->sc_clock_freq <= 50) + sc->sc_tcp[0] = sc->sc_tcp[3]; + else + sc->sc_tcp[0] = 3000 / sc->sc_clock_freq; +#endif + + siopreset (sc); +} + +void +siopreset(sc) + struct siop_softc *sc; +{ + siop_regmap_p rp; + u_int i, s; + u_char dummy; + struct siop_acb *acb; + + rp = sc->sc_siopp; + + if (sc->sc_flags & SIOP_ALIVE) + siopabort(sc, rp, "reset"); + + s = splbio(); + + /* + * Reset the chip + * XXX - is this really needed? + */ + rp->siop_istat |= SIOP_ISTAT_ABRT; /* abort current script */ + rp->siop_istat |= SIOP_ISTAT_RST; /* reset chip */ + rp->siop_istat &= ~SIOP_ISTAT_RST; + /* + * Reset SCSI bus (do we really want this?) + */ + rp->siop_sien = 0; + rp->siop_scntl1 |= SIOP_SCNTL1_RST; + delay(1); + rp->siop_scntl1 &= ~SIOP_SCNTL1_RST; + + /* + * Set up various chip parameters + */ + rp->siop_scntl0 = SIOP_ARB_FULL | SIOP_SCNTL0_EPC | SIOP_SCNTL0_EPG; + rp->siop_scntl1 = SIOP_SCNTL1_ESR; + rp->siop_dcntl = sc->sc_dcntl; + rp->siop_dmode = 0x80; /* burst length = 4 */ + rp->siop_sien = 0x00; /* don't enable interrupts yet */ + rp->siop_dien = 0x00; /* don't enable interrupts yet */ + rp->siop_scid = 1 << sc->sc_link.adapter_target; + rp->siop_dwt = 0x00; + rp->siop_ctest0 |= SIOP_CTEST0_BTD | SIOP_CTEST0_EAN; + rp->siop_ctest7 = sc->sc_ctest7; + + /* will need to re-negotiate sync xfers */ + bzero(&sc->sc_sync, sizeof (sc->sc_sync)); + + i = rp->siop_istat; + if (i & SIOP_ISTAT_SIP) + dummy = rp->siop_sstat0; + if (i & SIOP_ISTAT_DIP) + dummy = rp->siop_dstat; + + splx(s); + + delay(siop_reset_delay * 1000); + printf(": version %d target %d\n", rp->siop_ctest8 >> 4, + sc->sc_link.adapter_target); + + if ((sc->sc_flags & SIOP_ALIVE) == 0) { + TAILQ_INIT(&sc->ready_list); + TAILQ_INIT(&sc->nexus_list); + TAILQ_INIT(&sc->free_list); + sc->sc_nexus = NULL; + acb = sc->sc_acb; + bzero(acb, sizeof(sc->sc_acb)); + for (i = 0; i < sizeof(sc->sc_acb) / sizeof(*acb); i++) { + TAILQ_INSERT_TAIL(&sc->free_list, acb, chain); + acb++; + } + bzero(sc->sc_tinfo, sizeof(sc->sc_tinfo)); + } else { + if (sc->sc_nexus != NULL) { + sc->sc_nexus->xs->error = XS_DRIVER_STUFFUP; + siop_scsidone(sc->sc_nexus, sc->sc_nexus->stat[0]); + } + while (acb = sc->nexus_list.tqh_first) { + acb->xs->error = XS_DRIVER_STUFFUP; + siop_scsidone(acb, acb->stat[0]); + } + } + + sc->sc_flags |= SIOP_ALIVE; + sc->sc_flags &= ~(SIOP_INTDEFER|SIOP_INTSOFF); + /* enable SCSI and DMA interrupts */ + sc->sc_sien = SIOP_SIEN_M_A | SIOP_SIEN_STO | /*SIOP_SIEN_SEL |*/ SIOP_SIEN_SGE | + SIOP_SIEN_UDC | SIOP_SIEN_RST | SIOP_SIEN_PAR; + sc->sc_dien = SIOP_DIEN_BF | SIOP_DIEN_ABRT | SIOP_DIEN_SIR | + /*SIOP_DIEN_WTD |*/ SIOP_DIEN_IID; + rp->siop_sien = sc->sc_sien; + rp->siop_dien = sc->sc_dien; +} + +/* + * Setup Data Storage for 53C710 and start SCRIPTS processing + */ + +void +siop_start (sc, target, lun, cbuf, clen, buf, len) + struct siop_softc *sc; + int target; + int lun; + u_char *cbuf; + int clen; + u_char *buf; + int len; +{ + siop_regmap_p rp = sc->sc_siopp; + int i; + int nchain; + int count, tcount; + char *addr, *dmaend; + struct siop_acb *acb = sc->sc_nexus; + +#ifdef DEBUG + if (siop_debug & 0x100 && rp->siop_sbcl & SIOP_BSY) { + printf ("ACK! siop was busy: rp %x script %x dsa %x active %d\n", + rp, &scripts, &acb->ds, sc->sc_active); + printf ("istat %02x sfbr %02x lcrc %02x sien %02x dien %02x\n", + rp->siop_istat, rp->siop_sfbr, rp->siop_lcrc, + rp->siop_sien, rp->siop_dien); +#ifdef DDB + /*Debugger();*/ +#endif + } +#endif + acb->msgout[0] = MSG_IDENTIFY | lun; + if (siop_allow_disc[target] & 2 || + (siop_allow_disc[target] && len == 0)) + acb->msgout[0] = MSG_IDENTIFY_DR | lun; + acb->status = 0; + acb->stat[0] = -1; + acb->msg[0] = -1; + acb->ds.scsi_addr = (0x10000 << target) | (sc->sc_sync[target].sxfer << 8); + acb->ds.idlen = 1; + acb->ds.idbuf = (char *) kvtop(&acb->msgout[0]); + acb->ds.cmdlen = clen; + acb->ds.cmdbuf = (char *) kvtop(cbuf); + acb->ds.stslen = 1; + acb->ds.stsbuf = (char *) kvtop(&acb->stat[0]); + acb->ds.msglen = 1; + acb->ds.msgbuf = (char *) kvtop(&acb->msg[0]); + acb->msg[1] = -1; + acb->ds.msginlen = 1; + acb->ds.extmsglen = 1; + acb->ds.synmsglen = 3; + acb->ds.msginbuf = (char *) kvtop(&acb->msg[1]); + acb->ds.extmsgbuf = (char *) kvtop(&acb->msg[2]); + acb->ds.synmsgbuf = (char *) kvtop(&acb->msg[3]); + bzero(&acb->ds.chain, sizeof (acb->ds.chain)); + + if (sc->sc_sync[target].state == SYNC_START) { + if (siop_inhibit_sync[target]) { + sc->sc_sync[target].state = SYNC_DONE; + sc->sc_sync[target].sbcl = 0; + sc->sc_sync[target].sxfer = 0; +#ifdef DEBUG + if (siopsync_debug) + printf ("Forcing target %d asynchronous\n", target); +#endif + } + else { + acb->msg[2] = -1; + acb->msgout[1] = MSG_EXT_MESSAGE; + acb->msgout[2] = 3; + acb->msgout[3] = MSG_SYNC_REQ; +#ifdef MAXTOR_SYNC_KLUDGE + acb->msgout[4] = 50 / 4; /* ask for ridiculous period */ +#else + acb->msgout[4] = sc->sc_minsync; +#endif + acb->msgout[5] = SIOP_MAX_OFFSET; + acb->ds.idlen = 6; + sc->sc_sync[target].state = SYNC_SENT; +#ifdef DEBUG + if (siopsync_debug) + printf ("Sending sync request to target %d\n", target); +#endif + } + } + +/* + * Build physical DMA addresses for scatter/gather I/O + */ + acb->iob_buf = buf; + acb->iob_len = len; + acb->iob_curbuf = acb->iob_curlen = 0; + nchain = 0; + count = len; + addr = buf; + dmaend = NULL; + while (count > 0) { + acb->ds.chain[nchain].databuf = (char *) kvtop (addr); + if (count < (tcount = NBPG - ((int) addr & PGOFSET))) + tcount = count; + acb->ds.chain[nchain].datalen = tcount; + addr += tcount; + count -= tcount; + if (acb->ds.chain[nchain].databuf == dmaend) { + dmaend += acb->ds.chain[nchain].datalen; + acb->ds.chain[nchain].datalen = 0; + acb->ds.chain[--nchain].datalen += tcount; +#ifdef DEBUG + ++siopdma_hits; +#endif + } + else { + dmaend = acb->ds.chain[nchain].databuf + + acb->ds.chain[nchain].datalen; + acb->ds.chain[nchain].datalen = tcount; +#ifdef DEBUG + if (nchain) /* Don't count miss on first one */ + ++siopdma_misses; +#endif + } + ++nchain; + } +#ifdef DEBUG + if (nchain != 1 && len != 0 && siop_debug & 3) { + printf ("DMA chaining set: %d\n", nchain); + for (i = 0; i < nchain; ++i) { + printf (" [%d] %8x %4x\n", i, acb->ds.chain[i].databuf, + acb->ds.chain[i].datalen); + } + } +#endif + + /* push data cache for all data the 53c710 needs to access */ + dma_cachectl (sc, sizeof (struct siop_softc)); + dma_cachectl (cbuf, clen); + if (buf != NULL && len != 0) + dma_cachectl (buf, len); +#ifdef DEBUG + if (siop_debug & 0x100 && rp->siop_sbcl & SIOP_BSY) { + printf ("ACK! siop was busy at start: rp %x script %x dsa %x active %d\n", + rp, &scripts, &acb->ds, sc->sc_active); +#ifdef DDB + /*Debugger();*/ +#endif + } +#endif + if (sc->nexus_list.tqh_first == NULL) { + if (rp->siop_istat & SIOP_ISTAT_CON) + printf("%s: siop_select while connected?\n", + sc->sc_dev.dv_xname); + rp->siop_temp = 0; + rp->siop_sbcl = sc->sc_sync[target].sbcl; + rp->siop_dsa = kvtop(&acb->ds); + rp->siop_dsp = sc->sc_scriptspa; + SIOP_TRACE('s',1,0,0) + } else { + if ((rp->siop_istat & SIOP_ISTAT_CON) == 0) { + rp->siop_istat = SIOP_ISTAT_SIGP; + SIOP_TRACE('s',2,0,0); + } + else { + SIOP_TRACE('s',3,rp->siop_istat,0); + } + } +#ifdef DEBUG + ++siopstarts; +#endif +} + +/* + * Process a DMA or SCSI interrupt from the 53C710 SIOP + */ + +int +siop_checkintr(sc, istat, dstat, sstat0, status) + struct siop_softc *sc; + u_char istat; + u_char dstat; + u_char sstat0; + int *status; +{ + siop_regmap_p rp = sc->sc_siopp; + struct siop_acb *acb = sc->sc_nexus; + int target; + int dfifo, dbc, sstat1; + + dfifo = rp->siop_dfifo; + dbc = rp->siop_dbc0; + sstat1 = rp->siop_sstat1; + rp->siop_ctest8 |= SIOP_CTEST8_CLF; + while ((rp->siop_ctest1 & SIOP_CTEST1_FMT) != SIOP_CTEST1_FMT) + ; + rp->siop_ctest8 &= ~SIOP_CTEST8_CLF; +#ifdef DEBUG + ++siopints; +#if 0 + if (siop_debug & 0x100) { + DCIAS(&acb->stat[0]); /* XXX */ + printf ("siopchkintr: istat %x dstat %x sstat0 %x dsps %x sbcl %x sts %x msg %x\n", + istat, dstat, sstat0, rp->siop_dsps, rp->siop_sbcl, acb->stat[0], acb->msg[0]); + printf ("sync msg in: %02x %02x %02x %02x %02x %02x\n", + acb->msg[0], acb->msg[1], acb->msg[2], + acb->msg[3], acb->msg[4], acb->msg[5]); + } +#endif + if (rp->siop_dsp && (rp->siop_dsp < sc->sc_scriptspa || + rp->siop_dsp >= sc->sc_scriptspa + sizeof(scripts))) { + printf ("%s: dsp not within script dsp %x scripts %x:%x", + sc->sc_dev.dv_xname, rp->siop_dsp, sc->sc_scriptspa, + sc->sc_scriptspa + sizeof(scripts)); + printf(" istat %x dstat %x sstat0 %x\n", + istat, dstat, sstat0); + Debugger(); + } +#endif + SIOP_TRACE('i',dstat,istat,(istat&SIOP_ISTAT_DIP)?rp->siop_dsps&0xff:sstat0); + if (dstat & SIOP_DSTAT_SIR && rp->siop_dsps == 0xff00) { + /* Normal completion status, or check condition */ +#ifdef DEBUG + if (rp->siop_dsa != kvtop(&acb->ds)) { + printf ("siop: invalid dsa: %x %x\n", rp->siop_dsa, + kvtop(&acb->ds)); + panic("*** siop DSA invalid ***"); + } +#endif + target = acb->xs->sc_link->target; + if (sc->sc_sync[target].state == SYNC_SENT) { +#ifdef DEBUG + if (siopsync_debug) + printf ("sync msg in: %02x %02x %02x %02x %02x %02x\n", + acb->msg[0], acb->msg[1], acb->msg[2], + acb->msg[3], acb->msg[4], acb->msg[5]); +#endif + if (acb->msg[1] == 0xff) + printf ("%s: target %d ignored sync request\n", + sc->sc_dev.dv_xname, target); + else if (acb->msg[1] == MSG_REJECT) + printf ("%s: target %d rejected sync request\n", + sc->sc_dev.dv_xname, target); + sc->sc_sync[target].state = SYNC_DONE; + sc->sc_sync[target].sxfer = 0; + sc->sc_sync[target].sbcl = 0; + if (acb->msg[2] == 3 && + acb->msg[3] == MSG_SYNC_REQ && + acb->msg[5] != 0) { +#ifdef MAXTOR_KLUDGE + /* + * Kludge for my Maxtor XT8580S + * It accepts whatever we request, even + * though it won't work. So we ask for + * a short period than we can handle. If + * the device says it can do it, use 208ns. + * If the device says it can do less than + * 100ns, then we limit it to 100ns. + */ + if (acb->msg[4] && acb->msg[4] < 100 / 4) { +#ifdef DEBUG + printf ("%d: target %d wanted %dns period\n", + sc->sc_dev.dv_xname, target, + acb->msg[4] * 4); +#endif + if (acb->msg[4] == 50 / 4) + acb->msg[4] = 208 / 4; + else + acb->msg[4] = 100 / 4; + } +#endif /* MAXTOR_KLUDGE */ + printf ("%s: target %d now synchronous, period=%dns, offset=%d\n", + sc->sc_dev.dv_xname, target, + acb->msg[4] * 4, acb->msg[5]); + scsi_period_to_siop (sc, target); + } + } + dma_cachectl(&acb->stat[0], 1); + *status = acb->stat[0]; +#ifdef DEBUG + if (rp->siop_sbcl & SIOP_BSY) { + /*printf ("ACK! siop was busy at end: rp %x script %x dsa %x\n", + rp, &scripts, &acb->ds);*/ +#ifdef DDB + /*Debugger();*/ +#endif + } + if (acb->msg[0] != 0x00) + printf("%s: message was not COMMAND COMPLETE: %x\n", + sc->sc_dev.dv_xname, acb->msg[0]); +#endif + if (sc->nexus_list.tqh_first) + rp->siop_dcntl |= SIOP_DCNTL_STD; + return 1; + } + if (sstat0 & SIOP_SSTAT0_M_A) { /* Phase mismatch */ +#ifdef DEBUG + ++siopphmm; + if (acb == NULL) + printf("%s: Phase mismatch with no active command?\n", + sc->sc_dev.dv_xname); +#endif + if (acb->iob_len) { + int adjust; + adjust = ((dfifo - (dbc & 0x7f)) & 0x7f); + if (sstat1 & SIOP_SSTAT1_ORF) + ++adjust; + if (sstat1 & SIOP_SSTAT1_OLF) + ++adjust; + acb->iob_curlen = *((long *)&rp->siop_dcmd) & 0xffffff; + acb->iob_curlen += adjust; + acb->iob_curbuf = *((long *)&rp->siop_dnad) - adjust; +#ifdef DEBUG + if (siop_debug & 0x100) { + int i; + printf ("Phase mismatch: curbuf %x curlen %x dfifo %x dbc %x sstat1 %x adjust %x sbcl %x starts %d acb %x\n", + acb->iob_curbuf, acb->iob_curlen, dfifo, + dbc, sstat1, adjust, rp->siop_sbcl, siopstarts, acb); + if (acb->ds.chain[1].datalen) { + for (i = 0; acb->ds.chain[i].datalen; ++i) + printf("chain[%d] addr %x len %x\n", + i, acb->ds.chain[i].databuf, + acb->ds.chain[i].datalen); + } + } +#endif + dma_cachectl (acb, sizeof(*acb)); + } +#ifdef DEBUG + SIOP_TRACE('m',rp->siop_sbcl,(rp->siop_dsp>>8),rp->siop_dsp); + if (siop_debug & 9) + printf ("Phase mismatch: %x dsp +%x dcmd %x\n", + rp->siop_sbcl, + rp->siop_dsp - sc->sc_scriptspa, + *((long *)&rp->siop_dcmd)); +#endif + if ((rp->siop_sbcl & SIOP_REQ) == 0) { + printf ("Phase mismatch: REQ not asserted! %02x dsp %x\n", + rp->siop_sbcl, rp->siop_dsp); +#ifdef DEBUG + Debugger(); +#endif + } + switch (rp->siop_sbcl & 7) { + case 0: /* data out */ + case 1: /* data in */ + case 2: /* status */ + case 3: /* command */ + case 6: /* message in */ + case 7: /* message out */ + rp->siop_dsp = sc->sc_scriptspa + Ent_switch; + break; + default: + goto bad_phase; + } + return 0; + } + if (sstat0 & SIOP_SSTAT0_STO) { /* Select timed out */ +#ifdef DEBUG + if (acb == NULL) + printf("%s: Select timeout with no active command?\n", + sc->sc_dev.dv_xname); + if (rp->siop_sbcl & SIOP_BSY) { + printf ("ACK! siop was busy at timeout: rp %x script %x dsa %x\n", + rp, &scripts, &acb->ds); + printf(" sbcl %x sdid %x istat %x dstat %x sstat0 %x\n", + rp->siop_sbcl, rp->siop_sdid, istat, dstat, sstat0); + if (!(rp->siop_sbcl & SIOP_BSY)) { + printf ("Yikes, it's not busy now!\n"); +#if 0 + *status = -1; + if (sc->nexus_list.tqh_first) + rp->siop_dsp = sc->sc_scriptspa + Ent_wait_reselect; + return 1; +#endif + } +/* rp->siop_dcntl |= SIOP_DCNTL_STD;*/ + return (0); +#ifdef DDB + Debugger(); +#endif + } +#endif + *status = -1; + acb->xs->error = XS_SELTIMEOUT; + if (sc->nexus_list.tqh_first) + rp->siop_dsp = sc->sc_scriptspa + Ent_wait_reselect; + return 1; + } + if (acb) + target = acb->xs->sc_link->target; + else + target = 7; + if (sstat0 & SIOP_SSTAT0_UDC) { +#ifdef DEBUG + if (acb == NULL) + printf("%s: Unexpected disconnect with no active command?\n", + sc->sc_dev.dv_xname); + printf ("%s: target %d disconnected unexpectedly\n", + sc->sc_dev.dv_xname, target); +#endif +#if 0 + siopabort (sc, rp, "siopchkintr"); +#endif + *status = STS_BUSY; + if (sc->nexus_list.tqh_first) + rp->siop_dsp = sc->sc_scriptspa + Ent_wait_reselect; + return 1; + } + if (dstat & SIOP_DSTAT_SIR && (rp->siop_dsps == 0xff01 || + rp->siop_dsps == 0xff02)) { +#ifdef DEBUG + if (siop_debug & 0x100) + printf ("%s: ID %02x disconnected TEMP %x (+%x) curbuf %x curlen %x buf %x len %x dfifo %x dbc %x sstat1 %x starts %d acb %x\n", + sc->sc_dev.dv_xname, 1 << target, rp->siop_temp, + rp->siop_temp ? rp->siop_temp - sc->sc_scriptspa : 0, + acb->iob_curbuf, acb->iob_curlen, + acb->ds.chain[0].databuf, acb->ds.chain[0].datalen, dfifo, dbc, sstat1, siopstarts, acb); +#endif + if (acb == NULL) { + printf("%s: Disconnect with no active command?\n", + sc->sc_dev.dv_xname); + return (0); + } + /* + * XXXX need to update iob_curbuf/iob_curlen to reflect + * current data transferred. If device disconnected in + * the middle of a DMA block, they should already be set + * by the phase change interrupt. If the disconnect + * occurs on a DMA block boundary, we have to figure out + * which DMA block it was. + */ + if (acb->iob_len && rp->siop_temp) { + int n = rp->siop_temp - sc->sc_scriptspa; + + if (acb->iob_curlen && acb->iob_curlen != acb->ds.chain[0].datalen) + printf("%s: iob_curbuf/len already set? n %x iob %x/%x chain[0] %x/%x\n", + sc->sc_dev.dv_xname, n, acb->iob_curbuf, acb->iob_curlen, + acb->ds.chain[0].databuf, acb->ds.chain[0].datalen); + if (n < Ent_datain) + n = (n - Ent_dataout) / 16; + else + n = (n - Ent_datain) / 16; + if (n <= 0 && n > DMAMAXIO) + printf("TEMP invalid %d\n", n); + else { + acb->iob_curbuf = (u_long)acb->ds.chain[n].databuf; + acb->iob_curlen = acb->ds.chain[n].datalen; + } +#ifdef DEBUG + if (siop_debug & 0x100) { + printf("%s: TEMP offset %d", sc->sc_dev.dv_xname, n); + printf(" curbuf %x curlen %x\n", acb->iob_curbuf, + acb->iob_curlen); + } +#endif + } + /* + * If data transfer was interrupted by disconnect, iob_curbuf + * and iob_curlen should reflect the point of interruption. + * Adjust the DMA chain so that the data transfer begins + * at the appropriate place upon reselection. + * XXX This should only be done on save data pointer message? + */ + if (acb->iob_curlen) { + int i, j; + +#ifdef DEBUG + if (siop_debug & 0x100) + printf ("%s: adjusting DMA chain\n", + sc->sc_dev.dv_xname); + if (rp->siop_dsps == 0xff02) + printf ("%s: ID %02x disconnected without Save Data Pointers\n", + sc->sc_dev.dv_xname, 1 << target); +#endif + for (i = 0; i < DMAMAXIO; ++i) { + if (acb->ds.chain[i].datalen == 0) + break; + if (acb->iob_curbuf >= (long)acb->ds.chain[i].databuf && + acb->iob_curbuf < (long)(acb->ds.chain[i].databuf + + acb->ds.chain[i].datalen)) + break; + } + if (i >= DMAMAXIO || acb->ds.chain[i].datalen == 0) + printf("couldn't find saved data pointer\n"); +#ifdef DEBUG + if (siop_debug & 0x100) + printf(" chain[0]: %x/%x -> %x/%x\n", + acb->ds.chain[0].databuf, + acb->ds.chain[0].datalen, + acb->iob_curbuf, + acb->iob_curlen); +#endif + acb->ds.chain[0].databuf = (char *)acb->iob_curbuf; + acb->ds.chain[0].datalen = acb->iob_curlen; + for (j = 1, ++i; i < DMAMAXIO && acb->ds.chain[i].datalen; ++i, ++j) { +#ifdef DEBUG + if (siop_debug & 0x100) + printf(" chain[%d]: %x/%x -> %x/%x\n", j, + acb->ds.chain[j].databuf, + acb->ds.chain[j].datalen, + acb->ds.chain[i].databuf, + acb->ds.chain[i].datalen); +#endif + acb->ds.chain[j].databuf = acb->ds.chain[i].databuf; + acb->ds.chain[j].datalen = acb->ds.chain[i].datalen; + } + if (j < DMAMAXIO) + acb->ds.chain[j].datalen = 0; + DCIAS(kvtop(&acb->ds.chain)); + } + ++sc->sc_tinfo[target].dconns; + /* + * add nexus to waiting list + * clear nexus + * try to start another command for another target/lun + */ + acb->status = sc->sc_flags & SIOP_INTSOFF; + TAILQ_INSERT_HEAD(&sc->nexus_list, acb, chain); + sc->sc_nexus = NULL; /* no current device */ + /* start script to wait for reselect */ + if (sc->sc_nexus == NULL) + rp->siop_dsp = sc->sc_scriptspa + Ent_wait_reselect; +/* XXXX start another command ? */ + if (sc->ready_list.tqh_first) + siop_sched(sc); + return (0); + } + if (dstat & SIOP_DSTAT_SIR && rp->siop_dsps == 0xff03) { + int reselid = rp->siop_scratch & 0x7f; + int reselun = rp->siop_sfbr & 0x07; + + sc->sc_sstat1 = rp->siop_sbcl; /* XXXX save current SBCL */ +#ifdef DEBUG + if (siop_debug & 0x100) + printf ("%s: target ID %02x reselected dsps %x\n", + sc->sc_dev.dv_xname, reselid, + rp->siop_dsps); + if ((rp->siop_sfbr & 0x80) == 0) + printf("%s: Reselect message in was not identify: %x\n", + sc->sc_dev.dv_xname, rp->siop_sfbr); +#endif + if (sc->sc_nexus) { +#ifdef DEBUG + if (siop_debug & 0x100) + printf ("%s: reselect ID %02x w/active\n", + sc->sc_dev.dv_xname, reselid); +#endif + TAILQ_INSERT_HEAD(&sc->ready_list, sc->sc_nexus, chain); + sc->sc_tinfo[sc->sc_nexus->xs->sc_link->target].lubusy + &= ~(1 << sc->sc_nexus->xs->sc_link->lun); + --sc->sc_active; + } + /* + * locate acb of reselecting device + * set sc->sc_nexus to acb + */ + for (acb = sc->nexus_list.tqh_first; acb; + acb = acb->chain.tqe_next) { + if (reselid != (acb->ds.scsi_addr >> 16) || + reselun != (acb->msgout[0] & 0x07)) + continue; + TAILQ_REMOVE(&sc->nexus_list, acb, chain); + sc->sc_nexus = acb; + sc->sc_flags |= acb->status; + acb->status = 0; + DCIAS(kvtop(&acb->stat[0])); + rp->siop_dsa = kvtop(&acb->ds); + rp->siop_sxfer = sc->sc_sync[acb->xs->sc_link->target].sxfer; + rp->siop_sbcl = sc->sc_sync[acb->xs->sc_link->target].sbcl; + break; + } + if (acb == NULL) { + printf("%s: target ID %02x reselect nexus_list %x\n", + sc->sc_dev.dv_xname, reselid, + sc->nexus_list.tqh_first); + panic("unable to find reselecting device"); + } + dma_cachectl (acb, sizeof(*acb)); + rp->siop_temp = 0; + rp->siop_dcntl |= SIOP_DCNTL_STD; + return (0); + } + if (dstat & SIOP_DSTAT_SIR && rp->siop_dsps == 0xff04) { + u_short ctest2 = rp->siop_ctest2; + + /* reselect was interrupted (by Sig_P or select) */ +#ifdef DEBUG + if (siop_debug & 0x100 || + (ctest2 & SIOP_CTEST2_SIGP) == 0) + printf ("%s: reselect interrupted (Sig_P?) scntl1 %x ctest2 %x sfbr %x istat %x/%x\n", + sc->sc_dev.dv_xname, rp->siop_scntl1, + ctest2, rp->siop_sfbr, istat, rp->siop_istat); +#endif + /* XXX assumes it was not select */ + if (sc->sc_nexus == NULL) { + printf("%s: reselect interrupted, sc_nexus == NULL\n", + sc->sc_dev.dv_xname); +#if 0 + siop_dump(sc); +#ifdef DDB + Debugger(); +#endif +#endif + rp->siop_dcntl |= SIOP_DCNTL_STD; + return(0); + } + target = sc->sc_nexus->xs->sc_link->target; + rp->siop_temp = 0; + rp->siop_dsa = kvtop(&sc->sc_nexus->ds); + rp->siop_sxfer = sc->sc_sync[target].sxfer; + rp->siop_sbcl = sc->sc_sync[target].sbcl; + rp->siop_dsp = sc->sc_scriptspa; + return (0); + } + if (dstat & SIOP_DSTAT_SIR && rp->siop_dsps == 0xff06) { + if (acb == NULL) + printf("%s: Bad message-in with no active command?\n", + sc->sc_dev.dv_xname); + /* Unrecognized message in byte */ + dma_cachectl (&acb->msg[1],1); + printf ("%s: Unrecognized message in data sfbr %x msg %x sbcl %x\n", + sc->sc_dev.dv_xname, rp->siop_sfbr, acb->msg[1], rp->siop_sbcl); + /* what should be done here? */ + DCIAS(kvtop(&acb->msg[1])); + rp->siop_dsp = sc->sc_scriptspa + Ent_switch; + return (0); + } + if (dstat & SIOP_DSTAT_SIR && rp->siop_dsps == 0xff0a) { + /* Status phase wasn't followed by message in phase? */ + printf ("%s: Status phase not followed by message in phase? sbcl %x sbdl %x\n", + sc->sc_dev.dv_xname, rp->siop_sbcl, rp->siop_sbdl); + if (rp->siop_sbcl == 0xa7) { + /* It is now, just continue the script? */ + rp->siop_dcntl |= SIOP_DCNTL_STD; + return (0); + } + } + if (sstat0 == 0 && dstat & SIOP_DSTAT_SIR) { + dma_cachectl (&acb->stat[0], 1); + dma_cachectl (&acb->msg[0], 1); + printf ("SIOP interrupt: %x sts %x msg %x %x sbcl %x\n", + rp->siop_dsps, acb->stat[0], acb->msg[0], acb->msg[1], + rp->siop_sbcl); + siopreset (sc); + *status = -1; + return 0; /* siopreset has cleaned up */ + } + if (sstat0 & SIOP_SSTAT0_SGE) + printf ("SIOP: SCSI Gross Error\n"); + if (sstat0 & SIOP_SSTAT0_PAR) + printf ("SIOP: Parity Error\n"); + if (dstat & SIOP_DSTAT_IID) + printf ("SIOP: Invalid instruction detected\n"); +bad_phase: + /* + * temporary panic for unhandled conditions + * displays various things about the 53C710 status and registers + * then panics. + * XXXX need to clean this up to print out the info, reset, and continue + */ + printf ("siopchkintr: target %x ds %x\n", target, &acb->ds); + printf ("scripts %x ds %x rp %x dsp %x dcmd %x\n", sc->sc_scriptspa, + kvtop(&acb->ds), kvtop(rp), rp->siop_dsp, + *((long *)&rp->siop_dcmd)); + printf ("siopchkintr: istat %x dstat %x sstat0 %x dsps %x dsa %x sbcl %x sts %x msg %x %x sfbr %x\n", + istat, dstat, sstat0, rp->siop_dsps, rp->siop_dsa, + rp->siop_sbcl, acb->stat[0], acb->msg[0], acb->msg[1], rp->siop_sfbr); +#ifdef DEBUG + if (siop_debug & 0x20) + panic("siopchkintr: **** temp ****"); +#endif +#ifdef DDB + Debugger (); +#endif + siopreset (sc); /* hard reset */ + *status = -1; + return 0; /* siopreset cleaned up */ +} + +void +siop_select(sc) + struct siop_softc *sc; +{ + siop_regmap_p rp; + struct siop_acb *acb = sc->sc_nexus; + +#ifdef DEBUG + if (siop_debug & 1) + printf ("%s: select ", sc->sc_dev.dv_xname); +#endif + + rp = sc->sc_siopp; + if (acb->xs->flags & SCSI_POLL || siop_no_dma) { + sc->sc_flags |= SIOP_INTSOFF; + sc->sc_flags &= ~SIOP_INTDEFER; + if ((rp->siop_istat & 0x08) == 0) { + rp->siop_sien = 0; + rp->siop_dien = 0; + } +#if 0 + } else if ((sc->sc_flags & SIOP_INTDEFER) == 0) { + sc->sc_flags &= ~SIOP_INTSOFF; + if ((rp->siop_istat & 0x08) == 0) { + rp->siop_sien = sc->sc_sien; + rp->siop_dien = sc->sc_dien; + } +#endif + } +#ifdef DEBUG + if (siop_debug & 1) + printf ("siop_select: target %x cmd %02x ds %x\n", + acb->xs->sc_link->target, acb->cmd.opcode, + &sc->sc_nexus->ds); +#endif + + siop_start(sc, acb->xs->sc_link->target, acb->xs->sc_link->lun, + &acb->cmd, acb->clen, acb->daddr, acb->dleft); + + return; +} + +/* + * 53C710 interrupt handler + */ + +int +siopintr (sc) + register struct siop_softc *sc; +{ + siop_regmap_p rp; + register u_char istat, dstat, sstat0; + int status; + int s = splbio(); + + istat = sc->sc_istat; + if ((istat & (SIOP_ISTAT_SIP | SIOP_ISTAT_DIP)) == 0) { + splx(s); + return; + } + + /* Got a valid interrupt on this device */ + rp = sc->sc_siopp; + dstat = sc->sc_dstat; + sstat0 = sc->sc_sstat0; + if (dstat & SIOP_DSTAT_SIR) + sc->sc_intcode = rp->siop_dsps; + sc->sc_istat = 0; +#ifdef DEBUG + if (siop_debug & 1) + printf ("%s: intr istat %x dstat %x sstat0 %x\n", + sc->sc_dev.dv_xname, istat, dstat, sstat0); + if (!sc->sc_active) { + printf ("%s: spurious interrupt? istat %x dstat %x sstat0 %x status %x\n", + sc->sc_dev.dv_xname, istat, dstat, sstat0, sc->sc_nexus->stat[0]); + } +#endif + +#ifdef DEBUG + if (siop_debug & 5) { + DCIAS(kvtop(&sc->sc_nexus->stat[0])); + printf ("%s: intr istat %x dstat %x sstat0 %x dsps %x sbcl %x sts %x msg %x\n", + sc->sc_dev.dv_xname, istat, dstat, sstat0, + rp->siop_dsps, rp->siop_sbcl, + sc->sc_nexus->stat[0], sc->sc_nexus->msg[0]); + } +#endif + if (sc->sc_flags & SIOP_INTDEFER) { + sc->sc_flags &= ~(SIOP_INTDEFER | SIOP_INTSOFF); + rp->siop_sien = sc->sc_sien; + rp->siop_dien = sc->sc_dien; + } + if (siop_checkintr (sc, istat, dstat, sstat0, &status)) { +#if 1 + if (status == 0xff) + printf ("siopintr: status == 0xff\n"); +#endif + if ((sc->sc_flags & (SIOP_INTSOFF | SIOP_INTDEFER)) != SIOP_INTSOFF) { +#if 0 + if (rp->siop_sbcl & SIOP_BSY) { + printf ("%s: SCSI bus busy at completion", + sc->sc_dev.dv_xname); + printf(" targ %d sbcl %02x sfbr %x lcrc %02x dsp +%x\n", + sc->sc_nexus->xs->sc_link->target, + rp->siop_sbcl, rp->siop_sfbr, rp->siop_lcrc, + rp->siop_dsp - sc->sc_scriptspa); + } +#endif + siop_scsidone(sc->sc_nexus, sc->sc_nexus->stat[0]); + } + } + splx(s); +} + +/* + * This is based on the Progressive Peripherals 33Mhz Zeus driver and will + * not be correct for other 53c710 boards. + * + */ +scsi_period_to_siop (sc, target) + struct siop_softc *sc; +{ + int period, offset, i, sxfer, sbcl; + + period = sc->sc_nexus->msg[4]; + offset = sc->sc_nexus->msg[5]; + for (sbcl = 1; sbcl < 4; ++sbcl) { + sxfer = (period * 4 - 1) / sc->sc_tcp[sbcl] - 3; + if (sxfer >= 0 && sxfer <= 7) + break; + } + if (sbcl > 3) { + printf("siop_sync: unable to compute sync params for period %dns\n", + period * 4); + /* + * XXX need to pick a value we can do and renegotiate + */ + sxfer = sbcl = 0; + } else + sxfer = (sxfer << 4) | ((offset <= SIOP_MAX_OFFSET) ? + offset : SIOP_MAX_OFFSET); + sc->sc_sync[target].sxfer = sxfer; + sc->sc_sync[target].sbcl = sbcl; +#ifdef DEBUG + printf ("siop sync: siop_sxfr %02x, siop_sbcl %02x\n", sxfer, sbcl); +#endif +} + +#ifdef DEBUG + +#if SIOP_TRACE_SIZE +void +siop_dump_trace() +{ + int i; + + printf("siop trace: next index %d\n", siop_trix); + i = siop_trix; + do { + printf("%3d: '%c' %02x %02x %02x\n", i, siop_trbuf[i], + siop_trbuf[i + 1], siop_trbuf[i + 2], siop_trbuf[i + 3]); + i = (i + 4) & (SIOP_TRACE_SIZE - 1); + } while (i != siop_trix); +} +#endif + +void +siop_dump_acb(acb) + struct siop_acb *acb; +{ + u_char *b = (u_char *) &acb->cmd; + int i; + +#if SIOP_TRACE_SIZE + siop_dump_trace(); +#endif + printf("acb@%x ", acb); + if (acb->xs == NULL) { + printf("<unused>\n"); + return; + } + printf("(%d:%d) flags %2x clen %2d cmd ", acb->xs->sc_link->target, + acb->xs->sc_link->lun, acb->flags, acb->clen); + for (i = acb->clen; i; --i) + printf(" %02x", *b++); + printf("\n"); + printf(" xs: %08x data %8x:%04x ", acb->xs, acb->xs->data, + acb->xs->datalen); + printf("va %8x:%04x ", acb->iob_buf, acb->iob_len); + printf("cur %8x:%04x\n", acb->iob_curbuf, acb->iob_curlen); + } + +void +siop_dump(sc) + struct siop_softc *sc; +{ + struct siop_acb *acb; + siop_regmap_p rp = sc->sc_siopp; + int s; + int i; + + s = splbio(); + printf("%s@%x regs %x istat %x\n", + sc->sc_dev.dv_xname, sc, rp, rp->siop_istat); + if (acb = sc->free_list.tqh_first) { + printf("Free list:\n"); + while (acb) { + siop_dump_acb(acb); + acb = acb->chain.tqe_next; + } + } + if (acb = sc->ready_list.tqh_first) { + printf("Ready list:\n"); + while (acb) { + siop_dump_acb(acb); + acb = acb->chain.tqe_next; + } + } + if (acb = sc->nexus_list.tqh_first) { + printf("Nexus list:\n"); + while (acb) { + siop_dump_acb(acb); + acb = acb->chain.tqe_next; + } + } + if (sc->sc_nexus) { + printf("Nexus:\n"); + siop_dump_acb(sc->sc_nexus); + } + for (i = 0; i < 8; ++i) { + if (sc->sc_tinfo[i].cmds > 2) { + printf("tgt %d: cmds %d disc %d senses %d lubusy %x\n", + i, sc->sc_tinfo[i].cmds, + sc->sc_tinfo[i].dconns, + sc->sc_tinfo[i].senses, + sc->sc_tinfo[i].lubusy); + } + } + splx(s); +} +#endif diff --git a/sys/arch/mvme68k/dev/siop_script.out b/sys/arch/mvme68k/dev/siop_script.out new file mode 100644 index 00000000000..c6a91178fc8 --- /dev/null +++ b/sys/arch/mvme68k/dev/siop_script.out @@ -0,0 +1,175 @@ +unsigned long scripts[] = { + 0x47000000, 0x00000118, /* 000 - 0 */ + 0x878b0000, 0x00000030, /* 008 - 8 */ + 0x868a0000, 0x00000168, /* 010 - 16 */ + 0x828a0000, 0x00000170, /* 018 - 24 */ + 0x808a0000, 0x00000180, /* 020 - 32 */ + 0x818a0000, 0x00000288, /* 028 - 40 */ + 0x838a0000, 0x00000390, /* 030 - 48 */ + 0x98080000, 0x0000ff05, /* 038 - 56 */ + 0x1f000024, 0x00000024, /* 040 - 64 */ + 0x808c0001, 0x00000040, /* 048 - 72 */ + 0x808c0004, 0x00000078, /* 050 - 80 */ + 0x808c0002, 0x00000088, /* 058 - 88 */ + 0x808c0007, 0x00000010, /* 060 - 96 */ + 0x808c0003, 0x00000008, /* 068 - 104 */ + 0x98080000, 0x0000ff06, /* 070 - 112 */ + 0x60000040, 0x00000000, /* 078 - 120 */ + 0x60000008, 0x00000000, /* 080 - 128 */ + 0x80880000, 0xffffff78, /* 088 - 136 */ + 0x60000040, 0x00000000, /* 090 - 144 */ + 0x1f00002c, 0x0000002c, /* 098 - 152 */ + 0x808c0003, 0x00000008, /* 0a0 - 160 */ + 0x98080000, 0x0000ff07, /* 0a8 - 168 */ + 0x60000040, 0x00000000, /* 0b0 - 176 */ + 0x1f000034, 0x00000034, /* 0b8 - 184 */ + 0x60000040, 0x00000000, /* 0c0 - 192 */ + 0x80880000, 0xffffff38, /* 0c8 - 200 */ + 0x60000040, 0x00000000, /* 0d0 - 208 */ + 0x48000000, 0x00000000, /* 0d8 - 216 */ + 0x98080000, 0x0000ff02, /* 0e0 - 224 */ + 0x60000040, 0x00000000, /* 0e8 - 232 */ + 0x87830000, 0xffffff10, /* 0f0 - 240 */ + 0x1f00002c, 0x0000002c, /* 0f8 - 248 */ + 0x98040004, 0x0000ff08, /* 100 - 256 */ + 0x60000040, 0x00000000, /* 108 - 264 */ + 0x48000000, 0x00000000, /* 110 - 272 */ + 0x98080000, 0x0000ff01, /* 118 - 280 */ + 0x54000000, 0x00000038, /* 120 - 288 */ + 0x72230000, 0x00000000, /* 128 - 296 */ + 0x6a340000, 0x00000000, /* 130 - 304 */ + 0x9f030000, 0x0000ff09, /* 138 - 312 */ + 0x1f00001c, 0x0000001c, /* 140 - 320 */ + 0x98080000, 0x0000ff03, /* 148 - 328 */ + 0x60000040, 0x00000000, /* 150 - 336 */ + 0x80880000, 0xfffffea8, /* 158 - 344 */ + 0x74011000, 0x00000000, /* 160 - 352 */ + 0x980c0000, 0x0000ff04, /* 168 - 360 */ + 0x74164000, 0x00000000, /* 170 - 368 */ + 0x80880000, 0xffffffa0, /* 178 - 376 */ + 0x1e000004, 0x00000004, /* 180 - 384 */ + 0x80880000, 0xfffffe78, /* 188 - 392 */ + 0x60000008, 0x00000000, /* 190 - 400 */ + 0x1a00000c, 0x0000000c, /* 198 - 408 */ + 0x80880000, 0xfffffe60, /* 1a0 - 416 */ + 0x1800003c, 0x0000003c, /* 1a8 - 424 */ + 0x88830000, 0xfffffe50, /* 1b0 - 432 */ + 0x18000044, 0x00000044, /* 1b8 - 440 */ + 0x88830000, 0xfffffe40, /* 1c0 - 448 */ + 0x1800004c, 0x0000004c, /* 1c8 - 456 */ + 0x88830000, 0xfffffe30, /* 1d0 - 464 */ + 0x18000054, 0x00000054, /* 1d8 - 472 */ + 0x88830000, 0xfffffe20, /* 1e0 - 480 */ + 0x1800005c, 0x0000005c, /* 1e8 - 488 */ + 0x88830000, 0xfffffe10, /* 1f0 - 496 */ + 0x18000064, 0x00000064, /* 1f8 - 504 */ + 0x88830000, 0xfffffe00, /* 200 - 512 */ + 0x1800006c, 0x0000006c, /* 208 - 520 */ + 0x88830000, 0xfffffdf0, /* 210 - 528 */ + 0x18000074, 0x00000074, /* 218 - 536 */ + 0x88830000, 0xfffffde0, /* 220 - 544 */ + 0x1800007c, 0x0000007c, /* 228 - 552 */ + 0x88830000, 0xfffffdd0, /* 230 - 560 */ + 0x18000084, 0x00000084, /* 238 - 568 */ + 0x88830000, 0xfffffdc0, /* 240 - 576 */ + 0x1800008c, 0x0000008c, /* 248 - 584 */ + 0x88830000, 0xfffffdb0, /* 250 - 592 */ + 0x18000094, 0x00000094, /* 258 - 600 */ + 0x88830000, 0xfffffda0, /* 260 - 608 */ + 0x1800009c, 0x0000009c, /* 268 - 616 */ + 0x88830000, 0xfffffd90, /* 270 - 624 */ + 0x180000a4, 0x000000a4, /* 278 - 632 */ + 0x88830000, 0xfffffd80, /* 280 - 640 */ + 0x180000ac, 0x000000ac, /* 288 - 648 */ + 0x88830000, 0xfffffd70, /* 290 - 656 */ + 0x180000b4, 0x000000b4, /* 298 - 664 */ + 0x88830000, 0xfffffd60, /* 2a0 - 672 */ + 0x180000bc, 0x000000bc, /* 2a8 - 680 */ + 0x88880000, 0xfffffd50, /* 2b0 - 688 */ + 0x1900003c, 0x0000003c, /* 2b8 - 696 */ + 0x89830000, 0xfffffd40, /* 2c0 - 704 */ + 0x19000044, 0x00000044, /* 2c8 - 712 */ + 0x89830000, 0xfffffd30, /* 2d0 - 720 */ + 0x1900004c, 0x0000004c, /* 2d8 - 728 */ + 0x89830000, 0xfffffd20, /* 2e0 - 736 */ + 0x19000054, 0x00000054, /* 2e8 - 744 */ + 0x89830000, 0xfffffd10, /* 2f0 - 752 */ + 0x1900005c, 0x0000005c, /* 2f8 - 760 */ + 0x89830000, 0xfffffd00, /* 300 - 768 */ + 0x19000064, 0x00000064, /* 308 - 776 */ + 0x89830000, 0xfffffcf0, /* 310 - 784 */ + 0x1900006c, 0x0000006c, /* 318 - 792 */ + 0x89830000, 0xfffffce0, /* 320 - 800 */ + 0x19000074, 0x00000074, /* 328 - 808 */ + 0x89830000, 0xfffffcd0, /* 330 - 816 */ + 0x1900007c, 0x0000007c, /* 338 - 824 */ + 0x89830000, 0xfffffcc0, /* 340 - 832 */ + 0x19000084, 0x00000084, /* 348 - 840 */ + 0x89830000, 0xfffffcb0, /* 350 - 848 */ + 0x1900008c, 0x0000008c, /* 358 - 856 */ + 0x89830000, 0xfffffca0, /* 360 - 864 */ + 0x19000094, 0x00000094, /* 368 - 872 */ + 0x89830000, 0xfffffc90, /* 370 - 880 */ + 0x1900009c, 0x0000009c, /* 378 - 888 */ + 0x89830000, 0xfffffc80, /* 380 - 896 */ + 0x190000a4, 0x000000a4, /* 388 - 904 */ + 0x89830000, 0xfffffc70, /* 390 - 912 */ + 0x190000ac, 0x000000ac, /* 398 - 920 */ + 0x89830000, 0xfffffc60, /* 3a0 - 928 */ + 0x190000b4, 0x000000b4, /* 3a8 - 936 */ + 0x89830000, 0xfffffc50, /* 3b0 - 944 */ + 0x190000bc, 0x000000bc, /* 3b8 - 952 */ + 0x88880000, 0xfffffc40, /* 3c0 - 960 */ + 0x1b000014, 0x00000014, /* 3c8 - 968 */ + 0x9f030000, 0x0000ff0a, /* 3d0 - 976 */ + 0x1f00001c, 0x0000001c, /* 3d8 - 984 */ + 0x60000040, 0x00000000, /* 3e0 - 992 */ + 0x48000000, 0x00000000, /* 3e8 - 1000 */ + 0x98080000, 0x0000ff00, /* 3f0 - 1008 */ + 0x80880000, 0xfffffd20, /* 3f8 - 1016 */ +}; + +#define A_ds_Device 0x00000000 +#define A_ds_MsgOut 0x00000004 +#define A_ds_Cmd 0x0000000c +#define A_ds_Status 0x00000014 +#define A_ds_Msg 0x0000001c +#define A_ds_MsgIn 0x00000024 +#define A_ds_ExtMsg 0x0000002c +#define A_ds_SyncMsg 0x00000034 +#define A_ds_Data1 0x0000003c +#define A_ds_Data2 0x00000044 +#define A_ds_Data3 0x0000004c +#define A_ds_Data4 0x00000054 +#define A_ds_Data5 0x0000005c +#define A_ds_Data6 0x00000064 +#define A_ds_Data7 0x0000006c +#define A_ds_Data8 0x00000074 +#define A_ds_Data9 0x0000007c +#define A_ds_Data10 0x00000084 +#define A_ds_Data11 0x0000008c +#define A_ds_Data12 0x00000094 +#define A_ds_Data13 0x0000009c +#define A_ds_Data14 0x000000a4 +#define A_ds_Data15 0x000000ac +#define A_ds_Data16 0x000000b4 +#define A_ds_Data17 0x000000bc +#define A_ok 0x0000ff00 +#define A_err1 0x0000ff01 +#define A_err2 0x0000ff02 +#define A_err3 0x0000ff03 +#define A_err4 0x0000ff04 +#define A_err5 0x0000ff05 +#define A_err6 0x0000ff06 +#define A_err7 0x0000ff07 +#define A_err8 0x0000ff08 +#define A_err9 0x0000ff09 +#define A_err10 0x0000ff0a +#define Ent_scripts 0x00000000 +#define Ent_switch 0x00000008 +#define Ent_wait_reselect 0x00000120 +#define Ent_dataout 0x000001a8 +#define Ent_datain 0x000002b8 + +unsigned long INSTRUCTIONS = 0x00000080; +unsigned long PATCHES = 0x00000000; diff --git a/sys/arch/mvme68k/dev/siop_script.ss b/sys/arch/mvme68k/dev/siop_script.ss new file mode 100644 index 00000000000..f43c74bfe10 --- /dev/null +++ b/sys/arch/mvme68k/dev/siop_script.ss @@ -0,0 +1,205 @@ +; $NetBSD: siop_script.ss,v 1.3 1995/08/18 15:28:11 chopps Exp $ + +; +; Copyright (c) 1995 Michael L. Hitch +; 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 Michael L. Hitch. +; 4. The name of the author may not be used to endorse or promote products +; derived from this software without specific prior written permission +; +; THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +; + +; NCR 53c710 script +; +ABSOLUTE ds_Device = 0 +ABSOLUTE ds_MsgOut = ds_Device + 4 +ABSOLUTE ds_Cmd = ds_MsgOut + 8 +ABSOLUTE ds_Status = ds_Cmd + 8 +ABSOLUTE ds_Msg = ds_Status + 8 +ABSOLUTE ds_MsgIn = ds_Msg + 8 +ABSOLUTE ds_ExtMsg = ds_MsgIn + 8 +ABSOLUTE ds_SyncMsg = ds_ExtMsg + 8 +ABSOLUTE ds_Data1 = ds_SyncMsg + 8 +ABSOLUTE ds_Data2 = ds_Data1 + 8 +ABSOLUTE ds_Data3 = ds_Data2 + 8 +ABSOLUTE ds_Data4 = ds_Data3 + 8 +ABSOLUTE ds_Data5 = ds_Data4 + 8 +ABSOLUTE ds_Data6 = ds_Data5 + 8 +ABSOLUTE ds_Data7 = ds_Data6 + 8 +ABSOLUTE ds_Data8 = ds_Data7 + 8 +ABSOLUTE ds_Data9 = ds_Data8 + 8 + +ABSOLUTE ok = 0xff00 +ABSOLUTE err1 = 0xff01 +ABSOLUTE err2 = 0xff02 +ABSOLUTE err3 = 0xff03 +ABSOLUTE err4 = 0xff04 +ABSOLUTE err5 = 0xff05 +ABSOLUTE err6 = 0xff06 +ABSOLUTE err7 = 0xff07 +ABSOLUTE err8 = 0xff08 +ABSOLUTE err9 = 0xff09 +ABSOLUTE err10 = 0xff0a + +ENTRY scripts +ENTRY switch +ENTRY wait_reselect +ENTRY dataout +ENTRY datain + +PROC scripts: + +scripts: + + SELECT ATN FROM ds_Device, REL(reselect) +; +switch: + JUMP REL(msgin), WHEN MSG_IN + JUMP REL(msgout), IF MSG_OUT + JUMP REL(command_phase), IF CMD + JUMP REL(dataout), IF DATA_OUT + JUMP REL(datain), IF DATA_IN + JUMP REL(end), IF STATUS + + INT err5 ; Unrecognized phase + +msgin: + MOVE FROM ds_MsgIn, WHEN MSG_IN + JUMP REL(ext_msg), IF 0x01 ; extended message + JUMP REL(disc), IF 0x04 ; disconnect message + JUMP REL(msg_sdp), IF 0x02 ; save data pointers + JUMP REL(msg_rej), IF 0x07 ; message reject + JUMP REL(msg_rdp), IF 0x03 ; restore data pointers + INT err6 ; unrecognized message + +msg_rdp: +msg_rej: + CLEAR ACK + CLEAR ATN + JUMP REL(switch) + +ext_msg: + CLEAR ACK + MOVE FROM ds_ExtMsg, WHEN MSG_IN + JUMP REL(sync_msg), IF 0x03 + int err7 ; extended message not SDTR + +sync_msg: + CLEAR ACK + MOVE FROM ds_SyncMsg, WHEN MSG_IN + CLEAR ACK + JUMP REL(switch) + +disc: + CLEAR ACK + WAIT DISCONNECT + + int err2 ; signal disconnect w/o save DP + +msg_sdp: + CLEAR ACK ; acknowledge message + JUMP REL(switch), WHEN NOT MSG_IN + MOVE FROM ds_ExtMsg, WHEN MSG_IN + INT err8, IF NOT 0x04 ; interrupt if not disconnect + CLEAR ACK + WAIT DISCONNECT + + INT err1 ; signal disconnect + +reselect: +wait_reselect: + WAIT RESELECT REL(select_adr) + MOVE LCRC to SFBR ; Save reselect ID + MOVE SFBR to SCRATCH0 + + INT err9, WHEN NOT MSG_IN ; didn't get IDENTIFY + MOVE FROM ds_Msg, WHEN MSG_IN + INT err3 ; let host know about reconnect + CLEAR ACK ; acknowlege the message + JUMP REL(switch) + + +select_adr: + MOVE SCNTL1 & 0x10 to SFBR ; get connected status + INT err4, IF 0x00 ; tell host if not connected + MOVE CTEST2 & 0x40 to SFBR ; clear Sig_P + JUMP REL(wait_reselect) ; and try reselect again + +msgout: + MOVE FROM ds_MsgOut, WHEN MSG_OUT + JUMP REL(switch) + +command_phase: + CLEAR ATN + MOVE FROM ds_Cmd, WHEN CMD + JUMP REL(switch) + +dataout: + MOVE FROM ds_Data1, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data2, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data3, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data4, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data5, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data6, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data7, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data8, WHEN DATA_OUT + CALL REL(switch), WHEN NOT DATA_OUT + MOVE FROM ds_Data9, WHEN DATA_OUT + CALL REL(switch) + +datain: + MOVE FROM ds_Data1, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data2, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data3, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data4, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data5, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data6, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data7, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data8, WHEN DATA_IN + CALL REL(switch), WHEN NOT DATA_IN + MOVE FROM ds_Data9, WHEN DATA_IN + CALL REL(switch) + +end: + MOVE FROM ds_Status, WHEN STATUS + int err10, WHEN NOT MSG_IN ; status not followed by msg + MOVE FROM ds_Msg, WHEN MSG_IN + CLEAR ACK + WAIT DISCONNECT + INT ok ; signal completion + JUMP REL(wait_reselect) diff --git a/sys/arch/mvme68k/dev/siopdma.c b/sys/arch/mvme68k/dev/siopdma.c new file mode 100644 index 00000000000..439c4a9d3ea --- /dev/null +++ b/sys/arch/mvme68k/dev/siopdma.c @@ -0,0 +1,223 @@ +/* $NetBSD: afsc.c,v 1.6 1995/02/12 19:19:00 chopps Exp $ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * Copyright (c) 1994 Michael L. Hitch + * Copyright (c) 1982, 1990 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. + * + * @(#)dma.c + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> +#include <machine/autoconf.h> +#include <mvme68k/dev/siopreg.h> +#include <mvme68k/dev/siopvar.h> + +#include "mc.h" +#include "pcctwo.h" + +#if NMC > 0 +#include <mvme68k/dev/mcreg.h> +#endif +#if NPCCTWO > 0 +#include <mvme68k/dev/pcctworeg.h> +#endif + +int afscmatch __P((struct device *, void *, void *)); +void afscattach __P((struct device *, struct device *, void *)); + +int afscprint __P((void *auxp, char *)); +int siopintr __P((struct siop_softc *)); +int afsc_dmaintr __P((struct siop_softc *)); + +struct scsi_adapter afsc_scsiswitch = { + siop_scsicmd, + siop_minphys, + 0, /* no lun support */ + 0, /* no lun support */ +}; + +struct scsi_device afsc_scsidev = { + NULL, /* use default error handler */ + NULL, /* do not have a start function */ + NULL, /* have no async handler */ + NULL, /* Use default done routine */ +}; + +struct cfdriver siopcd = { + NULL, "siop", afscmatch, afscattach, + DV_DULL, sizeof(struct siop_softc), NULL, 0 }; + +int +afscmatch(pdp, vcf, args) + struct device *pdp; + void *vcf, *args; +{ + struct cfdata *cf = vcf; + struct confargs *ca = args; + + return (!badvaddr(ca->ca_vaddr, 4)); +} + +void +afscattach(parent, self, auxp) + struct device *parent, *self; + void *auxp; +{ + struct siop_softc *sc = (struct siop_softc *)self; + struct confargs *ca = auxp; + siop_regmap_p rp; + extern int cpuspeed; + + sc->sc_siopp = rp = ca->ca_vaddr; + + /* + * siop uses sc_clock_freq to define the dcntl & ctest7 reg values + * (was 0x0221, but i added SIOP_CTEST7_SC0 for snooping control) + * XXX does the clock frequency change for the 33MHz processors? + */ + sc->sc_clock_freq = cpuspeed * 2; +#ifdef MVME177 + /* XXX this is a guess! */ + if (cputyp == CPU_177) + sc->sc_clock_freq = cpuspeed; +#endif + sc->sc_dcntl = SIOP_DCNTL_EA; +/*X*/ if (sc->sc_clock_freq <= 25) +/*X*/ sc->sc_dcntl |= (2 << 6); +/*X*/ else if (sc->sc_clock_freq <= 37) +/*X*/ sc->sc_dcntl |= (1 << 6); +/*X*/ else if (sc->sc_clock_freq <= 50) +/*X*/ sc->sc_dcntl |= (0 << 6); +/*X*/ else +/*X*/ sc->sc_dcntl |= (3 << 6); + + sc->sc_ctest7 = SIOP_CTEST7_SNOOP | SIOP_CTEST7_TT1; + + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter_target = 7; /* XXXX should ask ROM */ + sc->sc_link.adapter = &afsc_scsiswitch; + sc->sc_link.device = &afsc_scsidev; + sc->sc_link.openings = 1; + + sc->sc_ih.ih_fn = afsc_dmaintr; + sc->sc_ih.ih_arg = sc; + sc->sc_ih.ih_ipl = ca->ca_ipl; + + siopinitialize(sc); + + switch (ca->ca_bustype) { +#if NMC > 0 + case BUS_MC: + { + struct mcreg *mc = (struct mcreg *)ca->ca_master; + + mcintr_establish(MCV_NCR, &sc->sc_ih); + mc->mc_ncrirq = ca->ca_ipl | MC_IRQ_IEN; + break; + } +#endif +#if NPCCTWO > 0 + case BUS_PCCTWO: + { + struct pcctworeg *pcc2 = (struct pcctworeg *)ca->ca_master; + + pcctwointr_establish(PCC2V_NCR, &sc->sc_ih); + pcc2->pcc2_ncrirq = ca->ca_ipl | PCC2_IRQ_IEN; + break; + } +#endif + } + + evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt); + + /* + * attach all scsi units on us + */ + config_found(self, &sc->sc_link, afscprint); +} + +/* + * print diag if pnp is NULL else just extra + */ +int +afscprint(auxp, pnp) + void *auxp; + char *pnp; +{ + if (pnp == NULL) + return (UNCONF); + return (QUIET); +} + +int +afsc_dmaintr(sc) + struct siop_softc *sc; +{ + siop_regmap_p rp; + u_char istat; + + rp = sc->sc_siopp; + istat = rp->siop_istat; + if ((istat & (SIOP_ISTAT_SIP | SIOP_ISTAT_DIP)) == 0) + return (0); + if ((rp->siop_sien | rp->siop_dien) == 0) + return (0); /* no interrupts enabled */ + + /* + * save interrupt status, DMA status, and SCSI status 0 + * (may need to deal with stacked interrupts?) + */ + sc->sc_istat = istat; + sc->sc_dstat = rp->siop_dstat; + sc->sc_sstat0 = rp->siop_sstat0; + siopintr(sc); + sc->sc_intrcnt.ev_count++; + return (1); +} + +#ifdef DEBUG +void +afsc_dump() +{ + int i; + + for (i = 0; i < afsccd.cd_ndevs; ++i) + if (afsccd.cd_devs[i]) + siop_dump(afsccd.cd_devs[i]); +} +#endif diff --git a/sys/arch/mvme68k/dev/siopreg.h b/sys/arch/mvme68k/dev/siopreg.h new file mode 100644 index 00000000000..0fce258fbc5 --- /dev/null +++ b/sys/arch/mvme68k/dev/siopreg.h @@ -0,0 +1,336 @@ +/* $NetBSD: siopreg.h,v 1.7 1995/08/18 15:28:13 chopps Exp $ */ + +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * 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. + * + * @(#)siopreg.h 7.3 (Berkeley) 2/5/91 + */ + +/* + * NCR 53C710 SCSI interface hardware description. + * + * From the Mach scsi driver for the 53C700 + */ + +typedef struct { +/*00*/ volatile unsigned char siop_sien; /* rw: SCSI Interrupt Enable */ +/*01*/ volatile unsigned char siop_sdid; /* rw: SCSI Destination ID */ +/*02*/ volatile unsigned char siop_scntl1; /* rw: SCSI control reg 1 */ +/*03*/ volatile unsigned char siop_scntl0; /* rw: SCSI control reg 0 */ +/*04*/ volatile unsigned char siop_socl; /* rw: SCSI Output Control Latch */ +/*05*/ volatile unsigned char siop_sodl; /* rw: SCSI Output Data Latch */ +/*06*/ volatile unsigned char siop_sxfer; /* rw: SCSI Transfer reg */ +/*07*/ volatile unsigned char siop_scid; /* rw: SCSI Chip ID reg */ +/*08*/ volatile unsigned char siop_sbcl; /* ro: SCSI Bus Control Lines */ +/*09*/ volatile unsigned char siop_sbdl; /* ro: SCSI Bus Data Lines */ +/*0a*/ volatile unsigned char siop_sidl; /* ro: SCSI Input Data Latch */ +/*0b*/ volatile unsigned char siop_sfbr; /* ro: SCSI First Byte Received */ +/*0c*/ volatile unsigned char siop_sstat2; /* ro: SCSI status reg 2 */ +/*0d*/ volatile unsigned char siop_sstat1; /* ro: SCSI status reg 1 */ +/*0e*/ volatile unsigned char siop_sstat0; /* ro: SCSI status reg 0 */ +/*0f*/ volatile unsigned char siop_dstat; /* ro: DMA status */ +/*10*/ volatile unsigned long siop_dsa; /* rw: Data Structure Address */ +/*14*/ volatile unsigned char siop_ctest3; /* ro: Chip test register 3 */ +/*15*/ volatile unsigned char siop_ctest2; /* ro: Chip test register 2 */ +/*16*/ volatile unsigned char siop_ctest1; /* ro: Chip test register 1 */ +/*17*/ volatile unsigned char siop_ctest0; /* ro: Chip test register 0 */ +/*18*/ volatile unsigned char siop_ctest7; /* rw: Chip test register 7 */ +/*19*/ volatile unsigned char siop_ctest6; /* rw: Chip test register 6 */ +/*1a*/ volatile unsigned char siop_ctest5; /* rw: Chip test register 5 */ +/*1b*/ volatile unsigned char siop_ctest4; /* rw: Chip test register 4 */ +/*1c*/ volatile unsigned long siop_temp; /* rw: Temporary Stack reg */ +/*20*/ volatile unsigned char siop_lcrc; /* rw: LCRC value */ +/*21*/ volatile unsigned char siop_ctest8; /* rw: Chip test register 8 */ +/*22*/ volatile unsigned char siop_istat; /* rw: Interrupt Status reg */ +/*23*/ volatile unsigned char siop_dfifo; /* rw: DMA FIFO */ +/*24*/ volatile unsigned char siop_dcmd; /* rw: DMA Command Register */ +/*25*/ volatile unsigned char siop_dbc2; /* rw: DMA Byte Counter reg */ +/*26*/ volatile unsigned char siop_dbc1; +/*27*/ volatile unsigned char siop_dbc0; +/*28*/ volatile unsigned long siop_dnad; /* rw: DMA Next Address */ +/*2c*/ volatile unsigned long siop_dsp; /* rw: DMA SCRIPTS Pointer reg */ +/*30*/ volatile unsigned long siop_dsps; /* rw: DMA SCRIPTS Pointer Save reg */ +/*34*/ volatile unsigned long siop_scratch; /* rw: Scratch Register */ +/*38*/ volatile unsigned char siop_dcntl; /* rw: DMA Control reg */ +/*39*/ volatile unsigned char siop_dwt; /* rw: DMA Watchdog Timer */ +/*3a*/ volatile unsigned char siop_dien; /* rw: DMA Interrupt Enable */ +/*3b*/ volatile unsigned char siop_dmode; /* rw: DMA Mode reg */ +/*3c*/ volatile unsigned long siop_adder; + +} siop_regmap_t; +typedef volatile siop_regmap_t *siop_regmap_p; + +/* + * Register defines + */ + +/* Scsi control register 0 (scntl0) */ + +#define SIOP_SCNTL0_ARB 0xc0 /* Arbitration mode */ +# define SIOP_ARB_SIMPLE 0x00 +# define SIOP_ARB_FULL 0xc0 +#define SIOP_SCNTL0_START 0x20 /* Start Sequence */ +#define SIOP_SCNTL0_WATN 0x10 /* (Select) With ATN */ +#define SIOP_SCNTL0_EPC 0x08 /* Enable Parity Checking */ +#define SIOP_SCNTL0_EPG 0x04 /* Enable Parity Generation */ +#define SIOP_SCNTL0_AAP 0x02 /* Assert ATN on Parity Error */ +#define SIOP_SCNTL0_TRG 0x01 /* Target Mode */ + +/* Scsi control register 1 (scntl1) */ + +#define SIOP_SCNTL1_EXC 0x80 /* Extra Clock Cycle of data setup */ +#define SIOP_SCNTL1_ADB 0x40 /* Assert Data Bus */ +#define SIOP_SCNTL1_ESR 0x20 /* Enable Selection/Reselection */ +#define SIOP_SCNTL1_CON 0x10 /* Connected */ +#define SIOP_SCNTL1_RST 0x08 /* Assert RST */ +#define SIOP_SCNTL1_AESP 0x04 /* Assert even SCSI parity */ +#define SIOP_SCNTL1_RES0 0x02 /* Reserved */ +#define SIOP_SCNTL1_RES1 0x01 /* Reserved */ + +/* Scsi interrupt enable register (sien) */ + +#define SIOP_SIEN_M_A 0x80 /* Phase Mismatch or ATN active */ +#define SIOP_SIEN_FCMP 0x40 /* Function Complete */ +#define SIOP_SIEN_STO 0x20 /* (Re)Selection timeout */ +#define SIOP_SIEN_SEL 0x10 /* (Re)Selected */ +#define SIOP_SIEN_SGE 0x08 /* SCSI Gross Error */ +#define SIOP_SIEN_UDC 0x04 /* Unexpected Disconnect */ +#define SIOP_SIEN_RST 0x02 /* RST asserted */ +#define SIOP_SIEN_PAR 0x01 /* Parity Error */ + +/* Scsi chip ID (scid) */ + +#define SIOP_SCID_VALUE(i) (1<<i) + +/* Scsi transfer register (sxfer) */ + +#define SIOP_SXFER_DHP 0x80 /* Disable Halt on Parity error/ ATN asserted */ +#define SIOP_SXFER_TP 0x70 /* Synch Transfer Period */ + /* see specs for formulas: + Period = TCP * (4 + XFERP ) + TCP = 1 + CLK + 1..2; + */ +#define SIOP_SXFER_MO 0x0f /* Synch Max Offset */ +# define SIOP_MAX_OFFSET 8 + +/* Scsi output data latch register (sodl) */ + +/* Scsi output control latch register (socl) */ + +#define SIOP_REQ 0x80 /* SCSI signal <x> asserted */ +#define SIOP_ACK 0x40 +#define SIOP_BSY 0x20 +#define SIOP_SEL 0x10 +#define SIOP_ATN 0x08 +#define SIOP_MSG 0x04 +#define SIOP_CD 0x02 +#define SIOP_IO 0x01 + +#define SIOP_PHASE(socl) SCSI_PHASE(socl) + +/* Scsi first byte received register (sfbr) */ + +/* Scsi input data latch register (sidl) */ + +/* Scsi bus data lines register (sbdl) */ + +/* Scsi bus control lines register (sbcl). Same as socl */ + +/* DMA status register (dstat) */ + +#define SIOP_DSTAT_DFE 0x80 /* DMA FIFO empty */ +#define SIOP_DSTAT_RES 0x40 +#define SIOP_DSTAT_BF 0x20 /* Bus fault */ +#define SIOP_DSTAT_ABRT 0x10 /* Aborted */ +#define SIOP_DSTAT_SSI 0x08 /* SCRIPT Single Step */ +#define SIOP_DSTAT_SIR 0x04 /* SCRIPT Interrupt Instruction */ +#define SIOP_DSTAT_WTD 0x02 /* Watchdog Timeout Detected */ +#define SIOP_DSTAT_IID 0x01 /* Invalid Instruction Detected */ + +/* Scsi status register 0 (sstat0) */ + +#define SIOP_SSTAT0_M_A 0x80 /* Phase Mismatch or ATN active */ +#define SIOP_SSTAT0_FCMP 0x40 /* Function Complete */ +#define SIOP_SSTAT0_STO 0x20 /* (Re)Selection timeout */ +#define SIOP_SSTAT0_SEL 0x10 /* (Re)Selected */ +#define SIOP_SSTAT0_SGE 0x08 /* SCSI Gross Error */ +#define SIOP_SSTAT0_UDC 0x04 /* Unexpected Disconnect */ +#define SIOP_SSTAT0_RST 0x02 /* RST asserted */ +#define SIOP_SSTAT0_PAR 0x01 /* Parity Error */ + +/* Scsi status register 1 (sstat1) */ + +#define SIOP_SSTAT1_ILF 0x80 /* Input latch (sidl) full */ +#define SIOP_SSTAT1_ORF 0x40 /* output reg (sodr) full */ +#define SIOP_SSTAT1_OLF 0x20 /* output latch (sodl) full */ +#define SIOP_SSTAT1_AIP 0x10 /* Arbitration in progress */ +#define SIOP_SSTAT1_LOA 0x08 /* Lost arbitration */ +#define SIOP_SSTAT1_WOA 0x04 /* Won arbitration */ +#define SIOP_SSTAT1_RST 0x02 /* SCSI RST current value */ +#define SIOP_SSTAT1_SDP 0x01 /* SCSI SDP current value */ + +/* Scsi status register 2 (sstat2) */ + +#define SIOP_SSTAT2_FF 0xf0 /* SCSI FIFO flags (bytecount) */ +# define SIOP_SCSI_FIFO_DEEP 8 +#define SIOP_SSTAT2_SDP 0x08 /* Latched (on REQ) SCSI SDP */ +#define SIOP_SSTAT2_MSG 0x04 /* Latched SCSI phase */ +#define SIOP_SSTAT2_CD 0x02 +#define SIOP_SSTAT2_IO 0x01 + +/* Chip test register 0 (ctest0) */ + +#define SIOP_CTEST0_RES0 0x80 +#define SIOP_CTEST0_BTD 0x40 /* Byte-to-byte Timer Disable */ +#define SIOP_CTEST0_GRP 0x20 /* Generate Receive Parity for Passthrough */ +#define SIOP_CTEST0_EAN 0x10 /* Enable Active Negation */ +#define SIOP_CTEST0_HSC 0x08 /* Halt SCSI clock */ +#define SIOP_CTEST0_ERF 0x04 /* Extend REQ/ACK Filtering */ +#define SIOP_CTEST0_RES1 0x02 +#define SIOP_CTEST0_DDIR 0x01 /* Xfer direction (1-> from SCSI bus) */ + +/* Chip test register 1 (ctest1) */ + +#define SIOP_CTEST1_FMT 0xf0 /* Byte empty in DMA FIFO bottom (high->byte3) */ +#define SIOP_CTEST1_FFL 0x0f /* Byte full in DMA FIFO top, same */ + +/* Chip test register 2 (ctest2) */ + +#define SIOP_CTEST2_RES 0x80 +#define SIOP_CTEST2_SIGP 0x40 /* Signal process */ +#define SIOP_CTEST2_SOFF 0x20 /* Synch Offset compare (1-> zero Init, max Tgt */ +#define SIOP_CTEST2_SFP 0x10 /* SCSI FIFO Parity */ +#define SIOP_CTEST2_DFP 0x08 /* DMA FIFO Parity */ +#define SIOP_CTEST2_TEOP 0x04 /* True EOP (a-la 5380) */ +#define SIOP_CTEST2_DREQ 0x02 /* DREQ status */ +#define SIOP_CTEST2_DACK 0x01 /* DACK status */ + +/* Chip test register 3 (ctest3) read-only, top of SCSI FIFO */ + +/* Chip test register 4 (ctest4) */ + +#define SIOP_CTEST4_MUX 0x80 /* Host bus multiplex mode */ +#define SIOP_CTEST4_ZMOD 0x40 /* High-impedance outputs */ +#define SIOP_CTEST4_SZM 0x20 /* ditto, SCSI "outputs" */ +#define SIOP_CTEST4_SLBE 0x10 /* SCSI loobpack enable */ +#define SIOP_CTEST4_SFWR 0x08 /* SCSI FIFO write enable (from sodl) */ +#define SIOP_CTEST4_FBL 0x07 /* DMA FIFO Byte Lane select (from ctest6) + 4->0, .. 7->3 */ + +/* Chip test register 5 (ctest5) */ + +#define SIOP_CTEST5_ADCK 0x80 /* Clock Address Incrementor */ +#define SIOP_CTEST5_BBCK 0x40 /* Clock Byte counter */ +#define SIOP_CTEST5_ROFF 0x20 /* Reset SCSI offset */ +#define SIOP_CTEST5_MASR 0x10 /* Master set/reset pulses (of bits 3-0) */ +#define SIOP_CTEST5_DDIR 0x08 /* (re)set internal DMA direction */ +#define SIOP_CTEST5_EOP 0x04 /* (re)set internal EOP */ +#define SIOP_CTEST5_DREQ 0x02 /* (re)set internal REQ */ +#define SIOP_CTEST5_DACK 0x01 /* (re)set internal ACK */ + +/* Chip test register 6 (ctest6) DMA FIFO access */ + +/* Chip test register 7 (ctest7) */ + +#define SIOP_CTEST7_CDIS 0x80 /* Cache burst disable */ +#define SIOP_CTEST7_SC1 0x40 /* Snoop control 1 */ +#define SIOP_CTEST7_SC0 0x20 /* Snoop contorl 0 */ +#define SIOP_CTEST7_INHIBIT (0 << 5) +#define SIOP_CTEST7_SNOOP (1 << 5) +#define SIOP_CTEST7_INVAL (2 << 5) +#define SIOP_CTEST7_RESV (3 << 5) +#define SIOP_CTEST7_STD 0x10 /* Selection timeout disable */ +#define SIOP_CTEST7_DFP 0x08 /* DMA FIFO parity bit */ +#define SIOP_CTEST7_EVP 0x04 /* Even parity (to host bus) */ +#define SIOP_CTEST7_TT1 0x02 /* Transfer type bit */ +#define SIOP_CTEST7_DIFF 0x01 /* Differential mode */ + +/* DMA FIFO register (dfifo) */ + +#define SIOP_DFIFO_RES 0x80 +#define SIOP_DFIFO_BO 0x7f /* FIFO byte offset counter */ + +/* Interrupt status register (istat) */ + +#define SIOP_ISTAT_ABRT 0x80 /* Abort operation */ +#define SIOP_ISTAT_RST 0x40 /* Software reset */ +#define SIOP_ISTAT_SIGP 0x20 /* Signal process */ +#define SIOP_ISTAT_RES 0x10 +#define SIOP_ISTAT_CON 0x08 /* Connected */ +#define SIOP_ISTAT_RES1 0x04 +#define SIOP_ISTAT_SIP 0x02 /* SCSI Interrupt pending */ +#define SIOP_ISTAT_DIP 0x01 /* DMA Interrupt pending */ + +/* Chip test register 8 (ctest8) + +#define SIOP_CTEST8_V 0xf0 /* Chip revision level */ +#define SIOP_CTEST8_FLF 0x08 /* Flush DMA FIFO */ +#define SIOP_CTEST8_CLF 0x04 /* Clear DMA and SCSI FIFOs */ +#define SIOP_CTEST8_FM 0x02 /* Fetch pin mode */ +#define SIOP_CTEST8_SM 0x01 /* Snoop pins mode */ + +/* DMA Mode register (dmode) */ + +#define SIOP_DMODE_BL_MASK 0xc0 /* 0->1 1->2 2->4 3->8 */ +#define SIOP_DMODE_FC 0x30 /* Function code */ +#define SIOP_DMODE_PD 0x08 /* Program/data */ +#define SIOP_DMODE_FAM 0x04 /* Fixed address mode */ +#define SIOP_DMODE_U0 0x02 /* User programmable transfer type */ +#define SIOP_DMODE_MAN 0x01 /* Manual start mode */ + +/* DMA interrupt enable register (dien) */ + +#define SIOP_DIEN_RES 0xc0 +#define SIOP_DIEN_BF 0x20 /* On Bus Fault */ +#define SIOP_DIEN_ABRT 0x10 /* On Abort */ +#define SIOP_DIEN_SSI 0x08 /* On SCRIPTS sstep */ +#define SIOP_DIEN_SIR 0x04 /* On SCRIPTS intr instruction */ +#define SIOP_DIEN_WTD 0x02 /* On watchdog timeout */ +#define SIOP_DIEN_IID 0x01 /* On illegal instruction detected */ + +/* DMA control register (dcntl) */ + +#define SIOP_DCNTL_CF_MASK 0xc0 /* Clock frequency dividers: + 0 --> 37.51..50.00 Mhz, div=2 + 1 --> 25.01..37.50 Mhz, div=1.5 + 2 --> 16.67..25.00 Mhz, div=1 + 3 --> 50.01..66.67 Mhz, div=3 + */ +#define SIOP_DCNTL_EA 0x20 /* Enable ack */ +#define SIOP_DCNTL_SSM 0x10 /* Single step mode */ +#define SIOP_DCNTL_LLM 0x08 /* Enable SCSI Low-level mode */ +#define SIOP_DCNTL_STD 0x04 /* Start DMA operation */ +#define SIOP_DCNTL_FA 0x02 /* Fast arbitration */ +#define SIOP_DCNTL_COM 0x01 /* 53C700 compatibility */ diff --git a/sys/arch/mvme68k/dev/siopvar.h b/sys/arch/mvme68k/dev/siopvar.h new file mode 100644 index 00000000000..af0b3c2fc22 --- /dev/null +++ b/sys/arch/mvme68k/dev/siopvar.h @@ -0,0 +1,201 @@ +/* $NetBSD: siopvar.h,v 1.11 1995/08/18 15:28:14 chopps Exp $ */ + +/* + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson of Lawrence Berkeley Laboratory. + * + * 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. + * + * @(#)siopvar.h 7.1 (Berkeley) 5/8/90 + */ +#ifndef _SIOPVAR_H_ +#define _SIOPVAR_H_ + +/* + * The largest single request will be MAXPHYS bytes which will require + * at most MAXPHYS/NBPG+1 chain elements to describe, i.e. if none of + * the buffer pages are physically contiguous (MAXPHYS/NBPG) and the + * buffer is not page aligned (+1). + */ +#define DMAMAXIO (MAXPHYS/NBPG+1) + +/* + * Data Structure for SCRIPTS program + */ +struct siop_ds { +/*00*/ long scsi_addr; /* SCSI ID & sync */ +/*04*/ long idlen; /* Identify message */ +/*08*/ char *idbuf; +/*0c*/ long cmdlen; /* SCSI command */ +/*10*/ char *cmdbuf; +/*14*/ long stslen; /* Status */ +/*18*/ char *stsbuf; +/*1c*/ long msglen; /* Message */ +/*20*/ char *msgbuf; +/*24*/ long msginlen; /* Message in */ +/*28*/ char *msginbuf; +/*2c*/ long extmsglen; /* Extended message in */ +/*30*/ char *extmsgbuf; +/*34*/ long synmsglen; /* Sync transfer request */ +/*38*/ char *synmsgbuf; + struct { +/*3c*/ long datalen; +/*40*/ char *databuf; + } chain[DMAMAXIO]; +}; + +/* + * ACB. Holds additional information for each SCSI command Comments: We + * need a separate scsi command block because we may need to overwrite it + * with a request sense command. Basicly, we refrain from fiddling with + * the scsi_xfer struct (except do the expected updating of return values). + * We'll generally update: xs->{flags,resid,error,sense,status} and + * occasionally xs->retries. + */ +struct siop_acb { +/*00*/ TAILQ_ENTRY(siop_acb) chain; +/*08*/ struct scsi_xfer *xs; /* SCSI xfer ctrl block from above */ +/*0c*/ int flags; /* Status */ +#define ACB_FREE 0x00 +#define ACB_ACTIVE 0x01 +#define ACB_DONE 0x04 +#define ACB_CHKSENSE 0x08 +/*10*/ struct scsi_generic cmd; /* SCSI command block */ +/*1c*/ struct siop_ds ds; +/*a0*/ void *iob_buf; +/*a4*/ u_long iob_curbuf; +/*a8*/ u_long iob_len, iob_curlen; +/*b0*/ u_char msgout[6]; +/*b6*/ u_char msg[6]; +/*bc*/ u_char stat[1]; +/*bd*/ u_char status; +/*be*/ u_char dummy[2]; +/*c0*/ int clen; +/*c4*/ char *daddr; /* Saved data pointer */ +/*c8*/ int dleft; /* Residue */ +}; + +/* + * Some info about each (possible) target on the SCSI bus. This should + * probably have been a "per target+lunit" structure, but we'll leave it at + * this for now. Is there a way to reliably hook it up to sc->fordriver?? + */ +struct siop_tinfo { + int cmds; /* #commands processed */ + int dconns; /* #disconnects */ + int touts; /* #timeouts */ + int perrs; /* #parity errors */ + int senses; /* #request sense commands sent */ + ushort lubusy; /* What local units/subr. are busy? */ + u_char flags; + u_char period; /* Period suggestion */ + u_char offset; /* Offset suggestion */ +} tinfo_t; + +struct siop_softc { + struct device sc_dev; + struct intrhand sc_ih; + struct evcnt sc_intrcnt; + + u_char sc_istat; + u_char sc_dstat; + u_char sc_sstat0; + u_char sc_sstat1; + u_long sc_intcode; + struct scsi_link sc_link; /* proto for sub devices */ + u_long sc_scriptspa; /* physical address of scripts */ + siop_regmap_p sc_siopp; /* the SIOP */ + u_long sc_active; /* number of active I/O's */ + + /* Lists of command blocks */ + TAILQ_HEAD(acb_list, siop_acb) free_list, + ready_list, + nexus_list; + + struct siop_acb *sc_nexus; /* current command */ + struct siop_acb sc_acb[8]; /* the real command blocks */ + struct siop_tinfo sc_tinfo[8]; + + u_short sc_clock_freq; + u_char sc_dcntl; + u_char sc_ctest7; + u_short sc_tcp[4]; + u_char sc_flags; + u_char sc_sien; + u_char sc_dien; + u_char sc_minsync; + /* one for each target */ + struct syncpar { + u_char state; + u_char sxfer; + u_char sbcl; + } sc_sync[8]; +}; + +/* sc_flags */ +#define SIOP_INTSOFF 0x80 /* Interrupts turned off */ +#define SIOP_INTDEFER 0x40 /* Level 6 interrupt has been deferred */ +#define SIOP_ALIVE 0x01 /* controller initialized */ +#define SIOP_SELECTED 0x04 /* bus is in selected state. Needed for + correct abort procedure. */ + +/* sync states */ +#define SYNC_START 0 /* no sync handshake started */ +#define SYNC_SENT 1 /* we sent sync request, no answer yet */ +#define SYNC_DONE 2 /* target accepted our (or inferior) settings, + or it rejected the request and we stay async */ + +#define MSG_CMD_COMPLETE 0x00 +#define MSG_EXT_MESSAGE 0x01 +#define MSG_SAVE_DATA_PTR 0x02 +#define MSG_RESTORE_PTR 0x03 +#define MSG_DISCONNECT 0x04 +#define MSG_INIT_DETECT_ERROR 0x05 +#define MSG_ABORT 0x06 +#define MSG_REJECT 0x07 +#define MSG_NOOP 0x08 +#define MSG_PARITY_ERROR 0x09 +#define MSG_BUS_DEVICE_RESET 0x0C +#define MSG_IDENTIFY 0x80 +#define MSG_IDENTIFY_DR 0xc0 /* (disconnect/reconnect allowed) */ +#define MSG_SYNC_REQ 0x01 + +#define STS_CHECKCOND 0x02 /* Check Condition (ie., read sense) */ +#define STS_CONDMET 0x04 /* Condition Met (ie., search worked) */ +#define STS_BUSY 0x08 +#define STS_INTERMED 0x10 /* Intermediate status sent */ +#define STS_EXT 0x80 /* Extended status valid */ + +void siop_minphys __P((struct buf *bp)); +int siop_scsicmd __P((struct scsi_xfer *)); + +#endif /* _SIOPVAR_H */ diff --git a/sys/arch/mvme68k/dev/sram.c b/sys/arch/mvme68k/dev/sram.c new file mode 100644 index 00000000000..77299175e89 --- /dev/null +++ b/sys/arch/mvme68k/dev/sram.c @@ -0,0 +1,231 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/ioctl.h> +#include <sys/buf.h> +#include <sys/systm.h> +#include <sys/uio.h> +#include <sys/malloc.h> + +#include <sys/device.h> +#include <machine/cpu.h> +#include <machine/autoconf.h> +#include <machine/mioctl.h> + +#include "mc.h" + +#if NMC > 0 +#include <mvme68k/dev/mcreg.h> +#endif + +#include <vm/vm.h> + +struct sramsoftc { + struct device sc_dev; + caddr_t sc_paddr; + caddr_t sc_vaddr; + int sc_len; +}; + +void sramattach __P((struct device *, struct device *, void *)); +int srammatch __P((struct device *, void *, void *)); + +struct cfdriver sramcd = { + NULL, "sram", srammatch, sramattach, + DV_DULL, sizeof(struct sramsoftc), 0 +}; + +int +srammatch(parent, vcf, args) + struct device *parent; + void *vcf, *args; +{ + struct cfdata *cf = vcf; + struct confargs *ca = args; + + if (cputyp == CPU_147) + return (0); + if (ca->ca_vaddr == (caddr_t)-1) + return (!badpaddr(ca->ca_paddr, 1)); + return (!badvaddr(ca->ca_vaddr, 1)); +} + +void +sramattach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct confargs *ca = args; + struct sramsoftc *sc = (struct sramsoftc *)self; + struct mcreg *mc; + int i; + + switch (cputyp) { +#ifdef MVME162 + case CPU_162: + /* XXX this code will almost never be used. just in case. */ + mc = sys_mc; + if (!mc) + mc = (struct mcreg *)(IIOV(0xfff00000) + MC_MCCHIP_OFF); + + switch (mc->mc_memoptions & MC_MEMOPTIONS_SRAMMASK) { + case MC_MEMOPTIONS_SRAM128K: + sc->sc_len = 128*1024; + break; + case MC_MEMOPTIONS_SRAM512K: + sc->sc_len = 512*1024; + break; + case MC_MEMOPTIONS_SRAM1M: + sc->sc_len = 1024*1024; + break; + case MC_MEMOPTIONS_SRAM2M: + sc->sc_len = 2048*1024; + break; + } + break; +#endif +#ifdef MVME167 + case CPU_167: + case CPU_166: + sc->sc_len = 128*1024; /* always 128K */ + break; +#endif +#ifdef MVME177 + case CPU_177: + XXX + break; +#endif + default: + sc->sc_len = 0; + break; + } + + printf(": len %d", sc->sc_len); + + sc->sc_paddr = ca->ca_paddr; + sc->sc_vaddr = mapiodev((caddr_t)sc->sc_paddr, sc->sc_len); + if (sc->sc_vaddr == NULL) { + sc->sc_len = 0; + printf(" -- failed to map"); + } + printf("\n"); +} + +/*ARGSUSED*/ +int +sramopen(dev, flag, mode) + dev_t dev; + int flag, mode; +{ + if (minor(dev) >= sramcd.cd_ndevs || + sramcd.cd_devs[minor(dev)] == NULL) + return (ENODEV); + return (0); +} + +/*ARGSUSED*/ +int +sramclose(dev, flag, mode) + dev_t dev; + int flag, mode; +{ + + return (0); +} + +/*ARGSUSED*/ +int +sramioctl(dev, cmd, data, flag, p) + dev_t dev; + caddr_t data; + int cmd, flag; + struct proc *p; +{ + int unit = minor(dev); + struct sramsoftc *sc = (struct sramsoftc *) sramcd.cd_devs[unit]; + int error = 0; + + switch (cmd) { + case MIOCGSIZ: + *(int *)data = sc->sc_len; + break; + default: + error = ENOTTY; + break; + } + return (error); +} + +/*ARGSUSED*/ +int +sramread(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + int unit = minor(dev); + struct sramsoftc *sc = (struct sramsoftc *) sramcd.cd_devs[unit]; + + return (memdevrw(sc->sc_vaddr, sc->sc_len, uio, flags)); +} + +/*ARGSUSED*/ +int +sramwrite(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + int unit = minor(dev); + struct sramsoftc *sc = (struct sramsoftc *) sramcd.cd_devs[unit]; + + return (memdevrw(sc->sc_vaddr, sc->sc_len, uio, flags)); +} + +int +srammmap(dev, off, prot) + dev_t dev; + int off, prot; +{ + int unit = minor(dev); + struct sramsoftc *sc = (struct sramsoftc *) sramcd.cd_devs[unit]; + + if (minor(dev) != 0) + return (-1); + + /* allow access only in RAM */ + if (off > sc->sc_len) + return (-1); + return (m68k_btop(sc->sc_paddr + off)); +} diff --git a/sys/arch/mvme68k/dev/vme.c b/sys/arch/mvme68k/dev/vme.c new file mode 100644 index 00000000000..ddd4099dd63 --- /dev/null +++ b/sys/arch/mvme68k/dev/vme.c @@ -0,0 +1,557 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/ioctl.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/tty.h> +#include <sys/uio.h> +#include <sys/callout.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/syslog.h> +#include <sys/fcntl.h> +#include <sys/device.h> +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <mvme68k/dev/vme.h> + +#include "pcc.h" +#include "mc.h" +#include "pcctwo.h" + +#if NPCC > 0 +#include <mvme68k/dev/pccreg.h> +#endif +#if NMC > 0 +#include <mvme68k/dev/mcreg.h> +#endif +#if NPCCTWO > 0 +#include <mvme68k/dev/pcctworeg.h> +#endif + +int vmematch __P((struct device *, void *, void *)); +void vmeattach __P((struct device *, struct device *, void *)); + +int vme1chip_init __P((struct vmesoftc *sc)); +int vme2chip_init __P((struct vmesoftc *sc)); +u_long vme2chip_map __P((u_long base, int len, int dwidth)); +int vme2abort __P((struct frame *frame)); + +static int vmebustype; + +struct cfdriver vmecd = { + NULL, "vme", vmematch, vmeattach, + DV_DULL, sizeof(struct vmesoftc), 0 +}; + +int +vmematch(parent, cf, args) + struct device *parent; + void *cf; + void *args; +{ + struct confargs *ca = args; + +#if NMC > 0 + if (ca->ca_bustype == BUS_MC) { + struct mcreg *mc = (struct mcreg *)ca->ca_master; + + if (mc->mc_ver & MC_VER_NOVME) + return (0); + } +#endif + return (1); +} + +/* + * Returns a physical address mapping for a VME address & length. + * Note: on some hardware it is not possible to create certain + * mappings, ie. the MVME147 cannot do 32 bit accesses to VME bus + * addresses from 0 to physmem. + */ +caddr_t +vmepmap(sc, vmeaddr, len, bustype) + struct vmesoftc *sc; + caddr_t vmeaddr; + int len; + int bustype; +{ + u_long base = (u_long)vmeaddr; + + len = roundup(len, NBPG); + switch (vmebustype) { +#if NPCC > 0 + case BUS_PCC: + switch (bustype) { + case BUS_VMES: + if (base > VME1_A16BASE && + (base+len - VME1_A16BASE) < VME1_A16D16LEN) + base = base - VME1_A16BASE + VME1_A16D16BASE; + else if (base+len < VME1_A32D16LEN) + base = base + VME1_A32D16BASE; + else { + printf("%s: cannot map pa %x len %x\n", + sc->sc_dev.dv_xname, base, len); + return (NULL); + } + break; + case BUS_VMEL: + if (base >= physmem && (base+len) < VME1_A32D32LEN) + base = base + VME1_A32D32BASE; + else if (base+len < VME1_A32D16LEN) /* HACK! */ + base = base + VME1_A32D16BASE; + else { + printf("%s: cannot map pa %x len %x\n", + sc->sc_dev.dv_xname, base, len); + return (NULL); + } + break; + } + break; +#endif +#if NMC > 0 || NPCCTWO > 0 + case BUS_MC: + case BUS_PCCTWO: + switch (bustype) { + case BUS_VMES: + if (base > VME2_A16BASE && + (base+len-VME2_A16BASE) < VME2_A16D16LEN) + base = base - VME2_A16BASE + VME2_A16D16BASE; + else if (base > VME2_A24BASE && + (base+len-VME2_A24BASE) < VME2_A24D16LEN) + base = base - VME2_A24BASE + VME2_A24D16BASE; + else if ((base+len) < VME2_A32D16LEN) + base = base + VME2_A32D16BASE; + else { + base = vme2chip_map(base, len, 16); + if (base == NULL) + return (NULL); + } + break; + case BUS_VMEL: +#if 0 + if (base > VME2_A16BASE && + (base+len-VME2_A16BASE) < VME2_A16D32LEN) + base = base - VME2_A16BASE + VME2_A16D32BASE; +#endif + base = vme2chip_map(base, len, 32); + if (base == NULL) + return (NULL); + break; + } + break; +#endif + } + return ((caddr_t)base); +} + +/* if successful, returns the va of a vme bus mapping */ +caddr_t +vmemap(sc, vmeaddr, len, bustype) + struct vmesoftc *sc; + caddr_t vmeaddr; + int len; + int bustype; +{ + caddr_t pa, va; + + pa = vmepmap(sc, pa, len, bustype); + if (pa == NULL) + return (NULL); + va = mapiodev(pa, len); + return (va); +} + +void +vmeunmap(va, len) + caddr_t va; + int len; +{ + unmapiodev(va, len); +} + +int +vmerw(sc, uio, flags, bus) + struct vmesoftc *sc; + struct uio *uio; + int flags; + int bus; +{ + register vm_offset_t o, v; + register int c; + register struct iovec *iov; + caddr_t vme; + int error = 0; + + while (uio->uio_resid > 0 && error == 0) { + iov = uio->uio_iov; + if (iov->iov_len == 0) { + uio->uio_iov++; + uio->uio_iovcnt--; + if (uio->uio_iovcnt < 0) + panic("vmerw"); + continue; + } + + v = uio->uio_offset; + c = min(iov->iov_len, MAXPHYS); + if ((v & PGOFSET) + c > NBPG) /* max NBPG at a time */ + c = NBPG - (v & PGOFSET); + if (c == 0) + return (0); + vme = vmemap(sc, (caddr_t)(v & ~PGOFSET), + NBPG, BUS_VMES); + if (vme == NULL) { + error = EFAULT; /* XXX? */ + continue; + } + error = uiomove((caddr_t)vme + (v & PGOFSET), c, uio); + vmeunmap(vme, NBPG); + } + return (error); +} + +int +vmeprint(args, bus) + void *args; + char *bus; +{ + struct confargs *ca = args; + + printf(" offset 0x%x", ca->ca_offset); + if (ca->ca_vec > 0) + printf(" vec %d", ca->ca_vec); + if (ca->ca_ipl > 0) + printf(" ipl %d", ca->ca_ipl); + return (UNCONF); +} + +int +vmescan(parent, child, args, bustype) + struct device *parent; + void *child, *args; + int bustype; +{ + struct cfdata *cf = child; + struct vmesoftc *sc = (struct vmesoftc *)parent; + struct confargs *ca = args; + struct confargs oca; + + if (parent->dv_cfdata->cf_driver->cd_indirect) { + printf(" indirect devices not supported\n"); + return 0; + } + + bzero(&oca, sizeof oca); + oca.ca_bustype = bustype; + oca.ca_paddr = (void *)cf->cf_loc[0]; + oca.ca_len = cf->cf_loc[1]; + oca.ca_vec = cf->cf_loc[2]; + oca.ca_ipl = cf->cf_loc[3]; + if (oca.ca_ipl > 0 && oca.ca_vec == -1) + oca.ca_vec = intr_freevec(); + + oca.ca_offset = (int)oca.ca_paddr; + oca.ca_vaddr = (void *)vmemap(sc, oca.ca_paddr, oca.ca_len, + oca.ca_bustype); + if (!oca.ca_vaddr) + oca.ca_vaddr = (void *)-1; + oca.ca_master = (void *)sc; + oca.ca_name = cf->cf_driver->cd_name; + if ((*cf->cf_driver->cd_match)(parent, cf, &oca) == 0) { + if (oca.ca_vaddr != (void *)-1) + vmeunmap(oca.ca_vaddr, oca.ca_len); + return (0); + } + config_attach(parent, cf, &oca, vmeprint); + return (1); +} + +void +vmeattach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct vmesoftc *sc = (struct vmesoftc *)self; + struct confargs *ca = args; + struct vme1reg *vme1; + struct vme2reg *vme2; + int scon; + + sc->sc_vaddr = ca->ca_vaddr; + + vmebustype = ca->ca_bustype; + switch (ca->ca_bustype) { +#if NPCC > 0 + case BUS_PCC: + vme1 = (struct vme1reg *)sc->sc_vaddr; + scon = (vme1->vme1_scon & VME1_SCON_SWITCH); + printf(": %sscon\n", scon ? "" : "not "); + vme1chip_init(sc); + break; +#endif +#if NMC > 0 || NPCCTWO > 0 + case BUS_MC: + case BUS_PCCTWO: + vme2 = (struct vme2reg *)sc->sc_vaddr; + scon = (vme2->vme2_tctl & VME2_TCTL_SCON); + printf(": %sscon\n", scon ? "" : "not "); + vme2chip_init(sc); + break; +#endif + } + + while (config_found(self, NULL, NULL)) + ; +} + +/* + * On the VMEbus, only one cpu may be configured to respond to any + * particular vme ipl. Therefore, it wouldn't make sense to globally + * enable all the interrupts all the time -- it would not be possible + * to put two cpu's and one vme card into a single cage. Rather, we + * enable each vme interrupt only when we are attaching a device that + * uses it. This makes it easier (though not trivial) to put two cpu + * cards in one VME cage, and both can have some limited access to vme + * interrupts (just can't share the same irq). + * Obviously no check is made to see if another cpu is using that + * interrupt. If you share you will lose. + */ +int +vmeintr_establish(vec, ih) + int vec; + struct intrhand *ih; +{ + struct vmesoftc *sc = (struct vmesoftc *) vmecd.cd_devs[0]; +#if NPCC > 0 + struct vme1reg *vme1; +#endif +#if NMC > 0 || NPCCTWO > 0 + struct vme2reg *vme2; +#endif + int x; + + x = (intr_establish(vec, ih)); + + switch (vmebustype) { +#if NPCC > 0 + case BUS_PCC: + vme1 = (struct vme1reg *)sc->sc_vaddr; + vme1->vme1_irqen = vme1->vme1_irqen | + VME1_IRQ_VME(ih->ih_ipl); + break; +#endif +#if NMC > 0 || NPCCTWO > 0 + case BUS_MC: + case BUS_PCCTWO: + vme2 = (struct vme2reg *)sc->sc_vaddr; + vme2->vme2_irqen = vme2->vme2_irqen | + VME2_IRQ_VME(ih->ih_ipl); + break; +#endif + } + return (x); +} + +#if defined(MVME147) +int +vme1chip_init(sc) + struct vmesoftc *sc; +{ + struct vme1reg *vme1 = (struct vme1reg *)sc->sc_vaddr; + + vme1->vme1_scon &= ~VME1_SCON_SYSFAIL; /* XXX doesn't work */ +} +#endif + +#if defined(MVME162) || defined(MVME167) || defined(MVME177) + +/* + * make local addresses 1G-2G correspond to VME addresses 3G-4G, + * as D32 + */ +#define VME2_D32STARTPHYS (1*1024*1024*1024UL) +#define VME2_D32ENDPHYS (2*1024*1024*1024UL) +#define VME2_D32STARTVME (3*1024*1024*1024UL) +#define VME2_D32BITSVME (3*1024*1024*1024UL) + +/* + * make local addresses 3G-3.75G correspond to VME addresses 3G-3.75G, + * as D16 + */ +#define VME2_D16STARTPHYS (3*1024*1024*1024UL) +#define VME2_D16ENDPHYS (3*1024*1024*1024UL + 768*1024*1024UL) + +/* + * XXX what AM bits should be used for the D32/D16 mappings? + */ +int +vme2chip_init(sc) + struct vmesoftc *sc; +{ + struct vme2reg *vme2 = (struct vme2reg *)sc->sc_vaddr; + u_long ctl; + + /* turn off SYSFAIL LED */ + vme2->vme2_tctl &= ~VME2_TCTL_SYSFAIL; + + ctl = vme2->vme2_masterctl; + +#if 0 + /* unused decoders 1 & 2 */ + printf("%s: phys 0x%08x-0x%08x to VMExxx 0x%08x-0x%08x\n", + sc->sc_dev.dv_xname, + vme2->vme2_master1 << 16, vme2->vme2_master1 & 0xffff0000, + vme2->vme2_master1 << 16, vme2->vme2_master1 & 0xffff0000); + printf("%s: phys 0x%08x-0x%08x to VMExxx 0x%08x-0x%08x\n", + sc->sc_dev.dv_xname, + vme2->vme2_master2 << 16, vme2->vme2_master2 & 0xffff0000, + vme2->vme2_master2 << 16, vme2->vme2_master2 & 0xffff0000); +#endif + + /* setup a D16 space */ + vme2->vme2_master3 = ((VME2_D16ENDPHYS-1) & 0xffff0000) | + (VME2_D16STARTPHYS >> 16); + ctl &= ~(VME2_MASTERCTL_ALL << VME2_MASTERCTL_3SHIFT); + ctl |= (VME2_MASTERCTL_AM32SP | VME2_MASTERCTL_D16) << + VME2_MASTERCTL_3SHIFT; +#if 0 + printf("%s: phys 0x%08x-0x%08x to VMED16 0x%08x-0x%08x\n", + sc->sc_dev.dv_xname, + VME2_D16STARTPHYS, VME2_D16ENDPHYS-1, + VME2_D16STARTPHYS, VME2_D16ENDPHYS-1); +#endif + + /* setup a D32 space */ + vme2->vme2_master4 = ((VME2_D32ENDPHYS-1) & 0xffff0000) | + (VME2_D32STARTPHYS >> 16); + vme2->vme2_master4mod = (VME2_D32STARTVME & 0xffff0000) | + (VME2_D32BITSVME >> 16); + ctl &= ~(VME2_MASTERCTL_ALL << VME2_MASTERCTL_4SHIFT); + ctl |= (VME2_MASTERCTL_AM32SP) << + VME2_MASTERCTL_4SHIFT; +#if 0 + printf("%s: phys 0x%08x-0x%08x to VMED32 0x%08x-0x%08x\n", + sc->sc_dev.dv_xname, + VME2_D32STARTPHYS, VME2_D32ENDPHYS-1, + VME2_D32STARTVME, VME2_D32STARTVME | ~VME2_D32BITSVME); +#endif + + vme2->vme2_masterctl = ctl; + + ctl = vme2->vme2_gcsrctl; + + /* enable A16 short IO map decoder (0xffffxxxx) */ + ctl &= ~(VME2_GCSRCTL_I1EN | VME2_GCSRCTL_I1D16 | VME2_GCSRCTL_I1WP | + VME2_GCSRCTL_I1SU); + ctl |= VME2_GCSRCTL_I1EN | VME2_GCSRCTL_I1D16 | VME2_GCSRCTL_I1SU; + + /* enable A24D16 (0xf0xxxxxx) and A32D16 (0xf[1-e]xxxxxx) decoders */ + ctl &= ~(VME2_GCSRCTL_I2EN | VME2_GCSRCTL_I2WP | VME2_GCSRCTL_I2SU | + VME2_GCSRCTL_I2PD); + ctl |= VME2_GCSRCTL_I2EN | VME2_GCSRCTL_I2SU | VME2_GCSRCTL_I2PD; + + /* map decoders 3 & 4 which were just configured */ + ctl &= ~(VME2_GCSRCTL_MDEN4 | VME2_GCSRCTL_MDEN3 | VME2_GCSRCTL_MDEN1 | + VME2_GCSRCTL_MDEN2); + ctl |= VME2_GCSRCTL_MDEN4 | VME2_GCSRCTL_MDEN3; + + vme2->vme2_gcsrctl = ctl; + + /* + * Map the VME irq levels to the cpu levels 1:1. + * This is rather inflexible, but much easier. + */ + vme2->vme2_irql4 = (7 << VME2_IRQL4_VME7SHIFT) | + (6 << VME2_IRQL4_VME6SHIFT) | (5 << VME2_IRQL4_VME5SHIFT) | + (4 << VME2_IRQL4_VME4SHIFT) | (3 << VME2_IRQL4_VME3SHIFT) | + (2 << VME2_IRQL4_VME2SHIFT) | (1 << VME2_IRQL4_VME1SHIFT); + +#if NPCCTWO > 0 + if (vmebustype == BUS_PCCTWO) { + sc->sc_abih.ih_fn = vme2abort; + sc->sc_abih.ih_arg = 0; + sc->sc_abih.ih_ipl = 7; + sc->sc_abih.ih_wantframe = 1; + + intr_establish(110, &sc->sc_abih); /* XXX 110 */ + vme2->vme2_irqen |= VME2_IRQ_AB; + } +#endif +} + +/* + * A32 accesses on the MVME1[67]x require setting up mappings in + * the VME2 chip. + * XXX VME address must be between 2G and 4G + * XXX We only support D32 at the moment.. + */ +u_long +vme2chip_map(base, len, dwidth) + u_long base; + int len, dwidth; +{ + switch (dwidth) { + case 16: + if (base < VME2_D16STARTPHYS || + base + (u_long)len > VME2_D16ENDPHYS) + return (NULL); + return (base); + case 32: + if (base < VME2_D32STARTVME) + return (NULL); + return (base - VME2_D32STARTVME + VME2_D32STARTPHYS); + } +} + +#if NPCCTWO > 0 +int +vme2abort(frame) + struct frame *frame; +{ + struct vmesoftc *sc = (struct vmesoftc *)vmecd.cd_devs[0]; + struct vme2reg *vme2 = (struct vme2reg *)sc->sc_vaddr; + + if (vme2->vme2_irqstat & VME2_IRQ_AB == 0) { + printf("%s: vme2chip irq not set\n", sc->sc_dev.dv_xname); + return (0); + } + vme2->vme2_irqclr = VME2_IRQ_AB; + nmihand(frame); + return (1); +} +#endif + +#endif diff --git a/sys/arch/mvme68k/dev/vme.h b/sys/arch/mvme68k/dev/vme.h new file mode 100644 index 00000000000..b49918247aa --- /dev/null +++ b/sys/arch/mvme68k/dev/vme.h @@ -0,0 +1,325 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +struct vmesoftc { + struct device sc_dev; + caddr_t sc_vaddr; + struct intrhand sc_abih; /* `abort' switch */ +}; + +/* + * MVME147 vme configuration registers. +*/ +struct vme1reg { +/*01*/ volatile u_short vme1_scon; +#define VME1_SCON_SWITCH 0x01 /* SCON jumper is set */ +#define VME1_SCON_SRESET 0x02 /* assert SRESET on bus */ +#define VME1_SCON_SYSFAIL 0x04 /* assert SYSFAIL on bus */ +#define VME1_SCON_ROBIN 0x08 /* round robin bus requests */ +/*03*/ volatile u_short vme1_reqconf; +#define VME1_REQ_IPLMASK 0x03 /* interrupt level for requester */ +#define VME1_REQ_RNEVER 0x08 +#define VME1_REQ_RWD 0x10 +#define VME1_REQ_DHB 0x40 +#define VME1_REQ_DWB 0x80 +/*05*/ volatile u_short vme1_masconf; +#define VME1_MAS_D16 0x01 /* force d8/16 accesses only */ +#define VME1_MAS_MASA24 0x02 /* send address mod for A24 access */ +#define VME1_MAS_MASA16 0x04 /* send address mod for A16 access */ +#define VME1_MAS_MASUAT 0x08 /* handle unaligned VME cycles */ +#define VME1_MAS_CFILL 0x10 /* DO NOT USE */ +#define VME1_MAS_MASWP 0x20 /* VME fast mode (DO NOT USE) */ +/*07*/ volatile u_short vme1_slconf; +#define VME1_SLAVE_SLVD16 0x01 /* DO NOT USE */ +#define VME1_SLAVE_SLVWP 0x20 /* DO NOT USE */ +#define VME1_SLAVE_SLVEN 0x80 /* allow access to onboard DRAM */ +/*09*/ volatile u_short vme1_timerconf; +#define VME1_TIMER_LOCAL_MASK 0x03 +#define VME1_TIMER_LOCAL_T0 0x00 /* local timeout 102 microsec */ +#define VME1_TIMER_LOCAL_T1 0x01 /* local timeout 205 microsec */ +#define VME1_TIMER_LOCAL_T2 0x02 /* local timeout 410 microsec */ +#define VME1_TIMER_LOCAL_T3 0x03 /* local timeout disabled */ +#define VME1_TIMER_VMEACC_MASK 0x0c +#define VME1_TIMER_VMEACC_T0 0x00 /* VME access timeout 102 microsec */ +#define VME1_TIMER_VMEACC_T1 0x04 /* VME access timeout 1.6 millisec */ +#define VME1_TIMER_VMEACC_T2 0x08 /* VME access timeout 51 millisec */ +#define VME1_TIMER_VMEACC_T3 0x0c /* VME access timeout disabled */ +#define VME1_TIMER_VMEGLO_MASK 0x30 +#define VME1_TIMER_VMEGLO_T0 0x00 /* VME glob timeout 102 microsec */ +#define VME1_TIMER_VMEGLO_T1 0x10 /* VME glob timeout 205 microsec */ +#define VME1_TIMER_VMEGLO_T2 0x20 /* VME glob timeout 410 microsec */ +#define VME1_TIMER_VMEGLO_T3 0x30 /* VME glob timeout disabled */ +#define VME1_TIMER_ARBTO 0x40 /* enable VME arbitration timer */ +/*0b*/ volatile u_short vme1_sladdrmod; +#define VME1_SLMOD_DATA 0x01 +#define VME1_SLMOD_PRGRM 0x02 +#define VME1_SLMOD_BLOCK 0x04 +#define VME1_SLMOD_SHORT 0x08 +#define VME1_SLMOD_STND 0x10 +#define VME1_SLMOD_EXTED 0x20 +#define VME1_SLMOD_USER 0x40 +#define VME1_SLMOD_SUPER 0x80 +/*0d*/ volatile u_short vme1_msaddrmod; +#define VME1_MSMOD_AM_MASK 0x3f +#define VME1_MSMOD_AMSEL 0x80 +/*0f*/ volatile u_short vme1_irqen; +#define VME1_IRQ_VME(x) (1 << (x)) +/*11*/ volatile u_short vme1_uirqen; +/*13*/ volatile u_short vme1_uirq; +/*15*/ volatile u_short vme1_irq; +/*17*/ volatile u_short vme1_vmeid; +/*19*/ volatile u_short vme1_buserr; +/*1b*/ volatile u_short vme1_gcsr; +#define VME1_GCSR_OFF 0x0f +/*1d*/ u_short :16; +/*1f*/ u_short :16; +/*21*/ volatile u_short vme1_gcsr_gr0; +/*23*/ volatile u_short vme1_gcsr_gr1; +/*25*/ volatile u_short vme1_gcsr_boardid; +/*27*/ volatile u_short vme1_gcsr_gpr0; +/*29*/ volatile u_short vme1_gcsr_gpr1; +/*2b*/ volatile u_short vme1_gcsr_gpr2; +/*2d*/ volatile u_short vme1_gcsr_gpr3; +/*2f*/ volatile u_short vme1_gcsr_gpr4; +}; + +/* + * Basic VME memory layout for the MVME147 follows: + * - A32D32 accesses occur at memsize-0xefffffff. This makes it + * impossible to do A32D32 accesses before the end of your onboard + * memory. If you want to do low address A24D32 accesses, and you + * have 16M or more onboard memory you'll find you cannot. + * - A32D16 accesses can occur at 0xf0000000-0xff7fffff. + * - A16D16 accesses can occur at 0xffff0000-0xffffffff. + */ +#define VME1_A32D32BASE 0x00000000UL +#define VME1_A32D32LEN 0xf0000000UL +#define VME1_A32D16BASE 0xf0000000UL +#define VME1_A32D16LEN 0x0f800000UL +#define VME1_A16D16BASE 0xffff0000UL +#define VME1_A16D16LEN 0x00010000UL +#define VME1_A16BASE 0xffff0000UL + +/* + * XXX: this chip has some rather inane access rules! + */ +struct vme2reg { +/*00*/ volatile u_long vme2_slaveaddr1; +/*04*/ volatile u_long vme2_slaveaddr2; +#define VME2_SADDR_END 0xffff0000 /* VME address END & START */ +#define VME2_SADDR_START 0x0000ffff +/*08*/ volatile u_long vme2_slavelmod1; +/*0c*/ volatile u_long vme2_slavelmod2; +#define VME2_SADDR_LADDR 0xffff0000 /* local base address */ +#define VME2_SADDR_SIZE(mem) (0x1000 - (mem) >> 16) /* encoding of size */ +/*10*/ volatile u_long vme2_slavectl; +#define VME2_SLAVE_CHOOSE(bits, num) ((bits) << (16*((num)-1))) +#define VME2_SLAVECTL_WP 0x00000100 /* write posting */ +#define VME2_SLAVECTL_SNP_NO 0x00000000 /* no snooping */ +#define VME2_SLAVECTL_SNP_SINK 0x00000200 /* sink data */ +#define VME2_SLAVECTL_SNP_INVAL 0x00000400 /* invalidate */ +#define VME2_SLAVECTL_ADDER 0x00000800 /* use adder */ +#define VME2_SLAVECTL_SUP 0x00000080 /* modifier bit */ +#define VME2_SLAVECTL_USR 0x00000040 /* modifier bit */ +#define VME2_SLAVECTL_A32 0x00000020 /* modifier bit */ +#define VME2_SLAVECTL_A24 0x00000010 /* modifier bit */ +#define VME2_SLAVECTL_D64 0x00000008 /* modifier bit */ +#define VME2_SLAVECTL_BLK 0x00000004 /* modifier bit */ +#define VME2_SLAVECTL_PGM 0x00000002 /* modifier bit */ +#define VME2_SLAVECTL_DAT 0x00000001 /* modifier bit */ +/*14*/ volatile u_long vme2_master1; +/*18*/ volatile u_long vme2_master2; +/*1c*/ volatile u_long vme2_master3; +/*20*/ volatile u_long vme2_master4; +/*24*/ volatile u_long vme2_master4mod; +/*28*/ volatile u_long vme2_masterctl; +#define VME2_MASTERCTL_4SHIFT 24 +#define VME2_MASTERCTL_3SHIFT 16 +#define VME2_MASTERCTL_2SHIFT 8 +#define VME2_MASTERCTL_1SHIFT 0 +#define VME2_MASTERCTL_D16 0x80 +#define VME2_MASTERCTL_WP 0x40 +#define VME2_MASTERCTL_AM 0x3f +#define VME2_MASTERCTL_AM24SB 0x3f /* A24 Supervisory Block Transfer */ +#define VME2_MASTERCTL_AM24SP 0x3e /* A24 Supervisory Program Access */ +#define VME2_MASTERCTL_AM24SD 0x3d /* A24 Supervisory Data Access */ +#define VME2_MASTERCTL_AM24UB 0x3b /* A24 Non-priv. Block Transfer */ +#define VME2_MASTERCTL_AM24UP 0x3a /* A24 Non-priv. Program Access */ +#define VME2_MASTERCTL_AM24UD 0x39 /* A24 Non-priv. Data Access */ +#define VME2_MASTERCTL_AM16S 0x2d /* A16 Supervisory Access */ +#define VME2_MASTERCTL_AM16U 0x29 /* A16 Non-priv. Access */ +#define VME2_MASTERCTL_AM32SB 0x0f /* A32 Supervisory Block Transfer */ +#define VME2_MASTERCTL_AM32SP 0x0e /* A32 Supervisory Program Access */ +#define VME2_MASTERCTL_AM32SD 0x0d /* A32 Supervisory Data Access */ +#define VME2_MASTERCTL_AM32UB 0x0b /* A32 Non-priv. Block Transfer */ +#define VME2_MASTERCTL_AM32UP 0x0a /* A32 Non-priv. Program Access */ +#define VME2_MASTERCTL_AM32UD 0x09 /* A32 Non-priv Data Access */ + +#define VME2_MASTERCTL_ALL 0xff +/*2c*/ volatile u_long vme2_gcsrctl; +#define VME2_GCSRCTL_OFF 0xf0000000 +#define VME2_GCSRCTL_MDEN4 0x00080000 +#define VME2_GCSRCTL_MDEN3 0x00040000 +#define VME2_GCSRCTL_MDEN2 0x00020000 +#define VME2_GCSRCTL_MDEN1 0x00010000 +#define VME2_GCSRCTL_I2EN 0x00008000 /* F decode (A24D16/A32D16) on */ +#define VME2_GCSRCTL_I2WP 0x00004000 /* F decode write post */ +#define VME2_GCSRCTL_I2SU 0x00002000 /* F decode is supervisor */ +#define VME2_GCSRCTL_I2PD 0x00001000 /* F decode is program */ +#define VME2_GCSRCTL_I1EN 0x00000800 /* short decode (A16Dx) on */ +#define VME2_GCSRCTL_I1D16 0x00000400 /* short decode is D16 */ +#define VME2_GCSRCTL_I1WP 0x00000200 /* short decode write post */ +#define VME2_GCSRCTL_I1SU 0x00000100 /* short decode is supervisor */ +#define VME2_GCSRCTL_ROMSIZE 0x000000c0 /* size of ROM */ +#define VME2_GCSRCTL_ROMBSPD 0x00000038 /* speed of ROM */ +#define VME2_GCSRCTL_ROMASPD 0x00000007 /* speed of ROM */ +/*30*/ volatile u_long vme2_dmactl; +/*34*/ volatile u_long vme2_dmamode; +/*38*/ volatile u_long vme2_dmaladdr; +/*3c*/ volatile u_long vme2_dmavmeaddr; +/*40*/ volatile u_long vme2_dmacount; +/*44*/ volatile u_long vme2_dmatable; +/*48*/ volatile u_long vme2_dmastat; +/*4c*/ volatile u_long vme2_vmejunk; +/*50*/ volatile u_long vme2_t1cmp; +/*54*/ volatile u_long vme2_t1count; +/*58*/ volatile u_long vme2_t2cmp; +/*5c*/ volatile u_long vme2_t2count; +/*60*/ volatile u_long vme2_tctl; +#define VME2_TCTL_SCON 0x40000000 /* we are SCON */ +#define VME2_TCTL_SYSFAIL 0x20000000 /* light SYSFAIL led */ +#define VME2_TCTL_SRST 0x00800000 /* system reset */ +/*64*/ volatile u_long vme2_prescale; +/*68*/ volatile u_long vme2_irqstat; +/*6c*/ volatile u_long vme2_irqen; +/*70*/ volatile u_long vme2_setsoftirq; /* VME2_IRQ_SWx only */ +/*74*/ volatile u_long vme2_irqclr; /* except VME2_IRQ_VMEx */ +#define VME2_IRQ_ACF 0x80000000 +#define VME2_IRQ_AB 0x40000000 +#define VME2_IRQ_SYSF 0x20000000 +#define VME2_IRQ_MWP 0x10000000 +#define VME2_IRQ_PE 0x08000000 +#define VME2_IRQ_V1IE 0x04000000 +#define VME2_IRQ_TIC2 0x02000000 +#define VME2_IRQ_TIC1 0x01000000 +#define VME2_IRQ_VIA 0x00800000 +#define VME2_IRQ_DMA 0x00400000 +#define VME2_IRQ_SIG3 0x00200000 +#define VME2_IRQ_SIG2 0x00100000 +#define VME2_IRQ_SIG1 0x00080000 +#define VME2_IRQ_SIG0 0x00040000 +#define VME2_IRQ_LM1 0x00020000 +#define VME2_IRQ_LM0 0x00010000 +#define VME2_IRQ_SW7 0x00008000 +#define VME2_IRQ_SW6 0x00004000 +#define VME2_IRQ_SW5 0x00002000 +#define VME2_IRQ_SW4 0x00001000 +#define VME2_IRQ_SW3 0x00000800 +#define VME2_IRQ_SW2 0x00000400 +#define VME2_IRQ_SW1 0x00000200 +#define VME2_IRQ_SW0 0x00000100 +#define VME2_IRQ_SPARE 0x00000080 +#define VME2_IRQ_VME7 0x00000040 +#define VME2_IRQ_VME6 0x00000020 +#define VME2_IRQ_VME5 0x00000010 +#define VME2_IRQ_VME4 0x00000008 +#define VME2_IRQ_VME3 0x00000004 +#define VME2_IRQ_VME2 0x00000002 +#define VME2_IRQ_VME1 0x00000001 +#define VME2_IRQ_VME(x) (1 << ((x) - 1)) +/*78*/ volatile u_long vme2_irql1; +#define VME2_IRQL1_ACFSHIFT 28 +#define VME2_IRQL1_ABSHIFT 24 +#define VME2_IRQL1_SYSFSHIFT 20 +#define VME2_IRQL1_WPESHIFT 16 +#define VME2_IRQL1_PESHIFT 12 +#define VME2_IRQL1_V1IESHIFT 8 +#define VME2_IRQL1_TIC2SHIFT 4 +#define VME2_IRQL1_TIC1SHIFT 0 +/*7c*/ volatile u_long vme2_irql2; +#define VME2_IRQL2_VIASHIFT 28 +#define VME2_IRQL2_DMASHIFT 24 +#define VME2_IRQL2_SIG3SHIFT 20 +#define VME2_IRQL2_SIG2SHIFT 16 +#define VME2_IRQL2_SIG1SHIFT 12 +#define VME2_IRQL2_SIG0SHIFT 8 +#define VME2_IRQL2_LM1SHIFT 4 +#define VME2_IRQL2_LM0SHIFT 0 +/*80*/ volatile u_long vme2_irql3; +#define VME2_IRQL3_SW7SHIFT 28 +#define VME2_IRQL3_SW6SHIFT 24 +#define VME2_IRQL3_SW5SHIFT 20 +#define VME2_IRQL3_SW4SHIFT 16 +#define VME2_IRQL3_SW3SHIFT 12 +#define VME2_IRQL3_SW2SHIFT 8 +#define VME2_IRQL3_SW1SHIFT 4 +#define VME2_IRQL3_SW0SHIFT 0 +/*84*/ volatile u_long vme2_irql4; +#define VME2_IRQL4_SPARESHIFT 28 +#define VME2_IRQL4_VME7SHIFT 24 +#define VME2_IRQL4_VME6SHIFT 20 +#define VME2_IRQL4_VME5SHIFT 16 +#define VME2_IRQL4_VME4SHIFT 12 +#define VME2_IRQL4_VME3SHIFT 8 +#define VME2_IRQL4_VME2SHIFT 4 +#define VME2_IRQL4_VME1SHIFT 0 +/*88*/ volatile u_long vme2_vbr; +#define VME2_VBR_0SHIFT 28 +#define VME2_VBR_1SHIFT 24 +#define VME2_VBR_GPOXXXX 0x00ffffff +/*8c*/ volatile u_long vme2_misc; +#define VME2_MISC_MPIRQEN 0x00000080 /* do not set */ +#define VME2_MISC_REVEROM 0x00000040 /* 167: dis eprom. 166: en flash */ +#define VME2_MISC_DISSRAM 0x00000020 /* do not set */ +#define VME2_MISC_DISMST 0x00000010 +#define VME2_MISC_NOELBBSY 0x00000008 /* do not set */ +#define VME2_MISC_DISBSYT 0x00000004 /* do not set */ +#define VME2_MISC_ENINT 0x00000002 /* do not set */ +#define VME2_MISC_DISBGN 0x00000001 /* do not set */ +}; + +#define VME2_A16D32BASE 0xffff0000UL +#define VME2_A16D32LEN 0x00010000UL +#define VME2_A32D16BASE 0xf1000000UL +#define VME2_A32D16LEN 0x01000000UL +#define VME2_A16D16BASE 0xffff0000UL +#define VME2_A16D16LEN 0x00010000UL +#define VME2_A24D16BASE 0xf0000000UL +#define VME2_A24D16LEN 0x01000000UL +#define VME2_A16BASE 0xffff0000UL +#define VME2_A24BASE 0xff000000UL + +caddr_t vmepmap __P((struct vmesoftc *sc, caddr_t vmeaddr, int len, + int bustype)); +caddr_t vmemap __P((struct vmesoftc *sc, caddr_t vmeaddr, int len, + int bustype)); +int vmerw __P((struct vmesoftc *sc, struct uio *uio, int flags, int bus)); diff --git a/sys/arch/mvme68k/dev/vmel.c b/sys/arch/mvme68k/dev/vmel.c new file mode 100644 index 00000000000..68fdb1cf7f2 --- /dev/null +++ b/sys/arch/mvme68k/dev/vmel.c @@ -0,0 +1,173 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <mvme68k/dev/vme.h> + +/* + * The VMEL driver deals with D32 transfers on the VME bus. The number + * of address bits (A16, A24, A32) is irrelevant since the mapping + * functions will decide how many address bits are relevant. + */ + +void vmelattach __P((struct device *, struct device *, void *)); +int vmelmatch __P((struct device *, void *, void *)); + +struct vmelsoftc { + struct device sc_dev; + struct vmesoftc *sc_vme; +}; + +struct cfdriver vmelcd = { + NULL, "vmel", vmelmatch, vmelattach, + DV_DULL, sizeof(struct vmelsoftc), 0 +}; + +int +vmelmatch(parent, cf, args) + struct device *parent; + void *cf, *args; +{ + return (1); +} + +int +vmelscan(parent, child, args) + struct device *parent; + void *child, *args; +{ + return (vmescan(parent, child, args, BUS_VMEL)); +} + +void +vmelattach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct vmelsoftc *sc = (struct vmelsoftc *)self; + + printf("\n"); + + sc->sc_vme = (struct vmesoftc *)parent; + + config_search(vmelscan, self, args); +} + +/*ARGSUSED*/ +int +vmelopen(dev, flag, mode) + dev_t dev; + int flag, mode; +{ + if (minor(dev) >= vmelcd.cd_ndevs || + vmelcd.cd_devs[minor(dev)] == NULL) + return (ENODEV); + return (0); +} + +/*ARGSUSED*/ +int +vmelclose(dev, flag, mode) + dev_t dev; + int flag, mode; +{ + + return (0); +} + +/*ARGSUSED*/ +int +vmelioctl(dev, cmd, data, flag, p) + dev_t dev; + caddr_t data; + int cmd, flag; + struct proc *p; +{ + int unit = minor(dev); + struct vmelsoftc *sc = (struct vmelsoftc *) vmelcd.cd_devs[unit]; + int error = 0; + + switch (cmd) { + default: + error = ENOTTY; + break; + } + return (error); +} + +int +vmelread(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + int unit = minor(dev); + struct vmelsoftc *sc = (struct vmelsoftc *) vmelcd.cd_devs[unit]; + + return (vmerw(sc->sc_vme, uio, flags, BUS_VMEL)); +} + +int +vmelwrite(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + int unit = minor(dev); + struct vmelsoftc *sc = (struct vmelsoftc *) vmelcd.cd_devs[unit]; + + return (vmerw(sc->sc_vme, uio, flags, BUS_VMEL)); +} + +int +vmelmmap(dev, off, prot) + dev_t dev; + int off, prot; +{ + int unit = minor(dev); + struct vmelsoftc *sc = (struct vmelsoftc *) vmelcd.cd_devs[unit]; + caddr_t pa; + + pa = vmepmap(sc->sc_vme, (caddr_t)off, NBPG, BUS_VMEL); + printf("vmel %x pa %x\n", off, pa); + if (pa == NULL) + return (-1); + return (m68k_btop(pa)); +} diff --git a/sys/arch/mvme68k/dev/vmes.c b/sys/arch/mvme68k/dev/vmes.c new file mode 100644 index 00000000000..b4d6ac87725 --- /dev/null +++ b/sys/arch/mvme68k/dev/vmes.c @@ -0,0 +1,173 @@ +/* $NetBSD$ */ + +/* + * Copyright (c) 1995 Theo de Raadt + * 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 Theo de Raadt + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include <sys/param.h> +#include <sys/conf.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <mvme68k/dev/vme.h> + +/* + * The VMES driver deals with D16 transfers on the VME bus. The number + * of address bits (A16, A24, A32) is irrelevant since the mapping + * functions will decide how many address bits are relevant. + */ + +void vmesattach __P((struct device *, struct device *, void *)); +int vmesmatch __P((struct device *, void *, void *)); + +struct vmessoftc { + struct device sc_dev; + struct vmesoftc *sc_vme; +}; + +struct cfdriver vmescd = { + NULL, "vmes", vmesmatch, vmesattach, + DV_DULL, sizeof(struct vmessoftc), 0 +}; + +int +vmesmatch(parent, cf, args) + struct device *parent; + void *cf, *args; +{ + return (1); +} + +int +vmesscan(parent, child, args) + struct device *parent; + void *child, *args; +{ + return (vmescan(parent, child, args, BUS_VMES)); +} + +void +vmesattach(parent, self, args) + struct device *parent, *self; + void *args; +{ + struct vmessoftc *sc = (struct vmessoftc *)self; + + printf("\n"); + + sc->sc_vme = (struct vmesoftc *)parent; + + config_search(vmesscan, self, args); +} + +/*ARGSUSED*/ +int +vmesopen(dev, flag, mode) + dev_t dev; + int flag, mode; +{ + if (minor(dev) >= vmescd.cd_ndevs || + vmescd.cd_devs[minor(dev)] == NULL) + return (ENODEV); + return (0); +} + +/*ARGSUSED*/ +int +vmesclose(dev, flag, mode) + dev_t dev; + int flag, mode; +{ + + return (0); +} + +/*ARGSUSED*/ +int +vmesioctl(dev, cmd, data, flag, p) + dev_t dev; + caddr_t data; + int cmd, flag; + struct proc *p; +{ + int unit = minor(dev); + struct vmessoftc *sc = (struct vmessoftc *) vmescd.cd_devs[unit]; + int error = 0; + + switch (cmd) { + default: + error = ENOTTY; + break; + } + return (error); +} + +int +vmesread(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + int unit = minor(dev); + struct vmessoftc *sc = (struct vmessoftc *) vmescd.cd_devs[unit]; + + return (vmerw(sc->sc_vme, uio, flags, BUS_VMES)); +} + +int +vmeswrite(dev, uio, flags) + dev_t dev; + struct uio *uio; + int flags; +{ + int unit = minor(dev); + struct vmessoftc *sc = (struct vmessoftc *) vmescd.cd_devs[unit]; + + return (vmerw(sc->sc_vme, uio, flags, BUS_VMES)); +} + +int +vmesmmap(dev, off, prot) + dev_t dev; + int off, prot; +{ + int unit = minor(dev); + struct vmessoftc *sc = (struct vmessoftc *) vmescd.cd_devs[unit]; + caddr_t pa; + + pa = vmepmap(sc->sc_vme, (caddr_t)off, NBPG, BUS_VMES); + printf("vmes %x pa %x\n", off, pa); + if (pa == NULL) + return (-1); + return (m68k_btop(pa)); +} |