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