if_sis.c revision 87059
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 87059 2001-11-28 16:10:37Z luigi $ 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> 6787059Sluigi#include <sys/sysctl.h> 6850974Swpaul 6950974Swpaul#include <net/if.h> 7050974Swpaul#include <net/if_arp.h> 7150974Swpaul#include <net/ethernet.h> 7250974Swpaul#include <net/if_dl.h> 7350974Swpaul#include <net/if_media.h> 7450974Swpaul 7550974Swpaul#include <net/bpf.h> 7650974Swpaul 7750974Swpaul#include <machine/bus_pio.h> 7850974Swpaul#include <machine/bus_memio.h> 7950974Swpaul#include <machine/bus.h> 8050974Swpaul#include <machine/resource.h> 8150974Swpaul#include <sys/bus.h> 8250974Swpaul#include <sys/rman.h> 8350974Swpaul 8450974Swpaul#include <dev/mii/mii.h> 8550974Swpaul#include <dev/mii/miivar.h> 8650974Swpaul 8750974Swpaul#include <pci/pcireg.h> 8850974Swpaul#include <pci/pcivar.h> 8950974Swpaul 9050974Swpaul#define SIS_USEIOSPACE 9150974Swpaul 9250974Swpaul#include <pci/if_sisreg.h> 9350974Swpaul 9459758SpeterMODULE_DEPEND(sis, miibus, 1, 1, 1); 9559758Speter 9651089Speter/* "controller miibus0" required. See GENERIC if you get errors here. */ 9750974Swpaul#include "miibus_if.h" 9850974Swpaul 9950974Swpaul#ifndef lint 10050974Swpaulstatic const char rcsid[] = 10150974Swpaul "$FreeBSD: head/sys/pci/if_sis.c 87059 2001-11-28 16:10:37Z luigi $"; 10250974Swpaul#endif 10350974Swpaul 10450974Swpaul/* 10550974Swpaul * Various supported device vendors/types and their names. 10650974Swpaul */ 10750974Swpaulstatic struct sis_type sis_devs[] = { 10850974Swpaul { SIS_VENDORID, SIS_DEVICEID_900, "SiS 900 10/100BaseTX" }, 10950974Swpaul { SIS_VENDORID, SIS_DEVICEID_7016, "SiS 7016 10/100BaseTX" }, 11062672Swpaul { NS_VENDORID, NS_DEVICEID_DP83815, "NatSemi DP83815 10/100BaseTX" }, 11150974Swpaul { 0, 0, NULL } 11250974Swpaul}; 11350974Swpaul 11450974Swpaulstatic int sis_probe __P((device_t)); 11550974Swpaulstatic int sis_attach __P((device_t)); 11650974Swpaulstatic int sis_detach __P((device_t)); 11750974Swpaul 11850974Swpaulstatic int sis_newbuf __P((struct sis_softc *, 11950974Swpaul struct sis_desc *, 12050974Swpaul struct mbuf *)); 12150974Swpaulstatic int sis_encap __P((struct sis_softc *, 12250974Swpaul struct mbuf *, u_int32_t *)); 12350974Swpaulstatic void sis_rxeof __P((struct sis_softc *)); 12450974Swpaulstatic void sis_rxeoc __P((struct sis_softc *)); 12550974Swpaulstatic void sis_txeof __P((struct sis_softc *)); 12650974Swpaulstatic void sis_intr __P((void *)); 12750974Swpaulstatic void sis_tick __P((void *)); 12850974Swpaulstatic void sis_start __P((struct ifnet *)); 12950974Swpaulstatic int sis_ioctl __P((struct ifnet *, u_long, caddr_t)); 13050974Swpaulstatic void sis_init __P((void *)); 13150974Swpaulstatic void sis_stop __P((struct sis_softc *)); 13250974Swpaulstatic void sis_watchdog __P((struct ifnet *)); 13350974Swpaulstatic void sis_shutdown __P((device_t)); 13450974Swpaulstatic int sis_ifmedia_upd __P((struct ifnet *)); 13550974Swpaulstatic void sis_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 13650974Swpaul 13762672Swpaulstatic u_int16_t sis_reverse __P((u_int16_t)); 13850974Swpaulstatic void sis_delay __P((struct sis_softc *)); 13950974Swpaulstatic void sis_eeprom_idle __P((struct sis_softc *)); 14050974Swpaulstatic void sis_eeprom_putbyte __P((struct sis_softc *, int)); 14150974Swpaulstatic void sis_eeprom_getword __P((struct sis_softc *, int, u_int16_t *)); 14250974Swpaulstatic void sis_read_eeprom __P((struct sis_softc *, caddr_t, int, 14350974Swpaul int, int)); 14472197Swpaul#ifdef __i386__ 14572197Swpaulstatic void sis_read_cmos __P((struct sis_softc *, device_t, caddr_t, 14672197Swpaul int, int)); 14772197Swpaulstatic device_t sis_find_bridge __P((device_t)); 14872197Swpaul#endif 14972197Swpaul 15050974Swpaulstatic int sis_miibus_readreg __P((device_t, int, int)); 15150974Swpaulstatic int sis_miibus_writereg __P((device_t, int, int, int)); 15250974Swpaulstatic void sis_miibus_statchg __P((device_t)); 15350974Swpaul 15462672Swpaulstatic void sis_setmulti_sis __P((struct sis_softc *)); 15562672Swpaulstatic void sis_setmulti_ns __P((struct sis_softc *)); 15662672Swpaulstatic u_int32_t sis_crc __P((struct sis_softc *, caddr_t)); 15750974Swpaulstatic void sis_reset __P((struct sis_softc *)); 15850974Swpaulstatic int sis_list_rx_init __P((struct sis_softc *)); 15950974Swpaulstatic int sis_list_tx_init __P((struct sis_softc *)); 16050974Swpaul 16181713Swpaulstatic void sis_dma_map_desc_ptr __P((void *, bus_dma_segment_t *, 16281713Swpaul int, int)); 16381713Swpaulstatic void sis_dma_map_desc_next __P((void *, bus_dma_segment_t *, 16481713Swpaul int, int)); 16581713Swpaulstatic void sis_dma_map_ring __P((void *, bus_dma_segment_t *, 16681713Swpaul int, int)); 16750974Swpaul#ifdef SIS_USEIOSPACE 16850974Swpaul#define SIS_RES SYS_RES_IOPORT 16950974Swpaul#define SIS_RID SIS_PCI_LOIO 17050974Swpaul#else 17151030Swpaul#define SIS_RES SYS_RES_MEMORY 17251030Swpaul#define SIS_RID SIS_PCI_LOMEM 17350974Swpaul#endif 17450974Swpaul 17550974Swpaulstatic device_method_t sis_methods[] = { 17650974Swpaul /* Device interface */ 17750974Swpaul DEVMETHOD(device_probe, sis_probe), 17850974Swpaul DEVMETHOD(device_attach, sis_attach), 17950974Swpaul DEVMETHOD(device_detach, sis_detach), 18050974Swpaul DEVMETHOD(device_shutdown, sis_shutdown), 18150974Swpaul 18250974Swpaul /* bus interface */ 18350974Swpaul DEVMETHOD(bus_print_child, bus_generic_print_child), 18450974Swpaul DEVMETHOD(bus_driver_added, bus_generic_driver_added), 18550974Swpaul 18650974Swpaul /* MII interface */ 18750974Swpaul DEVMETHOD(miibus_readreg, sis_miibus_readreg), 18850974Swpaul DEVMETHOD(miibus_writereg, sis_miibus_writereg), 18950974Swpaul DEVMETHOD(miibus_statchg, sis_miibus_statchg), 19050974Swpaul 19150974Swpaul { 0, 0 } 19250974Swpaul}; 19350974Swpaul 19450974Swpaulstatic driver_t sis_driver = { 19551455Swpaul "sis", 19650974Swpaul sis_methods, 19750974Swpaul sizeof(struct sis_softc) 19850974Swpaul}; 19950974Swpaul 20050974Swpaulstatic devclass_t sis_devclass; 20150974Swpaul 20287059Sluigi#ifdef __i386__ 20387059Sluigistatic int sis_quick=1; 20487059SluigiSYSCTL_INT(_hw, OID_AUTO, sis_quick, CTLFLAG_RW, 20587059Sluigi &sis_quick,0,"do not mdevget in sis driver"); 20687059Sluigi#endif 20787059Sluigi 20851533SwpaulDRIVER_MODULE(if_sis, pci, sis_driver, sis_devclass, 0, 0); 20951473SwpaulDRIVER_MODULE(miibus, sis, miibus_driver, miibus_devclass, 0, 0); 21050974Swpaul 21150974Swpaul#define SIS_SETBIT(sc, reg, x) \ 21250974Swpaul CSR_WRITE_4(sc, reg, \ 21350974Swpaul CSR_READ_4(sc, reg) | (x)) 21450974Swpaul 21550974Swpaul#define SIS_CLRBIT(sc, reg, x) \ 21650974Swpaul CSR_WRITE_4(sc, reg, \ 21750974Swpaul CSR_READ_4(sc, reg) & ~(x)) 21850974Swpaul 21950974Swpaul#define SIO_SET(x) \ 22050974Swpaul CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) | x) 22150974Swpaul 22250974Swpaul#define SIO_CLR(x) \ 22350974Swpaul CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) & ~x) 22450974Swpaul 22581713Swpaulstatic void 22681713Swpaulsis_dma_map_desc_next(arg, segs, nseg, error) 22781713Swpaul void *arg; 22881713Swpaul bus_dma_segment_t *segs; 22981713Swpaul int nseg, error; 23081713Swpaul{ 23181713Swpaul struct sis_desc *r; 23281713Swpaul 23381713Swpaul r = arg; 23481713Swpaul r->sis_next = segs->ds_addr; 23581713Swpaul 23681713Swpaul return; 23781713Swpaul} 23881713Swpaul 23981713Swpaulstatic void 24081713Swpaulsis_dma_map_desc_ptr(arg, segs, nseg, error) 24181713Swpaul void *arg; 24281713Swpaul bus_dma_segment_t *segs; 24381713Swpaul int nseg, error; 24481713Swpaul{ 24581713Swpaul struct sis_desc *r; 24681713Swpaul 24781713Swpaul r = arg; 24881713Swpaul r->sis_ptr = segs->ds_addr; 24981713Swpaul 25081713Swpaul return; 25181713Swpaul} 25281713Swpaul 25381713Swpaulstatic void 25481713Swpaulsis_dma_map_ring(arg, segs, nseg, error) 25581713Swpaul void *arg; 25681713Swpaul bus_dma_segment_t *segs; 25781713Swpaul int nseg, error; 25881713Swpaul{ 25981713Swpaul u_int32_t *p; 26081713Swpaul 26181713Swpaul p = arg; 26281713Swpaul *p = segs->ds_addr; 26381713Swpaul 26481713Swpaul return; 26581713Swpaul} 26681713Swpaul 26762672Swpaul/* 26862672Swpaul * Routine to reverse the bits in a word. Stolen almost 26962672Swpaul * verbatim from /usr/games/fortune. 27062672Swpaul */ 27162672Swpaulstatic u_int16_t sis_reverse(n) 27262672Swpaul u_int16_t n; 27362672Swpaul{ 27462672Swpaul n = ((n >> 1) & 0x5555) | ((n << 1) & 0xaaaa); 27562672Swpaul n = ((n >> 2) & 0x3333) | ((n << 2) & 0xcccc); 27662672Swpaul n = ((n >> 4) & 0x0f0f) | ((n << 4) & 0xf0f0); 27762672Swpaul n = ((n >> 8) & 0x00ff) | ((n << 8) & 0xff00); 27862672Swpaul 27962672Swpaul return(n); 28062672Swpaul} 28162672Swpaul 28250974Swpaulstatic void sis_delay(sc) 28350974Swpaul struct sis_softc *sc; 28450974Swpaul{ 28550974Swpaul int idx; 28650974Swpaul 28750974Swpaul for (idx = (300 / 33) + 1; idx > 0; idx--) 28850974Swpaul CSR_READ_4(sc, SIS_CSR); 28950974Swpaul 29050974Swpaul return; 29150974Swpaul} 29250974Swpaul 29350974Swpaulstatic void sis_eeprom_idle(sc) 29450974Swpaul struct sis_softc *sc; 29550974Swpaul{ 29650974Swpaul register int i; 29750974Swpaul 29850974Swpaul SIO_SET(SIS_EECTL_CSEL); 29950974Swpaul sis_delay(sc); 30050974Swpaul SIO_SET(SIS_EECTL_CLK); 30150974Swpaul sis_delay(sc); 30250974Swpaul 30350974Swpaul for (i = 0; i < 25; i++) { 30450974Swpaul SIO_CLR(SIS_EECTL_CLK); 30550974Swpaul sis_delay(sc); 30650974Swpaul SIO_SET(SIS_EECTL_CLK); 30750974Swpaul sis_delay(sc); 30850974Swpaul } 30950974Swpaul 31050974Swpaul SIO_CLR(SIS_EECTL_CLK); 31150974Swpaul sis_delay(sc); 31250974Swpaul SIO_CLR(SIS_EECTL_CSEL); 31350974Swpaul sis_delay(sc); 31450974Swpaul CSR_WRITE_4(sc, SIS_EECTL, 0x00000000); 31550974Swpaul 31650974Swpaul return; 31750974Swpaul} 31850974Swpaul 31950974Swpaul/* 32050974Swpaul * Send a read command and address to the EEPROM, check for ACK. 32150974Swpaul */ 32250974Swpaulstatic void sis_eeprom_putbyte(sc, addr) 32350974Swpaul struct sis_softc *sc; 32450974Swpaul int addr; 32550974Swpaul{ 32650974Swpaul register int d, i; 32750974Swpaul 32850974Swpaul d = addr | SIS_EECMD_READ; 32950974Swpaul 33050974Swpaul /* 33150974Swpaul * Feed in each bit and stobe the clock. 33250974Swpaul */ 33350974Swpaul for (i = 0x400; i; i >>= 1) { 33450974Swpaul if (d & i) { 33550974Swpaul SIO_SET(SIS_EECTL_DIN); 33650974Swpaul } else { 33750974Swpaul SIO_CLR(SIS_EECTL_DIN); 33850974Swpaul } 33950974Swpaul sis_delay(sc); 34050974Swpaul SIO_SET(SIS_EECTL_CLK); 34150974Swpaul sis_delay(sc); 34250974Swpaul SIO_CLR(SIS_EECTL_CLK); 34350974Swpaul sis_delay(sc); 34450974Swpaul } 34550974Swpaul 34650974Swpaul return; 34750974Swpaul} 34850974Swpaul 34950974Swpaul/* 35050974Swpaul * Read a word of data stored in the EEPROM at address 'addr.' 35150974Swpaul */ 35250974Swpaulstatic void sis_eeprom_getword(sc, addr, dest) 35350974Swpaul struct sis_softc *sc; 35450974Swpaul int addr; 35550974Swpaul u_int16_t *dest; 35650974Swpaul{ 35750974Swpaul register int i; 35850974Swpaul u_int16_t word = 0; 35950974Swpaul 36050974Swpaul /* Force EEPROM to idle state. */ 36150974Swpaul sis_eeprom_idle(sc); 36250974Swpaul 36350974Swpaul /* Enter EEPROM access mode. */ 36450974Swpaul sis_delay(sc); 36562672Swpaul SIO_CLR(SIS_EECTL_CLK); 36662672Swpaul sis_delay(sc); 36750974Swpaul SIO_SET(SIS_EECTL_CSEL); 36850974Swpaul sis_delay(sc); 36950974Swpaul 37050974Swpaul /* 37150974Swpaul * Send address of word we want to read. 37250974Swpaul */ 37350974Swpaul sis_eeprom_putbyte(sc, addr); 37450974Swpaul 37550974Swpaul /* 37650974Swpaul * Start reading bits from EEPROM. 37750974Swpaul */ 37850974Swpaul for (i = 0x8000; i; i >>= 1) { 37950974Swpaul SIO_SET(SIS_EECTL_CLK); 38050974Swpaul sis_delay(sc); 38150974Swpaul if (CSR_READ_4(sc, SIS_EECTL) & SIS_EECTL_DOUT) 38250974Swpaul word |= i; 38350974Swpaul sis_delay(sc); 38450974Swpaul SIO_CLR(SIS_EECTL_CLK); 38550974Swpaul sis_delay(sc); 38650974Swpaul } 38750974Swpaul 38850974Swpaul /* Turn off EEPROM access mode. */ 38950974Swpaul sis_eeprom_idle(sc); 39050974Swpaul 39150974Swpaul *dest = word; 39250974Swpaul 39350974Swpaul return; 39450974Swpaul} 39550974Swpaul 39650974Swpaul/* 39750974Swpaul * Read a sequence of words from the EEPROM. 39850974Swpaul */ 39950974Swpaulstatic void sis_read_eeprom(sc, dest, off, cnt, swap) 40050974Swpaul struct sis_softc *sc; 40150974Swpaul caddr_t dest; 40250974Swpaul int off; 40350974Swpaul int cnt; 40450974Swpaul int swap; 40550974Swpaul{ 40650974Swpaul int i; 40750974Swpaul u_int16_t word = 0, *ptr; 40850974Swpaul 40950974Swpaul for (i = 0; i < cnt; i++) { 41050974Swpaul sis_eeprom_getword(sc, off + i, &word); 41150974Swpaul ptr = (u_int16_t *)(dest + (i * 2)); 41250974Swpaul if (swap) 41350974Swpaul *ptr = ntohs(word); 41450974Swpaul else 41550974Swpaul *ptr = word; 41650974Swpaul } 41750974Swpaul 41850974Swpaul return; 41950974Swpaul} 42050974Swpaul 42172197Swpaul#ifdef __i386__ 42272197Swpaulstatic device_t sis_find_bridge(dev) 42372197Swpaul device_t dev; 42472197Swpaul{ 42572197Swpaul devclass_t pci_devclass; 42672197Swpaul device_t *pci_devices; 42772197Swpaul int pci_count = 0; 42872197Swpaul device_t *pci_children; 42972197Swpaul int pci_childcount = 0; 43072197Swpaul device_t *busp, *childp; 43172197Swpaul int i, j; 43272197Swpaul 43372197Swpaul if ((pci_devclass = devclass_find("pci")) == NULL) 43472197Swpaul return(NULL); 43572197Swpaul 43672197Swpaul devclass_get_devices(pci_devclass, &pci_devices, &pci_count); 43772197Swpaul 43872197Swpaul for (i = 0, busp = pci_devices; i < pci_count; i++, busp++) { 43972197Swpaul pci_childcount = 0; 44072197Swpaul device_get_children(*busp, &pci_children, &pci_childcount); 44172197Swpaul for (j = 0, childp = pci_children; 44272197Swpaul j < pci_childcount; j++, childp++) { 44372197Swpaul if (pci_get_vendor(*childp) == SIS_VENDORID && 44472197Swpaul pci_get_device(*childp) == 0x0008) { 44572197Swpaul free(pci_devices, M_TEMP); 44672197Swpaul free(pci_children, M_TEMP); 44772197Swpaul return(*childp); 44872197Swpaul } 44972197Swpaul } 45072197Swpaul } 45172197Swpaul 45272197Swpaul free(pci_devices, M_TEMP); 45372197Swpaul free(pci_children, M_TEMP); 45472197Swpaul return(NULL); 45572197Swpaul} 45672197Swpaul 45772197Swpaulstatic void sis_read_cmos(sc, dev, dest, off, cnt) 45872197Swpaul struct sis_softc *sc; 45972197Swpaul device_t dev; 46072197Swpaul caddr_t dest; 46172197Swpaul int off; 46272197Swpaul int cnt; 46372197Swpaul{ 46472197Swpaul device_t bridge; 46572197Swpaul u_int8_t reg; 46672197Swpaul int i; 46772197Swpaul bus_space_tag_t btag; 46872197Swpaul 46972197Swpaul bridge = sis_find_bridge(dev); 47072197Swpaul if (bridge == NULL) 47172197Swpaul return; 47272197Swpaul reg = pci_read_config(bridge, 0x48, 1); 47372197Swpaul pci_write_config(bridge, 0x48, reg|0x40, 1); 47472197Swpaul 47572197Swpaul /* XXX */ 47672197Swpaul btag = I386_BUS_SPACE_IO; 47772197Swpaul 47872197Swpaul for (i = 0; i < cnt; i++) { 47972197Swpaul bus_space_write_1(btag, 0x0, 0x70, i + off); 48072197Swpaul *(dest + i) = bus_space_read_1(btag, 0x0, 0x71); 48172197Swpaul } 48272197Swpaul 48372197Swpaul pci_write_config(bridge, 0x48, reg & ~0x40, 1); 48472197Swpaul return; 48572197Swpaul} 48672197Swpaul#endif 48772197Swpaul 48850974Swpaulstatic int sis_miibus_readreg(dev, phy, reg) 48950974Swpaul device_t dev; 49050974Swpaul int phy, reg; 49150974Swpaul{ 49250974Swpaul struct sis_softc *sc; 49362672Swpaul int i, val = 0; 49450974Swpaul 49550974Swpaul sc = device_get_softc(dev); 49650974Swpaul 49762672Swpaul if (sc->sis_type == SIS_TYPE_83815) { 49862672Swpaul if (phy != 0) 49962672Swpaul return(0); 50062672Swpaul /* 50162672Swpaul * The NatSemi chip can take a while after 50262672Swpaul * a reset to come ready, during which the BMSR 50362672Swpaul * returns a value of 0. This is *never* supposed 50462672Swpaul * to happen: some of the BMSR bits are meant to 50562672Swpaul * be hardwired in the on position, and this can 50662672Swpaul * confuse the miibus code a bit during the probe 50762672Swpaul * and attach phase. So we make an effort to check 50862672Swpaul * for this condition and wait for it to clear. 50962672Swpaul */ 51062672Swpaul if (!CSR_READ_4(sc, NS_BMSR)) 51162672Swpaul DELAY(1000); 51262672Swpaul val = CSR_READ_4(sc, NS_BMCR + (reg * 4)); 51362672Swpaul return(val); 51462672Swpaul } 51562672Swpaul 51650974Swpaul if (sc->sis_type == SIS_TYPE_900 && phy != 0) 51750974Swpaul return(0); 51850974Swpaul 51950974Swpaul CSR_WRITE_4(sc, SIS_PHYCTL, (phy << 11) | (reg << 6) | SIS_PHYOP_READ); 52050974Swpaul SIS_SETBIT(sc, SIS_PHYCTL, SIS_PHYCTL_ACCESS); 52150974Swpaul 52250974Swpaul for (i = 0; i < SIS_TIMEOUT; i++) { 52350974Swpaul if (!(CSR_READ_4(sc, SIS_PHYCTL) & SIS_PHYCTL_ACCESS)) 52450974Swpaul break; 52550974Swpaul } 52650974Swpaul 52750974Swpaul if (i == SIS_TIMEOUT) { 52850974Swpaul printf("sis%d: PHY failed to come ready\n", sc->sis_unit); 52950974Swpaul return(0); 53050974Swpaul } 53150974Swpaul 53250974Swpaul val = (CSR_READ_4(sc, SIS_PHYCTL) >> 16) & 0xFFFF; 53350974Swpaul 53450974Swpaul if (val == 0xFFFF) 53550974Swpaul return(0); 53650974Swpaul 53750974Swpaul return(val); 53850974Swpaul} 53950974Swpaul 54050974Swpaulstatic int sis_miibus_writereg(dev, phy, reg, data) 54150974Swpaul device_t dev; 54250974Swpaul int phy, reg, data; 54350974Swpaul{ 54450974Swpaul struct sis_softc *sc; 54550974Swpaul int i; 54650974Swpaul 54750974Swpaul sc = device_get_softc(dev); 54850974Swpaul 54962672Swpaul if (sc->sis_type == SIS_TYPE_83815) { 55062672Swpaul if (phy != 0) 55162672Swpaul return(0); 55262672Swpaul CSR_WRITE_4(sc, NS_BMCR + (reg * 4), data); 55362672Swpaul return(0); 55462672Swpaul } 55562672Swpaul 55650974Swpaul if (sc->sis_type == SIS_TYPE_900 && phy != 0) 55750974Swpaul return(0); 55850974Swpaul 55950974Swpaul CSR_WRITE_4(sc, SIS_PHYCTL, (data << 16) | (phy << 11) | 56050974Swpaul (reg << 6) | SIS_PHYOP_WRITE); 56150974Swpaul SIS_SETBIT(sc, SIS_PHYCTL, SIS_PHYCTL_ACCESS); 56250974Swpaul 56350974Swpaul for (i = 0; i < SIS_TIMEOUT; i++) { 56450974Swpaul if (!(CSR_READ_4(sc, SIS_PHYCTL) & SIS_PHYCTL_ACCESS)) 56550974Swpaul break; 56650974Swpaul } 56750974Swpaul 56850974Swpaul if (i == SIS_TIMEOUT) 56950974Swpaul printf("sis%d: PHY failed to come ready\n", sc->sis_unit); 57050974Swpaul 57150974Swpaul return(0); 57250974Swpaul} 57350974Swpaul 57450974Swpaulstatic void sis_miibus_statchg(dev) 57550974Swpaul device_t dev; 57650974Swpaul{ 57750974Swpaul struct sis_softc *sc; 57850974Swpaul 57950974Swpaul sc = device_get_softc(dev); 58064963Swpaul sis_init(sc); 58150974Swpaul 58250974Swpaul return; 58350974Swpaul} 58450974Swpaul 58562672Swpaulstatic u_int32_t sis_crc(sc, addr) 58662672Swpaul struct sis_softc *sc; 58750974Swpaul caddr_t addr; 58850974Swpaul{ 58950974Swpaul u_int32_t crc, carry; 59050974Swpaul int i, j; 59150974Swpaul u_int8_t c; 59250974Swpaul 59350974Swpaul /* Compute CRC for the address value. */ 59450974Swpaul crc = 0xFFFFFFFF; /* initial value */ 59550974Swpaul 59650974Swpaul for (i = 0; i < 6; i++) { 59750974Swpaul c = *(addr + i); 59850974Swpaul for (j = 0; j < 8; j++) { 59950974Swpaul carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01); 60050974Swpaul crc <<= 1; 60150974Swpaul c >>= 1; 60250974Swpaul if (carry) 60350974Swpaul crc = (crc ^ 0x04c11db6) | carry; 60450974Swpaul } 60550974Swpaul } 60650974Swpaul 60762672Swpaul /* 60862672Swpaul * return the filter bit position 60962672Swpaul * 61062672Swpaul * The NatSemi chip has a 512-bit filter, which is 61162672Swpaul * different than the SiS, so we special-case it. 61262672Swpaul */ 61362672Swpaul if (sc->sis_type == SIS_TYPE_83815) 61462672Swpaul return((crc >> 23) & 0x1FF); 61562672Swpaul 61650974Swpaul return((crc >> 25) & 0x0000007F); 61750974Swpaul} 61850974Swpaul 61962672Swpaulstatic void sis_setmulti_ns(sc) 62050974Swpaul struct sis_softc *sc; 62150974Swpaul{ 62250974Swpaul struct ifnet *ifp; 62350974Swpaul struct ifmultiaddr *ifma; 62450974Swpaul u_int32_t h = 0, i, filtsave; 62562672Swpaul int bit, index; 62650974Swpaul 62750974Swpaul ifp = &sc->arpcom.ac_if; 62850974Swpaul 62950974Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 63062672Swpaul SIS_CLRBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH); 63150974Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); 63250974Swpaul return; 63350974Swpaul } 63450974Swpaul 63562672Swpaul /* 63662672Swpaul * We have to explicitly enable the multicast hash table 63762672Swpaul * on the NatSemi chip if we want to use it, which we do. 63862672Swpaul */ 63962672Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_MCHASH); 64050974Swpaul SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); 64150974Swpaul 64250974Swpaul filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL); 64350974Swpaul 64450974Swpaul /* first, zot all the existing hash bits */ 64562672Swpaul for (i = 0; i < 32; i++) { 64662672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + (i*2)); 64762672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 0); 64862672Swpaul } 64962672Swpaul 65072084Sphk TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 65162672Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 65262672Swpaul continue; 65362672Swpaul h = sis_crc(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 65462672Swpaul index = h >> 3; 65562672Swpaul bit = h & 0x1F; 65662672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_FMEM_LO + index); 65762672Swpaul if (bit > 0xF) 65862672Swpaul bit -= 0x10; 65962672Swpaul SIS_SETBIT(sc, SIS_RXFILT_DATA, (1 << bit)); 66062672Swpaul } 66162672Swpaul 66262672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave); 66362672Swpaul 66462672Swpaul return; 66562672Swpaul} 66662672Swpaul 66762672Swpaulstatic void sis_setmulti_sis(sc) 66862672Swpaul struct sis_softc *sc; 66962672Swpaul{ 67062672Swpaul struct ifnet *ifp; 67162672Swpaul struct ifmultiaddr *ifma; 67262672Swpaul u_int32_t h = 0, i, filtsave; 67362672Swpaul 67462672Swpaul ifp = &sc->arpcom.ac_if; 67562672Swpaul 67662672Swpaul if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC) { 67762672Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); 67862672Swpaul return; 67962672Swpaul } 68062672Swpaul 68162672Swpaul SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLMULTI); 68262672Swpaul 68362672Swpaul filtsave = CSR_READ_4(sc, SIS_RXFILT_CTL); 68462672Swpaul 68562672Swpaul /* first, zot all the existing hash bits */ 68650974Swpaul for (i = 0; i < 8; i++) { 68750974Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, (4 + ((i * 16) >> 4)) << 16); 68850974Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 0); 68950974Swpaul } 69050974Swpaul 69150974Swpaul /* now program new ones */ 69272084Sphk TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 69350974Swpaul if (ifma->ifma_addr->sa_family != AF_LINK) 69450974Swpaul continue; 69562672Swpaul h = sis_crc(sc, LLADDR((struct sockaddr_dl *)ifma->ifma_addr)); 69650974Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, (4 + (h >> 4)) << 16); 69750974Swpaul SIS_SETBIT(sc, SIS_RXFILT_DATA, (1 << (h & 0xF))); 69850974Swpaul } 69950974Swpaul 70050974Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, filtsave); 70150974Swpaul 70250974Swpaul return; 70350974Swpaul} 70450974Swpaul 70550974Swpaulstatic void sis_reset(sc) 70650974Swpaul struct sis_softc *sc; 70750974Swpaul{ 70850974Swpaul register int i; 70950974Swpaul 71050974Swpaul SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RESET); 71150974Swpaul 71250974Swpaul for (i = 0; i < SIS_TIMEOUT; i++) { 71350974Swpaul if (!(CSR_READ_4(sc, SIS_CSR) & SIS_CSR_RESET)) 71450974Swpaul break; 71550974Swpaul } 71650974Swpaul 71750974Swpaul if (i == SIS_TIMEOUT) 71850974Swpaul printf("sis%d: reset never completed\n", sc->sis_unit); 71950974Swpaul 72050974Swpaul /* Wait a little while for the chip to get its brains in order. */ 72150974Swpaul DELAY(1000); 72272813Swpaul 72372813Swpaul /* 72472813Swpaul * If this is a NetSemi chip, make sure to clear 72572813Swpaul * PME mode. 72672813Swpaul */ 72772813Swpaul if (sc->sis_type == SIS_TYPE_83815) { 72872813Swpaul CSR_WRITE_4(sc, NS_CLKRUN, NS_CLKRUN_PMESTS); 72972813Swpaul CSR_WRITE_4(sc, NS_CLKRUN, 0); 73072813Swpaul } 73172813Swpaul 73250974Swpaul return; 73350974Swpaul} 73450974Swpaul 73550974Swpaul/* 73650974Swpaul * Probe for an SiS chip. Check the PCI vendor and device 73750974Swpaul * IDs against our list and return a device name if we find a match. 73850974Swpaul */ 73950974Swpaulstatic int sis_probe(dev) 74050974Swpaul device_t dev; 74150974Swpaul{ 74250974Swpaul struct sis_type *t; 74350974Swpaul 74450974Swpaul t = sis_devs; 74550974Swpaul 74650974Swpaul while(t->sis_name != NULL) { 74750974Swpaul if ((pci_get_vendor(dev) == t->sis_vid) && 74850974Swpaul (pci_get_device(dev) == t->sis_did)) { 74950974Swpaul device_set_desc(dev, t->sis_name); 75050974Swpaul return(0); 75150974Swpaul } 75250974Swpaul t++; 75350974Swpaul } 75450974Swpaul 75550974Swpaul return(ENXIO); 75650974Swpaul} 75750974Swpaul 75850974Swpaul/* 75950974Swpaul * Attach the interface. Allocate softc structures, do ifmedia 76050974Swpaul * setup and ethernet/BPF attach. 76150974Swpaul */ 76250974Swpaulstatic int sis_attach(dev) 76350974Swpaul device_t dev; 76450974Swpaul{ 76550974Swpaul u_char eaddr[ETHER_ADDR_LEN]; 76650974Swpaul u_int32_t command; 76750974Swpaul struct sis_softc *sc; 76850974Swpaul struct ifnet *ifp; 76950974Swpaul int unit, error = 0, rid; 77050974Swpaul 77150974Swpaul sc = device_get_softc(dev); 77250974Swpaul unit = device_get_unit(dev); 77350974Swpaul bzero(sc, sizeof(struct sis_softc)); 77450974Swpaul 77571228Sbmilekic mtx_init(&sc->sis_mtx, device_get_nameunit(dev), MTX_DEF | MTX_RECURSE); 77669583Swpaul SIS_LOCK(sc); 77769583Swpaul 77850974Swpaul if (pci_get_device(dev) == SIS_DEVICEID_900) 77950974Swpaul sc->sis_type = SIS_TYPE_900; 78050974Swpaul if (pci_get_device(dev) == SIS_DEVICEID_7016) 78150974Swpaul sc->sis_type = SIS_TYPE_7016; 78262672Swpaul if (pci_get_vendor(dev) == NS_VENDORID) 78362672Swpaul sc->sis_type = SIS_TYPE_83815; 78450974Swpaul 78550974Swpaul /* 78650974Swpaul * Handle power management nonsense. 78750974Swpaul */ 78872813Swpaul if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { 78972813Swpaul u_int32_t iobase, membase, irq; 79050974Swpaul 79172813Swpaul /* Save important PCI config data. */ 79272813Swpaul iobase = pci_read_config(dev, SIS_PCI_LOIO, 4); 79372813Swpaul membase = pci_read_config(dev, SIS_PCI_LOMEM, 4); 79472813Swpaul irq = pci_read_config(dev, SIS_PCI_INTLINE, 4); 79550974Swpaul 79672813Swpaul /* Reset the power state. */ 79772813Swpaul printf("sis%d: chip is in D%d power mode " 79872813Swpaul "-- setting to D0\n", unit, 79972813Swpaul pci_get_powerstate(dev)); 80072813Swpaul pci_set_powerstate(dev, PCI_POWERSTATE_D0); 80150974Swpaul 80272813Swpaul /* Restore PCI config data. */ 80372813Swpaul pci_write_config(dev, SIS_PCI_LOIO, iobase, 4); 80472813Swpaul pci_write_config(dev, SIS_PCI_LOMEM, membase, 4); 80572813Swpaul pci_write_config(dev, SIS_PCI_INTLINE, irq, 4); 80650974Swpaul } 80750974Swpaul 80850974Swpaul /* 80950974Swpaul * Map control/status registers. 81050974Swpaul */ 81172813Swpaul pci_enable_busmaster(dev); 81279472Swpaul pci_enable_io(dev, SYS_RES_IOPORT); 81379472Swpaul pci_enable_io(dev, SYS_RES_MEMORY); 81461041Speter command = pci_read_config(dev, PCIR_COMMAND, 4); 81550974Swpaul 81650974Swpaul#ifdef SIS_USEIOSPACE 81750974Swpaul if (!(command & PCIM_CMD_PORTEN)) { 81850974Swpaul printf("sis%d: failed to enable I/O ports!\n", unit); 81950974Swpaul error = ENXIO;; 82050974Swpaul goto fail; 82150974Swpaul } 82250974Swpaul#else 82350974Swpaul if (!(command & PCIM_CMD_MEMEN)) { 82450974Swpaul printf("sis%d: failed to enable memory mapping!\n", unit); 82550974Swpaul error = ENXIO;; 82650974Swpaul goto fail; 82750974Swpaul } 82850974Swpaul#endif 82950974Swpaul 83050974Swpaul rid = SIS_RID; 83150974Swpaul sc->sis_res = bus_alloc_resource(dev, SIS_RES, &rid, 83250974Swpaul 0, ~0, 1, RF_ACTIVE); 83350974Swpaul 83450974Swpaul if (sc->sis_res == NULL) { 83550974Swpaul printf("sis%d: couldn't map ports/memory\n", unit); 83650974Swpaul error = ENXIO; 83750974Swpaul goto fail; 83850974Swpaul } 83950974Swpaul 84050974Swpaul sc->sis_btag = rman_get_bustag(sc->sis_res); 84150974Swpaul sc->sis_bhandle = rman_get_bushandle(sc->sis_res); 84250974Swpaul 84350974Swpaul /* Allocate interrupt */ 84450974Swpaul rid = 0; 84550974Swpaul sc->sis_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 84650974Swpaul RF_SHAREABLE | RF_ACTIVE); 84750974Swpaul 84850974Swpaul if (sc->sis_irq == NULL) { 84950974Swpaul printf("sis%d: couldn't map interrupt\n", unit); 85050974Swpaul bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); 85150974Swpaul error = ENXIO; 85250974Swpaul goto fail; 85350974Swpaul } 85450974Swpaul 85550974Swpaul error = bus_setup_intr(dev, sc->sis_irq, INTR_TYPE_NET, 85650974Swpaul sis_intr, sc, &sc->sis_intrhand); 85750974Swpaul 85850974Swpaul if (error) { 85968216Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq); 86050974Swpaul bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); 86150974Swpaul printf("sis%d: couldn't set up irq\n", unit); 86250974Swpaul goto fail; 86350974Swpaul } 86450974Swpaul 86550974Swpaul /* Reset the adapter. */ 86650974Swpaul sis_reset(sc); 86750974Swpaul 86850974Swpaul /* 86950974Swpaul * Get station address from the EEPROM. 87050974Swpaul */ 87162672Swpaul switch (pci_get_vendor(dev)) { 87262672Swpaul case NS_VENDORID: 87362672Swpaul /* 87462672Swpaul * Reading the MAC address out of the EEPROM on 87562672Swpaul * the NatSemi chip takes a bit more work than 87662672Swpaul * you'd expect. The address spans 4 16-bit words, 87762672Swpaul * with the first word containing only a single bit. 87862672Swpaul * You have to shift everything over one bit to 87962672Swpaul * get it aligned properly. Also, the bits are 88062672Swpaul * stored backwards (the LSB is really the MSB, 88162672Swpaul * and so on) so you have to reverse them in order 88262672Swpaul * to get the MAC address into the form we want. 88362672Swpaul * Why? Who the hell knows. 88462672Swpaul */ 88562672Swpaul { 88662672Swpaul u_int16_t tmp[4]; 88750974Swpaul 88862672Swpaul sis_read_eeprom(sc, (caddr_t)&tmp, 88962672Swpaul NS_EE_NODEADDR, 4, 0); 89062672Swpaul 89162672Swpaul /* Shift everything over one bit. */ 89262672Swpaul tmp[3] = tmp[3] >> 1; 89362681Swpaul tmp[3] |= tmp[2] << 15; 89462672Swpaul tmp[2] = tmp[2] >> 1; 89562681Swpaul tmp[2] |= tmp[1] << 15; 89662672Swpaul tmp[1] = tmp[1] >> 1; 89762681Swpaul tmp[1] |= tmp[0] << 15; 89862672Swpaul 89962672Swpaul /* Now reverse all the bits. */ 90062672Swpaul tmp[3] = sis_reverse(tmp[3]); 90162672Swpaul tmp[2] = sis_reverse(tmp[2]); 90262672Swpaul tmp[1] = sis_reverse(tmp[1]); 90362672Swpaul 90462672Swpaul bcopy((char *)&tmp[1], eaddr, ETHER_ADDR_LEN); 90562672Swpaul } 90662672Swpaul break; 90762672Swpaul case SIS_VENDORID: 90862672Swpaul default: 90972197Swpaul#ifdef __i386__ 91072197Swpaul /* 91172197Swpaul * If this is a SiS 630E chipset with an embedded 91272197Swpaul * SiS 900 controller, we have to read the MAC address 91372197Swpaul * from the APC CMOS RAM. Our method for doing this 91472197Swpaul * is very ugly since we have to reach out and grab 91572197Swpaul * ahold of hardware for which we cannot properly 91672197Swpaul * allocate resources. This code is only compiled on 91772197Swpaul * the i386 architecture since the SiS 630E chipset 91872197Swpaul * is for x86 motherboards only. Note that there are 91972197Swpaul * a lot of magic numbers in this hack. These are 92072197Swpaul * taken from SiS's Linux driver. I'd like to replace 92172197Swpaul * them with proper symbolic definitions, but that 92272197Swpaul * requires some datasheets that I don't have access 92372197Swpaul * to at the moment. 92472197Swpaul */ 92572197Swpaul command = pci_read_config(dev, PCIR_REVID, 1); 92672197Swpaul if (command == SIS_REV_630S || 92772197Swpaul command == SIS_REV_630E || 92872197Swpaul command == SIS_REV_630EA1) 92972197Swpaul sis_read_cmos(sc, dev, (caddr_t)&eaddr, 0x9, 6); 93072197Swpaul else 93172197Swpaul#endif 93272197Swpaul sis_read_eeprom(sc, (caddr_t)&eaddr, 93372197Swpaul SIS_EE_NODEADDR, 3, 0); 93462672Swpaul break; 93562672Swpaul } 93662672Swpaul 93750974Swpaul /* 93850974Swpaul * A SiS chip was detected. Inform the world. 93950974Swpaul */ 94050974Swpaul printf("sis%d: Ethernet address: %6D\n", unit, eaddr, ":"); 94150974Swpaul 94250974Swpaul sc->sis_unit = unit; 94350974Swpaul callout_handle_init(&sc->sis_stat_ch); 94450974Swpaul bcopy(eaddr, (char *)&sc->arpcom.ac_enaddr, ETHER_ADDR_LEN); 94550974Swpaul 94681713Swpaul /* 94781713Swpaul * Allocate the parent bus DMA tag appropriate for PCI. 94881713Swpaul */ 94981713Swpaul#define SIS_NSEG_NEW 32 95081713Swpaul error = bus_dma_tag_create(NULL, /* parent */ 95181713Swpaul 1, 0, /* alignment, boundary */ 95281713Swpaul BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ 95381713Swpaul BUS_SPACE_MAXADDR, /* highaddr */ 95481713Swpaul NULL, NULL, /* filter, filterarg */ 95581713Swpaul MAXBSIZE, SIS_NSEG_NEW, /* maxsize, nsegments */ 95681713Swpaul BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 95781713Swpaul BUS_DMA_ALLOCNOW, /* flags */ 95881713Swpaul &sc->sis_parent_tag); 95950974Swpaul 96081713Swpaul /* 96181713Swpaul * Now allocate a tag for the DMA descriptor lists. 96281713Swpaul * All of our lists are allocated as a contiguous block 96381713Swpaul * of memory. 96481713Swpaul */ 96581713Swpaul error = bus_dma_tag_create(sc->sis_parent_tag, /* parent */ 96681713Swpaul 1, 0, /* alignment, boundary */ 96781713Swpaul BUS_SPACE_MAXADDR, /* lowaddr */ 96881713Swpaul BUS_SPACE_MAXADDR, /* highaddr */ 96981713Swpaul NULL, NULL, /* filter, filterarg */ 97081713Swpaul SIS_RX_LIST_SZ, 1, /* maxsize,nsegments */ 97181713Swpaul BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 97281713Swpaul 0, /* flags */ 97381713Swpaul &sc->sis_ldata.sis_rx_tag); 97481713Swpaul 97581713Swpaul error = bus_dma_tag_create(sc->sis_parent_tag, /* parent */ 97681713Swpaul 1, 0, /* alignment, boundary */ 97781713Swpaul BUS_SPACE_MAXADDR, /* lowaddr */ 97881713Swpaul BUS_SPACE_MAXADDR, /* highaddr */ 97981713Swpaul NULL, NULL, /* filter, filterarg */ 98081713Swpaul SIS_TX_LIST_SZ, 1, /* maxsize,nsegments */ 98181713Swpaul BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 98281713Swpaul 0, /* flags */ 98381713Swpaul &sc->sis_ldata.sis_tx_tag); 98481713Swpaul 98581713Swpaul error = bus_dma_tag_create(sc->sis_parent_tag, /* parent */ 98681713Swpaul 1, 0, /* alignment, boundary */ 98781713Swpaul BUS_SPACE_MAXADDR, /* lowaddr */ 98881713Swpaul BUS_SPACE_MAXADDR, /* highaddr */ 98981713Swpaul NULL, NULL, /* filter, filterarg */ 99081713Swpaul SIS_TX_LIST_SZ, 1, /* maxsize,nsegments */ 99181713Swpaul BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 99281713Swpaul 0, /* flags */ 99381713Swpaul &sc->sis_tag); 99481713Swpaul 99581713Swpaul /* 99681713Swpaul * Now allocate a chunk of DMA-able memory based on the 99781713Swpaul * tag we just created. 99881713Swpaul */ 99981713Swpaul error = bus_dmamem_alloc(sc->sis_ldata.sis_tx_tag, 100081713Swpaul (void **)&sc->sis_ldata.sis_tx_list, BUS_DMA_NOWAIT, 100181713Swpaul &sc->sis_ldata.sis_tx_dmamap); 100281713Swpaul 100381713Swpaul if (error) { 100450974Swpaul printf("sis%d: no memory for list buffers!\n", unit); 100550974Swpaul bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand); 100650974Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq); 100750974Swpaul bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); 100881713Swpaul bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag); 100981713Swpaul bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag); 101050974Swpaul error = ENXIO; 101150974Swpaul goto fail; 101250974Swpaul } 101350974Swpaul 101481713Swpaul error = bus_dmamem_alloc(sc->sis_ldata.sis_rx_tag, 101581713Swpaul (void **)&sc->sis_ldata.sis_rx_list, BUS_DMA_NOWAIT, 101681713Swpaul &sc->sis_ldata.sis_rx_dmamap); 101781713Swpaul 101881713Swpaul if (error) { 101981713Swpaul printf("sis%d: no memory for list buffers!\n", unit); 102081713Swpaul bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand); 102181713Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq); 102281713Swpaul bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); 102381713Swpaul bus_dmamem_free(sc->sis_ldata.sis_rx_tag, 102481713Swpaul sc->sis_ldata.sis_tx_list, sc->sis_ldata.sis_tx_dmamap); 102581713Swpaul bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag); 102681713Swpaul bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag); 102781713Swpaul error = ENXIO; 102881713Swpaul goto fail; 102981713Swpaul } 103081713Swpaul 103181713Swpaul 103281713Swpaul bzero(sc->sis_ldata.sis_tx_list, SIS_TX_LIST_SZ); 103381713Swpaul bzero(sc->sis_ldata.sis_rx_list, SIS_RX_LIST_SZ); 103481713Swpaul 103581713Swpaul /* 103681713Swpaul * Obtain the physical addresses of the RX and TX 103781713Swpaul * rings which we'll need later in the init routine. 103881713Swpaul */ 103981713Swpaul bus_dmamap_load(sc->sis_ldata.sis_tx_tag, 104081713Swpaul sc->sis_ldata.sis_tx_dmamap, &(sc->sis_ldata.sis_tx_list[0]), 104181713Swpaul sizeof(struct sis_desc), sis_dma_map_ring, 104281713Swpaul &sc->sis_cdata.sis_tx_paddr, 0); 104381713Swpaul bus_dmamap_load(sc->sis_ldata.sis_rx_tag, 104481713Swpaul sc->sis_ldata.sis_rx_dmamap, &(sc->sis_ldata.sis_rx_list[0]), 104581713Swpaul sizeof(struct sis_desc), sis_dma_map_ring, 104681713Swpaul &sc->sis_cdata.sis_rx_paddr, 0); 104781713Swpaul 104850974Swpaul ifp = &sc->arpcom.ac_if; 104950974Swpaul ifp->if_softc = sc; 105050974Swpaul ifp->if_unit = unit; 105150974Swpaul ifp->if_name = "sis"; 105250974Swpaul ifp->if_mtu = ETHERMTU; 105350974Swpaul ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 105450974Swpaul ifp->if_ioctl = sis_ioctl; 105550974Swpaul ifp->if_output = ether_output; 105650974Swpaul ifp->if_start = sis_start; 105750974Swpaul ifp->if_watchdog = sis_watchdog; 105850974Swpaul ifp->if_init = sis_init; 105950974Swpaul ifp->if_baudrate = 10000000; 106050974Swpaul ifp->if_snd.ifq_maxlen = SIS_TX_LIST_CNT - 1; 106150974Swpaul 106250974Swpaul /* 106350974Swpaul * Do MII setup. 106450974Swpaul */ 106550974Swpaul if (mii_phy_probe(dev, &sc->sis_miibus, 106650974Swpaul sis_ifmedia_upd, sis_ifmedia_sts)) { 106750974Swpaul printf("sis%d: MII without any PHY!\n", sc->sis_unit); 106850974Swpaul bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand); 106950974Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq); 107050974Swpaul bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); 107181713Swpaul bus_dmamem_free(sc->sis_ldata.sis_rx_tag, 107281713Swpaul sc->sis_ldata.sis_rx_list, sc->sis_ldata.sis_rx_dmamap); 107381713Swpaul bus_dmamem_free(sc->sis_ldata.sis_rx_tag, 107481713Swpaul sc->sis_ldata.sis_tx_list, sc->sis_ldata.sis_tx_dmamap); 107581713Swpaul bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag); 107681713Swpaul bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag); 107750974Swpaul error = ENXIO; 107850974Swpaul goto fail; 107950974Swpaul } 108050974Swpaul 108150974Swpaul /* 108263090Sarchie * Call MI attach routine. 108350974Swpaul */ 108463090Sarchie ether_ifattach(ifp, ETHER_BPF_SUPPORTED); 108550974Swpaul callout_handle_init(&sc->sis_stat_ch); 108667087Swpaul SIS_UNLOCK(sc); 108767087Swpaul return(0); 108850974Swpaul 108950974Swpaulfail: 109067087Swpaul SIS_UNLOCK(sc); 109167087Swpaul mtx_destroy(&sc->sis_mtx); 109250974Swpaul return(error); 109350974Swpaul} 109450974Swpaul 109550974Swpaulstatic int sis_detach(dev) 109650974Swpaul device_t dev; 109750974Swpaul{ 109850974Swpaul struct sis_softc *sc; 109950974Swpaul struct ifnet *ifp; 110050974Swpaul 110150974Swpaul 110250974Swpaul sc = device_get_softc(dev); 110367087Swpaul SIS_LOCK(sc); 110450974Swpaul ifp = &sc->arpcom.ac_if; 110550974Swpaul 110650974Swpaul sis_reset(sc); 110750974Swpaul sis_stop(sc); 110863090Sarchie ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); 110950974Swpaul 111050974Swpaul bus_generic_detach(dev); 111150974Swpaul device_delete_child(dev, sc->sis_miibus); 111250974Swpaul 111350974Swpaul bus_teardown_intr(dev, sc->sis_irq, sc->sis_intrhand); 111450974Swpaul bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sis_irq); 111550974Swpaul bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res); 111650974Swpaul 111781713Swpaul bus_dmamap_unload(sc->sis_ldata.sis_rx_tag, 111881713Swpaul sc->sis_ldata.sis_rx_dmamap); 111981713Swpaul bus_dmamap_unload(sc->sis_ldata.sis_tx_tag, 112081713Swpaul sc->sis_ldata.sis_tx_dmamap); 112181713Swpaul bus_dmamem_free(sc->sis_ldata.sis_rx_tag, 112281713Swpaul sc->sis_ldata.sis_rx_list, sc->sis_ldata.sis_rx_dmamap); 112381713Swpaul bus_dmamem_free(sc->sis_ldata.sis_rx_tag, 112481713Swpaul sc->sis_ldata.sis_tx_list, sc->sis_ldata.sis_tx_dmamap); 112581713Swpaul bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag); 112681713Swpaul bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag); 112781713Swpaul bus_dma_tag_destroy(sc->sis_parent_tag); 112850974Swpaul 112967087Swpaul SIS_UNLOCK(sc); 113067087Swpaul mtx_destroy(&sc->sis_mtx); 113150974Swpaul 113250974Swpaul return(0); 113350974Swpaul} 113450974Swpaul 113550974Swpaul/* 113650974Swpaul * Initialize the transmit descriptors. 113750974Swpaul */ 113850974Swpaulstatic int sis_list_tx_init(sc) 113950974Swpaul struct sis_softc *sc; 114050974Swpaul{ 114150974Swpaul struct sis_list_data *ld; 114250974Swpaul struct sis_ring_data *cd; 114387059Sluigi int i, nexti; 114450974Swpaul 114550974Swpaul cd = &sc->sis_cdata; 114681713Swpaul ld = &sc->sis_ldata; 114750974Swpaul 114850974Swpaul for (i = 0; i < SIS_TX_LIST_CNT; i++) { 114987059Sluigi nexti = (i == (SIS_TX_LIST_CNT - 1)) ? 0 : i+1 ; 115050974Swpaul ld->sis_tx_list[i].sis_nextdesc = 115187059Sluigi &ld->sis_tx_list[nexti]; 115281713Swpaul bus_dmamap_load(sc->sis_ldata.sis_tx_tag, 115381713Swpaul sc->sis_ldata.sis_tx_dmamap, 115487059Sluigi &ld->sis_tx_list[nexti], sizeof(struct sis_desc), 115581713Swpaul sis_dma_map_desc_next, &ld->sis_tx_list[i], 0); 115650974Swpaul ld->sis_tx_list[i].sis_mbuf = NULL; 115750974Swpaul ld->sis_tx_list[i].sis_ptr = 0; 115850974Swpaul ld->sis_tx_list[i].sis_ctl = 0; 115950974Swpaul } 116050974Swpaul 116150974Swpaul cd->sis_tx_prod = cd->sis_tx_cons = cd->sis_tx_cnt = 0; 116250974Swpaul 116381713Swpaul bus_dmamap_sync(sc->sis_ldata.sis_tx_tag, 116481713Swpaul sc->sis_ldata.sis_rx_dmamap, BUS_DMASYNC_PREWRITE); 116581713Swpaul 116650974Swpaul return(0); 116750974Swpaul} 116850974Swpaul 116950974Swpaul/* 117050974Swpaul * Initialize the RX descriptors and allocate mbufs for them. Note that 117150974Swpaul * we arrange the descriptors in a closed ring, so that the last descriptor 117250974Swpaul * points back to the first. 117350974Swpaul */ 117450974Swpaulstatic int sis_list_rx_init(sc) 117550974Swpaul struct sis_softc *sc; 117650974Swpaul{ 117750974Swpaul struct sis_list_data *ld; 117850974Swpaul struct sis_ring_data *cd; 117987059Sluigi int i,nexti; 118050974Swpaul 118181713Swpaul ld = &sc->sis_ldata; 118250974Swpaul cd = &sc->sis_cdata; 118350974Swpaul 118450974Swpaul for (i = 0; i < SIS_RX_LIST_CNT; i++) { 118550974Swpaul if (sis_newbuf(sc, &ld->sis_rx_list[i], NULL) == ENOBUFS) 118650974Swpaul return(ENOBUFS); 118787059Sluigi nexti = (i == (SIS_RX_LIST_CNT - 1)) ? 0 : i+1 ; 118850974Swpaul ld->sis_rx_list[i].sis_nextdesc = 118987059Sluigi &ld->sis_rx_list[nexti]; 119081713Swpaul bus_dmamap_load(sc->sis_ldata.sis_rx_tag, 119181713Swpaul sc->sis_ldata.sis_rx_dmamap, 119287059Sluigi &ld->sis_rx_list[nexti], 119381713Swpaul sizeof(struct sis_desc), sis_dma_map_desc_next, 119481713Swpaul &ld->sis_rx_list[i], 0); 119550974Swpaul } 119650974Swpaul 119781713Swpaul bus_dmamap_sync(sc->sis_ldata.sis_rx_tag, 119881713Swpaul sc->sis_ldata.sis_rx_dmamap, BUS_DMASYNC_PREWRITE); 119981713Swpaul 120050974Swpaul cd->sis_rx_prod = 0; 120150974Swpaul 120250974Swpaul return(0); 120350974Swpaul} 120450974Swpaul 120550974Swpaul/* 120650974Swpaul * Initialize an RX descriptor and attach an MBUF cluster. 120750974Swpaul */ 120850974Swpaulstatic int sis_newbuf(sc, c, m) 120950974Swpaul struct sis_softc *sc; 121050974Swpaul struct sis_desc *c; 121150974Swpaul struct mbuf *m; 121250974Swpaul{ 121350974Swpaul struct mbuf *m_new = NULL; 121450974Swpaul 121581713Swpaul if (c == NULL) 121681713Swpaul return(EINVAL); 121781713Swpaul 121850974Swpaul if (m == NULL) { 121950974Swpaul MGETHDR(m_new, M_DONTWAIT, MT_DATA); 122087059Sluigi if (m_new == NULL) 122150974Swpaul return(ENOBUFS); 122250974Swpaul 122350974Swpaul MCLGET(m_new, M_DONTWAIT); 122450974Swpaul if (!(m_new->m_flags & M_EXT)) { 122550974Swpaul m_freem(m_new); 122650974Swpaul return(ENOBUFS); 122750974Swpaul } 122850974Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 122950974Swpaul } else { 123050974Swpaul m_new = m; 123150974Swpaul m_new->m_len = m_new->m_pkthdr.len = MCLBYTES; 123250974Swpaul m_new->m_data = m_new->m_ext.ext_buf; 123350974Swpaul } 123450974Swpaul 123550974Swpaul m_adj(m_new, sizeof(u_int64_t)); 123650974Swpaul 123750974Swpaul c->sis_mbuf = m_new; 123850974Swpaul c->sis_ctl = SIS_RXLEN; 123950974Swpaul 124081713Swpaul bus_dmamap_create(sc->sis_tag, 0, &c->sis_map); 124181713Swpaul bus_dmamap_load(sc->sis_tag, c->sis_map, 124281713Swpaul mtod(m_new, void *), m_new->m_len, 124381713Swpaul sis_dma_map_desc_ptr, c, 0); 124481713Swpaul bus_dmamap_sync(sc->sis_tag, c->sis_map, BUS_DMASYNC_PREWRITE); 124581713Swpaul 124650974Swpaul return(0); 124750974Swpaul} 124850974Swpaul 124950974Swpaul/* 125050974Swpaul * A frame has been uploaded: pass the resulting mbuf chain up to 125150974Swpaul * the higher level protocols. 125250974Swpaul */ 125350974Swpaulstatic void sis_rxeof(sc) 125450974Swpaul struct sis_softc *sc; 125550974Swpaul{ 125650974Swpaul struct ether_header *eh; 125750974Swpaul struct mbuf *m; 125850974Swpaul struct ifnet *ifp; 125950974Swpaul struct sis_desc *cur_rx; 126050974Swpaul int i, total_len = 0; 126150974Swpaul u_int32_t rxstat; 126250974Swpaul 126350974Swpaul ifp = &sc->arpcom.ac_if; 126450974Swpaul i = sc->sis_cdata.sis_rx_prod; 126550974Swpaul 126681713Swpaul while(SIS_OWNDESC(&sc->sis_ldata.sis_rx_list[i])) { 126750974Swpaul 126881713Swpaul cur_rx = &sc->sis_ldata.sis_rx_list[i]; 126950974Swpaul rxstat = cur_rx->sis_rxstat; 127081713Swpaul bus_dmamap_sync(sc->sis_tag, 127181713Swpaul cur_rx->sis_map, BUS_DMASYNC_POSTWRITE); 127281713Swpaul bus_dmamap_unload(sc->sis_tag, cur_rx->sis_map); 127381713Swpaul bus_dmamap_destroy(sc->sis_tag, cur_rx->sis_map); 127450974Swpaul m = cur_rx->sis_mbuf; 127550974Swpaul cur_rx->sis_mbuf = NULL; 127650974Swpaul total_len = SIS_RXBYTES(cur_rx); 127750974Swpaul SIS_INC(i, SIS_RX_LIST_CNT); 127850974Swpaul 127950974Swpaul /* 128050974Swpaul * If an error occurs, update stats, clear the 128150974Swpaul * status word and leave the mbuf cluster in place: 128250974Swpaul * it should simply get re-used next time this descriptor 128350974Swpaul * comes up in the ring. 128450974Swpaul */ 128550974Swpaul if (!(rxstat & SIS_CMDSTS_PKT_OK)) { 128650974Swpaul ifp->if_ierrors++; 128750974Swpaul if (rxstat & SIS_RXSTAT_COLL) 128850974Swpaul ifp->if_collisions++; 128950974Swpaul sis_newbuf(sc, cur_rx, m); 129050974Swpaul continue; 129150974Swpaul } 129250974Swpaul 129350974Swpaul /* No errors; receive the packet. */ 129487059Sluigi#ifdef __i386__ 129587059Sluigi /* 129687059Sluigi * On the x86 we do not have alignment problems, so try to 129787059Sluigi * allocate a new buffer for the receive ring, and pass up 129887059Sluigi * the one where the packet is already, saving the expensive 129987059Sluigi * copy done in m_devget(). 130087059Sluigi * If we are on an architecture with alignment problems, or 130187059Sluigi * if the allocation fails, then use m_devget and leave the 130287059Sluigi * existing buffer in the receive ring. 130387059Sluigi */ 130487059Sluigi if (sis_quick && sis_newbuf(sc, cur_rx, NULL) == 0) { 130587059Sluigi m->m_pkthdr.rcvif = ifp; 130687059Sluigi m->m_pkthdr.len = m->m_len = total_len; 130787059Sluigi } else 130887059Sluigi#endif 130987059Sluigi { 131087059Sluigi struct mbuf *m0; 131187059Sluigi m0 = m_devget(mtod(m, char *), total_len, 131287059Sluigi ETHER_ALIGN, ifp, NULL); 131387059Sluigi sis_newbuf(sc, cur_rx, m); 131487059Sluigi if (m0 == NULL) { 131587059Sluigi ifp->if_ierrors++; 131687059Sluigi continue; 131787059Sluigi } 131887059Sluigi m = m0; 131950974Swpaul } 132050974Swpaul 132150974Swpaul ifp->if_ipackets++; 132250974Swpaul eh = mtod(m, struct ether_header *); 132351583Swpaul 132450974Swpaul /* Remove header from mbuf and pass it on. */ 132550974Swpaul m_adj(m, sizeof(struct ether_header)); 132650974Swpaul ether_input(ifp, eh, m); 132750974Swpaul } 132850974Swpaul 132950974Swpaul sc->sis_cdata.sis_rx_prod = i; 133050974Swpaul 133150974Swpaul return; 133250974Swpaul} 133350974Swpaul 133450974Swpaulvoid sis_rxeoc(sc) 133550974Swpaul struct sis_softc *sc; 133650974Swpaul{ 133750974Swpaul sis_rxeof(sc); 133850974Swpaul sis_init(sc); 133950974Swpaul return; 134050974Swpaul} 134150974Swpaul 134250974Swpaul/* 134350974Swpaul * A frame was downloaded to the chip. It's safe for us to clean up 134450974Swpaul * the list buffers. 134550974Swpaul */ 134650974Swpaul 134750974Swpaulstatic void sis_txeof(sc) 134850974Swpaul struct sis_softc *sc; 134950974Swpaul{ 135050974Swpaul struct sis_desc *cur_tx = NULL; 135150974Swpaul struct ifnet *ifp; 135250974Swpaul u_int32_t idx; 135350974Swpaul 135450974Swpaul ifp = &sc->arpcom.ac_if; 135550974Swpaul 135650974Swpaul /* Clear the timeout timer. */ 135750974Swpaul ifp->if_timer = 0; 135850974Swpaul 135950974Swpaul /* 136050974Swpaul * Go through our tx list and free mbufs for those 136150974Swpaul * frames that have been transmitted. 136250974Swpaul */ 136350974Swpaul idx = sc->sis_cdata.sis_tx_cons; 136450974Swpaul while (idx != sc->sis_cdata.sis_tx_prod) { 136581713Swpaul cur_tx = &sc->sis_ldata.sis_tx_list[idx]; 136650974Swpaul 136750974Swpaul if (SIS_OWNDESC(cur_tx)) 136850974Swpaul break; 136950974Swpaul 137050974Swpaul if (cur_tx->sis_ctl & SIS_CMDSTS_MORE) { 137150974Swpaul sc->sis_cdata.sis_tx_cnt--; 137250974Swpaul SIS_INC(idx, SIS_TX_LIST_CNT); 137350974Swpaul continue; 137450974Swpaul } 137550974Swpaul 137650974Swpaul if (!(cur_tx->sis_ctl & SIS_CMDSTS_PKT_OK)) { 137750974Swpaul ifp->if_oerrors++; 137850974Swpaul if (cur_tx->sis_txstat & SIS_TXSTAT_EXCESSCOLLS) 137950974Swpaul ifp->if_collisions++; 138050974Swpaul if (cur_tx->sis_txstat & SIS_TXSTAT_OUTOFWINCOLL) 138150974Swpaul ifp->if_collisions++; 138250974Swpaul } 138350974Swpaul 138450974Swpaul ifp->if_collisions += 138550974Swpaul (cur_tx->sis_txstat & SIS_TXSTAT_COLLCNT) >> 16; 138650974Swpaul 138750974Swpaul ifp->if_opackets++; 138850974Swpaul if (cur_tx->sis_mbuf != NULL) { 138950974Swpaul m_freem(cur_tx->sis_mbuf); 139050974Swpaul cur_tx->sis_mbuf = NULL; 139181713Swpaul bus_dmamap_unload(sc->sis_tag, cur_tx->sis_map); 139281713Swpaul bus_dmamap_destroy(sc->sis_tag, cur_tx->sis_map); 139350974Swpaul } 139450974Swpaul 139550974Swpaul sc->sis_cdata.sis_tx_cnt--; 139650974Swpaul SIS_INC(idx, SIS_TX_LIST_CNT); 139750974Swpaul ifp->if_timer = 0; 139850974Swpaul } 139950974Swpaul 140050974Swpaul sc->sis_cdata.sis_tx_cons = idx; 140150974Swpaul 140250974Swpaul if (cur_tx != NULL) 140350974Swpaul ifp->if_flags &= ~IFF_OACTIVE; 140450974Swpaul 140550974Swpaul return; 140650974Swpaul} 140750974Swpaul 140850974Swpaulstatic void sis_tick(xsc) 140950974Swpaul void *xsc; 141050974Swpaul{ 141150974Swpaul struct sis_softc *sc; 141250974Swpaul struct mii_data *mii; 141364963Swpaul struct ifnet *ifp; 141450974Swpaul 141550974Swpaul sc = xsc; 141667087Swpaul SIS_LOCK(sc); 141764963Swpaul ifp = &sc->arpcom.ac_if; 141864963Swpaul 141950974Swpaul mii = device_get_softc(sc->sis_miibus); 142050974Swpaul mii_tick(mii); 142164963Swpaul 142284147Sjlemon if (!sc->sis_link && mii->mii_media_status & IFM_ACTIVE && 142384147Sjlemon IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) { 142484147Sjlemon sc->sis_link++; 142584147Sjlemon if (ifp->if_snd.ifq_head != NULL) 142684147Sjlemon sis_start(ifp); 142764963Swpaul } 142864963Swpaul 142951031Swpaul sc->sis_stat_ch = timeout(sis_tick, sc, hz); 143050974Swpaul 143167087Swpaul SIS_UNLOCK(sc); 143250974Swpaul 143350974Swpaul return; 143450974Swpaul} 143550974Swpaul 143650974Swpaulstatic void sis_intr(arg) 143750974Swpaul void *arg; 143850974Swpaul{ 143950974Swpaul struct sis_softc *sc; 144050974Swpaul struct ifnet *ifp; 144150974Swpaul u_int32_t status; 144250974Swpaul 144350974Swpaul sc = arg; 144450974Swpaul ifp = &sc->arpcom.ac_if; 144550974Swpaul 144686984Sluigi SIS_LOCK(sc); 144750974Swpaul /* Supress unwanted interrupts */ 144850974Swpaul if (!(ifp->if_flags & IFF_UP)) { 144950974Swpaul sis_stop(sc); 145086984Sluigi goto done; 145150974Swpaul } 145250974Swpaul 145350974Swpaul /* Disable interrupts. */ 145450974Swpaul CSR_WRITE_4(sc, SIS_IER, 0); 145550974Swpaul 145650974Swpaul for (;;) { 145750974Swpaul /* Reading the ISR register clears all interrupts. */ 145850974Swpaul status = CSR_READ_4(sc, SIS_ISR); 145950974Swpaul 146050974Swpaul if ((status & SIS_INTRS) == 0) 146150974Swpaul break; 146250974Swpaul 146386984Sluigi if (status & 146486984Sluigi (SIS_ISR_TX_DESC_OK | SIS_ISR_TX_ERR | 146586984Sluigi SIS_ISR_TX_OK | SIS_ISR_TX_IDLE) ) 146650974Swpaul sis_txeof(sc); 146750974Swpaul 146886984Sluigi if (status & (SIS_ISR_RX_DESC_OK|SIS_ISR_RX_OK|SIS_ISR_RX_IDLE)) 146950974Swpaul sis_rxeof(sc); 147050974Swpaul 147186984Sluigi if (status & (SIS_ISR_RX_ERR | SIS_ISR_RX_OFLOW)) 147250974Swpaul sis_rxeoc(sc); 147350974Swpaul 147486984Sluigi if (status & (SIS_ISR_RX_IDLE)) 147586984Sluigi SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); 147686984Sluigi 147750974Swpaul if (status & SIS_ISR_SYSERR) { 147850974Swpaul sis_reset(sc); 147950974Swpaul sis_init(sc); 148050974Swpaul } 148150974Swpaul } 148250974Swpaul 148350974Swpaul /* Re-enable interrupts. */ 148450974Swpaul CSR_WRITE_4(sc, SIS_IER, 1); 148550974Swpaul 148650974Swpaul if (ifp->if_snd.ifq_head != NULL) 148750974Swpaul sis_start(ifp); 148886984Sluigidone: 148967087Swpaul SIS_UNLOCK(sc); 149067087Swpaul 149150974Swpaul return; 149250974Swpaul} 149350974Swpaul 149450974Swpaul/* 149550974Swpaul * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data 149650974Swpaul * pointers to the fragment pointers. 149750974Swpaul */ 149850974Swpaulstatic int sis_encap(sc, m_head, txidx) 149950974Swpaul struct sis_softc *sc; 150050974Swpaul struct mbuf *m_head; 150150974Swpaul u_int32_t *txidx; 150250974Swpaul{ 150350974Swpaul struct sis_desc *f = NULL; 150450974Swpaul struct mbuf *m; 150550974Swpaul int frag, cur, cnt = 0; 150650974Swpaul 150750974Swpaul /* 150850974Swpaul * Start packing the mbufs in this chain into 150950974Swpaul * the fragment pointers. Stop when we run out 151050974Swpaul * of fragments or hit the end of the mbuf chain. 151150974Swpaul */ 151250974Swpaul m = m_head; 151350974Swpaul cur = frag = *txidx; 151450974Swpaul 151550974Swpaul for (m = m_head; m != NULL; m = m->m_next) { 151650974Swpaul if (m->m_len != 0) { 151751042Swpaul if ((SIS_TX_LIST_CNT - 151850974Swpaul (sc->sis_cdata.sis_tx_cnt + cnt)) < 2) 151950974Swpaul return(ENOBUFS); 152081713Swpaul f = &sc->sis_ldata.sis_tx_list[frag]; 152150974Swpaul f->sis_ctl = SIS_CMDSTS_MORE | m->m_len; 152281713Swpaul bus_dmamap_create(sc->sis_tag, 0, &f->sis_map); 152381713Swpaul bus_dmamap_load(sc->sis_tag, f->sis_map, 152481713Swpaul mtod(m, void *), m->m_len, 152581713Swpaul sis_dma_map_desc_ptr, f, 0); 152681713Swpaul bus_dmamap_sync(sc->sis_tag, 152781713Swpaul f->sis_map, BUS_DMASYNC_PREREAD); 152850974Swpaul if (cnt != 0) 152950974Swpaul f->sis_ctl |= SIS_CMDSTS_OWN; 153050974Swpaul cur = frag; 153150974Swpaul SIS_INC(frag, SIS_TX_LIST_CNT); 153250974Swpaul cnt++; 153350974Swpaul } 153450974Swpaul } 153550974Swpaul 153650974Swpaul if (m != NULL) 153750974Swpaul return(ENOBUFS); 153850974Swpaul 153981713Swpaul sc->sis_ldata.sis_tx_list[cur].sis_mbuf = m_head; 154081713Swpaul sc->sis_ldata.sis_tx_list[cur].sis_ctl &= ~SIS_CMDSTS_MORE; 154181713Swpaul sc->sis_ldata.sis_tx_list[*txidx].sis_ctl |= SIS_CMDSTS_OWN; 154250974Swpaul sc->sis_cdata.sis_tx_cnt += cnt; 154350974Swpaul *txidx = frag; 154450974Swpaul 154550974Swpaul return(0); 154650974Swpaul} 154750974Swpaul 154850974Swpaul/* 154950974Swpaul * Main transmit routine. To avoid having to do mbuf copies, we put pointers 155050974Swpaul * to the mbuf data regions directly in the transmit lists. We also save a 155150974Swpaul * copy of the pointers since the transmit list fragment pointers are 155250974Swpaul * physical addresses. 155350974Swpaul */ 155450974Swpaul 155550974Swpaulstatic void sis_start(ifp) 155650974Swpaul struct ifnet *ifp; 155750974Swpaul{ 155850974Swpaul struct sis_softc *sc; 155950974Swpaul struct mbuf *m_head = NULL; 156050974Swpaul u_int32_t idx; 156150974Swpaul 156250974Swpaul sc = ifp->if_softc; 156367087Swpaul SIS_LOCK(sc); 156450974Swpaul 156567087Swpaul if (!sc->sis_link) { 156667087Swpaul SIS_UNLOCK(sc); 156764963Swpaul return; 156867087Swpaul } 156964963Swpaul 157050974Swpaul idx = sc->sis_cdata.sis_tx_prod; 157150974Swpaul 157267087Swpaul if (ifp->if_flags & IFF_OACTIVE) { 157367087Swpaul SIS_UNLOCK(sc); 157450974Swpaul return; 157567087Swpaul } 157650974Swpaul 157781713Swpaul while(sc->sis_ldata.sis_tx_list[idx].sis_mbuf == NULL) { 157850974Swpaul IF_DEQUEUE(&ifp->if_snd, m_head); 157950974Swpaul if (m_head == NULL) 158050974Swpaul break; 158150974Swpaul 158250974Swpaul if (sis_encap(sc, m_head, &idx)) { 158350974Swpaul IF_PREPEND(&ifp->if_snd, m_head); 158450974Swpaul ifp->if_flags |= IFF_OACTIVE; 158550974Swpaul break; 158650974Swpaul } 158750974Swpaul 158850974Swpaul /* 158950974Swpaul * If there's a BPF listener, bounce a copy of this frame 159050974Swpaul * to him. 159150974Swpaul */ 159250974Swpaul if (ifp->if_bpf) 159350974Swpaul bpf_mtap(ifp, m_head); 159451583Swpaul 159550974Swpaul } 159650974Swpaul 159750974Swpaul /* Transmit */ 159850974Swpaul sc->sis_cdata.sis_tx_prod = idx; 159950974Swpaul SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_ENABLE); 160050974Swpaul 160150974Swpaul /* 160250974Swpaul * Set a timeout in case the chip goes out to lunch. 160350974Swpaul */ 160450974Swpaul ifp->if_timer = 5; 160550974Swpaul 160667087Swpaul SIS_UNLOCK(sc); 160767087Swpaul 160850974Swpaul return; 160950974Swpaul} 161050974Swpaul 161150974Swpaulstatic void sis_init(xsc) 161250974Swpaul void *xsc; 161350974Swpaul{ 161450974Swpaul struct sis_softc *sc = xsc; 161550974Swpaul struct ifnet *ifp = &sc->arpcom.ac_if; 161650974Swpaul struct mii_data *mii; 161750974Swpaul 161867087Swpaul SIS_LOCK(sc); 161950974Swpaul 162050974Swpaul /* 162150974Swpaul * Cancel pending I/O and free all RX/TX buffers. 162250974Swpaul */ 162350974Swpaul sis_stop(sc); 162450974Swpaul 162550974Swpaul mii = device_get_softc(sc->sis_miibus); 162650974Swpaul 162750974Swpaul /* Set MAC address */ 162862672Swpaul if (sc->sis_type == SIS_TYPE_83815) { 162962672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR0); 163062672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 163162672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); 163262672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR1); 163362672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 163462672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); 163562672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, NS_FILTADDR_PAR2); 163662672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 163762672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); 163862672Swpaul } else { 163962672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR0); 164062672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 164162672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[0]); 164262672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR1); 164362672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 164462672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[1]); 164562672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_CTL, SIS_FILTADDR_PAR2); 164662672Swpaul CSR_WRITE_4(sc, SIS_RXFILT_DATA, 164762672Swpaul ((u_int16_t *)sc->arpcom.ac_enaddr)[2]); 164862672Swpaul } 164950974Swpaul 165050974Swpaul /* Init circular RX list. */ 165150974Swpaul if (sis_list_rx_init(sc) == ENOBUFS) { 165250974Swpaul printf("sis%d: initialization failed: no " 165350974Swpaul "memory for rx buffers\n", sc->sis_unit); 165450974Swpaul sis_stop(sc); 165567087Swpaul SIS_UNLOCK(sc); 165650974Swpaul return; 165750974Swpaul } 165850974Swpaul 165950974Swpaul /* 166050974Swpaul * Init tx descriptors. 166150974Swpaul */ 166250974Swpaul sis_list_tx_init(sc); 166350974Swpaul 166462672Swpaul /* 166562672Swpaul * For the NatSemi chip, we have to explicitly enable the 166662672Swpaul * reception of ARP frames, as well as turn on the 'perfect 166762672Swpaul * match' filter where we store the station address, otherwise 166862672Swpaul * we won't receive unicasts meant for this host. 166962672Swpaul */ 167062672Swpaul if (sc->sis_type == SIS_TYPE_83815) { 167162672Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_ARP); 167262672Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, NS_RXFILTCTL_PERFECT); 167362672Swpaul } 167462672Swpaul 167550974Swpaul /* If we want promiscuous mode, set the allframes bit. */ 167650974Swpaul if (ifp->if_flags & IFF_PROMISC) { 167750974Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS); 167850974Swpaul } else { 167950974Swpaul SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ALLPHYS); 168050974Swpaul } 168150974Swpaul 168250974Swpaul /* 168350974Swpaul * Set the capture broadcast bit to capture broadcast frames. 168450974Swpaul */ 168550974Swpaul if (ifp->if_flags & IFF_BROADCAST) { 168650974Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD); 168750974Swpaul } else { 168850974Swpaul SIS_CLRBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_BROAD); 168950974Swpaul } 169050974Swpaul 169150974Swpaul /* 169250974Swpaul * Load the multicast filter. 169350974Swpaul */ 169462672Swpaul if (sc->sis_type == SIS_TYPE_83815) 169562672Swpaul sis_setmulti_ns(sc); 169662672Swpaul else 169762672Swpaul sis_setmulti_sis(sc); 169850974Swpaul 169950974Swpaul /* Turn the receive filter on */ 170050974Swpaul SIS_SETBIT(sc, SIS_RXFILT_CTL, SIS_RXFILTCTL_ENABLE); 170150974Swpaul 170250974Swpaul /* 170350974Swpaul * Load the address of the RX and TX lists. 170450974Swpaul */ 170581713Swpaul CSR_WRITE_4(sc, SIS_RX_LISTPTR, sc->sis_cdata.sis_rx_paddr); 170681713Swpaul CSR_WRITE_4(sc, SIS_TX_LISTPTR, sc->sis_cdata.sis_tx_paddr); 170750974Swpaul 170850974Swpaul /* Set RX configuration */ 170950974Swpaul CSR_WRITE_4(sc, SIS_RX_CFG, SIS_RXCFG); 171064963Swpaul 171150974Swpaul /* Set TX configuration */ 171264963Swpaul if (IFM_SUBTYPE(mii->mii_media_active) == IFM_10_T) { 171364963Swpaul CSR_WRITE_4(sc, SIS_TX_CFG, SIS_TXCFG_10); 171464963Swpaul } else { 171564963Swpaul CSR_WRITE_4(sc, SIS_TX_CFG, SIS_TXCFG_100); 171664963Swpaul } 171750974Swpaul 171864963Swpaul /* Set full/half duplex mode. */ 171964963Swpaul if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 172064963Swpaul SIS_SETBIT(sc, SIS_TX_CFG, 172164963Swpaul (SIS_TXCFG_IGN_HBEAT|SIS_TXCFG_IGN_CARR)); 172264963Swpaul SIS_SETBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_TXPKTS); 172364963Swpaul } else { 172464963Swpaul SIS_CLRBIT(sc, SIS_TX_CFG, 172564963Swpaul (SIS_TXCFG_IGN_HBEAT|SIS_TXCFG_IGN_CARR)); 172664963Swpaul SIS_CLRBIT(sc, SIS_RX_CFG, SIS_RXCFG_RX_TXPKTS); 172764963Swpaul } 172864963Swpaul 172950974Swpaul /* 173050974Swpaul * Enable interrupts. 173150974Swpaul */ 173250974Swpaul CSR_WRITE_4(sc, SIS_IMR, SIS_INTRS); 173350974Swpaul CSR_WRITE_4(sc, SIS_IER, 1); 173450974Swpaul 173550974Swpaul /* Enable receiver and transmitter. */ 173650974Swpaul SIS_CLRBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE); 173750974Swpaul SIS_SETBIT(sc, SIS_CSR, SIS_CSR_RX_ENABLE); 173850974Swpaul 173964963Swpaul#ifdef notdef 174050974Swpaul mii_mediachg(mii); 174164963Swpaul#endif 174250974Swpaul 174364963Swpaul /* 174464963Swpaul * Page 75 of the DP83815 manual recommends the 174564963Swpaul * following register settings "for optimum 174664963Swpaul * performance." Note however that at least three 174764963Swpaul * of the registers are listed as "reserved" in 174864963Swpaul * the register map, so who knows what they do. 174964963Swpaul */ 175064963Swpaul if (sc->sis_type == SIS_TYPE_83815) { 175164963Swpaul CSR_WRITE_4(sc, NS_PHY_PAGE, 0x0001); 175264963Swpaul CSR_WRITE_4(sc, NS_PHY_CR, 0x189C); 175364963Swpaul CSR_WRITE_4(sc, NS_PHY_TDATA, 0x0000); 175464963Swpaul CSR_WRITE_4(sc, NS_PHY_DSPCFG, 0x5040); 175564963Swpaul CSR_WRITE_4(sc, NS_PHY_SDCFG, 0x008C); 175664963Swpaul } 175764963Swpaul 175850974Swpaul ifp->if_flags |= IFF_RUNNING; 175950974Swpaul ifp->if_flags &= ~IFF_OACTIVE; 176050974Swpaul 176150974Swpaul sc->sis_stat_ch = timeout(sis_tick, sc, hz); 176250974Swpaul 176367087Swpaul SIS_UNLOCK(sc); 176467087Swpaul 176550974Swpaul return; 176650974Swpaul} 176750974Swpaul 176850974Swpaul/* 176950974Swpaul * Set media options. 177050974Swpaul */ 177150974Swpaulstatic int sis_ifmedia_upd(ifp) 177250974Swpaul struct ifnet *ifp; 177350974Swpaul{ 177450974Swpaul struct sis_softc *sc; 177564963Swpaul struct mii_data *mii; 177650974Swpaul 177750974Swpaul sc = ifp->if_softc; 177850974Swpaul 177964963Swpaul mii = device_get_softc(sc->sis_miibus); 178064963Swpaul sc->sis_link = 0; 178164963Swpaul if (mii->mii_instance) { 178264963Swpaul struct mii_softc *miisc; 178372012Sphk LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 178464963Swpaul mii_phy_reset(miisc); 178564963Swpaul } 178664963Swpaul mii_mediachg(mii); 178750974Swpaul 178850974Swpaul return(0); 178950974Swpaul} 179050974Swpaul 179150974Swpaul/* 179250974Swpaul * Report current media status. 179350974Swpaul */ 179450974Swpaulstatic void sis_ifmedia_sts(ifp, ifmr) 179550974Swpaul struct ifnet *ifp; 179650974Swpaul struct ifmediareq *ifmr; 179750974Swpaul{ 179850974Swpaul struct sis_softc *sc; 179950974Swpaul struct mii_data *mii; 180050974Swpaul 180150974Swpaul sc = ifp->if_softc; 180250974Swpaul 180350974Swpaul mii = device_get_softc(sc->sis_miibus); 180450974Swpaul mii_pollstat(mii); 180550974Swpaul ifmr->ifm_active = mii->mii_media_active; 180650974Swpaul ifmr->ifm_status = mii->mii_media_status; 180750974Swpaul 180850974Swpaul return; 180950974Swpaul} 181050974Swpaul 181150974Swpaulstatic int sis_ioctl(ifp, command, data) 181250974Swpaul struct ifnet *ifp; 181350974Swpaul u_long command; 181450974Swpaul caddr_t data; 181550974Swpaul{ 181650974Swpaul struct sis_softc *sc = ifp->if_softc; 181750974Swpaul struct ifreq *ifr = (struct ifreq *) data; 181850974Swpaul struct mii_data *mii; 181967087Swpaul int error = 0; 182050974Swpaul 182150974Swpaul switch(command) { 182250974Swpaul case SIOCSIFADDR: 182350974Swpaul case SIOCGIFADDR: 182450974Swpaul case SIOCSIFMTU: 182550974Swpaul error = ether_ioctl(ifp, command, data); 182650974Swpaul break; 182750974Swpaul case SIOCSIFFLAGS: 182850974Swpaul if (ifp->if_flags & IFF_UP) { 182950974Swpaul sis_init(sc); 183050974Swpaul } else { 183150974Swpaul if (ifp->if_flags & IFF_RUNNING) 183250974Swpaul sis_stop(sc); 183350974Swpaul } 183450974Swpaul error = 0; 183550974Swpaul break; 183650974Swpaul case SIOCADDMULTI: 183750974Swpaul case SIOCDELMULTI: 183881713Swpaul SIS_LOCK(sc); 183962672Swpaul if (sc->sis_type == SIS_TYPE_83815) 184062672Swpaul sis_setmulti_ns(sc); 184162672Swpaul else 184262672Swpaul sis_setmulti_sis(sc); 184381713Swpaul SIS_UNLOCK(sc); 184450974Swpaul error = 0; 184550974Swpaul break; 184650974Swpaul case SIOCGIFMEDIA: 184750974Swpaul case SIOCSIFMEDIA: 184850974Swpaul mii = device_get_softc(sc->sis_miibus); 184981713Swpaul SIS_LOCK(sc); 185050974Swpaul error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command); 185181713Swpaul SIS_UNLOCK(sc); 185250974Swpaul break; 185350974Swpaul default: 185450974Swpaul error = EINVAL; 185550974Swpaul break; 185650974Swpaul } 185750974Swpaul 185850974Swpaul return(error); 185950974Swpaul} 186050974Swpaul 186150974Swpaulstatic void sis_watchdog(ifp) 186250974Swpaul struct ifnet *ifp; 186350974Swpaul{ 186450974Swpaul struct sis_softc *sc; 186550974Swpaul 186650974Swpaul sc = ifp->if_softc; 186750974Swpaul 186867087Swpaul SIS_LOCK(sc); 186967087Swpaul 187050974Swpaul ifp->if_oerrors++; 187150974Swpaul printf("sis%d: watchdog timeout\n", sc->sis_unit); 187250974Swpaul 187350974Swpaul sis_stop(sc); 187450974Swpaul sis_reset(sc); 187550974Swpaul sis_init(sc); 187650974Swpaul 187750974Swpaul if (ifp->if_snd.ifq_head != NULL) 187850974Swpaul sis_start(ifp); 187950974Swpaul 188067087Swpaul SIS_UNLOCK(sc); 188167087Swpaul 188250974Swpaul return; 188350974Swpaul} 188450974Swpaul 188550974Swpaul/* 188650974Swpaul * Stop the adapter and free any mbufs allocated to the 188750974Swpaul * RX and TX lists. 188850974Swpaul */ 188950974Swpaulstatic void sis_stop(sc) 189050974Swpaul struct sis_softc *sc; 189150974Swpaul{ 189250974Swpaul register int i; 189350974Swpaul struct ifnet *ifp; 189450974Swpaul 189567087Swpaul SIS_LOCK(sc); 189650974Swpaul ifp = &sc->arpcom.ac_if; 189750974Swpaul ifp->if_timer = 0; 189850974Swpaul 189950974Swpaul untimeout(sis_tick, sc, sc->sis_stat_ch); 190050974Swpaul CSR_WRITE_4(sc, SIS_IER, 0); 190150974Swpaul CSR_WRITE_4(sc, SIS_IMR, 0); 190250974Swpaul SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE); 190350974Swpaul DELAY(1000); 190450974Swpaul CSR_WRITE_4(sc, SIS_TX_LISTPTR, 0); 190550974Swpaul CSR_WRITE_4(sc, SIS_RX_LISTPTR, 0); 190650974Swpaul 190764963Swpaul sc->sis_link = 0; 190864963Swpaul 190950974Swpaul /* 191050974Swpaul * Free data in the RX lists. 191150974Swpaul */ 191250974Swpaul for (i = 0; i < SIS_RX_LIST_CNT; i++) { 191381713Swpaul if (sc->sis_ldata.sis_rx_list[i].sis_mbuf != NULL) { 191481713Swpaul bus_dmamap_unload(sc->sis_tag, 191581713Swpaul sc->sis_ldata.sis_rx_list[i].sis_map); 191681713Swpaul bus_dmamap_destroy(sc->sis_tag, 191781713Swpaul sc->sis_ldata.sis_rx_list[i].sis_map); 191881713Swpaul m_freem(sc->sis_ldata.sis_rx_list[i].sis_mbuf); 191981713Swpaul sc->sis_ldata.sis_rx_list[i].sis_mbuf = NULL; 192050974Swpaul } 192150974Swpaul } 192281713Swpaul bzero(sc->sis_ldata.sis_rx_list, 192381713Swpaul sizeof(sc->sis_ldata.sis_rx_list)); 192450974Swpaul 192550974Swpaul /* 192650974Swpaul * Free the TX list buffers. 192750974Swpaul */ 192850974Swpaul for (i = 0; i < SIS_TX_LIST_CNT; i++) { 192981713Swpaul if (sc->sis_ldata.sis_tx_list[i].sis_mbuf != NULL) { 193081713Swpaul bus_dmamap_unload(sc->sis_tag, 193181713Swpaul sc->sis_ldata.sis_tx_list[i].sis_map); 193281713Swpaul bus_dmamap_destroy(sc->sis_tag, 193381713Swpaul sc->sis_ldata.sis_tx_list[i].sis_map); 193481713Swpaul m_freem(sc->sis_ldata.sis_tx_list[i].sis_mbuf); 193581713Swpaul sc->sis_ldata.sis_tx_list[i].sis_mbuf = NULL; 193650974Swpaul } 193750974Swpaul } 193850974Swpaul 193981713Swpaul bzero(sc->sis_ldata.sis_tx_list, 194081713Swpaul sizeof(sc->sis_ldata.sis_tx_list)); 194150974Swpaul 194250974Swpaul ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); 194350974Swpaul 194467087Swpaul SIS_UNLOCK(sc); 194567087Swpaul 194650974Swpaul return; 194750974Swpaul} 194850974Swpaul 194950974Swpaul/* 195050974Swpaul * Stop all chip I/O so that the kernel's probe routines don't 195150974Swpaul * get confused by errant DMAs when rebooting. 195250974Swpaul */ 195350974Swpaulstatic void sis_shutdown(dev) 195450974Swpaul device_t dev; 195550974Swpaul{ 195650974Swpaul struct sis_softc *sc; 195750974Swpaul 195850974Swpaul sc = device_get_softc(dev); 195967087Swpaul SIS_LOCK(sc); 196050974Swpaul sis_reset(sc); 196150974Swpaul sis_stop(sc); 196267087Swpaul SIS_UNLOCK(sc); 196350974Swpaul 196450974Swpaul return; 196550974Swpaul} 1966