Author: karels
Date: Wed Apr 22 00:42:10 2020
New Revision: 360181
URL: https://svnweb.freebsd.org/changeset/base/360181

Log:
  Add genet driver for Raspberry Pi 4B Ethernet
  
  Add driver for Broadcom "GENET" version 5, as found in BCM-2711 on
  Raspberry Pi 4B. The driver is derived in part from the bcmgenet.c
  driver in NetBSD, along with bcmgenetreg.h.
  
  Reviewed by:  manu
  Obtained from:        in part from NetBSD
  Relnotes:     yes, note addition
  Differential Revision:        https://reviews.freebsd.org/D24436

Added:
  head/sys/arm64/broadcom/genet/
  head/sys/arm64/broadcom/genet/if_genet.c   (contents, props changed)
  head/sys/arm64/broadcom/genet/if_genetreg.h   (contents, props changed)
Modified:
  head/sys/arm64/conf/GENERIC
  head/sys/arm64/include/bus.h
  head/sys/conf/files.arm64

Added: head/sys/arm64/broadcom/genet/if_genet.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/arm64/broadcom/genet/if_genet.c    Wed Apr 22 00:42:10 2020        
(r360181)
@@ -0,0 +1,1678 @@
+/*-
+ * Copyright (c) 2020 Michael J Karels
+ * Copyright (c) 2016, 2020 Jared McNeill <jmcne...@invisible.ca>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * RPi4 (BCM 2711) Gigabit Ethernet ("GENET") controller
+ *
+ * This driver is derived in large part from bcmgenet.c from NetBSD by
+ * Jared McNeill.  Parts of the structure and other common code in
+ * this driver have been copied from if_awg.c for the Allwinner EMAC,
+ * also by Jared McNeill.
+ */
+
+#include "opt_device_polling.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/endian.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/module.h>
+#include <sys/taskqueue.h>
+#include <sys/gpio.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_var.h>
+
+#include <machine/bus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#define __BIT(_x)      (1 << (_x))
+#include "if_genetreg.h"
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/mii/mii_fdt.h>
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+
+#include "syscon_if.h"
+#include "miibus_if.h"
+#include "gpio_if.h"
+
+#define        RD4(sc, reg)            bus_read_4((sc)->res[_RES_MAC], (reg))
+#define        WR4(sc, reg, val)       bus_write_4((sc)->res[_RES_MAC], (reg), 
(val))
+
+#define        GEN_LOCK(sc)            mtx_lock(&(sc)->mtx)
+#define        GEN_UNLOCK(sc)          mtx_unlock(&(sc)->mtx)
+#define        GEN_ASSERT_LOCKED(sc)   mtx_assert(&(sc)->mtx, MA_OWNED)
+#define        GEN_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED)
+
+#define        TX_DESC_COUNT           GENET_DMA_DESC_COUNT
+#define        RX_DESC_COUNT           GENET_DMA_DESC_COUNT
+
+#define        TX_NEXT(n, count)               (((n) + 1) & ((count) - 1))
+#define        RX_NEXT(n, count)               (((n) + 1) & ((count) - 1))
+
+
+#define        TX_MAX_SEGS             20
+
+/* Maximum number of mbufs to send to if_input */
+static int gen_rx_batch = 16 /* RX_BATCH_DEFAULT */;
+TUNABLE_INT("hw.gen.rx_batch", &gen_rx_batch);
+
+static struct ofw_compat_data compat_data[] = {
+       { "brcm,genet-v1",              1 },
+       { "brcm,genet-v2",              2 },
+       { "brcm,genet-v3",              3 },
+       { "brcm,genet-v4",              4 },
+       { "brcm,genet-v5",              5 },
+       { NULL,                         0 }
+};
+
+enum {
+       _RES_MAC,               /* what to call this? */
+       _RES_IRQ1,
+       _RES_IRQ2,
+       _RES_NITEMS
+};
+
+static struct resource_spec gen_spec[] = {
+       { SYS_RES_MEMORY,       0,      RF_ACTIVE },
+       { SYS_RES_IRQ,          0,      RF_ACTIVE },
+       { SYS_RES_IRQ,          1,      RF_ACTIVE },
+       { -1, 0 }
+};
+
+/* structure per ring entry */
+struct gen_ring_ent {
+       bus_dmamap_t            map;
+       struct mbuf             *mbuf;
+};
+
+struct tx_queue {
+       int                     hwindex;                /* hardware index */
+       int                     nentries;
+       u_int                   queued;                 /* or avail? */
+       u_int                   cur;
+       u_int                   next;
+       u_int                   prod_idx;
+       u_int                   cons_idx;
+       struct gen_ring_ent     *entries;
+};
+
+struct rx_queue {
+       int                     hwindex;                /* hardware index */
+       int                     nentries;
+       u_int                   cur;
+       u_int                   prod_idx;
+       u_int                   cons_idx;
+       struct gen_ring_ent     *entries;
+};
+
+struct gen_softc {
+       struct resource         *res[_RES_NITEMS];
+       struct mtx              mtx;
+       if_t                    ifp;
+       device_t                dev;
+       device_t                miibus;
+       mii_contype_t           phy_mode;
+
+       struct callout          stat_ch;
+       struct task             link_task;
+       void                    *ih;
+       void                    *ih2;
+       int                     type;
+       int                     if_flags;
+       int                     link;
+       bus_dma_tag_t           tx_buf_tag;
+       /*
+        * The genet chip has multiple queues for transmit and receive.
+        * This driver uses only one (queue 16, the default), but is cast
+        * with multiple rings.  The additional rings are used for different
+        * priorities.
+        */
+#define DEF_TXQUEUE    0
+#define NTXQUEUE       1
+       struct tx_queue         tx_queue[NTXQUEUE];
+       struct gen_ring_ent     tx_ring_ent[TX_DESC_COUNT];  /* ring entries */
+
+       bus_dma_tag_t           rx_buf_tag;
+#define DEF_RXQUEUE    0
+#define NRXQUEUE       1
+       struct rx_queue         rx_queue[NRXQUEUE];
+       struct gen_ring_ent     rx_ring_ent[RX_DESC_COUNT];  /* ring entries */
+};
+
+static void gen_init(void *softc);
+static void gen_start(if_t ifp);
+static void gen_destroy(struct gen_softc *sc);
+static int gen_encap(struct gen_softc *sc, struct mbuf **mp);
+static int gen_parse_tx(struct mbuf *m, int csum_flags);
+static int gen_ioctl(if_t ifp, u_long cmd, caddr_t data);
+static int gen_get_phy_mode(device_t dev);
+static bool gen_get_eaddr(device_t dev, struct ether_addr *eaddr);
+static void gen_set_enaddr(struct gen_softc *sc);
+static void gen_setup_rxfilter(struct gen_softc *sc);
+static void gen_reset(struct gen_softc *sc);
+static void gen_enable(struct gen_softc *sc);
+static void gen_dma_disable(device_t dev);
+static int gen_bus_dma_init(struct gen_softc *sc);
+static void gen_bus_dma_teardown(struct gen_softc *sc);
+static void gen_enable_intr(struct gen_softc *sc);
+static void gen_init_txrings(struct gen_softc *sc);
+static void gen_init_rxrings(struct gen_softc *sc);
+static void gen_intr(void *softc);
+static int gen_rxintr(struct gen_softc *sc, struct rx_queue *q);
+static void gen_txintr(struct gen_softc *sc, struct tx_queue *q);
+static void gen_intr2(void *softc);
+static int gen_newbuf_rx(struct gen_softc *sc, struct rx_queue *q, int index);
+static int gen_mapbuf_rx(struct gen_softc *sc, struct rx_queue *q, int index,
+    struct mbuf *m);
+static void gen_link_task(void *arg, int pending);
+static void gen_media_status(if_t ifp, struct ifmediareq *ifmr);
+static int gen_media_change(if_t ifp);
+static void gen_tick(void *softc);
+
+static int
+gen_probe(device_t dev)
+{
+       if (!ofw_bus_status_okay(dev))
+               return (ENXIO);
+
+       if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+               return (ENXIO);
+
+       device_set_desc(dev, "RPi4 Gigabit Ethernet");
+       return (BUS_PROBE_DEFAULT);
+}
+
+static int
+gen_attach(device_t dev)
+{
+       struct ether_addr eaddr;
+       struct gen_softc *sc;
+       int major, minor, error;
+       bool eaddr_found;
+
+       sc = device_get_softc(dev);
+       sc->dev = dev;
+       sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+       if (bus_alloc_resources(dev, gen_spec, sc->res) != 0) {
+               device_printf(dev, "cannot allocate resources for device\n");
+               error = ENXIO;
+               goto fail;
+       }
+
+       major = (RD4(sc, GENET_SYS_REV_CTRL) & REV_MAJOR) >> REV_MAJOR_SHIFT;
+       if (major != REV_MAJOR_V5) {
+               device_printf(dev, "version %d is not supported\n", major);
+               error = ENXIO;
+               goto fail;
+       }
+       minor = (RD4(sc, GENET_SYS_REV_CTRL) & REV_MINOR) >> REV_MINOR_SHIFT;
+       device_printf(dev, "GENET version 5.%d phy 0x%04x\n", minor,
+               RD4(sc, GENET_SYS_REV_CTRL) & REV_PHY);
+
+       mtx_init(&sc->mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF);
+       callout_init_mtx(&sc->stat_ch, &sc->mtx, 0);
+       TASK_INIT(&sc->link_task, 0, gen_link_task, sc);
+
+       error = gen_get_phy_mode(dev);
+       if (error != 0)
+               goto fail;
+
+       bzero(&eaddr, sizeof(eaddr));
+       eaddr_found = gen_get_eaddr(dev, &eaddr);
+
+       /* reset core */
+       gen_reset(sc);
+
+       gen_dma_disable(dev);
+
+       /* Setup DMA */
+       error = gen_bus_dma_init(sc);
+       if (error != 0) {
+               device_printf(dev, "cannot setup bus dma\n");
+               goto fail;
+       }
+
+       /* Install interrupt handlers */
+       error = bus_setup_intr(dev, sc->res[_RES_IRQ1],
+           INTR_TYPE_NET | INTR_MPSAFE, NULL, gen_intr, sc, &sc->ih);
+       if (error != 0) {
+               device_printf(dev, "cannot setup interrupt handler1\n");
+               goto fail;
+       }
+
+       error = bus_setup_intr(dev, sc->res[_RES_IRQ2],
+           INTR_TYPE_NET | INTR_MPSAFE, NULL, gen_intr2, sc, &sc->ih2);
+       if (error != 0) {
+               device_printf(dev, "cannot setup interrupt handler2\n");
+               goto fail;
+       }
+
+       /* Setup ethernet interface */
+       sc->ifp = if_alloc(IFT_ETHER);
+       if_setsoftc(sc->ifp, sc);
+       if_initname(sc->ifp, device_get_name(dev), device_get_unit(dev));
+       if_setflags(sc->ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
+       if_setstartfn(sc->ifp, gen_start);
+       if_setioctlfn(sc->ifp, gen_ioctl);
+       if_setinitfn(sc->ifp, gen_init);
+       if_setsendqlen(sc->ifp, TX_DESC_COUNT - 1);
+       if_setsendqready(sc->ifp);
+#define GEN_CSUM_FEATURES      (CSUM_UDP | CSUM_TCP)
+       if_sethwassist(sc->ifp, GEN_CSUM_FEATURES);
+       if_setcapabilities(sc->ifp, IFCAP_VLAN_MTU | IFCAP_HWCSUM |
+           IFCAP_HWCSUM_IPV6);
+       if_setcapenable(sc->ifp, if_getcapabilities(sc->ifp));
+
+       /* Attach MII driver */
+       error = mii_attach(dev, &sc->miibus, sc->ifp, gen_media_change,
+           gen_media_status, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY,
+           MIIF_DOPAUSE);
+       if (error != 0) {
+               device_printf(dev, "cannot attach PHY\n");
+               goto fail;
+       }
+
+       /* If address was not found, create one based on the hostid and name. */
+       if (eaddr_found == 0)
+               ether_gen_addr(sc->ifp, &eaddr);
+       /* Attach ethernet interface */
+       ether_ifattach(sc->ifp, eaddr.octet);
+
+fail:
+       if (error)
+               gen_destroy(sc);
+       return (error);
+}
+
+/* Free resources after failed attach.  This is not a complete detach. */
+static void
+gen_destroy(struct gen_softc *sc)
+{
+
+       if (sc->miibus) {       /* can't happen */
+               device_delete_child(sc->dev, sc->miibus);
+               sc->miibus = NULL;
+       }
+       bus_teardown_intr(sc->dev, sc->res[_RES_IRQ1], sc->ih);
+       bus_teardown_intr(sc->dev, sc->res[_RES_IRQ2], sc->ih2);
+       gen_bus_dma_teardown(sc);
+       callout_drain(&sc->stat_ch);
+       if (mtx_initialized(&sc->mtx))
+               mtx_destroy(&sc->mtx);
+       bus_release_resources(sc->dev, gen_spec, sc->res);
+       if (sc->ifp != NULL) {
+               if_free(sc->ifp);
+               sc->ifp = NULL;
+       }
+}
+
+static int
+gen_get_phy_mode(device_t dev)
+{
+       struct gen_softc *sc;
+       phandle_t node;
+       mii_contype_t type;
+       int error = 0;
+
+       sc = device_get_softc(dev);
+       node = ofw_bus_get_node(dev);
+       type = mii_fdt_get_contype(node);
+
+       switch (type) {
+       case MII_CONTYPE_RGMII:
+       case MII_CONTYPE_RGMII_RXID:
+       case MII_CONTYPE_RGMII_TXID:
+               sc->phy_mode = type;
+               break;
+       default:
+               device_printf(dev, "unknown phy-mode '%s'\n",
+                   mii_fdt_contype_to_name(type));
+               error = ENXIO;
+               break;
+       }
+
+       return (error);
+}
+
+static bool
+gen_get_eaddr(device_t dev, struct ether_addr *eaddr)
+{
+       struct gen_softc *sc;
+       uint32_t maclo, machi, val;
+       phandle_t node;
+
+       sc = device_get_softc(dev);
+
+       node = ofw_bus_get_node(dev);
+       if (OF_getprop(node, "mac-address", eaddr->octet,
+           ETHER_ADDR_LEN) != -1 ||
+           OF_getprop(node, "local-mac-address", eaddr->octet,
+           ETHER_ADDR_LEN) != -1 ||
+           OF_getprop(node, "address", eaddr->octet, ETHER_ADDR_LEN) != -1)
+               return (true);
+
+       device_printf(dev, "No Ethernet address found in fdt!\n");
+       maclo = machi = 0;
+
+       val = RD4(sc, GENET_SYS_RBUF_FLUSH_CTRL);
+       if ((val & GENET_SYS_RBUF_FLUSH_RESET) == 0) {
+               maclo = htobe32(RD4(sc, GENET_UMAC_MAC0));
+               machi = htobe16(RD4(sc, GENET_UMAC_MAC1) & 0xffff);
+       }
+
+       if (maclo == 0 && machi == 0) {
+               if (bootverbose)
+                       device_printf(dev,
+                           "No Ethernet address found in controller\n");
+               return (false);
+       } else {
+               eaddr->octet[0] = maclo & 0xff;
+               eaddr->octet[1] = (maclo >> 8) & 0xff;
+               eaddr->octet[2] = (maclo >> 16) & 0xff;
+               eaddr->octet[3] = (maclo >> 24) & 0xff;
+               eaddr->octet[4] = machi & 0xff;
+               eaddr->octet[5] = (machi >> 8) & 0xff;
+               return (true);
+       }
+}
+
+static void
+gen_reset(struct gen_softc *sc)
+{
+       uint32_t val;
+
+       val = RD4(sc, GENET_SYS_RBUF_FLUSH_CTRL);
+       val |= GENET_SYS_RBUF_FLUSH_RESET;
+       WR4(sc, GENET_SYS_RBUF_FLUSH_CTRL, val);
+       DELAY(10);
+
+       val &= ~GENET_SYS_RBUF_FLUSH_RESET;
+       WR4(sc, GENET_SYS_RBUF_FLUSH_CTRL, val);
+       DELAY(10);
+
+       WR4(sc, GENET_SYS_RBUF_FLUSH_CTRL, 0);
+       DELAY(10);
+
+       WR4(sc, GENET_UMAC_CMD, 0);
+       WR4(sc, GENET_UMAC_CMD,
+           GENET_UMAC_CMD_LCL_LOOP_EN | GENET_UMAC_CMD_SW_RESET);
+       DELAY(10);
+       WR4(sc, GENET_UMAC_CMD, 0);
+
+       WR4(sc, GENET_UMAC_MIB_CTRL, GENET_UMAC_MIB_RESET_RUNT |
+           GENET_UMAC_MIB_RESET_RX | GENET_UMAC_MIB_RESET_TX);
+       WR4(sc, GENET_UMAC_MIB_CTRL, 0);
+
+       WR4(sc, GENET_UMAC_MAX_FRAME_LEN, 1536);
+
+       val = RD4(sc, GENET_RBUF_CTRL);
+       val |= GENET_RBUF_ALIGN_2B;
+       WR4(sc, GENET_RBUF_CTRL, val);
+
+       WR4(sc, GENET_RBUF_TBUF_SIZE_CTRL, 1);
+}
+
+static void
+gen_enable(struct gen_softc *sc)
+{
+       u_int val;
+
+       /* Enable transmitter and receiver */
+       val = RD4(sc, GENET_UMAC_CMD);
+       val |= GENET_UMAC_CMD_TXEN;
+       val |= GENET_UMAC_CMD_RXEN;
+       WR4(sc, GENET_UMAC_CMD, val);
+
+       /* Enable interrupts */
+       gen_enable_intr(sc);
+       WR4(sc, GENET_INTRL2_CPU_CLEAR_MASK,
+           GENET_IRQ_TXDMA_DONE | GENET_IRQ_RXDMA_DONE);
+}
+
+static void
+gen_enable_offload(struct gen_softc *sc)
+{
+       uint32_t check_ctrl, buf_ctrl;
+
+       check_ctrl = RD4(sc, GENET_RBUF_CHECK_CTRL);
+       buf_ctrl  = RD4(sc, GENET_RBUF_CTRL);
+       if ((if_getcapenable(sc->ifp) & IFCAP_RXCSUM) != 0) {
+               check_ctrl |= GENET_RBUF_CHECK_CTRL_EN;
+               buf_ctrl |= GENET_RBUF_64B_EN;
+       } else {
+               check_ctrl &= ~GENET_RBUF_CHECK_CTRL_EN;
+               buf_ctrl &= ~GENET_RBUF_64B_EN;
+       }
+       WR4(sc, GENET_RBUF_CHECK_CTRL, check_ctrl);
+       WR4(sc, GENET_RBUF_CTRL, buf_ctrl);
+
+       buf_ctrl  = RD4(sc, GENET_TBUF_CTRL);
+       if ((if_getcapenable(sc->ifp) & (IFCAP_TXCSUM | IFCAP_TXCSUM_IPV6)) !=
+           0)
+               buf_ctrl |= GENET_RBUF_64B_EN;
+       else
+               buf_ctrl &= ~GENET_RBUF_64B_EN;
+       WR4(sc, GENET_TBUF_CTRL, buf_ctrl);
+}
+
+static void
+gen_dma_disable(device_t dev)
+{
+       struct gen_softc *sc = device_get_softc(dev);
+       int val;
+
+       val = RD4(sc, GENET_TX_DMA_CTRL);
+       val &= ~GENET_TX_DMA_CTRL_EN;
+       val &= ~GENET_TX_DMA_CTRL_RBUF_EN(GENET_DMA_DEFAULT_QUEUE);
+       WR4(sc, GENET_TX_DMA_CTRL, val);
+
+       val = RD4(sc, GENET_RX_DMA_CTRL);
+       val &= ~GENET_RX_DMA_CTRL_EN;
+       val &= ~GENET_RX_DMA_CTRL_RBUF_EN(GENET_DMA_DEFAULT_QUEUE);
+       WR4(sc, GENET_RX_DMA_CTRL, val);
+}
+
+static int
+gen_bus_dma_init(struct gen_softc *sc)
+{
+       struct device *dev = sc->dev;
+       int i, error;
+
+       error = bus_dma_tag_create(
+           bus_get_dma_tag(dev),       /* Parent tag */
+           4, 0,                       /* alignment, boundary */
+           BUS_SPACE_MAXADDR_40BIT,    /* lowaddr */
+           BUS_SPACE_MAXADDR,          /* highaddr */
+           NULL, NULL,                 /* filter, filterarg */
+           MCLBYTES, TX_MAX_SEGS,      /* maxsize, nsegs */
+           MCLBYTES,                   /* maxsegsize */
+           0,                          /* flags */
+           NULL, NULL,                 /* lockfunc, lockarg */
+           &sc->tx_buf_tag);
+       if (error != 0) {
+               device_printf(dev, "cannot create TX buffer tag\n");
+               return (error);
+       }
+
+       for (i = 0; i < TX_DESC_COUNT; i++) {
+               error = bus_dmamap_create(sc->tx_buf_tag, 0,
+                   &sc->tx_ring_ent[i].map);
+               if (error != 0) {
+                       device_printf(dev, "cannot create TX buffer map\n");
+                       return (error);
+               }
+       }
+
+       error = bus_dma_tag_create(
+           bus_get_dma_tag(dev),       /* Parent tag */
+           4, 0,                       /* alignment, boundary */
+           BUS_SPACE_MAXADDR_40BIT,    /* lowaddr */
+           BUS_SPACE_MAXADDR,          /* highaddr */
+           NULL, NULL,                 /* filter, filterarg */
+           MCLBYTES, 1,                /* maxsize, nsegs */
+           MCLBYTES,                   /* maxsegsize */
+           0,                          /* flags */
+           NULL, NULL,                 /* lockfunc, lockarg */
+           &sc->rx_buf_tag);
+       if (error != 0) {
+               device_printf(dev, "cannot create RX buffer tag\n");
+               return (error);
+       }
+
+       for (i = 0; i < RX_DESC_COUNT; i++) {
+               error = bus_dmamap_create(sc->rx_buf_tag, 0,
+                   &sc->rx_ring_ent[i].map);
+               if (error != 0) {
+                       device_printf(dev, "cannot create RX buffer map\n");
+                       return (error);
+               }
+       }
+       return (0);
+}
+
+static void
+gen_bus_dma_teardown(struct gen_softc *sc)
+{
+       int i, error;
+
+       if (sc->tx_buf_tag != NULL) {
+               for (i = 0; i < TX_DESC_COUNT; i++) {
+                       error = bus_dmamap_destroy(sc->tx_buf_tag,
+                           sc->tx_ring_ent[i].map);
+                       sc->tx_ring_ent[i].map = NULL;
+                       if (error)
+                               device_printf(sc->dev,
+                                   "%s: bus_dmamap_destroy failed: %d\n",
+                                   __func__, error);
+               }
+               error = bus_dma_tag_destroy(sc->tx_buf_tag);
+               sc->tx_buf_tag = NULL;
+               if (error)
+                       device_printf(sc->dev,
+                           "%s: bus_dma_tag_destroy failed: %d\n", __func__,
+                           error);
+       }
+
+       if (sc->tx_buf_tag != NULL) {
+               for (i = 0; i < RX_DESC_COUNT; i++) {
+                       error = bus_dmamap_destroy(sc->rx_buf_tag,
+                           sc->rx_ring_ent[i].map);
+                       sc->rx_ring_ent[i].map = NULL;
+                       if (error)
+                               device_printf(sc->dev,
+                                   "%s: bus_dmamap_destroy failed: %d\n",
+                                   __func__, error);
+               }
+               error = bus_dma_tag_destroy(sc->rx_buf_tag);
+               sc->rx_buf_tag = NULL;
+               if (error)
+                       device_printf(sc->dev,
+                           "%s: bus_dma_tag_destroy failed: %d\n", __func__,
+                           error);
+       }
+}
+
+static void
+gen_enable_intr(struct gen_softc *sc)
+{
+
+       WR4(sc, GENET_INTRL2_CPU_CLEAR_MASK,
+           GENET_IRQ_TXDMA_DONE | GENET_IRQ_RXDMA_DONE);
+}
+
+/*
+ * "queue" is the software queue index (0-4); "qid" is the hardware index
+ * (0-16).  "base" is the starting index in the ring array.
+ */
+static void
+gen_init_txring(struct gen_softc *sc, int queue, int qid, int base,
+    int nentries)
+{
+       struct tx_queue *q;
+       uint32_t val;
+
+       q = &sc->tx_queue[queue];
+       q->entries = &sc->tx_ring_ent[base];
+       q->hwindex = qid;
+       q->nentries = nentries;
+
+       /* TX ring */
+
+       q->queued = 0;
+       q->cons_idx = q->prod_idx = 0;
+
+       WR4(sc, GENET_TX_SCB_BURST_SIZE, 0x08);
+
+       WR4(sc, GENET_TX_DMA_READ_PTR_LO(qid), 0);
+       WR4(sc, GENET_TX_DMA_READ_PTR_HI(qid), 0);
+       WR4(sc, GENET_TX_DMA_CONS_INDEX(qid), 0);
+       WR4(sc, GENET_TX_DMA_PROD_INDEX(qid), 0);
+       WR4(sc, GENET_TX_DMA_RING_BUF_SIZE(qid),
+           (nentries << GENET_TX_DMA_RING_BUF_SIZE_DESC_SHIFT) |
+           (MCLBYTES & GENET_TX_DMA_RING_BUF_SIZE_BUF_LEN_MASK));
+       WR4(sc, GENET_TX_DMA_START_ADDR_LO(qid), 0);
+       WR4(sc, GENET_TX_DMA_START_ADDR_HI(qid), 0);
+       WR4(sc, GENET_TX_DMA_END_ADDR_LO(qid),
+           TX_DESC_COUNT * GENET_DMA_DESC_SIZE / 4 - 1);
+       WR4(sc, GENET_TX_DMA_END_ADDR_HI(qid), 0);
+       WR4(sc, GENET_TX_DMA_MBUF_DONE_THRES(qid), 1);
+       WR4(sc, GENET_TX_DMA_FLOW_PERIOD(qid), 0);
+       WR4(sc, GENET_TX_DMA_WRITE_PTR_LO(qid), 0);
+       WR4(sc, GENET_TX_DMA_WRITE_PTR_HI(qid), 0);
+
+       WR4(sc, GENET_TX_DMA_RING_CFG, __BIT(qid));     /* enable */
+
+       /* Enable transmit DMA */
+       val = RD4(sc, GENET_TX_DMA_CTRL);
+       val |= GENET_TX_DMA_CTRL_EN;
+       val |= GENET_TX_DMA_CTRL_RBUF_EN(qid);
+       WR4(sc, GENET_TX_DMA_CTRL, val);
+}
+
+/*
+ * "queue" is the software queue index (0-4); "qid" is the hardware index
+ * (0-16).  "base" is the starting index in the ring array.
+ */
+static void
+gen_init_rxring(struct gen_softc *sc, int queue, int qid, int base,
+    int nentries)
+{
+       struct rx_queue *q;
+       uint32_t val;
+       int i;
+
+       q = &sc->rx_queue[queue];
+       q->entries = &sc->rx_ring_ent[base];
+       q->hwindex = qid;
+       q->nentries = nentries;
+       q->cons_idx = q->prod_idx = 0;
+
+       WR4(sc, GENET_RX_SCB_BURST_SIZE, 0x08);
+
+       WR4(sc, GENET_RX_DMA_WRITE_PTR_LO(qid), 0);
+       WR4(sc, GENET_RX_DMA_WRITE_PTR_HI(qid), 0);
+       WR4(sc, GENET_RX_DMA_PROD_INDEX(qid), 0);
+       WR4(sc, GENET_RX_DMA_CONS_INDEX(qid), 0);
+       WR4(sc, GENET_RX_DMA_RING_BUF_SIZE(qid),
+           (nentries << GENET_RX_DMA_RING_BUF_SIZE_DESC_SHIFT) |
+           (MCLBYTES & GENET_RX_DMA_RING_BUF_SIZE_BUF_LEN_MASK));
+       WR4(sc, GENET_RX_DMA_START_ADDR_LO(qid), 0);
+       WR4(sc, GENET_RX_DMA_START_ADDR_HI(qid), 0);
+       WR4(sc, GENET_RX_DMA_END_ADDR_LO(qid),
+           RX_DESC_COUNT * GENET_DMA_DESC_SIZE / 4 - 1);
+       WR4(sc, GENET_RX_DMA_END_ADDR_HI(qid), 0);
+       WR4(sc, GENET_RX_DMA_XON_XOFF_THRES(qid),
+           (5 << GENET_RX_DMA_XON_XOFF_THRES_LO_SHIFT) | (RX_DESC_COUNT >> 4));
+       WR4(sc, GENET_RX_DMA_READ_PTR_LO(qid), 0);
+       WR4(sc, GENET_RX_DMA_READ_PTR_HI(qid), 0);
+
+       WR4(sc, GENET_RX_DMA_RING_CFG, __BIT(qid));     /* enable */
+
+       /* fill ring */
+       for (i = 0; i < RX_DESC_COUNT; i++)
+               gen_newbuf_rx(sc, &sc->rx_queue[DEF_RXQUEUE], i);
+
+       /* Enable receive DMA */
+       val = RD4(sc, GENET_RX_DMA_CTRL);
+       val |= GENET_RX_DMA_CTRL_EN;
+       val |= GENET_RX_DMA_CTRL_RBUF_EN(qid);
+       WR4(sc, GENET_RX_DMA_CTRL, val);
+}
+
+static void
+gen_init_txrings(struct gen_softc *sc)
+{
+       int base = 0;
+#ifdef PRI_RINGS
+       int i;
+
+       /* init priority rings */
+       for (i = 0; i < PRI_RINGS; i++) {
+               gen_init_txring(sc, i, i, base, TX_DESC_PRICOUNT);
+               sc->tx_queue[i].queue = i;
+               base += TX_DESC_PRICOUNT;
+               dma_ring_conf |= 1 << i;
+               dma_control |= DMA_RENABLE(i);
+       }
+#endif
+
+       /* init GENET_DMA_DEFAULT_QUEUE (16) */
+       gen_init_txring(sc, DEF_TXQUEUE, GENET_DMA_DEFAULT_QUEUE, base,
+           TX_DESC_COUNT);
+       sc->tx_queue[DEF_TXQUEUE].hwindex = GENET_DMA_DEFAULT_QUEUE;
+}
+
+static void
+gen_init_rxrings(struct gen_softc *sc)
+{
+       int base = 0;
+#ifdef PRI_RINGS
+       int i;
+
+       /* init priority rings */
+       for (i = 0; i < PRI_RINGS; i++) {
+               gen_init_rxring(sc, i, i, base, TX_DESC_PRICOUNT);
+               sc->rx_queue[i].queue = i;
+               base += TX_DESC_PRICOUNT;
+               dma_ring_conf |= 1 << i;
+               dma_control |= DMA_RENABLE(i);
+       }
+#endif
+
+       /* init GENET_DMA_DEFAULT_QUEUE (16) */
+       gen_init_rxring(sc, DEF_RXQUEUE, GENET_DMA_DEFAULT_QUEUE, base,
+           RX_DESC_COUNT);
+       sc->rx_queue[DEF_RXQUEUE].hwindex = GENET_DMA_DEFAULT_QUEUE;
+
+}
+
+static void
+gen_init_locked(struct gen_softc *sc)
+{
+       struct mii_data *mii;
+       if_t ifp;
+
+       mii = device_get_softc(sc->miibus);
+       ifp = sc->ifp;
+
+       GEN_ASSERT_LOCKED(sc);
+
+       if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
+               return;
+
+       if (sc->phy_mode == MII_CONTYPE_RGMII ||
+           sc->phy_mode == MII_CONTYPE_RGMII_RXID)
+               WR4(sc, GENET_SYS_PORT_CTRL,
+                   GENET_SYS_PORT_MODE_EXT_GPHY);
+
+       gen_set_enaddr(sc);
+
+       /* Setup RX filter */
+       gen_setup_rxfilter(sc);
+
+       gen_init_txrings(sc);
+       gen_init_rxrings(sc);
+       gen_enable(sc);
+       gen_enable_offload(sc);
+
+       if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE);
+
+       mii_mediachg(mii);
+       callout_reset(&sc->stat_ch, hz, gen_tick, sc);
+}
+
+static void
+gen_init(void *softc)
+{
+        struct gen_softc *sc;
+
+        sc = softc;
+       GEN_LOCK(sc);
+       gen_init_locked(sc);
+       GEN_UNLOCK(sc);
+}
+
+static uint8_t ether_broadcastaddr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+
+static void
+gen_setup_rxfilter_mdf(struct gen_softc *sc, u_int n, const uint8_t *ea)
+{
+       uint32_t addr0 = (ea[0] << 8) | ea[1];
+       uint32_t addr1 = (ea[2] << 24) | (ea[3] << 16) | (ea[4] << 8) | ea[5];
+
+       WR4(sc, GENET_UMAC_MDF_ADDR0(n), addr0);
+       WR4(sc, GENET_UMAC_MDF_ADDR1(n), addr1);
+}
+
+static u_int
+gen_setup_multi(void *arg, struct sockaddr_dl *sdl, u_int count)
+{
+       struct gen_softc *sc = arg;
+
+       /* "count + 2" to account for unicast and broadcast */
+       gen_setup_rxfilter_mdf(sc, count + 2, LLADDR(sdl));
+       return (1);             /* increment to count */
+}
+
+static void
+gen_setup_rxfilter(struct gen_softc *sc)
+{
+       struct ifnet *ifp = sc->ifp;
+       uint32_t cmd, mdf_ctrl;
+       u_int n;
+
+       GEN_ASSERT_LOCKED(sc);
+
+       cmd = RD4(sc, GENET_UMAC_CMD);
+
+       /*
+        * Count the required number of hardware filters. We need one
+        * for each multicast address, plus one for our own address and
+        * the broadcast address.
+        */
+       n = if_llmaddr_count(ifp) + 2;
+
+       if (n > GENET_MAX_MDF_FILTER)
+               ifp->if_flags |= IFF_ALLMULTI;
+       else
+               ifp->if_flags &= ~IFF_ALLMULTI;
+
+       if ((ifp->if_flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0) {
+               cmd |= GENET_UMAC_CMD_PROMISC;
+               mdf_ctrl = 0;
+       } else {
+               cmd &= ~GENET_UMAC_CMD_PROMISC;
+               gen_setup_rxfilter_mdf(sc, 0, ether_broadcastaddr);
+               gen_setup_rxfilter_mdf(sc, 1, IF_LLADDR(ifp));
+               (void) if_foreach_llmaddr(ifp, gen_setup_multi, sc);
+               mdf_ctrl = (__BIT(GENET_MAX_MDF_FILTER) - 1)  &~
+                   (__BIT(GENET_MAX_MDF_FILTER - n) - 1);
+       }
+
+       WR4(sc, GENET_UMAC_CMD, cmd);
+       WR4(sc, GENET_UMAC_MDF_CTRL, mdf_ctrl);
+}
+
+static void
+gen_set_enaddr(struct gen_softc *sc)
+{
+       uint8_t *enaddr;
+       uint32_t val;
+       if_t ifp;
+
+       GEN_ASSERT_LOCKED(sc);
+
+       ifp = sc->ifp;
+
+       /* Write our unicast address */
+       enaddr = IF_LLADDR(ifp);
+       /* Write hardware address */
+       val = enaddr[3] | (enaddr[2] << 8) | (enaddr[1] << 16) |
+           (enaddr[0] << 24);
+       WR4(sc, GENET_UMAC_MAC0, val);
+       val = enaddr[5] | (enaddr[4] << 8);
+       WR4(sc, GENET_UMAC_MAC1, val);
+}
+
+static void
+gen_start_locked(struct gen_softc *sc)
+{
+       struct mbuf *m;
+       if_t ifp;
+       int cnt, err;
+
+       GEN_ASSERT_LOCKED(sc);
+
+       if (!sc->link)
+               return;
+
+       ifp = sc->ifp;
+
+       if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
+           IFF_DRV_RUNNING)
+               return;
+
+       for (cnt = 0; ; cnt++) {
+               m = if_dequeue(ifp);
+               if (m == NULL)
+                       break;
+
+               err = gen_encap(sc, &m);
+               if (err != 0) {
+                       if (err == ENOBUFS)
+                               if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
+                       if (m != NULL)
+                               if_sendq_prepend(ifp, m);
+                       break;
+               }
+               if_bpfmtap(ifp, m);
+       }
+}
+
+static void
+gen_start(if_t ifp)
+{
+       struct gen_softc *sc;
+
+       sc = if_getsoftc(ifp);
+
+       GEN_LOCK(sc);
+       gen_start_locked(sc);
+       GEN_UNLOCK(sc);
+}
+
+static int
+gen_encap(struct gen_softc *sc, struct mbuf **mp)
+{
+       bus_dmamap_t map;
+       bus_dma_segment_t segs[TX_MAX_SEGS];
+       int error, nsegs, cur, first, i, index, offset;
+       uint32_t csuminfo, length_status, csum_flags = 0, csumdata;
+       struct mbuf *m;
+       struct statusblock *sb = NULL;
+       struct tx_queue *q;
+       struct gen_ring_ent *ent;
+
+       GEN_ASSERT_LOCKED(sc);
+
+       q = &sc->tx_queue[DEF_TXQUEUE];
+
+       m = *mp;
+       if ((if_getcapenable(sc->ifp) & (IFCAP_TXCSUM | IFCAP_TXCSUM_IPV6)) !=
+           0) {
+               csum_flags = m->m_pkthdr.csum_flags;
+               csumdata = m->m_pkthdr.csum_data;
+               M_PREPEND(m, sizeof(struct statusblock), M_NOWAIT);
+               if (m == NULL) {
+                       if (sc->ifp->if_flags & IFF_DEBUG)
+                               device_printf(sc->dev, "prepend fail\n");
+                       *mp = NULL;
+                       return (ENOMEM);
+               }
+               offset = gen_parse_tx(m, csum_flags);
+               sb = mtod(m, struct statusblock *);
+               if (csum_flags != 0) {
+                       csuminfo = (offset << TXCSUM_OFF_SHIFT) |
+                           (offset + csumdata);
+                       if (csum_flags & (CSUM_TCP | CSUM_UDP))
+                               csuminfo |= TXCSUM_LEN_VALID;
+                       if (csum_flags & CSUM_UDP)
+                               csuminfo |= TXCSUM_UDP;
+                       sb->txcsuminfo = csuminfo;
+               } else
+                       sb->txcsuminfo = 0;
+       }
+
+       *mp = m;
+
+       cur = first = q->cur;
+       ent = &q->entries[cur];
+       map = ent->map;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to