summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2020-04-14 21:02:40 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2020-04-14 21:02:40 +0000
commit56d9f1f08cb7b0c41e64aed3e02fd7c8e5c9c10b (patch)
tree90d0ffd7a326ec4c98ad6852bf5ba6ca6b731fba /sys
parent18ab39925eabb13e61344342b790f2640fedbacf (diff)
Add bse(4), a driver for the Broadcom GENET v5 network interface found on
the Raspberry Pi4. ok patrick@
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/acpi/files.acpi6
-rw-r--r--sys/dev/acpi/if_bse_acpi.c168
-rw-r--r--sys/dev/ic/bcmgenet.c1020
-rw-r--r--sys/dev/ic/bcmgenetreg.h175
-rw-r--r--sys/dev/ic/bcmgenetvar.h83
5 files changed, 1451 insertions, 1 deletions
diff --git a/sys/dev/acpi/files.acpi b/sys/dev/acpi/files.acpi
index 1a635fd86be..496d7ef1ed1 100644
--- a/sys/dev/acpi/files.acpi
+++ b/sys/dev/acpi/files.acpi
@@ -1,4 +1,4 @@
-# $OpenBSD: files.acpi,v 1.53 2019/12/23 08:05:42 kettenis Exp $
+# $OpenBSD: files.acpi,v 1.54 2020/04/14 21:02:39 kettenis Exp $
#
# Config file and device description for machine-independent ACPI code.
# Included by ports that need it.
@@ -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
diff --git a/sys/dev/acpi/if_bse_acpi.c b/sys/dev/acpi/if_bse_acpi.c
new file mode 100644
index 00000000000..5ba0b162129
--- /dev/null
+++ b/sys/dev/acpi/if_bse_acpi.c
@@ -0,0 +1,168 @@
+/* $OpenBSD: if_bse_acpi.c,v 1.1 2020/04/14 21:02:39 kettenis Exp $ */
+/*
+ * 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:
+ 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;
+}
diff --git a/sys/dev/ic/bcmgenet.c b/sys/dev/ic/bcmgenet.c
new file mode 100644
index 00000000000..60ca5145606
--- /dev/null
+++ b/sys/dev/ic/bcmgenet.c
@@ -0,0 +1,1020 @@
+/* $OpenBSD: bcmgenet.c,v 1.1 2020/04/14 21:02:39 kettenis Exp $ */
+/* $NetBSD: bcmgenet.c,v 1.3 2020/02/27 17:30:07 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2020 Jared McNeill <jmcneill@invisible.ca>
+ * Copyright (c) 2020 Mark Kettenis <kettenis@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 at 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;
+}
diff --git a/sys/dev/ic/bcmgenetreg.h b/sys/dev/ic/bcmgenetreg.h
new file mode 100644
index 00000000000..2cf24bb6ac0
--- /dev/null
+++ b/sys/dev/ic/bcmgenetreg.h
@@ -0,0 +1,175 @@
+/* $OpenBSD: bcmgenetreg.h,v 1.1 2020/04/14 21:02:39 kettenis Exp $ */
+/* $NetBSD: bcmgenetreg.h,v 1.2 2020/02/22 13:41:41 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2020 Jared McNeill <jmcneill@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 */
diff --git a/sys/dev/ic/bcmgenetvar.h b/sys/dev/ic/bcmgenetvar.h
new file mode 100644
index 00000000000..8076b8027d9
--- /dev/null
+++ b/sys/dev/ic/bcmgenetvar.h
@@ -0,0 +1,83 @@
+/* $OpenBSD: bcmgenetvar.h,v 1.1 2020/04/14 21:02:39 kettenis Exp $ */
+/* $NetBSD: bcmgenetvar.h,v 1.1 2020/02/22 00:28:35 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2020 Jared McNeill <jmcneill@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 */