if_vr.c revision 43515
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 * 3243515Swpaul * $Id: if_vr.c,v 1.7 1999/01/10 18:51:49 wpaul Exp $ 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 6241502Swpaul#include "bpfilter.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 7841502Swpaul#if NBPFILTER > 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> 8841502Swpaul 8941502Swpaul#include <pci/pcireg.h> 9041502Swpaul#include <pci/pcivar.h> 9141502Swpaul 9241502Swpaul#define VR_USEIOSPACE 9341502Swpaul 9441502Swpaul/* #define VR_BACKGROUND_AUTONEG */ 9541502Swpaul 9641502Swpaul#include <pci/if_vrreg.h> 9741502Swpaul 9841502Swpaul#ifndef lint 9941591Sarchiestatic const char rcsid[] = 10043515Swpaul "$Id: if_vr.c,v 1.7 1999/01/10 18:51:49 wpaul Exp $"; 10141502Swpaul#endif 10241502Swpaul 10341502Swpaul/* 10441502Swpaul * Various supported device vendors/types and their names. 10541502Swpaul */ 10641502Swpaulstatic struct vr_type vr_devs[] = { 10741502Swpaul { VIA_VENDORID, VIA_DEVICEID_RHINE, 10841502Swpaul "VIA VT3043 Rhine I 10/100BaseTX" }, 10941502Swpaul { VIA_VENDORID, VIA_DEVICEID_RHINE_II, 11041502Swpaul "VIA VT86C100A Rhine II 10/100BaseTX" }, 11141502Swpaul { 0, 0, NULL } 11241502Swpaul}; 11341502Swpaul 11441502Swpaul/* 11541502Swpaul * Various supported PHY vendors/types and their names. Note that 11641502Swpaul * this driver will work with pretty much any MII-compliant PHY, 11741502Swpaul * so failure to positively identify the chip is not a fatal error. 11841502Swpaul */ 11941502Swpaul 12041502Swpaulstatic struct vr_type vr_phys[] = { 12141502Swpaul { TI_PHY_VENDORID, TI_PHY_10BT, "<TI ThunderLAN 10BT (internal)>" }, 12241502Swpaul { TI_PHY_VENDORID, TI_PHY_100VGPMI, "<TI TNETE211 100VG Any-LAN>" }, 12341502Swpaul { NS_PHY_VENDORID, NS_PHY_83840A, "<National Semiconductor DP83840A>"}, 12441502Swpaul { LEVEL1_PHY_VENDORID, LEVEL1_PHY_LXT970, "<Level 1 LXT970>" }, 12541502Swpaul { INTEL_PHY_VENDORID, INTEL_PHY_82555, "<Intel 82555>" }, 12641502Swpaul { SEEQ_PHY_VENDORID, SEEQ_PHY_80220, "<SEEQ 80220>" }, 12741502Swpaul { 0, 0, "<MII-compliant physical interface>" } 12841502Swpaul}; 12941502Swpaul 13041502Swpaulstatic unsigned long vr_count = 0; 13141771Sdillonstatic const char *vr_probe __P((pcici_t, pcidi_t)); 13241502Swpaulstatic void vr_attach __P((pcici_t, int)); 13341502Swpaul 13441502Swpaulstatic int vr_newbuf __P((struct vr_softc *, 13541502Swpaul struct vr_chain_onefrag *)); 13641502Swpaulstatic int vr_encap __P((struct vr_softc *, struct vr_chain *, 13741502Swpaul struct mbuf * )); 13841502Swpaul 13941502Swpaulstatic void vr_rxeof __P((struct vr_softc *)); 14041502Swpaulstatic void vr_rxeoc __P((struct vr_softc *)); 14141502Swpaulstatic void vr_txeof __P((struct vr_softc *)); 14241502Swpaulstatic void vr_txeoc __P((struct vr_softc *)); 14341502Swpaulstatic void vr_intr __P((void *)); 14441502Swpaulstatic void vr_start __P((struct ifnet *)); 14541502Swpaulstatic int vr_ioctl __P((struct ifnet *, u_long, caddr_t)); 14641502Swpaulstatic void vr_init __P((void *)); 14741502Swpaulstatic void vr_stop __P((struct vr_softc *)); 14841502Swpaulstatic void vr_watchdog __P((struct ifnet *)); 14941502Swpaulstatic void vr_shutdown __P((int, void *)); 15041502Swpaulstatic int vr_ifmedia_upd __P((struct ifnet *)); 15141502Swpaulstatic void vr_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 15241502Swpaul 15341502Swpaulstatic void vr_mii_sync __P((struct vr_softc *)); 15441502Swpaulstatic void vr_mii_send __P((struct vr_softc *, u_int32_t, int)); 15541502Swpaulstatic int vr_mii_readreg __P((struct vr_softc *, struct vr_mii_frame *)); 15641502Swpaulstatic int vr_mii_writereg __P((struct vr_softc *, struct vr_mii_frame *)); 15741502Swpaulstatic u_int16_t vr_phy_readreg __P((struct vr_softc *, int)); 15841502Swpaulstatic void vr_phy_writereg __P((struct vr_softc *, u_int16_t, u_int16_t)); 15941502Swpaul 16041502Swpaulstatic void vr_autoneg_xmit __P((struct vr_softc *)); 16141502Swpaulstatic void vr_autoneg_mii __P((struct vr_softc *, int, int)); 16241502Swpaulstatic void vr_setmode_mii __P((struct vr_softc *, int)); 16341502Swpaulstatic void vr_getmode_mii __P((struct vr_softc *)); 16441502Swpaulstatic void vr_setcfg __P((struct vr_softc *, u_int16_t)); 16541502Swpaulstatic u_int8_t vr_calchash __P((u_int8_t *)); 16641502Swpaulstatic void vr_setmulti __P((struct vr_softc *)); 16741502Swpaulstatic void vr_reset __P((struct vr_softc *)); 16841502Swpaulstatic int vr_list_rx_init __P((struct vr_softc *)); 16941502Swpaulstatic int vr_list_tx_init __P((struct vr_softc *)); 17041502Swpaul 17141502Swpaul#define VR_SETBIT(sc, reg, x) \ 17241502Swpaul CSR_WRITE_1(sc, reg, \ 17341502Swpaul CSR_READ_1(sc, reg) | x) 17441502Swpaul 17541502Swpaul#define VR_CLRBIT(sc, reg, x) \ 17641502Swpaul CSR_WRITE_1(sc, reg, \ 17741502Swpaul CSR_READ_1(sc, reg) & ~x) 17841502Swpaul 17941502Swpaul#define VR_SETBIT16(sc, reg, x) \ 18041502Swpaul CSR_WRITE_2(sc, reg, \ 18141502Swpaul CSR_READ_2(sc, reg) | x) 18241502Swpaul 18341502Swpaul#define VR_CLRBIT16(sc, reg, x) \ 18441502Swpaul CSR_WRITE_2(sc, reg, \ 18541502Swpaul CSR_READ_2(sc, reg) & ~x) 18641502Swpaul 18741502Swpaul#define VR_SETBIT32(sc, reg, x) \ 18841502Swpaul CSR_WRITE_4(sc, reg, \ 18941502Swpaul CSR_READ_4(sc, reg) | x) 19041502Swpaul 19141502Swpaul#define VR_CLRBIT32(sc, reg, x) \ 19241502Swpaul CSR_WRITE_4(sc, reg, \ 19341502Swpaul CSR_READ_4(sc, reg) & ~x) 19441502Swpaul 19541502Swpaul#define SIO_SET(x) \ 19641502Swpaul CSR_WRITE_1(sc, VR_MIICMD, \ 19741502Swpaul CSR_READ_1(sc, VR_MIICMD) | x) 19841502Swpaul 19941502Swpaul#define SIO_CLR(x) \ 20041502Swpaul CSR_WRITE_1(sc, VR_MIICMD, \ 20141502Swpaul CSR_READ_1(sc, VR_MIICMD) & ~x) 20241502Swpaul 20341502Swpaul/* 20441502Swpaul * Sync the PHYs by setting data bit and strobing the clock 32 times. 20541502Swpaul */ 20641502Swpaulstatic void vr_mii_sync(sc) 20741502Swpaul struct vr_softc *sc; 20841502Swpaul{ 20941502Swpaul register int i; 21041502Swpaul 21141502Swpaul SIO_SET(VR_MIICMD_DIR|VR_MIICMD_DATAIN); 21241502Swpaul 21341502Swpaul for (i = 0; i < 32; i++) { 21441502Swpaul SIO_SET(VR_MIICMD_CLK); 21541502Swpaul DELAY(1); 21641502Swpaul SIO_CLR(VR_MIICMD_CLK); 21741502Swpaul DELAY(1); 21841502Swpaul } 21941502Swpaul 22041502Swpaul return; 22141502Swpaul} 22241502Swpaul 22341502Swpaul/* 22441502Swpaul * Clock a series of bits through the MII. 22541502Swpaul */ 22641502Swpaulstatic void vr_mii_send(sc, bits, cnt) 22741502Swpaul struct vr_softc *sc; 22841502Swpaul u_int32_t bits; 22941502Swpaul int cnt; 23041502Swpaul{ 23141502Swpaul int i; 23241502Swpaul 23341502Swpaul SIO_CLR(VR_MIICMD_CLK); 23441502Swpaul 23541502Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 23641502Swpaul if (bits & i) { 23741502Swpaul SIO_SET(VR_MIICMD_DATAIN); 23841502Swpaul } else { 23941502Swpaul SIO_CLR(VR_MIICMD_DATAIN); 24041502Swpaul } 24141502Swpaul DELAY(1); 24241502Swpaul SIO_CLR(VR_MIICMD_CLK); 24341502Swpaul DELAY(1); 24441502Swpaul SIO_SET(VR_MIICMD_CLK); 24541502Swpaul } 24641502Swpaul} 24741502Swpaul 24841502Swpaul/* 24941502Swpaul * Read an PHY register through the MII. 25041502Swpaul */ 25141502Swpaulstatic int vr_mii_readreg(sc, frame) 25241502Swpaul struct vr_softc *sc; 25341502Swpaul struct vr_mii_frame *frame; 25441502Swpaul 25541502Swpaul{ 25641502Swpaul int i, ack, s; 25741502Swpaul 25841502Swpaul s = splimp(); 25941502Swpaul 26041502Swpaul /* 26141502Swpaul * Set up frame for RX. 26241502Swpaul */ 26341502Swpaul frame->mii_stdelim = VR_MII_STARTDELIM; 26441502Swpaul frame->mii_opcode = VR_MII_READOP; 26541502Swpaul frame->mii_turnaround = 0; 26641502Swpaul frame->mii_data = 0; 26741502Swpaul 26841502Swpaul CSR_WRITE_1(sc, VR_MIICMD, 0); 26941502Swpaul VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); 27041502Swpaul 27141502Swpaul /* 27241502Swpaul * Turn on data xmit. 27341502Swpaul */ 27441502Swpaul SIO_SET(VR_MIICMD_DIR); 27541502Swpaul 27641502Swpaul vr_mii_sync(sc); 27741502Swpaul 27841502Swpaul /* 27941502Swpaul * Send command/address info. 28041502Swpaul */ 28141502Swpaul vr_mii_send(sc, frame->mii_stdelim, 2); 28241502Swpaul vr_mii_send(sc, frame->mii_opcode, 2); 28341502Swpaul vr_mii_send(sc, frame->mii_phyaddr, 5); 28441502Swpaul vr_mii_send(sc, frame->mii_regaddr, 5); 28541502Swpaul 28641502Swpaul /* Idle bit */ 28741502Swpaul SIO_CLR((VR_MIICMD_CLK|VR_MIICMD_DATAIN)); 28841502Swpaul DELAY(1); 28941502Swpaul SIO_SET(VR_MIICMD_CLK); 29041502Swpaul DELAY(1); 29141502Swpaul 29241502Swpaul /* Turn off xmit. */ 29341502Swpaul SIO_CLR(VR_MIICMD_DIR); 29441502Swpaul 29541502Swpaul /* Check for ack */ 29641502Swpaul SIO_CLR(VR_MIICMD_CLK); 29741502Swpaul DELAY(1); 29841502Swpaul SIO_SET(VR_MIICMD_CLK); 29941502Swpaul DELAY(1); 30041502Swpaul ack = CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT; 30141502Swpaul 30241502Swpaul /* 30341502Swpaul * Now try reading data bits. If the ack failed, we still 30441502Swpaul * need to clock through 16 cycles to keep the PHY(s) in sync. 30541502Swpaul */ 30641502Swpaul if (ack) { 30741502Swpaul for(i = 0; i < 16; i++) { 30841502Swpaul SIO_CLR(VR_MIICMD_CLK); 30941502Swpaul DELAY(1); 31041502Swpaul SIO_SET(VR_MIICMD_CLK); 31141502Swpaul DELAY(1); 31241502Swpaul } 31341502Swpaul goto fail; 31441502Swpaul } 31541502Swpaul 31641502Swpaul for (i = 0x8000; i; i >>= 1) { 31741502Swpaul SIO_CLR(VR_MIICMD_CLK); 31841502Swpaul DELAY(1); 31941502Swpaul if (!ack) { 32041502Swpaul if (CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT) 32141502Swpaul frame->mii_data |= i; 32241502Swpaul DELAY(1); 32341502Swpaul } 32441502Swpaul SIO_SET(VR_MIICMD_CLK); 32541502Swpaul DELAY(1); 32641502Swpaul } 32741502Swpaul 32841502Swpaulfail: 32941502Swpaul 33041502Swpaul SIO_CLR(VR_MIICMD_CLK); 33141502Swpaul DELAY(1); 33241502Swpaul SIO_SET(VR_MIICMD_CLK); 33341502Swpaul DELAY(1); 33441502Swpaul 33541502Swpaul splx(s); 33641502Swpaul 33741502Swpaul if (ack) 33841502Swpaul return(1); 33941502Swpaul return(0); 34041502Swpaul} 34141502Swpaul 34241502Swpaul/* 34341502Swpaul * Write to a PHY register through the MII. 34441502Swpaul */ 34541502Swpaulstatic int vr_mii_writereg(sc, frame) 34641502Swpaul struct vr_softc *sc; 34741502Swpaul struct vr_mii_frame *frame; 34841502Swpaul 34941502Swpaul{ 35041502Swpaul int s; 35141502Swpaul 35241502Swpaul s = splimp(); 35341502Swpaul 35441502Swpaul CSR_WRITE_1(sc, VR_MIICMD, 0); 35541502Swpaul VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); 35641502Swpaul 35741502Swpaul /* 35841502Swpaul * Set up frame for TX. 35941502Swpaul */ 36041502Swpaul 36141502Swpaul frame->mii_stdelim = VR_MII_STARTDELIM; 36241502Swpaul frame->mii_opcode = VR_MII_WRITEOP; 36341502Swpaul frame->mii_turnaround = VR_MII_TURNAROUND; 36441502Swpaul 36541502Swpaul /* 36641502Swpaul * Turn on data output. 36741502Swpaul */ 36841502Swpaul SIO_SET(VR_MIICMD_DIR); 36941502Swpaul 37041502Swpaul vr_mii_sync(sc); 37141502Swpaul 37241502Swpaul vr_mii_send(sc, frame->mii_stdelim, 2); 37341502Swpaul vr_mii_send(sc, frame->mii_opcode, 2); 37441502Swpaul vr_mii_send(sc, frame->mii_phyaddr, 5); 37541502Swpaul vr_mii_send(sc, frame->mii_regaddr, 5); 37641502Swpaul vr_mii_send(sc, frame->mii_turnaround, 2); 37741502Swpaul vr_mii_send(sc, frame->mii_data, 16); 37841502Swpaul 37941502Swpaul /* Idle bit. */ 38041502Swpaul SIO_SET(VR_MIICMD_CLK); 38141502Swpaul DELAY(1); 38241502Swpaul SIO_CLR(VR_MIICMD_CLK); 38341502Swpaul DELAY(1); 38441502Swpaul 38541502Swpaul /* 38641502Swpaul * Turn off xmit. 38741502Swpaul */ 38841502Swpaul SIO_CLR(VR_MIICMD_DIR); 38941502Swpaul 39041502Swpaul splx(s); 39141502Swpaul 39241502Swpaul return(0); 39341502Swpaul} 39441502Swpaul 39541502Swpaulstatic u_int16_t vr_phy_readreg(sc, reg) 39641502Swpaul struct vr_softc *sc; 39741502Swpaul int reg; 39841502Swpaul{ 39941502Swpaul struct vr_mii_frame frame; 40041502Swpaul 40141502Swpaul bzero((char *)&frame, sizeof(frame)); 40241502Swpaul 40341502Swpaul frame.mii_phyaddr = sc->vr_phy_addr; 40441502Swpaul frame.mii_regaddr = reg; 40541502Swpaul vr_mii_readreg(sc, &frame); 40641502Swpaul 40741502Swpaul return(frame.mii_data); 40841502Swpaul} 40941502Swpaul 41041502Swpaulstatic void vr_phy_writereg(sc, reg, data) 41141502Swpaul struct vr_softc *sc; 41241502Swpaul u_int16_t reg; 41341502Swpaul u_int16_t data; 41441502Swpaul{ 41541502Swpaul struct vr_mii_frame frame; 41641502Swpaul 41741502Swpaul bzero((char *)&frame, sizeof(frame)); 41841502Swpaul 41941502Swpaul frame.mii_phyaddr = sc->vr_phy_addr; 42041502Swpaul frame.mii_regaddr = reg; 42141502Swpaul frame.mii_data = data; 42241502Swpaul 42341502Swpaul vr_mii_writereg(sc, &frame); 42441502Swpaul 42541502Swpaul return; 42641502Swpaul} 42741502Swpaul 42841502Swpaul/* 42941502Swpaul * Calculate CRC of a multicast group address, return the lower 6 bits. 43041502Swpaul */ 43141502Swpaulstatic u_int8_t vr_calchash(addr) 43241502Swpaul u_int8_t *addr; 43341502Swpaul{ 43441502Swpaul u_int32_t crc, carry; 43541502Swpaul int i, j; 43641502Swpaul u_int8_t c; 43741502Swpaul 43841502Swpaul /* Compute CRC for the address value. */ 43941502Swpaul crc = 0xFFFFFFFF; /* initial value */ 44041502Swpaul 44141502Swpaul for (i = 0; i < 6; i++) { 44241502Swpaul c = *(addr + i); 44341502Swpaul for (j = 0; j < 8; j++) { 44441502Swpaul carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); 44541502Swpaul crc <<= 1; 44641502Swpaul c >>= 1; 44741502Swpaul if (carry) 44841502Swpaul crc = (crc ^ 0x04c11db6) | carry; 44941502Swpaul } 45041502Swpaul } 45141502Swpaul 45241502Swpaul /* return the filter bit position */ 45341502Swpaul return((crc >> 26) & 0x0000003F); 45441502Swpaul} 45541502Swpaul 45641502Swpaul/* 45741502Swpaul * Program the 64-bit multicast hash filter. 45841502Swpaul */ 45941502Swpaulstatic void vr_setmulti(sc) 46041502Swpaul struct vr_softc *sc; 46141502Swpaul{ 46241502Swpaul struct ifnet *ifp; 46341502Swpaul int h = 0; 46441502Swpaul u_int32_t hashes[2] = { 0, 0 }; 46541502Swpaul struct ifmultiaddr *ifma; 46641502Swpaul u_int8_t rxfilt; 46741502Swpaul int mcnt = 0; 46841502Swpaul 46941502Swpaul ifp = &sc->arpcom.ac_if; 47041502Swpaul 47141502Swpaul rxfilt = CSR_READ_1(sc, VR_RXCFG); 47241502Swpaul 47341502Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 47441502Swpaul rxfilt |= VR_RXCFG_RX_MULTI; 47541502Swpaul CSR_WRITE_1(sc, VR_RXCFG, rxfilt); 47641502Swpaul CSR_WRITE_4(sc, VR_MAR0, 0xFFFFFFFF); 47741502Swpaul CSR_WRITE_4(sc, VR_MAR1, 0xFFFFFFFF); 47841502Swpaul return; 47941502Swpaul } 48041502Swpaul 48141502Swpaul /* first, zot all the existing hash bits */ 48241502Swpaul CSR_WRITE_4(sc, VR_MAR0, 0); 48341502Swpaul CSR_WRITE_4(sc, VR_MAR1, 0); 48441502Swpaul 48541502Swpaul /* now program new ones */ 48641502Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 48741502Swpaul ifma = ifma->ifma_link.le_next) { 48841502Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 48941502Swpaul continue; 49041502Swpaul h = vr_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 49141502Swpaul if (h < 32) 49241502Swpaul hashes[0] |= (1 << h); 49341502Swpaul else 49441502Swpaul hashes[1] |= (1 << (h - 32)); 49541502Swpaul mcnt++; 49641502Swpaul } 49741502Swpaul 49841502Swpaul if (mcnt) 49941502Swpaul rxfilt |= VR_RXCFG_RX_MULTI; 50041502Swpaul else 50141502Swpaul rxfilt &= ~VR_RXCFG_RX_MULTI; 50241502Swpaul 50341502Swpaul CSR_WRITE_4(sc, VR_MAR0, hashes[0]); 50441502Swpaul CSR_WRITE_4(sc, VR_MAR1, hashes[1]); 50541502Swpaul CSR_WRITE_1(sc, VR_RXCFG, rxfilt); 50641502Swpaul 50741502Swpaul return; 50841502Swpaul} 50941502Swpaul 51041502Swpaul/* 51141502Swpaul * Initiate an autonegotiation session. 51241502Swpaul */ 51341502Swpaulstatic void vr_autoneg_xmit(sc) 51441502Swpaul struct vr_softc *sc; 51541502Swpaul{ 51641502Swpaul u_int16_t phy_sts; 51741502Swpaul 51841502Swpaul vr_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); 51941502Swpaul DELAY(500); 52041502Swpaul while(vr_phy_readreg(sc, PHY_BMCR) 52141502Swpaul & PHY_BMCR_RESET); 52241502Swpaul 52341502Swpaul phy_sts = vr_phy_readreg(sc, PHY_BMCR); 52441502Swpaul phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR; 52541502Swpaul vr_phy_writereg(sc, PHY_BMCR, phy_sts); 52641502Swpaul 52741502Swpaul return; 52841502Swpaul} 52941502Swpaul 53041502Swpaul/* 53141502Swpaul * Invoke autonegotiation on a PHY. 53241502Swpaul */ 53341502Swpaulstatic void vr_autoneg_mii(sc, flag, verbose) 53441502Swpaul struct vr_softc *sc; 53541502Swpaul int flag; 53641502Swpaul int verbose; 53741502Swpaul{ 53841502Swpaul u_int16_t phy_sts = 0, media, advert, ability; 53941502Swpaul struct ifnet *ifp; 54041502Swpaul struct ifmedia *ifm; 54141502Swpaul 54241502Swpaul ifm = &sc->ifmedia; 54341502Swpaul ifp = &sc->arpcom.ac_if; 54441502Swpaul 54541502Swpaul ifm->ifm_media = IFM_ETHER | IFM_AUTO; 54641502Swpaul 54741502Swpaul /* 54841502Swpaul * The 100baseT4 PHY on the 3c905-T4 has the 'autoneg supported' 54941502Swpaul * bit cleared in the status register, but has the 'autoneg enabled' 55041502Swpaul * bit set in the control register. This is a contradiction, and 55141502Swpaul * I'm not sure how to handle it. If you want to force an attempt 55241502Swpaul * to autoneg for 100baseT4 PHYs, #define FORCE_AUTONEG_TFOUR 55341502Swpaul * and see what happens. 55441502Swpaul */ 55541502Swpaul#ifndef FORCE_AUTONEG_TFOUR 55641502Swpaul /* 55741502Swpaul * First, see if autoneg is supported. If not, there's 55841502Swpaul * no point in continuing. 55941502Swpaul */ 56041502Swpaul phy_sts = vr_phy_readreg(sc, PHY_BMSR); 56141502Swpaul if (!(phy_sts & PHY_BMSR_CANAUTONEG)) { 56241502Swpaul if (verbose) 56341502Swpaul printf("vr%d: autonegotiation not supported\n", 56441502Swpaul sc->vr_unit); 56541502Swpaul ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; 56641502Swpaul return; 56741502Swpaul } 56841502Swpaul#endif 56941502Swpaul 57041502Swpaul switch (flag) { 57141502Swpaul case VR_FLAG_FORCEDELAY: 57241502Swpaul /* 57341502Swpaul * XXX Never use this option anywhere but in the probe 57441502Swpaul * routine: making the kernel stop dead in its tracks 57541502Swpaul * for three whole seconds after we've gone multi-user 57641502Swpaul * is really bad manners. 57741502Swpaul */ 57841502Swpaul vr_autoneg_xmit(sc); 57941502Swpaul DELAY(5000000); 58041502Swpaul break; 58141502Swpaul case VR_FLAG_SCHEDDELAY: 58241502Swpaul /* 58341502Swpaul * Wait for the transmitter to go idle before starting 58441502Swpaul * an autoneg session, otherwise vr_start() may clobber 58541502Swpaul * our timeout, and we don't want to allow transmission 58641502Swpaul * during an autoneg session since that can screw it up. 58741502Swpaul */ 58841502Swpaul if (sc->vr_cdata.vr_tx_head != NULL) { 58941502Swpaul sc->vr_want_auto = 1; 59041502Swpaul return; 59141502Swpaul } 59241502Swpaul vr_autoneg_xmit(sc); 59341502Swpaul ifp->if_timer = 5; 59441502Swpaul sc->vr_autoneg = 1; 59541502Swpaul sc->vr_want_auto = 0; 59641502Swpaul return; 59741502Swpaul break; 59841502Swpaul case VR_FLAG_DELAYTIMEO: 59941502Swpaul ifp->if_timer = 0; 60041502Swpaul sc->vr_autoneg = 0; 60141502Swpaul break; 60241502Swpaul default: 60341502Swpaul printf("vr%d: invalid autoneg flag: %d\n", sc->vr_unit, flag); 60441502Swpaul return; 60541502Swpaul } 60641502Swpaul 60741502Swpaul if (vr_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_AUTONEGCOMP) { 60841502Swpaul if (verbose) 60941502Swpaul printf("vr%d: autoneg complete, ", sc->vr_unit); 61041502Swpaul phy_sts = vr_phy_readreg(sc, PHY_BMSR); 61141502Swpaul } else { 61241502Swpaul if (verbose) 61341502Swpaul printf("vr%d: autoneg not complete, ", sc->vr_unit); 61441502Swpaul } 61541502Swpaul 61641502Swpaul media = vr_phy_readreg(sc, PHY_BMCR); 61741502Swpaul 61841502Swpaul /* Link is good. Report modes and set duplex mode. */ 61941502Swpaul if (vr_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT) { 62041502Swpaul if (verbose) 62141502Swpaul printf("link status good "); 62241502Swpaul advert = vr_phy_readreg(sc, PHY_ANAR); 62341502Swpaul ability = vr_phy_readreg(sc, PHY_LPAR); 62441502Swpaul 62541502Swpaul if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) { 62641502Swpaul ifm->ifm_media = IFM_ETHER|IFM_100_T4; 62741502Swpaul media |= PHY_BMCR_SPEEDSEL; 62841502Swpaul media &= ~PHY_BMCR_DUPLEX; 62941502Swpaul printf("(100baseT4)\n"); 63041502Swpaul } else if (advert & PHY_ANAR_100BTXFULL && 63141502Swpaul ability & PHY_ANAR_100BTXFULL) { 63241502Swpaul ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; 63341502Swpaul media |= PHY_BMCR_SPEEDSEL; 63441502Swpaul media |= PHY_BMCR_DUPLEX; 63541502Swpaul printf("(full-duplex, 100Mbps)\n"); 63641502Swpaul } else if (advert & PHY_ANAR_100BTXHALF && 63741502Swpaul ability & PHY_ANAR_100BTXHALF) { 63841502Swpaul ifm->ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; 63941502Swpaul media |= PHY_BMCR_SPEEDSEL; 64041502Swpaul media &= ~PHY_BMCR_DUPLEX; 64141502Swpaul printf("(half-duplex, 100Mbps)\n"); 64241502Swpaul } else if (advert & PHY_ANAR_10BTFULL && 64341502Swpaul ability & PHY_ANAR_10BTFULL) { 64441502Swpaul ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; 64541502Swpaul media &= ~PHY_BMCR_SPEEDSEL; 64641502Swpaul media |= PHY_BMCR_DUPLEX; 64741502Swpaul printf("(full-duplex, 10Mbps)\n"); 64841502Swpaul } else { 64941502Swpaul ifm->ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; 65041502Swpaul media &= ~PHY_BMCR_SPEEDSEL; 65141502Swpaul media &= ~PHY_BMCR_DUPLEX; 65241502Swpaul printf("(half-duplex, 10Mbps)\n"); 65341502Swpaul } 65441502Swpaul 65541502Swpaul media &= ~PHY_BMCR_AUTONEGENBL; 65641502Swpaul 65741502Swpaul /* Set ASIC's duplex mode to match the PHY. */ 65841502Swpaul vr_setcfg(sc, media); 65941502Swpaul vr_phy_writereg(sc, PHY_BMCR, media); 66041502Swpaul } else { 66141502Swpaul if (verbose) 66241502Swpaul printf("no carrier\n"); 66341502Swpaul } 66441502Swpaul 66541502Swpaul vr_init(sc); 66641502Swpaul 66741502Swpaul if (sc->vr_tx_pend) { 66841502Swpaul sc->vr_autoneg = 0; 66941502Swpaul sc->vr_tx_pend = 0; 67041502Swpaul vr_start(ifp); 67141502Swpaul } 67241502Swpaul 67341502Swpaul return; 67441502Swpaul} 67541502Swpaul 67641502Swpaulstatic void vr_getmode_mii(sc) 67741502Swpaul struct vr_softc *sc; 67841502Swpaul{ 67941502Swpaul u_int16_t bmsr; 68041502Swpaul struct ifnet *ifp; 68141502Swpaul 68241502Swpaul ifp = &sc->arpcom.ac_if; 68341502Swpaul 68441502Swpaul bmsr = vr_phy_readreg(sc, PHY_BMSR); 68541502Swpaul if (bootverbose) 68641502Swpaul printf("vr%d: PHY status word: %x\n", sc->vr_unit, bmsr); 68741502Swpaul 68841502Swpaul /* fallback */ 68941502Swpaul sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_HDX; 69041502Swpaul 69141502Swpaul if (bmsr & PHY_BMSR_10BTHALF) { 69241502Swpaul if (bootverbose) 69341502Swpaul printf("vr%d: 10Mbps half-duplex mode supported\n", 69441502Swpaul sc->vr_unit); 69541502Swpaul ifmedia_add(&sc->ifmedia, 69641502Swpaul IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL); 69741502Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 69841502Swpaul } 69941502Swpaul 70041502Swpaul if (bmsr & PHY_BMSR_10BTFULL) { 70141502Swpaul if (bootverbose) 70241502Swpaul printf("vr%d: 10Mbps full-duplex mode supported\n", 70341502Swpaul sc->vr_unit); 70441502Swpaul ifmedia_add(&sc->ifmedia, 70541502Swpaul IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL); 70641502Swpaul sc->ifmedia.ifm_media = IFM_ETHER|IFM_10_T|IFM_FDX; 70741502Swpaul } 70841502Swpaul 70941502Swpaul if (bmsr & PHY_BMSR_100BTXHALF) { 71041502Swpaul if (bootverbose) 71141502Swpaul printf("vr%d: 100Mbps half-duplex mode supported\n", 71241502Swpaul sc->vr_unit); 71341502Swpaul ifp->if_baudrate = 100000000; 71441502Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_TX, 0, NULL); 71541502Swpaul ifmedia_add(&sc->ifmedia, 71641502Swpaul IFM_ETHER|IFM_100_TX|IFM_HDX, 0, NULL); 71741502Swpaul sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_HDX; 71841502Swpaul } 71941502Swpaul 72041502Swpaul if (bmsr & PHY_BMSR_100BTXFULL) { 72141502Swpaul if (bootverbose) 72241502Swpaul printf("vr%d: 100Mbps full-duplex mode supported\n", 72341502Swpaul sc->vr_unit); 72441502Swpaul ifp->if_baudrate = 100000000; 72541502Swpaul ifmedia_add(&sc->ifmedia, 72641502Swpaul IFM_ETHER|IFM_100_TX|IFM_FDX, 0, NULL); 72741502Swpaul sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_TX|IFM_FDX; 72841502Swpaul } 72941502Swpaul 73041502Swpaul /* Some also support 100BaseT4. */ 73141502Swpaul if (bmsr & PHY_BMSR_100BT4) { 73241502Swpaul if (bootverbose) 73341502Swpaul printf("vr%d: 100baseT4 mode supported\n", sc->vr_unit); 73441502Swpaul ifp->if_baudrate = 100000000; 73541502Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_T4, 0, NULL); 73641502Swpaul sc->ifmedia.ifm_media = IFM_ETHER|IFM_100_T4; 73741502Swpaul#ifdef FORCE_AUTONEG_TFOUR 73841502Swpaul if (bootverbose) 73941502Swpaul printf("vr%d: forcing on autoneg support for BT4\n", 74041502Swpaul sc->vr_unit); 74141502Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0 NULL): 74241502Swpaul sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO; 74341502Swpaul#endif 74441502Swpaul } 74541502Swpaul 74641502Swpaul if (bmsr & PHY_BMSR_CANAUTONEG) { 74741502Swpaul if (bootverbose) 74841502Swpaul printf("vr%d: autoneg supported\n", sc->vr_unit); 74941502Swpaul ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); 75041502Swpaul sc->ifmedia.ifm_media = IFM_ETHER|IFM_AUTO; 75141502Swpaul } 75241502Swpaul 75341502Swpaul return; 75441502Swpaul} 75541502Swpaul 75641502Swpaul/* 75741502Swpaul * Set speed and duplex mode. 75841502Swpaul */ 75941502Swpaulstatic void vr_setmode_mii(sc, media) 76041502Swpaul struct vr_softc *sc; 76141502Swpaul int media; 76241502Swpaul{ 76341502Swpaul u_int16_t bmcr; 76441502Swpaul struct ifnet *ifp; 76541502Swpaul 76641502Swpaul ifp = &sc->arpcom.ac_if; 76741502Swpaul 76841502Swpaul /* 76941502Swpaul * If an autoneg session is in progress, stop it. 77041502Swpaul */ 77141502Swpaul if (sc->vr_autoneg) { 77241502Swpaul printf("vr%d: canceling autoneg session\n", sc->vr_unit); 77341502Swpaul ifp->if_timer = sc->vr_autoneg = sc->vr_want_auto = 0; 77441502Swpaul bmcr = vr_phy_readreg(sc, PHY_BMCR); 77541502Swpaul bmcr &= ~PHY_BMCR_AUTONEGENBL; 77641502Swpaul vr_phy_writereg(sc, PHY_BMCR, bmcr); 77741502Swpaul } 77841502Swpaul 77941502Swpaul printf("vr%d: selecting MII, ", sc->vr_unit); 78041502Swpaul 78141502Swpaul bmcr = vr_phy_readreg(sc, PHY_BMCR); 78241502Swpaul 78341502Swpaul bmcr &= ~(PHY_BMCR_AUTONEGENBL|PHY_BMCR_SPEEDSEL| 78441502Swpaul PHY_BMCR_DUPLEX|PHY_BMCR_LOOPBK); 78541502Swpaul 78641502Swpaul if (IFM_SUBTYPE(media) == IFM_100_T4) { 78741502Swpaul printf("100Mbps/T4, half-duplex\n"); 78841502Swpaul bmcr |= PHY_BMCR_SPEEDSEL; 78941502Swpaul bmcr &= ~PHY_BMCR_DUPLEX; 79041502Swpaul } 79141502Swpaul 79241502Swpaul if (IFM_SUBTYPE(media) == IFM_100_TX) { 79341502Swpaul printf("100Mbps, "); 79441502Swpaul bmcr |= PHY_BMCR_SPEEDSEL; 79541502Swpaul } 79641502Swpaul 79741502Swpaul if (IFM_SUBTYPE(media) == IFM_10_T) { 79841502Swpaul printf("10Mbps, "); 79941502Swpaul bmcr &= ~PHY_BMCR_SPEEDSEL; 80041502Swpaul } 80141502Swpaul 80241502Swpaul if ((media & IFM_GMASK) == IFM_FDX) { 80341502Swpaul printf("full duplex\n"); 80441502Swpaul bmcr |= PHY_BMCR_DUPLEX; 80541502Swpaul } else { 80641502Swpaul printf("half duplex\n"); 80741502Swpaul bmcr &= ~PHY_BMCR_DUPLEX; 80841502Swpaul } 80941502Swpaul 81041502Swpaul vr_setcfg(sc, bmcr); 81141502Swpaul vr_phy_writereg(sc, PHY_BMCR, bmcr); 81241502Swpaul 81341502Swpaul return; 81441502Swpaul} 81541502Swpaul 81641502Swpaul/* 81741502Swpaul * In order to fiddle with the 81841502Swpaul * 'full-duplex' and '100Mbps' bits in the netconfig register, we 81941502Swpaul * first have to put the transmit and/or receive logic in the idle state. 82041502Swpaul */ 82141502Swpaulstatic void vr_setcfg(sc, bmcr) 82241502Swpaul struct vr_softc *sc; 82341502Swpaul u_int16_t bmcr; 82441502Swpaul{ 82541502Swpaul int restart = 0; 82641502Swpaul 82741502Swpaul if (CSR_READ_2(sc, VR_COMMAND) & (VR_CMD_TX_ON|VR_CMD_RX_ON)) { 82841502Swpaul restart = 1; 82941502Swpaul VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_TX_ON|VR_CMD_RX_ON)); 83041502Swpaul } 83141502Swpaul 83241502Swpaul if (bmcr & PHY_BMCR_DUPLEX) 83341502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); 83441502Swpaul else 83541502Swpaul VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); 83641502Swpaul 83741502Swpaul if (restart) 83841502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_RX_ON); 83941502Swpaul 84041502Swpaul return; 84141502Swpaul} 84241502Swpaul 84341502Swpaulstatic void vr_reset(sc) 84441502Swpaul struct vr_softc *sc; 84541502Swpaul{ 84641502Swpaul register int i; 84741502Swpaul 84841502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RESET); 84941502Swpaul 85041502Swpaul for (i = 0; i < VR_TIMEOUT; i++) { 85141502Swpaul DELAY(10); 85241502Swpaul if (!(CSR_READ_2(sc, VR_COMMAND) & VR_CMD_RESET)) 85341502Swpaul break; 85441502Swpaul } 85541502Swpaul if (i == VR_TIMEOUT) 85641502Swpaul printf("vr%d: reset never completed!\n", sc->vr_unit); 85741502Swpaul 85841502Swpaul /* Wait a little while for the chip to get its brains in order. */ 85941502Swpaul DELAY(1000); 86041502Swpaul 86141502Swpaul return; 86241502Swpaul} 86341502Swpaul 86441502Swpaul/* 86541502Swpaul * Probe for a VIA Rhine chip. Check the PCI vendor and device 86641502Swpaul * IDs against our list and return a device name if we find a match. 86741502Swpaul */ 86841771Sdillonstatic const char * 86941502Swpaulvr_probe(config_id, device_id) 87041502Swpaul pcici_t config_id; 87141502Swpaul pcidi_t device_id; 87241502Swpaul{ 87341502Swpaul struct vr_type *t; 87441502Swpaul 87541502Swpaul t = vr_devs; 87641502Swpaul 87741502Swpaul while(t->vr_name != NULL) { 87841502Swpaul if ((device_id & 0xFFFF) == t->vr_vid && 87941502Swpaul ((device_id >> 16) & 0xFFFF) == t->vr_did) { 88041502Swpaul return(t->vr_name); 88141502Swpaul } 88241502Swpaul t++; 88341502Swpaul } 88441502Swpaul 88541502Swpaul return(NULL); 88641502Swpaul} 88741502Swpaul 88841502Swpaul/* 88941502Swpaul * Attach the interface. Allocate softc structures, do ifmedia 89041502Swpaul * setup and ethernet/BPF attach. 89141502Swpaul */ 89241502Swpaulstatic void 89341502Swpaulvr_attach(config_id, unit) 89441502Swpaul pcici_t config_id; 89541502Swpaul int unit; 89641502Swpaul{ 89741502Swpaul int s, i; 89841502Swpaul#ifndef VR_USEIOSPACE 89941502Swpaul vm_offset_t pbase, vbase; 90041502Swpaul#endif 90141502Swpaul u_char eaddr[ETHER_ADDR_LEN]; 90241502Swpaul u_int32_t command; 90341502Swpaul struct vr_softc *sc; 90441502Swpaul struct ifnet *ifp; 90541502Swpaul int media = IFM_ETHER|IFM_100_TX|IFM_FDX; 90641502Swpaul unsigned int round; 90741502Swpaul caddr_t roundptr; 90841502Swpaul struct vr_type *p; 90941502Swpaul u_int16_t phy_vid, phy_did, phy_sts; 91041502Swpaul 91141502Swpaul s = splimp(); 91241502Swpaul 91341502Swpaul sc = malloc(sizeof(struct vr_softc), M_DEVBUF, M_NOWAIT); 91441502Swpaul if (sc == NULL) { 91541502Swpaul printf("vr%d: no memory for softc struct!\n", unit); 91641502Swpaul return; 91741502Swpaul } 91841502Swpaul bzero(sc, sizeof(struct vr_softc)); 91941502Swpaul 92041502Swpaul /* 92141502Swpaul * Handle power management nonsense. 92241502Swpaul */ 92341502Swpaul 92441502Swpaul command = pci_conf_read(config_id, VR_PCI_CAPID) & 0x000000FF; 92541502Swpaul if (command == 0x01) { 92641502Swpaul 92741502Swpaul command = pci_conf_read(config_id, VR_PCI_PWRMGMTCTRL); 92841502Swpaul if (command & VR_PSTATE_MASK) { 92941502Swpaul u_int32_t iobase, membase, irq; 93041502Swpaul 93141502Swpaul /* Save important PCI config data. */ 93241502Swpaul iobase = pci_conf_read(config_id, VR_PCI_LOIO); 93341502Swpaul membase = pci_conf_read(config_id, VR_PCI_LOMEM); 93441502Swpaul irq = pci_conf_read(config_id, VR_PCI_INTLINE); 93541502Swpaul 93641502Swpaul /* Reset the power state. */ 93741502Swpaul printf("vr%d: chip is in D%d power mode " 93841502Swpaul "-- setting to D0\n", unit, command & VR_PSTATE_MASK); 93941502Swpaul command &= 0xFFFFFFFC; 94041502Swpaul pci_conf_write(config_id, VR_PCI_PWRMGMTCTRL, command); 94141502Swpaul 94241502Swpaul /* Restore PCI config data. */ 94341502Swpaul pci_conf_write(config_id, VR_PCI_LOIO, iobase); 94441502Swpaul pci_conf_write(config_id, VR_PCI_LOMEM, membase); 94541502Swpaul pci_conf_write(config_id, VR_PCI_INTLINE, irq); 94641502Swpaul } 94741502Swpaul } 94841502Swpaul 94941502Swpaul /* 95041502Swpaul * Map control/status registers. 95141502Swpaul */ 95241502Swpaul command = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG); 95341502Swpaul command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 95441502Swpaul pci_conf_write(config_id, PCI_COMMAND_STATUS_REG, command); 95541502Swpaul command = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG); 95641502Swpaul 95741502Swpaul#ifdef VR_USEIOSPACE 95841502Swpaul if (!(command & PCIM_CMD_PORTEN)) { 95941502Swpaul printf("vr%d: failed to enable I/O ports!\n", unit); 96041502Swpaul free(sc, M_DEVBUF); 96141502Swpaul goto fail; 96241502Swpaul } 96341502Swpaul 96441502Swpaul if (!pci_map_port(config_id, VR_PCI_LOIO, 96541502Swpaul (u_int16_t *)(&sc->vr_bhandle))) { 96641502Swpaul printf ("vr%d: couldn't map ports\n", unit); 96741502Swpaul goto fail; 96841502Swpaul } 96941502Swpaul sc->vr_btag = I386_BUS_SPACE_IO; 97041502Swpaul#else 97141502Swpaul if (!(command & PCIM_CMD_MEMEN)) { 97241502Swpaul printf("vr%d: failed to enable memory mapping!\n", unit); 97341502Swpaul goto fail; 97441502Swpaul } 97541502Swpaul 97641502Swpaul if (!pci_map_mem(config_id, VR_PCI_LOMEM, &vbase, &pbase)) { 97741502Swpaul printf ("vr%d: couldn't map memory\n", unit); 97841502Swpaul goto fail; 97941502Swpaul } 98041502Swpaul 98141502Swpaul sc->vr_bhandle = vbase; 98241502Swpaul sc->vr_btag = I386_BUS_SPACE_MEM; 98341502Swpaul#endif 98441502Swpaul 98541502Swpaul /* Allocate interrupt */ 98641502Swpaul if (!pci_map_int(config_id, vr_intr, sc, &net_imask)) { 98741502Swpaul printf("vr%d: couldn't map interrupt\n", unit); 98841502Swpaul goto fail; 98941502Swpaul } 99041502Swpaul 99141502Swpaul /* Reset the adapter. */ 99241502Swpaul vr_reset(sc); 99341502Swpaul 99441502Swpaul /* 99541502Swpaul * Get station address. The way the Rhine chips work, 99641502Swpaul * you're not allowed to directly access the EEPROM once 99741502Swpaul * they've been programmed a special way. Consequently, 99841502Swpaul * we need to read the node address from the PAR0 and PAR1 99941502Swpaul * registers. 100041502Swpaul */ 100141502Swpaul VR_SETBIT(sc, VR_EECSR, VR_EECSR_LOAD); 100241502Swpaul DELAY(200); 100341502Swpaul for (i = 0; i < ETHER_ADDR_LEN; i++) 100441502Swpaul eaddr[i] = CSR_READ_1(sc, VR_PAR0 + i); 100541502Swpaul 100641502Swpaul /* 100741502Swpaul * A Rhine chip was detected. Inform the world. 100841502Swpaul */ 100941502Swpaul printf("vr%d: Ethernet address: %6D\n", unit, eaddr, ":"); 101041502Swpaul 101141502Swpaul sc->vr_unit = unit; 101241502Swpaul bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 101341502Swpaul 101441502Swpaul sc->vr_ldata_ptr = malloc(sizeof(struct vr_list_data) + 8, 101541502Swpaul M_DEVBUF, M_NOWAIT); 101641502Swpaul if (sc->vr_ldata_ptr == NULL) { 101741502Swpaul free(sc, M_DEVBUF); 101841502Swpaul printf("vr%d: no memory for list buffers!\n", unit); 101941502Swpaul return; 102041502Swpaul } 102141502Swpaul 102241502Swpaul sc->vr_ldata = (struct vr_list_data *)sc->vr_ldata_ptr; 102341502Swpaul round = (unsigned int)sc->vr_ldata_ptr & 0xF; 102441502Swpaul roundptr = sc->vr_ldata_ptr; 102541502Swpaul for (i = 0; i < 8; i++) { 102641502Swpaul if (round % 8) { 102741502Swpaul round++; 102841502Swpaul roundptr++; 102941502Swpaul } else 103041502Swpaul break; 103141502Swpaul } 103241502Swpaul sc->vr_ldata = (struct vr_list_data *)roundptr; 103341502Swpaul bzero(sc->vr_ldata, sizeof(struct vr_list_data)); 103441502Swpaul 103541502Swpaul ifp = &sc->arpcom.ac_if; 103641502Swpaul ifp->if_softc = sc; 103741502Swpaul ifp->if_unit = unit; 103841502Swpaul ifp->if_name = "vr"; 103941502Swpaul ifp->if_mtu = ETHERMTU; 104041502Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 104141502Swpaul ifp->if_ioctl = vr_ioctl; 104241502Swpaul ifp->if_output = ether_output; 104341502Swpaul ifp->if_start = vr_start; 104441502Swpaul ifp->if_watchdog = vr_watchdog; 104541502Swpaul ifp->if_init = vr_init; 104641502Swpaul ifp->if_baudrate = 10000000; 104743515Swpaul ifp->if_snd.ifq_maxlen = VR_TX_LIST_CNT - 1; 104841502Swpaul 104941502Swpaul if (bootverbose) 105041502Swpaul printf("vr%d: probing for a PHY\n", sc->vr_unit); 105141502Swpaul for (i = VR_PHYADDR_MIN; i < VR_PHYADDR_MAX + 1; i++) { 105241502Swpaul if (bootverbose) 105341502Swpaul printf("vr%d: checking address: %d\n", 105441502Swpaul sc->vr_unit, i); 105541502Swpaul sc->vr_phy_addr = i; 105641502Swpaul vr_phy_writereg(sc, PHY_BMCR, PHY_BMCR_RESET); 105741502Swpaul DELAY(500); 105841502Swpaul while(vr_phy_readreg(sc, PHY_BMCR) 105941502Swpaul & PHY_BMCR_RESET); 106041502Swpaul if ((phy_sts = vr_phy_readreg(sc, PHY_BMSR))) 106141502Swpaul break; 106241502Swpaul } 106341502Swpaul if (phy_sts) { 106441502Swpaul phy_vid = vr_phy_readreg(sc, PHY_VENID); 106541502Swpaul phy_did = vr_phy_readreg(sc, PHY_DEVID); 106641502Swpaul if (bootverbose) 106741502Swpaul printf("vr%d: found PHY at address %d, ", 106841502Swpaul sc->vr_unit, sc->vr_phy_addr); 106941502Swpaul if (bootverbose) 107041502Swpaul printf("vendor id: %x device id: %x\n", 107141502Swpaul phy_vid, phy_did); 107241502Swpaul p = vr_phys; 107341502Swpaul while(p->vr_vid) { 107441502Swpaul if (phy_vid == p->vr_vid && 107541502Swpaul (phy_did | 0x000F) == p->vr_did) { 107641502Swpaul sc->vr_pinfo = p; 107741502Swpaul break; 107841502Swpaul } 107941502Swpaul p++; 108041502Swpaul } 108141502Swpaul if (sc->vr_pinfo == NULL) 108241502Swpaul sc->vr_pinfo = &vr_phys[PHY_UNKNOWN]; 108341502Swpaul if (bootverbose) 108441502Swpaul printf("vr%d: PHY type: %s\n", 108541502Swpaul sc->vr_unit, sc->vr_pinfo->vr_name); 108641502Swpaul } else { 108741502Swpaul printf("vr%d: MII without any phy!\n", sc->vr_unit); 108841502Swpaul goto fail; 108941502Swpaul } 109041502Swpaul 109141502Swpaul /* 109241502Swpaul * Do ifmedia setup. 109341502Swpaul */ 109441502Swpaul ifmedia_init(&sc->ifmedia, 0, vr_ifmedia_upd, vr_ifmedia_sts); 109541502Swpaul 109641502Swpaul vr_getmode_mii(sc); 109741502Swpaul vr_autoneg_mii(sc, VR_FLAG_FORCEDELAY, 1); 109841502Swpaul media = sc->ifmedia.ifm_media; 109941502Swpaul vr_stop(sc); 110041502Swpaul 110141502Swpaul ifmedia_set(&sc->ifmedia, media); 110241502Swpaul 110341502Swpaul /* 110441502Swpaul * Call MI attach routines. 110541502Swpaul */ 110641502Swpaul if_attach(ifp); 110741502Swpaul ether_ifattach(ifp); 110841502Swpaul 110941502Swpaul#if NBPFILTER > 0 111041502Swpaul bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 111141502Swpaul#endif 111241502Swpaul 111341502Swpaul at_shutdown(vr_shutdown, sc, SHUTDOWN_POST_SYNC); 111441502Swpaul 111541502Swpaulfail: 111641502Swpaul splx(s); 111741502Swpaul return; 111841502Swpaul} 111941502Swpaul 112041502Swpaul/* 112141502Swpaul * Initialize the transmit descriptors. 112241502Swpaul */ 112341502Swpaulstatic int vr_list_tx_init(sc) 112441502Swpaul struct vr_softc *sc; 112541502Swpaul{ 112641502Swpaul struct vr_chain_data *cd; 112741502Swpaul struct vr_list_data *ld; 112841502Swpaul int i; 112941502Swpaul 113041502Swpaul cd = &sc->vr_cdata; 113141502Swpaul ld = sc->vr_ldata; 113241502Swpaul for (i = 0; i < VR_TX_LIST_CNT; i++) { 113341502Swpaul cd->vr_tx_chain[i].vr_ptr = &ld->vr_tx_list[i]; 113441502Swpaul if (i == (VR_TX_LIST_CNT - 1)) 113541502Swpaul cd->vr_tx_chain[i].vr_nextdesc = 113641502Swpaul &cd->vr_tx_chain[0]; 113741502Swpaul else 113841502Swpaul cd->vr_tx_chain[i].vr_nextdesc = 113941502Swpaul &cd->vr_tx_chain[i + 1]; 114041502Swpaul } 114141502Swpaul 114241502Swpaul cd->vr_tx_free = &cd->vr_tx_chain[0]; 114341502Swpaul cd->vr_tx_tail = cd->vr_tx_head = NULL; 114441502Swpaul 114541502Swpaul return(0); 114641502Swpaul} 114741502Swpaul 114841502Swpaul 114941502Swpaul/* 115041502Swpaul * Initialize the RX descriptors and allocate mbufs for them. Note that 115141502Swpaul * we arrange the descriptors in a closed ring, so that the last descriptor 115241502Swpaul * points back to the first. 115341502Swpaul */ 115441502Swpaulstatic int vr_list_rx_init(sc) 115541502Swpaul struct vr_softc *sc; 115641502Swpaul{ 115741502Swpaul struct vr_chain_data *cd; 115841502Swpaul struct vr_list_data *ld; 115941502Swpaul int i; 116041502Swpaul 116141502Swpaul cd = &sc->vr_cdata; 116241502Swpaul ld = sc->vr_ldata; 116341502Swpaul 116441502Swpaul for (i = 0; i < VR_RX_LIST_CNT; i++) { 116541502Swpaul cd->vr_rx_chain[i].vr_ptr = 116641502Swpaul (struct vr_desc *)&ld->vr_rx_list[i]; 116741502Swpaul if (vr_newbuf(sc, &cd->vr_rx_chain[i]) == ENOBUFS) 116841502Swpaul return(ENOBUFS); 116941502Swpaul if (i == (VR_RX_LIST_CNT - 1)) { 117041502Swpaul cd->vr_rx_chain[i].vr_nextdesc = 117141502Swpaul &cd->vr_rx_chain[0]; 117241502Swpaul ld->vr_rx_list[i].vr_next = 117341502Swpaul vtophys(&ld->vr_rx_list[0]); 117441502Swpaul } else { 117541502Swpaul cd->vr_rx_chain[i].vr_nextdesc = 117641502Swpaul &cd->vr_rx_chain[i + 1]; 117741502Swpaul ld->vr_rx_list[i].vr_next = 117841502Swpaul vtophys(&ld->vr_rx_list[i + 1]); 117941502Swpaul } 118041502Swpaul } 118141502Swpaul 118241502Swpaul cd->vr_rx_head = &cd->vr_rx_chain[0]; 118341502Swpaul 118441502Swpaul return(0); 118541502Swpaul} 118641502Swpaul 118741502Swpaul/* 118841502Swpaul * Initialize an RX descriptor and attach an MBUF cluster. 118941502Swpaul * Note: the length fields are only 11 bits wide, which means the 119041502Swpaul * largest size we can specify is 2047. This is important because 119141502Swpaul * MCLBYTES is 2048, so we have to subtract one otherwise we'll 119241502Swpaul * overflow the field and make a mess. 119341502Swpaul */ 119441502Swpaulstatic int vr_newbuf(sc, c) 119541502Swpaul struct vr_softc *sc; 119641502Swpaul struct vr_chain_onefrag *c; 119741502Swpaul{ 119841502Swpaul struct mbuf *m_new = NULL; 119941502Swpaul 120041502Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 120141502Swpaul if (m_new == NULL) { 120241502Swpaul printf("vr%d: no memory for rx list -- packet dropped!\n", 120341502Swpaul sc->vr_unit); 120441502Swpaul return(ENOBUFS); 120541502Swpaul } 120641502Swpaul 120741502Swpaul MCLGET(m_new, M_DONTWAIT); 120841502Swpaul if (!(m_new->m_flags & M_EXT)) { 120941502Swpaul printf("vr%d: no memory for rx list -- packet dropped!\n", 121041502Swpaul sc->vr_unit); 121141502Swpaul m_freem(m_new); 121241502Swpaul return(ENOBUFS); 121341502Swpaul } 121441502Swpaul 121541502Swpaul c->vr_mbuf = m_new; 121641502Swpaul c->vr_ptr->vr_status = VR_RXSTAT; 121741502Swpaul c->vr_ptr->vr_data = vtophys(mtod(m_new, caddr_t)); 121842491Swpaul c->vr_ptr->vr_ctl = VR_RXCTL | VR_RXLEN; 121941502Swpaul 122041502Swpaul return(0); 122141502Swpaul} 122241502Swpaul 122341502Swpaul/* 122441502Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to 122541502Swpaul * the higher level protocols. 122641502Swpaul */ 122741502Swpaulstatic void vr_rxeof(sc) 122841502Swpaul struct vr_softc *sc; 122941502Swpaul{ 123041502Swpaul struct ether_header *eh; 123141502Swpaul struct mbuf *m; 123241502Swpaul struct ifnet *ifp; 123341502Swpaul struct vr_chain_onefrag *cur_rx; 123441502Swpaul int total_len = 0; 123541502Swpaul u_int32_t rxstat; 123641502Swpaul 123741502Swpaul ifp = &sc->arpcom.ac_if; 123841502Swpaul 123941502Swpaul while(!((rxstat = sc->vr_cdata.vr_rx_head->vr_ptr->vr_status) & 124041502Swpaul VR_RXSTAT_OWN)) { 124141502Swpaul cur_rx = sc->vr_cdata.vr_rx_head; 124241502Swpaul sc->vr_cdata.vr_rx_head = cur_rx->vr_nextdesc; 124341502Swpaul 124441502Swpaul /* 124541502Swpaul * If an error occurs, update stats, clear the 124641502Swpaul * status word and leave the mbuf cluster in place: 124741502Swpaul * it should simply get re-used next time this descriptor 124841502Swpaul * comes up in the ring. 124941502Swpaul */ 125041502Swpaul if (rxstat & VR_RXSTAT_RXERR) { 125141502Swpaul ifp->if_ierrors++; 125241502Swpaul printf("vr%d: rx error: ", sc->vr_unit); 125341502Swpaul switch(rxstat & 0x000000FF) { 125441502Swpaul case VR_RXSTAT_CRCERR: 125541502Swpaul printf("crc error\n"); 125641502Swpaul break; 125741502Swpaul case VR_RXSTAT_FRAMEALIGNERR: 125841502Swpaul printf("frame alignment error\n"); 125941502Swpaul break; 126041502Swpaul case VR_RXSTAT_FIFOOFLOW: 126141502Swpaul printf("FIFO overflow\n"); 126241502Swpaul break; 126341502Swpaul case VR_RXSTAT_GIANT: 126441502Swpaul printf("received giant packet\n"); 126541502Swpaul break; 126641502Swpaul case VR_RXSTAT_RUNT: 126741502Swpaul printf("received runt packet\n"); 126841502Swpaul break; 126941502Swpaul case VR_RXSTAT_BUSERR: 127041502Swpaul printf("system bus error\n"); 127141502Swpaul break; 127241502Swpaul case VR_RXSTAT_BUFFERR: 127341502Swpaul printf("rx buffer error\n"); 127441502Swpaul break; 127541502Swpaul default: 127641502Swpaul printf("unknown rx error\n"); 127741502Swpaul break; 127841502Swpaul } 127941502Swpaul cur_rx->vr_ptr->vr_status = VR_RXSTAT; 128042491Swpaul cur_rx->vr_ptr->vr_ctl = VR_RXCTL|VR_RXLEN; 128141502Swpaul continue; 128241502Swpaul } 128341502Swpaul 128441502Swpaul /* No errors; receive the packet. */ 128541502Swpaul m = cur_rx->vr_mbuf; 128641502Swpaul total_len = VR_RXBYTES(cur_rx->vr_ptr->vr_status); 128741502Swpaul 128841502Swpaul /* 128942048Swpaul * XXX The VIA Rhine chip includes the CRC with every 129042048Swpaul * received frame, and there's no way to turn this 129142048Swpaul * behavior off (at least, I can't find anything in 129242048Swpaul * the manual that explains how to do it) so we have 129342048Swpaul * to trim off the CRC manually. 129442048Swpaul */ 129542048Swpaul total_len -= ETHER_CRC_LEN; 129642048Swpaul 129742048Swpaul /* 129841502Swpaul * Try to conjure up a new mbuf cluster. If that 129941502Swpaul * fails, it means we have an out of memory condition and 130041502Swpaul * should leave the buffer in place and continue. This will 130141502Swpaul * result in a lost packet, but there's little else we 130241502Swpaul * can do in this situation. 130341502Swpaul */ 130441502Swpaul if (vr_newbuf(sc, cur_rx) == ENOBUFS) { 130541502Swpaul ifp->if_ierrors++; 130642260Swpaul cur_rx->vr_ptr->vr_status = VR_RXSTAT; 130742491Swpaul cur_rx->vr_ptr->vr_ctl = VR_RXCTL|VR_RXLEN; 130841502Swpaul continue; 130941502Swpaul } 131041502Swpaul 131141502Swpaul ifp->if_ipackets++; 131241502Swpaul eh = mtod(m, struct ether_header *); 131341502Swpaul m->m_pkthdr.rcvif = ifp; 131441502Swpaul m->m_pkthdr.len = m->m_len = total_len; 131541502Swpaul#if NBPFILTER > 0 131641502Swpaul /* 131741502Swpaul * Handle BPF listeners. Let the BPF user see the packet, but 131841502Swpaul * don't pass it up to the ether_input() layer unless it's 131941502Swpaul * a broadcast packet, multicast packet, matches our ethernet 132041502Swpaul * address or the interface is in promiscuous mode. 132141502Swpaul */ 132241502Swpaul if (ifp->if_bpf) { 132341502Swpaul bpf_mtap(ifp, m); 132441502Swpaul if (ifp->if_flags & IFF_PROMISC && 132541502Swpaul (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, 132641502Swpaul ETHER_ADDR_LEN) && 132741502Swpaul (eh->ether_dhost[0] & 1) == 0)) { 132841502Swpaul m_freem(m); 132941502Swpaul continue; 133041502Swpaul } 133141502Swpaul } 133241502Swpaul#endif 133341502Swpaul /* Remove header from mbuf and pass it on. */ 133441502Swpaul m_adj(m, sizeof(struct ether_header)); 133541502Swpaul ether_input(ifp, eh, m); 133641502Swpaul } 133741502Swpaul 133841502Swpaul return; 133941502Swpaul} 134041502Swpaul 134141502Swpaulvoid vr_rxeoc(sc) 134241502Swpaul struct vr_softc *sc; 134341502Swpaul{ 134441502Swpaul 134541502Swpaul vr_rxeof(sc); 134641502Swpaul VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); 134741502Swpaul CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); 134841502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); 134941502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_GO); 135041502Swpaul 135141502Swpaul return; 135241502Swpaul} 135341502Swpaul 135441502Swpaul/* 135541502Swpaul * A frame was downloaded to the chip. It's safe for us to clean up 135641502Swpaul * the list buffers. 135741502Swpaul */ 135841502Swpaul 135941502Swpaulstatic void vr_txeof(sc) 136041502Swpaul struct vr_softc *sc; 136141502Swpaul{ 136241502Swpaul struct vr_chain *cur_tx; 136341502Swpaul struct ifnet *ifp; 136441502Swpaul register struct mbuf *n; 136541502Swpaul 136641502Swpaul ifp = &sc->arpcom.ac_if; 136741502Swpaul 136841502Swpaul /* Clear the timeout timer. */ 136941502Swpaul ifp->if_timer = 0; 137041502Swpaul 137141502Swpaul /* Sanity check. */ 137241502Swpaul if (sc->vr_cdata.vr_tx_head == NULL) 137341502Swpaul return; 137441502Swpaul 137541502Swpaul /* 137641502Swpaul * Go through our tx list and free mbufs for those 137741502Swpaul * frames that have been transmitted. 137841502Swpaul */ 137941502Swpaul while(sc->vr_cdata.vr_tx_head->vr_mbuf != NULL) { 138041502Swpaul u_int32_t txstat; 138141502Swpaul 138241502Swpaul cur_tx = sc->vr_cdata.vr_tx_head; 138341502Swpaul txstat = cur_tx->vr_ptr->vr_status; 138441502Swpaul 138542491Swpaul if (txstat & VR_TXSTAT_OWN) 138641502Swpaul break; 138741502Swpaul 138841502Swpaul if (txstat & VR_TXSTAT_ERRSUM) { 138941502Swpaul ifp->if_oerrors++; 139041502Swpaul if (txstat & VR_TXSTAT_DEFER) 139141502Swpaul ifp->if_collisions++; 139241502Swpaul if (txstat & VR_TXSTAT_LATECOLL) 139341502Swpaul ifp->if_collisions++; 139441502Swpaul } 139541502Swpaul 139641502Swpaul ifp->if_collisions +=(txstat & VR_TXSTAT_COLLCNT) >> 3; 139741502Swpaul 139841502Swpaul ifp->if_opackets++; 139941502Swpaul MFREE(cur_tx->vr_mbuf, n); 140041502Swpaul cur_tx->vr_mbuf = NULL; 140141502Swpaul 140241502Swpaul if (sc->vr_cdata.vr_tx_head == sc->vr_cdata.vr_tx_tail) { 140341502Swpaul sc->vr_cdata.vr_tx_head = NULL; 140441502Swpaul sc->vr_cdata.vr_tx_tail = NULL; 140541502Swpaul break; 140641502Swpaul } 140741502Swpaul 140841502Swpaul sc->vr_cdata.vr_tx_head = cur_tx->vr_nextdesc; 140941502Swpaul } 141041502Swpaul 141141502Swpaul return; 141241502Swpaul} 141341502Swpaul 141441502Swpaul/* 141541502Swpaul * TX 'end of channel' interrupt handler. 141641502Swpaul */ 141741502Swpaulstatic void vr_txeoc(sc) 141841502Swpaul struct vr_softc *sc; 141941502Swpaul{ 142041502Swpaul struct ifnet *ifp; 142141502Swpaul 142241502Swpaul ifp = &sc->arpcom.ac_if; 142341502Swpaul 142441502Swpaul ifp->if_timer = 0; 142541502Swpaul 142641502Swpaul if (sc->vr_cdata.vr_tx_head == NULL) { 142741502Swpaul ifp->if_flags &= ~IFF_OACTIVE; 142841502Swpaul sc->vr_cdata.vr_tx_tail = NULL; 142941502Swpaul if (sc->vr_want_auto) 143041502Swpaul vr_autoneg_mii(sc, VR_FLAG_SCHEDDELAY, 1); 143141502Swpaul } 143241502Swpaul 143341502Swpaul return; 143441502Swpaul} 143541502Swpaul 143641502Swpaulstatic void vr_intr(arg) 143741502Swpaul void *arg; 143841502Swpaul{ 143941502Swpaul struct vr_softc *sc; 144041502Swpaul struct ifnet *ifp; 144141502Swpaul u_int16_t status; 144241502Swpaul 144341502Swpaul sc = arg; 144441502Swpaul ifp = &sc->arpcom.ac_if; 144541502Swpaul 144641502Swpaul /* Supress unwanted interrupts. */ 144741502Swpaul if (!(ifp->if_flags & IFF_UP)) { 144841502Swpaul vr_stop(sc); 144941502Swpaul return; 145041502Swpaul } 145141502Swpaul 145241502Swpaul /* Disable interrupts. */ 145341502Swpaul CSR_WRITE_2(sc, VR_IMR, 0x0000); 145441502Swpaul 145541502Swpaul for (;;) { 145641502Swpaul 145741502Swpaul status = CSR_READ_2(sc, VR_ISR); 145841502Swpaul if (status) 145941502Swpaul CSR_WRITE_2(sc, VR_ISR, status); 146041502Swpaul 146141502Swpaul if ((status & VR_INTRS) == 0) 146241502Swpaul break; 146341502Swpaul 146441502Swpaul if (status & VR_ISR_RX_OK) 146541502Swpaul vr_rxeof(sc); 146641502Swpaul 146741502Swpaul if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) || 146841502Swpaul (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW) || 146941502Swpaul (status & VR_ISR_RX_DROPPED)) { 147041502Swpaul vr_rxeof(sc); 147141502Swpaul vr_rxeoc(sc); 147241502Swpaul } 147341502Swpaul 147441502Swpaul if (status & VR_ISR_TX_OK) { 147541502Swpaul vr_txeof(sc); 147641502Swpaul vr_txeoc(sc); 147741502Swpaul } 147841502Swpaul 147941502Swpaul if ((status & VR_ISR_TX_UNDERRUN)||(status & VR_ISR_TX_ABRT)){ 148041502Swpaul ifp->if_oerrors++; 148141502Swpaul vr_txeof(sc); 148241502Swpaul if (sc->vr_cdata.vr_tx_head != NULL) { 148341502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON); 148441502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO); 148541502Swpaul } 148641502Swpaul } 148741502Swpaul 148841502Swpaul if (status & VR_ISR_BUSERR) { 148941502Swpaul vr_reset(sc); 149041502Swpaul vr_init(sc); 149141502Swpaul } 149241502Swpaul } 149341502Swpaul 149441502Swpaul /* Re-enable interrupts. */ 149541502Swpaul CSR_WRITE_2(sc, VR_IMR, VR_INTRS); 149641502Swpaul 149741502Swpaul if (ifp->if_snd.ifq_head != NULL) { 149841502Swpaul vr_start(ifp); 149941502Swpaul } 150041502Swpaul 150141502Swpaul return; 150241502Swpaul} 150341502Swpaul 150441502Swpaul/* 150541502Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 150641502Swpaul * pointers to the fragment pointers. 150741502Swpaul */ 150841502Swpaulstatic int vr_encap(sc, c, m_head) 150941502Swpaul struct vr_softc *sc; 151041502Swpaul struct vr_chain *c; 151141502Swpaul struct mbuf *m_head; 151241502Swpaul{ 151341502Swpaul int frag = 0; 151441502Swpaul struct vr_desc *f = NULL; 151541502Swpaul int total_len; 151641502Swpaul struct mbuf *m; 151741502Swpaul 151841502Swpaul m = m_head; 151941502Swpaul total_len = 0; 152041502Swpaul 152141502Swpaul /* 152241502Swpaul * The VIA Rhine wants packet buffers to be longword 152341502Swpaul * aligned, but very often our mbufs aren't. Rather than 152441502Swpaul * waste time trying to decide when to copy and when not 152541502Swpaul * to copy, just do it all the time. 152641502Swpaul */ 152741502Swpaul if (m != NULL) { 152841502Swpaul struct mbuf *m_new = NULL; 152941502Swpaul 153041502Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 153141502Swpaul if (m_new == NULL) { 153241502Swpaul printf("vr%d: no memory for tx list", sc->vr_unit); 153341502Swpaul return(1); 153441502Swpaul } 153541502Swpaul if (m_head->m_pkthdr.len > MHLEN) { 153641502Swpaul MCLGET(m_new, M_DONTWAIT); 153741502Swpaul if (!(m_new->m_flags & M_EXT)) { 153841502Swpaul m_freem(m_new); 153941502Swpaul printf("vr%d: no memory for tx list", 154041502Swpaul sc->vr_unit); 154141502Swpaul return(1); 154241502Swpaul } 154341502Swpaul } 154441502Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 154541502Swpaul mtod(m_new, caddr_t)); 154641502Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 154741502Swpaul m_freem(m_head); 154841502Swpaul m_head = m_new; 154941502Swpaul /* 155041502Swpaul * The Rhine chip doesn't auto-pad, so we have to make 155141502Swpaul * sure to pad short frames out to the minimum frame length 155241502Swpaul * ourselves. 155341502Swpaul */ 155441502Swpaul if (m_head->m_len < VR_MIN_FRAMELEN) { 155541502Swpaul m_new->m_pkthdr.len += VR_MIN_FRAMELEN - m_new->m_len; 155641502Swpaul m_new->m_len = m_new->m_pkthdr.len; 155741502Swpaul } 155841502Swpaul f = c->vr_ptr; 155941502Swpaul f->vr_data = vtophys(mtod(m_new, caddr_t)); 156041502Swpaul f->vr_ctl = total_len = m_new->m_len; 156141502Swpaul f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG; 156241502Swpaul f->vr_status = 0; 156341502Swpaul frag = 1; 156441502Swpaul } 156541502Swpaul 156641502Swpaul c->vr_mbuf = m_head; 156742491Swpaul c->vr_ptr->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT; 156841502Swpaul c->vr_ptr->vr_next = vtophys(c->vr_nextdesc->vr_ptr); 156941502Swpaul 157041502Swpaul return(0); 157141502Swpaul} 157241502Swpaul 157341502Swpaul/* 157441502Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 157541502Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 157641502Swpaul * copy of the pointers since the transmit list fragment pointers are 157741502Swpaul * physical addresses. 157841502Swpaul */ 157941502Swpaul 158041502Swpaulstatic void vr_start(ifp) 158141502Swpaul struct ifnet *ifp; 158241502Swpaul{ 158341502Swpaul struct vr_softc *sc; 158441502Swpaul struct mbuf *m_head = NULL; 158541502Swpaul struct vr_chain *cur_tx = NULL, *start_tx; 158641502Swpaul 158741502Swpaul sc = ifp->if_softc; 158841502Swpaul 158941502Swpaul if (sc->vr_autoneg) { 159041502Swpaul sc->vr_tx_pend = 1; 159141502Swpaul return; 159241502Swpaul } 159341502Swpaul 159441502Swpaul /* 159541502Swpaul * Check for an available queue slot. If there are none, 159641502Swpaul * punt. 159741502Swpaul */ 159841502Swpaul if (sc->vr_cdata.vr_tx_free->vr_mbuf != NULL) { 159941502Swpaul ifp->if_flags |= IFF_OACTIVE; 160041502Swpaul return; 160141502Swpaul } 160241502Swpaul 160341502Swpaul start_tx = sc->vr_cdata.vr_tx_free; 160441502Swpaul 160541502Swpaul while(sc->vr_cdata.vr_tx_free->vr_mbuf == NULL) { 160641502Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 160741502Swpaul if (m_head == NULL) 160841502Swpaul break; 160941502Swpaul 161041502Swpaul /* Pick a descriptor off the free list. */ 161141502Swpaul cur_tx = sc->vr_cdata.vr_tx_free; 161241502Swpaul sc->vr_cdata.vr_tx_free = cur_tx->vr_nextdesc; 161341502Swpaul 161441502Swpaul /* Pack the data into the descriptor. */ 161541502Swpaul vr_encap(sc, cur_tx, m_head); 161641502Swpaul 161741502Swpaul if (cur_tx != start_tx) 161841502Swpaul VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; 161941502Swpaul 162041502Swpaul#if NBPFILTER > 0 162141502Swpaul /* 162241502Swpaul * If there's a BPF listener, bounce a copy of this frame 162341502Swpaul * to him. 162441502Swpaul */ 162541502Swpaul if (ifp->if_bpf) 162641502Swpaul bpf_mtap(ifp, cur_tx->vr_mbuf); 162741502Swpaul#endif 162842491Swpaul VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; 162942491Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_TX_GO); 163041502Swpaul } 163141502Swpaul 163241502Swpaul /* 163341526Swpaul * If there are no frames queued, bail. 163441526Swpaul */ 163541526Swpaul if (cur_tx == NULL) 163641526Swpaul return; 163741526Swpaul 163841502Swpaul sc->vr_cdata.vr_tx_tail = cur_tx; 163941502Swpaul 164042491Swpaul if (sc->vr_cdata.vr_tx_head == NULL) 164141502Swpaul sc->vr_cdata.vr_tx_head = start_tx; 164241502Swpaul 164341502Swpaul /* 164441502Swpaul * Set a timeout in case the chip goes out to lunch. 164541502Swpaul */ 164641502Swpaul ifp->if_timer = 5; 164741502Swpaul 164841502Swpaul return; 164941502Swpaul} 165041502Swpaul 165141502Swpaulstatic void vr_init(xsc) 165241502Swpaul void *xsc; 165341502Swpaul{ 165441502Swpaul struct vr_softc *sc = xsc; 165541502Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 165641502Swpaul u_int16_t phy_bmcr = 0; 165741502Swpaul int s; 165841502Swpaul 165941502Swpaul if (sc->vr_autoneg) 166041502Swpaul return; 166141502Swpaul 166241502Swpaul s = splimp(); 166341502Swpaul 166441502Swpaul if (sc->vr_pinfo != NULL) 166541502Swpaul phy_bmcr = vr_phy_readreg(sc, PHY_BMCR); 166641502Swpaul 166741502Swpaul /* 166841502Swpaul * Cancel pending I/O and free all RX/TX buffers. 166941502Swpaul */ 167041502Swpaul vr_stop(sc); 167141502Swpaul vr_reset(sc); 167241502Swpaul 167341502Swpaul VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_THRESH); 167441502Swpaul VR_SETBIT(sc, VR_RXCFG, VR_RXTHRESH_STORENFWD); 167541502Swpaul 167641502Swpaul VR_CLRBIT(sc, VR_TXCFG, VR_TXCFG_TX_THRESH); 167741502Swpaul VR_SETBIT(sc, VR_TXCFG, VR_TXTHRESH_STORENFWD); 167841502Swpaul 167941502Swpaul /* Init circular RX list. */ 168041502Swpaul if (vr_list_rx_init(sc) == ENOBUFS) { 168141502Swpaul printf("vr%d: initialization failed: no " 168241502Swpaul "memory for rx buffers\n", sc->vr_unit); 168341502Swpaul vr_stop(sc); 168441502Swpaul (void)splx(s); 168541502Swpaul return; 168641502Swpaul } 168741502Swpaul 168841502Swpaul /* 168941502Swpaul * Init tx descriptors. 169041502Swpaul */ 169141502Swpaul vr_list_tx_init(sc); 169241502Swpaul 169341502Swpaul /* If we want promiscuous mode, set the allframes bit. */ 169441502Swpaul if (ifp->if_flags & IFF_PROMISC) 169541502Swpaul VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); 169641502Swpaul else 169741502Swpaul VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); 169841502Swpaul 169941502Swpaul /* Set capture broadcast bit to capture broadcast frames. */ 170041502Swpaul if (ifp->if_flags & IFF_BROADCAST) 170141502Swpaul VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); 170241502Swpaul else 170341502Swpaul VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); 170441502Swpaul 170541502Swpaul /* 170641502Swpaul * Program the multicast filter, if necessary. 170741502Swpaul */ 170841502Swpaul vr_setmulti(sc); 170941502Swpaul 171041502Swpaul /* 171141502Swpaul * Load the address of the RX list. 171241502Swpaul */ 171341502Swpaul CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); 171441502Swpaul 171541502Swpaul /* Enable receiver and transmitter. */ 171641502Swpaul CSR_WRITE_2(sc, VR_COMMAND, VR_CMD_TX_NOPOLL|VR_CMD_START| 171741502Swpaul VR_CMD_TX_ON|VR_CMD_RX_ON| 171841502Swpaul VR_CMD_RX_GO); 171941502Swpaul 172041502Swpaul vr_setcfg(sc, vr_phy_readreg(sc, PHY_BMCR)); 172141502Swpaul 172241502Swpaul CSR_WRITE_4(sc, VR_TXADDR, vtophys(&sc->vr_ldata->vr_tx_list[0])); 172341502Swpaul 172441502Swpaul /* 172541502Swpaul * Enable interrupts. 172641502Swpaul */ 172741502Swpaul CSR_WRITE_2(sc, VR_ISR, 0xFFFF); 172841502Swpaul CSR_WRITE_2(sc, VR_IMR, VR_INTRS); 172941502Swpaul 173041502Swpaul /* Restore state of BMCR */ 173141502Swpaul if (sc->vr_pinfo != NULL) 173241502Swpaul vr_phy_writereg(sc, PHY_BMCR, phy_bmcr); 173341502Swpaul 173441502Swpaul ifp->if_flags |= IFF_RUNNING; 173541502Swpaul ifp->if_flags &= ~IFF_OACTIVE; 173641502Swpaul 173741502Swpaul (void)splx(s); 173841502Swpaul 173941502Swpaul return; 174041502Swpaul} 174141502Swpaul 174241502Swpaul/* 174341502Swpaul * Set media options. 174441502Swpaul */ 174541502Swpaulstatic int vr_ifmedia_upd(ifp) 174641502Swpaul struct ifnet *ifp; 174741502Swpaul{ 174841502Swpaul struct vr_softc *sc; 174941502Swpaul struct ifmedia *ifm; 175041502Swpaul 175141502Swpaul sc = ifp->if_softc; 175241502Swpaul ifm = &sc->ifmedia; 175341502Swpaul 175441502Swpaul if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) 175541502Swpaul return(EINVAL); 175641502Swpaul 175741502Swpaul if (IFM_SUBTYPE(ifm->ifm_media) == IFM_AUTO) 175841502Swpaul vr_autoneg_mii(sc, VR_FLAG_SCHEDDELAY, 1); 175941502Swpaul else 176041502Swpaul vr_setmode_mii(sc, ifm->ifm_media); 176141502Swpaul 176241502Swpaul return(0); 176341502Swpaul} 176441502Swpaul 176541502Swpaul/* 176641502Swpaul * Report current media status. 176741502Swpaul */ 176841502Swpaulstatic void vr_ifmedia_sts(ifp, ifmr) 176941502Swpaul struct ifnet *ifp; 177041502Swpaul struct ifmediareq *ifmr; 177141502Swpaul{ 177241502Swpaul struct vr_softc *sc; 177341502Swpaul u_int16_t advert = 0, ability = 0; 177441502Swpaul 177541502Swpaul sc = ifp->if_softc; 177641502Swpaul 177741502Swpaul ifmr->ifm_active = IFM_ETHER; 177841502Swpaul 177941502Swpaul if (!(vr_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_AUTONEGENBL)) { 178041502Swpaul if (vr_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_SPEEDSEL) 178141502Swpaul ifmr->ifm_active = IFM_ETHER|IFM_100_TX; 178241502Swpaul else 178341502Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_T; 178441502Swpaul if (vr_phy_readreg(sc, PHY_BMCR) & PHY_BMCR_DUPLEX) 178541502Swpaul ifmr->ifm_active |= IFM_FDX; 178641502Swpaul else 178741502Swpaul ifmr->ifm_active |= IFM_HDX; 178841502Swpaul return; 178941502Swpaul } 179041502Swpaul 179141502Swpaul ability = vr_phy_readreg(sc, PHY_LPAR); 179241502Swpaul advert = vr_phy_readreg(sc, PHY_ANAR); 179341502Swpaul if (advert & PHY_ANAR_100BT4 && 179441502Swpaul ability & PHY_ANAR_100BT4) { 179541502Swpaul ifmr->ifm_active = IFM_ETHER|IFM_100_T4; 179641502Swpaul } else if (advert & PHY_ANAR_100BTXFULL && 179741502Swpaul ability & PHY_ANAR_100BTXFULL) { 179841502Swpaul ifmr->ifm_active = IFM_ETHER|IFM_100_TX|IFM_FDX; 179941502Swpaul } else if (advert & PHY_ANAR_100BTXHALF && 180041502Swpaul ability & PHY_ANAR_100BTXHALF) { 180141502Swpaul ifmr->ifm_active = IFM_ETHER|IFM_100_TX|IFM_HDX; 180241502Swpaul } else if (advert & PHY_ANAR_10BTFULL && 180341502Swpaul ability & PHY_ANAR_10BTFULL) { 180441502Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_T|IFM_FDX; 180541502Swpaul } else if (advert & PHY_ANAR_10BTHALF && 180641502Swpaul ability & PHY_ANAR_10BTHALF) { 180741502Swpaul ifmr->ifm_active = IFM_ETHER|IFM_10_T|IFM_HDX; 180841502Swpaul } 180941502Swpaul 181041502Swpaul return; 181141502Swpaul} 181241502Swpaul 181341502Swpaulstatic int vr_ioctl(ifp, command, data) 181441502Swpaul struct ifnet *ifp; 181541502Swpaul u_long command; 181641502Swpaul caddr_t data; 181741502Swpaul{ 181841502Swpaul struct vr_softc *sc = ifp->if_softc; 181941502Swpaul struct ifreq *ifr = (struct ifreq *) data; 182041502Swpaul int s, error = 0; 182141502Swpaul 182241502Swpaul s = splimp(); 182341502Swpaul 182441502Swpaul switch(command) { 182541502Swpaul case SIOCSIFADDR: 182641502Swpaul case SIOCGIFADDR: 182741502Swpaul case SIOCSIFMTU: 182841502Swpaul error = ether_ioctl(ifp, command, data); 182941502Swpaul break; 183041502Swpaul case SIOCSIFFLAGS: 183141502Swpaul if (ifp->if_flags & IFF_UP) { 183241502Swpaul vr_init(sc); 183341502Swpaul } else { 183441502Swpaul if (ifp->if_flags & IFF_RUNNING) 183541502Swpaul vr_stop(sc); 183641502Swpaul } 183741502Swpaul error = 0; 183841502Swpaul break; 183941502Swpaul case SIOCADDMULTI: 184041502Swpaul case SIOCDELMULTI: 184141502Swpaul vr_setmulti(sc); 184241502Swpaul error = 0; 184341502Swpaul break; 184441502Swpaul case SIOCGIFMEDIA: 184541502Swpaul case SIOCSIFMEDIA: 184641502Swpaul error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); 184741502Swpaul break; 184841502Swpaul default: 184941502Swpaul error = EINVAL; 185041502Swpaul break; 185141502Swpaul } 185241502Swpaul 185341502Swpaul (void)splx(s); 185441502Swpaul 185541502Swpaul return(error); 185641502Swpaul} 185741502Swpaul 185841502Swpaulstatic void vr_watchdog(ifp) 185941502Swpaul struct ifnet *ifp; 186041502Swpaul{ 186141502Swpaul struct vr_softc *sc; 186241502Swpaul 186341502Swpaul sc = ifp->if_softc; 186441502Swpaul 186541502Swpaul if (sc->vr_autoneg) { 186641502Swpaul vr_autoneg_mii(sc, VR_FLAG_DELAYTIMEO, 1); 186741502Swpaul return; 186841502Swpaul } 186941502Swpaul 187041502Swpaul ifp->if_oerrors++; 187141502Swpaul printf("vr%d: watchdog timeout\n", sc->vr_unit); 187241502Swpaul 187341502Swpaul if (!(vr_phy_readreg(sc, PHY_BMSR) & PHY_BMSR_LINKSTAT)) 187441502Swpaul printf("vr%d: no carrier - transceiver cable problem?\n", 187541502Swpaul sc->vr_unit); 187641502Swpaul 187741502Swpaul vr_stop(sc); 187841502Swpaul vr_reset(sc); 187941502Swpaul vr_init(sc); 188041502Swpaul 188141502Swpaul if (ifp->if_snd.ifq_head != NULL) 188241502Swpaul vr_start(ifp); 188341502Swpaul 188441502Swpaul return; 188541502Swpaul} 188641502Swpaul 188741502Swpaul/* 188841502Swpaul * Stop the adapter and free any mbufs allocated to the 188941502Swpaul * RX and TX lists. 189041502Swpaul */ 189141502Swpaulstatic void vr_stop(sc) 189241502Swpaul struct vr_softc *sc; 189341502Swpaul{ 189441502Swpaul register int i; 189541502Swpaul struct ifnet *ifp; 189641502Swpaul 189741502Swpaul ifp = &sc->arpcom.ac_if; 189841502Swpaul ifp->if_timer = 0; 189941502Swpaul 190041502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_STOP); 190141502Swpaul VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_RX_ON|VR_CMD_TX_ON)); 190241502Swpaul CSR_WRITE_2(sc, VR_IMR, 0x0000); 190341502Swpaul CSR_WRITE_4(sc, VR_TXADDR, 0x00000000); 190441502Swpaul CSR_WRITE_4(sc, VR_RXADDR, 0x00000000); 190541502Swpaul 190641502Swpaul /* 190741502Swpaul * Free data in the RX lists. 190841502Swpaul */ 190941502Swpaul for (i = 0; i < VR_RX_LIST_CNT; i++) { 191041502Swpaul if (sc->vr_cdata.vr_rx_chain[i].vr_mbuf != NULL) { 191141502Swpaul m_freem(sc->vr_cdata.vr_rx_chain[i].vr_mbuf); 191241502Swpaul sc->vr_cdata.vr_rx_chain[i].vr_mbuf = NULL; 191341502Swpaul } 191441502Swpaul } 191541502Swpaul bzero((char *)&sc->vr_ldata->vr_rx_list, 191641502Swpaul sizeof(sc->vr_ldata->vr_rx_list)); 191741502Swpaul 191841502Swpaul /* 191941502Swpaul * Free the TX list buffers. 192041502Swpaul */ 192141502Swpaul for (i = 0; i < VR_TX_LIST_CNT; i++) { 192241502Swpaul if (sc->vr_cdata.vr_tx_chain[i].vr_mbuf != NULL) { 192341502Swpaul m_freem(sc->vr_cdata.vr_tx_chain[i].vr_mbuf); 192441502Swpaul sc->vr_cdata.vr_tx_chain[i].vr_mbuf = NULL; 192541502Swpaul } 192641502Swpaul } 192741502Swpaul 192841502Swpaul bzero((char *)&sc->vr_ldata->vr_tx_list, 192941502Swpaul sizeof(sc->vr_ldata->vr_tx_list)); 193041502Swpaul 193141502Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 193241502Swpaul 193341502Swpaul return; 193441502Swpaul} 193541502Swpaul 193641502Swpaul/* 193741502Swpaul * Stop all chip I/O so that the kernel's probe routines don't 193841502Swpaul * get confused by errant DMAs when rebooting. 193941502Swpaul */ 194041502Swpaulstatic void vr_shutdown(howto, arg) 194141502Swpaul int howto; 194241502Swpaul void *arg; 194341502Swpaul{ 194441502Swpaul struct vr_softc *sc = (struct vr_softc *)arg; 194541502Swpaul 194641502Swpaul vr_stop(sc); 194741502Swpaul 194841502Swpaul return; 194941502Swpaul} 195041502Swpaul 195141502Swpaulstatic struct pci_device vr_device = { 195241502Swpaul "vr", 195341502Swpaul vr_probe, 195441502Swpaul vr_attach, 195541502Swpaul &vr_count, 195641502Swpaul NULL 195741502Swpaul}; 195841502SwpaulDATA_SET(pcidevice_set, vr_device); 1959