if_sis.c revision 72084
150974Swpaul/*
250974Swpaul * Copyright (c) 1997, 1998, 1999
350974Swpaul *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
450974Swpaul *
550974Swpaul * Redistribution and use in source and binary forms, with or without
650974Swpaul * modification, are permitted provided that the following conditions
750974Swpaul * are met:
850974Swpaul * 1. Redistributions of source code must retain the above copyright
950974Swpaul *    notice, this list of conditions and the following disclaimer.
1050974Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1150974Swpaul *    notice, this list of conditions and the following disclaimer in the
1250974Swpaul *    documentation and/or other materials provided with the distribution.
1350974Swpaul * 3. All advertising materials mentioning features or use of this software
1450974Swpaul *    must display the following acknowledgement:
1550974Swpaul *	This product includes software developed by Bill Paul.
1650974Swpaul * 4. Neither the name of the author nor the names of any co-contributors
1750974Swpaul *    may be used to endorse or promote products derived from this software
1850974Swpaul *    without specific prior written permission.
1950974Swpaul *
2050974Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2150974Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2250974Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2350974Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2450974Swpaul * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2550974Swpaul * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2650974Swpaul * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2750974Swpaul * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2850974Swpaul * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2950974Swpaul * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3050974Swpaul * THE POSSIBILITY OF SUCH DAMAGE.
3150974Swpaul *
3250974Swpaul * $FreeBSD: head/sys/pci/if_sis.c 72084 2001-02-06 10:12:15Z phk $
3350974Swpaul */
3450974Swpaul
3550974Swpaul/*
3650974Swpaul * SiS 900/SiS 7016 fast ethernet PCI NIC driver. Datasheets are
3750974Swpaul * available from http://www.sis.com.tw.
3850974Swpaul *
3964963Swpaul * This driver also supports the NatSemi DP83815. Datasheets are
4064963Swpaul * available from http://www.national.com.
4164963Swpaul *
4250974Swpaul * Written by Bill Paul <wpaul@ee.columbia.edu>
4350974Swpaul * Electrical Engineering Department
4450974Swpaul * Columbia University, New York City
4550974Swpaul */
4650974Swpaul
4750974Swpaul/*
4850974Swpaul * The SiS 900 is a fairly simple chip. It uses bus master DMA with
4950974Swpaul * simple TX and RX descriptors of 3 longwords in size. The receiver
5050974Swpaul * has a single perfect filter entry for the station address and a
5150974Swpaul * 128-bit multicast hash table. The SiS 900 has a built-in MII-based
5250974Swpaul * transceiver while the 7016 requires an external transceiver chip.
5350974Swpaul * Both chips offer the standard bit-bang MII interface as well as
5450974Swpaul * an enchanced PHY interface which simplifies accessing MII registers.
5550974Swpaul *
5650974Swpaul * The only downside to this chipset is that RX descriptors must be
5750974Swpaul * longword aligned.
5850974Swpaul */
5950974Swpaul
6050974Swpaul#include <sys/param.h>
6150974Swpaul#include <sys/systm.h>
6250974Swpaul#include <sys/sockio.h>
6350974Swpaul#include <sys/mbuf.h>
6450974Swpaul#include <sys/malloc.h>
6550974Swpaul#include <sys/kernel.h>
6650974Swpaul#include <sys/socket.h>
6750974Swpaul
6850974Swpaul#include <net/if.h>
6950974Swpaul#include <net/if_arp.h>
7050974Swpaul#include <net/ethernet.h>
7150974Swpaul#include <net/if_dl.h>
7250974Swpaul#include <net/if_media.h>
7350974Swpaul
7450974Swpaul#include <net/bpf.h>
7550974Swpaul
7650974Swpaul#include <vm/vm.h>              /* for vtophys */
7750974Swpaul#include <vm/pmap.h>            /* for vtophys */
7850974Swpaul#include <machine/bus_pio.h>
7950974Swpaul#include <machine/bus_memio.h>
8050974Swpaul#include <machine/bus.h>
8150974Swpaul#include <machine/resource.h>
8250974Swpaul#include <sys/bus.h>
8350974Swpaul#include <sys/rman.h>
8450974Swpaul
8550974Swpaul#include <dev/mii/mii.h>
8650974Swpaul#include <dev/mii/miivar.h>
8750974Swpaul
8850974Swpaul#include <pci/pcireg.h>
8950974Swpaul#include <pci/pcivar.h>
9050974Swpaul
9150974Swpaul#define SIS_USEIOSPACE
9250974Swpaul
9350974Swpaul#include <pci/if_sisreg.h>
9450974Swpaul
9559758SpeterMODULE_DEPEND(sis, miibus, 1, 1, 1);
9659758Speter
9751089Speter/* "controller miibus0" required.  See GENERIC if you get errors here. */
9850974Swpaul#include "miibus_if.h"
9950974Swpaul
10050974Swpaul#ifndef lint
10150974Swpaulstatic const char rcsid[] =
10250974Swpaul  "$FreeBSD: head/sys/pci/if_sis.c 72084 2001-02-06 10:12:15Z phk $";
10350974Swpaul#endif
10450974Swpaul
10550974Swpaul/*
10650974Swpaul * Various supported device vendors/types and their names.
10750974Swpaul */
10850974Swpaulstatic struct sis_type sis_devs[] = {
10950974Swpaul	{ SIS_VENDORID, SIS_DEVICEID_900, "SiS 900 10/100BaseTX" },
11050974Swpaul	{ SIS_VENDORID, SIS_DEVICEID_7016, "SiS 7016 10/100BaseTX" },
11162672Swpaul	{ NS_VENDORID, NS_DEVICEID_DP83815, "NatSemi DP83815 10/100BaseTX" },
11250974Swpaul	{ 0, 0, NULL }
11350974Swpaul};
11450974Swpaul
11550974Swpaulstatic int sis_probe		__P((device_t));
11650974Swpaulstatic int sis_attach		__P((device_t));
11750974Swpaulstatic int sis_detach		__P((device_t));
11850974Swpaul
11950974Swpaulstatic int sis_newbuf		__P((struct sis_softc *,
12050974Swpaul					struct sis_desc *,
12150974Swpaul					struct mbuf *));
12250974Swpaulstatic int sis_encap		__P((struct sis_softc *,
12350974Swpaul					struct mbuf *, u_int32_t *));
12450974Swpaulstatic void sis_rxeof		__P((struct sis_softc *));
12550974Swpaulstatic void sis_rxeoc		__P((struct sis_softc *));
12650974Swpaulstatic void sis_txeof		__P((struct sis_softc *));
12750974Swpaulstatic void sis_intr		__P((void *));
12850974Swpaulstatic void sis_tick		__P((void *));
12950974Swpaulstatic void sis_start		__P((struct ifnet *));
13050974Swpaulstatic int sis_ioctl		__P((struct ifnet *, u_long, caddr_t));
13150974Swpaulstatic void sis_init		__P((void *));
13250974Swpaulstatic void sis_stop		__P((struct sis_softc *));
13350974Swpaulstatic void sis_watchdog		__P((struct ifnet *));
13450974Swpaulstatic void sis_shutdown		__P((device_t));
13550974Swpaulstatic int sis_ifmedia_upd	__P((struct ifnet *));
13650974Swpaulstatic void sis_ifmedia_sts	__P((struct ifnet *, struct ifmediareq *));
13750974Swpaul
13862672Swpaulstatic u_int16_t sis_reverse	__P((u_int16_t));
13950974Swpaulstatic void sis_delay		__P((struct sis_softc *));
14050974Swpaulstatic void sis_eeprom_idle	__P((struct sis_softc *));
14150974Swpaulstatic void sis_eeprom_putbyte	__P((struct sis_softc *, int));
14250974Swpaulstatic void sis_eeprom_getword	__P((struct sis_softc *, int, u_int16_t *));
14350974Swpaulstatic void sis_read_eeprom	__P((struct sis_softc *, caddr_t, int,
14450974Swpaul							int, int));
14550974Swpaulstatic int sis_miibus_readreg	__P((device_t, int, int));
14650974Swpaulstatic int sis_miibus_writereg	__P((device_t, int, int, int));
14750974Swpaulstatic void sis_miibus_statchg	__P((device_t));
14850974Swpaul
14962672Swpaulstatic void sis_setmulti_sis	__P((struct sis_softc *));
15062672Swpaulstatic void sis_setmulti_ns	__P((struct sis_softc *));
15162672Swpaulstatic u_int32_t sis_crc	__P((struct sis_softc *, caddr_t));
15250974Swpaulstatic void sis_reset		__P((struct sis_softc *));
15350974Swpaulstatic int sis_list_rx_init	__P((struct sis_softc *));
15450974Swpaulstatic int sis_list_tx_init	__P((struct sis_softc *));
15550974Swpaul
15650974Swpaul#ifdef SIS_USEIOSPACE
15750974Swpaul#define SIS_RES			SYS_RES_IOPORT
15850974Swpaul#define SIS_RID			SIS_PCI_LOIO
15950974Swpaul#else
16051030Swpaul#define SIS_RES			SYS_RES_MEMORY
16151030Swpaul#define SIS_RID			SIS_PCI_LOMEM
16250974Swpaul#endif
16350974Swpaul
16450974Swpaulstatic device_method_t sis_methods[] = {
16550974Swpaul	/* Device interface */
16650974Swpaul	DEVMETHOD(device_probe,		sis_probe),
16750974Swpaul	DEVMETHOD(device_attach,	sis_attach),
16850974Swpaul	DEVMETHOD(device_detach,	sis_detach),
16950974Swpaul	DEVMETHOD(device_shutdown,	sis_shutdown),
17050974Swpaul
17150974Swpaul	/* bus interface */
17250974Swpaul	DEVMETHOD(bus_print_child,	bus_generic_print_child),
17350974Swpaul	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
17450974Swpaul
17550974Swpaul	/* MII interface */
17650974Swpaul	DEVMETHOD(miibus_readreg,	sis_miibus_readreg),
17750974Swpaul	DEVMETHOD(miibus_writereg,	sis_miibus_writereg),
17850974Swpaul	DEVMETHOD(miibus_statchg,	sis_miibus_statchg),
17950974Swpaul
18050974Swpaul	{ 0, 0 }
18150974Swpaul};
18250974Swpaul
18350974Swpaulstatic driver_t sis_driver = {
18451455Swpaul	"sis",
18550974Swpaul	sis_methods,
18650974Swpaul	sizeof(struct sis_softc)
18750974Swpaul};
18850974Swpaul
18950974Swpaulstatic devclass_t sis_devclass;
19050974Swpaul
19151533SwpaulDRIVER_MODULE(if_sis, pci, sis_driver, sis_devclass, 0, 0);
19251473SwpaulDRIVER_MODULE(miibus, sis, miibus_driver, miibus_devclass, 0, 0);
19350974Swpaul
19450974Swpaul#define SIS_SETBIT(sc, reg, x)				\
19550974Swpaul	CSR_WRITE_4(sc, reg,				\
19650974Swpaul		CSR_READ_4(sc, reg) | (x))
19750974Swpaul
19850974Swpaul#define SIS_CLRBIT(sc, reg, x)				\
19950974Swpaul	CSR_WRITE_4(sc, reg,				\
20050974Swpaul		CSR_READ_4(sc, reg) & ~(x))
20150974Swpaul
20250974Swpaul#define SIO_SET(x)					\
20350974Swpaul	CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) | x)
20450974Swpaul
20550974Swpaul#define SIO_CLR(x)					\
20650974Swpaul	CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) & ~x)
20750974Swpaul
20862672Swpaul/*
20962672Swpaul * Routine to reverse the bits in a word. Stolen almost
21062672Swpaul * verbatim from /usr/games/fortune.
21162672Swpaul */
21262672Swpaulstatic u_int16_t sis_reverse(n)
21362672Swpaul	u_int16_t		n;
21462672Swpaul{
21562672Swpaul	n = ((n >>  1) & 0x5555) | ((n <<  1) & 0xaaaa);
21662672Swpaul	n = ((n >>  2) & 0x3333) | ((n <<  2) & 0xcccc);
21762672Swpaul	n = ((n >>  4) & 0x0f0f) | ((n <<  4) & 0xf0f0);
21862672Swpaul	n = ((n >>  8) & 0x00ff) | ((n <<  8) & 0xff00);
21962672Swpaul
22062672Swpaul	return(n);
22162672Swpaul}
22262672Swpaul
22350974Swpaulstatic void sis_delay(sc)
22450974Swpaul	struct sis_softc	*sc;
22550974Swpaul{
22650974Swpaul	int			idx;
22750974Swpaul
22850974Swpaul	for (idx = (300 / 33) + 1; idx > 0; idx--)
22950974Swpaul		CSR_READ_4(sc, SIS_CSR);
23050974Swpaul
23150974Swpaul	return;
23250974Swpaul}
23350974Swpaul
23450974Swpaulstatic void sis_eeprom_idle(sc)
23550974Swpaul	struct sis_softc	*sc;
23650974Swpaul{
23750974Swpaul	register int		i;
23850974Swpaul
23950974Swpaul	SIO_SET(SIS_EECTL_CSEL);
24050974Swpaul	sis_delay(sc);
24150974Swpaul	SIO_SET(SIS_EECTL_CLK);
24250974Swpaul	sis_delay(sc);
24350974Swpaul
24450974Swpaul	for (i = 0; i < 25; i++) {
24550974Swpaul		SIO_CLR(SIS_EECTL_CLK);
24650974Swpaul		sis_delay(sc);
24750974Swpaul		SIO_SET(SIS_EECTL_CLK);
24850974Swpaul		sis_delay(sc);
24950974Swpaul	}
25050974Swpaul
25150974Swpaul	SIO_CLR(SIS_EECTL_CLK);
25250974Swpaul	sis_delay(sc);
25350974Swpaul	SIO_CLR(SIS_EECTL_CSEL);
25450974Swpaul	sis_delay(sc);
25550974Swpaul	CSR_WRITE_4(sc, SIS_EECTL, 0x00000000);
25650974Swpaul
25750974Swpaul	return;
25850974Swpaul}
25950974Swpaul
26050974Swpaul/*
26150974Swpaul * Send a read command and address to the EEPROM, check for ACK.
26250974Swpaul */
26350974Swpaulstatic void sis_eeprom_putbyte(sc, addr)
26450974Swpaul	struct sis_softc	*sc;
26550974Swpaul	int			addr;
26650974Swpaul{
26750974Swpaul	register int		d, i;
26850974Swpaul
26950974Swpaul	d = addr | SIS_EECMD_READ;
27050974Swpaul
27150974Swpaul	/*
27250974Swpaul	 * Feed in each bit and stobe the clock.
27350974Swpaul	 */
27450974Swpaul	for (i = 0x400; i; i >>= 1) {
27550974Swpaul		if (d & i) {
27650974Swpaul			SIO_SET(SIS_EECTL_DIN);
27750974Swpaul		} else {
27850974Swpaul			SIO_CLR(SIS_EECTL_DIN);
27950974Swpaul		}
28050974Swpaul		sis_delay(sc);
28150974Swpaul		SIO_SET(SIS_EECTL_CLK);
28250974Swpaul		sis_delay(sc);
28350974Swpaul		SIO_CLR(SIS_EECTL_CLK);
28450974Swpaul		sis_delay(sc);
28550974Swpaul	}
28650974Swpaul
28750974Swpaul	return;
28850974Swpaul}
28950974Swpaul
29050974Swpaul/*
29150974Swpaul * Read a word of data stored in the EEPROM at address 'addr.'
29250974Swpaul */
29350974Swpaulstatic void sis_eeprom_getword(sc, addr, dest)
29450974Swpaul	struct sis_softc	*sc;
29550974Swpaul	int			addr;
29650974Swpaul	u_int16_t		*dest;
29750974Swpaul{
29850974Swpaul	register int		i;
29950974Swpaul	u_int16_t		word = 0;
30050974Swpaul
30150974Swpaul	/* Force EEPROM to idle state. */
30250974Swpaul	sis_eeprom_idle(sc);
30350974Swpaul
30450974Swpaul	/* Enter EEPROM access mode. */
30550974Swpaul	sis_delay(sc);
30662672Swpaul	SIO_CLR(SIS_EECTL_CLK);
30762672Swpaul	sis_delay(sc);
30850974Swpaul	SIO_SET(SIS_EECTL_CSEL);
30950974Swpaul	sis_delay(sc);
31050974Swpaul
31150974Swpaul	/*
31250974Swpaul	 * Send address of word we want to read.
31350974Swpaul	 */
31450974Swpaul	sis_eeprom_putbyte(sc, addr);
31550974Swpaul
31650974Swpaul	/*
31750974Swpaul	 * Start reading bits from EEPROM.
31850974Swpaul	 */
31950974Swpaul	for (i = 0x8000; i; i >>= 1) {
32050974Swpaul		SIO_SET(SIS_EECTL_CLK);
32150974Swpaul		sis_delay(sc);
32250974Swpaul		if (CSR_READ_4(sc, SIS_EECTL) & SIS_EECTL_DOUT)
32350974Swpaul			word |= i;
32450974Swpaul		sis_delay(sc);
32550974Swpaul		SIO_CLR(SIS_EECTL_CLK);
32650974Swpaul		sis_delay(sc);
32750974Swpaul	}
32850974Swpaul
32950974Swpaul	/* Turn off EEPROM access mode. */
33050974Swpaul	sis_eeprom_idle(sc);
33150974Swpaul
33250974Swpaul	*dest = word;
33350974Swpaul
33450974Swpaul	return;
33550974Swpaul}
33650974Swpaul
33750974Swpaul/*
33850974Swpaul * Read a sequence of words from the EEPROM.
33950974Swpaul */
34050974Swpaulstatic void sis_read_eeprom(sc, dest, off, cnt, swap)
34150974Swpaul	struct sis_softc	*sc;
34250974Swpaul	caddr_t			dest;
34350974Swpaul	int			off;
34450974Swpaul	int			cnt;
34550974Swpaul	int			swap;
34650974Swpaul{
34750974Swpaul	int			i;
34850974Swpaul	u_int16_t		word = 0, *ptr;
34950974Swpaul
35050974Swpaul	for (i = 0; i < cnt; i++) {
35150974Swpaul		sis_eeprom_getword(sc, off + i, &word);
35250974Swpaul		ptr = (u_int16_t *)(dest + (i * 2));
35350974Swpaul		if (swap)
35450974Swpaul			*ptr = ntohs(word);
35550974Swpaul		else
35650974Swpaul			*ptr = word;
35750974Swpaul	}
35850974Swpaul
35950974Swpaul	return;
36050974Swpaul}
36150974Swpaul
36250974Swpaulstatic int sis_miibus_readreg(dev, phy, reg)
36350974Swpaul	device_t		dev;
36450974Swpaul	int			phy, reg;
36550974Swpaul{
36650974Swpaul	struct sis_softc	*sc;
36762672Swpaul	int			i, val = 0;
36850974Swpaul
36950974Swpaul	sc = device_get_softc(dev);
37050974Swpaul
37162672Swpaul	if (sc->sis_type == SIS_TYPE_83815) {
37262672Swpaul		if (phy != 0)
37362672Swpaul			return(0);
37462672Swpaul		/*
37562672Swpaul		 * The NatSemi chip can take a while after
37662672Swpaul		 * a reset to come ready, during which the BMSR
37762672Swpaul		 * returns a value of 0. This is *never* supposed
37862672Swpaul		 * to happen: some of the BMSR bits are meant to
37962672Swpaul		 * be hardwired in the on position, and this can
38062672Swpaul		 * confuse the miibus code a bit during the probe
38162672Swpaul		 * and attach phase. So we make an effort to check
38262672Swpaul		 * for this condition and wait for it to clear.
38362672Swpaul		 */
38462672Swpaul		if (!CSR_READ_4(sc, NS_BMSR))
38562672Swpaul			DELAY(1000);
38662672Swpaul		val = CSR_READ_4(sc, NS_BMCR + (reg * 4));
38762672Swpaul		return(val);
38862672Swpaul	}
38962672Swpaul
39050974Swpaul	if (sc->sis_type == SIS_TYPE_900 && phy != 0)
39150974Swpaul		return(0);
39250974Swpaul
39350974Swpaul	CSR_WRITE_4(sc, SIS_PHYCTL, (phy << 11) | (reg << 6) | SIS_PHYOP_READ);
39450974Swpaul	SIS_SETBIT(sc, SIS_PHYCTL, SIS_PHYCTL_ACCESS);
39550974Swpaul
39650974Swpaul	for (i = 0; i < SIS_TIMEOUT; i++) {
39750974Swpaul		if (!(CSR_READ_4(sc, SIS_PHYCTL) & SIS_PHYCTL_ACCESS))
39850974Swpaul			break;
39950974Swpaul	}
40050974Swpaul
40150974Swpaul	if (i == SIS_TIMEOUT) {
40250974Swpaul		printf("sis%d: PHY failed to come ready\n", sc->sis_unit);
40350974Swpaul		return(0);
40450974Swpaul	}
40550974Swpaul
40650974Swpaul	val = (CSR_READ_4(sc, SIS_PHYCTL) >> 16) & 0xFFFF;
40750974Swpaul
40850974Swpaul	if (val == 0xFFFF)
40950974Swpaul		return(0);
41050974Swpaul
41150974Swpaul	return(val);
41250974Swpaul}
41350974Swpaul
41450974Swpaulstatic int sis_miibus_writereg(dev, phy, reg, data)
41550974Swpaul	device_t		dev;
41650974Swpaul	int			phy, reg, data;
41750974Swpaul{
41850974Swpaul	struct sis_softc	*sc;
41950974Swpaul	int			i;
42050974Swpaul
42150974Swpaul	sc = device_get_softc(dev);
42250974Swpaul
42362672Swpaul	if (sc->sis_type == SIS_TYPE_83815) {
42462672Swpaul		if (phy != 0)
42562672Swpaul			return(0);
42662672Swpaul		CSR_WRITE_4(sc, NS_BMCR + (reg * 4), data);
42762672Swpaul		return(0);
42862672Swpaul	}
42962672Swpaul
43050974Swpaul	if (sc->sis_type == SIS_TYPE_900 && phy != 0)
43150974Swpaul		return(0);
43250974Swpaul
43350974Swpaul	CSR_WRITE_4(sc, SIS_PHYCTL, (data << 16) | (phy << 11) |
43450974Swpaul	    (reg << 6) | SIS_PHYOP_WRITE);
43550974Swpaul	SIS_SETBIT(sc, SIS_PHYCTL, SIS_PHYCTL_ACCESS);
43650974Swpaul
43750974Swpaul	for (i = 0; i < SIS_TIMEOUT; i++) {
43850974Swpaul		if (!(CSR_READ_4(sc, SIS_PHYCTL) & SIS_PHYCTL_ACCESS))
43950974Swpaul			break;
44050974Swpaul	}
44150974Swpaul
44250974Swpaul	if (i == SIS_TIMEOUT)
44350974Swpaul		printf("sis%d: PHY failed to come ready\n", sc->sis_unit);
44450974Swpaul
44550974Swpaul	return(0);
44650974Swpaul}
44750974Swpaul
44850974Swpaulstatic void sis_miibus_statchg(dev)
44950974Swpaul	device_t		dev;
45050974Swpaul{
45150974Swpaul	struct sis_softc	*sc;
45250974Swpaul
45350974Swpaul	sc = device_get_softc(dev);
45464963Swpaul	sis_init(sc);
45550974Swpaul
45650974Swpaul	return;
45750974Swpaul}
45850974Swpaul
45962672Swpaulstatic u_int32_t sis_crc(sc, addr)
46062672Swpaul	struct sis_softc	*sc;
46150974Swpaul	caddr_t			addr;
46250974Swpaul{
46350974Swpaul	u_int32_t		crc, carry;
46450974Swpaul	int			i, j;
46550974Swpaul	u_int8_t		c;
46650974Swpaul
46750974Swpaul	/* Compute CRC for the address value. */
46850974Swpaul	crc = 0xFFFFFFFF; /* initial value */
46950974Swpaul
47050974Swpaul	for (i = 0; i < 6; i++) {
47150974Swpaul		c = *(addr + i);
47250974Swpaul		for (j = 0; j < 8; j++) {
47350974Swpaul			carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01);
47450974Swpaul			crc <<= 1;
47550974Swpaul			c >>= 1;
47650974Swpaul			if (carry)
47750974Swpaul				crc = (crc ^ 0x04c11db6) | carry;
47850974Swpaul		}
47950974Swpaul	}
48050974Swpaul
48162672Swpaul	/*
48262672Swpaul	 * return the filter bit position
48362672Swpaul	 *
48462672Swpaul	 * The NatSemi chip has a 512-bit filter, which is
48562672Swpaul	 * different than the SiS, so we special-case it.
48662672Swpaul	 */
48762672Swpaul	if (sc->sis_type == SIS_TYPE_83815)
48862672Swpaul		return((crc >> 23) & 0x1FF);
48962672Swpaul
49050974Swpaul	return((crc >> 25) & 0x0000007F);
49150974Swpaul}
49250974Swpaul
49362672Swpaulstatic void sis_setmulti_ns(sc)
49450974Swpaul	struct sis_softc	*sc;
49550974Swpaul{
49650974Swpaul	struct ifnet		*ifp;
49750974Swpaul	struct ifmultiaddr	*ifma;
49850974Swpaul	u_int32_t		h = 0, i, filtsave;
49962672Swpaul	int			bit, index;
50050974Swpaul
50150974Swpaul	ifp = &sc->arpcom.ac_if;
50250974Swpaul
50350974Swpaul	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
50462672Swpaul		SIS_CLRBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH);
50550974Swpaul		SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI);
50650974Swpaul		return;
50750974Swpaul	}
50850974Swpaul
50962672Swpaul	/*
51062672Swpaul	 * We have to explicitly enable the multicast hash table
51162672Swpaul	 * on the NatSemi chip if we want to use it, which we do.
51262672Swpaul	 */
51362672Swpaul	SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH);
51450974Swpaul	SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI);
51550974Swpaul
51650974Swpaul	filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL);
51750974Swpaul
51850974Swpaul	/* first, zot all the existing hash bits */
51962672Swpaul	for (i = 0; i < 32; i++) {
52062672Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + (i*2));
52162672Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_DATA, 0);
52262672Swpaul	}
52362672Swpaul
52472084Sphk	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
52562672Swpaul		if (ifma->ifma_addr->sa_family != AF_LINK)
52662672Swpaul			continue;
52762672Swpaul		h = sis_crc(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
52862672Swpaul		index = h >> 3;
52962672Swpaul		bit = h & 0x1F;
53062672Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + index);
53162672Swpaul		if (bit > 0xF)
53262672Swpaul			bit -= 0x10;
53362672Swpaul		SIS_SETBIT(sc, SIS_RXFILT_DATA, (1 << bit));
53462672Swpaul	}
53562672Swpaul
53662672Swpaul	CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave);
53762672Swpaul
53862672Swpaul	return;
53962672Swpaul}
54062672Swpaul
54162672Swpaulstatic void sis_setmulti_sis(sc)
54262672Swpaul	struct sis_softc	*sc;
54362672Swpaul{
54462672Swpaul	struct ifnet		*ifp;
54562672Swpaul	struct ifmultiaddr	*ifma;
54662672Swpaul	u_int32_t		h = 0, i, filtsave;
54762672Swpaul
54862672Swpaul	ifp = &sc->arpcom.ac_if;
54962672Swpaul
55062672Swpaul	if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) {
55162672Swpaul		SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI);
55262672Swpaul		return;
55362672Swpaul	}
55462672Swpaul
55562672Swpaul	SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI);
55662672Swpaul
55762672Swpaul	filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL);
55862672Swpaul
55962672Swpaul	/* first, zot all the existing hash bits */
56050974Swpaul	for (i = 0; i < 8; i++) {
56150974Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_CTL, (4 + ((i * 16) >> 4)) << 16);
56250974Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_DATA, 0);
56350974Swpaul	}
56450974Swpaul
56550974Swpaul	/* now program new ones */
56672084Sphk	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
56750974Swpaul		if (ifma->ifma_addr->sa_family != AF_LINK)
56850974Swpaul			continue;
56962672Swpaul		h = sis_crc(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr));
57050974Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_CTL, (4 + (h >> 4)) << 16);
57150974Swpaul		SIS_SETBIT(sc, SIS_RXFILT_DATA, (1 << (h & 0xF)));
57250974Swpaul	}
57350974Swpaul
57450974Swpaul	CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave);
57550974Swpaul
57650974Swpaul	return;
57750974Swpaul}
57850974Swpaul
57950974Swpaulstatic void sis_reset(sc)
58050974Swpaul	struct sis_softc	*sc;
58150974Swpaul{
58250974Swpaul	register int		i;
58350974Swpaul
58450974Swpaul	SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RESET);
58550974Swpaul
58650974Swpaul	for (i = 0; i < SIS_TIMEOUT; i++) {
58750974Swpaul		if (!(CSR_READ_4(sc, SIS_CSR) & SIS_CSR_RESET))
58850974Swpaul			break;
58950974Swpaul	}
59050974Swpaul
59150974Swpaul	if (i == SIS_TIMEOUT)
59250974Swpaul		printf("sis%d: reset never completed\n", sc->sis_unit);
59350974Swpaul
59450974Swpaul	/* Wait a little while for the chip to get its brains in order. */
59550974Swpaul	DELAY(1000);
59650974Swpaul        return;
59750974Swpaul}
59850974Swpaul
59950974Swpaul/*
60050974Swpaul * Probe for an SiS chip. Check the PCI vendor and device
60150974Swpaul * IDs against our list and return a device name if we find a match.
60250974Swpaul */
60350974Swpaulstatic int sis_probe(dev)
60450974Swpaul	device_t		dev;
60550974Swpaul{
60650974Swpaul	struct sis_type		*t;
60750974Swpaul
60850974Swpaul	t = sis_devs;
60950974Swpaul
61050974Swpaul	while(t->sis_name != NULL) {
61150974Swpaul		if ((pci_get_vendor(dev) == t->sis_vid) &&
61250974Swpaul		    (pci_get_device(dev) == t->sis_did)) {
61350974Swpaul			device_set_desc(dev, t->sis_name);
61450974Swpaul			return(0);
61550974Swpaul		}
61650974Swpaul		t++;
61750974Swpaul	}
61850974Swpaul
61950974Swpaul	return(ENXIO);
62050974Swpaul}
62150974Swpaul
62250974Swpaul/*
62350974Swpaul * Attach the interface. Allocate softc structures, do ifmedia
62450974Swpaul * setup and ethernet/BPF attach.
62550974Swpaul */
62650974Swpaulstatic int sis_attach(dev)
62750974Swpaul	device_t		dev;
62850974Swpaul{
62950974Swpaul	u_char			eaddr[ETHER_ADDR_LEN];
63050974Swpaul	u_int32_t		command;
63150974Swpaul	struct sis_softc	*sc;
63250974Swpaul	struct ifnet		*ifp;
63350974Swpaul	int			unit, error = 0, rid;
63450974Swpaul
63550974Swpaul	sc = device_get_softc(dev);
63650974Swpaul	unit = device_get_unit(dev);
63750974Swpaul	bzero(sc, sizeof(struct sis_softc));
63850974Swpaul
63971228Sbmilekic	mtx_init(&sc->sis_mtx, device_get_nameunit(dev), MTX_DEF | MTX_RECURSE);
64069583Swpaul	SIS_LOCK(sc);
64169583Swpaul
64250974Swpaul	if (pci_get_device(dev) == SIS_DEVICEID_900)
64350974Swpaul		sc->sis_type = SIS_TYPE_900;
64450974Swpaul	if (pci_get_device(dev) == SIS_DEVICEID_7016)
64550974Swpaul		sc->sis_type = SIS_TYPE_7016;
64662672Swpaul	if (pci_get_vendor(dev) == NS_VENDORID)
64762672Swpaul		sc->sis_type = SIS_TYPE_83815;
64850974Swpaul
64950974Swpaul	/*
65050974Swpaul	 * Handle power management nonsense.
65150974Swpaul	 */
65250974Swpaul
65350974Swpaul	command = pci_read_config(dev, SIS_PCI_CAPID, 4) & 0x000000FF;
65450974Swpaul	if (command == 0x01) {
65550974Swpaul
65650974Swpaul		command = pci_read_config(dev, SIS_PCI_PWRMGMTCTRL, 4);
65750974Swpaul		if (command & SIS_PSTATE_MASK) {
65850974Swpaul			u_int32_t		iobase, membase, irq;
65950974Swpaul
66050974Swpaul			/* Save important PCI config data. */
66150974Swpaul			iobase = pci_read_config(dev, SIS_PCI_LOIO, 4);
66250974Swpaul			membase = pci_read_config(dev, SIS_PCI_LOMEM, 4);
66350974Swpaul			irq = pci_read_config(dev, SIS_PCI_INTLINE, 4);
66450974Swpaul
66550974Swpaul			/* Reset the power state. */
66650974Swpaul			printf("sis%d: chip is in D%d power mode "
66750974Swpaul			"-- setting to D0\n", unit, command & SIS_PSTATE_MASK);
66850974Swpaul			command &= 0xFFFFFFFC;
66950974Swpaul			pci_write_config(dev, SIS_PCI_PWRMGMTCTRL, command, 4);
67050974Swpaul
67150974Swpaul			/* Restore PCI config data. */
67250974Swpaul			pci_write_config(dev, SIS_PCI_LOIO, iobase, 4);
67350974Swpaul			pci_write_config(dev, SIS_PCI_LOMEM, membase, 4);
67450974Swpaul			pci_write_config(dev, SIS_PCI_INTLINE, irq, 4);
67550974Swpaul		}
67650974Swpaul	}
67750974Swpaul
67850974Swpaul	/*
67950974Swpaul	 * Map control/status registers.
68050974Swpaul	 */
68161041Speter	command = pci_read_config(dev, PCIR_COMMAND, 4);
68250974Swpaul	command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
68361041Speter	pci_write_config(dev, PCIR_COMMAND, command, 4);
68461041Speter	command = pci_read_config(dev, PCIR_COMMAND, 4);
68550974Swpaul
68650974Swpaul#ifdef SIS_USEIOSPACE
68750974Swpaul	if (!(command & PCIM_CMD_PORTEN)) {
68850974Swpaul		printf("sis%d: failed to enable I/O ports!\n", unit);
68950974Swpaul		error = ENXIO;;
69050974Swpaul		goto fail;
69150974Swpaul	}
69250974Swpaul#else
69350974Swpaul	if (!(command & PCIM_CMD_MEMEN)) {
69450974Swpaul		printf("sis%d: failed to enable memory mapping!\n", unit);
69550974Swpaul		error = ENXIO;;
69650974Swpaul		goto fail;
69750974Swpaul	}
69850974Swpaul#endif
69950974Swpaul
70050974Swpaul	rid = SIS_RID;
70150974Swpaul	sc->sis_res = bus_alloc_resource(dev, SIS_RES, &rid,
70250974Swpaul	    0, ~0, 1, RF_ACTIVE);
70350974Swpaul
70450974Swpaul	if (sc->sis_res == NULL) {
70550974Swpaul		printf("sis%d: couldn't map ports/memory\n", unit);
70650974Swpaul		error = ENXIO;
70750974Swpaul		goto fail;
70850974Swpaul	}
70950974Swpaul
71050974Swpaul	sc->sis_btag = rman_get_bustag(sc->sis_res);
71150974Swpaul	sc->sis_bhandle = rman_get_bushandle(sc->sis_res);
71250974Swpaul
71350974Swpaul	/* Allocate interrupt */
71450974Swpaul	rid = 0;
71550974Swpaul	sc->sis_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1,
71650974Swpaul	    RF_SHAREABLE | RF_ACTIVE);
71750974Swpaul
71850974Swpaul	if (sc->sis_irq == NULL) {
71950974Swpaul		printf("sis%d: couldn't map interrupt\n", unit);
72050974Swpaul		bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res);
72150974Swpaul		error = ENXIO;
72250974Swpaul		goto fail;
72350974Swpaul	}
72450974Swpaul
72550974Swpaul	error = bus_setup_intr(dev, sc->sis_irq, INTR_TYPE_NET,
72650974Swpaul	    sis_intr, sc, &sc->sis_intrhand);
72750974Swpaul
72850974Swpaul	if (error) {
72968216Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq);
73050974Swpaul		bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res);
73150974Swpaul		printf("sis%d: couldn't set up irq\n", unit);
73250974Swpaul		goto fail;
73350974Swpaul	}
73450974Swpaul
73550974Swpaul	/* Reset the adapter. */
73650974Swpaul	sis_reset(sc);
73750974Swpaul
73850974Swpaul	/*
73950974Swpaul	 * Get station address from the EEPROM.
74050974Swpaul	 */
74162672Swpaul	switch (pci_get_vendor(dev)) {
74262672Swpaul	case NS_VENDORID:
74362672Swpaul		/*
74462672Swpaul		 * Reading the MAC address out of the EEPROM on
74562672Swpaul		 * the NatSemi chip takes a bit more work than
74662672Swpaul		 * you'd expect. The address spans 4 16-bit words,
74762672Swpaul		 * with the first word containing only a single bit.
74862672Swpaul		 * You have to shift everything over one bit to
74962672Swpaul		 * get it aligned properly. Also, the bits are
75062672Swpaul		 * stored backwards (the LSB is really the MSB,
75162672Swpaul		 * and so on) so you have to reverse them in order
75262672Swpaul		 * to get the MAC address into the form we want.
75362672Swpaul		 * Why? Who the hell knows.
75462672Swpaul		 */
75562672Swpaul		{
75662672Swpaul			u_int16_t		tmp[4];
75750974Swpaul
75862672Swpaul			sis_read_eeprom(sc, (caddr_t)&tmp,
75962672Swpaul			    NS_EE_NODEADDR, 4, 0);
76062672Swpaul
76162672Swpaul			/* Shift everything over one bit. */
76262672Swpaul			tmp[3] = tmp[3] >> 1;
76362681Swpaul			tmp[3] |= tmp[2] << 15;
76462672Swpaul			tmp[2] = tmp[2] >> 1;
76562681Swpaul			tmp[2] |= tmp[1] << 15;
76662672Swpaul			tmp[1] = tmp[1] >> 1;
76762681Swpaul			tmp[1] |= tmp[0] << 15;
76862672Swpaul
76962672Swpaul			/* Now reverse all the bits. */
77062672Swpaul			tmp[3] = sis_reverse(tmp[3]);
77162672Swpaul			tmp[2] = sis_reverse(tmp[2]);
77262672Swpaul			tmp[1] = sis_reverse(tmp[1]);
77362672Swpaul
77462672Swpaul			bcopy((char *)&tmp[1], eaddr, ETHER_ADDR_LEN);
77562672Swpaul		}
77662672Swpaul		break;
77762672Swpaul	case SIS_VENDORID:
77862672Swpaul	default:
77962672Swpaul		sis_read_eeprom(sc, (caddr_t)&eaddr, SIS_EE_NODEADDR, 3, 0);
78062672Swpaul		break;
78162672Swpaul	}
78262672Swpaul
78350974Swpaul	/*
78450974Swpaul	 * A SiS chip was detected. Inform the world.
78550974Swpaul	 */
78650974Swpaul	printf("sis%d: Ethernet address: %6D\n", unit, eaddr, ":");
78750974Swpaul
78850974Swpaul	sc->sis_unit = unit;
78950974Swpaul	callout_handle_init(&sc->sis_stat_ch);
79050974Swpaul	bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
79150974Swpaul
79250974Swpaul	sc->sis_ldata = contigmalloc(sizeof(struct sis_list_data), M_DEVBUF,
79351657Swpaul	    M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0);
79450974Swpaul
79550974Swpaul	if (sc->sis_ldata == NULL) {
79650974Swpaul		printf("sis%d: no memory for list buffers!\n", unit);
79750974Swpaul		bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand);
79850974Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq);
79950974Swpaul		bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res);
80050974Swpaul		error = ENXIO;
80150974Swpaul		goto fail;
80250974Swpaul	}
80350974Swpaul	bzero(sc->sis_ldata, sizeof(struct sis_list_data));
80450974Swpaul
80550974Swpaul	ifp = &sc->arpcom.ac_if;
80650974Swpaul	ifp->if_softc = sc;
80750974Swpaul	ifp->if_unit = unit;
80850974Swpaul	ifp->if_name = "sis";
80950974Swpaul	ifp->if_mtu = ETHERMTU;
81050974Swpaul	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
81150974Swpaul	ifp->if_ioctl = sis_ioctl;
81250974Swpaul	ifp->if_output = ether_output;
81350974Swpaul	ifp->if_start = sis_start;
81450974Swpaul	ifp->if_watchdog = sis_watchdog;
81550974Swpaul	ifp->if_init = sis_init;
81650974Swpaul	ifp->if_baudrate = 10000000;
81750974Swpaul	ifp->if_snd.ifq_maxlen = SIS_TX_LIST_CNT - 1;
81850974Swpaul
81950974Swpaul	/*
82050974Swpaul	 * Do MII setup.
82150974Swpaul	 */
82250974Swpaul	if (mii_phy_probe(dev, &sc->sis_miibus,
82350974Swpaul	    sis_ifmedia_upd, sis_ifmedia_sts)) {
82450974Swpaul		printf("sis%d: MII without any PHY!\n", sc->sis_unit);
82550974Swpaul		bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand);
82650974Swpaul		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq);
82750974Swpaul		bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res);
82850974Swpaul		error = ENXIO;
82950974Swpaul		goto fail;
83050974Swpaul	}
83150974Swpaul
83250974Swpaul	/*
83363090Sarchie	 * Call MI attach routine.
83450974Swpaul	 */
83563090Sarchie	ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
83650974Swpaul	callout_handle_init(&sc->sis_stat_ch);
83767087Swpaul	SIS_UNLOCK(sc);
83867087Swpaul	return(0);
83950974Swpaul
84050974Swpaulfail:
84167087Swpaul	SIS_UNLOCK(sc);
84267087Swpaul	mtx_destroy(&sc->sis_mtx);
84350974Swpaul	return(error);
84450974Swpaul}
84550974Swpaul
84650974Swpaulstatic int sis_detach(dev)
84750974Swpaul	device_t		dev;
84850974Swpaul{
84950974Swpaul	struct sis_softc	*sc;
85050974Swpaul	struct ifnet		*ifp;
85150974Swpaul
85250974Swpaul
85350974Swpaul	sc = device_get_softc(dev);
85467087Swpaul	SIS_LOCK(sc);
85550974Swpaul	ifp = &sc->arpcom.ac_if;
85650974Swpaul
85750974Swpaul	sis_reset(sc);
85850974Swpaul	sis_stop(sc);
85963090Sarchie	ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
86050974Swpaul
86150974Swpaul	bus_generic_detach(dev);
86250974Swpaul	device_delete_child(dev, sc->sis_miibus);
86350974Swpaul
86450974Swpaul	bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand);
86550974Swpaul	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq);
86650974Swpaul	bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res);
86750974Swpaul
86850974Swpaul	contigfree(sc->sis_ldata, sizeof(struct sis_list_data), M_DEVBUF);
86950974Swpaul
87067087Swpaul	SIS_UNLOCK(sc);
87167087Swpaul	mtx_destroy(&sc->sis_mtx);
87250974Swpaul
87350974Swpaul	return(0);
87450974Swpaul}
87550974Swpaul
87650974Swpaul/*
87750974Swpaul * Initialize the transmit descriptors.
87850974Swpaul */
87950974Swpaulstatic int sis_list_tx_init(sc)
88050974Swpaul	struct sis_softc	*sc;
88150974Swpaul{
88250974Swpaul	struct sis_list_data	*ld;
88350974Swpaul	struct sis_ring_data	*cd;
88450974Swpaul	int			i;
88550974Swpaul
88650974Swpaul	cd = &sc->sis_cdata;
88750974Swpaul	ld = sc->sis_ldata;
88850974Swpaul
88950974Swpaul	for (i = 0; i < SIS_TX_LIST_CNT; i++) {
89050974Swpaul		if (i == (SIS_TX_LIST_CNT - 1)) {
89150974Swpaul			ld->sis_tx_list[i].sis_nextdesc =
89250974Swpaul			    &ld->sis_tx_list[0];
89350974Swpaul			ld->sis_tx_list[i].sis_next =
89450974Swpaul			    vtophys(&ld->sis_tx_list[0]);
89550974Swpaul		} else {
89650974Swpaul			ld->sis_tx_list[i].sis_nextdesc =
89750974Swpaul			    &ld->sis_tx_list[i + 1];
89850974Swpaul			ld->sis_tx_list[i].sis_next =
89950974Swpaul			    vtophys(&ld->sis_tx_list[i + 1]);
90050974Swpaul		}
90150974Swpaul		ld->sis_tx_list[i].sis_mbuf = NULL;
90250974Swpaul		ld->sis_tx_list[i].sis_ptr = 0;
90350974Swpaul		ld->sis_tx_list[i].sis_ctl = 0;
90450974Swpaul	}
90550974Swpaul
90650974Swpaul	cd->sis_tx_prod = cd->sis_tx_cons = cd->sis_tx_cnt = 0;
90750974Swpaul
90850974Swpaul	return(0);
90950974Swpaul}
91050974Swpaul
91150974Swpaul
91250974Swpaul/*
91350974Swpaul * Initialize the RX descriptors and allocate mbufs for them. Note that
91450974Swpaul * we arrange the descriptors in a closed ring, so that the last descriptor
91550974Swpaul * points back to the first.
91650974Swpaul */
91750974Swpaulstatic int sis_list_rx_init(sc)
91850974Swpaul	struct sis_softc	*sc;
91950974Swpaul{
92050974Swpaul	struct sis_list_data	*ld;
92150974Swpaul	struct sis_ring_data	*cd;
92250974Swpaul	int			i;
92350974Swpaul
92450974Swpaul	ld = sc->sis_ldata;
92550974Swpaul	cd = &sc->sis_cdata;
92650974Swpaul
92750974Swpaul	for (i = 0; i < SIS_RX_LIST_CNT; i++) {
92850974Swpaul		if (sis_newbuf(sc, &ld->sis_rx_list[i], NULL) == ENOBUFS)
92950974Swpaul			return(ENOBUFS);
93050974Swpaul		if (i == (SIS_RX_LIST_CNT - 1)) {
93150974Swpaul			ld->sis_rx_list[i].sis_nextdesc =
93250974Swpaul			    &ld->sis_rx_list[0];
93350974Swpaul			ld->sis_rx_list[i].sis_next =
93450974Swpaul			    vtophys(&ld->sis_rx_list[0]);
93550974Swpaul		} else {
93650974Swpaul			ld->sis_rx_list[i].sis_nextdesc =
93750974Swpaul			    &ld->sis_rx_list[i + 1];
93850974Swpaul			ld->sis_rx_list[i].sis_next =
93950974Swpaul			    vtophys(&ld->sis_rx_list[i + 1]);
94050974Swpaul		}
94150974Swpaul	}
94250974Swpaul
94350974Swpaul	cd->sis_rx_prod = 0;
94450974Swpaul
94550974Swpaul	return(0);
94650974Swpaul}
94750974Swpaul
94850974Swpaul/*
94950974Swpaul * Initialize an RX descriptor and attach an MBUF cluster.
95050974Swpaul */
95150974Swpaulstatic int sis_newbuf(sc, c, m)
95250974Swpaul	struct sis_softc	*sc;
95350974Swpaul	struct sis_desc		*c;
95450974Swpaul	struct mbuf		*m;
95550974Swpaul{
95650974Swpaul	struct mbuf		*m_new = NULL;
95750974Swpaul
95850974Swpaul	if (m == NULL) {
95950974Swpaul		MGETHDR(m_new, M_DONTWAIT, MT_DATA);
96050974Swpaul		if (m_new == NULL) {
96150974Swpaul			printf("sis%d: no memory for rx list "
96250974Swpaul			    "-- packet dropped!\n", sc->sis_unit);
96350974Swpaul			return(ENOBUFS);
96450974Swpaul		}
96550974Swpaul
96650974Swpaul		MCLGET(m_new, M_DONTWAIT);
96750974Swpaul		if (!(m_new->m_flags & M_EXT)) {
96850974Swpaul			printf("sis%d: no memory for rx list "
96950974Swpaul			    "-- packet dropped!\n", sc->sis_unit);
97050974Swpaul			m_freem(m_new);
97150974Swpaul			return(ENOBUFS);
97250974Swpaul		}
97350974Swpaul		m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
97450974Swpaul	} else {
97550974Swpaul		m_new = m;
97650974Swpaul		m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
97750974Swpaul		m_new->m_data = m_new->m_ext.ext_buf;
97850974Swpaul	}
97950974Swpaul
98050974Swpaul	m_adj(m_new, sizeof(u_int64_t));
98150974Swpaul
98250974Swpaul	c->sis_mbuf = m_new;
98350974Swpaul	c->sis_ptr = vtophys(mtod(m_new, caddr_t));
98450974Swpaul	c->sis_ctl = SIS_RXLEN;
98550974Swpaul
98650974Swpaul	return(0);
98750974Swpaul}
98850974Swpaul
98950974Swpaul/*
99050974Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to
99150974Swpaul * the higher level protocols.
99250974Swpaul */
99350974Swpaulstatic void sis_rxeof(sc)
99450974Swpaul	struct sis_softc	*sc;
99550974Swpaul{
99650974Swpaul        struct ether_header	*eh;
99750974Swpaul        struct mbuf		*m;
99850974Swpaul        struct ifnet		*ifp;
99950974Swpaul	struct sis_desc		*cur_rx;
100050974Swpaul	int			i, total_len = 0;
100150974Swpaul	u_int32_t		rxstat;
100250974Swpaul
100350974Swpaul	ifp = &sc->arpcom.ac_if;
100450974Swpaul	i = sc->sis_cdata.sis_rx_prod;
100550974Swpaul
100650974Swpaul	while(SIS_OWNDESC(&sc->sis_ldata->sis_rx_list[i])) {
100750974Swpaul		struct mbuf		*m0 = NULL;
100850974Swpaul
100950974Swpaul		cur_rx = &sc->sis_ldata->sis_rx_list[i];
101050974Swpaul		rxstat = cur_rx->sis_rxstat;
101150974Swpaul		m = cur_rx->sis_mbuf;
101250974Swpaul		cur_rx->sis_mbuf = NULL;
101350974Swpaul		total_len = SIS_RXBYTES(cur_rx);
101450974Swpaul		SIS_INC(i, SIS_RX_LIST_CNT);
101550974Swpaul
101650974Swpaul		/*
101750974Swpaul		 * If an error occurs, update stats, clear the
101850974Swpaul		 * status word and leave the mbuf cluster in place:
101950974Swpaul		 * it should simply get re-used next time this descriptor
102050974Swpaul	 	 * comes up in the ring.
102150974Swpaul		 */
102250974Swpaul		if (!(rxstat & SIS_CMDSTS_PKT_OK)) {
102350974Swpaul			ifp->if_ierrors++;
102450974Swpaul			if (rxstat & SIS_RXSTAT_COLL)
102550974Swpaul				ifp->if_collisions++;
102650974Swpaul			sis_newbuf(sc, cur_rx, m);
102750974Swpaul			continue;
102850974Swpaul		}
102950974Swpaul
103050974Swpaul		/* No errors; receive the packet. */
103150974Swpaul		m0 = m_devget(mtod(m, char *) - ETHER_ALIGN,
103250974Swpaul		    total_len + ETHER_ALIGN, 0, ifp, NULL);
103350974Swpaul		sis_newbuf(sc, cur_rx, m);
103450974Swpaul		if (m0 == NULL) {
103550974Swpaul			ifp->if_ierrors++;
103650974Swpaul			continue;
103750974Swpaul		}
103850974Swpaul		m_adj(m0, ETHER_ALIGN);
103950974Swpaul		m = m0;
104050974Swpaul
104150974Swpaul		ifp->if_ipackets++;
104250974Swpaul		eh = mtod(m, struct ether_header *);
104351583Swpaul
104450974Swpaul		/* Remove header from mbuf and pass it on. */
104550974Swpaul		m_adj(m, sizeof(struct ether_header));
104650974Swpaul		ether_input(ifp, eh, m);
104750974Swpaul	}
104850974Swpaul
104950974Swpaul	sc->sis_cdata.sis_rx_prod = i;
105050974Swpaul
105150974Swpaul	return;
105250974Swpaul}
105350974Swpaul
105450974Swpaulvoid sis_rxeoc(sc)
105550974Swpaul	struct sis_softc	*sc;
105650974Swpaul{
105750974Swpaul	sis_rxeof(sc);
105850974Swpaul	sis_init(sc);
105950974Swpaul	return;
106050974Swpaul}
106150974Swpaul
106250974Swpaul/*
106350974Swpaul * A frame was downloaded to the chip. It's safe for us to clean up
106450974Swpaul * the list buffers.
106550974Swpaul */
106650974Swpaul
106750974Swpaulstatic void sis_txeof(sc)
106850974Swpaul	struct sis_softc	*sc;
106950974Swpaul{
107050974Swpaul	struct sis_desc		*cur_tx = NULL;
107150974Swpaul	struct ifnet		*ifp;
107250974Swpaul	u_int32_t		idx;
107350974Swpaul
107450974Swpaul	ifp = &sc->arpcom.ac_if;
107550974Swpaul
107650974Swpaul	/* Clear the timeout timer. */
107750974Swpaul	ifp->if_timer = 0;
107850974Swpaul
107950974Swpaul	/*
108050974Swpaul	 * Go through our tx list and free mbufs for those
108150974Swpaul	 * frames that have been transmitted.
108250974Swpaul	 */
108350974Swpaul	idx = sc->sis_cdata.sis_tx_cons;
108450974Swpaul	while (idx != sc->sis_cdata.sis_tx_prod) {
108550974Swpaul		cur_tx = &sc->sis_ldata->sis_tx_list[idx];
108650974Swpaul
108750974Swpaul		if (SIS_OWNDESC(cur_tx))
108850974Swpaul			break;
108950974Swpaul
109050974Swpaul		if (cur_tx->sis_ctl & SIS_CMDSTS_MORE) {
109150974Swpaul			sc->sis_cdata.sis_tx_cnt--;
109250974Swpaul			SIS_INC(idx, SIS_TX_LIST_CNT);
109350974Swpaul			continue;
109450974Swpaul		}
109550974Swpaul
109650974Swpaul		if (!(cur_tx->sis_ctl & SIS_CMDSTS_PKT_OK)) {
109750974Swpaul			ifp->if_oerrors++;
109850974Swpaul			if (cur_tx->sis_txstat & SIS_TXSTAT_EXCESSCOLLS)
109950974Swpaul				ifp->if_collisions++;
110050974Swpaul			if (cur_tx->sis_txstat & SIS_TXSTAT_OUTOFWINCOLL)
110150974Swpaul				ifp->if_collisions++;
110250974Swpaul		}
110350974Swpaul
110450974Swpaul		ifp->if_collisions +=
110550974Swpaul		    (cur_tx->sis_txstat & SIS_TXSTAT_COLLCNT) >> 16;
110650974Swpaul
110750974Swpaul		ifp->if_opackets++;
110850974Swpaul		if (cur_tx->sis_mbuf != NULL) {
110950974Swpaul			m_freem(cur_tx->sis_mbuf);
111050974Swpaul			cur_tx->sis_mbuf = NULL;
111150974Swpaul		}
111250974Swpaul
111350974Swpaul		sc->sis_cdata.sis_tx_cnt--;
111450974Swpaul		SIS_INC(idx, SIS_TX_LIST_CNT);
111550974Swpaul		ifp->if_timer = 0;
111650974Swpaul	}
111750974Swpaul
111850974Swpaul	sc->sis_cdata.sis_tx_cons = idx;
111950974Swpaul
112050974Swpaul	if (cur_tx != NULL)
112150974Swpaul		ifp->if_flags &= ~IFF_OACTIVE;
112250974Swpaul
112350974Swpaul	return;
112450974Swpaul}
112550974Swpaul
112650974Swpaulstatic void sis_tick(xsc)
112750974Swpaul	void			*xsc;
112850974Swpaul{
112950974Swpaul	struct sis_softc	*sc;
113050974Swpaul	struct mii_data		*mii;
113164963Swpaul	struct ifnet		*ifp;
113250974Swpaul
113350974Swpaul	sc = xsc;
113467087Swpaul	SIS_LOCK(sc);
113564963Swpaul	ifp = &sc->arpcom.ac_if;
113664963Swpaul
113750974Swpaul	mii = device_get_softc(sc->sis_miibus);
113850974Swpaul	mii_tick(mii);
113964963Swpaul
114064963Swpaul	if (!sc->sis_link) {
114164963Swpaul		mii_pollstat(mii);
114264963Swpaul		if (mii->mii_media_status & IFM_ACTIVE &&
114364963Swpaul		    IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)
114464963Swpaul			sc->sis_link++;
114564963Swpaul			if (ifp->if_snd.ifq_head != NULL)
114664963Swpaul				sis_start(ifp);
114764963Swpaul	}
114864963Swpaul
114951031Swpaul	sc->sis_stat_ch = timeout(sis_tick, sc, hz);
115050974Swpaul
115167087Swpaul	SIS_UNLOCK(sc);
115250974Swpaul
115350974Swpaul	return;
115450974Swpaul}
115550974Swpaul
115650974Swpaulstatic void sis_intr(arg)
115750974Swpaul	void			*arg;
115850974Swpaul{
115950974Swpaul	struct sis_softc	*sc;
116050974Swpaul	struct ifnet		*ifp;
116150974Swpaul	u_int32_t		status;
116250974Swpaul
116350974Swpaul	sc = arg;
116467087Swpaul	SIS_LOCK(sc);
116550974Swpaul	ifp = &sc->arpcom.ac_if;
116650974Swpaul
116750974Swpaul	/* Supress unwanted interrupts */
116850974Swpaul	if (!(ifp->if_flags & IFF_UP)) {
116950974Swpaul		sis_stop(sc);
117067087Swpaul		SIS_UNLOCK(sc);
117150974Swpaul		return;
117250974Swpaul	}
117350974Swpaul
117450974Swpaul	/* Disable interrupts. */
117550974Swpaul	CSR_WRITE_4(sc, SIS_IER, 0);
117650974Swpaul
117750974Swpaul	for (;;) {
117850974Swpaul		/* Reading the ISR register clears all interrupts. */
117950974Swpaul		status = CSR_READ_4(sc, SIS_ISR);
118050974Swpaul
118150974Swpaul		if ((status & SIS_INTRS) == 0)
118250974Swpaul			break;
118350974Swpaul
118464963Swpaul		if ((status & SIS_ISR_TX_DESC_OK) ||
118550974Swpaul		    (status & SIS_ISR_TX_ERR) ||
118664963Swpaul		    (status & SIS_ISR_TX_OK) ||
118750974Swpaul		    (status & SIS_ISR_TX_IDLE))
118850974Swpaul			sis_txeof(sc);
118950974Swpaul
119064963Swpaul		if ((status & SIS_ISR_RX_DESC_OK) ||
119164963Swpaul		    (status & SIS_ISR_RX_OK))
119250974Swpaul			sis_rxeof(sc);
119350974Swpaul
119450974Swpaul		if ((status & SIS_ISR_RX_ERR) ||
119550974Swpaul		    (status & SIS_ISR_RX_OFLOW)) {
119650974Swpaul			sis_rxeoc(sc);
119750974Swpaul		}
119850974Swpaul
119950974Swpaul		if (status & SIS_ISR_SYSERR) {
120050974Swpaul			sis_reset(sc);
120150974Swpaul			sis_init(sc);
120250974Swpaul		}
120350974Swpaul	}
120450974Swpaul
120550974Swpaul	/* Re-enable interrupts. */
120650974Swpaul	CSR_WRITE_4(sc, SIS_IER, 1);
120750974Swpaul
120850974Swpaul	if (ifp->if_snd.ifq_head != NULL)
120950974Swpaul		sis_start(ifp);
121050974Swpaul
121167087Swpaul	SIS_UNLOCK(sc);
121267087Swpaul
121350974Swpaul	return;
121450974Swpaul}
121550974Swpaul
121650974Swpaul/*
121750974Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
121850974Swpaul * pointers to the fragment pointers.
121950974Swpaul */
122050974Swpaulstatic int sis_encap(sc, m_head, txidx)
122150974Swpaul	struct sis_softc	*sc;
122250974Swpaul	struct mbuf		*m_head;
122350974Swpaul	u_int32_t		*txidx;
122450974Swpaul{
122550974Swpaul	struct sis_desc		*f = NULL;
122650974Swpaul	struct mbuf		*m;
122750974Swpaul	int			frag, cur, cnt = 0;
122850974Swpaul
122950974Swpaul	/*
123050974Swpaul 	 * Start packing the mbufs in this chain into
123150974Swpaul	 * the fragment pointers. Stop when we run out
123250974Swpaul 	 * of fragments or hit the end of the mbuf chain.
123350974Swpaul	 */
123450974Swpaul	m = m_head;
123550974Swpaul	cur = frag = *txidx;
123650974Swpaul
123750974Swpaul	for (m = m_head; m != NULL; m = m->m_next) {
123850974Swpaul		if (m->m_len != 0) {
123951042Swpaul			if ((SIS_TX_LIST_CNT -
124050974Swpaul			    (sc->sis_cdata.sis_tx_cnt + cnt)) < 2)
124150974Swpaul				return(ENOBUFS);
124250974Swpaul			f = &sc->sis_ldata->sis_tx_list[frag];
124350974Swpaul			f->sis_ctl = SIS_CMDSTS_MORE | m->m_len;
124450974Swpaul			f->sis_ptr = vtophys(mtod(m, vm_offset_t));
124550974Swpaul			if (cnt != 0)
124650974Swpaul				f->sis_ctl |= SIS_CMDSTS_OWN;
124750974Swpaul			cur = frag;
124850974Swpaul			SIS_INC(frag, SIS_TX_LIST_CNT);
124950974Swpaul			cnt++;
125050974Swpaul		}
125150974Swpaul	}
125250974Swpaul
125350974Swpaul	if (m != NULL)
125450974Swpaul		return(ENOBUFS);
125550974Swpaul
125650974Swpaul	sc->sis_ldata->sis_tx_list[cur].sis_mbuf = m_head;
125750974Swpaul	sc->sis_ldata->sis_tx_list[cur].sis_ctl &= ~SIS_CMDSTS_MORE;
125850974Swpaul	sc->sis_ldata->sis_tx_list[*txidx].sis_ctl |= SIS_CMDSTS_OWN;
125950974Swpaul	sc->sis_cdata.sis_tx_cnt += cnt;
126050974Swpaul	*txidx = frag;
126150974Swpaul
126250974Swpaul	return(0);
126350974Swpaul}
126450974Swpaul
126550974Swpaul/*
126650974Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers
126750974Swpaul * to the mbuf data regions directly in the transmit lists. We also save a
126850974Swpaul * copy of the pointers since the transmit list fragment pointers are
126950974Swpaul * physical addresses.
127050974Swpaul */
127150974Swpaul
127250974Swpaulstatic void sis_start(ifp)
127350974Swpaul	struct ifnet		*ifp;
127450974Swpaul{
127550974Swpaul	struct sis_softc	*sc;
127650974Swpaul	struct mbuf		*m_head = NULL;
127750974Swpaul	u_int32_t		idx;
127850974Swpaul
127950974Swpaul	sc = ifp->if_softc;
128067087Swpaul	SIS_LOCK(sc);
128150974Swpaul
128267087Swpaul	if (!sc->sis_link) {
128367087Swpaul		SIS_UNLOCK(sc);
128464963Swpaul		return;
128567087Swpaul	}
128664963Swpaul
128750974Swpaul	idx = sc->sis_cdata.sis_tx_prod;
128850974Swpaul
128967087Swpaul	if (ifp->if_flags & IFF_OACTIVE) {
129067087Swpaul		SIS_UNLOCK(sc);
129150974Swpaul		return;
129267087Swpaul	}
129350974Swpaul
129450974Swpaul	while(sc->sis_ldata->sis_tx_list[idx].sis_mbuf == NULL) {
129550974Swpaul		IF_DEQUEUE(&ifp->if_snd, m_head);
129650974Swpaul		if (m_head == NULL)
129750974Swpaul			break;
129850974Swpaul
129950974Swpaul		if (sis_encap(sc, m_head, &idx)) {
130050974Swpaul			IF_PREPEND(&ifp->if_snd, m_head);
130150974Swpaul			ifp->if_flags |= IFF_OACTIVE;
130250974Swpaul			break;
130350974Swpaul		}
130450974Swpaul
130550974Swpaul		/*
130650974Swpaul		 * If there's a BPF listener, bounce a copy of this frame
130750974Swpaul		 * to him.
130850974Swpaul		 */
130950974Swpaul		if (ifp->if_bpf)
131050974Swpaul			bpf_mtap(ifp, m_head);
131151583Swpaul
131250974Swpaul	}
131350974Swpaul
131450974Swpaul	/* Transmit */
131550974Swpaul	sc->sis_cdata.sis_tx_prod = idx;
131650974Swpaul	SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_ENABLE);
131750974Swpaul
131850974Swpaul	/*
131950974Swpaul	 * Set a timeout in case the chip goes out to lunch.
132050974Swpaul	 */
132150974Swpaul	ifp->if_timer = 5;
132250974Swpaul
132367087Swpaul	SIS_UNLOCK(sc);
132467087Swpaul
132550974Swpaul	return;
132650974Swpaul}
132750974Swpaul
132850974Swpaulstatic void sis_init(xsc)
132950974Swpaul	void			*xsc;
133050974Swpaul{
133150974Swpaul	struct sis_softc	*sc = xsc;
133250974Swpaul	struct ifnet		*ifp = &sc->arpcom.ac_if;
133350974Swpaul	struct mii_data		*mii;
133450974Swpaul
133567087Swpaul	SIS_LOCK(sc);
133650974Swpaul
133750974Swpaul	/*
133850974Swpaul	 * Cancel pending I/O and free all RX/TX buffers.
133950974Swpaul	 */
134050974Swpaul	sis_stop(sc);
134150974Swpaul
134250974Swpaul	mii = device_get_softc(sc->sis_miibus);
134350974Swpaul
134450974Swpaul	/* Set MAC address */
134562672Swpaul	if (sc->sis_type == SIS_TYPE_83815) {
134662672Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR0);
134762672Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_DATA,
134862672Swpaul		    ((u_int16_t *)sc->arpcom.ac_enaddr)[0]);
134962672Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR1);
135062672Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_DATA,
135162672Swpaul		    ((u_int16_t *)sc->arpcom.ac_enaddr)[1]);
135262672Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR2);
135362672Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_DATA,
135462672Swpaul		    ((u_int16_t *)sc->arpcom.ac_enaddr)[2]);
135562672Swpaul	} else {
135662672Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR0);
135762672Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_DATA,
135862672Swpaul		    ((u_int16_t *)sc->arpcom.ac_enaddr)[0]);
135962672Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR1);
136062672Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_DATA,
136162672Swpaul		    ((u_int16_t *)sc->arpcom.ac_enaddr)[1]);
136262672Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR2);
136362672Swpaul		CSR_WRITE_4(sc, SIS_RXFILT_DATA,
136462672Swpaul		    ((u_int16_t *)sc->arpcom.ac_enaddr)[2]);
136562672Swpaul	}
136650974Swpaul
136750974Swpaul	/* Init circular RX list. */
136850974Swpaul	if (sis_list_rx_init(sc) == ENOBUFS) {
136950974Swpaul		printf("sis%d: initialization failed: no "
137050974Swpaul			"memory for rx buffers\n", sc->sis_unit);
137150974Swpaul		sis_stop(sc);
137267087Swpaul		SIS_UNLOCK(sc);
137350974Swpaul		return;
137450974Swpaul	}
137550974Swpaul
137650974Swpaul	/*
137750974Swpaul	 * Init tx descriptors.
137850974Swpaul	 */
137950974Swpaul	sis_list_tx_init(sc);
138050974Swpaul
138162672Swpaul	/*
138262672Swpaul	 * For the NatSemi chip, we have to explicitly enable the
138362672Swpaul	 * reception of ARP frames, as well as turn on the 'perfect
138462672Swpaul	 * match' filter where we store the station address, otherwise
138562672Swpaul	 * we won't receive unicasts meant for this host.
138662672Swpaul	 */
138762672Swpaul	if (sc->sis_type == SIS_TYPE_83815) {
138862672Swpaul		SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_ARP);
138962672Swpaul		SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_PERFECT);
139062672Swpaul	}
139162672Swpaul
139250974Swpaul	 /* If we want promiscuous mode, set the allframes bit. */
139350974Swpaul	if (ifp->if_flags & IFF_PROMISC) {
139450974Swpaul		SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS);
139550974Swpaul	} else {
139650974Swpaul		SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS);
139750974Swpaul	}
139850974Swpaul
139950974Swpaul	/*
140050974Swpaul	 * Set the capture broadcast bit to capture broadcast frames.
140150974Swpaul	 */
140250974Swpaul	if (ifp->if_flags & IFF_BROADCAST) {
140350974Swpaul		SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD);
140450974Swpaul	} else {
140550974Swpaul		SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD);
140650974Swpaul	}
140750974Swpaul
140850974Swpaul	/*
140950974Swpaul	 * Load the multicast filter.
141050974Swpaul	 */
141162672Swpaul	if (sc->sis_type == SIS_TYPE_83815)
141262672Swpaul		sis_setmulti_ns(sc);
141362672Swpaul	else
141462672Swpaul		sis_setmulti_sis(sc);
141550974Swpaul
141650974Swpaul	/* Turn the receive filter on */
141750974Swpaul	SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ENABLE);
141850974Swpaul
141950974Swpaul	/*
142050974Swpaul	 * Load the address of the RX and TX lists.
142150974Swpaul	 */
142250974Swpaul	CSR_WRITE_4(sc, SIS_RX_LISTPTR,
142350974Swpaul	    vtophys(&sc->sis_ldata->sis_rx_list[0]));
142450974Swpaul	CSR_WRITE_4(sc, SIS_TX_LISTPTR,
142550974Swpaul	    vtophys(&sc->sis_ldata->sis_tx_list[0]));
142650974Swpaul
142750974Swpaul	/* Set RX configuration */
142850974Swpaul	CSR_WRITE_4(sc, SIS_RX_CFG, SIS_RXCFG);
142964963Swpaul
143050974Swpaul	/* Set TX configuration */
143164963Swpaul	if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) {
143264963Swpaul		CSR_WRITE_4(sc, SIS_TX_CFG, SIS_TXCFG_10);
143364963Swpaul	} else {
143464963Swpaul		CSR_WRITE_4(sc, SIS_TX_CFG, SIS_TXCFG_100);
143564963Swpaul	}
143650974Swpaul
143764963Swpaul	/* Set full/half duplex mode. */
143864963Swpaul	if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) {
143964963Swpaul		SIS_SETBIT(sc, SIS_TX_CFG,
144064963Swpaul		    (SIS_TXCFG_IGN_HBEAT|SIS_TXCFG_IGN_CARR));
144164963Swpaul		SIS_SETBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_TXPKTS);
144264963Swpaul	} else {
144364963Swpaul		SIS_CLRBIT(sc, SIS_TX_CFG,
144464963Swpaul		    (SIS_TXCFG_IGN_HBEAT|SIS_TXCFG_IGN_CARR));
144564963Swpaul		SIS_CLRBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_TXPKTS);
144664963Swpaul	}
144764963Swpaul
144850974Swpaul	/*
144950974Swpaul	 * Enable interrupts.
145050974Swpaul	 */
145150974Swpaul	CSR_WRITE_4(sc, SIS_IMR, SIS_INTRS);
145250974Swpaul	CSR_WRITE_4(sc, SIS_IER, 1);
145350974Swpaul
145450974Swpaul	/* Enable receiver and transmitter. */
145550974Swpaul	SIS_CLRBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE);
145650974Swpaul	SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE);
145750974Swpaul
145864963Swpaul#ifdef notdef
145950974Swpaul	mii_mediachg(mii);
146064963Swpaul#endif
146150974Swpaul
146264963Swpaul	/*
146364963Swpaul	 * Page 75 of the DP83815 manual recommends the
146464963Swpaul	 * following register settings "for optimum
146564963Swpaul	 * performance." Note however that at least three
146664963Swpaul	 * of the registers are listed as "reserved" in
146764963Swpaul	 * the register map, so who knows what they do.
146864963Swpaul	 */
146964963Swpaul	if (sc->sis_type == SIS_TYPE_83815) {
147064963Swpaul		CSR_WRITE_4(sc, NS_PHY_PAGE, 0x0001);
147164963Swpaul		CSR_WRITE_4(sc, NS_PHY_CR, 0x189C);
147264963Swpaul		CSR_WRITE_4(sc, NS_PHY_TDATA, 0x0000);
147364963Swpaul		CSR_WRITE_4(sc, NS_PHY_DSPCFG, 0x5040);
147464963Swpaul		CSR_WRITE_4(sc, NS_PHY_SDCFG, 0x008C);
147564963Swpaul	}
147664963Swpaul
147750974Swpaul	ifp->if_flags |= IFF_RUNNING;
147850974Swpaul	ifp->if_flags &= ~IFF_OACTIVE;
147950974Swpaul
148050974Swpaul	sc->sis_stat_ch = timeout(sis_tick, sc, hz);
148150974Swpaul
148267087Swpaul	SIS_UNLOCK(sc);
148367087Swpaul
148450974Swpaul	return;
148550974Swpaul}
148650974Swpaul
148750974Swpaul/*
148850974Swpaul * Set media options.
148950974Swpaul */
149050974Swpaulstatic int sis_ifmedia_upd(ifp)
149150974Swpaul	struct ifnet		*ifp;
149250974Swpaul{
149350974Swpaul	struct sis_softc	*sc;
149464963Swpaul	struct mii_data		*mii;
149550974Swpaul
149650974Swpaul	sc = ifp->if_softc;
149750974Swpaul
149864963Swpaul	mii = device_get_softc(sc->sis_miibus);
149964963Swpaul	sc->sis_link = 0;
150064963Swpaul	if (mii->mii_instance) {
150164963Swpaul		struct mii_softc	*miisc;
150272012Sphk		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
150364963Swpaul			mii_phy_reset(miisc);
150464963Swpaul	}
150564963Swpaul	mii_mediachg(mii);
150650974Swpaul
150750974Swpaul	return(0);
150850974Swpaul}
150950974Swpaul
151050974Swpaul/*
151150974Swpaul * Report current media status.
151250974Swpaul */
151350974Swpaulstatic void sis_ifmedia_sts(ifp, ifmr)
151450974Swpaul	struct ifnet		*ifp;
151550974Swpaul	struct ifmediareq	*ifmr;
151650974Swpaul{
151750974Swpaul	struct sis_softc	*sc;
151850974Swpaul	struct mii_data		*mii;
151950974Swpaul
152050974Swpaul	sc = ifp->if_softc;
152150974Swpaul
152250974Swpaul	mii = device_get_softc(sc->sis_miibus);
152350974Swpaul	mii_pollstat(mii);
152450974Swpaul	ifmr->ifm_active = mii->mii_media_active;
152550974Swpaul	ifmr->ifm_status = mii->mii_media_status;
152650974Swpaul
152750974Swpaul	return;
152850974Swpaul}
152950974Swpaul
153050974Swpaulstatic int sis_ioctl(ifp, command, data)
153150974Swpaul	struct ifnet		*ifp;
153250974Swpaul	u_long			command;
153350974Swpaul	caddr_t			data;
153450974Swpaul{
153550974Swpaul	struct sis_softc	*sc = ifp->if_softc;
153650974Swpaul	struct ifreq		*ifr = (struct ifreq *) data;
153750974Swpaul	struct mii_data		*mii;
153867087Swpaul	int			error = 0;
153950974Swpaul
154067087Swpaul	SIS_LOCK(sc);
154150974Swpaul
154250974Swpaul	switch(command) {
154350974Swpaul	case SIOCSIFADDR:
154450974Swpaul	case SIOCGIFADDR:
154550974Swpaul	case SIOCSIFMTU:
154650974Swpaul		error = ether_ioctl(ifp, command, data);
154750974Swpaul		break;
154850974Swpaul	case SIOCSIFFLAGS:
154950974Swpaul		if (ifp->if_flags & IFF_UP) {
155050974Swpaul			sis_init(sc);
155150974Swpaul		} else {
155250974Swpaul			if (ifp->if_flags & IFF_RUNNING)
155350974Swpaul				sis_stop(sc);
155450974Swpaul		}
155550974Swpaul		error = 0;
155650974Swpaul		break;
155750974Swpaul	case SIOCADDMULTI:
155850974Swpaul	case SIOCDELMULTI:
155962672Swpaul		if (sc->sis_type == SIS_TYPE_83815)
156062672Swpaul			sis_setmulti_ns(sc);
156162672Swpaul		else
156262672Swpaul			sis_setmulti_sis(sc);
156350974Swpaul		error = 0;
156450974Swpaul		break;
156550974Swpaul	case SIOCGIFMEDIA:
156650974Swpaul	case SIOCSIFMEDIA:
156750974Swpaul		mii = device_get_softc(sc->sis_miibus);
156850974Swpaul		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
156950974Swpaul		break;
157050974Swpaul	default:
157150974Swpaul		error = EINVAL;
157250974Swpaul		break;
157350974Swpaul	}
157450974Swpaul
157567087Swpaul	SIS_UNLOCK(sc);
157650974Swpaul
157750974Swpaul	return(error);
157850974Swpaul}
157950974Swpaul
158050974Swpaulstatic void sis_watchdog(ifp)
158150974Swpaul	struct ifnet		*ifp;
158250974Swpaul{
158350974Swpaul	struct sis_softc	*sc;
158450974Swpaul
158550974Swpaul	sc = ifp->if_softc;
158650974Swpaul
158767087Swpaul	SIS_LOCK(sc);
158867087Swpaul
158950974Swpaul	ifp->if_oerrors++;
159050974Swpaul	printf("sis%d: watchdog timeout\n", sc->sis_unit);
159150974Swpaul
159250974Swpaul	sis_stop(sc);
159350974Swpaul	sis_reset(sc);
159450974Swpaul	sis_init(sc);
159550974Swpaul
159650974Swpaul	if (ifp->if_snd.ifq_head != NULL)
159750974Swpaul		sis_start(ifp);
159850974Swpaul
159967087Swpaul	SIS_UNLOCK(sc);
160067087Swpaul
160150974Swpaul	return;
160250974Swpaul}
160350974Swpaul
160450974Swpaul/*
160550974Swpaul * Stop the adapter and free any mbufs allocated to the
160650974Swpaul * RX and TX lists.
160750974Swpaul */
160850974Swpaulstatic void sis_stop(sc)
160950974Swpaul	struct sis_softc	*sc;
161050974Swpaul{
161150974Swpaul	register int		i;
161250974Swpaul	struct ifnet		*ifp;
161350974Swpaul
161467087Swpaul	SIS_LOCK(sc);
161550974Swpaul	ifp = &sc->arpcom.ac_if;
161650974Swpaul	ifp->if_timer = 0;
161750974Swpaul
161850974Swpaul	untimeout(sis_tick, sc, sc->sis_stat_ch);
161950974Swpaul	CSR_WRITE_4(sc, SIS_IER, 0);
162050974Swpaul	CSR_WRITE_4(sc, SIS_IMR, 0);
162150974Swpaul	SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE);
162250974Swpaul	DELAY(1000);
162350974Swpaul	CSR_WRITE_4(sc, SIS_TX_LISTPTR, 0);
162450974Swpaul	CSR_WRITE_4(sc, SIS_RX_LISTPTR, 0);
162550974Swpaul
162664963Swpaul	sc->sis_link = 0;
162764963Swpaul
162850974Swpaul	/*
162950974Swpaul	 * Free data in the RX lists.
163050974Swpaul	 */
163150974Swpaul	for (i = 0; i < SIS_RX_LIST_CNT; i++) {
163250974Swpaul		if (sc->sis_ldata->sis_rx_list[i].sis_mbuf != NULL) {
163350974Swpaul			m_freem(sc->sis_ldata->sis_rx_list[i].sis_mbuf);
163450974Swpaul			sc->sis_ldata->sis_rx_list[i].sis_mbuf = NULL;
163550974Swpaul		}
163650974Swpaul	}
163750974Swpaul	bzero((char *)&sc->sis_ldata->sis_rx_list,
163850974Swpaul		sizeof(sc->sis_ldata->sis_rx_list));
163950974Swpaul
164050974Swpaul	/*
164150974Swpaul	 * Free the TX list buffers.
164250974Swpaul	 */
164350974Swpaul	for (i = 0; i < SIS_TX_LIST_CNT; i++) {
164450974Swpaul		if (sc->sis_ldata->sis_tx_list[i].sis_mbuf != NULL) {
164550974Swpaul			m_freem(sc->sis_ldata->sis_tx_list[i].sis_mbuf);
164650974Swpaul			sc->sis_ldata->sis_tx_list[i].sis_mbuf = NULL;
164750974Swpaul		}
164850974Swpaul	}
164950974Swpaul
165050974Swpaul	bzero((char *)&sc->sis_ldata->sis_tx_list,
165150974Swpaul		sizeof(sc->sis_ldata->sis_tx_list));
165250974Swpaul
165350974Swpaul	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
165450974Swpaul
165567087Swpaul	SIS_UNLOCK(sc);
165667087Swpaul
165750974Swpaul	return;
165850974Swpaul}
165950974Swpaul
166050974Swpaul/*
166150974Swpaul * Stop all chip I/O so that the kernel's probe routines don't
166250974Swpaul * get confused by errant DMAs when rebooting.
166350974Swpaul */
166450974Swpaulstatic void sis_shutdown(dev)
166550974Swpaul	device_t		dev;
166650974Swpaul{
166750974Swpaul	struct sis_softc	*sc;
166850974Swpaul
166950974Swpaul	sc = device_get_softc(dev);
167067087Swpaul	SIS_LOCK(sc);
167150974Swpaul	sis_reset(sc);
167250974Swpaul	sis_stop(sc);
167367087Swpaul	SIS_UNLOCK(sc);
167450974Swpaul
167550974Swpaul	return;
167650974Swpaul}
1677