if_sis.c revision 63090
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 63090 2000-07-13 22:54:34Z archie $ 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 63090 2000-07-13 22:54:34Z archie $"; 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 /* 84763090Sarchie * Call MI attach routine. 84850974Swpaul */ 84963090Sarchie ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 85050974Swpaul callout_handle_init(&sc->sis_stat_ch); 85150974Swpaul 85250974Swpaulfail: 85350974Swpaul splx(s); 85450974Swpaul return(error); 85550974Swpaul} 85650974Swpaul 85750974Swpaulstatic int sis_detach(dev) 85850974Swpaul device_t dev; 85950974Swpaul{ 86050974Swpaul struct sis_softc *sc; 86150974Swpaul struct ifnet *ifp; 86250974Swpaul int s; 86350974Swpaul 86450974Swpaul s = splimp(); 86550974Swpaul 86650974Swpaul sc = device_get_softc(dev); 86750974Swpaul ifp = &sc->arpcom.ac_if; 86850974Swpaul 86950974Swpaul sis_reset(sc); 87050974Swpaul sis_stop(sc); 87163090Sarchie ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); 87250974Swpaul 87350974Swpaul bus_generic_detach(dev); 87450974Swpaul device_delete_child(dev, sc->sis_miibus); 87550974Swpaul 87650974Swpaul bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand); 87750974Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq); 87850974Swpaul bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); 87950974Swpaul 88050974Swpaul contigfree(sc->sis_ldata, sizeof(struct sis_list_data), M_DEVBUF); 88150974Swpaul 88250974Swpaul splx(s); 88350974Swpaul 88450974Swpaul return(0); 88550974Swpaul} 88650974Swpaul 88750974Swpaul/* 88850974Swpaul * Initialize the transmit descriptors. 88950974Swpaul */ 89050974Swpaulstatic int sis_list_tx_init(sc) 89150974Swpaul struct sis_softc *sc; 89250974Swpaul{ 89350974Swpaul struct sis_list_data *ld; 89450974Swpaul struct sis_ring_data *cd; 89550974Swpaul int i; 89650974Swpaul 89750974Swpaul cd = &sc->sis_cdata; 89850974Swpaul ld = sc->sis_ldata; 89950974Swpaul 90050974Swpaul for (i = 0; i < SIS_TX_LIST_CNT; i++) { 90150974Swpaul if (i == (SIS_TX_LIST_CNT - 1)) { 90250974Swpaul ld->sis_tx_list[i].sis_nextdesc = 90350974Swpaul &ld->sis_tx_list[0]; 90450974Swpaul ld->sis_tx_list[i].sis_next = 90550974Swpaul vtophys(&ld->sis_tx_list[0]); 90650974Swpaul } else { 90750974Swpaul ld->sis_tx_list[i].sis_nextdesc = 90850974Swpaul &ld->sis_tx_list[i + 1]; 90950974Swpaul ld->sis_tx_list[i].sis_next = 91050974Swpaul vtophys(&ld->sis_tx_list[i + 1]); 91150974Swpaul } 91250974Swpaul ld->sis_tx_list[i].sis_mbuf = NULL; 91350974Swpaul ld->sis_tx_list[i].sis_ptr = 0; 91450974Swpaul ld->sis_tx_list[i].sis_ctl = 0; 91550974Swpaul } 91650974Swpaul 91750974Swpaul cd->sis_tx_prod = cd->sis_tx_cons = cd->sis_tx_cnt = 0; 91850974Swpaul 91950974Swpaul return(0); 92050974Swpaul} 92150974Swpaul 92250974Swpaul 92350974Swpaul/* 92450974Swpaul * Initialize the RX descriptors and allocate mbufs for them. Note that 92550974Swpaul * we arrange the descriptors in a closed ring, so that the last descriptor 92650974Swpaul * points back to the first. 92750974Swpaul */ 92850974Swpaulstatic int sis_list_rx_init(sc) 92950974Swpaul struct sis_softc *sc; 93050974Swpaul{ 93150974Swpaul struct sis_list_data *ld; 93250974Swpaul struct sis_ring_data *cd; 93350974Swpaul int i; 93450974Swpaul 93550974Swpaul ld = sc->sis_ldata; 93650974Swpaul cd = &sc->sis_cdata; 93750974Swpaul 93850974Swpaul for (i = 0; i < SIS_RX_LIST_CNT; i++) { 93950974Swpaul if (sis_newbuf(sc, &ld->sis_rx_list[i], NULL) == ENOBUFS) 94050974Swpaul return(ENOBUFS); 94150974Swpaul if (i == (SIS_RX_LIST_CNT - 1)) { 94250974Swpaul ld->sis_rx_list[i].sis_nextdesc = 94350974Swpaul &ld->sis_rx_list[0]; 94450974Swpaul ld->sis_rx_list[i].sis_next = 94550974Swpaul vtophys(&ld->sis_rx_list[0]); 94650974Swpaul } else { 94750974Swpaul ld->sis_rx_list[i].sis_nextdesc = 94850974Swpaul &ld->sis_rx_list[i + 1]; 94950974Swpaul ld->sis_rx_list[i].sis_next = 95050974Swpaul vtophys(&ld->sis_rx_list[i + 1]); 95150974Swpaul } 95250974Swpaul } 95350974Swpaul 95450974Swpaul cd->sis_rx_prod = 0; 95550974Swpaul 95650974Swpaul return(0); 95750974Swpaul} 95850974Swpaul 95950974Swpaul/* 96050974Swpaul * Initialize an RX descriptor and attach an MBUF cluster. 96150974Swpaul */ 96250974Swpaulstatic int sis_newbuf(sc, c, m) 96350974Swpaul struct sis_softc *sc; 96450974Swpaul struct sis_desc *c; 96550974Swpaul struct mbuf *m; 96650974Swpaul{ 96750974Swpaul struct mbuf *m_new = NULL; 96850974Swpaul 96950974Swpaul if (m == NULL) { 97050974Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 97150974Swpaul if (m_new == NULL) { 97250974Swpaul printf("sis%d: no memory for rx list " 97350974Swpaul "-- packet dropped!\n", sc->sis_unit); 97450974Swpaul return(ENOBUFS); 97550974Swpaul } 97650974Swpaul 97750974Swpaul MCLGET(m_new, M_DONTWAIT); 97850974Swpaul if (!(m_new->m_flags & M_EXT)) { 97950974Swpaul printf("sis%d: no memory for rx list " 98050974Swpaul "-- packet dropped!\n", sc->sis_unit); 98150974Swpaul m_freem(m_new); 98250974Swpaul return(ENOBUFS); 98350974Swpaul } 98450974Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 98550974Swpaul } else { 98650974Swpaul m_new = m; 98750974Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 98850974Swpaul m_new->m_data = m_new->m_ext.ext_buf; 98950974Swpaul } 99050974Swpaul 99150974Swpaul m_adj(m_new, sizeof(u_int64_t)); 99250974Swpaul 99350974Swpaul c->sis_mbuf = m_new; 99450974Swpaul c->sis_ptr = vtophys(mtod(m_new, caddr_t)); 99550974Swpaul c->sis_ctl = SIS_RXLEN; 99650974Swpaul 99750974Swpaul return(0); 99850974Swpaul} 99950974Swpaul 100050974Swpaul/* 100150974Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to 100250974Swpaul * the higher level protocols. 100350974Swpaul */ 100450974Swpaulstatic void sis_rxeof(sc) 100550974Swpaul struct sis_softc *sc; 100650974Swpaul{ 100750974Swpaul struct ether_header *eh; 100850974Swpaul struct mbuf *m; 100950974Swpaul struct ifnet *ifp; 101050974Swpaul struct sis_desc *cur_rx; 101150974Swpaul int i, total_len = 0; 101250974Swpaul u_int32_t rxstat; 101350974Swpaul 101450974Swpaul ifp = &sc->arpcom.ac_if; 101550974Swpaul i = sc->sis_cdata.sis_rx_prod; 101650974Swpaul 101750974Swpaul while(SIS_OWNDESC(&sc->sis_ldata->sis_rx_list[i])) { 101850974Swpaul struct mbuf *m0 = NULL; 101950974Swpaul 102050974Swpaul cur_rx = &sc->sis_ldata->sis_rx_list[i]; 102150974Swpaul rxstat = cur_rx->sis_rxstat; 102250974Swpaul m = cur_rx->sis_mbuf; 102350974Swpaul cur_rx->sis_mbuf = NULL; 102450974Swpaul total_len = SIS_RXBYTES(cur_rx); 102550974Swpaul SIS_INC(i, SIS_RX_LIST_CNT); 102650974Swpaul 102750974Swpaul /* 102850974Swpaul * If an error occurs, update stats, clear the 102950974Swpaul * status word and leave the mbuf cluster in place: 103050974Swpaul * it should simply get re-used next time this descriptor 103150974Swpaul * comes up in the ring. 103250974Swpaul */ 103350974Swpaul if (!(rxstat & SIS_CMDSTS_PKT_OK)) { 103450974Swpaul ifp->if_ierrors++; 103550974Swpaul if (rxstat & SIS_RXSTAT_COLL) 103650974Swpaul ifp->if_collisions++; 103750974Swpaul sis_newbuf(sc, cur_rx, m); 103850974Swpaul continue; 103950974Swpaul } 104050974Swpaul 104150974Swpaul /* No errors; receive the packet. */ 104250974Swpaul m0 = m_devget(mtod(m, char *) - ETHER_ALIGN, 104350974Swpaul total_len + ETHER_ALIGN, 0, ifp, NULL); 104450974Swpaul sis_newbuf(sc, cur_rx, m); 104550974Swpaul if (m0 == NULL) { 104650974Swpaul ifp->if_ierrors++; 104750974Swpaul continue; 104850974Swpaul } 104950974Swpaul m_adj(m0, ETHER_ALIGN); 105050974Swpaul m = m0; 105150974Swpaul 105250974Swpaul ifp->if_ipackets++; 105350974Swpaul eh = mtod(m, struct ether_header *); 105451583Swpaul 105550974Swpaul /* Remove header from mbuf and pass it on. */ 105650974Swpaul m_adj(m, sizeof(struct ether_header)); 105750974Swpaul ether_input(ifp, eh, m); 105850974Swpaul } 105950974Swpaul 106050974Swpaul sc->sis_cdata.sis_rx_prod = i; 106150974Swpaul 106250974Swpaul return; 106350974Swpaul} 106450974Swpaul 106550974Swpaulvoid sis_rxeoc(sc) 106650974Swpaul struct sis_softc *sc; 106750974Swpaul{ 106850974Swpaul sis_rxeof(sc); 106950974Swpaul sis_init(sc); 107050974Swpaul return; 107150974Swpaul} 107250974Swpaul 107350974Swpaul/* 107450974Swpaul * A frame was downloaded to the chip. It's safe for us to clean up 107550974Swpaul * the list buffers. 107650974Swpaul */ 107750974Swpaul 107850974Swpaulstatic void sis_txeof(sc) 107950974Swpaul struct sis_softc *sc; 108050974Swpaul{ 108150974Swpaul struct sis_desc *cur_tx = NULL; 108250974Swpaul struct ifnet *ifp; 108350974Swpaul u_int32_t idx; 108450974Swpaul 108550974Swpaul ifp = &sc->arpcom.ac_if; 108650974Swpaul 108750974Swpaul /* Clear the timeout timer. */ 108850974Swpaul ifp->if_timer = 0; 108950974Swpaul 109050974Swpaul /* 109150974Swpaul * Go through our tx list and free mbufs for those 109250974Swpaul * frames that have been transmitted. 109350974Swpaul */ 109450974Swpaul idx = sc->sis_cdata.sis_tx_cons; 109550974Swpaul while (idx != sc->sis_cdata.sis_tx_prod) { 109650974Swpaul cur_tx = &sc->sis_ldata->sis_tx_list[idx]; 109750974Swpaul 109850974Swpaul if (SIS_OWNDESC(cur_tx)) 109950974Swpaul break; 110050974Swpaul 110150974Swpaul if (cur_tx->sis_ctl & SIS_CMDSTS_MORE) { 110250974Swpaul sc->sis_cdata.sis_tx_cnt--; 110350974Swpaul SIS_INC(idx, SIS_TX_LIST_CNT); 110450974Swpaul continue; 110550974Swpaul } 110650974Swpaul 110750974Swpaul if (!(cur_tx->sis_ctl & SIS_CMDSTS_PKT_OK)) { 110850974Swpaul ifp->if_oerrors++; 110950974Swpaul if (cur_tx->sis_txstat & SIS_TXSTAT_EXCESSCOLLS) 111050974Swpaul ifp->if_collisions++; 111150974Swpaul if (cur_tx->sis_txstat & SIS_TXSTAT_OUTOFWINCOLL) 111250974Swpaul ifp->if_collisions++; 111350974Swpaul } 111450974Swpaul 111550974Swpaul ifp->if_collisions += 111650974Swpaul (cur_tx->sis_txstat & SIS_TXSTAT_COLLCNT) >> 16; 111750974Swpaul 111850974Swpaul ifp->if_opackets++; 111950974Swpaul if (cur_tx->sis_mbuf != NULL) { 112050974Swpaul m_freem(cur_tx->sis_mbuf); 112150974Swpaul cur_tx->sis_mbuf = NULL; 112250974Swpaul } 112350974Swpaul 112450974Swpaul sc->sis_cdata.sis_tx_cnt--; 112550974Swpaul SIS_INC(idx, SIS_TX_LIST_CNT); 112650974Swpaul ifp->if_timer = 0; 112750974Swpaul } 112850974Swpaul 112950974Swpaul sc->sis_cdata.sis_tx_cons = idx; 113050974Swpaul 113150974Swpaul if (cur_tx != NULL) 113250974Swpaul ifp->if_flags &= ~IFF_OACTIVE; 113350974Swpaul 113450974Swpaul return; 113550974Swpaul} 113650974Swpaul 113750974Swpaulstatic void sis_tick(xsc) 113850974Swpaul void *xsc; 113950974Swpaul{ 114050974Swpaul struct sis_softc *sc; 114150974Swpaul struct mii_data *mii; 114250974Swpaul int s; 114350974Swpaul 114450974Swpaul s = splimp(); 114550974Swpaul 114650974Swpaul sc = xsc; 114750974Swpaul mii = device_get_softc(sc->sis_miibus); 114850974Swpaul mii_tick(mii); 114951031Swpaul sc->sis_stat_ch = timeout(sis_tick, sc, hz); 115050974Swpaul 115150974Swpaul splx(s); 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; 116450974Swpaul ifp = &sc->arpcom.ac_if; 116550974Swpaul 116650974Swpaul /* Supress unwanted interrupts */ 116750974Swpaul if (!(ifp->if_flags & IFF_UP)) { 116850974Swpaul sis_stop(sc); 116950974Swpaul return; 117050974Swpaul } 117150974Swpaul 117250974Swpaul /* Disable interrupts. */ 117350974Swpaul CSR_WRITE_4(sc, SIS_IER, 0); 117450974Swpaul 117550974Swpaul for (;;) { 117650974Swpaul /* Reading the ISR register clears all interrupts. */ 117750974Swpaul status = CSR_READ_4(sc, SIS_ISR); 117850974Swpaul 117950974Swpaul if ((status & SIS_INTRS) == 0) 118050974Swpaul break; 118150974Swpaul 118250974Swpaul if ((status & SIS_ISR_TX_OK) || 118350974Swpaul (status & SIS_ISR_TX_ERR) || 118450974Swpaul (status & SIS_ISR_TX_IDLE)) 118550974Swpaul sis_txeof(sc); 118650974Swpaul 118750974Swpaul if (status & SIS_ISR_RX_OK) 118850974Swpaul sis_rxeof(sc); 118950974Swpaul 119050974Swpaul if ((status & SIS_ISR_RX_ERR) || 119150974Swpaul (status & SIS_ISR_RX_OFLOW)) { 119250974Swpaul sis_rxeoc(sc); 119350974Swpaul } 119450974Swpaul 119550974Swpaul if (status & SIS_ISR_SYSERR) { 119650974Swpaul sis_reset(sc); 119750974Swpaul sis_init(sc); 119850974Swpaul } 119950974Swpaul } 120050974Swpaul 120150974Swpaul /* Re-enable interrupts. */ 120250974Swpaul CSR_WRITE_4(sc, SIS_IER, 1); 120350974Swpaul 120450974Swpaul if (ifp->if_snd.ifq_head != NULL) 120550974Swpaul sis_start(ifp); 120650974Swpaul 120750974Swpaul return; 120850974Swpaul} 120950974Swpaul 121050974Swpaul/* 121150974Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 121250974Swpaul * pointers to the fragment pointers. 121350974Swpaul */ 121450974Swpaulstatic int sis_encap(sc, m_head, txidx) 121550974Swpaul struct sis_softc *sc; 121650974Swpaul struct mbuf *m_head; 121750974Swpaul u_int32_t *txidx; 121850974Swpaul{ 121950974Swpaul struct sis_desc *f = NULL; 122050974Swpaul struct mbuf *m; 122150974Swpaul int frag, cur, cnt = 0; 122250974Swpaul 122350974Swpaul /* 122450974Swpaul * Start packing the mbufs in this chain into 122550974Swpaul * the fragment pointers. Stop when we run out 122650974Swpaul * of fragments or hit the end of the mbuf chain. 122750974Swpaul */ 122850974Swpaul m = m_head; 122950974Swpaul cur = frag = *txidx; 123050974Swpaul 123150974Swpaul for (m = m_head; m != NULL; m = m->m_next) { 123250974Swpaul if (m->m_len != 0) { 123351042Swpaul if ((SIS_TX_LIST_CNT - 123450974Swpaul (sc->sis_cdata.sis_tx_cnt + cnt)) < 2) 123550974Swpaul return(ENOBUFS); 123650974Swpaul f = &sc->sis_ldata->sis_tx_list[frag]; 123750974Swpaul f->sis_ctl = SIS_CMDSTS_MORE | m->m_len; 123850974Swpaul f->sis_ptr = vtophys(mtod(m, vm_offset_t)); 123950974Swpaul if (cnt != 0) 124050974Swpaul f->sis_ctl |= SIS_CMDSTS_OWN; 124150974Swpaul cur = frag; 124250974Swpaul SIS_INC(frag, SIS_TX_LIST_CNT); 124350974Swpaul cnt++; 124450974Swpaul } 124550974Swpaul } 124650974Swpaul 124750974Swpaul if (m != NULL) 124850974Swpaul return(ENOBUFS); 124950974Swpaul 125050974Swpaul sc->sis_ldata->sis_tx_list[cur].sis_mbuf = m_head; 125150974Swpaul sc->sis_ldata->sis_tx_list[cur].sis_ctl &= ~SIS_CMDSTS_MORE; 125250974Swpaul sc->sis_ldata->sis_tx_list[*txidx].sis_ctl |= SIS_CMDSTS_OWN; 125350974Swpaul sc->sis_cdata.sis_tx_cnt += cnt; 125450974Swpaul *txidx = frag; 125550974Swpaul 125650974Swpaul return(0); 125750974Swpaul} 125850974Swpaul 125950974Swpaul/* 126050974Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 126150974Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 126250974Swpaul * copy of the pointers since the transmit list fragment pointers are 126350974Swpaul * physical addresses. 126450974Swpaul */ 126550974Swpaul 126650974Swpaulstatic void sis_start(ifp) 126750974Swpaul struct ifnet *ifp; 126850974Swpaul{ 126950974Swpaul struct sis_softc *sc; 127050974Swpaul struct mbuf *m_head = NULL; 127150974Swpaul u_int32_t idx; 127250974Swpaul 127350974Swpaul sc = ifp->if_softc; 127450974Swpaul 127550974Swpaul idx = sc->sis_cdata.sis_tx_prod; 127650974Swpaul 127750974Swpaul if (ifp->if_flags & IFF_OACTIVE) 127850974Swpaul return; 127950974Swpaul 128050974Swpaul while(sc->sis_ldata->sis_tx_list[idx].sis_mbuf == NULL) { 128150974Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 128250974Swpaul if (m_head == NULL) 128350974Swpaul break; 128450974Swpaul 128550974Swpaul if (sis_encap(sc, m_head, &idx)) { 128650974Swpaul IF_PREPEND(&ifp->if_snd, m_head); 128750974Swpaul ifp->if_flags |= IFF_OACTIVE; 128850974Swpaul break; 128950974Swpaul } 129050974Swpaul 129150974Swpaul /* 129250974Swpaul * If there's a BPF listener, bounce a copy of this frame 129350974Swpaul * to him. 129450974Swpaul */ 129550974Swpaul if (ifp->if_bpf) 129650974Swpaul bpf_mtap(ifp, m_head); 129751583Swpaul 129850974Swpaul } 129950974Swpaul 130050974Swpaul /* Transmit */ 130150974Swpaul sc->sis_cdata.sis_tx_prod = idx; 130250974Swpaul SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_ENABLE); 130350974Swpaul 130450974Swpaul /* 130550974Swpaul * Set a timeout in case the chip goes out to lunch. 130650974Swpaul */ 130750974Swpaul ifp->if_timer = 5; 130850974Swpaul 130950974Swpaul return; 131050974Swpaul} 131150974Swpaul 131250974Swpaulstatic void sis_init(xsc) 131350974Swpaul void *xsc; 131450974Swpaul{ 131550974Swpaul struct sis_softc *sc = xsc; 131650974Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 131750974Swpaul struct mii_data *mii; 131850974Swpaul int s; 131950974Swpaul 132050974Swpaul s = splimp(); 132150974Swpaul 132250974Swpaul /* 132350974Swpaul * Cancel pending I/O and free all RX/TX buffers. 132450974Swpaul */ 132550974Swpaul sis_stop(sc); 132650974Swpaul 132750974Swpaul mii = device_get_softc(sc->sis_miibus); 132850974Swpaul 132950974Swpaul /* Set MAC address */ 133062672Swpaul if (sc->sis_type == SIS_TYPE_83815) { 133162672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR0); 133262672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 133362672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); 133462672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR1); 133562672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 133662672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); 133762672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR2); 133862672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 133962672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); 134062672Swpaul } else { 134162672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR0); 134262672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 134362672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); 134462672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR1); 134562672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 134662672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); 134762672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR2); 134862672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 134962672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); 135062672Swpaul } 135150974Swpaul 135250974Swpaul /* Init circular RX list. */ 135350974Swpaul if (sis_list_rx_init(sc) == ENOBUFS) { 135450974Swpaul printf("sis%d: initialization failed: no " 135550974Swpaul "memory for rx buffers\n", sc->sis_unit); 135650974Swpaul sis_stop(sc); 135750974Swpaul (void)splx(s); 135850974Swpaul return; 135950974Swpaul } 136050974Swpaul 136150974Swpaul /* 136250974Swpaul * Init tx descriptors. 136350974Swpaul */ 136450974Swpaul sis_list_tx_init(sc); 136550974Swpaul 136662672Swpaul /* 136762672Swpaul * For the NatSemi chip, we have to explicitly enable the 136862672Swpaul * reception of ARP frames, as well as turn on the 'perfect 136962672Swpaul * match' filter where we store the station address, otherwise 137062672Swpaul * we won't receive unicasts meant for this host. 137162672Swpaul */ 137262672Swpaul if (sc->sis_type == SIS_TYPE_83815) { 137362672Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_ARP); 137462672Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_PERFECT); 137562672Swpaul } 137662672Swpaul 137750974Swpaul /* If we want promiscuous mode, set the allframes bit. */ 137850974Swpaul if (ifp->if_flags & IFF_PROMISC) { 137950974Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS); 138050974Swpaul } else { 138150974Swpaul SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS); 138250974Swpaul } 138350974Swpaul 138450974Swpaul /* 138550974Swpaul * Set the capture broadcast bit to capture broadcast frames. 138650974Swpaul */ 138750974Swpaul if (ifp->if_flags & IFF_BROADCAST) { 138850974Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD); 138950974Swpaul } else { 139050974Swpaul SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD); 139150974Swpaul } 139250974Swpaul 139350974Swpaul /* 139450974Swpaul * Load the multicast filter. 139550974Swpaul */ 139662672Swpaul if (sc->sis_type == SIS_TYPE_83815) 139762672Swpaul sis_setmulti_ns(sc); 139862672Swpaul else 139962672Swpaul sis_setmulti_sis(sc); 140050974Swpaul 140150974Swpaul /* Turn the receive filter on */ 140250974Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ENABLE); 140350974Swpaul 140450974Swpaul /* 140550974Swpaul * Load the address of the RX and TX lists. 140650974Swpaul */ 140750974Swpaul CSR_WRITE_4(sc, SIS_RX_LISTPTR, 140850974Swpaul vtophys(&sc->sis_ldata->sis_rx_list[0])); 140950974Swpaul CSR_WRITE_4(sc, SIS_TX_LISTPTR, 141050974Swpaul vtophys(&sc->sis_ldata->sis_tx_list[0])); 141150974Swpaul 141250974Swpaul /* Set RX configuration */ 141350974Swpaul CSR_WRITE_4(sc, SIS_RX_CFG, SIS_RXCFG); 141450974Swpaul /* Set TX configuration */ 141550974Swpaul CSR_WRITE_4(sc, SIS_TX_CFG, SIS_TXCFG); 141650974Swpaul 141750974Swpaul /* 141850974Swpaul * Enable interrupts. 141950974Swpaul */ 142050974Swpaul CSR_WRITE_4(sc, SIS_IMR, SIS_INTRS); 142150974Swpaul CSR_WRITE_4(sc, SIS_IER, 1); 142250974Swpaul 142350974Swpaul /* Enable receiver and transmitter. */ 142450974Swpaul SIS_CLRBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE); 142550974Swpaul SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); 142650974Swpaul 142750974Swpaul mii_mediachg(mii); 142850974Swpaul 142950974Swpaul ifp->if_flags |= IFF_RUNNING; 143050974Swpaul ifp->if_flags &= ~IFF_OACTIVE; 143150974Swpaul 143250974Swpaul (void)splx(s); 143350974Swpaul 143450974Swpaul sc->sis_stat_ch = timeout(sis_tick, sc, hz); 143550974Swpaul 143650974Swpaul return; 143750974Swpaul} 143850974Swpaul 143950974Swpaul/* 144050974Swpaul * Set media options. 144150974Swpaul */ 144250974Swpaulstatic int sis_ifmedia_upd(ifp) 144350974Swpaul struct ifnet *ifp; 144450974Swpaul{ 144550974Swpaul struct sis_softc *sc; 144650974Swpaul 144750974Swpaul sc = ifp->if_softc; 144850974Swpaul 144950974Swpaul if (ifp->if_flags & IFF_UP) 145050974Swpaul sis_init(sc); 145150974Swpaul 145250974Swpaul return(0); 145350974Swpaul} 145450974Swpaul 145550974Swpaul/* 145650974Swpaul * Report current media status. 145750974Swpaul */ 145850974Swpaulstatic void sis_ifmedia_sts(ifp, ifmr) 145950974Swpaul struct ifnet *ifp; 146050974Swpaul struct ifmediareq *ifmr; 146150974Swpaul{ 146250974Swpaul struct sis_softc *sc; 146350974Swpaul struct mii_data *mii; 146450974Swpaul 146550974Swpaul sc = ifp->if_softc; 146650974Swpaul 146750974Swpaul mii = device_get_softc(sc->sis_miibus); 146850974Swpaul mii_pollstat(mii); 146950974Swpaul ifmr->ifm_active = mii->mii_media_active; 147050974Swpaul ifmr->ifm_status = mii->mii_media_status; 147150974Swpaul 147250974Swpaul return; 147350974Swpaul} 147450974Swpaul 147550974Swpaulstatic int sis_ioctl(ifp, command, data) 147650974Swpaul struct ifnet *ifp; 147750974Swpaul u_long command; 147850974Swpaul caddr_t data; 147950974Swpaul{ 148050974Swpaul struct sis_softc *sc = ifp->if_softc; 148150974Swpaul struct ifreq *ifr = (struct ifreq *) data; 148250974Swpaul struct mii_data *mii; 148350974Swpaul int s, error = 0; 148450974Swpaul 148550974Swpaul s = splimp(); 148650974Swpaul 148750974Swpaul switch(command) { 148850974Swpaul case SIOCSIFADDR: 148950974Swpaul case SIOCGIFADDR: 149050974Swpaul case SIOCSIFMTU: 149150974Swpaul error = ether_ioctl(ifp, command, data); 149250974Swpaul break; 149350974Swpaul case SIOCSIFFLAGS: 149450974Swpaul if (ifp->if_flags & IFF_UP) { 149550974Swpaul sis_init(sc); 149650974Swpaul } else { 149750974Swpaul if (ifp->if_flags & IFF_RUNNING) 149850974Swpaul sis_stop(sc); 149950974Swpaul } 150050974Swpaul error = 0; 150150974Swpaul break; 150250974Swpaul case SIOCADDMULTI: 150350974Swpaul case SIOCDELMULTI: 150462672Swpaul if (sc->sis_type == SIS_TYPE_83815) 150562672Swpaul sis_setmulti_ns(sc); 150662672Swpaul else 150762672Swpaul sis_setmulti_sis(sc); 150850974Swpaul error = 0; 150950974Swpaul break; 151050974Swpaul case SIOCGIFMEDIA: 151150974Swpaul case SIOCSIFMEDIA: 151250974Swpaul mii = device_get_softc(sc->sis_miibus); 151350974Swpaul error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 151450974Swpaul break; 151550974Swpaul default: 151650974Swpaul error = EINVAL; 151750974Swpaul break; 151850974Swpaul } 151950974Swpaul 152050974Swpaul (void)splx(s); 152150974Swpaul 152250974Swpaul return(error); 152350974Swpaul} 152450974Swpaul 152550974Swpaulstatic void sis_watchdog(ifp) 152650974Swpaul struct ifnet *ifp; 152750974Swpaul{ 152850974Swpaul struct sis_softc *sc; 152950974Swpaul 153050974Swpaul sc = ifp->if_softc; 153150974Swpaul 153250974Swpaul ifp->if_oerrors++; 153350974Swpaul printf("sis%d: watchdog timeout\n", sc->sis_unit); 153450974Swpaul 153550974Swpaul sis_stop(sc); 153650974Swpaul sis_reset(sc); 153750974Swpaul sis_init(sc); 153850974Swpaul 153950974Swpaul if (ifp->if_snd.ifq_head != NULL) 154050974Swpaul sis_start(ifp); 154150974Swpaul 154250974Swpaul return; 154350974Swpaul} 154450974Swpaul 154550974Swpaul/* 154650974Swpaul * Stop the adapter and free any mbufs allocated to the 154750974Swpaul * RX and TX lists. 154850974Swpaul */ 154950974Swpaulstatic void sis_stop(sc) 155050974Swpaul struct sis_softc *sc; 155150974Swpaul{ 155250974Swpaul register int i; 155350974Swpaul struct ifnet *ifp; 155450974Swpaul 155550974Swpaul ifp = &sc->arpcom.ac_if; 155650974Swpaul ifp->if_timer = 0; 155750974Swpaul 155850974Swpaul untimeout(sis_tick, sc, sc->sis_stat_ch); 155950974Swpaul CSR_WRITE_4(sc, SIS_IER, 0); 156050974Swpaul CSR_WRITE_4(sc, SIS_IMR, 0); 156150974Swpaul SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE); 156250974Swpaul DELAY(1000); 156350974Swpaul CSR_WRITE_4(sc, SIS_TX_LISTPTR, 0); 156450974Swpaul CSR_WRITE_4(sc, SIS_RX_LISTPTR, 0); 156550974Swpaul 156650974Swpaul /* 156750974Swpaul * Free data in the RX lists. 156850974Swpaul */ 156950974Swpaul for (i = 0; i < SIS_RX_LIST_CNT; i++) { 157050974Swpaul if (sc->sis_ldata->sis_rx_list[i].sis_mbuf != NULL) { 157150974Swpaul m_freem(sc->sis_ldata->sis_rx_list[i].sis_mbuf); 157250974Swpaul sc->sis_ldata->sis_rx_list[i].sis_mbuf = NULL; 157350974Swpaul } 157450974Swpaul } 157550974Swpaul bzero((char *)&sc->sis_ldata->sis_rx_list, 157650974Swpaul sizeof(sc->sis_ldata->sis_rx_list)); 157750974Swpaul 157850974Swpaul /* 157950974Swpaul * Free the TX list buffers. 158050974Swpaul */ 158150974Swpaul for (i = 0; i < SIS_TX_LIST_CNT; i++) { 158250974Swpaul if (sc->sis_ldata->sis_tx_list[i].sis_mbuf != NULL) { 158350974Swpaul m_freem(sc->sis_ldata->sis_tx_list[i].sis_mbuf); 158450974Swpaul sc->sis_ldata->sis_tx_list[i].sis_mbuf = NULL; 158550974Swpaul } 158650974Swpaul } 158750974Swpaul 158850974Swpaul bzero((char *)&sc->sis_ldata->sis_tx_list, 158950974Swpaul sizeof(sc->sis_ldata->sis_tx_list)); 159050974Swpaul 159150974Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 159250974Swpaul 159350974Swpaul return; 159450974Swpaul} 159550974Swpaul 159650974Swpaul/* 159750974Swpaul * Stop all chip I/O so that the kernel's probe routines don't 159850974Swpaul * get confused by errant DMAs when rebooting. 159950974Swpaul */ 160050974Swpaulstatic void sis_shutdown(dev) 160150974Swpaul device_t dev; 160250974Swpaul{ 160350974Swpaul struct sis_softc *sc; 160450974Swpaul 160550974Swpaul sc = device_get_softc(dev); 160650974Swpaul 160750974Swpaul sis_reset(sc); 160850974Swpaul sis_stop(sc); 160950974Swpaul 161050974Swpaul return; 161150974Swpaul} 1612