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