if_vr.c revision 61041
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 61041 2000-05-28 16:13:43Z 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 6241502Swpaul#include <sys/param.h> 6341502Swpaul#include <sys/systm.h> 6441502Swpaul#include <sys/sockio.h> 6541502Swpaul#include <sys/mbuf.h> 6641502Swpaul#include <sys/malloc.h> 6741502Swpaul#include <sys/kernel.h> 6841502Swpaul#include <sys/socket.h> 6941502Swpaul 7041502Swpaul#include <net/if.h> 7141502Swpaul#include <net/if_arp.h> 7241502Swpaul#include <net/ethernet.h> 7341502Swpaul#include <net/if_dl.h> 7441502Swpaul#include <net/if_media.h> 7541502Swpaul 7641502Swpaul#include <net/bpf.h> 7741502Swpaul 7841502Swpaul#include <vm/vm.h> /* for vtophys */ 7941502Swpaul#include <vm/pmap.h> /* for vtophys */ 8041502Swpaul#include <machine/clock.h> /* for DELAY */ 8141502Swpaul#include <machine/bus_pio.h> 8241502Swpaul#include <machine/bus_memio.h> 8341502Swpaul#include <machine/bus.h> 8449610Swpaul#include <machine/resource.h> 8549610Swpaul#include <sys/bus.h> 8649610Swpaul#include <sys/rman.h> 8741502Swpaul 8851432Swpaul#include <dev/mii/mii.h> 8951432Swpaul#include <dev/mii/miivar.h> 9051432Swpaul 9141502Swpaul#include <pci/pcireg.h> 9241502Swpaul#include <pci/pcivar.h> 9341502Swpaul 9441502Swpaul#define VR_USEIOSPACE 9541502Swpaul 9641502Swpaul#include <pci/if_vrreg.h> 9741502Swpaul 9859758SpeterMODULE_DEPEND(vr, miibus, 1, 1, 1); 9959758Speter 10051432Swpaul/* "controller miibus0" required. See GENERIC if you get errors here. */ 10151432Swpaul#include "miibus_if.h" 10251432Swpaul 10341502Swpaul#ifndef lint 10441591Sarchiestatic const char rcsid[] = 10550477Speter "$FreeBSD: head/sys/dev/vr/if_vr.c 61041 2000-05-28 16:13:43Z peter $"; 10641502Swpaul#endif 10741502Swpaul 10841502Swpaul/* 10941502Swpaul * Various supported device vendors/types and their names. 11041502Swpaul */ 11141502Swpaulstatic struct vr_type vr_devs[] = { 11241502Swpaul { VIA_VENDORID, VIA_DEVICEID_RHINE, 11341502Swpaul "VIA VT3043 Rhine I 10/100BaseTX" }, 11441502Swpaul { VIA_VENDORID, VIA_DEVICEID_RHINE_II, 11541502Swpaul "VIA VT86C100A Rhine II 10/100BaseTX" }, 11644238Swpaul { DELTA_VENDORID, DELTA_DEVICEID_RHINE_II, 11744238Swpaul "Delta Electronics Rhine II 10/100BaseTX" }, 11844238Swpaul { ADDTRON_VENDORID, ADDTRON_DEVICEID_RHINE_II, 11944238Swpaul "Addtron Technology Rhine II 10/100BaseTX" }, 12041502Swpaul { 0, 0, NULL } 12141502Swpaul}; 12241502Swpaul 12349610Swpaulstatic int vr_probe __P((device_t)); 12449610Swpaulstatic int vr_attach __P((device_t)); 12549610Swpaulstatic int vr_detach __P((device_t)); 12641502Swpaul 12741502Swpaulstatic int vr_newbuf __P((struct vr_softc *, 12849610Swpaul struct vr_chain_onefrag *, 12949610Swpaul struct mbuf *)); 13041502Swpaulstatic int vr_encap __P((struct vr_softc *, struct vr_chain *, 13141502Swpaul struct mbuf * )); 13241502Swpaul 13341502Swpaulstatic void vr_rxeof __P((struct vr_softc *)); 13441502Swpaulstatic void vr_rxeoc __P((struct vr_softc *)); 13541502Swpaulstatic void vr_txeof __P((struct vr_softc *)); 13641502Swpaulstatic void vr_txeoc __P((struct vr_softc *)); 13751432Swpaulstatic void vr_tick __P((void *)); 13841502Swpaulstatic void vr_intr __P((void *)); 13941502Swpaulstatic void vr_start __P((struct ifnet *)); 14041502Swpaulstatic int vr_ioctl __P((struct ifnet *, u_long, caddr_t)); 14141502Swpaulstatic void vr_init __P((void *)); 14241502Swpaulstatic void vr_stop __P((struct vr_softc *)); 14341502Swpaulstatic void vr_watchdog __P((struct ifnet *)); 14449610Swpaulstatic void vr_shutdown __P((device_t)); 14541502Swpaulstatic int vr_ifmedia_upd __P((struct ifnet *)); 14641502Swpaulstatic void vr_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 14741502Swpaul 14841502Swpaulstatic void vr_mii_sync __P((struct vr_softc *)); 14941502Swpaulstatic void vr_mii_send __P((struct vr_softc *, u_int32_t, int)); 15041502Swpaulstatic int vr_mii_readreg __P((struct vr_softc *, struct vr_mii_frame *)); 15141502Swpaulstatic int vr_mii_writereg __P((struct vr_softc *, struct vr_mii_frame *)); 15251432Swpaulstatic int vr_miibus_readreg __P((device_t, int, int)); 15351432Swpaulstatic int vr_miibus_writereg __P((device_t, int, int, int)); 15451432Swpaulstatic void vr_miibus_statchg __P((device_t)); 15541502Swpaul 15651432Swpaulstatic void vr_setcfg __P((struct vr_softc *, int)); 15741502Swpaulstatic u_int8_t vr_calchash __P((u_int8_t *)); 15841502Swpaulstatic void vr_setmulti __P((struct vr_softc *)); 15941502Swpaulstatic void vr_reset __P((struct vr_softc *)); 16041502Swpaulstatic int vr_list_rx_init __P((struct vr_softc *)); 16141502Swpaulstatic int vr_list_tx_init __P((struct vr_softc *)); 16241502Swpaul 16349610Swpaul#ifdef VR_USEIOSPACE 16449610Swpaul#define VR_RES SYS_RES_IOPORT 16549610Swpaul#define VR_RID VR_PCI_LOIO 16649610Swpaul#else 16749610Swpaul#define VR_RES SYS_RES_MEMORY 16849610Swpaul#define VR_RID VR_PCI_LOMEM 16949610Swpaul#endif 17049610Swpaul 17149610Swpaulstatic device_method_t vr_methods[] = { 17249610Swpaul /* Device interface */ 17349610Swpaul DEVMETHOD(device_probe, vr_probe), 17449610Swpaul DEVMETHOD(device_attach, vr_attach), 17549610Swpaul DEVMETHOD(device_detach, vr_detach), 17649610Swpaul DEVMETHOD(device_shutdown, vr_shutdown), 17751432Swpaul 17851432Swpaul /* bus interface */ 17951432Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 18051432Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 18151432Swpaul 18251432Swpaul /* MII interface */ 18351432Swpaul DEVMETHOD(miibus_readreg, vr_miibus_readreg), 18451432Swpaul DEVMETHOD(miibus_writereg, vr_miibus_writereg), 18551432Swpaul DEVMETHOD(miibus_statchg, vr_miibus_statchg), 18651432Swpaul 18749610Swpaul { 0, 0 } 18849610Swpaul}; 18949610Swpaul 19049610Swpaulstatic driver_t vr_driver = { 19151455Swpaul "vr", 19249610Swpaul vr_methods, 19349610Swpaul sizeof(struct vr_softc) 19449610Swpaul}; 19549610Swpaul 19649610Swpaulstatic devclass_t vr_devclass; 19749610Swpaul 19851533SwpaulDRIVER_MODULE(if_vr, pci, vr_driver, vr_devclass, 0, 0); 19951473SwpaulDRIVER_MODULE(miibus, vr, miibus_driver, miibus_devclass, 0, 0); 20049610Swpaul 20141502Swpaul#define VR_SETBIT(sc, reg, x) \ 20241502Swpaul CSR_WRITE_1(sc, reg, \ 20341502Swpaul CSR_READ_1(sc, reg) | x) 20441502Swpaul 20541502Swpaul#define VR_CLRBIT(sc, reg, x) \ 20641502Swpaul CSR_WRITE_1(sc, reg, \ 20741502Swpaul CSR_READ_1(sc, reg) & ~x) 20841502Swpaul 20941502Swpaul#define VR_SETBIT16(sc, reg, x) \ 21041502Swpaul CSR_WRITE_2(sc, reg, \ 21141502Swpaul CSR_READ_2(sc, reg) | x) 21241502Swpaul 21341502Swpaul#define VR_CLRBIT16(sc, reg, x) \ 21441502Swpaul CSR_WRITE_2(sc, reg, \ 21541502Swpaul CSR_READ_2(sc, reg) & ~x) 21641502Swpaul 21741502Swpaul#define VR_SETBIT32(sc, reg, x) \ 21841502Swpaul CSR_WRITE_4(sc, reg, \ 21941502Swpaul CSR_READ_4(sc, reg) | x) 22041502Swpaul 22141502Swpaul#define VR_CLRBIT32(sc, reg, x) \ 22241502Swpaul CSR_WRITE_4(sc, reg, \ 22341502Swpaul CSR_READ_4(sc, reg) & ~x) 22441502Swpaul 22541502Swpaul#define SIO_SET(x) \ 22641502Swpaul CSR_WRITE_1(sc, VR_MIICMD, \ 22741502Swpaul CSR_READ_1(sc, VR_MIICMD) | x) 22841502Swpaul 22941502Swpaul#define SIO_CLR(x) \ 23041502Swpaul CSR_WRITE_1(sc, VR_MIICMD, \ 23141502Swpaul CSR_READ_1(sc, VR_MIICMD) & ~x) 23241502Swpaul 23341502Swpaul/* 23441502Swpaul * Sync the PHYs by setting data bit and strobing the clock 32 times. 23541502Swpaul */ 23641502Swpaulstatic void vr_mii_sync(sc) 23741502Swpaul struct vr_softc *sc; 23841502Swpaul{ 23941502Swpaul register int i; 24041502Swpaul 24141502Swpaul SIO_SET(VR_MIICMD_DIR|VR_MIICMD_DATAIN); 24241502Swpaul 24341502Swpaul for (i = 0; i < 32; i++) { 24441502Swpaul SIO_SET(VR_MIICMD_CLK); 24541502Swpaul DELAY(1); 24641502Swpaul SIO_CLR(VR_MIICMD_CLK); 24741502Swpaul DELAY(1); 24841502Swpaul } 24941502Swpaul 25041502Swpaul return; 25141502Swpaul} 25241502Swpaul 25341502Swpaul/* 25441502Swpaul * Clock a series of bits through the MII. 25541502Swpaul */ 25641502Swpaulstatic void vr_mii_send(sc, bits, cnt) 25741502Swpaul struct vr_softc *sc; 25841502Swpaul u_int32_t bits; 25941502Swpaul int cnt; 26041502Swpaul{ 26141502Swpaul int i; 26241502Swpaul 26341502Swpaul SIO_CLR(VR_MIICMD_CLK); 26441502Swpaul 26541502Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 26641502Swpaul if (bits & i) { 26741502Swpaul SIO_SET(VR_MIICMD_DATAIN); 26841502Swpaul } else { 26941502Swpaul SIO_CLR(VR_MIICMD_DATAIN); 27041502Swpaul } 27141502Swpaul DELAY(1); 27241502Swpaul SIO_CLR(VR_MIICMD_CLK); 27341502Swpaul DELAY(1); 27441502Swpaul SIO_SET(VR_MIICMD_CLK); 27541502Swpaul } 27641502Swpaul} 27741502Swpaul 27841502Swpaul/* 27941502Swpaul * Read an PHY register through the MII. 28041502Swpaul */ 28141502Swpaulstatic int vr_mii_readreg(sc, frame) 28241502Swpaul struct vr_softc *sc; 28341502Swpaul struct vr_mii_frame *frame; 28441502Swpaul 28541502Swpaul{ 28641502Swpaul int i, ack, s; 28741502Swpaul 28841502Swpaul s = splimp(); 28941502Swpaul 29041502Swpaul /* 29141502Swpaul * Set up frame for RX. 29241502Swpaul */ 29341502Swpaul frame->mii_stdelim = VR_MII_STARTDELIM; 29441502Swpaul frame->mii_opcode = VR_MII_READOP; 29541502Swpaul frame->mii_turnaround = 0; 29641502Swpaul frame->mii_data = 0; 29741502Swpaul 29841502Swpaul CSR_WRITE_1(sc, VR_MIICMD, 0); 29941502Swpaul VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); 30041502Swpaul 30141502Swpaul /* 30241502Swpaul * Turn on data xmit. 30341502Swpaul */ 30441502Swpaul SIO_SET(VR_MIICMD_DIR); 30541502Swpaul 30641502Swpaul vr_mii_sync(sc); 30741502Swpaul 30841502Swpaul /* 30941502Swpaul * Send command/address info. 31041502Swpaul */ 31141502Swpaul vr_mii_send(sc, frame->mii_stdelim, 2); 31241502Swpaul vr_mii_send(sc, frame->mii_opcode, 2); 31341502Swpaul vr_mii_send(sc, frame->mii_phyaddr, 5); 31441502Swpaul vr_mii_send(sc, frame->mii_regaddr, 5); 31541502Swpaul 31641502Swpaul /* Idle bit */ 31741502Swpaul SIO_CLR((VR_MIICMD_CLK|VR_MIICMD_DATAIN)); 31841502Swpaul DELAY(1); 31941502Swpaul SIO_SET(VR_MIICMD_CLK); 32041502Swpaul DELAY(1); 32141502Swpaul 32241502Swpaul /* Turn off xmit. */ 32341502Swpaul SIO_CLR(VR_MIICMD_DIR); 32441502Swpaul 32541502Swpaul /* Check for ack */ 32641502Swpaul SIO_CLR(VR_MIICMD_CLK); 32741502Swpaul DELAY(1); 32841502Swpaul SIO_SET(VR_MIICMD_CLK); 32941502Swpaul DELAY(1); 33041502Swpaul ack = CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT; 33141502Swpaul 33241502Swpaul /* 33341502Swpaul * Now try reading data bits. If the ack failed, we still 33441502Swpaul * need to clock through 16 cycles to keep the PHY(s) in sync. 33541502Swpaul */ 33641502Swpaul if (ack) { 33741502Swpaul for(i = 0; i < 16; i++) { 33841502Swpaul SIO_CLR(VR_MIICMD_CLK); 33941502Swpaul DELAY(1); 34041502Swpaul SIO_SET(VR_MIICMD_CLK); 34141502Swpaul DELAY(1); 34241502Swpaul } 34341502Swpaul goto fail; 34441502Swpaul } 34541502Swpaul 34641502Swpaul for (i = 0x8000; i; i >>= 1) { 34741502Swpaul SIO_CLR(VR_MIICMD_CLK); 34841502Swpaul DELAY(1); 34941502Swpaul if (!ack) { 35041502Swpaul if (CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT) 35141502Swpaul frame->mii_data |= i; 35241502Swpaul DELAY(1); 35341502Swpaul } 35441502Swpaul SIO_SET(VR_MIICMD_CLK); 35541502Swpaul DELAY(1); 35641502Swpaul } 35741502Swpaul 35841502Swpaulfail: 35941502Swpaul 36041502Swpaul SIO_CLR(VR_MIICMD_CLK); 36141502Swpaul DELAY(1); 36241502Swpaul SIO_SET(VR_MIICMD_CLK); 36341502Swpaul DELAY(1); 36441502Swpaul 36541502Swpaul splx(s); 36641502Swpaul 36741502Swpaul if (ack) 36841502Swpaul return(1); 36941502Swpaul return(0); 37041502Swpaul} 37141502Swpaul 37241502Swpaul/* 37341502Swpaul * Write to a PHY register through the MII. 37441502Swpaul */ 37541502Swpaulstatic int vr_mii_writereg(sc, frame) 37641502Swpaul struct vr_softc *sc; 37741502Swpaul struct vr_mii_frame *frame; 37841502Swpaul 37941502Swpaul{ 38041502Swpaul int s; 38141502Swpaul 38241502Swpaul s = splimp(); 38341502Swpaul 38441502Swpaul CSR_WRITE_1(sc, VR_MIICMD, 0); 38541502Swpaul VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); 38641502Swpaul 38741502Swpaul /* 38841502Swpaul * Set up frame for TX. 38941502Swpaul */ 39041502Swpaul 39141502Swpaul frame->mii_stdelim = VR_MII_STARTDELIM; 39241502Swpaul frame->mii_opcode = VR_MII_WRITEOP; 39341502Swpaul frame->mii_turnaround = VR_MII_TURNAROUND; 39441502Swpaul 39541502Swpaul /* 39641502Swpaul * Turn on data output. 39741502Swpaul */ 39841502Swpaul SIO_SET(VR_MIICMD_DIR); 39941502Swpaul 40041502Swpaul vr_mii_sync(sc); 40141502Swpaul 40241502Swpaul vr_mii_send(sc, frame->mii_stdelim, 2); 40341502Swpaul vr_mii_send(sc, frame->mii_opcode, 2); 40441502Swpaul vr_mii_send(sc, frame->mii_phyaddr, 5); 40541502Swpaul vr_mii_send(sc, frame->mii_regaddr, 5); 40641502Swpaul vr_mii_send(sc, frame->mii_turnaround, 2); 40741502Swpaul vr_mii_send(sc, frame->mii_data, 16); 40841502Swpaul 40941502Swpaul /* Idle bit. */ 41041502Swpaul SIO_SET(VR_MIICMD_CLK); 41141502Swpaul DELAY(1); 41241502Swpaul SIO_CLR(VR_MIICMD_CLK); 41341502Swpaul DELAY(1); 41441502Swpaul 41541502Swpaul /* 41641502Swpaul * Turn off xmit. 41741502Swpaul */ 41841502Swpaul SIO_CLR(VR_MIICMD_DIR); 41941502Swpaul 42041502Swpaul splx(s); 42141502Swpaul 42241502Swpaul return(0); 42341502Swpaul} 42441502Swpaul 42551432Swpaulstatic int vr_miibus_readreg(dev, phy, reg) 42651432Swpaul device_t dev; 42751432Swpaul int phy, reg; 42851432Swpaul{ 42941502Swpaul struct vr_softc *sc; 43041502Swpaul struct vr_mii_frame frame; 43141502Swpaul 43251432Swpaul sc = device_get_softc(dev); 43341502Swpaul bzero((char *)&frame, sizeof(frame)); 43441502Swpaul 43551432Swpaul frame.mii_phyaddr = phy; 43641502Swpaul frame.mii_regaddr = reg; 43741502Swpaul vr_mii_readreg(sc, &frame); 43841502Swpaul 43941502Swpaul return(frame.mii_data); 44041502Swpaul} 44141502Swpaul 44251432Swpaulstatic int vr_miibus_writereg(dev, phy, reg, data) 44351432Swpaul device_t dev; 44451432Swpaul u_int16_t phy, reg, data; 44551432Swpaul{ 44641502Swpaul struct vr_softc *sc; 44741502Swpaul struct vr_mii_frame frame; 44841502Swpaul 44951432Swpaul sc = device_get_softc(dev); 45041502Swpaul bzero((char *)&frame, sizeof(frame)); 45141502Swpaul 45251432Swpaul frame.mii_phyaddr = phy; 45341502Swpaul frame.mii_regaddr = reg; 45441502Swpaul frame.mii_data = data; 45541502Swpaul 45641502Swpaul vr_mii_writereg(sc, &frame); 45741502Swpaul 45851432Swpaul return(0); 45951432Swpaul} 46051432Swpaul 46151432Swpaulstatic void vr_miibus_statchg(dev) 46251432Swpaul device_t dev; 46351432Swpaul{ 46451432Swpaul struct vr_softc *sc; 46551432Swpaul struct mii_data *mii; 46651432Swpaul 46751432Swpaul sc = device_get_softc(dev); 46851432Swpaul mii = device_get_softc(sc->vr_miibus); 46951432Swpaul vr_setcfg(sc, mii->mii_media_active); 47051432Swpaul 47141502Swpaul return; 47241502Swpaul} 47341502Swpaul 47441502Swpaul/* 47541502Swpaul * Calculate CRC of a multicast group address, return the lower 6 bits. 47641502Swpaul */ 47741502Swpaulstatic u_int8_t vr_calchash(addr) 47841502Swpaul u_int8_t *addr; 47941502Swpaul{ 48041502Swpaul u_int32_t crc, carry; 48141502Swpaul int i, j; 48241502Swpaul u_int8_t c; 48341502Swpaul 48441502Swpaul /* Compute CRC for the address value. */ 48541502Swpaul crc = 0xFFFFFFFF; /* initial value */ 48641502Swpaul 48741502Swpaul for (i = 0; i < 6; i++) { 48841502Swpaul c = *(addr + i); 48941502Swpaul for (j = 0; j < 8; j++) { 49041502Swpaul carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); 49141502Swpaul crc <<= 1; 49241502Swpaul c >>= 1; 49341502Swpaul if (carry) 49441502Swpaul crc = (crc ^ 0x04c11db6) | carry; 49541502Swpaul } 49641502Swpaul } 49741502Swpaul 49841502Swpaul /* return the filter bit position */ 49941502Swpaul return((crc >> 26) & 0x0000003F); 50041502Swpaul} 50141502Swpaul 50241502Swpaul/* 50341502Swpaul * Program the 64-bit multicast hash filter. 50441502Swpaul */ 50541502Swpaulstatic void vr_setmulti(sc) 50641502Swpaul struct vr_softc *sc; 50741502Swpaul{ 50841502Swpaul struct ifnet *ifp; 50941502Swpaul int h = 0; 51041502Swpaul u_int32_t hashes[2] = { 0, 0 }; 51141502Swpaul struct ifmultiaddr *ifma; 51241502Swpaul u_int8_t rxfilt; 51341502Swpaul int mcnt = 0; 51441502Swpaul 51541502Swpaul ifp = &sc->arpcom.ac_if; 51641502Swpaul 51741502Swpaul rxfilt = CSR_READ_1(sc, VR_RXCFG); 51841502Swpaul 51941502Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 52041502Swpaul rxfilt |= VR_RXCFG_RX_MULTI; 52141502Swpaul CSR_WRITE_1(sc, VR_RXCFG, rxfilt); 52241502Swpaul CSR_WRITE_4(sc, VR_MAR0, 0xFFFFFFFF); 52341502Swpaul CSR_WRITE_4(sc, VR_MAR1, 0xFFFFFFFF); 52441502Swpaul return; 52541502Swpaul } 52641502Swpaul 52741502Swpaul /* first, zot all the existing hash bits */ 52841502Swpaul CSR_WRITE_4(sc, VR_MAR0, 0); 52941502Swpaul CSR_WRITE_4(sc, VR_MAR1, 0); 53041502Swpaul 53141502Swpaul /* now program new ones */ 53241502Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 53341502Swpaul ifma = ifma->ifma_link.le_next) { 53441502Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 53541502Swpaul continue; 53641502Swpaul h = vr_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 53741502Swpaul if (h < 32) 53841502Swpaul hashes[0] |= (1 << h); 53941502Swpaul else 54041502Swpaul hashes[1] |= (1 << (h - 32)); 54141502Swpaul mcnt++; 54241502Swpaul } 54341502Swpaul 54441502Swpaul if (mcnt) 54541502Swpaul rxfilt |= VR_RXCFG_RX_MULTI; 54641502Swpaul else 54741502Swpaul rxfilt &= ~VR_RXCFG_RX_MULTI; 54841502Swpaul 54941502Swpaul CSR_WRITE_4(sc, VR_MAR0, hashes[0]); 55041502Swpaul CSR_WRITE_4(sc, VR_MAR1, hashes[1]); 55141502Swpaul CSR_WRITE_1(sc, VR_RXCFG, rxfilt); 55241502Swpaul 55341502Swpaul return; 55441502Swpaul} 55541502Swpaul 55641502Swpaul/* 55741502Swpaul * In order to fiddle with the 55841502Swpaul * 'full-duplex' and '100Mbps' bits in the netconfig register, we 55941502Swpaul * first have to put the transmit and/or receive logic in the idle state. 56041502Swpaul */ 56151432Swpaulstatic void vr_setcfg(sc, media) 56241502Swpaul struct vr_softc *sc; 56351432Swpaul int media; 56441502Swpaul{ 56541502Swpaul int restart = 0; 56641502Swpaul 56741502Swpaul if (CSR_READ_2(sc, VR_COMMAND) & (VR_CMD_TX_ON|VR_CMD_RX_ON)) { 56841502Swpaul restart = 1; 56941502Swpaul VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_TX_ON|VR_CMD_RX_ON)); 57041502Swpaul } 57141502Swpaul 57251432Swpaul if ((media & IFM_GMASK) == IFM_FDX) 57341502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); 57441502Swpaul else 57541502Swpaul VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); 57641502Swpaul 57741502Swpaul if (restart) 57841502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_RX_ON); 57941502Swpaul 58041502Swpaul return; 58141502Swpaul} 58241502Swpaul 58341502Swpaulstatic void vr_reset(sc) 58441502Swpaul struct vr_softc *sc; 58541502Swpaul{ 58641502Swpaul register int i; 58741502Swpaul 58841502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RESET); 58941502Swpaul 59041502Swpaul for (i = 0; i < VR_TIMEOUT; i++) { 59141502Swpaul DELAY(10); 59241502Swpaul if (!(CSR_READ_2(sc, VR_COMMAND) & VR_CMD_RESET)) 59341502Swpaul break; 59441502Swpaul } 59541502Swpaul if (i == VR_TIMEOUT) 59641502Swpaul printf("vr%d: reset never completed!\n", sc->vr_unit); 59741502Swpaul 59841502Swpaul /* Wait a little while for the chip to get its brains in order. */ 59941502Swpaul DELAY(1000); 60041502Swpaul 60141502Swpaul return; 60241502Swpaul} 60341502Swpaul 60441502Swpaul/* 60541502Swpaul * Probe for a VIA Rhine chip. Check the PCI vendor and device 60641502Swpaul * IDs against our list and return a device name if we find a match. 60741502Swpaul */ 60849610Swpaulstatic int vr_probe(dev) 60949610Swpaul device_t dev; 61041502Swpaul{ 61141502Swpaul struct vr_type *t; 61241502Swpaul 61341502Swpaul t = vr_devs; 61441502Swpaul 61541502Swpaul while(t->vr_name != NULL) { 61649610Swpaul if ((pci_get_vendor(dev) == t->vr_vid) && 61749610Swpaul (pci_get_device(dev) == t->vr_did)) { 61849610Swpaul device_set_desc(dev, t->vr_name); 61949610Swpaul return(0); 62041502Swpaul } 62141502Swpaul t++; 62241502Swpaul } 62341502Swpaul 62449610Swpaul return(ENXIO); 62541502Swpaul} 62641502Swpaul 62741502Swpaul/* 62841502Swpaul * Attach the interface. Allocate softc structures, do ifmedia 62941502Swpaul * setup and ethernet/BPF attach. 63041502Swpaul */ 63149610Swpaulstatic int vr_attach(dev) 63249610Swpaul device_t dev; 63341502Swpaul{ 63451432Swpaul int i, s; 63541502Swpaul u_char eaddr[ETHER_ADDR_LEN]; 63641502Swpaul u_int32_t command; 63741502Swpaul struct vr_softc *sc; 63841502Swpaul struct ifnet *ifp; 63949610Swpaul int unit, error = 0, rid; 64041502Swpaul 64141502Swpaul s = splimp(); 64241502Swpaul 64349610Swpaul sc = device_get_softc(dev); 64449610Swpaul unit = device_get_unit(dev); 64549610Swpaul bzero(sc, sizeof(struct vr_softc *)); 64641502Swpaul 64741502Swpaul /* 64841502Swpaul * Handle power management nonsense. 64941502Swpaul */ 65041502Swpaul 65149610Swpaul command = pci_read_config(dev, VR_PCI_CAPID, 4) & 0x000000FF; 65241502Swpaul if (command == 0x01) { 65341502Swpaul 65449610Swpaul command = pci_read_config(dev, VR_PCI_PWRMGMTCTRL, 4); 65541502Swpaul if (command & VR_PSTATE_MASK) { 65641502Swpaul u_int32_t iobase, membase, irq; 65741502Swpaul 65841502Swpaul /* Save important PCI config data. */ 65949610Swpaul iobase = pci_read_config(dev, VR_PCI_LOIO, 4); 66049610Swpaul membase = pci_read_config(dev, VR_PCI_LOMEM, 4); 66149610Swpaul irq = pci_read_config(dev, VR_PCI_INTLINE, 4); 66241502Swpaul 66341502Swpaul /* Reset the power state. */ 66441502Swpaul printf("vr%d: chip is in D%d power mode " 66541502Swpaul "-- setting to D0\n", unit, command & VR_PSTATE_MASK); 66641502Swpaul command &= 0xFFFFFFFC; 66749610Swpaul pci_write_config(dev, VR_PCI_PWRMGMTCTRL, command, 4); 66841502Swpaul 66941502Swpaul /* Restore PCI config data. */ 67049610Swpaul pci_write_config(dev, VR_PCI_LOIO, iobase, 4); 67149610Swpaul pci_write_config(dev, VR_PCI_LOMEM, membase, 4); 67249610Swpaul pci_write_config(dev, VR_PCI_INTLINE, irq, 4); 67341502Swpaul } 67441502Swpaul } 67541502Swpaul 67641502Swpaul /* 67741502Swpaul * Map control/status registers. 67841502Swpaul */ 67961041Speter command = pci_read_config(dev, PCIR_COMMAND, 4); 68041502Swpaul command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 68161041Speter pci_write_config(dev, PCIR_COMMAND, command, 4); 68261041Speter command = pci_read_config(dev, PCIR_COMMAND, 4); 68341502Swpaul 68441502Swpaul#ifdef VR_USEIOSPACE 68541502Swpaul if (!(command & PCIM_CMD_PORTEN)) { 68641502Swpaul printf("vr%d: failed to enable I/O ports!\n", unit); 68741502Swpaul free(sc, M_DEVBUF); 68841502Swpaul goto fail; 68941502Swpaul } 69041502Swpaul#else 69141502Swpaul if (!(command & PCIM_CMD_MEMEN)) { 69241502Swpaul printf("vr%d: failed to enable memory mapping!\n", unit); 69341502Swpaul goto fail; 69441502Swpaul } 69549610Swpaul#endif 69641502Swpaul 69749610Swpaul rid = VR_RID; 69849610Swpaul sc->vr_res = bus_alloc_resource(dev, VR_RES, &rid, 69949610Swpaul 0, ~0, 1, RF_ACTIVE); 70049610Swpaul 70149610Swpaul if (sc->vr_res == NULL) { 70249610Swpaul printf("vr%d: couldn't map ports/memory\n", unit); 70349610Swpaul error = ENXIO; 70441502Swpaul goto fail; 70541502Swpaul } 70641502Swpaul 70749610Swpaul sc->vr_btag = rman_get_bustag(sc->vr_res); 70849610Swpaul sc->vr_bhandle = rman_get_bushandle(sc->vr_res); 70941502Swpaul 71041502Swpaul /* Allocate interrupt */ 71149610Swpaul rid = 0; 71249610Swpaul sc->vr_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 71349610Swpaul RF_SHAREABLE | RF_ACTIVE); 71449610Swpaul 71549610Swpaul if (sc->vr_irq == NULL) { 71641502Swpaul printf("vr%d: couldn't map interrupt\n", unit); 71749610Swpaul bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); 71849610Swpaul error = ENXIO; 71941502Swpaul goto fail; 72041502Swpaul } 72141502Swpaul 72249610Swpaul error = bus_setup_intr(dev, sc->vr_irq, INTR_TYPE_NET, 72349610Swpaul vr_intr, sc, &sc->vr_intrhand); 72449610Swpaul 72549610Swpaul if (error) { 72649610Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); 72749610Swpaul bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); 72849610Swpaul printf("vr%d: couldn't set up irq\n", unit); 72949610Swpaul goto fail; 73049610Swpaul } 73149610Swpaul 73241502Swpaul /* Reset the adapter. */ 73341502Swpaul vr_reset(sc); 73441502Swpaul 73541502Swpaul /* 73641502Swpaul * Get station address. The way the Rhine chips work, 73741502Swpaul * you're not allowed to directly access the EEPROM once 73841502Swpaul * they've been programmed a special way. Consequently, 73941502Swpaul * we need to read the node address from the PAR0 and PAR1 74041502Swpaul * registers. 74141502Swpaul */ 74241502Swpaul VR_SETBIT(sc, VR_EECSR, VR_EECSR_LOAD); 74341502Swpaul DELAY(200); 74441502Swpaul for (i = 0; i < ETHER_ADDR_LEN; i++) 74541502Swpaul eaddr[i] = CSR_READ_1(sc, VR_PAR0 + i); 74641502Swpaul 74741502Swpaul /* 74841502Swpaul * A Rhine chip was detected. Inform the world. 74941502Swpaul */ 75041502Swpaul printf("vr%d: Ethernet address: %6D\n", unit, eaddr, ":"); 75141502Swpaul 75241502Swpaul sc->vr_unit = unit; 75341502Swpaul bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 75441502Swpaul 75551432Swpaul sc->vr_ldata = contigmalloc(sizeof(struct vr_list_data), M_DEVBUF, 75651657Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 75751432Swpaul 75851432Swpaul if (sc->vr_ldata == NULL) { 75941502Swpaul printf("vr%d: no memory for list buffers!\n", unit); 76049610Swpaul bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); 76149610Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); 76249610Swpaul bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); 76349610Swpaul error = ENXIO; 76449610Swpaul goto fail; 76541502Swpaul } 76641502Swpaul 76741502Swpaul bzero(sc->vr_ldata, sizeof(struct vr_list_data)); 76841502Swpaul 76941502Swpaul ifp = &sc->arpcom.ac_if; 77041502Swpaul ifp->if_softc = sc; 77141502Swpaul ifp->if_unit = unit; 77241502Swpaul ifp->if_name = "vr"; 77341502Swpaul ifp->if_mtu = ETHERMTU; 77441502Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 77541502Swpaul ifp->if_ioctl = vr_ioctl; 77641502Swpaul ifp->if_output = ether_output; 77741502Swpaul ifp->if_start = vr_start; 77841502Swpaul ifp->if_watchdog = vr_watchdog; 77941502Swpaul ifp->if_init = vr_init; 78041502Swpaul ifp->if_baudrate = 10000000; 78143515Swpaul ifp->if_snd.ifq_maxlen = VR_TX_LIST_CNT - 1; 78241502Swpaul 78351432Swpaul /* 78451432Swpaul * Do MII setup. 78551432Swpaul */ 78651432Swpaul if (mii_phy_probe(dev, &sc->vr_miibus, 78751432Swpaul vr_ifmedia_upd, vr_ifmedia_sts)) { 78841502Swpaul printf("vr%d: MII without any phy!\n", sc->vr_unit); 78949610Swpaul bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); 79049610Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); 79149610Swpaul bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); 79251432Swpaul contigfree(sc->vr_ldata, 79351432Swpaul sizeof(struct vr_list_data), M_DEVBUF); 79449610Swpaul error = ENXIO; 79541502Swpaul goto fail; 79641502Swpaul } 79741502Swpaul 79851432Swpaul callout_handle_init(&sc->vr_stat_ch); 79941502Swpaul 80041502Swpaul /* 80141502Swpaul * Call MI attach routines. 80241502Swpaul */ 80341502Swpaul if_attach(ifp); 80441502Swpaul ether_ifattach(ifp); 80541502Swpaul 80641502Swpaul bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 80741502Swpaul 80841502Swpaulfail: 80941502Swpaul splx(s); 81049610Swpaul return(error); 81141502Swpaul} 81241502Swpaul 81349610Swpaulstatic int vr_detach(dev) 81449610Swpaul device_t dev; 81549610Swpaul{ 81649610Swpaul struct vr_softc *sc; 81749610Swpaul struct ifnet *ifp; 81849610Swpaul int s; 81949610Swpaul 82049610Swpaul s = splimp(); 82149610Swpaul 82249610Swpaul sc = device_get_softc(dev); 82349610Swpaul ifp = &sc->arpcom.ac_if; 82449610Swpaul 82549610Swpaul vr_stop(sc); 82649610Swpaul if_detach(ifp); 82749610Swpaul 82851432Swpaul bus_generic_detach(dev); 82951432Swpaul device_delete_child(dev, sc->vr_miibus); 83051432Swpaul 83149610Swpaul bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); 83249610Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); 83349610Swpaul bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); 83449610Swpaul 83551432Swpaul contigfree(sc->vr_ldata, sizeof(struct vr_list_data), M_DEVBUF); 83649610Swpaul 83749610Swpaul splx(s); 83849610Swpaul 83949610Swpaul return(0); 84049610Swpaul} 84149610Swpaul 84241502Swpaul/* 84341502Swpaul * Initialize the transmit descriptors. 84441502Swpaul */ 84541502Swpaulstatic int vr_list_tx_init(sc) 84641502Swpaul struct vr_softc *sc; 84741502Swpaul{ 84841502Swpaul struct vr_chain_data *cd; 84941502Swpaul struct vr_list_data *ld; 85041502Swpaul int i; 85141502Swpaul 85241502Swpaul cd = &sc->vr_cdata; 85341502Swpaul ld = sc->vr_ldata; 85441502Swpaul for (i = 0; i < VR_TX_LIST_CNT; i++) { 85541502Swpaul cd->vr_tx_chain[i].vr_ptr = &ld->vr_tx_list[i]; 85641502Swpaul if (i == (VR_TX_LIST_CNT - 1)) 85741502Swpaul cd->vr_tx_chain[i].vr_nextdesc = 85841502Swpaul &cd->vr_tx_chain[0]; 85941502Swpaul else 86041502Swpaul cd->vr_tx_chain[i].vr_nextdesc = 86141502Swpaul &cd->vr_tx_chain[i + 1]; 86241502Swpaul } 86341502Swpaul 86441502Swpaul cd->vr_tx_free = &cd->vr_tx_chain[0]; 86541502Swpaul cd->vr_tx_tail = cd->vr_tx_head = NULL; 86641502Swpaul 86741502Swpaul return(0); 86841502Swpaul} 86941502Swpaul 87041502Swpaul 87141502Swpaul/* 87241502Swpaul * Initialize the RX descriptors and allocate mbufs for them. Note that 87341502Swpaul * we arrange the descriptors in a closed ring, so that the last descriptor 87441502Swpaul * points back to the first. 87541502Swpaul */ 87641502Swpaulstatic int vr_list_rx_init(sc) 87741502Swpaul struct vr_softc *sc; 87841502Swpaul{ 87941502Swpaul struct vr_chain_data *cd; 88041502Swpaul struct vr_list_data *ld; 88141502Swpaul int i; 88241502Swpaul 88341502Swpaul cd = &sc->vr_cdata; 88441502Swpaul ld = sc->vr_ldata; 88541502Swpaul 88641502Swpaul for (i = 0; i < VR_RX_LIST_CNT; i++) { 88741502Swpaul cd->vr_rx_chain[i].vr_ptr = 88841502Swpaul (struct vr_desc *)&ld->vr_rx_list[i]; 88949610Swpaul if (vr_newbuf(sc, &cd->vr_rx_chain[i], NULL) == ENOBUFS) 89041502Swpaul return(ENOBUFS); 89141502Swpaul if (i == (VR_RX_LIST_CNT - 1)) { 89241502Swpaul cd->vr_rx_chain[i].vr_nextdesc = 89341502Swpaul &cd->vr_rx_chain[0]; 89441502Swpaul ld->vr_rx_list[i].vr_next = 89541502Swpaul vtophys(&ld->vr_rx_list[0]); 89641502Swpaul } else { 89741502Swpaul cd->vr_rx_chain[i].vr_nextdesc = 89841502Swpaul &cd->vr_rx_chain[i + 1]; 89941502Swpaul ld->vr_rx_list[i].vr_next = 90041502Swpaul vtophys(&ld->vr_rx_list[i + 1]); 90141502Swpaul } 90241502Swpaul } 90341502Swpaul 90441502Swpaul cd->vr_rx_head = &cd->vr_rx_chain[0]; 90541502Swpaul 90641502Swpaul return(0); 90741502Swpaul} 90841502Swpaul 90941502Swpaul/* 91041502Swpaul * Initialize an RX descriptor and attach an MBUF cluster. 91141502Swpaul * Note: the length fields are only 11 bits wide, which means the 91241502Swpaul * largest size we can specify is 2047. This is important because 91341502Swpaul * MCLBYTES is 2048, so we have to subtract one otherwise we'll 91441502Swpaul * overflow the field and make a mess. 91541502Swpaul */ 91649610Swpaulstatic int vr_newbuf(sc, c, m) 91741502Swpaul struct vr_softc *sc; 91841502Swpaul struct vr_chain_onefrag *c; 91949610Swpaul struct mbuf *m; 92041502Swpaul{ 92141502Swpaul struct mbuf *m_new = NULL; 92241502Swpaul 92349610Swpaul if (m == NULL) { 92449610Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 92549610Swpaul if (m_new == NULL) { 92649610Swpaul printf("vr%d: no memory for rx list " 92749610Swpaul "-- packet dropped!\n", sc->vr_unit); 92849610Swpaul return(ENOBUFS); 92949610Swpaul } 93041502Swpaul 93149610Swpaul MCLGET(m_new, M_DONTWAIT); 93249610Swpaul if (!(m_new->m_flags & M_EXT)) { 93349610Swpaul printf("vr%d: no memory for rx list " 93449610Swpaul "-- packet dropped!\n", sc->vr_unit); 93549610Swpaul m_freem(m_new); 93649610Swpaul return(ENOBUFS); 93749610Swpaul } 93849610Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 93949610Swpaul } else { 94049610Swpaul m_new = m; 94149610Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 94249610Swpaul m_new->m_data = m_new->m_ext.ext_buf; 94341502Swpaul } 94441502Swpaul 94549610Swpaul m_adj(m_new, sizeof(u_int64_t)); 94649610Swpaul 94741502Swpaul c->vr_mbuf = m_new; 94841502Swpaul c->vr_ptr->vr_status = VR_RXSTAT; 94941502Swpaul c->vr_ptr->vr_data = vtophys(mtod(m_new, caddr_t)); 95042491Swpaul c->vr_ptr->vr_ctl = VR_RXCTL | VR_RXLEN; 95141502Swpaul 95241502Swpaul return(0); 95341502Swpaul} 95441502Swpaul 95541502Swpaul/* 95641502Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to 95741502Swpaul * the higher level protocols. 95841502Swpaul */ 95941502Swpaulstatic void vr_rxeof(sc) 96041502Swpaul struct vr_softc *sc; 96141502Swpaul{ 96241502Swpaul struct ether_header *eh; 96341502Swpaul struct mbuf *m; 96441502Swpaul struct ifnet *ifp; 96541502Swpaul struct vr_chain_onefrag *cur_rx; 96641502Swpaul int total_len = 0; 96741502Swpaul u_int32_t rxstat; 96841502Swpaul 96941502Swpaul ifp = &sc->arpcom.ac_if; 97041502Swpaul 97141502Swpaul while(!((rxstat = sc->vr_cdata.vr_rx_head->vr_ptr->vr_status) & 97241502Swpaul VR_RXSTAT_OWN)) { 97349610Swpaul struct mbuf *m0 = NULL; 97449610Swpaul 97541502Swpaul cur_rx = sc->vr_cdata.vr_rx_head; 97641502Swpaul sc->vr_cdata.vr_rx_head = cur_rx->vr_nextdesc; 97749610Swpaul m = cur_rx->vr_mbuf; 97841502Swpaul 97941502Swpaul /* 98041502Swpaul * If an error occurs, update stats, clear the 98141502Swpaul * status word and leave the mbuf cluster in place: 98241502Swpaul * it should simply get re-used next time this descriptor 98341502Swpaul * comes up in the ring. 98441502Swpaul */ 98541502Swpaul if (rxstat & VR_RXSTAT_RXERR) { 98641502Swpaul ifp->if_ierrors++; 98741502Swpaul printf("vr%d: rx error: ", sc->vr_unit); 98841502Swpaul switch(rxstat & 0x000000FF) { 98941502Swpaul case VR_RXSTAT_CRCERR: 99041502Swpaul printf("crc error\n"); 99141502Swpaul break; 99241502Swpaul case VR_RXSTAT_FRAMEALIGNERR: 99341502Swpaul printf("frame alignment error\n"); 99441502Swpaul break; 99541502Swpaul case VR_RXSTAT_FIFOOFLOW: 99641502Swpaul printf("FIFO overflow\n"); 99741502Swpaul break; 99841502Swpaul case VR_RXSTAT_GIANT: 99941502Swpaul printf("received giant packet\n"); 100041502Swpaul break; 100141502Swpaul case VR_RXSTAT_RUNT: 100241502Swpaul printf("received runt packet\n"); 100341502Swpaul break; 100441502Swpaul case VR_RXSTAT_BUSERR: 100541502Swpaul printf("system bus error\n"); 100641502Swpaul break; 100741502Swpaul case VR_RXSTAT_BUFFERR: 100841502Swpaul printf("rx buffer error\n"); 100941502Swpaul break; 101041502Swpaul default: 101141502Swpaul printf("unknown rx error\n"); 101241502Swpaul break; 101341502Swpaul } 101449610Swpaul vr_newbuf(sc, cur_rx, m); 101541502Swpaul continue; 101641502Swpaul } 101741502Swpaul 101841502Swpaul /* No errors; receive the packet. */ 101941502Swpaul total_len = VR_RXBYTES(cur_rx->vr_ptr->vr_status); 102041502Swpaul 102141502Swpaul /* 102242048Swpaul * XXX The VIA Rhine chip includes the CRC with every 102342048Swpaul * received frame, and there's no way to turn this 102442048Swpaul * behavior off (at least, I can't find anything in 102542048Swpaul * the manual that explains how to do it) so we have 102642048Swpaul * to trim off the CRC manually. 102742048Swpaul */ 102842048Swpaul total_len -= ETHER_CRC_LEN; 102942048Swpaul 103049610Swpaul m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, 103149610Swpaul total_len + ETHER_ALIGN, 0, ifp, NULL); 103249610Swpaul vr_newbuf(sc, cur_rx, m); 103349610Swpaul if (m0 == NULL) { 103441502Swpaul ifp->if_ierrors++; 103541502Swpaul continue; 103641502Swpaul } 103749610Swpaul m_adj(m0, ETHER_ALIGN); 103849610Swpaul m = m0; 103941502Swpaul 104041502Swpaul ifp->if_ipackets++; 104141502Swpaul eh = mtod(m, struct ether_header *); 104249610Swpaul 104341502Swpaul /* Remove header from mbuf and pass it on. */ 104441502Swpaul m_adj(m, sizeof(struct ether_header)); 104541502Swpaul ether_input(ifp, eh, m); 104641502Swpaul } 104741502Swpaul 104841502Swpaul return; 104941502Swpaul} 105041502Swpaul 105141502Swpaulvoid vr_rxeoc(sc) 105241502Swpaul struct vr_softc *sc; 105341502Swpaul{ 105441502Swpaul 105541502Swpaul vr_rxeof(sc); 105641502Swpaul VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); 105741502Swpaul CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); 105841502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); 105941502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_GO); 106041502Swpaul 106141502Swpaul return; 106241502Swpaul} 106341502Swpaul 106441502Swpaul/* 106541502Swpaul * A frame was downloaded to the chip. It's safe for us to clean up 106641502Swpaul * the list buffers. 106741502Swpaul */ 106841502Swpaul 106941502Swpaulstatic void vr_txeof(sc) 107041502Swpaul struct vr_softc *sc; 107141502Swpaul{ 107241502Swpaul struct vr_chain *cur_tx; 107341502Swpaul struct ifnet *ifp; 107441502Swpaul 107541502Swpaul ifp = &sc->arpcom.ac_if; 107641502Swpaul 107741502Swpaul /* Clear the timeout timer. */ 107841502Swpaul ifp->if_timer = 0; 107941502Swpaul 108041502Swpaul /* Sanity check. */ 108141502Swpaul if (sc->vr_cdata.vr_tx_head == NULL) 108241502Swpaul return; 108341502Swpaul 108441502Swpaul /* 108541502Swpaul * Go through our tx list and free mbufs for those 108641502Swpaul * frames that have been transmitted. 108741502Swpaul */ 108841502Swpaul while(sc->vr_cdata.vr_tx_head->vr_mbuf != NULL) { 108941502Swpaul u_int32_t txstat; 109041502Swpaul 109141502Swpaul cur_tx = sc->vr_cdata.vr_tx_head; 109241502Swpaul txstat = cur_tx->vr_ptr->vr_status; 109341502Swpaul 109442491Swpaul if (txstat & VR_TXSTAT_OWN) 109541502Swpaul break; 109641502Swpaul 109741502Swpaul if (txstat & VR_TXSTAT_ERRSUM) { 109841502Swpaul ifp->if_oerrors++; 109941502Swpaul if (txstat & VR_TXSTAT_DEFER) 110041502Swpaul ifp->if_collisions++; 110141502Swpaul if (txstat & VR_TXSTAT_LATECOLL) 110241502Swpaul ifp->if_collisions++; 110341502Swpaul } 110441502Swpaul 110541502Swpaul ifp->if_collisions +=(txstat & VR_TXSTAT_COLLCNT) >> 3; 110641502Swpaul 110741502Swpaul ifp->if_opackets++; 110851432Swpaul if (cur_tx->vr_mbuf != NULL) { 110951432Swpaul m_freem(cur_tx->vr_mbuf); 111051432Swpaul cur_tx->vr_mbuf = NULL; 111151432Swpaul } 111241502Swpaul 111341502Swpaul if (sc->vr_cdata.vr_tx_head == sc->vr_cdata.vr_tx_tail) { 111441502Swpaul sc->vr_cdata.vr_tx_head = NULL; 111541502Swpaul sc->vr_cdata.vr_tx_tail = NULL; 111641502Swpaul break; 111741502Swpaul } 111841502Swpaul 111941502Swpaul sc->vr_cdata.vr_tx_head = cur_tx->vr_nextdesc; 112041502Swpaul } 112141502Swpaul 112241502Swpaul return; 112341502Swpaul} 112441502Swpaul 112541502Swpaul/* 112641502Swpaul * TX 'end of channel' interrupt handler. 112741502Swpaul */ 112841502Swpaulstatic void vr_txeoc(sc) 112941502Swpaul struct vr_softc *sc; 113041502Swpaul{ 113141502Swpaul struct ifnet *ifp; 113241502Swpaul 113341502Swpaul ifp = &sc->arpcom.ac_if; 113441502Swpaul 113541502Swpaul ifp->if_timer = 0; 113641502Swpaul 113741502Swpaul if (sc->vr_cdata.vr_tx_head == NULL) { 113841502Swpaul ifp->if_flags &= ~IFF_OACTIVE; 113941502Swpaul sc->vr_cdata.vr_tx_tail = NULL; 114041502Swpaul } 114141502Swpaul 114241502Swpaul return; 114341502Swpaul} 114441502Swpaul 114551432Swpaulstatic void vr_tick(xsc) 114651432Swpaul void *xsc; 114751432Swpaul{ 114851432Swpaul struct vr_softc *sc; 114951432Swpaul struct mii_data *mii; 115051432Swpaul int s; 115151432Swpaul 115251432Swpaul s = splimp(); 115351432Swpaul 115451432Swpaul sc = xsc; 115551432Swpaul mii = device_get_softc(sc->vr_miibus); 115651432Swpaul mii_tick(mii); 115751432Swpaul 115851432Swpaul sc->vr_stat_ch = timeout(vr_tick, sc, hz); 115951432Swpaul 116051432Swpaul splx(s); 116151432Swpaul 116251432Swpaul return; 116351432Swpaul} 116451432Swpaul 116541502Swpaulstatic void vr_intr(arg) 116641502Swpaul void *arg; 116741502Swpaul{ 116841502Swpaul struct vr_softc *sc; 116941502Swpaul struct ifnet *ifp; 117041502Swpaul u_int16_t status; 117141502Swpaul 117241502Swpaul sc = arg; 117341502Swpaul ifp = &sc->arpcom.ac_if; 117441502Swpaul 117541502Swpaul /* Supress unwanted interrupts. */ 117641502Swpaul if (!(ifp->if_flags & IFF_UP)) { 117741502Swpaul vr_stop(sc); 117841502Swpaul return; 117941502Swpaul } 118041502Swpaul 118141502Swpaul /* Disable interrupts. */ 118241502Swpaul CSR_WRITE_2(sc, VR_IMR, 0x0000); 118341502Swpaul 118441502Swpaul for (;;) { 118541502Swpaul 118641502Swpaul status = CSR_READ_2(sc, VR_ISR); 118741502Swpaul if (status) 118841502Swpaul CSR_WRITE_2(sc, VR_ISR, status); 118941502Swpaul 119041502Swpaul if ((status & VR_INTRS) == 0) 119141502Swpaul break; 119241502Swpaul 119341502Swpaul if (status & VR_ISR_RX_OK) 119441502Swpaul vr_rxeof(sc); 119541502Swpaul 119641502Swpaul if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) || 119741502Swpaul (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW) || 119841502Swpaul (status & VR_ISR_RX_DROPPED)) { 119941502Swpaul vr_rxeof(sc); 120041502Swpaul vr_rxeoc(sc); 120141502Swpaul } 120241502Swpaul 120341502Swpaul if (status & VR_ISR_TX_OK) { 120441502Swpaul vr_txeof(sc); 120541502Swpaul vr_txeoc(sc); 120641502Swpaul } 120741502Swpaul 120841502Swpaul if ((status & VR_ISR_TX_UNDERRUN)||(status & VR_ISR_TX_ABRT)){ 120941502Swpaul ifp->if_oerrors++; 121041502Swpaul vr_txeof(sc); 121141502Swpaul if (sc->vr_cdata.vr_tx_head != NULL) { 121241502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON); 121341502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO); 121441502Swpaul } 121541502Swpaul } 121641502Swpaul 121741502Swpaul if (status & VR_ISR_BUSERR) { 121841502Swpaul vr_reset(sc); 121941502Swpaul vr_init(sc); 122041502Swpaul } 122141502Swpaul } 122241502Swpaul 122341502Swpaul /* Re-enable interrupts. */ 122441502Swpaul CSR_WRITE_2(sc, VR_IMR, VR_INTRS); 122541502Swpaul 122641502Swpaul if (ifp->if_snd.ifq_head != NULL) { 122741502Swpaul vr_start(ifp); 122841502Swpaul } 122941502Swpaul 123041502Swpaul return; 123141502Swpaul} 123241502Swpaul 123341502Swpaul/* 123441502Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 123541502Swpaul * pointers to the fragment pointers. 123641502Swpaul */ 123741502Swpaulstatic int vr_encap(sc, c, m_head) 123841502Swpaul struct vr_softc *sc; 123941502Swpaul struct vr_chain *c; 124041502Swpaul struct mbuf *m_head; 124141502Swpaul{ 124241502Swpaul int frag = 0; 124341502Swpaul struct vr_desc *f = NULL; 124441502Swpaul int total_len; 124541502Swpaul struct mbuf *m; 124641502Swpaul 124741502Swpaul m = m_head; 124841502Swpaul total_len = 0; 124941502Swpaul 125041502Swpaul /* 125141502Swpaul * The VIA Rhine wants packet buffers to be longword 125241502Swpaul * aligned, but very often our mbufs aren't. Rather than 125341502Swpaul * waste time trying to decide when to copy and when not 125441502Swpaul * to copy, just do it all the time. 125541502Swpaul */ 125641502Swpaul if (m != NULL) { 125741502Swpaul struct mbuf *m_new = NULL; 125841502Swpaul 125941502Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 126041502Swpaul if (m_new == NULL) { 126141502Swpaul printf("vr%d: no memory for tx list", sc->vr_unit); 126241502Swpaul return(1); 126341502Swpaul } 126441502Swpaul if (m_head->m_pkthdr.len > MHLEN) { 126541502Swpaul MCLGET(m_new, M_DONTWAIT); 126641502Swpaul if (!(m_new->m_flags & M_EXT)) { 126741502Swpaul m_freem(m_new); 126841502Swpaul printf("vr%d: no memory for tx list", 126941502Swpaul sc->vr_unit); 127041502Swpaul return(1); 127141502Swpaul } 127241502Swpaul } 127341502Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 127441502Swpaul mtod(m_new, caddr_t)); 127541502Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 127641502Swpaul m_freem(m_head); 127741502Swpaul m_head = m_new; 127841502Swpaul /* 127941502Swpaul * The Rhine chip doesn't auto-pad, so we have to make 128041502Swpaul * sure to pad short frames out to the minimum frame length 128141502Swpaul * ourselves. 128241502Swpaul */ 128341502Swpaul if (m_head->m_len < VR_MIN_FRAMELEN) { 128441502Swpaul m_new->m_pkthdr.len += VR_MIN_FRAMELEN - m_new->m_len; 128541502Swpaul m_new->m_len = m_new->m_pkthdr.len; 128641502Swpaul } 128741502Swpaul f = c->vr_ptr; 128841502Swpaul f->vr_data = vtophys(mtod(m_new, caddr_t)); 128941502Swpaul f->vr_ctl = total_len = m_new->m_len; 129041502Swpaul f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG; 129141502Swpaul f->vr_status = 0; 129241502Swpaul frag = 1; 129341502Swpaul } 129441502Swpaul 129541502Swpaul c->vr_mbuf = m_head; 129642491Swpaul c->vr_ptr->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT; 129741502Swpaul c->vr_ptr->vr_next = vtophys(c->vr_nextdesc->vr_ptr); 129841502Swpaul 129941502Swpaul return(0); 130041502Swpaul} 130141502Swpaul 130241502Swpaul/* 130341502Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 130441502Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 130541502Swpaul * copy of the pointers since the transmit list fragment pointers are 130641502Swpaul * physical addresses. 130741502Swpaul */ 130841502Swpaul 130941502Swpaulstatic void vr_start(ifp) 131041502Swpaul struct ifnet *ifp; 131141502Swpaul{ 131241502Swpaul struct vr_softc *sc; 131341502Swpaul struct mbuf *m_head = NULL; 131441502Swpaul struct vr_chain *cur_tx = NULL, *start_tx; 131541502Swpaul 131641502Swpaul sc = ifp->if_softc; 131741502Swpaul 131851432Swpaul if (ifp->if_flags & IFF_OACTIVE) 131941502Swpaul return; 132041502Swpaul 132141502Swpaul /* 132241502Swpaul * Check for an available queue slot. If there are none, 132341502Swpaul * punt. 132441502Swpaul */ 132541502Swpaul if (sc->vr_cdata.vr_tx_free->vr_mbuf != NULL) { 132641502Swpaul ifp->if_flags |= IFF_OACTIVE; 132741502Swpaul return; 132841502Swpaul } 132941502Swpaul 133041502Swpaul start_tx = sc->vr_cdata.vr_tx_free; 133141502Swpaul 133241502Swpaul while(sc->vr_cdata.vr_tx_free->vr_mbuf == NULL) { 133341502Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 133441502Swpaul if (m_head == NULL) 133541502Swpaul break; 133641502Swpaul 133741502Swpaul /* Pick a descriptor off the free list. */ 133841502Swpaul cur_tx = sc->vr_cdata.vr_tx_free; 133941502Swpaul sc->vr_cdata.vr_tx_free = cur_tx->vr_nextdesc; 134041502Swpaul 134141502Swpaul /* Pack the data into the descriptor. */ 134241502Swpaul vr_encap(sc, cur_tx, m_head); 134341502Swpaul 134441502Swpaul if (cur_tx != start_tx) 134541502Swpaul VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; 134641502Swpaul 134741502Swpaul /* 134841502Swpaul * If there's a BPF listener, bounce a copy of this frame 134941502Swpaul * to him. 135041502Swpaul */ 135141502Swpaul if (ifp->if_bpf) 135241502Swpaul bpf_mtap(ifp, cur_tx->vr_mbuf); 135351583Swpaul 135442491Swpaul VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; 135551432Swpaul VR_SETBIT16(sc, VR_COMMAND, /*VR_CMD_TX_ON|*/VR_CMD_TX_GO); 135641502Swpaul } 135741502Swpaul 135841502Swpaul /* 135941526Swpaul * If there are no frames queued, bail. 136041526Swpaul */ 136141526Swpaul if (cur_tx == NULL) 136241526Swpaul return; 136341526Swpaul 136441502Swpaul sc->vr_cdata.vr_tx_tail = cur_tx; 136541502Swpaul 136642491Swpaul if (sc->vr_cdata.vr_tx_head == NULL) 136741502Swpaul sc->vr_cdata.vr_tx_head = start_tx; 136841502Swpaul 136941502Swpaul /* 137041502Swpaul * Set a timeout in case the chip goes out to lunch. 137141502Swpaul */ 137241502Swpaul ifp->if_timer = 5; 137341502Swpaul 137441502Swpaul return; 137541502Swpaul} 137641502Swpaul 137741502Swpaulstatic void vr_init(xsc) 137841502Swpaul void *xsc; 137941502Swpaul{ 138041502Swpaul struct vr_softc *sc = xsc; 138141502Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 138251432Swpaul struct mii_data *mii; 138341502Swpaul int s; 138441502Swpaul 138541502Swpaul s = splimp(); 138641502Swpaul 138751432Swpaul mii = device_get_softc(sc->vr_miibus); 138841502Swpaul 138941502Swpaul /* 139041502Swpaul * Cancel pending I/O and free all RX/TX buffers. 139141502Swpaul */ 139241502Swpaul vr_stop(sc); 139341502Swpaul vr_reset(sc); 139441502Swpaul 139541502Swpaul VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_THRESH); 139641502Swpaul VR_SETBIT(sc, VR_RXCFG, VR_RXTHRESH_STORENFWD); 139741502Swpaul 139841502Swpaul VR_CLRBIT(sc, VR_TXCFG, VR_TXCFG_TX_THRESH); 139941502Swpaul VR_SETBIT(sc, VR_TXCFG, VR_TXTHRESH_STORENFWD); 140041502Swpaul 140141502Swpaul /* Init circular RX list. */ 140241502Swpaul if (vr_list_rx_init(sc) == ENOBUFS) { 140341502Swpaul printf("vr%d: initialization failed: no " 140441502Swpaul "memory for rx buffers\n", sc->vr_unit); 140541502Swpaul vr_stop(sc); 140641502Swpaul (void)splx(s); 140741502Swpaul return; 140841502Swpaul } 140941502Swpaul 141041502Swpaul /* 141141502Swpaul * Init tx descriptors. 141241502Swpaul */ 141341502Swpaul vr_list_tx_init(sc); 141441502Swpaul 141541502Swpaul /* If we want promiscuous mode, set the allframes bit. */ 141641502Swpaul if (ifp->if_flags & IFF_PROMISC) 141741502Swpaul VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); 141841502Swpaul else 141941502Swpaul VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); 142041502Swpaul 142141502Swpaul /* Set capture broadcast bit to capture broadcast frames. */ 142241502Swpaul if (ifp->if_flags & IFF_BROADCAST) 142341502Swpaul VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); 142441502Swpaul else 142541502Swpaul VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); 142641502Swpaul 142741502Swpaul /* 142841502Swpaul * Program the multicast filter, if necessary. 142941502Swpaul */ 143041502Swpaul vr_setmulti(sc); 143141502Swpaul 143241502Swpaul /* 143341502Swpaul * Load the address of the RX list. 143441502Swpaul */ 143541502Swpaul CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); 143641502Swpaul 143741502Swpaul /* Enable receiver and transmitter. */ 143841502Swpaul CSR_WRITE_2(sc, VR_COMMAND, VR_CMD_TX_NOPOLL|VR_CMD_START| 143941502Swpaul VR_CMD_TX_ON|VR_CMD_RX_ON| 144041502Swpaul VR_CMD_RX_GO); 144141502Swpaul 144241502Swpaul CSR_WRITE_4(sc, VR_TXADDR, vtophys(&sc->vr_ldata->vr_tx_list[0])); 144341502Swpaul 144441502Swpaul /* 144541502Swpaul * Enable interrupts. 144641502Swpaul */ 144741502Swpaul CSR_WRITE_2(sc, VR_ISR, 0xFFFF); 144841502Swpaul CSR_WRITE_2(sc, VR_IMR, VR_INTRS); 144941502Swpaul 145051432Swpaul mii_mediachg(mii); 145141502Swpaul 145241502Swpaul ifp->if_flags |= IFF_RUNNING; 145341502Swpaul ifp->if_flags &= ~IFF_OACTIVE; 145441502Swpaul 145541502Swpaul (void)splx(s); 145641502Swpaul 145751432Swpaul sc->vr_stat_ch = timeout(vr_tick, sc, hz); 145851432Swpaul 145941502Swpaul return; 146041502Swpaul} 146141502Swpaul 146241502Swpaul/* 146341502Swpaul * Set media options. 146441502Swpaul */ 146541502Swpaulstatic int vr_ifmedia_upd(ifp) 146641502Swpaul struct ifnet *ifp; 146741502Swpaul{ 146841502Swpaul struct vr_softc *sc; 146941502Swpaul 147041502Swpaul sc = ifp->if_softc; 147141502Swpaul 147251432Swpaul if (ifp->if_flags & IFF_UP) 147351432Swpaul vr_init(sc); 147441502Swpaul 147541502Swpaul return(0); 147641502Swpaul} 147741502Swpaul 147841502Swpaul/* 147941502Swpaul * Report current media status. 148041502Swpaul */ 148141502Swpaulstatic void vr_ifmedia_sts(ifp, ifmr) 148241502Swpaul struct ifnet *ifp; 148341502Swpaul struct ifmediareq *ifmr; 148441502Swpaul{ 148541502Swpaul struct vr_softc *sc; 148651432Swpaul struct mii_data *mii; 148741502Swpaul 148841502Swpaul sc = ifp->if_softc; 148951432Swpaul mii = device_get_softc(sc->vr_miibus); 149051432Swpaul mii_pollstat(mii); 149151432Swpaul ifmr->ifm_active = mii->mii_media_active; 149251432Swpaul ifmr->ifm_status = mii->mii_media_status; 149341502Swpaul 149441502Swpaul return; 149541502Swpaul} 149641502Swpaul 149741502Swpaulstatic int vr_ioctl(ifp, command, data) 149841502Swpaul struct ifnet *ifp; 149941502Swpaul u_long command; 150041502Swpaul caddr_t data; 150141502Swpaul{ 150241502Swpaul struct vr_softc *sc = ifp->if_softc; 150341502Swpaul struct ifreq *ifr = (struct ifreq *) data; 150451432Swpaul struct mii_data *mii; 150541502Swpaul int s, error = 0; 150641502Swpaul 150741502Swpaul s = splimp(); 150841502Swpaul 150941502Swpaul switch(command) { 151041502Swpaul case SIOCSIFADDR: 151141502Swpaul case SIOCGIFADDR: 151241502Swpaul case SIOCSIFMTU: 151341502Swpaul error = ether_ioctl(ifp, command, data); 151441502Swpaul break; 151541502Swpaul case SIOCSIFFLAGS: 151641502Swpaul if (ifp->if_flags & IFF_UP) { 151741502Swpaul vr_init(sc); 151841502Swpaul } else { 151941502Swpaul if (ifp->if_flags & IFF_RUNNING) 152041502Swpaul vr_stop(sc); 152141502Swpaul } 152241502Swpaul error = 0; 152341502Swpaul break; 152441502Swpaul case SIOCADDMULTI: 152541502Swpaul case SIOCDELMULTI: 152641502Swpaul vr_setmulti(sc); 152741502Swpaul error = 0; 152841502Swpaul break; 152941502Swpaul case SIOCGIFMEDIA: 153041502Swpaul case SIOCSIFMEDIA: 153151432Swpaul mii = device_get_softc(sc->vr_miibus); 153251432Swpaul error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 153341502Swpaul break; 153441502Swpaul default: 153541502Swpaul error = EINVAL; 153641502Swpaul break; 153741502Swpaul } 153841502Swpaul 153941502Swpaul (void)splx(s); 154041502Swpaul 154141502Swpaul return(error); 154241502Swpaul} 154341502Swpaul 154441502Swpaulstatic void vr_watchdog(ifp) 154541502Swpaul struct ifnet *ifp; 154641502Swpaul{ 154741502Swpaul struct vr_softc *sc; 154841502Swpaul 154941502Swpaul sc = ifp->if_softc; 155041502Swpaul 155141502Swpaul ifp->if_oerrors++; 155241502Swpaul printf("vr%d: watchdog timeout\n", sc->vr_unit); 155341502Swpaul 155441502Swpaul vr_stop(sc); 155541502Swpaul vr_reset(sc); 155641502Swpaul vr_init(sc); 155741502Swpaul 155841502Swpaul if (ifp->if_snd.ifq_head != NULL) 155941502Swpaul vr_start(ifp); 156041502Swpaul 156141502Swpaul return; 156241502Swpaul} 156341502Swpaul 156441502Swpaul/* 156541502Swpaul * Stop the adapter and free any mbufs allocated to the 156641502Swpaul * RX and TX lists. 156741502Swpaul */ 156841502Swpaulstatic void vr_stop(sc) 156941502Swpaul struct vr_softc *sc; 157041502Swpaul{ 157141502Swpaul register int i; 157241502Swpaul struct ifnet *ifp; 157341502Swpaul 157441502Swpaul ifp = &sc->arpcom.ac_if; 157541502Swpaul ifp->if_timer = 0; 157641502Swpaul 157751432Swpaul untimeout(vr_tick, sc, sc->vr_stat_ch); 157851432Swpaul 157941502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_STOP); 158041502Swpaul VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_RX_ON|VR_CMD_TX_ON)); 158141502Swpaul CSR_WRITE_2(sc, VR_IMR, 0x0000); 158241502Swpaul CSR_WRITE_4(sc, VR_TXADDR, 0x00000000); 158341502Swpaul CSR_WRITE_4(sc, VR_RXADDR, 0x00000000); 158441502Swpaul 158541502Swpaul /* 158641502Swpaul * Free data in the RX lists. 158741502Swpaul */ 158841502Swpaul for (i = 0; i < VR_RX_LIST_CNT; i++) { 158941502Swpaul if (sc->vr_cdata.vr_rx_chain[i].vr_mbuf != NULL) { 159041502Swpaul m_freem(sc->vr_cdata.vr_rx_chain[i].vr_mbuf); 159141502Swpaul sc->vr_cdata.vr_rx_chain[i].vr_mbuf = NULL; 159241502Swpaul } 159341502Swpaul } 159441502Swpaul bzero((char *)&sc->vr_ldata->vr_rx_list, 159541502Swpaul sizeof(sc->vr_ldata->vr_rx_list)); 159641502Swpaul 159741502Swpaul /* 159841502Swpaul * Free the TX list buffers. 159941502Swpaul */ 160041502Swpaul for (i = 0; i < VR_TX_LIST_CNT; i++) { 160141502Swpaul if (sc->vr_cdata.vr_tx_chain[i].vr_mbuf != NULL) { 160241502Swpaul m_freem(sc->vr_cdata.vr_tx_chain[i].vr_mbuf); 160341502Swpaul sc->vr_cdata.vr_tx_chain[i].vr_mbuf = NULL; 160441502Swpaul } 160541502Swpaul } 160641502Swpaul 160741502Swpaul bzero((char *)&sc->vr_ldata->vr_tx_list, 160841502Swpaul sizeof(sc->vr_ldata->vr_tx_list)); 160941502Swpaul 161041502Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 161141502Swpaul 161241502Swpaul return; 161341502Swpaul} 161441502Swpaul 161541502Swpaul/* 161641502Swpaul * Stop all chip I/O so that the kernel's probe routines don't 161741502Swpaul * get confused by errant DMAs when rebooting. 161841502Swpaul */ 161949610Swpaulstatic void vr_shutdown(dev) 162049610Swpaul device_t dev; 162141502Swpaul{ 162249610Swpaul struct vr_softc *sc; 162341502Swpaul 162449610Swpaul sc = device_get_softc(dev); 162549610Swpaul 162641502Swpaul vr_stop(sc); 162741502Swpaul 162841502Swpaul return; 162941502Swpaul} 1630