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