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