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 <egore...@gmail.com>
+ * and by Damien Bergamini <damien.bergam...@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 ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to