xref: /freebsd-src/sys/dev/bfe/if_bfe.c (revision b196276c20b577b364372f1aa1a646b9ce34bf5c)
1098ca2bdSWarner Losh /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
4b9f78d2bSBill Paul  * Copyright (c) 2003 Stuart Walsh<stu@ipng.org.uk>
5b9f78d2bSBill Paul  * and Duncan Barclay<dmlb@dmlb.org>
6098ca2bdSWarner Losh  *
7b9f78d2bSBill Paul  * Redistribution and use in source and binary forms, with or without
8b9f78d2bSBill Paul  * modification, are permitted provided that the following conditions
9b9f78d2bSBill Paul  * are met:
10b9f78d2bSBill Paul  * 1. Redistributions of source code must retain the above copyright
11b9f78d2bSBill Paul  *    notice, this list of conditions and the following disclaimer.
12b9f78d2bSBill Paul  * 2. Redistributions in binary form must reproduce the above copyright
13b9f78d2bSBill Paul  *    notice, this list of conditions and the following disclaimer in the
14b9f78d2bSBill Paul  *    documentation and/or other materials provided with the distribution.
15b9f78d2bSBill Paul  *
16b9f78d2bSBill Paul  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS 'AS IS' AND
17b9f78d2bSBill Paul  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18b9f78d2bSBill Paul  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19b9f78d2bSBill Paul  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20b9f78d2bSBill Paul  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21b9f78d2bSBill Paul  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22b9f78d2bSBill Paul  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23b9f78d2bSBill Paul  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24b9f78d2bSBill Paul  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25b9f78d2bSBill Paul  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26b9f78d2bSBill Paul  * SUCH DAMAGE.
27b9f78d2bSBill Paul  */
28b9f78d2bSBill Paul 
29b9f78d2bSBill Paul #include <sys/param.h>
30b9f78d2bSBill Paul #include <sys/systm.h>
3196ee09c5SPyun YongHyeon #include <sys/bus.h>
3296ee09c5SPyun YongHyeon #include <sys/endian.h>
33b9f78d2bSBill Paul #include <sys/kernel.h>
3496ee09c5SPyun YongHyeon #include <sys/malloc.h>
3596ee09c5SPyun YongHyeon #include <sys/mbuf.h>
36fe12f24bSPoul-Henning Kamp #include <sys/module.h>
3796ee09c5SPyun YongHyeon #include <sys/rman.h>
38b9f78d2bSBill Paul #include <sys/socket.h>
3996ee09c5SPyun YongHyeon #include <sys/sockio.h>
40861cf54cSPyun YongHyeon #include <sys/sysctl.h>
41b9f78d2bSBill Paul 
4296ee09c5SPyun YongHyeon #include <net/bpf.h>
43b9f78d2bSBill Paul #include <net/if.h>
4476039bc8SGleb Smirnoff #include <net/if_var.h>
45b9f78d2bSBill Paul #include <net/ethernet.h>
46b9f78d2bSBill Paul #include <net/if_dl.h>
47b9f78d2bSBill Paul #include <net/if_media.h>
48b9f78d2bSBill Paul #include <net/if_types.h>
49b9f78d2bSBill Paul #include <net/if_vlan_var.h>
50b9f78d2bSBill Paul 
51b9f78d2bSBill Paul #include <dev/mii/mii.h>
52b9f78d2bSBill Paul #include <dev/mii/miivar.h>
53b9f78d2bSBill Paul 
54b9f78d2bSBill Paul #include <dev/pci/pcireg.h>
55b9f78d2bSBill Paul #include <dev/pci/pcivar.h>
56b9f78d2bSBill Paul 
5796ee09c5SPyun YongHyeon #include <machine/bus.h>
5896ee09c5SPyun YongHyeon 
59b9f78d2bSBill Paul #include <dev/bfe/if_bfereg.h>
60b9f78d2bSBill Paul 
61b9f78d2bSBill Paul MODULE_DEPEND(bfe, pci, 1, 1, 1);
62b9f78d2bSBill Paul MODULE_DEPEND(bfe, ether, 1, 1, 1);
63b9f78d2bSBill Paul MODULE_DEPEND(bfe, miibus, 1, 1, 1);
64b9f78d2bSBill Paul 
657b279558SWarner Losh /* "device miibus" required.  See GENERIC if you get errors here. */
66b9f78d2bSBill Paul #include "miibus_if.h"
67b9f78d2bSBill Paul 
68b9f78d2bSBill Paul #define BFE_DEVDESC_MAX		64	/* Maximum device description length */
69b9f78d2bSBill Paul 
70b9f78d2bSBill Paul static struct bfe_type bfe_devs[] = {
71b9f78d2bSBill Paul 	{ BCOM_VENDORID, BCOM_DEVICEID_BCM4401,
72b9f78d2bSBill Paul 		"Broadcom BCM4401 Fast Ethernet" },
73037fc73dSDag-Erling Smørgrav 	{ BCOM_VENDORID, BCOM_DEVICEID_BCM4401B0,
74037fc73dSDag-Erling Smørgrav 		"Broadcom BCM4401-B0 Fast Ethernet" },
75b9f78d2bSBill Paul 		{ 0, 0, NULL }
76b9f78d2bSBill Paul };
77b9f78d2bSBill Paul 
78b9f78d2bSBill Paul static int  bfe_probe				(device_t);
79b9f78d2bSBill Paul static int  bfe_attach				(device_t);
80b9f78d2bSBill Paul static int  bfe_detach				(device_t);
816daee769SJohn Baldwin static int  bfe_suspend				(device_t);
826daee769SJohn Baldwin static int  bfe_resume				(device_t);
83b9f78d2bSBill Paul static void bfe_release_resources	(struct bfe_softc *);
84b9f78d2bSBill Paul static void bfe_intr				(void *);
8596ee09c5SPyun YongHyeon static int  bfe_encap				(struct bfe_softc *, struct mbuf **);
86c0e5e270SJustin Hibbits static void bfe_start				(if_t);
87c0e5e270SJustin Hibbits static void bfe_start_locked			(if_t);
88c0e5e270SJustin Hibbits static int  bfe_ioctl				(if_t, u_long, caddr_t);
89b9f78d2bSBill Paul static void bfe_init				(void *);
90f16b4811SMike Makonnen static void bfe_init_locked			(void *);
91b9f78d2bSBill Paul static void bfe_stop				(struct bfe_softc *);
926ceb40baSPyun YongHyeon static void bfe_watchdog			(struct bfe_softc *);
936a087a87SPyun YongHyeon static int  bfe_shutdown			(device_t);
94b9f78d2bSBill Paul static void bfe_tick				(void *);
95b9f78d2bSBill Paul static void bfe_txeof				(struct bfe_softc *);
96b9f78d2bSBill Paul static void bfe_rxeof				(struct bfe_softc *);
97b9f78d2bSBill Paul static void bfe_set_rx_mode			(struct bfe_softc *);
98b9f78d2bSBill Paul static int  bfe_list_rx_init		(struct bfe_softc *);
9996ee09c5SPyun YongHyeon static void bfe_list_tx_init		(struct bfe_softc *);
10096ee09c5SPyun YongHyeon static void bfe_discard_buf		(struct bfe_softc *, int);
10196ee09c5SPyun YongHyeon static int  bfe_list_newbuf			(struct bfe_softc *, int);
102b9f78d2bSBill Paul static void bfe_rx_ring_free		(struct bfe_softc *);
103b9f78d2bSBill Paul 
104b9f78d2bSBill Paul static void bfe_pci_setup			(struct bfe_softc *, u_int32_t);
105c0e5e270SJustin Hibbits static int  bfe_ifmedia_upd			(if_t);
106c0e5e270SJustin Hibbits static void bfe_ifmedia_sts			(if_t, struct ifmediareq *);
107b9f78d2bSBill Paul static int  bfe_miibus_readreg		(device_t, int, int);
108b9f78d2bSBill Paul static int  bfe_miibus_writereg		(device_t, int, int, int);
109b9f78d2bSBill Paul static void bfe_miibus_statchg		(device_t);
110b9f78d2bSBill Paul static int  bfe_wait_bit			(struct bfe_softc *, u_int32_t, u_int32_t,
111b9f78d2bSBill Paul 		u_long, const int);
112b9f78d2bSBill Paul static void bfe_get_config			(struct bfe_softc *sc);
113b9f78d2bSBill Paul static void bfe_read_eeprom			(struct bfe_softc *, u_int8_t *);
114b9f78d2bSBill Paul static void bfe_stats_update		(struct bfe_softc *);
115b9f78d2bSBill Paul static void bfe_clear_stats			(struct bfe_softc *);
116b9f78d2bSBill Paul static int  bfe_readphy				(struct bfe_softc *, u_int32_t, u_int32_t*);
117b9f78d2bSBill Paul static int  bfe_writephy			(struct bfe_softc *, u_int32_t, u_int32_t);
118b9f78d2bSBill Paul static int  bfe_resetphy			(struct bfe_softc *);
119b9f78d2bSBill Paul static int  bfe_setupphy			(struct bfe_softc *);
120b9f78d2bSBill Paul static void bfe_chip_reset			(struct bfe_softc *);
121b9f78d2bSBill Paul static void bfe_chip_halt			(struct bfe_softc *);
122b9f78d2bSBill Paul static void bfe_core_reset			(struct bfe_softc *);
123b9f78d2bSBill Paul static void bfe_core_disable		(struct bfe_softc *);
12496ee09c5SPyun YongHyeon static int  bfe_dma_alloc			(struct bfe_softc *);
12596ee09c5SPyun YongHyeon static void bfe_dma_free		(struct bfe_softc *sc);
126b9f78d2bSBill Paul static void bfe_dma_map				(void *, bus_dma_segment_t *, int, int);
127b9f78d2bSBill Paul static void bfe_cam_write			(struct bfe_softc *, u_char *, int);
128861cf54cSPyun YongHyeon static int  sysctl_bfe_stats		(SYSCTL_HANDLER_ARGS);
129b9f78d2bSBill Paul 
130b9f78d2bSBill Paul static device_method_t bfe_methods[] = {
131b9f78d2bSBill Paul 	/* Device interface */
132b9f78d2bSBill Paul 	DEVMETHOD(device_probe,		bfe_probe),
133b9f78d2bSBill Paul 	DEVMETHOD(device_attach,	bfe_attach),
134b9f78d2bSBill Paul 	DEVMETHOD(device_detach,	bfe_detach),
135b9f78d2bSBill Paul 	DEVMETHOD(device_shutdown,	bfe_shutdown),
1366daee769SJohn Baldwin 	DEVMETHOD(device_suspend,	bfe_suspend),
1376daee769SJohn Baldwin 	DEVMETHOD(device_resume,	bfe_resume),
138b9f78d2bSBill Paul 
139b9f78d2bSBill Paul 	/* MII interface */
140b9f78d2bSBill Paul 	DEVMETHOD(miibus_readreg,	bfe_miibus_readreg),
141b9f78d2bSBill Paul 	DEVMETHOD(miibus_writereg,	bfe_miibus_writereg),
142b9f78d2bSBill Paul 	DEVMETHOD(miibus_statchg,	bfe_miibus_statchg),
143b9f78d2bSBill Paul 
1444b7ec270SMarius Strobl 	DEVMETHOD_END
145b9f78d2bSBill Paul };
146b9f78d2bSBill Paul 
147b9f78d2bSBill Paul static driver_t bfe_driver = {
148b9f78d2bSBill Paul 	"bfe",
149b9f78d2bSBill Paul 	bfe_methods,
150b9f78d2bSBill Paul 	sizeof(struct bfe_softc)
151b9f78d2bSBill Paul };
152b9f78d2bSBill Paul 
15348ebc940SJohn Baldwin DRIVER_MODULE(bfe, pci, bfe_driver, 0, 0);
1546c15b8d9SWarner Losh MODULE_PNP_INFO("U16:vendor;U16:device;D:#", pci, bfe, bfe_devs,
155329e817fSWarner Losh     nitems(bfe_devs) - 1);
1563e38757dSJohn Baldwin DRIVER_MODULE(miibus, bfe, miibus_driver, 0, 0);
157b9f78d2bSBill Paul 
158b9f78d2bSBill Paul /*
159b9f78d2bSBill Paul  * Probe for a Broadcom 4401 chip.
160b9f78d2bSBill Paul  */
161b9f78d2bSBill Paul static int
162b9f78d2bSBill Paul bfe_probe(device_t dev)
163b9f78d2bSBill Paul {
164b9f78d2bSBill Paul 	struct bfe_type *t;
165b9f78d2bSBill Paul 
166b9f78d2bSBill Paul 	t = bfe_devs;
167b9f78d2bSBill Paul 
168b9f78d2bSBill Paul 	while (t->bfe_name != NULL) {
169a73a4056SPyun YongHyeon 		if (pci_get_vendor(dev) == t->bfe_vid &&
170a73a4056SPyun YongHyeon 		    pci_get_device(dev) == t->bfe_did) {
171a823cbc6SPyun YongHyeon 			device_set_desc(dev, t->bfe_name);
17253ee7173SWarner Losh 			return (BUS_PROBE_DEFAULT);
173b9f78d2bSBill Paul 		}
174b9f78d2bSBill Paul 		t++;
175b9f78d2bSBill Paul 	}
176b9f78d2bSBill Paul 
177b9f78d2bSBill Paul 	return (ENXIO);
178b9f78d2bSBill Paul }
179b9f78d2bSBill Paul 
18096ee09c5SPyun YongHyeon struct bfe_dmamap_arg {
18196ee09c5SPyun YongHyeon 	bus_addr_t	bfe_busaddr;
18296ee09c5SPyun YongHyeon };
183b9f78d2bSBill Paul 
18496ee09c5SPyun YongHyeon static int
18596ee09c5SPyun YongHyeon bfe_dma_alloc(struct bfe_softc *sc)
18696ee09c5SPyun YongHyeon {
18796ee09c5SPyun YongHyeon 	struct bfe_dmamap_arg ctx;
18896ee09c5SPyun YongHyeon 	struct bfe_rx_data *rd;
18996ee09c5SPyun YongHyeon 	struct bfe_tx_data *td;
19096ee09c5SPyun YongHyeon 	int error, i;
191b9f78d2bSBill Paul 
1929171a12bSScott Long 	/*
1939171a12bSScott Long 	 * parent tag.  Apparently the chip cannot handle any DMA address
1949171a12bSScott Long 	 * greater than 1GB.
1959171a12bSScott Long 	 */
19696ee09c5SPyun YongHyeon 	error = bus_dma_tag_create(bus_get_dma_tag(sc->bfe_dev), /* parent */
19796ee09c5SPyun YongHyeon 	    1, 0,			/* alignment, boundary */
19896ee09c5SPyun YongHyeon 	    BFE_DMA_MAXADDR, 		/* lowaddr */
1999171a12bSScott Long 	    BUS_SPACE_MAXADDR,		/* highaddr */
200b9f78d2bSBill Paul 	    NULL, NULL,			/* filter, filterarg */
20196ee09c5SPyun YongHyeon 	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsize */
20296ee09c5SPyun YongHyeon 	    0,				/* nsegments */
20396ee09c5SPyun YongHyeon 	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
2043981b687SMike Silbersack 	    0,				/* flags */
205b9f78d2bSBill Paul 	    NULL, NULL,			/* lockfunc, lockarg */
206b9f78d2bSBill Paul 	    &sc->bfe_parent_tag);
20796ee09c5SPyun YongHyeon 	if (error != 0) {
20896ee09c5SPyun YongHyeon 		device_printf(sc->bfe_dev, "cannot create parent DMA tag.\n");
20996ee09c5SPyun YongHyeon 		goto fail;
21096ee09c5SPyun YongHyeon 	}
211b9f78d2bSBill Paul 
21296ee09c5SPyun YongHyeon 	/* Create tag for Tx ring. */
21396ee09c5SPyun YongHyeon 	error = bus_dma_tag_create(sc->bfe_parent_tag, /* parent */
21496ee09c5SPyun YongHyeon 	    BFE_TX_RING_ALIGN, 0,	/* alignment, boundary */
21596ee09c5SPyun YongHyeon 	    BUS_SPACE_MAXADDR, 		/* lowaddr */
21696ee09c5SPyun YongHyeon 	    BUS_SPACE_MAXADDR,		/* highaddr */
21796ee09c5SPyun YongHyeon 	    NULL, NULL,			/* filter, filterarg */
21896ee09c5SPyun YongHyeon 	    BFE_TX_LIST_SIZE,		/* maxsize */
21996ee09c5SPyun YongHyeon 	    1,				/* nsegments */
22096ee09c5SPyun YongHyeon 	    BFE_TX_LIST_SIZE,		/* maxsegsize */
22196ee09c5SPyun YongHyeon 	    0,				/* flags */
22296ee09c5SPyun YongHyeon 	    NULL, NULL,			/* lockfunc, lockarg */
223f2b1c158SJulian Elischer 	    &sc->bfe_tx_tag);
22496ee09c5SPyun YongHyeon 	if (error != 0) {
22596ee09c5SPyun YongHyeon 		device_printf(sc->bfe_dev, "cannot create Tx ring DMA tag.\n");
22696ee09c5SPyun YongHyeon 		goto fail;
227b9f78d2bSBill Paul 	}
228b9f78d2bSBill Paul 
22996ee09c5SPyun YongHyeon 	/* Create tag for Rx ring. */
23096ee09c5SPyun YongHyeon 	error = bus_dma_tag_create(sc->bfe_parent_tag, /* parent */
23196ee09c5SPyun YongHyeon 	    BFE_RX_RING_ALIGN, 0,	/* alignment, boundary */
23296ee09c5SPyun YongHyeon 	    BUS_SPACE_MAXADDR, 		/* lowaddr */
23396ee09c5SPyun YongHyeon 	    BUS_SPACE_MAXADDR,		/* highaddr */
23496ee09c5SPyun YongHyeon 	    NULL, NULL,			/* filter, filterarg */
23596ee09c5SPyun YongHyeon 	    BFE_RX_LIST_SIZE,		/* maxsize */
23696ee09c5SPyun YongHyeon 	    1,				/* nsegments */
23796ee09c5SPyun YongHyeon 	    BFE_RX_LIST_SIZE,		/* maxsegsize */
23896ee09c5SPyun YongHyeon 	    0,				/* flags */
23996ee09c5SPyun YongHyeon 	    NULL, NULL,			/* lockfunc, lockarg */
240f2b1c158SJulian Elischer 	    &sc->bfe_rx_tag);
24196ee09c5SPyun YongHyeon 	if (error != 0) {
24296ee09c5SPyun YongHyeon 		device_printf(sc->bfe_dev, "cannot create Rx ring DMA tag.\n");
24396ee09c5SPyun YongHyeon 		goto fail;
244b9f78d2bSBill Paul 	}
245b9f78d2bSBill Paul 
24696ee09c5SPyun YongHyeon 	/* Create tag for Tx buffers. */
24796ee09c5SPyun YongHyeon 	error = bus_dma_tag_create(sc->bfe_parent_tag, /* parent */
24896ee09c5SPyun YongHyeon 	    1, 0,			/* alignment, boundary */
24996ee09c5SPyun YongHyeon 	    BUS_SPACE_MAXADDR, 		/* lowaddr */
25096ee09c5SPyun YongHyeon 	    BUS_SPACE_MAXADDR,		/* highaddr */
25196ee09c5SPyun YongHyeon 	    NULL, NULL,			/* filter, filterarg */
25296ee09c5SPyun YongHyeon 	    MCLBYTES * BFE_MAXTXSEGS,	/* maxsize */
25396ee09c5SPyun YongHyeon 	    BFE_MAXTXSEGS,		/* nsegments */
25496ee09c5SPyun YongHyeon 	    MCLBYTES,			/* maxsegsize */
25596ee09c5SPyun YongHyeon 	    0,				/* flags */
25696ee09c5SPyun YongHyeon 	    NULL, NULL,			/* lockfunc, lockarg */
25796ee09c5SPyun YongHyeon 	    &sc->bfe_txmbuf_tag);
25896ee09c5SPyun YongHyeon 	if (error != 0) {
25996ee09c5SPyun YongHyeon 		device_printf(sc->bfe_dev,
26096ee09c5SPyun YongHyeon 		    "cannot create Tx buffer DMA tag.\n");
26196ee09c5SPyun YongHyeon 		goto fail;
262b9f78d2bSBill Paul 	}
263b9f78d2bSBill Paul 
26496ee09c5SPyun YongHyeon 	/* Create tag for Rx buffers. */
26596ee09c5SPyun YongHyeon 	error = bus_dma_tag_create(sc->bfe_parent_tag, /* parent */
26696ee09c5SPyun YongHyeon 	    1, 0,			/* alignment, boundary */
26796ee09c5SPyun YongHyeon 	    BUS_SPACE_MAXADDR, 		/* lowaddr */
26896ee09c5SPyun YongHyeon 	    BUS_SPACE_MAXADDR,		/* highaddr */
26996ee09c5SPyun YongHyeon 	    NULL, NULL,			/* filter, filterarg */
27096ee09c5SPyun YongHyeon 	    MCLBYTES,			/* maxsize */
27196ee09c5SPyun YongHyeon 	    1,				/* nsegments */
27296ee09c5SPyun YongHyeon 	    MCLBYTES,			/* maxsegsize */
27396ee09c5SPyun YongHyeon 	    0,				/* flags */
27496ee09c5SPyun YongHyeon 	    NULL, NULL,			/* lockfunc, lockarg */
27596ee09c5SPyun YongHyeon 	    &sc->bfe_rxmbuf_tag);
27696ee09c5SPyun YongHyeon 	if (error != 0) {
27796ee09c5SPyun YongHyeon 		device_printf(sc->bfe_dev,
27896ee09c5SPyun YongHyeon 		    "cannot create Rx buffer DMA tag.\n");
27996ee09c5SPyun YongHyeon 		goto fail;
280b9f78d2bSBill Paul 	}
281b9f78d2bSBill Paul 
28296ee09c5SPyun YongHyeon 	/* Allocate DMA'able memory and load DMA map. */
283b9f78d2bSBill Paul 	error = bus_dmamem_alloc(sc->bfe_tx_tag, (void *)&sc->bfe_tx_list,
28496ee09c5SPyun YongHyeon 	  BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->bfe_tx_map);
28596ee09c5SPyun YongHyeon 	if (error != 0) {
28696ee09c5SPyun YongHyeon 		device_printf(sc->bfe_dev,
28796ee09c5SPyun YongHyeon 		    "cannot allocate DMA'able memory for Tx ring.\n");
28896ee09c5SPyun YongHyeon 		goto fail;
28996ee09c5SPyun YongHyeon 	}
29096ee09c5SPyun YongHyeon 	ctx.bfe_busaddr = 0;
291b9f78d2bSBill Paul 	error = bus_dmamap_load(sc->bfe_tx_tag, sc->bfe_tx_map,
29296ee09c5SPyun YongHyeon 	    sc->bfe_tx_list, BFE_TX_LIST_SIZE, bfe_dma_map, &ctx,
29396ee09c5SPyun YongHyeon 	    BUS_DMA_NOWAIT);
29496ee09c5SPyun YongHyeon 	if (error != 0 || ctx.bfe_busaddr == 0) {
29596ee09c5SPyun YongHyeon 		device_printf(sc->bfe_dev,
29696ee09c5SPyun YongHyeon 		    "cannot load DMA'able memory for Tx ring.\n");
29796ee09c5SPyun YongHyeon 		goto fail;
29896ee09c5SPyun YongHyeon 	}
29996ee09c5SPyun YongHyeon 	sc->bfe_tx_dma = BFE_ADDR_LO(ctx.bfe_busaddr);
300b9f78d2bSBill Paul 
30196ee09c5SPyun YongHyeon 	error = bus_dmamem_alloc(sc->bfe_rx_tag, (void *)&sc->bfe_rx_list,
30296ee09c5SPyun YongHyeon 	  BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->bfe_rx_map);
30396ee09c5SPyun YongHyeon 	if (error != 0) {
30496ee09c5SPyun YongHyeon 		device_printf(sc->bfe_dev,
30596ee09c5SPyun YongHyeon 		    "cannot allocate DMA'able memory for Rx ring.\n");
30696ee09c5SPyun YongHyeon 		goto fail;
30796ee09c5SPyun YongHyeon 	}
30896ee09c5SPyun YongHyeon 	ctx.bfe_busaddr = 0;
30996ee09c5SPyun YongHyeon 	error = bus_dmamap_load(sc->bfe_rx_tag, sc->bfe_rx_map,
31096ee09c5SPyun YongHyeon 	    sc->bfe_rx_list, BFE_RX_LIST_SIZE, bfe_dma_map, &ctx,
31196ee09c5SPyun YongHyeon 	    BUS_DMA_NOWAIT);
31296ee09c5SPyun YongHyeon 	if (error != 0 || ctx.bfe_busaddr == 0) {
31396ee09c5SPyun YongHyeon 		device_printf(sc->bfe_dev,
31496ee09c5SPyun YongHyeon 		    "cannot load DMA'able memory for Rx ring.\n");
31596ee09c5SPyun YongHyeon 		goto fail;
31696ee09c5SPyun YongHyeon 	}
31796ee09c5SPyun YongHyeon 	sc->bfe_rx_dma = BFE_ADDR_LO(ctx.bfe_busaddr);
318b9f78d2bSBill Paul 
31996ee09c5SPyun YongHyeon 	/* Create DMA maps for Tx buffers. */
32096ee09c5SPyun YongHyeon 	for (i = 0; i < BFE_TX_LIST_CNT; i++) {
32196ee09c5SPyun YongHyeon 		td = &sc->bfe_tx_ring[i];
32296ee09c5SPyun YongHyeon 		td->bfe_mbuf = NULL;
32396ee09c5SPyun YongHyeon 		td->bfe_map = NULL;
32496ee09c5SPyun YongHyeon 		error = bus_dmamap_create(sc->bfe_txmbuf_tag, 0, &td->bfe_map);
32596ee09c5SPyun YongHyeon 		if (error != 0) {
32696ee09c5SPyun YongHyeon 			device_printf(sc->bfe_dev,
32796ee09c5SPyun YongHyeon 			    "cannot create DMA map for Tx.\n");
32896ee09c5SPyun YongHyeon 			goto fail;
32996ee09c5SPyun YongHyeon 		}
33096ee09c5SPyun YongHyeon 	}
33196ee09c5SPyun YongHyeon 
33296ee09c5SPyun YongHyeon 	/* Create spare DMA map for Rx buffers. */
33396ee09c5SPyun YongHyeon 	error = bus_dmamap_create(sc->bfe_rxmbuf_tag, 0, &sc->bfe_rx_sparemap);
33496ee09c5SPyun YongHyeon 	if (error != 0) {
33596ee09c5SPyun YongHyeon 		device_printf(sc->bfe_dev, "cannot create spare DMA map for Rx.\n");
33696ee09c5SPyun YongHyeon 		goto fail;
33796ee09c5SPyun YongHyeon 	}
33896ee09c5SPyun YongHyeon 	/* Create DMA maps for Rx buffers. */
33996ee09c5SPyun YongHyeon 	for (i = 0; i < BFE_RX_LIST_CNT; i++) {
34096ee09c5SPyun YongHyeon 		rd = &sc->bfe_rx_ring[i];
34196ee09c5SPyun YongHyeon 		rd->bfe_mbuf = NULL;
34296ee09c5SPyun YongHyeon 		rd->bfe_map = NULL;
34396ee09c5SPyun YongHyeon 		rd->bfe_ctrl = 0;
34496ee09c5SPyun YongHyeon 		error = bus_dmamap_create(sc->bfe_rxmbuf_tag, 0, &rd->bfe_map);
34596ee09c5SPyun YongHyeon 		if (error != 0) {
34696ee09c5SPyun YongHyeon 			device_printf(sc->bfe_dev,
34796ee09c5SPyun YongHyeon 			    "cannot create DMA map for Rx.\n");
34896ee09c5SPyun YongHyeon 			goto fail;
34996ee09c5SPyun YongHyeon 		}
35096ee09c5SPyun YongHyeon 	}
35196ee09c5SPyun YongHyeon 
35296ee09c5SPyun YongHyeon fail:
35396ee09c5SPyun YongHyeon 	return (error);
35496ee09c5SPyun YongHyeon }
35596ee09c5SPyun YongHyeon 
35696ee09c5SPyun YongHyeon static void
35796ee09c5SPyun YongHyeon bfe_dma_free(struct bfe_softc *sc)
35896ee09c5SPyun YongHyeon {
35996ee09c5SPyun YongHyeon 	struct bfe_tx_data *td;
36096ee09c5SPyun YongHyeon 	struct bfe_rx_data *rd;
36196ee09c5SPyun YongHyeon 	int i;
36296ee09c5SPyun YongHyeon 
36396ee09c5SPyun YongHyeon 	/* Tx ring. */
36496ee09c5SPyun YongHyeon 	if (sc->bfe_tx_tag != NULL) {
365068d8643SJohn Baldwin 		if (sc->bfe_tx_dma != 0)
36696ee09c5SPyun YongHyeon 			bus_dmamap_unload(sc->bfe_tx_tag, sc->bfe_tx_map);
367068d8643SJohn Baldwin 		if (sc->bfe_tx_list != NULL)
36896ee09c5SPyun YongHyeon 			bus_dmamem_free(sc->bfe_tx_tag, sc->bfe_tx_list,
36996ee09c5SPyun YongHyeon 			    sc->bfe_tx_map);
370068d8643SJohn Baldwin 		sc->bfe_tx_dma = 0;
37196ee09c5SPyun YongHyeon 		sc->bfe_tx_list = NULL;
37296ee09c5SPyun YongHyeon 		bus_dma_tag_destroy(sc->bfe_tx_tag);
37396ee09c5SPyun YongHyeon 		sc->bfe_tx_tag = NULL;
37496ee09c5SPyun YongHyeon 	}
37596ee09c5SPyun YongHyeon 
37696ee09c5SPyun YongHyeon 	/* Rx ring. */
37796ee09c5SPyun YongHyeon 	if (sc->bfe_rx_tag != NULL) {
378068d8643SJohn Baldwin 		if (sc->bfe_rx_dma != 0)
37996ee09c5SPyun YongHyeon 			bus_dmamap_unload(sc->bfe_rx_tag, sc->bfe_rx_map);
380068d8643SJohn Baldwin 		if (sc->bfe_rx_list != NULL)
38196ee09c5SPyun YongHyeon 			bus_dmamem_free(sc->bfe_rx_tag, sc->bfe_rx_list,
38296ee09c5SPyun YongHyeon 			    sc->bfe_rx_map);
383068d8643SJohn Baldwin 		sc->bfe_rx_dma = 0;
38496ee09c5SPyun YongHyeon 		sc->bfe_rx_list = NULL;
38596ee09c5SPyun YongHyeon 		bus_dma_tag_destroy(sc->bfe_rx_tag);
38696ee09c5SPyun YongHyeon 		sc->bfe_rx_tag = NULL;
38796ee09c5SPyun YongHyeon 	}
38896ee09c5SPyun YongHyeon 
38996ee09c5SPyun YongHyeon 	/* Tx buffers. */
39096ee09c5SPyun YongHyeon 	if (sc->bfe_txmbuf_tag != NULL) {
39196ee09c5SPyun YongHyeon 		for (i = 0; i < BFE_TX_LIST_CNT; i++) {
39296ee09c5SPyun YongHyeon 			td = &sc->bfe_tx_ring[i];
39396ee09c5SPyun YongHyeon 			if (td->bfe_map != NULL) {
39496ee09c5SPyun YongHyeon 				bus_dmamap_destroy(sc->bfe_txmbuf_tag,
39596ee09c5SPyun YongHyeon 				    td->bfe_map);
39696ee09c5SPyun YongHyeon 				td->bfe_map = NULL;
39796ee09c5SPyun YongHyeon 			}
39896ee09c5SPyun YongHyeon 		}
39996ee09c5SPyun YongHyeon 		bus_dma_tag_destroy(sc->bfe_txmbuf_tag);
40096ee09c5SPyun YongHyeon 		sc->bfe_txmbuf_tag = NULL;
40196ee09c5SPyun YongHyeon 	}
40296ee09c5SPyun YongHyeon 
40396ee09c5SPyun YongHyeon 	/* Rx buffers. */
40496ee09c5SPyun YongHyeon 	if (sc->bfe_rxmbuf_tag != NULL) {
40596ee09c5SPyun YongHyeon 		for (i = 0; i < BFE_RX_LIST_CNT; i++) {
40696ee09c5SPyun YongHyeon 			rd = &sc->bfe_rx_ring[i];
40796ee09c5SPyun YongHyeon 			if (rd->bfe_map != NULL) {
40896ee09c5SPyun YongHyeon 				bus_dmamap_destroy(sc->bfe_rxmbuf_tag,
40996ee09c5SPyun YongHyeon 				    rd->bfe_map);
41096ee09c5SPyun YongHyeon 				rd->bfe_map = NULL;
41196ee09c5SPyun YongHyeon 			}
41296ee09c5SPyun YongHyeon 		}
41396ee09c5SPyun YongHyeon 		if (sc->bfe_rx_sparemap != NULL) {
41496ee09c5SPyun YongHyeon 			bus_dmamap_destroy(sc->bfe_rxmbuf_tag,
41596ee09c5SPyun YongHyeon 			    sc->bfe_rx_sparemap);
41696ee09c5SPyun YongHyeon 			sc->bfe_rx_sparemap = NULL;
41796ee09c5SPyun YongHyeon 		}
41896ee09c5SPyun YongHyeon 		bus_dma_tag_destroy(sc->bfe_rxmbuf_tag);
41996ee09c5SPyun YongHyeon 		sc->bfe_rxmbuf_tag = NULL;
42096ee09c5SPyun YongHyeon 	}
42196ee09c5SPyun YongHyeon 
42296ee09c5SPyun YongHyeon 	if (sc->bfe_parent_tag != NULL) {
42396ee09c5SPyun YongHyeon 		bus_dma_tag_destroy(sc->bfe_parent_tag);
42496ee09c5SPyun YongHyeon 		sc->bfe_parent_tag = NULL;
42596ee09c5SPyun YongHyeon 	}
426b9f78d2bSBill Paul }
427b9f78d2bSBill Paul 
428b9f78d2bSBill Paul static int
429b9f78d2bSBill Paul bfe_attach(device_t dev)
430b9f78d2bSBill Paul {
431c0e5e270SJustin Hibbits 	if_t ifp = NULL;
432b9f78d2bSBill Paul 	struct bfe_softc *sc;
433be280562SPyun YongHyeon 	int error = 0, rid;
434b9f78d2bSBill Paul 
435b9f78d2bSBill Paul 	sc = device_get_softc(dev);
436b9f78d2bSBill Paul 	mtx_init(&sc->bfe_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
437f16b4811SMike Makonnen 			MTX_DEF);
4386ceb40baSPyun YongHyeon 	callout_init_mtx(&sc->bfe_stat_co, &sc->bfe_mtx, 0);
439b9f78d2bSBill Paul 
440b9f78d2bSBill Paul 	sc->bfe_dev = dev;
441b9f78d2bSBill Paul 
442b9f78d2bSBill Paul 	/*
443b9f78d2bSBill Paul 	 * Map control/status registers.
444b9f78d2bSBill Paul 	 */
445b9f78d2bSBill Paul 	pci_enable_busmaster(dev);
446b9f78d2bSBill Paul 
44796ee09c5SPyun YongHyeon 	rid = PCIR_BAR(0);
4485f96beb9SNate Lawson 	sc->bfe_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
449b9f78d2bSBill Paul 			RF_ACTIVE);
450b9f78d2bSBill Paul 	if (sc->bfe_res == NULL) {
451be280562SPyun YongHyeon 		device_printf(dev, "couldn't map memory\n");
452b9f78d2bSBill Paul 		error = ENXIO;
453b9f78d2bSBill Paul 		goto fail;
454b9f78d2bSBill Paul 	}
455b9f78d2bSBill Paul 
456b9f78d2bSBill Paul 	/* Allocate interrupt */
457b9f78d2bSBill Paul 	rid = 0;
458b9f78d2bSBill Paul 
4595f96beb9SNate Lawson 	sc->bfe_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
460b9f78d2bSBill Paul 			RF_SHAREABLE | RF_ACTIVE);
461b9f78d2bSBill Paul 	if (sc->bfe_irq == NULL) {
462be280562SPyun YongHyeon 		device_printf(dev, "couldn't map interrupt\n");
463b9f78d2bSBill Paul 		error = ENXIO;
464b9f78d2bSBill Paul 		goto fail;
465b9f78d2bSBill Paul 	}
466b9f78d2bSBill Paul 
46796ee09c5SPyun YongHyeon 	if (bfe_dma_alloc(sc) != 0) {
468be280562SPyun YongHyeon 		device_printf(dev, "failed to allocate DMA resources\n");
469b9f78d2bSBill Paul 		error = ENXIO;
470b9f78d2bSBill Paul 		goto fail;
471b9f78d2bSBill Paul 	}
472b9f78d2bSBill Paul 
473861cf54cSPyun YongHyeon 	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
474861cf54cSPyun YongHyeon 	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
4757029da5cSPawel Biernacki 	    "stats", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
4767029da5cSPawel Biernacki 	    sysctl_bfe_stats, "I", "Statistics");
477861cf54cSPyun YongHyeon 
478b9f78d2bSBill Paul 	/* Set up ifnet structure */
479fc74a9f9SBrooks Davis 	ifp = sc->bfe_ifp = if_alloc(IFT_ETHER);
480c0e5e270SJustin Hibbits 	if_setsoftc(ifp, sc);
4819bf40edeSBrooks Davis 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
482c0e5e270SJustin Hibbits 	if_setflags(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
483c0e5e270SJustin Hibbits 	if_setioctlfn(ifp, bfe_ioctl);
484c0e5e270SJustin Hibbits 	if_setstartfn(ifp, bfe_start);
485c0e5e270SJustin Hibbits 	if_setinitfn(ifp, bfe_init);
486c0e5e270SJustin Hibbits 	if_setsendqlen(ifp, BFE_TX_QLEN);
487c0e5e270SJustin Hibbits 	if_setsendqready(ifp);
488b9f78d2bSBill Paul 
489b9f78d2bSBill Paul 	bfe_get_config(sc);
490b9f78d2bSBill Paul 
491b9f78d2bSBill Paul 	/* Reset the chip and turn on the PHY */
492f16b4811SMike Makonnen 	BFE_LOCK(sc);
493b9f78d2bSBill Paul 	bfe_chip_reset(sc);
494f16b4811SMike Makonnen 	BFE_UNLOCK(sc);
495b9f78d2bSBill Paul 
4968e5d93dbSMarius Strobl 	error = mii_attach(dev, &sc->bfe_miibus, ifp, bfe_ifmedia_upd,
4978e5d93dbSMarius Strobl 	    bfe_ifmedia_sts, BMSR_DEFCAPMASK, sc->bfe_phyaddr, MII_OFFSET_ANY,
4988e5d93dbSMarius Strobl 	    0);
4998e5d93dbSMarius Strobl 	if (error != 0) {
5008e5d93dbSMarius Strobl 		device_printf(dev, "attaching PHYs failed\n");
501b9f78d2bSBill Paul 		goto fail;
502b9f78d2bSBill Paul 	}
503b9f78d2bSBill Paul 
504fc74a9f9SBrooks Davis 	ether_ifattach(ifp, sc->bfe_enaddr);
505b9f78d2bSBill Paul 
506b9f78d2bSBill Paul 	/*
507912bf9d3SDag-Erling Smørgrav 	 * Tell the upper layer(s) we support long frames.
508912bf9d3SDag-Erling Smørgrav 	 */
509c0e5e270SJustin Hibbits 	if_setifheaderlen(ifp, sizeof(struct ether_vlan_header));
510c0e5e270SJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_VLAN_MTU, 0);
511c0e5e270SJustin Hibbits 	if_setcapenablebit(ifp, IFCAP_VLAN_MTU, 0);
512912bf9d3SDag-Erling Smørgrav 
513912bf9d3SDag-Erling Smørgrav 	/*
514b9f78d2bSBill Paul 	 * Hook interrupt last to avoid having to lock softc
515b9f78d2bSBill Paul 	 */
516f16b4811SMike Makonnen 	error = bus_setup_intr(dev, sc->bfe_irq, INTR_TYPE_NET | INTR_MPSAFE,
517ef544f63SPaolo Pisati 			NULL, bfe_intr, sc, &sc->bfe_intrhand);
518b9f78d2bSBill Paul 
519b9f78d2bSBill Paul 	if (error) {
520be280562SPyun YongHyeon 		device_printf(dev, "couldn't set up irq\n");
521b9f78d2bSBill Paul 		goto fail;
522b9f78d2bSBill Paul 	}
523b9f78d2bSBill Paul fail:
52496ee09c5SPyun YongHyeon 	if (error != 0)
52596ee09c5SPyun YongHyeon 		bfe_detach(dev);
526b9f78d2bSBill Paul 	return (error);
527b9f78d2bSBill Paul }
528b9f78d2bSBill Paul 
529b9f78d2bSBill Paul static int
530b9f78d2bSBill Paul bfe_detach(device_t dev)
531b9f78d2bSBill Paul {
532b9f78d2bSBill Paul 	struct bfe_softc *sc;
533c0e5e270SJustin Hibbits 	if_t ifp;
534b9f78d2bSBill Paul 
535b9f78d2bSBill Paul 	sc = device_get_softc(dev);
536b9f78d2bSBill Paul 
537fc74a9f9SBrooks Davis 	ifp = sc->bfe_ifp;
538b9f78d2bSBill Paul 
539b9f78d2bSBill Paul 	if (device_is_attached(dev)) {
5406ceb40baSPyun YongHyeon 		BFE_LOCK(sc);
54149bbfbc5SPyun YongHyeon 		sc->bfe_flags |= BFE_FLAG_DETACH;
542b9f78d2bSBill Paul 		bfe_stop(sc);
5436ceb40baSPyun YongHyeon 		BFE_UNLOCK(sc);
5446ceb40baSPyun YongHyeon 		callout_drain(&sc->bfe_stat_co);
5456ceb40baSPyun YongHyeon 		if (ifp != NULL)
546b9f78d2bSBill Paul 			ether_ifdetach(ifp);
547b9f78d2bSBill Paul 	}
548b9f78d2bSBill Paul 
54996ee09c5SPyun YongHyeon 	BFE_LOCK(sc);
550b9f78d2bSBill Paul 	bfe_chip_reset(sc);
55196ee09c5SPyun YongHyeon 	BFE_UNLOCK(sc);
552b9f78d2bSBill Paul 
553b9f78d2bSBill Paul 	bus_generic_detach(dev);
554b9f78d2bSBill Paul 
555b9f78d2bSBill Paul 	bfe_release_resources(sc);
55696ee09c5SPyun YongHyeon 	bfe_dma_free(sc);
557b9f78d2bSBill Paul 	mtx_destroy(&sc->bfe_mtx);
558b9f78d2bSBill Paul 
559b9f78d2bSBill Paul 	return (0);
560b9f78d2bSBill Paul }
561b9f78d2bSBill Paul 
562b9f78d2bSBill Paul /*
563b9f78d2bSBill Paul  * Stop all chip I/O so that the kernel's probe routines don't
564b9f78d2bSBill Paul  * get confused by errant DMAs when rebooting.
565b9f78d2bSBill Paul  */
5666a087a87SPyun YongHyeon static int
567b9f78d2bSBill Paul bfe_shutdown(device_t dev)
568b9f78d2bSBill Paul {
569b9f78d2bSBill Paul 	struct bfe_softc *sc;
570b9f78d2bSBill Paul 
571b9f78d2bSBill Paul 	sc = device_get_softc(dev);
572b9f78d2bSBill Paul 	BFE_LOCK(sc);
573b9f78d2bSBill Paul 	bfe_stop(sc);
574b9f78d2bSBill Paul 
575b9f78d2bSBill Paul 	BFE_UNLOCK(sc);
5766a087a87SPyun YongHyeon 
5776a087a87SPyun YongHyeon 	return (0);
578b9f78d2bSBill Paul }
579b9f78d2bSBill Paul 
580b9f78d2bSBill Paul static int
5816daee769SJohn Baldwin bfe_suspend(device_t dev)
5826daee769SJohn Baldwin {
5836daee769SJohn Baldwin 	struct bfe_softc *sc;
5846daee769SJohn Baldwin 
5856daee769SJohn Baldwin 	sc = device_get_softc(dev);
5866daee769SJohn Baldwin 	BFE_LOCK(sc);
5876daee769SJohn Baldwin 	bfe_stop(sc);
5886daee769SJohn Baldwin 	BFE_UNLOCK(sc);
5896daee769SJohn Baldwin 
5906daee769SJohn Baldwin 	return (0);
5916daee769SJohn Baldwin }
5926daee769SJohn Baldwin 
5936daee769SJohn Baldwin static int
5946daee769SJohn Baldwin bfe_resume(device_t dev)
5956daee769SJohn Baldwin {
5966daee769SJohn Baldwin 	struct bfe_softc *sc;
597c0e5e270SJustin Hibbits 	if_t ifp;
5986daee769SJohn Baldwin 
5996daee769SJohn Baldwin 	sc = device_get_softc(dev);
6006daee769SJohn Baldwin 	ifp = sc->bfe_ifp;
6016daee769SJohn Baldwin 	BFE_LOCK(sc);
6026daee769SJohn Baldwin 	bfe_chip_reset(sc);
603c0e5e270SJustin Hibbits 	if (if_getflags(ifp) & IFF_UP) {
6046daee769SJohn Baldwin 		bfe_init_locked(sc);
605c0e5e270SJustin Hibbits 		if (if_getdrvflags(ifp) & IFF_DRV_RUNNING &&
606c0e5e270SJustin Hibbits 		    !if_sendq_empty(ifp))
6076daee769SJohn Baldwin 			bfe_start_locked(ifp);
6086daee769SJohn Baldwin 	}
6096daee769SJohn Baldwin 	BFE_UNLOCK(sc);
6106daee769SJohn Baldwin 
6116daee769SJohn Baldwin 	return (0);
6126daee769SJohn Baldwin }
6136daee769SJohn Baldwin 
6146daee769SJohn Baldwin static int
615b9f78d2bSBill Paul bfe_miibus_readreg(device_t dev, int phy, int reg)
616b9f78d2bSBill Paul {
617b9f78d2bSBill Paul 	struct bfe_softc *sc;
618b9f78d2bSBill Paul 	u_int32_t ret;
619b9f78d2bSBill Paul 
620b9f78d2bSBill Paul 	sc = device_get_softc(dev);
621b9f78d2bSBill Paul 	bfe_readphy(sc, reg, &ret);
622b9f78d2bSBill Paul 
623b9f78d2bSBill Paul 	return (ret);
624b9f78d2bSBill Paul }
625b9f78d2bSBill Paul 
626b9f78d2bSBill Paul static int
627b9f78d2bSBill Paul bfe_miibus_writereg(device_t dev, int phy, int reg, int val)
628b9f78d2bSBill Paul {
629b9f78d2bSBill Paul 	struct bfe_softc *sc;
630b9f78d2bSBill Paul 
631b9f78d2bSBill Paul 	sc = device_get_softc(dev);
632b9f78d2bSBill Paul 	bfe_writephy(sc, reg, val);
633b9f78d2bSBill Paul 
634b9f78d2bSBill Paul 	return (0);
635b9f78d2bSBill Paul }
636b9f78d2bSBill Paul 
637b9f78d2bSBill Paul static void
638b9f78d2bSBill Paul bfe_miibus_statchg(device_t dev)
639b9f78d2bSBill Paul {
6406ceb40baSPyun YongHyeon 	struct bfe_softc *sc;
6416ceb40baSPyun YongHyeon 	struct mii_data *mii;
6428f3562e2SMateusz Guzik 	u_int32_t val;
6438f3562e2SMateusz Guzik #ifdef notyet
6448f3562e2SMateusz Guzik 	u_int32_t flow;
6458f3562e2SMateusz Guzik #endif
6466ceb40baSPyun YongHyeon 
6476ceb40baSPyun YongHyeon 	sc = device_get_softc(dev);
6486ceb40baSPyun YongHyeon 	mii = device_get_softc(sc->bfe_miibus);
6496ceb40baSPyun YongHyeon 
65049bbfbc5SPyun YongHyeon 	sc->bfe_flags &= ~BFE_FLAG_LINK;
65149bbfbc5SPyun YongHyeon 	if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
65249bbfbc5SPyun YongHyeon 	    (IFM_ACTIVE | IFM_AVALID)) {
65349bbfbc5SPyun YongHyeon 		switch (IFM_SUBTYPE(mii->mii_media_active)) {
65449bbfbc5SPyun YongHyeon 		case IFM_10_T:
65549bbfbc5SPyun YongHyeon 		case IFM_100_TX:
65649bbfbc5SPyun YongHyeon 			sc->bfe_flags |= BFE_FLAG_LINK;
65749bbfbc5SPyun YongHyeon 			break;
65849bbfbc5SPyun YongHyeon 		default:
65949bbfbc5SPyun YongHyeon 			break;
66049bbfbc5SPyun YongHyeon 		}
66149bbfbc5SPyun YongHyeon 	}
6626ceb40baSPyun YongHyeon 
6636ceb40baSPyun YongHyeon 	/* XXX Should stop Rx/Tx engine prior to touching MAC. */
6646ceb40baSPyun YongHyeon 	val = CSR_READ_4(sc, BFE_TX_CTRL);
6656ceb40baSPyun YongHyeon 	val &= ~BFE_TX_DUPLEX;
6666ceb40baSPyun YongHyeon 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
6676ceb40baSPyun YongHyeon 		val |= BFE_TX_DUPLEX;
6686ceb40baSPyun YongHyeon #ifdef notyet
6696ceb40baSPyun YongHyeon 		flow = CSR_READ_4(sc, BFE_RXCONF);
6706ceb40baSPyun YongHyeon 		flow &= ~BFE_RXCONF_FLOW;
6716ceb40baSPyun YongHyeon 		if ((IFM_OPTIONS(sc->sc_mii->mii_media_active) &
6726ceb40baSPyun YongHyeon 		    IFM_ETH_RXPAUSE) != 0)
6736ceb40baSPyun YongHyeon 			flow |= BFE_RXCONF_FLOW;
6746ceb40baSPyun YongHyeon 		CSR_WRITE_4(sc, BFE_RXCONF, flow);
6756ceb40baSPyun YongHyeon 		/*
6766ceb40baSPyun YongHyeon 		 * It seems that the hardware has Tx pause issues
6776ceb40baSPyun YongHyeon 		 * so enable only Rx pause.
6786ceb40baSPyun YongHyeon 		 */
6796ceb40baSPyun YongHyeon 		flow = CSR_READ_4(sc, BFE_MAC_FLOW);
6806ceb40baSPyun YongHyeon 		flow &= ~BFE_FLOW_PAUSE_ENAB;
6816ceb40baSPyun YongHyeon 		CSR_WRITE_4(sc, BFE_MAC_FLOW, flow);
6826ceb40baSPyun YongHyeon #endif
6836ceb40baSPyun YongHyeon 	}
6846ceb40baSPyun YongHyeon 	CSR_WRITE_4(sc, BFE_TX_CTRL, val);
685b9f78d2bSBill Paul }
686b9f78d2bSBill Paul 
687b9f78d2bSBill Paul static void
688b9f78d2bSBill Paul bfe_tx_ring_free(struct bfe_softc *sc)
689b9f78d2bSBill Paul {
690b9f78d2bSBill Paul 	int i;
691b9f78d2bSBill Paul 
692b9f78d2bSBill Paul 	for(i = 0; i < BFE_TX_LIST_CNT; i++) {
693b9f78d2bSBill Paul 		if (sc->bfe_tx_ring[i].bfe_mbuf != NULL) {
69496ee09c5SPyun YongHyeon 			bus_dmamap_sync(sc->bfe_txmbuf_tag,
69596ee09c5SPyun YongHyeon 			    sc->bfe_tx_ring[i].bfe_map, BUS_DMASYNC_POSTWRITE);
69696ee09c5SPyun YongHyeon 			bus_dmamap_unload(sc->bfe_txmbuf_tag,
69796ee09c5SPyun YongHyeon 			    sc->bfe_tx_ring[i].bfe_map);
698b9f78d2bSBill Paul 			m_freem(sc->bfe_tx_ring[i].bfe_mbuf);
699b9f78d2bSBill Paul 			sc->bfe_tx_ring[i].bfe_mbuf = NULL;
700b9f78d2bSBill Paul 		}
701b9f78d2bSBill Paul 	}
702b9f78d2bSBill Paul 	bzero(sc->bfe_tx_list, BFE_TX_LIST_SIZE);
70396ee09c5SPyun YongHyeon 	bus_dmamap_sync(sc->bfe_tx_tag, sc->bfe_tx_map,
70496ee09c5SPyun YongHyeon 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
705b9f78d2bSBill Paul }
706b9f78d2bSBill Paul 
707b9f78d2bSBill Paul static void
708b9f78d2bSBill Paul bfe_rx_ring_free(struct bfe_softc *sc)
709b9f78d2bSBill Paul {
710b9f78d2bSBill Paul 	int i;
711b9f78d2bSBill Paul 
712b9f78d2bSBill Paul 	for (i = 0; i < BFE_RX_LIST_CNT; i++) {
713b9f78d2bSBill Paul 		if (sc->bfe_rx_ring[i].bfe_mbuf != NULL) {
71496ee09c5SPyun YongHyeon 			bus_dmamap_sync(sc->bfe_rxmbuf_tag,
71596ee09c5SPyun YongHyeon 			    sc->bfe_rx_ring[i].bfe_map, BUS_DMASYNC_POSTREAD);
71696ee09c5SPyun YongHyeon 			bus_dmamap_unload(sc->bfe_rxmbuf_tag,
71796ee09c5SPyun YongHyeon 			    sc->bfe_rx_ring[i].bfe_map);
718b9f78d2bSBill Paul 			m_freem(sc->bfe_rx_ring[i].bfe_mbuf);
719b9f78d2bSBill Paul 			sc->bfe_rx_ring[i].bfe_mbuf = NULL;
720b9f78d2bSBill Paul 		}
721b9f78d2bSBill Paul 	}
722b9f78d2bSBill Paul 	bzero(sc->bfe_rx_list, BFE_RX_LIST_SIZE);
72396ee09c5SPyun YongHyeon 	bus_dmamap_sync(sc->bfe_rx_tag, sc->bfe_rx_map,
72496ee09c5SPyun YongHyeon 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
725b9f78d2bSBill Paul }
726b9f78d2bSBill Paul 
727b9f78d2bSBill Paul static int
728b9f78d2bSBill Paul bfe_list_rx_init(struct bfe_softc *sc)
729b9f78d2bSBill Paul {
73096ee09c5SPyun YongHyeon 	struct bfe_rx_data *rd;
731b9f78d2bSBill Paul 	int i;
732b9f78d2bSBill Paul 
73396ee09c5SPyun YongHyeon 	sc->bfe_rx_prod = sc->bfe_rx_cons = 0;
73496ee09c5SPyun YongHyeon 	bzero(sc->bfe_rx_list, BFE_RX_LIST_SIZE);
735b9f78d2bSBill Paul 	for (i = 0; i < BFE_RX_LIST_CNT; i++) {
73696ee09c5SPyun YongHyeon 		rd = &sc->bfe_rx_ring[i];
73796ee09c5SPyun YongHyeon 		rd->bfe_mbuf = NULL;
73896ee09c5SPyun YongHyeon 		rd->bfe_ctrl = 0;
73996ee09c5SPyun YongHyeon 		if (bfe_list_newbuf(sc, i) != 0)
74027de24a7SDag-Erling Smørgrav 			return (ENOBUFS);
741b9f78d2bSBill Paul 	}
742b9f78d2bSBill Paul 
74396ee09c5SPyun YongHyeon 	bus_dmamap_sync(sc->bfe_rx_tag, sc->bfe_rx_map,
74496ee09c5SPyun YongHyeon 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
745b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_DMARX_PTR, (i * sizeof(struct bfe_desc)));
746b9f78d2bSBill Paul 
747b9f78d2bSBill Paul 	return (0);
748b9f78d2bSBill Paul }
749b9f78d2bSBill Paul 
75096ee09c5SPyun YongHyeon static void
75196ee09c5SPyun YongHyeon bfe_list_tx_init(struct bfe_softc *sc)
75296ee09c5SPyun YongHyeon {
75396ee09c5SPyun YongHyeon 	int i;
75496ee09c5SPyun YongHyeon 
75596ee09c5SPyun YongHyeon 	sc->bfe_tx_cnt = sc->bfe_tx_prod = sc->bfe_tx_cons = 0;
75696ee09c5SPyun YongHyeon 	bzero(sc->bfe_tx_list, BFE_TX_LIST_SIZE);
75796ee09c5SPyun YongHyeon 	for (i = 0; i < BFE_TX_LIST_CNT; i++)
75896ee09c5SPyun YongHyeon 		sc->bfe_tx_ring[i].bfe_mbuf = NULL;
75996ee09c5SPyun YongHyeon 
76096ee09c5SPyun YongHyeon 	bus_dmamap_sync(sc->bfe_tx_tag, sc->bfe_tx_map,
76196ee09c5SPyun YongHyeon 	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
76296ee09c5SPyun YongHyeon }
76396ee09c5SPyun YongHyeon 
76496ee09c5SPyun YongHyeon static void
76596ee09c5SPyun YongHyeon bfe_discard_buf(struct bfe_softc *sc, int c)
76696ee09c5SPyun YongHyeon {
76796ee09c5SPyun YongHyeon 	struct bfe_rx_data *r;
76896ee09c5SPyun YongHyeon 	struct bfe_desc *d;
76996ee09c5SPyun YongHyeon 
77096ee09c5SPyun YongHyeon 	r = &sc->bfe_rx_ring[c];
77196ee09c5SPyun YongHyeon 	d = &sc->bfe_rx_list[c];
77296ee09c5SPyun YongHyeon 	d->bfe_ctrl = htole32(r->bfe_ctrl);
77396ee09c5SPyun YongHyeon }
77496ee09c5SPyun YongHyeon 
775b9f78d2bSBill Paul static int
77696ee09c5SPyun YongHyeon bfe_list_newbuf(struct bfe_softc *sc, int c)
777b9f78d2bSBill Paul {
778b9f78d2bSBill Paul 	struct bfe_rxheader *rx_header;
779b9f78d2bSBill Paul 	struct bfe_desc *d;
78096ee09c5SPyun YongHyeon 	struct bfe_rx_data *r;
78196ee09c5SPyun YongHyeon 	struct mbuf *m;
78296ee09c5SPyun YongHyeon 	bus_dma_segment_t segs[1];
78396ee09c5SPyun YongHyeon 	bus_dmamap_t map;
784b9f78d2bSBill Paul 	u_int32_t ctrl;
78596ee09c5SPyun YongHyeon 	int nsegs;
786b9f78d2bSBill Paul 
787c6499eccSGleb Smirnoff 	m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
78872b09927SGleb Smirnoff 	if (m == NULL)
78972b09927SGleb Smirnoff 		return (ENOBUFS);
790b9f78d2bSBill Paul 	m->m_len = m->m_pkthdr.len = MCLBYTES;
79196ee09c5SPyun YongHyeon 
79296ee09c5SPyun YongHyeon 	if (bus_dmamap_load_mbuf_sg(sc->bfe_rxmbuf_tag, sc->bfe_rx_sparemap,
79396ee09c5SPyun YongHyeon 	    m, segs, &nsegs, 0) != 0) {
79496ee09c5SPyun YongHyeon 		m_freem(m);
79596ee09c5SPyun YongHyeon 		return (ENOBUFS);
796b9f78d2bSBill Paul 	}
79796ee09c5SPyun YongHyeon 
79896ee09c5SPyun YongHyeon 	KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
79996ee09c5SPyun YongHyeon 	r = &sc->bfe_rx_ring[c];
80096ee09c5SPyun YongHyeon 	if (r->bfe_mbuf != NULL) {
80196ee09c5SPyun YongHyeon 		bus_dmamap_sync(sc->bfe_rxmbuf_tag, r->bfe_map,
80296ee09c5SPyun YongHyeon 		    BUS_DMASYNC_POSTREAD);
80396ee09c5SPyun YongHyeon 		bus_dmamap_unload(sc->bfe_rxmbuf_tag, r->bfe_map);
80496ee09c5SPyun YongHyeon 	}
80596ee09c5SPyun YongHyeon 	map = r->bfe_map;
80696ee09c5SPyun YongHyeon 	r->bfe_map = sc->bfe_rx_sparemap;
80796ee09c5SPyun YongHyeon 	sc->bfe_rx_sparemap = map;
80896ee09c5SPyun YongHyeon 	r->bfe_mbuf = m;
809b9f78d2bSBill Paul 
810b9f78d2bSBill Paul 	rx_header = mtod(m, struct bfe_rxheader *);
811b9f78d2bSBill Paul 	rx_header->len = 0;
812b9f78d2bSBill Paul 	rx_header->flags = 0;
81396ee09c5SPyun YongHyeon 	bus_dmamap_sync(sc->bfe_rxmbuf_tag, r->bfe_map, BUS_DMASYNC_PREREAD);
814b9f78d2bSBill Paul 
81596ee09c5SPyun YongHyeon 	ctrl = segs[0].ds_len & BFE_DESC_LEN;
81696ee09c5SPyun YongHyeon 	KASSERT(ctrl > ETHER_MAX_LEN + 32, ("%s: buffer size too small(%d)!",
81796ee09c5SPyun YongHyeon 	    __func__, ctrl));
818b9f78d2bSBill Paul 	if (c == BFE_RX_LIST_CNT - 1)
819b9f78d2bSBill Paul 		ctrl |= BFE_DESC_EOT;
82096ee09c5SPyun YongHyeon 	r->bfe_ctrl = ctrl;
821b9f78d2bSBill Paul 
82296ee09c5SPyun YongHyeon 	d = &sc->bfe_rx_list[c];
82396ee09c5SPyun YongHyeon 	d->bfe_ctrl = htole32(ctrl);
82496ee09c5SPyun YongHyeon 	/* The chip needs all addresses to be added to BFE_PCI_DMA. */
82596ee09c5SPyun YongHyeon 	d->bfe_addr = htole32(BFE_ADDR_LO(segs[0].ds_addr) + BFE_PCI_DMA);
82696ee09c5SPyun YongHyeon 
827b9f78d2bSBill Paul 	return (0);
828b9f78d2bSBill Paul }
829b9f78d2bSBill Paul 
830b9f78d2bSBill Paul static void
831b9f78d2bSBill Paul bfe_get_config(struct bfe_softc *sc)
832b9f78d2bSBill Paul {
833b9f78d2bSBill Paul 	u_int8_t eeprom[128];
834b9f78d2bSBill Paul 
835b9f78d2bSBill Paul 	bfe_read_eeprom(sc, eeprom);
836b9f78d2bSBill Paul 
837fc74a9f9SBrooks Davis 	sc->bfe_enaddr[0] = eeprom[79];
838fc74a9f9SBrooks Davis 	sc->bfe_enaddr[1] = eeprom[78];
839fc74a9f9SBrooks Davis 	sc->bfe_enaddr[2] = eeprom[81];
840fc74a9f9SBrooks Davis 	sc->bfe_enaddr[3] = eeprom[80];
841fc74a9f9SBrooks Davis 	sc->bfe_enaddr[4] = eeprom[83];
842fc74a9f9SBrooks Davis 	sc->bfe_enaddr[5] = eeprom[82];
843b9f78d2bSBill Paul 
844b9f78d2bSBill Paul 	sc->bfe_phyaddr = eeprom[90] & 0x1f;
845b9f78d2bSBill Paul 	sc->bfe_mdc_port = (eeprom[90] >> 14) & 0x1;
846b9f78d2bSBill Paul 
847b9f78d2bSBill Paul 	sc->bfe_core_unit = 0;
848b9f78d2bSBill Paul 	sc->bfe_dma_offset = BFE_PCI_DMA;
849b9f78d2bSBill Paul }
850b9f78d2bSBill Paul 
851b9f78d2bSBill Paul static void
852b9f78d2bSBill Paul bfe_pci_setup(struct bfe_softc *sc, u_int32_t cores)
853b9f78d2bSBill Paul {
8540fd7b4c7SScott Long 	u_int32_t bar_orig, val;
855b9f78d2bSBill Paul 
856b9f78d2bSBill Paul 	bar_orig = pci_read_config(sc->bfe_dev, BFE_BAR0_WIN, 4);
857b9f78d2bSBill Paul 	pci_write_config(sc->bfe_dev, BFE_BAR0_WIN, BFE_REG_PCI, 4);
858b9f78d2bSBill Paul 
859b9f78d2bSBill Paul 	val = CSR_READ_4(sc, BFE_SBINTVEC);
860b9f78d2bSBill Paul 	val |= cores;
861b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_SBINTVEC, val);
862b9f78d2bSBill Paul 
863b9f78d2bSBill Paul 	val = CSR_READ_4(sc, BFE_SSB_PCI_TRANS_2);
864b9f78d2bSBill Paul 	val |= BFE_SSB_PCI_PREF | BFE_SSB_PCI_BURST;
865b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_SSB_PCI_TRANS_2, val);
866b9f78d2bSBill Paul 
867b9f78d2bSBill Paul 	pci_write_config(sc->bfe_dev, BFE_BAR0_WIN, bar_orig, 4);
868b9f78d2bSBill Paul }
869b9f78d2bSBill Paul 
870b9f78d2bSBill Paul static void
871b9f78d2bSBill Paul bfe_clear_stats(struct bfe_softc *sc)
872b9f78d2bSBill Paul {
873861cf54cSPyun YongHyeon 	uint32_t reg;
874b9f78d2bSBill Paul 
875f16b4811SMike Makonnen 	BFE_LOCK_ASSERT(sc);
876b9f78d2bSBill Paul 
877b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_MIB_CTRL, BFE_MIB_CLR_ON_READ);
878b9f78d2bSBill Paul 	for (reg = BFE_TX_GOOD_O; reg <= BFE_TX_PAUSE; reg += 4)
879b9f78d2bSBill Paul 		CSR_READ_4(sc, reg);
880b9f78d2bSBill Paul 	for (reg = BFE_RX_GOOD_O; reg <= BFE_RX_NPAUSE; reg += 4)
881b9f78d2bSBill Paul 		CSR_READ_4(sc, reg);
882b9f78d2bSBill Paul }
883b9f78d2bSBill Paul 
884b9f78d2bSBill Paul static int
885b9f78d2bSBill Paul bfe_resetphy(struct bfe_softc *sc)
886b9f78d2bSBill Paul {
887b9f78d2bSBill Paul 	u_int32_t val;
888b9f78d2bSBill Paul 
889b9f78d2bSBill Paul 	bfe_writephy(sc, 0, BMCR_RESET);
890b9f78d2bSBill Paul 	DELAY(100);
891b9f78d2bSBill Paul 	bfe_readphy(sc, 0, &val);
892b9f78d2bSBill Paul 	if (val & BMCR_RESET) {
893be280562SPyun YongHyeon 		device_printf(sc->bfe_dev, "PHY Reset would not complete.\n");
89427de24a7SDag-Erling Smørgrav 		return (ENXIO);
895b9f78d2bSBill Paul 	}
89627de24a7SDag-Erling Smørgrav 	return (0);
897b9f78d2bSBill Paul }
898b9f78d2bSBill Paul 
899b9f78d2bSBill Paul static void
900b9f78d2bSBill Paul bfe_chip_halt(struct bfe_softc *sc)
901b9f78d2bSBill Paul {
902f16b4811SMike Makonnen 	BFE_LOCK_ASSERT(sc);
903b9f78d2bSBill Paul 	/* disable interrupts - not that it actually does..*/
904b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_IMASK, 0);
905b9f78d2bSBill Paul 	CSR_READ_4(sc, BFE_IMASK);
906b9f78d2bSBill Paul 
907b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_ENET_CTRL, BFE_ENET_DISABLE);
908b9f78d2bSBill Paul 	bfe_wait_bit(sc, BFE_ENET_CTRL, BFE_ENET_DISABLE, 200, 1);
909b9f78d2bSBill Paul 
910b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_DMARX_CTRL, 0);
911b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_DMATX_CTRL, 0);
912b9f78d2bSBill Paul 	DELAY(10);
913b9f78d2bSBill Paul }
914b9f78d2bSBill Paul 
915b9f78d2bSBill Paul static void
916b9f78d2bSBill Paul bfe_chip_reset(struct bfe_softc *sc)
917b9f78d2bSBill Paul {
918b9f78d2bSBill Paul 	u_int32_t val;
919b9f78d2bSBill Paul 
920f16b4811SMike Makonnen 	BFE_LOCK_ASSERT(sc);
921b9f78d2bSBill Paul 
922b9f78d2bSBill Paul 	/* Set the interrupt vector for the enet core */
923b9f78d2bSBill Paul 	bfe_pci_setup(sc, BFE_INTVEC_ENET0);
924b9f78d2bSBill Paul 
925b9f78d2bSBill Paul 	/* is core up? */
926f2b1c158SJulian Elischer 	val = CSR_READ_4(sc, BFE_SBTMSLOW) &
927f2b1c158SJulian Elischer 	    (BFE_RESET | BFE_REJECT | BFE_CLOCK);
928b9f78d2bSBill Paul 	if (val == BFE_CLOCK) {
929b9f78d2bSBill Paul 		/* It is, so shut it down */
930b9f78d2bSBill Paul 		CSR_WRITE_4(sc, BFE_RCV_LAZY, 0);
931b9f78d2bSBill Paul 		CSR_WRITE_4(sc, BFE_ENET_CTRL, BFE_ENET_DISABLE);
932b9f78d2bSBill Paul 		bfe_wait_bit(sc, BFE_ENET_CTRL, BFE_ENET_DISABLE, 100, 1);
933b9f78d2bSBill Paul 		CSR_WRITE_4(sc, BFE_DMATX_CTRL, 0);
934b9f78d2bSBill Paul 		if (CSR_READ_4(sc, BFE_DMARX_STAT) & BFE_STAT_EMASK)
935f2b1c158SJulian Elischer 			bfe_wait_bit(sc, BFE_DMARX_STAT, BFE_STAT_SIDLE,
936f2b1c158SJulian Elischer 			    100, 0);
937b9f78d2bSBill Paul 		CSR_WRITE_4(sc, BFE_DMARX_CTRL, 0);
938b9f78d2bSBill Paul 	}
939b9f78d2bSBill Paul 
940b9f78d2bSBill Paul 	bfe_core_reset(sc);
941b9f78d2bSBill Paul 	bfe_clear_stats(sc);
942b9f78d2bSBill Paul 
943b9f78d2bSBill Paul 	/*
944b9f78d2bSBill Paul 	 * We want the phy registers to be accessible even when
945b9f78d2bSBill Paul 	 * the driver is "downed" so initialize MDC preamble, frequency,
946b9f78d2bSBill Paul 	 * and whether internal or external phy here.
947b9f78d2bSBill Paul 	 */
948b9f78d2bSBill Paul 
949b9f78d2bSBill Paul 	/* 4402 has 62.5Mhz SB clock and internal phy */
950b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_MDIO_CTRL, 0x8d);
951b9f78d2bSBill Paul 
952b9f78d2bSBill Paul 	/* Internal or external PHY? */
953b9f78d2bSBill Paul 	val = CSR_READ_4(sc, BFE_DEVCTRL);
954b9f78d2bSBill Paul 	if (!(val & BFE_IPP))
955b9f78d2bSBill Paul 		CSR_WRITE_4(sc, BFE_ENET_CTRL, BFE_ENET_EPSEL);
956b9f78d2bSBill Paul 	else if (CSR_READ_4(sc, BFE_DEVCTRL) & BFE_EPR) {
957b9f78d2bSBill Paul 		BFE_AND(sc, BFE_DEVCTRL, ~BFE_EPR);
958b9f78d2bSBill Paul 		DELAY(100);
959b9f78d2bSBill Paul 	}
960b9f78d2bSBill Paul 
96162a9464cSDuncan Barclay 	/* Enable CRC32 generation and set proper LED modes */
96262a9464cSDuncan Barclay 	BFE_OR(sc, BFE_MAC_CTRL, BFE_CTRL_CRC32_ENAB | BFE_CTRL_LED);
96362a9464cSDuncan Barclay 
96462a9464cSDuncan Barclay 	/* Reset or clear powerdown control bit  */
96562a9464cSDuncan Barclay 	BFE_AND(sc, BFE_MAC_CTRL, ~BFE_CTRL_PDOWN);
96662a9464cSDuncan Barclay 
967b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_RCV_LAZY, ((1 << BFE_LAZY_FC_SHIFT) &
968b9f78d2bSBill Paul 				BFE_LAZY_FC_MASK));
969b9f78d2bSBill Paul 
970b9f78d2bSBill Paul 	/*
971f2b1c158SJulian Elischer 	 * We don't want lazy interrupts, so just send them at
972f2b1c158SJulian Elischer 	 * the end of a frame, please
973b9f78d2bSBill Paul 	 */
974b9f78d2bSBill Paul 	BFE_OR(sc, BFE_RCV_LAZY, 0);
975b9f78d2bSBill Paul 
976b9f78d2bSBill Paul 	/* Set max lengths, accounting for VLAN tags */
977b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_RXMAXLEN, ETHER_MAX_LEN+32);
978b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_TXMAXLEN, ETHER_MAX_LEN+32);
979b9f78d2bSBill Paul 
980b9f78d2bSBill Paul 	/* Set watermark XXX - magic */
981b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_TX_WMARK, 56);
982b9f78d2bSBill Paul 
983b9f78d2bSBill Paul 	/*
984f2b1c158SJulian Elischer 	 * Initialise DMA channels
985f2b1c158SJulian Elischer 	 * - not forgetting dma addresses need to be added to BFE_PCI_DMA
986b9f78d2bSBill Paul 	 */
987b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_DMATX_CTRL, BFE_TX_CTRL_ENABLE);
988b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_DMATX_ADDR, sc->bfe_tx_dma + BFE_PCI_DMA);
989b9f78d2bSBill Paul 
990b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_DMARX_CTRL, (BFE_RX_OFFSET << BFE_RX_CTRL_ROSHIFT) |
991b9f78d2bSBill Paul 			BFE_RX_CTRL_ENABLE);
992b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_DMARX_ADDR, sc->bfe_rx_dma + BFE_PCI_DMA);
993b9f78d2bSBill Paul 
994b9f78d2bSBill Paul 	bfe_resetphy(sc);
995b9f78d2bSBill Paul 	bfe_setupphy(sc);
996b9f78d2bSBill Paul }
997b9f78d2bSBill Paul 
998b9f78d2bSBill Paul static void
999b9f78d2bSBill Paul bfe_core_disable(struct bfe_softc *sc)
1000b9f78d2bSBill Paul {
1001b9f78d2bSBill Paul 	if ((CSR_READ_4(sc, BFE_SBTMSLOW)) & BFE_RESET)
1002b9f78d2bSBill Paul 		return;
1003b9f78d2bSBill Paul 
1004b9f78d2bSBill Paul 	/*
1005f2b1c158SJulian Elischer 	 * Set reject, wait for it set, then wait for the core to stop
1006f2b1c158SJulian Elischer 	 * being busy, then set reset and reject and enable the clocks.
1007b9f78d2bSBill Paul 	 */
1008b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_SBTMSLOW, (BFE_REJECT | BFE_CLOCK));
1009b9f78d2bSBill Paul 	bfe_wait_bit(sc, BFE_SBTMSLOW, BFE_REJECT, 1000, 0);
1010b9f78d2bSBill Paul 	bfe_wait_bit(sc, BFE_SBTMSHIGH, BFE_BUSY, 1000, 1);
1011b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_SBTMSLOW, (BFE_FGC | BFE_CLOCK | BFE_REJECT |
1012b9f78d2bSBill Paul 				BFE_RESET));
1013b9f78d2bSBill Paul 	CSR_READ_4(sc, BFE_SBTMSLOW);
1014b9f78d2bSBill Paul 	DELAY(10);
1015b9f78d2bSBill Paul 	/* Leave reset and reject set */
1016b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_SBTMSLOW, (BFE_REJECT | BFE_RESET));
1017b9f78d2bSBill Paul 	DELAY(10);
1018b9f78d2bSBill Paul }
1019b9f78d2bSBill Paul 
1020b9f78d2bSBill Paul static void
1021b9f78d2bSBill Paul bfe_core_reset(struct bfe_softc *sc)
1022b9f78d2bSBill Paul {
1023b9f78d2bSBill Paul 	u_int32_t val;
1024b9f78d2bSBill Paul 
1025b9f78d2bSBill Paul 	/* Disable the core */
1026b9f78d2bSBill Paul 	bfe_core_disable(sc);
1027b9f78d2bSBill Paul 
1028b9f78d2bSBill Paul 	/* and bring it back up */
1029b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_SBTMSLOW, (BFE_RESET | BFE_CLOCK | BFE_FGC));
1030b9f78d2bSBill Paul 	CSR_READ_4(sc, BFE_SBTMSLOW);
1031b9f78d2bSBill Paul 	DELAY(10);
1032b9f78d2bSBill Paul 
1033b9f78d2bSBill Paul 	/* Chip bug, clear SERR, IB and TO if they are set. */
1034b9f78d2bSBill Paul 	if (CSR_READ_4(sc, BFE_SBTMSHIGH) & BFE_SERR)
1035b9f78d2bSBill Paul 		CSR_WRITE_4(sc, BFE_SBTMSHIGH, 0);
1036b9f78d2bSBill Paul 	val = CSR_READ_4(sc, BFE_SBIMSTATE);
1037b9f78d2bSBill Paul 	if (val & (BFE_IBE | BFE_TO))
1038b9f78d2bSBill Paul 		CSR_WRITE_4(sc, BFE_SBIMSTATE, val & ~(BFE_IBE | BFE_TO));
1039b9f78d2bSBill Paul 
1040b9f78d2bSBill Paul 	/* Clear reset and allow it to move through the core */
1041b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_SBTMSLOW, (BFE_CLOCK | BFE_FGC));
1042b9f78d2bSBill Paul 	CSR_READ_4(sc, BFE_SBTMSLOW);
1043b9f78d2bSBill Paul 	DELAY(10);
1044b9f78d2bSBill Paul 
1045b9f78d2bSBill Paul 	/* Leave the clock set */
1046b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_SBTMSLOW, BFE_CLOCK);
1047b9f78d2bSBill Paul 	CSR_READ_4(sc, BFE_SBTMSLOW);
1048b9f78d2bSBill Paul 	DELAY(10);
1049b9f78d2bSBill Paul }
1050b9f78d2bSBill Paul 
1051b9f78d2bSBill Paul static void
1052b9f78d2bSBill Paul bfe_cam_write(struct bfe_softc *sc, u_char *data, int index)
1053b9f78d2bSBill Paul {
1054b9f78d2bSBill Paul 	u_int32_t val;
1055b9f78d2bSBill Paul 
1056b9f78d2bSBill Paul 	val  = ((u_int32_t) data[2]) << 24;
1057b9f78d2bSBill Paul 	val |= ((u_int32_t) data[3]) << 16;
1058b9f78d2bSBill Paul 	val |= ((u_int32_t) data[4]) <<  8;
1059b9f78d2bSBill Paul 	val |= ((u_int32_t) data[5]);
1060b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_CAM_DATA_LO, val);
1061b9f78d2bSBill Paul 	val = (BFE_CAM_HI_VALID |
1062b9f78d2bSBill Paul 			(((u_int32_t) data[0]) << 8) |
1063b9f78d2bSBill Paul 			(((u_int32_t) data[1])));
1064b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_CAM_DATA_HI, val);
1065b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_CAM_CTRL, (BFE_CAM_WRITE |
106662a9464cSDuncan Barclay 				((u_int32_t) index << BFE_CAM_INDEX_SHIFT)));
1067b9f78d2bSBill Paul 	bfe_wait_bit(sc, BFE_CAM_CTRL, BFE_CAM_BUSY, 10000, 1);
1068b9f78d2bSBill Paul }
1069b9f78d2bSBill Paul 
1070f0bcd699SGleb Smirnoff static u_int
1071f0bcd699SGleb Smirnoff bfe_write_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
1072f0bcd699SGleb Smirnoff {
1073f0bcd699SGleb Smirnoff 	struct bfe_softc *sc = arg;
1074f0bcd699SGleb Smirnoff 
1075f0bcd699SGleb Smirnoff 	bfe_cam_write(sc, LLADDR(sdl), cnt + 1);
1076f0bcd699SGleb Smirnoff 
1077f0bcd699SGleb Smirnoff 	return (1);
1078f0bcd699SGleb Smirnoff }
1079f0bcd699SGleb Smirnoff 
1080b9f78d2bSBill Paul static void
1081b9f78d2bSBill Paul bfe_set_rx_mode(struct bfe_softc *sc)
1082b9f78d2bSBill Paul {
1083c0e5e270SJustin Hibbits 	if_t ifp = sc->bfe_ifp;
1084b9f78d2bSBill Paul 	u_int32_t val;
1085b9f78d2bSBill Paul 
1086861cf54cSPyun YongHyeon 	BFE_LOCK_ASSERT(sc);
1087861cf54cSPyun YongHyeon 
1088b9f78d2bSBill Paul 	val = CSR_READ_4(sc, BFE_RXCONF);
1089b9f78d2bSBill Paul 
1090c0e5e270SJustin Hibbits 	if (if_getflags(ifp) & IFF_PROMISC)
1091b9f78d2bSBill Paul 		val |= BFE_RXCONF_PROMISC;
1092b9f78d2bSBill Paul 	else
1093b9f78d2bSBill Paul 		val &= ~BFE_RXCONF_PROMISC;
1094b9f78d2bSBill Paul 
1095c0e5e270SJustin Hibbits 	if (if_getflags(ifp) & IFF_BROADCAST)
1096b9f78d2bSBill Paul 		val &= ~BFE_RXCONF_DBCAST;
1097b9f78d2bSBill Paul 	else
1098b9f78d2bSBill Paul 		val |= BFE_RXCONF_DBCAST;
1099b9f78d2bSBill Paul 
1100b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_CAM_CTRL, 0);
1101c0e5e270SJustin Hibbits 	bfe_cam_write(sc, if_getlladdr(sc->bfe_ifp), 0);
1102b9f78d2bSBill Paul 
1103c0e5e270SJustin Hibbits 	if (if_getflags(ifp) & IFF_ALLMULTI)
1104b9f78d2bSBill Paul 		val |= BFE_RXCONF_ALLMULTI;
1105b9f78d2bSBill Paul 	else {
1106b9f78d2bSBill Paul 		val &= ~BFE_RXCONF_ALLMULTI;
1107f0bcd699SGleb Smirnoff 		if_foreach_llmaddr(ifp, bfe_write_maddr, sc);
1108b9f78d2bSBill Paul 	}
1109b9f78d2bSBill Paul 
1110b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_RXCONF, val);
1111b9f78d2bSBill Paul 	BFE_OR(sc, BFE_CAM_CTRL, BFE_CAM_ENABLE);
1112b9f78d2bSBill Paul }
1113b9f78d2bSBill Paul 
1114b9f78d2bSBill Paul static void
1115b9f78d2bSBill Paul bfe_dma_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
1116b9f78d2bSBill Paul {
111796ee09c5SPyun YongHyeon 	struct bfe_dmamap_arg *ctx;
1118b9f78d2bSBill Paul 
111996ee09c5SPyun YongHyeon 	if (error != 0)
112096ee09c5SPyun YongHyeon 		return;
1121b9f78d2bSBill Paul 
112296ee09c5SPyun YongHyeon 	KASSERT(nseg == 1, ("%s : %d segments returned!", __func__, nseg));
1123b9f78d2bSBill Paul 
112496ee09c5SPyun YongHyeon 	ctx = (struct bfe_dmamap_arg *)arg;
112596ee09c5SPyun YongHyeon 	ctx->bfe_busaddr = segs[0].ds_addr;
1126b9f78d2bSBill Paul }
1127b9f78d2bSBill Paul 
1128b9f78d2bSBill Paul static void
1129b9f78d2bSBill Paul bfe_release_resources(struct bfe_softc *sc)
1130b9f78d2bSBill Paul {
1131b9f78d2bSBill Paul 
1132b9f78d2bSBill Paul 	if (sc->bfe_intrhand != NULL)
113396ee09c5SPyun YongHyeon 		bus_teardown_intr(sc->bfe_dev, sc->bfe_irq, sc->bfe_intrhand);
1134b9f78d2bSBill Paul 
1135b9f78d2bSBill Paul 	if (sc->bfe_irq != NULL)
113696ee09c5SPyun YongHyeon 		bus_release_resource(sc->bfe_dev, SYS_RES_IRQ, 0, sc->bfe_irq);
1137b9f78d2bSBill Paul 
1138b9f78d2bSBill Paul 	if (sc->bfe_res != NULL)
113996ee09c5SPyun YongHyeon 		bus_release_resource(sc->bfe_dev, SYS_RES_MEMORY, PCIR_BAR(0),
114096ee09c5SPyun YongHyeon 		    sc->bfe_res);
1141b9f78d2bSBill Paul 
1142ad61f896SRuslan Ermilov 	if (sc->bfe_ifp != NULL)
1143ad61f896SRuslan Ermilov 		if_free(sc->bfe_ifp);
1144b9f78d2bSBill Paul }
1145b9f78d2bSBill Paul 
1146b9f78d2bSBill Paul static void
1147b9f78d2bSBill Paul bfe_read_eeprom(struct bfe_softc *sc, u_int8_t *data)
1148b9f78d2bSBill Paul {
1149b9f78d2bSBill Paul 	long i;
1150b9f78d2bSBill Paul 	u_int16_t *ptr = (u_int16_t *)data;
1151b9f78d2bSBill Paul 
1152b9f78d2bSBill Paul 	for(i = 0; i < 128; i += 2)
1153b9f78d2bSBill Paul 		ptr[i/2] = CSR_READ_4(sc, 4096 + i);
1154b9f78d2bSBill Paul }
1155b9f78d2bSBill Paul 
1156b9f78d2bSBill Paul static int
1157b9f78d2bSBill Paul bfe_wait_bit(struct bfe_softc *sc, u_int32_t reg, u_int32_t bit,
1158b9f78d2bSBill Paul 		u_long timeout, const int clear)
1159b9f78d2bSBill Paul {
1160b9f78d2bSBill Paul 	u_long i;
1161b9f78d2bSBill Paul 
1162b9f78d2bSBill Paul 	for (i = 0; i < timeout; i++) {
1163b9f78d2bSBill Paul 		u_int32_t val = CSR_READ_4(sc, reg);
1164b9f78d2bSBill Paul 
1165b9f78d2bSBill Paul 		if (clear && !(val & bit))
1166b9f78d2bSBill Paul 			break;
1167b9f78d2bSBill Paul 		if (!clear && (val & bit))
1168b9f78d2bSBill Paul 			break;
1169b9f78d2bSBill Paul 		DELAY(10);
1170b9f78d2bSBill Paul 	}
1171b9f78d2bSBill Paul 	if (i == timeout) {
1172be280562SPyun YongHyeon 		device_printf(sc->bfe_dev,
1173be280562SPyun YongHyeon 		    "BUG!  Timeout waiting for bit %08x of register "
1174be280562SPyun YongHyeon 		    "%x to %s.\n", bit, reg, (clear ? "clear" : "set"));
117527de24a7SDag-Erling Smørgrav 		return (-1);
1176b9f78d2bSBill Paul 	}
117727de24a7SDag-Erling Smørgrav 	return (0);
1178b9f78d2bSBill Paul }
1179b9f78d2bSBill Paul 
1180b9f78d2bSBill Paul static int
1181b9f78d2bSBill Paul bfe_readphy(struct bfe_softc *sc, u_int32_t reg, u_int32_t *val)
1182b9f78d2bSBill Paul {
1183b9f78d2bSBill Paul 	int err;
1184b9f78d2bSBill Paul 
1185b9f78d2bSBill Paul 	/* Clear MII ISR */
1186b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_EMAC_ISTAT, BFE_EMAC_INT_MII);
1187b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_MDIO_DATA, (BFE_MDIO_SB_START |
1188b9f78d2bSBill Paul 				(BFE_MDIO_OP_READ << BFE_MDIO_OP_SHIFT) |
1189b9f78d2bSBill Paul 				(sc->bfe_phyaddr << BFE_MDIO_PMD_SHIFT) |
1190b9f78d2bSBill Paul 				(reg << BFE_MDIO_RA_SHIFT) |
1191b9f78d2bSBill Paul 				(BFE_MDIO_TA_VALID << BFE_MDIO_TA_SHIFT)));
1192b9f78d2bSBill Paul 	err = bfe_wait_bit(sc, BFE_EMAC_ISTAT, BFE_EMAC_INT_MII, 100, 0);
1193b9f78d2bSBill Paul 	*val = CSR_READ_4(sc, BFE_MDIO_DATA) & BFE_MDIO_DATA_DATA;
1194b9f78d2bSBill Paul 
119527de24a7SDag-Erling Smørgrav 	return (err);
1196b9f78d2bSBill Paul }
1197b9f78d2bSBill Paul 
1198b9f78d2bSBill Paul static int
1199b9f78d2bSBill Paul bfe_writephy(struct bfe_softc *sc, u_int32_t reg, u_int32_t val)
1200b9f78d2bSBill Paul {
1201b9f78d2bSBill Paul 	int status;
1202b9f78d2bSBill Paul 
1203b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_EMAC_ISTAT, BFE_EMAC_INT_MII);
1204b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_MDIO_DATA, (BFE_MDIO_SB_START |
1205b9f78d2bSBill Paul 				(BFE_MDIO_OP_WRITE << BFE_MDIO_OP_SHIFT) |
1206b9f78d2bSBill Paul 				(sc->bfe_phyaddr << BFE_MDIO_PMD_SHIFT) |
1207b9f78d2bSBill Paul 				(reg << BFE_MDIO_RA_SHIFT) |
1208b9f78d2bSBill Paul 				(BFE_MDIO_TA_VALID << BFE_MDIO_TA_SHIFT) |
1209b9f78d2bSBill Paul 				(val & BFE_MDIO_DATA_DATA)));
1210b9f78d2bSBill Paul 	status = bfe_wait_bit(sc, BFE_EMAC_ISTAT, BFE_EMAC_INT_MII, 100, 0);
1211b9f78d2bSBill Paul 
121227de24a7SDag-Erling Smørgrav 	return (status);
1213b9f78d2bSBill Paul }
1214b9f78d2bSBill Paul 
1215b9f78d2bSBill Paul /*
1216b9f78d2bSBill Paul  * XXX - I think this is handled by the PHY driver, but it can't hurt to do it
1217b9f78d2bSBill Paul  * twice
1218b9f78d2bSBill Paul  */
1219b9f78d2bSBill Paul static int
1220b9f78d2bSBill Paul bfe_setupphy(struct bfe_softc *sc)
1221b9f78d2bSBill Paul {
1222b9f78d2bSBill Paul 	u_int32_t val;
1223b9f78d2bSBill Paul 
1224b9f78d2bSBill Paul 	/* Enable activity LED */
1225b9f78d2bSBill Paul 	bfe_readphy(sc, 26, &val);
1226b9f78d2bSBill Paul 	bfe_writephy(sc, 26, val & 0x7fff);
1227b9f78d2bSBill Paul 	bfe_readphy(sc, 26, &val);
1228b9f78d2bSBill Paul 
1229b9f78d2bSBill Paul 	/* Enable traffic meter LED mode */
1230b9f78d2bSBill Paul 	bfe_readphy(sc, 27, &val);
1231b9f78d2bSBill Paul 	bfe_writephy(sc, 27, val | (1 << 6));
1232b9f78d2bSBill Paul 
123327de24a7SDag-Erling Smørgrav 	return (0);
1234b9f78d2bSBill Paul }
1235b9f78d2bSBill Paul 
1236b9f78d2bSBill Paul static void
1237b9f78d2bSBill Paul bfe_stats_update(struct bfe_softc *sc)
1238b9f78d2bSBill Paul {
1239861cf54cSPyun YongHyeon 	struct bfe_hw_stats *stats;
1240c0e5e270SJustin Hibbits 	if_t ifp;
1241861cf54cSPyun YongHyeon 	uint32_t mib[BFE_MIB_CNT];
1242861cf54cSPyun YongHyeon 	uint32_t reg, *val;
1243b9f78d2bSBill Paul 
1244861cf54cSPyun YongHyeon 	BFE_LOCK_ASSERT(sc);
1245861cf54cSPyun YongHyeon 
1246861cf54cSPyun YongHyeon 	val = mib;
1247861cf54cSPyun YongHyeon 	CSR_WRITE_4(sc, BFE_MIB_CTRL, BFE_MIB_CLR_ON_READ);
1248861cf54cSPyun YongHyeon 	for (reg = BFE_TX_GOOD_O; reg <= BFE_TX_PAUSE; reg += 4)
1249861cf54cSPyun YongHyeon 		*val++ = CSR_READ_4(sc, reg);
1250861cf54cSPyun YongHyeon 	for (reg = BFE_RX_GOOD_O; reg <= BFE_RX_NPAUSE; reg += 4)
1251861cf54cSPyun YongHyeon 		*val++ = CSR_READ_4(sc, reg);
1252861cf54cSPyun YongHyeon 
1253861cf54cSPyun YongHyeon 	ifp = sc->bfe_ifp;
1254861cf54cSPyun YongHyeon 	stats = &sc->bfe_stats;
1255861cf54cSPyun YongHyeon 	/* Tx stat. */
1256861cf54cSPyun YongHyeon 	stats->tx_good_octets += mib[MIB_TX_GOOD_O];
1257861cf54cSPyun YongHyeon 	stats->tx_good_frames += mib[MIB_TX_GOOD_P];
1258861cf54cSPyun YongHyeon 	stats->tx_octets += mib[MIB_TX_O];
1259861cf54cSPyun YongHyeon 	stats->tx_frames += mib[MIB_TX_P];
1260861cf54cSPyun YongHyeon 	stats->tx_bcast_frames += mib[MIB_TX_BCAST];
1261861cf54cSPyun YongHyeon 	stats->tx_mcast_frames += mib[MIB_TX_MCAST];
1262861cf54cSPyun YongHyeon 	stats->tx_pkts_64 += mib[MIB_TX_64];
1263861cf54cSPyun YongHyeon 	stats->tx_pkts_65_127 += mib[MIB_TX_65_127];
1264861cf54cSPyun YongHyeon 	stats->tx_pkts_128_255 += mib[MIB_TX_128_255];
1265861cf54cSPyun YongHyeon 	stats->tx_pkts_256_511 += mib[MIB_TX_256_511];
1266861cf54cSPyun YongHyeon 	stats->tx_pkts_512_1023 += mib[MIB_TX_512_1023];
1267861cf54cSPyun YongHyeon 	stats->tx_pkts_1024_max += mib[MIB_TX_1024_MAX];
1268861cf54cSPyun YongHyeon 	stats->tx_jabbers += mib[MIB_TX_JABBER];
1269861cf54cSPyun YongHyeon 	stats->tx_oversize_frames += mib[MIB_TX_OSIZE];
1270861cf54cSPyun YongHyeon 	stats->tx_frag_frames += mib[MIB_TX_FRAG];
1271861cf54cSPyun YongHyeon 	stats->tx_underruns += mib[MIB_TX_URUNS];
1272861cf54cSPyun YongHyeon 	stats->tx_colls += mib[MIB_TX_TCOLS];
1273861cf54cSPyun YongHyeon 	stats->tx_single_colls += mib[MIB_TX_SCOLS];
1274861cf54cSPyun YongHyeon 	stats->tx_multi_colls += mib[MIB_TX_MCOLS];
1275861cf54cSPyun YongHyeon 	stats->tx_excess_colls += mib[MIB_TX_ECOLS];
1276861cf54cSPyun YongHyeon 	stats->tx_late_colls += mib[MIB_TX_LCOLS];
1277861cf54cSPyun YongHyeon 	stats->tx_deferrals += mib[MIB_TX_DEFERED];
1278861cf54cSPyun YongHyeon 	stats->tx_carrier_losts += mib[MIB_TX_CLOST];
1279861cf54cSPyun YongHyeon 	stats->tx_pause_frames += mib[MIB_TX_PAUSE];
1280861cf54cSPyun YongHyeon 	/* Rx stat. */
1281861cf54cSPyun YongHyeon 	stats->rx_good_octets += mib[MIB_RX_GOOD_O];
1282861cf54cSPyun YongHyeon 	stats->rx_good_frames += mib[MIB_RX_GOOD_P];
1283861cf54cSPyun YongHyeon 	stats->rx_octets += mib[MIB_RX_O];
1284861cf54cSPyun YongHyeon 	stats->rx_frames += mib[MIB_RX_P];
1285861cf54cSPyun YongHyeon 	stats->rx_bcast_frames += mib[MIB_RX_BCAST];
1286861cf54cSPyun YongHyeon 	stats->rx_mcast_frames += mib[MIB_RX_MCAST];
1287861cf54cSPyun YongHyeon 	stats->rx_pkts_64 += mib[MIB_RX_64];
1288861cf54cSPyun YongHyeon 	stats->rx_pkts_65_127 += mib[MIB_RX_65_127];
1289861cf54cSPyun YongHyeon 	stats->rx_pkts_128_255 += mib[MIB_RX_128_255];
1290861cf54cSPyun YongHyeon 	stats->rx_pkts_256_511 += mib[MIB_RX_256_511];
1291861cf54cSPyun YongHyeon 	stats->rx_pkts_512_1023 += mib[MIB_RX_512_1023];
1292861cf54cSPyun YongHyeon 	stats->rx_pkts_1024_max += mib[MIB_RX_1024_MAX];
1293861cf54cSPyun YongHyeon 	stats->rx_jabbers += mib[MIB_RX_JABBER];
1294861cf54cSPyun YongHyeon 	stats->rx_oversize_frames += mib[MIB_RX_OSIZE];
1295861cf54cSPyun YongHyeon 	stats->rx_frag_frames += mib[MIB_RX_FRAG];
1296861cf54cSPyun YongHyeon 	stats->rx_missed_frames += mib[MIB_RX_MISS];
1297861cf54cSPyun YongHyeon 	stats->rx_crc_align_errs += mib[MIB_RX_CRCA];
1298861cf54cSPyun YongHyeon 	stats->rx_runts += mib[MIB_RX_USIZE];
1299861cf54cSPyun YongHyeon 	stats->rx_crc_errs += mib[MIB_RX_CRC];
1300861cf54cSPyun YongHyeon 	stats->rx_align_errs += mib[MIB_RX_ALIGN];
1301861cf54cSPyun YongHyeon 	stats->rx_symbol_errs += mib[MIB_RX_SYM];
1302861cf54cSPyun YongHyeon 	stats->rx_pause_frames += mib[MIB_RX_PAUSE];
1303861cf54cSPyun YongHyeon 	stats->rx_control_frames += mib[MIB_RX_NPAUSE];
1304861cf54cSPyun YongHyeon 
1305861cf54cSPyun YongHyeon 	/* Update counters in ifnet. */
130610b77d66SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_OPACKETS, (u_long)mib[MIB_TX_GOOD_P]);
130710b77d66SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_COLLISIONS, (u_long)mib[MIB_TX_TCOLS]);
130810b77d66SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_OERRORS, (u_long)mib[MIB_TX_URUNS] +
1309861cf54cSPyun YongHyeon 	    (u_long)mib[MIB_TX_ECOLS] +
1310861cf54cSPyun YongHyeon 	    (u_long)mib[MIB_TX_DEFERED] +
131110b77d66SGleb Smirnoff 	    (u_long)mib[MIB_TX_CLOST]);
1312861cf54cSPyun YongHyeon 
131310b77d66SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_IPACKETS, (u_long)mib[MIB_RX_GOOD_P]);
1314861cf54cSPyun YongHyeon 
131510b77d66SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_IERRORS, mib[MIB_RX_JABBER] +
1316861cf54cSPyun YongHyeon 	    mib[MIB_RX_MISS] +
1317861cf54cSPyun YongHyeon 	    mib[MIB_RX_CRCA] +
1318861cf54cSPyun YongHyeon 	    mib[MIB_RX_USIZE] +
1319861cf54cSPyun YongHyeon 	    mib[MIB_RX_CRC] +
1320861cf54cSPyun YongHyeon 	    mib[MIB_RX_ALIGN] +
132110b77d66SGleb Smirnoff 	    mib[MIB_RX_SYM]);
1322b9f78d2bSBill Paul }
1323b9f78d2bSBill Paul 
1324b9f78d2bSBill Paul static void
1325b9f78d2bSBill Paul bfe_txeof(struct bfe_softc *sc)
1326b9f78d2bSBill Paul {
132796ee09c5SPyun YongHyeon 	struct bfe_tx_data *r;
1328c0e5e270SJustin Hibbits 	if_t ifp;
1329b9f78d2bSBill Paul 	int i, chipidx;
1330b9f78d2bSBill Paul 
1331f16b4811SMike Makonnen 	BFE_LOCK_ASSERT(sc);
1332b9f78d2bSBill Paul 
1333fc74a9f9SBrooks Davis 	ifp = sc->bfe_ifp;
1334b9f78d2bSBill Paul 
1335b9f78d2bSBill Paul 	chipidx = CSR_READ_4(sc, BFE_DMATX_STAT) & BFE_STAT_CDMASK;
1336b9f78d2bSBill Paul 	chipidx /= sizeof(struct bfe_desc);
1337b9f78d2bSBill Paul 
1338b9f78d2bSBill Paul 	i = sc->bfe_tx_cons;
133996ee09c5SPyun YongHyeon 	if (i == chipidx)
134096ee09c5SPyun YongHyeon 		return;
134196ee09c5SPyun YongHyeon 	bus_dmamap_sync(sc->bfe_tx_tag, sc->bfe_tx_map,
134296ee09c5SPyun YongHyeon 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1343b9f78d2bSBill Paul 	/* Go through the mbufs and free those that have been transmitted */
134496ee09c5SPyun YongHyeon 	for (; i != chipidx; BFE_INC(i, BFE_TX_LIST_CNT)) {
134596ee09c5SPyun YongHyeon 		r = &sc->bfe_tx_ring[i];
134696ee09c5SPyun YongHyeon 		sc->bfe_tx_cnt--;
134796ee09c5SPyun YongHyeon 		if (r->bfe_mbuf == NULL)
134896ee09c5SPyun YongHyeon 			continue;
134996ee09c5SPyun YongHyeon 		bus_dmamap_sync(sc->bfe_txmbuf_tag, r->bfe_map,
135096ee09c5SPyun YongHyeon 		    BUS_DMASYNC_POSTWRITE);
135196ee09c5SPyun YongHyeon 		bus_dmamap_unload(sc->bfe_txmbuf_tag, r->bfe_map);
135296ee09c5SPyun YongHyeon 
1353b9f78d2bSBill Paul 		m_freem(r->bfe_mbuf);
1354b9f78d2bSBill Paul 		r->bfe_mbuf = NULL;
1355b9f78d2bSBill Paul 	}
1356b9f78d2bSBill Paul 
1357b9f78d2bSBill Paul 	if (i != sc->bfe_tx_cons) {
1358b9f78d2bSBill Paul 		/* we freed up some mbufs */
1359b9f78d2bSBill Paul 		sc->bfe_tx_cons = i;
1360c0e5e270SJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
1361b9f78d2bSBill Paul 	}
13626ceb40baSPyun YongHyeon 
1363b9f78d2bSBill Paul 	if (sc->bfe_tx_cnt == 0)
13646ceb40baSPyun YongHyeon 		sc->bfe_watchdog_timer = 0;
1365b9f78d2bSBill Paul }
1366b9f78d2bSBill Paul 
1367b9f78d2bSBill Paul /* Pass a received packet up the stack */
1368b9f78d2bSBill Paul static void
1369b9f78d2bSBill Paul bfe_rxeof(struct bfe_softc *sc)
1370b9f78d2bSBill Paul {
1371b9f78d2bSBill Paul 	struct mbuf *m;
1372c0e5e270SJustin Hibbits 	if_t ifp;
1373b9f78d2bSBill Paul 	struct bfe_rxheader *rxheader;
137496ee09c5SPyun YongHyeon 	struct bfe_rx_data *r;
137596ee09c5SPyun YongHyeon 	int cons, prog;
1376b9f78d2bSBill Paul 	u_int32_t status, current, len, flags;
1377b9f78d2bSBill Paul 
1378f16b4811SMike Makonnen 	BFE_LOCK_ASSERT(sc);
1379b9f78d2bSBill Paul 	cons = sc->bfe_rx_cons;
1380b9f78d2bSBill Paul 	status = CSR_READ_4(sc, BFE_DMARX_STAT);
1381b9f78d2bSBill Paul 	current = (status & BFE_STAT_CDMASK) / sizeof(struct bfe_desc);
1382b9f78d2bSBill Paul 
1383fc74a9f9SBrooks Davis 	ifp = sc->bfe_ifp;
1384b9f78d2bSBill Paul 
138596ee09c5SPyun YongHyeon 	bus_dmamap_sync(sc->bfe_rx_tag, sc->bfe_rx_map,
138696ee09c5SPyun YongHyeon 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
138796ee09c5SPyun YongHyeon 
138896ee09c5SPyun YongHyeon 	for (prog = 0; current != cons; prog++,
138996ee09c5SPyun YongHyeon 	    BFE_INC(cons, BFE_RX_LIST_CNT)) {
1390b9f78d2bSBill Paul 		r = &sc->bfe_rx_ring[cons];
1391b9f78d2bSBill Paul 		m = r->bfe_mbuf;
139296ee09c5SPyun YongHyeon 		/*
139396ee09c5SPyun YongHyeon 		 * Rx status should be read from mbuf such that we can't
139496ee09c5SPyun YongHyeon 		 * delay bus_dmamap_sync(9). This hardware limiation
1395f697fe65SGordon Bergling 		 * results in inefficient mbuf usage as bfe(4) couldn't
139696ee09c5SPyun YongHyeon 		 * reuse mapped buffer from errored frame.
139796ee09c5SPyun YongHyeon 		 */
139896ee09c5SPyun YongHyeon 		if (bfe_list_newbuf(sc, cons) != 0) {
139910b77d66SGleb Smirnoff 			if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
140096ee09c5SPyun YongHyeon 			bfe_discard_buf(sc, cons);
140196ee09c5SPyun YongHyeon 			continue;
140296ee09c5SPyun YongHyeon 		}
1403b9f78d2bSBill Paul 		rxheader = mtod(m, struct bfe_rxheader*);
140496ee09c5SPyun YongHyeon 		len = le16toh(rxheader->len);
140596ee09c5SPyun YongHyeon 		flags = le16toh(rxheader->flags);
1406b9f78d2bSBill Paul 
140796ee09c5SPyun YongHyeon 		/* Remove CRC bytes. */
1408b9f78d2bSBill Paul 		len -= ETHER_CRC_LEN;
1409b9f78d2bSBill Paul 
1410b9f78d2bSBill Paul 		/* flag an error and try again */
1411b9f78d2bSBill Paul 		if ((len > ETHER_MAX_LEN+32) || (flags & BFE_RX_FLAG_ERRORS)) {
141296ee09c5SPyun YongHyeon 			m_freem(m);
1413b9f78d2bSBill Paul 			continue;
1414b9f78d2bSBill Paul 		}
1415b9f78d2bSBill Paul 
141696ee09c5SPyun YongHyeon 		/* Make sure to skip header bytes written by hardware. */
1417b9f78d2bSBill Paul 		m_adj(m, BFE_RX_OFFSET);
1418b9f78d2bSBill Paul 		m->m_len = m->m_pkthdr.len = len;
1419b9f78d2bSBill Paul 
1420b9f78d2bSBill Paul 		m->m_pkthdr.rcvif = ifp;
14215120abbfSSam Leffler 		BFE_UNLOCK(sc);
1422c0e5e270SJustin Hibbits 		if_input(ifp, m);
14235120abbfSSam Leffler 		BFE_LOCK(sc);
1424b9f78d2bSBill Paul 	}
142596ee09c5SPyun YongHyeon 
142696ee09c5SPyun YongHyeon 	if (prog > 0) {
1427b9f78d2bSBill Paul 		sc->bfe_rx_cons = cons;
142896ee09c5SPyun YongHyeon 		bus_dmamap_sync(sc->bfe_rx_tag, sc->bfe_rx_map,
142996ee09c5SPyun YongHyeon 		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
143096ee09c5SPyun YongHyeon 	}
1431b9f78d2bSBill Paul }
1432b9f78d2bSBill Paul 
1433b9f78d2bSBill Paul static void
1434b9f78d2bSBill Paul bfe_intr(void *xsc)
1435b9f78d2bSBill Paul {
1436b9f78d2bSBill Paul 	struct bfe_softc *sc = xsc;
1437c0e5e270SJustin Hibbits 	if_t ifp;
1438861cf54cSPyun YongHyeon 	u_int32_t istat;
1439b9f78d2bSBill Paul 
1440fc74a9f9SBrooks Davis 	ifp = sc->bfe_ifp;
1441b9f78d2bSBill Paul 
1442b9f78d2bSBill Paul 	BFE_LOCK(sc);
1443b9f78d2bSBill Paul 
1444b9f78d2bSBill Paul 	istat = CSR_READ_4(sc, BFE_ISTAT);
1445b9f78d2bSBill Paul 
1446b9f78d2bSBill Paul 	/*
1447b9f78d2bSBill Paul 	 * Defer unsolicited interrupts - This is necessary because setting the
1448b9f78d2bSBill Paul 	 * chips interrupt mask register to 0 doesn't actually stop the
1449b9f78d2bSBill Paul 	 * interrupts
1450b9f78d2bSBill Paul 	 */
14512be30c0dSPyun YongHyeon 	istat &= BFE_IMASK_DEF;
1452b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_ISTAT, istat);
1453b9f78d2bSBill Paul 	CSR_READ_4(sc, BFE_ISTAT);
1454b9f78d2bSBill Paul 
1455b9f78d2bSBill Paul 	/* not expecting this interrupt, disregard it */
1456c0e5e270SJustin Hibbits 	if (istat == 0 || (if_getdrvflags(ifp) & IFF_DRV_RUNNING) == 0) {
1457b9f78d2bSBill Paul 		BFE_UNLOCK(sc);
1458b9f78d2bSBill Paul 		return;
1459b9f78d2bSBill Paul 	}
1460b9f78d2bSBill Paul 
1461861cf54cSPyun YongHyeon 	/* A packet was received */
1462861cf54cSPyun YongHyeon 	if (istat & BFE_ISTAT_RX)
1463861cf54cSPyun YongHyeon 		bfe_rxeof(sc);
1464861cf54cSPyun YongHyeon 
1465861cf54cSPyun YongHyeon 	/* A packet was sent */
1466861cf54cSPyun YongHyeon 	if (istat & BFE_ISTAT_TX)
1467861cf54cSPyun YongHyeon 		bfe_txeof(sc);
1468861cf54cSPyun YongHyeon 
1469b9f78d2bSBill Paul 	if (istat & BFE_ISTAT_ERRORS) {
1470678d2a9aSMike Silbersack 		if (istat & BFE_ISTAT_DSCE) {
1471be280562SPyun YongHyeon 			device_printf(sc->bfe_dev, "Descriptor Error\n");
1472678d2a9aSMike Silbersack 			bfe_stop(sc);
1473678d2a9aSMike Silbersack 			BFE_UNLOCK(sc);
1474678d2a9aSMike Silbersack 			return;
1475678d2a9aSMike Silbersack 		}
1476678d2a9aSMike Silbersack 
1477678d2a9aSMike Silbersack 		if (istat & BFE_ISTAT_DPE) {
1478be280562SPyun YongHyeon 			device_printf(sc->bfe_dev,
1479be280562SPyun YongHyeon 			    "Descriptor Protocol Error\n");
1480678d2a9aSMike Silbersack 			bfe_stop(sc);
1481678d2a9aSMike Silbersack 			BFE_UNLOCK(sc);
1482678d2a9aSMike Silbersack 			return;
1483678d2a9aSMike Silbersack 		}
1484c0e5e270SJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
1485f16b4811SMike Makonnen 		bfe_init_locked(sc);
1486b9f78d2bSBill Paul 	}
1487b9f78d2bSBill Paul 
1488b9f78d2bSBill Paul 	/* We have packets pending, fire them out */
1489c0e5e270SJustin Hibbits 	if (!if_sendq_empty(ifp))
1490f16b4811SMike Makonnen 		bfe_start_locked(ifp);
1491b9f78d2bSBill Paul 
1492b9f78d2bSBill Paul 	BFE_UNLOCK(sc);
1493b9f78d2bSBill Paul }
1494b9f78d2bSBill Paul 
1495b9f78d2bSBill Paul static int
149696ee09c5SPyun YongHyeon bfe_encap(struct bfe_softc *sc, struct mbuf **m_head)
1497b9f78d2bSBill Paul {
149896ee09c5SPyun YongHyeon 	struct bfe_desc *d;
149996ee09c5SPyun YongHyeon 	struct bfe_tx_data *r, *r1;
1500b9f78d2bSBill Paul 	struct mbuf *m;
150196ee09c5SPyun YongHyeon 	bus_dmamap_t map;
150296ee09c5SPyun YongHyeon 	bus_dma_segment_t txsegs[BFE_MAXTXSEGS];
150396ee09c5SPyun YongHyeon 	uint32_t cur, si;
150496ee09c5SPyun YongHyeon 	int error, i, nsegs;
1505b9f78d2bSBill Paul 
150696ee09c5SPyun YongHyeon 	BFE_LOCK_ASSERT(sc);
1507b9f78d2bSBill Paul 
150896ee09c5SPyun YongHyeon 	M_ASSERTPKTHDR((*m_head));
1509b9f78d2bSBill Paul 
151096ee09c5SPyun YongHyeon 	si = cur = sc->bfe_tx_prod;
151196ee09c5SPyun YongHyeon 	r = &sc->bfe_tx_ring[cur];
151296ee09c5SPyun YongHyeon 	error = bus_dmamap_load_mbuf_sg(sc->bfe_txmbuf_tag, r->bfe_map, *m_head,
151396ee09c5SPyun YongHyeon 	    txsegs, &nsegs, 0);
151496ee09c5SPyun YongHyeon 	if (error == EFBIG) {
1515c6499eccSGleb Smirnoff 		m = m_collapse(*m_head, M_NOWAIT, BFE_MAXTXSEGS);
151696ee09c5SPyun YongHyeon 		if (m == NULL) {
151796ee09c5SPyun YongHyeon 			m_freem(*m_head);
151896ee09c5SPyun YongHyeon 			*m_head = NULL;
151996ee09c5SPyun YongHyeon 			return (ENOMEM);
152096ee09c5SPyun YongHyeon 		}
15215511c4d6SMike Silbersack 		*m_head = m;
152296ee09c5SPyun YongHyeon 		error = bus_dmamap_load_mbuf_sg(sc->bfe_txmbuf_tag, r->bfe_map,
152396ee09c5SPyun YongHyeon 		    *m_head, txsegs, &nsegs, 0);
152496ee09c5SPyun YongHyeon 		if (error != 0) {
152596ee09c5SPyun YongHyeon 			m_freem(*m_head);
152696ee09c5SPyun YongHyeon 			*m_head = NULL;
152796ee09c5SPyun YongHyeon 			return (error);
152896ee09c5SPyun YongHyeon 		}
152996ee09c5SPyun YongHyeon 	} else if (error != 0)
153096ee09c5SPyun YongHyeon 		return (error);
153196ee09c5SPyun YongHyeon 	if (nsegs == 0) {
153296ee09c5SPyun YongHyeon 		m_freem(*m_head);
153396ee09c5SPyun YongHyeon 		*m_head = NULL;
153496ee09c5SPyun YongHyeon 		return (EIO);
1535b9f78d2bSBill Paul 	}
1536b9f78d2bSBill Paul 
153796ee09c5SPyun YongHyeon 	if (sc->bfe_tx_cnt + nsegs > BFE_TX_LIST_CNT - 1) {
153896ee09c5SPyun YongHyeon 		bus_dmamap_unload(sc->bfe_txmbuf_tag, r->bfe_map);
1539b9f78d2bSBill Paul 		return (ENOBUFS);
154096ee09c5SPyun YongHyeon 	}
1541b9f78d2bSBill Paul 
154296ee09c5SPyun YongHyeon 	for (i = 0; i < nsegs; i++) {
1543b9f78d2bSBill Paul 		d = &sc->bfe_tx_list[cur];
154496ee09c5SPyun YongHyeon 		d->bfe_ctrl = htole32(txsegs[i].ds_len & BFE_DESC_LEN);
154596ee09c5SPyun YongHyeon 		d->bfe_ctrl |= htole32(BFE_DESC_IOC);
1546b9f78d2bSBill Paul 		if (cur == BFE_TX_LIST_CNT - 1)
1547f2b1c158SJulian Elischer 			/*
1548f2b1c158SJulian Elischer 			 * Tell the chip to wrap to the start of
154996ee09c5SPyun YongHyeon 			 * the descriptor list.
1550f2b1c158SJulian Elischer 			 */
155196ee09c5SPyun YongHyeon 			d->bfe_ctrl |= htole32(BFE_DESC_EOT);
155296ee09c5SPyun YongHyeon 		/* The chip needs all addresses to be added to BFE_PCI_DMA. */
155396ee09c5SPyun YongHyeon 		d->bfe_addr = htole32(BFE_ADDR_LO(txsegs[i].ds_addr) +
155496ee09c5SPyun YongHyeon 		    BFE_PCI_DMA);
1555b9f78d2bSBill Paul 		BFE_INC(cur, BFE_TX_LIST_CNT);
1556b9f78d2bSBill Paul 	}
1557b9f78d2bSBill Paul 
155896ee09c5SPyun YongHyeon 	/* Update producer index. */
155996ee09c5SPyun YongHyeon 	sc->bfe_tx_prod = cur;
1560b9f78d2bSBill Paul 
156196ee09c5SPyun YongHyeon 	/* Set EOF on the last descriptor. */
156296ee09c5SPyun YongHyeon 	cur = (cur + BFE_TX_LIST_CNT - 1) % BFE_TX_LIST_CNT;
156396ee09c5SPyun YongHyeon 	d = &sc->bfe_tx_list[cur];
156496ee09c5SPyun YongHyeon 	d->bfe_ctrl |= htole32(BFE_DESC_EOF);
1565b9f78d2bSBill Paul 
156696ee09c5SPyun YongHyeon 	/* Lastly set SOF on the first descriptor to avoid races. */
156796ee09c5SPyun YongHyeon 	d = &sc->bfe_tx_list[si];
156896ee09c5SPyun YongHyeon 	d->bfe_ctrl |= htole32(BFE_DESC_SOF);
156996ee09c5SPyun YongHyeon 
157096ee09c5SPyun YongHyeon 	r1 = &sc->bfe_tx_ring[cur];
157196ee09c5SPyun YongHyeon 	map = r->bfe_map;
157296ee09c5SPyun YongHyeon 	r->bfe_map = r1->bfe_map;
157396ee09c5SPyun YongHyeon 	r1->bfe_map = map;
157496ee09c5SPyun YongHyeon 	r1->bfe_mbuf = *m_head;
157596ee09c5SPyun YongHyeon 	sc->bfe_tx_cnt += nsegs;
157696ee09c5SPyun YongHyeon 
157796ee09c5SPyun YongHyeon 	bus_dmamap_sync(sc->bfe_txmbuf_tag, map, BUS_DMASYNC_PREWRITE);
157896ee09c5SPyun YongHyeon 
1579b9f78d2bSBill Paul 	return (0);
1580b9f78d2bSBill Paul }
1581b9f78d2bSBill Paul 
1582b9f78d2bSBill Paul /*
1583f16b4811SMike Makonnen  * Set up to transmit a packet.
1584b9f78d2bSBill Paul  */
1585b9f78d2bSBill Paul static void
1586c0e5e270SJustin Hibbits bfe_start(if_t ifp)
1587b9f78d2bSBill Paul {
1588c0e5e270SJustin Hibbits 	BFE_LOCK((struct bfe_softc *)if_getsoftc(ifp));
1589f16b4811SMike Makonnen 	bfe_start_locked(ifp);
1590c0e5e270SJustin Hibbits 	BFE_UNLOCK((struct bfe_softc *)if_getsoftc(ifp));
1591f16b4811SMike Makonnen }
1592f16b4811SMike Makonnen 
1593f16b4811SMike Makonnen /*
1594f16b4811SMike Makonnen  * Set up to transmit a packet. The softc is already locked.
1595f16b4811SMike Makonnen  */
1596f16b4811SMike Makonnen static void
1597c0e5e270SJustin Hibbits bfe_start_locked(if_t ifp)
1598f16b4811SMike Makonnen {
1599b9f78d2bSBill Paul 	struct bfe_softc *sc;
160096ee09c5SPyun YongHyeon 	struct mbuf *m_head;
160196ee09c5SPyun YongHyeon 	int queued;
1602b9f78d2bSBill Paul 
1603c0e5e270SJustin Hibbits 	sc = if_getsoftc(ifp);
1604b9f78d2bSBill Paul 
1605f16b4811SMike Makonnen 	BFE_LOCK_ASSERT(sc);
1606b9f78d2bSBill Paul 
1607b9f78d2bSBill Paul 	/*
1608f2b1c158SJulian Elischer 	 * Not much point trying to send if the link is down
1609f2b1c158SJulian Elischer 	 * or we have nothing to send.
1610b9f78d2bSBill Paul 	 */
1611c0e5e270SJustin Hibbits 	if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
161249bbfbc5SPyun YongHyeon 	    IFF_DRV_RUNNING || (sc->bfe_flags & BFE_FLAG_LINK) == 0)
1613b9f78d2bSBill Paul 		return;
1614b9f78d2bSBill Paul 
1615c0e5e270SJustin Hibbits 	for (queued = 0; !if_sendq_empty(ifp) &&
161696ee09c5SPyun YongHyeon 	    sc->bfe_tx_cnt < BFE_TX_LIST_CNT - 1;) {
1617c0e5e270SJustin Hibbits 		m_head = if_dequeue(ifp);
1618b9f78d2bSBill Paul 		if (m_head == NULL)
1619b9f78d2bSBill Paul 			break;
1620b9f78d2bSBill Paul 
1621b9f78d2bSBill Paul 		/*
1622f2b1c158SJulian Elischer 		 * Pack the data into the tx ring.  If we dont have
1623f2b1c158SJulian Elischer 		 * enough room, let the chip drain the ring.
1624b9f78d2bSBill Paul 		 */
162596ee09c5SPyun YongHyeon 		if (bfe_encap(sc, &m_head)) {
162696ee09c5SPyun YongHyeon 			if (m_head == NULL)
162796ee09c5SPyun YongHyeon 				break;
1628c0e5e270SJustin Hibbits 			if_sendq_prepend(ifp, m_head);
1629c0e5e270SJustin Hibbits 			if_setdrvflagbits(ifp, IFF_DRV_OACTIVE, 0);
1630b9f78d2bSBill Paul 			break;
1631b9f78d2bSBill Paul 		}
1632b9f78d2bSBill Paul 
163322d0ab2eSMax Laier 		queued++;
163422d0ab2eSMax Laier 
1635b9f78d2bSBill Paul 		/*
1636b9f78d2bSBill Paul 		 * If there's a BPF listener, bounce a copy of this frame
1637b9f78d2bSBill Paul 		 * to him.
1638b9f78d2bSBill Paul 		 */
1639b9f78d2bSBill Paul 		BPF_MTAP(ifp, m_head);
1640b9f78d2bSBill Paul 	}
1641b9f78d2bSBill Paul 
164222d0ab2eSMax Laier 	if (queued) {
164396ee09c5SPyun YongHyeon 		bus_dmamap_sync(sc->bfe_tx_tag, sc->bfe_tx_map,
164496ee09c5SPyun YongHyeon 		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1645b9f78d2bSBill Paul 		/* Transmit - twice due to apparent hardware bug */
164696ee09c5SPyun YongHyeon 		CSR_WRITE_4(sc, BFE_DMATX_PTR,
164796ee09c5SPyun YongHyeon 		    sc->bfe_tx_prod * sizeof(struct bfe_desc));
164896ee09c5SPyun YongHyeon 		/*
164996ee09c5SPyun YongHyeon 		 * XXX It seems the following write is not necessary
165096ee09c5SPyun YongHyeon 		 * to kick Tx command. What might be required would be
165196ee09c5SPyun YongHyeon 		 * a way flushing PCI posted write. Reading the register
165296ee09c5SPyun YongHyeon 		 * back ensures the flush operation. In addition,
165396ee09c5SPyun YongHyeon 		 * hardware will execute PCI posted write in the long
165496ee09c5SPyun YongHyeon 		 * run and watchdog timer for the kick command was set
165596ee09c5SPyun YongHyeon 		 * to 5 seconds. Therefore I think the second write
165696ee09c5SPyun YongHyeon 		 * access is not necessary or could be replaced with
165796ee09c5SPyun YongHyeon 		 * read operation.
165896ee09c5SPyun YongHyeon 		 */
165996ee09c5SPyun YongHyeon 		CSR_WRITE_4(sc, BFE_DMATX_PTR,
166096ee09c5SPyun YongHyeon 		    sc->bfe_tx_prod * sizeof(struct bfe_desc));
1661b9f78d2bSBill Paul 
1662b9f78d2bSBill Paul 		/*
1663b9f78d2bSBill Paul 		 * Set a timeout in case the chip goes out to lunch.
1664b9f78d2bSBill Paul 		 */
16656ceb40baSPyun YongHyeon 		sc->bfe_watchdog_timer = 5;
166622d0ab2eSMax Laier 	}
1667b9f78d2bSBill Paul }
1668b9f78d2bSBill Paul 
1669b9f78d2bSBill Paul static void
1670b9f78d2bSBill Paul bfe_init(void *xsc)
1671b9f78d2bSBill Paul {
1672f16b4811SMike Makonnen 	BFE_LOCK((struct bfe_softc *)xsc);
1673f16b4811SMike Makonnen 	bfe_init_locked(xsc);
1674f16b4811SMike Makonnen 	BFE_UNLOCK((struct bfe_softc *)xsc);
1675f16b4811SMike Makonnen }
1676f16b4811SMike Makonnen 
1677f16b4811SMike Makonnen static void
1678f16b4811SMike Makonnen bfe_init_locked(void *xsc)
1679f16b4811SMike Makonnen {
1680b9f78d2bSBill Paul 	struct bfe_softc *sc = (struct bfe_softc*)xsc;
1681c0e5e270SJustin Hibbits 	if_t ifp = sc->bfe_ifp;
16826ceb40baSPyun YongHyeon 	struct mii_data *mii;
1683b9f78d2bSBill Paul 
1684f16b4811SMike Makonnen 	BFE_LOCK_ASSERT(sc);
1685b9f78d2bSBill Paul 
16866ceb40baSPyun YongHyeon 	mii = device_get_softc(sc->bfe_miibus);
16876ceb40baSPyun YongHyeon 
1688c0e5e270SJustin Hibbits 	if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
1689b9f78d2bSBill Paul 		return;
1690b9f78d2bSBill Paul 
1691b9f78d2bSBill Paul 	bfe_stop(sc);
1692b9f78d2bSBill Paul 	bfe_chip_reset(sc);
1693b9f78d2bSBill Paul 
1694b9f78d2bSBill Paul 	if (bfe_list_rx_init(sc) == ENOBUFS) {
1695be280562SPyun YongHyeon 		device_printf(sc->bfe_dev,
1696be280562SPyun YongHyeon 		    "%s: Not enough memory for list buffers\n", __func__);
1697b9f78d2bSBill Paul 		bfe_stop(sc);
1698b9f78d2bSBill Paul 		return;
1699b9f78d2bSBill Paul 	}
170096ee09c5SPyun YongHyeon 	bfe_list_tx_init(sc);
1701b9f78d2bSBill Paul 
1702b9f78d2bSBill Paul 	bfe_set_rx_mode(sc);
1703b9f78d2bSBill Paul 
1704b9f78d2bSBill Paul 	/* Enable the chip and core */
1705b9f78d2bSBill Paul 	BFE_OR(sc, BFE_ENET_CTRL, BFE_ENET_ENABLE);
1706b9f78d2bSBill Paul 	/* Enable interrupts */
1707b9f78d2bSBill Paul 	CSR_WRITE_4(sc, BFE_IMASK, BFE_IMASK_DEF);
1708b9f78d2bSBill Paul 
17096ceb40baSPyun YongHyeon 	/* Clear link state and change media. */
171049bbfbc5SPyun YongHyeon 	sc->bfe_flags &= ~BFE_FLAG_LINK;
17116ceb40baSPyun YongHyeon 	mii_mediachg(mii);
17126ceb40baSPyun YongHyeon 
1713c0e5e270SJustin Hibbits 	if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
1714c0e5e270SJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
1715b9f78d2bSBill Paul 
17166ceb40baSPyun YongHyeon 	callout_reset(&sc->bfe_stat_co, hz, bfe_tick, sc);
1717b9f78d2bSBill Paul }
1718b9f78d2bSBill Paul 
1719b9f78d2bSBill Paul /*
1720b9f78d2bSBill Paul  * Set media options.
1721b9f78d2bSBill Paul  */
1722b9f78d2bSBill Paul static int
1723c0e5e270SJustin Hibbits bfe_ifmedia_upd(if_t ifp)
1724b9f78d2bSBill Paul {
1725b9f78d2bSBill Paul 	struct bfe_softc *sc;
1726b9f78d2bSBill Paul 	struct mii_data *mii;
17273fcb7a53SMarius Strobl 	struct mii_softc *miisc;
17286ceb40baSPyun YongHyeon 	int error;
1729b9f78d2bSBill Paul 
1730c0e5e270SJustin Hibbits 	sc = if_getsoftc(ifp);
17316ceb40baSPyun YongHyeon 	BFE_LOCK(sc);
1732b9f78d2bSBill Paul 
1733b9f78d2bSBill Paul 	mii = device_get_softc(sc->bfe_miibus);
17343fcb7a53SMarius Strobl 	LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
17353fcb7a53SMarius Strobl 		PHY_RESET(miisc);
17366ceb40baSPyun YongHyeon 	error = mii_mediachg(mii);
17376ceb40baSPyun YongHyeon 	BFE_UNLOCK(sc);
1738b9f78d2bSBill Paul 
17396ceb40baSPyun YongHyeon 	return (error);
1740b9f78d2bSBill Paul }
1741b9f78d2bSBill Paul 
1742b9f78d2bSBill Paul /*
1743b9f78d2bSBill Paul  * Report current media status.
1744b9f78d2bSBill Paul  */
1745b9f78d2bSBill Paul static void
1746c0e5e270SJustin Hibbits bfe_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
1747b9f78d2bSBill Paul {
1748c0e5e270SJustin Hibbits 	struct bfe_softc *sc = if_getsoftc(ifp);
1749b9f78d2bSBill Paul 	struct mii_data *mii;
1750b9f78d2bSBill Paul 
17516ceb40baSPyun YongHyeon 	BFE_LOCK(sc);
1752b9f78d2bSBill Paul 	mii = device_get_softc(sc->bfe_miibus);
1753b9f78d2bSBill Paul 	mii_pollstat(mii);
1754b9f78d2bSBill Paul 	ifmr->ifm_active = mii->mii_media_active;
1755b9f78d2bSBill Paul 	ifmr->ifm_status = mii->mii_media_status;
17566ceb40baSPyun YongHyeon 	BFE_UNLOCK(sc);
1757b9f78d2bSBill Paul }
1758b9f78d2bSBill Paul 
1759b9f78d2bSBill Paul static int
1760c0e5e270SJustin Hibbits bfe_ioctl(if_t ifp, u_long command, caddr_t data)
1761b9f78d2bSBill Paul {
1762c0e5e270SJustin Hibbits 	struct bfe_softc *sc = if_getsoftc(ifp);
1763b9f78d2bSBill Paul 	struct ifreq *ifr = (struct ifreq *) data;
1764b9f78d2bSBill Paul 	struct mii_data *mii;
1765b9f78d2bSBill Paul 	int error = 0;
1766b9f78d2bSBill Paul 
1767b9f78d2bSBill Paul 	switch (command) {
1768b9f78d2bSBill Paul 	case SIOCSIFFLAGS:
1769f16b4811SMike Makonnen 		BFE_LOCK(sc);
1770c0e5e270SJustin Hibbits 		if (if_getflags(ifp) & IFF_UP) {
1771c0e5e270SJustin Hibbits 			if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
1772b9f78d2bSBill Paul 				bfe_set_rx_mode(sc);
177349bbfbc5SPyun YongHyeon 			else if ((sc->bfe_flags & BFE_FLAG_DETACH) == 0)
1774f16b4811SMike Makonnen 				bfe_init_locked(sc);
1775c0e5e270SJustin Hibbits 		} else if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
1776b9f78d2bSBill Paul 			bfe_stop(sc);
1777f16b4811SMike Makonnen 		BFE_UNLOCK(sc);
1778b9f78d2bSBill Paul 		break;
1779b9f78d2bSBill Paul 	case SIOCADDMULTI:
1780b9f78d2bSBill Paul 	case SIOCDELMULTI:
1781f16b4811SMike Makonnen 		BFE_LOCK(sc);
1782c0e5e270SJustin Hibbits 		if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
1783b9f78d2bSBill Paul 			bfe_set_rx_mode(sc);
1784f16b4811SMike Makonnen 		BFE_UNLOCK(sc);
1785b9f78d2bSBill Paul 		break;
1786b9f78d2bSBill Paul 	case SIOCGIFMEDIA:
1787b9f78d2bSBill Paul 	case SIOCSIFMEDIA:
1788b9f78d2bSBill Paul 		mii = device_get_softc(sc->bfe_miibus);
1789cde25118SPyun YongHyeon 		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, command);
1790b9f78d2bSBill Paul 		break;
1791b9f78d2bSBill Paul 	default:
1792b9f78d2bSBill Paul 		error = ether_ioctl(ifp, command, data);
1793b9f78d2bSBill Paul 		break;
1794b9f78d2bSBill Paul 	}
1795b9f78d2bSBill Paul 
179627de24a7SDag-Erling Smørgrav 	return (error);
1797b9f78d2bSBill Paul }
1798b9f78d2bSBill Paul 
1799b9f78d2bSBill Paul static void
18006ceb40baSPyun YongHyeon bfe_watchdog(struct bfe_softc *sc)
1801b9f78d2bSBill Paul {
1802c0e5e270SJustin Hibbits 	if_t ifp;
1803b9f78d2bSBill Paul 
18046ceb40baSPyun YongHyeon 	BFE_LOCK_ASSERT(sc);
1805b9f78d2bSBill Paul 
18066ceb40baSPyun YongHyeon 	if (sc->bfe_watchdog_timer == 0 || --sc->bfe_watchdog_timer)
18076ceb40baSPyun YongHyeon 		return;
18086ceb40baSPyun YongHyeon 
18096ceb40baSPyun YongHyeon 	ifp = sc->bfe_ifp;
1810b9f78d2bSBill Paul 
1811be280562SPyun YongHyeon 	device_printf(sc->bfe_dev, "watchdog timeout -- resetting\n");
1812b9f78d2bSBill Paul 
181310b77d66SGleb Smirnoff 	if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
1814c0e5e270SJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
1815f16b4811SMike Makonnen 	bfe_init_locked(sc);
1816b9f78d2bSBill Paul 
1817c0e5e270SJustin Hibbits 	if (!if_sendq_empty(ifp))
18186ceb40baSPyun YongHyeon 		bfe_start_locked(ifp);
1819b9f78d2bSBill Paul }
1820b9f78d2bSBill Paul 
1821b9f78d2bSBill Paul static void
1822b9f78d2bSBill Paul bfe_tick(void *xsc)
1823b9f78d2bSBill Paul {
1824b9f78d2bSBill Paul 	struct bfe_softc *sc = xsc;
1825b9f78d2bSBill Paul 	struct mii_data *mii;
1826b9f78d2bSBill Paul 
18276ceb40baSPyun YongHyeon 	BFE_LOCK_ASSERT(sc);
1828b9f78d2bSBill Paul 
1829b9f78d2bSBill Paul 	mii = device_get_softc(sc->bfe_miibus);
1830b9f78d2bSBill Paul 	mii_tick(mii);
18316ceb40baSPyun YongHyeon 	bfe_stats_update(sc);
18326ceb40baSPyun YongHyeon 	bfe_watchdog(sc);
18336ceb40baSPyun YongHyeon 	callout_reset(&sc->bfe_stat_co, hz, bfe_tick, sc);
1834b9f78d2bSBill Paul }
1835b9f78d2bSBill Paul 
1836b9f78d2bSBill Paul /*
1837b9f78d2bSBill Paul  * Stop the adapter and free any mbufs allocated to the
1838b9f78d2bSBill Paul  * RX and TX lists.
1839b9f78d2bSBill Paul  */
1840b9f78d2bSBill Paul static void
1841b9f78d2bSBill Paul bfe_stop(struct bfe_softc *sc)
1842b9f78d2bSBill Paul {
1843c0e5e270SJustin Hibbits 	if_t ifp;
1844b9f78d2bSBill Paul 
1845f16b4811SMike Makonnen 	BFE_LOCK_ASSERT(sc);
1846b9f78d2bSBill Paul 
1847fc74a9f9SBrooks Davis 	ifp = sc->bfe_ifp;
1848c0e5e270SJustin Hibbits 	if_setdrvflagbits(ifp, 0, (IFF_DRV_RUNNING | IFF_DRV_OACTIVE));
184949bbfbc5SPyun YongHyeon 	sc->bfe_flags &= ~BFE_FLAG_LINK;
18506ceb40baSPyun YongHyeon 	callout_stop(&sc->bfe_stat_co);
18516ceb40baSPyun YongHyeon 	sc->bfe_watchdog_timer = 0;
1852b9f78d2bSBill Paul 
1853b9f78d2bSBill Paul 	bfe_chip_halt(sc);
1854b9f78d2bSBill Paul 	bfe_tx_ring_free(sc);
1855b9f78d2bSBill Paul 	bfe_rx_ring_free(sc);
1856b9f78d2bSBill Paul }
1857861cf54cSPyun YongHyeon 
1858861cf54cSPyun YongHyeon static int
1859861cf54cSPyun YongHyeon sysctl_bfe_stats(SYSCTL_HANDLER_ARGS)
1860861cf54cSPyun YongHyeon {
1861861cf54cSPyun YongHyeon 	struct bfe_softc *sc;
1862861cf54cSPyun YongHyeon 	struct bfe_hw_stats *stats;
1863861cf54cSPyun YongHyeon 	int error, result;
1864861cf54cSPyun YongHyeon 
1865861cf54cSPyun YongHyeon 	result = -1;
1866861cf54cSPyun YongHyeon 	error = sysctl_handle_int(oidp, &result, 0, req);
1867861cf54cSPyun YongHyeon 
1868861cf54cSPyun YongHyeon 	if (error != 0 || req->newptr == NULL)
1869861cf54cSPyun YongHyeon 		return (error);
1870861cf54cSPyun YongHyeon 
1871861cf54cSPyun YongHyeon 	if (result != 1)
1872861cf54cSPyun YongHyeon 		return (error);
1873861cf54cSPyun YongHyeon 
1874861cf54cSPyun YongHyeon 	sc = (struct bfe_softc *)arg1;
1875861cf54cSPyun YongHyeon 	stats = &sc->bfe_stats;
1876861cf54cSPyun YongHyeon 
1877861cf54cSPyun YongHyeon 	printf("%s statistics:\n", device_get_nameunit(sc->bfe_dev));
1878861cf54cSPyun YongHyeon 	printf("Transmit good octets : %ju\n",
1879861cf54cSPyun YongHyeon 	    (uintmax_t)stats->tx_good_octets);
1880861cf54cSPyun YongHyeon 	printf("Transmit good frames : %ju\n",
1881861cf54cSPyun YongHyeon 	    (uintmax_t)stats->tx_good_frames);
1882861cf54cSPyun YongHyeon 	printf("Transmit octets : %ju\n",
1883861cf54cSPyun YongHyeon 	    (uintmax_t)stats->tx_octets);
1884861cf54cSPyun YongHyeon 	printf("Transmit frames : %ju\n",
1885861cf54cSPyun YongHyeon 	    (uintmax_t)stats->tx_frames);
1886861cf54cSPyun YongHyeon 	printf("Transmit broadcast frames : %ju\n",
1887861cf54cSPyun YongHyeon 	    (uintmax_t)stats->tx_bcast_frames);
1888861cf54cSPyun YongHyeon 	printf("Transmit multicast frames : %ju\n",
1889861cf54cSPyun YongHyeon 	    (uintmax_t)stats->tx_mcast_frames);
1890861cf54cSPyun YongHyeon 	printf("Transmit frames 64 bytes : %ju\n",
1891861cf54cSPyun YongHyeon 	    (uint64_t)stats->tx_pkts_64);
1892861cf54cSPyun YongHyeon 	printf("Transmit frames 65 to 127 bytes : %ju\n",
1893861cf54cSPyun YongHyeon 	    (uint64_t)stats->tx_pkts_65_127);
1894861cf54cSPyun YongHyeon 	printf("Transmit frames 128 to 255 bytes : %ju\n",
1895861cf54cSPyun YongHyeon 	    (uint64_t)stats->tx_pkts_128_255);
1896861cf54cSPyun YongHyeon 	printf("Transmit frames 256 to 511 bytes : %ju\n",
1897861cf54cSPyun YongHyeon 	    (uint64_t)stats->tx_pkts_256_511);
1898861cf54cSPyun YongHyeon 	printf("Transmit frames 512 to 1023 bytes : %ju\n",
1899861cf54cSPyun YongHyeon 	    (uint64_t)stats->tx_pkts_512_1023);
1900861cf54cSPyun YongHyeon 	printf("Transmit frames 1024 to max bytes : %ju\n",
1901861cf54cSPyun YongHyeon 	    (uint64_t)stats->tx_pkts_1024_max);
1902861cf54cSPyun YongHyeon 	printf("Transmit jabber errors : %u\n", stats->tx_jabbers);
1903861cf54cSPyun YongHyeon 	printf("Transmit oversized frames : %ju\n",
1904861cf54cSPyun YongHyeon 	    (uint64_t)stats->tx_oversize_frames);
1905861cf54cSPyun YongHyeon 	printf("Transmit fragmented frames : %ju\n",
1906861cf54cSPyun YongHyeon 	    (uint64_t)stats->tx_frag_frames);
1907861cf54cSPyun YongHyeon 	printf("Transmit underruns : %u\n", stats->tx_colls);
1908861cf54cSPyun YongHyeon 	printf("Transmit total collisions : %u\n", stats->tx_single_colls);
1909861cf54cSPyun YongHyeon 	printf("Transmit single collisions : %u\n", stats->tx_single_colls);
1910861cf54cSPyun YongHyeon 	printf("Transmit multiple collisions : %u\n", stats->tx_multi_colls);
1911861cf54cSPyun YongHyeon 	printf("Transmit excess collisions : %u\n", stats->tx_excess_colls);
1912861cf54cSPyun YongHyeon 	printf("Transmit late collisions : %u\n", stats->tx_late_colls);
1913861cf54cSPyun YongHyeon 	printf("Transmit deferrals : %u\n", stats->tx_deferrals);
1914861cf54cSPyun YongHyeon 	printf("Transmit carrier losts : %u\n", stats->tx_carrier_losts);
1915861cf54cSPyun YongHyeon 	printf("Transmit pause frames : %u\n", stats->tx_pause_frames);
1916861cf54cSPyun YongHyeon 
1917861cf54cSPyun YongHyeon 	printf("Receive good octets : %ju\n",
1918861cf54cSPyun YongHyeon 	    (uintmax_t)stats->rx_good_octets);
1919861cf54cSPyun YongHyeon 	printf("Receive good frames : %ju\n",
1920861cf54cSPyun YongHyeon 	    (uintmax_t)stats->rx_good_frames);
1921861cf54cSPyun YongHyeon 	printf("Receive octets : %ju\n",
1922861cf54cSPyun YongHyeon 	    (uintmax_t)stats->rx_octets);
1923861cf54cSPyun YongHyeon 	printf("Receive frames : %ju\n",
1924861cf54cSPyun YongHyeon 	    (uintmax_t)stats->rx_frames);
1925861cf54cSPyun YongHyeon 	printf("Receive broadcast frames : %ju\n",
1926861cf54cSPyun YongHyeon 	    (uintmax_t)stats->rx_bcast_frames);
1927861cf54cSPyun YongHyeon 	printf("Receive multicast frames : %ju\n",
1928861cf54cSPyun YongHyeon 	    (uintmax_t)stats->rx_mcast_frames);
1929861cf54cSPyun YongHyeon 	printf("Receive frames 64 bytes : %ju\n",
1930861cf54cSPyun YongHyeon 	    (uint64_t)stats->rx_pkts_64);
1931861cf54cSPyun YongHyeon 	printf("Receive frames 65 to 127 bytes : %ju\n",
1932861cf54cSPyun YongHyeon 	    (uint64_t)stats->rx_pkts_65_127);
1933861cf54cSPyun YongHyeon 	printf("Receive frames 128 to 255 bytes : %ju\n",
1934861cf54cSPyun YongHyeon 	    (uint64_t)stats->rx_pkts_128_255);
1935861cf54cSPyun YongHyeon 	printf("Receive frames 256 to 511 bytes : %ju\n",
1936861cf54cSPyun YongHyeon 	    (uint64_t)stats->rx_pkts_256_511);
1937861cf54cSPyun YongHyeon 	printf("Receive frames 512 to 1023 bytes : %ju\n",
1938861cf54cSPyun YongHyeon 	    (uint64_t)stats->rx_pkts_512_1023);
1939861cf54cSPyun YongHyeon 	printf("Receive frames 1024 to max bytes : %ju\n",
1940861cf54cSPyun YongHyeon 	    (uint64_t)stats->rx_pkts_1024_max);
1941861cf54cSPyun YongHyeon 	printf("Receive jabber errors : %u\n", stats->rx_jabbers);
1942861cf54cSPyun YongHyeon 	printf("Receive oversized frames : %ju\n",
1943861cf54cSPyun YongHyeon 	    (uint64_t)stats->rx_oversize_frames);
1944861cf54cSPyun YongHyeon 	printf("Receive fragmented frames : %ju\n",
1945861cf54cSPyun YongHyeon 	    (uint64_t)stats->rx_frag_frames);
1946861cf54cSPyun YongHyeon 	printf("Receive missed frames : %u\n", stats->rx_missed_frames);
1947861cf54cSPyun YongHyeon 	printf("Receive CRC align errors : %u\n", stats->rx_crc_align_errs);
1948861cf54cSPyun YongHyeon 	printf("Receive undersized frames : %u\n", stats->rx_runts);
1949861cf54cSPyun YongHyeon 	printf("Receive CRC errors : %u\n", stats->rx_crc_errs);
1950861cf54cSPyun YongHyeon 	printf("Receive align errors : %u\n", stats->rx_align_errs);
1951861cf54cSPyun YongHyeon 	printf("Receive symbol errors : %u\n", stats->rx_symbol_errs);
1952861cf54cSPyun YongHyeon 	printf("Receive pause frames : %u\n", stats->rx_pause_frames);
1953861cf54cSPyun YongHyeon 	printf("Receive control frames : %u\n", stats->rx_control_frames);
1954861cf54cSPyun YongHyeon 
1955861cf54cSPyun YongHyeon 	return (error);
1956861cf54cSPyun YongHyeon }
1957