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