xref: /freebsd/sys/dev/stge/if_stge.c (revision 4e62c3cafa4c4e41efd6f87b7fe559cf819cf3e4)
157808251SPyun YongHyeon /*	$NetBSD: if_stge.c,v 1.32 2005/12/11 12:22:49 christos Exp $	*/
257808251SPyun YongHyeon 
357808251SPyun YongHyeon /*-
4*b61a5730SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
5718cf2ccSPedro F. Giffuni  *
657808251SPyun YongHyeon  * Copyright (c) 2001 The NetBSD Foundation, Inc.
757808251SPyun YongHyeon  * All rights reserved.
857808251SPyun YongHyeon  *
957808251SPyun YongHyeon  * This code is derived from software contributed to The NetBSD Foundation
1057808251SPyun YongHyeon  * by Jason R. Thorpe.
1157808251SPyun YongHyeon  *
1257808251SPyun YongHyeon  * Redistribution and use in source and binary forms, with or without
1357808251SPyun YongHyeon  * modification, are permitted provided that the following conditions
1457808251SPyun YongHyeon  * are met:
1557808251SPyun YongHyeon  * 1. Redistributions of source code must retain the above copyright
1657808251SPyun YongHyeon  *    notice, this list of conditions and the following disclaimer.
1757808251SPyun YongHyeon  * 2. Redistributions in binary form must reproduce the above copyright
1857808251SPyun YongHyeon  *    notice, this list of conditions and the following disclaimer in the
1957808251SPyun YongHyeon  *    documentation and/or other materials provided with the distribution.
2057808251SPyun YongHyeon  *
2157808251SPyun YongHyeon  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2257808251SPyun YongHyeon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2357808251SPyun YongHyeon  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2457808251SPyun YongHyeon  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2557808251SPyun YongHyeon  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2657808251SPyun YongHyeon  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2757808251SPyun YongHyeon  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2857808251SPyun YongHyeon  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2957808251SPyun YongHyeon  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3057808251SPyun YongHyeon  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3157808251SPyun YongHyeon  * POSSIBILITY OF SUCH DAMAGE.
3257808251SPyun YongHyeon  */
3357808251SPyun YongHyeon 
3457808251SPyun YongHyeon /*
3557808251SPyun YongHyeon  * Device driver for the Sundance Tech. TC9021 10/100/1000
3657808251SPyun YongHyeon  * Ethernet controller.
3757808251SPyun YongHyeon  */
3857808251SPyun YongHyeon 
3957808251SPyun YongHyeon #include <sys/cdefs.h>
4057808251SPyun YongHyeon #ifdef HAVE_KERNEL_OPTION_HEADERS
4157808251SPyun YongHyeon #include "opt_device_polling.h"
4257808251SPyun YongHyeon #endif
4357808251SPyun YongHyeon 
4457808251SPyun YongHyeon #include <sys/param.h>
4557808251SPyun YongHyeon #include <sys/systm.h>
4657808251SPyun YongHyeon #include <sys/endian.h>
4757808251SPyun YongHyeon #include <sys/mbuf.h>
4857808251SPyun YongHyeon #include <sys/malloc.h>
4957808251SPyun YongHyeon #include <sys/kernel.h>
5057808251SPyun YongHyeon #include <sys/module.h>
5157808251SPyun YongHyeon #include <sys/socket.h>
5257808251SPyun YongHyeon #include <sys/sockio.h>
5357808251SPyun YongHyeon #include <sys/sysctl.h>
5457808251SPyun YongHyeon #include <sys/taskqueue.h>
5557808251SPyun YongHyeon 
5657808251SPyun YongHyeon #include <net/bpf.h>
5757808251SPyun YongHyeon #include <net/ethernet.h>
5857808251SPyun YongHyeon #include <net/if.h>
5976039bc8SGleb Smirnoff #include <net/if_var.h>
6057808251SPyun YongHyeon #include <net/if_dl.h>
6157808251SPyun YongHyeon #include <net/if_media.h>
6257808251SPyun YongHyeon #include <net/if_types.h>
6357808251SPyun YongHyeon #include <net/if_vlan_var.h>
6457808251SPyun YongHyeon 
6557808251SPyun YongHyeon #include <machine/bus.h>
6657808251SPyun YongHyeon #include <machine/resource.h>
6757808251SPyun YongHyeon #include <sys/bus.h>
6857808251SPyun YongHyeon #include <sys/rman.h>
6957808251SPyun YongHyeon 
7057808251SPyun YongHyeon #include <dev/mii/mii.h>
718c1093fcSMarius Strobl #include <dev/mii/mii_bitbang.h>
7257808251SPyun YongHyeon #include <dev/mii/miivar.h>
7357808251SPyun YongHyeon 
7457808251SPyun YongHyeon #include <dev/pci/pcireg.h>
7557808251SPyun YongHyeon #include <dev/pci/pcivar.h>
7657808251SPyun YongHyeon 
7757808251SPyun YongHyeon #include <dev/stge/if_stgereg.h>
7857808251SPyun YongHyeon 
7957808251SPyun YongHyeon #define	STGE_CSUM_FEATURES	(CSUM_IP | CSUM_TCP | CSUM_UDP)
8057808251SPyun YongHyeon 
8157808251SPyun YongHyeon MODULE_DEPEND(stge, pci, 1, 1, 1);
8257808251SPyun YongHyeon MODULE_DEPEND(stge, ether, 1, 1, 1);
8357808251SPyun YongHyeon MODULE_DEPEND(stge, miibus, 1, 1, 1);
8457808251SPyun YongHyeon 
8557808251SPyun YongHyeon /* "device miibus" required.  See GENERIC if you get errors here. */
8657808251SPyun YongHyeon #include "miibus_if.h"
8757808251SPyun YongHyeon 
8857808251SPyun YongHyeon /*
8957808251SPyun YongHyeon  * Devices supported by this driver.
9057808251SPyun YongHyeon  */
918c1093fcSMarius Strobl static const struct stge_product {
9257808251SPyun YongHyeon 	uint16_t	stge_vendorid;
9357808251SPyun YongHyeon 	uint16_t	stge_deviceid;
9457808251SPyun YongHyeon 	const char	*stge_name;
9529658c96SDimitry Andric } stge_products[] = {
9657808251SPyun YongHyeon 	{ VENDOR_SUNDANCETI,	DEVICEID_SUNDANCETI_ST1023,
9757808251SPyun YongHyeon 	  "Sundance ST-1023 Gigabit Ethernet" },
9857808251SPyun YongHyeon 
9957808251SPyun YongHyeon 	{ VENDOR_SUNDANCETI,	DEVICEID_SUNDANCETI_ST2021,
10057808251SPyun YongHyeon 	  "Sundance ST-2021 Gigabit Ethernet" },
10157808251SPyun YongHyeon 
10257808251SPyun YongHyeon 	{ VENDOR_TAMARACK,	DEVICEID_TAMARACK_TC9021,
10357808251SPyun YongHyeon 	  "Tamarack TC9021 Gigabit Ethernet" },
10457808251SPyun YongHyeon 
10557808251SPyun YongHyeon 	{ VENDOR_TAMARACK,	DEVICEID_TAMARACK_TC9021_ALT,
10657808251SPyun YongHyeon 	  "Tamarack TC9021 Gigabit Ethernet" },
10757808251SPyun YongHyeon 
10857808251SPyun YongHyeon 	/*
10957808251SPyun YongHyeon 	 * The Sundance sample boards use the Sundance vendor ID,
11057808251SPyun YongHyeon 	 * but the Tamarack product ID.
11157808251SPyun YongHyeon 	 */
11257808251SPyun YongHyeon 	{ VENDOR_SUNDANCETI,	DEVICEID_TAMARACK_TC9021,
11357808251SPyun YongHyeon 	  "Sundance TC9021 Gigabit Ethernet" },
11457808251SPyun YongHyeon 
11557808251SPyun YongHyeon 	{ VENDOR_SUNDANCETI,	DEVICEID_TAMARACK_TC9021_ALT,
11657808251SPyun YongHyeon 	  "Sundance TC9021 Gigabit Ethernet" },
11757808251SPyun YongHyeon 
11857808251SPyun YongHyeon 	{ VENDOR_DLINK,		DEVICEID_DLINK_DL4000,
11957808251SPyun YongHyeon 	  "D-Link DL-4000 Gigabit Ethernet" },
12057808251SPyun YongHyeon 
12157808251SPyun YongHyeon 	{ VENDOR_ANTARES,	DEVICEID_ANTARES_TC9021,
12257808251SPyun YongHyeon 	  "Antares Gigabit Ethernet" }
12357808251SPyun YongHyeon };
12457808251SPyun YongHyeon 
12557808251SPyun YongHyeon static int	stge_probe(device_t);
12657808251SPyun YongHyeon static int	stge_attach(device_t);
12757808251SPyun YongHyeon static int	stge_detach(device_t);
1286a087a87SPyun YongHyeon static int	stge_shutdown(device_t);
12957808251SPyun YongHyeon static int	stge_suspend(device_t);
13057808251SPyun YongHyeon static int	stge_resume(device_t);
13157808251SPyun YongHyeon 
13257808251SPyun YongHyeon static int	stge_encap(struct stge_softc *, struct mbuf **);
13308ca347fSJustin Hibbits static void	stge_start(if_t);
13408ca347fSJustin Hibbits static void	stge_start_locked(if_t);
135eb7a67daSPyun YongHyeon static void	stge_watchdog(struct stge_softc *);
13608ca347fSJustin Hibbits static int	stge_ioctl(if_t, u_long, caddr_t);
13757808251SPyun YongHyeon static void	stge_init(void *);
13857808251SPyun YongHyeon static void	stge_init_locked(struct stge_softc *);
13957808251SPyun YongHyeon static void	stge_vlan_setup(struct stge_softc *);
14057808251SPyun YongHyeon static void	stge_stop(struct stge_softc *);
14157808251SPyun YongHyeon static void	stge_start_tx(struct stge_softc *);
14257808251SPyun YongHyeon static void	stge_start_rx(struct stge_softc *);
14357808251SPyun YongHyeon static void	stge_stop_tx(struct stge_softc *);
14457808251SPyun YongHyeon static void	stge_stop_rx(struct stge_softc *);
14557808251SPyun YongHyeon 
14657808251SPyun YongHyeon static void	stge_reset(struct stge_softc *, uint32_t);
14757808251SPyun YongHyeon static int	stge_eeprom_wait(struct stge_softc *);
14857808251SPyun YongHyeon static void	stge_read_eeprom(struct stge_softc *, int, uint16_t *);
14957808251SPyun YongHyeon static void	stge_tick(void *);
15057808251SPyun YongHyeon static void	stge_stats_update(struct stge_softc *);
15157808251SPyun YongHyeon static void	stge_set_filter(struct stge_softc *);
15257808251SPyun YongHyeon static void	stge_set_multi(struct stge_softc *);
15357808251SPyun YongHyeon 
15457808251SPyun YongHyeon static void	stge_link_task(void *, int);
15557808251SPyun YongHyeon static void	stge_intr(void *);
15657808251SPyun YongHyeon static __inline int stge_tx_error(struct stge_softc *);
15757808251SPyun YongHyeon static void	stge_txeof(struct stge_softc *);
1581abcdbd1SAttilio Rao static int	stge_rxeof(struct stge_softc *);
15957808251SPyun YongHyeon static __inline void stge_discard_rxbuf(struct stge_softc *, int);
16057808251SPyun YongHyeon static int	stge_newbuf(struct stge_softc *, int);
16157808251SPyun YongHyeon #ifndef __NO_STRICT_ALIGNMENT
16257808251SPyun YongHyeon static __inline struct mbuf *stge_fixup_rx(struct stge_softc *, struct mbuf *);
16357808251SPyun YongHyeon #endif
16457808251SPyun YongHyeon 
16557808251SPyun YongHyeon static int	stge_miibus_readreg(device_t, int, int);
16657808251SPyun YongHyeon static int	stge_miibus_writereg(device_t, int, int, int);
16757808251SPyun YongHyeon static void	stge_miibus_statchg(device_t);
16808ca347fSJustin Hibbits static int	stge_mediachange(if_t);
16908ca347fSJustin Hibbits static void	stge_mediastatus(if_t, struct ifmediareq *);
17057808251SPyun YongHyeon 
17157808251SPyun YongHyeon static void	stge_dmamap_cb(void *, bus_dma_segment_t *, int, int);
17257808251SPyun YongHyeon static int	stge_dma_alloc(struct stge_softc *);
17357808251SPyun YongHyeon static void	stge_dma_free(struct stge_softc *);
17457808251SPyun YongHyeon static void	stge_dma_wait(struct stge_softc *);
17557808251SPyun YongHyeon static void	stge_init_tx_ring(struct stge_softc *);
17657808251SPyun YongHyeon static int	stge_init_rx_ring(struct stge_softc *);
17757808251SPyun YongHyeon #ifdef DEVICE_POLLING
17808ca347fSJustin Hibbits static int	stge_poll(if_t, enum poll_cmd, int);
17957808251SPyun YongHyeon #endif
18057808251SPyun YongHyeon 
181346de09fSPyun YongHyeon static void	stge_setwol(struct stge_softc *);
18257808251SPyun YongHyeon static int	sysctl_int_range(SYSCTL_HANDLER_ARGS, int, int);
18357808251SPyun YongHyeon static int	sysctl_hw_stge_rxint_nframe(SYSCTL_HANDLER_ARGS);
18457808251SPyun YongHyeon static int	sysctl_hw_stge_rxint_dmawait(SYSCTL_HANDLER_ARGS);
18557808251SPyun YongHyeon 
1868c1093fcSMarius Strobl /*
1878c1093fcSMarius Strobl  * MII bit-bang glue
1888c1093fcSMarius Strobl  */
1898c1093fcSMarius Strobl static uint32_t stge_mii_bitbang_read(device_t);
1908c1093fcSMarius Strobl static void	stge_mii_bitbang_write(device_t, uint32_t);
1918c1093fcSMarius Strobl 
1928c1093fcSMarius Strobl static const struct mii_bitbang_ops stge_mii_bitbang_ops = {
1938c1093fcSMarius Strobl 	stge_mii_bitbang_read,
1948c1093fcSMarius Strobl 	stge_mii_bitbang_write,
1958c1093fcSMarius Strobl 	{
1968c1093fcSMarius Strobl 		PC_MgmtData,		/* MII_BIT_MDO */
1978c1093fcSMarius Strobl 		PC_MgmtData,		/* MII_BIT_MDI */
1988c1093fcSMarius Strobl 		PC_MgmtClk,		/* MII_BIT_MDC */
1998c1093fcSMarius Strobl 		PC_MgmtDir,		/* MII_BIT_DIR_HOST_PHY */
2008c1093fcSMarius Strobl 		0,			/* MII_BIT_DIR_PHY_HOST */
2018c1093fcSMarius Strobl 	}
2028c1093fcSMarius Strobl };
2038c1093fcSMarius Strobl 
20457808251SPyun YongHyeon static device_method_t stge_methods[] = {
20557808251SPyun YongHyeon 	/* Device interface */
20657808251SPyun YongHyeon 	DEVMETHOD(device_probe,		stge_probe),
20757808251SPyun YongHyeon 	DEVMETHOD(device_attach,	stge_attach),
20857808251SPyun YongHyeon 	DEVMETHOD(device_detach,	stge_detach),
20957808251SPyun YongHyeon 	DEVMETHOD(device_shutdown,	stge_shutdown),
21057808251SPyun YongHyeon 	DEVMETHOD(device_suspend,	stge_suspend),
21157808251SPyun YongHyeon 	DEVMETHOD(device_resume,	stge_resume),
21257808251SPyun YongHyeon 
21357808251SPyun YongHyeon 	/* MII interface */
21457808251SPyun YongHyeon 	DEVMETHOD(miibus_readreg,	stge_miibus_readreg),
21557808251SPyun YongHyeon 	DEVMETHOD(miibus_writereg,	stge_miibus_writereg),
21657808251SPyun YongHyeon 	DEVMETHOD(miibus_statchg,	stge_miibus_statchg),
21757808251SPyun YongHyeon 
218848e30ffSMarius Strobl 	DEVMETHOD_END
21957808251SPyun YongHyeon };
22057808251SPyun YongHyeon 
22157808251SPyun YongHyeon static driver_t stge_driver = {
22257808251SPyun YongHyeon 	"stge",
22357808251SPyun YongHyeon 	stge_methods,
22457808251SPyun YongHyeon 	sizeof(struct stge_softc)
22557808251SPyun YongHyeon };
22657808251SPyun YongHyeon 
2276540641cSJohn Baldwin DRIVER_MODULE(stge, pci, stge_driver, 0, 0);
2283e38757dSJohn Baldwin DRIVER_MODULE(miibus, stge, miibus_driver, 0, 0);
22957808251SPyun YongHyeon 
23057808251SPyun YongHyeon static struct resource_spec stge_res_spec_io[] = {
23157808251SPyun YongHyeon 	{ SYS_RES_IOPORT,	PCIR_BAR(0),	RF_ACTIVE },
23257808251SPyun YongHyeon 	{ SYS_RES_IRQ,		0,		RF_ACTIVE | RF_SHAREABLE },
23357808251SPyun YongHyeon 	{ -1,			0,		0 }
23457808251SPyun YongHyeon };
23557808251SPyun YongHyeon 
23657808251SPyun YongHyeon static struct resource_spec stge_res_spec_mem[] = {
23757808251SPyun YongHyeon 	{ SYS_RES_MEMORY,	PCIR_BAR(1),	RF_ACTIVE },
23857808251SPyun YongHyeon 	{ SYS_RES_IRQ,		0,		RF_ACTIVE | RF_SHAREABLE },
23957808251SPyun YongHyeon 	{ -1,			0,		0 }
24057808251SPyun YongHyeon };
24157808251SPyun YongHyeon 
2428c1093fcSMarius Strobl /*
2438c1093fcSMarius Strobl  * stge_mii_bitbang_read: [mii bit-bang interface function]
2448c1093fcSMarius Strobl  *
2458c1093fcSMarius Strobl  *	Read the MII serial port for the MII bit-bang module.
2468c1093fcSMarius Strobl  */
2478c1093fcSMarius Strobl static uint32_t
stge_mii_bitbang_read(device_t dev)2488c1093fcSMarius Strobl stge_mii_bitbang_read(device_t dev)
2498c1093fcSMarius Strobl {
2508c1093fcSMarius Strobl 	struct stge_softc *sc;
2518c1093fcSMarius Strobl 	uint32_t val;
2528c1093fcSMarius Strobl 
2538c1093fcSMarius Strobl 	sc = device_get_softc(dev);
2548c1093fcSMarius Strobl 
2558c1093fcSMarius Strobl 	val = CSR_READ_1(sc, STGE_PhyCtrl);
2568c1093fcSMarius Strobl 	CSR_BARRIER(sc, STGE_PhyCtrl, 1,
2578c1093fcSMarius Strobl 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
2588c1093fcSMarius Strobl 	return (val);
2598c1093fcSMarius Strobl }
26057808251SPyun YongHyeon 
26157808251SPyun YongHyeon /*
2628c1093fcSMarius Strobl  * stge_mii_bitbang_write: [mii big-bang interface function]
2638c1093fcSMarius Strobl  *
2648c1093fcSMarius Strobl  *	Write the MII serial port for the MII bit-bang module.
26557808251SPyun YongHyeon  */
26657808251SPyun YongHyeon static void
stge_mii_bitbang_write(device_t dev,uint32_t val)2678c1093fcSMarius Strobl stge_mii_bitbang_write(device_t dev, uint32_t val)
26857808251SPyun YongHyeon {
2698c1093fcSMarius Strobl 	struct stge_softc *sc;
27057808251SPyun YongHyeon 
2718c1093fcSMarius Strobl 	sc = device_get_softc(dev);
27257808251SPyun YongHyeon 
2738c1093fcSMarius Strobl 	CSR_WRITE_1(sc, STGE_PhyCtrl, val);
2748c1093fcSMarius Strobl 	CSR_BARRIER(sc, STGE_PhyCtrl, 1,
2758c1093fcSMarius Strobl 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
27657808251SPyun YongHyeon }
27757808251SPyun YongHyeon 
27857808251SPyun YongHyeon /*
27957808251SPyun YongHyeon  * sc_miibus_readreg:	[mii interface function]
28057808251SPyun YongHyeon  *
28157808251SPyun YongHyeon  *	Read a PHY register on the MII of the TC9021.
28257808251SPyun YongHyeon  */
28357808251SPyun YongHyeon static int
stge_miibus_readreg(device_t dev,int phy,int reg)28457808251SPyun YongHyeon stge_miibus_readreg(device_t dev, int phy, int reg)
28557808251SPyun YongHyeon {
28657808251SPyun YongHyeon 	struct stge_softc *sc;
2878c1093fcSMarius Strobl 	int error, val;
28857808251SPyun YongHyeon 
28957808251SPyun YongHyeon 	sc = device_get_softc(dev);
29057808251SPyun YongHyeon 
29157808251SPyun YongHyeon 	if (reg == STGE_PhyCtrl) {
29257808251SPyun YongHyeon 		/* XXX allow ip1000phy read STGE_PhyCtrl register. */
29357808251SPyun YongHyeon 		STGE_MII_LOCK(sc);
29457808251SPyun YongHyeon 		error = CSR_READ_1(sc, STGE_PhyCtrl);
29557808251SPyun YongHyeon 		STGE_MII_UNLOCK(sc);
29657808251SPyun YongHyeon 		return (error);
29757808251SPyun YongHyeon 	}
29857808251SPyun YongHyeon 
29957808251SPyun YongHyeon 	STGE_MII_LOCK(sc);
3008c1093fcSMarius Strobl 	val = mii_bitbang_readreg(dev, &stge_mii_bitbang_ops, phy, reg);
30157808251SPyun YongHyeon 	STGE_MII_UNLOCK(sc);
3028c1093fcSMarius Strobl 	return (val);
30357808251SPyun YongHyeon }
30457808251SPyun YongHyeon 
30557808251SPyun YongHyeon /*
30657808251SPyun YongHyeon  * stge_miibus_writereg:	[mii interface function]
30757808251SPyun YongHyeon  *
30857808251SPyun YongHyeon  *	Write a PHY register on the MII of the TC9021.
30957808251SPyun YongHyeon  */
31057808251SPyun YongHyeon static int
stge_miibus_writereg(device_t dev,int phy,int reg,int val)31157808251SPyun YongHyeon stge_miibus_writereg(device_t dev, int phy, int reg, int val)
31257808251SPyun YongHyeon {
31357808251SPyun YongHyeon 	struct stge_softc *sc;
31457808251SPyun YongHyeon 
31557808251SPyun YongHyeon 	sc = device_get_softc(dev);
31657808251SPyun YongHyeon 
31757808251SPyun YongHyeon 	STGE_MII_LOCK(sc);
3188c1093fcSMarius Strobl 	mii_bitbang_writereg(dev, &stge_mii_bitbang_ops, phy, reg, val);
31957808251SPyun YongHyeon 	STGE_MII_UNLOCK(sc);
32057808251SPyun YongHyeon 	return (0);
32157808251SPyun YongHyeon }
32257808251SPyun YongHyeon 
32357808251SPyun YongHyeon /*
32457808251SPyun YongHyeon  * stge_miibus_statchg:	[mii interface function]
32557808251SPyun YongHyeon  *
32657808251SPyun YongHyeon  *	Callback from MII layer when media changes.
32757808251SPyun YongHyeon  */
32857808251SPyun YongHyeon static void
stge_miibus_statchg(device_t dev)32957808251SPyun YongHyeon stge_miibus_statchg(device_t dev)
33057808251SPyun YongHyeon {
33157808251SPyun YongHyeon 	struct stge_softc *sc;
33257808251SPyun YongHyeon 
33357808251SPyun YongHyeon 	sc = device_get_softc(dev);
33457808251SPyun YongHyeon 	taskqueue_enqueue(taskqueue_swi, &sc->sc_link_task);
33557808251SPyun YongHyeon }
33657808251SPyun YongHyeon 
33757808251SPyun YongHyeon /*
33857808251SPyun YongHyeon  * stge_mediastatus:	[ifmedia interface function]
33957808251SPyun YongHyeon  *
34057808251SPyun YongHyeon  *	Get the current interface media status.
34157808251SPyun YongHyeon  */
34257808251SPyun YongHyeon static void
stge_mediastatus(if_t ifp,struct ifmediareq * ifmr)34308ca347fSJustin Hibbits stge_mediastatus(if_t ifp, struct ifmediareq *ifmr)
34457808251SPyun YongHyeon {
34557808251SPyun YongHyeon 	struct stge_softc *sc;
34657808251SPyun YongHyeon 	struct mii_data *mii;
34757808251SPyun YongHyeon 
34808ca347fSJustin Hibbits 	sc = if_getsoftc(ifp);
34957808251SPyun YongHyeon 	mii = device_get_softc(sc->sc_miibus);
35057808251SPyun YongHyeon 
35157808251SPyun YongHyeon 	mii_pollstat(mii);
35257808251SPyun YongHyeon 	ifmr->ifm_status = mii->mii_media_status;
35357808251SPyun YongHyeon 	ifmr->ifm_active = mii->mii_media_active;
35457808251SPyun YongHyeon }
35557808251SPyun YongHyeon 
35657808251SPyun YongHyeon /*
35757808251SPyun YongHyeon  * stge_mediachange:	[ifmedia interface function]
35857808251SPyun YongHyeon  *
35957808251SPyun YongHyeon  *	Set hardware to newly-selected media.
36057808251SPyun YongHyeon  */
36157808251SPyun YongHyeon static int
stge_mediachange(if_t ifp)36208ca347fSJustin Hibbits stge_mediachange(if_t ifp)
36357808251SPyun YongHyeon {
36457808251SPyun YongHyeon 	struct stge_softc *sc;
36557808251SPyun YongHyeon 	struct mii_data *mii;
36657808251SPyun YongHyeon 
36708ca347fSJustin Hibbits 	sc = if_getsoftc(ifp);
36857808251SPyun YongHyeon 	mii = device_get_softc(sc->sc_miibus);
36957808251SPyun YongHyeon 	mii_mediachg(mii);
37057808251SPyun YongHyeon 
37157808251SPyun YongHyeon 	return (0);
37257808251SPyun YongHyeon }
37357808251SPyun YongHyeon 
37457808251SPyun YongHyeon static int
stge_eeprom_wait(struct stge_softc * sc)37557808251SPyun YongHyeon stge_eeprom_wait(struct stge_softc *sc)
37657808251SPyun YongHyeon {
37757808251SPyun YongHyeon 	int i;
37857808251SPyun YongHyeon 
37957808251SPyun YongHyeon 	for (i = 0; i < STGE_TIMEOUT; i++) {
38057808251SPyun YongHyeon 		DELAY(1000);
38157808251SPyun YongHyeon 		if ((CSR_READ_2(sc, STGE_EepromCtrl) & EC_EepromBusy) == 0)
38257808251SPyun YongHyeon 			return (0);
38357808251SPyun YongHyeon 	}
38457808251SPyun YongHyeon 	return (1);
38557808251SPyun YongHyeon }
38657808251SPyun YongHyeon 
38757808251SPyun YongHyeon /*
38857808251SPyun YongHyeon  * stge_read_eeprom:
38957808251SPyun YongHyeon  *
39057808251SPyun YongHyeon  *	Read data from the serial EEPROM.
39157808251SPyun YongHyeon  */
39257808251SPyun YongHyeon static void
stge_read_eeprom(struct stge_softc * sc,int offset,uint16_t * data)39357808251SPyun YongHyeon stge_read_eeprom(struct stge_softc *sc, int offset, uint16_t *data)
39457808251SPyun YongHyeon {
39557808251SPyun YongHyeon 
39657808251SPyun YongHyeon 	if (stge_eeprom_wait(sc))
39757808251SPyun YongHyeon 		device_printf(sc->sc_dev, "EEPROM failed to come ready\n");
39857808251SPyun YongHyeon 
39957808251SPyun YongHyeon 	CSR_WRITE_2(sc, STGE_EepromCtrl,
40057808251SPyun YongHyeon 	    EC_EepromAddress(offset) | EC_EepromOpcode(EC_OP_RR));
40157808251SPyun YongHyeon 	if (stge_eeprom_wait(sc))
40257808251SPyun YongHyeon 		device_printf(sc->sc_dev, "EEPROM read timed out\n");
40357808251SPyun YongHyeon 	*data = CSR_READ_2(sc, STGE_EepromData);
40457808251SPyun YongHyeon }
40557808251SPyun YongHyeon 
40657808251SPyun YongHyeon static int
stge_probe(device_t dev)40757808251SPyun YongHyeon stge_probe(device_t dev)
40857808251SPyun YongHyeon {
4098c1093fcSMarius Strobl 	const struct stge_product *sp;
41057808251SPyun YongHyeon 	int i;
41157808251SPyun YongHyeon 	uint16_t vendor, devid;
41257808251SPyun YongHyeon 
41357808251SPyun YongHyeon 	vendor = pci_get_vendor(dev);
41457808251SPyun YongHyeon 	devid = pci_get_device(dev);
41557808251SPyun YongHyeon 	sp = stge_products;
41673a1170aSPedro F. Giffuni 	for (i = 0; i < nitems(stge_products); i++, sp++) {
41757808251SPyun YongHyeon 		if (vendor == sp->stge_vendorid &&
41857808251SPyun YongHyeon 		    devid == sp->stge_deviceid) {
41957808251SPyun YongHyeon 			device_set_desc(dev, sp->stge_name);
42057808251SPyun YongHyeon 			return (BUS_PROBE_DEFAULT);
42157808251SPyun YongHyeon 		}
42257808251SPyun YongHyeon 	}
42357808251SPyun YongHyeon 
42457808251SPyun YongHyeon 	return (ENXIO);
42557808251SPyun YongHyeon }
42657808251SPyun YongHyeon 
42757808251SPyun YongHyeon static int
stge_attach(device_t dev)42857808251SPyun YongHyeon stge_attach(device_t dev)
42957808251SPyun YongHyeon {
43057808251SPyun YongHyeon 	struct stge_softc *sc;
43108ca347fSJustin Hibbits 	if_t ifp;
43257808251SPyun YongHyeon 	uint8_t enaddr[ETHER_ADDR_LEN];
4338e5d93dbSMarius Strobl 	int error, flags, i;
43457808251SPyun YongHyeon 	uint16_t cmd;
43557808251SPyun YongHyeon 	uint32_t val;
43657808251SPyun YongHyeon 
43757808251SPyun YongHyeon 	error = 0;
43857808251SPyun YongHyeon 	sc = device_get_softc(dev);
43957808251SPyun YongHyeon 	sc->sc_dev = dev;
44057808251SPyun YongHyeon 
44157808251SPyun YongHyeon 	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
44257808251SPyun YongHyeon 	    MTX_DEF);
44357808251SPyun YongHyeon 	mtx_init(&sc->sc_mii_mtx, "stge_mii_mutex", NULL, MTX_DEF);
44457808251SPyun YongHyeon 	callout_init_mtx(&sc->sc_tick_ch, &sc->sc_mtx, 0);
44557808251SPyun YongHyeon 	TASK_INIT(&sc->sc_link_task, 0, stge_link_task, sc);
44657808251SPyun YongHyeon 
44757808251SPyun YongHyeon 	/*
44857808251SPyun YongHyeon 	 * Map the device.
44957808251SPyun YongHyeon 	 */
45057808251SPyun YongHyeon 	pci_enable_busmaster(dev);
45157808251SPyun YongHyeon 	cmd = pci_read_config(dev, PCIR_COMMAND, 2);
45257808251SPyun YongHyeon 	val = pci_read_config(dev, PCIR_BAR(1), 4);
453c68534f1SScott Long 	if (PCI_BAR_IO(val))
45457808251SPyun YongHyeon 		sc->sc_spec = stge_res_spec_mem;
45557808251SPyun YongHyeon 	else {
45657808251SPyun YongHyeon 		val = pci_read_config(dev, PCIR_BAR(0), 4);
457c68534f1SScott Long 		if (!PCI_BAR_IO(val)) {
45857808251SPyun YongHyeon 			device_printf(sc->sc_dev, "couldn't locate IO BAR\n");
45957808251SPyun YongHyeon 			error = ENXIO;
46057808251SPyun YongHyeon 			goto fail;
46157808251SPyun YongHyeon 		}
46257808251SPyun YongHyeon 		sc->sc_spec = stge_res_spec_io;
46357808251SPyun YongHyeon 	}
46457808251SPyun YongHyeon 	error = bus_alloc_resources(dev, sc->sc_spec, sc->sc_res);
46557808251SPyun YongHyeon 	if (error != 0) {
46657808251SPyun YongHyeon 		device_printf(dev, "couldn't allocate %s resources\n",
46757808251SPyun YongHyeon 		    sc->sc_spec == stge_res_spec_mem ? "memory" : "I/O");
46857808251SPyun YongHyeon 		goto fail;
46957808251SPyun YongHyeon 	}
47057808251SPyun YongHyeon 	sc->sc_rev = pci_get_revid(dev);
47157808251SPyun YongHyeon 
47257808251SPyun YongHyeon 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
47357808251SPyun YongHyeon 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
4747029da5cSPawel Biernacki 	    "rxint_nframe", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
4757029da5cSPawel Biernacki 	    &sc->sc_rxint_nframe, 0, sysctl_hw_stge_rxint_nframe, "I",
4767029da5cSPawel Biernacki 	    "stge rx interrupt nframe");
47757808251SPyun YongHyeon 
47857808251SPyun YongHyeon 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
47957808251SPyun YongHyeon 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
4807029da5cSPawel Biernacki 	    "rxint_dmawait", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
4817029da5cSPawel Biernacki 	    &sc->sc_rxint_dmawait, 0, sysctl_hw_stge_rxint_dmawait, "I",
4827029da5cSPawel Biernacki 	    "stge rx interrupt dmawait");
48357808251SPyun YongHyeon 
48457808251SPyun YongHyeon 	/* Pull in device tunables. */
48557808251SPyun YongHyeon 	sc->sc_rxint_nframe = STGE_RXINT_NFRAME_DEFAULT;
48657808251SPyun YongHyeon 	error = resource_int_value(device_get_name(dev), device_get_unit(dev),
48757808251SPyun YongHyeon 	    "rxint_nframe", &sc->sc_rxint_nframe);
48857808251SPyun YongHyeon 	if (error == 0) {
48957808251SPyun YongHyeon 		if (sc->sc_rxint_nframe < STGE_RXINT_NFRAME_MIN ||
49057808251SPyun YongHyeon 		    sc->sc_rxint_nframe > STGE_RXINT_NFRAME_MAX) {
49157808251SPyun YongHyeon 			device_printf(dev, "rxint_nframe value out of range; "
49257808251SPyun YongHyeon 			    "using default: %d\n", STGE_RXINT_NFRAME_DEFAULT);
49357808251SPyun YongHyeon 			sc->sc_rxint_nframe = STGE_RXINT_NFRAME_DEFAULT;
49457808251SPyun YongHyeon 		}
49557808251SPyun YongHyeon 	}
49657808251SPyun YongHyeon 
49757808251SPyun YongHyeon 	sc->sc_rxint_dmawait = STGE_RXINT_DMAWAIT_DEFAULT;
49857808251SPyun YongHyeon 	error = resource_int_value(device_get_name(dev), device_get_unit(dev),
49957808251SPyun YongHyeon 	    "rxint_dmawait", &sc->sc_rxint_dmawait);
50057808251SPyun YongHyeon 	if (error == 0) {
50157808251SPyun YongHyeon 		if (sc->sc_rxint_dmawait < STGE_RXINT_DMAWAIT_MIN ||
50257808251SPyun YongHyeon 		    sc->sc_rxint_dmawait > STGE_RXINT_DMAWAIT_MAX) {
50357808251SPyun YongHyeon 			device_printf(dev, "rxint_dmawait value out of range; "
50457808251SPyun YongHyeon 			    "using default: %d\n", STGE_RXINT_DMAWAIT_DEFAULT);
50557808251SPyun YongHyeon 			sc->sc_rxint_dmawait = STGE_RXINT_DMAWAIT_DEFAULT;
50657808251SPyun YongHyeon 		}
50757808251SPyun YongHyeon 	}
50857808251SPyun YongHyeon 
5099dda5c8fSPyun YongHyeon 	if ((error = stge_dma_alloc(sc)) != 0)
51057808251SPyun YongHyeon 		goto fail;
51157808251SPyun YongHyeon 
51257808251SPyun YongHyeon 	/*
51357808251SPyun YongHyeon 	 * Determine if we're copper or fiber.  It affects how we
51457808251SPyun YongHyeon 	 * reset the card.
51557808251SPyun YongHyeon 	 */
51657808251SPyun YongHyeon 	if (CSR_READ_4(sc, STGE_AsicCtrl) & AC_PhyMedia)
51757808251SPyun YongHyeon 		sc->sc_usefiber = 1;
51857808251SPyun YongHyeon 	else
51957808251SPyun YongHyeon 		sc->sc_usefiber = 0;
52057808251SPyun YongHyeon 
52157808251SPyun YongHyeon 	/* Load LED configuration from EEPROM. */
52257808251SPyun YongHyeon 	stge_read_eeprom(sc, STGE_EEPROM_LEDMode, &sc->sc_led);
52357808251SPyun YongHyeon 
52457808251SPyun YongHyeon 	/*
52557808251SPyun YongHyeon 	 * Reset the chip to a known state.
52657808251SPyun YongHyeon 	 */
52757808251SPyun YongHyeon 	STGE_LOCK(sc);
52857808251SPyun YongHyeon 	stge_reset(sc, STGE_RESET_FULL);
52957808251SPyun YongHyeon 	STGE_UNLOCK(sc);
53057808251SPyun YongHyeon 
53157808251SPyun YongHyeon 	/*
53257808251SPyun YongHyeon 	 * Reading the station address from the EEPROM doesn't seem
53357808251SPyun YongHyeon 	 * to work, at least on my sample boards.  Instead, since
53457808251SPyun YongHyeon 	 * the reset sequence does AutoInit, read it from the station
53557808251SPyun YongHyeon 	 * address registers. For Sundance 1023 you can only read it
53657808251SPyun YongHyeon 	 * from EEPROM.
53757808251SPyun YongHyeon 	 */
53857808251SPyun YongHyeon 	if (pci_get_device(dev) != DEVICEID_SUNDANCETI_ST1023) {
53957808251SPyun YongHyeon 		uint16_t v;
54057808251SPyun YongHyeon 
54157808251SPyun YongHyeon 		v = CSR_READ_2(sc, STGE_StationAddress0);
54257808251SPyun YongHyeon 		enaddr[0] = v & 0xff;
54357808251SPyun YongHyeon 		enaddr[1] = v >> 8;
54457808251SPyun YongHyeon 		v = CSR_READ_2(sc, STGE_StationAddress1);
54557808251SPyun YongHyeon 		enaddr[2] = v & 0xff;
54657808251SPyun YongHyeon 		enaddr[3] = v >> 8;
54757808251SPyun YongHyeon 		v = CSR_READ_2(sc, STGE_StationAddress2);
54857808251SPyun YongHyeon 		enaddr[4] = v & 0xff;
54957808251SPyun YongHyeon 		enaddr[5] = v >> 8;
55057808251SPyun YongHyeon 		sc->sc_stge1023 = 0;
55157808251SPyun YongHyeon 	} else {
55257808251SPyun YongHyeon 		uint16_t myaddr[ETHER_ADDR_LEN / 2];
55357808251SPyun YongHyeon 		for (i = 0; i <ETHER_ADDR_LEN / 2; i++) {
55457808251SPyun YongHyeon 			stge_read_eeprom(sc, STGE_EEPROM_StationAddress0 + i,
55557808251SPyun YongHyeon 			    &myaddr[i]);
55657808251SPyun YongHyeon 			myaddr[i] = le16toh(myaddr[i]);
55757808251SPyun YongHyeon 		}
55857808251SPyun YongHyeon 		bcopy(myaddr, enaddr, sizeof(enaddr));
55957808251SPyun YongHyeon 		sc->sc_stge1023 = 1;
56057808251SPyun YongHyeon 	}
56157808251SPyun YongHyeon 
56257808251SPyun YongHyeon 	ifp = sc->sc_ifp = if_alloc(IFT_ETHER);
56308ca347fSJustin Hibbits 	if_setsoftc(ifp, sc);
56457808251SPyun YongHyeon 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
56508ca347fSJustin Hibbits 	if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
56608ca347fSJustin Hibbits 	if_setioctlfn(ifp, stge_ioctl);
56708ca347fSJustin Hibbits 	if_setstartfn(ifp, stge_start);
56808ca347fSJustin Hibbits 	if_setinitfn(ifp, stge_init);
56908ca347fSJustin Hibbits 	if_setsendqlen(ifp, STGE_TX_RING_CNT - 1);
57008ca347fSJustin Hibbits 	if_setsendqready(ifp);
57157808251SPyun YongHyeon 	/* Revision B3 and earlier chips have checksum bug. */
57257808251SPyun YongHyeon 	if (sc->sc_rev >= 0x0c) {
57308ca347fSJustin Hibbits 		if_sethwassist(ifp, STGE_CSUM_FEATURES);
57408ca347fSJustin Hibbits 		if_setcapabilities(ifp, IFCAP_HWCSUM);
57557808251SPyun YongHyeon 	} else {
57608ca347fSJustin Hibbits 		if_sethwassist(ifp, 0);
57708ca347fSJustin Hibbits 		if_setcapabilities(ifp, 0);
57857808251SPyun YongHyeon 	}
57908ca347fSJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_WOL_MAGIC, 0);
58008ca347fSJustin Hibbits 	if_setcapenable(ifp, if_getcapabilities(ifp));
58157808251SPyun YongHyeon 
58257808251SPyun YongHyeon 	/*
58357808251SPyun YongHyeon 	 * Read some important bits from the PhyCtrl register.
58457808251SPyun YongHyeon 	 */
58557808251SPyun YongHyeon 	sc->sc_PhyCtrl = CSR_READ_1(sc, STGE_PhyCtrl) &
58657808251SPyun YongHyeon 	    (PC_PhyDuplexPolarity | PC_PhyLnkPolarity);
58757808251SPyun YongHyeon 
58857808251SPyun YongHyeon 	/* Set up MII bus. */
589efd4fc3fSMarius Strobl 	flags = MIIF_DOPAUSE;
5908e5d93dbSMarius Strobl 	if (sc->sc_rev >= 0x40 && sc->sc_rev <= 0x4e)
5918e5d93dbSMarius Strobl 		flags |= MIIF_MACPRIV0;
5928e5d93dbSMarius Strobl 	error = mii_attach(sc->sc_dev, &sc->sc_miibus, ifp, stge_mediachange,
5938e5d93dbSMarius Strobl 	    stge_mediastatus, BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY,
5948e5d93dbSMarius Strobl 	    flags);
5958e5d93dbSMarius Strobl 	if (error != 0) {
5968e5d93dbSMarius Strobl 		device_printf(sc->sc_dev, "attaching PHYs failed\n");
59757808251SPyun YongHyeon 		goto fail;
59857808251SPyun YongHyeon 	}
59957808251SPyun YongHyeon 
60057808251SPyun YongHyeon 	ether_ifattach(ifp, enaddr);
60157808251SPyun YongHyeon 
60257808251SPyun YongHyeon 	/* VLAN capability setup */
60308ca347fSJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING, 0);
60457808251SPyun YongHyeon 	if (sc->sc_rev >= 0x0c)
60508ca347fSJustin Hibbits 		if_setcapabilitiesbit(ifp, IFCAP_VLAN_HWCSUM, 0);
60608ca347fSJustin Hibbits 	if_setcapenable(ifp, if_getcapabilities(ifp));
60757808251SPyun YongHyeon #ifdef DEVICE_POLLING
60808ca347fSJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_POLLING, 0);
60957808251SPyun YongHyeon #endif
61057808251SPyun YongHyeon 	/*
61157808251SPyun YongHyeon 	 * Tell the upper layer(s) we support long frames.
61257808251SPyun YongHyeon 	 * Must appear after the call to ether_ifattach() because
61357808251SPyun YongHyeon 	 * ether_ifattach() sets ifi_hdrlen to the default value.
61457808251SPyun YongHyeon 	 */
61508ca347fSJustin Hibbits 	if_setifheaderlen(ifp, sizeof(struct ether_vlan_header));
61657808251SPyun YongHyeon 
61757808251SPyun YongHyeon 	/*
61857808251SPyun YongHyeon 	 * The manual recommends disabling early transmit, so we
61957808251SPyun YongHyeon 	 * do.  It's disabled anyway, if using IP checksumming,
62057808251SPyun YongHyeon 	 * since the entire packet must be in the FIFO in order
62157808251SPyun YongHyeon 	 * for the chip to perform the checksum.
62257808251SPyun YongHyeon 	 */
62357808251SPyun YongHyeon 	sc->sc_txthresh = 0x0fff;
62457808251SPyun YongHyeon 
62557808251SPyun YongHyeon 	/*
62657808251SPyun YongHyeon 	 * Disable MWI if the PCI layer tells us to.
62757808251SPyun YongHyeon 	 */
62857808251SPyun YongHyeon 	sc->sc_DMACtrl = 0;
62957808251SPyun YongHyeon 	if ((cmd & PCIM_CMD_MWRICEN) == 0)
63057808251SPyun YongHyeon 		sc->sc_DMACtrl |= DMAC_MWIDisable;
63157808251SPyun YongHyeon 
63257808251SPyun YongHyeon 	/*
63357808251SPyun YongHyeon 	 * Hookup IRQ
63457808251SPyun YongHyeon 	 */
63557808251SPyun YongHyeon 	error = bus_setup_intr(dev, sc->sc_res[1], INTR_TYPE_NET | INTR_MPSAFE,
636ef544f63SPaolo Pisati 	    NULL, stge_intr, sc, &sc->sc_ih);
63757808251SPyun YongHyeon 	if (error != 0) {
63857808251SPyun YongHyeon 		ether_ifdetach(ifp);
63957808251SPyun YongHyeon 		device_printf(sc->sc_dev, "couldn't set up IRQ\n");
64057808251SPyun YongHyeon 		sc->sc_ifp = NULL;
64157808251SPyun YongHyeon 		goto fail;
64257808251SPyun YongHyeon 	}
64357808251SPyun YongHyeon 
64457808251SPyun YongHyeon fail:
64557808251SPyun YongHyeon 	if (error != 0)
64657808251SPyun YongHyeon 		stge_detach(dev);
64757808251SPyun YongHyeon 
64857808251SPyun YongHyeon 	return (error);
64957808251SPyun YongHyeon }
65057808251SPyun YongHyeon 
65157808251SPyun YongHyeon static int
stge_detach(device_t dev)65257808251SPyun YongHyeon stge_detach(device_t dev)
65357808251SPyun YongHyeon {
65457808251SPyun YongHyeon 	struct stge_softc *sc;
65508ca347fSJustin Hibbits 	if_t ifp;
65657808251SPyun YongHyeon 
65757808251SPyun YongHyeon 	sc = device_get_softc(dev);
65857808251SPyun YongHyeon 
65957808251SPyun YongHyeon 	ifp = sc->sc_ifp;
66057808251SPyun YongHyeon #ifdef DEVICE_POLLING
66108ca347fSJustin Hibbits 	if (ifp && if_getcapenable(ifp) & IFCAP_POLLING)
66257808251SPyun YongHyeon 		ether_poll_deregister(ifp);
66357808251SPyun YongHyeon #endif
66457808251SPyun YongHyeon 	if (device_is_attached(dev)) {
66557808251SPyun YongHyeon 		STGE_LOCK(sc);
66657808251SPyun YongHyeon 		/* XXX */
66757808251SPyun YongHyeon 		sc->sc_detach = 1;
66857808251SPyun YongHyeon 		stge_stop(sc);
66957808251SPyun YongHyeon 		STGE_UNLOCK(sc);
67057808251SPyun YongHyeon 		callout_drain(&sc->sc_tick_ch);
67157808251SPyun YongHyeon 		taskqueue_drain(taskqueue_swi, &sc->sc_link_task);
67257808251SPyun YongHyeon 		ether_ifdetach(ifp);
67357808251SPyun YongHyeon 	}
67457808251SPyun YongHyeon 
67557808251SPyun YongHyeon 	bus_generic_detach(dev);
67657808251SPyun YongHyeon 	stge_dma_free(sc);
67757808251SPyun YongHyeon 
67857808251SPyun YongHyeon 	if (ifp != NULL) {
67957808251SPyun YongHyeon 		if_free(ifp);
68057808251SPyun YongHyeon 		sc->sc_ifp = NULL;
68157808251SPyun YongHyeon 	}
68257808251SPyun YongHyeon 
68357808251SPyun YongHyeon 	if (sc->sc_ih) {
68457808251SPyun YongHyeon 		bus_teardown_intr(dev, sc->sc_res[1], sc->sc_ih);
68557808251SPyun YongHyeon 		sc->sc_ih = NULL;
68657808251SPyun YongHyeon 	}
6872108cc72STong Zhang 
6882108cc72STong Zhang 	if (sc->sc_spec)
68957808251SPyun YongHyeon 		bus_release_resources(dev, sc->sc_spec, sc->sc_res);
69057808251SPyun YongHyeon 
69157808251SPyun YongHyeon 	mtx_destroy(&sc->sc_mii_mtx);
69257808251SPyun YongHyeon 	mtx_destroy(&sc->sc_mtx);
69357808251SPyun YongHyeon 
69457808251SPyun YongHyeon 	return (0);
69557808251SPyun YongHyeon }
69657808251SPyun YongHyeon 
69757808251SPyun YongHyeon struct stge_dmamap_arg {
69857808251SPyun YongHyeon 	bus_addr_t	stge_busaddr;
69957808251SPyun YongHyeon };
70057808251SPyun YongHyeon 
70157808251SPyun YongHyeon static void
stge_dmamap_cb(void * arg,bus_dma_segment_t * segs,int nseg,int error)70257808251SPyun YongHyeon stge_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
70357808251SPyun YongHyeon {
70457808251SPyun YongHyeon 	struct stge_dmamap_arg *ctx;
70557808251SPyun YongHyeon 
70657808251SPyun YongHyeon 	if (error != 0)
70757808251SPyun YongHyeon 		return;
70857808251SPyun YongHyeon 
70957808251SPyun YongHyeon 	ctx = (struct stge_dmamap_arg *)arg;
71057808251SPyun YongHyeon 	ctx->stge_busaddr = segs[0].ds_addr;
71157808251SPyun YongHyeon }
71257808251SPyun YongHyeon 
71357808251SPyun YongHyeon static int
stge_dma_alloc(struct stge_softc * sc)71457808251SPyun YongHyeon stge_dma_alloc(struct stge_softc *sc)
71557808251SPyun YongHyeon {
71657808251SPyun YongHyeon 	struct stge_dmamap_arg ctx;
71757808251SPyun YongHyeon 	struct stge_txdesc *txd;
71857808251SPyun YongHyeon 	struct stge_rxdesc *rxd;
71957808251SPyun YongHyeon 	int error, i;
72057808251SPyun YongHyeon 
72157808251SPyun YongHyeon 	/* create parent tag. */
722c2175ff5SMarius Strobl 	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev),/* parent */
72357808251SPyun YongHyeon 		    1, 0,			/* algnmnt, boundary */
72457808251SPyun YongHyeon 		    STGE_DMA_MAXADDR,		/* lowaddr */
72557808251SPyun YongHyeon 		    BUS_SPACE_MAXADDR,		/* highaddr */
72657808251SPyun YongHyeon 		    NULL, NULL,			/* filter, filterarg */
72757808251SPyun YongHyeon 		    BUS_SPACE_MAXSIZE_32BIT,	/* maxsize */
72857808251SPyun YongHyeon 		    0,				/* nsegments */
72957808251SPyun YongHyeon 		    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
73057808251SPyun YongHyeon 		    0,				/* flags */
73157808251SPyun YongHyeon 		    NULL, NULL,			/* lockfunc, lockarg */
73257808251SPyun YongHyeon 		    &sc->sc_cdata.stge_parent_tag);
73357808251SPyun YongHyeon 	if (error != 0) {
73457808251SPyun YongHyeon 		device_printf(sc->sc_dev, "failed to create parent DMA tag\n");
73557808251SPyun YongHyeon 		goto fail;
73657808251SPyun YongHyeon 	}
73757808251SPyun YongHyeon 	/* create tag for Tx ring. */
73857808251SPyun YongHyeon 	error = bus_dma_tag_create(sc->sc_cdata.stge_parent_tag,/* parent */
73957808251SPyun YongHyeon 		    STGE_RING_ALIGN, 0,		/* algnmnt, boundary */
74057808251SPyun YongHyeon 		    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
74157808251SPyun YongHyeon 		    BUS_SPACE_MAXADDR,		/* highaddr */
74257808251SPyun YongHyeon 		    NULL, NULL,			/* filter, filterarg */
74357808251SPyun YongHyeon 		    STGE_TX_RING_SZ,		/* maxsize */
74457808251SPyun YongHyeon 		    1,				/* nsegments */
74557808251SPyun YongHyeon 		    STGE_TX_RING_SZ,		/* maxsegsize */
74657808251SPyun YongHyeon 		    0,				/* flags */
74757808251SPyun YongHyeon 		    NULL, NULL,			/* lockfunc, lockarg */
74857808251SPyun YongHyeon 		    &sc->sc_cdata.stge_tx_ring_tag);
74957808251SPyun YongHyeon 	if (error != 0) {
75057808251SPyun YongHyeon 		device_printf(sc->sc_dev,
75157808251SPyun YongHyeon 		    "failed to allocate Tx ring DMA tag\n");
75257808251SPyun YongHyeon 		goto fail;
75357808251SPyun YongHyeon 	}
75457808251SPyun YongHyeon 
75557808251SPyun YongHyeon 	/* create tag for Rx ring. */
75657808251SPyun YongHyeon 	error = bus_dma_tag_create(sc->sc_cdata.stge_parent_tag,/* parent */
75757808251SPyun YongHyeon 		    STGE_RING_ALIGN, 0,		/* algnmnt, boundary */
75857808251SPyun YongHyeon 		    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
75957808251SPyun YongHyeon 		    BUS_SPACE_MAXADDR,		/* highaddr */
76057808251SPyun YongHyeon 		    NULL, NULL,			/* filter, filterarg */
76157808251SPyun YongHyeon 		    STGE_RX_RING_SZ,		/* maxsize */
76257808251SPyun YongHyeon 		    1,				/* nsegments */
76357808251SPyun YongHyeon 		    STGE_RX_RING_SZ,		/* maxsegsize */
76457808251SPyun YongHyeon 		    0,				/* flags */
76557808251SPyun YongHyeon 		    NULL, NULL,			/* lockfunc, lockarg */
76657808251SPyun YongHyeon 		    &sc->sc_cdata.stge_rx_ring_tag);
76757808251SPyun YongHyeon 	if (error != 0) {
76857808251SPyun YongHyeon 		device_printf(sc->sc_dev,
76957808251SPyun YongHyeon 		    "failed to allocate Rx ring DMA tag\n");
77057808251SPyun YongHyeon 		goto fail;
77157808251SPyun YongHyeon 	}
77257808251SPyun YongHyeon 
77357808251SPyun YongHyeon 	/* create tag for Tx buffers. */
77457808251SPyun YongHyeon 	error = bus_dma_tag_create(sc->sc_cdata.stge_parent_tag,/* parent */
77557808251SPyun YongHyeon 		    1, 0,			/* algnmnt, boundary */
77657808251SPyun YongHyeon 		    BUS_SPACE_MAXADDR,		/* lowaddr */
77757808251SPyun YongHyeon 		    BUS_SPACE_MAXADDR,		/* highaddr */
77857808251SPyun YongHyeon 		    NULL, NULL,			/* filter, filterarg */
77957808251SPyun YongHyeon 		    MCLBYTES * STGE_MAXTXSEGS,	/* maxsize */
78057808251SPyun YongHyeon 		    STGE_MAXTXSEGS,		/* nsegments */
78157808251SPyun YongHyeon 		    MCLBYTES,			/* maxsegsize */
78257808251SPyun YongHyeon 		    0,				/* flags */
78357808251SPyun YongHyeon 		    NULL, NULL,			/* lockfunc, lockarg */
78457808251SPyun YongHyeon 		    &sc->sc_cdata.stge_tx_tag);
78557808251SPyun YongHyeon 	if (error != 0) {
78657808251SPyun YongHyeon 		device_printf(sc->sc_dev, "failed to allocate Tx DMA tag\n");
78757808251SPyun YongHyeon 		goto fail;
78857808251SPyun YongHyeon 	}
78957808251SPyun YongHyeon 
79057808251SPyun YongHyeon 	/* create tag for Rx buffers. */
79157808251SPyun YongHyeon 	error = bus_dma_tag_create(sc->sc_cdata.stge_parent_tag,/* parent */
79257808251SPyun YongHyeon 		    1, 0,			/* algnmnt, boundary */
79357808251SPyun YongHyeon 		    BUS_SPACE_MAXADDR,		/* lowaddr */
79457808251SPyun YongHyeon 		    BUS_SPACE_MAXADDR,		/* highaddr */
79557808251SPyun YongHyeon 		    NULL, NULL,			/* filter, filterarg */
79657808251SPyun YongHyeon 		    MCLBYTES,			/* maxsize */
79757808251SPyun YongHyeon 		    1,				/* nsegments */
79857808251SPyun YongHyeon 		    MCLBYTES,			/* maxsegsize */
79957808251SPyun YongHyeon 		    0,				/* flags */
80057808251SPyun YongHyeon 		    NULL, NULL,			/* lockfunc, lockarg */
80157808251SPyun YongHyeon 		    &sc->sc_cdata.stge_rx_tag);
80257808251SPyun YongHyeon 	if (error != 0) {
80357808251SPyun YongHyeon 		device_printf(sc->sc_dev, "failed to allocate Rx DMA tag\n");
80457808251SPyun YongHyeon 		goto fail;
80557808251SPyun YongHyeon 	}
80657808251SPyun YongHyeon 
80757808251SPyun YongHyeon 	/* allocate DMA'able memory and load the DMA map for Tx ring. */
80857808251SPyun YongHyeon 	error = bus_dmamem_alloc(sc->sc_cdata.stge_tx_ring_tag,
809a1dd7a55SMarius Strobl 	    (void **)&sc->sc_rdata.stge_tx_ring, BUS_DMA_NOWAIT |
810a1dd7a55SMarius Strobl 	    BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->sc_cdata.stge_tx_ring_map);
81157808251SPyun YongHyeon 	if (error != 0) {
81257808251SPyun YongHyeon 		device_printf(sc->sc_dev,
81357808251SPyun YongHyeon 		    "failed to allocate DMA'able memory for Tx ring\n");
81457808251SPyun YongHyeon 		goto fail;
81557808251SPyun YongHyeon 	}
81657808251SPyun YongHyeon 
81757808251SPyun YongHyeon 	ctx.stge_busaddr = 0;
81857808251SPyun YongHyeon 	error = bus_dmamap_load(sc->sc_cdata.stge_tx_ring_tag,
81957808251SPyun YongHyeon 	    sc->sc_cdata.stge_tx_ring_map, sc->sc_rdata.stge_tx_ring,
82057808251SPyun YongHyeon 	    STGE_TX_RING_SZ, stge_dmamap_cb, &ctx, BUS_DMA_NOWAIT);
82157808251SPyun YongHyeon 	if (error != 0 || ctx.stge_busaddr == 0) {
82257808251SPyun YongHyeon 		device_printf(sc->sc_dev,
82357808251SPyun YongHyeon 		    "failed to load DMA'able memory for Tx ring\n");
82457808251SPyun YongHyeon 		goto fail;
82557808251SPyun YongHyeon 	}
82657808251SPyun YongHyeon 	sc->sc_rdata.stge_tx_ring_paddr = ctx.stge_busaddr;
82757808251SPyun YongHyeon 
82857808251SPyun YongHyeon 	/* allocate DMA'able memory and load the DMA map for Rx ring. */
82957808251SPyun YongHyeon 	error = bus_dmamem_alloc(sc->sc_cdata.stge_rx_ring_tag,
830a1dd7a55SMarius Strobl 	    (void **)&sc->sc_rdata.stge_rx_ring, BUS_DMA_NOWAIT |
831a1dd7a55SMarius Strobl 	    BUS_DMA_COHERENT | BUS_DMA_ZERO, &sc->sc_cdata.stge_rx_ring_map);
83257808251SPyun YongHyeon 	if (error != 0) {
83357808251SPyun YongHyeon 		device_printf(sc->sc_dev,
83457808251SPyun YongHyeon 		    "failed to allocate DMA'able memory for Rx ring\n");
83557808251SPyun YongHyeon 		goto fail;
83657808251SPyun YongHyeon 	}
83757808251SPyun YongHyeon 
83857808251SPyun YongHyeon 	ctx.stge_busaddr = 0;
83957808251SPyun YongHyeon 	error = bus_dmamap_load(sc->sc_cdata.stge_rx_ring_tag,
84057808251SPyun YongHyeon 	    sc->sc_cdata.stge_rx_ring_map, sc->sc_rdata.stge_rx_ring,
84157808251SPyun YongHyeon 	    STGE_RX_RING_SZ, stge_dmamap_cb, &ctx, BUS_DMA_NOWAIT);
84257808251SPyun YongHyeon 	if (error != 0 || ctx.stge_busaddr == 0) {
84357808251SPyun YongHyeon 		device_printf(sc->sc_dev,
84457808251SPyun YongHyeon 		    "failed to load DMA'able memory for Rx ring\n");
84557808251SPyun YongHyeon 		goto fail;
84657808251SPyun YongHyeon 	}
84757808251SPyun YongHyeon 	sc->sc_rdata.stge_rx_ring_paddr = ctx.stge_busaddr;
84857808251SPyun YongHyeon 
84957808251SPyun YongHyeon 	/* create DMA maps for Tx buffers. */
85057808251SPyun YongHyeon 	for (i = 0; i < STGE_TX_RING_CNT; i++) {
85157808251SPyun YongHyeon 		txd = &sc->sc_cdata.stge_txdesc[i];
85257808251SPyun YongHyeon 		txd->tx_m = NULL;
85357808251SPyun YongHyeon 		txd->tx_dmamap = 0;
85457808251SPyun YongHyeon 		error = bus_dmamap_create(sc->sc_cdata.stge_tx_tag, 0,
85557808251SPyun YongHyeon 		    &txd->tx_dmamap);
85657808251SPyun YongHyeon 		if (error != 0) {
85757808251SPyun YongHyeon 			device_printf(sc->sc_dev,
85857808251SPyun YongHyeon 			    "failed to create Tx dmamap\n");
85957808251SPyun YongHyeon 			goto fail;
86057808251SPyun YongHyeon 		}
86157808251SPyun YongHyeon 	}
86257808251SPyun YongHyeon 	/* create DMA maps for Rx buffers. */
86357808251SPyun YongHyeon 	if ((error = bus_dmamap_create(sc->sc_cdata.stge_rx_tag, 0,
86457808251SPyun YongHyeon 	    &sc->sc_cdata.stge_rx_sparemap)) != 0) {
86557808251SPyun YongHyeon 		device_printf(sc->sc_dev, "failed to create spare Rx dmamap\n");
86657808251SPyun YongHyeon 		goto fail;
86757808251SPyun YongHyeon 	}
86857808251SPyun YongHyeon 	for (i = 0; i < STGE_RX_RING_CNT; i++) {
86957808251SPyun YongHyeon 		rxd = &sc->sc_cdata.stge_rxdesc[i];
87057808251SPyun YongHyeon 		rxd->rx_m = NULL;
87157808251SPyun YongHyeon 		rxd->rx_dmamap = 0;
87257808251SPyun YongHyeon 		error = bus_dmamap_create(sc->sc_cdata.stge_rx_tag, 0,
87357808251SPyun YongHyeon 		    &rxd->rx_dmamap);
87457808251SPyun YongHyeon 		if (error != 0) {
87557808251SPyun YongHyeon 			device_printf(sc->sc_dev,
87657808251SPyun YongHyeon 			    "failed to create Rx dmamap\n");
87757808251SPyun YongHyeon 			goto fail;
87857808251SPyun YongHyeon 		}
87957808251SPyun YongHyeon 	}
88057808251SPyun YongHyeon 
88157808251SPyun YongHyeon fail:
88257808251SPyun YongHyeon 	return (error);
88357808251SPyun YongHyeon }
88457808251SPyun YongHyeon 
88557808251SPyun YongHyeon static void
stge_dma_free(struct stge_softc * sc)88657808251SPyun YongHyeon stge_dma_free(struct stge_softc *sc)
88757808251SPyun YongHyeon {
88857808251SPyun YongHyeon 	struct stge_txdesc *txd;
88957808251SPyun YongHyeon 	struct stge_rxdesc *rxd;
89057808251SPyun YongHyeon 	int i;
89157808251SPyun YongHyeon 
89257808251SPyun YongHyeon 	/* Tx ring */
89357808251SPyun YongHyeon 	if (sc->sc_cdata.stge_tx_ring_tag) {
894068d8643SJohn Baldwin 		if (sc->sc_rdata.stge_tx_ring_paddr)
89557808251SPyun YongHyeon 			bus_dmamap_unload(sc->sc_cdata.stge_tx_ring_tag,
89657808251SPyun YongHyeon 			    sc->sc_cdata.stge_tx_ring_map);
897068d8643SJohn Baldwin 		if (sc->sc_rdata.stge_tx_ring)
89857808251SPyun YongHyeon 			bus_dmamem_free(sc->sc_cdata.stge_tx_ring_tag,
89957808251SPyun YongHyeon 			    sc->sc_rdata.stge_tx_ring,
90057808251SPyun YongHyeon 			    sc->sc_cdata.stge_tx_ring_map);
90157808251SPyun YongHyeon 		sc->sc_rdata.stge_tx_ring = NULL;
902068d8643SJohn Baldwin 		sc->sc_rdata.stge_tx_ring_paddr = 0;
90357808251SPyun YongHyeon 		bus_dma_tag_destroy(sc->sc_cdata.stge_tx_ring_tag);
90457808251SPyun YongHyeon 		sc->sc_cdata.stge_tx_ring_tag = NULL;
90557808251SPyun YongHyeon 	}
90657808251SPyun YongHyeon 	/* Rx ring */
90757808251SPyun YongHyeon 	if (sc->sc_cdata.stge_rx_ring_tag) {
908068d8643SJohn Baldwin 		if (sc->sc_rdata.stge_rx_ring_paddr)
90957808251SPyun YongHyeon 			bus_dmamap_unload(sc->sc_cdata.stge_rx_ring_tag,
91057808251SPyun YongHyeon 			    sc->sc_cdata.stge_rx_ring_map);
911068d8643SJohn Baldwin 		if (sc->sc_rdata.stge_rx_ring)
91257808251SPyun YongHyeon 			bus_dmamem_free(sc->sc_cdata.stge_rx_ring_tag,
91357808251SPyun YongHyeon 			    sc->sc_rdata.stge_rx_ring,
91457808251SPyun YongHyeon 			    sc->sc_cdata.stge_rx_ring_map);
91557808251SPyun YongHyeon 		sc->sc_rdata.stge_rx_ring = NULL;
916068d8643SJohn Baldwin 		sc->sc_rdata.stge_rx_ring_paddr = 0;
91757808251SPyun YongHyeon 		bus_dma_tag_destroy(sc->sc_cdata.stge_rx_ring_tag);
91857808251SPyun YongHyeon 		sc->sc_cdata.stge_rx_ring_tag = NULL;
91957808251SPyun YongHyeon 	}
92057808251SPyun YongHyeon 	/* Tx buffers */
92157808251SPyun YongHyeon 	if (sc->sc_cdata.stge_tx_tag) {
92257808251SPyun YongHyeon 		for (i = 0; i < STGE_TX_RING_CNT; i++) {
92357808251SPyun YongHyeon 			txd = &sc->sc_cdata.stge_txdesc[i];
92457808251SPyun YongHyeon 			if (txd->tx_dmamap) {
92557808251SPyun YongHyeon 				bus_dmamap_destroy(sc->sc_cdata.stge_tx_tag,
92657808251SPyun YongHyeon 				    txd->tx_dmamap);
92757808251SPyun YongHyeon 				txd->tx_dmamap = 0;
92857808251SPyun YongHyeon 			}
92957808251SPyun YongHyeon 		}
93057808251SPyun YongHyeon 		bus_dma_tag_destroy(sc->sc_cdata.stge_tx_tag);
93157808251SPyun YongHyeon 		sc->sc_cdata.stge_tx_tag = NULL;
93257808251SPyun YongHyeon 	}
93357808251SPyun YongHyeon 	/* Rx buffers */
93457808251SPyun YongHyeon 	if (sc->sc_cdata.stge_rx_tag) {
93557808251SPyun YongHyeon 		for (i = 0; i < STGE_RX_RING_CNT; i++) {
93657808251SPyun YongHyeon 			rxd = &sc->sc_cdata.stge_rxdesc[i];
93757808251SPyun YongHyeon 			if (rxd->rx_dmamap) {
93857808251SPyun YongHyeon 				bus_dmamap_destroy(sc->sc_cdata.stge_rx_tag,
93957808251SPyun YongHyeon 				    rxd->rx_dmamap);
94057808251SPyun YongHyeon 				rxd->rx_dmamap = 0;
94157808251SPyun YongHyeon 			}
94257808251SPyun YongHyeon 		}
94357808251SPyun YongHyeon 		if (sc->sc_cdata.stge_rx_sparemap) {
94457808251SPyun YongHyeon 			bus_dmamap_destroy(sc->sc_cdata.stge_rx_tag,
94557808251SPyun YongHyeon 			    sc->sc_cdata.stge_rx_sparemap);
94657808251SPyun YongHyeon 			sc->sc_cdata.stge_rx_sparemap = 0;
94757808251SPyun YongHyeon 		}
94857808251SPyun YongHyeon 		bus_dma_tag_destroy(sc->sc_cdata.stge_rx_tag);
94957808251SPyun YongHyeon 		sc->sc_cdata.stge_rx_tag = NULL;
95057808251SPyun YongHyeon 	}
95157808251SPyun YongHyeon 
95257808251SPyun YongHyeon 	if (sc->sc_cdata.stge_parent_tag) {
95357808251SPyun YongHyeon 		bus_dma_tag_destroy(sc->sc_cdata.stge_parent_tag);
95457808251SPyun YongHyeon 		sc->sc_cdata.stge_parent_tag = NULL;
95557808251SPyun YongHyeon 	}
95657808251SPyun YongHyeon }
95757808251SPyun YongHyeon 
95857808251SPyun YongHyeon /*
95957808251SPyun YongHyeon  * stge_shutdown:
96057808251SPyun YongHyeon  *
96157808251SPyun YongHyeon  *	Make sure the interface is stopped at reboot time.
96257808251SPyun YongHyeon  */
9636a087a87SPyun YongHyeon static int
stge_shutdown(device_t dev)96457808251SPyun YongHyeon stge_shutdown(device_t dev)
96557808251SPyun YongHyeon {
96657808251SPyun YongHyeon 
967346de09fSPyun YongHyeon 	return (stge_suspend(dev));
968346de09fSPyun YongHyeon }
96957808251SPyun YongHyeon 
970346de09fSPyun YongHyeon static void
stge_setwol(struct stge_softc * sc)971346de09fSPyun YongHyeon stge_setwol(struct stge_softc *sc)
972346de09fSPyun YongHyeon {
97308ca347fSJustin Hibbits 	if_t ifp;
974346de09fSPyun YongHyeon 	uint8_t v;
9756a087a87SPyun YongHyeon 
976346de09fSPyun YongHyeon 	STGE_LOCK_ASSERT(sc);
977346de09fSPyun YongHyeon 
978346de09fSPyun YongHyeon 	ifp = sc->sc_ifp;
979346de09fSPyun YongHyeon 	v = CSR_READ_1(sc, STGE_WakeEvent);
980346de09fSPyun YongHyeon 	/* Disable all WOL bits. */
981346de09fSPyun YongHyeon 	v &= ~(WE_WakePktEnable | WE_MagicPktEnable | WE_LinkEventEnable |
982346de09fSPyun YongHyeon 	    WE_WakeOnLanEnable);
98308ca347fSJustin Hibbits 	if ((if_getcapenable(ifp) & IFCAP_WOL_MAGIC) != 0)
984346de09fSPyun YongHyeon 		v |= WE_MagicPktEnable | WE_WakeOnLanEnable;
985346de09fSPyun YongHyeon 	CSR_WRITE_1(sc, STGE_WakeEvent, v);
986346de09fSPyun YongHyeon 	/* Reset Tx and prevent transmission. */
987346de09fSPyun YongHyeon 	CSR_WRITE_4(sc, STGE_AsicCtrl,
988346de09fSPyun YongHyeon 	    CSR_READ_4(sc, STGE_AsicCtrl) | AC_TxReset);
989346de09fSPyun YongHyeon 	/*
990346de09fSPyun YongHyeon 	 * TC9021 automatically reset link speed to 100Mbps when it's put
991346de09fSPyun YongHyeon 	 * into sleep so there is no need to try to resetting link speed.
992346de09fSPyun YongHyeon 	 */
99357808251SPyun YongHyeon }
99457808251SPyun YongHyeon 
99557808251SPyun YongHyeon static int
stge_suspend(device_t dev)99657808251SPyun YongHyeon stge_suspend(device_t dev)
99757808251SPyun YongHyeon {
99857808251SPyun YongHyeon 	struct stge_softc *sc;
99957808251SPyun YongHyeon 
100057808251SPyun YongHyeon 	sc = device_get_softc(dev);
100157808251SPyun YongHyeon 
100257808251SPyun YongHyeon 	STGE_LOCK(sc);
100357808251SPyun YongHyeon 	stge_stop(sc);
100457808251SPyun YongHyeon 	sc->sc_suspended = 1;
1005346de09fSPyun YongHyeon 	stge_setwol(sc);
100657808251SPyun YongHyeon 	STGE_UNLOCK(sc);
100757808251SPyun YongHyeon 
100857808251SPyun YongHyeon 	return (0);
100957808251SPyun YongHyeon }
101057808251SPyun YongHyeon 
101157808251SPyun YongHyeon static int
stge_resume(device_t dev)101257808251SPyun YongHyeon stge_resume(device_t dev)
101357808251SPyun YongHyeon {
101457808251SPyun YongHyeon 	struct stge_softc *sc;
101508ca347fSJustin Hibbits 	if_t ifp;
1016346de09fSPyun YongHyeon 	uint8_t v;
101757808251SPyun YongHyeon 
101857808251SPyun YongHyeon 	sc = device_get_softc(dev);
101957808251SPyun YongHyeon 
102057808251SPyun YongHyeon 	STGE_LOCK(sc);
1021346de09fSPyun YongHyeon 	/*
1022346de09fSPyun YongHyeon 	 * Clear WOL bits, so special frames wouldn't interfere
1023346de09fSPyun YongHyeon 	 * normal Rx operation anymore.
1024346de09fSPyun YongHyeon 	 */
1025346de09fSPyun YongHyeon 	v = CSR_READ_1(sc, STGE_WakeEvent);
1026346de09fSPyun YongHyeon 	v &= ~(WE_WakePktEnable | WE_MagicPktEnable | WE_LinkEventEnable |
1027346de09fSPyun YongHyeon 	    WE_WakeOnLanEnable);
1028346de09fSPyun YongHyeon 	CSR_WRITE_1(sc, STGE_WakeEvent, v);
102957808251SPyun YongHyeon 	ifp = sc->sc_ifp;
103008ca347fSJustin Hibbits 	if (if_getflags(ifp) & IFF_UP)
103157808251SPyun YongHyeon 		stge_init_locked(sc);
103257808251SPyun YongHyeon 
103357808251SPyun YongHyeon 	sc->sc_suspended = 0;
103457808251SPyun YongHyeon 	STGE_UNLOCK(sc);
103557808251SPyun YongHyeon 
103657808251SPyun YongHyeon 	return (0);
103757808251SPyun YongHyeon }
103857808251SPyun YongHyeon 
103957808251SPyun YongHyeon static void
stge_dma_wait(struct stge_softc * sc)104057808251SPyun YongHyeon stge_dma_wait(struct stge_softc *sc)
104157808251SPyun YongHyeon {
104257808251SPyun YongHyeon 	int i;
104357808251SPyun YongHyeon 
104457808251SPyun YongHyeon 	for (i = 0; i < STGE_TIMEOUT; i++) {
104557808251SPyun YongHyeon 		DELAY(2);
104657808251SPyun YongHyeon 		if ((CSR_READ_4(sc, STGE_DMACtrl) & DMAC_TxDMAInProg) == 0)
104757808251SPyun YongHyeon 			break;
104857808251SPyun YongHyeon 	}
104957808251SPyun YongHyeon 
105057808251SPyun YongHyeon 	if (i == STGE_TIMEOUT)
105157808251SPyun YongHyeon 		device_printf(sc->sc_dev, "DMA wait timed out\n");
105257808251SPyun YongHyeon }
105357808251SPyun YongHyeon 
105457808251SPyun YongHyeon static int
stge_encap(struct stge_softc * sc,struct mbuf ** m_head)105557808251SPyun YongHyeon stge_encap(struct stge_softc *sc, struct mbuf **m_head)
105657808251SPyun YongHyeon {
105757808251SPyun YongHyeon 	struct stge_txdesc *txd;
105857808251SPyun YongHyeon 	struct stge_tfd *tfd;
1059ff9c95a4SPyun YongHyeon 	struct mbuf *m;
106057808251SPyun YongHyeon 	bus_dma_segment_t txsegs[STGE_MAXTXSEGS];
106157808251SPyun YongHyeon 	int error, i, nsegs, si;
106257808251SPyun YongHyeon 	uint64_t csum_flags, tfc;
106357808251SPyun YongHyeon 
106457808251SPyun YongHyeon 	STGE_LOCK_ASSERT(sc);
106557808251SPyun YongHyeon 
106657808251SPyun YongHyeon 	if ((txd = STAILQ_FIRST(&sc->sc_cdata.stge_txfreeq)) == NULL)
106757808251SPyun YongHyeon 		return (ENOBUFS);
106857808251SPyun YongHyeon 
106957808251SPyun YongHyeon 	error =  bus_dmamap_load_mbuf_sg(sc->sc_cdata.stge_tx_tag,
1070ff9c95a4SPyun YongHyeon 	    txd->tx_dmamap, *m_head, txsegs, &nsegs, 0);
107157808251SPyun YongHyeon 	if (error == EFBIG) {
1072c6499eccSGleb Smirnoff 		m = m_collapse(*m_head, M_NOWAIT, STGE_MAXTXSEGS);
1073ff9c95a4SPyun YongHyeon 		if (m == NULL) {
1074ff9c95a4SPyun YongHyeon 			m_freem(*m_head);
1075ff9c95a4SPyun YongHyeon 			*m_head = NULL;
107657808251SPyun YongHyeon 			return (ENOMEM);
107757808251SPyun YongHyeon 		}
1078ff9c95a4SPyun YongHyeon 		*m_head = m;
107957808251SPyun YongHyeon 		error = bus_dmamap_load_mbuf_sg(sc->sc_cdata.stge_tx_tag,
1080ff9c95a4SPyun YongHyeon 		    txd->tx_dmamap, *m_head, txsegs, &nsegs, 0);
108157808251SPyun YongHyeon 		if (error != 0) {
1082ff9c95a4SPyun YongHyeon 			m_freem(*m_head);
1083ff9c95a4SPyun YongHyeon 			*m_head = NULL;
108457808251SPyun YongHyeon 			return (error);
108557808251SPyun YongHyeon 		}
108657808251SPyun YongHyeon 	} else if (error != 0)
108757808251SPyun YongHyeon 		return (error);
108857808251SPyun YongHyeon 	if (nsegs == 0) {
1089ff9c95a4SPyun YongHyeon 		m_freem(*m_head);
1090ff9c95a4SPyun YongHyeon 		*m_head = NULL;
109157808251SPyun YongHyeon 		return (EIO);
109257808251SPyun YongHyeon 	}
109357808251SPyun YongHyeon 
1094ff9c95a4SPyun YongHyeon 	m = *m_head;
109557808251SPyun YongHyeon 	csum_flags = 0;
109657808251SPyun YongHyeon 	if ((m->m_pkthdr.csum_flags & STGE_CSUM_FEATURES) != 0) {
109757808251SPyun YongHyeon 		if (m->m_pkthdr.csum_flags & CSUM_IP)
109857808251SPyun YongHyeon 			csum_flags |= TFD_IPChecksumEnable;
109957808251SPyun YongHyeon 		if (m->m_pkthdr.csum_flags & CSUM_TCP)
110057808251SPyun YongHyeon 			csum_flags |= TFD_TCPChecksumEnable;
110157808251SPyun YongHyeon 		else if (m->m_pkthdr.csum_flags & CSUM_UDP)
110257808251SPyun YongHyeon 			csum_flags |= TFD_UDPChecksumEnable;
110357808251SPyun YongHyeon 	}
110457808251SPyun YongHyeon 
110557808251SPyun YongHyeon 	si = sc->sc_cdata.stge_tx_prod;
110657808251SPyun YongHyeon 	tfd = &sc->sc_rdata.stge_tx_ring[si];
110757808251SPyun YongHyeon 	for (i = 0; i < nsegs; i++)
110857808251SPyun YongHyeon 		tfd->tfd_frags[i].frag_word0 =
110957808251SPyun YongHyeon 		    htole64(FRAG_ADDR(txsegs[i].ds_addr) |
111057808251SPyun YongHyeon 		    FRAG_LEN(txsegs[i].ds_len));
111157808251SPyun YongHyeon 	sc->sc_cdata.stge_tx_cnt++;
111257808251SPyun YongHyeon 
111357808251SPyun YongHyeon 	tfc = TFD_FrameId(si) | TFD_WordAlign(TFD_WordAlign_disable) |
111457808251SPyun YongHyeon 	    TFD_FragCount(nsegs) | csum_flags;
111557808251SPyun YongHyeon 	if (sc->sc_cdata.stge_tx_cnt >= STGE_TX_HIWAT)
111657808251SPyun YongHyeon 		tfc |= TFD_TxDMAIndicate;
111757808251SPyun YongHyeon 
111857808251SPyun YongHyeon 	/* Update producer index. */
111957808251SPyun YongHyeon 	sc->sc_cdata.stge_tx_prod = (si + 1) % STGE_TX_RING_CNT;
112057808251SPyun YongHyeon 
112157808251SPyun YongHyeon 	/* Check if we have a VLAN tag to insert. */
112278ba57b9SAndre Oppermann 	if (m->m_flags & M_VLANTAG)
112378ba57b9SAndre Oppermann 		tfc |= (TFD_VLANTagInsert | TFD_VID(m->m_pkthdr.ether_vtag));
112457808251SPyun YongHyeon 	tfd->tfd_control = htole64(tfc);
112557808251SPyun YongHyeon 
112657808251SPyun YongHyeon 	/* Update Tx Queue. */
112757808251SPyun YongHyeon 	STAILQ_REMOVE_HEAD(&sc->sc_cdata.stge_txfreeq, tx_q);
112857808251SPyun YongHyeon 	STAILQ_INSERT_TAIL(&sc->sc_cdata.stge_txbusyq, txd, tx_q);
112957808251SPyun YongHyeon 	txd->tx_m = m;
113057808251SPyun YongHyeon 
113157808251SPyun YongHyeon 	/* Sync descriptors. */
113257808251SPyun YongHyeon 	bus_dmamap_sync(sc->sc_cdata.stge_tx_tag, txd->tx_dmamap,
113357808251SPyun YongHyeon 	    BUS_DMASYNC_PREWRITE);
113457808251SPyun YongHyeon 	bus_dmamap_sync(sc->sc_cdata.stge_tx_ring_tag,
113557808251SPyun YongHyeon 	    sc->sc_cdata.stge_tx_ring_map,
113657808251SPyun YongHyeon 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
113757808251SPyun YongHyeon 
113857808251SPyun YongHyeon 	return (0);
113957808251SPyun YongHyeon }
114057808251SPyun YongHyeon 
114157808251SPyun YongHyeon /*
114257808251SPyun YongHyeon  * stge_start:		[ifnet interface function]
114357808251SPyun YongHyeon  *
114457808251SPyun YongHyeon  *	Start packet transmission on the interface.
114557808251SPyun YongHyeon  */
114657808251SPyun YongHyeon static void
stge_start(if_t ifp)114708ca347fSJustin Hibbits stge_start(if_t ifp)
114857808251SPyun YongHyeon {
114957808251SPyun YongHyeon 	struct stge_softc *sc;
115057808251SPyun YongHyeon 
115108ca347fSJustin Hibbits 	sc = if_getsoftc(ifp);
115257808251SPyun YongHyeon 	STGE_LOCK(sc);
115357808251SPyun YongHyeon 	stge_start_locked(ifp);
115457808251SPyun YongHyeon 	STGE_UNLOCK(sc);
115557808251SPyun YongHyeon }
115657808251SPyun YongHyeon 
115757808251SPyun YongHyeon static void
stge_start_locked(if_t ifp)115808ca347fSJustin Hibbits stge_start_locked(if_t ifp)
115957808251SPyun YongHyeon {
116057808251SPyun YongHyeon         struct stge_softc *sc;
116157808251SPyun YongHyeon         struct mbuf *m_head;
116257808251SPyun YongHyeon 	int enq;
116357808251SPyun YongHyeon 
116408ca347fSJustin Hibbits 	sc = if_getsoftc(ifp);
116557808251SPyun YongHyeon 
116657808251SPyun YongHyeon 	STGE_LOCK_ASSERT(sc);
116757808251SPyun YongHyeon 
116808ca347fSJustin Hibbits 	if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
11697ef4ec5dSPyun YongHyeon 	    IFF_DRV_RUNNING || sc->sc_link == 0)
117057808251SPyun YongHyeon 		return;
117157808251SPyun YongHyeon 
117208ca347fSJustin Hibbits 	for (enq = 0; !if_sendq_empty(ifp); ) {
117357808251SPyun YongHyeon 		if (sc->sc_cdata.stge_tx_cnt >= STGE_TX_HIWAT) {
117408ca347fSJustin Hibbits 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
117557808251SPyun YongHyeon 			break;
117657808251SPyun YongHyeon 		}
117757808251SPyun YongHyeon 
117808ca347fSJustin Hibbits 		m_head = if_dequeue(ifp);
117957808251SPyun YongHyeon 		if (m_head == NULL)
118057808251SPyun YongHyeon 			break;
118157808251SPyun YongHyeon 		/*
118257808251SPyun YongHyeon 		 * Pack the data into the transmit ring. If we
118357808251SPyun YongHyeon 		 * don't have room, set the OACTIVE flag and wait
118457808251SPyun YongHyeon 		 * for the NIC to drain the ring.
118557808251SPyun YongHyeon 		 */
118657808251SPyun YongHyeon 		if (stge_encap(sc, &m_head)) {
118757808251SPyun YongHyeon 			if (m_head == NULL)
118857808251SPyun YongHyeon 				break;
118908ca347fSJustin Hibbits 			if_sendq_prepend(ifp, m_head);
119008ca347fSJustin Hibbits 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
119157808251SPyun YongHyeon 			break;
119257808251SPyun YongHyeon 		}
119357808251SPyun YongHyeon 
119457808251SPyun YongHyeon 		enq++;
119557808251SPyun YongHyeon 		/*
119657808251SPyun YongHyeon 		 * If there's a BPF listener, bounce a copy of this frame
119757808251SPyun YongHyeon 		 * to him.
119857808251SPyun YongHyeon 		 */
119959a0d28bSChristian S.J. Peron 		ETHER_BPF_MTAP(ifp, m_head);
120057808251SPyun YongHyeon 	}
120157808251SPyun YongHyeon 
120257808251SPyun YongHyeon 	if (enq > 0) {
120357808251SPyun YongHyeon 		/* Transmit */
120457808251SPyun YongHyeon 		CSR_WRITE_4(sc, STGE_DMACtrl, DMAC_TxDMAPollNow);
120557808251SPyun YongHyeon 
120657808251SPyun YongHyeon 		/* Set a timeout in case the chip goes out to lunch. */
1207eb7a67daSPyun YongHyeon 		sc->sc_watchdog_timer = 5;
120857808251SPyun YongHyeon 	}
120957808251SPyun YongHyeon }
121057808251SPyun YongHyeon 
121157808251SPyun YongHyeon /*
1212eb7a67daSPyun YongHyeon  * stge_watchdog:
121357808251SPyun YongHyeon  *
121457808251SPyun YongHyeon  *	Watchdog timer handler.
121557808251SPyun YongHyeon  */
121657808251SPyun YongHyeon static void
stge_watchdog(struct stge_softc * sc)1217eb7a67daSPyun YongHyeon stge_watchdog(struct stge_softc *sc)
121857808251SPyun YongHyeon {
121908ca347fSJustin Hibbits 	if_t ifp;
122057808251SPyun YongHyeon 
1221eb7a67daSPyun YongHyeon 	STGE_LOCK_ASSERT(sc);
122257808251SPyun YongHyeon 
1223eb7a67daSPyun YongHyeon 	if (sc->sc_watchdog_timer == 0 || --sc->sc_watchdog_timer)
1224eb7a67daSPyun YongHyeon 		return;
1225eb7a67daSPyun YongHyeon 
1226eb7a67daSPyun YongHyeon 	ifp = sc->sc_ifp;
122757808251SPyun YongHyeon 	if_printf(sc->sc_ifp, "device timeout\n");
1228d5002dd1SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
122908ca347fSJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
123057808251SPyun YongHyeon 	stge_init_locked(sc);
123108ca347fSJustin Hibbits 	if (!if_sendq_empty(ifp))
1232787b3adeSPyun YongHyeon 		stge_start_locked(ifp);
123357808251SPyun YongHyeon }
123457808251SPyun YongHyeon 
123557808251SPyun YongHyeon /*
123657808251SPyun YongHyeon  * stge_ioctl:		[ifnet interface function]
123757808251SPyun YongHyeon  *
123857808251SPyun YongHyeon  *	Handle control requests from the operator.
123957808251SPyun YongHyeon  */
124057808251SPyun YongHyeon static int
stge_ioctl(if_t ifp,u_long cmd,caddr_t data)124108ca347fSJustin Hibbits stge_ioctl(if_t ifp, u_long cmd, caddr_t data)
124257808251SPyun YongHyeon {
124357808251SPyun YongHyeon 	struct stge_softc *sc;
124457808251SPyun YongHyeon 	struct ifreq *ifr;
124557808251SPyun YongHyeon 	struct mii_data *mii;
124657808251SPyun YongHyeon 	int error, mask;
124757808251SPyun YongHyeon 
124808ca347fSJustin Hibbits 	sc = if_getsoftc(ifp);
124957808251SPyun YongHyeon 	ifr = (struct ifreq *)data;
125057808251SPyun YongHyeon 	error = 0;
125157808251SPyun YongHyeon 	switch (cmd) {
125257808251SPyun YongHyeon 	case SIOCSIFMTU:
125357808251SPyun YongHyeon 		if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > STGE_JUMBO_MTU)
125457808251SPyun YongHyeon 			error = EINVAL;
125508ca347fSJustin Hibbits 		else if (if_getmtu(ifp) != ifr->ifr_mtu) {
125608ca347fSJustin Hibbits 			if_setmtu(ifp, ifr->ifr_mtu);
125757808251SPyun YongHyeon 			STGE_LOCK(sc);
125808ca347fSJustin Hibbits 			if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) {
125908ca347fSJustin Hibbits 				if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
126057808251SPyun YongHyeon 				stge_init_locked(sc);
1261fc05868aSPyun YongHyeon 			}
126257808251SPyun YongHyeon 			STGE_UNLOCK(sc);
126357808251SPyun YongHyeon 		}
126457808251SPyun YongHyeon 		break;
126557808251SPyun YongHyeon 	case SIOCSIFFLAGS:
126657808251SPyun YongHyeon 		STGE_LOCK(sc);
126708ca347fSJustin Hibbits 		if ((if_getflags(ifp) & IFF_UP) != 0) {
126808ca347fSJustin Hibbits 			if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) {
126908ca347fSJustin Hibbits 				if (((if_getflags(ifp) ^ sc->sc_if_flags)
127057808251SPyun YongHyeon 				    & IFF_PROMISC) != 0)
127157808251SPyun YongHyeon 					stge_set_filter(sc);
127257808251SPyun YongHyeon 			} else {
127357808251SPyun YongHyeon 				if (sc->sc_detach == 0)
127457808251SPyun YongHyeon 					stge_init_locked(sc);
127557808251SPyun YongHyeon 			}
127657808251SPyun YongHyeon 		} else {
127708ca347fSJustin Hibbits 			if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
127857808251SPyun YongHyeon 				stge_stop(sc);
127957808251SPyun YongHyeon 		}
128008ca347fSJustin Hibbits 		sc->sc_if_flags = if_getflags(ifp);
128157808251SPyun YongHyeon 		STGE_UNLOCK(sc);
128257808251SPyun YongHyeon 		break;
128357808251SPyun YongHyeon 	case SIOCADDMULTI:
128457808251SPyun YongHyeon 	case SIOCDELMULTI:
128557808251SPyun YongHyeon 		STGE_LOCK(sc);
128608ca347fSJustin Hibbits 		if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
128757808251SPyun YongHyeon 			stge_set_multi(sc);
128857808251SPyun YongHyeon 		STGE_UNLOCK(sc);
128957808251SPyun YongHyeon 		break;
129057808251SPyun YongHyeon 	case SIOCSIFMEDIA:
129157808251SPyun YongHyeon 	case SIOCGIFMEDIA:
129257808251SPyun YongHyeon 		mii = device_get_softc(sc->sc_miibus);
129357808251SPyun YongHyeon 		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
129457808251SPyun YongHyeon 		break;
129557808251SPyun YongHyeon 	case SIOCSIFCAP:
129608ca347fSJustin Hibbits 		mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
129757808251SPyun YongHyeon #ifdef DEVICE_POLLING
129857808251SPyun YongHyeon 		if ((mask & IFCAP_POLLING) != 0) {
129957808251SPyun YongHyeon 			if ((ifr->ifr_reqcap & IFCAP_POLLING) != 0) {
130057808251SPyun YongHyeon 				error = ether_poll_register(stge_poll, ifp);
130157808251SPyun YongHyeon 				if (error != 0)
130257808251SPyun YongHyeon 					break;
130357808251SPyun YongHyeon 				STGE_LOCK(sc);
130457808251SPyun YongHyeon 				CSR_WRITE_2(sc, STGE_IntEnable, 0);
130508ca347fSJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_POLLING, 0);
130657808251SPyun YongHyeon 				STGE_UNLOCK(sc);
130757808251SPyun YongHyeon 			} else {
130857808251SPyun YongHyeon 				error = ether_poll_deregister(ifp);
130957808251SPyun YongHyeon 				if (error != 0)
131057808251SPyun YongHyeon 					break;
131157808251SPyun YongHyeon 				STGE_LOCK(sc);
131257808251SPyun YongHyeon 				CSR_WRITE_2(sc, STGE_IntEnable,
131357808251SPyun YongHyeon 				    sc->sc_IntEnable);
131408ca347fSJustin Hibbits 				if_setcapenablebit(ifp, 0, IFCAP_POLLING);
131557808251SPyun YongHyeon 				STGE_UNLOCK(sc);
131657808251SPyun YongHyeon 			}
131757808251SPyun YongHyeon 		}
131857808251SPyun YongHyeon #endif
131957808251SPyun YongHyeon 		if ((mask & IFCAP_HWCSUM) != 0) {
132008ca347fSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_HWCSUM);
132108ca347fSJustin Hibbits 			if ((IFCAP_HWCSUM & if_getcapenable(ifp)) != 0 &&
132208ca347fSJustin Hibbits 			    (IFCAP_HWCSUM & if_getcapabilities(ifp)) != 0)
132308ca347fSJustin Hibbits 				if_sethwassist(ifp, STGE_CSUM_FEATURES);
132457808251SPyun YongHyeon 			else
132508ca347fSJustin Hibbits 				if_sethwassist(ifp, 0);
132657808251SPyun YongHyeon 		}
1327346de09fSPyun YongHyeon 		if ((mask & IFCAP_WOL) != 0 &&
132808ca347fSJustin Hibbits 		    (if_getcapabilities(ifp) & IFCAP_WOL) != 0) {
1329346de09fSPyun YongHyeon 			if ((mask & IFCAP_WOL_MAGIC) != 0)
133008ca347fSJustin Hibbits 				if_togglecapenable(ifp, IFCAP_WOL_MAGIC);
1331346de09fSPyun YongHyeon 		}
133257808251SPyun YongHyeon 		if ((mask & IFCAP_VLAN_HWTAGGING) != 0) {
133308ca347fSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_VLAN_HWTAGGING);
133408ca347fSJustin Hibbits 			if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) {
133557808251SPyun YongHyeon 				STGE_LOCK(sc);
133657808251SPyun YongHyeon 				stge_vlan_setup(sc);
133757808251SPyun YongHyeon 				STGE_UNLOCK(sc);
133857808251SPyun YongHyeon 			}
133957808251SPyun YongHyeon 		}
134057808251SPyun YongHyeon 		VLAN_CAPABILITIES(ifp);
134157808251SPyun YongHyeon 		break;
134257808251SPyun YongHyeon 	default:
134357808251SPyun YongHyeon 		error = ether_ioctl(ifp, cmd, data);
134457808251SPyun YongHyeon 		break;
134557808251SPyun YongHyeon 	}
134657808251SPyun YongHyeon 
134757808251SPyun YongHyeon 	return (error);
134857808251SPyun YongHyeon }
134957808251SPyun YongHyeon 
135057808251SPyun YongHyeon static void
stge_link_task(void * arg,int pending)135157808251SPyun YongHyeon stge_link_task(void *arg, int pending)
135257808251SPyun YongHyeon {
135357808251SPyun YongHyeon 	struct stge_softc *sc;
13547ef4ec5dSPyun YongHyeon 	struct mii_data *mii;
135557808251SPyun YongHyeon 	uint32_t v, ac;
135657808251SPyun YongHyeon 	int i;
135757808251SPyun YongHyeon 
135857808251SPyun YongHyeon 	sc = (struct stge_softc *)arg;
135957808251SPyun YongHyeon 	STGE_LOCK(sc);
13607ef4ec5dSPyun YongHyeon 
13617ef4ec5dSPyun YongHyeon 	mii = device_get_softc(sc->sc_miibus);
13627ef4ec5dSPyun YongHyeon 	if (mii->mii_media_status & IFM_ACTIVE) {
13637ef4ec5dSPyun YongHyeon 		if (IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE)
13647ef4ec5dSPyun YongHyeon 			sc->sc_link = 1;
13657ef4ec5dSPyun YongHyeon 	} else
13667ef4ec5dSPyun YongHyeon 		sc->sc_link = 0;
13677ef4ec5dSPyun YongHyeon 
13687ef4ec5dSPyun YongHyeon 	sc->sc_MACCtrl = 0;
13697ef4ec5dSPyun YongHyeon 	if (((mii->mii_media_active & IFM_GMASK) & IFM_FDX) != 0)
13707ef4ec5dSPyun YongHyeon 		sc->sc_MACCtrl |= MC_DuplexSelect;
1371efd4fc3fSMarius Strobl 	if (((mii->mii_media_active & IFM_GMASK) & IFM_ETH_RXPAUSE) != 0)
13727ef4ec5dSPyun YongHyeon 		sc->sc_MACCtrl |= MC_RxFlowControlEnable;
1373efd4fc3fSMarius Strobl 	if (((mii->mii_media_active & IFM_GMASK) & IFM_ETH_TXPAUSE) != 0)
13747ef4ec5dSPyun YongHyeon 		sc->sc_MACCtrl |= MC_TxFlowControlEnable;
137557808251SPyun YongHyeon 	/*
137657808251SPyun YongHyeon 	 * Update STGE_MACCtrl register depending on link status.
137757808251SPyun YongHyeon 	 * (duplex, flow control etc)
137857808251SPyun YongHyeon 	 */
137957808251SPyun YongHyeon 	v = ac = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK;
138057808251SPyun YongHyeon 	v &= ~(MC_DuplexSelect|MC_RxFlowControlEnable|MC_TxFlowControlEnable);
138157808251SPyun YongHyeon 	v |= sc->sc_MACCtrl;
138257808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_MACCtrl, v);
138357808251SPyun YongHyeon 	if (((ac ^ sc->sc_MACCtrl) & MC_DuplexSelect) != 0) {
138457808251SPyun YongHyeon 		/* Duplex setting changed, reset Tx/Rx functions. */
138557808251SPyun YongHyeon 		ac = CSR_READ_4(sc, STGE_AsicCtrl);
138657808251SPyun YongHyeon 		ac |= AC_TxReset | AC_RxReset;
138757808251SPyun YongHyeon 		CSR_WRITE_4(sc, STGE_AsicCtrl, ac);
138857808251SPyun YongHyeon 		for (i = 0; i < STGE_TIMEOUT; i++) {
138957808251SPyun YongHyeon 			DELAY(100);
139057808251SPyun YongHyeon 			if ((CSR_READ_4(sc, STGE_AsicCtrl) & AC_ResetBusy) == 0)
139157808251SPyun YongHyeon 				break;
139257808251SPyun YongHyeon 		}
139357808251SPyun YongHyeon 		if (i == STGE_TIMEOUT)
139457808251SPyun YongHyeon 			device_printf(sc->sc_dev, "reset failed to complete\n");
139557808251SPyun YongHyeon 	}
139657808251SPyun YongHyeon 	STGE_UNLOCK(sc);
139757808251SPyun YongHyeon }
139857808251SPyun YongHyeon 
139957808251SPyun YongHyeon static __inline int
stge_tx_error(struct stge_softc * sc)140057808251SPyun YongHyeon stge_tx_error(struct stge_softc *sc)
140157808251SPyun YongHyeon {
140257808251SPyun YongHyeon 	uint32_t txstat;
140357808251SPyun YongHyeon 	int error;
140457808251SPyun YongHyeon 
140557808251SPyun YongHyeon 	for (error = 0;;) {
140657808251SPyun YongHyeon 		txstat = CSR_READ_4(sc, STGE_TxStatus);
140757808251SPyun YongHyeon 		if ((txstat & TS_TxComplete) == 0)
140857808251SPyun YongHyeon 			break;
140957808251SPyun YongHyeon 		/* Tx underrun */
141057808251SPyun YongHyeon 		if ((txstat & TS_TxUnderrun) != 0) {
141157808251SPyun YongHyeon 			/*
141257808251SPyun YongHyeon 			 * XXX
141357808251SPyun YongHyeon 			 * There should be a more better way to recover
141457808251SPyun YongHyeon 			 * from Tx underrun instead of a full reset.
141557808251SPyun YongHyeon 			 */
141657808251SPyun YongHyeon 			if (sc->sc_nerr++ < STGE_MAXERR)
141757808251SPyun YongHyeon 				device_printf(sc->sc_dev, "Tx underrun, "
141857808251SPyun YongHyeon 				    "resetting...\n");
141957808251SPyun YongHyeon 			if (sc->sc_nerr == STGE_MAXERR)
142057808251SPyun YongHyeon 				device_printf(sc->sc_dev, "too many errors; "
142157808251SPyun YongHyeon 				    "not reporting any more\n");
142257808251SPyun YongHyeon 			error = -1;
142357808251SPyun YongHyeon 			break;
142457808251SPyun YongHyeon 		}
142557808251SPyun YongHyeon 		/* Maximum/Late collisions, Re-enable Tx MAC. */
142657808251SPyun YongHyeon 		if ((txstat & (TS_MaxCollisions|TS_LateCollision)) != 0)
142757808251SPyun YongHyeon 			CSR_WRITE_4(sc, STGE_MACCtrl,
142857808251SPyun YongHyeon 			    (CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK) |
142957808251SPyun YongHyeon 			    MC_TxEnable);
143057808251SPyun YongHyeon 	}
143157808251SPyun YongHyeon 
143257808251SPyun YongHyeon 	return (error);
143357808251SPyun YongHyeon }
143457808251SPyun YongHyeon 
143557808251SPyun YongHyeon /*
143657808251SPyun YongHyeon  * stge_intr:
143757808251SPyun YongHyeon  *
143857808251SPyun YongHyeon  *	Interrupt service routine.
143957808251SPyun YongHyeon  */
144057808251SPyun YongHyeon static void
stge_intr(void * arg)144157808251SPyun YongHyeon stge_intr(void *arg)
144257808251SPyun YongHyeon {
144357808251SPyun YongHyeon 	struct stge_softc *sc;
144408ca347fSJustin Hibbits 	if_t ifp;
144557808251SPyun YongHyeon 	int reinit;
144657808251SPyun YongHyeon 	uint16_t status;
144757808251SPyun YongHyeon 
144857808251SPyun YongHyeon 	sc = (struct stge_softc *)arg;
144957808251SPyun YongHyeon 	ifp = sc->sc_ifp;
145057808251SPyun YongHyeon 
145157808251SPyun YongHyeon 	STGE_LOCK(sc);
145257808251SPyun YongHyeon 
145357808251SPyun YongHyeon #ifdef DEVICE_POLLING
145408ca347fSJustin Hibbits 	if ((if_getcapenable(ifp) & IFCAP_POLLING) != 0)
145557808251SPyun YongHyeon 		goto done_locked;
145657808251SPyun YongHyeon #endif
145757808251SPyun YongHyeon 	status = CSR_READ_2(sc, STGE_IntStatus);
145857808251SPyun YongHyeon 	if (sc->sc_suspended || (status & IS_InterruptStatus) == 0)
145957808251SPyun YongHyeon 		goto done_locked;
146057808251SPyun YongHyeon 
146157808251SPyun YongHyeon 	/* Disable interrupts. */
146257808251SPyun YongHyeon 	for (reinit = 0;;) {
146357808251SPyun YongHyeon 		status = CSR_READ_2(sc, STGE_IntStatusAck);
146457808251SPyun YongHyeon 		status &= sc->sc_IntEnable;
146557808251SPyun YongHyeon 		if (status == 0)
146657808251SPyun YongHyeon 			break;
146757808251SPyun YongHyeon 		/* Host interface errors. */
146857808251SPyun YongHyeon 		if ((status & IS_HostError) != 0) {
146957808251SPyun YongHyeon 			device_printf(sc->sc_dev,
147057808251SPyun YongHyeon 			    "Host interface error, resetting...\n");
147157808251SPyun YongHyeon 			reinit = 1;
147257808251SPyun YongHyeon 			goto force_init;
147357808251SPyun YongHyeon 		}
147457808251SPyun YongHyeon 
147557808251SPyun YongHyeon 		/* Receive interrupts. */
147657808251SPyun YongHyeon 		if ((status & IS_RxDMAComplete) != 0) {
147757808251SPyun YongHyeon 			stge_rxeof(sc);
147857808251SPyun YongHyeon 			if ((status & IS_RFDListEnd) != 0)
147957808251SPyun YongHyeon 				CSR_WRITE_4(sc, STGE_DMACtrl,
148057808251SPyun YongHyeon 				    DMAC_RxDMAPollNow);
148157808251SPyun YongHyeon 		}
148257808251SPyun YongHyeon 
148357808251SPyun YongHyeon 		/* Transmit interrupts. */
148457808251SPyun YongHyeon 		if ((status & (IS_TxDMAComplete | IS_TxComplete)) != 0)
148557808251SPyun YongHyeon 			stge_txeof(sc);
148657808251SPyun YongHyeon 
148757808251SPyun YongHyeon 		/* Transmission errors.*/
148857808251SPyun YongHyeon 		if ((status & IS_TxComplete) != 0) {
148957808251SPyun YongHyeon 			if ((reinit = stge_tx_error(sc)) != 0)
149057808251SPyun YongHyeon 				break;
149157808251SPyun YongHyeon 		}
149257808251SPyun YongHyeon 	}
149357808251SPyun YongHyeon 
149457808251SPyun YongHyeon force_init:
1495fc05868aSPyun YongHyeon 	if (reinit != 0) {
149608ca347fSJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
149757808251SPyun YongHyeon 		stge_init_locked(sc);
1498fc05868aSPyun YongHyeon 	}
149957808251SPyun YongHyeon 
150057808251SPyun YongHyeon 	/* Re-enable interrupts. */
150157808251SPyun YongHyeon 	CSR_WRITE_2(sc, STGE_IntEnable, sc->sc_IntEnable);
150257808251SPyun YongHyeon 
150357808251SPyun YongHyeon 	/* Try to get more packets going. */
150408ca347fSJustin Hibbits 	if (!if_sendq_empty(ifp))
150557808251SPyun YongHyeon 		stge_start_locked(ifp);
150657808251SPyun YongHyeon 
150757808251SPyun YongHyeon done_locked:
150857808251SPyun YongHyeon 	STGE_UNLOCK(sc);
150957808251SPyun YongHyeon }
151057808251SPyun YongHyeon 
151157808251SPyun YongHyeon /*
151257808251SPyun YongHyeon  * stge_txeof:
151357808251SPyun YongHyeon  *
151457808251SPyun YongHyeon  *	Helper; handle transmit interrupts.
151557808251SPyun YongHyeon  */
151657808251SPyun YongHyeon static void
stge_txeof(struct stge_softc * sc)151757808251SPyun YongHyeon stge_txeof(struct stge_softc *sc)
151857808251SPyun YongHyeon {
151908ca347fSJustin Hibbits 	if_t ifp;
152057808251SPyun YongHyeon 	struct stge_txdesc *txd;
152157808251SPyun YongHyeon 	uint64_t control;
152257808251SPyun YongHyeon 	int cons;
152357808251SPyun YongHyeon 
152457808251SPyun YongHyeon 	STGE_LOCK_ASSERT(sc);
152557808251SPyun YongHyeon 
152657808251SPyun YongHyeon 	ifp = sc->sc_ifp;
152757808251SPyun YongHyeon 
152857808251SPyun YongHyeon 	txd = STAILQ_FIRST(&sc->sc_cdata.stge_txbusyq);
152957808251SPyun YongHyeon 	if (txd == NULL)
153057808251SPyun YongHyeon 		return;
153157808251SPyun YongHyeon 	bus_dmamap_sync(sc->sc_cdata.stge_tx_ring_tag,
153257808251SPyun YongHyeon 	    sc->sc_cdata.stge_tx_ring_map, BUS_DMASYNC_POSTREAD);
153357808251SPyun YongHyeon 
153457808251SPyun YongHyeon 	/*
153557808251SPyun YongHyeon 	 * Go through our Tx list and free mbufs for those
153657808251SPyun YongHyeon 	 * frames which have been transmitted.
153757808251SPyun YongHyeon 	 */
153857808251SPyun YongHyeon 	for (cons = sc->sc_cdata.stge_tx_cons;;
153957808251SPyun YongHyeon 	    cons = (cons + 1) % STGE_TX_RING_CNT) {
154057808251SPyun YongHyeon 		if (sc->sc_cdata.stge_tx_cnt <= 0)
154157808251SPyun YongHyeon 			break;
154257808251SPyun YongHyeon 		control = le64toh(sc->sc_rdata.stge_tx_ring[cons].tfd_control);
154357808251SPyun YongHyeon 		if ((control & TFD_TFDDone) == 0)
154457808251SPyun YongHyeon 			break;
154557808251SPyun YongHyeon 		sc->sc_cdata.stge_tx_cnt--;
154608ca347fSJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
154757808251SPyun YongHyeon 
154857808251SPyun YongHyeon 		bus_dmamap_sync(sc->sc_cdata.stge_tx_tag, txd->tx_dmamap,
154957808251SPyun YongHyeon 		    BUS_DMASYNC_POSTWRITE);
155057808251SPyun YongHyeon 		bus_dmamap_unload(sc->sc_cdata.stge_tx_tag, txd->tx_dmamap);
155157808251SPyun YongHyeon 
155257808251SPyun YongHyeon 		/* Output counter is updated with statistics register */
155357808251SPyun YongHyeon 		m_freem(txd->tx_m);
155457808251SPyun YongHyeon 		txd->tx_m = NULL;
155557808251SPyun YongHyeon 		STAILQ_REMOVE_HEAD(&sc->sc_cdata.stge_txbusyq, tx_q);
155657808251SPyun YongHyeon 		STAILQ_INSERT_TAIL(&sc->sc_cdata.stge_txfreeq, txd, tx_q);
155757808251SPyun YongHyeon 		txd = STAILQ_FIRST(&sc->sc_cdata.stge_txbusyq);
155857808251SPyun YongHyeon 	}
155957808251SPyun YongHyeon 	sc->sc_cdata.stge_tx_cons = cons;
156057808251SPyun YongHyeon 	if (sc->sc_cdata.stge_tx_cnt == 0)
1561eb7a67daSPyun YongHyeon 		sc->sc_watchdog_timer = 0;
156257808251SPyun YongHyeon 
156357808251SPyun YongHyeon         bus_dmamap_sync(sc->sc_cdata.stge_tx_ring_tag,
156457808251SPyun YongHyeon 	    sc->sc_cdata.stge_tx_ring_map,
156557808251SPyun YongHyeon 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
156657808251SPyun YongHyeon }
156757808251SPyun YongHyeon 
156857808251SPyun YongHyeon static __inline void
stge_discard_rxbuf(struct stge_softc * sc,int idx)156957808251SPyun YongHyeon stge_discard_rxbuf(struct stge_softc *sc, int idx)
157057808251SPyun YongHyeon {
157157808251SPyun YongHyeon 	struct stge_rfd *rfd;
157257808251SPyun YongHyeon 
157357808251SPyun YongHyeon 	rfd = &sc->sc_rdata.stge_rx_ring[idx];
157457808251SPyun YongHyeon 	rfd->rfd_status = 0;
157557808251SPyun YongHyeon }
157657808251SPyun YongHyeon 
157757808251SPyun YongHyeon #ifndef __NO_STRICT_ALIGNMENT
157857808251SPyun YongHyeon /*
157957808251SPyun YongHyeon  * It seems that TC9021's DMA engine has alignment restrictions in
158057808251SPyun YongHyeon  * DMA scatter operations. The first DMA segment has no address
158157808251SPyun YongHyeon  * alignment restrictins but the rest should be aligned on 4(?) bytes
158257808251SPyun YongHyeon  * boundary. Otherwise it would corrupt random memory. Since we don't
158357808251SPyun YongHyeon  * know which one is used for the first segment in advance we simply
158457808251SPyun YongHyeon  * don't align at all.
158557808251SPyun YongHyeon  * To avoid copying over an entire frame to align, we allocate a new
158657808251SPyun YongHyeon  * mbuf and copy ethernet header to the new mbuf. The new mbuf is
158757808251SPyun YongHyeon  * prepended into the existing mbuf chain.
158857808251SPyun YongHyeon  */
158957808251SPyun YongHyeon static __inline struct mbuf *
stge_fixup_rx(struct stge_softc * sc,struct mbuf * m)159057808251SPyun YongHyeon stge_fixup_rx(struct stge_softc *sc, struct mbuf *m)
159157808251SPyun YongHyeon {
159257808251SPyun YongHyeon 	struct mbuf *n;
159357808251SPyun YongHyeon 
159457808251SPyun YongHyeon 	n = NULL;
159557808251SPyun YongHyeon 	if (m->m_len <= (MCLBYTES - ETHER_HDR_LEN)) {
159657808251SPyun YongHyeon 		bcopy(m->m_data, m->m_data + ETHER_HDR_LEN, m->m_len);
159757808251SPyun YongHyeon 		m->m_data += ETHER_HDR_LEN;
159857808251SPyun YongHyeon 		n = m;
159957808251SPyun YongHyeon 	} else {
1600c6499eccSGleb Smirnoff 		MGETHDR(n, M_NOWAIT, MT_DATA);
160157808251SPyun YongHyeon 		if (n != NULL) {
160257808251SPyun YongHyeon 			bcopy(m->m_data, n->m_data, ETHER_HDR_LEN);
160357808251SPyun YongHyeon 			m->m_data += ETHER_HDR_LEN;
160457808251SPyun YongHyeon 			m->m_len -= ETHER_HDR_LEN;
160557808251SPyun YongHyeon 			n->m_len = ETHER_HDR_LEN;
160657808251SPyun YongHyeon 			M_MOVE_PKTHDR(n, m);
160757808251SPyun YongHyeon 			n->m_next = m;
160857808251SPyun YongHyeon 		} else
160957808251SPyun YongHyeon 			m_freem(m);
161057808251SPyun YongHyeon 	}
161157808251SPyun YongHyeon 
161257808251SPyun YongHyeon 	return (n);
161357808251SPyun YongHyeon }
161457808251SPyun YongHyeon #endif
161557808251SPyun YongHyeon 
161657808251SPyun YongHyeon /*
161757808251SPyun YongHyeon  * stge_rxeof:
161857808251SPyun YongHyeon  *
161957808251SPyun YongHyeon  *	Helper; handle receive interrupts.
162057808251SPyun YongHyeon  */
16211abcdbd1SAttilio Rao static int
stge_rxeof(struct stge_softc * sc)162257808251SPyun YongHyeon stge_rxeof(struct stge_softc *sc)
162357808251SPyun YongHyeon {
162408ca347fSJustin Hibbits 	if_t ifp;
162557808251SPyun YongHyeon 	struct stge_rxdesc *rxd;
162657808251SPyun YongHyeon 	struct mbuf *mp, *m;
162757808251SPyun YongHyeon 	uint64_t status64;
162857808251SPyun YongHyeon 	uint32_t status;
16291abcdbd1SAttilio Rao 	int cons, prog, rx_npkts;
163057808251SPyun YongHyeon 
163157808251SPyun YongHyeon 	STGE_LOCK_ASSERT(sc);
163257808251SPyun YongHyeon 
16331abcdbd1SAttilio Rao 	rx_npkts = 0;
163457808251SPyun YongHyeon 	ifp = sc->sc_ifp;
163557808251SPyun YongHyeon 
163657808251SPyun YongHyeon 	bus_dmamap_sync(sc->sc_cdata.stge_rx_ring_tag,
163757808251SPyun YongHyeon 	    sc->sc_cdata.stge_rx_ring_map, BUS_DMASYNC_POSTREAD);
163857808251SPyun YongHyeon 
163957808251SPyun YongHyeon 	prog = 0;
164057808251SPyun YongHyeon 	for (cons = sc->sc_cdata.stge_rx_cons; prog < STGE_RX_RING_CNT;
164157808251SPyun YongHyeon 	    prog++, cons = (cons + 1) % STGE_RX_RING_CNT) {
164257808251SPyun YongHyeon 		status64 = le64toh(sc->sc_rdata.stge_rx_ring[cons].rfd_status);
164357808251SPyun YongHyeon 		status = RFD_RxStatus(status64);
164457808251SPyun YongHyeon 		if ((status & RFD_RFDDone) == 0)
164557808251SPyun YongHyeon 			break;
164657808251SPyun YongHyeon #ifdef DEVICE_POLLING
164708ca347fSJustin Hibbits 		if (if_getcapenable(ifp) & IFCAP_POLLING) {
164857808251SPyun YongHyeon 			if (sc->sc_cdata.stge_rxcycles <= 0)
164957808251SPyun YongHyeon 				break;
165057808251SPyun YongHyeon 			sc->sc_cdata.stge_rxcycles--;
165157808251SPyun YongHyeon 		}
165257808251SPyun YongHyeon #endif
165357808251SPyun YongHyeon 		prog++;
165457808251SPyun YongHyeon 		rxd = &sc->sc_cdata.stge_rxdesc[cons];
165557808251SPyun YongHyeon 		mp = rxd->rx_m;
165657808251SPyun YongHyeon 
165757808251SPyun YongHyeon 		/*
165857808251SPyun YongHyeon 		 * If the packet had an error, drop it.  Note we count
165957808251SPyun YongHyeon 		 * the error later in the periodic stats update.
166057808251SPyun YongHyeon 		 */
166157808251SPyun YongHyeon 		if ((status & RFD_FrameEnd) != 0 && (status &
166257808251SPyun YongHyeon 		    (RFD_RxFIFOOverrun | RFD_RxRuntFrame |
166357808251SPyun YongHyeon 		    RFD_RxAlignmentError | RFD_RxFCSError |
166457808251SPyun YongHyeon 		    RFD_RxLengthError)) != 0) {
166557808251SPyun YongHyeon 			stge_discard_rxbuf(sc, cons);
166657808251SPyun YongHyeon 			if (sc->sc_cdata.stge_rxhead != NULL) {
166757808251SPyun YongHyeon 				m_freem(sc->sc_cdata.stge_rxhead);
166857808251SPyun YongHyeon 				STGE_RXCHAIN_RESET(sc);
166957808251SPyun YongHyeon 			}
167057808251SPyun YongHyeon 			continue;
167157808251SPyun YongHyeon 		}
167257808251SPyun YongHyeon 		/*
167357808251SPyun YongHyeon 		 * Add a new receive buffer to the ring.
167457808251SPyun YongHyeon 		 */
167557808251SPyun YongHyeon 		if (stge_newbuf(sc, cons) != 0) {
1676d5002dd1SGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
167757808251SPyun YongHyeon 			stge_discard_rxbuf(sc, cons);
167857808251SPyun YongHyeon 			if (sc->sc_cdata.stge_rxhead != NULL) {
167957808251SPyun YongHyeon 				m_freem(sc->sc_cdata.stge_rxhead);
168057808251SPyun YongHyeon 				STGE_RXCHAIN_RESET(sc);
168157808251SPyun YongHyeon 			}
168257808251SPyun YongHyeon 			continue;
168357808251SPyun YongHyeon 		}
168457808251SPyun YongHyeon 
168557808251SPyun YongHyeon 		if ((status & RFD_FrameEnd) != 0)
168657808251SPyun YongHyeon 			mp->m_len = RFD_RxDMAFrameLen(status) -
168757808251SPyun YongHyeon 			    sc->sc_cdata.stge_rxlen;
168857808251SPyun YongHyeon 		sc->sc_cdata.stge_rxlen += mp->m_len;
168957808251SPyun YongHyeon 
169057808251SPyun YongHyeon 		/* Chain mbufs. */
169157808251SPyun YongHyeon 		if (sc->sc_cdata.stge_rxhead == NULL) {
169257808251SPyun YongHyeon 			sc->sc_cdata.stge_rxhead = mp;
169357808251SPyun YongHyeon 			sc->sc_cdata.stge_rxtail = mp;
169457808251SPyun YongHyeon 		} else {
169557808251SPyun YongHyeon 			mp->m_flags &= ~M_PKTHDR;
169657808251SPyun YongHyeon 			sc->sc_cdata.stge_rxtail->m_next = mp;
169757808251SPyun YongHyeon 			sc->sc_cdata.stge_rxtail = mp;
169857808251SPyun YongHyeon 		}
169957808251SPyun YongHyeon 
170057808251SPyun YongHyeon 		if ((status & RFD_FrameEnd) != 0) {
170157808251SPyun YongHyeon 			m = sc->sc_cdata.stge_rxhead;
170257808251SPyun YongHyeon 			m->m_pkthdr.rcvif = ifp;
170357808251SPyun YongHyeon 			m->m_pkthdr.len = sc->sc_cdata.stge_rxlen;
170457808251SPyun YongHyeon 
170557808251SPyun YongHyeon 			if (m->m_pkthdr.len > sc->sc_if_framesize) {
170657808251SPyun YongHyeon 				m_freem(m);
170757808251SPyun YongHyeon 				STGE_RXCHAIN_RESET(sc);
170857808251SPyun YongHyeon 				continue;
170957808251SPyun YongHyeon 			}
171057808251SPyun YongHyeon 			/*
171157808251SPyun YongHyeon 			 * Set the incoming checksum information for
171257808251SPyun YongHyeon 			 * the packet.
171357808251SPyun YongHyeon 			 */
171408ca347fSJustin Hibbits 			if ((if_getcapenable(ifp) & IFCAP_RXCSUM) != 0) {
171557808251SPyun YongHyeon 				if ((status & RFD_IPDetected) != 0) {
171657808251SPyun YongHyeon 					m->m_pkthdr.csum_flags |=
171757808251SPyun YongHyeon 						CSUM_IP_CHECKED;
171857808251SPyun YongHyeon 					if ((status & RFD_IPError) == 0)
171957808251SPyun YongHyeon 						m->m_pkthdr.csum_flags |=
172057808251SPyun YongHyeon 						    CSUM_IP_VALID;
172157808251SPyun YongHyeon 				}
172257808251SPyun YongHyeon 				if (((status & RFD_TCPDetected) != 0 &&
172357808251SPyun YongHyeon 				    (status & RFD_TCPError) == 0) ||
172457808251SPyun YongHyeon 				    ((status & RFD_UDPDetected) != 0 &&
172557808251SPyun YongHyeon 				    (status & RFD_UDPError) == 0)) {
172657808251SPyun YongHyeon 					m->m_pkthdr.csum_flags |=
172757808251SPyun YongHyeon 					    (CSUM_DATA_VALID | CSUM_PSEUDO_HDR);
172857808251SPyun YongHyeon 					m->m_pkthdr.csum_data = 0xffff;
172957808251SPyun YongHyeon 				}
173057808251SPyun YongHyeon 			}
173157808251SPyun YongHyeon 
173257808251SPyun YongHyeon #ifndef __NO_STRICT_ALIGNMENT
173357808251SPyun YongHyeon 			if (sc->sc_if_framesize > (MCLBYTES - ETHER_ALIGN)) {
173457808251SPyun YongHyeon 				if ((m = stge_fixup_rx(sc, m)) == NULL) {
173557808251SPyun YongHyeon 					STGE_RXCHAIN_RESET(sc);
173657808251SPyun YongHyeon 					continue;
173757808251SPyun YongHyeon 				}
173857808251SPyun YongHyeon 			}
173957808251SPyun YongHyeon #endif
174057808251SPyun YongHyeon 			/* Check for VLAN tagged packets. */
174157808251SPyun YongHyeon 			if ((status & RFD_VLANDetected) != 0 &&
174208ca347fSJustin Hibbits 			    (if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING) != 0) {
174378ba57b9SAndre Oppermann 				m->m_pkthdr.ether_vtag = RFD_TCI(status64);
174478ba57b9SAndre Oppermann 				m->m_flags |= M_VLANTAG;
174578ba57b9SAndre Oppermann 			}
174657808251SPyun YongHyeon 
174757808251SPyun YongHyeon 			STGE_UNLOCK(sc);
174857808251SPyun YongHyeon 			/* Pass it on. */
174908ca347fSJustin Hibbits 			if_input(ifp, m);
175057808251SPyun YongHyeon 			STGE_LOCK(sc);
17511abcdbd1SAttilio Rao 			rx_npkts++;
175257808251SPyun YongHyeon 
175357808251SPyun YongHyeon 			STGE_RXCHAIN_RESET(sc);
175457808251SPyun YongHyeon 		}
175557808251SPyun YongHyeon 	}
175657808251SPyun YongHyeon 
175757808251SPyun YongHyeon 	if (prog > 0) {
175857808251SPyun YongHyeon 		/* Update the consumer index. */
175957808251SPyun YongHyeon 		sc->sc_cdata.stge_rx_cons = cons;
176057808251SPyun YongHyeon 		bus_dmamap_sync(sc->sc_cdata.stge_rx_ring_tag,
176157808251SPyun YongHyeon 		    sc->sc_cdata.stge_rx_ring_map,
176257808251SPyun YongHyeon 		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
176357808251SPyun YongHyeon 	}
17641abcdbd1SAttilio Rao 	return (rx_npkts);
176557808251SPyun YongHyeon }
176657808251SPyun YongHyeon 
176757808251SPyun YongHyeon #ifdef DEVICE_POLLING
17681abcdbd1SAttilio Rao static int
stge_poll(if_t ifp,enum poll_cmd cmd,int count)176908ca347fSJustin Hibbits stge_poll(if_t ifp, enum poll_cmd cmd, int count)
177057808251SPyun YongHyeon {
177157808251SPyun YongHyeon 	struct stge_softc *sc;
177257808251SPyun YongHyeon 	uint16_t status;
17731abcdbd1SAttilio Rao 	int rx_npkts;
177457808251SPyun YongHyeon 
17751abcdbd1SAttilio Rao 	rx_npkts = 0;
177608ca347fSJustin Hibbits 	sc = if_getsoftc(ifp);
177757808251SPyun YongHyeon 	STGE_LOCK(sc);
177808ca347fSJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) {
177957808251SPyun YongHyeon 		STGE_UNLOCK(sc);
17801abcdbd1SAttilio Rao 		return (rx_npkts);
178157808251SPyun YongHyeon 	}
178257808251SPyun YongHyeon 
178357808251SPyun YongHyeon 	sc->sc_cdata.stge_rxcycles = count;
17841abcdbd1SAttilio Rao 	rx_npkts = stge_rxeof(sc);
178557808251SPyun YongHyeon 	stge_txeof(sc);
178657808251SPyun YongHyeon 
178757808251SPyun YongHyeon 	if (cmd == POLL_AND_CHECK_STATUS) {
178857808251SPyun YongHyeon 		status = CSR_READ_2(sc, STGE_IntStatus);
178957808251SPyun YongHyeon 		status &= sc->sc_IntEnable;
179057808251SPyun YongHyeon 		if (status != 0) {
179157808251SPyun YongHyeon 			if ((status & IS_HostError) != 0) {
179257808251SPyun YongHyeon 				device_printf(sc->sc_dev,
179357808251SPyun YongHyeon 				    "Host interface error, resetting...\n");
179408ca347fSJustin Hibbits 				if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
179557808251SPyun YongHyeon 				stge_init_locked(sc);
179657808251SPyun YongHyeon 			}
179757808251SPyun YongHyeon 			if ((status & IS_TxComplete) != 0) {
1798fc05868aSPyun YongHyeon 				if (stge_tx_error(sc) != 0) {
179908ca347fSJustin Hibbits 					if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
180057808251SPyun YongHyeon 					stge_init_locked(sc);
1801fc05868aSPyun YongHyeon 				}
180257808251SPyun YongHyeon 			}
180357808251SPyun YongHyeon 		}
180457808251SPyun YongHyeon 	}
180557808251SPyun YongHyeon 
180608ca347fSJustin Hibbits 	if (!if_sendq_empty(ifp))
180757808251SPyun YongHyeon 		stge_start_locked(ifp);
180857808251SPyun YongHyeon 
180957808251SPyun YongHyeon 	STGE_UNLOCK(sc);
18101abcdbd1SAttilio Rao 	return (rx_npkts);
181157808251SPyun YongHyeon }
181257808251SPyun YongHyeon #endif	/* DEVICE_POLLING */
181357808251SPyun YongHyeon 
181457808251SPyun YongHyeon /*
181557808251SPyun YongHyeon  * stge_tick:
181657808251SPyun YongHyeon  *
181757808251SPyun YongHyeon  *	One second timer, used to tick the MII.
181857808251SPyun YongHyeon  */
181957808251SPyun YongHyeon static void
stge_tick(void * arg)182057808251SPyun YongHyeon stge_tick(void *arg)
182157808251SPyun YongHyeon {
182257808251SPyun YongHyeon 	struct stge_softc *sc;
182357808251SPyun YongHyeon 	struct mii_data *mii;
182457808251SPyun YongHyeon 
182557808251SPyun YongHyeon 	sc = (struct stge_softc *)arg;
182657808251SPyun YongHyeon 
182757808251SPyun YongHyeon 	STGE_LOCK_ASSERT(sc);
182857808251SPyun YongHyeon 
182957808251SPyun YongHyeon 	mii = device_get_softc(sc->sc_miibus);
183057808251SPyun YongHyeon 	mii_tick(mii);
183157808251SPyun YongHyeon 
183257808251SPyun YongHyeon 	/* Update statistics counters. */
183357808251SPyun YongHyeon 	stge_stats_update(sc);
183457808251SPyun YongHyeon 
183557808251SPyun YongHyeon 	/*
183657808251SPyun YongHyeon 	 * Relcaim any pending Tx descriptors to release mbufs in a
183757808251SPyun YongHyeon 	 * timely manner as we don't generate Tx completion interrupts
183857808251SPyun YongHyeon 	 * for every frame. This limits the delay to a maximum of one
183957808251SPyun YongHyeon 	 * second.
184057808251SPyun YongHyeon 	 */
184157808251SPyun YongHyeon 	if (sc->sc_cdata.stge_tx_cnt != 0)
184257808251SPyun YongHyeon 		stge_txeof(sc);
184357808251SPyun YongHyeon 
1844eb7a67daSPyun YongHyeon 	stge_watchdog(sc);
1845eb7a67daSPyun YongHyeon 
184657808251SPyun YongHyeon 	callout_reset(&sc->sc_tick_ch, hz, stge_tick, sc);
184757808251SPyun YongHyeon }
184857808251SPyun YongHyeon 
184957808251SPyun YongHyeon /*
185057808251SPyun YongHyeon  * stge_stats_update:
185157808251SPyun YongHyeon  *
185257808251SPyun YongHyeon  *	Read the TC9021 statistics counters.
185357808251SPyun YongHyeon  */
185457808251SPyun YongHyeon static void
stge_stats_update(struct stge_softc * sc)185557808251SPyun YongHyeon stge_stats_update(struct stge_softc *sc)
185657808251SPyun YongHyeon {
185708ca347fSJustin Hibbits 	if_t ifp;
185857808251SPyun YongHyeon 
185957808251SPyun YongHyeon 	STGE_LOCK_ASSERT(sc);
186057808251SPyun YongHyeon 
186157808251SPyun YongHyeon 	ifp = sc->sc_ifp;
186257808251SPyun YongHyeon 
186357808251SPyun YongHyeon 	CSR_READ_4(sc,STGE_OctetRcvOk);
186457808251SPyun YongHyeon 
1865d5002dd1SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_IPACKETS, CSR_READ_4(sc, STGE_FramesRcvdOk));
186657808251SPyun YongHyeon 
1867d5002dd1SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_IERRORS, CSR_READ_2(sc, STGE_FramesLostRxErrors));
186857808251SPyun YongHyeon 
186957808251SPyun YongHyeon 	CSR_READ_4(sc, STGE_OctetXmtdOk);
187057808251SPyun YongHyeon 
1871d5002dd1SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_OPACKETS, CSR_READ_4(sc, STGE_FramesXmtdOk));
187257808251SPyun YongHyeon 
1873d5002dd1SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_COLLISIONS,
187457808251SPyun YongHyeon 	    CSR_READ_4(sc, STGE_LateCollisions) +
187557808251SPyun YongHyeon 	    CSR_READ_4(sc, STGE_MultiColFrames) +
1876d5002dd1SGleb Smirnoff 	    CSR_READ_4(sc, STGE_SingleColFrames));
187757808251SPyun YongHyeon 
1878d5002dd1SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_OERRORS,
187957808251SPyun YongHyeon 	    CSR_READ_2(sc, STGE_FramesAbortXSColls) +
1880d5002dd1SGleb Smirnoff 	    CSR_READ_2(sc, STGE_FramesWEXDeferal));
188157808251SPyun YongHyeon }
188257808251SPyun YongHyeon 
188357808251SPyun YongHyeon /*
188457808251SPyun YongHyeon  * stge_reset:
188557808251SPyun YongHyeon  *
188657808251SPyun YongHyeon  *	Perform a soft reset on the TC9021.
188757808251SPyun YongHyeon  */
188857808251SPyun YongHyeon static void
stge_reset(struct stge_softc * sc,uint32_t how)188957808251SPyun YongHyeon stge_reset(struct stge_softc *sc, uint32_t how)
189057808251SPyun YongHyeon {
189157808251SPyun YongHyeon 	uint32_t ac;
189257808251SPyun YongHyeon 	uint8_t v;
189357808251SPyun YongHyeon 	int i, dv;
189457808251SPyun YongHyeon 
189557808251SPyun YongHyeon 	STGE_LOCK_ASSERT(sc);
189657808251SPyun YongHyeon 
189757808251SPyun YongHyeon 	dv = 5000;
189857808251SPyun YongHyeon 	ac = CSR_READ_4(sc, STGE_AsicCtrl);
189957808251SPyun YongHyeon 	switch (how) {
190057808251SPyun YongHyeon 	case STGE_RESET_TX:
190157808251SPyun YongHyeon 		ac |= AC_TxReset | AC_FIFO;
190257808251SPyun YongHyeon 		dv = 100;
190357808251SPyun YongHyeon 		break;
190457808251SPyun YongHyeon 	case STGE_RESET_RX:
190557808251SPyun YongHyeon 		ac |= AC_RxReset | AC_FIFO;
190657808251SPyun YongHyeon 		dv = 100;
190757808251SPyun YongHyeon 		break;
190857808251SPyun YongHyeon 	case STGE_RESET_FULL:
190957808251SPyun YongHyeon 	default:
191057808251SPyun YongHyeon 		/*
191157808251SPyun YongHyeon 		 * Only assert RstOut if we're fiber.  We need GMII clocks
191257808251SPyun YongHyeon 		 * to be present in order for the reset to complete on fiber
191357808251SPyun YongHyeon 		 * cards.
191457808251SPyun YongHyeon 		 */
191557808251SPyun YongHyeon 		ac |= AC_GlobalReset | AC_RxReset | AC_TxReset |
191657808251SPyun YongHyeon 		    AC_DMA | AC_FIFO | AC_Network | AC_Host | AC_AutoInit |
191757808251SPyun YongHyeon 		    (sc->sc_usefiber ? AC_RstOut : 0);
191857808251SPyun YongHyeon 		break;
191957808251SPyun YongHyeon 	}
192057808251SPyun YongHyeon 
192157808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_AsicCtrl, ac);
192257808251SPyun YongHyeon 
192357808251SPyun YongHyeon 	/* Account for reset problem at 10Mbps. */
192457808251SPyun YongHyeon 	DELAY(dv);
192557808251SPyun YongHyeon 
192657808251SPyun YongHyeon 	for (i = 0; i < STGE_TIMEOUT; i++) {
192757808251SPyun YongHyeon 		if ((CSR_READ_4(sc, STGE_AsicCtrl) & AC_ResetBusy) == 0)
192857808251SPyun YongHyeon 			break;
192957808251SPyun YongHyeon 		DELAY(dv);
193057808251SPyun YongHyeon 	}
193157808251SPyun YongHyeon 
193257808251SPyun YongHyeon 	if (i == STGE_TIMEOUT)
193357808251SPyun YongHyeon 		device_printf(sc->sc_dev, "reset failed to complete\n");
193457808251SPyun YongHyeon 
193557808251SPyun YongHyeon 	/* Set LED, from Linux IPG driver. */
193657808251SPyun YongHyeon 	ac = CSR_READ_4(sc, STGE_AsicCtrl);
193757808251SPyun YongHyeon 	ac &= ~(AC_LEDMode | AC_LEDSpeed | AC_LEDModeBit1);
193857808251SPyun YongHyeon 	if ((sc->sc_led & 0x01) != 0)
193957808251SPyun YongHyeon 		ac |= AC_LEDMode;
194057808251SPyun YongHyeon 	if ((sc->sc_led & 0x03) != 0)
194157808251SPyun YongHyeon 		ac |= AC_LEDModeBit1;
194257808251SPyun YongHyeon 	if ((sc->sc_led & 0x08) != 0)
194357808251SPyun YongHyeon 		ac |= AC_LEDSpeed;
194457808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_AsicCtrl, ac);
194557808251SPyun YongHyeon 
194657808251SPyun YongHyeon 	/* Set PHY, from Linux IPG driver */
194757808251SPyun YongHyeon 	v = CSR_READ_1(sc, STGE_PhySet);
194857808251SPyun YongHyeon 	v &= ~(PS_MemLenb9b | PS_MemLen | PS_NonCompdet);
194957808251SPyun YongHyeon 	v |= ((sc->sc_led & 0x70) >> 4);
195057808251SPyun YongHyeon 	CSR_WRITE_1(sc, STGE_PhySet, v);
195157808251SPyun YongHyeon }
195257808251SPyun YongHyeon 
195357808251SPyun YongHyeon /*
195457808251SPyun YongHyeon  * stge_init:		[ ifnet interface function ]
195557808251SPyun YongHyeon  *
195657808251SPyun YongHyeon  *	Initialize the interface.
195757808251SPyun YongHyeon  */
195857808251SPyun YongHyeon static void
stge_init(void * xsc)195957808251SPyun YongHyeon stge_init(void *xsc)
196057808251SPyun YongHyeon {
196157808251SPyun YongHyeon 	struct stge_softc *sc;
196257808251SPyun YongHyeon 
196357808251SPyun YongHyeon 	sc = (struct stge_softc *)xsc;
196457808251SPyun YongHyeon 	STGE_LOCK(sc);
196557808251SPyun YongHyeon 	stge_init_locked(sc);
196657808251SPyun YongHyeon 	STGE_UNLOCK(sc);
196757808251SPyun YongHyeon }
196857808251SPyun YongHyeon 
196957808251SPyun YongHyeon static void
stge_init_locked(struct stge_softc * sc)197057808251SPyun YongHyeon stge_init_locked(struct stge_softc *sc)
197157808251SPyun YongHyeon {
197208ca347fSJustin Hibbits 	if_t ifp;
197357808251SPyun YongHyeon 	struct mii_data *mii;
197457808251SPyun YongHyeon 	uint16_t eaddr[3];
197557808251SPyun YongHyeon 	uint32_t v;
197657808251SPyun YongHyeon 	int error;
197757808251SPyun YongHyeon 
197857808251SPyun YongHyeon 	STGE_LOCK_ASSERT(sc);
197957808251SPyun YongHyeon 
198057808251SPyun YongHyeon 	ifp = sc->sc_ifp;
198108ca347fSJustin Hibbits 	if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0)
1982fc05868aSPyun YongHyeon 		return;
198357808251SPyun YongHyeon 	mii = device_get_softc(sc->sc_miibus);
198457808251SPyun YongHyeon 
198557808251SPyun YongHyeon 	/*
198657808251SPyun YongHyeon 	 * Cancel any pending I/O.
198757808251SPyun YongHyeon 	 */
198857808251SPyun YongHyeon 	stge_stop(sc);
198957808251SPyun YongHyeon 
1990346de09fSPyun YongHyeon 	/*
1991346de09fSPyun YongHyeon 	 * Reset the chip to a known state.
1992346de09fSPyun YongHyeon 	 */
1993346de09fSPyun YongHyeon 	stge_reset(sc, STGE_RESET_FULL);
1994346de09fSPyun YongHyeon 
199557808251SPyun YongHyeon 	/* Init descriptors. */
199657808251SPyun YongHyeon 	error = stge_init_rx_ring(sc);
199757808251SPyun YongHyeon         if (error != 0) {
199857808251SPyun YongHyeon                 device_printf(sc->sc_dev,
199957808251SPyun YongHyeon                     "initialization failed: no memory for rx buffers\n");
200057808251SPyun YongHyeon                 stge_stop(sc);
200157808251SPyun YongHyeon 		goto out;
200257808251SPyun YongHyeon         }
200357808251SPyun YongHyeon 	stge_init_tx_ring(sc);
200457808251SPyun YongHyeon 
200557808251SPyun YongHyeon 	/* Set the station address. */
200608ca347fSJustin Hibbits 	bcopy(if_getlladdr(ifp), eaddr, ETHER_ADDR_LEN);
200757808251SPyun YongHyeon 	CSR_WRITE_2(sc, STGE_StationAddress0, htole16(eaddr[0]));
200857808251SPyun YongHyeon 	CSR_WRITE_2(sc, STGE_StationAddress1, htole16(eaddr[1]));
200957808251SPyun YongHyeon 	CSR_WRITE_2(sc, STGE_StationAddress2, htole16(eaddr[2]));
201057808251SPyun YongHyeon 
201157808251SPyun YongHyeon 	/*
201257808251SPyun YongHyeon 	 * Set the statistics masks.  Disable all the RMON stats,
201357808251SPyun YongHyeon 	 * and disable selected stats in the non-RMON stats registers.
201457808251SPyun YongHyeon 	 */
201557808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_RMONStatisticsMask, 0xffffffff);
201657808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_StatisticsMask,
201757808251SPyun YongHyeon 	    (1U << 1) | (1U << 2) | (1U << 3) | (1U << 4) | (1U << 5) |
201857808251SPyun YongHyeon 	    (1U << 6) | (1U << 7) | (1U << 8) | (1U << 9) | (1U << 10) |
201957808251SPyun YongHyeon 	    (1U << 13) | (1U << 14) | (1U << 15) | (1U << 19) | (1U << 20) |
202057808251SPyun YongHyeon 	    (1U << 21));
202157808251SPyun YongHyeon 
202257808251SPyun YongHyeon 	/* Set up the receive filter. */
202357808251SPyun YongHyeon 	stge_set_filter(sc);
202457808251SPyun YongHyeon 	/* Program multicast filter. */
202557808251SPyun YongHyeon 	stge_set_multi(sc);
202657808251SPyun YongHyeon 
202757808251SPyun YongHyeon 	/*
202857808251SPyun YongHyeon 	 * Give the transmit and receive ring to the chip.
202957808251SPyun YongHyeon 	 */
203057808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_TFDListPtrHi,
203157808251SPyun YongHyeon 	    STGE_ADDR_HI(STGE_TX_RING_ADDR(sc, 0)));
203257808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_TFDListPtrLo,
203357808251SPyun YongHyeon 	    STGE_ADDR_LO(STGE_TX_RING_ADDR(sc, 0)));
203457808251SPyun YongHyeon 
203557808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_RFDListPtrHi,
203657808251SPyun YongHyeon 	    STGE_ADDR_HI(STGE_RX_RING_ADDR(sc, 0)));
203757808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_RFDListPtrLo,
203857808251SPyun YongHyeon 	    STGE_ADDR_LO(STGE_RX_RING_ADDR(sc, 0)));
203957808251SPyun YongHyeon 
204057808251SPyun YongHyeon 	/*
204157808251SPyun YongHyeon 	 * Initialize the Tx auto-poll period.  It's OK to make this number
204257808251SPyun YongHyeon 	 * large (255 is the max, but we use 127) -- we explicitly kick the
204357808251SPyun YongHyeon 	 * transmit engine when there's actually a packet.
204457808251SPyun YongHyeon 	 */
204557808251SPyun YongHyeon 	CSR_WRITE_1(sc, STGE_TxDMAPollPeriod, 127);
204657808251SPyun YongHyeon 
204757808251SPyun YongHyeon 	/* ..and the Rx auto-poll period. */
204857808251SPyun YongHyeon 	CSR_WRITE_1(sc, STGE_RxDMAPollPeriod, 1);
204957808251SPyun YongHyeon 
205057808251SPyun YongHyeon 	/* Initialize the Tx start threshold. */
205157808251SPyun YongHyeon 	CSR_WRITE_2(sc, STGE_TxStartThresh, sc->sc_txthresh);
205257808251SPyun YongHyeon 
205357808251SPyun YongHyeon 	/* Rx DMA thresholds, from Linux */
205457808251SPyun YongHyeon 	CSR_WRITE_1(sc, STGE_RxDMABurstThresh, 0x30);
205557808251SPyun YongHyeon 	CSR_WRITE_1(sc, STGE_RxDMAUrgentThresh, 0x30);
205657808251SPyun YongHyeon 
205757808251SPyun YongHyeon 	/* Rx early threhold, from Linux */
205857808251SPyun YongHyeon 	CSR_WRITE_2(sc, STGE_RxEarlyThresh, 0x7ff);
205957808251SPyun YongHyeon 
206057808251SPyun YongHyeon 	/* Tx DMA thresholds, from Linux */
206157808251SPyun YongHyeon 	CSR_WRITE_1(sc, STGE_TxDMABurstThresh, 0x30);
206257808251SPyun YongHyeon 	CSR_WRITE_1(sc, STGE_TxDMAUrgentThresh, 0x04);
206357808251SPyun YongHyeon 
206457808251SPyun YongHyeon 	/*
206557808251SPyun YongHyeon 	 * Initialize the Rx DMA interrupt control register.  We
206657808251SPyun YongHyeon 	 * request an interrupt after every incoming packet, but
206757808251SPyun YongHyeon 	 * defer it for sc_rxint_dmawait us. When the number of
206857808251SPyun YongHyeon 	 * interrupts pending reaches STGE_RXINT_NFRAME, we stop
206957808251SPyun YongHyeon 	 * deferring the interrupt, and signal it immediately.
207057808251SPyun YongHyeon 	 */
207157808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_RxDMAIntCtrl,
207257808251SPyun YongHyeon 	    RDIC_RxFrameCount(sc->sc_rxint_nframe) |
207357808251SPyun YongHyeon 	    RDIC_RxDMAWaitTime(STGE_RXINT_USECS2TICK(sc->sc_rxint_dmawait)));
207457808251SPyun YongHyeon 
207557808251SPyun YongHyeon 	/*
207657808251SPyun YongHyeon 	 * Initialize the interrupt mask.
207757808251SPyun YongHyeon 	 */
207857808251SPyun YongHyeon 	sc->sc_IntEnable = IS_HostError | IS_TxComplete |
207957808251SPyun YongHyeon 	    IS_TxDMAComplete | IS_RxDMAComplete | IS_RFDListEnd;
208057808251SPyun YongHyeon #ifdef DEVICE_POLLING
208157808251SPyun YongHyeon 	/* Disable interrupts if we are polling. */
208208ca347fSJustin Hibbits 	if ((if_getcapenable(ifp) & IFCAP_POLLING) != 0)
208357808251SPyun YongHyeon 		CSR_WRITE_2(sc, STGE_IntEnable, 0);
208457808251SPyun YongHyeon 	else
208557808251SPyun YongHyeon #endif
208657808251SPyun YongHyeon 	CSR_WRITE_2(sc, STGE_IntEnable, sc->sc_IntEnable);
208757808251SPyun YongHyeon 
208857808251SPyun YongHyeon 	/*
208957808251SPyun YongHyeon 	 * Configure the DMA engine.
209057808251SPyun YongHyeon 	 * XXX Should auto-tune TxBurstLimit.
209157808251SPyun YongHyeon 	 */
209257808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_DMACtrl, sc->sc_DMACtrl | DMAC_TxBurstLimit(3));
209357808251SPyun YongHyeon 
209457808251SPyun YongHyeon 	/*
209557808251SPyun YongHyeon 	 * Send a PAUSE frame when we reach 29,696 bytes in the Rx
209657808251SPyun YongHyeon 	 * FIFO, and send an un-PAUSE frame when we reach 3056 bytes
209757808251SPyun YongHyeon 	 * in the Rx FIFO.
209857808251SPyun YongHyeon 	 */
209957808251SPyun YongHyeon 	CSR_WRITE_2(sc, STGE_FlowOnTresh, 29696 / 16);
210057808251SPyun YongHyeon 	CSR_WRITE_2(sc, STGE_FlowOffThresh, 3056 / 16);
210157808251SPyun YongHyeon 
210257808251SPyun YongHyeon 	/*
210357808251SPyun YongHyeon 	 * Set the maximum frame size.
210457808251SPyun YongHyeon 	 */
210508ca347fSJustin Hibbits 	sc->sc_if_framesize = if_getmtu(ifp) + ETHER_HDR_LEN + ETHER_CRC_LEN;
210657808251SPyun YongHyeon 	CSR_WRITE_2(sc, STGE_MaxFrameSize, sc->sc_if_framesize);
210757808251SPyun YongHyeon 
210857808251SPyun YongHyeon 	/*
210957808251SPyun YongHyeon 	 * Initialize MacCtrl -- do it before setting the media,
211057808251SPyun YongHyeon 	 * as setting the media will actually program the register.
211157808251SPyun YongHyeon 	 *
211257808251SPyun YongHyeon 	 * Note: We have to poke the IFS value before poking
211357808251SPyun YongHyeon 	 * anything else.
211457808251SPyun YongHyeon 	 */
211557808251SPyun YongHyeon 	/* Tx/Rx MAC should be disabled before programming IFS.*/
211657808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_MACCtrl, MC_IFSSelect(MC_IFS96bit));
211757808251SPyun YongHyeon 
211857808251SPyun YongHyeon 	stge_vlan_setup(sc);
211957808251SPyun YongHyeon 
212057808251SPyun YongHyeon 	if (sc->sc_rev >= 6) {		/* >= B.2 */
212157808251SPyun YongHyeon 		/* Multi-frag frame bug work-around. */
212257808251SPyun YongHyeon 		CSR_WRITE_2(sc, STGE_DebugCtrl,
212357808251SPyun YongHyeon 		    CSR_READ_2(sc, STGE_DebugCtrl) | 0x0200);
212457808251SPyun YongHyeon 
212557808251SPyun YongHyeon 		/* Tx Poll Now bug work-around. */
212657808251SPyun YongHyeon 		CSR_WRITE_2(sc, STGE_DebugCtrl,
212757808251SPyun YongHyeon 		    CSR_READ_2(sc, STGE_DebugCtrl) | 0x0010);
212857808251SPyun YongHyeon 		/* Tx Poll Now bug work-around. */
212957808251SPyun YongHyeon 		CSR_WRITE_2(sc, STGE_DebugCtrl,
213057808251SPyun YongHyeon 		    CSR_READ_2(sc, STGE_DebugCtrl) | 0x0020);
213157808251SPyun YongHyeon 	}
213257808251SPyun YongHyeon 
213357808251SPyun YongHyeon 	v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK;
213457808251SPyun YongHyeon 	v |= MC_StatisticsEnable | MC_TxEnable | MC_RxEnable;
213557808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_MACCtrl, v);
213657808251SPyun YongHyeon 	/*
213757808251SPyun YongHyeon 	 * It seems that transmitting frames without checking the state of
213857808251SPyun YongHyeon 	 * Rx/Tx MAC wedge the hardware.
213957808251SPyun YongHyeon 	 */
214057808251SPyun YongHyeon 	stge_start_tx(sc);
214157808251SPyun YongHyeon 	stge_start_rx(sc);
214257808251SPyun YongHyeon 
21437ef4ec5dSPyun YongHyeon 	sc->sc_link = 0;
214457808251SPyun YongHyeon 	/*
214557808251SPyun YongHyeon 	 * Set the current media.
214657808251SPyun YongHyeon 	 */
214757808251SPyun YongHyeon 	mii_mediachg(mii);
214857808251SPyun YongHyeon 
214957808251SPyun YongHyeon 	/*
215057808251SPyun YongHyeon 	 * Start the one second MII clock.
215157808251SPyun YongHyeon 	 */
215257808251SPyun YongHyeon 	callout_reset(&sc->sc_tick_ch, hz, stge_tick, sc);
215357808251SPyun YongHyeon 
215457808251SPyun YongHyeon 	/*
215557808251SPyun YongHyeon 	 * ...all done!
215657808251SPyun YongHyeon 	 */
215708ca347fSJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
215808ca347fSJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
215957808251SPyun YongHyeon 
216057808251SPyun YongHyeon  out:
216157808251SPyun YongHyeon 	if (error != 0)
216257808251SPyun YongHyeon 		device_printf(sc->sc_dev, "interface not running\n");
216357808251SPyun YongHyeon }
216457808251SPyun YongHyeon 
216557808251SPyun YongHyeon static void
stge_vlan_setup(struct stge_softc * sc)216657808251SPyun YongHyeon stge_vlan_setup(struct stge_softc *sc)
216757808251SPyun YongHyeon {
216808ca347fSJustin Hibbits 	if_t ifp;
216957808251SPyun YongHyeon 	uint32_t v;
217057808251SPyun YongHyeon 
217157808251SPyun YongHyeon 	ifp = sc->sc_ifp;
217257808251SPyun YongHyeon 	/*
217357808251SPyun YongHyeon 	 * The NIC always copy a VLAN tag regardless of STGE_MACCtrl
217457808251SPyun YongHyeon 	 * MC_AutoVLANuntagging bit.
217557808251SPyun YongHyeon 	 * MC_AutoVLANtagging bit selects which VLAN source to use
217657808251SPyun YongHyeon 	 * between STGE_VLANTag and TFC. However TFC TFD_VLANTagInsert
217757808251SPyun YongHyeon 	 * bit has priority over MC_AutoVLANtagging bit. So we always
217857808251SPyun YongHyeon 	 * use TFC instead of STGE_VLANTag register.
217957808251SPyun YongHyeon 	 */
218057808251SPyun YongHyeon 	v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK;
218108ca347fSJustin Hibbits 	if ((if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING) != 0)
218257808251SPyun YongHyeon 		v |= MC_AutoVLANuntagging;
218357808251SPyun YongHyeon 	else
218457808251SPyun YongHyeon 		v &= ~MC_AutoVLANuntagging;
218557808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_MACCtrl, v);
218657808251SPyun YongHyeon }
218757808251SPyun YongHyeon 
218857808251SPyun YongHyeon /*
218957808251SPyun YongHyeon  *	Stop transmission on the interface.
219057808251SPyun YongHyeon  */
219157808251SPyun YongHyeon static void
stge_stop(struct stge_softc * sc)219257808251SPyun YongHyeon stge_stop(struct stge_softc *sc)
219357808251SPyun YongHyeon {
219408ca347fSJustin Hibbits 	if_t ifp;
219557808251SPyun YongHyeon 	struct stge_txdesc *txd;
219657808251SPyun YongHyeon 	struct stge_rxdesc *rxd;
219757808251SPyun YongHyeon 	uint32_t v;
219857808251SPyun YongHyeon 	int i;
219957808251SPyun YongHyeon 
220057808251SPyun YongHyeon 	STGE_LOCK_ASSERT(sc);
220157808251SPyun YongHyeon 	/*
220257808251SPyun YongHyeon 	 * Stop the one second clock.
220357808251SPyun YongHyeon 	 */
220457808251SPyun YongHyeon 	callout_stop(&sc->sc_tick_ch);
2205eb7a67daSPyun YongHyeon 	sc->sc_watchdog_timer = 0;
220657808251SPyun YongHyeon 
220757808251SPyun YongHyeon 	/*
220857808251SPyun YongHyeon 	 * Disable interrupts.
220957808251SPyun YongHyeon 	 */
221057808251SPyun YongHyeon 	CSR_WRITE_2(sc, STGE_IntEnable, 0);
221157808251SPyun YongHyeon 
221257808251SPyun YongHyeon 	/*
221357808251SPyun YongHyeon 	 * Stop receiver, transmitter, and stats update.
221457808251SPyun YongHyeon 	 */
221557808251SPyun YongHyeon 	stge_stop_rx(sc);
221657808251SPyun YongHyeon 	stge_stop_tx(sc);
221757808251SPyun YongHyeon 	v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK;
221857808251SPyun YongHyeon 	v |= MC_StatisticsDisable;
221957808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_MACCtrl, v);
222057808251SPyun YongHyeon 
222157808251SPyun YongHyeon 	/*
222257808251SPyun YongHyeon 	 * Stop the transmit and receive DMA.
222357808251SPyun YongHyeon 	 */
222457808251SPyun YongHyeon 	stge_dma_wait(sc);
222557808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_TFDListPtrHi, 0);
222657808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_TFDListPtrLo, 0);
222757808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_RFDListPtrHi, 0);
222857808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_RFDListPtrLo, 0);
222957808251SPyun YongHyeon 
223057808251SPyun YongHyeon 	/*
223157808251SPyun YongHyeon 	 * Free RX and TX mbufs still in the queues.
223257808251SPyun YongHyeon 	 */
223357808251SPyun YongHyeon 	for (i = 0; i < STGE_RX_RING_CNT; i++) {
223457808251SPyun YongHyeon 		rxd = &sc->sc_cdata.stge_rxdesc[i];
223557808251SPyun YongHyeon 		if (rxd->rx_m != NULL) {
223657808251SPyun YongHyeon 			bus_dmamap_sync(sc->sc_cdata.stge_rx_tag,
223757808251SPyun YongHyeon 			    rxd->rx_dmamap, BUS_DMASYNC_POSTREAD);
223857808251SPyun YongHyeon 			bus_dmamap_unload(sc->sc_cdata.stge_rx_tag,
223957808251SPyun YongHyeon 			    rxd->rx_dmamap);
224057808251SPyun YongHyeon 			m_freem(rxd->rx_m);
224157808251SPyun YongHyeon 			rxd->rx_m = NULL;
224257808251SPyun YongHyeon 		}
224357808251SPyun YongHyeon         }
224457808251SPyun YongHyeon 	for (i = 0; i < STGE_TX_RING_CNT; i++) {
224557808251SPyun YongHyeon 		txd = &sc->sc_cdata.stge_txdesc[i];
224657808251SPyun YongHyeon 		if (txd->tx_m != NULL) {
224757808251SPyun YongHyeon 			bus_dmamap_sync(sc->sc_cdata.stge_tx_tag,
224857808251SPyun YongHyeon 			    txd->tx_dmamap, BUS_DMASYNC_POSTWRITE);
224957808251SPyun YongHyeon 			bus_dmamap_unload(sc->sc_cdata.stge_tx_tag,
225057808251SPyun YongHyeon 			    txd->tx_dmamap);
225157808251SPyun YongHyeon 			m_freem(txd->tx_m);
225257808251SPyun YongHyeon 			txd->tx_m = NULL;
225357808251SPyun YongHyeon 		}
225457808251SPyun YongHyeon         }
225557808251SPyun YongHyeon 
225657808251SPyun YongHyeon 	/*
225757808251SPyun YongHyeon 	 * Mark the interface down and cancel the watchdog timer.
225857808251SPyun YongHyeon 	 */
225957808251SPyun YongHyeon 	ifp = sc->sc_ifp;
226008ca347fSJustin Hibbits 	if_setdrvflagbits(ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE));
22617ef4ec5dSPyun YongHyeon 	sc->sc_link = 0;
226257808251SPyun YongHyeon }
226357808251SPyun YongHyeon 
226457808251SPyun YongHyeon static void
stge_start_tx(struct stge_softc * sc)226557808251SPyun YongHyeon stge_start_tx(struct stge_softc *sc)
226657808251SPyun YongHyeon {
226757808251SPyun YongHyeon 	uint32_t v;
226857808251SPyun YongHyeon 	int i;
226957808251SPyun YongHyeon 
227057808251SPyun YongHyeon 	v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK;
227157808251SPyun YongHyeon 	if ((v & MC_TxEnabled) != 0)
227257808251SPyun YongHyeon 		return;
227357808251SPyun YongHyeon 	v |= MC_TxEnable;
227457808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_MACCtrl, v);
227557808251SPyun YongHyeon 	CSR_WRITE_1(sc, STGE_TxDMAPollPeriod, 127);
227657808251SPyun YongHyeon 	for (i = STGE_TIMEOUT; i > 0; i--) {
227757808251SPyun YongHyeon 		DELAY(10);
227857808251SPyun YongHyeon 		v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK;
227957808251SPyun YongHyeon 		if ((v & MC_TxEnabled) != 0)
228057808251SPyun YongHyeon 			break;
228157808251SPyun YongHyeon 	}
228257808251SPyun YongHyeon 	if (i == 0)
228357808251SPyun YongHyeon 		device_printf(sc->sc_dev, "Starting Tx MAC timed out\n");
228457808251SPyun YongHyeon }
228557808251SPyun YongHyeon 
228657808251SPyun YongHyeon static void
stge_start_rx(struct stge_softc * sc)228757808251SPyun YongHyeon stge_start_rx(struct stge_softc *sc)
228857808251SPyun YongHyeon {
228957808251SPyun YongHyeon 	uint32_t v;
229057808251SPyun YongHyeon 	int i;
229157808251SPyun YongHyeon 
229257808251SPyun YongHyeon 	v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK;
229357808251SPyun YongHyeon 	if ((v & MC_RxEnabled) != 0)
229457808251SPyun YongHyeon 		return;
229557808251SPyun YongHyeon 	v |= MC_RxEnable;
229657808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_MACCtrl, v);
229757808251SPyun YongHyeon 	CSR_WRITE_1(sc, STGE_RxDMAPollPeriod, 1);
229857808251SPyun YongHyeon 	for (i = STGE_TIMEOUT; i > 0; i--) {
229957808251SPyun YongHyeon 		DELAY(10);
230057808251SPyun YongHyeon 		v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK;
230157808251SPyun YongHyeon 		if ((v & MC_RxEnabled) != 0)
230257808251SPyun YongHyeon 			break;
230357808251SPyun YongHyeon 	}
230457808251SPyun YongHyeon 	if (i == 0)
230557808251SPyun YongHyeon 		device_printf(sc->sc_dev, "Starting Rx MAC timed out\n");
230657808251SPyun YongHyeon }
230757808251SPyun YongHyeon 
230857808251SPyun YongHyeon static void
stge_stop_tx(struct stge_softc * sc)230957808251SPyun YongHyeon stge_stop_tx(struct stge_softc *sc)
231057808251SPyun YongHyeon {
231157808251SPyun YongHyeon 	uint32_t v;
231257808251SPyun YongHyeon 	int i;
231357808251SPyun YongHyeon 
231457808251SPyun YongHyeon 	v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK;
231557808251SPyun YongHyeon 	if ((v & MC_TxEnabled) == 0)
231657808251SPyun YongHyeon 		return;
231757808251SPyun YongHyeon 	v |= MC_TxDisable;
231857808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_MACCtrl, v);
231957808251SPyun YongHyeon 	for (i = STGE_TIMEOUT; i > 0; i--) {
232057808251SPyun YongHyeon 		DELAY(10);
232157808251SPyun YongHyeon 		v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK;
232257808251SPyun YongHyeon 		if ((v & MC_TxEnabled) == 0)
232357808251SPyun YongHyeon 			break;
232457808251SPyun YongHyeon 	}
232557808251SPyun YongHyeon 	if (i == 0)
232657808251SPyun YongHyeon 		device_printf(sc->sc_dev, "Stopping Tx MAC timed out\n");
232757808251SPyun YongHyeon }
232857808251SPyun YongHyeon 
232957808251SPyun YongHyeon static void
stge_stop_rx(struct stge_softc * sc)233057808251SPyun YongHyeon stge_stop_rx(struct stge_softc *sc)
233157808251SPyun YongHyeon {
233257808251SPyun YongHyeon 	uint32_t v;
233357808251SPyun YongHyeon 	int i;
233457808251SPyun YongHyeon 
233557808251SPyun YongHyeon 	v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK;
233657808251SPyun YongHyeon 	if ((v & MC_RxEnabled) == 0)
233757808251SPyun YongHyeon 		return;
233857808251SPyun YongHyeon 	v |= MC_RxDisable;
233957808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_MACCtrl, v);
234057808251SPyun YongHyeon 	for (i = STGE_TIMEOUT; i > 0; i--) {
234157808251SPyun YongHyeon 		DELAY(10);
234257808251SPyun YongHyeon 		v = CSR_READ_4(sc, STGE_MACCtrl) & MC_MASK;
234357808251SPyun YongHyeon 		if ((v & MC_RxEnabled) == 0)
234457808251SPyun YongHyeon 			break;
234557808251SPyun YongHyeon 	}
234657808251SPyun YongHyeon 	if (i == 0)
234757808251SPyun YongHyeon 		device_printf(sc->sc_dev, "Stopping Rx MAC timed out\n");
234857808251SPyun YongHyeon }
234957808251SPyun YongHyeon 
235057808251SPyun YongHyeon static void
stge_init_tx_ring(struct stge_softc * sc)235157808251SPyun YongHyeon stge_init_tx_ring(struct stge_softc *sc)
235257808251SPyun YongHyeon {
235357808251SPyun YongHyeon 	struct stge_ring_data *rd;
235457808251SPyun YongHyeon 	struct stge_txdesc *txd;
235557808251SPyun YongHyeon 	bus_addr_t addr;
235657808251SPyun YongHyeon 	int i;
235757808251SPyun YongHyeon 
235857808251SPyun YongHyeon 	STAILQ_INIT(&sc->sc_cdata.stge_txfreeq);
235957808251SPyun YongHyeon 	STAILQ_INIT(&sc->sc_cdata.stge_txbusyq);
236057808251SPyun YongHyeon 
236157808251SPyun YongHyeon 	sc->sc_cdata.stge_tx_prod = 0;
236257808251SPyun YongHyeon 	sc->sc_cdata.stge_tx_cons = 0;
236357808251SPyun YongHyeon 	sc->sc_cdata.stge_tx_cnt = 0;
236457808251SPyun YongHyeon 
236557808251SPyun YongHyeon 	rd = &sc->sc_rdata;
236657808251SPyun YongHyeon 	bzero(rd->stge_tx_ring, STGE_TX_RING_SZ);
236757808251SPyun YongHyeon 	for (i = 0; i < STGE_TX_RING_CNT; i++) {
236857808251SPyun YongHyeon 		if (i == (STGE_TX_RING_CNT - 1))
236957808251SPyun YongHyeon 			addr = STGE_TX_RING_ADDR(sc, 0);
237057808251SPyun YongHyeon 		else
237157808251SPyun YongHyeon 			addr = STGE_TX_RING_ADDR(sc, i + 1);
237257808251SPyun YongHyeon 		rd->stge_tx_ring[i].tfd_next = htole64(addr);
237357808251SPyun YongHyeon 		rd->stge_tx_ring[i].tfd_control = htole64(TFD_TFDDone);
237457808251SPyun YongHyeon 		txd = &sc->sc_cdata.stge_txdesc[i];
237557808251SPyun YongHyeon 		STAILQ_INSERT_TAIL(&sc->sc_cdata.stge_txfreeq, txd, tx_q);
237657808251SPyun YongHyeon 	}
237757808251SPyun YongHyeon 
237857808251SPyun YongHyeon 	bus_dmamap_sync(sc->sc_cdata.stge_tx_ring_tag,
237957808251SPyun YongHyeon 	    sc->sc_cdata.stge_tx_ring_map,
238057808251SPyun YongHyeon 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
238157808251SPyun YongHyeon 
238257808251SPyun YongHyeon }
238357808251SPyun YongHyeon 
238457808251SPyun YongHyeon static int
stge_init_rx_ring(struct stge_softc * sc)238557808251SPyun YongHyeon stge_init_rx_ring(struct stge_softc *sc)
238657808251SPyun YongHyeon {
238757808251SPyun YongHyeon 	struct stge_ring_data *rd;
238857808251SPyun YongHyeon 	bus_addr_t addr;
238957808251SPyun YongHyeon 	int i;
239057808251SPyun YongHyeon 
239157808251SPyun YongHyeon 	sc->sc_cdata.stge_rx_cons = 0;
239257808251SPyun YongHyeon 	STGE_RXCHAIN_RESET(sc);
239357808251SPyun YongHyeon 
239457808251SPyun YongHyeon 	rd = &sc->sc_rdata;
239557808251SPyun YongHyeon 	bzero(rd->stge_rx_ring, STGE_RX_RING_SZ);
239657808251SPyun YongHyeon 	for (i = 0; i < STGE_RX_RING_CNT; i++) {
239757808251SPyun YongHyeon 		if (stge_newbuf(sc, i) != 0)
239857808251SPyun YongHyeon 			return (ENOBUFS);
239957808251SPyun YongHyeon 		if (i == (STGE_RX_RING_CNT - 1))
240057808251SPyun YongHyeon 			addr = STGE_RX_RING_ADDR(sc, 0);
240157808251SPyun YongHyeon 		else
240257808251SPyun YongHyeon 			addr = STGE_RX_RING_ADDR(sc, i + 1);
240357808251SPyun YongHyeon 		rd->stge_rx_ring[i].rfd_next = htole64(addr);
240457808251SPyun YongHyeon 		rd->stge_rx_ring[i].rfd_status = 0;
240557808251SPyun YongHyeon 	}
240657808251SPyun YongHyeon 
240757808251SPyun YongHyeon 	bus_dmamap_sync(sc->sc_cdata.stge_rx_ring_tag,
240857808251SPyun YongHyeon 	    sc->sc_cdata.stge_rx_ring_map,
240957808251SPyun YongHyeon 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
241057808251SPyun YongHyeon 
241157808251SPyun YongHyeon 	return (0);
241257808251SPyun YongHyeon }
241357808251SPyun YongHyeon 
241457808251SPyun YongHyeon /*
241557808251SPyun YongHyeon  * stge_newbuf:
241657808251SPyun YongHyeon  *
241757808251SPyun YongHyeon  *	Add a receive buffer to the indicated descriptor.
241857808251SPyun YongHyeon  */
241957808251SPyun YongHyeon static int
stge_newbuf(struct stge_softc * sc,int idx)242057808251SPyun YongHyeon stge_newbuf(struct stge_softc *sc, int idx)
242157808251SPyun YongHyeon {
242257808251SPyun YongHyeon 	struct stge_rxdesc *rxd;
242357808251SPyun YongHyeon 	struct stge_rfd *rfd;
242457808251SPyun YongHyeon 	struct mbuf *m;
242557808251SPyun YongHyeon 	bus_dma_segment_t segs[1];
242657808251SPyun YongHyeon 	bus_dmamap_t map;
242757808251SPyun YongHyeon 	int nsegs;
242857808251SPyun YongHyeon 
2429c6499eccSGleb Smirnoff 	m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
243057808251SPyun YongHyeon 	if (m == NULL)
243157808251SPyun YongHyeon 		return (ENOBUFS);
243257808251SPyun YongHyeon 	m->m_len = m->m_pkthdr.len = MCLBYTES;
243357808251SPyun YongHyeon 	/*
243457808251SPyun YongHyeon 	 * The hardware requires 4bytes aligned DMA address when JUMBO
243557808251SPyun YongHyeon 	 * frame is used.
243657808251SPyun YongHyeon 	 */
243757808251SPyun YongHyeon 	if (sc->sc_if_framesize <= (MCLBYTES - ETHER_ALIGN))
243857808251SPyun YongHyeon 		m_adj(m, ETHER_ALIGN);
243957808251SPyun YongHyeon 
244057808251SPyun YongHyeon 	if (bus_dmamap_load_mbuf_sg(sc->sc_cdata.stge_rx_tag,
244157808251SPyun YongHyeon 	    sc->sc_cdata.stge_rx_sparemap, m, segs, &nsegs, 0) != 0) {
244257808251SPyun YongHyeon 		m_freem(m);
244357808251SPyun YongHyeon 		return (ENOBUFS);
244457808251SPyun YongHyeon 	}
244557808251SPyun YongHyeon 	KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
244657808251SPyun YongHyeon 
244757808251SPyun YongHyeon 	rxd = &sc->sc_cdata.stge_rxdesc[idx];
244857808251SPyun YongHyeon 	if (rxd->rx_m != NULL) {
244957808251SPyun YongHyeon 		bus_dmamap_sync(sc->sc_cdata.stge_rx_tag, rxd->rx_dmamap,
245057808251SPyun YongHyeon 		    BUS_DMASYNC_POSTREAD);
245157808251SPyun YongHyeon 		bus_dmamap_unload(sc->sc_cdata.stge_rx_tag, rxd->rx_dmamap);
245257808251SPyun YongHyeon 	}
245357808251SPyun YongHyeon 	map = rxd->rx_dmamap;
245457808251SPyun YongHyeon 	rxd->rx_dmamap = sc->sc_cdata.stge_rx_sparemap;
245557808251SPyun YongHyeon 	sc->sc_cdata.stge_rx_sparemap = map;
245657808251SPyun YongHyeon 	bus_dmamap_sync(sc->sc_cdata.stge_rx_tag, rxd->rx_dmamap,
245757808251SPyun YongHyeon 	    BUS_DMASYNC_PREREAD);
245857808251SPyun YongHyeon 	rxd->rx_m = m;
245957808251SPyun YongHyeon 
246057808251SPyun YongHyeon 	rfd = &sc->sc_rdata.stge_rx_ring[idx];
246157808251SPyun YongHyeon 	rfd->rfd_frag.frag_word0 =
246257808251SPyun YongHyeon 	    htole64(FRAG_ADDR(segs[0].ds_addr) | FRAG_LEN(segs[0].ds_len));
246357808251SPyun YongHyeon 	rfd->rfd_status = 0;
246457808251SPyun YongHyeon 
246557808251SPyun YongHyeon 	return (0);
246657808251SPyun YongHyeon }
246757808251SPyun YongHyeon 
246857808251SPyun YongHyeon /*
246957808251SPyun YongHyeon  * stge_set_filter:
247057808251SPyun YongHyeon  *
247157808251SPyun YongHyeon  *	Set up the receive filter.
247257808251SPyun YongHyeon  */
247357808251SPyun YongHyeon static void
stge_set_filter(struct stge_softc * sc)247457808251SPyun YongHyeon stge_set_filter(struct stge_softc *sc)
247557808251SPyun YongHyeon {
247608ca347fSJustin Hibbits 	if_t ifp;
247757808251SPyun YongHyeon 	uint16_t mode;
247857808251SPyun YongHyeon 
247957808251SPyun YongHyeon 	STGE_LOCK_ASSERT(sc);
248057808251SPyun YongHyeon 
248157808251SPyun YongHyeon 	ifp = sc->sc_ifp;
248257808251SPyun YongHyeon 
248357808251SPyun YongHyeon 	mode = CSR_READ_2(sc, STGE_ReceiveMode);
248457808251SPyun YongHyeon 	mode |= RM_ReceiveUnicast;
248508ca347fSJustin Hibbits 	if ((if_getflags(ifp) & IFF_BROADCAST) != 0)
248657808251SPyun YongHyeon 		mode |= RM_ReceiveBroadcast;
248757808251SPyun YongHyeon 	else
248857808251SPyun YongHyeon 		mode &= ~RM_ReceiveBroadcast;
248908ca347fSJustin Hibbits 	if ((if_getflags(ifp) & IFF_PROMISC) != 0)
249057808251SPyun YongHyeon 		mode |= RM_ReceiveAllFrames;
249157808251SPyun YongHyeon 	else
249257808251SPyun YongHyeon 		mode &= ~RM_ReceiveAllFrames;
249357808251SPyun YongHyeon 
249457808251SPyun YongHyeon 	CSR_WRITE_2(sc, STGE_ReceiveMode, mode);
249557808251SPyun YongHyeon }
249657808251SPyun YongHyeon 
2497e9c5104fSGleb Smirnoff static u_int
stge_hash_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)2498e9c5104fSGleb Smirnoff stge_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
2499e9c5104fSGleb Smirnoff {
2500e9c5104fSGleb Smirnoff 	uint32_t crc, *mchash = arg;
2501e9c5104fSGleb Smirnoff 
2502e9c5104fSGleb Smirnoff 	crc = ether_crc32_be(LLADDR(sdl), ETHER_ADDR_LEN);
2503e9c5104fSGleb Smirnoff 	/* Just want the 6 least significant bits. */
2504e9c5104fSGleb Smirnoff 	crc &= 0x3f;
2505e9c5104fSGleb Smirnoff 	/* Set the corresponding bit in the hash table. */
2506e9c5104fSGleb Smirnoff 	mchash[crc >> 5] |= 1 << (crc & 0x1f);
2507e9c5104fSGleb Smirnoff 
2508e9c5104fSGleb Smirnoff 	return (1);
2509e9c5104fSGleb Smirnoff }
2510e9c5104fSGleb Smirnoff 
251157808251SPyun YongHyeon static void
stge_set_multi(struct stge_softc * sc)251257808251SPyun YongHyeon stge_set_multi(struct stge_softc *sc)
251357808251SPyun YongHyeon {
251408ca347fSJustin Hibbits 	if_t ifp;
251557808251SPyun YongHyeon 	uint32_t mchash[2];
251657808251SPyun YongHyeon 	uint16_t mode;
251757808251SPyun YongHyeon 	int count;
251857808251SPyun YongHyeon 
251957808251SPyun YongHyeon 	STGE_LOCK_ASSERT(sc);
252057808251SPyun YongHyeon 
252157808251SPyun YongHyeon 	ifp = sc->sc_ifp;
252257808251SPyun YongHyeon 
252357808251SPyun YongHyeon 	mode = CSR_READ_2(sc, STGE_ReceiveMode);
252408ca347fSJustin Hibbits 	if ((if_getflags(ifp) & (IFF_PROMISC | IFF_ALLMULTI)) != 0) {
252508ca347fSJustin Hibbits 		if ((if_getflags(ifp) & IFF_PROMISC) != 0)
252657808251SPyun YongHyeon 			mode |= RM_ReceiveAllFrames;
252708ca347fSJustin Hibbits 		else if ((if_getflags(ifp) & IFF_ALLMULTI) != 0)
252857808251SPyun YongHyeon 			mode |= RM_ReceiveMulticast;
252957808251SPyun YongHyeon 		CSR_WRITE_2(sc, STGE_ReceiveMode, mode);
253057808251SPyun YongHyeon 		return;
253157808251SPyun YongHyeon 	}
253257808251SPyun YongHyeon 
253357808251SPyun YongHyeon 	/* clear existing filters. */
253457808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_HashTable0, 0);
253557808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_HashTable1, 0);
253657808251SPyun YongHyeon 
253757808251SPyun YongHyeon 	/*
253857808251SPyun YongHyeon 	 * Set up the multicast address filter by passing all multicast
253957808251SPyun YongHyeon 	 * addresses through a CRC generator, and then using the low-order
254057808251SPyun YongHyeon 	 * 6 bits as an index into the 64 bit multicast hash table.  The
254157808251SPyun YongHyeon 	 * high order bits select the register, while the rest of the bits
254257808251SPyun YongHyeon 	 * select the bit within the register.
254357808251SPyun YongHyeon 	 */
254457808251SPyun YongHyeon 	bzero(mchash, sizeof(mchash));
2545e9c5104fSGleb Smirnoff 	count = if_foreach_llmaddr(ifp, stge_hash_maddr, mchash);
254657808251SPyun YongHyeon 
254757808251SPyun YongHyeon 	mode &= ~(RM_ReceiveMulticast | RM_ReceiveAllFrames);
254857808251SPyun YongHyeon 	if (count > 0)
254957808251SPyun YongHyeon 		mode |= RM_ReceiveMulticastHash;
255057808251SPyun YongHyeon 	else
255157808251SPyun YongHyeon 		mode &= ~RM_ReceiveMulticastHash;
255257808251SPyun YongHyeon 
255357808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_HashTable0, mchash[0]);
255457808251SPyun YongHyeon 	CSR_WRITE_4(sc, STGE_HashTable1, mchash[1]);
255557808251SPyun YongHyeon 	CSR_WRITE_2(sc, STGE_ReceiveMode, mode);
255657808251SPyun YongHyeon }
255757808251SPyun YongHyeon 
255857808251SPyun YongHyeon static int
sysctl_int_range(SYSCTL_HANDLER_ARGS,int low,int high)255957808251SPyun YongHyeon sysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high)
256057808251SPyun YongHyeon {
256157808251SPyun YongHyeon 	int error, value;
256257808251SPyun YongHyeon 
256357808251SPyun YongHyeon 	if (!arg1)
256457808251SPyun YongHyeon 		return (EINVAL);
256557808251SPyun YongHyeon 	value = *(int *)arg1;
256657808251SPyun YongHyeon 	error = sysctl_handle_int(oidp, &value, 0, req);
256757808251SPyun YongHyeon 	if (error || !req->newptr)
256857808251SPyun YongHyeon 		return (error);
256957808251SPyun YongHyeon 	if (value < low || value > high)
257057808251SPyun YongHyeon 		return (EINVAL);
257157808251SPyun YongHyeon         *(int *)arg1 = value;
257257808251SPyun YongHyeon 
257357808251SPyun YongHyeon         return (0);
257457808251SPyun YongHyeon }
257557808251SPyun YongHyeon 
257657808251SPyun YongHyeon static int
sysctl_hw_stge_rxint_nframe(SYSCTL_HANDLER_ARGS)257757808251SPyun YongHyeon sysctl_hw_stge_rxint_nframe(SYSCTL_HANDLER_ARGS)
257857808251SPyun YongHyeon {
257957808251SPyun YongHyeon 	return (sysctl_int_range(oidp, arg1, arg2, req,
258057808251SPyun YongHyeon 	    STGE_RXINT_NFRAME_MIN, STGE_RXINT_NFRAME_MAX));
258157808251SPyun YongHyeon }
258257808251SPyun YongHyeon 
258357808251SPyun YongHyeon static int
sysctl_hw_stge_rxint_dmawait(SYSCTL_HANDLER_ARGS)258457808251SPyun YongHyeon sysctl_hw_stge_rxint_dmawait(SYSCTL_HANDLER_ARGS)
258557808251SPyun YongHyeon {
258657808251SPyun YongHyeon 	return (sysctl_int_range(oidp, arg1, arg2, req,
258757808251SPyun YongHyeon 	    STGE_RXINT_DMAWAIT_MIN, STGE_RXINT_DMAWAIT_MAX));
258857808251SPyun YongHyeon }
2589