if_vx.c revision 139749
1139749Simp/*-
219410Sguido * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca>
319410Sguido * All rights reserved.
419410Sguido *
519410Sguido * Redistribution and use in source and binary forms, with or without
619410Sguido * modification, are permitted provided that the following conditions
719410Sguido * are met:
819410Sguido * 1. Redistributions of source code must retain the above copyright
919410Sguido *    notice, this list of conditions and the following disclaimer.
1019410Sguido * 2. Redistributions in binary form must reproduce the above copyright
1119410Sguido *    notice, this list of conditions and the following disclaimer in the
1219410Sguido *    documentation and/or other materials provided with the distribution.
1319410Sguido * 3. All advertising materials mentioning features or use of this software
1419410Sguido *    must display the following acknowledgement:
1519410Sguido *      This product includes software developed by Herb Peyerl.
1619410Sguido * 4. The name of Herb Peyerl may not be used to endorse or promote products
1719410Sguido *    derived from this software without specific prior written permission.
1819410Sguido *
1919410Sguido * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2019410Sguido * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2119410Sguido * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2219410Sguido * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2319410Sguido * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2419410Sguido * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2519410Sguido * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2619410Sguido * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2719410Sguido * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2819410Sguido * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2919410Sguido *
3033707Sgpalmer *
3119410Sguido */
3219410Sguido
33119418Sobrien#include <sys/cdefs.h>
34119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/vx/if_vx.c 139749 2005-01-06 01:43:34Z imp $");
35119418Sobrien
3619410Sguido/*
3719410Sguido * Created from if_ep.c driver by Fred Gray (fgray@rice.edu) to support
3819410Sguido * the 3c590 family.
3919410Sguido */
4019410Sguido
4119410Sguido/*
4219410Sguido *	Modified from the FreeBSD 1.1.5.1 version by:
4319410Sguido *		 	Andres Vega Garcia
4419410Sguido *			INRIA - Sophia Antipolis, France
4519410Sguido *			avega@sophia.inria.fr
4619410Sguido */
4719410Sguido
4819410Sguido/*
4919410Sguido *  Promiscuous mode added and interrupt logic slightly changed
5019410Sguido *  to reduce the number of adapter failures. Transceiver select
5119410Sguido *  logic changed to use value from EEPROM. Autoconfiguration
5219410Sguido *  features added.
5319410Sguido *  Done by:
5419410Sguido *          Serge Babkin
5519410Sguido *          Chelindbank (Chelyabinsk, Russia)
5619410Sguido *          babkin@hq.icb.chel.su
5719410Sguido */
5819410Sguido
5919410Sguido
6019410Sguido#include <sys/param.h>
6119410Sguido#include <sys/systm.h>
6224204Sbde#include <sys/sockio.h>
6326640Sbde#include <sys/malloc.h>
6419410Sguido#include <sys/mbuf.h>
6519410Sguido#include <sys/socket.h>
6619410Sguido
6719410Sguido#include <net/if.h>
6819410Sguido
6932350Seivind#include <net/ethernet.h>
7032350Seivind#include <net/if_arp.h>
7119410Sguido
7268417Swpaul#include <machine/bus_pio.h>
7368417Swpaul#include <machine/bus.h>
7468417Swpaul
75121816Sbrooks#include <sys/bus.h>
76121816Sbrooks
7719410Sguido#include <net/bpf.h>
7819410Sguido
7919410Sguido#include <dev/vx/if_vxreg.h>
80121491Simp#include <dev/vx/if_vxvar.h>
8119410Sguido
8219410Sguido#define ETHER_MAX_LEN	1518
8319410Sguido#define ETHER_ADDR_LEN	6
8469732Swpaul#define ETHER_ALIGN 	2
8519410Sguido
8619410Sguidostatic struct connector_entry {
87133980Sgibbs	int bit;
88133980Sgibbs	char *name;
8920096Sguido} conn_tab[VX_CONNECTORS] = {
90133980Sgibbs
9119410Sguido#define CONNECTOR_UTP	0
92133980Sgibbs	{
93133980Sgibbs		0x08, "utp"
94133980Sgibbs	},
9519410Sguido#define CONNECTOR_AUI	1
96133980Sgibbs	{
97133980Sgibbs		0x20, "aui"
98133980Sgibbs	},
9919410Sguido/* dummy */
100133980Sgibbs	{
101133980Sgibbs		0, "???"
102133980Sgibbs	},
10319410Sguido#define CONNECTOR_BNC	3
104133980Sgibbs	{
105133980Sgibbs		0x10, "bnc"
106133980Sgibbs	},
10719410Sguido#define CONNECTOR_TX	4
108133980Sgibbs	{
109133980Sgibbs		0x02, "tx"
110133980Sgibbs	},
11119410Sguido#define CONNECTOR_FX	5
112133980Sgibbs	{
113133980Sgibbs		0x04, "fx"
114133980Sgibbs	},
11519410Sguido#define CONNECTOR_MII	6
116133980Sgibbs	{
117133980Sgibbs		0x40, "mii"
118133980Sgibbs	},
119133980Sgibbs	{
120133980Sgibbs		0, "???"
121133980Sgibbs	}
12219410Sguido};
12319410Sguido
12492739Salfred/* int vxattach(struct vx_softc *); */
12592739Salfredstatic void vxtxstat(struct vx_softc *);
12692739Salfredstatic int vxstatus(struct vx_softc *);
12792739Salfredstatic void vxinit(void *);
128133980Sgibbsstatic int vxioctl(struct ifnet *, u_long, caddr_t);
129133980Sgibbsstatic void vxstart(struct ifnet *);
13092739Salfredstatic void vxwatchdog(struct ifnet *);
13192739Salfredstatic void vxreset(struct vx_softc *);
13292739Salfredstatic void vxread(struct vx_softc *);
13392739Salfredstatic struct mbuf *vxget(struct vx_softc *, u_int);
13492739Salfredstatic void vxmbuffill(void *);
13592739Salfredstatic void vxmbufempty(struct vx_softc *);
13692739Salfredstatic void vxsetfilter(struct vx_softc *);
13792739Salfredstatic void vxgetlink(struct vx_softc *);
13892739Salfredstatic void vxsetlink(struct vx_softc *);
13919410Sguido
14019410Sguidoint
141133980Sgibbsvxattach(device_t dev)
14219410Sguido{
143133980Sgibbs	struct vx_softc *sc = device_get_softc(dev);
144133980Sgibbs	struct ifnet *ifp = &sc->arpcom.ac_if;
145133980Sgibbs	int i;
14619410Sguido
147133980Sgibbs	callout_handle_init(&sc->ch);
148133980Sgibbs	GO_WINDOW(0);
149133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, GLOBAL_RESET);
150133980Sgibbs	VX_BUSY_WAIT;
15119410Sguido
152133980Sgibbs	vxgetlink(sc);
15319410Sguido
154133980Sgibbs	/*
155133980Sgibbs         * Read the station address from the eeprom
156133980Sgibbs         */
157133980Sgibbs	GO_WINDOW(0);
158133980Sgibbs	for (i = 0; i < 3; i++) {
159133980Sgibbs		int x;
16019410Sguido
161133980Sgibbs		if (vxbusyeeprom(sc))
162133980Sgibbs			return 0;
163133980Sgibbs		CSR_WRITE_2(sc, VX_W0_EEPROM_COMMAND, EEPROM_CMD_RD
164133980Sgibbs		    | (EEPROM_OEM_ADDR0 + i));
165133980Sgibbs		if (vxbusyeeprom(sc))
166133980Sgibbs			return 0;
167133980Sgibbs		x = CSR_READ_2(sc, VX_W0_EEPROM_DATA);
168133980Sgibbs		sc->arpcom.ac_enaddr[(i << 1)] = x >> 8;
169133980Sgibbs		sc->arpcom.ac_enaddr[(i << 1) + 1] = x;
170133980Sgibbs	}
17119410Sguido
172133980Sgibbs	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
173133980Sgibbs	ifp->if_mtu = ETHERMTU;
174133980Sgibbs	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
175133980Sgibbs	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST |
176133980Sgibbs	    IFF_NEEDSGIANT;
177133980Sgibbs	ifp->if_start = vxstart;
178133980Sgibbs	ifp->if_ioctl = vxioctl;
179133980Sgibbs	ifp->if_init = vxinit;
180133980Sgibbs	ifp->if_watchdog = vxwatchdog;
181133980Sgibbs	ifp->if_softc = sc;
18219410Sguido
183133980Sgibbs	ether_ifattach(ifp, sc->arpcom.ac_enaddr);
18419410Sguido
185133980Sgibbs	sc->tx_start_thresh = 20;	/* probably a good starting point. */
18619410Sguido
187133980Sgibbs	vxstop(sc);
188133980Sgibbs
189133980Sgibbs	return 1;
19019410Sguido}
19119410Sguido
19219410Sguido/*
19319410Sguido * The order in here seems important. Otherwise we may not receive
19419410Sguido * interrupts. ?!
19519410Sguido */
19619410Sguidostatic void
197133980Sgibbsvxinit(void *xsc)
19819410Sguido{
199133980Sgibbs	struct vx_softc *sc = (struct vx_softc *)xsc;
200133980Sgibbs	struct ifnet *ifp = &sc->arpcom.ac_if;
201133980Sgibbs	int i;
20219410Sguido
203133980Sgibbs	VX_BUSY_WAIT;
20419410Sguido
205133980Sgibbs	GO_WINDOW(2);
20619410Sguido
207133980Sgibbs	for (i = 0; i < 6; i++)	/* Reload the ether_addr. */
208133980Sgibbs		CSR_WRITE_1(sc, VX_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]);
20919410Sguido
210133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, RX_RESET);
211133980Sgibbs	VX_BUSY_WAIT;
212133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, TX_RESET);
213133980Sgibbs	VX_BUSY_WAIT;
21419410Sguido
215133980Sgibbs	GO_WINDOW(1);		/* Window 1 is operating window */
216133980Sgibbs	for (i = 0; i < 31; i++)
217133980Sgibbs		CSR_READ_1(sc, VX_W1_TX_STATUS);
21819410Sguido
219133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE |
220133980Sgibbs	    S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL);
221133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, SET_INTR_MASK | S_CARD_FAILURE |
222133980Sgibbs	    S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL);
22319410Sguido
224133980Sgibbs	/*
225133980Sgibbs         * Attempt to get rid of any stray interrupts that occured during
226133980Sgibbs         * configuration.  On the i386 this isn't possible because one may
227133980Sgibbs         * already be queued.  However, a single stray interrupt is
228133980Sgibbs         * unimportant.
229133980Sgibbs         */
230133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, ACK_INTR | 0xff);
23119410Sguido
232133980Sgibbs	vxsetfilter(sc);
233133980Sgibbs	vxsetlink(sc);
23419410Sguido
235133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, RX_ENABLE);
236133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, TX_ENABLE);
23719410Sguido
238133980Sgibbs	vxmbuffill((caddr_t) sc);
23919410Sguido
240133980Sgibbs	/* Interface is now `running', with no output active. */
241133980Sgibbs	ifp->if_flags |= IFF_RUNNING;
242133980Sgibbs	ifp->if_flags &= ~IFF_OACTIVE;
24319410Sguido
244133980Sgibbs	/* Attempt to start output, if any. */
245133980Sgibbs	vxstart(ifp);
24619410Sguido}
24719410Sguido
24819410Sguidostatic void
249133980Sgibbsvxsetfilter(struct vx_softc *sc)
25019410Sguido{
251133980Sgibbs	register struct ifnet *ifp = &sc->arpcom.ac_if;
25219410Sguido
253133980Sgibbs	GO_WINDOW(1);		/* Window 1 is operating window */
254133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, SET_RX_FILTER |
255133980Sgibbs	    FIL_INDIVIDUAL | FIL_BRDCST | FIL_MULTICAST |
256133980Sgibbs	    ((ifp->if_flags & IFF_PROMISC) ? FIL_PROMISC : 0));
257133980Sgibbs}
258133980Sgibbs
259133980Sgibbsstatic void
260133980Sgibbsvxgetlink(struct vx_softc *sc)
26119410Sguido{
262133980Sgibbs	int n, k;
26319410Sguido
264133980Sgibbs	GO_WINDOW(3);
265133980Sgibbs	sc->vx_connectors = CSR_READ_2(sc, VX_W3_RESET_OPT) & 0x7f;
266133980Sgibbs	for (n = 0, k = 0; k < VX_CONNECTORS; k++) {
267133980Sgibbs		if (sc->vx_connectors & conn_tab[k].bit) {
268133980Sgibbs			if (n > 0)
269133980Sgibbs				printf("/");
270133980Sgibbs			printf("%s", conn_tab[k].name);
271133980Sgibbs			n++;
272133980Sgibbs		}
27319410Sguido	}
274133980Sgibbs	if (sc->vx_connectors == 0) {
275133980Sgibbs		printf("no connectors!");
276133980Sgibbs		return;
277133980Sgibbs	}
278133980Sgibbs	GO_WINDOW(3);
279133980Sgibbs	sc->vx_connector =
280133980Sgibbs	    (CSR_READ_4(sc, VX_W3_INTERNAL_CFG) & INTERNAL_CONNECTOR_MASK)
281133980Sgibbs	    >> INTERNAL_CONNECTOR_BITS;
282133980Sgibbs	if (sc->vx_connector & 0x10) {
283133980Sgibbs		sc->vx_connector &= 0x0f;
284133980Sgibbs		printf("[*%s*]", conn_tab[(int)sc->vx_connector].name);
285133980Sgibbs		printf(": disable 'auto select' with DOS util!");
286133980Sgibbs	} else {
287133980Sgibbs		printf("[*%s*]", conn_tab[(int)sc->vx_connector].name);
288133980Sgibbs	}
28919410Sguido}
29019410Sguido
291133980Sgibbsstatic void
292133980Sgibbsvxsetlink(struct vx_softc *sc)
293133980Sgibbs{
294133980Sgibbs	register struct ifnet *ifp = &sc->arpcom.ac_if;
295133980Sgibbs	int i, j, k;
296133980Sgibbs	char *reason, *warning;
297133980Sgibbs	static int prev_flags;
298133980Sgibbs	static char prev_conn = -1;
29919410Sguido
300133980Sgibbs	if (prev_conn == -1)
301133980Sgibbs		prev_conn = sc->vx_connector;
30220096Sguido
303133980Sgibbs	/*
304133980Sgibbs         * S.B.
305133980Sgibbs         *
306133980Sgibbs         * Now behavior was slightly changed:
307133980Sgibbs         *
308133980Sgibbs         * if any of flags link[0-2] is used and its connector is
309133980Sgibbs         * physically present the following connectors are used:
310133980Sgibbs         *
311133980Sgibbs         *   link0 - AUI * highest precedence
312133980Sgibbs         *   link1 - BNC
313133980Sgibbs         *   link2 - UTP * lowest precedence
314133980Sgibbs         *
315133980Sgibbs         * If none of them is specified then
316133980Sgibbs         * connector specified in the EEPROM is used
317133980Sgibbs         * (if present on card or UTP if not).
318133980Sgibbs         */
319133980Sgibbs	i = sc->vx_connector;	/* default in EEPROM */
320133980Sgibbs	reason = "default";
321133980Sgibbs	warning = 0;
32220096Sguido
323133980Sgibbs	if (ifp->if_flags & IFF_LINK0) {
324133980Sgibbs		if (sc->vx_connectors & conn_tab[CONNECTOR_AUI].bit) {
325133980Sgibbs			i = CONNECTOR_AUI;
326133980Sgibbs			reason = "link0";
327133980Sgibbs		} else {
328133980Sgibbs			warning = "aui not present! (link0)";
329133980Sgibbs		}
330133980Sgibbs	} else if (ifp->if_flags & IFF_LINK1) {
331133980Sgibbs		if (sc->vx_connectors & conn_tab[CONNECTOR_BNC].bit) {
332133980Sgibbs			i = CONNECTOR_BNC;
333133980Sgibbs			reason = "link1";
334133980Sgibbs		} else {
335133980Sgibbs			warning = "bnc not present! (link1)";
336133980Sgibbs		}
337133980Sgibbs	} else if (ifp->if_flags & IFF_LINK2) {
338133980Sgibbs		if (sc->vx_connectors & conn_tab[CONNECTOR_UTP].bit) {
339133980Sgibbs			i = CONNECTOR_UTP;
340133980Sgibbs			reason = "link2";
341133980Sgibbs		} else {
342133980Sgibbs			warning = "utp not present! (link2)";
343133980Sgibbs		}
344133980Sgibbs	} else if ((sc->vx_connectors & conn_tab[(int)sc->vx_connector].bit) == 0) {
345133980Sgibbs		warning = "strange connector type in EEPROM.";
346133980Sgibbs		reason = "forced";
347133980Sgibbs		i = CONNECTOR_UTP;
34820096Sguido	}
349133980Sgibbs	/* Avoid unnecessary message. */
350133980Sgibbs	k = (prev_flags ^ ifp->if_flags) & (IFF_LINK0 | IFF_LINK1 | IFF_LINK2);
351133980Sgibbs	if ((k != 0) || (prev_conn != i)) {
352133980Sgibbs		if (warning != 0) {
353133980Sgibbs			printf("vx%d: warning: %s\n", sc->unit, warning);
354133980Sgibbs		}
355133980Sgibbs		printf("vx%d: selected %s. (%s)\n",
356133980Sgibbs		    sc->unit, conn_tab[i].name, reason);
35720096Sguido	}
358133980Sgibbs	/* Set the selected connector. */
359133980Sgibbs	GO_WINDOW(3);
360133980Sgibbs	j = CSR_READ_4(sc, VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK;
361133980Sgibbs	CSR_WRITE_4(sc, VX_W3_INTERNAL_CFG, j | (i << INTERNAL_CONNECTOR_BITS));
36220096Sguido
363133980Sgibbs	/* First, disable all. */
364133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, STOP_TRANSCEIVER);
36520096Sguido	DELAY(800);
36620096Sguido	GO_WINDOW(4);
367133980Sgibbs	CSR_WRITE_2(sc, VX_W4_MEDIA_TYPE, 0);
36820096Sguido
369133980Sgibbs	/* Second, enable the selected one. */
370133980Sgibbs	switch (i) {
371133980Sgibbs	case CONNECTOR_UTP:
372133980Sgibbs		GO_WINDOW(4);
373133980Sgibbs		CSR_WRITE_2(sc, VX_W4_MEDIA_TYPE, ENABLE_UTP);
374133980Sgibbs		break;
375133980Sgibbs	case CONNECTOR_BNC:
376133980Sgibbs		CSR_WRITE_2(sc, VX_COMMAND, START_TRANSCEIVER);
377133980Sgibbs		DELAY(800);
378133980Sgibbs		break;
379133980Sgibbs	case CONNECTOR_TX:
380133980Sgibbs	case CONNECTOR_FX:
381133980Sgibbs		GO_WINDOW(4);
382133980Sgibbs		CSR_WRITE_2(sc, VX_W4_MEDIA_TYPE, LINKBEAT_ENABLE);
383133980Sgibbs		break;
384133980Sgibbs	default:		/* AUI and MII fall here */
385133980Sgibbs		break;
386133980Sgibbs	}
387133980Sgibbs	GO_WINDOW(1);
388133980Sgibbs
389133980Sgibbs	prev_flags = ifp->if_flags;
390133980Sgibbs	prev_conn = i;
39119410Sguido}
39219410Sguido
39319410Sguidostatic void
394133980Sgibbsvxstart(struct ifnet *ifp)
39519410Sguido{
396133980Sgibbs	register struct vx_softc *sc = ifp->if_softc;
397133980Sgibbs	register struct mbuf *m;
398133980Sgibbs	int sh, len, pad;
39919410Sguido
400133980Sgibbs	/* Don't transmit if interface is busy or not running */
401133980Sgibbs	if ((sc->arpcom.ac_if.if_flags &
402133980Sgibbs	    (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
403133980Sgibbs		return;
40419410Sguido
40519410Sguidostartagain:
406133980Sgibbs	/* Sneak a peek at the next packet */
407133980Sgibbs	m = ifp->if_snd.ifq_head;
408133980Sgibbs	if (m == NULL) {
409133980Sgibbs		return;
410133980Sgibbs	}
411133980Sgibbs	/* We need to use m->m_pkthdr.len, so require the header */
412133980Sgibbs	M_ASSERTPKTHDR(m);
413133980Sgibbs	len = m->m_pkthdr.len;
41419410Sguido
415133980Sgibbs	pad = (4 - len) & 3;
41619410Sguido
417133980Sgibbs	/*
418133980Sgibbs         * The 3c509 automatically pads short packets to minimum ethernet
419133980Sgibbs	 * length, but we drop packets that are too large. Perhaps we should
420133980Sgibbs	 * truncate them instead?
421133980Sgibbs         */
422133980Sgibbs	if (len + pad > ETHER_MAX_LEN) {
423133980Sgibbs		/* packet is obviously too large: toss it */
424133980Sgibbs		++ifp->if_oerrors;
425133980Sgibbs		IF_DEQUEUE(&ifp->if_snd, m);
426133980Sgibbs		m_freem(m);
427133980Sgibbs		goto readcheck;
428133980Sgibbs	}
429133980Sgibbs	VX_BUSY_WAIT;
430133980Sgibbs	if (CSR_READ_2(sc, VX_W1_FREE_TX) < len + pad + 4) {
431133980Sgibbs		CSR_WRITE_2(sc, VX_COMMAND,
432133980Sgibbs		    SET_TX_AVAIL_THRESH | ((len + pad + 4) >> 2));
433133980Sgibbs		/* not enough room in FIFO - make sure */
434133980Sgibbs		if (CSR_READ_2(sc, VX_W1_FREE_TX) < len + pad + 4) {
435133980Sgibbs			ifp->if_flags |= IFF_OACTIVE;
436133980Sgibbs			ifp->if_timer = 1;
437133980Sgibbs			return;
438133980Sgibbs		}
439133980Sgibbs	}
440133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, SET_TX_AVAIL_THRESH | (8188 >> 2));
44190227Sdillon	IF_DEQUEUE(&ifp->if_snd, m);
442133980Sgibbs	if (m == NULL)		/* not really needed */
443133980Sgibbs		return;
44419410Sguido
445133980Sgibbs	VX_BUSY_WAIT;
446133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, SET_TX_START_THRESH |
447133980Sgibbs	    ((len / 4 + sc->tx_start_thresh) >> 2));
44819410Sguido
449133980Sgibbs	BPF_MTAP(&sc->arpcom.ac_if, m);
45019410Sguido
451133980Sgibbs	/*
452133980Sgibbs         * Do the output at splhigh() so that an interrupt from another device
453133980Sgibbs         * won't cause a FIFO underrun.
454133980Sgibbs         */
455133980Sgibbs	sh = splhigh();
45619410Sguido
457133980Sgibbs	CSR_WRITE_4(sc, VX_W1_TX_PIO_WR_1, len | TX_INDICATE);
45819410Sguido
459133980Sgibbs	while (m) {
460133980Sgibbs		if (m->m_len > 3)
461133980Sgibbs			bus_space_write_multi_4(sc->bst, sc->bsh,
462133980Sgibbs			    VX_W1_TX_PIO_WR_1, (u_int32_t *)mtod(m, caddr_t),
463133980Sgibbs			    m->m_len / 4);
464133980Sgibbs		if (m->m_len & 3)
465133980Sgibbs			bus_space_write_multi_1(sc->bst, sc->bsh,
466133980Sgibbs			    VX_W1_TX_PIO_WR_1,
467133980Sgibbs			    mtod(m, caddr_t) + (m->m_len & ~3), m->m_len & 3);
468133980Sgibbs		m = m_free(m);
469133980Sgibbs	}
470133980Sgibbs	while (pad--)
471133980Sgibbs		CSR_WRITE_1(sc, VX_W1_TX_PIO_WR_1, 0);	/* Padding */
47219410Sguido
473133980Sgibbs	splx(sh);
47419410Sguido
475133980Sgibbs	++ifp->if_opackets;
476133980Sgibbs	ifp->if_timer = 1;
47719410Sguido
47819410Sguidoreadcheck:
479133980Sgibbs	if ((CSR_READ_2(sc, VX_W1_RX_STATUS) & ERR_INCOMPLETE) == 0) {
480133980Sgibbs		/* We received a complete packet. */
481133980Sgibbs
482133980Sgibbs		if ((CSR_READ_2(sc, VX_STATUS) & S_INTR_LATCH) == 0) {
483133980Sgibbs			/*
484133980Sgibbs		         * No interrupt, read the packet and continue
485133980Sgibbs		         * Is this supposed to happen?  Is my motherboard
486133980Sgibbs		         * completely busted?
487133980Sgibbs		         */
488133980Sgibbs			vxread(sc);
489133980Sgibbs		} else
490133980Sgibbs			/*
491133980Sgibbs			 * Got an interrupt, return so that it gets
492133980Sgibbs			 * serviced.
493133980Sgibbs			 */
494133980Sgibbs			return;
495133980Sgibbs	} else {
496133980Sgibbs		/* Check if we are stuck and reset [see XXX comment] */
497133980Sgibbs		if (vxstatus(sc)) {
498133980Sgibbs			if (ifp->if_flags & IFF_DEBUG)
499133980Sgibbs				if_printf(ifp, "adapter reset\n");
500133980Sgibbs			vxreset(sc);
501133980Sgibbs		}
50219410Sguido	}
50319410Sguido
504133980Sgibbs	goto startagain;
50519410Sguido}
50619410Sguido
50719410Sguido/*
50819410Sguido * XXX: The 3c509 card can get in a mode where both the fifo status bit
50919410Sguido *      FIFOS_RX_OVERRUN and the status bit ERR_INCOMPLETE are set
51019410Sguido *      We detect this situation and we reset the adapter.
51119410Sguido *      It happens at times when there is a lot of broadcast traffic
51219410Sguido *      on the cable (once in a blue moon).
51319410Sguido */
51419410Sguidostatic int
515133980Sgibbsvxstatus(struct vx_softc *sc)
51619410Sguido{
517133980Sgibbs	int fifost;
51819410Sguido
519133980Sgibbs	/*
520133980Sgibbs         * Check the FIFO status and act accordingly
521133980Sgibbs         */
522133980Sgibbs	GO_WINDOW(4);
523133980Sgibbs	fifost = CSR_READ_2(sc, VX_W4_FIFO_DIAG);
524133980Sgibbs	GO_WINDOW(1);
52519410Sguido
526133980Sgibbs	if (fifost & FIFOS_RX_UNDERRUN) {
527133980Sgibbs		if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
528133980Sgibbs			printf("vx%d: RX underrun\n", sc->unit);
529133980Sgibbs		vxreset(sc);
530133980Sgibbs		return 0;
531133980Sgibbs	}
532133980Sgibbs	if (fifost & FIFOS_RX_STATUS_OVERRUN) {
533133980Sgibbs		if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
534133980Sgibbs			printf("vx%d: RX Status overrun\n", sc->unit);
535133980Sgibbs		return 1;
536133980Sgibbs	}
537133980Sgibbs	if (fifost & FIFOS_RX_OVERRUN) {
538133980Sgibbs		if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
539133980Sgibbs			printf("vx%d: RX overrun\n", sc->unit);
540133980Sgibbs		return 1;
541133980Sgibbs	}
542133980Sgibbs	if (fifost & FIFOS_TX_OVERRUN) {
543133980Sgibbs		if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
544133980Sgibbs			printf("vx%d: TX overrun\n", sc->unit);
545133980Sgibbs		vxreset(sc);
546133980Sgibbs		return 0;
547133980Sgibbs	}
54819410Sguido	return 0;
54919410Sguido}
55019410Sguido
551133980Sgibbsstatic void
552133980Sgibbsvxtxstat(struct vx_softc *sc)
55319410Sguido{
554133980Sgibbs	int i;
55519410Sguido
556133980Sgibbs	/*
557133980Sgibbs        * We need to read+write TX_STATUS until we get a 0 status
558133980Sgibbs        * in order to turn off the interrupt flag.
559133980Sgibbs        */
560133980Sgibbs	while ((i = CSR_READ_1(sc, VX_W1_TX_STATUS)) & TXS_COMPLETE) {
561133980Sgibbs		CSR_WRITE_1(sc, VX_W1_TX_STATUS, 0x0);
56219410Sguido
563133980Sgibbs		if (i & TXS_JABBER) {
564133980Sgibbs			++sc->arpcom.ac_if.if_oerrors;
565133980Sgibbs			if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
566133980Sgibbs				printf("vx%d: jabber (%x)\n", sc->unit, i);
567133980Sgibbs			vxreset(sc);
568133980Sgibbs		} else if (i & TXS_UNDERRUN) {
569133980Sgibbs			++sc->arpcom.ac_if.if_oerrors;
570133980Sgibbs			if (sc->arpcom.ac_if.if_flags & IFF_DEBUG)
571133980Sgibbs				printf("vx%d: fifo underrun (%x) @%d\n",
572133980Sgibbs				    sc->unit, i, sc->tx_start_thresh);
573133980Sgibbs			if (sc->tx_succ_ok < 100)
574133980Sgibbs				sc->tx_start_thresh =
575133980Sgibbs				    min(ETHER_MAX_LEN,sc->tx_start_thresh + 20);
576133980Sgibbs			sc->tx_succ_ok = 0;
577133980Sgibbs			vxreset(sc);
578133980Sgibbs		} else if (i & TXS_MAX_COLLISION) {
579133980Sgibbs			++sc->arpcom.ac_if.if_collisions;
580133980Sgibbs			CSR_WRITE_2(sc, VX_COMMAND, TX_ENABLE);
581133980Sgibbs			sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
582133980Sgibbs		} else
583133980Sgibbs			sc->tx_succ_ok = (sc->tx_succ_ok + 1) & 127;
584133980Sgibbs	}
58519410Sguido}
58619410Sguido
58719410Sguidovoid
588133980Sgibbsvxintr(void *voidsc)
58919410Sguido{
590133980Sgibbs	register short status;
591133980Sgibbs	struct vx_softc *sc = voidsc;
592133980Sgibbs	struct ifnet *ifp = &sc->arpcom.ac_if;
59319410Sguido
594133980Sgibbs	for (;;) {
595133980Sgibbs		CSR_WRITE_2(sc, VX_COMMAND, C_INTR_LATCH);
59619410Sguido
597133980Sgibbs		status = CSR_READ_2(sc, VX_STATUS);
59819410Sguido
599133980Sgibbs		if ((status & (S_TX_COMPLETE | S_TX_AVAIL |
600133980Sgibbs		    S_RX_COMPLETE | S_CARD_FAILURE)) == 0)
601133980Sgibbs			break;
60219410Sguido
603133980Sgibbs		/*
604133980Sgibbs		 * Acknowledge any interrupts.  It's important that we do this
605133980Sgibbs		 * first, since there would otherwise be a race condition.
606133980Sgibbs		 * Due to the i386 interrupt queueing, we may get spurious
607133980Sgibbs		 * interrupts occasionally.
608133980Sgibbs		 */
609133980Sgibbs		CSR_WRITE_2(sc, VX_COMMAND, ACK_INTR | status);
61019410Sguido
611133980Sgibbs		if (status & S_RX_COMPLETE)
612133980Sgibbs			vxread(sc);
613133980Sgibbs		if (status & S_TX_AVAIL) {
614133980Sgibbs			ifp->if_timer = 0;
615133980Sgibbs			sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE;
616133980Sgibbs			vxstart(&sc->arpcom.ac_if);
617133980Sgibbs		}
618133980Sgibbs		if (status & S_CARD_FAILURE) {
619133980Sgibbs			printf("vx%d: adapter failure (%x)\n",
620133980Sgibbs			    sc->unit, status);
621133980Sgibbs			ifp->if_timer = 0;
622133980Sgibbs			vxreset(sc);
623133980Sgibbs			return;
624133980Sgibbs		}
625133980Sgibbs		if (status & S_TX_COMPLETE) {
626133980Sgibbs			ifp->if_timer = 0;
627133980Sgibbs			vxtxstat(sc);
628133980Sgibbs			vxstart(ifp);
629133980Sgibbs		}
63019410Sguido	}
63119410Sguido
632133980Sgibbs	/* no more interrupts */
633133980Sgibbs	return;
63419410Sguido}
63519410Sguido
63619410Sguidostatic void
637133980Sgibbsvxread(struct vx_softc *sc)
63819410Sguido{
639133980Sgibbs	struct ifnet *ifp = &sc->arpcom.ac_if;
640133980Sgibbs	struct mbuf *m;
641133980Sgibbs	struct ether_header *eh;
642133980Sgibbs	u_int len;
64319410Sguido
644133980Sgibbs	len = CSR_READ_2(sc, VX_W1_RX_STATUS);
64519410Sguido
64619410Sguidoagain:
64719410Sguido
648133980Sgibbs	if (ifp->if_flags & IFF_DEBUG) {
649133980Sgibbs		int err = len & ERR_MASK;
650133980Sgibbs		char *s = NULL;
65119410Sguido
652133980Sgibbs		if (len & ERR_INCOMPLETE)
653133980Sgibbs			s = "incomplete packet";
654133980Sgibbs		else if (err == ERR_OVERRUN)
655133980Sgibbs			s = "packet overrun";
656133980Sgibbs		else if (err == ERR_RUNT)
657133980Sgibbs			s = "runt packet";
658133980Sgibbs		else if (err == ERR_ALIGNMENT)
659133980Sgibbs			s = "bad alignment";
660133980Sgibbs		else if (err == ERR_CRC)
661133980Sgibbs			s = "bad crc";
662133980Sgibbs		else if (err == ERR_OVERSIZE)
663133980Sgibbs			s = "oversized packet";
664133980Sgibbs		else if (err == ERR_DRIBBLE)
665133980Sgibbs			s = "dribble bits";
666133980Sgibbs
667133980Sgibbs		if (s)
668133980Sgibbs			printf("vx%d: %s\n", sc->unit, s);
669133980Sgibbs	}
67019410Sguido	if (len & ERR_INCOMPLETE)
671133980Sgibbs		return;
67219410Sguido
673133980Sgibbs	if (len & ERR_RX) {
674133980Sgibbs		++ifp->if_ierrors;
675133980Sgibbs		goto abort;
676133980Sgibbs	}
677133980Sgibbs	len &= RX_BYTES_MASK;	/* Lower 11 bits = RX bytes. */
67819410Sguido
679133980Sgibbs	/* Pull packet off interface. */
680133980Sgibbs	m = vxget(sc, len);
681133980Sgibbs	if (m == 0) {
68269732Swpaul		ifp->if_ierrors++;
68369732Swpaul		goto abort;
68469732Swpaul	}
685133980Sgibbs	++ifp->if_ipackets;
68669732Swpaul
687133980Sgibbs	{
688133980Sgibbs		struct mbuf *m0;
68969732Swpaul
690133980Sgibbs		m0 = m_devget(mtod(m, char *), m->m_pkthdr.len, ETHER_ALIGN, ifp, NULL);
691133980Sgibbs		if (m0 == NULL) {
692133980Sgibbs			ifp->if_ierrors++;
693133980Sgibbs			goto abort;
694133980Sgibbs		}
695133980Sgibbs		m_freem(m);
696133980Sgibbs		m = m0;
697133980Sgibbs	}
69819410Sguido
699133980Sgibbs	/* We assume the header fit entirely in one mbuf. */
700133980Sgibbs	eh = mtod(m, struct ether_header *);
70122062Sphk
702133980Sgibbs	/*
703133980Sgibbs         * XXX: Some cards seem to be in promiscous mode all the time.
704133980Sgibbs         * we need to make sure we only get our own stuff always.
705133980Sgibbs         * bleah!
706133980Sgibbs         */
70760536Sarchie
708133980Sgibbs	if ((eh->ether_dhost[0] & 1) == 0	/* !mcast and !bcast */
709133980Sgibbs	    && bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN)!=0) {
710133980Sgibbs		m_freem(m);
711133980Sgibbs		return;
712133980Sgibbs	}
713133980Sgibbs	(*ifp->if_input) (ifp, m);
71419410Sguido
715133980Sgibbs	/*
716133980Sgibbs        * In periods of high traffic we can actually receive enough
717133980Sgibbs        * packets so that the fifo overrun bit will be set at this point,
718133980Sgibbs        * even though we just read a packet. In this case we
719133980Sgibbs        * are not going to receive any more interrupts. We check for
720133980Sgibbs        * this condition and read again until the fifo is not full.
721133980Sgibbs        * We could simplify this test by not using vxstatus(), but
722133980Sgibbs        * rechecking the RX_STATUS register directly. This test could
723133980Sgibbs        * result in unnecessary looping in cases where there is a new
724133980Sgibbs        * packet but the fifo is not full, but it will not fix the
725133980Sgibbs        * stuck behavior.
726133980Sgibbs        *
727133980Sgibbs        * Even with this improvement, we still get packet overrun errors
728133980Sgibbs        * which are hurting performance. Maybe when I get some more time
729133980Sgibbs        * I'll modify vxread() so that it can handle RX_EARLY interrupts.
730133980Sgibbs        */
731133980Sgibbs	if (vxstatus(sc)) {
732133980Sgibbs		len = CSR_READ_2(sc, VX_W1_RX_STATUS);
733133980Sgibbs		/* Check if we are stuck and reset [see XXX comment] */
734133980Sgibbs		if (len & ERR_INCOMPLETE) {
735133980Sgibbs			if (ifp->if_flags & IFF_DEBUG)
736133980Sgibbs				printf("vx%d: adapter reset\n", sc->unit);
737133980Sgibbs			vxreset(sc);
738133980Sgibbs			return;
739133980Sgibbs		}
740133980Sgibbs		goto again;
74119410Sguido	}
742133980Sgibbs	return;
74319410Sguido
74419410Sguidoabort:
745133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, RX_DISCARD_TOP_PACK);
74619410Sguido}
74719410Sguido
74819410Sguidostatic struct mbuf *
749133980Sgibbsvxget(struct vx_softc *sc, u_int totlen)
75019410Sguido{
751133980Sgibbs	struct ifnet *ifp = &sc->arpcom.ac_if;
752133980Sgibbs	struct mbuf *top, **mp, *m;
753133980Sgibbs	int len;
754133980Sgibbs	int sh;
75519410Sguido
756133980Sgibbs	m = sc->mb[sc->next_mb];
757133980Sgibbs	sc->mb[sc->next_mb] = 0;
758133980Sgibbs	if (m == 0) {
759133980Sgibbs		MGETHDR(m, M_DONTWAIT, MT_DATA);
760133980Sgibbs		if (m == 0)
761133980Sgibbs			return 0;
762133980Sgibbs	} else {
763133980Sgibbs		/* If the queue is no longer full, refill. */
764133980Sgibbs		if (sc->last_mb == sc->next_mb && sc->buffill_pending == 0) {
765133980Sgibbs			sc->ch = timeout(vxmbuffill, sc, 1);
766133980Sgibbs			sc->buffill_pending = 1;
767133980Sgibbs		}
768133980Sgibbs		/* Convert one of our saved mbuf's. */
769133980Sgibbs		sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
770133980Sgibbs		m->m_data = m->m_pktdat;
771133980Sgibbs		m->m_flags = M_PKTHDR;
772133980Sgibbs		bzero(&m->m_pkthdr, sizeof(m->m_pkthdr));
77329671Sgibbs	}
774133980Sgibbs	m->m_pkthdr.rcvif = ifp;
775133980Sgibbs	m->m_pkthdr.len = totlen;
776133980Sgibbs	len = MHLEN;
777133980Sgibbs	top = 0;
778133980Sgibbs	mp = &top;
77919410Sguido
780133980Sgibbs	/*
781133980Sgibbs         * We read the packet at splhigh() so that an interrupt from another
782133980Sgibbs         * device doesn't cause the card's buffer to overflow while we're
783133980Sgibbs         * reading it.  We may still lose packets at other times.
784133980Sgibbs         */
785133980Sgibbs	sh = splhigh();
78619410Sguido
787133980Sgibbs	/*
788133980Sgibbs         * Since we don't set allowLargePackets bit in MacControl register,
789133980Sgibbs         * we can assume that totlen <= 1500bytes.
790133980Sgibbs         * The while loop will be performed iff we have a packet with
791133980Sgibbs         * MLEN < m_len < MINCLSIZE.
792133980Sgibbs         */
793133980Sgibbs	while (totlen > 0) {
794133980Sgibbs		if (top) {
795133980Sgibbs			m = sc->mb[sc->next_mb];
796133980Sgibbs			sc->mb[sc->next_mb] = 0;
797133980Sgibbs			if (m == 0) {
798133980Sgibbs				MGET(m, M_DONTWAIT, MT_DATA);
799133980Sgibbs				if (m == 0) {
800133980Sgibbs					splx(sh);
801133980Sgibbs					m_freem(top);
802133980Sgibbs					return 0;
803133980Sgibbs				}
804133980Sgibbs			} else {
805133980Sgibbs				sc->next_mb = (sc->next_mb + 1) % MAX_MBS;
806133980Sgibbs			}
807133980Sgibbs			len = MLEN;
808133980Sgibbs		}
809133980Sgibbs		if (totlen >= MINCLSIZE) {
810133980Sgibbs			MCLGET(m, M_DONTWAIT);
811133980Sgibbs			if (m->m_flags & M_EXT)
812133980Sgibbs				len = MCLBYTES;
813133980Sgibbs		}
814133980Sgibbs		len = min(totlen, len);
815133980Sgibbs		if (len > 3)
816133980Sgibbs			bus_space_read_multi_4(sc->bst, sc->bsh,
817133980Sgibbs			    VX_W1_RX_PIO_RD_1, mtod(m, u_int32_t *), len / 4);
818133980Sgibbs		if (len & 3) {
819133980Sgibbs			bus_space_read_multi_1(sc->bst, sc->bsh,
820133980Sgibbs			    VX_W1_RX_PIO_RD_1, mtod(m, u_int8_t *) + (len & ~3),
821133980Sgibbs			    len & 3);
822133980Sgibbs		}
823133980Sgibbs		m->m_len = len;
824133980Sgibbs		totlen -= len;
825133980Sgibbs		*mp = m;
826133980Sgibbs		mp = &m->m_next;
82730022Sitojun	}
82819410Sguido
829133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, RX_DISCARD_TOP_PACK);
83019410Sguido
831133980Sgibbs	splx(sh);
83219410Sguido
833133980Sgibbs	return top;
83419410Sguido}
83519410Sguido
83619410Sguido
83719410Sguidostatic int
838133980Sgibbsvxioctl(register struct ifnet *ifp, u_long cmd, caddr_t data)
83919410Sguido{
840133980Sgibbs	struct vx_softc *sc = ifp->if_softc;
841133980Sgibbs	struct ifreq *ifr = (struct ifreq *) data;
842133980Sgibbs	int s, error = 0;
84319410Sguido
844133980Sgibbs	s = splimp();
84519410Sguido
846133980Sgibbs	switch (cmd) {
847133980Sgibbs	case SIOCSIFFLAGS:
848133980Sgibbs		if ((ifp->if_flags & IFF_UP) == 0 &&
849133980Sgibbs		    (ifp->if_flags & IFF_RUNNING) != 0) {
850133980Sgibbs			/*
851133980Sgibbs	                 * If interface is marked up and it is stopped, then
852133980Sgibbs	                 * start it.
853133980Sgibbs	                 */
854133980Sgibbs			vxstop(sc);
855133980Sgibbs			ifp->if_flags &= ~IFF_RUNNING;
856133980Sgibbs		} else if ((ifp->if_flags & IFF_UP) != 0 &&
857133980Sgibbs		    (ifp->if_flags & IFF_RUNNING) == 0) {
858133980Sgibbs			/*
859133980Sgibbs	                 * If interface is marked up and it is stopped, then
860133980Sgibbs	                 * start it.
861133980Sgibbs	                 */
862133980Sgibbs			vxinit(sc);
863133980Sgibbs		} else {
864133980Sgibbs			/*
865133980Sgibbs	                 * deal with flags changes:
866133980Sgibbs	                 * IFF_MULTICAST, IFF_PROMISC,
867133980Sgibbs	                 * IFF_LINK0, IFF_LINK1,
868133980Sgibbs	                 */
869133980Sgibbs			vxsetfilter(sc);
870133980Sgibbs			vxsetlink(sc);
871133980Sgibbs		}
872133980Sgibbs		break;
87319410Sguido
874133980Sgibbs	case SIOCSIFMTU:
875133980Sgibbs		/*
876133980Sgibbs	         * Set the interface MTU.
877133980Sgibbs	         */
878133980Sgibbs		if (ifr->ifr_mtu > ETHERMTU) {
879133980Sgibbs			error = EINVAL;
880133980Sgibbs		} else {
881133980Sgibbs			ifp->if_mtu = ifr->ifr_mtu;
882133980Sgibbs		}
883133980Sgibbs		break;
88419410Sguido
885133980Sgibbs	case SIOCADDMULTI:
886133980Sgibbs	case SIOCDELMULTI:
887133980Sgibbs		/*
888133980Sgibbs		 * Multicast list has changed; set the hardware filter
889133980Sgibbs		 * accordingly.
890133980Sgibbs		 */
891133980Sgibbs		vxreset(sc);
892133980Sgibbs		error = 0;
893133980Sgibbs		break;
89419410Sguido
89519410Sguido
896133980Sgibbs	default:
897133980Sgibbs		error = ether_ioctl(ifp, cmd, data);
898133980Sgibbs		break;
899133980Sgibbs	}
90019410Sguido
901133980Sgibbs	splx(s);
90219410Sguido
903133980Sgibbs	return (error);
90419410Sguido}
90519410Sguido
90619410Sguidostatic void
907133980Sgibbsvxreset(struct vx_softc *sc)
90819410Sguido{
909133980Sgibbs	int s;
91019410Sguido
911133980Sgibbs	s = splimp();
912133980Sgibbs
913133980Sgibbs	vxstop(sc);
914133980Sgibbs	vxinit(sc);
915133980Sgibbs	splx(s);
91619410Sguido}
91719410Sguido
91819410Sguidostatic void
919133980Sgibbsvxwatchdog(struct ifnet *ifp)
92019410Sguido{
921133980Sgibbs	struct vx_softc *sc = ifp->if_softc;
92219410Sguido
923133980Sgibbs	if (ifp->if_flags & IFF_DEBUG)
924133980Sgibbs		if_printf(ifp, "device timeout\n");
925133980Sgibbs	ifp->if_flags &= ~IFF_OACTIVE;
926133980Sgibbs	vxstart(ifp);
927133980Sgibbs	vxintr(sc);
92819410Sguido}
92919410Sguido
93019410Sguidovoid
931133980Sgibbsvxstop(struct vx_softc *sc)
93219410Sguido{
933133980Sgibbs	struct ifnet *ifp = &sc->arpcom.ac_if;
93419410Sguido
935133980Sgibbs	ifp->if_timer = 0;
93619410Sguido
937133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, RX_DISABLE);
938133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, RX_DISCARD_TOP_PACK);
939133980Sgibbs	VX_BUSY_WAIT;
940133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, TX_DISABLE);
941133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, STOP_TRANSCEIVER);
942133980Sgibbs	DELAY(800);
943133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, RX_RESET);
944133980Sgibbs	VX_BUSY_WAIT;
945133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, TX_RESET);
946133980Sgibbs	VX_BUSY_WAIT;
947133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, C_INTR_LATCH);
948133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, SET_RD_0_MASK);
949133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, SET_INTR_MASK);
950133980Sgibbs	CSR_WRITE_2(sc, VX_COMMAND, SET_RX_FILTER);
95119410Sguido
952133980Sgibbs	vxmbufempty(sc);
95319410Sguido}
95419410Sguido
95519410Sguidoint
956133980Sgibbsvxbusyeeprom(struct vx_softc *sc)
95719410Sguido{
958133980Sgibbs	int j, i = 100;
95919410Sguido
960133980Sgibbs	while (i--) {
961133980Sgibbs		j = CSR_READ_2(sc, VX_W0_EEPROM_COMMAND);
962133980Sgibbs		if (j & EEPROM_BUSY)
963133980Sgibbs			DELAY(100);
964133980Sgibbs		else
965133980Sgibbs			break;
966133980Sgibbs	}
967133980Sgibbs	if (!i) {
968133980Sgibbs		printf("vx%d: eeprom failed to come ready\n", sc->unit);
969133980Sgibbs		return (1);
970133980Sgibbs	}
971133980Sgibbs	return (0);
97219410Sguido}
97319410Sguido
97419410Sguidostatic void
975133980Sgibbsvxmbuffill(void *sp)
97619410Sguido{
977133980Sgibbs	struct vx_softc *sc = (struct vx_softc *)sp;
978133980Sgibbs	int s, i;
97919410Sguido
980133980Sgibbs	s = splimp();
981133980Sgibbs	i = sc->last_mb;
982133980Sgibbs	do {
983133980Sgibbs		if (sc->mb[i] == NULL)
984133980Sgibbs			MGET(sc->mb[i], M_DONTWAIT, MT_DATA);
985133980Sgibbs		if (sc->mb[i] == NULL)
986133980Sgibbs			break;
987133980Sgibbs		i = (i + 1) % MAX_MBS;
988133980Sgibbs	} while (i != sc->next_mb);
989133980Sgibbs	sc->last_mb = i;
990133980Sgibbs	/* If the queue was not filled, try again. */
991133980Sgibbs	if (sc->last_mb != sc->next_mb) {
992133980Sgibbs		sc->ch = timeout(vxmbuffill, sc, 1);
993133980Sgibbs		sc->buffill_pending = 1;
994133980Sgibbs	} else {
995133980Sgibbs		sc->buffill_pending = 0;
996133980Sgibbs	}
997133980Sgibbs	splx(s);
99819410Sguido}
99919410Sguido
100019410Sguidostatic void
1001133980Sgibbsvxmbufempty(struct vx_softc *sc)
100219410Sguido{
1003133980Sgibbs	int s, i;
100419410Sguido
1005133980Sgibbs	s = splimp();
1006133980Sgibbs	for (i = 0; i < MAX_MBS; i++) {
1007133980Sgibbs		if (sc->mb[i]) {
1008133980Sgibbs			m_freem(sc->mb[i]);
1009133980Sgibbs			sc->mb[i] = NULL;
1010133980Sgibbs		}
101119410Sguido	}
1012133980Sgibbs	sc->last_mb = sc->next_mb = 0;
1013133980Sgibbs	if (sc->buffill_pending != 0)
1014133980Sgibbs		untimeout(vxmbuffill, sc, sc->ch);
1015133980Sgibbs	splx(s);
101619410Sguido}
1017