1188808Sgonzo/*-
2188808Sgonzo * Copyright (c) 2009, Oleksandr Tymoshenko
3188808Sgonzo * All rights reserved.
4188808Sgonzo *
5188808Sgonzo * Redistribution and use in source and binary forms, with or without
6188808Sgonzo * modification, are permitted provided that the following conditions
7188808Sgonzo * are met:
8188808Sgonzo * 1. Redistributions of source code must retain the above copyright
9188808Sgonzo *    notice unmodified, this list of conditions, and the following
10188808Sgonzo *    disclaimer.
11188808Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
12188808Sgonzo *    notice, this list of conditions and the following disclaimer in the
13188808Sgonzo *    documentation and/or other materials provided with the distribution.
14188808Sgonzo *
15188808Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16188808Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17188808Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18188808Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19188808Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20188808Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21188808Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22188808Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23188808Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24188808Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25188808Sgonzo * SUCH DAMAGE.
26188808Sgonzo */
27188808Sgonzo
28188808Sgonzo#include <sys/cdefs.h>
29188808Sgonzo__FBSDID("$FreeBSD$");
30188808Sgonzo
31188808Sgonzo/*
32188808Sgonzo * AR71XX gigabit ethernet driver
33188808Sgonzo */
34192783Sgonzo#ifdef HAVE_KERNEL_OPTION_HEADERS
35192783Sgonzo#include "opt_device_polling.h"
36192783Sgonzo#endif
37192783Sgonzo
38234859Sadrian#include "opt_arge.h"
39234859Sadrian
40188808Sgonzo#include <sys/param.h>
41188808Sgonzo#include <sys/endian.h>
42188808Sgonzo#include <sys/systm.h>
43188808Sgonzo#include <sys/sockio.h>
44188808Sgonzo#include <sys/mbuf.h>
45188808Sgonzo#include <sys/malloc.h>
46188808Sgonzo#include <sys/kernel.h>
47188808Sgonzo#include <sys/module.h>
48188808Sgonzo#include <sys/socket.h>
49188808Sgonzo#include <sys/taskqueue.h>
50209802Sadrian#include <sys/sysctl.h>
51188808Sgonzo
52188808Sgonzo#include <net/if.h>
53188808Sgonzo#include <net/if_arp.h>
54188808Sgonzo#include <net/ethernet.h>
55188808Sgonzo#include <net/if_dl.h>
56188808Sgonzo#include <net/if_media.h>
57188808Sgonzo#include <net/if_types.h>
58188808Sgonzo
59188808Sgonzo#include <net/bpf.h>
60188808Sgonzo
61188808Sgonzo#include <machine/bus.h>
62188808Sgonzo#include <machine/cache.h>
63188808Sgonzo#include <machine/resource.h>
64188808Sgonzo#include <vm/vm_param.h>
65188808Sgonzo#include <vm/vm.h>
66188808Sgonzo#include <vm/pmap.h>
67188808Sgonzo#include <machine/pmap.h>
68188808Sgonzo#include <sys/bus.h>
69188808Sgonzo#include <sys/rman.h>
70188808Sgonzo
71188808Sgonzo#include <dev/mii/mii.h>
72188808Sgonzo#include <dev/mii/miivar.h>
73188808Sgonzo
74188808Sgonzo#include <dev/pci/pcireg.h>
75188808Sgonzo#include <dev/pci/pcivar.h>
76188808Sgonzo
77234862Sadrian#include "opt_arge.h"
78234862Sadrian
79234862Sadrian#if defined(ARGE_MDIO)
80234862Sadrian#include <dev/etherswitch/mdio.h>
81234862Sadrian#include <dev/etherswitch/miiproxy.h>
82234862Sadrian#include "mdio_if.h"
83234862Sadrian#endif
84234862Sadrian
85234862Sadrian
86188808SgonzoMODULE_DEPEND(arge, ether, 1, 1, 1);
87188808SgonzoMODULE_DEPEND(arge, miibus, 1, 1, 1);
88234862SadrianMODULE_VERSION(arge, 1);
89188808Sgonzo
90188808Sgonzo#include "miibus_if.h"
91188808Sgonzo
92188808Sgonzo#include <mips/atheros/ar71xxreg.h>
93188808Sgonzo#include <mips/atheros/if_argevar.h>
94219589Sadrian#include <mips/atheros/ar71xx_setup.h>
95211477Sadrian#include <mips/atheros/ar71xx_cpudef.h>
96188808Sgonzo
97220354Sadriantypedef enum {
98220354Sadrian	ARGE_DBG_MII 	=	0x00000001,
99220356Sadrian	ARGE_DBG_INTR	=	0x00000002,
100220356Sadrian	ARGE_DBG_TX	=	0x00000004,
101220356Sadrian	ARGE_DBG_RX	=	0x00000008,
102220356Sadrian	ARGE_DBG_ERR	=	0x00000010,
103220356Sadrian	ARGE_DBG_RESET	=	0x00000020,
104234919Sadrian	ARGE_DBG_PLL	=	0x00000040,
105220354Sadrian} arge_debug_flags;
106220354Sadrian
107234910Sadrianstatic const char * arge_miicfg_str[] = {
108234910Sadrian	"NONE",
109234910Sadrian	"GMII",
110234910Sadrian	"MII",
111234910Sadrian	"RGMII",
112234910Sadrian	"RMII"
113234910Sadrian};
114234910Sadrian
115188808Sgonzo#ifdef ARGE_DEBUG
116220354Sadrian#define	ARGEDEBUG(_sc, _m, ...) 					\
117220354Sadrian	do {								\
118220354Sadrian		if ((_m) & (_sc)->arge_debug)				\
119220354Sadrian			device_printf((_sc)->arge_dev, __VA_ARGS__);	\
120220354Sadrian	} while (0)
121188808Sgonzo#else
122220354Sadrian#define	ARGEDEBUG(_sc, _m, ...)
123188808Sgonzo#endif
124188808Sgonzo
125188808Sgonzostatic int arge_attach(device_t);
126188808Sgonzostatic int arge_detach(device_t);
127188808Sgonzostatic void arge_flush_ddr(struct arge_softc *);
128188808Sgonzostatic int arge_ifmedia_upd(struct ifnet *);
129188808Sgonzostatic void arge_ifmedia_sts(struct ifnet *, struct ifmediareq *);
130188808Sgonzostatic int arge_ioctl(struct ifnet *, u_long, caddr_t);
131188808Sgonzostatic void arge_init(void *);
132188808Sgonzostatic void arge_init_locked(struct arge_softc *);
133188808Sgonzostatic void arge_link_task(void *, int);
134232914Sadrianstatic void arge_update_link_locked(struct arge_softc *sc);
135199234Sgonzostatic void arge_set_pll(struct arge_softc *, int, int);
136188808Sgonzostatic int arge_miibus_readreg(device_t, int, int);
137188808Sgonzostatic void arge_miibus_statchg(device_t);
138188808Sgonzostatic int arge_miibus_writereg(device_t, int, int, int);
139188808Sgonzostatic int arge_probe(device_t);
140188808Sgonzostatic void arge_reset_dma(struct arge_softc *);
141188808Sgonzostatic int arge_resume(device_t);
142188808Sgonzostatic int arge_rx_ring_init(struct arge_softc *);
143232912Sadrianstatic void arge_rx_ring_free(struct arge_softc *sc);
144188808Sgonzostatic int arge_tx_ring_init(struct arge_softc *);
145255300Sloosstatic void arge_tx_ring_free(struct arge_softc *);
146192821Sgonzo#ifdef DEVICE_POLLING
147198667Sgonzostatic int arge_poll(struct ifnet *, enum poll_cmd, int);
148192821Sgonzo#endif
149194059Sgonzostatic int arge_shutdown(device_t);
150188808Sgonzostatic void arge_start(struct ifnet *);
151188808Sgonzostatic void arge_start_locked(struct ifnet *);
152188808Sgonzostatic void arge_stop(struct arge_softc *);
153188808Sgonzostatic int arge_suspend(device_t);
154188808Sgonzo
155198667Sgonzostatic int arge_rx_locked(struct arge_softc *);
156188808Sgonzostatic void arge_tx_locked(struct arge_softc *);
157188808Sgonzostatic void arge_intr(void *);
158188808Sgonzostatic int arge_intr_filter(void *);
159188808Sgonzostatic void arge_tick(void *);
160188808Sgonzo
161234862Sadrianstatic void arge_hinted_child(device_t bus, const char *dname, int dunit);
162234862Sadrian
163199234Sgonzo/*
164199234Sgonzo * ifmedia callbacks for multiPHY MAC
165199234Sgonzo */
166199234Sgonzovoid arge_multiphy_mediastatus(struct ifnet *, struct ifmediareq *);
167199234Sgonzoint arge_multiphy_mediachange(struct ifnet *);
168199234Sgonzo
169188808Sgonzostatic void arge_dmamap_cb(void *, bus_dma_segment_t *, int, int);
170188808Sgonzostatic int arge_dma_alloc(struct arge_softc *);
171188808Sgonzostatic void arge_dma_free(struct arge_softc *);
172188808Sgonzostatic int arge_newbuf(struct arge_softc *, int);
173188808Sgonzostatic __inline void arge_fixup_rx(struct mbuf *);
174188808Sgonzo
175188808Sgonzostatic device_method_t arge_methods[] = {
176188808Sgonzo	/* Device interface */
177188808Sgonzo	DEVMETHOD(device_probe,		arge_probe),
178188808Sgonzo	DEVMETHOD(device_attach,	arge_attach),
179188808Sgonzo	DEVMETHOD(device_detach,	arge_detach),
180188808Sgonzo	DEVMETHOD(device_suspend,	arge_suspend),
181188808Sgonzo	DEVMETHOD(device_resume,	arge_resume),
182188808Sgonzo	DEVMETHOD(device_shutdown,	arge_shutdown),
183188808Sgonzo
184188808Sgonzo	/* MII interface */
185188808Sgonzo	DEVMETHOD(miibus_readreg,	arge_miibus_readreg),
186188808Sgonzo	DEVMETHOD(miibus_writereg,	arge_miibus_writereg),
187188808Sgonzo	DEVMETHOD(miibus_statchg,	arge_miibus_statchg),
188188808Sgonzo
189234862Sadrian	/* bus interface */
190234862Sadrian	DEVMETHOD(bus_add_child,	device_add_child_ordered),
191234862Sadrian	DEVMETHOD(bus_hinted_child,	arge_hinted_child),
192234862Sadrian
193227843Smarius	DEVMETHOD_END
194188808Sgonzo};
195188808Sgonzo
196188808Sgonzostatic driver_t arge_driver = {
197188808Sgonzo	"arge",
198188808Sgonzo	arge_methods,
199188808Sgonzo	sizeof(struct arge_softc)
200188808Sgonzo};
201188808Sgonzo
202188808Sgonzostatic devclass_t arge_devclass;
203188808Sgonzo
204188808SgonzoDRIVER_MODULE(arge, nexus, arge_driver, arge_devclass, 0, 0);
205188808SgonzoDRIVER_MODULE(miibus, arge, miibus_driver, miibus_devclass, 0, 0);
206188808Sgonzo
207234862Sadrian#if defined(ARGE_MDIO)
208234862Sadrianstatic int argemdio_probe(device_t);
209234862Sadrianstatic int argemdio_attach(device_t);
210234862Sadrianstatic int argemdio_detach(device_t);
211234862Sadrian
212188808Sgonzo/*
213234862Sadrian * Declare an additional, separate driver for accessing the MDIO bus.
214234862Sadrian */
215234862Sadrianstatic device_method_t argemdio_methods[] = {
216234862Sadrian	/* Device interface */
217234862Sadrian	DEVMETHOD(device_probe,		argemdio_probe),
218234862Sadrian	DEVMETHOD(device_attach,	argemdio_attach),
219234862Sadrian	DEVMETHOD(device_detach,	argemdio_detach),
220234862Sadrian
221234862Sadrian	/* bus interface */
222234862Sadrian	DEVMETHOD(bus_add_child,	device_add_child_ordered),
223234862Sadrian
224234862Sadrian	/* MDIO access */
225234862Sadrian	DEVMETHOD(mdio_readreg,		arge_miibus_readreg),
226234862Sadrian	DEVMETHOD(mdio_writereg,	arge_miibus_writereg),
227234862Sadrian};
228234862Sadrian
229234862SadrianDEFINE_CLASS_0(argemdio, argemdio_driver, argemdio_methods,
230234862Sadrian    sizeof(struct arge_softc));
231234862Sadrianstatic devclass_t argemdio_devclass;
232234862Sadrian
233234862SadrianDRIVER_MODULE(miiproxy, arge, miiproxy_driver, miiproxy_devclass, 0, 0);
234234862SadrianDRIVER_MODULE(argemdio, nexus, argemdio_driver, argemdio_devclass, 0, 0);
235234862SadrianDRIVER_MODULE(mdio, argemdio, mdio_driver, mdio_devclass, 0, 0);
236234862Sadrian#endif
237234862Sadrian
238234862Sadrian/*
239232627Sray * RedBoot passes MAC address to entry point as environment
240192179Sgonzo * variable. platfrom_start parses it and stores in this variable
241192179Sgonzo */
242192179Sgonzoextern uint32_t ar711_base_mac[ETHER_ADDR_LEN];
243192179Sgonzo
244199038Sgonzostatic struct mtx miibus_mtx;
245199038Sgonzo
246206400SgonzoMTX_SYSINIT(miibus_mtx, &miibus_mtx, "arge mii lock", MTX_DEF);
247199038Sgonzo
248192179Sgonzo/*
249232627Sray * Flushes all
250188808Sgonzo */
251188808Sgonzostatic void
252188808Sgonzoarge_flush_ddr(struct arge_softc *sc)
253188808Sgonzo{
254228064Sray
255228064Sray	ar71xx_device_flush_ddr_ge(sc->arge_mac_unit);
256188808Sgonzo}
257188808Sgonzo
258232627Sraystatic int
259188808Sgonzoarge_probe(device_t dev)
260188808Sgonzo{
261188808Sgonzo
262188808Sgonzo	device_set_desc(dev, "Atheros AR71xx built-in ethernet interface");
263188808Sgonzo	return (0);
264188808Sgonzo}
265188808Sgonzo
266209802Sadrianstatic void
267209802Sadrianarge_attach_sysctl(device_t dev)
268209802Sadrian{
269209802Sadrian	struct arge_softc *sc = device_get_softc(dev);
270209802Sadrian	struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev);
271209802Sadrian	struct sysctl_oid *tree = device_get_sysctl_tree(dev);
272209802Sadrian
273220355Sadrian#ifdef	ARGE_DEBUG
274209802Sadrian	SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
275209802Sadrian		"debug", CTLFLAG_RW, &sc->arge_debug, 0,
276209802Sadrian		"arge interface debugging flags");
277220355Sadrian#endif
278209809Sadrian
279209809Sadrian	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
280209809Sadrian		"tx_pkts_aligned", CTLFLAG_RW, &sc->stats.tx_pkts_aligned, 0,
281209809Sadrian		"number of TX aligned packets");
282209809Sadrian
283209809Sadrian	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
284232628Sray		"tx_pkts_unaligned", CTLFLAG_RW, &sc->stats.tx_pkts_unaligned,
285232628Sray		0, "number of TX unaligned packets");
286220354Sadrian
287220355Sadrian#ifdef	ARGE_DEBUG
288220355Sadrian	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_prod",
289220355Sadrian	    CTLFLAG_RW, &sc->arge_cdata.arge_tx_prod, 0, "");
290220355Sadrian	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_cons",
291220355Sadrian	    CTLFLAG_RW, &sc->arge_cdata.arge_tx_cons, 0, "");
292220355Sadrian	SYSCTL_ADD_UINT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tx_cnt",
293220355Sadrian	    CTLFLAG_RW, &sc->arge_cdata.arge_tx_cnt, 0, "");
294220355Sadrian#endif
295209802Sadrian}
296209802Sadrian
297234862Sadrianstatic void
298234862Sadrianarge_reset_mac(struct arge_softc *sc)
299234862Sadrian{
300234862Sadrian	uint32_t reg;
301234862Sadrian
302234862Sadrian	/* Step 1. Soft-reset MAC */
303234862Sadrian	ARGE_SET_BITS(sc, AR71XX_MAC_CFG1, MAC_CFG1_SOFT_RESET);
304234862Sadrian	DELAY(20);
305234862Sadrian
306234862Sadrian	/* Step 2. Punt the MAC core from the central reset register */
307234862Sadrian	ar71xx_device_stop(sc->arge_mac_unit == 0 ? RST_RESET_GE0_MAC :
308234862Sadrian	    RST_RESET_GE1_MAC);
309234862Sadrian	DELAY(100);
310234862Sadrian	ar71xx_device_start(sc->arge_mac_unit == 0 ? RST_RESET_GE0_MAC :
311234862Sadrian	    RST_RESET_GE1_MAC);
312234862Sadrian
313234862Sadrian	/* Step 3. Reconfigure MAC block */
314234862Sadrian	ARGE_WRITE(sc, AR71XX_MAC_CFG1,
315234862Sadrian		MAC_CFG1_SYNC_RX | MAC_CFG1_RX_ENABLE |
316234862Sadrian		MAC_CFG1_SYNC_TX | MAC_CFG1_TX_ENABLE);
317234862Sadrian
318234862Sadrian	reg = ARGE_READ(sc, AR71XX_MAC_CFG2);
319234862Sadrian	reg |= MAC_CFG2_ENABLE_PADCRC | MAC_CFG2_LENGTH_FIELD ;
320234862Sadrian	ARGE_WRITE(sc, AR71XX_MAC_CFG2, reg);
321234862Sadrian
322234862Sadrian	ARGE_WRITE(sc, AR71XX_MAC_MAX_FRAME_LEN, 1536);
323234862Sadrian}
324234862Sadrian
325234862Sadrianstatic void
326234862Sadrianarge_reset_miibus(struct arge_softc *sc)
327234862Sadrian{
328234862Sadrian
329234862Sadrian	/* Reset MII bus */
330234862Sadrian	ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_RESET);
331234862Sadrian	DELAY(100);
332234862Sadrian	ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_CLOCK_DIV_28);
333234862Sadrian	DELAY(100);
334234862Sadrian}
335234862Sadrian
336234919Sadrianstatic void
337234919Sadrianarge_fetch_pll_config(struct arge_softc *sc)
338234919Sadrian{
339234919Sadrian	long int val;
340234919Sadrian
341234919Sadrian	if (resource_long_value(device_get_name(sc->arge_dev),
342234919Sadrian	    device_get_unit(sc->arge_dev),
343234919Sadrian	    "pll_10", &val) == 0) {
344234919Sadrian		sc->arge_pllcfg.pll_10 = val;
345234919Sadrian		device_printf(sc->arge_dev, "%s: pll_10 = 0x%x\n",
346234919Sadrian		    __func__, (int) val);
347234919Sadrian	}
348234919Sadrian	if (resource_long_value(device_get_name(sc->arge_dev),
349234919Sadrian	    device_get_unit(sc->arge_dev),
350234919Sadrian	    "pll_100", &val) == 0) {
351234919Sadrian		sc->arge_pllcfg.pll_100 = val;
352234919Sadrian		device_printf(sc->arge_dev, "%s: pll_100 = 0x%x\n",
353234919Sadrian		    __func__, (int) val);
354234919Sadrian	}
355234919Sadrian	if (resource_long_value(device_get_name(sc->arge_dev),
356234919Sadrian	    device_get_unit(sc->arge_dev),
357234919Sadrian	    "pll_1000", &val) == 0) {
358234919Sadrian		sc->arge_pllcfg.pll_1000 = val;
359234919Sadrian		device_printf(sc->arge_dev, "%s: pll_1000 = 0x%x\n",
360234919Sadrian		    __func__, (int) val);
361234919Sadrian	}
362234919Sadrian}
363234919Sadrian
364188808Sgonzostatic int
365188808Sgonzoarge_attach(device_t dev)
366188808Sgonzo{
367188808Sgonzo	struct ifnet		*ifp;
368188808Sgonzo	struct arge_softc	*sc;
369234862Sadrian	int			error = 0, rid;
370234862Sadrian	uint32_t		rnd;
371234862Sadrian	int			is_base_mac_empty, i;
372199234Sgonzo	uint32_t		hint;
373220260Sadrian	long			eeprom_mac_addr = 0;
374234910Sadrian	int			miicfg = 0;
375254690Ssbruno	int			readascii = 0;
376188808Sgonzo
377188808Sgonzo	sc = device_get_softc(dev);
378188808Sgonzo	sc->arge_dev = dev;
379188808Sgonzo	sc->arge_mac_unit = device_get_unit(dev);
380188808Sgonzo
381220260Sadrian	/*
382220260Sadrian	 * Some units (eg the TP-Link WR-1043ND) do not have a convenient
383220260Sadrian	 * EEPROM location to read the ethernet MAC address from.
384220260Sadrian	 * OpenWRT simply snaffles it from a fixed location.
385220260Sadrian	 *
386220260Sadrian	 * Since multiple units seem to use this feature, include
387220260Sadrian	 * a method of setting the MAC address based on an flash location
388220260Sadrian	 * in CPU address space.
389254690Ssbruno	 *
390254690Ssbruno	 * Some vendors have decided to store the mac address as a literal
391254690Ssbruno	 * string of 18 characters in xx:xx:xx:xx:xx:xx format instead of
392254690Ssbruno	 * an array of numbers.  Expose a hint to turn on this conversion
393254690Ssbruno	 * feature via strtol()
394220260Sadrian	 */
395254690Ssbruno	 if (resource_long_value(device_get_name(dev), device_get_unit(dev),
396220260Sadrian	    "eeprommac", &eeprom_mac_addr) == 0) {
397220260Sadrian		int i;
398232628Sray		const char *mac =
399232628Sray		    (const char *) MIPS_PHYS_TO_KSEG1(eeprom_mac_addr);
400220260Sadrian		device_printf(dev, "Overriding MAC from EEPROM\n");
401254690Ssbruno		if (resource_int_value(device_get_name(dev), device_get_unit(dev),
402254690Ssbruno			"readascii", &readascii) == 0) {
403254690Ssbruno			device_printf(dev, "Vendor stores MAC in ASCII format\n");
404254690Ssbruno			for (i = 0; i < 6; i++) {
405254690Ssbruno				ar711_base_mac[i] = strtol(&(mac[i*3]), NULL, 16);
406254690Ssbruno			}
407254690Ssbruno		} else {
408254690Ssbruno			for (i = 0; i < 6; i++) {
409254690Ssbruno				ar711_base_mac[i] = mac[i];
410254690Ssbruno			}
411220260Sadrian		}
412220260Sadrian	}
413220260Sadrian
414232627Sray	KASSERT(((sc->arge_mac_unit == 0) || (sc->arge_mac_unit == 1)),
415188808Sgonzo	    ("if_arge: Only MAC0 and MAC1 supported"));
416188808Sgonzo
417188808Sgonzo	/*
418234919Sadrian	 * Fetch the PLL configuration.
419234919Sadrian	 */
420234919Sadrian	arge_fetch_pll_config(sc);
421234919Sadrian
422234919Sadrian	/*
423234910Sadrian	 * Get the MII configuration, if applicable.
424234910Sadrian	 */
425234910Sadrian	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
426234910Sadrian	    "miimode", &miicfg) == 0) {
427234910Sadrian		/* XXX bounds check? */
428234910Sadrian		device_printf(dev, "%s: overriding MII mode to '%s'\n",
429234910Sadrian		    __func__, arge_miicfg_str[miicfg]);
430234910Sadrian		sc->arge_miicfg = miicfg;
431234910Sadrian	}
432234910Sadrian
433234910Sadrian	/*
434188808Sgonzo	 *  Get which PHY of 5 available we should use for this unit
435188808Sgonzo	 */
436234862Sadrian	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
437234862Sadrian	    "phymask", &sc->arge_phymask) != 0) {
438188808Sgonzo		/*
439232627Sray		 * Use port 4 (WAN) for GE0. For any other port use
440232627Sray		 * its PHY the same as its unit number
441188808Sgonzo		 */
442188808Sgonzo		if (sc->arge_mac_unit == 0)
443234862Sadrian			sc->arge_phymask = (1 << 4);
444188808Sgonzo		else
445199234Sgonzo			/* Use all phys up to 4 */
446234862Sadrian			sc->arge_phymask = (1 << 4) - 1;
447188808Sgonzo
448234862Sadrian		device_printf(dev, "No PHY specified, using mask %d\n", sc->arge_phymask);
449188808Sgonzo	}
450188808Sgonzo
451199234Sgonzo	/*
452232627Sray	 *  Get default media & duplex mode, by default its Base100T
453199234Sgonzo	 *  and full duplex
454199234Sgonzo	 */
455232627Sray	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
456199234Sgonzo	    "media", &hint) != 0)
457199234Sgonzo		hint = 0;
458188808Sgonzo
459199234Sgonzo	if (hint == 1000)
460199234Sgonzo		sc->arge_media_type = IFM_1000_T;
461199234Sgonzo	else
462199234Sgonzo		sc->arge_media_type = IFM_100_TX;
463199234Sgonzo
464232627Sray	if (resource_int_value(device_get_name(dev), device_get_unit(dev),
465199234Sgonzo	    "fduplex", &hint) != 0)
466199234Sgonzo		hint = 1;
467199234Sgonzo
468199234Sgonzo	if (hint)
469199234Sgonzo		sc->arge_duplex_mode = IFM_FDX;
470199234Sgonzo	else
471199234Sgonzo		sc->arge_duplex_mode = 0;
472199234Sgonzo
473188808Sgonzo	mtx_init(&sc->arge_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
474188808Sgonzo	    MTX_DEF);
475188808Sgonzo	callout_init_mtx(&sc->arge_stat_callout, &sc->arge_mtx, 0);
476188808Sgonzo	TASK_INIT(&sc->arge_link_task, 0, arge_link_task, sc);
477188808Sgonzo
478188808Sgonzo	/* Map control/status registers. */
479188808Sgonzo	sc->arge_rid = 0;
480234862Sadrian	sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
481234862Sadrian	    &sc->arge_rid, RF_ACTIVE | RF_SHAREABLE);
482188808Sgonzo
483188808Sgonzo	if (sc->arge_res == NULL) {
484188808Sgonzo		device_printf(dev, "couldn't map memory\n");
485188808Sgonzo		error = ENXIO;
486188808Sgonzo		goto fail;
487188808Sgonzo	}
488188808Sgonzo
489188808Sgonzo	/* Allocate interrupts */
490188808Sgonzo	rid = 0;
491232627Sray	sc->arge_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
492188808Sgonzo	    RF_SHAREABLE | RF_ACTIVE);
493188808Sgonzo
494188808Sgonzo	if (sc->arge_irq == NULL) {
495188808Sgonzo		device_printf(dev, "couldn't map interrupt\n");
496188808Sgonzo		error = ENXIO;
497188808Sgonzo		goto fail;
498188808Sgonzo	}
499188808Sgonzo
500188808Sgonzo	/* Allocate ifnet structure. */
501188808Sgonzo	ifp = sc->arge_ifp = if_alloc(IFT_ETHER);
502188808Sgonzo
503188808Sgonzo	if (ifp == NULL) {
504188808Sgonzo		device_printf(dev, "couldn't allocate ifnet structure\n");
505188808Sgonzo		error = ENOSPC;
506188808Sgonzo		goto fail;
507188808Sgonzo	}
508188808Sgonzo
509188808Sgonzo	ifp->if_softc = sc;
510188808Sgonzo	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
511188808Sgonzo	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
512188808Sgonzo	ifp->if_ioctl = arge_ioctl;
513188808Sgonzo	ifp->if_start = arge_start;
514188808Sgonzo	ifp->if_init = arge_init;
515198932Sgonzo	sc->arge_if_flags = ifp->if_flags;
516188808Sgonzo
517188808Sgonzo	/* XXX: add real size */
518207554Ssobomax	IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen);
519207554Ssobomax	ifp->if_snd.ifq_maxlen = ifqmaxlen;
520188808Sgonzo	IFQ_SET_READY(&ifp->if_snd);
521188808Sgonzo
522188808Sgonzo	ifp->if_capenable = ifp->if_capabilities;
523192783Sgonzo#ifdef DEVICE_POLLING
524192783Sgonzo	ifp->if_capabilities |= IFCAP_POLLING;
525192783Sgonzo#endif
526188808Sgonzo
527192179Sgonzo	is_base_mac_empty = 1;
528192179Sgonzo	for (i = 0; i < ETHER_ADDR_LEN; i++) {
529234862Sadrian		sc->arge_eaddr[i] = ar711_base_mac[i] & 0xff;
530234862Sadrian		if (sc->arge_eaddr[i] != 0)
531192179Sgonzo			is_base_mac_empty = 0;
532192179Sgonzo	}
533188808Sgonzo
534192179Sgonzo	if (is_base_mac_empty) {
535192179Sgonzo		/*
536192179Sgonzo		 * No MAC address configured. Generate the random one.
537192179Sgonzo		 */
538198933Sgonzo		if  (bootverbose)
539232627Sray			device_printf(dev,
540192179Sgonzo			    "Generating random ethernet address.\n");
541192179Sgonzo
542192179Sgonzo		rnd = arc4random();
543234862Sadrian		sc->arge_eaddr[0] = 'b';
544234862Sadrian		sc->arge_eaddr[1] = 's';
545234862Sadrian		sc->arge_eaddr[2] = 'd';
546234862Sadrian		sc->arge_eaddr[3] = (rnd >> 24) & 0xff;
547234862Sadrian		sc->arge_eaddr[4] = (rnd >> 16) & 0xff;
548234862Sadrian		sc->arge_eaddr[5] = (rnd >> 8) & 0xff;
549192179Sgonzo	}
550198970Sgonzo	if (sc->arge_mac_unit != 0)
551234862Sadrian		sc->arge_eaddr[5] +=  sc->arge_mac_unit;
552198970Sgonzo
553188808Sgonzo	if (arge_dma_alloc(sc) != 0) {
554188808Sgonzo		error = ENXIO;
555188808Sgonzo		goto fail;
556188808Sgonzo	}
557188808Sgonzo
558234862Sadrian	/*
559234862Sadrian	 * Don't do this for the MDIO bus case - it's already done
560234862Sadrian	 * as part of the MDIO bus attachment.
561234862Sadrian	 */
562234862Sadrian#if !defined(ARGE_MDIO)
563192569Sdwhite	/* Initialize the MAC block */
564234862Sadrian	arge_reset_mac(sc);
565234862Sadrian	arge_reset_miibus(sc);
566234862Sadrian#endif
567232627Sray
568234910Sadrian	/* Configure MII mode, just for convienence */
569234910Sadrian	if (sc->arge_miicfg != 0)
570234910Sadrian		ar71xx_device_set_mii_if(sc->arge_mac_unit, sc->arge_miicfg);
571234910Sadrian
572232627Sray	/*
573188808Sgonzo	 * Set all Ethernet address registers to the same initial values
574232627Sray	 * set all four addresses to 66-88-aa-cc-dd-ee
575188808Sgonzo	 */
576234862Sadrian	ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR1, (sc->arge_eaddr[2] << 24)
577234862Sadrian	    | (sc->arge_eaddr[3] << 16) | (sc->arge_eaddr[4] << 8)
578234862Sadrian	    | sc->arge_eaddr[5]);
579234862Sadrian	ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR2, (sc->arge_eaddr[0] << 8)
580234862Sadrian	    | sc->arge_eaddr[1]);
581188808Sgonzo
582232627Sray	ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG0,
583188808Sgonzo	    FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT);
584188808Sgonzo
585219589Sadrian	switch (ar71xx_soc) {
586219589Sadrian		case AR71XX_SOC_AR7240:
587219589Sadrian		case AR71XX_SOC_AR7241:
588219589Sadrian		case AR71XX_SOC_AR7242:
589249123Sadrian		case AR71XX_SOC_AR9330:
590249123Sadrian		case AR71XX_SOC_AR9331:
591219589Sadrian			ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG1, 0x0010ffff);
592219589Sadrian			ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG2, 0x015500aa);
593219589Sadrian			break;
594219589Sadrian		default:
595219589Sadrian			ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG1, 0x0fff0000);
596219589Sadrian			ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG2, 0x00001fff);
597219589Sadrian	}
598219589Sadrian
599232627Sray	ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMATCH,
600192783Sgonzo	    FIFO_RX_FILTMATCH_DEFAULT);
601188808Sgonzo
602232627Sray	ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK,
603192783Sgonzo	    FIFO_RX_FILTMASK_DEFAULT);
604188808Sgonzo
605234862Sadrian#if defined(ARGE_MDIO)
606234862Sadrian	sc->arge_miiproxy = mii_attach_proxy(sc->arge_dev);
607234862Sadrian#endif
608199234Sgonzo
609234862Sadrian	device_printf(sc->arge_dev, "finishing attachment, phymask %04x"
610234862Sadrian	    ", proxy %s \n", sc->arge_phymask, sc->arge_miiproxy == NULL ?
611234862Sadrian	    "null" : "set");
612234862Sadrian	for (i = 0; i < ARGE_NPHY; i++) {
613234862Sadrian		if (((1 << i) & sc->arge_phymask) != 0) {
614234862Sadrian			error = mii_attach(sc->arge_miiproxy != NULL ?
615234862Sadrian			    sc->arge_miiproxy : sc->arge_dev,
616234862Sadrian			    &sc->arge_miibus, sc->arge_ifp,
617234862Sadrian			    arge_ifmedia_upd, arge_ifmedia_sts,
618234862Sadrian			    BMSR_DEFCAPMASK, i, MII_OFFSET_ANY, 0);
619234862Sadrian			if (error != 0) {
620234862Sadrian				device_printf(sc->arge_dev, "unable to attach"
621234862Sadrian				    " PHY %d: %d\n", i, error);
622234862Sadrian				goto fail;
623234862Sadrian			}
624199234Sgonzo		}
625199234Sgonzo	}
626234862Sadrian	if (sc->arge_miibus == NULL) {
627234862Sadrian		/* no PHY, so use hard-coded values */
628234862Sadrian		ifmedia_init(&sc->arge_ifmedia, 0,
629199234Sgonzo		    arge_multiphy_mediachange,
630199234Sgonzo		    arge_multiphy_mediastatus);
631199234Sgonzo		ifmedia_add(&sc->arge_ifmedia,
632232627Sray		    IFM_ETHER | sc->arge_media_type  | sc->arge_duplex_mode,
633199234Sgonzo		    0, NULL);
634199234Sgonzo		ifmedia_set(&sc->arge_ifmedia,
635199234Sgonzo		    IFM_ETHER | sc->arge_media_type  | sc->arge_duplex_mode);
636199234Sgonzo		arge_set_pll(sc, sc->arge_media_type, sc->arge_duplex_mode);
637199234Sgonzo	}
638199234Sgonzo
639188808Sgonzo	/* Call MI attach routine. */
640234862Sadrian	ether_ifattach(sc->arge_ifp, sc->arge_eaddr);
641188808Sgonzo
642188808Sgonzo	/* Hook interrupt last to avoid having to lock softc */
643234862Sadrian	error = bus_setup_intr(sc->arge_dev, sc->arge_irq, INTR_TYPE_NET | INTR_MPSAFE,
644188808Sgonzo	    arge_intr_filter, arge_intr, sc, &sc->arge_intrhand);
645188808Sgonzo
646188808Sgonzo	if (error) {
647234862Sadrian		device_printf(sc->arge_dev, "couldn't set up irq\n");
648234862Sadrian		ether_ifdetach(sc->arge_ifp);
649188808Sgonzo		goto fail;
650188808Sgonzo	}
651188808Sgonzo
652209802Sadrian	/* setup sysctl variables */
653234862Sadrian	arge_attach_sysctl(sc->arge_dev);
654209802Sadrian
655188808Sgonzofail:
656234862Sadrian	if (error)
657188808Sgonzo		arge_detach(dev);
658188808Sgonzo
659188808Sgonzo	return (error);
660188808Sgonzo}
661188808Sgonzo
662188808Sgonzostatic int
663188808Sgonzoarge_detach(device_t dev)
664188808Sgonzo{
665192783Sgonzo	struct arge_softc	*sc = device_get_softc(dev);
666188808Sgonzo	struct ifnet		*ifp = sc->arge_ifp;
667188808Sgonzo
668232628Sray	KASSERT(mtx_initialized(&sc->arge_mtx),
669232628Sray	    ("arge mutex not initialized"));
670188808Sgonzo
671188808Sgonzo	/* These should only be active if attach succeeded */
672188808Sgonzo	if (device_is_attached(dev)) {
673188808Sgonzo		ARGE_LOCK(sc);
674188808Sgonzo		sc->arge_detach = 1;
675192783Sgonzo#ifdef DEVICE_POLLING
676192783Sgonzo		if (ifp->if_capenable & IFCAP_POLLING)
677192783Sgonzo			ether_poll_deregister(ifp);
678192783Sgonzo#endif
679192783Sgonzo
680188808Sgonzo		arge_stop(sc);
681188808Sgonzo		ARGE_UNLOCK(sc);
682188808Sgonzo		taskqueue_drain(taskqueue_swi, &sc->arge_link_task);
683188808Sgonzo		ether_ifdetach(ifp);
684188808Sgonzo	}
685188808Sgonzo
686188808Sgonzo	if (sc->arge_miibus)
687188808Sgonzo		device_delete_child(dev, sc->arge_miibus);
688199234Sgonzo
689234862Sadrian	if (sc->arge_miiproxy)
690234862Sadrian		device_delete_child(dev, sc->arge_miiproxy);
691234862Sadrian
692188808Sgonzo	bus_generic_detach(dev);
693188808Sgonzo
694188808Sgonzo	if (sc->arge_intrhand)
695188808Sgonzo		bus_teardown_intr(dev, sc->arge_irq, sc->arge_intrhand);
696188808Sgonzo
697188808Sgonzo	if (sc->arge_res)
698232627Sray		bus_release_resource(dev, SYS_RES_MEMORY, sc->arge_rid,
699188808Sgonzo		    sc->arge_res);
700188808Sgonzo
701188808Sgonzo	if (ifp)
702188808Sgonzo		if_free(ifp);
703188808Sgonzo
704188808Sgonzo	arge_dma_free(sc);
705188808Sgonzo
706188808Sgonzo	mtx_destroy(&sc->arge_mtx);
707188808Sgonzo
708188808Sgonzo	return (0);
709188808Sgonzo
710188808Sgonzo}
711188808Sgonzo
712188808Sgonzostatic int
713188808Sgonzoarge_suspend(device_t dev)
714188808Sgonzo{
715188808Sgonzo
716188808Sgonzo	panic("%s", __func__);
717188808Sgonzo	return 0;
718188808Sgonzo}
719188808Sgonzo
720188808Sgonzostatic int
721188808Sgonzoarge_resume(device_t dev)
722188808Sgonzo{
723188808Sgonzo
724188808Sgonzo	panic("%s", __func__);
725188808Sgonzo	return 0;
726188808Sgonzo}
727188808Sgonzo
728194059Sgonzostatic int
729188808Sgonzoarge_shutdown(device_t dev)
730188808Sgonzo{
731188808Sgonzo	struct arge_softc	*sc;
732188808Sgonzo
733188808Sgonzo	sc = device_get_softc(dev);
734188808Sgonzo
735188808Sgonzo	ARGE_LOCK(sc);
736188808Sgonzo	arge_stop(sc);
737188808Sgonzo	ARGE_UNLOCK(sc);
738194059Sgonzo
739194059Sgonzo	return (0);
740188808Sgonzo}
741188808Sgonzo
742234862Sadrianstatic void
743234862Sadrianarge_hinted_child(device_t bus, const char *dname, int dunit)
744234862Sadrian{
745234862Sadrian	BUS_ADD_CHILD(bus, 0, dname, dunit);
746234862Sadrian	device_printf(bus, "hinted child %s%d\n", dname, dunit);
747234862Sadrian}
748234862Sadrian
749188808Sgonzostatic int
750188808Sgonzoarge_miibus_readreg(device_t dev, int phy, int reg)
751188808Sgonzo{
752188808Sgonzo	struct arge_softc * sc = device_get_softc(dev);
753188808Sgonzo	int i, result;
754232627Sray	uint32_t addr = (phy << MAC_MII_PHY_ADDR_SHIFT)
755188808Sgonzo	    | (reg & MAC_MII_REG_MASK);
756188808Sgonzo
757199038Sgonzo	mtx_lock(&miibus_mtx);
758234862Sadrian	ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE);
759234862Sadrian	ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_ADDR, addr);
760234862Sadrian	ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ);
761188808Sgonzo
762188808Sgonzo	i = ARGE_MII_TIMEOUT;
763234862Sadrian	while ((ARGE_MDIO_READ(sc, AR71XX_MAC_MII_INDICATOR) &
764188808Sgonzo	    MAC_MII_INDICATOR_BUSY) && (i--))
765188808Sgonzo		DELAY(5);
766188808Sgonzo
767188808Sgonzo	if (i < 0) {
768199038Sgonzo		mtx_unlock(&miibus_mtx);
769220354Sadrian		ARGEDEBUG(sc, ARGE_DBG_MII, "%s timedout\n", __func__);
770188808Sgonzo		/* XXX: return ERRNO istead? */
771188808Sgonzo		return (-1);
772188808Sgonzo	}
773188808Sgonzo
774234862Sadrian	result = ARGE_MDIO_READ(sc, AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK;
775234862Sadrian	ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE);
776199038Sgonzo	mtx_unlock(&miibus_mtx);
777199038Sgonzo
778232628Sray	ARGEDEBUG(sc, ARGE_DBG_MII,
779232628Sray	    "%s: phy=%d, reg=%02x, value[%08x]=%04x\n",
780232628Sray	    __func__, phy, reg, addr, result);
781188808Sgonzo
782188808Sgonzo	return (result);
783188808Sgonzo}
784188808Sgonzo
785188808Sgonzostatic int
786188808Sgonzoarge_miibus_writereg(device_t dev, int phy, int reg, int data)
787188808Sgonzo{
788188808Sgonzo	struct arge_softc * sc = device_get_softc(dev);
789188808Sgonzo	int i;
790232627Sray	uint32_t addr =
791196794Sgonzo	    (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK);
792188808Sgonzo
793234862Sadrian	ARGEDEBUG(sc, ARGE_DBG_MII, "%s: phy=%d, reg=%02x, value=%04x\n", __func__,
794234862Sadrian	    phy, reg, data);
795199038Sgonzo
796199038Sgonzo	mtx_lock(&miibus_mtx);
797234862Sadrian	ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_ADDR, addr);
798234862Sadrian	ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CONTROL, data);
799188808Sgonzo
800188808Sgonzo	i = ARGE_MII_TIMEOUT;
801234862Sadrian	while ((ARGE_MDIO_READ(sc, AR71XX_MAC_MII_INDICATOR) &
802188808Sgonzo	    MAC_MII_INDICATOR_BUSY) && (i--))
803188808Sgonzo		DELAY(5);
804188808Sgonzo
805199038Sgonzo	mtx_unlock(&miibus_mtx);
806199038Sgonzo
807188808Sgonzo	if (i < 0) {
808220354Sadrian		ARGEDEBUG(sc, ARGE_DBG_MII, "%s timedout\n", __func__);
809188808Sgonzo		/* XXX: return ERRNO istead? */
810188808Sgonzo		return (-1);
811188808Sgonzo	}
812188808Sgonzo
813188808Sgonzo	return (0);
814188808Sgonzo}
815188808Sgonzo
816188808Sgonzostatic void
817188808Sgonzoarge_miibus_statchg(device_t dev)
818188808Sgonzo{
819232627Sray	struct arge_softc	*sc;
820188808Sgonzo
821188808Sgonzo	sc = device_get_softc(dev);
822188808Sgonzo	taskqueue_enqueue(taskqueue_swi, &sc->arge_link_task);
823188808Sgonzo}
824188808Sgonzo
825188808Sgonzostatic void
826188808Sgonzoarge_link_task(void *arg, int pending)
827188808Sgonzo{
828188808Sgonzo	struct arge_softc	*sc;
829232914Sadrian	sc = (struct arge_softc *)arg;
830232914Sadrian
831232914Sadrian	ARGE_LOCK(sc);
832232914Sadrian	arge_update_link_locked(sc);
833232914Sadrian	ARGE_UNLOCK(sc);
834232914Sadrian}
835232914Sadrian
836232914Sadrianstatic void
837232914Sadrianarge_update_link_locked(struct arge_softc *sc)
838232914Sadrian{
839188808Sgonzo	struct mii_data		*mii;
840188808Sgonzo	struct ifnet		*ifp;
841199234Sgonzo	uint32_t		media, duplex;
842188808Sgonzo
843188808Sgonzo	mii = device_get_softc(sc->arge_miibus);
844188808Sgonzo	ifp = sc->arge_ifp;
845188808Sgonzo	if (mii == NULL || ifp == NULL ||
846188808Sgonzo	    (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
847188808Sgonzo		return;
848188808Sgonzo	}
849188808Sgonzo
850188808Sgonzo	if (mii->mii_media_status & IFM_ACTIVE) {
851188808Sgonzo
852188808Sgonzo		media = IFM_SUBTYPE(mii->mii_media_active);
853188808Sgonzo		if (media != IFM_NONE) {
854188808Sgonzo			sc->arge_link_status = 1;
855199234Sgonzo			duplex = mii->mii_media_active & IFM_GMASK;
856234907Sadrian			ARGEDEBUG(sc, ARGE_DBG_MII, "%s: media=%d, duplex=%d\n",
857234907Sadrian			    __func__,
858234907Sadrian			    media,
859234907Sadrian			    duplex);
860199234Sgonzo			arge_set_pll(sc, media, duplex);
861199234Sgonzo		}
862232914Sadrian	} else {
863199234Sgonzo		sc->arge_link_status = 0;
864232914Sadrian	}
865199234Sgonzo}
866192783Sgonzo
867199234Sgonzostatic void
868199234Sgonzoarge_set_pll(struct arge_softc *sc, int media, int duplex)
869199234Sgonzo{
870211511Sadrian	uint32_t		cfg, ifcontrol, rx_filtmask;
871234907Sadrian	uint32_t		fifo_tx, pll;
872211511Sadrian	int if_speed;
873192783Sgonzo
874234919Sadrian	ARGEDEBUG(sc, ARGE_DBG_PLL, "set_pll(%04x, %s)\n", media,
875234862Sadrian	    duplex == IFM_FDX ? "full" : "half");
876199234Sgonzo	cfg = ARGE_READ(sc, AR71XX_MAC_CFG2);
877232627Sray	cfg &= ~(MAC_CFG2_IFACE_MODE_1000
878232627Sray	    | MAC_CFG2_IFACE_MODE_10_100
879199234Sgonzo	    | MAC_CFG2_FULL_DUPLEX);
880188808Sgonzo
881199234Sgonzo	if (duplex == IFM_FDX)
882199234Sgonzo		cfg |= MAC_CFG2_FULL_DUPLEX;
883188808Sgonzo
884199234Sgonzo	ifcontrol = ARGE_READ(sc, AR71XX_MAC_IFCONTROL);
885199234Sgonzo	ifcontrol &= ~MAC_IFCONTROL_SPEED;
886232627Sray	rx_filtmask =
887199234Sgonzo	    ARGE_READ(sc, AR71XX_MAC_FIFO_RX_FILTMASK);
888199234Sgonzo	rx_filtmask &= ~FIFO_RX_MASK_BYTE_MODE;
889188808Sgonzo
890199234Sgonzo	switch(media) {
891199234Sgonzo	case IFM_10_T:
892199234Sgonzo		cfg |= MAC_CFG2_IFACE_MODE_10_100;
893211511Sadrian		if_speed = 10;
894199234Sgonzo		break;
895199234Sgonzo	case IFM_100_TX:
896199234Sgonzo		cfg |= MAC_CFG2_IFACE_MODE_10_100;
897199234Sgonzo		ifcontrol |= MAC_IFCONTROL_SPEED;
898211511Sadrian		if_speed = 100;
899199234Sgonzo		break;
900199234Sgonzo	case IFM_1000_T:
901199234Sgonzo	case IFM_1000_SX:
902199234Sgonzo		cfg |= MAC_CFG2_IFACE_MODE_1000;
903199234Sgonzo		rx_filtmask |= FIFO_RX_MASK_BYTE_MODE;
904211511Sadrian		if_speed = 1000;
905199234Sgonzo		break;
906199234Sgonzo	default:
907211511Sadrian		if_speed = 100;
908232627Sray		device_printf(sc->arge_dev,
909199234Sgonzo		    "Unknown media %d\n", media);
910199234Sgonzo	}
911188808Sgonzo
912234919Sadrian	ARGEDEBUG(sc, ARGE_DBG_PLL, "%s: if_speed=%d\n", __func__, if_speed);
913234907Sadrian
914219589Sadrian	switch (ar71xx_soc) {
915219589Sadrian		case AR71XX_SOC_AR7240:
916219589Sadrian		case AR71XX_SOC_AR7241:
917219589Sadrian		case AR71XX_SOC_AR7242:
918249123Sadrian		case AR71XX_SOC_AR9330:
919249123Sadrian		case AR71XX_SOC_AR9331:
920219589Sadrian			fifo_tx = 0x01f00140;
921219589Sadrian			break;
922219589Sadrian		case AR71XX_SOC_AR9130:
923219589Sadrian		case AR71XX_SOC_AR9132:
924219589Sadrian			fifo_tx = 0x00780fff;
925219589Sadrian			break;
926219589Sadrian		default:
927219589Sadrian			fifo_tx = 0x008001ff;
928219589Sadrian	}
929188808Sgonzo
930199234Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_CFG2, cfg);
931199234Sgonzo	ARGE_WRITE(sc, AR71XX_MAC_IFCONTROL, ifcontrol);
932232627Sray	ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK,
933199234Sgonzo	    rx_filtmask);
934219589Sadrian	ARGE_WRITE(sc, AR71XX_MAC_FIFO_TX_THRESHOLD, fifo_tx);
935188808Sgonzo
936234919Sadrian	/* fetch PLL registers */
937234907Sadrian	pll = ar71xx_device_get_eth_pll(sc->arge_mac_unit, if_speed);
938234919Sadrian	ARGEDEBUG(sc, ARGE_DBG_PLL, "%s: pll=0x%x\n", __func__, pll);
939234907Sadrian
940234919Sadrian	/* Override if required by platform data */
941234919Sadrian	if (if_speed == 10 && sc->arge_pllcfg.pll_10 != 0)
942234919Sadrian		pll = sc->arge_pllcfg.pll_10;
943234919Sadrian	else if (if_speed == 100 && sc->arge_pllcfg.pll_100 != 0)
944234919Sadrian		pll = sc->arge_pllcfg.pll_100;
945234919Sadrian	else if (if_speed == 1000 && sc->arge_pllcfg.pll_1000 != 0)
946234919Sadrian		pll = sc->arge_pllcfg.pll_1000;
947234919Sadrian	ARGEDEBUG(sc, ARGE_DBG_PLL, "%s: final pll=0x%x\n", __func__, pll);
948234919Sadrian
949234907Sadrian	/* XXX ensure pll != 0 */
950234907Sadrian	ar71xx_device_set_pll_ge(sc->arge_mac_unit, if_speed, pll);
951234907Sadrian
952234907Sadrian	/* set MII registers */
953234992Sadrian	/*
954234992Sadrian	 * This was introduced to match what the Linux ag71xx ethernet
955234992Sadrian	 * driver does.  For the AR71xx case, it does set the port
956234992Sadrian	 * MII speed.  However, if this is done, non-gigabit speeds
957234992Sadrian	 * are not at all reliable when speaking via RGMII through
958234992Sadrian	 * 'bridge' PHY port that's pretending to be a local PHY.
959234992Sadrian	 *
960234992Sadrian	 * Until that gets root caused, and until an AR71xx + normal
961234992Sadrian	 * PHY board is tested, leave this disabled.
962234992Sadrian	 */
963234992Sadrian#if 0
964234907Sadrian	ar71xx_device_set_mii_speed(sc->arge_mac_unit, if_speed);
965234992Sadrian#endif
966188808Sgonzo}
967188808Sgonzo
968199234Sgonzo
969188808Sgonzostatic void
970188808Sgonzoarge_reset_dma(struct arge_softc *sc)
971188808Sgonzo{
972188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, 0);
973188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, 0);
974188808Sgonzo
975188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, 0);
976188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, 0);
977188808Sgonzo
978188808Sgonzo	/* Clear all possible RX interrupts */
979192569Sdwhite	while(ARGE_READ(sc, AR71XX_DMA_RX_STATUS) & DMA_RX_STATUS_PKT_RECVD)
980188808Sgonzo		ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD);
981188808Sgonzo
982232627Sray	/*
983188808Sgonzo	 * Clear all possible TX interrupts
984188808Sgonzo	 */
985192569Sdwhite	while(ARGE_READ(sc, AR71XX_DMA_TX_STATUS) & DMA_TX_STATUS_PKT_SENT)
986188808Sgonzo		ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT);
987188808Sgonzo
988232627Sray	/*
989188808Sgonzo	 * Now Rx/Tx errors
990188808Sgonzo	 */
991232627Sray	ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS,
992188808Sgonzo	    DMA_RX_STATUS_BUS_ERROR | DMA_RX_STATUS_OVERFLOW);
993232627Sray	ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS,
994188808Sgonzo	    DMA_TX_STATUS_BUS_ERROR | DMA_TX_STATUS_UNDERRUN);
995232912Sadrian
996232912Sadrian	/*
997232912Sadrian	 * Force a DDR flush so any pending data is properly
998232912Sadrian	 * flushed to RAM before underlying buffers are freed.
999232912Sadrian	 */
1000232912Sadrian	arge_flush_ddr(sc);
1001188808Sgonzo}
1002188808Sgonzo
1003188808Sgonzo
1004188808Sgonzo
1005188808Sgonzostatic void
1006188808Sgonzoarge_init(void *xsc)
1007188808Sgonzo{
1008188808Sgonzo	struct arge_softc	 *sc = xsc;
1009188808Sgonzo
1010188808Sgonzo	ARGE_LOCK(sc);
1011188808Sgonzo	arge_init_locked(sc);
1012188808Sgonzo	ARGE_UNLOCK(sc);
1013188808Sgonzo}
1014188808Sgonzo
1015188808Sgonzostatic void
1016188808Sgonzoarge_init_locked(struct arge_softc *sc)
1017188808Sgonzo{
1018188808Sgonzo	struct ifnet		*ifp = sc->arge_ifp;
1019188808Sgonzo	struct mii_data		*mii;
1020188808Sgonzo
1021188808Sgonzo	ARGE_LOCK_ASSERT(sc);
1022188808Sgonzo
1023255021Sloos	if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
1024255021Sloos		return;
1025188808Sgonzo
1026188808Sgonzo	/* Init circular RX list. */
1027188808Sgonzo	if (arge_rx_ring_init(sc) != 0) {
1028188808Sgonzo		device_printf(sc->arge_dev,
1029188808Sgonzo		    "initialization failed: no memory for rx buffers\n");
1030188808Sgonzo		arge_stop(sc);
1031188808Sgonzo		return;
1032188808Sgonzo	}
1033188808Sgonzo
1034188808Sgonzo	/* Init tx descriptors. */
1035188808Sgonzo	arge_tx_ring_init(sc);
1036188808Sgonzo
1037188808Sgonzo	arge_reset_dma(sc);
1038188808Sgonzo
1039199234Sgonzo	if (sc->arge_miibus) {
1040199234Sgonzo		mii = device_get_softc(sc->arge_miibus);
1041199234Sgonzo		mii_mediachg(mii);
1042199234Sgonzo	}
1043199234Sgonzo	else {
1044199234Sgonzo		/*
1045199234Sgonzo		 * Sun always shines over multiPHY interface
1046199234Sgonzo		 */
1047199234Sgonzo		sc->arge_link_status = 1;
1048199234Sgonzo	}
1049199234Sgonzo
1050188808Sgonzo	ifp->if_drv_flags |= IFF_DRV_RUNNING;
1051188808Sgonzo	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1052188808Sgonzo
1053232914Sadrian	if (sc->arge_miibus) {
1054199234Sgonzo		callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc);
1055232914Sadrian		arge_update_link_locked(sc);
1056232914Sadrian	}
1057192783Sgonzo
1058188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, ARGE_TX_RING_ADDR(sc, 0));
1059188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, ARGE_RX_RING_ADDR(sc, 0));
1060188808Sgonzo
1061188808Sgonzo	/* Start listening */
1062188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, DMA_RX_CONTROL_EN);
1063188808Sgonzo
1064188808Sgonzo	/* Enable interrupts */
1065188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL);
1066188808Sgonzo}
1067188808Sgonzo
1068188808Sgonzo/*
1069209807Sadrian * Return whether the mbuf chain is correctly aligned
1070209807Sadrian * for the arge TX engine.
1071209807Sadrian *
1072209807Sadrian * The TX engine requires each fragment to be aligned to a
1073209807Sadrian * 4 byte boundary and the size of each fragment except
1074209807Sadrian * the last to be a multiple of 4 bytes.
1075209807Sadrian */
1076209807Sadrianstatic int
1077209807Sadrianarge_mbuf_chain_is_tx_aligned(struct mbuf *m0)
1078209807Sadrian{
1079209807Sadrian	struct mbuf *m;
1080209807Sadrian
1081209807Sadrian	for (m = m0; m != NULL; m = m->m_next) {
1082209807Sadrian		if((mtod(m, intptr_t) & 3) != 0)
1083209807Sadrian			return 0;
1084209807Sadrian		if ((m->m_next != NULL) && ((m->m_len & 0x03) != 0))
1085209807Sadrian			return 0;
1086209807Sadrian	}
1087209807Sadrian	return 1;
1088209807Sadrian}
1089209807Sadrian
1090209807Sadrian/*
1091188808Sgonzo * Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
1092188808Sgonzo * pointers to the fragment pointers.
1093188808Sgonzo */
1094188808Sgonzostatic int
1095188808Sgonzoarge_encap(struct arge_softc *sc, struct mbuf **m_head)
1096188808Sgonzo{
1097188808Sgonzo	struct arge_txdesc	*txd;
1098188808Sgonzo	struct arge_desc	*desc, *prev_desc;
1099188808Sgonzo	bus_dma_segment_t	txsegs[ARGE_MAXFRAGS];
1100192569Sdwhite	int			error, i, nsegs, prod, prev_prod;
1101192783Sgonzo	struct mbuf		*m;
1102188808Sgonzo
1103188808Sgonzo	ARGE_LOCK_ASSERT(sc);
1104188808Sgonzo
1105192783Sgonzo	/*
1106192783Sgonzo	 * Fix mbuf chain, all fragments should be 4 bytes aligned and
1107192783Sgonzo	 * even 4 bytes
1108192783Sgonzo	 */
1109192783Sgonzo	m = *m_head;
1110209807Sadrian	if (! arge_mbuf_chain_is_tx_aligned(m)) {
1111209809Sadrian		sc->stats.tx_pkts_unaligned++;
1112243882Sglebius		m = m_defrag(*m_head, M_NOWAIT);
1113192783Sgonzo		if (m == NULL) {
1114192783Sgonzo			*m_head = NULL;
1115192783Sgonzo			return (ENOBUFS);
1116192783Sgonzo		}
1117192783Sgonzo		*m_head = m;
1118209809Sadrian	} else
1119209809Sadrian		sc->stats.tx_pkts_aligned++;
1120192783Sgonzo
1121188808Sgonzo	prod = sc->arge_cdata.arge_tx_prod;
1122188808Sgonzo	txd = &sc->arge_cdata.arge_txdesc[prod];
1123232627Sray	error = bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_tx_tag,
1124188808Sgonzo	    txd->tx_dmamap, *m_head, txsegs, &nsegs, BUS_DMA_NOWAIT);
1125188808Sgonzo
1126188808Sgonzo	if (error == EFBIG) {
1127188808Sgonzo		panic("EFBIG");
1128188808Sgonzo	} else if (error != 0)
1129188808Sgonzo		return (error);
1130188808Sgonzo
1131188808Sgonzo	if (nsegs == 0) {
1132188808Sgonzo		m_freem(*m_head);
1133188808Sgonzo		*m_head = NULL;
1134188808Sgonzo		return (EIO);
1135188808Sgonzo	}
1136188808Sgonzo
1137188808Sgonzo	/* Check number of available descriptors. */
1138188808Sgonzo	if (sc->arge_cdata.arge_tx_cnt + nsegs >= (ARGE_TX_RING_COUNT - 1)) {
1139188808Sgonzo		bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap);
1140188808Sgonzo		return (ENOBUFS);
1141188808Sgonzo	}
1142188808Sgonzo
1143188808Sgonzo	txd->tx_m = *m_head;
1144188808Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap,
1145188808Sgonzo	    BUS_DMASYNC_PREWRITE);
1146188808Sgonzo
1147232627Sray	/*
1148188808Sgonzo	 * Make a list of descriptors for this packet. DMA controller will
1149188808Sgonzo	 * walk through it while arge_link is not zero.
1150188808Sgonzo	 */
1151188808Sgonzo	prev_prod = prod;
1152188808Sgonzo	desc = prev_desc = NULL;
1153188808Sgonzo	for (i = 0; i < nsegs; i++) {
1154188808Sgonzo		desc = &sc->arge_rdata.arge_tx_ring[prod];
1155188808Sgonzo		desc->packet_ctrl = ARGE_DMASIZE(txsegs[i].ds_len);
1156188808Sgonzo
1157192783Sgonzo		if (txsegs[i].ds_addr & 3)
1158192783Sgonzo			panic("TX packet address unaligned\n");
1159192783Sgonzo
1160188808Sgonzo		desc->packet_addr = txsegs[i].ds_addr;
1161232627Sray
1162188808Sgonzo		/* link with previous descriptor */
1163188808Sgonzo		if (prev_desc)
1164188808Sgonzo			prev_desc->packet_ctrl |= ARGE_DESC_MORE;
1165188808Sgonzo
1166188808Sgonzo		sc->arge_cdata.arge_tx_cnt++;
1167188808Sgonzo		prev_desc = desc;
1168188808Sgonzo		ARGE_INC(prod, ARGE_TX_RING_COUNT);
1169188808Sgonzo	}
1170188808Sgonzo
1171188808Sgonzo	/* Update producer index. */
1172188808Sgonzo	sc->arge_cdata.arge_tx_prod = prod;
1173188808Sgonzo
1174188808Sgonzo	/* Sync descriptors. */
1175188808Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
1176188808Sgonzo	    sc->arge_cdata.arge_tx_ring_map,
1177188808Sgonzo	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1178188808Sgonzo
1179188808Sgonzo	/* Start transmitting */
1180232628Sray	ARGEDEBUG(sc, ARGE_DBG_TX, "%s: setting DMA_TX_CONTROL_EN\n",
1181232628Sray	    __func__);
1182188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL, DMA_TX_CONTROL_EN);
1183188808Sgonzo	return (0);
1184188808Sgonzo}
1185188808Sgonzo
1186188808Sgonzostatic void
1187188808Sgonzoarge_start(struct ifnet *ifp)
1188188808Sgonzo{
1189188808Sgonzo	struct arge_softc	 *sc;
1190188808Sgonzo
1191188808Sgonzo	sc = ifp->if_softc;
1192188808Sgonzo
1193188808Sgonzo	ARGE_LOCK(sc);
1194188808Sgonzo	arge_start_locked(ifp);
1195188808Sgonzo	ARGE_UNLOCK(sc);
1196188808Sgonzo}
1197188808Sgonzo
1198188808Sgonzostatic void
1199188808Sgonzoarge_start_locked(struct ifnet *ifp)
1200188808Sgonzo{
1201188808Sgonzo	struct arge_softc	*sc;
1202188808Sgonzo	struct mbuf		*m_head;
1203220356Sadrian	int			enq = 0;
1204188808Sgonzo
1205188808Sgonzo	sc = ifp->if_softc;
1206188808Sgonzo
1207188808Sgonzo	ARGE_LOCK_ASSERT(sc);
1208188808Sgonzo
1209220356Sadrian	ARGEDEBUG(sc, ARGE_DBG_TX, "%s: beginning\n", __func__);
1210220356Sadrian
1211188808Sgonzo	if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
1212188808Sgonzo	    IFF_DRV_RUNNING || sc->arge_link_status == 0 )
1213188808Sgonzo		return;
1214188808Sgonzo
1215220356Sadrian	/*
1216220356Sadrian	 * Before we go any further, check whether we're already full.
1217220356Sadrian	 * The below check errors out immediately if the ring is full
1218220356Sadrian	 * and never gets a chance to set this flag. Although it's
1219220356Sadrian	 * likely never needed, this at least avoids an unexpected
1220220356Sadrian	 * situation.
1221220356Sadrian	 */
1222220356Sadrian	if (sc->arge_cdata.arge_tx_cnt >= ARGE_TX_RING_COUNT - 2) {
1223220356Sadrian		ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1224232628Sray		ARGEDEBUG(sc, ARGE_DBG_ERR,
1225232628Sray		    "%s: tx_cnt %d >= max %d; setting IFF_DRV_OACTIVE\n",
1226232628Sray		    __func__, sc->arge_cdata.arge_tx_cnt,
1227232628Sray		    ARGE_TX_RING_COUNT - 2);
1228220356Sadrian		return;
1229220356Sadrian	}
1230220356Sadrian
1231188808Sgonzo	arge_flush_ddr(sc);
1232188808Sgonzo
1233188808Sgonzo	for (enq = 0; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) &&
1234188808Sgonzo	    sc->arge_cdata.arge_tx_cnt < ARGE_TX_RING_COUNT - 2; ) {
1235188808Sgonzo		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
1236188808Sgonzo		if (m_head == NULL)
1237188808Sgonzo			break;
1238188808Sgonzo
1239188808Sgonzo
1240188808Sgonzo		/*
1241188808Sgonzo		 * Pack the data into the transmit ring.
1242188808Sgonzo		 */
1243188808Sgonzo		if (arge_encap(sc, &m_head)) {
1244188808Sgonzo			if (m_head == NULL)
1245188808Sgonzo				break;
1246188808Sgonzo			IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
1247188808Sgonzo			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
1248188808Sgonzo			break;
1249188808Sgonzo		}
1250188808Sgonzo
1251188808Sgonzo		enq++;
1252188808Sgonzo		/*
1253188808Sgonzo		 * If there's a BPF listener, bounce a copy of this frame
1254188808Sgonzo		 * to him.
1255188808Sgonzo		 */
1256188808Sgonzo		ETHER_BPF_MTAP(ifp, m_head);
1257188808Sgonzo	}
1258232628Sray	ARGEDEBUG(sc, ARGE_DBG_TX, "%s: finished; queued %d packets\n",
1259232628Sray	    __func__, enq);
1260188808Sgonzo}
1261188808Sgonzo
1262188808Sgonzostatic void
1263188808Sgonzoarge_stop(struct arge_softc *sc)
1264188808Sgonzo{
1265188808Sgonzo	struct ifnet	    *ifp;
1266188808Sgonzo
1267188808Sgonzo	ARGE_LOCK_ASSERT(sc);
1268188808Sgonzo
1269188808Sgonzo	ifp = sc->arge_ifp;
1270188808Sgonzo	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
1271199234Sgonzo	if (sc->arge_miibus)
1272199234Sgonzo		callout_stop(&sc->arge_stat_callout);
1273188808Sgonzo
1274188808Sgonzo	/* mask out interrupts */
1275188808Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_INTR, 0);
1276188808Sgonzo
1277188808Sgonzo	arge_reset_dma(sc);
1278232912Sadrian
1279232912Sadrian	/* Flush FIFO and free any existing mbufs */
1280232912Sadrian	arge_flush_ddr(sc);
1281232912Sadrian	arge_rx_ring_free(sc);
1282255300Sloos	arge_tx_ring_free(sc);
1283188808Sgonzo}
1284188808Sgonzo
1285188808Sgonzo
1286188808Sgonzostatic int
1287188808Sgonzoarge_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
1288188808Sgonzo{
1289188808Sgonzo	struct arge_softc		*sc = ifp->if_softc;
1290188808Sgonzo	struct ifreq		*ifr = (struct ifreq *) data;
1291188808Sgonzo	struct mii_data		*mii;
1292188808Sgonzo	int			error;
1293192783Sgonzo#ifdef DEVICE_POLLING
1294192783Sgonzo	int			mask;
1295192783Sgonzo#endif
1296188808Sgonzo
1297188808Sgonzo	switch (command) {
1298188808Sgonzo	case SIOCSIFFLAGS:
1299198932Sgonzo		ARGE_LOCK(sc);
1300198932Sgonzo		if ((ifp->if_flags & IFF_UP) != 0) {
1301198932Sgonzo			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
1302198932Sgonzo				if (((ifp->if_flags ^ sc->arge_if_flags)
1303198939Sgonzo				    & (IFF_PROMISC | IFF_ALLMULTI)) != 0) {
1304198939Sgonzo					/* XXX: handle promisc & multi flags */
1305198939Sgonzo				}
1306232627Sray
1307198932Sgonzo			} else {
1308198932Sgonzo				if (!sc->arge_detach)
1309198932Sgonzo					arge_init_locked(sc);
1310198932Sgonzo			}
1311198932Sgonzo		} else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) {
1312198932Sgonzo			ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
1313198932Sgonzo			arge_stop(sc);
1314198932Sgonzo		}
1315198932Sgonzo		sc->arge_if_flags = ifp->if_flags;
1316198932Sgonzo		ARGE_UNLOCK(sc);
1317188808Sgonzo		error = 0;
1318188808Sgonzo		break;
1319188808Sgonzo	case SIOCADDMULTI:
1320188808Sgonzo	case SIOCDELMULTI:
1321198932Sgonzo		/* XXX: implement SIOCDELMULTI */
1322188808Sgonzo		error = 0;
1323188808Sgonzo		break;
1324188808Sgonzo	case SIOCGIFMEDIA:
1325188808Sgonzo	case SIOCSIFMEDIA:
1326199234Sgonzo		if (sc->arge_miibus) {
1327199234Sgonzo			mii = device_get_softc(sc->arge_miibus);
1328232628Sray			error = ifmedia_ioctl(ifp, ifr, &mii->mii_media,
1329232628Sray			    command);
1330199234Sgonzo		}
1331232627Sray		else
1332232628Sray			error = ifmedia_ioctl(ifp, ifr, &sc->arge_ifmedia,
1333232628Sray			    command);
1334188808Sgonzo		break;
1335198933Sgonzo	case SIOCSIFCAP:
1336198932Sgonzo		/* XXX: Check other capabilities */
1337192783Sgonzo#ifdef DEVICE_POLLING
1338198933Sgonzo		mask = ifp->if_capenable ^ ifr->ifr_reqcap;
1339198933Sgonzo		if (mask & IFCAP_POLLING) {
1340198933Sgonzo			if (ifr->ifr_reqcap & IFCAP_POLLING) {
1341192783Sgonzo				ARGE_WRITE(sc, AR71XX_DMA_INTR, 0);
1342198933Sgonzo				error = ether_poll_register(arge_poll, ifp);
1343198933Sgonzo				if (error)
1344198933Sgonzo					return error;
1345198933Sgonzo				ARGE_LOCK(sc);
1346198933Sgonzo				ifp->if_capenable |= IFCAP_POLLING;
1347198933Sgonzo				ARGE_UNLOCK(sc);
1348198933Sgonzo			} else {
1349192783Sgonzo				ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL);
1350198933Sgonzo				error = ether_poll_deregister(ifp);
1351198933Sgonzo				ARGE_LOCK(sc);
1352198933Sgonzo				ifp->if_capenable &= ~IFCAP_POLLING;
1353198933Sgonzo				ARGE_UNLOCK(sc);
1354198933Sgonzo			}
1355198933Sgonzo		}
1356198932Sgonzo		error = 0;
1357198933Sgonzo		break;
1358192783Sgonzo#endif
1359188808Sgonzo	default:
1360188808Sgonzo		error = ether_ioctl(ifp, command, data);
1361188808Sgonzo		break;
1362188808Sgonzo	}
1363188808Sgonzo
1364188808Sgonzo	return (error);
1365188808Sgonzo}
1366188808Sgonzo
1367188808Sgonzo/*
1368188808Sgonzo * Set media options.
1369188808Sgonzo */
1370188808Sgonzostatic int
1371188808Sgonzoarge_ifmedia_upd(struct ifnet *ifp)
1372188808Sgonzo{
1373188808Sgonzo	struct arge_softc		*sc;
1374188808Sgonzo	struct mii_data		*mii;
1375188808Sgonzo	struct mii_softc	*miisc;
1376188808Sgonzo	int			error;
1377188808Sgonzo
1378188808Sgonzo	sc = ifp->if_softc;
1379188808Sgonzo	ARGE_LOCK(sc);
1380188808Sgonzo	mii = device_get_softc(sc->arge_miibus);
1381221407Smarius	LIST_FOREACH(miisc, &mii->mii_phys, mii_list)
1382221407Smarius		PHY_RESET(miisc);
1383188808Sgonzo	error = mii_mediachg(mii);
1384188808Sgonzo	ARGE_UNLOCK(sc);
1385188808Sgonzo
1386188808Sgonzo	return (error);
1387188808Sgonzo}
1388188808Sgonzo
1389188808Sgonzo/*
1390188808Sgonzo * Report current media status.
1391188808Sgonzo */
1392188808Sgonzostatic void
1393188808Sgonzoarge_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
1394188808Sgonzo{
1395188808Sgonzo	struct arge_softc		*sc = ifp->if_softc;
1396188808Sgonzo	struct mii_data		*mii;
1397188808Sgonzo
1398188808Sgonzo	mii = device_get_softc(sc->arge_miibus);
1399188808Sgonzo	ARGE_LOCK(sc);
1400188808Sgonzo	mii_pollstat(mii);
1401188808Sgonzo	ifmr->ifm_active = mii->mii_media_active;
1402188808Sgonzo	ifmr->ifm_status = mii->mii_media_status;
1403226478Syongari	ARGE_UNLOCK(sc);
1404188808Sgonzo}
1405188808Sgonzo
1406188808Sgonzostruct arge_dmamap_arg {
1407188808Sgonzo	bus_addr_t	arge_busaddr;
1408188808Sgonzo};
1409188808Sgonzo
1410188808Sgonzostatic void
1411188808Sgonzoarge_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
1412188808Sgonzo{
1413188808Sgonzo	struct arge_dmamap_arg	*ctx;
1414188808Sgonzo
1415188808Sgonzo	if (error != 0)
1416188808Sgonzo		return;
1417188808Sgonzo	ctx = arg;
1418188808Sgonzo	ctx->arge_busaddr = segs[0].ds_addr;
1419188808Sgonzo}
1420188808Sgonzo
1421188808Sgonzostatic int
1422188808Sgonzoarge_dma_alloc(struct arge_softc *sc)
1423188808Sgonzo{
1424188808Sgonzo	struct arge_dmamap_arg	ctx;
1425188808Sgonzo	struct arge_txdesc	*txd;
1426188808Sgonzo	struct arge_rxdesc	*rxd;
1427188808Sgonzo	int			error, i;
1428188808Sgonzo
1429188808Sgonzo	/* Create parent DMA tag. */
1430188808Sgonzo	error = bus_dma_tag_create(
1431188808Sgonzo	    bus_get_dma_tag(sc->arge_dev),	/* parent */
1432188808Sgonzo	    1, 0,			/* alignment, boundary */
1433188808Sgonzo	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
1434188808Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
1435188808Sgonzo	    NULL, NULL,			/* filter, filterarg */
1436188808Sgonzo	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsize */
1437188808Sgonzo	    0,				/* nsegments */
1438188808Sgonzo	    BUS_SPACE_MAXSIZE_32BIT,	/* maxsegsize */
1439188808Sgonzo	    0,				/* flags */
1440188808Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
1441188808Sgonzo	    &sc->arge_cdata.arge_parent_tag);
1442188808Sgonzo	if (error != 0) {
1443232628Sray		device_printf(sc->arge_dev,
1444232628Sray		    "failed to create parent DMA tag\n");
1445188808Sgonzo		goto fail;
1446188808Sgonzo	}
1447188808Sgonzo	/* Create tag for Tx ring. */
1448188808Sgonzo	error = bus_dma_tag_create(
1449188808Sgonzo	    sc->arge_cdata.arge_parent_tag,	/* parent */
1450188808Sgonzo	    ARGE_RING_ALIGN, 0,		/* alignment, boundary */
1451188808Sgonzo	    BUS_SPACE_MAXADDR,		/* lowaddr */
1452188808Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
1453188808Sgonzo	    NULL, NULL,			/* filter, filterarg */
1454188808Sgonzo	    ARGE_TX_DMA_SIZE,		/* maxsize */
1455188808Sgonzo	    1,				/* nsegments */
1456188808Sgonzo	    ARGE_TX_DMA_SIZE,		/* maxsegsize */
1457188808Sgonzo	    0,				/* flags */
1458188808Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
1459188808Sgonzo	    &sc->arge_cdata.arge_tx_ring_tag);
1460188808Sgonzo	if (error != 0) {
1461232628Sray		device_printf(sc->arge_dev,
1462232628Sray		    "failed to create Tx ring DMA tag\n");
1463188808Sgonzo		goto fail;
1464188808Sgonzo	}
1465188808Sgonzo
1466188808Sgonzo	/* Create tag for Rx ring. */
1467188808Sgonzo	error = bus_dma_tag_create(
1468188808Sgonzo	    sc->arge_cdata.arge_parent_tag,	/* parent */
1469188808Sgonzo	    ARGE_RING_ALIGN, 0,		/* alignment, boundary */
1470188808Sgonzo	    BUS_SPACE_MAXADDR,		/* lowaddr */
1471188808Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
1472188808Sgonzo	    NULL, NULL,			/* filter, filterarg */
1473188808Sgonzo	    ARGE_RX_DMA_SIZE,		/* maxsize */
1474188808Sgonzo	    1,				/* nsegments */
1475188808Sgonzo	    ARGE_RX_DMA_SIZE,		/* maxsegsize */
1476188808Sgonzo	    0,				/* flags */
1477188808Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
1478188808Sgonzo	    &sc->arge_cdata.arge_rx_ring_tag);
1479188808Sgonzo	if (error != 0) {
1480232628Sray		device_printf(sc->arge_dev,
1481232628Sray		    "failed to create Rx ring DMA tag\n");
1482188808Sgonzo		goto fail;
1483188808Sgonzo	}
1484188808Sgonzo
1485188808Sgonzo	/* Create tag for Tx buffers. */
1486188808Sgonzo	error = bus_dma_tag_create(
1487188808Sgonzo	    sc->arge_cdata.arge_parent_tag,	/* parent */
1488188808Sgonzo	    sizeof(uint32_t), 0,	/* alignment, boundary */
1489188808Sgonzo	    BUS_SPACE_MAXADDR,		/* lowaddr */
1490188808Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
1491188808Sgonzo	    NULL, NULL,			/* filter, filterarg */
1492188808Sgonzo	    MCLBYTES * ARGE_MAXFRAGS,	/* maxsize */
1493188808Sgonzo	    ARGE_MAXFRAGS,		/* nsegments */
1494188808Sgonzo	    MCLBYTES,			/* maxsegsize */
1495188808Sgonzo	    0,				/* flags */
1496188808Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
1497188808Sgonzo	    &sc->arge_cdata.arge_tx_tag);
1498188808Sgonzo	if (error != 0) {
1499188808Sgonzo		device_printf(sc->arge_dev, "failed to create Tx DMA tag\n");
1500188808Sgonzo		goto fail;
1501188808Sgonzo	}
1502188808Sgonzo
1503188808Sgonzo	/* Create tag for Rx buffers. */
1504188808Sgonzo	error = bus_dma_tag_create(
1505188808Sgonzo	    sc->arge_cdata.arge_parent_tag,	/* parent */
1506188808Sgonzo	    ARGE_RX_ALIGN, 0,		/* alignment, boundary */
1507188808Sgonzo	    BUS_SPACE_MAXADDR,		/* lowaddr */
1508188808Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
1509188808Sgonzo	    NULL, NULL,			/* filter, filterarg */
1510188808Sgonzo	    MCLBYTES,			/* maxsize */
1511192821Sgonzo	    ARGE_MAXFRAGS,		/* nsegments */
1512188808Sgonzo	    MCLBYTES,			/* maxsegsize */
1513188808Sgonzo	    0,				/* flags */
1514188808Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
1515188808Sgonzo	    &sc->arge_cdata.arge_rx_tag);
1516188808Sgonzo	if (error != 0) {
1517188808Sgonzo		device_printf(sc->arge_dev, "failed to create Rx DMA tag\n");
1518188808Sgonzo		goto fail;
1519188808Sgonzo	}
1520188808Sgonzo
1521188808Sgonzo	/* Allocate DMA'able memory and load the DMA map for Tx ring. */
1522188808Sgonzo	error = bus_dmamem_alloc(sc->arge_cdata.arge_tx_ring_tag,
1523188808Sgonzo	    (void **)&sc->arge_rdata.arge_tx_ring, BUS_DMA_WAITOK |
1524232628Sray	    BUS_DMA_COHERENT | BUS_DMA_ZERO,
1525232628Sray	    &sc->arge_cdata.arge_tx_ring_map);
1526188808Sgonzo	if (error != 0) {
1527188808Sgonzo		device_printf(sc->arge_dev,
1528188808Sgonzo		    "failed to allocate DMA'able memory for Tx ring\n");
1529188808Sgonzo		goto fail;
1530188808Sgonzo	}
1531188808Sgonzo
1532188808Sgonzo	ctx.arge_busaddr = 0;
1533188808Sgonzo	error = bus_dmamap_load(sc->arge_cdata.arge_tx_ring_tag,
1534188808Sgonzo	    sc->arge_cdata.arge_tx_ring_map, sc->arge_rdata.arge_tx_ring,
1535188808Sgonzo	    ARGE_TX_DMA_SIZE, arge_dmamap_cb, &ctx, 0);
1536188808Sgonzo	if (error != 0 || ctx.arge_busaddr == 0) {
1537188808Sgonzo		device_printf(sc->arge_dev,
1538188808Sgonzo		    "failed to load DMA'able memory for Tx ring\n");
1539188808Sgonzo		goto fail;
1540188808Sgonzo	}
1541188808Sgonzo	sc->arge_rdata.arge_tx_ring_paddr = ctx.arge_busaddr;
1542188808Sgonzo
1543188808Sgonzo	/* Allocate DMA'able memory and load the DMA map for Rx ring. */
1544188808Sgonzo	error = bus_dmamem_alloc(sc->arge_cdata.arge_rx_ring_tag,
1545188808Sgonzo	    (void **)&sc->arge_rdata.arge_rx_ring, BUS_DMA_WAITOK |
1546232628Sray	    BUS_DMA_COHERENT | BUS_DMA_ZERO,
1547232628Sray	    &sc->arge_cdata.arge_rx_ring_map);
1548188808Sgonzo	if (error != 0) {
1549188808Sgonzo		device_printf(sc->arge_dev,
1550188808Sgonzo		    "failed to allocate DMA'able memory for Rx ring\n");
1551188808Sgonzo		goto fail;
1552188808Sgonzo	}
1553188808Sgonzo
1554188808Sgonzo	ctx.arge_busaddr = 0;
1555188808Sgonzo	error = bus_dmamap_load(sc->arge_cdata.arge_rx_ring_tag,
1556188808Sgonzo	    sc->arge_cdata.arge_rx_ring_map, sc->arge_rdata.arge_rx_ring,
1557188808Sgonzo	    ARGE_RX_DMA_SIZE, arge_dmamap_cb, &ctx, 0);
1558188808Sgonzo	if (error != 0 || ctx.arge_busaddr == 0) {
1559188808Sgonzo		device_printf(sc->arge_dev,
1560188808Sgonzo		    "failed to load DMA'able memory for Rx ring\n");
1561188808Sgonzo		goto fail;
1562188808Sgonzo	}
1563188808Sgonzo	sc->arge_rdata.arge_rx_ring_paddr = ctx.arge_busaddr;
1564188808Sgonzo
1565188808Sgonzo	/* Create DMA maps for Tx buffers. */
1566188808Sgonzo	for (i = 0; i < ARGE_TX_RING_COUNT; i++) {
1567188808Sgonzo		txd = &sc->arge_cdata.arge_txdesc[i];
1568188808Sgonzo		txd->tx_m = NULL;
1569188808Sgonzo		txd->tx_dmamap = NULL;
1570188808Sgonzo		error = bus_dmamap_create(sc->arge_cdata.arge_tx_tag, 0,
1571188808Sgonzo		    &txd->tx_dmamap);
1572188808Sgonzo		if (error != 0) {
1573188808Sgonzo			device_printf(sc->arge_dev,
1574188808Sgonzo			    "failed to create Tx dmamap\n");
1575188808Sgonzo			goto fail;
1576188808Sgonzo		}
1577188808Sgonzo	}
1578188808Sgonzo	/* Create DMA maps for Rx buffers. */
1579188808Sgonzo	if ((error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0,
1580188808Sgonzo	    &sc->arge_cdata.arge_rx_sparemap)) != 0) {
1581188808Sgonzo		device_printf(sc->arge_dev,
1582188808Sgonzo		    "failed to create spare Rx dmamap\n");
1583188808Sgonzo		goto fail;
1584188808Sgonzo	}
1585188808Sgonzo	for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
1586188808Sgonzo		rxd = &sc->arge_cdata.arge_rxdesc[i];
1587188808Sgonzo		rxd->rx_m = NULL;
1588188808Sgonzo		rxd->rx_dmamap = NULL;
1589188808Sgonzo		error = bus_dmamap_create(sc->arge_cdata.arge_rx_tag, 0,
1590188808Sgonzo		    &rxd->rx_dmamap);
1591188808Sgonzo		if (error != 0) {
1592188808Sgonzo			device_printf(sc->arge_dev,
1593188808Sgonzo			    "failed to create Rx dmamap\n");
1594188808Sgonzo			goto fail;
1595188808Sgonzo		}
1596188808Sgonzo	}
1597188808Sgonzo
1598188808Sgonzofail:
1599188808Sgonzo	return (error);
1600188808Sgonzo}
1601188808Sgonzo
1602188808Sgonzostatic void
1603188808Sgonzoarge_dma_free(struct arge_softc *sc)
1604188808Sgonzo{
1605188808Sgonzo	struct arge_txdesc	*txd;
1606188808Sgonzo	struct arge_rxdesc	*rxd;
1607188808Sgonzo	int			i;
1608188808Sgonzo
1609188808Sgonzo	/* Tx ring. */
1610188808Sgonzo	if (sc->arge_cdata.arge_tx_ring_tag) {
1611188808Sgonzo		if (sc->arge_cdata.arge_tx_ring_map)
1612188808Sgonzo			bus_dmamap_unload(sc->arge_cdata.arge_tx_ring_tag,
1613188808Sgonzo			    sc->arge_cdata.arge_tx_ring_map);
1614188808Sgonzo		if (sc->arge_cdata.arge_tx_ring_map &&
1615188808Sgonzo		    sc->arge_rdata.arge_tx_ring)
1616188808Sgonzo			bus_dmamem_free(sc->arge_cdata.arge_tx_ring_tag,
1617188808Sgonzo			    sc->arge_rdata.arge_tx_ring,
1618188808Sgonzo			    sc->arge_cdata.arge_tx_ring_map);
1619188808Sgonzo		sc->arge_rdata.arge_tx_ring = NULL;
1620188808Sgonzo		sc->arge_cdata.arge_tx_ring_map = NULL;
1621188808Sgonzo		bus_dma_tag_destroy(sc->arge_cdata.arge_tx_ring_tag);
1622188808Sgonzo		sc->arge_cdata.arge_tx_ring_tag = NULL;
1623188808Sgonzo	}
1624188808Sgonzo	/* Rx ring. */
1625188808Sgonzo	if (sc->arge_cdata.arge_rx_ring_tag) {
1626188808Sgonzo		if (sc->arge_cdata.arge_rx_ring_map)
1627188808Sgonzo			bus_dmamap_unload(sc->arge_cdata.arge_rx_ring_tag,
1628188808Sgonzo			    sc->arge_cdata.arge_rx_ring_map);
1629188808Sgonzo		if (sc->arge_cdata.arge_rx_ring_map &&
1630188808Sgonzo		    sc->arge_rdata.arge_rx_ring)
1631188808Sgonzo			bus_dmamem_free(sc->arge_cdata.arge_rx_ring_tag,
1632188808Sgonzo			    sc->arge_rdata.arge_rx_ring,
1633188808Sgonzo			    sc->arge_cdata.arge_rx_ring_map);
1634188808Sgonzo		sc->arge_rdata.arge_rx_ring = NULL;
1635188808Sgonzo		sc->arge_cdata.arge_rx_ring_map = NULL;
1636188808Sgonzo		bus_dma_tag_destroy(sc->arge_cdata.arge_rx_ring_tag);
1637188808Sgonzo		sc->arge_cdata.arge_rx_ring_tag = NULL;
1638188808Sgonzo	}
1639188808Sgonzo	/* Tx buffers. */
1640188808Sgonzo	if (sc->arge_cdata.arge_tx_tag) {
1641188808Sgonzo		for (i = 0; i < ARGE_TX_RING_COUNT; i++) {
1642188808Sgonzo			txd = &sc->arge_cdata.arge_txdesc[i];
1643188808Sgonzo			if (txd->tx_dmamap) {
1644188808Sgonzo				bus_dmamap_destroy(sc->arge_cdata.arge_tx_tag,
1645188808Sgonzo				    txd->tx_dmamap);
1646188808Sgonzo				txd->tx_dmamap = NULL;
1647188808Sgonzo			}
1648188808Sgonzo		}
1649188808Sgonzo		bus_dma_tag_destroy(sc->arge_cdata.arge_tx_tag);
1650188808Sgonzo		sc->arge_cdata.arge_tx_tag = NULL;
1651188808Sgonzo	}
1652188808Sgonzo	/* Rx buffers. */
1653188808Sgonzo	if (sc->arge_cdata.arge_rx_tag) {
1654188808Sgonzo		for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
1655188808Sgonzo			rxd = &sc->arge_cdata.arge_rxdesc[i];
1656188808Sgonzo			if (rxd->rx_dmamap) {
1657188808Sgonzo				bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag,
1658188808Sgonzo				    rxd->rx_dmamap);
1659188808Sgonzo				rxd->rx_dmamap = NULL;
1660188808Sgonzo			}
1661188808Sgonzo		}
1662188808Sgonzo		if (sc->arge_cdata.arge_rx_sparemap) {
1663188808Sgonzo			bus_dmamap_destroy(sc->arge_cdata.arge_rx_tag,
1664188808Sgonzo			    sc->arge_cdata.arge_rx_sparemap);
1665188808Sgonzo			sc->arge_cdata.arge_rx_sparemap = 0;
1666188808Sgonzo		}
1667188808Sgonzo		bus_dma_tag_destroy(sc->arge_cdata.arge_rx_tag);
1668188808Sgonzo		sc->arge_cdata.arge_rx_tag = NULL;
1669188808Sgonzo	}
1670188808Sgonzo
1671188808Sgonzo	if (sc->arge_cdata.arge_parent_tag) {
1672188808Sgonzo		bus_dma_tag_destroy(sc->arge_cdata.arge_parent_tag);
1673188808Sgonzo		sc->arge_cdata.arge_parent_tag = NULL;
1674188808Sgonzo	}
1675188808Sgonzo}
1676188808Sgonzo
1677188808Sgonzo/*
1678188808Sgonzo * Initialize the transmit descriptors.
1679188808Sgonzo */
1680188808Sgonzostatic int
1681188808Sgonzoarge_tx_ring_init(struct arge_softc *sc)
1682188808Sgonzo{
1683188808Sgonzo	struct arge_ring_data	*rd;
1684188808Sgonzo	struct arge_txdesc	*txd;
1685188808Sgonzo	bus_addr_t		addr;
1686188808Sgonzo	int			i;
1687188808Sgonzo
1688188808Sgonzo	sc->arge_cdata.arge_tx_prod = 0;
1689188808Sgonzo	sc->arge_cdata.arge_tx_cons = 0;
1690188808Sgonzo	sc->arge_cdata.arge_tx_cnt = 0;
1691188808Sgonzo
1692188808Sgonzo	rd = &sc->arge_rdata;
1693188808Sgonzo	bzero(rd->arge_tx_ring, sizeof(rd->arge_tx_ring));
1694188808Sgonzo	for (i = 0; i < ARGE_TX_RING_COUNT; i++) {
1695188808Sgonzo		if (i == ARGE_TX_RING_COUNT - 1)
1696188808Sgonzo			addr = ARGE_TX_RING_ADDR(sc, 0);
1697188808Sgonzo		else
1698188808Sgonzo			addr = ARGE_TX_RING_ADDR(sc, i + 1);
1699188808Sgonzo		rd->arge_tx_ring[i].packet_ctrl = ARGE_DESC_EMPTY;
1700188808Sgonzo		rd->arge_tx_ring[i].next_desc = addr;
1701188808Sgonzo		txd = &sc->arge_cdata.arge_txdesc[i];
1702188808Sgonzo		txd->tx_m = NULL;
1703188808Sgonzo	}
1704188808Sgonzo
1705188808Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
1706188808Sgonzo	    sc->arge_cdata.arge_tx_ring_map,
1707188808Sgonzo	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
1708188808Sgonzo
1709188808Sgonzo	return (0);
1710188808Sgonzo}
1711188808Sgonzo
1712188808Sgonzo/*
1713255300Sloos * Free the Tx ring, unload any pending dma transaction and free the mbuf.
1714255300Sloos */
1715255300Sloosstatic void
1716255300Sloosarge_tx_ring_free(struct arge_softc *sc)
1717255300Sloos{
1718255300Sloos	struct arge_txdesc	*txd;
1719255300Sloos	int			i;
1720255300Sloos
1721255300Sloos	/* Free the Tx buffers. */
1722255300Sloos	for (i = 0; i < ARGE_TX_RING_COUNT; i++) {
1723255300Sloos		txd = &sc->arge_cdata.arge_txdesc[i];
1724255300Sloos		if (txd->tx_dmamap) {
1725255300Sloos			bus_dmamap_sync(sc->arge_cdata.arge_tx_tag,
1726255300Sloos			    txd->tx_dmamap, BUS_DMASYNC_POSTWRITE);
1727255300Sloos			bus_dmamap_unload(sc->arge_cdata.arge_tx_tag,
1728255300Sloos			    txd->tx_dmamap);
1729255300Sloos		}
1730255300Sloos		if (txd->tx_m)
1731255300Sloos			m_freem(txd->tx_m);
1732255300Sloos		txd->tx_m = NULL;
1733255300Sloos	}
1734255300Sloos}
1735255300Sloos
1736255300Sloos/*
1737188808Sgonzo * Initialize the RX descriptors and allocate mbufs for them. Note that
1738188808Sgonzo * we arrange the descriptors in a closed ring, so that the last descriptor
1739188808Sgonzo * points back to the first.
1740188808Sgonzo */
1741188808Sgonzostatic int
1742188808Sgonzoarge_rx_ring_init(struct arge_softc *sc)
1743188808Sgonzo{
1744188808Sgonzo	struct arge_ring_data	*rd;
1745188808Sgonzo	struct arge_rxdesc	*rxd;
1746188808Sgonzo	bus_addr_t		addr;
1747188808Sgonzo	int			i;
1748188808Sgonzo
1749188808Sgonzo	sc->arge_cdata.arge_rx_cons = 0;
1750188808Sgonzo
1751188808Sgonzo	rd = &sc->arge_rdata;
1752188808Sgonzo	bzero(rd->arge_rx_ring, sizeof(rd->arge_rx_ring));
1753188808Sgonzo	for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
1754188808Sgonzo		rxd = &sc->arge_cdata.arge_rxdesc[i];
1755232912Sadrian		if (rxd->rx_m != NULL) {
1756232912Sadrian			device_printf(sc->arge_dev,
1757232912Sadrian			    "%s: ring[%d] rx_m wasn't free?\n",
1758232912Sadrian			    __func__,
1759232912Sadrian			    i);
1760232912Sadrian		}
1761188808Sgonzo		rxd->rx_m = NULL;
1762188808Sgonzo		rxd->desc = &rd->arge_rx_ring[i];
1763188808Sgonzo		if (i == ARGE_RX_RING_COUNT - 1)
1764188808Sgonzo			addr = ARGE_RX_RING_ADDR(sc, 0);
1765188808Sgonzo		else
1766188808Sgonzo			addr = ARGE_RX_RING_ADDR(sc, i + 1);
1767188808Sgonzo		rd->arge_rx_ring[i].next_desc = addr;
1768192783Sgonzo		if (arge_newbuf(sc, i) != 0) {
1769188808Sgonzo			return (ENOBUFS);
1770192783Sgonzo		}
1771188808Sgonzo	}
1772188808Sgonzo
1773188808Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
1774188808Sgonzo	    sc->arge_cdata.arge_rx_ring_map,
1775195434Sgonzo	    BUS_DMASYNC_PREWRITE);
1776188808Sgonzo
1777188808Sgonzo	return (0);
1778188808Sgonzo}
1779188808Sgonzo
1780188808Sgonzo/*
1781232912Sadrian * Free all the buffers in the RX ring.
1782232912Sadrian *
1783232912Sadrian * TODO: ensure that DMA is disabled and no pending DMA
1784232912Sadrian * is lurking in the FIFO.
1785232912Sadrian */
1786232912Sadrianstatic void
1787232912Sadrianarge_rx_ring_free(struct arge_softc *sc)
1788232912Sadrian{
1789232912Sadrian	int i;
1790232912Sadrian	struct arge_rxdesc	*rxd;
1791232912Sadrian
1792232912Sadrian	ARGE_LOCK_ASSERT(sc);
1793232912Sadrian
1794232912Sadrian	for (i = 0; i < ARGE_RX_RING_COUNT; i++) {
1795232912Sadrian		rxd = &sc->arge_cdata.arge_rxdesc[i];
1796232912Sadrian		/* Unmap the mbuf */
1797232912Sadrian		if (rxd->rx_m != NULL) {
1798232912Sadrian			bus_dmamap_unload(sc->arge_cdata.arge_rx_tag,
1799232912Sadrian			    rxd->rx_dmamap);
1800232912Sadrian			m_free(rxd->rx_m);
1801232912Sadrian			rxd->rx_m = NULL;
1802232912Sadrian		}
1803232912Sadrian	}
1804232912Sadrian}
1805232912Sadrian
1806232912Sadrian/*
1807188808Sgonzo * Initialize an RX descriptor and attach an MBUF cluster.
1808188808Sgonzo */
1809188808Sgonzostatic int
1810188808Sgonzoarge_newbuf(struct arge_softc *sc, int idx)
1811188808Sgonzo{
1812188808Sgonzo	struct arge_desc		*desc;
1813188808Sgonzo	struct arge_rxdesc	*rxd;
1814188808Sgonzo	struct mbuf		*m;
1815188808Sgonzo	bus_dma_segment_t	segs[1];
1816188808Sgonzo	bus_dmamap_t		map;
1817188808Sgonzo	int			nsegs;
1818188808Sgonzo
1819243882Sglebius	m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
1820188808Sgonzo	if (m == NULL)
1821188808Sgonzo		return (ENOBUFS);
1822188808Sgonzo	m->m_len = m->m_pkthdr.len = MCLBYTES;
1823188808Sgonzo	m_adj(m, sizeof(uint64_t));
1824188808Sgonzo
1825188808Sgonzo	if (bus_dmamap_load_mbuf_sg(sc->arge_cdata.arge_rx_tag,
1826188808Sgonzo	    sc->arge_cdata.arge_rx_sparemap, m, segs, &nsegs, 0) != 0) {
1827188808Sgonzo		m_freem(m);
1828188808Sgonzo		return (ENOBUFS);
1829188808Sgonzo	}
1830188808Sgonzo	KASSERT(nsegs == 1, ("%s: %d segments returned!", __func__, nsegs));
1831188808Sgonzo
1832188808Sgonzo	rxd = &sc->arge_cdata.arge_rxdesc[idx];
1833188808Sgonzo	if (rxd->rx_m != NULL) {
1834188808Sgonzo		bus_dmamap_unload(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap);
1835188808Sgonzo	}
1836188808Sgonzo	map = rxd->rx_dmamap;
1837188808Sgonzo	rxd->rx_dmamap = sc->arge_cdata.arge_rx_sparemap;
1838188808Sgonzo	sc->arge_cdata.arge_rx_sparemap = map;
1839188808Sgonzo	rxd->rx_m = m;
1840188808Sgonzo	desc = rxd->desc;
1841192783Sgonzo	if (segs[0].ds_addr & 3)
1842192783Sgonzo		panic("RX packet address unaligned");
1843188808Sgonzo	desc->packet_addr = segs[0].ds_addr;
1844192783Sgonzo	desc->packet_ctrl = ARGE_DESC_EMPTY | ARGE_DMASIZE(segs[0].ds_len);
1845188808Sgonzo
1846195434Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
1847195434Sgonzo	    sc->arge_cdata.arge_rx_ring_map,
1848195434Sgonzo	    BUS_DMASYNC_PREWRITE);
1849195434Sgonzo
1850188808Sgonzo	return (0);
1851188808Sgonzo}
1852188808Sgonzo
1853188808Sgonzostatic __inline void
1854188808Sgonzoarge_fixup_rx(struct mbuf *m)
1855188808Sgonzo{
1856198933Sgonzo	int		i;
1857198933Sgonzo	uint16_t	*src, *dst;
1858188808Sgonzo
1859188808Sgonzo	src = mtod(m, uint16_t *);
1860188808Sgonzo	dst = src - 1;
1861188808Sgonzo
1862195434Sgonzo	for (i = 0; i < m->m_len / sizeof(uint16_t); i++) {
1863188808Sgonzo		*dst++ = *src++;
1864195434Sgonzo	}
1865188808Sgonzo
1866195434Sgonzo	if (m->m_len % sizeof(uint16_t))
1867195434Sgonzo		*(uint8_t *)dst = *(uint8_t *)src;
1868195434Sgonzo
1869188808Sgonzo	m->m_data -= ETHER_ALIGN;
1870188808Sgonzo}
1871188808Sgonzo
1872192783Sgonzo#ifdef DEVICE_POLLING
1873198667Sgonzostatic int
1874192783Sgonzoarge_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
1875192783Sgonzo{
1876192783Sgonzo	struct arge_softc *sc = ifp->if_softc;
1877198667Sgonzo	int rx_npkts = 0;
1878188808Sgonzo
1879198933Sgonzo	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
1880192783Sgonzo		ARGE_LOCK(sc);
1881192783Sgonzo		arge_tx_locked(sc);
1882198667Sgonzo		rx_npkts = arge_rx_locked(sc);
1883192783Sgonzo		ARGE_UNLOCK(sc);
1884198933Sgonzo	}
1885198667Sgonzo
1886198667Sgonzo	return (rx_npkts);
1887192783Sgonzo}
1888192783Sgonzo#endif /* DEVICE_POLLING */
1889192783Sgonzo
1890192783Sgonzo
1891188808Sgonzostatic void
1892188808Sgonzoarge_tx_locked(struct arge_softc *sc)
1893188808Sgonzo{
1894188808Sgonzo	struct arge_txdesc	*txd;
1895188808Sgonzo	struct arge_desc	*cur_tx;
1896188808Sgonzo	struct ifnet		*ifp;
1897188808Sgonzo	uint32_t		ctrl;
1898188808Sgonzo	int			cons, prod;
1899188808Sgonzo
1900188808Sgonzo	ARGE_LOCK_ASSERT(sc);
1901188808Sgonzo
1902188808Sgonzo	cons = sc->arge_cdata.arge_tx_cons;
1903188808Sgonzo	prod = sc->arge_cdata.arge_tx_prod;
1904220356Sadrian
1905232628Sray	ARGEDEBUG(sc, ARGE_DBG_TX, "%s: cons=%d, prod=%d\n", __func__, cons,
1906232628Sray	    prod);
1907220356Sadrian
1908188808Sgonzo	if (cons == prod)
1909188808Sgonzo		return;
1910188808Sgonzo
1911188808Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
1912188808Sgonzo	    sc->arge_cdata.arge_tx_ring_map,
1913188808Sgonzo	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1914188808Sgonzo
1915188808Sgonzo	ifp = sc->arge_ifp;
1916188808Sgonzo	/*
1917188808Sgonzo	 * Go through our tx list and free mbufs for those
1918188808Sgonzo	 * frames that have been transmitted.
1919188808Sgonzo	 */
1920188808Sgonzo	for (; cons != prod; ARGE_INC(cons, ARGE_TX_RING_COUNT)) {
1921188808Sgonzo		cur_tx = &sc->arge_rdata.arge_tx_ring[cons];
1922188808Sgonzo		ctrl = cur_tx->packet_ctrl;
1923188808Sgonzo		/* Check if descriptor has "finished" flag */
1924188808Sgonzo		if ((ctrl & ARGE_DESC_EMPTY) == 0)
1925188808Sgonzo			break;
1926188808Sgonzo
1927188808Sgonzo		ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_PKT_SENT);
1928188808Sgonzo
1929188808Sgonzo		sc->arge_cdata.arge_tx_cnt--;
1930188808Sgonzo		ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
1931188808Sgonzo
1932188808Sgonzo		txd = &sc->arge_cdata.arge_txdesc[cons];
1933188808Sgonzo
1934188808Sgonzo		ifp->if_opackets++;
1935188808Sgonzo
1936188808Sgonzo		bus_dmamap_sync(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap,
1937188808Sgonzo		    BUS_DMASYNC_POSTWRITE);
1938188808Sgonzo		bus_dmamap_unload(sc->arge_cdata.arge_tx_tag, txd->tx_dmamap);
1939188808Sgonzo
1940188808Sgonzo		/* Free only if it's first descriptor in list */
1941188808Sgonzo		if (txd->tx_m)
1942188808Sgonzo			m_freem(txd->tx_m);
1943188808Sgonzo		txd->tx_m = NULL;
1944188808Sgonzo
1945188808Sgonzo		/* reset descriptor */
1946188808Sgonzo		cur_tx->packet_addr = 0;
1947188808Sgonzo	}
1948188808Sgonzo
1949188808Sgonzo	sc->arge_cdata.arge_tx_cons = cons;
1950188808Sgonzo
1951188808Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_tx_ring_tag,
1952188808Sgonzo	    sc->arge_cdata.arge_tx_ring_map, BUS_DMASYNC_PREWRITE);
1953188808Sgonzo}
1954188808Sgonzo
1955188808Sgonzo
1956198667Sgonzostatic int
1957188808Sgonzoarge_rx_locked(struct arge_softc *sc)
1958188808Sgonzo{
1959188808Sgonzo	struct arge_rxdesc	*rxd;
1960188808Sgonzo	struct ifnet		*ifp = sc->arge_ifp;
1961192783Sgonzo	int			cons, prog, packet_len, i;
1962188808Sgonzo	struct arge_desc	*cur_rx;
1963188808Sgonzo	struct mbuf		*m;
1964198667Sgonzo	int			rx_npkts = 0;
1965188808Sgonzo
1966188808Sgonzo	ARGE_LOCK_ASSERT(sc);
1967188808Sgonzo
1968188808Sgonzo	cons = sc->arge_cdata.arge_rx_cons;
1969188808Sgonzo
1970188808Sgonzo	bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
1971188808Sgonzo	    sc->arge_cdata.arge_rx_ring_map,
1972188808Sgonzo	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
1973188808Sgonzo
1974232627Sray	for (prog = 0; prog < ARGE_RX_RING_COUNT;
1975188808Sgonzo	    ARGE_INC(cons, ARGE_RX_RING_COUNT)) {
1976188808Sgonzo		cur_rx = &sc->arge_rdata.arge_rx_ring[cons];
1977188808Sgonzo		rxd = &sc->arge_cdata.arge_rxdesc[cons];
1978188808Sgonzo		m = rxd->rx_m;
1979188808Sgonzo
1980188808Sgonzo		if ((cur_rx->packet_ctrl & ARGE_DESC_EMPTY) != 0)
1981232627Sray		       break;
1982188808Sgonzo
1983188808Sgonzo		ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_PKT_RECVD);
1984188808Sgonzo
1985188808Sgonzo		prog++;
1986188808Sgonzo
1987188808Sgonzo		packet_len = ARGE_DMASIZE(cur_rx->packet_ctrl);
1988188808Sgonzo		bus_dmamap_sync(sc->arge_cdata.arge_rx_tag, rxd->rx_dmamap,
1989195434Sgonzo		    BUS_DMASYNC_POSTREAD);
1990188808Sgonzo		m = rxd->rx_m;
1991188808Sgonzo
1992188808Sgonzo		arge_fixup_rx(m);
1993188808Sgonzo		m->m_pkthdr.rcvif = ifp;
1994188808Sgonzo		/* Skip 4 bytes of CRC */
1995188808Sgonzo		m->m_pkthdr.len = m->m_len = packet_len - ETHER_CRC_LEN;
1996188808Sgonzo		ifp->if_ipackets++;
1997198667Sgonzo		rx_npkts++;
1998188808Sgonzo
1999188808Sgonzo		ARGE_UNLOCK(sc);
2000188808Sgonzo		(*ifp->if_input)(ifp, m);
2001188808Sgonzo		ARGE_LOCK(sc);
2002192783Sgonzo		cur_rx->packet_addr = 0;
2003192783Sgonzo	}
2004188808Sgonzo
2005192783Sgonzo	if (prog > 0) {
2006192783Sgonzo
2007192783Sgonzo		i = sc->arge_cdata.arge_rx_cons;
2008192783Sgonzo		for (; prog > 0 ; prog--) {
2009192783Sgonzo			if (arge_newbuf(sc, i) != 0) {
2010232627Sray				device_printf(sc->arge_dev,
2011192783Sgonzo				    "Failed to allocate buffer\n");
2012192783Sgonzo				break;
2013192783Sgonzo			}
2014192783Sgonzo			ARGE_INC(i, ARGE_RX_RING_COUNT);
2015188808Sgonzo		}
2016188808Sgonzo
2017188808Sgonzo		bus_dmamap_sync(sc->arge_cdata.arge_rx_ring_tag,
2018188808Sgonzo		    sc->arge_cdata.arge_rx_ring_map,
2019195434Sgonzo		    BUS_DMASYNC_PREWRITE);
2020188808Sgonzo
2021188808Sgonzo		sc->arge_cdata.arge_rx_cons = cons;
2022188808Sgonzo	}
2023198667Sgonzo
2024198667Sgonzo	return (rx_npkts);
2025188808Sgonzo}
2026188808Sgonzo
2027188808Sgonzostatic int
2028188808Sgonzoarge_intr_filter(void *arg)
2029188808Sgonzo{
2030188808Sgonzo	struct arge_softc	*sc = arg;
2031188808Sgonzo	uint32_t		status, ints;
2032188808Sgonzo
2033188808Sgonzo	status = ARGE_READ(sc, AR71XX_DMA_INTR_STATUS);
2034188808Sgonzo	ints = ARGE_READ(sc, AR71XX_DMA_INTR);
2035188808Sgonzo
2036220354Sadrian	ARGEDEBUG(sc, ARGE_DBG_INTR, "int mask(filter) = %b\n", ints,
2037188808Sgonzo	    "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD"
2038188808Sgonzo	    "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT");
2039232627Sray	ARGEDEBUG(sc, ARGE_DBG_INTR, "status(filter) = %b\n", status,
2040188808Sgonzo	    "\20\10RX_BUS_ERROR\7RX_OVERFLOW\5RX_PKT_RCVD"
2041188808Sgonzo	    "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT");
2042188808Sgonzo
2043188808Sgonzo	if (status & DMA_INTR_ALL) {
2044191644Sgonzo		sc->arge_intr_status |= status;
2045192783Sgonzo		ARGE_WRITE(sc, AR71XX_DMA_INTR, 0);
2046188808Sgonzo		return (FILTER_SCHEDULE_THREAD);
2047232627Sray	}
2048188808Sgonzo
2049188808Sgonzo	sc->arge_intr_status = 0;
2050188808Sgonzo	return (FILTER_STRAY);
2051188808Sgonzo}
2052188808Sgonzo
2053188808Sgonzostatic void
2054188808Sgonzoarge_intr(void *arg)
2055188808Sgonzo{
2056188808Sgonzo	struct arge_softc	*sc = arg;
2057188808Sgonzo	uint32_t		status;
2058220356Sadrian	struct ifnet		*ifp = sc->arge_ifp;
2059188808Sgonzo
2060192783Sgonzo	status = ARGE_READ(sc, AR71XX_DMA_INTR_STATUS);
2061192783Sgonzo	status |= sc->arge_intr_status;
2062188808Sgonzo
2063232627Sray	ARGEDEBUG(sc, ARGE_DBG_INTR, "int status(intr) = %b\n", status,
2064188808Sgonzo	    "\20\10\7RX_OVERFLOW\5RX_PKT_RCVD"
2065188808Sgonzo	    "\4TX_BUS_ERROR\2TX_UNDERRUN\1TX_PKT_SENT");
2066188808Sgonzo
2067232627Sray	/*
2068232627Sray	 * Is it our interrupt at all?
2069188808Sgonzo	 */
2070188808Sgonzo	if (status == 0)
2071188808Sgonzo		return;
2072188808Sgonzo
2073188808Sgonzo	if (status & DMA_INTR_RX_BUS_ERROR) {
2074188808Sgonzo		ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_BUS_ERROR);
2075188808Sgonzo		device_printf(sc->arge_dev, "RX bus error");
2076188808Sgonzo		return;
2077188808Sgonzo	}
2078188808Sgonzo
2079188808Sgonzo	if (status & DMA_INTR_TX_BUS_ERROR) {
2080188808Sgonzo		ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_BUS_ERROR);
2081188808Sgonzo		device_printf(sc->arge_dev, "TX bus error");
2082188808Sgonzo		return;
2083188808Sgonzo	}
2084188808Sgonzo
2085192783Sgonzo	ARGE_LOCK(sc);
2086188808Sgonzo
2087192783Sgonzo	if (status & DMA_INTR_RX_PKT_RCVD)
2088192783Sgonzo		arge_rx_locked(sc);
2089188808Sgonzo
2090232627Sray	/*
2091232627Sray	 * RX overrun disables the receiver.
2092232627Sray	 * Clear indication and re-enable rx.
2093192783Sgonzo	 */
2094192783Sgonzo	if ( status & DMA_INTR_RX_OVERFLOW) {
2095192783Sgonzo		ARGE_WRITE(sc, AR71XX_DMA_RX_STATUS, DMA_RX_STATUS_OVERFLOW);
2096192783Sgonzo		ARGE_WRITE(sc, AR71XX_DMA_RX_CONTROL, DMA_RX_CONTROL_EN);
2097220356Sadrian		sc->stats.rx_overflow++;
2098192783Sgonzo	}
2099188808Sgonzo
2100192783Sgonzo	if (status & DMA_INTR_TX_PKT_SENT)
2101192783Sgonzo		arge_tx_locked(sc);
2102232627Sray	/*
2103232627Sray	 * Underrun turns off TX. Clear underrun indication.
2104232627Sray	 * If there's anything left in the ring, reactivate the tx.
2105192783Sgonzo	 */
2106192569Sdwhite	if (status & DMA_INTR_TX_UNDERRUN) {
2107192569Sdwhite		ARGE_WRITE(sc, AR71XX_DMA_TX_STATUS, DMA_TX_STATUS_UNDERRUN);
2108220356Sadrian		sc->stats.tx_underflow++;
2109232628Sray		ARGEDEBUG(sc, ARGE_DBG_TX, "%s: TX underrun; tx_cnt=%d\n",
2110232628Sray		    __func__, sc->arge_cdata.arge_tx_cnt);
2111219590Sadrian		if (sc->arge_cdata.arge_tx_cnt > 0 ) {
2112232627Sray			ARGE_WRITE(sc, AR71XX_DMA_TX_CONTROL,
2113192783Sgonzo			    DMA_TX_CONTROL_EN);
2114192783Sgonzo		}
2115192569Sdwhite	}
2116192569Sdwhite
2117192946Sgonzo	/*
2118220357Sadrian	 * If we've finished TXing and there's space for more packets
2119220357Sadrian	 * to be queued for TX, do so. Otherwise we may end up in a
2120220357Sadrian	 * situation where the interface send queue was filled
2121220357Sadrian	 * whilst the hardware queue was full, then the hardware
2122220357Sadrian	 * queue was drained by the interface send queue wasn't,
2123220357Sadrian	 * and thus if_start() is never called to kick-start
2124220357Sadrian	 * the send process (and all subsequent packets are simply
2125220357Sadrian	 * discarded.
2126220357Sadrian	 *
2127220357Sadrian	 * XXX TODO: make sure that the hardware deals nicely
2128220357Sadrian	 * with the possibility of the queue being enabled above
2129220357Sadrian	 * after a TX underrun, then having the hardware queue added
2130220357Sadrian	 * to below.
2131220357Sadrian	 */
2132220357Sadrian	if (status & (DMA_INTR_TX_PKT_SENT | DMA_INTR_TX_UNDERRUN) &&
2133220357Sadrian	    (ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) {
2134220357Sadrian		if (!IFQ_IS_EMPTY(&ifp->if_snd))
2135220357Sadrian			arge_start_locked(ifp);
2136220357Sadrian	}
2137220357Sadrian
2138220357Sadrian	/*
2139192946Sgonzo	 * We handled all bits, clear status
2140192946Sgonzo	 */
2141192946Sgonzo	sc->arge_intr_status = 0;
2142188808Sgonzo	ARGE_UNLOCK(sc);
2143192783Sgonzo	/*
2144232627Sray	 * re-enable all interrupts
2145192783Sgonzo	 */
2146192783Sgonzo	ARGE_WRITE(sc, AR71XX_DMA_INTR, DMA_INTR_ALL);
2147188808Sgonzo}
2148188808Sgonzo
2149192783Sgonzo
2150188808Sgonzostatic void
2151188808Sgonzoarge_tick(void *xsc)
2152188808Sgonzo{
2153188808Sgonzo	struct arge_softc	*sc = xsc;
2154188808Sgonzo	struct mii_data		*mii;
2155188808Sgonzo
2156188808Sgonzo	ARGE_LOCK_ASSERT(sc);
2157188808Sgonzo
2158199234Sgonzo	if (sc->arge_miibus) {
2159199234Sgonzo		mii = device_get_softc(sc->arge_miibus);
2160199234Sgonzo		mii_tick(mii);
2161199234Sgonzo		callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc);
2162199234Sgonzo	}
2163188808Sgonzo}
2164199234Sgonzo
2165199234Sgonzoint
2166199234Sgonzoarge_multiphy_mediachange(struct ifnet *ifp)
2167199234Sgonzo{
2168199234Sgonzo	struct arge_softc *sc = ifp->if_softc;
2169199234Sgonzo	struct ifmedia *ifm = &sc->arge_ifmedia;
2170199234Sgonzo	struct ifmedia_entry *ife = ifm->ifm_cur;
2171199234Sgonzo
2172199234Sgonzo	if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
2173199234Sgonzo		return (EINVAL);
2174199234Sgonzo
2175199234Sgonzo	if (IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO) {
2176232627Sray		device_printf(sc->arge_dev,
2177199234Sgonzo		    "AUTO is not supported for multiphy MAC");
2178199234Sgonzo		return (EINVAL);
2179199234Sgonzo	}
2180199234Sgonzo
2181199234Sgonzo	/*
2182199234Sgonzo	 * Ignore everything
2183199234Sgonzo	 */
2184199234Sgonzo	return (0);
2185199234Sgonzo}
2186199234Sgonzo
2187199234Sgonzovoid
2188199234Sgonzoarge_multiphy_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
2189199234Sgonzo{
2190199234Sgonzo	struct arge_softc *sc = ifp->if_softc;
2191199234Sgonzo
2192199234Sgonzo	ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
2193232627Sray	ifmr->ifm_active = IFM_ETHER | sc->arge_media_type |
2194199234Sgonzo	    sc->arge_duplex_mode;
2195199234Sgonzo}
2196199234Sgonzo
2197234862Sadrian#if defined(ARGE_MDIO)
2198234862Sadrianstatic int
2199234862Sadrianargemdio_probe(device_t dev)
2200234862Sadrian{
2201234862Sadrian	device_set_desc(dev, "Atheros AR71xx built-in ethernet interface, MDIO controller");
2202234862Sadrian	return (0);
2203234862Sadrian}
2204234862Sadrian
2205234862Sadrianstatic int
2206234862Sadrianargemdio_attach(device_t dev)
2207234862Sadrian{
2208234862Sadrian	struct arge_softc	*sc;
2209234862Sadrian	int			error = 0;
2210234862Sadrian
2211234862Sadrian	sc = device_get_softc(dev);
2212234862Sadrian	sc->arge_dev = dev;
2213234862Sadrian	sc->arge_mac_unit = device_get_unit(dev);
2214234862Sadrian	sc->arge_rid = 0;
2215234862Sadrian	sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
2216234862Sadrian	    &sc->arge_rid, RF_ACTIVE | RF_SHAREABLE);
2217234862Sadrian	if (sc->arge_res == NULL) {
2218234862Sadrian		device_printf(dev, "couldn't map memory\n");
2219234862Sadrian		error = ENXIO;
2220234862Sadrian		goto fail;
2221234862Sadrian	}
2222234862Sadrian
2223234862Sadrian	/* Reset MAC - required for AR71xx MDIO to successfully occur */
2224234862Sadrian	arge_reset_mac(sc);
2225234862Sadrian	/* Reset MII bus */
2226234862Sadrian	arge_reset_miibus(sc);
2227234862Sadrian
2228234862Sadrian	bus_generic_probe(dev);
2229234862Sadrian	bus_enumerate_hinted_children(dev);
2230234862Sadrian	error = bus_generic_attach(dev);
2231234862Sadrianfail:
2232234862Sadrian	return (error);
2233234862Sadrian}
2234234862Sadrian
2235234862Sadrianstatic int
2236234862Sadrianargemdio_detach(device_t dev)
2237234862Sadrian{
2238234862Sadrian	return (0);
2239234862Sadrian}
2240234862Sadrian
2241234862Sadrian#endif
2242