svn commit: r206625 - in head/sys: amd64/conf boot/forth conf dev/sge i386/conf modules modules/sge

Pyun YongHyeon yongari at FreeBSD.org
Wed Apr 14 20:45:34 UTC 2010


Author: yongari
Date: Wed Apr 14 20:45:33 2010
New Revision: 206625
URL: http://svn.freebsd.org/changeset/base/206625

Log:
  Add driver for Silicon Integrated Systems SiS190/191 Fast/Gigabit Ethernet.
  This driver was written by Alexander Pohoyda and greatly enhanced
  by Nikolay Denev. I don't have these hardwares but this driver was
  tested by Nikolay Denev and xclin.
  
  Because SiS didn't release data sheet for this controller, programming
  information came from Linux driver and OpenSolaris. Unlike other open
  source driver for SiS190/191, sge(4) takes full advantage of TX/RX
  checksum offloading and does not require additional copy operation in
  RX handler.
  The controller seems to have advanced offloading features like VLAN
  hardware tag insertion/stripping, TCP segmentation offload(TSO) as
  well as jumbo frame support but these features are not available
  yet. Special thanks to xclin <xclin<> cs dot nctu dot edu dot tw>
  who sent fix for receiving VLAN oversized frames.

Added:
  head/sys/dev/sge/
  head/sys/dev/sge/if_sge.c   (contents, props changed)
  head/sys/dev/sge/if_sgereg.h   (contents, props changed)
  head/sys/modules/sge/
  head/sys/modules/sge/Makefile   (contents, props changed)
Modified:
  head/sys/amd64/conf/GENERIC
  head/sys/boot/forth/loader.conf
  head/sys/conf/NOTES
  head/sys/conf/files
  head/sys/i386/conf/GENERIC
  head/sys/modules/Makefile

Modified: head/sys/amd64/conf/GENERIC
==============================================================================
--- head/sys/amd64/conf/GENERIC	Wed Apr 14 20:31:06 2010	(r206624)
+++ head/sys/amd64/conf/GENERIC	Wed Apr 14 20:45:33 2010	(r206625)
@@ -224,6 +224,7 @@ device		pcn		# AMD Am79C97x PCI 10/100 (
 device		re		# RealTek 8139C+/8169/8169S/8110S
 device		rl		# RealTek 8129/8139
 device		sf		# Adaptec AIC-6915 (``Starfire'')
+device		sge		# Silicon Integrated Systems SiS190/191
 device		sis		# Silicon Integrated Systems SiS 900/SiS 7016
 device		sk		# SysKonnect SK-984x & SK-982x gigabit Ethernet
 device		ste		# Sundance ST201 (D-Link DFE-550TX)

Modified: head/sys/boot/forth/loader.conf
==============================================================================
--- head/sys/boot/forth/loader.conf	Wed Apr 14 20:31:06 2010	(r206624)
+++ head/sys/boot/forth/loader.conf	Wed Apr 14 20:45:33 2010	(r206625)
@@ -271,6 +271,7 @@ if_rl_load="NO"			# RealTek 8129/8139
 if_rue_load="NO"		# RealTek RTL8150 USB to Fast Ethernet
 if_sbni_load="NO"		# Granch SBNI12 leased line adapters
 if_sf_load="NO"			# Adaptec Duralink PCI (AIC-6915 "starfire")
+if_sge_load="NO"		# Silicon Integrated Systems SiS190/191
 if_sis_load="NO"		# Silicon Integrated Systems SiS 900/7016
 if_sk_load="NO"			# SysKonnect SK-984x series PCI Gigabit Ethernet
 if_sn_load="NO"			# SMC 91Cxx

Modified: head/sys/conf/NOTES
==============================================================================
--- head/sys/conf/NOTES	Wed Apr 14 20:31:06 2010	(r206624)
+++ head/sys/conf/NOTES	Wed Apr 14 20:45:33 2010	(r206625)
@@ -1952,6 +1952,7 @@ device		xmphy		# XaQti XMAC II
 #       This includes dual and quad port cards, as well as one 100baseFX card.
 #       Most of these are 64-bit PCI devices, except for one single port
 #       card which is 32-bit.
+# sge:  Silicon Integrated Systems SiS190/191 Fast/Gigabit Ethernet adapter
 # sis:  Support for NICs based on the Silicon Integrated Systems SiS 900,
 #       SiS 7016 and NS DP83815 PCI fast ethernet controller chips.
 # sk:   Support for the SysKonnect SK-984x series PCI gigabit ethernet NICs.
@@ -2046,6 +2047,7 @@ device		re		# RealTek 8139C+/8169/8169S/
 device		rl		# RealTek 8129/8139
 device		pcn		# AMD Am79C97x PCI 10/100 NICs
 device		sf		# Adaptec AIC-6915 (``Starfire'')
+device		sge		# Silicon Integrated Systems SiS190/191
 device		sis		# Silicon Integrated Systems SiS 900/SiS 7016
 device		sk		# SysKonnect SK-984x & SK-982x gigabit Ethernet
 device		ste		# Sundance ST201 (D-Link DFE-550TX)

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Wed Apr 14 20:31:06 2010	(r206624)
+++ head/sys/conf/files	Wed Apr 14 20:45:33 2010	(r206625)
@@ -1490,6 +1490,7 @@ dev/scd/scd.c			optional scd isa
 dev/scd/scd_isa.c		optional scd isa
 dev/sdhci/sdhci.c		optional sdhci pci
 dev/sf/if_sf.c			optional sf pci
+dev/sge/if_sge.c		optional sge pci
 dev/si/si.c			optional si
 dev/si/si2_z280.c		optional si
 dev/si/si3_t225.c		optional si

Added: head/sys/dev/sge/if_sge.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/sge/if_sge.c	Wed Apr 14 20:45:33 2010	(r206625)
@@ -0,0 +1,1745 @@
+/*-
+ * Copyright (c) 2008-2010 Nikolay Denev <ndenev at gmail.com>
+ * Copyright (c) 2007-2008 Alexander Pohoyda <alexander.pohoyda at gmx.net>
+ * Copyright (c) 1997, 1998, 1999
+ *	Bill Paul <wpaul at ctr.columbia.edu>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL AUTHORS OR
+ * THE VOICES IN THEIR HEADS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * SiS 190/191 PCI Ethernet NIC driver.
+ *
+ * Adapted to SiS 190 NIC by Alexander Pohoyda based on the original
+ * SiS 900 driver by Bill Paul, using SiS 190/191 Solaris driver by
+ * Masayuki Murayama and SiS 190/191 GNU/Linux driver by K.M. Liu
+ * <kmliu at sis.com>.  Thanks to Pyun YongHyeon <pyunyh at gmail.com> for
+ * review and very useful comments.
+ *
+ * Adapted to SiS 191 NIC by Nikolay Denev with further ideas from the
+ * Linux and Solaris drivers.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_vlan_var.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include "if_sgereg.h"
+
+MODULE_DEPEND(sge, pci, 1, 1, 1);
+MODULE_DEPEND(sge, ether, 1, 1, 1);
+MODULE_DEPEND(sge, miibus, 1, 1, 1);
+
+/* "device miibus0" required.  See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+/*
+ * Various supported device vendors/types and their names.
+ */
+static struct sge_type sge_devs[] = {
+	{ SIS_VENDORID, SIS_DEVICEID_190, "SiS190 Fast Ethernet" },
+	{ SIS_VENDORID, SIS_DEVICEID_191, "SiS191 Fast/Gigabit Ethernet" },
+	{ 0, 0, NULL }
+};
+
+static int	sge_probe(device_t);
+static int	sge_attach(device_t);
+static int	sge_detach(device_t);
+static int	sge_shutdown(device_t);
+static int	sge_suspend(device_t);
+static int	sge_resume(device_t);
+
+static int	sge_miibus_readreg(device_t, int, int);
+static int	sge_miibus_writereg(device_t, int, int, int);
+static void	sge_miibus_statchg(device_t);
+
+static int	sge_newbuf(struct sge_softc *, int);
+static int	sge_encap(struct sge_softc *, struct mbuf **);
+#ifndef __NO_STRICT_ALIGNMENT
+static __inline void
+		sge_fixup_rx(struct mbuf *);
+#endif
+static __inline void
+		sge_discard_rxbuf(struct sge_softc *, int);
+static void	sge_rxeof(struct sge_softc *);
+static void	sge_txeof(struct sge_softc *);
+static void	sge_intr(void *);
+static void	sge_tick(void *);
+static void	sge_start(struct ifnet *);
+static void	sge_start_locked(struct ifnet *);
+static int	sge_ioctl(struct ifnet *, u_long, caddr_t);
+static void	sge_init(void *);
+static void	sge_init_locked(struct sge_softc *);
+static void	sge_stop(struct sge_softc *);
+static void	sge_watchdog(struct sge_softc *);
+static int	sge_ifmedia_upd(struct ifnet *);
+static void	sge_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+static int	sge_get_mac_addr_apc(struct sge_softc *, uint8_t *);
+static int	sge_get_mac_addr_eeprom(struct sge_softc *, uint8_t *);
+static uint16_t	sge_read_eeprom(struct sge_softc *, int);
+
+static void	sge_rxfilter(struct sge_softc *);
+static void	sge_reset(struct sge_softc *);
+static int	sge_list_rx_init(struct sge_softc *);
+static int	sge_list_rx_free(struct sge_softc *);
+static int	sge_list_tx_init(struct sge_softc *);
+static int	sge_list_tx_free(struct sge_softc *);
+
+static int	sge_dma_alloc(struct sge_softc *);
+static void	sge_dma_free(struct sge_softc *);
+static void	sge_dma_map_addr(void *, bus_dma_segment_t *, int, int);
+
+static device_method_t sge_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		sge_probe),
+	DEVMETHOD(device_attach,	sge_attach),
+	DEVMETHOD(device_detach,	sge_detach),
+	DEVMETHOD(device_suspend,	sge_suspend),
+	DEVMETHOD(device_resume,	sge_resume),
+	DEVMETHOD(device_shutdown,	sge_shutdown),
+
+	/* Bus interface */
+	DEVMETHOD(bus_print_child,	bus_generic_print_child),
+	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
+
+	/* MII interface */
+	DEVMETHOD(miibus_readreg,	sge_miibus_readreg),
+	DEVMETHOD(miibus_writereg,	sge_miibus_writereg),
+	DEVMETHOD(miibus_statchg,	sge_miibus_statchg),
+
+	KOBJMETHOD_END
+};
+
+static driver_t sge_driver = {
+	"sge", sge_methods, sizeof(struct sge_softc)
+};
+
+static devclass_t sge_devclass;
+
+DRIVER_MODULE(sge, pci, sge_driver, sge_devclass, 0, 0);
+DRIVER_MODULE(miibus, sge, miibus_driver, miibus_devclass, 0, 0);
+
+/*
+ * Register space access macros.
+ */
+#define	CSR_WRITE_4(sc, reg, val)	bus_write_4(sc->sge_res, reg, val)
+#define	CSR_WRITE_2(sc, reg, val)	bus_write_2(sc->sge_res, reg, val)
+#define	CSR_WRITE_1(cs, reg, val)	bus_write_1(sc->sge_res, reg, val)
+
+#define	CSR_READ_4(sc, reg)		bus_read_4(sc->sge_res, reg)
+#define	CSR_READ_2(sc, reg)		bus_read_2(sc->sge_res, reg)
+#define	CSR_READ_1(sc, reg)		bus_read_1(sc->sge_res, reg)
+
+/* Define to show Tx/Rx error status. */
+#undef SGE_SHOW_ERRORS
+
+#define	SGE_CSUM_FEATURES	(CSUM_IP | CSUM_TCP | CSUM_UDP)
+
+static void
+sge_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+	bus_addr_t *p;
+
+	if (error != 0)
+		return;
+	KASSERT(nseg == 1, ("too many DMA segments, %d should be 1", nseg));
+	p  = arg;
+	*p = segs->ds_addr;
+}
+
+/*
+ * Read a sequence of words from the EEPROM.
+ */
+static uint16_t
+sge_read_eeprom(struct sge_softc *sc, int offset)
+{
+	uint32_t val;
+	int i;
+
+	KASSERT(offset <= EI_OFFSET, ("EEPROM offset too big"));
+	CSR_WRITE_4(sc, ROMInterface,
+	    EI_REQ | EI_OP_RD | (offset << EI_OFFSET_SHIFT));
+	DELAY(500);
+	for (i = 0; i < SGE_TIMEOUT; i++) {
+		val = CSR_READ_4(sc, ROMInterface);
+		if ((val & EI_REQ) == 0)
+			break;
+		DELAY(100);
+	}
+	if (i == SGE_TIMEOUT) {
+		device_printf(sc->sge_dev,
+		    "EEPROM read timeout : 0x%08x\n", val);
+		return (0xffff);
+	}
+
+	return ((val & EI_DATA) >> EI_DATA_SHIFT);
+}
+
+static int
+sge_get_mac_addr_eeprom(struct sge_softc *sc, uint8_t *dest)
+{
+	uint16_t val;
+	int i;
+
+	val = sge_read_eeprom(sc, EEPROMSignature);
+	if (val == 0xffff || val == 0) {
+		device_printf(sc->sge_dev,
+		    "invalid EEPROM signature : 0x%04x\n", val);
+		return (EINVAL);
+	}
+
+	for (i = 0; i < ETHER_ADDR_LEN; i += 2) {
+		val = sge_read_eeprom(sc, EEPROMMACAddr + i / 2);
+		dest[i + 0] = (uint8_t)val;
+		dest[i + 1] = (uint8_t)(val >> 8);
+	}
+
+	if ((sge_read_eeprom(sc, EEPROMInfo) & 0x80) != 0)
+		sc->sge_flags |= SGE_FLAG_RGMII;
+	return (0);
+}
+
+/*
+ * For SiS96x, APC CMOS RAM is used to store ethernet address.
+ * APC CMOS RAM is accessed through ISA bridge.
+ */
+static int
+sge_get_mac_addr_apc(struct sge_softc *sc, uint8_t *dest)
+{
+#if defined(__amd64__) || defined(__i386__)
+	devclass_t pci;
+	device_t bus, dev = NULL;
+	device_t *kids;
+	struct apc_tbl {
+		uint16_t vid;
+		uint16_t did;
+	} *tp, apc_tbls[] = {
+		{ SIS_VENDORID, 0x0965 },
+		{ SIS_VENDORID, 0x0966 },
+		{ SIS_VENDORID, 0x0968 }
+	};
+	uint8_t reg;
+	int busnum, cnt, i, j, numkids;
+
+	cnt = sizeof(apc_tbls) / sizeof(apc_tbls[0]);
+	pci = devclass_find("pci");
+	for (busnum = 0; busnum < devclass_get_maxunit(pci); busnum++) {
+		bus = devclass_get_device(pci, busnum);
+		if (!bus)
+			continue;
+		if (device_get_children(bus, &kids, &numkids) != 0)
+			continue;
+		for (i = 0; i < numkids; i++) {
+			dev = kids[i];
+			if (pci_get_class(dev) == PCIC_BRIDGE &&
+			    pci_get_subclass(dev) == PCIS_BRIDGE_ISA) {
+				tp = apc_tbls;
+				for (j = 0; j < cnt; j++) {
+					if (pci_get_vendor(dev) == tp->vid &&
+					    pci_get_device(dev) == tp->did) {
+						free(kids, M_TEMP);
+						goto apc_found;
+					}
+					tp++;
+				}
+			}
+                }
+		free(kids, M_TEMP);
+	}
+	device_printf(sc->sge_dev, "couldn't find PCI-ISA bridge\n");
+	return (EINVAL);
+apc_found:
+	/* Enable port 0x78 and 0x79 to access APC registers. */
+	reg = pci_read_config(dev, 0x48, 1);
+	pci_write_config(dev, 0x48, reg & ~0x02, 1);
+	DELAY(50);
+	pci_read_config(dev, 0x48, 1);
+	/* Read stored ethernet address. */
+	for (i = 0; i < ETHER_ADDR_LEN; i++) {
+		outb(0x78, 0x09 + i);
+		dest[i] = inb(0x79);
+	}
+	outb(0x78, 0x12);
+	if ((inb(0x79) & 0x80) != 0)
+		sc->sge_flags |= SGE_FLAG_RGMII;
+	/* Restore access to APC registers. */
+	pci_write_config(dev, 0x48, reg, 1);
+
+	return (0);
+#else
+	return (EINVAL);
+#endif
+}
+
+static int
+sge_miibus_readreg(device_t dev, int phy, int reg)
+{
+	struct sge_softc *sc;
+	uint32_t val;
+	int i;
+
+	sc = device_get_softc(dev);
+	CSR_WRITE_4(sc, GMIIControl, (phy << GMI_PHY_SHIFT) |
+	    (reg << GMI_REG_SHIFT) | GMI_OP_RD | GMI_REQ);
+	DELAY(10);
+	for (i = 0; i < SGE_TIMEOUT; i++) {
+		val = CSR_READ_4(sc, GMIIControl);
+		if ((val & GMI_REQ) == 0)
+			break;
+		DELAY(10);
+	}
+	if (i == SGE_TIMEOUT) {
+		device_printf(sc->sge_dev, "PHY read timeout : %d\n", reg);
+		return (0);
+	}
+	return ((val & GMI_DATA) >> GMI_DATA_SHIFT);
+}
+
+static int
+sge_miibus_writereg(device_t dev, int phy, int reg, int data)
+{
+	struct sge_softc *sc;
+	uint32_t val;
+	int i;
+
+	sc = device_get_softc(dev);
+	CSR_WRITE_4(sc, GMIIControl, (phy << GMI_PHY_SHIFT) |
+	    (reg << GMI_REG_SHIFT) | (data << GMI_DATA_SHIFT) |
+	    GMI_OP_WR | GMI_REQ);
+	DELAY(10);
+	for (i = 0; i < SGE_TIMEOUT; i++) {
+		val = CSR_READ_4(sc, GMIIControl);
+		if ((val & GMI_REQ) == 0)
+			break;
+		DELAY(10);
+	}
+	if (i == SGE_TIMEOUT)
+		device_printf(sc->sge_dev, "PHY write timeout : %d\n", reg);
+	return (0);
+}
+
+static void
+sge_miibus_statchg(device_t dev)
+{
+	struct sge_softc *sc;
+	struct mii_data *mii;
+	struct ifnet *ifp;
+	uint32_t ctl, speed;
+
+	sc = device_get_softc(dev);
+	mii = device_get_softc(sc->sge_miibus);
+	ifp = sc->sge_ifp;
+	if (mii == NULL || ifp == NULL ||
+	    (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+		return;
+	speed = 0;
+	sc->sge_flags &= ~SGE_FLAG_LINK;
+	if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
+	    (IFM_ACTIVE | IFM_AVALID)) {
+		switch (IFM_SUBTYPE(mii->mii_media_active)) {
+		case IFM_10_T:
+			sc->sge_flags |= SGE_FLAG_LINK;
+			speed = SC_SPEED_10;
+			break;
+		case IFM_100_TX:
+			sc->sge_flags |= SGE_FLAG_LINK;
+			speed = SC_SPEED_100;
+			break;
+		case IFM_1000_T:
+			if ((sc->sge_flags & SGE_FLAG_FASTETHER) == 0) {
+				sc->sge_flags |= SGE_FLAG_LINK;
+				speed = SC_SPEED_1000;
+			}
+			break;
+		default:
+			break;
+                }
+        }
+	if ((sc->sge_flags & SGE_FLAG_LINK) == 0)
+		return;
+	/* Reprogram MAC to resolved speed/duplex/flow-control parameters. */
+	ctl = CSR_READ_4(sc, StationControl);
+	ctl &= ~(0x0f000000 | SC_FDX | SC_SPEED_MASK);
+	if (speed == SC_SPEED_1000) {
+		ctl |= 0x07000000;
+		sc->sge_flags |= SGE_FLAG_SPEED_1000;
+	} else {
+		ctl |= 0x04000000;
+		sc->sge_flags &= ~SGE_FLAG_SPEED_1000;
+	}
+#ifdef notyet
+	if ((sc->sge_flags & SGE_FLAG_GMII) != 0)
+		ctl |= 0x03000000;
+#endif
+	ctl |= speed;
+	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
+		ctl |= SC_FDX;
+		sc->sge_flags |= SGE_FLAG_FDX;
+	} else
+		sc->sge_flags &= ~SGE_FLAG_FDX;
+	CSR_WRITE_4(sc, StationControl, ctl);
+	if ((sc->sge_flags & SGE_FLAG_RGMII) != 0) {
+		CSR_WRITE_4(sc, RGMIIDelay, 0x0441);
+		CSR_WRITE_4(sc, RGMIIDelay, 0x0440);
+	}
+}
+
+static void
+sge_rxfilter(struct sge_softc *sc)
+{
+	struct ifnet *ifp;
+	struct ifmultiaddr *ifma;
+	uint32_t crc, hashes[2];
+	uint16_t rxfilt;
+
+	SGE_LOCK_ASSERT(sc);
+
+	ifp = sc->sge_ifp;
+	hashes[0] = hashes[1] = 0;
+	rxfilt = AcceptMyPhys;
+	if ((ifp->if_flags & IFF_BROADCAST) != 0)
+		rxfilt |= AcceptBroadcast;
+	if ((ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) != 0) {
+		if ((ifp->if_flags & IFF_PROMISC) != 0)
+			rxfilt |= AcceptAllPhys;
+		rxfilt |= AcceptMulticast;
+		hashes[0] = 0xFFFFFFFF;
+		hashes[1] = 0xFFFFFFFF;
+		goto done;
+	}
+	rxfilt |= AcceptMulticast;
+	/* Now program new ones. */
+	if_maddr_rlock(ifp);
+	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
+		if (ifma->ifma_addr->sa_family != AF_LINK)
+			continue;
+		crc = ether_crc32_be(LLADDR((struct sockaddr_dl *)
+		    ifma->ifma_addr), ETHER_ADDR_LEN);
+		hashes[crc >> 31] |= 1 << ((crc >> 26) & 0x1f);
+	}
+	if_maddr_runlock(ifp);
+done:
+	CSR_WRITE_2(sc, RxMacControl, rxfilt | 0x02);
+	CSR_WRITE_4(sc, RxHashTable, hashes[0]);
+	CSR_WRITE_4(sc, RxHashTable2, hashes[1]);
+}
+
+static void
+sge_reset(struct sge_softc *sc)
+{
+
+	CSR_WRITE_4(sc, IntrMask, 0);
+	CSR_WRITE_4(sc, IntrStatus, 0xffffffff);
+
+	/* Soft reset. */
+	CSR_WRITE_4(sc, IntrControl, 0x8000);
+	CSR_READ_4(sc, IntrControl);
+	DELAY(100);
+	CSR_WRITE_4(sc, IntrControl, 0);
+	/* Stop MAC. */
+	CSR_WRITE_4(sc, TX_CTL, 0x1a00);
+	CSR_WRITE_4(sc, RX_CTL, 0x1a00);
+
+	CSR_WRITE_4(sc, IntrMask, 0);
+	CSR_WRITE_4(sc, IntrStatus, 0xffffffff);
+
+	CSR_WRITE_4(sc, GMIIControl, 0);
+}
+
+/*
+ * Probe for an SiS chip. Check the PCI vendor and device
+ * IDs against our list and return a device name if we find a match.
+ */
+static int
+sge_probe(device_t dev)
+{
+	struct sge_type *t;
+
+	t = sge_devs;
+	while (t->sge_name != NULL) {
+		if ((pci_get_vendor(dev) == t->sge_vid) &&
+		    (pci_get_device(dev) == t->sge_did)) {
+			device_set_desc(dev, t->sge_name);
+			return (BUS_PROBE_DEFAULT);
+		}
+		t++;
+	}
+
+	return (ENXIO);
+}
+
+/*
+ * Attach the interface.  Allocate softc structures, do ifmedia
+ * setup and ethernet/BPF attach.
+ */
+static int
+sge_attach(device_t dev)
+{
+	struct sge_softc *sc;
+	struct ifnet *ifp;
+	uint8_t eaddr[ETHER_ADDR_LEN];
+	int error = 0, rid;
+
+	sc = device_get_softc(dev);
+	sc->sge_dev = dev;
+
+	mtx_init(&sc->sge_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
+	    MTX_DEF);
+        callout_init_mtx(&sc->sge_stat_ch, &sc->sge_mtx, 0);
+
+	/*
+	 * Map control/status registers.
+	 */
+	pci_enable_busmaster(dev);
+
+	/* Allocate resources. */
+	sc->sge_res_id = PCIR_BAR(0);
+	sc->sge_res_type = SYS_RES_MEMORY;
+	sc->sge_res = bus_alloc_resource_any(dev, sc->sge_res_type,
+	    &sc->sge_res_id, RF_ACTIVE);
+	if (sc->sge_res == NULL) {
+		device_printf(dev, "couldn't allocate resource\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+	rid = 0;
+	sc->sge_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_SHAREABLE | RF_ACTIVE);
+	if (sc->sge_irq == NULL) {
+		device_printf(dev, "couldn't allocate IRQ resources\n");
+		error = ENXIO;
+		goto fail;
+	}
+	sc->sge_rev = pci_get_revid(dev);
+	if (pci_get_device(dev) == SIS_DEVICEID_190)
+		sc->sge_flags |= SGE_FLAG_FASTETHER;
+	/* Reset the adapter. */
+	sge_reset(sc);
+
+	/* Get MAC address from the EEPROM. */
+	if ((pci_read_config(dev, 0x73, 1) & 0x01) != 0)
+		sge_get_mac_addr_apc(sc, eaddr);
+	else
+		sge_get_mac_addr_eeprom(sc, eaddr);
+
+	if ((error = sge_dma_alloc(sc)) != 0)
+		goto fail;
+
+	ifp = sc->sge_ifp = if_alloc(IFT_ETHER);
+	if (ifp == NULL) {
+		device_printf(dev, "cannot allocate ifnet structure.\n");
+		error = ENOSPC;
+		goto fail;
+	}
+	ifp->if_softc = sc;
+	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
+	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+	ifp->if_ioctl = sge_ioctl;
+	ifp->if_start = sge_start;
+	ifp->if_init = sge_init;
+	ifp->if_snd.ifq_drv_maxlen = SGE_TX_RING_CNT - 1;
+	IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen);
+	IFQ_SET_READY(&ifp->if_snd);
+	ifp->if_capabilities = IFCAP_TXCSUM | IFCAP_RXCSUM;
+	ifp->if_hwassist = SGE_CSUM_FEATURES;
+	ifp->if_capenable = ifp->if_capabilities;
+	/*
+	 * Do MII setup.
+	 */
+	if (mii_phy_probe(dev, &sc->sge_miibus, sge_ifmedia_upd,
+	    sge_ifmedia_sts)) {
+		device_printf(dev, "no PHY found!\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+	/*
+	 * Call MI attach routine.
+	 */
+	ether_ifattach(ifp, eaddr);
+
+	/* VLAN setup. */
+	ifp->if_capabilities |= IFCAP_VLAN_MTU;
+	ifp->if_capenable = ifp->if_capabilities;
+	/* Tell the upper layer(s) we support long frames. */
+	ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
+
+	/* Hook interrupt last to avoid having to lock softc */
+	error = bus_setup_intr(dev, sc->sge_irq, INTR_TYPE_NET | INTR_MPSAFE,
+	    NULL, sge_intr, sc, &sc->sge_intrhand);
+	if (error) {
+		device_printf(dev, "couldn't set up irq\n");
+		ether_ifdetach(ifp);
+		goto fail;
+	}
+
+fail:
+	if (error)
+		sge_detach(dev);
+
+	return (error);
+}
+
+/*
+ * Shutdown hardware and free up resources.  This can be called any
+ * time after the mutex has been initialized.  It is called in both
+ * the error case in attach and the normal detach case so it needs
+ * to be careful about only freeing resources that have actually been
+ * allocated.
+ */
+static int
+sge_detach(device_t dev)
+{
+	struct sge_softc *sc;
+	struct ifnet *ifp;
+
+	sc = device_get_softc(dev);
+	ifp = sc->sge_ifp;
+	/* These should only be active if attach succeeded. */
+	if (device_is_attached(dev)) {
+		ether_ifdetach(ifp);
+		SGE_LOCK(sc);
+		sge_stop(sc);
+		SGE_UNLOCK(sc);
+		callout_drain(&sc->sge_stat_ch);
+	}
+	if (sc->sge_miibus)
+		device_delete_child(dev, sc->sge_miibus);
+	bus_generic_detach(dev);
+
+	if (sc->sge_intrhand)
+		bus_teardown_intr(dev, sc->sge_irq, sc->sge_intrhand);
+	if (sc->sge_irq)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sge_irq);
+	if (sc->sge_res)
+		bus_release_resource(dev, sc->sge_res_type, sc->sge_res_id,
+		    sc->sge_res);
+	if (ifp)
+		if_free(ifp);
+	sge_dma_free(sc);
+	mtx_destroy(&sc->sge_mtx);
+
+	return (0);
+}
+
+/*
+ * Stop all chip I/O so that the kernel's probe routines don't
+ * get confused by errant DMAs when rebooting.
+ */
+static int
+sge_shutdown(device_t dev)
+{
+	struct sge_softc *sc;
+
+	sc = device_get_softc(dev);
+	SGE_LOCK(sc);
+	sge_stop(sc);
+	SGE_UNLOCK(sc);
+	return (0);
+}
+
+static int
+sge_suspend(device_t dev)
+{
+	struct sge_softc *sc;
+	struct ifnet *ifp;
+
+	sc = device_get_softc(dev);
+	SGE_LOCK(sc);
+	ifp = sc->sge_ifp;
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+		sge_stop(sc);
+	SGE_UNLOCK(sc);
+	return (0);
+}
+
+static int
+sge_resume(device_t dev)
+{
+	struct sge_softc *sc;
+	struct ifnet *ifp;
+
+	sc = device_get_softc(dev);
+	SGE_LOCK(sc);
+	ifp = sc->sge_ifp;
+	if ((ifp->if_flags & IFF_UP) != 0)
+		sge_init_locked(sc);
+	SGE_UNLOCK(sc);
+	return (0);
+}
+
+static int
+sge_dma_alloc(struct sge_softc *sc)
+{
+	struct sge_chain_data *cd;
+	struct sge_list_data *ld;
+	int error, i;
+
+	cd = &sc->sge_cdata;
+	ld = &sc->sge_ldata;
+	error = bus_dma_tag_create(bus_get_dma_tag(sc->sge_dev),
+	    1, 0,			/* alignment, boundary */
+	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
+	    BUS_SPACE_MAXADDR,		/* highaddr */
+	    NULL, NULL,			/* filter, filterarg */
+	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsize */
+	    1,				/* nsegments */
+	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
+	    0,				/* flags */
+	    NULL,			/* lockfunc */
+	    NULL,			/* lockarg */
+	    &cd->sge_tag);
+	if (error != 0) {
+		device_printf(sc->sge_dev,
+		    "could not create parent DMA tag.\n");
+		goto fail;
+	}
+
+	/* RX descriptor ring */
+	error = bus_dma_tag_create(cd->sge_tag,
+	    SGE_DESC_ALIGN, 0,		/* alignment, boundary */
+	    BUS_SPACE_MAXADDR,		/* lowaddr */
+	    BUS_SPACE_MAXADDR,		/* highaddr */
+	    NULL, NULL,			/* filter, filterarg */
+	    SGE_RX_RING_SZ, 1,		/* maxsize,nsegments */
+	    SGE_RX_RING_SZ,		/* maxsegsize */
+	    0,				/* flags */
+	    NULL,			/* lockfunc */
+	    NULL,			/* lockarg */
+	    &cd->sge_rx_tag);
+	if (error != 0) {
+		device_printf(sc->sge_dev,
+		    "could not create Rx ring DMA tag.\n");
+		goto fail;
+	}
+	/* Allocate DMA'able memory and load DMA map for RX ring. */
+	error = bus_dmamem_alloc(cd->sge_rx_tag, (void **)&ld->sge_rx_ring,
+	    BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT,
+	    &cd->sge_rx_dmamap);
+	if (error != 0) {
+		device_printf(sc->sge_dev,
+		    "could not allocate DMA'able memory for Rx ring.\n");
+		goto fail;
+	}
+	error = bus_dmamap_load(cd->sge_rx_tag, cd->sge_rx_dmamap,
+	    ld->sge_rx_ring, SGE_RX_RING_SZ, sge_dma_map_addr,
+	    &ld->sge_rx_paddr, BUS_DMA_NOWAIT);
+	if (error != 0) {
+		device_printf(sc->sge_dev,
+		    "could not load DMA'able memory for Rx ring.\n");
+	}
+
+	/* TX descriptor ring */
+	error = bus_dma_tag_create(cd->sge_tag,
+	    SGE_DESC_ALIGN, 0,		/* alignment, boundary */
+	    BUS_SPACE_MAXADDR,		/* lowaddr */
+	    BUS_SPACE_MAXADDR,		/* highaddr */
+	    NULL, NULL,			/* filter, filterarg */
+	    SGE_TX_RING_SZ, 1,		/* maxsize,nsegments */
+	    SGE_TX_RING_SZ,		/* maxsegsize */
+	    0,				/* flags */
+	    NULL,			/* lockfunc */
+	    NULL,			/* lockarg */
+	    &cd->sge_tx_tag);
+	if (error != 0) {
+		device_printf(sc->sge_dev,
+		    "could not create Rx ring DMA tag.\n");
+		goto fail;
+	}
+	/* Allocate DMA'able memory and load DMA map for TX ring. */
+	error = bus_dmamem_alloc(cd->sge_tx_tag, (void **)&ld->sge_tx_ring,
+	    BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT,
+	    &cd->sge_tx_dmamap);
+	if (error != 0) {
+		device_printf(sc->sge_dev,
+		    "could not allocate DMA'able memory for Tx ring.\n");
+		goto fail;
+	}
+	error = bus_dmamap_load(cd->sge_tx_tag, cd->sge_tx_dmamap,
+	    ld->sge_tx_ring, SGE_TX_RING_SZ, sge_dma_map_addr,
+	    &ld->sge_tx_paddr, BUS_DMA_NOWAIT);
+	if (error != 0) {
+		device_printf(sc->sge_dev,
+		    "could not load DMA'able memory for Rx ring.\n");
+		goto fail;
+	}
+
+	/* Create DMA tag for Tx buffers. */
+	error = bus_dma_tag_create(cd->sge_tag, 1, 0, BUS_SPACE_MAXADDR,
+	    BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES * SGE_MAXTXSEGS,
+	    SGE_MAXTXSEGS, MCLBYTES, 0, NULL, NULL, &cd->sge_txmbuf_tag);
+	if (error != 0) {
+		device_printf(sc->sge_dev,
+		    "could not create Tx mbuf DMA tag.\n");
+		goto fail;
+	}
+
+	/* Create DMA tag for Rx buffers. */
+	error = bus_dma_tag_create(cd->sge_tag, SGE_RX_BUF_ALIGN, 0,
+	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 1,
+	    MCLBYTES, 0, NULL, NULL, &cd->sge_rxmbuf_tag);
+	if (error != 0) {
+		device_printf(sc->sge_dev,
+		    "could not create Rx mbuf DMA tag.\n");
+		goto fail;
+	}
+
+	/* Create DMA maps for Tx buffers. */
+	for (i = 0; i < SGE_TX_RING_CNT; i++) {
+		error = bus_dmamap_create(cd->sge_txmbuf_tag, 0,
+		    &cd->sge_tx_map[i]);
+		if (error != 0) {
+			device_printf(sc->sge_dev,
+			    "could not create Tx DMA map.\n");
+			goto fail;
+		}
+	}
+	/* Create spare DMA map for Rx buffer. */
+	error = bus_dmamap_create(cd->sge_rxmbuf_tag, 0, &cd->sge_rx_spare_map);
+	if (error != 0) {
+		device_printf(sc->sge_dev,
+		    "could not create spare Rx DMA map.\n");
+		goto fail;
+	}
+	/* Create DMA maps for Rx buffers. */
+	for (i = 0; i < SGE_RX_RING_CNT; i++) {
+		error = bus_dmamap_create(cd->sge_rxmbuf_tag, 0,
+		    &cd->sge_rx_map[i]);
+		if (error) {
+			device_printf(sc->sge_dev,
+			    "could not create Rx DMA map.\n");
+			goto fail;
+		}
+	}
+fail:
+	return (error);
+}
+
+static void
+sge_dma_free(struct sge_softc *sc)
+{
+	struct sge_chain_data *cd;
+	struct sge_list_data *ld;
+	int i;
+
+	cd = &sc->sge_cdata;
+	ld = &sc->sge_ldata;
+	/* Rx ring. */
+	if (cd->sge_rx_tag != NULL) {
+		if (cd->sge_rx_dmamap != NULL)
+			bus_dmamap_unload(cd->sge_rx_tag, cd->sge_rx_dmamap);
+		if (cd->sge_rx_dmamap != NULL && ld->sge_rx_ring != NULL)
+			bus_dmamem_free(cd->sge_rx_tag, ld->sge_rx_ring,
+			    cd->sge_rx_dmamap);
+		ld->sge_rx_ring = NULL;
+		cd->sge_rx_dmamap = NULL;
+		bus_dma_tag_destroy(cd->sge_rx_tag);
+		cd->sge_rx_tag = NULL;
+	}
+	/* Tx ring. */
+	if (cd->sge_tx_tag != NULL) {
+		if (cd->sge_tx_dmamap != NULL)
+			bus_dmamap_unload(cd->sge_tx_tag, cd->sge_tx_dmamap);
+		if (cd->sge_tx_dmamap != NULL && ld->sge_tx_ring != NULL)
+			bus_dmamem_free(cd->sge_tx_tag, ld->sge_tx_ring,
+			    cd->sge_tx_dmamap);
+		ld->sge_tx_ring = NULL;
+		cd->sge_tx_dmamap = NULL;
+		bus_dma_tag_destroy(cd->sge_tx_tag);
+		cd->sge_tx_tag = NULL;
+	}
+	/* Rx buffers. */
+	if (cd->sge_rxmbuf_tag != NULL) {
+		for (i = 0; i < SGE_RX_RING_CNT; i++) {
+			if (cd->sge_rx_map[i] != NULL) {
+				bus_dmamap_destroy(cd->sge_rxmbuf_tag,
+				    cd->sge_rx_map[i]);
+				cd->sge_rx_map[i] = NULL;
+			}
+		}
+		if (cd->sge_rx_spare_map != NULL) {
+			bus_dmamap_destroy(cd->sge_rxmbuf_tag,
+			    cd->sge_rx_spare_map);
+			cd->sge_rx_spare_map = NULL;
+		}
+		bus_dma_tag_destroy(cd->sge_rxmbuf_tag);
+		cd->sge_rxmbuf_tag = NULL;
+	}
+	/* Tx buffers. */
+	if (cd->sge_txmbuf_tag != NULL) {
+		for (i = 0; i < SGE_TX_RING_CNT; i++) {
+			if (cd->sge_tx_map[i] != NULL) {
+				bus_dmamap_destroy(cd->sge_txmbuf_tag,
+				    cd->sge_tx_map[i]);
+				cd->sge_tx_map[i] = NULL;
+			}
+		}
+		bus_dma_tag_destroy(cd->sge_txmbuf_tag);
+		cd->sge_txmbuf_tag = NULL;
+	}
+	if (cd->sge_tag != NULL)
+		bus_dma_tag_destroy(cd->sge_tag);
+	cd->sge_tag = NULL;
+}
+
+/*
+ * Initialize the TX descriptors.
+ */
+static int
+sge_list_tx_init(struct sge_softc *sc)
+{

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-all mailing list