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