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, ®) == 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>
