12608aefcSPyun YongHyeon /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 * 1052608aefcSPyun YongHyeon vte_fixup_rx(struct ifnet *, 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 *); 1132608aefcSPyun YongHyeon static int vte_ioctl(struct ifnet *, u_long, caddr_t); 11424b83dc6SGleb Smirnoff static uint64_t vte_get_counter(struct ifnet *, 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); 1192608aefcSPyun YongHyeon static int vte_mediachange(struct ifnet *); 1202608aefcSPyun YongHyeon static int vte_mediachange_locked(struct ifnet *); 1212608aefcSPyun YongHyeon static void vte_mediastatus(struct ifnet *, 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); 1292608aefcSPyun YongHyeon static void vte_start(struct ifnet *); 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 1672608aefcSPyun YongHyeon static devclass_t vte_devclass; 1682608aefcSPyun YongHyeon 1692608aefcSPyun YongHyeon DRIVER_MODULE(vte, pci, vte_driver, vte_devclass, 0, 0); 1702608aefcSPyun YongHyeon DRIVER_MODULE(miibus, vte, miibus_driver, miibus_devclass, 0, 0); 1712608aefcSPyun YongHyeon 1722608aefcSPyun YongHyeon static int 1732608aefcSPyun YongHyeon vte_miibus_readreg(device_t dev, int phy, int reg) 1742608aefcSPyun YongHyeon { 1752608aefcSPyun YongHyeon struct vte_softc *sc; 1762608aefcSPyun YongHyeon int i; 1772608aefcSPyun YongHyeon 1782608aefcSPyun YongHyeon sc = device_get_softc(dev); 1792608aefcSPyun YongHyeon 1802608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MMDIO, MMDIO_READ | 1812608aefcSPyun YongHyeon (phy << MMDIO_PHY_ADDR_SHIFT) | (reg << MMDIO_REG_ADDR_SHIFT)); 1822608aefcSPyun YongHyeon for (i = VTE_PHY_TIMEOUT; i > 0; i--) { 1832608aefcSPyun YongHyeon DELAY(5); 1842608aefcSPyun YongHyeon if ((CSR_READ_2(sc, VTE_MMDIO) & MMDIO_READ) == 0) 1852608aefcSPyun YongHyeon break; 1862608aefcSPyun YongHyeon } 1872608aefcSPyun YongHyeon 1882608aefcSPyun YongHyeon if (i == 0) { 1892608aefcSPyun YongHyeon device_printf(sc->vte_dev, "phy read timeout : %d\n", reg); 1902608aefcSPyun YongHyeon return (0); 1912608aefcSPyun YongHyeon } 1922608aefcSPyun YongHyeon 1932608aefcSPyun YongHyeon return (CSR_READ_2(sc, VTE_MMRD)); 1942608aefcSPyun YongHyeon } 1952608aefcSPyun YongHyeon 1962608aefcSPyun YongHyeon static int 1972608aefcSPyun YongHyeon vte_miibus_writereg(device_t dev, int phy, int reg, int val) 1982608aefcSPyun YongHyeon { 1992608aefcSPyun YongHyeon struct vte_softc *sc; 2002608aefcSPyun YongHyeon int i; 2012608aefcSPyun YongHyeon 2022608aefcSPyun YongHyeon sc = device_get_softc(dev); 2032608aefcSPyun YongHyeon 2042608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MMWD, val); 2052608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MMDIO, MMDIO_WRITE | 2062608aefcSPyun YongHyeon (phy << MMDIO_PHY_ADDR_SHIFT) | (reg << MMDIO_REG_ADDR_SHIFT)); 2072608aefcSPyun YongHyeon for (i = VTE_PHY_TIMEOUT; i > 0; i--) { 2082608aefcSPyun YongHyeon DELAY(5); 2092608aefcSPyun YongHyeon if ((CSR_READ_2(sc, VTE_MMDIO) & MMDIO_WRITE) == 0) 2102608aefcSPyun YongHyeon break; 2112608aefcSPyun YongHyeon } 2122608aefcSPyun YongHyeon 2132608aefcSPyun YongHyeon if (i == 0) 2142608aefcSPyun YongHyeon device_printf(sc->vte_dev, "phy write timeout : %d\n", reg); 2152608aefcSPyun YongHyeon 2162608aefcSPyun YongHyeon return (0); 2172608aefcSPyun YongHyeon } 2182608aefcSPyun YongHyeon 2192608aefcSPyun YongHyeon static void 2202608aefcSPyun YongHyeon vte_miibus_statchg(device_t dev) 2212608aefcSPyun YongHyeon { 2222608aefcSPyun YongHyeon struct vte_softc *sc; 2232608aefcSPyun YongHyeon struct mii_data *mii; 2242608aefcSPyun YongHyeon struct ifnet *ifp; 2252608aefcSPyun YongHyeon uint16_t val; 2262608aefcSPyun YongHyeon 2272608aefcSPyun YongHyeon sc = device_get_softc(dev); 2282608aefcSPyun YongHyeon 2292608aefcSPyun YongHyeon mii = device_get_softc(sc->vte_miibus); 2302608aefcSPyun YongHyeon ifp = sc->vte_ifp; 2312608aefcSPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 2322608aefcSPyun YongHyeon return; 2332608aefcSPyun YongHyeon 2342608aefcSPyun YongHyeon sc->vte_flags &= ~VTE_FLAG_LINK; 2352608aefcSPyun YongHyeon if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == 2362608aefcSPyun YongHyeon (IFM_ACTIVE | IFM_AVALID)) { 2372608aefcSPyun YongHyeon switch (IFM_SUBTYPE(mii->mii_media_active)) { 2382608aefcSPyun YongHyeon case IFM_10_T: 2392608aefcSPyun YongHyeon case IFM_100_TX: 2402608aefcSPyun YongHyeon sc->vte_flags |= VTE_FLAG_LINK; 2412608aefcSPyun YongHyeon break; 2422608aefcSPyun YongHyeon default: 2432608aefcSPyun YongHyeon break; 2442608aefcSPyun YongHyeon } 2452608aefcSPyun YongHyeon } 2462608aefcSPyun YongHyeon 2472608aefcSPyun YongHyeon /* Stop RX/TX MACs. */ 2482608aefcSPyun YongHyeon vte_stop_mac(sc); 2492608aefcSPyun YongHyeon /* Program MACs with resolved duplex and flow control. */ 2502608aefcSPyun YongHyeon if ((sc->vte_flags & VTE_FLAG_LINK) != 0) { 2512608aefcSPyun YongHyeon /* 2522608aefcSPyun YongHyeon * Timer waiting time : (63 + TIMER * 64) MII clock. 2532608aefcSPyun YongHyeon * MII clock : 25MHz(100Mbps) or 2.5MHz(10Mbps). 2542608aefcSPyun YongHyeon */ 2552608aefcSPyun YongHyeon if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) 2562608aefcSPyun YongHyeon val = 18 << VTE_IM_TIMER_SHIFT; 2572608aefcSPyun YongHyeon else 2582608aefcSPyun YongHyeon val = 1 << VTE_IM_TIMER_SHIFT; 2592608aefcSPyun YongHyeon val |= sc->vte_int_rx_mod << VTE_IM_BUNDLE_SHIFT; 2602608aefcSPyun YongHyeon /* 48.6us for 100Mbps, 50.8us for 10Mbps */ 2612608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MRICR, val); 2622608aefcSPyun YongHyeon 2632608aefcSPyun YongHyeon if (IFM_SUBTYPE(mii->mii_media_active) == IFM_100_TX) 2642608aefcSPyun YongHyeon val = 18 << VTE_IM_TIMER_SHIFT; 2652608aefcSPyun YongHyeon else 2662608aefcSPyun YongHyeon val = 1 << VTE_IM_TIMER_SHIFT; 2672608aefcSPyun YongHyeon val |= sc->vte_int_tx_mod << VTE_IM_BUNDLE_SHIFT; 2682608aefcSPyun YongHyeon /* 48.6us for 100Mbps, 50.8us for 10Mbps */ 2692608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MTICR, val); 2702608aefcSPyun YongHyeon 2712608aefcSPyun YongHyeon vte_mac_config(sc); 2722608aefcSPyun YongHyeon vte_start_mac(sc); 2732608aefcSPyun YongHyeon } 2742608aefcSPyun YongHyeon } 2752608aefcSPyun YongHyeon 2762608aefcSPyun YongHyeon static void 2772608aefcSPyun YongHyeon vte_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) 2782608aefcSPyun YongHyeon { 2792608aefcSPyun YongHyeon struct vte_softc *sc; 2802608aefcSPyun YongHyeon struct mii_data *mii; 2812608aefcSPyun YongHyeon 2822608aefcSPyun YongHyeon sc = ifp->if_softc; 2832608aefcSPyun YongHyeon VTE_LOCK(sc); 2842608aefcSPyun YongHyeon if ((ifp->if_flags & IFF_UP) == 0) { 2852608aefcSPyun YongHyeon VTE_UNLOCK(sc); 2862608aefcSPyun YongHyeon return; 2872608aefcSPyun YongHyeon } 2882608aefcSPyun YongHyeon mii = device_get_softc(sc->vte_miibus); 2892608aefcSPyun YongHyeon 2902608aefcSPyun YongHyeon mii_pollstat(mii); 2912608aefcSPyun YongHyeon ifmr->ifm_status = mii->mii_media_status; 2922608aefcSPyun YongHyeon ifmr->ifm_active = mii->mii_media_active; 29357c81d92SPyun YongHyeon VTE_UNLOCK(sc); 2942608aefcSPyun YongHyeon } 2952608aefcSPyun YongHyeon 2962608aefcSPyun YongHyeon static int 2972608aefcSPyun YongHyeon vte_mediachange(struct ifnet *ifp) 2982608aefcSPyun YongHyeon { 2992608aefcSPyun YongHyeon struct vte_softc *sc; 3002608aefcSPyun YongHyeon int error; 3012608aefcSPyun YongHyeon 3022608aefcSPyun YongHyeon sc = ifp->if_softc; 3032608aefcSPyun YongHyeon VTE_LOCK(sc); 3042608aefcSPyun YongHyeon error = vte_mediachange_locked(ifp); 3052608aefcSPyun YongHyeon VTE_UNLOCK(sc); 3062608aefcSPyun YongHyeon return (error); 3072608aefcSPyun YongHyeon } 3082608aefcSPyun YongHyeon 3092608aefcSPyun YongHyeon static int 3102608aefcSPyun YongHyeon vte_mediachange_locked(struct ifnet *ifp) 3112608aefcSPyun YongHyeon { 3122608aefcSPyun YongHyeon struct vte_softc *sc; 3132608aefcSPyun YongHyeon struct mii_data *mii; 3142608aefcSPyun YongHyeon struct mii_softc *miisc; 3152608aefcSPyun YongHyeon int error; 3162608aefcSPyun YongHyeon 3172608aefcSPyun YongHyeon sc = ifp->if_softc; 3182608aefcSPyun YongHyeon mii = device_get_softc(sc->vte_miibus); 3192608aefcSPyun YongHyeon LIST_FOREACH(miisc, &mii->mii_phys, mii_list) 3203fcb7a53SMarius Strobl PHY_RESET(miisc); 3212608aefcSPyun YongHyeon error = mii_mediachg(mii); 3222608aefcSPyun YongHyeon 3232608aefcSPyun YongHyeon return (error); 3242608aefcSPyun YongHyeon } 3252608aefcSPyun YongHyeon 3262608aefcSPyun YongHyeon static const struct vte_ident * 3272608aefcSPyun YongHyeon vte_find_ident(device_t dev) 3282608aefcSPyun YongHyeon { 3292608aefcSPyun YongHyeon const struct vte_ident *ident; 3302608aefcSPyun YongHyeon uint16_t vendor, devid; 3312608aefcSPyun YongHyeon 3322608aefcSPyun YongHyeon vendor = pci_get_vendor(dev); 3332608aefcSPyun YongHyeon devid = pci_get_device(dev); 3342608aefcSPyun YongHyeon for (ident = vte_ident_table; ident->name != NULL; ident++) { 3352608aefcSPyun YongHyeon if (vendor == ident->vendorid && devid == ident->deviceid) 3362608aefcSPyun YongHyeon return (ident); 3372608aefcSPyun YongHyeon } 3382608aefcSPyun YongHyeon 3392608aefcSPyun YongHyeon return (NULL); 3402608aefcSPyun YongHyeon } 3412608aefcSPyun YongHyeon 3422608aefcSPyun YongHyeon static int 3432608aefcSPyun YongHyeon vte_probe(device_t dev) 3442608aefcSPyun YongHyeon { 3452608aefcSPyun YongHyeon const struct vte_ident *ident; 3462608aefcSPyun YongHyeon 3472608aefcSPyun YongHyeon ident = vte_find_ident(dev); 3482608aefcSPyun YongHyeon if (ident != NULL) { 3492608aefcSPyun YongHyeon device_set_desc(dev, ident->name); 3502608aefcSPyun YongHyeon return (BUS_PROBE_DEFAULT); 3512608aefcSPyun YongHyeon } 3522608aefcSPyun YongHyeon 3532608aefcSPyun YongHyeon return (ENXIO); 3542608aefcSPyun YongHyeon } 3552608aefcSPyun YongHyeon 3562608aefcSPyun YongHyeon static void 3572608aefcSPyun YongHyeon vte_get_macaddr(struct vte_softc *sc) 3582608aefcSPyun YongHyeon { 3592608aefcSPyun YongHyeon uint16_t mid; 3602608aefcSPyun YongHyeon 3612608aefcSPyun YongHyeon /* 3622608aefcSPyun YongHyeon * It seems there is no way to reload station address and 3632608aefcSPyun YongHyeon * it is supposed to be set by BIOS. 3642608aefcSPyun YongHyeon */ 3652608aefcSPyun YongHyeon mid = CSR_READ_2(sc, VTE_MID0L); 3662608aefcSPyun YongHyeon sc->vte_eaddr[0] = (mid >> 0) & 0xFF; 3672608aefcSPyun YongHyeon sc->vte_eaddr[1] = (mid >> 8) & 0xFF; 3682608aefcSPyun YongHyeon mid = CSR_READ_2(sc, VTE_MID0M); 3692608aefcSPyun YongHyeon sc->vte_eaddr[2] = (mid >> 0) & 0xFF; 3702608aefcSPyun YongHyeon sc->vte_eaddr[3] = (mid >> 8) & 0xFF; 3712608aefcSPyun YongHyeon mid = CSR_READ_2(sc, VTE_MID0H); 3722608aefcSPyun YongHyeon sc->vte_eaddr[4] = (mid >> 0) & 0xFF; 3732608aefcSPyun YongHyeon sc->vte_eaddr[5] = (mid >> 8) & 0xFF; 3742608aefcSPyun YongHyeon } 3752608aefcSPyun YongHyeon 3762608aefcSPyun YongHyeon static int 3772608aefcSPyun YongHyeon vte_attach(device_t dev) 3782608aefcSPyun YongHyeon { 3792608aefcSPyun YongHyeon struct vte_softc *sc; 3802608aefcSPyun YongHyeon struct ifnet *ifp; 3812608aefcSPyun YongHyeon uint16_t macid; 3822608aefcSPyun YongHyeon int error, rid; 3832608aefcSPyun YongHyeon 3842608aefcSPyun YongHyeon error = 0; 3852608aefcSPyun YongHyeon sc = device_get_softc(dev); 3862608aefcSPyun YongHyeon sc->vte_dev = dev; 3872608aefcSPyun YongHyeon 3882608aefcSPyun YongHyeon mtx_init(&sc->vte_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, 3892608aefcSPyun YongHyeon MTX_DEF); 3902608aefcSPyun YongHyeon callout_init_mtx(&sc->vte_tick_ch, &sc->vte_mtx, 0); 3912608aefcSPyun YongHyeon sc->vte_ident = vte_find_ident(dev); 3922608aefcSPyun YongHyeon 3932608aefcSPyun YongHyeon /* Map the device. */ 3942608aefcSPyun YongHyeon pci_enable_busmaster(dev); 3952608aefcSPyun YongHyeon sc->vte_res_id = PCIR_BAR(1); 3962608aefcSPyun YongHyeon sc->vte_res_type = SYS_RES_MEMORY; 3972608aefcSPyun YongHyeon sc->vte_res = bus_alloc_resource_any(dev, sc->vte_res_type, 3982608aefcSPyun YongHyeon &sc->vte_res_id, RF_ACTIVE); 3992608aefcSPyun YongHyeon if (sc->vte_res == NULL) { 4002608aefcSPyun YongHyeon sc->vte_res_id = PCIR_BAR(0); 4012608aefcSPyun YongHyeon sc->vte_res_type = SYS_RES_IOPORT; 4022608aefcSPyun YongHyeon sc->vte_res = bus_alloc_resource_any(dev, sc->vte_res_type, 4032608aefcSPyun YongHyeon &sc->vte_res_id, RF_ACTIVE); 4042608aefcSPyun YongHyeon if (sc->vte_res == NULL) { 4052608aefcSPyun YongHyeon device_printf(dev, "cannot map memory/ports.\n"); 4062608aefcSPyun YongHyeon mtx_destroy(&sc->vte_mtx); 4072608aefcSPyun YongHyeon return (ENXIO); 4082608aefcSPyun YongHyeon } 4092608aefcSPyun YongHyeon } 4102608aefcSPyun YongHyeon if (bootverbose) { 4112608aefcSPyun YongHyeon device_printf(dev, "using %s space register mapping\n", 4122608aefcSPyun YongHyeon sc->vte_res_type == SYS_RES_MEMORY ? "memory" : "I/O"); 4132608aefcSPyun YongHyeon device_printf(dev, "MAC Identifier : 0x%04x\n", 4142608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_MACID)); 4152608aefcSPyun YongHyeon macid = CSR_READ_2(sc, VTE_MACID_REV); 4162608aefcSPyun YongHyeon device_printf(dev, "MAC Id. 0x%02x, Rev. 0x%02x\n", 4172608aefcSPyun YongHyeon (macid & VTE_MACID_MASK) >> VTE_MACID_SHIFT, 4182608aefcSPyun YongHyeon (macid & VTE_MACID_REV_MASK) >> VTE_MACID_REV_SHIFT); 4192608aefcSPyun YongHyeon } 4202608aefcSPyun YongHyeon 4212608aefcSPyun YongHyeon rid = 0; 4222608aefcSPyun YongHyeon sc->vte_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 4232608aefcSPyun YongHyeon RF_SHAREABLE | RF_ACTIVE); 4242608aefcSPyun YongHyeon if (sc->vte_irq == NULL) { 4252608aefcSPyun YongHyeon device_printf(dev, "cannot allocate IRQ resources.\n"); 4262608aefcSPyun YongHyeon error = ENXIO; 4272608aefcSPyun YongHyeon goto fail; 4282608aefcSPyun YongHyeon } 4292608aefcSPyun YongHyeon 4302608aefcSPyun YongHyeon /* Reset the ethernet controller. */ 4312608aefcSPyun YongHyeon vte_reset(sc); 4322608aefcSPyun YongHyeon 4339dda5c8fSPyun YongHyeon if ((error = vte_dma_alloc(sc)) != 0) 4342608aefcSPyun YongHyeon goto fail; 4352608aefcSPyun YongHyeon 4362608aefcSPyun YongHyeon /* Create device sysctl node. */ 4372608aefcSPyun YongHyeon vte_sysctl_node(sc); 4382608aefcSPyun YongHyeon 4392608aefcSPyun YongHyeon /* Load station address. */ 4402608aefcSPyun YongHyeon vte_get_macaddr(sc); 4412608aefcSPyun YongHyeon 4422608aefcSPyun YongHyeon ifp = sc->vte_ifp = if_alloc(IFT_ETHER); 4432608aefcSPyun YongHyeon if (ifp == NULL) { 4442608aefcSPyun YongHyeon device_printf(dev, "cannot allocate ifnet structure.\n"); 4452608aefcSPyun YongHyeon error = ENXIO; 4462608aefcSPyun YongHyeon goto fail; 4472608aefcSPyun YongHyeon } 4482608aefcSPyun YongHyeon 4492608aefcSPyun YongHyeon ifp->if_softc = sc; 4502608aefcSPyun YongHyeon if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 4512608aefcSPyun YongHyeon ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 4522608aefcSPyun YongHyeon ifp->if_ioctl = vte_ioctl; 4532608aefcSPyun YongHyeon ifp->if_start = vte_start; 4542608aefcSPyun YongHyeon ifp->if_init = vte_init; 45524b83dc6SGleb Smirnoff ifp->if_get_counter = vte_get_counter; 4562608aefcSPyun YongHyeon ifp->if_snd.ifq_drv_maxlen = VTE_TX_RING_CNT - 1; 4572608aefcSPyun YongHyeon IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen); 4582608aefcSPyun YongHyeon IFQ_SET_READY(&ifp->if_snd); 4592608aefcSPyun YongHyeon 4602608aefcSPyun YongHyeon /* 4612608aefcSPyun YongHyeon * Set up MII bus. 4622608aefcSPyun YongHyeon * BIOS would have initialized VTE_MPSCCR to catch PHY 4632608aefcSPyun YongHyeon * status changes so driver may be able to extract 4642608aefcSPyun YongHyeon * configured PHY address. Since it's common to see BIOS 4652608aefcSPyun YongHyeon * fails to initialize the register(including the sample 4662608aefcSPyun YongHyeon * board I have), let mii(4) probe it. This is more 4672608aefcSPyun YongHyeon * reliable than relying on BIOS's initialization. 4682608aefcSPyun YongHyeon * 4692608aefcSPyun YongHyeon * Advertising flow control capability to mii(4) was 4702608aefcSPyun YongHyeon * intentionally disabled due to severe problems in TX 4712608aefcSPyun YongHyeon * pause frame generation. See vte_rxeof() for more 4722608aefcSPyun YongHyeon * details. 4732608aefcSPyun YongHyeon */ 4742608aefcSPyun YongHyeon error = mii_attach(dev, &sc->vte_miibus, ifp, vte_mediachange, 4752608aefcSPyun YongHyeon vte_mediastatus, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); 4762608aefcSPyun YongHyeon if (error != 0) { 4772608aefcSPyun YongHyeon device_printf(dev, "attaching PHYs failed\n"); 4782608aefcSPyun YongHyeon goto fail; 4792608aefcSPyun YongHyeon } 4802608aefcSPyun YongHyeon 4812608aefcSPyun YongHyeon ether_ifattach(ifp, sc->vte_eaddr); 4822608aefcSPyun YongHyeon 4832608aefcSPyun YongHyeon /* VLAN capability setup. */ 4842608aefcSPyun YongHyeon ifp->if_capabilities |= IFCAP_VLAN_MTU; 4852608aefcSPyun YongHyeon ifp->if_capenable = ifp->if_capabilities; 4862608aefcSPyun YongHyeon /* Tell the upper layer we support VLAN over-sized frames. */ 4872608aefcSPyun YongHyeon ifp->if_hdrlen = sizeof(struct ether_vlan_header); 4882608aefcSPyun YongHyeon 4892608aefcSPyun YongHyeon error = bus_setup_intr(dev, sc->vte_irq, INTR_TYPE_NET | INTR_MPSAFE, 4902608aefcSPyun YongHyeon NULL, vte_intr, sc, &sc->vte_intrhand); 4912608aefcSPyun YongHyeon if (error != 0) { 4922608aefcSPyun YongHyeon device_printf(dev, "could not set up interrupt handler.\n"); 4932608aefcSPyun YongHyeon ether_ifdetach(ifp); 4942608aefcSPyun YongHyeon goto fail; 4952608aefcSPyun YongHyeon } 4962608aefcSPyun YongHyeon 4972608aefcSPyun YongHyeon fail: 4982608aefcSPyun YongHyeon if (error != 0) 4992608aefcSPyun YongHyeon vte_detach(dev); 5002608aefcSPyun YongHyeon 5012608aefcSPyun YongHyeon return (error); 5022608aefcSPyun YongHyeon } 5032608aefcSPyun YongHyeon 5042608aefcSPyun YongHyeon static int 5052608aefcSPyun YongHyeon vte_detach(device_t dev) 5062608aefcSPyun YongHyeon { 5072608aefcSPyun YongHyeon struct vte_softc *sc; 5082608aefcSPyun YongHyeon struct ifnet *ifp; 5092608aefcSPyun YongHyeon 5102608aefcSPyun YongHyeon sc = device_get_softc(dev); 5112608aefcSPyun YongHyeon 5122608aefcSPyun YongHyeon ifp = sc->vte_ifp; 5132608aefcSPyun YongHyeon if (device_is_attached(dev)) { 5142608aefcSPyun YongHyeon VTE_LOCK(sc); 5152608aefcSPyun YongHyeon vte_stop(sc); 5162608aefcSPyun YongHyeon VTE_UNLOCK(sc); 5172608aefcSPyun YongHyeon callout_drain(&sc->vte_tick_ch); 5182608aefcSPyun YongHyeon ether_ifdetach(ifp); 5192608aefcSPyun YongHyeon } 5202608aefcSPyun YongHyeon 5212608aefcSPyun YongHyeon if (sc->vte_miibus != NULL) { 5222608aefcSPyun YongHyeon device_delete_child(dev, sc->vte_miibus); 5232608aefcSPyun YongHyeon sc->vte_miibus = NULL; 5242608aefcSPyun YongHyeon } 5252608aefcSPyun YongHyeon bus_generic_detach(dev); 5262608aefcSPyun YongHyeon 5272608aefcSPyun YongHyeon if (sc->vte_intrhand != NULL) { 5282608aefcSPyun YongHyeon bus_teardown_intr(dev, sc->vte_irq, sc->vte_intrhand); 5292608aefcSPyun YongHyeon sc->vte_intrhand = NULL; 5302608aefcSPyun YongHyeon } 5312608aefcSPyun YongHyeon if (sc->vte_irq != NULL) { 5322608aefcSPyun YongHyeon bus_release_resource(dev, SYS_RES_IRQ, 0, sc->vte_irq); 5332608aefcSPyun YongHyeon sc->vte_irq = NULL; 5342608aefcSPyun YongHyeon } 5352608aefcSPyun YongHyeon if (sc->vte_res != NULL) { 5362608aefcSPyun YongHyeon bus_release_resource(dev, sc->vte_res_type, sc->vte_res_id, 5372608aefcSPyun YongHyeon sc->vte_res); 5382608aefcSPyun YongHyeon sc->vte_res = NULL; 5392608aefcSPyun YongHyeon } 5402608aefcSPyun YongHyeon if (ifp != NULL) { 5412608aefcSPyun YongHyeon if_free(ifp); 5422608aefcSPyun YongHyeon sc->vte_ifp = NULL; 5432608aefcSPyun YongHyeon } 5442608aefcSPyun YongHyeon vte_dma_free(sc); 5452608aefcSPyun YongHyeon mtx_destroy(&sc->vte_mtx); 5462608aefcSPyun YongHyeon 5472608aefcSPyun YongHyeon return (0); 5482608aefcSPyun YongHyeon } 5492608aefcSPyun YongHyeon 5502608aefcSPyun YongHyeon #define VTE_SYSCTL_STAT_ADD32(c, h, n, p, d) \ 5512608aefcSPyun YongHyeon SYSCTL_ADD_UINT(c, h, OID_AUTO, n, CTLFLAG_RD, p, 0, d) 5522608aefcSPyun YongHyeon 5532608aefcSPyun YongHyeon static void 5542608aefcSPyun YongHyeon vte_sysctl_node(struct vte_softc *sc) 5552608aefcSPyun YongHyeon { 5562608aefcSPyun YongHyeon struct sysctl_ctx_list *ctx; 5572608aefcSPyun YongHyeon struct sysctl_oid_list *child, *parent; 5582608aefcSPyun YongHyeon struct sysctl_oid *tree; 5592608aefcSPyun YongHyeon struct vte_hw_stats *stats; 5602608aefcSPyun YongHyeon int error; 5612608aefcSPyun YongHyeon 5622608aefcSPyun YongHyeon stats = &sc->vte_stats; 5632608aefcSPyun YongHyeon ctx = device_get_sysctl_ctx(sc->vte_dev); 5642608aefcSPyun YongHyeon child = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->vte_dev)); 5652608aefcSPyun YongHyeon 5662608aefcSPyun YongHyeon SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "int_rx_mod", 567*7029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 568*7029da5cSPawel Biernacki &sc->vte_int_rx_mod, 0, sysctl_hw_vte_int_mod, "I", 569*7029da5cSPawel Biernacki "vte RX interrupt moderation"); 5702608aefcSPyun YongHyeon SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "int_tx_mod", 571*7029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, 572*7029da5cSPawel Biernacki &sc->vte_int_tx_mod, 0, sysctl_hw_vte_int_mod, "I", 573*7029da5cSPawel Biernacki "vte TX interrupt moderation"); 5742608aefcSPyun YongHyeon /* Pull in device tunables. */ 5752608aefcSPyun YongHyeon sc->vte_int_rx_mod = VTE_IM_RX_BUNDLE_DEFAULT; 5762608aefcSPyun YongHyeon error = resource_int_value(device_get_name(sc->vte_dev), 5772608aefcSPyun YongHyeon device_get_unit(sc->vte_dev), "int_rx_mod", &sc->vte_int_rx_mod); 5782608aefcSPyun YongHyeon if (error == 0) { 5792608aefcSPyun YongHyeon if (sc->vte_int_rx_mod < VTE_IM_BUNDLE_MIN || 5802608aefcSPyun YongHyeon sc->vte_int_rx_mod > VTE_IM_BUNDLE_MAX) { 5812608aefcSPyun YongHyeon device_printf(sc->vte_dev, "int_rx_mod value out of " 5822608aefcSPyun YongHyeon "range; using default: %d\n", 5832608aefcSPyun YongHyeon VTE_IM_RX_BUNDLE_DEFAULT); 5842608aefcSPyun YongHyeon sc->vte_int_rx_mod = VTE_IM_RX_BUNDLE_DEFAULT; 5852608aefcSPyun YongHyeon } 5862608aefcSPyun YongHyeon } 5872608aefcSPyun YongHyeon 5882608aefcSPyun YongHyeon sc->vte_int_tx_mod = VTE_IM_TX_BUNDLE_DEFAULT; 5892608aefcSPyun YongHyeon error = resource_int_value(device_get_name(sc->vte_dev), 5902608aefcSPyun YongHyeon device_get_unit(sc->vte_dev), "int_tx_mod", &sc->vte_int_tx_mod); 5912608aefcSPyun YongHyeon if (error == 0) { 5922608aefcSPyun YongHyeon if (sc->vte_int_tx_mod < VTE_IM_BUNDLE_MIN || 5932608aefcSPyun YongHyeon sc->vte_int_tx_mod > VTE_IM_BUNDLE_MAX) { 5942608aefcSPyun YongHyeon device_printf(sc->vte_dev, "int_tx_mod value out of " 5952608aefcSPyun YongHyeon "range; using default: %d\n", 5962608aefcSPyun YongHyeon VTE_IM_TX_BUNDLE_DEFAULT); 5972608aefcSPyun YongHyeon sc->vte_int_tx_mod = VTE_IM_TX_BUNDLE_DEFAULT; 5982608aefcSPyun YongHyeon } 5992608aefcSPyun YongHyeon } 6002608aefcSPyun YongHyeon 601*7029da5cSPawel Biernacki tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "stats", 602*7029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "VTE statistics"); 6032608aefcSPyun YongHyeon parent = SYSCTL_CHILDREN(tree); 6042608aefcSPyun YongHyeon 6052608aefcSPyun YongHyeon /* RX statistics. */ 606*7029da5cSPawel Biernacki tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "rx", 607*7029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "RX MAC statistics"); 6082608aefcSPyun YongHyeon child = SYSCTL_CHILDREN(tree); 6092608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "good_frames", 6102608aefcSPyun YongHyeon &stats->rx_frames, "Good frames"); 6112608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "good_bcast_frames", 6122608aefcSPyun YongHyeon &stats->rx_bcast_frames, "Good broadcast frames"); 6132608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "good_mcast_frames", 6142608aefcSPyun YongHyeon &stats->rx_mcast_frames, "Good multicast frames"); 6152608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "runt", 6162608aefcSPyun YongHyeon &stats->rx_runts, "Too short frames"); 6172608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "crc_errs", 6182608aefcSPyun YongHyeon &stats->rx_crcerrs, "CRC errors"); 6192608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "long_frames", 6202608aefcSPyun YongHyeon &stats->rx_long_frames, 6212608aefcSPyun YongHyeon "Frames that have longer length than maximum packet length"); 6222608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "fifo_full", 6232608aefcSPyun YongHyeon &stats->rx_fifo_full, "FIFO full"); 6242608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "desc_unavail", 6252608aefcSPyun YongHyeon &stats->rx_desc_unavail, "Descriptor unavailable frames"); 6262608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "pause_frames", 6272608aefcSPyun YongHyeon &stats->rx_pause_frames, "Pause control frames"); 6282608aefcSPyun YongHyeon 6292608aefcSPyun YongHyeon /* TX statistics. */ 630*7029da5cSPawel Biernacki tree = SYSCTL_ADD_NODE(ctx, parent, OID_AUTO, "tx", 631*7029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "TX MAC statistics"); 6322608aefcSPyun YongHyeon child = SYSCTL_CHILDREN(tree); 6332608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "good_frames", 6342608aefcSPyun YongHyeon &stats->tx_frames, "Good frames"); 6352608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "underruns", 6362608aefcSPyun YongHyeon &stats->tx_underruns, "FIFO underruns"); 6372608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "late_colls", 6382608aefcSPyun YongHyeon &stats->tx_late_colls, "Late collisions"); 6392608aefcSPyun YongHyeon VTE_SYSCTL_STAT_ADD32(ctx, child, "pause_frames", 6402608aefcSPyun YongHyeon &stats->tx_pause_frames, "Pause control frames"); 6412608aefcSPyun YongHyeon } 6422608aefcSPyun YongHyeon 6432608aefcSPyun YongHyeon #undef VTE_SYSCTL_STAT_ADD32 6442608aefcSPyun YongHyeon 6452608aefcSPyun YongHyeon struct vte_dmamap_arg { 6462608aefcSPyun YongHyeon bus_addr_t vte_busaddr; 6472608aefcSPyun YongHyeon }; 6482608aefcSPyun YongHyeon 6492608aefcSPyun YongHyeon static void 6502608aefcSPyun YongHyeon vte_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 6512608aefcSPyun YongHyeon { 6522608aefcSPyun YongHyeon struct vte_dmamap_arg *ctx; 6532608aefcSPyun YongHyeon 6542608aefcSPyun YongHyeon if (error != 0) 6552608aefcSPyun YongHyeon return; 6562608aefcSPyun YongHyeon 6572608aefcSPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 6582608aefcSPyun YongHyeon 6592608aefcSPyun YongHyeon ctx = (struct vte_dmamap_arg *)arg; 6602608aefcSPyun YongHyeon ctx->vte_busaddr = segs[0].ds_addr; 6612608aefcSPyun YongHyeon } 6622608aefcSPyun YongHyeon 6632608aefcSPyun YongHyeon static int 6642608aefcSPyun YongHyeon vte_dma_alloc(struct vte_softc *sc) 6652608aefcSPyun YongHyeon { 6662608aefcSPyun YongHyeon struct vte_txdesc *txd; 6672608aefcSPyun YongHyeon struct vte_rxdesc *rxd; 6682608aefcSPyun YongHyeon struct vte_dmamap_arg ctx; 6692608aefcSPyun YongHyeon int error, i; 6702608aefcSPyun YongHyeon 6712608aefcSPyun YongHyeon /* Create parent DMA tag. */ 6722608aefcSPyun YongHyeon error = bus_dma_tag_create( 6732608aefcSPyun YongHyeon bus_get_dma_tag(sc->vte_dev), /* parent */ 6742608aefcSPyun YongHyeon 1, 0, /* alignment, boundary */ 6752608aefcSPyun YongHyeon BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 6762608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 6772608aefcSPyun YongHyeon NULL, NULL, /* filter, filterarg */ 6782608aefcSPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 6792608aefcSPyun YongHyeon 0, /* nsegments */ 6802608aefcSPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 6812608aefcSPyun YongHyeon 0, /* flags */ 6822608aefcSPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 6832608aefcSPyun YongHyeon &sc->vte_cdata.vte_parent_tag); 6842608aefcSPyun YongHyeon if (error != 0) { 6852608aefcSPyun YongHyeon device_printf(sc->vte_dev, 6862608aefcSPyun YongHyeon "could not create parent DMA tag.\n"); 6872608aefcSPyun YongHyeon goto fail; 6882608aefcSPyun YongHyeon } 6892608aefcSPyun YongHyeon 6902608aefcSPyun YongHyeon /* Create DMA tag for TX descriptor ring. */ 6912608aefcSPyun YongHyeon error = bus_dma_tag_create( 6922608aefcSPyun YongHyeon sc->vte_cdata.vte_parent_tag, /* parent */ 6932608aefcSPyun YongHyeon VTE_TX_RING_ALIGN, 0, /* alignment, boundary */ 6942608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 6952608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 6962608aefcSPyun YongHyeon NULL, NULL, /* filter, filterarg */ 6972608aefcSPyun YongHyeon VTE_TX_RING_SZ, /* maxsize */ 6982608aefcSPyun YongHyeon 1, /* nsegments */ 6992608aefcSPyun YongHyeon VTE_TX_RING_SZ, /* maxsegsize */ 7002608aefcSPyun YongHyeon 0, /* flags */ 7012608aefcSPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 7022608aefcSPyun YongHyeon &sc->vte_cdata.vte_tx_ring_tag); 7032608aefcSPyun YongHyeon if (error != 0) { 7042608aefcSPyun YongHyeon device_printf(sc->vte_dev, 7052608aefcSPyun YongHyeon "could not create TX ring DMA tag.\n"); 7062608aefcSPyun YongHyeon goto fail; 7072608aefcSPyun YongHyeon } 7082608aefcSPyun YongHyeon 7092608aefcSPyun YongHyeon /* Create DMA tag for RX free descriptor ring. */ 7102608aefcSPyun YongHyeon error = bus_dma_tag_create( 7112608aefcSPyun YongHyeon sc->vte_cdata.vte_parent_tag, /* parent */ 7122608aefcSPyun YongHyeon VTE_RX_RING_ALIGN, 0, /* alignment, boundary */ 7132608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 7142608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 7152608aefcSPyun YongHyeon NULL, NULL, /* filter, filterarg */ 7162608aefcSPyun YongHyeon VTE_RX_RING_SZ, /* maxsize */ 7172608aefcSPyun YongHyeon 1, /* nsegments */ 7182608aefcSPyun YongHyeon VTE_RX_RING_SZ, /* maxsegsize */ 7192608aefcSPyun YongHyeon 0, /* flags */ 7202608aefcSPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 7212608aefcSPyun YongHyeon &sc->vte_cdata.vte_rx_ring_tag); 7222608aefcSPyun YongHyeon if (error != 0) { 7232608aefcSPyun YongHyeon device_printf(sc->vte_dev, 7242608aefcSPyun YongHyeon "could not create RX ring DMA tag.\n"); 7252608aefcSPyun YongHyeon goto fail; 7262608aefcSPyun YongHyeon } 7272608aefcSPyun YongHyeon 7282608aefcSPyun YongHyeon /* Allocate DMA'able memory and load the DMA map for TX ring. */ 7292608aefcSPyun YongHyeon error = bus_dmamem_alloc(sc->vte_cdata.vte_tx_ring_tag, 7302608aefcSPyun YongHyeon (void **)&sc->vte_cdata.vte_tx_ring, 7312608aefcSPyun YongHyeon BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, 7322608aefcSPyun YongHyeon &sc->vte_cdata.vte_tx_ring_map); 7332608aefcSPyun YongHyeon if (error != 0) { 7342608aefcSPyun YongHyeon device_printf(sc->vte_dev, 7352608aefcSPyun YongHyeon "could not allocate DMA'able memory for TX ring.\n"); 7362608aefcSPyun YongHyeon goto fail; 7372608aefcSPyun YongHyeon } 7382608aefcSPyun YongHyeon ctx.vte_busaddr = 0; 7392608aefcSPyun YongHyeon error = bus_dmamap_load(sc->vte_cdata.vte_tx_ring_tag, 7402608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_map, sc->vte_cdata.vte_tx_ring, 7412608aefcSPyun YongHyeon VTE_TX_RING_SZ, vte_dmamap_cb, &ctx, 0); 7422608aefcSPyun YongHyeon if (error != 0 || ctx.vte_busaddr == 0) { 7432608aefcSPyun YongHyeon device_printf(sc->vte_dev, 7442608aefcSPyun YongHyeon "could not load DMA'able memory for TX ring.\n"); 7452608aefcSPyun YongHyeon goto fail; 7462608aefcSPyun YongHyeon } 7472608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_paddr = ctx.vte_busaddr; 7482608aefcSPyun YongHyeon 7492608aefcSPyun YongHyeon /* Allocate DMA'able memory and load the DMA map for RX ring. */ 7502608aefcSPyun YongHyeon error = bus_dmamem_alloc(sc->vte_cdata.vte_rx_ring_tag, 7512608aefcSPyun YongHyeon (void **)&sc->vte_cdata.vte_rx_ring, 7522608aefcSPyun YongHyeon BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, 7532608aefcSPyun YongHyeon &sc->vte_cdata.vte_rx_ring_map); 7542608aefcSPyun YongHyeon if (error != 0) { 7552608aefcSPyun YongHyeon device_printf(sc->vte_dev, 7562608aefcSPyun YongHyeon "could not allocate DMA'able memory for RX ring.\n"); 7572608aefcSPyun YongHyeon goto fail; 7582608aefcSPyun YongHyeon } 7592608aefcSPyun YongHyeon ctx.vte_busaddr = 0; 7602608aefcSPyun YongHyeon error = bus_dmamap_load(sc->vte_cdata.vte_rx_ring_tag, 7612608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_map, sc->vte_cdata.vte_rx_ring, 7622608aefcSPyun YongHyeon VTE_RX_RING_SZ, vte_dmamap_cb, &ctx, 0); 7632608aefcSPyun YongHyeon if (error != 0 || ctx.vte_busaddr == 0) { 7642608aefcSPyun YongHyeon device_printf(sc->vte_dev, 7652608aefcSPyun YongHyeon "could not load DMA'able memory for RX ring.\n"); 7662608aefcSPyun YongHyeon goto fail; 7672608aefcSPyun YongHyeon } 7682608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_paddr = ctx.vte_busaddr; 7692608aefcSPyun YongHyeon 7702608aefcSPyun YongHyeon /* Create TX buffer parent tag. */ 7712608aefcSPyun YongHyeon error = bus_dma_tag_create( 7722608aefcSPyun YongHyeon bus_get_dma_tag(sc->vte_dev), /* parent */ 7732608aefcSPyun YongHyeon 1, 0, /* alignment, boundary */ 7742608aefcSPyun YongHyeon BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ 7752608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 7762608aefcSPyun YongHyeon NULL, NULL, /* filter, filterarg */ 7772608aefcSPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsize */ 7782608aefcSPyun YongHyeon 0, /* nsegments */ 7792608aefcSPyun YongHyeon BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 7802608aefcSPyun YongHyeon 0, /* flags */ 7812608aefcSPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 7822608aefcSPyun YongHyeon &sc->vte_cdata.vte_buffer_tag); 7832608aefcSPyun YongHyeon if (error != 0) { 7842608aefcSPyun YongHyeon device_printf(sc->vte_dev, 7852608aefcSPyun YongHyeon "could not create parent buffer DMA tag.\n"); 7862608aefcSPyun YongHyeon goto fail; 7872608aefcSPyun YongHyeon } 7882608aefcSPyun YongHyeon 7892608aefcSPyun YongHyeon /* Create DMA tag for TX buffers. */ 7902608aefcSPyun YongHyeon error = bus_dma_tag_create( 7912608aefcSPyun YongHyeon sc->vte_cdata.vte_buffer_tag, /* parent */ 7922608aefcSPyun YongHyeon 1, 0, /* alignment, boundary */ 7932608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 7942608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 7952608aefcSPyun YongHyeon NULL, NULL, /* filter, filterarg */ 7962608aefcSPyun YongHyeon MCLBYTES, /* maxsize */ 7972608aefcSPyun YongHyeon 1, /* nsegments */ 7982608aefcSPyun YongHyeon MCLBYTES, /* maxsegsize */ 7992608aefcSPyun YongHyeon 0, /* flags */ 8002608aefcSPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 8012608aefcSPyun YongHyeon &sc->vte_cdata.vte_tx_tag); 8022608aefcSPyun YongHyeon if (error != 0) { 8032608aefcSPyun YongHyeon device_printf(sc->vte_dev, "could not create TX DMA tag.\n"); 8042608aefcSPyun YongHyeon goto fail; 8052608aefcSPyun YongHyeon } 8062608aefcSPyun YongHyeon 8072608aefcSPyun YongHyeon /* Create DMA tag for RX buffers. */ 8082608aefcSPyun YongHyeon error = bus_dma_tag_create( 8092608aefcSPyun YongHyeon sc->vte_cdata.vte_buffer_tag, /* parent */ 8102608aefcSPyun YongHyeon VTE_RX_BUF_ALIGN, 0, /* alignment, boundary */ 8112608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* lowaddr */ 8122608aefcSPyun YongHyeon BUS_SPACE_MAXADDR, /* highaddr */ 8132608aefcSPyun YongHyeon NULL, NULL, /* filter, filterarg */ 8142608aefcSPyun YongHyeon MCLBYTES, /* maxsize */ 8152608aefcSPyun YongHyeon 1, /* nsegments */ 8162608aefcSPyun YongHyeon MCLBYTES, /* maxsegsize */ 8172608aefcSPyun YongHyeon 0, /* flags */ 8182608aefcSPyun YongHyeon NULL, NULL, /* lockfunc, lockarg */ 8192608aefcSPyun YongHyeon &sc->vte_cdata.vte_rx_tag); 8202608aefcSPyun YongHyeon if (error != 0) { 8212608aefcSPyun YongHyeon device_printf(sc->vte_dev, "could not create RX DMA tag.\n"); 8222608aefcSPyun YongHyeon goto fail; 8232608aefcSPyun YongHyeon } 8242608aefcSPyun YongHyeon /* Create DMA maps for TX buffers. */ 8252608aefcSPyun YongHyeon for (i = 0; i < VTE_TX_RING_CNT; i++) { 8262608aefcSPyun YongHyeon txd = &sc->vte_cdata.vte_txdesc[i]; 8272608aefcSPyun YongHyeon txd->tx_m = NULL; 8282608aefcSPyun YongHyeon txd->tx_dmamap = NULL; 8292608aefcSPyun YongHyeon error = bus_dmamap_create(sc->vte_cdata.vte_tx_tag, 0, 8302608aefcSPyun YongHyeon &txd->tx_dmamap); 8312608aefcSPyun YongHyeon if (error != 0) { 8322608aefcSPyun YongHyeon device_printf(sc->vte_dev, 8332608aefcSPyun YongHyeon "could not create TX dmamap.\n"); 8342608aefcSPyun YongHyeon goto fail; 8352608aefcSPyun YongHyeon } 8362608aefcSPyun YongHyeon } 8372608aefcSPyun YongHyeon /* Create DMA maps for RX buffers. */ 8382608aefcSPyun YongHyeon if ((error = bus_dmamap_create(sc->vte_cdata.vte_rx_tag, 0, 8392608aefcSPyun YongHyeon &sc->vte_cdata.vte_rx_sparemap)) != 0) { 8402608aefcSPyun YongHyeon device_printf(sc->vte_dev, 8412608aefcSPyun YongHyeon "could not create spare RX dmamap.\n"); 8422608aefcSPyun YongHyeon goto fail; 8432608aefcSPyun YongHyeon } 8442608aefcSPyun YongHyeon for (i = 0; i < VTE_RX_RING_CNT; i++) { 8452608aefcSPyun YongHyeon rxd = &sc->vte_cdata.vte_rxdesc[i]; 8462608aefcSPyun YongHyeon rxd->rx_m = NULL; 8472608aefcSPyun YongHyeon rxd->rx_dmamap = NULL; 8482608aefcSPyun YongHyeon error = bus_dmamap_create(sc->vte_cdata.vte_rx_tag, 0, 8492608aefcSPyun YongHyeon &rxd->rx_dmamap); 8502608aefcSPyun YongHyeon if (error != 0) { 8512608aefcSPyun YongHyeon device_printf(sc->vte_dev, 8522608aefcSPyun YongHyeon "could not create RX dmamap.\n"); 8532608aefcSPyun YongHyeon goto fail; 8542608aefcSPyun YongHyeon } 8552608aefcSPyun YongHyeon } 8562608aefcSPyun YongHyeon 8572608aefcSPyun YongHyeon fail: 8582608aefcSPyun YongHyeon return (error); 8592608aefcSPyun YongHyeon } 8602608aefcSPyun YongHyeon 8612608aefcSPyun YongHyeon static void 8622608aefcSPyun YongHyeon vte_dma_free(struct vte_softc *sc) 8632608aefcSPyun YongHyeon { 8642608aefcSPyun YongHyeon struct vte_txdesc *txd; 8652608aefcSPyun YongHyeon struct vte_rxdesc *rxd; 8662608aefcSPyun YongHyeon int i; 8672608aefcSPyun YongHyeon 8682608aefcSPyun YongHyeon /* TX buffers. */ 8692608aefcSPyun YongHyeon if (sc->vte_cdata.vte_tx_tag != NULL) { 8702608aefcSPyun YongHyeon for (i = 0; i < VTE_TX_RING_CNT; i++) { 8712608aefcSPyun YongHyeon txd = &sc->vte_cdata.vte_txdesc[i]; 8722608aefcSPyun YongHyeon if (txd->tx_dmamap != NULL) { 8732608aefcSPyun YongHyeon bus_dmamap_destroy(sc->vte_cdata.vte_tx_tag, 8742608aefcSPyun YongHyeon txd->tx_dmamap); 8752608aefcSPyun YongHyeon txd->tx_dmamap = NULL; 8762608aefcSPyun YongHyeon } 8772608aefcSPyun YongHyeon } 8782608aefcSPyun YongHyeon bus_dma_tag_destroy(sc->vte_cdata.vte_tx_tag); 8792608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_tag = NULL; 8802608aefcSPyun YongHyeon } 8812608aefcSPyun YongHyeon /* RX buffers */ 8822608aefcSPyun YongHyeon if (sc->vte_cdata.vte_rx_tag != NULL) { 8832608aefcSPyun YongHyeon for (i = 0; i < VTE_RX_RING_CNT; i++) { 8842608aefcSPyun YongHyeon rxd = &sc->vte_cdata.vte_rxdesc[i]; 8852608aefcSPyun YongHyeon if (rxd->rx_dmamap != NULL) { 8862608aefcSPyun YongHyeon bus_dmamap_destroy(sc->vte_cdata.vte_rx_tag, 8872608aefcSPyun YongHyeon rxd->rx_dmamap); 8882608aefcSPyun YongHyeon rxd->rx_dmamap = NULL; 8892608aefcSPyun YongHyeon } 8902608aefcSPyun YongHyeon } 8912608aefcSPyun YongHyeon if (sc->vte_cdata.vte_rx_sparemap != NULL) { 8922608aefcSPyun YongHyeon bus_dmamap_destroy(sc->vte_cdata.vte_rx_tag, 8932608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_sparemap); 8942608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_sparemap = NULL; 8952608aefcSPyun YongHyeon } 8962608aefcSPyun YongHyeon bus_dma_tag_destroy(sc->vte_cdata.vte_rx_tag); 8972608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_tag = NULL; 8982608aefcSPyun YongHyeon } 8992608aefcSPyun YongHyeon /* TX descriptor ring. */ 9002608aefcSPyun YongHyeon if (sc->vte_cdata.vte_tx_ring_tag != NULL) { 901068d8643SJohn Baldwin if (sc->vte_cdata.vte_tx_ring_paddr != 0) 9022608aefcSPyun YongHyeon bus_dmamap_unload(sc->vte_cdata.vte_tx_ring_tag, 9032608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_map); 904068d8643SJohn Baldwin if (sc->vte_cdata.vte_tx_ring != NULL) 9052608aefcSPyun YongHyeon bus_dmamem_free(sc->vte_cdata.vte_tx_ring_tag, 9062608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring, 9072608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_map); 9082608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring = NULL; 909068d8643SJohn Baldwin sc->vte_cdata.vte_tx_ring_paddr = 0; 9102608aefcSPyun YongHyeon bus_dma_tag_destroy(sc->vte_cdata.vte_tx_ring_tag); 9112608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_tag = NULL; 9122608aefcSPyun YongHyeon } 9132608aefcSPyun YongHyeon /* RX ring. */ 9142608aefcSPyun YongHyeon if (sc->vte_cdata.vte_rx_ring_tag != NULL) { 915068d8643SJohn Baldwin if (sc->vte_cdata.vte_rx_ring_paddr != 0) 9162608aefcSPyun YongHyeon bus_dmamap_unload(sc->vte_cdata.vte_rx_ring_tag, 9172608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_map); 918068d8643SJohn Baldwin if (sc->vte_cdata.vte_rx_ring != NULL) 9192608aefcSPyun YongHyeon bus_dmamem_free(sc->vte_cdata.vte_rx_ring_tag, 9202608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring, 9212608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_map); 9222608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring = NULL; 923068d8643SJohn Baldwin sc->vte_cdata.vte_rx_ring_paddr = 0; 9242608aefcSPyun YongHyeon bus_dma_tag_destroy(sc->vte_cdata.vte_rx_ring_tag); 9252608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_tag = NULL; 9262608aefcSPyun YongHyeon } 9272608aefcSPyun YongHyeon if (sc->vte_cdata.vte_buffer_tag != NULL) { 9282608aefcSPyun YongHyeon bus_dma_tag_destroy(sc->vte_cdata.vte_buffer_tag); 9292608aefcSPyun YongHyeon sc->vte_cdata.vte_buffer_tag = NULL; 9302608aefcSPyun YongHyeon } 9312608aefcSPyun YongHyeon if (sc->vte_cdata.vte_parent_tag != NULL) { 9322608aefcSPyun YongHyeon bus_dma_tag_destroy(sc->vte_cdata.vte_parent_tag); 9332608aefcSPyun YongHyeon sc->vte_cdata.vte_parent_tag = NULL; 9342608aefcSPyun YongHyeon } 9352608aefcSPyun YongHyeon } 9362608aefcSPyun YongHyeon 9372608aefcSPyun YongHyeon static int 9382608aefcSPyun YongHyeon vte_shutdown(device_t dev) 9392608aefcSPyun YongHyeon { 9402608aefcSPyun YongHyeon 9412608aefcSPyun YongHyeon return (vte_suspend(dev)); 9422608aefcSPyun YongHyeon } 9432608aefcSPyun YongHyeon 9442608aefcSPyun YongHyeon static int 9452608aefcSPyun YongHyeon vte_suspend(device_t dev) 9462608aefcSPyun YongHyeon { 9472608aefcSPyun YongHyeon struct vte_softc *sc; 9482608aefcSPyun YongHyeon struct ifnet *ifp; 9492608aefcSPyun YongHyeon 9502608aefcSPyun YongHyeon sc = device_get_softc(dev); 9512608aefcSPyun YongHyeon 9522608aefcSPyun YongHyeon VTE_LOCK(sc); 9532608aefcSPyun YongHyeon ifp = sc->vte_ifp; 9542608aefcSPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 9552608aefcSPyun YongHyeon vte_stop(sc); 9562608aefcSPyun YongHyeon VTE_UNLOCK(sc); 9572608aefcSPyun YongHyeon 9582608aefcSPyun YongHyeon return (0); 9592608aefcSPyun YongHyeon } 9602608aefcSPyun YongHyeon 9612608aefcSPyun YongHyeon static int 9622608aefcSPyun YongHyeon vte_resume(device_t dev) 9632608aefcSPyun YongHyeon { 9642608aefcSPyun YongHyeon struct vte_softc *sc; 9652608aefcSPyun YongHyeon struct ifnet *ifp; 9662608aefcSPyun YongHyeon 9672608aefcSPyun YongHyeon sc = device_get_softc(dev); 9682608aefcSPyun YongHyeon 9692608aefcSPyun YongHyeon VTE_LOCK(sc); 9702608aefcSPyun YongHyeon ifp = sc->vte_ifp; 9712608aefcSPyun YongHyeon if ((ifp->if_flags & IFF_UP) != 0) { 9722608aefcSPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 9732608aefcSPyun YongHyeon vte_init_locked(sc); 9742608aefcSPyun YongHyeon } 9752608aefcSPyun YongHyeon VTE_UNLOCK(sc); 9762608aefcSPyun YongHyeon 9772608aefcSPyun YongHyeon return (0); 9782608aefcSPyun YongHyeon } 9792608aefcSPyun YongHyeon 9802608aefcSPyun YongHyeon static struct vte_txdesc * 9812608aefcSPyun YongHyeon vte_encap(struct vte_softc *sc, struct mbuf **m_head) 9822608aefcSPyun YongHyeon { 9832608aefcSPyun YongHyeon struct vte_txdesc *txd; 9842608aefcSPyun YongHyeon struct mbuf *m, *n; 9852608aefcSPyun YongHyeon bus_dma_segment_t txsegs[1]; 9862608aefcSPyun YongHyeon int copy, error, nsegs, padlen; 9872608aefcSPyun YongHyeon 9882608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 9892608aefcSPyun YongHyeon 9902608aefcSPyun YongHyeon M_ASSERTPKTHDR((*m_head)); 9912608aefcSPyun YongHyeon 9922608aefcSPyun YongHyeon txd = &sc->vte_cdata.vte_txdesc[sc->vte_cdata.vte_tx_prod]; 9932608aefcSPyun YongHyeon m = *m_head; 9942608aefcSPyun YongHyeon /* 9952608aefcSPyun YongHyeon * Controller doesn't auto-pad, so we have to make sure pad 9962608aefcSPyun YongHyeon * short frames out to the minimum frame length. 9972608aefcSPyun YongHyeon */ 9982608aefcSPyun YongHyeon if (m->m_pkthdr.len < VTE_MIN_FRAMELEN) 9992608aefcSPyun YongHyeon padlen = VTE_MIN_FRAMELEN - m->m_pkthdr.len; 10002608aefcSPyun YongHyeon else 10012608aefcSPyun YongHyeon padlen = 0; 10022608aefcSPyun YongHyeon 10032608aefcSPyun YongHyeon /* 10042608aefcSPyun YongHyeon * Controller does not support multi-fragmented TX buffers. 10052608aefcSPyun YongHyeon * Controller spends most of its TX processing time in 10062608aefcSPyun YongHyeon * de-fragmenting TX buffers. Either faster CPU or more 10072608aefcSPyun YongHyeon * advanced controller DMA engine is required to speed up 10082608aefcSPyun YongHyeon * TX path processing. 10092608aefcSPyun YongHyeon * To mitigate the de-fragmenting issue, perform deep copy 10102608aefcSPyun YongHyeon * from fragmented mbuf chains to a pre-allocated mbuf 10112608aefcSPyun YongHyeon * cluster with extra cost of kernel memory. For frames 10122608aefcSPyun YongHyeon * that is composed of single TX buffer, the deep copy is 10132608aefcSPyun YongHyeon * bypassed. 10142608aefcSPyun YongHyeon */ 10152608aefcSPyun YongHyeon if (tx_deep_copy != 0) { 10162608aefcSPyun YongHyeon copy = 0; 10172608aefcSPyun YongHyeon if (m->m_next != NULL) 10182608aefcSPyun YongHyeon copy++; 10192608aefcSPyun YongHyeon if (padlen > 0 && (M_WRITABLE(m) == 0 || 10202608aefcSPyun YongHyeon padlen > M_TRAILINGSPACE(m))) 10212608aefcSPyun YongHyeon copy++; 10222608aefcSPyun YongHyeon if (copy != 0) { 10232608aefcSPyun YongHyeon /* Avoid expensive m_defrag(9) and do deep copy. */ 10242608aefcSPyun YongHyeon n = sc->vte_cdata.vte_txmbufs[sc->vte_cdata.vte_tx_prod]; 10252608aefcSPyun YongHyeon m_copydata(m, 0, m->m_pkthdr.len, mtod(n, char *)); 10262608aefcSPyun YongHyeon n->m_pkthdr.len = m->m_pkthdr.len; 10272608aefcSPyun YongHyeon n->m_len = m->m_pkthdr.len; 10282608aefcSPyun YongHyeon m = n; 10292608aefcSPyun YongHyeon txd->tx_flags |= VTE_TXMBUF; 10302608aefcSPyun YongHyeon } 10312608aefcSPyun YongHyeon 10322608aefcSPyun YongHyeon if (padlen > 0) { 10332608aefcSPyun YongHyeon /* Zero out the bytes in the pad area. */ 10342608aefcSPyun YongHyeon bzero(mtod(m, char *) + m->m_pkthdr.len, padlen); 10352608aefcSPyun YongHyeon m->m_pkthdr.len += padlen; 10362608aefcSPyun YongHyeon m->m_len = m->m_pkthdr.len; 10372608aefcSPyun YongHyeon } 10382608aefcSPyun YongHyeon } else { 10392608aefcSPyun YongHyeon if (M_WRITABLE(m) == 0) { 10402608aefcSPyun YongHyeon if (m->m_next != NULL || padlen > 0) { 10412608aefcSPyun YongHyeon /* Get a writable copy. */ 1042c6499eccSGleb Smirnoff m = m_dup(*m_head, M_NOWAIT); 10432608aefcSPyun YongHyeon /* Release original mbuf chains. */ 10442608aefcSPyun YongHyeon m_freem(*m_head); 10452608aefcSPyun YongHyeon if (m == NULL) { 10462608aefcSPyun YongHyeon *m_head = NULL; 10472608aefcSPyun YongHyeon return (NULL); 10482608aefcSPyun YongHyeon } 10492608aefcSPyun YongHyeon *m_head = m; 10502608aefcSPyun YongHyeon } 10512608aefcSPyun YongHyeon } 10522608aefcSPyun YongHyeon 10532608aefcSPyun YongHyeon if (m->m_next != NULL) { 1054c6499eccSGleb Smirnoff m = m_defrag(*m_head, M_NOWAIT); 10552608aefcSPyun YongHyeon if (m == NULL) { 10562608aefcSPyun YongHyeon m_freem(*m_head); 10572608aefcSPyun YongHyeon *m_head = NULL; 10582608aefcSPyun YongHyeon return (NULL); 10592608aefcSPyun YongHyeon } 10602608aefcSPyun YongHyeon *m_head = m; 10612608aefcSPyun YongHyeon } 10622608aefcSPyun YongHyeon 10632608aefcSPyun YongHyeon if (padlen > 0) { 10642608aefcSPyun YongHyeon if (M_TRAILINGSPACE(m) < padlen) { 1065c6499eccSGleb Smirnoff m = m_defrag(*m_head, M_NOWAIT); 10662608aefcSPyun YongHyeon if (m == NULL) { 10672608aefcSPyun YongHyeon m_freem(*m_head); 10682608aefcSPyun YongHyeon *m_head = NULL; 10692608aefcSPyun YongHyeon return (NULL); 10702608aefcSPyun YongHyeon } 10712608aefcSPyun YongHyeon *m_head = m; 10722608aefcSPyun YongHyeon } 10732608aefcSPyun YongHyeon /* Zero out the bytes in the pad area. */ 10742608aefcSPyun YongHyeon bzero(mtod(m, char *) + m->m_pkthdr.len, padlen); 10752608aefcSPyun YongHyeon m->m_pkthdr.len += padlen; 10762608aefcSPyun YongHyeon m->m_len = m->m_pkthdr.len; 10772608aefcSPyun YongHyeon } 10782608aefcSPyun YongHyeon } 10792608aefcSPyun YongHyeon 10802608aefcSPyun YongHyeon error = bus_dmamap_load_mbuf_sg(sc->vte_cdata.vte_tx_tag, 10812608aefcSPyun YongHyeon txd->tx_dmamap, m, txsegs, &nsegs, 0); 10822608aefcSPyun YongHyeon if (error != 0) { 10832608aefcSPyun YongHyeon txd->tx_flags &= ~VTE_TXMBUF; 10842608aefcSPyun YongHyeon return (NULL); 10852608aefcSPyun YongHyeon } 10862608aefcSPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 10872608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_tx_tag, txd->tx_dmamap, 10882608aefcSPyun YongHyeon BUS_DMASYNC_PREWRITE); 10892608aefcSPyun YongHyeon 10902608aefcSPyun YongHyeon txd->tx_desc->dtlen = htole16(VTE_TX_LEN(txsegs[0].ds_len)); 10912608aefcSPyun YongHyeon txd->tx_desc->dtbp = htole32(txsegs[0].ds_addr); 10922608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_cnt++; 10932608aefcSPyun YongHyeon /* Update producer index. */ 10942608aefcSPyun YongHyeon VTE_DESC_INC(sc->vte_cdata.vte_tx_prod, VTE_TX_RING_CNT); 10952608aefcSPyun YongHyeon 10962608aefcSPyun YongHyeon /* Finally hand over ownership to controller. */ 10972608aefcSPyun YongHyeon txd->tx_desc->dtst = htole16(VTE_DTST_TX_OWN); 10982608aefcSPyun YongHyeon txd->tx_m = m; 10992608aefcSPyun YongHyeon 11002608aefcSPyun YongHyeon return (txd); 11012608aefcSPyun YongHyeon } 11022608aefcSPyun YongHyeon 11032608aefcSPyun YongHyeon static void 11042608aefcSPyun YongHyeon vte_start(struct ifnet *ifp) 11052608aefcSPyun YongHyeon { 11062608aefcSPyun YongHyeon struct vte_softc *sc; 11072608aefcSPyun YongHyeon 11082608aefcSPyun YongHyeon sc = ifp->if_softc; 11092608aefcSPyun YongHyeon VTE_LOCK(sc); 11102608aefcSPyun YongHyeon vte_start_locked(sc); 11112608aefcSPyun YongHyeon VTE_UNLOCK(sc); 11122608aefcSPyun YongHyeon } 11132608aefcSPyun YongHyeon 11142608aefcSPyun YongHyeon static void 11152608aefcSPyun YongHyeon vte_start_locked(struct vte_softc *sc) 11162608aefcSPyun YongHyeon { 11172608aefcSPyun YongHyeon struct ifnet *ifp; 11182608aefcSPyun YongHyeon struct vte_txdesc *txd; 11192608aefcSPyun YongHyeon struct mbuf *m_head; 11202608aefcSPyun YongHyeon int enq; 11212608aefcSPyun YongHyeon 11222608aefcSPyun YongHyeon ifp = sc->vte_ifp; 11232608aefcSPyun YongHyeon 11242608aefcSPyun YongHyeon if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) != 11252608aefcSPyun YongHyeon IFF_DRV_RUNNING || (sc->vte_flags & VTE_FLAG_LINK) == 0) 11262608aefcSPyun YongHyeon return; 11272608aefcSPyun YongHyeon 11282608aefcSPyun YongHyeon for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd); ) { 11292608aefcSPyun YongHyeon /* Reserve one free TX descriptor. */ 11302608aefcSPyun YongHyeon if (sc->vte_cdata.vte_tx_cnt >= VTE_TX_RING_CNT - 1) { 11312608aefcSPyun YongHyeon ifp->if_drv_flags |= IFF_DRV_OACTIVE; 11322608aefcSPyun YongHyeon break; 11332608aefcSPyun YongHyeon } 11342608aefcSPyun YongHyeon IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head); 11352608aefcSPyun YongHyeon if (m_head == NULL) 11362608aefcSPyun YongHyeon break; 11372608aefcSPyun YongHyeon /* 11382608aefcSPyun YongHyeon * Pack the data into the transmit ring. If we 11392608aefcSPyun YongHyeon * don't have room, set the OACTIVE flag and wait 11402608aefcSPyun YongHyeon * for the NIC to drain the ring. 11412608aefcSPyun YongHyeon */ 11422608aefcSPyun YongHyeon if ((txd = vte_encap(sc, &m_head)) == NULL) { 11432608aefcSPyun YongHyeon if (m_head != NULL) 11442608aefcSPyun YongHyeon IFQ_DRV_PREPEND(&ifp->if_snd, m_head); 11452608aefcSPyun YongHyeon break; 11462608aefcSPyun YongHyeon } 11472608aefcSPyun YongHyeon 11482608aefcSPyun YongHyeon enq++; 11492608aefcSPyun YongHyeon /* 11502608aefcSPyun YongHyeon * If there's a BPF listener, bounce a copy of this frame 11512608aefcSPyun YongHyeon * to him. 11522608aefcSPyun YongHyeon */ 11532608aefcSPyun YongHyeon ETHER_BPF_MTAP(ifp, m_head); 11542608aefcSPyun YongHyeon /* Free consumed TX frame. */ 11552608aefcSPyun YongHyeon if ((txd->tx_flags & VTE_TXMBUF) != 0) 11562608aefcSPyun YongHyeon m_freem(m_head); 11572608aefcSPyun YongHyeon } 11582608aefcSPyun YongHyeon 11592608aefcSPyun YongHyeon if (enq > 0) { 11602608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_tx_ring_tag, 11612608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_map, BUS_DMASYNC_PREREAD | 11622608aefcSPyun YongHyeon BUS_DMASYNC_PREWRITE); 11632608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_TX_POLL, TX_POLL_START); 11642608aefcSPyun YongHyeon sc->vte_watchdog_timer = VTE_TX_TIMEOUT; 11652608aefcSPyun YongHyeon } 11662608aefcSPyun YongHyeon } 11672608aefcSPyun YongHyeon 11682608aefcSPyun YongHyeon static void 11692608aefcSPyun YongHyeon vte_watchdog(struct vte_softc *sc) 11702608aefcSPyun YongHyeon { 11712608aefcSPyun YongHyeon struct ifnet *ifp; 11722608aefcSPyun YongHyeon 11732608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 11742608aefcSPyun YongHyeon 11752608aefcSPyun YongHyeon if (sc->vte_watchdog_timer == 0 || --sc->vte_watchdog_timer) 11762608aefcSPyun YongHyeon return; 11772608aefcSPyun YongHyeon 11782608aefcSPyun YongHyeon ifp = sc->vte_ifp; 11792608aefcSPyun YongHyeon if_printf(sc->vte_ifp, "watchdog timeout -- resetting\n"); 118024b83dc6SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); 11812608aefcSPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 11822608aefcSPyun YongHyeon vte_init_locked(sc); 11832608aefcSPyun YongHyeon if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 11842608aefcSPyun YongHyeon vte_start_locked(sc); 11852608aefcSPyun YongHyeon } 11862608aefcSPyun YongHyeon 11872608aefcSPyun YongHyeon static int 11882608aefcSPyun YongHyeon vte_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 11892608aefcSPyun YongHyeon { 11902608aefcSPyun YongHyeon struct vte_softc *sc; 11912608aefcSPyun YongHyeon struct ifreq *ifr; 11922608aefcSPyun YongHyeon struct mii_data *mii; 11932608aefcSPyun YongHyeon int error; 11942608aefcSPyun YongHyeon 11952608aefcSPyun YongHyeon sc = ifp->if_softc; 11962608aefcSPyun YongHyeon ifr = (struct ifreq *)data; 11972608aefcSPyun YongHyeon error = 0; 11982608aefcSPyun YongHyeon switch (cmd) { 11992608aefcSPyun YongHyeon case SIOCSIFFLAGS: 12002608aefcSPyun YongHyeon VTE_LOCK(sc); 12012608aefcSPyun YongHyeon if ((ifp->if_flags & IFF_UP) != 0) { 12022608aefcSPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0 && 12032608aefcSPyun YongHyeon ((ifp->if_flags ^ sc->vte_if_flags) & 12042608aefcSPyun YongHyeon (IFF_PROMISC | IFF_ALLMULTI)) != 0) 12052608aefcSPyun YongHyeon vte_rxfilter(sc); 12062608aefcSPyun YongHyeon else 12072608aefcSPyun YongHyeon vte_init_locked(sc); 12082608aefcSPyun YongHyeon } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 12092608aefcSPyun YongHyeon vte_stop(sc); 12102608aefcSPyun YongHyeon sc->vte_if_flags = ifp->if_flags; 12112608aefcSPyun YongHyeon VTE_UNLOCK(sc); 12122608aefcSPyun YongHyeon break; 12132608aefcSPyun YongHyeon case SIOCADDMULTI: 12142608aefcSPyun YongHyeon case SIOCDELMULTI: 12152608aefcSPyun YongHyeon VTE_LOCK(sc); 12162608aefcSPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 12172608aefcSPyun YongHyeon vte_rxfilter(sc); 12182608aefcSPyun YongHyeon VTE_UNLOCK(sc); 12192608aefcSPyun YongHyeon break; 12202608aefcSPyun YongHyeon case SIOCSIFMEDIA: 12212608aefcSPyun YongHyeon case SIOCGIFMEDIA: 12222608aefcSPyun YongHyeon mii = device_get_softc(sc->vte_miibus); 12232608aefcSPyun YongHyeon error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 12242608aefcSPyun YongHyeon break; 12252608aefcSPyun YongHyeon default: 12262608aefcSPyun YongHyeon error = ether_ioctl(ifp, cmd, data); 12272608aefcSPyun YongHyeon break; 12282608aefcSPyun YongHyeon } 12292608aefcSPyun YongHyeon 12302608aefcSPyun YongHyeon return (error); 12312608aefcSPyun YongHyeon } 12322608aefcSPyun YongHyeon 12332608aefcSPyun YongHyeon static void 12342608aefcSPyun YongHyeon vte_mac_config(struct vte_softc *sc) 12352608aefcSPyun YongHyeon { 12362608aefcSPyun YongHyeon struct mii_data *mii; 12372608aefcSPyun YongHyeon uint16_t mcr; 12382608aefcSPyun YongHyeon 12392608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 12402608aefcSPyun YongHyeon 12412608aefcSPyun YongHyeon mii = device_get_softc(sc->vte_miibus); 12422608aefcSPyun YongHyeon mcr = CSR_READ_2(sc, VTE_MCR0); 12432608aefcSPyun YongHyeon mcr &= ~(MCR0_FC_ENB | MCR0_FULL_DUPLEX); 12442608aefcSPyun YongHyeon if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) { 12452608aefcSPyun YongHyeon mcr |= MCR0_FULL_DUPLEX; 12462608aefcSPyun YongHyeon #ifdef notyet 12472608aefcSPyun YongHyeon if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0) 12482608aefcSPyun YongHyeon mcr |= MCR0_FC_ENB; 12492608aefcSPyun YongHyeon /* 12502608aefcSPyun YongHyeon * The data sheet is not clear whether the controller 12512608aefcSPyun YongHyeon * honors received pause frames or not. The is no 12522608aefcSPyun YongHyeon * separate control bit for RX pause frame so just 12532608aefcSPyun YongHyeon * enable MCR0_FC_ENB bit. 12542608aefcSPyun YongHyeon */ 12552608aefcSPyun YongHyeon if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0) 12562608aefcSPyun YongHyeon mcr |= MCR0_FC_ENB; 12572608aefcSPyun YongHyeon #endif 12582608aefcSPyun YongHyeon } 12592608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MCR0, mcr); 12602608aefcSPyun YongHyeon } 12612608aefcSPyun YongHyeon 12622608aefcSPyun YongHyeon static void 12632608aefcSPyun YongHyeon vte_stats_clear(struct vte_softc *sc) 12642608aefcSPyun YongHyeon { 12652608aefcSPyun YongHyeon 12662608aefcSPyun YongHyeon /* Reading counter registers clears its contents. */ 12672608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_RX_DONE); 12682608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_MECNT0); 12692608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_MECNT1); 12702608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_MECNT2); 12712608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_MECNT3); 12722608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_TX_DONE); 12732608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_MECNT4); 12742608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_CNT_PAUSE); 12752608aefcSPyun YongHyeon } 12762608aefcSPyun YongHyeon 12772608aefcSPyun YongHyeon static void 12782608aefcSPyun YongHyeon vte_stats_update(struct vte_softc *sc) 12792608aefcSPyun YongHyeon { 12802608aefcSPyun YongHyeon struct vte_hw_stats *stat; 12812608aefcSPyun YongHyeon uint16_t value; 12822608aefcSPyun YongHyeon 12832608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 12842608aefcSPyun YongHyeon 12852608aefcSPyun YongHyeon stat = &sc->vte_stats; 12862608aefcSPyun YongHyeon 12872608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_MECISR); 12882608aefcSPyun YongHyeon /* RX stats. */ 12892608aefcSPyun YongHyeon stat->rx_frames += CSR_READ_2(sc, VTE_CNT_RX_DONE); 12902608aefcSPyun YongHyeon value = CSR_READ_2(sc, VTE_CNT_MECNT0); 12912608aefcSPyun YongHyeon stat->rx_bcast_frames += (value >> 8); 12922608aefcSPyun YongHyeon stat->rx_mcast_frames += (value & 0xFF); 12932608aefcSPyun YongHyeon value = CSR_READ_2(sc, VTE_CNT_MECNT1); 12942608aefcSPyun YongHyeon stat->rx_runts += (value >> 8); 12952608aefcSPyun YongHyeon stat->rx_crcerrs += (value & 0xFF); 12962608aefcSPyun YongHyeon value = CSR_READ_2(sc, VTE_CNT_MECNT2); 12972608aefcSPyun YongHyeon stat->rx_long_frames += (value & 0xFF); 12982608aefcSPyun YongHyeon value = CSR_READ_2(sc, VTE_CNT_MECNT3); 12992608aefcSPyun YongHyeon stat->rx_fifo_full += (value >> 8); 13002608aefcSPyun YongHyeon stat->rx_desc_unavail += (value & 0xFF); 13012608aefcSPyun YongHyeon 13022608aefcSPyun YongHyeon /* TX stats. */ 13032608aefcSPyun YongHyeon stat->tx_frames += CSR_READ_2(sc, VTE_CNT_TX_DONE); 13042608aefcSPyun YongHyeon value = CSR_READ_2(sc, VTE_CNT_MECNT4); 13052608aefcSPyun YongHyeon stat->tx_underruns += (value >> 8); 13062608aefcSPyun YongHyeon stat->tx_late_colls += (value & 0xFF); 13072608aefcSPyun YongHyeon 13082608aefcSPyun YongHyeon value = CSR_READ_2(sc, VTE_CNT_PAUSE); 13092608aefcSPyun YongHyeon stat->tx_pause_frames += (value >> 8); 13102608aefcSPyun YongHyeon stat->rx_pause_frames += (value & 0xFF); 131124b83dc6SGleb Smirnoff } 13122608aefcSPyun YongHyeon 131324b83dc6SGleb Smirnoff static uint64_t 131424b83dc6SGleb Smirnoff vte_get_counter(struct ifnet *ifp, ift_counter cnt) 131524b83dc6SGleb Smirnoff { 131624b83dc6SGleb Smirnoff struct vte_softc *sc; 131724b83dc6SGleb Smirnoff struct vte_hw_stats *stat; 131824b83dc6SGleb Smirnoff 131924b83dc6SGleb Smirnoff sc = if_getsoftc(ifp); 132024b83dc6SGleb Smirnoff stat = &sc->vte_stats; 132124b83dc6SGleb Smirnoff 132224b83dc6SGleb Smirnoff switch (cnt) { 132324b83dc6SGleb Smirnoff case IFCOUNTER_OPACKETS: 132424b83dc6SGleb Smirnoff return (stat->tx_frames); 132524b83dc6SGleb Smirnoff case IFCOUNTER_COLLISIONS: 132624b83dc6SGleb Smirnoff return (stat->tx_late_colls); 132724b83dc6SGleb Smirnoff case IFCOUNTER_OERRORS: 132824b83dc6SGleb Smirnoff return (stat->tx_late_colls + stat->tx_underruns); 132924b83dc6SGleb Smirnoff case IFCOUNTER_IPACKETS: 133024b83dc6SGleb Smirnoff return (stat->rx_frames); 133124b83dc6SGleb Smirnoff case IFCOUNTER_IERRORS: 133224b83dc6SGleb Smirnoff return (stat->rx_crcerrs + stat->rx_runts + 133324b83dc6SGleb Smirnoff stat->rx_long_frames + stat->rx_fifo_full); 133424b83dc6SGleb Smirnoff default: 133524b83dc6SGleb Smirnoff return (if_get_counter_default(ifp, cnt)); 133624b83dc6SGleb Smirnoff } 13372608aefcSPyun YongHyeon } 13382608aefcSPyun YongHyeon 13392608aefcSPyun YongHyeon static void 13402608aefcSPyun YongHyeon vte_intr(void *arg) 13412608aefcSPyun YongHyeon { 13422608aefcSPyun YongHyeon struct vte_softc *sc; 13432608aefcSPyun YongHyeon struct ifnet *ifp; 13442608aefcSPyun YongHyeon uint16_t status; 13452608aefcSPyun YongHyeon int n; 13462608aefcSPyun YongHyeon 13472608aefcSPyun YongHyeon sc = (struct vte_softc *)arg; 13482608aefcSPyun YongHyeon VTE_LOCK(sc); 13492608aefcSPyun YongHyeon 13502608aefcSPyun YongHyeon ifp = sc->vte_ifp; 13512608aefcSPyun YongHyeon /* Reading VTE_MISR acknowledges interrupts. */ 13522608aefcSPyun YongHyeon status = CSR_READ_2(sc, VTE_MISR); 13532608aefcSPyun YongHyeon if ((status & VTE_INTRS) == 0) { 13542608aefcSPyun YongHyeon /* Not ours. */ 13552608aefcSPyun YongHyeon VTE_UNLOCK(sc); 13562608aefcSPyun YongHyeon return; 13572608aefcSPyun YongHyeon } 13582608aefcSPyun YongHyeon 13592608aefcSPyun YongHyeon /* Disable interrupts. */ 13602608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MIER, 0); 13612608aefcSPyun YongHyeon for (n = 8; (status & VTE_INTRS) != 0;) { 13622608aefcSPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) 13632608aefcSPyun YongHyeon break; 13642608aefcSPyun YongHyeon if ((status & (MISR_RX_DONE | MISR_RX_DESC_UNAVAIL | 13652608aefcSPyun YongHyeon MISR_RX_FIFO_FULL)) != 0) 13662608aefcSPyun YongHyeon vte_rxeof(sc); 13672608aefcSPyun YongHyeon if ((status & MISR_TX_DONE) != 0) 13682608aefcSPyun YongHyeon vte_txeof(sc); 13692608aefcSPyun YongHyeon if ((status & MISR_EVENT_CNT_OFLOW) != 0) 13702608aefcSPyun YongHyeon vte_stats_update(sc); 13712608aefcSPyun YongHyeon if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) 13722608aefcSPyun YongHyeon vte_start_locked(sc); 13732608aefcSPyun YongHyeon if (--n > 0) 13742608aefcSPyun YongHyeon status = CSR_READ_2(sc, VTE_MISR); 13752608aefcSPyun YongHyeon else 13762608aefcSPyun YongHyeon break; 13772608aefcSPyun YongHyeon } 13782608aefcSPyun YongHyeon 13792608aefcSPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { 13802608aefcSPyun YongHyeon /* Re-enable interrupts. */ 13812608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MIER, VTE_INTRS); 13822608aefcSPyun YongHyeon } 13832608aefcSPyun YongHyeon VTE_UNLOCK(sc); 13842608aefcSPyun YongHyeon } 13852608aefcSPyun YongHyeon 13862608aefcSPyun YongHyeon static void 13872608aefcSPyun YongHyeon vte_txeof(struct vte_softc *sc) 13882608aefcSPyun YongHyeon { 13892608aefcSPyun YongHyeon struct ifnet *ifp; 13902608aefcSPyun YongHyeon struct vte_txdesc *txd; 13912608aefcSPyun YongHyeon uint16_t status; 13922608aefcSPyun YongHyeon int cons, prog; 13932608aefcSPyun YongHyeon 13942608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 13952608aefcSPyun YongHyeon 13962608aefcSPyun YongHyeon ifp = sc->vte_ifp; 13972608aefcSPyun YongHyeon 13982608aefcSPyun YongHyeon if (sc->vte_cdata.vte_tx_cnt == 0) 13992608aefcSPyun YongHyeon return; 14002608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_tx_ring_tag, 14012608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_map, BUS_DMASYNC_POSTREAD | 14022608aefcSPyun YongHyeon BUS_DMASYNC_POSTWRITE); 14032608aefcSPyun YongHyeon cons = sc->vte_cdata.vte_tx_cons; 14042608aefcSPyun YongHyeon /* 14052608aefcSPyun YongHyeon * Go through our TX list and free mbufs for those 14062608aefcSPyun YongHyeon * frames which have been transmitted. 14072608aefcSPyun YongHyeon */ 14082608aefcSPyun YongHyeon for (prog = 0; sc->vte_cdata.vte_tx_cnt > 0; prog++) { 14092608aefcSPyun YongHyeon txd = &sc->vte_cdata.vte_txdesc[cons]; 14102608aefcSPyun YongHyeon status = le16toh(txd->tx_desc->dtst); 14112608aefcSPyun YongHyeon if ((status & VTE_DTST_TX_OWN) != 0) 14122608aefcSPyun YongHyeon break; 14132608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_cnt--; 14142608aefcSPyun YongHyeon /* Reclaim transmitted mbufs. */ 14152608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_tx_tag, txd->tx_dmamap, 14162608aefcSPyun YongHyeon BUS_DMASYNC_POSTWRITE); 14172608aefcSPyun YongHyeon bus_dmamap_unload(sc->vte_cdata.vte_tx_tag, txd->tx_dmamap); 14182608aefcSPyun YongHyeon if ((txd->tx_flags & VTE_TXMBUF) == 0) 14192608aefcSPyun YongHyeon m_freem(txd->tx_m); 14202608aefcSPyun YongHyeon txd->tx_flags &= ~VTE_TXMBUF; 14212608aefcSPyun YongHyeon txd->tx_m = NULL; 14222608aefcSPyun YongHyeon prog++; 14232608aefcSPyun YongHyeon VTE_DESC_INC(cons, VTE_TX_RING_CNT); 14242608aefcSPyun YongHyeon } 14252608aefcSPyun YongHyeon 14262608aefcSPyun YongHyeon if (prog > 0) { 14272608aefcSPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 14282608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_cons = cons; 14292608aefcSPyun YongHyeon /* 14302608aefcSPyun YongHyeon * Unarm watchdog timer only when there is no pending 14312608aefcSPyun YongHyeon * frames in TX queue. 14322608aefcSPyun YongHyeon */ 14332608aefcSPyun YongHyeon if (sc->vte_cdata.vte_tx_cnt == 0) 14342608aefcSPyun YongHyeon sc->vte_watchdog_timer = 0; 14352608aefcSPyun YongHyeon } 14362608aefcSPyun YongHyeon } 14372608aefcSPyun YongHyeon 14382608aefcSPyun YongHyeon static int 14392608aefcSPyun YongHyeon vte_newbuf(struct vte_softc *sc, struct vte_rxdesc *rxd) 14402608aefcSPyun YongHyeon { 14412608aefcSPyun YongHyeon struct mbuf *m; 14422608aefcSPyun YongHyeon bus_dma_segment_t segs[1]; 14432608aefcSPyun YongHyeon bus_dmamap_t map; 14442608aefcSPyun YongHyeon int nsegs; 14452608aefcSPyun YongHyeon 1446c6499eccSGleb Smirnoff m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 14472608aefcSPyun YongHyeon if (m == NULL) 14482608aefcSPyun YongHyeon return (ENOBUFS); 14492608aefcSPyun YongHyeon m->m_len = m->m_pkthdr.len = MCLBYTES; 14502608aefcSPyun YongHyeon m_adj(m, sizeof(uint32_t)); 14512608aefcSPyun YongHyeon 14522608aefcSPyun YongHyeon if (bus_dmamap_load_mbuf_sg(sc->vte_cdata.vte_rx_tag, 14532608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_sparemap, m, segs, &nsegs, 0) != 0) { 14542608aefcSPyun YongHyeon m_freem(m); 14552608aefcSPyun YongHyeon return (ENOBUFS); 14562608aefcSPyun YongHyeon } 14572608aefcSPyun YongHyeon KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs)); 14582608aefcSPyun YongHyeon 14592608aefcSPyun YongHyeon if (rxd->rx_m != NULL) { 14602608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_rx_tag, rxd->rx_dmamap, 14612608aefcSPyun YongHyeon BUS_DMASYNC_POSTREAD); 14622608aefcSPyun YongHyeon bus_dmamap_unload(sc->vte_cdata.vte_rx_tag, rxd->rx_dmamap); 14632608aefcSPyun YongHyeon } 14642608aefcSPyun YongHyeon map = rxd->rx_dmamap; 14652608aefcSPyun YongHyeon rxd->rx_dmamap = sc->vte_cdata.vte_rx_sparemap; 14662608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_sparemap = map; 14672608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_rx_tag, rxd->rx_dmamap, 14682608aefcSPyun YongHyeon BUS_DMASYNC_PREREAD); 14692608aefcSPyun YongHyeon rxd->rx_m = m; 14702608aefcSPyun YongHyeon rxd->rx_desc->drbp = htole32(segs[0].ds_addr); 14712608aefcSPyun YongHyeon rxd->rx_desc->drlen = htole16(VTE_RX_LEN(segs[0].ds_len)); 14722608aefcSPyun YongHyeon rxd->rx_desc->drst = htole16(VTE_DRST_RX_OWN); 14732608aefcSPyun YongHyeon 14742608aefcSPyun YongHyeon return (0); 14752608aefcSPyun YongHyeon } 14762608aefcSPyun YongHyeon 14772608aefcSPyun YongHyeon /* 14782608aefcSPyun YongHyeon * It's not supposed to see this controller on strict-alignment 14792608aefcSPyun YongHyeon * architectures but make it work for completeness. 14802608aefcSPyun YongHyeon */ 14812608aefcSPyun YongHyeon #ifndef __NO_STRICT_ALIGNMENT 14822608aefcSPyun YongHyeon static struct mbuf * 14832608aefcSPyun YongHyeon vte_fixup_rx(struct ifnet *ifp, struct mbuf *m) 14842608aefcSPyun YongHyeon { 14852608aefcSPyun YongHyeon uint16_t *src, *dst; 14862608aefcSPyun YongHyeon int i; 14872608aefcSPyun YongHyeon 14882608aefcSPyun YongHyeon src = mtod(m, uint16_t *); 14892608aefcSPyun YongHyeon dst = src - 1; 14902608aefcSPyun YongHyeon 14912608aefcSPyun YongHyeon for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++) 14922608aefcSPyun YongHyeon *dst++ = *src++; 14932608aefcSPyun YongHyeon m->m_data -= ETHER_ALIGN; 14942608aefcSPyun YongHyeon return (m); 14952608aefcSPyun YongHyeon } 14962608aefcSPyun YongHyeon #endif 14972608aefcSPyun YongHyeon 14982608aefcSPyun YongHyeon static void 14992608aefcSPyun YongHyeon vte_rxeof(struct vte_softc *sc) 15002608aefcSPyun YongHyeon { 15012608aefcSPyun YongHyeon struct ifnet *ifp; 15022608aefcSPyun YongHyeon struct vte_rxdesc *rxd; 15032608aefcSPyun YongHyeon struct mbuf *m; 15042608aefcSPyun YongHyeon uint16_t status, total_len; 15052608aefcSPyun YongHyeon int cons, prog; 15062608aefcSPyun YongHyeon 15072608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_rx_ring_tag, 15082608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_map, BUS_DMASYNC_POSTREAD | 15092608aefcSPyun YongHyeon BUS_DMASYNC_POSTWRITE); 15102608aefcSPyun YongHyeon cons = sc->vte_cdata.vte_rx_cons; 15112608aefcSPyun YongHyeon ifp = sc->vte_ifp; 15122608aefcSPyun YongHyeon for (prog = 0; (ifp->if_drv_flags & IFF_DRV_RUNNING) != 0; prog++, 15132608aefcSPyun YongHyeon VTE_DESC_INC(cons, VTE_RX_RING_CNT)) { 15142608aefcSPyun YongHyeon rxd = &sc->vte_cdata.vte_rxdesc[cons]; 15152608aefcSPyun YongHyeon status = le16toh(rxd->rx_desc->drst); 15162608aefcSPyun YongHyeon if ((status & VTE_DRST_RX_OWN) != 0) 15172608aefcSPyun YongHyeon break; 15182608aefcSPyun YongHyeon total_len = VTE_RX_LEN(le16toh(rxd->rx_desc->drlen)); 15192608aefcSPyun YongHyeon m = rxd->rx_m; 15202608aefcSPyun YongHyeon if ((status & VTE_DRST_RX_OK) == 0) { 15212608aefcSPyun YongHyeon /* Discard errored frame. */ 15222608aefcSPyun YongHyeon rxd->rx_desc->drlen = 15232608aefcSPyun YongHyeon htole16(MCLBYTES - sizeof(uint32_t)); 15242608aefcSPyun YongHyeon rxd->rx_desc->drst = htole16(VTE_DRST_RX_OWN); 15252608aefcSPyun YongHyeon continue; 15262608aefcSPyun YongHyeon } 15272608aefcSPyun YongHyeon if (vte_newbuf(sc, rxd) != 0) { 152824b83dc6SGleb Smirnoff if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); 15292608aefcSPyun YongHyeon rxd->rx_desc->drlen = 15302608aefcSPyun YongHyeon htole16(MCLBYTES - sizeof(uint32_t)); 15312608aefcSPyun YongHyeon rxd->rx_desc->drst = htole16(VTE_DRST_RX_OWN); 15322608aefcSPyun YongHyeon continue; 15332608aefcSPyun YongHyeon } 15342608aefcSPyun YongHyeon 15352608aefcSPyun YongHyeon /* 15362608aefcSPyun YongHyeon * It seems there is no way to strip FCS bytes. 15372608aefcSPyun YongHyeon */ 15382608aefcSPyun YongHyeon m->m_pkthdr.len = m->m_len = total_len - ETHER_CRC_LEN; 15392608aefcSPyun YongHyeon m->m_pkthdr.rcvif = ifp; 15402608aefcSPyun YongHyeon #ifndef __NO_STRICT_ALIGNMENT 15412608aefcSPyun YongHyeon vte_fixup_rx(ifp, m); 15422608aefcSPyun YongHyeon #endif 15432608aefcSPyun YongHyeon VTE_UNLOCK(sc); 15442608aefcSPyun YongHyeon (*ifp->if_input)(ifp, m); 15452608aefcSPyun YongHyeon VTE_LOCK(sc); 15462608aefcSPyun YongHyeon } 15472608aefcSPyun YongHyeon 15482608aefcSPyun YongHyeon if (prog > 0) { 15492608aefcSPyun YongHyeon /* Update the consumer index. */ 15502608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_cons = cons; 15512608aefcSPyun YongHyeon /* 15522608aefcSPyun YongHyeon * Sync updated RX descriptors such that controller see 15532608aefcSPyun YongHyeon * modified RX buffer addresses. 15542608aefcSPyun YongHyeon */ 15552608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_rx_ring_tag, 15562608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_map, 15572608aefcSPyun YongHyeon BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 15582608aefcSPyun YongHyeon #ifdef notyet 15592608aefcSPyun YongHyeon /* 15602608aefcSPyun YongHyeon * Update residue counter. Controller does not 15612608aefcSPyun YongHyeon * keep track of number of available RX descriptors 15622608aefcSPyun YongHyeon * such that driver should have to update VTE_MRDCR 15632608aefcSPyun YongHyeon * to make controller know how many free RX 15642608aefcSPyun YongHyeon * descriptors were added to controller. This is 15652608aefcSPyun YongHyeon * a similar mechanism used in VIA velocity 15662608aefcSPyun YongHyeon * controllers and it indicates controller just 15672608aefcSPyun YongHyeon * polls OWN bit of current RX descriptor pointer. 15682608aefcSPyun YongHyeon * A couple of severe issues were seen on sample 15692608aefcSPyun YongHyeon * board where the controller continuously emits TX 15702608aefcSPyun YongHyeon * pause frames once RX pause threshold crossed. 15712608aefcSPyun YongHyeon * Once triggered it never recovered form that 15722608aefcSPyun YongHyeon * state, I couldn't find a way to make it back to 15732608aefcSPyun YongHyeon * work at least. This issue effectively 15742608aefcSPyun YongHyeon * disconnected the system from network. Also, the 15752608aefcSPyun YongHyeon * controller used 00:00:00:00:00:00 as source 15762608aefcSPyun YongHyeon * station address of TX pause frame. Probably this 15772608aefcSPyun YongHyeon * is one of reason why vendor recommends not to 15782608aefcSPyun YongHyeon * enable flow control on R6040 controller. 15792608aefcSPyun YongHyeon */ 15802608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MRDCR, prog | 15812608aefcSPyun YongHyeon (((VTE_RX_RING_CNT * 2) / 10) << 15822608aefcSPyun YongHyeon VTE_MRDCR_RX_PAUSE_THRESH_SHIFT)); 15832608aefcSPyun YongHyeon #endif 15842608aefcSPyun YongHyeon } 15852608aefcSPyun YongHyeon } 15862608aefcSPyun YongHyeon 15872608aefcSPyun YongHyeon static void 15882608aefcSPyun YongHyeon vte_tick(void *arg) 15892608aefcSPyun YongHyeon { 15902608aefcSPyun YongHyeon struct vte_softc *sc; 15912608aefcSPyun YongHyeon struct mii_data *mii; 15922608aefcSPyun YongHyeon 15932608aefcSPyun YongHyeon sc = (struct vte_softc *)arg; 15942608aefcSPyun YongHyeon 15952608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 15962608aefcSPyun YongHyeon 15972608aefcSPyun YongHyeon mii = device_get_softc(sc->vte_miibus); 15982608aefcSPyun YongHyeon mii_tick(mii); 15992608aefcSPyun YongHyeon vte_stats_update(sc); 16002608aefcSPyun YongHyeon vte_txeof(sc); 16012608aefcSPyun YongHyeon vte_watchdog(sc); 16022608aefcSPyun YongHyeon callout_reset(&sc->vte_tick_ch, hz, vte_tick, sc); 16032608aefcSPyun YongHyeon } 16042608aefcSPyun YongHyeon 16052608aefcSPyun YongHyeon static void 16062608aefcSPyun YongHyeon vte_reset(struct vte_softc *sc) 16072608aefcSPyun YongHyeon { 16082608aefcSPyun YongHyeon uint16_t mcr; 16092608aefcSPyun YongHyeon int i; 16102608aefcSPyun YongHyeon 16112608aefcSPyun YongHyeon mcr = CSR_READ_2(sc, VTE_MCR1); 16122608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MCR1, mcr | MCR1_MAC_RESET); 16132608aefcSPyun YongHyeon for (i = VTE_RESET_TIMEOUT; i > 0; i--) { 16142608aefcSPyun YongHyeon DELAY(10); 16152608aefcSPyun YongHyeon if ((CSR_READ_2(sc, VTE_MCR1) & MCR1_MAC_RESET) == 0) 16162608aefcSPyun YongHyeon break; 16172608aefcSPyun YongHyeon } 16182608aefcSPyun YongHyeon if (i == 0) 16192608aefcSPyun YongHyeon device_printf(sc->vte_dev, "reset timeout(0x%04x)!\n", mcr); 16202608aefcSPyun YongHyeon /* 16212608aefcSPyun YongHyeon * Follow the guide of vendor recommended way to reset MAC. 16222608aefcSPyun YongHyeon * Vendor confirms relying on MCR1_MAC_RESET of VTE_MCR1 is 16232608aefcSPyun YongHyeon * not reliable so manually reset internal state machine. 16242608aefcSPyun YongHyeon */ 16252608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MACSM, 0x0002); 16262608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MACSM, 0); 16272608aefcSPyun YongHyeon DELAY(5000); 16282608aefcSPyun YongHyeon } 16292608aefcSPyun YongHyeon 16302608aefcSPyun YongHyeon static void 16312608aefcSPyun YongHyeon vte_init(void *xsc) 16322608aefcSPyun YongHyeon { 16332608aefcSPyun YongHyeon struct vte_softc *sc; 16342608aefcSPyun YongHyeon 16352608aefcSPyun YongHyeon sc = (struct vte_softc *)xsc; 16362608aefcSPyun YongHyeon VTE_LOCK(sc); 16372608aefcSPyun YongHyeon vte_init_locked(sc); 16382608aefcSPyun YongHyeon VTE_UNLOCK(sc); 16392608aefcSPyun YongHyeon } 16402608aefcSPyun YongHyeon 16412608aefcSPyun YongHyeon static void 16422608aefcSPyun YongHyeon vte_init_locked(struct vte_softc *sc) 16432608aefcSPyun YongHyeon { 16442608aefcSPyun YongHyeon struct ifnet *ifp; 16452608aefcSPyun YongHyeon bus_addr_t paddr; 16462608aefcSPyun YongHyeon uint8_t *eaddr; 16472608aefcSPyun YongHyeon 16482608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 16492608aefcSPyun YongHyeon 16502608aefcSPyun YongHyeon ifp = sc->vte_ifp; 16512608aefcSPyun YongHyeon 16522608aefcSPyun YongHyeon if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) 16532608aefcSPyun YongHyeon return; 16542608aefcSPyun YongHyeon /* 16552608aefcSPyun YongHyeon * Cancel any pending I/O. 16562608aefcSPyun YongHyeon */ 16572608aefcSPyun YongHyeon vte_stop(sc); 16582608aefcSPyun YongHyeon /* 16592608aefcSPyun YongHyeon * Reset the chip to a known state. 16602608aefcSPyun YongHyeon */ 16612608aefcSPyun YongHyeon vte_reset(sc); 16622608aefcSPyun YongHyeon 16632608aefcSPyun YongHyeon /* Initialize RX descriptors. */ 16642608aefcSPyun YongHyeon if (vte_init_rx_ring(sc) != 0) { 16652608aefcSPyun YongHyeon device_printf(sc->vte_dev, "no memory for RX buffers.\n"); 16662608aefcSPyun YongHyeon vte_stop(sc); 16672608aefcSPyun YongHyeon return; 16682608aefcSPyun YongHyeon } 16692608aefcSPyun YongHyeon if (vte_init_tx_ring(sc) != 0) { 16702608aefcSPyun YongHyeon device_printf(sc->vte_dev, "no memory for TX buffers.\n"); 16712608aefcSPyun YongHyeon vte_stop(sc); 16722608aefcSPyun YongHyeon return; 16732608aefcSPyun YongHyeon } 16742608aefcSPyun YongHyeon 16752608aefcSPyun YongHyeon /* 16762608aefcSPyun YongHyeon * Reprogram the station address. Controller supports up 16772608aefcSPyun YongHyeon * to 4 different station addresses so driver programs the 16782608aefcSPyun YongHyeon * first station address as its own ethernet address and 16792608aefcSPyun YongHyeon * configure the remaining three addresses as perfect 16802608aefcSPyun YongHyeon * multicast addresses. 16812608aefcSPyun YongHyeon */ 16822608aefcSPyun YongHyeon eaddr = IF_LLADDR(sc->vte_ifp); 16832608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MID0L, eaddr[1] << 8 | eaddr[0]); 16842608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MID0M, eaddr[3] << 8 | eaddr[2]); 16852608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MID0H, eaddr[5] << 8 | eaddr[4]); 16862608aefcSPyun YongHyeon 16872608aefcSPyun YongHyeon /* Set TX descriptor base addresses. */ 16882608aefcSPyun YongHyeon paddr = sc->vte_cdata.vte_tx_ring_paddr; 16892608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MTDSA1, paddr >> 16); 16902608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MTDSA0, paddr & 0xFFFF); 16912608aefcSPyun YongHyeon /* Set RX descriptor base addresses. */ 16922608aefcSPyun YongHyeon paddr = sc->vte_cdata.vte_rx_ring_paddr; 16932608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MRDSA1, paddr >> 16); 16942608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MRDSA0, paddr & 0xFFFF); 16952608aefcSPyun YongHyeon /* 16962608aefcSPyun YongHyeon * Initialize RX descriptor residue counter and set RX 16972608aefcSPyun YongHyeon * pause threshold to 20% of available RX descriptors. 16982608aefcSPyun YongHyeon * See comments on vte_rxeof() for details on flow control 16992608aefcSPyun YongHyeon * issues. 17002608aefcSPyun YongHyeon */ 17012608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MRDCR, (VTE_RX_RING_CNT & VTE_MRDCR_RESIDUE_MASK) | 17022608aefcSPyun YongHyeon (((VTE_RX_RING_CNT * 2) / 10) << VTE_MRDCR_RX_PAUSE_THRESH_SHIFT)); 17032608aefcSPyun YongHyeon 17042608aefcSPyun YongHyeon /* 17052608aefcSPyun YongHyeon * Always use maximum frame size that controller can 17062608aefcSPyun YongHyeon * support. Otherwise received frames that has longer 17072608aefcSPyun YongHyeon * frame length than vte(4) MTU would be silently dropped 17082608aefcSPyun YongHyeon * in controller. This would break path-MTU discovery as 17092608aefcSPyun YongHyeon * sender wouldn't get any responses from receiver. The 17102608aefcSPyun YongHyeon * RX buffer size should be multiple of 4. 17112608aefcSPyun YongHyeon * Note, jumbo frames are silently ignored by controller 17122608aefcSPyun YongHyeon * and even MAC counters do not detect them. 17132608aefcSPyun YongHyeon */ 17142608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MRBSR, VTE_RX_BUF_SIZE_MAX); 17152608aefcSPyun YongHyeon 17162608aefcSPyun YongHyeon /* Configure FIFO. */ 17172608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MBCR, MBCR_FIFO_XFER_LENGTH_16 | 17182608aefcSPyun YongHyeon MBCR_TX_FIFO_THRESH_64 | MBCR_RX_FIFO_THRESH_16 | 17192608aefcSPyun YongHyeon MBCR_SDRAM_BUS_REQ_TIMER_DEFAULT); 17202608aefcSPyun YongHyeon 17212608aefcSPyun YongHyeon /* 17222608aefcSPyun YongHyeon * Configure TX/RX MACs. Actual resolved duplex and flow 17232608aefcSPyun YongHyeon * control configuration is done after detecting a valid 17242608aefcSPyun YongHyeon * link. Note, we don't generate early interrupt here 17252608aefcSPyun YongHyeon * as well since FreeBSD does not have interrupt latency 17262608aefcSPyun YongHyeon * problems like Windows. 17272608aefcSPyun YongHyeon */ 17282608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MCR0, MCR0_ACCPT_LONG_PKT); 17292608aefcSPyun YongHyeon /* 17302608aefcSPyun YongHyeon * We manually keep track of PHY status changes to 17312608aefcSPyun YongHyeon * configure resolved duplex and flow control since only 17322608aefcSPyun YongHyeon * duplex configuration can be automatically reflected to 17332608aefcSPyun YongHyeon * MCR0. 17342608aefcSPyun YongHyeon */ 17352608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MCR1, MCR1_PKT_LENGTH_1537 | 17362608aefcSPyun YongHyeon MCR1_EXCESS_COL_RETRY_16); 17372608aefcSPyun YongHyeon 17382608aefcSPyun YongHyeon /* Initialize RX filter. */ 17392608aefcSPyun YongHyeon vte_rxfilter(sc); 17402608aefcSPyun YongHyeon 17412608aefcSPyun YongHyeon /* Disable TX/RX interrupt moderation control. */ 17422608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MRICR, 0); 17432608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MTICR, 0); 17442608aefcSPyun YongHyeon 17452608aefcSPyun YongHyeon /* Enable MAC event counter interrupts. */ 17462608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MECIER, VTE_MECIER_INTRS); 17472608aefcSPyun YongHyeon /* Clear MAC statistics. */ 17482608aefcSPyun YongHyeon vte_stats_clear(sc); 17492608aefcSPyun YongHyeon 17502608aefcSPyun YongHyeon /* Acknowledge all pending interrupts and clear it. */ 17512608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MIER, VTE_INTRS); 17522608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MISR, 0); 17532608aefcSPyun YongHyeon 17542608aefcSPyun YongHyeon sc->vte_flags &= ~VTE_FLAG_LINK; 17552608aefcSPyun YongHyeon /* Switch to the current media. */ 17562608aefcSPyun YongHyeon vte_mediachange_locked(ifp); 17572608aefcSPyun YongHyeon 17582608aefcSPyun YongHyeon callout_reset(&sc->vte_tick_ch, hz, vte_tick, sc); 17592608aefcSPyun YongHyeon 17602608aefcSPyun YongHyeon ifp->if_drv_flags |= IFF_DRV_RUNNING; 17612608aefcSPyun YongHyeon ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 17622608aefcSPyun YongHyeon } 17632608aefcSPyun YongHyeon 17642608aefcSPyun YongHyeon static void 17652608aefcSPyun YongHyeon vte_stop(struct vte_softc *sc) 17662608aefcSPyun YongHyeon { 17672608aefcSPyun YongHyeon struct ifnet *ifp; 17682608aefcSPyun YongHyeon struct vte_txdesc *txd; 17692608aefcSPyun YongHyeon struct vte_rxdesc *rxd; 17702608aefcSPyun YongHyeon int i; 17712608aefcSPyun YongHyeon 17722608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 17732608aefcSPyun YongHyeon /* 17742608aefcSPyun YongHyeon * Mark the interface down and cancel the watchdog timer. 17752608aefcSPyun YongHyeon */ 17762608aefcSPyun YongHyeon ifp = sc->vte_ifp; 17772608aefcSPyun YongHyeon ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 17782608aefcSPyun YongHyeon sc->vte_flags &= ~VTE_FLAG_LINK; 17792608aefcSPyun YongHyeon callout_stop(&sc->vte_tick_ch); 17802608aefcSPyun YongHyeon sc->vte_watchdog_timer = 0; 17812608aefcSPyun YongHyeon vte_stats_update(sc); 17822608aefcSPyun YongHyeon /* Disable interrupts. */ 17832608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MIER, 0); 17842608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MECIER, 0); 17852608aefcSPyun YongHyeon /* Stop RX/TX MACs. */ 17862608aefcSPyun YongHyeon vte_stop_mac(sc); 17872608aefcSPyun YongHyeon /* Clear interrupts. */ 17882608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_MISR); 17892608aefcSPyun YongHyeon /* 17902608aefcSPyun YongHyeon * Free TX/RX mbufs still in the queues. 17912608aefcSPyun YongHyeon */ 17922608aefcSPyun YongHyeon for (i = 0; i < VTE_RX_RING_CNT; i++) { 17932608aefcSPyun YongHyeon rxd = &sc->vte_cdata.vte_rxdesc[i]; 17942608aefcSPyun YongHyeon if (rxd->rx_m != NULL) { 17952608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_rx_tag, 17962608aefcSPyun YongHyeon rxd->rx_dmamap, BUS_DMASYNC_POSTREAD); 17972608aefcSPyun YongHyeon bus_dmamap_unload(sc->vte_cdata.vte_rx_tag, 17982608aefcSPyun YongHyeon rxd->rx_dmamap); 17992608aefcSPyun YongHyeon m_freem(rxd->rx_m); 18002608aefcSPyun YongHyeon rxd->rx_m = NULL; 18012608aefcSPyun YongHyeon } 18022608aefcSPyun YongHyeon } 18032608aefcSPyun YongHyeon for (i = 0; i < VTE_TX_RING_CNT; i++) { 18042608aefcSPyun YongHyeon txd = &sc->vte_cdata.vte_txdesc[i]; 18052608aefcSPyun YongHyeon if (txd->tx_m != NULL) { 18062608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_tx_tag, 18072608aefcSPyun YongHyeon txd->tx_dmamap, BUS_DMASYNC_POSTWRITE); 18082608aefcSPyun YongHyeon bus_dmamap_unload(sc->vte_cdata.vte_tx_tag, 18092608aefcSPyun YongHyeon txd->tx_dmamap); 18102608aefcSPyun YongHyeon if ((txd->tx_flags & VTE_TXMBUF) == 0) 18112608aefcSPyun YongHyeon m_freem(txd->tx_m); 18122608aefcSPyun YongHyeon txd->tx_m = NULL; 18132608aefcSPyun YongHyeon txd->tx_flags &= ~VTE_TXMBUF; 18142608aefcSPyun YongHyeon } 18152608aefcSPyun YongHyeon } 18162608aefcSPyun YongHyeon /* Free TX mbuf pools used for deep copy. */ 18172608aefcSPyun YongHyeon for (i = 0; i < VTE_TX_RING_CNT; i++) { 18182608aefcSPyun YongHyeon if (sc->vte_cdata.vte_txmbufs[i] != NULL) { 18192608aefcSPyun YongHyeon m_freem(sc->vte_cdata.vte_txmbufs[i]); 18202608aefcSPyun YongHyeon sc->vte_cdata.vte_txmbufs[i] = NULL; 18212608aefcSPyun YongHyeon } 18222608aefcSPyun YongHyeon } 18232608aefcSPyun YongHyeon } 18242608aefcSPyun YongHyeon 18252608aefcSPyun YongHyeon static void 18262608aefcSPyun YongHyeon vte_start_mac(struct vte_softc *sc) 18272608aefcSPyun YongHyeon { 18282608aefcSPyun YongHyeon uint16_t mcr; 18292608aefcSPyun YongHyeon int i; 18302608aefcSPyun YongHyeon 18312608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 18322608aefcSPyun YongHyeon 18332608aefcSPyun YongHyeon /* Enable RX/TX MACs. */ 18342608aefcSPyun YongHyeon mcr = CSR_READ_2(sc, VTE_MCR0); 18352608aefcSPyun YongHyeon if ((mcr & (MCR0_RX_ENB | MCR0_TX_ENB)) != 18362608aefcSPyun YongHyeon (MCR0_RX_ENB | MCR0_TX_ENB)) { 18372608aefcSPyun YongHyeon mcr |= MCR0_RX_ENB | MCR0_TX_ENB; 18382608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MCR0, mcr); 18392608aefcSPyun YongHyeon for (i = VTE_TIMEOUT; i > 0; i--) { 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 break; 18442608aefcSPyun YongHyeon DELAY(10); 18452608aefcSPyun YongHyeon } 18462608aefcSPyun YongHyeon if (i == 0) 18472608aefcSPyun YongHyeon device_printf(sc->vte_dev, 18482608aefcSPyun YongHyeon "could not enable RX/TX MAC(0x%04x)!\n", mcr); 18492608aefcSPyun YongHyeon } 18502608aefcSPyun YongHyeon } 18512608aefcSPyun YongHyeon 18522608aefcSPyun YongHyeon static void 18532608aefcSPyun YongHyeon vte_stop_mac(struct vte_softc *sc) 18542608aefcSPyun YongHyeon { 18552608aefcSPyun YongHyeon uint16_t mcr; 18562608aefcSPyun YongHyeon int i; 18572608aefcSPyun YongHyeon 18582608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 18592608aefcSPyun YongHyeon 18602608aefcSPyun YongHyeon /* Disable RX/TX MACs. */ 18612608aefcSPyun YongHyeon mcr = CSR_READ_2(sc, VTE_MCR0); 18622608aefcSPyun YongHyeon if ((mcr & (MCR0_RX_ENB | MCR0_TX_ENB)) != 0) { 18632608aefcSPyun YongHyeon mcr &= ~(MCR0_RX_ENB | MCR0_TX_ENB); 18642608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MCR0, mcr); 18652608aefcSPyun YongHyeon for (i = VTE_TIMEOUT; i > 0; i--) { 18662608aefcSPyun YongHyeon mcr = CSR_READ_2(sc, VTE_MCR0); 18672608aefcSPyun YongHyeon if ((mcr & (MCR0_RX_ENB | MCR0_TX_ENB)) == 0) 18682608aefcSPyun YongHyeon break; 18692608aefcSPyun YongHyeon DELAY(10); 18702608aefcSPyun YongHyeon } 18712608aefcSPyun YongHyeon if (i == 0) 18722608aefcSPyun YongHyeon device_printf(sc->vte_dev, 18732608aefcSPyun YongHyeon "could not disable RX/TX MAC(0x%04x)!\n", mcr); 18742608aefcSPyun YongHyeon } 18752608aefcSPyun YongHyeon } 18762608aefcSPyun YongHyeon 18772608aefcSPyun YongHyeon static int 18782608aefcSPyun YongHyeon vte_init_tx_ring(struct vte_softc *sc) 18792608aefcSPyun YongHyeon { 18802608aefcSPyun YongHyeon struct vte_tx_desc *desc; 18812608aefcSPyun YongHyeon struct vte_txdesc *txd; 18822608aefcSPyun YongHyeon bus_addr_t addr; 18832608aefcSPyun YongHyeon int i; 18842608aefcSPyun YongHyeon 18852608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 18862608aefcSPyun YongHyeon 18872608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_prod = 0; 18882608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_cons = 0; 18892608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_cnt = 0; 18902608aefcSPyun YongHyeon 18912608aefcSPyun YongHyeon /* Pre-allocate TX mbufs for deep copy. */ 18922608aefcSPyun YongHyeon if (tx_deep_copy != 0) { 18932608aefcSPyun YongHyeon for (i = 0; i < VTE_TX_RING_CNT; i++) { 1894c6499eccSGleb Smirnoff sc->vte_cdata.vte_txmbufs[i] = m_getcl(M_NOWAIT, 18952608aefcSPyun YongHyeon MT_DATA, M_PKTHDR); 18962608aefcSPyun YongHyeon if (sc->vte_cdata.vte_txmbufs[i] == NULL) 18972608aefcSPyun YongHyeon return (ENOBUFS); 18982608aefcSPyun YongHyeon sc->vte_cdata.vte_txmbufs[i]->m_pkthdr.len = MCLBYTES; 18992608aefcSPyun YongHyeon sc->vte_cdata.vte_txmbufs[i]->m_len = MCLBYTES; 19002608aefcSPyun YongHyeon } 19012608aefcSPyun YongHyeon } 19022608aefcSPyun YongHyeon desc = sc->vte_cdata.vte_tx_ring; 19032608aefcSPyun YongHyeon bzero(desc, VTE_TX_RING_SZ); 19042608aefcSPyun YongHyeon for (i = 0; i < VTE_TX_RING_CNT; i++) { 19052608aefcSPyun YongHyeon txd = &sc->vte_cdata.vte_txdesc[i]; 19062608aefcSPyun YongHyeon txd->tx_m = NULL; 19072608aefcSPyun YongHyeon if (i != VTE_TX_RING_CNT - 1) 19082608aefcSPyun YongHyeon addr = sc->vte_cdata.vte_tx_ring_paddr + 19092608aefcSPyun YongHyeon sizeof(struct vte_tx_desc) * (i + 1); 19102608aefcSPyun YongHyeon else 19112608aefcSPyun YongHyeon addr = sc->vte_cdata.vte_tx_ring_paddr + 19122608aefcSPyun YongHyeon sizeof(struct vte_tx_desc) * 0; 19132608aefcSPyun YongHyeon desc = &sc->vte_cdata.vte_tx_ring[i]; 19142608aefcSPyun YongHyeon desc->dtnp = htole32(addr); 19152608aefcSPyun YongHyeon txd->tx_desc = desc; 19162608aefcSPyun YongHyeon } 19172608aefcSPyun YongHyeon 19182608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_tx_ring_tag, 19192608aefcSPyun YongHyeon sc->vte_cdata.vte_tx_ring_map, BUS_DMASYNC_PREREAD | 19202608aefcSPyun YongHyeon BUS_DMASYNC_PREWRITE); 19212608aefcSPyun YongHyeon return (0); 19222608aefcSPyun YongHyeon } 19232608aefcSPyun YongHyeon 19242608aefcSPyun YongHyeon static int 19252608aefcSPyun YongHyeon vte_init_rx_ring(struct vte_softc *sc) 19262608aefcSPyun YongHyeon { 19272608aefcSPyun YongHyeon struct vte_rx_desc *desc; 19282608aefcSPyun YongHyeon struct vte_rxdesc *rxd; 19292608aefcSPyun YongHyeon bus_addr_t addr; 19302608aefcSPyun YongHyeon int i; 19312608aefcSPyun YongHyeon 19322608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 19332608aefcSPyun YongHyeon 19342608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_cons = 0; 19352608aefcSPyun YongHyeon desc = sc->vte_cdata.vte_rx_ring; 19362608aefcSPyun YongHyeon bzero(desc, VTE_RX_RING_SZ); 19372608aefcSPyun YongHyeon for (i = 0; i < VTE_RX_RING_CNT; i++) { 19382608aefcSPyun YongHyeon rxd = &sc->vte_cdata.vte_rxdesc[i]; 19392608aefcSPyun YongHyeon rxd->rx_m = NULL; 19402608aefcSPyun YongHyeon if (i != VTE_RX_RING_CNT - 1) 19412608aefcSPyun YongHyeon addr = sc->vte_cdata.vte_rx_ring_paddr + 19422608aefcSPyun YongHyeon sizeof(struct vte_rx_desc) * (i + 1); 19432608aefcSPyun YongHyeon else 19442608aefcSPyun YongHyeon addr = sc->vte_cdata.vte_rx_ring_paddr + 19452608aefcSPyun YongHyeon sizeof(struct vte_rx_desc) * 0; 19462608aefcSPyun YongHyeon desc = &sc->vte_cdata.vte_rx_ring[i]; 19472608aefcSPyun YongHyeon desc->drnp = htole32(addr); 19482608aefcSPyun YongHyeon rxd->rx_desc = desc; 19492608aefcSPyun YongHyeon if (vte_newbuf(sc, rxd) != 0) 19502608aefcSPyun YongHyeon return (ENOBUFS); 19512608aefcSPyun YongHyeon } 19522608aefcSPyun YongHyeon 19532608aefcSPyun YongHyeon bus_dmamap_sync(sc->vte_cdata.vte_rx_ring_tag, 19542608aefcSPyun YongHyeon sc->vte_cdata.vte_rx_ring_map, BUS_DMASYNC_PREREAD | 19552608aefcSPyun YongHyeon BUS_DMASYNC_PREWRITE); 19562608aefcSPyun YongHyeon 19572608aefcSPyun YongHyeon return (0); 19582608aefcSPyun YongHyeon } 19592608aefcSPyun YongHyeon 19605d0c611cSGleb Smirnoff struct vte_maddr_ctx { 19615d0c611cSGleb Smirnoff uint16_t rxfilt_perf[VTE_RXFILT_PERFECT_CNT][3]; 19625d0c611cSGleb Smirnoff uint16_t mchash[4]; 19635d0c611cSGleb Smirnoff u_int nperf; 19645d0c611cSGleb Smirnoff }; 19655d0c611cSGleb Smirnoff 19665d0c611cSGleb Smirnoff static u_int 19675d0c611cSGleb Smirnoff vte_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt) 19685d0c611cSGleb Smirnoff { 19695d0c611cSGleb Smirnoff struct vte_maddr_ctx *ctx = arg; 19705d0c611cSGleb Smirnoff uint8_t *eaddr; 19715d0c611cSGleb Smirnoff uint32_t crc; 19725d0c611cSGleb Smirnoff 19735d0c611cSGleb Smirnoff /* 19745d0c611cSGleb Smirnoff * Program the first 3 multicast groups into the perfect filter. 19755d0c611cSGleb Smirnoff * For all others, use the hash table. 19765d0c611cSGleb Smirnoff */ 19775d0c611cSGleb Smirnoff if (ctx->nperf < VTE_RXFILT_PERFECT_CNT) { 19785d0c611cSGleb Smirnoff eaddr = LLADDR(sdl); 19795d0c611cSGleb Smirnoff ctx->rxfilt_perf[ctx->nperf][0] = eaddr[1] << 8 | eaddr[0]; 19805d0c611cSGleb Smirnoff ctx->rxfilt_perf[ctx->nperf][1] = eaddr[3] << 8 | eaddr[2]; 19815d0c611cSGleb Smirnoff ctx->rxfilt_perf[ctx->nperf][2] = eaddr[5] << 8 | eaddr[4]; 19825d0c611cSGleb Smirnoff ctx->nperf++; 19835d0c611cSGleb Smirnoff 19845d0c611cSGleb Smirnoff return (1); 19855d0c611cSGleb Smirnoff } 19865d0c611cSGleb Smirnoff crc = ether_crc32_be(LLADDR(sdl), ETHER_ADDR_LEN); 19875d0c611cSGleb Smirnoff ctx->mchash[crc >> 30] |= 1 << ((crc >> 26) & 0x0F); 19885d0c611cSGleb Smirnoff 19895d0c611cSGleb Smirnoff return (1); 19905d0c611cSGleb Smirnoff } 19915d0c611cSGleb Smirnoff 19922608aefcSPyun YongHyeon static void 19932608aefcSPyun YongHyeon vte_rxfilter(struct vte_softc *sc) 19942608aefcSPyun YongHyeon { 19952608aefcSPyun YongHyeon struct ifnet *ifp; 19965d0c611cSGleb Smirnoff struct vte_maddr_ctx ctx; 19975d0c611cSGleb Smirnoff uint16_t mcr; 19985d0c611cSGleb Smirnoff int i; 19992608aefcSPyun YongHyeon 20002608aefcSPyun YongHyeon VTE_LOCK_ASSERT(sc); 20012608aefcSPyun YongHyeon 20022608aefcSPyun YongHyeon ifp = sc->vte_ifp; 20032608aefcSPyun YongHyeon 20045d0c611cSGleb Smirnoff bzero(ctx.mchash, sizeof(ctx.mchash)); 20052608aefcSPyun YongHyeon for (i = 0; i < VTE_RXFILT_PERFECT_CNT; i++) { 20065d0c611cSGleb Smirnoff ctx.rxfilt_perf[i][0] = 0xFFFF; 20075d0c611cSGleb Smirnoff ctx.rxfilt_perf[i][1] = 0xFFFF; 20085d0c611cSGleb Smirnoff ctx.rxfilt_perf[i][2] = 0xFFFF; 20092608aefcSPyun YongHyeon } 20105d0c611cSGleb Smirnoff ctx.nperf = 0; 20112608aefcSPyun YongHyeon 20122608aefcSPyun YongHyeon mcr = CSR_READ_2(sc, VTE_MCR0); 2013d8f226b6SPyun YongHyeon mcr &= ~(MCR0_PROMISC | MCR0_MULTICAST); 2014d8f226b6SPyun YongHyeon mcr |= MCR0_BROADCAST_DIS; 20152608aefcSPyun YongHyeon if ((ifp->if_flags & IFF_BROADCAST) != 0) 2016d8f226b6SPyun YongHyeon mcr &= ~MCR0_BROADCAST_DIS; 20172608aefcSPyun YongHyeon if ((ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) != 0) { 20182608aefcSPyun YongHyeon if ((ifp->if_flags & IFF_PROMISC) != 0) 20192608aefcSPyun YongHyeon mcr |= MCR0_PROMISC; 20202608aefcSPyun YongHyeon if ((ifp->if_flags & IFF_ALLMULTI) != 0) 20212608aefcSPyun YongHyeon mcr |= MCR0_MULTICAST; 20225d0c611cSGleb Smirnoff ctx.mchash[0] = 0xFFFF; 20235d0c611cSGleb Smirnoff ctx.mchash[1] = 0xFFFF; 20245d0c611cSGleb Smirnoff ctx.mchash[2] = 0xFFFF; 20255d0c611cSGleb Smirnoff ctx.mchash[3] = 0xFFFF; 20262608aefcSPyun YongHyeon goto chipit; 20272608aefcSPyun YongHyeon } 20282608aefcSPyun YongHyeon 20295d0c611cSGleb Smirnoff if_foreach_llmaddr(ifp, vte_hash_maddr, &ctx); 20305d0c611cSGleb Smirnoff if (ctx.mchash[0] != 0 || ctx.mchash[1] != 0 || 20315d0c611cSGleb Smirnoff ctx.mchash[2] != 0 || ctx.mchash[3] != 0) 20322608aefcSPyun YongHyeon mcr |= MCR0_MULTICAST; 20332608aefcSPyun YongHyeon 20342608aefcSPyun YongHyeon chipit: 20352608aefcSPyun YongHyeon /* Program multicast hash table. */ 20365d0c611cSGleb Smirnoff CSR_WRITE_2(sc, VTE_MAR0, ctx.mchash[0]); 20375d0c611cSGleb Smirnoff CSR_WRITE_2(sc, VTE_MAR1, ctx.mchash[1]); 20385d0c611cSGleb Smirnoff CSR_WRITE_2(sc, VTE_MAR2, ctx.mchash[2]); 20395d0c611cSGleb Smirnoff CSR_WRITE_2(sc, VTE_MAR3, ctx.mchash[3]); 20402608aefcSPyun YongHyeon /* Program perfect filter table. */ 20412608aefcSPyun YongHyeon for (i = 0; i < VTE_RXFILT_PERFECT_CNT; i++) { 20422608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_RXFILTER_PEEFECT_BASE + 8 * i + 0, 20435d0c611cSGleb Smirnoff ctx.rxfilt_perf[i][0]); 20442608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_RXFILTER_PEEFECT_BASE + 8 * i + 2, 20455d0c611cSGleb Smirnoff ctx.rxfilt_perf[i][1]); 20462608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_RXFILTER_PEEFECT_BASE + 8 * i + 4, 20475d0c611cSGleb Smirnoff ctx.rxfilt_perf[i][2]); 20482608aefcSPyun YongHyeon } 20492608aefcSPyun YongHyeon CSR_WRITE_2(sc, VTE_MCR0, mcr); 20502608aefcSPyun YongHyeon CSR_READ_2(sc, VTE_MCR0); 20512608aefcSPyun YongHyeon } 20522608aefcSPyun YongHyeon 20532608aefcSPyun YongHyeon static int 20542608aefcSPyun YongHyeon sysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high) 20552608aefcSPyun YongHyeon { 20562608aefcSPyun YongHyeon int error, value; 20572608aefcSPyun YongHyeon 20582608aefcSPyun YongHyeon if (arg1 == NULL) 20592608aefcSPyun YongHyeon return (EINVAL); 20602608aefcSPyun YongHyeon value = *(int *)arg1; 20612608aefcSPyun YongHyeon error = sysctl_handle_int(oidp, &value, 0, req); 20622608aefcSPyun YongHyeon if (error || req->newptr == NULL) 20632608aefcSPyun YongHyeon return (error); 20642608aefcSPyun YongHyeon if (value < low || value > high) 20652608aefcSPyun YongHyeon return (EINVAL); 20662608aefcSPyun YongHyeon *(int *)arg1 = value; 20672608aefcSPyun YongHyeon 20682608aefcSPyun YongHyeon return (0); 20692608aefcSPyun YongHyeon } 20702608aefcSPyun YongHyeon 20712608aefcSPyun YongHyeon static int 20722608aefcSPyun YongHyeon sysctl_hw_vte_int_mod(SYSCTL_HANDLER_ARGS) 20732608aefcSPyun YongHyeon { 20742608aefcSPyun YongHyeon 20752608aefcSPyun YongHyeon return (sysctl_int_range(oidp, arg1, arg2, req, 20762608aefcSPyun YongHyeon VTE_IM_BUNDLE_MIN, VTE_IM_BUNDLE_MAX)); 20772608aefcSPyun YongHyeon } 2078