V9/sys/sundev/ie.c
/*
* Copyright (c) 1985 by Sun Microsystems, Inc.
*/
#include "ie.h"
#if NIE > 0
/*
* Sun Intel Ethernet Controller interface
*/
#include "../h/param.h"
#include "../h/systm.h"
#include "../machine/pte.h"
#include "../h/map.h"
#include "../h/buf.h"
#include "../h/vmmac.h"
#include "../h/conf.h"
#include "../h/ttyio.h"
#include "../h/enio.h"
#include "../h/ttyld.h"
#include "../h/stream.h"
#include "../h/ethernet.h"
#include "../sundev/mbvar.h"
#include "../sundev/iereg.h"
#include "../sundev/iem.h"
#include "../sundev/ieob.h"
/* controller types */
#define IE_MB 1 /* Multibus */
#define IE_OB 2 /* Onboard I/O */
#define IEOBMEMDFT (40*1024) /* default memory allocated for obie */
#define CBCACHESIZ (80) /* size of control block cache */
#define OVFLWARNMASK (3) /* overflow count mask before warning */
#define NPOOL_RBD 50
#define NTRANSBUF 4 /* NUmber of transmit buffers */
int ieprobe(), ieattach(), iepoll();
struct mb_device *ieinfo[NIE];
struct mb_driver iedriver = {
ieprobe, 0, ieattach, 0, 0, iepoll,
/* take the larger of the two devices */
sizeof (struct mie_device), "ie", ieinfo,
};
caddr_t from_ieaddr();
ieoff_t to_ieoff();
caddr_t from_ieoff();
ieaddr_t to_ieaddr();
long escbget(), escbput();
ieint_t to_ieint();
#define from_ieint to_ieint
#define iepages(x) (((x) + IEPAGSIZ-1)>>IEPAGSHIFT)
int iecbcsmall;
int iecbclarge;
/*
* Ethernet software status per interface.
*/
struct ie_softc {
/* Added by D.A.K for version 9 compatability */
char myetheradr[6];
char attached; /* Non zero when ie has been attached */
int state;
int collisions;
int ierrors, oerrors;
int ipackets, opackets;
struct ietfd *es_tfd[NTRANSBUF]; /* transmit TFD */
struct ietbd *es_tbd[NTRANSBUF]; /* transmit TBD */
caddr_t es_tbuffer[NTRANSBUF]; /* transmit buffer */
int es_tfree; /* transmitter bit map */
/* Supplied by SUN */
struct mie_device *es_mie; /* Multibus board registers */
struct obie_device *es_obie; /* On-board registers */
caddr_t es_memspace; /* + chip addr = kernel addr */
int es_paddr; /* starting page number for board */
int es_vmemsize; /* size of multibus mem port */
caddr_t es_base; /* our addr of control block base */
struct map *es_cbmap; /* rmap for control blocks */
struct map *es_memmap; /* rmap for memory */
struct map *es_pgmap; /* rmap for 1K page (ND) memory
accessed thru if_memmap */
struct iescb *es_scb; /* SCB ptr */
struct iescp *es_scp; /* SCP ptr */
struct iecb *es_cbhead; /* CBL head */
struct iecb *es_cbtail; /* CBL tail */
struct ierfd *es_rfdhead; /* head of RFD list */
struct ierfd *es_rfdtail; /* tail of RFD list */
struct ierbd *es_rbdhead; /* head of RBD list */
struct ierbd *es_rbdtail; /* tail of RBD list */
struct ieipack *es_iepavail; /* standby input packets */
int es_obmem; /* total IE_OB main memory */
char es_type; /* type of controller */
char es_simple; /* doing simple flag */
u_int es_runotready; /* RU was not ready counter */
u_int es_xmiturun; /* xmit DMA underrun counter */
u_int es_ieheart; /* heartbeat counter */
u_int es_iedefer; /* deferred transmission counter */
struct ieierr {
u_int crc;
u_int aln;
u_int rsc;
u_int ovr;
} es_ierr; /* input error counters */
int es_cbsize; /* control block area size */
int es_cbc_lo; /* small block cache pointer */
int es_cbc_hi; /* large block cache pointer */
u_int es_cbc_ovfl; /* overflow counter */
int es_cbcbusy; /* cache is loaded */
long es_cbcache[CBCACHESIZ]; /* control block cache */
} ie_softc[NIE];
#define NIECHAN (CHANS_PER_UNIT * NIE)
struct iechan {
int unit;
int packets;
struct queue *ieq;
int type;
} iechan[NIECHAN];
int nodev(), ieopen(), ieclose(), ieput(), iesrv();
static struct qinit ierinit = { nodev, NULL, ieopen, ieclose, 0, 0 };
static struct qinit iewinit = { ieput, NULL, ieopen, ieclose, 1514, 0 };
/* 1514 bytes is minimum highwater mark to send 1514 byte packets */
struct streamtab iesinfo = { &ierinit, &iewinit };
#define IEMAPSIZ 100
struct map iecbmap[NIE][IEMAPSIZ];
struct map iememmap[NIE][IEMAPSIZ];
struct map iepgmap[NIE][IEMAPSIZ];
#define escballoc(es, type, cached) (type *)escbget(es, sizeof(type), cached)
#define escbfree(es, ptr) escbput(es, sizeof(*ptr), (long)ptr);
#define escbpin(es, len, addr) rmget(es->es_cbmap, (int)len, (int)addr)
#define esmemalloc(es, len) rmalloc(es->es_memmap, (long)len)
#define esmemfree(es, len, ptr) rmfree(es->es_memmap, (long)len, (long)ptr)
#define esmempin(es, len, addr) rmget(es->es_memmap, (int)len, (int)addr)
/*
* fixed header size for page alignment of received data buffers
* this is sizeof(struct ndpack) for ND. (doesn't include ether header)
*/
#define OPTHDRSIZ 48
#define IPACKOVH 8 /* struct ieipack header overhead */
struct ieipack {
struct ieipack *iep_next; /* next in list; MUST BE FIRST FIELD */
struct ie_softc *iep_es; /* assoc ie_softc */
char iep_data[1500]; /* the packet */
};
#define IEDELAY 400000 /* delay period (in ms) before giving up */
#define IEKLUDGE 20 /* delay period (in ms) to make chip work */
/*
* Probe for device.
*/
ieprobe(reg, unit)
caddr_t reg;
{
register short *sp;
struct ie_softc *es = &ie_softc[unit];
if ((getkpgmap(reg) & PGT_MASK) == PGT_OBIO) {
register struct obie_device *obie = (struct obie_device *)reg;
/* onboard Ethernet */
if (pokec(reg, 0))
return (0);
if (peekc(reg) == -1)
return (0);
if (obie->obie_noreset)
return (0);
es->es_type = IE_OB;
} else {
register struct mie_device *mie = (struct mie_device *)reg;
/* Multibus Ethernet */
sp = (short *)mie;
if (poke(sp, 0))
return (0);
sp = &mie->mie_prom[0];
if (poke(sp, 0x6789))
return (0);
if (peek(sp) == 0x6789)
return (0);
es->es_type = IE_MB;
}
return (1);
}
/*
* Interface exists; make available by filling in network interface
* record. System will initialize the interface when it is ready
* to accept packets.
*/
ieattach(md)
struct mb_device *md;
{
struct ie_softc *es = &ie_softc[md->md_unit];
if (md->md_intr) /* set up vectored interrupts */
*(md->md_intr->v_vptr) = (int)es;
if (es->es_type == IE_OB) {
es->es_obie = (struct obie_device *)md->md_addr;
es->es_obmem = IEOBMEMDFT;
} else {
es->es_mie = (struct mie_device *)md->md_addr;
/*
* magic thru config used to artificially restrict
* the amount of MBMEM used, as there is only 1 Mb
* available, making it a scarce resource.
*/
switch (md->md_flags) {
case 0:
default:
es->es_vmemsize = 256*1024;
break;
case 1:
es->es_vmemsize = 128*1024;
break;
case 2:
es->es_vmemsize = 64*1024;
break;
}
}
iechipreset(es);
localetheraddr(NULL, es->myetheradr);
ieinit(md->md_unit);
es->attached = 1;
}
/* ARGSUSED */
ieopen(q, dev)
register struct queue *q;
register dev_t dev;
{
register struct iechan *ied;
register struct ie_softc *es;
register unit;
dev = minor(dev);
unit = dev / CHANS_PER_UNIT;
if (dev >= NIECHAN)
return(0);
if (unit >= NIE)
return(0);
es = &ie_softc[unit];
if(!es->attached)
return(0);
ied = &iechan[dev];
if (ied->ieq)
return(0);
ied->unit = unit;
ied->ieq = q;
q->ptr = (caddr_t)ied;
q->flag |= QBIGB | QDELIM;
WR(q)->ptr = (caddr_t)ied;
WR(q)->flag |= QBIGB|QDELIM;
return(1);
}
ieclose(q)
register struct queue *q;
{
register struct iechan *ied;
ied = (struct iechan *)q->ptr;
flushq(WR(q), 1);
ied->ieq = NULL;
}
ieput(q, bp)
register struct queue *q;
register struct block *bp;
{
register struct iechan *ied;
register struct ie_softc *es;
register unit, s;
ied = (struct iechan *)q->ptr;
unit = ied->unit;
es = &ie_softc[unit];
switch(bp->type) {
case M_IOCTL:
ieioctl(q, bp);
return;
case M_DATA:
putq(q, bp);
return;
case M_DELIM:
break;
default:
freeb(bp);
return;
}
putq(q, bp);
s = splie();
ied->packets++;
if (es->es_tfree)
iestartout(unit);
splx(s);
}
/*
* Process an ioctl request.
*/
ieioctl(q, bp)
register struct queue *q;
register struct block *bp;
{
register struct iechan *ied;
register struct ie_softc *es;
register u_char *msg;
int unit, cmd;
ied = (struct iechan *)q->ptr;
unit = ied->unit;
es = &ie_softc[unit];
cmd = ((union stmsg *)(bp->rptr))->ioc1.com;
msg = (u_char *)&((union stmsg *)(bp->rptr))->ioc1.sb;
bp->type = M_IOCACK;
switch (cmd) {
case ENIOADDR: /* get my Ethernet address */
bcopy(es->myetheradr, (int *)msg, 6);
break;
case ENIOTYPE:
ied->type = *(int *)msg;
break;
default:
bp->type = M_IOCNAK;
break;
}
qreply(q, bp);
}
/*
* Unconditionally restart the interface from ground zero.
*/
ieinit(unit)
int unit;
{
struct ie_softc *es = &ie_softc[unit];
int s = splie();
iechipreset(es);
iedomaps(es);
if (!iechipinit(es))
goto exit;
iedoaddr(es);
/*
* Hang out receive buffers and start any pending writes.
*/
ierustart(es);
iesplice(es);
iestartout(unit);
exit:
(void) splx(s);
}
/*
* Handle Polling Ethernet interrupts
*/
iepoll()
{
register struct ie_softc *es;
register int found = 0;
for (es = ie_softc; es < &ie_softc[NIE]; es++) {
switch (es->es_type) {
case IE_MB:
if (es->es_mie->mie_pie && es->es_mie->mie_pe) {
ieparity(es);
return (1);
}
if (es->es_mie->mie_ie == 0)
continue;
if (es->es_mie->mie_intr)
found = 1;
break;
case IE_OB:
if (es->es_obie->obie_buserr)
panic("ie: bus error");
if (es->es_obie->obie_ie == 0)
continue;
if (es->es_obie->obie_intr)
found = 1;
break;
default: /* not present */
continue;
}
/*
* Since the 82586 can take away an interrupt
* request after presenting it to the processsor
* (to facilitate an update of a new interrupt
* condition in the scb), we also have to check
* the scb to see if it indicates an interrupt
* condition from the chip.
*/
if (found == 0) {
register struct iescb *scb = es->es_scb;
if (scb->ie_cx || scb->ie_fr ||
scb->ie_cnr || scb->ie_rnr)
found = 1;
}
if (found) {
ieintr(es);
break;
}
}
return (found);
}
/*
* Handle Ethernet interrupts
*/
ieintr(es)
register struct ie_softc *es;
{
register struct iescb *scb;
register int cmd, unit;
unit = es - ie_softc;
scb = es->es_scb;
iechkcca(scb);
if (scb->ie_cmd != 0) {
register struct mb_device *md;
printf("ie%d: lost synch\n", unit);
iestat(es);
if (unit >= NIE || (md = ieinfo[unit]) == 0 ||
md->md_alive == 0)
return;
ieinit(unit);
return;
}
cmd = 0;
/*
* This can be done faster with something like:
* cmd = (*(short *)scb->ie_cx) &
* (IECMD_ACK_CX|IECMD_ACK_FR|IECMD_ACK_CNR|IECMD_ACK_RNR);
*/
if (scb->ie_cx)
cmd |= IECMD_ACK_CX;
if (scb->ie_fr)
cmd |= IECMD_ACK_FR;
if (scb->ie_cnr)
cmd |= IECMD_ACK_CNR;
if (scb->ie_rnr)
cmd |= IECMD_ACK_RNR;
if (cmd == 0) {
printf("ie%d: spurious intr\n", unit);
cmd = IECMD_ACK_CX+IECMD_ACK_FR+IECMD_ACK_CNR+IECMD_ACK_RNR;
}
scb->ie_cmd = cmd;
ieca(es);
if (cmd & (IECMD_ACK_RNR|IECMD_ACK_FR))
ierecv(es);
if (cmd & (IECMD_ACK_CNR|IECMD_ACK_CX))
iecbdone(es);
if (es->es_cbhead && scb->ie_cus == IECUS_IDLE) {
iechkcca(scb);
if (es->es_cbhead && scb->ie_cus == IECUS_IDLE &&
!scb->ie_cx && !scb->ie_cnr)
printf("ie%d: CU out of synch\n", unit);
}
}
/*
* Tell the chip its ethernet address
*/
iedoaddr(es)
register struct ie_softc *es;
{
register struct ieiaddr *iad;
iad = escballoc(es, struct ieiaddr, 1);
if (iad == 0)
panic("iedoaddr: iad");
bzero((caddr_t)iad, sizeof (struct ieiaddr));
iad->ieia_cb.ie_cmd = IE_IADDR;
bcopy(es->myetheradr, iad->ieia_addr, sizeof(es->myetheradr));
iesimple(es, &iad->ieia_cb);
escbfree(es, iad);
}
/*
* Move info from driver toward protocol interface
*/
ieread(es, rfd)
register struct ie_softc *es;
register struct ierfd *rfd;
{
int unit = es - ie_softc;
register struct etherpup *header;
register struct ierbd *rbd;
register struct iechan *ied;
register struct queue *q;
register struct block *bp;
register len, min, i;
register u_char *p;
short type;
if (!rfd->ierfd_ok) {
es->ierrors++;
printf("ie%d: receive error\n", unit);
return;
}
if (rfd->ierfd_rbd == IENORBD) {
printf("ie%d: runt packet\n", unit);
es->ierrors++;
return;
}
rbd = (struct ierbd *)from_ieoff(es, (ieoff_t)rfd->ierfd_rbd);
if (!rbd->ierbd_eof) { /* length > 1500 */
printf("ie%d: giant packet\n", unit);
es->ierrors++;
return;
}
/*
* Pull packet off interface.
*/
header = (struct etherpup *)rfd->ierfd_dhost;
type = header->type;
ied = &iechan[unit * CHANS_PER_UNIT];
for (i = 0; i < CHANS_PER_UNIT; i++, ied++)
if (ied->ieq && ied->type == type)
break;
if (i >= CHANS_PER_UNIT)
return;
q = ied->ieq;
if (q->next->flag & QFULL) {
es->ierrors++;
return;
}
len = (rbd->ierbd_cnthi << 8) + rbd->ierbd_cntlo;
/*
* Splice in the ethernet header
* Assumes allocb will return a block at least as big as etherpup.
*/
bp = allocb(len + sizeof(struct etherpup));
*(struct etherpup *)bp->wptr = *header;
bp->wptr += sizeof(struct etherpup);
/*
* Now copy the data
*/
p = (u_char *)from_ieaddr(es, rbd->ierbd_buf);
i = MIN(((bp->lim - bp->base) - sizeof(struct etherpup)), len);
for(;;) {
len -= i;
while (i-- > 0)
*bp->wptr++ = *p++;
(*q->next->qinfo->putp)(q->next, bp);
if (len <= 0)
break;
bp = allocb(len);
i = MIN((bp->lim - bp->base), len);
}
if (putctl(q->next, M_DELIM))
es->ipackets++;
else
printf("ierint no DELIM bp\n");
}
/*
* Process completed input packets
* and recycle resources;
* only called from interrupt level by ieintr
*/
ierecv(es)
register struct ie_softc *es;
{
register struct ierfd *rfd, *nrfd;
register struct ierbd *rbd, *nrbd;
register struct iescb *scb = es->es_scb;
int eof, e;
rfd = es->es_rfdhead;
if (rfd == NULL) /* not initialized */
return;
top:
while (rfd && rfd->ierfd_done) {
ieread(es, rfd);
if (rfd->ierfd_rbd != IENORBD) {
rbd = (struct ierbd *)from_ieoff(es,
(ieoff_t)rfd->ierfd_rbd);
if (rbd != es->es_rbdhead)
panic("ierecv rbd");
while (rbd && rbd->ierbd_used) {
if (rbd != (struct ierbd *)from_ieoff(es,
(ieoff_t)es->es_rbdtail->ierbd_next))
panic("ierecv rbd list");
nrbd = (struct ierbd *)from_ieoff(es,
(ieoff_t)rbd->ierbd_next);
rbd->ierbd_el = 1;
eof = rbd->ierbd_eof;
*(short *)rbd = 0;
es->es_rbdtail->ierbd_el = 0;
es->es_rbdtail = rbd;
rbd = es->es_rbdhead = nrbd;
if (eof)
break;
}
}
if (rfd != (struct ierfd *)from_ieoff(es,
(ieoff_t)es->es_rfdtail->ierfd_next))
panic("ierecv rfd list");
nrfd = (struct ierfd *)from_ieoff(es, (ieoff_t)rfd->ierfd_next);
rfd->ierfd_rbd = IENORBD;
rfd->ierfd_el = 1;
*(short *)rfd = 0;
es->es_rfdtail->ierfd_el = 0;
es->es_rfdtail = rfd;
rfd = es->es_rfdhead = nrfd;
}
if (e = scb->ie_crcerrs) { /* count of CRC errors */
scb->ie_crcerrs = 0;
e = from_ieint(e);
es->ierrors += e;
es->es_ierr.crc += e;
}
if (e = scb->ie_alnerrs) { /* count of alignment errors */
scb->ie_alnerrs = 0;
e = from_ieint(e);
es->ierrors += e;
es->es_ierr.aln += e;
}
if (e = scb->ie_rscerrs) { /* count of discarded packets */
scb->ie_rscerrs = 0;
e = from_ieint(e);
es->ierrors += e;
es->es_ierr.rsc += e;
}
if (e = scb->ie_ovrnerrs) { /* count of overrun packets */
scb->ie_ovrnerrs = 0;
e = from_ieint(e);
es->ierrors += e;
es->es_ierr.ovr += e;
}
if (scb->ie_rus == IERUS_READY) /* as expected */
return;
es->es_runotready++;
/* following test must be made when we know chip is quiet */
if (es->es_rfdhead->ierfd_done) /* more snuck in */
goto top;
es->es_rfdhead->ierfd_rbd = to_ieoff(es, (caddr_t)es->es_rbdhead);
iechkcca(scb);
scb->ie_rfa = to_ieoff(es, (caddr_t)es->es_rfdhead);
scb->ie_cmd = IECMD_RU_START;
ieca(es);
}
/*
* Free up the resources after transmitting a packet.
* Called by iecuclean at splie or hardware level 3.
*/
iexmitdone(es, td)
register struct ie_softc *es;
register struct ietfd *td;
{
int unit = es - ie_softc;
register int i;
if (td->ietfd_ok) {
es->collisions += td->ietfd_ncoll;
es->opackets++;
if (td->ietfd_defer) es->es_iedefer++;
if (td->ietfd_heart) es->es_ieheart++;
} else {
es->oerrors++;
if (td->ietfd_xcoll)
printf("ie%d: Ethernet jammed\n", unit);
if (td->ietfd_nocarr)
printf("ie%d: no carrier\n", unit);
if (td->ietfd_nocts)
printf("ie%d: no CTS\n", unit);
if (td->ietfd_underrun)
es->es_xmiturun++;
}
if (!td->ietfd_tbd)
panic("ie%d: iexmitdone: no tbd");
for(i = 0; i < NTRANSBUF; i++)
if (td == es->es_tfd[i])
break;
if (es->es_tfree & (1 << i))
printf("ie%d stray xmit interrupt\n", unit);
es->es_tfree |= (1 << i);
}
#define rndtoeven(x) (((x)+1) & ~1)
/*
* Start or restart output to wire.
*/
iestartout(unit)
int unit;
{
register struct ie_softc *es = &ie_softc[unit];
register struct iechan *ied;
register cnt, i;
register caddr_t to;
int count, bufnum;
struct ietfd *td;
register struct ietbd *tbd;
register struct queue *q;
register struct block *bp, *nbp;
struct block *head, **bnext;
if (!es->es_tfree)
goto out;
ied = &iechan[unit * CHANS_PER_UNIT];
for(i = 0; i < CHANS_PER_UNIT; i++, ied++)
if (ied->ieq && ied->packets > 0)
break;
if (i >= CHANS_PER_UNIT)
goto out;
ied->packets--;
q = WR(ied->ieq);
/* The packet must be justified to the end of the buffer.
* Therefore, we have to count the bytes before copying.
*/
bnext = &head;
while (*bnext = bp = getq(q)) {
if (bp->type == M_DELIM) {
bp->next = 0;
break;
}
cnt += bp->wptr - bp->rptr;
bnext = &bp->next;
}
bp = head;
/* if there is no ethernet header in packet, throw it out */
if (cnt < sizeof(struct etherpup) ||
(bp->wptr - bp->rptr) < sizeof(struct etherpup)) {
while(bp) {
nbp = bp->next;
freeb(bp);
bp = nbp;
}
goto out;
}
for (bufnum = 0; ; bufnum++)
if (es->es_tfree & (1 << bufnum))
break;
es->es_tfree &= ~(1 << bufnum);
td = es->es_tfd[bufnum];
tbd = es->es_tbd[bufnum];
/* Setup the header */
bcopy(bp->rptr, (caddr_t)td->ietfd_dhost, 6); /* Dest */
bp->rptr += 12; /* Skip src */
bcopy(bp->rptr, (caddr_t)&td->ietfd_type, 2); /* Type */
bp->rptr += 2; /* Skip src */
cnt -= sizeof(struct etherpup);
/* Setup the data */
if (cnt > 1500) /* test for too large packets */
cnt = 1500;
count = cnt;
to = es->es_tbuffer[bufnum];
while (cnt > 0 && bp != 0) {
i = bp->wptr - bp->rptr;
bcopy(bp->rptr, to, i);
cnt -= i;
to += i;
nbp = bp->next;
freeb(bp);
bp = nbp;
}
if (count < 46) /* test for small packets */
count = 46;
/* flush remaining buffers, if any (too large packet: count > 1500) */
while (bp) {
nbp = bp->next;
freeb(bp);
bp = nbp;
}
/* Setup the buffer descriptor */
tbd->ietbd_eof = 1;
tbd->ietbd_next = 0;
tbd->ietbd_buf = to_ieaddr(es, es->es_tbuffer[bufnum]);
tbd->ietbd_cntlo = count & 0xFF;
tbd->ietbd_cnthi = count >> 8;
td->ietfd_tbd = to_ieoff(es, (caddr_t)tbd);
td->ietfd_cmd = IE_TRANSMIT;
iedocb(es, (struct iecb *)td);
out:
iecustart(es);
return;
}
/*
* Set the control block area size
*/
iesetcbsize(es)
register struct ie_softc *es;
{
int tfds; /* number of tfd's */
int tfdsize; /* cbsize for each tfd */
int tbufsize; /* cbsize for each tbuf */
int rbufs; /* number of rbuf's in cb area */
int rbufsize; /* cbsize for each rbuf */
int rfds; /* number of rfd's */
int rfdsize; /* cbsize for each rfd */
int fixed; /* fixed overhead, including slop */
fixed = sizeof (struct iescp) + sizeof (struct ieiscp)
+ sizeof (struct iescb) + sizeof (struct ieconf)
+ 100;
tfdsize = sizeof (struct ietfd) + sizeof (struct ietbd);
tbufsize = sizeof (struct ieipack);
rbufsize = sizeof (struct ieipack);
rfdsize = sizeof (struct ierfd) + sizeof (struct ierbd);
switch (es->es_type) {
case IE_OB:
tfds = NTRANSBUF;
rbufs = (es->es_obmem - fixed - tfds * (tfdsize+tbufsize)) /
(rbufsize + rfdsize);
break;
case IE_MB:
tfds = 50; /* maximum if_snd */
/* each rbuf eats into a 2K section of vmemsize */
rbufs = min(NPOOL_RBD, (u_int)(es->es_vmemsize>>11));
break;
}
rfds = rbufs;
es->es_cbsize = fixed
+ tfds * tfdsize
+ rfds * rfdsize;
}
/*
* Called by ieinit to (allocate and re)initialize rmap's
* We need to be careful, since the board may be in use
* (ieipacks loaned out, pages swapped)
*/
iedomaps(es)
register struct ie_softc *es;
{
int unit = es - ie_softc;
iesetcbsize(es);
bzero((caddr_t)iecbmap[unit], sizeof iecbmap[unit]);
bzero((caddr_t)iememmap[unit], sizeof iememmap[unit]);
es->es_cbhead = NULL;
es->es_cbcbusy = NULL;
iecbcinit(es);
if (es->es_type == IE_OB) {
int memall();
caddr_t va;
if (es->es_base == 0) {
va = wmemall(memall, es->es_obmem);
if (va == 0)
panic("ieattach: no memory");
es->es_base = va;
}
va = es->es_base;
es->es_cbmap = &iecbmap[unit][0];
es->es_memmap = es->es_cbmap;
rminit(es->es_cbmap, (long)es->es_obmem,
(long)es->es_base, "iecb", IEMAPSIZ);
es->es_memspace = (caddr_t)KERNELBASE;
#ifdef sun3
/* use the page already set up by the prom monitor */
es->es_scp = (struct iescp *)(IESCPADDR+es->es_memspace);
#else
/*
* Remap the [preallocated] virtual page containing
* the SCP to make it point to the same physical
* location as va, so that we can muck with the
* control blocks using the address returned from
* memall, and the chip can locate the scp at its
* fixed address
* Cache note: since we can not, in general, enforce
* the cache antialiasing separation requirement, we
* would need to avoid caching this physical page
*/
{
struct pte dummypte; /* for mapin to write on */
int paddr = getkpgmap(va) & PG_PFNUM;
mapin(&dummypte, (u_int)btop(IESCPADDR+
es->es_memspace), (u_int)paddr, 1, PG_V | PG_KW);
es->es_scp = (struct iescp *)escbpin(es,
(long)sizeof (struct iescp),
(long)es->es_base + (IESCPADDR & PGOFSET));
}
#endif sun3
} else { /* IE_MB */
register struct mie_device *mie = es->es_mie;
struct miepg *pg;
short *ap;
int i;
int a, vaddr, paddr;
int firsttime = es->es_memspace == 0;
if (firsttime) {
if ((a = rmalloc(kernelmap,(long)btoc(es->es_vmemsize)))
== 0) {
printf("ie%d: no kernelmap for ie memory\n",
unit);
panic("iedomaps");
}
es->es_memspace = (caddr_t)kmxtob(a);
}
vaddr = (int)es->es_memspace;
a = btokmx((struct pte *)vaddr);
/* board's mem boundary to byte addr */
paddr = mie->mie_mbmhi << 16;
/* preserve pagetype bits and which megabyte its in (for VME) */
es->es_paddr = getkpgmap((caddr_t)mie) & PG_PFNUM;
es->es_paddr &= ~(0x100000/NBPG -1);
es->es_paddr |= btop(paddr);
mapin(&Usrptmap[a], (u_int)btop(vaddr), (u_int)es->es_paddr,
(int)btoc(es->es_vmemsize), PG_V | PG_KW);
/* clear the board unless in use */
if (firsttime) {
ap = (short *)mie->mie_pgmap;
for (i=0; i<IEVVSIZ; i++) /* clears mp_p2mem */
*ap++ = 0;
for (i=0; i<IEPMEMSIZ/IEPAGSIZ; i++) {
mie->mie_pgmap[0].mp_pfnum = i;
bzero(es->es_memspace, IEPAGSIZ);
}
pg = &mie->mie_pgmap[0];
for (i=0; i<es->es_vmemsize/IEPAGSIZ; i++) {
pg->mp_swab = 1;
pg->mp_pfnum = i;
pg++;
}
/* use last onboard ie page for chip init */
/* (no need to reclaim, since beyond vmemsize) */
pg = &mie->mie_pgmap[IEVVSIZ-1];
pg->mp_swab = 1;
pg->mp_pfnum = 0;
/* patch potential powerup parity problem */
mie->mie_peack = 1;
}
es->es_base = es->es_memspace;
es->es_cbmap = &iecbmap[unit][0];
rminit(es->es_cbmap, (long)es->es_cbsize,
(long)es->es_base, "iecb", IEMAPSIZ);
es->es_scp = (struct iescp *)escbpin(es,
(long)sizeof (struct iescp),
(long)es->es_base + (IESCPADDR & (IEPAGSIZ-1)));
es->es_memmap = &iememmap[unit][0];
es->es_pgmap = &iepgmap[unit][0];
/*
* if we have enough memory, create a page pool which
* can manipulated via if_memmap, say by ND
*/
if (es->es_vmemsize == 256*1024) {
rminit(es->es_memmap, (long)(128*1024-es->es_cbsize),
(long)(es->es_memspace+es->es_cbsize),
"iemem", IEMAPSIZ);
if (firsttime)
rminit(es->es_pgmap, (long)(128*1024),
(long)(es->es_memspace+128*1024),
"iepg", IEMAPSIZ);
} else {
rminit(es->es_memmap,
(long)(es->es_vmemsize-es->es_cbsize),
(long)(es->es_memspace+es->es_cbsize),
"iemem", IEMAPSIZ);
rminit(es->es_pgmap, (long)0, (long)0,
"iepg", IEMAPSIZ);
}
}
}
/*
* Basic 82586 initialization
*/
int
iechipinit(es)
register struct ie_softc *es;
{
int unit = es - ie_softc;
struct ieiscp *iscp;
struct iescb *scb;
struct iecb *cb;
struct ieconf *ic;
int ok = 0;
int gotintr;
int i;
#ifdef notdef
struct ietdr *tdr;
#endif
if (es->es_scp == 0) {
printf("ie%d: scp alloc failed\n", unit);
goto exit;
}
iscp = escballoc(es, struct ieiscp, 0);
if (iscp == 0) {
printf("ie%d: iscp alloc failed\n", unit);
goto exit;
}
scb = escballoc(es, struct iescb, 0);
if (scb == 0) {
printf("ie%d: scb alloc failed\n", unit);
goto exit;
}
es->es_scb = scb;
reset:
bzero((caddr_t)es->es_scp, sizeof (struct iescp));
es->es_scp->ie_iscp = to_ieaddr(es, (caddr_t)iscp);
bzero((caddr_t)iscp, sizeof (struct ieiscp));
iscp->ie_busy = 1;
iscp->ie_cbbase = to_ieaddr(es, es->es_base);
iscp->ie_scb = to_ieoff(es, (caddr_t)scb);
bzero((caddr_t)scb, sizeof (struct iescb));
scb->ie_magic = IEMAGIC;
/*
* Hardware reset the chip. We make the interval from
* reset to initial channel attention as small as reasonable
* to reduce the risk of scribbling chips getting us.
*/
switch (es->es_type) {
case IE_MB:
/* hardware reset already occurred in iechipreset */
break;
case IE_OB:
es->es_obie->obie_noreset = 1;
DELAY(IEKLUDGE); /* REQUIRED */
break;
}
ieca(es);
CDELAY(!iscp->ie_busy, IEDELAY); /* ensure chip eats iscp */
CDELAY(scb->ie_cnr, IEDELAY); /* ensure scb updated too */
gotintr = iewaitintr(es); /* wait for interrupt */
if (iscp->ie_busy || !scb->ie_cnr || !gotintr) {
printf("ie%d: init failed:%s%s%s\n", unit,
iscp->ie_busy?" iscp busy":"",
!scb->ie_cnr ?" no cnr":"",
!gotintr ?" no intr":"");
goto exit;
}
if (scb->ie_cus != IECUS_IDLE ) {
printf("ie%d: cus not idle after reset\n", unit);
iechipreset(es);
goto reset;
}
cb = escballoc(es, struct iecb, 1);
if (cb == NULL) {
printf("ie%d: cb alloc failed\n", unit);
goto exit;
}
bzero((caddr_t)cb, sizeof (struct iecb));
cb->ie_cmd = IE_DIAGNOSE;
iesimple(es, cb);
if (!cb->ie_ok) {
printf("ie%d: Intel 82586 failed diagnostics\n", unit);
escbfree(es, cb);
goto exit;
}
escbfree(es, cb);
#ifdef notdef
/* skip, since hardware requires quiet net to work */
tdr = escballoc(es, struct ietdr, 1);
if (tdr == NULL) {
printf("ie%d: tdr alloc failed\n", unit);
goto exit;
}
bzero((caddr_t)tdr, sizeof (struct ietdr));
tdr->ietdr_cb.ie_cmd = IE_TDR;
iesimple(es, &tdr->ietdr_cb);
if (!tdr->ietdr_ok) {
#define TDRCONST 12 /* approx 0.77c/10Mhz */
int dist = (tdr->ietdr_timhi<<8)+tdr->ietdr_timlo;
if (dist != 0x7FF)
printf("ie%d: link not OK - distance = ~%dm\n",
unit, TDRCONST*dist);
else
printf("ie%d: link not OK\n", unit);
escbfree(es, tdr);
goto exit;
}
if (tdr->ietdr_xcvr) printf("ie%d: transceiver bad\n", unit);
if (tdr->ietdr_open) printf("ie%d: net not terminated\n", unit);
if (tdr->ietdr_shrt) printf("ie%d: net shorted\n", unit);
escbfree(es, tdr);
#endif notdef
ic = escballoc(es, struct ieconf, 1);
if (ic == NULL) {
printf("ie%d: ic alloc failed\n", unit);
goto exit;
}
iedefaultconf(ic);
iesimple(es, &ic->ieconf_cb);
escbfree(es, ic);
for (i = 0; i < NTRANSBUF; i++) {
if ((es->es_tfd[i] = escballoc(es, struct ietfd, 0)) == NULL) {
printf("ie%d: tfd alloc failed\n", unit);
goto exit;
}
if ((es->es_tbd[i] = escballoc(es, struct ietbd, 0)) == NULL) {
printf("ie%d: tbd alloc failed\n", unit);
goto exit;
}
if ((es->es_tbuffer[i]=(caddr_t)esmemalloc(es, 1500)) ==NULL) {
printf("ie%d: tbuffer alloc failed\n", unit);
goto exit;
}
es->es_tfree |= (1 << i);
}
ok = 1;
exit:
return (ok);
}
/*
* called by ierustart to create the receiver buffer list
*/
iegetrbufs(es)
register struct ie_softc *es;
{
caddr_t addr;
int count, avail;
register int page, last;
register struct ieipack *iep;
es->es_iepavail = NULL;
if (es->es_type == IE_OB) {
last = (es->es_obmem-es->es_cbsize) /
(sizeof (struct ieipack)) - NTRANSBUF;
for (count = 0; count < last; count++) {
iep = (struct ieipack *)esmemalloc(es,
sizeof (struct ieipack));
if (iep == 0)
break;
iep->iep_es = es;
iep->iep_next = es->es_iepavail;
es->es_iepavail = iep;
}
avail = count;
} else { /* IE_MB */
count = NPOOL_RBD;
/* XXX not really, since there could be page pool */
last = es->es_vmemsize >> IEPAGSHIFT;
/* 2 pages for xmit (vice receive) buffer */
page = iepages(es->es_cbsize) + 2;
for (; count > 0 && page < last; page++) {
addr = es->es_memspace + page*IEPAGSIZ;
addr += IEPAGSIZ - (IPACKOVH + OPTHDRSIZ);
if (!esmempin(es, sizeof (struct ieipack), (int)addr))
continue;
count--;
iep = (struct ieipack *)addr;
iep->iep_es = es;
iep->iep_next = es->es_iepavail;
es->es_iepavail = iep;
}
avail = NPOOL_RBD - count;
}
return (avail);
}
/*
* Initialize and start the Receive Unit
*/
ierustart(es)
register struct ie_softc *es;
{
int unit = es - ie_softc;
register struct ierbd *rbd;
register struct ierfd *rfd;
register struct iescb *scb;
register struct ieipack *iep;
register int i, nrfd, ninit_rbd;
ninit_rbd = iegetrbufs(es);
es->es_rbdhead = NULL;
for (i = 0; i < ninit_rbd; i++) {
if ((iep = es->es_iepavail) == NULL)
break;
rbd = escballoc(es, struct ierbd, 0);
if (rbd == NULL)
break;
es->es_iepavail = iep->iep_next;
*(short *)rbd = 0;
if (es->es_rbdhead) {
rbd->ierbd_next = to_ieoff(es, (caddr_t)es->es_rbdhead);
rbd->ierbd_el = 0;
} else {
es->es_rbdtail = rbd;
rbd->ierbd_next = 0;
rbd->ierbd_el = 1;
}
es->es_rbdhead = rbd;
rbd->ierbd_buf = to_ieaddr(es, iep->iep_data);
rbd->ierbd_sizehi = 1500 >> 8;
rbd->ierbd_sizelo = 1500 & 0xFF;
rbd->ierbd_iep = iep;
}
/*
* We allocate one fewer RFD than RBD to
* avoid a suspected microcode bug in the chip
*/
nrfd = i-1;
es->es_rbdtail->ierbd_next = to_ieoff(es, (caddr_t)es->es_rbdhead);
es->es_rfdhead = NULL;
for (i=0; i<nrfd; i++) {
rfd = escballoc(es, struct ierfd, 0);
if (rfd == NULL) {
break;
}
*(short *)rfd = 0;
if (es->es_rfdhead) {
rfd->ierfd_next = to_ieoff(es, (caddr_t)es->es_rfdhead);
rfd->ierfd_el = 0;
} else {
es->es_rfdtail = rfd;
rfd->ierfd_next = 0;
rfd->ierfd_el = 1;
}
es->es_rfdhead = rfd;
rfd->ierfd_susp = 0;
rfd->ierfd_rbd = IENORBD;
}
if (i != nrfd)
printf("ie%d: fewer RFD's were allocated than expected\n",
unit);
es->es_rfdtail->ierfd_next = to_ieoff(es, (caddr_t)es->es_rfdhead);
rfd = es->es_rfdhead;
rfd->ierfd_rbd = to_ieoff(es, (caddr_t)rbd);
scb = es->es_scb;
if (scb->ie_rus != IERUS_IDLE) {
printf("ie%d: RU not idle??\n", unit);
iestat(es);
iechkcca(scb);
scb->ie_cmd = IECMD_RU_ABORT;
ieca(es);
}
iechkcca(scb);
scb->ie_rfa = to_ieoff(es, (caddr_t)rfd);
scb->ie_cmd = IECMD_RU_START;
ieca(es);
CDELAY(scb->ie_rus == IERUS_READY, IEDELAY);
if (scb->ie_rus != IERUS_READY)
printf("ie%d: RU did not become ready\n", unit);
}
/*
* Put a CB on the CBL
*/
iedocb(es, cb)
register struct ie_softc *es;
register struct iecb *cb;
{
int s = splie();
*(short *)cb = 0; /* clear status bits */
cb->ie_susp = 0; /* clear suspend bit */
cb->ie_el = 1; /* will be reset in iecustart */
cb->ie_intr = 1;
cb->ie_next = 0;
if (es->es_cbhead) {
es->es_cbtail->ie_next = to_ieoff(es, (caddr_t)cb);
es->es_cbtail = cb;
} else {
es->es_cbhead = es->es_cbtail = cb;
}
(void) splx(s);
}
/*
* Process completed CBs, reclaiming specified storage.
* Allocator is responsible for reclaiming other storage.
* Called by iecustart at splie.
* Called by iecbdone at splie or hardware level 3.
*/
iecuclean(es)
register struct ie_softc *es;
{
register struct iecb *cb;
while ((cb = es->es_cbhead) && cb->ie_done) {
if (cb->ie_next)
es->es_cbhead = (struct iecb *)from_ieoff(es,
(ieoff_t)cb->ie_next);
else
es->es_cbhead = NULL;
switch (cb->ie_cmd) {
case IE_TRANSMIT:
iexmitdone(es, (struct ietfd *)cb);
break;
default:
if (!es->es_simple)
printf("ie%d: unknown cmd %x done\n",
es-ie_softc, cb->ie_cmd);
break;
}
}
}
/*
* Start the CU with the current CBL
*/
iecustart(es)
register struct ie_softc *es;
{
register struct iecb *cb;
register struct iescb *scb = es->es_scb;
int s = splie();
iechkcca(scb);
if (es->es_cbhead == NULL ||
scb->ie_cus == IECUS_READY) { /* still going */
(void) splx(s);
return;
}
iecuclean(es);
/* link remaining CBs into continuous list */
if ((cb = es->es_cbhead) == NULL) {
(void) splx(s);
return;
}
while (cb && cb->ie_next) {
cb->ie_el = 0;
cb = (struct iecb *)from_ieoff(es, (ieoff_t)cb->ie_next);
}
/* start CU */
scb->ie_cbl = to_ieoff(es, (caddr_t)es->es_cbhead);
scb->ie_cmd = IECMD_CU_START;
ieca(es);
(void) splx(s);
}
/*
* Clean up and restart the CBs on the CBL
* Called by ieintr at hardware level 3.
* Called by iesimple at splie.
*/
iecbdone(es)
register struct ie_softc *es;
{
iecuclean(es);
/* generate more CBs */
iestartout(es - ie_softc);
}
/*
* Do the command simply.
* Attempted sleep/wakeup calls, but it refused to work.
*/
iesimple(es, cb)
register struct ie_softc *es;
register struct iecb *cb;
{
register struct iescb *scb = es->es_scb;
int s, cmd = 0;
es->es_simple++;
iedocb(es, cb);
iecustart(es);
CDELAY(cb->ie_done, IEDELAY);
if (!cb->ie_done) {
iestat(es);
panic("iesimple");
}
s = splie();
iechkcca(scb);
if (scb->ie_cx)
cmd |= IECMD_ACK_CX;
if (scb->ie_cnr)
cmd |= IECMD_ACK_CNR;
scb->ie_cmd = cmd;
ieca(es);
if (cmd & (IECMD_ACK_CNR|IECMD_ACK_CX))
iecbdone(es);
es->es_simple--;
(void) splx(s);
}
/*
* Return chip's idea of given address or 0 if not chip accessible
*/
ieaddr(es, cp)
register struct ie_softc *es;
caddr_t cp;
{
int pte;
if (es->es_type == IE_OB) {
/* onboard ie may only reference obmem */
if ((getkpgmap(cp) & PGT_MASK) != PGT_OBMEM)
cp = (caddr_t)0;
#ifdef sun3
else if (cp < es->es_memspace)
cp = (caddr_t)0;
else
cp -= (u_long)es->es_memspace;
#endif sun3
return ((int)cp);
}
pte = getkpgmap(cp) & PG_PFNUM;
if (pte >= es->es_paddr && pte < es->es_paddr + btoc(es->es_vmemsize))
return ((pte - es->es_paddr) << BSHIFT(0)) + ((int)cp & CLOFSET);
return (0);
}
/*
* Check Control Command Acceptance by 82586
*/
iechkcca(scb)
register struct iescb *scb;
{
register i;
for (i=0; i < IEDELAY; i++) {
if (scb->ie_magic != IEMAGIC)
panic("ie: scb overwritten");
if (scb->ie_cmd == 0)
break;
}
if (i >= IEDELAY) {
printf("ie: cmd not accepted\n");
panic("iechkcca");
}
}
/*
* The control block caching code to bypass rmalloc/rmfree.
* It also allows us to check allocated lengths, as well
* as aiding instrumentation for verification and tuning.
* We have two sizes, small and large.
* If the request exceeds the small threshold, we allocate
* the larger block.
*/
#define lo es->es_cbc_lo
#define hi es->es_cbc_hi
#define cache es->es_cbcache
#define ovfl es->es_cbc_ovfl
#define small iecbcsmall
#define large iecbclarge
#define head es->es_cbchain.next
#define chain es->es_cbchain
/*
* Initialize the control block cache
*/
iecbcinit(es)
register struct ie_softc *es;
{
if (es->es_cbcbusy)
iecbcflush(es);
lo = -1;
hi = CBCACHESIZ;
small = sizeof (struct ietbd);
large = sizeof (struct ietfd);
}
/*
* cached is a flag used to indicate that the block must be
* cached, as it will be freed (for reallocation.)
*/
long
escbget(es, len0, cached)
register struct ie_softc *es;
int len0;
int cached;
{
long result;
int len = rndtoeven(len0);
if (!cached)
result = (rmalloc(es->es_cbmap, (long)len));
else if (len <= small)
if (lo > -1)
result = (cache[lo--]);
else
result = (rmalloc(es->es_cbmap, (long)small));
else if (len <= large)
if (hi < CBCACHESIZ)
result = (cache[hi++]);
else
result = (rmalloc(es->es_cbmap, (long)large));
else
panic ("escbget"); /* len too large */
if (result == 0 && es->es_cbcbusy) {
iecbcflush(es);
result = escbget(es, len0, cached);
}
return (result);
}
/*
* flush the cb cache
*/
iecbcflush(es)
struct ie_softc *es;
{
printf("WARNING: ie%d: flushing cb cache\n", es - ie_softc);
while (lo > -1)
rmfree(es->es_cbmap, (long)small, (long)cache[lo--]);
while (hi < CBCACHESIZ)
rmfree(es->es_cbmap, (long)large, (long)cache[hi++]);
es->es_cbcbusy = 0;
}
long
escbput(es, len, ptr)
register struct ie_softc *es;
int len;
long ptr;
{
int overflow = 0;
es->es_cbcbusy = 1;
len = rndtoeven(len);
if (len <= small)
if (lo < hi - 1)
cache[++lo] = ptr;
else {
overflow++;
rmfree(es->es_cbmap, (long)small, (long)ptr);
}
else if (len <= large)
if (hi > lo + 1)
cache[--hi] = ptr;
else {
overflow++;
rmfree(es->es_cbmap, (long)large, (long)ptr);
}
else
panic("escbput");
if (overflow) {
ovfl++;
if ((ovfl & OVFLWARNMASK) == 0) {
ovfl = 0;
printf("ie%d: cache overflowed\n", es - ie_softc);
}
}
}
#undef lo
#undef hi
#undef cache
#undef ovfl
#undef small
#undef large
int iefifolim = 12;
/*
* Set default configuration parameters
*/
iedefaultconf(ic)
register struct ieconf *ic;
{
bzero((caddr_t)ic, sizeof (struct ieconf));
ic->ieconf_cb.ie_cmd = IE_CONFIG;
ic->ieconf_bytes = 12;
ic->ieconf_fifolim = iefifolim;
ic->ieconf_pream = 2; /* 8 byte preamble */
ic->ieconf_alen = 6;
ic->ieconf_acloc = 0;
ic->ieconf_space = 96;
ic->ieconf_slttmh = 512 >> 8;
ic->ieconf_minfrm = 64;
ic->ieconf_retry = 15;
ic->ieconf_crfilt = 3;
}
iestat(es)
struct ie_softc *es;
{
register struct iescb *scb = es->es_scb;
static char *cus[] = { "idle", "suspended", "ready", "<3>",
"<4>", "<5>", "<6>", "<7>" };
static char *rus[] = { "idle", "suspended", "no resources",
"<3>", "ready", "<5>", "<6>", "<7>" };
printf("ie%d: scb: ", es - ie_softc);
if (scb->ie_cx) printf("cx ");
if (scb->ie_fr) printf("fr ");
if (scb->ie_cnr) printf("cnr ");
if (scb->ie_rnr) printf("rnr ");
printf("cus=%s ", cus[scb->ie_cus]);
printf("rus=%s\n", rus[scb->ie_rus]);
printf("cbl=0x%x rfa=0x%x crc=0x%x aln=0x%x rsc=0x%x ovrn=0x%x\n",
scb->ie_cbl, scb->ie_rfa,
scb->ie_crcerrs, scb->ie_alnerrs, scb->ie_rscerrs,
scb->ie_ovrnerrs);
if (scb->ie_cmd) printf("cmd=0x%x\n", scb->ie_cmd & 0xFFFF);
}
/*
* Parity error! Scan entire memory for errors
*/
ieparity(es)
register struct ie_softc *es;
{
register struct mie_device *mie = es->es_mie;
register u_short *s, *e, x;
printf("ie%d: parity error src=%d byte=%d addr=%x\n", es-ie_softc,
mie->mie_pesrc, mie->mie_pebyte, mie->mie_erraddr);
mie->mie_peack = 1;
s = (u_short *)es->es_memspace;
e = (u_short *)(es->es_memspace + es->es_vmemsize);
printf("scanning...\n");
while (s < e) {
x = *s;
#ifdef lint
x = x;
#endif
if (mie->mie_pe) {
printf("off=%x src=%d byte=%d addr=%x\n",
(int)s - (int)es->es_memspace,
mie->mie_pesrc, mie->mie_pebyte,
mie->mie_erraddr);
mie->mie_peack = 1;
}
s++;
}
printf("done\n");
}
/*
* Activate the channel attention line
*/
ieca(es)
register struct ie_softc *es;
{
if (es->es_type == IE_MB) {
es->es_mie->mie_ca = 1;
es->es_mie->mie_ca = 0;
} else {
es->es_obie->obie_ca = 1;
es->es_obie->obie_ca = 0;
}
}
/*
* Wait for an interrupt and relay results
*/
int
iewaitintr(es)
register struct ie_softc *es;
{
register struct obie_device *obie = es->es_obie;
register struct mie_device *mie = es->es_mie;
int ok;
switch (es->es_type) {
case IE_OB:
CDELAY(obie->obie_intr, IEDELAY);
ok = obie->obie_intr;
break;
case IE_MB:
CDELAY(mie->mie_intr, IEDELAY);
ok = mie->mie_intr;
break;
}
return (ok);
}
/*
* Cut the chip out of the loop and halt it by starting the reset cycle.
* For IE_MB, we must enable pagemaps, hence we complete the reset cycle.
*/
iechipreset(es)
register struct ie_softc *es;
{
switch (es->es_type) {
case IE_MB:
es->es_mie->mie_reset = 1;
DELAY(IEKLUDGE); /* REQUIRED */
*(char *)&es->es_mie->mie_status = 0; /* power on reset */
break;
case IE_OB:
*(char *)es->es_obie = 0; /* power on reset */
break;
default:
panic("iechipreset");
}
}
/*
* Splice the chip into the loop
*/
iesplice(es)
register struct ie_softc *es;
{
switch (es->es_type) {
case IE_MB:
es->es_mie->mie_ie = 1; /* enable chip interrupts */
es->es_mie->mie_pie = 1; /* enable parity interrupts */
es->es_mie->mie_noloop = 1; /* enable cable */
break;
case IE_OB:
es->es_obie->obie_ie = 1; /* enable interrupts */
es->es_obie->obie_noloop = 1; /* enable cable */
break;
}
}
/*
* Change 68000 address to Intel 24-bit address.
* We take advantage of the fact that all 82586 blocks with 24-bit
* addresses have an adjacent unused 8-bit field, and store 32 bits.
*/
ieaddr_t
to_ieaddr(es, cp)
struct ie_softc *es;
caddr_t cp;
{
union {
int n;
char c[4];
} a, b;
a.n = ieaddr(es, cp); /* necessary for double mapping */
b.c[0] = a.c[3];
b.c[1] = a.c[2];
b.c[2] = a.c[1];
b.c[3] = 0;
return (b.n);
}
caddr_t
from_ieaddr(es, n)
struct ie_softc *es;
ieaddr_t n;
{
union {
int n;
char c[4];
} a, b;
a.n = n;
b.c[0] = 0;
b.c[1] = a.c[2];
b.c[2] = a.c[1];
b.c[3] = a.c[0];
return (es->es_memspace + b.n);
}
ieoff_t
to_ieoff(es, addr)
register struct ie_softc *es;
caddr_t addr;
{
union xxx {
ieoff_t s;
char c[2];
} a, b;
a.s = (ieoff_t)(addr - es->es_base);
b.c[0] = a.c[1];
b.c[1] = a.c[0];
return (b.s);
}
caddr_t
from_ieoff(es, off)
register struct ie_softc *es;
ieoff_t off;
{
union {
ieoff_t s;
char c[2];
} a, b;
a.s = off;
b.c[0] = a.c[1];
b.c[1] = a.c[0];
return (es->es_base + b.s);
}
ieint_t
to_ieint(n)
ieint_t n;
{
union {
ieint_t s;
char c[2];
} a, b;
a.s = n;
b.c[0] = a.c[1];
b.c[1] = a.c[0];
return (b.s);
}
#endif NIE > 0