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