summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2006-08-30 19:28:14 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2006-08-30 19:28:14 +0000
commit302cfe701533ce5dd8ffa808ba95ceac1468a01a (patch)
treeef8e863d4796abdd6973bad3d244c451d864bcce
parent701389a42635590876733b8299f8de9263429650 (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.c4
-rw-r--r--sys/arch/vax/if/sgec.c210
-rw-r--r--sys/arch/vax/if/sgecvar.h6
-rw-r--r--sys/arch/vax/vsa/if_ze_vsbus.c4
-rw-r--r--sys/arch/vax/vxt/if_ze_vxtbus.c3
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);
}