svn commit: r216829 - in head/sys: boot/forth conf dev/vte i386/conf modules modules/vte

Pyun YongHyeon yongari at FreeBSD.org
Fri Dec 31 00:21:41 UTC 2010


Author: yongari
Date: Fri Dec 31 00:21:41 2010
New Revision: 216829
URL: http://svn.freebsd.org/changeset/base/216829

Log:
  Add driver for DM&P Vortex86 RDC R6040 Fast Ethernet.
  The controller is commonly found on DM&P Vortex86 x86 SoC.  The
  driver supports all hardware features except flow control.  The
  flow control was intentionally disabled due to silicon bug.
  
  DM&P Electronics, Inc. provided all necessary information including
  sample board to write driver and answered many questions I had.
  Many thanks for their support of FreeBSD.
  
  H/W donated by:	DM&P Electronics, Inc.

Added:
  head/sys/dev/vte/
  head/sys/dev/vte/if_vte.c   (contents, props changed)
  head/sys/dev/vte/if_vtereg.h   (contents, props changed)
  head/sys/dev/vte/if_vtevar.h   (contents, props changed)
  head/sys/modules/vte/
  head/sys/modules/vte/Makefile   (contents, props changed)
Modified:
  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/boot/forth/loader.conf
==============================================================================
--- head/sys/boot/forth/loader.conf	Thu Dec 30 23:50:25 2010	(r216828)
+++ head/sys/boot/forth/loader.conf	Fri Dec 31 00:21:41 2010	(r216829)
@@ -328,6 +328,7 @@ if_tl_load="NO"			# Texas Instruments TN
 if_tx_load="NO"			# SMC 83c17x Fast Ethernet
 if_txp_load="NO"		# 3Com 3XP Typhoon/Sidewinder (3CR990)
 if_vge_load="NO"		# VIA VT6122 PCI Gigabit Ethernet
+if_vte_load="NO"		# DM&P Vortex86 RDC R6040 Fast Ethernet
 if_uath_load="NO"		# Atheros USB wireless for AR5005UG & AR5005UX
 if_udav_load="NO"		# Davicom DM9601 USB Ethernet
 if_upgt_load="NO"		# Conexant/Intersil PrismGT USB wireless

Modified: head/sys/conf/NOTES
==============================================================================
--- head/sys/conf/NOTES	Thu Dec 30 23:50:25 2010	(r216828)
+++ head/sys/conf/NOTES	Fri Dec 31 00:21:41 2010	(r216829)
@@ -1995,6 +1995,7 @@ device		xmphy		# XaQti XMAC II
 #       Technologies VT3043 `Rhine I' and VT86C100A `Rhine II' chips,
 #       including the D-Link DFE520TX and D-Link DFE530TX (see 'rl' for
 #       DFE530TX+), the Hawking Technologies PN102TX, and the AOpen/Acer ALN-320.
+# vte:  DM&P Vortex86 RDC R6040 Fast Ethernet
 # vx:   3Com 3C590 and 3C595
 # wb:   Support for fast ethernet adapters based on the Winbond W89C840F chip.
 #       Note: this is not the same as the Winbond W89C940F, which is a
@@ -2067,6 +2068,7 @@ device		stge		# Sundance/Tamarack TC9021
 device		tl		# Texas Instruments ThunderLAN
 device		tx		# SMC EtherPower II (83c170 ``EPIC'')
 device		vr		# VIA Rhine, Rhine II
+device		vte		# DM&P Vortex86 RDC R6040 Fast Ethernet
 device		wb		# Winbond W89C840F
 device		xl		# 3Com 3c90x (``Boomerang'', ``Cyclone'')
 

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Thu Dec 30 23:50:25 2010	(r216828)
+++ head/sys/conf/files	Fri Dec 31 00:21:41 2010	(r216829)
@@ -1876,6 +1876,7 @@ dev/utopia/utopia.c		optional utopia
 dev/vge/if_vge.c		optional vge
 dev/vkbd/vkbd.c			optional vkbd
 dev/vr/if_vr.c			optional vr pci
+dev/vte/if_vte.c		optional vte pci
 dev/vx/if_vx.c			optional vx
 dev/vx/if_vx_eisa.c		optional vx eisa
 dev/vx/if_vx_pci.c		optional vx pci

Added: head/sys/dev/vte/if_vte.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/vte/if_vte.c	Fri Dec 31 00:21:41 2010	(r216829)
@@ -0,0 +1,2056 @@
+/*-
+ * Copyright (c) 2010, Pyun YongHyeon <yongari at FreeBSD.org>
+ * 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 unmodified, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 THE AUTHOR OR CONTRIBUTORS 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.
+ */
+
+/* Driver for DM&P Electronics, Inc, Vortex86 RDC R6040 FastEthernet. */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#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 <sys/sysctl.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_llc.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_vlan_var.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <machine/bus.h>
+
+#if 0
+#include "if_vtereg.h"
+#include "if_vtevar.h"
+#else
+#include <dev/vte/if_vtereg.h>
+#include <dev/vte/if_vtevar.h>
+#endif
+
+/* "device miibus" required.  See GENERIC if you get errors here. */
+#include "miibus_if.h"
+
+MODULE_DEPEND(vte, pci, 1, 1, 1);
+MODULE_DEPEND(vte, ether, 1, 1, 1);
+MODULE_DEPEND(vte, miibus, 1, 1, 1);
+
+/* Tunables. */
+static int tx_deep_copy = 1;
+TUNABLE_INT("hw.vte.tx_deep_copy", &tx_deep_copy);
+
+/*
+ * Devices supported by this driver.
+ */
+static const struct vte_ident vte_ident_table[] = {
+	{ VENDORID_RDC, DEVICEID_RDC_R6040, "RDC R6040 FastEthernet"},
+	{ 0, 0, NULL}
+};
+
+static int	vte_attach(device_t);
+static int	vte_detach(device_t);
+static int	vte_dma_alloc(struct vte_softc *);
+static void	vte_dma_free(struct vte_softc *);
+static void	vte_dmamap_cb(void *, bus_dma_segment_t *, int, int);
+static struct vte_txdesc *
+		vte_encap(struct vte_softc *, struct mbuf **);
+static const struct vte_ident *
+		vte_find_ident(device_t);
+#ifndef __NO_STRICT_ALIGNMENT
+static struct mbuf *
+		vte_fixup_rx(struct ifnet *, struct mbuf *);
+#endif
+static void	vte_get_macaddr(struct vte_softc *);
+static void	vte_init(void *);
+static void	vte_init_locked(struct vte_softc *);
+static int	vte_init_rx_ring(struct vte_softc *);
+static int	vte_init_tx_ring(struct vte_softc *);
+static void	vte_intr(void *);
+static int	vte_ioctl(struct ifnet *, u_long, caddr_t);
+static void	vte_mac_config(struct vte_softc *);
+static int	vte_miibus_readreg(device_t, int, int);
+static void	vte_miibus_statchg(device_t);
+static int	vte_miibus_writereg(device_t, int, int, int);
+static int	vte_mediachange(struct ifnet *);
+static int	vte_mediachange_locked(struct ifnet *);
+static void	vte_mediastatus(struct ifnet *, struct ifmediareq *);
+static int	vte_newbuf(struct vte_softc *, struct vte_rxdesc *);
+static int	vte_probe(device_t);
+static void	vte_reset(struct vte_softc *);
+static int	vte_resume(device_t);
+static void	vte_rxeof(struct vte_softc *);
+static void	vte_rxfilter(struct vte_softc *);
+static int	vte_shutdown(device_t);
+static void	vte_start(struct ifnet *);
+static void	vte_start_locked(struct vte_softc *);
+static void	vte_start_mac(struct vte_softc *);
+static void	vte_stats_clear(struct vte_softc *);
+static void	vte_stats_update(struct vte_softc *);
+static void	vte_stop(struct vte_softc *);
+static void	vte_stop_mac(struct vte_softc *);
+static int	vte_suspend(device_t);
+static void	vte_sysctl_node(struct vte_softc *);
+static void	vte_tick(void *);
+static void	vte_txeof(struct vte_softc *);
+static void	vte_watchdog(struct vte_softc *);
+static int	sysctl_int_range(SYSCTL_HANDLER_ARGS, int, int);
+static int	sysctl_hw_vte_int_mod(SYSCTL_HANDLER_ARGS);
+
+static device_method_t vte_methods[] = {
+	/* Device interface. */
+	DEVMETHOD(device_probe,		vte_probe),
+	DEVMETHOD(device_attach,	vte_attach),
+	DEVMETHOD(device_detach,	vte_detach),
+	DEVMETHOD(device_shutdown,	vte_shutdown),
+	DEVMETHOD(device_suspend,	vte_suspend),
+	DEVMETHOD(device_resume,	vte_resume),
+
+	/* MII interface. */
+	DEVMETHOD(miibus_readreg,	vte_miibus_readreg),
+	DEVMETHOD(miibus_writereg,	vte_miibus_writereg),
+	DEVMETHOD(miibus_statchg,	vte_miibus_statchg),
+
+	KOBJMETHOD_END
+};
+
+static driver_t vte_driver = {
+	"vte",
+	vte_methods,
+	sizeof(struct vte_softc)
+};
+
+static devclass_t vte_devclass;
+
+DRIVER_MODULE(vte, pci, vte_driver, vte_devclass, 0, 0);
+DRIVER_MODULE(miibus, vte, miibus_driver, miibus_devclass, 0, 0);
+
+static int
+vte_miibus_readreg(device_t dev, int phy, int reg)
+{
+	struct vte_softc *sc;
+	int i;
+
+	sc = device_get_softc(dev);
+
+	CSR_WRITE_2(sc, VTE_MMDIO, MMDIO_READ |
+	    (phy << MMDIO_PHY_ADDR_SHIFT) | (reg << MMDIO_REG_ADDR_SHIFT));
+	for (i = VTE_PHY_TIMEOUT; i > 0; i--) {
+		DELAY(5);
+		if ((CSR_READ_2(sc, VTE_MMDIO) & MMDIO_READ) == 0)
+			break;
+	}
+
+	if (i == 0) {
+		device_printf(sc->vte_dev, "phy read timeout : %d\n", reg);
+		return (0);
+	}
+
+	return (CSR_READ_2(sc, VTE_MMRD));
+}
+
+static int
+vte_miibus_writereg(device_t dev, int phy, int reg, int val)
+{
+	struct vte_softc *sc;
+	int i;
+
+	sc = device_get_softc(dev);
+
+	CSR_WRITE_2(sc, VTE_MMWD, val);
+	CSR_WRITE_2(sc, VTE_MMDIO, MMDIO_WRITE |
+	    (phy << MMDIO_PHY_ADDR_SHIFT) | (reg << MMDIO_REG_ADDR_SHIFT));
+	for (i = VTE_PHY_TIMEOUT; i > 0; i--) {
+		DELAY(5);
+		if ((CSR_READ_2(sc, VTE_MMDIO) & MMDIO_WRITE) == 0)
+			break;
+	}
+
+	if (i == 0)
+		device_printf(sc->vte_dev, "phy write timeout : %d\n", reg);
+
+	return (0);
+}
+
+static void
+vte_miibus_statchg(device_t dev)
+{
+	struct vte_softc *sc;
+	struct mii_data *mii;
+	struct ifnet *ifp;
+	uint16_t val;
+
+	sc = device_get_softc(dev);
+
+	mii = device_get_softc(sc->vte_miibus);
+	ifp = sc->vte_ifp;
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
+		return;
+
+	sc->vte_flags &= ~VTE_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:
+		case IFM_100_TX:
+			sc->vte_flags |= VTE_FLAG_LINK;
+			break;
+		default:
+			break;
+		}
+	}
+
+	/* Stop RX/TX MACs. */
+	vte_stop_mac(sc);
+	/* Program MACs with resolved duplex and flow control. */
+	if ((sc->vte_flags & VTE_FLAG_LINK) != 0) {
+		/*
+		 * Timer waiting time : (63 + TIMER * 64) MII clock.
+		 * MII clock : 25MHz(100Mbps) or 2.5MHz(10Mbps).
+		 */
+		if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX)
+			val = 18 << VTE_IM_TIMER_SHIFT;
+		else
+			val = 1 << VTE_IM_TIMER_SHIFT;
+		val |= sc->vte_int_rx_mod << VTE_IM_BUNDLE_SHIFT;
+		/* 48.6us for 100Mbps, 50.8us for 10Mbps */
+		CSR_WRITE_2(sc, VTE_MRICR, val);
+
+		if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX)
+			val = 18 << VTE_IM_TIMER_SHIFT;
+		else
+			val = 1 << VTE_IM_TIMER_SHIFT;
+		val |= sc->vte_int_tx_mod << VTE_IM_BUNDLE_SHIFT;
+		/* 48.6us for 100Mbps, 50.8us for 10Mbps */
+		CSR_WRITE_2(sc, VTE_MTICR, val);
+
+		vte_mac_config(sc);
+		vte_start_mac(sc);
+	}
+}
+
+static void
+vte_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+	struct vte_softc *sc;
+	struct mii_data *mii;
+
+	sc = ifp->if_softc;
+	VTE_LOCK(sc);
+	if ((ifp->if_flags & IFF_UP) == 0) {
+		VTE_UNLOCK(sc);
+		return;
+	}
+	mii = device_get_softc(sc->vte_miibus);
+
+	mii_pollstat(mii);
+	VTE_UNLOCK(sc);
+	ifmr->ifm_status = mii->mii_media_status;
+	ifmr->ifm_active = mii->mii_media_active;
+}
+
+static int
+vte_mediachange(struct ifnet *ifp)
+{
+	struct vte_softc *sc;
+	int error;
+
+	sc = ifp->if_softc;
+	VTE_LOCK(sc);
+	error = vte_mediachange_locked(ifp);
+	VTE_UNLOCK(sc);
+	return (error);
+}
+
+static int
+vte_mediachange_locked(struct ifnet *ifp)
+{
+	struct vte_softc *sc;
+	struct mii_data *mii;
+	struct mii_softc *miisc;
+	int error;
+
+	sc = ifp->if_softc;
+	mii = device_get_softc(sc->vte_miibus);
+	if (mii->mii_instance != 0) {
+		LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
+			mii_phy_reset(miisc);
+	}
+	error = mii_mediachg(mii);
+
+	return (error);
+}
+
+static const struct vte_ident *
+vte_find_ident(device_t dev)
+{
+	const struct vte_ident *ident;
+	uint16_t vendor, devid;
+
+	vendor = pci_get_vendor(dev);
+	devid = pci_get_device(dev);
+	for (ident = vte_ident_table; ident->name != NULL; ident++) {
+		if (vendor == ident->vendorid && devid == ident->deviceid)
+			return (ident);
+	}
+
+	return (NULL);
+}
+
+static int
+vte_probe(device_t dev)
+{
+	const struct vte_ident *ident;
+
+	ident = vte_find_ident(dev);
+	if (ident != NULL) {
+		device_set_desc(dev, ident->name);
+		return (BUS_PROBE_DEFAULT);
+	}
+
+	return (ENXIO);
+}
+
+static void
+vte_get_macaddr(struct vte_softc *sc)
+{
+	uint16_t mid;
+
+	/*
+	 * It seems there is no way to reload station address and
+	 * it is supposed to be set by BIOS.
+	 */
+	mid = CSR_READ_2(sc, VTE_MID0L);
+	sc->vte_eaddr[0] = (mid >> 0) & 0xFF;
+	sc->vte_eaddr[1] = (mid >> 8) & 0xFF;
+	mid = CSR_READ_2(sc, VTE_MID0M);
+	sc->vte_eaddr[2] = (mid >> 0) & 0xFF;
+	sc->vte_eaddr[3] = (mid >> 8) & 0xFF;
+	mid = CSR_READ_2(sc, VTE_MID0H);
+	sc->vte_eaddr[4] = (mid >> 0) & 0xFF;
+	sc->vte_eaddr[5] = (mid >> 8) & 0xFF;
+}
+
+static int
+vte_attach(device_t dev)
+{
+	struct vte_softc *sc;
+	struct ifnet *ifp;
+	uint16_t macid;
+	int error, rid;
+
+	error = 0;
+	sc = device_get_softc(dev);
+	sc->vte_dev = dev;
+
+	mtx_init(&sc->vte_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
+	    MTX_DEF);
+	callout_init_mtx(&sc->vte_tick_ch, &sc->vte_mtx, 0);
+	sc->vte_ident = vte_find_ident(dev);
+
+	/* Map the device. */
+	pci_enable_busmaster(dev);
+	sc->vte_res_id = PCIR_BAR(1);
+	sc->vte_res_type = SYS_RES_MEMORY;
+	sc->vte_res = bus_alloc_resource_any(dev, sc->vte_res_type,
+	    &sc->vte_res_id, RF_ACTIVE);
+	if (sc->vte_res == NULL) {
+		sc->vte_res_id = PCIR_BAR(0);
+		sc->vte_res_type = SYS_RES_IOPORT;
+		sc->vte_res = bus_alloc_resource_any(dev, sc->vte_res_type,
+		    &sc->vte_res_id, RF_ACTIVE);
+		if (sc->vte_res == NULL) {
+			device_printf(dev, "cannot map memory/ports.\n");
+			mtx_destroy(&sc->vte_mtx);
+			return (ENXIO);
+		}
+	}
+	if (bootverbose) {
+		device_printf(dev, "using %s space register mapping\n",
+		    sc->vte_res_type == SYS_RES_MEMORY ? "memory" : "I/O");
+		device_printf(dev, "MAC Identifier : 0x%04x\n",
+		    CSR_READ_2(sc, VTE_MACID));
+		macid = CSR_READ_2(sc, VTE_MACID_REV);
+		device_printf(dev, "MAC Id. 0x%02x, Rev. 0x%02x\n",
+		    (macid & VTE_MACID_MASK) >> VTE_MACID_SHIFT,
+		    (macid & VTE_MACID_REV_MASK) >> VTE_MACID_REV_SHIFT);
+	}
+
+	rid = 0;
+	sc->vte_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_SHAREABLE | RF_ACTIVE);
+	if (sc->vte_irq == NULL) {
+		device_printf(dev, "cannot allocate IRQ resources.\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+	/* Reset the ethernet controller. */
+	vte_reset(sc);
+
+	if ((error = vte_dma_alloc(sc) != 0))
+		goto fail;
+
+	/* Create device sysctl node. */
+	vte_sysctl_node(sc);
+
+	/* Load station address. */
+	vte_get_macaddr(sc);
+
+	ifp = sc->vte_ifp = if_alloc(IFT_ETHER);
+	if (ifp == NULL) {
+		device_printf(dev, "cannot allocate ifnet structure.\n");
+		error = ENXIO;
+		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 = vte_ioctl;
+	ifp->if_start = vte_start;
+	ifp->if_init = vte_init;
+	ifp->if_snd.ifq_drv_maxlen = VTE_TX_RING_CNT - 1;
+	IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen);
+	IFQ_SET_READY(&ifp->if_snd);
+
+	/*
+	 * Set up MII bus.
+	 * BIOS would have initialized VTE_MPSCCR to catch PHY
+	 * status changes so driver may be able to extract
+	 * configured PHY address.  Since it's common to see BIOS
+	 * fails to initialize the register(including the sample
+	 * board I have), let mii(4) probe it.  This is more
+	 * reliable than relying on BIOS's initialization.
+	 *
+	 * Advertising flow control capability to mii(4) was
+	 * intentionally disabled due to severe problems in TX
+	 * pause frame generation.  See vte_rxeof() for more
+	 * details.
+	 */
+	error = mii_attach(dev, &sc->vte_miibus, ifp, vte_mediachange,
+	    vte_mediastatus, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0);
+	if (error != 0) {
+		device_printf(dev, "attaching PHYs failed\n");
+		goto fail;
+	}
+
+	ether_ifattach(ifp, sc->vte_eaddr);
+
+	/* VLAN capability setup. */
+	ifp->if_capabilities |= IFCAP_VLAN_MTU;
+	ifp->if_capenable = ifp->if_capabilities;
+	/* Tell the upper layer we support VLAN over-sized frames. */
+	ifp->if_hdrlen = sizeof(struct ether_vlan_header);
+
+	error = bus_setup_intr(dev, sc->vte_irq, INTR_TYPE_NET | INTR_MPSAFE,
+	    NULL, vte_intr, sc, &sc->vte_intrhand);
+	if (error != 0) {
+		device_printf(dev, "could not set up interrupt handler.\n");
+		ether_ifdetach(ifp);
+		goto fail;
+	}
+
+fail:
+	if (error != 0)
+		vte_detach(dev);
+
+	return (error);
+}
+
+static int
+vte_detach(device_t dev)
+{
+	struct vte_softc *sc;
+	struct ifnet *ifp;
+
+	sc = device_get_softc(dev);
+
+	ifp = sc->vte_ifp;
+	if (device_is_attached(dev)) {
+		VTE_LOCK(sc);
+		vte_stop(sc);
+		VTE_UNLOCK(sc);
+		callout_drain(&sc->vte_tick_ch);
+		ether_ifdetach(ifp);
+	}
+
+	if (sc->vte_miibus != NULL) {
+		device_delete_child(dev, sc->vte_miibus);
+		sc->vte_miibus = NULL;
+	}
+	bus_generic_detach(dev);
+
+	if (sc->vte_intrhand != NULL) {
+		bus_teardown_intr(dev, sc->vte_irq, sc->vte_intrhand);
+		sc->vte_intrhand = NULL;
+	}
+	if (sc->vte_irq != NULL) {
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vte_irq);
+		sc->vte_irq = NULL;
+	}
+	if (sc->vte_res != NULL) {
+		bus_release_resource(dev, sc->vte_res_type, sc->vte_res_id,
+		    sc->vte_res);
+		sc->vte_res = NULL;
+	}
+	if (ifp != NULL) {
+		if_free(ifp);
+		sc->vte_ifp = NULL;
+	}
+	vte_dma_free(sc);
+	mtx_destroy(&sc->vte_mtx);
+
+	return (0);
+}
+
+#define	VTE_SYSCTL_STAT_ADD32(c, h, n, p, d)	\
+	    SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d)
+
+static void
+vte_sysctl_node(struct vte_softc *sc)
+{
+	struct sysctl_ctx_list *ctx;
+	struct sysctl_oid_list *child, *parent;
+	struct sysctl_oid *tree;
+	struct vte_hw_stats *stats;
+	int error;
+
+	stats = &sc->vte_stats;
+	ctx = device_get_sysctl_ctx(sc->vte_dev);
+	child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->vte_dev));
+
+	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "int_rx_mod",
+	    CTLTYPE_INT | CTLFLAG_RW, &sc->vte_int_rx_mod, 0,
+	    sysctl_hw_vte_int_mod, "I", "vte RX interrupt moderation");
+	SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "int_tx_mod",
+	    CTLTYPE_INT | CTLFLAG_RW, &sc->vte_int_tx_mod, 0,
+	    sysctl_hw_vte_int_mod, "I", "vte TX interrupt moderation");
+	/* Pull in device tunables. */
+	sc->vte_int_rx_mod = VTE_IM_RX_BUNDLE_DEFAULT;
+	error = resource_int_value(device_get_name(sc->vte_dev),
+	    device_get_unit(sc->vte_dev), "int_rx_mod", &sc->vte_int_rx_mod);
+	if (error == 0) {
+		if (sc->vte_int_rx_mod < VTE_IM_BUNDLE_MIN ||
+		    sc->vte_int_rx_mod > VTE_IM_BUNDLE_MAX) {
+			device_printf(sc->vte_dev, "int_rx_mod value out of "
+			    "range; using default: %d\n",
+			    VTE_IM_RX_BUNDLE_DEFAULT);
+			sc->vte_int_rx_mod = VTE_IM_RX_BUNDLE_DEFAULT;
+		}
+	}
+
+	sc->vte_int_tx_mod = VTE_IM_TX_BUNDLE_DEFAULT;
+	error = resource_int_value(device_get_name(sc->vte_dev),
+	    device_get_unit(sc->vte_dev), "int_tx_mod", &sc->vte_int_tx_mod);
+	if (error == 0) {
+		if (sc->vte_int_tx_mod < VTE_IM_BUNDLE_MIN ||
+		    sc->vte_int_tx_mod > VTE_IM_BUNDLE_MAX) {
+			device_printf(sc->vte_dev, "int_tx_mod value out of "
+			    "range; using default: %d\n",
+			    VTE_IM_TX_BUNDLE_DEFAULT);
+			sc->vte_int_tx_mod = VTE_IM_TX_BUNDLE_DEFAULT;
+		}
+	}
+
+	tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD,
+	    NULL, "VTE statistics");
+	parent = SYSCTL_CHILDREN(tree);
+
+	/* RX statistics. */
+	tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx", CTLFLAG_RD,
+	    NULL, "RX MAC statistics");
+	child = SYSCTL_CHILDREN(tree);
+	VTE_SYSCTL_STAT_ADD32(ctx, child, "good_frames",
+	    &stats->rx_frames, "Good frames");
+	VTE_SYSCTL_STAT_ADD32(ctx, child, "good_bcast_frames",
+	    &stats->rx_bcast_frames, "Good broadcast frames");
+	VTE_SYSCTL_STAT_ADD32(ctx, child, "good_mcast_frames",
+	    &stats->rx_mcast_frames, "Good multicast frames");
+	VTE_SYSCTL_STAT_ADD32(ctx, child, "runt",
+	    &stats->rx_runts, "Too short frames");
+	VTE_SYSCTL_STAT_ADD32(ctx, child, "crc_errs",
+	    &stats->rx_crcerrs, "CRC errors");
+	VTE_SYSCTL_STAT_ADD32(ctx, child, "long_frames",
+	    &stats->rx_long_frames,
+	    "Frames that have longer length than maximum packet length");
+	VTE_SYSCTL_STAT_ADD32(ctx, child, "fifo_full",
+	    &stats->rx_fifo_full, "FIFO full");
+	VTE_SYSCTL_STAT_ADD32(ctx, child, "desc_unavail",
+	    &stats->rx_desc_unavail, "Descriptor unavailable frames");
+	VTE_SYSCTL_STAT_ADD32(ctx, child, "pause_frames",
+	    &stats->rx_pause_frames, "Pause control frames");
+
+	/* TX statistics. */
+	tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", CTLFLAG_RD,
+	    NULL, "TX MAC statistics");
+	child = SYSCTL_CHILDREN(tree);
+	VTE_SYSCTL_STAT_ADD32(ctx, child, "good_frames",
+	    &stats->tx_frames, "Good frames");
+	VTE_SYSCTL_STAT_ADD32(ctx, child, "underruns",
+	    &stats->tx_underruns, "FIFO underruns");
+	VTE_SYSCTL_STAT_ADD32(ctx, child, "late_colls",
+	    &stats->tx_late_colls, "Late collisions");
+	VTE_SYSCTL_STAT_ADD32(ctx, child, "pause_frames",
+	    &stats->tx_pause_frames, "Pause control frames");
+}
+
+#undef VTE_SYSCTL_STAT_ADD32
+
+struct vte_dmamap_arg {
+	bus_addr_t	vte_busaddr;
+};
+
+static void
+vte_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+	struct vte_dmamap_arg *ctx;
+
+	if (error != 0)
+		return;
+
+	KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
+
+	ctx = (struct vte_dmamap_arg *)arg;
+	ctx->vte_busaddr = segs[0].ds_addr;
+}
+
+static int
+vte_dma_alloc(struct vte_softc *sc)
+{
+	struct vte_txdesc *txd;
+	struct vte_rxdesc *rxd;
+	struct vte_dmamap_arg ctx;
+	int error, i;
+
+	/* Create parent DMA tag. */
+	error = bus_dma_tag_create(
+	    bus_get_dma_tag(sc->vte_dev), /* parent */
+	    1, 0,			/* alignment, boundary */
+	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
+	    BUS_SPACE_MAXADDR,		/* highaddr */
+	    NULL, NULL,			/* filter, filterarg */
+	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsize */
+	    0,				/* nsegments */
+	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
+	    0,				/* flags */
+	    NULL, NULL,			/* lockfunc, lockarg */
+	    &sc->vte_cdata.vte_parent_tag);
+	if (error != 0) {
+		device_printf(sc->vte_dev,
+		    "could not create parent DMA tag.\n");
+		goto fail;
+	}
+
+	/* Create DMA tag for TX descriptor ring. */
+	error = bus_dma_tag_create(
+	    sc->vte_cdata.vte_parent_tag, /* parent */
+	    VTE_TX_RING_ALIGN, 0,	/* alignment, boundary */
+	    BUS_SPACE_MAXADDR,		/* lowaddr */
+	    BUS_SPACE_MAXADDR,		/* highaddr */
+	    NULL, NULL,			/* filter, filterarg */
+	    VTE_TX_RING_SZ,		/* maxsize */
+	    1,				/* nsegments */
+	    VTE_TX_RING_SZ,		/* maxsegsize */
+	    0,				/* flags */
+	    NULL, NULL,			/* lockfunc, lockarg */
+	    &sc->vte_cdata.vte_tx_ring_tag);
+	if (error != 0) {
+		device_printf(sc->vte_dev,
+		    "could not create TX ring DMA tag.\n");
+		goto fail;
+	}
+
+	/* Create DMA tag for RX free descriptor ring. */
+	error = bus_dma_tag_create(
+	    sc->vte_cdata.vte_parent_tag, /* parent */
+	    VTE_RX_RING_ALIGN, 0,	/* alignment, boundary */
+	    BUS_SPACE_MAXADDR,		/* lowaddr */
+	    BUS_SPACE_MAXADDR,		/* highaddr */
+	    NULL, NULL,			/* filter, filterarg */
+	    VTE_RX_RING_SZ,		/* maxsize */
+	    1,				/* nsegments */
+	    VTE_RX_RING_SZ,		/* maxsegsize */
+	    0,				/* flags */
+	    NULL, NULL,			/* lockfunc, lockarg */
+	    &sc->vte_cdata.vte_rx_ring_tag);
+	if (error != 0) {
+		device_printf(sc->vte_dev,
+		    "could not create RX ring DMA tag.\n");
+		goto fail;
+	}
+
+	/* Allocate DMA'able memory and load the DMA map for TX ring. */
+	error = bus_dmamem_alloc(sc->vte_cdata.vte_tx_ring_tag,
+	    (void **)&sc->vte_cdata.vte_tx_ring,
+	    BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT,
+	    &sc->vte_cdata.vte_tx_ring_map);
+	if (error != 0) {
+		device_printf(sc->vte_dev,
+		    "could not allocate DMA'able memory for TX ring.\n");
+		goto fail;
+	}
+	ctx.vte_busaddr = 0;
+	error = bus_dmamap_load(sc->vte_cdata.vte_tx_ring_tag,
+	    sc->vte_cdata.vte_tx_ring_map, sc->vte_cdata.vte_tx_ring,
+	    VTE_TX_RING_SZ, vte_dmamap_cb, &ctx, 0);
+	if (error != 0 || ctx.vte_busaddr == 0) {
+		device_printf(sc->vte_dev,
+		    "could not load DMA'able memory for TX ring.\n");
+		goto fail;
+	}
+	sc->vte_cdata.vte_tx_ring_paddr = ctx.vte_busaddr;
+
+	/* Allocate DMA'able memory and load the DMA map for RX ring. */
+	error = bus_dmamem_alloc(sc->vte_cdata.vte_rx_ring_tag,
+	    (void **)&sc->vte_cdata.vte_rx_ring,
+	    BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT,
+	    &sc->vte_cdata.vte_rx_ring_map);
+	if (error != 0) {
+		device_printf(sc->vte_dev,
+		    "could not allocate DMA'able memory for RX ring.\n");
+		goto fail;
+	}
+	ctx.vte_busaddr = 0;
+	error = bus_dmamap_load(sc->vte_cdata.vte_rx_ring_tag,
+	    sc->vte_cdata.vte_rx_ring_map, sc->vte_cdata.vte_rx_ring,
+	    VTE_RX_RING_SZ, vte_dmamap_cb, &ctx, 0);
+	if (error != 0 || ctx.vte_busaddr == 0) {
+		device_printf(sc->vte_dev,
+		    "could not load DMA'able memory for RX ring.\n");
+		goto fail;
+	}
+	sc->vte_cdata.vte_rx_ring_paddr = ctx.vte_busaddr;
+
+	/* Create TX buffer parent tag. */
+	error = bus_dma_tag_create(
+	    bus_get_dma_tag(sc->vte_dev), /* parent */
+	    1, 0,			/* alignment, boundary */
+	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
+	    BUS_SPACE_MAXADDR,		/* highaddr */
+	    NULL, NULL,			/* filter, filterarg */
+	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsize */
+	    0,				/* nsegments */
+	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
+	    0,				/* flags */
+	    NULL, NULL,			/* lockfunc, lockarg */
+	    &sc->vte_cdata.vte_buffer_tag);
+	if (error != 0) {
+		device_printf(sc->vte_dev,
+		    "could not create parent buffer DMA tag.\n");
+		goto fail;
+	}
+
+	/* Create DMA tag for TX buffers. */
+	error = bus_dma_tag_create(
+	    sc->vte_cdata.vte_buffer_tag, /* parent */
+	    1, 0,			/* alignment, boundary */
+	    BUS_SPACE_MAXADDR,		/* lowaddr */
+	    BUS_SPACE_MAXADDR,		/* highaddr */
+	    NULL, NULL,			/* filter, filterarg */
+	    MCLBYTES,			/* maxsize */
+	    1,				/* nsegments */
+	    MCLBYTES,			/* maxsegsize */
+	    0,				/* flags */
+	    NULL, NULL,			/* lockfunc, lockarg */
+	    &sc->vte_cdata.vte_tx_tag);
+	if (error != 0) {
+		device_printf(sc->vte_dev, "could not create TX DMA tag.\n");
+		goto fail;
+	}
+
+	/* Create DMA tag for RX buffers. */
+	error = bus_dma_tag_create(
+	    sc->vte_cdata.vte_buffer_tag, /* parent */
+	    VTE_RX_BUF_ALIGN, 0,	/* alignment, boundary */
+	    BUS_SPACE_MAXADDR,		/* lowaddr */
+	    BUS_SPACE_MAXADDR,		/* highaddr */
+	    NULL, NULL,			/* filter, filterarg */
+	    MCLBYTES,			/* maxsize */
+	    1,				/* nsegments */
+	    MCLBYTES,			/* maxsegsize */
+	    0,				/* flags */
+	    NULL, NULL,			/* lockfunc, lockarg */
+	    &sc->vte_cdata.vte_rx_tag);
+	if (error != 0) {
+		device_printf(sc->vte_dev, "could not create RX DMA tag.\n");
+		goto fail;
+	}
+	/* Create DMA maps for TX buffers. */
+	for (i = 0; i < VTE_TX_RING_CNT; i++) {
+		txd = &sc->vte_cdata.vte_txdesc[i];
+		txd->tx_m = NULL;
+		txd->tx_dmamap = NULL;
+		error = bus_dmamap_create(sc->vte_cdata.vte_tx_tag, 0,
+		    &txd->tx_dmamap);
+		if (error != 0) {
+			device_printf(sc->vte_dev,
+			    "could not create TX dmamap.\n");
+			goto fail;
+		}
+	}
+	/* Create DMA maps for RX buffers. */
+	if ((error = bus_dmamap_create(sc->vte_cdata.vte_rx_tag, 0,
+	    &sc->vte_cdata.vte_rx_sparemap)) != 0) {
+		device_printf(sc->vte_dev,
+		    "could not create spare RX dmamap.\n");
+		goto fail;
+	}
+	for (i = 0; i < VTE_RX_RING_CNT; i++) {
+		rxd = &sc->vte_cdata.vte_rxdesc[i];
+		rxd->rx_m = NULL;
+		rxd->rx_dmamap = NULL;
+		error = bus_dmamap_create(sc->vte_cdata.vte_rx_tag, 0,
+		    &rxd->rx_dmamap);
+		if (error != 0) {
+			device_printf(sc->vte_dev,
+			    "could not create RX dmamap.\n");
+			goto fail;
+		}
+	}
+
+fail:
+	return (error);
+}
+
+static void
+vte_dma_free(struct vte_softc *sc)
+{
+	struct vte_txdesc *txd;
+	struct vte_rxdesc *rxd;
+	int i;
+
+	/* TX buffers. */
+	if (sc->vte_cdata.vte_tx_tag != NULL) {
+		for (i = 0; i < VTE_TX_RING_CNT; i++) {
+			txd = &sc->vte_cdata.vte_txdesc[i];
+			if (txd->tx_dmamap != NULL) {
+				bus_dmamap_destroy(sc->vte_cdata.vte_tx_tag,
+				    txd->tx_dmamap);
+				txd->tx_dmamap = NULL;
+			}
+		}
+		bus_dma_tag_destroy(sc->vte_cdata.vte_tx_tag);
+		sc->vte_cdata.vte_tx_tag = NULL;
+	}
+	/* RX buffers */
+	if (sc->vte_cdata.vte_rx_tag != NULL) {
+		for (i = 0; i < VTE_RX_RING_CNT; i++) {
+			rxd = &sc->vte_cdata.vte_rxdesc[i];
+			if (rxd->rx_dmamap != NULL) {
+				bus_dmamap_destroy(sc->vte_cdata.vte_rx_tag,
+				    rxd->rx_dmamap);
+				rxd->rx_dmamap = NULL;
+			}
+		}
+		if (sc->vte_cdata.vte_rx_sparemap != NULL) {
+			bus_dmamap_destroy(sc->vte_cdata.vte_rx_tag,
+			    sc->vte_cdata.vte_rx_sparemap);
+			sc->vte_cdata.vte_rx_sparemap = NULL;
+		}
+		bus_dma_tag_destroy(sc->vte_cdata.vte_rx_tag);
+		sc->vte_cdata.vte_rx_tag = NULL;
+	}
+	/* TX descriptor ring. */
+	if (sc->vte_cdata.vte_tx_ring_tag != NULL) {
+		if (sc->vte_cdata.vte_tx_ring_map != NULL)
+			bus_dmamap_unload(sc->vte_cdata.vte_tx_ring_tag,
+			    sc->vte_cdata.vte_tx_ring_map);
+		if (sc->vte_cdata.vte_tx_ring_map != NULL &&
+		    sc->vte_cdata.vte_tx_ring != NULL)
+			bus_dmamem_free(sc->vte_cdata.vte_tx_ring_tag,
+			    sc->vte_cdata.vte_tx_ring,
+			    sc->vte_cdata.vte_tx_ring_map);
+		sc->vte_cdata.vte_tx_ring = NULL;
+		sc->vte_cdata.vte_tx_ring_map = NULL;
+		bus_dma_tag_destroy(sc->vte_cdata.vte_tx_ring_tag);
+		sc->vte_cdata.vte_tx_ring_tag = NULL;
+	}
+	/* RX ring. */
+	if (sc->vte_cdata.vte_rx_ring_tag != NULL) {
+		if (sc->vte_cdata.vte_rx_ring_map != NULL)
+			bus_dmamap_unload(sc->vte_cdata.vte_rx_ring_tag,
+			    sc->vte_cdata.vte_rx_ring_map);
+		if (sc->vte_cdata.vte_rx_ring_map != NULL &&
+		    sc->vte_cdata.vte_rx_ring != NULL)
+			bus_dmamem_free(sc->vte_cdata.vte_rx_ring_tag,
+			    sc->vte_cdata.vte_rx_ring,
+			    sc->vte_cdata.vte_rx_ring_map);
+		sc->vte_cdata.vte_rx_ring = NULL;
+		sc->vte_cdata.vte_rx_ring_map = NULL;
+		bus_dma_tag_destroy(sc->vte_cdata.vte_rx_ring_tag);
+		sc->vte_cdata.vte_rx_ring_tag = NULL;
+	}
+	if (sc->vte_cdata.vte_buffer_tag != NULL) {
+		bus_dma_tag_destroy(sc->vte_cdata.vte_buffer_tag);
+		sc->vte_cdata.vte_buffer_tag = NULL;
+	}
+	if (sc->vte_cdata.vte_parent_tag != NULL) {
+		bus_dma_tag_destroy(sc->vte_cdata.vte_parent_tag);
+		sc->vte_cdata.vte_parent_tag = NULL;
+	}
+}
+
+static int
+vte_shutdown(device_t dev)
+{
+
+	return (vte_suspend(dev));
+}
+
+static int
+vte_suspend(device_t dev)
+{
+	struct vte_softc *sc;
+	struct ifnet *ifp;
+
+	sc = device_get_softc(dev);
+
+	VTE_LOCK(sc);
+	ifp = sc->vte_ifp;
+	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
+		vte_stop(sc);
+	VTE_UNLOCK(sc);
+

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


More information about the svn-src-head mailing list