Page MenuHomeFreeBSD

D59191.id185132.diff
No OneTemporary

D59191.id185132.diff

Index: sys/conf/files
===================================================================
--- sys/conf/files
+++ sys/conf/files
@@ -1633,6 +1633,8 @@
dev/dwc/if_dwc_if.m optional dwc fdt
dev/dwc/dwc1000_core.c optional dwc fdt
dev/dwc/dwc1000_dma.c optional dwc fdt
+dev/dwc/dwc4_core.c optional dwc fdt
+dev/dwc/dwc4_dma.c optional dwc fdt
dev/efidev/efidev.c optional efirt efidev
dev/efidev/efirt.c optional efirt
dev/efidev/efirtc.c optional efirt efirtc
Index: sys/dev/dwc/dwc1000_dma.h
===================================================================
--- sys/dev/dwc/dwc1000_dma.h
+++ sys/dev/dwc/dwc1000_dma.h
@@ -30,18 +30,6 @@
#ifndef __DWC1000_DMA_H__
#define __DWC1000_DMA_H__
-/*
- * A hardware buffer descriptor. Rx and Tx buffers have the same descriptor
- * layout, but the bits in the fields have different meanings.
- */
-struct dwc_hwdesc
-{
- uint32_t desc0;
- uint32_t desc1;
- uint32_t addr1; /* ptr to first buffer data */
- uint32_t addr2; /* ptr to next descriptor / second buffer data*/
-};
-
int dma1000_init(struct dwc_softc *sc);
void dma1000_free(struct dwc_softc *sc);
void dma1000_start(struct dwc_softc *sc);
Index: sys/dev/dwc/dwc1000_dma.c
===================================================================
--- sys/dev/dwc/dwc1000_dma.c
+++ sys/dev/dwc/dwc1000_dma.c
@@ -151,6 +151,21 @@
#define ERDESC1_RBS1_MASK 0x7ffff
#define ERDESC1_RBS1_SHIFT 0 /* Receive Buffer 1 Size */
+/*
+ * A hardware buffer descriptor. Rx and Tx buffers have the same descriptor
+ * layout, but the bits in the fields have different meanings.
+ */
+struct dwc_hwdesc
+{
+ uint32_t desc0;
+ uint32_t desc1;
+ uint32_t addr1; /* ptr to first buffer data */
+ uint32_t addr2; /* ptr to next descriptor / second buffer data*/
+};
+
+#define RX_DESC_SIZE (sizeof(struct dwc_hwdesc) * RX_DESC_COUNT)
+#define TX_DESC_SIZE (sizeof(struct dwc_hwdesc) * TX_DESC_COUNT)
+
/*
* The hardware imposes alignment restrictions on various objects involved in
* DMA transfers. These values are expressed in bytes (not bits).
Index: sys/dev/dwc/dwc4_core.c
===================================================================
--- /dev/null
+++ sys/dev/dwc/dwc4_core.c
@@ -0,0 +1,432 @@
+/*-
+ * Copyright (c) 2026 Ruslan Bukin <br@bsdpad.com>
+ *
+ * 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+
+#include <machine/bus.h>
+
+#include <dev/clk/clk.h>
+#include <dev/hwreset/hwreset.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/mii/mii_fdt.h>
+
+#include <dev/dwc/if_dwcvar.h>
+#include <dev/dwc/dwc4_reg.h>
+#include <dev/dwc/dwc4_dma.h>
+
+#include "if_dwc_if.h"
+
+struct dwc_hash_maddr_ctx {
+ struct dwc_softc *sc;
+ uint32_t hash[8];
+};
+
+#define STATS_HARVEST_INTERVAL 2
+
+/* Pause time field in the transmitted control frame */
+static int dwc_pause_time = 0xffff;
+TUNABLE_INT("hw.dwc.pause_time", &dwc_pause_time);
+
+/*
+ * MIIBUS functions
+ */
+
+int
+dwc4_miibus_read_reg(device_t dev, int phy, int reg)
+{
+ struct dwc_softc *sc;
+ uint32_t mii;
+ size_t cnt;
+ int rv = 0;
+
+ sc = device_get_softc(dev);
+
+ mii = ((phy << MACMDIOAR_PA_S) & MACMDIOAR_PA_M)
+ | ((reg << MACMDIOAR_RDA_S) & MACMDIOAR_RDA_M)
+ | (sc->mii_clk << MACMDIOAR_CR_S)
+ | MACMDIOAR_READ
+ | MACMDIOAR_GB; /* Busy flag */
+
+ WRITE4(sc, ETH_MACMDIOAR, mii);
+
+ for (cnt = 0; cnt < 1000; cnt++) {
+ if (!(READ4(sc, ETH_MACMDIOAR) & MACMDIOAR_GB)) {
+ rv = READ4(sc, ETH_MACMDIODR);
+ break;
+ }
+ DELAY(10);
+ }
+
+ return rv;
+}
+
+int
+dwc4_miibus_write_reg(device_t dev, int phy, int reg, int val)
+{
+ struct dwc_softc *sc;
+ uint32_t mii;
+ size_t cnt;
+
+ sc = device_get_softc(dev);
+
+ mii = ((phy << MACMDIOAR_PA_S) & MACMDIOAR_PA_M)
+ | ((reg << MACMDIOAR_RDA_S) & MACMDIOAR_RDA_M)
+ | (sc->mii_clk << MACMDIOAR_CR_S)
+ | MACMDIOAR_WRITE
+ | MACMDIOAR_GB; /* Busy flag */
+
+ WRITE4(sc, ETH_MACMDIODR, val);
+ WRITE4(sc, ETH_MACMDIOAR, mii);
+
+ for (cnt = 0; cnt < 1000; cnt++) {
+ if (!(READ4(sc, ETH_MACMDIOAR) & MACMDIOAR_GB)) {
+ break;
+ }
+ DELAY(10);
+ }
+
+ return (0);
+}
+
+void
+dwc4_miibus_statchg(device_t dev)
+{
+ struct dwc_softc *sc;
+ struct mii_data *mii;
+ uint32_t reg;
+
+ /*
+ * Called by the MII bus driver when the PHY establishes
+ * link to set the MAC interface registers.
+ */
+
+ sc = device_get_softc(dev);
+
+ DWC_ASSERT_LOCKED(sc);
+
+ mii = sc->mii_softc;
+
+ if (mii->mii_media_status & IFM_ACTIVE)
+ sc->link_is_up = true;
+ else
+ sc->link_is_up = false;
+
+ reg = READ4(sc, ETH_MACCR);
+ switch (IFM_SUBTYPE(mii->mii_media_active)) {
+ case IFM_1000_T:
+ case IFM_1000_SX:
+ reg &= ~(MACCR_FES | MACCR_PS);
+ break;
+ case IFM_100_TX:
+ reg |= (MACCR_FES | MACCR_PS);
+ break;
+ case IFM_10_T:
+ reg &= ~(MACCR_FES);
+ reg |= (MACCR_PS);
+ break;
+ case IFM_NONE:
+ sc->link_is_up = false;
+ return;
+ default:
+ sc->link_is_up = false;
+ device_printf(dev, "Unsupported media %u\n",
+ IFM_SUBTYPE(mii->mii_media_active));
+ return;
+ }
+ if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
+ reg |= (MACCR_DM);
+ else
+ reg &= ~(MACCR_DM);
+ WRITE4(sc, ETH_MACCR, reg);
+
+ IF_DWC_SET_SPEED(dev, IFM_SUBTYPE(mii->mii_media_active));
+
+}
+
+void
+dwc4_core_setup(struct dwc_softc *sc)
+{
+ uint32_t reg;
+
+ DWC_ASSERT_LOCKED(sc);
+
+ reg = READ4(sc, ETH_MACVR);
+ device_printf(sc->dev, "MAC Version: %x\n", reg);
+
+ /* Enable core */
+ reg = READ4(sc, ETH_MACCR);
+ reg |= (MACCR_JD | MACCR_ACS | MACCR_BE);
+ WRITE4(sc, ETH_MACCR, reg);
+}
+
+void
+dwc4_enable_mac(struct dwc_softc *sc, bool enable)
+{
+ uint32_t reg;
+
+ DWC_ASSERT_LOCKED(sc);
+
+ reg = READ4(sc, ETH_MACCR);
+ if (enable)
+ reg |= MACCR_TE | MACCR_RE;
+ else
+ reg &= ~(MACCR_TE | MACCR_RE);
+ WRITE4(sc, ETH_MACCR, reg);
+}
+
+void
+dwc4_enable_csum_offload(struct dwc_softc *sc)
+{
+ uint32_t reg;
+
+ DWC_ASSERT_LOCKED(sc);
+ reg = READ4(sc, ETH_MACCR);
+ if ((if_getcapenable(sc->ifp) & IFCAP_RXCSUM) != 0)
+ reg |= MACCR_IPC;
+ else
+ reg &= ~MACCR_IPC;
+ WRITE4(sc, ETH_MACCR, reg);
+}
+
+static const uint8_t nibbletab[] = {
+ /* 0x0 0000 -> 0000 */ 0x0,
+ /* 0x1 0001 -> 1000 */ 0x8,
+ /* 0x2 0010 -> 0100 */ 0x4,
+ /* 0x3 0011 -> 1100 */ 0xc,
+ /* 0x4 0100 -> 0010 */ 0x2,
+ /* 0x5 0101 -> 1010 */ 0xa,
+ /* 0x6 0110 -> 0110 */ 0x6,
+ /* 0x7 0111 -> 1110 */ 0xe,
+ /* 0x8 1000 -> 0001 */ 0x1,
+ /* 0x9 1001 -> 1001 */ 0x9,
+ /* 0xa 1010 -> 0101 */ 0x5,
+ /* 0xb 1011 -> 1101 */ 0xd,
+ /* 0xc 1100 -> 0011 */ 0x3,
+ /* 0xd 1101 -> 1011 */ 0xb,
+ /* 0xe 1110 -> 0111 */ 0x7,
+ /* 0xf 1111 -> 1111 */ 0xf, };
+
+static uint8_t
+bitreverse(uint8_t x)
+{
+
+ return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4];
+}
+
+static u_int
+dwc_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
+{
+ struct dwc_hash_maddr_ctx *ctx = arg;
+ uint32_t crc, hashbit, hashreg;
+ uint8_t val;
+
+ crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN);
+ /* Take lower 8 bits and reverse it */
+ val = bitreverse(~crc & 0xff);
+ /*
+ * TODO: There is probably a HW_FEATURES bit which isn't
+ * related to the extended descriptors that describe this
+ */
+ if (!ctx->sc->dma_ext_desc)
+ val >>= 2; /* Only need lower 6 bits */
+ hashreg = (val >> 5);
+ hashbit = (val & 31);
+ ctx->hash[hashreg] |= (1 << hashbit);
+
+ return (1);
+}
+
+void
+dwc4_setup_rxfilter(struct dwc_softc *sc)
+{
+ struct dwc_hash_maddr_ctx ctx;
+ if_t ifp;
+ uint8_t *eaddr;
+ uint32_t ffval, hi, lo;
+ int nhash, i;
+
+ DWC_ASSERT_LOCKED(sc);
+
+ ifp = sc->ifp;
+ /*
+ * TODO: There is probably a HW_FEATURES bit which isn't
+ * related to the extended descriptors that describe this
+ */
+ nhash = sc->dma_ext_desc == false ? 2 : 8;
+
+ /*
+ * Set the multicast (group) filter hash.
+ */
+ if ((if_getflags(ifp) & IFF_ALLMULTI) != 0) {
+ ffval = (MACPFR_PM);
+ for (i = 0; i < nhash; i++)
+ ctx.hash[i] = ~0;
+ } else {
+ ffval = (MACPFR_HMC);
+ for (i = 0; i < nhash; i++)
+ ctx.hash[i] = 0;
+ ctx.sc = sc;
+ if_foreach_llmaddr(ifp, dwc_hash_maddr, &ctx);
+ }
+
+ /*
+ * Set the individual address filter hash.
+ */
+ if ((if_getflags(ifp) & IFF_PROMISC) != 0)
+ ffval |= MACPFR_PR;
+
+ /*
+ * Set the primary address.
+ */
+ eaddr = if_getlladdr(ifp);
+ lo = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) |
+ (eaddr[3] << 24);
+ hi = eaddr[4] | (eaddr[5] << 8);
+ WRITE4(sc, ETH_MACA0LR, lo);
+ WRITE4(sc, ETH_MACA0HR, hi);
+
+ WRITE4(sc, ETH_MACPFR, ffval);
+ if (!sc->dma_ext_desc) {
+ WRITE4(sc, ETH_MACHT0R, ctx.hash[0]);
+ WRITE4(sc, ETH_MACHT1R, ctx.hash[1]);
+ } else {
+ /* TODO: implement ext_desc */
+ }
+}
+
+void
+dwc4_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr)
+{
+ uint32_t hi, lo, rnd;
+
+ /*
+ * Try to recover a MAC address from the running hardware. If there's
+ * something non-zero there, assume the bootloader did the right thing
+ * and just use it.
+ *
+ * Otherwise, set the address to a convenient locally assigned address,
+ * 'bsd' + random 24 low-order bits. 'b' is 0x62, which has the locally
+ * assigned bit set, and the broadcast/multicast bit clear.
+ */
+ lo = READ4(sc, ETH_MACA0LR);
+ hi = READ4(sc, ETH_MACA0HR) & 0xffff;
+ if ((lo != 0xffffffff) || (hi != 0xffff)) {
+ hwaddr[0] = (lo >> 0) & 0xff;
+ hwaddr[1] = (lo >> 8) & 0xff;
+ hwaddr[2] = (lo >> 16) & 0xff;
+ hwaddr[3] = (lo >> 24) & 0xff;
+ hwaddr[4] = (hi >> 0) & 0xff;
+ hwaddr[5] = (hi >> 8) & 0xff;
+ } else {
+ rnd = arc4random() & 0x00ffffff;
+ hwaddr[0] = 'b';
+ hwaddr[1] = 's';
+ hwaddr[2] = 'd';
+ hwaddr[3] = rnd >> 16;
+ hwaddr[4] = rnd >> 8;
+ hwaddr[5] = rnd >> 0;
+ }
+}
+
+/*
+ * Stats
+ */
+
+static void
+dwc4_clear_stats(struct dwc_softc *sc)
+{
+ uint32_t reg;
+
+ reg = READ4(sc, ETH_MMC_CONTROL);
+ reg |= MMC_CONTROL_CNTRST;
+ WRITE4(sc, ETH_MMC_CONTROL, reg);
+}
+
+void
+dwc4_harvest_stats(struct dwc_softc *sc)
+{
+ if_t ifp __unused;
+ int val;
+
+ /* We don't need to harvest too often. */
+ if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL)
+ return;
+
+ sc->stats_harvest_count = 0;
+ ifp = sc->ifp;
+
+ val = READ4(sc, ETH_RX_RECEIVE_ERROR);
+ if_inc_counter(ifp, IFCOUNTER_IERRORS, val);
+
+ val = READ4(sc, ETH_TX_PACKET_COUNT_GOOD_BAD);
+ val -= READ4(sc, ETH_TX_PACKET_COUNT_GOOD);
+ if_inc_counter(ifp, IFCOUNTER_OERRORS, val);
+
+ val = READ4(sc, ETH_TX_SINGLE_COLLISION_GOOD_PACKETS);
+ val += READ4(sc, ETH_TX_MULTIPLE_COLLISION_GOOD_PACKETS);
+ val += READ4(sc, ETH_TX_LATE_COLLISION_PACKETS);
+ val += READ4(sc, ETH_TX_EXCESSIVE_COLLISION_PACKETS);
+ if_inc_counter(ifp, IFCOUNTER_COLLISIONS, val);
+
+ dwc4_clear_stats(sc);
+}
+
+void
+dwc4_intr(struct dwc_softc *sc)
+{
+ uint32_t reg;
+
+ DWC_ASSERT_LOCKED(sc);
+
+ reg = READ4(sc, ETH_MACISR);
+ if (reg)
+ READ4(sc, ETH_MACPHYCSR);
+}
+
+void
+dwc4_intr_disable(struct dwc_softc *sc)
+{
+
+ WRITE4(sc, ETH_MACIER, 0);
+}
Index: sys/dev/dwc/dwc4_dma.h
===================================================================
--- /dev/null
+++ sys/dev/dwc/dwc4_dma.h
@@ -0,0 +1,111 @@
+/*-
+ * Copyright (c) 2026 Ruslan Bukin <br@bsdpad.com>
+ *
+ * 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEV_DWC_DWC4_DMA_H_
+#define _DEV_DWC_DWC4_DMA_H_
+
+#define ETH_DMAMR 0x1000
+#define DMAMR_SWR (1 << 0) /* Software Reset */
+#define ETH_DMASBMR 0x1004
+#define DMASBMR_RD_OSR_LMT_S 16 /* AXI Maximum Read Outstanding Req Limit */
+#define DMASBMR_AAL (1 << 12) /* Address-Aligned Beats */
+#define DMASBMR_EAME (1 << 11) /* Enhanced Address Mode Enable */
+#define DMASBMR_BLEN16 (1 << 3) /* Fixed Burst Length */
+#define DMASBMR_BLEN8 (1 << 2) /* Fixed Burst Length */
+#define DMASBMR_BLEN4 (1 << 1) /* Fixed Burst Length */
+#define DMASBMR_FB (1 << 0) /* Fixed Burst Length */
+#define ETH_DMAISR 0x1008
+#define ETH_DMADS1R 0x100C
+#define ETH_DMADS2R 0x1010
+#define ETH_DMAA4TXACR 0x1020
+#define ETH_DMAA4RXACR 0x1024
+#define ETH_DMAA4DACR 0x1028
+#define ETH_DMALPIEI 0x1040
+#define ETH_DMATBSCTRLR(x) (0x1050 + 0x04 * (x))
+
+/* DMA Channel */
+#define ETH_DMACCR(x) (0x1100 + 0x80 * (x))
+#define DMACCR_PBLX8 (1 << 16) /* 8xPBL mode */
+#define DMACCR_DSL_S 18
+#define ETH_DMACTXCR(x) (0x1104 + 0x80 * (x))
+#define DMACTXCR_EDSE (1 << 28) /* Enhanced Descriptor Enable */
+#define DMACTXCR_TXPBL_S 16 /* Transmit Programmable Burst Length */
+#define DMACTXCR_TXPBL_M (0x3f << DMACTXCR_TXPBL_S)
+#define DMACTXCR_OSF (1 << 4) /* Operate on Second Packet */
+#define DMACTXCR_ST (1 << 0) /* Start or Stop Transmission Command */
+#define ETH_DMACRXCR(x) (0x1108 + 0x80 * (x))
+#define DMACRXCR_RXPBL_S 16 /* Receive Programmable Burst Length */
+#define DMACRXCR_RXPBL_M (0x3f << DMACRXCR_RXPBL_S)
+#define DMACRXCR_RBSZ_S 1 /* Receive Buffer size */
+#define DMACRXCR_RBSZ_M (0x3fff << DMACRXCR_RBSZ_S)
+#define DMACRXCR_SR (1 << 0) /* Start or Stop Receive */
+#define ETH_DMACTXDLAR_HI(x) (0x1110 + 0x80 * (x))
+#define ETH_DMACTXDLAR(x) (0x1114 + 0x80 * (x))
+#define ETH_DMACRXDLAR_HI(x) (0x1118 + 0x80 * (x))
+#define ETH_DMACRXDLAR(x) (0x111C + 0x80 * (x))
+#define ETH_DMACTXDTPR(x) (0x1120 + 0x80 * (x))
+#define ETH_DMACRXDTPR(x) (0x1128 + 0x80 * (x))
+#define ETH_DMACTXRLR(x) (0x112C + 0x80 * (x))
+#define ETH_DMACRXRLR(x) (0x1130 + 0x80 * (x))
+#define ETH_DMACIER(x) (0x1134 + 0x80 * (x))
+#define DMACIER_TIE (1 << 0) /* Transmit Interrupt Enable */
+#define DMACIER_TXSE (1 << 1) /* Transmit Stopped Enable */
+#define DMACIER_TBUE (1 << 2) /* Transmit Buffer Unavailable Enable */
+#define DMACIER_RIE (1 << 6) /* Receive Interrupt Enable */
+#define DMACIER_RBUE (1 << 7) /* Receive Buffer Unavailable Enable */
+#define DMACIER_RSE (1 << 8) /* Receive Stopped Enable */
+#define DMACIER_RWTE (1 << 9) /* Receive Watchdog Timeout Enable */
+#define DMACIER_ETIE (1 << 10) /* Early Transmit Interrupt Enable */
+#define DMACIER_ERIE (1 << 11) /* Early Receive Interrupt Enable */
+#define DMACIER_FBEE (1 << 12) /* Fatal Bus Error Enable */
+#define DMACIER_CDEE (1 << 13) /* Context Descriptor Error Enable */
+#define DMACIER_AIE (1 << 14) /* Abnormal Interrupt Summary Enable */
+#define DMACIER_NIE (1 << 15) /* Normal Interrupt Summary Enable */
+#define ETH_DMACRXIWTR(x) (0x1138 + 0x80 * (x))
+#define ETH_DMACSFCSR(x) (0x113C + 0x80 * (x))
+#define ETH_DMACCATXDR(x) (0x1144 + 0x80 * (x))
+#define ETH_DMACCARXDR(x) (0x114C + 0x80 * (x))
+#define ETH_DMACCATXBR(x) (0x1154 + 0x80 * (x))
+#define ETH_DMACCARXBR(x) (0x115C + 0x80 * (x))
+#define ETH_DMACSR(x) (0x1160 + 0x80 * (x))
+#define DMACSR_NIS (1 << 15) /* Normal Interrupt Summary */
+#define DMACSR_AIS (1 << 14) /* AIS: Abnormal Interrupt Summary */
+#define DMACSR_FBE (1 << 12) /* Fatal Bus Error */
+#define DMACSR_RI (1 << 6) /* Receive Interrupt */
+#define DMACSR_TI (1 << 0) /* Transmit Interrupt */
+#define ETH_DMACMFCR(x) (0x1164 + 0x80 * (x))
+
+int dma4_init(struct dwc_softc *sc);
+void dma4_free(struct dwc_softc *sc);
+void dma4_start(struct dwc_softc *sc);
+void dma4_stop(struct dwc_softc *sc);
+int dma4_reset(struct dwc_softc *sc);
+int dma4_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp);
+void dma4_txfinish_locked(struct dwc_softc *sc);
+void dma4_rxfinish_locked(struct dwc_softc *sc);
+void dma4_txstart(struct dwc_softc *sc);
+int dma4_intr(struct dwc_softc *sc);
+
+#endif /* !_DEV_DWC_DWC4_DMA_H_ */
Index: sys/dev/dwc/dwc4_dma.c
===================================================================
--- /dev/null
+++ sys/dev/dwc/dwc4_dma.c
@@ -0,0 +1,898 @@
+/*-
+ * Copyright (c) 2026 Ruslan Bukin <br@bsdpad.com>
+ *
+ * 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/socket.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_var.h>
+
+#include <machine/bus.h>
+
+#include <dev/clk/clk.h>
+#include <dev/hwreset/hwreset.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/dwc/if_dwcvar.h>
+#include <dev/dwc/dwc4_reg.h>
+#include <dev/dwc/dwc4_dma.h>
+#include <dev/dwc/dwc4_mtl.h>
+
+#define WATCHDOG_TIMEOUT_SECS 5
+#define DMA_RESET_TIMEOUT 100
+
+/* RX Read format */
+#define RDES3_OWN (1 << 31) /* DMA owns the descriptor */
+#define RDES3_IOC (1 << 30) /* Interrupt Enabled on Completion */
+#define RDES3_BUF2V (1 << 25) /* Buffer 2 Address Valid */
+#define RDES3_BUF1V (1 << 24) /* Buffer 1 Address Valid */
+
+/* RX Write-back format */
+#define RDES3_FD (1 << 29) /* First Descriptor */
+#define RDES3_LD (1 << 28) /* Last Descriptor */
+#define RDES3_RE (1 << 20) /* Receive Error */
+#define RDES3_PL_S 0 /* Packet Length */
+#define RDES3_PL_M (0x7fff << RDES3_PL_S)
+#define RDES3_CE (1 << 24) /* CRC Error */
+#define RDES3_LT_S 16 /* Length/Type Field */
+#define RDES3_LT_M (0x3 << RDES3_LT_S)
+
+/* TX Read format */
+#define TDES2_IOC (1 << 31) /* Interrupt on completion */
+#define TDES2_TTSE (1 << 30) /* Transmit Timestamp Enable */
+#define TDES2_B2L_S 16 /* Buffer 2 Length */
+#define TDES2_B2L_M (0x3fff << TDES2_B2L_S)
+#define TDES2_VTIR_S 14 /* VLAN Tag Insertion or Replacement */
+#define TDES2_VTIR_M (0x3 << TDES2_VTIR_S)
+
+#define TDES3_OWN (1 << 31) /* the DMA owns the descriptor */
+#define TDES3_CTXT (1 << 30) /* Context Type */
+#define TDES3_FD (1 << 29) /* First Descriptor */
+#define TDES3_LD (1 << 28) /* Last Descriptor */
+#define TDES3_CPC_S 26
+#define TDES3_CPC_M (0x3 << TDES3_CPC_S)
+#define TDES3_CPC_CRC_PAD (0x0 << TDES3_CPC_S)
+#define TDES3_CPC_CRC (0x1 << TDES3_CPC_S)
+#define TDES3_CPC_CRC_DISABLE (0x2 << TDES3_CPC_S)
+#define TDES3_CPC_CRC_REPLACE (0x3 << TDES3_CPC_S)
+#define TDES3_SAIC_S 23
+#define TDES3_SAIC_M (0x7 << TDES3_SAIC_S)
+#define TDES3_TSE 18 /* TCP Segmentation Enable */
+#define TDES3_CIC_S 16
+#define TDES3_CIC_M (0x3 << TDES3_CIC_S)
+#define TDES3_CIC_HDR (0x1 << TDES3_CIC_S)
+#define TDES3_CIC_FULL (0x2 << TDES3_CIC_S)
+#define TDES3_CIC_FULLP (0x3 << TDES3_CIC_S)
+#define TDES3_TPL_S 0 /* TCP Payload Length */
+#define TDES3_TPL_M (0x3ffff << TDES3_TPL_S)
+
+/*
+ * A hardware buffer descriptor. Rx and Tx buffers have the same descriptor
+ * layout, but the bits in the fields have different meanings.
+ */
+struct dwc_hwdesc
+{
+ uint32_t tdes0; /* Header or Buffer 1 Address[31:0] */
+ uint32_t tdes1; /* Buffer 2 Address [31:0] or Buffer 1 Address[63:32] */
+ uint32_t tdes2;
+ uint32_t tdes3;
+};
+
+#define RX_DESC_SIZE (sizeof(struct dwc_hwdesc) * RX_DESC_COUNT)
+#define TX_DESC_SIZE (sizeof(struct dwc_hwdesc) * TX_DESC_COUNT)
+
+/*
+ * The hardware imposes alignment restrictions on various objects involved in
+ * DMA transfers. These values are expressed in bytes (not bits).
+ */
+#define DWC_DESC_RING_ALIGN 2048
+
+static inline uint32_t
+next_txidx(struct dwc_softc *sc, uint32_t curidx)
+{
+
+ return ((curidx + 1) % TX_DESC_COUNT);
+}
+
+static inline uint32_t
+next_rxidx(struct dwc_softc *sc, uint32_t curidx)
+{
+
+ return ((curidx + 1) % RX_DESC_COUNT);
+}
+
+static void
+dwc_get1paddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+
+ if (error != 0)
+ return;
+ *(bus_addr_t *)arg = segs[0].ds_addr;
+}
+
+inline static void
+txdesc_clear(struct dwc_softc *sc, int idx)
+{
+
+ sc->tx_desccount--;
+ sc->txdesc_ring[idx].tdes0 = 0;
+ sc->txdesc_ring[idx].tdes1 = 0;
+ sc->txdesc_ring[idx].tdes2 = 0;
+ sc->txdesc_ring[idx].tdes3 = 0;
+}
+
+inline static void
+txdesc_setup(struct dwc_softc *sc, int idx, bus_addr_t paddr,
+ uint32_t len, uint32_t flags, bool first, bool last)
+{
+ uint32_t tdes0, tdes1, tdes2, tdes3;
+
+ if (!sc->dma_ext_desc) {
+ tdes0 = paddr;
+ tdes1 = paddr >> 32;
+ tdes2 = len | TDES2_TTSE;
+ tdes3 = flags;
+
+ if (first)
+ tdes3 |= TDES3_FD;
+ if (last) {
+ tdes2 |= TDES2_IOC;
+ tdes3 |= TDES3_LD;
+ }
+ } else {
+ tdes0 = 0;
+ tdes1 = 0;
+ tdes2 = 0;
+ tdes3 = 0;
+ }
+ ++sc->tx_desccount;
+ sc->txdesc_ring[idx].tdes0 = tdes0;
+ sc->txdesc_ring[idx].tdes1 = tdes1;
+ sc->txdesc_ring[idx].tdes2 = tdes2;
+ sc->txdesc_ring[idx].tdes3 = tdes3;
+
+ wmb();
+ sc->txdesc_ring[idx].tdes3 |= TDES3_OWN;
+ wmb();
+}
+
+inline static uint32_t
+rxdesc_setup(struct dwc_softc *sc, int idx, bus_addr_t paddr)
+{
+
+ sc->rxdesc_ring[idx].tdes0 = (uint32_t)paddr;
+ sc->rxdesc_ring[idx].tdes1 = (uint32_t)(paddr >> 32);
+ sc->rxdesc_ring[idx].tdes2 = 0;
+ sc->rxdesc_ring[idx].tdes3 = RDES3_IOC | RDES3_BUF1V;
+
+ wmb();
+ sc->rxdesc_ring[idx].tdes3 |= RDES3_OWN;
+ wmb();
+
+ return (0);
+}
+
+int
+dma4_setup_txbuf(struct dwc_softc *sc, int idx, struct mbuf **mp)
+{
+ struct bus_dma_segment segs[TX_MAP_MAX_SEGS];
+ int error, nsegs;
+ struct mbuf * m;
+ uint32_t flags;
+ int i;
+ int last;
+
+ flags = 0;
+ error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag, sc->txbuf_map[idx].map,
+ *mp, segs, &nsegs, 0);
+ if (error == EFBIG) {
+ /*
+ * The map may be partially mapped from the first call.
+ * Make sure to reset it.
+ */
+ bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map);
+ if ((m = m_defrag(*mp, M_NOWAIT)) == NULL)
+ return (ENOMEM);
+ *mp = m;
+ error = bus_dmamap_load_mbuf_sg(sc->txbuf_tag,
+ sc->txbuf_map[idx].map, *mp, segs, &nsegs, 0);
+ }
+ if (error != 0)
+ return (ENOMEM);
+
+ if (sc->tx_desccount + nsegs > TX_DESC_COUNT) {
+ bus_dmamap_unload(sc->txbuf_tag, sc->txbuf_map[idx].map);
+ return (ENOMEM);
+ }
+
+ m = *mp;
+
+ if ((m->m_pkthdr.csum_flags & CSUM_IP) != 0) {
+ if ((m->m_pkthdr.csum_flags & (CSUM_TCP|CSUM_UDP)) != 0) {
+ if (!sc->dma_ext_desc)
+ flags = TDES3_CIC_FULLP;
+ else
+ flags = 0; /* TODO */
+ } else {
+ if (!sc->dma_ext_desc)
+ flags = TDES3_CIC_HDR;
+ else
+ flags = 0; /* TODO */
+ }
+ }
+
+ bus_dmamap_sync(sc->txbuf_tag, sc->txbuf_map[idx].map,
+ BUS_DMASYNC_PREWRITE);
+
+ sc->txbuf_map[idx].mbuf = m;
+
+ for (i = 0; i < nsegs; i++) {
+ txdesc_setup(sc, sc->tx_desc_head,
+ segs[i].ds_addr, segs[i].ds_len,
+ (i == 0) ? flags : 0, /* only first desc needs flags */
+ (i == 0),
+ (i == nsegs - 1));
+ last = sc->tx_desc_head;
+ sc->tx_desc_head = next_txidx(sc, sc->tx_desc_head);
+ }
+
+ sc->txbuf_map[idx].last_desc_idx = last;
+
+ WRITE4(sc, ETH_DMACTXDTPR(0), sc->txdesc_ring_paddr +
+ sc->tx_desc_head * sizeof(struct dwc_hwdesc));
+
+ return (0);
+}
+
+static int
+dma4_setup_rxbuf(struct dwc_softc *sc, int idx, struct mbuf *m)
+{
+ struct bus_dma_segment seg;
+ int error, nsegs;
+
+ m_adj(m, ETHER_ALIGN);
+
+ error = bus_dmamap_load_mbuf_sg(sc->rxbuf_tag, sc->rxbuf_map[idx].map,
+ m, &seg, &nsegs, 0);
+ if (error != 0)
+ return (error);
+
+ KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
+
+ bus_dmamap_sync(sc->rxbuf_tag, sc->rxbuf_map[idx].map,
+ BUS_DMASYNC_PREREAD);
+
+ sc->rxbuf_map[idx].mbuf = m;
+ rxdesc_setup(sc, idx, seg.ds_addr);
+
+ return (0);
+}
+
+static struct mbuf *
+dwc_alloc_mbufcl(struct dwc_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 struct mbuf *
+dwc_rxfinish_one(struct dwc_softc *sc, struct dwc_hwdesc *desc,
+ struct dwc_bufmap *map)
+{
+ if_t ifp;
+ struct mbuf *m, *m0;
+ int len;
+ uint32_t rdes3;
+
+ m = map->mbuf;
+ ifp = sc->ifp;
+ rdes3 = desc->tdes3;
+
+ if ((rdes3 & (RDES3_FD | RDES3_LD)) != (RDES3_FD | RDES3_LD)) {
+ /*
+ * Something very wrong happens. The whole packet should be
+ * received in one descriptor. Report problem.
+ */
+ device_printf(sc->dev,
+ "%s: RX descriptor without FIRST and LAST bit set: 0x%08X",
+ __func__, rdes3);
+ return (NULL);
+ }
+
+ len = (rdes3 & RDES3_PL_M) >> RDES3_PL_S;
+
+ /* Allocate new buffer */
+ m0 = dwc_alloc_mbufcl(sc);
+ if (m0 == NULL) {
+ device_printf(sc->dev, "no mbufs\n");
+
+ /* no new mbuf available, recycle old */
+ if_inc_counter(sc->ifp, IFCOUNTER_IQDROPS, 1);
+ return (NULL);
+ }
+ /* Do dmasync for newly received packet */
+ bus_dmamap_sync(sc->rxbuf_tag, map->map, BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(sc->rxbuf_tag, map->map);
+
+ /* Received packet is valid, process it */
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = len;
+ m->m_len = len;
+ if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
+
+ /* TODO: implement checksum offload */
+
+ /* Remove trailing FCS */
+ m_adj(m, -ETHER_CRC_LEN);
+
+ DWC_UNLOCK(sc);
+ if_input(ifp, m);
+ DWC_LOCK(sc);
+ return (m0);
+}
+
+void
+dma4_txfinish_locked(struct dwc_softc *sc)
+{
+ struct dwc_bufmap *bmap;
+ struct dwc_hwdesc *desc;
+ if_t ifp;
+ int idx, last_idx;
+ bool map_finished;
+
+ DWC_ASSERT_LOCKED(sc);
+
+ ifp = sc->ifp;
+ /* check if all descriptors of the map are done */
+ while (sc->tx_map_tail != sc->tx_map_head) {
+ map_finished = true;
+ bmap = &sc->txbuf_map[sc->tx_map_tail];
+ idx = sc->tx_desc_tail;
+ last_idx = next_txidx(sc, bmap->last_desc_idx);
+ while (idx != last_idx) {
+ desc = &sc->txdesc_ring[idx];
+ if ((desc->tdes3 & TDES3_OWN) != 0) {
+ map_finished = false;
+ break;
+ }
+ idx = next_txidx(sc, idx);
+ }
+
+ if (!map_finished)
+ break;
+ bus_dmamap_sync(sc->txbuf_tag, bmap->map,
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->txbuf_tag, bmap->map);
+ m_freem(bmap->mbuf);
+ bmap->mbuf = NULL;
+ sc->tx_mapcount--;
+ while (sc->tx_desc_tail != last_idx) {
+ txdesc_clear(sc, sc->tx_desc_tail);
+ sc->tx_desc_tail = next_txidx(sc, sc->tx_desc_tail);
+ }
+ sc->tx_map_tail = next_txidx(sc, sc->tx_map_tail);
+ if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
+ if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
+ }
+
+ /* If there are no buffers outstanding, muzzle the watchdog. */
+ if (sc->tx_desc_tail == sc->tx_desc_head) {
+ sc->tx_watchdog_count = 0;
+ }
+}
+
+void
+dma4_txstart(struct dwc_softc *sc)
+{
+ int enqueued;
+ struct mbuf *m;
+
+ enqueued = 0;
+
+ for (;;) {
+ if (sc->tx_desccount > (TX_DESC_COUNT - TX_MAP_MAX_SEGS + 1)) {
+ if_setdrvflagbits(sc->ifp, IFF_DRV_OACTIVE, 0);
+ break;
+ }
+
+ if (sc->tx_mapcount == (TX_MAP_COUNT - 1)) {
+ if_setdrvflagbits(sc->ifp, IFF_DRV_OACTIVE, 0);
+ break;
+ }
+
+ m = if_dequeue(sc->ifp);
+ if (m == NULL)
+ break;
+ if (dma4_setup_txbuf(sc, sc->tx_map_head, &m) != 0) {
+ if_sendq_prepend(sc->ifp, m);
+ if_setdrvflagbits(sc->ifp, IFF_DRV_OACTIVE, 0);
+ break;
+ }
+ bpf_mtap_if(sc->ifp, m);
+ sc->tx_map_head = next_txidx(sc, sc->tx_map_head);
+ sc->tx_mapcount++;
+ ++enqueued;
+ }
+
+ if (enqueued != 0)
+ sc->tx_watchdog_count = WATCHDOG_TIMEOUT_SECS;
+}
+
+void
+dma4_rxfinish_locked(struct dwc_softc *sc)
+{
+ struct mbuf *m;
+ int error, idx;
+ struct dwc_hwdesc *desc;
+
+ DWC_ASSERT_LOCKED(sc);
+
+ for (;;) {
+ idx = sc->rx_idx;
+ desc = sc->rxdesc_ring + idx;
+ if (desc->tdes3 & RDES3_OWN)
+ break;
+ m = dwc_rxfinish_one(sc, desc, sc->rxbuf_map + idx);
+ if (m == NULL) {
+ wmb();
+ desc->tdes3 = RDES3_BUF1V | RDES3_IOC | RDES3_OWN;
+ wmb();
+ } else {
+ /* We cannot create hole in RX ring */
+ error = dma4_setup_rxbuf(sc, idx, m);
+ if (error != 0)
+ panic("dma4_setup_rxbuf failed: error %d\n",
+ error);
+ }
+
+ WRITE4(sc, ETH_DMACRXDTPR(0), sc->rxdesc_ring_paddr +
+ idx * sizeof(struct dwc_hwdesc));
+
+ sc->rx_idx = next_rxidx(sc, sc->rx_idx);
+ }
+}
+
+/*
+ * Start the DMA controller
+ */
+void
+dma4_start(struct dwc_softc *sc)
+{
+ uint32_t reg;
+
+ DWC_ASSERT_LOCKED(sc);
+
+ /* Initialize DMA and enable transmitters */
+
+ /* Configure TX channel 0 */
+ reg = READ4(sc, ETH_DMACTXCR(0));
+ reg |= DMACTXCR_OSF;
+ WRITE4(sc, ETH_DMACTXCR(0), reg);
+
+ reg = MTLTXQOMR_TSF; /* Transmit Store and Forward */
+ reg |= MTLTXQOMR_EN;
+ reg |= MTLTXQOMR_TQS_8K;
+ WRITE4(sc, ETH_MTLTXQOMR(0), reg);
+
+ /* Configure RX channel 0 */
+ reg = MTLRXQOMR_RSF;
+ reg |= MTLRXQOMR_EHFC;
+ reg |= MTLRXQOMR_RQS_4K;
+ WRITE4(sc, ETH_MTLRXQOMR(0), reg);
+
+ reg = READ4(sc, ETH_MACRXQC0R(0));
+ reg |= MACRXQC0R_RXQ0EN_GEN;
+ WRITE4(sc, ETH_MACRXQC0R(0), reg);
+
+ /* Enable Interrupts. */
+ reg = DMACIER_TIE | DMACIER_TXSE | DMACIER_TBUE | DMACIER_RIE |
+ DMACIER_RBUE | DMACIER_RSE | DMACIER_RWTE | DMACIER_ETIE |
+ DMACIER_ERIE | DMACIER_FBEE | DMACIER_CDEE | DMACIER_AIE |
+ DMACIER_NIE;
+ WRITE4(sc, ETH_DMACIER(0), reg);
+
+ /* Start DMA RX. */
+ reg = READ4(sc, ETH_DMACRXCR(0));
+ reg |= DMACRXCR_SR;
+ WRITE4(sc, ETH_DMACRXCR(0), reg);
+
+ /* Start DMA TX. */
+ reg = READ4(sc, ETH_DMACTXCR(0));
+ reg |= DMACTXCR_ST;
+ WRITE4(sc, ETH_DMACTXCR(0), reg);
+}
+
+/*
+ * Stop the DMA controller
+ */
+void
+dma4_stop(struct dwc_softc *sc)
+{
+ uint32_t reg;
+
+ DWC_ASSERT_LOCKED(sc);
+
+ /* Stop DMA TX */
+ reg = READ4(sc, ETH_DMACTXCR(0));
+ reg &= ~DMACTXCR_ST;
+ WRITE4(sc, ETH_DMACTXCR(0), reg);
+
+ /* Flush TX */
+ reg = READ4(sc, ETH_MTLTXQOMR(0));
+ reg |= MTLTXQOMR_FTQ;
+ WRITE4(sc, ETH_MTLTXQOMR(0), reg);
+
+ /* Stop DMA RX */
+ reg = READ4(sc, ETH_DMACRXCR(0));
+ reg &= ~DMACRXCR_SR;
+ WRITE4(sc, ETH_DMACRXCR(0), reg);
+}
+
+int
+dma4_reset(struct dwc_softc *sc)
+{
+ uint32_t reg;
+ int i;
+
+ reg = READ4(sc, ETH_DMAMR);
+ reg |= DMAMR_SWR;
+ WRITE4(sc, ETH_DMAMR, reg);
+
+ for (i = 0; i < DMA_RESET_TIMEOUT; i++) {
+ if ((READ4(sc, ETH_DMAMR) & DMAMR_SWR) == 0)
+ break;
+ DELAY(10);
+ }
+ if (i >= DMA_RESET_TIMEOUT)
+ return (ENXIO);
+
+ return (0);
+}
+
+/*
+ * Create the bus_dma resources
+ */
+int
+dma4_init(struct dwc_softc *sc)
+{
+ struct mbuf *m;
+ uint32_t reg;
+ int error;
+ int idx;
+
+ WRITE4(sc, ETH_MACQ0TXFCR, 0xffff << 16 | 1);
+ WRITE4(sc, ETH_MACRXFCR, 1);
+
+ reg = READ4(sc, ETH_DMACCR(0));
+ if (sc->nopblx8)
+ reg &= ~DMACCR_PBLX8;
+ else
+ reg |= DMACCR_PBLX8;
+ WRITE4(sc, ETH_DMACCR(0), reg);
+
+ WRITE4(sc, ETH_DMACTXCR(0), 0);
+
+ reg = (MCLBYTES << DMACRXCR_RBSZ_S);
+ WRITE4(sc, ETH_DMACRXCR(0), reg);
+
+ reg = DMASBMR_BLEN4;
+ reg |= DMASBMR_BLEN8;
+ reg |= DMASBMR_BLEN16;
+ reg |= DMASBMR_EAME;
+ WRITE4(sc, ETH_DMASBMR, reg);
+
+ reg = READ4(sc, ETH_DMASBMR);
+ if (sc->fixed_burst)
+ reg |= DMASBMR_FB;
+ else
+ reg &= ~DMASBMR_FB;
+ if (sc->aal)
+ reg |= DMASBMR_AAL;
+ else
+ reg &= ~DMASBMR_AAL;
+ WRITE4(sc, ETH_DMASBMR, reg);
+
+ /* No support in the driver. */
+ sc->dma_ext_desc = false;
+
+ /*
+ * DMA must be stop while changing descriptor list addresses.
+ */
+
+ /* Stop DMA TX */
+ reg = READ4(sc, ETH_DMACTXCR(0));
+ reg &= ~DMACTXCR_ST;
+ WRITE4(sc, ETH_DMACTXCR(0), reg);
+
+ /* Stop DMA RX */
+ reg = READ4(sc, ETH_DMACRXCR(0));
+ reg &= ~DMACRXCR_SR;
+ WRITE4(sc, ETH_DMACRXCR(0), reg);
+
+ /*
+ * Set up TX descriptor ring, descriptors, and dma maps.
+ */
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(sc->dev), /* Parent tag. */
+ DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ TX_DESC_SIZE, 1, /* maxsize, nsegments */
+ TX_DESC_SIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->txdesc_tag);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "could not create TX ring DMA tag.\n");
+ goto out;
+ }
+
+ error = bus_dmamem_alloc(sc->txdesc_tag, (void **)&sc->txdesc_ring,
+ BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO,
+ &sc->txdesc_map);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "could not allocate TX descriptor ring.\n");
+ goto out;
+ }
+
+ error = bus_dmamap_load(sc->txdesc_tag, sc->txdesc_map,
+ sc->txdesc_ring, TX_DESC_SIZE, dwc_get1paddr,
+ &sc->txdesc_ring_paddr, 0);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "could not load TX descriptor ring map.\n");
+ goto out;
+ }
+
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(sc->dev), /* Parent tag. */
+ 1, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MCLBYTES * TX_MAP_MAX_SEGS, /* maxsize */
+ TX_MAP_MAX_SEGS, /* nsegments */
+ MCLBYTES, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->txbuf_tag);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "could not create TX ring DMA tag.\n");
+ goto out;
+ }
+
+ for (idx = 0; idx < TX_MAP_COUNT; idx++) {
+ error = bus_dmamap_create(sc->txbuf_tag, 0,
+ &sc->txbuf_map[idx].map);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "could not create TX buffer DMA map.\n");
+ goto out;
+ }
+ }
+
+ for (idx = 0; idx < TX_DESC_COUNT; idx++)
+ txdesc_clear(sc, idx);
+
+ WRITE4(sc, ETH_DMACTXDLAR_HI(0), sc->txdesc_ring_paddr >> 32);
+ WRITE4(sc, ETH_DMACTXDLAR(0), sc->txdesc_ring_paddr);
+ WRITE4(sc, ETH_DMACTXRLR(0), TX_DESC_COUNT - 1);
+
+ /*
+ * Set up RX descriptor ring, descriptors, dma maps, and mbufs.
+ */
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(sc->dev), /* Parent tag. */
+ DWC_DESC_RING_ALIGN, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ RX_DESC_SIZE, 1, /* maxsize, nsegments */
+ RX_DESC_SIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->rxdesc_tag);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "could not create RX ring DMA tag.\n");
+ goto out;
+ }
+
+ error = bus_dmamem_alloc(sc->rxdesc_tag, (void **)&sc->rxdesc_ring,
+ BUS_DMA_COHERENT | BUS_DMA_WAITOK | BUS_DMA_ZERO,
+ &sc->rxdesc_map);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "could not allocate RX descriptor ring.\n");
+ goto out;
+ }
+
+ error = bus_dmamap_load(sc->rxdesc_tag, sc->rxdesc_map,
+ sc->rxdesc_ring, RX_DESC_SIZE, dwc_get1paddr,
+ &sc->rxdesc_ring_paddr, 0);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "could not load RX descriptor ring map.\n");
+ goto out;
+ }
+
+ error = bus_dma_tag_create(
+ bus_get_dma_tag(sc->dev), /* Parent tag. */
+ 1, 0, /* alignment, boundary */
+ BUS_SPACE_MAXADDR, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filter, filterarg */
+ MCLBYTES, 1, /* maxsize, nsegments */
+ MCLBYTES, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->rxbuf_tag);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "could not create RX buf DMA tag.\n");
+ goto out;
+ }
+
+ for (idx = 0; idx < RX_MAP_COUNT; idx++) {
+ error = bus_dmamap_create(sc->rxbuf_tag, 0,
+ &sc->rxbuf_map[idx].map);
+ if (error != 0) {
+ device_printf(sc->dev,
+ "could not create RX buffer DMA map.\n");
+ goto out;
+ }
+ if ((m = dwc_alloc_mbufcl(sc)) == NULL) {
+ device_printf(sc->dev, "Could not alloc mbuf\n");
+ error = ENOMEM;
+ goto out;
+ }
+ if ((error = dma4_setup_rxbuf(sc, idx, m)) != 0) {
+ device_printf(sc->dev,
+ "could not create new RX buffer.\n");
+ goto out;
+ }
+ }
+
+ WRITE4(sc, ETH_DMACRXDLAR_HI(0), sc->rxdesc_ring_paddr >> 32);
+ WRITE4(sc, ETH_DMACRXDLAR(0), sc->rxdesc_ring_paddr);
+
+ WRITE4(sc, ETH_DMACRXDTPR(0), sc->rxdesc_ring_paddr +
+ (RX_DESC_COUNT - 1) * sizeof(struct dwc_hwdesc));
+ WRITE4(sc, ETH_DMACRXRLR(0), RX_DESC_COUNT - 1);
+
+out:
+ if (error != 0)
+ return (ENXIO);
+
+ return (0);
+}
+
+/*
+ * Free the bus_dma resources
+ */
+void
+dma4_free(struct dwc_softc *sc)
+{
+ bus_dmamap_t map;
+ int idx;
+
+ /* Clean up RX DMA resources and free mbufs. */
+ for (idx = 0; idx < RX_DESC_COUNT; ++idx) {
+ if ((map = sc->rxbuf_map[idx].map) != NULL) {
+ bus_dmamap_unload(sc->rxbuf_tag, map);
+ bus_dmamap_destroy(sc->rxbuf_tag, map);
+ m_freem(sc->rxbuf_map[idx].mbuf);
+ }
+ }
+ if (sc->rxbuf_tag != NULL)
+ bus_dma_tag_destroy(sc->rxbuf_tag);
+ if (sc->rxdesc_map != NULL) {
+ bus_dmamap_unload(sc->rxdesc_tag, sc->rxdesc_map);
+ bus_dmamem_free(sc->rxdesc_tag, sc->rxdesc_ring,
+ sc->rxdesc_map);
+ }
+ if (sc->rxdesc_tag != NULL)
+ bus_dma_tag_destroy(sc->rxdesc_tag);
+
+ /* Clean up TX DMA resources. */
+ for (idx = 0; idx < TX_DESC_COUNT; ++idx) {
+ if ((map = sc->txbuf_map[idx].map) != NULL) {
+ /* TX maps are already unloaded. */
+ bus_dmamap_destroy(sc->txbuf_tag, map);
+ }
+ }
+ if (sc->txbuf_tag != NULL)
+ bus_dma_tag_destroy(sc->txbuf_tag);
+ if (sc->txdesc_map != NULL) {
+ bus_dmamap_unload(sc->txdesc_tag, sc->txdesc_map);
+ bus_dmamem_free(sc->txdesc_tag, sc->txdesc_ring,
+ sc->txdesc_map);
+ }
+ if (sc->txdesc_tag != NULL)
+ bus_dma_tag_destroy(sc->txdesc_tag);
+}
+
+/*
+ * Interrupt function
+ */
+
+int
+dma4_intr(struct dwc_softc *sc)
+{
+ uint32_t reg;
+ int rv;
+
+ rv = 0;
+
+ DWC_ASSERT_LOCKED(sc);
+
+ reg = READ4(sc, ETH_DMACSR(0));
+
+ if (reg & DMACSR_NIS) {
+ if (reg & DMACSR_RI)
+ dma4_rxfinish_locked(sc);
+
+ if (reg & DMACSR_TI) {
+ dma4_txfinish_locked(sc);
+ dma4_txstart(sc);
+ }
+ }
+
+ if (reg & DMACSR_AIS) {
+ if (reg & DMACSR_FBE) {
+ /* Fatal Bus Error. */
+ rv = EIO;
+ }
+ }
+
+ WRITE4(sc, ETH_DMACSR(0), reg);
+
+ return (rv);
+}
Index: sys/dev/dwc/dwc4_mtl.h
===================================================================
--- /dev/null
+++ sys/dev/dwc/dwc4_mtl.h
@@ -0,0 +1,124 @@
+/*-
+ * Copyright (c) 2026 Ruslan Bukin <br@bsdpad.com>
+ *
+ * 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEV_DWC_DWC4_MTL_H_
+#define _DEV_DWC_DWC4_MTL_H_
+
+#define ETH_MTLOMR 0x0C00
+#define ETH_MTLISR 0x0C20
+#define ETH_MTLRXQDMAMR 0x0C30
+#define ETH_MTLTBSCR 0x0C40
+#define ETH_MTLESTCR 0x0C50
+#define ETH_MTLESTECR 0x0C54
+#define ETH_MTLESTSR 0x0C58
+#define ETH_MTLESTSCHER 0x0C60
+#define ETH_MTLESTFSER 0x0C64
+#define ETH_MTLESTFSCR 0x0C68
+#define ETH_MTLESTIER 0x0C70
+#define ETH_MTLESTGCLCR 0x0C80
+#define ETH_MTLESTGCLDR 0x0C84
+#define ETH_MTLFPECSR 0x0C90
+#define ETH_MTLFPEAR 0x0C94
+
+#define ETH_MTLTXQOMR(x) (0x0D00 + 0x40 * (x))
+#define MTLTXQOMR_TQS_S 16 /* Transmit queue size */
+#define MTLTXQOMR_TQS_M (0x1f << MTLTXQOMR_TQS_S)
+#define MTLTXQOMR_TQS_8K (0x1f << MTLTXQOMR_TQS_S)
+#define MTLTXQOMR_TXQEN_S 2 /* Transmit Queue Enable */
+#define MTLTXQOMR_TXQEN_M (0x3 << MTLTXQOMR_TXQEN_S)
+#define MTLTXQOMR_EN_AV_MODE (0x1 << MTLTXQOMR_TXQEN_S)
+#define MTLTXQOMR_EN (0x2 << MTLTXQOMR_TXQEN_S)
+#define MTLTXQOMR_TSF (1 << 1) /* Transmit Store and Forward */
+#define MTLTXQOMR_FTQ (1 << 0) /* Flush Transmit Queue */
+#define ETH_MTLTXQUR(x) (0x0D04 + 0x40 * (x))
+#define ETH_MTLTXQDR(x) (0x0D08 + 0x40 * (x))
+#define ETH_MTLTXQESR(x) (0x0D14 + 0x40 * (x))
+#define ETH_MTLTXQQWR(x) (0x0D18 + 0x40 * (x))
+#define ETH_MTLQICSR(x) (0x0D2C + 0x40 * (x))
+#define ETH_MTLRXQOMR(x) (0x0D30 + 0x40 * (x))
+#define MTLRXQOMR_RQS_S 20 /* Receive Queue Size */
+#define MTLRXQOMR_RQS_M (0xf << MTLRXQOMR_RQS_S)
+#define MTLRXQOMR_RQS_4K (0xf << MTLRXQOMR_RQS_S)
+#define MTLRXQOMR_EHFC (1 << 7) /* Enable Hardware Flow Control */
+#define MTLRXQOMR_RSF (1 << 5) /* Receive Queue Store and Forward */
+#define MTLRXQOMR_RTC_S 0 /* Receive Queue Threshold Control */
+#define MTLRXQOMR_RTC_M (0x3 << MTLRXQOMR_RTC_S)
+#define MTLRXQOMR_RTC_64 (0x0 << MTLRXQOMR_RTC_S)
+#define MTLRXQOMR_RTC_32 (0x1 << MTLRXQOMR_RTC_S)
+#define MTLRXQOMR_RTC_96 (0x2 << MTLRXQOMR_RTC_S)
+#define MTLRXQOMR_RTC_128 (0x3 << MTLRXQOMR_RTC_S)
+#define ETH_MTLRXQMPOCR(x) (0x0D34 + 0x40 * (x))
+#define ETH_MTLRXQDR(x) (0x0D38 + 0x40 * (x))
+#define ETH_MTLRXQCR(x) (0x0D3C + 0x40 * (x))
+
+#define ETH_MTLTXQ0OMR 0x0D00
+#define ETH_MTLTXQ0UR 0x0D04
+#define ETH_MTLTXQ0DR 0x0D08
+#define ETH_MTLTXQ0ESR 0x0D14
+#define ETH_MTLTXQ0QWR 0x0D18
+#define ETH_MTLQ0ICSR 0x0D2C
+#define ETH_MTLRXQ0OMR 0x0D30
+#define ETH_MTLRXQ0MPOCR 0x0D34
+#define ETH_MTLRXQ0DR 0x0D38
+#define ETH_MTLRXQ0CR 0x0D3C
+
+#define ETH_MTLTXQ1OMR 0x0D40
+#define ETH_MTLTXQ1UR 0x0D44
+#define ETH_MTLTXQ1DR 0x0D48
+#define ETH_MTLTXQ1ECR 0x0D50
+#define ETH_MTLTXQ1ESR 0x0D54
+#define ETH_MTLTXQ1QWR 0x0D58
+#define ETH_MTLTXQ1SSCR 0x0D5C
+#define ETH_MTLTXQ1HCR 0x0D60
+#define ETH_MTLTXQ1LCR 0x0D64
+#define ETH_MTLQ1ICSR 0x0D6C
+#define ETH_MTLRXQ1OMR 0x0D70
+#define ETH_MTLRXQ1MPOCR 0x0D74
+#define ETH_MTLRXQ1DR 0x0D78
+#define ETH_MTLRXQ1CR 0x0D7C
+
+#define ETH_MTLTXQ2OMR 0x0D80
+#define ETH_MTLTXQ2UR 0x0D84
+#define ETH_MTLTXQ2DR 0x0D88
+#define ETH_MTLTXQ2ECR 0x0D90
+#define ETH_MTLTXQ2ESR 0x0D94
+#define ETH_MTLTXQ2QWR 0x0D98
+#define ETH_MTLTXQ2SSCR 0x0D9C
+#define ETH_MTLTXQ2HCR 0x0DA0
+#define ETH_MTLTXQ2LCR 0x0DA4
+#define ETH_MTLQ2ICSR 0x0DAC
+
+#define ETH_MTLTXQ3OMR 0x0DC0
+#define ETH_MTLTXQ3UR 0x0DC4
+#define ETH_MTLTXQ3DR 0x0DC8
+#define ETH_MTLTXQ3ECR 0x0DD0
+#define ETH_MTLTXQ3ESR 0x0DD4
+#define ETH_MTLTXQ3QWR 0x0DD8
+#define ETH_MTLTXQ3SSCR 0x0DDC
+#define ETH_MTLTXQ3HCR 0x0DE0
+#define ETH_MTLTXQ3LCR 0x0DE4
+#define ETH_MTLQ3ICSR 0x0DEC
+
+#endif /* _DEV_DWC_DWC4_MTL_H_ */
Index: sys/dev/dwc/dwc4_reg.h
===================================================================
--- /dev/null
+++ sys/dev/dwc/dwc4_reg.h
@@ -0,0 +1,242 @@
+/*-
+ * Copyright (c) 2026 Ruslan Bukin <br@bsdpad.com>
+ *
+ * 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEV_DWC_DWC4_REG_H_
+#define _DEV_DWC_DWC4_REG_H_
+
+#define ETH_MACCR 0x0000
+#define MACCR_IPC (1 << 27) /* Checksum Offload */
+#define MACCR_ACS (1 << 20) /* Automatic Pad or CRC Stripping */
+#define MACCR_BE (1 << 18) /* Packet Burst Enable */
+#define MACCR_JD (1 << 17) /* Jabber Disable */
+#define MACCR_PS (1 << 15) /* Port Select */
+#define MACCR_FES (1 << 14) /* MAC Speed */
+#define MACCR_DM (1 << 13) /* Duplex Mode */
+#define MACCR_TE (1 << 1) /* Transmitter Enable */
+#define MACCR_RE (1 << 0) /* Receiver Enable */
+#define ETH_MACECR 0x0004
+#define ETH_MACPFR 0x0008
+#define MACPFR_RA (1 << 31) /* Receive All */
+#define MACPFR_PM (1 << 4) /* Pass All Multicast */
+#define MACPFR_HMC (1 << 2) /* Hash Multicast */
+#define MACPFR_PR (1 << 0) /* Promiscuous Mode */
+#define ETH_MACWJBTR 0x000C
+#define ETH_MACHT0R 0x0010
+#define ETH_MACHT1R 0x0014
+#define ETH_MACVTCR 0x0050
+#define ETH_MACVTDR 0x0054
+#define ETH_MACVHTR 0x0058
+#define ETH_MACVIR 0x0060
+#define ETH_MACiVIR 0x0064
+#define ETH_MACQ0TXFCR 0x0070
+#define ETH_MACRXFCR 0x0090
+#define ETH_MACRXQCR 0x0094
+
+#define ETH_MACRXQC0R(x) (0x00A0 + 0x4 * (x))
+#define MACRXQC0R_RXQ0EN_S 0 /* Receive Queue 0 Enable */
+#define MACRXQC0R_RXQ0EN_AV (0x1 << MACRXQC0R_RXQ0EN_S)
+#define MACRXQC0R_RXQ0EN_GEN (0x2 << MACRXQC0R_RXQ0EN_S)
+#define MACRXQC0R_RXQ0EN_M (0x3 << MACRXQC0R_RXQ0EN_S)
+#define ETH_MACRXQC1R 0x00A4
+#define ETH_MACRXQC2R 0x00A8
+
+#define ETH_MACISR 0x00B0
+#define ETH_MACIER 0x00B4
+#define ETH_MACRXTXSR 0x00B8
+#define ETH_MACPCSR 0x00C0
+#define ETH_MACRWKPFR 0x00C4
+#define ETH_MACLCSR 0x00D0
+#define ETH_MACLTCR 0x00D4
+#define ETH_MACLETR 0x00D8
+#define ETH_MAC1USTCR 0x00DC
+#define ETH_MACPHYCSR 0x00F8
+#define ETH_MACVR 0x0110
+#define ETH_MACDR 0x0114
+#define ETH_MACHWF0R 0x011C
+#define ETH_MACHWF1R 0x0120
+#define ETH_MACHWF2R 0x0124
+#define ETH_MACHWF3R 0x0128
+#define ETH_MACMDIOAR 0x0200
+#define MACMDIOAR_CR_S 8 /* CSR Clock Range */
+#define MACMDIOAR_CR_M (0xf << MACMDIOAR_CR_S)
+#define MACMDIOAR_GOC_S 2 /* GMII Operation Command */
+#define MACMDIOAR_GOC_M (0x3 << MACMDIOAR_GOC_S)
+#define MACMDIOAR_WRITE (0x1 << MACMDIOAR_GOC_S)
+#define MACMDIOAR_POST_RD_INC (0x2 << MACMDIOAR_GOC_S)
+#define MACMDIOAR_READ (0x3 << MACMDIOAR_GOC_S)
+#define MACMDIOAR_GB (1 << 0) /* GMII Busy */
+#define MACMDIOAR_PA_S 21 /* Physical Layer Address */
+#define MACMDIOAR_PA_M (0x1f << MACMDIOAR_PA_S)
+#define MACMDIOAR_RDA_S 16 /* Register/Device Address */
+#define MACMDIOAR_RDA_M (0x1f << MACMDIOAR_RDA_S)
+#define ETH_MACMDIODR 0x0204
+#define ETH_MACARPAR 0x0210
+#define ETH_MACCSRSWCR 0x0230
+#define ETH_MACFPECSR 0x0234
+#define ETH_MACPRSTIMR 0x0240
+#define ETH_MACPRSTIMUR 0x0244
+#define ETH_MACA0HR 0x0300
+#define ETH_MACA0LR 0x0304
+#define ETH_MACA1HR 0x0308
+#define ETH_MACA1LR 0x030C
+#define ETH_MACA2HR 0x0310
+#define ETH_MACA2LR 0x0314
+#define ETH_MACA3HR 0x0318
+#define ETH_MACA3LR 0x031C
+#define ETH_MMC_CONTROL 0x0700
+#define MMC_CONTROL_CNTRST (1 << 0) /* Counters Reset */
+#define ETH_MMC_RX_INTERRUPT 0x0704
+#define ETH_MMC_TX_INTERRUPT 0x0708
+#define ETH_MMC_RX_INTERRUPT_MASK 0x070C
+#define ETH_MMC_TX_INTERRUPT_MASK 0x0710
+#define ETH_TX_OCTET_COUNT_GOOD_BAD 0x0714
+#define ETH_TX_PACKET_COUNT_GOOD_BAD 0x0718
+#define ETH_TX_BROADCAST_PACKETS_GOOD 0x071C
+#define ETH_TX_MULTICAST_PACKETS_GOOD 0x0720
+#define ETH_TX_64OCTETS_PACKETS_GOOD_BAD 0x0724
+#define ETH_TX_64TO127OCTETS_PACKETS_GOOD_BAD 0x0728
+#define ETH_TX_128TO255OCTETS_PACKETS_GOOD_BAD 0x072C
+#define ETH_TX_256TO511OCTETS_PACKETS_GOOD_BAD 0x0730
+#define ETH_TX_512TO1023OCTETS_PACKETS_GOOD_BAD 0x0734
+#define ETH_TX_1024TOMAXOCTETS_PACKETS_GOOD_BAD 0x0738
+#define ETH_TX_UNICAST_PACKETS_GOOD_BAD 0x073C
+#define ETH_TX_MULTICAST_PACKETS_GOOD_BAD 0x0740
+#define ETH_TX_BROADCAST_PACKETS_GOOD_BAD 0x0744
+#define ETH_TX_UNDERFLOW_ERROR_PACKETS 0x0748
+#define ETH_TX_SINGLE_COLLISION_GOOD_PACKETS 0x074C
+#define ETH_TX_MULTIPLE_COLLISION_GOOD_PACKETS 0x0750
+#define ETH_TX_DEFERRED_PACKETS 0x0754
+#define ETH_TX_LATE_COLLISION_PACKETS 0x0758
+#define ETH_TX_EXCESSIVE_COLLISION_PACKETS 0x075C
+#define ETH_TX_CARRIER_ERROR_PACKETS 0x0760
+#define ETH_TX_OCTET_COUNT_GOOD 0x0764
+#define ETH_TX_PACKET_COUNT_GOOD 0x0768
+#define ETH_TX_EXCESSIVE_DEFERRAL_ERROR 0x076C
+#define ETH_TX_PAUSE_PACKETS 0x0770
+#define ETH_TX_VLAN_PACKETS_GOOD 0x0774
+#define ETH_TX_OSIZE_PACKETS_GOOD 0x0778
+#define ETH_RX_PACKETS_COUNT_GOOD_BAD 0x0780
+#define ETH_RX_OCTET_COUNT_GOOD_BAD 0x0784
+#define ETH_RX_OCTET_COUNT_GOOD 0x0788
+#define ETH_RX_BROADCAST_PACKETS_GOOD 0x078C
+#define ETH_RX_MULTICAST_PACKETS_GOOD 0x0790
+#define ETH_RX_CRC_ERROR_PACKETS 0x0794
+#define ETH_RX_ALIGNMENT_ERROR_PACKETS 0x0798
+#define ETH_RX_RUNT_ERROR_PACKETS 0x079C
+#define ETH_RX_JABBER_ERROR_PACKETS 0x07A0
+#define ETH_RX_UNDERSIZE_PACKETS_GOOD 0x07A4
+#define ETH_RX_OVERSIZE_PACKETS_GOOD 0x07A8
+#define ETH_RX_64OCTETS_PACKETS_GOOD_BAD 0x07AC
+#define ETH_RX_65TO127OCTETS_PACKETS_GOOD_BAD 0x07B0
+#define ETH_RX_128TO255OCTETS_PACKETS_GOOD_BAD 0x07B4
+#define ETH_RX_256TO511OCTETS_PACKETS_GOOD_BAD 0x07B8
+#define ETH_RX_512TO1023OCTETS_PACKETS_GOOD_BAD 0x07BC
+#define ETH_RX_1024TOMAXOCTETS_PACKETS_GOOD_BAD 0x07C0
+#define ETH_RX_UNICAST_PACKETS_GOOD 0x07C4
+#define ETH_RX_LENGTH_ERROR_PACKETS 0x07C8
+#define ETH_RX_OUT_OF_RANGE_PACKETS 0x07CC
+#define ETH_RX_PAUSE_PACKETS 0x07D0
+#define ETH_RX_FIFO_OVERFLOW_PACKETS 0x07D4
+#define ETH_RX_VLAN_PACKETS_GOOD_BAD 0x07D8
+#define ETH_RX_WATCHDOG_ERROR_PACKETS 0x07DC
+#define ETH_RX_RECEIVE_ERROR 0x07E0
+#define ETH_RX_CONTROL_PACKETS_GOOD 0x07E4
+#define ETH_TX_LPI_USEC_CNTR 0x07EC
+#define ETH_TX_LPI_TRAN_CNTR 0x07F0
+#define ETH_RX_LPI_USEC_CNTR 0x07F4
+#define ETH_RX_LPI_TRAN_CNTR 0x07F8
+#define ETH_MMC_FPE_TX_ISR 0x08A0
+#define ETH_MMC_FPE_TX_IMR 0x08A4
+#define ETH_MMC_FPE_TX_FCR 0x08A8
+#define ETH_MMC_TX_HRCR 0x08AC
+#define ETH_MMC_FPE_RX_ISR 0x08C0
+#define ETH_MMC_FPE_RX_IMR 0x08C4
+#define ETH_RX_PACKET_ASM_ERR 0x08C8
+#define ETH_RX_PACKET_SMD_ERR 0x08CC
+#define ETH_RX_PACKET_ASM_OKR 0x08D0
+#define ETH_RX_FPE_FRAG_CR 0x08D4
+#define ETH_MACL3L4C0R 0x0900
+#define ETH_MACL4A0R 0x0904
+#define ETH_MACL3A00R 0x0910
+#define ETH_MACL3A10R 0x0914
+#define ETH_MACL3A20R 0x0918
+#define ETH_MACL3A30R 0x091C
+#define ETH_MACL3L4C1R 0x0930
+#define ETH_MACL4A1R 0x0934
+#define ETH_MACL3A01R 0x0940
+#define ETH_MACL3A11R 0x0944
+#define ETH_MACL3A21R 0x0948
+#define ETH_MACL3A31R 0x094C
+#define ETH_MAC_IACR 0x0A70
+#define ETH_MAC_TMRQR 0x0A74
+#define ETH_MACTSCR 0x0B00
+#define ETH_MACSSIR 0x0B04
+#define ETH_MACSTSR 0x0B08
+#define ETH_MACSTNR 0x0B0C
+#define ETH_MACSTSUR 0x0B10
+#define ETH_MACSTNUR 0x0B14
+#define ETH_MACTSAR 0x0B18
+#define ETH_MACTSSR 0x0B20
+#define ETH_MACRXDTI 0x0B24
+#define ETH_MACTXDTI 0x0B28
+#define ETH_MACTXTSSNR 0x0B30
+#define ETH_MACTXTSSSR 0x0B34
+#define ETH_MACACR 0x0B40
+#define ETH_MACATSNR 0x0B48
+#define ETH_MACATSSR 0x0B4C
+#define ETH_MACTSIACR 0x0B50
+#define ETH_MACTSEACR 0x0B54
+#define ETH_MACTSICNR 0x0B58
+#define ETH_MACTSECNR 0x0B5C
+#define ETH_MACTSILR 0x0B68
+#define ETH_MACTSELR 0x0B6C
+#define ETH_MACPPSCR 0x0B70
+#define ETH_MACPPSTTS0R 0x0B80
+#define ETH_MACPPSTTN0R 0x0B84
+#define ETH_MACPPSI0R 0x0B88
+#define ETH_MACPPSW0R 0x0B8C
+#define ETH_MACPPSTTS1R 0x0B90
+#define ETH_MACPPSTTN1R 0x0B94
+#define ETH_MACPPSI1R 0x0B98
+#define ETH_MACPPSW1R 0x0B9C
+#define ETH_MACPOCR 0x0BC0
+#define ETH_MACSPI0R 0x0BC4
+#define ETH_MACSPI1R 0x0BC8
+#define ETH_MACSPI2R 0x0BCC
+#define ETH_MACLMIR 0x0BD0
+
+int dwc4_miibus_read_reg(device_t dev, int phy, int reg);
+int dwc4_miibus_write_reg(device_t dev, int phy, int reg, int val);
+void dwc4_miibus_statchg(device_t dev);
+void dwc4_core_setup(struct dwc_softc *sc);
+void dwc4_enable_mac(struct dwc_softc *sc, bool enable);
+void dwc4_enable_csum_offload(struct dwc_softc *sc);
+void dwc4_setup_rxfilter(struct dwc_softc *sc);
+void dwc4_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr);
+void dwc4_harvest_stats(struct dwc_softc *sc);
+void dwc4_intr(struct dwc_softc *softc);
+void dwc4_intr_disable(struct dwc_softc *sc);
+
+#endif /* !_DEV_DWC_DWC4_REG_H_ */
Index: sys/dev/dwc/if_dwc.c
===================================================================
--- sys/dev/dwc/if_dwc.c
+++ sys/dev/dwc/if_dwc.c
@@ -70,7 +70,10 @@
#include <dev/dwc/if_dwcvar.h>
#include <dev/dwc/dwc1000_core.h>
+#include <dev/dwc/dwc1000_reg.h>
#include <dev/dwc/dwc1000_dma.h>
+#include <dev/dwc/dwc4_dma.h>
+#include <dev/dwc/dwc4_reg.h>
#include "if_dwc_if.h"
#include "gpio_if.h"
@@ -145,7 +148,7 @@
if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
IFF_DRV_RUNNING)
return;
- dma1000_txstart(sc);
+ sc->ops->dma_txstart(sc);
}
static void
@@ -169,16 +172,16 @@
return;
/*
- * Call mii_mediachg() which will call back into dwc1000_miibus_statchg()
+ * Call mii_mediachg() which will call back into dwc*_miibus_statchg()
* to set up the remaining config registers based on current media.
*/
mii_mediachg(sc->mii_softc);
- dwc1000_setup_rxfilter(sc);
- dwc1000_core_setup(sc);
- dwc1000_enable_mac(sc, true);
- dwc1000_enable_csum_offload(sc);
- dma1000_start(sc);
+ sc->ops->core_setup_rxfilter(sc);
+ sc->ops->core_setup(sc);
+ sc->ops->core_enable_mac(sc, true);
+ sc->ops->core_enable_csum_offload(sc);
+ sc->ops->dma_start(sc);
if_setdrvflagbits(ifp, IFF_DRV_RUNNING, IFF_DRV_OACTIVE);
@@ -209,8 +212,8 @@
callout_stop(&sc->dwc_callout);
- dma1000_stop(sc);
- dwc1000_enable_mac(sc, false);
+ sc->ops->dma_stop(sc);
+ sc->ops->core_enable_mac(sc, false);
}
static int
@@ -232,7 +235,7 @@
if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
flags = if_getflags(ifp) ^ sc->if_flags;
if ((flags & (IFF_PROMISC|IFF_ALLMULTI)) != 0)
- dwc1000_setup_rxfilter(sc);
+ sc->ops->core_setup_rxfilter(sc);
} else {
if (!sc->is_detaching)
dwc_init_locked(sc);
@@ -248,7 +251,7 @@
case SIOCDELMULTI:
if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
DWC_LOCK(sc);
- dwc1000_setup_rxfilter(sc);
+ sc->ops->core_setup_rxfilter(sc);
DWC_UNLOCK(sc);
}
break;
@@ -276,7 +279,7 @@
if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) {
DWC_LOCK(sc);
- dwc1000_enable_csum_offload(sc);
+ sc->ops->core_enable_csum_offload(sc);
DWC_UNLOCK(sc);
}
break;
@@ -302,8 +305,8 @@
sc = arg;
DWC_LOCK(sc);
- dwc1000_intr(sc);
- rv = dma1000_intr(sc);
+ sc->ops->core_intr(sc);
+ rv = sc->ops->dma_intr(sc);
if (rv == EIO) {
device_printf(sc->dev,
"Ethernet DMA error, restarting controller.\n");
@@ -336,12 +339,12 @@
*/
if (sc->tx_watchdog_count > 0) {
if (--sc->tx_watchdog_count == 0) {
- dma1000_txfinish_locked(sc);
+ sc->ops->dma_txfinish_locked(sc);
}
}
/* Gather stats from hardware counters. */
- dwc1000_harvest_stats(sc);
+ sc->ops->core_harvest_stats(sc);
/* Check the media status. */
link_was_up = sc->link_is_up;
@@ -473,6 +476,52 @@
return (0);
}
+static struct dwc_ops dwc_ops_3 = {
+ .dma_init = dma1000_init,
+ .dma_free = dma1000_free,
+ .dma_start = dma1000_start,
+ .dma_stop = dma1000_stop,
+ .dma_reset = dma1000_reset,
+ .dma_txfinish_locked = dma1000_txfinish_locked,
+ .dma_txstart = dma1000_txstart,
+ .dma_intr = dma1000_intr,
+
+ .core_miibus_read_reg = dwc1000_miibus_read_reg,
+ .core_miibus_write_reg = dwc1000_miibus_write_reg,
+ .core_miibus_statchg = dwc1000_miibus_statchg,
+ .core_setup = dwc1000_core_setup,
+ .core_enable_mac = dwc1000_enable_mac,
+ .core_enable_csum_offload = dwc1000_enable_csum_offload,
+ .core_setup_rxfilter = dwc1000_setup_rxfilter,
+ .core_get_hwaddr = dwc1000_get_hwaddr,
+ .core_harvest_stats = dwc1000_harvest_stats,
+ .core_intr = dwc1000_intr,
+ .core_intr_disable = dwc1000_intr_disable,
+};
+
+static struct dwc_ops dwc_ops_4 = {
+ .dma_init = dma4_init,
+ .dma_free = dma4_free,
+ .dma_start = dma4_start,
+ .dma_stop = dma4_stop,
+ .dma_reset = dma4_reset,
+ .dma_txfinish_locked = dma4_txfinish_locked,
+ .dma_txstart = dma4_txstart,
+ .dma_intr = dma4_intr,
+
+ .core_miibus_read_reg = dwc4_miibus_read_reg,
+ .core_miibus_write_reg = dwc4_miibus_write_reg,
+ .core_miibus_statchg = dwc4_miibus_statchg,
+ .core_setup = dwc4_core_setup,
+ .core_enable_mac = dwc4_enable_mac,
+ .core_enable_csum_offload = dwc4_enable_csum_offload,
+ .core_setup_rxfilter = dwc4_setup_rxfilter,
+ .core_get_hwaddr = dwc4_get_hwaddr,
+ .core_harvest_stats = dwc4_harvest_stats,
+ .core_intr = dwc4_intr,
+ .core_intr_disable = dwc4_intr_disable,
+};
+
/*
* Probe/Attach functions
*/
@@ -562,8 +611,16 @@
return (ENXIO);
}
+ if (sc->flags & DWC_HAS_GMAC5 || sc->flags & DWC_HAS_GMAC4) {
+ device_printf(dev, "gmac version %x\n", READ4(sc, ETH_MACVR));
+ sc->ops = &dwc_ops_4;
+ } else {
+ device_printf(dev, "gmac version %x\n", READ4(sc, VERSION));
+ sc->ops = &dwc_ops_3;
+ }
+
/* Read MAC before reset */
- dwc1000_get_hwaddr(sc, macaddr);
+ sc->ops->core_get_hwaddr(sc, macaddr);
/* Reset the PHY if needed */
if (dwc_reset_phy(sc) != 0) {
@@ -573,13 +630,13 @@
}
/* Reset */
- if ((error = dma1000_reset(sc)) != 0) {
+ if ((error = sc->ops->dma_reset(sc)) != 0) {
device_printf(sc->dev, "Can't reset DMA controller.\n");
bus_release_resources(sc->dev, dwc_spec, sc->res);
return (error);
}
- if (dma1000_init(sc)) {
+ if (sc->ops->dma_init(sc)) {
bus_release_resources(dev, dwc_spec, sc->res);
return (ENXIO);
}
@@ -644,7 +701,7 @@
* Disable and tear down interrupts before anything else, so we don't
* race with the handler.
*/
- dwc1000_intr_disable(sc);
+ sc->ops->core_intr_disable(sc);
if (sc->intr_cookie != NULL) {
bus_teardown_intr(dev, sc->res[1], sc->intr_cookie);
}
@@ -661,7 +718,7 @@
bus_generic_detach(dev);
/* Free DMA descriptors */
- dma1000_free(sc);
+ sc->ops->dma_free(sc);
if (sc->ifp != NULL) {
if_free(sc->ifp);
@@ -674,15 +731,42 @@
return (0);
}
+static int
+dwc_miibus_read_reg(device_t dev, int phy, int reg)
+{
+ struct dwc_softc *sc;
+
+ sc = device_get_softc(dev);
+ return (sc->ops->core_miibus_read_reg(dev, phy, reg));
+}
+
+static int
+dwc_miibus_write_reg(device_t dev, int phy, int reg, int val)
+{
+ struct dwc_softc *sc;
+
+ sc = device_get_softc(dev);
+ return (sc->ops->core_miibus_write_reg(dev, phy, reg, val));
+}
+
+static void
+dwc_miibus_statchg(device_t dev)
+{
+ struct dwc_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->ops->core_miibus_statchg(dev);
+}
+
static device_method_t dwc_methods[] = {
DEVMETHOD(device_probe, dwc_probe),
DEVMETHOD(device_attach, dwc_attach),
DEVMETHOD(device_detach, dwc_detach),
/* MII Interface */
- DEVMETHOD(miibus_readreg, dwc1000_miibus_read_reg),
- DEVMETHOD(miibus_writereg, dwc1000_miibus_write_reg),
- DEVMETHOD(miibus_statchg, dwc1000_miibus_statchg),
+ DEVMETHOD(miibus_readreg, dwc_miibus_read_reg),
+ DEVMETHOD(miibus_writereg, dwc_miibus_write_reg),
+ DEVMETHOD(miibus_statchg, dwc_miibus_statchg),
DEVMETHOD_END
};
Index: sys/dev/dwc/if_dwcvar.h
===================================================================
--- sys/dev/dwc/if_dwcvar.h
+++ sys/dev/dwc/if_dwcvar.h
@@ -42,14 +42,24 @@
* Driver data and defines.
*/
#define RX_DESC_COUNT 1024
-#define RX_DESC_SIZE (sizeof(struct dwc_hwdesc) * RX_DESC_COUNT)
+#define RX_MAP_COUNT RX_DESC_COUNT
#define TX_DESC_COUNT 1024
#define TX_MAP_COUNT TX_DESC_COUNT
-#define TX_DESC_SIZE (sizeof(struct dwc_hwdesc) * TX_DESC_COUNT)
#define TX_MAP_MAX_SEGS 32
#define DMA_DEFAULT_PBL 8
+/* Synopsys Core versions IDs */
+#define DWMAC_CORE_3_40 0x34
+#define DWMAC_CORE_3_50 0x35
+#define DWMAC_CORE_3_70 0x37
+#define DWMAC_CORE_4_00 0x40
+#define DWMAC_CORE_4_10 0x41
+#define DWMAC_CORE_5_00 0x50
+#define DWMAC_CORE_5_10 0x51
+#define DWMAC_CORE_5_20 0x52
+#define DWMAC_CORE_5_40 0x54
+
struct dwc_bufmap {
bus_dmamap_t map;
struct mbuf *mbuf;
@@ -57,6 +67,31 @@
int last_desc_idx;
};
+struct dwc_softc;
+
+struct dwc_ops {
+ int (*dma_init)(struct dwc_softc *sc);
+ void (*dma_free)(struct dwc_softc *sc);
+ void (*dma_start)(struct dwc_softc *sc);
+ void (*dma_stop)(struct dwc_softc *sc);
+ int (*dma_reset)(struct dwc_softc *sc);
+ void (*dma_txfinish_locked)(struct dwc_softc *sc);
+ void (*dma_txstart)(struct dwc_softc *sc);
+ int (*dma_intr)(struct dwc_softc *sc);
+
+ int (*core_miibus_read_reg)(device_t dev, int phy, int reg);
+ int (*core_miibus_write_reg)(device_t dev, int phy, int reg, int val);
+ void (*core_miibus_statchg)(device_t dev);
+ void (*core_setup)(struct dwc_softc *sc);
+ void (*core_enable_mac)(struct dwc_softc *sc, bool enable);
+ void (*core_enable_csum_offload)(struct dwc_softc *sc);
+ void (*core_setup_rxfilter)(struct dwc_softc *sc);
+ void (*core_get_hwaddr)(struct dwc_softc *sc, uint8_t *hwaddr);
+ void (*core_harvest_stats)(struct dwc_softc *sc);
+ void (*core_intr)(struct dwc_softc *softc);
+ void (*core_intr_disable)(struct dwc_softc *sc);
+};
+
struct dwc_softc {
struct resource *res[2];
device_t dev;
@@ -113,6 +148,12 @@
uint32_t tx_map_tail;
int tx_desccount;
int tx_mapcount;
+ struct dwc_ops *ops;
+
+ int flags;
+#define DWC_HAS_GMAC5 (1 << 2)
+#define DWC_HAS_GMAC4 (1 << 1)
+#define DWC_HAS_GMAC3 (1 << 0)
};
#define READ4(_sc, _reg) \

File Metadata

Mime Type
text/plain
Expires
Tue, Oct 20, 9:08 AM (1 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41103587
Default Alt Text
D59191.id185132.diff (64 KB)

Event Timeline