1179100Syongari/*-
2179100Syongari * Copyright (c) 2008, Pyun YongHyeon <yongari@FreeBSD.org>
3179100Syongari * All rights reserved.
4179100Syongari *
5179100Syongari * Redistribution and use in source and binary forms, with or without
6179100Syongari * modification, are permitted provided that the following conditions
7179100Syongari * are met:
8179100Syongari * 1. Redistributions of source code must retain the above copyright
9179100Syongari *    notice unmodified, this list of conditions, and the following
10179100Syongari *    disclaimer.
11179100Syongari * 2. Redistributions in binary form must reproduce the above copyright
12179100Syongari *    notice, this list of conditions and the following disclaimer in the
13179100Syongari *    documentation and/or other materials provided with the distribution.
14179100Syongari *
15179100Syongari * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16179100Syongari * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17179100Syongari * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18179100Syongari * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19179100Syongari * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20179100Syongari * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21179100Syongari * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22179100Syongari * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23179100Syongari * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24179100Syongari * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25179100Syongari * SUCH DAMAGE.
26179100Syongari */
27179100Syongari
28179100Syongari/* Driver for Attansic Technology Corp. L1 Gigabit Ethernet. */
29179100Syongari
30179100Syongari#include <sys/cdefs.h>
31179100Syongari__FBSDID("$FreeBSD$");
32179100Syongari
33179100Syongari#include <sys/param.h>
34179100Syongari#include <sys/systm.h>
35179100Syongari#include <sys/bus.h>
36179100Syongari#include <sys/endian.h>
37179100Syongari#include <sys/kernel.h>
38179100Syongari#include <sys/malloc.h>
39179100Syongari#include <sys/mbuf.h>
40179100Syongari#include <sys/rman.h>
41179100Syongari#include <sys/module.h>
42179100Syongari#include <sys/queue.h>
43179100Syongari#include <sys/socket.h>
44179100Syongari#include <sys/sockio.h>
45179100Syongari#include <sys/sysctl.h>
46179100Syongari#include <sys/taskqueue.h>
47179100Syongari
48179100Syongari#include <net/bpf.h>
49179100Syongari#include <net/if.h>
50257176Sglebius#include <net/if_var.h>
51179100Syongari#include <net/if_arp.h>
52179100Syongari#include <net/ethernet.h>
53179100Syongari#include <net/if_dl.h>
54179100Syongari#include <net/if_media.h>
55179100Syongari#include <net/if_types.h>
56179100Syongari#include <net/if_vlan_var.h>
57179100Syongari
58179100Syongari#include <netinet/in.h>
59179100Syongari#include <netinet/in_systm.h>
60179100Syongari#include <netinet/ip.h>
61179100Syongari#include <netinet/tcp.h>
62179100Syongari
63179100Syongari#include <dev/mii/mii.h>
64179100Syongari#include <dev/mii/miivar.h>
65179100Syongari
66179100Syongari#include <dev/pci/pcireg.h>
67179100Syongari#include <dev/pci/pcivar.h>
68179100Syongari
69179100Syongari#include <machine/bus.h>
70179100Syongari#include <machine/in_cksum.h>
71179100Syongari
72179100Syongari#include <dev/age/if_agereg.h>
73179100Syongari#include <dev/age/if_agevar.h>
74179100Syongari
75179100Syongari/* "device miibus" required.  See GENERIC if you get errors here. */
76179100Syongari#include "miibus_if.h"
77179100Syongari
78179100Syongari#define	AGE_CSUM_FEATURES	(CSUM_TCP | CSUM_UDP)
79179100Syongari
80179100SyongariMODULE_DEPEND(age, pci, 1, 1, 1);
81179100SyongariMODULE_DEPEND(age, ether, 1, 1, 1);
82179100SyongariMODULE_DEPEND(age, miibus, 1, 1, 1);
83179100Syongari
84179100Syongari/* Tunables. */
85179100Syongaristatic int msi_disable = 0;
86179100Syongaristatic int msix_disable = 0;
87179100SyongariTUNABLE_INT("hw.age.msi_disable", &msi_disable);
88179100SyongariTUNABLE_INT("hw.age.msix_disable", &msix_disable);
89179100Syongari
90179100Syongari/*
91179100Syongari * Devices supported by this driver.
92179100Syongari */
93179100Syongaristatic struct age_dev {
94179100Syongari	uint16_t	age_vendorid;
95179100Syongari	uint16_t	age_deviceid;
96179100Syongari	const char	*age_name;
97179100Syongari} age_devs[] = {
98179100Syongari	{ VENDORID_ATTANSIC, DEVICEID_ATTANSIC_L1,
99179100Syongari	    "Attansic Technology Corp, L1 Gigabit Ethernet" },
100179100Syongari};
101179100Syongari
102179100Syongaristatic int age_miibus_readreg(device_t, int, int);
103179100Syongaristatic int age_miibus_writereg(device_t, int, int, int);
104179100Syongaristatic void age_miibus_statchg(device_t);
105179100Syongaristatic void age_mediastatus(struct ifnet *, struct ifmediareq *);
106179100Syongaristatic int age_mediachange(struct ifnet *);
107179100Syongaristatic int age_probe(device_t);
108179100Syongaristatic void age_get_macaddr(struct age_softc *);
109179100Syongaristatic void age_phy_reset(struct age_softc *);
110179100Syongaristatic int age_attach(device_t);
111179100Syongaristatic int age_detach(device_t);
112179100Syongaristatic void age_sysctl_node(struct age_softc *);
113179100Syongaristatic void age_dmamap_cb(void *, bus_dma_segment_t *, int, int);
114179100Syongaristatic int age_check_boundary(struct age_softc *);
115179100Syongaristatic int age_dma_alloc(struct age_softc *);
116179100Syongaristatic void age_dma_free(struct age_softc *);
117179100Syongaristatic int age_shutdown(device_t);
118179100Syongaristatic void age_setwol(struct age_softc *);
119179100Syongaristatic int age_suspend(device_t);
120179100Syongaristatic int age_resume(device_t);
121179100Syongaristatic int age_encap(struct age_softc *, struct mbuf **);
122179100Syongaristatic void age_start(struct ifnet *);
123216925Sjhbstatic void age_start_locked(struct ifnet *);
124179100Syongaristatic void age_watchdog(struct age_softc *);
125179100Syongaristatic int age_ioctl(struct ifnet *, u_long, caddr_t);
126179100Syongaristatic void age_mac_config(struct age_softc *);
127179100Syongaristatic void age_link_task(void *, int);
128179100Syongaristatic void age_stats_update(struct age_softc *);
129179100Syongaristatic int age_intr(void *);
130179100Syongaristatic void age_int_task(void *, int);
131179100Syongaristatic void age_txintr(struct age_softc *, int);
132179100Syongaristatic void age_rxeof(struct age_softc *sc, struct rx_rdesc *);
133179100Syongaristatic int age_rxintr(struct age_softc *, int, int);
134179100Syongaristatic void age_tick(void *);
135179100Syongaristatic void age_reset(struct age_softc *);
136179100Syongaristatic void age_init(void *);
137179100Syongaristatic void age_init_locked(struct age_softc *);
138179100Syongaristatic void age_stop(struct age_softc *);
139179100Syongaristatic void age_stop_txmac(struct age_softc *);
140179100Syongaristatic void age_stop_rxmac(struct age_softc *);
141179100Syongaristatic void age_init_tx_ring(struct age_softc *);
142179100Syongaristatic int age_init_rx_ring(struct age_softc *);
143179100Syongaristatic void age_init_rr_ring(struct age_softc *);
144179100Syongaristatic void age_init_cmb_block(struct age_softc *);
145179100Syongaristatic void age_init_smb_block(struct age_softc *);
146246341Syongari#ifndef __NO_STRICT_ALIGNMENT
147246341Syongaristatic struct mbuf *age_fixup_rx(struct ifnet *, struct mbuf *);
148246341Syongari#endif
149179100Syongaristatic int age_newbuf(struct age_softc *, struct age_rxdesc *);
150179100Syongaristatic void age_rxvlan(struct age_softc *);
151179100Syongaristatic void age_rxfilter(struct age_softc *);
152179100Syongaristatic int sysctl_age_stats(SYSCTL_HANDLER_ARGS);
153179100Syongaristatic int sysctl_int_range(SYSCTL_HANDLER_ARGS, int, int);
154179100Syongaristatic int sysctl_hw_age_proc_limit(SYSCTL_HANDLER_ARGS);
155179100Syongaristatic int sysctl_hw_age_int_mod(SYSCTL_HANDLER_ARGS);
156179100Syongari
157179100Syongari
158179100Syongaristatic device_method_t age_methods[] = {
159179100Syongari	/* Device interface. */
160179100Syongari	DEVMETHOD(device_probe,		age_probe),
161179100Syongari	DEVMETHOD(device_attach,	age_attach),
162179100Syongari	DEVMETHOD(device_detach,	age_detach),
163179100Syongari	DEVMETHOD(device_shutdown,	age_shutdown),
164179100Syongari	DEVMETHOD(device_suspend,	age_suspend),
165179100Syongari	DEVMETHOD(device_resume,	age_resume),
166179100Syongari
167179100Syongari	/* MII interface. */
168179100Syongari	DEVMETHOD(miibus_readreg,	age_miibus_readreg),
169179100Syongari	DEVMETHOD(miibus_writereg,	age_miibus_writereg),
170179100Syongari	DEVMETHOD(miibus_statchg,	age_miibus_statchg),
171179100Syongari
172179100Syongari	{ NULL, NULL }
173179100Syongari};
174179100Syongari
175179100Syongaristatic driver_t age_driver = {
176179100Syongari	"age",
177179100Syongari	age_methods,
178179100Syongari	sizeof(struct age_softc)
179179100Syongari};
180179100Syongari
181179100Syongaristatic devclass_t age_devclass;
182179100Syongari
183179100SyongariDRIVER_MODULE(age, pci, age_driver, age_devclass, 0, 0);
184179100SyongariDRIVER_MODULE(miibus, age, miibus_driver, miibus_devclass, 0, 0);
185179100Syongari
186179100Syongaristatic struct resource_spec age_res_spec_mem[] = {
187179100Syongari	{ SYS_RES_MEMORY,	PCIR_BAR(0),	RF_ACTIVE },
188179100Syongari	{ -1,			0,		0 }
189179100Syongari};
190179100Syongari
191179100Syongaristatic struct resource_spec age_irq_spec_legacy[] = {
192179100Syongari	{ SYS_RES_IRQ,		0,		RF_ACTIVE | RF_SHAREABLE },
193179100Syongari	{ -1,			0,		0 }
194179100Syongari};
195179100Syongari
196179100Syongaristatic struct resource_spec age_irq_spec_msi[] = {
197179100Syongari	{ SYS_RES_IRQ,		1,		RF_ACTIVE },
198179100Syongari	{ -1,			0,		0 }
199179100Syongari};
200179100Syongari
201179100Syongaristatic struct resource_spec age_irq_spec_msix[] = {
202179100Syongari	{ SYS_RES_IRQ,		1,		RF_ACTIVE },
203179100Syongari	{ -1,			0,		0 }
204179100Syongari};
205179100Syongari
206179100Syongari/*
207179100Syongari *	Read a PHY register on the MII of the L1.
208179100Syongari */
209179100Syongaristatic int
210179100Syongariage_miibus_readreg(device_t dev, int phy, int reg)
211179100Syongari{
212179100Syongari	struct age_softc *sc;
213179100Syongari	uint32_t v;
214179100Syongari	int i;
215179100Syongari
216179100Syongari	sc = device_get_softc(dev);
217179100Syongari
218179100Syongari	CSR_WRITE_4(sc, AGE_MDIO, MDIO_OP_EXECUTE | MDIO_OP_READ |
219179100Syongari	    MDIO_SUP_PREAMBLE | MDIO_CLK_25_4 | MDIO_REG_ADDR(reg));
220179100Syongari	for (i = AGE_PHY_TIMEOUT; i > 0; i--) {
221179100Syongari		DELAY(1);
222179100Syongari		v = CSR_READ_4(sc, AGE_MDIO);
223179100Syongari		if ((v & (MDIO_OP_EXECUTE | MDIO_OP_BUSY)) == 0)
224179100Syongari			break;
225179100Syongari	}
226179100Syongari
227179100Syongari	if (i == 0) {
228179100Syongari		device_printf(sc->age_dev, "phy read timeout : %d\n", reg);
229179100Syongari		return (0);
230179100Syongari	}
231179100Syongari
232179100Syongari	return ((v & MDIO_DATA_MASK) >> MDIO_DATA_SHIFT);
233179100Syongari}
234179100Syongari
235179100Syongari/*
236179100Syongari *	Write a PHY register on the MII of the L1.
237179100Syongari */
238179100Syongaristatic int
239179100Syongariage_miibus_writereg(device_t dev, int phy, int reg, int val)
240179100Syongari{
241179100Syongari	struct age_softc *sc;
242179100Syongari	uint32_t v;
243179100Syongari	int i;
244179100Syongari
245179100Syongari	sc = device_get_softc(dev);
246179100Syongari
247179100Syongari	CSR_WRITE_4(sc, AGE_MDIO, MDIO_OP_EXECUTE | MDIO_OP_WRITE |
248179100Syongari	    (val & MDIO_DATA_MASK) << MDIO_DATA_SHIFT |
249179100Syongari	    MDIO_SUP_PREAMBLE | MDIO_CLK_25_4 | MDIO_REG_ADDR(reg));
250179100Syongari	for (i = AGE_PHY_TIMEOUT; i > 0; i--) {
251179100Syongari		DELAY(1);
252179100Syongari		v = CSR_READ_4(sc, AGE_MDIO);
253179100Syongari		if ((v & (MDIO_OP_EXECUTE | MDIO_OP_BUSY)) == 0)
254179100Syongari			break;
255179100Syongari	}
256179100Syongari
257179100Syongari	if (i == 0)
258179100Syongari		device_printf(sc->age_dev, "phy write timeout : %d\n", reg);
259179100Syongari
260179100Syongari	return (0);
261179100Syongari}
262179100Syongari
263179100Syongari/*
264179100Syongari *	Callback from MII layer when media changes.
265179100Syongari */
266179100Syongaristatic void
267179100Syongariage_miibus_statchg(device_t dev)
268179100Syongari{
269179100Syongari	struct age_softc *sc;
270179100Syongari
271179100Syongari	sc = device_get_softc(dev);
272179100Syongari	taskqueue_enqueue(taskqueue_swi, &sc->age_link_task);
273179100Syongari}
274179100Syongari
275179100Syongari/*
276179100Syongari *	Get the current interface media status.
277179100Syongari */
278179100Syongaristatic void
279179100Syongariage_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
280179100Syongari{
281179100Syongari	struct age_softc *sc;
282179100Syongari	struct mii_data *mii;
283179100Syongari
284179100Syongari	sc = ifp->if_softc;
285179100Syongari	AGE_LOCK(sc);
286179100Syongari	mii = device_get_softc(sc->age_miibus);
287179100Syongari
288179100Syongari	mii_pollstat(mii);
289179100Syongari	ifmr->ifm_status = mii->mii_media_status;
290179100Syongari	ifmr->ifm_active = mii->mii_media_active;
291226478Syongari	AGE_UNLOCK(sc);
292179100Syongari}
293179100Syongari
294179100Syongari/*
295179100Syongari *	Set hardware to newly-selected media.
296179100Syongari */
297179100Syongaristatic int
298179100Syongariage_mediachange(struct ifnet *ifp)
299179100Syongari{
300179100Syongari	struct age_softc *sc;
301179100Syongari	struct mii_data *mii;
302179100Syongari	struct mii_softc *miisc;
303179100Syongari	int error;
304179100Syongari
305179100Syongari	sc = ifp->if_softc;
306179100Syongari	AGE_LOCK(sc);
307179100Syongari	mii = device_get_softc(sc->age_miibus);
308221407Smarius	LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
309221407Smarius		PHY_RESET(miisc);
310179100Syongari	error = mii_mediachg(mii);
311179100Syongari	AGE_UNLOCK(sc);
312179100Syongari
313179100Syongari	return (error);
314179100Syongari}
315179100Syongari
316179100Syongaristatic int
317179100Syongariage_probe(device_t dev)
318179100Syongari{
319179100Syongari	struct age_dev *sp;
320179100Syongari	int i;
321179100Syongari	uint16_t vendor, devid;
322179100Syongari
323179100Syongari	vendor = pci_get_vendor(dev);
324179100Syongari	devid = pci_get_device(dev);
325179100Syongari	sp = age_devs;
326298307Spfg	for (i = 0; i < nitems(age_devs); i++, sp++) {
327179100Syongari		if (vendor == sp->age_vendorid &&
328179100Syongari		    devid == sp->age_deviceid) {
329179100Syongari			device_set_desc(dev, sp->age_name);
330179100Syongari			return (BUS_PROBE_DEFAULT);
331179100Syongari		}
332179100Syongari	}
333179100Syongari
334179100Syongari	return (ENXIO);
335179100Syongari}
336179100Syongari
337179100Syongaristatic void
338179100Syongariage_get_macaddr(struct age_softc *sc)
339179100Syongari{
340190499Syongari	uint32_t ea[2], reg;
341190499Syongari	int i, vpdc;
342179100Syongari
343179100Syongari	reg = CSR_READ_4(sc, AGE_SPI_CTRL);
344179100Syongari	if ((reg & SPI_VPD_ENB) != 0) {
345179100Syongari		/* Get VPD stored in TWSI EEPROM. */
346179100Syongari		reg &= ~SPI_VPD_ENB;
347179100Syongari		CSR_WRITE_4(sc, AGE_SPI_CTRL, reg);
348179100Syongari	}
349179100Syongari
350219902Sjhb	if (pci_find_cap(sc->age_dev, PCIY_VPD, &vpdc) == 0) {
351179100Syongari		/*
352190499Syongari		 * PCI VPD capability found, let TWSI reload EEPROM.
353190499Syongari		 * This will set ethernet address of controller.
354179100Syongari		 */
355190499Syongari		CSR_WRITE_4(sc, AGE_TWSI_CTRL, CSR_READ_4(sc, AGE_TWSI_CTRL) |
356190499Syongari		    TWSI_CTRL_SW_LD_START);
357190499Syongari		for (i = 100; i > 0; i--) {
358190499Syongari			DELAY(1000);
359190499Syongari			reg = CSR_READ_4(sc, AGE_TWSI_CTRL);
360190499Syongari			if ((reg & TWSI_CTRL_SW_LD_START) == 0)
361179100Syongari				break;
362179100Syongari		}
363190499Syongari		if (i == 0)
364190499Syongari			device_printf(sc->age_dev,
365190499Syongari			    "reloading EEPROM timeout!\n");
366179100Syongari	} else {
367184743Syongari		if (bootverbose)
368179100Syongari			device_printf(sc->age_dev,
369179100Syongari			    "PCI VPD capability not found!\n");
370179100Syongari	}
371179100Syongari
372190499Syongari	ea[0] = CSR_READ_4(sc, AGE_PAR0);
373190499Syongari	ea[1] = CSR_READ_4(sc, AGE_PAR1);
374190499Syongari	sc->age_eaddr[0] = (ea[1] >> 8) & 0xFF;
375190499Syongari	sc->age_eaddr[1] = (ea[1] >> 0) & 0xFF;
376190499Syongari	sc->age_eaddr[2] = (ea[0] >> 24) & 0xFF;
377190499Syongari	sc->age_eaddr[3] = (ea[0] >> 16) & 0xFF;
378190499Syongari	sc->age_eaddr[4] = (ea[0] >> 8) & 0xFF;
379190499Syongari	sc->age_eaddr[5] = (ea[0] >> 0) & 0xFF;
380179100Syongari}
381179100Syongari
382179100Syongaristatic void
383179100Syongariage_phy_reset(struct age_softc *sc)
384179100Syongari{
385190499Syongari	uint16_t reg, pn;
386190499Syongari	int i, linkup;
387179100Syongari
388179100Syongari	/* Reset PHY. */
389179100Syongari	CSR_WRITE_4(sc, AGE_GPHY_CTRL, GPHY_CTRL_RST);
390190499Syongari	DELAY(2000);
391179100Syongari	CSR_WRITE_4(sc, AGE_GPHY_CTRL, GPHY_CTRL_CLR);
392190499Syongari	DELAY(2000);
393190499Syongari
394190499Syongari#define	ATPHY_DBG_ADDR		0x1D
395190499Syongari#define	ATPHY_DBG_DATA		0x1E
396190499Syongari#define	ATPHY_CDTC		0x16
397190499Syongari#define	PHY_CDTC_ENB		0x0001
398190499Syongari#define	PHY_CDTC_POFF		8
399190499Syongari#define	ATPHY_CDTS		0x1C
400190499Syongari#define	PHY_CDTS_STAT_OK	0x0000
401190499Syongari#define	PHY_CDTS_STAT_SHORT	0x0100
402190499Syongari#define	PHY_CDTS_STAT_OPEN	0x0200
403190499Syongari#define	PHY_CDTS_STAT_INVAL	0x0300
404190499Syongari#define	PHY_CDTS_STAT_MASK	0x0300
405190499Syongari
406190499Syongari	/* Check power saving mode. Magic from Linux. */
407190499Syongari	age_miibus_writereg(sc->age_dev, sc->age_phyaddr, MII_BMCR, BMCR_RESET);
408190499Syongari	for (linkup = 0, pn = 0; pn < 4; pn++) {
409190499Syongari		age_miibus_writereg(sc->age_dev, sc->age_phyaddr, ATPHY_CDTC,
410190499Syongari		    (pn << PHY_CDTC_POFF) | PHY_CDTC_ENB);
411190499Syongari		for (i = 200; i > 0; i--) {
412190499Syongari			DELAY(1000);
413190499Syongari			reg = age_miibus_readreg(sc->age_dev, sc->age_phyaddr,
414190499Syongari			    ATPHY_CDTC);
415190499Syongari			if ((reg & PHY_CDTC_ENB) == 0)
416190499Syongari				break;
417190499Syongari		}
418190499Syongari		DELAY(1000);
419190499Syongari		reg = age_miibus_readreg(sc->age_dev, sc->age_phyaddr,
420190499Syongari		    ATPHY_CDTS);
421190499Syongari		if ((reg & PHY_CDTS_STAT_MASK) != PHY_CDTS_STAT_OPEN) {
422190499Syongari			linkup++;
423190499Syongari			break;
424190499Syongari		}
425190499Syongari	}
426190499Syongari	age_miibus_writereg(sc->age_dev, sc->age_phyaddr, MII_BMCR,
427190499Syongari	    BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG);
428190499Syongari	if (linkup == 0) {
429190499Syongari		age_miibus_writereg(sc->age_dev, sc->age_phyaddr,
430190499Syongari		    ATPHY_DBG_ADDR, 0);
431190499Syongari		age_miibus_writereg(sc->age_dev, sc->age_phyaddr,
432190499Syongari		    ATPHY_DBG_DATA, 0x124E);
433190499Syongari		age_miibus_writereg(sc->age_dev, sc->age_phyaddr,
434190499Syongari		    ATPHY_DBG_ADDR, 1);
435190499Syongari		reg = age_miibus_readreg(sc->age_dev, sc->age_phyaddr,
436190499Syongari		    ATPHY_DBG_DATA);
437190499Syongari		age_miibus_writereg(sc->age_dev, sc->age_phyaddr,
438190499Syongari		    ATPHY_DBG_DATA, reg | 0x03);
439190499Syongari		/* XXX */
440190499Syongari		DELAY(1500 * 1000);
441190499Syongari		age_miibus_writereg(sc->age_dev, sc->age_phyaddr,
442190499Syongari		    ATPHY_DBG_ADDR, 0);
443190499Syongari		age_miibus_writereg(sc->age_dev, sc->age_phyaddr,
444190499Syongari		    ATPHY_DBG_DATA, 0x024E);
445190499Syongari    }
446190499Syongari
447190499Syongari#undef	ATPHY_DBG_ADDR
448190499Syongari#undef	ATPHY_DBG_DATA
449190499Syongari#undef	ATPHY_CDTC
450190499Syongari#undef	PHY_CDTC_ENB
451190499Syongari#undef	PHY_CDTC_POFF
452190499Syongari#undef	ATPHY_CDTS
453190499Syongari#undef	PHY_CDTS_STAT_OK
454190499Syongari#undef	PHY_CDTS_STAT_SHORT
455190499Syongari#undef	PHY_CDTS_STAT_OPEN
456190499Syongari#undef	PHY_CDTS_STAT_INVAL
457190499Syongari#undef	PHY_CDTS_STAT_MASK
458179100Syongari}
459179100Syongari
460179100Syongaristatic int
461179100Syongariage_attach(device_t dev)
462179100Syongari{
463179100Syongari	struct age_softc *sc;
464179100Syongari	struct ifnet *ifp;
465179100Syongari	uint16_t burst;
466179100Syongari	int error, i, msic, msixc, pmc;
467179100Syongari
468179100Syongari	error = 0;
469179100Syongari	sc = device_get_softc(dev);
470179100Syongari	sc->age_dev = dev;
471179100Syongari
472179100Syongari	mtx_init(&sc->age_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
473179100Syongari	    MTX_DEF);
474179100Syongari	callout_init_mtx(&sc->age_tick_ch, &sc->age_mtx, 0);
475179100Syongari	TASK_INIT(&sc->age_int_task, 0, age_int_task, sc);
476179100Syongari	TASK_INIT(&sc->age_link_task, 0, age_link_task, sc);
477179100Syongari
478179100Syongari	/* Map the device. */
479179100Syongari	pci_enable_busmaster(dev);
480179100Syongari	sc->age_res_spec = age_res_spec_mem;
481179100Syongari	sc->age_irq_spec = age_irq_spec_legacy;
482179100Syongari	error = bus_alloc_resources(dev, sc->age_res_spec, sc->age_res);
483179100Syongari	if (error != 0) {
484179100Syongari		device_printf(dev, "cannot allocate memory resources.\n");
485179100Syongari		goto fail;
486179100Syongari	}
487179100Syongari
488179100Syongari	/* Set PHY address. */
489179100Syongari	sc->age_phyaddr = AGE_PHY_ADDR;
490179100Syongari
491179100Syongari	/* Reset PHY. */
492179100Syongari	age_phy_reset(sc);
493179100Syongari
494179100Syongari	/* Reset the ethernet controller. */
495179100Syongari	age_reset(sc);
496179100Syongari
497179100Syongari	/* Get PCI and chip id/revision. */
498179100Syongari	sc->age_rev = pci_get_revid(dev);
499179100Syongari	sc->age_chip_rev = CSR_READ_4(sc, AGE_MASTER_CFG) >>
500179100Syongari	    MASTER_CHIP_REV_SHIFT;
501184743Syongari	if (bootverbose) {
502190499Syongari		device_printf(dev, "PCI device revision : 0x%04x\n",
503190499Syongari		    sc->age_rev);
504179100Syongari		device_printf(dev, "Chip id/revision : 0x%04x\n",
505179100Syongari		    sc->age_chip_rev);
506179100Syongari	}
507179100Syongari
508179100Syongari	/*
509179100Syongari	 * XXX
510179100Syongari	 * Unintialized hardware returns an invalid chip id/revision
511179100Syongari	 * as well as 0xFFFFFFFF for Tx/Rx fifo length. It seems that
512179100Syongari	 * unplugged cable results in putting hardware into automatic
513179100Syongari	 * power down mode which in turn returns invalld chip revision.
514179100Syongari	 */
515179100Syongari	if (sc->age_chip_rev == 0xFFFF) {
516179100Syongari		device_printf(dev,"invalid chip revision : 0x%04x -- "
517179100Syongari		    "not initialized?\n", sc->age_chip_rev);
518179100Syongari		error = ENXIO;
519179100Syongari		goto fail;
520179100Syongari	}
521179100Syongari
522179100Syongari	device_printf(dev, "%d Tx FIFO, %d Rx FIFO\n",
523179100Syongari	    CSR_READ_4(sc, AGE_SRAM_TX_FIFO_LEN),
524179100Syongari	    CSR_READ_4(sc, AGE_SRAM_RX_FIFO_LEN));
525179100Syongari
526179100Syongari	/* Allocate IRQ resources. */
527179100Syongari	msixc = pci_msix_count(dev);
528179100Syongari	msic = pci_msi_count(dev);
529184743Syongari	if (bootverbose) {
530179100Syongari		device_printf(dev, "MSIX count : %d\n", msixc);
531179100Syongari		device_printf(dev, "MSI count : %d\n", msic);
532179100Syongari	}
533179100Syongari
534179100Syongari	/* Prefer MSIX over MSI. */
535179100Syongari	if (msix_disable == 0 || msi_disable == 0) {
536179100Syongari		if (msix_disable == 0 && msixc == AGE_MSIX_MESSAGES &&
537179100Syongari		    pci_alloc_msix(dev, &msixc) == 0) {
538179100Syongari			if (msic == AGE_MSIX_MESSAGES) {
539179100Syongari				device_printf(dev, "Using %d MSIX messages.\n",
540179100Syongari				    msixc);
541179100Syongari				sc->age_flags |= AGE_FLAG_MSIX;
542179100Syongari				sc->age_irq_spec = age_irq_spec_msix;
543179100Syongari			} else
544179100Syongari				pci_release_msi(dev);
545179100Syongari		}
546179100Syongari		if (msi_disable == 0 && (sc->age_flags & AGE_FLAG_MSIX) == 0 &&
547179100Syongari		    msic == AGE_MSI_MESSAGES &&
548179100Syongari		    pci_alloc_msi(dev, &msic) == 0) {
549179100Syongari			if (msic == AGE_MSI_MESSAGES) {
550179100Syongari				device_printf(dev, "Using %d MSI messages.\n",
551179100Syongari				    msic);
552179100Syongari				sc->age_flags |= AGE_FLAG_MSI;
553179100Syongari				sc->age_irq_spec = age_irq_spec_msi;
554179100Syongari			} else
555179100Syongari				pci_release_msi(dev);
556179100Syongari		}
557179100Syongari	}
558179100Syongari
559179100Syongari	error = bus_alloc_resources(dev, sc->age_irq_spec, sc->age_irq);
560179100Syongari	if (error != 0) {
561179100Syongari		device_printf(dev, "cannot allocate IRQ resources.\n");
562179100Syongari		goto fail;
563179100Syongari	}
564179100Syongari
565179100Syongari
566179100Syongari	/* Get DMA parameters from PCIe device control register. */
567219902Sjhb	if (pci_find_cap(dev, PCIY_EXPRESS, &i) == 0) {
568179100Syongari		sc->age_flags |= AGE_FLAG_PCIE;
569179100Syongari		burst = pci_read_config(dev, i + 0x08, 2);
570179100Syongari		/* Max read request size. */
571179100Syongari		sc->age_dma_rd_burst = ((burst >> 12) & 0x07) <<
572179100Syongari		    DMA_CFG_RD_BURST_SHIFT;
573179100Syongari		/* Max payload size. */
574179100Syongari		sc->age_dma_wr_burst = ((burst >> 5) & 0x07) <<
575179100Syongari		    DMA_CFG_WR_BURST_SHIFT;
576184743Syongari		if (bootverbose) {
577179100Syongari			device_printf(dev, "Read request size : %d bytes.\n",
578179100Syongari			    128 << ((burst >> 12) & 0x07));
579179100Syongari			device_printf(dev, "TLP payload size : %d bytes.\n",
580179100Syongari			    128 << ((burst >> 5) & 0x07));
581179100Syongari		}
582179100Syongari	} else {
583179100Syongari		sc->age_dma_rd_burst = DMA_CFG_RD_BURST_128;
584179100Syongari		sc->age_dma_wr_burst = DMA_CFG_WR_BURST_128;
585179100Syongari	}
586179100Syongari
587179100Syongari	/* Create device sysctl node. */
588179100Syongari	age_sysctl_node(sc);
589179100Syongari
590295735Syongari	if ((error = age_dma_alloc(sc)) != 0)
591179100Syongari		goto fail;
592179100Syongari
593179100Syongari	/* Load station address. */
594179100Syongari	age_get_macaddr(sc);
595179100Syongari
596179100Syongari	ifp = sc->age_ifp = if_alloc(IFT_ETHER);
597179100Syongari	if (ifp == NULL) {
598179100Syongari		device_printf(dev, "cannot allocate ifnet structure.\n");
599179100Syongari		error = ENXIO;
600179100Syongari		goto fail;
601179100Syongari	}
602179100Syongari
603179100Syongari	ifp->if_softc = sc;
604179100Syongari	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
605179100Syongari	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
606179100Syongari	ifp->if_ioctl = age_ioctl;
607179100Syongari	ifp->if_start = age_start;
608179100Syongari	ifp->if_init = age_init;
609179100Syongari	ifp->if_snd.ifq_drv_maxlen = AGE_TX_RING_CNT - 1;
610179100Syongari	IFQ_SET_MAXLEN(&ifp->if_snd, ifp->if_snd.ifq_drv_maxlen);
611179100Syongari	IFQ_SET_READY(&ifp->if_snd);
612179100Syongari	ifp->if_capabilities = IFCAP_HWCSUM | IFCAP_TSO4;
613179100Syongari	ifp->if_hwassist = AGE_CSUM_FEATURES | CSUM_TSO;
614219902Sjhb	if (pci_find_cap(dev, PCIY_PMG, &pmc) == 0) {
615179100Syongari		sc->age_flags |= AGE_FLAG_PMCAP;
616179100Syongari		ifp->if_capabilities |= IFCAP_WOL_MAGIC | IFCAP_WOL_MCAST;
617179100Syongari	}
618179100Syongari	ifp->if_capenable = ifp->if_capabilities;
619179100Syongari
620179100Syongari	/* Set up MII bus. */
621213893Smarius	error = mii_attach(dev, &sc->age_miibus, ifp, age_mediachange,
622213893Smarius	    age_mediastatus, BMSR_DEFCAPMASK, sc->age_phyaddr, MII_OFFSET_ANY,
623213893Smarius	    0);
624213893Smarius	if (error != 0) {
625213893Smarius		device_printf(dev, "attaching PHYs failed\n");
626179100Syongari		goto fail;
627179100Syongari	}
628179100Syongari
629179100Syongari	ether_ifattach(ifp, sc->age_eaddr);
630179100Syongari
631179100Syongari	/* VLAN capability setup. */
632204377Syongari	ifp->if_capabilities |= IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING |
633204377Syongari	    IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWTSO;
634179100Syongari	ifp->if_capenable = ifp->if_capabilities;
635179100Syongari
636179100Syongari	/* Tell the upper layer(s) we support long frames. */
637270856Sglebius	ifp->if_hdrlen = sizeof(struct ether_vlan_header);
638179100Syongari
639179100Syongari	/* Create local taskq. */
640179100Syongari	sc->age_tq = taskqueue_create_fast("age_taskq", M_WAITOK,
641179100Syongari	    taskqueue_thread_enqueue, &sc->age_tq);
642179100Syongari	if (sc->age_tq == NULL) {
643179100Syongari		device_printf(dev, "could not create taskqueue.\n");
644179100Syongari		ether_ifdetach(ifp);
645179100Syongari		error = ENXIO;
646179100Syongari		goto fail;
647179100Syongari	}
648179100Syongari	taskqueue_start_threads(&sc->age_tq, 1, PI_NET, "%s taskq",
649179100Syongari	    device_get_nameunit(sc->age_dev));
650179100Syongari
651179100Syongari	if ((sc->age_flags & AGE_FLAG_MSIX) != 0)
652179100Syongari		msic = AGE_MSIX_MESSAGES;
653179100Syongari	else if ((sc->age_flags & AGE_FLAG_MSI) != 0)
654179100Syongari		msic = AGE_MSI_MESSAGES;
655179100Syongari	else
656179100Syongari		msic = 1;
657179100Syongari	for (i = 0; i < msic; i++) {
658179100Syongari		error = bus_setup_intr(dev, sc->age_irq[i],
659179100Syongari		    INTR_TYPE_NET | INTR_MPSAFE, age_intr, NULL, sc,
660179100Syongari		    &sc->age_intrhand[i]);
661179100Syongari		if (error != 0)
662179100Syongari			break;
663179100Syongari	}
664179100Syongari	if (error != 0) {
665179100Syongari		device_printf(dev, "could not set up interrupt handler.\n");
666179100Syongari		taskqueue_free(sc->age_tq);
667179100Syongari		sc->age_tq = NULL;
668179100Syongari		ether_ifdetach(ifp);
669179100Syongari		goto fail;
670179100Syongari	}
671179100Syongari
672179100Syongarifail:
673179100Syongari	if (error != 0)
674179100Syongari		age_detach(dev);
675179100Syongari
676179100Syongari	return (error);
677179100Syongari}
678179100Syongari
679179100Syongaristatic int
680179100Syongariage_detach(device_t dev)
681179100Syongari{
682179100Syongari	struct age_softc *sc;
683179100Syongari	struct ifnet *ifp;
684179100Syongari	int i, msic;
685179100Syongari
686179100Syongari	sc = device_get_softc(dev);
687179100Syongari
688179100Syongari	ifp = sc->age_ifp;
689179100Syongari	if (device_is_attached(dev)) {
690179100Syongari		AGE_LOCK(sc);
691179100Syongari		sc->age_flags |= AGE_FLAG_DETACH;
692179100Syongari		age_stop(sc);
693179100Syongari		AGE_UNLOCK(sc);
694179100Syongari		callout_drain(&sc->age_tick_ch);
695179100Syongari		taskqueue_drain(sc->age_tq, &sc->age_int_task);
696179100Syongari		taskqueue_drain(taskqueue_swi, &sc->age_link_task);
697179100Syongari		ether_ifdetach(ifp);
698179100Syongari	}
699179100Syongari
700179100Syongari	if (sc->age_tq != NULL) {
701179100Syongari		taskqueue_drain(sc->age_tq, &sc->age_int_task);
702179100Syongari		taskqueue_free(sc->age_tq);
703179100Syongari		sc->age_tq = NULL;
704179100Syongari	}
705179100Syongari
706179100Syongari	if (sc->age_miibus != NULL) {
707179100Syongari		device_delete_child(dev, sc->age_miibus);
708179100Syongari		sc->age_miibus = NULL;
709179100Syongari	}
710179100Syongari	bus_generic_detach(dev);
711179100Syongari	age_dma_free(sc);
712179100Syongari
713179100Syongari	if (ifp != NULL) {
714179100Syongari		if_free(ifp);
715179100Syongari		sc->age_ifp = NULL;
716179100Syongari	}
717179100Syongari
718179100Syongari	if ((sc->age_flags & AGE_FLAG_MSIX) != 0)
719179100Syongari		msic = AGE_MSIX_MESSAGES;
720179100Syongari	else if ((sc->age_flags & AGE_FLAG_MSI) != 0)
721179100Syongari		msic = AGE_MSI_MESSAGES;
722179100Syongari	else
723179100Syongari		msic = 1;
724179100Syongari	for (i = 0; i < msic; i++) {
725179100Syongari		if (sc->age_intrhand[i] != NULL) {
726179100Syongari			bus_teardown_intr(dev, sc->age_irq[i],
727179100Syongari			    sc->age_intrhand[i]);
728179100Syongari			sc->age_intrhand[i] = NULL;
729179100Syongari		}
730179100Syongari	}
731179100Syongari
732179100Syongari	bus_release_resources(dev, sc->age_irq_spec, sc->age_irq);
733179100Syongari	if ((sc->age_flags & (AGE_FLAG_MSI | AGE_FLAG_MSIX)) != 0)
734179100Syongari		pci_release_msi(dev);
735179100Syongari	bus_release_resources(dev, sc->age_res_spec, sc->age_res);
736179100Syongari	mtx_destroy(&sc->age_mtx);
737179100Syongari
738179100Syongari	return (0);
739179100Syongari}
740179100Syongari
741179100Syongaristatic void
742179100Syongariage_sysctl_node(struct age_softc *sc)
743179100Syongari{
744179100Syongari	int error;
745179100Syongari
746179100Syongari	SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->age_dev),
747179100Syongari	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->age_dev)), OID_AUTO,
748179100Syongari	    "stats", CTLTYPE_INT | CTLFLAG_RW, sc, 0, sysctl_age_stats,
749179100Syongari	    "I", "Statistics");
750179100Syongari
751179100Syongari	SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->age_dev),
752179100Syongari	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->age_dev)), OID_AUTO,
753179100Syongari	    "int_mod", CTLTYPE_INT | CTLFLAG_RW, &sc->age_int_mod, 0,
754179100Syongari	    sysctl_hw_age_int_mod, "I", "age interrupt moderation");
755179100Syongari
756179100Syongari	/* Pull in device tunables. */
757179100Syongari	sc->age_int_mod = AGE_IM_TIMER_DEFAULT;
758179100Syongari	error = resource_int_value(device_get_name(sc->age_dev),
759179100Syongari	    device_get_unit(sc->age_dev), "int_mod", &sc->age_int_mod);
760179100Syongari	if (error == 0) {
761179100Syongari		if (sc->age_int_mod < AGE_IM_TIMER_MIN ||
762179100Syongari		    sc->age_int_mod > AGE_IM_TIMER_MAX) {
763179100Syongari			device_printf(sc->age_dev,
764179100Syongari			    "int_mod value out of range; using default: %d\n",
765179100Syongari			    AGE_IM_TIMER_DEFAULT);
766179100Syongari			sc->age_int_mod = AGE_IM_TIMER_DEFAULT;
767179100Syongari		}
768179100Syongari	}
769179100Syongari
770179100Syongari	SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->age_dev),
771179100Syongari	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->age_dev)), OID_AUTO,
772179100Syongari	    "process_limit", CTLTYPE_INT | CTLFLAG_RW, &sc->age_process_limit,
773179100Syongari	    0, sysctl_hw_age_proc_limit, "I",
774179100Syongari	    "max number of Rx events to process");
775179100Syongari
776179100Syongari	/* Pull in device tunables. */
777179100Syongari	sc->age_process_limit = AGE_PROC_DEFAULT;
778179100Syongari	error = resource_int_value(device_get_name(sc->age_dev),
779179100Syongari	    device_get_unit(sc->age_dev), "process_limit",
780179100Syongari	    &sc->age_process_limit);
781179100Syongari	if (error == 0) {
782179100Syongari		if (sc->age_process_limit < AGE_PROC_MIN ||
783179100Syongari		    sc->age_process_limit > AGE_PROC_MAX) {
784179100Syongari			device_printf(sc->age_dev,
785179100Syongari			    "process_limit value out of range; "
786179100Syongari			    "using default: %d\n", AGE_PROC_DEFAULT);
787179100Syongari			sc->age_process_limit = AGE_PROC_DEFAULT;
788179100Syongari		}
789179100Syongari	}
790179100Syongari}
791179100Syongari
792179100Syongaristruct age_dmamap_arg {
793179100Syongari	bus_addr_t	age_busaddr;
794179100Syongari};
795179100Syongari
796179100Syongaristatic void
797179100Syongariage_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
798179100Syongari{
799179100Syongari	struct age_dmamap_arg *ctx;
800179100Syongari
801179100Syongari	if (error != 0)
802179100Syongari		return;
803179100Syongari
804179100Syongari	KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
805179100Syongari
806179100Syongari	ctx = (struct age_dmamap_arg *)arg;
807179100Syongari	ctx->age_busaddr = segs[0].ds_addr;
808179100Syongari}
809179100Syongari
810179100Syongari/*
811179100Syongari * Attansic L1 controller have single register to specify high
812179100Syongari * address part of DMA blocks. So all descriptor structures and
813179100Syongari * DMA memory blocks should have the same high address of given
814179100Syongari * 4GB address space(i.e. crossing 4GB boundary is not allowed).
815179100Syongari */
816179100Syongaristatic int
817179100Syongariage_check_boundary(struct age_softc *sc)
818179100Syongari{
819179100Syongari	bus_addr_t rx_ring_end, rr_ring_end, tx_ring_end;
820179100Syongari	bus_addr_t cmb_block_end, smb_block_end;
821179100Syongari
822179100Syongari	/* Tx/Rx descriptor queue should reside within 4GB boundary. */
823179100Syongari	tx_ring_end = sc->age_rdata.age_tx_ring_paddr + AGE_TX_RING_SZ;
824179100Syongari	rx_ring_end = sc->age_rdata.age_rx_ring_paddr + AGE_RX_RING_SZ;
825179100Syongari	rr_ring_end = sc->age_rdata.age_rr_ring_paddr + AGE_RR_RING_SZ;
826179100Syongari	cmb_block_end = sc->age_rdata.age_cmb_block_paddr + AGE_CMB_BLOCK_SZ;
827179100Syongari	smb_block_end = sc->age_rdata.age_smb_block_paddr + AGE_SMB_BLOCK_SZ;
828179100Syongari
829179100Syongari	if ((AGE_ADDR_HI(tx_ring_end) !=
830179100Syongari	    AGE_ADDR_HI(sc->age_rdata.age_tx_ring_paddr)) ||
831179100Syongari	    (AGE_ADDR_HI(rx_ring_end) !=
832179100Syongari	    AGE_ADDR_HI(sc->age_rdata.age_rx_ring_paddr)) ||
833179100Syongari	    (AGE_ADDR_HI(rr_ring_end) !=
834179100Syongari	    AGE_ADDR_HI(sc->age_rdata.age_rr_ring_paddr)) ||
835179100Syongari	    (AGE_ADDR_HI(cmb_block_end) !=
836179100Syongari	    AGE_ADDR_HI(sc->age_rdata.age_cmb_block_paddr)) ||
837179100Syongari	    (AGE_ADDR_HI(smb_block_end) !=
838179100Syongari	    AGE_ADDR_HI(sc->age_rdata.age_smb_block_paddr)))
839179100Syongari		return (EFBIG);
840179100Syongari
841179100Syongari	if ((AGE_ADDR_HI(tx_ring_end) != AGE_ADDR_HI(rx_ring_end)) ||
842179100Syongari	    (AGE_ADDR_HI(tx_ring_end) != AGE_ADDR_HI(rr_ring_end)) ||
843179100Syongari	    (AGE_ADDR_HI(tx_ring_end) != AGE_ADDR_HI(cmb_block_end)) ||
844179100Syongari	    (AGE_ADDR_HI(tx_ring_end) != AGE_ADDR_HI(smb_block_end)))
845179100Syongari		return (EFBIG);
846179100Syongari
847179100Syongari	return (0);
848179100Syongari}
849179100Syongari
850179100Syongaristatic int
851179100Syongariage_dma_alloc(struct age_softc *sc)
852179100Syongari{
853179100Syongari	struct age_txdesc *txd;
854179100Syongari	struct age_rxdesc *rxd;
855179100Syongari	bus_addr_t lowaddr;
856179100Syongari	struct age_dmamap_arg ctx;
857179100Syongari	int error, i;
858179100Syongari
859179100Syongari	lowaddr = BUS_SPACE_MAXADDR;
860179100Syongari
861179100Syongariagain:
862179100Syongari	/* Create parent ring/DMA block tag. */
863179100Syongari	error = bus_dma_tag_create(
864179100Syongari	    bus_get_dma_tag(sc->age_dev), /* parent */
865179100Syongari	    1, 0,			/* alignment, boundary */
866179100Syongari	    lowaddr,			/* lowaddr */
867179100Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
868179100Syongari	    NULL, NULL,			/* filter, filterarg */
869179100Syongari	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsize */
870179100Syongari	    0,				/* nsegments */
871179100Syongari	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
872179100Syongari	    0,				/* flags */
873179100Syongari	    NULL, NULL,			/* lockfunc, lockarg */
874179100Syongari	    &sc->age_cdata.age_parent_tag);
875179100Syongari	if (error != 0) {
876179100Syongari		device_printf(sc->age_dev,
877179100Syongari		    "could not create parent DMA tag.\n");
878179100Syongari		goto fail;
879179100Syongari	}
880179100Syongari
881179100Syongari	/* Create tag for Tx ring. */
882179100Syongari	error = bus_dma_tag_create(
883179100Syongari	    sc->age_cdata.age_parent_tag, /* parent */
884179100Syongari	    AGE_TX_RING_ALIGN, 0,	/* alignment, boundary */
885179100Syongari	    BUS_SPACE_MAXADDR,		/* lowaddr */
886179100Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
887179100Syongari	    NULL, NULL,			/* filter, filterarg */
888179100Syongari	    AGE_TX_RING_SZ,		/* maxsize */
889179100Syongari	    1,				/* nsegments */
890179100Syongari	    AGE_TX_RING_SZ,		/* maxsegsize */
891179100Syongari	    0,				/* flags */
892179100Syongari	    NULL, NULL,			/* lockfunc, lockarg */
893179100Syongari	    &sc->age_cdata.age_tx_ring_tag);
894179100Syongari	if (error != 0) {
895179100Syongari		device_printf(sc->age_dev,
896179100Syongari		    "could not create Tx ring DMA tag.\n");
897179100Syongari		goto fail;
898179100Syongari	}
899179100Syongari
900179100Syongari	/* Create tag for Rx ring. */
901179100Syongari	error = bus_dma_tag_create(
902179100Syongari	    sc->age_cdata.age_parent_tag, /* parent */
903179100Syongari	    AGE_RX_RING_ALIGN, 0,	/* alignment, boundary */
904179100Syongari	    BUS_SPACE_MAXADDR,		/* lowaddr */
905179100Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
906179100Syongari	    NULL, NULL,			/* filter, filterarg */
907179100Syongari	    AGE_RX_RING_SZ,		/* maxsize */
908179100Syongari	    1,				/* nsegments */
909179100Syongari	    AGE_RX_RING_SZ,		/* maxsegsize */
910179100Syongari	    0,				/* flags */
911179100Syongari	    NULL, NULL,			/* lockfunc, lockarg */
912179100Syongari	    &sc->age_cdata.age_rx_ring_tag);
913179100Syongari	if (error != 0) {
914179100Syongari		device_printf(sc->age_dev,
915179100Syongari		    "could not create Rx ring DMA tag.\n");
916179100Syongari		goto fail;
917179100Syongari	}
918179100Syongari
919179100Syongari	/* Create tag for Rx return ring. */
920179100Syongari	error = bus_dma_tag_create(
921179100Syongari	    sc->age_cdata.age_parent_tag, /* parent */
922179100Syongari	    AGE_RR_RING_ALIGN, 0,	/* alignment, boundary */
923179100Syongari	    BUS_SPACE_MAXADDR,		/* lowaddr */
924179100Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
925179100Syongari	    NULL, NULL,			/* filter, filterarg */
926179100Syongari	    AGE_RR_RING_SZ,		/* maxsize */
927179100Syongari	    1,				/* nsegments */
928179100Syongari	    AGE_RR_RING_SZ,		/* maxsegsize */
929179100Syongari	    0,				/* flags */
930179100Syongari	    NULL, NULL,			/* lockfunc, lockarg */
931179100Syongari	    &sc->age_cdata.age_rr_ring_tag);
932179100Syongari	if (error != 0) {
933179100Syongari		device_printf(sc->age_dev,
934179100Syongari		    "could not create Rx return ring DMA tag.\n");
935179100Syongari		goto fail;
936179100Syongari	}
937179100Syongari
938179100Syongari	/* Create tag for coalesing message block. */
939179100Syongari	error = bus_dma_tag_create(
940179100Syongari	    sc->age_cdata.age_parent_tag, /* parent */
941179100Syongari	    AGE_CMB_ALIGN, 0,		/* alignment, boundary */
942179100Syongari	    BUS_SPACE_MAXADDR,		/* lowaddr */
943179100Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
944179100Syongari	    NULL, NULL,			/* filter, filterarg */
945179100Syongari	    AGE_CMB_BLOCK_SZ,		/* maxsize */
946179100Syongari	    1,				/* nsegments */
947179100Syongari	    AGE_CMB_BLOCK_SZ,		/* maxsegsize */
948179100Syongari	    0,				/* flags */
949179100Syongari	    NULL, NULL,			/* lockfunc, lockarg */
950179100Syongari	    &sc->age_cdata.age_cmb_block_tag);
951179100Syongari	if (error != 0) {
952179100Syongari		device_printf(sc->age_dev,
953179100Syongari		    "could not create CMB DMA tag.\n");
954179100Syongari		goto fail;
955179100Syongari	}
956179100Syongari
957179100Syongari	/* Create tag for statistics message block. */
958179100Syongari	error = bus_dma_tag_create(
959179100Syongari	    sc->age_cdata.age_parent_tag, /* parent */
960179100Syongari	    AGE_SMB_ALIGN, 0,		/* alignment, boundary */
961179100Syongari	    BUS_SPACE_MAXADDR,		/* lowaddr */
962179100Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
963179100Syongari	    NULL, NULL,			/* filter, filterarg */
964179100Syongari	    AGE_SMB_BLOCK_SZ,		/* maxsize */
965179100Syongari	    1,				/* nsegments */
966179100Syongari	    AGE_SMB_BLOCK_SZ,		/* maxsegsize */
967179100Syongari	    0,				/* flags */
968179100Syongari	    NULL, NULL,			/* lockfunc, lockarg */
969179100Syongari	    &sc->age_cdata.age_smb_block_tag);
970179100Syongari	if (error != 0) {
971179100Syongari		device_printf(sc->age_dev,
972179100Syongari		    "could not create SMB DMA tag.\n");
973179100Syongari		goto fail;
974179100Syongari	}
975179100Syongari
976179100Syongari	/* Allocate DMA'able memory and load the DMA map. */
977179100Syongari	error = bus_dmamem_alloc(sc->age_cdata.age_tx_ring_tag,
978179100Syongari	    (void **)&sc->age_rdata.age_tx_ring,
979179100Syongari	    BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT,
980179100Syongari	    &sc->age_cdata.age_tx_ring_map);
981179100Syongari	if (error != 0) {
982179100Syongari		device_printf(sc->age_dev,
983179100Syongari		    "could not allocate DMA'able memory for Tx ring.\n");
984179100Syongari		goto fail;
985179100Syongari	}
986179100Syongari	ctx.age_busaddr = 0;
987179100Syongari	error = bus_dmamap_load(sc->age_cdata.age_tx_ring_tag,
988179100Syongari	    sc->age_cdata.age_tx_ring_map, sc->age_rdata.age_tx_ring,
989179100Syongari	    AGE_TX_RING_SZ, age_dmamap_cb, &ctx, 0);
990179100Syongari	if (error != 0 || ctx.age_busaddr == 0) {
991179100Syongari		device_printf(sc->age_dev,
992179100Syongari		    "could not load DMA'able memory for Tx ring.\n");
993179100Syongari		goto fail;
994179100Syongari	}
995179100Syongari	sc->age_rdata.age_tx_ring_paddr = ctx.age_busaddr;
996179100Syongari	/* Rx ring */
997179100Syongari	error = bus_dmamem_alloc(sc->age_cdata.age_rx_ring_tag,
998179100Syongari	    (void **)&sc->age_rdata.age_rx_ring,
999179100Syongari	    BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT,
1000179100Syongari	    &sc->age_cdata.age_rx_ring_map);
1001179100Syongari	if (error != 0) {
1002179100Syongari		device_printf(sc->age_dev,
1003179100Syongari		    "could not allocate DMA'able memory for Rx ring.\n");
1004179100Syongari		goto fail;
1005179100Syongari	}
1006179100Syongari	ctx.age_busaddr = 0;
1007179100Syongari	error = bus_dmamap_load(sc->age_cdata.age_rx_ring_tag,
1008179100Syongari	    sc->age_cdata.age_rx_ring_map, sc->age_rdata.age_rx_ring,
1009179100Syongari	    AGE_RX_RING_SZ, age_dmamap_cb, &ctx, 0);
1010179100Syongari	if (error != 0 || ctx.age_busaddr == 0) {
1011179100Syongari		device_printf(sc->age_dev,
1012179100Syongari		    "could not load DMA'able memory for Rx ring.\n");
1013179100Syongari		goto fail;
1014179100Syongari	}
1015179100Syongari	sc->age_rdata.age_rx_ring_paddr = ctx.age_busaddr;
1016179100Syongari	/* Rx return ring */
1017179100Syongari	error = bus_dmamem_alloc(sc->age_cdata.age_rr_ring_tag,
1018179100Syongari	    (void **)&sc->age_rdata.age_rr_ring,
1019179100Syongari	    BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT,
1020179100Syongari	    &sc->age_cdata.age_rr_ring_map);
1021179100Syongari	if (error != 0) {
1022179100Syongari		device_printf(sc->age_dev,
1023179100Syongari		    "could not allocate DMA'able memory for Rx return ring.\n");
1024179100Syongari		goto fail;
1025179100Syongari	}
1026179100Syongari	ctx.age_busaddr = 0;
1027179100Syongari	error = bus_dmamap_load(sc->age_cdata.age_rr_ring_tag,
1028179100Syongari	    sc->age_cdata.age_rr_ring_map, sc->age_rdata.age_rr_ring,
1029179100Syongari	    AGE_RR_RING_SZ, age_dmamap_cb,
1030179100Syongari	    &ctx, 0);
1031179100Syongari	if (error != 0 || ctx.age_busaddr == 0) {
1032179100Syongari		device_printf(sc->age_dev,
1033179100Syongari		    "could not load DMA'able memory for Rx return ring.\n");
1034179100Syongari		goto fail;
1035179100Syongari	}
1036179100Syongari	sc->age_rdata.age_rr_ring_paddr = ctx.age_busaddr;
1037179100Syongari	/* CMB block */
1038179100Syongari	error = bus_dmamem_alloc(sc->age_cdata.age_cmb_block_tag,
1039179100Syongari	    (void **)&sc->age_rdata.age_cmb_block,
1040179100Syongari	    BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT,
1041179100Syongari	    &sc->age_cdata.age_cmb_block_map);
1042179100Syongari	if (error != 0) {
1043179100Syongari		device_printf(sc->age_dev,
1044179100Syongari		    "could not allocate DMA'able memory for CMB block.\n");
1045179100Syongari		goto fail;
1046179100Syongari	}
1047179100Syongari	ctx.age_busaddr = 0;
1048179100Syongari	error = bus_dmamap_load(sc->age_cdata.age_cmb_block_tag,
1049179100Syongari	    sc->age_cdata.age_cmb_block_map, sc->age_rdata.age_cmb_block,
1050179100Syongari	    AGE_CMB_BLOCK_SZ, age_dmamap_cb, &ctx, 0);
1051179100Syongari	if (error != 0 || ctx.age_busaddr == 0) {
1052179100Syongari		device_printf(sc->age_dev,
1053179100Syongari		    "could not load DMA'able memory for CMB block.\n");
1054179100Syongari		goto fail;
1055179100Syongari	}
1056179100Syongari	sc->age_rdata.age_cmb_block_paddr = ctx.age_busaddr;
1057179100Syongari	/* SMB block */
1058179100Syongari	error = bus_dmamem_alloc(sc->age_cdata.age_smb_block_tag,
1059179100Syongari	    (void **)&sc->age_rdata.age_smb_block,
1060179100Syongari	    BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT,
1061179100Syongari	    &sc->age_cdata.age_smb_block_map);
1062179100Syongari	if (error != 0) {
1063179100Syongari		device_printf(sc->age_dev,
1064179100Syongari		    "could not allocate DMA'able memory for SMB block.\n");
1065179100Syongari		goto fail;
1066179100Syongari	}
1067179100Syongari	ctx.age_busaddr = 0;
1068179100Syongari	error = bus_dmamap_load(sc->age_cdata.age_smb_block_tag,
1069179100Syongari	    sc->age_cdata.age_smb_block_map, sc->age_rdata.age_smb_block,
1070179100Syongari	    AGE_SMB_BLOCK_SZ, age_dmamap_cb, &ctx, 0);
1071179100Syongari	if (error != 0 || ctx.age_busaddr == 0) {
1072179100Syongari		device_printf(sc->age_dev,
1073179100Syongari		    "could not load DMA'able memory for SMB block.\n");
1074179100Syongari		goto fail;
1075179100Syongari	}
1076179100Syongari	sc->age_rdata.age_smb_block_paddr = ctx.age_busaddr;
1077179100Syongari
1078179100Syongari	/*
1079179100Syongari	 * All ring buffer and DMA blocks should have the same
1080179100Syongari	 * high address part of 64bit DMA address space.
1081179100Syongari	 */
1082179100Syongari	if (lowaddr != BUS_SPACE_MAXADDR_32BIT &&
1083179100Syongari	    (error = age_check_boundary(sc)) != 0) {
1084179100Syongari		device_printf(sc->age_dev, "4GB boundary crossed, "
1085179100Syongari		    "switching to 32bit DMA addressing mode.\n");
1086179100Syongari		age_dma_free(sc);
1087179100Syongari		/* Limit DMA address space to 32bit and try again. */
1088179100Syongari		lowaddr = BUS_SPACE_MAXADDR_32BIT;
1089179100Syongari		goto again;
1090179100Syongari	}
1091179100Syongari
1092179100Syongari	/*
1093179100Syongari	 * Create Tx/Rx buffer parent tag.
1094179100Syongari	 * L1 supports full 64bit DMA addressing in Tx/Rx buffers
1095179100Syongari	 * so it needs separate parent DMA tag.
1096220249Syongari	 * XXX
1097220249Syongari	 * It seems enabling 64bit DMA causes data corruption. Limit
1098220249Syongari	 * DMA address space to 32bit.
1099179100Syongari	 */
1100179100Syongari	error = bus_dma_tag_create(
1101179100Syongari	    bus_get_dma_tag(sc->age_dev), /* parent */
1102179100Syongari	    1, 0,			/* alignment, boundary */
1103220249Syongari	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
1104179100Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
1105179100Syongari	    NULL, NULL,			/* filter, filterarg */
1106179100Syongari	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsize */
1107179100Syongari	    0,				/* nsegments */
1108179100Syongari	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
1109179100Syongari	    0,				/* flags */
1110179100Syongari	    NULL, NULL,			/* lockfunc, lockarg */
1111179100Syongari	    &sc->age_cdata.age_buffer_tag);
1112179100Syongari	if (error != 0) {
1113179100Syongari		device_printf(sc->age_dev,
1114179100Syongari		    "could not create parent buffer DMA tag.\n");
1115179100Syongari		goto fail;
1116179100Syongari	}
1117179100Syongari
1118179100Syongari	/* Create tag for Tx buffers. */
1119179100Syongari	error = bus_dma_tag_create(
1120179100Syongari	    sc->age_cdata.age_buffer_tag, /* parent */
1121179100Syongari	    1, 0,			/* alignment, boundary */
1122179100Syongari	    BUS_SPACE_MAXADDR,		/* lowaddr */
1123179100Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
1124179100Syongari	    NULL, NULL,			/* filter, filterarg */
1125179100Syongari	    AGE_TSO_MAXSIZE,		/* maxsize */
1126179100Syongari	    AGE_MAXTXSEGS,		/* nsegments */
1127179100Syongari	    AGE_TSO_MAXSEGSIZE,		/* maxsegsize */
1128179100Syongari	    0,				/* flags */
1129179100Syongari	    NULL, NULL,			/* lockfunc, lockarg */
1130179100Syongari	    &sc->age_cdata.age_tx_tag);
1131179100Syongari	if (error != 0) {
1132179100Syongari		device_printf(sc->age_dev, "could not create Tx DMA tag.\n");
1133179100Syongari		goto fail;
1134179100Syongari	}
1135179100Syongari
1136179100Syongari	/* Create tag for Rx buffers. */
1137179100Syongari	error = bus_dma_tag_create(
1138179100Syongari	    sc->age_cdata.age_buffer_tag, /* parent */
1139246341Syongari	    AGE_RX_BUF_ALIGN, 0,	/* alignment, boundary */
1140179100Syongari	    BUS_SPACE_MAXADDR,		/* lowaddr */
1141179100Syongari	    BUS_SPACE_MAXADDR,		/* highaddr */
1142179100Syongari	    NULL, NULL,			/* filter, filterarg */
1143179100Syongari	    MCLBYTES,			/* maxsize */
1144179100Syongari	    1,				/* nsegments */
1145179100Syongari	    MCLBYTES,			/* maxsegsize */
1146179100Syongari	    0,				/* flags */
1147179100Syongari	    NULL, NULL,			/* lockfunc, lockarg */
1148179100Syongari	    &sc->age_cdata.age_rx_tag);
1149179100Syongari	if (error != 0) {
1150179100Syongari		device_printf(sc->age_dev, "could not create Rx DMA tag.\n");
1151179100Syongari		goto fail;
1152179100Syongari	}
1153179100Syongari
1154179100Syongari	/* Create DMA maps for Tx buffers. */
1155179100Syongari	for (i = 0; i < AGE_TX_RING_CNT; i++) {
1156179100Syongari		txd = &sc->age_cdata.age_txdesc[i];
1157179100Syongari		txd->tx_m = NULL;
1158179100Syongari		txd->tx_dmamap = NULL;
1159179100Syongari		error = bus_dmamap_create(sc->age_cdata.age_tx_tag, 0,
1160179100Syongari		    &txd->tx_dmamap);
1161179100Syongari		if (error != 0) {
1162179100Syongari			device_printf(sc->age_dev,
1163179100Syongari			    "could not create Tx dmamap.\n");
1164179100Syongari			goto fail;
1165179100Syongari		}
1166179100Syongari	}
1167179100Syongari	/* Create DMA maps for Rx buffers. */
1168179100Syongari	if ((error = bus_dmamap_create(sc->age_cdata.age_rx_tag, 0,
1169179100Syongari	    &sc->age_cdata.age_rx_sparemap)) != 0) {
1170179100Syongari		device_printf(sc->age_dev,
1171179100Syongari		    "could not create spare Rx dmamap.\n");
1172179100Syongari		goto fail;
1173179100Syongari	}
1174179100Syongari	for (i = 0; i < AGE_RX_RING_CNT; i++) {
1175179100Syongari		rxd = &sc->age_cdata.age_rxdesc[i];
1176179100Syongari		rxd->rx_m = NULL;
1177179100Syongari		rxd->rx_dmamap = NULL;
1178179100Syongari		error = bus_dmamap_create(sc->age_cdata.age_rx_tag, 0,
1179179100Syongari		    &rxd->rx_dmamap);
1180179100Syongari		if (error != 0) {
1181179100Syongari			device_printf(sc->age_dev,
1182179100Syongari			    "could not create Rx dmamap.\n");
1183179100Syongari			goto fail;
1184179100Syongari		}
1185179100Syongari	}
1186179100Syongari
1187179100Syongarifail:
1188179100Syongari	return (error);
1189179100Syongari}
1190179100Syongari
1191179100Syongaristatic void
1192179100Syongariage_dma_free(struct age_softc *sc)
1193179100Syongari{
1194179100Syongari	struct age_txdesc *txd;
1195179100Syongari	struct age_rxdesc *rxd;
1196179100Syongari	int i;
1197179100Syongari
1198179100Syongari	/* Tx buffers */
1199179100Syongari	if (sc->age_cdata.age_tx_tag != NULL) {
1200179100Syongari		for (i = 0; i < AGE_TX_RING_CNT; i++) {
1201179100Syongari			txd = &sc->age_cdata.age_txdesc[i];
1202179100Syongari			if (txd->tx_dmamap != NULL) {
1203179100Syongari				bus_dmamap_destroy(sc->age_cdata.age_tx_tag,
1204179100Syongari				    txd->tx_dmamap);
1205179100Syongari				txd->tx_dmamap = NULL;
1206179100Syongari			}
1207179100Syongari		}
1208179100Syongari		bus_dma_tag_destroy(sc->age_cdata.age_tx_tag);
1209179100Syongari		sc->age_cdata.age_tx_tag = NULL;
1210179100Syongari	}
1211179100Syongari	/* Rx buffers */
1212179100Syongari	if (sc->age_cdata.age_rx_tag != NULL) {
1213179100Syongari		for (i = 0; i < AGE_RX_RING_CNT; i++) {
1214179100Syongari			rxd = &sc->age_cdata.age_rxdesc[i];
1215179100Syongari			if (rxd->rx_dmamap != NULL) {
1216179100Syongari				bus_dmamap_destroy(sc->age_cdata.age_rx_tag,
1217179100Syongari				    rxd->rx_dmamap);
1218179100Syongari				rxd->rx_dmamap = NULL;
1219179100Syongari			}
1220179100Syongari		}
1221179100Syongari		if (sc->age_cdata.age_rx_sparemap != NULL) {
1222179100Syongari			bus_dmamap_destroy(sc->age_cdata.age_rx_tag,
1223179100Syongari			    sc->age_cdata.age_rx_sparemap);
1224179100Syongari			sc->age_cdata.age_rx_sparemap = NULL;
1225179100Syongari		}
1226179100Syongari		bus_dma_tag_destroy(sc->age_cdata.age_rx_tag);
1227179100Syongari		sc->age_cdata.age_rx_tag = NULL;
1228179100Syongari	}
1229179100Syongari	/* Tx ring. */
1230179100Syongari	if (sc->age_cdata.age_tx_ring_tag != NULL) {
1231267363Sjhb		if (sc->age_rdata.age_tx_ring_paddr != 0)
1232179100Syongari			bus_dmamap_unload(sc->age_cdata.age_tx_ring_tag,
1233179100Syongari			    sc->age_cdata.age_tx_ring_map);
1234267363Sjhb		if (sc->age_rdata.age_tx_ring != NULL)
1235179100Syongari			bus_dmamem_free(sc->age_cdata.age_tx_ring_tag,
1236179100Syongari			    sc->age_rdata.age_tx_ring,
1237179100Syongari			    sc->age_cdata.age_tx_ring_map);
1238267363Sjhb		sc->age_rdata.age_tx_ring_paddr = 0;
1239179100Syongari		sc->age_rdata.age_tx_ring = NULL;
1240179100Syongari		bus_dma_tag_destroy(sc->age_cdata.age_tx_ring_tag);
1241179100Syongari		sc->age_cdata.age_tx_ring_tag = NULL;
1242179100Syongari	}
1243179100Syongari	/* Rx ring. */
1244179100Syongari	if (sc->age_cdata.age_rx_ring_tag != NULL) {
1245267363Sjhb		if (sc->age_rdata.age_rx_ring_paddr != 0)
1246179100Syongari			bus_dmamap_unload(sc->age_cdata.age_rx_ring_tag,
1247179100Syongari			    sc->age_cdata.age_rx_ring_map);
1248267363Sjhb		if (sc->age_rdata.age_rx_ring != NULL)
1249179100Syongari			bus_dmamem_free(sc->age_cdata.age_rx_ring_tag,
1250179100Syongari			    sc->age_rdata.age_rx_ring,
1251179100Syongari			    sc->age_cdata.age_rx_ring_map);
1252267363Sjhb		sc->age_rdata.age_rx_ring_paddr = 0;
1253179100Syongari		sc->age_rdata.age_rx_ring = NULL;
1254179100Syongari		bus_dma_tag_destroy(sc->age_cdata.age_rx_ring_tag);
1255179100Syongari		sc->age_cdata.age_rx_ring_tag = NULL;
1256179100Syongari	}
1257179100Syongari	/* Rx return ring. */
1258179100Syongari	if (sc->age_cdata.age_rr_ring_tag != NULL) {
1259267363Sjhb		if (sc->age_rdata.age_rr_ring_paddr != 0)
1260179100Syongari			bus_dmamap_unload(sc->age_cdata.age_rr_ring_tag,
1261179100Syongari			    sc->age_cdata.age_rr_ring_map);
1262267363Sjhb		if (sc->age_rdata.age_rr_ring != NULL)
1263179100Syongari			bus_dmamem_free(sc->age_cdata.age_rr_ring_tag,
1264179100Syongari			    sc->age_rdata.age_rr_ring,
1265179100Syongari			    sc->age_cdata.age_rr_ring_map);
1266267363Sjhb		sc->age_rdata.age_rr_ring_paddr = 0;
1267179100Syongari		sc->age_rdata.age_rr_ring = NULL;
1268179100Syongari		bus_dma_tag_destroy(sc->age_cdata.age_rr_ring_tag);
1269179100Syongari		sc->age_cdata.age_rr_ring_tag = NULL;
1270179100Syongari	}
1271179100Syongari	/* CMB block */
1272179100Syongari	if (sc->age_cdata.age_cmb_block_tag != NULL) {
1273267363Sjhb		if (sc->age_rdata.age_cmb_block_paddr != 0)
1274179100Syongari			bus_dmamap_unload(sc->age_cdata.age_cmb_block_tag,
1275179100Syongari			    sc->age_cdata.age_cmb_block_map);
1276267363Sjhb		if (sc->age_rdata.age_cmb_block != NULL)
1277179100Syongari			bus_dmamem_free(sc->age_cdata.age_cmb_block_tag,
1278179100Syongari			    sc->age_rdata.age_cmb_block,
1279179100Syongari			    sc->age_cdata.age_cmb_block_map);
1280267363Sjhb		sc->age_rdata.age_cmb_block_paddr = 0;
1281179100Syongari		sc->age_rdata.age_cmb_block = NULL;
1282179100Syongari		bus_dma_tag_destroy(sc->age_cdata.age_cmb_block_tag);
1283179100Syongari		sc->age_cdata.age_cmb_block_tag = NULL;
1284179100Syongari	}
1285179100Syongari	/* SMB block */
1286179100Syongari	if (sc->age_cdata.age_smb_block_tag != NULL) {
1287267363Sjhb		if (sc->age_rdata.age_smb_block_paddr != 0)
1288179100Syongari			bus_dmamap_unload(sc->age_cdata.age_smb_block_tag,
1289179100Syongari			    sc->age_cdata.age_smb_block_map);
1290267363Sjhb		if (sc->age_rdata.age_smb_block != NULL)
1291179100Syongari			bus_dmamem_free(sc->age_cdata.age_smb_block_tag,
1292179100Syongari			    sc->age_rdata.age_smb_block,
1293179100Syongari			    sc->age_cdata.age_smb_block_map);
1294267363Sjhb		sc->age_rdata.age_smb_block_paddr = 0;
1295179100Syongari		sc->age_rdata.age_smb_block = NULL;
1296179100Syongari		bus_dma_tag_destroy(sc->age_cdata.age_smb_block_tag);
1297179100Syongari		sc->age_cdata.age_smb_block_tag = NULL;
1298179100Syongari	}
1299179100Syongari
1300179100Syongari	if (sc->age_cdata.age_buffer_tag != NULL) {
1301179100Syongari		bus_dma_tag_destroy(sc->age_cdata.age_buffer_tag);
1302179100Syongari		sc->age_cdata.age_buffer_tag = NULL;
1303179100Syongari	}
1304179100Syongari	if (sc->age_cdata.age_parent_tag != NULL) {
1305179100Syongari		bus_dma_tag_destroy(sc->age_cdata.age_parent_tag);
1306179100Syongari		sc->age_cdata.age_parent_tag = NULL;
1307179100Syongari	}
1308179100Syongari}
1309179100Syongari
1310179100Syongari/*
1311179100Syongari *	Make sure the interface is stopped at reboot time.
1312179100Syongari */
1313179100Syongaristatic int
1314179100Syongariage_shutdown(device_t dev)
1315179100Syongari{
1316179100Syongari
1317179100Syongari	return (age_suspend(dev));
1318179100Syongari}
1319179100Syongari
1320179100Syongaristatic void
1321179100Syongariage_setwol(struct age_softc *sc)
1322179100Syongari{
1323179100Syongari	struct ifnet *ifp;
1324179100Syongari	struct mii_data *mii;
1325179100Syongari	uint32_t reg, pmcs;
1326179100Syongari	uint16_t pmstat;
1327179100Syongari	int aneg, i, pmc;
1328179100Syongari
1329179100Syongari	AGE_LOCK_ASSERT(sc);
1330179100Syongari
1331219902Sjhb	if (pci_find_cap(sc->age_dev, PCIY_PMG, &pmc) != 0) {
1332179100Syongari		CSR_WRITE_4(sc, AGE_WOL_CFG, 0);
1333179100Syongari		/*
1334179100Syongari		 * No PME capability, PHY power down.
1335179100Syongari		 * XXX
1336179100Syongari		 * Due to an unknown reason powering down PHY resulted
1337179100Syongari		 * in unexpected results such as inaccessbility of
1338179100Syongari		 * hardware of freshly rebooted system. Disable
1339179100Syongari		 * powering down PHY until I got more information for
1340179100Syongari		 * Attansic/Atheros PHY hardwares.
1341179100Syongari		 */
1342179100Syongari#ifdef notyet
1343179100Syongari		age_miibus_writereg(sc->age_dev, sc->age_phyaddr,
1344179100Syongari		    MII_BMCR, BMCR_PDOWN);
1345179100Syongari#endif
1346179100Syongari		return;
1347179100Syongari	}
1348179100Syongari
1349179100Syongari	ifp = sc->age_ifp;
1350179100Syongari	if ((ifp->if_capenable & IFCAP_WOL) != 0) {
1351179100Syongari		/*
1352179100Syongari		 * Note, this driver resets the link speed to 10/100Mbps with
1353179100Syongari		 * auto-negotiation but we don't know whether that operation
1354179100Syongari		 * would succeed or not as it have no control after powering
1355179100Syongari		 * off. If the renegotiation fail WOL may not work. Running
1356179100Syongari		 * at 1Gbps will draw more power than 375mA at 3.3V which is
1357179100Syongari		 * specified in PCI specification and that would result in
1358179100Syongari		 * complete shutdowning power to ethernet controller.
1359179100Syongari		 *
1360179100Syongari		 * TODO
1361179100Syongari		 *  Save current negotiated media speed/duplex/flow-control
1362179100Syongari		 *  to softc and restore the same link again after resuming.
1363179100Syongari		 *  PHY handling such as power down/resetting to 100Mbps
1364179100Syongari		 *  may be better handled in suspend method in phy driver.
1365179100Syongari		 */
1366179100Syongari		mii = device_get_softc(sc->age_miibus);
1367179100Syongari		mii_pollstat(mii);
1368179100Syongari		aneg = 0;
1369179100Syongari		if ((mii->mii_media_status & IFM_AVALID) != 0) {
1370179100Syongari			switch IFM_SUBTYPE(mii->mii_media_active) {
1371179100Syongari			case IFM_10_T:
1372179100Syongari			case IFM_100_TX:
1373179100Syongari				goto got_link;
1374179100Syongari			case IFM_1000_T:
1375179100Syongari				aneg++;
1376179100Syongari			default:
1377179100Syongari				break;
1378179100Syongari			}
1379179100Syongari		}
1380179100Syongari		age_miibus_writereg(sc->age_dev, sc->age_phyaddr,
1381179100Syongari		    MII_100T2CR, 0);
1382179100Syongari		age_miibus_writereg(sc->age_dev, sc->age_phyaddr,
1383179100Syongari		    MII_ANAR, ANAR_TX_FD | ANAR_TX | ANAR_10_FD |
1384179100Syongari		    ANAR_10 | ANAR_CSMA);
1385179100Syongari		age_miibus_writereg(sc->age_dev, sc->age_phyaddr,
1386179100Syongari		    MII_BMCR, BMCR_RESET | BMCR_AUTOEN | BMCR_STARTNEG);
1387179100Syongari		DELAY(1000);
1388179100Syongari		if (aneg != 0) {
1389181717Skevlo			/* Poll link state until age(4) get a 10/100 link. */
1390179100Syongari			for (i = 0; i < MII_ANEGTICKS_GIGE; i++) {
1391179100Syongari				mii_pollstat(mii);
1392179100Syongari				if ((mii->mii_media_status & IFM_AVALID) != 0) {
1393179100Syongari					switch (IFM_SUBTYPE(
1394179100Syongari					    mii->mii_media_active)) {
1395179100Syongari					case IFM_10_T:
1396179100Syongari					case IFM_100_TX:
1397179100Syongari						age_mac_config(sc);
1398179100Syongari						goto got_link;
1399179100Syongari					default:
1400179100Syongari						break;
1401179100Syongari					}
1402179100Syongari				}
1403179100Syongari				AGE_UNLOCK(sc);
1404179100Syongari				pause("agelnk", hz);
1405179100Syongari				AGE_LOCK(sc);
1406179100Syongari			}
1407179100Syongari			if (i == MII_ANEGTICKS_GIGE)
1408179100Syongari				device_printf(sc->age_dev,
1409179100Syongari				    "establishing link failed, "
1410179100Syongari				    "WOL may not work!");
1411179100Syongari		}
1412179100Syongari		/*
1413179100Syongari		 * No link, force MAC to have 100Mbps, full-duplex link.
1414179100Syongari		 * This is the last resort and may/may not work.
1415179100Syongari		 */
1416179100Syongari		mii->mii_media_status = IFM_AVALID | IFM_ACTIVE;
1417179100Syongari		mii->mii_media_active = IFM_ETHER | IFM_100_TX | IFM_FDX;
1418179100Syongari		age_mac_config(sc);
1419179100Syongari	}
1420179100Syongari
1421179100Syongarigot_link:
1422179100Syongari	pmcs = 0;
1423179100Syongari	if ((ifp->if_capenable & IFCAP_WOL_MAGIC) != 0)
1424179100Syongari		pmcs |= WOL_CFG_MAGIC | WOL_CFG_MAGIC_ENB;
1425179100Syongari	CSR_WRITE_4(sc, AGE_WOL_CFG, pmcs);
1426179100Syongari	reg = CSR_READ_4(sc, AGE_MAC_CFG);
1427179100Syongari	reg &= ~(MAC_CFG_DBG | MAC_CFG_PROMISC);
1428179100Syongari	reg &= ~(MAC_CFG_ALLMULTI | MAC_CFG_BCAST);
1429179100Syongari	if ((ifp->if_capenable & IFCAP_WOL_MCAST) != 0)
1430179100Syongari		reg |= MAC_CFG_ALLMULTI | MAC_CFG_BCAST;
1431179100Syongari	if ((ifp->if_capenable & IFCAP_WOL) != 0) {
1432179100Syongari		reg |= MAC_CFG_RX_ENB;
1433179100Syongari		CSR_WRITE_4(sc, AGE_MAC_CFG, reg);
1434179100Syongari	}
1435179100Syongari
1436179100Syongari	/* Request PME. */
1437179100Syongari	pmstat = pci_read_config(sc->age_dev, pmc + PCIR_POWER_STATUS, 2);
1438179100Syongari	pmstat &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE);
1439179100Syongari	if ((ifp->if_capenable & IFCAP_WOL) != 0)
1440179100Syongari		pmstat |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE;
1441179100Syongari	pci_write_config(sc->age_dev, pmc + PCIR_POWER_STATUS, pmstat, 2);
1442179100Syongari#ifdef notyet
1443179100Syongari	/* See above for powering down PHY issues. */
1444179100Syongari	if ((ifp->if_capenable & IFCAP_WOL) == 0) {
1445179100Syongari		/* No WOL, PHY power down. */
1446179100Syongari		age_miibus_writereg(sc->age_dev, sc->age_phyaddr,
1447179100Syongari		    MII_BMCR, BMCR_PDOWN);
1448179100Syongari	}
1449179100Syongari#endif
1450179100Syongari}
1451179100Syongari
1452179100Syongaristatic int
1453179100Syongariage_suspend(device_t dev)
1454179100Syongari{
1455179100Syongari	struct age_softc *sc;
1456179100Syongari
1457179100Syongari	sc = device_get_softc(dev);
1458179100Syongari
1459179100Syongari	AGE_LOCK(sc);
1460179100Syongari	age_stop(sc);
1461179100Syongari	age_setwol(sc);
1462179100Syongari	AGE_UNLOCK(sc);
1463179100Syongari
1464179100Syongari	return (0);
1465179100Syongari}
1466179100Syongari
1467179100Syongaristatic int
1468179100Syongariage_resume(device_t dev)
1469179100Syongari{
1470179100Syongari	struct age_softc *sc;
1471179100Syongari	struct ifnet *ifp;
1472179100Syongari
1473179100Syongari	sc = device_get_softc(dev);
1474179100Syongari
1475179100Syongari	AGE_LOCK(sc);
1476190499Syongari	age_phy_reset(sc);
1477179100Syongari	ifp = sc->age_ifp;
1478179100Syongari	if ((ifp->if_flags & IFF_UP) != 0)
1479179100Syongari		age_init_locked(sc);
1480179100Syongari
1481179100Syongari	AGE_UNLOCK(sc);
1482179100Syongari
1483179100Syongari	return (0);
1484179100Syongari}
1485179100Syongari
1486179100Syongaristatic int
1487179100Syongariage_encap(struct age_softc *sc, struct mbuf **m_head)
1488179100Syongari{
1489179100Syongari	struct age_txdesc *txd, *txd_last;
1490179100Syongari	struct tx_desc *desc;
1491179100Syongari	struct mbuf *m;
1492179100Syongari	struct ip *ip;
1493179100Syongari	struct tcphdr *tcp;
1494179100Syongari	bus_dma_segment_t txsegs[AGE_MAXTXSEGS];
1495179100Syongari	bus_dmamap_t map;
1496242348Syongari	uint32_t cflags, hdrlen, ip_off, poff, vtag;
1497179100Syongari	int error, i, nsegs, prod, si;
1498179100Syongari
1499179100Syongari	AGE_LOCK_ASSERT(sc);
1500179100Syongari
1501179100Syongari	M_ASSERTPKTHDR((*m_head));
1502179100Syongari
1503179100Syongari	m = *m_head;
1504179100Syongari	ip = NULL;
1505179100Syongari	tcp = NULL;
1506179100Syongari	cflags = vtag = 0;
1507179100Syongari	ip_off = poff = 0;
1508179100Syongari	if ((m->m_pkthdr.csum_flags & (AGE_CSUM_FEATURES | CSUM_TSO)) != 0) {
1509179100Syongari		/*
1510179100Syongari		 * L1 requires offset of TCP/UDP payload in its Tx
1511179100Syongari		 * descriptor to perform hardware Tx checksum offload.
1512179100Syongari		 * Additionally, TSO requires IP/TCP header size and
1513179100Syongari		 * modification of IP/TCP header in order to make TSO
1514179100Syongari		 * engine work. This kind of operation takes many CPU
1515179100Syongari		 * cycles on FreeBSD so fast host CPU is needed to get
1516179100Syongari		 * smooth TSO performance.
1517179100Syongari		 */
1518179100Syongari		struct ether_header *eh;
1519179100Syongari
1520179100Syongari		if (M_WRITABLE(m) == 0) {
1521179100Syongari			/* Get a writable copy. */
1522243857Sglebius			m = m_dup(*m_head, M_NOWAIT);
1523179100Syongari			/* Release original mbufs. */
1524179100Syongari			m_freem(*m_head);
1525179100Syongari			if (m == NULL) {
1526179100Syongari				*m_head = NULL;
1527179100Syongari				return (ENOBUFS);
1528179100Syongari			}
1529179100Syongari			*m_head = m;
1530179100Syongari		}
1531179100Syongari		ip_off = sizeof(struct ether_header);
1532179100Syongari		m = m_pullup(m, ip_off);
1533179100Syongari		if (m == NULL) {
1534179100Syongari			*m_head = NULL;
1535179100Syongari			return (ENOBUFS);
1536179100Syongari		}
1537179100Syongari		eh = mtod(m, struct ether_header *);
1538179100Syongari		/*
1539179100Syongari		 * Check if hardware VLAN insertion is off.
1540179100Syongari		 * Additional check for LLC/SNAP frame?
1541179100Syongari		 */
1542179100Syongari		if (eh->ether_type == htons(ETHERTYPE_VLAN)) {
1543179100Syongari			ip_off = sizeof(struct ether_vlan_header);
1544179100Syongari			m = m_pullup(m, ip_off);
1545179100Syongari			if (m == NULL) {
1546179100Syongari				*m_head = NULL;
1547179100Syongari				return (ENOBUFS);
1548179100Syongari			}
1549179100Syongari		}
1550179100Syongari		m = m_pullup(m, ip_off + sizeof(struct ip));
1551179100Syongari		if (m == NULL) {
1552179100Syongari			*m_head = NULL;
1553179100Syongari			return (ENOBUFS);
1554179100Syongari		}
1555179100Syongari		ip = (struct ip *)(mtod(m, char *) + ip_off);
1556179100Syongari		poff = ip_off + (ip->ip_hl << 2);
1557179100Syongari		if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0) {
1558179100Syongari			m = m_pullup(m, poff + sizeof(struct tcphdr));
1559179100Syongari			if (m == NULL) {
1560179100Syongari				*m_head = NULL;
1561179100Syongari				return (ENOBUFS);
1562179100Syongari			}
1563179100Syongari			tcp = (struct tcphdr *)(mtod(m, char *) + poff);
1564242348Syongari			m = m_pullup(m, poff + (tcp->th_off << 2));
1565242348Syongari			if (m == NULL) {
1566242348Syongari				*m_head = NULL;
1567242348Syongari				return (ENOBUFS);
1568242348Syongari			}
1569179100Syongari			/*
1570179100Syongari			 * L1 requires IP/TCP header size and offset as
1571179100Syongari			 * well as TCP pseudo checksum which complicates
1572179100Syongari			 * TSO configuration. I guess this comes from the
1573179100Syongari			 * adherence to Microsoft NDIS Large Send
1574179100Syongari			 * specification which requires insertion of
1575179100Syongari			 * pseudo checksum by upper stack. The pseudo
1576179100Syongari			 * checksum that NDIS refers to doesn't include
1577179100Syongari			 * TCP payload length so age(4) should recompute
1578179100Syongari			 * the pseudo checksum here. Hopefully this wouldn't
1579179100Syongari			 * be much burden on modern CPUs.
1580179100Syongari			 * Reset IP checksum and recompute TCP pseudo
1581179100Syongari			 * checksum as NDIS specification said.
1582179100Syongari			 */
1583242348Syongari			ip = (struct ip *)(mtod(m, char *) + ip_off);
1584242348Syongari			tcp = (struct tcphdr *)(mtod(m, char *) + poff);
1585179100Syongari			ip->ip_sum = 0;
1586242348Syongari			tcp->th_sum = in_pseudo(ip->ip_src.s_addr,
1587242348Syongari			    ip->ip_dst.s_addr, htons(IPPROTO_TCP));
1588179100Syongari		}
1589179100Syongari		*m_head = m;
1590179100Syongari	}
1591179100Syongari
1592179100Syongari	si = prod = sc->age_cdata.age_tx_prod;
1593179100Syongari	txd = &sc->age_cdata.age_txdesc[prod];
1594179100Syongari	txd_last = txd;
1595179100Syongari	map = txd->tx_dmamap;
1596179100Syongari
1597179100Syongari	error =  bus_dmamap_load_mbuf_sg(sc->age_cdata.age_tx_tag, map,
1598179100Syongari	    *m_head, txsegs, &nsegs, 0);
1599179100Syongari	if (error == EFBIG) {
1600243857Sglebius		m = m_collapse(*m_head, M_NOWAIT, AGE_MAXTXSEGS);
1601179100Syongari		if (m == NULL) {
1602179100Syongari			m_freem(*m_head);
1603179100Syongari			*m_head = NULL;
1604179100Syongari			return (ENOMEM);
1605179100Syongari		}
1606179100Syongari		*m_head = m;
1607179100Syongari		error = bus_dmamap_load_mbuf_sg(sc->age_cdata.age_tx_tag, map,
1608179100Syongari		    *m_head, txsegs, &nsegs, 0);
1609179100Syongari		if (error != 0) {
1610179100Syongari			m_freem(*m_head);
1611179100Syongari			*m_head = NULL;
1612179100Syongari			return (error);
1613179100Syongari		}
1614179100Syongari	} else if (error != 0)
1615179100Syongari		return (error);
1616179100Syongari	if (nsegs == 0) {
1617179100Syongari		m_freem(*m_head);
1618179100Syongari		*m_head = NULL;
1619179100Syongari		return (EIO);
1620179100Syongari	}
1621179100Syongari
1622179100Syongari	/* Check descriptor overrun. */
1623179100Syongari	if (sc->age_cdata.age_tx_cnt + nsegs >= AGE_TX_RING_CNT - 2) {
1624179100Syongari		bus_dmamap_unload(sc->age_cdata.age_tx_tag, map);
1625179100Syongari		return (ENOBUFS);
1626179100Syongari	}
1627179100Syongari
1628179100Syongari	m = *m_head;
1629242348Syongari	/* Configure VLAN hardware tag insertion. */
1630242348Syongari	if ((m->m_flags & M_VLANTAG) != 0) {
1631242348Syongari		vtag = AGE_TX_VLAN_TAG(m->m_pkthdr.ether_vtag);
1632242348Syongari		vtag = ((vtag << AGE_TD_VLAN_SHIFT) & AGE_TD_VLAN_MASK);
1633242348Syongari		cflags |= AGE_TD_INSERT_VLAN_TAG;
1634242348Syongari	}
1635242348Syongari
1636242348Syongari	desc = NULL;
1637242348Syongari	i = 0;
1638179100Syongari	if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0) {
1639242348Syongari		/* Request TSO and set MSS. */
1640242348Syongari		cflags |= AGE_TD_TSO_IPV4;
1641242348Syongari		cflags |= AGE_TD_IPCSUM | AGE_TD_TCPCSUM;
1642242348Syongari		cflags |= ((uint32_t)m->m_pkthdr.tso_segsz <<
1643242348Syongari		    AGE_TD_TSO_MSS_SHIFT);
1644179100Syongari		/* Set IP/TCP header size. */
1645179100Syongari		cflags |= ip->ip_hl << AGE_TD_IPHDR_LEN_SHIFT;
1646179100Syongari		cflags |= tcp->th_off << AGE_TD_TSO_TCPHDR_LEN_SHIFT;
1647242348Syongari		/*
1648242348Syongari		 * L1 requires the first buffer should only hold IP/TCP
1649242348Syongari		 * header data. TCP payload should be handled in other
1650242348Syongari		 * descriptors.
1651242348Syongari		 */
1652242348Syongari		hdrlen = poff + (tcp->th_off << 2);
1653242348Syongari		desc = &sc->age_rdata.age_tx_ring[prod];
1654242348Syongari		desc->addr = htole64(txsegs[0].ds_addr);
1655242348Syongari		desc->len = htole32(AGE_TX_BYTES(hdrlen) | vtag);
1656242348Syongari		desc->flags = htole32(cflags);
1657242348Syongari		sc->age_cdata.age_tx_cnt++;
1658242348Syongari		AGE_DESC_INC(prod, AGE_TX_RING_CNT);
1659242348Syongari		if (m->m_len - hdrlen > 0) {
1660242348Syongari			/* Handle remaining payload of the 1st fragment. */
1661242348Syongari			desc = &sc->age_rdata.age_tx_ring[prod];
1662242348Syongari			desc->addr = htole64(txsegs[0].ds_addr + hdrlen);
1663242348Syongari			desc->len = htole32(AGE_TX_BYTES(m->m_len - hdrlen) |
1664242348Syongari			    vtag);
1665242348Syongari			desc->flags = htole32(cflags);
1666242348Syongari			sc->age_cdata.age_tx_cnt++;
1667242348Syongari			AGE_DESC_INC(prod, AGE_TX_RING_CNT);
1668242348Syongari		}
1669242348Syongari		/* Handle remaining fragments. */
1670242348Syongari		i = 1;
1671206876Syongari	} else if ((m->m_pkthdr.csum_flags & AGE_CSUM_FEATURES) != 0) {
1672206876Syongari		/* Configure Tx IP/TCP/UDP checksum offload. */
1673206876Syongari		cflags |= AGE_TD_CSUM;
1674206876Syongari		if ((m->m_pkthdr.csum_flags & CSUM_TCP) != 0)
1675206876Syongari			cflags |= AGE_TD_TCPCSUM;
1676206876Syongari		if ((m->m_pkthdr.csum_flags & CSUM_UDP) != 0)
1677206876Syongari			cflags |= AGE_TD_UDPCSUM;
1678206876Syongari		/* Set checksum start offset. */
1679206876Syongari		cflags |= (poff << AGE_TD_CSUM_PLOADOFFSET_SHIFT);
1680206876Syongari		/* Set checksum insertion position of TCP/UDP. */
1681206876Syongari		cflags |= ((poff + m->m_pkthdr.csum_data) <<
1682206876Syongari		    AGE_TD_CSUM_XSUMOFFSET_SHIFT);
1683179100Syongari	}
1684242348Syongari	for (; i < nsegs; i++) {
1685179100Syongari		desc = &sc->age_rdata.age_tx_ring[prod];
1686179100Syongari		desc->addr = htole64(txsegs[i].ds_addr);
1687179100Syongari		desc->len = htole32(AGE_TX_BYTES(txsegs[i].ds_len) | vtag);
1688179100Syongari		desc->flags = htole32(cflags);
1689179100Syongari		sc->age_cdata.age_tx_cnt++;
1690179100Syongari		AGE_DESC_INC(prod, AGE_TX_RING_CNT);
1691179100Syongari	}
1692179100Syongari	/* Update producer index. */
1693179100Syongari	sc->age_cdata.age_tx_prod = prod;
1694179100Syongari
1695179100Syongari	/* Set EOP on the last descriptor. */
1696179100Syongari	prod = (prod + AGE_TX_RING_CNT - 1) % AGE_TX_RING_CNT;
1697179100Syongari	desc = &sc->age_rdata.age_tx_ring[prod];
1698179100Syongari	desc->flags |= htole32(AGE_TD_EOP);
1699179100Syongari
1700179100Syongari	/* Lastly set TSO header and modify IP/TCP header for TSO operation. */
1701179100Syongari	if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0) {
1702179100Syongari		desc = &sc->age_rdata.age_tx_ring[si];
1703179100Syongari		desc->flags |= htole32(AGE_TD_TSO_HDR);
1704179100Syongari	}
1705179100Syongari
1706179100Syongari	/* Swap dmamap of the first and the last. */
1707179100Syongari	txd = &sc->age_cdata.age_txdesc[prod];
1708179100Syongari	map = txd_last->tx_dmamap;
1709179100Syongari	txd_last->tx_dmamap = txd->tx_dmamap;
1710179100Syongari	txd->tx_dmamap = map;
1711179100Syongari	txd->tx_m = m;
1712179100Syongari
1713179100Syongari	/* Sync descriptors. */
1714179100Syongari	bus_dmamap_sync(sc->age_cdata.age_tx_tag, map, BUS_DMASYNC_PREWRITE);
1715179100Syongari	bus_dmamap_sync(sc->age_cdata.age_tx_ring_tag,
1716179100Syongari	    sc->age_cdata.age_tx_ring_map,
1717179100Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1718179100Syongari
1719179100Syongari	return (0);
1720179100Syongari}
1721179100Syongari
1722179100Syongaristatic void
1723216925Sjhbage_start(struct ifnet *ifp)
1724179100Syongari{
1725216925Sjhb        struct age_softc *sc;
1726179100Syongari
1727216925Sjhb	sc = ifp->if_softc;
1728216925Sjhb	AGE_LOCK(sc);
1729216925Sjhb	age_start_locked(ifp);
1730216925Sjhb	AGE_UNLOCK(sc);
1731179100Syongari}
1732179100Syongari
1733179100Syongaristatic void
1734216925Sjhbage_start_locked(struct ifnet *ifp)
1735179100Syongari{
1736179100Syongari        struct age_softc *sc;
1737179100Syongari        struct mbuf *m_head;
1738179100Syongari	int enq;
1739179100Syongari
1740179100Syongari	sc = ifp->if_softc;
1741179100Syongari
1742216925Sjhb	AGE_LOCK_ASSERT(sc);
1743179100Syongari
1744179100Syongari	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
1745216925Sjhb	    IFF_DRV_RUNNING || (sc->age_flags & AGE_FLAG_LINK) == 0)
1746179100Syongari		return;
1747179100Syongari
1748179100Syongari	for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd); ) {
1749179100Syongari		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
1750179100Syongari		if (m_head == NULL)
1751179100Syongari			break;
1752179100Syongari		/*
1753179100Syongari		 * Pack the data into the transmit ring. If we
1754179100Syongari		 * don't have room, set the OACTIVE flag and wait
1755179100Syongari		 * for the NIC to drain the ring.
1756179100Syongari		 */
1757179100Syongari		if (age_encap(sc, &m_head)) {
1758179100Syongari			if (m_head == NULL)
1759179100Syongari				break;
1760179100Syongari			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
1761179100Syongari			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1762179100Syongari			break;
1763179100Syongari		}
1764179100Syongari
1765179100Syongari		enq++;
1766179100Syongari		/*
1767179100Syongari		 * If there's a BPF listener, bounce a copy of this frame
1768179100Syongari		 * to him.
1769179100Syongari		 */
1770179100Syongari		ETHER_BPF_MTAP(ifp, m_head);
1771179100Syongari	}
1772179100Syongari
1773179100Syongari	if (enq > 0) {
1774179100Syongari		/* Update mbox. */
1775179100Syongari		AGE_COMMIT_MBOX(sc);
1776179100Syongari		/* Set a timeout in case the chip goes out to lunch. */
1777179100Syongari		sc->age_watchdog_timer = AGE_TX_TIMEOUT;
1778179100Syongari	}
1779179100Syongari}
1780179100Syongari
1781179100Syongaristatic void
1782179100Syongariage_watchdog(struct age_softc *sc)
1783179100Syongari{
1784179100Syongari	struct ifnet *ifp;
1785179100Syongari
1786179100Syongari	AGE_LOCK_ASSERT(sc);
1787179100Syongari
1788179100Syongari	if (sc->age_watchdog_timer == 0 || --sc->age_watchdog_timer)
1789179100Syongari		return;
1790179100Syongari
1791179100Syongari	ifp = sc->age_ifp;
1792179100Syongari	if ((sc->age_flags & AGE_FLAG_LINK) == 0) {
1793179100Syongari		if_printf(sc->age_ifp, "watchdog timeout (missed link)\n");
1794271828Sglebius		if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
1795211768Syongari		ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
1796179100Syongari		age_init_locked(sc);
1797179100Syongari		return;
1798179100Syongari	}
1799179100Syongari	if (sc->age_cdata.age_tx_cnt == 0) {
1800179100Syongari		if_printf(sc->age_ifp,
1801179100Syongari		    "watchdog timeout (missed Tx interrupts) -- recovering\n");
1802179100Syongari		if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
1803216925Sjhb			age_start_locked(ifp);
1804179100Syongari		return;
1805179100Syongari	}
1806179100Syongari	if_printf(sc->age_ifp, "watchdog timeout\n");
1807271828Sglebius	if_inc_counter(ifp, IFCOUNTER_OERRORS, 1);
1808211768Syongari	ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
1809179100Syongari	age_init_locked(sc);
1810179100Syongari	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
1811216925Sjhb		age_start_locked(ifp);
1812179100Syongari}
1813179100Syongari
1814179100Syongaristatic int
1815179100Syongariage_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
1816179100Syongari{
1817179100Syongari	struct age_softc *sc;
1818179100Syongari	struct ifreq *ifr;
1819179100Syongari	struct mii_data *mii;
1820179100Syongari	uint32_t reg;
1821179100Syongari	int error, mask;
1822179100Syongari
1823179100Syongari	sc = ifp->if_softc;
1824179100Syongari	ifr = (struct ifreq *)data;
1825179100Syongari	error = 0;
1826179100Syongari	switch (cmd) {
1827179100Syongari	case SIOCSIFMTU:
1828179100Syongari		if (ifr->ifr_mtu < ETHERMIN || ifr->ifr_mtu > AGE_JUMBO_MTU)
1829179100Syongari			error = EINVAL;
1830179100Syongari		else if (ifp->if_mtu != ifr->ifr_mtu) {
1831179100Syongari			AGE_LOCK(sc);
1832179100Syongari			ifp->if_mtu = ifr->ifr_mtu;
1833211768Syongari			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
1834211768Syongari				ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
1835179100Syongari				age_init_locked(sc);
1836211768Syongari			}
1837179100Syongari			AGE_UNLOCK(sc);
1838179100Syongari		}
1839179100Syongari		break;
1840179100Syongari	case SIOCSIFFLAGS:
1841179100Syongari		AGE_LOCK(sc);
1842179100Syongari		if ((ifp->if_flags & IFF_UP) != 0) {
1843179100Syongari			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
1844179100Syongari				if (((ifp->if_flags ^ sc->age_if_flags)
1845179100Syongari				    & (IFF_PROMISC | IFF_ALLMULTI)) != 0)
1846179100Syongari					age_rxfilter(sc);
1847179100Syongari			} else {
1848179100Syongari				if ((sc->age_flags & AGE_FLAG_DETACH) == 0)
1849179100Syongari					age_init_locked(sc);
1850179100Syongari			}
1851179100Syongari		} else {
1852179100Syongari			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
1853179100Syongari				age_stop(sc);
1854179100Syongari		}
1855179100Syongari		sc->age_if_flags = ifp->if_flags;
1856179100Syongari		AGE_UNLOCK(sc);
1857179100Syongari		break;
1858179100Syongari	case SIOCADDMULTI:
1859179100Syongari	case SIOCDELMULTI:
1860179100Syongari		AGE_LOCK(sc);
1861179100Syongari		if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
1862179100Syongari			age_rxfilter(sc);
1863179100Syongari		AGE_UNLOCK(sc);
1864179100Syongari		break;
1865179100Syongari	case SIOCSIFMEDIA:
1866179100Syongari	case SIOCGIFMEDIA:
1867179100Syongari		mii = device_get_softc(sc->age_miibus);
1868179100Syongari		error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
1869179100Syongari		break;
1870179100Syongari	case SIOCSIFCAP:
1871179100Syongari		AGE_LOCK(sc);
1872179100Syongari		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
1873179100Syongari		if ((mask & IFCAP_TXCSUM) != 0 &&
1874179100Syongari		    (ifp->if_capabilities & IFCAP_TXCSUM) != 0) {
1875179100Syongari			ifp->if_capenable ^= IFCAP_TXCSUM;
1876179100Syongari			if ((ifp->if_capenable & IFCAP_TXCSUM) != 0)
1877179100Syongari				ifp->if_hwassist |= AGE_CSUM_FEATURES;
1878179100Syongari			else
1879179100Syongari				ifp->if_hwassist &= ~AGE_CSUM_FEATURES;
1880179100Syongari		}
1881179100Syongari		if ((mask & IFCAP_RXCSUM) != 0 &&
1882179100Syongari		    (ifp->if_capabilities & IFCAP_RXCSUM) != 0) {
1883179100Syongari			ifp->if_capenable ^= IFCAP_RXCSUM;
1884179100Syongari			reg = CSR_READ_4(sc, AGE_MAC_CFG);
1885179100Syongari			reg &= ~MAC_CFG_RXCSUM_ENB;
1886179100Syongari			if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
1887179100Syongari				reg |= MAC_CFG_RXCSUM_ENB;
1888179100Syongari			CSR_WRITE_4(sc, AGE_MAC_CFG, reg);
1889179100Syongari		}
1890179100Syongari		if ((mask & IFCAP_TSO4) != 0 &&
1891179100Syongari		    (ifp->if_capabilities & IFCAP_TSO4) != 0) {
1892179100Syongari			ifp->if_capenable ^= IFCAP_TSO4;
1893179100Syongari			if ((ifp->if_capenable & IFCAP_TSO4) != 0)
1894179100Syongari				ifp->if_hwassist |= CSUM_TSO;
1895179100Syongari			else
1896179100Syongari				ifp->if_hwassist &= ~CSUM_TSO;
1897179100Syongari		}
1898179100Syongari
1899179100Syongari		if ((mask & IFCAP_WOL_MCAST) != 0 &&
1900179100Syongari		    (ifp->if_capabilities & IFCAP_WOL_MCAST) != 0)
1901179100Syongari			ifp->if_capenable ^= IFCAP_WOL_MCAST;
1902179100Syongari		if ((mask & IFCAP_WOL_MAGIC) != 0 &&
1903179100Syongari		    (ifp->if_capabilities & IFCAP_WOL_MAGIC) != 0)
1904179100Syongari			ifp->if_capenable ^= IFCAP_WOL_MAGIC;
1905179100Syongari		if ((mask & IFCAP_VLAN_HWCSUM) != 0 &&
1906179100Syongari		    (ifp->if_capabilities & IFCAP_VLAN_HWCSUM) != 0)
1907179100Syongari			ifp->if_capenable ^= IFCAP_VLAN_HWCSUM;
1908179100Syongari		if ((mask & IFCAP_VLAN_HWTSO) != 0 &&
1909179100Syongari		    (ifp->if_capabilities & IFCAP_VLAN_HWTSO) != 0)
1910179100Syongari			ifp->if_capenable ^= IFCAP_VLAN_HWTSO;
1911204377Syongari		if ((mask & IFCAP_VLAN_HWTAGGING) != 0 &&
1912204377Syongari		    (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) != 0) {
1913204377Syongari			ifp->if_capenable ^= IFCAP_VLAN_HWTAGGING;
1914204377Syongari			if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) == 0)
1915204377Syongari				ifp->if_capenable &= ~IFCAP_VLAN_HWTSO;
1916204377Syongari			age_rxvlan(sc);
1917204377Syongari		}
1918179100Syongari		AGE_UNLOCK(sc);
1919179100Syongari		VLAN_CAPABILITIES(ifp);
1920179100Syongari		break;
1921179100Syongari	default:
1922179100Syongari		error = ether_ioctl(ifp, cmd, data);
1923179100Syongari		break;
1924179100Syongari	}
1925179100Syongari
1926179100Syongari	return (error);
1927179100Syongari}
1928179100Syongari
1929179100Syongaristatic void
1930179100Syongariage_mac_config(struct age_softc *sc)
1931179100Syongari{
1932179100Syongari	struct mii_data *mii;
1933179100Syongari	uint32_t reg;
1934179100Syongari
1935179100Syongari	AGE_LOCK_ASSERT(sc);
1936179100Syongari
1937179100Syongari	mii = device_get_softc(sc->age_miibus);
1938179100Syongari	reg = CSR_READ_4(sc, AGE_MAC_CFG);
1939179100Syongari	reg &= ~MAC_CFG_FULL_DUPLEX;
1940179100Syongari	reg &= ~(MAC_CFG_TX_FC | MAC_CFG_RX_FC);
1941179100Syongari	reg &= ~MAC_CFG_SPEED_MASK;
1942179100Syongari	/* Reprogram MAC with resolved speed/duplex. */
1943179100Syongari	switch (IFM_SUBTYPE(mii->mii_media_active)) {
1944179100Syongari	case IFM_10_T:
1945179100Syongari	case IFM_100_TX:
1946179100Syongari		reg |= MAC_CFG_SPEED_10_100;
1947179100Syongari		break;
1948179100Syongari	case IFM_1000_T:
1949179100Syongari		reg |= MAC_CFG_SPEED_1000;
1950179100Syongari		break;
1951179100Syongari	}
1952179100Syongari	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0) {
1953179100Syongari		reg |= MAC_CFG_FULL_DUPLEX;
1954179100Syongari#ifdef notyet
1955179100Syongari		if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
1956179100Syongari			reg |= MAC_CFG_TX_FC;
1957179100Syongari		if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
1958179100Syongari			reg |= MAC_CFG_RX_FC;
1959179100Syongari#endif
1960179100Syongari	}
1961179100Syongari
1962179100Syongari	CSR_WRITE_4(sc, AGE_MAC_CFG, reg);
1963179100Syongari}
1964179100Syongari
1965179100Syongaristatic void
1966179100Syongariage_link_task(void *arg, int pending)
1967179100Syongari{
1968179100Syongari	struct age_softc *sc;
1969179100Syongari	struct mii_data *mii;
1970179100Syongari	struct ifnet *ifp;
1971179100Syongari	uint32_t reg;
1972179100Syongari
1973179100Syongari	sc = (struct age_softc *)arg;
1974179100Syongari
1975179100Syongari	AGE_LOCK(sc);
1976179100Syongari	mii = device_get_softc(sc->age_miibus);
1977179100Syongari	ifp = sc->age_ifp;
1978179100Syongari	if (mii == NULL || ifp == NULL ||
1979179100Syongari	    (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
1980179100Syongari		AGE_UNLOCK(sc);
1981179100Syongari		return;
1982179100Syongari	}
1983179100Syongari
1984179100Syongari	sc->age_flags &= ~AGE_FLAG_LINK;
1985179100Syongari	if ((mii->mii_media_status & IFM_AVALID) != 0) {
1986179100Syongari		switch (IFM_SUBTYPE(mii->mii_media_active)) {
1987179100Syongari		case IFM_10_T:
1988179100Syongari		case IFM_100_TX:
1989179100Syongari		case IFM_1000_T:
1990179100Syongari			sc->age_flags |= AGE_FLAG_LINK;
1991179100Syongari			break;
1992179100Syongari		default:
1993179100Syongari			break;
1994179100Syongari		}
1995179100Syongari	}
1996179100Syongari
1997179100Syongari	/* Stop Rx/Tx MACs. */
1998179100Syongari	age_stop_rxmac(sc);
1999179100Syongari	age_stop_txmac(sc);
2000179100Syongari
2001179100Syongari	/* Program MACs with resolved speed/duplex/flow-control. */
2002179100Syongari	if ((sc->age_flags & AGE_FLAG_LINK) != 0) {
2003179100Syongari		age_mac_config(sc);
2004179100Syongari		reg = CSR_READ_4(sc, AGE_MAC_CFG);
2005179100Syongari		/* Restart DMA engine and Tx/Rx MAC. */
2006179100Syongari		CSR_WRITE_4(sc, AGE_DMA_CFG, CSR_READ_4(sc, AGE_DMA_CFG) |
2007179100Syongari		    DMA_CFG_RD_ENB | DMA_CFG_WR_ENB);
2008179100Syongari		reg |= MAC_CFG_TX_ENB | MAC_CFG_RX_ENB;
2009179100Syongari		CSR_WRITE_4(sc, AGE_MAC_CFG, reg);
2010179100Syongari	}
2011179100Syongari
2012179100Syongari	AGE_UNLOCK(sc);
2013179100Syongari}
2014179100Syongari
2015179100Syongaristatic void
2016179100Syongariage_stats_update(struct age_softc *sc)
2017179100Syongari{
2018179100Syongari	struct age_stats *stat;
2019179100Syongari	struct smb *smb;
2020179100Syongari	struct ifnet *ifp;
2021179100Syongari
2022179100Syongari	AGE_LOCK_ASSERT(sc);
2023179100Syongari
2024179100Syongari	stat = &sc->age_stat;
2025179100Syongari
2026179100Syongari	bus_dmamap_sync(sc->age_cdata.age_smb_block_tag,
2027179100Syongari	    sc->age_cdata.age_smb_block_map,
2028179100Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
2029179100Syongari
2030179100Syongari	smb = sc->age_rdata.age_smb_block;
2031179100Syongari	if (smb->updated == 0)
2032179100Syongari		return;
2033179100Syongari
2034179100Syongari	ifp = sc->age_ifp;
2035179100Syongari	/* Rx stats. */
2036179100Syongari	stat->rx_frames += smb->rx_frames;
2037179100Syongari	stat->rx_bcast_frames += smb->rx_bcast_frames;
2038179100Syongari	stat->rx_mcast_frames += smb->rx_mcast_frames;
2039179100Syongari	stat->rx_pause_frames += smb->rx_pause_frames;
2040179100Syongari	stat->rx_control_frames += smb->rx_control_frames;
2041179100Syongari	stat->rx_crcerrs += smb->rx_crcerrs;
2042179100Syongari	stat->rx_lenerrs += smb->rx_lenerrs;
2043179100Syongari	stat->rx_bytes += smb->rx_bytes;
2044179100Syongari	stat->rx_runts += smb->rx_runts;
2045179100Syongari	stat->rx_fragments += smb->rx_fragments;
2046179100Syongari	stat->rx_pkts_64 += smb->rx_pkts_64;
2047179100Syongari	stat->rx_pkts_65_127 += smb->rx_pkts_65_127;
2048179100Syongari	stat->rx_pkts_128_255 += smb->rx_pkts_128_255;
2049179100Syongari	stat->rx_pkts_256_511 += smb->rx_pkts_256_511;
2050179100Syongari	stat->rx_pkts_512_1023 += smb->rx_pkts_512_1023;
2051179100Syongari	stat->rx_pkts_1024_1518 += smb->rx_pkts_1024_1518;
2052179100Syongari	stat->rx_pkts_1519_max += smb->rx_pkts_1519_max;
2053179100Syongari	stat->rx_pkts_truncated += smb->rx_pkts_truncated;
2054179100Syongari	stat->rx_fifo_oflows += smb->rx_fifo_oflows;
2055179100Syongari	stat->rx_desc_oflows += smb->rx_desc_oflows;
2056179100Syongari	stat->rx_alignerrs += smb->rx_alignerrs;
2057179100Syongari	stat->rx_bcast_bytes += smb->rx_bcast_bytes;
2058179100Syongari	stat->rx_mcast_bytes += smb->rx_mcast_bytes;
2059179100Syongari	stat->rx_pkts_filtered += smb->rx_pkts_filtered;
2060179100Syongari
2061179100Syongari	/* Tx stats. */
2062179100Syongari	stat->tx_frames += smb->tx_frames;
2063179100Syongari	stat->tx_bcast_frames += smb->tx_bcast_frames;
2064179100Syongari	stat->tx_mcast_frames += smb->tx_mcast_frames;
2065179100Syongari	stat->tx_pause_frames += smb->tx_pause_frames;
2066179100Syongari	stat->tx_excess_defer += smb->tx_excess_defer;
2067179100Syongari	stat->tx_control_frames += smb->tx_control_frames;
2068179100Syongari	stat->tx_deferred += smb->tx_deferred;
2069179100Syongari	stat->tx_bytes += smb->tx_bytes;
2070179100Syongari	stat->tx_pkts_64 += smb->tx_pkts_64;
2071179100Syongari	stat->tx_pkts_65_127 += smb->tx_pkts_65_127;
2072179100Syongari	stat->tx_pkts_128_255 += smb->tx_pkts_128_255;
2073179100Syongari	stat->tx_pkts_256_511 += smb->tx_pkts_256_511;
2074179100Syongari	stat->tx_pkts_512_1023 += smb->tx_pkts_512_1023;
2075179100Syongari	stat->tx_pkts_1024_1518 += smb->tx_pkts_1024_1518;
2076179100Syongari	stat->tx_pkts_1519_max += smb->tx_pkts_1519_max;
2077179100Syongari	stat->tx_single_colls += smb->tx_single_colls;
2078179100Syongari	stat->tx_multi_colls += smb->tx_multi_colls;
2079179100Syongari	stat->tx_late_colls += smb->tx_late_colls;
2080179100Syongari	stat->tx_excess_colls += smb->tx_excess_colls;
2081179100Syongari	stat->tx_underrun += smb->tx_underrun;
2082179100Syongari	stat->tx_desc_underrun += smb->tx_desc_underrun;
2083179100Syongari	stat->tx_lenerrs += smb->tx_lenerrs;
2084179100Syongari	stat->tx_pkts_truncated += smb->tx_pkts_truncated;
2085179100Syongari	stat->tx_bcast_bytes += smb->tx_bcast_bytes;
2086179100Syongari	stat->tx_mcast_bytes += smb->tx_mcast_bytes;
2087179100Syongari
2088179100Syongari	/* Update counters in ifnet. */
2089271828Sglebius	if_inc_counter(ifp, IFCOUNTER_OPACKETS, smb->tx_frames);
2090179100Syongari
2091271828Sglebius	if_inc_counter(ifp, IFCOUNTER_COLLISIONS, smb->tx_single_colls +
2092179100Syongari	    smb->tx_multi_colls + smb->tx_late_colls +
2093271828Sglebius	    smb->tx_excess_colls * HDPX_CFG_RETRY_DEFAULT);
2094179100Syongari
2095271828Sglebius	if_inc_counter(ifp, IFCOUNTER_OERRORS, smb->tx_excess_colls +
2096179100Syongari	    smb->tx_late_colls + smb->tx_underrun +
2097271828Sglebius	    smb->tx_pkts_truncated);
2098179100Syongari
2099271828Sglebius	if_inc_counter(ifp, IFCOUNTER_IPACKETS, smb->rx_frames);
2100179100Syongari
2101271828Sglebius	if_inc_counter(ifp, IFCOUNTER_IERRORS, smb->rx_crcerrs +
2102271828Sglebius	    smb->rx_lenerrs + smb->rx_runts + smb->rx_pkts_truncated +
2103179100Syongari	    smb->rx_fifo_oflows + smb->rx_desc_oflows +
2104271828Sglebius	    smb->rx_alignerrs);
2105179100Syongari
2106179100Syongari	/* Update done, clear. */
2107179100Syongari	smb->updated = 0;
2108179100Syongari
2109179100Syongari	bus_dmamap_sync(sc->age_cdata.age_smb_block_tag,
2110179100Syongari	    sc->age_cdata.age_smb_block_map,
2111179100Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
2112179100Syongari}
2113179100Syongari
2114179100Syongaristatic int
2115179100Syongariage_intr(void *arg)
2116179100Syongari{
2117179100Syongari	struct age_softc *sc;
2118179100Syongari	uint32_t status;
2119179100Syongari
2120179100Syongari	sc = (struct age_softc *)arg;
2121179100Syongari
2122179100Syongari	status = CSR_READ_4(sc, AGE_INTR_STATUS);
2123179100Syongari	if (status == 0 || (status & AGE_INTRS) == 0)
2124179100Syongari		return (FILTER_STRAY);
2125179100Syongari	/* Disable interrupts. */
2126179100Syongari	CSR_WRITE_4(sc, AGE_INTR_STATUS, status | INTR_DIS_INT);
2127179100Syongari	taskqueue_enqueue(sc->age_tq, &sc->age_int_task);
2128179100Syongari
2129179100Syongari	return (FILTER_HANDLED);
2130179100Syongari}
2131179100Syongari
2132179100Syongaristatic void
2133179100Syongariage_int_task(void *arg, int pending)
2134179100Syongari{
2135179100Syongari	struct age_softc *sc;
2136179100Syongari	struct ifnet *ifp;
2137179100Syongari	struct cmb *cmb;
2138179100Syongari	uint32_t status;
2139179100Syongari
2140179100Syongari	sc = (struct age_softc *)arg;
2141179100Syongari
2142179100Syongari	AGE_LOCK(sc);
2143179100Syongari
2144179100Syongari	bus_dmamap_sync(sc->age_cdata.age_cmb_block_tag,
2145179100Syongari	    sc->age_cdata.age_cmb_block_map,
2146179100Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
2147179100Syongari	cmb = sc->age_rdata.age_cmb_block;
2148179100Syongari	status = le32toh(cmb->intr_status);
2149179100Syongari	if (sc->age_morework != 0)
2150179100Syongari		status |= INTR_CMB_RX;
2151179100Syongari	if ((status & AGE_INTRS) == 0)
2152179100Syongari		goto done;
2153179100Syongari
2154179100Syongari	sc->age_tpd_cons = (le32toh(cmb->tpd_cons) & TPD_CONS_MASK) >>
2155179100Syongari	    TPD_CONS_SHIFT;
2156179100Syongari	sc->age_rr_prod = (le32toh(cmb->rprod_cons) & RRD_PROD_MASK) >>
2157179100Syongari	    RRD_PROD_SHIFT;
2158179100Syongari	/* Let hardware know CMB was served. */
2159179100Syongari	cmb->intr_status = 0;
2160179100Syongari	bus_dmamap_sync(sc->age_cdata.age_cmb_block_tag,
2161179100Syongari	    sc->age_cdata.age_cmb_block_map,
2162179100Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
2163179100Syongari
2164179100Syongari#if 0
2165179100Syongari	printf("INTR: 0x%08x\n", status);
2166179100Syongari	status &= ~INTR_DIS_DMA;
2167179100Syongari	CSR_WRITE_4(sc, AGE_INTR_STATUS, status | INTR_DIS_INT);
2168179100Syongari#endif
2169179100Syongari	ifp = sc->age_ifp;
2170179100Syongari	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
2171179100Syongari		if ((status & INTR_CMB_RX) != 0)
2172179100Syongari			sc->age_morework = age_rxintr(sc, sc->age_rr_prod,
2173179100Syongari			    sc->age_process_limit);
2174179100Syongari		if ((status & INTR_CMB_TX) != 0)
2175179100Syongari			age_txintr(sc, sc->age_tpd_cons);
2176179100Syongari		if ((status & (INTR_DMA_RD_TO_RST | INTR_DMA_WR_TO_RST)) != 0) {
2177179100Syongari			if ((status & INTR_DMA_RD_TO_RST) != 0)
2178179100Syongari				device_printf(sc->age_dev,
2179179100Syongari				    "DMA read error! -- resetting\n");
2180179100Syongari			if ((status & INTR_DMA_WR_TO_RST) != 0)
2181179100Syongari				device_printf(sc->age_dev,
2182179100Syongari				    "DMA write error! -- resetting\n");
2183211768Syongari			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
2184179100Syongari			age_init_locked(sc);
2185179100Syongari		}
2186179100Syongari		if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
2187216925Sjhb			age_start_locked(ifp);
2188179100Syongari		if ((status & INTR_SMB) != 0)
2189179100Syongari			age_stats_update(sc);
2190179100Syongari	}
2191179100Syongari
2192179100Syongari	/* Check whether CMB was updated while serving Tx/Rx/SMB handler. */
2193179100Syongari	bus_dmamap_sync(sc->age_cdata.age_cmb_block_tag,
2194179100Syongari	    sc->age_cdata.age_cmb_block_map,
2195179100Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
2196179100Syongari	status = le32toh(cmb->intr_status);
2197179100Syongari	if (sc->age_morework != 0 || (status & AGE_INTRS) != 0) {
2198179100Syongari		taskqueue_enqueue(sc->age_tq, &sc->age_int_task);
2199179100Syongari		AGE_UNLOCK(sc);
2200179100Syongari		return;
2201179100Syongari	}
2202179100Syongari
2203179100Syongaridone:
2204179100Syongari	/* Re-enable interrupts. */
2205179100Syongari	CSR_WRITE_4(sc, AGE_INTR_STATUS, 0);
2206179100Syongari	AGE_UNLOCK(sc);
2207179100Syongari}
2208179100Syongari
2209179100Syongaristatic void
2210179100Syongariage_txintr(struct age_softc *sc, int tpd_cons)
2211179100Syongari{
2212179100Syongari	struct ifnet *ifp;
2213179100Syongari	struct age_txdesc *txd;
2214179100Syongari	int cons, prog;
2215179100Syongari
2216179100Syongari	AGE_LOCK_ASSERT(sc);
2217179100Syongari
2218179100Syongari	ifp = sc->age_ifp;
2219179100Syongari
2220179100Syongari	bus_dmamap_sync(sc->age_cdata.age_tx_ring_tag,
2221179100Syongari	    sc->age_cdata.age_tx_ring_map,
2222179100Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
2223179100Syongari
2224179100Syongari	/*
2225179100Syongari	 * Go through our Tx list and free mbufs for those
2226179100Syongari	 * frames which have been transmitted.
2227179100Syongari	 */
2228179100Syongari	cons = sc->age_cdata.age_tx_cons;
2229179100Syongari	for (prog = 0; cons != tpd_cons; AGE_DESC_INC(cons, AGE_TX_RING_CNT)) {
2230179100Syongari		if (sc->age_cdata.age_tx_cnt <= 0)
2231179100Syongari			break;
2232179100Syongari		prog++;
2233179100Syongari		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2234179100Syongari		sc->age_cdata.age_tx_cnt--;
2235179100Syongari		txd = &sc->age_cdata.age_txdesc[cons];
2236179100Syongari		/*
2237179100Syongari		 * Clear Tx descriptors, it's not required but would
2238179100Syongari		 * help debugging in case of Tx issues.
2239179100Syongari		 */
2240179100Syongari		txd->tx_desc->addr = 0;
2241179100Syongari		txd->tx_desc->len = 0;
2242179100Syongari		txd->tx_desc->flags = 0;
2243179100Syongari
2244179100Syongari		if (txd->tx_m == NULL)
2245179100Syongari			continue;
2246179100Syongari		/* Reclaim transmitted mbufs. */
2247179100Syongari		bus_dmamap_sync(sc->age_cdata.age_tx_tag, txd->tx_dmamap,
2248179100Syongari		    BUS_DMASYNC_POSTWRITE);
2249179100Syongari		bus_dmamap_unload(sc->age_cdata.age_tx_tag, txd->tx_dmamap);
2250179100Syongari		m_freem(txd->tx_m);
2251179100Syongari		txd->tx_m = NULL;
2252179100Syongari	}
2253179100Syongari
2254179100Syongari	if (prog > 0) {
2255179100Syongari		sc->age_cdata.age_tx_cons = cons;
2256179100Syongari
2257179100Syongari		/*
2258179100Syongari		 * Unarm watchdog timer only when there are no pending
2259179100Syongari		 * Tx descriptors in queue.
2260179100Syongari		 */
2261179100Syongari		if (sc->age_cdata.age_tx_cnt == 0)
2262179100Syongari			sc->age_watchdog_timer = 0;
2263179100Syongari		bus_dmamap_sync(sc->age_cdata.age_tx_ring_tag,
2264179100Syongari		    sc->age_cdata.age_tx_ring_map,
2265179100Syongari		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
2266179100Syongari	}
2267179100Syongari}
2268179100Syongari
2269246341Syongari#ifndef __NO_STRICT_ALIGNMENT
2270246341Syongaristatic struct mbuf *
2271246341Syongariage_fixup_rx(struct ifnet *ifp, struct mbuf *m)
2272246341Syongari{
2273246341Syongari	struct mbuf *n;
2274246341Syongari        int i;
2275246341Syongari        uint16_t *src, *dst;
2276246341Syongari
2277246341Syongari	src = mtod(m, uint16_t *);
2278246341Syongari	dst = src - 3;
2279246341Syongari
2280246341Syongari	if (m->m_next == NULL) {
2281246341Syongari		for (i = 0; i < (m->m_len / sizeof(uint16_t) + 1); i++)
2282246341Syongari			*dst++ = *src++;
2283246341Syongari		m->m_data -= 6;
2284246341Syongari		return (m);
2285246341Syongari	}
2286246341Syongari	/*
2287246341Syongari	 * Append a new mbuf to received mbuf chain and copy ethernet
2288246341Syongari	 * header from the mbuf chain. This can save lots of CPU
2289246341Syongari	 * cycles for jumbo frame.
2290246341Syongari	 */
2291246341Syongari	MGETHDR(n, M_NOWAIT, MT_DATA);
2292246341Syongari	if (n == NULL) {
2293271828Sglebius		if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
2294246341Syongari		m_freem(m);
2295246341Syongari		return (NULL);
2296246341Syongari	}
2297246341Syongari	bcopy(m->m_data, n->m_data, ETHER_HDR_LEN);
2298246341Syongari	m->m_data += ETHER_HDR_LEN;
2299246341Syongari	m->m_len -= ETHER_HDR_LEN;
2300246341Syongari	n->m_len = ETHER_HDR_LEN;
2301246341Syongari	M_MOVE_PKTHDR(n, m);
2302246341Syongari	n->m_next = m;
2303246341Syongari	return (n);
2304246341Syongari}
2305246341Syongari#endif
2306246341Syongari
2307179100Syongari/* Receive a frame. */
2308179100Syongaristatic void
2309179100Syongariage_rxeof(struct age_softc *sc, struct rx_rdesc *rxrd)
2310179100Syongari{
2311179100Syongari	struct age_rxdesc *rxd;
2312179100Syongari	struct ifnet *ifp;
2313179100Syongari	struct mbuf *mp, *m;
2314179100Syongari	uint32_t status, index, vtag;
2315246341Syongari	int count, nsegs;
2316179100Syongari	int rx_cons;
2317179100Syongari
2318179100Syongari	AGE_LOCK_ASSERT(sc);
2319179100Syongari
2320179100Syongari	ifp = sc->age_ifp;
2321179100Syongari	status = le32toh(rxrd->flags);
2322179100Syongari	index = le32toh(rxrd->index);
2323179100Syongari	rx_cons = AGE_RX_CONS(index);
2324179100Syongari	nsegs = AGE_RX_NSEGS(index);
2325179100Syongari
2326179100Syongari	sc->age_cdata.age_rxlen = AGE_RX_BYTES(le32toh(rxrd->len));
2327246341Syongari	if ((status & (AGE_RRD_ERROR | AGE_RRD_LENGTH_NOK)) != 0) {
2328179100Syongari		/*
2329179100Syongari		 * We want to pass the following frames to upper
2330179100Syongari		 * layer regardless of error status of Rx return
2331179100Syongari		 * ring.
2332179100Syongari		 *
2333179100Syongari		 *  o IP/TCP/UDP checksum is bad.
2334179100Syongari		 *  o frame length and protocol specific length
2335179100Syongari		 *     does not match.
2336179100Syongari		 */
2337246341Syongari		status |= AGE_RRD_IPCSUM_NOK | AGE_RRD_TCP_UDPCSUM_NOK;
2338246341Syongari		if ((status & (AGE_RRD_CRC | AGE_RRD_CODE | AGE_RRD_DRIBBLE |
2339246341Syongari		    AGE_RRD_RUNT | AGE_RRD_OFLOW | AGE_RRD_TRUNC)) != 0)
2340246341Syongari			return;
2341179100Syongari	}
2342179100Syongari
2343179100Syongari	for (count = 0; count < nsegs; count++,
2344179100Syongari	    AGE_DESC_INC(rx_cons, AGE_RX_RING_CNT)) {
2345179100Syongari		rxd = &sc->age_cdata.age_rxdesc[rx_cons];
2346179100Syongari		mp = rxd->rx_m;
2347179100Syongari		/* Add a new receive buffer to the ring. */
2348179100Syongari		if (age_newbuf(sc, rxd) != 0) {
2349271828Sglebius			if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1);
2350179100Syongari			/* Reuse Rx buffers. */
2351246341Syongari			if (sc->age_cdata.age_rxhead != NULL)
2352179100Syongari				m_freem(sc->age_cdata.age_rxhead);
2353179100Syongari			break;
2354179100Syongari		}
2355179100Syongari
2356246341Syongari		/*
2357246341Syongari		 * Assume we've received a full sized frame.
2358246341Syongari		 * Actual size is fixed when we encounter the end of
2359246341Syongari		 * multi-segmented frame.
2360246341Syongari		 */
2361246341Syongari		mp->m_len = AGE_RX_BUF_SIZE;
2362179100Syongari
2363179100Syongari		/* Chain received mbufs. */
2364179100Syongari		if (sc->age_cdata.age_rxhead == NULL) {
2365179100Syongari			sc->age_cdata.age_rxhead = mp;
2366179100Syongari			sc->age_cdata.age_rxtail = mp;
2367179100Syongari		} else {
2368179100Syongari			mp->m_flags &= ~M_PKTHDR;
2369179100Syongari			sc->age_cdata.age_rxprev_tail =
2370179100Syongari			    sc->age_cdata.age_rxtail;
2371179100Syongari			sc->age_cdata.age_rxtail->m_next = mp;
2372179100Syongari			sc->age_cdata.age_rxtail = mp;
2373179100Syongari		}
2374179100Syongari
2375179100Syongari		if (count == nsegs - 1) {
2376246341Syongari			/* Last desc. for this frame. */
2377246341Syongari			m = sc->age_cdata.age_rxhead;
2378246341Syongari			m->m_flags |= M_PKTHDR;
2379179100Syongari			/*
2380179100Syongari			 * It seems that L1 controller has no way
2381179100Syongari			 * to tell hardware to strip CRC bytes.
2382179100Syongari			 */
2383246341Syongari			m->m_pkthdr.len = sc->age_cdata.age_rxlen -
2384246341Syongari			    ETHER_CRC_LEN;
2385179100Syongari			if (nsegs > 1) {
2386246341Syongari				/* Set last mbuf size. */
2387246341Syongari				mp->m_len = sc->age_cdata.age_rxlen -
2388246341Syongari				    ((nsegs - 1) * AGE_RX_BUF_SIZE);
2389179100Syongari				/* Remove the CRC bytes in chained mbufs. */
2390179100Syongari				if (mp->m_len <= ETHER_CRC_LEN) {
2391179100Syongari					sc->age_cdata.age_rxtail =
2392179100Syongari					    sc->age_cdata.age_rxprev_tail;
2393179100Syongari					sc->age_cdata.age_rxtail->m_len -=
2394179100Syongari					    (ETHER_CRC_LEN - mp->m_len);
2395179100Syongari					sc->age_cdata.age_rxtail->m_next = NULL;
2396179100Syongari					m_freem(mp);
2397179100Syongari				} else {
2398179100Syongari					mp->m_len -= ETHER_CRC_LEN;
2399179100Syongari				}
2400246341Syongari			} else
2401246341Syongari				m->m_len = m->m_pkthdr.len;
2402179100Syongari			m->m_pkthdr.rcvif = ifp;
2403179100Syongari			/*
2404179100Syongari			 * Set checksum information.
2405179100Syongari			 * It seems that L1 controller can compute partial
2406179100Syongari			 * checksum. The partial checksum value can be used
2407179100Syongari			 * to accelerate checksum computation for fragmented
2408179100Syongari			 * TCP/UDP packets. Upper network stack already
2409179100Syongari			 * takes advantage of the partial checksum value in
2410179100Syongari			 * IP reassembly stage. But I'm not sure the
2411179100Syongari			 * correctness of the partial hardware checksum
2412179100Syongari			 * assistance due to lack of data sheet. If it is
2413179100Syongari			 * proven to work on L1 I'll enable it.
2414179100Syongari			 */
2415179100Syongari			if ((ifp->if_capenable & IFCAP_RXCSUM) != 0 &&
2416179100Syongari			    (status & AGE_RRD_IPV4) != 0) {
2417179100Syongari				if ((status & AGE_RRD_IPCSUM_NOK) == 0)
2418246341Syongari					m->m_pkthdr.csum_flags |=
2419246341Syongari					    CSUM_IP_CHECKED | CSUM_IP_VALID;
2420179100Syongari				if ((status & (AGE_RRD_TCP | AGE_RRD_UDP)) &&
2421179100Syongari				    (status & AGE_RRD_TCP_UDPCSUM_NOK) == 0) {
2422179100Syongari					m->m_pkthdr.csum_flags |=
2423179100Syongari					    CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
2424179100Syongari					m->m_pkthdr.csum_data = 0xffff;
2425179100Syongari				}
2426179100Syongari				/*
2427179100Syongari				 * Don't mark bad checksum for TCP/UDP frames
2428179100Syongari				 * as fragmented frames may always have set
2429179100Syongari				 * bad checksummed bit of descriptor status.
2430179100Syongari				 */
2431179100Syongari			}
2432179100Syongari
2433179100Syongari			/* Check for VLAN tagged frames. */
2434179100Syongari			if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0 &&
2435179100Syongari			    (status & AGE_RRD_VLAN) != 0) {
2436179100Syongari				vtag = AGE_RX_VLAN(le32toh(rxrd->vtags));
2437179100Syongari				m->m_pkthdr.ether_vtag = AGE_RX_VLAN_TAG(vtag);
2438179100Syongari				m->m_flags |= M_VLANTAG;
2439179100Syongari			}
2440246341Syongari#ifndef __NO_STRICT_ALIGNMENT
2441246341Syongari			m = age_fixup_rx(ifp, m);
2442246341Syongari			if (m != NULL)
2443246341Syongari#endif
2444246341Syongari			{
2445179100Syongari			/* Pass it on. */
2446179100Syongari			AGE_UNLOCK(sc);
2447179100Syongari			(*ifp->if_input)(ifp, m);
2448179100Syongari			AGE_LOCK(sc);
2449246341Syongari			}
2450179100Syongari		}
2451179100Syongari	}
2452179100Syongari
2453246341Syongari	/* Reset mbuf chains. */
2454246341Syongari	AGE_RXCHAIN_RESET(sc);
2455179100Syongari}
2456179100Syongari
2457179100Syongaristatic int
2458179100Syongariage_rxintr(struct age_softc *sc, int rr_prod, int count)
2459179100Syongari{
2460179100Syongari	struct rx_rdesc *rxrd;
2461179100Syongari	int rr_cons, nsegs, pktlen, prog;
2462179100Syongari
2463179100Syongari	AGE_LOCK_ASSERT(sc);
2464179100Syongari
2465179100Syongari	rr_cons = sc->age_cdata.age_rr_cons;
2466179100Syongari	if (rr_cons == rr_prod)
2467179100Syongari		return (0);
2468179100Syongari
2469179100Syongari	bus_dmamap_sync(sc->age_cdata.age_rr_ring_tag,
2470179100Syongari	    sc->age_cdata.age_rr_ring_map,
2471179100Syongari	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
2472220252Syongari	bus_dmamap_sync(sc->age_cdata.age_rx_ring_tag,
2473220252Syongari	    sc->age_cdata.age_rx_ring_map, BUS_DMASYNC_POSTWRITE);
2474179100Syongari
2475179100Syongari	for (prog = 0; rr_cons != rr_prod; prog++) {
2476251872Smarkj		if (count-- <= 0)
2477179100Syongari			break;
2478179100Syongari		rxrd = &sc->age_rdata.age_rr_ring[rr_cons];
2479179100Syongari		nsegs = AGE_RX_NSEGS(le32toh(rxrd->index));
2480179100Syongari		if (nsegs == 0)
2481179100Syongari			break;
2482179100Syongari		/*
2483179100Syongari		 * Check number of segments against received bytes.
2484179100Syongari		 * Non-matching value would indicate that hardware
2485179100Syongari		 * is still trying to update Rx return descriptors.
2486179100Syongari		 * I'm not sure whether this check is really needed.
2487179100Syongari		 */
2488179100Syongari		pktlen = AGE_RX_BYTES(le32toh(rxrd->len));
2489298646Spfg		if (nsegs != howmany(pktlen, AGE_RX_BUF_SIZE))
2490179100Syongari			break;
2491179100Syongari
2492179100Syongari		/* Received a frame. */
2493179100Syongari		age_rxeof(sc, rxrd);
2494179100Syongari		/* Clear return ring. */
2495179100Syongari		rxrd->index = 0;
2496179100Syongari		AGE_DESC_INC(rr_cons, AGE_RR_RING_CNT);
2497246341Syongari		sc->age_cdata.age_rx_cons += nsegs;
2498246341Syongari		sc->age_cdata.age_rx_cons %= AGE_RX_RING_CNT;
2499179100Syongari	}
2500179100Syongari
2501179100Syongari	if (prog > 0) {
2502179100Syongari		/* Update the consumer index. */
2503179100Syongari		sc->age_cdata.age_rr_cons = rr_cons;
2504179100Syongari
2505220252Syongari		bus_dmamap_sync(sc->age_cdata.age_rx_ring_tag,
2506220252Syongari		    sc->age_cdata.age_rx_ring_map, BUS_DMASYNC_PREWRITE);
2507179100Syongari		/* Sync descriptors. */
2508179100Syongari		bus_dmamap_sync(sc->age_cdata.age_rr_ring_tag,
2509179100Syongari		    sc->age_cdata.age_rr_ring_map,
2510179100Syongari		    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
2511179100Syongari
2512179100Syongari		/* Notify hardware availability of new Rx buffers. */
2513179100Syongari		AGE_COMMIT_MBOX(sc);
2514179100Syongari	}
2515179100Syongari
2516179100Syongari	return (count > 0 ? 0 : EAGAIN);
2517179100Syongari}
2518179100Syongari
2519179100Syongaristatic void
2520179100Syongariage_tick(void *arg)
2521179100Syongari{
2522179100Syongari	struct age_softc *sc;
2523179100Syongari	struct mii_data *mii;
2524179100Syongari
2525179100Syongari	sc = (struct age_softc *)arg;
2526179100Syongari
2527179100Syongari	AGE_LOCK_ASSERT(sc);
2528179100Syongari
2529179100Syongari	mii = device_get_softc(sc->age_miibus);
2530179100Syongari	mii_tick(mii);
2531179100Syongari	age_watchdog(sc);
2532179100Syongari	callout_reset(&sc->age_tick_ch, hz, age_tick, sc);
2533179100Syongari}
2534179100Syongari
2535179100Syongaristatic void
2536179100Syongariage_reset(struct age_softc *sc)
2537179100Syongari{
2538179100Syongari	uint32_t reg;
2539179100Syongari	int i;
2540179100Syongari
2541179100Syongari	CSR_WRITE_4(sc, AGE_MASTER_CFG, MASTER_RESET);
2542190499Syongari	CSR_READ_4(sc, AGE_MASTER_CFG);
2543190499Syongari	DELAY(1000);
2544179100Syongari	for (i = AGE_RESET_TIMEOUT; i > 0; i--) {
2545179100Syongari		if ((reg = CSR_READ_4(sc, AGE_IDLE_STATUS)) == 0)
2546179100Syongari			break;
2547179100Syongari		DELAY(10);
2548179100Syongari	}
2549179100Syongari
2550179100Syongari	if (i == 0)
2551179100Syongari		device_printf(sc->age_dev, "reset timeout(0x%08x)!\n", reg);
2552179100Syongari	/* Initialize PCIe module. From Linux. */
2553179100Syongari	CSR_WRITE_4(sc, 0x12FC, 0x6500);
2554179100Syongari	CSR_WRITE_4(sc, 0x1008, CSR_READ_4(sc, 0x1008) | 0x8000);
2555179100Syongari}
2556179100Syongari
2557179100Syongaristatic void
2558179100Syongariage_init(void *xsc)
2559179100Syongari{
2560179100Syongari	struct age_softc *sc;
2561179100Syongari
2562179100Syongari	sc = (struct age_softc *)xsc;
2563179100Syongari	AGE_LOCK(sc);
2564179100Syongari	age_init_locked(sc);
2565179100Syongari	AGE_UNLOCK(sc);
2566179100Syongari}
2567179100Syongari
2568179100Syongaristatic void
2569179100Syongariage_init_locked(struct age_softc *sc)
2570179100Syongari{
2571179100Syongari	struct ifnet *ifp;
2572179100Syongari	struct mii_data *mii;
2573179100Syongari	uint8_t eaddr[ETHER_ADDR_LEN];
2574179100Syongari	bus_addr_t paddr;
2575179100Syongari	uint32_t reg, fsize;
2576179100Syongari	uint32_t rxf_hi, rxf_lo, rrd_hi, rrd_lo;
2577179100Syongari	int error;
2578179100Syongari
2579179100Syongari	AGE_LOCK_ASSERT(sc);
2580179100Syongari
2581179100Syongari	ifp = sc->age_ifp;
2582179100Syongari	mii = device_get_softc(sc->age_miibus);
2583179100Syongari
2584211768Syongari	if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0)
2585211768Syongari		return;
2586211768Syongari
2587179100Syongari	/*
2588179100Syongari	 * Cancel any pending I/O.
2589179100Syongari	 */
2590179100Syongari	age_stop(sc);
2591179100Syongari
2592179100Syongari	/*
2593179100Syongari	 * Reset the chip to a known state.
2594179100Syongari	 */
2595179100Syongari	age_reset(sc);
2596179100Syongari
2597179100Syongari	/* Initialize descriptors. */
2598179100Syongari	error = age_init_rx_ring(sc);
2599179100Syongari        if (error != 0) {
2600179100Syongari                device_printf(sc->age_dev, "no memory for Rx buffers.\n");
2601179100Syongari                age_stop(sc);
2602179100Syongari		return;
2603179100Syongari        }
2604179100Syongari	age_init_rr_ring(sc);
2605179100Syongari	age_init_tx_ring(sc);
2606179100Syongari	age_init_cmb_block(sc);
2607179100Syongari	age_init_smb_block(sc);
2608179100Syongari
2609179100Syongari	/* Reprogram the station address. */
2610179100Syongari	bcopy(IF_LLADDR(ifp), eaddr, ETHER_ADDR_LEN);
2611179100Syongari	CSR_WRITE_4(sc, AGE_PAR0,
2612179100Syongari	    eaddr[2] << 24 | eaddr[3] << 16 | eaddr[4] << 8 | eaddr[5]);
2613179100Syongari	CSR_WRITE_4(sc, AGE_PAR1, eaddr[0] << 8 | eaddr[1]);
2614179100Syongari
2615179100Syongari	/* Set descriptor base addresses. */
2616179100Syongari	paddr = sc->age_rdata.age_tx_ring_paddr;
2617179100Syongari	CSR_WRITE_4(sc, AGE_DESC_ADDR_HI, AGE_ADDR_HI(paddr));
2618179100Syongari	paddr = sc->age_rdata.age_rx_ring_paddr;
2619179100Syongari	CSR_WRITE_4(sc, AGE_DESC_RD_ADDR_LO, AGE_ADDR_LO(paddr));
2620179100Syongari	paddr = sc->age_rdata.age_rr_ring_paddr;
2621179100Syongari	CSR_WRITE_4(sc, AGE_DESC_RRD_ADDR_LO, AGE_ADDR_LO(paddr));
2622179100Syongari	paddr = sc->age_rdata.age_tx_ring_paddr;
2623179100Syongari	CSR_WRITE_4(sc, AGE_DESC_TPD_ADDR_LO, AGE_ADDR_LO(paddr));
2624179100Syongari	paddr = sc->age_rdata.age_cmb_block_paddr;
2625179100Syongari	CSR_WRITE_4(sc, AGE_DESC_CMB_ADDR_LO, AGE_ADDR_LO(paddr));
2626179100Syongari	paddr = sc->age_rdata.age_smb_block_paddr;
2627179100Syongari	CSR_WRITE_4(sc, AGE_DESC_SMB_ADDR_LO, AGE_ADDR_LO(paddr));
2628179100Syongari	/* Set Rx/Rx return descriptor counter. */
2629179100Syongari	CSR_WRITE_4(sc, AGE_DESC_RRD_RD_CNT,
2630179100Syongari	    ((AGE_RR_RING_CNT << DESC_RRD_CNT_SHIFT) &
2631179100Syongari	    DESC_RRD_CNT_MASK) |
2632179100Syongari	    ((AGE_RX_RING_CNT << DESC_RD_CNT_SHIFT) & DESC_RD_CNT_MASK));
2633179100Syongari	/* Set Tx descriptor counter. */
2634179100Syongari	CSR_WRITE_4(sc, AGE_DESC_TPD_CNT,
2635179100Syongari	    (AGE_TX_RING_CNT << DESC_TPD_CNT_SHIFT) & DESC_TPD_CNT_MASK);
2636179100Syongari
2637179100Syongari	/* Tell hardware that we're ready to load descriptors. */
2638179100Syongari	CSR_WRITE_4(sc, AGE_DMA_BLOCK, DMA_BLOCK_LOAD);
2639179100Syongari
2640179100Syongari	/*
2641179100Syongari	 * Initialize mailbox register.
2642179100Syongari	 * Updated producer/consumer index information is exchanged
2643179100Syongari	 * through this mailbox register. However Tx producer and
2644179100Syongari	 * Rx return consumer/Rx producer are all shared such that
2645179100Syongari	 * it's hard to separate code path between Tx and Rx without
2646179100Syongari	 * locking. If L1 hardware have a separate mail box register
2647179100Syongari	 * for Tx and Rx consumer/producer management we could have
2648179100Syongari	 * indepent Tx/Rx handler which in turn Rx handler could have
2649179100Syongari	 * been run without any locking.
2650179100Syongari	 */
2651179100Syongari	AGE_COMMIT_MBOX(sc);
2652179100Syongari
2653179100Syongari	/* Configure IPG/IFG parameters. */
2654179100Syongari	CSR_WRITE_4(sc, AGE_IPG_IFG_CFG,
2655179100Syongari	    ((IPG_IFG_IPG2_DEFAULT << IPG_IFG_IPG2_SHIFT) & IPG_IFG_IPG2_MASK) |
2656179100Syongari	    ((IPG_IFG_IPG1_DEFAULT << IPG_IFG_IPG1_SHIFT) & IPG_IFG_IPG1_MASK) |
2657179100Syongari	    ((IPG_IFG_MIFG_DEFAULT << IPG_IFG_MIFG_SHIFT) & IPG_IFG_MIFG_MASK) |
2658179100Syongari	    ((IPG_IFG_IPGT_DEFAULT << IPG_IFG_IPGT_SHIFT) & IPG_IFG_IPGT_MASK));
2659179100Syongari
2660179100Syongari	/* Set parameters for half-duplex media. */
2661179100Syongari	CSR_WRITE_4(sc, AGE_HDPX_CFG,
2662179100Syongari	    ((HDPX_CFG_LCOL_DEFAULT << HDPX_CFG_LCOL_SHIFT) &
2663179100Syongari	    HDPX_CFG_LCOL_MASK) |
2664179100Syongari	    ((HDPX_CFG_RETRY_DEFAULT << HDPX_CFG_RETRY_SHIFT) &
2665179100Syongari	    HDPX_CFG_RETRY_MASK) | HDPX_CFG_EXC_DEF_EN |
2666179100Syongari	    ((HDPX_CFG_ABEBT_DEFAULT << HDPX_CFG_ABEBT_SHIFT) &
2667179100Syongari	    HDPX_CFG_ABEBT_MASK) |
2668179100Syongari	    ((HDPX_CFG_JAMIPG_DEFAULT << HDPX_CFG_JAMIPG_SHIFT) &
2669179100Syongari	    HDPX_CFG_JAMIPG_MASK));
2670179100Syongari
2671179100Syongari	/* Configure interrupt moderation timer. */
2672179100Syongari	CSR_WRITE_2(sc, AGE_IM_TIMER, AGE_USECS(sc->age_int_mod));
2673179100Syongari	reg = CSR_READ_4(sc, AGE_MASTER_CFG);
2674179100Syongari	reg &= ~MASTER_MTIMER_ENB;
2675179100Syongari	if (AGE_USECS(sc->age_int_mod) == 0)
2676179100Syongari		reg &= ~MASTER_ITIMER_ENB;
2677179100Syongari	else
2678179100Syongari		reg |= MASTER_ITIMER_ENB;
2679179100Syongari	CSR_WRITE_4(sc, AGE_MASTER_CFG, reg);
2680184743Syongari	if (bootverbose)
2681179100Syongari		device_printf(sc->age_dev, "interrupt moderation is %d us.\n",
2682179100Syongari		    sc->age_int_mod);
2683179100Syongari	CSR_WRITE_2(sc, AGE_INTR_CLR_TIMER, AGE_USECS(1000));
2684179100Syongari
2685179100Syongari	/* Set Maximum frame size but don't let MTU be lass than ETHER_MTU. */
2686179100Syongari	if (ifp->if_mtu < ETHERMTU)
2687179100Syongari		sc->age_max_frame_size = ETHERMTU;
2688179100Syongari	else
2689179100Syongari		sc->age_max_frame_size = ifp->if_mtu;
2690179100Syongari	sc->age_max_frame_size += ETHER_HDR_LEN +
2691179100Syongari	    sizeof(struct ether_vlan_header) + ETHER_CRC_LEN;
2692179100Syongari	CSR_WRITE_4(sc, AGE_FRAME_SIZE, sc->age_max_frame_size);
2693179100Syongari	/* Configure jumbo frame. */
2694179100Syongari	fsize = roundup(sc->age_max_frame_size, sizeof(uint64_t));
2695179100Syongari	CSR_WRITE_4(sc, AGE_RXQ_JUMBO_CFG,
2696179100Syongari	    (((fsize / sizeof(uint64_t)) <<
2697179100Syongari	    RXQ_JUMBO_CFG_SZ_THRESH_SHIFT) & RXQ_JUMBO_CFG_SZ_THRESH_MASK) |
2698179100Syongari	    ((RXQ_JUMBO_CFG_LKAH_DEFAULT <<
2699179100Syongari	    RXQ_JUMBO_CFG_LKAH_SHIFT) & RXQ_JUMBO_CFG_LKAH_MASK) |
2700179100Syongari	    ((AGE_USECS(8) << RXQ_JUMBO_CFG_RRD_TIMER_SHIFT) &
2701179100Syongari	    RXQ_JUMBO_CFG_RRD_TIMER_MASK));
2702179100Syongari
2703179100Syongari	/* Configure flow-control parameters. From Linux. */
2704179100Syongari	if ((sc->age_flags & AGE_FLAG_PCIE) != 0) {
2705179100Syongari		/*
2706179100Syongari		 * Magic workaround for old-L1.
2707179100Syongari		 * Don't know which hw revision requires this magic.
2708179100Syongari		 */
2709179100Syongari		CSR_WRITE_4(sc, 0x12FC, 0x6500);
2710179100Syongari		/*
2711179100Syongari		 * Another magic workaround for flow-control mode
2712179100Syongari		 * change. From Linux.
2713179100Syongari		 */
2714179100Syongari		CSR_WRITE_4(sc, 0x1008, CSR_READ_4(sc, 0x1008) | 0x8000);
2715179100Syongari	}
2716179100Syongari	/*
2717179100Syongari	 * TODO
2718179100Syongari	 *  Should understand pause parameter relationships between FIFO
2719179100Syongari	 *  size and number of Rx descriptors and Rx return descriptors.
2720179100Syongari	 *
2721179100Syongari	 *  Magic parameters came from Linux.
2722179100Syongari	 */
2723179100Syongari	switch (sc->age_chip_rev) {
2724179100Syongari	case 0x8001:
2725179100Syongari	case 0x9001:
2726179100Syongari	case 0x9002:
2727179100Syongari	case 0x9003:
2728179100Syongari		rxf_hi = AGE_RX_RING_CNT / 16;
2729179100Syongari		rxf_lo = (AGE_RX_RING_CNT * 7) / 8;
2730179100Syongari		rrd_hi = (AGE_RR_RING_CNT * 7) / 8;
2731179100Syongari		rrd_lo = AGE_RR_RING_CNT / 16;
2732179100Syongari		break;
2733179100Syongari	default:
2734179100Syongari		reg = CSR_READ_4(sc, AGE_SRAM_RX_FIFO_LEN);
2735179100Syongari		rxf_lo = reg / 16;
2736179100Syongari		if (rxf_lo < 192)
2737179100Syongari			rxf_lo = 192;
2738179100Syongari		rxf_hi = (reg * 7) / 8;
2739179100Syongari		if (rxf_hi < rxf_lo)
2740179100Syongari			rxf_hi = rxf_lo + 16;
2741179100Syongari		reg = CSR_READ_4(sc, AGE_SRAM_RRD_LEN);
2742179100Syongari		rrd_lo = reg / 8;
2743179100Syongari		rrd_hi = (reg * 7) / 8;
2744179100Syongari		if (rrd_lo < 2)
2745179100Syongari			rrd_lo = 2;
2746179100Syongari		if (rrd_hi < rrd_lo)
2747179100Syongari			rrd_hi = rrd_lo + 3;
2748179100Syongari		break;
2749179100Syongari	}
2750179100Syongari	CSR_WRITE_4(sc, AGE_RXQ_FIFO_PAUSE_THRESH,
2751179100Syongari	    ((rxf_lo << RXQ_FIFO_PAUSE_THRESH_LO_SHIFT) &
2752179100Syongari	    RXQ_FIFO_PAUSE_THRESH_LO_MASK) |
2753179100Syongari	    ((rxf_hi << RXQ_FIFO_PAUSE_THRESH_HI_SHIFT) &
2754179100Syongari	    RXQ_FIFO_PAUSE_THRESH_HI_MASK));
2755179100Syongari	CSR_WRITE_4(sc, AGE_RXQ_RRD_PAUSE_THRESH,
2756179100Syongari	    ((rrd_lo << RXQ_RRD_PAUSE_THRESH_LO_SHIFT) &
2757179100Syongari	    RXQ_RRD_PAUSE_THRESH_LO_MASK) |
2758179100Syongari	    ((rrd_hi << RXQ_RRD_PAUSE_THRESH_HI_SHIFT) &
2759179100Syongari	    RXQ_RRD_PAUSE_THRESH_HI_MASK));
2760179100Syongari
2761179100Syongari	/* Configure RxQ. */
2762179100Syongari	CSR_WRITE_4(sc, AGE_RXQ_CFG,
2763179100Syongari	    ((RXQ_CFG_RD_BURST_DEFAULT << RXQ_CFG_RD_BURST_SHIFT) &
2764179100Syongari	    RXQ_CFG_RD_BURST_MASK) |
2765179100Syongari	    ((RXQ_CFG_RRD_BURST_THRESH_DEFAULT <<
2766179100Syongari	    RXQ_CFG_RRD_BURST_THRESH_SHIFT) & RXQ_CFG_RRD_BURST_THRESH_MASK) |
2767179100Syongari	    ((RXQ_CFG_RD_PREF_MIN_IPG_DEFAULT <<
2768179100Syongari	    RXQ_CFG_RD_PREF_MIN_IPG_SHIFT) & RXQ_CFG_RD_PREF_MIN_IPG_MASK) |
2769179100Syongari	    RXQ_CFG_CUT_THROUGH_ENB | RXQ_CFG_ENB);
2770179100Syongari
2771179100Syongari	/* Configure TxQ. */
2772179100Syongari	CSR_WRITE_4(sc, AGE_TXQ_CFG,
2773179100Syongari	    ((TXQ_CFG_TPD_BURST_DEFAULT << TXQ_CFG_TPD_BURST_SHIFT) &
2774179100Syongari	    TXQ_CFG_TPD_BURST_MASK) |
2775179100Syongari	    ((TXQ_CFG_TX_FIFO_BURST_DEFAULT << TXQ_CFG_TX_FIFO_BURST_SHIFT) &
2776179100Syongari	    TXQ_CFG_TX_FIFO_BURST_MASK) |
2777179100Syongari	    ((TXQ_CFG_TPD_FETCH_DEFAULT <<
2778179100Syongari	    TXQ_CFG_TPD_FETCH_THRESH_SHIFT) & TXQ_CFG_TPD_FETCH_THRESH_MASK) |
2779179100Syongari	    TXQ_CFG_ENB);
2780179100Syongari
2781179100Syongari	CSR_WRITE_4(sc, AGE_TX_JUMBO_TPD_TH_IPG,
2782179100Syongari	    (((fsize / sizeof(uint64_t) << TX_JUMBO_TPD_TH_SHIFT)) &
2783179100Syongari	    TX_JUMBO_TPD_TH_MASK) |
2784179100Syongari	    ((TX_JUMBO_TPD_IPG_DEFAULT << TX_JUMBO_TPD_IPG_SHIFT) &
2785179100Syongari	    TX_JUMBO_TPD_IPG_MASK));
2786179100Syongari	/* Configure DMA parameters. */
2787179100Syongari	CSR_WRITE_4(sc, AGE_DMA_CFG,
2788179100Syongari	    DMA_CFG_ENH_ORDER | DMA_CFG_RCB_64 |
2789179100Syongari	    sc->age_dma_rd_burst | DMA_CFG_RD_ENB |
2790179100Syongari	    sc->age_dma_wr_burst | DMA_CFG_WR_ENB);
2791179100Syongari
2792179100Syongari	/* Configure CMB DMA write threshold. */
2793179100Syongari	CSR_WRITE_4(sc, AGE_CMB_WR_THRESH,
2794179100Syongari	    ((CMB_WR_THRESH_RRD_DEFAULT << CMB_WR_THRESH_RRD_SHIFT) &
2795179100Syongari	    CMB_WR_THRESH_RRD_MASK) |
2796179100Syongari	    ((CMB_WR_THRESH_TPD_DEFAULT << CMB_WR_THRESH_TPD_SHIFT) &
2797179100Syongari	    CMB_WR_THRESH_TPD_MASK));
2798179100Syongari
2799179100Syongari	/* Set CMB/SMB timer and enable them. */
2800179100Syongari	CSR_WRITE_4(sc, AGE_CMB_WR_TIMER,
2801179100Syongari	    ((AGE_USECS(2) << CMB_WR_TIMER_TX_SHIFT) & CMB_WR_TIMER_TX_MASK) |
2802179100Syongari	    ((AGE_USECS(2) << CMB_WR_TIMER_RX_SHIFT) & CMB_WR_TIMER_RX_MASK));
2803179100Syongari	/* Request SMB updates for every seconds. */
2804179100Syongari	CSR_WRITE_4(sc, AGE_SMB_TIMER, AGE_USECS(1000 * 1000));
2805179100Syongari	CSR_WRITE_4(sc, AGE_CSMB_CTRL, CSMB_CTRL_SMB_ENB | CSMB_CTRL_CMB_ENB);
2806179100Syongari
2807179100Syongari	/*
2808179100Syongari	 * Disable all WOL bits as WOL can interfere normal Rx
2809179100Syongari	 * operation.
2810179100Syongari	 */
2811179100Syongari	CSR_WRITE_4(sc, AGE_WOL_CFG, 0);
2812179100Syongari
2813179100Syongari	/*
2814179100Syongari	 * Configure Tx/Rx MACs.
2815179100Syongari	 *  - Auto-padding for short frames.
2816179100Syongari	 *  - Enable CRC generation.
2817179100Syongari	 *  Start with full-duplex/1000Mbps media. Actual reconfiguration
2818179100Syongari	 *  of MAC is followed after link establishment.
2819179100Syongari	 */
2820179100Syongari	CSR_WRITE_4(sc, AGE_MAC_CFG,
2821179100Syongari	    MAC_CFG_TX_CRC_ENB | MAC_CFG_TX_AUTO_PAD |
2822179100Syongari	    MAC_CFG_FULL_DUPLEX | MAC_CFG_SPEED_1000 |
2823179100Syongari	    ((MAC_CFG_PREAMBLE_DEFAULT << MAC_CFG_PREAMBLE_SHIFT) &
2824179100Syongari	    MAC_CFG_PREAMBLE_MASK));
2825179100Syongari	/* Set up the receive filter. */
2826179100Syongari	age_rxfilter(sc);
2827179100Syongari	age_rxvlan(sc);
2828179100Syongari
2829179100Syongari	reg = CSR_READ_4(sc, AGE_MAC_CFG);
2830179100Syongari	if ((ifp->if_capenable & IFCAP_RXCSUM) != 0)
2831179100Syongari		reg |= MAC_CFG_RXCSUM_ENB;
2832179100Syongari
2833179100Syongari	/* Ack all pending interrupts and clear it. */
2834179100Syongari	CSR_WRITE_4(sc, AGE_INTR_STATUS, 0);
2835179100Syongari	CSR_WRITE_4(sc, AGE_INTR_MASK, AGE_INTRS);
2836179100Syongari
2837179100Syongari	/* Finally enable Tx/Rx MAC. */
2838179100Syongari	CSR_WRITE_4(sc, AGE_MAC_CFG, reg | MAC_CFG_TX_ENB | MAC_CFG_RX_ENB);
2839179100Syongari
2840179100Syongari	sc->age_flags &= ~AGE_FLAG_LINK;
2841179100Syongari	/* Switch to the current media. */
2842179100Syongari	mii_mediachg(mii);
2843179100Syongari
2844179100Syongari	callout_reset(&sc->age_tick_ch, hz, age_tick, sc);
2845179100Syongari
2846179100Syongari	ifp->if_drv_flags |= IFF_DRV_RUNNING;
2847179100Syongari	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
2848179100Syongari}
2849179100Syongari
2850179100Syongaristatic void
2851179100Syongariage_stop(struct age_softc *sc)
2852179100Syongari{
2853179100Syongari	struct ifnet *ifp;
2854179100Syongari	struct age_txdesc *txd;
2855179100Syongari	struct age_rxdesc *rxd;
2856179100Syongari	uint32_t reg;
2857179100Syongari	int i;
2858179100Syongari
2859179100Syongari	AGE_LOCK_ASSERT(sc);
2860179100Syongari	/*
2861179100Syongari	 * Mark the interface down and cancel the watchdog timer.
2862179100Syongari	 */
2863179100Syongari	ifp = sc->age_ifp;
2864179100Syongari	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
2865179100Syongari	sc->age_flags &= ~AGE_FLAG_LINK;
2866179100Syongari	callout_stop(&sc->age_tick_ch);
2867179100Syongari	sc->age_watchdog_timer = 0;
2868179100Syongari
2869179100Syongari	/*
2870179100Syongari	 * Disable interrupts.
2871179100Syongari	 */
2872179100Syongari	CSR_WRITE_4(sc, AGE_INTR_MASK, 0);
2873179100Syongari	CSR_WRITE_4(sc, AGE_INTR_STATUS, 0xFFFFFFFF);
2874179100Syongari	/* Stop CMB/SMB updates. */
2875179100Syongari	CSR_WRITE_4(sc, AGE_CSMB_CTRL, 0);
2876179100Syongari	/* Stop Rx/Tx MAC. */
2877179100Syongari	age_stop_rxmac(sc);
2878179100Syongari	age_stop_txmac(sc);
2879179100Syongari	/* Stop DMA. */
2880179100Syongari	CSR_WRITE_4(sc, AGE_DMA_CFG,
2881179100Syongari	    CSR_READ_4(sc, AGE_DMA_CFG) & ~(DMA_CFG_RD_ENB | DMA_CFG_WR_ENB));
2882179100Syongari	/* Stop TxQ/RxQ. */
2883179100Syongari	CSR_WRITE_4(sc, AGE_TXQ_CFG,
2884179100Syongari	    CSR_READ_4(sc, AGE_TXQ_CFG) & ~TXQ_CFG_ENB);
2885179100Syongari	CSR_WRITE_4(sc, AGE_RXQ_CFG,
2886179100Syongari	    CSR_READ_4(sc, AGE_RXQ_CFG) & ~RXQ_CFG_ENB);
2887179100Syongari	for (i = AGE_RESET_TIMEOUT; i > 0; i--) {
2888179100Syongari		if ((reg = CSR_READ_4(sc, AGE_IDLE_STATUS)) == 0)
2889179100Syongari			break;
2890179100Syongari		DELAY(10);
2891179100Syongari	}
2892179100Syongari	if (i == 0)
2893179100Syongari		device_printf(sc->age_dev,
2894179100Syongari		    "stopping Rx/Tx MACs timed out(0x%08x)!\n", reg);
2895179100Syongari
2896179100Syongari	 /* Reclaim Rx buffers that have been processed. */
2897179100Syongari	if (sc->age_cdata.age_rxhead != NULL)
2898179100Syongari		m_freem(sc->age_cdata.age_rxhead);
2899179100Syongari	AGE_RXCHAIN_RESET(sc);
2900179100Syongari	/*
2901179100Syongari	 * Free RX and TX mbufs still in the queues.
2902179100Syongari	 */
2903179100Syongari	for (i = 0; i < AGE_RX_RING_CNT; i++) {
2904179100Syongari		rxd = &sc->age_cdata.age_rxdesc[i];
2905179100Syongari		if (rxd->rx_m != NULL) {
2906179100Syongari			bus_dmamap_sync(sc->age_cdata.age_rx_tag,
2907179100Syongari			    rxd->rx_dmamap, BUS_DMASYNC_POSTREAD);
2908179100Syongari			bus_dmamap_unload(sc->age_cdata.age_rx_tag,
2909179100Syongari			    rxd->rx_dmamap);
2910179100Syongari			m_freem(rxd->rx_m);
2911179100Syongari			rxd->rx_m = NULL;
2912179100Syongari		}
2913179100Syongari        }
2914179100Syongari	for (i = 0; i < AGE_TX_RING_CNT; i++) {
2915179100Syongari		txd = &sc->age_cdata.age_txdesc[i];
2916179100Syongari		if (txd->tx_m != NULL) {
2917179100Syongari			bus_dmamap_sync(sc->age_cdata.age_tx_tag,
2918179100Syongari			    txd->tx_dmamap, BUS_DMASYNC_POSTWRITE);
2919179100Syongari			bus_dmamap_unload(sc->age_cdata.age_tx_tag,
2920179100Syongari			    txd->tx_dmamap);
2921179100Syongari			m_freem(txd->tx_m);
2922179100Syongari			txd->tx_m = NULL;
2923179100Syongari		}
2924179100Syongari        }
2925179100Syongari}
2926179100Syongari
2927179100Syongaristatic void
2928179100Syongariage_stop_txmac(struct age_softc *sc)
2929179100Syongari{
2930179100Syongari	uint32_t reg;
2931179100Syongari	int i;
2932179100Syongari
2933179100Syongari	AGE_LOCK_ASSERT(sc);
2934179100Syongari
2935179100Syongari	reg = CSR_READ_4(sc, AGE_MAC_CFG);
2936179100Syongari	if ((reg & MAC_CFG_TX_ENB) != 0) {
2937179100Syongari		reg &= ~MAC_CFG_TX_ENB;
2938179100Syongari		CSR_WRITE_4(sc, AGE_MAC_CFG, reg);
2939179100Syongari	}
2940179100Syongari	/* Stop Tx DMA engine. */
2941179100Syongari	reg = CSR_READ_4(sc, AGE_DMA_CFG);
2942179100Syongari	if ((reg & DMA_CFG_RD_ENB) != 0) {
2943179100Syongari		reg &= ~DMA_CFG_RD_ENB;
2944179100Syongari		CSR_WRITE_4(sc, AGE_DMA_CFG, reg);
2945179100Syongari	}
2946179100Syongari	for (i = AGE_RESET_TIMEOUT; i > 0; i--) {
2947179100Syongari		if ((CSR_READ_4(sc, AGE_IDLE_STATUS) &
2948179100Syongari		    (IDLE_STATUS_TXMAC | IDLE_STATUS_DMARD)) == 0)
2949179100Syongari			break;
2950179100Syongari		DELAY(10);
2951179100Syongari	}
2952179100Syongari	if (i == 0)
2953179100Syongari		device_printf(sc->age_dev, "stopping TxMAC timeout!\n");
2954179100Syongari}
2955179100Syongari
2956179100Syongaristatic void
2957179100Syongariage_stop_rxmac(struct age_softc *sc)
2958179100Syongari{
2959179100Syongari	uint32_t reg;
2960179100Syongari	int i;
2961179100Syongari
2962179100Syongari	AGE_LOCK_ASSERT(sc);
2963179100Syongari
2964179100Syongari	reg = CSR_READ_4(sc, AGE_MAC_CFG);
2965179100Syongari	if ((reg & MAC_CFG_RX_ENB) != 0) {
2966179100Syongari		reg &= ~MAC_CFG_RX_ENB;
2967179100Syongari		CSR_WRITE_4(sc, AGE_MAC_CFG, reg);
2968179100Syongari	}
2969179100Syongari	/* Stop Rx DMA engine. */
2970179100Syongari	reg = CSR_READ_4(sc, AGE_DMA_CFG);
2971179100Syongari	if ((reg & DMA_CFG_WR_ENB) != 0) {
2972179100Syongari		reg &= ~DMA_CFG_WR_ENB;
2973179100Syongari		CSR_WRITE_4(sc, AGE_DMA_CFG, reg);
2974179100Syongari	}
2975179100Syongari	for (i = AGE_RESET_TIMEOUT; i > 0; i--) {
2976179100Syongari		if ((CSR_READ_4(sc, AGE_IDLE_STATUS) &
2977179100Syongari		    (IDLE_STATUS_RXMAC | IDLE_STATUS_DMAWR)) == 0)
2978179100Syongari			break;
2979179100Syongari		DELAY(10);
2980179100Syongari	}
2981179100Syongari	if (i == 0)
2982179100Syongari		device_printf(sc->age_dev, "stopping RxMAC timeout!\n");
2983179100Syongari}
2984179100Syongari
2985179100Syongaristatic void
2986179100Syongariage_init_tx_ring(struct age_softc *sc)
2987179100Syongari{
2988179100Syongari	struct age_ring_data *rd;
2989179100Syongari	struct age_txdesc *txd;
2990179100Syongari	int i;
2991179100Syongari
2992179100Syongari	AGE_LOCK_ASSERT(sc);
2993179100Syongari
2994179100Syongari	sc->age_cdata.age_tx_prod = 0;
2995179100Syongari	sc->age_cdata.age_tx_cons = 0;
2996179100Syongari	sc->age_cdata.age_tx_cnt = 0;
2997179100Syongari
2998179100Syongari	rd = &sc->age_rdata;
2999179100Syongari	bzero(rd->age_tx_ring, AGE_TX_RING_SZ);
3000179100Syongari	for (i = 0; i < AGE_TX_RING_CNT; i++) {
3001179100Syongari		txd = &sc->age_cdata.age_txdesc[i];
3002179100Syongari		txd->tx_desc = &rd->age_tx_ring[i];
3003179100Syongari		txd->tx_m = NULL;
3004179100Syongari	}
3005179100Syongari
3006179100Syongari	bus_dmamap_sync(sc->age_cdata.age_tx_ring_tag,
3007179100Syongari	    sc->age_cdata.age_tx_ring_map,
3008179100Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
3009179100Syongari}
3010179100Syongari
3011179100Syongaristatic int
3012179100Syongariage_init_rx_ring(struct age_softc *sc)
3013179100Syongari{
3014179100Syongari	struct age_ring_data *rd;
3015179100Syongari	struct age_rxdesc *rxd;
3016179100Syongari	int i;
3017179100Syongari
3018179100Syongari	AGE_LOCK_ASSERT(sc);
3019179100Syongari
3020179100Syongari	sc->age_cdata.age_rx_cons = AGE_RX_RING_CNT - 1;
3021179100Syongari	sc->age_morework = 0;
3022179100Syongari	rd = &sc->age_rdata;
3023179100Syongari	bzero(rd->age_rx_ring, AGE_RX_RING_SZ);
3024179100Syongari	for (i = 0; i < AGE_RX_RING_CNT; i++) {
3025179100Syongari		rxd = &sc->age_cdata.age_rxdesc[i];
3026179100Syongari		rxd->rx_m = NULL;
3027179100Syongari		rxd->rx_desc = &rd->age_rx_ring[i];
3028179100Syongari		if (age_newbuf(sc, rxd) != 0)
3029179100Syongari			return (ENOBUFS);
3030179100Syongari	}
3031179100Syongari
3032179100Syongari	bus_dmamap_sync(sc->age_cdata.age_rx_ring_tag,
3033220252Syongari	    sc->age_cdata.age_rx_ring_map, BUS_DMASYNC_PREWRITE);
3034179100Syongari
3035179100Syongari	return (0);
3036179100Syongari}
3037179100Syongari
3038179100Syongaristatic void
3039179100Syongariage_init_rr_ring(struct age_softc *sc)
3040179100Syongari{
3041179100Syongari	struct age_ring_data *rd;
3042179100Syongari
3043179100Syongari	AGE_LOCK_ASSERT(sc);
3044179100Syongari
3045179100Syongari	sc->age_cdata.age_rr_cons = 0;
3046179100Syongari	AGE_RXCHAIN_RESET(sc);
3047179100Syongari
3048179100Syongari	rd = &sc->age_rdata;
3049179100Syongari	bzero(rd->age_rr_ring, AGE_RR_RING_SZ);
3050179100Syongari	bus_dmamap_sync(sc->age_cdata.age_rr_ring_tag,
3051179100Syongari	    sc->age_cdata.age_rr_ring_map,
3052179100Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
3053179100Syongari}
3054179100Syongari
3055179100Syongaristatic void
3056179100Syongariage_init_cmb_block(struct age_softc *sc)
3057179100Syongari{
3058179100Syongari	struct age_ring_data *rd;
3059179100Syongari
3060179100Syongari	AGE_LOCK_ASSERT(sc);
3061179100Syongari
3062179100Syongari	rd = &sc->age_rdata;
3063179100Syongari	bzero(rd->age_cmb_block, AGE_CMB_BLOCK_SZ);
3064179100Syongari	bus_dmamap_sync(sc->age_cdata.age_cmb_block_tag,
3065179100Syongari	    sc->age_cdata.age_cmb_block_map,
3066179100Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
3067179100Syongari}
3068179100Syongari
3069179100Syongaristatic void
3070179100Syongariage_init_smb_block(struct age_softc *sc)
3071179100Syongari{
3072179100Syongari	struct age_ring_data *rd;
3073179100Syongari
3074179100Syongari	AGE_LOCK_ASSERT(sc);
3075179100Syongari
3076179100Syongari	rd = &sc->age_rdata;
3077179100Syongari	bzero(rd->age_smb_block, AGE_SMB_BLOCK_SZ);
3078179100Syongari	bus_dmamap_sync(sc->age_cdata.age_smb_block_tag,
3079179100Syongari	    sc->age_cdata.age_smb_block_map,
3080179100Syongari	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
3081179100Syongari}
3082179100Syongari
3083179100Syongaristatic int
3084179100Syongariage_newbuf(struct age_softc *sc, struct age_rxdesc *rxd)
3085179100Syongari{
3086179100Syongari	struct rx_desc *desc;
3087179100Syongari	struct mbuf *m;
3088179100Syongari	bus_dma_segment_t segs[1];
3089179100Syongari	bus_dmamap_t map;
3090179100Syongari	int nsegs;
3091179100Syongari
3092179100Syongari	AGE_LOCK_ASSERT(sc);
3093179100Syongari
3094243857Sglebius	m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
3095179100Syongari	if (m == NULL)
3096179100Syongari		return (ENOBUFS);
3097179100Syongari	m->m_len = m->m_pkthdr.len = MCLBYTES;
3098246341Syongari#ifndef __NO_STRICT_ALIGNMENT
3099246341Syongari	m_adj(m, AGE_RX_BUF_ALIGN);
3100246341Syongari#endif
3101179100Syongari
3102179100Syongari	if (bus_dmamap_load_mbuf_sg(sc->age_cdata.age_rx_tag,
3103179100Syongari	    sc->age_cdata.age_rx_sparemap, m, segs, &nsegs, 0) != 0) {
3104179100Syongari		m_freem(m);
3105179100Syongari		return (ENOBUFS);
3106179100Syongari	}
3107179100Syongari	KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
3108179100Syongari
3109179100Syongari	if (rxd->rx_m != NULL) {
3110179100Syongari		bus_dmamap_sync(sc->age_cdata.age_rx_tag, rxd->rx_dmamap,
3111179100Syongari		    BUS_DMASYNC_POSTREAD);
3112179100Syongari		bus_dmamap_unload(sc->age_cdata.age_rx_tag, rxd->rx_dmamap);
3113179100Syongari	}
3114179100Syongari	map = rxd->rx_dmamap;
3115179100Syongari	rxd->rx_dmamap = sc->age_cdata.age_rx_sparemap;
3116179100Syongari	sc->age_cdata.age_rx_sparemap = map;
3117179100Syongari	bus_dmamap_sync(sc->age_cdata.age_rx_tag, rxd->rx_dmamap,
3118179100Syongari	    BUS_DMASYNC_PREREAD);
3119179100Syongari	rxd->rx_m = m;
3120179100Syongari
3121179100Syongari	desc = rxd->rx_desc;
3122179100Syongari	desc->addr = htole64(segs[0].ds_addr);
3123179100Syongari	desc->len = htole32((segs[0].ds_len & AGE_RD_LEN_MASK) <<
3124179100Syongari	    AGE_RD_LEN_SHIFT);
3125179100Syongari	return (0);
3126179100Syongari}
3127179100Syongari
3128179100Syongaristatic void
3129179100Syongariage_rxvlan(struct age_softc *sc)
3130179100Syongari{
3131179100Syongari	struct ifnet *ifp;
3132179100Syongari	uint32_t reg;
3133179100Syongari
3134179100Syongari	AGE_LOCK_ASSERT(sc);
3135179100Syongari
3136179100Syongari	ifp = sc->age_ifp;
3137179100Syongari	reg = CSR_READ_4(sc, AGE_MAC_CFG);
3138179100Syongari	reg &= ~MAC_CFG_VLAN_TAG_STRIP;
3139179100Syongari	if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0)
3140179100Syongari		reg |= MAC_CFG_VLAN_TAG_STRIP;
3141179100Syongari	CSR_WRITE_4(sc, AGE_MAC_CFG, reg);
3142179100Syongari}
3143179100Syongari
3144179100Syongaristatic void
3145179100Syongariage_rxfilter(struct age_softc *sc)
3146179100Syongari{
3147179100Syongari	struct ifnet *ifp;
3148179100Syongari	struct ifmultiaddr *ifma;
3149179100Syongari	uint32_t crc;
3150179100Syongari	uint32_t mchash[2];
3151179100Syongari	uint32_t rxcfg;
3152179100Syongari
3153179100Syongari	AGE_LOCK_ASSERT(sc);
3154179100Syongari
3155179100Syongari	ifp = sc->age_ifp;
3156179100Syongari
3157179100Syongari	rxcfg = CSR_READ_4(sc, AGE_MAC_CFG);
3158179100Syongari	rxcfg &= ~(MAC_CFG_ALLMULTI | MAC_CFG_BCAST | MAC_CFG_PROMISC);
3159179100Syongari	if ((ifp->if_flags & IFF_BROADCAST) != 0)
3160179100Syongari		rxcfg |= MAC_CFG_BCAST;
3161179100Syongari	if ((ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI)) != 0) {
3162179100Syongari		if ((ifp->if_flags & IFF_PROMISC) != 0)
3163179100Syongari			rxcfg |= MAC_CFG_PROMISC;
3164179100Syongari		if ((ifp->if_flags & IFF_ALLMULTI) != 0)
3165179100Syongari			rxcfg |= MAC_CFG_ALLMULTI;
3166179100Syongari		CSR_WRITE_4(sc, AGE_MAR0, 0xFFFFFFFF);
3167179100Syongari		CSR_WRITE_4(sc, AGE_MAR1, 0xFFFFFFFF);
3168179100Syongari		CSR_WRITE_4(sc, AGE_MAC_CFG, rxcfg);
3169179100Syongari		return;
3170179100Syongari	}
3171179100Syongari
3172179100Syongari	/* Program new filter. */
3173179100Syongari	bzero(mchash, sizeof(mchash));
3174179100Syongari
3175195049Srwatson	if_maddr_rlock(ifp);
3176179100Syongari	TAILQ_FOREACH(ifma, &sc->age_ifp->if_multiaddrs, ifma_link) {
3177179100Syongari		if (ifma->ifma_addr->sa_family != AF_LINK)
3178179100Syongari			continue;
3179197627Syongari		crc = ether_crc32_be(LLADDR((struct sockaddr_dl *)
3180179100Syongari		    ifma->ifma_addr), ETHER_ADDR_LEN);
3181179100Syongari		mchash[crc >> 31] |= 1 << ((crc >> 26) & 0x1f);
3182179100Syongari	}
3183195049Srwatson	if_maddr_runlock(ifp);
3184179100Syongari
3185179100Syongari	CSR_WRITE_4(sc, AGE_MAR0, mchash[0]);
3186179100Syongari	CSR_WRITE_4(sc, AGE_MAR1, mchash[1]);
3187179100Syongari	CSR_WRITE_4(sc, AGE_MAC_CFG, rxcfg);
3188179100Syongari}
3189179100Syongari
3190179100Syongaristatic int
3191179100Syongarisysctl_age_stats(SYSCTL_HANDLER_ARGS)
3192179100Syongari{
3193179100Syongari	struct age_softc *sc;
3194179100Syongari	struct age_stats *stats;
3195179100Syongari	int error, result;
3196179100Syongari
3197179100Syongari	result = -1;
3198179100Syongari	error = sysctl_handle_int(oidp, &result, 0, req);
3199179100Syongari
3200179100Syongari	if (error != 0 || req->newptr == NULL)
3201179100Syongari		return (error);
3202179100Syongari
3203179100Syongari	if (result != 1)
3204179100Syongari		return (error);
3205179100Syongari
3206179100Syongari	sc = (struct age_softc *)arg1;
3207179100Syongari	stats = &sc->age_stat;
3208179100Syongari	printf("%s statistics:\n", device_get_nameunit(sc->age_dev));
3209179100Syongari	printf("Transmit good frames : %ju\n",
3210179100Syongari	    (uintmax_t)stats->tx_frames);
3211179100Syongari	printf("Transmit good broadcast frames : %ju\n",
3212179100Syongari	    (uintmax_t)stats->tx_bcast_frames);
3213179100Syongari	printf("Transmit good multicast frames : %ju\n",
3214179100Syongari	    (uintmax_t)stats->tx_mcast_frames);
3215179100Syongari	printf("Transmit pause control frames : %u\n",
3216179100Syongari	    stats->tx_pause_frames);
3217179100Syongari	printf("Transmit control frames : %u\n",
3218179100Syongari	    stats->tx_control_frames);
3219179100Syongari	printf("Transmit frames with excessive deferrals : %u\n",
3220179100Syongari	    stats->tx_excess_defer);
3221179100Syongari	printf("Transmit deferrals : %u\n",
3222179100Syongari	    stats->tx_deferred);
3223179100Syongari	printf("Transmit good octets : %ju\n",
3224179100Syongari	    (uintmax_t)stats->tx_bytes);
3225179100Syongari	printf("Transmit good broadcast octets : %ju\n",
3226179100Syongari	    (uintmax_t)stats->tx_bcast_bytes);
3227179100Syongari	printf("Transmit good multicast octets : %ju\n",
3228179100Syongari	    (uintmax_t)stats->tx_mcast_bytes);
3229179100Syongari	printf("Transmit frames 64 bytes : %ju\n",
3230179100Syongari	    (uintmax_t)stats->tx_pkts_64);
3231179100Syongari	printf("Transmit frames 65 to 127 bytes : %ju\n",
3232179100Syongari	    (uintmax_t)stats->tx_pkts_65_127);
3233179100Syongari	printf("Transmit frames 128 to 255 bytes : %ju\n",
3234179100Syongari	    (uintmax_t)stats->tx_pkts_128_255);
3235179100Syongari	printf("Transmit frames 256 to 511 bytes : %ju\n",
3236179100Syongari	    (uintmax_t)stats->tx_pkts_256_511);
3237179100Syongari	printf("Transmit frames 512 to 1024 bytes : %ju\n",
3238179100Syongari	    (uintmax_t)stats->tx_pkts_512_1023);
3239179100Syongari	printf("Transmit frames 1024 to 1518 bytes : %ju\n",
3240179100Syongari	    (uintmax_t)stats->tx_pkts_1024_1518);
3241179100Syongari	printf("Transmit frames 1519 to MTU bytes : %ju\n",
3242179100Syongari	    (uintmax_t)stats->tx_pkts_1519_max);
3243179100Syongari	printf("Transmit single collisions : %u\n",
3244179100Syongari	    stats->tx_single_colls);
3245179100Syongari	printf("Transmit multiple collisions : %u\n",
3246179100Syongari	    stats->tx_multi_colls);
3247179100Syongari	printf("Transmit late collisions : %u\n",
3248179100Syongari	    stats->tx_late_colls);
3249179100Syongari	printf("Transmit abort due to excessive collisions : %u\n",
3250179100Syongari	    stats->tx_excess_colls);
3251179100Syongari	printf("Transmit underruns due to FIFO underruns : %u\n",
3252179100Syongari	    stats->tx_underrun);
3253179100Syongari	printf("Transmit descriptor write-back errors : %u\n",
3254179100Syongari	    stats->tx_desc_underrun);
3255179100Syongari	printf("Transmit frames with length mismatched frame size : %u\n",
3256179100Syongari	    stats->tx_lenerrs);
3257179100Syongari	printf("Transmit frames with truncated due to MTU size : %u\n",
3258179100Syongari	    stats->tx_lenerrs);
3259179100Syongari
3260179100Syongari	printf("Receive good frames : %ju\n",
3261179100Syongari	    (uintmax_t)stats->rx_frames);
3262179100Syongari	printf("Receive good broadcast frames : %ju\n",
3263179100Syongari	    (uintmax_t)stats->rx_bcast_frames);
3264179100Syongari	printf("Receive good multicast frames : %ju\n",
3265179100Syongari	    (uintmax_t)stats->rx_mcast_frames);
3266179100Syongari	printf("Receive pause control frames : %u\n",
3267179100Syongari	    stats->rx_pause_frames);
3268179100Syongari	printf("Receive control frames : %u\n",
3269179100Syongari	    stats->rx_control_frames);
3270179100Syongari	printf("Receive CRC errors : %u\n",
3271179100Syongari	    stats->rx_crcerrs);
3272179100Syongari	printf("Receive frames with length errors : %u\n",
3273179100Syongari	    stats->rx_lenerrs);
3274179100Syongari	printf("Receive good octets : %ju\n",
3275179100Syongari	    (uintmax_t)stats->rx_bytes);
3276179100Syongari	printf("Receive good broadcast octets : %ju\n",
3277179100Syongari	    (uintmax_t)stats->rx_bcast_bytes);
3278179100Syongari	printf("Receive good multicast octets : %ju\n",
3279179100Syongari	    (uintmax_t)stats->rx_mcast_bytes);
3280179100Syongari	printf("Receive frames too short : %u\n",
3281179100Syongari	    stats->rx_runts);
3282179100Syongari	printf("Receive fragmented frames : %ju\n",
3283179100Syongari	    (uintmax_t)stats->rx_fragments);
3284179100Syongari	printf("Receive frames 64 bytes : %ju\n",
3285179100Syongari	    (uintmax_t)stats->rx_pkts_64);
3286179100Syongari	printf("Receive frames 65 to 127 bytes : %ju\n",
3287179100Syongari	    (uintmax_t)stats->rx_pkts_65_127);
3288179100Syongari	printf("Receive frames 128 to 255 bytes : %ju\n",
3289179100Syongari	    (uintmax_t)stats->rx_pkts_128_255);
3290179100Syongari	printf("Receive frames 256 to 511 bytes : %ju\n",
3291179100Syongari	    (uintmax_t)stats->rx_pkts_256_511);
3292179100Syongari	printf("Receive frames 512 to 1024 bytes : %ju\n",
3293179100Syongari	    (uintmax_t)stats->rx_pkts_512_1023);
3294179100Syongari	printf("Receive frames 1024 to 1518 bytes : %ju\n",
3295179100Syongari	    (uintmax_t)stats->rx_pkts_1024_1518);
3296179100Syongari	printf("Receive frames 1519 to MTU bytes : %ju\n",
3297179100Syongari	    (uintmax_t)stats->rx_pkts_1519_max);
3298179100Syongari	printf("Receive frames too long : %ju\n",
3299179100Syongari	    (uint64_t)stats->rx_pkts_truncated);
3300179100Syongari	printf("Receive frames with FIFO overflow : %u\n",
3301179100Syongari	    stats->rx_fifo_oflows);
3302179100Syongari	printf("Receive frames with return descriptor overflow : %u\n",
3303179100Syongari	    stats->rx_desc_oflows);
3304179100Syongari	printf("Receive frames with alignment errors : %u\n",
3305179100Syongari	    stats->rx_alignerrs);
3306179100Syongari	printf("Receive frames dropped due to address filtering : %ju\n",
3307179100Syongari	    (uint64_t)stats->rx_pkts_filtered);
3308179100Syongari
3309179100Syongari	return (error);
3310179100Syongari}
3311179100Syongari
3312179100Syongaristatic int
3313179100Syongarisysctl_int_range(SYSCTL_HANDLER_ARGS, int low, int high)
3314179100Syongari{
3315179100Syongari	int error, value;
3316179100Syongari
3317179100Syongari	if (arg1 == NULL)
3318179100Syongari		return (EINVAL);
3319179100Syongari	value = *(int *)arg1;
3320179100Syongari	error = sysctl_handle_int(oidp, &value, 0, req);
3321179100Syongari	if (error || req->newptr == NULL)
3322179100Syongari		return (error);
3323179100Syongari	if (value < low || value > high)
3324179100Syongari		return (EINVAL);
3325179100Syongari        *(int *)arg1 = value;
3326179100Syongari
3327179100Syongari        return (0);
3328179100Syongari}
3329179100Syongari
3330179100Syongaristatic int
3331179100Syongarisysctl_hw_age_proc_limit(SYSCTL_HANDLER_ARGS)
3332179100Syongari{
3333179100Syongari	return (sysctl_int_range(oidp, arg1, arg2, req,
3334179100Syongari	    AGE_PROC_MIN, AGE_PROC_MAX));
3335179100Syongari}
3336179100Syongari
3337179100Syongaristatic int
3338179100Syongarisysctl_hw_age_int_mod(SYSCTL_HANDLER_ARGS)
3339179100Syongari{
3340179100Syongari
3341179100Syongari	return (sysctl_int_range(oidp, arg1, arg2, req, AGE_IM_TIMER_MIN,
3342179100Syongari	    AGE_IM_TIMER_MAX));
3343179100Syongari}
3344