Module Name:    src
Committed By:   jmcneill
Date:           Sat Feb 22 00:28:36 UTC 2020

Modified Files:
        src/sys/arch/arm/broadcom: bcm283x_platform.c
        src/sys/arch/evbarm/conf: GENERIC64
        src/sys/conf: files
        src/sys/dev/fdt: files.fdt
Added Files:
        src/sys/dev/fdt: genet_fdt.c
        src/sys/dev/ic: bcmgenet.c bcmgenetreg.h bcmgenetvar.h

Log Message:
Add support for Broadcom GENET v5 ethernet controller as found on the
Raspberry Pi 4 (BCM2711).


To generate a diff of this commit:
cvs rdiff -u -r1.36 -r1.37 src/sys/arch/arm/broadcom/bcm283x_platform.c
cvs rdiff -u -r1.137 -r1.138 src/sys/arch/evbarm/conf/GENERIC64
cvs rdiff -u -r1.1255 -r1.1256 src/sys/conf/files
cvs rdiff -u -r1.50 -r1.51 src/sys/dev/fdt/files.fdt
cvs rdiff -u -r0 -r1.1 src/sys/dev/fdt/genet_fdt.c
cvs rdiff -u -r0 -r1.1 src/sys/dev/ic/bcmgenet.c src/sys/dev/ic/bcmgenetreg.h \
    src/sys/dev/ic/bcmgenetvar.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/broadcom/bcm283x_platform.c
diff -u src/sys/arch/arm/broadcom/bcm283x_platform.c:1.36 src/sys/arch/arm/broadcom/bcm283x_platform.c:1.37
--- src/sys/arch/arm/broadcom/bcm283x_platform.c:1.36	Sat Feb 22 00:17:54 2020
+++ src/sys/arch/arm/broadcom/bcm283x_platform.c	Sat Feb 22 00:28:35 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: bcm283x_platform.c,v 1.36 2020/02/22 00:17:54 jmcneill Exp $	*/
+/*	$NetBSD: bcm283x_platform.c,v 1.37 2020/02/22 00:28:35 jmcneill Exp $	*/
 
 /*-
  * Copyright (c) 2017 Jared D. McNeill <jmcne...@invisible.ca>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: bcm283x_platform.c,v 1.36 2020/02/22 00:17:54 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: bcm283x_platform.c,v 1.37 2020/02/22 00:28:35 jmcneill Exp $");
 
 #include "opt_arm_debug.h"
 #include "opt_bcm283x.h"
@@ -1415,7 +1415,9 @@ bcm283x_platform_device_register(device_
 		booted_device = dev;
 	}
 #endif
-	if ((device_is_a(dev, "usmsc") || device_is_a(dev, "mue")) &&
+	if ((device_is_a(dev, "usmsc") ||
+	     device_is_a(dev, "mue") ||
+	     device_is_a(dev, "genet")) &&
 	    vcprop_tag_success_p(&vb.vbt_macaddr.tag)) {
 		const uint8_t enaddr[ETHER_ADDR_LEN] = {
 		     (vb.vbt_macaddr.addr >> 0) & 0xff,

Index: src/sys/arch/evbarm/conf/GENERIC64
diff -u src/sys/arch/evbarm/conf/GENERIC64:1.137 src/sys/arch/evbarm/conf/GENERIC64:1.138
--- src/sys/arch/evbarm/conf/GENERIC64:1.137	Thu Feb 20 01:36:37 2020
+++ src/sys/arch/evbarm/conf/GENERIC64	Sat Feb 22 00:28:35 2020
@@ -1,5 +1,5 @@
 #
-#	$NetBSD: GENERIC64,v 1.137 2020/02/20 01:36:37 jmcneill Exp $
+#	$NetBSD: GENERIC64,v 1.138 2020/02/22 00:28:35 jmcneill Exp $
 #
 #	GENERIC ARM (aarch64) kernel
 #
@@ -327,6 +327,7 @@ emac*		at fdt?				# Allwinner Gigabit Et
 enet*		at fdt?				# IMX FEC
 aq*		at pci? dev ? function ?	# Aquantia AQC 10 gigabit
 ena*		at pci? dev ? function ?	# Amazon.com Elastic Network Adapter
+genet*		at fdt?				# Broadcom GENET v5
 mcx*		at pci? dev ? function ?	# Mellanox 5th generation Ethernet
 mskc*		at pci? dev ? function ?	# Marvell Yukon 2 Gigabit Ethernet
 msk*		at mskc?

Index: src/sys/conf/files
diff -u src/sys/conf/files:1.1255 src/sys/conf/files:1.1256
--- src/sys/conf/files:1.1255	Sat Feb  8 07:07:07 2020
+++ src/sys/conf/files	Sat Feb 22 00:28:35 2020
@@ -1,4 +1,4 @@
-#	$NetBSD: files,v 1.1255 2020/02/08 07:07:07 maxv Exp $
+#	$NetBSD: files,v 1.1256 2020/02/22 00:28:35 jmcneill Exp $
 #	@(#)files.newconf	7.5 (Berkeley) 5/10/93
 
 version 	20171118
@@ -1470,6 +1470,10 @@ file	dev/ic/dw_hdmi_phy.c		dwhdmi
 device	anxdp: edid, videomode, drmkms, drmkms_i2c
 file	dev/ic/anx_dp.c			anxdp
 
+# Broadcom GENET v5 ethernet
+device	genet: arp, ether, ifnet, mii
+file	dev/ic/bcmgenet.c		genet
+
 #
 # File systems
 #

Index: src/sys/dev/fdt/files.fdt
diff -u src/sys/dev/fdt/files.fdt:1.50 src/sys/dev/fdt/files.fdt:1.51
--- src/sys/dev/fdt/files.fdt:1.50	Thu Jan  2 00:57:10 2020
+++ src/sys/dev/fdt/files.fdt	Sat Feb 22 00:28:35 2020
@@ -1,4 +1,4 @@
-# $NetBSD: files.fdt,v 1.50 2020/01/02 00:57:10 jmcneill Exp $
+# $NetBSD: files.fdt,v 1.51 2020/02/22 00:28:35 jmcneill Exp $
 
 include	"external/bsd/libfdt/conf/files.libfdt"
 
@@ -171,3 +171,6 @@ device	simpleamp
 attach	simpleamp at fdt
 file	dev/fdt/simple_amplifier.c		simpleamp
 
+# Broadcom GENET v5
+attach	genet at fdt with genet_fdt
+file	dev/fdt/genet_fdt.c			genet_fdt

Added files:

Index: src/sys/dev/fdt/genet_fdt.c
diff -u /dev/null src/sys/dev/fdt/genet_fdt.c:1.1
--- /dev/null	Sat Feb 22 00:28:36 2020
+++ src/sys/dev/fdt/genet_fdt.c	Sat Feb 22 00:28:35 2020
@@ -0,0 +1,134 @@
+/* $NetBSD: genet_fdt.c,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.
+ */
+
+#include "opt_net_mpsafe.h"
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: genet_fdt.c,v 1.1 2020/02/22 00:28:35 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_ether.h>
+#include <net/if_media.h>
+
+#include <dev/mii/miivar.h>
+
+#include <dev/ic/bcmgenetvar.h>
+
+#include <dev/fdt/fdtvar.h>
+
+#ifdef NET_MPSAFE
+#define	FDT_INTR_FLAGS	FDT_INTR_MPSAFE
+#else
+#define	FDT_INTR_FLAGS	0
+#endif
+
+static int	genet_fdt_match(device_t, cfdata_t, void *);
+static void	genet_fdt_attach(device_t, device_t, void *);
+
+CFATTACH_DECL_NEW(genet_fdt, sizeof(struct genet_softc),
+	genet_fdt_match, genet_fdt_attach, NULL, NULL);
+
+static int
+genet_fdt_match(device_t parent, cfdata_t cf, void *aux)
+{
+	const char * const compatible[] = {
+		"brcm,bcm2711-genet-v5",
+		NULL
+	};
+	struct fdt_attach_args * const faa = aux;
+
+	return of_match_compatible(faa->faa_phandle, compatible);
+}
+
+static void
+genet_fdt_attach(device_t parent, device_t self, void *aux)
+{
+	struct genet_softc * const sc = device_private(self);
+	struct fdt_attach_args * const faa = aux;
+	const int phandle = faa->faa_phandle;
+	char intrstr[128];
+	bus_addr_t addr;
+	bus_size_t size;
+	void *ih;
+
+	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
+		aprint_error(": couldn't get registers\n");
+		return;
+	}
+
+	sc->sc_dev = self;
+	sc->sc_bst = faa->faa_bst;
+	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
+		aprint_error(": couldn't map registers\n");
+		return;
+	}
+	sc->sc_dmat = faa->faa_dmat;
+	sc->sc_phy_id = MII_PHY_ANY;
+
+	const char *phy_mode = fdtbus_get_string(phandle, "phy-mode");
+	if (phy_mode == NULL) {
+		aprint_error(": missing 'phy-mode' property\n");
+		return;
+	}
+
+	if (strcmp(phy_mode, "rgmii-rxid") == 0)
+		sc->sc_phy_mode = GENET_PHY_MODE_RGMII_RXID;
+	else if (strcmp(phy_mode, "rgmii-txid") == 0)
+		sc->sc_phy_mode = GENET_PHY_MODE_RGMII_TXID;
+	else if (strcmp(phy_mode, "rgmii") == 0)
+		sc->sc_phy_mode = GENET_PHY_MODE_RGMII;
+	else {
+		aprint_error(": unsupported phy-mode '%s'\n", phy_mode);
+		return;
+	}
+
+	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
+		aprint_error(": failed to decode interrupt\n");
+		return;
+	}
+
+	if (genet_attach(sc) != 0)
+		return;
+
+	ih = fdtbus_intr_establish(phandle, 0, IPL_NET, FDT_INTR_FLAGS,
+	    genet_intr, sc);
+	if (ih == NULL) {
+		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
+		    intrstr);
+		return;
+	}
+	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
+}

Index: src/sys/dev/ic/bcmgenet.c
diff -u /dev/null src/sys/dev/ic/bcmgenet.c:1.1
--- /dev/null	Sat Feb 22 00:28:36 2020
+++ src/sys/dev/ic/bcmgenet.c	Sat Feb 22 00:28:35 2020
@@ -0,0 +1,955 @@
+/* $NetBSD: bcmgenet.c,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
+ */
+
+#include "opt_net_mpsafe.h"
+#include "opt_ddb.h"
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: bcmgenet.c,v 1.1 2020/02/22 00:28:35 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/mutex.h>
+#include <sys/callout.h>
+#include <sys/cprng.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_ether.h>
+#include <net/if_media.h>
+#include <net/bpf.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
+
+#ifdef NET_MPSAFE
+#define	GENET_MPSAFE		1
+#define	CALLOUT_FLAGS		CALLOUT_MPSAFE
+#else
+#define	CALLOUT_FLAGS		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_LOCK(sc)		mutex_enter(&(sc)->sc_lock)
+#define	GENET_UNLOCK(sc)	mutex_exit(&(sc)->sc_lock)
+#define	GENET_ASSERT_LOCKED(sc)	KASSERT(mutex_owned(&(sc)->sc_lock))
+
+#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))
+
+static int
+genet_mii_readreg(device_t dev, int phy, int reg, uint16_t *val)
+{
+	struct genet_softc *sc = device_private(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) {
+			*val = RD4(sc, GENET_MDIO_CMD) & 0xffff;
+			break;
+		}
+		delay(10);
+	}
+
+
+	if (retry == 0) {
+		device_printf(dev, "phy read timeout, phy=%d reg=%d\n",
+		    phy, reg);
+		return ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int
+genet_mii_writereg(device_t dev, int phy, int reg, uint16_t val)
+{
+	struct genet_softc *sc = device_private(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)
+			break;
+		delay(10);
+	}
+
+	if (retry == 0) {
+		device_printf(dev, "phy write timeout, phy=%d reg=%d\n",
+		    phy, reg);
+		return ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static 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;
+	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);
+}
+
+static void
+genet_mii_statchg(struct ifnet *ifp)
+{
+	struct genet_softc * const sc = ifp->if_softc;
+
+	genet_update_link(sc);
+}
+
+static 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);
+}
+
+static 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;
+
+	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) {
+		device_printf(sc->sc_dev,
+		    "TX packet needs too many DMA segments, dropping...\n");
+		m_freem(m);
+		return 0;
+	}
+	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;
+}
+
+static 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));
+}
+
+static 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;
+}
+
+static struct mbuf *
+genet_alloc_mbufcl(struct genet_softc *sc)
+{
+	struct mbuf *m;
+
+	m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+	if (m != NULL)
+		m->m_pkthdr.len = m->m_len = m->m_ext.ext_size;
+
+	return m;
+}
+
+static void
+genet_enable_intr(struct genet_softc *sc)
+{
+	WR4(sc, GENET_INTRL2_CPU_CLEAR_MASK,
+	    GENET_IRQ_TXDMA_DONE | GENET_IRQ_RXDMA_DONE);
+}
+
+static 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);
+}
+
+static void
+genet_tick(void *softc)
+{
+	struct genet_softc *sc = softc;
+	struct mii_data *mii = &sc->sc_mii;
+#ifndef GENET_MPSAFE
+	int s = splnet();
+#endif
+
+	GENET_LOCK(sc);
+	mii_tick(mii);
+	callout_schedule(&sc->sc_stat_ch, hz);
+	GENET_UNLOCK(sc);
+
+#ifndef GENET_MPSAFE
+	splx(s);
+#endif
+}
+
+static void
+genet_setup_rxfilter(struct genet_softc *sc)
+{
+	uint32_t val;
+
+	GENET_ASSERT_LOCKED(sc);
+
+	/* Enable promiscuous mode */
+	val = RD4(sc, GENET_UMAC_CMD);
+	val |= GENET_UMAC_CMD_PROMISC;
+	WR4(sc, GENET_UMAC_CMD, val);
+
+	/* Disable filters */
+	WR4(sc, GENET_UMAC_MDF_CTRL, 0);
+}
+
+static 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;
+}
+
+static void
+genet_init_rings(struct genet_softc *sc, int qid)
+{
+	uint32_t val;
+
+	/* TX ring */
+
+	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), 0);
+	WR4(sc, GENET_TX_DMA_PROD_INDEX(qid), 0);
+	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.cidx = sc->sc_rx.pidx = 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),
+	    __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 */
+
+	/* 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);
+}
+
+static int
+genet_init_locked(struct genet_softc *sc)
+{
+	struct ifnet *ifp = &sc->sc_ec.ec_if;
+	struct mii_data *mii = &sc->sc_mii;
+	uint32_t val;
+	const uint8_t *enaddr = CLLADDR(ifp->if_sadl);
+
+	GENET_ASSERT_LOCKED(sc);
+
+	if ((ifp->if_flags & IFF_RUNNING) != 0)
+		return 0;
+
+	if (sc->sc_phy_mode == GENET_PHY_MODE_RGMII ||
+	    sc->sc_phy_mode == GENET_PHY_MODE_RGMII_RXID)
+		WR4(sc, GENET_SYS_PORT_CTRL,
+		    GENET_SYS_PORT_MODE_EXT_GPHY);
+
+	/* Write hardware address */
+	val = enaddr[0] | (enaddr[1] << 8) | (enaddr[2] << 16) |
+	    (enaddr[3] << 24);
+	WR4(sc, GENET_UMAC_MAC0, val);
+	val = enaddr[4] | (enaddr[5] << 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;
+	ifp->if_flags &= ~IFF_OACTIVE;
+
+	mii_mediachg(mii);
+	callout_schedule(&sc->sc_stat_ch, hz);
+
+	return 0;
+}
+
+static int
+genet_init(struct ifnet *ifp)
+{
+	struct genet_softc *sc = ifp->if_softc;
+	int error;
+
+	GENET_LOCK(sc);
+	error = genet_init_locked(sc);
+	GENET_UNLOCK(sc);
+
+	return error;
+}
+
+static void
+genet_stop_locked(struct genet_softc *sc, int disable)
+{
+	struct ifnet *ifp = &sc->sc_ec.ec_if;
+	uint32_t val;
+
+	GENET_ASSERT_LOCKED(sc);
+
+	callout_stop(&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;
+	WR4(sc, GENET_RX_DMA_CTRL, val);
+
+	/* Stop transmit DMA */
+	val = RD4(sc, GENET_TX_DMA_CTRL);
+	val &= ~GENET_TX_DMA_CTRL_EN;
+	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 | IFF_OACTIVE);
+}
+
+static void
+genet_stop(struct ifnet *ifp, int disable)
+{
+	struct genet_softc * const sc = ifp->if_softc;
+
+	GENET_LOCK(sc);
+	genet_stop_locked(sc, disable);
+	GENET_UNLOCK(sc);
+}
+
+static void
+genet_rxintr(struct genet_softc *sc, int qid)
+{
+	struct ifnet *ifp = &sc->sc_ec.ec_if;
+	int error, index, len, n;
+	struct mbuf *m, *m0;
+	uint32_t status, pidx, total;
+
+	pidx = RD4(sc, GENET_RX_DMA_PROD_INDEX(qid)) & 0xffff;
+	total = (pidx - sc->sc_rx.cidx) & 0xffff;
+
+	DPRINTF("RX pidx=%08x total=%d\n", pidx, total);
+
+	index = sc->sc_rx.cidx & (RX_DESC_COUNT - 1);
+	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);
+
+		if (len > ETHER_ALIGN) {
+			m = sc->sc_rx.buf_map[index].mbuf;
+
+			m_adj(m, ETHER_ALIGN);
+
+			m_set_rcvif(m, ifp);
+			m->m_len = m->m_pkthdr.len = len - ETHER_ALIGN;
+			m->m_nextpkt = NULL;
+
+			if_percpuq_enqueue(ifp->if_percpuq, m);
+		}
+
+		if ((m0 = genet_alloc_mbufcl(sc)) != NULL) {
+			error = genet_setup_rxbuf(sc, index, m0);
+			if (error != 0) {
+				/* XXX hole in RX ring */
+			}
+		} else {
+			if_statinc(ifp, if_ierrors);
+		}
+
+		index = RX_NEXT(index);
+
+		sc->sc_rx.cidx = (sc->sc_rx.cidx + 1) & 0xffff;
+		WR4(sc, GENET_RX_DMA_CONS_INDEX(qid), sc->sc_rx.cidx);
+	}
+}
+
+static void
+genet_txintr(struct genet_softc *sc, int qid)
+{
+	struct ifnet *ifp = &sc->sc_ec.ec_if;
+	struct genet_bufmap *bmap;
+	uint32_t cidx, total;
+	int i;
+
+	GENET_ASSERT_LOCKED(sc);
+
+	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;
+		ifp->if_flags &= ~IFF_OACTIVE;
+		if_statinc(ifp, if_opackets);
+	}
+
+	sc->sc_tx.next = i;
+	sc->sc_tx.cidx = cidx;
+}
+
+static void
+genet_start_locked(struct genet_softc *sc)
+{
+	struct ifnet *ifp = &sc->sc_ec.ec_if;
+	struct mbuf *m;
+	int nsegs, index, cnt;
+
+	GENET_ASSERT_LOCKED(sc);
+
+	if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
+		return;
+
+	const int qid = GENET_DMA_DEFAULT_QUEUE;
+
+	index = sc->sc_tx.pidx & (TX_DESC_COUNT - 1);
+	cnt = 0;
+
+	for (;;) {
+		IFQ_POLL(&ifp->if_snd, m);
+		if (m == NULL)
+			break;
+
+		nsegs = genet_setup_txbuf(sc, index, m);
+		if (nsegs <= 0) {
+			if (nsegs == -1)
+				ifp->if_flags |= IFF_OACTIVE;
+			break;
+		}
+		IFQ_DEQUEUE(&ifp->if_snd, m);
+		bpf_mtap(ifp, m, BPF_D_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);
+}
+
+static void
+genet_start(struct ifnet *ifp)
+{
+	struct genet_softc *sc = ifp->if_softc;
+
+	GENET_LOCK(sc);
+	genet_start_locked(sc);
+	GENET_UNLOCK(sc);
+}
+
+int
+genet_intr(void *arg)
+{
+	struct genet_softc *sc = arg;
+	struct ifnet *ifp = &sc->sc_ec.ec_if;
+	uint32_t val;
+
+	GENET_LOCK(sc);
+
+	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_schedule_deferred_start(ifp);
+	}
+
+	GENET_UNLOCK(sc);
+
+	return 1;
+}
+
+static int
+genet_ioctl(struct ifnet *ifp, u_long cmd, void *data)
+{
+	struct genet_softc *sc = ifp->if_softc;
+	int error, s;
+
+#ifndef GENET_MPSAFE
+	s = splnet();
+#endif
+
+	switch (cmd) {
+	default:
+#ifdef GENET_MPSAFE
+		s = splnet();
+#endif
+		error = ether_ioctl(ifp, cmd, data);
+#ifdef GENET_MPSAFE
+		splx(s);
+#endif
+		if (error != ENETRESET)
+			break;
+
+		error = 0;
+
+		if (cmd == SIOCSIFCAP)
+			error = (*ifp->if_init)(ifp);
+		else if (cmd != SIOCADDMULTI && cmd != SIOCDELMULTI)
+			;
+		else if ((ifp->if_flags & IFF_RUNNING) != 0) {
+			GENET_LOCK(sc);
+			genet_setup_rxfilter(sc);
+			GENET_UNLOCK(sc);
+		}
+		break;
+	}
+
+#ifndef GENET_MPSAFE
+	splx(s);
+#endif
+
+	return error;
+}
+
+static void
+genet_get_eaddr(struct genet_softc *sc, uint8_t *eaddr)
+{
+	prop_dictionary_t prop = device_properties(sc->sc_dev);
+	uint32_t maclo, machi;
+	prop_data_t eaprop;
+
+	eaprop = prop_dictionary_get(prop, "mac-address");
+	if (eaprop == NULL) {
+		/* Create one */
+		maclo = 0x00f2 | (cprng_strong32() & 0xffff0000);
+		machi = cprng_strong32() & 0xffff;
+
+		eaddr[0] = maclo & 0xff;
+		eaddr[1] = (maclo >> 8) & 0xff;
+		eaddr[2] = (maclo >> 16) & 0xff;
+		eaddr[3] = (maclo >> 24) & 0xff;
+		eaddr[4] = machi & 0xff;
+		eaddr[5] = (machi >> 8) & 0xff;
+	} else {
+		KASSERT(prop_object_type(eaprop) == PROP_TYPE_DATA);
+		KASSERT(prop_data_size(eaprop) == ETHER_ADDR_LEN);
+		memcpy(eaddr, prop_data_data_nocopy(eaprop),
+		    ETHER_ADDR_LEN);
+	}
+
+}
+
+static int
+genet_setup_dma(struct genet_softc *sc, int qid)
+{
+	struct mbuf *m;
+	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) {
+			device_printf(sc->sc_dev,
+			    "cannot create TX buffer map\n");
+			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) {
+			device_printf(sc->sc_dev,
+			    "cannot create RX buffer map\n");
+			return error;
+		}
+		if ((m = genet_alloc_mbufcl(sc)) == NULL) {
+			device_printf(sc->sc_dev, "cannot allocate RX mbuf\n");
+			return ENOMEM;
+		}
+		error = genet_setup_rxbuf(sc, i, m);
+		if (error != 0) {
+			device_printf(sc->sc_dev, "cannot create RX buffer\n");
+			return error;
+		}
+	}
+
+	return 0;
+}
+
+int
+genet_attach(struct genet_softc *sc)
+{
+	struct mii_data *mii = &sc->sc_mii;
+	struct ifnet *ifp = &sc->sc_ec.ec_if;
+	uint8_t eaddr[ETHER_ADDR_LEN];
+	u_int maj, min;
+
+	const uint32_t rev = RD4(sc, GENET_SYS_REV_CTRL);
+	min = __SHIFTOUT(rev, SYS_REV_MINOR);
+	maj = __SHIFTOUT(rev, SYS_REV_MAJOR);
+	if (maj == 0)
+		maj++;
+	else if (maj == 5 || maj == 6)
+		maj--;
+
+	if (maj != 5) {
+		aprint_error(": GENETv%d.%d not supported\n", maj, min);
+		return ENXIO;
+	}
+
+	aprint_naive("\n");
+	aprint_normal(": GENETv%d.%d\n", maj, min);
+
+	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NET);
+	callout_init(&sc->sc_stat_ch, CALLOUT_FLAGS);
+	callout_setfunc(&sc->sc_stat_ch, genet_tick, sc);
+
+	genet_get_eaddr(sc, eaddr);
+	aprint_normal_dev(sc->sc_dev, "Ethernet address %s\n", ether_sprintf(eaddr));
+
+	/* Soft reset EMAC core */
+	genet_reset(sc);
+
+	/* Setup DMA descriptors */
+	if (genet_setup_dma(sc, GENET_DMA_DEFAULT_QUEUE) != 0) {
+		aprint_error_dev(sc->sc_dev, "failed to setup DMA descriptors\n");
+		return EINVAL;
+	}
+
+	/* Setup ethernet interface */
+	ifp->if_softc = sc;
+	snprintf(ifp->if_xname, IFNAMSIZ, device_xname(sc->sc_dev));
+	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+#ifdef GENET_MPSAFE
+	ifp->if_extflags = IFEF_MPSAFE;
+#endif
+	ifp->if_start = genet_start;
+	ifp->if_ioctl = genet_ioctl;
+	ifp->if_init = genet_init;
+	ifp->if_stop = genet_stop;
+	ifp->if_capabilities = 0;
+	ifp->if_capenable = ifp->if_capabilities;
+	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
+	IFQ_SET_READY(&ifp->if_snd);
+
+	/* 802.1Q VLAN-sized frames are supported */
+	sc->sc_ec.ec_capabilities |= ETHERCAP_VLAN_MTU;
+
+	/* Attach MII driver */
+	sc->sc_ec.ec_mii = mii;
+	ifmedia_init(&mii->mii_media, 0, ether_mediachange, ether_mediastatus);
+	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,
+	    0);
+
+	if (LIST_EMPTY(&mii->mii_phys)) {
+		aprint_error_dev(sc->sc_dev, "no PHY found!\n");
+		return ENOENT;
+	}
+	ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
+
+	/* Attach interface */
+	if_attach(ifp);
+	if_deferred_start_init(ifp, NULL);
+
+	/* Attach ethernet interface */
+	ether_ifattach(ifp, eaddr);
+
+	return 0;
+}
+
+#ifdef DDB
+void	genet_debug(void);
+
+void
+genet_debug(void)
+{
+	device_t dev = device_find_by_xname("genet0");
+	if (dev == NULL)
+		return;
+
+	struct genet_softc * const sc = device_private(dev);
+	const int qid = GENET_DMA_DEFAULT_QUEUE;
+
+	printf("TX CIDX = %08x (soft)\n", sc->sc_tx.cidx);
+	printf("TX CIDX = %08x\n", RD4(sc, GENET_TX_DMA_CONS_INDEX(qid)));
+	printf("TX PIDX = %08x (soft)\n", sc->sc_tx.pidx);
+	printf("TX PIDX = %08x\n", RD4(sc, GENET_TX_DMA_PROD_INDEX(qid)));
+
+	printf("RX CIDX = %08x (soft)\n", sc->sc_rx.cidx);
+	printf("RX CIDX = %08x\n", RD4(sc, GENET_RX_DMA_CONS_INDEX(qid)));
+	printf("RX PIDX = %08x (soft)\n", sc->sc_rx.pidx);
+	printf("RX PIDX = %08x\n", RD4(sc, GENET_RX_DMA_PROD_INDEX(qid)));
+}
+#endif
Index: src/sys/dev/ic/bcmgenetreg.h
diff -u /dev/null src/sys/dev/ic/bcmgenetreg.h:1.1
--- /dev/null	Sat Feb 22 00:28:36 2020
+++ src/sys/dev/ic/bcmgenetreg.h	Sat Feb 22 00:28:35 2020
@@ -0,0 +1,165 @@
+/* $NetBSD: bcmgenetreg.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 _BCMGENETREG_H
+#define _BCMGENETREG_H
+
+#define	GENET_SYS_REV_CTRL		0x000
+#define	 SYS_REV_MAJOR			__BITS(27,24)
+#define	 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_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: src/sys/dev/ic/bcmgenetvar.h
diff -u /dev/null src/sys/dev/ic/bcmgenetvar.h:1.1
--- /dev/null	Sat Feb 22 00:28:36 2020
+++ src/sys/dev/ic/bcmgenetvar.h	Sat Feb 22 00:28:35 2020
@@ -0,0 +1,76 @@
+/* $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_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 {
+	device_t		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;
+
+	struct ethercom		sc_ec;
+	struct mii_data		sc_mii;
+	callout_t		sc_stat_ch;
+	kmutex_t		sc_lock;
+
+	struct genet_ring	sc_tx;
+	struct genet_ring	sc_rx;
+};
+
+int	genet_attach(struct genet_softc *);
+int	genet_intr(void *);
+
+#endif /* !_BCMGENETVAR_H */

Reply via email to