The diff below adds support for the Ethernet interface on the
Raspberry Pi4.  The new driver is called bse(4) for Broadcom SoC
Ethernet ;).  It is a port of the NetBSD driver (where the driver is
called genet(4), but over here we prefer short names for our network
drivers).  There have been many changes to adapt the driver to our
codebase, in particular the ring accounting framework.  Only the ACPI
attachment is provided for now.  An FDT attachment will follow later.
Seems I can sustain ~700 Mbit/s with this diff.

This is the complete diff including the acpi_getprop() and brgphy(4)
diffs I posted earlier.  It is my intention to commit those
separately.

ok?


Index: arch/arm64/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/arm64/conf/GENERIC,v
retrieving revision 1.149
diff -u -p -r1.149 GENERIC
--- arch/arm64/conf/GENERIC     3 Apr 2020 16:29:17 -0000       1.149
+++ arch/arm64/conf/GENERIC     12 Apr 2020 15:38:42 -0000
@@ -138,12 +138,13 @@ ssdfb*            at spi?
 wsdisplay*     at ssdfb?
 imxtmu*                at fdt?
 
-# Raspberry Pi 3
+# Raspberry Pi 3/4
 bcmaux*                at fdt?
 bcmintc*       at fdt?
 bcmdog*                at fdt?
 bcmrng*                at fdt?
 bcmtemp*       at fdt?
+bse*           at acpi?
 dwctwo*                at fdt?
 usb*           at dwctwo?
 
@@ -383,6 +384,7 @@ bwfm*               at uhub?                # Broadcom 
FullMAC
 
 amphy*         at mii?                 # AMD 79C873 PHYs
 atphy*         at mii?                 # Attansic F1 PHYs
+brgphy*                at mii?                 # Broadcom Gigabit PHYs
 eephy*         at mii?                 # Marvell 88E1000 series PHY
 rgephy*                at mii?                 # Realtek 8169S/8110S PHY
 rlphy*         at mii?                 # Realtek 8139 internal PHYs
Index: conf/files
===================================================================
RCS file: /cvs/src/sys/conf/files,v
retrieving revision 1.685
diff -u -p -r1.685 files
--- conf/files  2 Mar 2020 10:37:22 -0000       1.685
+++ conf/files  12 Apr 2020 15:38:43 -0000
@@ -306,6 +306,10 @@ file       dev/ic/gem.c                    gem
 device ti: ether, ifnet, ifmedia, mii, firmload
 file   dev/ic/ti.c                     ti
 
+# Broadcom BCM7XXX Ethernet controller
+device bse: ether, ifnet, ifmedia, mii
+file   dev/ic/bcmgenet.c               bse
+
 # 8250/16[45]50-based "com" ports
 device com: tty
 file   dev/ic/com.c                    com & (com | com_cardbus | com_gsc |
Index: dev/acpi/acpi.c
===================================================================
RCS file: /cvs/src/sys/dev/acpi/acpi.c,v
retrieving revision 1.381
diff -u -p -r1.381 acpi.c
--- dev/acpi/acpi.c     12 Apr 2020 09:21:19 -0000      1.381
+++ dev/acpi/acpi.c     12 Apr 2020 15:38:43 -0000
@@ -2961,6 +2961,57 @@ acpi_foundsony(struct aml_node *node, vo
 
 /* Support for _DSD Device Properties. */
 
+int
+acpi_getprop(struct aml_node *node, const char *prop, void *buf, int buflen)
+{
+       struct aml_value dsd;
+       int i;
+
+       /* daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
+       static uint8_t prop_guid[] = {
+               0x14, 0xd8, 0xff, 0xda, 0xba, 0x6e, 0x8c, 0x4d,
+               0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01,
+       };
+
+       if (aml_evalname(acpi_softc, node, "_DSD", 0, NULL, &dsd))
+               return -1;
+
+       if (dsd.type != AML_OBJTYPE_PACKAGE || dsd.length != 2 ||
+           dsd.v_package[0]->type != AML_OBJTYPE_BUFFER ||
+           dsd.v_package[1]->type != AML_OBJTYPE_PACKAGE)
+               return -1;
+
+       /* Check UUID. */
+       if (dsd.v_package[0]->length != sizeof(prop_guid) ||
+           memcmp(dsd.v_package[0]->v_buffer, prop_guid,
+           sizeof(prop_guid)) != 0)
+               return -1;
+
+       /* Check properties. */
+       for (i = 0; i < dsd.v_package[1]->length; i++) {
+               struct aml_value *res = dsd.v_package[1]->v_package[i];
+               int len;
+
+               if (res->type != AML_OBJTYPE_PACKAGE || res->length != 2 ||
+                   res->v_package[0]->type != AML_OBJTYPE_STRING)
+                       continue;
+
+               len = res->v_package[1]->length;
+               switch (res->v_package[1]->type) {
+               case AML_OBJTYPE_BUFFER:
+                       memcpy(buf, res->v_package[1]->v_buffer,
+                           min(len, buflen));
+                       return len;
+               case AML_OBJTYPE_STRING:
+                       memcpy(buf, res->v_package[1]->v_string,
+                           min(len, buflen));
+                       return len;
+               }
+       }
+
+       return -1;
+}
+
 uint32_t
 acpi_getpropint(struct aml_node *node, const char *prop, uint32_t defval)
 {
@@ -2999,7 +3050,7 @@ acpi_getpropint(struct aml_node *node, c
                if (strcmp(res->v_package[0]->v_string, prop) == 0)
                        return res->v_package[1]->v_integer;
        }
-       
+
        return defval;
 }
 
Index: dev/acpi/acpivar.h
===================================================================
RCS file: /cvs/src/sys/dev/acpi/acpivar.h,v
retrieving revision 1.106
diff -u -p -r1.106 acpivar.h
--- dev/acpi/acpivar.h  12 Apr 2020 09:21:19 -0000      1.106
+++ dev/acpi/acpivar.h  12 Apr 2020 15:38:43 -0000
@@ -372,6 +372,7 @@ int acpi_matchhids(struct acpi_attach_ar
 int    acpi_parsehid(struct aml_node *, void *, char *, char *, size_t);
 int64_t        acpi_getsta(struct acpi_softc *sc, struct aml_node *);
 
+int    acpi_getprop(struct aml_node *, const char *, void *, int);
 uint32_t acpi_getpropint(struct aml_node *, const char *, uint32_t);
 
 int    acpi_record_event(struct acpi_softc *, u_int);
Index: dev/acpi/files.acpi
===================================================================
RCS file: /cvs/src/sys/dev/acpi/files.acpi,v
retrieving revision 1.53
diff -u -p -r1.53 files.acpi
--- dev/acpi/files.acpi 23 Dec 2019 08:05:42 -0000      1.53
+++ dev/acpi/files.acpi 12 Apr 2020 15:38:43 -0000
@@ -212,3 +212,7 @@ file        dev/acpi/ccp_acpi.c             ccp_acpi
 device amdgpio
 attach amdgpio at acpi
 file   dev/acpi/amdgpio.c              amdgpio
+
+# Broadcom BC7XXX Ethernet controller
+attach bse at acpi with bse_acpi
+file   dev/acpi/if_bse_acpi.c          bse_acpi
Index: dev/acpi/if_bse_acpi.c
===================================================================
RCS file: dev/acpi/if_bse_acpi.c
diff -N dev/acpi/if_bse_acpi.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/acpi/if_bse_acpi.c      12 Apr 2020 15:38:43 -0000
@@ -0,0 +1,172 @@
+/*     $OpenBSD$       */
+/*
+ * Copyright (c) 2020 Mark Kettenis
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <dev/acpi/acpireg.h>
+#include <dev/acpi/acpivar.h>
+#include <dev/acpi/acpidev.h>
+#include <dev/acpi/amltypes.h>
+#include <dev/acpi/dsdt.h>
+
+
+#include <dev/mii/miivar.h>
+
+#include <dev/ic/bcmgenetvar.h>
+
+struct bse_acpi_softc {
+       struct genet_softc sc;
+       struct acpi_softc *sc_acpi;
+       struct aml_node *sc_node;
+
+       bus_addr_t      sc_addr;
+       bus_size_t      sc_size;
+
+       int             sc_irq;
+       int             sc_irq_flags;
+};
+
+int    bse_acpi_match(struct device *, void *, void *);
+void   bse_acpi_attach(struct device *, struct device *, void *);
+
+struct cfattach bse_acpi_ca = {
+       sizeof(struct bse_acpi_softc), bse_acpi_match, bse_acpi_attach
+};
+
+const char *bse_hids[] = {
+       "BCM6E4E",
+       NULL
+};
+
+int    bse_acpi_parse_resources(int, union acpi_resource *, void *);
+
+int
+bse_acpi_match(struct device *parent, void *match, void *aux)
+{
+       struct acpi_attach_args *aaa = aux;
+       struct cfdata *cf = match;
+
+       return acpi_matchhids(aaa, bse_hids, cf->cf_driver->cd_name);
+}
+
+void
+bse_acpi_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct bse_acpi_softc *sc = (struct bse_acpi_softc *)self;
+       struct acpi_attach_args *aaa = aux;
+       struct aml_value res;
+       char phy_mode[16] = { 0 };
+       int error;
+
+       sc->sc_acpi = (struct acpi_softc *)parent;
+       sc->sc_node = aaa->aaa_node;
+       printf(" %s", sc->sc_node->name);
+
+       if (aml_evalname(sc->sc_acpi, sc->sc_node, "_CRS", 0, NULL, &res)) {
+               printf(": can't find registers\n");
+               return;
+       }
+
+       aml_parse_resource(&res, bse_acpi_parse_resources, sc);
+       printf(" addr 0x%lx/0x%lx", sc->sc_addr, sc->sc_size);
+       if (sc->sc_addr == 0 || sc->sc_size == 0) {
+               printf("\n");
+               return;
+       }
+
+       printf(" irq %d", sc->sc_irq);
+
+       sc->sc.sc_bst = aaa->aaa_memt;
+       sc->sc.sc_dmat = aaa->aaa_dmat;
+
+       if (bus_space_map(sc->sc.sc_bst, sc->sc_addr, sc->sc_size, 0,
+           &sc->sc.sc_bsh)) {
+               printf(": can't map registers\n");
+               return;
+       }
+
+       sc->sc.sc_ih = acpi_intr_establish(sc->sc_irq, sc->sc_irq_flags,
+           IPL_NET, genet_intr, sc, sc->sc.sc_dev.dv_xname);
+       if (sc->sc.sc_ih == NULL) {
+               printf(": can't establish interrupt\n");
+               goto unmap;
+       }
+
+       /*
+        * UEFI firmware initializes the hardware MAC address
+        * registers.  Read them here before we reset the hardware.
+        */
+       genet_lladdr_read(&sc->sc, sc->sc.sc_lladdr);
+
+       acpi_getprop(sc->sc_node, "phy-mode", phy_mode, sizeof(phy_mode));
+       if (strcmp(phy_mode, "rgmii-id") == 0)
+               sc->sc.sc_phy_mode = GENET_PHY_MODE_RGMII_ID;
+       else if (strcmp(phy_mode, "rgmii-rxid") == 0)
+               sc->sc.sc_phy_mode = GENET_PHY_MODE_RGMII_RXID;
+       else if (strcmp(phy_mode, "rgmii-txid") == 0)
+               sc->sc.sc_phy_mode = GENET_PHY_MODE_RGMII_TXID;
+       else
+               sc->sc.sc_phy_mode = GENET_PHY_MODE_RGMII;
+
+       sc->sc.sc_phy_id = MII_PHY_ANY;
+       error = genet_attach(&sc->sc);
+       if (error)
+               goto disestablish;
+
+       return;
+
+disestablish:
+#ifdef notyet
+       acpi_intr_disestablish(sc->sc.sc_ih);
+#endif
+unmap:
+       bus_space_unmap(sc->sc.sc_bst, sc->sc.sc_bsh, sc->sc_size);
+       return;
+}
+
+int
+bse_acpi_parse_resources(int crsidx, union acpi_resource *crs, void *arg)
+{
+       struct bse_acpi_softc *sc = arg;
+       int type = AML_CRSTYPE(crs);
+
+       switch (type) {
+       case LR_MEM32FIXED:
+               /* XHCI registers are specified by the first resource. */
+               if (sc->sc_size == 0) {
+                       sc->sc_addr = crs->lr_m32fixed._bas;
+                       sc->sc_size = crs->lr_m32fixed._len;
+               }
+               break;
+       case LR_EXTIRQ:
+               sc->sc_irq = crs->lr_extirq.irq[0];
+               sc->sc_irq_flags = crs->lr_extirq.flags;
+               break;
+       }
+
+       return 0;
+}
Index: dev/ic/bcmgenet.c
===================================================================
RCS file: dev/ic/bcmgenet.c
diff -N dev/ic/bcmgenet.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/ic/bcmgenet.c   12 Apr 2020 15:38:43 -0000
@@ -0,0 +1,1020 @@
+/* $OpenBSD$ */
+/* $NetBSD: bcmgenet.c,v 1.3 2020/02/27 17:30:07 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2020 Jared McNeill <jmcne...@invisible.ca>
+ * Copyright (c) 2020 Mark Kettenis <kette...@openbsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, 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.
+ */
+
+/*
+ * Broadcom GENETv5
+ */
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mbuf.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/timeout.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/bpf.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/mii/miivar.h>
+
+#include <dev/ic/bcmgenetreg.h>
+#include <dev/ic/bcmgenetvar.h>
+
+CTASSERT(MCLBYTES == 2048);
+
+#ifdef GENET_DEBUG
+#define        DPRINTF(...)    printf(##__VA_ARGS__)
+#else
+#define        DPRINTF(...)    ((void)0)
+#endif
+
+#define        TX_SKIP(n, o)           (((n) + (o)) & (GENET_DMA_DESC_COUNT - 
1))
+#define        TX_NEXT(n)              TX_SKIP(n, 1)
+#define        RX_NEXT(n)              (((n) + 1) & (GENET_DMA_DESC_COUNT - 1))
+
+#define        TX_MAX_SEGS             128
+#define        TX_DESC_COUNT           GENET_DMA_DESC_COUNT
+#define        RX_DESC_COUNT           GENET_DMA_DESC_COUNT
+#define        MII_BUSY_RETRY          1000
+#define        GENET_MAX_MDF_FILTER    17
+
+#define        RD4(sc, reg)                    \
+       bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define        WR4(sc, reg, val)               \
+       bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+struct cfdriver bse_cd = {
+       0, "bse", DV_IFNET
+};
+
+int
+genet_media_change(struct ifnet *ifp)
+{
+       struct genet_softc *sc = ifp->if_softc;
+
+       if (LIST_FIRST(&sc->sc_mii.mii_phys))
+               mii_mediachg(&sc->sc_mii);
+
+       return (0);
+}
+
+void
+genet_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+       struct genet_softc *sc = ifp->if_softc;
+
+       if (LIST_FIRST(&sc->sc_mii.mii_phys)) {
+               mii_pollstat(&sc->sc_mii);
+               ifmr->ifm_active = sc->sc_mii.mii_media_active;
+               ifmr->ifm_status = sc->sc_mii.mii_media_status;
+       }
+}
+
+int
+genet_mii_readreg(struct device *dev, int phy, int reg)
+{
+       struct genet_softc *sc = (struct genet_softc *)dev;
+       int retry;
+
+       WR4(sc, GENET_MDIO_CMD,
+           GENET_MDIO_READ | GENET_MDIO_START_BUSY |
+           __SHIFTIN(phy, GENET_MDIO_PMD) |
+           __SHIFTIN(reg, GENET_MDIO_REG));
+       for (retry = MII_BUSY_RETRY; retry > 0; retry--) {
+               if ((RD4(sc, GENET_MDIO_CMD) & GENET_MDIO_START_BUSY) == 0)
+                       return RD4(sc, GENET_MDIO_CMD) & 0xffff;
+               delay(10);
+       }
+
+       printf("%s: phy read timeout, phy=%d reg=%d\n",
+           sc->sc_dev.dv_xname, phy, reg);
+       return 0;
+}
+
+void
+genet_mii_writereg(struct device *dev, int phy, int reg, int val)
+{
+       struct genet_softc *sc = (struct genet_softc *)dev;
+       int retry;
+
+       WR4(sc, GENET_MDIO_CMD,
+           val | GENET_MDIO_WRITE | GENET_MDIO_START_BUSY |
+           __SHIFTIN(phy, GENET_MDIO_PMD) |
+           __SHIFTIN(reg, GENET_MDIO_REG));
+       for (retry = MII_BUSY_RETRY; retry > 0; retry--) {
+               if ((RD4(sc, GENET_MDIO_CMD) & GENET_MDIO_START_BUSY) == 0)
+                       return;
+               delay(10);
+       }
+
+       printf("%s: phy write timeout, phy=%d reg=%d\n",
+           sc->sc_dev.dv_xname, phy, reg);
+}
+
+void
+genet_update_link(struct genet_softc *sc)
+{
+       struct mii_data *mii = &sc->sc_mii;
+       uint32_t val;
+       u_int speed;
+
+       if (IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_T ||
+           IFM_SUBTYPE(mii->mii_media_active) == IFM_1000_SX)
+               speed = GENET_UMAC_CMD_SPEED_1000;
+       else if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX)
+               speed = GENET_UMAC_CMD_SPEED_100;
+       else
+               speed = GENET_UMAC_CMD_SPEED_10;
+
+       val = RD4(sc, GENET_EXT_RGMII_OOB_CTRL);
+       val &= ~GENET_EXT_RGMII_OOB_OOB_DISABLE;
+       val |= GENET_EXT_RGMII_OOB_RGMII_LINK;
+       val |= GENET_EXT_RGMII_OOB_RGMII_MODE_EN;
+       if (sc->sc_phy_mode == GENET_PHY_MODE_RGMII)
+               val |= GENET_EXT_RGMII_OOB_ID_MODE_DISABLE;
+       else
+               val &= ~GENET_EXT_RGMII_OOB_ID_MODE_DISABLE;
+       WR4(sc, GENET_EXT_RGMII_OOB_CTRL, val);
+
+       val = RD4(sc, GENET_UMAC_CMD);
+       val &= ~GENET_UMAC_CMD_SPEED;
+       val |= __SHIFTIN(speed, GENET_UMAC_CMD_SPEED);
+       WR4(sc, GENET_UMAC_CMD, val);
+}
+
+void
+genet_mii_statchg(struct device *self)
+{
+       struct genet_softc *sc = (struct genet_softc *)self;
+
+       genet_update_link(sc);
+}
+
+void
+genet_setup_txdesc(struct genet_softc *sc, int index, int flags,
+    bus_addr_t paddr, u_int len)
+{
+       uint32_t status;
+
+       status = flags | __SHIFTIN(len, GENET_TX_DESC_STATUS_BUFLEN);
+       ++sc->sc_tx.queued;
+
+       WR4(sc, GENET_TX_DESC_ADDRESS_LO(index), (uint32_t)paddr);
+       WR4(sc, GENET_TX_DESC_ADDRESS_HI(index), (uint32_t)(paddr >> 32));
+       WR4(sc, GENET_TX_DESC_STATUS(index), status);
+}
+
+int
+genet_setup_txbuf(struct genet_softc *sc, int index, struct mbuf *m)
+{
+       bus_dma_segment_t *segs;
+       int error, nsegs, cur, i;
+       uint32_t flags;
+
+       /*
+        * XXX Hardware doesn't seem to like small fragments.  For now
+        * just look ate the first fragment and defrag if it is
+        * smaller than the minimum Ethernet packet size.
+        */
+       if (m->m_len < ETHER_MIN_LEN - ETHER_CRC_LEN) {
+               if (m_defrag(m, M_DONTWAIT))
+                       return 0;
+       }
+
+       error = bus_dmamap_load_mbuf(sc->sc_tx.buf_tag,
+           sc->sc_tx.buf_map[index].map, m, BUS_DMA_WRITE | BUS_DMA_NOWAIT);
+       if (error == EFBIG) {
+               if (m_defrag(m, M_DONTWAIT))
+                       return 0;
+               error = bus_dmamap_load_mbuf(sc->sc_tx.buf_tag,
+                   sc->sc_tx.buf_map[index].map, m,
+                   BUS_DMA_WRITE | BUS_DMA_NOWAIT);
+       }
+       if (error != 0)
+               return 0;
+
+       segs = sc->sc_tx.buf_map[index].map->dm_segs;
+       nsegs = sc->sc_tx.buf_map[index].map->dm_nsegs;
+
+       if (sc->sc_tx.queued >= GENET_DMA_DESC_COUNT - nsegs) {
+               bus_dmamap_unload(sc->sc_tx.buf_tag,
+                   sc->sc_tx.buf_map[index].map);
+               return -1;
+       }
+
+       flags = GENET_TX_DESC_STATUS_SOP |
+               GENET_TX_DESC_STATUS_CRC |
+               GENET_TX_DESC_STATUS_QTAG;
+
+       for (cur = index, i = 0; i < nsegs; i++) {
+               sc->sc_tx.buf_map[cur].mbuf = (i == 0 ? m : NULL);
+               if (i == nsegs - 1)
+                       flags |= GENET_TX_DESC_STATUS_EOP;
+
+               genet_setup_txdesc(sc, cur, flags, segs[i].ds_addr,
+                   segs[i].ds_len);
+
+               if (i == 0) {
+                       flags &= ~GENET_TX_DESC_STATUS_SOP;
+                       flags &= ~GENET_TX_DESC_STATUS_CRC;
+               }
+               cur = TX_NEXT(cur);
+       }
+
+       bus_dmamap_sync(sc->sc_tx.buf_tag, sc->sc_tx.buf_map[index].map,
+           0, sc->sc_tx.buf_map[index].map->dm_mapsize, BUS_DMASYNC_PREWRITE);
+
+       return nsegs;
+}
+
+void
+genet_setup_rxdesc(struct genet_softc *sc, int index,
+    bus_addr_t paddr, bus_size_t len)
+{
+       WR4(sc, GENET_RX_DESC_ADDRESS_LO(index), (uint32_t)paddr);
+       WR4(sc, GENET_RX_DESC_ADDRESS_HI(index), (uint32_t)(paddr >> 32));
+}
+
+int
+genet_setup_rxbuf(struct genet_softc *sc, int index, struct mbuf *m)
+{
+       int error;
+
+       error = bus_dmamap_load_mbuf(sc->sc_rx.buf_tag,
+           sc->sc_rx.buf_map[index].map, m, BUS_DMA_READ | BUS_DMA_NOWAIT);
+       if (error != 0)
+               return error;
+
+       bus_dmamap_sync(sc->sc_rx.buf_tag, sc->sc_rx.buf_map[index].map,
+           0, sc->sc_rx.buf_map[index].map->dm_mapsize,
+           BUS_DMASYNC_PREREAD);
+
+       sc->sc_rx.buf_map[index].mbuf = m;
+       genet_setup_rxdesc(sc, index,
+           sc->sc_rx.buf_map[index].map->dm_segs[0].ds_addr,
+           sc->sc_rx.buf_map[index].map->dm_segs[0].ds_len);
+
+       return 0;
+}
+
+struct mbuf *
+genet_alloc_mbufcl(struct genet_softc *sc)
+{
+       struct mbuf *m;
+
+       m = MCLGETI(NULL, M_DONTWAIT, NULL, MCLBYTES);
+       if (m != NULL)
+               m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
+
+       return m;
+}
+
+void
+genet_fill_rx_ring(struct genet_softc *sc, int qid)
+{
+       struct mbuf *m;
+       uint32_t cidx, index, total;
+       u_int slots;
+       int error;
+
+       cidx = sc->sc_rx.cidx;
+       total = (sc->sc_rx.pidx - cidx) & 0xffff;
+       KASSERT(total <= RX_DESC_COUNT);
+
+       index = sc->sc_rx.cidx & (RX_DESC_COUNT - 1);
+       for (slots = if_rxr_get(&sc->sc_rx_ring, total);
+            slots > 0; slots--) {
+               if ((m = genet_alloc_mbufcl(sc)) == NULL) {
+                       printf("%s: cannot allocate RX mbuf\n",
+                           sc->sc_dev.dv_xname);
+                       break;
+               }
+               error = genet_setup_rxbuf(sc, index, m);
+               if (error != 0) {
+                       printf("%s: cannot create RX buffer\n",
+                           sc->sc_dev.dv_xname);
+                       m_freem(m);
+                       break;
+               }
+
+               cidx = (cidx + 1) & 0xffff;
+               index = RX_NEXT(index);
+       }
+       if_rxr_put(&sc->sc_rx_ring, slots);
+
+       if (sc->sc_rx.cidx != cidx) {
+               sc->sc_rx.cidx = cidx;
+               WR4(sc, GENET_RX_DMA_CONS_INDEX(qid), sc->sc_rx.cidx);
+       }
+
+       if (if_rxr_inuse(&sc->sc_rx_ring) == 0)
+               timeout_add(&sc->sc_rxto, 1);
+}
+
+void
+genet_rxtick(void *arg)
+{
+       genet_fill_rx_ring(arg, GENET_DMA_DEFAULT_QUEUE);
+}
+
+void
+genet_enable_intr(struct genet_softc *sc)
+{
+       WR4(sc, GENET_INTRL2_CPU_CLEAR_MASK,
+           GENET_IRQ_TXDMA_DONE | GENET_IRQ_RXDMA_DONE);
+}
+
+void
+genet_disable_intr(struct genet_softc *sc)
+{
+       /* Disable interrupts */
+       WR4(sc, GENET_INTRL2_CPU_SET_MASK, 0xffffffff);
+       WR4(sc, GENET_INTRL2_CPU_CLEAR, 0xffffffff);
+}
+
+void
+genet_tick(void *softc)
+{
+       struct genet_softc *sc = softc;
+       struct mii_data *mii = &sc->sc_mii;
+       int s = splnet();
+
+       mii_tick(mii);
+       timeout_add_sec(&sc->sc_stat_ch, 1);
+
+       splx(s);
+}
+
+void
+genet_setup_rxfilter_mdf(struct genet_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);
+}
+
+void
+genet_setup_rxfilter(struct genet_softc *sc)
+{
+       struct arpcom *ac = &sc->sc_ac;
+       struct ifnet *ifp = &ac->ac_if;
+       struct ether_multistep step;
+       struct ether_multi *enm;
+       uint32_t cmd, mdf_ctrl;
+       u_int n;
+
+       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.
+        */
+       ETHER_FIRST_MULTI(step, ac, enm);
+       for (n = 2; enm != NULL; n++)
+               ETHER_NEXT_MULTI(step, enm);
+
+       if (n > GENET_MAX_MDF_FILTER || ac->ac_multirangecnt > 0)
+               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;
+               genet_setup_rxfilter_mdf(sc, 0, etherbroadcastaddr);
+               genet_setup_rxfilter_mdf(sc, 1, LLADDR(ifp->if_sadl));
+               ETHER_FIRST_MULTI(step, ac, enm);
+               for (n = 2; enm != NULL; n++) {
+                       genet_setup_rxfilter_mdf(sc, n, enm->enm_addrlo);
+                       ETHER_NEXT_MULTI(step, enm);
+               }
+               mdf_ctrl = __BITS(GENET_MAX_MDF_FILTER - 1,
+                                 GENET_MAX_MDF_FILTER - n);
+       }
+
+       WR4(sc, GENET_UMAC_CMD, cmd);
+       WR4(sc, GENET_UMAC_MDF_CTRL, mdf_ctrl);
+}
+
+int
+genet_reset(struct genet_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);
+
+       return 0;
+}
+
+void
+genet_init_rings(struct genet_softc *sc, int qid)
+{
+       uint32_t val;
+
+       /* TX ring */
+
+       sc->sc_tx.next = 0;
+       sc->sc_tx.queued = 0;
+       sc->sc_tx.cidx = sc->sc_tx.pidx = 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), sc->sc_tx.cidx);
+       WR4(sc, GENET_TX_DMA_PROD_INDEX(qid), sc->sc_tx.pidx);
+       WR4(sc, GENET_TX_DMA_RING_BUF_SIZE(qid),
+           __SHIFTIN(TX_DESC_COUNT, GENET_TX_DMA_RING_BUF_SIZE_DESC_COUNT) |
+           __SHIFTIN(MCLBYTES, GENET_TX_DMA_RING_BUF_SIZE_BUF_LENGTH));
+       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(GENET_DMA_DEFAULT_QUEUE);
+       WR4(sc, GENET_TX_DMA_CTRL, val);
+
+       /* RX ring */
+
+       sc->sc_rx.next = 0;
+       sc->sc_rx.cidx = 0;
+       sc->sc_rx.pidx = RX_DESC_COUNT;
+
+       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), sc->sc_rx.pidx);
+       WR4(sc, GENET_RX_DMA_CONS_INDEX(qid), sc->sc_rx.cidx);
+       WR4(sc, GENET_RX_DMA_RING_BUF_SIZE(qid),
+           __SHIFTIN(RX_DESC_COUNT, GENET_RX_DMA_RING_BUF_SIZE_DESC_COUNT) |
+           __SHIFTIN(MCLBYTES, GENET_RX_DMA_RING_BUF_SIZE_BUF_LENGTH));
+       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),
+           __SHIFTIN(5, GENET_RX_DMA_XON_XOFF_THRES_LO) |
+           __SHIFTIN(RX_DESC_COUNT >> 4, GENET_RX_DMA_XON_XOFF_THRES_HI));
+       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 */
+
+       if_rxr_init(&sc->sc_rx_ring, 2, RX_DESC_COUNT);
+       genet_fill_rx_ring(sc, qid);
+
+       /* Enable receive DMA */
+       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);
+}
+
+int
+genet_init(struct genet_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_ac.ac_if;
+       struct mii_data *mii = &sc->sc_mii;
+       uint32_t val;
+       uint8_t *enaddr = LLADDR(ifp->if_sadl);
+
+       if (ifp->if_flags & IFF_RUNNING)
+               return 0;
+
+       if (sc->sc_phy_mode == GENET_PHY_MODE_RGMII ||
+           sc->sc_phy_mode == GENET_PHY_MODE_RGMII_ID ||
+           sc->sc_phy_mode == GENET_PHY_MODE_RGMII_RXID ||
+           sc->sc_phy_mode == GENET_PHY_MODE_RGMII_TXID)
+               WR4(sc, GENET_SYS_PORT_CTRL,
+                   GENET_SYS_PORT_MODE_EXT_GPHY);
+
+       /* 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);
+
+       /* Setup RX filter */
+       genet_setup_rxfilter(sc);
+
+       /* Setup TX/RX rings */
+       genet_init_rings(sc, GENET_DMA_DEFAULT_QUEUE);
+
+       /* 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 */
+       genet_enable_intr(sc);
+
+       ifp->if_flags |= IFF_RUNNING;
+       ifq_clr_oactive(&ifp->if_snd);
+
+       mii_mediachg(mii);
+       timeout_add_sec(&sc->sc_stat_ch, 1);
+
+       return 0;
+}
+
+void
+genet_stop(struct genet_softc *sc)
+{
+       struct ifnet *ifp = &sc->sc_ac.ac_if;
+       struct genet_bufmap *bmap;
+       uint32_t val;
+       int i;
+
+       timeout_del(&sc->sc_rxto);
+       timeout_del(&sc->sc_stat_ch);
+
+       mii_down(&sc->sc_mii);
+
+       /* Disable receiver */
+       val = RD4(sc, GENET_UMAC_CMD);
+       val &= ~GENET_UMAC_CMD_RXEN;
+       WR4(sc, GENET_UMAC_CMD, val);
+
+       /* Stop receive DMA */
+       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);
+
+       /* Stop transmit DMA */
+       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);
+
+       /* Flush data in the TX FIFO */
+       WR4(sc, GENET_UMAC_TX_FLUSH, 1);
+       delay(10);
+       WR4(sc, GENET_UMAC_TX_FLUSH, 0);
+
+       /* Disable transmitter */
+       val = RD4(sc, GENET_UMAC_CMD);
+       val &= ~GENET_UMAC_CMD_TXEN;
+       WR4(sc, GENET_UMAC_CMD, val);
+
+       /* Disable interrupts */
+       genet_disable_intr(sc);
+
+       ifp->if_flags &= ~IFF_RUNNING;
+       ifq_clr_oactive(&ifp->if_snd);
+       ifp->if_timer = 0;
+
+       intr_barrier(sc->sc_ih);
+
+       /* Clean RX ring. */
+       for (i = 0; i < RX_DESC_COUNT; i++) {
+               bmap = &sc->sc_rx.buf_map[i];
+               if (bmap->mbuf) {
+                       bus_dmamap_sync(sc->sc_dmat, bmap->map, 0,
+                           bmap->map->dm_mapsize, BUS_DMASYNC_POSTREAD);
+                       bus_dmamap_unload(sc->sc_dmat, bmap->map);
+                       m_freem(bmap->mbuf);
+                       bmap->mbuf = NULL;
+               }
+       }
+
+       /* Clean TX ring. */
+       for (i = 0; i < TX_DESC_COUNT; i++) {
+               bmap = &sc->sc_tx.buf_map[i];
+               if (bmap->mbuf) {
+                       bus_dmamap_sync(sc->sc_dmat, bmap->map, 0,
+                           bmap->map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
+                       bus_dmamap_unload(sc->sc_dmat, bmap->map);
+                       m_freem(bmap->mbuf);
+                       bmap->mbuf = NULL;
+               }
+       }
+}
+
+void
+genet_rxintr(struct genet_softc *sc, int qid)
+{
+       struct ifnet *ifp = &sc->sc_ac.ac_if;
+       struct mbuf_list ml = MBUF_LIST_INITIALIZER();
+       struct mbuf *m;
+       int index, len, n;
+       uint32_t status, pidx, total;
+
+       pidx = RD4(sc, GENET_RX_DMA_PROD_INDEX(qid)) & 0xffff;
+       total = (pidx - sc->sc_rx.pidx) & 0xffff;
+
+       DPRINTF("RX pidx=%08x total=%d\n", pidx, total);
+
+       index = sc->sc_rx.next;
+       for (n = 0; n < total; n++) {
+               status = RD4(sc, GENET_RX_DESC_STATUS(index));
+               len = __SHIFTOUT(status, GENET_RX_DESC_STATUS_BUFLEN);
+
+               /* XXX check for errors */
+
+               bus_dmamap_sync(sc->sc_rx.buf_tag, sc->sc_rx.buf_map[index].map,
+                   0, sc->sc_rx.buf_map[index].map->dm_mapsize,
+                   BUS_DMASYNC_POSTREAD);
+               bus_dmamap_unload(sc->sc_rx.buf_tag, 
sc->sc_rx.buf_map[index].map);
+
+               DPRINTF("RX [#%d] index=%02x status=%08x len=%d adj_len=%d\n",
+                   n, index, status, len, len - ETHER_ALIGN);
+
+               m = sc->sc_rx.buf_map[index].mbuf;
+               sc->sc_rx.buf_map[index].mbuf = NULL;
+
+               if (len > ETHER_ALIGN) {
+                       m_adj(m, ETHER_ALIGN);
+
+                       m->m_len = m->m_pkthdr.len = len - ETHER_ALIGN;
+                       m->m_nextpkt = NULL;
+
+                       ml_enqueue(&ml, m);
+               } else {
+                       ifp->if_ierrors++;
+                       m_freem(m);
+               }
+
+               if_rxr_put(&sc->sc_rx_ring, 1);
+
+               index = RX_NEXT(index);
+       }
+
+       if (sc->sc_rx.pidx != pidx) {
+               sc->sc_rx.next = index;
+               sc->sc_rx.pidx = pidx;
+
+               genet_fill_rx_ring(sc, qid);
+               if_input(ifp, &ml);
+       }
+}
+
+void
+genet_txintr(struct genet_softc *sc, int qid)
+{
+       struct ifnet *ifp = &sc->sc_ac.ac_if;
+       struct genet_bufmap *bmap;
+       uint32_t cidx, total;
+       int i;
+
+       cidx = RD4(sc, GENET_TX_DMA_CONS_INDEX(qid)) & 0xffff;
+       total = (cidx - sc->sc_tx.cidx) & 0xffff;
+
+       for (i = sc->sc_tx.next; sc->sc_tx.queued > 0 && total > 0;
+            i = TX_NEXT(i), total--) {
+               /* XXX check for errors */
+
+               bmap = &sc->sc_tx.buf_map[i];
+               if (bmap->mbuf != NULL) {
+                       bus_dmamap_sync(sc->sc_tx.buf_tag, bmap->map,
+                           0, bmap->map->dm_mapsize,
+                           BUS_DMASYNC_POSTWRITE);
+                       bus_dmamap_unload(sc->sc_tx.buf_tag, bmap->map);
+                       m_freem(bmap->mbuf);
+                       bmap->mbuf = NULL;
+               }
+
+               --sc->sc_tx.queued;
+       }
+
+       if (sc->sc_tx.queued == 0)
+               ifp->if_timer = 0;
+
+       if (sc->sc_tx.cidx != cidx) {
+               sc->sc_tx.next = i;
+               sc->sc_tx.cidx = cidx;
+
+               if (ifq_is_oactive(&ifp->if_snd))
+                       ifq_restart(&ifp->if_snd);
+       }
+}
+
+void
+genet_start(struct ifnet *ifp)
+{
+       struct genet_softc *sc = ifp->if_softc;
+       struct mbuf *m;
+       const int qid = GENET_DMA_DEFAULT_QUEUE;
+       int nsegs, index, cnt;
+
+       if ((ifp->if_flags & IFF_RUNNING) == 0)
+               return;
+       if (ifq_is_oactive(&ifp->if_snd))
+               return;
+
+       index = sc->sc_tx.pidx & (TX_DESC_COUNT - 1);
+       cnt = 0;
+
+       for (;;) {
+               m = ifq_deq_begin(&ifp->if_snd);
+               if (m == NULL)
+                       break;
+
+               nsegs = genet_setup_txbuf(sc, index, m);
+               if (nsegs == -1) {
+                       ifq_deq_rollback(&ifp->if_snd, m);
+                       ifq_set_oactive(&ifp->if_snd);
+                       break;
+               }
+               if (nsegs == 0) {
+                       ifq_deq_commit(&ifp->if_snd, m);
+                       m_freem(m);
+                       ifp->if_oerrors++;
+                       continue;
+               }
+               ifq_deq_commit(&ifp->if_snd, m);
+               if (ifp->if_bpf)
+                       bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
+
+               index = TX_SKIP(index, nsegs);
+
+               sc->sc_tx.pidx = (sc->sc_tx.pidx + nsegs) & 0xffff;
+               cnt++;
+       }
+
+       if (cnt != 0) {
+               WR4(sc, GENET_TX_DMA_PROD_INDEX(qid), sc->sc_tx.pidx);
+               ifp->if_timer = 5;
+       }
+}
+
+int
+genet_intr(void *arg)
+{
+       struct genet_softc *sc = arg;
+       struct ifnet *ifp = &sc->sc_ac.ac_if;
+       uint32_t val;
+
+       val = RD4(sc, GENET_INTRL2_CPU_STAT);
+       val &= ~RD4(sc, GENET_INTRL2_CPU_STAT_MASK);
+       WR4(sc, GENET_INTRL2_CPU_CLEAR, val);
+
+       if (val & GENET_IRQ_RXDMA_DONE)
+               genet_rxintr(sc, GENET_DMA_DEFAULT_QUEUE);
+
+       if (val & GENET_IRQ_TXDMA_DONE) {
+               genet_txintr(sc, GENET_DMA_DEFAULT_QUEUE);
+               if (ifq_is_oactive(&ifp->if_snd))
+                       ifq_restart(&ifp->if_snd);
+       }
+
+       return 1;
+}
+
+int
+genet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t addr)
+{
+       struct genet_softc *sc = ifp->if_softc;
+       struct ifreq *ifr = (struct ifreq *)addr;
+       int error = 0, s;
+
+       s = splnet();
+
+       switch (cmd) {
+       case SIOCSIFADDR:
+               ifp->if_flags |= IFF_UP;
+               /* FALLTHROUGH */
+       case SIOCSIFFLAGS:
+               if (ifp->if_flags & IFF_UP) {
+                       if (ifp->if_flags & IFF_RUNNING)
+                               error = ENETRESET;
+                       else
+                               genet_init(sc);
+               } else {
+                       if (ifp->if_flags & IFF_RUNNING)
+                               genet_stop(sc);
+               }
+               break;
+
+       case SIOCGIFMEDIA:
+       case SIOCSIFMEDIA:
+               error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd);
+               break;
+
+       case SIOCGIFRXR:
+               error = if_rxr_ioctl((struct if_rxrinfo *)ifr->ifr_data,
+                   NULL, MCLBYTES, &sc->sc_rx_ring);
+               break;
+
+       default:
+               error = ether_ioctl(ifp, &sc->sc_ac, cmd, addr);
+               break;
+       }
+
+       if (error == ENETRESET) {
+               if (ifp->if_flags & IFF_RUNNING)
+                       genet_setup_rxfilter(sc);
+               error = 0;
+       }
+
+       splx(s);
+       return error;
+}
+
+int
+genet_setup_dma(struct genet_softc *sc, int qid)
+{
+       int error, i;
+
+       /* Setup TX ring */
+       sc->sc_tx.buf_tag = sc->sc_dmat;
+       for (i = 0; i < TX_DESC_COUNT; i++) {
+               error = bus_dmamap_create(sc->sc_tx.buf_tag, MCLBYTES,
+                   TX_MAX_SEGS, MCLBYTES, 0, BUS_DMA_WAITOK,
+                   &sc->sc_tx.buf_map[i].map);
+               if (error != 0) {
+                       printf("%s: cannot create TX buffer map\n",
+                           sc->sc_dev.dv_xname);
+                       return error;
+               }
+       }
+
+       /* Setup RX ring */
+       sc->sc_rx.buf_tag = sc->sc_dmat;
+       for (i = 0; i < RX_DESC_COUNT; i++) {
+               error = bus_dmamap_create(sc->sc_rx.buf_tag, MCLBYTES,
+                   1, MCLBYTES, 0, BUS_DMA_WAITOK,
+                   &sc->sc_rx.buf_map[i].map);
+               if (error != 0) {
+                       printf("%s: cannot create RX buffer map\n",
+                           sc->sc_dev.dv_xname);
+                       return error;
+               }
+       }
+
+       return 0;
+}
+
+int
+genet_attach(struct genet_softc *sc)
+{
+       struct mii_data *mii = &sc->sc_mii;
+       struct ifnet *ifp = &sc->sc_ac.ac_if;
+       int mii_flags = 0;
+
+       switch (sc->sc_phy_mode) {
+       case GENET_PHY_MODE_RGMII_ID:
+               mii_flags |= MIIF_RXID | MIIF_TXID;
+               break;
+       case GENET_PHY_MODE_RGMII_RXID:
+               mii_flags |= MIIF_RXID;
+               break;
+       case GENET_PHY_MODE_RGMII_TXID:
+               mii_flags |= MIIF_TXID;
+               break;
+       case GENET_PHY_MODE_RGMII:
+       default:
+               break;
+       }
+
+       printf(": address %s\n", ether_sprintf(sc->sc_lladdr));
+
+       /* Soft reset EMAC core */
+       genet_reset(sc);
+
+       /* Setup DMA descriptors */
+       if (genet_setup_dma(sc, GENET_DMA_DEFAULT_QUEUE) != 0) {
+               printf("%s: failed to setup DMA descriptors\n",
+                   sc->sc_dev.dv_xname);
+               return EINVAL;
+       }
+
+       timeout_set(&sc->sc_stat_ch, genet_tick, sc);
+       timeout_set(&sc->sc_rxto, genet_rxtick, sc);
+
+       /* Setup ethernet interface */
+       ifp->if_softc = sc;
+       snprintf(ifp->if_xname, IFNAMSIZ, "%s", sc->sc_dev.dv_xname);
+       ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+       ifp->if_start = genet_start;
+       ifp->if_ioctl = genet_ioctl;
+       IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+
+       /* 802.1Q VLAN-sized frames are supported */
+       ifp->if_capabilities = IFCAP_VLAN_MTU;
+
+       /* Attach MII driver */
+       ifmedia_init(&mii->mii_media, 0, genet_media_change, 
genet_media_status);
+       mii->mii_ifp = ifp;
+       mii->mii_readreg = genet_mii_readreg;
+       mii->mii_writereg = genet_mii_writereg;
+       mii->mii_statchg = genet_mii_statchg;
+       mii_attach(&sc->sc_dev, mii, 0xffffffff, sc->sc_phy_id,
+           MII_OFFSET_ANY, mii_flags);
+
+       if (LIST_EMPTY(&mii->mii_phys)) {
+               printf("%s: no PHY found!\n", sc->sc_dev.dv_xname);
+               ifmedia_add(&mii->mii_media, IFM_ETHER|IFM_MANUAL, 0, NULL);
+               ifmedia_set(&mii->mii_media, IFM_ETHER|IFM_MANUAL);
+       }
+       ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
+
+       /* Attach interface */
+       if_attach(ifp);
+
+       /* Attach ethernet interface */
+       ether_ifattach(ifp);
+
+       return 0;
+}
+
+void
+genet_lladdr_read(struct genet_softc *sc, uint8_t *lladdr)
+{
+       uint32_t maclo, machi;
+
+       maclo = RD4(sc, GENET_UMAC_MAC0);
+       machi = RD4(sc, GENET_UMAC_MAC1);
+
+       lladdr[0] = (maclo >> 24) & 0xff;
+       lladdr[1] = (maclo >> 16) & 0xff;
+       lladdr[2] = (maclo >> 8) & 0xff;
+       lladdr[3] = (maclo >> 0) & 0xff;
+       lladdr[4] = (machi >> 8) & 0xff;
+       lladdr[5] = (machi >> 0) & 0xff;
+}
Index: dev/ic/bcmgenetreg.h
===================================================================
RCS file: dev/ic/bcmgenetreg.h
diff -N dev/ic/bcmgenetreg.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/ic/bcmgenetreg.h        12 Apr 2020 15:38:43 -0000
@@ -0,0 +1,175 @@
+/* $OpenBSD$ */
+/* $NetBSD: bcmgenetreg.h,v 1.2 2020/02/22 13:41:41 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2020 Jared McNeill <jmcne...@invisible.ca>
+ * 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, 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.
+ */
+
+/*
+ * Broadcom GENETv5
+ */
+
+#ifndef _BCMGENETREG_H
+#define _BCMGENETREG_H
+
+#define __BIT(__n)     (1U << (__n))
+#define __BITS(__n, __m)  ((__BIT((__n) - (__m) + 1) - 1) << (__m))
+
+#define        __LOWEST_SET_BIT(__mask) ((((__mask) - 1) & (__mask)) ^ 
(__mask))
+#define        __SHIFTOUT(__x, __mask) (((__x) & (__mask)) / 
__LOWEST_SET_BIT(__mask))
+#define        __SHIFTIN(__x, __mask) ((__x) * __LOWEST_SET_BIT(__mask))
+
+#define        GENET_SYS_REV_CTRL              0x000
+#define         GENET_SYS_REV_MAJOR            __BITS(27,24)
+#define         GENET_SYS_REV_MINOR            __BITS(19,16)
+#define        GENET_SYS_PORT_CTRL             0x004
+#define         GENET_SYS_PORT_MODE_EXT_GPHY   3
+#define        GENET_SYS_RBUF_FLUSH_CTRL       0x008
+#define         GENET_SYS_RBUF_FLUSH_RESET     __BIT(1)
+#define        GENET_SYS_TBUF_FLUSH_CTRL       0x00c
+#define        GENET_EXT_RGMII_OOB_CTRL        0x08c
+#define         GENET_EXT_RGMII_OOB_ID_MODE_DISABLE    __BIT(16)
+#define         GENET_EXT_RGMII_OOB_RGMII_MODE_EN      __BIT(6)
+#define         GENET_EXT_RGMII_OOB_OOB_DISABLE        __BIT(5)
+#define         GENET_EXT_RGMII_OOB_RGMII_LINK         __BIT(4)
+#define        GENET_INTRL2_CPU_STAT           0x200
+#define        GENET_INTRL2_CPU_CLEAR          0x208
+#define        GENET_INTRL2_CPU_STAT_MASK      0x20c
+#define        GENET_INTRL2_CPU_SET_MASK       0x210
+#define        GENET_INTRL2_CPU_CLEAR_MASK     0x214
+#define         GENET_IRQ_MDIO_ERROR           __BIT(24)
+#define         GENET_IRQ_MDIO_DONE            __BIT(23)
+#define         GENET_IRQ_TXDMA_DONE           __BIT(16)
+#define         GENET_IRQ_RXDMA_DONE           __BIT(13)
+#define        GENET_RBUF_CTRL                 0x300
+#define         GENET_RBUF_BAD_DIS             __BIT(2)
+#define         GENET_RBUF_ALIGN_2B            __BIT(1)
+#define         GENET_RBUF_64B_EN              __BIT(0)
+#define        GENET_RBUF_TBUF_SIZE_CTRL       0x3b4
+#define        GENET_UMAC_CMD                  0x808
+#define         GENET_UMAC_CMD_LCL_LOOP_EN     __BIT(15)
+#define         GENET_UMAC_CMD_SW_RESET        __BIT(13)
+#define         GENET_UMAC_CMD_PROMISC         __BIT(4)
+#define         GENET_UMAC_CMD_SPEED           __BITS(3,2)
+#define          GENET_UMAC_CMD_SPEED_10       0
+#define          GENET_UMAC_CMD_SPEED_100      1
+#define          GENET_UMAC_CMD_SPEED_1000     2
+#define         GENET_UMAC_CMD_RXEN            __BIT(1)
+#define         GENET_UMAC_CMD_TXEN            __BIT(0)
+#define        GENET_UMAC_MAC0                 0x80c
+#define        GENET_UMAC_MAC1                 0x810
+#define        GENET_UMAC_MAX_FRAME_LEN        0x814
+#define        GENET_UMAC_TX_FLUSH             0xb34
+#define        GENET_UMAC_MIB_CTRL             0xd80
+#define         GENET_UMAC_MIB_RESET_TX        __BIT(2)
+#define         GENET_UMAC_MIB_RESET_RUNT      __BIT(1)
+#define         GENET_UMAC_MIB_RESET_RX        __BIT(0)
+#define        GENET_MDIO_CMD                  0xe14
+#define         GENET_MDIO_START_BUSY          __BIT(29)
+#define         GENET_MDIO_READ                __BIT(27)
+#define         GENET_MDIO_WRITE               __BIT(26)
+#define         GENET_MDIO_PMD                 __BITS(25,21)
+#define         GENET_MDIO_REG                 __BITS(20,16)
+#define        GENET_UMAC_MDF_CTRL             0xe50
+#define        GENET_UMAC_MDF_ADDR0(n)         (0xe54 + (n) * 0x8)
+#define        GENET_UMAC_MDF_ADDR1(n)         (0xe58 + (n) * 0x8)
+
+#define        GENET_DMA_DESC_COUNT            256
+#define        GENET_DMA_DESC_SIZE             12
+#define        GENET_DMA_DEFAULT_QUEUE         16
+
+#define        GENET_DMA_RING_SIZE             0x40
+#define        GENET_DMA_RINGS_SIZE            (GENET_DMA_RING_SIZE * 
(GENET_DMA_DEFAULT_QUEUE + 1))
+
+#define        GENET_RX_BASE                   0x2000
+#define        GENET_TX_BASE                   0x4000
+
+#define        GENET_RX_DMA_RINGBASE(qid)      (GENET_RX_BASE + 0xc00 + 
GENET_DMA_RING_SIZE * (qid))
+#define        GENET_RX_DMA_WRITE_PTR_LO(qid)  (GENET_RX_DMA_RINGBASE(qid) + 
0x00)
+#define        GENET_RX_DMA_WRITE_PTR_HI(qid)  (GENET_RX_DMA_RINGBASE(qid) + 
0x04)
+#define        GENET_RX_DMA_PROD_INDEX(qid)    (GENET_RX_DMA_RINGBASE(qid) + 
0x08)
+#define        GENET_RX_DMA_CONS_INDEX(qid)    (GENET_RX_DMA_RINGBASE(qid) + 
0x0c)
+#define        GENET_RX_DMA_RING_BUF_SIZE(qid) (GENET_RX_DMA_RINGBASE(qid) + 
0x10)
+#define         GENET_RX_DMA_RING_BUF_SIZE_DESC_COUNT  __BITS(31,16)
+#define         GENET_RX_DMA_RING_BUF_SIZE_BUF_LENGTH  __BITS(15,0)
+#define        GENET_RX_DMA_START_ADDR_LO(qid) (GENET_RX_DMA_RINGBASE(qid) + 
0x14)
+#define        GENET_RX_DMA_START_ADDR_HI(qid) (GENET_RX_DMA_RINGBASE(qid) + 
0x18)
+#define        GENET_RX_DMA_END_ADDR_LO(qid)   (GENET_RX_DMA_RINGBASE(qid) + 
0x1c)
+#define        GENET_RX_DMA_END_ADDR_HI(qid)   (GENET_RX_DMA_RINGBASE(qid) + 
0x20)
+#define        GENET_RX_DMA_XON_XOFF_THRES(qid) (GENET_RX_DMA_RINGBASE(qid) + 
0x28)
+#define         GENET_RX_DMA_XON_XOFF_THRES_LO         __BITS(31,16)
+#define         GENET_RX_DMA_XON_XOFF_THRES_HI         __BITS(15,0)
+#define        GENET_RX_DMA_READ_PTR_LO(qid)   (GENET_RX_DMA_RINGBASE(qid) + 
0x2c)
+#define        GENET_RX_DMA_READ_PTR_HI(qid)   (GENET_RX_DMA_RINGBASE(qid) + 
0x30)
+
+#define        GENET_TX_DMA_RINGBASE(qid)      (GENET_TX_BASE + 0xc00 + 
GENET_DMA_RING_SIZE * (qid))
+#define        GENET_TX_DMA_READ_PTR_LO(qid)   (GENET_TX_DMA_RINGBASE(qid) + 
0x00)
+#define        GENET_TX_DMA_READ_PTR_HI(qid)   (GENET_TX_DMA_RINGBASE(qid) + 
0x04)
+#define        GENET_TX_DMA_CONS_INDEX(qid)    (GENET_TX_DMA_RINGBASE(qid) + 
0x08)
+#define        GENET_TX_DMA_PROD_INDEX(qid)    (GENET_TX_DMA_RINGBASE(qid) + 
0x0c)
+#define        GENET_TX_DMA_RING_BUF_SIZE(qid) (GENET_TX_DMA_RINGBASE(qid) + 
0x10)
+#define         GENET_TX_DMA_RING_BUF_SIZE_DESC_COUNT  __BITS(31,16)
+#define         GENET_TX_DMA_RING_BUF_SIZE_BUF_LENGTH  __BITS(15,0)
+#define        GENET_TX_DMA_START_ADDR_LO(qid) (GENET_TX_DMA_RINGBASE(qid) + 
0x14)
+#define        GENET_TX_DMA_START_ADDR_HI(qid) (GENET_TX_DMA_RINGBASE(qid) + 
0x18)
+#define        GENET_TX_DMA_END_ADDR_LO(qid)   (GENET_TX_DMA_RINGBASE(qid) + 
0x1c)
+#define        GENET_TX_DMA_END_ADDR_HI(qid)   (GENET_TX_DMA_RINGBASE(qid) + 
0x20)
+#define        GENET_TX_DMA_MBUF_DONE_THRES(qid) (GENET_TX_DMA_RINGBASE(qid) + 
0x24)
+#define        GENET_TX_DMA_FLOW_PERIOD(qid)   (GENET_TX_DMA_RINGBASE(qid) + 
0x28)
+#define        GENET_TX_DMA_WRITE_PTR_LO(qid)  (GENET_TX_DMA_RINGBASE(qid) + 
0x2c)
+#define        GENET_TX_DMA_WRITE_PTR_HI(qid)  (GENET_TX_DMA_RINGBASE(qid) + 
0x30)
+
+#define        GENET_RX_DESC_STATUS(idx)       (GENET_RX_BASE + 
GENET_DMA_DESC_SIZE * (idx) + 0x00)
+#define         GENET_RX_DESC_STATUS_BUFLEN    __BITS(27,16)
+#define         GENET_RX_DESC_STATUS_OWN       __BIT(15)
+#define         GENET_RX_DESC_STATUS_EOP       __BIT(14)
+#define         GENET_RX_DESC_STATUS_SOP       __BIT(13)
+#define         GENET_RX_DESC_STATUS_RX_ERROR  __BIT(2)
+#define        GENET_RX_DESC_ADDRESS_LO(idx)   (GENET_RX_BASE + 
GENET_DMA_DESC_SIZE * (idx) + 0x04)
+#define        GENET_RX_DESC_ADDRESS_HI(idx)   (GENET_RX_BASE + 
GENET_DMA_DESC_SIZE * (idx) + 0x08)
+
+#define        GENET_TX_DESC_STATUS(idx)       (GENET_TX_BASE + 
GENET_DMA_DESC_SIZE * (idx) + 0x00)
+#define         GENET_TX_DESC_STATUS_BUFLEN    __BITS(27,16)
+#define         GENET_TX_DESC_STATUS_OWN       __BIT(15)
+#define         GENET_TX_DESC_STATUS_EOP       __BIT(14)
+#define         GENET_TX_DESC_STATUS_SOP       __BIT(13)
+#define         GENET_TX_DESC_STATUS_QTAG      __BITS(12,7)
+#define         GENET_TX_DESC_STATUS_CRC       __BIT(6)
+#define        GENET_TX_DESC_ADDRESS_LO(idx)   (GENET_TX_BASE + 
GENET_DMA_DESC_SIZE * (idx) + 0x04)
+#define        GENET_TX_DESC_ADDRESS_HI(idx)   (GENET_TX_BASE + 
GENET_DMA_DESC_SIZE * (idx) + 0x08)
+
+#define        GENET_RX_DMA_RING_CFG           (GENET_RX_BASE + 0x1040 + 0x00)
+#define        GENET_RX_DMA_CTRL               (GENET_RX_BASE + 0x1040 + 0x04)
+#define         GENET_RX_DMA_CTRL_RBUF_EN(qid) __BIT((qid) + 1)
+#define         GENET_RX_DMA_CTRL_EN           __BIT(0)
+#define        GENET_RX_SCB_BURST_SIZE         (GENET_RX_BASE + 0x1040 + 0x0c)
+
+#define        GENET_TX_DMA_RING_CFG           (GENET_TX_BASE + 0x1040 + 0x00)
+#define        GENET_TX_DMA_CTRL               (GENET_TX_BASE + 0x1040 + 0x04)
+#define         GENET_TX_DMA_CTRL_RBUF_EN(qid) __BIT((qid) + 1)
+#define         GENET_TX_DMA_CTRL_EN           __BIT(0)
+#define        GENET_TX_SCB_BURST_SIZE         (GENET_TX_BASE + 0x1040 + 0x0c)
+
+#endif /* !_BCMGENETREG_H */
Index: dev/ic/bcmgenetvar.h
===================================================================
RCS file: dev/ic/bcmgenetvar.h
diff -N dev/ic/bcmgenetvar.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ dev/ic/bcmgenetvar.h        12 Apr 2020 15:38:43 -0000
@@ -0,0 +1,83 @@
+/* $OpenBSD$ */
+/* $NetBSD: bcmgenetvar.h,v 1.1 2020/02/22 00:28:35 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2020 Jared McNeill <jmcne...@invisible.ca>
+ * 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, 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.
+ */
+
+/*
+ * Broadcom GENETv5
+ */
+
+#ifndef _BCMGENETVAR_H
+#define _BCMGENETVAR_H
+
+#include <dev/ic/bcmgenetreg.h>
+
+enum genet_phy_mode {
+       GENET_PHY_MODE_RGMII,
+       GENET_PHY_MODE_RGMII_ID,
+       GENET_PHY_MODE_RGMII_TXID,
+       GENET_PHY_MODE_RGMII_RXID,
+};
+
+struct genet_bufmap {
+       bus_dmamap_t            map;
+       struct mbuf             *mbuf;
+};
+
+struct genet_ring {
+       bus_dma_tag_t           buf_tag;
+       struct genet_bufmap     buf_map[GENET_DMA_DESC_COUNT];
+       u_int                   next, queued;
+       uint32_t                cidx, pidx;
+};
+
+struct genet_softc {
+       struct device           sc_dev;
+       bus_space_tag_t         sc_bst;
+       bus_space_handle_t      sc_bsh;
+       bus_dma_tag_t           sc_dmat;
+       int                     sc_phy_id;
+       enum genet_phy_mode     sc_phy_mode;
+
+       void                    *sc_ih;
+       
+       struct arpcom           sc_ac;
+#define sc_lladdr      sc_ac.ac_enaddr
+       struct mii_data         sc_mii;
+       struct timeout          sc_stat_ch;
+
+       struct genet_ring       sc_tx;
+       struct genet_ring       sc_rx;
+       struct if_rxring        sc_rx_ring;
+       struct timeout          sc_rxto;
+};
+
+int    genet_attach(struct genet_softc *);
+int    genet_intr(void *);
+void   genet_lladdr_read(struct genet_softc *, uint8_t *);
+
+#endif /* !_BCMGENETVAR_H */
Index: dev/mii/brgphy.c
===================================================================
RCS file: /cvs/src/sys/dev/mii/brgphy.c,v
retrieving revision 1.105
diff -u -p -r1.105 brgphy.c
--- dev/mii/brgphy.c    19 Jul 2015 06:28:12 -0000      1.105
+++ dev/mii/brgphy.c    12 Apr 2020 15:38:44 -0000
@@ -92,6 +92,7 @@ void  brgphy_crc_bug(struct mii_softc *);
 void   brgphy_disable_early_dac(struct mii_softc *sc);
 void   brgphy_jumbo_settings(struct mii_softc *);
 void   brgphy_eth_wirespeed(struct mii_softc *);
+void   brgphy_bcm54xx_clock_delay(struct mii_softc *);
 
 const struct mii_phy_funcs brgphy_copper_funcs = {            
        brgphy_service, brgphy_copper_status, brgphy_reset,          
@@ -180,6 +181,8 @@ static const struct mii_phydesc brgphys[
          MII_STR_xxBROADCOM3_BCM57765 },
        { MII_OUI_xxBROADCOM3,          MII_MODEL_xxBROADCOM3_BCM57780,
          MII_STR_xxBROADCOM3_BCM57780 },
+       { MII_OUI_xxBROADCOM4,          MII_MODEL_xxBROADCOM4_BCM54210E,
+         MII_STR_xxBROADCOM4_BCM54210E },
        { MII_OUI_BROADCOM2,            MII_MODEL_BROADCOM2_BCM5906,
          MII_STR_BROADCOM2_BCM5906 },
 
@@ -789,6 +792,12 @@ brgphy_reset(struct mii_softc *sc)
                        break;
                }
                break;
+       case MII_OUI_xxBROADCOM4:
+               switch (sc->mii_model) {
+               case MII_MODEL_xxBROADCOM4_BCM54210E:
+                       brgphy_bcm54xx_clock_delay(sc);
+                       break;
+               }
        }
 
        /* Handle any bge (NetXtreme/NetLink) workarounds. */
@@ -1187,11 +1196,38 @@ brgphy_jumbo_settings(struct mii_softc *
 void
 brgphy_eth_wirespeed(struct mii_softc *sc)
 {
-       u_int32_t val;
+       uint16_t val;
 
        /* Enable Ethernet@Wirespeed */
-       PHY_WRITE(sc, BRGPHY_MII_AUXCTL, 0x7007);
-       val = PHY_READ(sc, BRGPHY_MII_AUXCTL);
-       PHY_WRITE(sc, BRGPHY_MII_AUXCTL,
-               (val | (1 << 15) | (1 << 4)));
+       PHY_WRITE(sc, BRGPHY_MII_AUXCTL, BRGPHY_AUXCTL_SHADOW_MISC |
+           BRGPHY_AUXCTL_SHADOW_MISC << BRGPHY_AUXCTL_MISC_READ_SHIFT);
+       val = PHY_READ(sc, BRGPHY_MII_AUXCTL) & BRGPHY_AUXCTL_MISC_DATA_MASK;
+       val |= BRGPHY_AUXCTL_MISC_WIRESPEED_EN;
+       PHY_WRITE(sc, BRGPHY_MII_AUXCTL, BRGPHY_AUXCTL_MISC_WRITE_EN |
+           BRGPHY_AUXCTL_SHADOW_MISC | val);
+}
+
+void
+brgphy_bcm54xx_clock_delay(struct mii_softc *sc)
+{
+       uint16_t val;
+
+       PHY_WRITE(sc, BRGPHY_MII_AUXCTL, BRGPHY_AUXCTL_SHADOW_MISC |
+           BRGPHY_AUXCTL_SHADOW_MISC << BRGPHY_AUXCTL_MISC_READ_SHIFT);
+       val = PHY_READ(sc, BRGPHY_MII_AUXCTL) & BRGPHY_AUXCTL_MISC_DATA_MASK;
+       if (sc->mii_flags & MIIF_RXID)
+               val |= BRGPHY_AUXCTL_MISC_RGMII_SKEW_EN;
+       else
+               val &= ~BRGPHY_AUXCTL_MISC_RGMII_SKEW_EN;
+       PHY_WRITE(sc, BRGPHY_MII_AUXCTL, BRGPHY_AUXCTL_MISC_WRITE_EN |
+           BRGPHY_AUXCTL_SHADOW_MISC | val);
+
+       PHY_WRITE(sc, BRGPHY_MII_SHADOW_1C, BRGPHY_SHADOW_1C_CLK_CTRL);
+       val = PHY_READ(sc, BRGPHY_MII_SHADOW_1C) & BRGPHY_SHADOW_1C_DATA_MASK;
+       if (sc->mii_flags & MIIF_TXID)
+               val |= BRGPHY_SHADOW_1C_GTXCLK_EN;
+       else
+               val &= ~BRGPHY_SHADOW_1C_GTXCLK_EN;
+       PHY_WRITE(sc, BRGPHY_MII_SHADOW_1C, BRGPHY_SHADOW_1C_WRITE_EN |
+           BRGPHY_SHADOW_1C_CLK_CTRL | val);
 }
Index: dev/mii/brgphyreg.h
===================================================================
RCS file: /cvs/src/sys/dev/mii/brgphyreg.h,v
retrieving revision 1.17
diff -u -p -r1.17 brgphyreg.h
--- dev/mii/brgphyreg.h 19 Jul 2015 06:28:12 -0000      1.17
+++ dev/mii/brgphyreg.h 12 Apr 2020 15:38:44 -0000
@@ -195,6 +195,17 @@
 /* Begin: PHY register values for the 5706 PHY         */
 /*******************************************************/
 
+/*
+ * Aux control shadow register, bits 0-2 select function (0x00 to
+ * 0x07).
+ */
+#define BRGPHY_AUXCTL_SHADOW_MISC      0x07
+#define BRGPHY_AUXCTL_MISC_DATA_MASK   0x7ff8
+#define BRGPHY_AUXCTL_MISC_READ_SHIFT  12
+#define BRGPHY_AUXCTL_MISC_WRITE_EN    0x8000
+#define BRGPHY_AUXCTL_MISC_RGMII_SKEW_EN 0x0200
+#define BRGPHY_AUXCTL_MISC_WIRESPEED_EN        0x0010
+
 /* 
  * Shadow register 0x1C, bit 15 is write enable,
  * bits 14-10 select function (0x00 to 0x1F).
@@ -202,6 +213,11 @@
 #define BRGPHY_MII_SHADOW_1C           0x1C
 #define BRGPHY_SHADOW_1C_WRITE_EN      0x8000
 #define BRGPHY_SHADOW_1C_SELECT_MASK   0x7C00
+#define BRGPHY_SHADOW_1C_DATA_MASK     0x03FF
+
+/* Shadow 0x1C Clock Alignment Control Register (select value 0x03) */
+#define BRGPHY_SHADOW_1C_CLK_CTRL      (0x03 << 10)
+#define BRGPHY_SHADOW_1C_GTXCLK_EN     0x0200
 
 /* Shadow 0x1C Mode Control Register (select value 0x1F) */
 #define BRGPHY_SHADOW_1C_MODE_CTRL     (0x1F << 10)
Index: dev/mii/miidevs
===================================================================
RCS file: /cvs/src/sys/dev/mii/miidevs,v
retrieving revision 1.128
diff -u -p -r1.128 miidevs
--- dev/mii/miidevs     24 Sep 2019 14:35:22 -0000      1.128
+++ dev/mii/miidevs     12 Apr 2020 15:38:44 -0000
@@ -96,6 +96,7 @@ oui xxLEVEL1                  0x1e0400        Level 1
 /* Don't know what's going on here. */
 oui xxBROADCOM2                        0x0050ef        Broadcom
 oui xxBROADCOM3                        0x00d897        Broadcom
+oui xxBROADCOM4                        0x180361        Broadcom
 oui xxDAVICOM                  0x006040        Davicom
 
 /* This is the OUI of the gigE PHY in the Realtek 8169S/8110S chips */
@@ -177,6 +178,7 @@ model BROADCOM BCM5222              0x0032  BCM5222 D
 model BROADCOM BCM5220         0x0033  BCM5220 10/100 PHY
 model BROADCOM BCM4401         0x0036  BCM4401 10/100baseTX PHY
 model BROADCOM2 BCM5906                0x0004  BCM5906 10/100baseTX PHY
+model xxBROADCOM4 BCM54210E    0x000a  BCM54210E 10/100/1000baseT PHY 
 
 /* Cicada PHYs (now owned by Vitesse) */
 model xxCICADA CS8201B         0x0021  CS8201 10/100/1000TX PHY
Index: dev/mii/miidevs.h
===================================================================
RCS file: /cvs/src/sys/dev/mii/miidevs.h,v
retrieving revision 1.131
diff -u -p -r1.131 miidevs.h
--- dev/mii/miidevs.h   24 Sep 2019 14:36:00 -0000      1.131
+++ dev/mii/miidevs.h   12 Apr 2020 15:38:44 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: miidevs.h,v 1.131 2019/09/24 14:36:00 visa Exp $      */
+/*     $OpenBSD$       */
 
 /*
  * THIS FILE AUTOMATICALLY GENERATED.  DO NOT EDIT.
@@ -103,6 +103,7 @@
 /* Don't know what's going on here. */
 #define        MII_OUI_xxBROADCOM2     0x0050ef        /* Broadcom */
 #define        MII_OUI_xxBROADCOM3     0x00d897        /* Broadcom */
+#define        MII_OUI_xxBROADCOM4     0x180361        /* Broadcom */
 #define        MII_OUI_xxDAVICOM       0x006040        /* Davicom */
 
 /* This is the OUI of the gigE PHY in the Realtek 8169S/8110S chips */
@@ -242,6 +243,8 @@
 #define        MII_STR_BROADCOM_BCM4401        "BCM4401 10/100baseTX PHY"
 #define        MII_MODEL_BROADCOM2_BCM5906     0x0004
 #define        MII_STR_BROADCOM2_BCM5906       "BCM5906 10/100baseTX PHY"
+#define        MII_MODEL_xxBROADCOM4_BCM54210E 0x000a
+#define        MII_STR_xxBROADCOM4_BCM54210E   "BCM54210E 10/100/1000baseT PHY"
 
 /* Cicada PHYs (now owned by Vitesse) */
 #define        MII_MODEL_xxCICADA_CS8201B      0x0021
Index: dev/mii/miivar.h
===================================================================
RCS file: /cvs/src/sys/dev/mii/miivar.h,v
retrieving revision 1.34
diff -u -p -r1.34 miivar.h
--- dev/mii/miivar.h    12 Sep 2015 09:49:20 -0000      1.34
+++ dev/mii/miivar.h    12 Apr 2020 15:38:44 -0000
@@ -152,6 +152,8 @@ typedef struct mii_softc mii_softc_t;
 #define        MIIF_DOPAUSE    0x0100          /* advertise PAUSE capability */
 #define        MIIF_IS_HPNA    0x0200          /* is a HomePNA device */
 #define        MIIF_FORCEANEG  0x0400          /* force autonegotiation */
+#define        MIIF_RXID       0x0800          /* add Rx delay */
+#define        MIIF_TXID       0x1000          /* add Tx delay */
 
 #define        MIIF_INHERIT_MASK       
(MIIF_NOISOLATE|MIIF_NOLOOP|MIIF_AUTOTSLEEP)
 

Reply via email to