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