if_sf.c revision 51583
149076Swpaul/*
249076Swpaul * Copyright (c) 1997, 1998, 1999
349076Swpaul *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
449076Swpaul *
549076Swpaul * Redistribution and use in source and binary forms, with or without
649076Swpaul * modification, are permitted provided that the following conditions
749076Swpaul * are met:
849076Swpaul * 1. Redistributions of source code must retain the above copyright
949076Swpaul *    notice, this list of conditions and the following disclaimer.
1049076Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1149076Swpaul *    notice, this list of conditions and the following disclaimer in the
1249076Swpaul *    documentation and/or other materials provided with the distribution.
1349076Swpaul * 3. All advertising materials mentioning features or use of this software
1449076Swpaul *    must display the following acknowledgement:
1549076Swpaul *	This product includes software developed by Bill Paul.
1649076Swpaul * 4. Neither the name of the author nor the names of any co-contributors
1749076Swpaul *    may be used to endorse or promote products derived from this software
1849076Swpaul *    without specific prior written permission.
1949076Swpaul *
2049076Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2149076Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2249076Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2349076Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2449076Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2549076Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2649076Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2749076Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2849076Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2949076Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3049076Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
3149076Swpaul *
3250477Speter * $FreeBSD: head/sys/dev/sf/if_sf.c 51583 1999-09-23 03:32:57Z wpaul $
3349076Swpaul */
3449076Swpaul
3549076Swpaul/*
3649076Swpaul * Adaptec AIC-6915 "Starfire" PCI fast ethernet driver for FreeBSD.
3749076Swpaul * Programming manual is available from www.adaptec.com.
3849076Swpaul *
3949076Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu>
4049076Swpaul * Department of Electical Engineering
4149076Swpaul * Columbia University, New York City
4249076Swpaul */
4349076Swpaul
4449076Swpaul/*
4549076Swpaul * The Adaptec AIC-6915 "Starfire" is a 64-bit 10/100 PCI ethernet
4649076Swpaul * controller designed with flexibility and reducing CPU load in mind.
4749076Swpaul * The Starfire offers high and low priority buffer queues, a
4849076Swpaul * producer/consumer index mechanism and several different buffer
4949076Swpaul * queue and completion queue descriptor types. Any one of a number
5049076Swpaul * of different driver designs can be used, depending on system and
5149076Swpaul * OS requirements. This driver makes use of type0 transmit frame
5249076Swpaul * descriptors (since BSD fragments packets across an mbuf chain)
5349076Swpaul * and two RX buffer queues prioritized on size (one queue for small
5449076Swpaul * frames that will fit into a single mbuf, another with full size
5549076Swpaul * mbuf clusters for everything else). The producer/consumer indexes
5649076Swpaul * and completion queues are also used.
5749076Swpaul *
5849076Swpaul * One downside to the Starfire has to do with alignment: buffer
5949076Swpaul * queues must be aligned on 256-byte boundaries, and receive buffers
6049076Swpaul * must be aligned on longword boundaries. The receive buffer alignment
6149076Swpaul * causes problems on the Alpha platform, where the packet payload
6249076Swpaul * should be longword aligned. There is no simple way around this.
6349076Swpaul *
6449076Swpaul * For receive filtering, the Starfire offers 16 perfect filter slots
6549076Swpaul * and a 512-bit hash table.
6649076Swpaul *
6749076Swpaul * The Starfire has no internal transceiver, relying instead on an
6849076Swpaul * external MII-based transceiver. Accessing registers on external
6949076Swpaul * PHYs is done through a special register map rather than with the
7049076Swpaul * usual bitbang MDIO method.
7149076Swpaul *
7249076Swpaul * Acesssing the registers on the Starfire is a little tricky. The
7349076Swpaul * Starfire has a 512K internal register space. When programmed for
7449076Swpaul * PCI memory mapped mode, the entire register space can be accessed
7549076Swpaul * directly. However in I/O space mode, only 256 bytes are directly
7649076Swpaul * mapped into PCI I/O space. The other registers can be accessed
7749076Swpaul * indirectly using the SF_INDIRECTIO_ADDR and SF_INDIRECTIO_DATA
7849076Swpaul * registers inside the 256-byte I/O window.
7949076Swpaul */
8049076Swpaul
8149076Swpaul#include <sys/param.h>
8249076Swpaul#include <sys/systm.h>
8349076Swpaul#include <sys/sockio.h>
8449076Swpaul#include <sys/mbuf.h>
8549076Swpaul#include <sys/malloc.h>
8649076Swpaul#include <sys/kernel.h>
8749076Swpaul#include <sys/socket.h>
8849076Swpaul
8949076Swpaul#include <net/if.h>
9049076Swpaul#include <net/if_arp.h>
9149076Swpaul#include <net/ethernet.h>
9249076Swpaul#include <net/if_dl.h>
9349076Swpaul#include <net/if_media.h>
9449076Swpaul
9549076Swpaul#include <net/bpf.h>
9649076Swpaul
9749076Swpaul#include <vm/vm.h>              /* for vtophys */
9849076Swpaul#include <vm/pmap.h>            /* for vtophys */
9949076Swpaul#include <machine/clock.h>      /* for DELAY */
10049076Swpaul#include <machine/bus_pio.h>
10149076Swpaul#include <machine/bus_memio.h>
10249076Swpaul#include <machine/bus.h>
10349076Swpaul#include <machine/resource.h>
10449076Swpaul#include <sys/bus.h>
10549076Swpaul#include <sys/rman.h>
10649076Swpaul
10750675Swpaul#include <dev/mii/mii.h>
10850675Swpaul#include <dev/mii/miivar.h>
10950675Swpaul
11051089Speter/* "controller miibus0" required.  See GENERIC if you get errors here. */
11150675Swpaul#include "miibus_if.h"
11250675Swpaul
11349076Swpaul#include <pci/pcireg.h>
11449076Swpaul#include <pci/pcivar.h>
11549076Swpaul
11649076Swpaul#define SF_USEIOSPACE
11749076Swpaul
11849076Swpaul#include <pci/if_sfreg.h>
11949076Swpaul
12049076Swpaul#ifndef lint
12149076Swpaulstatic const char rcsid[] =
12250477Speter  "$FreeBSD: head/sys/dev/sf/if_sf.c 51583 1999-09-23 03:32:57Z wpaul $";
12349076Swpaul#endif
12449076Swpaul
12549076Swpaulstatic struct sf_type sf_devs[] = {
12649076Swpaul	{ AD_VENDORID, AD_DEVICEID_STARFIRE,
12749076Swpaul		"Adaptec AIC-6915 10/100BaseTX" },
12849076Swpaul	{ 0, 0, NULL }
12949076Swpaul};
13049076Swpaul
13149076Swpaulstatic int sf_probe		__P((device_t));
13249076Swpaulstatic int sf_attach		__P((device_t));
13349076Swpaulstatic int sf_detach		__P((device_t));
13449076Swpaulstatic void sf_intr		__P((void *));
13549076Swpaulstatic void sf_stats_update	__P((void *));
13649076Swpaulstatic void sf_rxeof		__P((struct sf_softc *));
13749076Swpaulstatic void sf_txeof		__P((struct sf_softc *));
13849076Swpaulstatic int sf_encap		__P((struct sf_softc *,
13949076Swpaul					struct sf_tx_bufdesc_type0 *,
14049076Swpaul					struct mbuf *));
14149076Swpaulstatic void sf_start		__P((struct ifnet *));
14249076Swpaulstatic int sf_ioctl		__P((struct ifnet *, u_long, caddr_t));
14349076Swpaulstatic void sf_init		__P((void *));
14449076Swpaulstatic void sf_stop		__P((struct sf_softc *));
14549076Swpaulstatic void sf_watchdog		__P((struct ifnet *));
14649076Swpaulstatic void sf_shutdown		__P((device_t));
14749076Swpaulstatic int sf_ifmedia_upd	__P((struct ifnet *));
14849076Swpaulstatic void sf_ifmedia_sts	__P((struct ifnet *, struct ifmediareq *));
14949076Swpaulstatic void sf_reset		__P((struct sf_softc *));
15049076Swpaulstatic int sf_init_rx_ring	__P((struct sf_softc *));
15149076Swpaulstatic void sf_init_tx_ring	__P((struct sf_softc *));
15249076Swpaulstatic int sf_newbuf		__P((struct sf_softc *,
15349076Swpaul					struct sf_rx_bufdesc_type0 *,
15449076Swpaul					struct mbuf *));
15549076Swpaulstatic void sf_setmulti		__P((struct sf_softc *));
15649076Swpaulstatic int sf_setperf		__P((struct sf_softc *, int, caddr_t));
15749076Swpaulstatic int sf_sethash		__P((struct sf_softc *, caddr_t, int));
15849076Swpaul#ifdef notdef
15949076Swpaulstatic int sf_setvlan		__P((struct sf_softc *, int, u_int32_t));
16049076Swpaul#endif
16149076Swpaul
16249076Swpaulstatic u_int8_t sf_read_eeprom	__P((struct sf_softc *, int));
16349076Swpaulstatic u_int32_t sf_calchash	__P((caddr_t));
16449076Swpaul
16550675Swpaulstatic int sf_miibus_readreg	__P((device_t, int, int));
16650675Swpaulstatic int sf_miibus_writereg	__P((device_t, int, int, int));
16750675Swpaulstatic void sf_miibus_statchg	__P((device_t));
16849076Swpaul
16949076Swpaulstatic u_int32_t csr_read_4	__P((struct sf_softc *, int));
17049076Swpaulstatic void csr_write_4		__P((struct sf_softc *, int, u_int32_t));
17149076Swpaul
17249076Swpaul#ifdef SF_USEIOSPACE
17349076Swpaul#define SF_RES			SYS_RES_IOPORT
17449076Swpaul#define SF_RID			SF_PCI_LOIO
17549076Swpaul#else
17649076Swpaul#define SF_RES			SYS_RES_MEMORY
17749076Swpaul#define SF_RID			SF_PCI_LOMEM
17849076Swpaul#endif
17949076Swpaul
18049076Swpaulstatic device_method_t sf_methods[] = {
18149076Swpaul	/* Device interface */
18249076Swpaul	DEVMETHOD(device_probe,		sf_probe),
18349076Swpaul	DEVMETHOD(device_attach,	sf_attach),
18449076Swpaul	DEVMETHOD(device_detach,	sf_detach),
18549076Swpaul	DEVMETHOD(device_shutdown,	sf_shutdown),
18650675Swpaul
18750675Swpaul	/* bus interface */
18850675Swpaul	DEVMETHOD(bus_print_child,	bus_generic_print_child),
18950675Swpaul	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
19050675Swpaul
19150675Swpaul	/* MII interface */
19250675Swpaul	DEVMETHOD(miibus_readreg,	sf_miibus_readreg),
19350675Swpaul	DEVMETHOD(miibus_writereg,	sf_miibus_writereg),
19450675Swpaul	DEVMETHOD(miibus_statchg,	sf_miibus_statchg),
19550675Swpaul
19649076Swpaul	{ 0, 0 }
19749076Swpaul};
19849076Swpaul
19949076Swpaulstatic driver_t sf_driver = {
20051455Swpaul	"sf",
20149076Swpaul	sf_methods,
20249076Swpaul	sizeof(struct sf_softc),
20349076Swpaul};
20449076Swpaul
20549076Swpaulstatic devclass_t sf_devclass;
20649076Swpaul
20751533SwpaulDRIVER_MODULE(if_sf, pci, sf_driver, sf_devclass, 0, 0);
20851473SwpaulDRIVER_MODULE(miibus, sf, miibus_driver, miibus_devclass, 0, 0);
20949076Swpaul
21049076Swpaul#define SF_SETBIT(sc, reg, x)	\
21149076Swpaul	csr_write_4(sc, reg, csr_read_4(sc, reg) | x)
21249076Swpaul
21349076Swpaul#define SF_CLRBIT(sc, reg, x)				\
21449076Swpaul	csr_write_4(sc, reg, csr_read_4(sc, reg) & ~x)
21549076Swpaul
21649076Swpaulstatic u_int32_t csr_read_4(sc, reg)
21749076Swpaul	struct sf_softc		*sc;
21849076Swpaul	int			reg;
21949076Swpaul{
22049076Swpaul	u_int32_t		val;
22149076Swpaul
22249076Swpaul#ifdef SF_USEIOSPACE
22349076Swpaul	CSR_WRITE_4(sc, SF_INDIRECTIO_ADDR, reg + SF_RMAP_INTREG_BASE);
22449076Swpaul	val = CSR_READ_4(sc, SF_INDIRECTIO_DATA);
22549076Swpaul#else
22649076Swpaul	val = CSR_READ_4(sc, (reg + SF_RMAP_INTREG_BASE));
22749076Swpaul#endif
22849076Swpaul
22949076Swpaul	return(val);
23049076Swpaul}
23149076Swpaul
23249076Swpaulstatic u_int8_t sf_read_eeprom(sc, reg)
23349076Swpaul	struct sf_softc		*sc;
23449076Swpaul	int			reg;
23549076Swpaul{
23649076Swpaul	u_int8_t		val;
23749076Swpaul
23849076Swpaul	val = (csr_read_4(sc, SF_EEADDR_BASE +
23949076Swpaul	    (reg & 0xFFFFFFFC)) >> (8 * (reg & 3))) & 0xFF;
24049076Swpaul
24149076Swpaul	return(val);
24249076Swpaul}
24349076Swpaul
24449076Swpaulstatic void csr_write_4(sc, reg, val)
24549076Swpaul	struct sf_softc		*sc;
24649076Swpaul	int			reg;
24749076Swpaul	u_int32_t		val;
24849076Swpaul{
24949076Swpaul#ifdef SF_USEIOSPACE
25049076Swpaul	CSR_WRITE_4(sc, SF_INDIRECTIO_ADDR, reg + SF_RMAP_INTREG_BASE);
25149076Swpaul	CSR_WRITE_4(sc, SF_INDIRECTIO_DATA, val);
25249076Swpaul#else
25349076Swpaul	CSR_WRITE_4(sc, (reg + SF_RMAP_INTREG_BASE), val);
25449076Swpaul#endif
25549076Swpaul	return;
25649076Swpaul}
25749076Swpaul
25849076Swpaulstatic u_int32_t sf_calchash(addr)
25949076Swpaul	caddr_t			addr;
26049076Swpaul{
26149076Swpaul	u_int32_t		crc, carry;
26249076Swpaul	int			i, j;
26349076Swpaul	u_int8_t		c;
26449076Swpaul
26549076Swpaul	/* Compute CRC for the address value. */
26649076Swpaul	crc = 0xFFFFFFFF; /* initial value */
26749076Swpaul
26849076Swpaul	for (i = 0; i < 6; i++) {
26949076Swpaul		c = *(addr + i);
27049076Swpaul		for (j = 0; j < 8; j++) {
27149076Swpaul			carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01);
27249076Swpaul			crc <<= 1;
27349076Swpaul			c >>= 1;
27449076Swpaul			if (carry)
27549076Swpaul				crc = (crc ^ 0x04c11db6) | carry;
27649076Swpaul		}
27749076Swpaul	}
27849076Swpaul
27949076Swpaul	/* return the filter bit position */
28049076Swpaul	return(crc >> 23 & 0x1FF);
28149076Swpaul}
28249076Swpaul
28349076Swpaul/*
28449076Swpaul * Copy the address 'mac' into the perfect RX filter entry at
28549076Swpaul * offset 'idx.' The perfect filter only has 16 entries so do
28649076Swpaul * some sanity tests.
28749076Swpaul */
28849076Swpaulstatic int sf_setperf(sc, idx, mac)
28949076Swpaul	struct sf_softc		*sc;
29049076Swpaul	int			idx;
29149076Swpaul	caddr_t			mac;
29249076Swpaul{
29349076Swpaul	u_int16_t		*p;
29449076Swpaul
29549076Swpaul	if (idx < 0 || idx > SF_RXFILT_PERFECT_CNT)
29649076Swpaul		return(EINVAL);
29749076Swpaul
29849076Swpaul	if (mac == NULL)
29949076Swpaul		return(EINVAL);
30049076Swpaul
30149076Swpaul	p = (u_int16_t *)mac;
30249076Swpaul
30349076Swpaul	csr_write_4(sc, SF_RXFILT_PERFECT_BASE +
30449076Swpaul	    (idx * SF_RXFILT_PERFECT_SKIP), htons(p[2]));
30549076Swpaul	csr_write_4(sc, SF_RXFILT_PERFECT_BASE +
30649076Swpaul	    (idx * SF_RXFILT_PERFECT_SKIP) + 4, htons(p[1]));
30749076Swpaul	csr_write_4(sc, SF_RXFILT_PERFECT_BASE +
30849076Swpaul	    (idx * SF_RXFILT_PERFECT_SKIP) + 8, htons(p[0]));
30949076Swpaul
31049076Swpaul	return(0);
31149076Swpaul}
31249076Swpaul
31349076Swpaul/*
31449076Swpaul * Set the bit in the 512-bit hash table that corresponds to the
31549076Swpaul * specified mac address 'mac.' If 'prio' is nonzero, update the
31649076Swpaul * priority hash table instead of the filter hash table.
31749076Swpaul */
31849076Swpaulstatic int sf_sethash(sc, mac, prio)
31949076Swpaul	struct sf_softc		*sc;
32049076Swpaul	caddr_t			mac;
32149076Swpaul	int			prio;
32249076Swpaul{
32349076Swpaul	u_int32_t		h = 0;
32449076Swpaul
32549076Swpaul	if (mac == NULL)
32649076Swpaul		return(EINVAL);
32749076Swpaul
32849076Swpaul	h = sf_calchash(mac);
32949076Swpaul
33049076Swpaul	if (prio) {
33149076Swpaul		SF_SETBIT(sc, SF_RXFILT_HASH_BASE + SF_RXFILT_HASH_PRIOOFF +
33249076Swpaul		    (SF_RXFILT_HASH_SKIP * (h >> 4)), (1 << (h & 0xF)));
33349076Swpaul	} else {
33449076Swpaul		SF_SETBIT(sc, SF_RXFILT_HASH_BASE + SF_RXFILT_HASH_ADDROFF +
33549076Swpaul		    (SF_RXFILT_HASH_SKIP * (h >> 4)), (1 << (h & 0xF)));
33649076Swpaul	}
33749076Swpaul
33849076Swpaul	return(0);
33949076Swpaul}
34049076Swpaul
34149076Swpaul#ifdef notdef
34249076Swpaul/*
34349076Swpaul * Set a VLAN tag in the receive filter.
34449076Swpaul */
34549076Swpaulstatic int sf_setvlan(sc, idx, vlan)
34649076Swpaul	struct sf_softc		*sc;
34749076Swpaul	int			idx;
34849076Swpaul	u_int32_t		vlan;
34949076Swpaul{
35049076Swpaul	if (idx < 0 || idx >> SF_RXFILT_HASH_CNT)
35149076Swpaul		return(EINVAL);
35249076Swpaul
35349076Swpaul	csr_write_4(sc, SF_RXFILT_HASH_BASE +
35449076Swpaul	    (idx * SF_RXFILT_HASH_SKIP) + SF_RXFILT_HASH_VLANOFF, vlan);
35549076Swpaul
35649076Swpaul	return(0);
35749076Swpaul}
35849076Swpaul#endif
35949076Swpaul
36050675Swpaulstatic int sf_miibus_readreg(dev, phy, reg)
36150675Swpaul	device_t		dev;
36250675Swpaul	int			phy, reg;
36350675Swpaul{
36449076Swpaul	struct sf_softc		*sc;
36549076Swpaul	int			i;
36649076Swpaul	u_int32_t		val = 0;
36749076Swpaul
36850675Swpaul	sc = device_get_softc(dev);
36950675Swpaul
37049076Swpaul	for (i = 0; i < SF_TIMEOUT; i++) {
37150675Swpaul		val = csr_read_4(sc, SF_PHY_REG(phy, reg));
37249076Swpaul		if (val & SF_MII_DATAVALID)
37349076Swpaul			break;
37449076Swpaul	}
37549076Swpaul
37649076Swpaul	if (i == SF_TIMEOUT)
37749076Swpaul		return(0);
37849076Swpaul
37949076Swpaul	if ((val & 0x0000FFFF) == 0xFFFF)
38049076Swpaul		return(0);
38149076Swpaul
38249076Swpaul	return(val & 0x0000FFFF);
38349076Swpaul}
38449076Swpaul
38550675Swpaulstatic int sf_miibus_writereg(dev, phy, reg, val)
38650675Swpaul	device_t		dev;
38750675Swpaul	int			phy, reg, val;
38850675Swpaul{
38949076Swpaul	struct sf_softc		*sc;
39049076Swpaul	int			i;
39149076Swpaul	int			busy;
39249076Swpaul
39350675Swpaul	sc = device_get_softc(dev);
39449076Swpaul
39550675Swpaul	csr_write_4(sc, SF_PHY_REG(phy, reg), val);
39650675Swpaul
39749076Swpaul	for (i = 0; i < SF_TIMEOUT; i++) {
39850675Swpaul		busy = csr_read_4(sc, SF_PHY_REG(phy, reg));
39949076Swpaul		if (!(busy & SF_MII_BUSY))
40049076Swpaul			break;
40149076Swpaul	}
40249076Swpaul
40350675Swpaul	return(0);
40450675Swpaul}
40550675Swpaul
40650675Swpaulstatic void sf_miibus_statchg(dev)
40750675Swpaul	device_t		dev;
40850675Swpaul{
40950675Swpaul	struct sf_softc		*sc;
41050675Swpaul	struct mii_data		*mii;
41150675Swpaul
41250675Swpaul	sc = device_get_softc(dev);
41350675Swpaul	mii = device_get_softc(sc->sf_miibus);
41450675Swpaul
41550675Swpaul	if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) {
41650675Swpaul		SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_FULLDUPLEX);
41750675Swpaul	} else {
41850675Swpaul		SF_CLRBIT(sc, SF_MACCFG_1, SF_MACCFG1_FULLDUPLEX);
41950675Swpaul	}
42050675Swpaul
42149076Swpaul	return;
42249076Swpaul}
42349076Swpaul
42449076Swpaulstatic void sf_setmulti(sc)
42549076Swpaul	struct sf_softc		*sc;
42649076Swpaul{
42749076Swpaul	struct ifnet		*ifp;
42849076Swpaul	int			i;
42949076Swpaul	struct ifmultiaddr	*ifma;
43049076Swpaul	u_int8_t		dummy[] = { 0, 0, 0, 0, 0, 0 };
43149076Swpaul
43249076Swpaul	ifp = &sc->arpcom.ac_if;
43349076Swpaul
43449076Swpaul	/* First zot all the existing filters. */
43549076Swpaul	for (i = 1; i < SF_RXFILT_PERFECT_CNT; i++)
43649076Swpaul		sf_setperf(sc, i, (char *)&dummy);
43749076Swpaul	for (i = SF_RXFILT_HASH_BASE;
43849076Swpaul	    i < (SF_RXFILT_HASH_MAX + 1); i += 4)
43949076Swpaul		csr_write_4(sc, i, 0);
44049076Swpaul	SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_ALLMULTI);
44149076Swpaul
44249076Swpaul	/* Now program new ones. */
44349076Swpaul	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
44449076Swpaul		SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_ALLMULTI);
44549076Swpaul	} else {
44649076Swpaul		i = 1;
44749076Swpaul		/* First find the tail of the list. */
44849076Swpaul		for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL;
44949076Swpaul					ifma = ifma->ifma_link.le_next) {
45049076Swpaul			if (ifma->ifma_link.le_next == NULL)
45149076Swpaul				break;
45249076Swpaul		}
45349076Swpaul		/* Now traverse the list backwards. */
45449076Swpaul		for (; ifma != NULL && ifma != (void *)&ifp->if_multiaddrs;
45549076Swpaul			ifma = (struct ifmultiaddr *)ifma->ifma_link.le_prev) {
45649076Swpaul			if (ifma->ifma_addr->sa_family != AF_LINK)
45749076Swpaul				continue;
45849076Swpaul			/*
45949076Swpaul			 * Program the first 15 multicast groups
46049076Swpaul			 * into the perfect filter. For all others,
46149076Swpaul			 * use the hash table.
46249076Swpaul			 */
46349076Swpaul			if (i < SF_RXFILT_PERFECT_CNT) {
46449076Swpaul				sf_setperf(sc, i,
46549076Swpaul			LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
46649076Swpaul				i++;
46749076Swpaul				continue;
46849076Swpaul			}
46949076Swpaul
47049076Swpaul			sf_sethash(sc,
47149076Swpaul			    LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 0);
47249076Swpaul		}
47349076Swpaul	}
47449076Swpaul
47549076Swpaul	return;
47649076Swpaul}
47749076Swpaul
47849076Swpaul/*
47949076Swpaul * Set media options.
48049076Swpaul */
48149076Swpaulstatic int sf_ifmedia_upd(ifp)
48249076Swpaul	struct ifnet		*ifp;
48349076Swpaul{
48449076Swpaul	struct sf_softc		*sc;
48550675Swpaul	struct mii_data		*mii;
48649076Swpaul
48749076Swpaul	sc = ifp->if_softc;
48850675Swpaul	mii = device_get_softc(sc->sf_miibus);
48950675Swpaul	mii_mediachg(mii);
49049076Swpaul
49149076Swpaul	return(0);
49249076Swpaul}
49349076Swpaul
49449076Swpaul/*
49549076Swpaul * Report current media status.
49649076Swpaul */
49749076Swpaulstatic void sf_ifmedia_sts(ifp, ifmr)
49849076Swpaul	struct ifnet		*ifp;
49949076Swpaul	struct ifmediareq	*ifmr;
50049076Swpaul{
50149076Swpaul	struct sf_softc		*sc;
50250675Swpaul	struct mii_data		*mii;
50349076Swpaul
50449076Swpaul	sc = ifp->if_softc;
50550675Swpaul	mii = device_get_softc(sc->sf_miibus);
50649076Swpaul
50750675Swpaul	mii_pollstat(mii);
50850675Swpaul	ifmr->ifm_active = mii->mii_media_active;
50950675Swpaul	ifmr->ifm_status = mii->mii_media_status;
51049076Swpaul
51149076Swpaul	return;
51249076Swpaul}
51349076Swpaul
51449076Swpaulstatic int sf_ioctl(ifp, command, data)
51549076Swpaul	struct ifnet		*ifp;
51649076Swpaul	u_long			command;
51749076Swpaul	caddr_t			data;
51849076Swpaul{
51949076Swpaul	struct sf_softc		*sc = ifp->if_softc;
52049076Swpaul	struct ifreq		*ifr = (struct ifreq *) data;
52150675Swpaul	struct mii_data		*mii;
52249076Swpaul	int			s, error = 0;
52349076Swpaul
52449076Swpaul	s = splimp();
52549076Swpaul
52649076Swpaul	switch(command) {
52749076Swpaul	case SIOCSIFADDR:
52849076Swpaul	case SIOCGIFADDR:
52949076Swpaul	case SIOCSIFMTU:
53049076Swpaul		error = ether_ioctl(ifp, command, data);
53149076Swpaul		break;
53249076Swpaul	case SIOCSIFFLAGS:
53349076Swpaul		if (ifp->if_flags & IFF_UP) {
53449076Swpaul			sf_init(sc);
53549076Swpaul		} else {
53649076Swpaul			if (ifp->if_flags & IFF_RUNNING)
53749076Swpaul				sf_stop(sc);
53849076Swpaul		}
53949076Swpaul		error = 0;
54049076Swpaul		break;
54149076Swpaul	case SIOCADDMULTI:
54249076Swpaul	case SIOCDELMULTI:
54349076Swpaul		sf_setmulti(sc);
54449076Swpaul		error = 0;
54549076Swpaul		break;
54649076Swpaul	case SIOCGIFMEDIA:
54749076Swpaul	case SIOCSIFMEDIA:
54850675Swpaul		mii = device_get_softc(sc->sf_miibus);
54950675Swpaul		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
55049076Swpaul		break;
55149076Swpaul	default:
55249076Swpaul		error = EINVAL;
55349076Swpaul		break;
55449076Swpaul	}
55549076Swpaul
55649076Swpaul	(void)splx(s);
55749076Swpaul
55849076Swpaul	return(error);
55949076Swpaul}
56049076Swpaul
56149076Swpaulstatic void sf_reset(sc)
56249076Swpaul	struct sf_softc		*sc;
56349076Swpaul{
56449076Swpaul	register int		i;
56549076Swpaul
56649076Swpaul	csr_write_4(sc, SF_GEN_ETH_CTL, 0);
56749076Swpaul	SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_SOFTRESET);
56849076Swpaul	DELAY(1000);
56949076Swpaul	SF_CLRBIT(sc, SF_MACCFG_1, SF_MACCFG1_SOFTRESET);
57049076Swpaul
57149076Swpaul	SF_SETBIT(sc, SF_PCI_DEVCFG, SF_PCIDEVCFG_RESET);
57249076Swpaul
57349076Swpaul	for (i = 0; i < SF_TIMEOUT; i++) {
57449076Swpaul		DELAY(10);
57549076Swpaul		if (!(csr_read_4(sc, SF_PCI_DEVCFG) & SF_PCIDEVCFG_RESET))
57649076Swpaul			break;
57749076Swpaul	}
57849076Swpaul
57949076Swpaul	if (i == SF_TIMEOUT)
58049076Swpaul		printf("sf%d: reset never completed!\n", sc->sf_unit);
58149076Swpaul
58249076Swpaul	/* Wait a little while for the chip to get its brains in order. */
58349076Swpaul	DELAY(1000);
58449076Swpaul	return;
58549076Swpaul}
58649076Swpaul
58749076Swpaul/*
58849076Swpaul * Probe for an Adaptec AIC-6915 chip. Check the PCI vendor and device
58949076Swpaul * IDs against our list and return a device name if we find a match.
59049076Swpaul * We also check the subsystem ID so that we can identify exactly which
59149076Swpaul * NIC has been found, if possible.
59249076Swpaul */
59349076Swpaulstatic int sf_probe(dev)
59449076Swpaul	device_t		dev;
59549076Swpaul{
59649076Swpaul	struct sf_type		*t;
59749076Swpaul
59849076Swpaul	t = sf_devs;
59949076Swpaul
60049076Swpaul	while(t->sf_name != NULL) {
60149076Swpaul		if ((pci_get_vendor(dev) == t->sf_vid) &&
60249076Swpaul		    (pci_get_device(dev) == t->sf_did)) {
60351336Swpaul			switch((pci_read_config(dev,
60451336Swpaul			    SF_PCI_SUBVEN_ID, 4) >> 16) & 0xFFFF) {
60549076Swpaul			case AD_SUBSYSID_62011_REV0:
60649076Swpaul			case AD_SUBSYSID_62011_REV1:
60749076Swpaul				device_set_desc(dev,
60849076Swpaul				    "Adaptec ANA-62011 10/100BaseTX");
60949076Swpaul				return(0);
61049076Swpaul				break;
61149076Swpaul			case AD_SUBSYSID_62022:
61249076Swpaul				device_set_desc(dev,
61349076Swpaul				    "Adaptec ANA-62022 10/100BaseTX");
61449076Swpaul				return(0);
61549076Swpaul				break;
61649076Swpaul			case AD_SUBSYSID_62044:
61749076Swpaul				device_set_desc(dev,
61849076Swpaul				    "Adaptec ANA-62044 10/100BaseTX");
61949076Swpaul				return(0);
62049076Swpaul				break;
62149076Swpaul			case AD_SUBSYSID_62020:
62249076Swpaul				device_set_desc(dev,
62349076Swpaul				    "Adaptec ANA-62020 10/100BaseFX");
62449076Swpaul				return(0);
62549076Swpaul				break;
62649076Swpaul			case AD_SUBSYSID_69011:
62749076Swpaul				device_set_desc(dev,
62849076Swpaul				    "Adaptec ANA-69011 10/100BaseTX");
62949076Swpaul				return(0);
63049076Swpaul				break;
63149076Swpaul			default:
63249076Swpaul				device_set_desc(dev, t->sf_name);
63349076Swpaul				return(0);
63449076Swpaul				break;
63549076Swpaul			}
63649076Swpaul		}
63749076Swpaul		t++;
63849076Swpaul	}
63949076Swpaul
64049076Swpaul	return(ENXIO);
64149076Swpaul}
64249076Swpaul
64349076Swpaul/*
64449076Swpaul * Attach the interface. Allocate softc structures, do ifmedia
64549076Swpaul * setup and ethernet/BPF attach.
64649076Swpaul */
64749076Swpaulstatic int sf_attach(dev)
64849076Swpaul	device_t		dev;
64949076Swpaul{
65049076Swpaul	int			s, i;
65149076Swpaul	u_int32_t		command;
65249076Swpaul	struct sf_softc		*sc;
65349076Swpaul	struct ifnet		*ifp;
65449076Swpaul	int			unit, rid, error = 0;
65549076Swpaul
65649076Swpaul	s = splimp();
65749076Swpaul
65849076Swpaul	sc = device_get_softc(dev);
65949076Swpaul	unit = device_get_unit(dev);
66049076Swpaul	bzero(sc, sizeof(struct sf_softc));
66149076Swpaul
66249076Swpaul	/*
66349076Swpaul	 * Handle power management nonsense.
66449076Swpaul	 */
66549076Swpaul	command = pci_read_config(dev, SF_PCI_CAPID, 4) & 0x000000FF;
66649076Swpaul	if (command == 0x01) {
66749076Swpaul
66849076Swpaul		command = pci_read_config(dev, SF_PCI_PWRMGMTCTRL, 4);
66949076Swpaul		if (command & SF_PSTATE_MASK) {
67049076Swpaul			u_int32_t		iobase, membase, irq;
67149076Swpaul
67249076Swpaul			/* Save important PCI config data. */
67349076Swpaul			iobase = pci_read_config(dev, SF_PCI_LOIO, 4);
67449076Swpaul			membase = pci_read_config(dev, SF_PCI_LOMEM, 4);
67549076Swpaul			irq = pci_read_config(dev, SF_PCI_INTLINE, 4);
67649076Swpaul
67749076Swpaul			/* Reset the power state. */
67849076Swpaul			printf("sf%d: chip is in D%d power mode "
67949076Swpaul			"-- setting to D0\n", unit, command & SF_PSTATE_MASK);
68049076Swpaul			command &= 0xFFFFFFFC;
68149076Swpaul			pci_write_config(dev, SF_PCI_PWRMGMTCTRL, command, 4);
68249076Swpaul
68349076Swpaul			/* Restore PCI config data. */
68449076Swpaul			pci_write_config(dev, SF_PCI_LOIO, iobase, 4);
68549076Swpaul			pci_write_config(dev, SF_PCI_LOMEM, membase, 4);
68649076Swpaul			pci_write_config(dev, SF_PCI_INTLINE, irq, 4);
68749076Swpaul		}
68849076Swpaul	}
68949076Swpaul
69049076Swpaul	/*
69149076Swpaul	 * Map control/status registers.
69249076Swpaul	 */
69349076Swpaul	command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4);
69449076Swpaul	command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
69549076Swpaul	pci_write_config(dev, PCI_COMMAND_STATUS_REG, command, 4);
69649076Swpaul	command = pci_read_config(dev, PCI_COMMAND_STATUS_REG, 4);
69749076Swpaul
69849076Swpaul#ifdef SF_USEIOSPACE
69949076Swpaul	if (!(command & PCIM_CMD_PORTEN)) {
70049076Swpaul		printf("sf%d: failed to enable I/O ports!\n", unit);
70149076Swpaul		error = ENXIO;
70249076Swpaul		goto fail;
70349076Swpaul	}
70449076Swpaul#else
70549076Swpaul	if (!(command & PCIM_CMD_MEMEN)) {
70649076Swpaul		printf("sf%d: failed to enable memory mapping!\n", unit);
70749076Swpaul		error = ENXIO;
70849076Swpaul		goto fail;
70949076Swpaul	}
71049076Swpaul#endif
71149076Swpaul
71249076Swpaul	rid = SF_RID;
71349076Swpaul	sc->sf_res = bus_alloc_resource(dev, SF_RES, &rid,
71449076Swpaul	    0, ~0, 1, RF_ACTIVE);
71549076Swpaul
71649076Swpaul	if (sc->sf_res == NULL) {
71749076Swpaul		printf ("sf%d: couldn't map ports\n", unit);
71849076Swpaul		error = ENXIO;
71949076Swpaul		goto fail;
72049076Swpaul	}
72149076Swpaul
72249076Swpaul	sc->sf_btag = rman_get_bustag(sc->sf_res);
72349076Swpaul	sc->sf_bhandle = rman_get_bushandle(sc->sf_res);
72449076Swpaul
72549076Swpaul	/* Allocate interrupt */
72649076Swpaul	rid = 0;
72749076Swpaul	sc->sf_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
72849076Swpaul	    RF_SHAREABLE | RF_ACTIVE);
72949076Swpaul
73049076Swpaul	if (sc->sf_irq == NULL) {
73149076Swpaul		printf("sf%d: couldn't map interrupt\n", unit);
73249076Swpaul		bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res);
73349076Swpaul		error = ENXIO;
73449076Swpaul		goto fail;
73549076Swpaul	}
73649076Swpaul
73749076Swpaul	error = bus_setup_intr(dev, sc->sf_irq, INTR_TYPE_NET,
73849076Swpaul	    sf_intr, sc, &sc->sf_intrhand);
73949076Swpaul
74049076Swpaul	if (error) {
74149076Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sf_res);
74249076Swpaul		bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res);
74349076Swpaul		printf("sf%d: couldn't set up irq\n", unit);
74449076Swpaul		goto fail;
74549076Swpaul	}
74649076Swpaul
74749076Swpaul	callout_handle_init(&sc->sf_stat_ch);
74849076Swpaul
74949076Swpaul	/* Reset the adapter. */
75049076Swpaul	sf_reset(sc);
75149076Swpaul
75249076Swpaul	/*
75349076Swpaul	 * Get station address from the EEPROM.
75449076Swpaul	 */
75549076Swpaul	for (i = 0; i < ETHER_ADDR_LEN; i++)
75649076Swpaul		sc->arpcom.ac_enaddr[i] =
75749076Swpaul		    sf_read_eeprom(sc, SF_EE_NODEADDR + ETHER_ADDR_LEN - i);
75849076Swpaul
75949076Swpaul	/*
76049076Swpaul	 * An Adaptec chip was detected. Inform the world.
76149076Swpaul	 */
76249076Swpaul	printf("sf%d: Ethernet address: %6D\n", unit,
76349076Swpaul	    sc->arpcom.ac_enaddr, ":");
76449076Swpaul
76549076Swpaul	sc->sf_unit = unit;
76649076Swpaul
76749076Swpaul	/* Allocate the descriptor queues. */
76849076Swpaul	sc->sf_ldata = contigmalloc(sizeof(struct sf_list_data), M_DEVBUF,
76950675Swpaul	    M_NOWAIT, 0x100000, 0xffffffff, PAGE_SIZE, 0);
77049076Swpaul
77149076Swpaul	if (sc->sf_ldata == NULL) {
77249076Swpaul		printf("sf%d: no memory for list buffers!\n", unit);
77349076Swpaul		bus_teardown_intr(dev, sc->sf_irq, sc->sf_intrhand);
77449076Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sf_irq);
77549076Swpaul		bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res);
77649076Swpaul		error = ENXIO;
77749076Swpaul		goto fail;
77849076Swpaul	}
77949076Swpaul
78049076Swpaul	bzero(sc->sf_ldata, sizeof(struct sf_list_data));
78149076Swpaul
78250675Swpaul	/* Do MII setup. */
78350675Swpaul	if (mii_phy_probe(dev, &sc->sf_miibus,
78450675Swpaul	    sf_ifmedia_upd, sf_ifmedia_sts)) {
78549076Swpaul		printf("sf%d: MII without any phy!\n", sc->sf_unit);
78649076Swpaul		free(sc->sf_ldata, M_DEVBUF);
78749076Swpaul		bus_teardown_intr(dev, sc->sf_irq, sc->sf_intrhand);
78849076Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sf_irq);
78949076Swpaul		bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res);
79049076Swpaul		error = ENXIO;
79149076Swpaul		goto fail;
79249076Swpaul	}
79349076Swpaul
79449076Swpaul	ifp = &sc->arpcom.ac_if;
79549076Swpaul	ifp->if_softc = sc;
79649076Swpaul	ifp->if_unit = unit;
79749076Swpaul	ifp->if_name = "sf";
79849076Swpaul	ifp->if_mtu = ETHERMTU;
79949076Swpaul	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
80049076Swpaul	ifp->if_ioctl = sf_ioctl;
80149076Swpaul	ifp->if_output = ether_output;
80249076Swpaul	ifp->if_start = sf_start;
80349076Swpaul	ifp->if_watchdog = sf_watchdog;
80449076Swpaul	ifp->if_init = sf_init;
80549076Swpaul	ifp->if_baudrate = 10000000;
80649076Swpaul	ifp->if_snd.ifq_maxlen = SF_TX_DLIST_CNT - 1;
80749076Swpaul
80849076Swpaul	/*
80949076Swpaul	 * Call MI attach routines.
81049076Swpaul	 */
81149076Swpaul	if_attach(ifp);
81249076Swpaul	ether_ifattach(ifp);
81349076Swpaul
81449076Swpaul	bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
81549076Swpaul
81649076Swpaulfail:
81749076Swpaul	splx(s);
81849076Swpaul	return(error);
81949076Swpaul}
82049076Swpaul
82149076Swpaulstatic int sf_detach(dev)
82249076Swpaul	device_t		dev;
82349076Swpaul{
82449076Swpaul	struct sf_softc		*sc;
82549076Swpaul	struct ifnet		*ifp;
82649076Swpaul	int			s;
82749076Swpaul
82849076Swpaul	s = splimp();
82949076Swpaul
83049076Swpaul	sc = device_get_softc(dev);
83149076Swpaul	ifp = &sc->arpcom.ac_if;
83249076Swpaul
83349076Swpaul	if_detach(ifp);
83449076Swpaul	sf_stop(sc);
83549076Swpaul
83650675Swpaul	bus_generic_detach(dev);
83750675Swpaul	device_delete_child(dev, sc->sf_miibus);
83850675Swpaul
83949076Swpaul	bus_teardown_intr(dev, sc->sf_irq, sc->sf_intrhand);
84049076Swpaul	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sf_irq);
84149076Swpaul	bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res);
84249076Swpaul
84349076Swpaul	free(sc->sf_ldata, M_DEVBUF);
84449076Swpaul
84549076Swpaul	splx(s);
84649076Swpaul
84749076Swpaul	return(0);
84849076Swpaul}
84949076Swpaul
85049076Swpaulstatic int sf_init_rx_ring(sc)
85149076Swpaul	struct sf_softc		*sc;
85249076Swpaul{
85349076Swpaul	struct sf_list_data	*ld;
85449076Swpaul	int			i;
85549076Swpaul
85649076Swpaul	ld = sc->sf_ldata;
85749076Swpaul
85849076Swpaul	bzero((char *)ld->sf_rx_dlist_big,
85949076Swpaul	    sizeof(struct sf_rx_bufdesc_type0) * SF_RX_DLIST_CNT);
86049076Swpaul	bzero((char *)ld->sf_rx_clist,
86149076Swpaul	    sizeof(struct sf_rx_cmpdesc_type3) * SF_RX_CLIST_CNT);
86249076Swpaul
86349076Swpaul	for (i = 0; i < SF_RX_DLIST_CNT; i++) {
86449076Swpaul		if (sf_newbuf(sc, &ld->sf_rx_dlist_big[i], NULL) == ENOBUFS)
86549076Swpaul			return(ENOBUFS);
86649076Swpaul	}
86749076Swpaul
86849076Swpaul	return(0);
86949076Swpaul}
87049076Swpaul
87149076Swpaulstatic void sf_init_tx_ring(sc)
87249076Swpaul	struct sf_softc		*sc;
87349076Swpaul{
87449076Swpaul	struct sf_list_data	*ld;
87549076Swpaul	int			i;
87649076Swpaul
87749076Swpaul	ld = sc->sf_ldata;
87849076Swpaul
87949076Swpaul	bzero((char *)ld->sf_tx_dlist,
88049076Swpaul	    sizeof(struct sf_tx_bufdesc_type0) * SF_TX_DLIST_CNT);
88149076Swpaul	bzero((char *)ld->sf_tx_clist,
88249076Swpaul	    sizeof(struct sf_tx_cmpdesc_type0) * SF_TX_CLIST_CNT);
88349076Swpaul
88449076Swpaul	for (i = 0; i < SF_TX_DLIST_CNT; i++)
88549076Swpaul		ld->sf_tx_dlist[i].sf_id = SF_TX_BUFDESC_ID;
88649076Swpaul	for (i = 0; i < SF_TX_CLIST_CNT; i++)
88749076Swpaul		ld->sf_tx_clist[i].sf_type = SF_TXCMPTYPE_TX;
88849076Swpaul
88949076Swpaul	ld->sf_tx_dlist[SF_TX_DLIST_CNT - 1].sf_end = 1;
89049076Swpaul	sc->sf_tx_cnt = 0;
89149076Swpaul
89249076Swpaul	return;
89349076Swpaul}
89449076Swpaul
89549076Swpaulstatic int sf_newbuf(sc, c, m)
89649076Swpaul	struct sf_softc		*sc;
89749076Swpaul	struct sf_rx_bufdesc_type0	*c;
89849076Swpaul	struct mbuf		*m;
89949076Swpaul{
90049076Swpaul	struct mbuf		*m_new = NULL;
90149076Swpaul
90249076Swpaul	if (m == NULL) {
90349076Swpaul		MGETHDR(m_new, M_DONTWAIT, MT_DATA);
90449076Swpaul		if (m_new == NULL) {
90549076Swpaul			printf("sf%d: no memory for rx list -- "
90649076Swpaul			    "packet dropped!\n", sc->sf_unit);
90749076Swpaul			return(ENOBUFS);
90849076Swpaul		}
90949076Swpaul
91049076Swpaul		MCLGET(m_new, M_DONTWAIT);
91149076Swpaul		if (!(m_new->m_flags & M_EXT)) {
91249076Swpaul			printf("sf%d: no memory for rx list -- "
91349076Swpaul			    "packet dropped!\n", sc->sf_unit);
91449076Swpaul			m_freem(m_new);
91549076Swpaul			return(ENOBUFS);
91649076Swpaul		}
91749076Swpaul		m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
91849076Swpaul	} else {
91949076Swpaul		m_new = m;
92049076Swpaul		m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
92149076Swpaul		m_new->m_data = m_new->m_ext.ext_buf;
92249076Swpaul	}
92349076Swpaul
92449076Swpaul	m_adj(m_new, sizeof(u_int64_t));
92549076Swpaul
92649076Swpaul	c->sf_mbuf = m_new;
92749076Swpaul	c->sf_addrlo = SF_RX_HOSTADDR(vtophys(mtod(m_new, caddr_t)));
92849076Swpaul	c->sf_valid = 1;
92949076Swpaul
93049076Swpaul	return(0);
93149076Swpaul}
93249076Swpaul
93349076Swpaul/*
93449076Swpaul * The starfire is programmed to use 'normal' mode for packet reception,
93549076Swpaul * which means we use the consumer/producer model for both the buffer
93649076Swpaul * descriptor queue and the completion descriptor queue. The only problem
93749076Swpaul * with this is that it involves a lot of register accesses: we have to
93849076Swpaul * read the RX completion consumer and producer indexes and the RX buffer
93949076Swpaul * producer index, plus the RX completion consumer and RX buffer producer
94049076Swpaul * indexes have to be updated. It would have been easier if Adaptec had
94149076Swpaul * put each index in a separate register, especially given that the damn
94249076Swpaul * NIC has a 512K register space.
94349076Swpaul *
94449076Swpaul * In spite of all the lovely features that Adaptec crammed into the 6915,
94549076Swpaul * it is marred by one truly stupid design flaw, which is that receive
94649076Swpaul * buffer addresses must be aligned on a longword boundary. This forces
94749076Swpaul * the packet payload to be unaligned, which is suboptimal on the x86 and
94849076Swpaul * completely unuseable on the Alpha. Our only recourse is to copy received
94949076Swpaul * packets into properly aligned buffers before handing them off.
95049076Swpaul */
95149076Swpaul
95249076Swpaulstatic void sf_rxeof(sc)
95349076Swpaul	struct sf_softc		*sc;
95449076Swpaul{
95549076Swpaul	struct ether_header	*eh;
95649076Swpaul	struct mbuf		*m;
95749076Swpaul	struct ifnet		*ifp;
95849076Swpaul	struct sf_rx_bufdesc_type0	*desc;
95949076Swpaul	struct sf_rx_cmpdesc_type3	*cur_rx;
96049076Swpaul	u_int32_t		rxcons, rxprod;
96149076Swpaul	int			cmpprodidx, cmpconsidx, bufprodidx;
96249076Swpaul
96349076Swpaul	ifp = &sc->arpcom.ac_if;
96449076Swpaul
96549076Swpaul	rxcons = csr_read_4(sc, SF_CQ_CONSIDX);
96649076Swpaul	rxprod = csr_read_4(sc, SF_RXDQ_PTR_Q1);
96749076Swpaul	cmpprodidx = SF_IDX_LO(csr_read_4(sc, SF_CQ_PRODIDX));
96849076Swpaul	cmpconsidx = SF_IDX_LO(rxcons);
96949076Swpaul	bufprodidx = SF_IDX_LO(rxprod);
97049076Swpaul
97149076Swpaul	while (cmpconsidx != cmpprodidx) {
97249076Swpaul		struct mbuf		*m0;
97349076Swpaul
97449076Swpaul		cur_rx = &sc->sf_ldata->sf_rx_clist[cmpconsidx];
97549076Swpaul		desc = &sc->sf_ldata->sf_rx_dlist_big[cur_rx->sf_endidx];
97649076Swpaul		m = desc->sf_mbuf;
97749076Swpaul		SF_INC(cmpconsidx, SF_RX_CLIST_CNT);
97849076Swpaul		SF_INC(bufprodidx, SF_RX_DLIST_CNT);
97949076Swpaul
98049076Swpaul		if (!(cur_rx->sf_status1 & SF_RXSTAT1_OK)) {
98149076Swpaul			ifp->if_ierrors++;
98249076Swpaul			sf_newbuf(sc, desc, m);
98349076Swpaul			continue;
98449076Swpaul		}
98549076Swpaul
98649076Swpaul		m0 = m_devget(mtod(m, char *) - ETHER_ALIGN,
98749076Swpaul		    cur_rx->sf_len + ETHER_ALIGN, 0, ifp, NULL);
98849076Swpaul		sf_newbuf(sc, desc, m);
98949076Swpaul		if (m0 == NULL) {
99049076Swpaul			ifp->if_ierrors++;
99149076Swpaul			continue;
99249076Swpaul		}
99349076Swpaul		m_adj(m0, ETHER_ALIGN);
99449076Swpaul		m = m0;
99549076Swpaul
99649076Swpaul		eh = mtod(m, struct ether_header *);
99749076Swpaul		ifp->if_ipackets++;
99849076Swpaul
99949076Swpaul		if (ifp->if_bpf) {
100049076Swpaul			bpf_mtap(ifp, m);
100149076Swpaul			if (ifp->if_flags & IFF_PROMISC &&
100249076Swpaul			    (bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr,
100349076Swpaul			    ETHER_ADDR_LEN) && !(eh->ether_dhost[0] & 1))) {
100449076Swpaul				m_freem(m);
100549076Swpaul				continue;
100649076Swpaul			}
100749076Swpaul		}
100849076Swpaul
100949076Swpaul		/* Remove header from mbuf and pass it on. */
101049076Swpaul		m_adj(m, sizeof(struct ether_header));
101149076Swpaul		ether_input(ifp, eh, m);
101249076Swpaul
101349076Swpaul	}
101449076Swpaul
101549076Swpaul	csr_write_4(sc, SF_CQ_CONSIDX,
101649076Swpaul	    (rxcons & ~SF_CQ_CONSIDX_RXQ1) | cmpconsidx);
101749076Swpaul	csr_write_4(sc, SF_RXDQ_PTR_Q1,
101849076Swpaul	    (rxprod & ~SF_RXDQ_PRODIDX) | bufprodidx);
101949076Swpaul
102049076Swpaul	return;
102149076Swpaul}
102249076Swpaul
102349076Swpaul/*
102449076Swpaul * Read the transmit status from the completion queue and release
102549076Swpaul * mbufs. Note that the buffer descriptor index in the completion
102649076Swpaul * descriptor is an offset from the start of the transmit buffer
102749076Swpaul * descriptor list in bytes. This is important because the manual
102849076Swpaul * gives the impression that it should match the producer/consumer
102949076Swpaul * index, which is the offset in 8 byte blocks.
103049076Swpaul */
103149076Swpaulstatic void sf_txeof(sc)
103249076Swpaul	struct sf_softc		*sc;
103349076Swpaul{
103449076Swpaul	int			txcons, cmpprodidx, cmpconsidx;
103549076Swpaul	struct sf_tx_cmpdesc_type1 *cur_cmp;
103649076Swpaul	struct sf_tx_bufdesc_type0 *cur_tx;
103749076Swpaul	struct ifnet		*ifp;
103849076Swpaul
103949076Swpaul	ifp = &sc->arpcom.ac_if;
104049076Swpaul
104149076Swpaul	txcons = csr_read_4(sc, SF_CQ_CONSIDX);
104249076Swpaul	cmpprodidx = SF_IDX_HI(csr_read_4(sc, SF_CQ_PRODIDX));
104349076Swpaul	cmpconsidx = SF_IDX_HI(txcons);
104449076Swpaul
104549076Swpaul	while (cmpconsidx != cmpprodidx) {
104649076Swpaul		cur_cmp = &sc->sf_ldata->sf_tx_clist[cmpconsidx];
104749076Swpaul		cur_tx = &sc->sf_ldata->sf_tx_dlist[cur_cmp->sf_index >> 7];
104849076Swpaul		SF_INC(cmpconsidx, SF_TX_CLIST_CNT);
104949076Swpaul
105049076Swpaul		if (cur_cmp->sf_txstat & SF_TXSTAT_TX_OK)
105149076Swpaul			ifp->if_opackets++;
105249076Swpaul		else
105349076Swpaul			ifp->if_oerrors++;
105449076Swpaul
105549076Swpaul		sc->sf_tx_cnt--;
105649076Swpaul		if (cur_tx->sf_mbuf != NULL) {
105749076Swpaul			m_freem(cur_tx->sf_mbuf);
105849076Swpaul			cur_tx->sf_mbuf = NULL;
105949076Swpaul		}
106049076Swpaul	}
106149076Swpaul
106249076Swpaul	ifp->if_timer = 0;
106349076Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
106449076Swpaul
106549076Swpaul	csr_write_4(sc, SF_CQ_CONSIDX,
106649076Swpaul	    (txcons & ~SF_CQ_CONSIDX_TXQ) |
106749076Swpaul	    ((cmpconsidx << 16) & 0xFFFF0000));
106849076Swpaul
106949076Swpaul	return;
107049076Swpaul}
107149076Swpaul
107249076Swpaulstatic void sf_intr(arg)
107349076Swpaul	void			*arg;
107449076Swpaul{
107549076Swpaul	struct sf_softc		*sc;
107649076Swpaul	struct ifnet		*ifp;
107749076Swpaul	u_int32_t		status;
107849076Swpaul
107949076Swpaul	sc = arg;
108049076Swpaul	ifp = &sc->arpcom.ac_if;
108149076Swpaul
108249076Swpaul	if (!(csr_read_4(sc, SF_ISR_SHADOW) & SF_ISR_PCIINT_ASSERTED))
108349076Swpaul		return;
108449076Swpaul
108549076Swpaul	/* Disable interrupts. */
108649076Swpaul	csr_write_4(sc, SF_IMR, 0x00000000);
108749076Swpaul
108849076Swpaul	for (;;) {
108949076Swpaul		status = csr_read_4(sc, SF_ISR);
109049076Swpaul		if (status)
109149076Swpaul			csr_write_4(sc, SF_ISR, status);
109249076Swpaul
109349076Swpaul		if (!(status & SF_INTRS))
109449076Swpaul			break;
109549076Swpaul
109649076Swpaul		if (status & SF_ISR_RXDQ1_DMADONE)
109749076Swpaul			sf_rxeof(sc);
109849076Swpaul
109949076Swpaul		if (status & SF_ISR_TX_TXDONE)
110049076Swpaul			sf_txeof(sc);
110149076Swpaul
110249076Swpaul		if (status & SF_ISR_ABNORMALINTR) {
110349076Swpaul			if (status & SF_ISR_STATSOFLOW) {
110449076Swpaul				untimeout(sf_stats_update, sc,
110549076Swpaul				    sc->sf_stat_ch);
110649076Swpaul				sf_stats_update(sc);
110749076Swpaul			} else
110849076Swpaul				sf_init(sc);
110949076Swpaul		}
111049076Swpaul	}
111149076Swpaul
111249076Swpaul	/* Re-enable interrupts. */
111349076Swpaul	csr_write_4(sc, SF_IMR, SF_INTRS);
111449076Swpaul
111549076Swpaul	if (ifp->if_snd.ifq_head != NULL)
111649076Swpaul		sf_start(ifp);
111749076Swpaul
111849076Swpaul	return;
111949076Swpaul}
112049076Swpaul
112149076Swpaulstatic void sf_init(xsc)
112249076Swpaul	void			*xsc;
112349076Swpaul{
112449076Swpaul	struct sf_softc		*sc;
112549076Swpaul	struct ifnet		*ifp;
112650675Swpaul	struct mii_data		*mii;
112749076Swpaul	int			i, s;
112849076Swpaul
112949076Swpaul	s = splimp();
113049076Swpaul
113149076Swpaul	sc = xsc;
113249076Swpaul	ifp = &sc->arpcom.ac_if;
113350675Swpaul	mii = device_get_softc(sc->sf_miibus);
113449076Swpaul
113549076Swpaul	sf_stop(sc);
113649076Swpaul	sf_reset(sc);
113749076Swpaul
113849076Swpaul	/* Init all the receive filter registers */
113949076Swpaul	for (i = SF_RXFILT_PERFECT_BASE;
114049076Swpaul	    i < (SF_RXFILT_HASH_MAX + 1); i += 4)
114149076Swpaul		csr_write_4(sc, i, 0);
114249076Swpaul
114349076Swpaul	/* Empty stats counter registers. */
114449076Swpaul	for (i = 0; i < sizeof(struct sf_stats)/sizeof(u_int32_t); i++)
114549076Swpaul		csr_write_4(sc, SF_STATS_BASE +
114649076Swpaul		    (i + sizeof(u_int32_t)), 0);
114749076Swpaul
114849076Swpaul	/* Init our MAC address */
114949076Swpaul	csr_write_4(sc, SF_PAR0, *(u_int32_t *)(&sc->arpcom.ac_enaddr[0]));
115049076Swpaul	csr_write_4(sc, SF_PAR1, *(u_int32_t *)(&sc->arpcom.ac_enaddr[4]));
115149076Swpaul	sf_setperf(sc, 0, (caddr_t)&sc->arpcom.ac_enaddr);
115249076Swpaul
115349076Swpaul	if (sf_init_rx_ring(sc) == ENOBUFS) {
115449076Swpaul		printf("sf%d: initialization failed: no "
115549076Swpaul		    "memory for rx buffers\n", sc->sf_unit);
115649076Swpaul		(void)splx(s);
115749076Swpaul		return;
115849076Swpaul	}
115949076Swpaul
116049076Swpaul	sf_init_tx_ring(sc);
116149076Swpaul
116249076Swpaul	csr_write_4(sc, SF_RXFILT, SF_PERFMODE_NORMAL|SF_HASHMODE_WITHVLAN);
116349076Swpaul
116449076Swpaul	/* If we want promiscuous mode, set the allframes bit. */
116549076Swpaul	if (ifp->if_flags & IFF_PROMISC) {
116649076Swpaul		SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC);
116749076Swpaul	} else {
116849076Swpaul		SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC);
116949076Swpaul	}
117049076Swpaul
117149076Swpaul	if (ifp->if_flags & IFF_BROADCAST) {
117249076Swpaul		SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_BROAD);
117349076Swpaul	} else {
117449076Swpaul		SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_BROAD);
117549076Swpaul	}
117649076Swpaul
117749076Swpaul	/* Init the completion queue indexes */
117849076Swpaul	csr_write_4(sc, SF_CQ_CONSIDX, 0);
117949076Swpaul	csr_write_4(sc, SF_CQ_PRODIDX, 0);
118049076Swpaul
118149076Swpaul	/* Init the RX completion queue */
118249076Swpaul	csr_write_4(sc, SF_RXCQ_CTL_1,
118349076Swpaul	    vtophys(sc->sf_ldata->sf_rx_clist) & SF_RXCQ_ADDR);
118449076Swpaul	SF_SETBIT(sc, SF_RXCQ_CTL_1, SF_RXCQTYPE_3);
118549076Swpaul
118649076Swpaul	/* Init RX DMA control. */
118749076Swpaul	SF_SETBIT(sc, SF_RXDMA_CTL, SF_RXDMA_REPORTBADPKTS);
118849076Swpaul
118949076Swpaul	/* Init the RX buffer descriptor queue. */
119049076Swpaul	csr_write_4(sc, SF_RXDQ_ADDR_Q1,
119149076Swpaul	    vtophys(sc->sf_ldata->sf_rx_dlist_big));
119249076Swpaul	csr_write_4(sc, SF_RXDQ_CTL_1, (MCLBYTES << 16) | SF_DESCSPACE_16BYTES);
119349076Swpaul	csr_write_4(sc, SF_RXDQ_PTR_Q1, SF_RX_DLIST_CNT - 1);
119449076Swpaul
119549076Swpaul	/* Init the TX completion queue */
119649076Swpaul	csr_write_4(sc, SF_TXCQ_CTL,
119749076Swpaul	    vtophys(sc->sf_ldata->sf_tx_clist) & SF_RXCQ_ADDR);
119849076Swpaul
119949076Swpaul	/* Init the TX buffer descriptor queue. */
120049076Swpaul	csr_write_4(sc, SF_TXDQ_ADDR_HIPRIO,
120149076Swpaul		vtophys(sc->sf_ldata->sf_tx_dlist));
120249076Swpaul	SF_SETBIT(sc, SF_TX_FRAMCTL, SF_TXFRMCTL_CPLAFTERTX);
120349076Swpaul	csr_write_4(sc, SF_TXDQ_CTL,
120449076Swpaul	    SF_TXBUFDESC_TYPE0|SF_TXMINSPACE_128BYTES|SF_TXSKIPLEN_8BYTES);
120549076Swpaul	SF_SETBIT(sc, SF_TXDQ_CTL, SF_TXDQCTL_NODMACMP);
120649076Swpaul
120749076Swpaul	/* Enable autopadding of short TX frames. */
120849076Swpaul	SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_AUTOPAD);
120949076Swpaul
121049076Swpaul	/* Make sure the duplex mode is set correctly. */
121150675Swpaul	if ((mii->mii_media.ifm_media & IFM_GMASK) == IFM_FDX) {
121249076Swpaul		SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_FULLDUPLEX);
121349076Swpaul	} else {
121449076Swpaul		SF_CLRBIT(sc, SF_MACCFG_1, SF_MACCFG1_FULLDUPLEX);
121549076Swpaul	}
121649076Swpaul
121749076Swpaul	/* Enable interrupts. */
121849076Swpaul	csr_write_4(sc, SF_IMR, SF_INTRS);
121949076Swpaul	SF_SETBIT(sc, SF_PCI_DEVCFG, SF_PCIDEVCFG_INTR_ENB);
122049076Swpaul
122149076Swpaul	/* Enable the RX and TX engines. */
122249076Swpaul	SF_SETBIT(sc, SF_GEN_ETH_CTL, SF_ETHCTL_RX_ENB|SF_ETHCTL_RXDMA_ENB);
122349076Swpaul	SF_SETBIT(sc, SF_GEN_ETH_CTL, SF_ETHCTL_TX_ENB|SF_ETHCTL_TXDMA_ENB);
122449076Swpaul
122550675Swpaul	mii_mediachg(mii);
122650675Swpaul
122749076Swpaul	ifp->if_flags |= IFF_RUNNING;
122849076Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
122949076Swpaul
123049076Swpaul	sc->sf_stat_ch = timeout(sf_stats_update, sc, hz);
123149076Swpaul
123249076Swpaul	splx(s);
123349076Swpaul
123449076Swpaul	return;
123549076Swpaul}
123649076Swpaul
123749076Swpaulstatic int sf_encap(sc, c, m_head)
123849076Swpaul	struct sf_softc		*sc;
123949076Swpaul	struct sf_tx_bufdesc_type0 *c;
124049076Swpaul	struct mbuf		*m_head;
124149076Swpaul{
124249076Swpaul	int			frag = 0;
124349076Swpaul	struct sf_frag		*f = NULL;
124449076Swpaul	struct mbuf		*m;
124549076Swpaul
124649076Swpaul	m = m_head;
124749076Swpaul
124849076Swpaul	for (m = m_head, frag = 0; m != NULL; m = m->m_next) {
124949076Swpaul		if (m->m_len != 0) {
125049076Swpaul			if (frag == SF_MAXFRAGS)
125149076Swpaul				break;
125249076Swpaul			f = &c->sf_frags[frag];
125349076Swpaul			if (frag == 0)
125449076Swpaul				f->sf_pktlen = m_head->m_pkthdr.len;
125549076Swpaul			f->sf_fraglen = m->m_len;
125649076Swpaul			f->sf_addr = vtophys(mtod(m, vm_offset_t));
125749076Swpaul			frag++;
125849076Swpaul		}
125949076Swpaul	}
126049076Swpaul
126149076Swpaul	if (m != NULL) {
126249076Swpaul		struct mbuf		*m_new = NULL;
126349076Swpaul
126449076Swpaul		MGETHDR(m_new, M_DONTWAIT, MT_DATA);
126549076Swpaul		if (m_new == NULL) {
126649076Swpaul			printf("sf%d: no memory for tx list", sc->sf_unit);
126749076Swpaul			return(1);
126849076Swpaul		}
126949076Swpaul
127049076Swpaul		if (m_head->m_pkthdr.len > MHLEN) {
127149076Swpaul			MCLGET(m_new, M_DONTWAIT);
127249076Swpaul			if (!(m_new->m_flags & M_EXT)) {
127349076Swpaul				m_freem(m_new);
127449076Swpaul				printf("sf%d: no memory for tx list",
127549076Swpaul				    sc->sf_unit);
127649076Swpaul				return(1);
127749076Swpaul			}
127849076Swpaul		}
127949076Swpaul		m_copydata(m_head, 0, m_head->m_pkthdr.len,
128049076Swpaul		    mtod(m_new, caddr_t));
128149076Swpaul		m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len;
128249076Swpaul		m_freem(m_head);
128349076Swpaul		m_head = m_new;
128449076Swpaul		f = &c->sf_frags[0];
128549076Swpaul		f->sf_fraglen = f->sf_pktlen = m_head->m_pkthdr.len;
128649076Swpaul		f->sf_addr = vtophys(mtod(m_head, caddr_t));
128749076Swpaul		frag = 1;
128849076Swpaul	}
128949076Swpaul
129049076Swpaul	c->sf_mbuf = m_head;
129149076Swpaul	c->sf_id = SF_TX_BUFDESC_ID;
129249076Swpaul	c->sf_fragcnt = frag;
129349076Swpaul	c->sf_intr = 1;
129449076Swpaul	c->sf_caltcp = 0;
129549076Swpaul	c->sf_crcen = 1;
129649076Swpaul
129749076Swpaul	return(0);
129849076Swpaul}
129949076Swpaul
130049076Swpaulstatic void sf_start(ifp)
130149076Swpaul	struct ifnet		*ifp;
130249076Swpaul{
130349076Swpaul	struct sf_softc		*sc;
130449076Swpaul	struct sf_tx_bufdesc_type0 *cur_tx = NULL;
130549076Swpaul	struct mbuf		*m_head = NULL;
130649076Swpaul	int			i, txprod;
130749076Swpaul
130849076Swpaul	sc = ifp->if_softc;
130949076Swpaul
131049076Swpaul	if (ifp->if_flags & IFF_OACTIVE)
131149076Swpaul		return;
131249076Swpaul
131349076Swpaul	txprod = csr_read_4(sc, SF_TXDQ_PRODIDX);
131449076Swpaul	i = SF_IDX_HI(txprod) >> 4;
131549076Swpaul
131649076Swpaul	while(sc->sf_ldata->sf_tx_dlist[i].sf_mbuf == NULL) {
131749076Swpaul		IF_DEQUEUE(&ifp->if_snd, m_head);
131849076Swpaul		if (m_head == NULL)
131949076Swpaul			break;
132049076Swpaul
132149076Swpaul		cur_tx = &sc->sf_ldata->sf_tx_dlist[i];
132249076Swpaul		sf_encap(sc, cur_tx, m_head);
132349076Swpaul
132449076Swpaul		/*
132549076Swpaul		 * If there's a BPF listener, bounce a copy of this frame
132649076Swpaul		 * to him.
132749076Swpaul		 */
132849076Swpaul		if (ifp->if_bpf)
132949076Swpaul			bpf_mtap(ifp, m_head);
133051583Swpaul
133149076Swpaul		SF_INC(i, SF_TX_DLIST_CNT);
133249076Swpaul		sc->sf_tx_cnt++;
133349076Swpaul		if (sc->sf_tx_cnt == (SF_TX_DLIST_CNT - 2))
133449076Swpaul			break;
133549076Swpaul	}
133649076Swpaul
133749076Swpaul	if (cur_tx == NULL)
133849076Swpaul		return;
133949076Swpaul
134049076Swpaul	/* Transmit */
134149076Swpaul	csr_write_4(sc, SF_TXDQ_PRODIDX,
134249076Swpaul	    (txprod & ~SF_TXDQ_PRODIDX_HIPRIO) |
134349076Swpaul	    ((i << 20) & 0xFFFF0000));
134449076Swpaul
134549076Swpaul	ifp->if_timer = 5;
134649076Swpaul
134749076Swpaul	return;
134849076Swpaul}
134949076Swpaul
135049076Swpaulstatic void sf_stop(sc)
135149076Swpaul	struct sf_softc		*sc;
135249076Swpaul{
135349076Swpaul	int			i;
135449077Swpaul	struct ifnet		*ifp;
135549076Swpaul
135649077Swpaul	ifp = &sc->arpcom.ac_if;
135749077Swpaul
135849076Swpaul	untimeout(sf_stats_update, sc, sc->sf_stat_ch);
135949076Swpaul
136049076Swpaul	csr_write_4(sc, SF_GEN_ETH_CTL, 0);
136149076Swpaul	csr_write_4(sc, SF_CQ_CONSIDX, 0);
136249076Swpaul	csr_write_4(sc, SF_CQ_PRODIDX, 0);
136349076Swpaul	csr_write_4(sc, SF_RXDQ_ADDR_Q1, 0);
136449076Swpaul	csr_write_4(sc, SF_RXDQ_CTL_1, 0);
136549076Swpaul	csr_write_4(sc, SF_RXDQ_PTR_Q1, 0);
136649076Swpaul	csr_write_4(sc, SF_TXCQ_CTL, 0);
136749076Swpaul	csr_write_4(sc, SF_TXDQ_ADDR_HIPRIO, 0);
136849076Swpaul	csr_write_4(sc, SF_TXDQ_CTL, 0);
136949076Swpaul	sf_reset(sc);
137049076Swpaul
137149076Swpaul	for (i = 0; i < SF_RX_DLIST_CNT; i++) {
137249076Swpaul		if (sc->sf_ldata->sf_rx_dlist_big[i].sf_mbuf != NULL) {
137349076Swpaul			m_freem(sc->sf_ldata->sf_rx_dlist_big[i].sf_mbuf);
137449076Swpaul			sc->sf_ldata->sf_rx_dlist_big[i].sf_mbuf = NULL;
137549076Swpaul		}
137649076Swpaul	}
137749076Swpaul
137849076Swpaul	for (i = 0; i < SF_TX_DLIST_CNT; i++) {
137949076Swpaul		if (sc->sf_ldata->sf_tx_dlist[i].sf_mbuf != NULL) {
138049076Swpaul			m_freem(sc->sf_ldata->sf_tx_dlist[i].sf_mbuf);
138149076Swpaul			sc->sf_ldata->sf_tx_dlist[i].sf_mbuf = NULL;
138249076Swpaul		}
138349076Swpaul	}
138449076Swpaul
138549077Swpaul	ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
138649077Swpaul
138749076Swpaul	return;
138849076Swpaul}
138949076Swpaul
139049076Swpaul/*
139149076Swpaul * Note: it is important that this function not be interrupted. We
139249076Swpaul * use a two-stage register access scheme: if we are interrupted in
139349076Swpaul * between setting the indirect address register and reading from the
139449076Swpaul * indirect data register, the contents of the address register could
139549076Swpaul * be changed out from under us.
139649076Swpaul */
139749076Swpaulstatic void sf_stats_update(xsc)
139849076Swpaul	void			*xsc;
139949076Swpaul{
140049076Swpaul	struct sf_softc		*sc;
140149076Swpaul	struct ifnet		*ifp;
140250675Swpaul	struct mii_data		*mii;
140349076Swpaul	struct sf_stats		stats;
140449076Swpaul	u_int32_t		*ptr;
140549076Swpaul	int			i, s;
140649076Swpaul
140749076Swpaul	s = splimp();
140849076Swpaul
140949076Swpaul	sc = xsc;
141049076Swpaul	ifp = &sc->arpcom.ac_if;
141150675Swpaul	mii = device_get_softc(sc->sf_miibus);
141249076Swpaul
141349076Swpaul	ptr = (u_int32_t *)&stats;
141449076Swpaul	for (i = 0; i < sizeof(stats)/sizeof(u_int32_t); i++)
141549076Swpaul		ptr[i] = csr_read_4(sc, SF_STATS_BASE +
141649076Swpaul		    (i + sizeof(u_int32_t)));
141749076Swpaul
141849076Swpaul	for (i = 0; i < sizeof(stats)/sizeof(u_int32_t); i++)
141949076Swpaul		csr_write_4(sc, SF_STATS_BASE +
142049076Swpaul		    (i + sizeof(u_int32_t)), 0);
142149076Swpaul
142249076Swpaul	ifp->if_collisions += stats.sf_tx_single_colls +
142349076Swpaul	    stats.sf_tx_multi_colls + stats.sf_tx_excess_colls;
142449076Swpaul
142550675Swpaul	mii_tick(mii);
142650675Swpaul
142749076Swpaul	sc->sf_stat_ch = timeout(sf_stats_update, sc, hz);
142849076Swpaul
142949076Swpaul	splx(s);
143049076Swpaul
143149076Swpaul	return;
143249076Swpaul}
143349076Swpaul
143449076Swpaulstatic void sf_watchdog(ifp)
143549076Swpaul	struct ifnet		*ifp;
143649076Swpaul{
143749076Swpaul	struct sf_softc		*sc;
143849076Swpaul
143949076Swpaul	sc = ifp->if_softc;
144049076Swpaul
144149076Swpaul	ifp->if_oerrors++;
144249076Swpaul	printf("sf%d: watchdog timeout\n", sc->sf_unit);
144349076Swpaul
144449076Swpaul	sf_stop(sc);
144549076Swpaul	sf_reset(sc);
144649076Swpaul	sf_init(sc);
144749076Swpaul
144849076Swpaul	if (ifp->if_snd.ifq_head != NULL)
144949076Swpaul		sf_start(ifp);
145049076Swpaul
145149076Swpaul	return;
145249076Swpaul}
145349076Swpaul
145449076Swpaulstatic void sf_shutdown(dev)
145549076Swpaul	device_t		dev;
145649076Swpaul{
145749076Swpaul	struct sf_softc		*sc;
145849076Swpaul
145949076Swpaul	sc = device_get_softc(dev);
146049076Swpaul
146149076Swpaul	sf_stop(sc);
146249076Swpaul
146349076Swpaul	return;
146449076Swpaul}
1465