if_sf.c revision 72012
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 72012 2001-02-04 16:08:18Z phk $
3349076Swpaul */
3449076Swpaul
3549076Swpaul/*
3649076Swpaul * Adaptec AIC-6915 "Starfire" PCI fast ethernet driver for FreeBSD.
3751682Swpaul * Programming manual is available from:
3851682Swpaul * ftp.adaptec.com:/pub/BBS/userguides/aic6915_pg.pdf.
3949076Swpaul *
4049076Swpaul * Written by Bill Paul <wpaul@ctr.columbia.edu>
4149076Swpaul * Department of Electical Engineering
4249076Swpaul * Columbia University, New York City
4349076Swpaul */
4449076Swpaul
4549076Swpaul/*
4649076Swpaul * The Adaptec AIC-6915 "Starfire" is a 64-bit 10/100 PCI ethernet
4749076Swpaul * controller designed with flexibility and reducing CPU load in mind.
4849076Swpaul * The Starfire offers high and low priority buffer queues, a
4949076Swpaul * producer/consumer index mechanism and several different buffer
5049076Swpaul * queue and completion queue descriptor types. Any one of a number
5149076Swpaul * of different driver designs can be used, depending on system and
5249076Swpaul * OS requirements. This driver makes use of type0 transmit frame
5349076Swpaul * descriptors (since BSD fragments packets across an mbuf chain)
5449076Swpaul * and two RX buffer queues prioritized on size (one queue for small
5549076Swpaul * frames that will fit into a single mbuf, another with full size
5649076Swpaul * mbuf clusters for everything else). The producer/consumer indexes
5749076Swpaul * and completion queues are also used.
5849076Swpaul *
5949076Swpaul * One downside to the Starfire has to do with alignment: buffer
6049076Swpaul * queues must be aligned on 256-byte boundaries, and receive buffers
6149076Swpaul * must be aligned on longword boundaries. The receive buffer alignment
6249076Swpaul * causes problems on the Alpha platform, where the packet payload
6349076Swpaul * should be longword aligned. There is no simple way around this.
6449076Swpaul *
6549076Swpaul * For receive filtering, the Starfire offers 16 perfect filter slots
6649076Swpaul * and a 512-bit hash table.
6749076Swpaul *
6849076Swpaul * The Starfire has no internal transceiver, relying instead on an
6949076Swpaul * external MII-based transceiver. Accessing registers on external
7049076Swpaul * PHYs is done through a special register map rather than with the
7149076Swpaul * usual bitbang MDIO method.
7249076Swpaul *
7349076Swpaul * Acesssing the registers on the Starfire is a little tricky. The
7449076Swpaul * Starfire has a 512K internal register space. When programmed for
7549076Swpaul * PCI memory mapped mode, the entire register space can be accessed
7649076Swpaul * directly. However in I/O space mode, only 256 bytes are directly
7749076Swpaul * mapped into PCI I/O space. The other registers can be accessed
7849076Swpaul * indirectly using the SF_INDIRECTIO_ADDR and SF_INDIRECTIO_DATA
7949076Swpaul * registers inside the 256-byte I/O window.
8049076Swpaul */
8149076Swpaul
8249076Swpaul#include <sys/param.h>
8349076Swpaul#include <sys/systm.h>
8449076Swpaul#include <sys/sockio.h>
8549076Swpaul#include <sys/mbuf.h>
8649076Swpaul#include <sys/malloc.h>
8749076Swpaul#include <sys/kernel.h>
8849076Swpaul#include <sys/socket.h>
8949076Swpaul
9049076Swpaul#include <net/if.h>
9149076Swpaul#include <net/if_arp.h>
9249076Swpaul#include <net/ethernet.h>
9349076Swpaul#include <net/if_dl.h>
9449076Swpaul#include <net/if_media.h>
9549076Swpaul
9649076Swpaul#include <net/bpf.h>
9749076Swpaul
9849076Swpaul#include <vm/vm.h>              /* for vtophys */
9949076Swpaul#include <vm/pmap.h>            /* for vtophys */
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
12059758SpeterMODULE_DEPEND(sf, miibus, 1, 1, 1);
12159758Speter
12249076Swpaul#ifndef lint
12349076Swpaulstatic const char rcsid[] =
12450477Speter  "$FreeBSD: head/sys/dev/sf/if_sf.c 72012 2001-02-04 16:08:18Z phk $";
12549076Swpaul#endif
12649076Swpaul
12749076Swpaulstatic struct sf_type sf_devs[] = {
12849076Swpaul	{ AD_VENDORID, AD_DEVICEID_STARFIRE,
12949076Swpaul		"Adaptec AIC-6915 10/100BaseTX" },
13049076Swpaul	{ 0, 0, NULL }
13149076Swpaul};
13249076Swpaul
13349076Swpaulstatic int sf_probe		__P((device_t));
13449076Swpaulstatic int sf_attach		__P((device_t));
13549076Swpaulstatic int sf_detach		__P((device_t));
13649076Swpaulstatic void sf_intr		__P((void *));
13749076Swpaulstatic void sf_stats_update	__P((void *));
13849076Swpaulstatic void sf_rxeof		__P((struct sf_softc *));
13949076Swpaulstatic void sf_txeof		__P((struct sf_softc *));
14049076Swpaulstatic int sf_encap		__P((struct sf_softc *,
14149076Swpaul					struct sf_tx_bufdesc_type0 *,
14249076Swpaul					struct mbuf *));
14349076Swpaulstatic void sf_start		__P((struct ifnet *));
14449076Swpaulstatic int sf_ioctl		__P((struct ifnet *, u_long, caddr_t));
14549076Swpaulstatic void sf_init		__P((void *));
14649076Swpaulstatic void sf_stop		__P((struct sf_softc *));
14749076Swpaulstatic void sf_watchdog		__P((struct ifnet *));
14849076Swpaulstatic void sf_shutdown		__P((device_t));
14949076Swpaulstatic int sf_ifmedia_upd	__P((struct ifnet *));
15049076Swpaulstatic void sf_ifmedia_sts	__P((struct ifnet *, struct ifmediareq *));
15149076Swpaulstatic void sf_reset		__P((struct sf_softc *));
15249076Swpaulstatic int sf_init_rx_ring	__P((struct sf_softc *));
15349076Swpaulstatic void sf_init_tx_ring	__P((struct sf_softc *));
15449076Swpaulstatic int sf_newbuf		__P((struct sf_softc *,
15549076Swpaul					struct sf_rx_bufdesc_type0 *,
15649076Swpaul					struct mbuf *));
15749076Swpaulstatic void sf_setmulti		__P((struct sf_softc *));
15849076Swpaulstatic int sf_setperf		__P((struct sf_softc *, int, caddr_t));
15949076Swpaulstatic int sf_sethash		__P((struct sf_softc *, caddr_t, int));
16049076Swpaul#ifdef notdef
16149076Swpaulstatic int sf_setvlan		__P((struct sf_softc *, int, u_int32_t));
16249076Swpaul#endif
16349076Swpaul
16449076Swpaulstatic u_int8_t sf_read_eeprom	__P((struct sf_softc *, int));
16549076Swpaulstatic u_int32_t sf_calchash	__P((caddr_t));
16649076Swpaul
16750675Swpaulstatic int sf_miibus_readreg	__P((device_t, int, int));
16850675Swpaulstatic int sf_miibus_writereg	__P((device_t, int, int, int));
16950675Swpaulstatic void sf_miibus_statchg	__P((device_t));
17049076Swpaul
17149076Swpaulstatic u_int32_t csr_read_4	__P((struct sf_softc *, int));
17249076Swpaulstatic void csr_write_4		__P((struct sf_softc *, int, u_int32_t));
17349076Swpaul
17449076Swpaul#ifdef SF_USEIOSPACE
17549076Swpaul#define SF_RES			SYS_RES_IOPORT
17649076Swpaul#define SF_RID			SF_PCI_LOIO
17749076Swpaul#else
17849076Swpaul#define SF_RES			SYS_RES_MEMORY
17949076Swpaul#define SF_RID			SF_PCI_LOMEM
18049076Swpaul#endif
18149076Swpaul
18249076Swpaulstatic device_method_t sf_methods[] = {
18349076Swpaul	/* Device interface */
18449076Swpaul	DEVMETHOD(device_probe,		sf_probe),
18549076Swpaul	DEVMETHOD(device_attach,	sf_attach),
18649076Swpaul	DEVMETHOD(device_detach,	sf_detach),
18749076Swpaul	DEVMETHOD(device_shutdown,	sf_shutdown),
18850675Swpaul
18950675Swpaul	/* bus interface */
19050675Swpaul	DEVMETHOD(bus_print_child,	bus_generic_print_child),
19150675Swpaul	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
19250675Swpaul
19350675Swpaul	/* MII interface */
19450675Swpaul	DEVMETHOD(miibus_readreg,	sf_miibus_readreg),
19550675Swpaul	DEVMETHOD(miibus_writereg,	sf_miibus_writereg),
19650675Swpaul	DEVMETHOD(miibus_statchg,	sf_miibus_statchg),
19750675Swpaul
19849076Swpaul	{ 0, 0 }
19949076Swpaul};
20049076Swpaul
20149076Swpaulstatic driver_t sf_driver = {
20251455Swpaul	"sf",
20349076Swpaul	sf_methods,
20449076Swpaul	sizeof(struct sf_softc),
20549076Swpaul};
20649076Swpaul
20749076Swpaulstatic devclass_t sf_devclass;
20849076Swpaul
20951533SwpaulDRIVER_MODULE(if_sf, pci, sf_driver, sf_devclass, 0, 0);
21051473SwpaulDRIVER_MODULE(miibus, sf, miibus_driver, miibus_devclass, 0, 0);
21149076Swpaul
21249076Swpaul#define SF_SETBIT(sc, reg, x)	\
21349076Swpaul	csr_write_4(sc, reg, csr_read_4(sc, reg) | x)
21449076Swpaul
21549076Swpaul#define SF_CLRBIT(sc, reg, x)				\
21649076Swpaul	csr_write_4(sc, reg, csr_read_4(sc, reg) & ~x)
21749076Swpaul
21849076Swpaulstatic u_int32_t csr_read_4(sc, reg)
21949076Swpaul	struct sf_softc		*sc;
22049076Swpaul	int			reg;
22149076Swpaul{
22249076Swpaul	u_int32_t		val;
22349076Swpaul
22449076Swpaul#ifdef SF_USEIOSPACE
22549076Swpaul	CSR_WRITE_4(sc, SF_INDIRECTIO_ADDR, reg + SF_RMAP_INTREG_BASE);
22649076Swpaul	val = CSR_READ_4(sc, SF_INDIRECTIO_DATA);
22749076Swpaul#else
22849076Swpaul	val = CSR_READ_4(sc, (reg + SF_RMAP_INTREG_BASE));
22949076Swpaul#endif
23049076Swpaul
23149076Swpaul	return(val);
23249076Swpaul}
23349076Swpaul
23449076Swpaulstatic u_int8_t sf_read_eeprom(sc, reg)
23549076Swpaul	struct sf_softc		*sc;
23649076Swpaul	int			reg;
23749076Swpaul{
23849076Swpaul	u_int8_t		val;
23949076Swpaul
24049076Swpaul	val = (csr_read_4(sc, SF_EEADDR_BASE +
24149076Swpaul	    (reg & 0xFFFFFFFC)) >> (8 * (reg & 3))) & 0xFF;
24249076Swpaul
24349076Swpaul	return(val);
24449076Swpaul}
24549076Swpaul
24649076Swpaulstatic void csr_write_4(sc, reg, val)
24749076Swpaul	struct sf_softc		*sc;
24849076Swpaul	int			reg;
24949076Swpaul	u_int32_t		val;
25049076Swpaul{
25149076Swpaul#ifdef SF_USEIOSPACE
25249076Swpaul	CSR_WRITE_4(sc, SF_INDIRECTIO_ADDR, reg + SF_RMAP_INTREG_BASE);
25349076Swpaul	CSR_WRITE_4(sc, SF_INDIRECTIO_DATA, val);
25449076Swpaul#else
25549076Swpaul	CSR_WRITE_4(sc, (reg + SF_RMAP_INTREG_BASE), val);
25649076Swpaul#endif
25749076Swpaul	return;
25849076Swpaul}
25949076Swpaul
26049076Swpaulstatic u_int32_t sf_calchash(addr)
26149076Swpaul	caddr_t			addr;
26249076Swpaul{
26349076Swpaul	u_int32_t		crc, carry;
26449076Swpaul	int			i, j;
26549076Swpaul	u_int8_t		c;
26649076Swpaul
26749076Swpaul	/* Compute CRC for the address value. */
26849076Swpaul	crc = 0xFFFFFFFF; /* initial value */
26949076Swpaul
27049076Swpaul	for (i = 0; i < 6; i++) {
27149076Swpaul		c = *(addr + i);
27249076Swpaul		for (j = 0; j < 8; j++) {
27349076Swpaul			carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01);
27449076Swpaul			crc <<= 1;
27549076Swpaul			c >>= 1;
27649076Swpaul			if (carry)
27749076Swpaul				crc = (crc ^ 0x04c11db6) | carry;
27849076Swpaul		}
27949076Swpaul	}
28049076Swpaul
28149076Swpaul	/* return the filter bit position */
28249076Swpaul	return(crc >> 23 & 0x1FF);
28349076Swpaul}
28449076Swpaul
28549076Swpaul/*
28649076Swpaul * Copy the address 'mac' into the perfect RX filter entry at
28749076Swpaul * offset 'idx.' The perfect filter only has 16 entries so do
28849076Swpaul * some sanity tests.
28949076Swpaul */
29049076Swpaulstatic int sf_setperf(sc, idx, mac)
29149076Swpaul	struct sf_softc		*sc;
29249076Swpaul	int			idx;
29349076Swpaul	caddr_t			mac;
29449076Swpaul{
29549076Swpaul	u_int16_t		*p;
29649076Swpaul
29749076Swpaul	if (idx < 0 || idx > SF_RXFILT_PERFECT_CNT)
29849076Swpaul		return(EINVAL);
29949076Swpaul
30049076Swpaul	if (mac == NULL)
30149076Swpaul		return(EINVAL);
30249076Swpaul
30349076Swpaul	p = (u_int16_t *)mac;
30449076Swpaul
30549076Swpaul	csr_write_4(sc, SF_RXFILT_PERFECT_BASE +
30649076Swpaul	    (idx * SF_RXFILT_PERFECT_SKIP), htons(p[2]));
30749076Swpaul	csr_write_4(sc, SF_RXFILT_PERFECT_BASE +
30849076Swpaul	    (idx * SF_RXFILT_PERFECT_SKIP) + 4, htons(p[1]));
30949076Swpaul	csr_write_4(sc, SF_RXFILT_PERFECT_BASE +
31049076Swpaul	    (idx * SF_RXFILT_PERFECT_SKIP) + 8, htons(p[0]));
31149076Swpaul
31249076Swpaul	return(0);
31349076Swpaul}
31449076Swpaul
31549076Swpaul/*
31649076Swpaul * Set the bit in the 512-bit hash table that corresponds to the
31749076Swpaul * specified mac address 'mac.' If 'prio' is nonzero, update the
31849076Swpaul * priority hash table instead of the filter hash table.
31949076Swpaul */
32049076Swpaulstatic int sf_sethash(sc, mac, prio)
32149076Swpaul	struct sf_softc		*sc;
32249076Swpaul	caddr_t			mac;
32349076Swpaul	int			prio;
32449076Swpaul{
32549076Swpaul	u_int32_t		h = 0;
32649076Swpaul
32749076Swpaul	if (mac == NULL)
32849076Swpaul		return(EINVAL);
32949076Swpaul
33049076Swpaul	h = sf_calchash(mac);
33149076Swpaul
33249076Swpaul	if (prio) {
33349076Swpaul		SF_SETBIT(sc, SF_RXFILT_HASH_BASE + SF_RXFILT_HASH_PRIOOFF +
33449076Swpaul		    (SF_RXFILT_HASH_SKIP * (h >> 4)), (1 << (h & 0xF)));
33549076Swpaul	} else {
33649076Swpaul		SF_SETBIT(sc, SF_RXFILT_HASH_BASE + SF_RXFILT_HASH_ADDROFF +
33749076Swpaul		    (SF_RXFILT_HASH_SKIP * (h >> 4)), (1 << (h & 0xF)));
33849076Swpaul	}
33949076Swpaul
34049076Swpaul	return(0);
34149076Swpaul}
34249076Swpaul
34349076Swpaul#ifdef notdef
34449076Swpaul/*
34549076Swpaul * Set a VLAN tag in the receive filter.
34649076Swpaul */
34749076Swpaulstatic int sf_setvlan(sc, idx, vlan)
34849076Swpaul	struct sf_softc		*sc;
34949076Swpaul	int			idx;
35049076Swpaul	u_int32_t		vlan;
35149076Swpaul{
35249076Swpaul	if (idx < 0 || idx >> SF_RXFILT_HASH_CNT)
35349076Swpaul		return(EINVAL);
35449076Swpaul
35549076Swpaul	csr_write_4(sc, SF_RXFILT_HASH_BASE +
35649076Swpaul	    (idx * SF_RXFILT_HASH_SKIP) + SF_RXFILT_HASH_VLANOFF, vlan);
35749076Swpaul
35849076Swpaul	return(0);
35949076Swpaul}
36049076Swpaul#endif
36149076Swpaul
36250675Swpaulstatic int sf_miibus_readreg(dev, phy, reg)
36350675Swpaul	device_t		dev;
36450675Swpaul	int			phy, reg;
36550675Swpaul{
36649076Swpaul	struct sf_softc		*sc;
36749076Swpaul	int			i;
36849076Swpaul	u_int32_t		val = 0;
36949076Swpaul
37050675Swpaul	sc = device_get_softc(dev);
37150675Swpaul
37249076Swpaul	for (i = 0; i < SF_TIMEOUT; i++) {
37350675Swpaul		val = csr_read_4(sc, SF_PHY_REG(phy, reg));
37449076Swpaul		if (val & SF_MII_DATAVALID)
37549076Swpaul			break;
37649076Swpaul	}
37749076Swpaul
37849076Swpaul	if (i == SF_TIMEOUT)
37949076Swpaul		return(0);
38049076Swpaul
38149076Swpaul	if ((val & 0x0000FFFF) == 0xFFFF)
38249076Swpaul		return(0);
38349076Swpaul
38449076Swpaul	return(val & 0x0000FFFF);
38549076Swpaul}
38649076Swpaul
38750675Swpaulstatic int sf_miibus_writereg(dev, phy, reg, val)
38850675Swpaul	device_t		dev;
38950675Swpaul	int			phy, reg, val;
39050675Swpaul{
39149076Swpaul	struct sf_softc		*sc;
39249076Swpaul	int			i;
39349076Swpaul	int			busy;
39449076Swpaul
39550675Swpaul	sc = device_get_softc(dev);
39649076Swpaul
39750675Swpaul	csr_write_4(sc, SF_PHY_REG(phy, reg), val);
39850675Swpaul
39949076Swpaul	for (i = 0; i < SF_TIMEOUT; i++) {
40050675Swpaul		busy = csr_read_4(sc, SF_PHY_REG(phy, reg));
40149076Swpaul		if (!(busy & SF_MII_BUSY))
40249076Swpaul			break;
40349076Swpaul	}
40449076Swpaul
40550675Swpaul	return(0);
40650675Swpaul}
40750675Swpaul
40850675Swpaulstatic void sf_miibus_statchg(dev)
40950675Swpaul	device_t		dev;
41050675Swpaul{
41150675Swpaul	struct sf_softc		*sc;
41250675Swpaul	struct mii_data		*mii;
41350675Swpaul
41450675Swpaul	sc = device_get_softc(dev);
41550675Swpaul	mii = device_get_softc(sc->sf_miibus);
41650675Swpaul
41750675Swpaul	if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) {
41850675Swpaul		SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_FULLDUPLEX);
41954161Swpaul		csr_write_4(sc, SF_BKTOBKIPG, SF_IPGT_FDX);
42050675Swpaul	} else {
42150675Swpaul		SF_CLRBIT(sc, SF_MACCFG_1, SF_MACCFG1_FULLDUPLEX);
42254161Swpaul		csr_write_4(sc, SF_BKTOBKIPG, SF_IPGT_HDX);
42350675Swpaul	}
42450675Swpaul
42549076Swpaul	return;
42649076Swpaul}
42749076Swpaul
42849076Swpaulstatic void sf_setmulti(sc)
42949076Swpaul	struct sf_softc		*sc;
43049076Swpaul{
43149076Swpaul	struct ifnet		*ifp;
43249076Swpaul	int			i;
43349076Swpaul	struct ifmultiaddr	*ifma;
43449076Swpaul	u_int8_t		dummy[] = { 0, 0, 0, 0, 0, 0 };
43549076Swpaul
43649076Swpaul	ifp = &sc->arpcom.ac_if;
43749076Swpaul
43849076Swpaul	/* First zot all the existing filters. */
43949076Swpaul	for (i = 1; i < SF_RXFILT_PERFECT_CNT; i++)
44049076Swpaul		sf_setperf(sc, i, (char *)&dummy);
44149076Swpaul	for (i = SF_RXFILT_HASH_BASE;
44249076Swpaul	    i < (SF_RXFILT_HASH_MAX + 1); i += 4)
44349076Swpaul		csr_write_4(sc, i, 0);
44449076Swpaul	SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_ALLMULTI);
44549076Swpaul
44649076Swpaul	/* Now program new ones. */
44749076Swpaul	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
44849076Swpaul		SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_ALLMULTI);
44949076Swpaul	} else {
45049076Swpaul		i = 1;
45149076Swpaul		/* First find the tail of the list. */
45271962Sphk		LIST_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
45371999Sphk			if (LIST_NEXT(ifma, ifma_link) == NULL)
45449076Swpaul				break;
45549076Swpaul		}
45649076Swpaul		/* Now traverse the list backwards. */
45749076Swpaul		for (; ifma != NULL && ifma != (void *)&ifp->if_multiaddrs;
45849076Swpaul			ifma = (struct ifmultiaddr *)ifma->ifma_link.le_prev) {
45949076Swpaul			if (ifma->ifma_addr->sa_family != AF_LINK)
46049076Swpaul				continue;
46149076Swpaul			/*
46249076Swpaul			 * Program the first 15 multicast groups
46349076Swpaul			 * into the perfect filter. For all others,
46449076Swpaul			 * use the hash table.
46549076Swpaul			 */
46649076Swpaul			if (i < SF_RXFILT_PERFECT_CNT) {
46749076Swpaul				sf_setperf(sc, i,
46849076Swpaul			LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
46949076Swpaul				i++;
47049076Swpaul				continue;
47149076Swpaul			}
47249076Swpaul
47349076Swpaul			sf_sethash(sc,
47449076Swpaul			    LLADDR((struct sockaddr_dl *)ifma->ifma_addr), 0);
47549076Swpaul		}
47649076Swpaul	}
47749076Swpaul
47849076Swpaul	return;
47949076Swpaul}
48049076Swpaul
48149076Swpaul/*
48249076Swpaul * Set media options.
48349076Swpaul */
48449076Swpaulstatic int sf_ifmedia_upd(ifp)
48549076Swpaul	struct ifnet		*ifp;
48649076Swpaul{
48749076Swpaul	struct sf_softc		*sc;
48850675Swpaul	struct mii_data		*mii;
48949076Swpaul
49049076Swpaul	sc = ifp->if_softc;
49150675Swpaul	mii = device_get_softc(sc->sf_miibus);
49254161Swpaul	sc->sf_link = 0;
49354161Swpaul	if (mii->mii_instance) {
49454161Swpaul		struct mii_softc        *miisc;
49572012Sphk		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
49654161Swpaul			mii_phy_reset(miisc);
49754161Swpaul	}
49850675Swpaul	mii_mediachg(mii);
49949076Swpaul
50049076Swpaul	return(0);
50149076Swpaul}
50249076Swpaul
50349076Swpaul/*
50449076Swpaul * Report current media status.
50549076Swpaul */
50649076Swpaulstatic void sf_ifmedia_sts(ifp, ifmr)
50749076Swpaul	struct ifnet		*ifp;
50849076Swpaul	struct ifmediareq	*ifmr;
50949076Swpaul{
51049076Swpaul	struct sf_softc		*sc;
51150675Swpaul	struct mii_data		*mii;
51249076Swpaul
51349076Swpaul	sc = ifp->if_softc;
51450675Swpaul	mii = device_get_softc(sc->sf_miibus);
51549076Swpaul
51650675Swpaul	mii_pollstat(mii);
51750675Swpaul	ifmr->ifm_active = mii->mii_media_active;
51850675Swpaul	ifmr->ifm_status = mii->mii_media_status;
51949076Swpaul
52049076Swpaul	return;
52149076Swpaul}
52249076Swpaul
52349076Swpaulstatic int sf_ioctl(ifp, command, data)
52449076Swpaul	struct ifnet		*ifp;
52549076Swpaul	u_long			command;
52649076Swpaul	caddr_t			data;
52749076Swpaul{
52849076Swpaul	struct sf_softc		*sc = ifp->if_softc;
52949076Swpaul	struct ifreq		*ifr = (struct ifreq *) data;
53050675Swpaul	struct mii_data		*mii;
53167087Swpaul	int			error = 0;
53249076Swpaul
53367087Swpaul	SF_LOCK(sc);
53449076Swpaul
53549076Swpaul	switch(command) {
53649076Swpaul	case SIOCSIFADDR:
53749076Swpaul	case SIOCGIFADDR:
53849076Swpaul	case SIOCSIFMTU:
53949076Swpaul		error = ether_ioctl(ifp, command, data);
54049076Swpaul		break;
54149076Swpaul	case SIOCSIFFLAGS:
54249076Swpaul		if (ifp->if_flags & IFF_UP) {
54354161Swpaul			if (ifp->if_flags & IFF_RUNNING &&
54454161Swpaul			    ifp->if_flags & IFF_PROMISC &&
54554161Swpaul			    !(sc->sf_if_flags & IFF_PROMISC)) {
54654161Swpaul				SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC);
54754161Swpaul			} else if (ifp->if_flags & IFF_RUNNING &&
54854161Swpaul			    !(ifp->if_flags & IFF_PROMISC) &&
54954161Swpaul			    sc->sf_if_flags & IFF_PROMISC) {
55054161Swpaul				SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC);
55154161Swpaul			} else if (!(ifp->if_flags & IFF_RUNNING))
55254161Swpaul				sf_init(sc);
55349076Swpaul		} else {
55449076Swpaul			if (ifp->if_flags & IFF_RUNNING)
55549076Swpaul				sf_stop(sc);
55649076Swpaul		}
55754161Swpaul		sc->sf_if_flags = ifp->if_flags;
55849076Swpaul		error = 0;
55949076Swpaul		break;
56049076Swpaul	case SIOCADDMULTI:
56149076Swpaul	case SIOCDELMULTI:
56249076Swpaul		sf_setmulti(sc);
56349076Swpaul		error = 0;
56449076Swpaul		break;
56549076Swpaul	case SIOCGIFMEDIA:
56649076Swpaul	case SIOCSIFMEDIA:
56750675Swpaul		mii = device_get_softc(sc->sf_miibus);
56850675Swpaul		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
56949076Swpaul		break;
57049076Swpaul	default:
57149076Swpaul		error = EINVAL;
57249076Swpaul		break;
57349076Swpaul	}
57449076Swpaul
57567087Swpaul	SF_UNLOCK(sc);
57649076Swpaul
57749076Swpaul	return(error);
57849076Swpaul}
57949076Swpaul
58049076Swpaulstatic void sf_reset(sc)
58149076Swpaul	struct sf_softc		*sc;
58249076Swpaul{
58349076Swpaul	register int		i;
58449076Swpaul
58549076Swpaul	csr_write_4(sc, SF_GEN_ETH_CTL, 0);
58649076Swpaul	SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_SOFTRESET);
58749076Swpaul	DELAY(1000);
58849076Swpaul	SF_CLRBIT(sc, SF_MACCFG_1, SF_MACCFG1_SOFTRESET);
58949076Swpaul
59049076Swpaul	SF_SETBIT(sc, SF_PCI_DEVCFG, SF_PCIDEVCFG_RESET);
59149076Swpaul
59249076Swpaul	for (i = 0; i < SF_TIMEOUT; i++) {
59349076Swpaul		DELAY(10);
59449076Swpaul		if (!(csr_read_4(sc, SF_PCI_DEVCFG) & SF_PCIDEVCFG_RESET))
59549076Swpaul			break;
59649076Swpaul	}
59749076Swpaul
59849076Swpaul	if (i == SF_TIMEOUT)
59949076Swpaul		printf("sf%d: reset never completed!\n", sc->sf_unit);
60049076Swpaul
60149076Swpaul	/* Wait a little while for the chip to get its brains in order. */
60249076Swpaul	DELAY(1000);
60349076Swpaul	return;
60449076Swpaul}
60549076Swpaul
60649076Swpaul/*
60749076Swpaul * Probe for an Adaptec AIC-6915 chip. Check the PCI vendor and device
60849076Swpaul * IDs against our list and return a device name if we find a match.
60949076Swpaul * We also check the subsystem ID so that we can identify exactly which
61049076Swpaul * NIC has been found, if possible.
61149076Swpaul */
61249076Swpaulstatic int sf_probe(dev)
61349076Swpaul	device_t		dev;
61449076Swpaul{
61549076Swpaul	struct sf_type		*t;
61649076Swpaul
61749076Swpaul	t = sf_devs;
61849076Swpaul
61949076Swpaul	while(t->sf_name != NULL) {
62049076Swpaul		if ((pci_get_vendor(dev) == t->sf_vid) &&
62149076Swpaul		    (pci_get_device(dev) == t->sf_did)) {
62251336Swpaul			switch((pci_read_config(dev,
62351336Swpaul			    SF_PCI_SUBVEN_ID, 4) >> 16) & 0xFFFF) {
62449076Swpaul			case AD_SUBSYSID_62011_REV0:
62549076Swpaul			case AD_SUBSYSID_62011_REV1:
62649076Swpaul				device_set_desc(dev,
62749076Swpaul				    "Adaptec ANA-62011 10/100BaseTX");
62849076Swpaul				return(0);
62949076Swpaul				break;
63049076Swpaul			case AD_SUBSYSID_62022:
63149076Swpaul				device_set_desc(dev,
63249076Swpaul				    "Adaptec ANA-62022 10/100BaseTX");
63349076Swpaul				return(0);
63449076Swpaul				break;
63553468Swpaul			case AD_SUBSYSID_62044_REV0:
63653468Swpaul			case AD_SUBSYSID_62044_REV1:
63749076Swpaul				device_set_desc(dev,
63849076Swpaul				    "Adaptec ANA-62044 10/100BaseTX");
63949076Swpaul				return(0);
64049076Swpaul				break;
64149076Swpaul			case AD_SUBSYSID_62020:
64249076Swpaul				device_set_desc(dev,
64349076Swpaul				    "Adaptec ANA-62020 10/100BaseFX");
64449076Swpaul				return(0);
64549076Swpaul				break;
64649076Swpaul			case AD_SUBSYSID_69011:
64749076Swpaul				device_set_desc(dev,
64849076Swpaul				    "Adaptec ANA-69011 10/100BaseTX");
64949076Swpaul				return(0);
65049076Swpaul				break;
65149076Swpaul			default:
65249076Swpaul				device_set_desc(dev, t->sf_name);
65349076Swpaul				return(0);
65449076Swpaul				break;
65549076Swpaul			}
65649076Swpaul		}
65749076Swpaul		t++;
65849076Swpaul	}
65949076Swpaul
66049076Swpaul	return(ENXIO);
66149076Swpaul}
66249076Swpaul
66349076Swpaul/*
66449076Swpaul * Attach the interface. Allocate softc structures, do ifmedia
66549076Swpaul * setup and ethernet/BPF attach.
66649076Swpaul */
66749076Swpaulstatic int sf_attach(dev)
66849076Swpaul	device_t		dev;
66949076Swpaul{
67067087Swpaul	int			i;
67149076Swpaul	u_int32_t		command;
67249076Swpaul	struct sf_softc		*sc;
67349076Swpaul	struct ifnet		*ifp;
67449076Swpaul	int			unit, rid, error = 0;
67549076Swpaul
67649076Swpaul	sc = device_get_softc(dev);
67749076Swpaul	unit = device_get_unit(dev);
67849076Swpaul	bzero(sc, sizeof(struct sf_softc));
67949076Swpaul
68071228Sbmilekic	mtx_init(&sc->sf_mtx, device_get_nameunit(dev), MTX_DEF | MTX_RECURSE);
68169583Swpaul	SF_LOCK(sc);
68249076Swpaul	/*
68349076Swpaul	 * Handle power management nonsense.
68449076Swpaul	 */
68549076Swpaul	command = pci_read_config(dev, SF_PCI_CAPID, 4) & 0x000000FF;
68649076Swpaul	if (command == 0x01) {
68749076Swpaul
68849076Swpaul		command = pci_read_config(dev, SF_PCI_PWRMGMTCTRL, 4);
68949076Swpaul		if (command & SF_PSTATE_MASK) {
69049076Swpaul			u_int32_t		iobase, membase, irq;
69149076Swpaul
69249076Swpaul			/* Save important PCI config data. */
69349076Swpaul			iobase = pci_read_config(dev, SF_PCI_LOIO, 4);
69449076Swpaul			membase = pci_read_config(dev, SF_PCI_LOMEM, 4);
69549076Swpaul			irq = pci_read_config(dev, SF_PCI_INTLINE, 4);
69649076Swpaul
69749076Swpaul			/* Reset the power state. */
69849076Swpaul			printf("sf%d: chip is in D%d power mode "
69949076Swpaul			"-- setting to D0\n", unit, command & SF_PSTATE_MASK);
70049076Swpaul			command &= 0xFFFFFFFC;
70149076Swpaul			pci_write_config(dev, SF_PCI_PWRMGMTCTRL, command, 4);
70249076Swpaul
70349076Swpaul			/* Restore PCI config data. */
70449076Swpaul			pci_write_config(dev, SF_PCI_LOIO, iobase, 4);
70549076Swpaul			pci_write_config(dev, SF_PCI_LOMEM, membase, 4);
70649076Swpaul			pci_write_config(dev, SF_PCI_INTLINE, irq, 4);
70749076Swpaul		}
70849076Swpaul	}
70949076Swpaul
71049076Swpaul	/*
71149076Swpaul	 * Map control/status registers.
71249076Swpaul	 */
71361041Speter	command = pci_read_config(dev, PCIR_COMMAND, 4);
71449076Swpaul	command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
71561041Speter	pci_write_config(dev, PCIR_COMMAND, command, 4);
71661041Speter	command = pci_read_config(dev, PCIR_COMMAND, 4);
71749076Swpaul
71849076Swpaul#ifdef SF_USEIOSPACE
71949076Swpaul	if (!(command & PCIM_CMD_PORTEN)) {
72049076Swpaul		printf("sf%d: failed to enable I/O ports!\n", unit);
72149076Swpaul		error = ENXIO;
72249076Swpaul		goto fail;
72349076Swpaul	}
72449076Swpaul#else
72549076Swpaul	if (!(command & PCIM_CMD_MEMEN)) {
72649076Swpaul		printf("sf%d: failed to enable memory mapping!\n", unit);
72749076Swpaul		error = ENXIO;
72849076Swpaul		goto fail;
72949076Swpaul	}
73049076Swpaul#endif
73149076Swpaul
73249076Swpaul	rid = SF_RID;
73349076Swpaul	sc->sf_res = bus_alloc_resource(dev, SF_RES, &rid,
73449076Swpaul	    0, ~0, 1, RF_ACTIVE);
73549076Swpaul
73649076Swpaul	if (sc->sf_res == NULL) {
73749076Swpaul		printf ("sf%d: couldn't map ports\n", unit);
73849076Swpaul		error = ENXIO;
73949076Swpaul		goto fail;
74049076Swpaul	}
74149076Swpaul
74249076Swpaul	sc->sf_btag = rman_get_bustag(sc->sf_res);
74349076Swpaul	sc->sf_bhandle = rman_get_bushandle(sc->sf_res);
74449076Swpaul
74549076Swpaul	/* Allocate interrupt */
74649076Swpaul	rid = 0;
74749076Swpaul	sc->sf_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
74849076Swpaul	    RF_SHAREABLE | RF_ACTIVE);
74949076Swpaul
75049076Swpaul	if (sc->sf_irq == NULL) {
75149076Swpaul		printf("sf%d: couldn't map interrupt\n", unit);
75249076Swpaul		bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res);
75349076Swpaul		error = ENXIO;
75449076Swpaul		goto fail;
75549076Swpaul	}
75649076Swpaul
75749076Swpaul	error = bus_setup_intr(dev, sc->sf_irq, INTR_TYPE_NET,
75849076Swpaul	    sf_intr, sc, &sc->sf_intrhand);
75949076Swpaul
76049076Swpaul	if (error) {
76149076Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sf_res);
76249076Swpaul		bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res);
76349076Swpaul		printf("sf%d: couldn't set up irq\n", unit);
76449076Swpaul		goto fail;
76549076Swpaul	}
76649076Swpaul
76749076Swpaul	callout_handle_init(&sc->sf_stat_ch);
76849076Swpaul	/* Reset the adapter. */
76949076Swpaul	sf_reset(sc);
77049076Swpaul
77149076Swpaul	/*
77249076Swpaul	 * Get station address from the EEPROM.
77349076Swpaul	 */
77449076Swpaul	for (i = 0; i < ETHER_ADDR_LEN; i++)
77549076Swpaul		sc->arpcom.ac_enaddr[i] =
77649076Swpaul		    sf_read_eeprom(sc, SF_EE_NODEADDR + ETHER_ADDR_LEN - i);
77749076Swpaul
77849076Swpaul	/*
77949076Swpaul	 * An Adaptec chip was detected. Inform the world.
78049076Swpaul	 */
78149076Swpaul	printf("sf%d: Ethernet address: %6D\n", unit,
78249076Swpaul	    sc->arpcom.ac_enaddr, ":");
78349076Swpaul
78449076Swpaul	sc->sf_unit = unit;
78549076Swpaul
78649076Swpaul	/* Allocate the descriptor queues. */
78749076Swpaul	sc->sf_ldata = contigmalloc(sizeof(struct sf_list_data), M_DEVBUF,
78851657Swpaul	    M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0);
78949076Swpaul
79049076Swpaul	if (sc->sf_ldata == NULL) {
79149076Swpaul		printf("sf%d: no memory for list buffers!\n", unit);
79249076Swpaul		bus_teardown_intr(dev, sc->sf_irq, sc->sf_intrhand);
79349076Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sf_irq);
79449076Swpaul		bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res);
79549076Swpaul		error = ENXIO;
79649076Swpaul		goto fail;
79749076Swpaul	}
79849076Swpaul
79949076Swpaul	bzero(sc->sf_ldata, sizeof(struct sf_list_data));
80049076Swpaul
80150675Swpaul	/* Do MII setup. */
80250675Swpaul	if (mii_phy_probe(dev, &sc->sf_miibus,
80350675Swpaul	    sf_ifmedia_upd, sf_ifmedia_sts)) {
80449076Swpaul		printf("sf%d: MII without any phy!\n", sc->sf_unit);
80554161Swpaul		contigfree(sc->sf_ldata,sizeof(struct sf_list_data),M_DEVBUF);
80649076Swpaul		bus_teardown_intr(dev, sc->sf_irq, sc->sf_intrhand);
80749076Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sf_irq);
80849076Swpaul		bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res);
80949076Swpaul		error = ENXIO;
81049076Swpaul		goto fail;
81149076Swpaul	}
81249076Swpaul
81349076Swpaul	ifp = &sc->arpcom.ac_if;
81449076Swpaul	ifp->if_softc = sc;
81549076Swpaul	ifp->if_unit = unit;
81649076Swpaul	ifp->if_name = "sf";
81749076Swpaul	ifp->if_mtu = ETHERMTU;
81849076Swpaul	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
81949076Swpaul	ifp->if_ioctl = sf_ioctl;
82049076Swpaul	ifp->if_output = ether_output;
82149076Swpaul	ifp->if_start = sf_start;
82249076Swpaul	ifp->if_watchdog = sf_watchdog;
82349076Swpaul	ifp->if_init = sf_init;
82449076Swpaul	ifp->if_baudrate = 10000000;
82549076Swpaul	ifp->if_snd.ifq_maxlen = SF_TX_DLIST_CNT - 1;
82649076Swpaul
82749076Swpaul	/*
82863090Sarchie	 * Call MI attach routine.
82949076Swpaul	 */
83063090Sarchie	ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
83167087Swpaul	SF_UNLOCK(sc);
83267087Swpaul	return(0);
83349076Swpaul
83449076Swpaulfail:
83567087Swpaul	SF_UNLOCK(sc);
83667087Swpaul	mtx_destroy(&sc->sf_mtx);
83749076Swpaul	return(error);
83849076Swpaul}
83949076Swpaul
84049076Swpaulstatic int sf_detach(dev)
84149076Swpaul	device_t		dev;
84249076Swpaul{
84349076Swpaul	struct sf_softc		*sc;
84449076Swpaul	struct ifnet		*ifp;
84549076Swpaul
84649076Swpaul	sc = device_get_softc(dev);
84767087Swpaul	SF_LOCK(sc);
84849076Swpaul	ifp = &sc->arpcom.ac_if;
84949076Swpaul
85063090Sarchie	ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
85149076Swpaul	sf_stop(sc);
85249076Swpaul
85350675Swpaul	bus_generic_detach(dev);
85450675Swpaul	device_delete_child(dev, sc->sf_miibus);
85550675Swpaul
85649076Swpaul	bus_teardown_intr(dev, sc->sf_irq, sc->sf_intrhand);
85749076Swpaul	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sf_irq);
85849076Swpaul	bus_release_resource(dev, SF_RES, SF_RID, sc->sf_res);
85949076Swpaul
86054161Swpaul	contigfree(sc->sf_ldata, sizeof(struct sf_list_data), M_DEVBUF);
86149076Swpaul
86267087Swpaul	SF_UNLOCK(sc);
86367087Swpaul	mtx_destroy(&sc->sf_mtx);
86449076Swpaul
86549076Swpaul	return(0);
86649076Swpaul}
86749076Swpaul
86849076Swpaulstatic int sf_init_rx_ring(sc)
86949076Swpaul	struct sf_softc		*sc;
87049076Swpaul{
87149076Swpaul	struct sf_list_data	*ld;
87249076Swpaul	int			i;
87349076Swpaul
87449076Swpaul	ld = sc->sf_ldata;
87549076Swpaul
87649076Swpaul	bzero((char *)ld->sf_rx_dlist_big,
87749076Swpaul	    sizeof(struct sf_rx_bufdesc_type0) * SF_RX_DLIST_CNT);
87849076Swpaul	bzero((char *)ld->sf_rx_clist,
87949076Swpaul	    sizeof(struct sf_rx_cmpdesc_type3) * SF_RX_CLIST_CNT);
88049076Swpaul
88149076Swpaul	for (i = 0; i < SF_RX_DLIST_CNT; i++) {
88249076Swpaul		if (sf_newbuf(sc, &ld->sf_rx_dlist_big[i], NULL) == ENOBUFS)
88349076Swpaul			return(ENOBUFS);
88449076Swpaul	}
88549076Swpaul
88649076Swpaul	return(0);
88749076Swpaul}
88849076Swpaul
88949076Swpaulstatic void sf_init_tx_ring(sc)
89049076Swpaul	struct sf_softc		*sc;
89149076Swpaul{
89249076Swpaul	struct sf_list_data	*ld;
89349076Swpaul	int			i;
89449076Swpaul
89549076Swpaul	ld = sc->sf_ldata;
89649076Swpaul
89749076Swpaul	bzero((char *)ld->sf_tx_dlist,
89849076Swpaul	    sizeof(struct sf_tx_bufdesc_type0) * SF_TX_DLIST_CNT);
89949076Swpaul	bzero((char *)ld->sf_tx_clist,
90049076Swpaul	    sizeof(struct sf_tx_cmpdesc_type0) * SF_TX_CLIST_CNT);
90149076Swpaul
90249076Swpaul	for (i = 0; i < SF_TX_DLIST_CNT; i++)
90349076Swpaul		ld->sf_tx_dlist[i].sf_id = SF_TX_BUFDESC_ID;
90449076Swpaul	for (i = 0; i < SF_TX_CLIST_CNT; i++)
90549076Swpaul		ld->sf_tx_clist[i].sf_type = SF_TXCMPTYPE_TX;
90649076Swpaul
90749076Swpaul	ld->sf_tx_dlist[SF_TX_DLIST_CNT - 1].sf_end = 1;
90849076Swpaul	sc->sf_tx_cnt = 0;
90949076Swpaul
91049076Swpaul	return;
91149076Swpaul}
91249076Swpaul
91349076Swpaulstatic int sf_newbuf(sc, c, m)
91449076Swpaul	struct sf_softc		*sc;
91549076Swpaul	struct sf_rx_bufdesc_type0	*c;
91649076Swpaul	struct mbuf		*m;
91749076Swpaul{
91849076Swpaul	struct mbuf		*m_new = NULL;
91949076Swpaul
92049076Swpaul	if (m == NULL) {
92149076Swpaul		MGETHDR(m_new, M_DONTWAIT, MT_DATA);
92249076Swpaul		if (m_new == NULL) {
92349076Swpaul			printf("sf%d: no memory for rx list -- "
92449076Swpaul			    "packet dropped!\n", sc->sf_unit);
92549076Swpaul			return(ENOBUFS);
92649076Swpaul		}
92749076Swpaul
92849076Swpaul		MCLGET(m_new, M_DONTWAIT);
92949076Swpaul		if (!(m_new->m_flags & M_EXT)) {
93049076Swpaul			printf("sf%d: no memory for rx list -- "
93149076Swpaul			    "packet dropped!\n", sc->sf_unit);
93249076Swpaul			m_freem(m_new);
93349076Swpaul			return(ENOBUFS);
93449076Swpaul		}
93549076Swpaul		m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
93649076Swpaul	} else {
93749076Swpaul		m_new = m;
93849076Swpaul		m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
93949076Swpaul		m_new->m_data = m_new->m_ext.ext_buf;
94049076Swpaul	}
94149076Swpaul
94249076Swpaul	m_adj(m_new, sizeof(u_int64_t));
94349076Swpaul
94449076Swpaul	c->sf_mbuf = m_new;
94549076Swpaul	c->sf_addrlo = SF_RX_HOSTADDR(vtophys(mtod(m_new, caddr_t)));
94649076Swpaul	c->sf_valid = 1;
94749076Swpaul
94849076Swpaul	return(0);
94949076Swpaul}
95049076Swpaul
95149076Swpaul/*
95249076Swpaul * The starfire is programmed to use 'normal' mode for packet reception,
95349076Swpaul * which means we use the consumer/producer model for both the buffer
95449076Swpaul * descriptor queue and the completion descriptor queue. The only problem
95549076Swpaul * with this is that it involves a lot of register accesses: we have to
95649076Swpaul * read the RX completion consumer and producer indexes and the RX buffer
95749076Swpaul * producer index, plus the RX completion consumer and RX buffer producer
95849076Swpaul * indexes have to be updated. It would have been easier if Adaptec had
95949076Swpaul * put each index in a separate register, especially given that the damn
96049076Swpaul * NIC has a 512K register space.
96149076Swpaul *
96249076Swpaul * In spite of all the lovely features that Adaptec crammed into the 6915,
96349076Swpaul * it is marred by one truly stupid design flaw, which is that receive
96449076Swpaul * buffer addresses must be aligned on a longword boundary. This forces
96549076Swpaul * the packet payload to be unaligned, which is suboptimal on the x86 and
96649076Swpaul * completely unuseable on the Alpha. Our only recourse is to copy received
96749076Swpaul * packets into properly aligned buffers before handing them off.
96849076Swpaul */
96949076Swpaul
97049076Swpaulstatic void sf_rxeof(sc)
97149076Swpaul	struct sf_softc		*sc;
97249076Swpaul{
97349076Swpaul	struct ether_header	*eh;
97449076Swpaul	struct mbuf		*m;
97549076Swpaul	struct ifnet		*ifp;
97649076Swpaul	struct sf_rx_bufdesc_type0	*desc;
97749076Swpaul	struct sf_rx_cmpdesc_type3	*cur_rx;
97849076Swpaul	u_int32_t		rxcons, rxprod;
97949076Swpaul	int			cmpprodidx, cmpconsidx, bufprodidx;
98049076Swpaul
98149076Swpaul	ifp = &sc->arpcom.ac_if;
98249076Swpaul
98349076Swpaul	rxcons = csr_read_4(sc, SF_CQ_CONSIDX);
98449076Swpaul	rxprod = csr_read_4(sc, SF_RXDQ_PTR_Q1);
98549076Swpaul	cmpprodidx = SF_IDX_LO(csr_read_4(sc, SF_CQ_PRODIDX));
98649076Swpaul	cmpconsidx = SF_IDX_LO(rxcons);
98749076Swpaul	bufprodidx = SF_IDX_LO(rxprod);
98849076Swpaul
98949076Swpaul	while (cmpconsidx != cmpprodidx) {
99049076Swpaul		struct mbuf		*m0;
99149076Swpaul
99249076Swpaul		cur_rx = &sc->sf_ldata->sf_rx_clist[cmpconsidx];
99349076Swpaul		desc = &sc->sf_ldata->sf_rx_dlist_big[cur_rx->sf_endidx];
99449076Swpaul		m = desc->sf_mbuf;
99549076Swpaul		SF_INC(cmpconsidx, SF_RX_CLIST_CNT);
99649076Swpaul		SF_INC(bufprodidx, SF_RX_DLIST_CNT);
99749076Swpaul
99849076Swpaul		if (!(cur_rx->sf_status1 & SF_RXSTAT1_OK)) {
99949076Swpaul			ifp->if_ierrors++;
100049076Swpaul			sf_newbuf(sc, desc, m);
100149076Swpaul			continue;
100249076Swpaul		}
100349076Swpaul
100449076Swpaul		m0 = m_devget(mtod(m, char *) - ETHER_ALIGN,
100549076Swpaul		    cur_rx->sf_len + ETHER_ALIGN, 0, ifp, NULL);
100649076Swpaul		sf_newbuf(sc, desc, m);
100749076Swpaul		if (m0 == NULL) {
100849076Swpaul			ifp->if_ierrors++;
100949076Swpaul			continue;
101049076Swpaul		}
101149076Swpaul		m_adj(m0, ETHER_ALIGN);
101249076Swpaul		m = m0;
101349076Swpaul
101449076Swpaul		eh = mtod(m, struct ether_header *);
101549076Swpaul		ifp->if_ipackets++;
101649076Swpaul
101749076Swpaul		/* Remove header from mbuf and pass it on. */
101849076Swpaul		m_adj(m, sizeof(struct ether_header));
101949076Swpaul		ether_input(ifp, eh, m);
102049076Swpaul	}
102149076Swpaul
102249076Swpaul	csr_write_4(sc, SF_CQ_CONSIDX,
102349076Swpaul	    (rxcons & ~SF_CQ_CONSIDX_RXQ1) | cmpconsidx);
102449076Swpaul	csr_write_4(sc, SF_RXDQ_PTR_Q1,
102549076Swpaul	    (rxprod & ~SF_RXDQ_PRODIDX) | bufprodidx);
102649076Swpaul
102749076Swpaul	return;
102849076Swpaul}
102949076Swpaul
103049076Swpaul/*
103149076Swpaul * Read the transmit status from the completion queue and release
103249076Swpaul * mbufs. Note that the buffer descriptor index in the completion
103349076Swpaul * descriptor is an offset from the start of the transmit buffer
103449076Swpaul * descriptor list in bytes. This is important because the manual
103549076Swpaul * gives the impression that it should match the producer/consumer
103649076Swpaul * index, which is the offset in 8 byte blocks.
103749076Swpaul */
103849076Swpaulstatic void sf_txeof(sc)
103949076Swpaul	struct sf_softc		*sc;
104049076Swpaul{
104149076Swpaul	int			txcons, cmpprodidx, cmpconsidx;
104249076Swpaul	struct sf_tx_cmpdesc_type1 *cur_cmp;
104349076Swpaul	struct sf_tx_bufdesc_type0 *cur_tx;
104449076Swpaul	struct ifnet		*ifp;
104549076Swpaul
104649076Swpaul	ifp = &sc->arpcom.ac_if;
104749076Swpaul
104849076Swpaul	txcons = csr_read_4(sc, SF_CQ_CONSIDX);
104949076Swpaul	cmpprodidx = SF_IDX_HI(csr_read_4(sc, SF_CQ_PRODIDX));
105049076Swpaul	cmpconsidx = SF_IDX_HI(txcons);
105149076Swpaul
105249076Swpaul	while (cmpconsidx != cmpprodidx) {
105349076Swpaul		cur_cmp = &sc->sf_ldata->sf_tx_clist[cmpconsidx];
105449076Swpaul		cur_tx = &sc->sf_ldata->sf_tx_dlist[cur_cmp->sf_index >> 7];
105549076Swpaul		SF_INC(cmpconsidx, SF_TX_CLIST_CNT);
105649076Swpaul
105749076Swpaul		if (cur_cmp->sf_txstat & SF_TXSTAT_TX_OK)
105849076Swpaul			ifp->if_opackets++;
105949076Swpaul		else
106049076Swpaul			ifp->if_oerrors++;
106149076Swpaul
106249076Swpaul		sc->sf_tx_cnt--;
106349076Swpaul		if (cur_tx->sf_mbuf != NULL) {
106449076Swpaul			m_freem(cur_tx->sf_mbuf);
106549076Swpaul			cur_tx->sf_mbuf = NULL;
106649076Swpaul		}
106749076Swpaul	}
106849076Swpaul
106949076Swpaul	ifp->if_timer = 0;
107049076Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
107149076Swpaul
107249076Swpaul	csr_write_4(sc, SF_CQ_CONSIDX,
107349076Swpaul	    (txcons & ~SF_CQ_CONSIDX_TXQ) |
107449076Swpaul	    ((cmpconsidx << 16) & 0xFFFF0000));
107549076Swpaul
107649076Swpaul	return;
107749076Swpaul}
107849076Swpaul
107949076Swpaulstatic void sf_intr(arg)
108049076Swpaul	void			*arg;
108149076Swpaul{
108249076Swpaul	struct sf_softc		*sc;
108349076Swpaul	struct ifnet		*ifp;
108449076Swpaul	u_int32_t		status;
108549076Swpaul
108649076Swpaul	sc = arg;
108767087Swpaul	SF_LOCK(sc);
108867087Swpaul
108949076Swpaul	ifp = &sc->arpcom.ac_if;
109049076Swpaul
109167087Swpaul	if (!(csr_read_4(sc, SF_ISR_SHADOW) & SF_ISR_PCIINT_ASSERTED)) {
109267087Swpaul		SF_UNLOCK(sc);
109349076Swpaul		return;
109467087Swpaul	}
109549076Swpaul
109649076Swpaul	/* Disable interrupts. */
109749076Swpaul	csr_write_4(sc, SF_IMR, 0x00000000);
109849076Swpaul
109949076Swpaul	for (;;) {
110049076Swpaul		status = csr_read_4(sc, SF_ISR);
110149076Swpaul		if (status)
110249076Swpaul			csr_write_4(sc, SF_ISR, status);
110349076Swpaul
110449076Swpaul		if (!(status & SF_INTRS))
110549076Swpaul			break;
110649076Swpaul
110749076Swpaul		if (status & SF_ISR_RXDQ1_DMADONE)
110849076Swpaul			sf_rxeof(sc);
110949076Swpaul
111049076Swpaul		if (status & SF_ISR_TX_TXDONE)
111149076Swpaul			sf_txeof(sc);
111249076Swpaul
111349076Swpaul		if (status & SF_ISR_ABNORMALINTR) {
111449076Swpaul			if (status & SF_ISR_STATSOFLOW) {
111549076Swpaul				untimeout(sf_stats_update, sc,
111649076Swpaul				    sc->sf_stat_ch);
111749076Swpaul				sf_stats_update(sc);
111849076Swpaul			} else
111949076Swpaul				sf_init(sc);
112049076Swpaul		}
112149076Swpaul	}
112249076Swpaul
112349076Swpaul	/* Re-enable interrupts. */
112449076Swpaul	csr_write_4(sc, SF_IMR, SF_INTRS);
112549076Swpaul
112649076Swpaul	if (ifp->if_snd.ifq_head != NULL)
112749076Swpaul		sf_start(ifp);
112849076Swpaul
112967087Swpaul	SF_UNLOCK(sc);
113049076Swpaul	return;
113149076Swpaul}
113249076Swpaul
113349076Swpaulstatic void sf_init(xsc)
113449076Swpaul	void			*xsc;
113549076Swpaul{
113649076Swpaul	struct sf_softc		*sc;
113749076Swpaul	struct ifnet		*ifp;
113850675Swpaul	struct mii_data		*mii;
113967087Swpaul	int			i;
114049076Swpaul
114149076Swpaul	sc = xsc;
114267087Swpaul	SF_LOCK(sc);
114349076Swpaul	ifp = &sc->arpcom.ac_if;
114450675Swpaul	mii = device_get_softc(sc->sf_miibus);
114549076Swpaul
114649076Swpaul	sf_stop(sc);
114749076Swpaul	sf_reset(sc);
114849076Swpaul
114949076Swpaul	/* Init all the receive filter registers */
115049076Swpaul	for (i = SF_RXFILT_PERFECT_BASE;
115149076Swpaul	    i < (SF_RXFILT_HASH_MAX + 1); i += 4)
115249076Swpaul		csr_write_4(sc, i, 0);
115349076Swpaul
115449076Swpaul	/* Empty stats counter registers. */
115549076Swpaul	for (i = 0; i < sizeof(struct sf_stats)/sizeof(u_int32_t); i++)
115649076Swpaul		csr_write_4(sc, SF_STATS_BASE +
115749076Swpaul		    (i + sizeof(u_int32_t)), 0);
115849076Swpaul
115949076Swpaul	/* Init our MAC address */
116049076Swpaul	csr_write_4(sc, SF_PAR0, *(u_int32_t *)(&sc->arpcom.ac_enaddr[0]));
116149076Swpaul	csr_write_4(sc, SF_PAR1, *(u_int32_t *)(&sc->arpcom.ac_enaddr[4]));
116249076Swpaul	sf_setperf(sc, 0, (caddr_t)&sc->arpcom.ac_enaddr);
116349076Swpaul
116449076Swpaul	if (sf_init_rx_ring(sc) == ENOBUFS) {
116549076Swpaul		printf("sf%d: initialization failed: no "
116649076Swpaul		    "memory for rx buffers\n", sc->sf_unit);
116767087Swpaul		SF_UNLOCK(sc);
116849076Swpaul		return;
116949076Swpaul	}
117049076Swpaul
117149076Swpaul	sf_init_tx_ring(sc);
117249076Swpaul
117349076Swpaul	csr_write_4(sc, SF_RXFILT, SF_PERFMODE_NORMAL|SF_HASHMODE_WITHVLAN);
117449076Swpaul
117549076Swpaul	/* If we want promiscuous mode, set the allframes bit. */
117649076Swpaul	if (ifp->if_flags & IFF_PROMISC) {
117749076Swpaul		SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC);
117849076Swpaul	} else {
117949076Swpaul		SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_PROMISC);
118049076Swpaul	}
118149076Swpaul
118249076Swpaul	if (ifp->if_flags & IFF_BROADCAST) {
118349076Swpaul		SF_SETBIT(sc, SF_RXFILT, SF_RXFILT_BROAD);
118449076Swpaul	} else {
118549076Swpaul		SF_CLRBIT(sc, SF_RXFILT, SF_RXFILT_BROAD);
118649076Swpaul	}
118749076Swpaul
118863166Swpaul	/*
118963166Swpaul	 * Load the multicast filter.
119063166Swpaul	 */
119163166Swpaul	sf_setmulti(sc);
119263166Swpaul
119349076Swpaul	/* Init the completion queue indexes */
119449076Swpaul	csr_write_4(sc, SF_CQ_CONSIDX, 0);
119549076Swpaul	csr_write_4(sc, SF_CQ_PRODIDX, 0);
119649076Swpaul
119749076Swpaul	/* Init the RX completion queue */
119849076Swpaul	csr_write_4(sc, SF_RXCQ_CTL_1,
119949076Swpaul	    vtophys(sc->sf_ldata->sf_rx_clist) & SF_RXCQ_ADDR);
120049076Swpaul	SF_SETBIT(sc, SF_RXCQ_CTL_1, SF_RXCQTYPE_3);
120149076Swpaul
120249076Swpaul	/* Init RX DMA control. */
120349076Swpaul	SF_SETBIT(sc, SF_RXDMA_CTL, SF_RXDMA_REPORTBADPKTS);
120449076Swpaul
120549076Swpaul	/* Init the RX buffer descriptor queue. */
120649076Swpaul	csr_write_4(sc, SF_RXDQ_ADDR_Q1,
120749076Swpaul	    vtophys(sc->sf_ldata->sf_rx_dlist_big));
120849076Swpaul	csr_write_4(sc, SF_RXDQ_CTL_1, (MCLBYTES << 16) | SF_DESCSPACE_16BYTES);
120949076Swpaul	csr_write_4(sc, SF_RXDQ_PTR_Q1, SF_RX_DLIST_CNT - 1);
121049076Swpaul
121149076Swpaul	/* Init the TX completion queue */
121249076Swpaul	csr_write_4(sc, SF_TXCQ_CTL,
121349076Swpaul	    vtophys(sc->sf_ldata->sf_tx_clist) & SF_RXCQ_ADDR);
121449076Swpaul
121549076Swpaul	/* Init the TX buffer descriptor queue. */
121649076Swpaul	csr_write_4(sc, SF_TXDQ_ADDR_HIPRIO,
121749076Swpaul		vtophys(sc->sf_ldata->sf_tx_dlist));
121849076Swpaul	SF_SETBIT(sc, SF_TX_FRAMCTL, SF_TXFRMCTL_CPLAFTERTX);
121949076Swpaul	csr_write_4(sc, SF_TXDQ_CTL,
122049076Swpaul	    SF_TXBUFDESC_TYPE0|SF_TXMINSPACE_128BYTES|SF_TXSKIPLEN_8BYTES);
122149076Swpaul	SF_SETBIT(sc, SF_TXDQ_CTL, SF_TXDQCTL_NODMACMP);
122249076Swpaul
122349076Swpaul	/* Enable autopadding of short TX frames. */
122449076Swpaul	SF_SETBIT(sc, SF_MACCFG_1, SF_MACCFG1_AUTOPAD);
122549076Swpaul
122649076Swpaul	/* Enable interrupts. */
122749076Swpaul	csr_write_4(sc, SF_IMR, SF_INTRS);
122849076Swpaul	SF_SETBIT(sc, SF_PCI_DEVCFG, SF_PCIDEVCFG_INTR_ENB);
122949076Swpaul
123049076Swpaul	/* Enable the RX and TX engines. */
123149076Swpaul	SF_SETBIT(sc, SF_GEN_ETH_CTL, SF_ETHCTL_RX_ENB|SF_ETHCTL_RXDMA_ENB);
123249076Swpaul	SF_SETBIT(sc, SF_GEN_ETH_CTL, SF_ETHCTL_TX_ENB|SF_ETHCTL_TXDMA_ENB);
123349076Swpaul
123454161Swpaul	/*mii_mediachg(mii);*/
123554161Swpaul	sf_ifmedia_upd(ifp);
123650675Swpaul
123749076Swpaul	ifp->if_flags |= IFF_RUNNING;
123849076Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
123949076Swpaul
124049076Swpaul	sc->sf_stat_ch = timeout(sf_stats_update, sc, hz);
124149076Swpaul
124267087Swpaul	SF_UNLOCK(sc);
124349076Swpaul
124449076Swpaul	return;
124549076Swpaul}
124649076Swpaul
124749076Swpaulstatic int sf_encap(sc, c, m_head)
124849076Swpaul	struct sf_softc		*sc;
124949076Swpaul	struct sf_tx_bufdesc_type0 *c;
125049076Swpaul	struct mbuf		*m_head;
125149076Swpaul{
125249076Swpaul	int			frag = 0;
125349076Swpaul	struct sf_frag		*f = NULL;
125449076Swpaul	struct mbuf		*m;
125549076Swpaul
125649076Swpaul	m = m_head;
125749076Swpaul
125849076Swpaul	for (m = m_head, frag = 0; m != NULL; m = m->m_next) {
125949076Swpaul		if (m->m_len != 0) {
126049076Swpaul			if (frag == SF_MAXFRAGS)
126149076Swpaul				break;
126249076Swpaul			f = &c->sf_frags[frag];
126349076Swpaul			if (frag == 0)
126449076Swpaul				f->sf_pktlen = m_head->m_pkthdr.len;
126549076Swpaul			f->sf_fraglen = m->m_len;
126649076Swpaul			f->sf_addr = vtophys(mtod(m, vm_offset_t));
126749076Swpaul			frag++;
126849076Swpaul		}
126949076Swpaul	}
127049076Swpaul
127149076Swpaul	if (m != NULL) {
127249076Swpaul		struct mbuf		*m_new = NULL;
127349076Swpaul
127449076Swpaul		MGETHDR(m_new, M_DONTWAIT, MT_DATA);
127549076Swpaul		if (m_new == NULL) {
127649076Swpaul			printf("sf%d: no memory for tx list", sc->sf_unit);
127749076Swpaul			return(1);
127849076Swpaul		}
127949076Swpaul
128049076Swpaul		if (m_head->m_pkthdr.len > MHLEN) {
128149076Swpaul			MCLGET(m_new, M_DONTWAIT);
128249076Swpaul			if (!(m_new->m_flags & M_EXT)) {
128349076Swpaul				m_freem(m_new);
128449076Swpaul				printf("sf%d: no memory for tx list",
128549076Swpaul				    sc->sf_unit);
128649076Swpaul				return(1);
128749076Swpaul			}
128849076Swpaul		}
128949076Swpaul		m_copydata(m_head, 0, m_head->m_pkthdr.len,
129049076Swpaul		    mtod(m_new, caddr_t));
129149076Swpaul		m_new->m_pkthdr.len = m_new->m_len = m_head->m_pkthdr.len;
129249076Swpaul		m_freem(m_head);
129349076Swpaul		m_head = m_new;
129449076Swpaul		f = &c->sf_frags[0];
129549076Swpaul		f->sf_fraglen = f->sf_pktlen = m_head->m_pkthdr.len;
129649076Swpaul		f->sf_addr = vtophys(mtod(m_head, caddr_t));
129749076Swpaul		frag = 1;
129849076Swpaul	}
129949076Swpaul
130049076Swpaul	c->sf_mbuf = m_head;
130149076Swpaul	c->sf_id = SF_TX_BUFDESC_ID;
130249076Swpaul	c->sf_fragcnt = frag;
130349076Swpaul	c->sf_intr = 1;
130449076Swpaul	c->sf_caltcp = 0;
130549076Swpaul	c->sf_crcen = 1;
130649076Swpaul
130749076Swpaul	return(0);
130849076Swpaul}
130949076Swpaul
131049076Swpaulstatic void sf_start(ifp)
131149076Swpaul	struct ifnet		*ifp;
131249076Swpaul{
131349076Swpaul	struct sf_softc		*sc;
131449076Swpaul	struct sf_tx_bufdesc_type0 *cur_tx = NULL;
131549076Swpaul	struct mbuf		*m_head = NULL;
131649076Swpaul	int			i, txprod;
131749076Swpaul
131849076Swpaul	sc = ifp->if_softc;
131967087Swpaul	SF_LOCK(sc);
132049076Swpaul
132167087Swpaul	if (!sc->sf_link) {
132267087Swpaul		SF_UNLOCK(sc);
132354161Swpaul		return;
132467087Swpaul	}
132554161Swpaul
132667087Swpaul	if (ifp->if_flags & IFF_OACTIVE) {
132767087Swpaul		SF_UNLOCK(sc);
132849076Swpaul		return;
132967087Swpaul	}
133049076Swpaul
133149076Swpaul	txprod = csr_read_4(sc, SF_TXDQ_PRODIDX);
133249076Swpaul	i = SF_IDX_HI(txprod) >> 4;
133349076Swpaul
133449076Swpaul	while(sc->sf_ldata->sf_tx_dlist[i].sf_mbuf == NULL) {
133571276Swpaul		if (sc->sf_tx_cnt == (SF_TX_DLIST_CNT - 2)) {
133671276Swpaul			ifp->if_flags |= IFF_OACTIVE;
133771276Swpaul			cur_tx = NULL;
133871276Swpaul			break;
133971276Swpaul		}
134049076Swpaul		IF_DEQUEUE(&ifp->if_snd, m_head);
134149076Swpaul		if (m_head == NULL)
134249076Swpaul			break;
134349076Swpaul
134449076Swpaul		cur_tx = &sc->sf_ldata->sf_tx_dlist[i];
134571276Swpaul		if (sf_encap(sc, cur_tx, m_head)) {
134671276Swpaul			IF_PREPEND(&ifp->if_snd, m_head);
134771276Swpaul			ifp->if_flags |= IFF_OACTIVE;
134871276Swpaul			cur_tx = NULL;
134971276Swpaul			break;
135071276Swpaul		}
135149076Swpaul
135249076Swpaul		/*
135349076Swpaul		 * If there's a BPF listener, bounce a copy of this frame
135449076Swpaul		 * to him.
135549076Swpaul		 */
135649076Swpaul		if (ifp->if_bpf)
135749076Swpaul			bpf_mtap(ifp, m_head);
135851583Swpaul
135949076Swpaul		SF_INC(i, SF_TX_DLIST_CNT);
136049076Swpaul		sc->sf_tx_cnt++;
136149076Swpaul	}
136249076Swpaul
136367087Swpaul	if (cur_tx == NULL) {
136467087Swpaul		SF_UNLOCK(sc);
136549076Swpaul		return;
136667087Swpaul	}
136749076Swpaul
136849076Swpaul	/* Transmit */
136949076Swpaul	csr_write_4(sc, SF_TXDQ_PRODIDX,
137049076Swpaul	    (txprod & ~SF_TXDQ_PRODIDX_HIPRIO) |
137149076Swpaul	    ((i << 20) & 0xFFFF0000));
137249076Swpaul
137349076Swpaul	ifp->if_timer = 5;
137449076Swpaul
137567087Swpaul	SF_UNLOCK(sc);
137667087Swpaul
137749076Swpaul	return;
137849076Swpaul}
137949076Swpaul
138049076Swpaulstatic void sf_stop(sc)
138149076Swpaul	struct sf_softc		*sc;
138249076Swpaul{
138349076Swpaul	int			i;
138449077Swpaul	struct ifnet		*ifp;
138549076Swpaul
138667087Swpaul	SF_LOCK(sc);
138767087Swpaul
138849077Swpaul	ifp = &sc->arpcom.ac_if;
138949077Swpaul
139049076Swpaul	untimeout(sf_stats_update, sc, sc->sf_stat_ch);
139149076Swpaul
139249076Swpaul	csr_write_4(sc, SF_GEN_ETH_CTL, 0);
139349076Swpaul	csr_write_4(sc, SF_CQ_CONSIDX, 0);
139449076Swpaul	csr_write_4(sc, SF_CQ_PRODIDX, 0);
139549076Swpaul	csr_write_4(sc, SF_RXDQ_ADDR_Q1, 0);
139649076Swpaul	csr_write_4(sc, SF_RXDQ_CTL_1, 0);
139749076Swpaul	csr_write_4(sc, SF_RXDQ_PTR_Q1, 0);
139849076Swpaul	csr_write_4(sc, SF_TXCQ_CTL, 0);
139949076Swpaul	csr_write_4(sc, SF_TXDQ_ADDR_HIPRIO, 0);
140049076Swpaul	csr_write_4(sc, SF_TXDQ_CTL, 0);
140149076Swpaul	sf_reset(sc);
140249076Swpaul
140354161Swpaul	sc->sf_link = 0;
140454161Swpaul
140549076Swpaul	for (i = 0; i < SF_RX_DLIST_CNT; i++) {
140649076Swpaul		if (sc->sf_ldata->sf_rx_dlist_big[i].sf_mbuf != NULL) {
140749076Swpaul			m_freem(sc->sf_ldata->sf_rx_dlist_big[i].sf_mbuf);
140849076Swpaul			sc->sf_ldata->sf_rx_dlist_big[i].sf_mbuf = NULL;
140949076Swpaul		}
141049076Swpaul	}
141149076Swpaul
141249076Swpaul	for (i = 0; i < SF_TX_DLIST_CNT; i++) {
141349076Swpaul		if (sc->sf_ldata->sf_tx_dlist[i].sf_mbuf != NULL) {
141449076Swpaul			m_freem(sc->sf_ldata->sf_tx_dlist[i].sf_mbuf);
141549076Swpaul			sc->sf_ldata->sf_tx_dlist[i].sf_mbuf = NULL;
141649076Swpaul		}
141749076Swpaul	}
141849076Swpaul
141949077Swpaul	ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
142067087Swpaul	SF_UNLOCK(sc);
142149077Swpaul
142249076Swpaul	return;
142349076Swpaul}
142449076Swpaul
142549076Swpaul/*
142649076Swpaul * Note: it is important that this function not be interrupted. We
142749076Swpaul * use a two-stage register access scheme: if we are interrupted in
142849076Swpaul * between setting the indirect address register and reading from the
142949076Swpaul * indirect data register, the contents of the address register could
143049076Swpaul * be changed out from under us.
143149076Swpaul */
143249076Swpaulstatic void sf_stats_update(xsc)
143349076Swpaul	void			*xsc;
143449076Swpaul{
143549076Swpaul	struct sf_softc		*sc;
143649076Swpaul	struct ifnet		*ifp;
143750675Swpaul	struct mii_data		*mii;
143849076Swpaul	struct sf_stats		stats;
143949076Swpaul	u_int32_t		*ptr;
144067087Swpaul	int			i;
144149076Swpaul
144249076Swpaul	sc = xsc;
144367087Swpaul	SF_LOCK(sc);
144449076Swpaul	ifp = &sc->arpcom.ac_if;
144550675Swpaul	mii = device_get_softc(sc->sf_miibus);
144649076Swpaul
144749076Swpaul	ptr = (u_int32_t *)&stats;
144849076Swpaul	for (i = 0; i < sizeof(stats)/sizeof(u_int32_t); i++)
144949076Swpaul		ptr[i] = csr_read_4(sc, SF_STATS_BASE +
145049076Swpaul		    (i + sizeof(u_int32_t)));
145149076Swpaul
145249076Swpaul	for (i = 0; i < sizeof(stats)/sizeof(u_int32_t); i++)
145349076Swpaul		csr_write_4(sc, SF_STATS_BASE +
145449076Swpaul		    (i + sizeof(u_int32_t)), 0);
145549076Swpaul
145649076Swpaul	ifp->if_collisions += stats.sf_tx_single_colls +
145749076Swpaul	    stats.sf_tx_multi_colls + stats.sf_tx_excess_colls;
145849076Swpaul
145950675Swpaul	mii_tick(mii);
146054161Swpaul	if (!sc->sf_link) {
146154161Swpaul		mii_pollstat(mii);
146254161Swpaul		if (mii->mii_media_status & IFM_ACTIVE &&
146354161Swpaul		    IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)
146454161Swpaul			sc->sf_link++;
146554161Swpaul			if (ifp->if_snd.ifq_head != NULL)
146654161Swpaul				sf_start(ifp);
146754161Swpaul	}
146850675Swpaul
146949076Swpaul	sc->sf_stat_ch = timeout(sf_stats_update, sc, hz);
147049076Swpaul
147167087Swpaul	SF_UNLOCK(sc);
147249076Swpaul
147349076Swpaul	return;
147449076Swpaul}
147549076Swpaul
147649076Swpaulstatic void sf_watchdog(ifp)
147749076Swpaul	struct ifnet		*ifp;
147849076Swpaul{
147949076Swpaul	struct sf_softc		*sc;
148049076Swpaul
148149076Swpaul	sc = ifp->if_softc;
148249076Swpaul
148367087Swpaul	SF_LOCK(sc);
148467087Swpaul
148549076Swpaul	ifp->if_oerrors++;
148649076Swpaul	printf("sf%d: watchdog timeout\n", sc->sf_unit);
148749076Swpaul
148849076Swpaul	sf_stop(sc);
148949076Swpaul	sf_reset(sc);
149049076Swpaul	sf_init(sc);
149149076Swpaul
149249076Swpaul	if (ifp->if_snd.ifq_head != NULL)
149349076Swpaul		sf_start(ifp);
149449076Swpaul
149567087Swpaul	SF_UNLOCK(sc);
149667087Swpaul
149749076Swpaul	return;
149849076Swpaul}
149949076Swpaul
150049076Swpaulstatic void sf_shutdown(dev)
150149076Swpaul	device_t		dev;
150249076Swpaul{
150349076Swpaul	struct sf_softc		*sc;
150449076Swpaul
150549076Swpaul	sc = device_get_softc(dev);
150649076Swpaul
150749076Swpaul	sf_stop(sc);
150849076Swpaul
150949076Swpaul	return;
151049076Swpaul}
1511