Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 15 Dec 2025 02:00:18 +0000
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org
Subject:   git: 4bf8ce037dc8 - main - if_rge: initial import of if_rge driver from OpenBSD.
Message-ID:  <693f6bb2.34b50.7a5205de@gitrepo.freebsd.org>

index | next in thread | raw e-mail

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, &reg) == 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 ***


help

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?693f6bb2.34b50.7a5205de>