git: 4bf8ce037dc8 - main - if_rge: initial import of if_rge driver from OpenBSD.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 15 Dec 2025 02:00:18 UTC
The branch main has been updated by adrian:
URL: https://cgit.FreeBSD.org/src/commit/?id=4bf8ce037dc8fa699be87350bb6467f1b74cb96d
commit 4bf8ce037dc8fa699be87350bb6467f1b74cb96d
Author: Adrian Chadd <adrian@FreeBSD.org>
AuthorDate: 2025-12-06 05:26:30 +0000
Commit: Adrian Chadd <adrian@FreeBSD.org>
CommitDate: 2025-12-15 02:00:09 +0000
if_rge: initial import of if_rge driver from OpenBSD.
This is an initial import of the if_rge driver from OpenBSD
and adapted to FreeBSD.
Differential Revision: https://reviews.freebsd.org/D54101
---
share/man/man4/Makefile | 1 +
share/man/man4/rge.4 | 165 +
sys/conf/files | 4 +
sys/dev/rge/if_rge.c | 2683 ++++++++
sys/dev/rge/if_rge_debug.h | 63 +
sys/dev/rge/if_rge_hw.c | 1935 ++++++
sys/dev/rge/if_rge_hw.h | 41 +
sys/dev/rge/if_rge_microcode.h | 13401 +++++++++++++++++++++++++++++++++++++++
sys/dev/rge/if_rge_stats.c | 133 +
sys/dev/rge/if_rge_stats.h | 26 +
sys/dev/rge/if_rge_sysctl.c | 238 +
sys/dev/rge/if_rge_sysctl.h | 25 +
sys/dev/rge/if_rge_vendor.h | 28 +
sys/dev/rge/if_rgereg.h | 387 ++
sys/dev/rge/if_rgevar.h | 269 +
sys/modules/Makefile | 1 +
sys/modules/rge/Makefile | 7 +
17 files changed, 19407 insertions(+)
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 8cf4a06ea9bb..25c3ba18073f 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -501,6 +501,7 @@ MAN= aac.4 \
random.4 \
rctl.4 \
re.4 \
+ rge.4 \
rgephy.4 \
rights.4 \
rl.4 \
diff --git a/share/man/man4/rge.4 b/share/man/man4/rge.4
new file mode 100644
index 000000000000..072f1ece3230
--- /dev/null
+++ b/share/man/man4/rge.4
@@ -0,0 +1,165 @@
+.\"
+.\" Copyright (c) 2025 Adrian Chadd <adrian@FreeBSD.org>
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd December 12, 2025
+.Dt RGE 4
+.Os
+.Sh NAME
+.Nm rge
+.Nd RealTek RTL8125/RTL8126/RTL8127/Killer E3000 PCIe Ethernet adapter driver
+.Sh SYNOPSIS
+To compile this driver into the kernel,
+place the following lines in your
+kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device rge"
+.Ed
+.Pp
+Alternatively, to load the driver as a
+module at boot time, place the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+if_rge_load="YES"
+.Ed
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for various NICs based on the RealTek RTL8125,
+RTL8126 and RTL8127 PCIe Ethernet controllers.
+.Pp
+All of the NICs supported by this driver support 10, 100 and 1000Mbit
+over CAT5 cable.
+NICs based on the RTL8125 additionally support 2.5Gbit over CAT6 cable.
+NICs based on the RTL8126 additionally support 2.5Gbit and 5Gbit over CAT6
+cable.
+NICs based on the RTL8127 additionally support 2.5Gbit, 5Gbit and 10Gbit
+over CAT6 cable.
+.Pp
+All NICs supported by the
+.Nm
+driver have TCP/IP checksum offload and hardware VLAN tagging/insertion
+features, and use a descriptor-based DMA mechanism.
+They are also
+capable of TCP large send (TCP segmentation offload).
+.Pp
+The RTL8125, RTL8126 and RTL8127 devices are single-chip solutions combining
+both a MAC and PHY.
+Standalone cards are available in 1x PCIe models.
+.Pp
+The RTL8125, RTL8126 and RTL8127 also support jumbo frames, which can be
+configured via the interface MTU setting.
+The MTU is limited to 9126.
+Selecting an MTU larger than 1500 bytes with the
+.Xr ifconfig 8
+utility configures the adapter to receive and transmit jumbo frames.
+.Pp
+The
+.Nm
+driver supports the following media types:
+.Bl -tag -width "10baseT/UTP"
+.It Cm autoselect
+Enable autoselection of the media type and options.
+The user can manually override
+the autoselected mode by adding media options to
+.Xr rc.conf 5 .
+.It Cm 10baseT/UTP
+Set 10Mbps operation.
+The
+.Xr ifconfig 8
+.Cm mediaopt
+option can also be used to select either
+.Cm full-duplex
+or
+.Cm half-duplex
+modes.
+.It Cm 100baseTX
+Set 100Mbps (Fast Ethernet) operation.
+The
+.Xr ifconfig 8
+.Cm mediaopt
+option can also be used to select either
+.Cm full-duplex
+or
+.Cm half-duplex
+modes.
+.It Cm 1000baseTX
+Set 1000baseTX operation over twisted pair.
+The RealTek gigE chips support 1000Mbps in
+.Cm full-duplex
+mode only.
+.It Cm 2500baseTX
+Set 2500baseTX operation over twisted pair.
+The RealTek devices support 2.5Gbit in
+.Cm full-duplex
+mode only.
+.It Cm 5000baseTX
+Set 5000baseTX operation over twisted pair.
+The RealTek devices support 5Gbit in
+.Cm full-duplex
+mode only.
+.It Cm 10000baseTX
+Set 10000baseTX operation over twisted pair.
+The RealTek devices support 10Gbit in
+.Cm full-duplex
+mode only.
+.El
+.Pp
+The
+.Nm
+driver supports the following media options:
+.Bl -tag -width "full-duplex"
+.It Cm full-duplex
+Force full duplex operation.
+.It Cm half-duplex
+Force half duplex operation.
+.El
+.Pp
+For more information on configuring this device, see
+.Xr ifconfig 8 .
+.Sh HARDWARE
+The
+.Nm
+driver supports RealTek RTL8125, RTL8126, RTL8125 and Killer E3000 based
+PCIe 1GB to 1GB Ethernet devices.
+.Sh SYSCTL VARIABLES
+The following variables are available as both
+.Xr sysctl 8
+variables and
+.Xr loader 8
+tunables:
+.Bl -tag -width "xxxxxx"
+.It Va dev.rge.%d.debug
+Configure runtime debug output. This is a 32 bit bitmask.
+.El
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "rge%d: watchdog timeout"
+The device has stopped responding to the network, or there is a problem with
+the network connection (cable).
+.El
+.Sh SEE ALSO
+.Xr altq 4 ,
+.Xr arp 4 ,
+.Xr miibus 4 ,
+.Xr netintro 4 ,
+.Xr ng_ether 4 ,
+.Xr polling 4 ,
+.Xr vlan 4 ,
+.Xr ifconfig 8
+.Rs
+.%U https://www.realtek.com/
+.Re
+.Sh HISTORY
+The
+.Nm
+device driver first appeared in
+.Fx 16.0 .
+.Sh AUTHORS
+The
+.Nm
+driver was written by
+.An Kevin Lo Aq Mt kevlo@openbsd.org
+and ported to FreeBSD by
+.An Adrian Chadd Aq Mt adrian@freebsd.org .
diff --git a/sys/conf/files b/sys/conf/files
index 9c5220c7befa..df939f5788ca 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -2866,6 +2866,10 @@ dev/regulator/regnode_if.m optional regulator
dev/regulator/regulator.c optional regulator
dev/regulator/regulator_bus.c optional regulator fdt
dev/regulator/regulator_fixed.c optional regulator
+dev/rge/if_rge.c optional rge
+dev/rge/if_rge_hw.c optional rge
+dev/rge/if_rge_stats.c optional rge
+dev/rge/if_rge_sysctl.c optional rge
dev/rl/if_rl.c optional rl pci
dev/rndtest/rndtest.c optional rndtest
#
diff --git a/sys/dev/rge/if_rge.c b/sys/dev/rge/if_rge.c
new file mode 100644
index 000000000000..b011968905a6
--- /dev/null
+++ b/sys/dev/rge/if_rge.c
@@ -0,0 +1,2683 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019, 2020, 2023-2025 Kevin Lo <kevlo@openbsd.org>
+ * Copyright (c) 2025 Adrian Chadd <adrian@FreeBSD.org>
+ *
+ * Hardware programming portions from Realtek Semiconductor.
+ *
+ * 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.
+ */
+
+/* $OpenBSD: if_rge.c,v 1.38 2025/09/19 00:41:14 kevlo Exp $ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/endian.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_media.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <net/bpf.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_vlan_var.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/mii/mii.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+
+#include "if_rge_vendor.h"
+#include "if_rgereg.h"
+#include "if_rgevar.h"
+#include "if_rge_hw.h"
+#include "if_rge_microcode.h"
+#include "if_rge_debug.h"
+#include "if_rge_sysctl.h"
+#include "if_rge_stats.h"
+
+#define RGE_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP)
+
+static int rge_attach(device_t);
+static int rge_detach(device_t);
+
+#if 0
+int rge_activate(struct device *, int);
+#endif
+static void rge_intr_msi(void *);
+static int rge_ioctl(struct ifnet *, u_long, caddr_t);
+static int rge_transmit_if(if_t, struct mbuf *);
+static void rge_qflush_if(if_t);
+static void rge_init_if(void *);
+static void rge_init_locked(struct rge_softc *);
+static void rge_stop_locked(struct rge_softc *);
+static int rge_ifmedia_upd(if_t);
+static void rge_ifmedia_sts(if_t, struct ifmediareq *);
+static int rge_allocmem(struct rge_softc *);
+static int rge_alloc_stats_mem(struct rge_softc *);
+static int rge_freemem(struct rge_softc *);
+static int rge_free_stats_mem(struct rge_softc *);
+static int rge_newbuf(struct rge_queues *);
+static void rge_rx_list_init(struct rge_queues *);
+static void rge_tx_list_init(struct rge_queues *);
+static void rge_fill_rx_ring(struct rge_queues *);
+static int rge_rxeof(struct rge_queues *, struct mbufq *);
+static int rge_txeof(struct rge_queues *);
+static void rge_iff_locked(struct rge_softc *);
+static void rge_add_media_types(struct rge_softc *);
+static void rge_tx_task(void *, int);
+static void rge_txq_flush_mbufs(struct rge_softc *sc);
+static void rge_tick(void *);
+static void rge_link_state(struct rge_softc *);
+#if 0
+#ifndef SMALL_KERNEL
+int rge_wol(struct ifnet *, int);
+void rge_wol_power(struct rge_softc *);
+#endif
+#endif
+
+struct rge_matchid {
+ uint16_t vendor;
+ uint16_t device;
+ const char *name;
+};
+
+const struct rge_matchid rge_devices[] = {
+ { PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_E3000, "Killer E3000" },
+ { PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RTL8125, "RTL8125" },
+ { PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RTL8126, "RTL8126", },
+ { PCI_VENDOR_REALTEK, PCI_PRODUCT_REALTEK_RTL8127, "RTL8127" },
+ { 0, 0, NULL }
+};
+
+static int
+rge_probe(device_t dev)
+{
+ uint16_t vendor, device;
+ const struct rge_matchid *ri;
+
+ vendor = pci_get_vendor(dev);
+ device = pci_get_device(dev);
+
+ for (ri = rge_devices; ri->name != NULL; ri++) {
+ if ((vendor == ri->vendor) && (device == ri->device)) {
+ device_set_desc(dev, ri->name);
+ return (BUS_PROBE_DEFAULT);
+ }
+ }
+
+ return (ENXIO);
+}
+
+static void
+rge_attach_if(struct rge_softc *sc, const char *eaddr)
+{
+ if_initname(sc->sc_ifp, device_get_name(sc->sc_dev),
+ device_get_unit(sc->sc_dev));
+ if_setdev(sc->sc_ifp, sc->sc_dev);
+ if_setinitfn(sc->sc_ifp, rge_init_if);
+ if_setsoftc(sc->sc_ifp, sc);
+ if_setflags(sc->sc_ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
+ if_setioctlfn(sc->sc_ifp, rge_ioctl);
+ if_settransmitfn(sc->sc_ifp, rge_transmit_if);
+ if_setqflushfn(sc->sc_ifp, rge_qflush_if);
+
+ /* Set offload as appropriate */
+ if_sethwassist(sc->sc_ifp, CSUM_IP | CSUM_TCP | CSUM_UDP);
+ if_setcapabilities(sc->sc_ifp, IFCAP_HWCSUM);
+ if_setcapenable(sc->sc_ifp, if_getcapabilities(sc->sc_ifp));
+
+ /* TODO: set WOL */
+
+ /* Attach interface */
+ ether_ifattach(sc->sc_ifp, eaddr);
+ sc->sc_ether_attached = true;
+
+ /* post ether_ifattach() bits */
+
+ /* VLAN capabilities */
+ if_setcapabilitiesbit(sc->sc_ifp, IFCAP_VLAN_MTU |
+ IFCAP_VLAN_HWTAGGING, 0);
+ if_setcapabilitiesbit(sc->sc_ifp, IFCAP_VLAN_HWCSUM, 0);
+ if_setcapenable(sc->sc_ifp, if_getcapabilities(sc->sc_ifp));
+
+ if_setifheaderlen(sc->sc_ifp, sizeof(struct ether_vlan_header));
+
+ /* TODO: is this needed for iftransmit? */
+ if_setsendqlen(sc->sc_ifp, RGE_TX_LIST_CNT - 1);
+ if_setsendqready(sc->sc_ifp);
+}
+
+static int
+rge_attach(device_t dev)
+{
+ uint8_t eaddr[ETHER_ADDR_LEN];
+ struct rge_softc *sc;
+ struct rge_queues *q;
+ uint32_t hwrev, reg;
+ int i, rid;
+ int error;
+ int msic;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+ sc->sc_ifp = if_gethandle(IFT_ETHER);
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
+ MTX_DEF);
+
+ /* Enable bus mastering */
+ pci_enable_busmaster(dev);
+
+ /*
+ * Map control/status registers.
+ */
+
+ /*
+ * The openbsd driver (and my E3000 NIC) handle registering three
+ * kinds of BARs - a 64 bit MMIO BAR, a 32 bit MMIO BAR, and then
+ * a legacy IO port BAR.
+ *
+ * To simplify bring-up, I'm going to request resources for the first
+ * MMIO BAR (BAR2) which should be a 32 bit BAR.
+ */
+ rid = PCIR_BAR(2);
+ sc->sc_bres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_bres == NULL) {
+ RGE_PRINT_ERROR(sc,
+ "Unable to allocate bus resource: memory\n");
+ goto fail;
+ }
+ sc->rge_bhandle = rman_get_bushandle(sc->sc_bres);
+ sc->rge_btag = rman_get_bustag(sc->sc_bres);
+ sc->rge_bsize = rman_get_size(sc->sc_bres);
+
+ q = malloc(sizeof(struct rge_queues), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (q == NULL) {
+ RGE_PRINT_ERROR(sc, "Unable to malloc rge_queues memory\n");
+ goto fail;
+ }
+ q->q_sc = sc;
+ q->q_index = 0;
+
+ sc->sc_queues = q;
+ sc->sc_nqueues = 1;
+
+ /* Check if PCIe */
+ if (pci_find_cap(dev, PCIY_EXPRESS, ®) == 0) {
+ sc->rge_flags |= RGE_FLAG_PCIE;
+ sc->sc_expcap = reg;
+ }
+
+ /* Allocate MSI */
+ msic = pci_msi_count(dev);
+ if (msic == 0) {
+ RGE_PRINT_ERROR(sc, "%s: only MSI interrupts supported\n",
+ __func__);
+ goto fail;
+ }
+
+ msic = RGE_MSI_MESSAGES;
+ if (pci_alloc_msi(dev, &msic) != 0) {
+ RGE_PRINT_ERROR(sc, "%s: failed to allocate MSI\n",
+ __func__);
+ goto fail;
+ }
+
+ sc->rge_flags |= RGE_FLAG_MSI;
+
+ /* We need at least one MSI */
+ if (msic < RGE_MSI_MESSAGES) {
+ RGE_PRINT_ERROR(sc, "%s: didn't allocate enough MSI\n",
+ __func__);
+ goto fail;
+ }
+
+ /*
+ * Allocate interrupt entries.
+ */
+ for (i = 0, rid = 1; i < RGE_MSI_MESSAGES; i++, rid++) {
+ sc->sc_irq[i] = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &rid, RF_ACTIVE);
+ if (sc->sc_irq[i] == NULL) {
+ RGE_PRINT_ERROR(sc, "%s: couldn't allocate MSI %d",
+ __func__, rid);
+ goto fail;
+ }
+ }
+
+ /* Hook interrupts */
+ for (i = 0; i < RGE_MSI_MESSAGES; i++) {
+ error = bus_setup_intr(dev, sc->sc_irq[i],
+ INTR_TYPE_NET | INTR_MPSAFE, NULL, rge_intr_msi,
+ sc, &sc->sc_ih[i]);
+ if (error != 0) {
+ RGE_PRINT_ERROR(sc,
+ "%s: couldn't setup intr %d (error %d)", __func__,
+ i, error);
+ goto fail;
+ }
+ }
+
+ /* Allocate top level bus DMA tag */
+ error = bus_dma_tag_create(bus_get_dma_tag(dev),
+ 1, /* alignment */
+ 0, /* boundary */
+ BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR,
+ NULL, NULL, /* filter (unused) */
+ BUS_SPACE_MAXADDR, /* maxsize */
+ BUS_SPACE_UNRESTRICTED, /* nsegments */
+ BUS_SPACE_MAXADDR, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->sc_dmat);
+ if (error) {
+ RGE_PRINT_ERROR(sc,
+ "couldn't allocate device DMA tag (error %d)\n", error);
+ goto fail;
+ }
+
+ /* Allocate TX/RX descriptor and buffer tags */
+ error = bus_dma_tag_create(sc->sc_dmat,
+ RGE_ALIGN, /* alignment */
+ 0, /* boundary */
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
+ NULL, NULL, /* filter (unused) */
+ RGE_TX_LIST_SZ, /* maxsize */
+ 1, /* nsegments */
+ RGE_TX_LIST_SZ, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->sc_dmat_tx_desc);
+ if (error) {
+ RGE_PRINT_ERROR(sc,
+ "couldn't allocate device TX descriptor "
+ "DMA tag (error %d)\n", error);
+ goto fail;
+ }
+
+ error = bus_dma_tag_create(sc->sc_dmat,
+ 1, /* alignment */
+ 0, /* boundary */
+ BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR,
+ NULL, NULL, /* filter (unused) */
+ RGE_JUMBO_FRAMELEN, /* maxsize */
+ RGE_TX_NSEGS, /* nsegments */
+ RGE_JUMBO_FRAMELEN, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->sc_dmat_tx_buf);
+ if (error) {
+ RGE_PRINT_ERROR(sc,
+ "couldn't allocate device TX buffer DMA tag (error %d)\n",
+ error);
+ goto fail;
+ }
+
+ error = bus_dma_tag_create(sc->sc_dmat,
+ RGE_ALIGN, /* alignment */
+ 0, /* boundary */
+ BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR,
+ NULL, NULL, /* filter (unused) */
+ RGE_RX_LIST_SZ, /* maxsize */
+ 1, /* nsegments */
+ RGE_RX_LIST_SZ, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->sc_dmat_rx_desc);
+ if (error) {
+ RGE_PRINT_ERROR(sc,
+ "couldn't allocate device RX descriptor "
+ "DMA tag (error %d)\n", error);
+ goto fail;
+ }
+
+ error = bus_dma_tag_create(sc->sc_dmat,
+ 1, /* alignment */
+ 0, /* boundary */
+ BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR,
+ NULL, NULL, /* filter (unused) */
+ MCLBYTES, /* maxsize */
+ 1, /* nsegments */
+ MCLBYTES, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->sc_dmat_rx_buf);
+ if (error) {
+ RGE_PRINT_ERROR(sc,
+ "couldn't allocate device RX buffer DMA tag (error %d)\n",
+ error);
+ goto fail;
+ }
+
+ error = bus_dma_tag_create(sc->sc_dmat,
+ RGE_STATS_ALIGNMENT, /* alignment */
+ 0, /* boundary */
+ BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR,
+ NULL, NULL, /* filter (unused) */
+ RGE_STATS_BUF_SIZE, /* maxsize */
+ 1, /* nsegments */
+ RGE_STATS_BUF_SIZE, /* maxsegsize */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockarg */
+ &sc->sc_dmat_stats_buf);
+ if (error) {
+ RGE_PRINT_ERROR(sc,
+ "couldn't allocate device RX buffer DMA tag (error %d)\n",
+ error);
+ goto fail;
+ }
+
+
+ /* Attach sysctl nodes */
+ rge_sysctl_attach(sc);
+
+ /* Determine hardware revision */
+ hwrev = RGE_READ_4(sc, RGE_TXCFG) & RGE_TXCFG_HWREV;
+ switch (hwrev) {
+ case 0x60900000:
+ sc->rge_type = MAC_R25;
+// device_printf(dev, "RTL8125\n");
+ break;
+ case 0x64100000:
+ sc->rge_type = MAC_R25B;
+// device_printf(dev, "RTL8125B\n");
+ break;
+ case 0x64900000:
+ sc->rge_type = MAC_R26;
+// device_printf(dev, "RTL8126\n");
+ break;
+ case 0x68800000:
+ sc->rge_type = MAC_R25D;
+// device_printf(dev, "RTL8125D\n");
+ break;
+ case 0x6c900000:
+ sc->rge_type = MAC_R27;
+// device_printf(dev, "RTL8127\n");
+ break;
+ default:
+ RGE_PRINT_ERROR(sc, "unknown version 0x%08x\n", hwrev);
+ goto fail;
+ }
+
+ rge_config_imtype(sc, RGE_IMTYPE_SIM);
+
+ /* TODO: disable ASPM/ECPM? */
+
+#if 0
+ /*
+ * PCI Express check.
+ */
+ if (pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_PCIEXPRESS,
+ &offset, NULL)) {
+ /* Disable PCIe ASPM and ECPM. */
+ reg = pci_conf_read(pa->pa_pc, pa->pa_tag,
+ offset + PCI_PCIE_LCSR);
+ reg &= ~(PCI_PCIE_LCSR_ASPM_L0S | PCI_PCIE_LCSR_ASPM_L1 |
+ PCI_PCIE_LCSR_ECPM);
+ pci_conf_write(pa->pa_pc, pa->pa_tag, offset + PCI_PCIE_LCSR,
+ reg);
+ }
+#endif
+
+ RGE_LOCK(sc);
+ if (rge_chipinit(sc)) {
+ RGE_UNLOCK(sc);
+ goto fail;
+ }
+
+ rge_get_macaddr(sc, eaddr);
+ RGE_UNLOCK(sc);
+
+ if (rge_allocmem(sc))
+ goto fail;
+ if (rge_alloc_stats_mem(sc))
+ goto fail;
+
+ /* Initialize ifmedia structures. */
+ ifmedia_init(&sc->sc_media, IFM_IMASK, rge_ifmedia_upd,
+ rge_ifmedia_sts);
+ rge_add_media_types(sc);
+ ifmedia_add(&sc->sc_media, IFM_ETHER | IFM_AUTO, 0, NULL);
+ ifmedia_set(&sc->sc_media, IFM_ETHER | IFM_AUTO);
+ sc->sc_media.ifm_media = sc->sc_media.ifm_cur->ifm_media;
+
+ rge_attach_if(sc, eaddr);
+
+ /*
+ * TODO: technically should be per txq but we only support
+ * one TXQ at the moment.
+ */
+ mbufq_init(&sc->sc_txq, RGE_TX_LIST_CNT);
+
+ snprintf(sc->sc_tq_name, sizeof(sc->sc_tq_name),
+ "%s taskq", device_get_nameunit(sc->sc_dev));
+ snprintf(sc->sc_tq_thr_name, sizeof(sc->sc_tq_thr_name),
+ "%s taskq thread", device_get_nameunit(sc->sc_dev));
+
+ sc->sc_tq = taskqueue_create(sc->sc_tq_name, M_NOWAIT,
+ taskqueue_thread_enqueue, &sc->sc_tq);
+ taskqueue_start_threads(&sc->sc_tq, 1, PI_NET, "%s",
+ sc->sc_tq_thr_name);
+
+ TASK_INIT(&sc->sc_tx_task, 0, rge_tx_task, sc);
+
+ callout_init_mtx(&sc->sc_timeout, &sc->sc_mtx, 0);
+
+ return (0);
+fail:
+ rge_detach(dev);
+ return (ENXIO);
+}
+
+/**
+ * @brief flush the mbufq queue
+ *
+ * Again this should likely be per-TXQ.
+ *
+ * This should be called with the driver lock held.
+ */
+static void
+rge_txq_flush_mbufs(struct rge_softc *sc)
+{
+ struct mbuf *m;
+ int ntx = 0;
+
+ RGE_ASSERT_LOCKED(sc);
+
+ while ((m = mbufq_dequeue(&sc->sc_txq)) != NULL) {
+ m_freem(m);
+ ntx++;
+ }
+
+ RGE_DPRINTF(sc, RGE_DEBUG_XMIT, "%s: %d frames flushed\n", __func__,
+ ntx);
+}
+
+static int
+rge_detach(device_t dev)
+{
+ struct rge_softc *sc = device_get_softc(dev);
+ int i, rid;
+
+ /* global flag, detaching */
+ RGE_LOCK(sc);
+ sc->sc_stopped = true;
+ sc->sc_detaching = true;
+ RGE_UNLOCK(sc);
+
+ /* stop/drain network interface */
+ callout_drain(&sc->sc_timeout);
+
+ /* Make sure TX task isn't running */
+ if (sc->sc_tq != NULL) {
+ while (taskqueue_cancel(sc->sc_tq, &sc->sc_tx_task, NULL) != 0)
+ taskqueue_drain(sc->sc_tq, &sc->sc_tx_task);
+ }
+
+ RGE_LOCK(sc);
+ callout_stop(&sc->sc_timeout);
+
+ /* stop NIC / DMA */
+ rge_stop_locked(sc);
+
+ /* TODO: wait for completion */
+
+ /* Free pending TX mbufs */
+ rge_txq_flush_mbufs(sc);
+
+ RGE_UNLOCK(sc);
+
+ /* Free taskqueue */
+ if (sc->sc_tq != NULL) {
+ taskqueue_free(sc->sc_tq);
+ sc->sc_tq = NULL;
+ }
+
+ /* Free descriptor memory */
+ RGE_DPRINTF(sc, RGE_DEBUG_SETUP, "%s: freemem\n", __func__);
+ rge_freemem(sc);
+ rge_free_stats_mem(sc);
+
+ if (sc->sc_ifp) {
+ RGE_DPRINTF(sc, RGE_DEBUG_SETUP, "%s: ifdetach/if_free\n",
+ __func__);
+ if (sc->sc_ether_attached)
+ ether_ifdetach(sc->sc_ifp);
+ if_free(sc->sc_ifp);
+ }
+
+ RGE_DPRINTF(sc, RGE_DEBUG_SETUP, "%s: sc_dmat_tx_desc\n", __func__);
+ if (sc->sc_dmat_tx_desc)
+ bus_dma_tag_destroy(sc->sc_dmat_tx_desc);
+ RGE_DPRINTF(sc, RGE_DEBUG_SETUP, "%s: sc_dmat_tx_buf\n", __func__);
+ if (sc->sc_dmat_tx_buf)
+ bus_dma_tag_destroy(sc->sc_dmat_tx_buf);
+ RGE_DPRINTF(sc, RGE_DEBUG_SETUP, "%s: sc_dmat_rx_desc\n", __func__);
+ if (sc->sc_dmat_rx_desc)
+ bus_dma_tag_destroy(sc->sc_dmat_rx_desc);
+ RGE_DPRINTF(sc, RGE_DEBUG_SETUP, "%s: sc_dmat_rx_buf\n", __func__);
+ if (sc->sc_dmat_rx_buf)
+ bus_dma_tag_destroy(sc->sc_dmat_rx_buf);
+ RGE_DPRINTF(sc, RGE_DEBUG_SETUP, "%s: sc_dmat_stats_buf\n", __func__);
+ if (sc->sc_dmat_stats_buf)
+ bus_dma_tag_destroy(sc->sc_dmat_stats_buf);
+ RGE_DPRINTF(sc, RGE_DEBUG_SETUP, "%s: sc_dmat\n", __func__);
+ if (sc->sc_dmat)
+ bus_dma_tag_destroy(sc->sc_dmat);
+
+ /* Teardown interrupts */
+ for (i = 0; i < RGE_MSI_MESSAGES; i++) {
+ if (sc->sc_ih[i] != NULL) {
+ bus_teardown_intr(sc->sc_dev, sc->sc_irq[i],
+ sc->sc_ih[i]);
+ sc->sc_ih[i] = NULL;
+ }
+ }
+
+ /* Free interrupt resources */
+ for (i = 0, rid = 1; i < RGE_MSI_MESSAGES; i++, rid++) {
+ if (sc->sc_irq[i] != NULL) {
+ bus_release_resource(sc->sc_dev, SYS_RES_IRQ,
+ rid, sc->sc_irq[i]);
+ sc->sc_irq[i] = NULL;
+ }
+ }
+
+ /* Free MSI allocation */
+ if (sc->rge_flags & RGE_FLAG_MSI)
+ pci_release_msi(dev);
+
+ if (sc->sc_bres) {
+ RGE_DPRINTF(sc, RGE_DEBUG_SETUP, "%s: release mmio\n",
+ __func__);
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ rman_get_rid(sc->sc_bres), sc->sc_bres);
+ sc->sc_bres = NULL;
+ }
+
+ if (sc->sc_queues) {
+ free(sc->sc_queues, M_DEVBUF);
+ sc->sc_queues = NULL;
+ }
+
+ mtx_destroy(&sc->sc_mtx);
+
+ return (0);
+}
+
+#if 0
+
+int
+rge_activate(struct device *self, int act)
+{
+#ifndef SMALL_KERNEL
+ struct rge_softc *sc = (struct rge_softc *)self;
+#endif
+
+ switch (act) {
+ case DVACT_POWERDOWN:
+#ifndef SMALL_KERNEL
+ rge_wol_power(sc);
+#endif
+ break;
+ }
+ return (0);
+}
+#endif
+
+static void
+rge_intr_msi(void *arg)
+{
+ struct mbufq rx_mq;
+ struct epoch_tracker et;
+ struct mbuf *m;
+ struct rge_softc *sc = arg;
+ struct rge_queues *q = sc->sc_queues;
+ uint32_t status;
+ int claimed = 0, rv;
+
+ sc->sc_drv_stats.intr_cnt++;
+
+ mbufq_init(&rx_mq, RGE_RX_LIST_CNT);
+
+ if ((if_getdrvflags(sc->sc_ifp) & IFF_DRV_RUNNING) == 0)
+ return;
+
+ RGE_LOCK(sc);
+
+ if (sc->sc_suspended || sc->sc_stopped || sc->sc_detaching) {
+ RGE_UNLOCK(sc);
+ return;
+ }
+
+ /* Disable interrupts. */
+ RGE_WRITE_4(sc, RGE_IMR, 0);
+
+ if (!(sc->rge_flags & RGE_FLAG_MSI)) {
+ if ((RGE_READ_4(sc, RGE_ISR) & sc->rge_intrs) == 0)
+ goto done;
+ }
+
+ status = RGE_READ_4(sc, RGE_ISR);
+ if (status)
+ RGE_WRITE_4(sc, RGE_ISR, status);
+
+ if (status & RGE_ISR_PCS_TIMEOUT)
+ claimed = 1;
+
+ rv = 0;
+ if (status & sc->rge_intrs) {
+
+ (void) q;
+ rv |= rge_rxeof(q, &rx_mq);
+ rv |= rge_txeof(q);
+
+ if (status & RGE_ISR_SYSTEM_ERR) {
+ sc->sc_drv_stats.intr_system_err_cnt++;
+ rge_init_locked(sc);
+ }
+ claimed = 1;
+ }
+
+ if (sc->rge_timerintr) {
+ if (!rv) {
+ /*
+ * Nothing needs to be processed, fallback
+ * to use TX/RX interrupts.
+ */
+ rge_setup_intr(sc, RGE_IMTYPE_NONE);
+
+ /*
+ * Recollect, mainly to avoid the possible
+ * race introduced by changing interrupt
+ * masks.
+ */
+ rge_rxeof(q, &rx_mq);
+ rge_txeof(q);
+ } else
+ RGE_WRITE_4(sc, RGE_TIMERCNT, 1);
+ } else if (rv) {
+ /*
+ * Assume that using simulated interrupt moderation
+ * (hardware timer based) could reduce the interrupt
+ * rate.
+ */
+ rge_setup_intr(sc, RGE_IMTYPE_SIM);
+ }
+
+ RGE_WRITE_4(sc, RGE_IMR, sc->rge_intrs);
+
+done:
+ RGE_UNLOCK(sc);
+
+ NET_EPOCH_ENTER(et);
+ /* Handle any RX frames, outside of the driver lock */
+ while ((m = mbufq_dequeue(&rx_mq)) != NULL) {
*** 18573 LINES SKIPPED ***