diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2006-08-30 19:28:14 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2006-08-30 19:28:14 +0000 |
commit | 302cfe701533ce5dd8ffa808ba95ceac1468a01a (patch) | |
tree | ef8e863d4796abdd6973bad3d244c451d864bcce | |
parent | 701389a42635590876733b8299f8de9263429650 (diff) |
Teach the SGEC driver about the Real World:
- check for error bits in descriptors, and count and report errors
accordingly; make sure we do not pass up known-to-be-bad packets up the
receive path.
- check for various error conditions which would cause the transmit process
to go to suspended state, and recover from them, before the timeout expires.
- finally, on the VXT2000, the transmit process for some reason will reset
its ring position to the beginning of the ring every time it completes a
transmission burst, while we don't, so play with the head of ring register
behind its back. This makes my VXT2000+ (net)boot multiuser.
Tested on VXT2000 (ze@vxtbus), KA49 (ze@vsbus) and KA53 (ze@ibus); ok deraadt@
-rw-r--r-- | sys/arch/vax/if/if_ze.c | 4 | ||||
-rw-r--r-- | sys/arch/vax/if/sgec.c | 210 | ||||
-rw-r--r-- | sys/arch/vax/if/sgecvar.h | 6 | ||||
-rw-r--r-- | sys/arch/vax/vsa/if_ze_vsbus.c | 4 | ||||
-rw-r--r-- | sys/arch/vax/vxt/if_ze_vxtbus.c | 3 |
5 files changed, 165 insertions, 62 deletions
diff --git a/sys/arch/vax/if/if_ze.c b/sys/arch/vax/if/if_ze.c index 05c8027aca1..bc5f9c91a31 100644 --- a/sys/arch/vax/if/if_ze.c +++ b/sys/arch/vax/if/if_ze.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ze.c,v 1.6 2006/08/27 16:50:44 miod Exp $ */ +/* $OpenBSD: if_ze.c,v 1.7 2006/08/30 19:28:11 miod Exp $ */ /* $NetBSD: if_ze.c,v 1.3 2000/01/24 02:54:03 matt Exp $ */ /* * Copyright (c) 1999 Ludd, University of Lule}, Sweden. All rights reserved. @@ -125,6 +125,8 @@ zeattach(parent, self, aux) scb_vecalloc(SGECVEC, (void (*)(void *)) sgec_intr, sc, SCB_ISTACK, &sc->sc_intrcnt); + evcount_attach(&sc->sc_intrcnt, sc->sc_dev.dv_xname, + (void *)&sc->sc_intvec, &evcount_intr); sgec_attach(sc); } diff --git a/sys/arch/vax/if/sgec.c b/sys/arch/vax/if/sgec.c index ccc7bbef789..45aca10ba3d 100644 --- a/sys/arch/vax/if/sgec.c +++ b/sys/arch/vax/if/sgec.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sgec.c,v 1.13 2006/08/27 16:50:44 miod Exp $ */ +/* $OpenBSD: sgec.c,v 1.14 2006/08/30 19:28:11 miod Exp $ */ /* $NetBSD: sgec.c,v 1.5 2000/06/04 02:14:14 matt Exp $ */ /* * Copyright (c) 1999 Ludd, University of Lule}, Sweden. All rights reserved. @@ -70,13 +70,16 @@ #include <vax/if/sgecreg.h> #include <vax/if/sgecvar.h> -static void zeinit(struct ze_softc *); -static void zestart(struct ifnet *); -static int zeioctl(struct ifnet *, u_long, caddr_t); -static int ze_add_rxbuf(struct ze_softc *, int); -static void ze_setup(struct ze_softc *); -static void zetimeout(struct ifnet *); -static int zereset(struct ze_softc *); +void sgec_rxintr(struct ze_softc *); +void sgec_txintr(struct ze_softc *); +void zeinit(struct ze_softc *); +int zeioctl(struct ifnet *, u_long, caddr_t); +void zekick(struct ze_softc *); +int zereset(struct ze_softc *); +void zestart(struct ifnet *); +void zetimeout(struct ifnet *); +int ze_add_rxbuf(struct ze_softc *, int); +void ze_setup(struct ze_softc *); struct cfdriver ze_cd = { NULL, "ze", DV_IFNET @@ -177,11 +180,6 @@ sgec_attach(sc) } } - /* For vmstat -i - */ - evcount_attach(&sc->sc_intrcnt, sc->sc_dev.dv_xname, - (void *)&sc->sc_intvec, &evcount_intr); - /* * Create ring loops of the buffer chains. * This is only done once. @@ -288,8 +286,8 @@ zeinit(sc) zc->zc_recv[i].ze_framelen = ZE_FRAMELEN_OW; sc->sc_nextrx = 0; - ZE_WCSR(ZE_CSR6, ZE_NICSR6_IE|ZE_NICSR6_BL_8|ZE_NICSR6_ST| - ZE_NICSR6_SR|ZE_NICSR6_DC); + ZE_WCSR(ZE_CSR6, ZE_NICSR6_IE | ZE_NICSR6_BL_8 | ZE_NICSR6_ST | + ZE_NICSR6_SR | ZE_NICSR6_DC); ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; @@ -303,6 +301,39 @@ zeinit(sc) } /* + * Kick off the transmit logic, if it is stopped. + * On the VXT2000 we need to always reprogram CSR4, + * so stop it unconditionnaly. + */ +void +zekick(struct ze_softc *sc) +{ + u_int csr5; + + csr5 = ZE_RCSR(ZE_CSR5); + if (ISSET(sc->sc_flags, SGECF_VXTQUIRKS)) { + if ((csr5 & ZE_NICSR5_TS) == ZE_NICSR5_TS_RUN) { + ZE_WCSR(ZE_CSR6, ZE_RCSR(ZE_CSR6) & ~ZE_NICSR6_ST); + while ((ZE_RCSR(ZE_CSR5) & ZE_NICSR5_TS) != + ZE_NICSR5_TS_STOP) + DELAY(10); + } + ZE_WCSR(ZE_CSR4, + (vaddr_t)&sc->sc_pzedata->zc_xmit[sc->sc_nexttx]); + ZE_WCSR(ZE_CSR1, ZE_NICSR1_TXPD); + if ((csr5 & ZE_NICSR5_TS) == ZE_NICSR5_TS_RUN) { + ZE_WCSR(ZE_CSR6, ZE_RCSR(ZE_CSR6) | ZE_NICSR6_ST); + while ((ZE_RCSR(ZE_CSR5) & ZE_NICSR5_TS) == + ZE_NICSR5_TS_STOP) + DELAY(10); + } + } else { + if ((csr5 & ZE_NICSR5_TS) != ZE_NICSR5_TS_RUN) + ZE_WCSR(ZE_CSR1, ZE_NICSR1_TXPD); + } +} + +/* * Start output on interface. */ void @@ -319,8 +350,7 @@ zestart(ifp) s = splnet(); while (sc->sc_inq < (TXDESCS - 1)) { - - if (sc->sc_setup) { + if (ISSET(sc->sc_flags, SGECF_SETUP)) { ze_setup(sc); continue; } @@ -390,8 +420,7 @@ zestart(ifp) /* * Kick off the transmit logic, if it is stopped. */ - if ((ZE_RCSR(ZE_CSR5) & ZE_NICSR5_TS) != ZE_NICSR5_TS_RUN) - ZE_WCSR(ZE_CSR1, ZE_NICSR1_TXPD); + zekick(sc); sc->sc_nexttx = idx; } if (sc->sc_inq == (TXDESCS - 1)) @@ -402,33 +431,39 @@ out: if (old_inq < sc->sc_inq) splx(s); } -int -sgec_intr(sc) - struct ze_softc *sc; +void +sgec_rxintr(struct ze_softc *sc) { struct ze_cdata *zc = sc->sc_zedata; struct ifnet *ifp = &sc->sc_if; struct ether_header *eh; struct mbuf *m; - int csr, len; - - csr = ZE_RCSR(ZE_CSR5); - if ((csr & ZE_NICSR5_IS) == 0) /* Wasn't we */ - return 0; - ZE_WCSR(ZE_CSR5, csr); - - if (csr & ZE_NICSR5_RI) - while ((zc->zc_recv[sc->sc_nextrx].ze_framelen & - ZE_FRAMELEN_OW) == 0) { - + u_short rdes0; + int len; + + while ((zc->zc_recv[sc->sc_nextrx].ze_framelen & + ZE_FRAMELEN_OW) == 0) { + rdes0 = zc->zc_recv[sc->sc_nextrx].ze_rdes0; + if (rdes0 & ZE_RDES0_ES) { + rdes0 &= ~ZE_RDES0_TL; /* not really an error */ + if ((rdes0 & (ZE_RDES0_OF | ZE_RDES0_CE | ZE_RDES0_CS | + ZE_RDES0_LE | ZE_RDES0_RF)) == 0) + rdes0 &= ~ZE_RDES0_ES; + } + if (rdes0 & ZE_RDES0_ES) { + ifp->if_ierrors++; + if (rdes0 & ZE_RDES0_CS) + ifp->if_collisions++; + m = NULL; + } else { ifp->if_ipackets++; m = sc->sc_rxmbuf[sc->sc_nextrx]; len = zc->zc_recv[sc->sc_nextrx].ze_framelen; - ze_add_rxbuf(sc, sc->sc_nextrx); + } + ze_add_rxbuf(sc, sc->sc_nextrx); + if (m != NULL) { m->m_pkthdr.rcvif = ifp; m->m_pkthdr.len = m->m_len = len; - if (++sc->sc_nextrx == RXDESCS) - sc->sc_nextrx = 0; eh = mtod(m, struct ether_header *); #if NBPFILTER > 0 if (ifp->if_bpf) { @@ -452,24 +487,56 @@ sgec_intr(sc) m_freem(m); continue; } - ether_input_mbuf(ifp, m); } + if (++sc->sc_nextrx == RXDESCS) + sc->sc_nextrx = 0; + } +} - if (csr & ZE_NICSR5_TI) { - while ((zc->zc_xmit[sc->sc_lastack].ze_tdr & ZE_TDR_OW) == 0) { - int idx = sc->sc_lastack; +void +sgec_txintr(struct ze_softc *sc) +{ + struct ze_cdata *zc = sc->sc_zedata; + struct ifnet *ifp = &sc->sc_if; + u_short tdes0; - if (sc->sc_lastack == sc->sc_nexttx) - break; - sc->sc_inq--; - if (++sc->sc_lastack == TXDESCS) - sc->sc_lastack = 0; + while ((zc->zc_xmit[sc->sc_lastack].ze_tdr & ZE_TDR_OW) == 0) { + int idx = sc->sc_lastack; - if ((zc->zc_xmit[idx].ze_tdes1 & ZE_TDES1_DT) == - ZE_TDES1_DT_SETUP) - continue; - /* XXX collect statistics */ + if (sc->sc_lastack == sc->sc_nexttx) + break; + sc->sc_inq--; + if (++sc->sc_lastack == TXDESCS) + sc->sc_lastack = 0; + + if ((zc->zc_xmit[idx].ze_tdes1 & ZE_TDES1_DT) == + ZE_TDES1_DT_SETUP) { + continue; + } + + tdes0 = zc->zc_xmit[idx].ze_tdes0; + if (tdes0 & ZE_TDES0_ES) { + if (tdes0 & ZE_TDES0_TO) + printf("%s: transmit watchdog timeout\n", + sc->sc_dev.dv_xname); + if (tdes0 & (ZE_TDES0_LO | ZE_TDES0_NC)) + printf("%s: no carrier\n", + sc->sc_dev.dv_xname); + if (tdes0 & ZE_TDES0_EC) { + printf("%s: excessive collisions, tdr %d\n", + sc->sc_dev.dv_xname, + zc->zc_xmit[idx].ze_tdr & ~ZE_TDR_OW); + ifp->if_collisions += 16; + } else if (tdes0 & ZE_TDES0_LC) + ifp->if_collisions += + (tdes0 & ZE_TDES0_CC) >> 3; + if (tdes0 & ZE_TDES0_UF) + printf("%s: underflow\n", sc->sc_dev.dv_xname); + ifp->if_oerrors++; + if (tdes0 & (ZE_TDES0_TO | ZE_TDES0_UF)) + zeinit(sc); + } else { if (zc->zc_xmit[idx].ze_tdes1 & ZE_TDES1_LS) ifp->if_opackets++; bus_dmamap_unload(sc->sc_dmat, sc->sc_xmtmap[idx]); @@ -478,11 +545,36 @@ sgec_intr(sc) sc->sc_txmbuf[idx] = 0; } } - if (sc->sc_inq == 0) - ifp->if_timer = 0; - ifp->if_flags &= ~IFF_OACTIVE; - zestart(ifp); /* Put in more in queue */ } + if (sc->sc_inq == 0) + ifp->if_timer = 0; + ifp->if_flags &= ~IFF_OACTIVE; + zestart(ifp); /* Put in more in queue */ +} + +int +sgec_intr(sc) + struct ze_softc *sc; +{ + int csr; + + csr = ZE_RCSR(ZE_CSR5); + if ((csr & ZE_NICSR5_IS) == 0) /* Wasn't we */ + return 0; + ZE_WCSR(ZE_CSR5, csr); + + if (csr & ZE_NICSR5_ME) { + printf("%s: memory error, resetting\n", sc->sc_dev.dv_xname); + zeinit(sc); + return (1); + } + + if (csr & ZE_NICSR5_RI) + sgec_rxintr(sc); + + if (csr & ZE_NICSR5_TI) + sgec_txintr(sc); + return 1; } @@ -632,11 +724,12 @@ ze_setup(sc) s = splnet(); if (sc->sc_inq == (TXDESCS - 1)) { - sc->sc_setup = 1; + SET(sc->sc_flags, SGECF_SETUP); splx(s); return; } - sc->sc_setup = 0; + CLR(sc->sc_flags, SGECF_SETUP); + /* * Init the setup packet with valid info. */ @@ -670,6 +763,8 @@ ze_setup(sc) reg = ZE_RCSR(ZE_CSR6); DELAY(10); ZE_WCSR(ZE_CSR6, reg & ~ZE_NICSR6_SR); /* Stop rx */ + while ((ZE_RCSR(ZE_CSR5) & ZE_NICSR5_RS) != ZE_NICSR5_RS_STOP) + DELAY(10); reg &= ~ZE_NICSR6_AF; if (ifp->if_flags & IFF_PROMISC) reg |= ZE_NICSR6_AF_PROM; @@ -677,6 +772,8 @@ ze_setup(sc) reg |= ZE_NICSR6_AF_ALLM; DELAY(10); ZE_WCSR(ZE_CSR6, reg); + while ((ZE_RCSR(ZE_CSR5) & ZE_NICSR5_RS) == ZE_NICSR5_RS_STOP) + DELAY(10); /* * Only send a setup packet if needed. */ @@ -687,8 +784,7 @@ ze_setup(sc) zc->zc_xmit[idx].ze_bufaddr = sc->sc_pzedata->zc_setup; zc->zc_xmit[idx].ze_tdr = ZE_TDR_OW; - if ((ZE_RCSR(ZE_CSR5) & ZE_NICSR5_TS) != ZE_NICSR5_TS_RUN) - ZE_WCSR(ZE_CSR1, -1); + zekick(sc); sc->sc_inq++; if (++sc->sc_nexttx == TXDESCS) diff --git a/sys/arch/vax/if/sgecvar.h b/sys/arch/vax/if/sgecvar.h index 268d2310102..6e1417527d6 100644 --- a/sys/arch/vax/if/sgecvar.h +++ b/sys/arch/vax/if/sgecvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sgecvar.h,v 1.4 2004/07/07 23:10:45 deraadt Exp $ */ +/* $OpenBSD: sgecvar.h,v 1.5 2006/08/30 19:28:11 miod Exp $ */ /* $NetBSD: sgecvar.h,v 1.2 2000/06/04 02:14:14 matt Exp $ */ /* * Copyright (c) 1999 Ludd, University of Lule}, Sweden. All rights reserved. @@ -62,7 +62,9 @@ struct ze_softc { int sc_inq; int sc_lastack; int sc_nextrx; - int sc_setup; /* Setup packet in queue */ + int sc_flags; +#define SGECF_SETUP 0x00000001 /* need to send setup packet */ +#define SGECF_VXTQUIRKS 0x00000002 /* need VXT2000 care */ }; void sgec_attach(struct ze_softc *); diff --git a/sys/arch/vax/vsa/if_ze_vsbus.c b/sys/arch/vax/vsa/if_ze_vsbus.c index 66fd54b3563..24035da888e 100644 --- a/sys/arch/vax/vsa/if_ze_vsbus.c +++ b/sys/arch/vax/vsa/if_ze_vsbus.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ze_vsbus.c,v 1.3 2003/11/07 10:16:45 jmc Exp $ */ +/* $OpenBSD: if_ze_vsbus.c,v 1.4 2006/08/30 19:28:13 miod Exp $ */ /* $NetBSD: if_ze_vsbus.c,v 1.5 2000/07/26 21:50:49 matt Exp $ */ /* * Copyright (c) 1999 Ludd, University of Lule}, Sweden. All rights reserved. @@ -116,6 +116,8 @@ zeattach(parent, self, aux) sc->sc_intvec = SGECVEC; scb_vecalloc(va->va_cvec, (void (*)(void *)) sgec_intr, sc, SCB_ISTACK, &sc->sc_intrcnt); + evcount_attach(&sc->sc_intrcnt, sc->sc_dev.dv_xname, + (void *)&sc->sc_intvec, &evcount_intr); /* * Map in, read and release ethernet rom address. diff --git a/sys/arch/vax/vxt/if_ze_vxtbus.c b/sys/arch/vax/vxt/if_ze_vxtbus.c index ee4e4ed08e1..727cded771f 100644 --- a/sys/arch/vax/vxt/if_ze_vxtbus.c +++ b/sys/arch/vax/vxt/if_ze_vxtbus.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_ze_vxtbus.c,v 1.1 2006/08/27 16:55:41 miod Exp $ */ +/* $OpenBSD: if_ze_vxtbus.c,v 1.2 2006/08/30 19:28:13 miod Exp $ */ /* * Copyright (c) 1999 Ludd, University of Lule}, Sweden. All rights reserved. * @@ -91,5 +91,6 @@ ze_vxt_attach(struct device *parent, struct device *self, void *aux) sc->sc_ac.ac_enaddr[i] = ea[i] & 0xff; vax_unmap_physmem((vaddr_t)ea, 1); + SET(sc->sc_flags, SGECF_VXTQUIRKS); sgec_attach(sc); } |