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