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