From owner-svn-src-stable-10@FreeBSD.ORG Sat Oct 25 20:34:12 2014 Return-Path: Delivered-To: svn-src-stable-10@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id DB28C301; Sat, 25 Oct 2014 20:34:12 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id C549C772; Sat, 25 Oct 2014 20:34:12 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id s9PKYCKs008765; Sat, 25 Oct 2014 20:34:12 GMT (envelope-from ian@FreeBSD.org) Received: (from ian@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id s9PKYBYA008753; Sat, 25 Oct 2014 20:34:11 GMT (envelope-from ian@FreeBSD.org) Message-Id: <201410252034.s9PKYBYA008753@svn.freebsd.org> X-Authentication-Warning: svn.freebsd.org: ian set sender to ian@FreeBSD.org using -f From: Ian Lepore Date: Sat, 25 Oct 2014 20:34:11 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r273645 - in stable/10/sys: arm/conf arm/xilinx boot/fdt/dts/arm dev/cadence X-SVN-Group: stable-10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable-10@freebsd.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: SVN commit messages for only the 10-stable src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 25 Oct 2014 20:34:13 -0000 Author: ian Date: Sat Oct 25 20:34:10 2014 New Revision: 273645 URL: https://svnweb.freebsd.org/changeset/base/273645 Log: MFC r268633, r271046: Fixes and enhancements for the if_cgem driver... - miibus fixes as suggested by Yonghyeon Pyun. - enable VLAN MTU support. - fix a few WITNESS complaints in cgem_attach(). - have cgem_attach() properly init the ifnet struct before calling mii_attach() to fix panic when using e1000phy. - fix ethernet address changing. - fix transmit queue overflow handling. - tweak receive queue handling to reduce receive overflows. - bring out MAC statistic counters to sysctls. - add e1000phy to config file. - implement receive hang work-around described in reference guide. - change device name from if_cgem to cgem to be consistent with other interfaces. Fix the Zedboard/Zynq ethernet driver to handle media speed changes so that it can connect to switches at speeds other than 1gb. Modified: stable/10/sys/arm/conf/ZEDBOARD stable/10/sys/arm/xilinx/files.zynq7 stable/10/sys/arm/xilinx/zy7_slcr.c stable/10/sys/arm/xilinx/zy7_slcr.h stable/10/sys/boot/fdt/dts/arm/zedboard.dts stable/10/sys/dev/cadence/if_cgem.c stable/10/sys/dev/cadence/if_cgem_hw.h Directory Properties: stable/10/ (props changed) Modified: stable/10/sys/arm/conf/ZEDBOARD ============================================================================== --- stable/10/sys/arm/conf/ZEDBOARD Sat Oct 25 19:31:34 2014 (r273644) +++ stable/10/sys/arm/conf/ZEDBOARD Sat Oct 25 20:34:10 2014 (r273645) @@ -72,8 +72,9 @@ options KDB device loop device random device ether -device if_cgem # Zynq-7000 gig ethernet device +device cgem # Zynq-7000 gig ethernet device device mii +device e1000phy device pty device uart device gpio Modified: stable/10/sys/arm/xilinx/files.zynq7 ============================================================================== --- stable/10/sys/arm/xilinx/files.zynq7 Sat Oct 25 19:31:34 2014 (r273644) +++ stable/10/sys/arm/xilinx/files.zynq7 Sat Oct 25 20:34:10 2014 (r273645) @@ -23,7 +23,7 @@ arm/xilinx/zy7_slcr.c standard arm/xilinx/zy7_devcfg.c standard arm/xilinx/zy7_mp.c optional smp -dev/cadence/if_cgem.c optional if_cgem +dev/cadence/if_cgem.c optional cgem dev/sdhci/sdhci_fdt.c optional sdhci arm/xilinx/zy7_ehci.c optional ehci arm/xilinx/uart_dev_cdnc.c optional uart Modified: stable/10/sys/arm/xilinx/zy7_slcr.c ============================================================================== --- stable/10/sys/arm/xilinx/zy7_slcr.c Sat Oct 25 19:31:34 2014 (r273644) +++ stable/10/sys/arm/xilinx/zy7_slcr.c Sat Oct 25 20:34:10 2014 (r273645) @@ -71,12 +71,14 @@ extern void (*zynq7_cpu_reset); #define ZSLCR_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) #define ZSLCR_LOCK_INIT(sc) \ mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \ - "zy7_slcr", MTX_SPIN) + "zy7_slcr", MTX_DEF) #define ZSLCR_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define RD4(sc, off) (bus_read_4((sc)->mem_res, (off))) #define WR4(sc, off, val) (bus_write_4((sc)->mem_res, (off), (val))) +#define ZYNQ_DEFAULT_PS_CLK_FREQUENCY 33333333 /* 33.3 Mhz */ + SYSCTL_NODE(_hw, OID_AUTO, zynq, CTLFLAG_RD, 0, "Xilinx Zynq-7000"); @@ -84,7 +86,7 @@ static char zynq_bootmode[64]; SYSCTL_STRING(_hw_zynq, OID_AUTO, bootmode, CTLFLAG_RD, zynq_bootmode, 0, "Zynq boot mode"); -static char zynq_pssid[80]; +static char zynq_pssid[100]; SYSCTL_STRING(_hw_zynq, OID_AUTO, pssid, CTLFLAG_RD, zynq_pssid, 0, "Zynq PSS IDCODE"); @@ -92,6 +94,22 @@ static uint32_t zynq_reboot_status; SYSCTL_INT(_hw_zynq, OID_AUTO, reboot_status, CTLFLAG_RD, &zynq_reboot_status, 0, "Zynq REBOOT_STATUS register"); +static int ps_clk_frequency; +SYSCTL_INT(_hw_zynq, OID_AUTO, ps_clk_frequency, CTLFLAG_RD, &ps_clk_frequency, + 0, "Zynq PS_CLK Frequency"); + +static int io_pll_frequency; +SYSCTL_INT(_hw_zynq, OID_AUTO, io_pll_frequency, CTLFLAG_RD, &io_pll_frequency, + 0, "Zynq IO PLL Frequency"); + +static int arm_pll_frequency; +SYSCTL_INT(_hw_zynq, OID_AUTO, arm_pll_frequency, CTLFLAG_RD, + &arm_pll_frequency, 0, "Zynq ARM PLL Frequency"); + +static int ddr_pll_frequency; +SYSCTL_INT(_hw_zynq, OID_AUTO, ddr_pll_frequency, CTLFLAG_RD, + &ddr_pll_frequency, 0, "Zynq DDR PLL Frequency"); + static void zy7_slcr_unlock(struct zy7_slcr_softc *sc) { @@ -189,6 +207,54 @@ zy7_slcr_postload_pl(int en_level_shifte ZSLCR_UNLOCK(sc); } +/* Override cgem_set_refclk() in gigabit ethernet driver + * (sys/dev/cadence/if_cgem.c). This function is called to + * request a change in the gem's reference clock speed. + */ +int +cgem_set_ref_clk(int unit, int frequency) +{ + struct zy7_slcr_softc *sc = zy7_slcr_softc_p; + int div0, div1; + + if (!sc) + return (-1); + + /* Find suitable divisor pairs. Round result to nearest khz + * to test for match. + */ + for (div1 = 1; div1 <= ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX; div1++) { + div0 = (io_pll_frequency + div1 * frequency / 2) / + div1 / frequency; + if (div0 > 0 && div0 <= ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_MAX && + ((io_pll_frequency / div0 / div1) + 500) / 1000 == + (frequency + 500) / 1000) + break; + } + + if (div1 > ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX) + return (-1); + + ZSLCR_LOCK(sc); + + /* Unlock SLCR registers. */ + zy7_slcr_unlock(sc); + + /* Modify GEM reference clock. */ + WR4(sc, unit ? ZY7_SLCR_GEM1_CLK_CTRL : ZY7_SLCR_GEM0_CLK_CTRL, + (div1 << ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_SHIFT) | + (div0 << ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_SHIFT) | + ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_IO_PLL | + ZY7_SLCR_GEM_CLK_CTRL_CLKACT); + + /* Lock SLCR registers. */ + zy7_slcr_lock(sc); + + ZSLCR_UNLOCK(sc); + + return (0); +} + static int zy7_slcr_probe(device_t dev) { @@ -208,8 +274,13 @@ zy7_slcr_attach(device_t dev) { struct zy7_slcr_softc *sc = device_get_softc(dev); int rid; + phandle_t node; + pcell_t cell; uint32_t bootmode; uint32_t pss_idcode; + uint32_t arm_pll_ctrl; + uint32_t ddr_pll_ctrl; + uint32_t io_pll_ctrl; static char *bootdev_names[] = { "JTAG", "Quad-SPI", "NOR", "(3?)", "NAND", "SD Card", "(6?)", "(7?)" @@ -260,6 +331,53 @@ zy7_slcr_attach(device_t dev) zynq_reboot_status = RD4(sc, ZY7_SLCR_REBOOT_STAT); + /* Derive PLL frequencies from PS_CLK. */ + node = ofw_bus_get_node(dev); + if (OF_getprop(node, "clock-frequency", &cell, sizeof(cell)) > 0) + ps_clk_frequency = fdt32_to_cpu(cell); + else + ps_clk_frequency = ZYNQ_DEFAULT_PS_CLK_FREQUENCY; + + arm_pll_ctrl = RD4(sc, ZY7_SLCR_ARM_PLL_CTRL); + ddr_pll_ctrl = RD4(sc, ZY7_SLCR_DDR_PLL_CTRL); + io_pll_ctrl = RD4(sc, ZY7_SLCR_IO_PLL_CTRL); + + /* Determine ARM PLL frequency. */ + if (((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 && + (arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) || + ((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 && + (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0)) + /* PLL is bypassed. */ + arm_pll_frequency = ps_clk_frequency; + else + arm_pll_frequency = ps_clk_frequency * + ((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >> + ZY7_SLCR_PLL_CTRL_FDIV_SHIFT); + + /* Determine DDR PLL frequency. */ + if (((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 && + (ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) || + ((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 && + (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0)) + /* PLL is bypassed. */ + ddr_pll_frequency = ps_clk_frequency; + else + ddr_pll_frequency = ps_clk_frequency * + ((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >> + ZY7_SLCR_PLL_CTRL_FDIV_SHIFT); + + /* Determine IO PLL frequency. */ + if (((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 && + (io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) || + ((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 && + (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0)) + /* PLL is bypassed. */ + io_pll_frequency = ps_clk_frequency; + else + io_pll_frequency = ps_clk_frequency * + ((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >> + ZY7_SLCR_PLL_CTRL_FDIV_SHIFT); + /* Lock SLCR registers. */ zy7_slcr_lock(sc); Modified: stable/10/sys/arm/xilinx/zy7_slcr.h ============================================================================== --- stable/10/sys/arm/xilinx/zy7_slcr.h Sat Oct 25 19:31:34 2014 (r273644) +++ stable/10/sys/arm/xilinx/zy7_slcr.h Sat Oct 25 20:34:10 2014 (r273645) @@ -126,6 +126,18 @@ #define ZY7_SLCR_GEM1_RCLK_CTRL 0x013c #define ZY7_SLCR_GEM0_CLK_CTRL 0x0140 #define ZY7_SLCR_GEM1_CLK_CTRL 0x0144 +#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MASK (0x3f<<20) +#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_SHIFT 20 +#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX 0x3f +#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_MASK (0x3f<<8) +#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_SHIFT 8 +#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_MAX 0x3f +#define ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_MASK (7<<4) +#define ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_IO_PLL (0<<4) +#define ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_ARM_PLL (2<<4) +#define ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_DDR_PLL (3<<4) +#define ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_EMIO_CLK (4<<4) +#define ZY7_SLCR_GEM_CLK_CTRL_CLKACT 1 #define ZY7_SLCR_SMC_CLK_CTRL 0x0148 #define ZY7_SLCR_LQSPI_CLK_CTRL 0x014c #define ZY7_SLCR_SDIO_CLK_CTRL 0x0150 @@ -274,6 +286,7 @@ #ifdef _KERNEL extern void zy7_slcr_preload_pl(void); -extern void zy7_slcr_postload_pl(int); +extern void zy7_slcr_postload_pl(int en_level_shifters); +extern int cgem_set_ref_clk(int unit, int frequency); #endif #endif /* _ZY7_SLCR_H_ */ Modified: stable/10/sys/boot/fdt/dts/arm/zedboard.dts ============================================================================== --- stable/10/sys/boot/fdt/dts/arm/zedboard.dts Sat Oct 25 19:31:34 2014 (r273644) +++ stable/10/sys/boot/fdt/dts/arm/zedboard.dts Sat Oct 25 20:34:10 2014 (r273645) @@ -63,6 +63,7 @@ slcr: slcr@7000 { compatible = "xlnx,zy7_slcr"; reg = <0x0 0x1000>; + clock-frequency = <33333333>; // 33Mhz PS_CLK }; // Interrupt controller @@ -175,6 +176,7 @@ reg = <0xb000 0x1000>; interrupts = <54 55>; interrupt-parent = <&GIC>; + ref-clock-num = <0>; }; // SDIO Modified: stable/10/sys/dev/cadence/if_cgem.c ============================================================================== --- stable/10/sys/dev/cadence/if_cgem.c Sat Oct 25 19:31:34 2014 (r273644) +++ stable/10/sys/dev/cadence/if_cgem.c Sat Oct 25 20:34:10 2014 (r273645) @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2012-2013 Thomas Skibo + * Copyright (c) 2012-2014 Thomas Skibo * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -81,17 +81,17 @@ __FBSDID("$FreeBSD$"); #define IF_CGEM_NAME "cgem" -#define CGEM_NUM_RX_DESCS 256 /* size of receive descriptor ring */ -#define CGEM_NUM_TX_DESCS 256 /* size of transmit descriptor ring */ +#define CGEM_NUM_RX_DESCS 512 /* size of receive descriptor ring */ +#define CGEM_NUM_TX_DESCS 512 /* size of transmit descriptor ring */ #define MAX_DESC_RING_SIZE (MAX(CGEM_NUM_RX_DESCS*sizeof(struct cgem_rx_desc),\ CGEM_NUM_TX_DESCS*sizeof(struct cgem_tx_desc))) /* Default for sysctl rxbufs. Must be < CGEM_NUM_RX_DESCS of course. */ -#define DEFAULT_NUM_RX_BUFS 64 /* number of receive bufs to queue. */ +#define DEFAULT_NUM_RX_BUFS 256 /* number of receive bufs to queue. */ -#define TX_MAX_DMA_SEGS 4 /* maximum segs in a tx mbuf dma */ +#define TX_MAX_DMA_SEGS 8 /* maximum segs in a tx mbuf dma */ #define CGEM_CKSUM_ASSIST (CSUM_IP | CSUM_TCP | CSUM_UDP | \ CSUM_TCP_IPV6 | CSUM_UDP_IPV6) @@ -101,12 +101,14 @@ struct cgem_softc { struct mtx sc_mtx; device_t dev; device_t miibus; + u_int mii_media_active; /* last active media */ int if_old_flags; struct resource *mem_res; struct resource *irq_res; void *intrhand; struct callout tick_ch; uint32_t net_ctl_shadow; + int ref_clk_num; u_char eaddr[6]; bus_dma_tag_t desc_dma_tag; @@ -122,7 +124,11 @@ struct cgem_softc { int rxring_queued; /* how many rcv bufs queued */ bus_dmamap_t rxring_dma_map; int rxbufs; /* tunable number rcv bufs */ - int rxoverruns; /* rx ring overruns */ + int rxhangwar; /* rx hang work-around */ + u_int rxoverruns; /* rx overruns */ + u_int rxnobufs; /* rx buf ring empty events */ + u_int rxdmamapfails; /* rx dmamap failures */ + uint32_t rx_frames_prev; /* transmit descriptor ring */ struct cgem_tx_desc *txring; @@ -133,6 +139,56 @@ struct cgem_softc { int txring_tl_ptr; /* next xmit mbuf to free */ int txring_queued; /* num xmits segs queued */ bus_dmamap_t txring_dma_map; + u_int txfull; /* tx ring full events */ + u_int txdefrags; /* tx calls to m_defrag() */ + u_int txdefragfails; /* tx m_defrag() failures */ + u_int txdmamapfails; /* tx dmamap failures */ + + /* hardware provided statistics */ + struct cgem_hw_stats { + uint64_t tx_bytes; + uint32_t tx_frames; + uint32_t tx_frames_bcast; + uint32_t tx_frames_multi; + uint32_t tx_frames_pause; + uint32_t tx_frames_64b; + uint32_t tx_frames_65to127b; + uint32_t tx_frames_128to255b; + uint32_t tx_frames_256to511b; + uint32_t tx_frames_512to1023b; + uint32_t tx_frames_1024to1536b; + uint32_t tx_under_runs; + uint32_t tx_single_collisn; + uint32_t tx_multi_collisn; + uint32_t tx_excsv_collisn; + uint32_t tx_late_collisn; + uint32_t tx_deferred_frames; + uint32_t tx_carrier_sense_errs; + + uint64_t rx_bytes; + uint32_t rx_frames; + uint32_t rx_frames_bcast; + uint32_t rx_frames_multi; + uint32_t rx_frames_pause; + uint32_t rx_frames_64b; + uint32_t rx_frames_65to127b; + uint32_t rx_frames_128to255b; + uint32_t rx_frames_256to511b; + uint32_t rx_frames_512to1023b; + uint32_t rx_frames_1024to1536b; + uint32_t rx_frames_undersize; + uint32_t rx_frames_oversize; + uint32_t rx_frames_jabber; + uint32_t rx_frames_fcs_errs; + uint32_t rx_frames_length_errs; + uint32_t rx_symbol_errs; + uint32_t rx_align_errs; + uint32_t rx_resource_errs; + uint32_t rx_overrun_errs; + uint32_t rx_ip_hdr_csum_errs; + uint32_t rx_tcp_csum_errs; + uint32_t rx_udp_csum_errs; + } stats; }; #define RD4(sc, off) (bus_read_4((sc)->mem_res, (off))) @@ -148,6 +204,9 @@ struct cgem_softc { #define CGEM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx) #define CGEM_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) +/* Allow platforms to optionally provide a way to set the reference clock. */ +int cgem_set_ref_clk(int unit, int frequency); + static devclass_t cgem_devclass; static int cgem_probe(device_t dev); @@ -156,6 +215,8 @@ static int cgem_detach(device_t dev); static void cgem_tick(void *); static void cgem_intr(void *); +static void cgem_mediachange(struct cgem_softc *, struct mii_data *); + static void cgem_get_mac(struct cgem_softc *sc, u_char eaddr[]) { @@ -192,10 +253,16 @@ cgem_get_mac(struct cgem_softc *sc, u_ch "random: %02x:%02x:%02x:%02x:%02x:%02x\n", eaddr[0], eaddr[1], eaddr[2], eaddr[3], eaddr[4], eaddr[5]); + } - WR4(sc, CGEM_SPEC_ADDR_LOW(0), (eaddr[3] << 24) | - (eaddr[2] << 16) | (eaddr[1] << 8) | eaddr[0]); - WR4(sc, CGEM_SPEC_ADDR_HI(0), (eaddr[5] << 8) | eaddr[4]); + /* Move address to first slot and zero out the rest. */ + WR4(sc, CGEM_SPEC_ADDR_LOW(0), (eaddr[3] << 24) | + (eaddr[2] << 16) | (eaddr[1] << 8) | eaddr[0]); + WR4(sc, CGEM_SPEC_ADDR_HI(0), (eaddr[5] << 8) | eaddr[4]); + + for (i = 1; i < 4; i++) { + WR4(sc, CGEM_SPEC_ADDR_LOW(i), 0); + WR4(sc, CGEM_SPEC_ADDR_HI(i), 0); } } @@ -421,7 +488,7 @@ cgem_fill_rqueue(struct cgem_softc *sc) if (bus_dmamap_load_mbuf_sg(sc->mbuf_dma_tag, sc->rxring_m_dmamap[sc->rxring_hd_ptr], m, segs, &nsegs, BUS_DMA_NOWAIT)) { - /* XXX: warn? */ + sc->rxdmamapfails++; m_free(m); break; } @@ -450,12 +517,14 @@ static void cgem_recv(struct cgem_softc *sc) { struct ifnet *ifp = sc->ifp; - struct mbuf *m; + struct mbuf *m, *m_hd, **m_tl; uint32_t ctl; CGEM_ASSERT_LOCKED(sc); /* Pick up all packets in which the OWN bit is set. */ + m_hd = NULL; + m_tl = &m_hd; while (sc->rxring_queued > 0 && (sc->rxring[sc->rxring_tl_ptr].addr & CGEM_RXDESC_OWN) != 0) { @@ -492,7 +561,7 @@ cgem_recv(struct cgem_softc *sc) continue; } - /* Hand it off to upper layers. */ + /* Ready it to hand off to upper layers. */ m->m_data += ETHER_ALIGN; m->m_len = (ctl & CGEM_RXDESC_LENGTH_MASK); m->m_pkthdr.rcvif = ifp; @@ -520,11 +589,24 @@ cgem_recv(struct cgem_softc *sc) } } + /* Queue it up for delivery below. */ + *m_tl = m; + m_tl = &m->m_next; + } + + /* Replenish receive buffers. */ + cgem_fill_rqueue(sc); + + /* Unlock and send up packets. */ + CGEM_UNLOCK(sc); + while (m_hd != NULL) { + m = m_hd; + m_hd = m_hd->m_next; + m->m_next = NULL; ifp->if_ipackets++; - CGEM_UNLOCK(sc); (*ifp->if_input)(ifp, m); - CGEM_LOCK(sc); } + CGEM_LOCK(sc); } /* Find completed transmits and free their mbufs. */ @@ -590,6 +672,8 @@ cgem_clean_tx(struct cgem_softc *sc) else sc->txring_tl_ptr++; sc->txring_queued--; + + sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } } @@ -610,16 +694,17 @@ cgem_start_locked(struct ifnet *ifp) for (;;) { /* Check that there is room in the descriptor ring. */ - if (sc->txring_queued >= CGEM_NUM_TX_DESCS - - TX_MAX_DMA_SEGS - 1) { + if (sc->txring_queued >= + CGEM_NUM_TX_DESCS - TX_MAX_DMA_SEGS * 2) { /* Try to make room. */ cgem_clean_tx(sc); /* Still no room? */ - if (sc->txring_queued >= CGEM_NUM_TX_DESCS - - TX_MAX_DMA_SEGS - 1) { + if (sc->txring_queued >= + CGEM_NUM_TX_DESCS - TX_MAX_DMA_SEGS * 2) { ifp->if_drv_flags |= IFF_DRV_OACTIVE; + sc->txfull++; break; } } @@ -638,6 +723,7 @@ cgem_start_locked(struct ifnet *ifp) struct mbuf *m2 = m_defrag(m, M_NOWAIT); if (m2 == NULL) { + sc->txdefragfails++; m_freem(m); continue; } @@ -645,10 +731,12 @@ cgem_start_locked(struct ifnet *ifp) err = bus_dmamap_load_mbuf_sg(sc->mbuf_dma_tag, sc->txring_m_dmamap[sc->txring_hd_ptr], m, segs, &nsegs, BUS_DMA_NOWAIT); + sc->txdefrags++; } if (err) { /* Give up. */ m_freem(m); + sc->txdmamapfails++; continue; } sc->txring_m[sc->txring_hd_ptr] = m; @@ -692,8 +780,10 @@ cgem_start_locked(struct ifnet *ifp) /* Kick the transmitter. */ WR4(sc, CGEM_NET_CTRL, sc->net_ctl_shadow | CGEM_NET_CTRL_START_TX); - } + /* If there is a BPF listener, bounce a copy to to him. */ + ETHER_BPF_MTAP(ifp, m); + } } static void @@ -706,27 +796,69 @@ cgem_start(struct ifnet *ifp) CGEM_UNLOCK(sc); } -/* Respond to changes in media. */ static void -cgem_media_update(struct cgem_softc *sc, int active) +cgem_poll_hw_stats(struct cgem_softc *sc) { - uint32_t net_cfg; + uint32_t n; CGEM_ASSERT_LOCKED(sc); - /* Update hardware to reflect phy status. */ - net_cfg = RD4(sc, CGEM_NET_CFG); - net_cfg &= ~(CGEM_NET_CFG_SPEED100 | CGEM_NET_CFG_GIGE_EN | - CGEM_NET_CFG_FULL_DUPLEX); - - if (IFM_SUBTYPE(active) == IFM_1000_T) - net_cfg |= (CGEM_NET_CFG_SPEED100 | CGEM_NET_CFG_GIGE_EN); - else if (IFM_SUBTYPE(active) == IFM_100_TX) - net_cfg |= CGEM_NET_CFG_SPEED100; + sc->stats.tx_bytes += RD4(sc, CGEM_OCTETS_TX_BOT); + sc->stats.tx_bytes += (uint64_t)RD4(sc, CGEM_OCTETS_TX_TOP) << 32; - if ((active & IFM_FDX) != 0) - net_cfg |= CGEM_NET_CFG_FULL_DUPLEX; - WR4(sc, CGEM_NET_CFG, net_cfg); + sc->stats.tx_frames += RD4(sc, CGEM_FRAMES_TX); + sc->stats.tx_frames_bcast += RD4(sc, CGEM_BCAST_FRAMES_TX); + sc->stats.tx_frames_multi += RD4(sc, CGEM_MULTI_FRAMES_TX); + sc->stats.tx_frames_pause += RD4(sc, CGEM_PAUSE_FRAMES_TX); + sc->stats.tx_frames_64b += RD4(sc, CGEM_FRAMES_64B_TX); + sc->stats.tx_frames_65to127b += RD4(sc, CGEM_FRAMES_65_127B_TX); + sc->stats.tx_frames_128to255b += RD4(sc, CGEM_FRAMES_128_255B_TX); + sc->stats.tx_frames_256to511b += RD4(sc, CGEM_FRAMES_256_511B_TX); + sc->stats.tx_frames_512to1023b += RD4(sc, CGEM_FRAMES_512_1023B_TX); + sc->stats.tx_frames_1024to1536b += RD4(sc, CGEM_FRAMES_1024_1518B_TX); + sc->stats.tx_under_runs += RD4(sc, CGEM_TX_UNDERRUNS); + + n = RD4(sc, CGEM_SINGLE_COLL_FRAMES); + sc->stats.tx_single_collisn += n; + sc->ifp->if_collisions += n; + n = RD4(sc, CGEM_MULTI_COLL_FRAMES); + sc->stats.tx_multi_collisn += n; + sc->ifp->if_collisions += n; + n = RD4(sc, CGEM_EXCESSIVE_COLL_FRAMES); + sc->stats.tx_excsv_collisn += n; + sc->ifp->if_collisions += n; + n = RD4(sc, CGEM_LATE_COLL); + sc->stats.tx_late_collisn += n; + sc->ifp->if_collisions += n; + + sc->stats.tx_deferred_frames += RD4(sc, CGEM_DEFERRED_TX_FRAMES); + sc->stats.tx_carrier_sense_errs += RD4(sc, CGEM_CARRIER_SENSE_ERRS); + + sc->stats.rx_bytes += RD4(sc, CGEM_OCTETS_RX_BOT); + sc->stats.rx_bytes += (uint64_t)RD4(sc, CGEM_OCTETS_RX_TOP) << 32; + + sc->stats.rx_frames += RD4(sc, CGEM_FRAMES_RX); + sc->stats.rx_frames_bcast += RD4(sc, CGEM_BCAST_FRAMES_RX); + sc->stats.rx_frames_multi += RD4(sc, CGEM_MULTI_FRAMES_RX); + sc->stats.rx_frames_pause += RD4(sc, CGEM_PAUSE_FRAMES_RX); + sc->stats.rx_frames_64b += RD4(sc, CGEM_FRAMES_64B_RX); + sc->stats.rx_frames_65to127b += RD4(sc, CGEM_FRAMES_65_127B_RX); + sc->stats.rx_frames_128to255b += RD4(sc, CGEM_FRAMES_128_255B_RX); + sc->stats.rx_frames_256to511b += RD4(sc, CGEM_FRAMES_256_511B_RX); + sc->stats.rx_frames_512to1023b += RD4(sc, CGEM_FRAMES_512_1023B_RX); + sc->stats.rx_frames_1024to1536b += RD4(sc, CGEM_FRAMES_1024_1518B_RX); + sc->stats.rx_frames_undersize += RD4(sc, CGEM_UNDERSZ_RX); + sc->stats.rx_frames_oversize += RD4(sc, CGEM_OVERSZ_RX); + sc->stats.rx_frames_jabber += RD4(sc, CGEM_JABBERS_RX); + sc->stats.rx_frames_fcs_errs += RD4(sc, CGEM_FCS_ERRS); + sc->stats.rx_frames_length_errs += RD4(sc, CGEM_LENGTH_FIELD_ERRS); + sc->stats.rx_symbol_errs += RD4(sc, CGEM_RX_SYMBOL_ERRS); + sc->stats.rx_align_errs += RD4(sc, CGEM_ALIGN_ERRS); + sc->stats.rx_resource_errs += RD4(sc, CGEM_RX_RESOURCE_ERRS); + sc->stats.rx_overrun_errs += RD4(sc, CGEM_RX_OVERRUN_ERRS); + sc->stats.rx_ip_hdr_csum_errs += RD4(sc, CGEM_IP_HDR_CKSUM_ERRS); + sc->stats.rx_tcp_csum_errs += RD4(sc, CGEM_TCP_CKSUM_ERRS); + sc->stats.rx_udp_csum_errs += RD4(sc, CGEM_UDP_CKSUM_ERRS); } static void @@ -734,21 +866,32 @@ cgem_tick(void *arg) { struct cgem_softc *sc = (struct cgem_softc *)arg; struct mii_data *mii; - int active; CGEM_ASSERT_LOCKED(sc); /* Poll the phy. */ if (sc->miibus != NULL) { mii = device_get_softc(sc->miibus); - active = mii->mii_media_active; mii_tick(mii); - if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == - (IFM_ACTIVE | IFM_AVALID) && - active != mii->mii_media_active) - cgem_media_update(sc, mii->mii_media_active); } + /* Poll statistics registers. */ + cgem_poll_hw_stats(sc); + + /* Check for receiver hang. */ + if (sc->rxhangwar && sc->rx_frames_prev == sc->stats.rx_frames) { + /* + * Reset receiver logic by toggling RX_EN bit. 1usec + * delay is necessary especially when operating at 100mbps + * and 10mbps speeds. + */ + WR4(sc, CGEM_NET_CTRL, sc->net_ctl_shadow & + ~CGEM_NET_CTRL_RX_EN); + DELAY(1); + WR4(sc, CGEM_NET_CTRL, sc->net_ctl_shadow); + } + sc->rx_frames_prev = sc->stats.rx_frames; + /* Next callout in one second. */ callout_reset(&sc->tick_ch, hz, cgem_tick, sc); } @@ -767,33 +910,43 @@ cgem_intr(void *arg) return; } + /* Read interrupt status and immediately clear the bits. */ istatus = RD4(sc, CGEM_INTR_STAT); - WR4(sc, CGEM_INTR_STAT, istatus & - (CGEM_INTR_RX_COMPLETE | CGEM_INTR_TX_USED_READ | - CGEM_INTR_RX_OVERRUN | CGEM_INTR_HRESP_NOT_OK)); + WR4(sc, CGEM_INTR_STAT, istatus); - /* Hresp not ok. Something very bad with DMA. Try to clear. */ + /* Packets received. */ + if ((istatus & CGEM_INTR_RX_COMPLETE) != 0) + cgem_recv(sc); + + /* Free up any completed transmit buffers. */ + cgem_clean_tx(sc); + + /* Hresp not ok. Something is very bad with DMA. Try to clear. */ if ((istatus & CGEM_INTR_HRESP_NOT_OK) != 0) { - printf("cgem_intr: hresp not okay! rx_status=0x%x\n", - RD4(sc, CGEM_RX_STAT)); + device_printf(sc->dev, "cgem_intr: hresp not okay! " + "rx_status=0x%x\n", RD4(sc, CGEM_RX_STAT)); WR4(sc, CGEM_RX_STAT, CGEM_RX_STAT_HRESP_NOT_OK); } - /* Transmitter has idled. Free up any spent transmit buffers. */ - if ((istatus & CGEM_INTR_TX_USED_READ) != 0) - cgem_clean_tx(sc); + /* Receiver overrun. */ + if ((istatus & CGEM_INTR_RX_OVERRUN) != 0) { + /* Clear status bit. */ + WR4(sc, CGEM_RX_STAT, CGEM_RX_STAT_OVERRUN); + sc->rxoverruns++; + } - /* Packets received or overflow. */ - if ((istatus & (CGEM_INTR_RX_COMPLETE | CGEM_INTR_RX_OVERRUN)) != 0) { - cgem_recv(sc); + /* Receiver ran out of bufs. */ + if ((istatus & CGEM_INTR_RX_USED_READ) != 0) { + WR4(sc, CGEM_NET_CTRL, sc->net_ctl_shadow | + CGEM_NET_CTRL_FLUSH_DPRAM_PKT); cgem_fill_rqueue(sc); - if ((istatus & CGEM_INTR_RX_OVERRUN) != 0) { - /* Clear rx status register. */ - sc->rxoverruns++; - WR4(sc, CGEM_RX_STAT, CGEM_RX_STAT_ALL); - } + sc->rxnobufs++; } + /* Restart transmitter if needed. */ + if (!IFQ_DRV_IS_EMPTY(&sc->ifp->if_snd)) + cgem_start_locked(sc->ifp); + CGEM_UNLOCK(sc); } @@ -830,6 +983,7 @@ cgem_config(struct cgem_softc *sc) { uint32_t net_cfg; uint32_t dma_cfg; + u_char *eaddr = IF_LLADDR(sc->ifp); CGEM_ASSERT_LOCKED(sc); @@ -839,6 +993,7 @@ cgem_config(struct cgem_softc *sc) CGEM_NET_CFG_FCS_REMOVE | CGEM_NET_CFG_RX_BUF_OFFSET(ETHER_ALIGN) | CGEM_NET_CFG_GIGE_EN | + CGEM_NET_CFG_1536RXEN | CGEM_NET_CFG_FULL_DUPLEX | CGEM_NET_CFG_SPEED100; @@ -852,7 +1007,8 @@ cgem_config(struct cgem_softc *sc) dma_cfg = CGEM_DMA_CFG_RX_BUF_SIZE(MCLBYTES) | CGEM_DMA_CFG_RX_PKTBUF_MEMSZ_SEL_8K | CGEM_DMA_CFG_TX_PKTBUF_MEMSZ_SEL | - CGEM_DMA_CFG_AHB_FIXED_BURST_LEN_16; + CGEM_DMA_CFG_AHB_FIXED_BURST_LEN_16 | + CGEM_DMA_CFG_DISC_WHEN_NO_AHB; /* Enable transmit checksum offloading? */ if ((sc->ifp->if_capenable & IFCAP_TXCSUM) != 0) @@ -868,10 +1024,16 @@ cgem_config(struct cgem_softc *sc) sc->net_ctl_shadow |= (CGEM_NET_CTRL_TX_EN | CGEM_NET_CTRL_RX_EN); WR4(sc, CGEM_NET_CTRL, sc->net_ctl_shadow); + /* Set receive address in case it changed. */ + WR4(sc, CGEM_SPEC_ADDR_LOW(0), (eaddr[3] << 24) | + (eaddr[2] << 16) | (eaddr[1] << 8) | eaddr[0]); + WR4(sc, CGEM_SPEC_ADDR_HI(0), (eaddr[5] << 8) | eaddr[4]); + /* Set up interrupts. */ WR4(sc, CGEM_INTR_EN, - CGEM_INTR_RX_COMPLETE | CGEM_INTR_TX_USED_READ | - CGEM_INTR_RX_OVERRUN | CGEM_INTR_HRESP_NOT_OK); + CGEM_INTR_RX_COMPLETE | CGEM_INTR_RX_OVERRUN | + CGEM_INTR_TX_USED_READ | CGEM_INTR_RX_USED_READ | + CGEM_INTR_HRESP_NOT_OK); } /* Turn on interface and load up receive ring with buffers. */ @@ -892,9 +1054,7 @@ cgem_init_locked(struct cgem_softc *sc) sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; mii = device_get_softc(sc->miibus); - mii_pollstat(mii); - cgem_media_update(sc, mii->mii_media_active); - cgem_start_locked(sc->ifp); + mii_mediachg(mii); callout_reset(&sc->tick_ch, hz, cgem_tick, sc); } @@ -957,6 +1117,9 @@ cgem_stop(struct cgem_softc *sc) sc->rxring_hd_ptr = 0; sc->rxring_tl_ptr = 0; sc->rxring_queued = 0; + + /* Force next statchg or linkchg to program net config register. */ + sc->mii_media_active = 0; } @@ -1046,6 +1209,11 @@ cgem_ioctl(struct ifnet *ifp, u_long cmd ~CGEM_NET_CFG_RX_CHKSUM_OFFLD_EN); } } + if ((ifp->if_capenable & (IFCAP_RXCSUM | IFCAP_TXCSUM)) == + (IFCAP_RXCSUM | IFCAP_TXCSUM)) + ifp->if_capenable |= IFCAP_VLAN_HWCSUM; + else + ifp->if_capenable &= ~IFCAP_VLAN_HWCSUM; CGEM_UNLOCK(sc); break; @@ -1063,6 +1231,7 @@ static void cgem_child_detached(device_t dev, device_t child) { struct cgem_softc *sc = device_get_softc(dev); + if (child == sc->miibus) sc->miibus = NULL; } @@ -1072,12 +1241,19 @@ cgem_ifmedia_upd(struct ifnet *ifp) { struct cgem_softc *sc = (struct cgem_softc *) ifp->if_softc; struct mii_data *mii; + struct mii_softc *miisc; + int error = 0; mii = device_get_softc(sc->miibus); CGEM_LOCK(sc); - mii_mediachg(mii); + if ((ifp->if_flags & IFF_UP) != 0) { + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + PHY_RESET(miisc); + error = mii_mediachg(mii); + } CGEM_UNLOCK(sc); - return (0); + + return (error); } static void @@ -1118,6 +1294,13 @@ cgem_miibus_readreg(device_t dev, int ph val = RD4(sc, CGEM_PHY_MAINT) & CGEM_PHY_MAINT_DATA_MASK; + if (reg == MII_EXTSR) + /* + * MAC does not support half-duplex at gig speeds. + * Let mii(4) exclude the capability. + */ + val &= ~(EXTSR_1000XHDX | EXTSR_1000THDX); + return (val); } @@ -1147,6 +1330,265 @@ cgem_miibus_writereg(device_t dev, int p return (0); } +static void +cgem_miibus_statchg(device_t dev) +{ + struct cgem_softc *sc = device_get_softc(dev); + struct mii_data *mii = device_get_softc(sc->miibus); + + CGEM_ASSERT_LOCKED(sc); + + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID) && + sc->mii_media_active != mii->mii_media_active) + cgem_mediachange(sc, mii); +} + +static void +cgem_miibus_linkchg(device_t dev) +{ + struct cgem_softc *sc = device_get_softc(dev); + struct mii_data *mii = device_get_softc(sc->miibus); + + CGEM_ASSERT_LOCKED(sc); + + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID) && + sc->mii_media_active != mii->mii_media_active) + cgem_mediachange(sc, mii); +} + +/* + * Overridable weak symbol cgem_set_ref_clk(). This allows platforms to + * provide a function to set the cgem's reference clock. + */ +static int __used +cgem_default_set_ref_clk(int unit, int frequency) +{ + + return 0; +} +__weak_reference(cgem_default_set_ref_clk, cgem_set_ref_clk); + +/* Call to set reference clock and network config bits according to media. */ +static void +cgem_mediachange(struct cgem_softc *sc, struct mii_data *mii) +{ + uint32_t net_cfg; + int ref_clk_freq; + + CGEM_ASSERT_LOCKED(sc); + + /* Update hardware to reflect media. */ + net_cfg = RD4(sc, CGEM_NET_CFG); + net_cfg &= ~(CGEM_NET_CFG_SPEED100 | CGEM_NET_CFG_GIGE_EN | + CGEM_NET_CFG_FULL_DUPLEX); + + switch (IFM_SUBTYPE(mii->mii_media_active)) { + case IFM_1000_T: + net_cfg |= (CGEM_NET_CFG_SPEED100 | + CGEM_NET_CFG_GIGE_EN); + ref_clk_freq = 125000000; + break; + case IFM_100_TX: + net_cfg |= CGEM_NET_CFG_SPEED100; + ref_clk_freq = 25000000; + break; + default: + ref_clk_freq = 2500000; + } + + if ((mii->mii_media_active & IFM_FDX) != 0) + net_cfg |= CGEM_NET_CFG_FULL_DUPLEX; + + WR4(sc, CGEM_NET_CFG, net_cfg); + + /* Set the reference clock if necessary. */ + if (cgem_set_ref_clk(sc->ref_clk_num, ref_clk_freq)) + device_printf(sc->dev, "cgem_mediachange: " + "could not set ref clk%d to %d.\n", + sc->ref_clk_num, ref_clk_freq); + + sc->mii_media_active = mii->mii_media_active; +} + +static void +cgem_add_sysctls(device_t dev) +{ + struct cgem_softc *sc = device_get_softc(dev); + struct sysctl_ctx_list *ctx; + struct sysctl_oid_list *child; + struct sysctl_oid *tree; + + ctx = device_get_sysctl_ctx(dev); + child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); + + SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rxbufs", CTLFLAG_RW, + &sc->rxbufs, 0, + "Number receive buffers to provide"); + + SYSCTL_ADD_INT(ctx, child, OID_AUTO, "rxhangwar", CTLFLAG_RW, + &sc->rxhangwar, 0, + "Enable receive hang work-around"); + + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_rxoverruns", CTLFLAG_RD, + &sc->rxoverruns, 0, + "Receive overrun events"); + + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_rxnobufs", CTLFLAG_RD, + &sc->rxnobufs, 0, + "Receive buf queue empty events"); + + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_rxdmamapfails", CTLFLAG_RD, + &sc->rxdmamapfails, 0, + "Receive DMA map failures"); + + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_txfull", CTLFLAG_RD, + &sc->txfull, 0, + "Transmit ring full events"); + + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_txdmamapfails", CTLFLAG_RD, + &sc->txdmamapfails, 0, + "Transmit DMA map failures"); + + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_txdefrags", CTLFLAG_RD, + &sc->txdefrags, 0, + "Transmit m_defrag() calls"); + + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "_txdefragfails", CTLFLAG_RD, + &sc->txdefragfails, 0, + "Transmit m_defrag() failures"); + + tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", CTLFLAG_RD, + NULL, "GEM statistics"); + child = SYSCTL_CHILDREN(tree); + + SYSCTL_ADD_UQUAD(ctx, child, OID_AUTO, "tx_bytes", CTLFLAG_RD, + &sc->stats.tx_bytes, "Total bytes transmitted"); + + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames", CTLFLAG_RD, + &sc->stats.tx_frames, 0, "Total frames transmitted"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_bcast", CTLFLAG_RD, + &sc->stats.tx_frames_bcast, 0, + "Number broadcast frames transmitted"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_multi", CTLFLAG_RD, + &sc->stats.tx_frames_multi, 0, + "Number multicast frames transmitted"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_pause", + CTLFLAG_RD, &sc->stats.tx_frames_pause, 0, + "Number pause frames transmitted"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_64b", CTLFLAG_RD, + &sc->stats.tx_frames_64b, 0, + "Number frames transmitted of size 64 bytes or less"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_65to127b", CTLFLAG_RD, + &sc->stats.tx_frames_65to127b, 0, + "Number frames transmitted of size 65-127 bytes"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_128to255b", + CTLFLAG_RD, &sc->stats.tx_frames_128to255b, 0, + "Number frames transmitted of size 128-255 bytes"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_256to511b", + CTLFLAG_RD, &sc->stats.tx_frames_256to511b, 0, + "Number frames transmitted of size 256-511 bytes"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_512to1023b", + CTLFLAG_RD, &sc->stats.tx_frames_512to1023b, 0, + "Number frames transmitted of size 512-1023 bytes"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_frames_1024to1536b", + CTLFLAG_RD, &sc->stats.tx_frames_1024to1536b, 0, *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***