12608aefcSPyun YongHyeon /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 42608aefcSPyun YongHyeon * Copyright (c) 2010, Pyun YongHyeon <yongari@FreeBSD.org> 52608aefcSPyun YongHyeon * All rights reserved. 62608aefcSPyun YongHyeon * 72608aefcSPyun YongHyeon * Redistribution and use in source and binary forms, with or without 82608aefcSPyun YongHyeon * modification, are permitted provided that the following conditions 92608aefcSPyun YongHyeon * are met: 102608aefcSPyun YongHyeon * 1. Redistributions of source code must retain the above copyright 112608aefcSPyun YongHyeon * notice unmodified, this list of conditions, and the following 122608aefcSPyun YongHyeon * disclaimer. 132608aefcSPyun YongHyeon * 2. Redistributions in binary form must reproduce the above copyright 142608aefcSPyun YongHyeon * notice, this list of conditions and the following disclaimer in the 152608aefcSPyun YongHyeon * documentation and/or other materials provided with the distribution. 162608aefcSPyun YongHyeon * 172608aefcSPyun YongHyeon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 182608aefcSPyun YongHyeon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 192608aefcSPyun YongHyeon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 202608aefcSPyun YongHyeon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 212608aefcSPyun YongHyeon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 222608aefcSPyun YongHyeon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 232608aefcSPyun YongHyeon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 242608aefcSPyun YongHyeon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 252608aefcSPyun YongHyeon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 262608aefcSPyun YongHyeon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 272608aefcSPyun YongHyeon * SUCH DAMAGE. 282608aefcSPyun YongHyeon */ 292608aefcSPyun YongHyeon 302608aefcSPyun YongHyeon /* Driver for DM&P Electronics, Inc, Vortex86 RDC R6040 FastEthernet. */ 312608aefcSPyun YongHyeon 322608aefcSPyun YongHyeon #include <sys/cdefs.h> 332608aefcSPyun YongHyeon __FBSDID("$FreeBSD$"); 342608aefcSPyun YongHyeon 352608aefcSPyun YongHyeon #include <sys/param.h> 362608aefcSPyun YongHyeon #include <sys/systm.h> 372608aefcSPyun YongHyeon #include <sys/bus.h> 382608aefcSPyun YongHyeon #include <sys/endian.h> 392608aefcSPyun YongHyeon #include <sys/kernel.h> 402608aefcSPyun YongHyeon #include <sys/lock.h> 412608aefcSPyun YongHyeon #include <sys/malloc.h> 422608aefcSPyun YongHyeon #include <sys/mbuf.h> 432608aefcSPyun YongHyeon #include <sys/module.h> 442608aefcSPyun YongHyeon #include <sys/mutex.h> 452608aefcSPyun YongHyeon #include <sys/rman.h> 462608aefcSPyun YongHyeon #include <sys/socket.h> 472608aefcSPyun YongHyeon #include <sys/sockio.h> 482608aefcSPyun YongHyeon #include <sys/sysctl.h> 492608aefcSPyun YongHyeon 502608aefcSPyun YongHyeon #include <net/bpf.h> 512608aefcSPyun YongHyeon #include <net/if.h> 5276039bc8SGleb Smirnoff #include <net/if_var.h> 532608aefcSPyun YongHyeon #include <net/if_arp.h> 542608aefcSPyun YongHyeon #include <net/ethernet.h> 552608aefcSPyun YongHyeon #include <net/if_dl.h> 562608aefcSPyun YongHyeon #include <net/if_llc.h> 572608aefcSPyun YongHyeon #include <net/if_media.h> 582608aefcSPyun YongHyeon #include <net/if_types.h> 592608aefcSPyun YongHyeon #include <net/if_vlan_var.h> 602608aefcSPyun YongHyeon 612608aefcSPyun YongHyeon #include <netinet/in.h> 622608aefcSPyun YongHyeon #include <netinet/in_systm.h> 632608aefcSPyun YongHyeon 642608aefcSPyun YongHyeon #include <dev/mii/mii.h> 652608aefcSPyun YongHyeon #include <dev/mii/miivar.h> 662608aefcSPyun YongHyeon 672608aefcSPyun YongHyeon #include <dev/pci/pcireg.h> 682608aefcSPyun YongHyeon #include <dev/pci/pcivar.h> 692608aefcSPyun YongHyeon 702608aefcSPyun YongHyeon #include <machine/bus.h> 712608aefcSPyun YongHyeon 722608aefcSPyun YongHyeon #include <dev/vte/if_vtereg.h> 732608aefcSPyun YongHyeon #include <dev/vte/if_vtevar.h> 742608aefcSPyun YongHyeon 752608aefcSPyun YongHyeon /* "device miibus" required. See GENERIC if you get errors here. */ 762608aefcSPyun YongHyeon #include "miibus_if.h" 772608aefcSPyun YongHyeon 782608aefcSPyun YongHyeon MODULE_DEPEND(vte, pci, 1, 1, 1); 792608aefcSPyun YongHyeon MODULE_DEPEND(vte, ether, 1, 1, 1); 802608aefcSPyun YongHyeon MODULE_DEPEND(vte, miibus, 1, 1, 1); 812608aefcSPyun YongHyeon 822608aefcSPyun YongHyeon /* Tunables. */ 832608aefcSPyun YongHyeon static int tx_deep_copy = 1; 842608aefcSPyun YongHyeon TUNABLE_INT("hw.vte.tx_deep_copy", &tx_deep_copy); 852608aefcSPyun YongHyeon 862608aefcSPyun YongHyeon /* 872608aefcSPyun YongHyeon * Devices supported by this driver. 882608aefcSPyun YongHyeon */ 892608aefcSPyun YongHyeon static const struct vte_ident vte_ident_table[] = { 902608aefcSPyun YongHyeon { VENDORID_RDC, DEVICEID_RDC_R6040, "RDC R6040 FastEthernet"}, 912608aefcSPyun YongHyeon { 0, 0, NULL} 922608aefcSPyun YongHyeon }; 932608aefcSPyun YongHyeon 942608aefcSPyun YongHyeon static int vte_attach(device_t); 952608aefcSPyun YongHyeon static int vte_detach(device_t); 962608aefcSPyun YongHyeon static int vte_dma_alloc(struct vte_softc *); 972608aefcSPyun YongHyeon static void vte_dma_free(struct vte_softc *); 982608aefcSPyun YongHyeon static void vte_dmamap_cb(void *, bus_dma_segment_t *, int, int); 992608aefcSPyun YongHyeon static struct vte_txdesc * 1002608aefcSPyun YongHyeon vte_encap(struct vte_softc *, struct mbuf **); 1012608aefcSPyun YongHyeon static const struct vte_ident * 1022608aefcSPyun YongHyeon vte_find_ident(device_t); 1032608aefcSPyun YongHyeon #ifndef __NO_STRICT_ALIGNMENT 1042608aefcSPyun YongHyeon static struct mbuf * 1053486b835SJustin Hibbits vte_fixup_rx(if_t, struct mbuf *); 1062608aefcSPyun YongHyeon #endif 1072608aefcSPyun YongHyeon static void vte_get_macaddr(struct vte_softc *); 1082608aefcSPyun YongHyeon static void vte_init(void *); 1092608aefcSPyun YongHyeon static void vte_init_locked(struct vte_softc *); 1102608aefcSPyun YongHyeon static int vte_init_rx_ring(struct vte_softc *); 1112608aefcSPyun YongHyeon static int vte_init_tx_ring(struct vte_softc *); 1122608aefcSPyun YongHyeon static void vte_intr(void *); 1133486b835SJustin Hibbits static int vte_ioctl(if_t, u_long, caddr_t); 1143486b835SJustin Hibbits static uint64_t vte_get_counter(if_t, ift_counter); 1152608aefcSPyun YongHyeon static void vte_mac_config(struct vte_softc *); 1162608aefcSPyun YongHyeon static int vte_miibus_readreg(device_t, int, int); 1172608aefcSPyun YongHyeon static void vte_miibus_statchg(device_t); 1182608aefcSPyun YongHyeon static int vte_miibus_writereg(device_t, int, int, int); 1193486b835SJustin Hibbits static int vte_mediachange(if_t); 1203486b835SJustin Hibbits static int vte_mediachange_locked(if_t); 1213486b835SJustin Hibbits static void vte_mediastatus(if_t, struct ifmediareq *); 1222608aefcSPyun YongHyeon static int vte_newbuf(struct vte_softc *, struct vte_rxdesc *); 1232608aefcSPyun YongHyeon static int vte_probe(device_t); 1242608aefcSPyun YongHyeon static void vte_reset(struct vte_softc *); 1252608aefcSPyun YongHyeon static int vte_resume(device_t); 1262608aefcSPyun YongHyeon static void vte_rxeof(struct vte_softc *); 1272608aefcSPyun YongHyeon static void vte_rxfilter(struct vte_softc *); 1282608aefcSPyun YongHyeon static int vte_shutdown(device_t); 1293486b835SJustin Hibbits static void vte_start(if_t); 1302608aefcSPyun YongHyeon static void vte_start_locked(struct vte_softc *); 1312608aefcSPyun YongHyeon static void vte_start_mac(struct vte_softc *); 1322608aefcSPyun YongHyeon static void vte_stats_clear(struct vte_softc *); 1332608aefcSPyun YongHyeon static void vte_stats_update(struct vte_softc *); 1342608aefcSPyun YongHyeon static void vte_stop(struct vte_softc *); 1352608aefcSPyun YongHyeon static void vte_stop_mac(struct vte_softc *); 1362608aefcSPyun YongHyeon static int vte_suspend(device_t); 1372608aefcSPyun YongHyeon static void vte_sysctl_node(struct vte_softc *); 1382608aefcSPyun YongHyeon static void vte_tick(void *); 1392608aefcSPyun YongHyeon static void vte_txeof(struct vte_softc *); 1402608aefcSPyun YongHyeon static void vte_watchdog(struct vte_softc *); 1412608aefcSPyun YongHyeon static int sysctl_int_range(SYSCTL_HANDLER_ARGS, int, int); 1422608aefcSPyun YongHyeon static int sysctl_hw_vte_int_mod(SYSCTL_HANDLER_ARGS); 1432608aefcSPyun YongHyeon 1442608aefcSPyun YongHyeon static device_method_t vte_methods[] = { 1452608aefcSPyun YongHyeon /* Device interface. */ 1462608aefcSPyun YongHyeon DEVMETHOD(device_probe, vte_probe), 1472608aefcSPyun YongHyeon DEVMETHOD(device_attach, vte_attach), 1482608aefcSPyun YongHyeon DEVMETHOD(device_detach, vte_detach), 1492608aefcSPyun YongHyeon DEVMETHOD(device_shutdown, vte_shutdown), 1502608aefcSPyun YongHyeon DEVMETHOD(device_suspend, vte_suspend), 1512608aefcSPyun YongHyeon DEVMETHOD(device_resume, vte_resume), 1522608aefcSPyun YongHyeon 1532608aefcSPyun YongHyeon /* MII interface. */ 1542608aefcSPyun YongHyeon DEVMETHOD(miibus_readreg, vte_miibus_readreg), 1552608aefcSPyun YongHyeon DEVMETHOD(miibus_writereg, vte_miibus_writereg), 1562608aefcSPyun YongHyeon DEVMETHOD(miibus_statchg, vte_miibus_statchg), 1572608aefcSPyun YongHyeon 158848e30ffSMarius Strobl DEVMETHOD_END 1592608aefcSPyun YongHyeon }; 1602608aefcSPyun YongHyeon 1612608aefcSPyun YongHyeon static driver_t vte_driver = { 1622608aefcSPyun YongHyeon "vte", 1632608aefcSPyun YongHyeon vte_methods, 1642608aefcSPyun YongHyeon sizeof(struct vte_softc) 1652608aefcSPyun YongHyeon }; 1662608aefcSPyun YongHyeon 16733bc7691SJohn Baldwin DRIVER_MODULE(vte, pci, vte_driver, 0, 0); 1683e38757dSJohn Baldwin DRIVER_MODULE(miibus, vte, miibus_driver, 0, 0); 1692608aefcSPyun YongHyeon 1702608aefcSPyun YongHyeon static int 1712608aefcSPyun YongHyeon vte_miibus_readreg(device_t dev, int phy, int reg) 1722608aefcSPyun YongHyeon { 1732608aefcSPyun YongHyeon struct vte_softc *sc; 1742608aefcSPyun YongHyeon int i; 1752608aefcSPyun YongHyeon 1762608aefcSPyun YongHyeon sc = device_get_softc(dev); 1772608aefcSPyun YongHyeon 1782608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MMDIO, MMDIO_READ | 1792608aefcSPyun YongHyeon (phy << MMDIO_PHY_ADDR_SHIFT) | (reg << MMDIO_REG_ADDR_SHIFT)); 1802608aefcSPyun YongHyeon for (i = VTE_PHY_TIMEOUT; i > 0; i--) { 1812608aefcSPyun YongHyeon DELAY(5); 1822608aefcSPyun YongHyeon if ((CSR_READ_2(sc, VTE_MMDIO) & MMDIO_READ) == 0) 1832608aefcSPyun YongHyeon break; 1842608aefcSPyun YongHyeon } 1852608aefcSPyun YongHyeon 1862608aefcSPyun YongHyeon if (i == 0) { 1872608aefcSPyun YongHyeon device_printf(sc->vte_dev, "phy read timeout : %d\n", reg); 1882608aefcSPyun YongHyeon return (0); 1892608aefcSPyun YongHyeon } 1902608aefcSPyun YongHyeon 1912608aefcSPyun YongHyeon return (CSR_READ_2(sc, VTE_MMRD)); 1922608aefcSPyun YongHyeon } 1932608aefcSPyun YongHyeon 1942608aefcSPyun YongHyeon static int 1952608aefcSPyun YongHyeon vte_miibus_writereg(device_t dev, int phy, int reg, int val) 1962608aefcSPyun YongHyeon { 1972608aefcSPyun YongHyeon struct vte_softc *sc; 1982608aefcSPyun YongHyeon int i; 1992608aefcSPyun YongHyeon 2002608aefcSPyun YongHyeon sc = device_get_softc(dev); 2012608aefcSPyun YongHyeon 2022608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MMWD, val); 2032608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MMDIO, MMDIO_WRITE | 2042608aefcSPyun YongHyeon (phy << MMDIO_PHY_ADDR_SHIFT) | (reg << MMDIO_REG_ADDR_SHIFT)); 2052608aefcSPyun YongHyeon for (i = VTE_PHY_TIMEOUT; i > 0; i--) { 2062608aefcSPyun YongHyeon DELAY(5); 2072608aefcSPyun YongHyeon if ((CSR_READ_2(sc, VTE_MMDIO) & MMDIO_WRITE) == 0) 2082608aefcSPyun YongHyeon break; 2092608aefcSPyun YongHyeon } 2102608aefcSPyun YongHyeon 2112608aefcSPyun YongHyeon if (i == 0) 2122608aefcSPyun YongHyeon device_printf(sc->vte_dev, "phy write timeout : %d\n", reg); 2132608aefcSPyun YongHyeon 2142608aefcSPyun YongHyeon return (0); 2152608aefcSPyun YongHyeon } 2162608aefcSPyun YongHyeon 2172608aefcSPyun YongHyeon static void 2182608aefcSPyun YongHyeon vte_miibus_statchg(device_t dev) 2192608aefcSPyun YongHyeon { 2202608aefcSPyun YongHyeon struct vte_softc *sc; 2212608aefcSPyun YongHyeon struct mii_data *mii; 2223486b835SJustin Hibbits if_t ifp; 2232608aefcSPyun YongHyeon uint16_t val; 2242608aefcSPyun YongHyeon 2252608aefcSPyun YongHyeon sc = device_get_softc(dev); 2262608aefcSPyun YongHyeon 2272608aefcSPyun YongHyeon mii = device_get_softc(sc->vte_miibus); 2282608aefcSPyun YongHyeon ifp = sc->vte_ifp; 2293486b835SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) 2302608aefcSPyun YongHyeon return; 2312608aefcSPyun YongHyeon 2322608aefcSPyun YongHyeon sc->vte_flags &= ~VTE_FLAG_LINK; 2332608aefcSPyun YongHyeon if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == 2342608aefcSPyun YongHyeon (IFM_ACTIVE | IFM_AVALID)) { 2352608aefcSPyun YongHyeon switch (IFM_SUBTYPE(mii->mii_media_active)) { 2362608aefcSPyun YongHyeon case IFM_10_T: 2372608aefcSPyun YongHyeon case IFM_100_TX: 2382608aefcSPyun YongHyeon sc->vte_flags |= VTE_FLAG_LINK; 2392608aefcSPyun YongHyeon break; 2402608aefcSPyun YongHyeon default: 2412608aefcSPyun YongHyeon break; 2422608aefcSPyun YongHyeon } 2432608aefcSPyun YongHyeon } 2442608aefcSPyun YongHyeon 2452608aefcSPyun YongHyeon /* Stop RX/TX MACs. */ 2462608aefcSPyun YongHyeon vte_stop_mac(sc); 2472608aefcSPyun YongHyeon /* Program MACs with resolved duplex and flow control. */ 2482608aefcSPyun YongHyeon if ((sc->vte_flags & VTE_FLAG_LINK) != 0) { 2492608aefcSPyun YongHyeon /* 2502608aefcSPyun YongHyeon * Timer waiting time : (63 + TIMER * 64) MII clock. 2512608aefcSPyun YongHyeon * MII clock : 25MHz(100Mbps) or 2.5MHz(10Mbps). 2522608aefcSPyun YongHyeon */ 2532608aefcSPyun YongHyeon if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) 2542608aefcSPyun YongHyeon val = 18 << VTE_IM_TIMER_SHIFT; 2552608aefcSPyun YongHyeon else 2562608aefcSPyun YongHyeon val = 1 << VTE_IM_TIMER_SHIFT; 2572608aefcSPyun YongHyeon val |= sc->vte_int_rx_mod << VTE_IM_BUNDLE_SHIFT; 2582608aefcSPyun YongHyeon /* 48.6us for 100Mbps, 50.8us for 10Mbps */ 2592608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MRICR, val); 2602608aefcSPyun YongHyeon 2612608aefcSPyun YongHyeon if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) 2622608aefcSPyun YongHyeon val = 18 << VTE_IM_TIMER_SHIFT; 2632608aefcSPyun YongHyeon else 2642608aefcSPyun YongHyeon val = 1 << VTE_IM_TIMER_SHIFT; 2652608aefcSPyun YongHyeon val |= sc->vte_int_tx_mod << VTE_IM_BUNDLE_SHIFT; 2662608aefcSPyun YongHyeon /* 48.6us for 100Mbps, 50.8us for 10Mbps */ 2672608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MTICR, val); 2682608aefcSPyun YongHyeon 2692608aefcSPyun YongHyeon vte_mac_config(sc); 2702608aefcSPyun YongHyeon vte_start_mac(sc); 2712608aefcSPyun YongHyeon } 2722608aefcSPyun YongHyeon } 2732608aefcSPyun YongHyeon 2742608aefcSPyun YongHyeon static void 2753486b835SJustin Hibbits vte_mediastatus(if_t ifp, struct ifmediareq *ifmr) 2762608aefcSPyun YongHyeon { 2772608aefcSPyun YongHyeon struct vte_softc *sc; 2782608aefcSPyun YongHyeon struct mii_data *mii; 2792608aefcSPyun YongHyeon 2803486b835SJustin Hibbits sc = if_getsoftc(ifp); 2812608aefcSPyun YongHyeon VTE_LOCK(sc); 2823486b835SJustin Hibbits if ((if_getflags(ifp) & IFF_UP) == 0) { 2832608aefcSPyun YongHyeon VTE_UNLOCK(sc); 2842608aefcSPyun YongHyeon return; 2852608aefcSPyun YongHyeon } 2862608aefcSPyun YongHyeon mii = device_get_softc(sc->vte_miibus); 2872608aefcSPyun YongHyeon 2882608aefcSPyun YongHyeon mii_pollstat(mii); 2892608aefcSPyun YongHyeon ifmr->ifm_status = mii->mii_media_status; 2902608aefcSPyun YongHyeon ifmr->ifm_active = mii->mii_media_active; 29157c81d92SPyun YongHyeon VTE_UNLOCK(sc); 2922608aefcSPyun YongHyeon } 2932608aefcSPyun YongHyeon 2942608aefcSPyun YongHyeon static int 2953486b835SJustin Hibbits vte_mediachange(if_t ifp) 2962608aefcSPyun YongHyeon { 2972608aefcSPyun YongHyeon struct vte_softc *sc; 2982608aefcSPyun YongHyeon int error; 2992608aefcSPyun YongHyeon 3003486b835SJustin Hibbits sc = if_getsoftc(ifp); 3012608aefcSPyun YongHyeon VTE_LOCK(sc); 3022608aefcSPyun YongHyeon error = vte_mediachange_locked(ifp); 3032608aefcSPyun YongHyeon VTE_UNLOCK(sc); 3042608aefcSPyun YongHyeon return (error); 3052608aefcSPyun YongHyeon } 3062608aefcSPyun YongHyeon 3072608aefcSPyun YongHyeon static int 3083486b835SJustin Hibbits vte_mediachange_locked(if_t ifp) 3092608aefcSPyun YongHyeon { 3102608aefcSPyun YongHyeon struct vte_softc *sc; 3112608aefcSPyun YongHyeon struct mii_data *mii; 3122608aefcSPyun YongHyeon struct mii_softc *miisc; 3132608aefcSPyun YongHyeon int error; 3142608aefcSPyun YongHyeon 3153486b835SJustin Hibbits sc = if_getsoftc(ifp); 3162608aefcSPyun YongHyeon mii = device_get_softc(sc->vte_miibus); 3172608aefcSPyun YongHyeon LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 3183fcb7a53SMarius Strobl PHY_RESET(miisc); 3192608aefcSPyun YongHyeon error = mii_mediachg(mii); 3202608aefcSPyun YongHyeon 3212608aefcSPyun YongHyeon return (error); 3222608aefcSPyun YongHyeon } 3232608aefcSPyun YongHyeon 3242608aefcSPyun YongHyeon static const struct vte_ident * 3252608aefcSPyun YongHyeon vte_find_ident(device_t dev) 3262608aefcSPyun YongHyeon { 3272608aefcSPyun YongHyeon const struct vte_ident *ident; 3282608aefcSPyun YongHyeon uint16_t vendor, devid; 3292608aefcSPyun YongHyeon 3302608aefcSPyun YongHyeon vendor = pci_get_vendor(dev); 3312608aefcSPyun YongHyeon devid = pci_get_device(dev); 3322608aefcSPyun YongHyeon for (ident = vte_ident_table; ident->name != NULL; ident++) { 3332608aefcSPyun YongHyeon if (vendor == ident->vendorid && devid == ident->deviceid) 3342608aefcSPyun YongHyeon return (ident); 3352608aefcSPyun YongHyeon } 3362608aefcSPyun YongHyeon 3372608aefcSPyun YongHyeon return (NULL); 3382608aefcSPyun YongHyeon } 3392608aefcSPyun YongHyeon 3402608aefcSPyun YongHyeon static int 3412608aefcSPyun YongHyeon vte_probe(device_t dev) 3422608aefcSPyun YongHyeon { 3432608aefcSPyun YongHyeon const struct vte_ident *ident; 3442608aefcSPyun YongHyeon 3452608aefcSPyun YongHyeon ident = vte_find_ident(dev); 3462608aefcSPyun YongHyeon if (ident != NULL) { 3472608aefcSPyun YongHyeon device_set_desc(dev, ident->name); 3482608aefcSPyun YongHyeon return (BUS_PROBE_DEFAULT); 3492608aefcSPyun YongHyeon } 3502608aefcSPyun YongHyeon 3512608aefcSPyun YongHyeon return (ENXIO); 3522608aefcSPyun YongHyeon } 3532608aefcSPyun YongHyeon 3542608aefcSPyun YongHyeon static void 3552608aefcSPyun YongHyeon vte_get_macaddr(struct vte_softc *sc) 3562608aefcSPyun YongHyeon { 3572608aefcSPyun YongHyeon uint16_t mid; 3582608aefcSPyun YongHyeon 3592608aefcSPyun YongHyeon /* 3602608aefcSPyun YongHyeon * It seems there is no way to reload station address and 3612608aefcSPyun YongHyeon * it is supposed to be set by BIOS. 3622608aefcSPyun YongHyeon */ 3632608aefcSPyun YongHyeon mid = CSR_READ_2(sc, VTE_MID0L); 3642608aefcSPyun YongHyeon sc->vte_eaddr[0] = (mid >> 0) & 0xFF; 3652608aefcSPyun YongHyeon sc->vte_eaddr[1] = (mid >> 8) & 0xFF; 3662608aefcSPyun YongHyeon mid = CSR_READ_2(sc, VTE_MID0M); 3672608aefcSPyun YongHyeon sc->vte_eaddr[2] = (mid >> 0) & 0xFF; 3682608aefcSPyun YongHyeon sc->vte_eaddr[3] = (mid >> 8) & 0xFF; 3692608aefcSPyun YongHyeon mid = CSR_READ_2(sc, VTE_MID0H); 3702608aefcSPyun YongHyeon sc->vte_eaddr[4] = (mid >> 0) & 0xFF; 3712608aefcSPyun YongHyeon sc->vte_eaddr[5] = (mid >> 8) & 0xFF; 3722608aefcSPyun YongHyeon } 3732608aefcSPyun YongHyeon 3742608aefcSPyun YongHyeon static int 3752608aefcSPyun YongHyeon vte_attach(device_t dev) 3762608aefcSPyun YongHyeon { 3772608aefcSPyun YongHyeon struct vte_softc *sc; 3783486b835SJustin Hibbits if_t ifp; 3792608aefcSPyun YongHyeon uint16_t macid; 3802608aefcSPyun YongHyeon int error, rid; 3812608aefcSPyun YongHyeon 3822608aefcSPyun YongHyeon error = 0; 3832608aefcSPyun YongHyeon sc = device_get_softc(dev); 3842608aefcSPyun YongHyeon sc->vte_dev = dev; 3852608aefcSPyun YongHyeon 3862608aefcSPyun YongHyeon mtx_init(&sc->vte_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 3872608aefcSPyun YongHyeon MTX_DEF); 3882608aefcSPyun YongHyeon callout_init_mtx(&sc->vte_tick_ch, &sc->vte_mtx, 0); 3892608aefcSPyun YongHyeon sc->vte_ident = vte_find_ident(dev); 3902608aefcSPyun YongHyeon 3912608aefcSPyun YongHyeon /* Map the device. */ 3922608aefcSPyun YongHyeon pci_enable_busmaster(dev); 3932608aefcSPyun YongHyeon sc->vte_res_id = PCIR_BAR(1); 3942608aefcSPyun YongHyeon sc->vte_res_type = SYS_RES_MEMORY; 3952608aefcSPyun YongHyeon sc->vte_res = bus_alloc_resource_any(dev, sc->vte_res_type, 3962608aefcSPyun YongHyeon &sc->vte_res_id, RF_ACTIVE); 3972608aefcSPyun YongHyeon if (sc->vte_res == NULL) { 3982608aefcSPyun YongHyeon sc->vte_res_id = PCIR_BAR(0); 3992608aefcSPyun YongHyeon sc->vte_res_type = SYS_RES_IOPORT; 4002608aefcSPyun YongHyeon sc->vte_res = bus_alloc_resource_any(dev, sc->vte_res_type, 4012608aefcSPyun YongHyeon &sc->vte_res_id, RF_ACTIVE); 4022608aefcSPyun YongHyeon if (sc->vte_res == NULL) { 4032608aefcSPyun YongHyeon device_printf(dev, "cannot map memory/ports.\n"); 4042608aefcSPyun YongHyeon mtx_destroy(&sc->vte_mtx); 4052608aefcSPyun YongHyeon return (ENXIO); 4062608aefcSPyun YongHyeon } 4072608aefcSPyun YongHyeon } 4082608aefcSPyun YongHyeon if (bootverbose) { 4092608aefcSPyun YongHyeon device_printf(dev, "using %s space register mapping\n", 4102608aefcSPyun YongHyeon sc->vte_res_type == SYS_RES_MEMORY ? "memory" : "I/O"); 4112608aefcSPyun YongHyeon device_printf(dev, "MAC Identifier : 0x%04x\n", 4122608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_MACID)); 4132608aefcSPyun YongHyeon macid = CSR_READ_2(sc, VTE_MACID_REV); 4142608aefcSPyun YongHyeon device_printf(dev, "MAC Id. 0x%02x, Rev. 0x%02x\n", 4152608aefcSPyun YongHyeon (macid & VTE_MACID_MASK) >> VTE_MACID_SHIFT, 4162608aefcSPyun YongHyeon (macid & VTE_MACID_REV_MASK) >> VTE_MACID_REV_SHIFT); 4172608aefcSPyun YongHyeon } 4182608aefcSPyun YongHyeon 4192608aefcSPyun YongHyeon rid = 0; 4202608aefcSPyun YongHyeon sc->vte_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 4212608aefcSPyun YongHyeon RF_SHAREABLE | RF_ACTIVE); 4222608aefcSPyun YongHyeon if (sc->vte_irq == NULL) { 4232608aefcSPyun YongHyeon device_printf(dev, "cannot allocate IRQ resources.\n"); 4242608aefcSPyun YongHyeon error = ENXIO; 4252608aefcSPyun YongHyeon goto fail; 4262608aefcSPyun YongHyeon } 4272608aefcSPyun YongHyeon 4282608aefcSPyun YongHyeon /* Reset the ethernet controller. */ 4292608aefcSPyun YongHyeon vte_reset(sc); 4302608aefcSPyun YongHyeon 4319dda5c8fSPyun YongHyeon if ((error = vte_dma_alloc(sc)) != 0) 4322608aefcSPyun YongHyeon goto fail; 4332608aefcSPyun YongHyeon 4342608aefcSPyun YongHyeon /* Create device sysctl node. */ 4352608aefcSPyun YongHyeon vte_sysctl_node(sc); 4362608aefcSPyun YongHyeon 4372608aefcSPyun YongHyeon /* Load station address. */ 4382608aefcSPyun YongHyeon vte_get_macaddr(sc); 4392608aefcSPyun YongHyeon 4402608aefcSPyun YongHyeon ifp = sc->vte_ifp = if_alloc(IFT_ETHER); 4412608aefcSPyun YongHyeon if (ifp == NULL) { 4422608aefcSPyun YongHyeon device_printf(dev, "cannot allocate ifnet structure.\n"); 4432608aefcSPyun YongHyeon error = ENXIO; 4442608aefcSPyun YongHyeon goto fail; 4452608aefcSPyun YongHyeon } 4462608aefcSPyun YongHyeon 4473486b835SJustin Hibbits if_setsoftc(ifp, sc); 4482608aefcSPyun YongHyeon if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 4493486b835SJustin Hibbits if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST); 4503486b835SJustin Hibbits if_setioctlfn(ifp, vte_ioctl); 4513486b835SJustin Hibbits if_setstartfn(ifp, vte_start); 4523486b835SJustin Hibbits if_setinitfn(ifp, vte_init); 4533486b835SJustin Hibbits if_setgetcounterfn(ifp, vte_get_counter); 4543486b835SJustin Hibbits if_setsendqlen(ifp, VTE_TX_RING_CNT - 1); 4553486b835SJustin Hibbits if_setsendqready(ifp); 4562608aefcSPyun YongHyeon 4572608aefcSPyun YongHyeon /* 4582608aefcSPyun YongHyeon * Set up MII bus. 4592608aefcSPyun YongHyeon * BIOS would have initialized VTE_MPSCCR to catch PHY 4602608aefcSPyun YongHyeon * status changes so driver may be able to extract 4612608aefcSPyun YongHyeon * configured PHY address. Since it's common to see BIOS 4622608aefcSPyun YongHyeon * fails to initialize the register(including the sample 4632608aefcSPyun YongHyeon * board I have), let mii(4) probe it. This is more 4642608aefcSPyun YongHyeon * reliable than relying on BIOS's initialization. 4652608aefcSPyun YongHyeon * 4662608aefcSPyun YongHyeon * Advertising flow control capability to mii(4) was 4672608aefcSPyun YongHyeon * intentionally disabled due to severe problems in TX 4682608aefcSPyun YongHyeon * pause frame generation. See vte_rxeof() for more 4692608aefcSPyun YongHyeon * details. 4702608aefcSPyun YongHyeon */ 4712608aefcSPyun YongHyeon error = mii_attach(dev, &sc->vte_miibus, ifp, vte_mediachange, 4722608aefcSPyun YongHyeon vte_mediastatus, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); 4732608aefcSPyun YongHyeon if (error != 0) { 4742608aefcSPyun YongHyeon device_printf(dev, "attaching PHYs failed\n"); 4752608aefcSPyun YongHyeon goto fail; 4762608aefcSPyun YongHyeon } 4772608aefcSPyun YongHyeon 4782608aefcSPyun YongHyeon ether_ifattach(ifp, sc->vte_eaddr); 4792608aefcSPyun YongHyeon 4802608aefcSPyun YongHyeon /* VLAN capability setup. */ 4813486b835SJustin Hibbits if_setcapabilitiesbit(ifp, IFCAP_VLAN_MTU, 0); 4823486b835SJustin Hibbits if_setcapenable(ifp, if_getcapabilities(ifp)); 4832608aefcSPyun YongHyeon /* Tell the upper layer we support VLAN over-sized frames. */ 4843486b835SJustin Hibbits if_setifheaderlen(ifp, sizeof(struct ether_vlan_header)); 4852608aefcSPyun YongHyeon 4862608aefcSPyun YongHyeon error = bus_setup_intr(dev, sc->vte_irq, INTR_TYPE_NET | INTR_MPSAFE, 4872608aefcSPyun YongHyeon NULL, vte_intr, sc, &sc->vte_intrhand); 4882608aefcSPyun YongHyeon if (error != 0) { 4892608aefcSPyun YongHyeon device_printf(dev, "could not set up interrupt handler.\n"); 4902608aefcSPyun YongHyeon ether_ifdetach(ifp); 4912608aefcSPyun YongHyeon goto fail; 4922608aefcSPyun YongHyeon } 4932608aefcSPyun YongHyeon 4942608aefcSPyun YongHyeon fail: 4952608aefcSPyun YongHyeon if (error != 0) 4962608aefcSPyun YongHyeon vte_detach(dev); 4972608aefcSPyun YongHyeon 4982608aefcSPyun YongHyeon return (error); 4992608aefcSPyun YongHyeon } 5002608aefcSPyun YongHyeon 5012608aefcSPyun YongHyeon static int 5022608aefcSPyun YongHyeon vte_detach(device_t dev) 5032608aefcSPyun YongHyeon { 5042608aefcSPyun YongHyeon struct vte_softc *sc; 5053486b835SJustin Hibbits if_t ifp; 5062608aefcSPyun YongHyeon 5072608aefcSPyun YongHyeon sc = device_get_softc(dev); 5082608aefcSPyun YongHyeon 5092608aefcSPyun YongHyeon ifp = sc->vte_ifp; 5102608aefcSPyun YongHyeon if (device_is_attached(dev)) { 5112608aefcSPyun YongHyeon VTE_LOCK(sc); 5122608aefcSPyun YongHyeon vte_stop(sc); 5132608aefcSPyun YongHyeon VTE_UNLOCK(sc); 5142608aefcSPyun YongHyeon callout_drain(&sc->vte_tick_ch); 5152608aefcSPyun YongHyeon ether_ifdetach(ifp); 5162608aefcSPyun YongHyeon } 5172608aefcSPyun YongHyeon 5182608aefcSPyun YongHyeon if (sc->vte_miibus != NULL) { 5192608aefcSPyun YongHyeon device_delete_child(dev, sc->vte_miibus); 5202608aefcSPyun YongHyeon sc->vte_miibus = NULL; 5212608aefcSPyun YongHyeon } 5222608aefcSPyun YongHyeon bus_generic_detach(dev); 5232608aefcSPyun YongHyeon 5242608aefcSPyun YongHyeon if (sc->vte_intrhand != NULL) { 5252608aefcSPyun YongHyeon bus_teardown_intr(dev, sc->vte_irq, sc->vte_intrhand); 5262608aefcSPyun YongHyeon sc->vte_intrhand = NULL; 5272608aefcSPyun YongHyeon } 5282608aefcSPyun YongHyeon if (sc->vte_irq != NULL) { 5292608aefcSPyun YongHyeon bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vte_irq); 5302608aefcSPyun YongHyeon sc->vte_irq = NULL; 5312608aefcSPyun YongHyeon } 5322608aefcSPyun YongHyeon if (sc->vte_res != NULL) { 5332608aefcSPyun YongHyeon bus_release_resource(dev, sc->vte_res_type, sc->vte_res_id, 5342608aefcSPyun YongHyeon sc->vte_res); 5352608aefcSPyun YongHyeon sc->vte_res = NULL; 5362608aefcSPyun YongHyeon } 5372608aefcSPyun YongHyeon if (ifp != NULL) { 5382608aefcSPyun YongHyeon if_free(ifp); 5392608aefcSPyun YongHyeon sc->vte_ifp = NULL; 5402608aefcSPyun YongHyeon } 5412608aefcSPyun YongHyeon vte_dma_free(sc); 5422608aefcSPyun YongHyeon mtx_destroy(&sc->vte_mtx); 5432608aefcSPyun YongHyeon 5442608aefcSPyun YongHyeon return (0); 5452608aefcSPyun YongHyeon } 5462608aefcSPyun YongHyeon 5472608aefcSPyun YongHyeon #define VTE_SYSCTL_STAT_ADD32(c, h, n, p, d) \ 5482608aefcSPyun YongHyeon SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) 5492608aefcSPyun YongHyeon 5502608aefcSPyun YongHyeon static void 5512608aefcSPyun YongHyeon vte_sysctl_node(struct vte_softc *sc) 5522608aefcSPyun YongHyeon { 5532608aefcSPyun YongHyeon struct sysctl_ctx_list *ctx; 5542608aefcSPyun YongHyeon struct sysctl_oid_list *child, *parent; 5552608aefcSPyun YongHyeon struct sysctl_oid *tree; 5562608aefcSPyun YongHyeon struct vte_hw_stats *stats; 5572608aefcSPyun YongHyeon int error; 5582608aefcSPyun YongHyeon 5592608aefcSPyun YongHyeon stats = &sc->vte_stats; 5602608aefcSPyun YongHyeon ctx = device_get_sysctl_ctx(sc->vte_dev); 5612608aefcSPyun YongHyeon child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->vte_dev)); 5622608aefcSPyun YongHyeon 5632608aefcSPyun YongHyeon SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "int_rx_mod", 5647029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 5657029da5cSPawel Biernacki &sc->vte_int_rx_mod, 0, sysctl_hw_vte_int_mod, "I", 5667029da5cSPawel Biernacki "vte RX interrupt moderation"); 5672608aefcSPyun YongHyeon SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "int_tx_mod", 5687029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 5697029da5cSPawel Biernacki &sc->vte_int_tx_mod, 0, sysctl_hw_vte_int_mod, "I", 5707029da5cSPawel Biernacki "vte TX interrupt moderation"); 5712608aefcSPyun YongHyeon /* Pull in device tunables. */ 5722608aefcSPyun YongHyeon sc->vte_int_rx_mod = VTE_IM_RX_BUNDLE_DEFAULT; 5732608aefcSPyun YongHyeon error = resource_int_value(device_get_name(sc->vte_dev), 5742608aefcSPyun YongHyeon device_get_unit(sc->vte_dev), "int_rx_mod", &sc->vte_int_rx_mod); 5752608aefcSPyun YongHyeon if (error == 0) { 5762608aefcSPyun YongHyeon if (sc->vte_int_rx_mod < VTE_IM_BUNDLE_MIN || 5772608aefcSPyun YongHyeon sc->vte_int_rx_mod > VTE_IM_BUNDLE_MAX) { 5782608aefcSPyun YongHyeon device_printf(sc->vte_dev, "int_rx_mod value out of " 5792608aefcSPyun YongHyeon "range; using default: %d\n", 5802608aefcSPyun YongHyeon VTE_IM_RX_BUNDLE_DEFAULT); 5812608aefcSPyun YongHyeon sc->vte_int_rx_mod = VTE_IM_RX_BUNDLE_DEFAULT; 5822608aefcSPyun YongHyeon } 5832608aefcSPyun YongHyeon } 5842608aefcSPyun YongHyeon 5852608aefcSPyun YongHyeon sc->vte_int_tx_mod = VTE_IM_TX_BUNDLE_DEFAULT; 5862608aefcSPyun YongHyeon error = resource_int_value(device_get_name(sc->vte_dev), 5872608aefcSPyun YongHyeon device_get_unit(sc->vte_dev), "int_tx_mod", &sc->vte_int_tx_mod); 5882608aefcSPyun YongHyeon if (error == 0) { 5892608aefcSPyun YongHyeon if (sc->vte_int_tx_mod < VTE_IM_BUNDLE_MIN || 5902608aefcSPyun YongHyeon sc->vte_int_tx_mod > VTE_IM_BUNDLE_MAX) { 5912608aefcSPyun YongHyeon device_printf(sc->vte_dev, "int_tx_mod value out of " 5922608aefcSPyun YongHyeon "range; using default: %d\n", 5932608aefcSPyun YongHyeon VTE_IM_TX_BUNDLE_DEFAULT); 5942608aefcSPyun YongHyeon sc->vte_int_tx_mod = VTE_IM_TX_BUNDLE_DEFAULT; 5952608aefcSPyun YongHyeon } 5962608aefcSPyun YongHyeon } 5972608aefcSPyun YongHyeon 5987029da5cSPawel Biernacki tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", 5997029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "VTE statistics"); 6002608aefcSPyun YongHyeon parent = SYSCTL_CHILDREN(tree); 6012608aefcSPyun YongHyeon 6022608aefcSPyun YongHyeon /* RX statistics. */ 6037029da5cSPawel Biernacki tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx", 6047029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "RX MAC statistics"); 6052608aefcSPyun YongHyeon child = SYSCTL_CHILDREN(tree); 6062608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "good_frames", 6072608aefcSPyun YongHyeon &stats->rx_frames, "Good frames"); 6082608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "good_bcast_frames", 6092608aefcSPyun YongHyeon &stats->rx_bcast_frames, "Good broadcast frames"); 6102608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "good_mcast_frames", 6112608aefcSPyun YongHyeon &stats->rx_mcast_frames, "Good multicast frames"); 6122608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "runt", 6132608aefcSPyun YongHyeon &stats->rx_runts, "Too short frames"); 6142608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "crc_errs", 6152608aefcSPyun YongHyeon &stats->rx_crcerrs, "CRC errors"); 6162608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "long_frames", 6172608aefcSPyun YongHyeon &stats->rx_long_frames, 6182608aefcSPyun YongHyeon "Frames that have longer length than maximum packet length"); 6192608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "fifo_full", 6202608aefcSPyun YongHyeon &stats->rx_fifo_full, "FIFO full"); 6212608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "desc_unavail", 6222608aefcSPyun YongHyeon &stats->rx_desc_unavail, "Descriptor unavailable frames"); 6232608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "pause_frames", 6242608aefcSPyun YongHyeon &stats->rx_pause_frames, "Pause control frames"); 6252608aefcSPyun YongHyeon 6262608aefcSPyun YongHyeon /* TX statistics. */ 6277029da5cSPawel Biernacki tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", 6287029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "TX MAC statistics"); 6292608aefcSPyun YongHyeon child = SYSCTL_CHILDREN(tree); 6302608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "good_frames", 6312608aefcSPyun YongHyeon &stats->tx_frames, "Good frames"); 6322608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "underruns", 6332608aefcSPyun YongHyeon &stats->tx_underruns, "FIFO underruns"); 6342608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "late_colls", 6352608aefcSPyun YongHyeon &stats->tx_late_colls, "Late collisions"); 6362608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "pause_frames", 6372608aefcSPyun YongHyeon &stats->tx_pause_frames, "Pause control frames"); 6382608aefcSPyun YongHyeon } 6392608aefcSPyun YongHyeon 6402608aefcSPyun YongHyeon #undef VTE_SYSCTL_STAT_ADD32 6412608aefcSPyun YongHyeon 6422608aefcSPyun YongHyeon struct vte_dmamap_arg { 6432608aefcSPyun YongHyeon bus_addr_t vte_busaddr; 6442608aefcSPyun YongHyeon }; 6452608aefcSPyun YongHyeon 6462608aefcSPyun YongHyeon static void 6472608aefcSPyun YongHyeon vte_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 6482608aefcSPyun YongHyeon { 6492608aefcSPyun YongHyeon struct vte_dmamap_arg *ctx; 6502608aefcSPyun YongHyeon 6512608aefcSPyun YongHyeon if (error != 0) 6522608aefcSPyun YongHyeon return; 6532608aefcSPyun YongHyeon 6542608aefcSPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 6552608aefcSPyun YongHyeon 6562608aefcSPyun YongHyeon ctx = (struct vte_dmamap_arg *)arg; 6572608aefcSPyun YongHyeon ctx->vte_busaddr = segs[0].ds_addr; 6582608aefcSPyun YongHyeon } 6592608aefcSPyun YongHyeon 6602608aefcSPyun YongHyeon static int 6612608aefcSPyun YongHyeon vte_dma_alloc(struct vte_softc *sc) 6622608aefcSPyun YongHyeon { 6632608aefcSPyun YongHyeon struct vte_txdesc *txd; 6642608aefcSPyun YongHyeon struct vte_rxdesc *rxd; 6652608aefcSPyun YongHyeon struct vte_dmamap_arg ctx; 6662608aefcSPyun YongHyeon int error, i; 6672608aefcSPyun YongHyeon 6682608aefcSPyun YongHyeon /* Create parent DMA tag. */ 6692608aefcSPyun YongHyeon error = bus_dma_tag_create( 6702608aefcSPyun YongHyeon bus_get_dma_tag(sc->vte_dev), /* parent */ 6712608aefcSPyun YongHyeon 1, 0, /* alignment, boundary */ 6722608aefcSPyun YongHyeon BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 6732608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 6742608aefcSPyun YongHyeon NULL, NULL, /* filter, filterarg */ 6752608aefcSPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 6762608aefcSPyun YongHyeon 0, /* nsegments */ 6772608aefcSPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 6782608aefcSPyun YongHyeon 0, /* flags */ 6792608aefcSPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 6802608aefcSPyun YongHyeon &sc->vte_cdata.vte_parent_tag); 6812608aefcSPyun YongHyeon if (error != 0) { 6822608aefcSPyun YongHyeon device_printf(sc->vte_dev, 6832608aefcSPyun YongHyeon "could not create parent DMA tag.\n"); 6842608aefcSPyun YongHyeon goto fail; 6852608aefcSPyun YongHyeon } 6862608aefcSPyun YongHyeon 6872608aefcSPyun YongHyeon /* Create DMA tag for TX descriptor ring. */ 6882608aefcSPyun YongHyeon error = bus_dma_tag_create( 6892608aefcSPyun YongHyeon sc->vte_cdata.vte_parent_tag, /* parent */ 6902608aefcSPyun YongHyeon VTE_TX_RING_ALIGN, 0, /* alignment, boundary */ 6912608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 6922608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 6932608aefcSPyun YongHyeon NULL, NULL, /* filter, filterarg */ 6942608aefcSPyun YongHyeon VTE_TX_RING_SZ, /* maxsize */ 6952608aefcSPyun YongHyeon 1, /* nsegments */ 6962608aefcSPyun YongHyeon VTE_TX_RING_SZ, /* maxsegsize */ 6972608aefcSPyun YongHyeon 0, /* flags */ 6982608aefcSPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 6992608aefcSPyun YongHyeon &sc->vte_cdata.vte_tx_ring_tag); 7002608aefcSPyun YongHyeon if (error != 0) { 7012608aefcSPyun YongHyeon device_printf(sc->vte_dev, 7022608aefcSPyun YongHyeon "could not create TX ring DMA tag.\n"); 7032608aefcSPyun YongHyeon goto fail; 7042608aefcSPyun YongHyeon } 7052608aefcSPyun YongHyeon 7062608aefcSPyun YongHyeon /* Create DMA tag for RX free descriptor ring. */ 7072608aefcSPyun YongHyeon error = bus_dma_tag_create( 7082608aefcSPyun YongHyeon sc->vte_cdata.vte_parent_tag, /* parent */ 7092608aefcSPyun YongHyeon VTE_RX_RING_ALIGN, 0, /* alignment, boundary */ 7102608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 7112608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 7122608aefcSPyun YongHyeon NULL, NULL, /* filter, filterarg */ 7132608aefcSPyun YongHyeon VTE_RX_RING_SZ, /* maxsize */ 7142608aefcSPyun YongHyeon 1, /* nsegments */ 7152608aefcSPyun YongHyeon VTE_RX_RING_SZ, /* maxsegsize */ 7162608aefcSPyun YongHyeon 0, /* flags */ 7172608aefcSPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 7182608aefcSPyun YongHyeon &sc->vte_cdata.vte_rx_ring_tag); 7192608aefcSPyun YongHyeon if (error != 0) { 7202608aefcSPyun YongHyeon device_printf(sc->vte_dev, 7212608aefcSPyun YongHyeon "could not create RX ring DMA tag.\n"); 7222608aefcSPyun YongHyeon goto fail; 7232608aefcSPyun YongHyeon } 7242608aefcSPyun YongHyeon 7252608aefcSPyun YongHyeon /* Allocate DMA'able memory and load the DMA map for TX ring. */ 7262608aefcSPyun YongHyeon error = bus_dmamem_alloc(sc->vte_cdata.vte_tx_ring_tag, 7272608aefcSPyun YongHyeon (void **)&sc->vte_cdata.vte_tx_ring, 7282608aefcSPyun YongHyeon BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, 7292608aefcSPyun YongHyeon &sc->vte_cdata.vte_tx_ring_map); 7302608aefcSPyun YongHyeon if (error != 0) { 7312608aefcSPyun YongHyeon device_printf(sc->vte_dev, 7322608aefcSPyun YongHyeon "could not allocate DMA'able memory for TX ring.\n"); 7332608aefcSPyun YongHyeon goto fail; 7342608aefcSPyun YongHyeon } 7352608aefcSPyun YongHyeon ctx.vte_busaddr = 0; 7362608aefcSPyun YongHyeon error = bus_dmamap_load(sc->vte_cdata.vte_tx_ring_tag, 7372608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_map, sc->vte_cdata.vte_tx_ring, 7382608aefcSPyun YongHyeon VTE_TX_RING_SZ, vte_dmamap_cb, &ctx, 0); 7392608aefcSPyun YongHyeon if (error != 0 || ctx.vte_busaddr == 0) { 7402608aefcSPyun YongHyeon device_printf(sc->vte_dev, 7412608aefcSPyun YongHyeon "could not load DMA'able memory for TX ring.\n"); 7422608aefcSPyun YongHyeon goto fail; 7432608aefcSPyun YongHyeon } 7442608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_paddr = ctx.vte_busaddr; 7452608aefcSPyun YongHyeon 7462608aefcSPyun YongHyeon /* Allocate DMA'able memory and load the DMA map for RX ring. */ 7472608aefcSPyun YongHyeon error = bus_dmamem_alloc(sc->vte_cdata.vte_rx_ring_tag, 7482608aefcSPyun YongHyeon (void **)&sc->vte_cdata.vte_rx_ring, 7492608aefcSPyun YongHyeon BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, 7502608aefcSPyun YongHyeon &sc->vte_cdata.vte_rx_ring_map); 7512608aefcSPyun YongHyeon if (error != 0) { 7522608aefcSPyun YongHyeon device_printf(sc->vte_dev, 7532608aefcSPyun YongHyeon "could not allocate DMA'able memory for RX ring.\n"); 7542608aefcSPyun YongHyeon goto fail; 7552608aefcSPyun YongHyeon } 7562608aefcSPyun YongHyeon ctx.vte_busaddr = 0; 7572608aefcSPyun YongHyeon error = bus_dmamap_load(sc->vte_cdata.vte_rx_ring_tag, 7582608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_map, sc->vte_cdata.vte_rx_ring, 7592608aefcSPyun YongHyeon VTE_RX_RING_SZ, vte_dmamap_cb, &ctx, 0); 7602608aefcSPyun YongHyeon if (error != 0 || ctx.vte_busaddr == 0) { 7612608aefcSPyun YongHyeon device_printf(sc->vte_dev, 7622608aefcSPyun YongHyeon "could not load DMA'able memory for RX ring.\n"); 7632608aefcSPyun YongHyeon goto fail; 7642608aefcSPyun YongHyeon } 7652608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_paddr = ctx.vte_busaddr; 7662608aefcSPyun YongHyeon 7672608aefcSPyun YongHyeon /* Create TX buffer parent tag. */ 7682608aefcSPyun YongHyeon error = bus_dma_tag_create( 7692608aefcSPyun YongHyeon bus_get_dma_tag(sc->vte_dev), /* parent */ 7702608aefcSPyun YongHyeon 1, 0, /* alignment, boundary */ 7712608aefcSPyun YongHyeon BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 7722608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 7732608aefcSPyun YongHyeon NULL, NULL, /* filter, filterarg */ 7742608aefcSPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 7752608aefcSPyun YongHyeon 0, /* nsegments */ 7762608aefcSPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 7772608aefcSPyun YongHyeon 0, /* flags */ 7782608aefcSPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 7792608aefcSPyun YongHyeon &sc->vte_cdata.vte_buffer_tag); 7802608aefcSPyun YongHyeon if (error != 0) { 7812608aefcSPyun YongHyeon device_printf(sc->vte_dev, 7822608aefcSPyun YongHyeon "could not create parent buffer DMA tag.\n"); 7832608aefcSPyun YongHyeon goto fail; 7842608aefcSPyun YongHyeon } 7852608aefcSPyun YongHyeon 7862608aefcSPyun YongHyeon /* Create DMA tag for TX buffers. */ 7872608aefcSPyun YongHyeon error = bus_dma_tag_create( 7882608aefcSPyun YongHyeon sc->vte_cdata.vte_buffer_tag, /* parent */ 7892608aefcSPyun YongHyeon 1, 0, /* alignment, boundary */ 7902608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 7912608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 7922608aefcSPyun YongHyeon NULL, NULL, /* filter, filterarg */ 7932608aefcSPyun YongHyeon MCLBYTES, /* maxsize */ 7942608aefcSPyun YongHyeon 1, /* nsegments */ 7952608aefcSPyun YongHyeon MCLBYTES, /* maxsegsize */ 7962608aefcSPyun YongHyeon 0, /* flags */ 7972608aefcSPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 7982608aefcSPyun YongHyeon &sc->vte_cdata.vte_tx_tag); 7992608aefcSPyun YongHyeon if (error != 0) { 8002608aefcSPyun YongHyeon device_printf(sc->vte_dev, "could not create TX DMA tag.\n"); 8012608aefcSPyun YongHyeon goto fail; 8022608aefcSPyun YongHyeon } 8032608aefcSPyun YongHyeon 8042608aefcSPyun YongHyeon /* Create DMA tag for RX buffers. */ 8052608aefcSPyun YongHyeon error = bus_dma_tag_create( 8062608aefcSPyun YongHyeon sc->vte_cdata.vte_buffer_tag, /* parent */ 8072608aefcSPyun YongHyeon VTE_RX_BUF_ALIGN, 0, /* alignment, boundary */ 8082608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 8092608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 8102608aefcSPyun YongHyeon NULL, NULL, /* filter, filterarg */ 8112608aefcSPyun YongHyeon MCLBYTES, /* maxsize */ 8122608aefcSPyun YongHyeon 1, /* nsegments */ 8132608aefcSPyun YongHyeon MCLBYTES, /* maxsegsize */ 8142608aefcSPyun YongHyeon 0, /* flags */ 8152608aefcSPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 8162608aefcSPyun YongHyeon &sc->vte_cdata.vte_rx_tag); 8172608aefcSPyun YongHyeon if (error != 0) { 8182608aefcSPyun YongHyeon device_printf(sc->vte_dev, "could not create RX DMA tag.\n"); 8192608aefcSPyun YongHyeon goto fail; 8202608aefcSPyun YongHyeon } 8212608aefcSPyun YongHyeon /* Create DMA maps for TX buffers. */ 8222608aefcSPyun YongHyeon for (i = 0; i < VTE_TX_RING_CNT; i++) { 8232608aefcSPyun YongHyeon txd = &sc->vte_cdata.vte_txdesc[i]; 8242608aefcSPyun YongHyeon txd->tx_m = NULL; 8252608aefcSPyun YongHyeon txd->tx_dmamap = NULL; 8262608aefcSPyun YongHyeon error = bus_dmamap_create(sc->vte_cdata.vte_tx_tag, 0, 8272608aefcSPyun YongHyeon &txd->tx_dmamap); 8282608aefcSPyun YongHyeon if (error != 0) { 8292608aefcSPyun YongHyeon device_printf(sc->vte_dev, 8302608aefcSPyun YongHyeon "could not create TX dmamap.\n"); 8312608aefcSPyun YongHyeon goto fail; 8322608aefcSPyun YongHyeon } 8332608aefcSPyun YongHyeon } 8342608aefcSPyun YongHyeon /* Create DMA maps for RX buffers. */ 8352608aefcSPyun YongHyeon if ((error = bus_dmamap_create(sc->vte_cdata.vte_rx_tag, 0, 8362608aefcSPyun YongHyeon &sc->vte_cdata.vte_rx_sparemap)) != 0) { 8372608aefcSPyun YongHyeon device_printf(sc->vte_dev, 8382608aefcSPyun YongHyeon "could not create spare RX dmamap.\n"); 8392608aefcSPyun YongHyeon goto fail; 8402608aefcSPyun YongHyeon } 8412608aefcSPyun YongHyeon for (i = 0; i < VTE_RX_RING_CNT; i++) { 8422608aefcSPyun YongHyeon rxd = &sc->vte_cdata.vte_rxdesc[i]; 8432608aefcSPyun YongHyeon rxd->rx_m = NULL; 8442608aefcSPyun YongHyeon rxd->rx_dmamap = NULL; 8452608aefcSPyun YongHyeon error = bus_dmamap_create(sc->vte_cdata.vte_rx_tag, 0, 8462608aefcSPyun YongHyeon &rxd->rx_dmamap); 8472608aefcSPyun YongHyeon if (error != 0) { 8482608aefcSPyun YongHyeon device_printf(sc->vte_dev, 8492608aefcSPyun YongHyeon "could not create RX dmamap.\n"); 8502608aefcSPyun YongHyeon goto fail; 8512608aefcSPyun YongHyeon } 8522608aefcSPyun YongHyeon } 8532608aefcSPyun YongHyeon 8542608aefcSPyun YongHyeon fail: 8552608aefcSPyun YongHyeon return (error); 8562608aefcSPyun YongHyeon } 8572608aefcSPyun YongHyeon 8582608aefcSPyun YongHyeon static void 8592608aefcSPyun YongHyeon vte_dma_free(struct vte_softc *sc) 8602608aefcSPyun YongHyeon { 8612608aefcSPyun YongHyeon struct vte_txdesc *txd; 8622608aefcSPyun YongHyeon struct vte_rxdesc *rxd; 8632608aefcSPyun YongHyeon int i; 8642608aefcSPyun YongHyeon 8652608aefcSPyun YongHyeon /* TX buffers. */ 8662608aefcSPyun YongHyeon if (sc->vte_cdata.vte_tx_tag != NULL) { 8672608aefcSPyun YongHyeon for (i = 0; i < VTE_TX_RING_CNT; i++) { 8682608aefcSPyun YongHyeon txd = &sc->vte_cdata.vte_txdesc[i]; 8692608aefcSPyun YongHyeon if (txd->tx_dmamap != NULL) { 8702608aefcSPyun YongHyeon bus_dmamap_destroy(sc->vte_cdata.vte_tx_tag, 8712608aefcSPyun YongHyeon txd->tx_dmamap); 8722608aefcSPyun YongHyeon txd->tx_dmamap = NULL; 8732608aefcSPyun YongHyeon } 8742608aefcSPyun YongHyeon } 8752608aefcSPyun YongHyeon bus_dma_tag_destroy(sc->vte_cdata.vte_tx_tag); 8762608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_tag = NULL; 8772608aefcSPyun YongHyeon } 8782608aefcSPyun YongHyeon /* RX buffers */ 8792608aefcSPyun YongHyeon if (sc->vte_cdata.vte_rx_tag != NULL) { 8802608aefcSPyun YongHyeon for (i = 0; i < VTE_RX_RING_CNT; i++) { 8812608aefcSPyun YongHyeon rxd = &sc->vte_cdata.vte_rxdesc[i]; 8822608aefcSPyun YongHyeon if (rxd->rx_dmamap != NULL) { 8832608aefcSPyun YongHyeon bus_dmamap_destroy(sc->vte_cdata.vte_rx_tag, 8842608aefcSPyun YongHyeon rxd->rx_dmamap); 8852608aefcSPyun YongHyeon rxd->rx_dmamap = NULL; 8862608aefcSPyun YongHyeon } 8872608aefcSPyun YongHyeon } 8882608aefcSPyun YongHyeon if (sc->vte_cdata.vte_rx_sparemap != NULL) { 8892608aefcSPyun YongHyeon bus_dmamap_destroy(sc->vte_cdata.vte_rx_tag, 8902608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_sparemap); 8912608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_sparemap = NULL; 8922608aefcSPyun YongHyeon } 8932608aefcSPyun YongHyeon bus_dma_tag_destroy(sc->vte_cdata.vte_rx_tag); 8942608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_tag = NULL; 8952608aefcSPyun YongHyeon } 8962608aefcSPyun YongHyeon /* TX descriptor ring. */ 8972608aefcSPyun YongHyeon if (sc->vte_cdata.vte_tx_ring_tag != NULL) { 898068d8643SJohn Baldwin if (sc->vte_cdata.vte_tx_ring_paddr != 0) 8992608aefcSPyun YongHyeon bus_dmamap_unload(sc->vte_cdata.vte_tx_ring_tag, 9002608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_map); 901068d8643SJohn Baldwin if (sc->vte_cdata.vte_tx_ring != NULL) 9022608aefcSPyun YongHyeon bus_dmamem_free(sc->vte_cdata.vte_tx_ring_tag, 9032608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring, 9042608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_map); 9052608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring = NULL; 906068d8643SJohn Baldwin sc->vte_cdata.vte_tx_ring_paddr = 0; 9072608aefcSPyun YongHyeon bus_dma_tag_destroy(sc->vte_cdata.vte_tx_ring_tag); 9082608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_tag = NULL; 9092608aefcSPyun YongHyeon } 9102608aefcSPyun YongHyeon /* RX ring. */ 9112608aefcSPyun YongHyeon if (sc->vte_cdata.vte_rx_ring_tag != NULL) { 912068d8643SJohn Baldwin if (sc->vte_cdata.vte_rx_ring_paddr != 0) 9132608aefcSPyun YongHyeon bus_dmamap_unload(sc->vte_cdata.vte_rx_ring_tag, 9142608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_map); 915068d8643SJohn Baldwin if (sc->vte_cdata.vte_rx_ring != NULL) 9162608aefcSPyun YongHyeon bus_dmamem_free(sc->vte_cdata.vte_rx_ring_tag, 9172608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring, 9182608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_map); 9192608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring = NULL; 920068d8643SJohn Baldwin sc->vte_cdata.vte_rx_ring_paddr = 0; 9212608aefcSPyun YongHyeon bus_dma_tag_destroy(sc->vte_cdata.vte_rx_ring_tag); 9222608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_tag = NULL; 9232608aefcSPyun YongHyeon } 9242608aefcSPyun YongHyeon if (sc->vte_cdata.vte_buffer_tag != NULL) { 9252608aefcSPyun YongHyeon bus_dma_tag_destroy(sc->vte_cdata.vte_buffer_tag); 9262608aefcSPyun YongHyeon sc->vte_cdata.vte_buffer_tag = NULL; 9272608aefcSPyun YongHyeon } 9282608aefcSPyun YongHyeon if (sc->vte_cdata.vte_parent_tag != NULL) { 9292608aefcSPyun YongHyeon bus_dma_tag_destroy(sc->vte_cdata.vte_parent_tag); 9302608aefcSPyun YongHyeon sc->vte_cdata.vte_parent_tag = NULL; 9312608aefcSPyun YongHyeon } 9322608aefcSPyun YongHyeon } 9332608aefcSPyun YongHyeon 9342608aefcSPyun YongHyeon static int 9352608aefcSPyun YongHyeon vte_shutdown(device_t dev) 9362608aefcSPyun YongHyeon { 9372608aefcSPyun YongHyeon 9382608aefcSPyun YongHyeon return (vte_suspend(dev)); 9392608aefcSPyun YongHyeon } 9402608aefcSPyun YongHyeon 9412608aefcSPyun YongHyeon static int 9422608aefcSPyun YongHyeon vte_suspend(device_t dev) 9432608aefcSPyun YongHyeon { 9442608aefcSPyun YongHyeon struct vte_softc *sc; 9453486b835SJustin Hibbits if_t ifp; 9462608aefcSPyun YongHyeon 9472608aefcSPyun YongHyeon sc = device_get_softc(dev); 9482608aefcSPyun YongHyeon 9492608aefcSPyun YongHyeon VTE_LOCK(sc); 9502608aefcSPyun YongHyeon ifp = sc->vte_ifp; 9513486b835SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) 9522608aefcSPyun YongHyeon vte_stop(sc); 9532608aefcSPyun YongHyeon VTE_UNLOCK(sc); 9542608aefcSPyun YongHyeon 9552608aefcSPyun YongHyeon return (0); 9562608aefcSPyun YongHyeon } 9572608aefcSPyun YongHyeon 9582608aefcSPyun YongHyeon static int 9592608aefcSPyun YongHyeon vte_resume(device_t dev) 9602608aefcSPyun YongHyeon { 9612608aefcSPyun YongHyeon struct vte_softc *sc; 9623486b835SJustin Hibbits if_t ifp; 9632608aefcSPyun YongHyeon 9642608aefcSPyun YongHyeon sc = device_get_softc(dev); 9652608aefcSPyun YongHyeon 9662608aefcSPyun YongHyeon VTE_LOCK(sc); 9672608aefcSPyun YongHyeon ifp = sc->vte_ifp; 9683486b835SJustin Hibbits if ((if_getflags(ifp) & IFF_UP) != 0) { 9693486b835SJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING); 9702608aefcSPyun YongHyeon vte_init_locked(sc); 9712608aefcSPyun YongHyeon } 9722608aefcSPyun YongHyeon VTE_UNLOCK(sc); 9732608aefcSPyun YongHyeon 9742608aefcSPyun YongHyeon return (0); 9752608aefcSPyun YongHyeon } 9762608aefcSPyun YongHyeon 9772608aefcSPyun YongHyeon static struct vte_txdesc * 9782608aefcSPyun YongHyeon vte_encap(struct vte_softc *sc, struct mbuf **m_head) 9792608aefcSPyun YongHyeon { 9802608aefcSPyun YongHyeon struct vte_txdesc *txd; 9812608aefcSPyun YongHyeon struct mbuf *m, *n; 9822608aefcSPyun YongHyeon bus_dma_segment_t txsegs[1]; 9832608aefcSPyun YongHyeon int copy, error, nsegs, padlen; 9842608aefcSPyun YongHyeon 9852608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 9862608aefcSPyun YongHyeon 9872608aefcSPyun YongHyeon M_ASSERTPKTHDR((*m_head)); 9882608aefcSPyun YongHyeon 9892608aefcSPyun YongHyeon txd = &sc->vte_cdata.vte_txdesc[sc->vte_cdata.vte_tx_prod]; 9902608aefcSPyun YongHyeon m = *m_head; 9912608aefcSPyun YongHyeon /* 9922608aefcSPyun YongHyeon * Controller doesn't auto-pad, so we have to make sure pad 9932608aefcSPyun YongHyeon * short frames out to the minimum frame length. 9942608aefcSPyun YongHyeon */ 9952608aefcSPyun YongHyeon if (m->m_pkthdr.len < VTE_MIN_FRAMELEN) 9962608aefcSPyun YongHyeon padlen = VTE_MIN_FRAMELEN - m->m_pkthdr.len; 9972608aefcSPyun YongHyeon else 9982608aefcSPyun YongHyeon padlen = 0; 9992608aefcSPyun YongHyeon 10002608aefcSPyun YongHyeon /* 10012608aefcSPyun YongHyeon * Controller does not support multi-fragmented TX buffers. 10022608aefcSPyun YongHyeon * Controller spends most of its TX processing time in 10032608aefcSPyun YongHyeon * de-fragmenting TX buffers. Either faster CPU or more 10042608aefcSPyun YongHyeon * advanced controller DMA engine is required to speed up 10052608aefcSPyun YongHyeon * TX path processing. 10062608aefcSPyun YongHyeon * To mitigate the de-fragmenting issue, perform deep copy 10072608aefcSPyun YongHyeon * from fragmented mbuf chains to a pre-allocated mbuf 10082608aefcSPyun YongHyeon * cluster with extra cost of kernel memory. For frames 10092608aefcSPyun YongHyeon * that is composed of single TX buffer, the deep copy is 10102608aefcSPyun YongHyeon * bypassed. 10112608aefcSPyun YongHyeon */ 10122608aefcSPyun YongHyeon if (tx_deep_copy != 0) { 10132608aefcSPyun YongHyeon copy = 0; 10142608aefcSPyun YongHyeon if (m->m_next != NULL) 10152608aefcSPyun YongHyeon copy++; 10162608aefcSPyun YongHyeon if (padlen > 0 && (M_WRITABLE(m) == 0 || 10172608aefcSPyun YongHyeon padlen > M_TRAILINGSPACE(m))) 10182608aefcSPyun YongHyeon copy++; 10192608aefcSPyun YongHyeon if (copy != 0) { 10202608aefcSPyun YongHyeon /* Avoid expensive m_defrag(9) and do deep copy. */ 10212608aefcSPyun YongHyeon n = sc->vte_cdata.vte_txmbufs[sc->vte_cdata.vte_tx_prod]; 10222608aefcSPyun YongHyeon m_copydata(m, 0, m->m_pkthdr.len, mtod(n, char *)); 10232608aefcSPyun YongHyeon n->m_pkthdr.len = m->m_pkthdr.len; 10242608aefcSPyun YongHyeon n->m_len = m->m_pkthdr.len; 10252608aefcSPyun YongHyeon m = n; 10262608aefcSPyun YongHyeon txd->tx_flags |= VTE_TXMBUF; 10272608aefcSPyun YongHyeon } 10282608aefcSPyun YongHyeon 10292608aefcSPyun YongHyeon if (padlen > 0) { 10302608aefcSPyun YongHyeon /* Zero out the bytes in the pad area. */ 10312608aefcSPyun YongHyeon bzero(mtod(m, char *) + m->m_pkthdr.len, padlen); 10322608aefcSPyun YongHyeon m->m_pkthdr.len += padlen; 10332608aefcSPyun YongHyeon m->m_len = m->m_pkthdr.len; 10342608aefcSPyun YongHyeon } 10352608aefcSPyun YongHyeon } else { 10362608aefcSPyun YongHyeon if (M_WRITABLE(m) == 0) { 10372608aefcSPyun YongHyeon if (m->m_next != NULL || padlen > 0) { 10382608aefcSPyun YongHyeon /* Get a writable copy. */ 1039c6499eccSGleb Smirnoff m = m_dup(*m_head, M_NOWAIT); 10402608aefcSPyun YongHyeon /* Release original mbuf chains. */ 10412608aefcSPyun YongHyeon m_freem(*m_head); 10422608aefcSPyun YongHyeon if (m == NULL) { 10432608aefcSPyun YongHyeon *m_head = NULL; 10442608aefcSPyun YongHyeon return (NULL); 10452608aefcSPyun YongHyeon } 10462608aefcSPyun YongHyeon *m_head = m; 10472608aefcSPyun YongHyeon } 10482608aefcSPyun YongHyeon } 10492608aefcSPyun YongHyeon 10502608aefcSPyun YongHyeon if (m->m_next != NULL) { 1051c6499eccSGleb Smirnoff m = m_defrag(*m_head, M_NOWAIT); 10522608aefcSPyun YongHyeon if (m == NULL) { 10532608aefcSPyun YongHyeon m_freem(*m_head); 10542608aefcSPyun YongHyeon *m_head = NULL; 10552608aefcSPyun YongHyeon return (NULL); 10562608aefcSPyun YongHyeon } 10572608aefcSPyun YongHyeon *m_head = m; 10582608aefcSPyun YongHyeon } 10592608aefcSPyun YongHyeon 10602608aefcSPyun YongHyeon if (padlen > 0) { 10612608aefcSPyun YongHyeon if (M_TRAILINGSPACE(m) < padlen) { 1062c6499eccSGleb Smirnoff m = m_defrag(*m_head, M_NOWAIT); 10632608aefcSPyun YongHyeon if (m == NULL) { 10642608aefcSPyun YongHyeon m_freem(*m_head); 10652608aefcSPyun YongHyeon *m_head = NULL; 10662608aefcSPyun YongHyeon return (NULL); 10672608aefcSPyun YongHyeon } 10682608aefcSPyun YongHyeon *m_head = m; 10692608aefcSPyun YongHyeon } 10702608aefcSPyun YongHyeon /* Zero out the bytes in the pad area. */ 10712608aefcSPyun YongHyeon bzero(mtod(m, char *) + m->m_pkthdr.len, padlen); 10722608aefcSPyun YongHyeon m->m_pkthdr.len += padlen; 10732608aefcSPyun YongHyeon m->m_len = m->m_pkthdr.len; 10742608aefcSPyun YongHyeon } 10752608aefcSPyun YongHyeon } 10762608aefcSPyun YongHyeon 10772608aefcSPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->vte_cdata.vte_tx_tag, 10782608aefcSPyun YongHyeon txd->tx_dmamap, m, txsegs, &nsegs, 0); 10792608aefcSPyun YongHyeon if (error != 0) { 10802608aefcSPyun YongHyeon txd->tx_flags &= ~VTE_TXMBUF; 10812608aefcSPyun YongHyeon return (NULL); 10822608aefcSPyun YongHyeon } 10832608aefcSPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 10842608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_tx_tag, txd->tx_dmamap, 10852608aefcSPyun YongHyeon BUS_DMASYNC_PREWRITE); 10862608aefcSPyun YongHyeon 10872608aefcSPyun YongHyeon txd->tx_desc->dtlen = htole16(VTE_TX_LEN(txsegs[0].ds_len)); 10882608aefcSPyun YongHyeon txd->tx_desc->dtbp = htole32(txsegs[0].ds_addr); 10892608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_cnt++; 10902608aefcSPyun YongHyeon /* Update producer index. */ 10912608aefcSPyun YongHyeon VTE_DESC_INC(sc->vte_cdata.vte_tx_prod, VTE_TX_RING_CNT); 10922608aefcSPyun YongHyeon 10932608aefcSPyun YongHyeon /* Finally hand over ownership to controller. */ 10942608aefcSPyun YongHyeon txd->tx_desc->dtst = htole16(VTE_DTST_TX_OWN); 10952608aefcSPyun YongHyeon txd->tx_m = m; 10962608aefcSPyun YongHyeon 10972608aefcSPyun YongHyeon return (txd); 10982608aefcSPyun YongHyeon } 10992608aefcSPyun YongHyeon 11002608aefcSPyun YongHyeon static void 11013486b835SJustin Hibbits vte_start(if_t ifp) 11022608aefcSPyun YongHyeon { 11032608aefcSPyun YongHyeon struct vte_softc *sc; 11042608aefcSPyun YongHyeon 11053486b835SJustin Hibbits sc = if_getsoftc(ifp); 11062608aefcSPyun YongHyeon VTE_LOCK(sc); 11072608aefcSPyun YongHyeon vte_start_locked(sc); 11082608aefcSPyun YongHyeon VTE_UNLOCK(sc); 11092608aefcSPyun YongHyeon } 11102608aefcSPyun YongHyeon 11112608aefcSPyun YongHyeon static void 11122608aefcSPyun YongHyeon vte_start_locked(struct vte_softc *sc) 11132608aefcSPyun YongHyeon { 11143486b835SJustin Hibbits if_t ifp; 11152608aefcSPyun YongHyeon struct vte_txdesc *txd; 11162608aefcSPyun YongHyeon struct mbuf *m_head; 11172608aefcSPyun YongHyeon int enq; 11182608aefcSPyun YongHyeon 11192608aefcSPyun YongHyeon ifp = sc->vte_ifp; 11202608aefcSPyun YongHyeon 11213486b835SJustin Hibbits if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 11222608aefcSPyun YongHyeon IFF_DRV_RUNNING || (sc->vte_flags & VTE_FLAG_LINK) == 0) 11232608aefcSPyun YongHyeon return; 11242608aefcSPyun YongHyeon 11253486b835SJustin Hibbits for (enq = 0; !if_sendq_empty(ifp); ) { 11262608aefcSPyun YongHyeon /* Reserve one free TX descriptor. */ 11272608aefcSPyun YongHyeon if (sc->vte_cdata.vte_tx_cnt >= VTE_TX_RING_CNT - 1) { 11283486b835SJustin Hibbits if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0); 11292608aefcSPyun YongHyeon break; 11302608aefcSPyun YongHyeon } 11313486b835SJustin Hibbits m_head = if_dequeue(ifp); 11322608aefcSPyun YongHyeon if (m_head == NULL) 11332608aefcSPyun YongHyeon break; 11342608aefcSPyun YongHyeon /* 11352608aefcSPyun YongHyeon * Pack the data into the transmit ring. If we 11362608aefcSPyun YongHyeon * don't have room, set the OACTIVE flag and wait 11372608aefcSPyun YongHyeon * for the NIC to drain the ring. 11382608aefcSPyun YongHyeon */ 11392608aefcSPyun YongHyeon if ((txd = vte_encap(sc, &m_head)) == NULL) { 11402608aefcSPyun YongHyeon if (m_head != NULL) 11413486b835SJustin Hibbits if_sendq_prepend(ifp, m_head); 11422608aefcSPyun YongHyeon break; 11432608aefcSPyun YongHyeon } 11442608aefcSPyun YongHyeon 11452608aefcSPyun YongHyeon enq++; 11462608aefcSPyun YongHyeon /* 11472608aefcSPyun YongHyeon * If there's a BPF listener, bounce a copy of this frame 11482608aefcSPyun YongHyeon * to him. 11492608aefcSPyun YongHyeon */ 11502608aefcSPyun YongHyeon ETHER_BPF_MTAP(ifp, m_head); 11512608aefcSPyun YongHyeon /* Free consumed TX frame. */ 11522608aefcSPyun YongHyeon if ((txd->tx_flags & VTE_TXMBUF) != 0) 11532608aefcSPyun YongHyeon m_freem(m_head); 11542608aefcSPyun YongHyeon } 11552608aefcSPyun YongHyeon 11562608aefcSPyun YongHyeon if (enq > 0) { 11572608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_tx_ring_tag, 11582608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_map, BUS_DMASYNC_PREREAD | 11592608aefcSPyun YongHyeon BUS_DMASYNC_PREWRITE); 11602608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_TX_POLL, TX_POLL_START); 11612608aefcSPyun YongHyeon sc->vte_watchdog_timer = VTE_TX_TIMEOUT; 11622608aefcSPyun YongHyeon } 11632608aefcSPyun YongHyeon } 11642608aefcSPyun YongHyeon 11652608aefcSPyun YongHyeon static void 11662608aefcSPyun YongHyeon vte_watchdog(struct vte_softc *sc) 11672608aefcSPyun YongHyeon { 11683486b835SJustin Hibbits if_t ifp; 11692608aefcSPyun YongHyeon 11702608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 11712608aefcSPyun YongHyeon 11722608aefcSPyun YongHyeon if (sc->vte_watchdog_timer == 0 || --sc->vte_watchdog_timer) 11732608aefcSPyun YongHyeon return; 11742608aefcSPyun YongHyeon 11752608aefcSPyun YongHyeon ifp = sc->vte_ifp; 11762608aefcSPyun YongHyeon if_printf(sc->vte_ifp, "watchdog timeout -- resetting\n"); 117724b83dc6SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 11783486b835SJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING); 11792608aefcSPyun YongHyeon vte_init_locked(sc); 11803486b835SJustin Hibbits if (!if_sendq_empty(ifp)) 11812608aefcSPyun YongHyeon vte_start_locked(sc); 11822608aefcSPyun YongHyeon } 11832608aefcSPyun YongHyeon 11842608aefcSPyun YongHyeon static int 11853486b835SJustin Hibbits vte_ioctl(if_t ifp, u_long cmd, caddr_t data) 11862608aefcSPyun YongHyeon { 11872608aefcSPyun YongHyeon struct vte_softc *sc; 11882608aefcSPyun YongHyeon struct ifreq *ifr; 11892608aefcSPyun YongHyeon struct mii_data *mii; 11902608aefcSPyun YongHyeon int error; 11912608aefcSPyun YongHyeon 11923486b835SJustin Hibbits sc = if_getsoftc(ifp); 11932608aefcSPyun YongHyeon ifr = (struct ifreq *)data; 11942608aefcSPyun YongHyeon error = 0; 11952608aefcSPyun YongHyeon switch (cmd) { 11962608aefcSPyun YongHyeon case SIOCSIFFLAGS: 11972608aefcSPyun YongHyeon VTE_LOCK(sc); 11983486b835SJustin Hibbits if ((if_getflags(ifp) & IFF_UP) != 0) { 11993486b835SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0 && 12003486b835SJustin Hibbits ((if_getflags(ifp) ^ sc->vte_if_flags) & 12012608aefcSPyun YongHyeon (IFF_PROMISC | IFF_ALLMULTI)) != 0) 12022608aefcSPyun YongHyeon vte_rxfilter(sc); 12032608aefcSPyun YongHyeon else 12042608aefcSPyun YongHyeon vte_init_locked(sc); 12053486b835SJustin Hibbits } else if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) 12062608aefcSPyun YongHyeon vte_stop(sc); 12073486b835SJustin Hibbits sc->vte_if_flags = if_getflags(ifp); 12082608aefcSPyun YongHyeon VTE_UNLOCK(sc); 12092608aefcSPyun YongHyeon break; 12102608aefcSPyun YongHyeon case SIOCADDMULTI: 12112608aefcSPyun YongHyeon case SIOCDELMULTI: 12122608aefcSPyun YongHyeon VTE_LOCK(sc); 12133486b835SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) 12142608aefcSPyun YongHyeon vte_rxfilter(sc); 12152608aefcSPyun YongHyeon VTE_UNLOCK(sc); 12162608aefcSPyun YongHyeon break; 12172608aefcSPyun YongHyeon case SIOCSIFMEDIA: 12182608aefcSPyun YongHyeon case SIOCGIFMEDIA: 12192608aefcSPyun YongHyeon mii = device_get_softc(sc->vte_miibus); 12202608aefcSPyun YongHyeon error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 12212608aefcSPyun YongHyeon break; 12222608aefcSPyun YongHyeon default: 12232608aefcSPyun YongHyeon error = ether_ioctl(ifp, cmd, data); 12242608aefcSPyun YongHyeon break; 12252608aefcSPyun YongHyeon } 12262608aefcSPyun YongHyeon 12272608aefcSPyun YongHyeon return (error); 12282608aefcSPyun YongHyeon } 12292608aefcSPyun YongHyeon 12302608aefcSPyun YongHyeon static void 12312608aefcSPyun YongHyeon vte_mac_config(struct vte_softc *sc) 12322608aefcSPyun YongHyeon { 12332608aefcSPyun YongHyeon struct mii_data *mii; 12342608aefcSPyun YongHyeon uint16_t mcr; 12352608aefcSPyun YongHyeon 12362608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 12372608aefcSPyun YongHyeon 12382608aefcSPyun YongHyeon mii = device_get_softc(sc->vte_miibus); 12392608aefcSPyun YongHyeon mcr = CSR_READ_2(sc, VTE_MCR0); 12402608aefcSPyun YongHyeon mcr &= ~(MCR0_FC_ENB | MCR0_FULL_DUPLEX); 12412608aefcSPyun YongHyeon if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { 12422608aefcSPyun YongHyeon mcr |= MCR0_FULL_DUPLEX; 12432608aefcSPyun YongHyeon #ifdef notyet 12442608aefcSPyun YongHyeon if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) 12452608aefcSPyun YongHyeon mcr |= MCR0_FC_ENB; 12462608aefcSPyun YongHyeon /* 12472608aefcSPyun YongHyeon * The data sheet is not clear whether the controller 12482608aefcSPyun YongHyeon * honors received pause frames or not. The is no 12492608aefcSPyun YongHyeon * separate control bit for RX pause frame so just 12502608aefcSPyun YongHyeon * enable MCR0_FC_ENB bit. 12512608aefcSPyun YongHyeon */ 12522608aefcSPyun YongHyeon if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) 12532608aefcSPyun YongHyeon mcr |= MCR0_FC_ENB; 12542608aefcSPyun YongHyeon #endif 12552608aefcSPyun YongHyeon } 12562608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MCR0, mcr); 12572608aefcSPyun YongHyeon } 12582608aefcSPyun YongHyeon 12592608aefcSPyun YongHyeon static void 12602608aefcSPyun YongHyeon vte_stats_clear(struct vte_softc *sc) 12612608aefcSPyun YongHyeon { 12622608aefcSPyun YongHyeon 12632608aefcSPyun YongHyeon /* Reading counter registers clears its contents. */ 12642608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_RX_DONE); 12652608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_MECNT0); 12662608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_MECNT1); 12672608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_MECNT2); 12682608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_MECNT3); 12692608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_TX_DONE); 12702608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_MECNT4); 12712608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_PAUSE); 12722608aefcSPyun YongHyeon } 12732608aefcSPyun YongHyeon 12742608aefcSPyun YongHyeon static void 12752608aefcSPyun YongHyeon vte_stats_update(struct vte_softc *sc) 12762608aefcSPyun YongHyeon { 12772608aefcSPyun YongHyeon struct vte_hw_stats *stat; 12782608aefcSPyun YongHyeon uint16_t value; 12792608aefcSPyun YongHyeon 12802608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 12812608aefcSPyun YongHyeon 12822608aefcSPyun YongHyeon stat = &sc->vte_stats; 12832608aefcSPyun YongHyeon 12842608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_MECISR); 12852608aefcSPyun YongHyeon /* RX stats. */ 12862608aefcSPyun YongHyeon stat->rx_frames += CSR_READ_2(sc, VTE_CNT_RX_DONE); 12872608aefcSPyun YongHyeon value = CSR_READ_2(sc, VTE_CNT_MECNT0); 12882608aefcSPyun YongHyeon stat->rx_bcast_frames += (value >> 8); 12892608aefcSPyun YongHyeon stat->rx_mcast_frames += (value & 0xFF); 12902608aefcSPyun YongHyeon value = CSR_READ_2(sc, VTE_CNT_MECNT1); 12912608aefcSPyun YongHyeon stat->rx_runts += (value >> 8); 12922608aefcSPyun YongHyeon stat->rx_crcerrs += (value & 0xFF); 12932608aefcSPyun YongHyeon value = CSR_READ_2(sc, VTE_CNT_MECNT2); 12942608aefcSPyun YongHyeon stat->rx_long_frames += (value & 0xFF); 12952608aefcSPyun YongHyeon value = CSR_READ_2(sc, VTE_CNT_MECNT3); 12962608aefcSPyun YongHyeon stat->rx_fifo_full += (value >> 8); 12972608aefcSPyun YongHyeon stat->rx_desc_unavail += (value & 0xFF); 12982608aefcSPyun YongHyeon 12992608aefcSPyun YongHyeon /* TX stats. */ 13002608aefcSPyun YongHyeon stat->tx_frames += CSR_READ_2(sc, VTE_CNT_TX_DONE); 13012608aefcSPyun YongHyeon value = CSR_READ_2(sc, VTE_CNT_MECNT4); 13022608aefcSPyun YongHyeon stat->tx_underruns += (value >> 8); 13032608aefcSPyun YongHyeon stat->tx_late_colls += (value & 0xFF); 13042608aefcSPyun YongHyeon 13052608aefcSPyun YongHyeon value = CSR_READ_2(sc, VTE_CNT_PAUSE); 13062608aefcSPyun YongHyeon stat->tx_pause_frames += (value >> 8); 13072608aefcSPyun YongHyeon stat->rx_pause_frames += (value & 0xFF); 130824b83dc6SGleb Smirnoff } 13092608aefcSPyun YongHyeon 131024b83dc6SGleb Smirnoff static uint64_t 13113486b835SJustin Hibbits vte_get_counter(if_t ifp, ift_counter cnt) 131224b83dc6SGleb Smirnoff { 131324b83dc6SGleb Smirnoff struct vte_softc *sc; 131424b83dc6SGleb Smirnoff struct vte_hw_stats *stat; 131524b83dc6SGleb Smirnoff 131624b83dc6SGleb Smirnoff sc = if_getsoftc(ifp); 131724b83dc6SGleb Smirnoff stat = &sc->vte_stats; 131824b83dc6SGleb Smirnoff 131924b83dc6SGleb Smirnoff switch (cnt) { 132024b83dc6SGleb Smirnoff case IFCOUNTER_OPACKETS: 132124b83dc6SGleb Smirnoff return (stat->tx_frames); 132224b83dc6SGleb Smirnoff case IFCOUNTER_COLLISIONS: 132324b83dc6SGleb Smirnoff return (stat->tx_late_colls); 132424b83dc6SGleb Smirnoff case IFCOUNTER_OERRORS: 132524b83dc6SGleb Smirnoff return (stat->tx_late_colls + stat->tx_underruns); 132624b83dc6SGleb Smirnoff case IFCOUNTER_IPACKETS: 132724b83dc6SGleb Smirnoff return (stat->rx_frames); 132824b83dc6SGleb Smirnoff case IFCOUNTER_IERRORS: 132924b83dc6SGleb Smirnoff return (stat->rx_crcerrs + stat->rx_runts + 133024b83dc6SGleb Smirnoff stat->rx_long_frames + stat->rx_fifo_full); 133124b83dc6SGleb Smirnoff default: 133224b83dc6SGleb Smirnoff return (if_get_counter_default(ifp, cnt)); 133324b83dc6SGleb Smirnoff } 13342608aefcSPyun YongHyeon } 13352608aefcSPyun YongHyeon 13362608aefcSPyun YongHyeon static void 13372608aefcSPyun YongHyeon vte_intr(void *arg) 13382608aefcSPyun YongHyeon { 13392608aefcSPyun YongHyeon struct vte_softc *sc; 13403486b835SJustin Hibbits if_t ifp; 13412608aefcSPyun YongHyeon uint16_t status; 13422608aefcSPyun YongHyeon int n; 13432608aefcSPyun YongHyeon 13442608aefcSPyun YongHyeon sc = (struct vte_softc *)arg; 13452608aefcSPyun YongHyeon VTE_LOCK(sc); 13462608aefcSPyun YongHyeon 13472608aefcSPyun YongHyeon ifp = sc->vte_ifp; 13482608aefcSPyun YongHyeon /* Reading VTE_MISR acknowledges interrupts. */ 13492608aefcSPyun YongHyeon status = CSR_READ_2(sc, VTE_MISR); 13502608aefcSPyun YongHyeon if ((status & VTE_INTRS) == 0) { 13512608aefcSPyun YongHyeon /* Not ours. */ 13522608aefcSPyun YongHyeon VTE_UNLOCK(sc); 13532608aefcSPyun YongHyeon return; 13542608aefcSPyun YongHyeon } 13552608aefcSPyun YongHyeon 13562608aefcSPyun YongHyeon /* Disable interrupts. */ 13572608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MIER, 0); 13582608aefcSPyun YongHyeon for (n = 8; (status & VTE_INTRS) != 0;) { 13593486b835SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) 13602608aefcSPyun YongHyeon break; 13612608aefcSPyun YongHyeon if ((status & (MISR_RX_DONE | MISR_RX_DESC_UNAVAIL | 13622608aefcSPyun YongHyeon MISR_RX_FIFO_FULL)) != 0) 13632608aefcSPyun YongHyeon vte_rxeof(sc); 13642608aefcSPyun YongHyeon if ((status & MISR_TX_DONE) != 0) 13652608aefcSPyun YongHyeon vte_txeof(sc); 13662608aefcSPyun YongHyeon if ((status & MISR_EVENT_CNT_OFLOW) != 0) 13672608aefcSPyun YongHyeon vte_stats_update(sc); 13683486b835SJustin Hibbits if (!if_sendq_empty(ifp)) 13692608aefcSPyun YongHyeon vte_start_locked(sc); 13702608aefcSPyun YongHyeon if (--n > 0) 13712608aefcSPyun YongHyeon status = CSR_READ_2(sc, VTE_MISR); 13722608aefcSPyun YongHyeon else 13732608aefcSPyun YongHyeon break; 13742608aefcSPyun YongHyeon } 13752608aefcSPyun YongHyeon 13763486b835SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) { 13772608aefcSPyun YongHyeon /* Re-enable interrupts. */ 13782608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MIER, VTE_INTRS); 13792608aefcSPyun YongHyeon } 13802608aefcSPyun YongHyeon VTE_UNLOCK(sc); 13812608aefcSPyun YongHyeon } 13822608aefcSPyun YongHyeon 13832608aefcSPyun YongHyeon static void 13842608aefcSPyun YongHyeon vte_txeof(struct vte_softc *sc) 13852608aefcSPyun YongHyeon { 13863486b835SJustin Hibbits if_t ifp; 13872608aefcSPyun YongHyeon struct vte_txdesc *txd; 13882608aefcSPyun YongHyeon uint16_t status; 13892608aefcSPyun YongHyeon int cons, prog; 13902608aefcSPyun YongHyeon 13912608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 13922608aefcSPyun YongHyeon 13932608aefcSPyun YongHyeon ifp = sc->vte_ifp; 13942608aefcSPyun YongHyeon 13952608aefcSPyun YongHyeon if (sc->vte_cdata.vte_tx_cnt == 0) 13962608aefcSPyun YongHyeon return; 13972608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_tx_ring_tag, 13982608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_map, BUS_DMASYNC_POSTREAD | 13992608aefcSPyun YongHyeon BUS_DMASYNC_POSTWRITE); 14002608aefcSPyun YongHyeon cons = sc->vte_cdata.vte_tx_cons; 14012608aefcSPyun YongHyeon /* 14022608aefcSPyun YongHyeon * Go through our TX list and free mbufs for those 14032608aefcSPyun YongHyeon * frames which have been transmitted. 14042608aefcSPyun YongHyeon */ 14052608aefcSPyun YongHyeon for (prog = 0; sc->vte_cdata.vte_tx_cnt > 0; prog++) { 14062608aefcSPyun YongHyeon txd = &sc->vte_cdata.vte_txdesc[cons]; 14072608aefcSPyun YongHyeon status = le16toh(txd->tx_desc->dtst); 14082608aefcSPyun YongHyeon if ((status & VTE_DTST_TX_OWN) != 0) 14092608aefcSPyun YongHyeon break; 14102608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_cnt--; 14112608aefcSPyun YongHyeon /* Reclaim transmitted mbufs. */ 14122608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_tx_tag, txd->tx_dmamap, 14132608aefcSPyun YongHyeon BUS_DMASYNC_POSTWRITE); 14142608aefcSPyun YongHyeon bus_dmamap_unload(sc->vte_cdata.vte_tx_tag, txd->tx_dmamap); 14152608aefcSPyun YongHyeon if ((txd->tx_flags & VTE_TXMBUF) == 0) 14162608aefcSPyun YongHyeon m_freem(txd->tx_m); 14172608aefcSPyun YongHyeon txd->tx_flags &= ~VTE_TXMBUF; 14182608aefcSPyun YongHyeon txd->tx_m = NULL; 14192608aefcSPyun YongHyeon prog++; 14202608aefcSPyun YongHyeon VTE_DESC_INC(cons, VTE_TX_RING_CNT); 14212608aefcSPyun YongHyeon } 14222608aefcSPyun YongHyeon 14232608aefcSPyun YongHyeon if (prog > 0) { 14243486b835SJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); 14252608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_cons = cons; 14262608aefcSPyun YongHyeon /* 14272608aefcSPyun YongHyeon * Unarm watchdog timer only when there is no pending 14282608aefcSPyun YongHyeon * frames in TX queue. 14292608aefcSPyun YongHyeon */ 14302608aefcSPyun YongHyeon if (sc->vte_cdata.vte_tx_cnt == 0) 14312608aefcSPyun YongHyeon sc->vte_watchdog_timer = 0; 14322608aefcSPyun YongHyeon } 14332608aefcSPyun YongHyeon } 14342608aefcSPyun YongHyeon 14352608aefcSPyun YongHyeon static int 14362608aefcSPyun YongHyeon vte_newbuf(struct vte_softc *sc, struct vte_rxdesc *rxd) 14372608aefcSPyun YongHyeon { 14382608aefcSPyun YongHyeon struct mbuf *m; 14392608aefcSPyun YongHyeon bus_dma_segment_t segs[1]; 14402608aefcSPyun YongHyeon bus_dmamap_t map; 14412608aefcSPyun YongHyeon int nsegs; 14422608aefcSPyun YongHyeon 1443c6499eccSGleb Smirnoff m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 14442608aefcSPyun YongHyeon if (m == NULL) 14452608aefcSPyun YongHyeon return (ENOBUFS); 14462608aefcSPyun YongHyeon m->m_len = m->m_pkthdr.len = MCLBYTES; 14472608aefcSPyun YongHyeon m_adj(m, sizeof(uint32_t)); 14482608aefcSPyun YongHyeon 14492608aefcSPyun YongHyeon if (bus_dmamap_load_mbuf_sg(sc->vte_cdata.vte_rx_tag, 14502608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_sparemap, m, segs, &nsegs, 0) != 0) { 14512608aefcSPyun YongHyeon m_freem(m); 14522608aefcSPyun YongHyeon return (ENOBUFS); 14532608aefcSPyun YongHyeon } 14542608aefcSPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 14552608aefcSPyun YongHyeon 14562608aefcSPyun YongHyeon if (rxd->rx_m != NULL) { 14572608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_rx_tag, rxd->rx_dmamap, 14582608aefcSPyun YongHyeon BUS_DMASYNC_POSTREAD); 14592608aefcSPyun YongHyeon bus_dmamap_unload(sc->vte_cdata.vte_rx_tag, rxd->rx_dmamap); 14602608aefcSPyun YongHyeon } 14612608aefcSPyun YongHyeon map = rxd->rx_dmamap; 14622608aefcSPyun YongHyeon rxd->rx_dmamap = sc->vte_cdata.vte_rx_sparemap; 14632608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_sparemap = map; 14642608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_rx_tag, rxd->rx_dmamap, 14652608aefcSPyun YongHyeon BUS_DMASYNC_PREREAD); 14662608aefcSPyun YongHyeon rxd->rx_m = m; 14672608aefcSPyun YongHyeon rxd->rx_desc->drbp = htole32(segs[0].ds_addr); 14682608aefcSPyun YongHyeon rxd->rx_desc->drlen = htole16(VTE_RX_LEN(segs[0].ds_len)); 14692608aefcSPyun YongHyeon rxd->rx_desc->drst = htole16(VTE_DRST_RX_OWN); 14702608aefcSPyun YongHyeon 14712608aefcSPyun YongHyeon return (0); 14722608aefcSPyun YongHyeon } 14732608aefcSPyun YongHyeon 14742608aefcSPyun YongHyeon /* 14752608aefcSPyun YongHyeon * It's not supposed to see this controller on strict-alignment 14762608aefcSPyun YongHyeon * architectures but make it work for completeness. 14772608aefcSPyun YongHyeon */ 14782608aefcSPyun YongHyeon #ifndef __NO_STRICT_ALIGNMENT 14792608aefcSPyun YongHyeon static struct mbuf * 14803486b835SJustin Hibbits vte_fixup_rx(if_t ifp, struct mbuf *m) 14812608aefcSPyun YongHyeon { 14822608aefcSPyun YongHyeon uint16_t *src, *dst; 14832608aefcSPyun YongHyeon int i; 14842608aefcSPyun YongHyeon 14852608aefcSPyun YongHyeon src = mtod(m, uint16_t *); 14862608aefcSPyun YongHyeon dst = src - 1; 14872608aefcSPyun YongHyeon 14882608aefcSPyun YongHyeon for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++) 14892608aefcSPyun YongHyeon *dst++ = *src++; 14902608aefcSPyun YongHyeon m->m_data -= ETHER_ALIGN; 14912608aefcSPyun YongHyeon return (m); 14922608aefcSPyun YongHyeon } 14932608aefcSPyun YongHyeon #endif 14942608aefcSPyun YongHyeon 14952608aefcSPyun YongHyeon static void 14962608aefcSPyun YongHyeon vte_rxeof(struct vte_softc *sc) 14972608aefcSPyun YongHyeon { 14983486b835SJustin Hibbits if_t ifp; 14992608aefcSPyun YongHyeon struct vte_rxdesc *rxd; 15002608aefcSPyun YongHyeon struct mbuf *m; 15012608aefcSPyun YongHyeon uint16_t status, total_len; 15022608aefcSPyun YongHyeon int cons, prog; 15032608aefcSPyun YongHyeon 15042608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_rx_ring_tag, 15052608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_map, BUS_DMASYNC_POSTREAD | 15062608aefcSPyun YongHyeon BUS_DMASYNC_POSTWRITE); 15072608aefcSPyun YongHyeon cons = sc->vte_cdata.vte_rx_cons; 15082608aefcSPyun YongHyeon ifp = sc->vte_ifp; 15093486b835SJustin Hibbits for (prog = 0; (if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0; prog++, 15102608aefcSPyun YongHyeon VTE_DESC_INC(cons, VTE_RX_RING_CNT)) { 15112608aefcSPyun YongHyeon rxd = &sc->vte_cdata.vte_rxdesc[cons]; 15122608aefcSPyun YongHyeon status = le16toh(rxd->rx_desc->drst); 15132608aefcSPyun YongHyeon if ((status & VTE_DRST_RX_OWN) != 0) 15142608aefcSPyun YongHyeon break; 15152608aefcSPyun YongHyeon total_len = VTE_RX_LEN(le16toh(rxd->rx_desc->drlen)); 15162608aefcSPyun YongHyeon m = rxd->rx_m; 15172608aefcSPyun YongHyeon if ((status & VTE_DRST_RX_OK) == 0) { 15182608aefcSPyun YongHyeon /* Discard errored frame. */ 15192608aefcSPyun YongHyeon rxd->rx_desc->drlen = 15202608aefcSPyun YongHyeon htole16(MCLBYTES - sizeof(uint32_t)); 15212608aefcSPyun YongHyeon rxd->rx_desc->drst = htole16(VTE_DRST_RX_OWN); 15222608aefcSPyun YongHyeon continue; 15232608aefcSPyun YongHyeon } 15242608aefcSPyun YongHyeon if (vte_newbuf(sc, rxd) != 0) { 152524b83dc6SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); 15262608aefcSPyun YongHyeon rxd->rx_desc->drlen = 15272608aefcSPyun YongHyeon htole16(MCLBYTES - sizeof(uint32_t)); 15282608aefcSPyun YongHyeon rxd->rx_desc->drst = htole16(VTE_DRST_RX_OWN); 15292608aefcSPyun YongHyeon continue; 15302608aefcSPyun YongHyeon } 15312608aefcSPyun YongHyeon 15322608aefcSPyun YongHyeon /* 15332608aefcSPyun YongHyeon * It seems there is no way to strip FCS bytes. 15342608aefcSPyun YongHyeon */ 15352608aefcSPyun YongHyeon m->m_pkthdr.len = m->m_len = total_len - ETHER_CRC_LEN; 15362608aefcSPyun YongHyeon m->m_pkthdr.rcvif = ifp; 15372608aefcSPyun YongHyeon #ifndef __NO_STRICT_ALIGNMENT 15382608aefcSPyun YongHyeon vte_fixup_rx(ifp, m); 15392608aefcSPyun YongHyeon #endif 15402608aefcSPyun YongHyeon VTE_UNLOCK(sc); 15413486b835SJustin Hibbits if_input(ifp, m); 15422608aefcSPyun YongHyeon VTE_LOCK(sc); 15432608aefcSPyun YongHyeon } 15442608aefcSPyun YongHyeon 15452608aefcSPyun YongHyeon if (prog > 0) { 15462608aefcSPyun YongHyeon /* Update the consumer index. */ 15472608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_cons = cons; 15482608aefcSPyun YongHyeon /* 15492608aefcSPyun YongHyeon * Sync updated RX descriptors such that controller see 15502608aefcSPyun YongHyeon * modified RX buffer addresses. 15512608aefcSPyun YongHyeon */ 15522608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_rx_ring_tag, 15532608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_map, 15542608aefcSPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 15552608aefcSPyun YongHyeon #ifdef notyet 15562608aefcSPyun YongHyeon /* 15572608aefcSPyun YongHyeon * Update residue counter. Controller does not 15582608aefcSPyun YongHyeon * keep track of number of available RX descriptors 15592608aefcSPyun YongHyeon * such that driver should have to update VTE_MRDCR 15602608aefcSPyun YongHyeon * to make controller know how many free RX 15612608aefcSPyun YongHyeon * descriptors were added to controller. This is 15622608aefcSPyun YongHyeon * a similar mechanism used in VIA velocity 15632608aefcSPyun YongHyeon * controllers and it indicates controller just 15642608aefcSPyun YongHyeon * polls OWN bit of current RX descriptor pointer. 15652608aefcSPyun YongHyeon * A couple of severe issues were seen on sample 15662608aefcSPyun YongHyeon * board where the controller continuously emits TX 15672608aefcSPyun YongHyeon * pause frames once RX pause threshold crossed. 15682608aefcSPyun YongHyeon * Once triggered it never recovered form that 15692608aefcSPyun YongHyeon * state, I couldn't find a way to make it back to 15702608aefcSPyun YongHyeon * work at least. This issue effectively 15712608aefcSPyun YongHyeon * disconnected the system from network. Also, the 15722608aefcSPyun YongHyeon * controller used 00:00:00:00:00:00 as source 15732608aefcSPyun YongHyeon * station address of TX pause frame. Probably this 15742608aefcSPyun YongHyeon * is one of reason why vendor recommends not to 15752608aefcSPyun YongHyeon * enable flow control on R6040 controller. 15762608aefcSPyun YongHyeon */ 15772608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MRDCR, prog | 15782608aefcSPyun YongHyeon (((VTE_RX_RING_CNT * 2) / 10) << 15792608aefcSPyun YongHyeon VTE_MRDCR_RX_PAUSE_THRESH_SHIFT)); 15802608aefcSPyun YongHyeon #endif 15812608aefcSPyun YongHyeon } 15822608aefcSPyun YongHyeon } 15832608aefcSPyun YongHyeon 15842608aefcSPyun YongHyeon static void 15852608aefcSPyun YongHyeon vte_tick(void *arg) 15862608aefcSPyun YongHyeon { 15872608aefcSPyun YongHyeon struct vte_softc *sc; 15882608aefcSPyun YongHyeon struct mii_data *mii; 15892608aefcSPyun YongHyeon 15902608aefcSPyun YongHyeon sc = (struct vte_softc *)arg; 15912608aefcSPyun YongHyeon 15922608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 15932608aefcSPyun YongHyeon 15942608aefcSPyun YongHyeon mii = device_get_softc(sc->vte_miibus); 15952608aefcSPyun YongHyeon mii_tick(mii); 15962608aefcSPyun YongHyeon vte_stats_update(sc); 15972608aefcSPyun YongHyeon vte_txeof(sc); 15982608aefcSPyun YongHyeon vte_watchdog(sc); 15992608aefcSPyun YongHyeon callout_reset(&sc->vte_tick_ch, hz, vte_tick, sc); 16002608aefcSPyun YongHyeon } 16012608aefcSPyun YongHyeon 16022608aefcSPyun YongHyeon static void 16032608aefcSPyun YongHyeon vte_reset(struct vte_softc *sc) 16042608aefcSPyun YongHyeon { 16058f216d28SKevin Lo uint16_t mcr, mdcsc; 16062608aefcSPyun YongHyeon int i; 16072608aefcSPyun YongHyeon 16088f216d28SKevin Lo mdcsc = CSR_READ_2(sc, VTE_MDCSC); 16092608aefcSPyun YongHyeon mcr = CSR_READ_2(sc, VTE_MCR1); 16102608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MCR1, mcr | MCR1_MAC_RESET); 16112608aefcSPyun YongHyeon for (i = VTE_RESET_TIMEOUT; i > 0; i--) { 16122608aefcSPyun YongHyeon DELAY(10); 16132608aefcSPyun YongHyeon if ((CSR_READ_2(sc, VTE_MCR1) & MCR1_MAC_RESET) == 0) 16142608aefcSPyun YongHyeon break; 16152608aefcSPyun YongHyeon } 16162608aefcSPyun YongHyeon if (i == 0) 16172608aefcSPyun YongHyeon device_printf(sc->vte_dev, "reset timeout(0x%04x)!\n", mcr); 16182608aefcSPyun YongHyeon /* 16192608aefcSPyun YongHyeon * Follow the guide of vendor recommended way to reset MAC. 16202608aefcSPyun YongHyeon * Vendor confirms relying on MCR1_MAC_RESET of VTE_MCR1 is 16212608aefcSPyun YongHyeon * not reliable so manually reset internal state machine. 16222608aefcSPyun YongHyeon */ 16232608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MACSM, 0x0002); 16242608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MACSM, 0); 16252608aefcSPyun YongHyeon DELAY(5000); 16268f216d28SKevin Lo 16278f216d28SKevin Lo /* 16288f216d28SKevin Lo * On some SoCs (like Vortex86DX3) MDC speed control register value 16298f216d28SKevin Lo * needs to be restored to original value instead of default one, 16308f216d28SKevin Lo * otherwise some PHY registers may fail to be read. 16318f216d28SKevin Lo */ 16328f216d28SKevin Lo if (mdcsc != MDCSC_DEFAULT) 16338f216d28SKevin Lo CSR_WRITE_2(sc, VTE_MDCSC, mdcsc); 16342608aefcSPyun YongHyeon } 16352608aefcSPyun YongHyeon 16362608aefcSPyun YongHyeon static void 16372608aefcSPyun YongHyeon vte_init(void *xsc) 16382608aefcSPyun YongHyeon { 16392608aefcSPyun YongHyeon struct vte_softc *sc; 16402608aefcSPyun YongHyeon 16412608aefcSPyun YongHyeon sc = (struct vte_softc *)xsc; 16422608aefcSPyun YongHyeon VTE_LOCK(sc); 16432608aefcSPyun YongHyeon vte_init_locked(sc); 16442608aefcSPyun YongHyeon VTE_UNLOCK(sc); 16452608aefcSPyun YongHyeon } 16462608aefcSPyun YongHyeon 16472608aefcSPyun YongHyeon static void 16482608aefcSPyun YongHyeon vte_init_locked(struct vte_softc *sc) 16492608aefcSPyun YongHyeon { 16503486b835SJustin Hibbits if_t ifp; 16512608aefcSPyun YongHyeon bus_addr_t paddr; 16522608aefcSPyun YongHyeon uint8_t *eaddr; 16532608aefcSPyun YongHyeon 16542608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 16552608aefcSPyun YongHyeon 16562608aefcSPyun YongHyeon ifp = sc->vte_ifp; 16572608aefcSPyun YongHyeon 16583486b835SJustin Hibbits if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) 16592608aefcSPyun YongHyeon return; 16602608aefcSPyun YongHyeon /* 16612608aefcSPyun YongHyeon * Cancel any pending I/O. 16622608aefcSPyun YongHyeon */ 16632608aefcSPyun YongHyeon vte_stop(sc); 16642608aefcSPyun YongHyeon /* 16652608aefcSPyun YongHyeon * Reset the chip to a known state. 16662608aefcSPyun YongHyeon */ 16672608aefcSPyun YongHyeon vte_reset(sc); 16682608aefcSPyun YongHyeon 16692608aefcSPyun YongHyeon /* Initialize RX descriptors. */ 16702608aefcSPyun YongHyeon if (vte_init_rx_ring(sc) != 0) { 16712608aefcSPyun YongHyeon device_printf(sc->vte_dev, "no memory for RX buffers.\n"); 16722608aefcSPyun YongHyeon vte_stop(sc); 16732608aefcSPyun YongHyeon return; 16742608aefcSPyun YongHyeon } 16752608aefcSPyun YongHyeon if (vte_init_tx_ring(sc) != 0) { 16762608aefcSPyun YongHyeon device_printf(sc->vte_dev, "no memory for TX buffers.\n"); 16772608aefcSPyun YongHyeon vte_stop(sc); 16782608aefcSPyun YongHyeon return; 16792608aefcSPyun YongHyeon } 16802608aefcSPyun YongHyeon 16812608aefcSPyun YongHyeon /* 16822608aefcSPyun YongHyeon * Reprogram the station address. Controller supports up 16832608aefcSPyun YongHyeon * to 4 different station addresses so driver programs the 16842608aefcSPyun YongHyeon * first station address as its own ethernet address and 16852608aefcSPyun YongHyeon * configure the remaining three addresses as perfect 16862608aefcSPyun YongHyeon * multicast addresses. 16872608aefcSPyun YongHyeon */ 16883486b835SJustin Hibbits eaddr = if_getlladdr(sc->vte_ifp); 16892608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MID0L, eaddr[1] << 8 | eaddr[0]); 16902608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MID0M, eaddr[3] << 8 | eaddr[2]); 16912608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MID0H, eaddr[5] << 8 | eaddr[4]); 16922608aefcSPyun YongHyeon 16932608aefcSPyun YongHyeon /* Set TX descriptor base addresses. */ 16942608aefcSPyun YongHyeon paddr = sc->vte_cdata.vte_tx_ring_paddr; 16952608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MTDSA1, paddr >> 16); 16962608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MTDSA0, paddr & 0xFFFF); 16972608aefcSPyun YongHyeon /* Set RX descriptor base addresses. */ 16982608aefcSPyun YongHyeon paddr = sc->vte_cdata.vte_rx_ring_paddr; 16992608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MRDSA1, paddr >> 16); 17002608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MRDSA0, paddr & 0xFFFF); 17012608aefcSPyun YongHyeon /* 17022608aefcSPyun YongHyeon * Initialize RX descriptor residue counter and set RX 17032608aefcSPyun YongHyeon * pause threshold to 20% of available RX descriptors. 17042608aefcSPyun YongHyeon * See comments on vte_rxeof() for details on flow control 17052608aefcSPyun YongHyeon * issues. 17062608aefcSPyun YongHyeon */ 17072608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MRDCR, (VTE_RX_RING_CNT & VTE_MRDCR_RESIDUE_MASK) | 17082608aefcSPyun YongHyeon (((VTE_RX_RING_CNT * 2) / 10) << VTE_MRDCR_RX_PAUSE_THRESH_SHIFT)); 17092608aefcSPyun YongHyeon 17102608aefcSPyun YongHyeon /* 17112608aefcSPyun YongHyeon * Always use maximum frame size that controller can 17122608aefcSPyun YongHyeon * support. Otherwise received frames that has longer 17132608aefcSPyun YongHyeon * frame length than vte(4) MTU would be silently dropped 17142608aefcSPyun YongHyeon * in controller. This would break path-MTU discovery as 17152608aefcSPyun YongHyeon * sender wouldn't get any responses from receiver. The 17162608aefcSPyun YongHyeon * RX buffer size should be multiple of 4. 17172608aefcSPyun YongHyeon * Note, jumbo frames are silently ignored by controller 17182608aefcSPyun YongHyeon * and even MAC counters do not detect them. 17192608aefcSPyun YongHyeon */ 17202608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MRBSR, VTE_RX_BUF_SIZE_MAX); 17212608aefcSPyun YongHyeon 17222608aefcSPyun YongHyeon /* Configure FIFO. */ 17232608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MBCR, MBCR_FIFO_XFER_LENGTH_16 | 17242608aefcSPyun YongHyeon MBCR_TX_FIFO_THRESH_64 | MBCR_RX_FIFO_THRESH_16 | 17252608aefcSPyun YongHyeon MBCR_SDRAM_BUS_REQ_TIMER_DEFAULT); 17262608aefcSPyun YongHyeon 17272608aefcSPyun YongHyeon /* 17282608aefcSPyun YongHyeon * Configure TX/RX MACs. Actual resolved duplex and flow 17292608aefcSPyun YongHyeon * control configuration is done after detecting a valid 17302608aefcSPyun YongHyeon * link. Note, we don't generate early interrupt here 17312608aefcSPyun YongHyeon * as well since FreeBSD does not have interrupt latency 17322608aefcSPyun YongHyeon * problems like Windows. 17332608aefcSPyun YongHyeon */ 17342608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MCR0, MCR0_ACCPT_LONG_PKT); 17352608aefcSPyun YongHyeon /* 17362608aefcSPyun YongHyeon * We manually keep track of PHY status changes to 17372608aefcSPyun YongHyeon * configure resolved duplex and flow control since only 17382608aefcSPyun YongHyeon * duplex configuration can be automatically reflected to 17392608aefcSPyun YongHyeon * MCR0. 17402608aefcSPyun YongHyeon */ 17412608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MCR1, MCR1_PKT_LENGTH_1537 | 17422608aefcSPyun YongHyeon MCR1_EXCESS_COL_RETRY_16); 17432608aefcSPyun YongHyeon 17442608aefcSPyun YongHyeon /* Initialize RX filter. */ 17452608aefcSPyun YongHyeon vte_rxfilter(sc); 17462608aefcSPyun YongHyeon 17472608aefcSPyun YongHyeon /* Disable TX/RX interrupt moderation control. */ 17482608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MRICR, 0); 17492608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MTICR, 0); 17502608aefcSPyun YongHyeon 17512608aefcSPyun YongHyeon /* Enable MAC event counter interrupts. */ 17522608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MECIER, VTE_MECIER_INTRS); 17532608aefcSPyun YongHyeon /* Clear MAC statistics. */ 17542608aefcSPyun YongHyeon vte_stats_clear(sc); 17552608aefcSPyun YongHyeon 17562608aefcSPyun YongHyeon /* Acknowledge all pending interrupts and clear it. */ 17572608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MIER, VTE_INTRS); 17582608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MISR, 0); 17592608aefcSPyun YongHyeon 17602608aefcSPyun YongHyeon sc->vte_flags &= ~VTE_FLAG_LINK; 17612608aefcSPyun YongHyeon /* Switch to the current media. */ 17622608aefcSPyun YongHyeon vte_mediachange_locked(ifp); 17632608aefcSPyun YongHyeon 17642608aefcSPyun YongHyeon callout_reset(&sc->vte_tick_ch, hz, vte_tick, sc); 17652608aefcSPyun YongHyeon 17663486b835SJustin Hibbits if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0); 17673486b835SJustin Hibbits if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE); 17682608aefcSPyun YongHyeon } 17692608aefcSPyun YongHyeon 17702608aefcSPyun YongHyeon static void 17712608aefcSPyun YongHyeon vte_stop(struct vte_softc *sc) 17722608aefcSPyun YongHyeon { 17733486b835SJustin Hibbits if_t ifp; 17742608aefcSPyun YongHyeon struct vte_txdesc *txd; 17752608aefcSPyun YongHyeon struct vte_rxdesc *rxd; 17762608aefcSPyun YongHyeon int i; 17772608aefcSPyun YongHyeon 17782608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 17792608aefcSPyun YongHyeon /* 17802608aefcSPyun YongHyeon * Mark the interface down and cancel the watchdog timer. 17812608aefcSPyun YongHyeon */ 17822608aefcSPyun YongHyeon ifp = sc->vte_ifp; 17833486b835SJustin Hibbits if_setdrvflagbits(ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)); 17842608aefcSPyun YongHyeon sc->vte_flags &= ~VTE_FLAG_LINK; 17852608aefcSPyun YongHyeon callout_stop(&sc->vte_tick_ch); 17862608aefcSPyun YongHyeon sc->vte_watchdog_timer = 0; 17872608aefcSPyun YongHyeon vte_stats_update(sc); 17882608aefcSPyun YongHyeon /* Disable interrupts. */ 17892608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MIER, 0); 17902608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MECIER, 0); 17912608aefcSPyun YongHyeon /* Stop RX/TX MACs. */ 17922608aefcSPyun YongHyeon vte_stop_mac(sc); 17932608aefcSPyun YongHyeon /* Clear interrupts. */ 17942608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_MISR); 17952608aefcSPyun YongHyeon /* 17962608aefcSPyun YongHyeon * Free TX/RX mbufs still in the queues. 17972608aefcSPyun YongHyeon */ 17982608aefcSPyun YongHyeon for (i = 0; i < VTE_RX_RING_CNT; i++) { 17992608aefcSPyun YongHyeon rxd = &sc->vte_cdata.vte_rxdesc[i]; 18002608aefcSPyun YongHyeon if (rxd->rx_m != NULL) { 18012608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_rx_tag, 18022608aefcSPyun YongHyeon rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); 18032608aefcSPyun YongHyeon bus_dmamap_unload(sc->vte_cdata.vte_rx_tag, 18042608aefcSPyun YongHyeon rxd->rx_dmamap); 18052608aefcSPyun YongHyeon m_freem(rxd->rx_m); 18062608aefcSPyun YongHyeon rxd->rx_m = NULL; 18072608aefcSPyun YongHyeon } 18082608aefcSPyun YongHyeon } 18092608aefcSPyun YongHyeon for (i = 0; i < VTE_TX_RING_CNT; i++) { 18102608aefcSPyun YongHyeon txd = &sc->vte_cdata.vte_txdesc[i]; 18112608aefcSPyun YongHyeon if (txd->tx_m != NULL) { 18122608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_tx_tag, 18132608aefcSPyun YongHyeon txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); 18142608aefcSPyun YongHyeon bus_dmamap_unload(sc->vte_cdata.vte_tx_tag, 18152608aefcSPyun YongHyeon txd->tx_dmamap); 18162608aefcSPyun YongHyeon if ((txd->tx_flags & VTE_TXMBUF) == 0) 18172608aefcSPyun YongHyeon m_freem(txd->tx_m); 18182608aefcSPyun YongHyeon txd->tx_m = NULL; 18192608aefcSPyun YongHyeon txd->tx_flags &= ~VTE_TXMBUF; 18202608aefcSPyun YongHyeon } 18212608aefcSPyun YongHyeon } 18222608aefcSPyun YongHyeon /* Free TX mbuf pools used for deep copy. */ 18232608aefcSPyun YongHyeon for (i = 0; i < VTE_TX_RING_CNT; i++) { 18242608aefcSPyun YongHyeon if (sc->vte_cdata.vte_txmbufs[i] != NULL) { 18252608aefcSPyun YongHyeon m_freem(sc->vte_cdata.vte_txmbufs[i]); 18262608aefcSPyun YongHyeon sc->vte_cdata.vte_txmbufs[i] = NULL; 18272608aefcSPyun YongHyeon } 18282608aefcSPyun YongHyeon } 18292608aefcSPyun YongHyeon } 18302608aefcSPyun YongHyeon 18312608aefcSPyun YongHyeon static void 18322608aefcSPyun YongHyeon vte_start_mac(struct vte_softc *sc) 18332608aefcSPyun YongHyeon { 18342608aefcSPyun YongHyeon uint16_t mcr; 18352608aefcSPyun YongHyeon int i; 18362608aefcSPyun YongHyeon 18372608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 18382608aefcSPyun YongHyeon 18392608aefcSPyun YongHyeon /* Enable RX/TX MACs. */ 18402608aefcSPyun YongHyeon mcr = CSR_READ_2(sc, VTE_MCR0); 18412608aefcSPyun YongHyeon if ((mcr & (MCR0_RX_ENB | MCR0_TX_ENB)) != 18422608aefcSPyun YongHyeon (MCR0_RX_ENB | MCR0_TX_ENB)) { 18432608aefcSPyun YongHyeon mcr |= MCR0_RX_ENB | MCR0_TX_ENB; 18442608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MCR0, mcr); 18452608aefcSPyun YongHyeon for (i = VTE_TIMEOUT; i > 0; i--) { 18462608aefcSPyun YongHyeon mcr = CSR_READ_2(sc, VTE_MCR0); 18472608aefcSPyun YongHyeon if ((mcr & (MCR0_RX_ENB | MCR0_TX_ENB)) == 18482608aefcSPyun YongHyeon (MCR0_RX_ENB | MCR0_TX_ENB)) 18492608aefcSPyun YongHyeon break; 18502608aefcSPyun YongHyeon DELAY(10); 18512608aefcSPyun YongHyeon } 18522608aefcSPyun YongHyeon if (i == 0) 18532608aefcSPyun YongHyeon device_printf(sc->vte_dev, 18542608aefcSPyun YongHyeon "could not enable RX/TX MAC(0x%04x)!\n", mcr); 18552608aefcSPyun YongHyeon } 18562608aefcSPyun YongHyeon } 18572608aefcSPyun YongHyeon 18582608aefcSPyun YongHyeon static void 18592608aefcSPyun YongHyeon vte_stop_mac(struct vte_softc *sc) 18602608aefcSPyun YongHyeon { 18612608aefcSPyun YongHyeon uint16_t mcr; 18622608aefcSPyun YongHyeon int i; 18632608aefcSPyun YongHyeon 18642608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 18652608aefcSPyun YongHyeon 18662608aefcSPyun YongHyeon /* Disable RX/TX MACs. */ 18672608aefcSPyun YongHyeon mcr = CSR_READ_2(sc, VTE_MCR0); 18682608aefcSPyun YongHyeon if ((mcr & (MCR0_RX_ENB | MCR0_TX_ENB)) != 0) { 18692608aefcSPyun YongHyeon mcr &= ~(MCR0_RX_ENB | MCR0_TX_ENB); 18702608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MCR0, mcr); 18712608aefcSPyun YongHyeon for (i = VTE_TIMEOUT; i > 0; i--) { 18722608aefcSPyun YongHyeon mcr = CSR_READ_2(sc, VTE_MCR0); 18732608aefcSPyun YongHyeon if ((mcr & (MCR0_RX_ENB | MCR0_TX_ENB)) == 0) 18742608aefcSPyun YongHyeon break; 18752608aefcSPyun YongHyeon DELAY(10); 18762608aefcSPyun YongHyeon } 18772608aefcSPyun YongHyeon if (i == 0) 18782608aefcSPyun YongHyeon device_printf(sc->vte_dev, 18792608aefcSPyun YongHyeon "could not disable RX/TX MAC(0x%04x)!\n", mcr); 18802608aefcSPyun YongHyeon } 18812608aefcSPyun YongHyeon } 18822608aefcSPyun YongHyeon 18832608aefcSPyun YongHyeon static int 18842608aefcSPyun YongHyeon vte_init_tx_ring(struct vte_softc *sc) 18852608aefcSPyun YongHyeon { 18862608aefcSPyun YongHyeon struct vte_tx_desc *desc; 18872608aefcSPyun YongHyeon struct vte_txdesc *txd; 18882608aefcSPyun YongHyeon bus_addr_t addr; 18892608aefcSPyun YongHyeon int i; 18902608aefcSPyun YongHyeon 18912608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 18922608aefcSPyun YongHyeon 18932608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_prod = 0; 18942608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_cons = 0; 18952608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_cnt = 0; 18962608aefcSPyun YongHyeon 18972608aefcSPyun YongHyeon /* Pre-allocate TX mbufs for deep copy. */ 18982608aefcSPyun YongHyeon if (tx_deep_copy != 0) { 18992608aefcSPyun YongHyeon for (i = 0; i < VTE_TX_RING_CNT; i++) { 1900c6499eccSGleb Smirnoff sc->vte_cdata.vte_txmbufs[i] = m_getcl(M_NOWAIT, 19012608aefcSPyun YongHyeon MT_DATA, M_PKTHDR); 19022608aefcSPyun YongHyeon if (sc->vte_cdata.vte_txmbufs[i] == NULL) 19032608aefcSPyun YongHyeon return (ENOBUFS); 19042608aefcSPyun YongHyeon sc->vte_cdata.vte_txmbufs[i]->m_pkthdr.len = MCLBYTES; 19052608aefcSPyun YongHyeon sc->vte_cdata.vte_txmbufs[i]->m_len = MCLBYTES; 19062608aefcSPyun YongHyeon } 19072608aefcSPyun YongHyeon } 19082608aefcSPyun YongHyeon desc = sc->vte_cdata.vte_tx_ring; 19092608aefcSPyun YongHyeon bzero(desc, VTE_TX_RING_SZ); 19102608aefcSPyun YongHyeon for (i = 0; i < VTE_TX_RING_CNT; i++) { 19112608aefcSPyun YongHyeon txd = &sc->vte_cdata.vte_txdesc[i]; 19122608aefcSPyun YongHyeon txd->tx_m = NULL; 19132608aefcSPyun YongHyeon if (i != VTE_TX_RING_CNT - 1) 19142608aefcSPyun YongHyeon addr = sc->vte_cdata.vte_tx_ring_paddr + 19152608aefcSPyun YongHyeon sizeof(struct vte_tx_desc) * (i + 1); 19162608aefcSPyun YongHyeon else 19172608aefcSPyun YongHyeon addr = sc->vte_cdata.vte_tx_ring_paddr + 19182608aefcSPyun YongHyeon sizeof(struct vte_tx_desc) * 0; 19192608aefcSPyun YongHyeon desc = &sc->vte_cdata.vte_tx_ring[i]; 19202608aefcSPyun YongHyeon desc->dtnp = htole32(addr); 19212608aefcSPyun YongHyeon txd->tx_desc = desc; 19222608aefcSPyun YongHyeon } 19232608aefcSPyun YongHyeon 19242608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_tx_ring_tag, 19252608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_map, BUS_DMASYNC_PREREAD | 19262608aefcSPyun YongHyeon BUS_DMASYNC_PREWRITE); 19272608aefcSPyun YongHyeon return (0); 19282608aefcSPyun YongHyeon } 19292608aefcSPyun YongHyeon 19302608aefcSPyun YongHyeon static int 19312608aefcSPyun YongHyeon vte_init_rx_ring(struct vte_softc *sc) 19322608aefcSPyun YongHyeon { 19332608aefcSPyun YongHyeon struct vte_rx_desc *desc; 19342608aefcSPyun YongHyeon struct vte_rxdesc *rxd; 19352608aefcSPyun YongHyeon bus_addr_t addr; 19362608aefcSPyun YongHyeon int i; 19372608aefcSPyun YongHyeon 19382608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 19392608aefcSPyun YongHyeon 19402608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_cons = 0; 19412608aefcSPyun YongHyeon desc = sc->vte_cdata.vte_rx_ring; 19422608aefcSPyun YongHyeon bzero(desc, VTE_RX_RING_SZ); 19432608aefcSPyun YongHyeon for (i = 0; i < VTE_RX_RING_CNT; i++) { 19442608aefcSPyun YongHyeon rxd = &sc->vte_cdata.vte_rxdesc[i]; 19452608aefcSPyun YongHyeon rxd->rx_m = NULL; 19462608aefcSPyun YongHyeon if (i != VTE_RX_RING_CNT - 1) 19472608aefcSPyun YongHyeon addr = sc->vte_cdata.vte_rx_ring_paddr + 19482608aefcSPyun YongHyeon sizeof(struct vte_rx_desc) * (i + 1); 19492608aefcSPyun YongHyeon else 19502608aefcSPyun YongHyeon addr = sc->vte_cdata.vte_rx_ring_paddr + 19512608aefcSPyun YongHyeon sizeof(struct vte_rx_desc) * 0; 19522608aefcSPyun YongHyeon desc = &sc->vte_cdata.vte_rx_ring[i]; 19532608aefcSPyun YongHyeon desc->drnp = htole32(addr); 19542608aefcSPyun YongHyeon rxd->rx_desc = desc; 19552608aefcSPyun YongHyeon if (vte_newbuf(sc, rxd) != 0) 19562608aefcSPyun YongHyeon return (ENOBUFS); 19572608aefcSPyun YongHyeon } 19582608aefcSPyun YongHyeon 19592608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_rx_ring_tag, 19602608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_map, BUS_DMASYNC_PREREAD | 19612608aefcSPyun YongHyeon BUS_DMASYNC_PREWRITE); 19622608aefcSPyun YongHyeon 19632608aefcSPyun YongHyeon return (0); 19642608aefcSPyun YongHyeon } 19652608aefcSPyun YongHyeon 19665d0c611cSGleb Smirnoff struct vte_maddr_ctx { 19675d0c611cSGleb Smirnoff uint16_t rxfilt_perf[VTE_RXFILT_PERFECT_CNT][3]; 19685d0c611cSGleb Smirnoff uint16_t mchash[4]; 19695d0c611cSGleb Smirnoff u_int nperf; 19705d0c611cSGleb Smirnoff }; 19715d0c611cSGleb Smirnoff 19725d0c611cSGleb Smirnoff static u_int 19735d0c611cSGleb Smirnoff vte_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) 19745d0c611cSGleb Smirnoff { 19755d0c611cSGleb Smirnoff struct vte_maddr_ctx *ctx = arg; 19765d0c611cSGleb Smirnoff uint8_t *eaddr; 19775d0c611cSGleb Smirnoff uint32_t crc; 19785d0c611cSGleb Smirnoff 19795d0c611cSGleb Smirnoff /* 19805d0c611cSGleb Smirnoff * Program the first 3 multicast groups into the perfect filter. 19815d0c611cSGleb Smirnoff * For all others, use the hash table. 19825d0c611cSGleb Smirnoff */ 19835d0c611cSGleb Smirnoff if (ctx->nperf < VTE_RXFILT_PERFECT_CNT) { 19845d0c611cSGleb Smirnoff eaddr = LLADDR(sdl); 19855d0c611cSGleb Smirnoff ctx->rxfilt_perf[ctx->nperf][0] = eaddr[1] << 8 | eaddr[0]; 19865d0c611cSGleb Smirnoff ctx->rxfilt_perf[ctx->nperf][1] = eaddr[3] << 8 | eaddr[2]; 19875d0c611cSGleb Smirnoff ctx->rxfilt_perf[ctx->nperf][2] = eaddr[5] << 8 | eaddr[4]; 19885d0c611cSGleb Smirnoff ctx->nperf++; 19895d0c611cSGleb Smirnoff 19905d0c611cSGleb Smirnoff return (1); 19915d0c611cSGleb Smirnoff } 19925d0c611cSGleb Smirnoff crc = ether_crc32_be(LLADDR(sdl), ETHER_ADDR_LEN); 19935d0c611cSGleb Smirnoff ctx->mchash[crc >> 30] |= 1 << ((crc >> 26) & 0x0F); 19945d0c611cSGleb Smirnoff 19955d0c611cSGleb Smirnoff return (1); 19965d0c611cSGleb Smirnoff } 19975d0c611cSGleb Smirnoff 19982608aefcSPyun YongHyeon static void 19992608aefcSPyun YongHyeon vte_rxfilter(struct vte_softc *sc) 20002608aefcSPyun YongHyeon { 20013486b835SJustin Hibbits if_t ifp; 20025d0c611cSGleb Smirnoff struct vte_maddr_ctx ctx; 20035d0c611cSGleb Smirnoff uint16_t mcr; 20045d0c611cSGleb Smirnoff int i; 20052608aefcSPyun YongHyeon 20062608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 20072608aefcSPyun YongHyeon 20082608aefcSPyun YongHyeon ifp = sc->vte_ifp; 20092608aefcSPyun YongHyeon 20105d0c611cSGleb Smirnoff bzero(ctx.mchash, sizeof(ctx.mchash)); 20112608aefcSPyun YongHyeon for (i = 0; i < VTE_RXFILT_PERFECT_CNT; i++) { 20125d0c611cSGleb Smirnoff ctx.rxfilt_perf[i][0] = 0xFFFF; 20135d0c611cSGleb Smirnoff ctx.rxfilt_perf[i][1] = 0xFFFF; 20145d0c611cSGleb Smirnoff ctx.rxfilt_perf[i][2] = 0xFFFF; 20152608aefcSPyun YongHyeon } 20165d0c611cSGleb Smirnoff ctx.nperf = 0; 20172608aefcSPyun YongHyeon 20182608aefcSPyun YongHyeon mcr = CSR_READ_2(sc, VTE_MCR0); 2019d8f226b6SPyun YongHyeon mcr &= ~(MCR0_PROMISC | MCR0_MULTICAST); 2020d8f226b6SPyun YongHyeon mcr |= MCR0_BROADCAST_DIS; 20213486b835SJustin Hibbits if ((if_getflags(ifp) & IFF_BROADCAST) != 0) 2022d8f226b6SPyun YongHyeon mcr &= ~MCR0_BROADCAST_DIS; 20233486b835SJustin Hibbits if ((if_getflags(ifp) & (IFF_PROMISC | IFF_ALLMULTI)) != 0) { 20243486b835SJustin Hibbits if ((if_getflags(ifp) & IFF_PROMISC) != 0) 20252608aefcSPyun YongHyeon mcr |= MCR0_PROMISC; 20263486b835SJustin Hibbits if ((if_getflags(ifp) & IFF_ALLMULTI) != 0) 20272608aefcSPyun YongHyeon mcr |= MCR0_MULTICAST; 20285d0c611cSGleb Smirnoff ctx.mchash[0] = 0xFFFF; 20295d0c611cSGleb Smirnoff ctx.mchash[1] = 0xFFFF; 20305d0c611cSGleb Smirnoff ctx.mchash[2] = 0xFFFF; 20315d0c611cSGleb Smirnoff ctx.mchash[3] = 0xFFFF; 20322608aefcSPyun YongHyeon goto chipit; 20332608aefcSPyun YongHyeon } 20342608aefcSPyun YongHyeon 20355d0c611cSGleb Smirnoff if_foreach_llmaddr(ifp, vte_hash_maddr, &ctx); 20365d0c611cSGleb Smirnoff if (ctx.mchash[0] != 0 || ctx.mchash[1] != 0 || 20375d0c611cSGleb Smirnoff ctx.mchash[2] != 0 || ctx.mchash[3] != 0) 20382608aefcSPyun YongHyeon mcr |= MCR0_MULTICAST; 20392608aefcSPyun YongHyeon 20402608aefcSPyun YongHyeon chipit: 20412608aefcSPyun YongHyeon /* Program multicast hash table. */ 20425d0c611cSGleb Smirnoff CSR_WRITE_2(sc, VTE_MAR0, ctx.mchash[0]); 20435d0c611cSGleb Smirnoff CSR_WRITE_2(sc, VTE_MAR1, ctx.mchash[1]); 20445d0c611cSGleb Smirnoff CSR_WRITE_2(sc, VTE_MAR2, ctx.mchash[2]); 20455d0c611cSGleb Smirnoff CSR_WRITE_2(sc, VTE_MAR3, ctx.mchash[3]); 20462608aefcSPyun YongHyeon /* Program perfect filter table. */ 20472608aefcSPyun YongHyeon for (i = 0; i < VTE_RXFILT_PERFECT_CNT; i++) { 20482608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_RXFILTER_PEEFECT_BASE + 8 * i + 0, 20495d0c611cSGleb Smirnoff ctx.rxfilt_perf[i][0]); 20502608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_RXFILTER_PEEFECT_BASE + 8 * i + 2, 20515d0c611cSGleb Smirnoff ctx.rxfilt_perf[i][1]); 20522608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_RXFILTER_PEEFECT_BASE + 8 * i + 4, 20535d0c611cSGleb Smirnoff ctx.rxfilt_perf[i][2]); 20542608aefcSPyun YongHyeon } 20552608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MCR0, mcr); 20562608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_MCR0); 20572608aefcSPyun YongHyeon } 20582608aefcSPyun YongHyeon 20592608aefcSPyun YongHyeon static int 20602608aefcSPyun YongHyeon sysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high) 20612608aefcSPyun YongHyeon { 20622608aefcSPyun YongHyeon int error, value; 20632608aefcSPyun YongHyeon 20642608aefcSPyun YongHyeon if (arg1 == NULL) 20652608aefcSPyun YongHyeon return (EINVAL); 20662608aefcSPyun YongHyeon value = *(int *)arg1; 20672608aefcSPyun YongHyeon error = sysctl_handle_int(oidp, &value, 0, req); 20682608aefcSPyun YongHyeon if (error || req->newptr == NULL) 20692608aefcSPyun YongHyeon return (error); 20702608aefcSPyun YongHyeon if (value < low || value > high) 20712608aefcSPyun YongHyeon return (EINVAL); 20722608aefcSPyun YongHyeon *(int *)arg1 = value; 20732608aefcSPyun YongHyeon 20742608aefcSPyun YongHyeon return (0); 20752608aefcSPyun YongHyeon } 20762608aefcSPyun YongHyeon 20772608aefcSPyun YongHyeon static int 20782608aefcSPyun YongHyeon sysctl_hw_vte_int_mod(SYSCTL_HANDLER_ARGS) 20792608aefcSPyun YongHyeon { 20802608aefcSPyun YongHyeon 20812608aefcSPyun YongHyeon return (sysctl_int_range(oidp, arg1, arg2, req, 20822608aefcSPyun YongHyeon VTE_IM_BUNDLE_MIN, VTE_IM_BUNDLE_MAX)); 20832608aefcSPyun YongHyeon } 2084