if_sis.c revision 62681
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 62681 2000-07-06 19:21:07Z wpaul $ 3350974Swpaul */ 3450974Swpaul 3550974Swpaul/* 3650974Swpaul * SiS 900/SiS 7016 fast ethernet PCI NIC driver. Datasheets are 3750974Swpaul * available from http://www.sis.com.tw. 3850974Swpaul * 3950974Swpaul * Written by Bill Paul <wpaul@ee.columbia.edu> 4050974Swpaul * Electrical Engineering Department 4150974Swpaul * Columbia University, New York City 4250974Swpaul */ 4350974Swpaul 4450974Swpaul/* 4550974Swpaul * The SiS 900 is a fairly simple chip. It uses bus master DMA with 4650974Swpaul * simple TX and RX descriptors of 3 longwords in size. The receiver 4750974Swpaul * has a single perfect filter entry for the station address and a 4850974Swpaul * 128-bit multicast hash table. The SiS 900 has a built-in MII-based 4950974Swpaul * transceiver while the 7016 requires an external transceiver chip. 5050974Swpaul * Both chips offer the standard bit-bang MII interface as well as 5150974Swpaul * an enchanced PHY interface which simplifies accessing MII registers. 5250974Swpaul * 5350974Swpaul * The only downside to this chipset is that RX descriptors must be 5450974Swpaul * longword aligned. 5550974Swpaul */ 5650974Swpaul 5750974Swpaul#include <sys/param.h> 5850974Swpaul#include <sys/systm.h> 5950974Swpaul#include <sys/sockio.h> 6050974Swpaul#include <sys/mbuf.h> 6150974Swpaul#include <sys/malloc.h> 6250974Swpaul#include <sys/kernel.h> 6350974Swpaul#include <sys/socket.h> 6450974Swpaul 6550974Swpaul#include <net/if.h> 6650974Swpaul#include <net/if_arp.h> 6750974Swpaul#include <net/ethernet.h> 6850974Swpaul#include <net/if_dl.h> 6950974Swpaul#include <net/if_media.h> 7050974Swpaul 7150974Swpaul#include <net/bpf.h> 7250974Swpaul 7350974Swpaul#include <vm/vm.h> /* for vtophys */ 7450974Swpaul#include <vm/pmap.h> /* for vtophys */ 7550974Swpaul#include <machine/clock.h> /* for DELAY */ 7650974Swpaul#include <machine/bus_pio.h> 7750974Swpaul#include <machine/bus_memio.h> 7850974Swpaul#include <machine/bus.h> 7950974Swpaul#include <machine/resource.h> 8050974Swpaul#include <sys/bus.h> 8150974Swpaul#include <sys/rman.h> 8250974Swpaul 8350974Swpaul#include <dev/mii/mii.h> 8450974Swpaul#include <dev/mii/miivar.h> 8550974Swpaul 8650974Swpaul#include <pci/pcireg.h> 8750974Swpaul#include <pci/pcivar.h> 8850974Swpaul 8950974Swpaul#define SIS_USEIOSPACE 9050974Swpaul 9150974Swpaul#include <pci/if_sisreg.h> 9250974Swpaul 9359758SpeterMODULE_DEPEND(sis, miibus, 1, 1, 1); 9459758Speter 9551089Speter/* "controller miibus0" required. See GENERIC if you get errors here. */ 9650974Swpaul#include "miibus_if.h" 9750974Swpaul 9850974Swpaul#ifndef lint 9950974Swpaulstatic const char rcsid[] = 10050974Swpaul "$FreeBSD: head/sys/pci/if_sis.c 62681 2000-07-06 19:21:07Z wpaul $"; 10150974Swpaul#endif 10250974Swpaul 10350974Swpaul/* 10450974Swpaul * Various supported device vendors/types and their names. 10550974Swpaul */ 10650974Swpaulstatic struct sis_type sis_devs[] = { 10750974Swpaul { SIS_VENDORID, SIS_DEVICEID_900, "SiS 900 10/100BaseTX" }, 10850974Swpaul { SIS_VENDORID, SIS_DEVICEID_7016, "SiS 7016 10/100BaseTX" }, 10962672Swpaul { NS_VENDORID, NS_DEVICEID_DP83815, "NatSemi DP83815 10/100BaseTX" }, 11050974Swpaul { 0, 0, NULL } 11150974Swpaul}; 11250974Swpaul 11350974Swpaulstatic int sis_probe __P((device_t)); 11450974Swpaulstatic int sis_attach __P((device_t)); 11550974Swpaulstatic int sis_detach __P((device_t)); 11650974Swpaul 11750974Swpaulstatic int sis_newbuf __P((struct sis_softc *, 11850974Swpaul struct sis_desc *, 11950974Swpaul struct mbuf *)); 12050974Swpaulstatic int sis_encap __P((struct sis_softc *, 12150974Swpaul struct mbuf *, u_int32_t *)); 12250974Swpaulstatic void sis_rxeof __P((struct sis_softc *)); 12350974Swpaulstatic void sis_rxeoc __P((struct sis_softc *)); 12450974Swpaulstatic void sis_txeof __P((struct sis_softc *)); 12550974Swpaulstatic void sis_intr __P((void *)); 12650974Swpaulstatic void sis_tick __P((void *)); 12750974Swpaulstatic void sis_start __P((struct ifnet *)); 12850974Swpaulstatic int sis_ioctl __P((struct ifnet *, u_long, caddr_t)); 12950974Swpaulstatic void sis_init __P((void *)); 13050974Swpaulstatic void sis_stop __P((struct sis_softc *)); 13150974Swpaulstatic void sis_watchdog __P((struct ifnet *)); 13250974Swpaulstatic void sis_shutdown __P((device_t)); 13350974Swpaulstatic int sis_ifmedia_upd __P((struct ifnet *)); 13450974Swpaulstatic void sis_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 13550974Swpaul 13662672Swpaulstatic u_int16_t sis_reverse __P((u_int16_t)); 13750974Swpaulstatic void sis_delay __P((struct sis_softc *)); 13850974Swpaulstatic void sis_eeprom_idle __P((struct sis_softc *)); 13950974Swpaulstatic void sis_eeprom_putbyte __P((struct sis_softc *, int)); 14050974Swpaulstatic void sis_eeprom_getword __P((struct sis_softc *, int, u_int16_t *)); 14150974Swpaulstatic void sis_read_eeprom __P((struct sis_softc *, caddr_t, int, 14250974Swpaul int, int)); 14350974Swpaulstatic int sis_miibus_readreg __P((device_t, int, int)); 14450974Swpaulstatic int sis_miibus_writereg __P((device_t, int, int, int)); 14550974Swpaulstatic void sis_miibus_statchg __P((device_t)); 14650974Swpaul 14762672Swpaulstatic void sis_setmulti_sis __P((struct sis_softc *)); 14862672Swpaulstatic void sis_setmulti_ns __P((struct sis_softc *)); 14962672Swpaulstatic u_int32_t sis_crc __P((struct sis_softc *, caddr_t)); 15050974Swpaulstatic void sis_reset __P((struct sis_softc *)); 15150974Swpaulstatic int sis_list_rx_init __P((struct sis_softc *)); 15250974Swpaulstatic int sis_list_tx_init __P((struct sis_softc *)); 15350974Swpaul 15450974Swpaul#ifdef SIS_USEIOSPACE 15550974Swpaul#define SIS_RES SYS_RES_IOPORT 15650974Swpaul#define SIS_RID SIS_PCI_LOIO 15750974Swpaul#else 15851030Swpaul#define SIS_RES SYS_RES_MEMORY 15951030Swpaul#define SIS_RID SIS_PCI_LOMEM 16050974Swpaul#endif 16150974Swpaul 16250974Swpaulstatic device_method_t sis_methods[] = { 16350974Swpaul /* Device interface */ 16450974Swpaul DEVMETHOD(device_probe, sis_probe), 16550974Swpaul DEVMETHOD(device_attach, sis_attach), 16650974Swpaul DEVMETHOD(device_detach, sis_detach), 16750974Swpaul DEVMETHOD(device_shutdown, sis_shutdown), 16850974Swpaul 16950974Swpaul /* bus interface */ 17050974Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 17150974Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 17250974Swpaul 17350974Swpaul /* MII interface */ 17450974Swpaul DEVMETHOD(miibus_readreg, sis_miibus_readreg), 17550974Swpaul DEVMETHOD(miibus_writereg, sis_miibus_writereg), 17650974Swpaul DEVMETHOD(miibus_statchg, sis_miibus_statchg), 17750974Swpaul 17850974Swpaul { 0, 0 } 17950974Swpaul}; 18050974Swpaul 18150974Swpaulstatic driver_t sis_driver = { 18251455Swpaul "sis", 18350974Swpaul sis_methods, 18450974Swpaul sizeof(struct sis_softc) 18550974Swpaul}; 18650974Swpaul 18750974Swpaulstatic devclass_t sis_devclass; 18850974Swpaul 18951533SwpaulDRIVER_MODULE(if_sis, pci, sis_driver, sis_devclass, 0, 0); 19051473SwpaulDRIVER_MODULE(miibus, sis, miibus_driver, miibus_devclass, 0, 0); 19150974Swpaul 19250974Swpaul#define SIS_SETBIT(sc, reg, x) \ 19350974Swpaul CSR_WRITE_4(sc, reg, \ 19450974Swpaul CSR_READ_4(sc, reg) | (x)) 19550974Swpaul 19650974Swpaul#define SIS_CLRBIT(sc, reg, x) \ 19750974Swpaul CSR_WRITE_4(sc, reg, \ 19850974Swpaul CSR_READ_4(sc, reg) & ~(x)) 19950974Swpaul 20050974Swpaul#define SIO_SET(x) \ 20150974Swpaul CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) | x) 20250974Swpaul 20350974Swpaul#define SIO_CLR(x) \ 20450974Swpaul CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) & ~x) 20550974Swpaul 20662672Swpaul/* 20762672Swpaul * Routine to reverse the bits in a word. Stolen almost 20862672Swpaul * verbatim from /usr/games/fortune. 20962672Swpaul */ 21062672Swpaulstatic u_int16_t sis_reverse(n) 21162672Swpaul u_int16_t n; 21262672Swpaul{ 21362672Swpaul n = ((n >> 1) & 0x5555) | ((n << 1) & 0xaaaa); 21462672Swpaul n = ((n >> 2) & 0x3333) | ((n << 2) & 0xcccc); 21562672Swpaul n = ((n >> 4) & 0x0f0f) | ((n << 4) & 0xf0f0); 21662672Swpaul n = ((n >> 8) & 0x00ff) | ((n << 8) & 0xff00); 21762672Swpaul 21862672Swpaul return(n); 21962672Swpaul} 22062672Swpaul 22150974Swpaulstatic void sis_delay(sc) 22250974Swpaul struct sis_softc *sc; 22350974Swpaul{ 22450974Swpaul int idx; 22550974Swpaul 22650974Swpaul for (idx = (300 / 33) + 1; idx > 0; idx--) 22750974Swpaul CSR_READ_4(sc, SIS_CSR); 22850974Swpaul 22950974Swpaul return; 23050974Swpaul} 23150974Swpaul 23250974Swpaulstatic void sis_eeprom_idle(sc) 23350974Swpaul struct sis_softc *sc; 23450974Swpaul{ 23550974Swpaul register int i; 23650974Swpaul 23750974Swpaul SIO_SET(SIS_EECTL_CSEL); 23850974Swpaul sis_delay(sc); 23950974Swpaul SIO_SET(SIS_EECTL_CLK); 24050974Swpaul sis_delay(sc); 24150974Swpaul 24250974Swpaul for (i = 0; i < 25; i++) { 24350974Swpaul SIO_CLR(SIS_EECTL_CLK); 24450974Swpaul sis_delay(sc); 24550974Swpaul SIO_SET(SIS_EECTL_CLK); 24650974Swpaul sis_delay(sc); 24750974Swpaul } 24850974Swpaul 24950974Swpaul SIO_CLR(SIS_EECTL_CLK); 25050974Swpaul sis_delay(sc); 25150974Swpaul SIO_CLR(SIS_EECTL_CSEL); 25250974Swpaul sis_delay(sc); 25350974Swpaul CSR_WRITE_4(sc, SIS_EECTL, 0x00000000); 25450974Swpaul 25550974Swpaul return; 25650974Swpaul} 25750974Swpaul 25850974Swpaul/* 25950974Swpaul * Send a read command and address to the EEPROM, check for ACK. 26050974Swpaul */ 26150974Swpaulstatic void sis_eeprom_putbyte(sc, addr) 26250974Swpaul struct sis_softc *sc; 26350974Swpaul int addr; 26450974Swpaul{ 26550974Swpaul register int d, i; 26650974Swpaul 26750974Swpaul d = addr | SIS_EECMD_READ; 26850974Swpaul 26950974Swpaul /* 27050974Swpaul * Feed in each bit and stobe the clock. 27150974Swpaul */ 27250974Swpaul for (i = 0x400; i; i >>= 1) { 27350974Swpaul if (d & i) { 27450974Swpaul SIO_SET(SIS_EECTL_DIN); 27550974Swpaul } else { 27650974Swpaul SIO_CLR(SIS_EECTL_DIN); 27750974Swpaul } 27850974Swpaul sis_delay(sc); 27950974Swpaul SIO_SET(SIS_EECTL_CLK); 28050974Swpaul sis_delay(sc); 28150974Swpaul SIO_CLR(SIS_EECTL_CLK); 28250974Swpaul sis_delay(sc); 28350974Swpaul } 28450974Swpaul 28550974Swpaul return; 28650974Swpaul} 28750974Swpaul 28850974Swpaul/* 28950974Swpaul * Read a word of data stored in the EEPROM at address 'addr.' 29050974Swpaul */ 29150974Swpaulstatic void sis_eeprom_getword(sc, addr, dest) 29250974Swpaul struct sis_softc *sc; 29350974Swpaul int addr; 29450974Swpaul u_int16_t *dest; 29550974Swpaul{ 29650974Swpaul register int i; 29750974Swpaul u_int16_t word = 0; 29850974Swpaul 29950974Swpaul /* Force EEPROM to idle state. */ 30050974Swpaul sis_eeprom_idle(sc); 30150974Swpaul 30250974Swpaul /* Enter EEPROM access mode. */ 30350974Swpaul sis_delay(sc); 30462672Swpaul SIO_CLR(SIS_EECTL_CLK); 30562672Swpaul sis_delay(sc); 30650974Swpaul SIO_SET(SIS_EECTL_CSEL); 30750974Swpaul sis_delay(sc); 30850974Swpaul 30950974Swpaul /* 31050974Swpaul * Send address of word we want to read. 31150974Swpaul */ 31250974Swpaul sis_eeprom_putbyte(sc, addr); 31350974Swpaul 31450974Swpaul /* 31550974Swpaul * Start reading bits from EEPROM. 31650974Swpaul */ 31750974Swpaul for (i = 0x8000; i; i >>= 1) { 31850974Swpaul SIO_SET(SIS_EECTL_CLK); 31950974Swpaul sis_delay(sc); 32050974Swpaul if (CSR_READ_4(sc, SIS_EECTL) & SIS_EECTL_DOUT) 32150974Swpaul word |= i; 32250974Swpaul sis_delay(sc); 32350974Swpaul SIO_CLR(SIS_EECTL_CLK); 32450974Swpaul sis_delay(sc); 32550974Swpaul } 32650974Swpaul 32750974Swpaul /* Turn off EEPROM access mode. */ 32850974Swpaul sis_eeprom_idle(sc); 32950974Swpaul 33050974Swpaul *dest = word; 33150974Swpaul 33250974Swpaul return; 33350974Swpaul} 33450974Swpaul 33550974Swpaul/* 33650974Swpaul * Read a sequence of words from the EEPROM. 33750974Swpaul */ 33850974Swpaulstatic void sis_read_eeprom(sc, dest, off, cnt, swap) 33950974Swpaul struct sis_softc *sc; 34050974Swpaul caddr_t dest; 34150974Swpaul int off; 34250974Swpaul int cnt; 34350974Swpaul int swap; 34450974Swpaul{ 34550974Swpaul int i; 34650974Swpaul u_int16_t word = 0, *ptr; 34750974Swpaul 34850974Swpaul for (i = 0; i < cnt; i++) { 34950974Swpaul sis_eeprom_getword(sc, off + i, &word); 35050974Swpaul ptr = (u_int16_t *)(dest + (i * 2)); 35150974Swpaul if (swap) 35250974Swpaul *ptr = ntohs(word); 35350974Swpaul else 35450974Swpaul *ptr = word; 35550974Swpaul } 35650974Swpaul 35750974Swpaul return; 35850974Swpaul} 35950974Swpaul 36050974Swpaulstatic int sis_miibus_readreg(dev, phy, reg) 36150974Swpaul device_t dev; 36250974Swpaul int phy, reg; 36350974Swpaul{ 36450974Swpaul struct sis_softc *sc; 36562672Swpaul int i, val = 0; 36650974Swpaul 36750974Swpaul sc = device_get_softc(dev); 36850974Swpaul 36962672Swpaul if (sc->sis_type == SIS_TYPE_83815) { 37062672Swpaul if (phy != 0) 37162672Swpaul return(0); 37262672Swpaul /* 37362672Swpaul * The NatSemi chip can take a while after 37462672Swpaul * a reset to come ready, during which the BMSR 37562672Swpaul * returns a value of 0. This is *never* supposed 37662672Swpaul * to happen: some of the BMSR bits are meant to 37762672Swpaul * be hardwired in the on position, and this can 37862672Swpaul * confuse the miibus code a bit during the probe 37962672Swpaul * and attach phase. So we make an effort to check 38062672Swpaul * for this condition and wait for it to clear. 38162672Swpaul */ 38262672Swpaul if (!CSR_READ_4(sc, NS_BMSR)) 38362672Swpaul DELAY(1000); 38462672Swpaul val = CSR_READ_4(sc, NS_BMCR + (reg * 4)); 38562672Swpaul return(val); 38662672Swpaul } 38762672Swpaul 38850974Swpaul if (sc->sis_type == SIS_TYPE_900 && phy != 0) 38950974Swpaul return(0); 39050974Swpaul 39150974Swpaul CSR_WRITE_4(sc, SIS_PHYCTL, (phy << 11) | (reg << 6) | SIS_PHYOP_READ); 39250974Swpaul SIS_SETBIT(sc, SIS_PHYCTL, SIS_PHYCTL_ACCESS); 39350974Swpaul 39450974Swpaul for (i = 0; i < SIS_TIMEOUT; i++) { 39550974Swpaul if (!(CSR_READ_4(sc, SIS_PHYCTL) & SIS_PHYCTL_ACCESS)) 39650974Swpaul break; 39750974Swpaul } 39850974Swpaul 39950974Swpaul if (i == SIS_TIMEOUT) { 40050974Swpaul printf("sis%d: PHY failed to come ready\n", sc->sis_unit); 40150974Swpaul return(0); 40250974Swpaul } 40350974Swpaul 40450974Swpaul val = (CSR_READ_4(sc, SIS_PHYCTL) >> 16) & 0xFFFF; 40550974Swpaul 40650974Swpaul if (val == 0xFFFF) 40750974Swpaul return(0); 40850974Swpaul 40950974Swpaul return(val); 41050974Swpaul} 41150974Swpaul 41250974Swpaulstatic int sis_miibus_writereg(dev, phy, reg, data) 41350974Swpaul device_t dev; 41450974Swpaul int phy, reg, data; 41550974Swpaul{ 41650974Swpaul struct sis_softc *sc; 41750974Swpaul int i; 41850974Swpaul 41950974Swpaul sc = device_get_softc(dev); 42050974Swpaul 42162672Swpaul if (sc->sis_type == SIS_TYPE_83815) { 42262672Swpaul if (phy != 0) 42362672Swpaul return(0); 42462672Swpaul CSR_WRITE_4(sc, NS_BMCR + (reg * 4), data); 42562672Swpaul return(0); 42662672Swpaul } 42762672Swpaul 42850974Swpaul if (sc->sis_type == SIS_TYPE_900 && phy != 0) 42950974Swpaul return(0); 43050974Swpaul 43162672Swpaul if (sc->sis_type == SIS_TYPE_900 && phy != 0) 43262672Swpaul return(0); 43362672Swpaul 43450974Swpaul CSR_WRITE_4(sc, SIS_PHYCTL, (data << 16) | (phy << 11) | 43550974Swpaul (reg << 6) | SIS_PHYOP_WRITE); 43650974Swpaul SIS_SETBIT(sc, SIS_PHYCTL, SIS_PHYCTL_ACCESS); 43750974Swpaul 43850974Swpaul for (i = 0; i < SIS_TIMEOUT; i++) { 43950974Swpaul if (!(CSR_READ_4(sc, SIS_PHYCTL) & SIS_PHYCTL_ACCESS)) 44050974Swpaul break; 44150974Swpaul } 44250974Swpaul 44350974Swpaul if (i == SIS_TIMEOUT) 44450974Swpaul printf("sis%d: PHY failed to come ready\n", sc->sis_unit); 44550974Swpaul 44650974Swpaul return(0); 44750974Swpaul} 44850974Swpaul 44950974Swpaulstatic void sis_miibus_statchg(dev) 45050974Swpaul device_t dev; 45150974Swpaul{ 45250974Swpaul struct sis_softc *sc; 45350974Swpaul struct mii_data *mii; 45450974Swpaul 45550974Swpaul sc = device_get_softc(dev); 45650974Swpaul mii = device_get_softc(sc->sis_miibus); 45750974Swpaul 45850974Swpaul if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 45950974Swpaul SIS_SETBIT(sc, SIS_TX_CFG, 46050974Swpaul (SIS_TXCFG_IGN_HBEAT|SIS_TXCFG_IGN_CARR)); 46150974Swpaul SIS_SETBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_TXPKTS); 46250974Swpaul } else { 46350974Swpaul SIS_CLRBIT(sc, SIS_TX_CFG, 46450974Swpaul (SIS_TXCFG_IGN_HBEAT|SIS_TXCFG_IGN_CARR)); 46550974Swpaul SIS_CLRBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_TXPKTS); 46650974Swpaul } 46750974Swpaul 46850974Swpaul return; 46950974Swpaul} 47050974Swpaul 47162672Swpaulstatic u_int32_t sis_crc(sc, addr) 47262672Swpaul struct sis_softc *sc; 47350974Swpaul caddr_t addr; 47450974Swpaul{ 47550974Swpaul u_int32_t crc, carry; 47650974Swpaul int i, j; 47750974Swpaul u_int8_t c; 47850974Swpaul 47950974Swpaul /* Compute CRC for the address value. */ 48050974Swpaul crc = 0xFFFFFFFF; /* initial value */ 48150974Swpaul 48250974Swpaul for (i = 0; i < 6; i++) { 48350974Swpaul c = *(addr + i); 48450974Swpaul for (j = 0; j < 8; j++) { 48550974Swpaul carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); 48650974Swpaul crc <<= 1; 48750974Swpaul c >>= 1; 48850974Swpaul if (carry) 48950974Swpaul crc = (crc ^ 0x04c11db6) | carry; 49050974Swpaul } 49150974Swpaul } 49250974Swpaul 49362672Swpaul /* 49462672Swpaul * return the filter bit position 49562672Swpaul * 49662672Swpaul * The NatSemi chip has a 512-bit filter, which is 49762672Swpaul * different than the SiS, so we special-case it. 49862672Swpaul */ 49962672Swpaul if (sc->sis_type == SIS_TYPE_83815) 50062672Swpaul return((crc >> 23) & 0x1FF); 50162672Swpaul 50250974Swpaul return((crc >> 25) & 0x0000007F); 50350974Swpaul} 50450974Swpaul 50562672Swpaulstatic void sis_setmulti_ns(sc) 50650974Swpaul struct sis_softc *sc; 50750974Swpaul{ 50850974Swpaul struct ifnet *ifp; 50950974Swpaul struct ifmultiaddr *ifma; 51050974Swpaul u_int32_t h = 0, i, filtsave; 51162672Swpaul int bit, index; 51250974Swpaul 51350974Swpaul ifp = &sc->arpcom.ac_if; 51450974Swpaul 51550974Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 51662672Swpaul SIS_CLRBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH); 51750974Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); 51850974Swpaul return; 51950974Swpaul } 52050974Swpaul 52162672Swpaul /* 52262672Swpaul * We have to explicitly enable the multicast hash table 52362672Swpaul * on the NatSemi chip if we want to use it, which we do. 52462672Swpaul */ 52562672Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH); 52650974Swpaul SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); 52750974Swpaul 52850974Swpaul filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL); 52950974Swpaul 53050974Swpaul /* first, zot all the existing hash bits */ 53162672Swpaul for (i = 0; i < 32; i++) { 53262672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + (i*2)); 53362672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 0); 53462672Swpaul } 53562672Swpaul 53662672Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 53762672Swpaul ifma = ifma->ifma_link.le_next) { 53862672Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 53962672Swpaul continue; 54062672Swpaul h = sis_crc(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 54162672Swpaul index = h >> 3; 54262672Swpaul bit = h & 0x1F; 54362672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + index); 54462672Swpaul if (bit > 0xF) 54562672Swpaul bit -= 0x10; 54662672Swpaul SIS_SETBIT(sc, SIS_RXFILT_DATA, (1 << bit)); 54762672Swpaul } 54862672Swpaul 54962672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave); 55062672Swpaul 55162672Swpaul return; 55262672Swpaul} 55362672Swpaul 55462672Swpaulstatic void sis_setmulti_sis(sc) 55562672Swpaul struct sis_softc *sc; 55662672Swpaul{ 55762672Swpaul struct ifnet *ifp; 55862672Swpaul struct ifmultiaddr *ifma; 55962672Swpaul u_int32_t h = 0, i, filtsave; 56062672Swpaul 56162672Swpaul ifp = &sc->arpcom.ac_if; 56262672Swpaul 56362672Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 56462672Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); 56562672Swpaul return; 56662672Swpaul } 56762672Swpaul 56862672Swpaul SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); 56962672Swpaul 57062672Swpaul filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL); 57162672Swpaul 57262672Swpaul /* first, zot all the existing hash bits */ 57350974Swpaul for (i = 0; i < 8; i++) { 57450974Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, (4 + ((i * 16) >> 4)) << 16); 57550974Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 0); 57650974Swpaul } 57750974Swpaul 57850974Swpaul /* now program new ones */ 57950974Swpaul for (ifma = ifp->if_multiaddrs.lh_first; ifma != NULL; 58050974Swpaul ifma = ifma->ifma_link.le_next) { 58150974Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 58250974Swpaul continue; 58362672Swpaul h = sis_crc(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 58450974Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, (4 + (h >> 4)) << 16); 58550974Swpaul SIS_SETBIT(sc, SIS_RXFILT_DATA, (1 << (h & 0xF))); 58650974Swpaul } 58750974Swpaul 58850974Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave); 58950974Swpaul 59050974Swpaul return; 59150974Swpaul} 59250974Swpaul 59350974Swpaulstatic void sis_reset(sc) 59450974Swpaul struct sis_softc *sc; 59550974Swpaul{ 59650974Swpaul register int i; 59750974Swpaul 59850974Swpaul SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RESET); 59950974Swpaul 60050974Swpaul for (i = 0; i < SIS_TIMEOUT; i++) { 60150974Swpaul if (!(CSR_READ_4(sc, SIS_CSR) & SIS_CSR_RESET)) 60250974Swpaul break; 60350974Swpaul } 60450974Swpaul 60550974Swpaul if (i == SIS_TIMEOUT) 60650974Swpaul printf("sis%d: reset never completed\n", sc->sis_unit); 60750974Swpaul 60850974Swpaul /* Wait a little while for the chip to get its brains in order. */ 60950974Swpaul DELAY(1000); 61050974Swpaul return; 61150974Swpaul} 61250974Swpaul 61350974Swpaul/* 61450974Swpaul * Probe for an SiS chip. Check the PCI vendor and device 61550974Swpaul * IDs against our list and return a device name if we find a match. 61650974Swpaul */ 61750974Swpaulstatic int sis_probe(dev) 61850974Swpaul device_t dev; 61950974Swpaul{ 62050974Swpaul struct sis_type *t; 62150974Swpaul 62250974Swpaul t = sis_devs; 62350974Swpaul 62450974Swpaul while(t->sis_name != NULL) { 62550974Swpaul if ((pci_get_vendor(dev) == t->sis_vid) && 62650974Swpaul (pci_get_device(dev) == t->sis_did)) { 62750974Swpaul device_set_desc(dev, t->sis_name); 62850974Swpaul return(0); 62950974Swpaul } 63050974Swpaul t++; 63150974Swpaul } 63250974Swpaul 63350974Swpaul return(ENXIO); 63450974Swpaul} 63550974Swpaul 63650974Swpaul/* 63750974Swpaul * Attach the interface. Allocate softc structures, do ifmedia 63850974Swpaul * setup and ethernet/BPF attach. 63950974Swpaul */ 64050974Swpaulstatic int sis_attach(dev) 64150974Swpaul device_t dev; 64250974Swpaul{ 64350974Swpaul int s; 64450974Swpaul u_char eaddr[ETHER_ADDR_LEN]; 64550974Swpaul u_int32_t command; 64650974Swpaul struct sis_softc *sc; 64750974Swpaul struct ifnet *ifp; 64850974Swpaul int unit, error = 0, rid; 64950974Swpaul 65050974Swpaul s = splimp(); 65150974Swpaul 65250974Swpaul sc = device_get_softc(dev); 65350974Swpaul unit = device_get_unit(dev); 65450974Swpaul bzero(sc, sizeof(struct sis_softc)); 65550974Swpaul 65650974Swpaul if (pci_get_device(dev) == SIS_DEVICEID_900) 65750974Swpaul sc->sis_type = SIS_TYPE_900; 65850974Swpaul if (pci_get_device(dev) == SIS_DEVICEID_7016) 65950974Swpaul sc->sis_type = SIS_TYPE_7016; 66062672Swpaul if (pci_get_vendor(dev) == NS_VENDORID) 66162672Swpaul sc->sis_type = SIS_TYPE_83815; 66250974Swpaul 66350974Swpaul /* 66450974Swpaul * Handle power management nonsense. 66550974Swpaul */ 66650974Swpaul 66750974Swpaul command = pci_read_config(dev, SIS_PCI_CAPID, 4) & 0x000000FF; 66850974Swpaul if (command == 0x01) { 66950974Swpaul 67050974Swpaul command = pci_read_config(dev, SIS_PCI_PWRMGMTCTRL, 4); 67150974Swpaul if (command & SIS_PSTATE_MASK) { 67250974Swpaul u_int32_t iobase, membase, irq; 67350974Swpaul 67450974Swpaul /* Save important PCI config data. */ 67550974Swpaul iobase = pci_read_config(dev, SIS_PCI_LOIO, 4); 67650974Swpaul membase = pci_read_config(dev, SIS_PCI_LOMEM, 4); 67750974Swpaul irq = pci_read_config(dev, SIS_PCI_INTLINE, 4); 67850974Swpaul 67950974Swpaul /* Reset the power state. */ 68050974Swpaul printf("sis%d: chip is in D%d power mode " 68150974Swpaul "-- setting to D0\n", unit, command & SIS_PSTATE_MASK); 68250974Swpaul command &= 0xFFFFFFFC; 68350974Swpaul pci_write_config(dev, SIS_PCI_PWRMGMTCTRL, command, 4); 68450974Swpaul 68550974Swpaul /* Restore PCI config data. */ 68650974Swpaul pci_write_config(dev, SIS_PCI_LOIO, iobase, 4); 68750974Swpaul pci_write_config(dev, SIS_PCI_LOMEM, membase, 4); 68850974Swpaul pci_write_config(dev, SIS_PCI_INTLINE, irq, 4); 68950974Swpaul } 69050974Swpaul } 69150974Swpaul 69250974Swpaul /* 69350974Swpaul * Map control/status registers. 69450974Swpaul */ 69561041Speter command = pci_read_config(dev, PCIR_COMMAND, 4); 69650974Swpaul command |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 69761041Speter pci_write_config(dev, PCIR_COMMAND, command, 4); 69861041Speter command = pci_read_config(dev, PCIR_COMMAND, 4); 69950974Swpaul 70050974Swpaul#ifdef SIS_USEIOSPACE 70150974Swpaul if (!(command & PCIM_CMD_PORTEN)) { 70250974Swpaul printf("sis%d: failed to enable I/O ports!\n", unit); 70350974Swpaul error = ENXIO;; 70450974Swpaul goto fail; 70550974Swpaul } 70650974Swpaul#else 70750974Swpaul if (!(command & PCIM_CMD_MEMEN)) { 70850974Swpaul printf("sis%d: failed to enable memory mapping!\n", unit); 70950974Swpaul error = ENXIO;; 71050974Swpaul goto fail; 71150974Swpaul } 71250974Swpaul#endif 71350974Swpaul 71450974Swpaul rid = SIS_RID; 71550974Swpaul sc->sis_res = bus_alloc_resource(dev, SIS_RES, &rid, 71650974Swpaul 0, ~0, 1, RF_ACTIVE); 71750974Swpaul 71850974Swpaul if (sc->sis_res == NULL) { 71950974Swpaul printf("sis%d: couldn't map ports/memory\n", unit); 72050974Swpaul error = ENXIO; 72150974Swpaul goto fail; 72250974Swpaul } 72350974Swpaul 72450974Swpaul sc->sis_btag = rman_get_bustag(sc->sis_res); 72550974Swpaul sc->sis_bhandle = rman_get_bushandle(sc->sis_res); 72650974Swpaul 72750974Swpaul /* Allocate interrupt */ 72850974Swpaul rid = 0; 72950974Swpaul sc->sis_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 73050974Swpaul RF_SHAREABLE | RF_ACTIVE); 73150974Swpaul 73250974Swpaul if (sc->sis_irq == NULL) { 73350974Swpaul printf("sis%d: couldn't map interrupt\n", unit); 73450974Swpaul bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); 73550974Swpaul error = ENXIO; 73650974Swpaul goto fail; 73750974Swpaul } 73850974Swpaul 73950974Swpaul error = bus_setup_intr(dev, sc->sis_irq, INTR_TYPE_NET, 74050974Swpaul sis_intr, sc, &sc->sis_intrhand); 74150974Swpaul 74250974Swpaul if (error) { 74350974Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_res); 74450974Swpaul bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); 74550974Swpaul printf("sis%d: couldn't set up irq\n", unit); 74650974Swpaul goto fail; 74750974Swpaul } 74850974Swpaul 74950974Swpaul /* Reset the adapter. */ 75050974Swpaul sis_reset(sc); 75150974Swpaul 75250974Swpaul /* 75350974Swpaul * Get station address from the EEPROM. 75450974Swpaul */ 75562672Swpaul switch (pci_get_vendor(dev)) { 75662672Swpaul case NS_VENDORID: 75762672Swpaul /* 75862672Swpaul * Reading the MAC address out of the EEPROM on 75962672Swpaul * the NatSemi chip takes a bit more work than 76062672Swpaul * you'd expect. The address spans 4 16-bit words, 76162672Swpaul * with the first word containing only a single bit. 76262672Swpaul * You have to shift everything over one bit to 76362672Swpaul * get it aligned properly. Also, the bits are 76462672Swpaul * stored backwards (the LSB is really the MSB, 76562672Swpaul * and so on) so you have to reverse them in order 76662672Swpaul * to get the MAC address into the form we want. 76762672Swpaul * Why? Who the hell knows. 76862672Swpaul */ 76962672Swpaul { 77062672Swpaul u_int16_t tmp[4]; 77150974Swpaul 77262672Swpaul sis_read_eeprom(sc, (caddr_t)&tmp, 77362672Swpaul NS_EE_NODEADDR, 4, 0); 77462672Swpaul 77562672Swpaul /* Shift everything over one bit. */ 77662672Swpaul tmp[3] = tmp[3] >> 1; 77762681Swpaul tmp[3] |= tmp[2] << 15; 77862672Swpaul tmp[2] = tmp[2] >> 1; 77962681Swpaul tmp[2] |= tmp[1] << 15; 78062672Swpaul tmp[1] = tmp[1] >> 1; 78162681Swpaul tmp[1] |= tmp[0] << 15; 78262672Swpaul 78362672Swpaul /* Now reverse all the bits. */ 78462672Swpaul tmp[3] = sis_reverse(tmp[3]); 78562672Swpaul tmp[2] = sis_reverse(tmp[2]); 78662672Swpaul tmp[1] = sis_reverse(tmp[1]); 78762672Swpaul 78862672Swpaul bcopy((char *)&tmp[1], eaddr, ETHER_ADDR_LEN); 78962672Swpaul } 79062672Swpaul break; 79162672Swpaul case SIS_VENDORID: 79262672Swpaul default: 79362672Swpaul sis_read_eeprom(sc, (caddr_t)&eaddr, SIS_EE_NODEADDR, 3, 0); 79462672Swpaul break; 79562672Swpaul } 79662672Swpaul 79750974Swpaul /* 79850974Swpaul * A SiS chip was detected. Inform the world. 79950974Swpaul */ 80050974Swpaul printf("sis%d: Ethernet address: %6D\n", unit, eaddr, ":"); 80150974Swpaul 80250974Swpaul sc->sis_unit = unit; 80350974Swpaul callout_handle_init(&sc->sis_stat_ch); 80450974Swpaul bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 80550974Swpaul 80650974Swpaul sc->sis_ldata = contigmalloc(sizeof(struct sis_list_data), M_DEVBUF, 80751657Swpaul M_NOWAIT, 0, 0xffffffff, PAGE_SIZE, 0); 80850974Swpaul 80950974Swpaul if (sc->sis_ldata == NULL) { 81050974Swpaul printf("sis%d: no memory for list buffers!\n", unit); 81150974Swpaul bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand); 81250974Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq); 81350974Swpaul bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); 81450974Swpaul error = ENXIO; 81550974Swpaul goto fail; 81650974Swpaul } 81750974Swpaul bzero(sc->sis_ldata, sizeof(struct sis_list_data)); 81850974Swpaul 81950974Swpaul ifp = &sc->arpcom.ac_if; 82050974Swpaul ifp->if_softc = sc; 82150974Swpaul ifp->if_unit = unit; 82250974Swpaul ifp->if_name = "sis"; 82350974Swpaul ifp->if_mtu = ETHERMTU; 82450974Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 82550974Swpaul ifp->if_ioctl = sis_ioctl; 82650974Swpaul ifp->if_output = ether_output; 82750974Swpaul ifp->if_start = sis_start; 82850974Swpaul ifp->if_watchdog = sis_watchdog; 82950974Swpaul ifp->if_init = sis_init; 83050974Swpaul ifp->if_baudrate = 10000000; 83150974Swpaul ifp->if_snd.ifq_maxlen = SIS_TX_LIST_CNT - 1; 83250974Swpaul 83350974Swpaul /* 83450974Swpaul * Do MII setup. 83550974Swpaul */ 83650974Swpaul if (mii_phy_probe(dev, &sc->sis_miibus, 83750974Swpaul sis_ifmedia_upd, sis_ifmedia_sts)) { 83850974Swpaul printf("sis%d: MII without any PHY!\n", sc->sis_unit); 83950974Swpaul bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand); 84050974Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq); 84150974Swpaul bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); 84250974Swpaul error = ENXIO; 84350974Swpaul goto fail; 84450974Swpaul } 84550974Swpaul 84650974Swpaul /* 84750974Swpaul * Call MI attach routines. 84850974Swpaul */ 84950974Swpaul if_attach(ifp); 85050974Swpaul ether_ifattach(ifp); 85150974Swpaul callout_handle_init(&sc->sis_stat_ch); 85250974Swpaul 85350974Swpaul bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 85450974Swpaul 85550974Swpaulfail: 85650974Swpaul splx(s); 85750974Swpaul return(error); 85850974Swpaul} 85950974Swpaul 86050974Swpaulstatic int sis_detach(dev) 86150974Swpaul device_t dev; 86250974Swpaul{ 86350974Swpaul struct sis_softc *sc; 86450974Swpaul struct ifnet *ifp; 86550974Swpaul int s; 86650974Swpaul 86750974Swpaul s = splimp(); 86850974Swpaul 86950974Swpaul sc = device_get_softc(dev); 87050974Swpaul ifp = &sc->arpcom.ac_if; 87150974Swpaul 87250974Swpaul sis_reset(sc); 87350974Swpaul sis_stop(sc); 87450974Swpaul if_detach(ifp); 87550974Swpaul 87650974Swpaul bus_generic_detach(dev); 87750974Swpaul device_delete_child(dev, sc->sis_miibus); 87850974Swpaul 87950974Swpaul bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand); 88050974Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq); 88150974Swpaul bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); 88250974Swpaul 88350974Swpaul contigfree(sc->sis_ldata, sizeof(struct sis_list_data), M_DEVBUF); 88450974Swpaul 88550974Swpaul splx(s); 88650974Swpaul 88750974Swpaul return(0); 88850974Swpaul} 88950974Swpaul 89050974Swpaul/* 89150974Swpaul * Initialize the transmit descriptors. 89250974Swpaul */ 89350974Swpaulstatic int sis_list_tx_init(sc) 89450974Swpaul struct sis_softc *sc; 89550974Swpaul{ 89650974Swpaul struct sis_list_data *ld; 89750974Swpaul struct sis_ring_data *cd; 89850974Swpaul int i; 89950974Swpaul 90050974Swpaul cd = &sc->sis_cdata; 90150974Swpaul ld = sc->sis_ldata; 90250974Swpaul 90350974Swpaul for (i = 0; i < SIS_TX_LIST_CNT; i++) { 90450974Swpaul if (i == (SIS_TX_LIST_CNT - 1)) { 90550974Swpaul ld->sis_tx_list[i].sis_nextdesc = 90650974Swpaul &ld->sis_tx_list[0]; 90750974Swpaul ld->sis_tx_list[i].sis_next = 90850974Swpaul vtophys(&ld->sis_tx_list[0]); 90950974Swpaul } else { 91050974Swpaul ld->sis_tx_list[i].sis_nextdesc = 91150974Swpaul &ld->sis_tx_list[i + 1]; 91250974Swpaul ld->sis_tx_list[i].sis_next = 91350974Swpaul vtophys(&ld->sis_tx_list[i + 1]); 91450974Swpaul } 91550974Swpaul ld->sis_tx_list[i].sis_mbuf = NULL; 91650974Swpaul ld->sis_tx_list[i].sis_ptr = 0; 91750974Swpaul ld->sis_tx_list[i].sis_ctl = 0; 91850974Swpaul } 91950974Swpaul 92050974Swpaul cd->sis_tx_prod = cd->sis_tx_cons = cd->sis_tx_cnt = 0; 92150974Swpaul 92250974Swpaul return(0); 92350974Swpaul} 92450974Swpaul 92550974Swpaul 92650974Swpaul/* 92750974Swpaul * Initialize the RX descriptors and allocate mbufs for them. Note that 92850974Swpaul * we arrange the descriptors in a closed ring, so that the last descriptor 92950974Swpaul * points back to the first. 93050974Swpaul */ 93150974Swpaulstatic int sis_list_rx_init(sc) 93250974Swpaul struct sis_softc *sc; 93350974Swpaul{ 93450974Swpaul struct sis_list_data *ld; 93550974Swpaul struct sis_ring_data *cd; 93650974Swpaul int i; 93750974Swpaul 93850974Swpaul ld = sc->sis_ldata; 93950974Swpaul cd = &sc->sis_cdata; 94050974Swpaul 94150974Swpaul for (i = 0; i < SIS_RX_LIST_CNT; i++) { 94250974Swpaul if (sis_newbuf(sc, &ld->sis_rx_list[i], NULL) == ENOBUFS) 94350974Swpaul return(ENOBUFS); 94450974Swpaul if (i == (SIS_RX_LIST_CNT - 1)) { 94550974Swpaul ld->sis_rx_list[i].sis_nextdesc = 94650974Swpaul &ld->sis_rx_list[0]; 94750974Swpaul ld->sis_rx_list[i].sis_next = 94850974Swpaul vtophys(&ld->sis_rx_list[0]); 94950974Swpaul } else { 95050974Swpaul ld->sis_rx_list[i].sis_nextdesc = 95150974Swpaul &ld->sis_rx_list[i + 1]; 95250974Swpaul ld->sis_rx_list[i].sis_next = 95350974Swpaul vtophys(&ld->sis_rx_list[i + 1]); 95450974Swpaul } 95550974Swpaul } 95650974Swpaul 95750974Swpaul cd->sis_rx_prod = 0; 95850974Swpaul 95950974Swpaul return(0); 96050974Swpaul} 96150974Swpaul 96250974Swpaul/* 96350974Swpaul * Initialize an RX descriptor and attach an MBUF cluster. 96450974Swpaul */ 96550974Swpaulstatic int sis_newbuf(sc, c, m) 96650974Swpaul struct sis_softc *sc; 96750974Swpaul struct sis_desc *c; 96850974Swpaul struct mbuf *m; 96950974Swpaul{ 97050974Swpaul struct mbuf *m_new = NULL; 97150974Swpaul 97250974Swpaul if (m == NULL) { 97350974Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 97450974Swpaul if (m_new == NULL) { 97550974Swpaul printf("sis%d: no memory for rx list " 97650974Swpaul "-- packet dropped!\n", sc->sis_unit); 97750974Swpaul return(ENOBUFS); 97850974Swpaul } 97950974Swpaul 98050974Swpaul MCLGET(m_new, M_DONTWAIT); 98150974Swpaul if (!(m_new->m_flags & M_EXT)) { 98250974Swpaul printf("sis%d: no memory for rx list " 98350974Swpaul "-- packet dropped!\n", sc->sis_unit); 98450974Swpaul m_freem(m_new); 98550974Swpaul return(ENOBUFS); 98650974Swpaul } 98750974Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 98850974Swpaul } else { 98950974Swpaul m_new = m; 99050974Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 99150974Swpaul m_new->m_data = m_new->m_ext.ext_buf; 99250974Swpaul } 99350974Swpaul 99450974Swpaul m_adj(m_new, sizeof(u_int64_t)); 99550974Swpaul 99650974Swpaul c->sis_mbuf = m_new; 99750974Swpaul c->sis_ptr = vtophys(mtod(m_new, caddr_t)); 99850974Swpaul c->sis_ctl = SIS_RXLEN; 99950974Swpaul 100050974Swpaul return(0); 100150974Swpaul} 100250974Swpaul 100350974Swpaul/* 100450974Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to 100550974Swpaul * the higher level protocols. 100650974Swpaul */ 100750974Swpaulstatic void sis_rxeof(sc) 100850974Swpaul struct sis_softc *sc; 100950974Swpaul{ 101050974Swpaul struct ether_header *eh; 101150974Swpaul struct mbuf *m; 101250974Swpaul struct ifnet *ifp; 101350974Swpaul struct sis_desc *cur_rx; 101450974Swpaul int i, total_len = 0; 101550974Swpaul u_int32_t rxstat; 101650974Swpaul 101750974Swpaul ifp = &sc->arpcom.ac_if; 101850974Swpaul i = sc->sis_cdata.sis_rx_prod; 101950974Swpaul 102050974Swpaul while(SIS_OWNDESC(&sc->sis_ldata->sis_rx_list[i])) { 102150974Swpaul struct mbuf *m0 = NULL; 102250974Swpaul 102350974Swpaul cur_rx = &sc->sis_ldata->sis_rx_list[i]; 102450974Swpaul rxstat = cur_rx->sis_rxstat; 102550974Swpaul m = cur_rx->sis_mbuf; 102650974Swpaul cur_rx->sis_mbuf = NULL; 102750974Swpaul total_len = SIS_RXBYTES(cur_rx); 102850974Swpaul SIS_INC(i, SIS_RX_LIST_CNT); 102950974Swpaul 103050974Swpaul /* 103150974Swpaul * If an error occurs, update stats, clear the 103250974Swpaul * status word and leave the mbuf cluster in place: 103350974Swpaul * it should simply get re-used next time this descriptor 103450974Swpaul * comes up in the ring. 103550974Swpaul */ 103650974Swpaul if (!(rxstat & SIS_CMDSTS_PKT_OK)) { 103750974Swpaul ifp->if_ierrors++; 103850974Swpaul if (rxstat & SIS_RXSTAT_COLL) 103950974Swpaul ifp->if_collisions++; 104050974Swpaul sis_newbuf(sc, cur_rx, m); 104150974Swpaul continue; 104250974Swpaul } 104350974Swpaul 104450974Swpaul /* No errors; receive the packet. */ 104550974Swpaul m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, 104650974Swpaul total_len + ETHER_ALIGN, 0, ifp, NULL); 104750974Swpaul sis_newbuf(sc, cur_rx, m); 104850974Swpaul if (m0 == NULL) { 104950974Swpaul ifp->if_ierrors++; 105050974Swpaul continue; 105150974Swpaul } 105250974Swpaul m_adj(m0, ETHER_ALIGN); 105350974Swpaul m = m0; 105450974Swpaul 105550974Swpaul ifp->if_ipackets++; 105650974Swpaul eh = mtod(m, struct ether_header *); 105751583Swpaul 105850974Swpaul /* Remove header from mbuf and pass it on. */ 105950974Swpaul m_adj(m, sizeof(struct ether_header)); 106050974Swpaul ether_input(ifp, eh, m); 106150974Swpaul } 106250974Swpaul 106350974Swpaul sc->sis_cdata.sis_rx_prod = i; 106450974Swpaul 106550974Swpaul return; 106650974Swpaul} 106750974Swpaul 106850974Swpaulvoid sis_rxeoc(sc) 106950974Swpaul struct sis_softc *sc; 107050974Swpaul{ 107150974Swpaul sis_rxeof(sc); 107250974Swpaul sis_init(sc); 107350974Swpaul return; 107450974Swpaul} 107550974Swpaul 107650974Swpaul/* 107750974Swpaul * A frame was downloaded to the chip. It's safe for us to clean up 107850974Swpaul * the list buffers. 107950974Swpaul */ 108050974Swpaul 108150974Swpaulstatic void sis_txeof(sc) 108250974Swpaul struct sis_softc *sc; 108350974Swpaul{ 108450974Swpaul struct sis_desc *cur_tx = NULL; 108550974Swpaul struct ifnet *ifp; 108650974Swpaul u_int32_t idx; 108750974Swpaul 108850974Swpaul ifp = &sc->arpcom.ac_if; 108950974Swpaul 109050974Swpaul /* Clear the timeout timer. */ 109150974Swpaul ifp->if_timer = 0; 109250974Swpaul 109350974Swpaul /* 109450974Swpaul * Go through our tx list and free mbufs for those 109550974Swpaul * frames that have been transmitted. 109650974Swpaul */ 109750974Swpaul idx = sc->sis_cdata.sis_tx_cons; 109850974Swpaul while (idx != sc->sis_cdata.sis_tx_prod) { 109950974Swpaul cur_tx = &sc->sis_ldata->sis_tx_list[idx]; 110050974Swpaul 110150974Swpaul if (SIS_OWNDESC(cur_tx)) 110250974Swpaul break; 110350974Swpaul 110450974Swpaul if (cur_tx->sis_ctl & SIS_CMDSTS_MORE) { 110550974Swpaul sc->sis_cdata.sis_tx_cnt--; 110650974Swpaul SIS_INC(idx, SIS_TX_LIST_CNT); 110750974Swpaul continue; 110850974Swpaul } 110950974Swpaul 111050974Swpaul if (!(cur_tx->sis_ctl & SIS_CMDSTS_PKT_OK)) { 111150974Swpaul ifp->if_oerrors++; 111250974Swpaul if (cur_tx->sis_txstat & SIS_TXSTAT_EXCESSCOLLS) 111350974Swpaul ifp->if_collisions++; 111450974Swpaul if (cur_tx->sis_txstat & SIS_TXSTAT_OUTOFWINCOLL) 111550974Swpaul ifp->if_collisions++; 111650974Swpaul } 111750974Swpaul 111850974Swpaul ifp->if_collisions += 111950974Swpaul (cur_tx->sis_txstat & SIS_TXSTAT_COLLCNT) >> 16; 112050974Swpaul 112150974Swpaul ifp->if_opackets++; 112250974Swpaul if (cur_tx->sis_mbuf != NULL) { 112350974Swpaul m_freem(cur_tx->sis_mbuf); 112450974Swpaul cur_tx->sis_mbuf = NULL; 112550974Swpaul } 112650974Swpaul 112750974Swpaul sc->sis_cdata.sis_tx_cnt--; 112850974Swpaul SIS_INC(idx, SIS_TX_LIST_CNT); 112950974Swpaul ifp->if_timer = 0; 113050974Swpaul } 113150974Swpaul 113250974Swpaul sc->sis_cdata.sis_tx_cons = idx; 113350974Swpaul 113450974Swpaul if (cur_tx != NULL) 113550974Swpaul ifp->if_flags &= ~IFF_OACTIVE; 113650974Swpaul 113750974Swpaul return; 113850974Swpaul} 113950974Swpaul 114050974Swpaulstatic void sis_tick(xsc) 114150974Swpaul void *xsc; 114250974Swpaul{ 114350974Swpaul struct sis_softc *sc; 114450974Swpaul struct mii_data *mii; 114550974Swpaul int s; 114650974Swpaul 114750974Swpaul s = splimp(); 114850974Swpaul 114950974Swpaul sc = xsc; 115050974Swpaul mii = device_get_softc(sc->sis_miibus); 115150974Swpaul mii_tick(mii); 115251031Swpaul sc->sis_stat_ch = timeout(sis_tick, sc, hz); 115350974Swpaul 115450974Swpaul splx(s); 115550974Swpaul 115650974Swpaul return; 115750974Swpaul} 115850974Swpaul 115950974Swpaulstatic void sis_intr(arg) 116050974Swpaul void *arg; 116150974Swpaul{ 116250974Swpaul struct sis_softc *sc; 116350974Swpaul struct ifnet *ifp; 116450974Swpaul u_int32_t status; 116550974Swpaul 116650974Swpaul sc = arg; 116750974Swpaul ifp = &sc->arpcom.ac_if; 116850974Swpaul 116950974Swpaul /* Supress unwanted interrupts */ 117050974Swpaul if (!(ifp->if_flags & IFF_UP)) { 117150974Swpaul sis_stop(sc); 117250974Swpaul return; 117350974Swpaul } 117450974Swpaul 117550974Swpaul /* Disable interrupts. */ 117650974Swpaul CSR_WRITE_4(sc, SIS_IER, 0); 117750974Swpaul 117850974Swpaul for (;;) { 117950974Swpaul /* Reading the ISR register clears all interrupts. */ 118050974Swpaul status = CSR_READ_4(sc, SIS_ISR); 118150974Swpaul 118250974Swpaul if ((status & SIS_INTRS) == 0) 118350974Swpaul break; 118450974Swpaul 118550974Swpaul if ((status & SIS_ISR_TX_OK) || 118650974Swpaul (status & SIS_ISR_TX_ERR) || 118750974Swpaul (status & SIS_ISR_TX_IDLE)) 118850974Swpaul sis_txeof(sc); 118950974Swpaul 119050974Swpaul if (status & SIS_ISR_RX_OK) 119150974Swpaul sis_rxeof(sc); 119250974Swpaul 119350974Swpaul if ((status & SIS_ISR_RX_ERR) || 119450974Swpaul (status & SIS_ISR_RX_OFLOW)) { 119550974Swpaul sis_rxeoc(sc); 119650974Swpaul } 119750974Swpaul 119850974Swpaul if (status & SIS_ISR_SYSERR) { 119950974Swpaul sis_reset(sc); 120050974Swpaul sis_init(sc); 120150974Swpaul } 120250974Swpaul } 120350974Swpaul 120450974Swpaul /* Re-enable interrupts. */ 120550974Swpaul CSR_WRITE_4(sc, SIS_IER, 1); 120650974Swpaul 120750974Swpaul if (ifp->if_snd.ifq_head != NULL) 120850974Swpaul sis_start(ifp); 120950974Swpaul 121050974Swpaul return; 121150974Swpaul} 121250974Swpaul 121350974Swpaul/* 121450974Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 121550974Swpaul * pointers to the fragment pointers. 121650974Swpaul */ 121750974Swpaulstatic int sis_encap(sc, m_head, txidx) 121850974Swpaul struct sis_softc *sc; 121950974Swpaul struct mbuf *m_head; 122050974Swpaul u_int32_t *txidx; 122150974Swpaul{ 122250974Swpaul struct sis_desc *f = NULL; 122350974Swpaul struct mbuf *m; 122450974Swpaul int frag, cur, cnt = 0; 122550974Swpaul 122650974Swpaul /* 122750974Swpaul * Start packing the mbufs in this chain into 122850974Swpaul * the fragment pointers. Stop when we run out 122950974Swpaul * of fragments or hit the end of the mbuf chain. 123050974Swpaul */ 123150974Swpaul m = m_head; 123250974Swpaul cur = frag = *txidx; 123350974Swpaul 123450974Swpaul for (m = m_head; m != NULL; m = m->m_next) { 123550974Swpaul if (m->m_len != 0) { 123651042Swpaul if ((SIS_TX_LIST_CNT - 123750974Swpaul (sc->sis_cdata.sis_tx_cnt + cnt)) < 2) 123850974Swpaul return(ENOBUFS); 123950974Swpaul f = &sc->sis_ldata->sis_tx_list[frag]; 124050974Swpaul f->sis_ctl = SIS_CMDSTS_MORE | m->m_len; 124150974Swpaul f->sis_ptr = vtophys(mtod(m, vm_offset_t)); 124250974Swpaul if (cnt != 0) 124350974Swpaul f->sis_ctl |= SIS_CMDSTS_OWN; 124450974Swpaul cur = frag; 124550974Swpaul SIS_INC(frag, SIS_TX_LIST_CNT); 124650974Swpaul cnt++; 124750974Swpaul } 124850974Swpaul } 124950974Swpaul 125050974Swpaul if (m != NULL) 125150974Swpaul return(ENOBUFS); 125250974Swpaul 125350974Swpaul sc->sis_ldata->sis_tx_list[cur].sis_mbuf = m_head; 125450974Swpaul sc->sis_ldata->sis_tx_list[cur].sis_ctl &= ~SIS_CMDSTS_MORE; 125550974Swpaul sc->sis_ldata->sis_tx_list[*txidx].sis_ctl |= SIS_CMDSTS_OWN; 125650974Swpaul sc->sis_cdata.sis_tx_cnt += cnt; 125750974Swpaul *txidx = frag; 125850974Swpaul 125950974Swpaul return(0); 126050974Swpaul} 126150974Swpaul 126250974Swpaul/* 126350974Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 126450974Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 126550974Swpaul * copy of the pointers since the transmit list fragment pointers are 126650974Swpaul * physical addresses. 126750974Swpaul */ 126850974Swpaul 126950974Swpaulstatic void sis_start(ifp) 127050974Swpaul struct ifnet *ifp; 127150974Swpaul{ 127250974Swpaul struct sis_softc *sc; 127350974Swpaul struct mbuf *m_head = NULL; 127450974Swpaul u_int32_t idx; 127550974Swpaul 127650974Swpaul sc = ifp->if_softc; 127750974Swpaul 127850974Swpaul idx = sc->sis_cdata.sis_tx_prod; 127950974Swpaul 128050974Swpaul if (ifp->if_flags & IFF_OACTIVE) 128150974Swpaul return; 128250974Swpaul 128350974Swpaul while(sc->sis_ldata->sis_tx_list[idx].sis_mbuf == NULL) { 128450974Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 128550974Swpaul if (m_head == NULL) 128650974Swpaul break; 128750974Swpaul 128850974Swpaul if (sis_encap(sc, m_head, &idx)) { 128950974Swpaul IF_PREPEND(&ifp->if_snd, m_head); 129050974Swpaul ifp->if_flags |= IFF_OACTIVE; 129150974Swpaul break; 129250974Swpaul } 129350974Swpaul 129450974Swpaul /* 129550974Swpaul * If there's a BPF listener, bounce a copy of this frame 129650974Swpaul * to him. 129750974Swpaul */ 129850974Swpaul if (ifp->if_bpf) 129950974Swpaul bpf_mtap(ifp, m_head); 130051583Swpaul 130150974Swpaul } 130250974Swpaul 130350974Swpaul /* Transmit */ 130450974Swpaul sc->sis_cdata.sis_tx_prod = idx; 130550974Swpaul SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_ENABLE); 130650974Swpaul 130750974Swpaul /* 130850974Swpaul * Set a timeout in case the chip goes out to lunch. 130950974Swpaul */ 131050974Swpaul ifp->if_timer = 5; 131150974Swpaul 131250974Swpaul return; 131350974Swpaul} 131450974Swpaul 131550974Swpaulstatic void sis_init(xsc) 131650974Swpaul void *xsc; 131750974Swpaul{ 131850974Swpaul struct sis_softc *sc = xsc; 131950974Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 132050974Swpaul struct mii_data *mii; 132150974Swpaul int s; 132250974Swpaul 132350974Swpaul s = splimp(); 132450974Swpaul 132550974Swpaul /* 132650974Swpaul * Cancel pending I/O and free all RX/TX buffers. 132750974Swpaul */ 132850974Swpaul sis_stop(sc); 132950974Swpaul 133050974Swpaul mii = device_get_softc(sc->sis_miibus); 133150974Swpaul 133250974Swpaul /* Set MAC address */ 133362672Swpaul if (sc->sis_type == SIS_TYPE_83815) { 133462672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR0); 133562672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 133662672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); 133762672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR1); 133862672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 133962672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); 134062672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR2); 134162672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 134262672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); 134362672Swpaul } else { 134462672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR0); 134562672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 134662672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); 134762672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR1); 134862672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 134962672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); 135062672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR2); 135162672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 135262672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); 135362672Swpaul } 135450974Swpaul 135550974Swpaul /* Init circular RX list. */ 135650974Swpaul if (sis_list_rx_init(sc) == ENOBUFS) { 135750974Swpaul printf("sis%d: initialization failed: no " 135850974Swpaul "memory for rx buffers\n", sc->sis_unit); 135950974Swpaul sis_stop(sc); 136050974Swpaul (void)splx(s); 136150974Swpaul return; 136250974Swpaul } 136350974Swpaul 136450974Swpaul /* 136550974Swpaul * Init tx descriptors. 136650974Swpaul */ 136750974Swpaul sis_list_tx_init(sc); 136850974Swpaul 136962672Swpaul /* 137062672Swpaul * For the NatSemi chip, we have to explicitly enable the 137162672Swpaul * reception of ARP frames, as well as turn on the 'perfect 137262672Swpaul * match' filter where we store the station address, otherwise 137362672Swpaul * we won't receive unicasts meant for this host. 137462672Swpaul */ 137562672Swpaul if (sc->sis_type == SIS_TYPE_83815) { 137662672Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_ARP); 137762672Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_PERFECT); 137862672Swpaul } 137962672Swpaul 138050974Swpaul /* If we want promiscuous mode, set the allframes bit. */ 138150974Swpaul if (ifp->if_flags & IFF_PROMISC) { 138250974Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS); 138350974Swpaul } else { 138450974Swpaul SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS); 138550974Swpaul } 138650974Swpaul 138750974Swpaul /* 138850974Swpaul * Set the capture broadcast bit to capture broadcast frames. 138950974Swpaul */ 139050974Swpaul if (ifp->if_flags & IFF_BROADCAST) { 139150974Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD); 139250974Swpaul } else { 139350974Swpaul SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD); 139450974Swpaul } 139550974Swpaul 139650974Swpaul /* 139750974Swpaul * Load the multicast filter. 139850974Swpaul */ 139962672Swpaul if (sc->sis_type == SIS_TYPE_83815) 140062672Swpaul sis_setmulti_ns(sc); 140162672Swpaul else 140262672Swpaul sis_setmulti_sis(sc); 140350974Swpaul 140450974Swpaul /* Turn the receive filter on */ 140550974Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ENABLE); 140650974Swpaul 140750974Swpaul /* 140850974Swpaul * Load the address of the RX and TX lists. 140950974Swpaul */ 141050974Swpaul CSR_WRITE_4(sc, SIS_RX_LISTPTR, 141150974Swpaul vtophys(&sc->sis_ldata->sis_rx_list[0])); 141250974Swpaul CSR_WRITE_4(sc, SIS_TX_LISTPTR, 141350974Swpaul vtophys(&sc->sis_ldata->sis_tx_list[0])); 141450974Swpaul 141550974Swpaul /* Set RX configuration */ 141650974Swpaul CSR_WRITE_4(sc, SIS_RX_CFG, SIS_RXCFG); 141750974Swpaul /* Set TX configuration */ 141850974Swpaul CSR_WRITE_4(sc, SIS_TX_CFG, SIS_TXCFG); 141950974Swpaul 142050974Swpaul /* 142150974Swpaul * Enable interrupts. 142250974Swpaul */ 142350974Swpaul CSR_WRITE_4(sc, SIS_IMR, SIS_INTRS); 142450974Swpaul CSR_WRITE_4(sc, SIS_IER, 1); 142550974Swpaul 142650974Swpaul /* Enable receiver and transmitter. */ 142750974Swpaul SIS_CLRBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE); 142850974Swpaul SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); 142950974Swpaul 143050974Swpaul mii_mediachg(mii); 143150974Swpaul 143250974Swpaul ifp->if_flags |= IFF_RUNNING; 143350974Swpaul ifp->if_flags &= ~IFF_OACTIVE; 143450974Swpaul 143550974Swpaul (void)splx(s); 143650974Swpaul 143750974Swpaul sc->sis_stat_ch = timeout(sis_tick, sc, hz); 143850974Swpaul 143950974Swpaul return; 144050974Swpaul} 144150974Swpaul 144250974Swpaul/* 144350974Swpaul * Set media options. 144450974Swpaul */ 144550974Swpaulstatic int sis_ifmedia_upd(ifp) 144650974Swpaul struct ifnet *ifp; 144750974Swpaul{ 144850974Swpaul struct sis_softc *sc; 144950974Swpaul 145050974Swpaul sc = ifp->if_softc; 145150974Swpaul 145250974Swpaul if (ifp->if_flags & IFF_UP) 145350974Swpaul sis_init(sc); 145450974Swpaul 145550974Swpaul return(0); 145650974Swpaul} 145750974Swpaul 145850974Swpaul/* 145950974Swpaul * Report current media status. 146050974Swpaul */ 146150974Swpaulstatic void sis_ifmedia_sts(ifp, ifmr) 146250974Swpaul struct ifnet *ifp; 146350974Swpaul struct ifmediareq *ifmr; 146450974Swpaul{ 146550974Swpaul struct sis_softc *sc; 146650974Swpaul struct mii_data *mii; 146750974Swpaul 146850974Swpaul sc = ifp->if_softc; 146950974Swpaul 147050974Swpaul mii = device_get_softc(sc->sis_miibus); 147150974Swpaul mii_pollstat(mii); 147250974Swpaul ifmr->ifm_active = mii->mii_media_active; 147350974Swpaul ifmr->ifm_status = mii->mii_media_status; 147450974Swpaul 147550974Swpaul return; 147650974Swpaul} 147750974Swpaul 147850974Swpaulstatic int sis_ioctl(ifp, command, data) 147950974Swpaul struct ifnet *ifp; 148050974Swpaul u_long command; 148150974Swpaul caddr_t data; 148250974Swpaul{ 148350974Swpaul struct sis_softc *sc = ifp->if_softc; 148450974Swpaul struct ifreq *ifr = (struct ifreq *) data; 148550974Swpaul struct mii_data *mii; 148650974Swpaul int s, error = 0; 148750974Swpaul 148850974Swpaul s = splimp(); 148950974Swpaul 149050974Swpaul switch(command) { 149150974Swpaul case SIOCSIFADDR: 149250974Swpaul case SIOCGIFADDR: 149350974Swpaul case SIOCSIFMTU: 149450974Swpaul error = ether_ioctl(ifp, command, data); 149550974Swpaul break; 149650974Swpaul case SIOCSIFFLAGS: 149750974Swpaul if (ifp->if_flags & IFF_UP) { 149850974Swpaul sis_init(sc); 149950974Swpaul } else { 150050974Swpaul if (ifp->if_flags & IFF_RUNNING) 150150974Swpaul sis_stop(sc); 150250974Swpaul } 150350974Swpaul error = 0; 150450974Swpaul break; 150550974Swpaul case SIOCADDMULTI: 150650974Swpaul case SIOCDELMULTI: 150762672Swpaul if (sc->sis_type == SIS_TYPE_83815) 150862672Swpaul sis_setmulti_ns(sc); 150962672Swpaul else 151062672Swpaul sis_setmulti_sis(sc); 151150974Swpaul error = 0; 151250974Swpaul break; 151350974Swpaul case SIOCGIFMEDIA: 151450974Swpaul case SIOCSIFMEDIA: 151550974Swpaul mii = device_get_softc(sc->sis_miibus); 151650974Swpaul error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 151750974Swpaul break; 151850974Swpaul default: 151950974Swpaul error = EINVAL; 152050974Swpaul break; 152150974Swpaul } 152250974Swpaul 152350974Swpaul (void)splx(s); 152450974Swpaul 152550974Swpaul return(error); 152650974Swpaul} 152750974Swpaul 152850974Swpaulstatic void sis_watchdog(ifp) 152950974Swpaul struct ifnet *ifp; 153050974Swpaul{ 153150974Swpaul struct sis_softc *sc; 153250974Swpaul 153350974Swpaul sc = ifp->if_softc; 153450974Swpaul 153550974Swpaul ifp->if_oerrors++; 153650974Swpaul printf("sis%d: watchdog timeout\n", sc->sis_unit); 153750974Swpaul 153850974Swpaul sis_stop(sc); 153950974Swpaul sis_reset(sc); 154050974Swpaul sis_init(sc); 154150974Swpaul 154250974Swpaul if (ifp->if_snd.ifq_head != NULL) 154350974Swpaul sis_start(ifp); 154450974Swpaul 154550974Swpaul return; 154650974Swpaul} 154750974Swpaul 154850974Swpaul/* 154950974Swpaul * Stop the adapter and free any mbufs allocated to the 155050974Swpaul * RX and TX lists. 155150974Swpaul */ 155250974Swpaulstatic void sis_stop(sc) 155350974Swpaul struct sis_softc *sc; 155450974Swpaul{ 155550974Swpaul register int i; 155650974Swpaul struct ifnet *ifp; 155750974Swpaul 155850974Swpaul ifp = &sc->arpcom.ac_if; 155950974Swpaul ifp->if_timer = 0; 156050974Swpaul 156150974Swpaul untimeout(sis_tick, sc, sc->sis_stat_ch); 156250974Swpaul CSR_WRITE_4(sc, SIS_IER, 0); 156350974Swpaul CSR_WRITE_4(sc, SIS_IMR, 0); 156450974Swpaul SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE); 156550974Swpaul DELAY(1000); 156650974Swpaul CSR_WRITE_4(sc, SIS_TX_LISTPTR, 0); 156750974Swpaul CSR_WRITE_4(sc, SIS_RX_LISTPTR, 0); 156850974Swpaul 156950974Swpaul /* 157050974Swpaul * Free data in the RX lists. 157150974Swpaul */ 157250974Swpaul for (i = 0; i < SIS_RX_LIST_CNT; i++) { 157350974Swpaul if (sc->sis_ldata->sis_rx_list[i].sis_mbuf != NULL) { 157450974Swpaul m_freem(sc->sis_ldata->sis_rx_list[i].sis_mbuf); 157550974Swpaul sc->sis_ldata->sis_rx_list[i].sis_mbuf = NULL; 157650974Swpaul } 157750974Swpaul } 157850974Swpaul bzero((char *)&sc->sis_ldata->sis_rx_list, 157950974Swpaul sizeof(sc->sis_ldata->sis_rx_list)); 158050974Swpaul 158150974Swpaul /* 158250974Swpaul * Free the TX list buffers. 158350974Swpaul */ 158450974Swpaul for (i = 0; i < SIS_TX_LIST_CNT; i++) { 158550974Swpaul if (sc->sis_ldata->sis_tx_list[i].sis_mbuf != NULL) { 158650974Swpaul m_freem(sc->sis_ldata->sis_tx_list[i].sis_mbuf); 158750974Swpaul sc->sis_ldata->sis_tx_list[i].sis_mbuf = NULL; 158850974Swpaul } 158950974Swpaul } 159050974Swpaul 159150974Swpaul bzero((char *)&sc->sis_ldata->sis_tx_list, 159250974Swpaul sizeof(sc->sis_ldata->sis_tx_list)); 159350974Swpaul 159450974Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 159550974Swpaul 159650974Swpaul return; 159750974Swpaul} 159850974Swpaul 159950974Swpaul/* 160050974Swpaul * Stop all chip I/O so that the kernel's probe routines don't 160150974Swpaul * get confused by errant DMAs when rebooting. 160250974Swpaul */ 160350974Swpaulstatic void sis_shutdown(dev) 160450974Swpaul device_t dev; 160550974Swpaul{ 160650974Swpaul struct sis_softc *sc; 160750974Swpaul 160850974Swpaul sc = device_get_softc(dev); 160950974Swpaul 161050974Swpaul sis_reset(sc); 161150974Swpaul sis_stop(sc); 161250974Swpaul 161350974Swpaul return; 161450974Swpaul} 1615