if_vr.c revision 62653
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 62653 2000-07-05 21:37:21Z 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 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 62653 2000-07-05 21:37:21Z wpaul $"; 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" }, 11662653Swpaul { VIA_VENDORID, VIA_DEVICEID_RHINE_II_2, 11762653Swpaul "VIA VT6102 Rhine II 10/100BaseTX" }, 11844238Swpaul { DELTA_VENDORID, DELTA_DEVICEID_RHINE_II, 11944238Swpaul "Delta Electronics Rhine II 10/100BaseTX" }, 12044238Swpaul { ADDTRON_VENDORID, ADDTRON_DEVICEID_RHINE_II, 12144238Swpaul "Addtron Technology Rhine II 10/100BaseTX" }, 12241502Swpaul { 0, 0, NULL } 12341502Swpaul}; 12441502Swpaul 12549610Swpaulstatic int vr_probe __P((device_t)); 12649610Swpaulstatic int vr_attach __P((device_t)); 12749610Swpaulstatic int vr_detach __P((device_t)); 12841502Swpaul 12941502Swpaulstatic int vr_newbuf __P((struct vr_softc *, 13049610Swpaul struct vr_chain_onefrag *, 13149610Swpaul struct mbuf *)); 13241502Swpaulstatic int vr_encap __P((struct vr_softc *, struct vr_chain *, 13341502Swpaul struct mbuf * )); 13441502Swpaul 13541502Swpaulstatic void vr_rxeof __P((struct vr_softc *)); 13641502Swpaulstatic void vr_rxeoc __P((struct vr_softc *)); 13741502Swpaulstatic void vr_txeof __P((struct vr_softc *)); 13841502Swpaulstatic void vr_txeoc __P((struct vr_softc *)); 13951432Swpaulstatic void vr_tick __P((void *)); 14041502Swpaulstatic void vr_intr __P((void *)); 14141502Swpaulstatic void vr_start __P((struct ifnet *)); 14241502Swpaulstatic int vr_ioctl __P((struct ifnet *, u_long, caddr_t)); 14341502Swpaulstatic void vr_init __P((void *)); 14441502Swpaulstatic void vr_stop __P((struct vr_softc *)); 14541502Swpaulstatic void vr_watchdog __P((struct ifnet *)); 14649610Swpaulstatic void vr_shutdown __P((device_t)); 14741502Swpaulstatic int vr_ifmedia_upd __P((struct ifnet *)); 14841502Swpaulstatic void vr_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 14941502Swpaul 15041502Swpaulstatic void vr_mii_sync __P((struct vr_softc *)); 15141502Swpaulstatic void vr_mii_send __P((struct vr_softc *, u_int32_t, int)); 15241502Swpaulstatic int vr_mii_readreg __P((struct vr_softc *, struct vr_mii_frame *)); 15341502Swpaulstatic int vr_mii_writereg __P((struct vr_softc *, struct vr_mii_frame *)); 15451432Swpaulstatic int vr_miibus_readreg __P((device_t, int, int)); 15551432Swpaulstatic int vr_miibus_writereg __P((device_t, int, int, int)); 15651432Swpaulstatic void vr_miibus_statchg __P((device_t)); 15741502Swpaul 15851432Swpaulstatic void vr_setcfg __P((struct vr_softc *, int)); 15941502Swpaulstatic u_int8_t vr_calchash __P((u_int8_t *)); 16041502Swpaulstatic void vr_setmulti __P((struct vr_softc *)); 16141502Swpaulstatic void vr_reset __P((struct vr_softc *)); 16241502Swpaulstatic int vr_list_rx_init __P((struct vr_softc *)); 16341502Swpaulstatic int vr_list_tx_init __P((struct vr_softc *)); 16441502Swpaul 16549610Swpaul#ifdef VR_USEIOSPACE 16649610Swpaul#define VR_RES SYS_RES_IOPORT 16749610Swpaul#define VR_RID VR_PCI_LOIO 16849610Swpaul#else 16949610Swpaul#define VR_RES SYS_RES_MEMORY 17049610Swpaul#define VR_RID VR_PCI_LOMEM 17149610Swpaul#endif 17249610Swpaul 17349610Swpaulstatic device_method_t vr_methods[] = { 17449610Swpaul /* Device interface */ 17549610Swpaul DEVMETHOD(device_probe, vr_probe), 17649610Swpaul DEVMETHOD(device_attach, vr_attach), 17749610Swpaul DEVMETHOD(device_detach, vr_detach), 17849610Swpaul DEVMETHOD(device_shutdown, vr_shutdown), 17951432Swpaul 18051432Swpaul /* bus interface */ 18151432Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 18251432Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 18351432Swpaul 18451432Swpaul /* MII interface */ 18551432Swpaul DEVMETHOD(miibus_readreg, vr_miibus_readreg), 18651432Swpaul DEVMETHOD(miibus_writereg, vr_miibus_writereg), 18751432Swpaul DEVMETHOD(miibus_statchg, vr_miibus_statchg), 18851432Swpaul 18949610Swpaul { 0, 0 } 19049610Swpaul}; 19149610Swpaul 19249610Swpaulstatic driver_t vr_driver = { 19351455Swpaul "vr", 19449610Swpaul vr_methods, 19549610Swpaul sizeof(struct vr_softc) 19649610Swpaul}; 19749610Swpaul 19849610Swpaulstatic devclass_t vr_devclass; 19949610Swpaul 20051533SwpaulDRIVER_MODULE(if_vr, pci, vr_driver, vr_devclass, 0, 0); 20151473SwpaulDRIVER_MODULE(miibus, vr, miibus_driver, miibus_devclass, 0, 0); 20249610Swpaul 20341502Swpaul#define VR_SETBIT(sc, reg, x) \ 20441502Swpaul CSR_WRITE_1(sc, reg, \ 20541502Swpaul CSR_READ_1(sc, reg) | x) 20641502Swpaul 20741502Swpaul#define VR_CLRBIT(sc, reg, x) \ 20841502Swpaul CSR_WRITE_1(sc, reg, \ 20941502Swpaul CSR_READ_1(sc, reg) & ~x) 21041502Swpaul 21141502Swpaul#define VR_SETBIT16(sc, reg, x) \ 21241502Swpaul CSR_WRITE_2(sc, reg, \ 21341502Swpaul CSR_READ_2(sc, reg) | x) 21441502Swpaul 21541502Swpaul#define VR_CLRBIT16(sc, reg, x) \ 21641502Swpaul CSR_WRITE_2(sc, reg, \ 21741502Swpaul CSR_READ_2(sc, reg) & ~x) 21841502Swpaul 21941502Swpaul#define VR_SETBIT32(sc, reg, x) \ 22041502Swpaul CSR_WRITE_4(sc, reg, \ 22141502Swpaul CSR_READ_4(sc, reg) | x) 22241502Swpaul 22341502Swpaul#define VR_CLRBIT32(sc, reg, x) \ 22441502Swpaul CSR_WRITE_4(sc, reg, \ 22541502Swpaul CSR_READ_4(sc, reg) & ~x) 22641502Swpaul 22741502Swpaul#define SIO_SET(x) \ 22841502Swpaul CSR_WRITE_1(sc, VR_MIICMD, \ 22941502Swpaul CSR_READ_1(sc, VR_MIICMD) | x) 23041502Swpaul 23141502Swpaul#define SIO_CLR(x) \ 23241502Swpaul CSR_WRITE_1(sc, VR_MIICMD, \ 23341502Swpaul CSR_READ_1(sc, VR_MIICMD) & ~x) 23441502Swpaul 23541502Swpaul/* 23641502Swpaul * Sync the PHYs by setting data bit and strobing the clock 32 times. 23741502Swpaul */ 23841502Swpaulstatic void vr_mii_sync(sc) 23941502Swpaul struct vr_softc *sc; 24041502Swpaul{ 24141502Swpaul register int i; 24241502Swpaul 24341502Swpaul SIO_SET(VR_MIICMD_DIR|VR_MIICMD_DATAIN); 24441502Swpaul 24541502Swpaul for (i = 0; i < 32; i++) { 24641502Swpaul SIO_SET(VR_MIICMD_CLK); 24741502Swpaul DELAY(1); 24841502Swpaul SIO_CLR(VR_MIICMD_CLK); 24941502Swpaul DELAY(1); 25041502Swpaul } 25141502Swpaul 25241502Swpaul return; 25341502Swpaul} 25441502Swpaul 25541502Swpaul/* 25641502Swpaul * Clock a series of bits through the MII. 25741502Swpaul */ 25841502Swpaulstatic void vr_mii_send(sc, bits, cnt) 25941502Swpaul struct vr_softc *sc; 26041502Swpaul u_int32_t bits; 26141502Swpaul int cnt; 26241502Swpaul{ 26341502Swpaul int i; 26441502Swpaul 26541502Swpaul SIO_CLR(VR_MIICMD_CLK); 26641502Swpaul 26741502Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 26841502Swpaul if (bits & i) { 26941502Swpaul SIO_SET(VR_MIICMD_DATAIN); 27041502Swpaul } else { 27141502Swpaul SIO_CLR(VR_MIICMD_DATAIN); 27241502Swpaul } 27341502Swpaul DELAY(1); 27441502Swpaul SIO_CLR(VR_MIICMD_CLK); 27541502Swpaul DELAY(1); 27641502Swpaul SIO_SET(VR_MIICMD_CLK); 27741502Swpaul } 27841502Swpaul} 27941502Swpaul 28041502Swpaul/* 28141502Swpaul * Read an PHY register through the MII. 28241502Swpaul */ 28341502Swpaulstatic int vr_mii_readreg(sc, frame) 28441502Swpaul struct vr_softc *sc; 28541502Swpaul struct vr_mii_frame *frame; 28641502Swpaul 28741502Swpaul{ 28841502Swpaul int i, ack, s; 28941502Swpaul 29041502Swpaul s = splimp(); 29141502Swpaul 29241502Swpaul /* 29341502Swpaul * Set up frame for RX. 29441502Swpaul */ 29541502Swpaul frame->mii_stdelim = VR_MII_STARTDELIM; 29641502Swpaul frame->mii_opcode = VR_MII_READOP; 29741502Swpaul frame->mii_turnaround = 0; 29841502Swpaul frame->mii_data = 0; 29941502Swpaul 30041502Swpaul CSR_WRITE_1(sc, VR_MIICMD, 0); 30141502Swpaul VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); 30241502Swpaul 30341502Swpaul /* 30441502Swpaul * Turn on data xmit. 30541502Swpaul */ 30641502Swpaul SIO_SET(VR_MIICMD_DIR); 30741502Swpaul 30841502Swpaul vr_mii_sync(sc); 30941502Swpaul 31041502Swpaul /* 31141502Swpaul * Send command/address info. 31241502Swpaul */ 31341502Swpaul vr_mii_send(sc, frame->mii_stdelim, 2); 31441502Swpaul vr_mii_send(sc, frame->mii_opcode, 2); 31541502Swpaul vr_mii_send(sc, frame->mii_phyaddr, 5); 31641502Swpaul vr_mii_send(sc, frame->mii_regaddr, 5); 31741502Swpaul 31841502Swpaul /* Idle bit */ 31941502Swpaul SIO_CLR((VR_MIICMD_CLK|VR_MIICMD_DATAIN)); 32041502Swpaul DELAY(1); 32141502Swpaul SIO_SET(VR_MIICMD_CLK); 32241502Swpaul DELAY(1); 32341502Swpaul 32441502Swpaul /* Turn off xmit. */ 32541502Swpaul SIO_CLR(VR_MIICMD_DIR); 32641502Swpaul 32741502Swpaul /* Check for ack */ 32841502Swpaul SIO_CLR(VR_MIICMD_CLK); 32941502Swpaul DELAY(1); 33041502Swpaul SIO_SET(VR_MIICMD_CLK); 33141502Swpaul DELAY(1); 33241502Swpaul ack = CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT; 33341502Swpaul 33441502Swpaul /* 33541502Swpaul * Now try reading data bits. If the ack failed, we still 33641502Swpaul * need to clock through 16 cycles to keep the PHY(s) in sync. 33741502Swpaul */ 33841502Swpaul if (ack) { 33941502Swpaul for(i = 0; i < 16; i++) { 34041502Swpaul SIO_CLR(VR_MIICMD_CLK); 34141502Swpaul DELAY(1); 34241502Swpaul SIO_SET(VR_MIICMD_CLK); 34341502Swpaul DELAY(1); 34441502Swpaul } 34541502Swpaul goto fail; 34641502Swpaul } 34741502Swpaul 34841502Swpaul for (i = 0x8000; i; i >>= 1) { 34941502Swpaul SIO_CLR(VR_MIICMD_CLK); 35041502Swpaul DELAY(1); 35141502Swpaul if (!ack) { 35241502Swpaul if (CSR_READ_4(sc, VR_MIICMD) & VR_MIICMD_DATAOUT) 35341502Swpaul frame->mii_data |= i; 35441502Swpaul DELAY(1); 35541502Swpaul } 35641502Swpaul SIO_SET(VR_MIICMD_CLK); 35741502Swpaul DELAY(1); 35841502Swpaul } 35941502Swpaul 36041502Swpaulfail: 36141502Swpaul 36241502Swpaul SIO_CLR(VR_MIICMD_CLK); 36341502Swpaul DELAY(1); 36441502Swpaul SIO_SET(VR_MIICMD_CLK); 36541502Swpaul DELAY(1); 36641502Swpaul 36741502Swpaul splx(s); 36841502Swpaul 36941502Swpaul if (ack) 37041502Swpaul return(1); 37141502Swpaul return(0); 37241502Swpaul} 37341502Swpaul 37441502Swpaul/* 37541502Swpaul * Write to a PHY register through the MII. 37641502Swpaul */ 37741502Swpaulstatic int vr_mii_writereg(sc, frame) 37841502Swpaul struct vr_softc *sc; 37941502Swpaul struct vr_mii_frame *frame; 38041502Swpaul 38141502Swpaul{ 38241502Swpaul int s; 38341502Swpaul 38441502Swpaul s = splimp(); 38541502Swpaul 38641502Swpaul CSR_WRITE_1(sc, VR_MIICMD, 0); 38741502Swpaul VR_SETBIT(sc, VR_MIICMD, VR_MIICMD_DIRECTPGM); 38841502Swpaul 38941502Swpaul /* 39041502Swpaul * Set up frame for TX. 39141502Swpaul */ 39241502Swpaul 39341502Swpaul frame->mii_stdelim = VR_MII_STARTDELIM; 39441502Swpaul frame->mii_opcode = VR_MII_WRITEOP; 39541502Swpaul frame->mii_turnaround = VR_MII_TURNAROUND; 39641502Swpaul 39741502Swpaul /* 39841502Swpaul * Turn on data output. 39941502Swpaul */ 40041502Swpaul SIO_SET(VR_MIICMD_DIR); 40141502Swpaul 40241502Swpaul vr_mii_sync(sc); 40341502Swpaul 40441502Swpaul vr_mii_send(sc, frame->mii_stdelim, 2); 40541502Swpaul vr_mii_send(sc, frame->mii_opcode, 2); 40641502Swpaul vr_mii_send(sc, frame->mii_phyaddr, 5); 40741502Swpaul vr_mii_send(sc, frame->mii_regaddr, 5); 40841502Swpaul vr_mii_send(sc, frame->mii_turnaround, 2); 40941502Swpaul vr_mii_send(sc, frame->mii_data, 16); 41041502Swpaul 41141502Swpaul /* Idle bit. */ 41241502Swpaul SIO_SET(VR_MIICMD_CLK); 41341502Swpaul DELAY(1); 41441502Swpaul SIO_CLR(VR_MIICMD_CLK); 41541502Swpaul DELAY(1); 41641502Swpaul 41741502Swpaul /* 41841502Swpaul * Turn off xmit. 41941502Swpaul */ 42041502Swpaul SIO_CLR(VR_MIICMD_DIR); 42141502Swpaul 42241502Swpaul splx(s); 42341502Swpaul 42441502Swpaul return(0); 42541502Swpaul} 42641502Swpaul 42751432Swpaulstatic int vr_miibus_readreg(dev, phy, reg) 42851432Swpaul device_t dev; 42951432Swpaul int phy, reg; 43051432Swpaul{ 43141502Swpaul struct vr_softc *sc; 43241502Swpaul struct vr_mii_frame frame; 43341502Swpaul 43451432Swpaul sc = device_get_softc(dev); 43541502Swpaul bzero((char *)&frame, sizeof(frame)); 43641502Swpaul 43751432Swpaul frame.mii_phyaddr = phy; 43841502Swpaul frame.mii_regaddr = reg; 43941502Swpaul vr_mii_readreg(sc, &frame); 44041502Swpaul 44141502Swpaul return(frame.mii_data); 44241502Swpaul} 44341502Swpaul 44451432Swpaulstatic int vr_miibus_writereg(dev, phy, reg, data) 44551432Swpaul device_t dev; 44651432Swpaul u_int16_t phy, reg, data; 44751432Swpaul{ 44841502Swpaul struct vr_softc *sc; 44941502Swpaul struct vr_mii_frame frame; 45041502Swpaul 45151432Swpaul sc = device_get_softc(dev); 45241502Swpaul bzero((char *)&frame, sizeof(frame)); 45341502Swpaul 45451432Swpaul frame.mii_phyaddr = phy; 45541502Swpaul frame.mii_regaddr = reg; 45641502Swpaul frame.mii_data = data; 45741502Swpaul 45841502Swpaul vr_mii_writereg(sc, &frame); 45941502Swpaul 46051432Swpaul return(0); 46151432Swpaul} 46251432Swpaul 46351432Swpaulstatic void vr_miibus_statchg(dev) 46451432Swpaul device_t dev; 46551432Swpaul{ 46651432Swpaul struct vr_softc *sc; 46751432Swpaul struct mii_data *mii; 46851432Swpaul 46951432Swpaul sc = device_get_softc(dev); 47051432Swpaul mii = device_get_softc(sc->vr_miibus); 47151432Swpaul vr_setcfg(sc, mii->mii_media_active); 47251432Swpaul 47341502Swpaul return; 47441502Swpaul} 47541502Swpaul 47641502Swpaul/* 47741502Swpaul * Calculate CRC of a multicast group address, return the lower 6 bits. 47841502Swpaul */ 47941502Swpaulstatic u_int8_t vr_calchash(addr) 48041502Swpaul u_int8_t *addr; 48141502Swpaul{ 48241502Swpaul u_int32_t crc, carry; 48341502Swpaul int i, j; 48441502Swpaul u_int8_t c; 48541502Swpaul 48641502Swpaul /* Compute CRC for the address value. */ 48741502Swpaul crc = 0xFFFFFFFF; /* initial value */ 48841502Swpaul 48941502Swpaul for (i = 0; i < 6; i++) { 49041502Swpaul c = *(addr + i); 49141502Swpaul for (j = 0; j < 8; j++) { 49241502Swpaul carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); 49341502Swpaul crc <<= 1; 49441502Swpaul c >>= 1; 49541502Swpaul if (carry) 49641502Swpaul crc = (crc ^ 0x04c11db6) | carry; 49741502Swpaul } 49841502Swpaul } 49941502Swpaul 50041502Swpaul /* return the filter bit position */ 50141502Swpaul return((crc >> 26) & 0x0000003F); 50241502Swpaul} 50341502Swpaul 50441502Swpaul/* 50541502Swpaul * Program the 64-bit multicast hash filter. 50641502Swpaul */ 50741502Swpaulstatic void vr_setmulti(sc) 50841502Swpaul struct vr_softc *sc; 50941502Swpaul{ 51041502Swpaul struct ifnet *ifp; 51141502Swpaul int h = 0; 51241502Swpaul u_int32_t hashes[2] = { 0, 0 }; 51341502Swpaul struct ifmultiaddr *ifma; 51441502Swpaul u_int8_t rxfilt; 51541502Swpaul int mcnt = 0; 51641502Swpaul 51741502Swpaul ifp = &sc->arpcom.ac_if; 51841502Swpaul 51941502Swpaul rxfilt = CSR_READ_1(sc, VR_RXCFG); 52041502Swpaul 52141502Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 52241502Swpaul rxfilt |= VR_RXCFG_RX_MULTI; 52341502Swpaul CSR_WRITE_1(sc, VR_RXCFG, rxfilt); 52441502Swpaul CSR_WRITE_4(sc, VR_MAR0, 0xFFFFFFFF); 52541502Swpaul CSR_WRITE_4(sc, VR_MAR1, 0xFFFFFFFF); 52641502Swpaul return; 52741502Swpaul } 52841502Swpaul 52941502Swpaul /* first, zot all the existing hash bits */ 53041502Swpaul CSR_WRITE_4(sc, VR_MAR0, 0); 53141502Swpaul CSR_WRITE_4(sc, VR_MAR1, 0); 53241502Swpaul 53341502Swpaul /* now program new ones */ 53441502Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 53541502Swpaul ifma = ifma->ifma_link.le_next) { 53641502Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 53741502Swpaul continue; 53841502Swpaul h = vr_calchash(LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 53941502Swpaul if (h < 32) 54041502Swpaul hashes[0] |= (1 << h); 54141502Swpaul else 54241502Swpaul hashes[1] |= (1 << (h - 32)); 54341502Swpaul mcnt++; 54441502Swpaul } 54541502Swpaul 54641502Swpaul if (mcnt) 54741502Swpaul rxfilt |= VR_RXCFG_RX_MULTI; 54841502Swpaul else 54941502Swpaul rxfilt &= ~VR_RXCFG_RX_MULTI; 55041502Swpaul 55141502Swpaul CSR_WRITE_4(sc, VR_MAR0, hashes[0]); 55241502Swpaul CSR_WRITE_4(sc, VR_MAR1, hashes[1]); 55341502Swpaul CSR_WRITE_1(sc, VR_RXCFG, rxfilt); 55441502Swpaul 55541502Swpaul return; 55641502Swpaul} 55741502Swpaul 55841502Swpaul/* 55941502Swpaul * In order to fiddle with the 56041502Swpaul * 'full-duplex' and '100Mbps' bits in the netconfig register, we 56141502Swpaul * first have to put the transmit and/or receive logic in the idle state. 56241502Swpaul */ 56351432Swpaulstatic void vr_setcfg(sc, media) 56441502Swpaul struct vr_softc *sc; 56551432Swpaul int media; 56641502Swpaul{ 56741502Swpaul int restart = 0; 56841502Swpaul 56941502Swpaul if (CSR_READ_2(sc, VR_COMMAND) & (VR_CMD_TX_ON|VR_CMD_RX_ON)) { 57041502Swpaul restart = 1; 57141502Swpaul VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_TX_ON|VR_CMD_RX_ON)); 57241502Swpaul } 57341502Swpaul 57451432Swpaul if ((media & IFM_GMASK) == IFM_FDX) 57541502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); 57641502Swpaul else 57741502Swpaul VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_FULLDUPLEX); 57841502Swpaul 57941502Swpaul if (restart) 58041502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON|VR_CMD_RX_ON); 58141502Swpaul 58241502Swpaul return; 58341502Swpaul} 58441502Swpaul 58541502Swpaulstatic void vr_reset(sc) 58641502Swpaul struct vr_softc *sc; 58741502Swpaul{ 58841502Swpaul register int i; 58941502Swpaul 59041502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RESET); 59141502Swpaul 59241502Swpaul for (i = 0; i < VR_TIMEOUT; i++) { 59341502Swpaul DELAY(10); 59441502Swpaul if (!(CSR_READ_2(sc, VR_COMMAND) & VR_CMD_RESET)) 59541502Swpaul break; 59641502Swpaul } 59741502Swpaul if (i == VR_TIMEOUT) 59841502Swpaul printf("vr%d: reset never completed!\n", sc->vr_unit); 59941502Swpaul 60041502Swpaul /* Wait a little while for the chip to get its brains in order. */ 60141502Swpaul DELAY(1000); 60241502Swpaul 60341502Swpaul return; 60441502Swpaul} 60541502Swpaul 60641502Swpaul/* 60741502Swpaul * Probe for a VIA Rhine chip. Check the PCI vendor and device 60841502Swpaul * IDs against our list and return a device name if we find a match. 60941502Swpaul */ 61049610Swpaulstatic int vr_probe(dev) 61149610Swpaul device_t dev; 61241502Swpaul{ 61341502Swpaul struct vr_type *t; 61441502Swpaul 61541502Swpaul t = vr_devs; 61641502Swpaul 61741502Swpaul while(t->vr_name != NULL) { 61849610Swpaul if ((pci_get_vendor(dev) == t->vr_vid) && 61949610Swpaul (pci_get_device(dev) == t->vr_did)) { 62049610Swpaul device_set_desc(dev, t->vr_name); 62149610Swpaul return(0); 62241502Swpaul } 62341502Swpaul t++; 62441502Swpaul } 62541502Swpaul 62649610Swpaul return(ENXIO); 62741502Swpaul} 62841502Swpaul 62941502Swpaul/* 63041502Swpaul * Attach the interface. Allocate softc structures, do ifmedia 63141502Swpaul * setup and ethernet/BPF attach. 63241502Swpaul */ 63349610Swpaulstatic int vr_attach(dev) 63449610Swpaul device_t dev; 63541502Swpaul{ 63651432Swpaul int i, s; 63741502Swpaul u_char eaddr[ETHER_ADDR_LEN]; 63841502Swpaul u_int32_t command; 63941502Swpaul struct vr_softc *sc; 64041502Swpaul struct ifnet *ifp; 64149610Swpaul int unit, error = 0, rid; 64241502Swpaul 64341502Swpaul s = splimp(); 64441502Swpaul 64549610Swpaul sc = device_get_softc(dev); 64649610Swpaul unit = device_get_unit(dev); 64749610Swpaul bzero(sc, sizeof(struct vr_softc *)); 64841502Swpaul 64941502Swpaul /* 65041502Swpaul * Handle power management nonsense. 65141502Swpaul */ 65241502Swpaul 65349610Swpaul command = pci_read_config(dev, VR_PCI_CAPID, 4) & 0x000000FF; 65441502Swpaul if (command == 0x01) { 65541502Swpaul 65649610Swpaul command = pci_read_config(dev, VR_PCI_PWRMGMTCTRL, 4); 65741502Swpaul if (command & VR_PSTATE_MASK) { 65841502Swpaul u_int32_t iobase, membase, irq; 65941502Swpaul 66041502Swpaul /* Save important PCI config data. */ 66149610Swpaul iobase = pci_read_config(dev, VR_PCI_LOIO, 4); 66249610Swpaul membase = pci_read_config(dev, VR_PCI_LOMEM, 4); 66349610Swpaul irq = pci_read_config(dev, VR_PCI_INTLINE, 4); 66441502Swpaul 66541502Swpaul /* Reset the power state. */ 66641502Swpaul printf("vr%d: chip is in D%d power mode " 66741502Swpaul "-- setting to D0\n", unit, command & VR_PSTATE_MASK); 66841502Swpaul command &= 0xFFFFFFFC; 66949610Swpaul pci_write_config(dev, VR_PCI_PWRMGMTCTRL, command, 4); 67041502Swpaul 67141502Swpaul /* Restore PCI config data. */ 67249610Swpaul pci_write_config(dev, VR_PCI_LOIO, iobase, 4); 67349610Swpaul pci_write_config(dev, VR_PCI_LOMEM, membase, 4); 67449610Swpaul pci_write_config(dev, VR_PCI_INTLINE, irq, 4); 67541502Swpaul } 67641502Swpaul } 67741502Swpaul 67841502Swpaul /* 67941502Swpaul * Map control/status registers. 68041502Swpaul */ 68161041Speter command = pci_read_config(dev, PCIR_COMMAND, 4); 68241502Swpaul command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 68361041Speter pci_write_config(dev, PCIR_COMMAND, command, 4); 68461041Speter command = pci_read_config(dev, PCIR_COMMAND, 4); 68541502Swpaul 68641502Swpaul#ifdef VR_USEIOSPACE 68741502Swpaul if (!(command & PCIM_CMD_PORTEN)) { 68841502Swpaul printf("vr%d: failed to enable I/O ports!\n", unit); 68941502Swpaul free(sc, M_DEVBUF); 69041502Swpaul goto fail; 69141502Swpaul } 69241502Swpaul#else 69341502Swpaul if (!(command & PCIM_CMD_MEMEN)) { 69441502Swpaul printf("vr%d: failed to enable memory mapping!\n", unit); 69541502Swpaul goto fail; 69641502Swpaul } 69749610Swpaul#endif 69841502Swpaul 69949610Swpaul rid = VR_RID; 70049610Swpaul sc->vr_res = bus_alloc_resource(dev, VR_RES, &rid, 70149610Swpaul 0, ~0, 1, RF_ACTIVE); 70249610Swpaul 70349610Swpaul if (sc->vr_res == NULL) { 70449610Swpaul printf("vr%d: couldn't map ports/memory\n", unit); 70549610Swpaul error = ENXIO; 70641502Swpaul goto fail; 70741502Swpaul } 70841502Swpaul 70949610Swpaul sc->vr_btag = rman_get_bustag(sc->vr_res); 71049610Swpaul sc->vr_bhandle = rman_get_bushandle(sc->vr_res); 71141502Swpaul 71241502Swpaul /* Allocate interrupt */ 71349610Swpaul rid = 0; 71449610Swpaul sc->vr_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 71549610Swpaul RF_SHAREABLE | RF_ACTIVE); 71649610Swpaul 71749610Swpaul if (sc->vr_irq == NULL) { 71841502Swpaul printf("vr%d: couldn't map interrupt\n", unit); 71949610Swpaul bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); 72049610Swpaul error = ENXIO; 72141502Swpaul goto fail; 72241502Swpaul } 72341502Swpaul 72449610Swpaul error = bus_setup_intr(dev, sc->vr_irq, INTR_TYPE_NET, 72549610Swpaul vr_intr, sc, &sc->vr_intrhand); 72649610Swpaul 72749610Swpaul if (error) { 72849610Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); 72949610Swpaul bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); 73049610Swpaul printf("vr%d: couldn't set up irq\n", unit); 73149610Swpaul goto fail; 73249610Swpaul } 73349610Swpaul 73441502Swpaul /* Reset the adapter. */ 73541502Swpaul vr_reset(sc); 73641502Swpaul 73741502Swpaul /* 73841502Swpaul * Get station address. The way the Rhine chips work, 73941502Swpaul * you're not allowed to directly access the EEPROM once 74041502Swpaul * they've been programmed a special way. Consequently, 74141502Swpaul * we need to read the node address from the PAR0 and PAR1 74241502Swpaul * registers. 74341502Swpaul */ 74441502Swpaul VR_SETBIT(sc, VR_EECSR, VR_EECSR_LOAD); 74541502Swpaul DELAY(200); 74641502Swpaul for (i = 0; i < ETHER_ADDR_LEN; i++) 74741502Swpaul eaddr[i] = CSR_READ_1(sc, VR_PAR0 + i); 74841502Swpaul 74941502Swpaul /* 75041502Swpaul * A Rhine chip was detected. Inform the world. 75141502Swpaul */ 75241502Swpaul printf("vr%d: Ethernet address: %6D\n", unit, eaddr, ":"); 75341502Swpaul 75441502Swpaul sc->vr_unit = unit; 75541502Swpaul bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 75641502Swpaul 75751432Swpaul sc->vr_ldata = contigmalloc(sizeof(struct vr_list_data), M_DEVBUF, 75851657Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 75951432Swpaul 76051432Swpaul if (sc->vr_ldata == NULL) { 76141502Swpaul printf("vr%d: no memory for list buffers!\n", unit); 76249610Swpaul bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); 76349610Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); 76449610Swpaul bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); 76549610Swpaul error = ENXIO; 76649610Swpaul goto fail; 76741502Swpaul } 76841502Swpaul 76941502Swpaul bzero(sc->vr_ldata, sizeof(struct vr_list_data)); 77041502Swpaul 77141502Swpaul ifp = &sc->arpcom.ac_if; 77241502Swpaul ifp->if_softc = sc; 77341502Swpaul ifp->if_unit = unit; 77441502Swpaul ifp->if_name = "vr"; 77541502Swpaul ifp->if_mtu = ETHERMTU; 77641502Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 77741502Swpaul ifp->if_ioctl = vr_ioctl; 77841502Swpaul ifp->if_output = ether_output; 77941502Swpaul ifp->if_start = vr_start; 78041502Swpaul ifp->if_watchdog = vr_watchdog; 78141502Swpaul ifp->if_init = vr_init; 78241502Swpaul ifp->if_baudrate = 10000000; 78343515Swpaul ifp->if_snd.ifq_maxlen = VR_TX_LIST_CNT - 1; 78441502Swpaul 78551432Swpaul /* 78651432Swpaul * Do MII setup. 78751432Swpaul */ 78851432Swpaul if (mii_phy_probe(dev, &sc->vr_miibus, 78951432Swpaul vr_ifmedia_upd, vr_ifmedia_sts)) { 79041502Swpaul printf("vr%d: MII without any phy!\n", sc->vr_unit); 79149610Swpaul bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); 79249610Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); 79349610Swpaul bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); 79451432Swpaul contigfree(sc->vr_ldata, 79551432Swpaul sizeof(struct vr_list_data), M_DEVBUF); 79649610Swpaul error = ENXIO; 79741502Swpaul goto fail; 79841502Swpaul } 79941502Swpaul 80051432Swpaul callout_handle_init(&sc->vr_stat_ch); 80141502Swpaul 80241502Swpaul /* 80341502Swpaul * Call MI attach routines. 80441502Swpaul */ 80541502Swpaul if_attach(ifp); 80641502Swpaul ether_ifattach(ifp); 80741502Swpaul 80841502Swpaul bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 80941502Swpaul 81041502Swpaulfail: 81141502Swpaul splx(s); 81249610Swpaul return(error); 81341502Swpaul} 81441502Swpaul 81549610Swpaulstatic int vr_detach(dev) 81649610Swpaul device_t dev; 81749610Swpaul{ 81849610Swpaul struct vr_softc *sc; 81949610Swpaul struct ifnet *ifp; 82049610Swpaul int s; 82149610Swpaul 82249610Swpaul s = splimp(); 82349610Swpaul 82449610Swpaul sc = device_get_softc(dev); 82549610Swpaul ifp = &sc->arpcom.ac_if; 82649610Swpaul 82749610Swpaul vr_stop(sc); 82849610Swpaul if_detach(ifp); 82949610Swpaul 83051432Swpaul bus_generic_detach(dev); 83151432Swpaul device_delete_child(dev, sc->vr_miibus); 83251432Swpaul 83349610Swpaul bus_teardown_intr(dev, sc->vr_irq, sc->vr_intrhand); 83449610Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vr_irq); 83549610Swpaul bus_release_resource(dev, VR_RES, VR_RID, sc->vr_res); 83649610Swpaul 83751432Swpaul contigfree(sc->vr_ldata, sizeof(struct vr_list_data), M_DEVBUF); 83849610Swpaul 83949610Swpaul splx(s); 84049610Swpaul 84149610Swpaul return(0); 84249610Swpaul} 84349610Swpaul 84441502Swpaul/* 84541502Swpaul * Initialize the transmit descriptors. 84641502Swpaul */ 84741502Swpaulstatic int vr_list_tx_init(sc) 84841502Swpaul struct vr_softc *sc; 84941502Swpaul{ 85041502Swpaul struct vr_chain_data *cd; 85141502Swpaul struct vr_list_data *ld; 85241502Swpaul int i; 85341502Swpaul 85441502Swpaul cd = &sc->vr_cdata; 85541502Swpaul ld = sc->vr_ldata; 85641502Swpaul for (i = 0; i < VR_TX_LIST_CNT; i++) { 85741502Swpaul cd->vr_tx_chain[i].vr_ptr = &ld->vr_tx_list[i]; 85841502Swpaul if (i == (VR_TX_LIST_CNT - 1)) 85941502Swpaul cd->vr_tx_chain[i].vr_nextdesc = 86041502Swpaul &cd->vr_tx_chain[0]; 86141502Swpaul else 86241502Swpaul cd->vr_tx_chain[i].vr_nextdesc = 86341502Swpaul &cd->vr_tx_chain[i + 1]; 86441502Swpaul } 86541502Swpaul 86641502Swpaul cd->vr_tx_free = &cd->vr_tx_chain[0]; 86741502Swpaul cd->vr_tx_tail = cd->vr_tx_head = NULL; 86841502Swpaul 86941502Swpaul return(0); 87041502Swpaul} 87141502Swpaul 87241502Swpaul 87341502Swpaul/* 87441502Swpaul * Initialize the RX descriptors and allocate mbufs for them. Note that 87541502Swpaul * we arrange the descriptors in a closed ring, so that the last descriptor 87641502Swpaul * points back to the first. 87741502Swpaul */ 87841502Swpaulstatic int vr_list_rx_init(sc) 87941502Swpaul struct vr_softc *sc; 88041502Swpaul{ 88141502Swpaul struct vr_chain_data *cd; 88241502Swpaul struct vr_list_data *ld; 88341502Swpaul int i; 88441502Swpaul 88541502Swpaul cd = &sc->vr_cdata; 88641502Swpaul ld = sc->vr_ldata; 88741502Swpaul 88841502Swpaul for (i = 0; i < VR_RX_LIST_CNT; i++) { 88941502Swpaul cd->vr_rx_chain[i].vr_ptr = 89041502Swpaul (struct vr_desc *)&ld->vr_rx_list[i]; 89149610Swpaul if (vr_newbuf(sc, &cd->vr_rx_chain[i], NULL) == ENOBUFS) 89241502Swpaul return(ENOBUFS); 89341502Swpaul if (i == (VR_RX_LIST_CNT - 1)) { 89441502Swpaul cd->vr_rx_chain[i].vr_nextdesc = 89541502Swpaul &cd->vr_rx_chain[0]; 89641502Swpaul ld->vr_rx_list[i].vr_next = 89741502Swpaul vtophys(&ld->vr_rx_list[0]); 89841502Swpaul } else { 89941502Swpaul cd->vr_rx_chain[i].vr_nextdesc = 90041502Swpaul &cd->vr_rx_chain[i + 1]; 90141502Swpaul ld->vr_rx_list[i].vr_next = 90241502Swpaul vtophys(&ld->vr_rx_list[i + 1]); 90341502Swpaul } 90441502Swpaul } 90541502Swpaul 90641502Swpaul cd->vr_rx_head = &cd->vr_rx_chain[0]; 90741502Swpaul 90841502Swpaul return(0); 90941502Swpaul} 91041502Swpaul 91141502Swpaul/* 91241502Swpaul * Initialize an RX descriptor and attach an MBUF cluster. 91341502Swpaul * Note: the length fields are only 11 bits wide, which means the 91441502Swpaul * largest size we can specify is 2047. This is important because 91541502Swpaul * MCLBYTES is 2048, so we have to subtract one otherwise we'll 91641502Swpaul * overflow the field and make a mess. 91741502Swpaul */ 91849610Swpaulstatic int vr_newbuf(sc, c, m) 91941502Swpaul struct vr_softc *sc; 92041502Swpaul struct vr_chain_onefrag *c; 92149610Swpaul struct mbuf *m; 92241502Swpaul{ 92341502Swpaul struct mbuf *m_new = NULL; 92441502Swpaul 92549610Swpaul if (m == NULL) { 92649610Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 92749610Swpaul if (m_new == NULL) { 92849610Swpaul printf("vr%d: no memory for rx list " 92949610Swpaul "-- packet dropped!\n", sc->vr_unit); 93049610Swpaul return(ENOBUFS); 93149610Swpaul } 93241502Swpaul 93349610Swpaul MCLGET(m_new, M_DONTWAIT); 93449610Swpaul if (!(m_new->m_flags & M_EXT)) { 93549610Swpaul printf("vr%d: no memory for rx list " 93649610Swpaul "-- packet dropped!\n", sc->vr_unit); 93749610Swpaul m_freem(m_new); 93849610Swpaul return(ENOBUFS); 93949610Swpaul } 94049610Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 94149610Swpaul } else { 94249610Swpaul m_new = m; 94349610Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 94449610Swpaul m_new->m_data = m_new->m_ext.ext_buf; 94541502Swpaul } 94641502Swpaul 94749610Swpaul m_adj(m_new, sizeof(u_int64_t)); 94849610Swpaul 94941502Swpaul c->vr_mbuf = m_new; 95041502Swpaul c->vr_ptr->vr_status = VR_RXSTAT; 95141502Swpaul c->vr_ptr->vr_data = vtophys(mtod(m_new, caddr_t)); 95242491Swpaul c->vr_ptr->vr_ctl = VR_RXCTL | VR_RXLEN; 95341502Swpaul 95441502Swpaul return(0); 95541502Swpaul} 95641502Swpaul 95741502Swpaul/* 95841502Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to 95941502Swpaul * the higher level protocols. 96041502Swpaul */ 96141502Swpaulstatic void vr_rxeof(sc) 96241502Swpaul struct vr_softc *sc; 96341502Swpaul{ 96441502Swpaul struct ether_header *eh; 96541502Swpaul struct mbuf *m; 96641502Swpaul struct ifnet *ifp; 96741502Swpaul struct vr_chain_onefrag *cur_rx; 96841502Swpaul int total_len = 0; 96941502Swpaul u_int32_t rxstat; 97041502Swpaul 97141502Swpaul ifp = &sc->arpcom.ac_if; 97241502Swpaul 97341502Swpaul while(!((rxstat = sc->vr_cdata.vr_rx_head->vr_ptr->vr_status) & 97441502Swpaul VR_RXSTAT_OWN)) { 97549610Swpaul struct mbuf *m0 = NULL; 97649610Swpaul 97741502Swpaul cur_rx = sc->vr_cdata.vr_rx_head; 97841502Swpaul sc->vr_cdata.vr_rx_head = cur_rx->vr_nextdesc; 97949610Swpaul m = cur_rx->vr_mbuf; 98041502Swpaul 98141502Swpaul /* 98241502Swpaul * If an error occurs, update stats, clear the 98341502Swpaul * status word and leave the mbuf cluster in place: 98441502Swpaul * it should simply get re-used next time this descriptor 98541502Swpaul * comes up in the ring. 98641502Swpaul */ 98741502Swpaul if (rxstat & VR_RXSTAT_RXERR) { 98841502Swpaul ifp->if_ierrors++; 98941502Swpaul printf("vr%d: rx error: ", sc->vr_unit); 99041502Swpaul switch(rxstat & 0x000000FF) { 99141502Swpaul case VR_RXSTAT_CRCERR: 99241502Swpaul printf("crc error\n"); 99341502Swpaul break; 99441502Swpaul case VR_RXSTAT_FRAMEALIGNERR: 99541502Swpaul printf("frame alignment error\n"); 99641502Swpaul break; 99741502Swpaul case VR_RXSTAT_FIFOOFLOW: 99841502Swpaul printf("FIFO overflow\n"); 99941502Swpaul break; 100041502Swpaul case VR_RXSTAT_GIANT: 100141502Swpaul printf("received giant packet\n"); 100241502Swpaul break; 100341502Swpaul case VR_RXSTAT_RUNT: 100441502Swpaul printf("received runt packet\n"); 100541502Swpaul break; 100641502Swpaul case VR_RXSTAT_BUSERR: 100741502Swpaul printf("system bus error\n"); 100841502Swpaul break; 100941502Swpaul case VR_RXSTAT_BUFFERR: 101041502Swpaul printf("rx buffer error\n"); 101141502Swpaul break; 101241502Swpaul default: 101341502Swpaul printf("unknown rx error\n"); 101441502Swpaul break; 101541502Swpaul } 101649610Swpaul vr_newbuf(sc, cur_rx, m); 101741502Swpaul continue; 101841502Swpaul } 101941502Swpaul 102041502Swpaul /* No errors; receive the packet. */ 102141502Swpaul total_len = VR_RXBYTES(cur_rx->vr_ptr->vr_status); 102241502Swpaul 102341502Swpaul /* 102442048Swpaul * XXX The VIA Rhine chip includes the CRC with every 102542048Swpaul * received frame, and there's no way to turn this 102642048Swpaul * behavior off (at least, I can't find anything in 102742048Swpaul * the manual that explains how to do it) so we have 102842048Swpaul * to trim off the CRC manually. 102942048Swpaul */ 103042048Swpaul total_len -= ETHER_CRC_LEN; 103142048Swpaul 103249610Swpaul m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, 103349610Swpaul total_len + ETHER_ALIGN, 0, ifp, NULL); 103449610Swpaul vr_newbuf(sc, cur_rx, m); 103549610Swpaul if (m0 == NULL) { 103641502Swpaul ifp->if_ierrors++; 103741502Swpaul continue; 103841502Swpaul } 103949610Swpaul m_adj(m0, ETHER_ALIGN); 104049610Swpaul m = m0; 104141502Swpaul 104241502Swpaul ifp->if_ipackets++; 104341502Swpaul eh = mtod(m, struct ether_header *); 104449610Swpaul 104541502Swpaul /* Remove header from mbuf and pass it on. */ 104641502Swpaul m_adj(m, sizeof(struct ether_header)); 104741502Swpaul ether_input(ifp, eh, m); 104841502Swpaul } 104941502Swpaul 105041502Swpaul return; 105141502Swpaul} 105241502Swpaul 105341502Swpaulvoid vr_rxeoc(sc) 105441502Swpaul struct vr_softc *sc; 105541502Swpaul{ 105641502Swpaul 105741502Swpaul vr_rxeof(sc); 105841502Swpaul VR_CLRBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); 105941502Swpaul CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); 106041502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_ON); 106141502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_RX_GO); 106241502Swpaul 106341502Swpaul return; 106441502Swpaul} 106541502Swpaul 106641502Swpaul/* 106741502Swpaul * A frame was downloaded to the chip. It's safe for us to clean up 106841502Swpaul * the list buffers. 106941502Swpaul */ 107041502Swpaul 107141502Swpaulstatic void vr_txeof(sc) 107241502Swpaul struct vr_softc *sc; 107341502Swpaul{ 107441502Swpaul struct vr_chain *cur_tx; 107541502Swpaul struct ifnet *ifp; 107641502Swpaul 107741502Swpaul ifp = &sc->arpcom.ac_if; 107841502Swpaul 107941502Swpaul /* Clear the timeout timer. */ 108041502Swpaul ifp->if_timer = 0; 108141502Swpaul 108241502Swpaul /* Sanity check. */ 108341502Swpaul if (sc->vr_cdata.vr_tx_head == NULL) 108441502Swpaul return; 108541502Swpaul 108641502Swpaul /* 108741502Swpaul * Go through our tx list and free mbufs for those 108841502Swpaul * frames that have been transmitted. 108941502Swpaul */ 109041502Swpaul while(sc->vr_cdata.vr_tx_head->vr_mbuf != NULL) { 109141502Swpaul u_int32_t txstat; 109241502Swpaul 109341502Swpaul cur_tx = sc->vr_cdata.vr_tx_head; 109441502Swpaul txstat = cur_tx->vr_ptr->vr_status; 109541502Swpaul 109642491Swpaul if (txstat & VR_TXSTAT_OWN) 109741502Swpaul break; 109841502Swpaul 109941502Swpaul if (txstat & VR_TXSTAT_ERRSUM) { 110041502Swpaul ifp->if_oerrors++; 110141502Swpaul if (txstat & VR_TXSTAT_DEFER) 110241502Swpaul ifp->if_collisions++; 110341502Swpaul if (txstat & VR_TXSTAT_LATECOLL) 110441502Swpaul ifp->if_collisions++; 110541502Swpaul } 110641502Swpaul 110741502Swpaul ifp->if_collisions +=(txstat & VR_TXSTAT_COLLCNT) >> 3; 110841502Swpaul 110941502Swpaul ifp->if_opackets++; 111051432Swpaul if (cur_tx->vr_mbuf != NULL) { 111151432Swpaul m_freem(cur_tx->vr_mbuf); 111251432Swpaul cur_tx->vr_mbuf = NULL; 111351432Swpaul } 111441502Swpaul 111541502Swpaul if (sc->vr_cdata.vr_tx_head == sc->vr_cdata.vr_tx_tail) { 111641502Swpaul sc->vr_cdata.vr_tx_head = NULL; 111741502Swpaul sc->vr_cdata.vr_tx_tail = NULL; 111841502Swpaul break; 111941502Swpaul } 112041502Swpaul 112141502Swpaul sc->vr_cdata.vr_tx_head = cur_tx->vr_nextdesc; 112241502Swpaul } 112341502Swpaul 112441502Swpaul return; 112541502Swpaul} 112641502Swpaul 112741502Swpaul/* 112841502Swpaul * TX 'end of channel' interrupt handler. 112941502Swpaul */ 113041502Swpaulstatic void vr_txeoc(sc) 113141502Swpaul struct vr_softc *sc; 113241502Swpaul{ 113341502Swpaul struct ifnet *ifp; 113441502Swpaul 113541502Swpaul ifp = &sc->arpcom.ac_if; 113641502Swpaul 113741502Swpaul ifp->if_timer = 0; 113841502Swpaul 113941502Swpaul if (sc->vr_cdata.vr_tx_head == NULL) { 114041502Swpaul ifp->if_flags &= ~IFF_OACTIVE; 114141502Swpaul sc->vr_cdata.vr_tx_tail = NULL; 114241502Swpaul } 114341502Swpaul 114441502Swpaul return; 114541502Swpaul} 114641502Swpaul 114751432Swpaulstatic void vr_tick(xsc) 114851432Swpaul void *xsc; 114951432Swpaul{ 115051432Swpaul struct vr_softc *sc; 115151432Swpaul struct mii_data *mii; 115251432Swpaul int s; 115351432Swpaul 115451432Swpaul s = splimp(); 115551432Swpaul 115651432Swpaul sc = xsc; 115751432Swpaul mii = device_get_softc(sc->vr_miibus); 115851432Swpaul mii_tick(mii); 115951432Swpaul 116051432Swpaul sc->vr_stat_ch = timeout(vr_tick, sc, hz); 116151432Swpaul 116251432Swpaul splx(s); 116351432Swpaul 116451432Swpaul return; 116551432Swpaul} 116651432Swpaul 116741502Swpaulstatic void vr_intr(arg) 116841502Swpaul void *arg; 116941502Swpaul{ 117041502Swpaul struct vr_softc *sc; 117141502Swpaul struct ifnet *ifp; 117241502Swpaul u_int16_t status; 117341502Swpaul 117441502Swpaul sc = arg; 117541502Swpaul ifp = &sc->arpcom.ac_if; 117641502Swpaul 117741502Swpaul /* Supress unwanted interrupts. */ 117841502Swpaul if (!(ifp->if_flags & IFF_UP)) { 117941502Swpaul vr_stop(sc); 118041502Swpaul return; 118141502Swpaul } 118241502Swpaul 118341502Swpaul /* Disable interrupts. */ 118441502Swpaul CSR_WRITE_2(sc, VR_IMR, 0x0000); 118541502Swpaul 118641502Swpaul for (;;) { 118741502Swpaul 118841502Swpaul status = CSR_READ_2(sc, VR_ISR); 118941502Swpaul if (status) 119041502Swpaul CSR_WRITE_2(sc, VR_ISR, status); 119141502Swpaul 119241502Swpaul if ((status & VR_INTRS) == 0) 119341502Swpaul break; 119441502Swpaul 119541502Swpaul if (status & VR_ISR_RX_OK) 119641502Swpaul vr_rxeof(sc); 119741502Swpaul 119841502Swpaul if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) || 119941502Swpaul (status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW) || 120041502Swpaul (status & VR_ISR_RX_DROPPED)) { 120141502Swpaul vr_rxeof(sc); 120241502Swpaul vr_rxeoc(sc); 120341502Swpaul } 120441502Swpaul 120541502Swpaul if (status & VR_ISR_TX_OK) { 120641502Swpaul vr_txeof(sc); 120741502Swpaul vr_txeoc(sc); 120841502Swpaul } 120941502Swpaul 121041502Swpaul if ((status & VR_ISR_TX_UNDERRUN)||(status & VR_ISR_TX_ABRT)){ 121141502Swpaul ifp->if_oerrors++; 121241502Swpaul vr_txeof(sc); 121341502Swpaul if (sc->vr_cdata.vr_tx_head != NULL) { 121441502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON); 121541502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO); 121641502Swpaul } 121741502Swpaul } 121841502Swpaul 121941502Swpaul if (status & VR_ISR_BUSERR) { 122041502Swpaul vr_reset(sc); 122141502Swpaul vr_init(sc); 122241502Swpaul } 122341502Swpaul } 122441502Swpaul 122541502Swpaul /* Re-enable interrupts. */ 122641502Swpaul CSR_WRITE_2(sc, VR_IMR, VR_INTRS); 122741502Swpaul 122841502Swpaul if (ifp->if_snd.ifq_head != NULL) { 122941502Swpaul vr_start(ifp); 123041502Swpaul } 123141502Swpaul 123241502Swpaul return; 123341502Swpaul} 123441502Swpaul 123541502Swpaul/* 123641502Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 123741502Swpaul * pointers to the fragment pointers. 123841502Swpaul */ 123941502Swpaulstatic int vr_encap(sc, c, m_head) 124041502Swpaul struct vr_softc *sc; 124141502Swpaul struct vr_chain *c; 124241502Swpaul struct mbuf *m_head; 124341502Swpaul{ 124441502Swpaul int frag = 0; 124541502Swpaul struct vr_desc *f = NULL; 124641502Swpaul int total_len; 124741502Swpaul struct mbuf *m; 124841502Swpaul 124941502Swpaul m = m_head; 125041502Swpaul total_len = 0; 125141502Swpaul 125241502Swpaul /* 125341502Swpaul * The VIA Rhine wants packet buffers to be longword 125441502Swpaul * aligned, but very often our mbufs aren't. Rather than 125541502Swpaul * waste time trying to decide when to copy and when not 125641502Swpaul * to copy, just do it all the time. 125741502Swpaul */ 125841502Swpaul if (m != NULL) { 125941502Swpaul struct mbuf *m_new = NULL; 126041502Swpaul 126141502Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 126241502Swpaul if (m_new == NULL) { 126341502Swpaul printf("vr%d: no memory for tx list", sc->vr_unit); 126441502Swpaul return(1); 126541502Swpaul } 126641502Swpaul if (m_head->m_pkthdr.len > MHLEN) { 126741502Swpaul MCLGET(m_new, M_DONTWAIT); 126841502Swpaul if (!(m_new->m_flags & M_EXT)) { 126941502Swpaul m_freem(m_new); 127041502Swpaul printf("vr%d: no memory for tx list", 127141502Swpaul sc->vr_unit); 127241502Swpaul return(1); 127341502Swpaul } 127441502Swpaul } 127541502Swpaul m_copydata(m_head, 0, m_head->m_pkthdr.len, 127641502Swpaul mtod(m_new, caddr_t)); 127741502Swpaul m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len; 127841502Swpaul m_freem(m_head); 127941502Swpaul m_head = m_new; 128041502Swpaul /* 128141502Swpaul * The Rhine chip doesn't auto-pad, so we have to make 128241502Swpaul * sure to pad short frames out to the minimum frame length 128341502Swpaul * ourselves. 128441502Swpaul */ 128541502Swpaul if (m_head->m_len < VR_MIN_FRAMELEN) { 128641502Swpaul m_new->m_pkthdr.len += VR_MIN_FRAMELEN - m_new->m_len; 128741502Swpaul m_new->m_len = m_new->m_pkthdr.len; 128841502Swpaul } 128941502Swpaul f = c->vr_ptr; 129041502Swpaul f->vr_data = vtophys(mtod(m_new, caddr_t)); 129141502Swpaul f->vr_ctl = total_len = m_new->m_len; 129241502Swpaul f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG; 129341502Swpaul f->vr_status = 0; 129441502Swpaul frag = 1; 129541502Swpaul } 129641502Swpaul 129741502Swpaul c->vr_mbuf = m_head; 129842491Swpaul c->vr_ptr->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT; 129941502Swpaul c->vr_ptr->vr_next = vtophys(c->vr_nextdesc->vr_ptr); 130041502Swpaul 130141502Swpaul return(0); 130241502Swpaul} 130341502Swpaul 130441502Swpaul/* 130541502Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 130641502Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 130741502Swpaul * copy of the pointers since the transmit list fragment pointers are 130841502Swpaul * physical addresses. 130941502Swpaul */ 131041502Swpaul 131141502Swpaulstatic void vr_start(ifp) 131241502Swpaul struct ifnet *ifp; 131341502Swpaul{ 131441502Swpaul struct vr_softc *sc; 131541502Swpaul struct mbuf *m_head = NULL; 131641502Swpaul struct vr_chain *cur_tx = NULL, *start_tx; 131741502Swpaul 131841502Swpaul sc = ifp->if_softc; 131941502Swpaul 132051432Swpaul if (ifp->if_flags & IFF_OACTIVE) 132141502Swpaul return; 132241502Swpaul 132341502Swpaul /* 132441502Swpaul * Check for an available queue slot. If there are none, 132541502Swpaul * punt. 132641502Swpaul */ 132741502Swpaul if (sc->vr_cdata.vr_tx_free->vr_mbuf != NULL) { 132841502Swpaul ifp->if_flags |= IFF_OACTIVE; 132941502Swpaul return; 133041502Swpaul } 133141502Swpaul 133241502Swpaul start_tx = sc->vr_cdata.vr_tx_free; 133341502Swpaul 133441502Swpaul while(sc->vr_cdata.vr_tx_free->vr_mbuf == NULL) { 133541502Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 133641502Swpaul if (m_head == NULL) 133741502Swpaul break; 133841502Swpaul 133941502Swpaul /* Pick a descriptor off the free list. */ 134041502Swpaul cur_tx = sc->vr_cdata.vr_tx_free; 134141502Swpaul sc->vr_cdata.vr_tx_free = cur_tx->vr_nextdesc; 134241502Swpaul 134341502Swpaul /* Pack the data into the descriptor. */ 134441502Swpaul vr_encap(sc, cur_tx, m_head); 134541502Swpaul 134641502Swpaul if (cur_tx != start_tx) 134741502Swpaul VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; 134841502Swpaul 134941502Swpaul /* 135041502Swpaul * If there's a BPF listener, bounce a copy of this frame 135141502Swpaul * to him. 135241502Swpaul */ 135341502Swpaul if (ifp->if_bpf) 135441502Swpaul bpf_mtap(ifp, cur_tx->vr_mbuf); 135551583Swpaul 135642491Swpaul VR_TXOWN(cur_tx) = VR_TXSTAT_OWN; 135751432Swpaul VR_SETBIT16(sc, VR_COMMAND, /*VR_CMD_TX_ON|*/VR_CMD_TX_GO); 135841502Swpaul } 135941502Swpaul 136041502Swpaul /* 136141526Swpaul * If there are no frames queued, bail. 136241526Swpaul */ 136341526Swpaul if (cur_tx == NULL) 136441526Swpaul return; 136541526Swpaul 136641502Swpaul sc->vr_cdata.vr_tx_tail = cur_tx; 136741502Swpaul 136842491Swpaul if (sc->vr_cdata.vr_tx_head == NULL) 136941502Swpaul sc->vr_cdata.vr_tx_head = start_tx; 137041502Swpaul 137141502Swpaul /* 137241502Swpaul * Set a timeout in case the chip goes out to lunch. 137341502Swpaul */ 137441502Swpaul ifp->if_timer = 5; 137541502Swpaul 137641502Swpaul return; 137741502Swpaul} 137841502Swpaul 137941502Swpaulstatic void vr_init(xsc) 138041502Swpaul void *xsc; 138141502Swpaul{ 138241502Swpaul struct vr_softc *sc = xsc; 138341502Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 138451432Swpaul struct mii_data *mii; 138541502Swpaul int s; 138641502Swpaul 138741502Swpaul s = splimp(); 138841502Swpaul 138951432Swpaul mii = device_get_softc(sc->vr_miibus); 139041502Swpaul 139141502Swpaul /* 139241502Swpaul * Cancel pending I/O and free all RX/TX buffers. 139341502Swpaul */ 139441502Swpaul vr_stop(sc); 139541502Swpaul vr_reset(sc); 139641502Swpaul 139741502Swpaul VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_THRESH); 139841502Swpaul VR_SETBIT(sc, VR_RXCFG, VR_RXTHRESH_STORENFWD); 139941502Swpaul 140041502Swpaul VR_CLRBIT(sc, VR_TXCFG, VR_TXCFG_TX_THRESH); 140141502Swpaul VR_SETBIT(sc, VR_TXCFG, VR_TXTHRESH_STORENFWD); 140241502Swpaul 140341502Swpaul /* Init circular RX list. */ 140441502Swpaul if (vr_list_rx_init(sc) == ENOBUFS) { 140541502Swpaul printf("vr%d: initialization failed: no " 140641502Swpaul "memory for rx buffers\n", sc->vr_unit); 140741502Swpaul vr_stop(sc); 140841502Swpaul (void)splx(s); 140941502Swpaul return; 141041502Swpaul } 141141502Swpaul 141241502Swpaul /* 141341502Swpaul * Init tx descriptors. 141441502Swpaul */ 141541502Swpaul vr_list_tx_init(sc); 141641502Swpaul 141741502Swpaul /* If we want promiscuous mode, set the allframes bit. */ 141841502Swpaul if (ifp->if_flags & IFF_PROMISC) 141941502Swpaul VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); 142041502Swpaul else 142141502Swpaul VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_PROMISC); 142241502Swpaul 142341502Swpaul /* Set capture broadcast bit to capture broadcast frames. */ 142441502Swpaul if (ifp->if_flags & IFF_BROADCAST) 142541502Swpaul VR_SETBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); 142641502Swpaul else 142741502Swpaul VR_CLRBIT(sc, VR_RXCFG, VR_RXCFG_RX_BROAD); 142841502Swpaul 142941502Swpaul /* 143041502Swpaul * Program the multicast filter, if necessary. 143141502Swpaul */ 143241502Swpaul vr_setmulti(sc); 143341502Swpaul 143441502Swpaul /* 143541502Swpaul * Load the address of the RX list. 143641502Swpaul */ 143741502Swpaul CSR_WRITE_4(sc, VR_RXADDR, vtophys(sc->vr_cdata.vr_rx_head->vr_ptr)); 143841502Swpaul 143941502Swpaul /* Enable receiver and transmitter. */ 144041502Swpaul CSR_WRITE_2(sc, VR_COMMAND, VR_CMD_TX_NOPOLL|VR_CMD_START| 144141502Swpaul VR_CMD_TX_ON|VR_CMD_RX_ON| 144241502Swpaul VR_CMD_RX_GO); 144341502Swpaul 144441502Swpaul CSR_WRITE_4(sc, VR_TXADDR, vtophys(&sc->vr_ldata->vr_tx_list[0])); 144541502Swpaul 144641502Swpaul /* 144741502Swpaul * Enable interrupts. 144841502Swpaul */ 144941502Swpaul CSR_WRITE_2(sc, VR_ISR, 0xFFFF); 145041502Swpaul CSR_WRITE_2(sc, VR_IMR, VR_INTRS); 145141502Swpaul 145251432Swpaul mii_mediachg(mii); 145341502Swpaul 145441502Swpaul ifp->if_flags |= IFF_RUNNING; 145541502Swpaul ifp->if_flags &= ~IFF_OACTIVE; 145641502Swpaul 145741502Swpaul (void)splx(s); 145841502Swpaul 145951432Swpaul sc->vr_stat_ch = timeout(vr_tick, sc, hz); 146051432Swpaul 146141502Swpaul return; 146241502Swpaul} 146341502Swpaul 146441502Swpaul/* 146541502Swpaul * Set media options. 146641502Swpaul */ 146741502Swpaulstatic int vr_ifmedia_upd(ifp) 146841502Swpaul struct ifnet *ifp; 146941502Swpaul{ 147041502Swpaul struct vr_softc *sc; 147141502Swpaul 147241502Swpaul sc = ifp->if_softc; 147341502Swpaul 147451432Swpaul if (ifp->if_flags & IFF_UP) 147551432Swpaul vr_init(sc); 147641502Swpaul 147741502Swpaul return(0); 147841502Swpaul} 147941502Swpaul 148041502Swpaul/* 148141502Swpaul * Report current media status. 148241502Swpaul */ 148341502Swpaulstatic void vr_ifmedia_sts(ifp, ifmr) 148441502Swpaul struct ifnet *ifp; 148541502Swpaul struct ifmediareq *ifmr; 148641502Swpaul{ 148741502Swpaul struct vr_softc *sc; 148851432Swpaul struct mii_data *mii; 148941502Swpaul 149041502Swpaul sc = ifp->if_softc; 149151432Swpaul mii = device_get_softc(sc->vr_miibus); 149251432Swpaul mii_pollstat(mii); 149351432Swpaul ifmr->ifm_active = mii->mii_media_active; 149451432Swpaul ifmr->ifm_status = mii->mii_media_status; 149541502Swpaul 149641502Swpaul return; 149741502Swpaul} 149841502Swpaul 149941502Swpaulstatic int vr_ioctl(ifp, command, data) 150041502Swpaul struct ifnet *ifp; 150141502Swpaul u_long command; 150241502Swpaul caddr_t data; 150341502Swpaul{ 150441502Swpaul struct vr_softc *sc = ifp->if_softc; 150541502Swpaul struct ifreq *ifr = (struct ifreq *) data; 150651432Swpaul struct mii_data *mii; 150741502Swpaul int s, error = 0; 150841502Swpaul 150941502Swpaul s = splimp(); 151041502Swpaul 151141502Swpaul switch(command) { 151241502Swpaul case SIOCSIFADDR: 151341502Swpaul case SIOCGIFADDR: 151441502Swpaul case SIOCSIFMTU: 151541502Swpaul error = ether_ioctl(ifp, command, data); 151641502Swpaul break; 151741502Swpaul case SIOCSIFFLAGS: 151841502Swpaul if (ifp->if_flags & IFF_UP) { 151941502Swpaul vr_init(sc); 152041502Swpaul } else { 152141502Swpaul if (ifp->if_flags & IFF_RUNNING) 152241502Swpaul vr_stop(sc); 152341502Swpaul } 152441502Swpaul error = 0; 152541502Swpaul break; 152641502Swpaul case SIOCADDMULTI: 152741502Swpaul case SIOCDELMULTI: 152841502Swpaul vr_setmulti(sc); 152941502Swpaul error = 0; 153041502Swpaul break; 153141502Swpaul case SIOCGIFMEDIA: 153241502Swpaul case SIOCSIFMEDIA: 153351432Swpaul mii = device_get_softc(sc->vr_miibus); 153451432Swpaul error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 153541502Swpaul break; 153641502Swpaul default: 153741502Swpaul error = EINVAL; 153841502Swpaul break; 153941502Swpaul } 154041502Swpaul 154141502Swpaul (void)splx(s); 154241502Swpaul 154341502Swpaul return(error); 154441502Swpaul} 154541502Swpaul 154641502Swpaulstatic void vr_watchdog(ifp) 154741502Swpaul struct ifnet *ifp; 154841502Swpaul{ 154941502Swpaul struct vr_softc *sc; 155041502Swpaul 155141502Swpaul sc = ifp->if_softc; 155241502Swpaul 155341502Swpaul ifp->if_oerrors++; 155441502Swpaul printf("vr%d: watchdog timeout\n", sc->vr_unit); 155541502Swpaul 155641502Swpaul vr_stop(sc); 155741502Swpaul vr_reset(sc); 155841502Swpaul vr_init(sc); 155941502Swpaul 156041502Swpaul if (ifp->if_snd.ifq_head != NULL) 156141502Swpaul vr_start(ifp); 156241502Swpaul 156341502Swpaul return; 156441502Swpaul} 156541502Swpaul 156641502Swpaul/* 156741502Swpaul * Stop the adapter and free any mbufs allocated to the 156841502Swpaul * RX and TX lists. 156941502Swpaul */ 157041502Swpaulstatic void vr_stop(sc) 157141502Swpaul struct vr_softc *sc; 157241502Swpaul{ 157341502Swpaul register int i; 157441502Swpaul struct ifnet *ifp; 157541502Swpaul 157641502Swpaul ifp = &sc->arpcom.ac_if; 157741502Swpaul ifp->if_timer = 0; 157841502Swpaul 157951432Swpaul untimeout(vr_tick, sc, sc->vr_stat_ch); 158051432Swpaul 158141502Swpaul VR_SETBIT16(sc, VR_COMMAND, VR_CMD_STOP); 158241502Swpaul VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_RX_ON|VR_CMD_TX_ON)); 158341502Swpaul CSR_WRITE_2(sc, VR_IMR, 0x0000); 158441502Swpaul CSR_WRITE_4(sc, VR_TXADDR, 0x00000000); 158541502Swpaul CSR_WRITE_4(sc, VR_RXADDR, 0x00000000); 158641502Swpaul 158741502Swpaul /* 158841502Swpaul * Free data in the RX lists. 158941502Swpaul */ 159041502Swpaul for (i = 0; i < VR_RX_LIST_CNT; i++) { 159141502Swpaul if (sc->vr_cdata.vr_rx_chain[i].vr_mbuf != NULL) { 159241502Swpaul m_freem(sc->vr_cdata.vr_rx_chain[i].vr_mbuf); 159341502Swpaul sc->vr_cdata.vr_rx_chain[i].vr_mbuf = NULL; 159441502Swpaul } 159541502Swpaul } 159641502Swpaul bzero((char *)&sc->vr_ldata->vr_rx_list, 159741502Swpaul sizeof(sc->vr_ldata->vr_rx_list)); 159841502Swpaul 159941502Swpaul /* 160041502Swpaul * Free the TX list buffers. 160141502Swpaul */ 160241502Swpaul for (i = 0; i < VR_TX_LIST_CNT; i++) { 160341502Swpaul if (sc->vr_cdata.vr_tx_chain[i].vr_mbuf != NULL) { 160441502Swpaul m_freem(sc->vr_cdata.vr_tx_chain[i].vr_mbuf); 160541502Swpaul sc->vr_cdata.vr_tx_chain[i].vr_mbuf = NULL; 160641502Swpaul } 160741502Swpaul } 160841502Swpaul 160941502Swpaul bzero((char *)&sc->vr_ldata->vr_tx_list, 161041502Swpaul sizeof(sc->vr_ldata->vr_tx_list)); 161141502Swpaul 161241502Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 161341502Swpaul 161441502Swpaul return; 161541502Swpaul} 161641502Swpaul 161741502Swpaul/* 161841502Swpaul * Stop all chip I/O so that the kernel's probe routines don't 161941502Swpaul * get confused by errant DMAs when rebooting. 162041502Swpaul */ 162149610Swpaulstatic void vr_shutdown(dev) 162249610Swpaul device_t dev; 162341502Swpaul{ 162449610Swpaul struct vr_softc *sc; 162541502Swpaul 162649610Swpaul sc = device_get_softc(dev); 162749610Swpaul 162841502Swpaul vr_stop(sc); 162941502Swpaul 163041502Swpaul return; 163141502Swpaul} 1632