if_nge.c revision 76479
176479Swpaul/* 276479Swpaul * Copyright (c) 2001 Wind River Systems 376479Swpaul * Copyright (c) 1997, 1998, 1999, 2000, 2001 476479Swpaul * Bill Paul <wpaul@bsdi.com>. All rights reserved. 576479Swpaul * 676479Swpaul * Redistribution and use in source and binary forms, with or without 776479Swpaul * modification, are permitted provided that the following conditions 876479Swpaul * are met: 976479Swpaul * 1. Redistributions of source code must retain the above copyright 1076479Swpaul * notice, this list of conditions and the following disclaimer. 1176479Swpaul * 2. Redistributions in binary form must reproduce the above copyright 1276479Swpaul * notice, this list of conditions and the following disclaimer in the 1376479Swpaul * documentation and/or other materials provided with the distribution. 1476479Swpaul * 3. All advertising materials mentioning features or use of this software 1576479Swpaul * must display the following acknowledgement: 1676479Swpaul * This product includes software developed by Bill Paul. 1776479Swpaul * 4. Neither the name of the author nor the names of any co-contributors 1876479Swpaul * may be used to endorse or promote products derived from this software 1976479Swpaul * without specific prior written permission. 2076479Swpaul * 2176479Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 2276479Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2376479Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2476479Swpaul * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD 2576479Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2676479Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2776479Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2876479Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2976479Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 3076479Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 3176479Swpaul * THE POSSIBILITY OF SUCH DAMAGE. 3276479Swpaul * 3376479Swpaul * $FreeBSD: head/sys/dev/nge/if_nge.c 76479 2001-05-11 19:56:39Z wpaul $ 3476479Swpaul */ 3576479Swpaul 3676479Swpaul/* 3776479Swpaul * National Semiconductor DP83820/DP83821 gigabit ethernet driver 3876479Swpaul * for FreeBSD. Datasheets are available from: 3976479Swpaul * 4076479Swpaul * http://www.national.com/ds/DP/DP83820.pdf 4176479Swpaul * http://www.national.com/ds/DP/DP83821.pdf 4276479Swpaul * 4376479Swpaul * These chips are used on several low cost gigabit ethernet NICs 4476479Swpaul * sold by D-Link, Addtron, SMC and Asante. Both parts are 4576479Swpaul * virtually the same, except the 83820 is a 64-bit/32-bit part, 4676479Swpaul * while the 83821 is 32-bit only. 4776479Swpaul * 4876479Swpaul * Many cards also use National gigE transceivers, such as the 4976479Swpaul * DP83891, DP83861 and DP83862 gigPHYTER parts. The DP83861 datasheet 5076479Swpaul * contains a full register description that applies to all of these 5176479Swpaul * components: 5276479Swpaul * 5376479Swpaul * http://www.national.com/ds/DP/DP83861.pdf 5476479Swpaul * 5576479Swpaul * Written by Bill Paul <wpaul@bsdi.com> 5676479Swpaul * BSDi Open Source Solutions 5776479Swpaul */ 5876479Swpaul 5976479Swpaul/* 6076479Swpaul * The NatSemi DP83820 and 83821 controllers are enhanced versions 6176479Swpaul * of the NatSemi MacPHYTER 10/100 devices. They support 10, 100 6276479Swpaul * and 1000Mbps speeds with 1000baseX (ten bit interface), MII and GMII 6376479Swpaul * ports. Other features include 8K TX FIFO and 32K RX FIFO, TCP/IP 6476479Swpaul * hardware checksum offload (IPv4 only), VLAN tagging and filtering, 6576479Swpaul * priority TX and RX queues, a 2048 bit multicast hash filter, 4 RX pattern 6676479Swpaul * matching buffers, one perfect address filter buffer and interrupt 6776479Swpaul * moderation. The 83820 supports both 64-bit and 32-bit addressing 6876479Swpaul * and data transfers: the 64-bit support can be toggled on or off 6976479Swpaul * via software. This affects the size of certain fields in the DMA 7076479Swpaul * descriptors. 7176479Swpaul * 7276479Swpaul * As far as I can tell, the 83820 and 83821 are decent chips, marred by 7376479Swpaul * only one flaw: the RX buffers must be aligned on 64-bit boundaries. 7476479Swpaul * So far this is the only gigE MAC that I've encountered with this 7576479Swpaul * requirement. 7676479Swpaul */ 7776479Swpaul 7876479Swpaul#include "opt_vlan.h" 7976479Swpaul 8076479Swpaul#include <sys/param.h> 8176479Swpaul#include <sys/systm.h> 8276479Swpaul#include <sys/sockio.h> 8376479Swpaul#include <sys/mbuf.h> 8476479Swpaul#include <sys/malloc.h> 8576479Swpaul#include <sys/kernel.h> 8676479Swpaul#include <sys/socket.h> 8776479Swpaul 8876479Swpaul#include <net/if.h> 8976479Swpaul#include <net/if_arp.h> 9076479Swpaul#include <net/ethernet.h> 9176479Swpaul#include <net/if_dl.h> 9276479Swpaul#include <net/if_media.h> 9376479Swpaul 9476479Swpaul#if NVLAN > 0 9576479Swpaul#include <net/if_types.h> 9676479Swpaul#include <net/if_vlan_var.h> 9776479Swpaul#endif 9876479Swpaul 9976479Swpaul#include <net/bpf.h> 10076479Swpaul 10176479Swpaul#include <vm/vm.h> /* for vtophys */ 10276479Swpaul#include <vm/pmap.h> /* for vtophys */ 10376479Swpaul#include <machine/clock.h> /* for DELAY */ 10476479Swpaul#include <machine/bus_pio.h> 10576479Swpaul#include <machine/bus_memio.h> 10676479Swpaul#include <machine/bus.h> 10776479Swpaul#include <machine/resource.h> 10876479Swpaul#include <sys/bus.h> 10976479Swpaul#include <sys/rman.h> 11076479Swpaul 11176479Swpaul#include <dev/mii/mii.h> 11276479Swpaul#include <dev/mii/miivar.h> 11376479Swpaul 11476479Swpaul#include <pci/pcireg.h> 11576479Swpaul#include <pci/pcivar.h> 11676479Swpaul 11776479Swpaul#define NGE_USEIOSPACE 11876479Swpaul 11976479Swpaul#include <pci/if_ngereg.h> 12076479Swpaul 12176479SwpaulMODULE_DEPEND(nge, miibus, 1, 1, 1); 12276479Swpaul 12376479Swpaul/* "controller miibus0" required. See GENERIC if you get errors here. */ 12476479Swpaul#include "miibus_if.h" 12576479Swpaul 12676479Swpaul#ifndef lint 12776479Swpaulstatic const char rcsid[] = 12876479Swpaul "$FreeBSD: head/sys/dev/nge/if_nge.c 76479 2001-05-11 19:56:39Z wpaul $"; 12976479Swpaul#endif 13076479Swpaul 13176479Swpaul#define NGE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP) 13276479Swpaul 13376479Swpaul/* 13476479Swpaul * Various supported device vendors/types and their names. 13576479Swpaul */ 13676479Swpaulstatic struct nge_type nge_devs[] = { 13776479Swpaul { NGE_VENDORID, NGE_DEVICEID, 13876479Swpaul "National Semiconductor Gigabit Ethernet" }, 13976479Swpaul { 0, 0, NULL } 14076479Swpaul}; 14176479Swpaul 14276479Swpaulstatic int nge_probe __P((device_t)); 14376479Swpaulstatic int nge_attach __P((device_t)); 14476479Swpaulstatic int nge_detach __P((device_t)); 14576479Swpaul 14676479Swpaulstatic int nge_alloc_jumbo_mem __P((struct nge_softc *)); 14776479Swpaulstatic void nge_free_jumbo_mem __P((struct nge_softc *)); 14876479Swpaulstatic void *nge_jalloc __P((struct nge_softc *)); 14976479Swpaulstatic void nge_jfree __P((caddr_t, void *)); 15076479Swpaul 15176479Swpaulstatic int nge_newbuf __P((struct nge_softc *, 15276479Swpaul struct nge_desc *, 15376479Swpaul struct mbuf *)); 15476479Swpaulstatic int nge_encap __P((struct nge_softc *, 15576479Swpaul struct mbuf *, u_int32_t *)); 15676479Swpaulstatic void nge_rxeof __P((struct nge_softc *)); 15776479Swpaulstatic void nge_rxeoc __P((struct nge_softc *)); 15876479Swpaulstatic void nge_txeof __P((struct nge_softc *)); 15976479Swpaulstatic void nge_intr __P((void *)); 16076479Swpaulstatic void nge_tick __P((void *)); 16176479Swpaulstatic void nge_start __P((struct ifnet *)); 16276479Swpaulstatic int nge_ioctl __P((struct ifnet *, u_long, caddr_t)); 16376479Swpaulstatic void nge_init __P((void *)); 16476479Swpaulstatic void nge_stop __P((struct nge_softc *)); 16576479Swpaulstatic void nge_watchdog __P((struct ifnet *)); 16676479Swpaulstatic void nge_shutdown __P((device_t)); 16776479Swpaulstatic int nge_ifmedia_upd __P((struct ifnet *)); 16876479Swpaulstatic void nge_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 16976479Swpaul 17076479Swpaulstatic void nge_delay __P((struct nge_softc *)); 17176479Swpaulstatic void nge_eeprom_idle __P((struct nge_softc *)); 17276479Swpaulstatic void nge_eeprom_putbyte __P((struct nge_softc *, int)); 17376479Swpaulstatic void nge_eeprom_getword __P((struct nge_softc *, int, u_int16_t *)); 17476479Swpaulstatic void nge_read_eeprom __P((struct nge_softc *, caddr_t, int, 17576479Swpaul int, int)); 17676479Swpaul 17776479Swpaulstatic void nge_mii_sync __P((struct nge_softc *)); 17876479Swpaulstatic void nge_mii_send __P((struct nge_softc *, u_int32_t, int)); 17976479Swpaulstatic int nge_mii_readreg __P((struct nge_softc *, 18076479Swpaul struct nge_mii_frame *)); 18176479Swpaulstatic int nge_mii_writereg __P((struct nge_softc *, 18276479Swpaul struct nge_mii_frame *)); 18376479Swpaul 18476479Swpaulstatic int nge_miibus_readreg __P((device_t, int, int)); 18576479Swpaulstatic int nge_miibus_writereg __P((device_t, int, int, int)); 18676479Swpaulstatic void nge_miibus_statchg __P((device_t)); 18776479Swpaul 18876479Swpaulstatic void nge_setmulti __P((struct nge_softc *)); 18976479Swpaulstatic u_int32_t nge_crc __P((struct nge_softc *, caddr_t)); 19076479Swpaulstatic void nge_reset __P((struct nge_softc *)); 19176479Swpaulstatic int nge_list_rx_init __P((struct nge_softc *)); 19276479Swpaulstatic int nge_list_tx_init __P((struct nge_softc *)); 19376479Swpaul 19476479Swpaul#ifdef NGE_USEIOSPACE 19576479Swpaul#define NGE_RES SYS_RES_IOPORT 19676479Swpaul#define NGE_RID NGE_PCI_LOIO 19776479Swpaul#else 19876479Swpaul#define NGE_RES SYS_RES_MEMORY 19976479Swpaul#define NGE_RID NGE_PCI_LOMEM 20076479Swpaul#endif 20176479Swpaul 20276479Swpaulstatic device_method_t nge_methods[] = { 20376479Swpaul /* Device interface */ 20476479Swpaul DEVMETHOD(device_probe, nge_probe), 20576479Swpaul DEVMETHOD(device_attach, nge_attach), 20676479Swpaul DEVMETHOD(device_detach, nge_detach), 20776479Swpaul DEVMETHOD(device_shutdown, nge_shutdown), 20876479Swpaul 20976479Swpaul /* bus interface */ 21076479Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 21176479Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 21276479Swpaul 21376479Swpaul /* MII interface */ 21476479Swpaul DEVMETHOD(miibus_readreg, nge_miibus_readreg), 21576479Swpaul DEVMETHOD(miibus_writereg, nge_miibus_writereg), 21676479Swpaul DEVMETHOD(miibus_statchg, nge_miibus_statchg), 21776479Swpaul 21876479Swpaul { 0, 0 } 21976479Swpaul}; 22076479Swpaul 22176479Swpaulstatic driver_t nge_driver = { 22276479Swpaul "nge", 22376479Swpaul nge_methods, 22476479Swpaul sizeof(struct nge_softc) 22576479Swpaul}; 22676479Swpaul 22776479Swpaulstatic devclass_t nge_devclass; 22876479Swpaul 22976479SwpaulDRIVER_MODULE(if_nge, pci, nge_driver, nge_devclass, 0, 0); 23076479SwpaulDRIVER_MODULE(miibus, nge, miibus_driver, miibus_devclass, 0, 0); 23176479Swpaul 23276479Swpaul#define NGE_SETBIT(sc, reg, x) \ 23376479Swpaul CSR_WRITE_4(sc, reg, \ 23476479Swpaul CSR_READ_4(sc, reg) | (x)) 23576479Swpaul 23676479Swpaul#define NGE_CLRBIT(sc, reg, x) \ 23776479Swpaul CSR_WRITE_4(sc, reg, \ 23876479Swpaul CSR_READ_4(sc, reg) & ~(x)) 23976479Swpaul 24076479Swpaul#define SIO_SET(x) \ 24176479Swpaul CSR_WRITE_4(sc, NGE_MEAR, CSR_READ_4(sc, NGE_MEAR) | x) 24276479Swpaul 24376479Swpaul#define SIO_CLR(x) \ 24476479Swpaul CSR_WRITE_4(sc, NGE_MEAR, CSR_READ_4(sc, NGE_MEAR) & ~x) 24576479Swpaul 24676479Swpaulstatic void nge_delay(sc) 24776479Swpaul struct nge_softc *sc; 24876479Swpaul{ 24976479Swpaul int idx; 25076479Swpaul 25176479Swpaul for (idx = (300 / 33) + 1; idx > 0; idx--) 25276479Swpaul CSR_READ_4(sc, NGE_CSR); 25376479Swpaul 25476479Swpaul return; 25576479Swpaul} 25676479Swpaul 25776479Swpaulstatic void nge_eeprom_idle(sc) 25876479Swpaul struct nge_softc *sc; 25976479Swpaul{ 26076479Swpaul register int i; 26176479Swpaul 26276479Swpaul SIO_SET(NGE_MEAR_EE_CSEL); 26376479Swpaul nge_delay(sc); 26476479Swpaul SIO_SET(NGE_MEAR_EE_CLK); 26576479Swpaul nge_delay(sc); 26676479Swpaul 26776479Swpaul for (i = 0; i < 25; i++) { 26876479Swpaul SIO_CLR(NGE_MEAR_EE_CLK); 26976479Swpaul nge_delay(sc); 27076479Swpaul SIO_SET(NGE_MEAR_EE_CLK); 27176479Swpaul nge_delay(sc); 27276479Swpaul } 27376479Swpaul 27476479Swpaul SIO_CLR(NGE_MEAR_EE_CLK); 27576479Swpaul nge_delay(sc); 27676479Swpaul SIO_CLR(NGE_MEAR_EE_CSEL); 27776479Swpaul nge_delay(sc); 27876479Swpaul CSR_WRITE_4(sc, NGE_MEAR, 0x00000000); 27976479Swpaul 28076479Swpaul return; 28176479Swpaul} 28276479Swpaul 28376479Swpaul/* 28476479Swpaul * Send a read command and address to the EEPROM, check for ACK. 28576479Swpaul */ 28676479Swpaulstatic void nge_eeprom_putbyte(sc, addr) 28776479Swpaul struct nge_softc *sc; 28876479Swpaul int addr; 28976479Swpaul{ 29076479Swpaul register int d, i; 29176479Swpaul 29276479Swpaul d = addr | NGE_EECMD_READ; 29376479Swpaul 29476479Swpaul /* 29576479Swpaul * Feed in each bit and stobe the clock. 29676479Swpaul */ 29776479Swpaul for (i = 0x400; i; i >>= 1) { 29876479Swpaul if (d & i) { 29976479Swpaul SIO_SET(NGE_MEAR_EE_DIN); 30076479Swpaul } else { 30176479Swpaul SIO_CLR(NGE_MEAR_EE_DIN); 30276479Swpaul } 30376479Swpaul nge_delay(sc); 30476479Swpaul SIO_SET(NGE_MEAR_EE_CLK); 30576479Swpaul nge_delay(sc); 30676479Swpaul SIO_CLR(NGE_MEAR_EE_CLK); 30776479Swpaul nge_delay(sc); 30876479Swpaul } 30976479Swpaul 31076479Swpaul return; 31176479Swpaul} 31276479Swpaul 31376479Swpaul/* 31476479Swpaul * Read a word of data stored in the EEPROM at address 'addr.' 31576479Swpaul */ 31676479Swpaulstatic void nge_eeprom_getword(sc, addr, dest) 31776479Swpaul struct nge_softc *sc; 31876479Swpaul int addr; 31976479Swpaul u_int16_t *dest; 32076479Swpaul{ 32176479Swpaul register int i; 32276479Swpaul u_int16_t word = 0; 32376479Swpaul 32476479Swpaul /* Force EEPROM to idle state. */ 32576479Swpaul nge_eeprom_idle(sc); 32676479Swpaul 32776479Swpaul /* Enter EEPROM access mode. */ 32876479Swpaul nge_delay(sc); 32976479Swpaul SIO_CLR(NGE_MEAR_EE_CLK); 33076479Swpaul nge_delay(sc); 33176479Swpaul SIO_SET(NGE_MEAR_EE_CSEL); 33276479Swpaul nge_delay(sc); 33376479Swpaul 33476479Swpaul /* 33576479Swpaul * Send address of word we want to read. 33676479Swpaul */ 33776479Swpaul nge_eeprom_putbyte(sc, addr); 33876479Swpaul 33976479Swpaul /* 34076479Swpaul * Start reading bits from EEPROM. 34176479Swpaul */ 34276479Swpaul for (i = 0x8000; i; i >>= 1) { 34376479Swpaul SIO_SET(NGE_MEAR_EE_CLK); 34476479Swpaul nge_delay(sc); 34576479Swpaul if (CSR_READ_4(sc, NGE_MEAR) & NGE_MEAR_EE_DOUT) 34676479Swpaul word |= i; 34776479Swpaul nge_delay(sc); 34876479Swpaul SIO_CLR(NGE_MEAR_EE_CLK); 34976479Swpaul nge_delay(sc); 35076479Swpaul } 35176479Swpaul 35276479Swpaul /* Turn off EEPROM access mode. */ 35376479Swpaul nge_eeprom_idle(sc); 35476479Swpaul 35576479Swpaul *dest = word; 35676479Swpaul 35776479Swpaul return; 35876479Swpaul} 35976479Swpaul 36076479Swpaul/* 36176479Swpaul * Read a sequence of words from the EEPROM. 36276479Swpaul */ 36376479Swpaulstatic void nge_read_eeprom(sc, dest, off, cnt, swap) 36476479Swpaul struct nge_softc *sc; 36576479Swpaul caddr_t dest; 36676479Swpaul int off; 36776479Swpaul int cnt; 36876479Swpaul int swap; 36976479Swpaul{ 37076479Swpaul int i; 37176479Swpaul u_int16_t word = 0, *ptr; 37276479Swpaul 37376479Swpaul for (i = 0; i < cnt; i++) { 37476479Swpaul nge_eeprom_getword(sc, off + i, &word); 37576479Swpaul ptr = (u_int16_t *)(dest + (i * 2)); 37676479Swpaul if (swap) 37776479Swpaul *ptr = ntohs(word); 37876479Swpaul else 37976479Swpaul *ptr = word; 38076479Swpaul } 38176479Swpaul 38276479Swpaul return; 38376479Swpaul} 38476479Swpaul 38576479Swpaul/* 38676479Swpaul * Sync the PHYs by setting data bit and strobing the clock 32 times. 38776479Swpaul */ 38876479Swpaulstatic void nge_mii_sync(sc) 38976479Swpaul struct nge_softc *sc; 39076479Swpaul{ 39176479Swpaul register int i; 39276479Swpaul 39376479Swpaul SIO_SET(NGE_MEAR_MII_DIR|NGE_MEAR_MII_DATA); 39476479Swpaul 39576479Swpaul for (i = 0; i < 32; i++) { 39676479Swpaul SIO_SET(NGE_MEAR_MII_CLK); 39776479Swpaul DELAY(1); 39876479Swpaul SIO_CLR(NGE_MEAR_MII_CLK); 39976479Swpaul DELAY(1); 40076479Swpaul } 40176479Swpaul 40276479Swpaul return; 40376479Swpaul} 40476479Swpaul 40576479Swpaul/* 40676479Swpaul * Clock a series of bits through the MII. 40776479Swpaul */ 40876479Swpaulstatic void nge_mii_send(sc, bits, cnt) 40976479Swpaul struct nge_softc *sc; 41076479Swpaul u_int32_t bits; 41176479Swpaul int cnt; 41276479Swpaul{ 41376479Swpaul int i; 41476479Swpaul 41576479Swpaul SIO_CLR(NGE_MEAR_MII_CLK); 41676479Swpaul 41776479Swpaul for (i = (0x1 << (cnt - 1)); i; i >>= 1) { 41876479Swpaul if (bits & i) { 41976479Swpaul SIO_SET(NGE_MEAR_MII_DATA); 42076479Swpaul } else { 42176479Swpaul SIO_CLR(NGE_MEAR_MII_DATA); 42276479Swpaul } 42376479Swpaul DELAY(1); 42476479Swpaul SIO_CLR(NGE_MEAR_MII_CLK); 42576479Swpaul DELAY(1); 42676479Swpaul SIO_SET(NGE_MEAR_MII_CLK); 42776479Swpaul } 42876479Swpaul} 42976479Swpaul 43076479Swpaul/* 43176479Swpaul * Read an PHY register through the MII. 43276479Swpaul */ 43376479Swpaulstatic int nge_mii_readreg(sc, frame) 43476479Swpaul struct nge_softc *sc; 43576479Swpaul struct nge_mii_frame *frame; 43676479Swpaul 43776479Swpaul{ 43876479Swpaul int i, ack, s; 43976479Swpaul 44076479Swpaul s = splimp(); 44176479Swpaul 44276479Swpaul /* 44376479Swpaul * Set up frame for RX. 44476479Swpaul */ 44576479Swpaul frame->mii_stdelim = NGE_MII_STARTDELIM; 44676479Swpaul frame->mii_opcode = NGE_MII_READOP; 44776479Swpaul frame->mii_turnaround = 0; 44876479Swpaul frame->mii_data = 0; 44976479Swpaul 45076479Swpaul CSR_WRITE_4(sc, NGE_MEAR, 0); 45176479Swpaul 45276479Swpaul /* 45376479Swpaul * Turn on data xmit. 45476479Swpaul */ 45576479Swpaul SIO_SET(NGE_MEAR_MII_DIR); 45676479Swpaul 45776479Swpaul nge_mii_sync(sc); 45876479Swpaul 45976479Swpaul /* 46076479Swpaul * Send command/address info. 46176479Swpaul */ 46276479Swpaul nge_mii_send(sc, frame->mii_stdelim, 2); 46376479Swpaul nge_mii_send(sc, frame->mii_opcode, 2); 46476479Swpaul nge_mii_send(sc, frame->mii_phyaddr, 5); 46576479Swpaul nge_mii_send(sc, frame->mii_regaddr, 5); 46676479Swpaul 46776479Swpaul /* Idle bit */ 46876479Swpaul SIO_CLR((NGE_MEAR_MII_CLK|NGE_MEAR_MII_DATA)); 46976479Swpaul DELAY(1); 47076479Swpaul SIO_SET(NGE_MEAR_MII_CLK); 47176479Swpaul DELAY(1); 47276479Swpaul 47376479Swpaul /* Turn off xmit. */ 47476479Swpaul SIO_CLR(NGE_MEAR_MII_DIR); 47576479Swpaul /* Check for ack */ 47676479Swpaul SIO_CLR(NGE_MEAR_MII_CLK); 47776479Swpaul DELAY(1); 47876479Swpaul SIO_SET(NGE_MEAR_MII_CLK); 47976479Swpaul DELAY(1); 48076479Swpaul ack = CSR_READ_4(sc, NGE_MEAR) & NGE_MEAR_MII_DATA; 48176479Swpaul 48276479Swpaul /* 48376479Swpaul * Now try reading data bits. If the ack failed, we still 48476479Swpaul * need to clock through 16 cycles to keep the PHY(s) in sync. 48576479Swpaul */ 48676479Swpaul if (ack) { 48776479Swpaul for(i = 0; i < 16; i++) { 48876479Swpaul SIO_CLR(NGE_MEAR_MII_CLK); 48976479Swpaul DELAY(1); 49076479Swpaul SIO_SET(NGE_MEAR_MII_CLK); 49176479Swpaul DELAY(1); 49276479Swpaul } 49376479Swpaul goto fail; 49476479Swpaul } 49576479Swpaul 49676479Swpaul for (i = 0x8000; i; i >>= 1) { 49776479Swpaul SIO_CLR(NGE_MEAR_MII_CLK); 49876479Swpaul DELAY(1); 49976479Swpaul if (!ack) { 50076479Swpaul if (CSR_READ_4(sc, NGE_MEAR) & NGE_MEAR_MII_DATA) 50176479Swpaul frame->mii_data |= i; 50276479Swpaul DELAY(1); 50376479Swpaul } 50476479Swpaul SIO_SET(NGE_MEAR_MII_CLK); 50576479Swpaul DELAY(1); 50676479Swpaul } 50776479Swpaul 50876479Swpaulfail: 50976479Swpaul 51076479Swpaul SIO_CLR(NGE_MEAR_MII_CLK); 51176479Swpaul DELAY(1); 51276479Swpaul SIO_SET(NGE_MEAR_MII_CLK); 51376479Swpaul DELAY(1); 51476479Swpaul 51576479Swpaul splx(s); 51676479Swpaul 51776479Swpaul if (ack) 51876479Swpaul return(1); 51976479Swpaul return(0); 52076479Swpaul} 52176479Swpaul 52276479Swpaul/* 52376479Swpaul * Write to a PHY register through the MII. 52476479Swpaul */ 52576479Swpaulstatic int nge_mii_writereg(sc, frame) 52676479Swpaul struct nge_softc *sc; 52776479Swpaul struct nge_mii_frame *frame; 52876479Swpaul 52976479Swpaul{ 53076479Swpaul int s; 53176479Swpaul 53276479Swpaul s = splimp(); 53376479Swpaul /* 53476479Swpaul * Set up frame for TX. 53576479Swpaul */ 53676479Swpaul 53776479Swpaul frame->mii_stdelim = NGE_MII_STARTDELIM; 53876479Swpaul frame->mii_opcode = NGE_MII_WRITEOP; 53976479Swpaul frame->mii_turnaround = NGE_MII_TURNAROUND; 54076479Swpaul 54176479Swpaul /* 54276479Swpaul * Turn on data output. 54376479Swpaul */ 54476479Swpaul SIO_SET(NGE_MEAR_MII_DIR); 54576479Swpaul 54676479Swpaul nge_mii_sync(sc); 54776479Swpaul 54876479Swpaul nge_mii_send(sc, frame->mii_stdelim, 2); 54976479Swpaul nge_mii_send(sc, frame->mii_opcode, 2); 55076479Swpaul nge_mii_send(sc, frame->mii_phyaddr, 5); 55176479Swpaul nge_mii_send(sc, frame->mii_regaddr, 5); 55276479Swpaul nge_mii_send(sc, frame->mii_turnaround, 2); 55376479Swpaul nge_mii_send(sc, frame->mii_data, 16); 55476479Swpaul 55576479Swpaul /* Idle bit. */ 55676479Swpaul SIO_SET(NGE_MEAR_MII_CLK); 55776479Swpaul DELAY(1); 55876479Swpaul SIO_CLR(NGE_MEAR_MII_CLK); 55976479Swpaul DELAY(1); 56076479Swpaul 56176479Swpaul /* 56276479Swpaul * Turn off xmit. 56376479Swpaul */ 56476479Swpaul SIO_CLR(NGE_MEAR_MII_DIR); 56576479Swpaul 56676479Swpaul splx(s); 56776479Swpaul 56876479Swpaul return(0); 56976479Swpaul} 57076479Swpaul 57176479Swpaulstatic int nge_miibus_readreg(dev, phy, reg) 57276479Swpaul device_t dev; 57376479Swpaul int phy, reg; 57476479Swpaul{ 57576479Swpaul struct nge_softc *sc; 57676479Swpaul struct nge_mii_frame frame; 57776479Swpaul 57876479Swpaul sc = device_get_softc(dev); 57976479Swpaul 58076479Swpaul bzero((char *)&frame, sizeof(frame)); 58176479Swpaul 58276479Swpaul frame.mii_phyaddr = phy; 58376479Swpaul frame.mii_regaddr = reg; 58476479Swpaul nge_mii_readreg(sc, &frame); 58576479Swpaul 58676479Swpaul return(frame.mii_data); 58776479Swpaul} 58876479Swpaul 58976479Swpaulstatic int nge_miibus_writereg(dev, phy, reg, data) 59076479Swpaul device_t dev; 59176479Swpaul int phy, reg, data; 59276479Swpaul{ 59376479Swpaul struct nge_softc *sc; 59476479Swpaul struct nge_mii_frame frame; 59576479Swpaul 59676479Swpaul sc = device_get_softc(dev); 59776479Swpaul 59876479Swpaul bzero((char *)&frame, sizeof(frame)); 59976479Swpaul 60076479Swpaul frame.mii_phyaddr = phy; 60176479Swpaul frame.mii_regaddr = reg; 60276479Swpaul frame.mii_data = data; 60376479Swpaul nge_mii_writereg(sc, &frame); 60476479Swpaul 60576479Swpaul return(0); 60676479Swpaul} 60776479Swpaul 60876479Swpaulstatic void nge_miibus_statchg(dev) 60976479Swpaul device_t dev; 61076479Swpaul{ 61176479Swpaul struct nge_softc *sc; 61276479Swpaul struct mii_data *mii; 61376479Swpaul 61476479Swpaul sc = device_get_softc(dev); 61576479Swpaul mii = device_get_softc(sc->nge_miibus); 61676479Swpaul 61776479Swpaul if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 61876479Swpaul NGE_SETBIT(sc, NGE_TX_CFG, 61976479Swpaul (NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR)); 62076479Swpaul NGE_SETBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX); 62176479Swpaul } else { 62276479Swpaul NGE_CLRBIT(sc, NGE_TX_CFG, 62376479Swpaul (NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR)); 62476479Swpaul NGE_CLRBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX); 62576479Swpaul } 62676479Swpaul 62776479Swpaul return; 62876479Swpaul} 62976479Swpaul 63076479Swpaulstatic u_int32_t nge_crc(sc, addr) 63176479Swpaul struct nge_softc *sc; 63276479Swpaul caddr_t addr; 63376479Swpaul{ 63476479Swpaul u_int32_t crc, carry; 63576479Swpaul int i, j; 63676479Swpaul u_int8_t c; 63776479Swpaul 63876479Swpaul /* Compute CRC for the address value. */ 63976479Swpaul crc = 0xFFFFFFFF; /* initial value */ 64076479Swpaul 64176479Swpaul for (i = 0; i < 6; i++) { 64276479Swpaul c = *(addr + i); 64376479Swpaul for (j = 0; j < 8; j++) { 64476479Swpaul carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); 64576479Swpaul crc <<= 1; 64676479Swpaul c >>= 1; 64776479Swpaul if (carry) 64876479Swpaul crc = (crc ^ 0x04c11db6) | carry; 64976479Swpaul } 65076479Swpaul } 65176479Swpaul 65276479Swpaul /* 65376479Swpaul * return the filter bit position 65476479Swpaul */ 65576479Swpaul 65676479Swpaul return((crc >> 21) & 0x00000FFF); 65776479Swpaul} 65876479Swpaul 65976479Swpaulstatic void nge_setmulti(sc) 66076479Swpaul struct nge_softc *sc; 66176479Swpaul{ 66276479Swpaul struct ifnet *ifp; 66376479Swpaul struct ifmultiaddr *ifma; 66476479Swpaul u_int32_t h = 0, i, filtsave; 66576479Swpaul int bit, index; 66676479Swpaul 66776479Swpaul ifp = &sc->arpcom.ac_if; 66876479Swpaul 66976479Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 67076479Swpaul NGE_CLRBIT(sc, NGE_RXFILT_CTL, 67176479Swpaul NGE_RXFILTCTL_MCHASH|NGE_RXFILTCTL_UCHASH); 67276479Swpaul NGE_SETBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_ALLMULTI); 67376479Swpaul return; 67476479Swpaul } 67576479Swpaul 67676479Swpaul /* 67776479Swpaul * We have to explicitly enable the multicast hash table 67876479Swpaul * on the NatSemi chip if we want to use it, which we do. 67976479Swpaul * We also have to tell it that we don't want to use the 68076479Swpaul * hash table for matching unicast addresses. 68176479Swpaul */ 68276479Swpaul NGE_SETBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_MCHASH); 68376479Swpaul NGE_CLRBIT(sc, NGE_RXFILT_CTL, 68476479Swpaul NGE_RXFILTCTL_ALLMULTI|NGE_RXFILTCTL_UCHASH); 68576479Swpaul 68676479Swpaul filtsave = CSR_READ_4(sc, NGE_RXFILT_CTL); 68776479Swpaul 68876479Swpaul /* first, zot all the existing hash bits */ 68976479Swpaul for (i = 0; i < NGE_MCAST_FILTER_LEN; i += 2) { 69076479Swpaul CSR_WRITE_4(sc, NGE_RXFILT_CTL, NGE_FILTADDR_MCAST_LO + i); 69176479Swpaul CSR_WRITE_4(sc, NGE_RXFILT_DATA, 0); 69276479Swpaul } 69376479Swpaul 69476479Swpaul /* 69576479Swpaul * From the 11 bits returned by the crc routine, the top 7 69676479Swpaul * bits represent the 16-bit word in the mcast hash table 69776479Swpaul * that needs to be updated, and the lower 4 bits represent 69876479Swpaul * which bit within that byte needs to be set. 69976479Swpaul */ 70076479Swpaul TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 70176479Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 70276479Swpaul continue; 70376479Swpaul h = nge_crc(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 70476479Swpaul index = (h >> 4) & 0x7F; 70576479Swpaul bit = h & 0xF; 70676479Swpaul CSR_WRITE_4(sc, NGE_RXFILT_CTL, 70776479Swpaul NGE_FILTADDR_MCAST_LO + (index * 2)); 70876479Swpaul NGE_SETBIT(sc, NGE_RXFILT_DATA, (1 << bit)); 70976479Swpaul } 71076479Swpaul 71176479Swpaul CSR_WRITE_4(sc, NGE_RXFILT_CTL, filtsave); 71276479Swpaul 71376479Swpaul return; 71476479Swpaul} 71576479Swpaul 71676479Swpaulstatic void nge_reset(sc) 71776479Swpaul struct nge_softc *sc; 71876479Swpaul{ 71976479Swpaul register int i; 72076479Swpaul 72176479Swpaul NGE_SETBIT(sc, NGE_CSR, NGE_CSR_RESET); 72276479Swpaul 72376479Swpaul for (i = 0; i < NGE_TIMEOUT; i++) { 72476479Swpaul if (!(CSR_READ_4(sc, NGE_CSR) & NGE_CSR_RESET)) 72576479Swpaul break; 72676479Swpaul } 72776479Swpaul 72876479Swpaul if (i == NGE_TIMEOUT) 72976479Swpaul printf("nge%d: reset never completed\n", sc->nge_unit); 73076479Swpaul 73176479Swpaul /* Wait a little while for the chip to get its brains in order. */ 73276479Swpaul DELAY(1000); 73376479Swpaul 73476479Swpaul /* 73576479Swpaul * If this is a NetSemi chip, make sure to clear 73676479Swpaul * PME mode. 73776479Swpaul */ 73876479Swpaul CSR_WRITE_4(sc, NGE_CLKRUN, NGE_CLKRUN_PMESTS); 73976479Swpaul CSR_WRITE_4(sc, NGE_CLKRUN, 0); 74076479Swpaul 74176479Swpaul return; 74276479Swpaul} 74376479Swpaul 74476479Swpaul/* 74576479Swpaul * Probe for an NatSemi chip. Check the PCI vendor and device 74676479Swpaul * IDs against our list and return a device name if we find a match. 74776479Swpaul */ 74876479Swpaulstatic int nge_probe(dev) 74976479Swpaul device_t dev; 75076479Swpaul{ 75176479Swpaul struct nge_type *t; 75276479Swpaul 75376479Swpaul t = nge_devs; 75476479Swpaul 75576479Swpaul while(t->nge_name != NULL) { 75676479Swpaul if ((pci_get_vendor(dev) == t->nge_vid) && 75776479Swpaul (pci_get_device(dev) == t->nge_did)) { 75876479Swpaul device_set_desc(dev, t->nge_name); 75976479Swpaul return(0); 76076479Swpaul } 76176479Swpaul t++; 76276479Swpaul } 76376479Swpaul 76476479Swpaul return(ENXIO); 76576479Swpaul} 76676479Swpaul 76776479Swpaul/* 76876479Swpaul * Attach the interface. Allocate softc structures, do ifmedia 76976479Swpaul * setup and ethernet/BPF attach. 77076479Swpaul */ 77176479Swpaulstatic int nge_attach(dev) 77276479Swpaul device_t dev; 77376479Swpaul{ 77476479Swpaul int s; 77576479Swpaul u_char eaddr[ETHER_ADDR_LEN]; 77676479Swpaul u_int32_t command; 77776479Swpaul struct nge_softc *sc; 77876479Swpaul struct ifnet *ifp; 77976479Swpaul int unit, error = 0, rid; 78076479Swpaul 78176479Swpaul s = splimp(); 78276479Swpaul 78376479Swpaul sc = device_get_softc(dev); 78476479Swpaul unit = device_get_unit(dev); 78576479Swpaul bzero(sc, sizeof(struct nge_softc)); 78676479Swpaul 78776479Swpaul mtx_init(&sc->nge_mtx, device_get_nameunit(dev), MTX_DEF|MTX_RECURSE); 78876479Swpaul 78976479Swpaul /* 79076479Swpaul * Handle power management nonsense. 79176479Swpaul */ 79276479Swpaul if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { 79376479Swpaul u_int32_t iobase, membase, irq; 79476479Swpaul 79576479Swpaul /* Save important PCI config data. */ 79676479Swpaul iobase = pci_read_config(dev, NGE_PCI_LOIO, 4); 79776479Swpaul membase = pci_read_config(dev, NGE_PCI_LOMEM, 4); 79876479Swpaul irq = pci_read_config(dev, NGE_PCI_INTLINE, 4); 79976479Swpaul 80076479Swpaul /* Reset the power state. */ 80176479Swpaul printf("nge%d: chip is in D%d power mode " 80276479Swpaul "-- setting to D0\n", unit, 80376479Swpaul pci_get_powerstate(dev)); 80476479Swpaul pci_set_powerstate(dev, PCI_POWERSTATE_D0); 80576479Swpaul 80676479Swpaul /* Restore PCI config data. */ 80776479Swpaul pci_write_config(dev, NGE_PCI_LOIO, iobase, 4); 80876479Swpaul pci_write_config(dev, NGE_PCI_LOMEM, membase, 4); 80976479Swpaul pci_write_config(dev, NGE_PCI_INTLINE, irq, 4); 81076479Swpaul } 81176479Swpaul 81276479Swpaul /* 81376479Swpaul * Map control/status registers. 81476479Swpaul */ 81576479Swpaul pci_enable_busmaster(dev); 81676479Swpaul pci_enable_io(dev, PCIM_CMD_PORTEN); 81776479Swpaul pci_enable_io(dev, PCIM_CMD_MEMEN); 81876479Swpaul command = pci_read_config(dev, PCIR_COMMAND, 4); 81976479Swpaul 82076479Swpaul#ifdef NGE_USEIOSPACE 82176479Swpaul if (!(command & PCIM_CMD_PORTEN)) { 82276479Swpaul printf("nge%d: failed to enable I/O ports!\n", unit); 82376479Swpaul error = ENXIO;; 82476479Swpaul goto fail; 82576479Swpaul } 82676479Swpaul#else 82776479Swpaul if (!(command & PCIM_CMD_MEMEN)) { 82876479Swpaul printf("nge%d: failed to enable memory mapping!\n", unit); 82976479Swpaul error = ENXIO;; 83076479Swpaul goto fail; 83176479Swpaul } 83276479Swpaul#endif 83376479Swpaul 83476479Swpaul rid = NGE_RID; 83576479Swpaul sc->nge_res = bus_alloc_resource(dev, NGE_RES, &rid, 83676479Swpaul 0, ~0, 1, RF_ACTIVE); 83776479Swpaul 83876479Swpaul if (sc->nge_res == NULL) { 83976479Swpaul printf("nge%d: couldn't map ports/memory\n", unit); 84076479Swpaul error = ENXIO; 84176479Swpaul goto fail; 84276479Swpaul } 84376479Swpaul 84476479Swpaul sc->nge_btag = rman_get_bustag(sc->nge_res); 84576479Swpaul sc->nge_bhandle = rman_get_bushandle(sc->nge_res); 84676479Swpaul 84776479Swpaul /* Allocate interrupt */ 84876479Swpaul rid = 0; 84976479Swpaul sc->nge_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 85076479Swpaul RF_SHAREABLE | RF_ACTIVE); 85176479Swpaul 85276479Swpaul if (sc->nge_irq == NULL) { 85376479Swpaul printf("nge%d: couldn't map interrupt\n", unit); 85476479Swpaul bus_release_resource(dev, NGE_RES, NGE_RID, sc->nge_res); 85576479Swpaul error = ENXIO; 85676479Swpaul goto fail; 85776479Swpaul } 85876479Swpaul 85976479Swpaul error = bus_setup_intr(dev, sc->nge_irq, INTR_TYPE_NET, 86076479Swpaul nge_intr, sc, &sc->nge_intrhand); 86176479Swpaul 86276479Swpaul if (error) { 86376479Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->nge_irq); 86476479Swpaul bus_release_resource(dev, NGE_RES, NGE_RID, sc->nge_res); 86576479Swpaul printf("nge%d: couldn't set up irq\n", unit); 86676479Swpaul goto fail; 86776479Swpaul } 86876479Swpaul 86976479Swpaul /* Reset the adapter. */ 87076479Swpaul nge_reset(sc); 87176479Swpaul 87276479Swpaul /* 87376479Swpaul * Get station address from the EEPROM. 87476479Swpaul */ 87576479Swpaul nge_read_eeprom(sc, (caddr_t)&eaddr[4], NGE_EE_NODEADDR, 1, 0); 87676479Swpaul nge_read_eeprom(sc, (caddr_t)&eaddr[2], NGE_EE_NODEADDR + 1, 1, 0); 87776479Swpaul nge_read_eeprom(sc, (caddr_t)&eaddr[0], NGE_EE_NODEADDR + 2, 1, 0); 87876479Swpaul 87976479Swpaul /* 88076479Swpaul * A NatSemi chip was detected. Inform the world. 88176479Swpaul */ 88276479Swpaul printf("nge%d: Ethernet address: %6D\n", unit, eaddr, ":"); 88376479Swpaul 88476479Swpaul sc->nge_unit = unit; 88576479Swpaul bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 88676479Swpaul 88776479Swpaul sc->nge_ldata = contigmalloc(sizeof(struct nge_list_data), M_DEVBUF, 88876479Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 88976479Swpaul 89076479Swpaul if (sc->nge_ldata == NULL) { 89176479Swpaul printf("nge%d: no memory for list buffers!\n", unit); 89276479Swpaul bus_teardown_intr(dev, sc->nge_irq, sc->nge_intrhand); 89376479Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->nge_irq); 89476479Swpaul bus_release_resource(dev, NGE_RES, NGE_RID, sc->nge_res); 89576479Swpaul error = ENXIO; 89676479Swpaul goto fail; 89776479Swpaul } 89876479Swpaul bzero(sc->nge_ldata, sizeof(struct nge_list_data)); 89976479Swpaul 90076479Swpaul /* Try to allocate memory for jumbo buffers. */ 90176479Swpaul if (nge_alloc_jumbo_mem(sc)) { 90276479Swpaul printf("nge%d: jumbo buffer allocation failed\n", 90376479Swpaul sc->nge_unit); 90476479Swpaul contigfree(sc->nge_ldata, 90576479Swpaul sizeof(struct nge_list_data), M_DEVBUF); 90676479Swpaul bus_teardown_intr(dev, sc->nge_irq, sc->nge_intrhand); 90776479Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->nge_irq); 90876479Swpaul bus_release_resource(dev, NGE_RES, NGE_RID, sc->nge_res); 90976479Swpaul error = ENXIO; 91076479Swpaul goto fail; 91176479Swpaul } 91276479Swpaul 91376479Swpaul ifp = &sc->arpcom.ac_if; 91476479Swpaul ifp->if_softc = sc; 91576479Swpaul ifp->if_unit = unit; 91676479Swpaul ifp->if_name = "nge"; 91776479Swpaul ifp->if_mtu = ETHERMTU; 91876479Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 91976479Swpaul ifp->if_ioctl = nge_ioctl; 92076479Swpaul ifp->if_output = ether_output; 92176479Swpaul ifp->if_start = nge_start; 92276479Swpaul ifp->if_watchdog = nge_watchdog; 92376479Swpaul ifp->if_init = nge_init; 92476479Swpaul ifp->if_baudrate = 1000000000; 92576479Swpaul ifp->if_snd.ifq_maxlen = NGE_TX_LIST_CNT - 1; 92676479Swpaul ifp->if_hwassist = NGE_CSUM_FEATURES; 92776479Swpaul 92876479Swpaul /* 92976479Swpaul * Do MII setup. 93076479Swpaul */ 93176479Swpaul if (mii_phy_probe(dev, &sc->nge_miibus, 93276479Swpaul nge_ifmedia_upd, nge_ifmedia_sts)) { 93376479Swpaul printf("nge%d: MII without any PHY!\n", sc->nge_unit); 93476479Swpaul nge_free_jumbo_mem(sc); 93576479Swpaul bus_teardown_intr(dev, sc->nge_irq, sc->nge_intrhand); 93676479Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->nge_irq); 93776479Swpaul bus_release_resource(dev, NGE_RES, NGE_RID, sc->nge_res); 93876479Swpaul error = ENXIO; 93976479Swpaul goto fail; 94076479Swpaul } 94176479Swpaul 94276479Swpaul /* 94376479Swpaul * Call MI attach routine. 94476479Swpaul */ 94576479Swpaul ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 94676479Swpaul callout_handle_init(&sc->nge_stat_ch); 94776479Swpaul 94876479Swpaulfail: 94976479Swpaul splx(s); 95076479Swpaul mtx_destroy(&sc->nge_mtx); 95176479Swpaul return(error); 95276479Swpaul} 95376479Swpaul 95476479Swpaulstatic int nge_detach(dev) 95576479Swpaul device_t dev; 95676479Swpaul{ 95776479Swpaul struct nge_softc *sc; 95876479Swpaul struct ifnet *ifp; 95976479Swpaul int s; 96076479Swpaul 96176479Swpaul s = splimp(); 96276479Swpaul 96376479Swpaul sc = device_get_softc(dev); 96476479Swpaul ifp = &sc->arpcom.ac_if; 96576479Swpaul 96676479Swpaul nge_reset(sc); 96776479Swpaul nge_stop(sc); 96876479Swpaul ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); 96976479Swpaul 97076479Swpaul bus_generic_detach(dev); 97176479Swpaul device_delete_child(dev, sc->nge_miibus); 97276479Swpaul 97376479Swpaul bus_teardown_intr(dev, sc->nge_irq, sc->nge_intrhand); 97476479Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->nge_irq); 97576479Swpaul bus_release_resource(dev, NGE_RES, NGE_RID, sc->nge_res); 97676479Swpaul 97776479Swpaul contigfree(sc->nge_ldata, sizeof(struct nge_list_data), M_DEVBUF); 97876479Swpaul nge_free_jumbo_mem(sc); 97976479Swpaul 98076479Swpaul splx(s); 98176479Swpaul mtx_destroy(&sc->nge_mtx); 98276479Swpaul 98376479Swpaul return(0); 98476479Swpaul} 98576479Swpaul 98676479Swpaul/* 98776479Swpaul * Initialize the transmit descriptors. 98876479Swpaul */ 98976479Swpaulstatic int nge_list_tx_init(sc) 99076479Swpaul struct nge_softc *sc; 99176479Swpaul{ 99276479Swpaul struct nge_list_data *ld; 99376479Swpaul struct nge_ring_data *cd; 99476479Swpaul int i; 99576479Swpaul 99676479Swpaul cd = &sc->nge_cdata; 99776479Swpaul ld = sc->nge_ldata; 99876479Swpaul 99976479Swpaul for (i = 0; i < NGE_TX_LIST_CNT; i++) { 100076479Swpaul if (i == (NGE_TX_LIST_CNT - 1)) { 100176479Swpaul ld->nge_tx_list[i].nge_nextdesc = 100276479Swpaul &ld->nge_tx_list[0]; 100376479Swpaul ld->nge_tx_list[i].nge_next = 100476479Swpaul vtophys(&ld->nge_tx_list[0]); 100576479Swpaul } else { 100676479Swpaul ld->nge_tx_list[i].nge_nextdesc = 100776479Swpaul &ld->nge_tx_list[i + 1]; 100876479Swpaul ld->nge_tx_list[i].nge_next = 100976479Swpaul vtophys(&ld->nge_tx_list[i + 1]); 101076479Swpaul } 101176479Swpaul ld->nge_tx_list[i].nge_mbuf = NULL; 101276479Swpaul ld->nge_tx_list[i].nge_ptr = 0; 101376479Swpaul ld->nge_tx_list[i].nge_ctl = 0; 101476479Swpaul } 101576479Swpaul 101676479Swpaul cd->nge_tx_prod = cd->nge_tx_cons = cd->nge_tx_cnt = 0; 101776479Swpaul 101876479Swpaul return(0); 101976479Swpaul} 102076479Swpaul 102176479Swpaul 102276479Swpaul/* 102376479Swpaul * Initialize the RX descriptors and allocate mbufs for them. Note that 102476479Swpaul * we arrange the descriptors in a closed ring, so that the last descriptor 102576479Swpaul * points back to the first. 102676479Swpaul */ 102776479Swpaulstatic int nge_list_rx_init(sc) 102876479Swpaul struct nge_softc *sc; 102976479Swpaul{ 103076479Swpaul struct nge_list_data *ld; 103176479Swpaul struct nge_ring_data *cd; 103276479Swpaul int i; 103376479Swpaul 103476479Swpaul ld = sc->nge_ldata; 103576479Swpaul cd = &sc->nge_cdata; 103676479Swpaul 103776479Swpaul for (i = 0; i < NGE_RX_LIST_CNT; i++) { 103876479Swpaul if (nge_newbuf(sc, &ld->nge_rx_list[i], NULL) == ENOBUFS) 103976479Swpaul return(ENOBUFS); 104076479Swpaul if (i == (NGE_RX_LIST_CNT - 1)) { 104176479Swpaul ld->nge_rx_list[i].nge_nextdesc = 104276479Swpaul &ld->nge_rx_list[0]; 104376479Swpaul ld->nge_rx_list[i].nge_next = 104476479Swpaul vtophys(&ld->nge_rx_list[0]); 104576479Swpaul } else { 104676479Swpaul ld->nge_rx_list[i].nge_nextdesc = 104776479Swpaul &ld->nge_rx_list[i + 1]; 104876479Swpaul ld->nge_rx_list[i].nge_next = 104976479Swpaul vtophys(&ld->nge_rx_list[i + 1]); 105076479Swpaul } 105176479Swpaul } 105276479Swpaul 105376479Swpaul cd->nge_rx_prod = 0; 105476479Swpaul 105576479Swpaul return(0); 105676479Swpaul} 105776479Swpaul 105876479Swpaul/* 105976479Swpaul * Initialize an RX descriptor and attach an MBUF cluster. 106076479Swpaul */ 106176479Swpaulstatic int nge_newbuf(sc, c, m) 106276479Swpaul struct nge_softc *sc; 106376479Swpaul struct nge_desc *c; 106476479Swpaul struct mbuf *m; 106576479Swpaul{ 106676479Swpaul struct mbuf *m_new = NULL; 106776479Swpaul caddr_t *buf = NULL; 106876479Swpaul 106976479Swpaul if (m == NULL) { 107076479Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 107176479Swpaul if (m_new == NULL) { 107276479Swpaul printf("nge%d: no memory for rx list " 107376479Swpaul "-- packet dropped!\n", sc->nge_unit); 107476479Swpaul return(ENOBUFS); 107576479Swpaul } 107676479Swpaul 107776479Swpaul /* Allocate the jumbo buffer */ 107876479Swpaul buf = nge_jalloc(sc); 107976479Swpaul if (buf == NULL) { 108076479Swpaul#ifdef NGE_VERBOSE 108176479Swpaul printf("nge%d: jumbo allocation failed " 108276479Swpaul "-- packet dropped!\n", sc->nge_unit); 108376479Swpaul#endif 108476479Swpaul m_freem(m_new); 108576479Swpaul return(ENOBUFS); 108676479Swpaul } 108776479Swpaul /* Attach the buffer to the mbuf */ 108876479Swpaul m_new->m_data = (void *)buf; 108976479Swpaul m_new->m_len = m_new->m_pkthdr.len = NGE_MCLBYTES; 109076479Swpaul MEXTADD(m_new, buf, NGE_MCLBYTES, nge_jfree, 109176479Swpaul (struct ti_softc *)sc, 0, EXT_NET_DRV); 109276479Swpaul } else { 109376479Swpaul m_new = m; 109476479Swpaul m_new->m_len = m_new->m_pkthdr.len = NGE_MCLBYTES; 109576479Swpaul m_new->m_data = m_new->m_ext.ext_buf; 109676479Swpaul } 109776479Swpaul 109876479Swpaul m_adj(m_new, sizeof(u_int64_t)); 109976479Swpaul 110076479Swpaul c->nge_mbuf = m_new; 110176479Swpaul c->nge_ptr = vtophys(mtod(m_new, caddr_t)); 110276479Swpaul c->nge_ctl = m_new->m_len; 110376479Swpaul c->nge_extsts = 0; 110476479Swpaul 110576479Swpaul return(0); 110676479Swpaul} 110776479Swpaul 110876479Swpaulstatic int nge_alloc_jumbo_mem(sc) 110976479Swpaul struct nge_softc *sc; 111076479Swpaul{ 111176479Swpaul caddr_t ptr; 111276479Swpaul register int i; 111376479Swpaul struct nge_jpool_entry *entry; 111476479Swpaul 111576479Swpaul /* Grab a big chunk o' storage. */ 111676479Swpaul sc->nge_cdata.nge_jumbo_buf = contigmalloc(NGE_JMEM, M_DEVBUF, 111776479Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 111876479Swpaul 111976479Swpaul if (sc->nge_cdata.nge_jumbo_buf == NULL) { 112076479Swpaul printf("nge%d: no memory for jumbo buffers!\n", sc->nge_unit); 112176479Swpaul return(ENOBUFS); 112276479Swpaul } 112376479Swpaul 112476479Swpaul SLIST_INIT(&sc->nge_jfree_listhead); 112576479Swpaul SLIST_INIT(&sc->nge_jinuse_listhead); 112676479Swpaul 112776479Swpaul /* 112876479Swpaul * Now divide it up into 9K pieces and save the addresses 112976479Swpaul * in an array. 113076479Swpaul */ 113176479Swpaul ptr = sc->nge_cdata.nge_jumbo_buf; 113276479Swpaul for (i = 0; i < NGE_JSLOTS; i++) { 113376479Swpaul sc->nge_cdata.nge_jslots[i] = ptr; 113476479Swpaul ptr += NGE_MCLBYTES; 113576479Swpaul entry = malloc(sizeof(struct nge_jpool_entry), 113676479Swpaul M_DEVBUF, M_NOWAIT); 113776479Swpaul if (entry == NULL) { 113876479Swpaul free(sc->nge_cdata.nge_jumbo_buf, M_DEVBUF); 113976479Swpaul sc->nge_cdata.nge_jumbo_buf = NULL; 114076479Swpaul printf("nge%d: no memory for jumbo " 114176479Swpaul "buffer queue!\n", sc->nge_unit); 114276479Swpaul return(ENOBUFS); 114376479Swpaul } 114476479Swpaul entry->slot = i; 114576479Swpaul SLIST_INSERT_HEAD(&sc->nge_jfree_listhead, 114676479Swpaul entry, jpool_entries); 114776479Swpaul } 114876479Swpaul 114976479Swpaul return(0); 115076479Swpaul} 115176479Swpaul 115276479Swpaulstatic void nge_free_jumbo_mem(sc) 115376479Swpaul struct nge_softc *sc; 115476479Swpaul{ 115576479Swpaul register int i; 115676479Swpaul struct nge_jpool_entry *entry; 115776479Swpaul 115876479Swpaul for (i = 0; i < NGE_JSLOTS; i++) { 115976479Swpaul entry = SLIST_FIRST(&sc->nge_jfree_listhead); 116076479Swpaul free(entry, M_DEVBUF); 116176479Swpaul SLIST_REMOVE_HEAD(&sc->nge_jfree_listhead, jpool_entries); 116276479Swpaul } 116376479Swpaul 116476479Swpaul contigfree(sc->nge_cdata.nge_jumbo_buf, NGE_JMEM, M_DEVBUF); 116576479Swpaul 116676479Swpaul return; 116776479Swpaul} 116876479Swpaul 116976479Swpaul/* 117076479Swpaul * Allocate a jumbo buffer. 117176479Swpaul */ 117276479Swpaulstatic void *nge_jalloc(sc) 117376479Swpaul struct nge_softc *sc; 117476479Swpaul{ 117576479Swpaul struct nge_jpool_entry *entry; 117676479Swpaul 117776479Swpaul entry = SLIST_FIRST(&sc->nge_jfree_listhead); 117876479Swpaul 117976479Swpaul if (entry == NULL) { 118076479Swpaul#ifdef NGE_VERBOSE 118176479Swpaul printf("nge%d: no free jumbo buffers\n", sc->nge_unit); 118276479Swpaul#endif 118376479Swpaul return(NULL); 118476479Swpaul } 118576479Swpaul 118676479Swpaul SLIST_REMOVE_HEAD(&sc->nge_jfree_listhead, jpool_entries); 118776479Swpaul SLIST_INSERT_HEAD(&sc->nge_jinuse_listhead, entry, jpool_entries); 118876479Swpaul return(sc->nge_cdata.nge_jslots[entry->slot]); 118976479Swpaul} 119076479Swpaul 119176479Swpaul/* 119276479Swpaul * Release a jumbo buffer. 119376479Swpaul */ 119476479Swpaulstatic void nge_jfree(buf, args) 119576479Swpaul caddr_t buf; 119676479Swpaul void *args; 119776479Swpaul{ 119876479Swpaul struct nge_softc *sc; 119976479Swpaul int i; 120076479Swpaul struct nge_jpool_entry *entry; 120176479Swpaul 120276479Swpaul /* Extract the softc struct pointer. */ 120376479Swpaul sc = args; 120476479Swpaul 120576479Swpaul if (sc == NULL) 120676479Swpaul panic("nge_jfree: can't find softc pointer!"); 120776479Swpaul 120876479Swpaul /* calculate the slot this buffer belongs to */ 120976479Swpaul i = ((vm_offset_t)buf 121076479Swpaul - (vm_offset_t)sc->nge_cdata.nge_jumbo_buf) / NGE_JLEN; 121176479Swpaul 121276479Swpaul if ((i < 0) || (i >= NGE_JSLOTS)) 121376479Swpaul panic("nge_jfree: asked to free buffer that we don't manage!"); 121476479Swpaul 121576479Swpaul entry = SLIST_FIRST(&sc->nge_jinuse_listhead); 121676479Swpaul if (entry == NULL) 121776479Swpaul panic("nge_jfree: buffer not in use!"); 121876479Swpaul entry->slot = i; 121976479Swpaul SLIST_REMOVE_HEAD(&sc->nge_jinuse_listhead, jpool_entries); 122076479Swpaul SLIST_INSERT_HEAD(&sc->nge_jfree_listhead, entry, jpool_entries); 122176479Swpaul 122276479Swpaul return; 122376479Swpaul} 122476479Swpaul/* 122576479Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to 122676479Swpaul * the higher level protocols. 122776479Swpaul */ 122876479Swpaulstatic void nge_rxeof(sc) 122976479Swpaul struct nge_softc *sc; 123076479Swpaul{ 123176479Swpaul struct ether_header *eh; 123276479Swpaul struct mbuf *m; 123376479Swpaul struct ifnet *ifp; 123476479Swpaul struct nge_desc *cur_rx; 123576479Swpaul int i, total_len = 0; 123676479Swpaul u_int32_t rxstat; 123776479Swpaul 123876479Swpaul ifp = &sc->arpcom.ac_if; 123976479Swpaul i = sc->nge_cdata.nge_rx_prod; 124076479Swpaul 124176479Swpaul while(NGE_OWNDESC(&sc->nge_ldata->nge_rx_list[i])) { 124276479Swpaul struct mbuf *m0 = NULL; 124376479Swpaul u_int32_t extsts; 124476479Swpaul 124576479Swpaul cur_rx = &sc->nge_ldata->nge_rx_list[i]; 124676479Swpaul rxstat = cur_rx->nge_rxstat; 124776479Swpaul extsts = cur_rx->nge_extsts; 124876479Swpaul m = cur_rx->nge_mbuf; 124976479Swpaul cur_rx->nge_mbuf = NULL; 125076479Swpaul total_len = NGE_RXBYTES(cur_rx); 125176479Swpaul NGE_INC(i, NGE_RX_LIST_CNT); 125276479Swpaul 125376479Swpaul /* 125476479Swpaul * If an error occurs, update stats, clear the 125576479Swpaul * status word and leave the mbuf cluster in place: 125676479Swpaul * it should simply get re-used next time this descriptor 125776479Swpaul * comes up in the ring. 125876479Swpaul */ 125976479Swpaul if (!(rxstat & NGE_CMDSTS_PKT_OK)) { 126076479Swpaul ifp->if_ierrors++; 126176479Swpaul nge_newbuf(sc, cur_rx, m); 126276479Swpaul continue; 126376479Swpaul } 126476479Swpaul 126576479Swpaul 126676479Swpaul /* 126776479Swpaul * Ok. NatSemi really screwed up here. This is the 126876479Swpaul * only gigE chip I know of with alignment constraints 126976479Swpaul * on receive buffers. RX buffers must be 64-bit aligned. 127076479Swpaul */ 127176479Swpaul m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, 127276479Swpaul total_len + ETHER_ALIGN, 0, ifp, NULL); 127376479Swpaul nge_newbuf(sc, cur_rx, m); 127476479Swpaul if (m0 == NULL) { 127576479Swpaul printf("nge%d: no receive buffers " 127676479Swpaul "available -- packet dropped!\n", 127776479Swpaul sc->nge_unit); 127876479Swpaul ifp->if_ierrors++; 127976479Swpaul continue; 128076479Swpaul } 128176479Swpaul m_adj(m0, ETHER_ALIGN); 128276479Swpaul m = m0; 128376479Swpaul 128476479Swpaul ifp->if_ipackets++; 128576479Swpaul eh = mtod(m, struct ether_header *); 128676479Swpaul 128776479Swpaul /* Remove header from mbuf and pass it on. */ 128876479Swpaul m_adj(m, sizeof(struct ether_header)); 128976479Swpaul 129076479Swpaul /* Do IP checksum checking. */ 129176479Swpaul if (ifp->if_hwassist) { 129276479Swpaul if (extsts & NGE_RXEXTSTS_IPPKT) 129376479Swpaul m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; 129476479Swpaul if (!(extsts & NGE_RXEXTSTS_IPCSUMERR)) 129576479Swpaul m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED; 129676479Swpaul } 129776479Swpaul 129876479Swpaul#if NVLAN > 0 129976479Swpaul /* 130076479Swpaul * If we received a packet with a vlan tag, pass it 130176479Swpaul * to vlan_input() instead of ether_input(). 130276479Swpaul */ 130376479Swpaul if (extsts & NGE_RXEXTSTS_VLANPKT) { 130476479Swpaul vlan_input_tag(eh, m, extsts & NGE_RXEXTSTS_VTCI); 130576479Swpaul continue; 130676479Swpaul } 130776479Swpaul#endif 130876479Swpaul 130976479Swpaul ether_input(ifp, eh, m); 131076479Swpaul } 131176479Swpaul 131276479Swpaul sc->nge_cdata.nge_rx_prod = i; 131376479Swpaul 131476479Swpaul return; 131576479Swpaul} 131676479Swpaul 131776479Swpaulvoid nge_rxeoc(sc) 131876479Swpaul struct nge_softc *sc; 131976479Swpaul{ 132076479Swpaul struct ifnet *ifp; 132176479Swpaul 132276479Swpaul ifp = &sc->arpcom.ac_if; 132376479Swpaul nge_rxeof(sc); 132476479Swpaul ifp->if_flags &= ~IFF_RUNNING; 132576479Swpaul nge_init(sc); 132676479Swpaul return; 132776479Swpaul} 132876479Swpaul 132976479Swpaul/* 133076479Swpaul * A frame was downloaded to the chip. It's safe for us to clean up 133176479Swpaul * the list buffers. 133276479Swpaul */ 133376479Swpaul 133476479Swpaulstatic void nge_txeof(sc) 133576479Swpaul struct nge_softc *sc; 133676479Swpaul{ 133776479Swpaul struct nge_desc *cur_tx = NULL; 133876479Swpaul struct ifnet *ifp; 133976479Swpaul u_int32_t idx; 134076479Swpaul 134176479Swpaul ifp = &sc->arpcom.ac_if; 134276479Swpaul 134376479Swpaul /* Clear the timeout timer. */ 134476479Swpaul ifp->if_timer = 0; 134576479Swpaul 134676479Swpaul /* 134776479Swpaul * Go through our tx list and free mbufs for those 134876479Swpaul * frames that have been transmitted. 134976479Swpaul */ 135076479Swpaul idx = sc->nge_cdata.nge_tx_cons; 135176479Swpaul while (idx != sc->nge_cdata.nge_tx_prod) { 135276479Swpaul cur_tx = &sc->nge_ldata->nge_tx_list[idx]; 135376479Swpaul 135476479Swpaul if (NGE_OWNDESC(cur_tx)) 135576479Swpaul break; 135676479Swpaul 135776479Swpaul if (cur_tx->nge_ctl & NGE_CMDSTS_MORE) { 135876479Swpaul sc->nge_cdata.nge_tx_cnt--; 135976479Swpaul NGE_INC(idx, NGE_TX_LIST_CNT); 136076479Swpaul continue; 136176479Swpaul } 136276479Swpaul 136376479Swpaul if (!(cur_tx->nge_ctl & NGE_CMDSTS_PKT_OK)) { 136476479Swpaul ifp->if_oerrors++; 136576479Swpaul if (cur_tx->nge_txstat & NGE_TXSTAT_EXCESSCOLLS) 136676479Swpaul ifp->if_collisions++; 136776479Swpaul if (cur_tx->nge_txstat & NGE_TXSTAT_OUTOFWINCOLL) 136876479Swpaul ifp->if_collisions++; 136976479Swpaul } 137076479Swpaul 137176479Swpaul ifp->if_collisions += 137276479Swpaul (cur_tx->nge_txstat & NGE_TXSTAT_COLLCNT) >> 16; 137376479Swpaul 137476479Swpaul ifp->if_opackets++; 137576479Swpaul if (cur_tx->nge_mbuf != NULL) { 137676479Swpaul m_freem(cur_tx->nge_mbuf); 137776479Swpaul cur_tx->nge_mbuf = NULL; 137876479Swpaul } 137976479Swpaul 138076479Swpaul sc->nge_cdata.nge_tx_cnt--; 138176479Swpaul NGE_INC(idx, NGE_TX_LIST_CNT); 138276479Swpaul ifp->if_timer = 0; 138376479Swpaul } 138476479Swpaul 138576479Swpaul sc->nge_cdata.nge_tx_cons = idx; 138676479Swpaul 138776479Swpaul if (cur_tx != NULL) 138876479Swpaul ifp->if_flags &= ~IFF_OACTIVE; 138976479Swpaul 139076479Swpaul return; 139176479Swpaul} 139276479Swpaul 139376479Swpaulstatic void nge_tick(xsc) 139476479Swpaul void *xsc; 139576479Swpaul{ 139676479Swpaul struct nge_softc *sc; 139776479Swpaul struct mii_data *mii; 139876479Swpaul struct ifnet *ifp; 139976479Swpaul int s; 140076479Swpaul 140176479Swpaul s = splimp(); 140276479Swpaul 140376479Swpaul sc = xsc; 140476479Swpaul ifp = &sc->arpcom.ac_if; 140576479Swpaul 140676479Swpaul mii = device_get_softc(sc->nge_miibus); 140776479Swpaul mii_tick(mii); 140876479Swpaul 140976479Swpaul if (!sc->nge_link) { 141076479Swpaul mii_pollstat(mii); 141176479Swpaul if (mii->mii_media_status & IFM_ACTIVE && 141276479Swpaul IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { 141376479Swpaul sc->nge_link++; 141476479Swpaul if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_TX) 141576479Swpaul printf("nge%d: gigabit link up\n", 141676479Swpaul sc->nge_unit); 141776479Swpaul if (ifp->if_snd.ifq_head != NULL) 141876479Swpaul nge_start(ifp); 141976479Swpaul } else 142076479Swpaul sc->nge_stat_ch = timeout(nge_tick, sc, hz); 142176479Swpaul } 142276479Swpaul 142376479Swpaul 142476479Swpaul splx(s); 142576479Swpaul 142676479Swpaul return; 142776479Swpaul} 142876479Swpaul 142976479Swpaulstatic void nge_intr(arg) 143076479Swpaul void *arg; 143176479Swpaul{ 143276479Swpaul struct nge_softc *sc; 143376479Swpaul struct ifnet *ifp; 143476479Swpaul u_int32_t status; 143576479Swpaul 143676479Swpaul sc = arg; 143776479Swpaul ifp = &sc->arpcom.ac_if; 143876479Swpaul 143976479Swpaul /* Supress unwanted interrupts */ 144076479Swpaul if (!(ifp->if_flags & IFF_UP)) { 144176479Swpaul nge_stop(sc); 144276479Swpaul return; 144376479Swpaul } 144476479Swpaul 144576479Swpaul /* Disable interrupts. */ 144676479Swpaul CSR_WRITE_4(sc, NGE_IER, 0); 144776479Swpaul 144876479Swpaul for (;;) { 144976479Swpaul /* Reading the ISR register clears all interrupts. */ 145076479Swpaul status = CSR_READ_4(sc, NGE_ISR); 145176479Swpaul 145276479Swpaul if ((status & NGE_INTRS) == 0) 145376479Swpaul break; 145476479Swpaul 145576479Swpaul if ((status & NGE_ISR_TX_DESC_OK) || 145676479Swpaul (status & NGE_ISR_TX_ERR) || 145776479Swpaul (status & NGE_ISR_TX_OK) || 145876479Swpaul (status & NGE_ISR_TX_IDLE)) 145976479Swpaul nge_txeof(sc); 146076479Swpaul 146176479Swpaul if ((status & NGE_ISR_RX_DESC_OK) || 146276479Swpaul (status & NGE_ISR_RX_OK)) 146376479Swpaul nge_rxeof(sc); 146476479Swpaul 146576479Swpaul if ((status & NGE_ISR_RX_ERR) || 146676479Swpaul (status & NGE_ISR_RX_OFLOW)) { 146776479Swpaul nge_rxeoc(sc); 146876479Swpaul } 146976479Swpaul 147076479Swpaul if (status & NGE_ISR_SYSERR) { 147176479Swpaul nge_reset(sc); 147276479Swpaul ifp->if_flags &= ~IFF_RUNNING; 147376479Swpaul nge_init(sc); 147476479Swpaul } 147576479Swpaul 147676479Swpaul if (status & NGE_IMR_PHY_INTR) { 147776479Swpaul sc->nge_link = 0; 147876479Swpaul nge_tick(sc); 147976479Swpaul } 148076479Swpaul } 148176479Swpaul 148276479Swpaul /* Re-enable interrupts. */ 148376479Swpaul CSR_WRITE_4(sc, NGE_IER, 1); 148476479Swpaul 148576479Swpaul if (ifp->if_snd.ifq_head != NULL) 148676479Swpaul nge_start(ifp); 148776479Swpaul 148876479Swpaul return; 148976479Swpaul} 149076479Swpaul 149176479Swpaul/* 149276479Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 149376479Swpaul * pointers to the fragment pointers. 149476479Swpaul */ 149576479Swpaulstatic int nge_encap(sc, m_head, txidx) 149676479Swpaul struct nge_softc *sc; 149776479Swpaul struct mbuf *m_head; 149876479Swpaul u_int32_t *txidx; 149976479Swpaul{ 150076479Swpaul struct nge_desc *f = NULL; 150176479Swpaul struct mbuf *m; 150276479Swpaul int frag, cur, cnt = 0; 150376479Swpaul#if NVLAN > 0 150476479Swpaul struct ifvlan *ifv = NULL; 150576479Swpaul 150676479Swpaul if ((m_head->m_flags & (M_PROTO1|M_PKTHDR)) == (M_PROTO1|M_PKTHDR) && 150776479Swpaul m_head->m_pkthdr.rcvif != NULL && 150876479Swpaul m_head->m_pkthdr.rcvif->if_type == IFT_8021_VLAN) 150976479Swpaul ifv = m_head->m_pkthdr.rcvif->if_softc; 151076479Swpaul#endif 151176479Swpaul 151276479Swpaul /* 151376479Swpaul * Start packing the mbufs in this chain into 151476479Swpaul * the fragment pointers. Stop when we run out 151576479Swpaul * of fragments or hit the end of the mbuf chain. 151676479Swpaul */ 151776479Swpaul m = m_head; 151876479Swpaul cur = frag = *txidx; 151976479Swpaul 152076479Swpaul for (m = m_head; m != NULL; m = m->m_next) { 152176479Swpaul if (m->m_len != 0) { 152276479Swpaul if ((NGE_TX_LIST_CNT - 152376479Swpaul (sc->nge_cdata.nge_tx_cnt + cnt)) < 2) 152476479Swpaul return(ENOBUFS); 152576479Swpaul f = &sc->nge_ldata->nge_tx_list[frag]; 152676479Swpaul f->nge_ctl = NGE_CMDSTS_MORE | m->m_len; 152776479Swpaul f->nge_ptr = vtophys(mtod(m, vm_offset_t)); 152876479Swpaul if (cnt != 0) 152976479Swpaul f->nge_ctl |= NGE_CMDSTS_OWN; 153076479Swpaul cur = frag; 153176479Swpaul NGE_INC(frag, NGE_TX_LIST_CNT); 153276479Swpaul cnt++; 153376479Swpaul } 153476479Swpaul } 153576479Swpaul 153676479Swpaul if (m != NULL) 153776479Swpaul return(ENOBUFS); 153876479Swpaul 153976479Swpaul sc->nge_ldata->nge_tx_list[cur].nge_extsts = 0; 154076479Swpaul if (m_head->m_pkthdr.csum_flags) { 154176479Swpaul if (m_head->m_pkthdr.csum_flags & CSUM_IP) 154276479Swpaul sc->nge_ldata->nge_tx_list[cur].nge_extsts |= 154376479Swpaul NGE_TXEXTSTS_IPCSUM; 154476479Swpaul if (m_head->m_pkthdr.csum_flags & CSUM_TCP) 154576479Swpaul sc->nge_ldata->nge_tx_list[cur].nge_extsts |= 154676479Swpaul NGE_TXEXTSTS_TCPCSUM; 154776479Swpaul if (m_head->m_pkthdr.csum_flags & CSUM_UDP) 154876479Swpaul sc->nge_ldata->nge_tx_list[cur].nge_extsts |= 154976479Swpaul NGE_TXEXTSTS_UDPCSUM; 155076479Swpaul } 155176479Swpaul 155276479Swpaul#if NVLAN > 0 155376479Swpaul if (ifv != NULL) { 155476479Swpaul sc->nge_ldata->nge_tx_list[cur].nge_extsts |= 155576479Swpaul (NGE_TXEXTSTS_VLANPKT|ifv->ifv_tag); 155676479Swpaul } 155776479Swpaul#endif 155876479Swpaul 155976479Swpaul sc->nge_ldata->nge_tx_list[cur].nge_mbuf = m_head; 156076479Swpaul sc->nge_ldata->nge_tx_list[cur].nge_ctl &= ~NGE_CMDSTS_MORE; 156176479Swpaul sc->nge_ldata->nge_tx_list[*txidx].nge_ctl |= NGE_CMDSTS_OWN; 156276479Swpaul sc->nge_cdata.nge_tx_cnt += cnt; 156376479Swpaul *txidx = frag; 156476479Swpaul 156576479Swpaul return(0); 156676479Swpaul} 156776479Swpaul 156876479Swpaul/* 156976479Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 157076479Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 157176479Swpaul * copy of the pointers since the transmit list fragment pointers are 157276479Swpaul * physical addresses. 157376479Swpaul */ 157476479Swpaul 157576479Swpaulstatic void nge_start(ifp) 157676479Swpaul struct ifnet *ifp; 157776479Swpaul{ 157876479Swpaul struct nge_softc *sc; 157976479Swpaul struct mbuf *m_head = NULL; 158076479Swpaul u_int32_t idx; 158176479Swpaul 158276479Swpaul sc = ifp->if_softc; 158376479Swpaul 158476479Swpaul if (!sc->nge_link) 158576479Swpaul return; 158676479Swpaul 158776479Swpaul idx = sc->nge_cdata.nge_tx_prod; 158876479Swpaul 158976479Swpaul if (ifp->if_flags & IFF_OACTIVE) 159076479Swpaul return; 159176479Swpaul 159276479Swpaul while(sc->nge_ldata->nge_tx_list[idx].nge_mbuf == NULL) { 159376479Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 159476479Swpaul if (m_head == NULL) 159576479Swpaul break; 159676479Swpaul 159776479Swpaul if (nge_encap(sc, m_head, &idx)) { 159876479Swpaul IF_PREPEND(&ifp->if_snd, m_head); 159976479Swpaul ifp->if_flags |= IFF_OACTIVE; 160076479Swpaul break; 160176479Swpaul } 160276479Swpaul 160376479Swpaul /* 160476479Swpaul * If there's a BPF listener, bounce a copy of this frame 160576479Swpaul * to him. 160676479Swpaul */ 160776479Swpaul if (ifp->if_bpf) 160876479Swpaul bpf_mtap(ifp, m_head); 160976479Swpaul 161076479Swpaul } 161176479Swpaul 161276479Swpaul /* Transmit */ 161376479Swpaul sc->nge_cdata.nge_tx_prod = idx; 161476479Swpaul NGE_SETBIT(sc, NGE_CSR, NGE_CSR_TX_ENABLE); 161576479Swpaul 161676479Swpaul /* 161776479Swpaul * Set a timeout in case the chip goes out to lunch. 161876479Swpaul */ 161976479Swpaul ifp->if_timer = 5; 162076479Swpaul 162176479Swpaul return; 162276479Swpaul} 162376479Swpaul 162476479Swpaulstatic void nge_init(xsc) 162576479Swpaul void *xsc; 162676479Swpaul{ 162776479Swpaul struct nge_softc *sc = xsc; 162876479Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 162976479Swpaul struct mii_data *mii; 163076479Swpaul int s; 163176479Swpaul 163276479Swpaul if (ifp->if_flags & IFF_RUNNING) 163376479Swpaul return; 163476479Swpaul 163576479Swpaul s = splimp(); 163676479Swpaul 163776479Swpaul /* 163876479Swpaul * Cancel pending I/O and free all RX/TX buffers. 163976479Swpaul */ 164076479Swpaul nge_stop(sc); 164176479Swpaul 164276479Swpaul mii = device_get_softc(sc->nge_miibus); 164376479Swpaul 164476479Swpaul /* Set MAC address */ 164576479Swpaul CSR_WRITE_4(sc, NGE_RXFILT_CTL, NGE_FILTADDR_PAR0); 164676479Swpaul CSR_WRITE_4(sc, NGE_RXFILT_DATA, 164776479Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); 164876479Swpaul CSR_WRITE_4(sc, NGE_RXFILT_CTL, NGE_FILTADDR_PAR1); 164976479Swpaul CSR_WRITE_4(sc, NGE_RXFILT_DATA, 165076479Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); 165176479Swpaul CSR_WRITE_4(sc, NGE_RXFILT_CTL, NGE_FILTADDR_PAR2); 165276479Swpaul CSR_WRITE_4(sc, NGE_RXFILT_DATA, 165376479Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); 165476479Swpaul 165576479Swpaul /* Init circular RX list. */ 165676479Swpaul if (nge_list_rx_init(sc) == ENOBUFS) { 165776479Swpaul printf("nge%d: initialization failed: no " 165876479Swpaul "memory for rx buffers\n", sc->nge_unit); 165976479Swpaul nge_stop(sc); 166076479Swpaul (void)splx(s); 166176479Swpaul return; 166276479Swpaul } 166376479Swpaul 166476479Swpaul /* 166576479Swpaul * Init tx descriptors. 166676479Swpaul */ 166776479Swpaul nge_list_tx_init(sc); 166876479Swpaul 166976479Swpaul /* 167076479Swpaul * For the NatSemi chip, we have to explicitly enable the 167176479Swpaul * reception of ARP frames, as well as turn on the 'perfect 167276479Swpaul * match' filter where we store the station address, otherwise 167376479Swpaul * we won't receive unicasts meant for this host. 167476479Swpaul */ 167576479Swpaul NGE_SETBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_ARP); 167676479Swpaul NGE_SETBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_PERFECT); 167776479Swpaul 167876479Swpaul /* If we want promiscuous mode, set the allframes bit. */ 167976479Swpaul if (ifp->if_flags & IFF_PROMISC) { 168076479Swpaul NGE_SETBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_ALLPHYS); 168176479Swpaul } else { 168276479Swpaul NGE_CLRBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_ALLPHYS); 168376479Swpaul } 168476479Swpaul 168576479Swpaul /* 168676479Swpaul * Set the capture broadcast bit to capture broadcast frames. 168776479Swpaul */ 168876479Swpaul if (ifp->if_flags & IFF_BROADCAST) { 168976479Swpaul NGE_SETBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_BROAD); 169076479Swpaul } else { 169176479Swpaul NGE_CLRBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_BROAD); 169276479Swpaul } 169376479Swpaul 169476479Swpaul /* 169576479Swpaul * Load the multicast filter. 169676479Swpaul */ 169776479Swpaul nge_setmulti(sc); 169876479Swpaul 169976479Swpaul /* Turn the receive filter on */ 170076479Swpaul NGE_SETBIT(sc, NGE_RXFILT_CTL, NGE_RXFILTCTL_ENABLE); 170176479Swpaul 170276479Swpaul /* 170376479Swpaul * Load the address of the RX and TX lists. 170476479Swpaul */ 170576479Swpaul CSR_WRITE_4(sc, NGE_RX_LISTPTR, 170676479Swpaul vtophys(&sc->nge_ldata->nge_rx_list[0])); 170776479Swpaul CSR_WRITE_4(sc, NGE_TX_LISTPTR, 170876479Swpaul vtophys(&sc->nge_ldata->nge_tx_list[0])); 170976479Swpaul 171076479Swpaul /* Set RX configuration */ 171176479Swpaul CSR_WRITE_4(sc, NGE_RX_CFG, NGE_RXCFG); 171276479Swpaul /* 171376479Swpaul * Enable hardware checksum validation for all IPv4 171476479Swpaul * packets, do not reject packets with bad checksums. 171576479Swpaul */ 171676479Swpaul if (ifp->if_hwassist) 171776479Swpaul CSR_WRITE_4(sc, NGE_VLAN_IP_RXCTL, NGE_VIPRXCTL_IPCSUM_ENB); 171876479Swpaul 171976479Swpaul#if NVLAN > 0 172076479Swpaul /* 172176479Swpaul * If VLAN support is enabled, tell the chip to detect 172276479Swpaul * and strip VLAN tag info from received frames. The tag 172376479Swpaul * will be provided in the extsts field in the RX descriptors. 172476479Swpaul */ 172576479Swpaul NGE_SETBIT(sc, NGE_VLAN_IP_RXCTL, 172676479Swpaul NGE_VIPRXCTL_TAG_DETECT_ENB|NGE_VIPRXCTL_TAG_STRIP_ENB); 172776479Swpaul#endif 172876479Swpaul 172976479Swpaul /* Set TX configuration */ 173076479Swpaul CSR_WRITE_4(sc, NGE_TX_CFG, NGE_TXCFG); 173176479Swpaul 173276479Swpaul /* 173376479Swpaul * Enable TX IPv4 checksumming on a per-packet basis. 173476479Swpaul */ 173576479Swpaul if (ifp->if_hwassist) 173676479Swpaul CSR_WRITE_4(sc, NGE_VLAN_IP_TXCTL, NGE_VIPTXCTL_CSUM_PER_PKT); 173776479Swpaul 173876479Swpaul#if NVLAN > 0 173976479Swpaul /* 174076479Swpaul * If VLAN support is enabled, tell the chip to insert 174176479Swpaul * VLAN tags on a per-packet basis as dictated by the 174276479Swpaul * code in the frame encapsulation routine. 174376479Swpaul */ 174476479Swpaul NGE_SETBIT(sc, NGE_VLAN_IP_TXCTL, NGE_VIPTXCTL_TAG_PER_PKT); 174576479Swpaul#endif 174676479Swpaul 174776479Swpaul /* Set full/half duplex mode. */ 174876479Swpaul if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 174976479Swpaul NGE_SETBIT(sc, NGE_TX_CFG, 175076479Swpaul (NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR)); 175176479Swpaul NGE_SETBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX); 175276479Swpaul } else { 175376479Swpaul NGE_CLRBIT(sc, NGE_TX_CFG, 175476479Swpaul (NGE_TXCFG_IGN_HBEAT|NGE_TXCFG_IGN_CARR)); 175576479Swpaul NGE_CLRBIT(sc, NGE_RX_CFG, NGE_RXCFG_RX_FDX); 175676479Swpaul } 175776479Swpaul 175876479Swpaul /* 175976479Swpaul * Enable the delivery of PHY interrupts based on 176076479Swpaul * link/speed/duplex status changes. 176176479Swpaul */ 176276479Swpaul NGE_SETBIT(sc, NGE_CFG, NGE_CFG_PHYINTR_SPD|NGE_CFG_MODE_1000| 176376479Swpaul NGE_CFG_PHYINTR_LNK|NGE_CFG_PHYINTR_DUP); 176476479Swpaul 176576479Swpaul /* 176676479Swpaul * Enable interrupts. 176776479Swpaul */ 176876479Swpaul CSR_WRITE_4(sc, NGE_IMR, NGE_INTRS); 176976479Swpaul CSR_WRITE_4(sc, NGE_IER, 1); 177076479Swpaul 177176479Swpaul /* Enable receiver and transmitter. */ 177276479Swpaul NGE_CLRBIT(sc, NGE_CSR, NGE_CSR_TX_DISABLE|NGE_CSR_RX_DISABLE); 177376479Swpaul NGE_SETBIT(sc, NGE_CSR, NGE_CSR_RX_ENABLE); 177476479Swpaul 177576479Swpaul nge_ifmedia_upd(ifp); 177676479Swpaul 177776479Swpaul ifp->if_flags |= IFF_RUNNING; 177876479Swpaul ifp->if_flags &= ~IFF_OACTIVE; 177976479Swpaul 178076479Swpaul (void)splx(s); 178176479Swpaul 178276479Swpaul return; 178376479Swpaul} 178476479Swpaul 178576479Swpaul/* 178676479Swpaul * Set media options. 178776479Swpaul */ 178876479Swpaulstatic int nge_ifmedia_upd(ifp) 178976479Swpaul struct ifnet *ifp; 179076479Swpaul{ 179176479Swpaul struct nge_softc *sc; 179276479Swpaul struct mii_data *mii; 179376479Swpaul 179476479Swpaul sc = ifp->if_softc; 179576479Swpaul 179676479Swpaul mii = device_get_softc(sc->nge_miibus); 179776479Swpaul sc->nge_link = 0; 179876479Swpaul if (mii->mii_instance) { 179976479Swpaul struct mii_softc *miisc; 180076479Swpaul for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL; 180176479Swpaul miisc = LIST_NEXT(miisc, mii_list)) 180276479Swpaul mii_phy_reset(miisc); 180376479Swpaul } 180476479Swpaul mii_mediachg(mii); 180576479Swpaul 180676479Swpaul return(0); 180776479Swpaul} 180876479Swpaul 180976479Swpaul/* 181076479Swpaul * Report current media status. 181176479Swpaul */ 181276479Swpaulstatic void nge_ifmedia_sts(ifp, ifmr) 181376479Swpaul struct ifnet *ifp; 181476479Swpaul struct ifmediareq *ifmr; 181576479Swpaul{ 181676479Swpaul struct nge_softc *sc; 181776479Swpaul struct mii_data *mii; 181876479Swpaul 181976479Swpaul sc = ifp->if_softc; 182076479Swpaul 182176479Swpaul mii = device_get_softc(sc->nge_miibus); 182276479Swpaul mii_pollstat(mii); 182376479Swpaul ifmr->ifm_active = mii->mii_media_active; 182476479Swpaul ifmr->ifm_status = mii->mii_media_status; 182576479Swpaul 182676479Swpaul return; 182776479Swpaul} 182876479Swpaul 182976479Swpaulstatic int nge_ioctl(ifp, command, data) 183076479Swpaul struct ifnet *ifp; 183176479Swpaul u_long command; 183276479Swpaul caddr_t data; 183376479Swpaul{ 183476479Swpaul struct nge_softc *sc = ifp->if_softc; 183576479Swpaul struct ifreq *ifr = (struct ifreq *) data; 183676479Swpaul struct mii_data *mii; 183776479Swpaul int s, error = 0; 183876479Swpaul 183976479Swpaul s = splimp(); 184076479Swpaul 184176479Swpaul switch(command) { 184276479Swpaul case SIOCSIFADDR: 184376479Swpaul case SIOCGIFADDR: 184476479Swpaul error = ether_ioctl(ifp, command, data); 184576479Swpaul break; 184676479Swpaul case SIOCSIFMTU: 184776479Swpaul if (ifr->ifr_mtu > NGE_JUMBO_MTU) 184876479Swpaul error = EINVAL; 184976479Swpaul else 185076479Swpaul ifp->if_mtu = ifr->ifr_mtu; 185176479Swpaul break; 185276479Swpaul case SIOCSIFFLAGS: 185376479Swpaul if (ifp->if_flags & IFF_UP) { 185476479Swpaul if (ifp->if_flags & IFF_RUNNING && 185576479Swpaul ifp->if_flags & IFF_PROMISC && 185676479Swpaul !(sc->nge_if_flags & IFF_PROMISC)) { 185776479Swpaul NGE_SETBIT(sc, NGE_RXFILT_CTL, 185876479Swpaul NGE_RXFILTCTL_ALLPHYS| 185976479Swpaul NGE_RXFILTCTL_ALLMULTI); 186076479Swpaul } else if (ifp->if_flags & IFF_RUNNING && 186176479Swpaul !(ifp->if_flags & IFF_PROMISC) && 186276479Swpaul sc->nge_if_flags & IFF_PROMISC) { 186376479Swpaul NGE_CLRBIT(sc, NGE_RXFILT_CTL, 186476479Swpaul NGE_RXFILTCTL_ALLPHYS); 186576479Swpaul if (!(ifp->if_flags & IFF_ALLMULTI)) 186676479Swpaul NGE_CLRBIT(sc, NGE_RXFILT_CTL, 186776479Swpaul NGE_RXFILTCTL_ALLMULTI); 186876479Swpaul } else { 186976479Swpaul ifp->if_flags &= ~IFF_RUNNING; 187076479Swpaul nge_init(sc); 187176479Swpaul } 187276479Swpaul } else { 187376479Swpaul if (ifp->if_flags & IFF_RUNNING) 187476479Swpaul nge_stop(sc); 187576479Swpaul } 187676479Swpaul sc->nge_if_flags = ifp->if_flags; 187776479Swpaul error = 0; 187876479Swpaul break; 187976479Swpaul case SIOCADDMULTI: 188076479Swpaul case SIOCDELMULTI: 188176479Swpaul nge_setmulti(sc); 188276479Swpaul error = 0; 188376479Swpaul break; 188476479Swpaul case SIOCGIFMEDIA: 188576479Swpaul case SIOCSIFMEDIA: 188676479Swpaul mii = device_get_softc(sc->nge_miibus); 188776479Swpaul error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 188876479Swpaul break; 188976479Swpaul default: 189076479Swpaul error = EINVAL; 189176479Swpaul break; 189276479Swpaul } 189376479Swpaul 189476479Swpaul (void)splx(s); 189576479Swpaul 189676479Swpaul return(error); 189776479Swpaul} 189876479Swpaul 189976479Swpaulstatic void nge_watchdog(ifp) 190076479Swpaul struct ifnet *ifp; 190176479Swpaul{ 190276479Swpaul struct nge_softc *sc; 190376479Swpaul 190476479Swpaul sc = ifp->if_softc; 190576479Swpaul 190676479Swpaul ifp->if_oerrors++; 190776479Swpaul printf("nge%d: watchdog timeout\n", sc->nge_unit); 190876479Swpaul 190976479Swpaul nge_stop(sc); 191076479Swpaul nge_reset(sc); 191176479Swpaul ifp->if_flags &= ~IFF_RUNNING; 191276479Swpaul nge_init(sc); 191376479Swpaul 191476479Swpaul if (ifp->if_snd.ifq_head != NULL) 191576479Swpaul nge_start(ifp); 191676479Swpaul 191776479Swpaul return; 191876479Swpaul} 191976479Swpaul 192076479Swpaul/* 192176479Swpaul * Stop the adapter and free any mbufs allocated to the 192276479Swpaul * RX and TX lists. 192376479Swpaul */ 192476479Swpaulstatic void nge_stop(sc) 192576479Swpaul struct nge_softc *sc; 192676479Swpaul{ 192776479Swpaul register int i; 192876479Swpaul struct ifnet *ifp; 192976479Swpaul struct ifmedia_entry *ifm; 193076479Swpaul struct mii_data *mii; 193176479Swpaul int mtmp, itmp; 193276479Swpaul 193376479Swpaul ifp = &sc->arpcom.ac_if; 193476479Swpaul ifp->if_timer = 0; 193576479Swpaul mii = device_get_softc(sc->nge_miibus); 193676479Swpaul 193776479Swpaul untimeout(nge_tick, sc, sc->nge_stat_ch); 193876479Swpaul CSR_WRITE_4(sc, NGE_IER, 0); 193976479Swpaul CSR_WRITE_4(sc, NGE_IMR, 0); 194076479Swpaul NGE_SETBIT(sc, NGE_CSR, NGE_CSR_TX_DISABLE|NGE_CSR_RX_DISABLE); 194176479Swpaul DELAY(1000); 194276479Swpaul CSR_WRITE_4(sc, NGE_TX_LISTPTR, 0); 194376479Swpaul CSR_WRITE_4(sc, NGE_RX_LISTPTR, 0); 194476479Swpaul 194576479Swpaul /* 194676479Swpaul * Isolate/power down the PHY, but leave the media selection 194776479Swpaul * unchanged so that things will be put back to normal when 194876479Swpaul * we bring the interface back up. 194976479Swpaul */ 195076479Swpaul itmp = ifp->if_flags; 195176479Swpaul ifp->if_flags |= IFF_UP; 195276479Swpaul ifm = mii->mii_media.ifm_cur; 195376479Swpaul mtmp = ifm->ifm_media; 195476479Swpaul ifm->ifm_media = IFM_ETHER|IFM_NONE; 195576479Swpaul mii_mediachg(mii); 195676479Swpaul ifm->ifm_media = mtmp; 195776479Swpaul ifp->if_flags = itmp; 195876479Swpaul 195976479Swpaul sc->nge_link = 0; 196076479Swpaul 196176479Swpaul /* 196276479Swpaul * Free data in the RX lists. 196376479Swpaul */ 196476479Swpaul for (i = 0; i < NGE_RX_LIST_CNT; i++) { 196576479Swpaul if (sc->nge_ldata->nge_rx_list[i].nge_mbuf != NULL) { 196676479Swpaul m_freem(sc->nge_ldata->nge_rx_list[i].nge_mbuf); 196776479Swpaul sc->nge_ldata->nge_rx_list[i].nge_mbuf = NULL; 196876479Swpaul } 196976479Swpaul } 197076479Swpaul bzero((char *)&sc->nge_ldata->nge_rx_list, 197176479Swpaul sizeof(sc->nge_ldata->nge_rx_list)); 197276479Swpaul 197376479Swpaul /* 197476479Swpaul * Free the TX list buffers. 197576479Swpaul */ 197676479Swpaul for (i = 0; i < NGE_TX_LIST_CNT; i++) { 197776479Swpaul if (sc->nge_ldata->nge_tx_list[i].nge_mbuf != NULL) { 197876479Swpaul m_freem(sc->nge_ldata->nge_tx_list[i].nge_mbuf); 197976479Swpaul sc->nge_ldata->nge_tx_list[i].nge_mbuf = NULL; 198076479Swpaul } 198176479Swpaul } 198276479Swpaul 198376479Swpaul bzero((char *)&sc->nge_ldata->nge_tx_list, 198476479Swpaul sizeof(sc->nge_ldata->nge_tx_list)); 198576479Swpaul 198676479Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 198776479Swpaul 198876479Swpaul return; 198976479Swpaul} 199076479Swpaul 199176479Swpaul/* 199276479Swpaul * Stop all chip I/O so that the kernel's probe routines don't 199376479Swpaul * get confused by errant DMAs when rebooting. 199476479Swpaul */ 199576479Swpaulstatic void nge_shutdown(dev) 199676479Swpaul device_t dev; 199776479Swpaul{ 199876479Swpaul struct nge_softc *sc; 199976479Swpaul 200076479Swpaul sc = device_get_softc(dev); 200176479Swpaul 200276479Swpaul nge_reset(sc); 200376479Swpaul nge_stop(sc); 200476479Swpaul 200576479Swpaul return; 200676479Swpaul} 2007