if_vr.c revision 50477
141502Swpaul/*
241502Swpaul * Copyright (c) 1997, 1998
341502Swpaul *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
441502Swpaul *
541502Swpaul * Redistribution and use in source and binary forms, with or without
641502Swpaul * modification, are permitted provided that the following conditions
741502Swpaul * are met:
841502Swpaul * 1. Redistributions of source code must retain the above copyright
941502Swpaul *    notice, this list of conditions and the following disclaimer.
1041502Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1141502Swpaul *    notice, this list of conditions and the following disclaimer in the
1241502Swpaul *    documentation and/or other materials provided with the distribution.
1341502Swpaul * 3. All advertising materials mentioning features or use of this software
1441502Swpaul *    must display the following acknowledgement:
1541502Swpaul *	This product includes software developed by Bill Paul.
1641502Swpaul * 4. Neither the name of the author nor the names of any co-contributors
1741502Swpaul *    may be used to endorse or promote products derived from this software
1841502Swpaul *    without specific prior written permission.
1941502Swpaul *
2041502Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2141502Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2241502Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2341502Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2441502Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2541502Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2641502Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2741502Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2841502Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2941502Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3041502Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
3141502Swpaul *
3250477Speter * $FreeBSD: head/sys/dev/vr/if_vr.c 50477 1999-08-28 01:08:13Z peter $
3341502Swpaul */
3441502Swpaul
3541502Swpaul/*
3641502Swpaul * VIA Rhine fast ethernet PCI NIC driver
3741502Swpaul *
3841502Swpaul * Supports various network adapters based on the VIA Rhine
3941502Swpaul * and Rhine II PCI controllers, including the D-Link DFE530TX.
4041502Swpaul * Datasheets are available at http://www.via.com.tw.
4141502Swpaul *
4241502Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu>
4341502Swpaul * Electrical Engineering Department
4441502Swpaul * Columbia University, New York City
4541502Swpaul */
4641502Swpaul
4741502Swpaul/*
4841502Swpaul * The VIA Rhine controllers are similar in some respects to the
4941502Swpaul * the DEC tulip chips, except less complicated. The controller
5041502Swpaul * uses an MII bus and an external physical layer interface. The
5141502Swpaul * receiver has a one entry perfect filter and a 64-bit hash table
5241502Swpaul * multicast filter. Transmit and receive descriptors are similar
5341502Swpaul * to the tulip.
5441502Swpaul *
5541502Swpaul * The Rhine has a serious flaw in its transmit DMA mechanism:
5641502Swpaul * transmit buffers must be longword aligned. Unfortunately,
5741502Swpaul * FreeBSD doesn't guarantee that mbufs will be filled in starting
5841502Swpaul * at longword boundaries, so we have to do a buffer copy before
5941502Swpaul * transmission.
6041502Swpaul */
6141502Swpaul
6248645Sdes#include "bpf.h"
6341502Swpaul
6441502Swpaul#include <sys/param.h>
6541502Swpaul#include <sys/systm.h>
6641502Swpaul#include <sys/sockio.h>
6741502Swpaul#include <sys/mbuf.h>
6841502Swpaul#include <sys/malloc.h>
6941502Swpaul#include <sys/kernel.h>
7041502Swpaul#include <sys/socket.h>
7141502Swpaul
7241502Swpaul#include <net/if.h>
7341502Swpaul#include <net/if_arp.h>
7441502Swpaul#include <net/ethernet.h>
7541502Swpaul#include <net/if_dl.h>
7641502Swpaul#include <net/if_media.h>
7741502Swpaul
7848645Sdes#if NBPF > 0
7941502Swpaul#include <net/bpf.h>
8041502Swpaul#endif
8141502Swpaul
8241502Swpaul#include <vm/vm.h>              /* for vtophys */
8341502Swpaul#include <vm/pmap.h>            /* for vtophys */
8441502Swpaul#include <machine/clock.h>      /* for DELAY */
8541502Swpaul#include <machine/bus_pio.h>
8641502Swpaul#include <machine/bus_memio.h>
8741502Swpaul#include <machine/bus.h>
8849610Swpaul#include <machine/resource.h>
8949610Swpaul#include <sys/bus.h>
9049610Swpaul#include <sys/rman.h>
9141502Swpaul
9241502Swpaul#include <pci/pcireg.h>
9341502Swpaul#include <pci/pcivar.h>
9441502Swpaul
9541502Swpaul#define VR_USEIOSPACE
9641502Swpaul
9741502Swpaul/* #define VR_BACKGROUND_AUTONEG */
9841502Swpaul
9941502Swpaul#include <pci/if_vrreg.h>
10041502Swpaul
10141502Swpaul#ifndef lint
10241591Sarchiestatic const char rcsid[] =
10350477Speter  "$FreeBSD: head/sys/dev/vr/if_vr.c 50477 1999-08-28 01:08:13Z peter $";
10441502Swpaul#endif
10541502Swpaul
10641502Swpaul/*
10741502Swpaul * Various supported device vendors/types and their names.
10841502Swpaul */
10941502Swpaulstatic struct vr_type vr_devs[] = {
11041502Swpaul	{ VIA_VENDORID, VIA_DEVICEID_RHINE,
11141502Swpaul		"VIA VT3043 Rhine I 10/100BaseTX" },
11241502Swpaul	{ VIA_VENDORID, VIA_DEVICEID_RHINE_II,
11341502Swpaul		"VIA VT86C100A Rhine II 10/100BaseTX" },
11444238Swpaul	{ DELTA_VENDORID, DELTA_DEVICEID_RHINE_II,
11544238Swpaul		"Delta Electronics Rhine II 10/100BaseTX" },
11644238Swpaul	{ ADDTRON_VENDORID, ADDTRON_DEVICEID_RHINE_II,
11744238Swpaul		"Addtron Technology Rhine II 10/100BaseTX" },
11841502Swpaul	{ 0, 0, NULL }
11941502Swpaul};
12041502Swpaul
12141502Swpaul/*
12241502Swpaul * Various supported PHY vendors/types and their names. Note that
12341502Swpaul * this driver will work with pretty much any MII-compliant PHY,
12441502Swpaul * so failure to positively identify the chip is not a fatal error.
12541502Swpaul */
12641502Swpaul
12741502Swpaulstatic struct vr_type vr_phys[] = {
12841502Swpaul	{ TI_PHY_VENDORID, TI_PHY_10BT, "<TI ThunderLAN 10BT (internal)>" },
12941502Swpaul	{ TI_PHY_VENDORID, TI_PHY_100VGPMI, "<TI TNETE211 100VG Any-LAN>" },
13041502Swpaul	{ NS_PHY_VENDORID, NS_PHY_83840A, "<National Semiconductor DP83840A>"},
13141502Swpaul	{ LEVEL1_PHY_VENDORID, LEVEL1_PHY_LXT970, "<Level 1 LXT970>" },
13241502Swpaul	{ INTEL_PHY_VENDORID, INTEL_PHY_82555, "<Intel 82555>" },
13341502Swpaul	{ SEEQ_PHY_VENDORID, SEEQ_PHY_80220, "<SEEQ 80220>" },
13441502Swpaul	{ 0, 0, "<MII-compliant physical interface>" }
13541502Swpaul};
13641502Swpaul
13749610Swpaulstatic int vr_probe		__P((device_t));
13849610Swpaulstatic int vr_attach		__P((device_t));
13949610Swpaulstatic int vr_detach		__P((device_t));
14041502Swpaul
14141502Swpaulstatic int vr_newbuf		__P((struct vr_softc *,
14249610Swpaul					struct vr_chain_onefrag *,
14349610Swpaul					struct mbuf *));
14441502Swpaulstatic int vr_encap		__P((struct vr_softc *, struct vr_chain *,
14541502Swpaul						struct mbuf * ));
14641502Swpaul
14741502Swpaulstatic void vr_rxeof		__P((struct vr_softc *));
14841502Swpaulstatic void vr_rxeoc		__P((struct vr_softc *));
14941502Swpaulstatic void vr_txeof		__P((struct vr_softc *));
15041502Swpaulstatic void vr_txeoc		__P((struct vr_softc *));
15141502Swpaulstatic void vr_intr		__P((void *));
15241502Swpaulstatic void vr_start		__P((struct ifnet *));
15341502Swpaulstatic int vr_ioctl		__P((struct ifnet *, u_long, caddr_t));
15441502Swpaulstatic void vr_init		__P((void *));
15541502Swpaulstatic void vr_stop		__P((struct vr_softc *));
15641502Swpaulstatic void vr_watchdog		__P((struct ifnet *));
15749610Swpaulstatic void vr_shutdown		__P((device_t));
15841502Swpaulstatic int vr_ifmedia_upd	__P((struct ifnet *));
15941502Swpaulstatic void vr_ifmedia_sts	__P((struct ifnet *, struct ifmediareq *));
16041502Swpaul
16141502Swpaulstatic void vr_mii_sync		__P((struct vr_softc *));
16241502Swpaulstatic void vr_mii_send		__P((struct vr_softc *, u_int32_t, int));
16341502Swpaulstatic int vr_mii_readreg	__P((struct vr_softc *, struct vr_mii_frame *));
16441502Swpaulstatic int vr_mii_writereg	__P((struct vr_softc *, struct vr_mii_frame *));
16541502Swpaulstatic u_int16_t vr_phy_readreg	__P((struct vr_softc *, int));
16641502Swpaulstatic void vr_phy_writereg	__P((struct vr_softc *, u_int16_t, u_int16_t));
16741502Swpaul
16841502Swpaulstatic void vr_autoneg_xmit	__P((struct vr_softc *));
16941502Swpaulstatic void vr_autoneg_mii	__P((struct vr_softc *, int, int));
17041502Swpaulstatic void vr_setmode_mii	__P((struct vr_softc *, int));
17141502Swpaulstatic void vr_getmode_mii	__P((struct vr_softc *));
17241502Swpaulstatic void vr_setcfg		__P((struct vr_softc *, u_int16_t));
17341502Swpaulstatic u_int8_t vr_calchash	__P((u_int8_t *));
17441502Swpaulstatic void vr_setmulti		__P((struct vr_softc *));
17541502Swpaulstatic void vr_reset		__P((struct vr_softc *));
17641502Swpaulstatic int vr_list_rx_init	__P((struct vr_softc *));
17741502Swpaulstatic int vr_list_tx_init	__P((struct vr_softc *));
17841502Swpaul
17949610Swpaul#ifdef VR_USEIOSPACE
18049610Swpaul#define VR_RES			SYS_RES_IOPORT
18149610Swpaul#define VR_RID			VR_PCI_LOIO
18249610Swpaul#else
18349610Swpaul#define VR_RES			SYS_RES_MEMORY
18449610Swpaul#define VR_RID			VR_PCI_LOMEM
18549610Swpaul#endif
18649610Swpaul
18749610Swpaulstatic device_method_t vr_methods[] = {
18849610Swpaul	/* Device interface */
18949610Swpaul	DEVMETHOD(device_probe,		vr_probe),
19049610Swpaul	DEVMETHOD(device_attach,	vr_attach),
19149610Swpaul	DEVMETHOD(device_detach, 	vr_detach),
19249610Swpaul	DEVMETHOD(device_shutdown,	vr_shutdown),
19349610Swpaul	{ 0, 0 }
19449610Swpaul};
19549610Swpaul
19649610Swpaulstatic driver_t vr_driver = {
19749610Swpaul	"vr",
19849610Swpaul	vr_methods,
19949610Swpaul	sizeof(struct vr_softc)
20049610Swpaul};
20149610Swpaul
20249610Swpaulstatic devclass_t vr_devclass;
20349610Swpaul
20449610SwpaulDRIVER_MODULE(vr, pci, vr_driver, vr_devclass, 0, 0);
20549610Swpaul
20641502Swpaul#define VR_SETBIT(sc, reg, x)				\
20741502Swpaul	CSR_WRITE_1(sc, reg,				\
20841502Swpaul		CSR_READ_1(sc, reg) | x)
20941502Swpaul
21041502Swpaul#define VR_CLRBIT(sc, reg, x)				\
21141502Swpaul	CSR_WRITE_1(sc, reg,				\
21241502Swpaul		CSR_READ_1(sc, reg) & ~x)
21341502Swpaul
21441502Swpaul#define VR_SETBIT16(sc, reg, x)				\
21541502Swpaul	CSR_WRITE_2(sc, reg,				\
21641502Swpaul		CSR_READ_2(sc, reg) | x)
21741502Swpaul
21841502Swpaul#define VR_CLRBIT16(sc, reg, x)				\
21941502Swpaul	CSR_WRITE_2(sc, reg,				\
22041502Swpaul		CSR_READ_2(sc, reg) & ~x)
22141502Swpaul
22241502Swpaul#define VR_SETBIT32(sc, reg, x)				\
22341502Swpaul	CSR_WRITE_4(sc, reg,				\
22441502Swpaul		CSR_READ_4(sc, reg) | x)
22541502Swpaul
22641502Swpaul#define VR_CLRBIT32(sc, reg, x)				\
22741502Swpaul	CSR_WRITE_4(sc, reg,				\
22841502Swpaul		CSR_READ_4(sc, reg) & ~x)
22941502Swpaul
23041502Swpaul#define SIO_SET(x)					\
23141502Swpaul	CSR_WRITE_1(sc, VR_MIICMD,			\
23241502Swpaul		CSR_READ_1(sc, VR_MIICMD) | x)
23341502Swpaul
23441502Swpaul#define SIO_CLR(x)					\
23541502Swpaul	CSR_WRITE_1(sc, VR_MIICMD,			\
23641502Swpaul		CSR_READ_1(sc, VR_MIICMD) & ~x)
23741502Swpaul
23841502Swpaul/*
23941502Swpaul * Sync the PHYs by setting data bit and strobing the clock 32 times.
24041502Swpaul */
24141502Swpaulstatic void vr_mii_sync(sc)
24241502Swpaul	struct vr_softc		*sc;
24341502Swpaul{
24441502Swpaul	register int		i;
24541502Swpaul
24641502Swpaul	SIO_SET(VR_MIICMD_DIR|VR_MIICMD_DATAIN);
24741502Swpaul
24841502Swpaul	for (i = 0; i < 32; i++) {
24941502Swpaul		SIO_SET(VR_MIICMD_CLK);
25041502Swpaul		DELAY(1);
25141502Swpaul		SIO_CLR(VR_MIICMD_CLK);
25241502Swpaul		DELAY(1);
25341502Swpaul	}
25441502Swpaul
25541502Swpaul	return;
25641502Swpaul}
25741502Swpaul
25841502Swpaul/*
25941502Swpaul * Clock a series of bits through the MII.
26041502Swpaul */
26141502Swpaulstatic void vr_mii_send(sc, bits, cnt)
26241502Swpaul	struct vr_softc		*sc;
26341502Swpaul	u_int32_t		bits;
26441502Swpaul	int			cnt;
26541502Swpaul{
26641502Swpaul	int			i;
26741502Swpaul
26841502Swpaul	SIO_CLR(VR_MIICMD_CLK);
26941502Swpaul
27041502Swpaul	for (i = (0x1 << (cnt - 1)); i; i >>= 1) {
27141502Swpaul                if (bits & i) {
27241502Swpaul			SIO_SET(VR_MIICMD_DATAIN);
27341502Swpaul                } else {
27441502Swpaul			SIO_CLR(VR_MIICMD_DATAIN);
27541502Swpaul                }
27641502Swpaul		DELAY(1);
27741502Swpaul		SIO_CLR(VR_MIICMD_CLK);
27841502Swpaul		DELAY(1);
27941502Swpaul		SIO_SET(VR_MIICMD_CLK);
28041502Swpaul	}
28141502Swpaul}
28241502Swpaul
28341502Swpaul/*
28441502Swpaul * Read an PHY register through the MII.
28541502Swpaul */
28641502Swpaulstatic int vr_mii_readreg(sc, frame)
28741502Swpaul	struct vr_softc		*sc;
28841502Swpaul	struct vr_mii_frame	*frame;
28941502Swpaul
29041502Swpaul{
29141502Swpaul	int			i, ack, s;
29241502Swpaul
29341502Swpaul	s = splimp();
29441502Swpaul
29541502Swpaul	/*
29641502Swpaul	 * Set up frame for RX.
29741502Swpaul	 */
29841502Swpaul	frame->mii_stdelim = VR_MII_STARTDELIM;
29941502Swpaul	frame->mii_opcode = VR_MII_READOP;
30041502Swpaul	frame->mii_turnaround = 0;
30141502Swpaul	frame->mii_data = 0;
30241502Swpaul
30341502Swpaul	CSR_WRITE_1(sc, VR_MIICMD, 0);
30441502Swpaul	VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM);
30541502Swpaul
30641502Swpaul	/*
30741502Swpaul 	 * Turn on data xmit.
30841502Swpaul	 */
30941502Swpaul	SIO_SET(VR_MIICMD_DIR);
31041502Swpaul
31141502Swpaul	vr_mii_sync(sc);
31241502Swpaul
31341502Swpaul	/*
31441502Swpaul	 * Send command/address info.
31541502Swpaul	 */
31641502Swpaul	vr_mii_send(sc, frame->mii_stdelim, 2);
31741502Swpaul	vr_mii_send(sc, frame->mii_opcode, 2);
31841502Swpaul	vr_mii_send(sc, frame->mii_phyaddr, 5);
31941502Swpaul	vr_mii_send(sc, frame->mii_regaddr, 5);
32041502Swpaul
32141502Swpaul	/* Idle bit */
32241502Swpaul	SIO_CLR((VR_MIICMD_CLK|VR_MIICMD_DATAIN));
32341502Swpaul	DELAY(1);
32441502Swpaul	SIO_SET(VR_MIICMD_CLK);
32541502Swpaul	DELAY(1);
32641502Swpaul
32741502Swpaul	/* Turn off xmit. */
32841502Swpaul	SIO_CLR(VR_MIICMD_DIR);
32941502Swpaul
33041502Swpaul	/* Check for ack */
33141502Swpaul	SIO_CLR(VR_MIICMD_CLK);
33241502Swpaul	DELAY(1);
33341502Swpaul	SIO_SET(VR_MIICMD_CLK);
33441502Swpaul	DELAY(1);
33541502Swpaul	ack = CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT;
33641502Swpaul
33741502Swpaul	/*
33841502Swpaul	 * Now try reading data bits. If the ack failed, we still
33941502Swpaul	 * need to clock through 16 cycles to keep the PHY(s) in sync.
34041502Swpaul	 */
34141502Swpaul	if (ack) {
34241502Swpaul		for(i = 0; i < 16; i++) {
34341502Swpaul			SIO_CLR(VR_MIICMD_CLK);
34441502Swpaul			DELAY(1);
34541502Swpaul			SIO_SET(VR_MIICMD_CLK);
34641502Swpaul			DELAY(1);
34741502Swpaul		}
34841502Swpaul		goto fail;
34941502Swpaul	}
35041502Swpaul
35141502Swpaul	for (i = 0x8000; i; i >>= 1) {
35241502Swpaul		SIO_CLR(VR_MIICMD_CLK);
35341502Swpaul		DELAY(1);
35441502Swpaul		if (!ack) {
35541502Swpaul			if (CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT)
35641502Swpaul				frame->mii_data |= i;
35741502Swpaul			DELAY(1);
35841502Swpaul		}
35941502Swpaul		SIO_SET(VR_MIICMD_CLK);
36041502Swpaul		DELAY(1);
36141502Swpaul	}
36241502Swpaul
36341502Swpaulfail:
36441502Swpaul
36541502Swpaul	SIO_CLR(VR_MIICMD_CLK);
36641502Swpaul	DELAY(1);
36741502Swpaul	SIO_SET(VR_MIICMD_CLK);
36841502Swpaul	DELAY(1);
36941502Swpaul
37041502Swpaul	splx(s);
37141502Swpaul
37241502Swpaul	if (ack)
37341502Swpaul		return(1);
37441502Swpaul	return(0);
37541502Swpaul}
37641502Swpaul
37741502Swpaul/*
37841502Swpaul * Write to a PHY register through the MII.
37941502Swpaul */
38041502Swpaulstatic int vr_mii_writereg(sc, frame)
38141502Swpaul	struct vr_softc		*sc;
38241502Swpaul	struct vr_mii_frame	*frame;
38341502Swpaul
38441502Swpaul{
38541502Swpaul	int			s;
38641502Swpaul
38741502Swpaul	s = splimp();
38841502Swpaul
38941502Swpaul	CSR_WRITE_1(sc, VR_MIICMD, 0);
39041502Swpaul	VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM);
39141502Swpaul
39241502Swpaul	/*
39341502Swpaul	 * Set up frame for TX.
39441502Swpaul	 */
39541502Swpaul
39641502Swpaul	frame->mii_stdelim = VR_MII_STARTDELIM;
39741502Swpaul	frame->mii_opcode = VR_MII_WRITEOP;
39841502Swpaul	frame->mii_turnaround = VR_MII_TURNAROUND;
39941502Swpaul
40041502Swpaul	/*
40141502Swpaul 	 * Turn on data output.
40241502Swpaul	 */
40341502Swpaul	SIO_SET(VR_MIICMD_DIR);
40441502Swpaul
40541502Swpaul	vr_mii_sync(sc);
40641502Swpaul
40741502Swpaul	vr_mii_send(sc, frame->mii_stdelim, 2);
40841502Swpaul	vr_mii_send(sc, frame->mii_opcode, 2);
40941502Swpaul	vr_mii_send(sc, frame->mii_phyaddr, 5);
41041502Swpaul	vr_mii_send(sc, frame->mii_regaddr, 5);
41141502Swpaul	vr_mii_send(sc, frame->mii_turnaround, 2);
41241502Swpaul	vr_mii_send(sc, frame->mii_data, 16);
41341502Swpaul
41441502Swpaul	/* Idle bit. */
41541502Swpaul	SIO_SET(VR_MIICMD_CLK);
41641502Swpaul	DELAY(1);
41741502Swpaul	SIO_CLR(VR_MIICMD_CLK);
41841502Swpaul	DELAY(1);
41941502Swpaul
42041502Swpaul	/*
42141502Swpaul	 * Turn off xmit.
42241502Swpaul	 */
42341502Swpaul	SIO_CLR(VR_MIICMD_DIR);
42441502Swpaul
42541502Swpaul	splx(s);
42641502Swpaul
42741502Swpaul	return(0);
42841502Swpaul}
42941502Swpaul
43041502Swpaulstatic u_int16_t vr_phy_readreg(sc, reg)
43141502Swpaul	struct vr_softc		*sc;
43241502Swpaul	int			reg;
43341502Swpaul{
43441502Swpaul	struct vr_mii_frame	frame;
43541502Swpaul
43641502Swpaul	bzero((char *)&frame, sizeof(frame));
43741502Swpaul
43841502Swpaul	frame.mii_phyaddr = sc->vr_phy_addr;
43941502Swpaul	frame.mii_regaddr = reg;
44041502Swpaul	vr_mii_readreg(sc, &frame);
44141502Swpaul
44241502Swpaul	return(frame.mii_data);
44341502Swpaul}
44441502Swpaul
44541502Swpaulstatic void vr_phy_writereg(sc, reg, data)
44641502Swpaul	struct vr_softc		*sc;
44741502Swpaul	u_int16_t		reg;
44841502Swpaul	u_int16_t		data;
44941502Swpaul{
45041502Swpaul	struct vr_mii_frame	frame;
45141502Swpaul
45241502Swpaul	bzero((char *)&frame, sizeof(frame));
45341502Swpaul
45441502Swpaul	frame.mii_phyaddr = sc->vr_phy_addr;
45541502Swpaul	frame.mii_regaddr = reg;
45641502Swpaul	frame.mii_data = data;
45741502Swpaul
45841502Swpaul	vr_mii_writereg(sc, &frame);
45941502Swpaul
46041502Swpaul	return;
46141502Swpaul}
46241502Swpaul
46341502Swpaul/*
46441502Swpaul * Calculate CRC of a multicast group address, return the lower 6 bits.
46541502Swpaul */
46641502Swpaulstatic u_int8_t vr_calchash(addr)
46741502Swpaul	u_int8_t		*addr;
46841502Swpaul{
46941502Swpaul	u_int32_t		crc, carry;
47041502Swpaul	int			i, j;
47141502Swpaul	u_int8_t		c;
47241502Swpaul
47341502Swpaul	/* Compute CRC for the address value. */
47441502Swpaul	crc = 0xFFFFFFFF; /* initial value */
47541502Swpaul
47641502Swpaul	for (i = 0; i < 6; i++) {
47741502Swpaul		c = *(addr + i);
47841502Swpaul		for (j = 0; j < 8; j++) {
47941502Swpaul			carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01);
48041502Swpaul			crc <<= 1;
48141502Swpaul			c >>= 1;
48241502Swpaul			if (carry)
48341502Swpaul				crc = (crc ^ 0x04c11db6) | carry;
48441502Swpaul		}
48541502Swpaul	}
48641502Swpaul
48741502Swpaul	/* return the filter bit position */
48841502Swpaul	return((crc >> 26) & 0x0000003F);
48941502Swpaul}
49041502Swpaul
49141502Swpaul/*
49241502Swpaul * Program the 64-bit multicast hash filter.
49341502Swpaul */
49441502Swpaulstatic void vr_setmulti(sc)
49541502Swpaul	struct vr_softc		*sc;
49641502Swpaul{
49741502Swpaul	struct ifnet		*ifp;
49841502Swpaul	int			h = 0;
49941502Swpaul	u_int32_t		hashes[2] = { 0, 0 };
50041502Swpaul	struct ifmultiaddr	*ifma;
50141502Swpaul	u_int8_t		rxfilt;
50241502Swpaul	int			mcnt = 0;
50341502Swpaul
50441502Swpaul	ifp = &sc->arpcom.ac_if;
50541502Swpaul
50641502Swpaul	rxfilt = CSR_READ_1(sc, VR_RXCFG);
50741502Swpaul
50841502Swpaul	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
50941502Swpaul		rxfilt |= VR_RXCFG_RX_MULTI;
51041502Swpaul		CSR_WRITE_1(sc, VR_RXCFG, rxfilt);
51141502Swpaul		CSR_WRITE_4(sc, VR_MAR0, 0xFFFFFFFF);
51241502Swpaul		CSR_WRITE_4(sc, VR_MAR1, 0xFFFFFFFF);
51341502Swpaul		return;
51441502Swpaul	}
51541502Swpaul
51641502Swpaul	/* first, zot all the existing hash bits */
51741502Swpaul	CSR_WRITE_4(sc, VR_MAR0, 0);
51841502Swpaul	CSR_WRITE_4(sc, VR_MAR1, 0);
51941502Swpaul
52041502Swpaul	/* now program new ones */
52141502Swpaul	for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;
52241502Swpaul				ifma = ifma->ifma_link.le_next) {
52341502Swpaul		if (ifma->ifma_addr->sa_family != AF_LINK)
52441502Swpaul			continue;
52541502Swpaul		h = vr_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
52641502Swpaul		if (h < 32)
52741502Swpaul			hashes[0] |= (1 << h);
52841502Swpaul		else
52941502Swpaul			hashes[1] |= (1 << (h - 32));
53041502Swpaul		mcnt++;
53141502Swpaul	}
53241502Swpaul
53341502Swpaul	if (mcnt)
53441502Swpaul		rxfilt |= VR_RXCFG_RX_MULTI;
53541502Swpaul	else
53641502Swpaul		rxfilt &= ~VR_RXCFG_RX_MULTI;
53741502Swpaul
53841502Swpaul	CSR_WRITE_4(sc, VR_MAR0, hashes[0]);
53941502Swpaul	CSR_WRITE_4(sc, VR_MAR1, hashes[1]);
54041502Swpaul	CSR_WRITE_1(sc, VR_RXCFG, rxfilt);
54141502Swpaul
54241502Swpaul	return;
54341502Swpaul}
54441502Swpaul
54541502Swpaul/*
54641502Swpaul * Initiate an autonegotiation session.
54741502Swpaul */
54841502Swpaulstatic void vr_autoneg_xmit(sc)
54941502Swpaul	struct vr_softc		*sc;
55041502Swpaul{
55141502Swpaul	u_int16_t		phy_sts;
55241502Swpaul
55341502Swpaul	vr_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET);
55441502Swpaul	DELAY(500);
55541502Swpaul	while(vr_phy_readreg(sc, PHY_BMCR)
55641502Swpaul			& PHY_BMCR_RESET);
55741502Swpaul
55841502Swpaul	phy_sts = vr_phy_readreg(sc, PHY_BMCR);
55941502Swpaul	phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR;
56041502Swpaul	vr_phy_writereg(sc, PHY_BMCR, phy_sts);
56141502Swpaul
56241502Swpaul	return;
56341502Swpaul}
56441502Swpaul
56541502Swpaul/*
56641502Swpaul * Invoke autonegotiation on a PHY.
56741502Swpaul */
56841502Swpaulstatic void vr_autoneg_mii(sc, flag, verbose)
56941502Swpaul	struct vr_softc		*sc;
57041502Swpaul	int			flag;
57141502Swpaul	int			verbose;
57241502Swpaul{
57341502Swpaul	u_int16_t		phy_sts = 0, media, advert, ability;
57441502Swpaul	struct ifnet		*ifp;
57541502Swpaul	struct ifmedia		*ifm;
57641502Swpaul
57741502Swpaul	ifm = &sc->ifmedia;
57841502Swpaul	ifp = &sc->arpcom.ac_if;
57941502Swpaul
58041502Swpaul	ifm->ifm_media = IFM_ETHER | IFM_AUTO;
58141502Swpaul
58241502Swpaul	/*
58341502Swpaul	 * The 100baseT4 PHY on the 3c905-T4 has the 'autoneg supported'
58441502Swpaul	 * bit cleared in the status register, but has the 'autoneg enabled'
58541502Swpaul	 * bit set in the control register. This is a contradiction, and
58641502Swpaul	 * I'm not sure how to handle it. If you want to force an attempt
58741502Swpaul	 * to autoneg for 100baseT4 PHYs, #define FORCE_AUTONEG_TFOUR
58841502Swpaul	 * and see what happens.
58941502Swpaul	 */
59041502Swpaul#ifndef FORCE_AUTONEG_TFOUR
59141502Swpaul	/*
59241502Swpaul	 * First, see if autoneg is supported. If not, there's
59341502Swpaul	 * no point in continuing.
59441502Swpaul	 */
59541502Swpaul	phy_sts = vr_phy_readreg(sc, PHY_BMSR);
59641502Swpaul	if (!(phy_sts & PHY_BMSR_CANAUTONEG)) {
59741502Swpaul		if (verbose)
59841502Swpaul			printf("vr%d: autonegotiation not supported\n",
59941502Swpaul							sc->vr_unit);
60041502Swpaul		ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX;
60141502Swpaul		return;
60241502Swpaul	}
60341502Swpaul#endif
60441502Swpaul
60541502Swpaul	switch (flag) {
60641502Swpaul	case VR_FLAG_FORCEDELAY:
60741502Swpaul		/*
60841502Swpaul	 	 * XXX Never use this option anywhere but in the probe
60941502Swpaul	 	 * routine: making the kernel stop dead in its tracks
61041502Swpaul 		 * for three whole seconds after we've gone multi-user
61141502Swpaul		 * is really bad manners.
61241502Swpaul	 	 */
61341502Swpaul		vr_autoneg_xmit(sc);
61441502Swpaul		DELAY(5000000);
61541502Swpaul		break;
61641502Swpaul	case VR_FLAG_SCHEDDELAY:
61741502Swpaul		/*
61841502Swpaul		 * Wait for the transmitter to go idle before starting
61941502Swpaul		 * an autoneg session, otherwise vr_start() may clobber
62041502Swpaul	 	 * our timeout, and we don't want to allow transmission
62141502Swpaul		 * during an autoneg session since that can screw it up.
62241502Swpaul	 	 */
62341502Swpaul		if (sc->vr_cdata.vr_tx_head != NULL) {
62441502Swpaul			sc->vr_want_auto = 1;
62541502Swpaul			return;
62641502Swpaul		}
62741502Swpaul		vr_autoneg_xmit(sc);
62841502Swpaul		ifp->if_timer = 5;
62941502Swpaul		sc->vr_autoneg = 1;
63041502Swpaul		sc->vr_want_auto = 0;
63141502Swpaul		return;
63241502Swpaul		break;
63341502Swpaul	case VR_FLAG_DELAYTIMEO:
63441502Swpaul		ifp->if_timer = 0;
63541502Swpaul		sc->vr_autoneg = 0;
63641502Swpaul		break;
63741502Swpaul	default:
63841502Swpaul		printf("vr%d: invalid autoneg flag: %d\n", sc->vr_unit, flag);
63941502Swpaul		return;
64041502Swpaul	}
64141502Swpaul
64241502Swpaul	if (vr_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_AUTONEGCOMP) {
64341502Swpaul		if (verbose)
64441502Swpaul			printf("vr%d: autoneg complete, ", sc->vr_unit);
64541502Swpaul		phy_sts = vr_phy_readreg(sc, PHY_BMSR);
64641502Swpaul	} else {
64741502Swpaul		if (verbose)
64841502Swpaul			printf("vr%d: autoneg not complete, ", sc->vr_unit);
64941502Swpaul	}
65041502Swpaul
65141502Swpaul	media = vr_phy_readreg(sc, PHY_BMCR);
65241502Swpaul
65341502Swpaul	/* Link is good. Report modes and set duplex mode. */
65441502Swpaul	if (vr_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT) {
65541502Swpaul		if (verbose)
65641502Swpaul			printf("link status good ");
65741502Swpaul		advert = vr_phy_readreg(sc, PHY_ANAR);
65841502Swpaul		ability = vr_phy_readreg(sc, PHY_LPAR);
65941502Swpaul
66041502Swpaul		if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) {
66141502Swpaul			ifm->ifm_media = IFM_ETHER|IFM_100_T4;
66241502Swpaul			media |= PHY_BMCR_SPEEDSEL;
66341502Swpaul			media &= ~PHY_BMCR_DUPLEX;
66441502Swpaul			printf("(100baseT4)\n");
66541502Swpaul		} else if (advert & PHY_ANAR_100BTXFULL &&
66641502Swpaul			ability & PHY_ANAR_100BTXFULL) {
66741502Swpaul			ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX;
66841502Swpaul			media |= PHY_BMCR_SPEEDSEL;
66941502Swpaul			media |= PHY_BMCR_DUPLEX;
67041502Swpaul			printf("(full-duplex, 100Mbps)\n");
67141502Swpaul		} else if (advert & PHY_ANAR_100BTXHALF &&
67241502Swpaul			ability & PHY_ANAR_100BTXHALF) {
67341502Swpaul			ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX;
67441502Swpaul			media |= PHY_BMCR_SPEEDSEL;
67541502Swpaul			media &= ~PHY_BMCR_DUPLEX;
67641502Swpaul			printf("(half-duplex, 100Mbps)\n");
67741502Swpaul		} else if (advert & PHY_ANAR_10BTFULL &&
67841502Swpaul			ability & PHY_ANAR_10BTFULL) {
67941502Swpaul			ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX;
68041502Swpaul			media &= ~PHY_BMCR_SPEEDSEL;
68141502Swpaul			media |= PHY_BMCR_DUPLEX;
68241502Swpaul			printf("(full-duplex, 10Mbps)\n");
68341502Swpaul		} else {
68441502Swpaul			ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX;
68541502Swpaul			media &= ~PHY_BMCR_SPEEDSEL;
68641502Swpaul			media &= ~PHY_BMCR_DUPLEX;
68741502Swpaul			printf("(half-duplex, 10Mbps)\n");
68841502Swpaul		}
68941502Swpaul
69041502Swpaul		media &= ~PHY_BMCR_AUTONEGENBL;
69141502Swpaul
69241502Swpaul		/* Set ASIC's duplex mode to match the PHY. */
69341502Swpaul		vr_setcfg(sc, media);
69441502Swpaul		vr_phy_writereg(sc, PHY_BMCR, media);
69541502Swpaul	} else {
69641502Swpaul		if (verbose)
69741502Swpaul			printf("no carrier\n");
69841502Swpaul	}
69941502Swpaul
70041502Swpaul	vr_init(sc);
70141502Swpaul
70241502Swpaul	if (sc->vr_tx_pend) {
70341502Swpaul		sc->vr_autoneg = 0;
70441502Swpaul		sc->vr_tx_pend = 0;
70541502Swpaul		vr_start(ifp);
70641502Swpaul	}
70741502Swpaul
70841502Swpaul	return;
70941502Swpaul}
71041502Swpaul
71141502Swpaulstatic void vr_getmode_mii(sc)
71241502Swpaul	struct vr_softc		*sc;
71341502Swpaul{
71441502Swpaul	u_int16_t		bmsr;
71541502Swpaul	struct ifnet		*ifp;
71641502Swpaul
71741502Swpaul	ifp = &sc->arpcom.ac_if;
71841502Swpaul
71941502Swpaul	bmsr = vr_phy_readreg(sc, PHY_BMSR);
72041502Swpaul	if (bootverbose)
72141502Swpaul		printf("vr%d: PHY status word: %x\n", sc->vr_unit, bmsr);
72241502Swpaul
72341502Swpaul	/* fallback */
72441502Swpaul	sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX;
72541502Swpaul
72641502Swpaul	if (bmsr & PHY_BMSR_10BTHALF) {
72741502Swpaul		if (bootverbose)
72841502Swpaul			printf("vr%d: 10Mbps half-duplex mode supported\n",
72941502Swpaul								sc->vr_unit);
73041502Swpaul		ifmedia_add(&sc->ifmedia,
73141502Swpaul			IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL);
73241502Swpaul		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL);
73341502Swpaul	}
73441502Swpaul
73541502Swpaul	if (bmsr & PHY_BMSR_10BTFULL) {
73641502Swpaul		if (bootverbose)
73741502Swpaul			printf("vr%d: 10Mbps full-duplex mode supported\n",
73841502Swpaul								sc->vr_unit);
73941502Swpaul		ifmedia_add(&sc->ifmedia,
74041502Swpaul			IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
74141502Swpaul		sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX;
74241502Swpaul	}
74341502Swpaul
74441502Swpaul	if (bmsr & PHY_BMSR_100BTXHALF) {
74541502Swpaul		if (bootverbose)
74641502Swpaul			printf("vr%d: 100Mbps half-duplex mode supported\n",
74741502Swpaul								sc->vr_unit);
74841502Swpaul		ifp->if_baudrate = 100000000;
74941502Swpaul		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL);
75041502Swpaul		ifmedia_add(&sc->ifmedia,
75141502Swpaul			IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL);
75241502Swpaul		sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX;
75341502Swpaul	}
75441502Swpaul
75541502Swpaul	if (bmsr & PHY_BMSR_100BTXFULL) {
75641502Swpaul		if (bootverbose)
75741502Swpaul			printf("vr%d: 100Mbps full-duplex mode supported\n",
75841502Swpaul								sc->vr_unit);
75941502Swpaul		ifp->if_baudrate = 100000000;
76041502Swpaul		ifmedia_add(&sc->ifmedia,
76141502Swpaul			IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL);
76241502Swpaul		sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX;
76341502Swpaul	}
76441502Swpaul
76541502Swpaul	/* Some also support 100BaseT4. */
76641502Swpaul	if (bmsr & PHY_BMSR_100BT4) {
76741502Swpaul		if (bootverbose)
76841502Swpaul			printf("vr%d: 100baseT4 mode supported\n", sc->vr_unit);
76941502Swpaul		ifp->if_baudrate = 100000000;
77041502Swpaul		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL);
77141502Swpaul		sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_T4;
77241502Swpaul#ifdef FORCE_AUTONEG_TFOUR
77341502Swpaul		if (bootverbose)
77441502Swpaul			printf("vr%d: forcing on autoneg support for BT4\n",
77541502Swpaul							 sc->vr_unit);
77641502Swpaul		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0 NULL):
77741502Swpaul		sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO;
77841502Swpaul#endif
77941502Swpaul	}
78041502Swpaul
78141502Swpaul	if (bmsr & PHY_BMSR_CANAUTONEG) {
78241502Swpaul		if (bootverbose)
78341502Swpaul			printf("vr%d: autoneg supported\n", sc->vr_unit);
78441502Swpaul		ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL);
78541502Swpaul		sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO;
78641502Swpaul	}
78741502Swpaul
78841502Swpaul	return;
78941502Swpaul}
79041502Swpaul
79141502Swpaul/*
79241502Swpaul * Set speed and duplex mode.
79341502Swpaul */
79441502Swpaulstatic void vr_setmode_mii(sc, media)
79541502Swpaul	struct vr_softc		*sc;
79641502Swpaul	int			media;
79741502Swpaul{
79841502Swpaul	u_int16_t		bmcr;
79941502Swpaul	struct ifnet		*ifp;
80041502Swpaul
80141502Swpaul	ifp = &sc->arpcom.ac_if;
80241502Swpaul
80341502Swpaul	/*
80441502Swpaul	 * If an autoneg session is in progress, stop it.
80541502Swpaul	 */
80641502Swpaul	if (sc->vr_autoneg) {
80741502Swpaul		printf("vr%d: canceling autoneg session\n", sc->vr_unit);
80841502Swpaul		ifp->if_timer = sc->vr_autoneg = sc->vr_want_auto = 0;
80941502Swpaul		bmcr = vr_phy_readreg(sc, PHY_BMCR);
81041502Swpaul		bmcr &= ~PHY_BMCR_AUTONEGENBL;
81141502Swpaul		vr_phy_writereg(sc, PHY_BMCR, bmcr);
81241502Swpaul	}
81341502Swpaul
81441502Swpaul	printf("vr%d: selecting MII, ", sc->vr_unit);
81541502Swpaul
81641502Swpaul	bmcr = vr_phy_readreg(sc, PHY_BMCR);
81741502Swpaul
81841502Swpaul	bmcr &= ~(PHY_BMCR_AUTONEGENBL|PHY_BMCR_SPEEDSEL|
81941502Swpaul			PHY_BMCR_DUPLEX|PHY_BMCR_LOOPBK);
82041502Swpaul
82141502Swpaul	if (IFM_SUBTYPE(media) == IFM_100_T4) {
82241502Swpaul		printf("100Mbps/T4, half-duplex\n");
82341502Swpaul		bmcr |= PHY_BMCR_SPEEDSEL;
82441502Swpaul		bmcr &= ~PHY_BMCR_DUPLEX;
82541502Swpaul	}
82641502Swpaul
82741502Swpaul	if (IFM_SUBTYPE(media) == IFM_100_TX) {
82841502Swpaul		printf("100Mbps, ");
82941502Swpaul		bmcr |= PHY_BMCR_SPEEDSEL;
83041502Swpaul	}
83141502Swpaul
83241502Swpaul	if (IFM_SUBTYPE(media) == IFM_10_T) {
83341502Swpaul		printf("10Mbps, ");
83441502Swpaul		bmcr &= ~PHY_BMCR_SPEEDSEL;
83541502Swpaul	}
83641502Swpaul
83741502Swpaul	if ((media & IFM_GMASK) == IFM_FDX) {
83841502Swpaul		printf("full duplex\n");
83941502Swpaul		bmcr |= PHY_BMCR_DUPLEX;
84041502Swpaul	} else {
84141502Swpaul		printf("half duplex\n");
84241502Swpaul		bmcr &= ~PHY_BMCR_DUPLEX;
84341502Swpaul	}
84441502Swpaul
84541502Swpaul	vr_setcfg(sc, bmcr);
84641502Swpaul	vr_phy_writereg(sc, PHY_BMCR, bmcr);
84741502Swpaul
84841502Swpaul	return;
84941502Swpaul}
85041502Swpaul
85141502Swpaul/*
85241502Swpaul * In order to fiddle with the
85341502Swpaul * 'full-duplex' and '100Mbps' bits in the netconfig register, we
85441502Swpaul * first have to put the transmit and/or receive logic in the idle state.
85541502Swpaul */
85641502Swpaulstatic void vr_setcfg(sc, bmcr)
85741502Swpaul	struct vr_softc		*sc;
85841502Swpaul	u_int16_t		bmcr;
85941502Swpaul{
86041502Swpaul	int			restart = 0;
86141502Swpaul
86241502Swpaul	if (CSR_READ_2(sc, VR_COMMAND) & (VR_CMD_TX_ON|VR_CMD_RX_ON)) {
86341502Swpaul		restart = 1;
86441502Swpaul		VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_TX_ON|VR_CMD_RX_ON));
86541502Swpaul	}
86641502Swpaul
86741502Swpaul	if (bmcr & PHY_BMCR_DUPLEX)
86841502Swpaul		VR_SETBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX);
86941502Swpaul	else
87041502Swpaul		VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX);
87141502Swpaul
87241502Swpaul	if (restart)
87341502Swpaul		VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_RX_ON);
87441502Swpaul
87541502Swpaul	return;
87641502Swpaul}
87741502Swpaul
87841502Swpaulstatic void vr_reset(sc)
87941502Swpaul	struct vr_softc		*sc;
88041502Swpaul{
88141502Swpaul	register int		i;
88241502Swpaul
88341502Swpaul	VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RESET);
88441502Swpaul
88541502Swpaul	for (i = 0; i < VR_TIMEOUT; i++) {
88641502Swpaul		DELAY(10);
88741502Swpaul		if (!(CSR_READ_2(sc, VR_COMMAND) & VR_CMD_RESET))
88841502Swpaul			break;
88941502Swpaul	}
89041502Swpaul	if (i == VR_TIMEOUT)
89141502Swpaul		printf("vr%d: reset never completed!\n", sc->vr_unit);
89241502Swpaul
89341502Swpaul	/* Wait a little while for the chip to get its brains in order. */
89441502Swpaul	DELAY(1000);
89541502Swpaul
89641502Swpaul        return;
89741502Swpaul}
89841502Swpaul
89941502Swpaul/*
90041502Swpaul * Probe for a VIA Rhine chip. Check the PCI vendor and device
90141502Swpaul * IDs against our list and return a device name if we find a match.
90241502Swpaul */
90349610Swpaulstatic int vr_probe(dev)
90449610Swpaul	device_t		dev;
90541502Swpaul{
90641502Swpaul	struct vr_type		*t;
90741502Swpaul
90841502Swpaul	t = vr_devs;
90941502Swpaul
91041502Swpaul	while(t->vr_name != NULL) {
91149610Swpaul		if ((pci_get_vendor(dev) == t->vr_vid) &&
91249610Swpaul		    (pci_get_device(dev) == t->vr_did)) {
91349610Swpaul			device_set_desc(dev, t->vr_name);
91449610Swpaul			return(0);
91541502Swpaul		}
91641502Swpaul		t++;
91741502Swpaul	}
91841502Swpaul
91949610Swpaul	return(ENXIO);
92041502Swpaul}
92141502Swpaul
92241502Swpaul/*
92341502Swpaul * Attach the interface. Allocate softc structures, do ifmedia
92441502Swpaul * setup and ethernet/BPF attach.
92541502Swpaul */
92649610Swpaulstatic int vr_attach(dev)
92749610Swpaul	device_t		dev;
92841502Swpaul{
92941502Swpaul	int			s, i;
93041502Swpaul	u_char			eaddr[ETHER_ADDR_LEN];
93141502Swpaul	u_int32_t		command;
93241502Swpaul	struct vr_softc		*sc;
93341502Swpaul	struct ifnet		*ifp;
93441502Swpaul	int			media = IFM_ETHER|IFM_100_TX|IFM_FDX;
93541502Swpaul	unsigned int		round;
93641502Swpaul	caddr_t			roundptr;
93741502Swpaul	struct vr_type		*p;
93841502Swpaul	u_int16_t		phy_vid, phy_did, phy_sts;
93949610Swpaul	int			unit, error = 0, rid;
94041502Swpaul
94141502Swpaul	s = splimp();
94241502Swpaul
94349610Swpaul	sc = device_get_softc(dev);
94449610Swpaul	unit = device_get_unit(dev);
94549610Swpaul	bzero(sc, sizeof(struct vr_softc *));
94641502Swpaul
94741502Swpaul	/*
94841502Swpaul	 * Handle power management nonsense.
94941502Swpaul	 */
95041502Swpaul
95149610Swpaul	command = pci_read_config(dev, VR_PCI_CAPID, 4) & 0x000000FF;
95241502Swpaul	if (command == 0x01) {
95341502Swpaul
95449610Swpaul		command = pci_read_config(dev, VR_PCI_PWRMGMTCTRL, 4);
95541502Swpaul		if (command & VR_PSTATE_MASK) {
95641502Swpaul			u_int32_t		iobase, membase, irq;
95741502Swpaul
95841502Swpaul			/* Save important PCI config data. */
95949610Swpaul			iobase = pci_read_config(dev, VR_PCI_LOIO, 4);
96049610Swpaul			membase = pci_read_config(dev, VR_PCI_LOMEM, 4);
96149610Swpaul			irq = pci_read_config(dev, VR_PCI_INTLINE, 4);
96241502Swpaul
96341502Swpaul			/* Reset the power state. */
96441502Swpaul			printf("vr%d: chip is in D%d power mode "
96541502Swpaul			"-- setting to D0\n", unit, command & VR_PSTATE_MASK);
96641502Swpaul			command &= 0xFFFFFFFC;
96749610Swpaul			pci_write_config(dev, VR_PCI_PWRMGMTCTRL, command, 4);
96841502Swpaul
96941502Swpaul			/* Restore PCI config data. */
97049610Swpaul			pci_write_config(dev, VR_PCI_LOIO, iobase, 4);
97149610Swpaul			pci_write_config(dev, VR_PCI_LOMEM, membase, 4);
97249610Swpaul			pci_write_config(dev, VR_PCI_INTLINE, irq, 4);
97341502Swpaul		}
97441502Swpaul	}
97541502Swpaul
97641502Swpaul	/*
97741502Swpaul	 * Map control/status registers.
97841502Swpaul	 */
97949610Swpaul	command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4);
98041502Swpaul	command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
98149610Swpaul	pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4);
98249610Swpaul	command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4);
98341502Swpaul
98441502Swpaul#ifdef VR_USEIOSPACE
98541502Swpaul	if (!(command & PCIM_CMD_PORTEN)) {
98641502Swpaul		printf("vr%d: failed to enable I/O ports!\n", unit);
98741502Swpaul		free(sc, M_DEVBUF);
98841502Swpaul		goto fail;
98941502Swpaul	}
99041502Swpaul#else
99141502Swpaul	if (!(command & PCIM_CMD_MEMEN)) {
99241502Swpaul		printf("vr%d: failed to enable memory mapping!\n", unit);
99341502Swpaul		goto fail;
99441502Swpaul	}
99549610Swpaul#endif
99641502Swpaul
99749610Swpaul	rid = VR_RID;
99849610Swpaul	sc->vr_res = bus_alloc_resource(dev, VR_RES, &rid,
99949610Swpaul	    0, ~0, 1, RF_ACTIVE);
100049610Swpaul
100149610Swpaul	if (sc->vr_res == NULL) {
100249610Swpaul		printf("vr%d: couldn't map ports/memory\n", unit);
100349610Swpaul		error = ENXIO;
100441502Swpaul		goto fail;
100541502Swpaul	}
100641502Swpaul
100749610Swpaul	sc->vr_btag = rman_get_bustag(sc->vr_res);
100849610Swpaul	sc->vr_bhandle = rman_get_bushandle(sc->vr_res);
100941502Swpaul
101041502Swpaul	/* Allocate interrupt */
101149610Swpaul	rid = 0;
101249610Swpaul	sc->vr_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
101349610Swpaul	    RF_SHAREABLE | RF_ACTIVE);
101449610Swpaul
101549610Swpaul	if (sc->vr_irq == NULL) {
101641502Swpaul		printf("vr%d: couldn't map interrupt\n", unit);
101749610Swpaul		bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res);
101849610Swpaul		error = ENXIO;
101941502Swpaul		goto fail;
102041502Swpaul	}
102141502Swpaul
102249610Swpaul	error = bus_setup_intr(dev, sc->vr_irq, INTR_TYPE_NET,
102349610Swpaul	    vr_intr, sc, &sc->vr_intrhand);
102449610Swpaul
102549610Swpaul	if (error) {
102649610Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq);
102749610Swpaul		bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res);
102849610Swpaul		printf("vr%d: couldn't set up irq\n", unit);
102949610Swpaul		goto fail;
103049610Swpaul	}
103149610Swpaul
103241502Swpaul	/* Reset the adapter. */
103341502Swpaul	vr_reset(sc);
103441502Swpaul
103541502Swpaul	/*
103641502Swpaul	 * Get station address. The way the Rhine chips work,
103741502Swpaul	 * you're not allowed to directly access the EEPROM once
103841502Swpaul	 * they've been programmed a special way. Consequently,
103941502Swpaul	 * we need to read the node address from the PAR0 and PAR1
104041502Swpaul	 * registers.
104141502Swpaul	 */
104241502Swpaul	VR_SETBIT(sc, VR_EECSR, VR_EECSR_LOAD);
104341502Swpaul	DELAY(200);
104441502Swpaul	for (i = 0; i < ETHER_ADDR_LEN; i++)
104541502Swpaul		eaddr[i] = CSR_READ_1(sc, VR_PAR0 + i);
104641502Swpaul
104741502Swpaul	/*
104841502Swpaul	 * A Rhine chip was detected. Inform the world.
104941502Swpaul	 */
105041502Swpaul	printf("vr%d: Ethernet address: %6D\n", unit, eaddr, ":");
105141502Swpaul
105241502Swpaul	sc->vr_unit = unit;
105341502Swpaul	bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
105441502Swpaul
105541502Swpaul	sc->vr_ldata_ptr = malloc(sizeof(struct vr_list_data) + 8,
105641502Swpaul				M_DEVBUF, M_NOWAIT);
105741502Swpaul	if (sc->vr_ldata_ptr == NULL) {
105841502Swpaul		printf("vr%d: no memory for list buffers!\n", unit);
105949610Swpaul		bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand);
106049610Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq);
106149610Swpaul		bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res);
106249610Swpaul		error = ENXIO;
106349610Swpaul		goto fail;
106441502Swpaul	}
106541502Swpaul
106641502Swpaul	sc->vr_ldata = (struct vr_list_data *)sc->vr_ldata_ptr;
106741502Swpaul	round = (unsigned int)sc->vr_ldata_ptr & 0xF;
106841502Swpaul	roundptr = sc->vr_ldata_ptr;
106941502Swpaul	for (i = 0; i < 8; i++) {
107041502Swpaul		if (round % 8) {
107141502Swpaul			round++;
107241502Swpaul			roundptr++;
107341502Swpaul		} else
107441502Swpaul			break;
107541502Swpaul	}
107641502Swpaul	sc->vr_ldata = (struct vr_list_data *)roundptr;
107741502Swpaul	bzero(sc->vr_ldata, sizeof(struct vr_list_data));
107841502Swpaul
107941502Swpaul	ifp = &sc->arpcom.ac_if;
108041502Swpaul	ifp->if_softc = sc;
108141502Swpaul	ifp->if_unit = unit;
108241502Swpaul	ifp->if_name = "vr";
108341502Swpaul	ifp->if_mtu = ETHERMTU;
108441502Swpaul	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
108541502Swpaul	ifp->if_ioctl = vr_ioctl;
108641502Swpaul	ifp->if_output = ether_output;
108741502Swpaul	ifp->if_start = vr_start;
108841502Swpaul	ifp->if_watchdog = vr_watchdog;
108941502Swpaul	ifp->if_init = vr_init;
109041502Swpaul	ifp->if_baudrate = 10000000;
109143515Swpaul	ifp->if_snd.ifq_maxlen = VR_TX_LIST_CNT - 1;
109241502Swpaul
109341502Swpaul	if (bootverbose)
109441502Swpaul		printf("vr%d: probing for a PHY\n", sc->vr_unit);
109541502Swpaul	for (i = VR_PHYADDR_MIN; i < VR_PHYADDR_MAX + 1; i++) {
109641502Swpaul		if (bootverbose)
109741502Swpaul			printf("vr%d: checking address: %d\n",
109841502Swpaul						sc->vr_unit, i);
109941502Swpaul		sc->vr_phy_addr = i;
110041502Swpaul		vr_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET);
110141502Swpaul		DELAY(500);
110241502Swpaul		while(vr_phy_readreg(sc, PHY_BMCR)
110341502Swpaul				& PHY_BMCR_RESET);
110441502Swpaul		if ((phy_sts = vr_phy_readreg(sc, PHY_BMSR)))
110541502Swpaul			break;
110641502Swpaul	}
110741502Swpaul	if (phy_sts) {
110841502Swpaul		phy_vid = vr_phy_readreg(sc, PHY_VENID);
110941502Swpaul		phy_did = vr_phy_readreg(sc, PHY_DEVID);
111041502Swpaul		if (bootverbose)
111141502Swpaul			printf("vr%d: found PHY at address %d, ",
111241502Swpaul					sc->vr_unit, sc->vr_phy_addr);
111341502Swpaul		if (bootverbose)
111441502Swpaul			printf("vendor id: %x device id: %x\n",
111541502Swpaul				phy_vid, phy_did);
111641502Swpaul		p = vr_phys;
111741502Swpaul		while(p->vr_vid) {
111841502Swpaul			if (phy_vid == p->vr_vid &&
111941502Swpaul				(phy_did | 0x000F) == p->vr_did) {
112041502Swpaul				sc->vr_pinfo = p;
112141502Swpaul				break;
112241502Swpaul			}
112341502Swpaul			p++;
112441502Swpaul		}
112541502Swpaul		if (sc->vr_pinfo == NULL)
112641502Swpaul			sc->vr_pinfo = &vr_phys[PHY_UNKNOWN];
112741502Swpaul		if (bootverbose)
112841502Swpaul			printf("vr%d: PHY type: %s\n",
112941502Swpaul				sc->vr_unit, sc->vr_pinfo->vr_name);
113041502Swpaul	} else {
113141502Swpaul		printf("vr%d: MII without any phy!\n", sc->vr_unit);
113249610Swpaul		bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand);
113349610Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq);
113449610Swpaul		bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res);
113549612Swpaul		free(sc->vr_ldata_ptr, M_DEVBUF);
113649610Swpaul		error = ENXIO;
113741502Swpaul		goto fail;
113841502Swpaul	}
113941502Swpaul
114041502Swpaul	/*
114141502Swpaul	 * Do ifmedia setup.
114241502Swpaul	 */
114341502Swpaul	ifmedia_init(&sc->ifmedia, 0, vr_ifmedia_upd, vr_ifmedia_sts);
114441502Swpaul
114541502Swpaul	vr_getmode_mii(sc);
114649610Swpaul	if (cold) {
114749610Swpaul		vr_autoneg_mii(sc, VR_FLAG_FORCEDELAY, 1);
114849610Swpaul		vr_stop(sc);
114949610Swpaul	} else {
115049610Swpaul		vr_init(sc);
115149610Swpaul		vr_autoneg_mii(sc, VR_FLAG_SCHEDDELAY, 1);
115249610Swpaul	}
115349610Swpaul
115441502Swpaul	media = sc->ifmedia.ifm_media;
115541502Swpaul
115641502Swpaul	ifmedia_set(&sc->ifmedia, media);
115741502Swpaul
115841502Swpaul	/*
115941502Swpaul	 * Call MI attach routines.
116041502Swpaul	 */
116141502Swpaul	if_attach(ifp);
116241502Swpaul	ether_ifattach(ifp);
116341502Swpaul
116448645Sdes#if NBPF > 0
116541502Swpaul	bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
116641502Swpaul#endif
116741502Swpaul
116841502Swpaulfail:
116941502Swpaul	splx(s);
117049610Swpaul	return(error);
117141502Swpaul}
117241502Swpaul
117349610Swpaulstatic int vr_detach(dev)
117449610Swpaul	device_t		dev;
117549610Swpaul{
117649610Swpaul	struct vr_softc		*sc;
117749610Swpaul	struct ifnet		*ifp;
117849610Swpaul	int			s;
117949610Swpaul
118049610Swpaul	s = splimp();
118149610Swpaul
118249610Swpaul	sc = device_get_softc(dev);
118349610Swpaul	ifp = &sc->arpcom.ac_if;
118449610Swpaul
118549610Swpaul	vr_stop(sc);
118649610Swpaul	if_detach(ifp);
118749610Swpaul
118849610Swpaul	bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand);
118949610Swpaul	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq);
119049610Swpaul	bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res);
119149610Swpaul
119249610Swpaul	free(sc->vr_ldata_ptr, M_DEVBUF);
119349610Swpaul	ifmedia_removeall(&sc->ifmedia);
119449610Swpaul
119549610Swpaul	splx(s);
119649610Swpaul
119749610Swpaul	return(0);
119849610Swpaul}
119949610Swpaul
120041502Swpaul/*
120141502Swpaul * Initialize the transmit descriptors.
120241502Swpaul */
120341502Swpaulstatic int vr_list_tx_init(sc)
120441502Swpaul	struct vr_softc		*sc;
120541502Swpaul{
120641502Swpaul	struct vr_chain_data	*cd;
120741502Swpaul	struct vr_list_data	*ld;
120841502Swpaul	int			i;
120941502Swpaul
121041502Swpaul	cd = &sc->vr_cdata;
121141502Swpaul	ld = sc->vr_ldata;
121241502Swpaul	for (i = 0; i < VR_TX_LIST_CNT; i++) {
121341502Swpaul		cd->vr_tx_chain[i].vr_ptr = &ld->vr_tx_list[i];
121441502Swpaul		if (i == (VR_TX_LIST_CNT - 1))
121541502Swpaul			cd->vr_tx_chain[i].vr_nextdesc =
121641502Swpaul				&cd->vr_tx_chain[0];
121741502Swpaul		else
121841502Swpaul			cd->vr_tx_chain[i].vr_nextdesc =
121941502Swpaul				&cd->vr_tx_chain[i + 1];
122041502Swpaul	}
122141502Swpaul
122241502Swpaul	cd->vr_tx_free = &cd->vr_tx_chain[0];
122341502Swpaul	cd->vr_tx_tail = cd->vr_tx_head = NULL;
122441502Swpaul
122541502Swpaul	return(0);
122641502Swpaul}
122741502Swpaul
122841502Swpaul
122941502Swpaul/*
123041502Swpaul * Initialize the RX descriptors and allocate mbufs for them. Note that
123141502Swpaul * we arrange the descriptors in a closed ring, so that the last descriptor
123241502Swpaul * points back to the first.
123341502Swpaul */
123441502Swpaulstatic int vr_list_rx_init(sc)
123541502Swpaul	struct vr_softc		*sc;
123641502Swpaul{
123741502Swpaul	struct vr_chain_data	*cd;
123841502Swpaul	struct vr_list_data	*ld;
123941502Swpaul	int			i;
124041502Swpaul
124141502Swpaul	cd = &sc->vr_cdata;
124241502Swpaul	ld = sc->vr_ldata;
124341502Swpaul
124441502Swpaul	for (i = 0; i < VR_RX_LIST_CNT; i++) {
124541502Swpaul		cd->vr_rx_chain[i].vr_ptr =
124641502Swpaul			(struct vr_desc *)&ld->vr_rx_list[i];
124749610Swpaul		if (vr_newbuf(sc, &cd->vr_rx_chain[i], NULL) == ENOBUFS)
124841502Swpaul			return(ENOBUFS);
124941502Swpaul		if (i == (VR_RX_LIST_CNT - 1)) {
125041502Swpaul			cd->vr_rx_chain[i].vr_nextdesc =
125141502Swpaul					&cd->vr_rx_chain[0];
125241502Swpaul			ld->vr_rx_list[i].vr_next =
125341502Swpaul					vtophys(&ld->vr_rx_list[0]);
125441502Swpaul		} else {
125541502Swpaul			cd->vr_rx_chain[i].vr_nextdesc =
125641502Swpaul					&cd->vr_rx_chain[i + 1];
125741502Swpaul			ld->vr_rx_list[i].vr_next =
125841502Swpaul					vtophys(&ld->vr_rx_list[i + 1]);
125941502Swpaul		}
126041502Swpaul	}
126141502Swpaul
126241502Swpaul	cd->vr_rx_head = &cd->vr_rx_chain[0];
126341502Swpaul
126441502Swpaul	return(0);
126541502Swpaul}
126641502Swpaul
126741502Swpaul/*
126841502Swpaul * Initialize an RX descriptor and attach an MBUF cluster.
126941502Swpaul * Note: the length fields are only 11 bits wide, which means the
127041502Swpaul * largest size we can specify is 2047. This is important because
127141502Swpaul * MCLBYTES is 2048, so we have to subtract one otherwise we'll
127241502Swpaul * overflow the field and make a mess.
127341502Swpaul */
127449610Swpaulstatic int vr_newbuf(sc, c, m)
127541502Swpaul	struct vr_softc		*sc;
127641502Swpaul	struct vr_chain_onefrag	*c;
127749610Swpaul	struct mbuf		*m;
127841502Swpaul{
127941502Swpaul	struct mbuf		*m_new = NULL;
128041502Swpaul
128149610Swpaul	if (m == NULL) {
128249610Swpaul		MGETHDR(m_new, M_DONTWAIT, MT_DATA);
128349610Swpaul		if (m_new == NULL) {
128449610Swpaul			printf("vr%d: no memory for rx list "
128549610Swpaul			    "-- packet dropped!\n", sc->vr_unit);
128649610Swpaul			return(ENOBUFS);
128749610Swpaul		}
128841502Swpaul
128949610Swpaul		MCLGET(m_new, M_DONTWAIT);
129049610Swpaul		if (!(m_new->m_flags & M_EXT)) {
129149610Swpaul			printf("vr%d: no memory for rx list "
129249610Swpaul			    "-- packet dropped!\n", sc->vr_unit);
129349610Swpaul			m_freem(m_new);
129449610Swpaul			return(ENOBUFS);
129549610Swpaul		}
129649610Swpaul		m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
129749610Swpaul	} else {
129849610Swpaul		m_new = m;
129949610Swpaul		m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
130049610Swpaul		m_new->m_data = m_new->m_ext.ext_buf;
130141502Swpaul	}
130241502Swpaul
130349610Swpaul	m_adj(m_new, sizeof(u_int64_t));
130449610Swpaul
130541502Swpaul	c->vr_mbuf = m_new;
130641502Swpaul	c->vr_ptr->vr_status = VR_RXSTAT;
130741502Swpaul	c->vr_ptr->vr_data = vtophys(mtod(m_new, caddr_t));
130842491Swpaul	c->vr_ptr->vr_ctl = VR_RXCTL | VR_RXLEN;
130941502Swpaul
131041502Swpaul	return(0);
131141502Swpaul}
131241502Swpaul
131341502Swpaul/*
131441502Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to
131541502Swpaul * the higher level protocols.
131641502Swpaul */
131741502Swpaulstatic void vr_rxeof(sc)
131841502Swpaul	struct vr_softc		*sc;
131941502Swpaul{
132041502Swpaul        struct ether_header	*eh;
132141502Swpaul        struct mbuf		*m;
132241502Swpaul        struct ifnet		*ifp;
132341502Swpaul	struct vr_chain_onefrag	*cur_rx;
132441502Swpaul	int			total_len = 0;
132541502Swpaul	u_int32_t		rxstat;
132641502Swpaul
132741502Swpaul	ifp = &sc->arpcom.ac_if;
132841502Swpaul
132941502Swpaul	while(!((rxstat = sc->vr_cdata.vr_rx_head->vr_ptr->vr_status) &
133041502Swpaul							VR_RXSTAT_OWN)) {
133149610Swpaul		struct mbuf		*m0 = NULL;
133249610Swpaul
133341502Swpaul		cur_rx = sc->vr_cdata.vr_rx_head;
133441502Swpaul		sc->vr_cdata.vr_rx_head = cur_rx->vr_nextdesc;
133549610Swpaul		m = cur_rx->vr_mbuf;
133641502Swpaul
133741502Swpaul		/*
133841502Swpaul		 * If an error occurs, update stats, clear the
133941502Swpaul		 * status word and leave the mbuf cluster in place:
134041502Swpaul		 * it should simply get re-used next time this descriptor
134141502Swpaul	 	 * comes up in the ring.
134241502Swpaul		 */
134341502Swpaul		if (rxstat & VR_RXSTAT_RXERR) {
134441502Swpaul			ifp->if_ierrors++;
134541502Swpaul			printf("vr%d: rx error: ", sc->vr_unit);
134641502Swpaul			switch(rxstat & 0x000000FF) {
134741502Swpaul			case VR_RXSTAT_CRCERR:
134841502Swpaul				printf("crc error\n");
134941502Swpaul				break;
135041502Swpaul			case VR_RXSTAT_FRAMEALIGNERR:
135141502Swpaul				printf("frame alignment error\n");
135241502Swpaul				break;
135341502Swpaul			case VR_RXSTAT_FIFOOFLOW:
135441502Swpaul				printf("FIFO overflow\n");
135541502Swpaul				break;
135641502Swpaul			case VR_RXSTAT_GIANT:
135741502Swpaul				printf("received giant packet\n");
135841502Swpaul				break;
135941502Swpaul			case VR_RXSTAT_RUNT:
136041502Swpaul				printf("received runt packet\n");
136141502Swpaul				break;
136241502Swpaul			case VR_RXSTAT_BUSERR:
136341502Swpaul				printf("system bus error\n");
136441502Swpaul				break;
136541502Swpaul			case VR_RXSTAT_BUFFERR:
136641502Swpaul				printf("rx buffer error\n");
136741502Swpaul				break;
136841502Swpaul			default:
136941502Swpaul				printf("unknown rx error\n");
137041502Swpaul				break;
137141502Swpaul			}
137249610Swpaul			vr_newbuf(sc, cur_rx, m);
137341502Swpaul			continue;
137441502Swpaul		}
137541502Swpaul
137641502Swpaul		/* No errors; receive the packet. */
137741502Swpaul		total_len = VR_RXBYTES(cur_rx->vr_ptr->vr_status);
137841502Swpaul
137941502Swpaul		/*
138042048Swpaul		 * XXX The VIA Rhine chip includes the CRC with every
138142048Swpaul		 * received frame, and there's no way to turn this
138242048Swpaul		 * behavior off (at least, I can't find anything in
138342048Swpaul	 	 * the manual that explains how to do it) so we have
138442048Swpaul		 * to trim off the CRC manually.
138542048Swpaul		 */
138642048Swpaul		total_len -= ETHER_CRC_LEN;
138742048Swpaul
138849610Swpaul		m0 = m_devget(mtod(m, char *) - ETHER_ALIGN,
138949610Swpaul		    total_len + ETHER_ALIGN, 0, ifp, NULL);
139049610Swpaul		vr_newbuf(sc, cur_rx, m);
139149610Swpaul		if (m0 == NULL) {
139241502Swpaul			ifp->if_ierrors++;
139341502Swpaul			continue;
139441502Swpaul		}
139549610Swpaul		m_adj(m0, ETHER_ALIGN);
139649610Swpaul		m = m0;
139741502Swpaul
139841502Swpaul		ifp->if_ipackets++;
139941502Swpaul		eh = mtod(m, struct ether_header *);
140049610Swpaul
140148645Sdes#if NBPF > 0
140241502Swpaul		/*
140341502Swpaul		 * Handle BPF listeners. Let the BPF user see the packet, but
140441502Swpaul		 * don't pass it up to the ether_input() layer unless it's
140541502Swpaul		 * a broadcast packet, multicast packet, matches our ethernet
140641502Swpaul		 * address or the interface is in promiscuous mode.
140741502Swpaul		 */
140841502Swpaul		if (ifp->if_bpf) {
140941502Swpaul			bpf_mtap(ifp, m);
141041502Swpaul			if (ifp->if_flags & IFF_PROMISC &&
141141502Swpaul				(bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
141241502Swpaul						ETHER_ADDR_LEN) &&
141341502Swpaul					(eh->ether_dhost[0] & 1) == 0)) {
141441502Swpaul				m_freem(m);
141541502Swpaul				continue;
141641502Swpaul			}
141741502Swpaul		}
141841502Swpaul#endif
141941502Swpaul		/* Remove header from mbuf and pass it on. */
142041502Swpaul		m_adj(m, sizeof(struct ether_header));
142141502Swpaul		ether_input(ifp, eh, m);
142241502Swpaul	}
142341502Swpaul
142441502Swpaul	return;
142541502Swpaul}
142641502Swpaul
142741502Swpaulvoid vr_rxeoc(sc)
142841502Swpaul	struct vr_softc		*sc;
142941502Swpaul{
143041502Swpaul
143141502Swpaul	vr_rxeof(sc);
143241502Swpaul	VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_RX_ON);
143341502Swpaul	CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr));
143441502Swpaul	VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_ON);
143541502Swpaul	VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_GO);
143641502Swpaul
143741502Swpaul	return;
143841502Swpaul}
143941502Swpaul
144041502Swpaul/*
144141502Swpaul * A frame was downloaded to the chip. It's safe for us to clean up
144241502Swpaul * the list buffers.
144341502Swpaul */
144441502Swpaul
144541502Swpaulstatic void vr_txeof(sc)
144641502Swpaul	struct vr_softc		*sc;
144741502Swpaul{
144841502Swpaul	struct vr_chain		*cur_tx;
144941502Swpaul	struct ifnet		*ifp;
145041502Swpaul	register struct mbuf	*n;
145141502Swpaul
145241502Swpaul	ifp = &sc->arpcom.ac_if;
145341502Swpaul
145441502Swpaul	/* Clear the timeout timer. */
145541502Swpaul	ifp->if_timer = 0;
145641502Swpaul
145741502Swpaul	/* Sanity check. */
145841502Swpaul	if (sc->vr_cdata.vr_tx_head == NULL)
145941502Swpaul		return;
146041502Swpaul
146141502Swpaul	/*
146241502Swpaul	 * Go through our tx list and free mbufs for those
146341502Swpaul	 * frames that have been transmitted.
146441502Swpaul	 */
146541502Swpaul	while(sc->vr_cdata.vr_tx_head->vr_mbuf != NULL) {
146641502Swpaul		u_int32_t		txstat;
146741502Swpaul
146841502Swpaul		cur_tx = sc->vr_cdata.vr_tx_head;
146941502Swpaul		txstat = cur_tx->vr_ptr->vr_status;
147041502Swpaul
147142491Swpaul		if (txstat & VR_TXSTAT_OWN)
147241502Swpaul			break;
147341502Swpaul
147441502Swpaul		if (txstat & VR_TXSTAT_ERRSUM) {
147541502Swpaul			ifp->if_oerrors++;
147641502Swpaul			if (txstat & VR_TXSTAT_DEFER)
147741502Swpaul				ifp->if_collisions++;
147841502Swpaul			if (txstat & VR_TXSTAT_LATECOLL)
147941502Swpaul				ifp->if_collisions++;
148041502Swpaul		}
148141502Swpaul
148241502Swpaul		ifp->if_collisions +=(txstat & VR_TXSTAT_COLLCNT) >> 3;
148341502Swpaul
148441502Swpaul		ifp->if_opackets++;
148541502Swpaul        	MFREE(cur_tx->vr_mbuf, n);
148641502Swpaul		cur_tx->vr_mbuf = NULL;
148741502Swpaul
148841502Swpaul		if (sc->vr_cdata.vr_tx_head == sc->vr_cdata.vr_tx_tail) {
148941502Swpaul			sc->vr_cdata.vr_tx_head = NULL;
149041502Swpaul			sc->vr_cdata.vr_tx_tail = NULL;
149141502Swpaul			break;
149241502Swpaul		}
149341502Swpaul
149441502Swpaul		sc->vr_cdata.vr_tx_head = cur_tx->vr_nextdesc;
149541502Swpaul	}
149641502Swpaul
149741502Swpaul	return;
149841502Swpaul}
149941502Swpaul
150041502Swpaul/*
150141502Swpaul * TX 'end of channel' interrupt handler.
150241502Swpaul */
150341502Swpaulstatic void vr_txeoc(sc)
150441502Swpaul	struct vr_softc		*sc;
150541502Swpaul{
150641502Swpaul	struct ifnet		*ifp;
150741502Swpaul
150841502Swpaul	ifp = &sc->arpcom.ac_if;
150941502Swpaul
151041502Swpaul	ifp->if_timer = 0;
151141502Swpaul
151241502Swpaul	if (sc->vr_cdata.vr_tx_head == NULL) {
151341502Swpaul		ifp->if_flags &= ~IFF_OACTIVE;
151441502Swpaul		sc->vr_cdata.vr_tx_tail = NULL;
151541502Swpaul		if (sc->vr_want_auto)
151641502Swpaul			vr_autoneg_mii(sc, VR_FLAG_SCHEDDELAY, 1);
151741502Swpaul	}
151841502Swpaul
151941502Swpaul	return;
152041502Swpaul}
152141502Swpaul
152241502Swpaulstatic void vr_intr(arg)
152341502Swpaul	void			*arg;
152441502Swpaul{
152541502Swpaul	struct vr_softc		*sc;
152641502Swpaul	struct ifnet		*ifp;
152741502Swpaul	u_int16_t		status;
152841502Swpaul
152941502Swpaul	sc = arg;
153041502Swpaul	ifp = &sc->arpcom.ac_if;
153141502Swpaul
153241502Swpaul	/* Supress unwanted interrupts. */
153341502Swpaul	if (!(ifp->if_flags & IFF_UP)) {
153441502Swpaul		vr_stop(sc);
153541502Swpaul		return;
153641502Swpaul	}
153741502Swpaul
153841502Swpaul	/* Disable interrupts. */
153941502Swpaul	CSR_WRITE_2(sc, VR_IMR, 0x0000);
154041502Swpaul
154141502Swpaul	for (;;) {
154241502Swpaul
154341502Swpaul		status = CSR_READ_2(sc, VR_ISR);
154441502Swpaul		if (status)
154541502Swpaul			CSR_WRITE_2(sc, VR_ISR, status);
154641502Swpaul
154741502Swpaul		if ((status & VR_INTRS) == 0)
154841502Swpaul			break;
154941502Swpaul
155041502Swpaul		if (status & VR_ISR_RX_OK)
155141502Swpaul			vr_rxeof(sc);
155241502Swpaul
155341502Swpaul		if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) ||
155441502Swpaul		    (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW) ||
155541502Swpaul		    (status & VR_ISR_RX_DROPPED)) {
155641502Swpaul			vr_rxeof(sc);
155741502Swpaul			vr_rxeoc(sc);
155841502Swpaul		}
155941502Swpaul
156041502Swpaul		if (status & VR_ISR_TX_OK) {
156141502Swpaul			vr_txeof(sc);
156241502Swpaul			vr_txeoc(sc);
156341502Swpaul		}
156441502Swpaul
156541502Swpaul		if ((status & VR_ISR_TX_UNDERRUN)||(status & VR_ISR_TX_ABRT)){
156641502Swpaul			ifp->if_oerrors++;
156741502Swpaul			vr_txeof(sc);
156841502Swpaul			if (sc->vr_cdata.vr_tx_head != NULL) {
156941502Swpaul				VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON);
157041502Swpaul				VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO);
157141502Swpaul			}
157241502Swpaul		}
157341502Swpaul
157441502Swpaul		if (status & VR_ISR_BUSERR) {
157541502Swpaul			vr_reset(sc);
157641502Swpaul			vr_init(sc);
157741502Swpaul		}
157841502Swpaul	}
157941502Swpaul
158041502Swpaul	/* Re-enable interrupts. */
158141502Swpaul	CSR_WRITE_2(sc, VR_IMR, VR_INTRS);
158241502Swpaul
158341502Swpaul	if (ifp->if_snd.ifq_head != NULL) {
158441502Swpaul		vr_start(ifp);
158541502Swpaul	}
158641502Swpaul
158741502Swpaul	return;
158841502Swpaul}
158941502Swpaul
159041502Swpaul/*
159141502Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
159241502Swpaul * pointers to the fragment pointers.
159341502Swpaul */
159441502Swpaulstatic int vr_encap(sc, c, m_head)
159541502Swpaul	struct vr_softc		*sc;
159641502Swpaul	struct vr_chain		*c;
159741502Swpaul	struct mbuf		*m_head;
159841502Swpaul{
159941502Swpaul	int			frag = 0;
160041502Swpaul	struct vr_desc		*f = NULL;
160141502Swpaul	int			total_len;
160241502Swpaul	struct mbuf		*m;
160341502Swpaul
160441502Swpaul	m = m_head;
160541502Swpaul	total_len = 0;
160641502Swpaul
160741502Swpaul	/*
160841502Swpaul	 * The VIA Rhine wants packet buffers to be longword
160941502Swpaul	 * aligned, but very often our mbufs aren't. Rather than
161041502Swpaul	 * waste time trying to decide when to copy and when not
161141502Swpaul	 * to copy, just do it all the time.
161241502Swpaul	 */
161341502Swpaul	if (m != NULL) {
161441502Swpaul		struct mbuf		*m_new = NULL;
161541502Swpaul
161641502Swpaul		MGETHDR(m_new, M_DONTWAIT, MT_DATA);
161741502Swpaul		if (m_new == NULL) {
161841502Swpaul			printf("vr%d: no memory for tx list", sc->vr_unit);
161941502Swpaul			return(1);
162041502Swpaul		}
162141502Swpaul		if (m_head->m_pkthdr.len > MHLEN) {
162241502Swpaul			MCLGET(m_new, M_DONTWAIT);
162341502Swpaul			if (!(m_new->m_flags & M_EXT)) {
162441502Swpaul				m_freem(m_new);
162541502Swpaul				printf("vr%d: no memory for tx list",
162641502Swpaul						sc->vr_unit);
162741502Swpaul				return(1);
162841502Swpaul			}
162941502Swpaul		}
163041502Swpaul		m_copydata(m_head, 0, m_head->m_pkthdr.len,
163141502Swpaul					mtod(m_new, caddr_t));
163241502Swpaul		m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len;
163341502Swpaul		m_freem(m_head);
163441502Swpaul		m_head = m_new;
163541502Swpaul		/*
163641502Swpaul		 * The Rhine chip doesn't auto-pad, so we have to make
163741502Swpaul		 * sure to pad short frames out to the minimum frame length
163841502Swpaul		 * ourselves.
163941502Swpaul		 */
164041502Swpaul		if (m_head->m_len < VR_MIN_FRAMELEN) {
164141502Swpaul			m_new->m_pkthdr.len += VR_MIN_FRAMELEN - m_new->m_len;
164241502Swpaul			m_new->m_len = m_new->m_pkthdr.len;
164341502Swpaul		}
164441502Swpaul		f = c->vr_ptr;
164541502Swpaul		f->vr_data = vtophys(mtod(m_new, caddr_t));
164641502Swpaul		f->vr_ctl = total_len = m_new->m_len;
164741502Swpaul		f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG;
164841502Swpaul		f->vr_status = 0;
164941502Swpaul		frag = 1;
165041502Swpaul	}
165141502Swpaul
165241502Swpaul	c->vr_mbuf = m_head;
165342491Swpaul	c->vr_ptr->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT;
165441502Swpaul	c->vr_ptr->vr_next = vtophys(c->vr_nextdesc->vr_ptr);
165541502Swpaul
165641502Swpaul	return(0);
165741502Swpaul}
165841502Swpaul
165941502Swpaul/*
166041502Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers
166141502Swpaul * to the mbuf data regions directly in the transmit lists. We also save a
166241502Swpaul * copy of the pointers since the transmit list fragment pointers are
166341502Swpaul * physical addresses.
166441502Swpaul */
166541502Swpaul
166641502Swpaulstatic void vr_start(ifp)
166741502Swpaul	struct ifnet		*ifp;
166841502Swpaul{
166941502Swpaul	struct vr_softc		*sc;
167041502Swpaul	struct mbuf		*m_head = NULL;
167141502Swpaul	struct vr_chain		*cur_tx = NULL, *start_tx;
167241502Swpaul
167341502Swpaul	sc = ifp->if_softc;
167441502Swpaul
167541502Swpaul	if (sc->vr_autoneg) {
167641502Swpaul		sc->vr_tx_pend = 1;
167741502Swpaul		return;
167841502Swpaul	}
167941502Swpaul
168041502Swpaul	/*
168141502Swpaul	 * Check for an available queue slot. If there are none,
168241502Swpaul	 * punt.
168341502Swpaul	 */
168441502Swpaul	if (sc->vr_cdata.vr_tx_free->vr_mbuf != NULL) {
168541502Swpaul		ifp->if_flags |= IFF_OACTIVE;
168641502Swpaul		return;
168741502Swpaul	}
168841502Swpaul
168941502Swpaul	start_tx = sc->vr_cdata.vr_tx_free;
169041502Swpaul
169141502Swpaul	while(sc->vr_cdata.vr_tx_free->vr_mbuf == NULL) {
169241502Swpaul		IF_DEQUEUE(&ifp->if_snd, m_head);
169341502Swpaul		if (m_head == NULL)
169441502Swpaul			break;
169541502Swpaul
169641502Swpaul		/* Pick a descriptor off the free list. */
169741502Swpaul		cur_tx = sc->vr_cdata.vr_tx_free;
169841502Swpaul		sc->vr_cdata.vr_tx_free = cur_tx->vr_nextdesc;
169941502Swpaul
170041502Swpaul		/* Pack the data into the descriptor. */
170141502Swpaul		vr_encap(sc, cur_tx, m_head);
170241502Swpaul
170341502Swpaul		if (cur_tx != start_tx)
170441502Swpaul			VR_TXOWN(cur_tx) = VR_TXSTAT_OWN;
170541502Swpaul
170648645Sdes#if NBPF > 0
170741502Swpaul		/*
170841502Swpaul		 * If there's a BPF listener, bounce a copy of this frame
170941502Swpaul		 * to him.
171041502Swpaul		 */
171141502Swpaul		if (ifp->if_bpf)
171241502Swpaul			bpf_mtap(ifp, cur_tx->vr_mbuf);
171341502Swpaul#endif
171442491Swpaul		VR_TXOWN(cur_tx) = VR_TXSTAT_OWN;
171542491Swpaul		VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_TX_GO);
171641502Swpaul	}
171741502Swpaul
171841502Swpaul	/*
171941526Swpaul	 * If there are no frames queued, bail.
172041526Swpaul	 */
172141526Swpaul	if (cur_tx == NULL)
172241526Swpaul		return;
172341526Swpaul
172441502Swpaul	sc->vr_cdata.vr_tx_tail = cur_tx;
172541502Swpaul
172642491Swpaul	if (sc->vr_cdata.vr_tx_head == NULL)
172741502Swpaul		sc->vr_cdata.vr_tx_head = start_tx;
172841502Swpaul
172941502Swpaul	/*
173041502Swpaul	 * Set a timeout in case the chip goes out to lunch.
173141502Swpaul	 */
173241502Swpaul	ifp->if_timer = 5;
173341502Swpaul
173441502Swpaul	return;
173541502Swpaul}
173641502Swpaul
173741502Swpaulstatic void vr_init(xsc)
173841502Swpaul	void			*xsc;
173941502Swpaul{
174041502Swpaul	struct vr_softc		*sc = xsc;
174141502Swpaul	struct ifnet		*ifp = &sc->arpcom.ac_if;
174241502Swpaul	u_int16_t		phy_bmcr = 0;
174341502Swpaul	int			s;
174441502Swpaul
174541502Swpaul	if (sc->vr_autoneg)
174641502Swpaul		return;
174741502Swpaul
174841502Swpaul	s = splimp();
174941502Swpaul
175041502Swpaul	if (sc->vr_pinfo != NULL)
175141502Swpaul		phy_bmcr = vr_phy_readreg(sc, PHY_BMCR);
175241502Swpaul
175341502Swpaul	/*
175441502Swpaul	 * Cancel pending I/O and free all RX/TX buffers.
175541502Swpaul	 */
175641502Swpaul	vr_stop(sc);
175741502Swpaul	vr_reset(sc);
175841502Swpaul
175941502Swpaul	VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_THRESH);
176041502Swpaul	VR_SETBIT(sc, VR_RXCFG, VR_RXTHRESH_STORENFWD);
176141502Swpaul
176241502Swpaul	VR_CLRBIT(sc, VR_TXCFG, VR_TXCFG_TX_THRESH);
176341502Swpaul	VR_SETBIT(sc, VR_TXCFG, VR_TXTHRESH_STORENFWD);
176441502Swpaul
176541502Swpaul	/* Init circular RX list. */
176641502Swpaul	if (vr_list_rx_init(sc) == ENOBUFS) {
176741502Swpaul		printf("vr%d: initialization failed: no "
176841502Swpaul			"memory for rx buffers\n", sc->vr_unit);
176941502Swpaul		vr_stop(sc);
177041502Swpaul		(void)splx(s);
177141502Swpaul		return;
177241502Swpaul	}
177341502Swpaul
177441502Swpaul	/*
177541502Swpaul	 * Init tx descriptors.
177641502Swpaul	 */
177741502Swpaul	vr_list_tx_init(sc);
177841502Swpaul
177941502Swpaul	/* If we want promiscuous mode, set the allframes bit. */
178041502Swpaul	if (ifp->if_flags & IFF_PROMISC)
178141502Swpaul		VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC);
178241502Swpaul	else
178341502Swpaul		VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC);
178441502Swpaul
178541502Swpaul	/* Set capture broadcast bit to capture broadcast frames. */
178641502Swpaul	if (ifp->if_flags & IFF_BROADCAST)
178741502Swpaul		VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD);
178841502Swpaul	else
178941502Swpaul		VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD);
179041502Swpaul
179141502Swpaul	/*
179241502Swpaul	 * Program the multicast filter, if necessary.
179341502Swpaul	 */
179441502Swpaul	vr_setmulti(sc);
179541502Swpaul
179641502Swpaul	/*
179741502Swpaul	 * Load the address of the RX list.
179841502Swpaul	 */
179941502Swpaul	CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr));
180041502Swpaul
180141502Swpaul	/* Enable receiver and transmitter. */
180241502Swpaul	CSR_WRITE_2(sc, VR_COMMAND, VR_CMD_TX_NOPOLL|VR_CMD_START|
180341502Swpaul				    VR_CMD_TX_ON|VR_CMD_RX_ON|
180441502Swpaul				    VR_CMD_RX_GO);
180541502Swpaul
180641502Swpaul	vr_setcfg(sc, vr_phy_readreg(sc, PHY_BMCR));
180741502Swpaul
180841502Swpaul	CSR_WRITE_4(sc, VR_TXADDR, vtophys(&sc->vr_ldata->vr_tx_list[0]));
180941502Swpaul
181041502Swpaul	/*
181141502Swpaul	 * Enable interrupts.
181241502Swpaul	 */
181341502Swpaul	CSR_WRITE_2(sc, VR_ISR, 0xFFFF);
181441502Swpaul	CSR_WRITE_2(sc, VR_IMR, VR_INTRS);
181541502Swpaul
181641502Swpaul	/* Restore state of BMCR */
181741502Swpaul	if (sc->vr_pinfo != NULL)
181841502Swpaul		vr_phy_writereg(sc, PHY_BMCR, phy_bmcr);
181941502Swpaul
182041502Swpaul	ifp->if_flags |= IFF_RUNNING;
182141502Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
182241502Swpaul
182341502Swpaul	(void)splx(s);
182441502Swpaul
182541502Swpaul	return;
182641502Swpaul}
182741502Swpaul
182841502Swpaul/*
182941502Swpaul * Set media options.
183041502Swpaul */
183141502Swpaulstatic int vr_ifmedia_upd(ifp)
183241502Swpaul	struct ifnet		*ifp;
183341502Swpaul{
183441502Swpaul	struct vr_softc		*sc;
183541502Swpaul	struct ifmedia		*ifm;
183641502Swpaul
183741502Swpaul	sc = ifp->if_softc;
183841502Swpaul	ifm = &sc->ifmedia;
183941502Swpaul
184041502Swpaul	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
184141502Swpaul		return(EINVAL);
184241502Swpaul
184341502Swpaul	if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO)
184441502Swpaul		vr_autoneg_mii(sc, VR_FLAG_SCHEDDELAY, 1);
184541502Swpaul	else
184641502Swpaul		vr_setmode_mii(sc, ifm->ifm_media);
184741502Swpaul
184841502Swpaul	return(0);
184941502Swpaul}
185041502Swpaul
185141502Swpaul/*
185241502Swpaul * Report current media status.
185341502Swpaul */
185441502Swpaulstatic void vr_ifmedia_sts(ifp, ifmr)
185541502Swpaul	struct ifnet		*ifp;
185641502Swpaul	struct ifmediareq	*ifmr;
185741502Swpaul{
185841502Swpaul	struct vr_softc		*sc;
185941502Swpaul	u_int16_t		advert = 0, ability = 0;
186041502Swpaul
186141502Swpaul	sc = ifp->if_softc;
186241502Swpaul
186341502Swpaul	ifmr->ifm_active = IFM_ETHER;
186441502Swpaul
186541502Swpaul	if (!(vr_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_AUTONEGENBL)) {
186641502Swpaul		if (vr_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_SPEEDSEL)
186741502Swpaul			ifmr->ifm_active = IFM_ETHER|IFM_100_TX;
186841502Swpaul		else
186941502Swpaul			ifmr->ifm_active = IFM_ETHER|IFM_10_T;
187041502Swpaul		if (vr_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_DUPLEX)
187141502Swpaul			ifmr->ifm_active |= IFM_FDX;
187241502Swpaul		else
187341502Swpaul			ifmr->ifm_active |= IFM_HDX;
187441502Swpaul		return;
187541502Swpaul	}
187641502Swpaul
187741502Swpaul	ability = vr_phy_readreg(sc, PHY_LPAR);
187841502Swpaul	advert = vr_phy_readreg(sc, PHY_ANAR);
187941502Swpaul	if (advert & PHY_ANAR_100BT4 &&
188041502Swpaul		ability & PHY_ANAR_100BT4) {
188141502Swpaul		ifmr->ifm_active = IFM_ETHER|IFM_100_T4;
188241502Swpaul	} else if (advert & PHY_ANAR_100BTXFULL &&
188341502Swpaul		ability & PHY_ANAR_100BTXFULL) {
188441502Swpaul		ifmr->ifm_active = IFM_ETHER|IFM_100_TX|IFM_FDX;
188541502Swpaul	} else if (advert & PHY_ANAR_100BTXHALF &&
188641502Swpaul		ability & PHY_ANAR_100BTXHALF) {
188741502Swpaul		ifmr->ifm_active = IFM_ETHER|IFM_100_TX|IFM_HDX;
188841502Swpaul	} else if (advert & PHY_ANAR_10BTFULL &&
188941502Swpaul		ability & PHY_ANAR_10BTFULL) {
189041502Swpaul		ifmr->ifm_active = IFM_ETHER|IFM_10_T|IFM_FDX;
189141502Swpaul	} else if (advert & PHY_ANAR_10BTHALF &&
189241502Swpaul		ability & PHY_ANAR_10BTHALF) {
189341502Swpaul		ifmr->ifm_active = IFM_ETHER|IFM_10_T|IFM_HDX;
189441502Swpaul	}
189541502Swpaul
189641502Swpaul	return;
189741502Swpaul}
189841502Swpaul
189941502Swpaulstatic int vr_ioctl(ifp, command, data)
190041502Swpaul	struct ifnet		*ifp;
190141502Swpaul	u_long			command;
190241502Swpaul	caddr_t			data;
190341502Swpaul{
190441502Swpaul	struct vr_softc		*sc = ifp->if_softc;
190541502Swpaul	struct ifreq		*ifr = (struct ifreq *) data;
190641502Swpaul	int			s, error = 0;
190741502Swpaul
190841502Swpaul	s = splimp();
190941502Swpaul
191041502Swpaul	switch(command) {
191141502Swpaul	case SIOCSIFADDR:
191241502Swpaul	case SIOCGIFADDR:
191341502Swpaul	case SIOCSIFMTU:
191441502Swpaul		error = ether_ioctl(ifp, command, data);
191541502Swpaul		break;
191641502Swpaul	case SIOCSIFFLAGS:
191741502Swpaul		if (ifp->if_flags & IFF_UP) {
191841502Swpaul			vr_init(sc);
191941502Swpaul		} else {
192041502Swpaul			if (ifp->if_flags & IFF_RUNNING)
192141502Swpaul				vr_stop(sc);
192241502Swpaul		}
192341502Swpaul		error = 0;
192441502Swpaul		break;
192541502Swpaul	case SIOCADDMULTI:
192641502Swpaul	case SIOCDELMULTI:
192741502Swpaul		vr_setmulti(sc);
192841502Swpaul		error = 0;
192941502Swpaul		break;
193041502Swpaul	case SIOCGIFMEDIA:
193141502Swpaul	case SIOCSIFMEDIA:
193241502Swpaul		error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command);
193341502Swpaul		break;
193441502Swpaul	default:
193541502Swpaul		error = EINVAL;
193641502Swpaul		break;
193741502Swpaul	}
193841502Swpaul
193941502Swpaul	(void)splx(s);
194041502Swpaul
194141502Swpaul	return(error);
194241502Swpaul}
194341502Swpaul
194441502Swpaulstatic void vr_watchdog(ifp)
194541502Swpaul	struct ifnet		*ifp;
194641502Swpaul{
194741502Swpaul	struct vr_softc		*sc;
194841502Swpaul
194941502Swpaul	sc = ifp->if_softc;
195041502Swpaul
195141502Swpaul	if (sc->vr_autoneg) {
195241502Swpaul		vr_autoneg_mii(sc, VR_FLAG_DELAYTIMEO, 1);
195349610Swpaul		if (!(ifp->if_flags & IFF_UP))
195449610Swpaul			vr_stop(sc);
195541502Swpaul		return;
195641502Swpaul	}
195741502Swpaul
195841502Swpaul	ifp->if_oerrors++;
195941502Swpaul	printf("vr%d: watchdog timeout\n", sc->vr_unit);
196041502Swpaul
196141502Swpaul	if (!(vr_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT))
196241502Swpaul		printf("vr%d: no carrier - transceiver cable problem?\n",
196341502Swpaul								sc->vr_unit);
196441502Swpaul
196541502Swpaul	vr_stop(sc);
196641502Swpaul	vr_reset(sc);
196741502Swpaul	vr_init(sc);
196841502Swpaul
196941502Swpaul	if (ifp->if_snd.ifq_head != NULL)
197041502Swpaul		vr_start(ifp);
197141502Swpaul
197241502Swpaul	return;
197341502Swpaul}
197441502Swpaul
197541502Swpaul/*
197641502Swpaul * Stop the adapter and free any mbufs allocated to the
197741502Swpaul * RX and TX lists.
197841502Swpaul */
197941502Swpaulstatic void vr_stop(sc)
198041502Swpaul	struct vr_softc		*sc;
198141502Swpaul{
198241502Swpaul	register int		i;
198341502Swpaul	struct ifnet		*ifp;
198441502Swpaul
198541502Swpaul	ifp = &sc->arpcom.ac_if;
198641502Swpaul	ifp->if_timer = 0;
198741502Swpaul
198841502Swpaul	VR_SETBIT16(sc, VR_COMMAND, VR_CMD_STOP);
198941502Swpaul	VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_RX_ON|VR_CMD_TX_ON));
199041502Swpaul	CSR_WRITE_2(sc, VR_IMR, 0x0000);
199141502Swpaul	CSR_WRITE_4(sc, VR_TXADDR, 0x00000000);
199241502Swpaul	CSR_WRITE_4(sc, VR_RXADDR, 0x00000000);
199341502Swpaul
199441502Swpaul	/*
199541502Swpaul	 * Free data in the RX lists.
199641502Swpaul	 */
199741502Swpaul	for (i = 0; i < VR_RX_LIST_CNT; i++) {
199841502Swpaul		if (sc->vr_cdata.vr_rx_chain[i].vr_mbuf != NULL) {
199941502Swpaul			m_freem(sc->vr_cdata.vr_rx_chain[i].vr_mbuf);
200041502Swpaul			sc->vr_cdata.vr_rx_chain[i].vr_mbuf = NULL;
200141502Swpaul		}
200241502Swpaul	}
200341502Swpaul	bzero((char *)&sc->vr_ldata->vr_rx_list,
200441502Swpaul		sizeof(sc->vr_ldata->vr_rx_list));
200541502Swpaul
200641502Swpaul	/*
200741502Swpaul	 * Free the TX list buffers.
200841502Swpaul	 */
200941502Swpaul	for (i = 0; i < VR_TX_LIST_CNT; i++) {
201041502Swpaul		if (sc->vr_cdata.vr_tx_chain[i].vr_mbuf != NULL) {
201141502Swpaul			m_freem(sc->vr_cdata.vr_tx_chain[i].vr_mbuf);
201241502Swpaul			sc->vr_cdata.vr_tx_chain[i].vr_mbuf = NULL;
201341502Swpaul		}
201441502Swpaul	}
201541502Swpaul
201641502Swpaul	bzero((char *)&sc->vr_ldata->vr_tx_list,
201741502Swpaul		sizeof(sc->vr_ldata->vr_tx_list));
201841502Swpaul
201941502Swpaul	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
202041502Swpaul
202141502Swpaul	return;
202241502Swpaul}
202341502Swpaul
202441502Swpaul/*
202541502Swpaul * Stop all chip I/O so that the kernel's probe routines don't
202641502Swpaul * get confused by errant DMAs when rebooting.
202741502Swpaul */
202849610Swpaulstatic void vr_shutdown(dev)
202949610Swpaul	device_t		dev;
203041502Swpaul{
203149610Swpaul	struct vr_softc		*sc;
203241502Swpaul
203349610Swpaul	sc = device_get_softc(dev);
203449610Swpaul
203541502Swpaul	vr_stop(sc);
203641502Swpaul
203741502Swpaul	return;
203841502Swpaul}
2039