svn commit: r223927 - in head/sys: conf dev/rt

Aleksandr Rybalko ray at FreeBSD.org
Mon Jul 11 08:24:00 UTC 2011


Author: ray
Date: Mon Jul 11 08:23:59 2011
New Revision: 223927
URL: http://svn.freebsd.org/changeset/base/223927

Log:
  Support of Ralink Ethernet MAC, used in RT3050F/RT3052F and I belive in other Ralink SoCs.
  
  Approved by: adrian (mentor)

Added:
  head/sys/dev/rt/
  head/sys/dev/rt/if_rt.c   (contents, props changed)
  head/sys/dev/rt/if_rtreg.h   (contents, props changed)
  head/sys/dev/rt/if_rtvar.h   (contents, props changed)
Modified:
  head/sys/conf/files.mips
  head/sys/conf/options.mips

Modified: head/sys/conf/files.mips
==============================================================================
--- head/sys/conf/files.mips	Mon Jul 11 05:57:49 2011	(r223926)
+++ head/sys/conf/files.mips	Mon Jul 11 08:23:59 2011	(r223927)
@@ -106,4 +106,5 @@ dev/siba/siba_pcib.c		optional	siba pci
 dev/hwpmc/hwpmc_mips.c		optional hwpmc
 dev/hwpmc/hwpmc_mips24k.c	optional hwpmc
 
+dev/rt/if_rt.c			optional 	rt
 dev/nvram2env/nvram2env.c	optional	nvram2env

Modified: head/sys/conf/options.mips
==============================================================================
--- head/sys/conf/options.mips	Mon Jul 11 05:57:49 2011	(r223926)
+++ head/sys/conf/options.mips	Mon Jul 11 08:23:59 2011	(r223927)
@@ -70,3 +70,11 @@ OCTEON_BOARD_CAPK_0100ND	opt_cvmx.h
 # Options that control the Atheros SoC peripherals
 #
 ARGE_DEBUG			opt_global.h
+
+#
+# Options that control the Ralink RT305xF Etherenet MAC.
+#
+IF_RT_DEBUG			opt_if_rt.h
+IF_RT_PHY_SUPPORT		opt_if_rt.h
+IF_RT_RING_DATA_COUNT		opt_if_rt.h
+

Added: head/sys/dev/rt/if_rt.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/rt/if_rt.c	Mon Jul 11 08:23:59 2011	(r223927)
@@ -0,0 +1,2616 @@
+/*-
+ * Copyright (c) 2011, Aleksandr Rybalko
+ * based on hard work
+ * by Alexander Egorenkov <egorenar at gmail.com>
+ * and by Damien Bergamini <damien.bergamini at free.fr>
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "if_rtvar.h"
+#include "if_rtreg.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 <net/bpf.h>
+
+#include <machine/bus.h>
+#include <machine/cache.h>
+#include <machine/cpufunc.h>
+#include <machine/resource.h>
+#include <vm/vm_param.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <machine/pmap.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <mips/rt305x/rt305x_sysctlvar.h>
+#include <mips/rt305x/rt305xreg.h>
+
+#ifdef IF_RT_PHY_SUPPORT
+#include "miibus_if.h"
+#endif
+
+/*
+ * Defines and macros
+ */
+#define	RT_MAX_AGG_SIZE			3840
+
+#define	RT_TX_DATA_SEG0_SIZE		MJUMPAGESIZE
+
+#define	RT_MS(_v, _f)			(((_v) & _f) >> _f##_S)
+#define	RT_SM(_v, _f)			(((_v) << _f##_S) & _f)
+
+#define	RT_TX_WATCHDOG_TIMEOUT		5
+
+/*
+ * Static function prototypes
+ */
+static int	rt_probe(device_t dev);
+static int	rt_attach(device_t dev);
+static int	rt_detach(device_t dev);
+static int	rt_shutdown(device_t dev);
+static int	rt_suspend(device_t dev);
+static int	rt_resume(device_t dev);
+static void	rt_init_locked(void *priv);
+static void	rt_init(void *priv);
+static void	rt_stop_locked(void *priv);
+static void	rt_stop(void *priv);
+static void	rt_start(struct ifnet *ifp);
+static int	rt_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
+static void	rt_periodic(void *arg);
+static void	rt_tx_watchdog(void *arg);
+static void	rt_intr(void *arg);
+static void	rt_tx_coherent_intr(struct rt_softc *sc);
+static void	rt_rx_coherent_intr(struct rt_softc *sc);
+static void	rt_rx_delay_intr(struct rt_softc *sc);
+static void	rt_tx_delay_intr(struct rt_softc *sc);
+static void	rt_rx_intr(struct rt_softc *sc);
+static void	rt_tx_intr(struct rt_softc *sc, int qid);
+static void	rt_rx_done_task(void *context, int pending);
+static void	rt_tx_done_task(void *context, int pending);
+static void	rt_periodic_task(void *context, int pending);
+static int	rt_rx_eof(struct rt_softc *sc, int limit);
+static void	rt_tx_eof(struct rt_softc *sc,
+		    struct rt_softc_tx_ring *ring);
+static void	rt_update_stats(struct rt_softc *sc);
+static void	rt_watchdog(struct rt_softc *sc);
+static void	rt_update_raw_counters(struct rt_softc *sc);
+static void	rt_intr_enable(struct rt_softc *sc, uint32_t intr_mask);
+static void	rt_intr_disable(struct rt_softc *sc, uint32_t intr_mask);
+static int	rt_txrx_enable(struct rt_softc *sc);
+static int	rt_alloc_rx_ring(struct rt_softc *sc,
+		    struct rt_softc_rx_ring *ring);
+static void	rt_reset_rx_ring(struct rt_softc *sc,
+		    struct rt_softc_rx_ring *ring);
+static void	rt_free_rx_ring(struct rt_softc *sc,
+		    struct rt_softc_rx_ring *ring);
+static int	rt_alloc_tx_ring(struct rt_softc *sc,
+		    struct rt_softc_tx_ring *ring, int qid);
+static void	rt_reset_tx_ring(struct rt_softc *sc,
+		    struct rt_softc_tx_ring *ring);
+static void	rt_free_tx_ring(struct rt_softc *sc,
+		    struct rt_softc_tx_ring *ring);
+static void	rt_dma_map_addr(void *arg, bus_dma_segment_t *segs,
+		    int nseg, int error);
+static void	rt_sysctl_attach(struct rt_softc *sc);
+#ifdef IF_RT_PHY_SUPPORT
+void		rt_miibus_statchg(device_t);
+static int	rt_miibus_readreg(device_t, int, int);
+static int	rt_miibus_writereg(device_t, int, int, int);
+#endif
+static int	rt_ifmedia_upd(struct ifnet *);
+static void	rt_ifmedia_sts(struct ifnet *, struct ifmediareq *);
+
+SYSCTL_NODE(_hw, OID_AUTO, rt, CTLFLAG_RD, 0, "RT driver parameters");
+#ifdef IF_RT_DEBUG
+static int rt_debug = 0;
+SYSCTL_INT(_hw_rt, OID_AUTO, debug, CTLFLAG_RW, &rt_debug, 0,
+    "RT debug level");
+TUNABLE_INT("hw.rt.debug", &rt_debug);
+#endif
+
+static int
+rt_probe(device_t dev)
+{
+	device_set_desc(dev, "Ralink RT305XF onChip Ethernet MAC");
+	return (0);
+}
+
+/*
+ * macaddr_atoi - translate string MAC address to uint8_t array
+ */
+static int
+macaddr_atoi(const char *str, uint8_t *mac)
+{
+	int count, i;
+	unsigned int amac[ETHER_ADDR_LEN];	/* Aligned version */
+
+	count = sscanf(str, "%x%*c%x%*c%x%*c%x%*c%x%*c%x",
+	    &amac[0], &amac[1], &amac[2],
+	    &amac[3], &amac[4], &amac[5]);
+	if (count < ETHER_ADDR_LEN) {
+		memset(mac, 0, ETHER_ADDR_LEN);
+		return (1);
+	}
+
+	/* Copy aligned to result */
+	for (i = 0; i < ETHER_ADDR_LEN; i ++)
+		mac[i] = (amac[i] & 0xff);
+
+	return (0);
+}
+
+#ifdef USE_GENERATED_MAC_ADDRESS
+static char *
+kernenv_next(char *cp)
+{
+
+	if (cp != NULL) {
+		while (*cp != 0)
+			cp++;
+		cp++;
+		if (*cp == 0)
+			cp = NULL;
+	}
+	return (cp);
+}
+
+/*
+ * generate_mac(uin8_t *mac)
+ * This is MAC address generator for cases when real device MAC address
+ * unknown or not yet accessible.
+ * Use 'b','s','d' signature and 3 octets from CRC32 on kenv.
+ * MAC = 'b', 's', 'd', CRC[3]^CRC[2], CRC[1], CRC[0]
+ *
+ * Output - MAC address, that do not change between reboots, if hints or
+ * bootloader info unchange.
+ */
+static void
+generate_mac(uint8_t *mac)
+{
+	unsigned char *cp;
+	int i = 0;
+	uint32_t crc = 0xffffffff;
+
+	/* Generate CRC32 on kenv */
+	if (dynamic_kenv) {
+		for (cp = kenvp[0]; cp != NULL; cp = kenvp[++i]) {
+			crc = calculate_crc32c(crc, cp, strlen(cp) + 1);
+		}
+	} else {
+		for (cp = kern_envp; cp != NULL; cp = kernenv_next(cp)) {
+			crc = calculate_crc32c(crc, cp, strlen(cp) + 1);
+		}
+	}
+	crc = ~crc;
+
+	mac[0] = 'b';
+	mac[1] = 's';
+	mac[2] = 'd';
+	mac[3] = (crc >> 24) ^ ((crc >> 16) & 0xff);
+	mac[4] = (crc >> 8) & 0xff;
+	mac[5] = crc & 0xff;
+}
+#endif
+
+/*
+ * ether_request_mac - try to find usable MAC address.
+ */
+static int
+ether_request_mac(device_t dev, uint8_t *mac)
+{
+	char *var;
+
+	/*
+	 * "ethaddr" is passed via envp on RedBoot platforms
+	 * "kmac" is passed via argv on RouterBOOT platforms
+	 */
+#if defined(__U_BOOT__) ||  defined(__REDBOOT__) || defined(__ROUTERBOOT__)
+	if ((var = getenv("ethaddr")) != NULL ||
+	    (var = getenv("kmac")) != NULL ) {
+
+		if(!macaddr_atoi(var, mac)) {
+			printf("%s: use %s macaddr from KENV\n",
+			    device_get_nameunit(dev), var);
+			freeenv(var);
+			return (0);
+		}
+		freeenv(var);
+	}
+#endif
+
+	/*
+	 * Try from hints
+	 * hint.[dev].[unit].macaddr
+	 */
+	if (!resource_string_value(device_get_name(dev),
+	    device_get_unit(dev), "macaddr", (const char **)&var)) {
+
+		if(!macaddr_atoi(var, mac)) {
+			printf("%s: use %s macaddr from hints\n",
+			    device_get_nameunit(dev), var);
+			return (0);
+		}
+	}
+
+#ifdef USE_GENERATED_MAC_ADDRESS
+	generate_mac(mac);
+
+	device_printf(dev, "use generated %02x:%02x:%02x:%02x:%02x:%02x "
+	    "macaddr\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+#else
+	/* Hardcoded */
+	mac[0] = 0x00;
+	mac[1] = 0x18;
+	mac[2] = 0xe7;
+	mac[3] = 0xd5;
+	mac[4] = 0x83;
+	mac[5] = 0x90;
+
+	device_printf(dev, "use hardcoded 00:18:e7:d5:83:90 macaddr\n");
+#endif
+
+	return (0);
+}
+
+static int
+rt_attach(device_t dev)
+{
+	struct rt_softc *sc;
+	struct ifnet *ifp;
+	int error, i;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+
+	mtx_init(&sc->lock, device_get_nameunit(dev), MTX_NETWORK_LOCK,
+	    MTX_DEF | MTX_RECURSE);
+
+	sc->mem_rid = 0;
+	sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->mem_rid,
+	    RF_ACTIVE);
+	if (sc->mem == NULL) {
+		device_printf(dev, "could not allocate memory resource\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+	sc->bst = rman_get_bustag(sc->mem);
+	sc->bsh = rman_get_bushandle(sc->mem);
+
+	sc->irq_rid = 0;
+	sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid,
+	    RF_ACTIVE);
+	if (sc->irq == NULL) {
+		device_printf(dev,
+		    "could not allocate interrupt resource\n");
+		error = ENXIO;
+		goto fail;
+	}
+
+#ifdef IF_RT_DEBUG
+	sc->debug = rt_debug;
+
+	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
+		SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
+		"debug", CTLFLAG_RW, &sc->debug, 0, "rt debug level");
+#endif
+
+	device_printf(dev, "RT305XF Ethernet MAC (rev 0x%08x)\n",
+	    sc->mac_rev);
+
+	/* Reset hardware */
+	RT_WRITE(sc, GE_PORT_BASE + FE_RST_GLO, PSE_RESET);
+
+	RT_WRITE(sc, GDMA1_BASE + GDMA_FWD_CFG,
+	    (
+	    GDM_ICS_EN | /* Enable IP Csum */
+	    GDM_TCS_EN | /* Enable TCP Csum */
+	    GDM_UCS_EN | /* Enable UDP Csum */
+	    GDM_STRPCRC | /* Strip CRC from packet */
+	    GDM_DST_PORT_CPU << GDM_UFRC_P_SHIFT | /* Forward UCast to CPU */
+	    GDM_DST_PORT_CPU << GDM_BFRC_P_SHIFT | /* Forward BCast to CPU */
+	    GDM_DST_PORT_CPU << GDM_MFRC_P_SHIFT | /* Forward MCast to CPU */
+	    GDM_DST_PORT_CPU << GDM_OFRC_P_SHIFT   /* Forward Other to CPU */
+	    ));
+
+	/* allocate Tx and Rx rings */
+	for (i = 0; i < RT_SOFTC_TX_RING_COUNT; i++) {
+		error = rt_alloc_tx_ring(sc, &sc->tx_ring[i], i);
+		if (error != 0) {
+			device_printf(dev, "could not allocate Tx ring #%d\n",
+			    i);
+			goto fail;
+		}
+	}
+
+	sc->tx_ring_mgtqid = 5;
+
+	error = rt_alloc_rx_ring(sc, &sc->rx_ring);
+	if (error != 0) {
+		device_printf(dev, "could not allocate Rx ring\n");
+		goto fail;
+	}
+
+	callout_init(&sc->periodic_ch, 0);
+	callout_init_mtx(&sc->tx_watchdog_ch, &sc->lock, 0);
+
+	ifp = sc->ifp = if_alloc(IFT_ETHER);
+	if (ifp == NULL) {
+		device_printf(dev, "could not if_alloc()\n");
+		error = ENOMEM;
+		goto fail;
+	}
+
+	ifp->if_softc = sc;
+	if_initname(ifp, device_get_name(sc->dev), device_get_unit(sc->dev));
+	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+	ifp->if_init = rt_init;
+	ifp->if_ioctl = rt_ioctl;
+	ifp->if_start = rt_start;
+	ifp->if_mtu = ETHERMTU;
+#define	RT_TX_QLEN	256
+
+	IFQ_SET_MAXLEN(&ifp->if_snd, RT_TX_QLEN);
+	ifp->if_snd.ifq_drv_maxlen = RT_TX_QLEN;
+	IFQ_SET_READY(&ifp->if_snd);
+
+#ifdef IF_RT_PHY_SUPPORT
+	error = mii_attach(dev, &sc->rt_miibus, ifp, rt_ifmedia_upd,
+	    rt_ifmedia_sts, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0);
+	if (error != 0) {
+		device_printf(dev, "attaching PHYs failed\n");
+		error = ENXIO;
+		goto fail;
+	}
+#else
+	ifmedia_init(&sc->rt_ifmedia, 0, rt_ifmedia_upd, rt_ifmedia_sts);
+	ifmedia_add(&sc->rt_ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX, 0,
+	    NULL);
+	ifmedia_set(&sc->rt_ifmedia, IFM_ETHER | IFM_100_TX | IFM_FDX);
+
+#endif /* IF_RT_PHY_SUPPORT */
+
+	ether_request_mac(dev, sc->mac_addr);
+	ether_ifattach(ifp, sc->mac_addr);
+
+	/*
+	 * Tell the upper layer(s) we support long frames.
+	 */
+	ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
+	ifp->if_capabilities |= IFCAP_VLAN_MTU;
+	ifp->if_capenable |= IFCAP_VLAN_MTU;
+	ifp->if_capabilities |= IFCAP_RXCSUM|IFCAP_TXCSUM;
+	ifp->if_capenable |= IFCAP_RXCSUM|IFCAP_TXCSUM;
+
+	/* init task queue */
+	TASK_INIT(&sc->rx_done_task, 0, rt_rx_done_task, sc);
+	TASK_INIT(&sc->tx_done_task, 0, rt_tx_done_task, sc);
+	TASK_INIT(&sc->periodic_task, 0, rt_periodic_task, sc);
+
+	sc->rx_process_limit = 100;
+
+	sc->taskqueue = taskqueue_create("rt_taskq", M_NOWAIT,
+	    taskqueue_thread_enqueue, &sc->taskqueue);
+
+	taskqueue_start_threads(&sc->taskqueue, 1, PI_NET, "%s taskq",
+	    device_get_nameunit(sc->dev));
+
+	rt_sysctl_attach(sc);
+
+	/* set up interrupt */
+	error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE,
+	    NULL, rt_intr, sc, &sc->irqh);
+	if (error != 0) {
+		printf("%s: could not set up interrupt\n",
+			device_get_nameunit(dev));
+		goto fail;
+	}
+#ifdef IF_RT_DEBUG
+	device_printf(dev, "debug var at %#08x\n", (u_int)&(sc->debug));
+#endif
+
+	return (0);
+
+fail:
+	/* free Tx and Rx rings */
+	for (i = 0; i < RT_SOFTC_TX_RING_COUNT; i++)
+		rt_free_tx_ring(sc, &sc->tx_ring[i]);
+
+	rt_free_rx_ring(sc, &sc->rx_ring);
+
+	mtx_destroy(&sc->lock);
+
+	if (sc->mem != NULL)
+		bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid,
+		    sc->mem);
+
+	if (sc->irq != NULL)
+		bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid,
+		    sc->irq);
+
+	return (error);
+}
+
+/*
+ * Set media options.
+ */
+static int
+rt_ifmedia_upd(struct ifnet *ifp)
+{
+	struct rt_softc *sc;
+#ifdef IF_RT_PHY_SUPPORT
+	struct mii_data *mii;
+	int error = 0;
+
+	sc = ifp->if_softc;
+	RT_SOFTC_LOCK(sc);
+
+	mii = device_get_softc(sc->rt_miibus);
+	if (mii->mii_instance) {
+		struct mii_softc *miisc;
+		for (miisc = LIST_FIRST(&mii->mii_phys); miisc != NULL;
+				miisc = LIST_NEXT(miisc, mii_list))
+			mii_phy_reset(miisc);
+	}
+	if (mii)
+		error = mii_mediachg(mii);
+	RT_SOFTC_UNLOCK(sc);
+
+	return (error);
+
+#else /* !IF_RT_PHY_SUPPORT */
+
+	struct ifmedia *ifm;
+	struct ifmedia_entry *ife;
+
+	sc = ifp->if_softc;
+	ifm = &sc->rt_ifmedia;
+	ife = ifm->ifm_cur;
+
+	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
+		return (EINVAL);
+
+	if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
+		device_printf(sc->dev,
+		    "AUTO is not supported for multiphy MAC");
+		return (EINVAL);
+	}
+
+	/*
+	 * Ignore everything
+	 */
+	return (0);
+#endif /* IF_RT_PHY_SUPPORT */
+}
+
+/*
+ * Report current media status.
+ */
+static void
+rt_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+#ifdef IF_RT_PHY_SUPPORT
+	struct rt_softc *sc;
+	struct mii_data *mii;
+
+	sc = ifp->if_softc;
+
+	RT_SOFTC_LOCK(sc);
+	mii = device_get_softc(sc->rt_miibus);
+	mii_pollstat(mii);
+	ifmr->ifm_active = mii->mii_media_active;
+	ifmr->ifm_status = mii->mii_media_status;
+	ifmr->ifm_active = IFM_ETHER | IFM_100_TX | IFM_FDX;
+	ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
+	RT_SOFTC_UNLOCK(sc);
+#else /* !IF_RT_PHY_SUPPORT */
+
+	ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
+	ifmr->ifm_active = IFM_ETHER | IFM_100_TX | IFM_FDX;
+#endif /* IF_RT_PHY_SUPPORT */
+}
+
+static int
+rt_detach(device_t dev)
+{
+	struct rt_softc *sc;
+	struct ifnet *ifp;
+	int i;
+
+	sc = device_get_softc(dev);
+	ifp = sc->ifp;
+
+	RT_DPRINTF(sc, RT_DEBUG_ANY, "detaching\n");
+
+	RT_SOFTC_LOCK(sc);
+
+	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+	callout_stop(&sc->periodic_ch);
+	callout_stop(&sc->tx_watchdog_ch);
+
+	taskqueue_drain(sc->taskqueue, &sc->rx_done_task);
+	taskqueue_drain(sc->taskqueue, &sc->tx_done_task);
+	taskqueue_drain(sc->taskqueue, &sc->periodic_task);
+
+	/* free Tx and Rx rings */
+	for (i = 0; i < RT_SOFTC_TX_RING_COUNT; i++)
+		rt_free_tx_ring(sc, &sc->tx_ring[i]);
+
+	rt_free_rx_ring(sc, &sc->rx_ring);
+
+	RT_SOFTC_UNLOCK(sc);
+
+#ifdef IF_RT_PHY_SUPPORT
+	if (sc->rt_miibus != NULL)
+		device_delete_child(dev, sc->rt_miibus);
+#endif
+
+	ether_ifdetach(ifp);
+	if_free(ifp);
+
+	taskqueue_free(sc->taskqueue);
+
+	mtx_destroy(&sc->lock);
+
+	bus_generic_detach(dev);
+	bus_teardown_intr(dev, sc->irq, sc->irqh);
+	bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid, sc->irq);
+	bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, sc->mem);
+
+	return (0);
+}
+
+static int
+rt_shutdown(device_t dev)
+{
+	struct rt_softc *sc;
+
+	sc = device_get_softc(dev);
+	RT_DPRINTF(sc, RT_DEBUG_ANY, "shutting down\n");
+	rt_stop(sc);
+
+	return (0);
+}
+
+static int
+rt_suspend(device_t dev)
+{
+	struct rt_softc *sc;
+
+	sc = device_get_softc(dev);
+	RT_DPRINTF(sc, RT_DEBUG_ANY, "suspending\n");
+	rt_stop(sc);
+
+	return (0);
+}
+
+static int
+rt_resume(device_t dev)
+{
+	struct rt_softc *sc;
+	struct ifnet *ifp;
+
+	sc = device_get_softc(dev);
+	ifp = sc->ifp;
+
+	RT_DPRINTF(sc, RT_DEBUG_ANY, "resuming\n");
+
+	if (ifp->if_flags & IFF_UP)
+		rt_init(sc);
+
+	return (0);
+}
+
+/*
+ * rt_init_locked - Run initialization process having locked mtx.
+ */
+static void
+rt_init_locked(void *priv)
+{
+	struct rt_softc *sc;
+	struct ifnet *ifp;
+#ifdef IF_RT_PHY_SUPPORT
+	struct mii_data *mii;
+#endif
+	int i, ntries;
+	uint32_t tmp;
+
+	sc = priv;
+	ifp = sc->ifp;
+#ifdef IF_RT_PHY_SUPPORT
+	mii = device_get_softc(sc->rt_miibus);
+#endif
+
+	RT_DPRINTF(sc, RT_DEBUG_ANY, "initializing\n");
+
+	RT_SOFTC_ASSERT_LOCKED(sc);
+
+	/* hardware reset */
+	RT_WRITE(sc, GE_PORT_BASE + FE_RST_GLO, PSE_RESET);
+	rt305x_sysctl_set(SYSCTL_RSTCTRL, SYSCTL_RSTCTRL_FRENG);
+
+	/* Fwd to CPU (uni|broad|multi)cast and Unknown */
+	RT_WRITE(sc, GDMA1_BASE + GDMA_FWD_CFG,
+	    (
+	    GDM_ICS_EN | /* Enable IP Csum */
+	    GDM_TCS_EN | /* Enable TCP Csum */
+	    GDM_UCS_EN | /* Enable UDP Csum */
+	    GDM_STRPCRC | /* Strip CRC from packet */
+	    GDM_DST_PORT_CPU << GDM_UFRC_P_SHIFT | /* Forward UCast to CPU */
+	    GDM_DST_PORT_CPU << GDM_BFRC_P_SHIFT | /* Forward BCast to CPU */
+	    GDM_DST_PORT_CPU << GDM_MFRC_P_SHIFT | /* Forward MCast to CPU */
+	    GDM_DST_PORT_CPU << GDM_OFRC_P_SHIFT   /* Forward Other to CPU */
+	    ));
+
+	/* disable DMA engine */
+	RT_WRITE(sc, PDMA_BASE + PDMA_GLO_CFG, 0);
+	RT_WRITE(sc, PDMA_BASE + PDMA_RST_IDX, 0xffffffff);
+
+	/* wait while DMA engine is busy */
+	for (ntries = 0; ntries < 100; ntries++) {
+		tmp = RT_READ(sc, PDMA_BASE + PDMA_GLO_CFG);
+		if (!(tmp & (FE_TX_DMA_BUSY | FE_RX_DMA_BUSY)))
+			break;
+		DELAY(1000);
+	}
+
+	if (ntries == 100) {
+		device_printf(sc->dev, "timeout waiting for DMA engine\n");
+		goto fail;
+	}
+
+	/* reset Rx and Tx rings */
+	tmp = FE_RST_DRX_IDX0 |
+		FE_RST_DTX_IDX3 |
+		FE_RST_DTX_IDX2 |
+		FE_RST_DTX_IDX1 |
+		FE_RST_DTX_IDX0;
+
+	RT_WRITE(sc, PDMA_BASE + PDMA_RST_IDX, tmp);
+
+	/* XXX switch set mac address */
+	for (i = 0; i < RT_SOFTC_TX_RING_COUNT; i++)
+		rt_reset_tx_ring(sc, &sc->tx_ring[i]);
+
+	for (i = 0; i < RT_SOFTC_TX_RING_COUNT; i++) {
+		/* update TX_BASE_PTRx */
+		RT_WRITE(sc, PDMA_BASE + TX_BASE_PTR(i),
+			sc->tx_ring[i].desc_phys_addr);
+		RT_WRITE(sc, PDMA_BASE + TX_MAX_CNT(i),
+			RT_SOFTC_TX_RING_DESC_COUNT);
+		RT_WRITE(sc, PDMA_BASE + TX_CTX_IDX(i), 0);
+	}
+
+	/* init Rx ring */
+	rt_reset_rx_ring(sc, &sc->rx_ring);
+
+	/* update RX_BASE_PTR0 */
+	RT_WRITE(sc, PDMA_BASE + RX_BASE_PTR0,
+		sc->rx_ring.desc_phys_addr);
+	RT_WRITE(sc, PDMA_BASE + RX_MAX_CNT0,
+		RT_SOFTC_RX_RING_DATA_COUNT);
+	RT_WRITE(sc, PDMA_BASE + RX_CALC_IDX0,
+		RT_SOFTC_RX_RING_DATA_COUNT - 1);
+
+	/* write back DDONE, 16byte burst enable RX/TX DMA */
+	RT_WRITE(sc, PDMA_BASE + PDMA_GLO_CFG,
+	    FE_TX_WB_DDONE | FE_DMA_BT_SIZE16 | FE_RX_DMA_EN | FE_TX_DMA_EN);
+
+	/* disable interrupts mitigation */
+	RT_WRITE(sc, PDMA_BASE + DELAY_INT_CFG, 0);
+
+	/* clear pending interrupts */
+	RT_WRITE(sc, GE_PORT_BASE + FE_INT_STATUS, 0xffffffff);
+
+	/* enable interrupts */
+	tmp = 	CNT_PPE_AF |
+		CNT_GDM_AF |
+		PSE_P2_FC |
+		GDM_CRC_DROP |
+		PSE_BUF_DROP |
+		GDM_OTHER_DROP |
+		PSE_P1_FC |
+		PSE_P0_FC |
+		PSE_FQ_EMPTY |
+		INT_TX_COHERENT |
+		INT_RX_COHERENT |
+		INT_TXQ3_DONE |
+		INT_TXQ2_DONE |
+		INT_TXQ1_DONE |
+		INT_TXQ0_DONE |
+		INT_RX_DONE;
+
+	sc->intr_enable_mask = tmp;
+
+	RT_WRITE(sc, GE_PORT_BASE + FE_INT_ENABLE, tmp);
+
+	if (rt_txrx_enable(sc) != 0)
+		goto fail;
+
+#ifdef IF_RT_PHY_SUPPORT
+	if (mii) mii_mediachg(mii);
+#endif /* IF_RT_PHY_SUPPORT */
+
+	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
+	ifp->if_drv_flags |= IFF_DRV_RUNNING;
+
+	sc->periodic_round = 0;
+
+	callout_reset(&sc->periodic_ch, hz / 10, rt_periodic, sc);
+
+	return;
+
+fail:
+	rt_stop_locked(sc);
+}
+
+/*
+ * rt_init - lock and initialize device.
+ */
+static void
+rt_init(void *priv)
+{
+	struct rt_softc *sc;
+
+	sc = priv;
+	RT_SOFTC_LOCK(sc);
+	rt_init_locked(sc);
+	RT_SOFTC_UNLOCK(sc);
+}
+
+/*
+ * rt_stop_locked - stop TX/RX w/ lock
+ */
+static void
+rt_stop_locked(void *priv)
+{
+	struct rt_softc *sc;
+	struct ifnet *ifp;
+
+	sc = priv;
+	ifp = sc->ifp;
+
+	RT_DPRINTF(sc, RT_DEBUG_ANY, "stopping\n");
+
+	RT_SOFTC_ASSERT_LOCKED(sc);
+	sc->tx_timer = 0;
+	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+	callout_stop(&sc->periodic_ch);
+	callout_stop(&sc->tx_watchdog_ch);
+	RT_SOFTC_UNLOCK(sc);
+	taskqueue_block(sc->taskqueue);
+
+	/*
+	 * Sometime rt_stop_locked called from isr and we get panic
+	 * When found, I fix it
+	 */
+#ifdef notyet
+	taskqueue_drain(sc->taskqueue, &sc->rx_done_task);
+	taskqueue_drain(sc->taskqueue, &sc->tx_done_task);
+	taskqueue_drain(sc->taskqueue, &sc->periodic_task);
+#endif
+	RT_SOFTC_LOCK(sc);
+
+	/* disable interrupts */
+	RT_WRITE(sc, GE_PORT_BASE + FE_INT_ENABLE, 0);
+
+	/* reset adapter */
+	RT_WRITE(sc, GE_PORT_BASE + FE_RST_GLO, PSE_RESET);
+
+	RT_WRITE(sc, GDMA1_BASE + GDMA_FWD_CFG,
+	    (
+	    GDM_ICS_EN | /* Enable IP Csum */
+	    GDM_TCS_EN | /* Enable TCP Csum */
+	    GDM_UCS_EN | /* Enable UDP Csum */
+	    GDM_STRPCRC | /* Strip CRC from packet */
+	    GDM_DST_PORT_CPU << GDM_UFRC_P_SHIFT | /* Forward UCast to CPU */
+	    GDM_DST_PORT_CPU << GDM_BFRC_P_SHIFT | /* Forward BCast to CPU */
+	    GDM_DST_PORT_CPU << GDM_MFRC_P_SHIFT | /* Forward MCast to CPU */
+	    GDM_DST_PORT_CPU << GDM_OFRC_P_SHIFT   /* Forward Other to CPU */
+	    ));
+}
+
+static void
+rt_stop(void *priv)
+{
+	struct rt_softc *sc;
+
+	sc = priv;
+	RT_SOFTC_LOCK(sc);
+	rt_stop_locked(sc);
+	RT_SOFTC_UNLOCK(sc);
+}
+
+/*
+ * rt_tx_data - transmit packet.
+ */
+static int
+rt_tx_data(struct rt_softc *sc, struct mbuf *m, int qid)
+{
+	struct ifnet *ifp;
+	struct rt_softc_tx_ring *ring;
+	struct rt_softc_tx_data *data;
+	struct rt_txdesc *desc;
+	struct mbuf *m_d;
+	bus_dma_segment_t dma_seg[RT_SOFTC_MAX_SCATTER];
+	int error, ndmasegs, ndescs, i;
+
+	KASSERT(qid >= 0 && qid < RT_SOFTC_TX_RING_COUNT,
+		("%s: Tx data: invalid qid=%d\n",
+		 device_get_nameunit(sc->dev), qid));
+
+	RT_SOFTC_TX_RING_ASSERT_LOCKED(&sc->tx_ring[qid]);
+
+	ifp = sc->ifp;
+	ring = &sc->tx_ring[qid];
+	desc = &ring->desc[ring->desc_cur];
+	data = &ring->data[ring->data_cur];
+
+	error = bus_dmamap_load_mbuf_sg(ring->data_dma_tag, data->dma_map, m,
+	    dma_seg, &ndmasegs, 0);
+	if (error != 0)	{
+		/* too many fragments, linearize */
+
+		RT_DPRINTF(sc, RT_DEBUG_TX,
+			"could not load mbuf DMA map, trying to linearize "
+			"mbuf: ndmasegs=%d, len=%d, error=%d\n",
+			ndmasegs, m->m_pkthdr.len, error);
+
+		m_d = m_collapse(m, M_DONTWAIT, 16);
+		if (m_d == NULL) {
+			m_freem(m);
+			m = NULL;
+			return (ENOMEM);
+		}
+		m = m_d;
+
+		sc->tx_defrag_packets++;
+
+		error = bus_dmamap_load_mbuf_sg(ring->data_dma_tag,
+		    data->dma_map, m, dma_seg, &ndmasegs, 0);
+		if (error != 0)	{
+			device_printf(sc->dev, "could not load mbuf DMA map: "
+			    "ndmasegs=%d, len=%d, error=%d\n",
+			    ndmasegs, m->m_pkthdr.len, error);
+			m_freem(m);
+			return (error);
+		}
+	}
+
+	if (m->m_pkthdr.len == 0)
+		ndmasegs = 0;
+
+	/* determine how many Tx descs are required */
+	ndescs = 1 + ndmasegs / 2;
+	if ((ring->desc_queued + ndescs) >
+	    (RT_SOFTC_TX_RING_DESC_COUNT - 2)) {
+		RT_DPRINTF(sc, RT_DEBUG_TX,
+		    "there are not enough Tx descs\n");
+
+		sc->no_tx_desc_avail++;
+
+		bus_dmamap_unload(ring->data_dma_tag, data->dma_map);
+		m_freem(m);
+		return (EFBIG);
+	}
+
+	data->m = m;
+
+	/* set up Tx descs */
+	for (i = 0; i < ndmasegs; i += 2) {
+		/* Set destenation */
+		desc->dst = (TXDSCR_DST_PORT_GDMA1);
+		if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
+			desc->dst |= (TXDSCR_IP_CSUM_GEN|TXDSCR_UDP_CSUM_GEN|
+			    TXDSCR_TCP_CSUM_GEN);
+		/* Set queue id */
+		desc->qn = qid;
+		/* No PPPoE */
+		desc->pppoe = 0;
+		/* No VLAN */
+		desc->vid = 0;
+
+		desc->sdp0 = htole32(dma_seg[i].ds_addr);
+		desc->sdl0 = htole16(dma_seg[i].ds_len |
+		    ( ((i+1) == ndmasegs )?RT_TXDESC_SDL0_LASTSEG:0 ));
+
+		if ((i+1) < ndmasegs) {
+			desc->sdp1 = htole32(dma_seg[i+1].ds_addr);
+			desc->sdl1 = htole16(dma_seg[i+1].ds_len |
+			    ( ((i+2) == ndmasegs )?RT_TXDESC_SDL1_LASTSEG:0 ));
+		} else {
+			desc->sdp1 = 0;
+			desc->sdl1 = 0;
+		}
+
+		if ((i+2) < ndmasegs) {
+			ring->desc_queued++;
+			ring->desc_cur = (ring->desc_cur + 1) %
+			    RT_SOFTC_TX_RING_DESC_COUNT;
+		}
+		desc = &ring->desc[ring->desc_cur];
+	}
+
+	RT_DPRINTF(sc, RT_DEBUG_TX, "sending data: len=%d, ndmasegs=%d, "
+	    "DMA ds_len=%d/%d/%d/%d/%d\n",
+	    m->m_pkthdr.len, ndmasegs,
+	    (int) dma_seg[0].ds_len,
+	    (int) dma_seg[1].ds_len,

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


More information about the svn-src-all mailing list