if_vr.c revision 51533
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 51533 1999-09-22 06:08:11Z wpaul $ 3341502Swpaul */ 3441502Swpaul 3541502Swpaul/* 3641502Swpaul * VIA Rhine fast ethernet PCI NIC driver 3741502Swpaul * 3841502Swpaul * Supports various network adapters based on the VIA Rhine 3941502Swpaul * and Rhine II PCI controllers, including the D-Link DFE530TX. 4041502Swpaul * Datasheets are available at http://www.via.com.tw. 4141502Swpaul * 4241502Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu> 4341502Swpaul * Electrical Engineering Department 4441502Swpaul * Columbia University, New York City 4541502Swpaul */ 4641502Swpaul 4741502Swpaul/* 4841502Swpaul * The VIA Rhine controllers are similar in some respects to the 4941502Swpaul * the DEC tulip chips, except less complicated. The controller 5041502Swpaul * uses an MII bus and an external physical layer interface. The 5141502Swpaul * receiver has a one entry perfect filter and a 64-bit hash table 5241502Swpaul * multicast filter. Transmit and receive descriptors are similar 5341502Swpaul * to the tulip. 5441502Swpaul * 5541502Swpaul * The Rhine has a serious flaw in its transmit DMA mechanism: 5641502Swpaul * transmit buffers must be longword aligned. Unfortunately, 5741502Swpaul * FreeBSD doesn't guarantee that mbufs will be filled in starting 5841502Swpaul * at longword boundaries, so we have to do a buffer copy before 5941502Swpaul * transmission. 6041502Swpaul */ 6141502Swpaul 6248645Sdes#include "bpf.h" 6341502Swpaul 6441502Swpaul#include <sys/param.h> 6541502Swpaul#include <sys/systm.h> 6641502Swpaul#include <sys/sockio.h> 6741502Swpaul#include <sys/mbuf.h> 6841502Swpaul#include <sys/malloc.h> 6941502Swpaul#include <sys/kernel.h> 7041502Swpaul#include <sys/socket.h> 7141502Swpaul 7241502Swpaul#include <net/if.h> 7341502Swpaul#include <net/if_arp.h> 7441502Swpaul#include <net/ethernet.h> 7541502Swpaul#include <net/if_dl.h> 7641502Swpaul#include <net/if_media.h> 7741502Swpaul 7848645Sdes#if NBPF > 0 7941502Swpaul#include <net/bpf.h> 8041502Swpaul#endif 8141502Swpaul 8251354Swpaul#include "opt_bdg.h" 8351354Swpaul#ifdef BRIDGE 8451354Swpaul#include <net/bridge.h> 8551354Swpaul#endif /* BRIDGE */ 8651354Swpaul 8741502Swpaul#include <vm/vm.h> /* for vtophys */ 8841502Swpaul#include <vm/pmap.h> /* for vtophys */ 8941502Swpaul#include <machine/clock.h> /* for DELAY */ 9041502Swpaul#include <machine/bus_pio.h> 9141502Swpaul#include <machine/bus_memio.h> 9241502Swpaul#include <machine/bus.h> 9349610Swpaul#include <machine/resource.h> 9449610Swpaul#include <sys/bus.h> 9549610Swpaul#include <sys/rman.h> 9641502Swpaul 9751432Swpaul#include <dev/mii/mii.h> 9851432Swpaul#include <dev/mii/miivar.h> 9951432Swpaul 10041502Swpaul#include <pci/pcireg.h> 10141502Swpaul#include <pci/pcivar.h> 10241502Swpaul 10341502Swpaul#define VR_USEIOSPACE 10441502Swpaul 10541502Swpaul#include <pci/if_vrreg.h> 10641502Swpaul 10751432Swpaul/* "controller miibus0" required. See GENERIC if you get errors here. */ 10851432Swpaul#include "miibus_if.h" 10951432Swpaul 11041502Swpaul#ifndef lint 11141591Sarchiestatic const char rcsid[] = 11250477Speter "$FreeBSD: head/sys/dev/vr/if_vr.c 51533 1999-09-22 06:08:11Z wpaul $"; 11341502Swpaul#endif 11441502Swpaul 11541502Swpaul/* 11641502Swpaul * Various supported device vendors/types and their names. 11741502Swpaul */ 11841502Swpaulstatic struct vr_type vr_devs[] = { 11941502Swpaul { VIA_VENDORID, VIA_DEVICEID_RHINE, 12041502Swpaul "VIA VT3043 Rhine I 10/100BaseTX" }, 12141502Swpaul { VIA_VENDORID, VIA_DEVICEID_RHINE_II, 12241502Swpaul "VIA VT86C100A Rhine II 10/100BaseTX" }, 12344238Swpaul { DELTA_VENDORID, DELTA_DEVICEID_RHINE_II, 12444238Swpaul "Delta Electronics Rhine II 10/100BaseTX" }, 12544238Swpaul { ADDTRON_VENDORID, ADDTRON_DEVICEID_RHINE_II, 12644238Swpaul "Addtron Technology Rhine II 10/100BaseTX" }, 12741502Swpaul { 0, 0, NULL } 12841502Swpaul}; 12941502Swpaul 13049610Swpaulstatic int vr_probe __P((device_t)); 13149610Swpaulstatic int vr_attach __P((device_t)); 13249610Swpaulstatic int vr_detach __P((device_t)); 13341502Swpaul 13441502Swpaulstatic int vr_newbuf __P((struct vr_softc *, 13549610Swpaul struct vr_chain_onefrag *, 13649610Swpaul struct mbuf *)); 13741502Swpaulstatic int vr_encap __P((struct vr_softc *, struct vr_chain *, 13841502Swpaul struct mbuf * )); 13941502Swpaul 14041502Swpaulstatic void vr_rxeof __P((struct vr_softc *)); 14141502Swpaulstatic void vr_rxeoc __P((struct vr_softc *)); 14241502Swpaulstatic void vr_txeof __P((struct vr_softc *)); 14341502Swpaulstatic void vr_txeoc __P((struct vr_softc *)); 14451432Swpaulstatic void vr_tick __P((void *)); 14541502Swpaulstatic void vr_intr __P((void *)); 14641502Swpaulstatic void vr_start __P((struct ifnet *)); 14741502Swpaulstatic int vr_ioctl __P((struct ifnet *, u_long, caddr_t)); 14841502Swpaulstatic void vr_init __P((void *)); 14941502Swpaulstatic void vr_stop __P((struct vr_softc *)); 15041502Swpaulstatic void vr_watchdog __P((struct ifnet *)); 15149610Swpaulstatic void vr_shutdown __P((device_t)); 15241502Swpaulstatic int vr_ifmedia_upd __P((struct ifnet *)); 15341502Swpaulstatic void vr_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 15441502Swpaul 15541502Swpaulstatic void vr_mii_sync __P((struct vr_softc *)); 15641502Swpaulstatic void vr_mii_send __P((struct vr_softc *, u_int32_t, int)); 15741502Swpaulstatic int vr_mii_readreg __P((struct vr_softc *, struct vr_mii_frame *)); 15841502Swpaulstatic int vr_mii_writereg __P((struct vr_softc *, struct vr_mii_frame *)); 15951432Swpaulstatic int vr_miibus_readreg __P((device_t, int, int)); 16051432Swpaulstatic int vr_miibus_writereg __P((device_t, int, int, int)); 16151432Swpaulstatic void vr_miibus_statchg __P((device_t)); 16241502Swpaul 16351432Swpaulstatic void vr_setcfg __P((struct vr_softc *, int)); 16441502Swpaulstatic u_int8_t vr_calchash __P((u_int8_t *)); 16541502Swpaulstatic void vr_setmulti __P((struct vr_softc *)); 16641502Swpaulstatic void vr_reset __P((struct vr_softc *)); 16741502Swpaulstatic int vr_list_rx_init __P((struct vr_softc *)); 16841502Swpaulstatic int vr_list_tx_init __P((struct vr_softc *)); 16941502Swpaul 17049610Swpaul#ifdef VR_USEIOSPACE 17149610Swpaul#define VR_RES SYS_RES_IOPORT 17249610Swpaul#define VR_RID VR_PCI_LOIO 17349610Swpaul#else 17449610Swpaul#define VR_RES SYS_RES_MEMORY 17549610Swpaul#define VR_RID VR_PCI_LOMEM 17649610Swpaul#endif 17749610Swpaul 17849610Swpaulstatic device_method_t vr_methods[] = { 17949610Swpaul /* Device interface */ 18049610Swpaul DEVMETHOD(device_probe, vr_probe), 18149610Swpaul DEVMETHOD(device_attach, vr_attach), 18249610Swpaul DEVMETHOD(device_detach, vr_detach), 18349610Swpaul DEVMETHOD(device_shutdown, vr_shutdown), 18451432Swpaul 18551432Swpaul /* bus interface */ 18651432Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 18751432Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 18851432Swpaul 18951432Swpaul /* MII interface */ 19051432Swpaul DEVMETHOD(miibus_readreg, vr_miibus_readreg), 19151432Swpaul DEVMETHOD(miibus_writereg, vr_miibus_writereg), 19251432Swpaul DEVMETHOD(miibus_statchg, vr_miibus_statchg), 19351432Swpaul 19449610Swpaul { 0, 0 } 19549610Swpaul}; 19649610Swpaul 19749610Swpaulstatic driver_t vr_driver = { 19851455Swpaul "vr", 19949610Swpaul vr_methods, 20049610Swpaul sizeof(struct vr_softc) 20149610Swpaul}; 20249610Swpaul 20349610Swpaulstatic devclass_t vr_devclass; 20449610Swpaul 20551533SwpaulDRIVER_MODULE(if_vr, pci, vr_driver, vr_devclass, 0, 0); 20651473SwpaulDRIVER_MODULE(miibus, vr, miibus_driver, miibus_devclass, 0, 0); 20749610Swpaul 20841502Swpaul#define VR_SETBIT(sc, reg, x) \ 20941502Swpaul CSR_WRITE_1(sc, reg, \ 21041502Swpaul CSR_READ_1(sc, reg) | x) 21141502Swpaul 21241502Swpaul#define VR_CLRBIT(sc, reg, x) \ 21341502Swpaul CSR_WRITE_1(sc, reg, \ 21441502Swpaul CSR_READ_1(sc, reg) & ~x) 21541502Swpaul 21641502Swpaul#define VR_SETBIT16(sc, reg, x) \ 21741502Swpaul CSR_WRITE_2(sc, reg, \ 21841502Swpaul CSR_READ_2(sc, reg) | x) 21941502Swpaul 22041502Swpaul#define VR_CLRBIT16(sc, reg, x) \ 22141502Swpaul CSR_WRITE_2(sc, reg, \ 22241502Swpaul CSR_READ_2(sc, reg) & ~x) 22341502Swpaul 22441502Swpaul#define VR_SETBIT32(sc, reg, x) \ 22541502Swpaul CSR_WRITE_4(sc, reg, \ 22641502Swpaul CSR_READ_4(sc, reg) | x) 22741502Swpaul 22841502Swpaul#define VR_CLRBIT32(sc, reg, x) \ 22941502Swpaul CSR_WRITE_4(sc, reg, \ 23041502Swpaul CSR_READ_4(sc, reg) & ~x) 23141502Swpaul 23241502Swpaul#define SIO_SET(x) \ 23341502Swpaul CSR_WRITE_1(sc, VR_MIICMD, \ 23441502Swpaul CSR_READ_1(sc, VR_MIICMD) | x) 23541502Swpaul 23641502Swpaul#define SIO_CLR(x) \ 23741502Swpaul CSR_WRITE_1(sc, VR_MIICMD, \ 23841502Swpaul CSR_READ_1(sc, VR_MIICMD) & ~x) 23941502Swpaul 24041502Swpaul/* 24141502Swpaul * Sync the PHYs by setting data bit and strobing the clock 32 times. 24241502Swpaul */ 24341502Swpaulstatic void vr_mii_sync(sc) 24441502Swpaul struct vr_softc *sc; 24541502Swpaul{ 24641502Swpaul register int i; 24741502Swpaul 24841502Swpaul SIO_SET(VR_MIICMD_DIR|VR_MIICMD_DATAIN); 24941502Swpaul 25041502Swpaul for (i = 0; i < 32; i++) { 25141502Swpaul SIO_SET(VR_MIICMD_CLK); 25241502Swpaul DELAY(1); 25341502Swpaul SIO_CLR(VR_MIICMD_CLK); 25441502Swpaul DELAY(1); 25541502Swpaul } 25641502Swpaul 25741502Swpaul return; 25841502Swpaul} 25941502Swpaul 26041502Swpaul/* 26141502Swpaul * Clock a series of bits through the MII. 26241502Swpaul */ 26341502Swpaulstatic void vr_mii_send(sc, bits, cnt) 26441502Swpaul struct vr_softc *sc; 26541502Swpaul u_int32_t bits; 26641502Swpaul int cnt; 26741502Swpaul{ 26841502Swpaul int i; 26941502Swpaul 27041502Swpaul SIO_CLR(VR_MIICMD_CLK); 27141502Swpaul 27241502Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 27341502Swpaul if (bits & i) { 27441502Swpaul SIO_SET(VR_MIICMD_DATAIN); 27541502Swpaul } else { 27641502Swpaul SIO_CLR(VR_MIICMD_DATAIN); 27741502Swpaul } 27841502Swpaul DELAY(1); 27941502Swpaul SIO_CLR(VR_MIICMD_CLK); 28041502Swpaul DELAY(1); 28141502Swpaul SIO_SET(VR_MIICMD_CLK); 28241502Swpaul } 28341502Swpaul} 28441502Swpaul 28541502Swpaul/* 28641502Swpaul * Read an PHY register through the MII. 28741502Swpaul */ 28841502Swpaulstatic int vr_mii_readreg(sc, frame) 28941502Swpaul struct vr_softc *sc; 29041502Swpaul struct vr_mii_frame *frame; 29141502Swpaul 29241502Swpaul{ 29341502Swpaul int i, ack, s; 29441502Swpaul 29541502Swpaul s = splimp(); 29641502Swpaul 29741502Swpaul /* 29841502Swpaul * Set up frame for RX. 29941502Swpaul */ 30041502Swpaul frame->mii_stdelim = VR_MII_STARTDELIM; 30141502Swpaul frame->mii_opcode = VR_MII_READOP; 30241502Swpaul frame->mii_turnaround = 0; 30341502Swpaul frame->mii_data = 0; 30441502Swpaul 30541502Swpaul CSR_WRITE_1(sc, VR_MIICMD, 0); 30641502Swpaul VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); 30741502Swpaul 30841502Swpaul /* 30941502Swpaul * Turn on data xmit. 31041502Swpaul */ 31141502Swpaul SIO_SET(VR_MIICMD_DIR); 31241502Swpaul 31341502Swpaul vr_mii_sync(sc); 31441502Swpaul 31541502Swpaul /* 31641502Swpaul * Send command/address info. 31741502Swpaul */ 31841502Swpaul vr_mii_send(sc, frame->mii_stdelim, 2); 31941502Swpaul vr_mii_send(sc, frame->mii_opcode, 2); 32041502Swpaul vr_mii_send(sc, frame->mii_phyaddr, 5); 32141502Swpaul vr_mii_send(sc, frame->mii_regaddr, 5); 32241502Swpaul 32341502Swpaul /* Idle bit */ 32441502Swpaul SIO_CLR((VR_MIICMD_CLK|VR_MIICMD_DATAIN)); 32541502Swpaul DELAY(1); 32641502Swpaul SIO_SET(VR_MIICMD_CLK); 32741502Swpaul DELAY(1); 32841502Swpaul 32941502Swpaul /* Turn off xmit. */ 33041502Swpaul SIO_CLR(VR_MIICMD_DIR); 33141502Swpaul 33241502Swpaul /* Check for ack */ 33341502Swpaul SIO_CLR(VR_MIICMD_CLK); 33441502Swpaul DELAY(1); 33541502Swpaul SIO_SET(VR_MIICMD_CLK); 33641502Swpaul DELAY(1); 33741502Swpaul ack = CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT; 33841502Swpaul 33941502Swpaul /* 34041502Swpaul * Now try reading data bits. If the ack failed, we still 34141502Swpaul * need to clock through 16 cycles to keep the PHY(s) in sync. 34241502Swpaul */ 34341502Swpaul if (ack) { 34441502Swpaul for(i = 0; i < 16; i++) { 34541502Swpaul SIO_CLR(VR_MIICMD_CLK); 34641502Swpaul DELAY(1); 34741502Swpaul SIO_SET(VR_MIICMD_CLK); 34841502Swpaul DELAY(1); 34941502Swpaul } 35041502Swpaul goto fail; 35141502Swpaul } 35241502Swpaul 35341502Swpaul for (i = 0x8000; i; i >>= 1) { 35441502Swpaul SIO_CLR(VR_MIICMD_CLK); 35541502Swpaul DELAY(1); 35641502Swpaul if (!ack) { 35741502Swpaul if (CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT) 35841502Swpaul frame->mii_data |= i; 35941502Swpaul DELAY(1); 36041502Swpaul } 36141502Swpaul SIO_SET(VR_MIICMD_CLK); 36241502Swpaul DELAY(1); 36341502Swpaul } 36441502Swpaul 36541502Swpaulfail: 36641502Swpaul 36741502Swpaul SIO_CLR(VR_MIICMD_CLK); 36841502Swpaul DELAY(1); 36941502Swpaul SIO_SET(VR_MIICMD_CLK); 37041502Swpaul DELAY(1); 37141502Swpaul 37241502Swpaul splx(s); 37341502Swpaul 37441502Swpaul if (ack) 37541502Swpaul return(1); 37641502Swpaul return(0); 37741502Swpaul} 37841502Swpaul 37941502Swpaul/* 38041502Swpaul * Write to a PHY register through the MII. 38141502Swpaul */ 38241502Swpaulstatic int vr_mii_writereg(sc, frame) 38341502Swpaul struct vr_softc *sc; 38441502Swpaul struct vr_mii_frame *frame; 38541502Swpaul 38641502Swpaul{ 38741502Swpaul int s; 38841502Swpaul 38941502Swpaul s = splimp(); 39041502Swpaul 39141502Swpaul CSR_WRITE_1(sc, VR_MIICMD, 0); 39241502Swpaul VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); 39341502Swpaul 39441502Swpaul /* 39541502Swpaul * Set up frame for TX. 39641502Swpaul */ 39741502Swpaul 39841502Swpaul frame->mii_stdelim = VR_MII_STARTDELIM; 39941502Swpaul frame->mii_opcode = VR_MII_WRITEOP; 40041502Swpaul frame->mii_turnaround = VR_MII_TURNAROUND; 40141502Swpaul 40241502Swpaul /* 40341502Swpaul * Turn on data output. 40441502Swpaul */ 40541502Swpaul SIO_SET(VR_MIICMD_DIR); 40641502Swpaul 40741502Swpaul vr_mii_sync(sc); 40841502Swpaul 40941502Swpaul vr_mii_send(sc, frame->mii_stdelim, 2); 41041502Swpaul vr_mii_send(sc, frame->mii_opcode, 2); 41141502Swpaul vr_mii_send(sc, frame->mii_phyaddr, 5); 41241502Swpaul vr_mii_send(sc, frame->mii_regaddr, 5); 41341502Swpaul vr_mii_send(sc, frame->mii_turnaround, 2); 41441502Swpaul vr_mii_send(sc, frame->mii_data, 16); 41541502Swpaul 41641502Swpaul /* Idle bit. */ 41741502Swpaul SIO_SET(VR_MIICMD_CLK); 41841502Swpaul DELAY(1); 41941502Swpaul SIO_CLR(VR_MIICMD_CLK); 42041502Swpaul DELAY(1); 42141502Swpaul 42241502Swpaul /* 42341502Swpaul * Turn off xmit. 42441502Swpaul */ 42541502Swpaul SIO_CLR(VR_MIICMD_DIR); 42641502Swpaul 42741502Swpaul splx(s); 42841502Swpaul 42941502Swpaul return(0); 43041502Swpaul} 43141502Swpaul 43251432Swpaulstatic int vr_miibus_readreg(dev, phy, reg) 43351432Swpaul device_t dev; 43451432Swpaul int phy, reg; 43551432Swpaul{ 43641502Swpaul struct vr_softc *sc; 43741502Swpaul struct vr_mii_frame frame; 43841502Swpaul 43951432Swpaul sc = device_get_softc(dev); 44041502Swpaul bzero((char *)&frame, sizeof(frame)); 44141502Swpaul 44251432Swpaul frame.mii_phyaddr = phy; 44341502Swpaul frame.mii_regaddr = reg; 44441502Swpaul vr_mii_readreg(sc, &frame); 44541502Swpaul 44641502Swpaul return(frame.mii_data); 44741502Swpaul} 44841502Swpaul 44951432Swpaulstatic int vr_miibus_writereg(dev, phy, reg, data) 45051432Swpaul device_t dev; 45151432Swpaul u_int16_t phy, reg, data; 45251432Swpaul{ 45341502Swpaul struct vr_softc *sc; 45441502Swpaul struct vr_mii_frame frame; 45541502Swpaul 45651432Swpaul sc = device_get_softc(dev); 45741502Swpaul bzero((char *)&frame, sizeof(frame)); 45841502Swpaul 45951432Swpaul frame.mii_phyaddr = phy; 46041502Swpaul frame.mii_regaddr = reg; 46141502Swpaul frame.mii_data = data; 46241502Swpaul 46341502Swpaul vr_mii_writereg(sc, &frame); 46441502Swpaul 46551432Swpaul return(0); 46651432Swpaul} 46751432Swpaul 46851432Swpaulstatic void vr_miibus_statchg(dev) 46951432Swpaul device_t dev; 47051432Swpaul{ 47151432Swpaul struct vr_softc *sc; 47251432Swpaul struct mii_data *mii; 47351432Swpaul 47451432Swpaul sc = device_get_softc(dev); 47551432Swpaul mii = device_get_softc(sc->vr_miibus); 47651432Swpaul vr_setcfg(sc, mii->mii_media_active); 47751432Swpaul 47841502Swpaul return; 47941502Swpaul} 48041502Swpaul 48141502Swpaul/* 48241502Swpaul * Calculate CRC of a multicast group address, return the lower 6 bits. 48341502Swpaul */ 48441502Swpaulstatic u_int8_t vr_calchash(addr) 48541502Swpaul u_int8_t *addr; 48641502Swpaul{ 48741502Swpaul u_int32_t crc, carry; 48841502Swpaul int i, j; 48941502Swpaul u_int8_t c; 49041502Swpaul 49141502Swpaul /* Compute CRC for the address value. */ 49241502Swpaul crc = 0xFFFFFFFF; /* initial value */ 49341502Swpaul 49441502Swpaul for (i = 0; i < 6; i++) { 49541502Swpaul c = *(addr + i); 49641502Swpaul for (j = 0; j < 8; j++) { 49741502Swpaul carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); 49841502Swpaul crc <<= 1; 49941502Swpaul c >>= 1; 50041502Swpaul if (carry) 50141502Swpaul crc = (crc ^ 0x04c11db6) | carry; 50241502Swpaul } 50341502Swpaul } 50441502Swpaul 50541502Swpaul /* return the filter bit position */ 50641502Swpaul return((crc >> 26) & 0x0000003F); 50741502Swpaul} 50841502Swpaul 50941502Swpaul/* 51041502Swpaul * Program the 64-bit multicast hash filter. 51141502Swpaul */ 51241502Swpaulstatic void vr_setmulti(sc) 51341502Swpaul struct vr_softc *sc; 51441502Swpaul{ 51541502Swpaul struct ifnet *ifp; 51641502Swpaul int h = 0; 51741502Swpaul u_int32_t hashes[2] = { 0, 0 }; 51841502Swpaul struct ifmultiaddr *ifma; 51941502Swpaul u_int8_t rxfilt; 52041502Swpaul int mcnt = 0; 52141502Swpaul 52241502Swpaul ifp = &sc->arpcom.ac_if; 52341502Swpaul 52441502Swpaul rxfilt = CSR_READ_1(sc, VR_RXCFG); 52541502Swpaul 52641502Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 52741502Swpaul rxfilt |= VR_RXCFG_RX_MULTI; 52841502Swpaul CSR_WRITE_1(sc, VR_RXCFG, rxfilt); 52941502Swpaul CSR_WRITE_4(sc, VR_MAR0, 0xFFFFFFFF); 53041502Swpaul CSR_WRITE_4(sc, VR_MAR1, 0xFFFFFFFF); 53141502Swpaul return; 53241502Swpaul } 53341502Swpaul 53441502Swpaul /* first, zot all the existing hash bits */ 53541502Swpaul CSR_WRITE_4(sc, VR_MAR0, 0); 53641502Swpaul CSR_WRITE_4(sc, VR_MAR1, 0); 53741502Swpaul 53841502Swpaul /* now program new ones */ 53941502Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 54041502Swpaul ifma = ifma->ifma_link.le_next) { 54141502Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 54241502Swpaul continue; 54341502Swpaul h = vr_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 54441502Swpaul if (h < 32) 54541502Swpaul hashes[0] |= (1 << h); 54641502Swpaul else 54741502Swpaul hashes[1] |= (1 << (h - 32)); 54841502Swpaul mcnt++; 54941502Swpaul } 55041502Swpaul 55141502Swpaul if (mcnt) 55241502Swpaul rxfilt |= VR_RXCFG_RX_MULTI; 55341502Swpaul else 55441502Swpaul rxfilt &= ~VR_RXCFG_RX_MULTI; 55541502Swpaul 55641502Swpaul CSR_WRITE_4(sc, VR_MAR0, hashes[0]); 55741502Swpaul CSR_WRITE_4(sc, VR_MAR1, hashes[1]); 55841502Swpaul CSR_WRITE_1(sc, VR_RXCFG, rxfilt); 55941502Swpaul 56041502Swpaul return; 56141502Swpaul} 56241502Swpaul 56341502Swpaul/* 56441502Swpaul * In order to fiddle with the 56541502Swpaul * 'full-duplex' and '100Mbps' bits in the netconfig register, we 56641502Swpaul * first have to put the transmit and/or receive logic in the idle state. 56741502Swpaul */ 56851432Swpaulstatic void vr_setcfg(sc, media) 56941502Swpaul struct vr_softc *sc; 57051432Swpaul int media; 57141502Swpaul{ 57241502Swpaul int restart = 0; 57341502Swpaul 57441502Swpaul if (CSR_READ_2(sc, VR_COMMAND) & (VR_CMD_TX_ON|VR_CMD_RX_ON)) { 57541502Swpaul restart = 1; 57641502Swpaul VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_TX_ON|VR_CMD_RX_ON)); 57741502Swpaul } 57841502Swpaul 57951432Swpaul if ((media & IFM_GMASK) == IFM_FDX) 58041502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); 58141502Swpaul else 58241502Swpaul VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); 58341502Swpaul 58441502Swpaul if (restart) 58541502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_RX_ON); 58641502Swpaul 58741502Swpaul return; 58841502Swpaul} 58941502Swpaul 59041502Swpaulstatic void vr_reset(sc) 59141502Swpaul struct vr_softc *sc; 59241502Swpaul{ 59341502Swpaul register int i; 59441502Swpaul 59541502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RESET); 59641502Swpaul 59741502Swpaul for (i = 0; i < VR_TIMEOUT; i++) { 59841502Swpaul DELAY(10); 59941502Swpaul if (!(CSR_READ_2(sc, VR_COMMAND) & VR_CMD_RESET)) 60041502Swpaul break; 60141502Swpaul } 60241502Swpaul if (i == VR_TIMEOUT) 60341502Swpaul printf("vr%d: reset never completed!\n", sc->vr_unit); 60441502Swpaul 60541502Swpaul /* Wait a little while for the chip to get its brains in order. */ 60641502Swpaul DELAY(1000); 60741502Swpaul 60841502Swpaul return; 60941502Swpaul} 61041502Swpaul 61141502Swpaul/* 61241502Swpaul * Probe for a VIA Rhine chip. Check the PCI vendor and device 61341502Swpaul * IDs against our list and return a device name if we find a match. 61441502Swpaul */ 61549610Swpaulstatic int vr_probe(dev) 61649610Swpaul device_t dev; 61741502Swpaul{ 61841502Swpaul struct vr_type *t; 61941502Swpaul 62041502Swpaul t = vr_devs; 62141502Swpaul 62241502Swpaul while(t->vr_name != NULL) { 62349610Swpaul if ((pci_get_vendor(dev) == t->vr_vid) && 62449610Swpaul (pci_get_device(dev) == t->vr_did)) { 62549610Swpaul device_set_desc(dev, t->vr_name); 62649610Swpaul return(0); 62741502Swpaul } 62841502Swpaul t++; 62941502Swpaul } 63041502Swpaul 63149610Swpaul return(ENXIO); 63241502Swpaul} 63341502Swpaul 63441502Swpaul/* 63541502Swpaul * Attach the interface. Allocate softc structures, do ifmedia 63641502Swpaul * setup and ethernet/BPF attach. 63741502Swpaul */ 63849610Swpaulstatic int vr_attach(dev) 63949610Swpaul device_t dev; 64041502Swpaul{ 64151432Swpaul int i, s; 64241502Swpaul u_char eaddr[ETHER_ADDR_LEN]; 64341502Swpaul u_int32_t command; 64441502Swpaul struct vr_softc *sc; 64541502Swpaul struct ifnet *ifp; 64649610Swpaul int unit, error = 0, rid; 64741502Swpaul 64841502Swpaul s = splimp(); 64941502Swpaul 65049610Swpaul sc = device_get_softc(dev); 65149610Swpaul unit = device_get_unit(dev); 65249610Swpaul bzero(sc, sizeof(struct vr_softc *)); 65341502Swpaul 65441502Swpaul /* 65541502Swpaul * Handle power management nonsense. 65641502Swpaul */ 65741502Swpaul 65849610Swpaul command = pci_read_config(dev, VR_PCI_CAPID, 4) & 0x000000FF; 65941502Swpaul if (command == 0x01) { 66041502Swpaul 66149610Swpaul command = pci_read_config(dev, VR_PCI_PWRMGMTCTRL, 4); 66241502Swpaul if (command & VR_PSTATE_MASK) { 66341502Swpaul u_int32_t iobase, membase, irq; 66441502Swpaul 66541502Swpaul /* Save important PCI config data. */ 66649610Swpaul iobase = pci_read_config(dev, VR_PCI_LOIO, 4); 66749610Swpaul membase = pci_read_config(dev, VR_PCI_LOMEM, 4); 66849610Swpaul irq = pci_read_config(dev, VR_PCI_INTLINE, 4); 66941502Swpaul 67041502Swpaul /* Reset the power state. */ 67141502Swpaul printf("vr%d: chip is in D%d power mode " 67241502Swpaul "-- setting to D0\n", unit, command & VR_PSTATE_MASK); 67341502Swpaul command &= 0xFFFFFFFC; 67449610Swpaul pci_write_config(dev, VR_PCI_PWRMGMTCTRL, command, 4); 67541502Swpaul 67641502Swpaul /* Restore PCI config data. */ 67749610Swpaul pci_write_config(dev, VR_PCI_LOIO, iobase, 4); 67849610Swpaul pci_write_config(dev, VR_PCI_LOMEM, membase, 4); 67949610Swpaul pci_write_config(dev, VR_PCI_INTLINE, irq, 4); 68041502Swpaul } 68141502Swpaul } 68241502Swpaul 68341502Swpaul /* 68441502Swpaul * Map control/status registers. 68541502Swpaul */ 68649610Swpaul command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); 68741502Swpaul command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 68849610Swpaul pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4); 68949610Swpaul command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4); 69041502Swpaul 69141502Swpaul#ifdef VR_USEIOSPACE 69241502Swpaul if (!(command & PCIM_CMD_PORTEN)) { 69341502Swpaul printf("vr%d: failed to enable I/O ports!\n", unit); 69441502Swpaul free(sc, M_DEVBUF); 69541502Swpaul goto fail; 69641502Swpaul } 69741502Swpaul#else 69841502Swpaul if (!(command & PCIM_CMD_MEMEN)) { 69941502Swpaul printf("vr%d: failed to enable memory mapping!\n", unit); 70041502Swpaul goto fail; 70141502Swpaul } 70249610Swpaul#endif 70341502Swpaul 70449610Swpaul rid = VR_RID; 70549610Swpaul sc->vr_res = bus_alloc_resource(dev, VR_RES, &rid, 70649610Swpaul 0, ~0, 1, RF_ACTIVE); 70749610Swpaul 70849610Swpaul if (sc->vr_res == NULL) { 70949610Swpaul printf("vr%d: couldn't map ports/memory\n", unit); 71049610Swpaul error = ENXIO; 71141502Swpaul goto fail; 71241502Swpaul } 71341502Swpaul 71449610Swpaul sc->vr_btag = rman_get_bustag(sc->vr_res); 71549610Swpaul sc->vr_bhandle = rman_get_bushandle(sc->vr_res); 71641502Swpaul 71741502Swpaul /* Allocate interrupt */ 71849610Swpaul rid = 0; 71949610Swpaul sc->vr_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 72049610Swpaul RF_SHAREABLE | RF_ACTIVE); 72149610Swpaul 72249610Swpaul if (sc->vr_irq == NULL) { 72341502Swpaul printf("vr%d: couldn't map interrupt\n", unit); 72449610Swpaul bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); 72549610Swpaul error = ENXIO; 72641502Swpaul goto fail; 72741502Swpaul } 72841502Swpaul 72949610Swpaul error = bus_setup_intr(dev, sc->vr_irq, INTR_TYPE_NET, 73049610Swpaul vr_intr, sc, &sc->vr_intrhand); 73149610Swpaul 73249610Swpaul if (error) { 73349610Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); 73449610Swpaul bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); 73549610Swpaul printf("vr%d: couldn't set up irq\n", unit); 73649610Swpaul goto fail; 73749610Swpaul } 73849610Swpaul 73941502Swpaul /* Reset the adapter. */ 74041502Swpaul vr_reset(sc); 74141502Swpaul 74241502Swpaul /* 74341502Swpaul * Get station address. The way the Rhine chips work, 74441502Swpaul * you're not allowed to directly access the EEPROM once 74541502Swpaul * they've been programmed a special way. Consequently, 74641502Swpaul * we need to read the node address from the PAR0 and PAR1 74741502Swpaul * registers. 74841502Swpaul */ 74941502Swpaul VR_SETBIT(sc, VR_EECSR, VR_EECSR_LOAD); 75041502Swpaul DELAY(200); 75141502Swpaul for (i = 0; i < ETHER_ADDR_LEN; i++) 75241502Swpaul eaddr[i] = CSR_READ_1(sc, VR_PAR0 + i); 75341502Swpaul 75441502Swpaul /* 75541502Swpaul * A Rhine chip was detected. Inform the world. 75641502Swpaul */ 75741502Swpaul printf("vr%d: Ethernet address: %6D\n", unit, eaddr, ":"); 75841502Swpaul 75941502Swpaul sc->vr_unit = unit; 76041502Swpaul bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 76141502Swpaul 76251432Swpaul sc->vr_ldata = contigmalloc(sizeof(struct vr_list_data), M_DEVBUF, 76351432Swpaul M_NOWAIT, 0x100000, 0xffffffff, PAGE_SIZE, 0); 76451432Swpaul 76551432Swpaul if (sc->vr_ldata == NULL) { 76641502Swpaul printf("vr%d: no memory for list buffers!\n", unit); 76749610Swpaul bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); 76849610Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); 76949610Swpaul bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); 77049610Swpaul error = ENXIO; 77149610Swpaul goto fail; 77241502Swpaul } 77341502Swpaul 77441502Swpaul bzero(sc->vr_ldata, sizeof(struct vr_list_data)); 77541502Swpaul 77641502Swpaul ifp = &sc->arpcom.ac_if; 77741502Swpaul ifp->if_softc = sc; 77841502Swpaul ifp->if_unit = unit; 77941502Swpaul ifp->if_name = "vr"; 78041502Swpaul ifp->if_mtu = ETHERMTU; 78141502Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 78241502Swpaul ifp->if_ioctl = vr_ioctl; 78341502Swpaul ifp->if_output = ether_output; 78441502Swpaul ifp->if_start = vr_start; 78541502Swpaul ifp->if_watchdog = vr_watchdog; 78641502Swpaul ifp->if_init = vr_init; 78741502Swpaul ifp->if_baudrate = 10000000; 78843515Swpaul ifp->if_snd.ifq_maxlen = VR_TX_LIST_CNT - 1; 78941502Swpaul 79051432Swpaul /* 79151432Swpaul * Do MII setup. 79251432Swpaul */ 79351432Swpaul if (mii_phy_probe(dev, &sc->vr_miibus, 79451432Swpaul vr_ifmedia_upd, vr_ifmedia_sts)) { 79541502Swpaul printf("vr%d: MII without any phy!\n", sc->vr_unit); 79649610Swpaul bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); 79749610Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); 79849610Swpaul bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); 79951432Swpaul contigfree(sc->vr_ldata, 80051432Swpaul sizeof(struct vr_list_data), M_DEVBUF); 80149610Swpaul error = ENXIO; 80241502Swpaul goto fail; 80341502Swpaul } 80441502Swpaul 80551432Swpaul callout_handle_init(&sc->vr_stat_ch); 80641502Swpaul 80741502Swpaul /* 80841502Swpaul * Call MI attach routines. 80941502Swpaul */ 81041502Swpaul if_attach(ifp); 81141502Swpaul ether_ifattach(ifp); 81241502Swpaul 81348645Sdes#if NBPF > 0 81441502Swpaul bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 81541502Swpaul#endif 81641502Swpaul 81741502Swpaulfail: 81841502Swpaul splx(s); 81949610Swpaul return(error); 82041502Swpaul} 82141502Swpaul 82249610Swpaulstatic int vr_detach(dev) 82349610Swpaul device_t dev; 82449610Swpaul{ 82549610Swpaul struct vr_softc *sc; 82649610Swpaul struct ifnet *ifp; 82749610Swpaul int s; 82849610Swpaul 82949610Swpaul s = splimp(); 83049610Swpaul 83149610Swpaul sc = device_get_softc(dev); 83249610Swpaul ifp = &sc->arpcom.ac_if; 83349610Swpaul 83449610Swpaul vr_stop(sc); 83549610Swpaul if_detach(ifp); 83649610Swpaul 83751432Swpaul bus_generic_detach(dev); 83851432Swpaul device_delete_child(dev, sc->vr_miibus); 83951432Swpaul 84049610Swpaul bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); 84149610Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); 84249610Swpaul bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); 84349610Swpaul 84451432Swpaul contigfree(sc->vr_ldata, sizeof(struct vr_list_data), M_DEVBUF); 84549610Swpaul 84649610Swpaul splx(s); 84749610Swpaul 84849610Swpaul return(0); 84949610Swpaul} 85049610Swpaul 85141502Swpaul/* 85241502Swpaul * Initialize the transmit descriptors. 85341502Swpaul */ 85441502Swpaulstatic int vr_list_tx_init(sc) 85541502Swpaul struct vr_softc *sc; 85641502Swpaul{ 85741502Swpaul struct vr_chain_data *cd; 85841502Swpaul struct vr_list_data *ld; 85941502Swpaul int i; 86041502Swpaul 86141502Swpaul cd = &sc->vr_cdata; 86241502Swpaul ld = sc->vr_ldata; 86341502Swpaul for (i = 0; i < VR_TX_LIST_CNT; i++) { 86441502Swpaul cd->vr_tx_chain[i].vr_ptr = &ld->vr_tx_list[i]; 86541502Swpaul if (i == (VR_TX_LIST_CNT - 1)) 86641502Swpaul cd->vr_tx_chain[i].vr_nextdesc = 86741502Swpaul &cd->vr_tx_chain[0]; 86841502Swpaul else 86941502Swpaul cd->vr_tx_chain[i].vr_nextdesc = 87041502Swpaul &cd->vr_tx_chain[i + 1]; 87141502Swpaul } 87241502Swpaul 87341502Swpaul cd->vr_tx_free = &cd->vr_tx_chain[0]; 87441502Swpaul cd->vr_tx_tail = cd->vr_tx_head = NULL; 87541502Swpaul 87641502Swpaul return(0); 87741502Swpaul} 87841502Swpaul 87941502Swpaul 88041502Swpaul/* 88141502Swpaul * Initialize the RX descriptors and allocate mbufs for them. Note that 88241502Swpaul * we arrange the descriptors in a closed ring, so that the last descriptor 88341502Swpaul * points back to the first. 88441502Swpaul */ 88541502Swpaulstatic int vr_list_rx_init(sc) 88641502Swpaul struct vr_softc *sc; 88741502Swpaul{ 88841502Swpaul struct vr_chain_data *cd; 88941502Swpaul struct vr_list_data *ld; 89041502Swpaul int i; 89141502Swpaul 89241502Swpaul cd = &sc->vr_cdata; 89341502Swpaul ld = sc->vr_ldata; 89441502Swpaul 89541502Swpaul for (i = 0; i < VR_RX_LIST_CNT; i++) { 89641502Swpaul cd->vr_rx_chain[i].vr_ptr = 89741502Swpaul (struct vr_desc *)&ld->vr_rx_list[i]; 89849610Swpaul if (vr_newbuf(sc, &cd->vr_rx_chain[i], NULL) == ENOBUFS) 89941502Swpaul return(ENOBUFS); 90041502Swpaul if (i == (VR_RX_LIST_CNT - 1)) { 90141502Swpaul cd->vr_rx_chain[i].vr_nextdesc = 90241502Swpaul &cd->vr_rx_chain[0]; 90341502Swpaul ld->vr_rx_list[i].vr_next = 90441502Swpaul vtophys(&ld->vr_rx_list[0]); 90541502Swpaul } else { 90641502Swpaul cd->vr_rx_chain[i].vr_nextdesc = 90741502Swpaul &cd->vr_rx_chain[i + 1]; 90841502Swpaul ld->vr_rx_list[i].vr_next = 90941502Swpaul vtophys(&ld->vr_rx_list[i + 1]); 91041502Swpaul } 91141502Swpaul } 91241502Swpaul 91341502Swpaul cd->vr_rx_head = &cd->vr_rx_chain[0]; 91441502Swpaul 91541502Swpaul return(0); 91641502Swpaul} 91741502Swpaul 91841502Swpaul/* 91941502Swpaul * Initialize an RX descriptor and attach an MBUF cluster. 92041502Swpaul * Note: the length fields are only 11 bits wide, which means the 92141502Swpaul * largest size we can specify is 2047. This is important because 92241502Swpaul * MCLBYTES is 2048, so we have to subtract one otherwise we'll 92341502Swpaul * overflow the field and make a mess. 92441502Swpaul */ 92549610Swpaulstatic int vr_newbuf(sc, c, m) 92641502Swpaul struct vr_softc *sc; 92741502Swpaul struct vr_chain_onefrag *c; 92849610Swpaul struct mbuf *m; 92941502Swpaul{ 93041502Swpaul struct mbuf *m_new = NULL; 93141502Swpaul 93249610Swpaul if (m == NULL) { 93349610Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 93449610Swpaul if (m_new == NULL) { 93549610Swpaul printf("vr%d: no memory for rx list " 93649610Swpaul "-- packet dropped!\n", sc->vr_unit); 93749610Swpaul return(ENOBUFS); 93849610Swpaul } 93941502Swpaul 94049610Swpaul MCLGET(m_new, M_DONTWAIT); 94149610Swpaul if (!(m_new->m_flags & M_EXT)) { 94249610Swpaul printf("vr%d: no memory for rx list " 94349610Swpaul "-- packet dropped!\n", sc->vr_unit); 94449610Swpaul m_freem(m_new); 94549610Swpaul return(ENOBUFS); 94649610Swpaul } 94749610Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 94849610Swpaul } else { 94949610Swpaul m_new = m; 95049610Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 95149610Swpaul m_new->m_data = m_new->m_ext.ext_buf; 95241502Swpaul } 95341502Swpaul 95449610Swpaul m_adj(m_new, sizeof(u_int64_t)); 95549610Swpaul 95641502Swpaul c->vr_mbuf = m_new; 95741502Swpaul c->vr_ptr->vr_status = VR_RXSTAT; 95841502Swpaul c->vr_ptr->vr_data = vtophys(mtod(m_new, caddr_t)); 95942491Swpaul c->vr_ptr->vr_ctl = VR_RXCTL | VR_RXLEN; 96041502Swpaul 96141502Swpaul return(0); 96241502Swpaul} 96341502Swpaul 96441502Swpaul/* 96541502Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to 96641502Swpaul * the higher level protocols. 96741502Swpaul */ 96841502Swpaulstatic void vr_rxeof(sc) 96941502Swpaul struct vr_softc *sc; 97041502Swpaul{ 97141502Swpaul struct ether_header *eh; 97241502Swpaul struct mbuf *m; 97341502Swpaul struct ifnet *ifp; 97441502Swpaul struct vr_chain_onefrag *cur_rx; 97541502Swpaul int total_len = 0; 97641502Swpaul u_int32_t rxstat; 97741502Swpaul 97841502Swpaul ifp = &sc->arpcom.ac_if; 97941502Swpaul 98041502Swpaul while(!((rxstat = sc->vr_cdata.vr_rx_head->vr_ptr->vr_status) & 98141502Swpaul VR_RXSTAT_OWN)) { 98249610Swpaul struct mbuf *m0 = NULL; 98349610Swpaul 98441502Swpaul cur_rx = sc->vr_cdata.vr_rx_head; 98541502Swpaul sc->vr_cdata.vr_rx_head = cur_rx->vr_nextdesc; 98649610Swpaul m = cur_rx->vr_mbuf; 98741502Swpaul 98841502Swpaul /* 98941502Swpaul * If an error occurs, update stats, clear the 99041502Swpaul * status word and leave the mbuf cluster in place: 99141502Swpaul * it should simply get re-used next time this descriptor 99241502Swpaul * comes up in the ring. 99341502Swpaul */ 99441502Swpaul if (rxstat & VR_RXSTAT_RXERR) { 99541502Swpaul ifp->if_ierrors++; 99641502Swpaul printf("vr%d: rx error: ", sc->vr_unit); 99741502Swpaul switch(rxstat & 0x000000FF) { 99841502Swpaul case VR_RXSTAT_CRCERR: 99941502Swpaul printf("crc error\n"); 100041502Swpaul break; 100141502Swpaul case VR_RXSTAT_FRAMEALIGNERR: 100241502Swpaul printf("frame alignment error\n"); 100341502Swpaul break; 100441502Swpaul case VR_RXSTAT_FIFOOFLOW: 100541502Swpaul printf("FIFO overflow\n"); 100641502Swpaul break; 100741502Swpaul case VR_RXSTAT_GIANT: 100841502Swpaul printf("received giant packet\n"); 100941502Swpaul break; 101041502Swpaul case VR_RXSTAT_RUNT: 101141502Swpaul printf("received runt packet\n"); 101241502Swpaul break; 101341502Swpaul case VR_RXSTAT_BUSERR: 101441502Swpaul printf("system bus error\n"); 101541502Swpaul break; 101641502Swpaul case VR_RXSTAT_BUFFERR: 101741502Swpaul printf("rx buffer error\n"); 101841502Swpaul break; 101941502Swpaul default: 102041502Swpaul printf("unknown rx error\n"); 102141502Swpaul break; 102241502Swpaul } 102349610Swpaul vr_newbuf(sc, cur_rx, m); 102441502Swpaul continue; 102541502Swpaul } 102641502Swpaul 102741502Swpaul /* No errors; receive the packet. */ 102841502Swpaul total_len = VR_RXBYTES(cur_rx->vr_ptr->vr_status); 102941502Swpaul 103041502Swpaul /* 103142048Swpaul * XXX The VIA Rhine chip includes the CRC with every 103242048Swpaul * received frame, and there's no way to turn this 103342048Swpaul * behavior off (at least, I can't find anything in 103442048Swpaul * the manual that explains how to do it) so we have 103542048Swpaul * to trim off the CRC manually. 103642048Swpaul */ 103742048Swpaul total_len -= ETHER_CRC_LEN; 103842048Swpaul 103949610Swpaul m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, 104049610Swpaul total_len + ETHER_ALIGN, 0, ifp, NULL); 104149610Swpaul vr_newbuf(sc, cur_rx, m); 104249610Swpaul if (m0 == NULL) { 104341502Swpaul ifp->if_ierrors++; 104441502Swpaul continue; 104541502Swpaul } 104649610Swpaul m_adj(m0, ETHER_ALIGN); 104749610Swpaul m = m0; 104841502Swpaul 104941502Swpaul ifp->if_ipackets++; 105041502Swpaul eh = mtod(m, struct ether_header *); 105149610Swpaul 105248645Sdes#if NBPF > 0 105341502Swpaul /* 105441502Swpaul * Handle BPF listeners. Let the BPF user see the packet, but 105541502Swpaul * don't pass it up to the ether_input() layer unless it's 105641502Swpaul * a broadcast packet, multicast packet, matches our ethernet 105741502Swpaul * address or the interface is in promiscuous mode. 105841502Swpaul */ 105941502Swpaul if (ifp->if_bpf) { 106041502Swpaul bpf_mtap(ifp, m); 106141502Swpaul if (ifp->if_flags & IFF_PROMISC && 106241502Swpaul (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, 106341502Swpaul ETHER_ADDR_LEN) && 106441502Swpaul (eh->ether_dhost[0] & 1) == 0)) { 106541502Swpaul m_freem(m); 106641502Swpaul continue; 106741502Swpaul } 106841502Swpaul } 106951354Swpaul#endif /* NBPF>0 */ 107051354Swpaul#ifdef BRIDGE 107151354Swpaul if (do_bridge) { 107251354Swpaul struct ifnet *bdg_ifp; 107351354Swpaul bdg_ifp = bridge_in(m); 107451354Swpaul if (bdg_ifp != BDG_LOCAL && bdg_ifp != BDG_DROP) 107551354Swpaul bdg_forward(&m, bdg_ifp); 107651354Swpaul if (((bdg_ifp != BDG_LOCAL) && (bdg_ifp != BDG_BCAST) && 107751354Swpaul (bdg_ifp != BDG_MCAST)) || bdg_ifp == BDG_DROP) { 107851354Swpaul m_freem(m); 107951354Swpaul continue; 108051354Swpaul } 108151354Swpaul } 108251354Swpaul#endif /* BRIDGE */ 108351354Swpaul 108441502Swpaul /* Remove header from mbuf and pass it on. */ 108541502Swpaul m_adj(m, sizeof(struct ether_header)); 108641502Swpaul ether_input(ifp, eh, m); 108741502Swpaul } 108841502Swpaul 108941502Swpaul return; 109041502Swpaul} 109141502Swpaul 109241502Swpaulvoid vr_rxeoc(sc) 109341502Swpaul struct vr_softc *sc; 109441502Swpaul{ 109541502Swpaul 109641502Swpaul vr_rxeof(sc); 109741502Swpaul VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); 109841502Swpaul CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); 109941502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); 110041502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_GO); 110141502Swpaul 110241502Swpaul return; 110341502Swpaul} 110441502Swpaul 110541502Swpaul/* 110641502Swpaul * A frame was downloaded to the chip. It's safe for us to clean up 110741502Swpaul * the list buffers. 110841502Swpaul */ 110941502Swpaul 111041502Swpaulstatic void vr_txeof(sc) 111141502Swpaul struct vr_softc *sc; 111241502Swpaul{ 111341502Swpaul struct vr_chain *cur_tx; 111441502Swpaul struct ifnet *ifp; 111541502Swpaul 111641502Swpaul ifp = &sc->arpcom.ac_if; 111741502Swpaul 111841502Swpaul /* Clear the timeout timer. */ 111941502Swpaul ifp->if_timer = 0; 112041502Swpaul 112141502Swpaul /* Sanity check. */ 112241502Swpaul if (sc->vr_cdata.vr_tx_head == NULL) 112341502Swpaul return; 112441502Swpaul 112541502Swpaul /* 112641502Swpaul * Go through our tx list and free mbufs for those 112741502Swpaul * frames that have been transmitted. 112841502Swpaul */ 112941502Swpaul while(sc->vr_cdata.vr_tx_head->vr_mbuf != NULL) { 113041502Swpaul u_int32_t txstat; 113141502Swpaul 113241502Swpaul cur_tx = sc->vr_cdata.vr_tx_head; 113341502Swpaul txstat = cur_tx->vr_ptr->vr_status; 113441502Swpaul 113542491Swpaul if (txstat & VR_TXSTAT_OWN) 113641502Swpaul break; 113741502Swpaul 113841502Swpaul if (txstat & VR_TXSTAT_ERRSUM) { 113941502Swpaul ifp->if_oerrors++; 114041502Swpaul if (txstat & VR_TXSTAT_DEFER) 114141502Swpaul ifp->if_collisions++; 114241502Swpaul if (txstat & VR_TXSTAT_LATECOLL) 114341502Swpaul ifp->if_collisions++; 114441502Swpaul } 114541502Swpaul 114641502Swpaul ifp->if_collisions +=(txstat & VR_TXSTAT_COLLCNT) >> 3; 114741502Swpaul 114841502Swpaul ifp->if_opackets++; 114951432Swpaul if (cur_tx->vr_mbuf != NULL) { 115051432Swpaul m_freem(cur_tx->vr_mbuf); 115151432Swpaul cur_tx->vr_mbuf = NULL; 115251432Swpaul } 115341502Swpaul 115441502Swpaul if (sc->vr_cdata.vr_tx_head == sc->vr_cdata.vr_tx_tail) { 115541502Swpaul sc->vr_cdata.vr_tx_head = NULL; 115641502Swpaul sc->vr_cdata.vr_tx_tail = NULL; 115741502Swpaul break; 115841502Swpaul } 115941502Swpaul 116041502Swpaul sc->vr_cdata.vr_tx_head = cur_tx->vr_nextdesc; 116141502Swpaul } 116241502Swpaul 116341502Swpaul return; 116441502Swpaul} 116541502Swpaul 116641502Swpaul/* 116741502Swpaul * TX 'end of channel' interrupt handler. 116841502Swpaul */ 116941502Swpaulstatic void vr_txeoc(sc) 117041502Swpaul struct vr_softc *sc; 117141502Swpaul{ 117241502Swpaul struct ifnet *ifp; 117341502Swpaul 117441502Swpaul ifp = &sc->arpcom.ac_if; 117541502Swpaul 117641502Swpaul ifp->if_timer = 0; 117741502Swpaul 117841502Swpaul if (sc->vr_cdata.vr_tx_head == NULL) { 117941502Swpaul ifp->if_flags &= ~IFF_OACTIVE; 118041502Swpaul sc->vr_cdata.vr_tx_tail = NULL; 118141502Swpaul } 118241502Swpaul 118341502Swpaul return; 118441502Swpaul} 118541502Swpaul 118651432Swpaulstatic void vr_tick(xsc) 118751432Swpaul void *xsc; 118851432Swpaul{ 118951432Swpaul struct vr_softc *sc; 119051432Swpaul struct mii_data *mii; 119151432Swpaul int s; 119251432Swpaul 119351432Swpaul s = splimp(); 119451432Swpaul 119551432Swpaul sc = xsc; 119651432Swpaul mii = device_get_softc(sc->vr_miibus); 119751432Swpaul mii_tick(mii); 119851432Swpaul 119951432Swpaul sc->vr_stat_ch = timeout(vr_tick, sc, hz); 120051432Swpaul 120151432Swpaul splx(s); 120251432Swpaul 120351432Swpaul return; 120451432Swpaul} 120551432Swpaul 120641502Swpaulstatic void vr_intr(arg) 120741502Swpaul void *arg; 120841502Swpaul{ 120941502Swpaul struct vr_softc *sc; 121041502Swpaul struct ifnet *ifp; 121141502Swpaul u_int16_t status; 121241502Swpaul 121341502Swpaul sc = arg; 121441502Swpaul ifp = &sc->arpcom.ac_if; 121541502Swpaul 121641502Swpaul /* Supress unwanted interrupts. */ 121741502Swpaul if (!(ifp->if_flags & IFF_UP)) { 121841502Swpaul vr_stop(sc); 121941502Swpaul return; 122041502Swpaul } 122141502Swpaul 122241502Swpaul /* Disable interrupts. */ 122341502Swpaul CSR_WRITE_2(sc, VR_IMR, 0x0000); 122441502Swpaul 122541502Swpaul for (;;) { 122641502Swpaul 122741502Swpaul status = CSR_READ_2(sc, VR_ISR); 122841502Swpaul if (status) 122941502Swpaul CSR_WRITE_2(sc, VR_ISR, status); 123041502Swpaul 123141502Swpaul if ((status & VR_INTRS) == 0) 123241502Swpaul break; 123341502Swpaul 123441502Swpaul if (status & VR_ISR_RX_OK) 123541502Swpaul vr_rxeof(sc); 123641502Swpaul 123741502Swpaul if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) || 123841502Swpaul (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW) || 123941502Swpaul (status & VR_ISR_RX_DROPPED)) { 124041502Swpaul vr_rxeof(sc); 124141502Swpaul vr_rxeoc(sc); 124241502Swpaul } 124341502Swpaul 124441502Swpaul if (status & VR_ISR_TX_OK) { 124541502Swpaul vr_txeof(sc); 124641502Swpaul vr_txeoc(sc); 124741502Swpaul } 124841502Swpaul 124941502Swpaul if ((status & VR_ISR_TX_UNDERRUN)||(status & VR_ISR_TX_ABRT)){ 125041502Swpaul ifp->if_oerrors++; 125141502Swpaul vr_txeof(sc); 125241502Swpaul if (sc->vr_cdata.vr_tx_head != NULL) { 125341502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON); 125441502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO); 125541502Swpaul } 125641502Swpaul } 125741502Swpaul 125841502Swpaul if (status & VR_ISR_BUSERR) { 125941502Swpaul vr_reset(sc); 126041502Swpaul vr_init(sc); 126141502Swpaul } 126241502Swpaul } 126341502Swpaul 126441502Swpaul /* Re-enable interrupts. */ 126541502Swpaul CSR_WRITE_2(sc, VR_IMR, VR_INTRS); 126641502Swpaul 126741502Swpaul if (ifp->if_snd.ifq_head != NULL) { 126841502Swpaul vr_start(ifp); 126941502Swpaul } 127041502Swpaul 127141502Swpaul return; 127241502Swpaul} 127341502Swpaul 127441502Swpaul/* 127541502Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 127641502Swpaul * pointers to the fragment pointers. 127741502Swpaul */ 127841502Swpaulstatic int vr_encap(sc, c, m_head) 127941502Swpaul struct vr_softc *sc; 128041502Swpaul struct vr_chain *c; 128141502Swpaul struct mbuf *m_head; 128241502Swpaul{ 128341502Swpaul int frag = 0; 128441502Swpaul struct vr_desc *f = NULL; 128541502Swpaul int total_len; 128641502Swpaul struct mbuf *m; 128741502Swpaul 128841502Swpaul m = m_head; 128941502Swpaul total_len = 0; 129041502Swpaul 129141502Swpaul /* 129241502Swpaul * The VIA Rhine wants packet buffers to be longword 129341502Swpaul * aligned, but very often our mbufs aren't. Rather than 129441502Swpaul * waste time trying to decide when to copy and when not 129541502Swpaul * to copy, just do it all the time. 129641502Swpaul */ 129741502Swpaul if (m != NULL) { 129841502Swpaul struct mbuf *m_new = NULL; 129941502Swpaul 130041502Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 130141502Swpaul if (m_new == NULL) { 130241502Swpaul printf("vr%d: no memory for tx list", sc->vr_unit); 130341502Swpaul return(1); 130441502Swpaul } 130541502Swpaul if (m_head->m_pkthdr.len > MHLEN) { 130641502Swpaul MCLGET(m_new, M_DONTWAIT); 130741502Swpaul if (!(m_new->m_flags & M_EXT)) { 130841502Swpaul m_freem(m_new); 130941502Swpaul printf("vr%d: no memory for tx list", 131041502Swpaul sc->vr_unit); 131141502Swpaul return(1); 131241502Swpaul } 131341502Swpaul } 131441502Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 131541502Swpaul mtod(m_new, caddr_t)); 131641502Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 131741502Swpaul m_freem(m_head); 131841502Swpaul m_head = m_new; 131941502Swpaul /* 132041502Swpaul * The Rhine chip doesn't auto-pad, so we have to make 132141502Swpaul * sure to pad short frames out to the minimum frame length 132241502Swpaul * ourselves. 132341502Swpaul */ 132441502Swpaul if (m_head->m_len < VR_MIN_FRAMELEN) { 132541502Swpaul m_new->m_pkthdr.len += VR_MIN_FRAMELEN - m_new->m_len; 132641502Swpaul m_new->m_len = m_new->m_pkthdr.len; 132741502Swpaul } 132841502Swpaul f = c->vr_ptr; 132941502Swpaul f->vr_data = vtophys(mtod(m_new, caddr_t)); 133041502Swpaul f->vr_ctl = total_len = m_new->m_len; 133141502Swpaul f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG; 133241502Swpaul f->vr_status = 0; 133341502Swpaul frag = 1; 133441502Swpaul } 133541502Swpaul 133641502Swpaul c->vr_mbuf = m_head; 133742491Swpaul c->vr_ptr->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT; 133841502Swpaul c->vr_ptr->vr_next = vtophys(c->vr_nextdesc->vr_ptr); 133941502Swpaul 134041502Swpaul return(0); 134141502Swpaul} 134241502Swpaul 134341502Swpaul/* 134441502Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 134541502Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 134641502Swpaul * copy of the pointers since the transmit list fragment pointers are 134741502Swpaul * physical addresses. 134841502Swpaul */ 134941502Swpaul 135041502Swpaulstatic void vr_start(ifp) 135141502Swpaul struct ifnet *ifp; 135241502Swpaul{ 135341502Swpaul struct vr_softc *sc; 135441502Swpaul struct mbuf *m_head = NULL; 135541502Swpaul struct vr_chain *cur_tx = NULL, *start_tx; 135641502Swpaul 135741502Swpaul sc = ifp->if_softc; 135841502Swpaul 135951432Swpaul if (ifp->if_flags & IFF_OACTIVE) 136041502Swpaul return; 136141502Swpaul 136241502Swpaul /* 136341502Swpaul * Check for an available queue slot. If there are none, 136441502Swpaul * punt. 136541502Swpaul */ 136641502Swpaul if (sc->vr_cdata.vr_tx_free->vr_mbuf != NULL) { 136741502Swpaul ifp->if_flags |= IFF_OACTIVE; 136841502Swpaul return; 136941502Swpaul } 137041502Swpaul 137141502Swpaul start_tx = sc->vr_cdata.vr_tx_free; 137241502Swpaul 137341502Swpaul while(sc->vr_cdata.vr_tx_free->vr_mbuf == NULL) { 137441502Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 137541502Swpaul if (m_head == NULL) 137641502Swpaul break; 137741502Swpaul 137841502Swpaul /* Pick a descriptor off the free list. */ 137941502Swpaul cur_tx = sc->vr_cdata.vr_tx_free; 138041502Swpaul sc->vr_cdata.vr_tx_free = cur_tx->vr_nextdesc; 138141502Swpaul 138241502Swpaul /* Pack the data into the descriptor. */ 138341502Swpaul vr_encap(sc, cur_tx, m_head); 138441502Swpaul 138541502Swpaul if (cur_tx != start_tx) 138641502Swpaul VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; 138741502Swpaul 138848645Sdes#if NBPF > 0 138941502Swpaul /* 139041502Swpaul * If there's a BPF listener, bounce a copy of this frame 139141502Swpaul * to him. 139241502Swpaul */ 139341502Swpaul if (ifp->if_bpf) 139441502Swpaul bpf_mtap(ifp, cur_tx->vr_mbuf); 139541502Swpaul#endif 139642491Swpaul VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; 139751432Swpaul VR_SETBIT16(sc, VR_COMMAND, /*VR_CMD_TX_ON|*/VR_CMD_TX_GO); 139841502Swpaul } 139941502Swpaul 140041502Swpaul /* 140141526Swpaul * If there are no frames queued, bail. 140241526Swpaul */ 140341526Swpaul if (cur_tx == NULL) 140441526Swpaul return; 140541526Swpaul 140641502Swpaul sc->vr_cdata.vr_tx_tail = cur_tx; 140741502Swpaul 140842491Swpaul if (sc->vr_cdata.vr_tx_head == NULL) 140941502Swpaul sc->vr_cdata.vr_tx_head = start_tx; 141041502Swpaul 141141502Swpaul /* 141241502Swpaul * Set a timeout in case the chip goes out to lunch. 141341502Swpaul */ 141441502Swpaul ifp->if_timer = 5; 141541502Swpaul 141641502Swpaul return; 141741502Swpaul} 141841502Swpaul 141941502Swpaulstatic void vr_init(xsc) 142041502Swpaul void *xsc; 142141502Swpaul{ 142241502Swpaul struct vr_softc *sc = xsc; 142341502Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 142451432Swpaul struct mii_data *mii; 142541502Swpaul int s; 142641502Swpaul 142741502Swpaul s = splimp(); 142841502Swpaul 142951432Swpaul mii = device_get_softc(sc->vr_miibus); 143041502Swpaul 143141502Swpaul /* 143241502Swpaul * Cancel pending I/O and free all RX/TX buffers. 143341502Swpaul */ 143441502Swpaul vr_stop(sc); 143541502Swpaul vr_reset(sc); 143641502Swpaul 143741502Swpaul VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_THRESH); 143841502Swpaul VR_SETBIT(sc, VR_RXCFG, VR_RXTHRESH_STORENFWD); 143941502Swpaul 144041502Swpaul VR_CLRBIT(sc, VR_TXCFG, VR_TXCFG_TX_THRESH); 144141502Swpaul VR_SETBIT(sc, VR_TXCFG, VR_TXTHRESH_STORENFWD); 144241502Swpaul 144341502Swpaul /* Init circular RX list. */ 144441502Swpaul if (vr_list_rx_init(sc) == ENOBUFS) { 144541502Swpaul printf("vr%d: initialization failed: no " 144641502Swpaul "memory for rx buffers\n", sc->vr_unit); 144741502Swpaul vr_stop(sc); 144841502Swpaul (void)splx(s); 144941502Swpaul return; 145041502Swpaul } 145141502Swpaul 145241502Swpaul /* 145341502Swpaul * Init tx descriptors. 145441502Swpaul */ 145541502Swpaul vr_list_tx_init(sc); 145641502Swpaul 145741502Swpaul /* If we want promiscuous mode, set the allframes bit. */ 145841502Swpaul if (ifp->if_flags & IFF_PROMISC) 145941502Swpaul VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); 146041502Swpaul else 146141502Swpaul VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); 146241502Swpaul 146341502Swpaul /* Set capture broadcast bit to capture broadcast frames. */ 146441502Swpaul if (ifp->if_flags & IFF_BROADCAST) 146541502Swpaul VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); 146641502Swpaul else 146741502Swpaul VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); 146841502Swpaul 146941502Swpaul /* 147041502Swpaul * Program the multicast filter, if necessary. 147141502Swpaul */ 147241502Swpaul vr_setmulti(sc); 147341502Swpaul 147441502Swpaul /* 147541502Swpaul * Load the address of the RX list. 147641502Swpaul */ 147741502Swpaul CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); 147841502Swpaul 147941502Swpaul /* Enable receiver and transmitter. */ 148041502Swpaul CSR_WRITE_2(sc, VR_COMMAND, VR_CMD_TX_NOPOLL|VR_CMD_START| 148141502Swpaul VR_CMD_TX_ON|VR_CMD_RX_ON| 148241502Swpaul VR_CMD_RX_GO); 148341502Swpaul 148441502Swpaul CSR_WRITE_4(sc, VR_TXADDR, vtophys(&sc->vr_ldata->vr_tx_list[0])); 148541502Swpaul 148641502Swpaul /* 148741502Swpaul * Enable interrupts. 148841502Swpaul */ 148941502Swpaul CSR_WRITE_2(sc, VR_ISR, 0xFFFF); 149041502Swpaul CSR_WRITE_2(sc, VR_IMR, VR_INTRS); 149141502Swpaul 149251432Swpaul mii_mediachg(mii); 149341502Swpaul 149441502Swpaul ifp->if_flags |= IFF_RUNNING; 149541502Swpaul ifp->if_flags &= ~IFF_OACTIVE; 149641502Swpaul 149741502Swpaul (void)splx(s); 149841502Swpaul 149951432Swpaul sc->vr_stat_ch = timeout(vr_tick, sc, hz); 150051432Swpaul 150141502Swpaul return; 150241502Swpaul} 150341502Swpaul 150441502Swpaul/* 150541502Swpaul * Set media options. 150641502Swpaul */ 150741502Swpaulstatic int vr_ifmedia_upd(ifp) 150841502Swpaul struct ifnet *ifp; 150941502Swpaul{ 151041502Swpaul struct vr_softc *sc; 151141502Swpaul 151241502Swpaul sc = ifp->if_softc; 151341502Swpaul 151451432Swpaul if (ifp->if_flags & IFF_UP) 151551432Swpaul vr_init(sc); 151641502Swpaul 151741502Swpaul return(0); 151841502Swpaul} 151941502Swpaul 152041502Swpaul/* 152141502Swpaul * Report current media status. 152241502Swpaul */ 152341502Swpaulstatic void vr_ifmedia_sts(ifp, ifmr) 152441502Swpaul struct ifnet *ifp; 152541502Swpaul struct ifmediareq *ifmr; 152641502Swpaul{ 152741502Swpaul struct vr_softc *sc; 152851432Swpaul struct mii_data *mii; 152941502Swpaul 153041502Swpaul sc = ifp->if_softc; 153151432Swpaul mii = device_get_softc(sc->vr_miibus); 153251432Swpaul mii_pollstat(mii); 153351432Swpaul ifmr->ifm_active = mii->mii_media_active; 153451432Swpaul ifmr->ifm_status = mii->mii_media_status; 153541502Swpaul 153641502Swpaul return; 153741502Swpaul} 153841502Swpaul 153941502Swpaulstatic int vr_ioctl(ifp, command, data) 154041502Swpaul struct ifnet *ifp; 154141502Swpaul u_long command; 154241502Swpaul caddr_t data; 154341502Swpaul{ 154441502Swpaul struct vr_softc *sc = ifp->if_softc; 154541502Swpaul struct ifreq *ifr = (struct ifreq *) data; 154651432Swpaul struct mii_data *mii; 154741502Swpaul int s, error = 0; 154841502Swpaul 154941502Swpaul s = splimp(); 155041502Swpaul 155141502Swpaul switch(command) { 155241502Swpaul case SIOCSIFADDR: 155341502Swpaul case SIOCGIFADDR: 155441502Swpaul case SIOCSIFMTU: 155541502Swpaul error = ether_ioctl(ifp, command, data); 155641502Swpaul break; 155741502Swpaul case SIOCSIFFLAGS: 155841502Swpaul if (ifp->if_flags & IFF_UP) { 155941502Swpaul vr_init(sc); 156041502Swpaul } else { 156141502Swpaul if (ifp->if_flags & IFF_RUNNING) 156241502Swpaul vr_stop(sc); 156341502Swpaul } 156441502Swpaul error = 0; 156541502Swpaul break; 156641502Swpaul case SIOCADDMULTI: 156741502Swpaul case SIOCDELMULTI: 156841502Swpaul vr_setmulti(sc); 156941502Swpaul error = 0; 157041502Swpaul break; 157141502Swpaul case SIOCGIFMEDIA: 157241502Swpaul case SIOCSIFMEDIA: 157351432Swpaul mii = device_get_softc(sc->vr_miibus); 157451432Swpaul error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 157541502Swpaul break; 157641502Swpaul default: 157741502Swpaul error = EINVAL; 157841502Swpaul break; 157941502Swpaul } 158041502Swpaul 158141502Swpaul (void)splx(s); 158241502Swpaul 158341502Swpaul return(error); 158441502Swpaul} 158541502Swpaul 158641502Swpaulstatic void vr_watchdog(ifp) 158741502Swpaul struct ifnet *ifp; 158841502Swpaul{ 158941502Swpaul struct vr_softc *sc; 159041502Swpaul 159141502Swpaul sc = ifp->if_softc; 159241502Swpaul 159341502Swpaul ifp->if_oerrors++; 159441502Swpaul printf("vr%d: watchdog timeout\n", sc->vr_unit); 159541502Swpaul 159641502Swpaul vr_stop(sc); 159741502Swpaul vr_reset(sc); 159841502Swpaul vr_init(sc); 159941502Swpaul 160041502Swpaul if (ifp->if_snd.ifq_head != NULL) 160141502Swpaul vr_start(ifp); 160241502Swpaul 160341502Swpaul return; 160441502Swpaul} 160541502Swpaul 160641502Swpaul/* 160741502Swpaul * Stop the adapter and free any mbufs allocated to the 160841502Swpaul * RX and TX lists. 160941502Swpaul */ 161041502Swpaulstatic void vr_stop(sc) 161141502Swpaul struct vr_softc *sc; 161241502Swpaul{ 161341502Swpaul register int i; 161441502Swpaul struct ifnet *ifp; 161541502Swpaul 161641502Swpaul ifp = &sc->arpcom.ac_if; 161741502Swpaul ifp->if_timer = 0; 161841502Swpaul 161951432Swpaul untimeout(vr_tick, sc, sc->vr_stat_ch); 162051432Swpaul 162141502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_STOP); 162241502Swpaul VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_RX_ON|VR_CMD_TX_ON)); 162341502Swpaul CSR_WRITE_2(sc, VR_IMR, 0x0000); 162441502Swpaul CSR_WRITE_4(sc, VR_TXADDR, 0x00000000); 162541502Swpaul CSR_WRITE_4(sc, VR_RXADDR, 0x00000000); 162641502Swpaul 162741502Swpaul /* 162841502Swpaul * Free data in the RX lists. 162941502Swpaul */ 163041502Swpaul for (i = 0; i < VR_RX_LIST_CNT; i++) { 163141502Swpaul if (sc->vr_cdata.vr_rx_chain[i].vr_mbuf != NULL) { 163241502Swpaul m_freem(sc->vr_cdata.vr_rx_chain[i].vr_mbuf); 163341502Swpaul sc->vr_cdata.vr_rx_chain[i].vr_mbuf = NULL; 163441502Swpaul } 163541502Swpaul } 163641502Swpaul bzero((char *)&sc->vr_ldata->vr_rx_list, 163741502Swpaul sizeof(sc->vr_ldata->vr_rx_list)); 163841502Swpaul 163941502Swpaul /* 164041502Swpaul * Free the TX list buffers. 164141502Swpaul */ 164241502Swpaul for (i = 0; i < VR_TX_LIST_CNT; i++) { 164341502Swpaul if (sc->vr_cdata.vr_tx_chain[i].vr_mbuf != NULL) { 164441502Swpaul m_freem(sc->vr_cdata.vr_tx_chain[i].vr_mbuf); 164541502Swpaul sc->vr_cdata.vr_tx_chain[i].vr_mbuf = NULL; 164641502Swpaul } 164741502Swpaul } 164841502Swpaul 164941502Swpaul bzero((char *)&sc->vr_ldata->vr_tx_list, 165041502Swpaul sizeof(sc->vr_ldata->vr_tx_list)); 165141502Swpaul 165241502Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 165341502Swpaul 165441502Swpaul return; 165541502Swpaul} 165641502Swpaul 165741502Swpaul/* 165841502Swpaul * Stop all chip I/O so that the kernel's probe routines don't 165941502Swpaul * get confused by errant DMAs when rebooting. 166041502Swpaul */ 166149610Swpaulstatic void vr_shutdown(dev) 166249610Swpaul device_t dev; 166341502Swpaul{ 166449610Swpaul struct vr_softc *sc; 166541502Swpaul 166649610Swpaul sc = device_get_softc(dev); 166749610Swpaul 166841502Swpaul vr_stop(sc); 166941502Swpaul 167041502Swpaul return; 167141502Swpaul} 1672